Implement CFishCloud

This commit is contained in:
Jack Andersen 2019-04-02 18:32:31 -10:00
parent 8e2db0795b
commit b72cc490e8
25 changed files with 943 additions and 159 deletions

View File

@ -17,6 +17,9 @@
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
<clangFormatSettings>
<option name="ENABLED" value="true" />
</clangFormatSettings>
<codeStyleSettings language="ObjectiveC">
<option name="INDENT_CASE_FROM_SWITCH" value="false" />
<indentOptions>

View File

@ -12,38 +12,38 @@ struct FishCloud : IScriptObject {
Value<atVec3f> location;
Value<atVec3f> orientation;
Value<atVec3f> volume;
Value<bool> unknown1;
Value<bool> active;
UniqueID32 model;
AnimationParameters animationParameters;
Value<float> unknown2;
Value<float> unknown3;
Value<float> unknown4;
Value<float> unknown5;
Value<float> unknown6;
Value<float> unknown7;
Value<float> unknown8;
Value<float> unknown9;
Value<float> unknown10;
Value<float> unknown11;
Value<float> unknown12;
Value<float> unknown13;
Value<float> unknown14;
Value<float> unknown15;
Value<atUint32> unknown16;
Value<atVec4f> unknown17; // CColor
Value<bool> unknown18;
Value<float> unknown19;
Value<atUint32> numBoids;
Value<float> speed;
Value<float> separationRadius;
Value<float> cohesionMagnitude;
Value<float> alignmentWeight;
Value<float> separationMagnitude;
Value<float> weaponRepelMagnitude;
Value<float> playerRepelMagnitude;
Value<float> containmentMagnitude;
Value<float> scatterVel;
Value<float> maxScatterAngle;
Value<float> weaponRepelDampingSpeed;
Value<float> playerRepelDampingSpeed;
Value<float> containmentRadius;
Value<atUint32> updateShift;
Value<atVec4f> color; // CColor
Value<bool> killable;
Value<float> weaponKillRadius;
UniqueID32 deathParticle1;
Value<atUint32> deathParticle1Frames;
Value<atUint32> deathParticle1Count;
UniqueID32 deathParticle2;
Value<atUint32> deathParticle2Frames;
Value<atUint32> deathParticle2Count;
UniqueID32 deathParticle3;
Value<atUint32> deathParticle3Frames;
Value<atUint32> deathParticle3Count;
UniqueID32 deathParticle4;
Value<atUint32> deathParticle4Frames;
Value<atUint32> deathParticle4Count;
Value<atUint32> deathSFX;
Value<bool> unknown29;
Value<bool> unknown30;
Value<bool> repelFromThreats;
Value<bool> hotInThermal;
void nameIDs(PAKRouter<PAKBridge>& pakRouter) const {
if (model) {

View File

@ -10,10 +10,10 @@ struct FishCloudModifier : IScriptObject {
AT_DECL_DNAV
String<-1> name;
Value<atVec3f> position;
Value<bool> unknown1;
Value<bool> unknown2;
Value<bool> unknown3;
Value<float> unknown4;
Value<float> unknown5;
Value<bool> active;
Value<bool> repulsor;
Value<bool> swirl;
Value<float> radius;
Value<float> priority;
};
} // namespace DataSpec::DNAMP1

View File

@ -1321,8 +1321,7 @@ void CStateManager::ApplyDamageToWorld(TUniqueId damager, const CActor& actor, c
bool bomb = false;
TCastToPtr<CWeapon> weapon = const_cast<CActor&>(actor);
if (weapon)
bomb = (weapon->GetAttribField() & (EProjectileAttrib::Bombs | EProjectileAttrib::PowerBombs)) !=
EProjectileAttrib::None;
bomb = True(weapon->GetAttribField() & (EProjectileAttrib::Bombs | EProjectileAttrib::PowerBombs));
rstl::reserved_vector<TUniqueId, 1024> nearList;
BuildNearList(nearList, aabb, filter, &actor);
@ -1508,8 +1507,7 @@ bool CStateManager::MultiRayCollideWorld(const zeus::CMRay& ray, const CMaterial
void CStateManager::TestBombHittingWater(const CActor& damager, const zeus::CVector3f& pos, CActor& damagee) {
if (TCastToPtr<CWeapon> wpn = const_cast<CActor&>(damager)) {
if ((wpn->GetAttribField() & (EProjectileAttrib::Bombs | EProjectileAttrib::PowerBombs)) !=
EProjectileAttrib::None) {
if (True(wpn->GetAttribField() & (EProjectileAttrib::Bombs | EProjectileAttrib::PowerBombs))) {
bool powerBomb = (wpn->GetAttribField() & EProjectileAttrib::PowerBombs) == EProjectileAttrib::PowerBombs;
if (TCastToPtr<CScriptWater> water = damagee) {
zeus::CAABox bounds = water->GetTriggerBoundsWR();

View File

@ -76,7 +76,7 @@ void CCameraManager::EnterCinematic(CStateManager& mgr) {
mgr.FreeScriptObject(explo->GetUniqueId());
} else if (TCastToPtr<CWeapon> weap = ent) {
if (weap->GetActive()) {
if ((weap->GetAttribField() & EProjectileAttrib::KeepInCinematic) == EProjectileAttrib::None) {
if (False(weap->GetAttribField() & EProjectileAttrib::KeepInCinematic)) {
if (TCastToConstPtr<CAi>(mgr.GetObjectById(weap->GetOwnerId())) ||
TCastToConstPtr<CPlayer>(mgr.GetObjectById(weap->GetOwnerId())))
mgr.FreeScriptObject(weap->GetUniqueId());

View File

@ -10,14 +10,14 @@
namespace urde {
CBoneTracking::CBoneTracking(const CAnimData& animData, std::string_view bone,
float maxTrackingAngle, float angSpeed, bool parentIk)
float maxTrackingAngle, float angSpeed, EBoneTrackingFlags flags)
: x14_segId(animData.GetCharLayoutInfo().GetSegIdFromString(bone))
, x1c_maxTrackingAngle(maxTrackingAngle)
, x20_angSpeed(angSpeed)
, x36_26_noParent(parentIk)
, x36_27_noParentOrigin(parentIk)
, x36_28_noHorizontalAim(parentIk)
, x36_29_parentIk(parentIk) {}
, x36_26_noParent(True(flags & EBoneTrackingFlags::NoParent))
, x36_27_noParentOrigin(True(flags & EBoneTrackingFlags::NoParentOrigin))
, x36_28_noHorizontalAim(True(flags & EBoneTrackingFlags::NoHorizontalAim))
, x36_29_parentIk(True(flags & EBoneTrackingFlags::ParentIk)) {}
void CBoneTracking::Update(float dt) { x18_time += dt; }

View File

@ -11,6 +11,16 @@ namespace urde {
class CAnimData;
class CStateManager;
class CBodyController;
enum class EBoneTrackingFlags {
None = 0,
NoParent = 1,
NoParentOrigin = 2,
NoHorizontalAim = 4,
ParentIk = 8
};
ENABLE_BITWISE_ENUM(EBoneTrackingFlags)
class CBoneTracking {
zeus::CQuaternion x0_curRotation = zeus::CQuaternion();
float x10_ = 0.f;
@ -34,7 +44,7 @@ class CBoneTracking {
public:
CBoneTracking(const CAnimData& animData, std::string_view bone,
float maxTrackingAngle, float angSpeed, bool parentIk);
float maxTrackingAngle, float angSpeed, EBoneTrackingFlags flags);
void Update(float dt);
void PreRender(const CStateManager& mgr, CAnimData& animData, const zeus::CTransform& xf,
const zeus::CVector3f& vec, const CBodyController& bodyController);

View File

@ -59,7 +59,8 @@ CBabygoth::CBabygoth(TUniqueId uid, std::string_view name, const CEntityInfo& in
, x6ec_(nullptr, 1, pInfo.GetPathfindingIndex(), 1.f, 1.f)
, x7d0_(nullptr, 1, pInfo.GetPathfindingIndex(), 1.f, 1.f)
, x8d0_(x3b4_speed)
, x8f0_boneTracking(*ModelData()->AnimationData(), "Head_1"sv, zeus::degToRad(80.f), zeus::degToRad(180.f), false)
, x8f0_boneTracking(*ModelData()->AnimationData(), "Head_1"sv, zeus::degToRad(80.f), zeus::degToRad(180.f),
EBoneTrackingFlags::None)
, x930_aabox(GetBoundingBox(), GetMaterialList())
, x958_(babyData.x8_, babyData.xc_)
, x984_flameThrowerDesc(babyData.x44_.IsValid() ? g_SimplePool->GetObj({SBIG('WPSC'), babyData.x44_})

View File

@ -17,7 +17,8 @@ CEyeball::CEyeball(TUniqueId uid, std::string_view name, CPatterned::EFlavorType
EColliderType::Zero, EBodyType::Restricted, actParms, EKnockBackVariant::Medium)
, x568_attackDelay(attackDelay)
, x56c_attackStartTime(attackStartTime)
, x570_boneTracking(*GetModelData()->GetAnimationData(), "Eye"sv, zeus::degToRad(45.f), zeus::degToRad(180.f), true)
, x570_boneTracking(*GetModelData()->GetAnimationData(), "Eye"sv, zeus::degToRad(45.f), zeus::degToRad(180.f),
EBoneTrackingFlags::NoParent)
, x5b4_projectileInfo(wpscId, dInfo)
, x5dc_beamContactFxId(beamContactFxId)
, x5e0_beamPulseFxId(beamPulseFxId)

View File

@ -20,7 +20,8 @@ CNewIntroBoss::CNewIntroBoss(TUniqueId uid, std::string_view name, const CEntity
: 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), false)
, 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)

View File

@ -341,7 +341,7 @@ CSpacePirate::CSpacePirate(TUniqueId uid, std::string_view name, const CEntityIn
, x568_pirateData(in, propCount)
, x660_pathFindSearch(nullptr, 0x1, pInfo.GetPathfindingIndex(), 1.f, 1.f)
, x750_initialHP(pInfo.GetHealthInfo().GetHP())
, x764_boneTracking(*x64_modelData->AnimationData(), "Head_1"sv, 1.22173f, 3.14159f, false)
, x764_boneTracking(*x64_modelData->AnimationData(), "Head_1"sv, 1.22173f, 3.14159f, EBoneTrackingFlags::None)
, x7c4_burstFire(skBursts, x568_pirateData.xac_firstBurstCount)
, x8b8_minCloakAlpha(x568_pirateData.xb0_CloakOpacity)
, x8bc_maxCloakAlpha(x568_pirateData.xb4_MaxCloakOpacity)
@ -1005,7 +1005,7 @@ void CSpacePirate::Touch(CActor& other, CStateManager& mgr) {
CPatterned::Touch(other, mgr);
if (x85c_ragDoll && x85c_ragDoll->IsPrimed()) {
if (TCastToPtr<CScriptTrigger> trig = other) {
if (trig->GetActive() && (trig->GetTriggerFlags() & ETriggerFlags::DetectAI) != ETriggerFlags::None &&
if (trig->GetActive() && True(trig->GetTriggerFlags() & ETriggerFlags::DetectAI) &&
trig->GetForceMagnitude() > 0.f) {
x85c_ragDoll->TorsoImpulse() += trig->GetForceVector();
}

View File

@ -53,7 +53,7 @@ CElementGen::CElementGen(const TToken<CGenDescription>& gen, EModelOrientationTy
CGenDescription* desc = x1c_genDesc.GetObj();
x28_loadedGenDesc = desc;
x26d_27_enableOPTS = (flags & EOptionalSystemFlags::Two) != EOptionalSystemFlags::None;
x26d_27_enableOPTS = True(flags & EOptionalSystemFlags::Two);
if (desc->x54_x40_TEXR)
desc->x54_x40_TEXR->GetValueTexture(0).GetObj();

View File

@ -10,7 +10,7 @@ CWeapon::CWeapon(TUniqueId uid, TAreaId aid, bool active, TUniqueId owner, EWeap
const zeus::CTransform& xf, const CMaterialFilter& filter, const CMaterialList& mList,
const CDamageInfo& dInfo, EProjectileAttrib attribs, CModelData&& mData)
: CActor(uid, active, name, CEntityInfo(aid, CEntity::NullConnectionList), xf, std::move(mData), mList,
CActorParameters::None(), kInvalidUniqueId)
CActorParameters::None().HotInThermal(true), kInvalidUniqueId)
, xe8_projectileAttribs(attribs)
, xec_ownerId(owner)
, xf0_weaponType(type)
@ -70,10 +70,10 @@ void CWeapon::FluidFXThink(EFluidState state, CScriptWater& water, CStateManager
break;
}
if ((xe8_projectileAttribs & EProjectileAttrib::ComboShot) != EProjectileAttrib::None &&
if (True(xe8_projectileAttribs & EProjectileAttrib::ComboShot) &&
state != EFluidState::InFluid)
mag += 0.5f;
if ((xe8_projectileAttribs & EProjectileAttrib::Charged) != EProjectileAttrib::None)
if (True(xe8_projectileAttribs & EProjectileAttrib::Charged))
mag += 0.25f;
if (mag > 1.f)
mag = 1.f;
@ -81,7 +81,7 @@ void CWeapon::FluidFXThink(EFluidState state, CScriptWater& water, CStateManager
if (doRipple) {
zeus::CVector3f pos = GetTranslation();
pos.z() = float(water.GetTriggerBoundsWR().max.z());
if ((xe8_projectileAttribs & EProjectileAttrib::ComboShot) != EProjectileAttrib::None) {
if (True(xe8_projectileAttribs & EProjectileAttrib::ComboShot)) {
if (!water.CanRippleAtPoint(pos))
doRipple = false;
} else if (state == EFluidState::InFluid) {

View File

@ -176,7 +176,7 @@ void CEnvFxManager::CalculateSnowForces(const CVectorFixed8_8& zVec,
void CEnvFxManager::BuildBlockObjectList(rstl::reserved_vector<TUniqueId, 1024>& list, CStateManager& mgr) {
for (CEntity* ent : mgr.GetAllObjectList()) {
TCastToPtr<CScriptTrigger> trig = ent;
if (trig && (trig->GetTriggerFlags() & ETriggerFlags::BlockEnvironmentalEffects) != ETriggerFlags::None) {
if (trig && True(trig->GetTriggerFlags() & ETriggerFlags::BlockEnvironmentalEffects)) {
list.push_back(ent->GetUniqueId());
} else if (TCastToPtr<CScriptWater> water = ent) {
list.push_back(ent->GetUniqueId());

View File

@ -98,7 +98,7 @@ class CEnvFxManager {
// bool x3c_snowflakeTextureMipBlanked = false; /* Shader simulates this texture mod */
TLockedToken<CTexture> x40_txtrEnvGradient;
rstl::reserved_vector<CEnvFxManagerGrid, 64> x50_grids;
float xb54_baseSplashRate;
float xb54_baseSplashRate = 0.f;
TLockedToken<CGenDescription> xb58_envRainSplash;
bool xb64_ = true;
TUniqueId xb68_envRainSplashId = kInvalidUniqueId;

View File

@ -1,26 +1,678 @@
#include "CFishCloud.hpp"
#include "CActorParameters.hpp"
#include "TCastTo.hpp"
#include "GameGlobalObjects.hpp"
#include "CSimplePool.hpp"
#include "Weapon/CWeapon.hpp"
#include "CStateManager.hpp"
#include "World/CWorld.hpp"
#include "World/CPlayer.hpp"
#include "Graphics/CVertexMorphEffect.hpp"
#include "Graphics/CSkinnedModel.hpp"
#include "Graphics/CBooRenderer.hpp"
namespace urde {
CFishCloud::CFishCloud(TUniqueId uid, bool active, std::string_view name, const CEntityInfo& info,
const zeus::CVector3f& scale, const zeus::CTransform& xf, CModelData&& mData,
const CAnimRes& aRes, u32 w1, float f1, float f2, float f3, float f4, float f5, float f6,
float f7, float f8, float f9, float f10, float f11, float f12, float f13, u32 w2,
const zeus::CColor& color, bool b1, float f14, CAssetId part1, u32 w3, CAssetId part2, u32 w4,
CAssetId part3, u32 w5, CAssetId part4, u32 w6, u32 w7, bool b2, bool b3)
: CActor(uid, active, name, info, xf, std::move(mData), {EMaterialTypes::NoStepLogic}, CActorParameters::None(),
kInvalidUniqueId) {}
const CAnimRes& aRes, u32 numBoids, float speed, float separationRadius, float cohesionMagnitude,
float alignmentWeight, float separationMagnitude, float weaponRepelMagnitude,
float playerRepelMagnitude, float containmentMagnitude, float scatterVel, float maxScatterAngle,
float weaponRepelDampingSpeed, float playerRepelDampingSpeed, float containmentRadius,
u32 updateShift, const zeus::CColor& color, bool killable, float weaponKillRadius,
CAssetId part1, u32 partCount1, CAssetId part2, u32 partCount2, CAssetId part3, u32 partCount3,
CAssetId part4, u32 partCount4, u32 deathSfx, bool repelFromThreats, bool hotInThermal)
: CActor(uid, active, name, info, xf, std::move(mData), {EMaterialTypes::NoStepLogic},
CActorParameters::None().HotInThermal(hotInThermal), kInvalidUniqueId)
, x11c_updateMask(u32((1 << updateShift) - 1))
, x120_scale(scale)
, x130_speed(speed)
, x134_numBoids(numBoids)
, x138_separationRadius(separationRadius)
, x13c_cohesionMagnitude(cohesionMagnitude)
, x140_alignmentWeight(alignmentWeight)
, x144_separationMagnitude(separationMagnitude)
, x148_weaponRepelMagnitude(weaponRepelMagnitude)
, x14c_playerRepelMagnitude(playerRepelMagnitude)
, x150_scatterVel(scatterVel)
, x154_maxScatterAngle(maxScatterAngle)
, x158_containmentMagnitude(containmentMagnitude)
, x15c_playerRepelDampingSpeed(playerRepelDampingSpeed)
, x160_weaponRepelDampingSpeed(weaponRepelDampingSpeed)
, x164_playerRepelDamping(playerRepelDampingSpeed)
, x168_weaponRepelDamping(weaponRepelDampingSpeed)
, x16c_color(color)
, x170_weaponKillRadius(weaponKillRadius)
, x174_containmentRadius(containmentRadius)
, x234_deathSfx(deathSfx != 0xffffffff ? CSfxManager::TranslateSFXID(u16(deathSfx & 0xffff)) : u16(0xffff)) {
x250_28_killable = killable;
x250_29_repelFromThreats = repelFromThreats;
x108_modifierSources.reserve(10);
x250_25_worldSpace = true; // The result of a close_enough paradox (weird inlined test?)
if (aRes.GetId().IsValid()) {
x1b0_models.emplace_back(new CModelData(aRes));
x1b0_models.emplace_back(new CModelData(aRes));
x1b0_models.emplace_back(new CModelData(aRes));
x1b0_models.emplace_back(new CModelData(aRes));
x250_27_validModel = true;
}
if (part1.IsValid())
x1c4_particleDescs.push_back(g_SimplePool->GetObj({FOURCC('PART'), part1}));
if (part2.IsValid())
x1c4_particleDescs.push_back(g_SimplePool->GetObj({FOURCC('PART'), part2}));
if (part3.IsValid())
x1c4_particleDescs.push_back(g_SimplePool->GetObj({FOURCC('PART'), part3}));
if (part4.IsValid())
x1c4_particleDescs.push_back(g_SimplePool->GetObj({FOURCC('PART'), part4}));
for (const auto& p : x1c4_particleDescs) {
x1f8_particleGens.emplace_back(new CElementGen(p));
x1f8_particleGens.back()->SetParticleEmission(false);
}
x21c_deathParticleCounts.push_back(partCount1);
x21c_deathParticleCounts.push_back(partCount2);
x21c_deathParticleCounts.push_back(partCount3);
x21c_deathParticleCounts.push_back(partCount4);
zeus::CAABox aabb = GetBoundingBox();
x238_partitionPitch = (aabb.max - aabb.min) / 7.f;
x244_ooPartitionPitch = 1.f / x238_partitionPitch;
}
void CFishCloud::Accept(IVisitor& visitor) { visitor.Visit(this); }
void CFishCloud::RemoveRepulsor(TUniqueId) {}
void CFishCloud::UpdateParticles(float dt) {
for (auto& p : x1f8_particleGens)
p->Update(dt);
}
void CFishCloud::RemoveAttractor(TUniqueId) {}
void CFishCloud::UpdatePartitionList() {
xf8_boidPartitionLists.clear();
xf8_boidPartitionLists.resize(xf8_boidPartitionLists.capacity());
auto aabb = GetBoundingBox();
for (auto& b : xe8_boids) {
zeus::CVector3f idxs = (b.x0_pos - aabb.min) * x244_ooPartitionPitch;
int idx = int(idxs.x()) + int(idxs.y()) * 7 + int(idxs.z()) * 49;
if (idx >= 0 && idx < 343) {
b.x1c_next = xf8_boidPartitionLists[idx];
xf8_boidPartitionLists[idx] = &b;
}
}
}
void CFishCloud::AddRepulsor(TUniqueId, float, float) {}
bool CFishCloud::PointInBox(const zeus::CAABox& aabb, const zeus::CVector3f& point) const {
if (!x250_25_worldSpace)
return aabb.pointInside(point);
return GetUntransformedBoundingBox().pointInside(GetTransform().transposeRotate(point - GetTranslation()));
}
void CFishCloud::AddAttractor(TUniqueId, float, float) {}
zeus::CPlane CFishCloud::FindClosestPlane(const zeus::CAABox& aabb, const zeus::CVector3f& point) const {
if (!x250_25_worldSpace) {
float minDist = FLT_MAX;
zeus::CAABox::EBoxFaceId minFace = zeus::CAABox::EBoxFaceId::YMin;
for (int i = 0; i < 6; ++i) {
auto tri = aabb.getTri(zeus::CAABox::EBoxFaceId(i), 0);
float dist = zeus::CPlane(tri.x10_[0], tri.x10_[2], tri.x10_[1]).pointToPlaneDist(point);
if (dist >= 0.f && dist < minDist) {
minDist = dist;
minFace = zeus::CAABox::EBoxFaceId(i);
}
}
auto tri = aabb.getTri(minFace, 0);
return zeus::CPlane(tri.x10_[0], tri.x10_[2], tri.x10_[1]);
} else {
auto unPoint = GetTransform().transposeRotate(point - GetTranslation());
auto unAabb = GetUntransformedBoundingBox();
float minDist = FLT_MAX;
zeus::CAABox::EBoxFaceId minFace = zeus::CAABox::EBoxFaceId::YMin;
for (int i = 0; i < 6; ++i) {
auto tri = unAabb.getTri(zeus::CAABox::EBoxFaceId(i), 0);
float dist = zeus::CPlane(tri.x10_[0], tri.x10_[2], tri.x10_[1]).pointToPlaneDist(unPoint);
if (dist >= 0.f && dist < minDist) {
minDist = dist;
minFace = zeus::CAABox::EBoxFaceId(i);
}
}
auto tri = unAabb.getTri(minFace, 0);
return zeus::CPlane(GetTransform() * tri.x10_[0], GetTransform() * tri.x10_[2], GetTransform() * tri.x10_[1]);
}
}
CFishCloud::CBoid* CFishCloud::GetListAt(const zeus::CVector3f& pos) {
zeus::CAABox aabb = GetBoundingBox();
zeus::CVector3f ints = (pos - aabb.min) * x244_ooPartitionPitch;
int idx = int(ints.x()) + int(ints.y()) * 7 + int(ints.z()) * 49;
if (idx < 0 || idx >= 343)
return nullptr;
return xf8_boidPartitionLists[idx];
}
void CFishCloud::BuildBoidNearList(const zeus::CVector3f& pos, float radius,
rstl::reserved_vector<CBoid*, 25>& nearList) {
float radiusSq = radius * radius;
CBoid* b = GetListAt(pos);
while (b && nearList.size() < 25) {
if (b->x20_active) {
float distSq = (b->GetTranslation() - pos).magSquared();
if (distSq != 0.f && distSq < radiusSq)
nearList.push_back(b);
}
b = b->x1c_next;
}
}
void CFishCloud::BuildBoidNearPartitionList(const zeus::CVector3f& pos, float radius,
rstl::reserved_vector<CBoid*, 25>& nearList) {
float radiusSq = radius * radius;
zeus::CAABox aabb = GetBoundingBox();
float x = std::max(radius * x244_ooPartitionPitch.x(), float(x238_partitionPitch.x()));
float y = std::max(radius * x244_ooPartitionPitch.y(), float(x238_partitionPitch.y()));
float z = std::max(radius * x244_ooPartitionPitch.z(), float(x238_partitionPitch.z()));
float nx = 0.01f - x;
float ny = 0.01f - y;
float nz = 0.01f - z;
for (float lnx = nx; lnx < x; lnx += x238_partitionPitch.x()) {
float cx = lnx + pos.x();
if (cx < aabb.min.x())
continue;
if (cx >= aabb.max.x())
break;
for (float lny = ny; lny < y; lny += x238_partitionPitch.y()) {
float cy = lny + pos.y();
if (cy < aabb.min.y())
continue;
if (cy >= aabb.max.y())
break;
for (float lnz = nz; lnz < z; lnz += x238_partitionPitch.z()) {
float cz = lnz + pos.z();
if (cz < aabb.min.z())
continue;
if (cz >= aabb.max.z())
break;
zeus::CVector3f ints = (zeus::CVector3f(cx, cy, cz) - aabb.min) * x244_ooPartitionPitch;
int idx = int(ints.x()) + int(ints.y()) * 7 + int(ints.z()) * 49;
if (idx < 0)
continue;
if (idx < 343) {
CBoid* boid = xf8_boidPartitionLists[idx];
while (boid) {
if (boid->x20_active) {
float distSq = (boid->x0_pos - pos).magSquared();
if (distSq != 0.f && distSq < radiusSq) {
nearList.push_back(boid);
if (nearList.size() == 25)
return;
}
}
boid = boid->x1c_next;
}
}
}
}
}
}
void CFishCloud::PlaceBoid(CStateManager& mgr, CBoid& boid, const zeus::CAABox& aabb) {
auto plane = FindClosestPlane(aabb, boid.x0_pos);
boid.x0_pos -= plane.pointToPlaneDist(boid.x0_pos) * plane.normal() + 0.0001f * plane.normal();
boid.xc_vel.y() = mgr.GetActiveRandom()->Float() - 0.5f;
boid.xc_vel.x() = mgr.GetActiveRandom()->Float() - 0.5f;
boid.xc_vel.z() = 0.f;
if (!x250_25_worldSpace) {
if (!aabb.pointInside(boid.x0_pos)) {
boid.x0_pos.z() = mgr.GetActiveRandom()->Float() * (aabb.max.z() - aabb.min.z()) + aabb.min.z();
boid.x0_pos.y() = mgr.GetActiveRandom()->Float() * (aabb.max.y() - aabb.min.y()) + aabb.min.y();
boid.x0_pos.x() = mgr.GetActiveRandom()->Float() * (aabb.max.x() - aabb.min.x()) + aabb.min.x();
}
} else {
if (!PointInBox(aabb, boid.x0_pos)) {
auto unAabb = GetUntransformedBoundingBox();
boid.x0_pos.z() = mgr.GetActiveRandom()->Float() * (unAabb.max.z() - unAabb.min.z()) + unAabb.min.z();
boid.x0_pos.y() = mgr.GetActiveRandom()->Float() * (unAabb.max.y() - unAabb.min.y()) + unAabb.min.y();
boid.x0_pos.x() = mgr.GetActiveRandom()->Float() * (unAabb.max.x() - unAabb.min.x()) + unAabb.min.x();
boid.x0_pos = GetTransform() * boid.x0_pos;
}
}
}
void CFishCloud::ApplySeparation(CBoid& boid, const rstl::reserved_vector<CBoid*, 25>& nearList) const {
if (nearList.empty())
return;
float minDist = FLT_MAX;
zeus::CVector3f pos;
for (CBoid* b : nearList) {
float dist = (boid.GetTranslation() - b->GetTranslation()).magSquared();
if (dist < minDist) {
minDist = dist;
pos = b->GetTranslation();
}
}
ApplySeparation(boid, pos, x138_separationRadius, x144_separationMagnitude);
}
void CFishCloud::ApplySeparation(CBoid& boid, const zeus::CVector3f& separateFrom,
float separationRadius, float separationMagnitude) const {
zeus::CVector3f delta = boid.GetTranslation() - separateFrom;
if (delta.canBeNormalized()) {
float deltaDistSq = delta.magSquared();
float capDeltaDistSq = separationRadius * separationRadius;
if (deltaDistSq < capDeltaDistSq)
boid.xc_vel += (1.f - deltaDistSq / capDeltaDistSq) * delta.normalized() * separationMagnitude;
}
}
void CFishCloud::ApplyCohesion(CBoid& boid, const rstl::reserved_vector<CBoid*, 25>& nearList) const {
if (nearList.empty())
return;
zeus::CVector3f avg;
for (CBoid* b : nearList)
avg += b->GetTranslation();
avg = avg / float(nearList.size());
ApplyCohesion(boid, avg, x138_separationRadius, x13c_cohesionMagnitude);
}
void CFishCloud::ApplyCohesion(CBoid& boid, const zeus::CVector3f& cohesionFrom,
float cohesionRadius, float cohesionMagnitude) const {
zeus::CVector3f delta = cohesionFrom - boid.GetTranslation();
if (delta.canBeNormalized()) {
float distSq = delta.magSquared();
float capDistSq = cohesionRadius * cohesionRadius;
boid.xc_vel += ((distSq > capDistSq) ? 1.f : distSq / capDistSq) * delta.normalized() * cohesionMagnitude;
}
}
void CFishCloud::ApplyAlignment(CBoid& boid, const rstl::reserved_vector<CBoid*, 25>& nearList) const {
if (nearList.empty())
return;
zeus::CVector3f avg;
for (CBoid* b : nearList)
avg += b->xc_vel;
avg = avg / float(nearList.size());
boid.xc_vel += zeus::CVector3f::getAngleDiff(boid.xc_vel, avg) / M_PIF *
(avg * x140_alignmentWeight);
}
void CFishCloud::ApplyAttraction(CBoid& boid, const zeus::CVector3f& attractTo,
float attractionRadius, float attractionMagnitude) const {
zeus::CVector3f delta = attractTo - boid.GetTranslation();
if (delta.canBeNormalized()) {
float distSq = delta.magSquared();
float capDistSq = attractionRadius * attractionRadius;
boid.xc_vel += ((distSq > capDistSq) ? 0.f : (1.f - distSq / capDistSq)) * delta.normalized() * attractionMagnitude;
}
}
void CFishCloud::ApplyRepulsion(CBoid& boid, const zeus::CVector3f& attractTo,
float repulsionRadius, float repulsionMagnitude) const {
ApplySeparation(boid, attractTo, repulsionRadius, repulsionMagnitude);
}
void CFishCloud::ApplySwirl(CBoid& boid, const zeus::CVector3f& swirlPoint, bool clockwise,
float magnitude, float radius) const {
zeus::CVector3f delta = boid.x0_pos - swirlPoint;
float deltaMag = delta.magnitude();
zeus::CVector3f alignVec;
if (clockwise)
alignVec = delta.normalized().cross(zeus::skUp);
else
alignVec = zeus::skUp.cross(delta / deltaMag);
float weight = deltaMag > radius ? 0.f : 1.f - deltaMag / radius;
boid.xc_vel += zeus::CVector3f::getAngleDiff(boid.xc_vel, alignVec) / M_PIF *
weight * (magnitude * alignVec);
}
void CFishCloud::ApplyContainment(CBoid& boid, const zeus::CAABox& aabb) const {
if (boid.xc_vel.canBeNormalized()) {
if (!PointInBox(aabb, boid.xc_vel.normalized() * x130_speed * x174_containmentRadius + boid.x0_pos)) {
ApplyAttraction(boid, aabb.center(), 100000.f, x158_containmentMagnitude);
}
}
}
void CFishCloud::ScatterBoid(CStateManager& mgr, CBoid& b) const {
float angle = (mgr.GetActiveRandom()->Float() - 0.5f) * M_PIF * x154_maxScatterAngle;
float cosAngle = std::cos(angle);
float sinAngle = std::sin(angle);
b.xc_vel.x() += x150_scatterVel * (b.xc_vel.y() * sinAngle + b.xc_vel.x() * cosAngle);
b.xc_vel.y() += x150_scatterVel * (b.xc_vel.y() * cosAngle + b.xc_vel.x() * sinAngle);
}
void CFishCloud::Think(float dt, CStateManager& mgr) {
if (!GetActive())
return;
const CGameArea* area = mgr.GetWorld()->GetAreaAlways(x4_areaId);
auto occState = area->IsPostConstructed() ? area->GetOcclusionState() : CGameArea::EOcclusionState::Occluded;
if (occState == CGameArea::EOcclusionState::Visible) {
x168_weaponRepelDamping = std::max(0.f, x168_weaponRepelDamping - x160_weaponRepelDampingSpeed * dt * 0.1f);
if (x250_26_enableWeaponRepelDamping)
x168_weaponRepelDamping = std::min(x160_weaponRepelDampingSpeed * dt + x168_weaponRepelDamping,
x148_weaponRepelMagnitude);
x164_playerRepelDamping = std::max(0.f, x164_playerRepelDamping - x15c_playerRepelDampingSpeed * dt * 0.1f);
if (x250_30_enablePlayerRepelDamping)
x164_playerRepelDamping = std::min(x15c_playerRepelDampingSpeed * dt + x164_playerRepelDamping,
x14c_playerRepelMagnitude);
x250_26_enableWeaponRepelDamping = false;
x250_30_enablePlayerRepelDamping = false;
++x118_thinkCounter;
UpdateParticles(dt);
UpdatePartitionList();
zeus::CAABox aabb = GetBoundingBox();
int idx = 0;
for (auto& b : xe8_boids) {
if (b.x20_active && (idx & x11c_updateMask) == (x118_thinkCounter & x11c_updateMask)) {
rstl::reserved_vector<CBoid*, 25> nearList;
if (x250_31_updateWithoutPartitions)
BuildBoidNearList(b.x0_pos, x138_separationRadius, nearList);
else
BuildBoidNearPartitionList(b.x0_pos, x138_separationRadius, nearList);
for (int i = 0; i < 5; ++i) {
switch (i) {
case 1:
ApplySeparation(b, nearList);
break;
case 2:
if (!x250_24_randomMovement || mgr.GetActiveRandom()->Float() > x12c_randomMovementTimer)
ApplyCohesion(b, nearList);
break;
case 3:
if (!x250_24_randomMovement || mgr.GetActiveRandom()->Float() > x12c_randomMovementTimer)
ApplyAlignment(b, nearList);
break;
case 4:
ScatterBoid(mgr, b);
break;
default:
break;
}
if (b.xc_vel.magSquared() > 3.2f)
break;
}
if (!x250_24_randomMovement && b.xc_vel.magSquared() < 3.2f) {
for (auto& m : x108_modifierSources) {
if (TCastToPtr<CActor> act = mgr.ObjectById(m.x0_source)) {
if (m.xd_isSwirl) {
ApplySwirl(b, act->GetTranslation(), m.xc_isRepulsor, m.x8_priority, m.x4_radius);
} else if (m.xc_isRepulsor) {
ApplyRepulsion(b, act->GetTranslation(), m.x4_radius, m.x8_priority);
} else {
ApplyAttraction(b, act->GetTranslation(), m.x4_radius, m.x8_priority);
}
} else {
if (m.xc_isRepulsor)
RemoveRepulsor(m.x0_source);
else
RemoveAttractor(m.x0_source);
break;
}
}
}
}
++idx;
}
for (auto& b : xe8_boids) {
if (b.x20_active) {
ApplyContainment(b, aabb);
float velMag = b.xc_vel.magnitude();
if (!zeus::close_enough(velMag, 0.f))
b.xc_vel = b.xc_vel / velMag;
b.xc_vel.z() *= 0.99f;
}
}
if (x12c_randomMovementTimer > 0.f) {
x12c_randomMovementTimer -= dt;
} else {
x12c_randomMovementTimer = 0.f;
x250_24_randomMovement = false;
}
for (auto& b : xe8_boids) {
if (b.x20_active) {
b.x0_pos += b.xc_vel * dt * x130_speed;
if (!PointInBox(aabb, b.x0_pos))
PlaceBoid(mgr, b, aabb);
}
}
if (x250_27_validModel) {
for (auto& m : x1b0_models) {
m->AnimationData()->SetPlaybackRate(1.f);
m->AdvanceAnimation(dt, mgr, x4_areaId, true);
}
}
}
}
void CFishCloud::CreatePartitionList() {
xf8_boidPartitionLists.reserve(343);
}
void CFishCloud::AllocateSkinnedModels(CStateManager& mgr, CModelData::EWhichModel which) {
int idx = 0;
for (auto& m : x1b0_models) {
m->EnableLooping(true);
m->AdvanceAnimation(
m->AnimationData()->GetAnimTimeRemaining("Whole Body"sv) * 0.25f * idx, mgr, x4_areaId, true);
++idx;
}
x230_whichModel = which;
}
void CFishCloud::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr) {
CActor::AcceptScriptMsg(msg, sender, mgr);
switch (msg) {
case EScriptObjectMessage::Registered: {
xe8_boids.reserve(x134_numBoids);
zeus::CAABox aabb = GetUntransformedBoundingBox();
zeus::CVector3f extent = aabb.max - aabb.min;
zeus::CVector3f randPoint;
for (int i = 0; i < x134_numBoids; ++i) {
randPoint.z() = mgr.GetActiveRandom()->Float() * extent.z() + aabb.min.z();
randPoint.y() = mgr.GetActiveRandom()->Float() * extent.y() + aabb.min.y();
randPoint.x() = mgr.GetActiveRandom()->Float() * extent.x() + aabb.min.x();
zeus::CVector3f vel;
vel.y() = mgr.GetActiveRandom()->Float() - 0.5f;
vel.x() = mgr.GetActiveRandom()->Float() - 0.5f;
xe8_boids.emplace_back(x34_transform * randPoint, vel,
0.2f * std::pow(mgr.GetActiveRandom()->Float(), 7.f) + 0.9f);
}
CreatePartitionList();
if (x250_27_validModel)
AllocateSkinnedModels(mgr, CModelData::EWhichModel::Normal);
break;
}
default:
break;
}
}
void CFishCloud::PreRender(CStateManager& mgr, const zeus::CFrustum& frustum) {
CActor::PreRender(mgr, frustum);
if (x250_27_validModel) {
for (auto& m : x1b0_models) {
m->AnimationData()->PreRender();
}
}
xe4_30_outOfFrustum = false;
}
void CFishCloud::AddParticlesToRenderer() const {
for (const auto& p : x1f8_particleGens)
g_Renderer->AddParticleGen(*p);
}
void CFishCloud::RenderBoid(int idx, const CBoid& boid, u32& drawMask,
bool thermalHot, const CModelFlags& flags) const {
u32 modelIndex = idx & 0x3;
CModelData& mData = *x1b0_models[modelIndex];
CSkinnedModel& model = mData.PickAnimatedModel(CModelData::EWhichModel::Normal);
if (!model.GetModelInst()->TryLockTextures())
return;
u32 thisDrawMask = 1u << modelIndex;
if (drawMask & thisDrawMask) {
drawMask &= ~thisDrawMask;
mData.AnimationData()->BuildPose();
}
model.GetModelInst()->SetAmbientColor(zeus::skWhite);
CGraphics::SetModelMatrix(zeus::lookAt(boid.x0_pos, boid.x0_pos + boid.xc_vel));
if (thermalHot) {
CModelFlags thermFlags(0, 0, 3, zeus::skWhite);
mData.RenderThermal(zeus::skWhite, zeus::CColor(0.f, 0.25f), thermFlags);
} else {
mData.AnimationData()->Render(model, flags, {}, nullptr);
}
}
void CFishCloud::Render(const CStateManager& mgr) const {
if (!GetActive())
return;
bool thermalHot = mgr.GetThermalDrawFlag() == EThermalDrawFlag::Hot;
CModelFlags flags(0, 0, 3, zeus::skWhite);
if (mgr.GetPlayerState()->GetActiveVisor(mgr) == CPlayerState::EPlayerVisor::XRay)
flags = CModelFlags(5, 0, 3, x16c_color);
else
flags = CModelFlags(1, 0, 3, x16c_color);
AddParticlesToRenderer();
if (x250_27_validModel) {
// Ambient white
int idx = 0;
u32 drawMask = 0xffffffff;
for (const auto& b : xe8_boids) {
if (b.x20_active)
RenderBoid(idx, b, drawMask, thermalHot, flags);
++idx;
}
} else {
CGraphics::SetModelMatrix(zeus::CTransform());
for (const auto& b : xe8_boids) {
if (b.x20_active) {
x64_modelData->SetScale(zeus::CVector3f(b.x18_scale));
x64_modelData->Render(mgr, zeus::lookAt(b.x0_pos, b.x0_pos + b.xc_vel), nullptr, flags);
}
}
}
}
void CFishCloud::CalculateRenderBounds() {
x9c_renderBounds = GetBoundingBox();
}
rstl::optional<zeus::CAABox> CFishCloud::GetTouchBounds() const {
return {GetBoundingBox()};
}
void CFishCloud::CreateBoidDeathParticle(CBoid& b) const {
auto it = x21c_deathParticleCounts.begin();
for (auto& p : x1f8_particleGens) {
p->SetParticleEmission(true);
p->SetTranslation(b.x0_pos);
p->ForceParticleCreation(*it);
p->SetParticleEmission(false);
++it;
}
}
void CFishCloud::KillBoid(CBoid& b) const {
b.x20_active = false;
CreateBoidDeathParticle(b);
CAudioSys::C3DEmitterParmData parmData = {};
parmData.x0_pos = b.x0_pos;
parmData.x18_maxDist = 250.f;
parmData.x1c_distComp = 0.1f;
parmData.x20_flags = 0x1;
parmData.x24_sfxId = x234_deathSfx;
parmData.x26_maxVol = 1.f;
parmData.x27_minVol = 0.157f;
parmData.x29_prio = 0x7f;
CSfxManager::AddEmitter(parmData, true, 0x7f, false, x4_areaId);
}
void CFishCloud::Touch(CActor& other, CStateManager& mgr) {
CActor::Touch(other, mgr);
if (TCastToPtr<CWeapon> weap = other) {
if (!x250_26_enableWeaponRepelDamping && x250_29_repelFromThreats) {
int idx = 0;
for (auto& b : xe8_boids) {
if ((idx & 0x3) == (x118_thinkCounter & 0x3))
ApplyRepulsion(b, weap->GetTranslation(), 8.f, x148_weaponRepelMagnitude - x168_weaponRepelDamping);
++idx;
}
}
x250_26_enableWeaponRepelDamping = true;
if (x250_28_killable) {
if (auto tb = weap->GetTouchBounds()) {
for (auto& b : xe8_boids) {
if (b.x20_active &&
tb->intersects(zeus::CAABox(weap->GetTranslation() - x170_weaponKillRadius,
weap->GetTranslation() + x170_weaponKillRadius)))
KillBoid(b);
}
}
}
}
if (x250_29_repelFromThreats) {
if (TCastToPtr<CPlayer> player = other) {
zeus::CVector3f playerPos = player->GetTranslation();
for (auto& b : xe8_boids) {
zeus::CVector3f adjPlayerPos = playerPos;
float zDelta = b.x0_pos.z() - adjPlayerPos.z();
if (zDelta > 0.f && zDelta < 2.3f)
adjPlayerPos.z() = float(b.x0_pos.z());
adjPlayerPos.x() += mgr.GetActiveRandom()->Float() * 0.2f - 0.1f;
adjPlayerPos.y() += mgr.GetActiveRandom()->Float() * 0.2f - 0.1f;
ApplyRepulsion(b, adjPlayerPos, 8.f, x14c_playerRepelMagnitude - x164_playerRepelDamping);
}
}
x250_30_enablePlayerRepelDamping = true;
}
}
zeus::CAABox CFishCloud::GetUntransformedBoundingBox() const {
zeus::CVector3f extent = x120_scale * 0.75f;
return zeus::CAABox(-extent, extent);
}
zeus::CAABox CFishCloud::GetBoundingBox() const {
return GetUntransformedBoundingBox().getTransformedAABox(x34_transform);
}
void CFishCloud::RemoveRepulsor(TUniqueId sourceId) {
CModifierSource source(sourceId, true, false, 0.f, 0.f);
auto it = rstl::binary_find(x108_modifierSources.begin(), x108_modifierSources.end(), source);
if (it != x108_modifierSources.end())
x108_modifierSources.erase(it);
}
void CFishCloud::RemoveAttractor(TUniqueId sourceId) {
CModifierSource source(sourceId, false, false, 0.f, 0.f);
auto it = rstl::binary_find(x108_modifierSources.begin(), x108_modifierSources.end(), source);
if (it != x108_modifierSources.end())
x108_modifierSources.erase(it);
}
bool CFishCloud::AddRepulsor(TUniqueId sourceId, bool swirl, float radius, float priority) {
CModifierSource source(sourceId, true, swirl, radius, priority);
auto it = rstl::binary_find(x108_modifierSources.begin(), x108_modifierSources.end(), source);
if (it != x108_modifierSources.end()) {
it->x4_radius = radius;
it->x8_priority = priority;
return true;
} else if (x108_modifierSources.size() < x108_modifierSources.capacity()) {
x108_modifierSources.insert(std::lower_bound(
x108_modifierSources.begin(), x108_modifierSources.end(), source), source);
return true;
}
return false;
}
bool CFishCloud::AddAttractor(TUniqueId sourceId, bool swirl, float radius, float priority) {
CModifierSource source(sourceId, false, swirl, radius, priority);
auto it = rstl::binary_find(x108_modifierSources.begin(), x108_modifierSources.end(), source);
if (it != x108_modifierSources.end()) {
it->x4_radius = radius;
it->x8_priority = priority;
return true;
} else if (x108_modifierSources.size() < x108_modifierSources.capacity()) {
x108_modifierSources.insert(std::lower_bound(
x108_modifierSources.begin(), x108_modifierSources.end(), source), source);
return true;
}
return false;
}
} // namespace urde

View File

@ -1,41 +1,152 @@
#pragma once
#include "CActor.hpp"
#include "Particle/CElementGen.hpp"
namespace urde {
class CFishCloud : public CActor {
class CModifierSource {
TUniqueId x0_source;
float x4_;
float x8_;
bool xc_;
bool xd_;
class CBoid {
friend class CFishCloud;
zeus::CVector3f x0_pos;
zeus::CVector3f xc_vel;
float x18_scale;
CBoid* x1c_next = nullptr;
bool x20_active = true;
public:
CModifierSource(TUniqueId, bool, float, float);
void SetAffectPriority(float);
void SetAffectRadius(float);
float GetAffectPriority() const;
float GetAffectRadius() const;
bool IsRepulsor();
TUniqueId GetSource() const;
bool operator<(const CModifierSource& other) const { return x0_source < other.x0_source; }
CBoid(const zeus::CVector3f& pos, const zeus::CVector3f& vel, float scale)
: x0_pos(pos), xc_vel(vel), x18_scale(scale) {}
zeus::CVector3f& Translation() { return x0_pos; }
const zeus::CVector3f& GetTranslation() const { return x0_pos; }
};
class CModifierSource {
friend class CFishCloud;
TUniqueId x0_source;
float x4_radius;
float x8_priority;
bool xc_isRepulsor;
bool xd_isSwirl;
public:
CModifierSource(TUniqueId source, bool repulsor, bool swirl, float radius, float priority)
: x0_source(source), x4_radius(radius), x8_priority(priority), xc_isRepulsor(repulsor), xd_isSwirl(swirl) {}
void SetAffectPriority(float p) { x8_priority = p; }
void SetAffectRadius(float r) { x4_radius = r; }
float GetAffectPriority() const { return x8_priority; }
float GetAffectRadius() const { return x4_radius; }
bool IsRepulsor() const { return xc_isRepulsor; }
bool IsSwirl() const { return xd_isSwirl; }
TUniqueId GetSource() const { return x0_source; }
bool operator<(const CModifierSource& other) const {
if (x0_source == other.x0_source)
return xc_isRepulsor < other.xc_isRepulsor;
return x0_source < other.x0_source;
}
};
std::vector<CBoid> xe8_boids;
std::vector<CBoid*> xf8_boidPartitionLists;
std::vector<CModifierSource> x108_modifierSources;
u32 x118_thinkCounter = 0;
u32 x11c_updateMask;
zeus::CVector3f x120_scale;
float x12c_randomMovementTimer = 0.f;
float x130_speed;
u32 x134_numBoids;
float x138_separationRadius;
float x13c_cohesionMagnitude;
float x140_alignmentWeight;
float x144_separationMagnitude;
float x148_weaponRepelMagnitude;
float x14c_playerRepelMagnitude;
float x150_scatterVel;
float x154_maxScatterAngle;
float x158_containmentMagnitude;
float x15c_playerRepelDampingSpeed;
float x160_weaponRepelDampingSpeed;
float x164_playerRepelDamping;
float x168_weaponRepelDamping;
zeus::CColor x16c_color;
float x170_weaponKillRadius;
float x174_containmentRadius;
/* Used to be position and normal arrays */
//rstl::reserved_vector<std::unique_ptr<float[]>, 4> x178_;
//rstl::reserved_vector<std::unique_ptr<float[]>, 4> x19c_;
rstl::reserved_vector<std::shared_ptr<CModelData>, 4> x1b0_models;
rstl::reserved_vector<TLockedToken<CGenDescription>, 4> x1c4_particleDescs;
rstl::reserved_vector<std::unique_ptr<CElementGen>, 4> x1f8_particleGens;
rstl::reserved_vector<u32, 4> x21c_deathParticleCounts;
CModelData::EWhichModel x230_whichModel;
u16 x234_deathSfx;
zeus::CVector3f x238_partitionPitch;
zeus::CVector3f x244_ooPartitionPitch;
union {
struct {
bool x250_24_randomMovement : 1;
bool x250_25_worldSpace : 1;
bool x250_26_enableWeaponRepelDamping : 1;
bool x250_27_validModel : 1;
bool x250_28_killable : 1;
bool x250_29_repelFromThreats : 1;
bool x250_30_enablePlayerRepelDamping : 1;
bool x250_31_updateWithoutPartitions : 1;
};
u32 _dummy = 0;
};
void UpdateParticles(float dt);
void UpdatePartitionList();
bool PointInBox(const zeus::CAABox& aabb, const zeus::CVector3f& point) const;
zeus::CPlane FindClosestPlane(const zeus::CAABox& aabb, const zeus::CVector3f& point) const;
CBoid* GetListAt(const zeus::CVector3f& pos);
void BuildBoidNearList(const zeus::CVector3f& pos, float radius, rstl::reserved_vector<CBoid*, 25>& nearList);
void BuildBoidNearPartitionList(const zeus::CVector3f& pos, float radius,
rstl::reserved_vector<CBoid*, 25>& nearList);
void PlaceBoid(CStateManager& mgr, CBoid& boid, const zeus::CAABox& aabb);
void ApplySeparation(CBoid& boid, const rstl::reserved_vector<CBoid*, 25>& nearList) const;
void ApplySeparation(CBoid& boid, const zeus::CVector3f& separateFrom,
float separationRadius, float separationMagnitude) const;
void ApplyCohesion(CBoid& boid, const rstl::reserved_vector<CBoid*, 25>& nearList) const;
void ApplyCohesion(CBoid& boid, const zeus::CVector3f& cohesionFrom,
float cohesionRadius, float cohesionMagnitude) const;
void ApplyAlignment(CBoid& boid, const rstl::reserved_vector<CBoid*, 25>& nearList) const;
void ApplyAttraction(CBoid& boid, const zeus::CVector3f& attractTo,
float attractionRadius, float attractionMagnitude) const;
void ApplyRepulsion(CBoid& boid, const zeus::CVector3f& attractTo,
float repulsionRadius, float repulsionMagnitude) const;
void ApplySwirl(CBoid& boid, const zeus::CVector3f& swirlPoint, bool clockwise, float magnitude, float radius) const;
void ApplyContainment(CBoid& boid, const zeus::CAABox& aabb) const;
void ScatterBoid(CStateManager& mgr, CBoid& b) const;
void CreateBoidDeathParticle(CBoid& b) const;
void KillBoid(CBoid& b) const;
zeus::CAABox GetUntransformedBoundingBox() const;
zeus::CAABox GetBoundingBox() const;
void CreatePartitionList();
void AllocateSkinnedModels(CStateManager& mgr, CModelData::EWhichModel which);
void AddParticlesToRenderer() const;
void RenderBoid(int idx, const CBoid& boid, u32& drawMask, bool thermalHot, const CModelFlags& flags) const;
public:
CFishCloud(TUniqueId uid, bool active, std::string_view name, const CEntityInfo& info, const zeus::CVector3f& scale,
const zeus::CTransform& xf, CModelData&& mData, const CAnimRes& aRes, u32 w1, float f1, float f2, float f3,
float f4, float f5, float f6, float f7, float f8, float f9, float f10, float f11, float f12, float f13,
u32 w2, const zeus::CColor& color, bool b1, float f14, CAssetId part1, u32 w3, CAssetId part2, u32 w4,
CAssetId part3, u32 w5, CAssetId part4, u32 w6, u32 w7, bool b2, bool b3);
CFishCloud(TUniqueId uid, bool active, std::string_view name, const CEntityInfo& info,
const zeus::CVector3f& scale, const zeus::CTransform& xf, CModelData&& mData,
const CAnimRes& aRes, u32 numBoids, float speed, float separationRadius, float cohesionMagnitude,
float alignmentWeight, float separationMagnitude, float weaponRepelMagnitude,
float playerRepelMagnitude, float containmentMagnitude, float scatterVel, float maxScatterAngle,
float weaponRepelDampingSpeed, float playerRepelDampingSpeed, float containmentRadius,
u32 updateShift, const zeus::CColor& color, bool killable, float weaponKillRadius,
CAssetId part1, u32 partCount1, CAssetId part2, u32 partCount2, CAssetId part3, u32 partCount3,
CAssetId part4, u32 partCount4, u32 deathSfx, bool repelFromThreats, bool hotInThermal);
void Accept(IVisitor& visitor);
void RemoveRepulsor(TUniqueId);
void RemoveAttractor(TUniqueId);
void AddRepulsor(TUniqueId, float, float);
void AddAttractor(TUniqueId, float, float);
void Think(float dt, CStateManager& mgr);
void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr);
void PreRender(CStateManager& mgr, const zeus::CFrustum& frustum);
void Render(const CStateManager& mgr) const;
void CalculateRenderBounds();
rstl::optional<zeus::CAABox> GetTouchBounds() const;
void Touch(CActor& other, CStateManager& mgr);
void RemoveRepulsor(TUniqueId source);
void RemoveAttractor(TUniqueId source);
bool AddRepulsor(TUniqueId source, bool swirl, float radius, float priority);
bool AddAttractor(TUniqueId source, bool swirl, float radius, float priority);
};
} // namespace urde

View File

@ -6,13 +6,14 @@
namespace urde {
CFishCloudModifier::CFishCloudModifier(TUniqueId uid, bool active, std::string_view name, const CEntityInfo& eInfo,
const zeus::CVector3f& pos, bool b2, bool b3, float f1, float f2)
const zeus::CVector3f& pos, bool isRepulsor, bool swirl, float radius,
float priority)
: CActor(uid, active, name, eInfo, zeus::CTransform::Translate(pos), CModelData::CModelDataNull(),
{EMaterialTypes::NoStepLogic}, CActorParameters::None(), kInvalidUniqueId)
, xe8_(f1)
, xec_(f2)
, xf0_isRepulsor(b2)
, xf1_(b3) {}
, xe8_radius(radius)
, xec_priority(priority)
, xf0_isRepulsor(isRepulsor)
, xf1_swirl(swirl) {}
void CFishCloudModifier::Accept(IVisitor& visitor) { visitor.Visit(this); }
@ -32,9 +33,9 @@ void CFishCloudModifier::AddSelf(CStateManager& mgr) {
if (TCastToPtr<CFishCloud> fishCloud = mgr.ObjectById(mgr.GetIdForScript(conn.x8_objId))) {
if (xf0_isRepulsor)
fishCloud->AddRepulsor(GetUniqueId(), xe8_, xec_);
fishCloud->AddRepulsor(GetUniqueId(), xf1_swirl, xe8_radius, xec_priority);
else
fishCloud->AddAttractor(GetUniqueId(), xe8_, xec_);
fishCloud->AddAttractor(GetUniqueId(), xf1_swirl, xe8_radius, xec_priority);
}
}
}

View File

@ -4,14 +4,15 @@
namespace urde {
class CFishCloudModifier : public CActor {
float xe8_;
float xec_;
float xe8_radius;
float xec_priority;
bool xf0_isRepulsor;
bool xf1_;
bool xf1_swirl;
public:
CFishCloudModifier(TUniqueId, bool, std::string_view, const CEntityInfo&, const zeus::CVector3f&, bool, bool, float,
float);
CFishCloudModifier(TUniqueId uid, bool active, std::string_view name, const CEntityInfo& eInfo,
const zeus::CVector3f& pos, bool isRepulsor, bool swirl, float radius,
float priority);
void Accept(IVisitor& visitor);
void AcceptScriptMsg(EScriptObjectMessage, TUniqueId, CStateManager&);

View File

@ -2497,7 +2497,7 @@ void CPlayer::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CState
break;
case EScriptObjectMessage::Damage:
if (TCastToPtr<CEnergyProjectile> energ = mgr.ObjectById(sender)) {
if ((energ->GetAttribField() & EProjectileAttrib::StaticInterference) != EProjectileAttrib::None) {
if (True(energ->GetAttribField() & EProjectileAttrib::StaticInterference)) {
mgr.GetPlayerState()->GetStaticInterference().AddSource(GetUniqueId(), 0.3f, energ->GetInterferenceDuration());
}
}

View File

@ -76,11 +76,11 @@ void CScriptTrigger::UpdateInhabitants(float dt, CStateManager& mgr) {
if (TCastToPtr<CActor> act = mgr.ObjectById(it->GetObjectId())) {
bool playerValid = true;
if (it->GetObjectId() == mgr.GetPlayer().GetUniqueId()) {
if ((x12c_flags & ETriggerFlags::DetectPlayer) == ETriggerFlags::None &&
if (False(x12c_flags & ETriggerFlags::DetectPlayer) &&
((mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Morphed &&
(x12c_flags & ETriggerFlags::DetectUnmorphedPlayer) != ETriggerFlags::None) ||
True(x12c_flags & ETriggerFlags::DetectUnmorphedPlayer)) ||
(mgr.GetPlayer().GetMorphballTransitionState() == CPlayer::EPlayerMorphBallState::Unmorphed &&
(x12c_flags & ETriggerFlags::DetectMorphedPlayer) != ETriggerFlags::None))) {
True(x12c_flags & ETriggerFlags::DetectMorphedPlayer)))) {
playerValid = false;
}
if (!playerValid) {
@ -114,11 +114,11 @@ void CScriptTrigger::UpdateInhabitants(float dt, CStateManager& mgr) {
if (x128_forceMagnitude > 0.f) {
if (TCastToPtr<CPhysicsActor> pact = act.GetPtr()) {
float forceMult = 1.f;
if ((x12c_flags & ETriggerFlags::UseBooleanIntersection) != ETriggerFlags::None)
if (True(x12c_flags & ETriggerFlags::UseBooleanIntersection))
forceMult = touchBounds->booleanIntersection(*actTouchBounds).volume() / actTouchBounds->volume();
zeus::CVector3f force = forceMult * x11c_forceField;
if ((x12c_flags & ETriggerFlags::UseCollisionImpulses) != ETriggerFlags::None) {
if (True(x12c_flags & ETriggerFlags::UseCollisionImpulses)) {
pact->ApplyImpulseWR(force, zeus::CAxisAngle());
pact->UseCollisionImpulses();
} else
@ -158,18 +158,18 @@ void CScriptTrigger::UpdateInhabitants(float dt, CStateManager& mgr) {
}
}
if ((x12c_flags & ETriggerFlags::DetectCamera) != ETriggerFlags::None || x148_24_detectCamera) {
if (True(x12c_flags & ETriggerFlags::DetectCamera) || x148_24_detectCamera) {
CGameCamera* cam = mgr.GetCameraManager()->GetCurrentCamera(mgr);
bool camInTrigger = GetTriggerBoundsWR().pointInside(cam->GetTranslation());
if (x148_25_camSubmerged) {
if (!camInTrigger) {
x148_25_camSubmerged = false;
if ((x12c_flags & ETriggerFlags::DetectCamera) != ETriggerFlags::None) {
if (True(x12c_flags & ETriggerFlags::DetectCamera)) {
sendExited = true;
InhabitantExited(*cam, mgr);
}
} else {
if ((x12c_flags & ETriggerFlags::DetectCamera) != ETriggerFlags::None) {
if (True(x12c_flags & ETriggerFlags::DetectCamera)) {
InhabitantIdle(*cam, mgr);
sendInside = true;
}
@ -177,7 +177,7 @@ void CScriptTrigger::UpdateInhabitants(float dt, CStateManager& mgr) {
} else {
if (camInTrigger) {
x148_25_camSubmerged = true;
if ((x12c_flags & ETriggerFlags::DetectCamera) != ETriggerFlags::None) {
if (True(x12c_flags & ETriggerFlags::DetectCamera)) {
InhabitantAdded(*cam, mgr);
SendScriptMsgs(EScriptObjectState::Entered, mgr, EScriptObjectMessage::Activate);
}
@ -215,7 +215,7 @@ void CScriptTrigger::Touch(CActor& act, CStateManager& mgr) {
ETriggerFlags testFlags = ETriggerFlags::None;
TCastToPtr<CPlayer> pl(act);
if (pl) {
if (x128_forceMagnitude > 0.f && (x12c_flags & ETriggerFlags::DetectPlayer) != ETriggerFlags::None &&
if (x128_forceMagnitude > 0.f && True(x12c_flags & ETriggerFlags::DetectPlayer) &&
mgr.GetLastTriggerId() == kInvalidUniqueId)
mgr.SetLastTriggerId(x8_uid);
@ -238,7 +238,7 @@ void CScriptTrigger::Touch(CActor& act, CStateManager& mgr) {
testFlags |= ETriggerFlags::DetectPowerBombs;
}
if ((testFlags & x12c_flags) != ETriggerFlags::None) {
if (True(testFlags & x12c_flags)) {
xe8_inhabitants.push_back(act.GetUniqueId());
InhabitantAdded(act, mgr);
@ -272,7 +272,7 @@ void CScriptTrigger::Touch(CActor& act, CStateManager& mgr) {
}
}
if ((x12c_flags & ETriggerFlags::KillOnEnter) != ETriggerFlags::None && act.HealthInfo(mgr)) {
if (True(x12c_flags & ETriggerFlags::KillOnEnter) && act.HealthInfo(mgr)) {
CHealthInfo* hInfo = act.HealthInfo(mgr);
mgr.ApplyDamage(x8_uid, act.GetUniqueId(), x8_uid, {sktonOHurtWeaponMode, 10.f * hInfo->GetHP(), 0.f, 0.f},
CMaterialFilter::MakeIncludeExclude({EMaterialTypes::Solid}, {0ull}), zeus::skZero3f);

View File

@ -2209,49 +2209,53 @@ CEntity* ScriptLoader::LoadFishCloud(CStateManager& mgr, CInputStream& in, int p
if (!EnsurePropertyCount(propCount, 36, "FishCloud"))
return nullptr;
SScaledActorHead aHead = LoadScaledActorHead(in, mgr);
bool b1 = in.readBool();
CAssetId w1(in);
bool active = in.readBool();
CAssetId model(in);
CAnimationParameters animParms(in);
u32 w5 = u32(in.readFloatBig());
float f1 = in.readFloatBig();
float f2 = in.readFloatBig();
float f3 = in.readFloatBig();
float f4 = in.readFloatBig();
float f5 = in.readFloatBig();
float f6 = in.readFloatBig();
float f7 = in.readFloatBig();
float f8 = in.readFloatBig();
float f9 = in.readFloatBig();
float f10 = in.readFloatBig();
float f11 = in.readFloatBig();
float f12 = in.readFloatBig();
float f13 = in.readFloatBig();
u32 w6 = in.readUint32Big();
u32 numBoids = u32(in.readFloatBig());
float speed = in.readFloatBig();
float separationRadius = in.readFloatBig();
float cohesionMagnitude = in.readFloatBig();
float alignmentWeight = in.readFloatBig();
float separationMagnitude = in.readFloatBig();
float weaponRepelMagnitude = in.readFloatBig();
float playerRepelMagnitude = in.readFloatBig();
float containmentMagnitude = in.readFloatBig();
float scatterVel = in.readFloatBig();
float maxScatterAngle = in.readFloatBig();
float weaponRepelDampingSpeed = in.readFloatBig();
float playerRepelDampingSpeed = in.readFloatBig();
float containmentRadius = in.readFloatBig();
u32 updateShift = in.readUint32Big();
if (!g_ResFactory->GetResourceTypeById(w1))
if (!g_ResFactory->GetResourceTypeById(model))
return nullptr;
zeus::CColor col = zeus::CColor::ReadRGBABig(in);
bool b2 = in.readBool();
float f14 = in.readFloatBig();
CAssetId w7 = in.readUint32Big();
u32 w8 = in.readUint32Big();
CAssetId w9 = in.readUint32Big();
u32 w10 = in.readUint32Big();
CAssetId w11 = in.readUint32Big();
u32 w12 = in.readUint32Big();
CAssetId w13 = in.readUint32Big();
u32 w14 = in.readUint32Big();
u32 w15 = in.readUint32Big();
bool b3 = in.readBool();
bool b4 = in.readBool();
zeus::CColor color = zeus::CColor::ReadRGBABig(in);
bool killable = in.readBool();
float weaponKillRadius = in.readFloatBig();
CAssetId part1 = in.readUint32Big();
u32 partCount1 = in.readUint32Big();
CAssetId part2 = in.readUint32Big();
u32 partCount2 = in.readUint32Big();
CAssetId part3 = in.readUint32Big();
u32 partCount3 = in.readUint32Big();
CAssetId part4 = in.readUint32Big();
u32 partCount4 = in.readUint32Big();
u32 deathSfx = in.readUint32Big();
bool repelFromThreats = in.readBool();
bool hotInThermal = in.readBool();
CModelData mData(CStaticRes(w1, zeus::skOne3f));
CModelData mData(CStaticRes(model, zeus::skOne3f));
CAnimRes animRes(animParms.GetACSFile(), animParms.GetCharacter(), zeus::skOne3f,
animParms.GetInitialAnimation(), true);
return new CFishCloud(mgr.AllocateUniqueId(), b1, aHead.x0_name, info, aHead.x40_scale, aHead.x10_transform,
std::move(mData), animRes, w5, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, w6, col,
b2, f14, w7, w8, w9, w10, w11, w12, w13, w14, w15, b3, b4);
return new CFishCloud(mgr.AllocateUniqueId(), active, aHead.x0_name, info, aHead.x40_scale, aHead.x10_transform,
std::move(mData), animRes, numBoids, speed, separationRadius, cohesionMagnitude,
alignmentWeight, separationMagnitude, weaponRepelMagnitude, playerRepelMagnitude,
containmentMagnitude, scatterVel, maxScatterAngle, weaponRepelDampingSpeed,
playerRepelDampingSpeed, containmentRadius, updateShift, color, killable, weaponKillRadius,
part1, partCount1, part2, partCount2, part3, partCount3, part4, partCount4, deathSfx,
repelFromThreats, hotInThermal);
}
CEntity* ScriptLoader::LoadFishCloudModifier(CStateManager& mgr, CInputStream& in, int propCount,
@ -2260,13 +2264,14 @@ CEntity* ScriptLoader::LoadFishCloudModifier(CStateManager& mgr, CInputStream& i
return nullptr;
std::string name = mgr.HashInstanceName(in);
zeus::CVector3f vec = zeus::CVector3f::ReadBig(in);
bool b1 = in.readBool();
bool b2 = in.readBool();
bool b3 = propCount > 6 ? in.readBool() : false;
float f1 = in.readFloatBig();
float f2 = in.readFloatBig();
return new CFishCloudModifier(mgr.AllocateUniqueId(), b1, name, info, vec, b2, b3, f1, f2);
zeus::CVector3f pos = zeus::CVector3f::ReadBig(in);
bool active = in.readBool();
bool repulsor = in.readBool();
bool swirl = propCount > 6 ? in.readBool() : false;
float radius = in.readFloatBig();
float priority = in.readFloatBig();
return new CFishCloudModifier(mgr.AllocateUniqueId(), active, name, info, pos, repulsor, swirl, radius, priority);
}
CEntity* ScriptLoader::LoadVisorFlare(CStateManager& mgr, CInputStream& in, int propCount, const CEntityInfo& info) {

2
hecl

@ -1 +1 @@
Subproject commit 957a6c6851ee80c413cd1ba69795f92a8f336363
Subproject commit 05e4c65848d6ff7a08bc7bcc4371369a6735446e

@ -1 +1 @@
Subproject commit 3a6ee9a70cc7cd015a9dccef18852c434103988a
Subproject commit 630119350bd083d7a63b8cfea6afc11e5b7fac76

@ -1 +1 @@
Subproject commit 6d00c4007a9eb090b6b1e8525766cfd9c6732100
Subproject commit ecea58334d51c163ec5d1a3ff26ba05f1deedbb9