func (p *Player) DealDamage(damage float32) (dead bool) { if damage >= p.curHP { p.curHP = 0 p.g.PostEvent(events.NewEvent( events.PlayerDeathId, events.PlayerDeath{Id: p.id})) dead = true } else { p.curHP -= damage } return }
func (z *Zombie) DealDamage(damage float32) (dead bool) { if damage >= z.curHP { z.curHP = 0 z.g.PostEvent(events.NewEvent( events.ZombieDeathId, events.ZombieDeath{Id: z.id})) dead = true } else { z.curHP -= damage } return }
/* * handleAttack processes a Attack message and fires a PlayerAttack event */ func (g *Game) handleAttack(c *network.Conn, msg interface{}) error { attack := msg.(messages.Attack) log.WithField("msg", attack).Info("Attack message") g.eventManager.PostEvent( events.NewEvent(events.PlayerAttackId, events.PlayerAttack{ Id: c.GetUserData().(protocol.ClientData).Id, EntityId: attack.Id, })) return nil }
/* * handleOperate processes a Operate message and fires a PlayerOperate event */ func (g *Game) handleOperate(c *network.Conn, msg interface{}) error { operate := msg.(messages.Operate) log.WithField("msg", operate).Info("Operate message") g.eventManager.PostEvent( events.NewEvent(events.PlayerOperateId, events.PlayerOperate{ Id: c.GetUserData().(protocol.ClientData).Id, EntityId: operate.Id, })) return nil }
/* * handleRepair processes a Repair message and fires a PlayerRepair event */ func (g *Game) handleRepair(c *network.Conn, msg interface{}) error { repair := msg.(messages.Repair) log.WithField("msg", repair).Info("Repair message") g.eventManager.PostEvent( events.NewEvent(events.PlayerRepairId, events.PlayerRepair{ Id: c.GetUserData().(protocol.ClientData).Id, BuildingId: repair.Id, })) return nil }
func (bb *BuildingBase) DealDamage(damage float32) (dead bool) { if damage >= bb.curHP { bb.curHP = 0 bb.g.PostEvent(events.NewEvent( events.BuildingDestroyId, events.BuildingDestroy{Id: bb.id})) dead = true } else { bb.curHP -= damage } return }
/* * handleBuild processes a Build message and fires a PlayerBuild event */ func (g *Game) handleBuild(c *network.Conn, msg interface{}) error { build := msg.(messages.Build) log.WithField("msg", build).Info("Build message") g.eventManager.PostEvent( events.NewEvent(events.PlayerBuildId, events.PlayerBuild{ Id: c.GetUserData().(protocol.ClientData).Id, Type: build.Type, Xpos: build.Xpos, Ypos: build.Ypos, })) return nil }
/* * handleMove processes a Move message and fires a PlayerMove event */ func (g *Game) handleMove(c *network.Conn, msg interface{}) error { move := msg.(messages.Move) log.WithField("msg", move).Info("Move message") g.eventManager.PostEvent( events.NewEvent( events.PlayerMoveId, events.PlayerMove{ Id: (c.GetUserData().(protocol.ClientData)).Id, Xpos: move.Xpos, Ypos: move.Ypos, })) return nil }
/* * Setup initializes the different game subsystems */ func NewGame(cfg Config) *Game { g := new(Game) // copy configuration g.cfg = cfg var ( err error lvl log.Level ) // setup logger if lvl, err = log.ParseLevel(g.cfg.LogLevel); err != nil { log.WithFields(log.Fields{ "level": g.cfg.LogLevel, "default": DefaultLogLevel, }).Warn("unknown log level, using default") g.cfg.LogLevel = DefaultLogLevel lvl, _ = log.ParseLevel(DefaultLogLevel) } log.StandardLogger().Level = lvl // dump config log.WithField("cfg", g.cfg).Info("Game configuration") // setup go runtime runtime.GOMAXPROCS(runtime.NumCPU()) // load assets g.gameData, err = g.loadAssets(g.cfg.AssetsPath) if err != nil { log.WithError(err).Error("Couldn't load assets") return nil } // initialize the gamestate g.state = newGameState(g, int16(cfg.GameStartingTime)) if err := g.state.init(g.gameData); err != nil { log.WithError(err).Error("Couldn't initialize gamestate") return nil } // init channels g.quitChan = make(chan struct{}) g.eventManager = events.NewManager() // creates the client registry allocId := func() uint32 { return g.state.allocEntityId() } g.clients = protocol.NewClientRegistry(allocId) // setup the telnet server if len(g.cfg.TelnetPort) > 0 { g.telnetReq = make(chan TelnetRequest) g.telnetDone = make(chan error) g.telnet = protocol.NewTelnetServer(g.cfg.TelnetPort, g.clients) g.registerTelnetHandlers() } // initialize the pathfinder module g.pathfinder = NewPathfinder(g) // init the AI director g.ai = NewAIDirector(g, int16(cfg.NightStartingTime), int16(cfg.NightEndingTime)) g.server = protocol.NewServer(g.cfg.Port, g.clients, g.telnet, &g.wg, g.clients) // this will be called after a new player has successfully joined the game g.server.OnPlayerJoined(func(ID uint32, playerType uint8) { g.PostEvent( events.NewEvent( events.PlayerJoinId, events.PlayerJoin{Id: ID, Type: playerType})) }) // this will be called after a player has effectively left the game g.server.OnPlayerLeft(func(ID uint32) { g.PostEvent( events.NewEvent( events.PlayerLeaveId, events.PlayerLeave{Id: ID})) }) g.registerMsgHandlers() return g }
/* * telnetHandler is the unique handlers for game related telnet request. * * Because it is exclusively called from inside a select case of the game loop, * it is safe to read/write the gamestate here. However, every call should only * perform non-blocking only operations, as the game logic is **not** updated * as long as the handler is in execution. */ func (g *Game) telnetHandler(msg TelnetRequest) error { log.WithField("msg", msg).Info("Handling a telnet game message") switch msg.Type { case TnGameStateId: var ( gsMsg *messages.GameState gs *TnGameState str string ) if gsMsg = g.state.pack(); gsMsg == nil { return errors.New("no gamestate to show") } gs = msg.Content.(*TnGameState) if gs.Short { str = fmt.Sprintf("%v\n", *gsMsg) } else { // marshal to YAML if b, err := yaml.Marshal(*gsMsg); err == nil { str = string(b) } else { str = fmt.Sprintf("%v\n", *gsMsg) } } io.WriteString(msg.Context.App.Writer, str) case TnMoveEntityId: move := msg.Content.(*TnMoveEntity) if g.state.getPlayer(move.Id) == nil { return fmt.Errorf("id %+v doesn't exist or isn't a player", move.Id) } // emit a PlayerMove event evt := events.NewEvent(events.PlayerMoveId, events.PlayerMove{ Id: move.Id, Xpos: float32(move.Dest[0]), Ypos: float32(move.Dest[1]), }) g.PostEvent(evt) case TnTeleportEntityId: teleport := msg.Content.(*TnTeleportEntity) return fmt.Errorf("not implemented! but teleport received: %v", *teleport) // TODO: implement instant telnet teleportation // be aware of the folowing though: // just setting player pos won't work if any pathfinding is in progress // what to do? // cancel the pathfinding? but also set the entity state to idle? // it's ok if it's a player... // but what will happen when it will be a zombie? case TnBuildId: build := msg.Content.(*TnBuild) if g.state.getPlayer(build.Id) == nil { return fmt.Errorf("id %+v doesn't exist or isn't a player", build.Id) } // TODO: hard-coded building type check for now if build.Type != 0 { return fmt.Errorf("unknown building type: %v", build.Type) } // emit a PlayerBuild event evt := events.NewEvent(events.PlayerBuildId, events.PlayerBuild{ Id: build.Id, Xpos: float32(build.Pos[0]), Ypos: float32(build.Pos[1]), Type: uint8(build.Type), }) g.PostEvent(evt) case TnRepairId: repair := msg.Content.(*TnRepair) if g.state.getPlayer(repair.Id) == nil { return fmt.Errorf("id %+v doesn't exist or isn't a player", repair.Id) } if g.state.getBuilding(repair.BuildingId) == nil { return fmt.Errorf("id %+v doesn't exist or isn't a building", repair.BuildingId) } // emit a PlayerRepair event evt := events.NewEvent(events.PlayerRepairId, events.PlayerRepair{ Id: repair.Id, BuildingId: repair.BuildingId, }) g.PostEvent(evt) case TnDestroyId: destroy := msg.Content.(*TnDestroy) if g.state.getBuilding(destroy.Id) == nil { return fmt.Errorf("id %+v doesn't exist or isn't a building", destroy.Id) } // remove the building g.state.RemoveEntity(destroy.Id) case TnSummonZombieId: g.ai.SummonZombie() default: return errors.New("unknow telnet message id") } return nil }