mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-04 14:32:26 +08:00
802 lines
30 KiB
Go
802 lines
30 KiB
Go
package game
|
||
|
||
import (
|
||
"strings"
|
||
"time"
|
||
|
||
"hk4e/common/constant"
|
||
"hk4e/gdconf"
|
||
"hk4e/gs/model"
|
||
"hk4e/pkg/logger"
|
||
"hk4e/pkg/reflection"
|
||
"hk4e/protocol/cmd"
|
||
"hk4e/protocol/proto"
|
||
|
||
pb "google.golang.org/protobuf/proto"
|
||
)
|
||
|
||
var cmdProtoMap *cmd.CmdProtoMap = nil
|
||
|
||
func DoForward[IET model.InvokeEntryType](player *model.Player, invokeHandler *model.InvokeHandler[IET],
|
||
cmdId uint16, newNtf pb.Message, forwardField string,
|
||
srcNtf pb.Message, copyFieldList []string) {
|
||
if cmdProtoMap == nil {
|
||
cmdProtoMap = cmd.NewCmdProtoMap()
|
||
}
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
if srcNtf != nil && copyFieldList != nil {
|
||
for _, fieldName := range copyFieldList {
|
||
reflection.CopyStructField(newNtf, srcNtf, fieldName)
|
||
}
|
||
}
|
||
if invokeHandler.AllLen() == 0 && invokeHandler.AllExceptCurLen() == 0 && invokeHandler.HostLen() == 0 {
|
||
return
|
||
}
|
||
if invokeHandler.AllLen() > 0 {
|
||
reflection.SetStructFieldValue(newNtf, forwardField, invokeHandler.EntryListForwardAll)
|
||
GAME.SendToSceneA(scene, cmdId, player.ClientSeq, newNtf)
|
||
}
|
||
if invokeHandler.AllExceptCurLen() > 0 {
|
||
reflection.SetStructFieldValue(newNtf, forwardField, invokeHandler.EntryListForwardAllExceptCur)
|
||
GAME.SendToSceneAEC(scene, cmdId, player.ClientSeq, newNtf, player.PlayerID)
|
||
}
|
||
if invokeHandler.HostLen() > 0 {
|
||
reflection.SetStructFieldValue(newNtf, forwardField, invokeHandler.EntryListForwardHost)
|
||
GAME.SendToWorldH(world, cmdId, player.ClientSeq, newNtf)
|
||
}
|
||
}
|
||
|
||
func (g *Game) UnionCmdNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.UnionCmdNotify)
|
||
_ = req
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
DoForward[proto.CombatInvokeEntry](player, player.CombatInvokeHandler,
|
||
cmd.CombatInvocationsNotify, new(proto.CombatInvocationsNotify), "InvokeList",
|
||
nil, nil)
|
||
DoForward[proto.AbilityInvokeEntry](player, player.AbilityInvokeHandler,
|
||
cmd.AbilityInvocationsNotify, new(proto.AbilityInvocationsNotify), "Invokes",
|
||
nil, nil)
|
||
player.CombatInvokeHandler.Clear()
|
||
player.AbilityInvokeHandler.Clear()
|
||
}
|
||
|
||
func (g *Game) MassiveEntityElementOpBatchNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.MassiveEntityElementOpBatchNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
req.OpIdx = scene.GetMeeoIndex()
|
||
scene.SetMeeoIndex(scene.GetMeeoIndex() + 1)
|
||
g.SendToSceneA(scene, cmd.MassiveEntityElementOpBatchNotify, player.ClientSeq, req)
|
||
}
|
||
|
||
func (g *Game) CombatInvocationsNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.CombatInvocationsNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
for _, entry := range req.InvokeList {
|
||
switch entry.ArgumentType {
|
||
case proto.CombatTypeArgument_COMBAT_EVT_BEING_HIT:
|
||
evtBeingHitInfo := new(proto.EvtBeingHitInfo)
|
||
err := pb.Unmarshal(entry.CombatData, evtBeingHitInfo)
|
||
if err != nil {
|
||
logger.Error("parse EvtBeingHitInfo error: %v", err)
|
||
break
|
||
}
|
||
// logger.Debug("EvtBeingHitInfo: %v, ForwardType: %v", evtBeingHitInfo, entry.ForwardType)
|
||
attackResult := evtBeingHitInfo.AttackResult
|
||
if attackResult == nil {
|
||
logger.Error("attackResult is nil")
|
||
break
|
||
}
|
||
target := scene.GetEntity(attackResult.DefenseId)
|
||
if target == nil {
|
||
logger.Error("could not found target, defense id: %v", attackResult.DefenseId)
|
||
break
|
||
}
|
||
fightProp := target.GetFightProp()
|
||
currHp := fightProp[constant.FIGHT_PROP_CUR_HP]
|
||
currHp -= attackResult.Damage
|
||
if currHp < 0 {
|
||
currHp = 0
|
||
}
|
||
fightProp[constant.FIGHT_PROP_CUR_HP] = currHp
|
||
g.EntityFightPropUpdateNotifyBroadcast(scene, target)
|
||
switch target.GetEntityType() {
|
||
case constant.ENTITY_TYPE_AVATAR:
|
||
case constant.ENTITY_TYPE_MONSTER:
|
||
if currHp == 0 {
|
||
g.KillEntity(player, scene, target.GetId(), proto.PlayerDieType_PLAYER_DIE_GM)
|
||
}
|
||
case constant.ENTITY_TYPE_GADGET:
|
||
gadgetEntity := target.GetGadgetEntity()
|
||
gadgetDataConfig := gdconf.GetGadgetDataById(int32(gadgetEntity.GetGadgetId()))
|
||
if gadgetDataConfig == nil {
|
||
logger.Error("get gadget data config is nil, gadgetId: %v", gadgetEntity.GetGadgetId())
|
||
break
|
||
}
|
||
logger.Debug("[EvtBeingHit] GadgetData: %+v, EntityId: %v, uid: %v", gadgetDataConfig, target.GetId(), player.PlayerID)
|
||
g.handleGadgetEntityBeHitLow(player, target, attackResult.ElementType)
|
||
}
|
||
case proto.CombatTypeArgument_ENTITY_MOVE:
|
||
entityMoveInfo := new(proto.EntityMoveInfo)
|
||
err := pb.Unmarshal(entry.CombatData, entityMoveInfo)
|
||
if err != nil {
|
||
logger.Error("parse EntityMoveInfo error: %v", err)
|
||
break
|
||
}
|
||
// logger.Debug("EntityMoveInfo: %v, ForwardType: %v", entityMoveInfo, entry.ForwardType)
|
||
motionInfo := entityMoveInfo.MotionInfo
|
||
if motionInfo.Pos == nil || motionInfo.Rot == nil {
|
||
break
|
||
}
|
||
sceneEntity := scene.GetEntity(entityMoveInfo.EntityId)
|
||
if sceneEntity == nil {
|
||
break
|
||
}
|
||
if sceneEntity.GetEntityType() == constant.ENTITY_TYPE_AVATAR {
|
||
// 玩家实体在移动
|
||
g.SceneBlockAoiPlayerMove(player, world, scene, player.Pos,
|
||
&model.Vector{X: float64(motionInfo.Pos.X), Y: float64(motionInfo.Pos.Y), Z: float64(motionInfo.Pos.Z)},
|
||
sceneEntity.GetId(),
|
||
)
|
||
if WORLD_MANAGER.IsBigWorld(world) {
|
||
g.BigWorldAoiPlayerMove(player, world, scene, player.Pos,
|
||
&model.Vector{X: float64(motionInfo.Pos.X), Y: float64(motionInfo.Pos.Y), Z: float64(motionInfo.Pos.Z)},
|
||
)
|
||
}
|
||
// 更新玩家的位置信息
|
||
player.Pos.X, player.Pos.Y, player.Pos.Z = float64(motionInfo.Pos.X), float64(motionInfo.Pos.Y), float64(motionInfo.Pos.Z)
|
||
player.Rot.X, player.Rot.Y, player.Rot.Z = float64(motionInfo.Rot.X), float64(motionInfo.Rot.Y), float64(motionInfo.Rot.Z)
|
||
// 玩家安全位置更新
|
||
switch motionInfo.State {
|
||
case proto.MotionState_MOTION_DANGER_RUN,
|
||
proto.MotionState_MOTION_RUN,
|
||
proto.MotionState_MOTION_DANGER_STANDBY_MOVE,
|
||
proto.MotionState_MOTION_DANGER_STANDBY,
|
||
proto.MotionState_MOTION_LADDER_TO_STANDBY,
|
||
proto.MotionState_MOTION_STANDBY_MOVE,
|
||
proto.MotionState_MOTION_STANDBY,
|
||
proto.MotionState_MOTION_DANGER_WALK,
|
||
proto.MotionState_MOTION_WALK,
|
||
proto.MotionState_MOTION_DASH:
|
||
// 仅在陆地时更新玩家安全位置
|
||
player.SafePos.X, player.SafePos.Y, player.SafePos.Z = player.Pos.X, player.Pos.Y, player.Pos.Z
|
||
}
|
||
// 处理耐力消耗
|
||
g.ImmediateStamina(player, motionInfo.State)
|
||
} else {
|
||
// 其他实体在移动
|
||
// 更新场景实体的位置信息
|
||
pos := sceneEntity.GetPos()
|
||
pos.X, pos.Y, pos.Z = float64(motionInfo.Pos.X), float64(motionInfo.Pos.Y), float64(motionInfo.Pos.Z)
|
||
rot := sceneEntity.GetRot()
|
||
rot.X, rot.Y, rot.Z = float64(motionInfo.Rot.X), float64(motionInfo.Rot.Y), float64(motionInfo.Rot.Z)
|
||
if sceneEntity.GetEntityType() == constant.ENTITY_TYPE_GADGET {
|
||
// 载具耐力消耗
|
||
gadgetEntity := sceneEntity.GetGadgetEntity()
|
||
if gadgetEntity.GetGadgetVehicleEntity() != nil {
|
||
// 处理耐力消耗
|
||
g.ImmediateStamina(player, motionInfo.State)
|
||
// 处理载具销毁请求
|
||
g.VehicleDestroyMotion(player, sceneEntity, motionInfo.State)
|
||
}
|
||
}
|
||
}
|
||
sceneEntity.SetMoveState(uint16(motionInfo.State))
|
||
sceneEntity.SetLastMoveSceneTimeMs(entityMoveInfo.SceneTime)
|
||
sceneEntity.SetLastMoveReliableSeq(entityMoveInfo.ReliableSeq)
|
||
// 众里寻他千百度 蓦然回首 那人却在灯火阑珊处
|
||
if motionInfo.State == proto.MotionState_MOTION_NOTIFY || motionInfo.State == proto.MotionState_MOTION_FIGHT {
|
||
// 只要转发了这两个包的其中之一 客户端的动画就会被打断
|
||
continue
|
||
}
|
||
case proto.CombatTypeArgument_COMBAT_ANIMATOR_PARAMETER_CHANGED:
|
||
evtAnimatorParameterInfo := new(proto.EvtAnimatorParameterInfo)
|
||
err := pb.Unmarshal(entry.CombatData, evtAnimatorParameterInfo)
|
||
if err != nil {
|
||
logger.Error("parse EvtAnimatorParameterInfo error: %v", err)
|
||
break
|
||
}
|
||
// logger.Debug("EvtAnimatorParameterInfo: %v, ForwardType: %v", evtAnimatorParameterInfo, entry.ForwardType)
|
||
case proto.CombatTypeArgument_COMBAT_ANIMATOR_STATE_CHANGED:
|
||
evtAnimatorStateChangedInfo := new(proto.EvtAnimatorStateChangedInfo)
|
||
err := pb.Unmarshal(entry.CombatData, evtAnimatorStateChangedInfo)
|
||
if err != nil {
|
||
logger.Error("parse EvtAnimatorStateChangedInfo error: %v", err)
|
||
break
|
||
}
|
||
// logger.Debug("EvtAnimatorStateChangedInfo: %v, ForwardType: %v", evtAnimatorStateChangedInfo, entry.ForwardType)
|
||
}
|
||
player.CombatInvokeHandler.AddEntry(entry.ForwardType, entry)
|
||
}
|
||
}
|
||
|
||
func (g *Game) SceneBlockAoiPlayerMove(player *model.Player, world *World, scene *Scene, oldPos *model.Vector, newPos *model.Vector, entityId uint32) {
|
||
// 服务器处理玩家移动场景区块aoi事件频率限制
|
||
now := uint64(time.Now().UnixMilli())
|
||
if now-player.LastSceneBlockAoiMoveTime < 200 {
|
||
return
|
||
}
|
||
player.LastSceneBlockAoiMoveTime = now
|
||
sceneBlockAoiMap := WORLD_MANAGER.GetSceneBlockAoiMap()
|
||
aoiManager, exist := sceneBlockAoiMap[player.SceneId]
|
||
if !exist {
|
||
logger.Error("get scene block aoi is nil, sceneId: %v, uid: %v", player.SceneId, player.PlayerID)
|
||
return
|
||
}
|
||
oldGid := aoiManager.GetGidByPos(float32(oldPos.X), 0.0, float32(oldPos.Z))
|
||
newGid := aoiManager.GetGidByPos(float32(newPos.X), 0.0, float32(newPos.Z))
|
||
if oldGid != newGid {
|
||
// 跨越了block格子
|
||
logger.Debug("player cross scene block grid, oldGid: %v, newGid: %v, uid: %v", oldGid, newGid, player.PlayerID)
|
||
}
|
||
// 加载和卸载的group
|
||
oldNeighborGroupMap := g.GetNeighborGroup(player.SceneId, oldPos)
|
||
newNeighborGroupMap := g.GetNeighborGroup(player.SceneId, newPos)
|
||
for groupId, groupConfig := range oldNeighborGroupMap {
|
||
_, exist := newNeighborGroupMap[groupId]
|
||
if exist {
|
||
continue
|
||
}
|
||
// 旧有新没有的group即为卸载的
|
||
if !world.GetMultiplayer() {
|
||
// 单人世界直接卸载group
|
||
g.RemoveSceneGroup(player, scene, groupConfig)
|
||
} else {
|
||
// 多人世界group附近没有任何玩家则卸载
|
||
remove := true
|
||
for _, otherPlayer := range scene.GetAllPlayer() {
|
||
for otherPlayerGroupId := range g.GetNeighborGroup(otherPlayer.SceneId, otherPlayer.Pos) {
|
||
if otherPlayerGroupId == groupId {
|
||
remove = false
|
||
break
|
||
}
|
||
}
|
||
if !remove {
|
||
break
|
||
}
|
||
}
|
||
if remove {
|
||
g.RemoveSceneGroup(player, scene, groupConfig)
|
||
}
|
||
}
|
||
}
|
||
for groupId, groupConfig := range newNeighborGroupMap {
|
||
_, exist := oldNeighborGroupMap[groupId]
|
||
if exist {
|
||
continue
|
||
}
|
||
// 新有旧没有的group即为加载的
|
||
g.AddSceneGroup(player, scene, groupConfig)
|
||
}
|
||
// 消失和出现的场景实体
|
||
oldVisionEntityMap := g.GetVisionEntity(scene, oldPos)
|
||
newVisionEntityMap := g.GetVisionEntity(scene, newPos)
|
||
delEntityIdList := make([]uint32, 0)
|
||
for entityId, entity := range oldVisionEntityMap {
|
||
_, exist := newVisionEntityMap[entityId]
|
||
if exist {
|
||
continue
|
||
}
|
||
if WORLD_MANAGER.IsBigWorld(world) {
|
||
if entity.GetEntityType() == constant.ENTITY_TYPE_AVATAR {
|
||
continue
|
||
}
|
||
}
|
||
// 旧有新没有的实体即为消失的
|
||
delEntityIdList = append(delEntityIdList, entityId)
|
||
}
|
||
addEntityIdList := make([]uint32, 0)
|
||
for entityId, entity := range newVisionEntityMap {
|
||
_, exist := oldVisionEntityMap[entityId]
|
||
if exist {
|
||
continue
|
||
}
|
||
if WORLD_MANAGER.IsBigWorld(world) {
|
||
if entity.GetEntityType() == constant.ENTITY_TYPE_AVATAR {
|
||
continue
|
||
}
|
||
}
|
||
// 新有旧没有的实体即为出现的
|
||
addEntityIdList = append(addEntityIdList, entityId)
|
||
}
|
||
// 同步客户端消失和出现的场景实体
|
||
if len(delEntityIdList) > 0 {
|
||
g.RemoveSceneEntityNotifyToPlayer(player, proto.VisionType_VISION_MISS, delEntityIdList)
|
||
}
|
||
if len(addEntityIdList) > 0 {
|
||
g.AddSceneEntityNotify(player, proto.VisionType_VISION_MEET, addEntityIdList, false, false)
|
||
}
|
||
// 场景区域触发器检测
|
||
g.SceneRegionTriggerCheck(player, oldPos, newPos, entityId)
|
||
}
|
||
|
||
func (g *Game) BigWorldAoiPlayerMove(player *model.Player, world *World, scene *Scene, oldPos *model.Vector, newPos *model.Vector) {
|
||
bigWorldAoi := world.GetBigWorldAoi()
|
||
oldGid := bigWorldAoi.GetGidByPos(float32(oldPos.X), float32(oldPos.Y), float32(oldPos.Z))
|
||
newGid := bigWorldAoi.GetGidByPos(float32(newPos.X), float32(newPos.Y), float32(newPos.Z))
|
||
if oldGid != newGid {
|
||
// 玩家跨越了格子
|
||
logger.Debug("player cross big world aoi grid, oldGid: %v, newGid: %v, uid: %v", oldGid, newGid, player.PlayerID)
|
||
// 找出本次移动所带来的消失和出现的格子
|
||
oldGridList := bigWorldAoi.GetSurrGridListByGid(oldGid)
|
||
newGridList := bigWorldAoi.GetSurrGridListByGid(newGid)
|
||
delGridIdList := make([]uint32, 0)
|
||
for _, oldGrid := range oldGridList {
|
||
exist := false
|
||
for _, newGrid := range newGridList {
|
||
if oldGrid.GetGid() == newGrid.GetGid() {
|
||
exist = true
|
||
break
|
||
}
|
||
}
|
||
if exist {
|
||
continue
|
||
}
|
||
delGridIdList = append(delGridIdList, oldGrid.GetGid())
|
||
}
|
||
addGridIdList := make([]uint32, 0)
|
||
for _, newGrid := range newGridList {
|
||
exist := false
|
||
for _, oldGrid := range oldGridList {
|
||
if newGrid.GetGid() == oldGrid.GetGid() {
|
||
exist = true
|
||
break
|
||
}
|
||
}
|
||
if exist {
|
||
continue
|
||
}
|
||
addGridIdList = append(addGridIdList, newGrid.GetGid())
|
||
}
|
||
activeAvatarId := world.GetPlayerActiveAvatarId(player)
|
||
activeWorldAvatar := world.GetPlayerWorldAvatar(player, activeAvatarId)
|
||
// 老格子移除玩家
|
||
bigWorldAoi.RemoveObjectFromGrid(int64(player.PlayerID), oldGid)
|
||
// 处理消失的格子
|
||
for _, delGridId := range delGridIdList {
|
||
// 通知自己 老格子里的其它玩家消失
|
||
oldOtherWorldAvatarMap := bigWorldAoi.GetObjectListByGid(delGridId)
|
||
delEntityIdList := make([]uint32, 0)
|
||
for _, otherWorldAvatarAny := range oldOtherWorldAvatarMap {
|
||
otherWorldAvatar := otherWorldAvatarAny.(*WorldAvatar)
|
||
delEntityIdList = append(delEntityIdList, otherWorldAvatar.GetAvatarEntityId())
|
||
}
|
||
if len(delEntityIdList) > 0 {
|
||
g.RemoveSceneEntityNotifyToPlayer(player, proto.VisionType_VISION_MISS, delEntityIdList)
|
||
}
|
||
// 通知老格子里的其它玩家 自己消失
|
||
for otherPlayerId := range oldOtherWorldAvatarMap {
|
||
otherPlayer := USER_MANAGER.GetOnlineUser(uint32(otherPlayerId))
|
||
if otherPlayer == nil {
|
||
logger.Error("get player is nil, target uid: %v, uid: %v", otherPlayerId, player.PlayerID)
|
||
continue
|
||
}
|
||
g.RemoveSceneEntityNotifyToPlayer(otherPlayer, proto.VisionType_VISION_MISS, []uint32{activeWorldAvatar.GetAvatarEntityId()})
|
||
}
|
||
}
|
||
// 处理出现的格子
|
||
for _, addGridId := range addGridIdList {
|
||
// 通知自己 新格子里的其他玩家出现
|
||
newOtherWorldAvatarMap := bigWorldAoi.GetObjectListByGid(addGridId)
|
||
addEntityIdList := make([]uint32, 0)
|
||
for _, otherWorldAvatarAny := range newOtherWorldAvatarMap {
|
||
otherWorldAvatar := otherWorldAvatarAny.(*WorldAvatar)
|
||
addEntityIdList = append(addEntityIdList, otherWorldAvatar.GetAvatarEntityId())
|
||
}
|
||
if len(addEntityIdList) > 0 {
|
||
g.AddSceneEntityNotify(player, proto.VisionType_VISION_MEET, addEntityIdList, false, false)
|
||
}
|
||
// 通知新格子里的其他玩家 自己出现
|
||
for otherPlayerId := range newOtherWorldAvatarMap {
|
||
otherPlayer := USER_MANAGER.GetOnlineUser(uint32(otherPlayerId))
|
||
if otherPlayer == nil {
|
||
logger.Error("get player is nil, target uid: %v, uid: %v", otherPlayerId, player.PlayerID)
|
||
continue
|
||
}
|
||
sceneEntityInfoAvatar := g.PacketSceneEntityInfoAvatar(scene, player, world.GetPlayerActiveAvatarId(player))
|
||
g.AddSceneEntityNotifyToPlayer(otherPlayer, proto.VisionType_VISION_MEET, []*proto.SceneEntityInfo{sceneEntityInfoAvatar})
|
||
}
|
||
}
|
||
// 新格子添加玩家
|
||
bigWorldAoi.AddObjectToGrid(int64(player.PlayerID), activeWorldAvatar, newGid)
|
||
}
|
||
}
|
||
|
||
func (g *Game) AbilityInvocationsNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.AbilityInvocationsNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
for _, entry := range req.Invokes {
|
||
player.AbilityInvokeHandler.AddEntry(entry.ForwardType, entry)
|
||
switch entry.ArgumentType {
|
||
case proto.AbilityInvokeArgument_ABILITY_META_MODIFIER_CHANGE:
|
||
modifierChange := new(proto.AbilityMetaModifierChange)
|
||
err := pb.Unmarshal(entry.AbilityData, modifierChange)
|
||
if err != nil {
|
||
logger.Error("parse AbilityMetaModifierChange error: %v", err)
|
||
continue
|
||
}
|
||
// logger.Debug("EntityId: %v, ModifierChange: %v", entry.EntityId, modifierChange)
|
||
// 处理耐力消耗
|
||
g.HandleAbilityStamina(player, entry)
|
||
g.handleGadgetEntityAbilityLow(player, entry.EntityId, entry.ArgumentType, modifierChange)
|
||
case proto.AbilityInvokeArgument_ABILITY_MIXIN_COST_STAMINA:
|
||
costStamina := new(proto.AbilityMixinCostStamina)
|
||
err := pb.Unmarshal(entry.AbilityData, costStamina)
|
||
if err != nil {
|
||
logger.Error("parse AbilityMixinCostStamina error: %v", err)
|
||
continue
|
||
}
|
||
// logger.Debug("EntityId: %v, MixinCostStamina: %v", entry.EntityId, costStamina)
|
||
// 处理耐力消耗
|
||
g.HandleAbilityStamina(player, entry)
|
||
case proto.AbilityInvokeArgument_ABILITY_META_MODIFIER_DURABILITY_CHANGE:
|
||
modifierDurabilityChange := new(proto.AbilityMetaModifierDurabilityChange)
|
||
err := pb.Unmarshal(entry.AbilityData, modifierDurabilityChange)
|
||
if err != nil {
|
||
logger.Error("parse AbilityMetaModifierDurabilityChange error: %v", err)
|
||
continue
|
||
}
|
||
// logger.Debug("EntityId: %v, DurabilityChange: %v", entry.EntityId, modifierDurabilityChange)
|
||
}
|
||
}
|
||
}
|
||
|
||
func (g *Game) ClientAbilityInitFinishNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.ClientAbilityInitFinishNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
invokeHandler := model.NewInvokeHandler[proto.AbilityInvokeEntry]()
|
||
for _, entry := range req.Invokes {
|
||
// logger.Debug("ClientAbilityInitFinishNotify: %v", entry)
|
||
invokeHandler.AddEntry(entry.ForwardType, entry)
|
||
}
|
||
DoForward[proto.AbilityInvokeEntry](player, invokeHandler,
|
||
cmd.ClientAbilityInitFinishNotify, new(proto.ClientAbilityInitFinishNotify), "Invokes",
|
||
req, []string{"EntityId"})
|
||
}
|
||
|
||
func (g *Game) ClientAbilityChangeNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.ClientAbilityChangeNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
invokeHandler := model.NewInvokeHandler[proto.AbilityInvokeEntry]()
|
||
for _, entry := range req.Invokes {
|
||
// logger.Debug("ClientAbilityChangeNotify: %v", entry)
|
||
invokeHandler.AddEntry(entry.ForwardType, entry)
|
||
}
|
||
DoForward[proto.AbilityInvokeEntry](player, invokeHandler,
|
||
cmd.ClientAbilityChangeNotify, new(proto.ClientAbilityChangeNotify), "Invokes",
|
||
req, []string{"IsInitHash", "EntityId"})
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
for _, abilityInvokeEntry := range req.Invokes {
|
||
switch abilityInvokeEntry.ArgumentType {
|
||
case proto.AbilityInvokeArgument_ABILITY_META_ADD_NEW_ABILITY:
|
||
abilityMetaAddAbility := new(proto.AbilityMetaAddAbility)
|
||
err := pb.Unmarshal(abilityInvokeEntry.AbilityData, abilityMetaAddAbility)
|
||
if err != nil {
|
||
logger.Error("parse AbilityMetaAddAbility error: %v", err)
|
||
continue
|
||
}
|
||
worldAvatar := world.GetWorldAvatarByEntityId(abilityInvokeEntry.EntityId)
|
||
if worldAvatar == nil {
|
||
continue
|
||
}
|
||
if abilityMetaAddAbility.Ability == nil {
|
||
continue
|
||
}
|
||
worldAvatar.AddAbility(abilityMetaAddAbility.Ability)
|
||
case proto.AbilityInvokeArgument_ABILITY_META_MODIFIER_CHANGE:
|
||
abilityMetaModifierChange := new(proto.AbilityMetaModifierChange)
|
||
err := pb.Unmarshal(abilityInvokeEntry.AbilityData, abilityMetaModifierChange)
|
||
if err != nil {
|
||
logger.Error("parse AbilityMetaModifierChange error: %v", err)
|
||
continue
|
||
}
|
||
abilityAppliedModifier := &proto.AbilityAppliedModifier{
|
||
ModifierLocalId: abilityMetaModifierChange.ModifierLocalId,
|
||
ParentAbilityEntityId: 0,
|
||
ParentAbilityName: abilityMetaModifierChange.ParentAbilityName,
|
||
ParentAbilityOverride: abilityMetaModifierChange.ParentAbilityOverride,
|
||
InstancedAbilityId: abilityInvokeEntry.Head.InstancedAbilityId,
|
||
InstancedModifierId: abilityInvokeEntry.Head.InstancedModifierId,
|
||
ExistDuration: 0,
|
||
AttachedInstancedModifier: abilityMetaModifierChange.AttachedInstancedModifier,
|
||
ApplyEntityId: abilityMetaModifierChange.ApplyEntityId,
|
||
IsAttachedParentAbility: abilityMetaModifierChange.IsAttachedParentAbility,
|
||
ModifierDurability: nil,
|
||
SbuffUid: 0,
|
||
IsServerbuffModifier: abilityInvokeEntry.Head.IsServerbuffModifier,
|
||
}
|
||
worldAvatar := world.GetWorldAvatarByEntityId(abilityInvokeEntry.EntityId)
|
||
if worldAvatar == nil {
|
||
continue
|
||
}
|
||
worldAvatar.AddModifier(abilityAppliedModifier)
|
||
}
|
||
}
|
||
}
|
||
|
||
func (g *Game) EvtDoSkillSuccNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EvtDoSkillSuccNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
logger.Debug("EvtDoSkillSuccNotify: %v", req)
|
||
// 处理技能开始的耐力消耗
|
||
g.SkillStartStamina(player, req.CasterId, req.SkillId)
|
||
g.TriggerQuest(player, constant.QUEST_FINISH_COND_TYPE_SKILL, "", int32(req.SkillId))
|
||
}
|
||
|
||
func (g *Game) EvtAvatarEnterFocusNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EvtAvatarEnterFocusNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
// logger.Debug("EvtAvatarEnterFocusNotify: %v", req)
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
g.SendToSceneA(scene, cmd.EvtAvatarEnterFocusNotify, player.ClientSeq, req)
|
||
}
|
||
|
||
func (g *Game) EvtAvatarUpdateFocusNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EvtAvatarUpdateFocusNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
// logger.Debug("EvtAvatarUpdateFocusNotify: %v", req)
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
g.SendToSceneA(scene, cmd.EvtAvatarUpdateFocusNotify, player.ClientSeq, req)
|
||
}
|
||
|
||
func (g *Game) EvtAvatarExitFocusNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EvtAvatarExitFocusNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
// logger.Debug("EvtAvatarExitFocusNotify: %v", req)
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
g.SendToSceneA(scene, cmd.EvtAvatarExitFocusNotify, player.ClientSeq, req)
|
||
}
|
||
|
||
func (g *Game) EvtEntityRenderersChangedNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EvtEntityRenderersChangedNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
// logger.Debug("EvtEntityRenderersChangedNotify: %v", req)
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
g.SendToSceneA(scene, cmd.EvtEntityRenderersChangedNotify, player.ClientSeq, req)
|
||
}
|
||
|
||
func (g *Game) EvtCreateGadgetNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EvtCreateGadgetNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
// logger.Debug("EvtCreateGadgetNotify: %v", req)
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
if req.InitPos == nil {
|
||
return
|
||
}
|
||
scene.CreateEntityGadgetClient(&model.Vector{
|
||
X: float64(req.InitPos.X),
|
||
Y: float64(req.InitPos.Y),
|
||
Z: float64(req.InitPos.Z),
|
||
}, &model.Vector{
|
||
X: float64(req.InitEulerAngles.X),
|
||
Y: float64(req.InitEulerAngles.Y),
|
||
Z: float64(req.InitEulerAngles.Z),
|
||
}, req.EntityId, req.ConfigId, req.CampId, req.CampType, req.OwnerEntityId, req.TargetEntityId, req.PropOwnerEntityId)
|
||
g.AddSceneEntityNotify(player, proto.VisionType_VISION_BORN, []uint32{req.EntityId}, true, true)
|
||
}
|
||
|
||
func (g *Game) EvtDestroyGadgetNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EvtDestroyGadgetNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
// logger.Debug("EvtDestroyGadgetNotify: %v", req)
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
scene.DestroyEntity(req.EntityId)
|
||
g.RemoveSceneEntityNotifyBroadcast(scene, proto.VisionType_VISION_MISS, []uint32{req.EntityId}, false, 0)
|
||
}
|
||
|
||
func (g *Game) EvtAiSyncSkillCdNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EvtAiSyncSkillCdNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
// logger.Debug("EvtAiSyncSkillCdNotify: %v", req)
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
g.SendToSceneA(scene, cmd.EvtAiSyncSkillCdNotify, player.ClientSeq, req)
|
||
}
|
||
|
||
func (g *Game) EvtAiSyncCombatThreatInfoNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EvtAiSyncCombatThreatInfoNotify)
|
||
if player.SceneLoadState != model.SceneEnterDone {
|
||
return
|
||
}
|
||
// logger.Debug("EvtAiSyncCombatThreatInfoNotify: %v", req)
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
g.SendToSceneA(scene, cmd.EvtAiSyncCombatThreatInfoNotify, player.ClientSeq, req)
|
||
}
|
||
|
||
func (g *Game) EntityConfigHashNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EntityConfigHashNotify)
|
||
_ = req
|
||
}
|
||
|
||
func (g *Game) MonsterAIConfigHashNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.MonsterAIConfigHashNotify)
|
||
_ = req
|
||
}
|
||
|
||
func (g *Game) SetEntityClientDataNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.SetEntityClientDataNotify)
|
||
g.SendMsg(cmd.SetEntityClientDataNotify, player.PlayerID, player.ClientSeq, req)
|
||
}
|
||
|
||
func (g *Game) EntityAiSyncNotify(player *model.Player, payloadMsg pb.Message) {
|
||
req := payloadMsg.(*proto.EntityAiSyncNotify)
|
||
entityAiSyncNotify := &proto.EntityAiSyncNotify{
|
||
InfoList: make([]*proto.AiSyncInfo, 0),
|
||
}
|
||
for _, monsterId := range req.LocalAvatarAlertedMonsterList {
|
||
entityAiSyncNotify.InfoList = append(entityAiSyncNotify.InfoList, &proto.AiSyncInfo{
|
||
EntityId: monsterId,
|
||
HasPathToTarget: true,
|
||
IsSelfKilling: false,
|
||
})
|
||
}
|
||
g.SendMsg(cmd.EntityAiSyncNotify, player.PlayerID, player.ClientSeq, entityAiSyncNotify)
|
||
}
|
||
|
||
// TODO 一些很low的解决方案 我本来是不想写的 有多low?要多low有多low!
|
||
|
||
func (g *Game) handleGadgetEntityBeHitLow(player *model.Player, entity *Entity, hitElementType uint32) {
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
if entity.GetEntityType() != constant.ENTITY_TYPE_GADGET {
|
||
return
|
||
}
|
||
gadgetEntity := entity.GetGadgetEntity()
|
||
gadgetId := gadgetEntity.GetGadgetId()
|
||
gadgetDataConfig := gdconf.GetGadgetDataById(int32(gadgetId))
|
||
if gadgetDataConfig == nil {
|
||
logger.Error("get gadget data config is nil, gadgetId: %v", gadgetEntity.GetGadgetId())
|
||
return
|
||
}
|
||
if strings.Contains(gadgetDataConfig.Name, "火把") ||
|
||
strings.Contains(gadgetDataConfig.Name, "火盆") ||
|
||
strings.Contains(gadgetDataConfig.Name, "篝火") {
|
||
// 火把点燃
|
||
if hitElementType != constant.ELEMENT_TYPE_FIRE {
|
||
return
|
||
}
|
||
g.ChangeGadgetState(player, entity.GetId(), constant.GADGET_STATE_GEAR_START)
|
||
} else if strings.Contains(gadgetDataConfig.ServerLuaScript, "Controller") {
|
||
// 元素方碑点亮
|
||
gadgetElementType := uint32(0)
|
||
if strings.Contains(gadgetDataConfig.ServerLuaScript, "Fire") {
|
||
gadgetElementType = constant.ELEMENT_TYPE_FIRE
|
||
} else if strings.Contains(gadgetDataConfig.ServerLuaScript, "Water") {
|
||
gadgetElementType = constant.ELEMENT_TYPE_WATER
|
||
} else if strings.Contains(gadgetDataConfig.ServerLuaScript, "Grass") {
|
||
gadgetElementType = constant.ELEMENT_TYPE_GRASS
|
||
} else if strings.Contains(gadgetDataConfig.ServerLuaScript, "Elec") {
|
||
gadgetElementType = constant.ELEMENT_TYPE_ELEC
|
||
} else if strings.Contains(gadgetDataConfig.ServerLuaScript, "Ice") {
|
||
gadgetElementType = constant.ELEMENT_TYPE_ICE
|
||
} else if strings.Contains(gadgetDataConfig.ServerLuaScript, "Wind") {
|
||
gadgetElementType = constant.ELEMENT_TYPE_WIND
|
||
} else if strings.Contains(gadgetDataConfig.ServerLuaScript, "Rock") {
|
||
gadgetElementType = constant.ELEMENT_TYPE_ROCK
|
||
}
|
||
if hitElementType != gadgetElementType {
|
||
return
|
||
}
|
||
g.ChangeGadgetState(player, entity.GetId(), constant.GADGET_STATE_GEAR_START)
|
||
} else if strings.Contains(gadgetDataConfig.ServerLuaScript, "SubfieldDrop_WoodenObject_Broken") {
|
||
// 木箱破碎
|
||
g.KillEntity(player, scene, entity.GetId(), proto.PlayerDieType_PLAYER_DIE_GM)
|
||
}
|
||
}
|
||
|
||
func (g *Game) handleGadgetEntityAbilityLow(player *model.Player, entityId uint32, argument proto.AbilityInvokeArgument, entry pb.Message) {
|
||
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
|
||
if world == nil {
|
||
return
|
||
}
|
||
scene := world.GetSceneById(player.SceneId)
|
||
entity := scene.GetEntity(entityId)
|
||
if entity == nil {
|
||
return
|
||
}
|
||
switch argument {
|
||
case proto.AbilityInvokeArgument_ABILITY_META_MODIFIER_CHANGE:
|
||
// 物件破碎
|
||
modifierChange := entry.(*proto.AbilityMetaModifierChange)
|
||
if modifierChange.Action != proto.ModifierAction_REMOVED {
|
||
return
|
||
}
|
||
if entity.GetEntityType() != constant.ENTITY_TYPE_GADGET {
|
||
return
|
||
}
|
||
gadgetEntity := entity.GetGadgetEntity()
|
||
gadgetId := gadgetEntity.GetGadgetId()
|
||
gadgetDataConfig := gdconf.GetGadgetDataById(int32(gadgetId))
|
||
if gadgetDataConfig == nil {
|
||
logger.Error("get gadget data config is nil, gadgetId: %v", gadgetEntity.GetGadgetId())
|
||
return
|
||
}
|
||
if strings.Contains(gadgetDataConfig.Name, "碎石堆") ||
|
||
strings.Contains(gadgetDataConfig.ServerLuaScript, "SubfieldDrop_WoodenObject_Broken") {
|
||
logger.Debug("物件破碎, entityId: %v, modifierChange: %v, uid: %v", entityId, modifierChange, player.PlayerID)
|
||
g.KillEntity(player, scene, entity.GetId(), proto.PlayerDieType_PLAYER_DIE_GM)
|
||
} else if strings.Contains(gadgetDataConfig.ServerLuaScript, "SubfieldDrop_Ore") {
|
||
g.KillEntity(player, scene, entity.GetId(), proto.PlayerDieType_PLAYER_DIE_GM)
|
||
g.CreateDropGadget(player, entity.GetPos(), 70900001, 233, 1)
|
||
}
|
||
}
|
||
}
|