From 391738e29a41b288b29a3333016740c57ef42cd3 Mon Sep 17 00:00:00 2001 From: UnKownOwO <80520429@qq.com> Date: Tue, 20 Dec 2022 15:12:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=B8=E6=B3=B3=E6=BA=BA=E6=B0=B4=E5=88=9D?= =?UTF-8?q?=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gs/constant/stamina_cost.go | 2 +- gs/game/tick_manager.go | 1 + gs/game/user_avatar.go | 8 ++- gs/game/user_fight_sync.go | 21 +++++-- gs/game/user_scene.go | 11 ++++ gs/game/user_stamina.go | 82 +++++++++++++++++++++++----- gs/game/user_vehicle.go | 4 +- gs/game/world_manager.go | 67 +++++++++++++++++++++++ gs/model/avatar.go | 2 + gs/model/stamina.go | 1 + protocol/cmd/cmd_id_proto_obj_map.go | 18 +++--- 11 files changed, 185 insertions(+), 32 deletions(-) diff --git a/gs/constant/stamina_cost.go b/gs/constant/stamina_cost.go index 93878ac0..762002fd 100644 --- a/gs/constant/stamina_cost.go +++ b/gs/constant/stamina_cost.go @@ -42,7 +42,7 @@ func InitStaminaCostConst() { StaminaCostConst.DASH = -360 StaminaCostConst.FLY = -60 StaminaCostConst.SPRINT = -1800 - StaminaCostConst.SWIM_DASH_START = -2000 + StaminaCostConst.SWIM_DASH_START = -200 StaminaCostConst.SWIM_DASH = -204 StaminaCostConst.SWIMMING = -400 StaminaCostConst.POWERED_FLY = 500 diff --git a/gs/game/tick_manager.go b/gs/game/tick_manager.go index cab74735..d17e6d14 100644 --- a/gs/game/tick_manager.go +++ b/gs/game/tick_manager.go @@ -233,6 +233,7 @@ func (t *TickManager) onTick200MilliSecond(now int64) { for _, player := range world.playerMap { GAME_MANAGER.SustainStaminaHandler(player) GAME_MANAGER.VehicleRestoreStaminaHandler(player) + GAME_MANAGER.DrownBackHandler(player) } } } diff --git a/gs/game/user_avatar.go b/gs/game/user_avatar.go index f3247123..1f96c5cf 100644 --- a/gs/game/user_avatar.go +++ b/gs/game/user_avatar.go @@ -239,7 +239,11 @@ func (g *GameManager) UpdateUserAvatarFightProp(userId uint32, avatarId uint32) logger.Error("player is nil, uid: %v", userId) return } - avatar := player.AvatarMap[avatarId] + avatar, ok := player.AvatarMap[avatarId] + if !ok { + logger.Error("avatar is nil, avatarId: %v", avatar) + return + } avatarFightPropNotify := &proto.AvatarFightPropNotify{ AvatarGuid: avatar.Guid, FightPropMap: avatar.FightPropMap, @@ -283,7 +287,7 @@ func (g *GameManager) PacketAvatarInfo(avatar *model.Avatar) *proto.AvatarInfo { Value: &proto.PropValue_Ival{Ival: 0}, }, }, - LifeState: 1, + LifeState: uint32(avatar.LifeState), EquipGuidList: object.ConvMapToList(avatar.EquipGuidList), FightPropMap: nil, SkillDepotId: avatar.SkillDepotId, diff --git a/gs/game/user_fight_sync.go b/gs/game/user_fight_sync.go index 4b8ef5e8..eb2fb0a7 100644 --- a/gs/game/user_fight_sync.go +++ b/gs/game/user_fight_sync.go @@ -113,12 +113,21 @@ func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg p if sceneEntity.avatarEntity != nil { // 玩家实体在移动 // 更新玩家的位置信息 - player.Pos.X = float64(motionInfo.Pos.X) - player.Pos.Y = float64(motionInfo.Pos.Y) - player.Pos.Z = float64(motionInfo.Pos.Z) - player.Rot.X = float64(motionInfo.Rot.X) - player.Rot.Y = float64(motionInfo.Rot.Y) - player.Rot.Z = float64(motionInfo.Rot.Z) + + switch motionInfo.State { + case proto.MotionState_MOTION_STATE_DANGER_RUN, proto.MotionState_MOTION_STATE_RUN, + proto.MotionState_MOTION_STATE_DANGER_STANDBY_MOVE, proto.MotionState_MOTION_STATE_DANGER_STANDBY, proto.MotionState_MOTION_STATE_LADDER_TO_STANDBY, proto.MotionState_MOTION_STATE_STANDBY_MOVE, proto.MotionState_MOTION_STATE_STANDBY, + proto.MotionState_MOTION_STATE_DANGER_WALK, proto.MotionState_MOTION_STATE_WALK, + proto.MotionState_MOTION_STATE_DASH: + // 为了实现游泳返回安全位置 但是我觉得这么做肯定会出问题 + player.Pos.X = float64(motionInfo.Pos.X) + player.Pos.Y = float64(motionInfo.Pos.Y) + player.Pos.Z = float64(motionInfo.Pos.Z) + player.Rot.X = float64(motionInfo.Rot.X) + player.Rot.Y = float64(motionInfo.Rot.Y) + player.Rot.Z = float64(motionInfo.Rot.Z) + + } // 处理耐力消耗 g.ImmediateStamina(player, motionInfo.State) diff --git a/gs/game/user_scene.go b/gs/game/user_scene.go index 98fc4590..a8d79243 100644 --- a/gs/game/user_scene.go +++ b/gs/game/user_scene.go @@ -476,6 +476,17 @@ func (g *GameManager) AddSceneEntityNotify(player *model.Player, visionType prot } } +func (g *GameManager) EntityFightPropUpdateNotifyBroadcast(scene *Scene, entity *Entity, fightPropId uint32) { + for _, player := range scene.playerMap { + // PacketEntityFightPropUpdateNotify + entityFightPropUpdateNotify := new(proto.EntityFightPropUpdateNotify) + entityFightPropUpdateNotify.EntityId = entity.id + entityFightPropUpdateNotify.FightPropMap = make(map[uint32]float32) + entityFightPropUpdateNotify.FightPropMap[fightPropId] = entity.fightProp[fightPropId] + g.SendMsg(cmd.EntityFightPropUpdateNotify, player.PlayerID, player.ClientSeq, entityFightPropUpdateNotify) + } +} + func (g *GameManager) PacketFightPropMapToPbFightPropList(fightPropMap map[uint32]float32) []*proto.FightPropPair { fightPropList := []*proto.FightPropPair{ { diff --git a/gs/game/user_stamina.go b/gs/game/user_stamina.go index b3690d60..4e5ef3fe 100644 --- a/gs/game/user_stamina.go +++ b/gs/game/user_stamina.go @@ -340,10 +340,6 @@ func (g *GameManager) GetChangeStamina(curStamina int32, maxStamina int32, stami // UpdateVehicleStamina 更新载具耐力 func (g *GameManager) UpdateVehicleStamina(player *model.Player, vehicleEntity *Entity, staminaCost int32) { - // 耐力增加0是没有意义的 - if staminaCost == 0 { - return - } staminaInfo := player.StaminaInfo // 添加的耐力大于0为恢复 if staminaCost > 0 { @@ -385,11 +381,6 @@ func (g *GameManager) UpdateVehicleStamina(player *model.Player, vehicleEntity * // UpdatePlayerStamina 更新玩家耐力 func (g *GameManager) UpdatePlayerStamina(player *model.Player, staminaCost int32) { - // 耐力增加0是没有意义的 - if staminaCost == 0 { - return - } - staminaInfo := player.StaminaInfo // 添加的耐力大于0为恢复 if staminaCost > 0 { @@ -413,6 +404,9 @@ func (g *GameManager) UpdatePlayerStamina(player *model.Player, staminaCost int3 // 将被变更的耐力 stamina := g.GetChangeStamina(curStamina, maxStamina, staminaCost) + // 检测玩家是否没耐力后执行溺水 + g.HandleDrown(player, stamina) + // 当前无变动不要频繁发包 if uint32(curStamina) == stamina { return @@ -422,6 +416,63 @@ func (g *GameManager) UpdatePlayerStamina(player *model.Player, staminaCost int3 g.SetPlayerStamina(player, stamina) } +// DrownBackHandler 玩家溺水返回安全点 +func (g *GameManager) DrownBackHandler(player *model.Player) { + // 溺水返回时间为0代表不进行返回 + if player.StaminaInfo.DrownBackTime == 0 { + return + } + if time.Now().UnixMilli() >= player.StaminaInfo.DrownBackTime { + world := WORLD_MANAGER.GetWorldByID(player.WorldId) + scene := world.GetSceneById(player.SceneId) + activeAvatar := world.GetPlayerWorldAvatar(player, player.TeamConfig.GetActiveAvatarId()) + avatarEntity := scene.GetEntity(activeAvatar.avatarEntityId) + if avatarEntity == nil { + logger.Error("avatar entity is nil, entityId: %v", activeAvatar.avatarEntityId) + return + } + // TODO 目前存在的问题 + // 传送会显示玩家实体后再传送 返回安全位置写的不是很好可能存在问题 + // 官服 游戏离线也会回到安全位置 + + // 设置角色存活 + scene.SetEntityLifeState(avatarEntity, constant.LifeStateConst.LIFE_ALIVE, proto.PlayerDieType_PLAYER_DIE_TYPE_NONE) + // 传送玩家至安全位置 + g.TeleportPlayer(player, player.SceneId, player.Pos) + + // 重置溺水返回时间 + player.StaminaInfo.DrownBackTime = 0 + // 重置动作状态否则可能会导致多次溺水 + player.StaminaInfo.State = 0 + } +} + +// HandleDrown 处理玩家溺水 +func (g *GameManager) HandleDrown(player *model.Player, stamina uint32) { + // 溺水需要耐力等于0 返回时间不等于0代表已处理过溺水正在等待返回 + if stamina != 0 || player.StaminaInfo.DrownBackTime != 0 { + return + } + + world := WORLD_MANAGER.GetWorldByID(player.WorldId) + scene := world.GetSceneById(player.SceneId) + activeAvatar := world.GetPlayerWorldAvatar(player, player.TeamConfig.GetActiveAvatarId()) + avatarEntity := scene.GetEntity(activeAvatar.avatarEntityId) + if avatarEntity == nil { + logger.Error("avatar entity is nil, entityId: %v", activeAvatar.avatarEntityId) + return + } + + // 确保玩家正在游泳 + if player.StaminaInfo.State == proto.MotionState_MOTION_STATE_SWIM_MOVE || player.StaminaInfo.State == proto.MotionState_MOTION_STATE_SWIM_DASH { + logger.Debug("player drown, curStamina: %v, state: %v", stamina, player.StaminaInfo.State) + // 设置角色为死亡 + scene.SetEntityLifeState(avatarEntity, constant.LifeStateConst.LIFE_DEAD, proto.PlayerDieType_PLAYER_DIE_TYPE_DRAWN) + // 溺水返回安全点的时间 + player.StaminaInfo.DrownBackTime = time.Now().Add(time.Second * 4).UnixMilli() + } +} + // SetVehicleStamina 设置载具耐力 func (g *GameManager) SetVehicleStamina(player *model.Player, vehicleEntity *Entity, stamina float32) { // 设置载具的耐力 @@ -442,14 +493,19 @@ func (g *GameManager) SetPlayerStamina(player *model.Player, stamina uint32) { player.PropertiesMap[prop] = stamina //logger.Debug("player stamina set, stamina: %v", stamina) + // PacketPlayerPropNotify + g.PlayerPropNotify(player, prop) +} + +func (g *GameManager) PlayerPropNotify(player *model.Player, playerPropId uint16) { // PacketPlayerPropNotify playerPropNotify := new(proto.PlayerPropNotify) playerPropNotify.PropMap = make(map[uint32]*proto.PropValue) - playerPropNotify.PropMap[uint32(prop)] = &proto.PropValue{ - Type: uint32(prop), - Val: int64(player.PropertiesMap[prop]), + playerPropNotify.PropMap[uint32(playerPropId)] = &proto.PropValue{ + Type: uint32(playerPropId), + Val: int64(player.PropertiesMap[playerPropId]), Value: &proto.PropValue_Ival{ - Ival: int64(player.PropertiesMap[prop]), + Ival: int64(player.PropertiesMap[playerPropId]), }, } g.SendMsg(cmd.PlayerPropNotify, player.PlayerID, player.ClientSeq, playerPropNotify) diff --git a/gs/game/user_vehicle.go b/gs/game/user_vehicle.go index dbfbe6d4..eb0a5600 100644 --- a/gs/game/user_vehicle.go +++ b/gs/game/user_vehicle.go @@ -55,7 +55,7 @@ func (g *GameManager) CreateVehicleReq(player *model.Player, payloadMsg pb.Messa g.CommonRetError(cmd.VehicleInteractRsp, player, &proto.VehicleInteractRsp{}) return } - GAME_MANAGER.AddSceneEntityNotify(player, proto.VisionType_VISION_TYPE_BORN, []uint32{entityId}, true, false) + GAME_MANAGER.AddSceneEntityNotifyBroadcast(player, scene, proto.VisionType_VISION_TYPE_BORN, []*proto.SceneEntityInfo{GAME_MANAGER.PacketSceneEntityInfoGadget(scene, entityId)}, false) // 记录创建的载具信息 player.VehicleInfo.LastCreateEntityIdMap[req.VehicleId] = entityId @@ -101,7 +101,7 @@ func (g *GameManager) DestroyVehicleEntity(player *model.Player, scene *Scene, v if entity.gadgetEntity.gadgetVehicleEntity.owner != player { return } - // 确保玩家正在载具中 + // 如果玩家正在载具中 if g.IsPlayerInVehicle(player, entity.gadgetEntity.gadgetVehicleEntity) { // 离开载具 g.ExitVehicle(player, entity, player.AvatarMap[player.TeamConfig.GetActiveAvatarId()].Guid) diff --git a/gs/game/world_manager.go b/gs/game/world_manager.go index 0370a186..215d494f 100644 --- a/gs/game/world_manager.go +++ b/gs/game/world_manager.go @@ -1,6 +1,7 @@ package game import ( + "hk4e/protocol/cmd" "math" "time" @@ -572,6 +573,7 @@ type GadgetEntity struct { type Entity struct { id uint32 scene *Scene + lifeState uint16 pos *model.Vector rot *model.Vector moveState uint16 @@ -617,11 +619,71 @@ func (s *Scene) RemovePlayer(player *model.Player) { } } +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 + } + // 设置角色存活状态 + avatar.LifeState = lifeState + + // PacketAvatarLifeStateChangeNotify + avatarLifeStateChangeNotify := &proto.AvatarLifeStateChangeNotify{ + LifeState: uint32(avatar.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(entity.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_TYPE_DIE, []uint32{entity.id}) + } +} + func (s *Scene) CreateEntityAvatar(player *model.Player, avatarId uint32) uint32 { entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.AVATAR) entity := &Entity{ id: entityId, scene: s, + lifeState: constant.LifeStateConst.LIFE_ALIVE, pos: player.Pos, rot: player.Rot, moveState: uint16(proto.MotionState_MOTION_STATE_NONE), @@ -658,6 +720,7 @@ func (s *Scene) CreateEntityWeapon() uint32 { entity := &Entity{ id: entityId, scene: s, + lifeState: constant.LifeStateConst.LIFE_ALIVE, pos: new(model.Vector), rot: new(model.Vector), moveState: uint16(proto.MotionState_MOTION_STATE_NONE), @@ -676,6 +739,7 @@ func (s *Scene) CreateEntityMonster(pos *model.Vector, level uint8, fightProp ma entity := &Entity{ id: entityId, scene: s, + lifeState: constant.LifeStateConst.LIFE_ALIVE, pos: pos, rot: new(model.Vector), moveState: uint16(proto.MotionState_MOTION_STATE_NONE), @@ -703,6 +767,7 @@ func (s *Scene) ClientCreateEntityGadget(pos, rot *model.Vector, entityId uint32 entity := &Entity{ id: entityId, scene: s, + lifeState: constant.LifeStateConst.LIFE_ALIVE, pos: pos, rot: rot, moveState: uint16(proto.MotionState_MOTION_STATE_NONE), @@ -736,6 +801,7 @@ func (s *Scene) CreateEntityGadget(pos *model.Vector, gatherId uint32) uint32 { entity := &Entity{ id: entityId, scene: s, + lifeState: constant.LifeStateConst.LIFE_ALIVE, pos: pos, rot: new(model.Vector), moveState: uint16(proto.MotionState_MOTION_STATE_NONE), @@ -770,6 +836,7 @@ func (s *Scene) CreateEntityGadgetVehicle(uid uint32, pos, rot *model.Vector, ve entity := &Entity{ id: entityId, scene: s, + lifeState: constant.LifeStateConst.LIFE_ALIVE, pos: pos, rot: rot, moveState: uint16(proto.MotionState_MOTION_STATE_NONE), diff --git a/gs/model/avatar.go b/gs/model/avatar.go index 5d265434..9ebdc360 100644 --- a/gs/model/avatar.go +++ b/gs/model/avatar.go @@ -10,6 +10,7 @@ import ( type Avatar struct { AvatarId uint32 `bson:"avatarId"` // 角色id + LifeState uint16 `bson:"lifeState"` // 存活状态 Level uint8 `bson:"level"` // 等级 Exp uint32 `bson:"exp"` // 经验值 Promote uint8 `bson:"promote"` // 突破等阶 @@ -110,6 +111,7 @@ func (p *Player) AddAvatar(avatarId uint32) { } avatar := &Avatar{ AvatarId: avatarId, + LifeState: constant.LifeStateConst.LIFE_ALIVE, Level: 1, Exp: 0, Promote: 0, diff --git a/gs/model/stamina.go b/gs/model/stamina.go index 243325f7..56c32abc 100644 --- a/gs/model/stamina.go +++ b/gs/model/stamina.go @@ -14,6 +14,7 @@ type StaminaInfo struct { LastSkillId uint32 // 最后释放的技能Id LastSkillTime int64 // 最后释放技能的时间 LastSkillStartTime int64 // 最后执行开始技能耐力消耗的时间 + DrownBackTime int64 // 溺水返回安全点的时间 } // SetStaminaCost 设置动作需要消耗的耐力 diff --git a/protocol/cmd/cmd_id_proto_obj_map.go b/protocol/cmd/cmd_id_proto_obj_map.go index 8e6148f9..f2178984 100644 --- a/protocol/cmd/cmd_id_proto_obj_map.go +++ b/protocol/cmd/cmd_id_proto_obj_map.go @@ -113,6 +113,7 @@ func (c *CmdProtoMap) registerAllMessage() { c.registerMessage(EvtEntityRenderersChangedNotify, &proto.EvtEntityRenderersChangedNotify{}) // 实体可视状态改变通知 c.registerMessage(EvtCreateGadgetNotify, &proto.EvtCreateGadgetNotify{}) // 创建实体通知 c.registerMessage(EvtDestroyGadgetNotify, &proto.EvtDestroyGadgetNotify{}) // 销毁实体通知 + c.registerMessage(LifeStateChangeNotify, &proto.LifeStateChangeNotify{}) // 实体存活状态改变通知 // 队伍 c.registerMessage(ChangeAvatarReq, &proto.ChangeAvatarReq{}) // 更换角色请求 切人 @@ -199,14 +200,15 @@ func (c *CmdProtoMap) registerAllMessage() { c.registerMessage(DoGachaRsp, &proto.DoGachaRsp{}) // 抽卡响应 // 角色 - c.registerMessage(AvatarDataNotify, &proto.AvatarDataNotify{}) // 角色信息通知 - c.registerMessage(AvatarAddNotify, &proto.AvatarAddNotify{}) // 角色新增通知 - c.registerMessage(AvatarChangeCostumeReq, &proto.AvatarChangeCostumeReq{}) // 角色换装请求 - c.registerMessage(AvatarChangeCostumeRsp, &proto.AvatarChangeCostumeRsp{}) // 角色换装响应 - c.registerMessage(AvatarChangeCostumeNotify, &proto.AvatarChangeCostumeNotify{}) // 角色换装通知 - c.registerMessage(AvatarWearFlycloakReq, &proto.AvatarWearFlycloakReq{}) // 角色换风之翼请求 - c.registerMessage(AvatarWearFlycloakRsp, &proto.AvatarWearFlycloakRsp{}) // 角色换风之翼响应 - c.registerMessage(AvatarFlycloakChangeNotify, &proto.AvatarFlycloakChangeNotify{}) // 角色换风之翼通知 + c.registerMessage(AvatarDataNotify, &proto.AvatarDataNotify{}) // 角色信息通知 + c.registerMessage(AvatarAddNotify, &proto.AvatarAddNotify{}) // 角色新增通知 + c.registerMessage(AvatarChangeCostumeReq, &proto.AvatarChangeCostumeReq{}) // 角色换装请求 + c.registerMessage(AvatarChangeCostumeRsp, &proto.AvatarChangeCostumeRsp{}) // 角色换装响应 + c.registerMessage(AvatarChangeCostumeNotify, &proto.AvatarChangeCostumeNotify{}) // 角色换装通知 + c.registerMessage(AvatarWearFlycloakReq, &proto.AvatarWearFlycloakReq{}) // 角色换风之翼请求 + c.registerMessage(AvatarWearFlycloakRsp, &proto.AvatarWearFlycloakRsp{}) // 角色换风之翼响应 + c.registerMessage(AvatarFlycloakChangeNotify, &proto.AvatarFlycloakChangeNotify{}) // 角色换风之翼通知 + c.registerMessage(AvatarLifeStateChangeNotify, &proto.AvatarLifeStateChangeNotify{}) // 角色存活状态改变通知 // 背包与道具 c.registerMessage(PlayerStoreNotify, &proto.PlayerStoreNotify{}) // 玩家背包数据通知