示例#1
0
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
}
示例#2
0
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
}
示例#3
0
/*
 * 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
}
示例#4
0
/*
 * 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
}
示例#5
0
/*
 * 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
}
示例#6
0
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
}
示例#7
0
/*
 * 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
}
示例#8
0
/*
 * 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
}
示例#9
0
/*
 * 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
}
示例#10
0
/*
 * 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
}