Work on projectiles

This commit is contained in:
Jack Andersen 2018-02-10 19:27:00 -10:00
parent 137c7424bb
commit 547471c6ba
24 changed files with 601 additions and 76 deletions

View File

@ -202,16 +202,16 @@ struct MetroidPrimeStage1 : IScriptObject
DamageInfo damageInfo3; DamageInfo damageInfo3;
CameraShakeData primeStruct2_5; CameraShakeData primeStruct2_5;
struct PrimeStruct5B : BigYAML struct PrimeProjectileInfo : BigYAML
{ {
DECL_YAML DECL_YAML
Value<atUint32> propertyCount; Value<atUint32> propertyCount;
UniqueID32 particle5; UniqueID32 particle;
DamageInfo damageInfo4; DamageInfo damageInfo4;
Value<float> unknown9; Value<float> unknown9;
Value<float> unknown10; Value<float> unknown10;
Value<float> unknown11; Value<float> unknown11;
UniqueID32 texture2; UniqueID32 texture;
Value<bool> unknown12; Value<bool> unknown12;
Value<bool> unknown13; Value<bool> unknown13;
Value<bool> unknown14; Value<bool> unknown14;
@ -219,24 +219,24 @@ struct MetroidPrimeStage1 : IScriptObject
void nameIDs(PAKRouter<PAKBridge>& pakRouter, const std::string& name) const void nameIDs(PAKRouter<PAKBridge>& pakRouter, const std::string& name) const
{ {
if (particle5) if (particle)
{ {
PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle5); PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(particle);
ent->name = name + "_part5"; ent->name = name + "_part";
} }
if (texture2) if (texture)
{ {
PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(texture2); PAK::Entry* ent = (PAK::Entry*)pakRouter.lookupEntry(texture);
ent->name = name + "_tex2"; ent->name = name + "_tex";
} }
} }
void depIDs(std::vector<hecl::ProjectPath>& pathsOut) const void depIDs(std::vector<hecl::ProjectPath>& pathsOut) const
{ {
g_curSpec->flattenDependencies(particle5, pathsOut); g_curSpec->flattenDependencies(particle, pathsOut);
g_curSpec->flattenDependencies(texture2, pathsOut); g_curSpec->flattenDependencies(texture, pathsOut);
} }
} primeStruct5b; } projectileInfo;
DamageInfo damageInfo5; DamageInfo damageInfo5;
CameraShakeData primeStruct2_6; CameraShakeData primeStruct2_6;
@ -299,7 +299,7 @@ struct MetroidPrimeStage1 : IScriptObject
primeStruct4s[1].nameIDs(pakRouter, name + "_prime42"); primeStruct4s[1].nameIDs(pakRouter, name + "_prime42");
primeStruct4s[2].nameIDs(pakRouter, name + "_prime43"); primeStruct4s[2].nameIDs(pakRouter, name + "_prime43");
primeStruct4s[3].nameIDs(pakRouter, name + "_prime44"); primeStruct4s[3].nameIDs(pakRouter, name + "_prime44");
primeStruct5b.nameIDs(pakRouter, name + "_prime5"); projectileInfo.nameIDs(pakRouter, name + "_projectileInfo");
} }
void depIDs(std::vector<hecl::ProjectPath>& pathsOut) const void depIDs(std::vector<hecl::ProjectPath>& pathsOut) const
@ -318,7 +318,7 @@ struct MetroidPrimeStage1 : IScriptObject
primeStruct4s[1].depIDs(pathsOut); primeStruct4s[1].depIDs(pathsOut);
primeStruct4s[2].depIDs(pathsOut); primeStruct4s[2].depIDs(pathsOut);
primeStruct4s[3].depIDs(pathsOut); primeStruct4s[3].depIDs(pathsOut);
primeStruct5b.depIDs(pathsOut); projectileInfo.depIDs(pathsOut);
} }
void scanIDs(std::vector<Scan>& scansOut) const void scanIDs(std::vector<Scan>& scansOut) const

View File

@ -62,7 +62,7 @@ public:
const rstl::optional_object<TLockedToken<CGenDescription>>& GetParticleDescription(EWeaponCollisionResponseTypes) const; const rstl::optional_object<TLockedToken<CGenDescription>>& GetParticleDescription(EWeaponCollisionResponseTypes) const;
const rstl::optional_object<TLockedToken<CDecalDescription>>& GetDecalDescription(EWeaponCollisionResponseTypes type) const; const rstl::optional_object<TLockedToken<CDecalDescription>>& GetDecalDescription(EWeaponCollisionResponseTypes type) const;
s32 GetSoundEffectId(EWeaponCollisionResponseTypes) const; s32 GetSoundEffectId(EWeaponCollisionResponseTypes) const;
EWeaponCollisionResponseTypes GetWorldCollisionResponseType(s32); static EWeaponCollisionResponseTypes GetWorldCollisionResponseType(s32);
static bool ResponseTypeIsEnemyShielded(EWeaponCollisionResponseTypes); static bool ResponseTypeIsEnemyShielded(EWeaponCollisionResponseTypes);
static bool ResponseTypeIsEnemyNormal(EWeaponCollisionResponseTypes); static bool ResponseTypeIsEnemyNormal(EWeaponCollisionResponseTypes);
static bool ResponseTypeIsEnemySpecial(EWeaponCollisionResponseTypes); static bool ResponseTypeIsEnemySpecial(EWeaponCollisionResponseTypes);

View File

