#include "common.h" #include "nu/nusys.h" #include "ld_addrs.h" #include "hud_element.h" #include "sprite.h" #include "overlay.h" s8 D_80074020 = 1; s8 D_80074021 = 5; GameStatus gGameStatus = {0}; GameStatus* gGameStatusPtr = &gGameStatus; s16 D_800741A0 = 0; s16 D_800741A2 = 0; s32 D_800741A4 = 0; s32 D_800741A8[] = { 0x00010000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; u16 gMatrixListPos = 0; u16 D_800741F2 = 0; s32 gCurrentDisplayContextIndex = 0; s32 D_800741F8 = 0; s32 D_800741FC = 0; s32 D_80074200[] = { 0x028001E0, 0x01FF0000, 0x028001E0, 0x01FF0000 }; Gfx D_80074210[] = { gsDPSetRenderMode(G_RM_OPA_SURF, G_RM_OPA_SURF2), gsDPSetCombineMode(G_CC_SHADE, G_CC_SHADE), gsDPSetColorDither(G_CD_BAYER), gsSPEndDisplayList(), }; Gfx D_80074230[] = { gsSPViewport(&D_80074200), gsSPClearGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR | G_LOD | G_SHADING_SMOOTH | G_CLIPPING | 0x0040F9FA), gsSPSetGeometryMode(G_ZBUFFER | G_SHADE | G_CULL_BACK | G_SHADING_SMOOTH), gsSPTexture(0, 0, 0, G_TX_RENDERTILE, G_OFF), gsSPEndDisplayList(), }; // BSS extern BSS s32 timeFreezeMode; extern s16 D_8009A690; void gfx_init_state(void); void gfx_draw_background(void); void step_game_loop(void) { PlayerData* playerData = &gPlayerData; update_input(); gGameStatusPtr->frameCounter++; playerData->frameCounter += 2; if (playerData->frameCounter > 215999999) { playerData->frameCounter = 215999999; } update_max_rumble_duration(); if (D_80074021 != 0) { D_80074021-- ; if (D_80074021 == 0) { D_80074021 = D_80074020; } else { return; } } func_8011BAE8(); npc_iter_no_op(); update_generic_entities(); update_triggers(); update_scripts(); update_messages(); update_hud_elements(); step_current_game_mode(); update_entities(); func_80138198(); bgm_update_music_settings(); update_ambient_sounds(); sfx_update_looping_sound_params(); update_windows(); update_curtains(); if (gOverrideFlags & 0x20) { switch (D_800741A2) { case 0: gOverrideFlags |= 0x200; disable_player_input(); if (D_800741A0 == 255) { D_800741A2 = 1; D_8009A690 = 3; } else { D_800741A0 += 10; if (D_800741A0 > 255) { D_800741A0 = 255; } } break; case 1: gOverrideFlags |= 0x8; D_8009A690--; if (D_8009A690 == 0) { sfx_stop_env_sounds(); set_game_mode(0); gOverrideFlags &= ~0x20; } break; } } else { D_800741A0 = 0; D_800741A2 = 0; } if (gOverrideFlags & 0x100) { gOverrideFlags |= 0x1000; } else { gOverrideFlags &= ~0x1000; } if (gOverrideFlags & 0x200) { gOverrideFlags |= 0x2000; } else { gOverrideFlags &= ~0x2000; } if (gOverrideFlags & 0x400) { gOverrideFlags |= 0x4000; } else { gOverrideFlags &= ~0x4000; } if (gOverrideFlags & 0x800) { gOverrideFlags |= 0x8000; } else { gOverrideFlags &= ~0x8000; } rand_int(1); } void gfx_task_background(void) { gDisplayContext = &D_80164000[gCurrentDisplayContextIndex]; gMasterGfxPos = &gDisplayContext->backgroundGfx[0]; gfx_init_state(); gfx_draw_background(); gDPFullSync(gMasterGfxPos++); gSPEndDisplayList(gMasterGfxPos++); // TODO these << 3 >> 3 shouldn't be necessary. There's almost definitely something we're missing here... ASSERT((s32)((u32)((gMasterGfxPos - gDisplayContext->backgroundGfx) << 3) >> 3) < ARRAY_COUNT( gDisplayContext->backgroundGfx)) nuGfxTaskStart(&gDisplayContext->backgroundGfx[0], (u32)(gMasterGfxPos - gDisplayContext->backgroundGfx) * 8, NU_GFX_UCODE_F3DEX2, NU_SC_NOSWAPBUFFER); } void gfx_draw_frame(void) { gMatrixListPos = 0; gMasterGfxPos = &gDisplayContext->mainGfx[0]; if (gOverrideFlags & 8) { gCurrentDisplayContextIndex = gCurrentDisplayContextIndex ^ 1; return; } gSPMatrix(gMasterGfxPos++, D_800741A8, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); spr_render_init(); if (!(gOverrideFlags & 2)) { render_frame(0); } player_render_interact_prompts(); func_802C3EE4(); render_screen_overlay_backUI(); render_generic_entities_backUI(); render_hud_elements_backUI(); render_effects_UI(); state_render_backUI(); if (!(gOverrideFlags & 0x10000)) { render_window_root(); } if (!(gOverrideFlags & 2) && gGameStatusPtr->disableScripts == 0) { render_frame(1); } if (!(gOverrideFlags & 0x100010)) { render_messages(); } render_generic_entities_frontUI(); render_hud_elements_frontUI(); render_screen_overlay_frontUI(); if ((gOverrideFlags & 0x100010) == 0x10) { render_messages(); } render_curtains(); if (gOverrideFlags & 0x100000) { render_messages(); } if (gOverrideFlags & 0x10000) { render_window_root(); } state_render_frontUI(); if (gOverrideFlags & 0x20) { switch (D_800741A2) { case 0: case 1: _render_transition_stencil(7, D_800741A0, NULL); break; } } ASSERT((s32)(((u32)(gMasterGfxPos - gDisplayContext->mainGfx) << 3) >> 3) < ARRAY_COUNT(gDisplayContext->mainGfx)); gDPFullSync(gMasterGfxPos++); gSPEndDisplayList(gMasterGfxPos++); nuGfxTaskStart(gDisplayContext->mainGfx, (u32)(gMasterGfxPos - gDisplayContext->mainGfx) * 8, NU_GFX_UCODE_F3DEX2, NU_SC_TASK_LODABLE | NU_SC_SWAPBUFFER); gCurrentDisplayContextIndex = gCurrentDisplayContextIndex ^ 1; crash_screen_set_draw_info(nuGfxCfb_ptr, SCREEN_WIDTH, SCREEN_HEIGHT); } void load_engine_data(void) { s32 i; dma_copy(FEE30_ROM_START, FEE30_ROM_END, FEE30_VRAM); dma_copy(_759B0_ROM_START, _759B0_ROM_END, _759B0_VRAM); dma_copy(evt_ROM_START, evt_ROM_END, evt_VRAM); dma_copy(_102610_ROM_START, _102610_ROM_END, _102610_VRAM); dma_copy(A5DD0_ROM_START, A5DD0_ROM_END, A5DD0_VRAM); dma_copy(_10CC10_ROM_START, _10CC10_ROM_END, _10CC10_VRAM); gOverrideFlags = 0; gGameStatusPtr->unk_79 = 0; gGameStatusPtr->enableBackground = 0; gGameStatusPtr->musicEnabled = 1; gGameStatusPtr->unk_7C = 1; gGameStatusPtr->creditsViewportMode = -1; gGameStatusPtr->demoFlags = 0; gGameStatusPtr->unk_81 = 0; gGameStatusPtr->unk_82 = -8; gGameStatusPtr->unk_83 = 4; timeFreezeMode = 0; gGameStatusPtr->debugQuizmo = gGameStatusPtr->unk_13C = 0; D_80074021 = 5; gGameStatusPtr->saveCount = 0; fio_init_flash(); func_80028838(); general_heap_create(); clear_render_tasks(); clear_generic_entity_list(); clear_script_list(); create_cameras_a(); clear_player_status(); spr_init_sprites(0); clear_entity_models(); clear_animator_list(); clear_model_data(); clear_sprite_shading_data(); reset_background_settings(); clear_character_set(); clear_printers(); clear_game_modes(); clear_npcs(); clear_hud_element_cache(); clear_trigger_data(); clear_entity_data(0); clear_player_data(); init_encounter_status(); clear_screen_overlays(); clear_effect_data(); clear_saved_variables(); clear_item_entity_data(); bgm_reset_sequence_players(); reset_ambient_sounds(); sfx_clear_sounds(); clear_windows(); initialize_curtains(); poll_rumble(); for (i = 0; i < ARRAY_COUNT(gGameStatusPtr->unk_50); i++) { gGameStatusPtr->unk_50[i] = 3; gGameStatusPtr->unk_48[i] = 12; } gOverrideFlags |= 0x8; set_game_mode(0); } /// Time freeze modes: /// 0: normal /// 1: NPCs move, can't be interacted with /// 2: NPCs don't move, no partner ability, can't interact, can't use exits /// 3: NPCs don't more or animate (partner switch menu) /// 4: NPCs can move, animations don't update, can use exits void set_time_freeze_mode(s32 mode) { switch (mode) { case 0: timeFreezeMode = mode; gOverrideFlags &= ~0xF00; resume_all_group(3); break; case 1: timeFreezeMode = mode; gOverrideFlags &= ~0xE00; gOverrideFlags |= 0x100; suspend_all_group(1); break; case 2: timeFreezeMode = mode; gOverrideFlags &= ~0xC00; gOverrideFlags |= 0x300; suspend_all_group(2); break; case 3: timeFreezeMode = mode; gOverrideFlags &= ~0x800; gOverrideFlags |= 0x700; suspend_all_group(2); break; case 4: timeFreezeMode = mode; gOverrideFlags |= 0xF00; break; } } s32 get_time_freeze_mode(void) { return timeFreezeMode; } void gfx_init_state(void) { gSPSegment(gMasterGfxPos++, 0x00, 0x0); gSPDisplayList(gMasterGfxPos++, OS_K0_TO_PHYSICAL(D_80074230)); gSPDisplayList(gMasterGfxPos++, OS_K0_TO_PHYSICAL(D_80074210)); } s32 func_800271FC(const u16* framebuf1, const u16* framebuf2, s32 y, s32 x, u8* out) { s32 pixel = SCREEN_WIDTH * y + x; out[3] = (framebuf2[pixel] >> 2) & 0xF; out[0] = framebuf1[pixel] >> 11; // red out[1] = (framebuf1[pixel] >> 6) & 0x1F; // green out[2] = (framebuf1[pixel] >> 1) & 0x1F; // blue } void func_8002725C(u8*, u32, u16*); INCLUDE_ASM(void, "main_loop", func_8002725C, u8* arg0, u32 arg1, u16* arg2); INCLUDE_ASM(s32, "main_loop", func_80027600); INCLUDE_ASM(s32, "main_loop", func_80027774); void func_800279B4(u16* arg0, u16* arg1, u16* arg2) { u8 filterBuf[0x18]; u8 sp30[4]; s32 y; s32 x; for (y = 1; y < SCREEN_HEIGHT - 1; y++) { for (x = 2; x < SCREEN_WIDTH - 2; x++) { s32 pixel = SCREEN_WIDTH * y + x; if (((arg1[pixel] >> 2) & 0xF) < 8) { func_800271FC(arg0, arg1, y - 1, x - 1, &filterBuf[0x00]); func_800271FC(arg0, arg1, y - 1, x + 1, &filterBuf[0x04]); func_800271FC(arg0, arg1, y, x - 2, &filterBuf[0x08]); func_800271FC(arg0, arg1, y, x + 2, &filterBuf[0x0C]); func_800271FC(arg0, arg1, y + 1, x - 1, &filterBuf[0x10]); func_800271FC(arg0, arg1, y + 1, x + 1, &filterBuf[0x14]); func_800271FC(arg0, arg1, y, x, &sp30); func_8002725C(&filterBuf, (sp30[0] << 24) | (sp30[1] << 16) | (sp30[2] << 8) | sp30[3], &arg2[pixel]); } else { // Edge arg2[pixel] = arg0[pixel] | 1; } } } } void func_80027BAC(s32 arg0, s32 arg1) { s32 i; s32 temp = 24; // todo figure out why this is needed and can't be used elsewhere gDPPipeSync(gMasterGfxPos++); gSPTexture(gMasterGfxPos++, -1, -1, 0, G_TX_RENDERTILE, G_ON); gDPSetColorImage(gMasterGfxPos++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, arg1); gDPSetCycleType(gMasterGfxPos++, G_CYC_COPY); gDPSetTexturePersp(gMasterGfxPos++, G_TP_NONE); gDPSetTextureLUT(gMasterGfxPos++, G_TT_NONE); gDPSetRenderMode(gMasterGfxPos++, G_RM_NOOP, G_RM_NOOP2); gDPSetTextureFilter(gMasterGfxPos++, G_TF_POINT); for (i = 0; i < 40; i++) { gDPLoadTextureTile(gMasterGfxPos++, arg0 + (0xF00 * i), G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH - 1, 5, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); gSPTextureRectangle(gMasterGfxPos++, 0, i * temp, 0x04FC, (i * 24) + 20, G_TX_RENDERTILE, 0, 0, 0x1000, 0x0400); gDPPipeSync(gMasterGfxPos++); } } void gfx_draw_background(void) { Camera* camera; s32 bgFlags; s32 backgroundMinW; s32 backgroundSumW; s32 backgroundMinH; s32 backgroundSumH; s32 viewportStartX; s32 i; s32 a = 0x18; gDPSetScissor(gMasterGfxPos++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); camera = &gCameras[gCurrentCameraID]; bgFlags = gGameStatusPtr->enableBackground & 0xF0; switch (bgFlags) { case 0x10: gDPPipeSync(gMasterGfxPos++); gDPSetColorImage(gMasterGfxPos++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, nuGfxCfb[1]); gDPSetCycleType(gMasterGfxPos++, G_CYC_1CYCLE); gDPSetBlendColor(gMasterGfxPos++, 0x80, 0x80, 0x80, 0xFF); gDPSetPrimDepth(gMasterGfxPos++, -1, -1); gDPSetDepthSource(gMasterGfxPos++, G_ZS_PRIM); gDPSetRenderMode(gMasterGfxPos++, G_RM_VISCVG, G_RM_VISCVG2); gDPFillRectangle(gMasterGfxPos++, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); gDPPipeSync(gMasterGfxPos++); gDPSetDepthSource(gMasterGfxPos++, G_ZS_PIXEL); gGameStatusPtr->enableBackground &= ~0xF0; gGameStatusPtr->enableBackground |= 0x20; break; case 0x20: func_800279B4(nuGfxCfb[0], nuGfxCfb[1], nuGfxZBuffer); D_800741F8 = 0; gGameStatusPtr->enableBackground &= ~0xF0; gGameStatusPtr->enableBackground |= 0x30; case 0x30: D_800741F8 += 0x10; if (D_800741F8 > 0x80) { D_800741F8 = 0x80; } gDPPipeSync(gMasterGfxPos++); gDPSetScissor(gMasterGfxPos++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); gDPSetCycleType(gMasterGfxPos++, G_CYC_FILL); gDPSetRenderMode(gMasterGfxPos++, G_RM_NOOP, G_RM_NOOP2); gDPSetColorImage(gMasterGfxPos++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, nuGfxCfb_ptr); gDPSetFillColor(gMasterGfxPos++, 0x00010001); gDPFillRectangle(gMasterGfxPos++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); gDPSetCycleType(gMasterGfxPos++, G_CYC_1CYCLE); gDPSetTexturePersp(gMasterGfxPos++, G_TP_NONE); gDPSetTextureLUT(gMasterGfxPos++, G_TT_NONE); gDPSetRenderMode(gMasterGfxPos++, G_RM_OPA_SURF, G_RM_OPA_SURF2); gDPSetCombineLERP(gMasterGfxPos++, PRIMITIVE, TEXEL0, PRIMITIVE_ALPHA, TEXEL0, 0, 0, 0, 1, PRIMITIVE, TEXEL1, PRIMITIVE_ALPHA, TEXEL1, 0, 0, 0, 1); gDPSetPrimColor(gMasterGfxPos++, 0, 0, 0x28, 0x28, 0x28, D_800741F8); gDPSetTextureFilter(gMasterGfxPos++, G_TF_POINT); for (i = 0; i < 40; i++) { gDPLoadTextureTile(gMasterGfxPos++, nuGfxZBuffer + (i * 0x780), G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WIDTH - 1, 5, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); gSPTextureRectangle(gMasterGfxPos++, 0, i * a, 0x0500, a + (i * 0x18), G_TX_RENDERTILE, -0x0020, 0, 0x0400, 0x0400); gDPPipeSync(gMasterGfxPos++); } break; default: if (gOverrideFlags & 8) { gDPSetColorImage(gMasterGfxPos++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, osVirtualToPhysical(nuGfxCfb_ptr)); return; } gDPSetDepthImage(gMasterGfxPos++, OS_PHYSICAL_TO_K0(nuGfxZBuffer)); // TODO: or OS_K0_TO_PHYSICAL gDPSetCycleType(gMasterGfxPos++, G_CYC_FILL); gDPSetRenderMode(gMasterGfxPos++, G_RM_NOOP, G_RM_NOOP2); gDPSetColorImage(gMasterGfxPos++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, OS_PHYSICAL_TO_K0(nuGfxZBuffer)); gDPSetFillColor(gMasterGfxPos++, 0xFFFCFFFC); gDPFillRectangle(gMasterGfxPos++, 0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); gDPPipeSync(gMasterGfxPos++); gDPSetColorImage(gMasterGfxPos++, G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, osVirtualToPhysical(nuGfxCfb_ptr)); gDPSetFillColor(gMasterGfxPos++, PACK_FILL_COLOR(camera->bgColor[0], camera->bgColor[1], camera->bgColor[2], 1)); backgroundMinW = gGameStatusPtr->backgroundMinW; backgroundSumW = backgroundMinW + gGameStatusPtr->backgroundMaxW; backgroundMinH = gGameStatusPtr->backgroundMinH; backgroundSumH = backgroundMinH + gGameStatusPtr->backgroundMaxH; viewportStartX = camera->viewportStartX; if (backgroundMinW < viewportStartX) { backgroundMinW = viewportStartX; } if (backgroundMinH < camera->viewportStartY) { backgroundMinH = camera->viewportStartY; } if (backgroundSumW > viewportStartX + camera->viewportW) { backgroundSumW = viewportStartX + camera->viewportW; } if (backgroundSumH > camera->viewportStartY + camera->viewportH) { backgroundSumH = camera->viewportStartY + camera->viewportH; } if (backgroundMinW < 0) { backgroundMinW = 0; } if (backgroundMinH < 0) { backgroundMinH = 0; } if (backgroundSumW < 1) { backgroundSumW = 1; } if (backgroundSumH < 1) { backgroundSumH = 1; } if (backgroundMinW > SCREEN_WIDTH - 1) { backgroundMinW = SCREEN_WIDTH - 1; } if (backgroundMinH > SCREEN_HEIGHT - 1) { backgroundMinH = SCREEN_HEIGHT - 1; } if (backgroundSumW > SCREEN_WIDTH) { backgroundSumW = SCREEN_WIDTH; } if (backgroundSumH > SCREEN_HEIGHT) { backgroundSumH = SCREEN_HEIGHT; } if (!(gGameStatusPtr->enableBackground & 1)) { gDPFillRectangle(gMasterGfxPos++, backgroundMinW, backgroundMinH, backgroundSumW - 1, backgroundSumH - 1); } else { appendGfx_background_texture(); } gDPPipeSync(gMasterGfxPos++); gDPSetCycleType(gMasterGfxPos++, G_CYC_FILL); gDPSetRenderMode(gMasterGfxPos++, G_RM_NOOP, G_RM_NOOP2); gDPSetFillColor(gMasterGfxPos++, 0x00010001); gDPPipeSync(gMasterGfxPos++); if (backgroundMinH > 0) { gDPFillRectangle(gMasterGfxPos++, 0, 0, SCREEN_WIDTH - 1, backgroundMinH - 1); gDPNoOp(gMasterGfxPos++); } if (backgroundMinW > 0) { gDPFillRectangle(gMasterGfxPos++, 0, backgroundMinH, backgroundMinW - 1, backgroundSumH - 1); gDPNoOp(gMasterGfxPos++); } if (backgroundSumW < SCREEN_WIDTH) { gDPFillRectangle(gMasterGfxPos++, backgroundSumW, backgroundMinH, SCREEN_WIDTH - 1, backgroundSumH - 1); gDPNoOp(gMasterGfxPos++); } if (backgroundSumH < 0xF0) { gDPFillRectangle(gMasterGfxPos++, 0, backgroundSumH, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1); gDPNoOp(gMasterGfxPos++); } break; } gDPPipeSync(gMasterGfxPos++); }