sm64pc/src/game/behaviors/water_ring.inc.c

235 lines
8.0 KiB
C

// water_ring.c.inc
f32 water_ring_calc_mario_dist(void) {
f32 marioDistX = o->oPosX - gMarioObject->header.gfx.pos[0];
f32 marioDistY = o->oPosY - (gMarioObject->header.gfx.pos[1] + 80.0f);
f32 marioDistZ = o->oPosZ - gMarioObject->header.gfx.pos[2];
f32 marioDistInFront = marioDistX * o->oWaterRingNormalX + marioDistY * o->oWaterRingNormalY
+ marioDistZ * o->oWaterRingNormalZ;
return marioDistInFront;
}
void water_ring_init(void) {
cur_obj_init_animation(0);
o->oWaterRingScalePhaseX = (s32)(random_float() * 4096.0f) + 0x1000;
o->oWaterRingScalePhaseY = (s32)(random_float() * 4096.0f) + 0x1000;
o->oWaterRingScalePhaseZ = (s32)(random_float() * 4096.0f) + 0x1000;
//! This normal calculation assumes a facing yaw of 0, which is not the case
// for the manta ray rings. It also errs by multiplying the normal X by -1.
// This cause the ring's orientation for the purposes of collision to be
// different than the graphical orientation, which means that Mario won't
// necessarily collect a ring even if he appears to swim through it.
o->oWaterRingNormalX = coss(o->oFaceAnglePitch) * sins(o->oFaceAngleRoll) * -1.0f;
o->oWaterRingNormalY = coss(o->oFaceAnglePitch) * coss(o->oFaceAngleRoll);
o->oWaterRingNormalZ = sins(o->oFaceAnglePitch);
o->oWaterRingMarioDistInFront = water_ring_calc_mario_dist();
// Adding this code will alter the ring's graphical orientation to align with the faulty
// collision orientation:
//
// o->oFaceAngleYaw = 0;
// o->oFaceAngleRoll *= -1;
}
void bhv_jet_stream_water_ring_init(void) {
water_ring_init();
o->oOpacity = 70;
cur_obj_init_animation(0);
o->oFaceAnglePitch = 0x8000;
}
// sp28 = arg0
// sp2c = ringManager
void water_ring_check_collection(f32 avgScale, struct Object *ringManager) {
f32 marioDistInFront = water_ring_calc_mario_dist();
struct Object *ringSpawner;
if (!is_point_close_to_object(o, gMarioObject->header.gfx.pos[0],
gMarioObject->header.gfx.pos[1] + 80.0f, gMarioObject->header.gfx.pos[2],
(avgScale + 0.2) * 120.0)) {
o->oWaterRingMarioDistInFront = marioDistInFront;
return;
}
if (o->oWaterRingMarioDistInFront * marioDistInFront < 0) {
ringSpawner = o->parentObj;
if (ringSpawner) {
if ((o->oWaterRingIndex == ringManager->oWaterRingMgrLastRingCollected + 1)
|| (ringSpawner->oWaterRingSpawnerRingsCollected == 0)) {
ringSpawner->oWaterRingSpawnerRingsCollected++;
if (ringSpawner->oWaterRingSpawnerRingsCollected < 6) {
spawn_orange_number(ringSpawner->oWaterRingSpawnerRingsCollected, 0, -40, 0);
#ifdef VERSION_JP
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
#else
play_sound(SOUND_MENU_COLLECT_SECRET
+ (((u8) ringSpawner->oWaterRingSpawnerRingsCollected - 1) << 16),
gDefaultSoundArgs);
#endif
}
ringManager->oWaterRingMgrLastRingCollected = o->oWaterRingIndex;
} else
ringSpawner->oWaterRingSpawnerRingsCollected = 0;
}
o->oAction = WATER_RING_ACT_COLLECTED;
}
o->oWaterRingMarioDistInFront = marioDistInFront;
}
void water_ring_set_scale(f32 avgScale) {
o->header.gfx.scale[0] = sins(o->oWaterRingScalePhaseX) * 0.1 + avgScale;
o->header.gfx.scale[1] = sins(o->oWaterRingScalePhaseY) * 0.5 + avgScale;
o->header.gfx.scale[2] = sins(o->oWaterRingScalePhaseZ) * 0.1 + avgScale;
o->oWaterRingScalePhaseX += 0x1700;
o->oWaterRingScalePhaseY += 0x1700;
o->oWaterRingScalePhaseZ += 0x1700;
}
void water_ring_act_collected(void) {
f32 avgScale = (f32) o->oTimer * 0.2 + o->oWaterRingAvgScale;
if (o->oTimer >= 21)
o->activeFlags = 0;
o->oOpacity -= 10;
if (o->oOpacity < 0)
o->oOpacity = 0;
water_ring_set_scale(avgScale);
}
void water_ring_act_not_collected(void) {
f32 avgScale = (f32) o->oTimer / 225.0 * 3.0 + 0.5;
//! In this case ringSpawner and ringManager are the same object,
// because the Jet Stream Ring Spawner is its own parent object.
struct Object *ringSpawner = o->parentObj;
struct Object *ringManager = ringSpawner->parentObj;
if (o->oTimer >= 226) {
o->oOpacity -= 2;
if (o->oOpacity < 3)
o->activeFlags = 0;
}
water_ring_check_collection(avgScale, ringManager);
water_ring_set_scale(avgScale);
o->oPosY += 10.0f;
o->oFaceAngleYaw += 0x100;
set_object_visibility(o, 5000);
if (ringSpawner->oWaterRingSpawnerRingsCollected == 4
&& o->oWaterRingIndex == ringManager->oWaterRingMgrLastRingCollected + 1)
o->oOpacity = sins(o->oTimer * 0x1000) * 200.0f + 50.0f;
o->oWaterRingAvgScale = avgScale;
}
void bhv_jet_stream_water_ring_loop(void) {
switch (o->oAction) {
case WATER_RING_ACT_NOT_COLLECTED:
water_ring_act_not_collected();
break;
case WATER_RING_ACT_COLLECTED:
water_ring_act_collected();
break;
}
}
void spawn_manta_ray_ring_manager(void) {
struct Object *ringManager = spawn_object(o, MODEL_NONE, bhvMantaRayRingManager);
o->parentObj = ringManager;
}
void water_ring_spawner_act_inactive(void) {
//! The Jet Stream Ring Spawner is its own parent object. The code may have been copied
// from the Manta Ray, which spawns rings but also has a Ring Manager object as its
// parent. The Jet Stream Ring Spawner functions as both a spawner and a Ring Manager.
struct Object *currentObj = o->parentObj;
struct Object *waterRing;
//! Because the index counter overflows at 10000, it's possible to wait
// for about 4 hours and 38 minutes if you miss a ring, and the index will
// come around again.
if (o->oTimer == 300)
o->oTimer = 0;
if ((o->oTimer == 0) || (o->oTimer == 50) || (o->oTimer == 150) || (o->oTimer == 200)
|| (o->oTimer == 250)) {
waterRing = spawn_object(o, MODEL_WATER_RING, bhvJetStreamWaterRing);
waterRing->oWaterRingIndex = currentObj->oWaterRingMgrNextRingIndex;
currentObj->oWaterRingMgrNextRingIndex++;
if (currentObj->oWaterRingMgrNextRingIndex >= 10001)
currentObj->oWaterRingMgrNextRingIndex = 0;
}
}
void bhv_jet_stream_ring_spawner_loop(void) {
switch (o->oAction) {
case JS_RING_SPAWNER_ACT_ACTIVE:
water_ring_spawner_act_inactive();
if (o->oWaterRingSpawnerRingsCollected == 5) {
spawn_mist_particles();
spawn_default_star(3400.0f, -3200.0f, -500.0f);
o->oAction = JS_RING_SPAWNER_ACT_INACTIVE;
}
break;
case JS_RING_SPAWNER_ACT_INACTIVE:
break;
}
}
void bhv_manta_ray_water_ring_init(void) {
water_ring_init();
o->oOpacity = 150;
}
void manta_water_ring_act_not_collected(void) {
f32 avgScale = (f32) o->oTimer / 50.0f * 1.3 + 0.1;
struct Object *ringSpawner = o->parentObj;
struct Object *ringManager = ringSpawner->parentObj;
if (avgScale > 1.3)
avgScale = 1.3;
if (o->oTimer >= 151) {
o->oOpacity -= 2;
if (o->oOpacity < 3)
o->activeFlags = 0;
}
water_ring_check_collection(avgScale, ringManager);
water_ring_set_scale(avgScale);
set_object_visibility(o, 5000);
if (ringSpawner->oWaterRingSpawnerRingsCollected == 4
&& o->oWaterRingIndex == ringManager->oWaterRingMgrLastRingCollected + 1)
o->oOpacity = sins(o->oTimer * 0x1000) * 200.0f + 50.0f;
o->oWaterRingAvgScale = avgScale;
}
void bhv_manta_ray_water_ring_loop(void) {
switch (o->oAction) {
case WATER_RING_ACT_NOT_COLLECTED:
manta_water_ring_act_not_collected();
break;
case WATER_RING_ACT_COLLECTED:
water_ring_act_collected();
break;
}
}