@ -19,7 +19,7 @@ private:
zeus::CVector3f x4_point; zeus::CVector3f x4_point;
zeus::CPlane x10_plane; zeus::CPlane x10_plane;
EInvalid x20_invalid = EInvalid::Invalid; EInvalid x20_invalid = EInvalid::Invalid;
CMaterialList x24_material; CMaterialList x28_material;
public: public:
CRayCastResult() = default; CRayCastResult() = default;
CRayCastResult(const CRayCastResult& other, EInvalid invalid) CRayCastResult(const CRayCastResult& other, EInvalid invalid)
@ -27,14 +27,14 @@ public:
x4_point(other.x4_point), x4_point(other.x4_point),
x10_plane(other.x10_plane), x10_plane(other.x10_plane),
x20_invalid(invalid), x20_invalid(invalid),
x24_material(other.x24_material) x28_material(other.x28_material)
{ {
} }
CRayCastResult(float t, const zeus::CVector3f& point, CRayCastResult(float t, const zeus::CVector3f& point,
const zeus::CPlane& plane, const CMaterialList& matList) const zeus::CPlane& plane, const CMaterialList& matList)
: x0_t(t), x4_point(point), x10_plane(plane), : x0_t(t), x4_point(point), x10_plane(plane),
x20_invalid(EInvalid::Valid), x24_material(matList) x20_invalid(EInvalid::Valid), x28_material(matList)
{} {}
void MakeInvalid(); void MakeInvalid();
@ -44,7 +44,7 @@ public:
float GetT() const { return x0_t; } float GetT() const { return x0_t; }
const zeus::CVector3f& GetPoint() const { return x4_point; } const zeus::CVector3f& GetPoint() const { return x4_point; }
const zeus::CPlane& GetPlane() const { return x10_plane; } const zeus::CPlane& GetPlane() const { return x10_plane; }
const CMaterialList& GetMaterial() const { return x24_material; } const CMaterialList& GetMaterial() const { return x28_material; }
void Transform(const zeus::CTransform&); void Transform(const zeus::CTransform&);
}; };

View File

@ -0,0 +1,24 @@
//
// Created by Jack Andersen on 2/10/18.
//
#include "CFlaahgraProjectile.hpp"
namespace urde::MP1
{
CFlaahgraProjectile::CFlaahgraProjectile(bool bigStrike, const TToken<CWeaponDescription>& desc,
const zeus::CTransform& xf, const CDamageInfo& damage, TUniqueId uid,
TAreaId aid, TUniqueId owner)
: CEnergyProjectile(true, desc, EWeaponType::AI, xf, EMaterialTypes::Character, damage, uid, aid, owner,
kInvalidUniqueId, EProjectileAttrib::BigProjectile, false, zeus::CVector3f::skOne,
{}, 0xffff, false), x3d8_bigStrike(bigStrike)
{
if (x3d8_bigStrike)
{
xe8_projectileAttribs |= EProjectileAttrib::BigStrike;
x150_damageDuration = 2.f;
}
}
}

View File

@ -0,0 +1,19 @@
#ifndef URDE_CFLAAHGRAPROJECTILE_HPP
#define URDE_CFLAAHGRAPROJECTILE_HPP
#include "Weapon/CEnergyProjectile.hpp"
namespace urde::MP1
{
class CFlaahgraProjectile : public CEnergyProjectile
{
bool x3d8_bigStrike;
public:
CFlaahgraProjectile(bool bigStrike, const TToken<CWeaponDescription>& desc, const zeus::CTransform& xf,
const CDamageInfo& damage, TUniqueId uid, TAreaId aid, TUniqueId owner);
};
}
#endif // URDE_CFLAAHGRAPROJECTILE_HPP

View File

@ -7,10 +7,13 @@ set(MP1_WORLD_SOURCES
CBabygoth.hpp CBabygoth.cpp CBabygoth.hpp CBabygoth.cpp
CMetroidPrimeRelay.hpp CMetroidPrimeRelay.cpp CMetroidPrimeRelay.hpp CMetroidPrimeRelay.cpp
CMetroidPrimeExo.hpp CMetroidPrimeExo.cpp CMetroidPrimeExo.hpp CMetroidPrimeExo.cpp
CMetroidPrimeProjectile.hpp CMetroidPrimeProjectile.cpp
CActorContraption.hpp CActorContraption.cpp CActorContraption.hpp CActorContraption.cpp
CThardusRockProjectile.hpp CThardusRockProjectile.cpp CThardusRockProjectile.hpp CThardusRockProjectile.cpp
CMetroidBeta.hpp CMetroidBeta.cpp CMetroidBeta.hpp CMetroidBeta.cpp
CMetroid.hpp CMetroid.cpp CMetroid.hpp CMetroid.cpp
CMetaree.hpp CMetaree.cpp) CMetaree.hpp CMetaree.cpp
CPuddleToadGamma.hpp CPuddleToadGamma.cpp
CFlaahgraProjectile.hpp CFlaahgraProjectile.cpp)
runtime_add_list(World MP1_WORLD_SOURCES) runtime_add_list(World MP1_WORLD_SOURCES)

View File

@ -41,21 +41,6 @@ SPrimeStruct4::SPrimeStruct4(CInputStream& in)
x8c_dInfo2(in) x8c_dInfo2(in)
{} {}
SPrimeStruct5B::SPrimeStruct5B(CInputStream& in)
: x0_propertyCount(in.readUint32Big()),
x4_particle(g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), in.readUint32Big()})),
xc_dInfo(in),
x28_(in.readFloatBig()),
x2c_(in.readFloatBig()),
x30_(in.readFloatBig()),
x34_texture(in.readUint32Big())
{
x38_24_ = in.readBool();
x38_25_ = in.readBool();
x38_26_ = in.readBool();
x38_27_ = in.readBool();
}
SPrimeStruct6::SPrimeStruct6(CInputStream& in) SPrimeStruct6::SPrimeStruct6(CInputStream& in)
: x0_propertyCount(in.readUint32Big()), : x0_propertyCount(in.readUint32Big()),
x4_damageVulnerability(in), x4_damageVulnerability(in),
@ -152,7 +137,7 @@ CMetroidPrimeExo::CMetroidPrimeExo(TUniqueId uid, std::string_view name, const C
const rstl::reserved_vector<SPrimeStruct4, 4>& struct4s, CAssetId wpsc1, const rstl::reserved_vector<SPrimeStruct4, 4>& struct4s, CAssetId wpsc1,
const CDamageInfo& dInfo1, const CCameraShakeData& shakeData4, CAssetId wpsc2, const CDamageInfo& dInfo1, const CCameraShakeData& shakeData4, CAssetId wpsc2,
const CDamageInfo& dInfo2, const CCameraShakeData& shakeData5, const CDamageInfo& dInfo2, const CCameraShakeData& shakeData5,
const SPrimeStruct5B& struct5b, const CDamageInfo& dInfo3, const SPrimeProjectileInfo& projectileInfo, const CDamageInfo& dInfo3,
const CCameraShakeData& shakeData6, CAssetId particle2, CAssetId swoosh, const CCameraShakeData& shakeData6, CAssetId particle2, CAssetId swoosh,
CAssetId particle3, CAssetId particle4, CAssetId particle3, CAssetId particle4,
const rstl::reserved_vector<SPrimeStruct6, 4>& struct6s) const rstl::reserved_vector<SPrimeStruct6, 4>& struct6s)

