// bowser.c.inc

void bowser_tail_anchor_act_0(void) {
    struct Object *bowser = o->parentObj;
    cur_obj_become_tangible();
    cur_obj_scale(1.0f);
    if (bowser->oAction == 19)
        bowser->oIntangibleTimer = -1;
    else if (obj_check_if_collided_with_object(o, gMarioObject)) {
        bowser->oIntangibleTimer = 0;
        o->oAction = 2;
    } else
        bowser->oIntangibleTimer = -1;
}

void bowser_tail_anchor_act_1(void) {
    if (o->oTimer > 30)
        o->oAction = 0;
}

void bowser_tail_anchor_act_2(void) {
    if (o->parentObj->oAction == 19) {
        o->parentObj->oIntangibleTimer = -1;
        o->oAction = 0;
    }
    cur_obj_become_intangible();
}

void (*sBowserTailAnchorActions[])(void) = { bowser_tail_anchor_act_0, bowser_tail_anchor_act_1,
                                             bowser_tail_anchor_act_2 };
s8 D_8032F4FC[] = { 7, 8, 9, 12, 13, 14, 15, 4, 3, 16, 17, 19, 3, 3, 3, 3 };
s16 D_8032F50C[] = { 60, 0 };
s16 D_8032F510[] = { 50, 0 };
s8 D_8032F514[] = { 24, 42, 60, -1 };
s16 sBowserDefeatedDialogText[3] = { DIALOG_119, DIALOG_120, DIALOG_121 };
s16 D_8032F520[][3] = { { 1, 10, 40 },   { 0, 0, 74 },    { -1, -10, 114 },  { 1, -20, 134 },
                        { -1, 20, 154 }, { 1, 40, 164 },  { -1, -40, 174 },  { 1, -80, 179 },
                        { -1, 80, 184 }, { 1, 160, 186 }, { -1, -160, 186 }, { 1, 0, 0 }, };

void bhv_bowser_tail_anchor_loop(void) {
    cur_obj_call_action_function(sBowserTailAnchorActions);
    o->oParentRelativePosX = 90.0f;
    if (o->parentObj->oAction == 4)
        o->parentObj->oIntangibleTimer = -1;
    o->oInteractStatus = 0;
}

void bhv_bowser_flame_spawn_loop(void) {
    struct Object *bowser = o->parentObj;
    s32 sp30;
    f32 sp2C;
    f32 sp28;
    f32 sp24 = coss(bowser->oMoveAngleYaw);
    f32 sp20 = sins(bowser->oMoveAngleYaw);
    s16 *sp1C = segmented_to_virtual(bowser_seg6_unkmoveshorts_060576FC);
    if (bowser->oSoundStateID == 6) {
        sp30 = bowser->header.gfx.animInfo.animFrame + 1.0f;
        if (bowser->header.gfx.animInfo.curAnim->loopEnd == sp30)
            sp30 = 0;
        if (sp30 > 45 && sp30 < 85) {
            cur_obj_play_sound_1(SOUND_AIR_BOWSER_SPIT_FIRE);
            sp2C = sp1C[5 * sp30];
            sp28 = sp1C[5 * sp30 + 2];
            o->oPosX = bowser->oPosX + (sp28 * sp20 + sp2C * sp24);
            o->oPosY = bowser->oPosY + sp1C[5 * sp30 + 1];
            o->oPosZ = bowser->oPosZ + (sp28 * sp24 - sp2C * sp20);
            o->oMoveAnglePitch = sp1C[5 * sp30 + 4] + 0xC00;
            o->oMoveAngleYaw = sp1C[5 * sp30 + 3] + (s16) bowser->oMoveAngleYaw;
            if (!(sp30 & 1))
                spawn_object(o, MODEL_RED_FLAME, bhvFlameMovingForwardGrowing);
        }
    }
}

void bhv_bowser_body_anchor_loop(void) {
    obj_copy_pos_and_angle(o, o->parentObj);
    if (o->parentObj->oAction == 4) {
#ifndef VERSION_JP
        if (o->parentObj->oSubAction == 11)
            o->oInteractType = 0;
        else
            o->oInteractType = 0x800000;
#else
        o->oInteractType = 0x800000;
#endif
    } else {
        o->oInteractType = 8;
        if (o->parentObj->oOpacity < 100)
            cur_obj_become_intangible();
        else
            cur_obj_become_tangible();
    }
    if (o->parentObj->oHeldState != HELD_FREE)
        cur_obj_become_intangible();
    o->oInteractStatus = 0;
}

s32 bowser_spawn_shockwave(void) {
    struct Object *wave;
    if (o->oBehParams2ndByte == 2) {
        wave = spawn_object(o, MODEL_BOWSER_WAVE, bhvBowserShockWave);
        wave->oPosY = o->oFloorHeight;
        return 1;
    }
    return 0;
}

void bowser_bounce(s32 *a) {
    if (o->oMoveFlags & OBJ_MOVE_LANDED) {
        a[0]++;
        if (a[0] < 4) {
            cur_obj_start_cam_event(o, CAM_EVENT_BOWSER_THROW_BOUNCE);
            spawn_mist_particles_variable(0, 0, 60.0f);
            cur_obj_play_sound_2(SOUND_OBJ_BOWSER_WALK);
        }
    }
}

#define BITDW (o->oBehParams2ndByte == 0)
#define BITFS (o->oBehParams2ndByte == 1)
#define BITS (o->oBehParams2ndByte == 2)

s32 bowser_set_anim_look_up_and_walk(void) {
    cur_obj_init_animation_with_sound(15);
    if (cur_obj_check_anim_frame(21))
        o->oForwardVel = 3.0f;
    if (cur_obj_check_if_near_animation_end())
        return 1;
    else
        return 0;
}

s32 bowser_set_anim_slow_gait(void) {
    o->oForwardVel = 3.0f;
    cur_obj_init_animation_with_sound(13);
    if (cur_obj_check_if_near_animation_end())
        return 1;
    else
        return 0;
}

s32 bowser_set_anim_look_down(void) {
    cur_obj_init_animation_with_sound(14);
    if (cur_obj_check_anim_frame(20))
        o->oForwardVel = 0.0f;
    if (cur_obj_check_if_near_animation_end())
        return 1;
    else
        return 0;
}

void bowser_initialize_action(void) {
    if (o->oBowserUnk88 == 0)
        o->oAction = 5;
    else if (o->oBowserUnk88 == 1)
        o->oAction = 6;
    else if (o->oBehParams2ndByte == 1)
        o->oAction = 13;
    else
        o->oAction = 0;
}

void bowser_act_text_wait(void) // not much
{
    o->oForwardVel = 0.0f;
    cur_obj_init_animation_with_sound(12);
    bowser_initialize_action();
}

void bowser_act_intro_walk(void) {
    if (o->oSubAction == 0) {
        if (bowser_set_anim_look_up_and_walk())
            o->oSubAction++;
    } else if (o->oSubAction == 1) {
        if (bowser_set_anim_slow_gait())
            o->oSubAction++;
    } else if (bowser_set_anim_look_down()) {
        if (o->oBowserUnk88 == 1)
            o->oBowserUnk88 = 0;
        bowser_initialize_action();
    }
}

static void bowser_debug_actions(void) // unused
{
    if (gDebugInfo[5][1] != 0) {
        o->oAction = D_8032F4FC[gDebugInfo[5][2] & 0xf];
        gDebugInfo[5][1] = 0;
    }
}

