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

210 lines
6.3 KiB
C

/**
* Behavior for bhvCloud and bhvCloudPart.
* bhvCloud includes both fwoosh and the cloud that lakitu rides (both nice and
* evil).
* bhvCloudPart is spawned by bhvCloud and is either a "chunk" of cloud, or fwoosh's
* face. It is purely visual.
* If spawned by a lakitu, its parent will be the lakitu.
* Processing order is lakitu -> cloud -> its cloud parts.
*/
/**
* The relative heights of each cloud part.
*/
static s8 sCloudPartHeights[] = { 11, 8, 12, 8, 9, 9 };
/**
* Spawn the visual parts of the cloud, including fwoosh's face.
*/
static void cloud_act_spawn_parts(void) {
struct Object *cloudPart;
s32 i;
// Spawn the pieces of the cloud itself
for (i = 0; i < 5; i++) {
cloudPart = spawn_object_relative(i, 0, 0, 0, o, MODEL_MIST, bhvCloudPart);
if (cloudPart != NULL) {
obj_set_billboard(cloudPart);
}
}
if (o->oBehParams2ndByte == CLOUD_BP_FWOOSH) {
// Spawn fwoosh's face
spawn_object_relative(5, 0, 0, 0, o, MODEL_FWOOSH, bhvCloudPart);
cur_obj_scale(3.0f);
o->oCloudCenterX = o->oPosX;
o->oCloudCenterY = o->oPosY;
}
o->oAction = CLOUD_ACT_MAIN;
}
/**
* Wait for mario to approach, then unhide and enter the spawn parts action.
*/
static void cloud_act_fwoosh_hidden(void) {
if (o->oDistanceToMario < 2000.0f) {
cur_obj_unhide();
o->oAction = CLOUD_ACT_SPAWN_PARTS;
}
}
/**
* Move in a circle. Unload if mario moves far away. If mario stays close for
* long enough, blow wind at him.
*/
static void cloud_fwoosh_update(void) {
if (o->oDistanceToMario > 2500.0f) {
o->oAction = CLOUD_ACT_UNLOAD;
} else {
if (o->oCloudBlowing) {
o->header.gfx.scale[0] += o->oCloudGrowSpeed;
if ((o->oCloudGrowSpeed -= 0.005f) < -0.16f) {
// Stop blowing once we are shrinking faster than -0.16
o->oCloudBlowing = o->oTimer = 0;
} else if (o->oCloudGrowSpeed < -0.1f) {
// Start blowing once we start shrinking faster than -0.1
cur_obj_play_sound_1(SOUND_AIR_BLOW_WIND);
cur_obj_spawn_strong_wind_particles(12, 3.0f, 0.0f, -50.0f, 120.0f);
} else {
cur_obj_play_sound_1(SOUND_ENV_WIND1);
}
} else {
// Return to normal size
approach_f32_ptr(&o->header.gfx.scale[0], 3.0f, 0.012f);
o->oCloudFwooshMovementRadius += 0xC8;
// If mario stays nearby for 100 frames, begin blowing
if (o->oDistanceToMario < 1000.0f) {
if (o->oTimer > 100) {
o->oCloudBlowing = TRUE;
o->oCloudGrowSpeed = 0.14f;
}
} else {
o->oTimer = 0;
}
o->oCloudCenterX = o->oHomeX + 100.0f * coss(o->oCloudFwooshMovementRadius);
o->oPosZ = o->oHomeZ + 100.0f * sins(o->oCloudFwooshMovementRadius);
o->oCloudCenterY = o->oHomeY;
}
cur_obj_scale(o->header.gfx.scale[0]);
}
}
/**
* Main update function for bhvCloud. This controls the cloud's movement, when it
* unloads, and when fwoosh blows wind.
*/
static void cloud_act_main(void) {
s16 localOffsetPhase;
f32 localOffset;
localOffsetPhase = 0x800 * gGlobalTimer;
if (o->parentObj != o) {
// Despawn if the parent lakitu does
if (o->parentObj->activeFlags == ACTIVE_FLAGS_DEACTIVATED) {
o->oAction = CLOUD_ACT_UNLOAD;
} else {
o->oCloudCenterX = o->parentObj->oPosX;
o->oCloudCenterY = o->parentObj->oPosY;
o->oPosZ = o->parentObj->oPosZ;
o->oMoveAngleYaw = o->parentObj->oFaceAngleYaw;
}
} else if (o->oBehParams2ndByte != CLOUD_BP_FWOOSH) {
// This code should never run, since a lakitu cloud should always have
// a parent
if (o->oDistanceToMario > 1500.0f) {
o->oAction = CLOUD_ACT_UNLOAD;
}
} else {
cloud_fwoosh_update();
}
localOffset = 2 * coss(localOffsetPhase) * o->header.gfx.scale[0];
o->oPosX = o->oCloudCenterX + localOffset;
o->oPosY = o->oCloudCenterY + localOffset + 12.0f * o->header.gfx.scale[0];
}
/**
* If fwoosh, return to home and hide. If lakitu cloud, despawn.
* This action informs the cloud parts to despawn.
*/
static void cloud_act_unload(void) {
if (o->oBehParams2ndByte != CLOUD_BP_FWOOSH) {
obj_mark_for_deletion(o);
} else {
o->oAction = CLOUD_ACT_FWOOSH_HIDDEN;
cur_obj_hide();
cur_obj_set_pos_to_home();
}
}
/**
* Update function for bhvCloud.
*/
void bhv_cloud_update(void) {
switch (o->oAction) {
case CLOUD_ACT_SPAWN_PARTS:
cloud_act_spawn_parts();
break;
case CLOUD_ACT_MAIN:
cloud_act_main();
break;
case CLOUD_ACT_UNLOAD:
cloud_act_unload();
break;
case CLOUD_ACT_FWOOSH_HIDDEN:
cloud_act_fwoosh_hidden();
break;
}
}
/**
* Update function for bhvCloudPart. Follow the parent cloud with some oscillation.
*/
void bhv_cloud_part_update(void) {
if (o->parentObj->oAction == CLOUD_ACT_UNLOAD) {
obj_mark_for_deletion(o);
} else {
f32 size = 2.0f / 3.0f * o->parentObj->header.gfx.scale[0];
s16 angleFromCenter = o->parentObj->oFaceAngleYaw + 0x10000 / 5 * o->oBehParams2ndByte;
// Takes 32 frames to cycle
s16 localOffsetPhase = 0x800 * gGlobalTimer + 0x4000 * o->oBehParams2ndByte;
f32 localOffset;
f32 cloudRadius;
cur_obj_scale(size);
// Cap fwoosh's face size
if (o->oBehParams2ndByte == 5 && size > 2.0f) {
size = o->header.gfx.scale[1] = 2.0f;
}
// Move back and forth along (1, 1, 1)
localOffset = 2 * coss(localOffsetPhase) * size;
cloudRadius = 25.0f * size;
o->oPosX = o->parentObj->oCloudCenterX + cloudRadius * sins(angleFromCenter) + localOffset;
o->oPosY =
o->parentObj->oCloudCenterY + localOffset + size * sCloudPartHeights[o->oBehParams2ndByte];
o->oPosZ = o->parentObj->oPosZ + cloudRadius * coss(angleFromCenter) + localOffset;
o->oFaceAngleYaw = o->parentObj->oFaceAngleYaw;
}
}