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:
Isghj 2021-11-03 08:10:54 -07:00 committed by GitHub
parent fcbd524b5d
commit 60b9cd789e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 692 additions and 21 deletions

View File

@ -4034,7 +4034,7 @@ extern Gfx D_04075B30[];
extern Gfx D_04076BC0[]; extern Gfx D_04076BC0[];
extern Gfx D_04077480[]; extern Gfx D_04077480[];
extern UNK_TYPE D_04079B10; extern UNK_TYPE D_04079B10;
extern Gfx D_0407AB10[]; extern Gfx D_0407AB10[]; // sun (sparkles when small) displaylist
extern Gfx D_0407AB58[]; extern Gfx D_0407AB58[];
extern UNK_TYPE D_0407AFB0; extern UNK_TYPE D_0407AFB0;
extern Gfx D_0407D590[]; extern Gfx D_0407D590[];

View File

@ -942,7 +942,7 @@ typedef struct {
/* 0xEC */ u8 unk_EC; /* 0xEC */ u8 unk_EC;
/* 0xED */ u8 unk_ED; /* 0xED */ u8 unk_ED;
/* 0xEE */ u8 unk_EE[4]; /* 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]; /* 0xFA */ u8 unk_FA[4];
} EnvironmentContext; // size = 0x100 } EnvironmentContext; // size = 0x100

3
spec
View File

@ -1156,8 +1156,7 @@ beginseg
name "ovl_Demo_Kankyo" name "ovl_Demo_Kankyo"
compress compress
include "build/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.o" 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/src/overlays/actors/ovl_Demo_Kankyo/ovl_Demo_Kankyo_reloc.o"
include "build/data/ovl_Demo_Kankyo/ovl_Demo_Kankyo.reloc.o"
endseg endseg
beginseg beginseg

View File

@ -15,12 +15,15 @@ void DemoKankyo_Destroy(Actor* thisx, GlobalContext* globalCtx);
void DemoKankyo_Update(Actor* thisx, GlobalContext* globalCtx); void DemoKankyo_Update(Actor* thisx, GlobalContext* globalCtx);
void DemoKankyo_Draw(Actor* thisx, GlobalContext* globalCtx); void DemoKankyo_Draw(Actor* thisx, GlobalContext* globalCtx);
void func_808CF06C(DemoKankyo* this, GlobalContext* globalCtx); void DemoKakyo_MoonSparklesActionFunc(DemoKankyo* this, GlobalContext* globalCtx);
void func_808CF0CC(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 = { const ActorInit Demo_Kankyo_InitVars = {
ACTOR_DEMO_KANKYO, ACTOR_DEMO_KANKYO,
ACTORCAT_ITEMACTION, ACTORCAT_ITEMACTION,
@ -33,24 +36,659 @@ const ActorInit Demo_Kankyo_InitVars = {
(ActorFunc)DemoKankyo_Draw, (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;
}
}

View File

@ -7,13 +7,47 @@ struct DemoKankyo;
typedef void (*DemoKankyoActionFunc)(struct DemoKankyo*, GlobalContext*); 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 { typedef struct DemoKankyo {
/* 0x0000 */ Actor actor; /* 0x0000 */ Actor actor;
/* 0x0144 */ char unk_144[0x1500]; /* 0x0144 */ DemoKankyoParticle particles[DEMOKANKYO_PARTICLE_COUNT];
/* 0x1644 */ DemoKankyoActionFunc actionFunc; /* 0x1644 */ DemoKankyoActionFunc actionFunc;
/* 0x1648 */ char unk_1648[0x8]; /* 0x1648 */ s32 objectId;
/* 0x164C */ u8 isSafeToDrawGiants;
} DemoKankyo; // size = 0x1650 } 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; extern const ActorInit Demo_Kankyo_InitVars;
#endif // Z_DEMO_KANKYO_H #endif // Z_DEMO_KANKYO_H