View File

@ -6,6 +6,7 @@
#include "World/CActorParameters.hpp" #include "World/CActorParameters.hpp"
#include "Camera/CCameraShakeData.hpp" #include "Camera/CCameraShakeData.hpp"
#include "Weapon/CBeamInfo.hpp" #include "Weapon/CBeamInfo.hpp"
#include "CMetroidPrimeProjectile.hpp"
namespace urde namespace urde
{ {
@ -54,22 +55,6 @@ struct SPrimeStruct4
explicit SPrimeStruct4(CInputStream& in); explicit SPrimeStruct4(CInputStream& in);
}; };
struct SPrimeStruct5B
{
u32 x0_propertyCount;
TToken<CGenDescription> x4_particle;
CDamageInfo xc_dInfo;
float x28_;
float x2c_;
float x30_;
CAssetId x34_texture;
bool x38_24_ : 1;
bool x38_25_ : 1;
bool x38_26_ : 1;
bool x38_27_ : 1;
explicit SPrimeStruct5B(CInputStream& in);
};
struct SPrimeStruct6 struct SPrimeStruct6
{ {
u32 x0_propertyCount; u32 x0_propertyCount;
@ -98,7 +83,7 @@ struct SPrimeExoParameters
CAssetId x7fc_wpsc2; CAssetId x7fc_wpsc2;
CDamageInfo x800_dInfo2; CDamageInfo x800_dInfo2;
CCameraShakeData x81c_shakeData2; CCameraShakeData x81c_shakeData2;
SPrimeStruct5B x8f0_; SPrimeProjectileInfo x8f0_;
CDamageInfo x92c_; CDamageInfo x92c_;
CCameraShakeData x948_; CCameraShakeData x948_;
CAssetId xa1c_particle2; CAssetId xa1c_particle2;
@ -125,9 +110,10 @@ public:
const CCameraShakeData& shakeData3, const SPrimeStruct2B& struct2b, CAssetId particle1, const CCameraShakeData& shakeData3, const SPrimeStruct2B& struct2b, CAssetId particle1,
const rstl::reserved_vector<SPrimeStruct4, 4>& struct4s, CAssetId wpsc1, const CDamageInfo& dInfo1, const rstl::reserved_vector<SPrimeStruct4, 4>& struct4s, CAssetId wpsc1, const CDamageInfo& dInfo1,
const CCameraShakeData& shakeData4, CAssetId wpsc2, const CDamageInfo& dInfo2, const CCameraShakeData& shakeData4, CAssetId wpsc2, const CDamageInfo& dInfo2,
const CCameraShakeData& shakeData5, const SPrimeStruct5B& struct5b, const CDamageInfo& dInfo3, const CCameraShakeData& shakeData5, const SPrimeProjectileInfo& projectileInfo,
const CCameraShakeData& shakeData6, CAssetId particle2, CAssetId swoosh, CAssetId particle3, const CDamageInfo& dInfo3, const CCameraShakeData& shakeData6, CAssetId particle2,
CAssetId particle4, const rstl::reserved_vector<SPrimeStruct6, 4>& struct6s); CAssetId swoosh, CAssetId particle3, CAssetId particle4,
const rstl::reserved_vector<SPrimeStruct6, 4>& struct6s);
}; };
} }

View File

@ -0,0 +1,36 @@
#include "CMetroidPrimeProjectile.hpp"
#include "GameGlobalObjects.hpp"
#include "CSimplePool.hpp"
namespace urde::MP1
{
SPrimeProjectileInfo::SPrimeProjectileInfo(CInputStream& in)
: x0_propertyCount(in.readUint32Big()),
x4_particle(g_SimplePool->GetObj(SObjectTag{FOURCC('PART'), in.readUint32Big()})),
xc_dInfo(in),
x28_(in.readFloatBig()),
x2c_(in.readFloatBig()),
x30_(in.readFloatBig()),
x34_texture(in.readUint32Big())
{
x38_24_ = in.readBool();
x38_25_ = in.readBool();
x38_26_ = in.readBool();
x38_27_ = in.readBool();
}
CMetroidPrimeProjectile::CMetroidPrimeProjectile(
bool active, const TToken<CWeaponDescription>& desc, EWeaponType type,
const zeus::CTransform& xf, EMaterialTypes materials, const CDamageInfo& damage,
TUniqueId uid, TAreaId aid, TUniqueId owner, const SPrimeProjectileInfo& auxData,
TUniqueId homingTarget, EProjectileAttrib attribs, const zeus::CVector3f& scale,
const rstl::optional_object<TLockedToken<CGenDescription>>& visorParticle,
u16 visorSfx, bool sendCollideMsg)
: CEnergyProjectile(active, desc, type, xf, materials, damage, uid, aid, owner, homingTarget, attribs, false,
scale, visorParticle, visorSfx, sendCollideMsg), x3d8_auxData(auxData)
{
}
}

View File

@ -0,0 +1,39 @@
#ifndef URDE_CMETROIDPRIMEPROJECTILE_HPP
#define URDE_CMETROIDPRIMEPROJECTILE_HPP
#include "Weapon/CEnergyProjectile.hpp"
namespace urde::MP1
{
struct SPrimeProjectileInfo
{
u32 x0_propertyCount;
TToken<CGenDescription> x4_particle;
CDamageInfo xc_dInfo;
float x28_;
float x2c_;
float x30_;
CAssetId x34_texture;
bool x38_24_ : 1;
bool x38_25_ : 1;
bool x38_26_ : 1;
bool x38_27_ : 1;
explicit SPrimeProjectileInfo(CInputStream& in);
};
class CMetroidPrimeProjectile : public CEnergyProjectile
{
SPrimeProjectileInfo x3d8_auxData;
public:
CMetroidPrimeProjectile(bool active, const TToken<CWeaponDescription>& desc, EWeaponType type,
const zeus::CTransform& xf, EMaterialTypes materials, const CDamageInfo& damage,
TUniqueId uid, TAreaId aid, TUniqueId owner, const SPrimeProjectileInfo& auxData,
TUniqueId homingTarget, EProjectileAttrib attribs, const zeus::CVector3f& scale,
const rstl::optional_object<TLockedToken<CGenDescription>>& visorParticle,
u16 visorSfx, bool sendCollideMsg);
};
}
#endif // URDE_CMETROIDPRIMEPROJECTILE_HPP

View File