void bowser_bitdw_act_controller(void) {
    f32 rand = random_float();
    if (o->oBowserUnk110 == 0) {
        if (o->oBowserUnkF4 & 2) {
            if (o->oDistanceToMario < 1500.0f)
                o->oAction = 15; // nearby
            else
                o->oAction = 17; // far away
        } else
            o->oAction = 14;
        o->oBowserUnk110++;
    } else {
        o->oBowserUnk110 = 0;
#ifndef VERSION_JP
        if (!gCurrDemoInput) {
            if (rand < 0.1)
                o->oAction = 3; // rare 1/10 chance
            else
                o->oAction = 14; // common
        } else {
            o->oAction = 14; // ensure demo starts with action 14.
        }
#else
        if (rand < 0.1)
            o->oAction = 3; // rare 1/10 chance
        else
            o->oAction = 14; // common
#endif
    }
}

void bowser_bitfs_act_controller(void) {
    f32 rand = random_float();
    if (o->oBowserUnk110 == 0) {
        if (o->oBowserUnkF4 & 2) {
            if (o->oDistanceToMario < 1300.0f) // nearby
            {
                if (rand < 0.5) // 50/50
                    o->oAction = 16;
                else
                    o->oAction = 9;
            } else // far away
            {
                o->oAction = 7;
                if (500.0f < o->oBowserDistToCentre && o->oBowserDistToCentre < 1500.0f
                    && rand < 0.5) // away from centre and good luck
                    o->oAction = 13;
            }
        } else
            o->oAction = 14;
        o->oBowserUnk110++;
    } else {
        o->oBowserUnk110 = 0;
        o->oAction = 14;
    }
}

void bowser_general_bits_act_controller(void) {
    f32 rand = random_float();
    if (o->oBowserUnkF4 & 2) {
        if (o->oDistanceToMario < 1000.0f) {
            if (rand < 0.4)
                o->oAction = 9;
            else if (rand < 0.8)
                o->oAction = 8;
            else
                o->oAction = 15;
        } else if (rand < 0.5)
            o->oAction = 13;
        else
            o->oAction = 7;
    } else
        o->oAction = 14;
}

void bowser_set_act_jump(void) {
    o->oAction = 13;
}

void bowser_bits_act_controller(void) {
    switch (o->oBowserUnk110) {
        case 0:
            if (o->oBowserUnk106 == 0)
                bowser_general_bits_act_controller();
            else
                bowser_set_act_jump();
            o->oBowserUnk110 = 1;
            break;
        case 1:
            o->oBowserUnk110 = 0;
            o->oAction = 14;
            break;
    }
}

#ifndef VERSION_JP
void bowser_reset_fallen_off_stage(void) {
    if (o->oVelY < 0 && o->oPosY < (o->oHomeY - 300.0f)) {
        o->oPosX = o->oPosZ = 0;
        o->oPosY = o->oHomeY + 2000.0f;
        o->oVelY = 0;
        o->oForwardVel = 0;
    }
}
#endif

void bowser_act_unused_slow_walk(void) // unused?
{
    if (cur_obj_init_animation_and_check_if_near_end(12))
        o->oAction = 0;
}

void bowser_act_default(void) // only lasts one frame
{
    o->oBowserEyesShut = 0;
    cur_obj_init_animation_with_sound(12);
    // stop him still
    o->oAngleVelYaw = 0;
    o->oForwardVel = 0.0f;
    o->oVelY = 0.0f;
    if (BITDW)
        bowser_bitdw_act_controller();
    else if (BITFS)
        bowser_bitfs_act_controller();
    else
        bowser_bits_act_controller();
    // Action 14 commonly follows
}

void bowser_act_breath_fire(void) {
    o->oForwardVel = 0.0f;
    if (o->oTimer == 0)
        cur_obj_play_sound_2(SOUND_OBJ_BOWSER_INHALING);
    if (cur_obj_init_animation_and_check_if_near_end(6))
        o->oAction = 0;
}

void bowser_act_walk_to_mario(void) // turn towards Mario
{
    UNUSED s32 facing; // is Bowser facing Mario?
    s16 turnSpeed;
    s16 angleFromMario = abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario);
    if (BITFS)
        turnSpeed = 0x400;
    else if (o->oHealth > 2)
        turnSpeed = 0x400;
    else if (o->oHealth == 2)
        turnSpeed = 0x300;
    else
        turnSpeed = 0x200;
    facing = cur_obj_rotate_yaw_toward(o->oAngleToMario, turnSpeed);
    if (o->oSubAction == 0) {
        o->oBowserUnkF8 = 0;
        if (bowser_set_anim_look_up_and_walk())
            o->oSubAction++;
    } else if (o->oSubAction == 1) {
        if (bowser_set_anim_slow_gait()) {
            o->oBowserUnkF8++;
            if (o->oBowserUnkF4 & 0x20000) {
                if (o->oBowserUnkF8 > 4)
                    o->oBowserUnkF4 &= ~0x20000;
            } else if (angleFromMario < 0x2000)
                o->oSubAction++;
        }
    } else if (bowser_set_anim_look_down())
        o->oAction = 0;
}

void bowser_act_teleport(void) {
    switch (o->oSubAction) {
        case 0:
            cur_obj_become_intangible();
            o->oBowserUnk1AC = 0;
            o->oBowserUnkF8 = 30;
            if (o->oTimer == 0)
                cur_obj_play_sound_2(SOUND_OBJ2_BOWSER_TELEPORT);
            if (o->oOpacity == 0) {
                o->oSubAction++;
                o->oMoveAngleYaw = o->oAngleToMario;
            }
            break;
        case 1:
            if (o->oBowserUnkF8--)
                o->oForwardVel = 100.0f;
            else {
                o->oSubAction = 2;
                o->oMoveAngleYaw = o->oAngleToMario;
            }
            if (abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario) > 0x4000)
                if (o->oDistanceToMario > 500.0f) {
                    o->oSubAction = 2;
                    o->oMoveAngleYaw = o->oAngleToMario; // large change in angle?
                    cur_obj_play_sound_2(SOUND_OBJ2_BOWSER_TELEPORT);
                }
            break;
        case 2:
            o->oForwardVel = 0.0f;
            o->oBowserUnk1AC = 0xFF;
            if (o->oOpacity == 0xFF)
                o->oAction = 0;
            cur_obj_become_tangible();
            break;
    }
}

void bowser_act_spit_fire_into_sky(void) // only in sky
{
    s32 frame;
    cur_obj_init_animation_with_sound(11);
    frame = o->header.gfx.animInfo.animFrame;
    if (frame > 24 && frame < 36) {
        cur_obj_play_sound_1(SOUND_AIR_BOWSER_SPIT_FIRE);
        if (frame == 35)
            spawn_object_relative(1, 0, 0x190, 0x64, o, MODEL_RED_FLAME, bhvBlueBowserFlame);
        else
            spawn_object_relative(0, 0, 0x190, 0x64, o, MODEL_RED_FLAME, bhvBlueBowserFlame);
    }
    if (cur_obj_check_if_near_animation_end())
        o->oAction = 0;
    o->oBowserUnkF4 |= 0x20000;
}

void bowser_act_hit_mine(void) {
    if (o->oTimer == 0) {
        o->oForwardVel = -400.0f;
        o->oVelY = 100.0f;
        o->oMoveAngleYaw = o->oBowserAngleToCentre + 0x8000;
        o->oBowserEyesShut = 1;
    }
    if (o->oSubAction == 0) {
        cur_obj_init_animation_with_sound(25);
        o->oSubAction++;
        o->oBowserUnkF8 = 0;
    } else if (o->oSubAction == 1) {
        cur_obj_init_animation_with_sound(25);
        cur_obj_extend_animation_if_at_end();
        bowser_bounce(&o->oBowserUnkF8);
        if ((o->oBowserUnkF8 > 2)) {
            cur_obj_init_animation_with_sound(26);
            o->oVelY = 0.0f;
            o->oForwardVel = 0.0f;
            o->oSubAction++;
        }
    } else if (o->oSubAction == 2) {
        if (cur_obj_check_if_near_animation_end()) {
            if (o->oHealth == 1)
                o->oAction = 3;
            else
                o->oAction = 0;
            o->oBowserEyesShut = 0;
        }
    } else {
    }
}

