#include "CPuddleToadGamma.hpp" #include "GameGlobalObjects.hpp" #include "CSimplePool.hpp" #include "CStateManager.hpp" #include "World/CPlayer.hpp" #include "Collision/CGameCollision.hpp" #include "Weapon/CBomb.hpp" #include "TCastTo.hpp" namespace urde::MP1 { const zeus::CVector3f CPuddleToadGamma::skBellyOffset(0.f, 0.1f, -.3f); CPuddleToadGamma::CPuddleToadGamma(TUniqueId uid, std::string_view name, EFlavorType flavor, const CEntityInfo& info, const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& aParms, float suckForceMultiplier, float suckAngle, float playerSuckRange, const zeus::CVector3f& localShootDir, float playerShootSpeed, float shouldAttackWaitTime, float spotPlayerWaitTime, const CDamageInfo& playerShootDamage, const CDamageInfo& dInfo2, CAssetId dcln) : CPatterned(ECharacter::PuddleToad, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Flyer, EColliderType::Zero, EBodyType::Restricted, aParms, EKnockBackVariant::Large) , x570_playerShootDamage(playerShootDamage) , x58c_(dInfo2) , x5a8_suckForceMultiplier(suckForceMultiplier) , x5ac_minSuckAngleProj(std::cos(zeus::degToRad(suckAngle * 0.5f))) , x5b0_playerSuckRange(playerSuckRange) , x5b4_localShootDir(localShootDir) , x5c0_playerShootSpeed(playerShootSpeed) , x5c4_shouldAttackWaitTime(shouldAttackWaitTime) , x5c8_spotPlayerWaitTime(spotPlayerWaitTime) , x5e8_24_playerInside(false) , x5e8_25_waitTimerActive(false) , x5e8_26_shotPlayer(false) { x401_26_disableMove = true; x460_knockBackController.SetEnableBurn(false); x460_knockBackController.SetEnableLaggedBurnDeath(false); x460_knockBackController.SetEnableShock(false); x460_knockBackController.SetX81_31(false); SetMovable(false); if (dcln.IsValid() && g_ResFactory->GetResourceTypeById(dcln) != 0) { TLockedToken container = g_SimplePool->GetObj({FOURCC('DCLN'), dcln}); x5e4_collisionTreePrim.reset(new CCollidableOBBTreeGroup(container.GetObj(), GetMaterialList())); } } void CPuddleToadGamma::SetSolid(CStateManager& mgr, bool solid) { if (solid) { AddMaterial(EMaterialTypes::Solid, mgr); RemoveMaterial(EMaterialTypes::NonSolidDamageable, mgr); } else { RemoveMaterial(EMaterialTypes::Solid, mgr); AddMaterial(EMaterialTypes::NonSolidDamageable, mgr); } } const CCollisionPrimitive* CPuddleToadGamma::GetCollisionPrimitive() const { if (!x5e4_collisionTreePrim) return CPhysicsActor::GetCollisionPrimitive(); return x5e4_collisionTreePrim.get(); } zeus::CTransform CPuddleToadGamma::GetPrimitiveTransform() const { zeus::CTransform xf = GetTransform(); xf.origin += GetPrimitiveOffset(); return xf; } void CPuddleToadGamma::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) { CPatterned::AcceptScriptMsg(msg, uid, mgr); if (msg == EScriptObjectMessage::Registered) { x450_bodyController->Activate(mgr); zeus::CTransform bellyXf = GetLctrTransform(mBellyLocatorName); zeus::CVector3f bellyOffset = GetTransform().rotate(skBellyOffset); x5d8_damageablePoint = x5cc_suckPoint = bellyXf.origin + bellyOffset; RemoveMaterial(EMaterialTypes::Target, EMaterialTypes::Orbit, mgr); AddMaterial(EMaterialTypes::Immovable, mgr); AddMaterial(EMaterialTypes::SolidCharacter); } } void CPuddleToadGamma::Think(float dt, CStateManager& mgr) { CPatterned::Think(dt, mgr); if (x5e8_25_waitTimerActive) x56c_waitTimer += dt; } std::optional CPuddleToadGamma::GetTouchBounds() const { if (!GetActive()) return {}; return (x5e4_collisionTreePrim ? x5e4_collisionTreePrim->CalculateAABox(GetTransform()) : GetBoundingBox()); } void CPuddleToadGamma::CenterPlayer(CStateManager& mgr, const zeus::CVector3f& pos, float dt) { zeus::CVector3f dir = (pos - mgr.GetPlayer().GetTranslation()).normalized(); mgr.GetPlayer().SetVelocityWR((1.f / (2.f * dt)) * dir); } const CDamageVulnerability* CPuddleToadGamma::GetDamageVulnerability(const zeus::CVector3f& pos, const zeus::CVector3f& dir, const CDamageInfo& dInfo) const { if (x5e8_24_playerInside && (x5d8_damageablePoint - pos).magSquared() < 4.f) return CAi::GetDamageVulnerability(); return &CDamageVulnerability::ImmuneVulnerabilty(); } void CPuddleToadGamma::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType type, float dt) { if (type == EUserEventType::Projectile) { ShootPlayer(mgr, x5c0_playerShootSpeed); return; } CPatterned::DoUserAnimEvent(mgr, node, type, dt); } bool CPuddleToadGamma::SpotPlayer(CStateManager&, float arg) { return x56c_waitTimer >= x5c8_spotPlayerWaitTime; } bool CPuddleToadGamma::ShouldAttack(CStateManager&, float) { return x56c_waitTimer >= x5c4_shouldAttackWaitTime; } bool CPuddleToadGamma::LostInterest(CStateManager& mgr, float) { zeus::CAABox box = *GetTouchBounds(); zeus::CAABox plBox = mgr.GetPlayer().GetBoundingBox(); return !box.intersects(plBox); } void CPuddleToadGamma::ShootPlayer(CStateManager& mgr, float speed) { zeus::CVector3f shootDir = x34_transform.rotate(x5b4_localShootDir.normalized()); mgr.GetPlayer().SetLeaveMorphBallAllowed(true); if (mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Morphed) { x5e8_26_shotPlayer = true; mgr.GetPlayer().Stop(); mgr.GetPlayer().SetVelocityWR(zeus::skZero3f); mgr.GetPlayer().ApplyImpulseWR(mgr.GetPlayer().GetMass() * shootDir * speed, {}); mgr.GetPlayer().SetMoveState(CPlayer::EPlayerMovementState::ApplyJump, mgr); mgr.ApplyDamage(GetUniqueId(), mgr.GetPlayer().GetUniqueId(), GetUniqueId(), x570_playerShootDamage, CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {}), zeus::skZero3f); mgr.GetPlayer().GetMorphBall()->SetAsProjectile(); rstl::reserved_vector nearList; mgr.BuildNearList(nearList, GetBoundingBox(), CMaterialFilter::MakeInclude({EMaterialTypes::Bomb}), this); for (TUniqueId id : nearList) { if (TCastToPtr bomb = mgr.ObjectById(id)) { bomb->SetVelocityWR((mgr.GetActiveRandom()->Float() * 5.f + 20.f) * shootDir); bomb->SetConstantAccelerationWR({0.f, 0.f, -CPhysicsActor::GravityConstant()}); } } } } bool CPuddleToadGamma::InAttackPosition(CStateManager& mgr, float) { return mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Morphed && PlayerInVortexArea(mgr); } static CMaterialFilter kSolidFilter = CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {EMaterialTypes::Character, EMaterialTypes::Player, EMaterialTypes::ProjectilePassthrough}); bool CPuddleToadGamma::PlayerInVortexArea(const CStateManager& mgr) { CPlayer& player = mgr.GetPlayer(); zeus::CTransform xf = GetLctrTransform(mMouthLocatorName); zeus::CVector3f playerOffset = player.GetTranslation() + zeus::CVector3f{0.f, 0.f, player.GetMorphBall()->GetBallRadius()}; zeus::CVector3f rotatedDir = GetTransform().rotate(zeus::skForward); zeus::CVector3f suckPointToPlayer = (playerOffset - (xf.origin - (1.f * rotatedDir))); float suckProj = suckPointToPlayer.normalized().dot(rotatedDir); float playerDist = suckPointToPlayer.magnitude(); float suckAngleProj = (player.GetTranslation() - (xf.origin - (4.f * rotatedDir))).normalized().dot(rotatedDir); if (playerDist > 2.f) { CRayCastResult result = mgr.RayStaticIntersection(suckPointToPlayer, 1.f / playerDist * suckPointToPlayer, playerDist - player.GetMorphBall()->GetBallRadius(), kSolidFilter); if (result.IsValid()) return false; } return (playerDist < x5b0_playerSuckRange && suckProj > 0.f && suckAngleProj > x5ac_minSuckAngleProj); } void CPuddleToadGamma::InActive(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { x450_bodyController->SetLocomotionType(pas::ELocomotionType::Relaxed); SetSolid(mgr, true); mgr.GetPlayer().SetLeaveMorphBallAllowed(true); x330_stateMachineState.SetDelay(2.f); } } void CPuddleToadGamma::Active(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { x450_bodyController->SetLocomotionType(pas::ELocomotionType::Lurk); zeus::CTransform xf = GetLctrTransform(mBellyLocatorName); x5cc_suckPoint = xf.origin + GetTransform().rotate(skBellyOffset); x56c_waitTimer = 0.f; x5e8_25_waitTimerActive = true; SetSolid(mgr, true); mgr.GetPlayer().SetLeaveMorphBallAllowed(true); } else if (msg == EStateMsg::Deactivate) { x5e8_25_waitTimerActive = false; } } void CPuddleToadGamma::Suck(CStateManager& mgr, EStateMsg msg, float arg) { if (msg == EStateMsg::Activate) { SetSolid(mgr, false); mgr.GetPlayer().SetLeaveMorphBallAllowed(false); mgr.GetPlayer().GetMorphBall()->DisableHalfPipeStatus(); } else if (msg == EStateMsg::Update) { if (x568_stateProg == 0) { if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::LoopReaction) { x568_stateProg = 1; return; } x450_bodyController->GetCommandMgr().DeliverCmd(CBCLoopReactionCmd(pas::EReactionType::Zero)); } else if (x568_stateProg == 1) SuckPlayer(mgr, arg); } else if (msg == EStateMsg::Deactivate) { x450_bodyController->GetCommandMgr().DeliverCmd(CBodyStateCmd(EBodyStateCmd::ExitState)); } } void CPuddleToadGamma::SuckPlayer(CStateManager& mgr, float arg) { CPlayer& player = mgr.GetPlayer(); if (player.GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed) return; zeus::CVector3f posDiff = player.GetTranslation() - x5cc_suckPoint; float posMag = posDiff.magnitude(); if (posMag < 3.f) { player.Stop(); CenterPlayer(mgr, x5cc_suckPoint, arg); return; } float forceMag = x5a8_suckForceMultiplier * (x5b0_playerSuckRange / (posMag * posMag)); zeus::CVector3f force = forceMag * (player.GetMass() * -posDiff); player.ApplyForceWR(force, zeus::CAxisAngle()); } void CPuddleToadGamma::Attack(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { mgr.GetPlayer().Stop(); mgr.GetPlayer().SetVelocityWR({}); x450_bodyController->GetCommandMgr().DeliverCmd(CBCMeleeAttackCmd(pas::ESeverity::One)); x5e8_26_shotPlayer = false; mgr.GetPlayer().GetMorphBall()->SetBombJumpState(CMorphBall::EBombJumpState::BombJumpDisabled); } else if (msg == EStateMsg::Update) { if (!x5e8_26_shotPlayer) { zeus::CTransform xf = GetLctrTransform(mBellyLocatorName); x5cc_suckPoint = xf.origin + GetTransform().rotate(skBellyOffset); SetPlayerPosition(mgr, x5cc_suckPoint); } else if (LostInterest(mgr, 0.f)) SetSolid(mgr, true); } else if (msg == EStateMsg::Deactivate) { SetSolid(mgr, true); mgr.GetPlayer().SetLeaveMorphBallAllowed(true); mgr.GetPlayer().GetMorphBall()->SetBombJumpState(CMorphBall::EBombJumpState::BombJumpAvailable); x5e8_24_playerInside = false; } } void CPuddleToadGamma::SetPlayerPosition(CStateManager& mgr, const zeus::CVector3f& targetPos) { float preThinkDt = x500_preThinkDt; CPlayer& player = mgr.GetPlayer(); player.Stop(); player.SetVelocityWR({}); bool hadPlayerMaterial = player.GetMaterialList().HasMaterial(EMaterialTypes::Player); if (hadPlayerMaterial) player.RemoveMaterial(EMaterialTypes::GroundCollider, mgr); player.RemoveMaterial(EMaterialTypes::Player, mgr); bool hadSolidMaterial = GetMaterialList().HasMaterial(EMaterialTypes::Solid); if (hadSolidMaterial) RemoveMaterial(EMaterialTypes::Solid, mgr); CPhysicsState physState = player.GetPhysicsState(); player.Stop(); player.MoveToWR(targetPos, preThinkDt); CGameCollision::Move(mgr, player, preThinkDt, nullptr); physState.SetTranslation(player.GetTranslation()); player.SetPhysicsState(physState); if (hadPlayerMaterial) player.AddMaterial(EMaterialTypes::GroundCollider, mgr); player.AddMaterial(EMaterialTypes::Player, mgr); if (hadSolidMaterial) AddMaterial(EMaterialTypes::Solid, mgr); } bool CPuddleToadGamma::Inside(CStateManager& mgr, float) { if (mgr.GetPlayer().GetMorphballTransitionState() != CPlayer::EPlayerMorphBallState::Morphed) return false; zeus::CVector3f posDiff = mgr.GetPlayer().GetTranslation() - x5cc_suckPoint; return posDiff.dot(GetTransform().frontVector()) <= 0.f && posDiff.magSquared() < 2.f; } void CPuddleToadGamma::Crouch(CStateManager& mgr, EStateMsg msg, float) { if (msg == EStateMsg::Activate) { x568_stateProg = 0; x56c_waitTimer = 0.f; x5e8_25_waitTimerActive = true; x5e8_24_playerInside = true; mgr.GetPlayer().Stop(); mgr.GetPlayer().SetVelocityWR({}); SendScriptMsgs(EScriptObjectState::Inside, mgr, EScriptObjectMessage::None); if (!mgr.GetPlayer().AttachActorToPlayer(GetUniqueId(), false)) x56c_waitTimer = 100.f; SetSolid(mgr, false); mgr.GetPlayer().SetLeaveMorphBallAllowed(false); mgr.GetPlayer().GetMorphBall()->DisableHalfPipeStatus(); SetSolid(mgr, false); } else if (msg == EStateMsg::Update) { zeus::CTransform xf = GetLctrTransform(mBellyLocatorName); x5cc_suckPoint = xf.origin + GetTransform().rotate(skBellyOffset); SetPlayerPosition(mgr, x5cc_suckPoint); if (x568_stateProg == 0) { if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Locomotion) x568_stateProg = 1; else x450_bodyController->SetLocomotionType(pas::ELocomotionType::Crouch); } } else if (msg == EStateMsg::Deactivate) { if (mgr.GetPlayer().GetAttachedActor() == GetUniqueId()) mgr.GetPlayer().DetachActorFromPlayer(); mgr.GetPlayer().SetLeaveMorphBallAllowed(true); x5e8_25_waitTimerActive = false; } } } // namespace urde::MP1