sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

mips.inc.c (9331B)


      1 
      2 /**
      3  * Behavior for MIPS (everyone's favorite yellow rabbit).
      4  */
      5 
      6 /**
      7  * Initializes MIPS' physics parameters and checks if he should be active,
      8  * hiding him if necessary.
      9  */
     10 void bhv_mips_init(void) {
     11     // Retrieve star flags for Castle Secret Stars on current save file.
     12     u8 starFlags = save_file_get_star_flags(gCurrSaveFileNum - 1, COURSE_NUM_TO_INDEX(COURSE_NONE));
     13 
     14     // If the player has >= 15 stars and hasn't collected first MIPS star...
     15     if (save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1) >= 15
     16         && !(starFlags & SAVE_FLAG_TO_STAR_FLAG(SAVE_FLAG_COLLECTED_MIPS_STAR_1))) {
     17         o->oBhvParams2ndByte = MIPS_BP_15_STARS;
     18 #ifndef VERSION_JP
     19         o->oMipsForwardVelocity = 40.0f;
     20 #endif
     21     }
     22     // If the player has >= 50 stars and hasn't collected second MIPS star...
     23     else if (save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1) >= 50
     24              && !(starFlags & SAVE_FLAG_TO_STAR_FLAG(SAVE_FLAG_COLLECTED_MIPS_STAR_2))) {
     25         o->oBhvParams2ndByte = MIPS_BP_50_STARS;
     26 #ifndef VERSION_JP
     27         o->oMipsForwardVelocity = 45.0f;
     28 #endif
     29     } else {
     30         // No MIPS stars are available, hide MIPS.
     31         o->activeFlags = ACTIVE_FLAG_DEACTIVATED;
     32     }
     33 
     34     o->oInteractionSubtype = INT_SUBTYPE_HOLDABLE_NPC;
     35 
     36 #ifndef VERSION_JP
     37     o->oGravity = 15.0f;
     38 #else
     39     o->oGravity = 2.5f;
     40 #endif
     41     o->oFriction = 0.89f;
     42     o->oBuoyancy = 1.2f;
     43 
     44     cur_obj_init_animation(0);
     45 }
     46 
     47 /**
     48  * Helper function that finds the waypoint that is both within 800 units of MIPS
     49  * and furthest from Mario's current location.
     50  */
     51 s16 bhv_mips_find_furthest_waypoint_to_mario(void) {
     52     s8 i;
     53     s16 x, y, z;
     54     s16 furthestWaypointIndex = -1;
     55     f32 furthestWaypointDistance = -10000.0f;
     56     f32 distanceToMario;
     57     struct Waypoint **pathBase = segmented_to_virtual(&inside_castle_seg7_trajectory_mips);
     58 
     59     // For each waypoint in MIPS path...
     60     for (i = 0; i < 10; i++) {
     61         struct Waypoint *waypoint = segmented_to_virtual(pathBase[i]);
     62         x = waypoint->pos[0];
     63         y = waypoint->pos[1];
     64         z = waypoint->pos[2];
     65 
     66         // Is the waypoint within 800 units of MIPS?
     67         if (is_point_close_to_object(o, x, y, z, 800)) {
     68             // Is this further from Mario than the last waypoint?
     69             distanceToMario =
     70                 sqr(x - gMarioObject->header.gfx.pos[0]) + sqr(z - gMarioObject->header.gfx.pos[2]);
     71             if (furthestWaypointDistance < distanceToMario) {
     72                 furthestWaypointIndex = i;
     73                 furthestWaypointDistance = distanceToMario;
     74             }
     75         }
     76     }
     77 
     78     // Set MIPS' next waypoint to be the closest waypoint to Mario.
     79     o->oMipsStartWaypointIndex = furthestWaypointIndex;
     80     return (s16) o->oMipsStartWaypointIndex;
     81 }
     82 
     83 /**
     84  * Wait until Mario comes close, then resume following our path.
     85  */
     86 void bhv_mips_act_wait_for_nearby_mario(void) {
     87     UNUSED s16 collisionFlags = 0;
     88 
     89     o->oForwardVel = 0.0f;
     90     collisionFlags = object_step();
     91 
     92     // If Mario is within 500 units...
     93     if (is_point_within_radius_of_mario(o->oPosX, o->oPosY, o->oPosZ, 500)) {
     94         // If we fail to find a suitable waypoint...
     95         if (bhv_mips_find_furthest_waypoint_to_mario() == -1) {
     96             // Call it quits.
     97             o->oAction = MIPS_ACT_WAIT_FOR_ANIMATION_DONE;
     98         } else {
     99             // Resume path following.
    100             cur_obj_init_animation(1);
    101             o->oAction = MIPS_ACT_FOLLOW_PATH;
    102         }
    103     }
    104 }
    105 
    106 /**
    107  * Continue to follow our path around the basement area.
    108  */
    109 void bhv_mips_act_follow_path(void) {
    110     s16 collisionFlags = 0;
    111     s32 followStatus;
    112 
    113     // Retrieve current waypoint.
    114     struct Waypoint **pathBase = segmented_to_virtual(&inside_castle_seg7_trajectory_mips);
    115     struct Waypoint *waypoint = segmented_to_virtual(*(pathBase + o->oMipsStartWaypointIndex));
    116 
    117 #ifdef AVOID_UB
    118     followStatus = 0;
    119 #endif
    120 
    121     // Set start waypoint and follow the path from there.
    122     o->oPathedStartWaypoint = waypoint;
    123     //! Uninitialized parameter, but the parameter is unused in the called function
    124     followStatus = cur_obj_follow_path(followStatus);
    125 
    126     // Update velocity and angle and do movement.
    127 #ifndef VERSION_JP
    128     o->oForwardVel = o->oMipsForwardVelocity;
    129 #else
    130     o->oForwardVel = 45.0f;
    131 #endif
    132     o->oMoveAngleYaw = o->oPathedTargetYaw;
    133     collisionFlags = object_step();
    134 
    135     // If we are at the end of the path, do idle animation and wait for Mario.
    136     if (followStatus == PATH_REACHED_END) {
    137         cur_obj_init_animation(0);
    138         o->oAction = MIPS_ACT_WAIT_FOR_NEARBY_MARIO;
    139     }
    140 
    141     // Play sounds during walk animation.
    142     if (cur_obj_check_if_near_animation_end() == TRUE && (collisionFlags & OBJ_COL_FLAG_UNDERWATER)) {
    143         cur_obj_play_sound_2(SOUND_OBJ_MIPS_RABBIT_WATER);
    144         spawn_object(o, MODEL_NONE, bhvShallowWaterSplash);
    145     } else if (cur_obj_check_if_near_animation_end() == TRUE) {
    146         cur_obj_play_sound_2(SOUND_OBJ_MIPS_RABBIT);
    147     }
    148 }
    149 
    150 /**
    151  * Seems to wait until the current animation is done, then go idle.
    152  */
    153 void bhv_mips_act_wait_for_animation_done(void) {
    154     if (cur_obj_check_if_near_animation_end() == TRUE) {
    155         cur_obj_init_animation(0);
    156         o->oAction = MIPS_ACT_IDLE;
    157     }
    158 }
    159 
    160 /**
    161  * Handles MIPS falling down after being thrown.
    162  */
    163 void bhv_mips_act_fall_down(void) {
    164     s16 collisionFlags = 0;
    165 
    166     collisionFlags = object_step();
    167     o->header.gfx.animInfo.animFrame = 0;
    168 
    169     if ((collisionFlags & OBJ_COL_FLAG_GROUNDED) == OBJ_COL_FLAG_GROUNDED) {
    170         o->oAction = MIPS_ACT_WAIT_FOR_ANIMATION_DONE;
    171 
    172         o->oFlags |= OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW;
    173         o->oMoveAngleYaw = o->oFaceAngleYaw;
    174 
    175         if (collisionFlags & OBJ_COL_FLAG_UNDERWATER) {
    176             spawn_object(o, MODEL_NONE, bhvShallowWaterSplash);
    177         }
    178     }
    179 }
    180 
    181 /**
    182  * Idle loop, after you catch MIPS and put him down.
    183  */
    184 void bhv_mips_act_idle(void) {
    185     UNUSED s16 collisionFlags = 0;
    186 
    187     o->oForwardVel = 0.0f;
    188     collisionFlags = object_step();
    189 
    190     // Spawn a star if he was just picked up for the first time.
    191     if (o->oMipsStarStatus == MIPS_STAR_STATUS_SHOULD_SPAWN_STAR) {
    192         bhv_spawn_star_no_level_exit(STAR_INDEX_ACT_4 + o->oBhvParams2ndByte);
    193         o->oMipsStarStatus = MIPS_STAR_STATUS_ALREADY_SPAWNED_STAR;
    194     }
    195 }
    196 
    197 /**
    198  * Handles all the actions MIPS does when he is not held.
    199  */
    200 void bhv_mips_free(void) {
    201     switch (o->oAction) {
    202         case MIPS_ACT_WAIT_FOR_NEARBY_MARIO:
    203             bhv_mips_act_wait_for_nearby_mario();
    204             break;
    205 
    206         case MIPS_ACT_FOLLOW_PATH:
    207             bhv_mips_act_follow_path();
    208             break;
    209 
    210         case MIPS_ACT_WAIT_FOR_ANIMATION_DONE:
    211             bhv_mips_act_wait_for_animation_done();
    212             break;
    213 
    214         case MIPS_ACT_FALL_DOWN:
    215             bhv_mips_act_fall_down();
    216             break;
    217 
    218         case MIPS_ACT_IDLE:
    219             bhv_mips_act_idle();
    220             break;
    221     }
    222 }
    223 
    224 /**
    225  * Handles MIPS being held by Mario.
    226  */
    227 void bhv_mips_held(void) {
    228     s16 dialogID;
    229 
    230     o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE;
    231     cur_obj_init_animation(4); // Held animation.
    232     cur_obj_set_pos_relative(gMarioObject, 0, 60.0f, 100.0f);
    233     cur_obj_become_intangible();
    234 
    235     // If MIPS hasn't spawned his star yet...
    236     if (o->oMipsStarStatus == MIPS_STAR_STATUS_HAVENT_SPAWNED_STAR) {
    237         // Choose dialog based on which MIPS encounter this is.
    238         if (o->oBhvParams2ndByte == MIPS_BP_15_STARS) {
    239             dialogID = DIALOG_084;
    240         } else { // MIPS_BP_50_STARS
    241             dialogID = DIALOG_162;
    242         }
    243 
    244         if (set_mario_npc_dialog(MARIO_DIALOG_LOOK_FRONT) == MARIO_DIALOG_STATUS_SPEAK) {
    245             o->activeFlags |= ACTIVE_FLAG_INITIATED_TIME_STOP;
    246             if (cutscene_object_with_dialog(CUTSCENE_DIALOG, o, dialogID) != 0) {
    247                 o->oInteractionSubtype |= INT_SUBTYPE_DROP_IMMEDIATELY;
    248                 o->activeFlags &= ~ACTIVE_FLAG_INITIATED_TIME_STOP;
    249                 o->oMipsStarStatus = MIPS_STAR_STATUS_SHOULD_SPAWN_STAR;
    250                 set_mario_npc_dialog(MARIO_DIALOG_STOP);
    251             }
    252         }
    253     }
    254 }
    255 
    256 /**
    257  * Handles MIPS being dropped by Mario.
    258  */
    259 void bhv_mips_dropped(void) {
    260     cur_obj_get_dropped();
    261     o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
    262     cur_obj_init_animation(0);
    263     o->oHeldState = HELD_FREE;
    264     cur_obj_become_tangible();
    265     o->oForwardVel = 3.0f;
    266     o->oAction = MIPS_ACT_IDLE;
    267 }
    268 
    269 /**
    270  * Handles MIPS being thrown by Mario.
    271  */
    272 void bhv_mips_thrown(void) {
    273     cur_obj_enable_rendering_2();
    274     o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
    275     o->oHeldState = HELD_FREE;
    276     o->oFlags &= ~OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW;
    277     cur_obj_init_animation(2);
    278     cur_obj_become_tangible();
    279     o->oForwardVel = 25.0f;
    280     o->oVelY = 20.0f;
    281     o->oAction = MIPS_ACT_FALL_DOWN;
    282 }
    283 
    284 /**
    285  * MIPS' main loop.
    286  */
    287 void bhv_mips_loop(void) {
    288     // Determine what to do based on MIPS' held status.
    289     switch (o->oHeldState) {
    290         case HELD_FREE:
    291             bhv_mips_free();
    292             break;
    293 
    294         case HELD_HELD:
    295             bhv_mips_held();
    296             break;
    297 
    298         case HELD_THROWN:
    299             bhv_mips_thrown();
    300             break;
    301 
    302         case HELD_DROPPED:
    303             bhv_mips_dropped();
    304             break;
    305     }
    306 }