mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-04 16:02:26 +08:00
1419 lines
39 KiB
Go
1419 lines
39 KiB
Go
package game
|
|
|
|
import (
|
|
"math"
|
|
"time"
|
|
|
|
"hk4e/common/constant"
|
|
"hk4e/common/mq"
|
|
"hk4e/gdconf"
|
|
"hk4e/gs/model"
|
|
"hk4e/pkg/alg"
|
|
"hk4e/pkg/logger"
|
|
"hk4e/protocol/cmd"
|
|
"hk4e/protocol/proto"
|
|
)
|
|
|
|
// 世界管理器
|
|
|
|
const (
|
|
ENTITY_NUM_UNLIMIT = false // 是否不限制场景内实体数量
|
|
ENTITY_MAX_SEND_NUM = 200 // 场景内最大实体数量
|
|
MAX_MULTIPLAYER_WORLD_NUM = 10 // 本服务器最大多人世界数量
|
|
)
|
|
|
|
type WorldManager struct {
|
|
worldMap map[uint32]*World
|
|
snowflake *alg.SnowflakeWorker
|
|
aiWorld *World // 本服的Ai玩家世界
|
|
sceneBlockAoiMap map[uint32]*alg.AoiManager // 全局各场景地图的aoi管理器
|
|
multiplayerWorldNum uint32 // 本服务器的多人世界数量
|
|
}
|
|
|
|
func NewWorldManager(snowflake *alg.SnowflakeWorker) (r *WorldManager) {
|
|
r = new(WorldManager)
|
|
r.worldMap = make(map[uint32]*World)
|
|
r.snowflake = snowflake
|
|
r.sceneBlockAoiMap = make(map[uint32]*alg.AoiManager)
|
|
for _, sceneConfig := range gdconf.CONF.SceneMap {
|
|
minX := int16(0)
|
|
maxX := int16(0)
|
|
minZ := int16(0)
|
|
maxZ := int16(0)
|
|
blockXLen := int16(0)
|
|
blockYLen := int16(0)
|
|
blockZLen := int16(0)
|
|
ok := true
|
|
for _, blockConfig := range sceneConfig.BlockMap {
|
|
if int16(blockConfig.BlockRange.Min.X) < minX {
|
|
minX = int16(blockConfig.BlockRange.Min.X)
|
|
}
|
|
if int16(blockConfig.BlockRange.Max.X) > maxX {
|
|
maxX = int16(blockConfig.BlockRange.Max.X)
|
|
}
|
|
if int16(blockConfig.BlockRange.Min.Z) < minZ {
|
|
minZ = int16(blockConfig.BlockRange.Min.Z)
|
|
}
|
|
if int16(blockConfig.BlockRange.Max.Z) > maxZ {
|
|
maxZ = int16(blockConfig.BlockRange.Max.Z)
|
|
}
|
|
xLen := int16(blockConfig.BlockRange.Max.X - blockConfig.BlockRange.Min.X)
|
|
yLen := int16(blockConfig.BlockRange.Max.Y - blockConfig.BlockRange.Min.Y)
|
|
zLen := int16(blockConfig.BlockRange.Max.Z - blockConfig.BlockRange.Min.Z)
|
|
if blockXLen == 0 {
|
|
blockXLen = xLen
|
|
} else {
|
|
if blockXLen != xLen {
|
|
ok = false
|
|
break
|
|
}
|
|
}
|
|
if blockYLen == 0 {
|
|
blockYLen = yLen
|
|
} else {
|
|
if blockYLen != yLen {
|
|
ok = false
|
|
break
|
|
}
|
|
}
|
|
if blockZLen == 0 {
|
|
blockZLen = zLen
|
|
} else {
|
|
if blockZLen != zLen {
|
|
ok = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if !ok {
|
|
continue
|
|
}
|
|
numX := int16(0)
|
|
if blockXLen != 0 {
|
|
if blockXLen > 32 {
|
|
blockXLen = 32
|
|
}
|
|
numX = (maxX - minX) / blockXLen
|
|
} else {
|
|
numX = 1
|
|
}
|
|
if numX == 0 {
|
|
numX = 1
|
|
}
|
|
numZ := int16(0)
|
|
if blockZLen != 0 {
|
|
if blockZLen > 32 {
|
|
blockZLen = 32
|
|
}
|
|
numZ = (maxZ - minZ) / blockZLen
|
|
} else {
|
|
numZ = 1
|
|
}
|
|
if numZ == 0 {
|
|
numZ = 1
|
|
}
|
|
aoiManager := alg.NewAoiManager()
|
|
aoiManager.SetAoiRange(minX, maxX, -1.0, 1.0, minZ, maxZ)
|
|
aoiManager.Init3DRectAoiManager(numX, 1, numZ)
|
|
for _, blockConfig := range sceneConfig.BlockMap {
|
|
for _, groupConfig := range blockConfig.GroupMap {
|
|
for _, monsterConfig := range groupConfig.MonsterList {
|
|
aoiManager.AddObjectToGridByPos(r.snowflake.GenId(), monsterConfig,
|
|
float32(monsterConfig.Pos.X),
|
|
float32(0.0),
|
|
float32(monsterConfig.Pos.Z))
|
|
}
|
|
for _, npcConfig := range groupConfig.NpcList {
|
|
aoiManager.AddObjectToGridByPos(r.snowflake.GenId(), npcConfig,
|
|
float32(npcConfig.Pos.X),
|
|
float32(0.0),
|
|
float32(npcConfig.Pos.Z))
|
|
}
|
|
for _, gadgetConfig := range groupConfig.GadgetList {
|
|
aoiManager.AddObjectToGridByPos(r.snowflake.GenId(), gadgetConfig,
|
|
float32(gadgetConfig.Pos.X),
|
|
float32(0.0),
|
|
float32(gadgetConfig.Pos.Z))
|
|
}
|
|
}
|
|
}
|
|
if sceneConfig.Id == 3 {
|
|
logger.Info("init scene aoi mgr, scene: %v", sceneConfig.Id)
|
|
aoiManager.AoiInfoLog(false)
|
|
}
|
|
r.sceneBlockAoiMap[uint32(sceneConfig.Id)] = aoiManager
|
|
}
|
|
r.multiplayerWorldNum = 0
|
|
return r
|
|
}
|
|
|
|
func (w *WorldManager) GetWorldByID(worldId uint32) *World {
|
|
return w.worldMap[worldId]
|
|
}
|
|
|
|
func (w *WorldManager) GetAllWorld() map[uint32]*World {
|
|
return w.worldMap
|
|
}
|
|
|
|
func (w *WorldManager) CreateWorld(owner *model.Player) *World {
|
|
worldId := uint32(w.snowflake.GenId())
|
|
world := &World{
|
|
id: worldId,
|
|
owner: owner,
|
|
playerMap: make(map[uint32]*model.Player),
|
|
sceneMap: make(map[uint32]*Scene),
|
|
entityIdCounter: 0,
|
|
worldLevel: 0,
|
|
multiplayer: false,
|
|
mpLevelEntityId: 0,
|
|
chatMsgList: make([]*proto.ChatInfo, 0),
|
|
playerFirstEnterMap: make(map[uint32]int64),
|
|
waitEnterPlayerMap: make(map[uint32]int64),
|
|
multiplayerTeam: CreateMultiplayerTeam(),
|
|
peerList: make([]*model.Player, 0),
|
|
}
|
|
world.mpLevelEntityId = world.GetNextWorldEntityId(constant.EntityIdTypeConst.MPLEVEL)
|
|
w.worldMap[worldId] = world
|
|
return world
|
|
}
|
|
|
|
func (w *WorldManager) DestroyWorld(worldId uint32) {
|
|
world := w.GetWorldByID(worldId)
|
|
for _, player := range world.playerMap {
|
|
world.RemovePlayer(player)
|
|
player.WorldId = 0
|
|
}
|
|
delete(w.worldMap, worldId)
|
|
if world.multiplayer {
|
|
w.multiplayerWorldNum--
|
|
}
|
|
}
|
|
|
|
// GetAiWorld 获取本服务器的Ai世界
|
|
func (w *WorldManager) GetAiWorld() *World {
|
|
return w.aiWorld
|
|
}
|
|
|
|
// InitAiWorld 初始化Ai世界
|
|
func (w *WorldManager) InitAiWorld(owner *model.Player) {
|
|
w.aiWorld = w.GetWorldByID(owner.WorldId)
|
|
w.aiWorld.ChangeToMultiplayer()
|
|
go RunPlayAudio()
|
|
}
|
|
|
|
func (w *WorldManager) IsAiWorld(world *World) bool {
|
|
return world.id == w.aiWorld.id
|
|
}
|
|
|
|
func (w *WorldManager) IsRobotWorld(world *World) bool {
|
|
return world.owner.PlayerID < 100000000
|
|
}
|
|
|
|
func (w *WorldManager) IsBigWorld(world *World) bool {
|
|
return (world.id == w.aiWorld.id) && (w.aiWorld.owner.PlayerID == BigWorldAiUid)
|
|
}
|
|
|
|
func (w *WorldManager) GetSceneBlockAoiMap() map[uint32]*alg.AoiManager {
|
|
return w.sceneBlockAoiMap
|
|
}
|
|
|
|
func (w *WorldManager) GetMultiplayerWorldNum() uint32 {
|
|
return w.multiplayerWorldNum
|
|
}
|
|
|
|
// 世界数据结构
|
|
|
|
type World struct {
|
|
id uint32
|
|
owner *model.Player
|
|
playerMap map[uint32]*model.Player
|
|
sceneMap map[uint32]*Scene
|
|
entityIdCounter uint32 // 世界的实体id生成计数器
|
|
worldLevel uint8 // 世界等级
|
|
multiplayer bool // 是否多人世界
|
|
mpLevelEntityId uint32
|
|
chatMsgList []*proto.ChatInfo // 世界聊天消息列表
|
|
playerFirstEnterMap map[uint32]int64 // 玩家第一次进入世界的时间 key:uid value:进入时间
|
|
waitEnterPlayerMap map[uint32]int64 // 进入世界的玩家等待列表 key:uid value:开始时间
|
|
multiplayerTeam *MultiplayerTeam
|
|
peerList []*model.Player // 玩家编号列表
|
|
}
|
|
|
|
func (w *World) GetId() uint32 {
|
|
return w.id
|
|
}
|
|
|
|
func (w *World) GetOwner() *model.Player {
|
|
return w.owner
|
|
}
|
|
|
|
func (w *World) GetAllPlayer() map[uint32]*model.Player {
|
|
return w.playerMap
|
|
}
|
|
|
|
func (w *World) GetAllScene() map[uint32]*Scene {
|
|
return w.sceneMap
|
|
}
|
|
|
|
func (w *World) GetWorldLevel() uint8 {
|
|
return w.worldLevel
|
|
}
|
|
|
|
func (w *World) GetMultiplayer() bool {
|
|
return w.multiplayer
|
|
}
|
|
|
|
func (w *World) GetMpLevelEntityId() uint32 {
|
|
return w.mpLevelEntityId
|
|
}
|
|
|
|
func (w *World) GetNextWorldEntityId(entityType uint16) uint32 {
|
|
for {
|
|
w.entityIdCounter++
|
|
ret := (uint32(entityType) << 24) + w.entityIdCounter
|
|
reTry := false
|
|
for _, scene := range w.sceneMap {
|
|
_, exist := scene.entityMap[ret]
|
|
if exist {
|
|
reTry = true
|
|
break
|
|
}
|
|
}
|
|
if reTry {
|
|
continue
|
|
} else {
|
|
return ret
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetPlayerPeerId 获取当前玩家世界内编号
|
|
func (w *World) GetPlayerPeerId(player *model.Player) uint32 {
|
|
peerId := uint32(0)
|
|
for peerIdIndex, worldPlayer := range w.peerList {
|
|
if worldPlayer.PlayerID == player.PlayerID {
|
|
peerId = uint32(peerIdIndex) + 1
|
|
}
|
|
}
|
|
// logger.Debug("get player peer id is: %v, uid: %v", peerId, player.PlayerID)
|
|
return peerId
|
|
}
|
|
|
|
// GetPlayerByPeerId 通过世界内编号获取玩家
|
|
func (w *World) GetPlayerByPeerId(peerId uint32) *model.Player {
|
|
peerIdIndex := int(peerId) - 1
|
|
if peerIdIndex >= len(w.peerList) {
|
|
return nil
|
|
}
|
|
return w.peerList[peerIdIndex]
|
|
}
|
|
|
|
// GetWorldPlayerNum 获取世界中玩家的数量
|
|
func (w *World) GetWorldPlayerNum() int {
|
|
return len(w.playerMap)
|
|
}
|
|
|
|
func (w *World) AddPlayer(player *model.Player, sceneId uint32) {
|
|
w.peerList = append(w.peerList, player)
|
|
w.playerMap[player.PlayerID] = player
|
|
// 将玩家自身当前的队伍角色信息复制到世界的玩家本地队伍
|
|
team := player.TeamConfig.GetActiveTeam()
|
|
if player.PlayerID == w.owner.PlayerID {
|
|
w.SetPlayerLocalTeam(player, team.GetAvatarIdList())
|
|
} else {
|
|
activeAvatarId := player.TeamConfig.GetActiveAvatarId()
|
|
w.SetPlayerLocalTeam(player, []uint32{activeAvatarId})
|
|
}
|
|
playerNum := w.GetWorldPlayerNum()
|
|
if playerNum > 4 {
|
|
if !WORLD_MANAGER.IsBigWorld(w) {
|
|
return
|
|
}
|
|
w.AddMultiplayerTeam(player)
|
|
} else {
|
|
w.UpdateMultiplayerTeam()
|
|
}
|
|
for _, worldPlayer := range w.playerMap {
|
|
list := w.GetPlayerWorldAvatarList(worldPlayer)
|
|
maxIndex := len(list) - 1
|
|
index := int(worldPlayer.TeamConfig.CurrAvatarIndex)
|
|
if index > maxIndex {
|
|
w.SetPlayerAvatarIndex(worldPlayer, 0)
|
|
} else {
|
|
w.SetPlayerAvatarIndex(worldPlayer, index)
|
|
}
|
|
}
|
|
scene := w.GetSceneById(sceneId)
|
|
scene.AddPlayer(player)
|
|
w.InitPlayerTeamEntityId(player)
|
|
}
|
|
|
|
func (w *World) RemovePlayer(player *model.Player) {
|
|
peerId := w.GetPlayerPeerId(player)
|
|
w.peerList = append(w.peerList[:peerId-1], w.peerList[peerId:]...)
|
|
scene := w.sceneMap[player.SceneId]
|
|
scene.RemovePlayer(player)
|
|
delete(w.playerMap, player.PlayerID)
|
|
delete(w.playerFirstEnterMap, player.PlayerID)
|
|
delete(w.multiplayerTeam.localTeamMap, player.PlayerID)
|
|
delete(w.multiplayerTeam.localAvatarIndexMap, player.PlayerID)
|
|
delete(w.multiplayerTeam.localTeamEntityMap, player.PlayerID)
|
|
playerNum := w.GetWorldPlayerNum()
|
|
if playerNum > 4 {
|
|
if !WORLD_MANAGER.IsBigWorld(w) {
|
|
return
|
|
}
|
|
w.RemoveMultiplayerTeam(player)
|
|
} else {
|
|
if player.PlayerID != w.owner.PlayerID {
|
|
w.UpdateMultiplayerTeam()
|
|
}
|
|
}
|
|
}
|
|
|
|
// WorldAvatar 世界角色
|
|
type WorldAvatar struct {
|
|
uid uint32
|
|
avatarId uint32
|
|
avatarEntityId uint32
|
|
weaponEntityId uint32
|
|
abilityList []*proto.AbilityAppliedAbility
|
|
modifierList []*proto.AbilityAppliedModifier
|
|
}
|
|
|
|
func (w *WorldAvatar) GetUid() uint32 {
|
|
return w.uid
|
|
}
|
|
|
|
func (w *WorldAvatar) GetAvatarId() uint32 {
|
|
return w.avatarId
|
|
}
|
|
|
|
func (w *WorldAvatar) GetAvatarEntityId() uint32 {
|
|
return w.avatarEntityId
|
|
}
|
|
|
|
func (w *WorldAvatar) GetWeaponEntityId() uint32 {
|
|
return w.weaponEntityId
|
|
}
|
|
|
|
func (w *WorldAvatar) SetWeaponEntityId(weaponEntityId uint32) {
|
|
w.weaponEntityId = weaponEntityId
|
|
}
|
|
|
|
func (w *WorldAvatar) GetAbilityList() []*proto.AbilityAppliedAbility {
|
|
return w.abilityList
|
|
}
|
|
|
|
func (w *WorldAvatar) SetAbilityList(abilityList []*proto.AbilityAppliedAbility) {
|
|
w.abilityList = abilityList
|
|
}
|
|
|
|
func (w *WorldAvatar) GetModifierList() []*proto.AbilityAppliedModifier {
|
|
return w.modifierList
|
|
}
|
|
|
|
func (w *WorldAvatar) SetModifierList(modifierList []*proto.AbilityAppliedModifier) {
|
|
w.modifierList = modifierList
|
|
}
|
|
|
|
// GetWorldAvatarList 获取世界队伍的全部角色列表
|
|
func (w *World) GetWorldAvatarList() []*WorldAvatar {
|
|
worldAvatarList := make([]*WorldAvatar, 0)
|
|
for _, worldAvatar := range w.multiplayerTeam.worldTeam {
|
|
if worldAvatar.uid == 0 {
|
|
continue
|
|
}
|
|
worldAvatarList = append(worldAvatarList, worldAvatar)
|
|
}
|
|
return worldAvatarList
|
|
}
|
|
|
|
// GetPlayerWorldAvatar 获取某玩家在世界队伍中的某角色
|
|
func (w *World) GetPlayerWorldAvatar(player *model.Player, avatarId uint32) *WorldAvatar {
|
|
for _, worldAvatar := range w.GetWorldAvatarList() {
|
|
if worldAvatar.uid == player.PlayerID && worldAvatar.avatarId == avatarId {
|
|
return worldAvatar
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetPlayerWorldAvatarList 获取某玩家在世界队伍中的所有角色列表
|
|
func (w *World) GetPlayerWorldAvatarList(player *model.Player) []*WorldAvatar {
|
|
worldAvatarList := make([]*WorldAvatar, 0)
|
|
for _, worldAvatar := range w.GetWorldAvatarList() {
|
|
if worldAvatar.uid == player.PlayerID {
|
|
worldAvatarList = append(worldAvatarList, worldAvatar)
|
|
}
|
|
}
|
|
return worldAvatarList
|
|
}
|
|
|
|
// GetWorldAvatarByEntityId 通过场景实体id获取世界队伍中的角色
|
|
func (w *World) GetWorldAvatarByEntityId(avatarEntityId uint32) *WorldAvatar {
|
|
for _, worldAvatar := range w.GetWorldAvatarList() {
|
|
if worldAvatar.avatarEntityId == avatarEntityId {
|
|
return worldAvatar
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// InitPlayerWorldAvatar 初始化某玩家在世界队伍中的所有角色
|
|
func (w *World) InitPlayerWorldAvatar(player *model.Player) {
|
|
scene := w.GetSceneById(player.SceneId)
|
|
for _, worldAvatar := range w.GetWorldAvatarList() {
|
|
if worldAvatar.uid != player.PlayerID {
|
|
continue
|
|
}
|
|
if !player.SceneJump && (worldAvatar.avatarEntityId != 0 || worldAvatar.weaponEntityId != 0) {
|
|
continue
|
|
}
|
|
worldAvatar.avatarEntityId = scene.CreateEntityAvatar(player, worldAvatar.avatarId)
|
|
worldAvatar.weaponEntityId = scene.CreateEntityWeapon()
|
|
}
|
|
}
|
|
|
|
// GetPlayerTeamEntityId 获取某玩家的本地队伍实体id
|
|
func (w *World) GetPlayerTeamEntityId(player *model.Player) uint32 {
|
|
return w.multiplayerTeam.localTeamEntityMap[player.PlayerID]
|
|
}
|
|
|
|
// InitPlayerTeamEntityId 初始化某玩家的本地队伍实体id
|
|
func (w *World) InitPlayerTeamEntityId(player *model.Player) {
|
|
w.multiplayerTeam.localTeamEntityMap[player.PlayerID] = w.GetNextWorldEntityId(constant.EntityIdTypeConst.TEAM)
|
|
}
|
|
|
|
// GetPlayerWorldAvatarEntityId 获取某玩家在世界队伍中的某角色的实体id
|
|
func (w *World) GetPlayerWorldAvatarEntityId(player *model.Player, avatarId uint32) uint32 {
|
|
worldAvatar := w.GetPlayerWorldAvatar(player, avatarId)
|
|
if worldAvatar == nil {
|
|
return 0
|
|
}
|
|
return worldAvatar.avatarEntityId
|
|
}
|
|
|
|
// GetPlayerWorldAvatarWeaponEntityId 获取某玩家在世界队伍中的某角色的武器的实体id
|
|
func (w *World) GetPlayerWorldAvatarWeaponEntityId(player *model.Player, avatarId uint32) uint32 {
|
|
worldAvatar := w.GetPlayerWorldAvatar(player, avatarId)
|
|
if worldAvatar == nil {
|
|
return 0
|
|
}
|
|
return worldAvatar.weaponEntityId
|
|
}
|
|
|
|
// GetPlayerAvatarIndex 获取某玩家当前角色索引
|
|
func (w *World) GetPlayerAvatarIndex(player *model.Player) int {
|
|
return w.multiplayerTeam.localAvatarIndexMap[player.PlayerID]
|
|
}
|
|
|
|
// SetPlayerAvatarIndex 设置某玩家当前角色索引
|
|
func (w *World) SetPlayerAvatarIndex(player *model.Player, index int) {
|
|
if index > len(w.GetPlayerLocalTeam(player))-1 {
|
|
return
|
|
}
|
|
w.multiplayerTeam.localAvatarIndexMap[player.PlayerID] = index
|
|
}
|
|
|
|
// GetPlayerActiveAvatarId 获取玩家当前活跃角色id
|
|
func (w *World) GetPlayerActiveAvatarId(player *model.Player) uint32 {
|
|
avatarIndex := w.GetPlayerAvatarIndex(player)
|
|
localTeam := w.GetPlayerLocalTeam(player)
|
|
worldTeamAvatar := localTeam[avatarIndex]
|
|
return worldTeamAvatar.avatarId
|
|
}
|
|
|
|
// GetPlayerAvatarIndexByAvatarId 获取玩家某角色的索引
|
|
func (w *World) GetPlayerAvatarIndexByAvatarId(player *model.Player, avatarId uint32) int {
|
|
localTeam := w.GetPlayerLocalTeam(player)
|
|
for index, worldAvatar := range localTeam {
|
|
if worldAvatar.avatarId == avatarId {
|
|
return index
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
type MultiplayerTeam struct {
|
|
// key:uid value:玩家的本地队伍
|
|
localTeamMap map[uint32][]*WorldAvatar
|
|
// key:uid value:玩家当前角色索引
|
|
localAvatarIndexMap map[uint32]int
|
|
localTeamEntityMap map[uint32]uint32
|
|
// 最终的世界队伍
|
|
worldTeam []*WorldAvatar
|
|
}
|
|
|
|
func CreateMultiplayerTeam() (r *MultiplayerTeam) {
|
|
r = new(MultiplayerTeam)
|
|
r.localTeamMap = make(map[uint32][]*WorldAvatar)
|
|
r.localAvatarIndexMap = make(map[uint32]int)
|
|
r.localTeamEntityMap = make(map[uint32]uint32)
|
|
r.worldTeam = make([]*WorldAvatar, 0)
|
|
return r
|
|
}
|
|
|
|
func (w *World) GetPlayerLocalTeam(player *model.Player) []*WorldAvatar {
|
|
return w.multiplayerTeam.localTeamMap[player.PlayerID]
|
|
}
|
|
|
|
func (w *World) SetPlayerLocalTeam(player *model.Player, avatarIdList []uint32) {
|
|
oldLocalTeam := w.multiplayerTeam.localTeamMap[player.PlayerID]
|
|
sameAvatarIdList := make([]uint32, 0)
|
|
diffAvatarIdList := make([]uint32, 0)
|
|
for _, avatarId := range avatarIdList {
|
|
exist := false
|
|
for _, worldAvatar := range oldLocalTeam {
|
|
if worldAvatar.avatarId == avatarId {
|
|
exist = true
|
|
}
|
|
}
|
|
if exist {
|
|
sameAvatarIdList = append(sameAvatarIdList, avatarId)
|
|
} else {
|
|
diffAvatarIdList = append(diffAvatarIdList, avatarId)
|
|
}
|
|
}
|
|
newLocalTeam := make([]*WorldAvatar, len(avatarIdList))
|
|
for _, avatarId := range sameAvatarIdList {
|
|
for _, worldAvatar := range oldLocalTeam {
|
|
if worldAvatar.avatarId == avatarId {
|
|
index := 0
|
|
for i, v := range avatarIdList {
|
|
if avatarId == v {
|
|
index = i
|
|
}
|
|
}
|
|
newLocalTeam[index] = worldAvatar
|
|
}
|
|
}
|
|
}
|
|
for _, avatarId := range diffAvatarIdList {
|
|
index := 0
|
|
for i, v := range avatarIdList {
|
|
if avatarId == v {
|
|
index = i
|
|
}
|
|
}
|
|
newLocalTeam[index] = &WorldAvatar{
|
|
uid: player.PlayerID,
|
|
avatarId: avatarId,
|
|
avatarEntityId: 0,
|
|
weaponEntityId: 0,
|
|
abilityList: make([]*proto.AbilityAppliedAbility, 0),
|
|
modifierList: make([]*proto.AbilityAppliedModifier, 0),
|
|
}
|
|
}
|
|
w.multiplayerTeam.localTeamMap[player.PlayerID] = newLocalTeam
|
|
}
|
|
|
|
func (w *World) copyLocalTeamToWorld(start int, end int, peerId uint32) {
|
|
player := w.GetPlayerByPeerId(peerId)
|
|
localTeam := w.GetPlayerLocalTeam(player)
|
|
localTeamIndex := 0
|
|
for index := start; index <= end; index++ {
|
|
if localTeamIndex >= len(localTeam) {
|
|
w.multiplayerTeam.worldTeam[index] = &WorldAvatar{
|
|
uid: 0,
|
|
avatarId: 0,
|
|
avatarEntityId: 0,
|
|
weaponEntityId: 0,
|
|
abilityList: nil,
|
|
modifierList: nil,
|
|
}
|
|
continue
|
|
}
|
|
w.multiplayerTeam.worldTeam[index] = localTeam[localTeamIndex]
|
|
localTeamIndex++
|
|
}
|
|
}
|
|
|
|
// TODO 为了实现大世界无限人数写的
|
|
// 现在看来把世界里所有人放进队伍里发给客户端超过8个客户端会崩溃
|
|
// 看来还是不能简单的走通用逻辑 需要对大世界场景队伍做特殊处理 欺骗客户端其他玩家仅仅以场景角色实体的形式出现
|
|
|
|
func (w *World) AddMultiplayerTeam(player *model.Player) {
|
|
if !WORLD_MANAGER.IsBigWorld(w) {
|
|
return
|
|
}
|
|
localTeam := w.GetPlayerLocalTeam(player)
|
|
w.multiplayerTeam.worldTeam = append(w.multiplayerTeam.worldTeam, localTeam...)
|
|
}
|
|
|
|
func (w *World) RemoveMultiplayerTeam(player *model.Player) {
|
|
worldTeam := make([]*WorldAvatar, 0)
|
|
for _, worldAvatar := range w.multiplayerTeam.worldTeam {
|
|
if worldAvatar.uid == player.PlayerID {
|
|
continue
|
|
}
|
|
worldTeam = append(worldTeam, worldAvatar)
|
|
}
|
|
w.multiplayerTeam.worldTeam = worldTeam
|
|
}
|
|
|
|
// UpdateMultiplayerTeam 整合所有玩家的本地队伍计算出世界队伍
|
|
func (w *World) UpdateMultiplayerTeam() {
|
|
playerNum := w.GetWorldPlayerNum()
|
|
if playerNum > 4 {
|
|
return
|
|
}
|
|
w.multiplayerTeam.worldTeam = make([]*WorldAvatar, 4)
|
|
switch playerNum {
|
|
case 1:
|
|
// 1P*4
|
|
w.copyLocalTeamToWorld(0, 3, 1)
|
|
case 2:
|
|
// 1P*2 + 2P*2
|
|
w.copyLocalTeamToWorld(0, 1, 1)
|
|
w.copyLocalTeamToWorld(2, 3, 2)
|
|
case 3:
|
|
// 1P*2 + 2P*1 + 3P*1
|
|
w.copyLocalTeamToWorld(0, 1, 1)
|
|
w.copyLocalTeamToWorld(2, 2, 2)
|
|
w.copyLocalTeamToWorld(3, 3, 3)
|
|
case 4:
|
|
// 1P*1 + 2P*1 + 3P*1 + 4P*1
|
|
w.copyLocalTeamToWorld(0, 0, 1)
|
|
w.copyLocalTeamToWorld(1, 1, 2)
|
|
w.copyLocalTeamToWorld(2, 2, 3)
|
|
w.copyLocalTeamToWorld(3, 3, 4)
|
|
}
|
|
}
|
|
|
|
// 世界聊天
|
|
|
|
func (w *World) AddChat(chatInfo *proto.ChatInfo) {
|
|
w.chatMsgList = append(w.chatMsgList, chatInfo)
|
|
}
|
|
|
|
func (w *World) GetChatList() []*proto.ChatInfo {
|
|
return w.chatMsgList
|
|
}
|
|
|
|
// ChangeToMultiplayer 转换为多人世界
|
|
func (w *World) ChangeToMultiplayer() {
|
|
WORLD_MANAGER.multiplayerWorldNum++
|
|
w.multiplayer = true
|
|
}
|
|
|
|
// IsPlayerFirstEnter 获取玩家是否首次加入本世界
|
|
func (w *World) IsPlayerFirstEnter(player *model.Player) bool {
|
|
_, exist := w.playerFirstEnterMap[player.PlayerID]
|
|
if !exist {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (w *World) PlayerEnter(uid uint32) {
|
|
w.playerFirstEnterMap[uid] = time.Now().UnixMilli()
|
|
}
|
|
|
|
func (w *World) AddWaitPlayer(uid uint32) {
|
|
w.waitEnterPlayerMap[uid] = time.Now().UnixMilli()
|
|
}
|
|
|
|
func (w *World) GetAllWaitPlayer() []uint32 {
|
|
uidList := make([]uint32, 0)
|
|
for uid := range w.waitEnterPlayerMap {
|
|
uidList = append(uidList, uid)
|
|
}
|
|
return uidList
|
|
}
|
|
|
|
func (w *World) RemoveWaitPlayer(uid uint32) {
|
|
delete(w.waitEnterPlayerMap, uid)
|
|
}
|
|
|
|
func (w *World) CreateScene(sceneId uint32) *Scene {
|
|
scene := &Scene{
|
|
id: sceneId,
|
|
world: w,
|
|
playerMap: make(map[uint32]*model.Player),
|
|
entityMap: make(map[uint32]*Entity),
|
|
objectIdEntityMap: make(map[int64]*Entity),
|
|
gameTime: 18 * 60,
|
|
createTime: time.Now().UnixMilli(),
|
|
meeoIndex: 0,
|
|
}
|
|
w.sceneMap[sceneId] = scene
|
|
return scene
|
|
}
|
|
|
|
func (w *World) GetSceneById(sceneId uint32) *Scene {
|
|
scene, exist := w.sceneMap[sceneId]
|
|
if !exist {
|
|
scene = w.CreateScene(sceneId)
|
|
}
|
|
return scene
|
|
}
|
|
|
|
// 场景数据结构
|
|
|
|
type Scene struct {
|
|
id uint32
|
|
world *World
|
|
playerMap map[uint32]*model.Player
|
|
entityMap map[uint32]*Entity
|
|
objectIdEntityMap map[int64]*Entity // 用于标识配置档里的唯一实体是否已被创建
|
|
gameTime uint32 // 游戏内提瓦特大陆的时间
|
|
createTime int64 // 场景创建时间
|
|
meeoIndex uint32 // 客户端风元素染色同步协议的计数器
|
|
}
|
|
|
|
func (s *Scene) GetId() uint32 {
|
|
return s.id
|
|
}
|
|
|
|
func (s *Scene) GetWorld() *World {
|
|
return s.world
|
|
}
|
|
|
|
func (s *Scene) GetAllPlayer() map[uint32]*model.Player {
|
|
return s.playerMap
|
|
}
|
|
|
|
func (s *Scene) GetAllEntity() map[uint32]*Entity {
|
|
return s.entityMap
|
|
}
|
|
|
|
func (s *Scene) GetGameTime() uint32 {
|
|
return s.gameTime
|
|
}
|
|
|
|
func (s *Scene) GetMeeoIndex() uint32 {
|
|
return s.meeoIndex
|
|
}
|
|
|
|
func (s *Scene) SetMeeoIndex(meeoIndex uint32) {
|
|
s.meeoIndex = meeoIndex
|
|
}
|
|
|
|
type AvatarEntity struct {
|
|
uid uint32
|
|
avatarId uint32
|
|
}
|
|
|
|
func (a *AvatarEntity) GetUid() uint32 {
|
|
return a.uid
|
|
}
|
|
|
|
func (a *AvatarEntity) GetAvatarId() uint32 {
|
|
return a.avatarId
|
|
}
|
|
|
|
type MonsterEntity struct {
|
|
monsterId uint32
|
|
}
|
|
|
|
func (m *MonsterEntity) GetMonsterId() uint32 {
|
|
return m.monsterId
|
|
}
|
|
|
|
type NpcEntity struct {
|
|
NpcId uint32
|
|
RoomId uint32
|
|
ParentQuestId uint32
|
|
BlockId uint32
|
|
}
|
|
|
|
const (
|
|
GADGET_TYPE_NORMAL = iota
|
|
GADGET_TYPE_GATHER
|
|
GADGET_TYPE_CLIENT
|
|
GADGET_TYPE_VEHICLE // 载具
|
|
)
|
|
|
|
type GadgetClientEntity struct {
|
|
configId uint32
|
|
campId uint32
|
|
campType uint32
|
|
ownerEntityId uint32
|
|
targetEntityId uint32
|
|
propOwnerEntityId uint32
|
|
}
|
|
|
|
func (g *GadgetClientEntity) GetConfigId() uint32 {
|
|
return g.configId
|
|
}
|
|
|
|
func (g *GadgetClientEntity) GetCampId() uint32 {
|
|
return g.campId
|
|
}
|
|
|
|
func (g *GadgetClientEntity) GetCampType() uint32 {
|
|
return g.campType
|
|
}
|
|
|
|
func (g *GadgetClientEntity) GetOwnerEntityId() uint32 {
|
|
return g.ownerEntityId
|
|
}
|
|
|
|
func (g *GadgetClientEntity) GetTargetEntityId() uint32 {
|
|
return g.targetEntityId
|
|
}
|
|
|
|
func (g *GadgetClientEntity) GetPropOwnerEntityId() uint32 {
|
|
return g.propOwnerEntityId
|
|
}
|
|
|
|
type GadgetGatherEntity struct {
|
|
gatherId uint32
|
|
}
|
|
|
|
func (g *GadgetGatherEntity) GetGatherId() uint32 {
|
|
return g.gatherId
|
|
}
|
|
|
|
type GadgetVehicleEntity struct {
|
|
vehicleId uint32
|
|
owner *model.Player
|
|
maxStamina float32
|
|
curStamina float32
|
|
memberMap map[uint32]*model.Player // uint32 = pos
|
|
}
|
|
|
|
func (g *GadgetVehicleEntity) GetVehicleId() uint32 {
|
|
return g.vehicleId
|
|
}
|
|
|
|
func (g *GadgetVehicleEntity) GetOwner() *model.Player {
|
|
return g.owner
|
|
}
|
|
|
|
func (g *GadgetVehicleEntity) GetMaxStamina() float32 {
|
|
return g.maxStamina
|
|
}
|
|
|
|
func (g *GadgetVehicleEntity) GetCurStamina() float32 {
|
|
return g.curStamina
|
|
}
|
|
|
|
func (g *GadgetVehicleEntity) SetCurStamina(curStamina float32) {
|
|
g.curStamina = curStamina
|
|
}
|
|
|
|
func (g *GadgetVehicleEntity) GetMemberMap() map[uint32]*model.Player {
|
|
return g.memberMap
|
|
}
|
|
|
|
type GadgetEntity struct {
|
|
gadgetType int
|
|
gadgetId uint32
|
|
gadgetClientEntity *GadgetClientEntity
|
|
gadgetGatherEntity *GadgetGatherEntity
|
|
gadgetVehicleEntity *GadgetVehicleEntity
|
|
}
|
|
|
|
func (g *GadgetEntity) GetGadgetType() int {
|
|
return g.gadgetType
|
|
}
|
|
|
|
func (g *GadgetEntity) GetGadgetId() uint32 {
|
|
return g.gadgetId
|
|
}
|
|
|
|
func (g *GadgetEntity) GetGadgetClientEntity() *GadgetClientEntity {
|
|
return g.gadgetClientEntity
|
|
}
|
|
|
|
func (g *GadgetEntity) GetGadgetGatherEntity() *GadgetGatherEntity {
|
|
return g.gadgetGatherEntity
|
|
}
|
|
|
|
func (g *GadgetEntity) GetGadgetVehicleEntity() *GadgetVehicleEntity {
|
|
return g.gadgetVehicleEntity
|
|
}
|
|
|
|
// 场景实体数据结构
|
|
|
|
type Entity struct {
|
|
id uint32
|
|
scene *Scene
|
|
lifeState uint16
|
|
pos *model.Vector
|
|
rot *model.Vector
|
|
moveState uint16
|
|
lastMoveSceneTimeMs uint32
|
|
lastMoveReliableSeq uint32
|
|
fightProp map[uint32]float32
|
|
entityType uint32
|
|
level uint8
|
|
avatarEntity *AvatarEntity
|
|
monsterEntity *MonsterEntity
|
|
npcEntity *NpcEntity
|
|
gadgetEntity *GadgetEntity
|
|
configId uint32
|
|
objectId int64
|
|
}
|
|
|
|
func (e *Entity) GetId() uint32 {
|
|
return e.id
|
|
}
|
|
|
|
func (e *Entity) GetLifeState() uint16 {
|
|
return e.lifeState
|
|
}
|
|
|
|
func (e *Entity) GetPos() *model.Vector {
|
|
return e.pos
|
|
}
|
|
|
|
func (e *Entity) GetRot() *model.Vector {
|
|
return e.rot
|
|
}
|
|
|
|
func (e *Entity) GetMoveState() uint16 {
|
|
return e.moveState
|
|
}
|
|
|
|
func (e *Entity) SetMoveState(moveState uint16) {
|
|
e.moveState = moveState
|
|
}
|
|
|
|
func (e *Entity) GetLastMoveSceneTimeMs() uint32 {
|
|
return e.lastMoveSceneTimeMs
|
|
}
|
|
|
|
func (e *Entity) SetLastMoveSceneTimeMs(lastMoveSceneTimeMs uint32) {
|
|
e.lastMoveSceneTimeMs = lastMoveSceneTimeMs
|
|
}
|
|
|
|
func (e *Entity) GetLastMoveReliableSeq() uint32 {
|
|
return e.lastMoveReliableSeq
|
|
}
|
|
|
|
func (e *Entity) SetLastMoveReliableSeq(lastMoveReliableSeq uint32) {
|
|
e.lastMoveReliableSeq = lastMoveReliableSeq
|
|
}
|
|
|
|
func (e *Entity) GetFightProp() map[uint32]float32 {
|
|
return e.fightProp
|
|
}
|
|
|
|
func (e *Entity) GetEntityType() uint32 {
|
|
return e.entityType
|
|
}
|
|
|
|
func (e *Entity) GetLevel() uint8 {
|
|
return e.level
|
|
}
|
|
|
|
func (e *Entity) GetAvatarEntity() *AvatarEntity {
|
|
return e.avatarEntity
|
|
}
|
|
|
|
func (e *Entity) GetMonsterEntity() *MonsterEntity {
|
|
return e.monsterEntity
|
|
}
|
|
|
|
func (e *Entity) GetNpcEntity() *NpcEntity {
|
|
return e.npcEntity
|
|
}
|
|
|
|
func (e *Entity) GetGadgetEntity() *GadgetEntity {
|
|
return e.gadgetEntity
|
|
}
|
|
|
|
func (e *Entity) GetConfigId() uint32 {
|
|
return e.configId
|
|
}
|
|
|
|
type Attack struct {
|
|
combatInvokeEntry *proto.CombatInvokeEntry
|
|
uid uint32
|
|
}
|
|
|
|
func (s *Scene) ChangeGameTime(time uint32) {
|
|
s.gameTime = time % 1440
|
|
}
|
|
|
|
func (s *Scene) GetSceneCreateTime() int64 {
|
|
return s.createTime
|
|
}
|
|
|
|
func (s *Scene) GetSceneTime() int64 {
|
|
now := time.Now().UnixMilli()
|
|
return now - s.createTime
|
|
}
|
|
|
|
func (s *Scene) AddPlayer(player *model.Player) {
|
|
s.playerMap[player.PlayerID] = player
|
|
s.world.InitPlayerWorldAvatar(player)
|
|
}
|
|
|
|
func (s *Scene) RemovePlayer(player *model.Player) {
|
|
delete(s.playerMap, player.PlayerID)
|
|
worldAvatarList := s.world.GetPlayerWorldAvatarList(player)
|
|
for _, worldAvatar := range worldAvatarList {
|
|
s.DestroyEntity(worldAvatar.avatarEntityId)
|
|
s.DestroyEntity(worldAvatar.weaponEntityId)
|
|
}
|
|
}
|
|
|
|
func (s *Scene) SetEntityLifeState(entity *Entity, lifeState uint16, dieType proto.PlayerDieType) {
|
|
if entity.avatarEntity != nil {
|
|
// 获取玩家对象
|
|
player := USER_MANAGER.GetOnlineUser(entity.avatarEntity.uid)
|
|
if player == nil {
|
|
logger.Error("player is nil, uid: %v", entity.avatarEntity.uid)
|
|
return
|
|
}
|
|
// 获取角色
|
|
avatar, ok := player.AvatarMap[entity.avatarEntity.avatarId]
|
|
if !ok {
|
|
logger.Error("avatar is nil, avatarId: %v", avatar)
|
|
return
|
|
}
|
|
// 设置角色存活状态
|
|
if lifeState == constant.LifeStateConst.LIFE_REVIVE {
|
|
avatar.LifeState = constant.LifeStateConst.LIFE_ALIVE
|
|
// 设置血量
|
|
entity.fightProp[uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP)] = 110
|
|
GAME_MANAGER.EntityFightPropUpdateNotifyBroadcast(s, entity, uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP))
|
|
}
|
|
|
|
// PacketAvatarLifeStateChangeNotify
|
|
avatarLifeStateChangeNotify := &proto.AvatarLifeStateChangeNotify{
|
|
LifeState: uint32(lifeState),
|
|
AttackTag: "",
|
|
DieType: dieType,
|
|
ServerBuffList: nil,
|
|
MoveReliableSeq: entity.lastMoveReliableSeq,
|
|
SourceEntityId: 0,
|
|
AvatarGuid: avatar.Guid,
|
|
}
|
|
for _, p := range s.playerMap {
|
|
GAME_MANAGER.SendMsg(cmd.AvatarLifeStateChangeNotify, p.PlayerID, p.ClientSeq, avatarLifeStateChangeNotify)
|
|
}
|
|
} else {
|
|
// 设置存活状态
|
|
entity.lifeState = lifeState
|
|
|
|
if lifeState == constant.LifeStateConst.LIFE_DEAD {
|
|
// 设置血量
|
|
entity.fightProp[uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP)] = 0
|
|
GAME_MANAGER.EntityFightPropUpdateNotifyBroadcast(s, entity, uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP))
|
|
}
|
|
|
|
// PacketLifeStateChangeNotify
|
|
lifeStateChangeNotify := &proto.LifeStateChangeNotify{
|
|
EntityId: entity.id,
|
|
AttackTag: "",
|
|
MoveReliableSeq: entity.lastMoveReliableSeq,
|
|
DieType: dieType,
|
|
LifeState: uint32(lifeState),
|
|
SourceEntityId: 0,
|
|
}
|
|
for _, p := range s.playerMap {
|
|
GAME_MANAGER.SendMsg(cmd.LifeStateChangeNotify, p.PlayerID, p.ClientSeq, lifeStateChangeNotify)
|
|
}
|
|
|
|
// 删除实体
|
|
s.DestroyEntity(entity.id)
|
|
GAME_MANAGER.RemoveSceneEntityNotifyBroadcast(s, proto.VisionType_VISION_DIE, []uint32{entity.id})
|
|
}
|
|
}
|
|
|
|
func (s *Scene) CreateEntityAvatar(player *model.Player, avatarId uint32) uint32 {
|
|
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.AVATAR)
|
|
avatar, ok := player.AvatarMap[avatarId]
|
|
if !ok {
|
|
logger.Error("avatar error, avatarId: %v", avatar)
|
|
return 0
|
|
}
|
|
entity := &Entity{
|
|
id: entityId,
|
|
scene: s,
|
|
lifeState: avatar.LifeState,
|
|
pos: player.Pos,
|
|
rot: player.Rot,
|
|
moveState: uint16(proto.MotionState_MOTION_NONE),
|
|
lastMoveSceneTimeMs: 0,
|
|
lastMoveReliableSeq: 0,
|
|
// fightProp: player.AvatarMap[avatarId].FightPropMap, // 使用角色结构的数据
|
|
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_AVATAR),
|
|
// level: 0, // 使用角色结构的数据
|
|
avatarEntity: &AvatarEntity{
|
|
uid: player.PlayerID,
|
|
avatarId: avatarId,
|
|
},
|
|
}
|
|
s.CreateEntity(entity, 0)
|
|
MESSAGE_QUEUE.SendToFight(s.world.owner.FightAppId, &mq.NetMsg{
|
|
MsgType: mq.MsgTypeFight,
|
|
EventId: mq.FightRoutineAddEntity,
|
|
FightMsg: &mq.FightMsg{
|
|
FightRoutineId: s.world.id,
|
|
EntityId: entity.id,
|
|
FightPropMap: entity.fightProp,
|
|
Uid: entity.avatarEntity.uid,
|
|
AvatarGuid: player.AvatarMap[avatarId].Guid,
|
|
},
|
|
})
|
|
return entity.id
|
|
}
|
|
|
|
func (s *Scene) CreateEntityWeapon() uint32 {
|
|
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.WEAPON)
|
|
entity := &Entity{
|
|
id: entityId,
|
|
scene: s,
|
|
lifeState: constant.LifeStateConst.LIFE_ALIVE,
|
|
pos: new(model.Vector),
|
|
rot: new(model.Vector),
|
|
moveState: uint16(proto.MotionState_MOTION_NONE),
|
|
lastMoveSceneTimeMs: 0,
|
|
lastMoveReliableSeq: 0,
|
|
fightProp: nil,
|
|
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_WEAPON),
|
|
}
|
|
s.CreateEntity(entity, 0)
|
|
return entity.id
|
|
}
|
|
|
|
func (s *Scene) CreateEntityMonster(pos, rot *model.Vector, monsterId uint32, level uint8, fightProp map[uint32]float32, configId uint32, objectId int64) uint32 {
|
|
_, exist := s.objectIdEntityMap[objectId]
|
|
if exist {
|
|
return 0
|
|
}
|
|
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.MONSTER)
|
|
entity := &Entity{
|
|
id: entityId,
|
|
scene: s,
|
|
lifeState: constant.LifeStateConst.LIFE_ALIVE,
|
|
pos: pos,
|
|
rot: rot,
|
|
moveState: uint16(proto.MotionState_MOTION_NONE),
|
|
lastMoveSceneTimeMs: 0,
|
|
lastMoveReliableSeq: 0,
|
|
fightProp: fightProp,
|
|
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_MONSTER),
|
|
level: level,
|
|
monsterEntity: &MonsterEntity{
|
|
monsterId: monsterId,
|
|
},
|
|
configId: configId,
|
|
objectId: objectId,
|
|
}
|
|
s.CreateEntity(entity, objectId)
|
|
MESSAGE_QUEUE.SendToFight(s.world.owner.FightAppId, &mq.NetMsg{
|
|
MsgType: mq.MsgTypeFight,
|
|
EventId: mq.FightRoutineAddEntity,
|
|
FightMsg: &mq.FightMsg{
|
|
FightRoutineId: s.world.id,
|
|
EntityId: entity.id,
|
|
FightPropMap: entity.fightProp,
|
|
},
|
|
})
|
|
return entity.id
|
|
}
|
|
|
|
func (s *Scene) CreateEntityNpc(pos, rot *model.Vector, npcId, roomId, parentQuestId, blockId, configId uint32, objectId int64) uint32 {
|
|
_, exist := s.objectIdEntityMap[objectId]
|
|
if exist {
|
|
return 0
|
|
}
|
|
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.NPC)
|
|
entity := &Entity{
|
|
id: entityId,
|
|
scene: s,
|
|
lifeState: constant.LifeStateConst.LIFE_ALIVE,
|
|
pos: pos,
|
|
rot: rot,
|
|
moveState: uint16(proto.MotionState_MOTION_NONE),
|
|
lastMoveSceneTimeMs: 0,
|
|
lastMoveReliableSeq: 0,
|
|
fightProp: map[uint32]float32{
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP): math.MaxFloat32,
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_MAX_HP): math.MaxFloat32,
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_HP): float32(1),
|
|
},
|
|
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_NPC),
|
|
npcEntity: &NpcEntity{
|
|
NpcId: npcId,
|
|
RoomId: roomId,
|
|
ParentQuestId: parentQuestId,
|
|
BlockId: blockId,
|
|
},
|
|
configId: configId,
|
|
objectId: objectId,
|
|
}
|
|
s.CreateEntity(entity, objectId)
|
|
return entity.id
|
|
}
|
|
|
|
func (s *Scene) CreateEntityGadgetNormal(pos, rot *model.Vector, gadgetId uint32, configId uint32, objectId int64) uint32 {
|
|
_, exist := s.objectIdEntityMap[objectId]
|
|
if exist {
|
|
return 0
|
|
}
|
|
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.GADGET)
|
|
entity := &Entity{
|
|
id: entityId,
|
|
scene: s,
|
|
lifeState: constant.LifeStateConst.LIFE_ALIVE,
|
|
pos: pos,
|
|
rot: rot,
|
|
moveState: uint16(proto.MotionState_MOTION_NONE),
|
|
lastMoveSceneTimeMs: 0,
|
|
lastMoveReliableSeq: 0,
|
|
fightProp: map[uint32]float32{
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP): math.MaxFloat32,
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_MAX_HP): math.MaxFloat32,
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_HP): float32(1),
|
|
},
|
|
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_GADGET),
|
|
gadgetEntity: &GadgetEntity{
|
|
gadgetId: gadgetId,
|
|
gadgetType: GADGET_TYPE_NORMAL,
|
|
},
|
|
configId: configId,
|
|
objectId: objectId,
|
|
}
|
|
s.CreateEntity(entity, objectId)
|
|
return entity.id
|
|
}
|
|
|
|
func (s *Scene) CreateEntityGadgetGather(pos, rot *model.Vector, gadgetId uint32, gatherId uint32, configId uint32, objectId int64) uint32 {
|
|
_, exist := s.objectIdEntityMap[objectId]
|
|
if exist {
|
|
return 0
|
|
}
|
|
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.GADGET)
|
|
entity := &Entity{
|
|
id: entityId,
|
|
scene: s,
|
|
lifeState: constant.LifeStateConst.LIFE_ALIVE,
|
|
pos: pos,
|
|
rot: rot,
|
|
moveState: uint16(proto.MotionState_MOTION_NONE),
|
|
lastMoveSceneTimeMs: 0,
|
|
lastMoveReliableSeq: 0,
|
|
fightProp: map[uint32]float32{
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP): math.MaxFloat32,
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_MAX_HP): math.MaxFloat32,
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_HP): float32(1),
|
|
},
|
|
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_GADGET),
|
|
gadgetEntity: &GadgetEntity{
|
|
gadgetId: gadgetId,
|
|
gadgetType: GADGET_TYPE_GATHER,
|
|
gadgetGatherEntity: &GadgetGatherEntity{
|
|
gatherId: gatherId,
|
|
},
|
|
},
|
|
configId: configId,
|
|
objectId: objectId,
|
|
}
|
|
s.CreateEntity(entity, objectId)
|
|
return entity.id
|
|
}
|
|
|
|
func (s *Scene) CreateEntityGadgetClient(pos, rot *model.Vector, entityId uint32, configId, campId, campType, ownerEntityId, targetEntityId, propOwnerEntityId uint32) {
|
|
entity := &Entity{
|
|
id: entityId,
|
|
scene: s,
|
|
lifeState: constant.LifeStateConst.LIFE_ALIVE,
|
|
pos: pos,
|
|
rot: rot,
|
|
moveState: uint16(proto.MotionState_MOTION_NONE),
|
|
lastMoveSceneTimeMs: 0,
|
|
lastMoveReliableSeq: 0,
|
|
fightProp: map[uint32]float32{
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP): math.MaxFloat32,
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_MAX_HP): math.MaxFloat32,
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_HP): float32(1),
|
|
},
|
|
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_GADGET),
|
|
gadgetEntity: &GadgetEntity{
|
|
gadgetType: GADGET_TYPE_CLIENT,
|
|
gadgetClientEntity: &GadgetClientEntity{
|
|
configId: configId,
|
|
campId: campId,
|
|
campType: campType,
|
|
ownerEntityId: ownerEntityId,
|
|
targetEntityId: targetEntityId,
|
|
propOwnerEntityId: propOwnerEntityId,
|
|
},
|
|
},
|
|
}
|
|
s.CreateEntity(entity, 0)
|
|
}
|
|
|
|
func (s *Scene) CreateEntityGadgetVehicle(uid uint32, pos, rot *model.Vector, vehicleId uint32) uint32 {
|
|
player := USER_MANAGER.GetOnlineUser(uid)
|
|
if player == nil {
|
|
logger.Error("player is nil, uid: %v", uid)
|
|
return 0
|
|
}
|
|
entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.GADGET)
|
|
entity := &Entity{
|
|
id: entityId,
|
|
scene: s,
|
|
lifeState: constant.LifeStateConst.LIFE_ALIVE,
|
|
pos: pos,
|
|
rot: rot,
|
|
moveState: uint16(proto.MotionState_MOTION_NONE),
|
|
lastMoveSceneTimeMs: 0,
|
|
lastMoveReliableSeq: 0,
|
|
fightProp: map[uint32]float32{
|
|
// TODO 以后使用配置表
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_CUR_HP): 114514,
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_MAX_HP): 114514,
|
|
uint32(constant.FightPropertyConst.FIGHT_PROP_BASE_HP): float32(1),
|
|
},
|
|
entityType: uint32(proto.ProtEntityType_PROT_ENTITY_GADGET),
|
|
gadgetEntity: &GadgetEntity{
|
|
gadgetType: GADGET_TYPE_VEHICLE,
|
|
gadgetVehicleEntity: &GadgetVehicleEntity{
|
|
vehicleId: vehicleId,
|
|
owner: player,
|
|
maxStamina: 240, // TODO 应该也能在配置表找到
|
|
curStamina: 240, // TODO 与maxStamina一致
|
|
memberMap: make(map[uint32]*model.Player),
|
|
},
|
|
},
|
|
}
|
|
s.CreateEntity(entity, 0)
|
|
return entity.id
|
|
}
|
|
|
|
func (s *Scene) CreateEntity(entity *Entity, objectId int64) {
|
|
if len(s.entityMap) >= ENTITY_MAX_SEND_NUM && !ENTITY_NUM_UNLIMIT {
|
|
logger.Error("above max scene entity num limit: %v, id: %v, pos: %v", ENTITY_MAX_SEND_NUM, entity.id, entity.pos)
|
|
return
|
|
}
|
|
if objectId != 0 {
|
|
s.objectIdEntityMap[objectId] = entity
|
|
}
|
|
s.entityMap[entity.id] = entity
|
|
}
|
|
|
|
func (s *Scene) DestroyEntity(entityId uint32) {
|
|
entity := s.GetEntity(entityId)
|
|
if entity == nil {
|
|
return
|
|
}
|
|
delete(s.entityMap, entity.id)
|
|
delete(s.objectIdEntityMap, entity.objectId)
|
|
MESSAGE_QUEUE.SendToFight(s.world.owner.FightAppId, &mq.NetMsg{
|
|
MsgType: mq.MsgTypeFight,
|
|
EventId: mq.FightRoutineDelEntity,
|
|
FightMsg: &mq.FightMsg{
|
|
FightRoutineId: s.world.id,
|
|
EntityId: entity.id,
|
|
},
|
|
})
|
|
}
|
|
|
|
func (s *Scene) GetEntity(entityId uint32) *Entity {
|
|
return s.entityMap[entityId]
|
|
}
|
|
|
|
func (s *Scene) GetEntityByObjectId(objectId int64) *Entity {
|
|
return s.objectIdEntityMap[objectId]
|
|
}
|