s32 bowser_set_anim_in_air(void) {
    cur_obj_init_animation_with_sound(9);
    if (cur_obj_check_anim_frame(11))
        return 1;
    else
        return 0;
}

s32 bowser_land(void) {
    if (o->oMoveFlags & OBJ_MOVE_LANDED) {
        o->oForwardVel = 0;
        o->oVelY = 0;
        spawn_mist_particles_variable(0, 0, 60.0f);
        cur_obj_init_animation_with_sound(8);
        o->header.gfx.animInfo.animFrame = 0;
        cur_obj_start_cam_event(o, CAM_EVENT_BOWSER_JUMP);
        if (BITDW) {
            if (o->oDistanceToMario < 850.0f)
                gMarioObject->oInteractStatus |= INT_STATUS_MARIO_UNK1;
            else
                gMarioObject->oInteractStatus |= INT_STATUS_HOOT_GRABBED_BY_MARIO; // hmm...
        }
        return 1;
    } else
        return 0;
}

void bowser_short_second_hop(void) {
    if (BITS && o->oBowserUnkF4 & 0x10000)
        if (o->oBowserDistToCentre > 1000.0f)
            o->oForwardVel = 60.0f;
}

void bowser_act_jump(void) {
    UNUSED s32 unused;
    if (o->oSubAction == 0) {
        if (bowser_set_anim_in_air()) {
            if (BITS && o->oBowserUnkF4 & 0x10000)
                o->oVelY = 70.0f;
            else
                o->oVelY = 80.0f;
            o->oBowserUnkF8 = 0;
            bowser_short_second_hop();
            o->oSubAction++;
        }
    } else if (o->oSubAction == 1) {
#ifndef VERSION_JP
        if (o->oBehParams2ndByte == 2 && o->oBowserUnkF4 & 0x10000)
            bowser_reset_fallen_off_stage();
#endif
        if (bowser_land()) {
            o->oBowserUnkF4 &= ~0x10000;
            o->oForwardVel = 0.0f;
            o->oSubAction++;
            bowser_spawn_shockwave();
            if (BITFS)
                o->oAction = 19;
        } else {
        }
    } else if (cur_obj_check_if_near_animation_end())
        o->oAction = 0;
}

void bowser_act_jump_towards_mario(void) {
    f32 sp1C = D_8032F50C[0];
    f32 sp18 = D_8032F510[0];
    if (o->oSubAction == 0) {
        if (bowser_set_anim_in_air()) {
            o->oVelY = sp1C;
            o->oForwardVel = sp18;
            o->oBowserUnkF8 = 0;
            o->oSubAction++;
        }
    } else if (o->oSubAction == 1) {
        if (bowser_land())
            o->oSubAction++;
    } else if (cur_obj_check_if_near_animation_end())
        o->oAction = 0;
}

void bowser_act_hit_edge(void) {
    o->oForwardVel = 0.0f;
    if (o->oTimer == 0)
        o->oBowserUnkF8 = 0;
    switch (o->oSubAction) {
        case 0:
            cur_obj_init_animation_with_sound(23);
            if (cur_obj_check_if_near_animation_end())
                o->oBowserUnkF8++;
            if (o->oBowserUnkF8 > 0)
                o->oSubAction++;
            break;
        case 1:
            cur_obj_init_animation_with_sound(24);
            if (cur_obj_check_if_near_animation_end())
                o->oAction = 11;
            break;
    }
}

void bowser_act_spit_fire_onto_floor(void) {
    if (gHudDisplay.wedges < 4)
        o->oBowserUnk108 = 3;
    else
        o->oBowserUnk108 = random_float() * 3.0f + 1.0f;
    cur_obj_init_animation_with_sound(22);
    if (cur_obj_check_anim_frame(5))
        obj_spit_fire(0, 200, 180, 7.0f, MODEL_RED_FLAME, 30.0f, 10.0f, 0x1000);
    if (cur_obj_check_if_near_animation_end())
        o->oSubAction++;
    if (o->oSubAction >= o->oBowserUnk108)
        o->oAction = 0;
}

s32 bowser_turn_on_timer(s32 a0, s16 a1) {
    if (o->oSubAction == 0) {
        if (cur_obj_init_animation_and_check_if_near_end(15))
            o->oSubAction++;
    } else if (o->oSubAction == 1) {
        if (cur_obj_init_animation_and_check_if_near_end(14))
            o->oSubAction++;
    } else
        cur_obj_init_animation_with_sound(12);
    o->oForwardVel = 0.0f;
    o->oMoveAngleYaw += a1;
    if (o->oTimer >= a0)
        return 1;
    else
        return 0;
}

void bowser_act_turn_from_edge(void) {
    if (bowser_turn_on_timer(63, 0x200))
        o->oAction = 0;
}

void bowser_act_charge_mario(void) {
    s32 sp34;
    if (o->oTimer == 0)
        o->oForwardVel = 0.0f;
    switch (o->oSubAction) {
        case 0:
            o->oBowserUnkF8 = 0;
            if (cur_obj_init_animation_and_check_if_near_end(18))
                o->oSubAction = 1;
            break;
        case 1:
            o->oForwardVel = 50.0f;
            if (cur_obj_init_animation_and_check_if_near_end(0x13) != 0) {
                o->oBowserUnkF8++;
                if (o->oBowserUnkF8 >= 6)
                    o->oSubAction = 3;
                if (o->oBowserUnkF8 >= 2)
                    if (abs_angle_diff(o->oAngleToMario, o->oMoveAngleYaw) > 0x2000)
                        o->oSubAction = 3;
            }
            cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x200);
            break;
        case 3:
            o->oBowserUnkF8 = 0;
            cur_obj_init_animation_with_sound(21);
            spawn_object_relative_with_scale(0, 100, -50, 0, 3.0f, o, MODEL_SMOKE, bhvWhitePuffSmoke2);
            spawn_object_relative_with_scale(0, -100, -50, 0, 3.0f, o, MODEL_SMOKE,
                                             bhvWhitePuffSmoke2);
            if (approach_f32_signed(&o->oForwardVel, 0, -1.0f))
                o->oSubAction = 2;
            cur_obj_extend_animation_if_at_end();
            break;
        case 2:
            o->oForwardVel = 0.0f;
            cur_obj_init_animation_with_sound(20);
            if (cur_obj_check_if_near_animation_end()) {
                if (BITS)
                    sp34 = 10;
                else
                    sp34 = 30;
                if (o->oBowserUnkF8 > sp34)
                    o->oAction = 0;
                o->oBowserUnkF8++;
            }
            cur_obj_extend_animation_if_at_end();
            break;
    }
    if (o->oMoveFlags & OBJ_MOVE_HIT_EDGE)
        o->oAction = 10;
}

s32 bowser_check_hit_mine(void) {
    struct Object *mine;
    f32 sp18;
    mine = cur_obj_find_nearest_object_with_behavior(bhvBowserBomb, &sp18);
    if (mine != NULL && sp18 < 800.0f) {
        mine->oInteractStatus |= INT_STATUS_HIT_MINE;
        return 1;
    }
    return 0;
}

void bowser_act_thrown_dropped(void)
{
    UNUSED s32 unused;
    if (o->oTimer < 2)
        o->oBowserUnkF8 = 0;
    if (o->oSubAction == 0) {
        cur_obj_init_animation_with_sound(2);
        bowser_bounce(&o->oBowserUnkF8);
        if (o->oMoveFlags & OBJ_MOVE_ON_GROUND) {
            o->oForwardVel = 0.0f;
            o->oSubAction++;
        }
    } else if (cur_obj_init_animation_and_check_if_near_end(0))
        o->oAction = 0;
    if (bowser_check_hit_mine()) {
        o->oHealth--;
        if (o->oHealth <= 0)
            o->oAction = 4;
        else
            o->oAction = 12;
    }
}

