From 158e81be6b8d369268407e858af260b7bf39eb80 Mon Sep 17 00:00:00 2001 From: xeon Date: Thu, 25 Jan 2024 13:55:10 +0300 Subject: [PATCH] Implement PropEntity spawn --- .../Level/Capture/StageObjectCapture.cs | 7 +++ RPG.GameCore/Level/Group/LevelGroupInfo.cs | 2 +- RPG.GameCore/Level/Objects/LevelPropInfo.cs | 35 +++++++++++ .../Game/Entity/AvatarEntity.cs | 2 +- .../Game/Entity/Factory/EntityFactory.cs | 7 ++- .../Game/Entity/PropEntity.cs | 34 +++++++++++ .../Game/Maze/MazeManager.cs | 58 ++++++++----------- RPG.Services.Gameserver/Game/Util/MazeUtil.cs | 25 ++++++++ .../Modules/AdventureModule.cs | 4 +- .../Modules/BattleModule.cs | 4 +- 10 files changed, 137 insertions(+), 41 deletions(-) create mode 100644 RPG.GameCore/Level/Capture/StageObjectCapture.cs create mode 100644 RPG.GameCore/Level/Objects/LevelPropInfo.cs create mode 100644 RPG.Services.Gameserver/Game/Entity/PropEntity.cs create mode 100644 RPG.Services.Gameserver/Game/Util/MazeUtil.cs diff --git a/RPG.GameCore/Level/Capture/StageObjectCapture.cs b/RPG.GameCore/Level/Capture/StageObjectCapture.cs new file mode 100644 index 0000000..b0e1616 --- /dev/null +++ b/RPG.GameCore/Level/Capture/StageObjectCapture.cs @@ -0,0 +1,7 @@ +namespace RPG.GameCore.Level.Capture; + +public class StageObjectCapture +{ + public string BlockAlias { get; set; } = string.Empty; // 0x10 + public string PrefabAlias { get; set; } = string.Empty; // 0x18 +} diff --git a/RPG.GameCore/Level/Group/LevelGroupInfo.cs b/RPG.GameCore/Level/Group/LevelGroupInfo.cs index ed41fd3..a7f62c9 100644 --- a/RPG.GameCore/Level/Group/LevelGroupInfo.cs +++ b/RPG.GameCore/Level/Group/LevelGroupInfo.cs @@ -19,7 +19,7 @@ public class LevelGroupInfo public LevelAnchorInfo[] AnchorList { get; set; } = []; // 0x50 // TODO: public LevelModelInfo[] ModelList { get; set; } // 0x58 public LevelMonsterInfo[] MonsterList { get; set; } = []; // 0x60 - // TODO: public LevelPropInfo[] PropList { get; set; } // 0x68 + public LevelPropInfo[] PropList { get; set; } = []; // 0x68 // TODO: public LevelWaypointInfo[] WaypointList { get; set; } // 0x70 // TODO: public LevelPathwayInfo[] PathwayList { get; set; } // 0x78 // TODO: public LevelBattleAreaInfo[] BattleAreaList { get; set; } // 0x80 diff --git a/RPG.GameCore/Level/Objects/LevelPropInfo.cs b/RPG.GameCore/Level/Objects/LevelPropInfo.cs new file mode 100644 index 0000000..23370be --- /dev/null +++ b/RPG.GameCore/Level/Objects/LevelPropInfo.cs @@ -0,0 +1,35 @@ +using RPG.GameCore.Enums; +using RPG.GameCore.Level.Capture; +using RPG.GameCore.Level.Graph; +using RPG.GameCore.Level.Trigger; +using System.Text.Json.Serialization; + +namespace RPG.GameCore.Level.Objects; + +public class LevelPropInfo : NamedLevelObjectInfo +{ + public float RotX { get; set; } // 0x40 + public float RotZ { get; set; } // 0x44 + public uint PropID { get; set; } // 0x48 + public bool IsLimitedLife { get; set; } // 0x4C + public float LifeTime { get; set; } // 0x50 + public string InitLevelGraph { get; set; } = string.Empty; // 0x58 + public LevelTriggerInfo? Trigger { get; set; } // 0x60 + public bool CreateOnInitial { get; set; } // 0x68 + public LevelGraphValueSource? ValueSource { get; set; } // 0x70 + public uint CampID { get; set; } // 0x78 + [JsonConverter(typeof(JsonStringEnumConverter))] + public PropState State { get; set; } // 0x7C + public uint RecordID { get; set; } // 0x80 + public uint EventID { get; set; } // 0x84 + public uint AnchorGroupID { get; set; } // 0x88 + public uint AnchorID { get; set; } // 0x8C + public uint MapTeleportID { get; set; } // 0x90 + public uint ChestID { get; set; } // 0x94 + public uint DialogueTriggerAngle { get; set; } // 0x98 + public uint[] DialogueGroups { get; set; } = []; // 0xA0 + public bool OverrideTriggerHint { get; set; } // 0xA8 + public float HintRange { get; set; } // 0xAC + public uint[] ServerInteractVerificationIDList { get; set; } = []; // 0xB0 + public StageObjectCapture? StageObjectCapture { get; set; } // 0xB8 +} diff --git a/RPG.Services.Gameserver/Game/Entity/AvatarEntity.cs b/RPG.Services.Gameserver/Game/Entity/AvatarEntity.cs index 1b26b3b..a4a2883 100644 --- a/RPG.Services.Gameserver/Game/Entity/AvatarEntity.cs +++ b/RPG.Services.Gameserver/Game/Entity/AvatarEntity.cs @@ -8,7 +8,7 @@ internal class AvatarEntity : EntityBase public AvatarType AvatarType { get; } public uint Uid { get; } - public AvatarEntity(uint id, uint groupId, uint instanceId, uint avatarId, AvatarType type, uint uid) : base(id, groupId, instanceId) + public AvatarEntity(uint id, uint avatarId, AvatarType type, uint uid) : base(id, 0, 0) { AvatarId = avatarId; AvatarType = type; diff --git a/RPG.Services.Gameserver/Game/Entity/Factory/EntityFactory.cs b/RPG.Services.Gameserver/Game/Entity/Factory/EntityFactory.cs index a151283..c627a66 100644 --- a/RPG.Services.Gameserver/Game/Entity/Factory/EntityFactory.cs +++ b/RPG.Services.Gameserver/Game/Entity/Factory/EntityFactory.cs @@ -9,7 +9,7 @@ internal class EntityFactory public AvatarEntity CreateAvatarEntity(uint avatarId, AvatarType type, uint uid) { - return new(NextEntityId(), 0, 0, avatarId, type, uid); + return new(NextEntityId(), avatarId, type, uid); } public NpcMonsterEntity CreateNpcMonsterEntity(LevelMonsterInfo levelMonster, uint groupId) @@ -17,6 +17,11 @@ internal class EntityFactory return new(NextEntityId(), groupId, levelMonster.ID, levelMonster.NPCMonsterID, levelMonster.EventID); } + public PropEntity CreatePropEntity(LevelPropInfo levelProp, uint groupId) + { + return new(NextEntityId(), groupId, levelProp.ID, levelProp); + } + public void Reset() { _entityIdSeed = 0; diff --git a/RPG.Services.Gameserver/Game/Entity/PropEntity.cs b/RPG.Services.Gameserver/Game/Entity/PropEntity.cs new file mode 100644 index 0000000..bdcb6e9 --- /dev/null +++ b/RPG.Services.Gameserver/Game/Entity/PropEntity.cs @@ -0,0 +1,34 @@ +using RPG.GameCore.Enums; +using RPG.GameCore.Level.Objects; +using RPG.Network.Proto; + +namespace RPG.Services.Gameserver.Game.Entity; + +internal class PropEntity : EntityBase +{ + public uint PropId { get; } + public PropState State { get; set; } + + public PropEntity(uint id, uint groupId, uint instanceId, LevelPropInfo info) : base(id, groupId, instanceId) + { + PropId = info.PropID; + State = info.State; + } + + public override EntityType Type => EntityType.EntityProp; + + public override SceneEntityInfo SceneEntityInfo + { + get + { + SceneEntityInfo info = base.SceneEntityInfo; + info.Prop = new() + { + PropId = PropId, + PropState = (uint)State + }; + + return info; + } + } +} diff --git a/RPG.Services.Gameserver/Game/Maze/MazeManager.cs b/RPG.Services.Gameserver/Game/Maze/MazeManager.cs index da785ba..739f253 100644 --- a/RPG.Services.Gameserver/Game/Maze/MazeManager.cs +++ b/RPG.Services.Gameserver/Game/Maze/MazeManager.cs @@ -8,13 +8,13 @@ using RPG.Network.Proto; using RPG.Services.Gameserver.Game.Entity; using RPG.Services.Gameserver.Game.Entity.Factory; using RPG.Services.Gameserver.Game.Team; +using RPG.Services.Gameserver.Game.Util; using MazeInfo = RPG.Network.Proto.Maze; namespace RPG.Services.Gameserver.Game.Maze; internal class MazeManager { - private readonly EntityManager _entityManager; private readonly EntityFactory _entityFactory; private readonly ExcelTables _excelTables; @@ -26,38 +26,35 @@ internal class MazeManager private PlayerTeam? _playerTeam; private uint _uid; + public EntityManager EntityManager { get; } + public MazeManager(EntityManager entityManager, EntityFactory entityFactory, ExcelTables excelTables, LevelTables levelTables) { - _entityManager = entityManager; + EntityManager = entityManager; _entityFactory = entityFactory; _excelTables = excelTables; _levelTables = levelTables; } - public EntityBase? GetEntityById(uint entityId) - { - return _entityManager.GetEntityById(entityId); - } - public void ResetAvatarPosition() { foreach (TeamMember? member in _playerTeam!.Members) { if (member == null) continue; - AvatarEntity? entity = _entityManager.GetAvatar(_uid, member.AvatarId); + AvatarEntity? entity = EntityManager.GetAvatar(_uid, member.AvatarId); if (entity != null) { entity.SetMotion(CreateInitialAvatarMotion()); - _entityManager.NotifyUpdate(entity); + EntityManager.NotifyUpdate(entity); } } } public void RemoveEntities(IEnumerable entities) { - _entityManager.RemoveEntities(entities); + EntityManager.RemoveEntities(entities); } public void SetPlayerUid(uint uid) @@ -104,7 +101,7 @@ internal class MazeManager { if (_mapEntry == null || _playerTeam == null) throw new InvalidOperationException("GetSceneInfo: scene not loaded!"); - AvatarEntity? leaderEntity = _entityManager.GetAvatar(_uid, _playerTeam.Leader.AvatarId); + AvatarEntity? leaderEntity = EntityManager.GetAvatar(_uid, _playerTeam.Leader.AvatarId); SceneInfo info = new() { @@ -114,7 +111,7 @@ internal class MazeManager LeaderEntityId = leaderEntity?.Id ?? 0, }; - info.EntityList.AddRange(_entityManager.EntityInfoList); + info.EntityList.AddRange(EntityManager.EntityInfoList); return info; } @@ -132,9 +129,19 @@ internal class MazeManager if (!levelMonster.CreateOnInitial) continue; NpcMonsterEntity monsterEntity = _entityFactory.CreateNpcMonsterEntity(levelMonster, groupInstanceInfo.ID); - monsterEntity.SetMotion(CreateDefaultMotion(levelMonster)); + monsterEntity.SetMotion(MazeUtil.CreateDefaultMotion(levelMonster)); - _entityManager.AddEntity(monsterEntity); + EntityManager.AddEntity(monsterEntity); + } + + foreach (LevelPropInfo levelProp in levelGroup.PropList) + { + if (!levelProp.CreateOnInitial) continue; + + PropEntity propEntity = _entityFactory.CreatePropEntity(levelProp, groupInstanceInfo.ID); + propEntity.SetMotion(MazeUtil.CreateDefaultMotion(levelProp)); + + EntityManager.AddEntity(propEntity); } } } @@ -148,7 +155,7 @@ internal class MazeManager AvatarEntity entity = _entityFactory.CreateAvatarEntity(member.AvatarId, member.AvatarType, _uid); entity.SetMotion(CreateInitialAvatarMotion()); - _entityManager.AddEntity(entity); + EntityManager.AddEntity(entity); } } @@ -157,29 +164,12 @@ internal class MazeManager LevelGroupInfo startGroup = _levelTables.GetGroupInfo(_floor!.StartGroup.GroupGUID)!; LevelAnchorInfo startAnchor = startGroup.AnchorList.First(anchor => anchor.ID == _floor.StartAnchorID); - return CreateDefaultMotion(startAnchor); - } - - private static MotionInfo CreateDefaultMotion(NamedLevelObjectInfo info) - { - return new() - { - Pos = new() - { - X = MathUtil.GetLogicCoordValue(info.PosX), - Y = MathUtil.GetLogicCoordValue(info.PosY), - Z = MathUtil.GetLogicCoordValue(info.PosZ) - }, - Rot = new() - { - Y = MathUtil.GetLogicCoordValue(info.RotY) - } - }; + return MazeUtil.CreateDefaultMotion(startAnchor); } private void BeforeEnter() { _entityFactory.Reset(); - _entityManager.Clear(); + EntityManager.Clear(); } } diff --git a/RPG.Services.Gameserver/Game/Util/MazeUtil.cs b/RPG.Services.Gameserver/Game/Util/MazeUtil.cs new file mode 100644 index 0000000..c773f5b --- /dev/null +++ b/RPG.Services.Gameserver/Game/Util/MazeUtil.cs @@ -0,0 +1,25 @@ +using RPG.GameCore.Level.Objects; +using RPG.GameCore.Util; +using RPG.Network.Proto; + +namespace RPG.Services.Gameserver.Game.Util; + +internal static class MazeUtil +{ + public static MotionInfo CreateDefaultMotion(NamedLevelObjectInfo info) + { + return new() + { + Pos = new() + { + X = MathUtil.GetLogicCoordValue(info.PosX), + Y = MathUtil.GetLogicCoordValue(info.PosY), + Z = MathUtil.GetLogicCoordValue(info.PosZ) + }, + Rot = new() + { + Y = MathUtil.GetLogicCoordValue(info.RotY) + } + }; + } +} diff --git a/RPG.Services.Gameserver/Modules/AdventureModule.cs b/RPG.Services.Gameserver/Modules/AdventureModule.cs index 900c4cc..eb0bd49 100644 --- a/RPG.Services.Gameserver/Modules/AdventureModule.cs +++ b/RPG.Services.Gameserver/Modules/AdventureModule.cs @@ -65,11 +65,11 @@ internal class AdventureModule : BaseModule SceneCastSkillCsReq req = SceneCastSkillCsReq.Parser.ParseFrom(body.Span); BattleModule battleModule = ModuleManager.Get(); - if (_mazeManager.GetEntityById(req.CastEntityId) is NpcMonsterEntity) + if (_mazeManager.EntityManager.GetEntityById(req.CastEntityId) is NpcMonsterEntity) { battleModule.OnBeingHitByMonster(req.CastEntityId, req.AssistMonsterEntityIdList); } - else if (_mazeManager.GetEntityById(req.AbilityTargetEntityId) is NpcMonsterEntity) + else if (_mazeManager.EntityManager.GetEntityById(req.AbilityTargetEntityId) is NpcMonsterEntity) { battleModule.OnSuccessfulAttack(req.AbilityTargetEntityId, req.AssistMonsterEntityIdList); } diff --git a/RPG.Services.Gameserver/Modules/BattleModule.cs b/RPG.Services.Gameserver/Modules/BattleModule.cs index 2032d64..2e391e2 100644 --- a/RPG.Services.Gameserver/Modules/BattleModule.cs +++ b/RPG.Services.Gameserver/Modules/BattleModule.cs @@ -72,14 +72,14 @@ internal class BattleModule : BaseModule List battleEvents = []; - if (_mazeManager.GetEntityById(monsterEntityId) is NpcMonsterEntity monster) + if (_mazeManager.EntityManager.GetEntityById(monsterEntityId) is NpcMonsterEntity monster) { battleEvents.Add(monster.EventId); } foreach (uint assistEntityId in assists) { - if (_mazeManager.GetEntityById(assistEntityId) is NpcMonsterEntity assistMonster) + if (_mazeManager.EntityManager.GetEntityById(assistEntityId) is NpcMonsterEntity assistMonster) { battleEvents.Add(assistMonster.EventId); }