mirror of
https://github.com/zeldaret/ss
synced 2026-05-23 23:05:20 -04:00
1280 lines
42 KiB
C++
1280 lines
42 KiB
C++
// clang-format off
|
|
// vtable order
|
|
#include "JSystem/JParticle/JPAEmitter.h"
|
|
#include "JSystem/JParticle/JPAParticle.h"
|
|
#include "d/d_gfx.h"
|
|
#include "m/m_heap.h"
|
|
#include "rvl/GX/GXTypes.h"
|
|
#include "toBeSorted/d_d3d.h"
|
|
// clang-format on
|
|
|
|
#include "toBeSorted/d_emitter.h"
|
|
|
|
#include "JSystem/JParticle/JPADrawInfo.h"
|
|
#include "JSystem/JParticle/JPAEmitter.h"
|
|
#include "c/c_math.h"
|
|
#include "common.h"
|
|
#include "d/a/d_a_base.h"
|
|
#include "d/a/obj/d_a_obj_base.h"
|
|
#include "d/d_base.h"
|
|
#include "d/d_heap.h"
|
|
#include "d/d_light_env.h"
|
|
#include "d/d_stage.h"
|
|
#include "d/d_stage_mgr.h"
|
|
#include "egg/core/eggHeap.h"
|
|
#include "f/f_base.h"
|
|
#include "f/f_profile_name.h"
|
|
#include "m/m3d/m3d.h"
|
|
#include "m/m_allocator.h"
|
|
#include "m/m_angle.h"
|
|
#include "m/m_color.h"
|
|
#include "m/m_mtx.h"
|
|
#include "m/m_vec.h"
|
|
#include "nw4r/g3d/g3d_camera.h"
|
|
#include "nw4r/g3d/g3d_state.h"
|
|
#include "nw4r/g3d/res/g3d_resfile.h"
|
|
#include "nw4r/g3d/res/g3d_resmat.h"
|
|
#include "nw4r/g3d/res/g3d_resmdl.h"
|
|
#include "nw4r/g3d/res/g3d_resshp.h"
|
|
#include "nw4r/math/math_arithmetic.h"
|
|
#include "sized_string.h"
|
|
#include "toBeSorted/arc_managers/oarc_manager.h"
|
|
#include "toBeSorted/d_d3d.h"
|
|
#include "toBeSorted/d_particle.h"
|
|
#include "toBeSorted/event_manager.h"
|
|
|
|
#include "rvl/GX.h"
|
|
#include "rvl/MTX.h"
|
|
|
|
void float_ordering_1(s32 a) {
|
|
(f32) a;
|
|
}
|
|
|
|
void float_ordering_2() {
|
|
255.0f;
|
|
}
|
|
|
|
void float_ordering_3(u32 a) {
|
|
(f32) a;
|
|
}
|
|
|
|
void float_ordering_4() {
|
|
0.0f;
|
|
}
|
|
|
|
dJEffManager_c::EffectsList dJEffManager_c::sPlayingEffectsList;
|
|
CommonEmitterCallback dJEffManager_c::sCommonEmitterCallbacks[2];
|
|
dShpEmitter_c dJEffManager_c::sShpEmitters[47];
|
|
dEmitterBase_c dJEffManager_c::sEmitter;
|
|
dParticleFogProc_c dJEffManager_c::sFogProcs[12];
|
|
dEffect2D_c dJEffManager_c::s2DEffects[3];
|
|
|
|
// broken by -ipa file - probably TList nonsense?
|
|
dEmitterCallback_c::~dEmitterCallback_c() {
|
|
for (EmitterCallbackList::Iterator it = mEmitterList.GetBeginIter(); it != mEmitterList.GetEndIter();) {
|
|
EmitterCallbackList::Iterator itCopy = it;
|
|
++itCopy;
|
|
it->setEmitterCallback(nullptr);
|
|
it = itCopy;
|
|
}
|
|
}
|
|
|
|
void dEmitterCallback_c::remove(dEmitterBase_c *emitter) {
|
|
if (mEmitterList.GetPosition(emitter) == mEmitterList.GetEndIter()) {
|
|
return;
|
|
}
|
|
mEmitterList.remove(emitter);
|
|
}
|
|
|
|
// broken by -ipa file - probably TList nonsense?
|
|
dParticleCallback_c::~dParticleCallback_c() {
|
|
for (ParticleCallbackList::Iterator it = mEmitterList.GetBeginIter(); it != mEmitterList.GetEndIter();) {
|
|
ParticleCallbackList::Iterator itCopy = it;
|
|
++itCopy;
|
|
it->setParticleCallback(nullptr);
|
|
it = itCopy;
|
|
}
|
|
}
|
|
|
|
void dParticleCallback_c::remove(dEmitterBase_c *emitter) {
|
|
if (mEmitterList.GetPosition(emitter) == mEmitterList.GetEndIter()) {
|
|
return;
|
|
}
|
|
mEmitterList.remove(emitter);
|
|
}
|
|
|
|
dEmitterBase_c::~dEmitterBase_c() {
|
|
deactivateEmitters();
|
|
if (mpEmitterCallback != nullptr) {
|
|
mpEmitterCallback->remove(this);
|
|
mpEmitterCallback = nullptr;
|
|
}
|
|
|
|
if (mpParticleCallback != nullptr) {
|
|
mpParticleCallback->remove(this);
|
|
mpParticleCallback = nullptr;
|
|
}
|
|
}
|
|
|
|
JPABaseEmitter *dEmitterBase_c::GetNextEmitter(JPABaseEmitter *head) {
|
|
// Portability hazard: u32->pointer cast
|
|
return reinterpret_cast<JPABaseEmitter *>(head->getUserWork());
|
|
}
|
|
|
|
bool dEmitterBase_c::createEmitters(
|
|
u16 effectResourceId, const mVec3_c &position, const mAng3_c *rot, const mVec3_c *scale, const GXColor *c1,
|
|
const GXColor *c2, s32 idx1, s32 idx2
|
|
) {
|
|
u16 iter = effectResourceId;
|
|
JPABaseEmitter *head = nullptr;
|
|
JPABaseEmitter *last;
|
|
for (; iter != 0xFFFF; iter = dParticle::mgr_c::GetInstance()->getJpnData(iter)) {
|
|
last = dParticle::mgr_c::GetInstance()->createEmitter(
|
|
iter, dJEffManager_c::getGroupId(iter), position, rot, scale
|
|
);
|
|
if (last != nullptr) {
|
|
if (head != nullptr) {
|
|
// Portability hazard: pointer->u32 cast
|
|
head->setUserWork(reinterpret_cast<u32>(last));
|
|
} else {
|
|
mpEmitterHead = last;
|
|
}
|
|
head = last;
|
|
last->setEmitterCallBackPtr(mpEmitterCallback);
|
|
last->setParticleCallBackPtr(mpParticleCallback);
|
|
loadColors(last, c1, c2, idx1, idx2);
|
|
}
|
|
}
|
|
|
|
if (mpEmitterHead != nullptr) {
|
|
if (mpEmitterCallback != nullptr) {
|
|
// why last and not head?
|
|
mpEmitterCallback->create(last);
|
|
}
|
|
if (mpParticleCallback != nullptr) {
|
|
// why last and not head?
|
|
mpParticleCallback->create(last);
|
|
}
|
|
}
|
|
return mpEmitterHead != nullptr;
|
|
}
|
|
|
|
void dEmitterBase_c::setEmitterCallback(dEmitterCallback_c *cb) {
|
|
if (mpEmitterCallback != cb) {
|
|
if (mpEmitterCallback != nullptr) {
|
|
mpEmitterCallback->remove(this);
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setEmitterCallBackPtr(nullptr);
|
|
}
|
|
}
|
|
mpEmitterCallback = cb;
|
|
if (cb != nullptr) {
|
|
cb->mEmitterList.append(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setParticleCallback(dParticleCallback_c *cb) {
|
|
if (mpParticleCallback != cb) {
|
|
if (mpParticleCallback != nullptr) {
|
|
mpParticleCallback->remove(this);
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setParticleCallBackPtr(nullptr);
|
|
}
|
|
}
|
|
mpParticleCallback = cb;
|
|
if (cb != nullptr) {
|
|
cb->mEmitterList.append(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setImmortal() {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->becomeImmortalEmitter();
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::deactivateEmitters() {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setEmitterCallBackPtr(nullptr);
|
|
emitter->setParticleCallBackPtr(nullptr);
|
|
emitter->quitImmortalEmitter();
|
|
if (emitter->mMaxFrame == 0) {
|
|
emitter->becomeInvalidEmitter();
|
|
}
|
|
emitter->playCalcEmitter();
|
|
}
|
|
mpEmitterHead = nullptr;
|
|
}
|
|
|
|
void dEmitterBase_c::stopCalcEmitters() {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->stopCalcEmitter();
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::playCalcEmitters() {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->playCalcEmitter();
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::stopDrawParticles() {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->stopDrawParticle();
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::playDrawParticles() {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->playDrawParticle();
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setPosRotScale(const mVec3_c &position, const mAng3_c *rot, const mVec3_c *scale) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setGlobalTranslation(position);
|
|
if (rot != nullptr) {
|
|
emitter->setGlobalRotation(*rot);
|
|
}
|
|
if (scale != nullptr) {
|
|
emitter->setGlobalScale(*scale);
|
|
}
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setPosRotScaleWithScreenScale(const mVec3_c &position, const mAng3_c *rot, const mVec3_c *scale) {
|
|
mVec3_c adjustedPosition(position.x * dGfx_c::getCurrentScreenTo4x3WidthScaleF(), position.y, position.z);
|
|
setPosRotScale(adjustedPosition, rot, scale);
|
|
}
|
|
|
|
void dEmitterBase_c::setTransform(const mMtx_c &mtx) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setGlobalSRTMatrix(mtx);
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::loadColors(const GXColor *c1, const GXColor *c2, s32 idx1, s32 idx2) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
loadColors(emitter, c1, c2, idx1, idx2);
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setGlobalAlpha(u8 alpha) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setGlobalAlpha(alpha);
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setRate(f32 rate) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setRate(rate);
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setDynamicsScale(const mVec3_c &scale) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setGlobalDynamicsScale(scale);
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setParticleScale(const mVec3_c &scale) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setGlobalParticleScale(scale);
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setAwayFromCenterSpeed(f32 speed) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setAwayFromCenterSpeed(speed);
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setVolumeSize(u16 size) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setVolumeSize(size);
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::setLifeTime(s16 lifetime) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setLifeTime(lifetime);
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::attachEmitterCallbackId(s32 id) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setEmitterCallBackPtr(&dJEffManager_c::sCommonEmitterCallbacks[id]);
|
|
}
|
|
}
|
|
|
|
void dEmitterBase_c::bindShpEmitter(s32 id, bool unused) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setParticleCallBackPtr(&dJEffManager_c::sShpEmitters[id]);
|
|
emitter->setGlobalAlpha(0);
|
|
}
|
|
}
|
|
|
|
EffectsStruct::EffectsStruct() : mpOwner(nullptr), mFlags(0), mEffect(0) {}
|
|
|
|
EffectsStruct::EffectsStruct(dBase_c *base) : mpOwner(base), mFlags(0), mEffect(0) {}
|
|
|
|
EffectsStruct::~EffectsStruct() {
|
|
mpOwner = nullptr;
|
|
remove(false);
|
|
}
|
|
|
|
void EffectsStruct::remove(bool bForceDeleteEmitters) {
|
|
offFlag(EMITTER_0x1);
|
|
JPABaseEmitter *emitter = bForceDeleteEmitters ? mpEmitterHead : nullptr;
|
|
deactivateEmitters();
|
|
for (; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
dParticle::mgr_c::GetInstance()->forceDeleteEmitter(emitter);
|
|
}
|
|
removeFromActiveEmittersList();
|
|
}
|
|
|
|
void EffectsStruct::addToActiveEmittersList(u16 resourceId, bool bFlags) {
|
|
mEffect = resourceId;
|
|
mFlags = 0;
|
|
setImmortal();
|
|
dJEffManager_c::sPlayingEffectsList.append(this);
|
|
onFlag(EMITTER_0x1);
|
|
if (bFlags) {
|
|
onFlag(EMITTER_0x4);
|
|
}
|
|
if (dParticle::mgr_c::GetInstance()->getResUserWork(resourceId) & 0x4000) {
|
|
onFlag(EMITTER_0x10);
|
|
}
|
|
}
|
|
|
|
bool EffectsStruct::areAllEmittersDone() {
|
|
bool allDone = true;
|
|
|
|
if (mpEmitterHead != nullptr) {
|
|
if (checkFlag(EMITTER_Fading) && mFadeTimer != 0) {
|
|
return false;
|
|
}
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
bool canDelete = emitter->isEnableDeleteEmitter();
|
|
if (!canDelete) {
|
|
if (emitter->mMaxFrame == 0) {
|
|
emitter->becomeInvalidEmitter();
|
|
}
|
|
allDone = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return allDone;
|
|
}
|
|
|
|
void EffectsStruct::execute() {
|
|
if (mpOwner != nullptr && (mpOwner->delete_request || mpOwner->lifecycle_state == fBase_c::TO_BE_DELETED)) {
|
|
mpOwner = nullptr;
|
|
}
|
|
if (dJEffManager_c::shouldBePaused(mpOwner)) {
|
|
if (!checkFlag(EMITTER_0x10)) {
|
|
stopCalcEmitters();
|
|
if (mpOwner != nullptr && !mpOwner->isBasePropertyFlag(0x100)) {
|
|
if (!mpEmitterHead->checkStatus(JPAEmtrStts_StopDraw)) {
|
|
onFlag(EMITTER_0x20);
|
|
stopDrawParticles();
|
|
}
|
|
} else {
|
|
if (checkFlag(EMITTER_0x20)) {
|
|
offFlag(EMITTER_0x20);
|
|
playDrawParticles();
|
|
}
|
|
}
|
|
}
|
|
onFlag(EMITTER_0x1);
|
|
} else {
|
|
playCalcEmitters();
|
|
if (checkFlag(EMITTER_0x20)) {
|
|
offFlag(EMITTER_0x20);
|
|
playDrawParticles();
|
|
}
|
|
if (checkFlag(EMITTER_Fading) && mFadeTimer != 0) {
|
|
mFadeTimer--;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool EffectsStruct::getOwnerPolyAttrs(s32 *pOut1, s32 *pOut2) {
|
|
if (mpOwner != nullptr && mpOwner->group_type == fBase_c::ACTOR) {
|
|
dAcBase_c *actor = static_cast<dAcBase_c *>(mpOwner);
|
|
*pOut1 = actor->polyAttr0;
|
|
*pOut2 = actor->polyAttr1;
|
|
return true;
|
|
} else {
|
|
*pOut1 = 0;
|
|
*pOut2 = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void EffectsStruct::realizeAlpha() {
|
|
setGlobalAlpha(mFadeTimer * (255.0f / mFadeDuration));
|
|
}
|
|
|
|
void EffectsStruct::setFading(u8 lifetime) {
|
|
if (!checkFlag(EMITTER_Fading)) {
|
|
mFadeTimer = lifetime;
|
|
onFlag(EMITTER_Fading);
|
|
mFadeDuration = lifetime;
|
|
}
|
|
}
|
|
|
|
bool dParticleFogProc_c::create(u32 idx, s32 prioOpa, s32 prioXlu, mHeapAllocator_c *alloc) {
|
|
if (!d3d::UnkProc::create(prioOpa, prioXlu, alloc)) {
|
|
return false;
|
|
}
|
|
mIdx = idx;
|
|
field_0x1C = idx == 0 || idx == 2 || idx == 4 || idx == 6 || idx == 10;
|
|
return true;
|
|
}
|
|
|
|
void dParticleFogProc_c::doDraw() {
|
|
nw4r::g3d::Camera c = m3d::getCurrentCamera();
|
|
mMtx_c camMtx, texProjMtx;
|
|
c.GetCameraMtx(camMtx);
|
|
c.GetProjectionTexMtx(texProjMtx);
|
|
JPADrawInfo info;
|
|
MTXCopy(camMtx, info.mCamMtx);
|
|
MTXCopy(texProjMtx, info.mPrjMtx);
|
|
if (field_0x1C) {
|
|
m3d::getFogMgr(0); // ignored, maybe an inline on FogMgr?
|
|
nw4r::g3d::G3DState::LoadFog(0);
|
|
} else {
|
|
mColor c(0);
|
|
GXSetFog(GX_FOG_NONE, c, 0.0f, 0.0f, 0.0f, 0.0f);
|
|
GXSetFogRangeAdj(GX_FALSE, 0, nullptr);
|
|
}
|
|
GXSetTevSwapModeTable(GX_TEV_SWAP0, GX_CH_RED, GX_CH_GREEN, GX_CH_BLUE, GX_CH_ALPHA);
|
|
dJEffManager_c::draw(&info, mIdx);
|
|
}
|
|
|
|
extern "C" bool fn_80054AD0();
|
|
void dEffect2D_c::draw() {
|
|
f32 proj[GX_PROJECTION_SZ];
|
|
GXGetProjectionv(proj);
|
|
f32 base = proj[1];
|
|
proj[1] = base / dGfx_c::get16x9to4x3WidthScaleF();
|
|
GXSetProjectionv(proj);
|
|
f32 f = dGfx_c::getWidth16x9() / 2;
|
|
f32 h = dGfx_c::getCurrentScreenHeightLimitF();
|
|
mMtx_c mtx1, mtx2;
|
|
JPADrawInfo info;
|
|
|
|
C_MTXLightOrtho(mtx1, -h, h, -f, f, 0.5f, 0.5f, 0.5f, 0.5f);
|
|
|
|
MTXScale(mtx2, fn_80054AD0() ? dGfx_c::get16x9to4x3WidthScaleF() : 1.0f, 1.0f, 1.0f);
|
|
MTXCopy(mtx2, info.mCamMtx);
|
|
MTXCopy(mtx1, info.mPrjMtx);
|
|
dJEffManager_c::draw(&info, mGroupId);
|
|
proj[1] = base;
|
|
GXSetProjectionv(proj);
|
|
}
|
|
|
|
void dEffect2D_c::create(u32 groupId, u8 prio) {
|
|
mGroupId = groupId;
|
|
setPriority(prio);
|
|
}
|
|
|
|
bool dMassObjEmitterCallback_c::start(const mVec3_c &v1, dAcObjBase_c *owner) {
|
|
if (field_0x658 >= 50) {
|
|
return false;
|
|
}
|
|
|
|
field_0x010[field_0x658] = v1;
|
|
field_0x4C0[field_0x658] = owner;
|
|
|
|
field_0x658++;
|
|
return true;
|
|
}
|
|
|
|
void dMassObjEmitterCallback_c::executeAfter(JPABaseEmitter *emitter) {
|
|
s32 createNumber = emitter->getCurrentCreateNumber();
|
|
if (createNumber > 0) {
|
|
s32 i = 0;
|
|
mVec3_c translate;
|
|
emitter->getLocalTranslation(translate);
|
|
if (field_0x654 != 0) {
|
|
for (; i < field_0x654; i++) {
|
|
mVec3_c newTranslate = mVec3_c(field_0x268[i] + translate);
|
|
emitter->setGlobalTranslation(newTranslate);
|
|
for (s32 j = 0; j < createNumber; j++) {
|
|
JPABaseParticle *p = emitter->createParticle();
|
|
if (p != nullptr) {
|
|
p->setOffsetPosition(newTranslate);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void dMassObjEmitterCallback_c::execute() {
|
|
for (s32 i = field_0x654 - 1; i >= 0; i--) {
|
|
if (field_0x588[i] != nullptr) {
|
|
if (!dJEffManager_c::shouldBePaused(field_0x588[i]) || field_0x588[i]->delete_request ||
|
|
field_0x588[i]->lifecycle_state == fBase_c::TO_BE_DELETED) {
|
|
if (field_0x654 != 0 && i != field_0x654 - 1) {
|
|
field_0x588[i] = field_0x588[field_0x654 - 1];
|
|
field_0x268[i] = field_0x268[field_0x654 - 1];
|
|
} else {
|
|
field_0x588[i] = nullptr;
|
|
}
|
|
field_0x654--;
|
|
}
|
|
} else {
|
|
field_0x654--;
|
|
}
|
|
}
|
|
if (field_0x658 + field_0x654 > 50) {
|
|
field_0x658 = 50 - field_0x654;
|
|
}
|
|
|
|
for (u32 i = 0; i < field_0x658; i++) {
|
|
field_0x588[field_0x654] = field_0x4C0[i];
|
|
field_0x268[field_0x654] = field_0x010[i];
|
|
field_0x654++;
|
|
}
|
|
field_0x658 = 0;
|
|
}
|
|
|
|
#pragma opt_unroll_loops off
|
|
void dMassObjEmitterCallback_c::clear() {
|
|
// TODO: this unrolls the entire loops,
|
|
// but the original code uses an unroll
|
|
// factor of 10 (5 iterations). 2D arrays
|
|
// work here but are probably fake.
|
|
field_0x654 = 0;
|
|
for (u32 i = 0; i < 50; i++) {
|
|
field_0x588[i] = nullptr;
|
|
}
|
|
field_0x658 = 0;
|
|
for (u32 i = 0; i < 50; i++) {
|
|
field_0x4C0[i] = nullptr;
|
|
}
|
|
}
|
|
#pragma opt_unroll_loops reset
|
|
|
|
void dMassObjEmitter_c::create(u16 resourceId) {
|
|
setEmitterCallback(&mCallback);
|
|
if (createEmitters(resourceId, mVec3_c::Zero, nullptr, nullptr, nullptr, nullptr, 0, 0)) {
|
|
for (JPABaseEmitter *emitter = mpEmitterHead; emitter != nullptr; emitter = GetNextEmitter(emitter)) {
|
|
emitter->setStatus(JPAEmtrStts_Immortal);
|
|
emitter->setStatus(JPAEmtrStts_StopEmit);
|
|
}
|
|
mCallback.clear();
|
|
}
|
|
}
|
|
|
|
void dShpEmitterProc::doDraw() {
|
|
mMtx_c viewMtx;
|
|
getViewMtx(viewMtx);
|
|
s32 ctrl = 0;
|
|
mLightSetId = mAlpha[0];
|
|
mMtx_c *pTransforms = mpTransforms;
|
|
bool *pFlags = mpFlags;
|
|
for (s32 idx = 0; idx < mCount;) {
|
|
if (*pFlags == false) {
|
|
mMtx_c mtx;
|
|
mtx.copyFrom(*pTransforms);
|
|
MTXConcat(viewMtx, mtx, mtx);
|
|
mMtx_c mtx2;
|
|
mtx2.copyFrom(mtx);
|
|
mtx2.fn_802F1C40(0, 1);
|
|
nw4r::math::MTX33 mtx3;
|
|
for (int a = 0; a < 3; a++) {
|
|
for (int b = 0; b < 3; b++) {
|
|
mtx3.m[a][b] = mtx2.m[a][b];
|
|
}
|
|
}
|
|
if (mLightSetId != mAlpha[idx]) {
|
|
mLightSetId = mAlpha[idx];
|
|
setupLight();
|
|
}
|
|
mVec3_c v;
|
|
mVec3_c zero(0.0f, 0.0f, 0.0f);
|
|
MTXMultVec(*pTransforms, zero, v);
|
|
dShpProcBase_c::draw(&v);
|
|
nw4r::g3d::G3DState::SetViewPosNrmMtxArray(mtx, &mtx3, nullptr);
|
|
nw4r::g3d::Draw1Mat1ShpDirectly(nw4r::g3d::ResMat(nullptr), mShp, mtx, mtx2, ctrl, nullptr, nullptr);
|
|
if (ctrl == 0) {
|
|
ctrl = 2;
|
|
}
|
|
}
|
|
idx++;
|
|
pTransforms++;
|
|
pFlags++;
|
|
}
|
|
mpOwner->clear();
|
|
}
|
|
|
|
bool dShpEmitter_c::create(mHeapAllocator_c *alloc) {
|
|
if (!mProc.create(nw4r::g3d::ResMat(nullptr), nw4r::g3d::ResShp(nullptr), 100, alloc, false, nullptr)) {
|
|
return false;
|
|
}
|
|
mProc.setOwner(this);
|
|
return true;
|
|
}
|
|
|
|
void dShpEmitter_c::init(const char *mdlName, const char *arcName, bool priority) {
|
|
clear();
|
|
void *ptr = OarcManager::GetInstance()->getSubEntryData(arcName, "g3d/model.brres");
|
|
if (ptr == nullptr) {
|
|
mProc.setResMat(nw4r::g3d::ResMat(nullptr));
|
|
mProc.setResShp(nw4r::g3d::ResShp(nullptr));
|
|
return;
|
|
}
|
|
|
|
nw4r::g3d::ResFile f(ptr);
|
|
|
|
if (strequals(arcName, "F300BrokenRockWall")) {
|
|
dStage_c::bindStageResToFile(&f);
|
|
}
|
|
|
|
nw4r::g3d::ResMdl mdl = f.GetResMdl(mdlName);
|
|
nw4r::g3d::ResMat mat = mdl.GetResMat(0);
|
|
mProc.setResMat(mat);
|
|
mProc.setResShp(mdl.GetResShp(0));
|
|
if (priority) {
|
|
mProc.setPriorityDraw(0x1C, 4);
|
|
}
|
|
}
|
|
|
|
void dShpEmitter_c::clear() {
|
|
field_0xA0 = 0;
|
|
mProc.setAllFlags();
|
|
}
|
|
|
|
void dShpEmitter_c::draw(JPABaseEmitter *emitter, JPABaseParticle *particle) {
|
|
// TODO
|
|
for (s32 i = 0; i < 100; i++) {
|
|
if (mProc.isDrawDisabled(i)) {
|
|
mProc.setDrawDisabled(i, false);
|
|
mVec3_c pos;
|
|
mVec3_c velocity;
|
|
particle->getGlobalPosition(pos);
|
|
particle->getVelVec(velocity);
|
|
mVec3_c v;
|
|
if (cM::isZero(velocity.squareMagXZ())) {
|
|
v = mVec3_c::Ex;
|
|
} else {
|
|
v = mVec3_c::Ey.cross(velocity);
|
|
v.normalize();
|
|
}
|
|
mMtx_c m;
|
|
m.setAxisRotation(v, mAng((u16)particle->mRotateAngle).radian2());
|
|
m.setBase(3, pos);
|
|
mMtx_c mtx2;
|
|
mVec3_c scale(
|
|
particle->getParticleScaleX() * emitter->getGlobalParticleScaleX(),
|
|
particle->getParticleScaleY() * emitter->getGlobalParticleScaleY(),
|
|
particle->getParticleScaleX() * emitter->getGlobalParticleScaleX()
|
|
);
|
|
MTXScale(mtx2, scale.x, scale.y, scale.z);
|
|
MTXConcat(m, mtx2, m);
|
|
mProc.setTransform(i, m);
|
|
mProc.setAlpha(i, emitter->getGlobalAlpha());
|
|
field_0xA0++;
|
|
break;
|
|
}
|
|
}
|
|
// Hide the particle - we're doing all the drawing ourselves
|
|
particle->setInvisibleParticleFlag();
|
|
}
|
|
|
|
void CommonEmitterCallback::draw(JPABaseEmitter *emitter) {
|
|
emitter->loadTexture(field_0x04, GX_TEXMAP0);
|
|
}
|
|
|
|
void dMassObjEmitter_c::remove() {
|
|
setEmitterCallback(nullptr);
|
|
mpEmitterHead = nullptr;
|
|
}
|
|
|
|
extern "C" u32 sNumMassObjEmitters; // should be const?
|
|
extern "C" dMassObjEmitter_c *CURRENT_EFFECT_MANAGER_INIT;
|
|
|
|
const u32 dJEffManager_c::moreInts[] = {3, 5, 7, 8, 12, 13, 14, 1, 9, 11};
|
|
|
|
s32 dJEffManager_c::getGroupId(u16 resourceId) {
|
|
u32 mask = dParticle::mgr_c::GetInstance()->getResUserWork(resourceId);
|
|
s32 bit = 1;
|
|
for (int i = 0; i < 10; i++) {
|
|
if ((mask & (1 << i)) != 0) {
|
|
bit = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int i = moreInts[bit];
|
|
// TODO explain this
|
|
if ((mask & 0x8000) != 0) {
|
|
if (i == 3) {
|
|
i = 2;
|
|
} else if (i == 5) {
|
|
i = 4;
|
|
} else if (i == 7) {
|
|
i = 6;
|
|
} else if (i == 1) {
|
|
i = 0;
|
|
} else if (i == 11) {
|
|
i = 10;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
// various grasses being cut
|
|
extern const u16 sEffectResourceIds[];
|
|
|
|
void dJEffManager_c::setupEffects() {
|
|
for (s32 idx = 0; idx < sNumMassObjEmitters; idx++) {
|
|
sMassObjEmitters[idx].create(sEffectResourceIds[idx]);
|
|
}
|
|
|
|
sShpEmitters[TsuboA].init("FX_TsuboA", "Tubo", false);
|
|
sShpEmitters[TsuboB].init("FX_TsuboB", "Tubo", false);
|
|
sShpEmitters[BRockA].init("FX_BRockA", "FXBRockA", true);
|
|
sShpEmitters[FruitA].init("FX_FruitA", "FruitA", false);
|
|
sShpEmitters[GrassCoil].init("FX_GrassCoil", "GrassCoilPiece", false);
|
|
sShpEmitters[BRockB].init("FX_BRockB", "RockFace", true);
|
|
sShpEmitters[Beehive].init("FX_Beehive", "Bee", false);
|
|
sShpEmitters[VeraRock].init("FX_VeraRock", "Bvd", false);
|
|
sShpEmitters[Pumpkin].init("FX_Pumpkin", "Pumpkin", false);
|
|
sShpEmitters[BRockC].init("FX_BRockC", "FX_BRockC", true);
|
|
sShpEmitters[F300BrokenRockWall_00].init("FX_F300BrokenRockWall_00", "F300BrokenRockWall", true);
|
|
sShpEmitters[F300BrokenRockWall_01].init("FX_F300BrokenRockWall_01", "F300BrokenRockWall", true);
|
|
sShpEmitters[F300BrokenRockWall_01T].init("FX_F300BrokenRockWall_01T", "F300BrokenRockWall", true);
|
|
sShpEmitters[LotusSeed].init("FX_LotusSeed", "LotusSeed", false);
|
|
sShpEmitters[Beamoss].init("FX_Beamoss", "Beamos", false);
|
|
sShpEmitters[Cakram].init("FX_Cakram", "Asura", false);
|
|
sShpEmitters[Dodai].init("FX_Dodai", "Asura", false);
|
|
sShpEmitters[LegParts].init("FX_LegParts", "Asura", false);
|
|
sShpEmitters[AsuraFloorBrk].init("FX_AsuraFloorBrk", "Asura", false);
|
|
sShpEmitters[BreakPillar].init("FX_BreakPillar", "BreakPillar", false);
|
|
sShpEmitters[Barrel].init("FX_Barrel", "Barrel", false);
|
|
sShpEmitters[BarrelBomb].init("FX_BarrelBomb", "BarrelBomb", false);
|
|
sShpEmitters[RockMiddle].init("FX_RockMiddle", "RockCarryMiddle", false);
|
|
sShpEmitters[BocoBone].init("FX_BocoBone", "Skull", false);
|
|
sShpEmitters[BRockStopA].init("FX_BRockStopA", "BRockStopA", true);
|
|
sShpEmitters[BWallD201].init("FX_BWallD201", "BWallD201", true);
|
|
sShpEmitters[Gunho].init("FX_Gunho", "Gunho", false);
|
|
sShpEmitters[TuboBig].init("FX_TuboBig", "TuboBig", false);
|
|
sShpEmitters[Amos].init("FX_Amos", "Amos", false);
|
|
sShpEmitters[HidokariBoneA].init("FX_HidokariBoneA", "HidokariS", false);
|
|
sShpEmitters[HidokariBoneB].init("FX_HidokariBoneB", "HidokariS", false);
|
|
sShpEmitters[Hydra].init("FX_Hydra", "Hydra", false);
|
|
sShpEmitters[Captain].init("FX_Captain", "Captain", false);
|
|
sShpEmitters[KibakoHang].init("FX_KibakoHang", "KibakoHang", true);
|
|
sShpEmitters[IslTreIRock].init("FX_IslTreIRock", "IslTreIRock", false);
|
|
sShpEmitters[BirdraceTarget].init("FX_BirdraceTarget", "BirdraceTarget", false);
|
|
sShpEmitters[FlowerA00F].init("FX_FlowerA00F", "FlowerA00", false);
|
|
sShpEmitters[FlowerA00L].init("FX_FlowerA00L", "FlowerA00", false);
|
|
sShpEmitters[FlowerB00].init("FX_FlowerB00", "FlowerB00", false);
|
|
sShpEmitters[FlowerB01].init("FX_FlowerB01", "FlowerB01", false);
|
|
sShpEmitters[GirahimClaymore].init("FX_GirahimClaymore", "GirahimBt03", false);
|
|
sShpEmitters[BirdKobunAEgg].init("FX_BirdKobunAEgg", "BirdEgg", false);
|
|
sShpEmitters[BirdKobunBEgg].init("FX_BirdKobunBEgg", "BirdEgg", false);
|
|
sShpEmitters[BirdRivalEgg].init("FX_BirdRivalEgg", "BirdEgg", false);
|
|
sShpEmitters[BWallAF200].init("FX_BWallAF200", "BWallAF200", true);
|
|
sShpEmitters[BWallBF200].init("FX_BWallBF200", "BWallBF200", true);
|
|
sShpEmitters[BWallF210].init("FX_BWallF210", "BWallF210", true);
|
|
}
|
|
|
|
u32 dJEffManager_c::sInts[] = {0x28, 0x29, 0x87, 0x88, 0x89, 0x8A, 0x8C, 0x8D, 0x91, 0x86, 0x1, 0x2};
|
|
u32 dJEffManager_c::sInts2[] = {0x2, 0x87, 0x8B};
|
|
|
|
void dJEffManager_c::removeEffManagers() {
|
|
for (s32 i = 0; i < sNumMassObjEmitters; i++) {
|
|
sMassObjEmitters[i].remove();
|
|
}
|
|
|
|
// TODO
|
|
EffectsList::Iterator it = sPlayingEffectsList.GetBeginIter();
|
|
while (it != sPlayingEffectsList.GetEndIter()) {
|
|
EffectsList::Iterator itCopy;
|
|
++itCopy;
|
|
it->remove(true);
|
|
it = itCopy;
|
|
}
|
|
|
|
dParticle::mgr_c::GetInstance()->forceDeleteAllEmitter();
|
|
for (s32 i = 0; i < 47; i++) {
|
|
sShpEmitters[i].clear();
|
|
}
|
|
}
|
|
|
|
void dJEffManager_c::doCustomSkywardSwordThing(f32 x, f32 y) {
|
|
EffectsList::Iterator it = sPlayingEffectsList.GetBeginIter();
|
|
while (it != sPlayingEffectsList.GetEndIter()) {
|
|
if (it->getEmitterCallback() != nullptr && it->hasEmitters() && it->getGroupId() < 12) {
|
|
it->getEmitterCallback()->vt_0x20(x, y);
|
|
}
|
|
++it;
|
|
}
|
|
|
|
for (s32 i = 0; i < 12; i++) {
|
|
dParticle::mgr_c::GetInstance()->doCustomSkywardSwordThing(i, x, y);
|
|
}
|
|
}
|
|
|
|
void dJEffManager_c::execute() {
|
|
for (EffectsList::Iterator it = sPlayingEffectsList.GetBeginIter(); it != sPlayingEffectsList.GetEndIter();) {
|
|
EffectsList::Iterator itNext = it;
|
|
++itNext;
|
|
it->execute();
|
|
if (it->checkFlag(EffectsStruct::EMITTER_Fading)) {
|
|
it->realizeAlpha();
|
|
}
|
|
if (it->checkFlag(EffectsStruct::EMITTER_0x1)) {
|
|
it->offFlag(EffectsStruct::EMITTER_0x1);
|
|
} else {
|
|
if (it->areAllEmittersDone()) {
|
|
it->remove(false);
|
|
} else {
|
|
it->onFlag(EffectsStruct::EMITTER_0x2);
|
|
}
|
|
}
|
|
it = itNext;
|
|
}
|
|
|
|
if (dStageMgr_c::GetInstance() != nullptr) {
|
|
CURRENT_EFFECT_MANAGER_INIT->setGlobalAlpha(dStageMgr_c::GetInstance()->getGlobalAlpha());
|
|
}
|
|
|
|
for (s32 i = 0; i < sNumMassObjEmitters; i++) {
|
|
mColor c = sMassObjEmitters[i].getField_0x67C();
|
|
sMassObjEmitters[i].loadColors(&c, nullptr, 0, 0);
|
|
sMassObjEmitters[i].execute();
|
|
}
|
|
|
|
if ((dBase_c::s_ExecuteControlFlags & 0x6F9) == 0) {
|
|
for (int i = 0; i < 12; i++) {
|
|
dParticle::mgr_c::GetInstance()->calc(i);
|
|
}
|
|
} else {
|
|
dParticle::mgr_c::GetInstance()->calc(9);
|
|
}
|
|
|
|
dParticle::mgr_c::GetInstance()->calc(12);
|
|
dParticle::mgr_c::GetInstance()->calc(13);
|
|
dParticle::mgr_c::GetInstance()->calc(14);
|
|
}
|
|
|
|
void dJEffManager_c::draw() {
|
|
for (s32 i = 0; i < 12; i++) {
|
|
sFogProcs[i].entry();
|
|
}
|
|
|
|
s2DEffects[0].addToDrawList();
|
|
s2DEffects[1].addToDrawList();
|
|
s2DEffects[2].addToDrawList();
|
|
|
|
for (s32 i = 0; i < 47; i++) {
|
|
sShpEmitters[i].draw();
|
|
}
|
|
}
|
|
|
|
bool dJEffManager_c::shouldBePaused(dBase_c *owner) {
|
|
return owner != nullptr && !owner->isBasePropertyFlag(dBase_c::BASE_PROP_0x4) &&
|
|
(EventManager::isInEvent() || owner->isProcControlFlag(fBase_c::DISABLE_EXECUTE) ||
|
|
// TODO execute control flags
|
|
(owner->s_ExecuteControlFlags & 0x6fb));
|
|
}
|
|
|
|
void dJEffManager_c::draw(const JPADrawInfo *info, u32 groupId) {
|
|
dParticle::mgr_c::GetInstance()->draw(info, groupId);
|
|
}
|
|
|
|
bool dJEffManager_c::createEffManagers() {
|
|
EGG::Heap *heap = dHeap::work1Heap.heap;
|
|
ms_allocator = new (heap, 4) mHeapAllocator_c();
|
|
sMassObjEmitters = new (heap, 4) dMassObjEmitter_c[sNumMassObjEmitters];
|
|
|
|
for (s32 idx = 0; idx < sNumMassObjEmitters; idx++) {
|
|
// TODO explain this
|
|
if (sEffectResourceIds[idx] == PARTICLE_RESOURCE_ID_MAPPING_119_) {
|
|
CURRENT_EFFECT_MANAGER_INIT = &sMassObjEmitters[idx];
|
|
break;
|
|
}
|
|
}
|
|
|
|
ms_allocator->createFrmHeapToCurrent(-1, dHeap::work1Heap.heap, "dJEffManager_c::ms_allocator", 0x20, mHeap::OPT_NONE);
|
|
for (s32 idx = 0; idx < 2; idx++) {
|
|
sFogProcs[idx].create(idx, sInts[idx], -1, ms_allocator);
|
|
}
|
|
for (s32 idx = 2; idx < 12; idx++) {
|
|
sFogProcs[idx].create(idx, -1, sInts[idx], ms_allocator);
|
|
}
|
|
|
|
for (s32 idx = 0; idx < 3; idx++) {
|
|
s2DEffects[idx].create(idx + 12, sInts2[idx]);
|
|
}
|
|
|
|
for (s32 idx = 0; idx < 47; idx++) {
|
|
sShpEmitters[idx].create(ms_allocator);
|
|
}
|
|
|
|
ms_allocator->adjustFrmHeapRestoreCurrent();
|
|
for (int i = 0; i < 2; i++) {
|
|
sCommonEmitterCallbacks[i].init(i);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
dEmitterBase_c *dJEffManager_c::spawnEffectInternal(
|
|
u16 effectResourceId, const mVec3_c &position, const mAng3_c *rot, const mVec3_c *scale, const GXColor *c1,
|
|
const GXColor *c2, s32 idx1, s32 idx2
|
|
) {
|
|
if (!sEmitter.createEmitters(effectResourceId, position, rot, scale, c1, c2, idx1, idx2)) {
|
|
return nullptr;
|
|
}
|
|
return &sEmitter;
|
|
}
|
|
|
|
dEmitterBase_c *dJEffManager_c::spawnEffectInternal(
|
|
u16 effectResourceId, const mMtx_c &transform, const GXColor *c1, const GXColor *c2, s32 idx1, s32 idx2
|
|
) {
|
|
dEmitterBase_c *e = spawnEffectInternal(effectResourceId, mVec3_c::Zero, nullptr, nullptr, c1, c2, idx1, idx2);
|
|
if (e != nullptr) {
|
|
e->setTransform(transform);
|
|
}
|
|
return e;
|
|
}
|
|
|
|
dEmitterBase_c *dJEffManager_c::spawnEffect(
|
|
u16 effectResourceId, const mVec3_c &position, const mAng3_c *rot, const mVec3_c *scale, const GXColor *c1,
|
|
const GXColor *c2, s32 idx1, s32 idx2
|
|
) {
|
|
return spawnEffectInternal(effectResourceId, position, rot, scale, c1, c2, idx1, idx2);
|
|
}
|
|
|
|
dEmitterBase_c *dJEffManager_c::spawnUIEffect(
|
|
u16 effectResourceId, const mVec3_c &position, const mAng3_c *rot, const mVec3_c *scale, const GXColor *c1,
|
|
const GXColor *c2
|
|
) {
|
|
mVec3_c adjustedPosition(position.x * dGfx_c::getCurrentScreenTo4x3WidthScaleF(), position.y, position.z);
|
|
return spawnEffectInternal(effectResourceId, adjustedPosition, rot, scale, c1, c2, 0, 0);
|
|
}
|
|
|
|
dEmitterBase_c *dJEffManager_c::spawnEffect(
|
|
u16 effectResourceId, const mMtx_c &transform, const GXColor *c1, const GXColor *c2, s32 idx1, s32 idx2
|
|
) {
|
|
return spawnEffectInternal(effectResourceId, transform, c1, c2, idx1, idx2);
|
|
}
|
|
|
|
bool EffectsStruct::createEffect(
|
|
u16 resourceId, const mVec3_c &pos, const mAng3_c *rot, const mVec3_c *scale, const GXColor *c1, const GXColor *c2
|
|
) {
|
|
return createEffect(true, resourceId, pos, rot, scale, c1, c2);
|
|
}
|
|
|
|
bool EffectsStruct::createUIEffect(
|
|
u16 resourceId, const mVec3_c &pos, const mAng3_c *rot, const mVec3_c *scale, const GXColor *c1, const GXColor *c2
|
|
) {
|
|
mVec3_c adjustedPosition(pos.x * dGfx_c::getCurrentScreenTo4x3WidthScaleF(), pos.y, pos.z);
|
|
return createEffect(true, resourceId, adjustedPosition, rot, scale, c1, c2);
|
|
}
|
|
|
|
bool EffectsStruct::createEffect(u16 resourceId, const mMtx_c &transform, const GXColor *c1, const GXColor *c2) {
|
|
return createEffect(true, resourceId, transform, c1, c2);
|
|
}
|
|
|
|
bool EffectsStruct::createContinuousEffect(
|
|
u16 resourceId, const mVec3_c &pos, const mAng3_c *rot, const mVec3_c *scale, const GXColor *c1, const GXColor *c2
|
|
) {
|
|
return createEffect(false, resourceId, pos, rot, scale, c1, c2);
|
|
}
|
|
|
|
bool EffectsStruct::createContinuousUIEffect(
|
|
u16 resourceId, const mVec3_c &pos, const mAng3_c *rot, const mVec3_c *scale, const GXColor *c1, const GXColor *c2
|
|
) {
|
|
mVec3_c adjustedPosition(pos.x * dGfx_c::getCurrentScreenTo4x3WidthScaleF(), pos.y, pos.z);
|
|
return createEffect(false, resourceId, adjustedPosition, rot, scale, c1, c2);
|
|
}
|
|
|
|
bool EffectsStruct::createContinuousEffect(
|
|
u16 resourceId, const mMtx_c &transform, const GXColor *c1, const GXColor *c2
|
|
) {
|
|
return createEffect(false, resourceId, transform, c1, c2);
|
|
}
|
|
|
|
bool dJEffManager_c::createMassObjEffect(
|
|
u16 effectResourceId, const mVec3_c &v1, dAcObjBase_c *owner, const mColor *color
|
|
) {
|
|
for (s32 i = 0; i < sNumMassObjEmitters; i++) {
|
|
if (effectResourceId == sEffectResourceIds[i]) {
|
|
if (color != nullptr) {
|
|
sMassObjEmitters[i].setField_0x67C(*color);
|
|
} else {
|
|
sMassObjEmitters[i].setField_0x67C(mColor(0xFF, 0xFF, 0xFF, 0xFF));
|
|
}
|
|
return sMassObjEmitters[i].start(v1, owner);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void dEmitterBase_c::loadColors(
|
|
JPABaseEmitter *emitter, const GXColor *color1, const GXColor *color2, s32 plltIdx1, s32 plltIdx2
|
|
) {
|
|
u8 r1 = 0xFF;
|
|
u8 g1 = 0xFF;
|
|
u8 b1 = 0xFF;
|
|
u8 r2 = 0xFF;
|
|
u8 g2 = 0xFF;
|
|
u8 b2 = 0xFF;
|
|
u8 r, g, b;
|
|
|
|
dLightEnv_c &mgr = dLightEnv_c::GetInstance();
|
|
const mColor c1 = mgr.GetCurrentSpf().mParticleTransparentClr;
|
|
const mColor c2 = mgr.GetCurrentSpf().mParticleSolidClr;
|
|
|
|
u32 flags = emitter->getDynResUserWork();
|
|
u32 factor = flags >> 24;
|
|
bool useBlack4 = false;
|
|
if (factor > 100) {
|
|
useBlack4 = true;
|
|
factor = factor - 100;
|
|
}
|
|
f32 fFactor = factor / 100.0f;
|
|
if (fFactor > 1.0f) {
|
|
fFactor = 1.0f;
|
|
}
|
|
|
|
f32 scaleR, scaleG, scaleB;
|
|
if (!useBlack4) {
|
|
scaleR = 1.0f - (1.0f - (c1.r / 255.0f)) * fFactor;
|
|
scaleG = 1.0f - (1.0f - (c1.g / 255.0f)) * fFactor;
|
|
scaleB = 1.0f - (1.0f - (c1.b / 255.0f)) * fFactor;
|
|
} else {
|
|
scaleR = 1.0f - (1.0f - (c2.r / 255.0f)) * fFactor;
|
|
scaleG = 1.0f - (1.0f - (c2.g / 255.0f)) * fFactor;
|
|
scaleB = 1.0f - (1.0f - (c2.b / 255.0f)) * fFactor;
|
|
}
|
|
|
|
if (color1 != nullptr) {
|
|
r1 = color1->r;
|
|
g1 = color1->g;
|
|
b1 = color1->b;
|
|
}
|
|
|
|
if (plltIdx1 > 0) {
|
|
if ((flags & 0x1000) != 0) {
|
|
const mColor &entry = mgr.getSmallEAF(plltIdx1 - 1, plltIdx2).field_0x00;
|
|
r1 = entry.r;
|
|
g1 = entry.g;
|
|
b1 = entry.b;
|
|
} else if ((flags & 0x2000) != 0) {
|
|
const mColor &entry = mgr.getSmallEAF(plltIdx1 - 1, plltIdx2).field_0x08;
|
|
r1 = entry.r;
|
|
g1 = entry.g;
|
|
b1 = entry.b;
|
|
}
|
|
}
|
|
|
|
if (factor != 0) {
|
|
r = r1 * scaleR * mgr.getfield_0x2F14();
|
|
g = g1 * scaleG * mgr.getfield_0x2F18();
|
|
b = b1 * scaleB * mgr.getfield_0x2F1C();
|
|
} else {
|
|
r = r1 * scaleR;
|
|
g = g1 * scaleG;
|
|
b = b1 * scaleB;
|
|
}
|
|
|
|
emitter->setGlobalPrmColor(r, g, b);
|
|
|
|
if (color2 != nullptr) {
|
|
r2 = color2->r;
|
|
g2 = color2->g;
|
|
b2 = color2->b;
|
|
}
|
|
|
|
if (plltIdx1 > 0) {
|
|
if ((flags & 0x1000) != 0) {
|
|
const mColor &entry = mgr.getSmallEAF(plltIdx1 - 1, plltIdx2).field_0x04;
|
|
r2 = entry.r;
|
|
g2 = entry.g;
|
|
b2 = entry.b;
|
|
} else if ((flags & 0x2000) != 0) {
|
|
const mColor &entry = mgr.getSmallEAF(plltIdx1 - 1, plltIdx2).field_0x0C;
|
|
r2 = entry.r;
|
|
g2 = entry.g;
|
|
b2 = entry.b;
|
|
}
|
|
}
|
|
|
|
if (factor != 0) {
|
|
r = mgr.getfield_0x2F14() * r2 * scaleR;
|
|
g = mgr.getfield_0x2F18() * g2 * scaleG;
|
|
b = mgr.getfield_0x2F1C() * b2 * scaleB;
|
|
} else {
|
|
r = r2 * scaleR;
|
|
g = g2 * scaleG;
|
|
b = b2 * scaleB;
|
|
}
|
|
|
|
emitter->setGlobalEnvColor(r, g, b);
|
|
}
|
|
|
|
void EffectsStruct::removeFromActiveEmittersList() {
|
|
if (dJEffManager_c::sPlayingEffectsList.GetPosition(this) != dJEffManager_c::sPlayingEffectsList.GetEndIter()) {
|
|
dJEffManager_c::sPlayingEffectsList.remove(this);
|
|
}
|
|
}
|
|
|
|
bool EffectsStruct::createEffect(
|
|
bool bFlags, u16 resourceId, const mVec3_c &pos, const mAng3_c *rot, const mVec3_c *scale, const GXColor *c1,
|
|
const GXColor *c2
|
|
) {
|
|
if (!bFlags && canReuse(resourceId)) {
|
|
setPosRotScale(pos, rot, scale);
|
|
s32 idx1 = 0;
|
|
s32 idx2 = 0;
|
|
getOwnerPolyAttrs(&idx1, &idx2);
|
|
loadColors(c1, c2, idx1, idx2);
|
|
onFlag(EMITTER_0x1);
|
|
} else {
|
|
remove(false);
|
|
s32 idx1 = 0;
|
|
s32 idx2 = 0;
|
|
getOwnerPolyAttrs(&idx1, &idx2);
|
|
if (createEmitters(resourceId, pos, rot, scale, c1, c2, idx1, idx2)) {
|
|
addToActiveEmittersList(resourceId, bFlags);
|
|
}
|
|
}
|
|
|
|
return hasEmitters();
|
|
}
|
|
|
|
bool EffectsStruct::createEffect(
|
|
bool bFlags, u16 resourceId, const mMtx_c &transform, const GXColor *c1, const GXColor *c2
|
|
) {
|
|
if (!bFlags && canReuse(resourceId)) {
|
|
s32 idx1 = 0;
|
|
s32 idx2 = 0;
|
|
getOwnerPolyAttrs(&idx1, &idx2);
|
|
loadColors(c1, c2, idx1, idx2);
|
|
onFlag(EMITTER_0x1);
|
|
} else {
|
|
remove(false);
|
|
s32 idx1 = 0;
|
|
s32 idx2 = 0;
|
|
getOwnerPolyAttrs(&idx1, &idx2);
|
|
if (createEmitters(resourceId, mVec3_c::Zero, nullptr, nullptr, c1, c2, idx1, idx2)) {
|
|
addToActiveEmittersList(resourceId, bFlags);
|
|
}
|
|
}
|
|
setTransform(transform);
|
|
return hasEmitters();
|
|
}
|
|
|
|
// TODO: Document PolyAttrs
|
|
s32 dJEffManager_c::polyAttrsToGroundEffectIdx(s32 polyAttr0, s32 polyAttr1) {
|
|
if (polyAttr0 == 0 || (polyAttr0 == 4 && polyAttr1 == 1) || (polyAttr0 == 9) ||
|
|
(polyAttr0 == 10 && polyAttr1 == 1) || (polyAttr0 == 12) || (polyAttr0 == 17 && polyAttr1 != 1) ||
|
|
(polyAttr0 == 13 && (polyAttr1 == 1 || polyAttr1 == 3)) || polyAttr0 == 18) {
|
|
return 6;
|
|
} else if (polyAttr0 == 17) {
|
|
return 5;
|
|
} else if (polyAttr0 == 6) {
|
|
return 2;
|
|
} else if (polyAttr0 == 4) {
|
|
return 1;
|
|
} else if (polyAttr0 == 3 || polyAttr0 == 15) {
|
|
return 3;
|
|
}
|
|
// ???
|
|
s32 result = 0;
|
|
if (polyAttr0 == 11) {
|
|
result = 4;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
dEmitterBase_c *dJEffManager_c::spawnGroundEffect(
|
|
const mVec3_c &pos, u8 polyAttr0, u8 polyAttr1, const mVec3_c &v1, s32 unk, f32 scale, f32 groundHeightMaybe
|
|
) {
|
|
static const u16 sEffArray[6][2] = {
|
|
{ PARTICLE_RESOURCE_ID_MAPPING_89_, PARTICLE_RESOURCE_ID_MAPPING_90_},
|
|
{ PARTICLE_RESOURCE_ID_MAPPING_87_, PARTICLE_RESOURCE_ID_MAPPING_88_},
|
|
{PARTICLE_RESOURCE_ID_MAPPING_429_, PARTICLE_RESOURCE_ID_MAPPING_429_},
|
|
{PARTICLE_RESOURCE_ID_MAPPING_416_, PARTICLE_RESOURCE_ID_MAPPING_417_},
|
|
{PARTICLE_RESOURCE_ID_MAPPING_418_, PARTICLE_RESOURCE_ID_MAPPING_419_},
|
|
{PARTICLE_RESOURCE_ID_MAPPING_893_, PARTICLE_RESOURCE_ID_MAPPING_894_},
|
|
};
|
|
|
|
if (pos.y < groundHeightMaybe) {
|
|
return nullptr;
|
|
}
|
|
|
|
s32 idx = polyAttrsToGroundEffectIdx(polyAttr0, polyAttr1);
|
|
if (idx == 6) {
|
|
return nullptr;
|
|
}
|
|
if (idx == 2 && unk == 0) {
|
|
scale *= 1.5f;
|
|
}
|
|
mMtx_c mtx;
|
|
mtx.makeRotationFromVecs(mVec3_c::Ey, v1, 1.0f);
|
|
mAng rot(cM::rndF(65536.0f));
|
|
mtx.YrotM(rot);
|
|
mtx.setTranslation(pos);
|
|
mMtx_c scaleMtx;
|
|
MTXScale(scaleMtx, scale, scale, scale);
|
|
MTXConcat(mtx, scaleMtx, mtx);
|
|
return spawnEffect(sEffArray[idx][unk], mtx, nullptr, nullptr, polyAttr0, polyAttr1);
|
|
}
|
|
|
|
void dWaterEffect_c::init(dAcObjBase_c *base, f32 height, f32 scale, f32 f3) {
|
|
mEff.init(base);
|
|
mHeight = height;
|
|
mScale = scale;
|
|
mDepth = f3;
|
|
}
|
|
|
|
void dWaterEffect_c::execute(f32 water, f32 ground) {
|
|
dAcObjBase_c *ac = getActor();
|
|
bool b = getActorGroundPos(ac) < water && ground < water;
|
|
if (b) {
|
|
if (!mIsInWater) {
|
|
mIsInWater = true;
|
|
mVec3_c pos(ac->position.x, water, ac->position.z);
|
|
mVec3_c scale(mScale, mScale, mScale);
|
|
if (mIsSmall || water - ground < 50.0f) {
|
|
// For small objects or shallow water, create a
|
|
// particle FX upon entering water
|
|
mAng3_c rot(0, cM::rndF(65536.0f), 0);
|
|
dJEffManager_c::spawnEffect(
|
|
PARTICLE_RESOURCE_ID_MAPPING_91_, pos, &rot, &scale, nullptr, nullptr, 0, 0
|
|
);
|
|
} else {
|
|
// Otherwise spawn a "water spout" with a 3d model
|
|
dAcObjBase_c::create(fProfile::OBJ_WATER_SPOUT, -1, 0, &pos, nullptr, &scale, -1);
|
|
}
|
|
}
|
|
} else if (getActorGroundPos(ac) - 5.0f > water) {
|
|
mIsInWater = false;
|
|
}
|
|
|
|
if (mIsInWater && getActorCeilPos(ac) > water) {
|
|
// Spawn effect while in water
|
|
mVec3_c pos(ac->position.x, water, ac->position.z);
|
|
mVec3_c scale(mScale, mScale, mScale);
|
|
mEff.createContinuousEffect(PARTICLE_RESOURCE_ID_MAPPING_127_, pos, nullptr, &scale, nullptr, nullptr);
|
|
f32 rate = nw4r::math::FAbs(ac->forwardSpeed) * 0.02f;
|
|
rate = rate > 0.95f ? 0.95f : rate;
|
|
mEff.setRate(rate + 0.05f);
|
|
}
|
|
}
|