mirror of https://github.com/AxioDL/metaforce.git
211 lines
8.5 KiB
C++
211 lines
8.5 KiB
C++
#include "Runtime/MP1/World/CBouncyGrenade.hpp"
|
|
|
|
#include "Runtime/CPlayerState.hpp"
|
|
#include "Runtime/CSimplePool.hpp"
|
|
#include "Runtime/CStateManager.hpp"
|
|
#include "Runtime/GameGlobalObjects.hpp"
|
|
#include "Runtime/Graphics/CBooRenderer.hpp"
|
|
#include "Runtime/World/CPlayer.hpp"
|
|
#include "Runtime/Collision/CCollisionActor.hpp"
|
|
|
|
namespace metaforce::MP1 {
|
|
CBouncyGrenade::CBouncyGrenade(TUniqueId uid, std::string_view name, const CEntityInfo& info,
|
|
const zeus::CTransform& xf, CModelData&& mData, const CActorParameters& actParams,
|
|
TUniqueId parentId, const SBouncyGrenadeData& data, float velocity,
|
|
float explodePlayerDistance)
|
|
: CPhysicsActor(uid, true, name, info, xf, std::move(mData), {EMaterialTypes::Projectile, EMaterialTypes::Solid},
|
|
mData.GetBounds(), SMoverData{data.GetUnkStruct().GetMass()}, actParams, 0.3f, 0.1f)
|
|
, x258_data(data)
|
|
, x294_numBounces(data.GetNumBounces())
|
|
, x298_parentId(parentId)
|
|
, x2a0_elementGenCombat(std::make_unique<CElementGen>(g_SimplePool->GetObj({'PART', data.GetElementGenId1()})))
|
|
, x2a4_elementGenXRay(std::make_unique<CElementGen>(g_SimplePool->GetObj({'PART', data.GetElementGenId2()})))
|
|
, x2a8_elementGenThermal(std::make_unique<CElementGen>(g_SimplePool->GetObj({'PART', data.GetElementGenId3()})))
|
|
, x2ac_elementGen4(std::make_unique<CElementGen>(g_SimplePool->GetObj({'PART', data.GetElementGenId4()})))
|
|
, x2b0_explodePlayerDistance(explodePlayerDistance) {
|
|
SetMomentumWR({0.f, 0.f, -GravityConstant() * GetMass()});
|
|
SetVelocityWR(velocity * xf.frontVector());
|
|
x2a0_elementGenCombat->SetParticleEmission(false);
|
|
x2a4_elementGenXRay->SetParticleEmission(false);
|
|
x2a8_elementGenThermal->SetParticleEmission(false);
|
|
x2ac_elementGen4->SetParticleEmission(true);
|
|
CMaterialFilter filter = GetMaterialFilter();
|
|
filter.ExcludeList().Add(EMaterialTypes::Character);
|
|
SetMaterialFilter(CMaterialFilter::MakeIncludeExclude(filter.IncludeList(), filter.ExcludeList()));
|
|
}
|
|
|
|
void CBouncyGrenade::AddToRenderer(const zeus::CFrustum& frustum, CStateManager& mgr) {
|
|
CActor::AddToRenderer(frustum, mgr);
|
|
if (!x2b4_24_exploded) {
|
|
g_Renderer->AddParticleGen(*x2ac_elementGen4);
|
|
return;
|
|
}
|
|
const auto visor = mgr.GetPlayerState()->GetActiveVisor(mgr);
|
|
if (visor == CPlayerState::EPlayerVisor::Combat || visor == CPlayerState::EPlayerVisor::Scan) {
|
|
g_Renderer->AddParticleGen(*x2a0_elementGenCombat);
|
|
} else if (visor == CPlayerState::EPlayerVisor::XRay || visor == CPlayerState::EPlayerVisor::Thermal) {
|
|
g_Renderer->AddParticleGen(*x2a8_elementGenThermal);
|
|
}
|
|
}
|
|
|
|
void CBouncyGrenade::CollidedWith(TUniqueId id, const CCollisionInfoList& list, CStateManager& mgr) {
|
|
constexpr auto matList = CMaterialList{
|
|
EMaterialTypes::Solid,
|
|
EMaterialTypes::Ceiling,
|
|
EMaterialTypes::Floor,
|
|
EMaterialTypes::Character,
|
|
};
|
|
|
|
bool shouldExplode = false;
|
|
if (x298_parentId != id) {
|
|
const CEntity* const entity = mgr.GetObjectById(id);
|
|
if (entity != nullptr) {
|
|
if (TCastToConstPtr<CCollisionActor> actor = entity) {
|
|
shouldExplode = actor->GetOwnerId() != x298_parentId;
|
|
} else {
|
|
shouldExplode = true;
|
|
}
|
|
}
|
|
}
|
|
if (shouldExplode) {
|
|
Explode(mgr, id);
|
|
} else {
|
|
for (const auto& info : list) {
|
|
if (info.GetMaterialLeft().SharesMaterials(matList)) {
|
|
if (x294_numBounces == 0) {
|
|
Explode(mgr, kInvalidUniqueId);
|
|
} else {
|
|
const zeus::CVector3f* normal = &info.GetNormalLeft();
|
|
if (GetVelocity().dot(info.GetNormalLeft()) > 0.f) {
|
|
normal = &info.GetNormalRight();
|
|
}
|
|
const zeus::CVector3f impulse =
|
|
(x258_data.GetUnkStruct().GetSpeed() * GetConstantForce().magnitude()) * *normal;
|
|
const zeus::CVector3f angle = -x258_data.GetUnkStruct().GetSpeed() * GetAngularMomentum();
|
|
ApplyImpulseWR(impulse, angle);
|
|
CSfxManager::AddEmitter(x258_data.GetBounceSfx(), GetTranslation(), zeus::skUp, false, false, 0x7f,
|
|
GetAreaIdAlways());
|
|
--x294_numBounces;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
CPhysicsActor::CollidedWith(id, list, mgr);
|
|
}
|
|
|
|
std::optional<zeus::CAABox> CBouncyGrenade::GetTouchBounds() const { return GetModelData()->GetBounds(GetTransform()); }
|
|
|
|
void CBouncyGrenade::Render(CStateManager& mgr) {
|
|
if (!x2b4_24_exploded) {
|
|
GetModelData()->Render(mgr, GetTransform(), nullptr, {0, 0, 3, zeus::skWhite});
|
|
} else if (mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::XRay) {
|
|
CElementGen::SetSubtractBlend(true);
|
|
CElementGen::SetMoveRedToAlphaBuffer(true);
|
|
CGraphics::SetFog(ERglFogMode::PerspLin, 0.f, 75.f, zeus::skBlack);
|
|
x2a4_elementGenXRay->Render();
|
|
mgr.SetupFogForArea(GetAreaIdAlways());
|
|
CElementGen::SetSubtractBlend(false);
|
|
CElementGen::SetMoveRedToAlphaBuffer(false);
|
|
}
|
|
}
|
|
|
|
void CBouncyGrenade::Think(float dt, CStateManager& mgr) {
|
|
if (GetActive()) {
|
|
const zeus::CTransform& orientation = GetTransform().getRotation();
|
|
const zeus::CVector3f& translation = GetTranslation();
|
|
const zeus::CVector3f& scale = GetModelData()->GetScale();
|
|
auto UpdateElementGen = [ orientation, translation, scale, dt ](CElementGen & gen) {
|
|
gen.SetOrientation(orientation);
|
|
gen.SetGlobalTranslation(translation);
|
|
gen.SetGlobalScale(scale);
|
|
gen.Update(dt);
|
|
};
|
|
if (x2b4_24_exploded) {
|
|
Stop();
|
|
UpdateElementGen(*x2a0_elementGenCombat);
|
|
UpdateElementGen(*x2a4_elementGenXRay);
|
|
UpdateElementGen(*x2a8_elementGenThermal);
|
|
} else {
|
|
UpdateElementGen(*x2ac_elementGen4);
|
|
}
|
|
x29c_ += dt;
|
|
if (x29c_ > 0.3f) {
|
|
x2b4_25_ = true;
|
|
}
|
|
const zeus::CVector3f& playerDistance = mgr.GetPlayer().GetTranslation() +
|
|
zeus::CVector3f{0.f, 0.f, 0.5f * mgr.GetPlayer().GetEyeHeight()} -
|
|
translation;
|
|
if (playerDistance.magSquared() < x2b0_explodePlayerDistance * x2b0_explodePlayerDistance) {
|
|
Explode(mgr, kInvalidUniqueId);
|
|
}
|
|
}
|
|
if (x2a0_elementGenCombat->IsSystemDeletable() && x2a4_elementGenXRay->IsSystemDeletable() &&
|
|
x2a8_elementGenThermal->IsSystemDeletable()) {
|
|
mgr.FreeScriptObject(GetUniqueId());
|
|
}
|
|
}
|
|
|
|
void CBouncyGrenade::Touch(CActor& act, CStateManager& mgr) { CActor::Touch(act, mgr); }
|
|
|
|
void CBouncyGrenade::Explode(CStateManager& mgr, TUniqueId uid) {
|
|
if (x2b4_24_exploded) {
|
|
return;
|
|
}
|
|
|
|
x2b4_24_exploded = true;
|
|
CSfxManager::AddEmitter(x258_data.GetExplodeSfx(), GetTranslation(), zeus::skUp, false, false, 0x7f,
|
|
GetAreaIdAlways());
|
|
x2a0_elementGenCombat->SetParticleEmission(true);
|
|
x2a4_elementGenXRay->SetParticleEmission(true);
|
|
x2a8_elementGenThermal->SetParticleEmission(true);
|
|
x2ac_elementGen4->SetParticleEmission(false);
|
|
|
|
const CDamageInfo& dInfo = x258_data.GetDamageInfo();
|
|
{
|
|
bool isParent = x298_parentId == uid;
|
|
if (TCastToConstPtr<CCollisionActor> actor = mgr.GetObjectById(uid)) {
|
|
isParent = x298_parentId == actor->GetOwnerId();
|
|
}
|
|
if (uid != kInvalidUniqueId && !isParent) {
|
|
mgr.ApplyDamage(GetUniqueId(), uid, GetUniqueId(), dInfo, CMaterialFilter::MakeInclude({EMaterialTypes::Solid}),
|
|
zeus::skZero3f);
|
|
}
|
|
}
|
|
|
|
const float radius = dInfo.GetRadius();
|
|
if (radius > 1.f) {
|
|
const zeus::CVector3f& pos = GetTranslation();
|
|
const CMaterialFilter filter = CMaterialFilter::MakeInclude({EMaterialTypes::Player, EMaterialTypes::Character});
|
|
rstl::reserved_vector<TUniqueId, 1024> nearList;
|
|
mgr.BuildNearList(nearList, {pos - radius, pos + radius}, filter, nullptr);
|
|
|
|
for (const auto& id : nearList) {
|
|
bool isParent = x298_parentId == id;
|
|
if (TCastToConstPtr<CCollisionActor> cActor = mgr.GetObjectById(id)) {
|
|
isParent = x298_parentId == cActor->GetOwnerId();
|
|
}
|
|
if (isParent) {
|
|
continue;
|
|
}
|
|
|
|
const auto* actor = static_cast<const CActor*>(mgr.GetObjectById(id));
|
|
if (actor == nullptr) {
|
|
continue;
|
|
}
|
|
|
|
const float magnitude = (actor->GetTranslation() - GetTranslation()).magnitude();
|
|
if (radius <= magnitude) {
|
|
continue;
|
|
}
|
|
|
|
float scale = (radius - magnitude) / radius;
|
|
const CDamageInfo info{dInfo.GetWeaponMode(), scale * dInfo.GetDamage(), radius,
|
|
scale * dInfo.GetKnockBackPower()};
|
|
mgr.ApplyDamage(GetUniqueId(), id, GetUniqueId(), info, CMaterialFilter::MakeInclude({EMaterialTypes::Solid}),
|
|
zeus::skZero3f);
|
|
}
|
|
}
|
|
}
|
|
} // namespace metaforce::MP1
|