diff --git a/Runtime/CObjectList.cpp b/Runtime/CObjectList.cpp index 56c3eb33c..1abb4d1be 100644 --- a/Runtime/CObjectList.cpp +++ b/Runtime/CObjectList.cpp @@ -9,7 +9,7 @@ CObjectList::CObjectList(EGameObjectList listEnum) void CObjectList::AddObject(CEntity& entity) { - if (IsQualified()) + if (IsQualified(entity)) { if (m_firstId != -1) m_list[m_firstId].prev = entity.GetUniqueId() & 0x3ff; @@ -67,6 +67,6 @@ CEntity* CObjectList::GetObjectById(TUniqueId uid) return ent.entity; } -bool CObjectList::IsQualified() {return true;} +bool CObjectList::IsQualified(const CEntity&) {return true;} } diff --git a/Runtime/CObjectList.hpp b/Runtime/CObjectList.hpp index f38c7e34e..010e74224 100644 --- a/Runtime/CObjectList.hpp +++ b/Runtime/CObjectList.hpp @@ -58,7 +58,7 @@ public: CEntity* GetObjectById(TUniqueId uid); TUniqueId GetFirstObjectIndex() const { return m_firstId; } TUniqueId GetNextObjectIndex(TUniqueId prev) const { return m_list[prev].next; } - virtual bool IsQualified(); + virtual bool IsQualified(const CEntity&); }; } diff --git a/Runtime/CStateManager.cpp b/Runtime/CStateManager.cpp index 8346c19ad..9a4dcd1b1 100644 --- a/Runtime/CStateManager.cpp +++ b/Runtime/CStateManager.cpp @@ -130,7 +130,7 @@ CStateManager::CStateManager(const std::weak_ptr&, x90c_loaderFuncs[int(EScriptObjectType::PlayerStateChange)] = ScriptLoader::LoadPlayerStateChange; x90c_loaderFuncs[int(EScriptObjectType::Thardus)] = ScriptLoader::LoadThardus; x90c_loaderFuncs[int(EScriptObjectType::WallCrawlerSwarm)] = ScriptLoader::LoadWallCrawlerSwarm; - x90c_loaderFuncs[int(EScriptObjectType::AIJumpPoint)] = ScriptLoader::LoadAIJumpPoint; + x90c_loaderFuncs[int(EScriptObjectType::AIJumpPoint)] = ScriptLoader::LoadAiJumpPoint; x90c_loaderFuncs[int(EScriptObjectType::FlaahgraTentacle)] = ScriptLoader::LoadFlaahgraTentacle; x90c_loaderFuncs[int(EScriptObjectType::RoomAcoustics)] = ScriptLoader::LoadRoomAcoustics; x90c_loaderFuncs[int(EScriptObjectType::ColorModulate)] = ScriptLoader::LoadColorModulate; @@ -646,6 +646,11 @@ void CStateManager::RemoveObject(TUniqueId) { } +void CStateManager::RemoveActor(TUniqueId) +{ + +} + void CStateManager::UpdateRoomAcoustics(TAreaId) { } diff --git a/Runtime/CStateManager.hpp b/Runtime/CStateManager.hpp index 4116e71e2..a6498b04b 100644 --- a/Runtime/CStateManager.hpp +++ b/Runtime/CStateManager.hpp @@ -246,6 +246,7 @@ public: CObjectList* ObjectListById(EGameObjectList type); const CObjectList* GetObjectListById(EGameObjectList type) const; void RemoveObject(TUniqueId); + void RemoveActor(TUniqueId); void UpdateRoomAcoustics(TAreaId); void SetCurrentAreaId(TAreaId); void ClearGraveyard(); diff --git a/Runtime/GameObjectLists.cpp b/Runtime/GameObjectLists.cpp index eeb31fb34..edffa453f 100644 --- a/Runtime/GameObjectLists.cpp +++ b/Runtime/GameObjectLists.cpp @@ -1,27 +1,80 @@ #include "GameObjectLists.hpp" +#include "World/CGameLight.hpp" +#include "World/CScriptDoor.hpp" +#include "World/CScriptPlatform.hpp" +#include "World/CScriptCoverPoint.hpp" +#include "World/CScriptAiJumpPoint.hpp" +#include "World/CPatterned.hpp" +#include "Camera/CGameCamera.hpp" namespace urde { CActorList::CActorList() -: CObjectList(EGameObjectList::Actor) {} + : CObjectList(EGameObjectList::Actor) {} + +bool CActorList::IsQualified(const CEntity& ent) +{ + return static_cast(&ent) != nullptr; +} CPhysicsActorList::CPhysicsActorList() -: CObjectList(EGameObjectList::PhysicsActor) {} + : CObjectList(EGameObjectList::PhysicsActor) {} + +bool CPhysicsActorList::IsQualified(const CEntity& ent) +{ + return static_cast(&ent) != nullptr; +} CGameCameraList::CGameCameraList() -: CObjectList(EGameObjectList::GameCamera) {} + : CObjectList(EGameObjectList::GameCamera) {} + +bool CGameCameraList::IsQualified(const CEntity& ent) +{ + return static_cast(&ent) != nullptr; +} CListeningAiList::CListeningAiList() -: CObjectList(EGameObjectList::ListeningAi) {} + : CObjectList(EGameObjectList::ListeningAi) {} + +bool CListeningAiList::IsQualified(const CEntity& ent) +{ + return (static_cast(&ent) != nullptr); +} CAiWaypointList::CAiWaypointList() -: CObjectList(EGameObjectList::AiWaypoint) {} + : CObjectList(EGameObjectList::AiWaypoint) {} + +bool CAiWaypointList::IsQualified(const CEntity& ent) +{ + return static_cast(&ent) != nullptr || + static_cast(&ent) != nullptr; +} CPlatformAndDoorList::CPlatformAndDoorList() -: CObjectList(EGameObjectList::PlatformAndDoor) {} + : CObjectList(EGameObjectList::PlatformAndDoor) {} + +bool CPlatformAndDoorList::IsQualified(const CEntity& ent) +{ + return IsDoor(ent) || IsPlatform(ent); +} + +bool CPlatformAndDoorList::IsDoor(const CEntity& ent) +{ + return static_cast(&ent) != nullptr; +} + +bool CPlatformAndDoorList::IsPlatform(const CEntity& ent) +{ + return static_cast(&ent) != nullptr; +} CGameLightList::CGameLightList() -: CObjectList(EGameObjectList::GameLight) {} + : CObjectList(EGameObjectList::GameLight) {} + +bool CGameLightList::IsQualified(const CEntity& lt) +{ + return static_cast(<) != nullptr; +} } diff --git a/Runtime/GameObjectLists.hpp b/Runtime/GameObjectLists.hpp index f147d6d01..1d5fe78ce 100644 --- a/Runtime/GameObjectLists.hpp +++ b/Runtime/GameObjectLists.hpp @@ -10,42 +10,55 @@ class CActorList : public CObjectList { public: CActorList(); + + bool IsQualified(const CEntity&); }; class CPhysicsActorList : public CObjectList { public: CPhysicsActorList(); + bool IsQualified(const CEntity&); }; class CGameCameraList : public CObjectList { public: CGameCameraList(); + bool IsQualified(const CEntity&); }; class CListeningAiList : public CObjectList { public: CListeningAiList(); + + bool IsQualified(const CEntity&); }; class CAiWaypointList : public CObjectList { public: CAiWaypointList(); + bool IsQualified(const CEntity&); }; class CPlatformAndDoorList : public CObjectList { public: CPlatformAndDoorList(); + + bool IsQualified(const CEntity&); + bool IsDoor(const CEntity&); + bool IsPlatform(const CEntity&); }; class CGameLightList : public CObjectList { public: CGameLightList(); + + bool IsQualified(const CEntity&); }; } diff --git a/Runtime/Graphics/CLight.hpp b/Runtime/Graphics/CLight.hpp index 06e05cdf4..62e8f4e69 100644 --- a/Runtime/Graphics/CLight.hpp +++ b/Runtime/Graphics/CLight.hpp @@ -28,6 +28,7 @@ class CLight friend class CGuiLight; friend class CBooModel; friend class CBooRenderer; + friend class CGameLight; zeus::CVector3f x0_pos; zeus::CVector3f xc_dir; @@ -67,11 +68,15 @@ public: x0_pos = pos; } + const zeus::CVector3f& GetPosition() const { return x0_pos; } + void SetDirection(const zeus::CVector3f& dir) { xc_dir = dir; } + const zeus::CVector3f& GetDirection() const { return xc_dir; } + void SetColor(const zeus::CColor& col) { x18_color = col; @@ -107,6 +112,8 @@ public: return x44_cachedRadius; } + ELightType GetType() const { return x1c_type; } + float GetIntensity() const; const zeus::CColor& GetColor() const { return x18_color; } diff --git a/Runtime/World/CEntity.hpp b/Runtime/World/CEntity.hpp index f596735e5..f0894650d 100644 --- a/Runtime/World/CEntity.hpp +++ b/Runtime/World/CEntity.hpp @@ -21,8 +21,8 @@ class CEntityInfo std::vector x4_conns; TEditorId x14_editorId; public: - CEntityInfo(TAreaId aid, const std::vector& conns, ResId savwId=-1) - : x0_areaId(aid), x4_conns(conns) {} + CEntityInfo(TAreaId aid, const std::vector& conns, TEditorId eid = kInvalidEditorId) + : x0_areaId(aid), x4_conns(conns), x14_editorId(eid) {} TAreaId GetAreaId() const {return x0_areaId;} std::vector GetConnectionList() const { return x4_conns; } TEditorId GetEditorId() const { return x14_editorId; } diff --git a/Runtime/World/CGameLight.cpp b/Runtime/World/CGameLight.cpp new file mode 100644 index 000000000..aefec0328 --- /dev/null +++ b/Runtime/World/CGameLight.cpp @@ -0,0 +1,58 @@ +#include "CGameLight.hpp" +#include "CActorParameters.hpp" +#include "CStateManager.hpp" + +namespace urde +{ + +CGameLight::CGameLight(TUniqueId uid, TAreaId aid, bool active, const std::string& name, const zeus::CTransform& xf, + TUniqueId parentId, const CLight& light, u32 w1, u32 w2, float f1) +: CActor(uid, active, name, CEntityInfo::CEntityInfo(aid, CEntity::NullConnectionList), xf, + CModelData::CModelDataNull(), CMaterialList(), CActorParameters::None(), kInvalidUniqueId), + xe8_parentId(parentId), xec_light(light), x13c_(w1), x140_(w2), x144_(f1) +{ + xec_light.GetRadius(); + xec_light.GetIntensity(); + SetLightPriorityAndId(); +} + +void CGameLight::Think(float dt, CStateManager& mgr) +{ + if (x144_ <= 0.f) + return; + x144_ -= dt; + + if (x144_ <= 0.f) + mgr.RemoveActor(GetUniqueId()); +} + +void CGameLight::SetLightPriorityAndId() +{ + xec_light.x3c_ = x140_; + xec_light.x40_loadedIdx = x13c_; +} + +void CGameLight::SetLight(const CLight& light) +{ + xec_light = light; + xec_light.GetRadius(); + xec_light.GetIntensity(); + SetLightPriorityAndId(); +} + +CLight CGameLight::GetLight() const +{ + CLight ret = xec_light; + ret.SetPosition(x34_transform * xec_light.GetPosition()); + + if (ret.GetType() != ELightType::Point) + ret.SetDirection(x34_transform.rotate(xec_light.GetDirection()).normalized()); + + return ret; +} + +TUniqueId CGameLight::GetParentId() const +{ + return xe8_parentId; +} +} diff --git a/Runtime/World/CGameLight.hpp b/Runtime/World/CGameLight.hpp new file mode 100644 index 000000000..3b1ed8c8c --- /dev/null +++ b/Runtime/World/CGameLight.hpp @@ -0,0 +1,27 @@ +#ifndef __URDE_CGAMELIGHT_HPP__ +#define __URDE_CGAMELIGHT_HPP__ + +#include "CActor.hpp" + +namespace urde +{ +class CGameLight : public CActor +{ + TUniqueId xe8_parentId; + CLight xec_light; + u32 x13c_; + u32 x140_; + float x144_; + +public: + CGameLight(TUniqueId, TAreaId, bool, const std::string&, const zeus::CTransform&, TUniqueId, const CLight&, u32, + u32, float); + + void Think(float, CStateManager&); + void SetLightPriorityAndId(); + void SetLight(const CLight&); + CLight GetLight() const; + TUniqueId GetParentId() const; +}; +} +#endif // __URDE_CGAMELIGHT_HPP__ diff --git a/Runtime/World/CMakeLists.txt b/Runtime/World/CMakeLists.txt index 5f3004b3e..f63add2f8 100644 --- a/Runtime/World/CMakeLists.txt +++ b/Runtime/World/CMakeLists.txt @@ -74,6 +74,7 @@ set(WORLD_SOURCES CScriptActorRotate.hpp CScriptActorRotate.cpp CScriptSpecialFunction.hpp CScriptSpecialFunction.cpp CScriptPlayerActor.hpp CScriptPlayerActor.cpp + CScriptAiJumpPoint.hpp CScriptAiJumpPoint.cpp CScriptColorModulate.hpp CScriptColorModulate.cpp CGrappleParameters.hpp CActorParameters.hpp @@ -89,6 +90,7 @@ set(WORLD_SOURCES CPatterned.hpp CPatterned.cpp CHUDMemoParms.hpp CHUDMemoParms.cpp CWorldShadow.hpp CWorldShadow.cpp + CGameLight.hpp CGameLight.cpp CFluidPlane.hpp) runtime_add_list(World WORLD_SOURCES) diff --git a/Runtime/World/CScriptAiJumpPoint.cpp b/Runtime/World/CScriptAiJumpPoint.cpp new file mode 100644 index 000000000..281f62057 --- /dev/null +++ b/Runtime/World/CScriptAiJumpPoint.cpp @@ -0,0 +1,60 @@ +#include "CScriptAiJumpPoint.hpp" +#include "CActorParameters.hpp" +#include "CStateManager.hpp" +#include "CScriptWaypoint.hpp" + +namespace urde +{ +CScriptAiJumpPoint::CScriptAiJumpPoint(TUniqueId uid, const std::string& name, const CEntityInfo& info, + zeus::CTransform& xf, bool active, float f1) + : CActor(uid, active, name, info, xf, CModelData::CModelDataNull(), CMaterialList(EMaterialTypes::Zero), + CActorParameters::None(), kInvalidUniqueId), + xe8_(f1) +{ + xec_.emplace(xf.origin, xf.origin); +} + +void CScriptAiJumpPoint::Think(float dt, CStateManager&) +{ + if (x110_timeRemaining <= 0) + return; + + x110_timeRemaining -= dt; +} + +void CScriptAiJumpPoint::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId other, CStateManager& mgr) +{ + AcceptScriptMsg(msg, other, mgr); + + if (msg != EScriptObjectMessage::InternalMessage13) + return; + + for (SConnection& conn : x20_conns) + { + if (conn.x0_state != EScriptObjectState::Arrived || conn.x4_msg != EScriptObjectMessage::Next) + continue; + + const CScriptWaypoint* wpnt = static_cast(mgr.GetObjectById(mgr.GetIdForScript(conn.x8_objId))); + if (wpnt) + { + x10c_currentWaypoint = wpnt->GetUniqueId(); + const CScriptWaypoint* nextWpnt = wpnt->NextWaypoint(mgr); + if (nextWpnt) + x10e_nextWaypoint = nextWpnt->GetUniqueId(); + } + } +} + +rstl::optional_object CScriptAiJumpPoint::GetTouchBounds() const +{ + return xec_; +} + +bool CScriptAiJumpPoint::GetInUse(TUniqueId uid) const +{ + if (x108_24 || x110_timeRemaining > 0.f || x10a_occupant != kInvalidUniqueId || uid != kInvalidUniqueId || uid != x10a_occupant) + return true; + + return false; +} +} diff --git a/Runtime/World/CScriptAiJumpPoint.hpp b/Runtime/World/CScriptAiJumpPoint.hpp new file mode 100644 index 000000000..8ff001908 --- /dev/null +++ b/Runtime/World/CScriptAiJumpPoint.hpp @@ -0,0 +1,37 @@ +#ifndef __URDE_CSCRIPTAIJUMPPOINT_HPP__ +#define __URDE_CSCRIPTAIJUMPPOINT_HPP__ + +#include "CActor.hpp" + +namespace urde +{ +class CScriptAiJumpPoint : public CActor +{ +private: + float xe8_; + std::experimental::optional xec_; + union + { + struct + { + bool x108_24 : 1; + }; + u8 dummy = 0; + }; + TUniqueId x10a_occupant = kInvalidUniqueId; + TUniqueId x10c_currentWaypoint = kInvalidUniqueId; + TUniqueId x10e_nextWaypoint = kInvalidUniqueId; + float x110_timeRemaining = 0.f; +public: + CScriptAiJumpPoint(TUniqueId, const std::string&, const CEntityInfo&, zeus::CTransform&, bool, float); + + + void Think(float, CStateManager&); + void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager &); + void AddToRenderer(const zeus::CFrustum &, const CStateManager &) const {} + void Render(const CStateManager &) const {} + rstl::optional_object GetTouchBounds() const; + bool GetInUse(TUniqueId uid) const; +}; +} +#endif // __URDE_CSCRIPTAIJUMPPOINT_HPP__ diff --git a/Runtime/World/CScriptCoverPoint.cpp b/Runtime/World/CScriptCoverPoint.cpp index 8809da055..5c475d916 100644 --- a/Runtime/World/CScriptCoverPoint.cpp +++ b/Runtime/World/CScriptCoverPoint.cpp @@ -14,9 +14,7 @@ CScriptCoverPoint::CScriptCoverPoint(TUniqueId uid, const std::string &name, con { xec_cosHorizontalAngle = std::cos(zeus::degToRad(horizontalAngle) * 0.5f); xf0_sinVerticalAngle = std::sin(zeus::degToRad(verticalAngle) * 0.5f); - zeus::CMatrix4f mtx = xf.toMatrix4f().transposed(); - x100_touchBounds.emplace(zeus::CAABox({mtx.vec[1].x, mtx.vec[2].y, mtx.vec[3].z}, - {mtx.vec[1].x, mtx.vec[2].y, mtx.vec[3].z})); + x100_touchBounds.emplace(xf.origin, xf.origin); } void CScriptCoverPoint::Think(float delta, CStateManager&) @@ -60,16 +58,14 @@ bool CScriptCoverPoint::Blown(const zeus::CVector3f& point) const if (ShouldWallHang()) { - zeus::CMatrix4f mtx = x34_transform.toMatrix4f().transposed(); - zeus::CVector3f posDif = point - zeus::CVector3f(mtx.vec[1].x, mtx.vec[2].y, mtx.vec[3].z); + zeus::CVector3f posDif = point - x34_transform.origin; posDif *= (1.0 / posDif.magnitude()); zeus::CVector3f normDif = posDif.normalized(); - /* zeus::CVector3f unkVec(mtx.vec[0].y, mtx.vec[1].z, mtx.vec[3].x); */ - zeus::CVector3f unkVec2(mtx.vec[1].z, mtx.vec[0].y, 0.f); - unkVec2.normalize(); + zeus::CVector3f frontVec = x34_transform.frontVector(); + frontVec.normalize(); - if (unkVec2.dot(normDif) <= GetCosHorizontalAngle() || (posDif.z * posDif.z) >= GetSinSqVerticalAngle()) + if (frontVec.dot(normDif) <= GetCosHorizontalAngle() || (posDif.z * posDif.z) >= GetSinSqVerticalAngle()) return true; } return false; diff --git a/Runtime/World/CScriptWaypoint.hpp b/Runtime/World/CScriptWaypoint.hpp index fa0c82086..f79d54e99 100644 --- a/Runtime/World/CScriptWaypoint.hpp +++ b/Runtime/World/CScriptWaypoint.hpp @@ -12,6 +12,8 @@ public: CScriptWaypoint(TUniqueId, const std::string&, const CEntityInfo&, const zeus::CTransform&, bool, float, float, u32, u32, u32, u32, u32, u32, u32); + + const CScriptWaypoint* NextWaypoint(CStateManager&) const { return nullptr; } }; } diff --git a/Runtime/World/ScriptLoader.cpp b/Runtime/World/ScriptLoader.cpp index abcfbc163..0bc346ab6 100644 --- a/Runtime/World/ScriptLoader.cpp +++ b/Runtime/World/ScriptLoader.cpp @@ -42,6 +42,7 @@ #include "CScriptDistanceFog.hpp" #include "CScriptActorRotate.hpp" #include "CScriptSpecialFunction.hpp" +#include "CScriptAiJumpPoint.hpp" #include "CScriptColorModulate.hpp" #include "Camera/CCinematicCamera.hpp" #include "MP1/CNewIntroBoss.hpp" @@ -1346,15 +1347,15 @@ CEntity* ScriptLoader::LoadCoverPoint(CStateManager& mgr, CInputStream& in, int return nullptr; SActorHead head = LoadActorHead(in, mgr); - bool b1 = in.readBool(); - u32 w1 = in.readUint32Big(); - bool b2 = in.readBool(); - float f1 = in.readFloatBig(); - float f2 = in.readFloatBig(); - float f3 = in.readFloatBig(); + bool active = in.readBool(); + u32 flags = in.readUint32Big(); + bool crouch = in.readBool(); + float horizontalAngle = in.readFloatBig(); + float verticalAngle = in.readFloatBig(); + float coverTime = in.readFloatBig(); - return new CScriptCoverPoint(mgr.AllocateUniqueId(), head.x0_name, info, head.x10_transform, b1, w1, b2, f1, f2, - f3); + return new CScriptCoverPoint(mgr.AllocateUniqueId(), head.x0_name, info, head.x10_transform, active, flags, crouch, + horizontalAngle, verticalAngle, coverTime); } CEntity* ScriptLoader::LoadSpiderBallWaypoint(CStateManager& mgr, CInputStream& in, int propCount, @@ -1677,9 +1678,16 @@ CEntity* ScriptLoader::LoadWallCrawlerSwarm(CStateManager& mgr, CInputStream& in return nullptr; } -CEntity* ScriptLoader::LoadAIJumpPoint(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) +CEntity* ScriptLoader::LoadAiJumpPoint(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) { - return nullptr; + if (!EnsurePropertyCount(propCount, 5, "AiJumpPoint")) + return nullptr; + + SActorHead aHead = LoadActorHead(in, mgr); + bool active = in.readBool(); + float f1 = in.readFloat(); + + return new CScriptAiJumpPoint(mgr.AllocateUniqueId(), aHead.x0_name, info, aHead.x10_transform, active, f1); } CEntity* ScriptLoader::LoadFlaahgraTentacle(CStateManager& mgr, CInputStream& in, int propCount, diff --git a/Runtime/World/ScriptLoader.hpp b/Runtime/World/ScriptLoader.hpp index 32afcf1e6..d1821d01b 100644 --- a/Runtime/World/ScriptLoader.hpp +++ b/Runtime/World/ScriptLoader.hpp @@ -117,7 +117,7 @@ public: static CEntity* LoadPlayerStateChange(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); static CEntity* LoadThardus(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); static CEntity* LoadWallCrawlerSwarm(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); - static CEntity* LoadAIJumpPoint(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); + static CEntity* LoadAiJumpPoint(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); static CEntity* LoadFlaahgraTentacle(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); static CEntity* LoadRoomAcoustics(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); static CEntity* LoadColorModulate(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info); diff --git a/Runtime/World/ScriptObjectSupport.hpp b/Runtime/World/ScriptObjectSupport.hpp index 75618d04e..e2a4f8959 100644 --- a/Runtime/World/ScriptObjectSupport.hpp +++ b/Runtime/World/ScriptObjectSupport.hpp @@ -216,7 +216,8 @@ enum class EScriptObjectMessage InternalMessage14 = 36, InternalMessage15 = 37, InternalMessage16 = 38, - InternalMessage17 = 39 + InternalMessage17 = 39, + InternalMessage18 = 40, }; }