void bowser_set_goal_invisible(void) {
    o->oBowserUnk1AC = 0;
    if (o->oOpacity == 0) {
        o->oForwardVel = 0.0f;
        o->oVelY = 0.0f;
        o->oPosY = o->oHomeY - 1000.0f;
    }
}

void bowser_act_jump_onto_stage(void) {
    s32 sp2C;
    UNUSED s32 unused;
    struct Surface *sp24 = o->oFloor;
    if (sp24 != NULL && sp24->flags & 1)
        sp2C = 1;
    else
        sp2C = 0;
    o->oBowserUnkF4 |= 0x10000;
    switch (o->oSubAction) {
        case 0:
            if (o->oTimer == 0) {
                o->oFaceAnglePitch = 0;
                o->oFaceAngleRoll = 0;
            } //? missing else
            o->oFaceAnglePitch += 0x800;
            o->oFaceAngleRoll += 0x800;
            if (!(o->oFaceAnglePitch & 0xFFFF))
                o->oSubAction++;
            bowser_set_goal_invisible();
            break;
        case 1:
            cur_obj_init_animation_with_sound(9);
            if (cur_obj_check_anim_frame(11)) {
                o->oMoveAngleYaw = o->oBowserAngleToCentre;
                o->oVelY = 150.0f;
                o->oBowserUnk1AC = 0xFF;
                o->oBowserUnkF8 = 0;
                o->oSubAction++;
            } else
                bowser_set_goal_invisible();
            break;
        case 2:
            if (o->oPosY > o->oHomeY) {
                o->oDragStrength = 0.0f;
                if (o->oBowserDistToCentre < 2500.0f) {
                    if (absf(o->oFloorHeight - o->oHomeY) < 100.0f)
                        approach_f32_signed(&o->oForwardVel, 0, -5.0f);
                    else
                        cur_obj_forward_vel_approach_upward(150.0f, 2.0f);
                } else
                    cur_obj_forward_vel_approach_upward(150.0f, 2.0f);
            }
            if (bowser_land()) {
                o->oDragStrength = 10.0f;
                o->oSubAction++;
                if (sp2C == 0)
                    bowser_spawn_shockwave();
                else if (BITS)
                    o->oAction = 13;
                if (BITFS)
                    o->oAction = 19;
            }
#ifndef VERSION_JP
            bowser_reset_fallen_off_stage();
#else
            if (o->oVelY < 0.0f && o->oPosY < o->oHomeY - 300.0f) {
                o->oPosZ = 0.0f, o->oPosX = o->oPosZ;
                o->oPosY = o->oHomeY + 2000.0f;
                o->oVelY = 0.0f;
            }
#endif
            break;
        case 3:
            if (cur_obj_check_if_near_animation_end()) {
                o->oAction = 0;
                o->oBowserUnkF4 &= ~0x10000;
                cur_obj_extend_animation_if_at_end();
            }
            break;
    }
    print_debug_bottom_up("sp %d", o->oForwardVel);
}

void bowser_act_dance(void) {
    if (is_item_in_array(o->oTimer, D_8032F514))
        cur_obj_play_sound_2(SOUND_OBJ_BOWSER_WALK);
    if (cur_obj_init_animation_and_check_if_near_end(10))
        o->oAction = 0;
}

void bowser_spawn_grand_star_key(void) {
    if (BITS)
        gSecondCameraFocus = spawn_object(o, MODEL_STAR, bhvGrandStar);
    else {
        gSecondCameraFocus = spawn_object(o, MODEL_BOWSER_KEY, bhvBowserKey);
        cur_obj_play_sound_2(SOUND_GENERAL2_BOWSER_KEY);
    }
    gSecondCameraFocus->oAngleVelYaw = o->oAngleVelYaw;
}

void bowser_fly_back_dead(void) {
    cur_obj_init_animation_with_sound(16);
    if (BITS)
        o->oForwardVel = -400.0f;
    else
        o->oForwardVel = -200.0f;
    o->oVelY = 100.0f;
    o->oMoveAngleYaw = o->oBowserAngleToCentre + 0x8000;
    o->oBowserUnkF8 = 0;
    o->oSubAction++;
}

void bowser_dead_bounce(void) {
    o->oBowserEyesShut = 1;
    bowser_bounce(&o->oBowserUnkF8);
    if (o->oMoveFlags & OBJ_MOVE_LANDED)
        cur_obj_play_sound_2(SOUND_OBJ_BOWSER_WALK);
    if (o->oMoveFlags & OBJ_MOVE_ON_GROUND) {
        o->oForwardVel = 0.0f;
        o->oSubAction++;
    }
}

s32 bowser_dead_wait_for_mario(void) {
    s32 ret = 0;
    cur_obj_become_intangible();
    if (cur_obj_init_animation_and_check_if_near_end(17) && o->oDistanceToMario < 700.0f
        && abs_angle_diff(gMarioObject->oMoveAngleYaw, o->oAngleToMario) > 0x6000)
        ret = 1;
    cur_obj_extend_animation_if_at_end();
    o->oBowserUnkF8 = 0;
    return ret;
}

s32 bowser_dead_twirl_into_trophy(void) {
    s32 ret = 0;
    if (o->header.gfx.scale[0] < 0.8)
        o->oAngleVelYaw += 0x80;
    if (o->header.gfx.scale[0] > 0.2) {
        o->header.gfx.scale[0] = o->header.gfx.scale[0] - 0.02;
        o->header.gfx.scale[2] = o->header.gfx.scale[2] - 0.02;
    } else {
        o->header.gfx.scale[1] = o->header.gfx.scale[1] - 0.01;
        o->oVelY = 20.0f;
        o->oGravity = 0.0f;
    }
    if (o->header.gfx.scale[1] < 0.5)
        ret = 1;
    o->oMoveAngleYaw += o->oAngleVelYaw;
    if (o->oOpacity >= 3)
        o->oOpacity -= 2;
    return ret;
}

void bowser_dead_hide(void) {
    cur_obj_scale(0);
    o->oForwardVel = 0;
    o->oVelY = 0;
    o->oGravity = 0;
}

s32 bowser_dead_not_bits_end(void) {
    s32 ret = 0;
    if (o->oBowserUnkF8 < 2) {
        if (o->oBowserUnkF8 == 0) {
            func_8031FFB4(SEQ_PLAYER_LEVEL, 60, 40);
            o->oBowserUnkF8++;
        }
        if (cur_obj_update_dialog(2, 18, sBowserDefeatedDialogText[o->oBehParams2ndByte], 0)) {
            o->oBowserUnkF8++;
            cur_obj_play_sound_2(SOUND_GENERAL2_BOWSER_EXPLODE);
            sequence_player_unlower(SEQ_PLAYER_LEVEL, 60);
            sequence_player_fade_out(0, 1);
        }
    } else if (bowser_dead_twirl_into_trophy()) {
        bowser_dead_hide();
        spawn_triangle_break_particles(20, 116, 1.0f, 0);
        bowser_spawn_grand_star_key();
        set_mario_npc_dialog(0);
        ret = 1;
    }
    return ret;
}

s32 bowser_dead_bits_end(void) {
    UNUSED s32 unused;
    s32 ret = 0;
    s32 dialogID;
    if (o->oBowserUnkF8 < 2) {
        if (gHudDisplay.stars < 120)
            dialogID = DIALOG_121;
        else
            dialogID = DIALOG_163;
        if (o->oBowserUnkF8 == 0) {
            func_8031FFB4(SEQ_PLAYER_LEVEL, 60, 40);
            o->oBowserUnkF8++;
        }
        if (cur_obj_update_dialog(2, 18, dialogID, 0)) {
            cur_obj_set_model(MODEL_BOWSER2);
            sequence_player_unlower(SEQ_PLAYER_LEVEL, 60);
            sequence_player_fade_out(0, 1);
            bowser_spawn_grand_star_key();
            o->oBowserUnkF8++;
        }
    } else if (o->oOpacity > 4)
        o->oOpacity -= 4;
    else {
        bowser_dead_hide();
        ret = 1;
    }
    return ret;
}