@ -0,0 +1,21 @@
//
// Created by Jack Andersen on 2/10/18.
//
#include "CPuddleToadGamma.hpp"
namespace urde::MP1
{
CPuddleToadGamma::CPuddleToadGamma(TUniqueId uid, std::string_view name, EFlavorType flavor, const CEntityInfo& info,
const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo,
const CActorParameters& aParms, float f1, float f2, float f3,
const zeus::CVector3f& v1,
float f4, float f5, float f6, const CDamageInfo& dInfo1, const CDamageInfo& dInfo2)
: CPatterned(ECharacter::PuddleToad, uid, name, flavor, info, xf, std::move(mData), pInfo, EMovementType::Flyer,
EColliderType::Zero, EBodyType::Restricted, aParms, 2)
{
}
}

View File

@ -0,0 +1,21 @@
#ifndef __URDE_MP1_CPUDDLETOADGAMMA_HPP__
#define __URDE_MP1_CPUDDLETOADGAMMA_HPP__
#include "World/CPatterned.hpp"
namespace urde::MP1
{
class CPuddleToadGamma : public CPatterned
{
public:
static constexpr ECharacter CharacterType = ECharacter::PuddleToad;
CPuddleToadGamma(TUniqueId uid, std::string_view name, EFlavorType flavor, const CEntityInfo& info,
const zeus::CTransform& xf, CModelData&& mData, const CPatternedInfo& pInfo,
const CActorParameters& aParms, float f1, float f2, float f3, const zeus::CVector3f& v1,
float f4, float f5, float f6, const CDamageInfo& dInfo1, const CDamageInfo& dInfo2);
};
}
#endif // __URDE_MP1_CPUDDLETOADGAMMA_HPP__

View File

@ -53,7 +53,7 @@ public:
bool xa4_EWTR = true; bool xa4_EWTR = true;
bool xa5_LWTR = true; bool xa5_LWTR = true;
bool xa6_SWTR = true; bool xa6_SWTR = true;
u32 xa8_PJFX; s32 xa8_PJFX;
std::unique_ptr<CRealElement> xac_RNGE; std::unique_ptr<CRealElement> xac_RNGE;
std::unique_ptr<CRealElement> xb0_FOFF; std::unique_ptr<CRealElement> xb0_FOFF;
}; };

View File

@ -1,4 +1,7 @@
#include <Runtime/GameGlobalObjects.hpp>
#include "CEnergyProjectile.hpp" #include "CEnergyProjectile.hpp"
#include "CStateManager.hpp"
#include "TCastTo.hpp"
namespace urde namespace urde
{ {
@ -7,14 +10,160 @@ CEnergyProjectile::CEnergyProjectile(bool active, const TToken<CWeaponDescriptio
const zeus::CTransform& xf, EMaterialTypes materials, const CDamageInfo& damage, const zeus::CTransform& xf, EMaterialTypes materials, const CDamageInfo& damage,
TUniqueId uid, TAreaId aid, TUniqueId owner, TUniqueId homingTarget, TUniqueId uid, TAreaId aid, TUniqueId owner, TUniqueId homingTarget,
EProjectileAttrib attribs, bool underwater, const zeus::CVector3f& scale, EProjectileAttrib attribs, bool underwater, const zeus::CVector3f& scale,
const rstl::optional_object<TLockedToken<CGenDescription>>& particle, const rstl::optional_object<TLockedToken<CGenDescription>>& visorParticle,
s16 w2, bool b2) u16 visorSfx, bool sendCollideMsg)
: CGameProjectile(active, desc, "GameProjectile", type, xf, materials, damage, uid, aid, : CGameProjectile(active, desc, "GameProjectile", type, xf, materials, damage, uid, aid,
owner, homingTarget, attribs, underwater, scale, particle, w2, b2), owner, homingTarget, attribs, underwater, scale, visorParticle, visorSfx, sendCollideMsg),
x2ec_dir(xf.basis[1]), x2f8_mag(x2ec_dir.magnitude()), x2ec_dir(xf.basis[1]), x2f8_mag(x2ec_dir.magnitude()),
x2fc_camShake(CCameraShakeData::BuildProjectileCameraShake(0.5f, 0.75f)) x2fc_camShake(CCameraShakeData::BuildProjectileCameraShake(0.5f, 0.75f))
{ {
xe6_27_thermalVisorFlags = 2; xe6_27_thermalVisorFlags = 2;
} }
void CEnergyProjectile::PlayImpactSound(const zeus::CVector3f& pos, EWeaponCollisionResponseTypes type)
{
u16 sfxId = x170_projectile.GetSoundIdForCollision(type);
if (sfxId >= 0.f)
{
CAudioSys::C3DEmitterParmData parmData = {};
parmData.x18_maxDist = x170_projectile.GetAudibleRange();
parmData.x1c_distComp = x170_projectile.GetAudibleFallOff();
parmData.x20_flags = 0x1; // Continuous parameter update
parmData.x24_sfxId = CSfxManager::TranslateSFXID(sfxId);
parmData.x26_maxVol = 1.f;
parmData.x27_minVol = 0.16f;
parmData.x29_prio = 0x7f;
CSfxHandle hnd = CSfxManager::AddEmitter(parmData, true, 0x7f, false, kInvalidAreaId);
if (x2e4_26_waterUpdate)
CSfxManager::PitchBend(hnd, -1.f);
}
}
void CEnergyProjectile::ChangeProjectileOwner(TUniqueId owner, CStateManager& mgr)
{
if (TCastToConstPtr<CActor> act = mgr.GetObjectById(owner))
{
float rDam = g_tweakPlayerGun->GetRichochetDamage(u32(x110_origDamageInfo.GetWeaponMode().GetType()));
x110_origDamageInfo.MultiplyDamageAndRadius(rDam);
mgr.RemoveWeaponId(xec_ownerId, xf0_weaponType);
xec_ownerId = owner;
mgr.AddWeaponId(xec_ownerId, xf0_weaponType);
/* Can now damage Player */
xf8_filter.ExcludeList().Add(EMaterialTypes::Character);
xf8_filter.ExcludeList().Remove(EMaterialTypes::Player);
xf8_filter = CMaterialFilter::MakeIncludeExclude(xf8_filter.GetIncludeList(), xf8_filter.GetExcludeList());
}
}
void CEnergyProjectile::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr)
{
switch (msg)
{
case EScriptObjectMessage::Deleted:
if (x2e4_24_)
mgr.RemoveWeaponId(xec_ownerId, xf0_weaponType);
if (x2e8_sfx)
{
CSfxManager::RemoveEmitter(x2e8_sfx);
x2e8_sfx.reset();
}
break;
case EScriptObjectMessage::Registered:
{
if (CElementGen* ps1 = x170_projectile.GetAttachedPS1())
if (ps1->SystemHasLight())
CreateProjectileLight("ProjectileLight_GameProjectile", ps1->GetLight(), mgr);
TLockedToken<CWeaponDescription> desc = x170_projectile.GetWeaponDescription();
s32 sfx = desc->xa8_PJFX;
if (sfx != -1)
{
float range = 50.f;
float falloff = 0.2f;
if (CRealElement* rnge = desc->xac_RNGE.get())
rnge->GetValue(0, range);
if (CRealElement* foff = desc->xb0_FOFF.get())
foff->GetValue(0, falloff);
CAudioSys::C3DEmitterParmData parmData = {};
parmData.x0_pos = x170_projectile.GetTranslation();
parmData.xc_dir = x170_projectile.GetVelocity();
parmData.x18_maxDist = range;
parmData.x1c_distComp = falloff;
parmData.x20_flags = 0x9; // Continuous parameter update, doppler
parmData.x24_sfxId = CSfxManager::TranslateSFXID(sfx);
parmData.x26_maxVol = 1.f;
parmData.x27_minVol = 0.16f;
parmData.x29_prio = 0x7f;
x2e8_sfx = CSfxManager::AddEmitter(parmData, true, 0x7f, false, kInvalidAreaId);
}
mgr.AddWeaponId(xec_ownerId, xf0_weaponType);
break;
}
default:
break;
}
CGameProjectile::AcceptScriptMsg(msg, sender, mgr);
}
void CEnergyProjectile::Accept(IVisitor& visitor)
{
visitor.Visit(this);
}
/* Material surface types only (not meta flags) */
static constexpr u64 kCheckMaterial = 0xE3FFFE;
void CEnergyProjectile::ResolveCollisionWithWorld(const CRayCastResult& res, CStateManager& mgr)
{
EWeaponCollisionResponseTypes crType = CCollisionResponseData::GetWorldCollisionResponseType(
CMaterialList::BitPosition(res.GetMaterial().GetValue() & 0xffffffff & kCheckMaterial));
if ((xe8_projectileAttribs & (EProjectileAttrib::Wave | EProjectileAttrib::ComboShot)) !=
(EProjectileAttrib::Wave | EProjectileAttrib::ComboShot))
{
/* Not wavebuster */
if (Explode(res.GetPoint(), res.GetPlane().normal(), crType, mgr,
CDamageVulnerability::NormalVulnerabilty(), kInvalidUniqueId))
mgr.ApplyDamageToWorld(xec_ownerId, *this, res.GetPoint(), x12c_curDamageInfo, xf8_filter);
x2c2_ = kInvalidUniqueId;
}
}
void CEnergyProjectile::ResolveCollisionWithActor(const CRayCastResult& res, CActor& act, CStateManager& mgr)
{
}
void CEnergyProjectile::Think(float dt, CStateManager& mgr)
{
}
void CEnergyProjectile::Render(const CStateManager& mgr) const
{
}
void CEnergyProjectile::AddToRenderer(const zeus::CFrustum& frustum, const CStateManager& mgr) const
{
}
void CEnergyProjectile::Touch(CActor& act, CStateManager& mgr)
{
}
bool CEnergyProjectile::Explode(const zeus::CVector3f& pos, const zeus::CVector3f& dir,
EWeaponCollisionResponseTypes type, CStateManager& mgr,
const CDamageVulnerability& dVuln, TUniqueId exploder)
{
return false;
}
void CEnergyProjectile::StopProjectile(CStateManager& mgr)
{
}
} }

