debug.c (18045B)
1 #include <PR/ultratypes.h> 2 3 #include "behavior_data.h" 4 #include "debug.h" 5 #include "engine/behavior_script.h" 6 #include "engine/surface_collision.h" 7 #include "game_init.h" 8 #include "main.h" 9 #include "object_constants.h" 10 #include "object_fields.h" 11 #include "object_helpers.h" 12 #include "object_list_processor.h" 13 #include "print.h" 14 #include "sm64.h" 15 #include "types.h" 16 17 #define DEBUG_INFO_NOFLAGS (0 << 0) 18 #define DEBUG_INFO_FLAG_DPRINT (1 << 0) 19 #define DEBUG_INFO_FLAG_LSELECT (1 << 1) 20 #define DEBUG_INFO_FLAG_ALL 0xFF 21 22 s16 gDebugPrintState1[6]; // prints top-down? 23 s16 gDebugPrintState2[6]; // prints bottom-up? 24 25 enum DebugPrintStateInfo { 26 DEBUG_PSTATE_DISABLED, 27 DEBUG_PSTATE_X_CURSOR, 28 DEBUG_PSTATE_Y_CURSOR, 29 DEBUG_PSTATE_MIN_Y_CURSOR, 30 DEBUG_PSTATE_MAX_X_CURSOR, 31 DEBUG_PSTATE_LINE_Y_OFFSET 32 }; 33 34 // DEBUG_SYS_EFFECTINFO 35 const char *sDebugEffectStringInfo[] = { 36 " a0 %d", " a1 %d", " a2 %d", " a3 %d", " a4 %d", " a5 %d", " a6 %d", " a7 %d", 37 "A" // cursor 38 }; 39 40 // DEBUG_SYS_ENEMYINFO 41 const char *sDebugEnemyStringInfo[] = { 42 " b0 %d", " b1 %d", " b2 %d", " b3 %d", " b4 %d", " b5 %d", " b6 %d", " b7 %d", 43 "B" // cursor 44 }; 45 46 s32 sDebugInfoDPadMask = 0; 47 s32 sDebugInfoDPadUpdID = 0; 48 s8 sDebugLvSelectCheckFlag = FALSE; 49 50 #define DEBUG_PAGE_MIN DEBUG_PAGE_OBJECTINFO 51 #define DEBUG_PAGE_MAX DEBUG_PAGE_ENEMYINFO 52 53 s8 sDebugPage = DEBUG_PAGE_MIN; 54 s8 sNoExtraDebug = FALSE; 55 s8 sDebugStringArrPrinted = FALSE; 56 s8 sDebugSysCursor = 0; 57 s8 sDebugInfoButtonSeqID = 0; 58 s16 sDebugInfoButtonSeq[] = { U_CBUTTONS, L_CBUTTONS, D_CBUTTONS, R_CBUTTONS, -1 }; 59 60 // most likely present in an ifdef DEBUG build. TODO: check DD version? 61 void stub_debug_1(void) { 62 } 63 64 void stub_debug_2(void) { 65 } 66 67 void stub_debug_3(void) { 68 } 69 70 void stub_debug_4(void) { 71 } 72 73 /* 74 * These 2 functions are called from the object list processor in regards to cycle 75 * counts. They likely have stubbed out code that calculated the clock count and 76 * its difference for consecutive calls. 77 */ 78 s64 get_current_clock(void) { 79 s64 wtf = 0; 80 81 return wtf; 82 } 83 84 s64 get_clock_difference(UNUSED s64 cycles) { 85 s64 wtf = 0; 86 87 return wtf; 88 } 89 90 /* 91 * Set the print state info given a pointer to a print state and the relevent 92 * information. Note the reset of the printing boolean. For all intenses and 93 * purposes this creates/formats a new print state. 94 */ 95 void set_print_state_info(s16 *printState, s16 xCursor, s16 yCursor, s16 minYCursor, s16 maxXCursor, 96 s16 lineYOffset) { 97 printState[DEBUG_PSTATE_DISABLED] = FALSE; 98 printState[DEBUG_PSTATE_X_CURSOR] = xCursor; 99 printState[DEBUG_PSTATE_Y_CURSOR] = yCursor; 100 printState[DEBUG_PSTATE_MIN_Y_CURSOR] = minYCursor; 101 printState[DEBUG_PSTATE_MAX_X_CURSOR] = maxXCursor; 102 printState[DEBUG_PSTATE_LINE_Y_OFFSET] = lineYOffset; 103 } 104 105 /* 106 * Take a print state array, string, and the number to print, and use its information to print 107 * the next entry in the list. If the current print state array is too far down the list, this 108 * will print "DPRINT OVER" instead, signaling that the print state overflowed. 109 */ 110 void print_text_array_info(s16 *printState, const char *str, s32 number) { 111 if (!printState[DEBUG_PSTATE_DISABLED]) { 112 if ((printState[DEBUG_PSTATE_Y_CURSOR] < printState[DEBUG_PSTATE_MIN_Y_CURSOR]) 113 || (printState[DEBUG_PSTATE_MAX_X_CURSOR] < printState[DEBUG_PSTATE_Y_CURSOR])) { 114 print_text(printState[DEBUG_PSTATE_X_CURSOR], printState[DEBUG_PSTATE_Y_CURSOR], 115 "DPRINT OVER"); 116 printState[DEBUG_PSTATE_DISABLED]++; // why not just = TRUE... 117 } else { 118 print_text_fmt_int(printState[DEBUG_PSTATE_X_CURSOR], printState[DEBUG_PSTATE_Y_CURSOR], 119 str, number); 120 printState[DEBUG_PSTATE_Y_CURSOR] += printState[DEBUG_PSTATE_LINE_Y_OFFSET]; 121 } 122 } 123 } 124 125 void set_text_array_x_y(s32 xOffset, s32 yOffset) { 126 s16 *printState = gDebugPrintState1; 127 128 printState[DEBUG_PSTATE_X_CURSOR] += xOffset; 129 printState[DEBUG_PSTATE_Y_CURSOR] = 130 yOffset * printState[DEBUG_PSTATE_LINE_Y_OFFSET] + printState[DEBUG_PSTATE_Y_CURSOR]; 131 } 132 133 /* 134 * These series of dprint functions print methods depending on the context of the 135 * current debug mode as well as the printer array (down to up vs up to down). 136 */ 137 void print_debug_bottom_up(const char *str, s32 number) { 138 if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT) { 139 print_text_array_info(gDebugPrintState2, str, number); 140 } 141 } 142 143 void print_debug_top_down_objectinfo(const char *str, s32 number) { 144 if ((gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT) && sDebugPage == DEBUG_PAGE_OBJECTINFO) { 145 print_text_array_info(gDebugPrintState1, str, number); 146 } 147 } 148 149 void print_debug_top_down_mapinfo(const char *str, s32 number) { 150 if (sNoExtraDebug) { // how come this is the only instance of the sNoExtraDebug check? 151 return; 152 } 153 154 if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT) { 155 print_text_array_info(gDebugPrintState1, str, number); 156 } 157 } 158 159 void print_debug_top_down_normal(const char *str, s32 number) { 160 if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT) { 161 print_text_array_info(gDebugPrintState1, str, number); 162 } 163 } 164 165 void print_mapinfo(void) { 166 // EU mostly stubbed this function out. 167 struct Surface *pfloor; 168 UNUSED f32 bgY; // unused in EU 169 UNUSED f32 water; // unused in EU 170 UNUSED s32 area; // unused in EU 171 UNUSED s32 angY; // unused in EU 172 173 angY = gCurrentObject->oMoveAngleYaw / 182.044000; 174 area = ((s32) gCurrentObject->oPosX + 0x2000) / 1024 175 + ((s32) gCurrentObject->oPosZ + 0x2000) / 1024 * 16; 176 177 bgY = find_floor(gCurrentObject->oPosX, gCurrentObject->oPosY, gCurrentObject->oPosZ, &pfloor); 178 water = find_water_level(gCurrentObject->oPosX, gCurrentObject->oPosZ); 179 180 print_debug_top_down_normal("mapinfo", 0); 181 #ifndef VERSION_EU 182 print_debug_top_down_mapinfo("area %x", area); 183 print_debug_top_down_mapinfo("wx %d", gCurrentObject->oPosX); 184 //! Fat finger: programmer hit tab instead of space. Japanese 185 // thumb shift keyboards had the tab key next to the spacebar, 186 // so this was likely the reason. 187 print_debug_top_down_mapinfo("wy\t %d", gCurrentObject->oPosY); 188 print_debug_top_down_mapinfo("wz %d", gCurrentObject->oPosZ); 189 print_debug_top_down_mapinfo("bgY %d", bgY); 190 print_debug_top_down_mapinfo("angY %d", angY); 191 192 if (pfloor != NULL) { 193 print_debug_top_down_mapinfo("bgcode %d", pfloor->type); 194 print_debug_top_down_mapinfo("bgstatus %d", pfloor->flags); 195 print_debug_top_down_mapinfo("bgarea %d", pfloor->room); 196 } 197 198 if (gCurrentObject->oPosY < water) { 199 print_debug_top_down_mapinfo("water %d", water); 200 } 201 #endif 202 } 203 204 void print_checkinfo(void) { 205 print_debug_top_down_normal("checkinfo", 0); 206 } 207 208 void print_surfaceinfo(void) { 209 debug_surface_list_info(gMarioObject->oPosX, gMarioObject->oPosZ); 210 } 211 212 void print_stageinfo(void) { 213 print_debug_top_down_normal("stageinfo", 0); 214 print_debug_top_down_normal("stage param %d", gTTCSpeedSetting); 215 } 216 217 /* 218 * Common printer function for effectinfo and enemyinfo. This function 219 * also prints the cursor functionality intended to be used with modifying 220 * gDebugInfo to set enemy/effect behaviors. 221 */ 222 void print_string_array_info(const char **strArr) { 223 s32 i; 224 225 if (!sDebugStringArrPrinted) { 226 sDebugStringArrPrinted++; // again, why not = TRUE... 227 for (i = 0; i < 8; i++) { 228 // sDebugPage is assumed to be 4 or 5 here. 229 print_debug_top_down_mapinfo(strArr[i], gDebugInfo[sDebugPage][i]); 230 } 231 // modify the cursor position so the cursor prints at the correct location. 232 // this is equivalent to (sDebugSysCursor - 8) 233 set_text_array_x_y(0, -1 - (u32)(7 - sDebugSysCursor)); 234 print_debug_top_down_mapinfo(strArr[8], 0); // print the cursor 235 set_text_array_x_y(0, 7 - sDebugSysCursor); 236 } 237 } 238 239 void print_effectinfo(void) { 240 print_debug_top_down_normal("effectinfo", 0); 241 print_string_array_info(sDebugEffectStringInfo); 242 } 243 244 void print_enemyinfo(void) { 245 print_debug_top_down_normal("enemyinfo", 0); 246 print_string_array_info(sDebugEnemyStringInfo); 247 } 248 249 void update_debug_dpadmask(void) { 250 s32 dPadMask = gPlayer1Controller->buttonDown & (U_JPAD | D_JPAD | L_JPAD | R_JPAD); 251 252 if (!dPadMask) { 253 sDebugInfoDPadUpdID = 0; 254 sDebugInfoDPadMask = 0; 255 } else { 256 // to prevent stuttering of mask updates, the first time is updated 6 257 // frames from start, and then every 2 frames when held down. 258 if (sDebugInfoDPadUpdID == 0) { 259 sDebugInfoDPadMask = dPadMask; 260 } else if (sDebugInfoDPadUpdID == 6) { 261 sDebugInfoDPadMask = dPadMask; 262 } else { 263 sDebugInfoDPadMask = 0; 264 } 265 sDebugInfoDPadUpdID++; 266 if (sDebugInfoDPadUpdID >= 8) { 267 sDebugInfoDPadUpdID = 6; // rapidly set to 6 from 8 as long as dPadMask is being set. 268 } 269 } 270 } 271 272 void debug_unknown_level_select_check(void) { 273 if (!sDebugLvSelectCheckFlag) { 274 sDebugLvSelectCheckFlag++; // again, just do = TRUE... 275 276 if (!gDebugLevelSelect) { 277 gDebugInfoFlags = DEBUG_INFO_NOFLAGS; 278 } else { 279 gDebugInfoFlags = DEBUG_INFO_FLAG_LSELECT; 280 } 281 282 gNumCalls.floor = 0; 283 gNumCalls.ceil = 0; 284 gNumCalls.wall = 0; 285 } 286 } 287 288 void reset_debug_objectinfo(void) { 289 gNumFindFloorMisses = 0; 290 gUnknownWallCount = 0; 291 gObjectCounter = 0; 292 sDebugStringArrPrinted = FALSE; 293 D_8035FEE2 = 0; 294 D_8035FEE4 = 0; 295 296 set_print_state_info(gDebugPrintState1, 20, 185, 40, 200, -15); 297 set_print_state_info(gDebugPrintState2, 180, 30, 0, 150, 15); 298 update_debug_dpadmask(); 299 } 300 301 /* 302 * This function checks for a button sequence (C Up, C Left, C Down, 303 * C Right) and then toggles the debug flags from FF to 2; 2 is unused, 304 * despite so this has no effect, being called. (unused) 305 */ 306 UNUSED static void check_debug_button_seq(void) { 307 s16 *buttonArr = sDebugInfoButtonSeq; 308 s16 cButtonMask; 309 310 if (!(gPlayer1Controller->buttonDown & L_TRIG)) { 311 sDebugInfoButtonSeqID = 0; 312 } else { 313 if ((s16)(cButtonMask = (gPlayer1Controller->buttonPressed & C_BUTTONS))) { 314 if (buttonArr[sDebugInfoButtonSeqID] == cButtonMask) { 315 sDebugInfoButtonSeqID++; 316 if (buttonArr[sDebugInfoButtonSeqID] == -1) { 317 if (gDebugInfoFlags == DEBUG_INFO_FLAG_ALL) { 318 gDebugInfoFlags = DEBUG_INFO_FLAG_LSELECT; 319 } else { 320 gDebugInfoFlags = DEBUG_INFO_FLAG_ALL; 321 } 322 } 323 } else { 324 sDebugInfoButtonSeqID = 0; 325 } 326 } 327 } 328 } 329 330 /* 331 * Poll the debug info flags and controller for appropriate presses that 332 * control sDebugPage's range. (unused) 333 */ 334 UNUSED static void try_change_debug_page(void) { 335 if (gDebugInfoFlags & DEBUG_INFO_FLAG_DPRINT) { 336 if ((gPlayer1Controller->buttonPressed & L_JPAD) 337 && (gPlayer1Controller->buttonDown & (L_TRIG | R_TRIG))) { 338 sDebugPage++; 339 } 340 if ((gPlayer1Controller->buttonPressed & R_JPAD) 341 && (gPlayer1Controller->buttonDown & (L_TRIG | R_TRIG))) { 342 sDebugPage--; 343 } 344 if (sDebugPage >= (DEBUG_PAGE_MAX + 1)) { 345 sDebugPage = DEBUG_PAGE_MIN; 346 } 347 if (sDebugPage < DEBUG_PAGE_MIN) { 348 sDebugPage = DEBUG_PAGE_MAX; 349 } 350 } 351 } 352 353 /* 354 * Use the controller to modify gDebugInfo and the sys cursor while 355 * using the DPad. L/R DPad modifies gDebugInfo while U/D modifies 356 * sDebugSysCursor. This is used to adjust enemy and effect behaviors 357 * on the fly. (unused) 358 */ 359 #ifdef VERSION_EU 360 UNUSED static 361 #endif 362 void try_modify_debug_controls(void) { 363 s32 sp4; 364 365 if (gPlayer1Controller->buttonPressed & Z_TRIG) { 366 sNoExtraDebug ^= 1; 367 } 368 if (!(gPlayer1Controller->buttonDown & (L_TRIG | R_TRIG)) && !sNoExtraDebug) { 369 sp4 = 1; 370 if (gPlayer1Controller->buttonDown & B_BUTTON) { 371 sp4 = 100; 372 } 373 374 if (sDebugInfoDPadMask & U_JPAD) { 375 sDebugSysCursor--; 376 if (sDebugSysCursor < 0) { 377 sDebugSysCursor = 0; 378 } 379 } 380 381 if (sDebugInfoDPadMask & D_JPAD) { 382 sDebugSysCursor++; 383 if (sDebugSysCursor >= 8) { 384 sDebugSysCursor = 7; 385 } 386 } 387 388 if (sDebugInfoDPadMask & L_JPAD) { 389 // we allow the player while in this mode to modify the debug controls. This is 390 // so the playtester can adjust enemy behavior and parameters on the fly, since 391 // various behaviors try to update their behaviors from gDebugInfo[4] and [5]. 392 if (gPlayer1Controller->buttonDown & A_BUTTON) { 393 gDebugInfo[sDebugPage][sDebugSysCursor] = 394 gDebugInfoOverwrite[sDebugPage][sDebugSysCursor]; 395 } else { 396 gDebugInfo[sDebugPage][sDebugSysCursor] = gDebugInfo[sDebugPage][sDebugSysCursor] - sp4; 397 } 398 } 399 400 if (sDebugInfoDPadMask & R_JPAD) { 401 gDebugInfo[sDebugPage][sDebugSysCursor] = gDebugInfo[sDebugPage][sDebugSysCursor] + sp4; 402 } 403 } 404 } 405 406 // possibly a removed debug control (TODO: check DD) 407 void stub_debug_5(void) { 408 } 409 410 /* 411 * If Mario's object exists, this function tries to print available object debug 412 * information depending on the debug sys ID. Additional information (updated obj 413 * count, floor misses, and an unknown wall counter) is also printed. 414 */ 415 void try_print_debug_mario_object_info(void) { 416 if (gMarioObject != NULL) { 417 switch (sDebugPage) { 418 case DEBUG_PAGE_CHECKSURFACEINFO: 419 print_surfaceinfo(); 420 break; 421 case DEBUG_PAGE_EFFECTINFO: 422 print_effectinfo(); 423 break; 424 case DEBUG_PAGE_ENEMYINFO: 425 print_enemyinfo(); 426 break; 427 default: 428 break; 429 } 430 } 431 432 print_debug_top_down_mapinfo("obj %d", gObjectCounter); 433 434 if (gNumFindFloorMisses != 0) { 435 print_debug_bottom_up("NULLBG %d", gNumFindFloorMisses); 436 } 437 438 if (gUnknownWallCount != 0) { 439 print_debug_bottom_up("WALL %d", gUnknownWallCount); 440 } 441 } 442 443 /* 444 * Similar to above, but with level information. (checkinfo, mapinfo, 445 * stageinfo) 446 */ 447 void try_print_debug_mario_level_info(void) { 448 switch (sDebugPage) { 449 case DEBUG_PAGE_OBJECTINFO: 450 break; // no info list is printed for obj info. 451 case DEBUG_PAGE_CHECKSURFACEINFO: 452 print_checkinfo(); 453 break; 454 case DEBUG_PAGE_MAPINFO: 455 print_mapinfo(); 456 break; 457 case DEBUG_PAGE_STAGEINFO: 458 print_stageinfo(); 459 break; 460 default: 461 break; 462 } 463 } 464 465 /* 466 * One of the only remaining debug controls activatable from the 467 * debug control array. This function lets you spawn 1 of 3 468 * objects: A koopa shell, a jumping box, and an underwater koopa 469 * shell. This can be reactivated by turning on modifying 470 * debug controls with try_modify_debug_controls and setting 471 * [5][7] (b7 in the string array) to 1 to enable debug spawn. 472 */ 473 void try_do_mario_debug_object_spawn(void) { 474 UNUSED u8 filler[4]; 475 476 if (sDebugPage == DEBUG_PAGE_STAGEINFO && gDebugInfo[DEBUG_PAGE_ENEMYINFO][7] == 1) { 477 if (gPlayer1Controller->buttonPressed & R_JPAD) { 478 spawn_object_relative(0, 0, 100, 200, gCurrentObject, MODEL_KOOPA_SHELL, bhvKoopaShell); 479 } 480 if (gPlayer1Controller->buttonPressed & L_JPAD) { 481 spawn_object_relative(0, 0, 100, 200, gCurrentObject, MODEL_BREAKABLE_BOX_SMALL, 482 bhvJumpingBox); 483 } 484 if (gPlayer1Controller->buttonPressed & D_JPAD) { 485 spawn_object_relative(0, 0, 100, 200, gCurrentObject, MODEL_KOOPA_SHELL, 486 bhvKoopaShellUnderwater); 487 } 488 } 489 } 490 491 // TODO: figure out what this is 492 void debug_print_obj_move_flags(void) { 493 #ifndef VERSION_EU // TODO: Is there a better way to diff this? static EU doesn't seem to work. 494 if (gCurrentObject->oMoveFlags & OBJ_MOVE_LANDED) { 495 print_debug_top_down_objectinfo("BOUND %x", gCurrentObject->oMoveFlags); 496 } 497 if (gCurrentObject->oMoveFlags & OBJ_MOVE_ON_GROUND) { 498 print_debug_top_down_objectinfo("TOUCH %x", gCurrentObject->oMoveFlags); 499 } 500 if (gCurrentObject->oMoveFlags & OBJ_MOVE_LEFT_GROUND) { 501 print_debug_top_down_objectinfo("TAKEOFF %x", gCurrentObject->oMoveFlags); 502 } 503 if (gCurrentObject->oMoveFlags & OBJ_MOVE_ENTERED_WATER) { 504 print_debug_top_down_objectinfo("DIVE %x", gCurrentObject->oMoveFlags); 505 } 506 if (gCurrentObject->oMoveFlags & OBJ_MOVE_AT_WATER_SURFACE) { 507 print_debug_top_down_objectinfo("S WATER %x", gCurrentObject->oMoveFlags); 508 } 509 if (gCurrentObject->oMoveFlags & OBJ_MOVE_UNDERWATER_OFF_GROUND) { 510 print_debug_top_down_objectinfo("U WATER %x", gCurrentObject->oMoveFlags); 511 } 512 if (gCurrentObject->oMoveFlags & OBJ_MOVE_UNDERWATER_ON_GROUND) { 513 print_debug_top_down_objectinfo("B WATER %x", gCurrentObject->oMoveFlags); 514 } 515 if (gCurrentObject->oMoveFlags & OBJ_MOVE_IN_AIR) { 516 print_debug_top_down_objectinfo("SKY %x", gCurrentObject->oMoveFlags); 517 } 518 if (gCurrentObject->oMoveFlags & OBJ_MOVE_OUT_SCOPE) { 519 print_debug_top_down_objectinfo("OUT SCOPE %x", gCurrentObject->oMoveFlags); 520 } 521 #endif 522 } 523 524 // unused, what is this? 525 void debug_enemy_unknown(s16 *enemyArr) { 526 // copy b1-b4 over to an unknown s16 array 527 enemyArr[4] = gDebugInfo[DEBUG_PAGE_ENEMYINFO][1]; 528 enemyArr[5] = gDebugInfo[DEBUG_PAGE_ENEMYINFO][2]; 529 enemyArr[6] = gDebugInfo[DEBUG_PAGE_ENEMYINFO][3]; 530 enemyArr[7] = gDebugInfo[DEBUG_PAGE_ENEMYINFO][4]; 531 }