From 003146f614050e9499f0074ac0b519379eefcf6e Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 29 Jun 2019 21:29:49 -1000 Subject: [PATCH] Work on CBabygoth --- DataSpec/DNAMP1/ScriptObjects/Babygoth.hpp | 128 ++-- NESEmulator/CMakeLists.txt | 2 +- NESEmulator/CNESEmulator.cpp | 24 +- Runtime/Collision/CMaterialList.hpp | 2 +- Runtime/MP1/World/CBabygoth.cpp | 711 +++++++++++++++------ Runtime/MP1/World/CBabygoth.hpp | 191 +++--- Runtime/MP1/World/CFlaahgra.cpp | 6 +- Runtime/Weapon/CFlameThrower.cpp | 9 +- Runtime/Weapon/CFlameThrower.hpp | 2 +- Runtime/Weapon/CGunWeapon.cpp | 1 + Runtime/World/CPatterned.hpp | 5 +- Runtime/World/CStateMachine.cpp | 2 + 12 files changed, 723 insertions(+), 360 deletions(-) diff --git a/DataSpec/DNAMP1/ScriptObjects/Babygoth.hpp b/DataSpec/DNAMP1/ScriptObjects/Babygoth.hpp index 11d1b6e94..2518ffff4 100644 --- a/DataSpec/DNAMP1/ScriptObjects/Babygoth.hpp +++ b/DataSpec/DNAMP1/ScriptObjects/Babygoth.hpp @@ -14,89 +14,89 @@ struct Babygoth : IScriptObject { Value scale; PatternedInfo patternedInfo; ActorParameters actorParameters; - Value unknown1; - Value unknown2; - UniqueID32 wpsc1; - DamageInfo damageInfo1; - DamageInfo damageInfo2; - UniqueID32 wpsc2; - UniqueID32 particle1; - DamageInfo damageInfo3; - DamageVulnerability damageVulnerabilty1; - DamageVulnerability damageVulnerabilty2; - UniqueID32 cmdlShelless; - UniqueID32 cskrShelless; - Value unknown3; - Value unknown4; - UniqueID32 particle2; - UniqueID32 particle3; - UniqueID32 particle4; - UniqueID32 particle5; - Value unknown5; - Value unknown6; - Value unknown7; - Value unknown8; - Value unknown9; - Value unknown10; - UniqueID32 texture; - Value unknown11; - UniqueID32 particle6; + Value fireballAttackTime; + Value fireballAttackTimeVariance; + UniqueID32 fireballWeapon; + DamageInfo fireballDamage; + DamageInfo attackContactDamage; + UniqueID32 fireBreathWeapon; + UniqueID32 fireBreathRes; + DamageInfo fireBreathDamage; + DamageVulnerability mouthVulnerabilities; + DamageVulnerability shellVulnerabilities; + UniqueID32 noShellModel; + UniqueID32 noShellSkin; + Value shellHitPoints; + Value shellCrackSfx; + UniqueID32 intermediateCrackParticle; + UniqueID32 crackOneParticle; + UniqueID32 crackTwoParticle; + UniqueID32 destroyShellParticle; + Value crackOneSfx; + Value crackTwoSfx; + Value destroyShellSfx; + Value timeUntilAttack; + Value attackCooldownTime; + Value interestTime; + UniqueID32 flamePlayerSteamTxtr; + Value flamePlayerHitSfx; + UniqueID32 flamePlayerIceTxtr; void addCMDLRigPairs(PAKRouter& pakRouter, CharacterAssociations& charAssoc) const { UniqueID32 cinf = patternedInfo.animationParameters.getCINF(pakRouter); actorParameters.addCMDLRigPairs(pakRouter, charAssoc, patternedInfo.animationParameters); - if (cmdlShelless && cskrShelless) { - charAssoc.m_cmdlRigs[cmdlShelless] = std::make_pair(cskrShelless, cinf); - charAssoc.m_cskrCinfToCharacter[cskrShelless] = + if (noShellModel && noShellSkin) { + charAssoc.m_cmdlRigs[noShellModel] = std::make_pair(noShellSkin, cinf); + charAssoc.m_cskrCinfToCharacter[noShellSkin] = std::make_pair(patternedInfo.animationParameters.animationCharacterSet, "ATTACH.SHELLESS.CSKR"); - charAssoc.addAttachmentRig(patternedInfo.animationParameters.animationCharacterSet, {}, cmdlShelless, "SHELLESS"); + charAssoc.addAttachmentRig(patternedInfo.animationParameters.animationCharacterSet, {}, noShellModel, "SHELLESS"); } } void nameIDs(PAKRouter& pakRouter) const { - if (wpsc1) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(wpsc1); + if (fireballWeapon) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(fireballWeapon); ent->name = name + "_wpsc1"; } - if (wpsc2) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(wpsc2); + if (fireBreathWeapon) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(fireBreathWeapon); ent->name = name + "_wpsc2"; } - if (particle1) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle1); + if (fireBreathRes) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(fireBreathRes); ent->name = name + "_part1"; } - if (cmdlShelless) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(cmdlShelless); + if (noShellModel) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(noShellModel); ent->name = name + "_emodel"; } - if (cskrShelless) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(cskrShelless); + if (noShellSkin) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(noShellSkin); ent->name = name + "_eskin"; } - if (particle2) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle2); + if (intermediateCrackParticle) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(intermediateCrackParticle); ent->name = name + "_part2"; } - if (particle3) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle3); + if (crackOneParticle) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(crackOneParticle); ent->name = name + "_part3"; } - if (particle4) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle4); + if (crackTwoParticle) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(crackTwoParticle); ent->name = name + "_part4"; } - if (particle5) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle5); + if (destroyShellParticle) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(destroyShellParticle); ent->name = name + "_part5"; } - if (texture) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(texture); + if (flamePlayerSteamTxtr) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(flamePlayerSteamTxtr); ent->name = name + "_tex"; } - if (particle6) { - PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle6); + if (flamePlayerIceTxtr) { + PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(flamePlayerIceTxtr); ent->name = name + "_part6"; } patternedInfo.nameIDs(pakRouter, name + "_patterned"); @@ -104,17 +104,17 @@ struct Babygoth : IScriptObject { } void gatherDependencies(std::vector& pathsOut, std::vector& lazyOut) const { - g_curSpec->flattenDependencies(wpsc1, pathsOut); - g_curSpec->flattenDependencies(wpsc2, pathsOut); - g_curSpec->flattenDependencies(particle1, pathsOut); - g_curSpec->flattenDependencies(cmdlShelless, pathsOut); - g_curSpec->flattenDependencies(cskrShelless, pathsOut); - g_curSpec->flattenDependencies(particle2, pathsOut); - g_curSpec->flattenDependencies(particle3, pathsOut); - g_curSpec->flattenDependencies(particle4, pathsOut); - g_curSpec->flattenDependencies(particle5, pathsOut); - g_curSpec->flattenDependencies(texture, pathsOut); - g_curSpec->flattenDependencies(particle6, pathsOut); + g_curSpec->flattenDependencies(fireballWeapon, pathsOut); + g_curSpec->flattenDependencies(fireBreathWeapon, pathsOut); + g_curSpec->flattenDependencies(fireBreathRes, pathsOut); + g_curSpec->flattenDependencies(noShellModel, pathsOut); + g_curSpec->flattenDependencies(noShellSkin, pathsOut); + g_curSpec->flattenDependencies(intermediateCrackParticle, pathsOut); + g_curSpec->flattenDependencies(crackOneParticle, pathsOut); + g_curSpec->flattenDependencies(crackTwoParticle, pathsOut); + g_curSpec->flattenDependencies(destroyShellParticle, pathsOut); + g_curSpec->flattenDependencies(flamePlayerSteamTxtr, pathsOut); + g_curSpec->flattenDependencies(flamePlayerIceTxtr, pathsOut); patternedInfo.depIDs(pathsOut); actorParameters.depIDs(pathsOut, lazyOut); } diff --git a/NESEmulator/CMakeLists.txt b/NESEmulator/CMakeLists.txt index df5b2786b..dc6e2348e 100644 --- a/NESEmulator/CMakeLists.txt +++ b/NESEmulator/CMakeLists.txt @@ -5,7 +5,7 @@ add_library(NESEmulator CNESEmulator.hpp CNESEmulator.cpp CNESShader.hpp CNESSha fixNES/mapper.c fixNES/mapperList.c fixNES/fm2play.c fixNES/vrc_irq.c ${MAPPER_SRCS}) target_include_directories(NESEmulator PRIVATE ${CMAKE_SOURCE_DIR}/DataSpec ${CMAKE_SOURCE_DIR}/Runtime PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) -target_compile_definitions(NESEmulator PRIVATE COL_32BIT=1) +target_compile_definitions(NESEmulator PRIVATE COL_32BIT=1 COL_TEX_BSWAP=1) target_link_libraries(NESEmulator boo hecl-full RuntimeCommon ${HECL_APPLICATION_REPS_TARGETS_LIST}) if (NOT MSVC) target_compile_options(NESEmulator PRIVATE -Wno-implicit-fallthrough -Wno-format -Wno-pointer-compare diff --git a/NESEmulator/CNESEmulator.cpp b/NESEmulator/CNESEmulator.cpp index 8279fc56a..c5f50e314 100644 --- a/NESEmulator/CNESEmulator.cpp +++ b/NESEmulator/CNESEmulator.cpp @@ -264,13 +264,23 @@ void CNESEmulator::InitializeEmulator() { // Nearest-neighbor FTW! m_texture = ctx.newDynamicTexture(VISIBLE_DOTS, linesToDraw, boo::TextureFormat::RGBA8, boo::TextureClampMode::ClampToEdgeNearest); - Vert verts[4] = { - {{-1.f, -1.f, 0.f}, {0.f, 1.f}}, - {{-1.f, 1.f, 0.f}, {0.f, 0.f}}, - {{1.f, -1.f, 0.f}, {1.f, 1.f}}, - {{1.f, 1.f, 0.f}, {1.f, 0.f}}, - }; - m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, verts, sizeof(Vert), 4); + if (ctx.platform() == boo::IGraphicsDataFactory::Platform::OpenGL) { + Vert verts[4] = { + {{-1.f, -1.f, 0.f}, {0.f, 1.f}}, + {{-1.f, 1.f, 0.f}, {0.f, 0.f}}, + {{1.f, -1.f, 0.f}, {1.f, 1.f}}, + {{1.f, 1.f, 0.f}, {1.f, 0.f}}, + }; + m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, verts, sizeof(Vert), 4); + } else { + Vert verts[4] = { + {{-1.f, 1.f, 0.f}, {0.f, 1.f}}, + {{-1.f, -1.f, 0.f}, {0.f, 0.f}}, + {{1.f, 1.f, 0.f}, {1.f, 1.f}}, + {{1.f, -1.f, 0.f}, {1.f, 0.f}}, + }; + m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, verts, sizeof(Vert), 4); + } m_uniBuf = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(Uniform), 1); m_shadBind = CNESShader::BuildShaderDataBinding(ctx, m_vbo, m_uniBuf, m_texture); return true; diff --git a/Runtime/Collision/CMaterialList.hpp b/Runtime/Collision/CMaterialList.hpp index 6593fa5e9..b6bbbf7e4 100644 --- a/Runtime/Collision/CMaterialList.hpp +++ b/Runtime/Collision/CMaterialList.hpp @@ -57,7 +57,7 @@ enum class EMaterialTypes { NonSolidDamageable = 50, RadarObject = 51, PlatformSlave = 52, - Unknown53 = 53, + AIJoint = 53, Unknown54 = 54, SolidCharacter = 55, ExcludeFromLineOfSightTest = 56, diff --git a/Runtime/MP1/World/CBabygoth.cpp b/Runtime/MP1/World/CBabygoth.cpp index c79089281..eb6db49a4 100644 --- a/Runtime/MP1/World/CBabygoth.cpp +++ b/Runtime/MP1/World/CBabygoth.cpp @@ -22,33 +22,33 @@ namespace urde::MP1 { const std::string_view CBabygoth::skpMouthDamageJoint = "LCTR_SHEMOUTH"sv; CBabygothData::CBabygothData(CInputStream& in) -: x0_(in.readFloatBig()) -, x4_(in.readFloatBig()) -, x8_(in) -, xc_(in) -, x28_(in) -, x44_(in) +: x0_fireballAttackTime(in.readFloatBig()) +, x4_fireballAttackTimeVariance(in.readFloatBig()) +, x8_fireballWeapon(in) +, xc_fireballDamage(in) +, x28_attackContactDamage(in) +, x44_fireBreathWeapon(in) , x48_fireBreathRes(in) , x4c_fireBreathDamage(in) -, x68_(in) +, x68_mouthVulnerabilities(in) , xd0_shellVulnerabilities(in) -, x138_(in) -, x13c_(in) +, x138_noShellModel(in) +, x13c_noShellSkin(in) , x140_shellHitPoints(in.readFloatBig()) , x144_shellCrackSfx(CSfxManager::TranslateSFXID(in.readUint32Big())) -, x148_(in) -, x14c_(in) -, x150_(in) -, x154_(in) -, x158_(CSfxManager::TranslateSFXID(in.readUint32Big())) -, x15a_(CSfxManager::TranslateSFXID(in.readUint32Big())) -, x15c_(CSfxManager::TranslateSFXID(in.readUint32Big())) -, x160_(in.readFloatBig()) -, x164_(in.readFloatBig()) -, x168_(in.readFloatBig()) -, x16c_(in) -, x170_(CSfxManager::TranslateSFXID(in.readUint32Big())) -, x174_(in) {} +, x148_intermediateCrackParticle(in) +, x14c_crackOneParticle(in) +, x150_crackTwoParticle(in) +, x154_destroyShellParticle(in) +, x158_crackOneSfx(CSfxManager::TranslateSFXID(in.readUint32Big())) +, x15a_crackTwoSfx(CSfxManager::TranslateSFXID(in.readUint32Big())) +, x15c_destroyShellSfx(CSfxManager::TranslateSFXID(in.readUint32Big())) +, x160_timeUntilAttack(in.readFloatBig()) +, x164_attackCooldownTime(in.readFloatBig()) +, x168_interestTime(in.readFloatBig()) +, x16c_flamePlayerSteamTxtr(in) +, x170_flamePlayerHitSfx(CSfxManager::TranslateSFXID(in.readUint32Big())) +, x174_flamePlayerIceTxtr(in) {} CBabygoth::CBabygoth(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& actParms, @@ -56,29 +56,30 @@ CBabygoth::CBabygoth(TUniqueId uid, std::string_view name, const CEntityInfo& in : CPatterned(ECharacter::Babygoth, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo, EMovementType::Ground, EColliderType::One, EBodyType::BiPedal, actParms, EKnockBackVariant::Medium) , x570_babyData(babyData) -, x6ec_(nullptr, 1, pInfo.GetPathfindingIndex(), 1.f, 1.f) -, x7d0_(nullptr, 1, pInfo.GetPathfindingIndex(), 1.f, 1.f) -, x8d0_(x3b4_speed) +, x6ec_pathSearch(nullptr, 1, pInfo.GetPathfindingIndex(), 1.f, 1.f) +, x7d0_approachPathSearch(nullptr, 1, pInfo.GetPathfindingIndex(), 1.f, 1.f) +, x8d0_initialSpeed(x3b4_speed) , x8f0_boneTracking(*ModelData()->AnimationData(), "Head_1"sv, zeus::degToRad(80.f), zeus::degToRad(180.f), EBoneTrackingFlags::None) , x930_aabox(GetBoundingBox(), GetMaterialList()) -, x958_(babyData.x8_, babyData.xc_) -, x984_flameThrowerDesc(babyData.x44_.IsValid() ? g_SimplePool->GetObj({SBIG('WPSC'), babyData.x44_}) - : g_SimplePool->GetObj("FlameThrower"sv)) -, x98c_(pInfo.GetDamageVulnerability()) +, x958_iceProjectile(babyData.x8_fireballWeapon, babyData.xc_fireballDamage) +, x984_flameThrowerDesc(babyData.x44_fireBreathWeapon.IsValid() ? + g_SimplePool->GetObj({SBIG('WPSC'), babyData.x44_fireBreathWeapon}) + : g_SimplePool->GetObj("FlameThrower"sv)) +, x98c_dVuln(pInfo.GetDamageVulnerability()) , xa00_shellHitPoints(babyData.GetShellHitPoints()) { - TLockedToken model = g_SimplePool->GetObj({SBIG('CMDL'), babyData.x138_}); - TLockedToken skin = g_SimplePool->GetObj({SBIG('CSKR'), babyData.x13c_}); + TLockedToken model = g_SimplePool->GetObj({SBIG('CMDL'), babyData.x138_noShellModel}); + TLockedToken skin = g_SimplePool->GetObj({SBIG('CSKR'), babyData.x13c_noShellSkin}); xa08_noShellModel = CToken(TObjOwnerDerivedFromIObj::GetNewDerivedObject(std::make_unique( model, skin, x64_modelData->AnimationData()->GetModelData()->GetLayoutInfo(), 1, 1))); - xa14_ = g_SimplePool->GetObj({SBIG('PART'), babyData.x14c_}); - xa20_ = g_SimplePool->GetObj({SBIG('PART'), babyData.x150_}); - xa2c_ = g_SimplePool->GetObj({SBIG('PART'), babyData.x154_}); - if (x570_babyData.x148_.IsValid()) - xa38_ = g_SimplePool->GetObj({SBIG('PART'), babyData.x148_}); - xa48_31_ = true; - x958_.Token().Lock(); + xa14_crackOneParticle = g_SimplePool->GetObj({SBIG('PART'), babyData.x14c_crackOneParticle}); + xa20_crackTwoParticle = g_SimplePool->GetObj({SBIG('PART'), babyData.x150_crackTwoParticle}); + xa2c_destroyShellParticle = g_SimplePool->GetObj({SBIG('PART'), babyData.x154_destroyShellParticle}); + if (x570_babyData.x148_intermediateCrackParticle.IsValid()) + xa38_intermediateCrackParticle = g_SimplePool->GetObj({SBIG('PART'), babyData.x148_intermediateCrackParticle}); + xa48_31_approachNeedsPathSearch = true; + x958_iceProjectile.Token().Lock(); UpdateTouchBounds(); x460_knockBackController.SetEnableFreeze(false); x460_knockBackController.SetAutoResetImpulse(true); @@ -111,7 +112,7 @@ void CBabygoth::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateM break; case EScriptObjectMessage::Deactivate: { x928_colActMgr->SetActive(mgr, false); - xa49_29_ = false; + xa49_29_objectSpaceCollision = false; RemoveFromTeam(mgr); break; } @@ -143,8 +144,8 @@ void CBabygoth::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateM break; } case EScriptObjectMessage::InitializedInArea: { - x6ec_.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea); - x7d0_.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea); + x6ec_pathSearch.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea); + x7d0_approachPathSearch.SetArea(mgr.GetWorld()->GetAreaAlways(GetAreaIdAlways())->GetPostConstructed()->x10bc_pathArea); break; } case EScriptObjectMessage::Touched: { @@ -163,10 +164,10 @@ void CBabygoth::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateM TakeDamage({}, 0.f); } else if (IsShell(uid)) { TakeDamage({}, 0.f); - if (x56c_ != 3 && x56c_ != 0) { + if (x56c_shellState != EShellState::Destroyed && x56c_shellState != EShellState::Default) { zeus::CTransform xf = wp->GetTransform(); xf.rotateLocalZ(zeus::degToRad(180.f)); - CrackShell(mgr, xa38_, xf, x570_babyData.GetShellCrackSfx(), false); + CrackShell(mgr, xa38_intermediateCrackParticle, xf, x570_babyData.GetShellCrackSfx(), false); } } KnockBack(GetTransform().frontVector(), mgr, wp->GetDamageInfo(), EKnockBackType::Radius, false, @@ -174,7 +175,7 @@ void CBabygoth::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateM } } xa48_24_isAlert = true; - x8e8_ = 0.f; + x8e8_interestTimer = 0.f; mgr.InformListeners(GetTranslation(), EListenNoiseType::PlayerFire); } else ApplyDamage(mgr, uid); @@ -185,7 +186,7 @@ void CBabygoth::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateM mgr.InformListeners(GetTranslation(), EListenNoiseType::PlayerFire); x400_24_hitByPlayerProjectile = true; xa48_24_isAlert = true; - x8e8_ = 0.f; + x8e8_interestTimer = 0.f; if (!TCastToPtr(mgr.ObjectById(uid))) ApplyDamage(mgr, uid); break; @@ -206,7 +207,7 @@ void CBabygoth::Think(float dt, CStateManager& mgr) { return; AvoidPlayerCollision(dt, mgr); - if (xa49_26_) { + if (xa49_26_readyForTeam) { if (!CTeamAiMgr::GetTeamAiRole(mgr, x6e8_teamMgr, GetUniqueId())) AddToTeam(mgr); } @@ -214,14 +215,14 @@ void CBabygoth::Think(float dt, CStateManager& mgr) { CPatterned::Think(dt, mgr); if (x450_bodyController->IsElectrocuting()) x8f0_boneTracking.SetActive(false); - sub8021e2c4(dt); + UpdateTimers(dt); ModelData()->AnimationData()->PreRender(); x8f0_boneTracking.Update(dt); x8f0_boneTracking.PreRender(mgr, *ModelData()->AnimationData(), GetTransform(), GetModelData()->GetScale(), *x450_bodyController); - x928_colActMgr->Update(dt, mgr, CCollisionActorManager::EUpdateOptions(xa49_29_)); - xa49_29_ = true; - sub8021e708(mgr); + x928_colActMgr->Update(dt, mgr, CCollisionActorManager::EUpdateOptions(!xa49_29_objectSpaceCollision)); + xa49_29_objectSpaceCollision = true; + UpdateHealth(mgr); UpdateParticleEffects(dt, mgr); TryToGetUp(mgr); CheckShouldWakeUp(mgr, dt); @@ -229,30 +230,41 @@ void CBabygoth::Think(float dt, CStateManager& mgr) { SetProjectilePasshtrough(mgr); } -void CBabygoth::DoUserAnimEvent(urde::CStateManager& mgr, const urde::CInt32POINode& node, EUserEventType type, - float dt) { +void CBabygoth::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) { switch (type) { case EUserEventType::Projectile: { - //zeus::CTransform xf = GetLctrTransform(node.GetLocatorName()); - //zeus::CVector3f plAimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); + zeus::CTransform xf = GetLctrTransform(node.GetLocatorName()); + zeus::CVector3f plAimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); + zeus::CVector3f pos = GetProjectileInfo()->PredictInterceptPos(xf.origin, plAimPos, mgr.GetPlayer(), false, dt); + zeus::CVector3f delta = pos - xf.origin; + if (zeus::CVector3f::getAngleDiff(GetTransform().basis[1], delta) > zeus::degToRad(30.f)) { + if (delta.canBeNormalized()) { + pos = zeus::CVector3f::slerp(GetTransform().basis[1], delta.normalized(), zeus::degToRad(30.f)) * + delta.magnitude() + xf.origin; + } else { + pos = xf.basis[1] * delta.magnitude() + xf.origin; + } + } + LaunchProjectile(zeus::lookAt(xf.origin, pos), mgr, 4, EProjectileAttrib::None, + false, {}, 0xffff, false, zeus::skOne3f); return; } case EUserEventType::DamageOn: { - if (xa48_26_) { + if (xa48_26_inProjectileAttack) { if (CFlameThrower* flame = static_cast(mgr.ObjectById(x980_flameThrower))) flame->Fire(GetTransform(), mgr, false); } break; } case EUserEventType::DamageOff: { - if (xa48_26_) { + if (xa48_26_inProjectileAttack) { if (CFlameThrower* flame = static_cast(mgr.ObjectById(x980_flameThrower))) flame->Reset(mgr, false); } break; } case EUserEventType::BeginAction: { - if (xa48_26_) { + if (xa48_26_inProjectileAttack) { x8f0_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId()); x8f0_boneTracking.SetActive(true); } @@ -290,21 +302,27 @@ void CBabygoth::SetupCollisionManager(CStateManager& mgr) { for (u32 i = 0; i < x928_colActMgr->GetNumCollisionActors(); ++i) { TUniqueId id = x928_colActMgr->GetCollisionDescFromIndex(i).GetCollisionActorId(); if (TCastToPtr colAct = mgr.ObjectById(id)) { - colAct->SetDamageVulnerability(x570_babyData.x68_); + colAct->SetDamageVulnerability(x570_babyData.x68_mouthVulnerabilities); if (colAct->GetName().find(skpMouthDamageJoint)) - x9f6_ = id; + x9f6_mouthCollisionActor = id; else if (colAct->GetName().find("Pelvis"sv)) { x9f8_shellIds.push_back(id); x300_maxAttackRange = 66; } } } + SetupHealthInfo(mgr); + + SetMaterialFilter(CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, + {EMaterialTypes::CollisionActor, EMaterialTypes::AIPassthrough, EMaterialTypes::Player})); + AddMaterial(EMaterialTypes::ProjectilePassthrough, mgr); + x928_colActMgr->AddMaterial(mgr, {EMaterialTypes::AIJoint, EMaterialTypes::CameraPassthrough}); } void CBabygoth::SetupHealthInfo(CStateManager& mgr) { CHealthInfo* thisHealth = HealthInfo(mgr); - x8ec_ = thisHealth->GetHP(); + x8ec_bodyHP = thisHealth->GetHP(); for (const TUniqueId& uid : x9f8_shellIds) { if (TCastToPtr colAct = mgr.ObjectById(uid)) { CHealthInfo* colHealth = colAct->HealthInfo(mgr); @@ -326,16 +344,18 @@ void CBabygoth::CreateFlameThrower(CStateManager& mgr) { CFlameInfo(6, 4, x570_babyData.GetFireBreathResId(), 15, 0.0625f, 20.f, 1.f), {}, EMaterialTypes::CollisionActor, x570_babyData.GetFireBreathDamage(), x980_flameThrower, GetAreaIdAlways(), GetUniqueId(), EProjectileAttrib::None, - x570_babyData.x16c_, x570_babyData.x170_, x570_babyData.x174_)); + x570_babyData.x16c_flamePlayerSteamTxtr, x570_babyData.x170_flamePlayerHitSfx, + x570_babyData.x174_flamePlayerIceTxtr)); } void CBabygoth::ApplyContactDamage(TUniqueId uid, CStateManager& mgr) { if (TCastToConstPtr colAct = mgr.GetObjectById(uid)) { if (colAct->GetHealthInfo(mgr)->GetHP() > 0.f && colAct->GetLastTouchedObject() == mgr.GetPlayer().GetUniqueId()) { - if (xa48_28_) { - mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), x570_babyData.x28_, + if (xa48_28_pendingAttackContactDamage) { + mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), + x570_babyData.x28_attackContactDamage, CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), {}); - xa48_28_ = false; + xa48_28_pendingAttackContactDamage = false; x420_curDamageRemTime = x424_damageWaitTime; } else if (x420_curDamageRemTime <= 0.f) { mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), GetContactDamage(), @@ -346,7 +366,7 @@ void CBabygoth::ApplyContactDamage(TUniqueId uid, CStateManager& mgr) { } } -void CBabygoth::RemoveFromTeam(urde::CStateManager& mgr) { +void CBabygoth::RemoveFromTeam(CStateManager& mgr) { if (x6e8_teamMgr == kInvalidUniqueId) return; @@ -356,7 +376,18 @@ void CBabygoth::RemoveFromTeam(urde::CStateManager& mgr) { } } -void CBabygoth::ApplySeparationBehavior(CStateManager& mgr) {} +void CBabygoth::ApplySeparationBehavior(CStateManager& mgr) { + for (CEntity* ent : mgr.GetListeningAiObjectList()) { + if (TCastToPtr ai = ent) { + if (ai.GetPtr() != this && GetAreaIdAlways() == ai->GetAreaIdAlways()) { + zeus::CVector3f sep = x45c_steeringBehaviors.Separation(*this, ai->GetTranslation(), 15.f); + if (!sep.isZero()) { + x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(sep, {}, 1.f)); + } + } + } + } +} void CBabygoth::ApplyDamage(CStateManager& mgr, TUniqueId uid) { if (TCastToConstPtr weap = mgr.GetObjectById(uid)) { @@ -368,6 +399,44 @@ void CBabygoth::ApplyDamage(CStateManager& mgr, TUniqueId uid) { } } +zeus::CVector3f CBabygoth::GetAimPosition(const CStateManager& mgr, float dt) const { + if (x450_bodyController->GetLocomotionType() != pas::ELocomotionType::Crouch && !x9f8_shellIds.empty()) { + zeus::CVector3f shellAvgPos; + for (TUniqueId id : x9f8_shellIds) { + if (TCastToConstPtr cact = mgr.GetObjectById(id)) + shellAvgPos += cact->GetSphereRadius() * zeus::skUp + cact->GetTranslation(); + } + if (!shellAvgPos.isZero()) + shellAvgPos = shellAvgPos / float(x9f8_shellIds.size()); + float mouthZ = shellAvgPos.z(); + if (TCastToConstPtr cact = mgr.GetObjectById(x9f6_mouthCollisionActor)) + mouthZ = cact->GetTranslation().z(); + float t = zeus::CVector2f::getAngleDiff(GetTransform().basis[1].toVec2f(), + (mgr.GetPlayer().GetTranslation() - GetTranslation()).toVec2f()) / M_PIF; + return zeus::CVector3f::lerp(zeus::CVector3f(shellAvgPos.toVec2f(), mouthZ), + zeus::CVector3f(shellAvgPos.toVec2f(), shellAvgPos.z()), t); + } else { + return CPatterned::GetAimPosition(mgr, 0.f); + } +} + +zeus::CVector3f CBabygoth::GetOrigin(const CStateManager& mgr, const CTeamAiRole& role, + const zeus::CVector3f& aimPos) const { + zeus::CVector3f ret = GetTranslation() - aimPos; + if (ret.canBeNormalized()) + ret = ret.normalized() * x2fc_minAttackRange + aimPos; + return ret; +} + +void CBabygoth::KnockBack(const zeus::CVector3f& backVec, CStateManager& mgr, const CDamageInfo& info, + EKnockBackType type, bool inDeferred, float magnitude) { + x460_knockBackController.SetAvailableState(EKnockBackAnimationState::Hurled, + x56c_shellState == EShellState::Destroyed); + CPatterned::KnockBack(backVec, mgr, info, type, inDeferred, magnitude); + if (x400_25_alive && x460_knockBackController.GetActiveParms().x0_animState == EKnockBackAnimationState::Hurled) + x330_stateMachineState.SetState(mgr, *this, GetStateMachine(), "GetUp"sv); +} + void CBabygoth::Shock(CStateManager& mgr, float duration, float damage) { if (x9f8_shellIds.empty()) return; @@ -391,7 +460,7 @@ void CBabygoth::UpdateTouchBounds() { void CBabygoth::UpdateAttackPosition(CStateManager& mgr, zeus::CVector3f& attackPos) { attackPos = GetTranslation(); - if (x8d8_ > 0.f) + if (x8d8_attackTimeLeft > 0.f) return; attackPos = mgr.GetPlayer().GetTranslation(); zeus::CVector3f distVec = GetTranslation() - attackPos; @@ -399,12 +468,12 @@ void CBabygoth::UpdateAttackPosition(CStateManager& mgr, zeus::CVector3f& attack attackPos += x2fc_minAttackRange * distVec.normalized(); } -void CBabygoth::sub8021e3f4(urde::CStateManager& mgr) { +void CBabygoth::UpdateShellHealth(CStateManager& mgr) { if (xa00_shellHitPoints <= 0.f) return; float hp = 0.f; - if (TCastToPtr colAct = mgr.ObjectById(x9f6_)) + if (TCastToPtr colAct = mgr.ObjectById(x9f6_mouthCollisionActor)) hp = zeus::max(hp, colAct->GetHealthInfo(mgr)->GetHP() - x570_babyData.GetShellHitPoints()); for (TUniqueId uid : x9f8_shellIds) { @@ -415,29 +484,29 @@ void CBabygoth::sub8021e3f4(urde::CStateManager& mgr) { xa00_shellHitPoints -= hp; if (xa00_shellHitPoints <= 0.f) { - x56c_ = 3; - sub8021d9d0(mgr); - CrackShell(mgr, xa2c_, x34_transform, x570_babyData.x15c_, false); + x56c_shellState = EShellState::Destroyed; + DestroyShell(mgr); + CrackShell(mgr, xa2c_destroyShellParticle, x34_transform, x570_babyData.x15c_destroyShellSfx, false); UpdateHealthInfo(mgr); } else { - if (xa00_shellHitPoints < CalculateShellCrackHP(2)) { - if (x56c_ != 2) { - CrackShell(mgr, xa20_, x34_transform, x570_babyData.x15a_, false); - x56c_ = 2; - xa04_ = 2; + if (xa00_shellHitPoints < CalculateShellCrackHP(EShellState::CrackTwo)) { + if (x56c_shellState != EShellState::CrackTwo) { + CrackShell(mgr, xa20_crackTwoParticle, x34_transform, x570_babyData.x15a_crackTwoSfx, false); + x56c_shellState = EShellState::CrackTwo; + xa04_drawMaterialIdx = 2; } - } else if (xa00_shellHitPoints < CalculateShellCrackHP(1)) { - if (x56c_ != 1) { - CrackShell(mgr, xa14_, x34_transform, x570_babyData.x158_, false); - x56c_ = 1; - xa04_ = 1; + } else if (xa00_shellHitPoints < CalculateShellCrackHP(EShellState::CrackOne)) { + if (x56c_shellState != EShellState::CrackOne) { + CrackShell(mgr, xa14_crackOneParticle, x34_transform, x570_babyData.x158_crackOneSfx, false); + x56c_shellState = EShellState::CrackOne; + xa04_drawMaterialIdx = 1; } } } - hp = (x56c_ == 3 ? x8ec_ : x570_babyData.GetShellHitPoints()); + hp = (x56c_shellState == EShellState::Destroyed ? x8ec_bodyHP : x570_babyData.GetShellHitPoints()); - if (TCastToPtr colAct = mgr.ObjectById(x9f6_)) { + if (TCastToPtr colAct = mgr.ObjectById(x9f6_mouthCollisionActor)) { colAct->HealthInfo(mgr)->SetHP(hp); } @@ -481,52 +550,52 @@ void CBabygoth::AddToTeam(CStateManager& mgr) { } } -void CBabygoth::sub8021e2c4(float dt) { - if (x8d8_ > 0.f) - x8d8_ -= dt * (xa49_28_ ? 2.f : 1.f); - if (x8e4_ > 0.f) - x8e4_ -= dt * (xa49_28_ ? 2.f : 1.f); - if (x8e0_ > 0.f) - x8e0_ -= dt; - if (x8e8_ < x570_babyData.x168_) - x8e8_ += dt; +void CBabygoth::UpdateTimers(float dt) { + if (x8d8_attackTimeLeft > 0.f) + x8d8_attackTimeLeft -= dt * (xa49_28_onApproachPath ? 2.f : 1.f); + if (x8e4_fireballAttackTimeLeft > 0.f) + x8e4_fireballAttackTimeLeft -= dt * (xa49_28_onApproachPath ? 2.f : 1.f); + if (x8e0_attackCooldownTimeLeft > 0.f) + x8e0_attackCooldownTimeLeft -= dt; + if (x8e8_interestTimer < x570_babyData.x168_interestTime) + x8e8_interestTimer += dt; } -void CBabygoth::sub8021e708(CStateManager& mgr) { +void CBabygoth::UpdateHealth(CStateManager& mgr) { if (!x400_25_alive) return; - if (x56c_ == 3) { + if (x56c_shellState == EShellState::Destroyed) { float hp = 0.f; - if (TCastToPtr colAct = mgr.ObjectById(x9f6_)) { - hp = zeus::max(hp, colAct->GetHealthInfo(mgr)->GetHP() - x8ec_); + if (TCastToPtr colAct = mgr.ObjectById(x9f6_mouthCollisionActor)) { + hp = zeus::max(hp, colAct->GetHealthInfo(mgr)->GetHP() - x8ec_bodyHP); } for (TUniqueId uid : x9f8_shellIds) { if (TCastToPtr colAct = mgr.ObjectById(uid)) { - hp = zeus::max(hp, colAct->GetHealthInfo(mgr)->GetHP() - x8ec_); + hp = zeus::max(hp, colAct->GetHealthInfo(mgr)->GetHP() - x8ec_bodyHP); } } HealthInfo(mgr)->SetHP(hp - HealthInfo(mgr)->GetHP()); if (HealthInfo(mgr)->GetHP() <= 0.f) { Death(mgr, {}, EScriptObjectState::DeathRattle); - xa48_26_ = true; - xa49_26_ = false; + xa48_26_inProjectileAttack = true; + xa49_26_readyForTeam = false; RemoveFromTeam(mgr); RemoveMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); } else { - if (TCastToPtr colAct = mgr.ObjectById(x9f6_)) - colAct->HealthInfo(mgr)->SetHP(x8ec_); + if (TCastToPtr colAct = mgr.ObjectById(x9f6_mouthCollisionActor)) + colAct->HealthInfo(mgr)->SetHP(x8ec_bodyHP); for (TUniqueId uid : x9f8_shellIds) { if (TCastToPtr colAct = mgr.ObjectById(uid)) { - colAct->HealthInfo(mgr)->SetHP(x8ec_); + colAct->HealthInfo(mgr)->SetHP(x8ec_bodyHP); } } } } else { - sub8021e3f4(mgr); + UpdateShellHealth(mgr); } } @@ -539,14 +608,14 @@ void CBabygoth::UpdateParticleEffects(float dt, CStateManager& mgr) { } void CBabygoth::TryToGetUp(CStateManager& mgr) { - if (!x400_25_alive || x450_bodyController->GetFallState() == pas::EFallState::Zero || xa49_24_) + if (!x400_25_alive || x450_bodyController->GetFallState() == pas::EFallState::Zero || xa49_24_gettingUp) return; x330_stateMachineState.SetState(mgr, *this, GetStateMachine(), "GetUp"sv); } bool CBabygoth::CheckShouldWakeUp(CStateManager& mgr, float dt) { - if (!xa48_30_) + if (!xa48_30_heardPlayerFire) return false; xa48_24_isAlert = x400_24_hitByPlayerProjectile && mgr.GetActiveRandom()->Float() < (0.5f * dt); @@ -567,8 +636,8 @@ void CBabygoth::TurnAround(CStateManager& mgr, EStateMsg msg, float arg) { x8f0_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId()); x8f0_boneTracking.SetActive(true); UpdateAttackPosition(mgr, x2e0_destPos); - SetPathFindMode(EPathFindMode::Zero); - CPatterned::PathFind(mgr, msg, arg); + SetPathFindMode(EPathFindMode::Normal); + CPatterned::PathFind(mgr, EStateMsg::Activate, arg); x450_bodyController->GetCommandMgr().ClearLocomotionCmds(); } else if (msg == EStateMsg::Update) { if (ShouldTurn(mgr, arg)) { @@ -586,56 +655,57 @@ void CBabygoth::TurnAround(CStateManager& mgr, EStateMsg msg, float arg) { void CBabygoth::GetUp(CStateManager&, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { - x568_ = 0; - xa49_24_ = true; + x568_stateProg = 0; + xa49_24_gettingUp = true; } else if (msg == EStateMsg::Update) { - if (x568_ == 0) { + if (x568_stateProg == 0) { if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Getup) - x568_ = 3; + x568_stateProg = 3; else x450_bodyController->GetCommandMgr().DeliverCmd(CBCGetupCmd(pas::EGetupType::Zero)); - } else if (x568_ == 3 && x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::Getup) - x568_ = 4; + } else if (x568_stateProg == 3 && + x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::Getup) + x568_stateProg = 4; } else if (msg == EStateMsg::Deactivate) { - xa49_24_ = false; + xa49_24_gettingUp = false; } } void CBabygoth::Enraged(CStateManager&, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { - xa48_29_ = true; - x568_ = 0; + xa48_29_hasBeenEnraged = true; + x568_stateProg = 0; } else if (msg == EStateMsg::Update) { - if (x568_ == 0) { + if (x568_stateProg == 0) { if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Generate) - x568_ = 3; + x568_stateProg = 3; else x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Three)); - } else if (x568_ == 3 && + } else if (x568_stateProg == 3 && x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::Generate) - x568_ = 4; + x568_stateProg = 4; } } void CBabygoth::FollowPattern(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { - x568_ = (xa49_25_ ? 0 : 4); - xa49_25_ = false; + x568_stateProg = (xa49_25_shouldStepBackwards ? 0 : 4); + xa49_25_shouldStepBackwards = false; x8f0_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId()); x8f0_boneTracking.SetActive(true); } else if (msg == EStateMsg::Update) { - if (x568_ == 0) { + if (x568_stateProg == 0) { x450_bodyController->GetCommandMgr().DeliverCmd( CBCStepCmd(pas::EStepDirection::Backward, pas::EStepType::Normal)); - } else if (x568_ == 3) { + } else if (x568_stateProg == 3) { if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Step) x450_bodyController->GetCommandMgr().SetTargetVector(mgr.GetPlayer().GetTranslation() - GetTranslation()); else - x568_ = 4; + x568_stateProg = 4; } } else if (msg == EStateMsg::Deactivate) { x8f0_boneTracking.SetActive(false); - SetPathFindMode(EPathFindMode::Zero); + SetPathFindMode(EPathFindMode::Normal); } } @@ -644,7 +714,7 @@ void CBabygoth::Taunt(CStateManager&, EStateMsg msg, float) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCTauntCmd(pas::ETauntType::One)); } else if (msg == EStateMsg::Update && x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::Taunt) { - x568_ = 4; + x568_stateProg = 4; } } @@ -653,9 +723,9 @@ void CBabygoth::Crouch(CStateManager& mgr, EStateMsg msg, float arg) { RemoveMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); mgr.GetPlayer().SetOrbitRequestForTarget(GetUniqueId(), CPlayer::EPlayerOrbitRequest::ActivateOrbitSource, mgr); x450_bodyController->SetLocomotionType(pas::ELocomotionType::Crouch); - xa48_30_ = xa48_24_isAlert = false; + xa48_30_heardPlayerFire = xa48_24_isAlert = false; x400_24_hitByPlayerProjectile = false; - x8e8_ = 0.f; + x8e8_interestTimer = 0.f; } else if (msg == EStateMsg::Deactivate) { AddMaterial(EMaterialTypes::Orbit, EMaterialTypes::Target, mgr); } @@ -663,55 +733,56 @@ void CBabygoth::Crouch(CStateManager& mgr, EStateMsg msg, float arg) { void CBabygoth::Deactivate(CStateManager&, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { - x568_ = 1; + x568_stateProg = 1; } else if (msg == EStateMsg::Update) { - if (x568_ == 0) { + if (x568_stateProg == 0) { if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Generate) { - x568_ = 3; + x568_stateProg = 3; x450_bodyController->SetLocomotionType(pas::ELocomotionType::Crouch); } - } else if (x568_ == 1) { + } else if (x568_stateProg == 1) { if ((x3a0_latestLeashPosition - GetTranslation()).magSquared() > 1.f) - x568_ = 2; + x568_stateProg = 2; else { zeus::CVector3f arrivalVec = x45c_steeringBehaviors.Arrival(*this, x3a0_latestLeashPosition, 15.f); x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(arrivalVec, {}, 1.f)); } - } else if (x568_ == 2) { - float angle = zeus::CVector3f::getAngleDiff(GetTranslation(), x8c4_); + } else if (x568_stateProg == 2) { + float angle = zeus::CVector3f::getAngleDiff(GetTranslation(), x8c4_initialFaceDir); if (angle > zeus::degToRad(5.f)) { - x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd({}, x8c4_, 1.f)); + x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd({}, x8c4_initialFaceDir, 1.f)); return; } - x568_ = 0; - } else if (x568_ == 3 && + x568_stateProg = 0; + } else if (x568_stateProg == 3 && x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Generate) { - x568_ = 4; + x568_stateProg = 4; } } } void CBabygoth::Generate(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { - x568_ = 0; - x8c4_ = GetTransform().basis[1]; + x568_stateProg = 0; + x8c4_initialFaceDir = GetTransform().basis[1]; } else if (msg == EStateMsg::Update) { - if (x568_ == 0) { + if (x568_stateProg == 0) { if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Generate) { x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); + x568_stateProg = 3; SendScriptMsgs(EScriptObjectState::Attack, mgr, EScriptObjectMessage::None); } else { x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(pas::EGenerateType::Zero)); } - } else if (x568_ == 3 && + } else if (x568_stateProg == 3 && x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::Generate) - x568_ = 4; + x568_stateProg = 4; } } void CBabygoth::TargetPatrol(CStateManager& mgr, EStateMsg msg, float arg) { if (msg == EStateMsg::Activate) { - xa49_29_ = false; + xa49_26_readyForTeam = false; RemoveFromTeam(mgr); x400_24_hitByPlayerProjectile = false; x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); @@ -721,18 +792,18 @@ void CBabygoth::TargetPatrol(CStateManager& mgr, EStateMsg msg, float arg) { } else { SetDestPos(x3a0_latestLeashPosition); } - x8b8_ = x2e0_destPos; + x8b8_backupDestPos = x2e0_destPos; if (GetSearchPath()) { - SetPathFindMode(EPathFindMode::Zero); - PathFind(mgr, msg, arg); + SetPathFindMode(EPathFindMode::Normal); + CPatterned::PathFind(mgr, msg, arg); } } else if (msg == EStateMsg::Update) { if (GetSearchPath() && !PathShagged(mgr, 0.f)) { - SetPathFindMode(EPathFindMode::Zero); - PathFind(mgr, msg, arg); + SetPathFindMode(EPathFindMode::Normal); + CPatterned::PathFind(mgr, msg, arg); ApplySeparationBehavior(mgr); } else { - zeus::CVector3f arrivalVec = x45c_steeringBehaviors.Arrival(*this, x8b8_, 9.f); + zeus::CVector3f arrivalVec = x45c_steeringBehaviors.Arrival(*this, x8b8_backupDestPos, 9.f); x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(arrivalVec, {}, 1.f)); } } @@ -751,32 +822,31 @@ void CBabygoth::Approach(CStateManager& mgr, EStateMsg msg, float arg) { x8f0_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId()); x8f0_boneTracking.SetActive(true); UpdateAttackPosition(mgr, x2e0_destPos); - SetPathFindMode(EPathFindMode::Zero); - CPatterned::PathFind(mgr, msg, arg); - if (!xa49_27_) + SetPathFindMode(EPathFindMode::Normal); + CPatterned::PathFind(mgr, EStateMsg::Activate, arg); + if (!xa49_27_locomotionValid) x450_bodyController->GetCommandMgr().ClearLocomotionCmds(); - xa48_31_ = true; - xa48_28_ = true; + xa48_31_approachNeedsPathSearch = true; + xa49_28_onApproachPath = true; } else if (msg == EStateMsg::Update) { - SetPathFindMode(EPathFindMode::One); - if (xa48_31_) { - x2e0_destPos = x8b8_; - if (x7d0_.FindClosestReachablePoint(GetTranslation(), x2e0_destPos) == CPathFindSearch::EResult::Success && - (x2e0_destPos - GetTranslation()).magSquared() < 10.f) { - x2e0_destPos = GetTranslation(); + SetPathFindMode(EPathFindMode::Approach); + if (xa48_31_approachNeedsPathSearch) { + x2e0_destPos = x8b8_backupDestPos; + if (x7d0_approachPathSearch.FindClosestReachablePoint(GetTranslation(), x2e0_destPos) == + CPathFindSearch::EResult::Success) { + if ((x2e0_destPos - GetTranslation()).magSquared() < 10.f) + x2e0_destPos = GetTranslation(); + x8b8_backupDestPos = x2e0_destPos; + CPatterned::PathFind(mgr, EStateMsg::Activate, arg); + if (!xa49_27_locomotionValid) + x450_bodyController->GetCommandMgr().ClearLocomotionCmds(); } - x8b8_ = x2e0_destPos; - CPatterned::PathFind(mgr, msg, arg); - - if (!xa49_27_) - x450_bodyController->GetCommandMgr().ClearLocomotionCmds(); - - xa48_31_ = true; + xa48_31_approachNeedsPathSearch = false; } - xa49_27_ = IsDestinationObstructed(mgr); - if (xa49_27_ && GetSearchPath() && !PathShagged(mgr, 0.f) && - (x7d0_.GetCurrentWaypoint() < x7d0_.GetWaypoints().size() - 1)) { + xa49_27_locomotionValid &= !IsDestinationObstructed(mgr); + if (xa49_27_locomotionValid && GetSearchPath() && !PathShagged(mgr, 0.f) && + (x7d0_approachPathSearch.GetCurrentWaypoint() < x7d0_approachPathSearch.GetWaypoints().size() - 1)) { CPatterned::PathFind(mgr, msg, arg); ApplySeparationBehavior(mgr); } else if (ShouldTurn(mgr, 0.f)) { @@ -785,31 +855,31 @@ void CBabygoth::Approach(CStateManager& mgr, EStateMsg msg, float arg) { x450_bodyController->GetCommandMgr().DeliverCmd(CBCLocomotionCmd(direction.normalized(), {}, 1.f)); } } - xa49_27_ = true; + xa49_27_locomotionValid = true; } else if (msg == EStateMsg::Deactivate) { x8f0_boneTracking.SetActive(false); - SetPathFindMode(EPathFindMode::Zero); + SetPathFindMode(EPathFindMode::Normal); } } void CBabygoth::PathFind(CStateManager& mgr, EStateMsg msg, float arg) { if (msg == EStateMsg::Activate) { - xa49_28_ = false; - xa49_26_ = true; + xa49_28_onApproachPath = false; + xa49_26_readyForTeam = true; xa48_24_isAlert = false; - x8e8_ = 0.f; + x8e8_interestTimer = 0.f; x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); x8f0_boneTracking.SetTarget(mgr.GetPlayer().GetUniqueId()); x8f0_boneTracking.SetActive(true); UpdateAttackPosition(mgr, x2e0_destPos); - x8b8_ = x2e0_destPos; - SetPathFindMode(EPathFindMode::Zero); + x8b8_backupDestPos = x2e0_destPos; + SetPathFindMode(EPathFindMode::Normal); CPatterned::PathFind(mgr, msg, arg); } else if (msg == EStateMsg::Update) { - SetPathFindMode(EPathFindMode::Zero); - if (GetSearchPath() && !PathShagged(mgr, 0.f) && x6ec_.GetCurrentWaypoint() < x6ec_.GetWaypoints().size() - 1) { + SetPathFindMode(EPathFindMode::Normal); + if (GetSearchPath() && !PathShagged(mgr, 0.f) && x6ec_pathSearch.GetCurrentWaypoint() < x6ec_pathSearch.GetWaypoints().size() - 1) { CPatterned::PathFind(mgr, msg, arg); - x8e8_ = 0.f; + x8e8_interestTimer = 0.f; zeus::CVector3f move = x450_bodyController->GetCommandMgr().GetMoveVector(); if (move.canBeNormalized()) { float mag = x45c_steeringBehaviors.Arrival(*this, mgr.GetPlayer().GetTranslation(), 15.f).magnitude(); @@ -832,17 +902,183 @@ void CBabygoth::PathFind(CStateManager& mgr, EStateMsg msg, float arg) { } } -void CBabygoth::SpecialAttack(CStateManager&, EStateMsg, float) {} +void CBabygoth::UpdateAttackTimeLeft(CStateManager& mgr) { + float mult = x56c_shellState == EShellState::Destroyed ? 0.6f : 1.f; + x8d8_attackTimeLeft = (mgr.GetActiveRandom()->Float() * x308_attackTimeVariation + x304_averageAttackTime) * mult; + x8e4_fireballAttackTimeLeft = (mgr.GetActiveRandom()->Float() * x570_babyData.GetFireballAttackVariance() + + x570_babyData.GetFireballAttackTime()) * mult; +} -void CBabygoth::Attack(CStateManager&, EStateMsg, float) {} +void CBabygoth::SpecialAttack(CStateManager& mgr, EStateMsg state, float) { + if (state == EStateMsg::Activate) { + xa48_27_ = true; + x8e8_interestTimer = 0.f; + xa49_27_locomotionValid = false; + TCastToPtr aimgr = mgr.ObjectById(x6e8_teamMgr); + if (aimgr && aimgr->HasTeamAiRole(GetUniqueId())) + x568_stateProg = aimgr->AddRangedAttacker(GetUniqueId()) ? 0 : 4; + else + x568_stateProg = 0; + } else if (state == EStateMsg::Update) { + switch (x568_stateProg) { + case 0: + if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::ProjectileAttack) { + x568_stateProg = 3; + x3b4_speed = 2.f * x8d0_initialSpeed; + } else { + x450_bodyController->GetCommandMgr().DeliverCmd( + CBCProjectileAttackCmd(pas::ESeverity::One, mgr.GetPlayer().GetTranslation(), false)); + } + break; + case 3: + if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::ProjectileAttack) + x568_stateProg = 4; + break; + default: + break; + } + } else if (state == EStateMsg::Deactivate) { + UpdateAttackTimeLeft(mgr); + xa48_27_ = false; + x3b4_speed = x8d0_initialSpeed; + if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::ProjectileAttack) + x450_bodyController->GetCommandMgr().DeliverCmd(CBodyStateCmd(EBodyStateCmd::NextState)); + CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Ranged, mgr, x6e8_teamMgr, GetUniqueId(), false); + } +} -void CBabygoth::ProjectileAttack(CStateManager&, EStateMsg, float) {} +void CBabygoth::ExtendCollisionActorTouchBounds(CStateManager& mgr, const zeus::CVector3f& extents) { + for (u32 i = 0; i < x928_colActMgr->GetNumCollisionActors(); ++i) { + const CJointCollisionDescription& desc = x928_colActMgr->GetCollisionDescFromIndex(i); + if (TCastToPtr cact = mgr.ObjectById(desc.GetCollisionActorId())) + cact->SetExtendedTouchBounds(extents); + } +} + +void CBabygoth::UpdateAttack(CStateManager& mgr, float dt) { + switch (x568_stateProg) { + case 0: + x8dc_attackTimer += dt; + if (x8dc_attackTimer < x570_babyData.x160_timeUntilAttack) { + if (!xa48_28_pendingAttackContactDamage) { + x568_stateProg = 3; + x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::One)); + } else { + zeus::CTransform mouthXf = GetLctrTransform(x9f4_mouthLocator); + if ((mgr.GetPlayer().GetTranslation() - mouthXf.origin).dot(GetTransform().basis[1]) > 0.f) { + SetPathFindMode(EPathFindMode::Normal); + auto* path = GetSearchPath(); + if (path && !PathShagged(mgr, 0.f)) { + CPatterned::PathFind(mgr, EStateMsg::Update, dt); + ApplySeparationBehavior(mgr); + } else { + x568_stateProg = 4; + } + } else { + x568_stateProg = 4; + } + } + } else { + x568_stateProg = 4; + } + break; + case 3: + if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::MeleeAttack) { + x568_stateProg = 4; + } else { + xa49_25_shouldStepBackwards = true; + } + break; + default: + break; + } +} + +void CBabygoth::Attack(CStateManager& mgr, EStateMsg state, float dt) { + if (state == EStateMsg::Activate) { + x568_stateProg = 0; + xa48_25_ = xa48_28_pendingAttackContactDamage = true; + x8e8_interestTimer = 0.f; + xa49_25_shouldStepBackwards = false; + x2e0_destPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); + SetPathFindMode(EPathFindMode::Normal); + CPatterned::PathFind(mgr, state, dt); + x8dc_attackTimer = 0.f; + x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::FullSpeed); + x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(1.f, 1.f); + ExtendCollisionActorTouchBounds(mgr, zeus::CVector3f(0.2f)); + } else if (state == EStateMsg::Update) { + UpdateAttack(mgr, dt); + } else if (state == EStateMsg::Deactivate) { + UpdateAttackTimeLeft(mgr); + xa48_25_ = xa48_28_pendingAttackContactDamage = false; + x8e0_attackCooldownTimeLeft = x570_babyData.x164_attackCooldownTime; + x8dc_attackTimer = 0.f; + float maxSpeed = x450_bodyController->GetBodyStateInfo().GetMaxSpeed(); + x450_bodyController->GetCommandMgr().SetSteeringBlendMode(ESteeringBlendMode::Clamped); + x450_bodyController->GetCommandMgr().SetSteeringSpeedRange(0.f, + (maxSpeed > 0.f) ? x450_bodyController->GetBodyStateInfo().GetLocomotionSpeed( + pas::ELocomotionAnim::Walk) / maxSpeed : 1.f); + ExtendCollisionActorTouchBounds(mgr, zeus::skZero3f); + CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Melee, mgr, x6e8_teamMgr, GetUniqueId(), true); + } +} + +void CBabygoth::ProjectileAttack(CStateManager& mgr, EStateMsg state, float) { + if (state == EStateMsg::Activate) { + xa48_26_inProjectileAttack = true; + x8e8_interestTimer = 0.f; + xa49_27_locomotionValid = false; + TCastToPtr aimgr = mgr.ObjectById(x6e8_teamMgr); + if (aimgr && aimgr->HasTeamAiRole(GetUniqueId())) + x568_stateProg = aimgr->AddRangedAttacker(GetUniqueId()) ? 0 : 4; + else + x568_stateProg = 0; + } else if (state == EStateMsg::Update) { + zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); + switch (x568_stateProg) { + case 0: + if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::ProjectileAttack) { + x568_stateProg = 3; + x3b4_speed = 2.f * x8d0_initialSpeed; + } else { + x450_bodyController->GetCommandMgr().DeliverCmd(CBCProjectileAttackCmd(pas::ESeverity::Two, aimPos, false)); + } + break; + case 3: + if (x450_bodyController->GetCurrentStateId() != pas::EAnimationState::ProjectileAttack) { + x568_stateProg = 4; + } else if (!mgr.ObjectById(x980_flameThrower)) { + x450_bodyController->GetCommandMgr().DeliverTargetVector(aimPos - GetTranslation()); + x8f0_boneTracking.UnsetTarget(); + x8f0_boneTracking.SetTargetPosition(aimPos); + } + } + } else if (state == EStateMsg::Deactivate) { + UpdateAttackTimeLeft(mgr); + if (auto* fthrower = static_cast(mgr.ObjectById(x980_flameThrower))) + fthrower->Reset(mgr, false); + xa48_26_inProjectileAttack = false; + if (x450_bodyController->GetCurrentStateId() == pas::EAnimationState::ProjectileAttack) + x450_bodyController->GetCommandMgr().DeliverCmd(CBodyStateCmd(EBodyStateCmd::NextState)); + x8f0_boneTracking.SetActive(false); + x3b4_speed = x8d0_initialSpeed; + CTeamAiMgr::ResetTeamAiRole(CTeamAiMgr::EAttackType::Ranged, mgr, x6e8_teamMgr, GetUniqueId(), false); + } +} + +bool CBabygoth::Leash(CStateManager& mgr, float) { + if ((x3a0_latestLeashPosition - GetTranslation()).magSquared() > x3c8_leashRadius * x3c8_leashRadius) + return (mgr.GetPlayer().GetTranslation() - GetTranslation()).magSquared() > + x3cc_playerLeashRadius * x3cc_playerLeashRadius && x3d4_curPlayerLeashTime > x3d0_playerLeashTime; + return false; +} bool CBabygoth::IsDestinationObstructed(CStateManager& mgr) { for (auto obj : mgr.GetListeningAiObjectList()) { if (TCastToPtr ai = obj) { if (ai->GetAreaIdAlways() == GetAreaIdAlways()) { - if ((x8b8_ - ai->GetTranslation()).magSquared() <= 10.f) + if ((x8b8_backupDestPos - ai->GetTranslation()).magSquared() <= 10.f) return true; } } @@ -850,7 +1086,7 @@ bool CBabygoth::IsDestinationObstructed(CStateManager& mgr) { return false; } -void CBabygoth::sub8021d9d0(urde::CStateManager& mgr) { +void CBabygoth::DestroyShell(CStateManager& mgr) { ModelData()->AnimationData()->SubstituteModelData(xa08_noShellModel); for (TUniqueId uid : x9f8_shellIds) { @@ -858,63 +1094,132 @@ void CBabygoth::sub8021d9d0(urde::CStateManager& mgr) { colAct->SetWeaponCollisionResponseType(EWeaponCollisionResponseTypes::Unknown41); } } - xa04_ = 0; + xa04_drawMaterialIdx = 0; } -void CBabygoth::CrackShell(CStateManager& mgr, const TLockedToken& desc, - const zeus::CTransform& xf, u16 sfx, bool b1) { +void CBabygoth::CrackShell(CStateManager& mgr, const TLockedToken& desc, + const zeus::CTransform& xf, u16 sfx, bool nonEmitterSfx) { mgr.AddObject(new CExplosion(desc, mgr.AllocateUniqueId(), true, CEntityInfo(GetAreaIdAlways(), CEntity::NullConnectionList), "Babygoth Shell Crack Fx"sv, xf, 0, GetModelData()->GetScale(), zeus::skWhite)); - if (b1) + if (nonEmitterSfx) CSfxManager::SfxStart(sfx, 0x7f, 64 / 127.f, false, 0x7f, false, -1); else CSfxManager::AddEmitter(sfx, GetTranslation(), zeus::skUp, false, false, 0x7f, GetAreaIdAlways()); } -void CBabygoth::UpdateHealthInfo(urde::CStateManager& mgr) { +void CBabygoth::UpdateHealthInfo(CStateManager& mgr) { CHealthInfo* hInfo = HealthInfo(mgr); - if (TCastToPtr colAct = mgr.ObjectById(x9f6_)) { + if (TCastToPtr colAct = mgr.ObjectById(x9f6_mouthCollisionActor)) { (*colAct->HealthInfo(mgr)) = *hInfo; - colAct->SetDamageVulnerability(x98c_); + colAct->SetDamageVulnerability(x98c_dVuln); } for (TUniqueId uid : x9f8_shellIds) { if (TCastToPtr colAct = mgr.ObjectById(uid)) { (*colAct->HealthInfo(mgr)) = *hInfo; - colAct->SetDamageVulnerability(x98c_); + colAct->SetDamageVulnerability(x98c_dVuln); } } } -float CBabygoth::CalculateShellCrackHP(u32 w1) { - if (w1 == 0) +float CBabygoth::CalculateShellCrackHP(EShellState state) { + if (state == EShellState::Default) return x570_babyData.GetShellHitPoints(); - else if (w1 == 1) + else if (state == EShellState::CrackOne) return 0.66666669f * x570_babyData.GetShellHitPoints(); - else if (w1 == 2) + else if (state == EShellState::CrackTwo) return 0.33333334f * x570_babyData.GetShellHitPoints(); return 0.f; } -bool CBabygoth::ShouldTurn(urde::CStateManager& mgr, float arg) { +bool CBabygoth::ShouldTurn(CStateManager& mgr, float arg) { const float speedScale = GetModelData()->GetAnimationData()->GetSpeedScale(); zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, (speedScale > 0.f ? 1.f / speedScale : 0.f)); return zeus::CVector2f::getAngleDiff(GetTransform().basis[1].toVec2f(), (aimPos - GetTranslation()).toVec2f()) > (arg == 0.f ? 0.78539819f : arg); } +bool CBabygoth::ShouldAttack(CStateManager& mgr, float arg) { + if (mgr.GetPlayer().GetAreaIdAlways() == GetAreaIdAlways() && !mgr.GetPlayer().GetFrozenState() && + x8d8_attackTimeLeft <= 0.f && x8e0_attackCooldownTimeLeft <= 0.f) { + zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); + if (x450_bodyController->GetBodyStateInfo().GetMaxSpeed() * x570_babyData.x160_timeUntilAttack > + (aimPos - GetTranslation()).magnitude() && !ShouldTurn(mgr, zeus::degToRad(25.f)) && + !IsPatternObstructed(mgr, GetLctrTransform(x9f4_mouthLocator).origin, aimPos)) { + if (TCastToPtr aimgr = mgr.ObjectById(x6e8_teamMgr)) { + if (aimgr->IsPartOfTeam(GetUniqueId())) + return aimgr->AddMeleeAttacker(GetUniqueId()); + } + return true; + } + } + return false; +} + +bool CBabygoth::ShouldSpecialAttack(CStateManager& mgr, float arg) { + if (mgr.GetPlayer().GetAreaIdAlways() == GetAreaIdAlways() && x8e4_fireballAttackTimeLeft <= 0.f) { + zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); + zeus::CVector3f mouthPos = GetLctrTransform(x9f4_mouthLocator).origin; + return (aimPos - mouthPos).magSquared() >= x300_maxAttackRange * x300_maxAttackRange && + !ShouldTurn(mgr, zeus::degToRad(25.f)) && !IsPatternObstructed(mgr, mouthPos, aimPos); + } + return false; +} + +bool CBabygoth::ShouldFire(CStateManager& mgr, float arg) { + if (mgr.GetPlayer().GetAreaIdAlways() == GetAreaIdAlways() && x8d8_attackTimeLeft <= 0.f) { + zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); + zeus::CVector3f mouthPos = GetLctrTransform(x9f4_mouthLocator).origin; + return (aimPos - mouthPos).magSquared() <= x300_maxAttackRange * x300_maxAttackRange && + !ShouldTurn(mgr, zeus::degToRad(30.f)) && !IsPatternObstructed(mgr, mouthPos, aimPos); + } + return false; +} + +bool CBabygoth::TooClose(CStateManager& mgr, float arg) { + if (x930_aabox.CalculateAABox(GetTransform()).intersects(mgr.GetPlayer().GetBoundingBox())) { + xa49_25_shouldStepBackwards = true; + return true; + } + return false; +} + +bool CBabygoth::LineOfSight(CStateManager& mgr, float arg) { + zeus::CVector3f aimPos = mgr.GetPlayer().GetAimPosition(mgr, 0.f); + zeus::CVector3f mouthPos = GetLctrTransform(x9f4_mouthLocator).origin; + return !IsPatternObstructed(mgr, mouthPos, aimPos); +} + +bool CBabygoth::LostInterest(CStateManager& mgr, float arg) { + return x8e8_interestTimer >= x570_babyData.x168_interestTime && + x6ec_pathSearch.OnPath(GetTranslation()) == CPathFindSearch::EResult::Success; +} + bool CBabygoth::InMaxRange(CStateManager& mgr, float) { return (GetTranslation() - mgr.GetPlayer().GetTranslation()).magSquared() < (1.5f * x300_maxAttackRange); } +bool CBabygoth::InDetectionRange(CStateManager& mgr, float arg) { + if (xa48_24_isAlert) + return true; + float range = (arg > 0.f ? arg : 1.f) * x3bc_detectionRange; + zeus::CVector3f delta = mgr.GetPlayer().GetTranslation() - GetTranslation(); + if (delta.magSquared() < range * range) { + if (x3c0_detectionHeightRange > 0.f) + return delta.z() * delta.z() < x3c0_detectionHeightRange * x3c0_detectionHeightRange; + return true; + } + return false; +} + bool CBabygoth::Listen(const zeus::CVector3f& origin, EListenNoiseType noiseType) { if (!x400_25_alive || noiseType != EListenNoiseType::PlayerFire || (origin - GetTranslation()).magSquared() >= 1600.f) return false; - xa48_30_ = true; + xa48_30_heardPlayerFire = true; return true; } } // namespace urde::MP1 diff --git a/Runtime/MP1/World/CBabygoth.hpp b/Runtime/MP1/World/CBabygoth.hpp index 9d672b347..b95f3e580 100644 --- a/Runtime/MP1/World/CBabygoth.hpp +++ b/Runtime/MP1/World/CBabygoth.hpp @@ -12,43 +12,42 @@ class CWeaponDescription; namespace urde::MP1 { struct CBabygothData { - float x0_; - float x4_; - CAssetId x8_; - CDamageInfo xc_; - CDamageInfo x28_; - CAssetId x44_; + float x0_fireballAttackTime; + float x4_fireballAttackTimeVariance; + CAssetId x8_fireballWeapon; + CDamageInfo xc_fireballDamage; + CDamageInfo x28_attackContactDamage; + CAssetId x44_fireBreathWeapon; CAssetId x48_fireBreathRes; CDamageInfo x4c_fireBreathDamage; - CDamageVulnerability x68_; + CDamageVulnerability x68_mouthVulnerabilities; CDamageVulnerability xd0_shellVulnerabilities; - CAssetId x138_; - CAssetId x13c_; + CAssetId x138_noShellModel; + CAssetId x13c_noShellSkin; float x140_shellHitPoints; s16 x144_shellCrackSfx; - CAssetId x148_; - CAssetId x14c_; - CAssetId x150_; - CAssetId x154_; - s16 x158_; - s16 x15a_; - s16 x15c_; - float x160_; - float x164_; - float x168_; - CAssetId x16c_; - s16 x170_; - CAssetId x174_; + CAssetId x148_intermediateCrackParticle; + CAssetId x14c_crackOneParticle; + CAssetId x150_crackTwoParticle; + CAssetId x154_destroyShellParticle; + s16 x158_crackOneSfx; + s16 x15a_crackTwoSfx; + s16 x15c_destroyShellSfx; + float x160_timeUntilAttack; + float x164_attackCooldownTime; + float x168_interestTime; + CAssetId x16c_flamePlayerSteamTxtr; + s16 x170_flamePlayerHitSfx; + CAssetId x174_flamePlayerIceTxtr; public: CBabygothData(CInputStream&); - CAssetId GetShellModelResId() const; - void GetFireballDamage() const; - CAssetId GetFireballResID() const; - void GetFireballAttackVariance() const; - void GetFireballAttackTime() const; + const CDamageInfo& GetFireballDamage() const { return xc_fireballDamage; } + CAssetId GetFireballResID() const { return x8_fireballWeapon; } + float GetFireballAttackVariance() const { return x4_fireballAttackTimeVariance; } + float GetFireballAttackTime() const { return x0_fireballAttackTime; } CAssetId GetFireBreathResId() const { return x48_fireBreathRes; } - CDamageInfo GetFireBreathDamage() const { return x4c_fireBreathDamage; } + const CDamageInfo& GetFireBreathDamage() const { return x4c_fireBreathDamage; } const CDamageVulnerability& GetShellDamageVulnerability() const { return xd0_shellVulnerabilities; } float GetShellHitPoints() const { return x140_shellHitPoints; } s16 GetShellCrackSfx() { return x144_shellCrackSfx; } @@ -56,62 +55,63 @@ public: class CBabygoth final : public CPatterned { public: - enum class EPathFindMode { Zero, One }; + enum class EPathFindMode { Normal, Approach }; + enum class EShellState { Default, CrackOne, CrackTwo, Destroyed }; private: static constexpr s32 skSphereJointCount = 5; static const SSphereJointInfo skSphereJointList[skSphereJointCount]; static const std::string_view skpMouthDamageJoint; - s32 x568_ = -1; - u32 x56c_ = 0; + s32 x568_stateProg = -1; + EShellState x56c_shellState = EShellState::Default; CBabygothData x570_babyData; TUniqueId x6e8_teamMgr = kInvalidUniqueId; - CPathFindSearch x6ec_; - CPathFindSearch x7d0_; + CPathFindSearch x6ec_pathSearch; + CPathFindSearch x7d0_approachPathSearch; EPathFindMode x8b4_pathFindMode; - zeus::CVector3f x8b8_; - zeus::CVector3f x8c4_; - float x8d0_; + zeus::CVector3f x8b8_backupDestPos; + zeus::CVector3f x8c4_initialFaceDir; + float x8d0_initialSpeed; float x8d4_stepBackwardDist = 0.f; - float x8d8_ = 0.f; - float x8dc_ = 0.f; - float x8e0_ = 0.f; - float x8e4_ = 0.f; - float x8e8_ = 0.f; - float x8ec_ = 0.f; + float x8d8_attackTimeLeft = 0.f; + float x8dc_attackTimer = 0.f; + float x8e0_attackCooldownTimeLeft = 0.f; + float x8e4_fireballAttackTimeLeft = 0.f; + float x8e8_interestTimer = 0.f; + float x8ec_bodyHP = 0.f; CBoneTracking x8f0_boneTracking; std::unique_ptr x928_colActMgr; CCollidableAABox x930_aabox; - CProjectileInfo x958_; + CProjectileInfo x958_iceProjectile; TUniqueId x980_flameThrower = kInvalidUniqueId; TToken x984_flameThrowerDesc; - CDamageVulnerability x98c_; + CDamageVulnerability x98c_dVuln; CSegId x9f4_mouthLocator; - TUniqueId x9f6_ = kInvalidUniqueId; + TUniqueId x9f6_mouthCollisionActor = kInvalidUniqueId; rstl::reserved_vector x9f8_shellIds; float xa00_shellHitPoints; - u32 xa04_ = 0; + u32 xa04_drawMaterialIdx = 0; TLockedToken xa08_noShellModel; - TToken xa14_; - TToken xa20_; - TToken xa2c_; - TLockedToken xa38_; // Used to be an optional, not necessary in URDE + TToken xa14_crackOneParticle; + TToken xa20_crackTwoParticle; + TToken xa2c_destroyShellParticle; + TLockedToken xa38_intermediateCrackParticle; // Used to be an optional, not necessary in URDE union { struct { bool xa48_24_isAlert : 1; bool xa48_25_ : 1; - bool xa48_26_ : 1; + bool xa48_26_inProjectileAttack : 1; bool xa48_27_ : 1; - bool xa48_28_ : 1; - bool xa48_29_ : 1; - bool xa48_30_ : 1; - bool xa48_31_ : 1; - bool xa49_24_ : 1; - bool xa49_25_ : 1; - bool xa49_26_ : 1; - bool xa49_27_ : 1; - bool xa49_28_ : 1; - bool xa49_29_ : 1; + bool xa48_28_pendingAttackContactDamage : 1; + bool xa48_29_hasBeenEnraged : 1; + bool xa48_30_heardPlayerFire : 1; + bool xa48_31_approachNeedsPathSearch : 1; + bool xa49_24_gettingUp : 1; + bool xa49_25_shouldStepBackwards : 1; + bool xa49_26_readyForTeam : 1; + bool xa49_27_locomotionValid : 1; + bool xa49_28_onApproachPath : 1; + bool xa49_29_objectSpaceCollision : 1; }; u32 _dummy = 0; }; @@ -130,7 +130,7 @@ private: void ApplySeparationBehavior(CStateManager&); - bool IsMouthCollisionActor(TUniqueId uid) { return x9f6_ == uid; } + bool IsMouthCollisionActor(TUniqueId uid) { return x9f6_mouthCollisionActor == uid; } bool IsShell(TUniqueId uid) { for (TUniqueId shellId : x9f8_shellIds) { @@ -146,9 +146,9 @@ private: void AddToTeam(CStateManager& mgr); - void sub8021e2c4(float); + void UpdateTimers(float); - void sub8021e708(CStateManager&); + void UpdateHealthInfo(CStateManager& mgr); void UpdateParticleEffects(float, CStateManager&); @@ -162,17 +162,23 @@ private: void UpdateAttackPosition(CStateManager&, zeus::CVector3f&); - void sub8021e3f4(CStateManager&); + void UpdateShellHealth(CStateManager&); bool IsDestinationObstructed(CStateManager&); - void sub8021d9d0(CStateManager&); + void DestroyShell(CStateManager& mgr); void CrackShell(CStateManager&, const TLockedToken&, const zeus::CTransform&, u16, bool); - void UpdateHealthInfo(CStateManager&); + void UpdateHealth(CStateManager&); - float CalculateShellCrackHP(u32); + float CalculateShellCrackHP(EShellState state); + + void UpdateAttackTimeLeft(CStateManager& mgr); + + void ExtendCollisionActorTouchBounds(CStateManager& mgr, const zeus::CVector3f& extents); + + void UpdateAttack(CStateManager& mgr, float dt); public: DEFINE_PATTERNED(Babygoth) @@ -184,7 +190,7 @@ public: void PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) { CPatterned::PreRender(mgr, frustum); - xb4_drawFlags.x1_matSetIdx = u8(xa04_); + xb4_drawFlags.x1_matSetIdx = u8(xa04_drawMaterialIdx); } void Think(float, CStateManager&); @@ -201,7 +207,7 @@ public: const CWeaponMode& wMode, EProjectileAttrib attrib) const { if (wMode.GetType() == EWeaponType::Ice) return EWeaponCollisionResponseTypes::None; - if (x56c_ != 3) + if (x56c_shellState != EShellState::Destroyed) return EWeaponCollisionResponseTypes::Unknown66; return CPatterned::GetCollisionResponseType(v1, v2, wMode, attrib); } @@ -215,11 +221,21 @@ public: return &CDamageVulnerability::ReflectVulnerabilty(); } + zeus::CVector3f GetAimPosition(const CStateManager& mgr, float dt) const; + + zeus::CVector3f GetOrigin(const CStateManager& mgr, const CTeamAiRole& role, + const zeus::CVector3f& aimPos) const; + void TakeDamage(const zeus::CVector3f&, float) { if (x400_25_alive) x428_damageCooldownTimer = 0.33f; } + bool IsListening() const { return true; } + + void KnockBack(const zeus::CVector3f&, CStateManager&, const CDamageInfo& info, EKnockBackType type, bool inDeferred, + float magnitude); + void Shock(CStateManager&, float, float); void TurnAround(CStateManager&, EStateMsg, float); @@ -252,7 +268,9 @@ public: void ProjectileAttack(CStateManager&, EStateMsg, float); - bool AnimOver(CStateManager&, float) { return x568_ == 4; } + bool Leash(CStateManager&, float); + + bool AnimOver(CStateManager&, float) { return x568_stateProg == 4; } bool SpotPlayer(CStateManager& mgr, float arg) { if (xa48_24_isAlert) @@ -260,17 +278,44 @@ public: return CPatterned::SpotPlayer(mgr, arg); } - bool InPosition(CStateManager&, float) { return (x8b8_ - GetTranslation()).magSquared() < 9.f; } + bool InPosition(CStateManager&, float) { return (x8b8_backupDestPos - GetTranslation()).magSquared() < 9.f; } + bool InMaxRange(CStateManager&, float); + bool InDetectionRange(CStateManager&, float); + bool ShotAt(CStateManager&, float) { return x400_24_hitByPlayerProjectile; } bool OffLine(CStateManager& mgr, float arg) { - SetPathFindMode(EPathFindMode::Zero); + SetPathFindMode(EPathFindMode::Normal); return PathShagged(mgr, arg); } + bool ShouldTurn(CStateManager& mgr, float arg); + + bool ShouldAttack(CStateManager& mgr, float arg); + + bool ShouldSpecialAttack(CStateManager& mgr, float arg); + + bool ShouldFire(CStateManager& mgr, float arg); + + bool TooClose(CStateManager& mgr, float arg); + + bool LineOfSight(CStateManager& mgr, float arg); + + bool AggressionCheck(CStateManager& mgr, float arg) { + return x400_25_alive && !xa48_29_hasBeenEnraged && x56c_shellState == EShellState::Destroyed; + } + + bool LostInterest(CStateManager& mgr, float arg); + bool Listen(const zeus::CVector3f&, EListenNoiseType); + + CPathFindSearch* GetSearchPath() { + return x8b4_pathFindMode == EPathFindMode::Normal ? &x6ec_pathSearch : &x7d0_approachPathSearch; + } + + CProjectileInfo* GetProjectileInfo() { return &x958_iceProjectile; } }; } // namespace urde::MP1 diff --git a/Runtime/MP1/World/CFlaahgra.cpp b/Runtime/MP1/World/CFlaahgra.cpp index fa0a40f53..8ee67c274 100644 --- a/Runtime/MP1/World/CFlaahgra.cpp +++ b/Runtime/MP1/World/CFlaahgra.cpp @@ -495,9 +495,9 @@ void CFlaahgra::SetupCollisionManagers(CStateManager& mgr) { AddMaterial(EMaterialTypes::ProjectilePassthrough, EMaterialTypes::Target, EMaterialTypes::Orbit, mgr); RemoveMaterial(EMaterialTypes::Solid, mgr); ModelData()->SetScale(oldScale); - x7a4_sphereCollision->AddMaterial(mgr, {EMaterialTypes::Unknown53, EMaterialTypes::CameraPassthrough}); - x79c_leftArmCollision->AddMaterial(mgr, {EMaterialTypes::Unknown53, EMaterialTypes::CameraPassthrough}); - x7a0_rightArmCollision->AddMaterial(mgr, {EMaterialTypes::Unknown53, EMaterialTypes::CameraPassthrough}); + x7a4_sphereCollision->AddMaterial(mgr, {EMaterialTypes::AIJoint, EMaterialTypes::CameraPassthrough}); + x79c_leftArmCollision->AddMaterial(mgr, {EMaterialTypes::AIJoint, EMaterialTypes::CameraPassthrough}); + x7a0_rightArmCollision->AddMaterial(mgr, {EMaterialTypes::AIJoint, EMaterialTypes::CameraPassthrough}); } void CFlaahgra::sub801ae980(CStateManager& mgr) { diff --git a/Runtime/Weapon/CFlameThrower.cpp b/Runtime/Weapon/CFlameThrower.cpp index a51ad0f99..f882c2554 100644 --- a/Runtime/Weapon/CFlameThrower.cpp +++ b/Runtime/Weapon/CFlameThrower.cpp @@ -17,7 +17,8 @@ const zeus::CVector3f CFlameThrower::kLightOffset(0, 3.f, 2.f); CFlameThrower::CFlameThrower(const TToken& wDesc, std::string_view name, EWeaponType wType, const CFlameInfo& flameInfo, const zeus::CTransform& xf, EMaterialTypes matType, const CDamageInfo& dInfo, TUniqueId uid, TAreaId aId, TUniqueId owner, - EProjectileAttrib attribs, CAssetId assetId1, s16 sId, CAssetId assetId2) + EProjectileAttrib attribs, CAssetId playerSteamTxtr, s16 playerHitSfx, + CAssetId playerIceTxtr) : CGameProjectile(false, wDesc, name, wType, xf, matType, dInfo, uid, aId, owner, kInvalidUniqueId, attribs, false, zeus::CVector3f(1.f), {}, -1, false) , x2e8_flameXf(xf) @@ -25,9 +26,9 @@ CFlameThrower::CFlameThrower(const TToken& wDesc, std::strin , x33c_flameDesc(g_SimplePool->GetObj({FOURCC('PART'), flameInfo.GetFlameFxId()})) , x348_flameGen(new CElementGen(x33c_flameDesc)) , x34c_flameWarp(176.f - float(flameInfo.GetLength()), xf.origin, bool(flameInfo.GetAttributes() & 0x4)) -, x3f4_playerSteamTxtr(assetId1) -, x3f8_playerHitSfx(sId) -, x3fc_playerIceTxtr(assetId2) +, x3f4_playerSteamTxtr(playerSteamTxtr) +, x3f8_playerHitSfx(playerHitSfx) +, x3fc_playerIceTxtr(playerIceTxtr) , x400_24_active(false) , x400_25_particlesActive(false) , x400_26_(!(flameInfo.GetAttributes() & 1)) diff --git a/Runtime/Weapon/CFlameThrower.hpp b/Runtime/Weapon/CFlameThrower.hpp index d23030671..f456312ab 100644 --- a/Runtime/Weapon/CFlameThrower.hpp +++ b/Runtime/Weapon/CFlameThrower.hpp @@ -49,7 +49,7 @@ public: CFlameThrower(const TToken& wDesc, std::string_view name, EWeaponType wType, const CFlameInfo& flameInfo, const zeus::CTransform& xf, EMaterialTypes matType, const CDamageInfo& dInfo, TUniqueId uid, TAreaId aId, TUniqueId owner, EProjectileAttrib attribs, - CAssetId w2, s16 sId, CAssetId w3); + CAssetId playerSteamTxtr, s16 playerHitSfx, CAssetId playerIceTxtr); void Accept(IVisitor& visitor); void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager&); diff --git a/Runtime/Weapon/CGunWeapon.cpp b/Runtime/Weapon/CGunWeapon.cpp index 1950ce21f..f08bc3c71 100644 --- a/Runtime/Weapon/CGunWeapon.cpp +++ b/Runtime/Weapon/CGunWeapon.cpp @@ -135,6 +135,7 @@ void CGunWeapon::UpdateGunFx(bool shotSmoke, float dt, const CStateManager& mgr, if (x218_26_loaded && x204_frozenEffect != EFrozenFxType::None) { if (x204_frozenEffect == EFrozenFxType::Thawed) { if (x1b8_frozenGenerator->IsSystemDeletable()) { + x204_frozenEffect = EFrozenFxType::None; x1b8_frozenGenerator.reset(); } else { x1b8_frozenGenerator->SetTranslation(xf.origin); diff --git a/Runtime/World/CPatterned.hpp b/Runtime/World/CPatterned.hpp index ff8a8f076..783e85e8f 100644 --- a/Runtime/World/CPatterned.hpp +++ b/Runtime/World/CPatterned.hpp @@ -362,9 +362,8 @@ public: const CKnockBackController& GetKnockBackController() const { return x460_knockBackController; } void SetupPlayerCollision(bool); void LaunchProjectile(const zeus::CTransform& gunXf, CStateManager& mgr, int maxAllowed, EProjectileAttrib attrib, - bool playerHoming, - const std::optional>& visorParticle, u16 visorSfx, - bool sendCollideMsg, const zeus::CVector3f& scale); + bool playerHoming, const std::optional>& visorParticle, + u16 visorSfx, bool sendCollideMsg, const zeus::CVector3f& scale); void DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt); void SetDestPos(const zeus::CVector3f& pos) { x2e0_destPos = pos; } diff --git a/Runtime/World/CStateMachine.cpp b/Runtime/World/CStateMachine.cpp index 3cb05b97e..c328a30f2 100644 --- a/Runtime/World/CStateMachine.cpp +++ b/Runtime/World/CStateMachine.cpp @@ -78,8 +78,10 @@ void CStateMachineState::Update(CStateManager& mgr, CAi& ai, float delta) { if (andPassed && state) { x4_state->CallFunc(mgr, ai, EStateMsg::Deactivate, 0.f); x4_state = state; +#ifndef NDEBUG printf("%04X %08X %s - %s %d\n", ai.GetUniqueId().Value(), ai.GetEditorId().id, ai.GetName().data(), state->xc_name, int(state - x0_machine->GetStateVector().data())); +#endif x8_time = 0.f; x18_24_codeTrigger = false; xc_random = mgr.GetActiveRandom()->Float();