void bowser_act_dead(void) {
    switch (o->oSubAction) {
        case 0:
            bowser_fly_back_dead();
            break;
        case 1:
            bowser_dead_bounce();
            break;
        case 2:
            if (bowser_dead_wait_for_mario()) {
                o->oBowserUnkF8 = 0;
                if (BITS)
                    o->oSubAction = 10;
                else {
                    o->activeFlags |= ACTIVE_FLAG_DITHERED_ALPHA;
                    o->oSubAction++;
                }
            }
            break;
        case 3:
            if (bowser_dead_not_bits_end())
                o->oSubAction++;
            break;
        case 4:
            break;
        case 10:
            if (bowser_dead_bits_end())
                o->oSubAction++;
            break;
        case 11:
            break;
    }
}

void bowser_tilt_platform(struct Object *platform, s16 a1) {
    s16 angle;
    angle = o->oBowserAngleToCentre + 0x8000;
    platform->oAngleVelPitch = coss(angle) * a1;
    platform->oAngleVelRoll = -sins(angle) * a1;
}

void bowser_act_ride_tilting_platform(void) {
    struct Object *platform = cur_obj_nearest_object_with_behavior(bhvTiltingBowserLavaPlatform);
    UNUSED s16 sp2A = o->oBowserAngleToCentre + 0x8000;
    s16 sp28;
    UNUSED s32 unused;
    s32 i;
    s32 sp1C;
    if (platform == NULL)
        o->oAction = 0;
    else {
        i = 0;
        sp1C = 1;
        while (D_8032F520[i][2] != 0) {
            if (o->oTimer < D_8032F520[i][2]) {
                sp28 = D_8032F520[i][1];
                if (D_8032F520[i][0] > 0)
                    sp28 = (D_8032F520[i][2] - o->oTimer - 1) * sp28;
                else
                    sp28 = (o->oTimer - D_8032F520[i - 1][2]) * sp28;
                bowser_tilt_platform(platform, sp28);
                if (sp28 != 0)
                    play_sound(SOUND_ENV_UNKNOWN4, platform->header.gfx.cameraToObject);
                sp1C = 0;
                break;
            }
            i++;
        }
        if (sp1C) {
            o->oAction = 0;
            platform->oAngleVelPitch = 0;
            platform->oAngleVelRoll = 0;
            platform->oFaceAnglePitch = 0;
            platform->oFaceAngleRoll = 0;
        }
    }
    cur_obj_extend_animation_if_at_end();
}

s32 bowser_check_fallen_off_stage(void) // bowser off stage?
{
    if (o->oAction != 2 && o->oAction != 19) {
        if (o->oPosY < o->oHomeY - 1000.0f)
            return 1;
        if (o->oMoveFlags & OBJ_MOVE_LANDED) {
            if (o->oFloorType == 1)
                return 1;
            if (o->oFloorType == 10)
                return 1;
        }
    }
    return 0;
}

void (*sBowserActions[])(void) = { bowser_act_default,  bowser_act_thrown_dropped,  bowser_act_jump_onto_stage,  bowser_act_dance,
                                   bowser_act_dead,  bowser_act_text_wait,  bowser_act_intro_walk,  bowser_act_charge_mario,
                                   bowser_act_spit_fire_into_sky,  bowser_act_spit_fire_onto_floor,  bowser_act_hit_edge, bowser_act_turn_from_edge,
                                   bowser_act_hit_mine, bowser_act_jump, bowser_act_walk_to_mario, bowser_act_breath_fire,
                                   bowser_act_teleport, bowser_act_jump_towards_mario, bowser_act_unused_slow_walk, bowser_act_ride_tilting_platform };
struct SoundState D_8032F5B8[] = { { 0, 0, 0, NO_SOUND },
                                   { 0, 0, 0, NO_SOUND },
                                   { 0, 0, 0, NO_SOUND },
                                   { 0, 0, 0, NO_SOUND },
                                   { 0, 0, 0, NO_SOUND },
                                   { 0, 0, 0, NO_SOUND },
                                   { 0, 0, 0, NO_SOUND },
                                   { 0, 0, 0, NO_SOUND },
                                   { 1, 0, -1, SOUND_OBJ_BOWSER_WALK },
                                   { 1, 0, -1, SOUND_OBJ2_BOWSER_ROAR },
                                   { 1, 0, -1, SOUND_OBJ2_BOWSER_ROAR },
                                   { 0, 0, 0, NO_SOUND },
                                   { 0, 0, 0, NO_SOUND },
                                   { 1, 20, 40, SOUND_OBJ_BOWSER_WALK },
                                   { 1, 20, -1, SOUND_OBJ_BOWSER_WALK },
                                   { 1, 20, 40, SOUND_OBJ_BOWSER_WALK },
                                   { 1, 0, -1, SOUND_OBJ_BOWSER_TAIL_PICKUP },
                                   { 1, 0, -1, SOUND_OBJ_BOWSER_DEFEATED },
                                   { 1, 8, -1, SOUND_OBJ_BOWSER_WALK },
                                   { 1, 8, 17, SOUND_OBJ_BOWSER_WALK },
                                   { 1, 8, -10, SOUND_OBJ_BOWSER_WALK },
                                   { 0, 0, 0, NO_SOUND },
                                   { 1, 5, -1, SOUND_OBJ_FLAME_BLOWN },
                                   { 0, 0, 0, NO_SOUND },
                                   { 0, 0, 0, NO_SOUND },
                                   { 1, 0, -1, SOUND_OBJ_BOWSER_TAIL_PICKUP },
                                   { 1, 0, -1, SOUND_OBJ2_BOWSER_ROAR } };
s8 D_8032F690[4] = { 0, 0, 1, 0 };
s8 D_8032F694[4] = { 1, 1, 3, 0 };
extern u8 bowser_3_seg7_collision_07004B94[];
extern u8 bowser_3_seg7_collision_07004C18[];
extern u8 bowser_3_seg7_collision_07004C9C[];
extern u8 bowser_3_seg7_collision_07004D20[];
extern u8 bowser_3_seg7_collision_07004DA4[];
extern u8 bowser_3_seg7_collision_07004E28[];
extern u8 bowser_3_seg7_collision_07004EAC[];
extern u8 bowser_3_seg7_collision_07004F30[];
extern u8 bowser_3_seg7_collision_07004FB4[];
extern u8 bowser_3_seg7_collision_07005038[];
struct Struct8032F698 D_8032F698[] = { { NULL, 0, 0, 0, 0 },
                                       { bowser_3_seg7_collision_07004B94, -800, -1000, -20992, 0 },
                                       { bowser_3_seg7_collision_07004C18, -1158, 390, -18432, 0 },
                                       { bowser_3_seg7_collision_07004C9C, -1158, 390, -7680, 0 },
                                       { bowser_3_seg7_collision_07004D20, 0, 1240, -6144, 0 },
                                       { bowser_3_seg7_collision_07004DA4, 0, 1240, 6144, 0 },
                                       { bowser_3_seg7_collision_07004E28, 1158, 390, 7680, 0 },
                                       { bowser_3_seg7_collision_07004EAC, 1158, 390, 18432, 0 },
                                       { bowser_3_seg7_collision_07004F30, 800, -1000, 20992, 0 },
                                       { bowser_3_seg7_collision_07004FB4, 800, -1000, -31744, 0 },
                                       { bowser_3_seg7_collision_07005038, -800, -1000, 31744, 0 } };