View File

@ -9,7 +9,7 @@ namespace urde
class CEnergyProjectile : public CGameProjectile class CEnergyProjectile : public CGameProjectile
{ {
u32 x2e8_ = 0; CSfxHandle x2e8_sfx;
zeus::CVector3f x2ec_dir; zeus::CVector3f x2ec_dir;
float x2f8_mag; float x2f8_mag;
CCameraShakeData x2fc_camShake; CCameraShakeData x2fc_camShake;
@ -25,14 +25,27 @@ class CEnergyProjectile : public CGameProjectile
u32 _dummy = 0; u32 _dummy = 0;
}; };
float x3d4_ = 0.f; float x3d4_ = 0.f;
void StopProjectile(CStateManager& mgr);
public: public:
CEnergyProjectile(bool active, const TToken<CWeaponDescription>& desc, EWeaponType type, CEnergyProjectile(bool active, const TToken<CWeaponDescription>& desc, EWeaponType type,
const zeus::CTransform& xf, EMaterialTypes materials, const CDamageInfo& damage, const zeus::CTransform& xf, EMaterialTypes materials, const CDamageInfo& damage,
TUniqueId uid, TAreaId aid, TUniqueId owner, TUniqueId homingTarget, TUniqueId uid, TAreaId aid, TUniqueId owner, TUniqueId homingTarget,
EProjectileAttrib attribs, bool underwater, const zeus::CVector3f& scale, EProjectileAttrib attribs, bool underwater, const zeus::CVector3f& scale,
const rstl::optional_object<TLockedToken<CGenDescription>>& particle, const rstl::optional_object<TLockedToken<CGenDescription>>& visorParticle,
s16 w2, bool b2); u16 visorSfx, bool sendCollideMsg);
void SetCameraShake(const CCameraShakeData& data) { x2fc_camShake = data; x3d0_27_camShakeDirty = true; } void SetCameraShake(const CCameraShakeData& data) { x2fc_camShake = data; x3d0_27_camShakeDirty = true; }
void PlayImpactSound(const zeus::CVector3f& pos, EWeaponCollisionResponseTypes type);
void ChangeProjectileOwner(TUniqueId owner, CStateManager& mgr);
void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId sender, CStateManager& mgr);
void Accept(IVisitor& visitor);
void ResolveCollisionWithWorld(const CRayCastResult& res, CStateManager& mgr);
void ResolveCollisionWithActor(const CRayCastResult& res, CActor& act, CStateManager& mgr);
void Think(float dt, CStateManager& mgr);
void Render(const CStateManager& mgr) const;
void AddToRenderer(const zeus::CFrustum& frustum, const CStateManager& mgr) const;
void Touch(CActor& act, CStateManager& mgr);
virtual bool Explode(const zeus::CVector3f& pos, const zeus::CVector3f& dir, EWeaponCollisionResponseTypes type,
CStateManager& mgr, const CDamageVulnerability& dVuln, TUniqueId exploder);
}; };
} }

