From 21159e604e5c9eb0e054225c1bf3e83bc4226327 Mon Sep 17 00:00:00 2001 From: Phillip Stephens Date: Sat, 14 Dec 2019 15:50:29 -0800 Subject: [PATCH] More CThardus --- Editor/ViewManager.cpp | 113 +++--- Editor/main.cpp | 19 +- Runtime/CSimplePool.hpp | 1 + .../Collision/CJointCollisionDescription.hpp | 6 + Runtime/MP1/World/CThardus.cpp | 359 +++++++++++++++++- Runtime/MP1/World/CThardus.hpp | 103 ++++- Runtime/World/CDestroyableRock.hpp | 4 + Runtime/World/CScriptPlayerActor.cpp | 4 +- Runtime/World/CScriptSpawnPoint.cpp | 2 +- Runtime/World/ScriptLoader.cpp | 2 +- hecl | 2 +- specter | 2 +- 12 files changed, 520 insertions(+), 97 deletions(-) diff --git a/Editor/ViewManager.cpp b/Editor/ViewManager.cpp index 883386b28..11b29bfeb 100644 --- a/Editor/ViewManager.cpp +++ b/Editor/ViewManager.cpp @@ -48,7 +48,7 @@ void ViewManager::TestGameView::resized(const boo::SWindowRect& root, const boo: void ViewManager::TestGameView::draw(boo::IGraphicsCommandQueue* gfxQ) { m_vm.m_projManager.mainDraw(); - if (m_debugText && g_StateManager && g_StateManager->Player()) + if (m_debugText) m_debugText->draw(gfxQ); } @@ -60,68 +60,75 @@ void ViewManager::TestGameView::think() { m_debugText->resized(rootView().subRect(), sub); } - if (m_debugText && g_StateManager) { + if (m_debugText) { std::string overlayText; const hecl::CVar* showFrameIdx = hecl::CVarManager::instance()->findCVar("debugOverlay.showFrameCounter"); const hecl::CVar* playerInfo = hecl::CVarManager::instance()->findCVar("debugOverlay.playerInfo"); const hecl::CVar* worldInfo = hecl::CVarManager::instance()->findCVar("debugOverlay.worldInfo"); const hecl::CVar* areaInfo = hecl::CVarManager::instance()->findCVar("debugOverlay.areaInfo"); const hecl::CVar* showInGameTime = hecl::CVarManager::instance()->findCVar("debugOverlay.showInGameTime"); - if (showFrameIdx && showFrameIdx->toBoolean()) - overlayText += fmt::format(fmt("Frame: {}\n"), g_StateManager->GetUpdateFrameIndex()); + const hecl::CVar* showResourceStats = hecl::CVarManager::instance()->findCVar("debugOverlay.showResourceStats"); + if (g_StateManager) { + if (showFrameIdx && showFrameIdx->toBoolean()) + overlayText += fmt::format(fmt("Frame: {}\n"), g_StateManager->GetUpdateFrameIndex()); - if (showInGameTime && showInGameTime->toBoolean()) { - double igt = g_GameState->GetTotalPlayTime(); - u32 ms = u64(igt * 1000) % 1000; - auto pt = std::div(igt, 3600); - overlayText += fmt::format(fmt("PlayTime: {:02d}:{:02d}:{:02d}.{:03d}\n"), pt.quot, pt.rem / 60, pt.rem % 60, - ms); - } - if (g_StateManager->Player() && playerInfo && playerInfo->toBoolean()) { - const CPlayer& pl = g_StateManager->GetPlayer(); - const zeus::CQuaternion plQ = zeus::CQuaternion(pl.GetTransform().getRotation().buildMatrix3f()); - const zeus::CTransform camXf = g_StateManager->GetCameraManager()->GetCurrentCameraTransform(*g_StateManager); - const zeus::CQuaternion camQ = zeus::CQuaternion(camXf.getRotation().buildMatrix3f()); - overlayText += - fmt::format(fmt("Player Position: x {}, y {}, z {}\n" - " Roll: {}, Pitch: {}, Yaw: {}\n" - " Momentum: x {}, y: {}, z: {}\n" - " Velocity: x {}, y: {}, z: {}\n" - "Camera Position: x {}, y {}, z {}\n" - " Roll: {}, Pitch: {}, Yaw: {}\n"), - pl.GetTranslation().x(), pl.GetTranslation().y(), pl.GetTranslation().z(), - zeus::radToDeg(plQ.roll()), zeus::radToDeg(plQ.pitch()), zeus::radToDeg(plQ.yaw()), - pl.GetMomentum().x(), pl.GetMomentum().y(), pl.GetMomentum().z(), pl.GetVelocity().x(), - pl.GetVelocity().y(), pl.GetVelocity().z(), camXf.origin.x(), camXf.origin.y(), camXf.origin.z(), - zeus::radToDeg(camQ.roll()), zeus::radToDeg(camQ.pitch()), zeus::radToDeg(camQ.yaw())); - } - if (worldInfo && worldInfo->toBoolean()) { - TLockedToken tbl = - g_SimplePool->GetObj({FOURCC('STRG'), g_StateManager->GetWorld()->IGetStringTableAssetId()}); - const urde::TAreaId aId = g_GameState->CurrentWorldState().GetCurrentAreaId(); - overlayText += fmt::format(fmt("World: 0x{}{}, Area: {}\n"), g_GameState->CurrentWorldAssetId(), - (tbl.IsLoaded() ? (" " + hecl::Char16ToUTF8(tbl->GetString(0))).c_str() : ""), aId); - } - - const urde::TAreaId aId = g_GameState->CurrentWorldState().GetCurrentAreaId(); - if (areaInfo && areaInfo->toBoolean() && g_StateManager->GetWorld() && - g_StateManager->GetWorld()->DoesAreaExist(aId)) { - const auto& layerStates = g_GameState->CurrentWorldState().GetLayerState(); - std::string layerBits; - u32 totalActive = 0; - for (u32 i = 0; i < layerStates->GetAreaLayerCount(aId); ++i) { - if (layerStates->IsLayerActive(aId, i)) { - ++totalActive; - layerBits += "1"; - } else - layerBits += "0"; + if (showInGameTime && showInGameTime->toBoolean()) { + double igt = g_GameState->GetTotalPlayTime(); + u32 ms = u64(igt * 1000) % 1000; + auto pt = std::div(igt, 3600); + overlayText += + fmt::format(fmt("PlayTime: {:02d}:{:02d}:{:02d}.{:03d}\n"), pt.quot, pt.rem / 60, pt.rem % 60, ms); + } + + if (g_StateManager->Player() && playerInfo && playerInfo->toBoolean()) { + const CPlayer& pl = g_StateManager->GetPlayer(); + const zeus::CQuaternion plQ = zeus::CQuaternion(pl.GetTransform().getRotation().buildMatrix3f()); + const zeus::CTransform camXf = g_StateManager->GetCameraManager()->GetCurrentCameraTransform(*g_StateManager); + const zeus::CQuaternion camQ = zeus::CQuaternion(camXf.getRotation().buildMatrix3f()); + overlayText += fmt::format(fmt("Player Position: x {}, y {}, z {}\n" + " Roll: {}, Pitch: {}, Yaw: {}\n" + " Momentum: x {}, y: {}, z: {}\n" + " Velocity: x {}, y: {}, z: {}\n" + "Camera Position: x {}, y {}, z {}\n" + " Roll: {}, Pitch: {}, Yaw: {}\n"), + pl.GetTranslation().x(), pl.GetTranslation().y(), pl.GetTranslation().z(), + zeus::radToDeg(plQ.roll()), zeus::radToDeg(plQ.pitch()), zeus::radToDeg(plQ.yaw()), + pl.GetMomentum().x(), pl.GetMomentum().y(), pl.GetMomentum().z(), + pl.GetVelocity().x(), pl.GetVelocity().y(), pl.GetVelocity().z(), camXf.origin.x(), + camXf.origin.y(), camXf.origin.z(), zeus::radToDeg(camQ.roll()), + zeus::radToDeg(camQ.pitch()), zeus::radToDeg(camQ.yaw())); + } + if (worldInfo && worldInfo->toBoolean()) { + TLockedToken tbl = + g_SimplePool->GetObj({FOURCC('STRG'), g_StateManager->GetWorld()->IGetStringTableAssetId()}); + const urde::TAreaId aId = g_GameState->CurrentWorldState().GetCurrentAreaId(); + overlayText += fmt::format(fmt("World: 0x{}{}, Area: {}\n"), g_GameState->CurrentWorldAssetId(), + (tbl.IsLoaded() ? (" " + hecl::Char16ToUTF8(tbl->GetString(0))).c_str() : ""), aId); + } + + const urde::TAreaId aId = g_GameState->CurrentWorldState().GetCurrentAreaId(); + if (areaInfo && areaInfo->toBoolean() && g_StateManager->GetWorld() && + g_StateManager->GetWorld()->DoesAreaExist(aId)) { + const auto& layerStates = g_GameState->CurrentWorldState().GetLayerState(); + std::string layerBits; + u32 totalActive = 0; + for (u32 i = 0; i < layerStates->GetAreaLayerCount(aId); ++i) { + if (layerStates->IsLayerActive(aId, i)) { + ++totalActive; + layerBits += "1"; + } else + layerBits += "0"; + } + overlayText += fmt::format(fmt("Area AssetId: 0x{}, Total Objects: {}\n" + "Active Layer bits: {}\n"), + g_StateManager->GetWorld()->GetArea(aId)->GetAreaAssetId(), + g_StateManager->GetAllObjectList().size(), layerBits); } - overlayText += fmt::format(fmt("Area AssetId: 0x{}, Total Objects: {}\n" - "Active Layer bits: {}\n"), - g_StateManager->GetWorld()->GetArea(aId)->GetAreaAssetId(), - g_StateManager->GetAllObjectList().size(), layerBits); } + if (showResourceStats && showResourceStats->toBoolean()) + overlayText += fmt::format(fmt("Resource Objects: {}\n"), g_SimplePool->GetLiveObjects()); + if (!overlayText.empty()) m_debugText->typesetGlyphs(overlayText); } diff --git a/Editor/main.cpp b/Editor/main.cpp index 1aa3eefbf..6fe7f1edb 100644 --- a/Editor/main.cpp +++ b/Editor/main.cpp @@ -9,8 +9,8 @@ #include "hecl/Console.hpp" static logvisor::Module AthenaLog("Athena"); -static void AthenaExc(athena::error::Level level, const char* file, const char*, int line, - fmt::string_view fmt, fmt::format_args args) { +static void AthenaExc(athena::error::Level level, const char* file, const char*, int line, fmt::string_view fmt, + fmt::format_args args) { AthenaLog.vreport(logvisor::Level(level), fmt, args); } @@ -55,8 +55,11 @@ struct Application : boo::IApplicationCallback { std::atomic_bool m_running = {true}; - Application() : m_fileMgr(_SYS_STR("urde")), m_cvarManager(m_fileMgr), m_cvarCommons(m_cvarManager), - m_viewManager(std::make_unique(m_fileMgr, m_cvarManager)) {} + Application() + : m_fileMgr(_SYS_STR("urde")) + , m_cvarManager(m_fileMgr) + , m_cvarCommons(m_cvarManager) + , m_viewManager(std::make_unique(m_fileMgr, m_cvarManager)) {} virtual ~Application() = default; @@ -127,6 +130,9 @@ struct Application : boo::IApplicationCallback { hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); m_cvarManager.findOrMakeCVar("debugOverlay.showInGameTime"sv, "Displays the current in game time"sv, false, hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); + m_cvarManager.findOrMakeCVar("debugOverlay.showResourceStats"sv, + "Displays the current live resource object and token counts"sv, false, + hecl::CVar::EFlags::Game | hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::ReadOnly); } }; @@ -139,9 +145,8 @@ static void SetupBasics(bool logging) { auto result = zeus::validateCPU(); if (!result.first) { #if _WIN32 && !WINDOWS_STORE - std::wstring msg = fmt::format( - fmt(L"ERROR: This build of URDE requires the following CPU features:\n{}\n"), - urde::CPUFeatureString(result.second)); + std::wstring msg = fmt::format(fmt(L"ERROR: This build of URDE requires the following CPU features:\n{}\n"), + urde::CPUFeatureString(result.second)); MessageBoxW(nullptr, msg.c_str(), L"CPU error", MB_OK | MB_ICONERROR); #else fmt::print(stderr, fmt("ERROR: This build of URDE requires the following CPU features:\n{}\n"), diff --git a/Runtime/CSimplePool.hpp b/Runtime/CSimplePool.hpp index b237c1f87..bc477536a 100644 --- a/Runtime/CSimplePool.hpp +++ b/Runtime/CSimplePool.hpp @@ -32,6 +32,7 @@ public: void Flush() override; void ObjectUnreferenced(const SObjectTag&) override; std::vector GetReferencedTags() const; + size_t GetLiveObjects() const { return x8_resources.size(); } }; } // namespace urde diff --git a/Runtime/Collision/CJointCollisionDescription.hpp b/Runtime/Collision/CJointCollisionDescription.hpp index 9f9d70aed..2410ccdca 100644 --- a/Runtime/Collision/CJointCollisionDescription.hpp +++ b/Runtime/Collision/CJointCollisionDescription.hpp @@ -14,6 +14,12 @@ struct SJointInfo { float radius; float separation; }; + +struct SAABoxJointInfo { + const char* name; + zeus::CVector3f extents; +}; + struct SOBBJointInfo { const char* from; const char* to; diff --git a/Runtime/MP1/World/CThardus.cpp b/Runtime/MP1/World/CThardus.cpp index 796a28b83..42f673507 100644 --- a/Runtime/MP1/World/CThardus.cpp +++ b/Runtime/MP1/World/CThardus.cpp @@ -1,16 +1,52 @@ #include "Runtime/MP1/World/CThardus.hpp" -#include "Runtime/CStateManager.hpp" #include "Runtime/Collision/CCollisionActor.hpp" #include "Runtime/Collision/CCollisionActorManager.hpp" +#include "Runtime/CStateManager.hpp" +#include "Runtime/MP1/World/CThardusRockProjectile.hpp" #include "Runtime/World/CActorParameters.hpp" #include "Runtime/World/CDestroyableRock.hpp" +#include "Runtime/World/CGameLight.hpp" #include "Runtime/World/CPatternedInfo.hpp" #include "Runtime/World/CPlayer.hpp" +#include "Runtime/World/CScriptWaypoint.hpp" +#include "Runtime/World/CWorld.hpp" #include "TCastTo.hpp" // Generated file, do not modify include path namespace urde::MP1 { + +namespace { +constexpr SSphereJointInfo skDamageableSphereJointInfoList1[7] = { + {"R_Knee", 1.f}, + {"R_Elbow_Collision_LCTR", 1.5f}, + {"L_Elbow_Collision_LCTR", 1.5f}, + {"L_Knee_Collision_LCTR", 1.f}, + {"R_Back_Rock_Collision_LCTR", 2.5f}, + {"L_Back_Rock_Collision_LCTR", 1.5f}, + {"Head_Collision_LCTR", 1.5f}, +}; + +constexpr SSphereJointInfo skDamageableSphereJointInfoList2[5] = { + {"R_Shoulder_Collision_LCTR", 0.75f}, {"L_Shoulder_Collision_LCTR", 0.75f}, {"Spine_Collision_LCTR", 0.75f}, + {"R_Hand_Collision_LCTR", 2.25f}, {"L_Hand_Collision_LCTR", 2.f}, +}; + +constexpr SAABoxJointInfo skFootCollision[2] = { + {"R_Foot_Collision_LCTR", zeus::CVector3f(3.f, 3.f, 1.f)}, + {"L_Foot_Collision_LCTR", zeus::CVector3f(3.f, 2.f, 3.f)}, +}; + +constexpr std::array skSearchJointNames = { + "R_Knee"sv, + "R_Elbow_Collision_LCTR"sv, + "L_Elbow_Collision_LCTR"sv, + "L_Knee_Collision_LCTR"sv, + "R_Back_Rock_Collision_LCTR"sv, + "L_Back_Rock_Collision_LCTR"sv, + "Head_Collision_LCTR"sv, +}; +} // namespace CThardus::CThardus(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& mData, const CActorParameters& actParms, const CPatternedInfo& pInfo, const std::vector& mData1, const std::vector& mData2, CAssetId particle1, @@ -73,20 +109,71 @@ CThardus::CThardus(TUniqueId uid, std::string_view name, const CEntityInfo& info SetMass(100000.f); } +void CThardus::Think(float dt, CStateManager& mgr) { +// if (!x7c8_) { +// float fVar2 = 10.f * GetModelData()->GetScale().x(); +// zeus::CVector3f diff = mgr.GetPlayer().GetTranslation() - GetTranslation(); +// if (diff.magSquared() < fVar2 * fVar2) { +// mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), +// CDamageInfo(CWeaponMode(EWeaponType::AI), 0.f, 0.f, 10.f), CMaterialFilter::skPassEverything, +// diff.normalized()); +// x688_ = true; +// x7c8_ = false; +// x7cc_.zeroOut(); +// } +// } + + //sub801dbf34(dt, mgr); + /* + if (!sub801dc2c8()) { + if (x648_ < x610_destroyableRocks.size() - 2) + x690_ = 1.f; + else + x690_ = 1.f; + } else { + x690_ = 1.f; + SendScriptMsgs(EScriptObjectState::DeactivateState, mgr, EScriptObjectMessage::None); + } +*/ + /* + if (!x93c_) { + x3b4_speed = x690_; + x402_28_isMakingBigStrike = false; + x504_damageDur = 0.f; + } else { + x3b4_speed = x690_; + x402_28_isMakingBigStrike = true; + x504_damageDur = 1.f; + } */ + + CPatterned::Think(dt, mgr); + + if (x648_ > 2 && !x689_) { + //sub801dc878(mgr); + } + + x5f0_->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace); + x5f4_->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace); + x5f8_->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace); + + //sub801dd608(mgr); + //sub801dcfa4(mgr); + +} void CThardus::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) { CPatterned::AcceptScriptMsg(msg, uid, mgr); switch (msg) { case EScriptObjectMessage::Reset: { - x95c_ = true; + x95c_doCodeTrigger = true; x450_bodyController->SetFallState(pas::EFallState::Zero); x450_bodyController->SetState(pas::EAnimationState::Locomotion); x93d_ = false; break; } case EScriptObjectMessage::SetToMax: { - for (size_t i = x648_; i < x610_.size() - 1; ++i) { - if (CEntity* ent = mgr.ObjectById(x610_[i])) { + for (size_t i = x648_; i < x610_destroyableRocks.size() - 1; ++i) { + if (CEntity* ent = mgr.ObjectById(x610_destroyableRocks[i])) { ent->SetActive(false); } ++x648_; @@ -98,8 +185,8 @@ void CThardus::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateMa break; } case EScriptObjectMessage::Action: { - if (!x5c8_) - x5c8_ = true; + if (!x5c8_heardPlayer) + x5c8_heardPlayer = true; break; } case EScriptObjectMessage::Touched: { @@ -136,18 +223,18 @@ void CThardus::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateMa break; } case EScriptObjectMessage::Registered: { - x610_.reserve(x5cc_.size()); + x610_destroyableRocks.reserve(x5cc_.size()); x6b0_.reserve(x5cc_.size()); - x6c0_.reserve(x5cc_.size()); + x6c0_rockLights.reserve(x5cc_.size()); x90c_.reserve(x5cc_.size()); for (size_t i = 0; i < x5cc_.size(); ++i) { - float dVar24 = (i == x5cc_.size() - 1) ? 2.f * x6a8_ : x6a8_; + float health = (i == x5cc_.size() - 1) ? 2.f * x6a8_ : x6a8_; TUniqueId rockId = mgr.AllocateUniqueId(); CModelData mData1(x5cc_[i]); CModelData mData2(x5dc_[i]); mgr.AddObject(new CDestroyableRock( rockId, true, "", CEntityInfo(GetAreaIdAlways(), NullConnectionList), {}, std::move(mData1), 0.f, - CHealthInfo(dVar24, 0.f), + CHealthInfo(health, 0.f), CDamageVulnerability( EVulnerability::Normal, EVulnerability::Deflect, EVulnerability::Normal, EVulnerability::Normal, EVulnerability::Normal, EVulnerability::Normal, EVulnerability::Normal, EVulnerability::Deflect, @@ -160,25 +247,132 @@ void CThardus::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateMa zeus::skZero3f, -1, -1, 0, 0), {}, {}, {}, {}, true, true, false, false, 0.f, 0.f, 1.f), std::move(mData2), 0)); + x610_destroyableRocks.push_back(rockId); + x6b0_.push_back(false); + TUniqueId lightId = mgr.AllocateUniqueId(); + mgr.AddObject(new CGameLight(lightId, GetAreaIdAlways(), false, ""sv, {}, GetUniqueId(), + CLight::BuildPoint({}, zeus::skBlue), 0, 0, 0.f)); + x6c0_rockLights.push_back(lightId); + x90c_.push_back(health); } AddMaterial(EMaterialTypes::ScanPassthrough, mgr); AddMaterial(EMaterialTypes::CameraPassthrough, mgr); RemoveMaterial(EMaterialTypes::Orbit, mgr); RemoveMaterial(EMaterialTypes::Target, mgr); - // sub801ddbe4(mgr); + _SetupCollisionManagers(mgr); x450_bodyController->SetFallState(pas::EFallState::Two); x450_bodyController->Activate(mgr); x450_bodyController->BodyStateInfo().SetLocoAnimChangeAtEndOfAnimOnly(true); - // sub801db560(0, mgr); - // sub801dec80(); + SetState(0, mgr); + sub801dec80(); AddMaterial(EMaterialTypes::RadarObject, mgr); break; } + case EScriptObjectMessage::Deleted: { + x5f0_->Destroy(mgr); + x5f4_->Destroy(mgr); + x5f8_->Destroy(mgr); + mgr.FreeScriptObject(x64c_); + for (const auto& id : x610_destroyableRocks) { + mgr.FreeScriptObject(id); + } + + for (const auto& id : x6c0_rockLights) { + mgr.FreeScriptObject(id); + } + break; + } + case EScriptObjectMessage::InitializedInArea: { + if (x94c_) + break; + x94c_ = true; + x764_ = GetTransform(); + + for (const SConnection& conn : GetConnectionList()) { + TUniqueId connId = mgr.GetIdForScript(conn.x8_objId); + if (connId == kInvalidUniqueId) + continue; + + if (conn.x0_state == EScriptObjectState::Patrol) { + if (TCastToPtr wp = mgr.ObjectById(connId)) { + rstl::reserved_vector wpIds; + GatherWaypoints(wp, mgr, wpIds); + for (const auto& id : wpIds) + x570_.push_back(id); + } else if (CPatterned* p = CPatterned::CastTo(mgr.ObjectById(connId))) { + x5fc_ = connId; + x60c_ = conn.x8_objId; + p->SetActive(false); + } else if (TCastToConstPtr(mgr.GetObjectById(connId))) { + x660_.push_back(connId); + } else if (TCastToConstPtr(mgr.GetObjectById(connId))) { + x64c_ = connId; + } + } else if (conn.x0_state == EScriptObjectState::Zero) { + if (TCastToPtr wp = mgr.ObjectById(connId)) { + x8f4_.push_back(connId); + wp->SetActive(false); + } + } else if (conn.x0_state == EScriptObjectState::Dead) { + if (TCastToConstPtr(mgr.GetObjectById(connId))) + x7a8_.push_back(connId); + } + } + x7f0_.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea); + break; + } + case EScriptObjectMessage::Damage: { + break; + } default: break; } } + +void CThardus::PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) { CPatterned::PreRender(mgr, frustum); } +void CThardus::Render(const CStateManager& mgr) const { + CPatterned::Render(mgr); + if (mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::Thermal && x7c4_ != 0) { + // sub801e32a0(mgr, x7c0_); + } +} +void CThardus::Touch(CActor& act, CStateManager& mgr) { CPatterned::Touch(act, mgr); } +zeus::CVector3f CThardus::GetOrbitPosition(const CStateManager& mgr) const { return GetAimPosition(mgr, 0.f); } +zeus::CVector3f CThardus::GetAimPosition(const CStateManager& mgr, float dt) const { + return GetLctrTransform(x93c_ ? "center_LCTR"sv : "Neck_1"sv).origin; +} +zeus::CAABox CThardus::GetSortingBounds(const CStateManager& mgr) const { + zeus::CVector3f extents = 0.15f * (x9c_renderBounds.max - x9c_renderBounds.min); + return zeus::CAABox(x9c_renderBounds.min - extents, x9c_renderBounds.max + extents); +} + +void CThardus::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) { + CPatterned::DoUserAnimEvent(mgr, node, type, dt); +} +void CThardus::Patrol(CStateManager& mgr, EStateMsg msg, float arg) { CPatterned::Patrol(mgr, msg, arg); } +void CThardus::Dead(CStateManager& mgr, EStateMsg msg, float arg) { CPatterned::Dead(mgr, msg, arg); } +void CThardus::PathFind(CStateManager& mgr, EStateMsg msg, float arg) { CPatterned::PathFind(mgr, msg, arg); } +void CThardus::TargetPatrol(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x5ec_ = 0; + if (x95e_) + return; + + mgr.SetBossParams(GetUniqueId(), GetHealthInfo(mgr)->GetHP(), 88); + x95e_ = true; + } else if (msg == EStateMsg::Update) { + if (x5ec_ == 0) { + if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Taunt) + x5ec_ = 2; + else + x450_bodyController->GetCommandMgr().DeliverCmd(CBCTauntCmd(pas::ETauntType::One)); + } else if (x5ec_ == 2 && + x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::Taunt) { + x5ec_ = 3; + } + } +} void CThardus::Generate(CStateManager& mgr, EStateMsg msg, float arg) { if (msg == EStateMsg::Activate) { x5ec_ = 0; @@ -196,9 +390,148 @@ void CThardus::Generate(CStateManager& mgr, EStateMsg msg, float arg) { x93d_ = false; } } +void CThardus::Attack(CStateManager& mgr, EStateMsg msg, float arg) { CAi::Attack(mgr, msg, arg); } +void CThardus::LoopedAttack(CStateManager& mgr, EStateMsg msg, float arg) { CAi::LoopedAttack(mgr, msg, arg); } +void CThardus::DoubleSnap(CStateManager& mgr, EStateMsg msg, float arg) { CAi::DoubleSnap(mgr, msg, arg); } +void CThardus::Shuffle(CStateManager& mgr, EStateMsg msg, float arg) { CAi::Shuffle(mgr, msg, arg); } void CThardus::GetUp(CStateManager& mgr, EStateMsg msg, float arg) { if (msg != EStateMsg::Activate) return; RemoveMaterial(EMaterialTypes::RadarObject, mgr); } + +void CThardus::Taunt(CStateManager& mgr, EStateMsg msg, float arg) { + if (msg == EStateMsg::Activate) { + x5ec_ = 0; + } else if (msg == EStateMsg::Update) { + if (x5ec_ == 0) { + if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::Taunt) + x5ec_ = 2; + else + x450_bodyController->GetCommandMgr().DeliverCmd(CBCTauntCmd(pas::ETauntType::One)); + } else if (x5ec_ == 2 && x450_bodyController->GetCurrentStateId() != pas::EAnimationState::Taunt) { + x5ec_ = 3; + } + } +} + +void CThardus::Suck(CStateManager& mgr, EStateMsg msg, float arg) { CAi::Suck(mgr, msg, arg); } +void CThardus::ProjectileAttack(CStateManager& mgr, EStateMsg msg, float arg) { CAi::ProjectileAttack(mgr, msg, arg); } +void CThardus::Flinch(CStateManager& mgr, EStateMsg msg, float arg) { CAi::Flinch(mgr, msg, arg); } +void CThardus::TelegraphAttack(CStateManager& mgr, EStateMsg msg, float arg) { CAi::TelegraphAttack(mgr, msg, arg); } +void CThardus::Explode(CStateManager& mgr, EStateMsg msg, float arg) { CAi::Explode(mgr, msg, arg); } +void CThardus::Cover(CStateManager& mgr, EStateMsg msg, float arg) { CAi::Cover(mgr, msg, arg); } +void CThardus::Enraged(CStateManager& mgr, EStateMsg msg, float arg) { CAi::Enraged(mgr, msg, arg); } +void CThardus::Growth(CStateManager& mgr, EStateMsg msg, float arg) { CAi::Growth(mgr, msg, arg); } +void CThardus::Faint(CStateManager& mgr, EStateMsg msg, float arg) { CAi::Faint(mgr, msg, arg); } +bool CThardus::PathFound(CStateManager& mgr, float arg) { return CPatterned::PathFound(mgr, arg); } +bool CThardus::InRange(CStateManager& mgr, float arg) { return CPatterned::InRange(mgr, arg); } +bool CThardus::PatternOver(CStateManager& mgr, float arg) { return CPatterned::PatternOver(mgr, arg); } +bool CThardus::AnimOver(CStateManager& mgr, float arg) { return CPatterned::AnimOver(mgr, arg); } +bool CThardus::InPosition(CStateManager& mgr, float arg) { return CPatterned::InPosition(mgr, arg); } +bool CThardus::ShouldTurn(CStateManager& mgr, float arg) { return CAi::ShouldTurn(mgr, arg); } +bool CThardus::HitSomething(CStateManager& mgr, float arg) { return CAi::HitSomething(mgr, arg); } + +void CThardus::GatherWaypoints(urde::CScriptWaypoint* wp, urde::CStateManager& mgr, + rstl::reserved_vector& uids) { + if (uids.size() < uids.capacity() && wp->GetActive()) { + uids.push_back(wp->GetUniqueId()); + wp->SetActive(false); + for (const SConnection& conn : wp->GetConnectionList()) { + TUniqueId uid = mgr.GetIdForScript(conn.x8_objId); + if (TCastToPtr wp2 = mgr.ObjectById(uid)) + GatherWaypoints(wp2, mgr, uids); + } + wp->SetActive(true); + } +} + +void CThardus::_BuildSphereJointList(const SSphereJointInfo* arr, int count, + std::vector& list) { + for (size_t i = 0; i < count; ++i) { + const auto& jInfo = arr[i]; + list.push_back(CJointCollisionDescription::SphereCollision( + GetModelData()->GetAnimationData()->GetLocatorSegId(jInfo.name), jInfo.radius, jInfo.name, 0.001f)); + } +} + +void CThardus::_BuildAABoxJointList(const SAABoxJointInfo* arr, int count, + std::vector& list) { + for (size_t i = 0; i < count; ++i) { + const auto& jInfo = arr[i]; + list.push_back(CJointCollisionDescription::AABoxCollision( + GetModelData()->GetAnimationData()->GetLocatorSegId(jInfo.name), jInfo.extents, jInfo.name, 0.001f)); + } +} + +void CThardus::_SetupCollisionActorMaterials(const std::unique_ptr& colMgr, + CStateManager& mgr) { + for (size_t i = 0; i < colMgr->GetNumCollisionActors(); ++i) { + const auto& colDesc = colMgr->GetCollisionDescFromIndex(i); + if (CActor* act = static_cast(mgr.ObjectById(colDesc.GetCollisionActorId()))) { + act->AddMaterial(EMaterialTypes::ScanPassthrough, mgr); + act->AddMaterial(EMaterialTypes::CameraPassthrough, mgr); + act->AddMaterial(EMaterialTypes::Immovable, mgr); + act->AddMaterial(EMaterialTypes::NoPlayerCollision, mgr); + CMaterialList include = GetMaterialFilter().GetIncludeList(); + include.Add(act->GetMaterialFilter().GetIncludeList()); + CMaterialList exclude = GetMaterialFilter().GetExcludeList(); + exclude.Add(act->GetMaterialFilter().GetExcludeList()); + act->SetMaterialFilter(CMaterialFilter::MakeIncludeExclude(include, exclude)); + } + } +} + +void CThardus::_SetupCollisionManagers(CStateManager& mgr) { + std::vector list; + _BuildSphereJointList(skDamageableSphereJointInfoList1, 7, list); + x5f0_.reset(new CCollisionActorManager(mgr, GetUniqueId(), GetAreaIdAlways(), list, true)); + _SetupCollisionActorMaterials(x5f0_, mgr); + list.clear(); + _BuildSphereJointList(skDamageableSphereJointInfoList2, 5, list); + x5f4_.reset(new CCollisionActorManager(mgr, GetUniqueId(), GetAreaIdAlways(), list, true)); + _SetupCollisionActorMaterials(x5f4_, mgr); + list.clear(); + _BuildAABoxJointList(skFootCollision, 2, list); + x5f8_.reset(new CCollisionActorManager(mgr, GetUniqueId(), GetAreaIdAlways(), list, true)); + _SetupCollisionActorMaterials(x5f8_, mgr); + list.clear(); + x634_.reserve(x5f4_->GetNumCollisionActors() + x5f0_->GetNumCollisionActors() + x5f8_->GetNumCollisionActors()); + sub801dd4fc(x5f4_); + sub801dd4fc(x5f8_); + for (size_t i = 0; i < x5f0_->GetNumCollisionActors(); ++i) { + const auto& colDesc = x5f0_->GetCollisionDescFromIndex(i); + if (TCastToPtr colAct = mgr.ObjectById(colDesc.GetCollisionActorId())) { + if (CDestroyableRock* rock = static_cast(mgr.ObjectById(x610_destroyableRocks[i]))) { + if (i == 0) { + colAct->SetDamageVulnerability(*rock->GetDamageVulnerability()); + rock->Set_x32c(0.8f); + } else { + colAct->SetDamageVulnerability(CDamageVulnerability::ImmuneVulnerabilty()); + rock->Set_x32c(0.f); + } + + *colAct->HealthInfo(mgr) = *rock->HealthInfo(mgr); + } + } + } +} + +void CThardus::sub801dd4fc(const std::unique_ptr& colMgr) { + for (size_t i = 0; i < colMgr->GetNumCollisionActors(); ++i) { + const auto& colDesc = colMgr->GetCollisionDescFromIndex(i); + TUniqueId uid = colDesc.GetCollisionActorId(); + bool foundBone = false; + for (const auto& name : skSearchJointNames) { + if (colDesc.GetName().find(name) != std::string::npos) { + foundBone = true; + break; + } + } + + if (!foundBone) + x634_.push_back(uid); + } +} + } \ No newline at end of file diff --git a/Runtime/MP1/World/CThardus.hpp b/Runtime/MP1/World/CThardus.hpp index e74d9d209..b1749fc95 100644 --- a/Runtime/MP1/World/CThardus.hpp +++ b/Runtime/MP1/World/CThardus.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include "Runtime/World/CPatterned.hpp" #include "Runtime/World/CPathFindSearch.hpp" @@ -17,16 +18,15 @@ class CThardus : public CPatterned { }; u32 x568_; TUniqueId x56c_ = kInvalidUniqueId; - u32 x570_ = 0; - u32 x574_ = 0; - u32 x578_ = 0; + std::vector x570_; u32 x5c4_ = 1; - bool x5c8_ = false; - /* NOTE(phil) These two vectors used to vectors of CModelData, They have been converted to vectors of CStaticRes due to - * the use of move semantics to prevent deep copies */ + bool x5c8_heardPlayer = false; + /* NOTE(phil) These two vectors used to vectors of CModelData, They have been converted to vectors of CStaticRes due + * to the use of move semantics to prevent deep copies */ std::vector x5cc_; std::vector x5dc_; s32 x5ec_ = -1; + std::unique_ptr x5f0_; std::unique_ptr x5f4_; std::unique_ptr x5f8_; TUniqueId x5fc_ = kInvalidUniqueId; @@ -34,25 +34,23 @@ class CThardus : public CPatterned { CAssetId x604_; CAssetId x608_; TEditorId x60c_ = kInvalidEditorId; - std::vector x610_; + std::vector x610_destroyableRocks; u32 x624_; u32 x628_; u32 x62c_; CAssetId x630_; - u32 x634_; - u32 x638_ = 0; - u32 x63c_ = 0; - u32 x640_ = 0; + std::vector x634_; s32 x644_ = -1; u32 x648_ = 0; TUniqueId x64c_ = kInvalidUniqueId; zeus::CVector2f x650_ = zeus::CVector2f(0.f, 1.f); s32 x658_ = -1; s32 x65c_ = -1; - u32 x660_ = 0; - u32 x664_ = 0; + std::vector x660_; bool x688_ = false; - bool x690_ = false; + bool x689_ = false; + u32 x68c_ = 0; + float x690_ = 0.f; float x694_; float x698_; float x69c_; @@ -60,8 +58,8 @@ class CThardus : public CPatterned { float x6a4_; float x6a8_; float x6ac_; - std::vector x6b0_; /* TODO: Determine real value */ - std::vector x6c0_; + std::vector x6b0_; /* TODO: Determine real value */ + std::vector x6c0_rockLights; CAssetId x6d0_; CAssetId x6d4_; CAssetId x6d8_; @@ -84,6 +82,7 @@ class CThardus : public CPatterned { std::vector x7a8_; float x7b8_ = 0.f; float x7bc_ = 10.f; + u32 x7c0_; u32 x7c4_ = 0; bool x7c8_ = false; zeus::CVector3f x7cc_; @@ -113,10 +112,26 @@ class CThardus : public CPatterned { bool x94c_ = false; bool x94d_ = false; zeus::CVector3f x950_; - bool x95c_ = false; + bool x95c_doCodeTrigger = false; bool x95d_ = false; bool x95e_ = false; + void SetState(s32 state, CStateManager& mgr) { + x644_ = state; + if (state == 2) + SendScriptMsgs(EScriptObjectState::Patrol, mgr, EScriptObjectMessage::None); + else if (state == 1) + SendScriptMsgs(EScriptObjectState::Retreat, mgr, EScriptObjectMessage::None); + } + + void GatherWaypoints(CScriptWaypoint* wp, CStateManager& mgr, rstl::reserved_vector& uids); + void sub801dec80() { x68c_ = 20000; } + void sub801dd4fc(const std::unique_ptr& colMgr); + void _SetupCollisionActorMaterials(const std::unique_ptr& colMgr, CStateManager& mgr); + void _SetupCollisionManagers(CStateManager& mgr); + void _BuildSphereJointList(const SSphereJointInfo* arr, int count, std::vector& list); + void _BuildAABoxJointList(const SAABoxJointInfo* arr, int count, std::vector& list); + public: DEFINE_PATTERNED(Thardus) CThardus(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, @@ -127,11 +142,63 @@ public: CAssetId particle8, CAssetId particle9, CAssetId texture, u32 sfxId1, CAssetId particle10, u32 sfxId2, u32 sfxId3, u32 sfxId4); + void Think(float dt, CStateManager& mgr) override; void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) override; + void PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) override; + void Render(const CStateManager& mgr) const override; + bool CanRenderUnsorted(const CStateManager&) const override { return false; } + void Touch(CActor& act, CStateManager& mgr) override; + zeus::CVector3f GetOrbitPosition(const CStateManager& mgr) const override; + zeus::CVector3f GetAimPosition(const CStateManager& mgr, float) const override; + zeus::CAABox GetSortingBounds(const CStateManager& mgr) const override; + void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) override; + void Patrol(CStateManager& mgr, EStateMsg msg, float arg) override; + void Dead(CStateManager& mgr, EStateMsg msg, float arg) override; + void PathFind(CStateManager& mgr, EStateMsg msg, float arg) override; + void TargetPatrol(CStateManager& mgr, EStateMsg msg, float arg) override; void Generate(CStateManager& mgr, EStateMsg msg, float arg) override; - + void Attack(CStateManager& mgr, EStateMsg msg, float arg) override; + void LoopedAttack(CStateManager& mgr, EStateMsg msg, float arg) override; + void DoubleSnap(CStateManager& mgr, EStateMsg msg, float arg) override; + void Shuffle(CStateManager& mgr, EStateMsg msg, float arg) override; void GetUp(CStateManager& mgr, EStateMsg msg, float arg) override; + void Taunt(CStateManager& mgr, EStateMsg msg, float arg) override; + void Suck(CStateManager& mgr, EStateMsg msg, float arg) override; + void ProjectileAttack(CStateManager& mgr, EStateMsg msg, float arg) override; + void Flinch(CStateManager& mgr, EStateMsg msg, float arg) override; + void TelegraphAttack(CStateManager& mgr, EStateMsg msg, float arg) override; + void Explode(CStateManager& mgr, EStateMsg msg, float arg) override; + void Cover(CStateManager& mgr, EStateMsg msg, float arg) override; + void Enraged(CStateManager& mgr, EStateMsg msg, float arg) override; + void Growth(CStateManager& mgr, EStateMsg msg, float arg) override; + void Faint(CStateManager& mgr, EStateMsg msg, float arg) override; + bool PathFound(CStateManager& mgr, float arg) override; + bool InRange(CStateManager& mgr, float arg) override; + bool PatternOver(CStateManager& mgr, float arg) override; + bool HasAttackPattern(CStateManager& mgr, float arg) override { + return x5c4_ == 1 && !ShouldMove(mgr, 0.f); + } + bool AnimOver(CStateManager& mgr, float arg) override; + bool InPosition(CStateManager& mgr, float arg) override; + bool ShouldTurn(CStateManager& mgr, float arg) override; + bool HitSomething(CStateManager& mgr, float arg) override; + bool HearPlayer(CStateManager& mgr, float arg) override { return x5c8_heardPlayer; } + bool CoverBlown(CStateManager& mgr, float arg) override { + return x5c4_ == 2 && !ShouldMove(mgr, 0.f); + } + bool CoveringFire(CStateManager& mgr, float arg) override { + return x5c4_ == 0 && !ShouldMove(mgr, 0.f); + } + bool AggressionCheck(CStateManager& mgr, float arg) override { return x330_stateMachineState.GetTime() > 0.1f; } + bool AttackOver(CStateManager& mgr, float arg) override { return true; } + bool ShouldTaunt(CStateManager& mgr, float arg) override { return false;} + bool ShouldMove(CStateManager& mgr, float arg) override { return x68c_ < x570_.size() || x93b_; } + bool CodeTrigger(CStateManager& mgr, float arg) override { return x95c_doCodeTrigger;} + bool IsDizzy(CStateManager& mgr, float arg) override { return x330_stateMachineState.GetTime() > 4.f; } + bool ShouldCallForBackup(CStateManager& mgr, float arg) override { return x330_stateMachineState.GetTime() > .5f; } + + CPathFindSearch* GetSearchPath() override { return &x7f0_; } }; } // namespace MP1 } // namespace urde diff --git a/Runtime/World/CDestroyableRock.hpp b/Runtime/World/CDestroyableRock.hpp index b2ca8fb15..ca186abe0 100644 --- a/Runtime/World/CDestroyableRock.hpp +++ b/Runtime/World/CDestroyableRock.hpp @@ -7,6 +7,8 @@ namespace urde { class CDestroyableRock : public CAi { + + float x32c_; public: CDestroyableRock(TUniqueId id, bool active, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& modelData, float mass, const CHealthInfo& health, @@ -17,6 +19,8 @@ public: void Death(CStateManager& mgr, const zeus::CVector3f& direction, EScriptObjectState state) override; void KnockBack(const zeus::CVector3f&, CStateManager&, const CDamageInfo& info, EKnockBackType type, bool inDeferred, float magnitude) override; + + void Set_x32c(float val) { x32c_ = val; } }; } // namespace urde diff --git a/Runtime/World/CScriptPlayerActor.cpp b/Runtime/World/CScriptPlayerActor.cpp index fe9939e43..00d67bf07 100644 --- a/Runtime/World/CScriptPlayerActor.cpp +++ b/Runtime/World/CScriptPlayerActor.cpp @@ -376,7 +376,7 @@ void CScriptPlayerActor::Render(const CStateManager& mgr) const { CBooModel::SetReflectionCube(m_reflectionCube); bool phazonSuit = x2e8_suitRes.GetCharacterNodeId() == 3; - if (phazonSuit) { + if (phazonSuit && false) { // Draw into alpha buffer CModelFlags flags = xb4_drawFlags; flags.x4_color = zeus::skWhite; @@ -397,7 +397,7 @@ void CScriptPlayerActor::Render(const CStateManager& mgr) const { x314_beamModelData->Render(mgr, modelXf, x90_actorLights.get(), flags); } - if (phazonSuit) { + if (phazonSuit && false) { zeus::CVector3f vecFromCam = GetBoundingBox().center() - mgr.GetCameraManager()->GetCurrentCamera(mgr)->GetTranslation(); float radius = zeus::clamp(0.25f, (6.f - vecFromCam.magnitude()) / 6.f, 2.f); diff --git a/Runtime/World/CScriptSpawnPoint.cpp b/Runtime/World/CScriptSpawnPoint.cpp index ae6b58c42..af51e659c 100644 --- a/Runtime/World/CScriptSpawnPoint.cpp +++ b/Runtime/World/CScriptSpawnPoint.cpp @@ -15,7 +15,7 @@ CScriptSpawnPoint::CScriptSpawnPoint(TUniqueId uid, std::string_view name, const #ifndef NDEBUG x64_itemCounts[int(CPlayerState::EItemType::MorphBall)] = 1; x64_itemCounts[int(CPlayerState::EItemType::MorphBallBombs)] = 1; - x64_itemCounts[int(CPlayerState::EItemType::GravitySuit)] = 1; + x64_itemCounts[int(CPlayerState::EItemType::PhazonSuit)] = 1; x64_itemCounts[int(CPlayerState::EItemType::ThermalVisor)] = 1; x64_itemCounts[int(CPlayerState::EItemType::XRayVisor)] = 1; x64_itemCounts[int(CPlayerState::EItemType::GrappleBeam)] = 1; diff --git a/Runtime/World/ScriptLoader.cpp b/Runtime/World/ScriptLoader.cpp index 469f0683e..f3f7059b4 100644 --- a/Runtime/World/ScriptLoader.cpp +++ b/Runtime/World/ScriptLoader.cpp @@ -2576,7 +2576,7 @@ CEntity* ScriptLoader::LoadThardus(CStateManager& mgr, CInputStream& in, int pro CModelData mData(CAnimRes(animParms.GetACSFile(), 0, actHead.x40_scale, animParms.GetInitialAnimation(), true)); return new MP1::CThardus(mgr.AllocateUniqueId(), actHead.x0_name, info, actHead.x10_transform, std::move(mData), - actParms, pInfo, mData1, mData2, particle2, particle2, particle3, f1, f2, f3, f4, f5, f6, + actParms, pInfo, mData1, mData2, particle1, particle2, particle3, f1, f2, f3, f4, f5, f6, stateMachine, particle4, particle5, particle6, particle7, particle8, particle9, texture, sfxID1, particle10, sfxID2, sfxID3, sfxID4); } diff --git a/hecl b/hecl index 91d9e56ed..db15125e5 160000 --- a/hecl +++ b/hecl @@ -1 +1 @@ -Subproject commit 91d9e56ed3a98b7fdb315c547b684e2e6fcaf6f4 +Subproject commit db15125e596c6308eac51332da686538ddadbeaa diff --git a/specter b/specter index 2a50903a4..29ad17cf9 160000 --- a/specter +++ b/specter @@ -1 +1 @@ -Subproject commit 2a50903a4130d415aa3f8f4f8e53c3d72c398c08 +Subproject commit 29ad17cf965330b69d7704dfb97124bb65fe831d