void bowser_free_update(void) {
    struct Surface *floor;
    struct Object *platform;
    UNUSED f32 floorHeight;
    if ((platform = o->platform) != NULL)
        apply_platform_displacement(FALSE, platform);
    o->oBowserUnk10E = 0;
    cur_obj_update_floor_and_walls();
    cur_obj_call_action_function(sBowserActions);
    cur_obj_move_standard(-78);
    if (bowser_check_fallen_off_stage())
        o->oAction = 2; // bowser go home?
    floorHeight = find_floor(o->oPosX, o->oPosY, o->oPosZ, &floor);
    if ((floor != NULL) && (floor->object != 0))
        o->platform = floor->object;
    else
        o->platform = NULL;
    exec_anim_sound_state(D_8032F5B8);
}

void bowser_held_update(void) {
    o->oBowserUnkF4 &= ~0x20000;
    cur_obj_become_intangible();
    switch (o->oBowserUnk10E) {
        case 0:
            cur_obj_play_sound_2(SOUND_OBJ_BOWSER_TAIL_PICKUP);
            cur_obj_unrender_and_reset_state(3, 1);
            o->oBowserUnk10E++;
            break;
        case 1:
            if (cur_obj_check_if_near_animation_end()) {
                cur_obj_init_animation_with_sound(2);
                o->oBowserUnk10E++;
            }
            break;
        case 2:
            break;
    }
    o->oMoveFlags = 0;
    o->oBowserHeldAnglePitch = gMarioObject->oMoveAnglePitch;
    o->oBowserHeldAngleVelYaw = gMarioObject->oAngleVelYaw;
    o->oMoveAngleYaw = gMarioObject->oMoveAngleYaw;
}

void bowser_thrown_dropped_update(void) {
    f32 sp1C;
    o->oBowserUnk10E = 0;
    cur_obj_get_thrown_or_placed(1.0f, 1.0f, 1);
    sp1C = o->oBowserHeldAngleVelYaw / 3000.0 * 70.0f;
    if (sp1C < 0.0f)
        sp1C = -sp1C;
    if (sp1C > 90.0f)
        sp1C *= 2.5; // > 90 => get bigger?
    o->oForwardVel = coss(o->oBowserHeldAnglePitch) * sp1C;
    o->oVelY = -sins(o->oBowserHeldAnglePitch) * sp1C;
    cur_obj_become_intangible();
    o->prevObj->oAction = 1; // not sure what prevObj is
    o->prevObj->oTimer = 0;
    o->prevObj->oSubAction = 0;
    o->oTimer = 0;
    o->oSubAction = 0;
}

void bhv_bowser_loop(void) {
    s16 angleToMario;  // AngleToMario    from Bowser's perspective
    s16 angleToCentre; // AngleToCentre from Bowser's perspective

    o->oBowserDistToCentre = sqrtf(o->oPosX * o->oPosX + o->oPosZ * o->oPosZ);
    o->oBowserAngleToCentre = atan2s(0.0f - o->oPosZ, 0.0f - o->oPosX);
    angleToMario = abs_angle_diff(o->oMoveAngleYaw, o->oAngleToMario);
    angleToCentre = abs_angle_diff(o->oMoveAngleYaw, o->oBowserAngleToCentre);
    o->oBowserUnkF4 &= ~0xFF;
    if (angleToMario < 0x2000)
        o->oBowserUnkF4 |= 2;
    if (angleToCentre < 0x3800)
        o->oBowserUnkF4 |= 4;
    if (o->oBowserDistToCentre < 1000.0f)
        o->oBowserUnkF4 |= 0x10;
    if (o->oDistanceToMario < 850.0f)
        o->oBowserUnkF4 |= 8;
    switch (o->oHeldState) {
        case HELD_FREE:
            bowser_free_update();
            break;
        case HELD_HELD:
            bowser_held_update();
            break;
        case HELD_THROWN:
            bowser_thrown_dropped_update();
            break;
        case HELD_DROPPED:
            bowser_thrown_dropped_update();
            break;
    }
    cur_obj_align_gfx_with_floor();
    if (o->oAction != 4)
        if (o->oBowserUnk1AC != o->oOpacity) {
            if (o->oBowserUnk1AC > o->oOpacity) {
                o->oOpacity += 20;
                if (o->oOpacity >= 0x100)
                    o->oOpacity = 0xFF;
            } else {
                o->oOpacity -= 20;
                if (o->oOpacity < 0)
                    o->oOpacity = 0;
            }
        }
}

void bhv_bowser_init(void) {
    s32 level; // 0 is dw, 1 is fs, 2 is sky
    o->oBowserUnk110 = 1;
    o->oOpacity = 0xFF;
    o->oBowserUnk1AC = 0xFF;
    if (gCurrLevelNum == LEVEL_BOWSER_2)
        level = 1;
    else if (gCurrLevelNum == LEVEL_BOWSER_3)
        level = 2;
    else
        level = 0;
    o->oBehParams2ndByte = level;
    o->oBowserUnk1B2 = D_8032F690[level];
    o->oHealth = D_8032F694[level];
    cur_obj_start_cam_event(o, CAM_EVENT_BOWSER_INIT);
    o->oAction = 5;
    o->oBowserUnk1AE = 0;
    o->oBowserEyesShut = 0;
}

#undef BITDW
#undef BITFS
#undef BITS

Gfx *geo_update_body_rot_from_parent(s32 run, UNUSED struct GraphNode *node, Mat4 mtx) {
    Mat4 sp20;
    struct Object *sp1C;

    if (run == TRUE) {
        sp1C = (struct Object *) gCurGraphNodeObject;
        if (sp1C->prevObj != NULL) {
            create_transformation_from_matrices(sp20, mtx, *gCurGraphNodeCamera->matrixPtr);
            obj_update_pos_from_parent_transformation(sp20, sp1C->prevObj);
            obj_set_gfx_pos_from_pos(sp1C->prevObj);
        }
    }
    return NULL;
}

void bowser_open_eye_switch(struct Object *a0, struct GraphNodeSwitchCase *switchCase) {
    s32 sp1C;
    s16 sp1A;
    sp1A = abs_angle_diff(a0->oMoveAngleYaw, a0->oAngleToMario);
    sp1C = switchCase->selectedCase;
    switch (sp1C) {
        case 0:
            if (sp1A > 0x2000) {
                if (a0->oAngleVelYaw > 0)
                    switchCase->selectedCase = 5;
                if (a0->oAngleVelYaw < 0)
                    switchCase->selectedCase = 3;
            }
            if (a0->oBowserUnk1AE > 50)
                switchCase->selectedCase = 1;
            break;
        case 1:
            if (a0->oBowserUnk1AE > 2)
                switchCase->selectedCase = 2;
            break;
        case 2:
            if (a0->oBowserUnk1AE > 2)
                switchCase->selectedCase = 9;
            break;
        case 9:
            if (a0->oBowserUnk1AE > 2)
                switchCase->selectedCase = 0;
            break;
        case 5:
            if (a0->oBowserUnk1AE > 2) {
                switchCase->selectedCase = 6;
                if (a0->oAngleVelYaw <= 0)
                    switchCase->selectedCase = 0;
            }
            break;
        case 6:
            if (a0->oAngleVelYaw <= 0)
                switchCase->selectedCase = 5;
            break;
        case 3:
            if (a0->oBowserUnk1AE > 2) {
                switchCase->selectedCase = 4;
                if (a0->oAngleVelYaw >= 0)
                    switchCase->selectedCase = 0;
            }
            break;
        case 4:
            if (a0->oAngleVelYaw >= 0)
                switchCase->selectedCase = 3;
            break;
        default:
            switchCase->selectedCase = 0;
    }
    if (switchCase->selectedCase != sp1C)
        a0->oBowserUnk1AE = -1;
}