View File

@ -7,6 +7,9 @@
#include "World/CWallCrawlerSwarm.hpp" #include "World/CWallCrawlerSwarm.hpp"
#include "World/CScriptDoor.hpp" #include "World/CScriptDoor.hpp"
#include "Collision/CInternalRayCastStructure.hpp" #include "Collision/CInternalRayCastStructure.hpp"
#include "MP1/World/CPuddleToadGamma.hpp"
#include "World/CScriptPlatform.hpp"
#include "Collision/CCollisionActor.hpp"
namespace urde namespace urde
{ {
@ -23,12 +26,13 @@ CGameProjectile::CGameProjectile(bool active, const TToken<CWeaponDescription>&
{EMaterialTypes::Projectile, EMaterialTypes::ProjectilePassthrough, matType, EMaterialTypes::Solid}), {EMaterialTypes::Projectile, EMaterialTypes::ProjectilePassthrough, matType, EMaterialTypes::Solid}),
CMaterialList(), dInfo, attribs | GetBeamAttribType(wType), CModelData::CModelDataNull()), CMaterialList(), dInfo, attribs | GetBeamAttribType(wType), CModelData::CModelDataNull()),
x158_visorParticle(visorParticle), x168_visorSfx(visorSfx), x170_projectile(wDesc, xf.origin, xf.basis, scale, x158_visorParticle(visorParticle), x168_visorSfx(visorSfx), x170_projectile(wDesc, xf.origin, xf.basis, scale,
(attribs & EProjectileAttrib::Sixteen) == EProjectileAttrib::Sixteen), x298_(xf.origin), (attribs & EProjectileAttrib::ParticleOPTS) == EProjectileAttrib::ParticleOPTS), x298_lastOrigin(xf.origin),
x2a4_((xe8_projectileAttribs & EProjectileAttrib::Ten) == EProjectileAttrib::Ten ? 0.25f : 0.1f), x2a4_projExtent((xe8_projectileAttribs & EProjectileAttrib::BigProjectile) ==
EProjectileAttrib::BigProjectile ? 0.25f : 0.1f),
x2c0_homingTargetId(homingTarget), x2cc_wpscId(wDesc.GetObjectTag()->id) x2c0_homingTargetId(homingTarget), x2cc_wpscId(wDesc.GetObjectTag()->id)
{ {
x2e4_24_ = true; x2e4_24_ = true;
x2e4_25_ = underwater; x2e4_25_startedUnderwater = underwater;
x2e4_26_waterUpdate = underwater; x2e4_26_waterUpdate = underwater;
x2e4_27_inWater = underwater; x2e4_27_inWater = underwater;
x2e4_28_sendProjectileCollideMsg = sendCollideMsg; x2e4_28_sendProjectileCollideMsg = sendCollideMsg;
@ -214,7 +218,7 @@ void CGameProjectile::UpdateProjectileMovement(float dt, CStateManager& mgr)
if (x2e4_26_waterUpdate) if (x2e4_26_waterUpdate)
useDt = 37.5f * dt * dt; useDt = 37.5f * dt * dt;
x298_ = x34_transform.origin; x298_lastOrigin = x34_transform.origin;
x170_projectile.Update(useDt); x170_projectile.Update(useDt);
SetTransform(x170_projectile.GetTransform()); SetTransform(x170_projectile.GetTransform());
SetTranslation(x170_projectile.GetTranslation()); SetTranslation(x170_projectile.GetTranslation());
@ -227,12 +231,12 @@ CGameProjectile::DoCollisionCheck(TUniqueId& idOut, CStateManager& mgr)
CRayCastResult res; CRayCastResult res;
if (x2e4_24_) if (x2e4_24_)
{ {
zeus::CVector3f posDelta = x34_transform.origin - x298_; zeus::CVector3f posDelta = x34_transform.origin - x298_lastOrigin;
rstl::reserved_vector<TUniqueId, 1024> nearList; rstl::reserved_vector<TUniqueId, 1024> nearList;
mgr.BuildNearList(nearList, GetProjectileBounds(), mgr.BuildNearList(nearList, GetProjectileBounds(),
CMaterialFilter::MakeExclude(EMaterialTypes::ProjectilePassthrough), this); CMaterialFilter::MakeExclude(EMaterialTypes::ProjectilePassthrough), this);
res = RayCollisionCheckWithWorld(idOut, x298_, x34_transform.origin, res = RayCollisionCheckWithWorld(idOut, x298_lastOrigin, x34_transform.origin,
posDelta.magnitude(), nearList, mgr); posDelta.magnitude(), nearList, mgr);
} }
@ -361,12 +365,174 @@ CGameProjectile::RayCollisionCheckWithWorld(TUniqueId& idOut, const zeus::CVecto
CProjectileTouchResult CGameProjectile::CanCollideWith(CActor& act, CStateManager& mgr) CProjectileTouchResult CGameProjectile::CanCollideWith(CActor& act, CStateManager& mgr)
{ {
return {{}, {}}; if (act.GetDamageVulnerability()->GetVulnerability(x12c_curDamageInfo.GetWeaponMode(), false) ==
EVulnerability::PassThrough)
{
return {kInvalidUniqueId, {}};
}
else
{
if (TCastToPtr<CScriptTrigger>(act))
{
return CanCollideWithTrigger(act, mgr);
}
else if (TCastToPtr<CScriptPlatform>(act) || TCastToPtr<CCollisionActor>(act) ||
CPatterned::CastTo<MP1::CPuddleToadGamma>(&act))
{
return CanCollideWithComplexCollision(act, mgr);
}
else
{
return CanCollideWithGameObject(act, mgr);
}
}
}
CProjectileTouchResult CGameProjectile::CanCollideWithComplexCollision(CActor& act, CStateManager& mgr)
{
CPhysicsActor* useAct = nullptr;
if (TCastToPtr<CScriptPlatform> plat = act)
{
if (plat->HasComplexCollision())
useAct = plat.GetPtr();
}
else if (MP1::CPuddleToadGamma* toad = CPatterned::CastTo<MP1::CPuddleToadGamma>(&act))
{
useAct = toad;
}
else if (TCastToPtr<CCollisionActor> cact = act)
{
if (cact->GetOwnerId() == xec_ownerId)
return {kInvalidUniqueId, {}};
useAct = cact.GetPtr();
}
if (useAct)
{
const CCollisionPrimitive* prim = useAct->GetCollisionPrimitive();
zeus::CTransform xf = useAct->GetPrimitiveTransform();
zeus::CVector3f deltaPos = GetTranslation() - x298_lastOrigin;
if (deltaPos.canBeNormalized())
{
zeus::CVector3f dir = deltaPos.normalized();
float mag = deltaPos.magnitude();
CRayCastResult res =
prim->CastRayInternal({x298_lastOrigin, dir, mag, xf,
CMaterialFilter::MakeIncludeExclude(
{EMaterialTypes::Solid},
{EMaterialTypes::ProjectilePassthrough})});
if (!res.IsValid())
{
if (prim->GetPrimType() == FOURCC('SPHR'))
{
mag *= 2.f;
CRayCastResult res2 =
prim->CastRayInternal({x298_lastOrigin - dir * mag, dir, deltaPos.magnitude(), xf,
CMaterialFilter::MakeIncludeExclude(
{EMaterialTypes::Solid},
{EMaterialTypes::ProjectilePassthrough})});
if (res2.IsValid())
return {act.GetUniqueId(), {res2}};
}
else if (TCastToPtr<CCollisionActor> cAct = act)
{
float rad = cAct->GetSphereRadius();
if ((x298_lastOrigin - GetTranslation()).magSquared() < rad * rad)
{
zeus::CVector3f point = x298_lastOrigin - dir * rad * 1.125f;
zeus::CUnitVector3f revDir(-dir);
return {act.GetUniqueId(), {{0.f, point, {revDir, point.dot(revDir)}, act.GetMaterialList()}}};
}
}
return {kInvalidUniqueId, {}};
}
else
{
return {act.GetUniqueId(), {res}};
}
}
else
{
return {kInvalidUniqueId, {}};
}
}
else
{
return {act.GetUniqueId(), {}};
}
}
CProjectileTouchResult CGameProjectile::CanCollideWithGameObject(CActor& act, CStateManager& mgr)
{
TCastToPtr<CGameProjectile> proj = act;
if (!proj)
{
if (!act.GetMaterialList().HasMaterial(EMaterialTypes::Solid) && !act.HealthInfo(mgr))
{
return {kInvalidUniqueId, {}};
}
else if (act.GetUniqueId() == xec_ownerId)
{
return {kInvalidUniqueId, {}};
}
else if (act.GetUniqueId() == x2c2_)
{
return {kInvalidUniqueId, {}};
}
else if (xf8_filter.GetExcludeList().Intersection(act.GetMaterialList()))
{
return {kInvalidUniqueId, {}};
}
else if (TCastToPtr<CAi> ai = act)
{
if (!ai->CanBeShot(mgr, int(xe8_projectileAttribs)))
return {kInvalidUniqueId, {}};
}
}
else if ((xe8_projectileAttribs & EProjectileAttrib::PartialCharge) == EProjectileAttrib::PartialCharge ||
(proj->xe8_projectileAttribs & EProjectileAttrib::PartialCharge) == EProjectileAttrib::PartialCharge)
{
return {act.GetUniqueId(), {}};
}
else if ((xe8_projectileAttribs & EProjectileAttrib::PartialCharge) != EProjectileAttrib::PartialCharge &&
(proj->xe8_projectileAttribs & EProjectileAttrib::PartialCharge) != EProjectileAttrib::PartialCharge)
{
return {kInvalidUniqueId, {}};
}
return {act.GetUniqueId(), {}};
}
CProjectileTouchResult CGameProjectile::CanCollideWithTrigger(CActor& act, CStateManager& mgr)
{
bool isWater = TCastToPtr<CScriptWater>(act).operator bool();
if (isWater)
{
bool enteredWater = false;
if (isWater && !x2e4_25_startedUnderwater)
{
if (!x170_projectile.GetWeaponDescription()->xa4_EWTR)
enteredWater = true;
}
/* This case is logically unreachable */
bool leftWater = false;
if (!isWater && x2e4_25_startedUnderwater)
{
if (!x170_projectile.GetWeaponDescription()->xa5_LWTR)
leftWater = true;
}
return {(enteredWater || leftWater) ? act.GetUniqueId() : kInvalidUniqueId, {}};
}
return {kInvalidUniqueId, {}};
} }
zeus::CAABox CGameProjectile::GetProjectileBounds() const zeus::CAABox CGameProjectile::GetProjectileBounds() const
{ {
return {}; return {{std::min(x298_lastOrigin.x, GetTranslation().x) - x2a4_projExtent,
std::min(x298_lastOrigin.y, GetTranslation().y) - x2a4_projExtent,
std::min(x298_lastOrigin.z, GetTranslation().z) - x2a4_projExtent},
{std::max(x298_lastOrigin.x, GetTranslation().x) + x2a4_projExtent,
std::max(x298_lastOrigin.y, GetTranslation().y) + x2a4_projExtent,
std::max(x298_lastOrigin.z, GetTranslation().z) + x2a4_projExtent}};
} }
} }

View File

@ -29,11 +29,12 @@ public:
class CGameProjectile : public CWeapon class CGameProjectile : public CWeapon
{ {
protected:
rstl::optional_object<TLockedToken<CGenDescription>> x158_visorParticle; rstl::optional_object<TLockedToken<CGenDescription>> x158_visorParticle;
u16 x168_visorSfx; u16 x168_visorSfx;
CProjectileWeapon x170_projectile; CProjectileWeapon x170_projectile;
zeus::CVector3f x298_; zeus::CVector3f x298_lastOrigin;
float x2a4_; float x2a4_projExtent;
float x2a8_homingDt = 0.03f; float x2a8_homingDt = 0.03f;
double x2b0_targetHomingTime = 0.0; double x2b0_targetHomingTime = 0.0;
double x2b8_curHomingTime = x2a8_homingDt; double x2b8_curHomingTime = x2a8_homingDt;
@ -50,7 +51,7 @@ class CGameProjectile : public CWeapon
struct struct
{ {
bool x2e4_24_ : 1; bool x2e4_24_ : 1;
bool x2e4_25_ : 1; bool x2e4_25_startedUnderwater : 1;
bool x2e4_26_waterUpdate : 1; bool x2e4_26_waterUpdate : 1;
bool x2e4_27_inWater : 1; bool x2e4_27_inWater : 1;
bool x2e4_28_sendProjectileCollideMsg : 1; bool x2e4_28_sendProjectileCollideMsg : 1;
@ -81,6 +82,9 @@ public:
const rstl::reserved_vector<TUniqueId, 1024>& nearList, const rstl::reserved_vector<TUniqueId, 1024>& nearList,
CStateManager& mgr); CStateManager& mgr);
CProjectileTouchResult CanCollideWith(CActor& act, CStateManager& mgr); CProjectileTouchResult CanCollideWith(CActor& act, CStateManager& mgr);
CProjectileTouchResult CanCollideWithComplexCollision(CActor& act, CStateManager& mgr);
CProjectileTouchResult CanCollideWithGameObject(CActor& act, CStateManager& mgr);
CProjectileTouchResult CanCollideWithTrigger(CActor& act, CStateManager& mgr);
zeus::CAABox GetProjectileBounds() const; zeus::CAABox GetProjectileBounds() const;
TUniqueId GetHomingTargetId() const { return x2c0_homingTargetId; } TUniqueId GetHomingTargetId() const { return x2c0_homingTargetId; }
}; };

View File

@ -20,6 +20,7 @@ set(WEAPON_SOURCES
CWeapon.hpp CWeapon.cpp CWeapon.hpp CWeapon.cpp
CGameProjectile.hpp CGameProjectile.cpp CGameProjectile.hpp CGameProjectile.cpp
CBeamProjectile.hpp CBeamProjectile.cpp CBeamProjectile.hpp CBeamProjectile.cpp
CTargetableProjectile.hpp CTargetableProjectile.cpp
CBeamInfo.hpp CBeamInfo.cpp CBeamInfo.hpp CBeamInfo.cpp
CPlasmaProjectile.hpp CPlasmaProjectile.cpp CPlasmaProjectile.hpp CPlasmaProjectile.cpp
CEnergyProjectile.cpp CEnergyProjectile.cpp CEnergyProjectile.cpp CEnergyProjectile.cpp

View File

@ -96,6 +96,7 @@ public:
virtual void Update(float dt); virtual void Update(float dt);
void SetGravity(const zeus::CVector3f& grav) { xbc_gravity = grav; } void SetGravity(const zeus::CVector3f& grav) { xbc_gravity = grav; }
static void SetGlobalSeed(u16 seed) { g_GlobalSeed = seed; } static void SetGlobalSeed(u16 seed) { g_GlobalSeed = seed; }
CElementGen* GetAttachedPS1() const { return xfc_APSMGen.get(); }
}; };
} }

