2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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);
|
|
|
|
|
2020-03-02 03:42:52 +00:00
|
|
|
cur_obj_scale(3.0f);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
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) {
|
2020-03-02 03:42:52 +00:00
|
|
|
cur_obj_unhide();
|
2019-08-25 04:46:40 +00:00
|
|
|
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
|
2020-03-02 03:42:52 +00:00
|
|
|
cur_obj_play_sound_1(SOUND_AIR_BLOW_WIND);
|
|
|
|
cur_obj_spawn_strong_wind_particles(12, 3.0f, 0.0f, -50.0f, 120.0f);
|
2019-08-25 04:46:40 +00:00
|
|
|
} else {
|
2020-03-02 03:42:52 +00:00
|
|
|
cur_obj_play_sound_1(SOUND_ENV_WIND1);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2020-03-02 03:42:52 +00:00
|
|
|
cur_obj_scale(o->header.gfx.scale[0]);
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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();
|
|
|
|
}
|
|
|
|
|
2020-02-03 05:51:26 +00:00
|
|
|
localOffset = 2 * coss(localOffsetPhase) * o->header.gfx.scale[0];
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
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) {
|
2020-03-02 03:42:52 +00:00
|
|
|
obj_mark_for_deletion(o);
|
2019-08-25 04:46:40 +00:00
|
|
|
} else {
|
|
|
|
o->oAction = CLOUD_ACT_FWOOSH_HIDDEN;
|
2020-03-02 03:42:52 +00:00
|
|
|
cur_obj_hide();
|
|
|
|
cur_obj_set_pos_to_home();
|
2019-08-25 04:46:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
2020-03-02 03:42:52 +00:00
|
|
|
obj_mark_for_deletion(o);
|
2019-08-25 04:46:40 +00:00
|
|
|
} 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;
|
|
|
|
|
2020-03-02 03:42:52 +00:00
|
|
|
cur_obj_scale(size);
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
// 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)
|
2020-02-03 05:51:26 +00:00
|
|
|
localOffset = 2 * coss(localOffsetPhase) * size;
|
2019-08-25 04:46:40 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|