游泳溺水初步

This commit is contained in:
UnKownOwO
2022-12-20 15:12:42 +08:00
parent 61e8ed92b1
commit 391738e29a
11 changed files with 185 additions and 32 deletions

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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{
{

View File

@@ -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)

View File

@@ -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)

View File

@@ -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),

View File

@@ -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,

View File

@@ -14,6 +14,7 @@ type StaminaInfo struct {
LastSkillId uint32 // 最后释放的技能Id
LastSkillTime int64 // 最后释放技能的时间
LastSkillStartTime int64 // 最后执行开始技能耐力消耗的时间
DrownBackTime int64 // 溺水返回安全点的时间
}
// SetStaminaCost 设置动作需要消耗的耐力

View File

@@ -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{}) // 玩家背包数据通知