View File

@ -0,0 +1,26 @@
//
// Created by Jack Andersen on 2/10/18.
//
#include "CTargetableProjectile.hpp"
namespace urde
{
CTargetableProjectile::CTargetableProjectile(
const TToken<CWeaponDescription>& desc, EWeaponType type,
const zeus::CTransform& xf, EMaterialTypes materials, const CDamageInfo& damage,
const CDamageInfo& damage2, TUniqueId uid, TAreaId aid, TUniqueId owner,
TUniqueId homingTarget, EProjectileAttrib attribs,
const rstl::optional_object<TLockedToken<CGenDescription>>& visorParticle,
u16 visorSfx, bool sendCollideMsg)
: CEnergyProjectile(true, desc, type, xf, materials, damage, uid, aid, owner, homingTarget,
attribs | EProjectileAttrib::BigProjectile | EProjectileAttrib::PartialCharge |
EProjectileAttrib::PlasmaProjectile, false, zeus::CVector3f::skOne, visorParticle,
visorSfx, sendCollideMsg), x3e0_dInfo2(damage2)
{
x68_material.Add(EMaterialTypes::Target);
x68_material.Add(EMaterialTypes::Orbit);
}
}

View File

@ -0,0 +1,23 @@
#ifndef URDE_CTARGETABLEPROJECTILE_HPP
#define URDE_CTARGETABLEPROJECTILE_HPP
#include "CEnergyProjectile.hpp"
namespace urde
{
class CTargetableProjectile : public CEnergyProjectile
{
CDamageInfo x3e0_dInfo2;
public:
CTargetableProjectile(const TToken<CWeaponDescription>& desc, EWeaponType type,
const zeus::CTransform& xf, EMaterialTypes materials, const CDamageInfo& damage,
const CDamageInfo& damage2, TUniqueId uid, TAreaId aid, TUniqueId owner,
TUniqueId homingTarget, EProjectileAttrib attribs,
const rstl::optional_object<TLockedToken<CGenDescription>>& visorParticle,
u16 visorSfx, bool sendCollideMsg);
};
}
#endif // URDE_CTARGETABLEPROJECTILE_HPP

