mirror of https://github.com/zeldaret/mm.git
Demo_Kankyo (Lost Woods Sparkles) (#380)
* Demo_Kankyo * DemoKankyo: docs * DemoKankyo: documenting this is a pain... * found bug * DemoKankyo: docs * DemoKankyo: function renamed * DemoKankyo: docs * DemoKankyo: more docks * DemoKankyo: more docks * DemoKankyo: docs and format * DemoKankyo: requested changes * DemoKankyo: bug documented * PR feedback via docs * DemoKankyo: minor documentation and format * DemoKankyo: requested changes * DemoKankyo: removed some old comments Co-authored-by: Maide <eeeedddccc@hotmail.co.uk> Co-authored-by: isghj8 <isghj8@gmail.com> Co-authored-by: engineer124 <engineer124engineer124@gmail.com>
This commit is contained in:
parent
fcbd524b5d
commit
60b9cd789e
|
@ -4034,7 +4034,7 @@ extern Gfx D_04075B30[];
|
|||
extern Gfx D_04076BC0[];
|
||||
extern Gfx D_04077480[];
|
||||
extern UNK_TYPE D_04079B10;
|
||||
extern Gfx D_0407AB10[];
|
||||
extern Gfx D_0407AB10[]; // sun (sparkles when small) displaylist
|
||||
extern Gfx D_0407AB58[];
|
||||
extern UNK_TYPE D_0407AFB0;
|
||||
extern Gfx D_0407D590[];
|
||||
|
|
|
@ -942,7 +942,7 @@ typedef struct {
|
|||
/* 0xEC */ u8 unk_EC;
|
||||
/* 0xED */ u8 unk_ED;
|
||||
/* 0xEE */ u8 unk_EE[4];
|
||||
/* 0xF2 */ u8 unk_F2[8];
|
||||
/* 0xF2 */ u8 unk_F2[8]; // [3] is used by both DemoKankyo and ObjectKankyo particle count
|
||||
/* 0xFA */ u8 unk_FA[4];
|
||||
} EnvironmentContext; // size = 0x100
|
||||
|
||||
|
|
3
spec
3
spec
|
@ -1156,8 +1156,7 @@ beginseg
|
|||
name "ovl_Demo_Kankyo"
|
||||
compress
|
||||
include "build/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.o"
|
||||
include "build/data/ovl_Demo_Kankyo/ovl_Demo_Kankyo.data.o"
|
||||
include "build/data/ovl_Demo_Kankyo/ovl_Demo_Kankyo.reloc.o"
|
||||
include "build/src/overlays/actors/ovl_Demo_Kankyo/ovl_Demo_Kankyo_reloc.o"
|
||||
endseg
|
||||
|
||||
beginseg
|
||||
|
|
|
@ -15,12 +15,15 @@ void DemoKankyo_Destroy(Actor* thisx, GlobalContext* globalCtx);
|
|||
void DemoKankyo_Update(Actor* thisx, GlobalContext* globalCtx);
|
||||
void DemoKankyo_Draw(Actor* thisx, GlobalContext* globalCtx);
|
||||
|
||||
void func_808CF06C(DemoKankyo* this, GlobalContext* globalCtx);
|
||||
void func_808CF0CC(DemoKankyo* this, GlobalContext* globalCtx);
|
||||
void DemoKakyo_MoonSparklesActionFunc(DemoKankyo* this, GlobalContext* globalCtx);
|
||||
|
||||
void DemoKankyo_SetupAction(DemoKankyo* this, DemoKankyoActionFunc actionFunc);
|
||||
extern Gfx D_0407AB58[];
|
||||
extern Gfx D_06001000[]; // the bubble display list used by shabom in giants type
|
||||
extern Gfx D_04023428[];
|
||||
|
||||
static u8 sLostWoodsSparklesMutex = false; // make sure only one can exist at once
|
||||
static s16 sLostWoodsSkyFishParticleNum = 0;
|
||||
|
||||
#if 0
|
||||
const ActorInit Demo_Kankyo_InitVars = {
|
||||
ACTOR_DEMO_KANKYO,
|
||||
ACTORCAT_ITEMACTION,
|
||||
|
@ -33,24 +36,659 @@ const ActorInit Demo_Kankyo_InitVars = {
|
|||
(ActorFunc)DemoKankyo_Draw,
|
||||
};
|
||||
|
||||
#endif
|
||||
static s32 sObjectBubbleIndex = OBJECT_BUBBLE | 0x10000;
|
||||
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Demo_Kankyo/DemoKankyo_SetupAction.s")
|
||||
void DemoKankyo_SetupAction(DemoKankyo* this, DemoKankyoActionFunc actionFunc) {
|
||||
this->actionFunc = actionFunc;
|
||||
}
|
||||
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Demo_Kankyo/func_808CE45C.s")
|
||||
void DemoKakyo_LostWoodsSparkleActionFunc(DemoKankyo* this, GlobalContext* globalCtx) {
|
||||
s32 pad;
|
||||
s32 i;
|
||||
f32 randSkyfishParticleNum;
|
||||
f32 repositionLimit; // Distance from posCenter when particles are relocated. Always set to 130.0f
|
||||
f32 eyeToAtNormX;
|
||||
f32 eyeToAtNormY;
|
||||
f32 eyeToAtNormZ;
|
||||
f32 eyeToAtMag;
|
||||
f32 posCenterX;
|
||||
f32 posCenterY;
|
||||
f32 posCenterZ;
|
||||
Vec3f eyeToAt;
|
||||
Player* player = GET_PLAYER(globalCtx);
|
||||
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Demo_Kankyo/func_808CF06C.s")
|
||||
if (globalCtx->roomCtx.unk7A[1] != 0) {
|
||||
if (globalCtx->envCtx.unk_F2[3] != 0) {
|
||||
globalCtx->envCtx.unk_F2[3]--;
|
||||
} else {
|
||||
Actor_MarkForDeath(&this->actor);
|
||||
}
|
||||
} else if (globalCtx->envCtx.unk_F2[3] < DEMOKANKYO_PARTICLE_COUNT) {
|
||||
globalCtx->envCtx.unk_F2[3] += 16;
|
||||
}
|
||||
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Demo_Kankyo/func_808CF0CC.s")
|
||||
// note: DemoKankyo can crash if placed in an area that snows (ObjectKankyo)
|
||||
// because they both use unk_F2 as a particle counter,
|
||||
// causing DemoKankyo to write beyond its particle array boundry
|
||||
// this crash can occur if the two actors are in different scenes connected by an exit
|
||||
// e.g. if you add DemoKankyo to GoronShrine, you will crash entering/leaving through door
|
||||
for (i = 0; i < globalCtx->envCtx.unk_F2[3]; i++) {
|
||||
repositionLimit = 130.0f;
|
||||
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Demo_Kankyo/DemoKankyo_Init.s")
|
||||
eyeToAt.x = globalCtx->view.at.x - globalCtx->view.eye.x;
|
||||
eyeToAt.y = globalCtx->view.at.y - globalCtx->view.eye.y;
|
||||
eyeToAt.z = globalCtx->view.at.z - globalCtx->view.eye.z;
|
||||
eyeToAtMag = sqrtf(SQXYZ(eyeToAt));
|
||||
eyeToAtNormX = eyeToAt.x / eyeToAtMag;
|
||||
eyeToAtNormY = eyeToAt.y / eyeToAtMag;
|
||||
eyeToAtNormZ = eyeToAt.z / eyeToAtMag;
|
||||
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Demo_Kankyo/DemoKankyo_Destroy.s")
|
||||
switch (this->particles[i].state) {
|
||||
case DEMO_KANKYO_STATE_INIT:
|
||||
this->particles[i].posBase.x = globalCtx->view.eye.x + (eyeToAtNormX * 80.0f);
|
||||
this->particles[i].posBase.y = globalCtx->view.eye.y + (eyeToAtNormY * 80.0f);
|
||||
this->particles[i].posBase.z = globalCtx->view.eye.z + (eyeToAtNormZ * 80.0f);
|
||||
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Demo_Kankyo/DemoKankyo_Update.s")
|
||||
this->particles[i].posOffset.x = (Rand_ZeroOne() - 0.5f) * 160.0f;
|
||||
this->particles[i].posOffset.y = 30.0f;
|
||||
this->particles[i].posOffset.z = (Rand_ZeroOne() - 0.5f) * 160.0f;
|
||||
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Demo_Kankyo/func_808CF970.s")
|
||||
this->particles[i].speedTarget = (Rand_ZeroOne() * 1.6f) + 0.5f;
|
||||
this->particles[i].alpha = 0;
|
||||
this->particles[i].alphaClock = Rand_ZeroOne() * 65535; // random 0 to max of u16
|
||||
this->particles[i].scale = 0.1f;
|
||||
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Demo_Kankyo/func_808CFE04.s")
|
||||
// speedClock is angles in radians,
|
||||
// should have used Rand_ZeroOne() * 2 * M_PI
|
||||
// however, due to properties of sine waves, this is effectively still random
|
||||
this->particles[i].speedClock.x = Rand_ZeroOne() * 360.0f;
|
||||
this->particles[i].speedClock.y = Rand_ZeroOne() * 360.0f;
|
||||
this->particles[i].speedClock.z = Rand_ZeroOne() * 360.0f;
|
||||
this->particles[i].pad50 = 0;
|
||||
this->particles[i].state += DEMO_KANKYO_STATE_SINGLE;
|
||||
break;
|
||||
|
||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Demo_Kankyo/DemoKankyo_Draw.s")
|
||||
case DEMO_KANKYO_STATE_SINGLE:
|
||||
case DEMO_KANKYO_STATE_SKYFISH:
|
||||
this->particles[i].alphaClock++;
|
||||
posCenterX = globalCtx->view.eye.x + (eyeToAtNormX * 80.0f);
|
||||
posCenterY = globalCtx->view.eye.y + (eyeToAtNormY * 80.0f);
|
||||
posCenterZ = globalCtx->view.eye.z + (eyeToAtNormZ * 80.0f);
|
||||
this->particles[i].posOffsetPrev.x = this->particles[i].posOffset.x;
|
||||
this->particles[i].posOffsetPrev.y = this->particles[i].posOffset.y;
|
||||
this->particles[i].posOffsetPrev.z = this->particles[i].posOffset.z;
|
||||
|
||||
if (this->particles[i].state == DEMO_KANKYO_STATE_SINGLE) {
|
||||
|
||||
// The first 32 particles will become skyfish particles
|
||||
// This block is also init code and only runs once
|
||||
if (i < 32) {
|
||||
if (Rand_ZeroOne() < 0.5f) {
|
||||
this->particles[i].LostWoodsSkyFishSpeedXZ = (s16)(Rand_ZeroOne() * 200.0f) + 200;
|
||||
} else {
|
||||
this->particles[i].LostWoodsSkyFishSpeedXZ = -200 - (s16)(Rand_ZeroOne() * 200.0f);
|
||||
}
|
||||
this->particles[i].LostWoodsSkyFishPosOffsetMax = (s16)(Rand_ZeroOne() * 50.0f) + 15;
|
||||
this->particles[i].LostWoodsSkyFishSpeedY = ((Rand_ZeroOne() * 10.0f) + 10.0f) * 0.01f;
|
||||
|
||||
// Only the 31st particle matters as sLostWoodsSkyFishParticleNum will be overwritten
|
||||
// every particle until the last skyfish particle is initialized
|
||||
randSkyfishParticleNum = Rand_ZeroOne();
|
||||
if (randSkyfishParticleNum < 0.2f) {
|
||||
sLostWoodsSkyFishParticleNum = 1;
|
||||
} else if (randSkyfishParticleNum < 0.2f) {
|
||||
// This case is never taken as the else-if conditional is identical to the previous one
|
||||
sLostWoodsSkyFishParticleNum = 3;
|
||||
} else if (randSkyfishParticleNum < 0.4f) {
|
||||
sLostWoodsSkyFishParticleNum = 7;
|
||||
} else {
|
||||
sLostWoodsSkyFishParticleNum = 15;
|
||||
}
|
||||
|
||||
if ((i & sLostWoodsSkyFishParticleNum) == 0) {
|
||||
// Head particle
|
||||
this->particles[i].posOffset.y = 0.0f;
|
||||
}
|
||||
this->particles[i].state = DEMO_KANKYO_STATE_SKYFISH;
|
||||
this->particles[i].speedTarget = 0.0f;
|
||||
}
|
||||
|
||||
Math_SmoothStepToF(&this->particles[i].scale, 0.1, 0.1f, 0.001f, 0.00001f);
|
||||
Math_SmoothStepToF(&this->particles[i].speed, this->particles[i].speedTarget, 0.5f, 0.2f, 0.02f);
|
||||
this->particles[i].posOffset.x +=
|
||||
__sinf(this->particles[i].speedClock.x) * this->particles[i].speed;
|
||||
this->particles[i].posOffset.y +=
|
||||
__sinf(this->particles[i].speedClock.y) * this->particles[i].speed;
|
||||
this->particles[i].posOffset.z +=
|
||||
__sinf(this->particles[i].speedClock.z) * this->particles[i].speed;
|
||||
|
||||
switch ((i >> 1) & 3) {
|
||||
case 0:
|
||||
this->particles[i].speedClock.x += 0.008f;
|
||||
this->particles[i].speedClock.y += 0.05f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.z += 0.015f;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
this->particles[i].speedClock.x += 0.01f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.y += 0.05f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.z += 0.005f * Rand_ZeroOne();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
this->particles[i].speedClock.x += 0.01f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.y += 0.4f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.z += 0.004f * Rand_ZeroOne();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
this->particles[i].speedClock.x += 0.01f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.y += 0.08f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.z += 0.05f * Rand_ZeroOne();
|
||||
break;
|
||||
}
|
||||
|
||||
} else if (this->particles[i].state == DEMO_KANKYO_STATE_SKYFISH) {
|
||||
if ((i & sLostWoodsSkyFishParticleNum) == 0) {
|
||||
// Head particle
|
||||
Math_SmoothStepToF(&this->particles[i].scale, 0.25f, 0.1f, 0.001f, 0.00001f);
|
||||
|
||||
Math_SmoothStepToF(&this->particles[i].posBase.x, player->actor.world.pos.x, 0.5f, 1.0f, 0.2f);
|
||||
Math_SmoothStepToF(&this->particles[i].posBase.y, player->actor.world.pos.y + 50.0f, 0.5f, 1.0f,
|
||||
0.2f);
|
||||
Math_SmoothStepToF(&this->particles[i].posBase.z, player->actor.world.pos.z, 0.5f, 1.0f, 0.2f);
|
||||
|
||||
Math_SmoothStepToF(&this->particles[i].posOffset.x,
|
||||
Math_SinS(this->particles[i].LostWoodsSkyFishSpeedXZClock - 0x8000) *
|
||||
this->particles[i].LostWoodsSkyFishPosOffsetMax,
|
||||
0.5f, 2.0f, 0.2f);
|
||||
Math_SmoothStepToF(&this->particles[i].posOffset.z,
|
||||
Math_CosS(this->particles[i].LostWoodsSkyFishSpeedXZClock - 0x8000) *
|
||||
this->particles[i].LostWoodsSkyFishPosOffsetMax,
|
||||
0.5f, 2.0f, 0.2f);
|
||||
this->particles[i].LostWoodsSkyFishSpeedXZClock += this->particles[i].LostWoodsSkyFishSpeedXZ;
|
||||
this->particles[i].posOffset.y += __sinf(this->particles[i].speedClock.y);
|
||||
this->particles[i].speedClock.x += 0.2f * Rand_ZeroOne(); // unused calculation
|
||||
this->particles[i].speedClock.y += this->particles[i].LostWoodsSkyFishSpeedY;
|
||||
this->particles[i].speedClock.z += 0.1f * Rand_ZeroOne(); // unused calculation
|
||||
|
||||
this->particles[i].posOffset.x =
|
||||
Math_SinS(this->particles[i].LostWoodsSkyFishSpeedXZClock - 0x8000) *
|
||||
this->particles[i].LostWoodsSkyFishPosOffsetMax;
|
||||
this->particles[i].posOffset.z =
|
||||
Math_CosS(this->particles[i].LostWoodsSkyFishSpeedXZClock - 0x8000) *
|
||||
this->particles[i].LostWoodsSkyFishPosOffsetMax;
|
||||
} else {
|
||||
// Tail Particles
|
||||
Math_SmoothStepToF(&this->particles[i].scale, 0.1, 0.1f, 0.001f, 0.00001f);
|
||||
|
||||
// Unused calculation, speed only used in posOffset calculations,
|
||||
// but posOffset gets overwritten for tail particles immediately below
|
||||
Math_SmoothStepToF(&this->particles[i].speed, 1.5f, 0.5f, 0.1f, 0.0002f);
|
||||
|
||||
// particles in the skyfish's tail are moved to the previous position of the particle directly
|
||||
// in front
|
||||
this->particles[i].posOffset.x =
|
||||
this->particles[i - 1].posOffsetPrev.x +
|
||||
(this->particles[i - 1].posBase.x - this->particles[i].posBase.x);
|
||||
this->particles[i].posOffset.y =
|
||||
this->particles[i - 1].posOffsetPrev.y +
|
||||
(this->particles[i - 1].posBase.y - this->particles[i].posBase.y);
|
||||
this->particles[i].posOffset.z =
|
||||
this->particles[i - 1].posOffsetPrev.z +
|
||||
(this->particles[i - 1].posBase.z - this->particles[i].posBase.z);
|
||||
}
|
||||
}
|
||||
|
||||
if ((this->particles[i].state != DEMO_KANKYO_STATE_SKYFISH) &&
|
||||
((((this->particles[i].posBase.x + this->particles[i].posOffset.x) - posCenterX) >
|
||||
repositionLimit) ||
|
||||
(((this->particles[i].posBase.x + this->particles[i].posOffset.x) - posCenterX) <
|
||||
-repositionLimit) ||
|
||||
(((this->particles[i].posBase.y + this->particles[i].posOffset.y) - posCenterY) >
|
||||
repositionLimit) ||
|
||||
(((this->particles[i].posBase.y + this->particles[i].posOffset.y) - posCenterY) <
|
||||
-repositionLimit) ||
|
||||
(((this->particles[i].posBase.z + this->particles[i].posOffset.z) - posCenterZ) >
|
||||
repositionLimit) ||
|
||||
(((this->particles[i].posBase.z + this->particles[i].posOffset.z) - posCenterZ) <
|
||||
-repositionLimit))) {
|
||||
if (((this->particles[i].posOffset.x + this->particles[i].posBase.x) - posCenterX) >
|
||||
repositionLimit) {
|
||||
this->particles[i].posOffset.x = 0.0f;
|
||||
this->particles[i].posBase.x = posCenterX - repositionLimit;
|
||||
}
|
||||
|
||||
if (((this->particles[i].posBase.x + this->particles[i].posOffset.x) - posCenterX) <
|
||||
-repositionLimit) {
|
||||
this->particles[i].posOffset.x = 0.0f;
|
||||
this->particles[i].posBase.x = posCenterX + repositionLimit;
|
||||
}
|
||||
|
||||
if (((this->particles[i].posBase.y + this->particles[i].posOffset.y) - posCenterY) > 50.0f) {
|
||||
this->particles[i].posOffset.y = 0.0f;
|
||||
this->particles[i].posBase.y = posCenterY - 50.0f;
|
||||
}
|
||||
|
||||
if (((this->particles[i].posBase.y + this->particles[i].posOffset.y) - posCenterY) < -50.0f) {
|
||||
this->particles[i].posOffset.y = 0.0f;
|
||||
this->particles[i].posBase.y = posCenterY + 50.0f;
|
||||
}
|
||||
|
||||
if (((this->particles[i].posBase.z + this->particles[i].posOffset.z) - posCenterZ) >
|
||||
repositionLimit) {
|
||||
this->particles[i].posOffset.z = 0.0f;
|
||||
this->particles[i].posBase.z = posCenterZ - repositionLimit;
|
||||
}
|
||||
|
||||
if (((this->particles[i].posBase.z + this->particles[i].posOffset.z) - posCenterZ) <
|
||||
-repositionLimit) {
|
||||
this->particles[i].posOffset.z = 0.0f;
|
||||
this->particles[i].posBase.z = posCenterZ + repositionLimit;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DEMO_KANKYO_STATE_DISABLED:
|
||||
this->particles[i].state = DEMO_KANKYO_STATE_INIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DemoKakyo_GiantObjectCheck(DemoKankyo* this, GlobalContext* globalCtx) {
|
||||
if (Object_IsLoaded(&globalCtx->objectCtx, this->objectId)) {
|
||||
this->isSafeToDrawGiants = true;
|
||||
this->actor.objBankIndex = this->objectId;
|
||||
DemoKankyo_SetupAction(this, DemoKakyo_MoonSparklesActionFunc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* used by Moon AND giants types
|
||||
*/
|
||||
void DemoKakyo_MoonSparklesActionFunc(DemoKankyo* this, GlobalContext* globalCtx) {
|
||||
s32 i;
|
||||
Vec3f eyeToAt;
|
||||
f32 eyeToAtNormX;
|
||||
f32 eyeToAtNormY;
|
||||
f32 eyeToAtNormZ;
|
||||
f32 eyeToAtMag;
|
||||
f32 halfScreenWidth;
|
||||
f32 randZeroOne;
|
||||
f32 pad0;
|
||||
Vec3f newEye;
|
||||
f32 halfScreenHeight;
|
||||
s32 pad1;
|
||||
Vec3f worldPos;
|
||||
|
||||
if (globalCtx->envCtx.unk_F2[3] < DEMOKANKYO_PARTICLE_COUNT) {
|
||||
globalCtx->envCtx.unk_F2[3] += 16;
|
||||
}
|
||||
|
||||
eyeToAt.x = globalCtx->view.at.x - globalCtx->view.eye.x;
|
||||
eyeToAt.y = globalCtx->view.at.y - globalCtx->view.eye.y;
|
||||
eyeToAt.z = globalCtx->view.at.z - globalCtx->view.eye.z;
|
||||
eyeToAtMag = sqrtf(SQXYZ(eyeToAt));
|
||||
eyeToAtNormX = eyeToAt.x / eyeToAtMag;
|
||||
eyeToAtNormY = eyeToAt.y / eyeToAtMag;
|
||||
eyeToAtNormZ = eyeToAt.z / eyeToAtMag;
|
||||
|
||||
halfScreenHeight = SCREEN_HEIGHT / 2;
|
||||
|
||||
for (i = 0; i < globalCtx->envCtx.unk_F2[3]; i++) {
|
||||
switch (this->particles[i].state) {
|
||||
case DEMO_KANKYO_STATE_INIT:
|
||||
this->particles[i].posBase.x = globalCtx->view.eye.x + (eyeToAtNormX * halfScreenHeight);
|
||||
this->particles[i].posBase.y = globalCtx->view.eye.y + (eyeToAtNormY * halfScreenHeight);
|
||||
this->particles[i].posBase.z = globalCtx->view.eye.z + (eyeToAtNormZ * halfScreenHeight);
|
||||
|
||||
this->particles[i].posOffset.x = (Rand_ZeroOne() - 0.5f) * (2.0f * halfScreenHeight);
|
||||
this->particles[i].posOffset.y = (Rand_ZeroOne() - 0.5f) * (2.0f * halfScreenHeight);
|
||||
this->particles[i].posOffset.z = (Rand_ZeroOne() - 0.5f) * (2.0f * halfScreenHeight);
|
||||
|
||||
this->particles[i].speedTarget = (Rand_ZeroOne() * 1.6f) + 0.5f;
|
||||
this->particles[i].alpha = 0;
|
||||
this->particles[i].alphaClock = (Rand_ZeroOne() * 65535);
|
||||
this->particles[i].scale = 0.2f;
|
||||
|
||||
// speedClock is angles in radians,
|
||||
// should have used Rand_ZeroOne() * 2 * M_PI
|
||||
// however, due to properties of sine waves, this is effectively still random
|
||||
this->particles[i].speedClock.x = Rand_ZeroOne() * 360.0f;
|
||||
this->particles[i].speedClock.y = Rand_ZeroOne() * 360.0f;
|
||||
this->particles[i].speedClock.z = Rand_ZeroOne() * 360.0f;
|
||||
|
||||
this->particles[i].pad50 = 0;
|
||||
this->particles[i].state += DEMO_KANKYO_STATE_SINGLE;
|
||||
break;
|
||||
|
||||
case DEMO_KANKYO_STATE_SINGLE:
|
||||
case DEMO_KANKYO_STATE_SKYFISH:
|
||||
this->particles[i].alphaClock++;
|
||||
|
||||
if (this->actor.params == DEMO_KANKYO_TYPE_MOON) { // this function gets reused for giants too
|
||||
this->particles[i].posBase.y =
|
||||
globalCtx->view.eye.y + (eyeToAtNormY * halfScreenHeight) + (SCREEN_HEIGHT / 3);
|
||||
}
|
||||
|
||||
newEye.x = globalCtx->view.eye.x + (eyeToAtNormX * halfScreenHeight);
|
||||
newEye.y = globalCtx->view.eye.y + (eyeToAtNormY * halfScreenHeight);
|
||||
newEye.z = globalCtx->view.eye.z + (eyeToAtNormZ * halfScreenHeight);
|
||||
|
||||
Math_SmoothStepToF(&this->particles[i].scale, 0.2f, 0.1f, 0.001f, 0.00001f);
|
||||
Math_SmoothStepToF(&this->particles[i].speed, this->particles[i].speedTarget, 0.5f, 0.2f, 0.02f);
|
||||
|
||||
this->particles[i].posOffset.x += __sinf(this->particles[i].speedClock.x) * this->particles[i].speed;
|
||||
this->particles[i].posOffset.y += __sinf(this->particles[i].speedClock.y) * this->particles[i].speed;
|
||||
this->particles[i].posOffset.z += __sinf(this->particles[i].speedClock.z) * this->particles[i].speed;
|
||||
|
||||
switch ((i >> 1) & 3) {
|
||||
case 0:
|
||||
this->particles[i].speedClock.x += 0.008f;
|
||||
this->particles[i].speedClock.y += 0.05f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.z += 0.015f;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
this->particles[i].speedClock.x += 0.01f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.y += 0.05f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.z += 0.005f * Rand_ZeroOne();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
this->particles[i].speedClock.x += 0.01f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.y += 0.4f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.z += 0.004f * Rand_ZeroOne();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
this->particles[i].speedClock.x += 0.01f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.y += 0.08f * Rand_ZeroOne();
|
||||
this->particles[i].speedClock.z += 0.05f * Rand_ZeroOne();
|
||||
break;
|
||||
}
|
||||
|
||||
if (((this->particles[i].posBase.x + this->particles[i].posOffset.x) - newEye.x) > halfScreenHeight) {
|
||||
this->particles[i].posBase.x = newEye.x - halfScreenHeight;
|
||||
}
|
||||
|
||||
if (((this->particles[i].posBase.x + this->particles[i].posOffset.x) - newEye.x) < -halfScreenHeight) {
|
||||
this->particles[i].posBase.x = newEye.x + halfScreenHeight;
|
||||
}
|
||||
|
||||
worldPos.x = this->particles[i].posBase.x + this->particles[i].posOffset.x;
|
||||
worldPos.y = this->particles[i].posBase.y + this->particles[i].posOffset.y;
|
||||
worldPos.z = this->particles[i].posBase.z + this->particles[i].posOffset.z;
|
||||
|
||||
randZeroOne = Math_Vec3f_DistXZ(&worldPos, &globalCtx->view.eye) / 200.0f;
|
||||
randZeroOne = CLAMP(randZeroOne, 0.0f, 1.0f);
|
||||
halfScreenWidth = 100.0f + randZeroOne + 60.0f; // range 160 to 161...? thats about half screen width
|
||||
|
||||
// I think this code is shifting the particles 1 frame -> half screen at a time to keep it in-view
|
||||
if (halfScreenWidth < ((this->particles[i].posBase.y + this->particles[i].posOffset.y) - newEye.y)) {
|
||||
this->particles[i].posBase.y = newEye.y - halfScreenWidth;
|
||||
}
|
||||
|
||||
if (((this->particles[i].posBase.y + this->particles[i].posOffset.y) - newEye.y) < -halfScreenWidth) {
|
||||
this->particles[i].posBase.y = newEye.y + halfScreenWidth;
|
||||
}
|
||||
|
||||
if (((this->particles[i].posBase.z + this->particles[i].posOffset.z) - newEye.z) > halfScreenHeight) {
|
||||
this->particles[i].posBase.z = newEye.z - halfScreenHeight;
|
||||
}
|
||||
|
||||
if (((this->particles[i].posBase.z + this->particles[i].posOffset.z) - newEye.z) < -halfScreenHeight) {
|
||||
this->particles[i].posBase.z = newEye.z + halfScreenHeight;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DEMO_KANKYO_STATE_DISABLED:
|
||||
this->particles[i].state = DEMO_KANKYO_STATE_INIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DemoKankyo_Init(Actor* thisx, GlobalContext* globalCtx) {
|
||||
DemoKankyo* this = THIS;
|
||||
s32 pad;
|
||||
s32 i;
|
||||
s32 objId;
|
||||
|
||||
// This must be a single line to match, possibly a macro?
|
||||
// clang-format off
|
||||
for (i = 0; i < ARRAY_COUNT(this->particles); i++) { this->particles[i].state = DEMO_KANKYO_STATE_INIT; }
|
||||
// clang-format on
|
||||
|
||||
if (1) {};
|
||||
|
||||
switch (this->actor.params) {
|
||||
case DEMO_KANKYO_TYPE_LOSTWOODS:
|
||||
objId = OBJECT_UNSET_0;
|
||||
this->actor.room = -1;
|
||||
if (sLostWoodsSparklesMutex == false) {
|
||||
DemoKankyo_SetupAction(this, DemoKakyo_LostWoodsSparkleActionFunc);
|
||||
sLostWoodsSparklesMutex = true;
|
||||
} else {
|
||||
Actor_MarkForDeath(&this->actor);
|
||||
}
|
||||
break;
|
||||
|
||||
case DEMO_KANKYO_TYPE_GIANTS:
|
||||
this->isSafeToDrawGiants = false;
|
||||
objId = Object_GetIndex(&globalCtx->objectCtx, sObjectBubbleIndex);
|
||||
DemoKankyo_SetupAction(this, DemoKakyo_GiantObjectCheck);
|
||||
break;
|
||||
|
||||
case DEMO_KANKYO_TYPE_MOON:
|
||||
objId = OBJECT_UNSET_0;
|
||||
this->isSafeToDrawGiants = true;
|
||||
DemoKankyo_SetupAction(this, DemoKakyo_MoonSparklesActionFunc);
|
||||
break;
|
||||
|
||||
default:
|
||||
//! @bug: this causes a crash because the actionfunc is never set
|
||||
objId = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (objId > -1) {
|
||||
this->objectId = objId;
|
||||
}
|
||||
}
|
||||
|
||||
void DemoKankyo_Destroy(Actor* thisx, GlobalContext* globalCtx) {
|
||||
DemoKankyo* this = THIS;
|
||||
|
||||
Actor_MarkForDeath(&this->actor);
|
||||
}
|
||||
|
||||
void DemoKankyo_Update(Actor* thisx, GlobalContext* globalCtx) {
|
||||
DemoKankyo* this = THIS;
|
||||
|
||||
this->actionFunc(this, globalCtx);
|
||||
}
|
||||
|
||||
void DemoKakyo_DrawLostWoodsSparkle(Actor* thisx, GlobalContext* globalCtx2) {
|
||||
DemoKankyo* this = THIS;
|
||||
GlobalContext* globalCtx = globalCtx2;
|
||||
s16 i;
|
||||
f32 scaleAlpha;
|
||||
Vec3f worldPos;
|
||||
Vec3f screenPos;
|
||||
|
||||
// if not underwater
|
||||
if (!(globalCtx->cameraPtrs[MAIN_CAM]->flags2 & 0x100)) {
|
||||
OPEN_DISPS(globalCtx->state.gfxCtx);
|
||||
|
||||
POLY_XLU_DISP = Gfx_CallSetupDL(POLY_XLU_DISP, 20);
|
||||
|
||||
gSPSegment(POLY_XLU_DISP++, 0x08, Lib_SegmentedToVirtual(&D_04079B10));
|
||||
gSPDisplayList(POLY_XLU_DISP++, D_0407AB10);
|
||||
|
||||
for (i = 0; i < globalCtx->envCtx.unk_F2[3]; i++) {
|
||||
worldPos.x = this->particles[i].posBase.x + this->particles[i].posOffset.x;
|
||||
worldPos.y = this->particles[i].posBase.y + this->particles[i].posOffset.y;
|
||||
worldPos.z = this->particles[i].posBase.z + this->particles[i].posOffset.z;
|
||||
|
||||
func_80169474(globalCtx, &worldPos, &screenPos); // unnamed Play_ function, func_800C016C from OoT
|
||||
|
||||
// checking if particle is on screen
|
||||
if (screenPos.x >= 0.0f && screenPos.x < SCREEN_WIDTH && screenPos.y >= 0.0f &&
|
||||
screenPos.y < SCREEN_HEIGHT) {
|
||||
Matrix_InsertTranslation(worldPos.x, worldPos.y, worldPos.z, MTXMODE_NEW);
|
||||
scaleAlpha = this->particles[i].alpha / 50.0f;
|
||||
if (scaleAlpha > 1.0f) {
|
||||
scaleAlpha = 1.0f;
|
||||
}
|
||||
|
||||
Matrix_Scale(this->particles[i].scale * scaleAlpha, this->particles[i].scale * scaleAlpha,
|
||||
this->particles[i].scale * scaleAlpha, MTXMODE_APPLY);
|
||||
|
||||
// adjust transparency of this particle
|
||||
if (i < 32) {
|
||||
// Skyfish particles
|
||||
if (this->particles[i].state != DEMO_KANKYO_STATE_SKYFISH) {
|
||||
// still initializing
|
||||
if (this->particles[i].alpha > 0) { // NOT DECR
|
||||
this->particles[i].alpha--;
|
||||
}
|
||||
} else if (this->particles[i].alpha < 100) {
|
||||
this->particles[i].alpha++;
|
||||
}
|
||||
} else if (this->particles[i].state != DEMO_KANKYO_STATE_SKYFISH) {
|
||||
if ((this->particles[i].alphaClock & 31) < 16) {
|
||||
if (this->particles[i].alpha < 235) {
|
||||
this->particles[i].alpha += 20;
|
||||
}
|
||||
} else if (this->particles[i].alpha > 20) {
|
||||
this->particles[i].alpha -= 20;
|
||||
}
|
||||
} else if ((this->particles[i].alphaClock & 15) < 8) {
|
||||
if (this->particles[i].alpha < 255) {
|
||||
this->particles[i].alpha += 100;
|
||||
}
|
||||
} else if (this->particles[i].alpha > 10) {
|
||||
this->particles[i].alpha -= 10;
|
||||
}
|
||||
|
||||
gDPPipeSync(POLY_XLU_DISP++);
|
||||
|
||||
switch (i & 1) {
|
||||
case 0: // gold particles
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 155, this->particles[i].alpha);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 250, 180, 0, this->particles[i].alpha);
|
||||
break;
|
||||
|
||||
case 1: // silver particles
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, this->particles[i].alpha);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 0, 100, 255, this->particles[i].alpha);
|
||||
break;
|
||||
}
|
||||
|
||||
Matrix_InsertMatrix(&globalCtx->mf_187FC, MTXMODE_APPLY);
|
||||
Matrix_InsertZRotation_f(DEGF_TO_RADF(globalCtx->state.frames * 20.0f), MTXMODE_APPLY);
|
||||
|
||||
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
gSPDisplayList(POLY_XLU_DISP++, D_0407AB58);
|
||||
}
|
||||
}
|
||||
|
||||
CLOSE_DISPS(globalCtx->state.gfxCtx);
|
||||
}
|
||||
}
|
||||
|
||||
// draw, giants and moon
|
||||
void DemoKankyo_DrawMoonAndGiant(Actor* thisx, GlobalContext* globalCtx2) {
|
||||
DemoKankyo* this = THIS;
|
||||
GlobalContext* globalCtx = globalCtx2;
|
||||
s16 i;
|
||||
f32 alphaScale;
|
||||
|
||||
if (this->isSafeToDrawGiants != false) {
|
||||
Vec3f worldPos;
|
||||
Vec3f screenPos;
|
||||
s32 pad;
|
||||
GraphicsContext* gfxCtx = globalCtx->state.gfxCtx;
|
||||
|
||||
OPEN_DISPS(gfxCtx);
|
||||
func_8012C2DC(gfxCtx);
|
||||
|
||||
for (i = 0; i < globalCtx->envCtx.unk_F2[3]; i++) {
|
||||
worldPos.x = this->particles[i].posBase.x + this->particles[i].posOffset.x;
|
||||
worldPos.y = this->particles[i].posBase.y + this->particles[i].posOffset.y;
|
||||
worldPos.z = this->particles[i].posBase.z + this->particles[i].posOffset.z;
|
||||
|
||||
func_80169474(globalCtx, &worldPos, &screenPos); // unnamed Play_ function, func_800C016C from OoT
|
||||
|
||||
// checking if particle is on screen
|
||||
if (screenPos.x >= 0.0f && screenPos.x < SCREEN_WIDTH && screenPos.y >= 0.0f &&
|
||||
screenPos.y < SCREEN_HEIGHT) {
|
||||
Matrix_InsertTranslation(worldPos.x, worldPos.y, worldPos.z, MTXMODE_NEW);
|
||||
alphaScale = this->particles[i].alpha / 50.0f;
|
||||
if (alphaScale > 1.0f) {
|
||||
alphaScale = 1.0f;
|
||||
}
|
||||
Matrix_Scale(this->particles[i].scale * alphaScale, this->particles[i].scale * alphaScale,
|
||||
this->particles[i].scale * alphaScale, MTXMODE_APPLY);
|
||||
alphaScale = Math_Vec3f_DistXYZ(&worldPos, &globalCtx->view.eye) / 300.0f;
|
||||
alphaScale = (alphaScale > 1.0f) ? 0.0f : (1.0f - alphaScale) > 1.0f ? 1.0f : 1.0f - alphaScale;
|
||||
|
||||
if (this->actor.params == DEMO_KANKYO_TYPE_GIANTS) {
|
||||
this->particles[i].alpha = 255.0f * alphaScale;
|
||||
} else {
|
||||
this->particles[i].alpha = 160.0f * alphaScale;
|
||||
}
|
||||
|
||||
gDPPipeSync(POLY_XLU_DISP++);
|
||||
|
||||
switch (i & 1) { // half/half slightly different shades of yellow/tan
|
||||
case 0:
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 230, 230, 220, this->particles[i].alpha);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 230, 230, 30, this->particles[i].alpha);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 200, 190, this->particles[i].alpha);
|
||||
gDPSetEnvColor(POLY_XLU_DISP++, 200, 200, 30, this->particles[i].alpha);
|
||||
break;
|
||||
}
|
||||
|
||||
gSPDisplayList(POLY_XLU_DISP++, &D_04023348);
|
||||
|
||||
Matrix_InsertMatrix(&globalCtx->mf_187FC, MTXMODE_APPLY);
|
||||
|
||||
Matrix_InsertZRotation_f(DEGF_TO_RADF(globalCtx->state.frames * 20.0f), MTXMODE_APPLY);
|
||||
|
||||
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
|
||||
if (this->actor.params == DEMO_KANKYO_TYPE_GIANTS) {
|
||||
gSPDisplayList(POLY_XLU_DISP++, D_06001000);
|
||||
} else {
|
||||
gSPDisplayList(POLY_XLU_DISP++, D_04023428);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CLOSE_DISPS(gfxCtx);
|
||||
}
|
||||
}
|
||||
|
||||
void DemoKankyo_Draw(Actor* thisx, GlobalContext* globalCtx) {
|
||||
DemoKankyo* this = THIS;
|
||||
|
||||
switch (this->actor.params) {
|
||||
case DEMO_KANKYO_TYPE_LOSTWOODS:
|
||||
DemoKakyo_DrawLostWoodsSparkle(&this->actor, globalCtx);
|
||||
break;
|
||||
|
||||
case DEMO_KANKYO_TYPE_GIANTS:
|
||||
case DEMO_KANKYO_TYPE_MOON:
|
||||
DemoKankyo_DrawMoonAndGiant(&this->actor, globalCtx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,13 +7,47 @@ struct DemoKankyo;
|
|||
|
||||
typedef void (*DemoKankyoActionFunc)(struct DemoKankyo*, GlobalContext*);
|
||||
|
||||
typedef struct {
|
||||
/* 0x000 */ u8 state;
|
||||
/* 0x004 */ Vec3f posOffset;
|
||||
/* 0x010 */ Vec3f posOffsetPrev;
|
||||
/* 0x01C */ Vec3f posBase;
|
||||
/* 0x028 */ Vec3f speedClock; // cycles in radians
|
||||
/* 0x034 */ f32 speed;
|
||||
/* 0x038 */ f32 speedTarget;
|
||||
/* 0x03C */ u16 alphaClock;
|
||||
/* 0x03E */ u16 LostWoodsSkyFishSpeedXZClock;
|
||||
/* 0x040 */ u8 alpha;
|
||||
/* 0x044 */ f32 scale; // size of the particle
|
||||
/* 0x048 */ u16 LostWoodsSkyFishSpeedXZ; // the x-z speed (angular velocity) the lost woods skyfish oscillates around player. pos or neg 200-400
|
||||
/* 0x04A */ u16 LostWoodsSkyFishPosOffsetMax; // The x-z range the lost woods skyfish oscillates around player. random value between 15-65
|
||||
/* 0x04C */ f32 LostWoodsSkyFishSpeedY; // the y speed (angular velocity) the lost woods skyfish oscillates around player.
|
||||
/* 0x050 */ u16 pad50; // unused, always assigned to 0, nothing else in this actor uses it
|
||||
} DemoKankyoParticle; // size = 0x54
|
||||
|
||||
#define DEMOKANKYO_PARTICLE_COUNT 64
|
||||
|
||||
typedef struct DemoKankyo {
|
||||
/* 0x0000 */ Actor actor;
|
||||
/* 0x0144 */ char unk_144[0x1500];
|
||||
/* 0x0144 */ DemoKankyoParticle particles[DEMOKANKYO_PARTICLE_COUNT];
|
||||
/* 0x1644 */ DemoKankyoActionFunc actionFunc;
|
||||
/* 0x1648 */ char unk_1648[0x8];
|
||||
/* 0x1648 */ s32 objectId;
|
||||
/* 0x164C */ u8 isSafeToDrawGiants;
|
||||
} DemoKankyo; // size = 0x1650
|
||||
|
||||
typedef enum {
|
||||
/* 0 */ DEMO_KANKYO_TYPE_LOSTWOODS,
|
||||
/* 1 */ DEMO_KANKYO_TYPE_GIANTS,
|
||||
/* 2 */ DEMO_KANKYO_TYPE_MOON,
|
||||
} DemoKankyoType;
|
||||
|
||||
typedef enum {
|
||||
/* 0 */ DEMO_KANKYO_STATE_INIT,
|
||||
/* 1 */ DEMO_KANKYO_STATE_SINGLE,
|
||||
/* 2 */ DEMO_KANKYO_STATE_SKYFISH,
|
||||
/* 3 */ DEMO_KANKYO_STATE_DISABLED,
|
||||
} DemoKankyoStateType;
|
||||
|
||||
extern const ActorInit Demo_Kankyo_InitVars;
|
||||
|
||||
#endif // Z_DEMO_KANKYO_H
|
||||
|
|
Loading…
Reference in New Issue