#include "m_scene.h" #include "m_player_lib.h" #include "libultra/libultra.h" #include "jsyswrap.h" #include "m_play.h" #include "m_common_data.h" #include "m_npc.h" #include "m_name_table.h" #include "m_card.h" #include "m_submenu.h" #include "m_scene_ftr.h" static void mSc_set_bank_status_after(Object_Bank_c* bank) { bank->bank_id = ABS(bank->bank_id); bank->ram_start = bank->dma_start; bank->state = 0; } static void mSc_clear_bank_status(Object_Bank_c* bank) { bank->bank_id = 0; bank->size = 0; bank->dma_start = NULL; bank->ram_start = NULL; } static void Object_Exchange_keep_new_Player(GAME_PLAY* play) { mPlib_Object_Exchange_keep_new_PlayerMdl(play); mPlib_Object_Exchange_keep_new_PlayerTex(play, 0, 0); mPlib_Object_Exchange_keep_new_PlayerPallet(play, 0, 0); mPlib_Object_Exchange_keep_new_PlayerTex(play, 1, 2); mPlib_Object_Exchange_keep_new_PlayerPallet(play, 1, 2); mPlib_Object_Exchange_keep_new_PlayerFaceTex(play); mPlib_Object_Exchange_keep_new_PlayerFacePallet(play, 1, 0); mPlib_Object_Exchange_keep_new_PlayerFacePallet(play, 2, 1); } extern char* mSc_secure_exchange_keep_bank(Object_Exchange_c* exchange, s16 bank_id, size_t size) { char* area = NULL; Object_Bank_c* bank = exchange->banks + exchange->bank_idx; if (exchange->bank_idx < mSc_OBJECT_BANK_NUM) { area = (char*)ALIGN_NEXT((u32)exchange->next_bank_ram_address + size, 32); if (area >= exchange->max_ram_address) { area = NULL; } else { bank->bank_id = bank_id; bank->ram_start = exchange->next_bank_ram_address; bank->dma_start = exchange->next_bank_ram_address; bank->rom_addr = 0; bank->size = size; bank->num_exist = 0; bank->state = 3; exchange->next_bank_ram_address = area; exchange->bank_idx++; } } return area; } extern void mSc_background_dmacopy_controller(Object_Bank_c* bank) { switch (bank->state) { case 1: { if (bank->_14 == 0) { osCreateMesgQueue(&bank->dma_controller_msg_queue, &bank->dma_controller_msg, 1); _JW_GetResourceAram(bank->rom_addr, (u8*)bank->dma_start, bank->size); DCStoreRangeNoSync(bank->dma_start, bank->size); bank->state = 0; } break; } case 2: { if (osRecvMesg(&bank->dma_controller_msg_queue, NULL, OS_MESSAGE_NOBLOCK) == FALSE) { mSc_set_bank_status_after(bank); } break; } } } extern void mSc_dmacopy_data_bank(Object_Exchange_c* exchange) { /* stubbed */ } extern int mSc_bank_regist_check(Object_Exchange_c* exchange, s16 bank_id) { int res = -1; int i; for (i = 0; i < mSc_OBJECT_BANK_NUM; i++) { if (ABS(exchange->banks[i].bank_id) == bank_id) { res = i; break; } } return res; } extern void mSc_regist_initial_exchange_bank(GAME_PLAY* play) { play->object_exchange.selected_partition = 0; play->object_exchange.exchange_id = play->object_exchange.bank_idx; if (Common_Get(field_type) == mFI_FIELDTYPE2_FG) { u32 size; /* Split the remaining object exchange space into two equal portions */ size = (u32)(play->object_exchange.max_ram_address - play->object_exchange.next_bank_ram_address) / 2; play->object_exchange.start_address_save[0] = play->object_exchange.next_bank_ram_address; play->object_exchange.end_address_save[0] = (char*)ALIGN_NEXT((u32)play->object_exchange.next_bank_ram_address + size, 32); play->object_exchange.start_address_save[1] = play->object_exchange.end_address_save[0]; play->object_exchange.end_address_save[1] = play->object_exchange.max_ram_address; /* Temporarily limit the maximum address to half the remaining free space */ play->object_exchange.max_ram_address = play->object_exchange.end_address_save[0]; } else { play->object_exchange.start_address_save[0] = play->object_exchange.next_bank_ram_address; play->object_exchange.end_address_save[0] = play->object_exchange.max_ram_address; play->object_exchange.start_address_save[1] = play->object_exchange.next_bank_ram_address; play->object_exchange.end_address_save[1] = play->object_exchange.max_ram_address; } play->object_exchange.exchange_id = play->object_exchange.bank_idx; /* leftover from DnM? */ } static void mSc_dmacopy_all_exchange_bank_sub(Object_Bank_c* bank, Object_Exchange_c* exchange, int idx) { if (idx >= exchange->exchange_id) { char* area = (char*)ALIGN_NEXT((u32)exchange->next_bank_ram_address + bank->size, 32); if (area >= exchange->max_ram_address) { exchange->selected_partition = (exchange->selected_partition + 1) % 2; exchange->next_bank_ram_address = exchange->start_address_save[exchange->selected_partition]; exchange->max_ram_address = exchange->end_address_save[exchange->selected_partition]; area = (char*)ALIGN_NEXT((u32)exchange->next_bank_ram_address + bank->size, 32); } bank->dma_start = exchange->next_bank_ram_address; bank->part_id = exchange->selected_partition; exchange->next_bank_ram_address = area; } _JW_GetResourceAram(bank->rom_addr, (u8*)bank->dma_start, bank->size); DCStoreRangeNoSync(bank->dma_start, bank->size); mSc_set_bank_status_after(bank); } extern void mSc_dmacopy_all_exchange_bank(Object_Exchange_c* exchange) { Object_Bank_c* bank; int i; bank = exchange->banks + exchange->keep_id; exchange->next_bank_ram_address = exchange->start_address_save[exchange->selected_partition]; for (i = exchange->keep_id; i < mSc_OBJECT_BANK_NUM; i++) { if (bank->bank_id != 0 && bank->state != 3) { if (bank->bank_id < 0) { mSc_dmacopy_all_exchange_bank_sub(bank, exchange, i); } else if (bank->num_exist != 0) { mSc_dmacopy_all_exchange_bank_sub(bank, exchange, i); } else if (i >= exchange->exchange_id) { mSc_clear_bank_status(bank); } else { bank->bank_id = 0; } } bank++; } if (Common_Get(clip).npc_clip != NULL) { Common_Get(clip).npc_clip->rebuild_dma_proc(); } if (Common_Get(clip)._068 != NULL) { (*Common_Get(clip)._068)(); } if (Common_Get(clip).shop_manekin_clip != NULL) { Common_Get(clip).shop_manekin_clip->manekin_dma_again_proc(); } if (Common_Get(clip).shop_indoor_clip != NULL) { Common_Get(clip).shop_indoor_clip->indoor_dma_again_proc(); } if (Common_Get(clip).my_room_clip != NULL) { Common_Get(clip).my_room_clip->redma_ftr_bank_proc(); } if (Common_Get(clip).my_indoor_clip != NULL) { Common_Get(clip).my_indoor_clip->indoor_dma_again_proc(); } if (Common_Get(clip).arrange_room_clip != NULL) { Common_Get(clip).arrange_room_clip->redma_proc(); } if (Common_Get(clip).shop_umbrella_clip != NULL) { Common_Get(clip).shop_umbrella_clip->umbrella_dma_again_proc(); } if (Common_Get(clip).ball_redma_proc != NULL) { (*Common_Get(clip).ball_redma_proc)(); } } extern void mSc_data_bank_ct(GAME_PLAY* play, Object_Exchange_c* exchange) { char* exchange_arena; bzero(exchange, sizeof(Object_Exchange_c)); exchange->keep_id = -1; exchange->exchange_id = -1; exchange_arena = (char*)THA_allocAlign(&play->game.tha, mSc_ARENA_SIZE, ~0x1F); exchange->next_bank_ram_address = exchange_arena; exchange->start_address_save[0] = exchange_arena; exchange->start_address_save[1] = exchange_arena; exchange_arena += mSc_ARENA_SIZE; exchange->max_ram_address = exchange_arena; exchange->end_address_save[0] = exchange_arena; exchange->end_address_save[1] = exchange_arena; } extern void mSc_decide_exchange_bank(Object_Exchange_c* exchange) { exchange->keep_id = exchange->bank_idx; exchange->_194C = exchange->next_bank_ram_address; } static void Scene_player_select(int scene_no, int npc_actor) { if (Save_Get(scene_no) == scene_no) { int met_villagers_bitfield = 0; int met_villagers_num = 0; mActor_name_t npc_id; Animal_c* animal = Save_Get(animals); Anmmem_c* memory; int i; int j; int selected; /* Track all villagers who have met a player from town before */ for (i = 0; i < ANIMAL_NUM_MAX; i++) { if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE) { memory = animal->memories; for (j = 0; j < ANIMAL_MEMORY_NUM; j++) { if (memory->memory_player_id.land_id == Save_Get(land_info).id && mLd_CheckCmpLandName(memory->memory_player_id.land_name, Save_Get(land_info).name) == TRUE) { met_villagers_bitfield |= (1 << i); met_villagers_num++; break; } memory++; } } animal++; } /* Roll a random villager if none have met a player in town before */ if (met_villagers_num == 0) { do { selected = RANDOM(ANIMAL_NUM_MAX); } while (mNpc_CheckFreeAnimalPersonalID(&Save_Get(animals[selected]).id)); npc_id = Save_Get(animals[selected]).id.npc_id; i = mNpc_SearchAnimalinfo(Save_Get(animals), npc_id, ANIMAL_NUM_MAX); } else { /* Pick a random villager which has met a player in town */ selected = RANDOM(met_villagers_num); for (i = 0; i < ANIMAL_NUM_MAX; i++) { if ((met_villagers_bitfield & 1) != 0) { if (selected == 0) { break; } else { selected--; } } met_villagers_bitfield >>= 1; } npc_id = Save_Get(animals[i]).id.npc_id; } if (scene_no == SCENE_PLAYERSELECT_2) { Common_Set(player_select_animal_no, i); } mNpc_RegistEventNpc(npc_actor, npc_id, npc_id, Save_Get(animals[i]).cloth); } } static void Scene_Proc_Player_Ptr(GAME_PLAY* play, Scene_Word_u* data); static void Scene_Proc_Ctrl_Actor_Ptr(GAME_PLAY* play, Scene_Word_u* data); static void Scene_Proc_Actor_Ptr(GAME_PLAY* play, Scene_Word_u* data); static void Scene_Proc_Object_Exchange_Bank_Ptr(GAME_PLAY* play, Scene_Word_u* data); static void Scene_Proc_Door_Data_Ptr(GAME_PLAY* play, Scene_Word_u* data); static void Scene_Proc_Field_ct(GAME_PLAY* play, Scene_Word_u* data); static void Scene_Proc_MyRoom_ct(GAME_PLAY* play, Scene_Word_u* data); static void Scene_Proc_ArrangeRoom_ct(GAME_PLAY* play, Scene_Word_u* data); static void Scene_Proc_ArrangeFurniture_ct(GAME_PLAY* play, Scene_Word_u* data); static void Scene_Proc_Sound(GAME_PLAY* play, Scene_Word_u* data); typedef void (*mSc_SCENE_WORD_PROC)(GAME_PLAY*, Scene_Word_u*); extern void Scene_ct(GAME_PLAY* play, Scene_Word_u* scene_data) { static mSc_SCENE_WORD_PROC Scene_Proc[mSc_SCENE_DATA_TYPE_NUM] = { &Scene_Proc_Player_Ptr, &Scene_Proc_Ctrl_Actor_Ptr, &Scene_Proc_Actor_Ptr, &Scene_Proc_Object_Exchange_Bank_Ptr, &Scene_Proc_Door_Data_Ptr, &Scene_Proc_Field_ct, &Scene_Proc_MyRoom_ct, &Scene_Proc_ArrangeRoom_ct, &Scene_Proc_ArrangeFurniture_ct, &Scene_Proc_Sound, NULL, }; /* Initialize all scene data */ while (TRUE) { u32 type = scene_data->misc.type; if (type == mSc_SCENE_DATA_TYPE_END) { break; } if (type < mSc_SCENE_DATA_TYPE_NUM) { (*Scene_Proc[type])(play, scene_data); } scene_data++; } /* Setup player select actors */ Scene_player_select(SCENE_PLAYERSELECT_2, SP_NPC_P_SEL2); // load Scene_player_select(SCENE_PLAYERSELECT_SAVE, SP_NPC_RESTART); // save /* Select Rover or Blanca for visiting town scene */ if (Save_Get(scene_no) == SCENE_START_DEMO3) { int rover_shown = FALSE; if (mLd_CheckCmpLand(Common_Get(now_private)->player_ID.land_name, Common_Get(now_private)->player_ID.land_id, Common_Get(travel_persistent_data).land.name, Common_Get(travel_persistent_data).land.id) == TRUE) { if (Common_Get(now_private)->state_flags & mPr_FLAG_MASK_CAT_SCHEDULED) { Common_Get(now_private)->state_flags &= ~mPr_FLAG_MASK_CAT_SCHEDULED; rover_shown = TRUE; } else if (fqrand() < 0.5f) { Common_Get(now_private)->state_flags |= mPr_FLAG_MASK_CAT_SCHEDULED; // Blanca is scheduled } else { Common_Get(now_private)->state_flags &= ~mPr_FLAG_MASK_CAT_SCHEDULED; rover_shown = TRUE; } } else { rover_shown = TRUE; } if (rover_shown == FALSE) { mActor_name_t blanca_cloth; mSP_SelectRandomItem_New(NULL, &blanca_cloth, 1, NULL, 0, mSP_KIND_CLOTH, mSP_LISTTYPE_ABC, FALSE); mNpc_RegistMaskNpc(SP_NPC_MASK_CAT2, SP_NPC_MASK_CAT2, blanca_cloth); Save_Get(mask_cat).cloth_no = (blanca_cloth >= ITM_CLOTH_START && blanca_cloth < ITM_CLOTH_END) ? (blanca_cloth - ITM_CLOTH_START) : EMPTY_NO; } else { mNpc_RegistMaskNpc(SP_NPC_MASK_CAT2, SP_NPC_GUIDE, EMPTY_NO); // Rover } } } static void Scene_Proc_Player_Ptr(GAME_PLAY* play, Scene_Word_u* scene_data) { static s16 angle_table[mSc_DIRECT_NUM] = { DEG2SHORT_ANGLE(0.0f), /* 0x0000 */ DEG2SHORT_ANGLE(45.0f), /* 0x2000 */ DEG2SHORT_ANGLE(90.0f), /* 0x4000 */ DEG2SHORT_ANGLE(135.0f), /* 0x6000 */ DEG2SHORT_ANGLE(-180.0f), /* 0x8000 */ DEG2SHORT_ANGLE(-135.0f), /* 0xA000 */ DEG2SHORT_ANGLE(-90.0f), /* 0xC000 */ DEG2SHORT_ANGLE(-45.0f) /* 0xE000 */ }; Actor_data* data = scene_data->actor.data_p; play->player_data = data; /* Update player position & orientation based on the current door exit data */ if (Common_Get(door_data).next_scene_id != 0) { mem_copy((u8*)&data->position, (u8*)&Common_Get(door_data).exit_position, sizeof(s_xyz)); data->rotation.y = angle_table[Common_Get(door_data).exit_orientation]; } data->arg = Common_Get(door_data).extra_data; Object_Exchange_keep_new_Player(play); mSM_Object_Exchange_keep_new_Menu(play); } static void Scene_Proc_Ctrl_Actor_Ptr(GAME_PLAY* play, Scene_Word_u* scene_data) { s16* data = scene_data->control_actor.ctrl_actor_profile_p; play->ctrl_actor_data_num = scene_data->control_actor.num_ctrl_actors; play->ctrl_actor_data = data; } static void Scene_Proc_Actor_Ptr(GAME_PLAY* play, Scene_Word_u* scene_data) { Actor_data* data_p = scene_data->actor.data_p; play->actor_data_num = scene_data->actor.num_actors; play->actor_data = data_p; } static void Scene_Proc_Object_Exchange_Bank_Ptr(GAME_PLAY* play, Scene_Word_u* scene_data) { play->obj_bank_data_num = scene_data->object_bank.num_banks; play->obj_bank_data = scene_data->object_bank.banks_p; } static void Scene_Proc_Door_Data_Ptr(GAME_PLAY* play, Scene_Word_u* scene_data) { Door_data_c* data_p = scene_data->door_data.door_data_p; play->door_info.num_doors = scene_data->door_data.num_doors; play->door_info.door_data_p = data_p; } extern void Door_info_ct(Door_info_c* door_info) { door_info->num_doors = 0; } static void Scene_Proc_Sound(GAME_PLAY* play, Scene_Word_u* scene_data) { // stubbed } static void set_item_info(GAME_PLAY* play, Scene_Word_Data_FieldCt_c* field_ct) { static s16 profile_table[4] = { mAc_PROFILE_BGITEM, mAc_PROFILE_DUMMY, mAc_PROFILE_BGPOLICEITEM, mAc_PROFILE_BGPOSTITEM, }; int item_type = field_ct->item_type; if (item_type == 0) { Common_Set(bg_item_profile, Common_Get(time).bgitem_profile); } else { Common_Set(bg_item_profile, profile_table[item_type]); } Common_Set(bg_item_type, item_type); } static void Scene_Proc_Field_ct(GAME_PLAY* play, Scene_Word_u* scene_data) { mFM_SetFieldInitData(scene_data->field_ct.bg_num, scene_data->field_ct.bg_disp_size); set_item_info(play, &scene_data->field_ct); Common_Set(field_draw_type, scene_data->field_ct.draw_type); Common_Set(game_started, FALSE); Common_Set(in_initial_block, TRUE); Common_Set(sunlight_flag, TRUE); } static void Scene_Proc_MyRoom_ct(GAME_PLAY* play, Scene_Word_u* scene_data) { mScn_ObtainMyRoomBank(play); } static void Scene_Proc_ArrangeRoom_ct(GAME_PLAY* play, Scene_Word_u* scene_data) { mScn_ObtainCarpetBank(play); } static void Scene_Proc_ArrangeFurniture_ct(GAME_PLAY* play, Scene_Word_u* scene_data) { Common_Get(clip).arrange_ftr_num = scene_data->arrange_ftr_ct.arrange_ftr_num; } extern int goto_other_scene(GAME_PLAY* play, Door_data_c* door_data, int update_player_mode) { PLAYER_ACTOR* player = get_player_actor_withoutCheck(play); int res = 0; // failed if (player != NULL) { if (play->fb_wipe_mode == 0) { play->fb_fade_type = 2; if (door_data->wipe_type == 0) { play->fb_wipe_type = 3; } else { play->fb_wipe_type = door_data->wipe_type; } if (Common_Get(transition).wipe_type == 0xFF) { Common_Get(transition).wipe_type = play->fb_wipe_type; } Common_Set(door_data, *door_data); Common_Get(door_data).next_scene_id = door_data->next_scene_id + 1; play->next_scene_no = door_data->next_scene_id; if (update_player_mode) { mPlib_request_main_invade_type1(play); } restore_fgdata_all(play); play->game.pad_initialized = FALSE; res = 1; // success } else { res = 2; // already changing scenes } } return res; } extern int goto_next_scene(GAME_PLAY* play, int next_idx, int update_player_mode) { int res = FALSE; if (play->door_info.num_doors != 0) { res = goto_other_scene(play, play->door_info.door_data_p + next_idx, update_player_mode); } return res; } extern int goto_emu_game(GAME* game, s16 famicom_rom_id) { GAME_PLAY* play = (GAME_PLAY*)game; PLAYER_ACTOR* player = get_player_actor_withoutCheck(play); int res = FALSE; if (player != NULL) { Door_data_c* door_data; play->fb_fade_type = 3; play->fb_wipe_type = 3; res = TRUE; door_data = Common_GetPointer(famicom_emu_exit_door_data); door_data->next_scene_id = Save_Get(scene_no); door_data->exit_orientation = 0; door_data->exit_type = 0; door_data->extra_data = 0; door_data->exit_position.x = player->actor_class.world.position.x; door_data->exit_position.y = player->actor_class.world.position.y; door_data->exit_position.z = player->actor_class.world.position.z; door_data->door_actor_name = EMPTY_NO; Common_Set(current_famicom_rom, famicom_rom_id); } return res; } extern void return_emu_game(GAME* game) { mem_copy((u8*)Common_GetPointer(door_data), (u8*)Common_GetPointer(famicom_emu_exit_door_data), sizeof(Door_data_c)); // copy exit door data Common_Get(door_data).next_scene_id = Common_Get(famicom_emu_exit_door_data).next_scene_id + 1; game->pad_initialized = FALSE; game_goto_next_game_play(game); Save_Set(scene_no, Common_Get(famicom_emu_exit_door_data).next_scene_id); }