View File

@ -24,13 +24,13 @@ public:
ComboShot = (1 << 7), ComboShot = (1 << 7),
Bombs = (1 << 8), Bombs = (1 << 8),
PowerBombs = (1 << 9), PowerBombs = (1 << 9),
Ten = (1 << 10), BigProjectile = (1 << 10),
ArmCannon = (1 << 11), ArmCannon = (1 << 11),
BigStrike = (1 << 12), BigStrike = (1 << 12),
DamageFalloff = (1 << 13), DamageFalloff = (1 << 13),
StaticInterference = (1 << 14), StaticInterference = (1 << 14),
PlayerUnFreeze = (1 << 15), PlayerUnFreeze = (1 << 15),
Sixteen = (1 << 16), ParticleOPTS = (1 << 16),
KeepInCinematic = (1 << 17), KeepInCinematic = (1 << 17),
}; };

View File

@ -61,6 +61,13 @@ public:
xc_radiusDamage *= m; xc_radiusDamage *= m;
x14_knockback *= m; x14_knockback *= m;
} }
void MultiplyDamageAndRadius(float m)
{
x8_damage *= m;
xc_radiusDamage *= m;
x10_radius *= m;
x14_knockback *= m;
}
}; };
} }

View File

@ -73,6 +73,7 @@ public:
std::vector<SRiders>& GetX338() { return x338_; } std::vector<SRiders>& GetX338() { return x338_; }
const std::vector<SRiders>& GetX338() const { return x338_; } const std::vector<SRiders>& GetX338() const { return x338_; }
void AddSlave(TUniqueId, CStateManager&) {} void AddSlave(TUniqueId, CStateManager&) {}
bool HasComplexCollision() const { return x314_treeGroup.operator bool(); }
}; };
} }