2
0
mirror of https://github.com/AxioDL/metaforce.git synced 2025-05-16 20:31:29 +00:00
metaforce/Runtime/MP1/World/CGrenadeLauncher.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

336 lines
13 KiB
C++

#include "Runtime/MP1/World/CGrenadeLauncher.hpp"
#include "Runtime/Character/CPASAnimParm.hpp"
#include "Runtime/Character/CPASAnimParmData.hpp"
#include "Runtime/CSimplePool.hpp"
#include "Runtime/CStateManager.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/Weapon/CGameProjectile.hpp"
#include "Runtime/World/CExplosion.hpp"
#include "Runtime/World/CPatterned.hpp"
#include "Runtime/World/CPlayer.hpp"
namespace urde::MP1 {
CGrenadeLauncher::CGrenadeLauncher(TUniqueId uid, std::string_view name, const CEntityInfo& info,
const zeus::CTransform& xf, CModelData&& mData, const zeus::CAABox& bounds,
const CHealthInfo& healthInfo, const CDamageVulnerability& vulnerability,
const CActorParameters& actParams, TUniqueId parentId,
const SGrenadeLauncherData& data, float f1)
: CPhysicsActor(uid, true, name, info, xf, std::move(mData), {EMaterialTypes::Character, EMaterialTypes::Solid}, bounds,
SMoverData{1000.f}, actParams, 0.3f, 0.1f)
, x25c_healthInfo(healthInfo)
, x264_vulnerability(vulnerability)
, x2cc_parentId(parentId)
, x2d0_data(data)
, x328_cSphere({{}, mData.GetScale().z()}, {EMaterialTypes::Character, EMaterialTypes::Solid})
, x350_grenadeActorParams(actParams)
, x3e8_thermalMag(actParams.GetThermalMag())
, x3f8_explodePlayerDistance(f1) {
if (data.GetExplosionGenDescId().IsValid()) {
x3b8_particleGenDesc = g_SimplePool->GetObj({SBIG('PART'), data.GetExplosionGenDescId()});
}
GetModelData()->EnableLooping(true);
const CPASDatabase& pasDatabase = GetModelData()->GetAnimationData()->GetCharacterInfo().GetPASDatabase();
for (size_t i = 0; i < x3c8_animIds.size(); ++i) {
const auto result = pasDatabase.FindBestAnimation(CPASAnimParmData{22, CPASAnimParm::FromEnum(int(i))}, -1);
x3c8_animIds[i] = result.second;
}
}
zeus::CVector3f CGrenadeLauncher::GrenadeTarget(const CStateManager& mgr) {
const zeus::CVector3f aim = mgr.GetPlayer().GetAimPosition(mgr, 1.f);
if (mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed) {
return aim - zeus::CVector3f{0.f, 0.f, 0.5f * mgr.GetPlayer().GetEyeHeight()};
}
return aim;
}
void CGrenadeLauncher::CalculateGrenadeTrajectory(const zeus::CVector3f& target, const zeus::CVector3f& origin,
const SGrenadeTrajectoryInfo& info, float& angleOut,
float& velocityOut) {
float angle = info.GetAngleMin();
float velocity = info.GetVelocityMin();
float delta = std::max(0.01f, 0.1f * (info.GetAngleMax() - info.GetAngleMin()));
zeus::CVector3f dist = target - origin;
float distXYMag = dist.toVec2f().magnitude();
float velocityMinSq = info.GetVelocityMin() * info.GetVelocityMin();
float velocityMaxSq = info.GetVelocityMax() * info.GetVelocityMax();
float gravAdj = distXYMag * ((0.5f * CPhysicsActor::GravityConstant()) * distXYMag);
float currAngle = info.GetAngleMin();
float leastResult = FLT_MAX;
while (info.GetAngleMax() >= currAngle) {
float cos = std::cos(currAngle);
float sin = std::sin(currAngle);
float result = (distXYMag * (cos * sin) - (dist.z() * (cos * cos)));
if (result > FLT_EPSILON) {
float div = gravAdj / result;
if (velocityMinSq <= result && result <= velocityMaxSq) {
angle = currAngle;
velocity = std::sqrt(div);
break;
}
if (result <= velocityMaxSq) {
result = velocityMinSq - result;
} else {
result = result - velocityMaxSq;
}
if (result < leastResult) {
angle = currAngle;
velocity = std::sqrt(div);
leastResult = result;
}
}
currAngle += delta;
}
angleOut = angle;
velocityOut = velocity;
}
void CGrenadeLauncher::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
CActor::AcceptScriptMsg(msg, uid, mgr);
switch (msg) {
case EScriptObjectMessage::Start:
if (x2cc_parentId == uid && x258_started != 1) {
x258_started = 1;
sub_80230438();
}
break;
case EScriptObjectMessage::Stop:
if (x2cc_parentId == uid && x258_started != 0) {
x258_started = 0;
sub_80230438();
}
break;
case EScriptObjectMessage::Action:
if (x2cc_parentId == uid && x258_started == 1) {
x3fc_launchGrenade = true;
}
break;
case EScriptObjectMessage::Registered:
sub_80230438();
break;
case EScriptObjectMessage::Damage:
x3ec_damageTimer = 0.33f;
break;
default:
break;
}
}
void CGrenadeLauncher::AddToRenderer(const zeus::CFrustum& frustum, CStateManager& mgr) {
CActor::AddToRenderer(frustum, mgr);
}
std::optional<zeus::CAABox> CGrenadeLauncher::GetTouchBounds() const {
return x328_cSphere.CalculateAABox(GetTransform());
}
void CGrenadeLauncher::PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) {
if (x3f4_color3.a() == 1.f) {
xb4_drawFlags = CModelFlags{2, 0, 3, zeus::skWhite};
// Original code redundantly sets a() = 1.f
xb4_drawFlags.addColor = x3f4_color3;
} else {
xb4_drawFlags = CModelFlags{5, 0, 3, zeus::skWhite};
xb4_drawFlags.addColor = x3f4_color3;
}
CActor::PreRender(mgr, frustum);
}
void CGrenadeLauncher::Render(const CStateManager& mgr) const {
if (x3fd_visible) {
CPhysicsActor::Render(mgr);
}
}
void CGrenadeLauncher::Think(float dt, CStateManager& mgr) {
if (GetActive()) {
if (x3fc_launchGrenade) {
LaunchGrenade(mgr);
x3fc_launchGrenade = false;
}
UpdateCollision();
UpdateColor(dt);
sub_8022f9e0(mgr, dt);
UpdateDamageTime(dt);
const SAdvancementDeltas& deltas = CActor::UpdateAnimation(dt, mgr, true);
MoveToOR(deltas.x0_posDelta, dt);
RotateToOR(deltas.xc_rotDelta, dt);
TCastToPtr<CPatterned> parent = mgr.ObjectById(x2cc_parentId);
if (parent == nullptr || !parent->IsAlive() || parent->HealthInfo(mgr)->GetHP() <= 0.f) {
mgr.SendScriptMsg(parent, GetUniqueId(), EScriptObjectMessage::Damage);
CreateExplosion(mgr);
mgr.FreeScriptObject(GetUniqueId());
}
}
}
void CGrenadeLauncher::Touch(CActor& act, CStateManager& mgr) {
if (TCastToPtr<CGameProjectile> projectile = act) {
if (projectile->GetOwnerId() == mgr.GetPlayer().GetUniqueId() &&
GetDamageVulnerability()->WeaponHurts(CWeaponMode{projectile->GetType()}, false)) {
x348_shotTimer = 0.5f;
CEntity* parent = mgr.ObjectById(x2cc_parentId);
if (parent != nullptr) {
mgr.SendScriptMsg(parent, GetUniqueId(), EScriptObjectMessage::Touched);
}
}
}
}
void CGrenadeLauncher::UpdateCollision() {
x328_cSphere.SetSphereCenter(GetLocatorTransform("lockon_target_LCTR"sv).origin);
}
void CGrenadeLauncher::UpdateColor(float arg) {
if (x348_shotTimer > 0.f) {
x348_shotTimer = std::max(0.f, x348_shotTimer - arg);
x34c_color1 = zeus::CColor::lerp(zeus::skWhite, zeus::skRed, x348_shotTimer);
}
}
void CGrenadeLauncher::UpdateDamageTime(float arg) {
if (x3ec_damageTimer <= 0.f) {
xd0_damageMag = x3e8_thermalMag;
} else {
x3ec_damageTimer = std::max(0.f, x3ec_damageTimer - arg);
x3f4_color3 = zeus::CColor::lerp(zeus::skBlack, x3f0_color2, std::clamp(x3ec_damageTimer / 0.33f, 0.f, 1.f));
xd0_damageMag = 5.f * x3ec_damageTimer + x3e8_thermalMag;
}
}
void CGrenadeLauncher::CreateExplosion(CStateManager& mgr) {
if (!x3b8_particleGenDesc) {
return;
}
mgr.AddObject(new CExplosion(*x3b8_particleGenDesc, mgr.AllocateUniqueId(), true,
{GetAreaIdAlways(), CEntity::NullConnectionList}, "Grenade Launcher Explode Fx"sv,
GetTransform(), 0, GetModelData()->GetScale(), zeus::skWhite));
CSfxManager::SfxStart(x2d0_data.GetExplosionSfx(), 1.f, 1.f, false, 0x7f, false, kInvalidAreaId);
}
void CGrenadeLauncher::sub_8022f9e0(CStateManager& mgr, float dt) {
CModelData* modelData = GetModelData();
CAnimData* animData = nullptr;
if (modelData != nullptr && (animData = modelData->GetAnimationData()) != nullptr && x258_started == 1 && x3fe_) {
const zeus::CVector3f target = mgr.GetPlayer().GetAimPosition(mgr, 0.f) - GetTranslation();
const zeus::CVector3f rot = GetTransform().rotate({target.x(), target.y(), 0.f}); // TODO double check
if (rot.canBeNormalized()) {
constexpr float p36d = zeus::degToRad(36.476f);
constexpr float n45d = zeus::degToRad(-45.f);
constexpr float p45d = zeus::degToRad(45.f);
float l84 = p36d * std::clamp(std::atan2(rot.x(), rot.y()), n45d, p45d);
float l88 = std::clamp((0.25f * (l84 - x3d8_)) / dt, -3.f, 3.f);
float l8c = std::clamp((l88 - x3dc_) / dt, -10.f, 10.f);
x3dc_ += dt * l8c;
float l90 = p36d * std::clamp(std::atan2(rot.z(), rot.toVec2f().magnitude()), n45d, p45d);
l88 = std::clamp((0.25f * (l90 - x3e0_)) / dt, -3.f, 3.f);
l8c = std::clamp((l88 - x3e4_) / dt, -10.f, 10.f);
x3e4_ += dt * l8c;
float dVar7 = std::clamp(dt * x3dc_ + x3d8_, -0.5f, 0.5f);
float dVar8 = std::clamp(dt * x3e4_ + x3e0_, -0.5f, 0.5f);
if (dVar7 != x3d8_) {
if (std::abs(x3d8_) > 0.f && x3d8_ * dVar7 <= 0.f) {
animData->DelAdditiveAnimation(x3c8_animIds[x3d8_ >= 0.f ? 1 : 0]);
}
float weight = std::abs(dVar7);
if (weight > 0.f) {
animData->AddAdditiveAnimation(x3c8_animIds[dVar7 >= 0.f ? 1 : 0], weight, false, false);
}
}
if (dVar8 != x3e0_) {
if (std::abs(x3e0_) > 0.f && x3e0_ * dVar8 <= 0.f) {
animData->DelAdditiveAnimation(x3c8_animIds[x3e0_ <= 0.f ? 3 : 2]);
}
float weight = std::abs(dVar8);
if (weight > 0.f) {
animData->AddAdditiveAnimation(x3c8_animIds[dVar8 <= 0.f ? 3 : 2], weight, false, false);
}
}
x3d8_ = dVar7;
x3e0_ = dVar8;
}
} else {
if (x3d8_ != 0.f) {
if (animData != nullptr) { // Original code does not check
animData->DelAdditiveAnimation(x3c8_animIds[x3d8_ >= 0.f ? 1 : 0]);
}
x3d8_ = 0.f;
}
if (x3e0_ != 0.f) {
if (animData != nullptr) { // Original code does not check
animData->DelAdditiveAnimation(x3c8_animIds[x3e0_ <= 0.f ? 3 : 2]);
}
x3e0_ = 0.f;
}
}
}
void CGrenadeLauncher::sub_80230438() {
CModelData* modelData = GetModelData();
CAnimData* animData;
if (modelData == nullptr || (animData = modelData->GetAnimationData()) == nullptr || x258_started <= -1 ||
x258_started >= 2) {
return;
}
constexpr std::array arr{0, 3};
const auto anim = animData->GetCharacterInfo().GetPASDatabase().FindBestAnimation(
CPASAnimParmData{5, CPASAnimParm::FromEnum(0), CPASAnimParm::FromEnum(arr[x258_started])}, -1);
if (anim.first > 0.f) {
animData->SetAnimation({anim.second, -1, 1.f, true}, false);
modelData->EnableLooping(true);
}
}
void CGrenadeLauncher::LaunchGrenade(CStateManager& mgr) {
CModelData* modelData = GetModelData();
CAnimData* animData;
if (modelData == nullptr || (animData = modelData->GetAnimationData()) == nullptr) {
return;
}
const auto& anim = animData->GetCharacterInfo().GetPASDatabase().FindBestAnimation(CPASAnimParmData{23}, -1);
if (anim.first > 0.f) {
animData->AddAdditiveAnimation(anim.second, 1.f, false, true);
const zeus::CVector3f origin =
GetTranslation() + GetTransform().rotate(GetLocatorTransform("grenade_LCTR"sv).origin);
const zeus::CVector3f target = GrenadeTarget(mgr);
float angleOut = x2d0_data.GetGrenadeTrajectoryInfo().GetAngleMin();
float velocityOut = x2d0_data.GetGrenadeTrajectoryInfo().GetVelocityMin();
CalculateGrenadeTrajectory(target, origin, x2d0_data.GetGrenadeTrajectoryInfo(), angleOut, velocityOut);
zeus::CVector3f dist = target - origin;
dist.z() = 0.f;
const zeus::CVector3f front = GetTransform().frontVector();
if (dist.canBeNormalized()) {
dist.normalize();
} else {
dist = front;
}
constexpr float maxAngle = zeus::degToRad(45.f);
if (zeus::CVector3f::getAngleDiff(front, dist) > maxAngle) {
dist = zeus::CVector3f::slerp(front, dist, maxAngle);
}
const zeus::CVector3f look = zeus::CVector3f::slerp(dist, zeus::skUp, angleOut);
const zeus::CTransform xf = zeus::lookAt(origin, origin + look, zeus::skUp);
CModelData mData{CStaticRes{x2d0_data.GetGrenadeModelId(), GetModelData()->GetScale()}};
mgr.AddObject(new CBouncyGrenade(mgr.AllocateUniqueId(), "Bouncy Grenade"sv,
{GetAreaIdAlways(), CEntity::NullConnectionList}, xf, std::move(mData),
x350_grenadeActorParams, x2cc_parentId, x2d0_data.GetGrenadeData(), velocityOut,
x3f8_explodePlayerDistance));
}
}
} // namespace urde::MP1