Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- diff --git a/src/comm/comm.go b/src/comm/comm.go
- index fe0f492..6d374f9 100644
- --- a/src/comm/comm.go
- +++ b/src/comm/comm.go
- @@ -55,7 +55,7 @@ func (cs *CommService) Run(input chan core.Msg) {
- shutdown := make(chan bool)
- go listen(cs.svc, input, "tcp", cs.address, shutdown)
- - cs.svc.Game <- core.MsgTick{input} // Service is ready
- + cs.svc.Game <- core.MsgTick{Origin: input} // Service is ready
- for {
- msg := <-input
- diff --git a/src/core/component.go b/src/core/component.go
- index b38aaa9..47d03fc 100644
- --- a/src/core/component.go
- +++ b/src/core/component.go
- @@ -34,7 +34,7 @@ type Action interface {
- // Returns a unique and semi-descriptive name for each Action (defined by
- // the Action)
- Name() string
- - Act(ent Entity, svc ServiceContext)
- + Act(ent Entity, svc ServiceContext, dt float64)
- }
- // An Entity is a struct composed from various States and Actions, which each
- @@ -130,11 +130,11 @@ func (cd *CmpData) Run(svc ServiceContext) {
- // Check at the start of the tick because Game actually removes at
- // the end of the previous tick
- if _, ok := cd.states[cmpId.Remove]; ok {
- - m.Origin <- MsgTick{cd.input}
- + m.Origin <- MsgTick{Origin: cd.input}
- return // Entity was removed, bail
- }
- - cd.update(svc)
- - m.Origin <- MsgTick{cd.input} // Reply that we are updated
- + cd.update(svc, m.Duration)
- + m.Origin <- MsgTick{Origin: cd.input} // Reply that we are updated
- case MsgGetState:
- cd.sendState(m)
- case MsgGetAllStates:
- @@ -144,7 +144,7 @@ func (cd *CmpData) Run(svc ServiceContext) {
- case MsgAddAction:
- cd.AddAction(m.Action)
- case MsgRunAction:
- - m.Action.Act(cd, svc)
- + m.Action.Act(cd, svc, 0.)
- if m.Add {
- cd.AddAction(m.Action)
- }
- @@ -153,9 +153,9 @@ func (cd *CmpData) Run(svc ServiceContext) {
- }
- // Loop through each Action and let it run
- -func (cd *CmpData) update(svc ServiceContext) {
- +func (cd *CmpData) update(svc ServiceContext, dt float64) {
- for _, v := range cd.actions {
- - v.Act(cd, svc)
- + v.Act(cd, svc, dt)
- }
- }
- diff --git a/src/core/messages.go b/src/core/messages.go
- index b41ba8a..fde662f 100644
- --- a/src/core/messages.go
- +++ b/src/core/messages.go
- @@ -13,7 +13,8 @@ type Msg interface{}
- // Message to signal an update and/or updated status.
- // A completion of update reply should be sent to the Origin channel.
- type MsgTick struct {
- - Origin chan Msg // Identifies the sources of the tick
- + Origin chan Msg // Identifies the sources of the tick
- + Duration float64 // Time in milliseconds since the last tick
- }
- // Tells the receiver to quit, shutdown, stop, halt, cease operations, close for
- @@ -75,3 +76,10 @@ type MsgAssignControl struct {
- Uid UniqueId // Entity to be given to a client
- Revoked bool // True, if control is to be removed
- }
- +
- +// Message for proof-of-concept demo's spider AI.
- +// Requests that the spider entity be moved toward the nearest player.
- +type MsgPersueNearestPlayer struct {
- + Entity *EntityDesc
- + Speed float64 // units per second
- +}
- diff --git a/src/game/game.go b/src/game/game.go
- index fec2fa5..6b0c15c 100644
- --- a/src/game/game.go
- +++ b/src/game/game.go
- @@ -47,10 +47,11 @@ func (g *Game) Run(input chan core.Msg) {
- // List of up to date entities
- updated := make(map[chan core.Msg]bool, len(g.ents))
- remove_list := []chan core.Msg{}
- - tick_msg := core.MsgTick{input}
- + tick_dur := float64(skip_ns) / 1.0e6
- for {
- tick_start := time.Nanoseconds()
- + tick_msg := core.MsgTick{input, tick_dur}
- // Tell all the entities that a new tick has started
- ent_num := len(g.ents) // Store ent count for *this* tick
- @@ -79,7 +80,7 @@ func (g *Game) Run(input chan core.Msg) {
- }
- }
- update_end:
- - g.svc.Comm <- tick_msg
- + g.svc.Comm <- core.MsgTick{input, tick_dur}
- // Remove all entities that reported themselves to be removed
- for _, ent := range remove_list {
- @@ -92,8 +93,10 @@ func (g *Game) Run(input chan core.Msg) {
- sleep_ns := (tick_start + skip_ns) - time.Nanoseconds()
- if sleep_ns > 0 {
- time.Sleep(sleep_ns)
- + tick_dur = float64(skip_ns) / 1.0e6
- } else {
- log.Println("game: behind by", sleep_ns/1e6*-1, "ms")
- + tick_dur = float64(skip_ns - sleep_ns) / 1.0e6
- }
- }
- }
- diff --git a/src/main/main.go b/src/main/main.go
- index 3689de1..3abde92 100644
- --- a/src/main/main.go
- +++ b/src/main/main.go
- @@ -6,6 +6,7 @@
- package main
- import (
- + "github.com/tm1rbrt/s3dm"
- "core"
- "game"
- "comm"
- @@ -34,5 +35,7 @@ func main() {
- func initGameSvc(g *game.Game, svc core.ServiceContext) {
- spider := sf.InitSpider(g.GetUid())
- g.AddEntity(spider)
- + spider.AddAction(sf.Persue{5.})
- + spider.SetState(sf.Position{s3dm.NewV3(3., 3., 0.)})
- go spider.Run(svc)
- }
- diff --git a/src/pubsub/pubsub.go b/src/pubsub/pubsub.go
- index f486d5d..04749ac 100644
- --- a/src/pubsub/pubsub.go
- +++ b/src/pubsub/pubsub.go
- @@ -47,7 +47,7 @@ func NewPubSub(svc core.ServiceContext) *PubSub {
- // Starts a loop to receive and handle messages from the passed channel
- func (ps *PubSub) Run(input chan core.Msg) {
- - ps.svc.Game <- core.MsgTick{input} // Service is ready
- + ps.svc.Game <- core.MsgTick{Origin: input} // Service is ready
- for {
- msg := <-input
- diff --git a/src/sf/actions.go b/src/sf/actions.go
- index ab99004..cfd1cbd 100644
- --- a/src/sf/actions.go
- +++ b/src/sf/actions.go
- @@ -19,7 +19,7 @@ func (a Move) Id() core.ActionId { return cmpId.Move }
- func (a Move) Name() string { return "Move" }
- // Modifies the Position of an Entity with the passed Move vector.
- -func (a Move) Act(ent core.Entity, svc core.ServiceContext) {
- +func (a Move) Act(ent core.Entity, svc core.ServiceContext, dt float64) {
- svc.World <- MoveMsg{core.NewEntityDesc(ent), a.Direction}
- }
- @@ -30,7 +30,7 @@ type Attack struct{}
- func (a Attack) Id() core.ActionId { return cmpId.Attack }
- func (a Attack) Name() string { return "Attack" }
- -func (a Attack) Act(ent core.Entity, svc core.ServiceContext) {
- +func (a Attack) Act(ent core.Entity, svc core.ServiceContext, dt float64) {
- var health Health
- var ok bool
- if health, ok = (ent.GetState(cmpId.Health)).(Health); !ok {
- @@ -43,3 +43,16 @@ func (a Attack) Act(ent core.Entity, svc core.ServiceContext) {
- }
- ent.SetState(health)
- }
- +
- +type Persue struct {
- + Speed float64
- +}
- +
- +func (a Persue) Id() core.ActionId { return cmpId.Persue }
- +func (a Persue) Name() string { return "Persue" }
- +
- +func (a Persue) Act(ent core.Entity, svc core.ServiceContext, dt float64) {
- + ed := core.NewEntityDesc(ent)
- + speed := a.Speed * .001 * dt
- + svc.World <- core.MsgPersueNearestPlayer{ed, speed}
- +}
- diff --git a/src/sf/cmpId/cmpId.go b/src/sf/cmpId/cmpId.go
- index fa9c36f..58ad336 100644
- --- a/src/sf/cmpId/cmpId.go
- +++ b/src/sf/cmpId/cmpId.go
- @@ -22,6 +22,7 @@ const (
- const (
- Move = iota + core.ACTION_END
- Attack
- + Persue
- )
- // Entities
- diff --git a/src/sf/world.go b/src/sf/world.go
- index 85793de..510cdc8 100644
- --- a/src/sf/world.go
- +++ b/src/sf/world.go
- @@ -36,18 +36,21 @@ type World struct {
- ents map[string]chan core.Msg
- // Entity position (or cells) as 3D vectors may be looked up with this
- pos map[core.UniqueId]*s3dm.V3
- + // List of the player entities' unique ID's
- + players []core.UniqueId
- }
- func NewWorld(svc core.ServiceContext) *World {
- ents := make(map[string]chan core.Msg)
- pos := make(map[core.UniqueId]*s3dm.V3)
- - return &World{svc, ents, pos}
- + players := make([]core.UniqueId, 0)
- + return &World{svc, ents, pos, players}
- }
- func (w *World) Run(input chan core.Msg) {
- // Subscribe to listen for new entities in order to track their position
- w.svc.PubSub <- pubsub.SubscribeMsg{"entity", input}
- - w.svc.Game <- core.MsgTick{input} // Service is ready
- + w.svc.Game <- core.MsgTick{Origin: input} // Service is ready
- for {
- msg := <-input
- @@ -60,10 +63,35 @@ func (w *World) Run(input chan core.Msg) {
- if pos, ok := (<-reply).(Position); ok {
- w.putInEmptyPos(m.Entity, pos.Position)
- }
- + if m.Entity.Id == cmpId.Player {
- + w.players = append(w.players, m.Entity.Uid)
- + }
- case core.MsgEntityRemoved:
- pos := w.pos[m.Entity.Uid]
- w.pos[m.Entity.Uid] = nil, false
- w.ents[hashV3(pos)] = nil, false
- + if m.Entity.Id == cmpId.Player {
- + for i, pl := range w.players {
- + if pl == m.Entity.Uid {
- + w.players = append(w.players[:i], w.players[i+1:]...)
- + break
- + }
- + }
- + }
- + case core.MsgPersueNearestPlayer:
- + const REMOVE_DIST = 100. // distance at which to remove entity
- + nearest, dist := w.findNearestPlayer(m.Entity)
- + if nearest <= 0 {
- + break
- + }
- + if dist >= REMOVE_DIST {
- + m.Entity.Chan <- core.MsgSetState{core.Remove{true}}
- + w.svc.Game <- core.MsgEntityRemoved{m.Entity}
- + break
- + }
- + spos, ppos := w.pos[m.Entity.Uid], w.pos[nearest]
- + dir := ppos.Sub(spos).Unit() // normalized direction vector
- + w.moveEnt(m.Entity, dir.Muls(m.Speed))
- }
- }
- }
- @@ -107,15 +135,10 @@ func (w *World) moveEnt(ent *core.EntityDesc, vel *s3dm.V3) {
- new_pos := old_pos.Add(vel)
- hash := hashV3(new_pos)
- - // See if destination cell is occupied
- - if ent_ch, ok := w.ents[hash]; ok {
- - // TODO: HACK remove following if block when spider AI uses time based move
- - // Update position if movement is less than 1, this lets spider move slowly
- - if math.Fabs(vel.X) < 1 && math.Fabs(vel.Y) < 1 {
- - w.pos[ent.Uid] = new_pos
- - ent.Chan <- core.MsgSetState{Position{new_pos}}
- - }
- - ent_ch <- core.MsgRunAction{Attack{}, false} // Can't move there, attack instead
- + // See if destination cell is occupied by another entity
- + if ch, ok := w.ents[hash]; ok && ch != ent.Chan {
- + // Can't move there, attack instead
- + ch <- core.MsgRunAction{Attack{}, false}
- return
- }
- // If not, move the entity to the new pos
- @@ -155,17 +178,37 @@ func (w *World) spawnSpiders(pos *s3dm.V3) {
- // Spawn spiders at a random point in a ring around the player
- // between MIN_DIST and MAX_DIST.
- - reply := make(chan *core.EntityDesc)
- for i := 0; i < count; i++ {
- radius := rand.Float64()*(MAX_DIST-MIN_DIST) + MIN_DIST
- angle := rand.Float64() * 2. * math.Pi
- x := pos.X + radius*math.Cos(angle)
- y := pos.Y + radius*math.Sin(angle)
- +
- // Create the spider entity and position it
- - go func(X, Y float64) {
- - w.svc.Game <- game.MsgSpawnEntity{InitSpider, reply}
- - spider := <-reply
- - spider.Chan <- core.MsgSetState{Position{s3dm.NewV3(X, Y, 0.)}}
- - }(x, y)
- + f := func(uid core.UniqueId) core.Entity {
- + ent := InitSpider(uid)
- + ent.SetState(Position{s3dm.NewV3(x, y, 0.)})
- + ent.AddAction(Persue{5.})
- + return ent
- + }
- + go func() {
- + w.svc.Game <- game.MsgSpawnEntity{f, nil}
- + }()
- + }
- +}
- +
- +// Returns UID and distance of player entity nearest to given entity
- +func (w *World) findNearestPlayer(ent *core.EntityDesc) (core.UniqueId, float64) {
- + spos := w.pos[ent.Uid]
- + var best core.UniqueId = 0
- + var best_dist float64
- + for _, pl := range w.players {
- + ppos := w.pos[pl]
- + dist := spos.Distance(ppos)
- + if best == 0 || dist < best_dist {
- + best = pl
- + best_dist = dist
- + }
- }
- + return best, best_dist
- }
Add Comment
Please, Sign In to add comment