mirror of https://github.com/AxioDL/metaforce.git
Initial CIceImpact
This commit is contained in:
parent
99b6034e5c
commit
5fd78e4c75
|
@ -21,6 +21,7 @@ class CCollidableOBBTreeGroupContainer {
|
|||
public:
|
||||
explicit CCollidableOBBTreeGroupContainer(CInputStream& in);
|
||||
CCollidableOBBTreeGroupContainer(const zeus::CVector3f&, const zeus::CVector3f&);
|
||||
u32 NumTrees() const { return x0_trees.size(); }
|
||||
};
|
||||
|
||||
class CCollidableOBBTreeGroup : public CCollisionPrimitive {
|
||||
|
@ -37,6 +38,8 @@ public:
|
|||
zeus::CAABox CalculateLocalAABox() const override;
|
||||
FourCC GetPrimType() const override;
|
||||
CRayCastResult CastRayInternal(const CInternalRayCastStructure&) const override;
|
||||
COBBTree const* GetOBBTreeAABox(int index) const { return x10_container->x0_trees[index].get(); }
|
||||
CCollidableOBBTreeGroupContainer const* GetContainer() const { return x10_container; }
|
||||
|
||||
static const Type& GetType();
|
||||
static void SetStaticTableIndex(u32 index);
|
||||
|
|
|
@ -168,6 +168,7 @@ public:
|
|||
const CMaterialList& matList, const zeus::CVector3f& dir, float mag,
|
||||
CCollisionInfo& infoOut, double& dOut);
|
||||
static void ResetInternalCounters();
|
||||
static std::array<u16, 0x4000> GetTriangleList() { return g_DupTriangleList; }
|
||||
};
|
||||
|
||||
class CAreaCollisionCache {
|
||||
|
|
|
@ -91,5 +91,6 @@ public:
|
|||
zeus::CAABox CalculateLocalAABox() const;
|
||||
zeus::CAABox CalculateAABox(const zeus::CTransform&) const;
|
||||
const CNode& GetRoot() const { return *x88_root; }
|
||||
u32 NumSurfaceMaterials() const { return x18_indexData.x30_surfaceMaterials.size(); }
|
||||
};
|
||||
} // namespace metaforce
|
||||
|
|
|
@ -1,14 +1,380 @@
|
|||
#include "Runtime/World/CIceImpact.hpp"
|
||||
|
||||
#include "Runtime/CStateManager.hpp"
|
||||
#include "Runtime/Collision/CCollidableOBBTreeGroup.hpp"
|
||||
#include "Runtime/Collision/CGameCollision.hpp"
|
||||
#include "Runtime/Collision/CollisionUtil.hpp"
|
||||
#include "Runtime/GameGlobalObjects.hpp"
|
||||
#include "Runtime/Graphics/CBooRenderer.hpp"
|
||||
#include "Runtime/Particle/CElementGen.hpp"
|
||||
#include "Runtime/World/CDamageVulnerability.hpp"
|
||||
#include "Runtime/World/CGameLight.hpp"
|
||||
#include "Runtime/World/CPatterned.hpp"
|
||||
#include "Runtime/World/CPlayer.hpp"
|
||||
#include "Runtime/World/CWallCrawlerSwarm.hpp"
|
||||
|
||||
#include "TCastTo.hpp" // Generated file, do not modify include path
|
||||
|
||||
namespace metaforce {
|
||||
|
||||
static bool PointInSphere(zeus::CSphere const& sphere, zeus::CVector3f const& point) {
|
||||
return (point - sphere.position).magSquared() <= (sphere.radius * sphere.radius);
|
||||
}
|
||||
|
||||
CIceImpact::CIceImpact(const TLockedToken<CGenDescription>& particle, TUniqueId uid, TAreaId aid, bool active,
|
||||
std::string_view name, const zeus::CTransform& xf, u32 flags, const zeus::CVector3f& scale,
|
||||
const zeus::CColor& color)
|
||||
: CEffect(uid, CEntityInfo(aid, CEntity::NullConnectionList), active, name, xf) {}
|
||||
: CEffect(uid, CEntityInfo(aid, CEntity::NullConnectionList), active, name, xf)
|
||||
, xe8_elementGen(std::make_unique<CElementGen>(particle, CElementGen::EModelOrientationType::One,
|
||||
CElementGen::EOptionalSystemFlags::One))
|
||||
, xf0_genAssetId(particle.GetObjectTag()->id)
|
||||
, x108_sphereGenRange(x34_transform.origin, 6.4f)
|
||||
, x118_grid(zeus::CAABox(xf.origin - zeus::CVector3f(x100_halfBounds), xf.origin + zeus::CVector3f(x100_halfBounds)))
|
||||
, x598_24_(flags & 0x2) {
|
||||
|
||||
xe6_27_thermalVisorFlags = 2;
|
||||
x540_impactSpheres.push_back(SImpactSphere(x34_transform.origin, 2.4f, 1.6f, 0.f, 0.f));
|
||||
x540_impactSpheres.push_back(SImpactSphere(x34_transform.origin, 0.f, 1.f, 1.f, 0.f));
|
||||
x540_impactSpheres.push_back(SImpactSphere(x34_transform.origin, 0.f, 1.f, 1.f, 0.f));
|
||||
// ???
|
||||
x540_impactSpheres[1].x18_d = x540_impactSpheres[2].x14_c;
|
||||
x540_impactSpheres[1].x14_c += x540_impactSpheres[2].x10_b;
|
||||
x540_impactSpheres[2].x18_d = x540_impactSpheres[2].x14_c;
|
||||
x540_impactSpheres[2].x14_c += x540_impactSpheres[2].x10_b;
|
||||
x118_grid.MarkCells(zeus::CSphere(x34_transform.origin, 2.4f), 2);
|
||||
}
|
||||
|
||||
void CIceImpact::Accept(IVisitor& visitor) { visitor.Visit(this); }
|
||||
|
||||
void CIceImpact::CalculateRenderBounds() {
|
||||
auto bounds = xe8_elementGen->GetBounds();
|
||||
if (bounds) {
|
||||
x598_25_hasRenderBounds = true;
|
||||
x9c_renderBounds = *bounds;
|
||||
} else {
|
||||
x598_25_hasRenderBounds = false;
|
||||
x9c_renderBounds = zeus::CAABox(x34_transform.origin, x34_transform.origin);
|
||||
}
|
||||
}
|
||||
|
||||
void CIceImpact::PreRender(CStateManager& mgr, zeus::CFrustum const& planes) {
|
||||
CActor::PreRender(mgr, planes);
|
||||
bool out_of_frustum = false;
|
||||
if (!x598_25_hasRenderBounds || !planes.aabbFrustumTest(x9c_renderBounds)) {
|
||||
out_of_frustum = true;
|
||||
}
|
||||
|
||||
xe4_30_outOfFrustum = out_of_frustum;
|
||||
}
|
||||
|
||||
void CIceImpact::AddToRenderer(zeus::CFrustum const& planes, CStateManager& mgr) {
|
||||
if (xe4_30_outOfFrustum) {
|
||||
return;
|
||||
}
|
||||
if (mgr.GetThermalDrawFlag() == EThermalDrawFlag::Hot) {
|
||||
EnsureRendered(mgr);
|
||||
} else {
|
||||
g_Renderer->AddParticleGen(*xe8_elementGen);
|
||||
}
|
||||
}
|
||||
|
||||
void CIceImpact::Render(CStateManager& mgr) {
|
||||
CElementGen::SetSubtractBlend(true);
|
||||
CBooModel::SetRenderModelBlack(true);
|
||||
xe8_elementGen->Render();
|
||||
CElementGen::SetSubtractBlend(false);
|
||||
CBooModel::SetRenderModelBlack(false);
|
||||
}
|
||||
|
||||
void CIceImpact::Think(float dt, CStateManager& mgr) {
|
||||
xf4_lifeTimer += dt;
|
||||
if (xf4_lifeTimer < 0.8f && xe8_elementGen->GetParticleCount() < 0x190) {
|
||||
for (SImpactSphere& sphere : x540_impactSpheres) {
|
||||
if (sphere.x14_c <= sphere.xc_a) {
|
||||
continue;
|
||||
}
|
||||
auto new_sphere = GenerateNewSphere();
|
||||
if (new_sphere) {
|
||||
sphere = *new_sphere;
|
||||
}
|
||||
}
|
||||
for (SImpactSphere& sphere : x540_impactSpheres) {
|
||||
if (sphere.x14_c > sphere.xc_a) {
|
||||
continue;
|
||||
}
|
||||
sphere.x18_d = sphere.x14_c;
|
||||
sphere.x14_c += sphere.x10_b;
|
||||
zeus::CSphere sphere_a(sphere.x0_pos, sphere.x14_c);
|
||||
zeus::CSphere sphere_b(sphere.x0_pos, sphere.x18_d);
|
||||
zeus::CAABox bbox(sphere_b.position - sphere_a.radius, sphere_b.position + sphere_a.radius);
|
||||
x104_ = 0.f;
|
||||
GenerateParticlesAgainstActors(mgr, bbox, sphere_a, sphere_b);
|
||||
CAreaCollisionCache cache(bbox);
|
||||
CGameCollision::BuildAreaCollisionCache(mgr, cache);
|
||||
for (auto const& leaf_cache : cache) {
|
||||
GenerateParticlesAgainstWorld(mgr, leaf_cache, sphere_a, sphere_b);
|
||||
}
|
||||
}
|
||||
}
|
||||
xe8_elementGen->SetOrientation(zeus::CTransform());
|
||||
xe8_elementGen->Update(dt);
|
||||
|
||||
if (xec_ != kInvalidUniqueId) {
|
||||
if (TCastToPtr<CGameLight> light = mgr.ObjectById(xec_)) {
|
||||
if (x30_24_active) {
|
||||
light->SetLight(xe8_elementGen->GetLight());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x598_24_) {
|
||||
mgr.SetActorAreaId(*this, mgr.GetPlayer().GetAreaIdAlways());
|
||||
}
|
||||
if (xe8_elementGen->IsSystemDeletable()) {
|
||||
mgr.FreeScriptObject(x8_uid);
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<CIceImpact::SImpactSphere> CIceImpact::GenerateNewSphere() {
|
||||
xfc_searchDirection = (xfc_searchDirection + 1) & 7;
|
||||
bool fwd_z = (xfc_searchDirection & 1) != 0u;
|
||||
bool fwd_y = (xfc_searchDirection & 2) != 0u;
|
||||
bool fwd_x = (xfc_searchDirection & 4) != 0u;
|
||||
|
||||
constexpr auto loop_cond = [](bool fwd, u32 counter) {
|
||||
if (fwd) {
|
||||
return counter < 14;
|
||||
}
|
||||
return counter != 0;
|
||||
};
|
||||
constexpr auto loop_step = [](bool fwd, u32 counter) {
|
||||
if (fwd) {
|
||||
return counter + 1;
|
||||
}
|
||||
return counter - 1;
|
||||
};
|
||||
|
||||
for (u32 z = 8; loop_cond(fwd_z, z); z = loop_step(fwd_z, z)) {
|
||||
for (u32 y = 8; loop_cond(fwd_y, y); y = loop_step(fwd_y, y)) {
|
||||
for (u32 x = 8; loop_cond(fwd_x, x); x = loop_step(fwd_x, x)) {
|
||||
u32 grid_val = x118_grid.GetValue(z, y, x);
|
||||
if (grid_val != 1) {
|
||||
zeus::CVector3f pos = x118_grid.GetWorldPositionForCell(z, y, x);
|
||||
x118_grid.SetValue(z, y, x, 3);
|
||||
if (PointInSphere(x108_sphereGenRange, pos)) {
|
||||
x118_grid.MarkCells(zeus::CSphere(pos, 1.6f), 2);
|
||||
return SImpactSphere(pos, 1.6f, 1.6f, 0.f, 0.f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void CIceImpact::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
|
||||
switch (msg) {
|
||||
case EScriptObjectMessage::Deleted:
|
||||
if (xec_ != kInvalidUniqueId) {
|
||||
mgr.FreeScriptObject(xec_);
|
||||
xec_ = kInvalidUniqueId;
|
||||
}
|
||||
break;
|
||||
case EScriptObjectMessage::Registered:
|
||||
if (xe8_elementGen->SystemHasLight()) {
|
||||
xec_ = mgr.AllocateUniqueId();
|
||||
auto* new_light = new CGameLight(xec_, x4_areaId, x30_24_active, "IcePLight_"sv, x34_transform, x8_uid,
|
||||
xe8_elementGen->GetLight(), xf0_genAssetId.Value(), 1, 0.f);
|
||||
mgr.AddObject(new_light);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
CActor::AcceptScriptMsg(msg, uid, mgr);
|
||||
if (xec_ != kInvalidUniqueId) {
|
||||
mgr.SendScriptMsgAlways(xec_, uid, msg);
|
||||
}
|
||||
}
|
||||
|
||||
void CIceImpact::Touch(CActor& actor, CStateManager& mgr) {
|
||||
if (xf4_lifeTimer > xf8_latestDamageTime) {
|
||||
return;
|
||||
}
|
||||
auto bounds = actor.GetTouchBounds();
|
||||
if (!bounds) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (TCastToPtr<CPatterned> ai = actor) {
|
||||
if (x118_grid.AABoxTouchesData(
|
||||
zeus::CAABox(bounds->min - zeus::CVector3f(0, 0, 0.5f), bounds->max + zeus::CVector3f(0, 0, 0.5f)), 1)) {
|
||||
CDamageVulnerability const* vuln = ai->GetDamageVulnerability();
|
||||
if (vuln->WeaponHits(CWeaponMode(EWeaponType::Ice), false) && ai->GetKnockBackController().GetEnableFreeze() &&
|
||||
ai->GetBodyController()->GetPercentageFrozen() == 0.f && (xf8_latestDamageTime - xf4_lifeTimer) > 0.5f &&
|
||||
xf4_lifeTimer < 0.8f) {
|
||||
mgr.ApplyDamage(x8_uid, actor.GetUniqueId(), kInvalidUniqueId,
|
||||
CDamageInfo(CWeaponMode(EWeaponType::Ice), 100.f, 0.f, 1.f),
|
||||
CMaterialFilter::MakeIncludeExclude(CMaterialList(EMaterialTypes::Solid), CMaterialList()),
|
||||
zeus::CVector3f());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actor.GetMaterialList().HasMaterial(EMaterialTypes::ExcludeFromLineOfSightTest) &&
|
||||
x118_grid.AABoxTouchesData(*bounds, 1)) {
|
||||
mgr.ApplyDamage(
|
||||
x8_uid, actor.GetUniqueId(), kInvalidUniqueId, CDamageInfo(CWeaponMode(EWeaponType::Ice), 100.f, 0.f, 1.f),
|
||||
CMaterialFilter::MakeIncludeExclude(CMaterialList(EMaterialTypes::Solid), CMaterialList()), zeus::CVector3f());
|
||||
}
|
||||
if (TCastToPtr<CWallCrawlerSwarm> wall_crawler = actor) {
|
||||
if (xf8_latestDamageTime - xf4_lifeTimer > 0.5f) {
|
||||
wall_crawler->FreezeCollision(x118_grid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CIceImpact::GenerateParticlesAgainstWorld(CStateManager& mgr,
|
||||
const CMetroidAreaCollider::COctreeLeafCache& leaf_cache,
|
||||
zeus::CSphere& a, zeus::CSphere& b) {
|
||||
CMetroidAreaCollider::ResetInternalCounters();
|
||||
auto triangle_list = CMetroidAreaCollider::GetTriangleList();
|
||||
auto filter = CMaterialFilter::MakeExclude(EMaterialTypes::ProjectilePassthrough);
|
||||
for (auto const& node : leaf_cache) {
|
||||
CAreaOctTree::TriListReference arr = node.GetTriangleArray();
|
||||
bool subdivide_result = false;
|
||||
for (int i = 0; i < static_cast<u32>(arr.GetAt(0)) && !subdivide_result; i++) {
|
||||
u16 v1 = arr.GetAt(i + 1) << 1;
|
||||
if (triangle_list[v1] != 0x6438) {
|
||||
triangle_list[v1] = 0x6438;
|
||||
CCollisionSurface surface = node.GetOwner().GetMasterListTriangle(arr.GetAt(i + 1));
|
||||
if (filter.Passes(CMaterialList(surface.GetSurfaceFlags()))) {
|
||||
subdivide_result =
|
||||
SubdivideAndGenerateParticles(mgr, surface.GetVert(0), surface.GetVert(1), surface.GetVert(1), a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CIceImpact::GenerateParticlesAgainstActors(CStateManager& mgr, const zeus::CAABox& box, const zeus::CSphere& a,
|
||||
const zeus::CSphere& b) {
|
||||
CMaterialFilter filter =
|
||||
CMaterialFilter::MakeExclude({EMaterialTypes::Character, EMaterialTypes::Player, EMaterialTypes::Projectile,
|
||||
EMaterialTypes::ProjectilePassthrough, EMaterialTypes::AIJoint});
|
||||
rstl::reserved_vector<TUniqueId, 1024> near_list;
|
||||
mgr.BuildNearList(near_list, box, filter, this);
|
||||
for (TUniqueId uid : near_list) {
|
||||
CEntity* ent = mgr.ObjectById(uid);
|
||||
TCastToPtr<CPhysicsActor> pAct = ent;
|
||||
if (pAct && pAct->GetCollisionPrimitive()->GetPrimType() == FOURCC('OBTG')) {
|
||||
const auto* prim = static_cast<const CCollidableOBBTreeGroup*>(pAct->GetCollisionPrimitive());
|
||||
for (int i = 0; i < prim->GetContainer()->NumTrees(); ++i) {
|
||||
GenerateParticlesAgainstOBBTree(mgr, *prim->GetOBBTreeAABox(i), pAct->GetPrimitiveTransform(), a, b);
|
||||
}
|
||||
} else if (const auto* actor = static_cast<CActor*>(ent)) {
|
||||
if (!actor->GetMaterialList().HasMaterial(EMaterialTypes::Solid)) {
|
||||
if (!TCastToPtr<CScriptWater>(ent)) {
|
||||
continue;
|
||||
}
|
||||
GenerateParticlesAgainstAABox(mgr, *actor->GetTouchBounds(), a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CIceImpact::GenerateParticlesAgainstOBBTree(CStateManager& mgr, const COBBTree& tree, const zeus::CTransform& xf,
|
||||
const zeus::CSphere& a, const zeus::CSphere& b) {
|
||||
auto filter = CMaterialFilter::MakeExclude(EMaterialTypes::Solid);
|
||||
|
||||
for (int i = 0; i < tree.NumSurfaceMaterials(); i++) {
|
||||
CCollisionSurface surface = tree.GetTransformedSurface(i, xf);
|
||||
if (filter.Passes(static_cast<u64>(surface.GetSurfaceFlags()))) {
|
||||
if (SubdivideAndGenerateParticles(mgr, surface.GetVert(0), surface.GetVert(1), surface.GetVert(2), a, b)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CIceImpact::GenerateParticlesAgainstAABox(CStateManager& mgr, const zeus::CAABox& box,
|
||||
const zeus::CSphere& a, const zeus::CSphere& b) {
|
||||
for (int i = 0; i < 12; ++i) {
|
||||
auto tri = box.getTri(zeus::CAABox::EBoxFaceId(i / 2), (i & 1) * 2);
|
||||
if (SubdivideAndGenerateParticles(mgr, tri.x10_verts[0], tri.x10_verts[2], tri.x10_verts[1], a, b)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CIceImpact::SubdivideAndGenerateParticles(CStateManager& mgr, zeus::CVector3f const& v1, zeus::CVector3f const& v2,
|
||||
zeus::CVector3f const& v3, zeus::CSphere const& a,
|
||||
zeus::CSphere const& b) {
|
||||
if (!CollisionUtil::TriSphereOverlap(a, v1, v2, v3)) {
|
||||
return false;
|
||||
}
|
||||
if (!PointInSphere(b, v1) && !PointInSphere(b, v2) && !PointInSphere(b, v3)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
zeus::CVector3f xprod = (v2 - v1).cross(v3 - v1);
|
||||
float mag = xprod.magnitude();
|
||||
if (mag <= 1.f) {
|
||||
x104_ += mag;
|
||||
int particle_create_count = static_cast<int>(x104_);
|
||||
x104_ -= particle_create_count;
|
||||
for (int i = 0; i < particle_create_count; i++) {
|
||||
float rx = mgr.GetActiveRandom()->Float();
|
||||
float ry = mgr.GetActiveRandom()->Float();
|
||||
float rz = mgr.GetActiveRandom()->Float();
|
||||
float random_inv = 1.f / (rx + ry + rz);
|
||||
zeus::CVector3f point =
|
||||
zeus::baryToWorld(v1, v2, v3, zeus::CVector3f(rx * random_inv, ry * random_inv, rz * random_inv));
|
||||
u32 cx, cy, cz;
|
||||
if (!PointInSphere(b, point) && PointInSphere(a, point) && x118_grid.GetCoords(point, cx, cy, cz) &&
|
||||
((x118_grid.GetValue(cx, cy, cz) & 1) == 0u)) {
|
||||
x118_grid.SetValue(cx, cy, cz, 1);
|
||||
zeus::CVector3f vec;
|
||||
switch (mgr.GetActiveRandom()->Range(0, 2)) {
|
||||
case 0:
|
||||
vec = v1 - point;
|
||||
if (!vec.canBeNormalized()) {
|
||||
vec = v2 - point;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
vec = v2 - point;
|
||||
if (!vec.canBeNormalized()) {
|
||||
vec = v3 - point;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
vec = v3 - point;
|
||||
if (!vec.canBeNormalized()) {
|
||||
vec = v1 - point;
|
||||
}
|
||||
break;
|
||||
}
|
||||
vec = vec.normalized();
|
||||
zeus::CVector3f norm_xprod = xprod.normalized();
|
||||
norm_xprod.x() = (mgr.GetActiveRandom()->Float() - 0.5f) * 0.4f + norm_xprod.x();
|
||||
norm_xprod.y() = (mgr.GetActiveRandom()->Float() - 0.5f) * 0.4f + norm_xprod.y();
|
||||
norm_xprod.z() = (mgr.GetActiveRandom()->Float() - 0.5f) * 0.4f + norm_xprod.z();
|
||||
xe8_elementGen->SetOrientation(zeus::lookAt(zeus::CVector3f(), norm_xprod.normalized(), vec));
|
||||
xe8_elementGen->SetTranslation(point);
|
||||
xe8_elementGen->ForceParticleCreation(1);
|
||||
if (xe8_elementGen->GetParticleCount() == xe8_elementGen->GetMaxParticles()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
zeus::CVector3f point = zeus::baryToWorld(v1, v2, v3, zeus::CVector3f(1.f / 3.f));
|
||||
SubdivideAndGenerateParticles(mgr, v1, v2, point, a, b);
|
||||
SubdivideAndGenerateParticles(mgr, v2, v3, point, a, b);
|
||||
SubdivideAndGenerateParticles(mgr, v3, v1, point, a, b);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace metaforce
|
||||
|
|
|
@ -1,16 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include "Runtime/Collision/CMetroidAreaCollider.hpp"
|
||||
#include "Runtime/World/CEffect.hpp"
|
||||
#include "Runtime/World/CMarkerGrid.hpp"
|
||||
|
||||
namespace metaforce {
|
||||
|
||||
class COBBTree;
|
||||
class CElementGen;
|
||||
class CIceImpact : public CEffect {
|
||||
private:
|
||||
struct SImpactSphere {
|
||||
zeus::CVector3f x0_pos;
|
||||
float xc_a;
|
||||
float x10_b;
|
||||
float x14_c;
|
||||
float x18_d;
|
||||
|
||||
SImpactSphere(zeus::CVector3f const& pos, float a, float b, float c, float d)
|
||||
: x0_pos(pos), xc_a(a), x10_b(b), x14_c(c), x18_d(d) {}
|
||||
};
|
||||
std::unique_ptr<CElementGen> xe8_elementGen;
|
||||
TUniqueId xec_ = kInvalidUniqueId;
|
||||
CAssetId xf0_genAssetId;
|
||||
float xf4_lifeTimer = 0.f;
|
||||
float xf8_latestDamageTime = 4.f;
|
||||
u32 xfc_searchDirection = 0;
|
||||
float x100_halfBounds = 8.f;
|
||||
float x104_ = 0;
|
||||
zeus::CSphere x108_sphereGenRange;
|
||||
CMarkerGrid x118_grid;
|
||||
rstl::reserved_vector<SImpactSphere, 3> x540_impactSpheres;
|
||||
bool x598_24_ : 1;
|
||||
bool x598_25_hasRenderBounds : 1 = false;
|
||||
|
||||
std::optional<CIceImpact::SImpactSphere> GenerateNewSphere();
|
||||
void GenerateParticlesAgainstWorld(CStateManager& mgr, const CMetroidAreaCollider::COctreeLeafCache& leaf_cache,
|
||||
zeus::CSphere& a, zeus::CSphere& b);
|
||||
void GenerateParticlesAgainstActors(CStateManager& mgr, const zeus::CAABox& box, const zeus::CSphere& a,
|
||||
const zeus::CSphere& b);
|
||||
void GenerateParticlesAgainstOBBTree(CStateManager& mgr, const COBBTree& tree, const zeus::CTransform& xf,
|
||||
const zeus::CSphere& a, const zeus::CSphere& b);
|
||||
void GenerateParticlesAgainstAABox(CStateManager& mgr, const zeus::CAABox& box, const zeus::CSphere& a,
|
||||
const zeus::CSphere& b);
|
||||
bool SubdivideAndGenerateParticles(CStateManager& mgr, zeus::CVector3f const& v1, zeus::CVector3f const& v2,
|
||||
zeus::CVector3f const& v3, zeus::CSphere const& a, zeus::CSphere const& b);
|
||||
|
||||
public:
|
||||
DEFINE_ENTITY
|
||||
CIceImpact(const TLockedToken<CGenDescription>& particle, TUniqueId uid, TAreaId aid, bool active,
|
||||
std::string_view name, const zeus::CTransform& xf, u32 flags, const zeus::CVector3f& scale,
|
||||
const zeus::CColor& color);
|
||||
void Accept(IVisitor& visitor) override;
|
||||
void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) override;
|
||||
void Think(float dt, CStateManager& mgr) override;
|
||||
void PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) override;
|
||||
void AddToRenderer(const zeus::CFrustum& frustum, CStateManager& mgr) override;
|
||||
void Render(CStateManager& frustum) override;
|
||||
std::optional<zeus::CAABox> GetTouchBounds() const override { return x118_grid.GetBounds(); }
|
||||
void Touch(CActor& actor, CStateManager& mgr) override;
|
||||
void CalculateRenderBounds() override;
|
||||
};
|
||||
|
||||
} // namespace metaforce
|
||||
|
|
|
@ -127,6 +127,7 @@ public:
|
|||
EKnockBackType type, float magnitude);
|
||||
void SetSeverity(pas::ESeverity v) { x7c_severity = v; }
|
||||
void SetEnableFreeze(bool b) { x81_25_enableFreeze = b; }
|
||||
bool GetEnableFreeze() const { return x81_25_enableFreeze; }
|
||||
void SetEnableShock(bool b) { x81_26_enableShock = b; }
|
||||
void SetEnableBurn(bool b) { x81_27_enableBurn = b; }
|
||||
void SetEnableBurnDeath(bool b) { x81_28_enableBurnDeath = b; }
|
||||
|
|
|
@ -130,6 +130,7 @@ set(WORLD_SOURCES
|
|||
CHUDBillboardEffect.hpp CHUDBillboardEffect.cpp
|
||||
CExplosion.hpp CExplosion.cpp
|
||||
CIceImpact.hpp CIceImpact.cpp
|
||||
CMarkerGrid.hpp CMarkerGrid.cpp
|
||||
CFire.hpp CFire.cpp
|
||||
CEntityInfo.hpp)
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
#include "Runtime/World/CMarkerGrid.hpp"
|
||||
|
||||
namespace metaforce {
|
||||
CMarkerGrid::CMarkerGrid(const zeus::CAABox& bounds) : x0_bounds(bounds) {
|
||||
x18_gridUnits = zeus::CVector3f((bounds.max - bounds.min) * 0.0625f);
|
||||
x24_gridState.resize(0x400);
|
||||
}
|
||||
|
||||
void CMarkerGrid::MarkCells(const zeus::CSphere& area, u32 val) {
|
||||
int width_units = static_cast<int>((area.radius - x18_gridUnits.x()) / x18_gridUnits.x());
|
||||
int length_units = static_cast<int>((area.radius - x18_gridUnits.y()) / x18_gridUnits.y());
|
||||
int height_units = static_cast<int>((area.radius - x18_gridUnits.z()) / x18_gridUnits.z());
|
||||
u32 x_coord, y_coord, z_coord;
|
||||
if (!GetCoords(area.position, x_coord, y_coord, z_coord)) {
|
||||
return;
|
||||
}
|
||||
for (int i = width_units - z_coord; i < (z_coord + width_units); i++) {
|
||||
for (int j = length_units - y_coord; j < (y_coord + length_units); j++) {
|
||||
for (int k = height_units - x_coord; k < (z_coord + height_units); k++) {
|
||||
u32 new_cell_val = val | GetValue(x_coord, y_coord, z_coord);
|
||||
SetValue(k, j, i, new_cell_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CMarkerGrid::GetCoords(zeus::CVector3f const& vec, u32& x, u32& y, u32& z) const {
|
||||
if (x0_bounds.pointInside(vec)) {
|
||||
x = static_cast<u32>((vec.x() - x0_bounds.min.x()) / x);
|
||||
y = static_cast<u32>((vec.y() - x0_bounds.min.y()) / y);
|
||||
z = static_cast<u32>((vec.z() - x0_bounds.min.z()) / z);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 CMarkerGrid::GetValue(u32 x, u32 y, u32 z) const {
|
||||
const u32 bit_offset = (x & 3) << 1;
|
||||
u8 marker_byte = x24_gridState[(z << 6) + (y << 2) + (x >> 2)];
|
||||
return static_cast<u32>((marker_byte & (3 << bit_offset)) >> bit_offset);
|
||||
}
|
||||
|
||||
void CMarkerGrid::SetValue(u32 x, u32 y, u32 z, u32 val) {
|
||||
const u32 bit_offset = (x & 3) << 1;
|
||||
const u32 grid_offset = (z << 6) + (y << 2) + (x >> 2);
|
||||
u8 marker_byte = x24_gridState[grid_offset];
|
||||
marker_byte |= (marker_byte & ~(3 << bit_offset)) | (val << bit_offset);
|
||||
x24_gridState[grid_offset] = marker_byte;
|
||||
}
|
||||
|
||||
bool CMarkerGrid::AABoxTouchesData(const zeus::CAABox& box, u32 val) const {
|
||||
if (!x0_bounds.intersects(box)) {
|
||||
return false;
|
||||
}
|
||||
zeus::CAABox in_box = box;
|
||||
if (!box.inside(x0_bounds)) {
|
||||
zeus::CVector3f max_of_min(x0_bounds.min.x() > box.min.x() ? x0_bounds.min.x() : box.min.x(),
|
||||
x0_bounds.min.y() > box.min.y() ? x0_bounds.min.y() : box.min.y(),
|
||||
x0_bounds.min.z() > box.min.z() ? x0_bounds.min.z() : box.min.z());
|
||||
zeus::CVector3f min_of_max(x0_bounds.max.x() < box.max.x() ? x0_bounds.max.x() : box.max.x(),
|
||||
x0_bounds.max.y() < box.max.y() ? x0_bounds.max.y() : box.max.y(),
|
||||
x0_bounds.max.z() < box.max.z() ? x0_bounds.max.z() : box.max.z());
|
||||
in_box = zeus::CAABox(max_of_min, min_of_max);
|
||||
}
|
||||
u32 c1x, c1y, c1z, c2x, c2y, c2z;
|
||||
GetCoords(in_box.min, c1x, c1y, c1z);
|
||||
GetCoords(in_box.max, c2x, c2y, c2z);
|
||||
|
||||
for (int i = c1z; i < c2z; i++) {
|
||||
for (int j = c1y; j < c2y; j++) {
|
||||
for (int k = c1x; k < c2x; k++) {
|
||||
if ((GetValue(k, j, i) & val) != 0u) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
zeus::CVector3f CMarkerGrid::GetWorldPositionForCell(u32 x, u32 y, u32 z) const {
|
||||
// returns the center of a given cell
|
||||
return zeus::CVector3f(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z)) * x18_gridUnits +
|
||||
x0_bounds.min + (x18_gridUnits / 2.f);
|
||||
}
|
||||
} // namespace urde
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <zeus/CAABox.hpp>
|
||||
#include "Runtime/RetroTypes.hpp"
|
||||
|
||||
namespace metaforce {
|
||||
class CMarkerGrid {
|
||||
private:
|
||||
zeus::CAABox x0_bounds;
|
||||
zeus::CVector3f x18_gridUnits;
|
||||
rstl::reserved_vector<u8, 0x400> x24_gridState;
|
||||
|
||||
public:
|
||||
CMarkerGrid(const zeus::CAABox& bounds);
|
||||
void MarkCells(const zeus::CSphere& area, u32 val);
|
||||
bool GetCoords(const zeus::CVector3f& vec, u32& x, u32& y, u32& z) const;
|
||||
u32 GetValue(u32 x, u32 y, u32 z) const;
|
||||
void SetValue(u32 x, u32 y, u32 z, u32 val);
|
||||
|
||||
bool AABoxTouchesData(const zeus::CAABox& box, u32 val) const;
|
||||
zeus::CVector3f GetWorldPositionForCell(u32 x, u32 y, u32 z) const;
|
||||
const zeus::CAABox& GetBounds() const { return x0_bounds; }
|
||||
};
|
||||
} // namespace urde
|
|
@ -15,6 +15,7 @@
|
|||
#include "Runtime/Graphics/CVertexMorphEffect.hpp"
|
||||
#include "Runtime/Weapon/CGameProjectile.hpp"
|
||||
#include "Runtime/World/CActorParameters.hpp"
|
||||
#include "Runtime/World/CMarkerGrid.hpp"
|
||||
#include "Runtime/World/CPhysicsActor.hpp"
|
||||
#include "Runtime/World/CPlayer.hpp"
|
||||
#include "Runtime/World/CScriptDoor.hpp"
|
||||
|
@ -1233,4 +1234,18 @@ void CWallCrawlerSwarm::ApplyRadiusDamage(const zeus::CVector3f& pos, const CDam
|
|||
}
|
||||
}
|
||||
|
||||
void CWallCrawlerSwarm::FreezeCollision(CMarkerGrid const &grid) {
|
||||
for (CBoid &boid : x108_boids) {
|
||||
if (!boid.x80_24_active) {
|
||||
continue;
|
||||
}
|
||||
const float rad_sq_1 = x378_touchRadius * x378_touchRadius + 0.3f;
|
||||
const float rad_sq_2 = x378_touchRadius * x378_touchRadius + 0.5f;
|
||||
const zeus::CVector3f half_bounds(rad_sq_1, rad_sq_1, rad_sq_2);
|
||||
zeus::CAABox coll_bounds(boid.x0_xf.origin - half_bounds, boid.x0_xf.origin + half_bounds);
|
||||
if (grid.AABoxTouchesData(coll_bounds, 1)) {
|
||||
boid.x48_timeToDie = 1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace metaforce
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
namespace metaforce {
|
||||
class CAreaCollisionCache;
|
||||
class CMarkerGrid;
|
||||
class CWallCrawlerSwarm : public CActor {
|
||||
public:
|
||||
enum class EFlavor { Parasite, Scarab, Crab };
|
||||
|
@ -195,5 +196,6 @@ public:
|
|||
int GetCurrentLockOnId() const { return x42c_lockOnIdx; }
|
||||
bool GetLockOnLocationValid(int id) const { return id >= 0 && id < x108_boids.size() && x108_boids[id].GetActive(); }
|
||||
const zeus::CVector3f& GetLockOnLocation(int id) const { return x108_boids[id].GetTranslation(); }
|
||||
void FreezeCollision(const CMarkerGrid& grid);
|
||||
};
|
||||
} // namespace metaforce
|
||||
|
|
Loading…
Reference in New Issue