/** * 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; } }