Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/engine/behavior_script.c
7857 views
1
#include <ultra64.h>
2
3
#include "sm64.h"
4
#include "behavior_data.h"
5
#include "behavior_script.h"
6
#include "game/area.h"
7
#include "game/behavior_actions.h"
8
#include "game/game_init.h"
9
#include "game/mario.h"
10
#include "game/memory.h"
11
#include "game/obj_behaviors_2.h"
12
#include "game/object_helpers.h"
13
#include "game/object_list_processor.h"
14
#include "graph_node.h"
15
#include "surface_collision.h"
16
17
#include "game/settings.h"
18
19
// Macros for retrieving arguments from behavior scripts.
20
#define BHV_CMD_GET_1ST_U8(index) (u8)((gCurBhvCommand[index] >> 24) & 0xFF) // unused
21
#define BHV_CMD_GET_2ND_U8(index) (u8)((gCurBhvCommand[index] >> 16) & 0xFF)
22
#define BHV_CMD_GET_3RD_U8(index) (u8)((gCurBhvCommand[index] >> 8) & 0xFF)
23
#define BHV_CMD_GET_4TH_U8(index) (u8)((gCurBhvCommand[index]) & 0xFF)
24
25
#define BHV_CMD_GET_1ST_S16(index) (s16)(gCurBhvCommand[index] >> 16)
26
#define BHV_CMD_GET_2ND_S16(index) (s16)(gCurBhvCommand[index] & 0xFFFF)
27
28
#define BHV_CMD_GET_U32(index) (u32)(gCurBhvCommand[index])
29
#define BHV_CMD_GET_VPTR(index) (void *)(gCurBhvCommand[index])
30
31
#define BHV_CMD_GET_ADDR_OF_CMD(index) (uintptr_t)(&gCurBhvCommand[index])
32
33
static u16 gRandomSeed16;
34
35
// Unused function that directly jumps to a behavior command and resets the object's stack index.
36
UNUSED static void goto_behavior_unused(const BehaviorScript *bhvAddr) {
37
gCurBhvCommand = segmented_to_virtual(bhvAddr);
38
gCurrentObject->bhvStackIndex = 0;
39
}
40
41
// Generate a pseudorandom integer from 0 to 65535 from the random seed, and update the seed.
42
u16 random_u16(void) {
43
u16 temp1, temp2;
44
45
if (gRandomSeed16 == 22026) {
46
gRandomSeed16 = 0;
47
}
48
49
temp1 = (gRandomSeed16 & 0x00FF) << 8;
50
temp1 = temp1 ^ gRandomSeed16;
51
52
gRandomSeed16 = ((temp1 & 0x00FF) << 8) + ((temp1 & 0xFF00) >> 8);
53
54
temp1 = ((temp1 & 0x00FF) << 1) ^ gRandomSeed16;
55
temp2 = (temp1 >> 1) ^ 0xFF80;
56
57
if ((temp1 & 1) == 0) {
58
if (temp2 == 43605) {
59
gRandomSeed16 = 0;
60
} else {
61
gRandomSeed16 = temp2 ^ 0x1FF4;
62
}
63
} else {
64
gRandomSeed16 = temp2 ^ 0x8180;
65
}
66
67
return gRandomSeed16;
68
}
69
70
// Generate a pseudorandom float in the range (0, 1).
71
f32 random_float(void) {
72
f32 rnd = random_u16();
73
return rnd / (double) 0x10000;
74
}
75
76
// Return either -1 or 1 with a 50:50 chance.
77
s32 random_sign(void) {
78
if (random_u16() >= 0x7FFF) {
79
return 1;
80
} else {
81
return -1;
82
}
83
}
84
85
// Update an object's graphical position and rotation to match its real position and rotation.
86
void obj_update_gfx_pos_and_angle(struct Object *obj) {
87
obj->header.gfx.pos[0] = obj->oPosX;
88
obj->header.gfx.pos[1] = obj->oPosY + obj->oGraphYOffset;
89
obj->header.gfx.pos[2] = obj->oPosZ;
90
91
obj->header.gfx.angle[0] = obj->oFaceAnglePitch & 0xFFFF;
92
obj->header.gfx.angle[1] = obj->oFaceAngleYaw & 0xFFFF;
93
obj->header.gfx.angle[2] = obj->oFaceAngleRoll & 0xFFFF;
94
}
95
96
// Push the address of a behavior command to the object's behavior stack.
97
static void cur_obj_bhv_stack_push(uintptr_t bhvAddr) {
98
gCurrentObject->bhvStack[gCurrentObject->bhvStackIndex] = bhvAddr;
99
gCurrentObject->bhvStackIndex++;
100
}
101
102
// Retrieve the last behavior command address from the object's behavior stack.
103
static uintptr_t cur_obj_bhv_stack_pop(void) {
104
uintptr_t bhvAddr;
105
106
gCurrentObject->bhvStackIndex--;
107
bhvAddr = gCurrentObject->bhvStack[gCurrentObject->bhvStackIndex];
108
109
return bhvAddr;
110
}
111
112
UNUSED static void stub_behavior_script_1(void) {
113
for (;;) {
114
;
115
}
116
}
117
118
// Command 0x22: Hides the current object.
119
// Usage: HIDE()
120
static s32 bhv_cmd_hide(void) {
121
cur_obj_hide();
122
123
gCurBhvCommand++;
124
return BHV_PROC_CONTINUE;
125
}
126
127
// Command 0x35: Disables rendering for the object.
128
// Usage: DISABLE_RENDERING()
129
static s32 bhv_cmd_disable_rendering(void) {
130
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
131
132
gCurBhvCommand++;
133
return BHV_PROC_CONTINUE;
134
}
135
136
// Command 0x21: Billboards the current object, making it always face the camera.
137
// Usage: BILLBOARD()
138
static s32 bhv_cmd_billboard(void) {
139
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_BILLBOARD;
140
141
gCurBhvCommand++;
142
return BHV_PROC_CONTINUE;
143
}
144
145
// Command 0x1B: Sets the current model ID of the object.
146
// Usage: SET_MODEL(modelID)
147
static s32 bhv_cmd_set_model(void) {
148
s32 modelID = BHV_CMD_GET_2ND_S16(0);
149
150
gCurrentObject->header.gfx.sharedChild = gLoadedGraphNodes[modelID];
151
152
gCurBhvCommand++;
153
return BHV_PROC_CONTINUE;
154
}
155
156
// Command 0x1C: Spawns a child object with the specified model and behavior.
157
// Usage: SPAWN_CHILD(modelID, behavior)
158
static s32 bhv_cmd_spawn_child(void) {
159
u32 model = BHV_CMD_GET_U32(1);
160
const BehaviorScript *behavior = BHV_CMD_GET_VPTR(2);
161
162
struct Object *child = spawn_object_at_origin(gCurrentObject, 0, model, behavior);
163
obj_copy_pos_and_angle(child, gCurrentObject);
164
165
gCurBhvCommand += 3;
166
return BHV_PROC_CONTINUE;
167
}
168
169
// Command 0x2C: Spawns a new object with the specified model and behavior.
170
// Usage: SPAWN_OBJ(modelID, behavior)
171
static s32 bhv_cmd_spawn_obj(void) {
172
u32 model = BHV_CMD_GET_U32(1);
173
const BehaviorScript *behavior = BHV_CMD_GET_VPTR(2);
174
175
struct Object *object = spawn_object_at_origin(gCurrentObject, 0, model, behavior);
176
obj_copy_pos_and_angle(object, gCurrentObject);
177
// TODO: Does this cmd need renaming? This line is the only difference between this and the above func.
178
gCurrentObject->prevObj = object;
179
180
gCurBhvCommand += 3;
181
return BHV_PROC_CONTINUE;
182
}
183
184
// Command 0x29: Spawns a child object with the specified model and behavior, plus a behavior param.
185
// Usage: SPAWN_CHILD_WITH_PARAM(bhvParam, modelID, behavior)
186
static s32 bhv_cmd_spawn_child_with_param(void) {
187
u32 bhvParam = BHV_CMD_GET_2ND_S16(0);
188
u32 modelID = BHV_CMD_GET_U32(1);
189
const BehaviorScript *behavior = BHV_CMD_GET_VPTR(2);
190
191
struct Object *child = spawn_object_at_origin(gCurrentObject, 0, modelID, behavior);
192
obj_copy_pos_and_angle(child, gCurrentObject);
193
child->oBehParams2ndByte = bhvParam;
194
195
gCurBhvCommand += 3;
196
return BHV_PROC_CONTINUE;
197
}
198
199
// Command 0x1D: Exits the behavior script and despawns the object.
200
// Usage: DEACTIVATE()
201
static s32 bhv_cmd_deactivate(void) {
202
gCurrentObject->activeFlags = ACTIVE_FLAG_DEACTIVATED;
203
return BHV_PROC_BREAK;
204
}
205
206
// Command 0x0A: Exits the behavior script.
207
// Usage: BREAK()
208
static s32 bhv_cmd_break(void) {
209
return BHV_PROC_BREAK;
210
}
211
212
// Command 0x0B: Exits the behavior script, unused.
213
// Usage: BREAK_UNUSED()
214
static s32 bhv_cmd_break_unused(void) {
215
return BHV_PROC_BREAK;
216
}
217
218
// Command 0x02: Jumps to a new behavior command and stores the return address in the object's behavior stack.
219
// Usage: CALL(addr)
220
static s32 bhv_cmd_call(void) {
221
const BehaviorScript *jumpAddress;
222
gCurBhvCommand++;
223
224
cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(1)); // Store address of the next bhv command in the stack.
225
jumpAddress = segmented_to_virtual(BHV_CMD_GET_VPTR(0));
226
gCurBhvCommand = jumpAddress; // Jump to the new address.
227
228
return BHV_PROC_CONTINUE;
229
}
230
231
// Command 0x03: Jumps back to the behavior command stored in the object's behavior stack. Used after CALL.
232
// Usage: RETURN()
233
static s32 bhv_cmd_return(void) {
234
gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Retrieve command address and jump to it.
235
return BHV_PROC_CONTINUE;
236
}
237
238
// Command 0x01: Delays the behavior script for a certain number of frames.
239
// Usage: DELAY(num)
240
static s32 bhv_cmd_delay(void) {
241
s16 num = BHV_CMD_GET_2ND_S16(0);
242
243
if (gCurrentObject->bhvDelayTimer < num - 1) {
244
gCurrentObject->bhvDelayTimer++; // Increment timer
245
} else {
246
gCurrentObject->bhvDelayTimer = 0;
247
gCurBhvCommand++; // Delay ended, move to next bhv command (note: following commands will not execute until next frame)
248
}
249
250
return BHV_PROC_BREAK;
251
}
252
253
// Command 0x25: Delays the behavior script for the number of frames given by the value of the specified field.
254
// Usage: DELAY_VAR(field)
255
static s32 bhv_cmd_delay_var(void) {
256
u8 field = BHV_CMD_GET_2ND_U8(0);
257
s32 num = cur_obj_get_int(field);
258
259
if (gCurrentObject->bhvDelayTimer < num - 1) {
260
gCurrentObject->bhvDelayTimer++; // Increment timer
261
} else {
262
gCurrentObject->bhvDelayTimer = 0;
263
gCurBhvCommand++; // Delay ended, move to next bhv command
264
}
265
266
return BHV_PROC_BREAK;
267
}
268
269
// Command 0x04: Jumps to a new behavior script without saving anything.
270
// Usage: GOTO(addr)
271
static s32 bhv_cmd_goto(void) {
272
gCurBhvCommand++; // Useless
273
gCurBhvCommand = segmented_to_virtual(BHV_CMD_GET_VPTR(0)); // Jump directly to address
274
return BHV_PROC_CONTINUE;
275
}
276
277
// Command 0x26: Unused. Marks the start of a loop that will repeat a certain number of times.
278
// Uses a u8 as the argument, instead of a s16 like the other version does.
279
// Usage: BEGIN_REPEAT_UNUSED(count)
280
static s32 bhv_cmd_begin_repeat_unused(void) {
281
s32 count = BHV_CMD_GET_2ND_U8(0);
282
283
cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(1)); // Store address of the first command of the loop in the stack
284
cur_obj_bhv_stack_push(count); // Store repeat count in the stack too
285
286
gCurBhvCommand++;
287
return BHV_PROC_CONTINUE;
288
}
289
290
// Command 0x05: Marks the start of a loop that will repeat a certain number of times.
291
// Usage: BEGIN_REPEAT(count)
292
static s32 bhv_cmd_begin_repeat(void) {
293
s32 count = BHV_CMD_GET_2ND_S16(0);
294
295
cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(1)); // Store address of the first command of the loop in the stack
296
cur_obj_bhv_stack_push(count); // Store repeat count in the stack too
297
298
gCurBhvCommand++;
299
return BHV_PROC_CONTINUE;
300
}
301
302
// Command 0x06: Marks the end of a repeating loop.
303
// Usage: END_REPEAT()
304
static s32 bhv_cmd_end_repeat(void) {
305
u32 count = cur_obj_bhv_stack_pop(); // Retrieve loop count from the stack.
306
count--;
307
308
if (count != 0) {
309
gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop
310
// Save address and count to the stack again
311
cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(0));
312
cur_obj_bhv_stack_push(count);
313
} else { // Finished iterating over the loop
314
cur_obj_bhv_stack_pop(); // Necessary to remove address from the stack
315
gCurBhvCommand++;
316
}
317
318
// Don't execute following commands until next frame
319
return BHV_PROC_BREAK;
320
}
321
322
// Command 0x07: Also marks the end of a repeating loop, but continues executing commands following the loop on the same frame.
323
// Usage: END_REPEAT_CONTINUE()
324
static s32 bhv_cmd_end_repeat_continue(void) {
325
u32 count = cur_obj_bhv_stack_pop();
326
count--;
327
328
if (count != 0) {
329
gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop
330
// Save address and count to the stack again
331
cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(0));
332
cur_obj_bhv_stack_push(count);
333
} else { // Finished iterating over the loop
334
cur_obj_bhv_stack_pop(); // Necessary to remove address from the stack
335
gCurBhvCommand++;
336
}
337
338
// Start executing following commands immediately
339
return BHV_PROC_CONTINUE;
340
}
341
342
// Command 0x08: Marks the beginning of an infinite loop.
343
// Usage: BEGIN_LOOP()
344
static s32 bhv_cmd_begin_loop(void) {
345
cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(1)); // Store address of the first command of the loop in the stack
346
347
gCurBhvCommand++;
348
return BHV_PROC_CONTINUE;
349
}
350
351
// Command 0x09: Marks the end of an infinite loop.
352
// Usage: END_LOOP()
353
static s32 bhv_cmd_end_loop(void) {
354
gCurBhvCommand = (const BehaviorScript *) cur_obj_bhv_stack_pop(); // Jump back to the first command in the loop
355
cur_obj_bhv_stack_push(BHV_CMD_GET_ADDR_OF_CMD(0)); // Save address to the stack again
356
357
return BHV_PROC_BREAK;
358
}
359
360
// Command 0x0C: Executes a native game function. Function must not take or return any values.
361
// Usage: CALL_NATIVE(func)
362
typedef void (*NativeBhvFunc)(void);
363
static s32 bhv_cmd_call_native(void) {
364
NativeBhvFunc behaviorFunc = BHV_CMD_GET_VPTR(1);
365
366
behaviorFunc();
367
368
gCurBhvCommand += 2;
369
return BHV_PROC_CONTINUE;
370
}
371
372
// Command 0x0E: Sets the specified field to a float.
373
// Usage: SET_FLOAT(field, value)
374
static s32 bhv_cmd_set_float(void) {
375
u8 field = BHV_CMD_GET_2ND_U8(0);
376
f32 value = BHV_CMD_GET_2ND_S16(0);
377
378
cur_obj_set_float(field, value);
379
380
gCurBhvCommand++;
381
return BHV_PROC_CONTINUE;
382
}
383
384
// Command 0x10: Sets the specified field to an integer.
385
// Usage: SET_INT(field, value)
386
static s32 bhv_cmd_set_int(void) {
387
u8 field = BHV_CMD_GET_2ND_U8(0);
388
s16 value = BHV_CMD_GET_2ND_S16(0);
389
390
cur_obj_set_int(field, value);
391
392
gCurBhvCommand++;
393
return BHV_PROC_CONTINUE;
394
}
395
396
// Command 0x36: Unused. Sets the specified field to an integer. Wastes 4 bytes of space for no reason at all.
397
static s32 bhv_cmd_set_int_unused(void) {
398
u8 field = BHV_CMD_GET_2ND_U8(0);
399
s32 value = BHV_CMD_GET_2ND_S16(1); // Taken from 2nd word instead of 1st
400
401
cur_obj_set_int(field, value);
402
403
gCurBhvCommand += 2; // Twice as long
404
return BHV_PROC_CONTINUE;
405
}
406
407
// Command 0x14: Sets the specified field to a random float in the given range.
408
// Usage: SET_RANDOM_FLOAT(field, min, range)
409
static s32 bhv_cmd_set_random_float(void) {
410
u8 field = BHV_CMD_GET_2ND_U8(0);
411
f32 min = BHV_CMD_GET_2ND_S16(0);
412
f32 range = BHV_CMD_GET_1ST_S16(1);
413
414
cur_obj_set_float(field, (range * random_float()) + min);
415
416
gCurBhvCommand += 2;
417
return BHV_PROC_CONTINUE;
418
}
419
420
// Command 0x15: Sets the specified field to a random integer in the given range.
421
// Usage: SET_RANDOM_INT(field, min, range)
422
static s32 bhv_cmd_set_random_int(void) {
423
u8 field = BHV_CMD_GET_2ND_U8(0);
424
s32 min = BHV_CMD_GET_2ND_S16(0);
425
s32 range = BHV_CMD_GET_1ST_S16(1);
426
427
cur_obj_set_int(field, (s32)(range * random_float()) + min);
428
429
gCurBhvCommand += 2;
430
return BHV_PROC_CONTINUE;
431
}
432
433
// Command 0x13: Gets a random short, right shifts it the specified amount and adds min to it, then sets the specified field to that value.
434
// Usage: SET_INT_RAND_RSHIFT(field, min, rshift)
435
static s32 bhv_cmd_set_int_rand_rshift(void) {
436
u8 field = BHV_CMD_GET_2ND_U8(0);
437
s32 min = BHV_CMD_GET_2ND_S16(0);
438
s32 rshift = BHV_CMD_GET_1ST_S16(1);
439
440
cur_obj_set_int(field, (random_u16() >> rshift) + min);
441
442
gCurBhvCommand += 2;
443
return BHV_PROC_CONTINUE;
444
}
445
446
// Command 0x16: Adds a random float in the given range to the specified field.
447
// Usage: ADD_RANDOM_FLOAT(field, min, range)
448
static s32 bhv_cmd_add_random_float(void) {
449
u8 field = BHV_CMD_GET_2ND_U8(0);
450
f32 min = BHV_CMD_GET_2ND_S16(0);
451
f32 range = BHV_CMD_GET_1ST_S16(1);
452
453
cur_obj_set_float(field, cur_obj_get_float(field) + min + (range * random_float()));
454
455
gCurBhvCommand += 2;
456
return BHV_PROC_CONTINUE;
457
}
458
459
// Command 0x17: Gets a random short, right shifts it the specified amount and adds min to it, then adds the value to the specified field. Unused.
460
// Usage: ADD_INT_RAND_RSHIFT(field, min, rshift)
461
static s32 bhv_cmd_add_int_rand_rshift(void) {
462
u8 field = BHV_CMD_GET_2ND_U8(0);
463
s32 min = BHV_CMD_GET_2ND_S16(0);
464
s32 rshift = BHV_CMD_GET_1ST_S16(1);
465
s32 rnd = random_u16();
466
467
cur_obj_set_int(field, (cur_obj_get_int(field) + min) + (rnd >> rshift));
468
469
gCurBhvCommand += 2;
470
return BHV_PROC_CONTINUE;
471
}
472
473
// Command 0x0D: Adds a float to the specified field.
474
// Usage: ADD_FLOAT(field, value)
475
static s32 bhv_cmd_add_float(void) {
476
u8 field = BHV_CMD_GET_2ND_U8(0);
477
f32 value = BHV_CMD_GET_2ND_S16(0);
478
479
cur_obj_add_float(field, value);
480
481
gCurBhvCommand++;
482
return BHV_PROC_CONTINUE;
483
}
484
485
// Command 0x0F: Adds an integer to the specified field.
486
// Usage: ADD_INT(field, value)
487
static s32 bhv_cmd_add_int(void) {
488
u8 field = BHV_CMD_GET_2ND_U8(0);
489
s16 value = BHV_CMD_GET_2ND_S16(0);
490
491
cur_obj_add_int(field, value);
492
493
gCurBhvCommand++;
494
return BHV_PROC_CONTINUE;
495
}
496
497
// Command 0x11: Performs a bitwise OR with the specified field and the given integer.
498
// Usually used to set an object's flags.
499
// Usage: OR_INT(field, value)
500
static s32 bhv_cmd_or_int(void) {
501
u8 objectOffset = BHV_CMD_GET_2ND_U8(0);
502
s32 value = BHV_CMD_GET_2ND_S16(0);
503
504
value &= 0xFFFF;
505
cur_obj_or_int(objectOffset, value);
506
507
gCurBhvCommand++;
508
return BHV_PROC_CONTINUE;
509
}
510
511
// Command 0x12: Performs a bit clear with the specified short. Unused.
512
// Usage: BIT_CLEAR(field, value)
513
static s32 bhv_cmd_bit_clear(void) {
514
u8 field = BHV_CMD_GET_2ND_U8(0);
515
s32 value = BHV_CMD_GET_2ND_S16(0);
516
517
value = (value & 0xFFFF) ^ 0xFFFF;
518
cur_obj_and_int(field, value);
519
520
gCurBhvCommand++;
521
return BHV_PROC_CONTINUE;
522
}
523
524
// Command 0x27: Loads the animations for the object. <field> is always set to oAnimations.
525
// Usage: LOAD_ANIMATIONS(field, anims)
526
static s32 bhv_cmd_load_animations(void) {
527
u8 field = BHV_CMD_GET_2ND_U8(0);
528
529
cur_obj_set_vptr(field, BHV_CMD_GET_VPTR(1));
530
531
gCurBhvCommand += 2;
532
return BHV_PROC_CONTINUE;
533
}
534
535
// Command 0x28: Begins animation and sets the object's current animation index to the specified value.
536
// Usage: ANIMATE(animIndex)
537
static s32 bhv_cmd_animate(void) {
538
s32 animIndex = BHV_CMD_GET_2ND_U8(0);
539
struct Animation **animations = gCurrentObject->oAnimations;
540
541
geo_obj_init_animation(&gCurrentObject->header.gfx, &animations[animIndex]);
542
543
gCurBhvCommand++;
544
return BHV_PROC_CONTINUE;
545
}
546
547
// Command 0x1E: Finds the floor triangle directly under the object and moves the object down to it.
548
// Usage: DROP_TO_FLOOR()
549
static s32 bhv_cmd_drop_to_floor(void) {
550
f32 x = gCurrentObject->oPosX;
551
f32 y = gCurrentObject->oPosY;
552
f32 z = gCurrentObject->oPosZ;
553
554
f32 floor = find_floor_height(x, y + 200.0f, z);
555
gCurrentObject->oPosY = floor;
556
gCurrentObject->oMoveFlags |= OBJ_MOVE_ON_GROUND;
557
558
gCurBhvCommand++;
559
return BHV_PROC_CONTINUE;
560
}
561
562
// Command 0x18: No operation. Unused.
563
// Usage: CMD_NOP_1(field)
564
static s32 bhv_cmd_nop_1(void) {
565
UNUSED u8 field = BHV_CMD_GET_2ND_U8(0);
566
567
gCurBhvCommand++;
568
return BHV_PROC_CONTINUE;
569
}
570
571
// Command 0x1A: No operation. Unused.
572
// Usage: CMD_NOP_3(field)
573
static s32 bhv_cmd_nop_3(void) {
574
UNUSED u8 field = BHV_CMD_GET_2ND_U8(0);
575
576
gCurBhvCommand++;
577
return BHV_PROC_CONTINUE;
578
}
579
580
// Command 0x19: No operation. Unused.
581
// Usage: CMD_NOP_2(field)
582
static s32 bhv_cmd_nop_2(void) {
583
UNUSED u8 field = BHV_CMD_GET_2ND_U8(0);
584
585
gCurBhvCommand++;
586
return BHV_PROC_CONTINUE;
587
}
588
589
// Command 0x1F: Sets the destination float field to the sum of the values of the given float fields.
590
// Usage: SUM_FLOAT(fieldDst, fieldSrc1, fieldSrc2)
591
static s32 bhv_cmd_sum_float(void) {
592
u32 fieldDst = BHV_CMD_GET_2ND_U8(0);
593
u32 fieldSrc1 = BHV_CMD_GET_3RD_U8(0);
594
u32 fieldSrc2 = BHV_CMD_GET_4TH_U8(0);
595
596
cur_obj_set_float(fieldDst, cur_obj_get_float(fieldSrc1) + cur_obj_get_float(fieldSrc2));
597
598
gCurBhvCommand++;
599
return BHV_PROC_CONTINUE;
600
}
601
602
// Command 0x20: Sets the destination integer field to the sum of the values of the given integer fields. Unused.
603
// Usage: SUM_INT(fieldDst, fieldSrc1, fieldSrc2)
604
static s32 bhv_cmd_sum_int(void) {
605
u32 fieldDst = BHV_CMD_GET_2ND_U8(0);
606
u32 fieldSrc1 = BHV_CMD_GET_3RD_U8(0);
607
u32 fieldSrc2 = BHV_CMD_GET_4TH_U8(0);
608
609
cur_obj_set_int(fieldDst, cur_obj_get_int(fieldSrc1) + cur_obj_get_int(fieldSrc2));
610
611
gCurBhvCommand++;
612
return BHV_PROC_CONTINUE;
613
}
614
615
// Command 0x23: Sets the size of the object's cylindrical hitbox.
616
// Usage: SET_HITBOX(radius, height)
617
static s32 bhv_cmd_set_hitbox(void) {
618
s16 radius = BHV_CMD_GET_1ST_S16(1);
619
s16 height = BHV_CMD_GET_2ND_S16(1);
620
621
gCurrentObject->hitboxRadius = radius;
622
gCurrentObject->hitboxHeight = height;
623
624
gCurBhvCommand += 2;
625
return BHV_PROC_CONTINUE;
626
}
627
628
// Command 0x2E: Sets the size of the object's cylindrical hurtbox.
629
// Usage: SET_HURTBOX(radius, height)
630
static s32 bhv_cmd_set_hurtbox(void) {
631
s16 radius = BHV_CMD_GET_1ST_S16(1);
632
s16 height = BHV_CMD_GET_2ND_S16(1);
633
634
gCurrentObject->hurtboxRadius = radius;
635
gCurrentObject->hurtboxHeight = height;
636
637
gCurBhvCommand += 2;
638
return BHV_PROC_CONTINUE;
639
}
640
641
// Command 0x2B: Sets the size of the object's cylindrical hitbox, and applies a downwards offset.
642
// Usage: SET_HITBOX_WITH_OFFSET(radius, height, downOffset)
643
static s32 bhv_cmd_set_hitbox_with_offset(void) {
644
s16 radius = BHV_CMD_GET_1ST_S16(1);
645
s16 height = BHV_CMD_GET_2ND_S16(1);
646
s16 downOffset = BHV_CMD_GET_1ST_S16(2);
647
648
gCurrentObject->hitboxRadius = radius;
649
gCurrentObject->hitboxHeight = height;
650
gCurrentObject->hitboxDownOffset = downOffset;
651
652
gCurBhvCommand += 3;
653
return BHV_PROC_CONTINUE;
654
}
655
656
// Command 0x24: No operation. Unused.
657
// Usage: CMD_NOP_4(field, value)
658
static s32 bhv_cmd_nop_4(void) {
659
UNUSED s16 field = BHV_CMD_GET_2ND_U8(0);
660
UNUSED s16 value = BHV_CMD_GET_2ND_S16(0);
661
662
gCurBhvCommand++;
663
return BHV_PROC_CONTINUE;
664
}
665
666
// Command 0x00: Defines the start of the behavior script as well as the object list the object belongs to.
667
// Has some special behavior for certain objects.
668
// Usage: BEGIN(objList)
669
static s32 bhv_cmd_begin(void) {
670
// These objects were likely very early objects, which is why this code is here
671
// instead of in the respective behavior scripts.
672
673
// Initiate the room if the object is a haunted chair or the mad piano.
674
if (cur_obj_has_behavior(bhvHauntedChair)) {
675
bhv_init_room();
676
}
677
if (cur_obj_has_behavior(bhvMadPiano)) {
678
bhv_init_room();
679
}
680
// Set collision distance if the object is a message panel.
681
if (cur_obj_has_behavior(bhvMessagePanel)) {
682
gCurrentObject->oCollisionDistance = 150.0f;
683
}
684
gCurBhvCommand++;
685
return BHV_PROC_CONTINUE;
686
}
687
688
// An unused, incomplete behavior command that does not have an entry in the lookup table, and so no command number.
689
// It cannot be simply re-added to the table, as unlike all other bhv commands it takes a parameter.
690
// Theoretically this command would have been of variable size.
691
// Included below is a modified/repaired version of this function that would work properly.
692
UNUSED static void bhv_cmd_set_int_random_from_table(s32 tableSize) {
693
u8 field = BHV_CMD_GET_2ND_U8(0);
694
s32 table[16];
695
s32 i;
696
// This for loop would not work as intended at all...
697
for (i = 0; i <= tableSize / 2; i += 2) {
698
table[i] = BHV_CMD_GET_1ST_S16(i + 1);
699
table[i + 1] = BHV_CMD_GET_2ND_S16(i + 1);
700
}
701
702
cur_obj_set_int(field, table[(s32)(tableSize * random_float())]);
703
704
// Does not increment gCurBhvCommand or return a bhv status
705
}
706
707
/**
708
// Command 0x??: Sets the specified field to a random entry in the given table, up to size 16.
709
// Bytes: ?? FF SS SS V1 V1 V2 V2 V3 V3 V4 V4... ...V15 V15 V16 V16 (no macro exists)
710
// F -> field, S -> table size, V1, V2, etc. -> table entries (up to 16)
711
static s32 bhv_cmd_set_int_random_from_table(void) {
712
u8 field = BHV_CMD_GET_2ND_U8(0);
713
// Retrieve tableSize from the bhv command instead of as a parameter.
714
s16 tableSize = BHV_CMD_GET_2ND_S16(0); // tableSize should not be greater than 16
715
s32 table[16];
716
s32 i;
717
718
// Construct the table from the behavior command.
719
for (i = 0; i <= tableSize; i += 2) {
720
table[i] = BHV_CMD_GET_1ST_S16((i / 2) + 1);
721
table[i + 1] = BHV_CMD_GET_2ND_S16((i / 2) + 1);
722
}
723
724
// Set the field to a random entry of the table.
725
cur_obj_set_int(field, table[(s32)(tableSize * random_float())]);
726
727
gCurBhvCommand += (tableSize / 2) + 1;
728
return BHV_PROC_CONTINUE;
729
}
730
**/
731
732
// Command 0x2A: Loads collision data for the object.
733
// Usage: LOAD_COLLISION_DATA(collisionData)
734
static s32 bhv_cmd_load_collision_data(void) {
735
u32 *collisionData = segmented_to_virtual(BHV_CMD_GET_VPTR(1));
736
737
gCurrentObject->collisionData = collisionData;
738
739
gCurBhvCommand += 2;
740
return BHV_PROC_CONTINUE;
741
}
742
743
// Command 0x2D: Sets the home position of the object to its current position.
744
// Usage: SET_HOME()
745
static s32 bhv_cmd_set_home(void) {
746
gCurrentObject->oHomeX = gCurrentObject->oPosX;
747
gCurrentObject->oHomeY = gCurrentObject->oPosY;
748
gCurrentObject->oHomeZ = gCurrentObject->oPosZ;
749
750
gCurBhvCommand++;
751
return BHV_PROC_CONTINUE;
752
}
753
754
// Command 0x2F: Sets the object's interaction type.
755
// Usage: SET_INTERACT_TYPE(type)
756
static s32 bhv_cmd_set_interact_type(void) {
757
gCurrentObject->oInteractType = BHV_CMD_GET_U32(1);
758
759
gCurBhvCommand += 2;
760
return BHV_PROC_CONTINUE;
761
}
762
763
// Command 0x31: Sets the object's interaction subtype. Unused.
764
// Usage: SET_INTERACT_SUBTYPE(subtype)
765
static s32 bhv_cmd_set_interact_subtype(void) {
766
gCurrentObject->oInteractionSubtype = BHV_CMD_GET_U32(1);
767
768
gCurBhvCommand += 2;
769
return BHV_PROC_CONTINUE;
770
}
771
772
// Command 0x32: Sets the object's size to the specified percentage.
773
// Usage: SCALE(unusedField, percent)
774
static s32 bhv_cmd_scale(void) {
775
UNUSED u8 unusedField = BHV_CMD_GET_2ND_U8(0);
776
s16 percent = BHV_CMD_GET_2ND_S16(0);
777
778
cur_obj_scale(percent / 100.0f);
779
780
gCurBhvCommand++;
781
return BHV_PROC_CONTINUE;
782
}
783
784
785
// Command 0x30: Sets various parameters that the object uses for calculating physics.
786
// Usage: SET_OBJ_PHYSICS(wallHitboxRadius, gravity, bounciness, dragStrength, friction, buoyancy, unused1, unused2)
787
static s32 bhv_cmd_set_obj_physics(void) {
788
UNUSED f32 unused1, unused2;
789
790
gCurrentObject->oWallHitboxRadius = BHV_CMD_GET_1ST_S16(1);
791
gCurrentObject->oGravity = BHV_CMD_GET_2ND_S16(1) / 100.0f;
792
gCurrentObject->oBounciness = BHV_CMD_GET_1ST_S16(2) / 100.0f;
793
gCurrentObject->oDragStrength = BHV_CMD_GET_2ND_S16(2) / 100.0f;
794
gCurrentObject->oFriction = BHV_CMD_GET_1ST_S16(3) / 100.0f;
795
gCurrentObject->oBuoyancy = BHV_CMD_GET_2ND_S16(3) / 100.0f;
796
797
unused1 = BHV_CMD_GET_1ST_S16(4) / 100.0f;
798
unused2 = BHV_CMD_GET_2ND_S16(4) / 100.0f;
799
800
gCurBhvCommand += 5;
801
return BHV_PROC_CONTINUE;
802
}
803
804
// Command 0x33: Performs a bit clear on the object's parent's field with the specified value.
805
// Used for clearing active particle flags fron Mario's object.
806
// Usage: PARENT_BIT_CLEAR(field, value)
807
static s32 bhv_cmd_parent_bit_clear(void) {
808
u8 field = BHV_CMD_GET_2ND_U8(0);
809
s32 value = BHV_CMD_GET_U32(1);
810
811
value = value ^ 0xFFFFFFFF;
812
obj_and_int(gCurrentObject->parentObj, field, value);
813
814
gCurBhvCommand += 2;
815
return BHV_PROC_CONTINUE;
816
}
817
818
// Command 0x37: Spawns a water droplet with the given parameters.
819
// Usage: SPAWN_WATER_DROPLET(dropletParams)
820
static s32 bhv_cmd_spawn_water_droplet(void) {
821
struct WaterDropletParams *dropletParams = BHV_CMD_GET_VPTR(1);
822
823
spawn_water_droplet(gCurrentObject, dropletParams);
824
825
gCurBhvCommand += 2;
826
return BHV_PROC_CONTINUE;
827
}
828
829
// Command 0x34: Animates an object using texture animation. <field> is always set to oAnimState.
830
// Usage: ANIMATE_TEXTURE(field, rate)
831
static s32 bhv_cmd_animate_texture(void) {
832
u8 field = BHV_CMD_GET_2ND_U8(0);
833
s16 rate = BHV_CMD_GET_2ND_S16(0);
834
835
// Increase the field (oAnimState) by 1 every <rate> frames.
836
if ((gGlobalTimer % rate) == 0) {
837
cur_obj_add_int(field, 1);
838
}
839
840
gCurBhvCommand++;
841
return BHV_PROC_CONTINUE;
842
}
843
844
void stub_behavior_script_2(void) {
845
}
846
847
typedef s32 (*BhvCommandProc)(void);
848
static BhvCommandProc BehaviorCmdTable[] = {
849
bhv_cmd_begin,
850
bhv_cmd_delay,
851
bhv_cmd_call,
852
bhv_cmd_return,
853
bhv_cmd_goto,
854
bhv_cmd_begin_repeat,
855
bhv_cmd_end_repeat,
856
bhv_cmd_end_repeat_continue,
857
bhv_cmd_begin_loop,
858
bhv_cmd_end_loop,
859
bhv_cmd_break,
860
bhv_cmd_break_unused,
861
bhv_cmd_call_native,
862
bhv_cmd_add_float,
863
bhv_cmd_set_float,
864
bhv_cmd_add_int,
865
bhv_cmd_set_int,
866
bhv_cmd_or_int,
867
bhv_cmd_bit_clear,
868
bhv_cmd_set_int_rand_rshift,
869
bhv_cmd_set_random_float,
870
bhv_cmd_set_random_int,
871
bhv_cmd_add_random_float,
872
bhv_cmd_add_int_rand_rshift,
873
bhv_cmd_nop_1,
874
bhv_cmd_nop_2,
875
bhv_cmd_nop_3,
876
bhv_cmd_set_model,
877
bhv_cmd_spawn_child,
878
bhv_cmd_deactivate,
879
bhv_cmd_drop_to_floor,
880
bhv_cmd_sum_float,
881
bhv_cmd_sum_int,
882
bhv_cmd_billboard,
883
bhv_cmd_hide,
884
bhv_cmd_set_hitbox,
885
bhv_cmd_nop_4,
886
bhv_cmd_delay_var,
887
bhv_cmd_begin_repeat_unused,
888
bhv_cmd_load_animations,
889
bhv_cmd_animate,
890
bhv_cmd_spawn_child_with_param,
891
bhv_cmd_load_collision_data,
892
bhv_cmd_set_hitbox_with_offset,
893
bhv_cmd_spawn_obj,
894
bhv_cmd_set_home,
895
bhv_cmd_set_hurtbox,
896
bhv_cmd_set_interact_type,
897
bhv_cmd_set_obj_physics,
898
bhv_cmd_set_interact_subtype,
899
bhv_cmd_scale,
900
bhv_cmd_parent_bit_clear,
901
bhv_cmd_animate_texture,
902
bhv_cmd_disable_rendering,
903
bhv_cmd_set_int_unused,
904
bhv_cmd_spawn_water_droplet,
905
};
906
907
// Execute the behavior script of the current object, process the object flags, and other miscellaneous code for updating objects.
908
void cur_obj_update(void) {
909
UNUSED u32 unused;
910
911
s16 objFlags = gCurrentObject->oFlags;
912
f32 distanceFromMario;
913
BhvCommandProc bhvCmdProc;
914
s32 bhvProcResult;
915
916
// Calculate the distance from the object to Mario.
917
if (objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) {
918
gCurrentObject->oDistanceToMario = dist_between_objects(gCurrentObject, gMarioObject);
919
distanceFromMario = gCurrentObject->oDistanceToMario;
920
} else {
921
distanceFromMario = 0.0f;
922
}
923
924
// Calculate the angle from the object to Mario.
925
if (objFlags & OBJ_FLAG_COMPUTE_ANGLE_TO_MARIO) {
926
gCurrentObject->oAngleToMario = obj_angle_to_object(gCurrentObject, gMarioObject);
927
}
928
929
// If the object's action has changed, reset the action timer.
930
if (gCurrentObject->oAction != gCurrentObject->oPrevAction) {
931
(void) (gCurrentObject->oTimer = 0, gCurrentObject->oSubAction = 0,
932
gCurrentObject->oPrevAction = gCurrentObject->oAction);
933
}
934
935
// Execute the behavior script.
936
gCurBhvCommand = gCurrentObject->curBhvCommand;
937
938
do {
939
bhvCmdProc = BehaviorCmdTable[*gCurBhvCommand >> 24];
940
bhvProcResult = bhvCmdProc();
941
} while (bhvProcResult == BHV_PROC_CONTINUE);
942
943
gCurrentObject->curBhvCommand = gCurBhvCommand;
944
945
// Increment the object's timer.
946
if (gCurrentObject->oTimer < 0x3FFFFFFF) {
947
gCurrentObject->oTimer++;
948
}
949
950
// If the object's action has changed, reset the action timer.
951
if (gCurrentObject->oAction != gCurrentObject->oPrevAction) {
952
(void) (gCurrentObject->oTimer = 0, gCurrentObject->oSubAction = 0,
953
gCurrentObject->oPrevAction = gCurrentObject->oAction);
954
}
955
956
// Execute various code based on object flags.
957
objFlags = (s16) gCurrentObject->oFlags;
958
959
if (objFlags & OBJ_FLAG_SET_FACE_ANGLE_TO_MOVE_ANGLE) {
960
obj_set_face_angle_to_move_angle(gCurrentObject);
961
}
962
963
if (objFlags & OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW) {
964
gCurrentObject->oFaceAngleYaw = gCurrentObject->oMoveAngleYaw;
965
}
966
967
if (objFlags & OBJ_FLAG_MOVE_XZ_USING_FVEL) {
968
cur_obj_move_xz_using_fvel_and_yaw();
969
}
970
971
if (objFlags & OBJ_FLAG_MOVE_Y_WITH_TERMINAL_VEL) {
972
cur_obj_move_y_with_terminal_vel();
973
}
974
975
if (objFlags & OBJ_FLAG_TRANSFORM_RELATIVE_TO_PARENT) {
976
obj_build_transform_relative_to_parent(gCurrentObject);
977
}
978
979
if (objFlags & OBJ_FLAG_SET_THROW_MATRIX_FROM_TRANSFORM) {
980
obj_set_throw_matrix_from_transform(gCurrentObject);
981
}
982
983
if (objFlags & OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE) {
984
obj_update_gfx_pos_and_angle(gCurrentObject);
985
}
986
987
// Handle visibility of object
988
if (gCurrentObject->oRoom != -1) {
989
// If the object is in a room, only show it when Mario is in the room.
990
cur_obj_enable_rendering_if_mario_in_room();
991
} else if ((objFlags & OBJ_FLAG_COMPUTE_DIST_TO_MARIO) && gCurrentObject->collisionData == NULL) {
992
if (!(objFlags & OBJ_FLAG_ACTIVE_FROM_AFAR)) {
993
if (configDrawDistanceMultiplier <= 0.0f) {
994
if (distanceFromMario <= gCurrentObject->oDrawingDistance && gCurrentObject->oHeldState == HELD_FREE)
995
{
996
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
997
gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY;
998
}
999
}
1000
else {
1001
// If the object has a render distance, check if it should be shown.
1002
if (distanceFromMario > gCurrentObject->oDrawingDistance * configDrawDistanceMultiplier) {
1003
// Out of render distance, hide the object.
1004
gCurrentObject->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
1005
gCurrentObject->activeFlags |= ACTIVE_FLAG_FAR_AWAY;
1006
} else if (gCurrentObject->oHeldState == HELD_FREE) {
1007
// In render distance (and not being held), show the object.
1008
gCurrentObject->header.gfx.node.flags |= GRAPH_RENDER_ACTIVE;
1009
gCurrentObject->activeFlags &= ~ACTIVE_FLAG_FAR_AWAY;
1010
}
1011
}
1012
}
1013
}
1014
}
1015