From 3a2b82585f1fb5d33beda612f679cba3d5b9ac5f Mon Sep 17 00:00:00 2001 From: UnKownOwO <80520429@qq.com> Date: Wed, 14 Dec 2022 23:21:19 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BD=BD=E5=85=B7=E5=88=9B=E5=BB=BA=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E5=88=9D=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gs/game/route_manager.go | 4 +- gs/game/user_scene.go | 84 ++++++++----- gs/game/user_vehicle.go | 170 +++++++++++++++++++++++++++ gs/game/world_manager.go | 82 +++++++++++-- protocol/cmd/cmd_id_proto_obj_map.go | 6 + 5 files changed, 305 insertions(+), 41 deletions(-) create mode 100644 gs/game/user_vehicle.go diff --git a/gs/game/route_manager.go b/gs/game/route_manager.go index 1b84cc29..f4ff80ef 100644 --- a/gs/game/route_manager.go +++ b/gs/game/route_manager.go @@ -37,7 +37,7 @@ func (r *RouteManager) doRoute(cmdId uint16, userId uint32, clientSeq uint32, pa if player == nil { logger.LOG.Error("player is nil, uid: %v", userId) // 临时为了调试便捷搞的重连 生产环境请务必去除 不然新用户会一直重连不能进入 - //GAME_MANAGER.ReconnectPlayer(userId) + // GAME_MANAGER.ReconnectPlayer(userId) return } player.ClientSeq = clientSeq @@ -112,6 +112,8 @@ func (r *RouteManager) InitRoute() { r.registerRouter(cmd.EvtEntityRenderersChangedNotify, GAME_MANAGER.EvtEntityRenderersChangedNotify) r.registerRouter(cmd.EvtCreateGadgetNotify, GAME_MANAGER.EvtCreateGadgetNotify) r.registerRouter(cmd.EvtDestroyGadgetNotify, GAME_MANAGER.EvtDestroyGadgetNotify) + r.registerRouter(cmd.CreateVehicleReq, GAME_MANAGER.CreateVehicleReq) + r.registerRouter(cmd.VehicleInteractReq, GAME_MANAGER.VehicleInteractReq) } func (r *RouteManager) RouteHandle(netMsg *cmd.NetMsg) { diff --git a/gs/game/user_scene.go b/gs/game/user_scene.go index 10d32740..c8042b0e 100644 --- a/gs/game/user_scene.go +++ b/gs/game/user_scene.go @@ -440,9 +440,9 @@ func (g *GameManager) AddSceneEntityNotify(player *model.Player, visionType prot scene := world.GetSceneById(player.SceneId) entityList := make([]*proto.SceneEntityInfo, 0) for _, entityId := range entityIdList { - entity := scene.entityMap[entityId] - if entity == nil { - logger.LOG.Error("get entity is nil, entityId: %v", entityId) + entity, ok := scene.entityMap[entityId] + if !ok { + // logger.LOG.Error("get entity is nil, entityId: %v", entityId) continue } switch entity.entityType { @@ -527,16 +527,17 @@ func (g *GameManager) PacketSceneEntityInfoAvatar(scene *Scene, player *model.Pl if entity == nil { return new(proto.SceneEntityInfo) } + pos := &proto.Vector{ + X: float32(entity.pos.X), + Y: float32(entity.pos.Y), + Z: float32(entity.pos.Z), + } worldAvatar := scene.world.GetWorldAvatarByEntityId(entity.id) sceneEntityInfo := &proto.SceneEntityInfo{ EntityType: proto.ProtEntityType_PROT_ENTITY_TYPE_AVATAR, EntityId: entity.id, MotionInfo: &proto.MotionInfo{ - Pos: &proto.Vector{ - X: float32(entity.pos.X), - Y: float32(entity.pos.Y), - Z: float32(entity.pos.Z), - }, + Pos: pos, Rot: &proto.Vector{ X: float32(entity.rot.X), Y: float32(entity.rot.Y), @@ -569,9 +570,9 @@ func (g *GameManager) PacketSceneEntityInfoAvatar(scene *Scene, player *model.Pl RendererChangedInfo: new(proto.EntityRendererChangedInfo), AiInfo: &proto.SceneEntityAiInfo{ IsAiOpen: true, - BornPos: new(proto.Vector), + BornPos: pos, }, - BornPos: new(proto.Vector), + BornPos: pos, }, LastMoveSceneTimeMs: entity.lastMoveSceneTimeMs, LastMoveReliableSeq: entity.lastMoveReliableSeq, @@ -632,15 +633,16 @@ func (g *GameManager) PacketSceneEntityInfoGadget(scene *Scene, entityId uint32) if entity == nil { return new(proto.SceneEntityInfo) } + pos := &proto.Vector{ + X: float32(entity.pos.X), + Y: float32(entity.pos.Y), + Z: float32(entity.pos.Z), + } sceneEntityInfo := &proto.SceneEntityInfo{ EntityType: proto.ProtEntityType_PROT_ENTITY_TYPE_GADGET, EntityId: entity.id, MotionInfo: &proto.MotionInfo{ - Pos: &proto.Vector{ - X: float32(entity.pos.X), - Y: float32(entity.pos.Y), - Z: float32(entity.pos.Z), - }, + Pos: pos, Rot: &proto.Vector{ X: float32(entity.rot.X), Y: float32(entity.rot.Y), @@ -663,19 +665,23 @@ func (g *GameManager) PacketSceneEntityInfoGadget(scene *Scene, entityId uint32) RendererChangedInfo: new(proto.EntityRendererChangedInfo), AiInfo: &proto.SceneEntityAiInfo{ IsAiOpen: true, - BornPos: new(proto.Vector), + BornPos: pos, }, - BornPos: new(proto.Vector), + BornPos: pos, }, } switch entity.gadgetEntity.gadgetType { case GADGET_TYPE_CLIENT: sceneEntityInfo.Entity = &proto.SceneEntityInfo_Gadget{ - Gadget: g.PacketSceneGadgetInfoAbility(entity.gadgetEntity), + Gadget: g.PacketSceneGadgetInfoAbility(entity.gadgetEntity.gadgetClientEntity), } case GADGET_TYPE_GATHER: sceneEntityInfo.Entity = &proto.SceneEntityInfo_Gadget{ - Gadget: g.PacketSceneGadgetInfoGather(entity.gadgetEntity), + Gadget: g.PacketSceneGadgetInfoGather(entity.gadgetEntity.gadgetGatherEntity), + } + case GADGET_TYPE_VEHICLE: + sceneEntityInfo.Entity = &proto.SceneEntityInfo_Gadget{ + Gadget: g.PacketSceneGadgetInfoVehicle(entity.gadgetEntity.gadgetVehicleEntity), } default: break @@ -731,8 +737,28 @@ func (g *GameManager) PacketSceneMonsterInfo() *proto.SceneMonsterInfo { return sceneMonsterInfo } -func (g *GameManager) PacketSceneGadgetInfoGather(gadgetEntity *GadgetEntity) *proto.SceneGadgetInfo { - gather := gdc.CONF.GatherDataMap[int32(gadgetEntity.gatherId)] +func (g *GameManager) PacketSceneGadgetInfoVehicle(gadgetVehicleEntity *GadgetVehicleEntity) *proto.SceneGadgetInfo { + sceneGadgetInfo := &proto.SceneGadgetInfo{ + GadgetId: gadgetVehicleEntity.vehicleId, + AuthorityPeerId: WORLD_MANAGER.GetWorldByID(gadgetVehicleEntity.owner.WorldId).GetPlayerPeerId(gadgetVehicleEntity.owner), + IsEnableInteract: true, + Content: &proto.SceneGadgetInfo_VehicleInfo{ + VehicleInfo: &proto.VehicleInfo{ + MemberList: make([]*proto.VehicleMember, 0, len(gadgetVehicleEntity.memberMap)), + OwnerUid: gadgetVehicleEntity.owner.PlayerID, + CurStamina: gadgetVehicleEntity.curStamina, + }, + }, + } + return sceneGadgetInfo +} + +func (g *GameManager) PacketSceneGadgetInfoGather(gadgetGatherEntity *GadgetGatherEntity) *proto.SceneGadgetInfo { + gather, ok := gdc.CONF.GatherDataMap[int32(gadgetGatherEntity.gatherId)] + if !ok { + logger.LOG.Error("gather data error, gatherId: %v", gadgetGatherEntity.gatherId) + return new(proto.SceneGadgetInfo) + } sceneGadgetInfo := &proto.SceneGadgetInfo{ GadgetId: uint32(gather.GadgetId), //GroupId: 133003011, @@ -750,21 +776,21 @@ func (g *GameManager) PacketSceneGadgetInfoGather(gadgetEntity *GadgetEntity) *p return sceneGadgetInfo } -func (g *GameManager) PacketSceneGadgetInfoAbility(gadgetEntity *GadgetEntity) *proto.SceneGadgetInfo { +func (g *GameManager) PacketSceneGadgetInfoAbility(gadgetClientEntity *GadgetClientEntity) *proto.SceneGadgetInfo { sceneGadgetInfo := &proto.SceneGadgetInfo{ - GadgetId: gadgetEntity.configId, - OwnerEntityId: gadgetEntity.ownerEntityId, + GadgetId: gadgetClientEntity.configId, + OwnerEntityId: gadgetClientEntity.ownerEntityId, AuthorityPeerId: 1, IsEnableInteract: true, Content: &proto.SceneGadgetInfo_ClientGadget{ ClientGadget: &proto.ClientGadgetInfo{ - CampId: gadgetEntity.campId, - CampType: gadgetEntity.campType, - OwnerEntityId: gadgetEntity.ownerEntityId, - TargetEntityId: gadgetEntity.targetEntityId, + CampId: gadgetClientEntity.campId, + CampType: gadgetClientEntity.campType, + OwnerEntityId: gadgetClientEntity.ownerEntityId, + TargetEntityId: gadgetClientEntity.targetEntityId, }, }, - PropOwnerEntityId: gadgetEntity.propOwnerEntityId, + PropOwnerEntityId: gadgetClientEntity.propOwnerEntityId, } return sceneGadgetInfo } diff --git a/gs/game/user_vehicle.go b/gs/game/user_vehicle.go new file mode 100644 index 00000000..1afe85fc --- /dev/null +++ b/gs/game/user_vehicle.go @@ -0,0 +1,170 @@ +package game + +import ( + pb "google.golang.org/protobuf/proto" + "hk4e/gs/model" + "hk4e/pkg/logger" + "hk4e/protocol/cmd" + "hk4e/protocol/proto" +) + +// CreateVehicleReq 创建载具 +func (g *GameManager) CreateVehicleReq(player *model.Player, payloadMsg pb.Message) { + req := payloadMsg.(*proto.CreateVehicleReq) + + world := WORLD_MANAGER.GetWorldByID(player.WorldId) + scene := world.GetSceneById(player.SceneId) + + // TODO req.ScenePointId 验证浪船锚点是否已解锁 + + // 清除已创建的载具 + for _, id := range scene.GetEntityIdList() { + entity := scene.GetEntity(id) + // 判断实体类型是否为载具 + if entity.entityType != uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_GADGET) || entity.gadgetEntity.gadgetType != GADGET_TYPE_VEHICLE { + continue + } + // 确保载具Id为将要创建的 (每种载具允许存在1个) + if entity.gadgetEntity.gadgetVehicleEntity.vehicleId != req.VehicleId { + continue + } + // 该载具是否为此玩家的 + if entity.gadgetEntity.gadgetVehicleEntity.owner != player { + continue + } + // 现行角色Guid + avatar, ok := player.AvatarMap[player.TeamConfig.GetActiveAvatarId()] + if !ok { + logger.LOG.Error("avatar is nil, avatarId: %v", player.TeamConfig.GetActiveAvatarId()) + g.CommonRetError(cmd.CreateVehicleRsp, player, &proto.CreateVehicleRsp{}) + return + } + // 确保玩家正在载具中 + if g.IsPlayerInVehicle(player, entity.gadgetEntity.gadgetVehicleEntity) { + // 离开载具 + g.ExitVehicle(player, entity, avatar.Guid) + } + // TODO 删除实体 需要杀死实体 暂时实体模块还没写这个 + // scene.DestroyEntity(entity.id) + } + + // 创建载具实体 + pos := &model.Vector{X: float64(req.Pos.X), Y: float64(req.Pos.Y), Z: float64(req.Pos.Z)} + rot := &model.Vector{X: float64(req.Rot.X), Y: float64(req.Rot.Y), Z: float64(req.Rot.Z)} + entityId := scene.CreateEntityGadgetVehicle(player.PlayerID, pos, rot, req.VehicleId) + if entityId == 0 { + logger.LOG.Error("vehicle entityId is 0, uid: %v", player.PlayerID) + g.CommonRetError(cmd.CreateVehicleRsp, player, &proto.CreateVehicleRsp{}) + return + } + GAME_MANAGER.AddSceneEntityNotify(player, proto.VisionType_VISION_TYPE_BORN, []uint32{entityId}, true, false) + + createVehicleRsp := &proto.CreateVehicleRsp{ + VehicleId: req.VehicleId, + EntityId: entityId, + } + g.SendMsg(cmd.CreateVehicleRsp, player.PlayerID, player.ClientSeq, createVehicleRsp) +} + +// IsPlayerInVehicle 判断玩家是否在载具中 +func (g *GameManager) IsPlayerInVehicle(player *model.Player, gadgetVehicleEntity *GadgetVehicleEntity) bool { + for _, p := range gadgetVehicleEntity.memberMap { + if p == player { + return true + } + } + return false +} + +// EnterVehicle 进入载具 +func (g *GameManager) EnterVehicle(player *model.Player, entity *Entity, avatarGuid uint64) { + // 玩家是否已进入载具 + if g.IsPlayerInVehicle(player, entity.gadgetEntity.gadgetVehicleEntity) { + logger.LOG.Error("vehicle has equal player, uid: %v", player.PlayerID) + g.CommonRetError(cmd.VehicleInteractRsp, player, &proto.VehicleInteractRsp{}) + return + } + // 找出载具空闲的位置 + pos := uint32(0) + for entity.gadgetEntity.gadgetVehicleEntity.memberMap[pos] != nil { + pos++ + } + // 载具成员记录玩家 + entity.gadgetEntity.gadgetVehicleEntity.memberMap[pos] = player + + vehicleInteractRsp := &proto.VehicleInteractRsp{ + InteractType: proto.VehicleInteractType_VEHICLE_INTERACT_TYPE_IN, + Member: &proto.VehicleMember{ + Uid: player.PlayerID, + AvatarGuid: avatarGuid, + Pos: pos, // 应该是多人坐船时的位置? + }, + EntityId: entity.id, + } + g.SendMsg(cmd.VehicleInteractRsp, player.PlayerID, player.ClientSeq, vehicleInteractRsp) +} + +// ExitVehicle 离开载具 +func (g *GameManager) ExitVehicle(player *model.Player, entity *Entity, avatarGuid uint64) { + // 玩家是否进入载具 + if !g.IsPlayerInVehicle(player, entity.gadgetEntity.gadgetVehicleEntity) { + logger.LOG.Error("vehicle not has player, uid: %v", player.PlayerID) + g.SendMsg(cmd.VehicleInteractRsp, player.PlayerID, player.ClientSeq, &proto.VehicleInteractRsp{Retcode: int32(proto.Retcode_RET_NOT_IN_VEHICLE)}) + return + } + // 载具成员删除玩家 + var memberPos uint32 + for pos, p := range entity.gadgetEntity.gadgetVehicleEntity.memberMap { + if p == player { + memberPos = pos + delete(entity.gadgetEntity.gadgetVehicleEntity.memberMap, pos) + } + } + + vehicleInteractRsp := &proto.VehicleInteractRsp{ + InteractType: proto.VehicleInteractType_VEHICLE_INTERACT_TYPE_OUT, + Member: &proto.VehicleMember{ + Uid: player.PlayerID, + AvatarGuid: avatarGuid, + Pos: memberPos, // 应该是多人坐船时的位置? + }, + EntityId: entity.id, + } + g.SendMsg(cmd.VehicleInteractRsp, player.PlayerID, player.ClientSeq, vehicleInteractRsp) +} + +// VehicleInteractReq 载具交互 +func (g *GameManager) VehicleInteractReq(player *model.Player, payloadMsg pb.Message) { + req := payloadMsg.(*proto.VehicleInteractReq) + + // 获取载具实体 + world := WORLD_MANAGER.GetWorldByID(player.WorldId) + entity := world.GetSceneById(player.SceneId).GetEntity(req.EntityId) + if entity == nil { + logger.LOG.Error("vehicle entity is nil, entityId: %v", req.EntityId) + g.CommonRetError(cmd.VehicleInteractRsp, player, &proto.VehicleInteractRsp{}) + return + } + // 判断实体类型是否为载具 + if entity.entityType != uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_GADGET) || entity.gadgetEntity.gadgetType != GADGET_TYPE_VEHICLE { + logger.LOG.Error("vehicle entity error, entityType: %v", entity.entityType) + g.SendMsg(cmd.VehicleInteractRsp, player.PlayerID, player.ClientSeq, &proto.VehicleInteractRsp{Retcode: int32(proto.Retcode_RET_GADGET_NOT_VEHICLE)}) + return + } + // 现行角色Guid + avatar, ok := player.AvatarMap[player.TeamConfig.GetActiveAvatarId()] + if !ok { + logger.LOG.Error("avatar is nil, avatarId: %v", player.TeamConfig.GetActiveAvatarId()) + g.CommonRetError(cmd.VehicleInteractRsp, player, &proto.VehicleInteractRsp{}) + return + } + + switch req.InteractType { + case proto.VehicleInteractType_VEHICLE_INTERACT_TYPE_IN: + // 进入载具 + g.EnterVehicle(player, entity, avatar.Guid) + case proto.VehicleInteractType_VEHICLE_INTERACT_TYPE_OUT: + // 离开载具 + g.ExitVehicle(player, entity, avatar.Guid) + } +} diff --git a/gs/game/world_manager.go b/gs/game/world_manager.go index d3d7f033..6c05345e 100644 --- a/gs/game/world_manager.go +++ b/gs/game/world_manager.go @@ -1,6 +1,7 @@ package game import ( + "hk4e/pkg/logger" "math" "time" @@ -520,11 +521,10 @@ type MonsterEntity struct { const ( GADGET_TYPE_CLIENT = iota GADGET_TYPE_GATHER + GADGET_TYPE_VEHICLE // 载具 ) -type GadgetEntity struct { - gadgetType int - gatherId uint32 +type GadgetClientEntity struct { configId uint32 campId uint32 campType uint32 @@ -533,6 +533,24 @@ type GadgetEntity struct { propOwnerEntityId uint32 } +type GadgetGatherEntity struct { + gatherId uint32 +} + +type GadgetVehicleEntity struct { + vehicleId uint32 + owner *model.Player + curStamina float32 + memberMap map[uint32]*model.Player // uint32 = pos +} + +type GadgetEntity struct { + gadgetType int + gadgetClientEntity *GadgetClientEntity + gadgetGatherEntity *GadgetGatherEntity + gadgetVehicleEntity *GadgetVehicleEntity +} + // 场景实体数据结构 type Entity struct { @@ -662,13 +680,15 @@ func (s *Scene) ClientCreateEntityGadget(pos, rot *model.Vector, entityId uint32 entityType: uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_GADGET), level: 0, gadgetEntity: &GadgetEntity{ - gadgetType: GADGET_TYPE_CLIENT, - configId: configId, - campId: campId, - campType: campType, - ownerEntityId: ownerEntityId, - targetEntityId: targetEntityId, - propOwnerEntityId: propOwnerEntityId, + gadgetType: GADGET_TYPE_CLIENT, + gadgetClientEntity: &GadgetClientEntity{ + configId: configId, + campId: campId, + campType: campType, + ownerEntityId: ownerEntityId, + targetEntityId: targetEntityId, + propOwnerEntityId: propOwnerEntityId, + }, }, } s.entityMap[entity.id] = entity @@ -694,7 +714,47 @@ func (s *Scene) CreateEntityGadget(pos *model.Vector, gatherId uint32) uint32 { level: 0, gadgetEntity: &GadgetEntity{ gadgetType: GADGET_TYPE_GATHER, - gatherId: gatherId, + gadgetGatherEntity: &GadgetGatherEntity{ + gatherId: gatherId, + }, + }, + } + s.entityMap[entity.id] = entity + s.world.aoiManager.AddEntityIdToGridByPos(entity.id, float32(entity.pos.X), float32(entity.pos.Y), float32(entity.pos.Z)) + return entity.id +} + +func (s *Scene) CreateEntityGadgetVehicle(uid uint32, pos, rot *model.Vector, vehicleId uint32) uint32 { + player := USER_MANAGER.GetOnlineUser(uid) + if player == nil { + logger.LOG.Error("player is nil, uid: %v", uid) + return 0 + } + entityId := s.world.GetNextWorldEntityId(constant.EntityIdTypeConst.GADGET) + entity := &Entity{ + id: entityId, + scene: s, + pos: pos, + rot: rot, + moveState: uint16(proto.MotionState_MOTION_STATE_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_TYPE_GADGET), + level: 0, + gadgetEntity: &GadgetEntity{ + gadgetType: GADGET_TYPE_VEHICLE, + gadgetVehicleEntity: &GadgetVehicleEntity{ + vehicleId: vehicleId, + owner: player, + curStamina: 240, // TODO 应该也能在配置表找到 + memberMap: make(map[uint32]*model.Player), + }, }, } s.entityMap[entity.id] = entity diff --git a/protocol/cmd/cmd_id_proto_obj_map.go b/protocol/cmd/cmd_id_proto_obj_map.go index a7776132..8f11df4d 100644 --- a/protocol/cmd/cmd_id_proto_obj_map.go +++ b/protocol/cmd/cmd_id_proto_obj_map.go @@ -230,6 +230,12 @@ func (c *CmdProtoMap) registerAllMessage() { c.registerMessage(McoinExchangeHcoinReq, &proto.McoinExchangeHcoinReq{}) // 结晶换原石请求 c.registerMessage(McoinExchangeHcoinRsp, &proto.McoinExchangeHcoinRsp{}) // 结晶换原石响应 + // 载具 + c.registerMessage(CreateVehicleReq, &proto.CreateVehicleReq{}) // 创建载具请求 + c.registerMessage(CreateVehicleRsp, &proto.CreateVehicleRsp{}) // 创建载具响应 + c.registerMessage(VehicleInteractReq, &proto.VehicleInteractReq{}) // 载具交互请求 + c.registerMessage(VehicleInteractRsp, &proto.VehicleInteractRsp{}) // 载具交互响应 + // 乱七八糟 c.registerMessage(MarkMapReq, &proto.MarkMapReq{}) // 标记地图请求 c.registerMessage(TowerAllDataReq, &proto.TowerAllDataReq{}) // 深渊数据请求