2
0
mirror of https://github.com/AxioDL/metaforce.git synced 2025-06-06 06:33:37 +00:00
metaforce/Runtime/MP1/World/CNewIntroBoss.cpp
Lioncash cabbfcc320 CActor: Make AddToRenderer() non-const
This member function alters instance state in a few implementations, so
it shouldn't be made const.

The state manager parameter also shouldn't be const. Retrieved data
from the post constructed instance is further modified in some
implementations. This removes the constness on this parameter in order
to fix more const_cast usages in a follow-up change.
2020-04-06 00:52:10 -04:00

433 lines
18 KiB
C++

#include "Runtime/MP1/World/CNewIntroBoss.hpp"
#include "Runtime/CStateManager.hpp"
#include "Runtime/Character/CCharLayoutInfo.hpp"
#include "Runtime/Collision/CCollisionActor.hpp"
#include "Runtime/Collision/CCollisionActorManager.hpp"
#include "Runtime/Collision/CGameCollision.hpp"
#include "Runtime/Collision/CJointCollisionDescription.hpp"
#include "Runtime/Weapon/CPlasmaProjectile.hpp"
#include "Runtime/World/CPlayer.hpp"
#include "TCastTo.hpp" // Generated file, do not modify include path
namespace urde::MP1 {
CNewIntroBoss::CNewIntroBoss(TUniqueId uid, std::string_view name, const CEntityInfo& info, const zeus::CTransform& xf,
CModelData&& mData, const CPatternedInfo& pInfo, const CActorParameters& actParms,
float minTurnAngle, CAssetId projectile, const CDamageInfo& dInfo,
CAssetId beamContactFxId, CAssetId beamPulseFxId, CAssetId beamTextureId,
CAssetId beamGlowTextureId)
: CPatterned(ECharacter::NewIntroBoss, uid, name, EFlavorType::Zero, info, xf, std::move(mData), pInfo,
EMovementType::Flyer, EColliderType::One, EBodyType::Restricted, actParms, EKnockBackVariant::Medium)
, x570_minTurnAngle(minTurnAngle)
, x574_boneTracking(*GetModelData()->GetAnimationData(), "Head_1"sv, zeus::degToRad(80.f), zeus::degToRad(180.f),
EBoneTrackingFlags::None)
, x5ac_projectileInfo(projectile, dInfo)
, x5f0_beamContactFxId(beamContactFxId)
, x5f4_beamPulseFxId(beamPulseFxId)
, x5f8_beamTextureId(beamTextureId)
, x5fc_beamGlowTextureId(beamGlowTextureId)
, x644_initialXf(xf) {
x5ac_projectileInfo.Token().Lock();
x574_boneTracking.SetActive(true);
}
void CNewIntroBoss::Accept(IVisitor& visitor) { visitor.Visit(this); }
static const SSphereJointInfo skSphereJoints[] = {{"Head_1", 1.5f}, {"Tail_1", 1.5f}};
static const SOBBJointInfo skOBBJoints[] = {
{"Pelvis", "Spine_3", {4.f, 1.f, 4.f}},
{"Spine_3", "Tail_1", {2.f, 1.f, 2.f}},
{"Tail_1", "Tail_2", {1.f, 1.f, 1.f}},
{"Tail_2", "Tail_3", {1.f, 1.f, 1.f}},
{"Tail_3", "Tail_4", {1.f, 1.f, 1.f}},
{"R_shoulder_front", "R_elbow_front", {.5f, .5f, .5f}},
{"R_elbow_front", "R_wrist_front", {.5f, .5f, .5f}},
{"L_shoulder_front", "L_elbow_front", {.5f, .5f, .5f}},
{"L_elbow_front", "L_wrist_front", {.5f, .5f, .5f}},
{"R_shoulder_back", "R_elbow_back", {.5f, .5f, .5f}},
{"R_elbow_back", "R_wrist_back", {.5f, .5f, .5f}},
{"L_shoulder_back", "L_elbow_back", {.5f, .5f, .5f}},
{"L_elbow_back", "L_wrist_back", {.5f, .5f, .5f}},
};
void CNewIntroBoss::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
if (msg == EScriptObjectMessage::Registered) {
RemoveMaterial(EMaterialTypes::Solid, mgr);
RemoveMaterial(EMaterialTypes::Target, mgr);
RemoveMaterial(EMaterialTypes::Orbit, mgr);
RemoveMaterial(EMaterialTypes::Occluder, mgr);
x450_bodyController->Activate(mgr);
if (x5d4_stage1Projectile == kInvalidUniqueId) {
CBeamInfo stage1BeamInfo(3, x5f0_beamContactFxId, x5f4_beamPulseFxId, x5f8_beamTextureId, x5fc_beamGlowTextureId,
50, 1.f, 1.f, 1.5f, 20.f, 1.f, 4.f, 8.f, zeus::skYellow,
zeus::CColor(0.1098f, 0.5764f, 0.1592f), 150.f);
CBeamInfo stage2BeamInfo(3, x5f0_beamContactFxId, x5f4_beamPulseFxId, x5f8_beamTextureId, x5fc_beamGlowTextureId,
50, 1.f, 1.f, 2.f, 20.f, 1.f, 4.f, 8.f, zeus::skYellow,
zeus::CColor(0.1098f, 0.5764f, 0.1592f), 150.f);
x5d4_stage1Projectile = mgr.AllocateUniqueId();
x5d6_stage2Projectile = mgr.AllocateUniqueId();
x5d8_stage3Projectile = mgr.AllocateUniqueId();
CPlasmaProjectile* stage1Projectile =
new CPlasmaProjectile(x5ac_projectileInfo.Token(), "IntroBoss_Beam"sv, EWeaponType::AI, stage1BeamInfo, {},
EMaterialTypes::Character, x5ac_projectileInfo.GetDamage(), x5d4_stage1Projectile,
GetAreaIdAlways(), GetUniqueId(), {}, true, EProjectileAttrib::KeepInCinematic);
CPlasmaProjectile* stage2Projectile =
new CPlasmaProjectile(x5ac_projectileInfo.Token(), "IntroBoss_Beam_Stage2"sv, EWeaponType::AI, stage2BeamInfo,
{}, EMaterialTypes::Character, x5ac_projectileInfo.GetDamage(), x5d6_stage2Projectile,
GetAreaIdAlways(), GetUniqueId(), {}, true, EProjectileAttrib::KeepInCinematic);
CPlasmaProjectile* stage3Projectile =
new CPlasmaProjectile(x5ac_projectileInfo.Token(), "IntroBoss_Beam_Stage2"sv, EWeaponType::AI, stage2BeamInfo,
{}, EMaterialTypes::Character, x5ac_projectileInfo.GetDamage(), x5d8_stage3Projectile,
GetAreaIdAlways(), GetUniqueId(), {}, true, EProjectileAttrib::KeepInCinematic);
mgr.AddObject(stage1Projectile);
mgr.AddObject(stage2Projectile);
mgr.AddObject(stage3Projectile);
x676_curProjectile = x5d4_stage1Projectile;
}
std::vector<CJointCollisionDescription> jointCollisions;
jointCollisions.reserve(15);
const CAnimData* animData = GetModelData()->GetAnimationData();
for (const SSphereJointInfo& joint : skSphereJoints) {
CSegId seg = animData->GetLocatorSegId(joint.name);
jointCollisions.push_back(CJointCollisionDescription::SphereCollision(seg, joint.radius, joint.name, 0.001f));
}
for (const SOBBJointInfo& joint : skOBBJoints) {
CSegId from = animData->GetLocatorSegId(joint.from);
CSegId to = animData->GetLocatorSegId(joint.to);
jointCollisions.push_back(CJointCollisionDescription::OBBAutoSizeCollision(
from, to, joint.bounds, CJointCollisionDescription::EOrientationType::One, joint.from, 0.001f));
}
x5ec_collisionManager.reset(
new CCollisionActorManager(mgr, GetUniqueId(), GetAreaIdAlways(), jointCollisions, GetActive()));
x640_initialHp = GetHealthInfo(mgr)->GetHP();
for (u32 i = 0; i < x5ec_collisionManager->GetNumCollisionActors(); ++i) {
const CJointCollisionDescription& desc = x5ec_collisionManager->GetCollisionDescFromIndex(i);
TCastToPtr<CCollisionActor> colAct = mgr.ObjectById(desc.GetCollisionActorId());
if (desc.GetName() == skSphereJoints[0].name) {
x600_headActor = desc.GetCollisionActorId();
if (colAct) {
CHealthInfo* thisHealthInfo = HealthInfo(mgr);
CHealthInfo* colHealthInfo = colAct->HealthInfo(mgr);
*colHealthInfo = *thisHealthInfo;
colAct->SetDamageVulnerability(*GetDamageVulnerability());
colAct->RemoveMaterial(EMaterialTypes::Orbit, mgr);
}
} else if (desc.GetName() == skOBBJoints[0].from) {
x602_pelvisActor = desc.GetCollisionActorId();
if (colAct) {
CHealthInfo* thisHealthInfo = HealthInfo(mgr);
CHealthInfo* colHealthInfo = colAct->HealthInfo(mgr);
*colHealthInfo = *thisHealthInfo;
colAct->SetDamageVulnerability(CDamageVulnerability::NormalVulnerabilty());
colAct->AddMaterial(EMaterialTypes::Orbit, mgr);
MoveScannableObjectInfoToActor(colAct, mgr);
}
} else
colAct->RemoveMaterial(EMaterialTypes::Orbit, mgr);
}
} else if (msg == EScriptObjectMessage::Deleted) {
DeleteBeam(mgr);
x5ec_collisionManager->Destroy(mgr);
} else if (msg == EScriptObjectMessage::Damage) {
if (uid == x600_headActor || uid == x602_pelvisActor)
TakeDamage({}, 0.f);
}
bool active = GetActive();
CPatterned::AcceptScriptMsg(msg, uid, mgr);
if (active == GetActive())
return;
if (x5ec_collisionManager)
x5ec_collisionManager->SetActive(mgr, GetActive());
x63c_attackTime = 8.f;
}
pas::ELocomotionType CNewIntroBoss::GetLocoForHealth(const CStateManager& mgr) const {
float hp = GetHealthInfo(mgr)->GetHP();
if (hp > .66f * x640_initialHp)
return pas::ELocomotionType::Relaxed;
else if (hp > .33f * x640_initialHp)
return pas::ELocomotionType::Lurk;
return pas::ELocomotionType::Combat;
}
void CNewIntroBoss::OnScanStateChanged(EScanState state, CStateManager& mgr) {
CPatterned::OnScanStateChanged(state, mgr);
if (state != EScanState::Done)
return;
TCastToPtr<CCollisionActor> colAct = mgr.ObjectById(x600_headActor);
if (colAct)
colAct->AddMaterial(EMaterialTypes::Orbit, mgr);
colAct = mgr.ObjectById(x602_pelvisActor);
if (colAct)
colAct->RemoveMaterial(EMaterialTypes::Orbit, mgr);
}
void CNewIntroBoss::DeleteBeam(CStateManager& mgr) {
if (x5d4_stage1Projectile != kInvalidUniqueId) {
mgr.FreeScriptObject(x5d4_stage1Projectile);
x5d4_stage1Projectile = kInvalidUniqueId;
}
if (x5d6_stage2Projectile != kInvalidUniqueId) {
mgr.FreeScriptObject(x5d6_stage2Projectile);
x5d6_stage2Projectile = kInvalidUniqueId;
}
if (x5d8_stage3Projectile != kInvalidUniqueId) {
mgr.FreeScriptObject(x5d8_stage3Projectile);
x5d8_stage3Projectile = kInvalidUniqueId;
}
StopRumble(mgr);
}
void CNewIntroBoss::StopRumble(CStateManager& mgr) {
if (x674_rumbleVoice == -1)
return;
mgr.GetRumbleManager().StopRumble(x674_rumbleVoice);
x674_rumbleVoice = -1;
}
void CNewIntroBoss::Think(float dt, CStateManager& mgr) {
CPatterned::Think(dt, mgr);
if (x638_ < 0.2f)
x638_ += dt;
if (x400_25_alive) {
x574_boneTracking.SetTargetPosition(x62c_targetPos + zeus::CVector3f{0.f, 0.f, 10.f});
x574_boneTracking.Update(dt);
}
if (x63c_attackTime > 0.f)
x63c_attackTime -= dt;
GetModelData()->GetAnimationData()->PreRender();
if (x400_25_alive)
x574_boneTracking.PreRender(mgr, *GetModelData()->GetAnimationData(), x34_transform, GetModelData()->GetScale(),
*x450_bodyController);
x5ec_collisionManager->Update(dt, mgr, CCollisionActorManager::EUpdateOptions::ObjectSpace);
CPlasmaProjectile* curProjectile = static_cast<CPlasmaProjectile*>(mgr.ObjectById(x676_curProjectile));
if (curProjectile && curProjectile->GetActive()) {
x628_firingTime += dt;
zeus::CTransform xf = GetLctrTransform(x5dc_damageLocator);
if (x400_25_alive) {
zeus::CQuaternion clampedQuat = zeus::CQuaternion::clampedRotateTo(
xf.frontVector(),
(x610_lookPos + (zeus::min(x628_firingTime / 1.5f, 1.f) * (x61c_startPlayerPos - x610_lookPos))) - xf.origin,
zeus::CRelAngle::FromDegrees(30.f));
zeus::CTransform newXf = clampedQuat.toTransform() * xf.getRotation();
newXf.origin = xf.origin;
curProjectile->UpdateFx(newXf, dt, mgr);
} else
curProjectile->UpdateFx(xf, dt, mgr);
}
TCastToPtr<CCollisionActor> headAct = mgr.ObjectById(x600_headActor);
TCastToPtr<CCollisionActor> pelvisAct = mgr.ObjectById(x602_pelvisActor);
if (headAct && pelvisAct) {
CHealthInfo* pelvisHealth = pelvisAct->HealthInfo(mgr);
CHealthInfo* headHealth = headAct->HealthInfo(mgr);
if (headHealth->GetHP() < pelvisHealth->GetHP()) {
*HealthInfo(mgr) = *headHealth;
*pelvisAct->HealthInfo(mgr) = *headHealth;
} else {
*HealthInfo(mgr) = *pelvisHealth;
*headAct->HealthInfo(mgr) = *pelvisHealth;
}
}
if (HealthInfo(mgr)->GetHP() <= 0.f && x400_25_alive) {
if (curProjectile)
curProjectile->ResetBeam(mgr, true);
x450_bodyController->SetPlaybackRate(1.f);
SetTransform(x644_initialXf);
StopRumble(mgr);
Death(mgr, GetTransform().frontVector(), EScriptObjectState::DeathRattle);
}
}
void CNewIntroBoss::DoUserAnimEvent(CStateManager& mgr, const CInt32POINode& node, EUserEventType event, float dt) {
if (event == EUserEventType::DamageOn) {
x5dc_damageLocator = node.GetLocatorName();
zeus::CTransform xf = GetLctrTransform(x5dc_damageLocator);
zeus::CVector3f playerPos = PlayerPos(mgr);
x604_predictedPlayerPos = x610_lookPos = x62c_targetPos = playerPos;
x61c_startPlayerPos = playerPos;
x628_firingTime = 0.f;
if (GetLocoForHealth(mgr) == pas::ELocomotionType::Combat)
x676_curProjectile = x5d8_stage3Projectile;
else if (GetLocoForHealth(mgr) == pas::ELocomotionType::Lurk)
x676_curProjectile = x5d6_stage2Projectile;
else
x676_curProjectile = x5d4_stage1Projectile;
if (CPlasmaProjectile* projectile = static_cast<CPlasmaProjectile*>(mgr.ObjectById(x676_curProjectile))) {
if (!projectile->GetActive()) {
projectile->Fire(zeus::lookAt(xf.origin, x610_lookPos), mgr, false);
if (x674_rumbleVoice == -1)
x674_rumbleVoice =
mgr.GetRumbleManager().Rumble(mgr, ERumbleFxId::IntroBossProjectile, 1.f, ERumblePriority::Two);
}
}
} else if (event == EUserEventType::DamageOff) {
if (CPlasmaProjectile* projectile = static_cast<CPlasmaProjectile*>(mgr.ObjectById(x676_curProjectile)))
projectile->ResetBeam(mgr, false);
StopRumble(mgr);
x63c_attackTime = GetNextAttackTime(mgr);
SendScriptMsgs(EScriptObjectState::Attack, mgr, EScriptObjectMessage::None);
} else {
CPatterned::DoUserAnimEvent(mgr, node, event, dt);
}
}
void CNewIntroBoss::AddToRenderer(const zeus::CFrustum&, CStateManager& mgr) { EnsureRendered(mgr); }
float CNewIntroBoss::GetNextAttackTime(CStateManager& mgr) const {
float attackTime = 2.f * mgr.GetActiveRandom()->Float() + 6.f;
float hp = GetHealthInfo(mgr)->GetHP();
if (hp > .66 * x640_initialHp)
return attackTime;
else if (hp > .33 * x640_initialHp)
return attackTime - (0.4125f * attackTime);
return attackTime - (0.825f * attackTime);
}
zeus::CVector3f CNewIntroBoss::PlayerPos(const CStateManager& mgr) const {
CRayCastResult result = CGameCollision::RayStaticIntersection(
mgr, mgr.GetPlayer().GetTranslation() + zeus::CVector3f{0.f, 0.f, mgr.GetPlayer().GetEyeHeight() * 0.5f},
zeus::skDown, 30.f, CMaterialFilter::MakeInclude({EMaterialTypes::Solid}));
if (result.IsValid())
return result.GetPoint() + zeus::CVector3f{0.f, 0.f, (mgr.GetPlayer().GetEyeHeight() * 0.5f) + 0.2f};
return mgr.GetPlayer().GetTranslation() + zeus::CVector3f{0.f, 0.f, (mgr.GetPlayer().GetEyeHeight() * 0.5f) + 0.2f};
}
bool CNewIntroBoss::ShouldTurn(CStateManager& mgr, float dt) {
zeus::CVector3f playerVel = 1.f * mgr.GetPlayer().GetVelocity();
zeus::CVector3f playerPos = PlayerPos(mgr);
x604_predictedPlayerPos = playerPos + playerVel;
zeus::CVector2f diffPos = (x604_predictedPlayerPos - GetTranslation()).toVec2f();
return zeus::CVector2f::getAngleDiff(GetTransform().frontVector().toVec2f(), diffPos) >
zeus::degToRad(x570_minTurnAngle);
}
bool CNewIntroBoss::ShouldAttack(CStateManager& mgr, float dt) {
if (x63c_attackTime > 0.f)
return false;
if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Turn)
return false;
return !ShouldTurn(mgr, dt);
}
bool CNewIntroBoss::AIStage(CStateManager& mgr, float) { return x568_locomotion != GetLocoForHealth(mgr); }
bool CNewIntroBoss::AnimOver(urde::CStateManager&, float) { return x56c_stateProg == 3; }
void CNewIntroBoss::Generate(CStateManager& mgr, EStateMsg msg, float dt) {
if (msg == EStateMsg::Activate) {
x56c_stateProg = 0;
x568_locomotion = GetLocoForHealth(mgr);
SendScriptMsgs(EScriptObjectState::Entered, mgr, EScriptObjectMessage::None);
} else if (msg == EStateMsg::Update) {
if (x56c_stateProg == 0) {
if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::Generate) {
x56c_stateProg = 2;
return;
}
x450_bodyController->GetCommandMgr().DeliverCmd(CBCGenerateCmd(GetGenerateForHealth(mgr)));
} else if (x56c_stateProg == 2) {
if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::Generate) {
x56c_stateProg = 3;
SendScriptMsgs(EScriptObjectState::Exited, mgr, EScriptObjectMessage::None);
}
}
}
}
pas::EGenerateType CNewIntroBoss::GetGenerateForHealth(const CStateManager& mgr) const {
return GetHealthInfo(mgr)->GetHP() > 0.33f * x640_initialHp ? pas::EGenerateType::Three : pas::EGenerateType::Four;
}
bool CNewIntroBoss::InAttackPosition(CStateManager& mgr, float dt) {
return x330_stateMachineState.GetTime() > 0.25f && x678_ &&
x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::Turn &&
!ShouldTurn(mgr, dt);
}
void CNewIntroBoss::Attack(CStateManager& mgr, EStateMsg msg, float dt) {
if (msg == EStateMsg::Activate)
x56c_stateProg = 0;
else if (msg == EStateMsg::Update) {
if (x56c_stateProg == 0) {
if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() == pas::EAnimationState::ProjectileAttack)
x56c_stateProg = 2;
else {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCProjectileAttackCmd(pas::ESeverity::One, mgr.GetPlayer().GetTranslation(), false));
}
} else if (x56c_stateProg == 2) {
if (x450_bodyController->GetBodyStateInfo().GetCurrentStateId() != pas::EAnimationState::ProjectileAttack) {
x56c_stateProg = 3;
x638_ = 0.f;
}
if (const CPlasmaProjectile* proj =
static_cast<const CPlasmaProjectile*>(mgr.GetObjectById(x676_curProjectile))) {
if (!proj->GetActive())
x62c_targetPos = mgr.GetPlayer().GetTranslation();
}
}
} else if (msg == EStateMsg::Deactivate) {
if (GetLocoForHealth(mgr) == pas::ELocomotionType::Lurk || GetLocoForHealth(mgr) == pas::ELocomotionType::Combat)
x678_ ^= 1;
else
x678_ = false;
}
}
void CNewIntroBoss::Patrol(CStateManager& mgr, EStateMsg msg, float dt) {
if (msg == EStateMsg::Activate || msg == EStateMsg::Update) {
x450_bodyController->SetLocomotionType(x568_locomotion);
if (x638_ > 0.2f)
x62c_targetPos = PlayerPos(mgr);
else
x62c_targetPos = x610_lookPos + ((x638_ / 0.2f) * (PlayerPos(mgr) - x610_lookPos));
if (ShouldTurn(mgr, 0.f)) {
x450_bodyController->GetCommandMgr().DeliverCmd(
CBCLocomotionCmd({}, (x604_predictedPlayerPos - GetTranslation()).normalized(), 1.f));
}
}
}
} // namespace urde::MP1