/** Geo switch for controlling the state of bowser's eye direction and open/closed
 * state. Checks whether oBowserEyesShut is TRUE and closes eyes if so and processes
 * direction otherwise.
 */
Gfx *geo_switch_bowser_eyes(s32 run, struct GraphNode *node, UNUSED Mat4 *mtx) {
    UNUSED s16 sp36;
    UNUSED s32 unused;
    struct Object *obj = (struct Object *) gCurGraphNodeObject;
    struct GraphNodeSwitchCase *switchCase = (struct GraphNodeSwitchCase *) node;
    if (run == TRUE) {
        if (gCurGraphNodeHeldObject != NULL)
            obj = gCurGraphNodeHeldObject->objNode;
        switch (sp36 = obj->oBowserEyesShut) {
            case 0: // eyes open, handle eye looking direction
                bowser_open_eye_switch(obj, switchCase);
                break;
            case 1: // eyes closed, blinking
                switchCase->selectedCase = 2;
                break;
        }
        obj->oBowserUnk1AE++;
    }
    return NULL;
}

Gfx *geo_bits_bowser_coloring(s32 run, struct GraphNode *node, UNUSED s32 a2) {
    Gfx *sp2C = NULL;
    Gfx *sp28;
    struct Object *sp24;
    struct GraphNodeGenerated *sp20;

    if (run == 1) {
        sp24 = (struct Object *) gCurGraphNodeObject;
        sp20 = (struct GraphNodeGenerated *) node;
        if (gCurGraphNodeHeldObject != 0)
            sp24 = gCurGraphNodeHeldObject->objNode;
        if (sp24->oOpacity == 0xFF)
            sp20->fnNode.node.flags = (sp20->fnNode.node.flags & 0xFF) | GRAPH_NODE_TYPE_FUNCTIONAL;
        else
            sp20->fnNode.node.flags = (sp20->fnNode.node.flags & 0xFF) | (GRAPH_NODE_TYPE_FUNCTIONAL | GRAPH_NODE_TYPE_400);
        sp28 = sp2C = alloc_display_list(2 * sizeof(Gfx));

        if (sp24->oBowserUnk1B2 != 0) {
            gSPClearGeometryMode(sp28++, G_LIGHTING);
        }
        gSPEndDisplayList(sp28);
    }
    return sp2C;
}

void falling_bowser_plat_act_0(void) {
    o->oPlatformUnkF8 = cur_obj_nearest_object_with_behavior(bhvBowser);
    obj_set_collision_data(o, D_8032F698[o->oBehParams2ndByte].unk0);
    if (o->oPlatformUnkF8 != 0)
        o->oAction = 1;
}

void falling_bowser_plat_act_1(void) {
    UNUSED s32 unused;
    struct Object *sp0 = o->oPlatformUnkF8;
    if (sp0->platform == o)
        if (sp0->oAction == 13 && sp0->oBowserUnkF4 & 0x10000)
            o->oAction = 2;
    if (sp0->oHealth == 1 && (sp0->oAction == 3 || sp0->oHeldState != HELD_FREE))
        o->oSubAction = 1;
    if (o->oSubAction == 0)
        o->oPlatformUnkFC = 0;
    else {
        if ((gDebugInfo[4][6] + 20) * (o->oBehParams2ndByte - 1) < o->oPlatformUnkFC)
            o->oAction = 2;
        o->oPlatformUnkFC++;
    }
}

void falling_bowser_plat_act_2(void) {
    Vec3f sp24;
    s16 sp22;
    f32 sp1C;
    UNUSED struct Object *sp18 = o->oPlatformUnkF8;
    if (o->oTimer == 0 || o->oTimer == 22)
        cur_obj_play_sound_2(SOUND_GENERAL_BOWSER_PLATFORM_2);
    if (o->oTimer < 22) {
        set_environmental_camera_shake(SHAKE_ENV_FALLING_BITS_PLAT);
        o->oVelY = 8.0f;
        o->oGravity = 0.0f;
    } else
        o->oGravity = -4.0f;
    if ((o->oTimer & 1) == 0 && o->oTimer < 14) {
        sp22 = D_8032F698[o->oBehParams2ndByte].unk3 + (gDebugInfo[4][1] << 8);
        sp1C = -(o->oTimer / 2) * 290 + 1740;
        vec3f_copy_2(sp24, &o->oPosX);
        o->oPosX = D_8032F698[o->oBehParams2ndByte].unk1 + sins(sp22 + 5296) * sp1C;
        o->oPosZ = D_8032F698[o->oBehParams2ndByte].unk2 + coss(sp22 + 5296) * sp1C;
        o->oPosY = 307.0f;
        spawn_mist_particles_variable(4, 0, 100.0f);
        o->oPosX = D_8032F698[o->oBehParams2ndByte].unk1 + sins(sp22 - 5296) * sp1C;
        o->oPosZ = D_8032F698[o->oBehParams2ndByte].unk2 + coss(sp22 - 5296) * sp1C;
        spawn_mist_particles_variable(4, 0, 100);
        vec3f_copy_2(&o->oPosX, sp24);
    }
    cur_obj_move_using_fvel_and_gravity();
    if (o->oTimer > 300)
        obj_mark_for_deletion(o);
}

void (*sFallingBowserPlatformActions[])(void) = { falling_bowser_plat_act_0,
                                                  falling_bowser_plat_act_1,
                                                  falling_bowser_plat_act_2 };

struct ObjectHitbox sGrowingBowserFlameHitbox = {
    /* interactType: */ INTERACT_FLAME,
    /* downOffset: */ 20,
    /* damageOrCoinValue: */ 1,
    /* health: */ 0,
    /* numLootCoins: */ 0,
    /* radius: */ 10,
    /* height: */ 40,
    /* hurtboxRadius: */ 0,
    /* hurtboxHeight: */ 0,
};

struct ObjectHitbox sBowserFlameHitbox = {
    /* interactType: */ INTERACT_FLAME,
    /* downOffset: */ 0,
    /* damageOrCoinValue: */ 1,
    /* health: */ 0,
    /* numLootCoins: */ 0,
    /* radius: */ 10,
    /* height: */ 40,
    /* hurtboxRadius: */ 0,
    /* hurtboxHeight: */ 0,
};

f32 D_8032F748[] = { -8.0f, -6.0f, -3.0f };

void bhv_falling_bowser_platform_loop(void) {
    cur_obj_call_action_function(sFallingBowserPlatformActions);
}

void bowser_flame_despawn(void) {
    obj_mark_for_deletion(o);
    spawn_object_with_scale(o, MODEL_NONE, bhvBlackSmokeUpward, 1.0f);
    if (random_float() < 0.1)
        spawn_object(o, MODEL_YELLOW_COIN, bhvTemporaryYellowCoin);
}

s32 bowser_flame_should_despawn(s32 maxTime) {
    if (maxTime < o->oTimer)
        return 1;
    if (o->oFloorType == 1)
        return 1;
    if (o->oFloorType == 10)
        return 1;
    return 0;
}

void bhv_flame_bowser_init(void) {
    o->oAnimState = (s32)(random_float() * 10.0f);
    o->oMoveAngleYaw = random_u16();
    if (random_float() < 0.2)
        o->oVelY = 80.0f;
    else
        o->oVelY = 20.0f;
    o->oForwardVel = 10.0f;
    o->oGravity = -1.0f;
    o->oFlameUnkF4 = random_float() + 1.0f;
}

void bhv_flame_large_burning_out_init(void) {
    o->oAnimState = (s32)(random_float() * 10.0f);
    o->oMoveAngleYaw = random_u16();
    o->oVelY = 10.0f;
    o->oForwardVel = 0.0f;
    o->oFlameUnkF4 = 7.0f;
}

