Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/interaction.c
7858 views
1
#include <PR/ultratypes.h>
2
3
#include "area.h"
4
#include "actors/common1.h"
5
#include "audio/external.h"
6
#include "behavior_actions.h"
7
#include "behavior_data.h"
8
#include "camera.h"
9
#include "course_table.h"
10
#include "dialog_ids.h"
11
#include "engine/math_util.h"
12
#include "engine/surface_collision.h"
13
#include "game_init.h"
14
#include "interaction.h"
15
#include "level_update.h"
16
#include "mario.h"
17
#include "mario_step.h"
18
#include "memory.h"
19
#include "obj_behaviors.h"
20
#include "object_helpers.h"
21
#include "save_file.h"
22
#include "seq_ids.h"
23
#include "sm64.h"
24
#include "sound_init.h"
25
#include "rumble_init.h"
26
27
#include "ingame_menu.h"
28
29
#include "settings.h"
30
31
#define INT_GROUND_POUND_OR_TWIRL (1 << 0) // 0x01
32
#define INT_PUNCH (1 << 1) // 0x02
33
#define INT_KICK (1 << 2) // 0x04
34
#define INT_TRIP (1 << 3) // 0x08
35
#define INT_SLIDE_KICK (1 << 4) // 0x10
36
#define INT_FAST_ATTACK_OR_SHELL (1 << 5) // 0x20
37
#define INT_HIT_FROM_ABOVE (1 << 6) // 0x40
38
#define INT_HIT_FROM_BELOW (1 << 7) // 0x80
39
40
#define INT_ATTACK_NOT_FROM_BELOW \
41
(INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \
42
| INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE)
43
44
#define INT_ANY_ATTACK \
45
(INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_SLIDE_KICK \
46
| INT_FAST_ATTACK_OR_SHELL | INT_HIT_FROM_ABOVE | INT_HIT_FROM_BELOW)
47
48
#define INT_ATTACK_NOT_WEAK_FROM_ABOVE \
49
(INT_GROUND_POUND_OR_TWIRL | INT_PUNCH | INT_KICK | INT_TRIP | INT_HIT_FROM_BELOW)
50
51
u8 sDelayInvincTimer;
52
s16 sInvulnerable;
53
u32 interact_coin(struct MarioState *, u32, struct Object *);
54
u32 interact_water_ring(struct MarioState *, u32, struct Object *);
55
u32 interact_star_or_key(struct MarioState *, u32, struct Object *);
56
u32 interact_bbh_entrance(struct MarioState *, u32, struct Object *);
57
u32 interact_warp(struct MarioState *, u32, struct Object *);
58
u32 interact_warp_door(struct MarioState *, u32, struct Object *);
59
u32 interact_door(struct MarioState *, u32, struct Object *);
60
u32 interact_cannon_base(struct MarioState *, u32, struct Object *);
61
u32 interact_igloo_barrier(struct MarioState *, u32, struct Object *);
62
u32 interact_tornado(struct MarioState *, u32, struct Object *);
63
u32 interact_whirlpool(struct MarioState *, u32, struct Object *);
64
u32 interact_strong_wind(struct MarioState *, u32, struct Object *);
65
u32 interact_flame(struct MarioState *, u32, struct Object *);
66
u32 interact_snufit_bullet(struct MarioState *, u32, struct Object *);
67
u32 interact_clam_or_bubba(struct MarioState *, u32, struct Object *);
68
u32 interact_bully(struct MarioState *, u32, struct Object *);
69
u32 interact_shock(struct MarioState *, u32, struct Object *);
70
u32 interact_mr_blizzard(struct MarioState *, u32, struct Object *);
71
u32 interact_hit_from_below(struct MarioState *, u32, struct Object *);
72
u32 interact_bounce_top(struct MarioState *, u32, struct Object *);
73
u32 interact_unknown_08(struct MarioState *, u32, struct Object *);
74
u32 interact_damage(struct MarioState *, u32, struct Object *);
75
u32 interact_breakable(struct MarioState *, u32, struct Object *);
76
u32 interact_koopa_shell(struct MarioState *, u32, struct Object *);
77
u32 interact_pole(struct MarioState *, u32, struct Object *);
78
u32 interact_hoot(struct MarioState *, u32, struct Object *);
79
u32 interact_cap(struct MarioState *, u32, struct Object *);
80
u32 interact_grabbable(struct MarioState *, u32, struct Object *);
81
u32 interact_text(struct MarioState *, u32, struct Object *);
82
83
struct InteractionHandler {
84
u32 interactType;
85
u32 (*handler)(struct MarioState *, u32, struct Object *);
86
};
87
88
static struct InteractionHandler sInteractionHandlers[] = {
89
{ INTERACT_COIN, interact_coin },
90
{ INTERACT_WATER_RING, interact_water_ring },
91
{ INTERACT_STAR_OR_KEY, interact_star_or_key },
92
{ INTERACT_BBH_ENTRANCE, interact_bbh_entrance },
93
{ INTERACT_WARP, interact_warp },
94
{ INTERACT_WARP_DOOR, interact_warp_door },
95
{ INTERACT_DOOR, interact_door },
96
{ INTERACT_CANNON_BASE, interact_cannon_base },
97
{ INTERACT_IGLOO_BARRIER, interact_igloo_barrier },
98
{ INTERACT_TORNADO, interact_tornado },
99
{ INTERACT_WHIRLPOOL, interact_whirlpool },
100
{ INTERACT_STRONG_WIND, interact_strong_wind },
101
{ INTERACT_FLAME, interact_flame },
102
{ INTERACT_SNUFIT_BULLET, interact_snufit_bullet },
103
{ INTERACT_CLAM_OR_BUBBA, interact_clam_or_bubba },
104
{ INTERACT_BULLY, interact_bully },
105
{ INTERACT_SHOCK, interact_shock },
106
{ INTERACT_BOUNCE_TOP2, interact_bounce_top },
107
{ INTERACT_MR_BLIZZARD, interact_mr_blizzard },
108
{ INTERACT_HIT_FROM_BELOW, interact_hit_from_below },
109
{ INTERACT_BOUNCE_TOP, interact_bounce_top },
110
{ INTERACT_DAMAGE, interact_damage },
111
{ INTERACT_POLE, interact_pole },
112
{ INTERACT_HOOT, interact_hoot },
113
{ INTERACT_BREAKABLE, interact_breakable },
114
{ INTERACT_KOOPA, interact_bounce_top },
115
{ INTERACT_KOOPA_SHELL, interact_koopa_shell },
116
{ INTERACT_UNKNOWN_08, interact_unknown_08 },
117
{ INTERACT_CAP, interact_cap },
118
{ INTERACT_GRABBABLE, interact_grabbable },
119
{ INTERACT_TEXT, interact_text },
120
};
121
122
static u32 sForwardKnockbackActions[][3] = {
123
{ ACT_SOFT_FORWARD_GROUND_KB, ACT_FORWARD_GROUND_KB, ACT_HARD_FORWARD_GROUND_KB },
124
{ ACT_FORWARD_AIR_KB, ACT_FORWARD_AIR_KB, ACT_HARD_FORWARD_AIR_KB },
125
{ ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB, ACT_FORWARD_WATER_KB },
126
};
127
128
static u32 sBackwardKnockbackActions[][3] = {
129
{ ACT_SOFT_BACKWARD_GROUND_KB, ACT_BACKWARD_GROUND_KB, ACT_HARD_BACKWARD_GROUND_KB },
130
{ ACT_BACKWARD_AIR_KB, ACT_BACKWARD_AIR_KB, ACT_HARD_BACKWARD_AIR_KB },
131
{ ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB, ACT_BACKWARD_WATER_KB },
132
};
133
134
static u8 sDisplayingDoorText = FALSE;
135
static u8 sJustTeleported = FALSE;
136
static u8 sPssSlideStarted = FALSE;
137
138
/**
139
* Returns the type of cap Mario is wearing.
140
*/
141
u32 get_mario_cap_flag(struct Object *capObject) {
142
const BehaviorScript *script = virtual_to_segmented(0x13, capObject->behavior);
143
144
if (script == bhvNormalCap) {
145
return MARIO_NORMAL_CAP;
146
} else if (script == bhvMetalCap) {
147
return MARIO_METAL_CAP;
148
} else if (script == bhvWingCap) {
149
return MARIO_WING_CAP;
150
} else if (script == bhvVanishCap) {
151
return MARIO_VANISH_CAP;
152
}
153
154
return 0;
155
}
156
157
/**
158
* Returns true if the passed in object has a moving angle yaw
159
* in the angular range given towards Mario.
160
*/
161
u32 object_facing_mario(struct MarioState *m, struct Object *o, s16 angleRange) {
162
f32 dx = m->pos[0] - o->oPosX;
163
f32 dz = m->pos[2] - o->oPosZ;
164
165
s16 angleToMario = atan2s(dz, dx);
166
s16 dAngle = angleToMario - o->oMoveAngleYaw;
167
168
if (-angleRange <= dAngle && dAngle <= angleRange) {
169
return TRUE;
170
}
171
172
return FALSE;
173
}
174
175
s16 mario_obj_angle_to_object(struct MarioState *m, struct Object *o) {
176
f32 dx = o->oPosX - m->pos[0];
177
f32 dz = o->oPosZ - m->pos[2];
178
179
return atan2s(dz, dx);
180
}
181
182
/**
183
* Determines Mario's interaction with a given object depending on their proximity,
184
* action, speed, and position.
185
*/
186
u32 determine_interaction(struct MarioState *m, struct Object *o) {
187
u32 interaction = 0;
188
u32 action = m->action;
189
190
if (action & ACT_FLAG_ATTACKING) {
191
if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING || action == ACT_JUMP_KICK) {
192
s16 dYawToObject = mario_obj_angle_to_object(m, o) - m->faceAngle[1];
193
194
if (m->flags & MARIO_PUNCHING) {
195
// 120 degrees total, or 60 each way
196
if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) {
197
interaction = INT_PUNCH;
198
}
199
}
200
if (m->flags & MARIO_KICKING) {
201
// 120 degrees total, or 60 each way
202
if (-0x2AAA <= dYawToObject && dYawToObject <= 0x2AAA) {
203
interaction = INT_KICK;
204
}
205
}
206
if (m->flags & MARIO_TRIPPING) {
207
// 180 degrees total, or 90 each way
208
if (-0x4000 <= dYawToObject && dYawToObject <= 0x4000) {
209
interaction = INT_TRIP;
210
}
211
}
212
} else if (action == ACT_GROUND_POUND || action == ACT_TWIRLING) {
213
if (m->vel[1] < 0.0f) {
214
interaction = INT_GROUND_POUND_OR_TWIRL;
215
}
216
} else if (action == ACT_GROUND_POUND_LAND || action == ACT_TWIRL_LAND) {
217
// Neither ground pounding nor twirling change Mario's vertical speed on landing.,
218
// so the speed check is nearly always true (perhaps not if you land while going upwards?)
219
// Additionally, actionState it set on each first thing in their action, so this is
220
// only true prior to the very first frame (i.e. active 1 frame prior to it run).
221
if (m->vel[1] < 0.0f && m->actionState == 0) {
222
interaction = INT_GROUND_POUND_OR_TWIRL;
223
}
224
} else if (action == ACT_SLIDE_KICK || action == ACT_SLIDE_KICK_SLIDE) {
225
interaction = INT_SLIDE_KICK;
226
} else if (action & ACT_FLAG_RIDING_SHELL) {
227
interaction = INT_FAST_ATTACK_OR_SHELL;
228
} else if (m->forwardVel <= -26.0f || 26.0f <= m->forwardVel) {
229
interaction = INT_FAST_ATTACK_OR_SHELL;
230
}
231
}
232
233
// Prior to this, the interaction type could be overwritten. This requires, however,
234
// that the interaction not be set prior. This specifically overrides turning a ground
235
// pound into just a bounce.
236
if (interaction == 0 && (action & ACT_FLAG_AIR)) {
237
if (m->vel[1] < 0.0f) {
238
if (m->pos[1] > o->oPosY) {
239
interaction = INT_HIT_FROM_ABOVE;
240
}
241
} else {
242
if (m->pos[1] < o->oPosY) {
243
interaction = INT_HIT_FROM_BELOW;
244
}
245
}
246
}
247
248
return interaction;
249
}
250
251
/**
252
* Sets the interaction types for INT_STATUS_INTERACTED, INT_STATUS_WAS_ATTACKED
253
*/
254
u32 attack_object(struct Object *o, s32 interaction) {
255
u32 attackType = 0;
256
257
switch (interaction) {
258
case INT_GROUND_POUND_OR_TWIRL:
259
attackType = ATTACK_GROUND_POUND_OR_TWIRL;
260
break;
261
case INT_PUNCH:
262
attackType = ATTACK_PUNCH;
263
break;
264
case INT_KICK:
265
case INT_TRIP:
266
attackType = ATTACK_KICK_OR_TRIP;
267
break;
268
case INT_SLIDE_KICK:
269
case INT_FAST_ATTACK_OR_SHELL:
270
attackType = ATTACK_FAST_ATTACK;
271
break;
272
case INT_HIT_FROM_ABOVE:
273
attackType = ATTACK_FROM_ABOVE;
274
break;
275
case INT_HIT_FROM_BELOW:
276
attackType = ATTACK_FROM_BELOW;
277
break;
278
}
279
280
o->oInteractStatus = attackType + (INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED);
281
return attackType;
282
}
283
284
void mario_stop_riding_object(struct MarioState *m) {
285
if (m->riddenObj != NULL) {
286
m->riddenObj->oInteractStatus = INT_STATUS_STOP_RIDING;
287
stop_shell_music();
288
m->riddenObj = NULL;
289
}
290
}
291
292
void mario_grab_used_object(struct MarioState *m) {
293
if (m->heldObj == NULL) {
294
m->heldObj = m->usedObj;
295
obj_set_held_state(m->heldObj, bhvCarrySomething3);
296
}
297
}
298
299
void mario_drop_held_object(struct MarioState *m) {
300
if (m->heldObj != NULL) {
301
if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) {
302
stop_shell_music();
303
}
304
305
obj_set_held_state(m->heldObj, bhvCarrySomething4);
306
307
// ! When dropping an object instead of throwing it, it will be put at Mario's
308
// y-positon instead of the HOLP's y-position. This fact is often exploited when
309
// cloning objects.
310
m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0];
311
m->heldObj->oPosY = m->pos[1];
312
m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2];
313
314
m->heldObj->oMoveAngleYaw = m->faceAngle[1];
315
316
m->heldObj = NULL;
317
}
318
}
319
320
void mario_throw_held_object(struct MarioState *m) {
321
if (m->heldObj != NULL) {
322
if (m->heldObj->behavior == segmented_to_virtual(bhvKoopaShellUnderwater)) {
323
stop_shell_music();
324
}
325
326
obj_set_held_state(m->heldObj, bhvCarrySomething5);
327
328
m->heldObj->oPosX = m->marioBodyState->heldObjLastPosition[0] + 32.0f * sins(m->faceAngle[1]);
329
m->heldObj->oPosY = m->marioBodyState->heldObjLastPosition[1];
330
m->heldObj->oPosZ = m->marioBodyState->heldObjLastPosition[2] + 32.0f * coss(m->faceAngle[1]);
331
332
m->heldObj->oMoveAngleYaw = m->faceAngle[1];
333
334
m->heldObj = NULL;
335
}
336
}
337
338
void mario_stop_riding_and_holding(struct MarioState *m) {
339
mario_drop_held_object(m);
340
mario_stop_riding_object(m);
341
342
if (m->action == ACT_RIDING_HOOT) {
343
m->usedObj->oInteractStatus = FALSE;
344
m->usedObj->oHootMarioReleaseTime = gGlobalTimer;
345
}
346
}
347
348
u32 does_mario_have_normal_cap_on_head(struct MarioState *m) {
349
return (m->flags & (MARIO_CAPS | MARIO_CAP_ON_HEAD)) == (MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD);
350
}
351
352
void mario_blow_off_cap(struct MarioState *m, f32 capSpeed) {
353
struct Object *capObject;
354
355
if (does_mario_have_normal_cap_on_head(m)) {
356
save_file_set_cap_pos(m->pos[0], m->pos[1], m->pos[2]);
357
358
m->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD);
359
360
capObject = spawn_object(m->marioObj, MODEL_MARIOS_CAP, bhvNormalCap);
361
362
capObject->oPosY += (m->action & ACT_FLAG_SHORT_HITBOX) ? 120.0f : 180.0f;
363
capObject->oForwardVel = capSpeed;
364
capObject->oMoveAngleYaw = (s16)(m->faceAngle[1] + 0x400);
365
366
if (m->forwardVel < 0.0f) {
367
capObject->oMoveAngleYaw = (s16)(capObject->oMoveAngleYaw + 0x8000);
368
}
369
}
370
}
371
372
u32 mario_lose_cap_to_enemy(u32 arg) {
373
u32 wasWearingCap = FALSE;
374
375
if (does_mario_have_normal_cap_on_head(gMarioState)) {
376
save_file_set_flags(arg == 1 ? SAVE_FLAG_CAP_ON_KLEPTO : SAVE_FLAG_CAP_ON_UKIKI);
377
gMarioState->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD);
378
wasWearingCap = TRUE;
379
}
380
381
return wasWearingCap;
382
}
383
384
void mario_retrieve_cap(void) {
385
mario_drop_held_object(gMarioState);
386
save_file_clear_flags(SAVE_FLAG_CAP_ON_KLEPTO | SAVE_FLAG_CAP_ON_UKIKI);
387
gMarioState->flags &= ~MARIO_CAP_ON_HEAD;
388
gMarioState->flags |= MARIO_NORMAL_CAP | MARIO_CAP_IN_HAND;
389
}
390
391
u32 able_to_grab_object(struct MarioState *m, UNUSED struct Object *o) {
392
u32 action = m->action;
393
394
if (action == ACT_DIVE_SLIDE || action == ACT_DIVE) {
395
if (!(o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) {
396
return TRUE;
397
}
398
} else if (action == ACT_PUNCHING || action == ACT_MOVE_PUNCHING) {
399
if (m->actionArg < 2) {
400
return TRUE;
401
}
402
}
403
404
return FALSE;
405
}
406
407
struct Object *mario_get_collided_object(struct MarioState *m, u32 interactType) {
408
s32 i;
409
struct Object *object;
410
411
for (i = 0; i < m->marioObj->numCollidedObjs; i++) {
412
object = m->marioObj->collidedObjs[i];
413
414
if (object->oInteractType == interactType) {
415
return object;
416
}
417
}
418
419
return NULL;
420
}
421
422
u32 mario_check_object_grab(struct MarioState *m) {
423
u32 result = FALSE;
424
const BehaviorScript *script;
425
426
if (m->input & INPUT_INTERACT_OBJ_GRABBABLE) {
427
script = virtual_to_segmented(0x13, m->interactObj->behavior);
428
429
if (script == bhvBowser) {
430
s16 facingDYaw = m->faceAngle[1] - m->interactObj->oMoveAngleYaw;
431
if (facingDYaw >= -0x5555 && facingDYaw <= 0x5555) {
432
m->faceAngle[1] = m->interactObj->oMoveAngleYaw;
433
m->usedObj = m->interactObj;
434
result = set_mario_action(m, ACT_PICKING_UP_BOWSER, 0);
435
}
436
} else {
437
s16 facingDYaw = mario_obj_angle_to_object(m, m->interactObj) - m->faceAngle[1];
438
if (facingDYaw >= -0x2AAA && facingDYaw <= 0x2AAA) {
439
m->usedObj = m->interactObj;
440
441
if (!(m->action & ACT_FLAG_AIR)) {
442
set_mario_action(
443
m, (m->action & ACT_FLAG_DIVING) ? ACT_DIVE_PICKING_UP : ACT_PICKING_UP, 0);
444
}
445
446
result = TRUE;
447
}
448
}
449
}
450
451
return result;
452
}
453
454
u32 bully_knock_back_mario(struct MarioState *mario) {
455
struct BullyCollisionData marioData;
456
struct BullyCollisionData bullyData;
457
s16 newMarioYaw;
458
s16 newBullyYaw;
459
s16 marioDYaw;
460
UNUSED s16 bullyDYaw;
461
462
u32 bonkAction = 0;
463
464
struct Object *bully = mario->interactObj;
465
466
//! Conversion ratios multiply to more than 1 (could allow unbounded speed
467
// with bonk cancel - but this isn't important for regular bully battery)
468
f32 bullyToMarioRatio = bully->hitboxRadius * 3 / 53;
469
f32 marioToBullyRatio = 53.0f / bully->hitboxRadius;
470
471
init_bully_collision_data(&marioData, mario->pos[0], mario->pos[2], mario->forwardVel,
472
mario->faceAngle[1], bullyToMarioRatio, 52.0f);
473
474
init_bully_collision_data(&bullyData, bully->oPosX, bully->oPosZ, bully->oForwardVel,
475
bully->oMoveAngleYaw, marioToBullyRatio, bully->hitboxRadius + 2.0f);
476
477
if (mario->forwardVel != 0.0f) {
478
transfer_bully_speed(&marioData, &bullyData);
479
} else {
480
transfer_bully_speed(&bullyData, &marioData);
481
}
482
483
newMarioYaw = atan2s(marioData.velZ, marioData.velX);
484
newBullyYaw = atan2s(bullyData.velZ, bullyData.velX);
485
486
marioDYaw = newMarioYaw - mario->faceAngle[1];
487
bullyDYaw = newBullyYaw - bully->oMoveAngleYaw;
488
489
mario->faceAngle[1] = newMarioYaw;
490
mario->forwardVel = sqrtf(marioData.velX * marioData.velX + marioData.velZ * marioData.velZ);
491
mario->pos[0] = marioData.posX;
492
mario->pos[2] = marioData.posZ;
493
494
bully->oMoveAngleYaw = newBullyYaw;
495
bully->oForwardVel = sqrtf(bullyData.velX * bullyData.velX + bullyData.velZ * bullyData.velZ);
496
bully->oPosX = bullyData.posX;
497
bully->oPosZ = bullyData.posZ;
498
499
if (marioDYaw < -0x4000 || marioDYaw > 0x4000) {
500
mario->faceAngle[1] += 0x8000;
501
mario->forwardVel *= -1.0f;
502
503
if (mario->action & ACT_FLAG_AIR) {
504
bonkAction = ACT_BACKWARD_AIR_KB;
505
} else {
506
bonkAction = ACT_SOFT_BACKWARD_GROUND_KB;
507
}
508
} else {
509
if (mario->action & ACT_FLAG_AIR) {
510
bonkAction = ACT_FORWARD_AIR_KB;
511
} else {
512
bonkAction = ACT_SOFT_FORWARD_GROUND_KB;
513
}
514
}
515
516
return bonkAction;
517
}
518
519
void bounce_off_object(struct MarioState *m, struct Object *o, f32 velY) {
520
m->pos[1] = o->oPosY + o->hitboxHeight;
521
m->vel[1] = velY;
522
523
m->flags &= ~MARIO_UNKNOWN_08;
524
525
play_sound(SOUND_ACTION_BOUNCE_OFF_OBJECT, m->marioObj->header.gfx.cameraToObject);
526
}
527
528
void hit_object_from_below(struct MarioState *m, UNUSED struct Object *o) {
529
m->vel[1] = 0.0f;
530
set_camera_shake_from_hit(SHAKE_HIT_FROM_BELOW);
531
}
532
533
UNUSED static u32 unused_determine_knockback_action(struct MarioState *m) {
534
u32 bonkAction;
535
s16 angleToObject = mario_obj_angle_to_object(m, m->interactObj);
536
s16 facingDYaw = angleToObject - m->faceAngle[1];
537
538
if (m->forwardVel < 16.0f) {
539
m->forwardVel = 16.0f;
540
}
541
542
m->faceAngle[1] = angleToObject;
543
544
if (facingDYaw >= -0x4000 && facingDYaw <= 0x4000) {
545
m->forwardVel *= -1.0f;
546
if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) {
547
bonkAction = ACT_BACKWARD_AIR_KB;
548
} else {
549
bonkAction = ACT_SOFT_BACKWARD_GROUND_KB;
550
}
551
} else {
552
m->faceAngle[1] += 0x8000;
553
if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) {
554
bonkAction = ACT_FORWARD_AIR_KB;
555
} else {
556
bonkAction = ACT_SOFT_FORWARD_GROUND_KB;
557
}
558
}
559
560
return bonkAction;
561
}
562
563
u32 determine_knockback_action(struct MarioState *m, UNUSED s32 arg) {
564
u32 bonkAction;
565
566
s16 terrainIndex = 0; // 1 = air, 2 = water, 0 = default
567
s16 strengthIndex = 0;
568
569
s16 angleToObject = mario_obj_angle_to_object(m, m->interactObj);
570
s16 facingDYaw = angleToObject - m->faceAngle[1];
571
s16 remainingHealth = m->health - 0x40 * m->hurtCounter;
572
573
if (m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
574
terrainIndex = 2;
575
} else if (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) {
576
terrainIndex = 1;
577
}
578
579
if (remainingHealth < 0x100) {
580
strengthIndex = 2;
581
} else if (m->interactObj->oDamageOrCoinValue >= 4) {
582
strengthIndex = 2;
583
} else if (m->interactObj->oDamageOrCoinValue >= 2) {
584
strengthIndex = 1;
585
}
586
587
m->faceAngle[1] = angleToObject;
588
589
if (terrainIndex == 2) {
590
if (m->forwardVel < 28.0f) {
591
mario_set_forward_vel(m, 28.0f);
592
}
593
594
if (m->pos[1] >= m->interactObj->oPosY) {
595
if (m->vel[1] < 20.0f) {
596
m->vel[1] = 20.0f;
597
}
598
} else {
599
if (m->vel[1] > 0.0f) {
600
m->vel[1] = 0.0f;
601
}
602
}
603
} else {
604
if (m->forwardVel < 16.0f) {
605
mario_set_forward_vel(m, 16.0f);
606
}
607
}
608
609
if (-0x4000 <= facingDYaw && facingDYaw <= 0x4000) {
610
m->forwardVel *= -1.0f;
611
bonkAction = sBackwardKnockbackActions[terrainIndex][strengthIndex];
612
} else {
613
m->faceAngle[1] += 0x8000;
614
bonkAction = sForwardKnockbackActions[terrainIndex][strengthIndex];
615
}
616
617
return bonkAction;
618
}
619
620
void push_mario_out_of_object(struct MarioState *m, struct Object *o, f32 padding) {
621
f32 minDistance = o->hitboxRadius + m->marioObj->hitboxRadius + padding;
622
623
f32 offsetX = m->pos[0] - o->oPosX;
624
f32 offsetZ = m->pos[2] - o->oPosZ;
625
f32 distance = sqrtf(offsetX * offsetX + offsetZ * offsetZ);
626
627
if (distance < minDistance) {
628
struct Surface *floor;
629
s16 pushAngle;
630
f32 newMarioX;
631
f32 newMarioZ;
632
633
if (distance == 0.0f) {
634
pushAngle = m->faceAngle[1];
635
} else {
636
pushAngle = atan2s(offsetZ, offsetX);
637
}
638
639
newMarioX = o->oPosX + minDistance * sins(pushAngle);
640
newMarioZ = o->oPosZ + minDistance * coss(pushAngle);
641
642
f32_find_wall_collision(&newMarioX, &m->pos[1], &newMarioZ, 60.0f, 50.0f);
643
644
find_floor(newMarioX, m->pos[1], newMarioZ, &floor);
645
if (floor != NULL) {
646
//! Doesn't update Mario's referenced floor (allows oob death when
647
// an object pushes you into a steep slope while in a ground action)
648
m->pos[0] = newMarioX;
649
m->pos[2] = newMarioZ;
650
}
651
}
652
}
653
654
void bounce_back_from_attack(struct MarioState *m, u32 interaction) {
655
if (interaction & (INT_PUNCH | INT_KICK | INT_TRIP)) {
656
if (m->action == ACT_PUNCHING) {
657
m->action = ACT_MOVE_PUNCHING;
658
}
659
660
if (m->action & ACT_FLAG_AIR) {
661
mario_set_forward_vel(m, -16.0f);
662
} else {
663
mario_set_forward_vel(m, -48.0f);
664
}
665
666
set_camera_shake_from_hit(SHAKE_ATTACK);
667
m->particleFlags |= PARTICLE_TRIANGLE;
668
}
669
670
if (interaction & (INT_PUNCH | INT_KICK | INT_TRIP | INT_FAST_ATTACK_OR_SHELL)) {
671
play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject);
672
}
673
}
674
675
u32 should_push_or_pull_door(struct MarioState *m, struct Object *o) {
676
f32 dx = o->oPosX - m->pos[0];
677
f32 dz = o->oPosZ - m->pos[2];
678
679
s16 dYaw = o->oMoveAngleYaw - atan2s(dz, dx);
680
681
return (dYaw >= -0x4000 && dYaw <= 0x4000) ? 0x00000001 : 0x00000002;
682
}
683
684
u32 take_damage_from_interact_object(struct MarioState *m) {
685
s32 shake;
686
s32 damage = m->interactObj->oDamageOrCoinValue;
687
688
if (damage >= 4) {
689
shake = SHAKE_LARGE_DAMAGE;
690
} else if (damage >= 2) {
691
shake = SHAKE_MED_DAMAGE;
692
} else {
693
shake = SHAKE_SMALL_DAMAGE;
694
}
695
696
if (!(m->flags & MARIO_CAP_ON_HEAD)) {
697
damage += (damage + 1) / 2;
698
}
699
700
if (m->flags & MARIO_METAL_CAP) {
701
damage = 0;
702
}
703
704
m->hurtCounter += 4 * damage;
705
706
#if ENABLE_RUMBLE
707
queue_rumble_data(5, 80);
708
#endif
709
set_camera_shake_from_hit(shake);
710
711
return damage;
712
}
713
714
u32 take_damage_and_knock_back(struct MarioState *m, struct Object *o) {
715
u32 damage;
716
717
if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP) && (!mario_has_improved_metal_cap(m))
718
&& !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
719
o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO;
720
m->interactObj = o;
721
722
damage = take_damage_from_interact_object(m);
723
724
if (o->oInteractionSubtype & INT_SUBTYPE_BIG_KNOCKBACK) {
725
m->forwardVel = 40.0f;
726
}
727
728
if (o->oDamageOrCoinValue > 0) {
729
play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject);
730
}
731
732
update_mario_sound_and_camera(m);
733
return drop_and_set_mario_action(m, determine_knockback_action(m, o->oDamageOrCoinValue),
734
damage);
735
}
736
737
return FALSE;
738
}
739
740
void reset_mario_pitch(struct MarioState *m) {
741
if (m->action == ACT_WATER_JUMP || m->action == ACT_SHOT_FROM_CANNON || m->action == ACT_FLYING) {
742
set_camera_mode(m->area->camera, m->area->camera->defMode, 1);
743
m->faceAngle[0] = 0;
744
}
745
}
746
747
u32 interact_coin(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
748
m->numCoins += o->oDamageOrCoinValue;
749
if (!configNoHealingMode)
750
m->healCounter += 4 * o->oDamageOrCoinValue;
751
752
o->oInteractStatus = INT_STATUS_INTERACTED;
753
754
if (COURSE_IS_MAIN_COURSE(gCurrCourseNum) && m->numCoins - o->oDamageOrCoinValue < configCoinStarCoins
755
&& m->numCoins >= configCoinStarCoins) {
756
bhv_spawn_star_no_level_exit(6);
757
}
758
#if ENABLE_RUMBLE
759
if (o->oDamageOrCoinValue >= 2) {
760
queue_rumble_data(5, 80);
761
}
762
#endif
763
764
return FALSE;
765
}
766
767
u32 interact_water_ring(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
768
if (!configNoHealingMode)
769
m->healCounter += 4 * o->oDamageOrCoinValue;
770
o->oInteractStatus = INT_STATUS_INTERACTED;
771
return FALSE;
772
}
773
774
void remain_mod_spawn_objects(struct Object *o)
775
{
776
if ((gCurrCourseNum == COURSE_BOB) && (gLastCompletedStarNum == 1))
777
{
778
struct Object *objToDelete;
779
780
// Delete Cannon + Barrel at start of level
781
objToDelete = find_nearest_object_with_behavior(bhvCannonBarrelBubbles, -5694, 128, 5600);
782
if (objToDelete != NULL)
783
{
784
obj_mark_for_deletion(objToDelete->parentObj); // bhvWaterBombCannon
785
obj_mark_for_deletion(objToDelete);
786
}
787
788
// Delete both Bob-Omb Buddies
789
for (int i = 0; i < 2; i++)
790
{
791
objToDelete = cur_obj_nearest_object_with_behavior(bhvBobombBuddy);
792
if (objToDelete != NULL)
793
{
794
obj_mark_for_deletion(objToDelete);
795
}
796
}
797
798
// Delete both Bowling Balls in Pit
799
for (int i = 0; i < 2; i++)
800
{
801
objToDelete = cur_obj_nearest_object_with_behavior(bhvPitBowlingBall);
802
if (objToDelete != NULL)
803
{
804
obj_mark_for_deletion(objToDelete);
805
}
806
}
807
808
// Delete existing BowlingBallSpawners - NOTE: This usually happens in Act 3
809
for (int i = 0; i < 2; i++)
810
{
811
objToDelete = cur_obj_nearest_object_with_behavior(bhvBobBowlingBallSpawner);
812
813
if (objToDelete != NULL)
814
{
815
obj_mark_for_deletion(objToDelete);
816
}
817
}
818
819
// Spawn faster BowlingBallSpawners + small koopa - NOTE: This usually happens in Act 3
820
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvTtmBowlingBallSpawner, 0x00000000, 1535, 3840, -5561, 0, 0, 0);
821
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvTtmBowlingBallSpawner, 0x00020000, 524, 2825, -5400, 0, 0, 0);
822
spawn_object_abs_with_rot_degrees(o, 0, MODEL_KOOPA_WITH_SHELL, bhvKoopa, 0x00010000, 3400, 770, 6500, 0, 0, 0);
823
824
// Spawn Act 2 Objects
825
spawn_object_abs_with_rot_degrees(o, 0, MODEL_BOWLING_BALL, bhvPitBowlingBall, 0x00000000, -993, 886, -3565, 0, 0, 0);
826
spawn_object_abs_with_rot_degrees(o, 0, MODEL_BOWLING_BALL, bhvPitBowlingBall, 0x00000000, -785, 886, -4301, 0, 0, 0);
827
spawn_object_abs_with_rot_degrees(o, 0, MODEL_BOWLING_BALL, bhvPitBowlingBall, 0x00000000, -93, 886, -3414, 0, 0, 0);
828
spawn_object_abs_with_rot_degrees(o, 0, MODEL_DL_CANNON_LID, bhvCannonClosed, 0x00000000, -5694, 128, 5600, 0, 180, 0);
829
spawn_object_abs_with_rot_degrees(o, 0, MODEL_KOOPA_WITH_SHELL, bhvKoopa, 0x01020000, -4004, 0, 5221, 0, 0, 0);
830
spawn_object_abs_with_rot_degrees(o, 0, MODEL_BOBOMB_BUDDY, bhvBobombBuddyOpensCannon, 0x00000000, -5723, 140, 6017, 0, 0, 0);
831
spawn_object_abs_with_rot_degrees(o, 0, MODEL_BOBOMB_BUDDY, bhvBobombBuddy, DIALOG_003 << 16, -6250, 0, 6680, 0, 0, 0);
832
}
833
if ((gCurrCourseNum == COURSE_WF) && (gLastCompletedStarNum == 1))
834
{
835
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvFadingWarp, 0x000E0000, 180, 3584, 340, 0, 0, 0);
836
}
837
if ((gCurrCourseNum == COURSE_JRB) && (gLastCompletedStarNum == 1))
838
{
839
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvFadingWarp, 0x000C0000, 0, 1258, 3000, 0, 0, 0);
840
}
841
if ((gCurrCourseNum == COURSE_BBH) && (gLastCompletedStarNum == 1))
842
{
843
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvFlamethrower, 0x00030000, 990, -2146, -908, 0, -45, 0);
844
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvMerryGoRoundBooManager, 0x01000000, -1100, -2372, 1100, 0, 135, 0);
845
}
846
if ((gCurrCourseNum == COURSE_LLL) && (gLastCompletedStarNum == 5))
847
{
848
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvFadingWarp, 0x000F0000, 2520, 3591, -910, 0, 0, 0);
849
}
850
if ((gCurrCourseNum == COURSE_LLL) && (gLastCompletedStarNum == 6))
851
{
852
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvFadingWarp, 0x00200000, 1805, 3232, 1440, 0, 0, 0);
853
}
854
if ((gCurrCourseNum == COURSE_SSL) && (gLastCompletedStarNum == 4))
855
{
856
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvFadingWarp, 0x00230000, 0, -1176, -3700, 0, 0, 0);
857
}
858
if ((gCurrCourseNum == COURSE_DDD) && (gLastCompletedStarNum == 1) && (configBowsersSub))
859
{
860
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvFadingWarp, 0x000B0000, 3900, 571, -600, 0, 0, 0);
861
}
862
if ((gCurrCourseNum == COURSE_SL) && (gLastCompletedStarNum == 3))
863
{
864
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvFadingWarp, 0x000F0000, 4350, 1229, 4350, 0, 0, 0);
865
}
866
if ((gCurrCourseNum == COURSE_THI) && (gLastCompletedStarNum == 6))
867
{
868
spawn_object_abs_with_rot_degrees(o, 0, MODEL_NONE, bhvFadingWarp, 0x000F0000, 0, 1843, 0, 0, 0, 0);
869
}
870
}
871
872
u32 interact_star_or_key(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
873
u32 starIndex;
874
u32 starGrabAction = ACT_STAR_DANCE_EXIT;
875
u32 noExit = (o->oInteractionSubtype & INT_SUBTYPE_NO_EXIT) != 0;
876
u32 grandStar = (o->oInteractionSubtype & INT_SUBTYPE_GRAND_STAR) != 0;
877
878
starIndex = (o->oBehParams >> 24) & 0x1F;
879
gCollectedStar = starIndex;
880
881
if (m->health >= 0x100) {
882
883
// Don't kick Mario if staying in levels is active
884
if (stay_in_level()) {
885
noExit = 1;
886
887
// Increase the act number
888
if (gCurrActNum-1 == gCollectedStar && gCurrActNum != 6) {
889
gCurrActNum++;
890
gDialogCourseActNum++;
891
while ((save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1) & 1 << (gCurrActNum-1)) && gCurrActNum != 6) {
892
gCurrActNum++;
893
gDialogCourseActNum++;
894
}
895
}
896
}
897
898
mario_stop_riding_and_holding(m);
899
#if ENABLE_RUMBLE
900
queue_rumble_data(5, 80);
901
#endif
902
903
if (!noExit) {
904
m->hurtCounter = 0;
905
m->healCounter = 0;
906
if (m->capTimer > 1) {
907
m->capTimer = 1;
908
}
909
}
910
911
// Make the stars heal Mario when this setting is on.
912
if (configStayInCourse) {
913
gMarioState->healCounter += 31.75;
914
if (gMarioState->healCounter > 31.75)
915
gMarioState->healCounter = 31.75;
916
}
917
918
if (noExit) {
919
starGrabAction = ACT_STAR_DANCE_NO_EXIT;
920
}
921
922
if (m->action & ACT_FLAG_SWIMMING) {
923
starGrabAction = ACT_STAR_DANCE_WATER;
924
}
925
926
if (m->action & ACT_FLAG_METAL_WATER) {
927
starGrabAction = ACT_STAR_DANCE_WATER;
928
}
929
930
if (m->action & ACT_FLAG_AIR) {
931
starGrabAction = ACT_FALL_AFTER_STAR_GRAB;
932
}
933
934
spawn_object(o, MODEL_NONE, bhvStarKeyCollectionPuffSpawner);
935
936
o->oInteractStatus = INT_STATUS_INTERACTED;
937
m->interactObj = o;
938
m->usedObj = o;
939
940
save_file_collect_star_or_key(m->numCoins, starIndex);
941
942
m->numStars =
943
save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1);
944
945
if (!noExit) {
946
drop_queued_background_music();
947
fadeout_level_music(126);
948
}
949
950
play_sound(SOUND_MENU_STAR_SOUND, m->marioObj->header.gfx.cameraToObject);
951
#ifndef VERSION_JP
952
update_mario_sound_and_camera(m);
953
#endif
954
955
if (configStayInCourse == 2)
956
{
957
remain_mod_spawn_objects(o);
958
}
959
960
if (grandStar) {
961
return set_mario_action(m, ACT_JUMBO_STAR_CUTSCENE, 0);
962
}
963
964
return set_mario_action(m, starGrabAction, noExit + 2 * grandStar);
965
}
966
967
return FALSE;
968
}
969
970
u32 interact_bbh_entrance(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
971
if (m->action != ACT_BBH_ENTER_SPIN && m->action != ACT_BBH_ENTER_JUMP) {
972
mario_stop_riding_and_holding(m);
973
974
o->oInteractStatus = INT_STATUS_INTERACTED;
975
m->interactObj = o;
976
m->usedObj = o;
977
978
if (m->action & ACT_FLAG_AIR) {
979
return set_mario_action(m, ACT_BBH_ENTER_SPIN, 0);
980
}
981
982
return set_mario_action(m, ACT_BBH_ENTER_JUMP, 0);
983
}
984
985
return FALSE;
986
}
987
988
u32 interact_warp(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
989
u32 action;
990
991
if (o->oInteractionSubtype & INT_SUBTYPE_FADING_WARP) {
992
action = m->action;
993
994
if (action == ACT_TELEPORT_FADE_IN) {
995
sJustTeleported = TRUE;
996
997
} else if (!sJustTeleported) {
998
if (action == ACT_IDLE || action == ACT_PANTING || action == ACT_STANDING_AGAINST_WALL
999
|| action == ACT_CROUCHING) {
1000
m->interactObj = o;
1001
m->usedObj = o;
1002
1003
sJustTeleported = TRUE;
1004
return set_mario_action(m, ACT_TELEPORT_FADE_OUT, 0);
1005
}
1006
}
1007
} else {
1008
if (m->action != ACT_EMERGE_FROM_PIPE) {
1009
o->oInteractStatus = INT_STATUS_INTERACTED;
1010
m->interactObj = o;
1011
m->usedObj = o;
1012
1013
#if ENABLE_RUMBLE
1014
if (o->collisionData == segmented_to_virtual(warp_pipe_seg3_collision_03009AC8)) {
1015
play_sound(SOUND_MENU_ENTER_PIPE, m->marioObj->header.gfx.cameraToObject);
1016
queue_rumble_data(15, 80);
1017
} else {
1018
play_sound(SOUND_MENU_ENTER_HOLE, m->marioObj->header.gfx.cameraToObject);
1019
queue_rumble_data(12, 80);
1020
}
1021
#else
1022
play_sound(o->collisionData == segmented_to_virtual(warp_pipe_seg3_collision_03009AC8)
1023
? SOUND_MENU_ENTER_PIPE
1024
: SOUND_MENU_ENTER_HOLE,
1025
m->marioObj->header.gfx.cameraToObject);
1026
#endif
1027
1028
mario_stop_riding_object(m);
1029
return set_mario_action(m, ACT_DISAPPEARED, (WARP_OP_WARP_OBJECT << 16) + 2);
1030
}
1031
}
1032
1033
return FALSE;
1034
}
1035
1036
u32 interact_warp_door(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1037
u32 doorAction = 0;
1038
u32 saveFlags = save_file_get_flags();
1039
s16 warpDoorId = o->oBehParams >> 24;
1040
u32 actionArg;
1041
1042
if (m->action == ACT_WALKING || m->action == ACT_DECELERATING) {
1043
if (warpDoorId == 1 && !(saveFlags & SAVE_FLAG_UNLOCKED_UPSTAIRS_DOOR)) {
1044
if (!(saveFlags & SAVE_FLAG_HAVE_KEY_2)) {
1045
if (!sDisplayingDoorText) {
1046
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG,
1047
(saveFlags & SAVE_FLAG_HAVE_KEY_1) ? DIALOG_023 : DIALOG_022);
1048
}
1049
sDisplayingDoorText = TRUE;
1050
1051
return FALSE;
1052
}
1053
1054
doorAction = ACT_UNLOCKING_KEY_DOOR;
1055
}
1056
1057
if (warpDoorId == 2 && !(saveFlags & SAVE_FLAG_UNLOCKED_BASEMENT_DOOR)) {
1058
if (!(saveFlags & SAVE_FLAG_HAVE_KEY_1)) {
1059
if (!sDisplayingDoorText) {
1060
// Moat door skip was intended confirmed
1061
set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG,
1062
(saveFlags & SAVE_FLAG_HAVE_KEY_2) ? DIALOG_023 : DIALOG_022);
1063
}
1064
sDisplayingDoorText = TRUE;
1065
1066
return FALSE;
1067
}
1068
1069
doorAction = ACT_UNLOCKING_KEY_DOOR;
1070
}
1071
1072
if (m->action == ACT_WALKING || m->action == ACT_DECELERATING) {
1073
actionArg = should_push_or_pull_door(m, o) + 0x00000004;
1074
1075
if (doorAction == 0) {
1076
if (actionArg & 0x00000001) {
1077
doorAction = ACT_PULLING_DOOR;
1078
} else {
1079
doorAction = ACT_PUSHING_DOOR;
1080
}
1081
}
1082
1083
m->interactObj = o;
1084
m->usedObj = o;
1085
return set_mario_action(m, doorAction, actionArg);
1086
}
1087
}
1088
1089
return FALSE;
1090
}
1091
1092
u32 get_door_save_file_flag(struct Object *door) {
1093
u32 saveFileFlag = 0;
1094
s16 requiredNumStars = door->oBehParams >> 24;
1095
1096
s16 isCcmDoor = door->oPosX < 0.0f;
1097
s16 isPssDoor = door->oPosY > 500.0f;
1098
1099
switch (requiredNumStars) {
1100
case 1:
1101
if (isPssDoor) {
1102
saveFileFlag = SAVE_FLAG_UNLOCKED_PSS_DOOR;
1103
} else {
1104
saveFileFlag = SAVE_FLAG_UNLOCKED_WF_DOOR;
1105
}
1106
break;
1107
1108
case 3:
1109
if (isCcmDoor) {
1110
saveFileFlag = SAVE_FLAG_UNLOCKED_CCM_DOOR;
1111
} else {
1112
saveFileFlag = SAVE_FLAG_UNLOCKED_JRB_DOOR;
1113
}
1114
break;
1115
1116
case 8:
1117
saveFileFlag = SAVE_FLAG_UNLOCKED_BITDW_DOOR;
1118
break;
1119
1120
case 30:
1121
saveFileFlag = SAVE_FLAG_UNLOCKED_BITFS_DOOR;
1122
break;
1123
1124
case 50:
1125
saveFileFlag = SAVE_FLAG_UNLOCKED_50_STAR_DOOR;
1126
break;
1127
}
1128
1129
return saveFileFlag;
1130
}
1131
1132
u32 interact_door(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1133
s16 requiredNumStars = o->oBehParams >> 24;
1134
s16 numStars = save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1);
1135
1136
if (m->action == ACT_WALKING || m->action == ACT_DECELERATING) {
1137
if (numStars >= requiredNumStars) {
1138
u32 actionArg = should_push_or_pull_door(m, o);
1139
u32 enterDoorAction;
1140
u32 doorSaveFileFlag;
1141
1142
if (actionArg & 0x00000001) {
1143
enterDoorAction = ACT_PULLING_DOOR;
1144
} else {
1145
enterDoorAction = ACT_PUSHING_DOOR;
1146
}
1147
1148
doorSaveFileFlag = get_door_save_file_flag(o);
1149
m->interactObj = o;
1150
m->usedObj = o;
1151
1152
if (o->oInteractionSubtype & INT_SUBTYPE_STAR_DOOR) {
1153
enterDoorAction = ACT_ENTERING_STAR_DOOR;
1154
}
1155
1156
if (!configSkipCutscenes && doorSaveFileFlag != 0 && !(save_file_get_flags() & doorSaveFileFlag)) {
1157
enterDoorAction = ACT_UNLOCKING_STAR_DOOR;
1158
}
1159
1160
return set_mario_action(m, enterDoorAction, actionArg);
1161
} else if (!sDisplayingDoorText) {
1162
u32 text = DIALOG_022 << 16;
1163
1164
switch (requiredNumStars) {
1165
case 1:
1166
text = DIALOG_024 << 16;
1167
break;
1168
case 3:
1169
text = DIALOG_025 << 16;
1170
break;
1171
case 8:
1172
text = DIALOG_026 << 16;
1173
break;
1174
case 30:
1175
text = DIALOG_027 << 16;
1176
break;
1177
case 50:
1178
text = DIALOG_028 << 16;
1179
break;
1180
case 70:
1181
text = DIALOG_029 << 16;
1182
break;
1183
}
1184
1185
text += requiredNumStars - numStars;
1186
1187
sDisplayingDoorText = TRUE;
1188
return set_mario_action(m, ACT_READING_AUTOMATIC_DIALOG, text);
1189
}
1190
} else if (m->action == ACT_IDLE && sDisplayingDoorText == TRUE && requiredNumStars == 70) {
1191
m->interactObj = o;
1192
m->usedObj = o;
1193
return set_mario_action(m, ACT_ENTERING_STAR_DOOR, should_push_or_pull_door(m, o));
1194
}
1195
1196
return FALSE;
1197
}
1198
1199
u32 interact_cannon_base(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1200
if (m->action != ACT_IN_CANNON) {
1201
mario_stop_riding_and_holding(m);
1202
o->oInteractStatus = INT_STATUS_INTERACTED;
1203
m->interactObj = o;
1204
m->usedObj = o;
1205
return set_mario_action(m, ACT_IN_CANNON, 0);
1206
}
1207
1208
return FALSE;
1209
}
1210
1211
u32 interact_igloo_barrier(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1212
//! Sets used object without changing action (LOTS of interesting glitches,
1213
// but unfortunately the igloo barrier is the only object with this interaction
1214
// type)
1215
m->interactObj = o;
1216
m->usedObj = o;
1217
push_mario_out_of_object(m, o, 5.0f);
1218
return FALSE;
1219
}
1220
1221
u32 interact_tornado(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1222
struct Object *marioObj = m->marioObj;
1223
1224
if (m->action != ACT_TORNADO_TWIRLING && m->action != ACT_SQUISHED) {
1225
mario_stop_riding_and_holding(m);
1226
mario_set_forward_vel(m, 0.0f);
1227
update_mario_sound_and_camera(m);
1228
1229
o->oInteractStatus = INT_STATUS_INTERACTED;
1230
m->interactObj = o;
1231
m->usedObj = o;
1232
1233
marioObj->oMarioTornadoYawVel = 0x400;
1234
marioObj->oMarioTornadoPosY = m->pos[1] - o->oPosY;
1235
1236
play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject);
1237
#if ENABLE_RUMBLE
1238
queue_rumble_data(30, 60);
1239
#endif
1240
return set_mario_action(m, ACT_TORNADO_TWIRLING, m->action == ACT_TWIRLING);
1241
}
1242
1243
return FALSE;
1244
}
1245
1246
u32 interact_whirlpool(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1247
struct Object *marioObj = m->marioObj;
1248
1249
if (!mario_has_improved_metal_cap(m) && m->action != ACT_CAUGHT_IN_WHIRLPOOL) {
1250
mario_stop_riding_and_holding(m);
1251
o->oInteractStatus = INT_STATUS_INTERACTED;
1252
m->interactObj = o;
1253
m->usedObj = o;
1254
1255
m->forwardVel = 0.0f;
1256
1257
marioObj->oMarioWhirlpoolPosY = m->pos[1] - o->oPosY;
1258
1259
play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject);
1260
#if ENABLE_RUMBLE
1261
queue_rumble_data(30, 60);
1262
#endif
1263
return set_mario_action(m, ACT_CAUGHT_IN_WHIRLPOOL, 0);
1264
}
1265
1266
return FALSE;
1267
}
1268
1269
u32 interact_strong_wind(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1270
UNUSED struct Object *marioObj = m->marioObj;
1271
1272
if (!mario_has_improved_metal_cap(m) && m->action != ACT_GETTING_BLOWN) {
1273
mario_stop_riding_and_holding(m);
1274
o->oInteractStatus = INT_STATUS_INTERACTED;
1275
m->interactObj = o;
1276
m->usedObj = o;
1277
1278
m->faceAngle[1] = o->oMoveAngleYaw + 0x8000;
1279
m->unkC4 = 0.4f;
1280
m->forwardVel = -24.0f;
1281
m->vel[1] = 12.0f;
1282
1283
play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject);
1284
update_mario_sound_and_camera(m);
1285
return set_mario_action(m, ACT_GETTING_BLOWN, 0);
1286
}
1287
1288
return FALSE;
1289
}
1290
1291
u32 interact_flame(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1292
u32 burningAction = ACT_BURNING_JUMP;
1293
1294
if (!sInvulnerable && !(m->flags & MARIO_METAL_CAP) && !(m->flags & MARIO_VANISH_CAP)
1295
&& !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1296
#if ENABLE_RUMBLE
1297
queue_rumble_data(5, 80);
1298
#endif
1299
o->oInteractStatus = INT_STATUS_INTERACTED;
1300
m->interactObj = o;
1301
1302
if ((m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER))
1303
|| m->waterLevel - m->pos[1] > 50.0f) {
1304
play_sound(SOUND_GENERAL_FLAME_OUT, m->marioObj->header.gfx.cameraToObject);
1305
} else {
1306
m->marioObj->oMarioBurnTimer = 0;
1307
update_mario_sound_and_camera(m);
1308
play_sound(SOUND_MARIO_ON_FIRE, m->marioObj->header.gfx.cameraToObject);
1309
1310
if ((m->action & ACT_FLAG_AIR) && m->vel[1] <= 0.0f) {
1311
burningAction = ACT_BURNING_FALL;
1312
}
1313
1314
return drop_and_set_mario_action(m, burningAction, 1);
1315
}
1316
}
1317
1318
return FALSE;
1319
}
1320
1321
u32 interact_snufit_bullet(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1322
if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP)) {
1323
if (m->flags & MARIO_METAL_CAP) {
1324
o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED;
1325
play_sound(SOUND_ACTION_UNKNOWN458, m->marioObj->header.gfx.cameraToObject);
1326
} else {
1327
o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO;
1328
m->interactObj = o;
1329
take_damage_from_interact_object(m);
1330
1331
play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject);
1332
update_mario_sound_and_camera(m);
1333
1334
return drop_and_set_mario_action(m, determine_knockback_action(m, o->oDamageOrCoinValue),
1335
o->oDamageOrCoinValue);
1336
}
1337
}
1338
1339
if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1340
sDelayInvincTimer = TRUE;
1341
}
1342
1343
return FALSE;
1344
}
1345
1346
u32 interact_clam_or_bubba(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1347
if (o->oInteractionSubtype & INT_SUBTYPE_EATS_MARIO) {
1348
o->oInteractStatus = INT_STATUS_INTERACTED;
1349
m->interactObj = o;
1350
return set_mario_action(m, ACT_EATEN_BY_BUBBA, 0);
1351
} else if (!mario_has_improved_metal_cap(m) && take_damage_and_knock_back(m, o)) {
1352
return TRUE;
1353
}
1354
1355
if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1356
sDelayInvincTimer = TRUE;
1357
}
1358
1359
return TRUE;
1360
}
1361
1362
u32 interact_bully(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1363
UNUSED u32 unused;
1364
1365
u32 interaction;
1366
if (m->flags & MARIO_METAL_CAP) {
1367
interaction = INT_FAST_ATTACK_OR_SHELL;
1368
} else {
1369
interaction = determine_interaction(m, o);
1370
}
1371
1372
m->interactObj = o;
1373
1374
if (interaction & INT_ATTACK_NOT_FROM_BELOW) {
1375
#if ENABLE_RUMBLE
1376
queue_rumble_data(5, 80);
1377
#endif
1378
push_mario_out_of_object(m, o, 5.0f);
1379
1380
m->forwardVel = -16.0f;
1381
o->oMoveAngleYaw = m->faceAngle[1];
1382
o->oForwardVel = 3392.0f / o->hitboxRadius;
1383
1384
attack_object(o, interaction);
1385
bounce_back_from_attack(m, interaction);
1386
return TRUE;
1387
}
1388
1389
else if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP)
1390
&& !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1391
o->oInteractStatus = INT_STATUS_INTERACTED;
1392
m->invincTimer = 2;
1393
1394
update_mario_sound_and_camera(m);
1395
play_sound(SOUND_MARIO_EEUH, m->marioObj->header.gfx.cameraToObject);
1396
play_sound(SOUND_OBJ_BULLY_METAL, m->marioObj->header.gfx.cameraToObject);
1397
1398
push_mario_out_of_object(m, o, 5.0f);
1399
drop_and_set_mario_action(m, bully_knock_back_mario(m), 0);
1400
#if ENABLE_RUMBLE
1401
queue_rumble_data(5, 80);
1402
#endif
1403
return TRUE;
1404
}
1405
1406
return FALSE;
1407
}
1408
1409
u32 interact_shock(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1410
if (!sInvulnerable && !(m->flags & MARIO_VANISH_CAP)
1411
&& !(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1412
u32 actionArg = (m->action & (ACT_FLAG_AIR | ACT_FLAG_ON_POLE | ACT_FLAG_HANGING)) == 0;
1413
1414
o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_ATTACKED_MARIO;
1415
m->interactObj = o;
1416
1417
take_damage_from_interact_object(m);
1418
play_sound(SOUND_MARIO_ATTACKED, m->marioObj->header.gfx.cameraToObject);
1419
#if ENABLE_RUMBLE
1420
queue_rumble_data(70, 60);
1421
#endif
1422
1423
if (m->action & (ACT_FLAG_SWIMMING | ACT_FLAG_METAL_WATER)) {
1424
return drop_and_set_mario_action(m, ACT_WATER_SHOCKED, 0);
1425
} else {
1426
update_mario_sound_and_camera(m);
1427
return drop_and_set_mario_action(m, ACT_SHOCKED, actionArg);
1428
}
1429
}
1430
1431
if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1432
sDelayInvincTimer = TRUE;
1433
}
1434
1435
return FALSE;
1436
}
1437
1438
UNUSED static u32 interact_stub(UNUSED struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1439
if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1440
sDelayInvincTimer = TRUE;
1441
}
1442
return FALSE;
1443
}
1444
1445
u32 interact_mr_blizzard(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1446
if (mario_has_improved_metal_cap(m))
1447
return FALSE;
1448
1449
if (take_damage_and_knock_back(m, o)) {
1450
return TRUE;
1451
}
1452
1453
if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1454
sDelayInvincTimer = TRUE;
1455
}
1456
1457
return FALSE;
1458
}
1459
1460
u32 interact_hit_from_below(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1461
UNUSED u32 unused;
1462
1463
u32 interaction;
1464
if (m->flags & MARIO_METAL_CAP) {
1465
interaction = INT_FAST_ATTACK_OR_SHELL;
1466
} else {
1467
interaction = determine_interaction(m, o);
1468
}
1469
1470
if (interaction & INT_ANY_ATTACK) {
1471
#if ENABLE_RUMBLE
1472
queue_rumble_data(5, 80);
1473
#endif
1474
attack_object(o, interaction);
1475
bounce_back_from_attack(m, interaction);
1476
1477
if (interaction & INT_HIT_FROM_BELOW) {
1478
hit_object_from_below(m, o);
1479
}
1480
1481
if (interaction & INT_HIT_FROM_ABOVE) {
1482
if (o->oInteractionSubtype & INT_SUBTYPE_TWIRL_BOUNCE) {
1483
bounce_off_object(m, o, 80.0f);
1484
reset_mario_pitch(m);
1485
#ifndef VERSION_JP
1486
play_sound(SOUND_MARIO_TWIRL_BOUNCE, m->marioObj->header.gfx.cameraToObject);
1487
#endif
1488
return drop_and_set_mario_action(m, ACT_TWIRLING, 0);
1489
} else {
1490
if (configEnemyBouncing)
1491
bounce_off_object(m, o, (m->input & INPUT_A_DOWN) ? 48.0f : 22.0f);
1492
else
1493
bounce_off_object(m, o, 30.0f);
1494
}
1495
}
1496
} else if (take_damage_and_knock_back(m, o)) {
1497
return TRUE;
1498
}
1499
1500
if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1501
sDelayInvincTimer = TRUE;
1502
}
1503
1504
return FALSE;
1505
}
1506
1507
u32 interact_bounce_top(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1508
u32 interaction;
1509
if (m->flags & MARIO_METAL_CAP) {
1510
interaction = INT_FAST_ATTACK_OR_SHELL;
1511
} else {
1512
interaction = determine_interaction(m, o);
1513
}
1514
1515
if (interaction & INT_ATTACK_NOT_FROM_BELOW) {
1516
#if ENABLE_RUMBLE
1517
queue_rumble_data(5, 80);
1518
#endif
1519
attack_object(o, interaction);
1520
bounce_back_from_attack(m, interaction);
1521
1522
if (interaction & INT_HIT_FROM_ABOVE) {
1523
if (o->oInteractionSubtype & INT_SUBTYPE_TWIRL_BOUNCE) {
1524
bounce_off_object(m, o, 80.0f);
1525
reset_mario_pitch(m);
1526
#ifndef VERSION_JP
1527
play_sound(SOUND_MARIO_TWIRL_BOUNCE, m->marioObj->header.gfx.cameraToObject);
1528
#endif
1529
return drop_and_set_mario_action(m, ACT_TWIRLING, 0);
1530
} else {
1531
if (configEnemyBouncing)
1532
bounce_off_object(m, o, (m->input & INPUT_A_DOWN) ? 48.0f : 22.0f);
1533
else
1534
bounce_off_object(m, o, 30.0f);
1535
}
1536
}
1537
} else if (take_damage_and_knock_back(m, o)) {
1538
return TRUE;
1539
}
1540
1541
if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1542
sDelayInvincTimer = TRUE;
1543
}
1544
1545
return FALSE;
1546
}
1547
1548
u32 interact_unknown_08(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1549
u32 interaction = determine_interaction(m, o);
1550
1551
if (interaction & INT_PUNCH) {
1552
o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_WAS_ATTACKED | ATTACK_PUNCH;
1553
bounce_back_from_attack(m, interaction);
1554
} else if (take_damage_and_knock_back(m, o)) {
1555
return TRUE;
1556
}
1557
1558
if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1559
sDelayInvincTimer = TRUE;
1560
}
1561
1562
return FALSE;
1563
}
1564
1565
u32 interact_damage(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1566
if (take_damage_and_knock_back(m, o)) {
1567
return TRUE;
1568
}
1569
1570
if (!(o->oInteractionSubtype & INT_SUBTYPE_DELAY_INVINCIBILITY)) {
1571
sDelayInvincTimer = TRUE;
1572
}
1573
1574
return FALSE;
1575
}
1576
1577
u32 interact_breakable(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1578
u32 interaction = determine_interaction(m, o);
1579
1580
if (interaction & INT_ATTACK_NOT_WEAK_FROM_ABOVE) {
1581
attack_object(o, interaction);
1582
bounce_back_from_attack(m, interaction);
1583
1584
m->interactObj = o;
1585
1586
switch (interaction) {
1587
case INT_HIT_FROM_ABOVE:
1588
bounce_off_object(m, o, 30.0f); //! Not in the 0x8F mask
1589
break;
1590
1591
case INT_HIT_FROM_BELOW:
1592
hit_object_from_below(m, o);
1593
break;
1594
}
1595
1596
return TRUE;
1597
}
1598
1599
return FALSE;
1600
}
1601
1602
u32 interact_koopa_shell(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1603
if (!(m->action & ACT_FLAG_RIDING_SHELL)) {
1604
u32 interaction = determine_interaction(m, o);
1605
1606
if (interaction == INT_HIT_FROM_ABOVE || m->action == ACT_WALKING
1607
|| m->action == ACT_HOLD_WALKING) {
1608
m->interactObj = o;
1609
m->usedObj = o;
1610
m->riddenObj = o;
1611
1612
attack_object(o, interaction);
1613
update_mario_sound_and_camera(m);
1614
play_shell_music();
1615
mario_drop_held_object(m);
1616
1617
//! Puts Mario in ground action even when in air, making it easy to
1618
// escape air actions into crouch slide (shell cancel)
1619
return set_mario_action(m, ACT_RIDING_SHELL_GROUND, 0);
1620
}
1621
1622
push_mario_out_of_object(m, o, 2.0f);
1623
}
1624
1625
return FALSE;
1626
}
1627
1628
u32 check_object_grab_mario(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1629
if (!mario_has_improved_metal_cap(m)
1630
&& (!(m->action & (ACT_FLAG_AIR | ACT_FLAG_INVULNERABLE | ACT_FLAG_ATTACKING)) || !sInvulnerable)
1631
&& (o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) {
1632
if (object_facing_mario(m, o, 0x2AAA)) {
1633
mario_stop_riding_and_holding(m);
1634
o->oInteractStatus = INT_STATUS_INTERACTED | INT_STATUS_GRABBED_MARIO;
1635
1636
m->faceAngle[1] = o->oMoveAngleYaw;
1637
m->interactObj = o;
1638
m->usedObj = o;
1639
1640
update_mario_sound_and_camera(m);
1641
play_sound(SOUND_MARIO_OOOF, m->marioObj->header.gfx.cameraToObject);
1642
#if ENABLE_RUMBLE
1643
queue_rumble_data(5, 80);
1644
#endif
1645
return set_mario_action(m, ACT_GRABBED, 0);
1646
}
1647
}
1648
1649
push_mario_out_of_object(m, o, -5.0f);
1650
return FALSE;
1651
}
1652
1653
u32 interact_pole(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1654
s32 actionId = m->action & ACT_ID_MASK;
1655
if (actionId >= 0x080 && actionId < 0x0A0) {
1656
if (!(m->prevAction & ACT_FLAG_ON_POLE) || m->usedObj != o) {
1657
#ifdef VERSION_SH
1658
f32 velConv = m->forwardVel; // conserve the velocity.
1659
struct Object *marioObj = m->marioObj;
1660
u32 lowSpeed;
1661
#else
1662
u32 lowSpeed = (m->forwardVel <= 10.0f);
1663
struct Object *marioObj = m->marioObj;
1664
#endif
1665
1666
mario_stop_riding_and_holding(m);
1667
1668
#ifdef VERSION_SH
1669
lowSpeed = (velConv <= 10.0f);
1670
#endif
1671
1672
m->interactObj = o;
1673
m->usedObj = o;
1674
m->vel[1] = 0.0f;
1675
m->forwardVel = 0.0f;
1676
1677
marioObj->oMarioPoleUnk108 = 0;
1678
marioObj->oMarioPoleYawVel = 0;
1679
if ((configApplyBugFixes > 1) && ((m->pos[1] - o->oPosY) < 0)) {
1680
marioObj->oMarioPolePos = -o->hitboxDownOffset;
1681
}
1682
else {
1683
marioObj->oMarioPolePos = m->pos[1] - o->oPosY;
1684
}
1685
1686
if (lowSpeed) {
1687
return set_mario_action(m, ACT_GRAB_POLE_SLOW, 0);
1688
}
1689
1690
//! @bug Using m->forwardVel here is assumed to be 0.0f due to the set from earlier.
1691
// This is fixed in the Shindou version.
1692
#ifdef VERSION_SH
1693
marioObj->oMarioPoleYawVel = (s32)(velConv * 0x100 + 0x1000);
1694
#else
1695
marioObj->oMarioPoleYawVel = (s32)(m->forwardVel * 0x100 + 0x1000);
1696
#endif
1697
reset_mario_pitch(m);
1698
#if ENABLE_RUMBLE
1699
queue_rumble_data(5, 80);
1700
#endif
1701
return set_mario_action(m, ACT_GRAB_POLE_FAST, 0);
1702
}
1703
}
1704
1705
return FALSE;
1706
}
1707
1708
u32 interact_hoot(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1709
s32 actionId = m->action & ACT_ID_MASK;
1710
1711
//! Can pause to advance the global timer without falling too far, allowing
1712
// you to regrab after letting go.
1713
if (actionId >= 0x080 && actionId < 0x098
1714
&& (gGlobalTimer - m->usedObj->oHootMarioReleaseTime > 30)) {
1715
mario_stop_riding_and_holding(m);
1716
1717
o->oInteractStatus = TRUE; //! Note: Not a flag, treated as a TRUE/FALSE statement
1718
m->interactObj = o;
1719
m->usedObj = o;
1720
1721
#if ENABLE_RUMBLE
1722
queue_rumble_data(5, 80);
1723
#endif
1724
update_mario_sound_and_camera(m);
1725
return set_mario_action(m, ACT_RIDING_HOOT, 0);
1726
}
1727
1728
return FALSE;
1729
}
1730
1731
u32 interact_cap(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1732
u32 capFlag = get_mario_cap_flag(o);
1733
u16 capMusic = 0;
1734
u16 capTime = 0;
1735
1736
if (m->action != ACT_GETTING_BLOWN && capFlag != 0) {
1737
m->interactObj = o;
1738
o->oInteractStatus = INT_STATUS_INTERACTED;
1739
1740
m->flags &= ~MARIO_CAP_ON_HEAD & ~MARIO_CAP_IN_HAND;
1741
m->flags |= capFlag;
1742
1743
switch (capFlag) {
1744
case MARIO_VANISH_CAP:
1745
capTime = 600;
1746
capMusic = SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP);
1747
break;
1748
1749
case MARIO_METAL_CAP:
1750
capTime = 600;
1751
capMusic = SEQUENCE_ARGS(4, SEQ_EVENT_METAL_CAP);
1752
break;
1753
1754
case MARIO_WING_CAP:
1755
capTime = 1800;
1756
capMusic = SEQUENCE_ARGS(4, SEQ_EVENT_POWERUP);
1757
break;
1758
}
1759
1760
if (capTime > m->capTimer) {
1761
m->capTimer = capTime;
1762
}
1763
1764
if ((m->action & ACT_FLAG_IDLE) || m->action == ACT_WALKING) {
1765
m->flags |= MARIO_CAP_IN_HAND;
1766
set_mario_action(m, ACT_PUTTING_ON_CAP, 0);
1767
} else {
1768
m->flags |= MARIO_CAP_ON_HEAD;
1769
}
1770
1771
play_sound(SOUND_MENU_STAR_SOUND, m->marioObj->header.gfx.cameraToObject);
1772
play_sound(SOUND_MARIO_HERE_WE_GO, m->marioObj->header.gfx.cameraToObject);
1773
1774
if (capMusic != 0) {
1775
play_cap_music(capMusic);
1776
}
1777
1778
return TRUE;
1779
}
1780
1781
return FALSE;
1782
}
1783
1784
u32 interact_grabbable(struct MarioState *m, u32 interactType, struct Object *o) {
1785
const BehaviorScript *script = virtual_to_segmented(0x13, o->behavior);
1786
1787
if (o->oInteractionSubtype & INT_SUBTYPE_KICKABLE) {
1788
u32 interaction = determine_interaction(m, o);
1789
if (interaction & (INT_KICK | INT_TRIP)) {
1790
attack_object(o, interaction);
1791
bounce_back_from_attack(m, interaction);
1792
return FALSE;
1793
}
1794
}
1795
1796
if ((o->oInteractionSubtype & INT_SUBTYPE_GRABS_MARIO)) {
1797
if (check_object_grab_mario(m, interactType, o)) {
1798
return TRUE;
1799
}
1800
}
1801
1802
if (able_to_grab_object(m, o)) {
1803
if (!(o->oInteractionSubtype & INT_SUBTYPE_NOT_GRABBABLE)) {
1804
m->interactObj = o;
1805
m->input |= INPUT_INTERACT_OBJ_GRABBABLE;
1806
return TRUE;
1807
}
1808
}
1809
1810
if (script != bhvBowser) {
1811
push_mario_out_of_object(m, o, -5.0f);
1812
}
1813
1814
return FALSE;
1815
}
1816
1817
u32 mario_can_talk(struct MarioState *m, u32 arg) {
1818
s16 val6;
1819
1820
if ((m->action & ACT_FLAG_IDLE) != 0x00000000) {
1821
return TRUE;
1822
}
1823
1824
if ((m->action == ACT_WALKING)
1825
|| (configTalkNPCs && ((m->action == ACT_DECELERATING) || (m->action == ACT_BRAKING) || m->action == ACT_BRAKING_STOP) )) {
1826
if (arg) {
1827
return TRUE;
1828
}
1829
1830
val6 = m->marioObj->header.gfx.animInfo.animID;
1831
1832
if (val6 == 0x0080 || val6 == 0x007F || val6 == 0x006C) {
1833
return TRUE;
1834
}
1835
}
1836
1837
return FALSE;
1838
}
1839
1840
#ifdef VERSION_JP
1841
#define READ_MASK (INPUT_B_PRESSED)
1842
#else
1843
#define READ_MASK (INPUT_B_PRESSED | INPUT_A_PRESSED)
1844
#endif
1845
1846
#ifdef VERSION_JP
1847
#define SIGN_RANGE 0x38E3
1848
#else
1849
#define SIGN_RANGE 0x4000
1850
#endif
1851
1852
u32 check_read_sign(struct MarioState *m, struct Object *o) {
1853
if (((m->input & READ_MASK && !configTalkNPCs) || (configTalkNPCs && m->input & INPUT_B_PRESSED))
1854
&& mario_can_talk(m, 0) && object_facing_mario(m, o, SIGN_RANGE)) {
1855
s16 facingDYaw = (s16)(o->oMoveAngleYaw + 0x8000) - m->faceAngle[1];
1856
if ((facingDYaw >= -SIGN_RANGE && facingDYaw <= SIGN_RANGE) || (configTalkNPCs)) {
1857
f32 targetX = o->oPosX + 105.0f * sins(o->oMoveAngleYaw);
1858
f32 targetZ = o->oPosZ + 105.0f * coss(o->oMoveAngleYaw);
1859
1860
m->marioObj->oMarioReadingSignDYaw = facingDYaw;
1861
m->marioObj->oMarioReadingSignDPosX = targetX - m->pos[0];
1862
m->marioObj->oMarioReadingSignDPosZ = targetZ - m->pos[2];
1863
1864
m->interactObj = o;
1865
m->usedObj = o;
1866
return set_mario_action(m, ACT_READING_SIGN, 0);
1867
}
1868
}
1869
1870
return FALSE;
1871
}
1872
1873
u32 check_npc_talk(struct MarioState *m, struct Object *o) {
1874
if (((m->input & READ_MASK && !configTalkNPCs) || (configTalkNPCs && m->input & INPUT_B_PRESSED))
1875
&& mario_can_talk(m, 1)) {
1876
s16 facingDYaw = mario_obj_angle_to_object(m, o) - m->faceAngle[1];
1877
if ((facingDYaw >= -0x4000 && facingDYaw <= 0x4000) || (configTalkNPCs)) {
1878
o->oInteractStatus = INT_STATUS_INTERACTED;
1879
1880
m->interactObj = o;
1881
m->usedObj = o;
1882
1883
push_mario_out_of_object(m, o, -10.0f);
1884
return set_mario_action(m, ACT_WAITING_FOR_DIALOG, 0);
1885
}
1886
}
1887
1888
push_mario_out_of_object(m, o, -10.0f);
1889
return FALSE;
1890
}
1891
1892
u32 interact_text(struct MarioState *m, UNUSED u32 interactType, struct Object *o) {
1893
u32 interact = FALSE;
1894
1895
if (o->oInteractionSubtype & INT_SUBTYPE_SIGN) {
1896
interact = check_read_sign(m, o);
1897
} else if (o->oInteractionSubtype & INT_SUBTYPE_NPC) {
1898
interact = check_npc_talk(m, o);
1899
} else {
1900
push_mario_out_of_object(m, o, 2.0f);
1901
}
1902
1903
return interact;
1904
}
1905
1906
void check_kick_or_punch_wall(struct MarioState *m) {
1907
if (m->flags & (MARIO_PUNCHING | MARIO_KICKING | MARIO_TRIPPING)) {
1908
Vec3f detector;
1909
detector[0] = m->pos[0] + 50.0f * sins(m->faceAngle[1]);
1910
detector[2] = m->pos[2] + 50.0f * coss(m->faceAngle[1]);
1911
detector[1] = m->pos[1];
1912
1913
if (resolve_and_return_wall_collisions(detector, 80.0f, 5.0f) != NULL) {
1914
if (m->action != ACT_MOVE_PUNCHING || m->forwardVel >= 0.0f) {
1915
if (m->action == ACT_PUNCHING) {
1916
m->action = ACT_MOVE_PUNCHING;
1917
}
1918
1919
mario_set_forward_vel(m, -48.0f);
1920
play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject);
1921
m->particleFlags |= PARTICLE_TRIANGLE;
1922
} else if (m->action & ACT_FLAG_AIR) {
1923
mario_set_forward_vel(m, -16.0f);
1924
play_sound(SOUND_ACTION_HIT_2, m->marioObj->header.gfx.cameraToObject);
1925
m->particleFlags |= PARTICLE_TRIANGLE;
1926
}
1927
}
1928
}
1929
}
1930
1931
void mario_process_interactions(struct MarioState *m) {
1932
sDelayInvincTimer = FALSE;
1933
sInvulnerable = (m->action & ACT_FLAG_INVULNERABLE) || m->invincTimer != 0;
1934
1935
if (!(m->action & ACT_FLAG_INTANGIBLE) && m->collidedObjInteractTypes != 0) {
1936
s32 i;
1937
for (i = 0; i < ARRAY_COUNT(sInteractionHandlers); i++) {
1938
u32 interactType = sInteractionHandlers[i].interactType;
1939
if (m->collidedObjInteractTypes & interactType) {
1940
struct Object *object = mario_get_collided_object(m, interactType);
1941
1942
m->collidedObjInteractTypes &= ~interactType;
1943
1944
if (!(object->oInteractStatus & INT_STATUS_INTERACTED)) {
1945
if (sInteractionHandlers[i].handler(m, interactType, object)) {
1946
break;
1947
}
1948
}
1949
}
1950
}
1951
}
1952
1953
if (m->invincTimer > 0 && !sDelayInvincTimer) {
1954
m->invincTimer -= 1;
1955
}
1956
1957
//! If the kick/punch flags are set and an object collision changes Mario's
1958
// action, he will get the kick/punch wall speed anyway.
1959
check_kick_or_punch_wall(m);
1960
m->flags &= ~MARIO_PUNCHING & ~MARIO_KICKING & ~MARIO_TRIPPING;
1961
1962
if (!(m->marioObj->collidedObjInteractTypes & (INTERACT_WARP_DOOR | INTERACT_DOOR))) {
1963
sDisplayingDoorText = FALSE;
1964
}
1965
if (!(m->marioObj->collidedObjInteractTypes & INTERACT_WARP)) {
1966
sJustTeleported = FALSE;
1967
}
1968
}
1969
1970
void check_death_barrier(struct MarioState *m) {
1971
if (m->pos[1] < m->floorHeight + 2048.0f) {
1972
if (level_trigger_warp(m, WARP_OP_WARP_FLOOR) == 20 && !(m->flags & MARIO_UNKNOWN_18)) {
1973
play_sound(SOUND_MARIO_WAAAOOOW, m->marioObj->header.gfx.cameraToObject);
1974
}
1975
}
1976
}
1977
1978
void check_lava_boost(struct MarioState *m) {
1979
if (!mario_has_improved_metal_cap(m) && !(m->action & ACT_FLAG_RIDING_SHELL) && m->pos[1] < m->floorHeight + 10.0f) {
1980
if (!(m->flags & MARIO_METAL_CAP)) {
1981
m->hurtCounter += (m->flags & MARIO_CAP_ON_HEAD) ? 12 : 18;
1982
}
1983
1984
update_mario_sound_and_camera(m);
1985
drop_and_set_mario_action(m, ACT_LAVA_BOOST, 0);
1986
}
1987
}
1988
1989
void pss_begin_slide(UNUSED struct MarioState *m) {
1990
if (!(gHudDisplay.flags & HUD_DISPLAY_FLAG_TIMER)) {
1991
level_control_timer(TIMER_CONTROL_SHOW);
1992
level_control_timer(TIMER_CONTROL_START);
1993
sPssSlideStarted = TRUE;
1994
}
1995
}
1996
1997
void pss_end_slide(struct MarioState *m) {
1998
//! This flag isn't set on death or level entry, allowing double star spawn
1999
if (sPssSlideStarted) {
2000
u16 slideTime = level_control_timer(TIMER_CONTROL_STOP);
2001
if (slideTime < 630) {
2002
m->marioObj->oBehParams = (1 << 24);
2003
spawn_default_star(-6358.0f, -4300.0f, 4700.0f);
2004
}
2005
sPssSlideStarted = FALSE;
2006
}
2007
}
2008
2009
void mario_handle_special_floors(struct MarioState *m) {
2010
if ((m->action & ACT_GROUP_MASK) == ACT_GROUP_CUTSCENE) {
2011
return;
2012
}
2013
2014
if (m->floor != NULL) {
2015
s32 floorType = m->floor->type;
2016
2017
switch (floorType) {
2018
case SURFACE_DEATH_PLANE:
2019
case SURFACE_VERTICAL_WIND:
2020
check_death_barrier(m);
2021
break;
2022
2023
case SURFACE_WARP:
2024
level_trigger_warp(m, WARP_OP_WARP_FLOOR);
2025
break;
2026
2027
case SURFACE_TIMER_START:
2028
pss_begin_slide(m);
2029
break;
2030
2031
case SURFACE_TIMER_END:
2032
pss_end_slide(m);
2033
break;
2034
}
2035
2036
if (!(m->action & ACT_FLAG_AIR) && !(m->action & ACT_FLAG_SWIMMING)) {
2037
switch (floorType) {
2038
case SURFACE_BURNING:
2039
check_lava_boost(m);
2040
break;
2041
}
2042
}
2043
}
2044
}
2045