init
This commit is contained in:
+219
@@ -0,0 +1,219 @@
|
||||
package sim
|
||||
|
||||
import (
|
||||
"wh/config"
|
||||
"wh/fixed"
|
||||
"wh/world"
|
||||
)
|
||||
|
||||
// tickCtx is per-tick shared context; asteroid positions are computed lazily
|
||||
// and cached because several ships may query them in one tick.
|
||||
type tickCtx struct {
|
||||
s *State
|
||||
cfg config.Config
|
||||
tick int
|
||||
now int64
|
||||
stPos fixed.Vec
|
||||
stVel fixed.Vec
|
||||
res *DayResult
|
||||
sold *[world.NumOre]int64
|
||||
|
||||
astPos, astVel []fixed.Vec
|
||||
astDone []bool
|
||||
}
|
||||
|
||||
func newTickCtx(s *State, cfg config.Config, tick int, now int64, stPos, stVel fixed.Vec, res *DayResult, sold *[world.NumOre]int64) *tickCtx {
|
||||
n := len(s.Asteroids)
|
||||
return &tickCtx{s: s, cfg: cfg, tick: tick, now: now, stPos: stPos, stVel: stVel, res: res, sold: sold,
|
||||
astPos: make([]fixed.Vec, n), astVel: make([]fixed.Vec, n), astDone: make([]bool, n)}
|
||||
}
|
||||
|
||||
func (tc *tickCtx) asteroid(i int) (fixed.Vec, fixed.Vec) {
|
||||
if !tc.astDone[i] {
|
||||
tc.astPos[i], tc.astVel[i] = tc.s.Asteroids[i].Orbit.State(tc.now)
|
||||
tc.astDone[i] = true
|
||||
}
|
||||
return tc.astPos[i], tc.astVel[i]
|
||||
}
|
||||
|
||||
// shipBus is one ship's view of its peripherals for one tick.
|
||||
type shipBus struct {
|
||||
tc *tickCtx
|
||||
sh *Ship
|
||||
}
|
||||
|
||||
func sat32(v int64) int32 {
|
||||
if v > 1<<31-1 {
|
||||
return 1<<31 - 1
|
||||
}
|
||||
if v < -1<<31 {
|
||||
return -1 << 31
|
||||
}
|
||||
return int32(v)
|
||||
}
|
||||
|
||||
func kmInt(f fixed.F) int32 { return sat32(f.Floor()) }
|
||||
|
||||
// mps converts km/s to whole m/s.
|
||||
func mps(f fixed.F) int32 { return sat32(f.MulInt(1000).Floor()) }
|
||||
|
||||
func (b *shipBus) target() (pos, vel fixed.Vec, ok bool) {
|
||||
id := b.sh.Target
|
||||
if id < 1 || id > int64(len(b.tc.s.Asteroids)) {
|
||||
return pos, vel, false
|
||||
}
|
||||
pos, vel = b.tc.asteroid(int(id - 1))
|
||||
return pos, vel, true
|
||||
}
|
||||
|
||||
// axis returns component (port-base) of v (0=X, 1=Y, 2=Z) using conv, for
|
||||
// ports laid out as three consecutive X, Y, Z registers.
|
||||
func axis(v fixed.Vec, port, base uint16, conv func(fixed.F) int32) int32 {
|
||||
switch port - base {
|
||||
case 0:
|
||||
return conv(v.X)
|
||||
case 1:
|
||||
return conv(v.Y)
|
||||
}
|
||||
return conv(v.Z)
|
||||
}
|
||||
|
||||
func (b *shipBus) In(port uint16) int32 {
|
||||
sh, tc := b.sh, b.tc
|
||||
switch {
|
||||
case port == PortTick:
|
||||
return int32(tc.tick)
|
||||
case port == PortDay:
|
||||
return sat32(tc.s.Day)
|
||||
case port == PortTicks:
|
||||
return int32(tc.cfg.TicksPerDay)
|
||||
case port >= PortPosX && port <= PortPosZ:
|
||||
return axis(sh.Pos, port, PortPosX, kmInt)
|
||||
case port >= PortVelX && port <= PortVelZ:
|
||||
return axis(sh.Vel, port, PortVelX, mps)
|
||||
case port == PortFuel:
|
||||
return sat32(sh.Fuel.Floor())
|
||||
case port == PortMass:
|
||||
return sat32(sh.Mass().Floor())
|
||||
case port == PortScanNearest:
|
||||
best, bestD := int64(0), fixed.Max
|
||||
for i := range tc.s.Asteroids {
|
||||
p, _ := tc.asteroid(i)
|
||||
if d := p.Sub(sh.Pos).Len(); d < bestD {
|
||||
best, bestD = int64(i+1), d
|
||||
}
|
||||
}
|
||||
return int32(best)
|
||||
case port >= PortScanRelX && port <= PortScanDist:
|
||||
p, v, ok := b.target()
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
rel, relV := p.Sub(sh.Pos), v.Sub(sh.Vel)
|
||||
switch {
|
||||
case port <= PortScanRelZ:
|
||||
return axis(rel, port, PortScanRelX, kmInt)
|
||||
case port <= PortScanRelVZ:
|
||||
return axis(relV, port, PortScanRelVX, mps)
|
||||
}
|
||||
return kmInt(rel.Len())
|
||||
case port >= PortScanOre && port < PortScanOre+8:
|
||||
if a := tc.s.asteroid(sh.Target); a != nil && port-PortScanOre < uint16(world.NumOre) {
|
||||
return sat32(a.Ore[port-PortScanOre])
|
||||
}
|
||||
return 0
|
||||
case port == PortCargo:
|
||||
return sat32(sh.CargoTotal())
|
||||
case port == PortCargoCap:
|
||||
return sat32(sh.Hull.CargoCap)
|
||||
case port >= PortCargoOre && port < PortCargoOre+8:
|
||||
if port-PortCargoOre < uint16(world.NumOre) {
|
||||
return sat32(sh.Cargo[port-PortCargoOre])
|
||||
}
|
||||
return 0
|
||||
case port >= PortStnRelX && port <= PortStnRelVZ:
|
||||
rel, relV := tc.stPos.Sub(sh.Pos), tc.stVel.Sub(sh.Vel)
|
||||
if port <= PortStnRelZ {
|
||||
return axis(rel, port, PortStnRelX, kmInt)
|
||||
}
|
||||
return axis(relV, port, PortStnRelVX, mps)
|
||||
case port == PortCredits:
|
||||
return sat32(sh.Earned)
|
||||
case port == PortUplinkNew:
|
||||
if sh.UplinkNew {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
case port == PortUplinkLen:
|
||||
return sh.UplinkLen
|
||||
case port == PortMathAtan2:
|
||||
a := fixed.Atan2(fixed.FromInt(int64(sh.MathY)), fixed.FromInt(int64(sh.MathX)))
|
||||
return sat32(a.MulInt(1000).Floor())
|
||||
case port == PortMathHypot:
|
||||
return sat32(fixed.Hypot(fixed.FromInt(int64(sh.MathX)), fixed.FromInt(int64(sh.MathY))).Floor())
|
||||
case port == PortMathNorm3:
|
||||
return sat32(fixed.Norm3(fixed.FromInt(int64(sh.MathX)), fixed.FromInt(int64(sh.MathY)), fixed.FromInt(int64(sh.MathZ))).Floor())
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (b *shipBus) Out(port uint16, v int32) {
|
||||
sh, tc := b.sh, b.tc
|
||||
switch port {
|
||||
case PortThrottle:
|
||||
sh.Throttle = v
|
||||
case PortAzimuth:
|
||||
sh.Azimuth = v
|
||||
case PortPitch:
|
||||
sh.Pitch = v
|
||||
case PortScanSelect:
|
||||
sh.Target = int64(v)
|
||||
case PortMine:
|
||||
sh.Mining = v != 0
|
||||
case PortSell:
|
||||
if v == 0 || !tc.s.canReach(sh, tc.stPos, tc.stVel, DockRangeKm) {
|
||||
return
|
||||
}
|
||||
value := tc.s.Market.Value(sh.Cargo)
|
||||
if value == 0 {
|
||||
return
|
||||
}
|
||||
tc.s.Credits[sh.Owner] += value
|
||||
sh.Earned += value
|
||||
for o, kg := range sh.Cargo {
|
||||
tc.sold[o] += kg
|
||||
sh.Cargo[o] = 0
|
||||
}
|
||||
tc.res.Events = append(tc.res.Events, Event{tc.tick, sh.ID, "sold", itoa(value) + " credits"})
|
||||
case PortUplinkNew:
|
||||
sh.UplinkNew = false
|
||||
case PortMathX:
|
||||
sh.MathX = v
|
||||
case PortMathY:
|
||||
sh.MathY = v
|
||||
case PortMathZ:
|
||||
sh.MathZ = v
|
||||
}
|
||||
}
|
||||
|
||||
func itoa(v int64) string {
|
||||
if v == 0 {
|
||||
return "0"
|
||||
}
|
||||
neg := v < 0
|
||||
if neg {
|
||||
v = -v
|
||||
}
|
||||
var b [20]byte
|
||||
i := len(b)
|
||||
for v > 0 {
|
||||
i--
|
||||
b[i] = byte('0' + v%10)
|
||||
v /= 10
|
||||
}
|
||||
if neg {
|
||||
i--
|
||||
b[i] = '-'
|
||||
}
|
||||
return string(b[i:])
|
||||
}
|
||||
Reference in New Issue
Block a user