void bowser_flame_move(void) {
    s32 sp4;
    sp4 = ((o->oFlameUnkF8 + gGlobalTimer) & 0x3F) << 10;
    o->oPosX += sins(o->oMoveAngleYaw) * sins(sp4) * 4.0f;
    o->oPosZ += coss(o->oMoveAngleYaw) * sins(sp4) * 4.0f;
}

void bhv_flame_bowser_loop(void) {
    cur_obj_update_floor_and_walls();
    cur_obj_move_standard(78);
    if (o->oVelY < -4.0f)
        o->oVelY = -4.0f;
    if (o->oAction == 0) {
        cur_obj_become_intangible();
        bowser_flame_move();
        if (o->oMoveFlags & OBJ_MOVE_LANDED) {
            o->oAction++;
            if (cur_obj_has_behavior(bhvFlameLargeBurningOut))
                o->oFlameUnkF4 = 8.0f;
            else
                o->oFlameUnkF4 = random_float() * 2 + 6.0f;
            o->oForwardVel = 0;
            o->oVelY = 0;
            o->oGravity = 0;
        }
    } else {
        cur_obj_become_tangible();
        if (o->oTimer > o->oFlameUnkF4 * 10 + 5.0f) {
            o->oFlameUnkF4 -= 0.15;
            if (o->oFlameUnkF4 <= 0)
                bowser_flame_despawn();
        }
    }
    cur_obj_scale(o->oFlameUnkF4);
    o->oGraphYOffset = o->header.gfx.scale[1] * 14.0f;
    obj_set_hitbox(o, &sBowserFlameHitbox);
}

void bhv_flame_moving_forward_growing_init(void) {
    o->oForwardVel = 30.0f;
    obj_translate_xz_random(o, 80.0f);
    o->oAnimState = (s32)(random_float() * 10.0f);
    o->oFlameUnkF4 = 3.0f;
}

void bhv_flame_moving_forward_growing_loop(void) {
    UNUSED s32 unused;
    UNUSED struct Object *sp18;
    obj_set_hitbox(o, &sGrowingBowserFlameHitbox);
    o->oFlameUnkF4 = o->oFlameUnkF4 + 0.5;
    cur_obj_scale(o->oFlameUnkF4);
    if (o->oMoveAnglePitch > 0x800)
        o->oMoveAnglePitch -= 0x200;
    cur_obj_set_pos_via_transform();
    cur_obj_update_floor_height();
    if (o->oFlameUnkF4 > 30.0f)
        obj_mark_for_deletion(o);
    if (o->oPosY < o->oFloorHeight) {
        o->oPosY = o->oFloorHeight;
        sp18 = spawn_object(o, MODEL_RED_FLAME, bhvFlameBowser);
        obj_mark_for_deletion(o);
    }
}

void bhv_flame_floating_landing_init(void) {
    o->oAnimState = (s32)(random_float() * 10.0f);
    o->oMoveAngleYaw = random_u16();
    if (o->oBehParams2ndByte != 0)
        o->oForwardVel = random_float() * 5.0f;
    else
        o->oForwardVel = random_float() * 70.0f;
    o->oVelY = random_float() * 20.0f;
    o->oGravity = -1.0f;
    o->oFlameUnkF8 = random_float() * 64.0f;
}

void bhv_flame_floating_landing_loop(void) {
    UNUSED s32 unused;
    cur_obj_update_floor_and_walls();
    cur_obj_move_standard(0x4e);
    bowser_flame_move();
    if (bowser_flame_should_despawn(900))
        obj_mark_for_deletion(o);
    if (o->oVelY < D_8032F748[o->oBehParams2ndByte])
        o->oVelY = D_8032F748[o->oBehParams2ndByte];
    if (o->oMoveFlags & OBJ_MOVE_LANDED) {
        if (o->oBehParams2ndByte == 0)
            spawn_object(o, MODEL_RED_FLAME, bhvFlameLargeBurningOut);
        else
            spawn_object(o, MODEL_NONE, bhvBlueFlamesGroup); //? wonder if they meant MODEL_BLUE_FLAME?
        obj_mark_for_deletion(o);
    }
    o->oGraphYOffset = o->header.gfx.scale[1] * 14.0f;
}

void bhv_blue_bowser_flame_init(void) {
    obj_translate_xz_random(o, 80.0f);
    o->oAnimState = (s32)(random_float() * 10.0f);
    o->oVelY = 7.0f;
    o->oForwardVel = 35.0f;
    o->oFlameUnkF4 = 3.0f;
    o->oFlameUnkFC = random_float() * 0.5;
    o->oGravity = 1.0f;
    o->oFlameUnkF8 = (s32)(random_float() * 64.0f);
}

void bhv_blue_bowser_flame_loop(void) {
    s32 i;
    obj_set_hitbox(o, &sGrowingBowserFlameHitbox);
    if (o->oFlameUnkF4 < 16.0f)
        o->oFlameUnkF4 = o->oFlameUnkF4 + 0.5;
    cur_obj_scale(o->oFlameUnkF4);
    cur_obj_update_floor_and_walls();
    cur_obj_move_standard(0x4e);
    if (o->oTimer > 0x14) {
        if (o->oBehParams2ndByte == 0)
            for (i = 0; i < 3; i++)
                spawn_object_relative_with_scale(0, 0, 0, 0, 5.0f, o, MODEL_RED_FLAME,
                                                 bhvFlameFloatingLanding);
        else {
            spawn_object_relative_with_scale(1, 0, 0, 0, 8.0f, o, MODEL_BLUE_FLAME,
                                             bhvFlameFloatingLanding);
            spawn_object_relative_with_scale(2, 0, 0, 0, 8.0f, o, MODEL_BLUE_FLAME,
                                             bhvFlameFloatingLanding);
        }
        obj_mark_for_deletion(o);
    }
}

void bhv_flame_bouncing_init(void) {
    o->oAnimState = (s32)(random_float() * 10.0f);
    o->oVelY = 30.0f;
    o->oForwardVel = 20.0f;
    o->oFlameUnkF4 = o->header.gfx.scale[0];
    o->oFlameUnkF8 = (s32)(random_float() * 64.0f);
}

void bhv_flame_bouncing_loop(void) {
    struct Object *sp1C;
    if (o->oTimer == 0)
        o->oFlameUnk100 = cur_obj_nearest_object_with_behavior(bhvBowser);
    sp1C = o->oFlameUnk100;
    o->oForwardVel = 15.0f;
    o->oBounciness = -1.0f;
    cur_obj_scale(o->oFlameUnkF4);
    obj_set_hitbox(o, &sGrowingBowserFlameHitbox);
    cur_obj_update_floor_and_walls();
    cur_obj_move_standard(78);
    if (bowser_flame_should_despawn(300))
        obj_mark_for_deletion(o);
    if (sp1C != NULL)
        if (sp1C->oHeldState == 0)
            if (lateral_dist_between_objects(o, sp1C) < 300.0f)
                obj_mark_for_deletion(o);
}

void bhv_blue_flames_group_loop(void) {
    struct Object *flame;
    s32 i;
    if (o->oTimer == 0) {
        o->oMoveAngleYaw = obj_angle_to_object(o, gMarioObject);
        o->oBlueFlameUnkF8 = 5.0f;
    }
    if (o->oTimer < 16) {
        if ((o->oTimer & 1) == 0) {
            for (i = 0; i < 3; i++) {
                flame = spawn_object(o, MODEL_BLUE_FLAME, bhvFlameBouncing);
                flame->oMoveAngleYaw += i * 0x5555;
                flame->header.gfx.scale[0] = o->oBlueFlameUnkF8;
            }
            o->oBlueFlameUnkF8 -= 0.5;
        }
    } else
        obj_mark_for_deletion(o);
}
