diff --git a/config/rel_slices.yml b/config/rel_slices.yml index 391c0b0f..fe2d4c6c 100644 --- a/config/rel_slices.yml +++ b/config/rel_slices.yml @@ -86,6 +86,10 @@ m_view.c: .rodata: [0x806433D8, 0x80643408] sys_stacks.c: .bss: [0x812F5670, 0x812F9670] +m_vibctl.c: + .text: [0x8040387C, 0x804040F0] + .rodata: [0x80643550, 0x806436C8] + .bss: [0x812F3098, 0x812F31B8] THA_GA.c: .text: [0x80404AE0, 0x80404B40] TwoHeadArena.c: diff --git a/include/ac_structure.h b/include/ac_structure.h new file mode 100644 index 00000000..5e61ec2f --- /dev/null +++ b/include/ac_structure.h @@ -0,0 +1,22 @@ +#ifndef AC_STRUCTURE_H +#define AC_STRUCTURE_H + +#include "types.h" +#include "m_actor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef ACTOR* (*aSTR_SETUP_ACTOR_PROC)(GAME_PLAY*, mActor_name_t, int, f32, f32); + +// TODO: finish clip +typedef struct actor_structure_clip_s { + aSTR_SETUP_ACTOR_PROC setup_actor_proc; +} aSTR_Clip_c; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/audio.h b/include/audio.h index 442dbbe6..54e53971 100644 --- a/include/audio.h +++ b/include/audio.h @@ -22,6 +22,9 @@ extern void sAdo_SysLevStart(u8 id); extern void sAdo_SysTrgStart(u16 id); +extern void sAdos_KishaStatusLevel(f32 speed, u32 ongenNum1, u16 angle1, f32 distance1, u32 ongenNum2, u16 angle2, f32 distance2); +extern void sAdos_KishaStatusTrg(u8 state); + /* Not sure about the last param name */ extern void sAdo_VoiceSe(u8 num, u8 num2, u8 num3, s16 character_idx, u8 scale, u8 mode); diff --git a/include/dolphin/pad.h b/include/dolphin/pad.h index 1c1980df..bc6f5501 100644 --- a/include/dolphin/pad.h +++ b/include/dolphin/pad.h @@ -7,6 +7,10 @@ extern "C" { #endif +#define PAD_MOTOR_STOP 0 +#define PAD_MOTOR_RUMBLE 1 +#define PAD_MOTOR_STOP_HARD 2 + #define PAD_BUTTON_LEFT 0x0001 #define PAD_BUTTON_RIGHT 0x0002 #define PAD_BUTTON_DOWN 0x0004 diff --git a/include/m_actor.h b/include/m_actor.h index 3a8c742e..50655b5e 100644 --- a/include/m_actor.h +++ b/include/m_actor.h @@ -127,6 +127,19 @@ struct actor_s { #define mActor_NONE_PROC1 ((mActor_proc)none_proc1) +typedef struct actor_list_s { + int num_actors; + ACTOR* actor; +} Actor_list; + +typedef struct actor_info_s { + int total_num; + Actor_list list[ACTOR_PART_NUM]; +} Actor_info; + +extern void Actor_delete(ACTOR* actor); +extern ACTOR* Actor_info_fgName_search(Actor_info* actor_info, mActor_name_t fg_name, int part); + #ifdef __cplusplus } #endif diff --git a/include/m_clip.h b/include/m_clip.h index 4c543995..0d7eea39 100644 --- a/include/m_clip.h +++ b/include/m_clip.h @@ -4,6 +4,7 @@ #include "types.h" #include "ac_gyoei_h.h" #include "ac_insect_h.h" +#include "ac_structure.h" #ifdef __cplusplus extern "C" { @@ -13,7 +14,9 @@ extern "C" { typedef struct clip_s { /* 0x000 */ void* _000[(0x07C - 0x000) / sizeof(void*)]; /* 0x07C */ aINS_Clip_c* insect_clip; - /* 0x080 */ void* _080[(0x0AC - 0x080) / sizeof(void*)]; + /* 0x080 */ void* _080[(0x08C - 0x080) / sizeof(void*)]; + /* 0x08C */ aSTR_Clip_c* structure_clip; + /* 0x090 */ void* _090[(0x0AC - 0x090) / sizeof(void*)]; /* 0x0AC */ aGYO_Clip_c* gyo_clip; /* 0x0B0 */ void* _0B0[(0x104 - 0x0B0) / sizeof(void*)]; } Clip_c; diff --git a/include/m_collision_bg.h b/include/m_collision_bg.h index 9522ce69..c72ed981 100644 --- a/include/m_collision_bg.h +++ b/include/m_collision_bg.h @@ -57,6 +57,7 @@ extern int mCoBG_CheckWaterAttribute(u32 attribute); extern f32 mCoBG_GetBgY_AngleS_FromWpos(s_xyz* angle_to_ground, xyz_t wpos, f32 offset); extern int mCoBG_CheckWaterAttribute_OutOfSea(u32 attribute); extern int mCoBG_CheckHole_OrgAttr(u32 attribute); +extern f32 mCoBG_GetBgY_OnlyCenter_FromWpos2(xyz_t wpos, f32 foot_dist); extern f32 mCoBG_GetWaterHeight_File(xyz_t wpos, char* file, int line); #define mCoBG_GetWaterHeight(wpos) mCoBG_GetWaterHeight_File(wpos, __FILE__, __LINE__) diff --git a/include/m_common_data.h b/include/m_common_data.h index 6d7b3b43..6ba449c7 100644 --- a/include/m_common_data.h +++ b/include/m_common_data.h @@ -162,7 +162,9 @@ typedef struct common_data_s { /* 0x02651E */ mActor_name_t last_field_id; /* 0x026520 */ u8 in_initial_block; /* when TRUE, the player is in the acre which they exited a building. FALSE otherwise. */ /* 0x026521 */ u8 submenu_disabled; /* when set, submenus cannot be accessed from start button */ - /* 0x026522 */ u8 _26522[0x2666C - 0x26522]; + /* 0x026522 */ u8 sunlight_flag; + /* 0x026523 */ u8 train_flag; + /* 0x026522 */ u8 _26524[0x2666C - 0x26524]; /* 0x02666C */ s16 weather; /* 0x02666E */ s16 weather_intensity; /* 0x026670 */ lbRTC_time_c weather_time; @@ -173,11 +175,27 @@ typedef struct common_data_s { /* 0x02852E */ s16 goods_power; /* 0x028530 */ Door_data_c door_data; /* misc door data */ /* 0x028544 */ Door_data_c structure_exit_door_data; /* door data for when exiting a building */ - /* 0x028558 */ u8 tmp1[0x28879 - 0x28558]; + /* 0x028558 */ u8 tmp1[0x02883E - 0x28558]; + /* 0x02883E */ u8 train_coming_flag; /* state tracker for when train is going to spawn/has spawned */ + /* 0x02883F */ u8 train_exists_flag; /* state tracker for when train exists */ + /* 0x028840 */ u8 train_control_state; /* current train state */ + /* 0x028841 */ u8 train_last_control_state; /* previous train state */ + /* 0x028842 */ u8 train_signal; + /* 0x028843 */ u8 train_day; + /* 0x028844 */ u8 train_action; + /* 0x028845 */ u8 train_timer; + /* 0x028848 */ u32 train_start_timer; + /* 0x02884C */ f32 train_speed; + /* 0x028850 */ xyz_t train_position; + /* 0x02885C */ f32 unused_02885C; + /* 0x028860 */ f32 unused_028860; + /* 0x028864 */ u16 unused_028864; + /* 0x028866 */ u16 unused_028866; + /* 0x028868 */ u8 _028868[0x028879 - 0x028868]; /* 0x028879 */ u8 auto_nwrite_count; /* 0x02887A */ lbRTC_year_t auto_nwrite_year; /* 0x02887C */ u8 save_error_type; /* set to one of the mFRm_ERROR_* states when save is invalid */ - /* 0x02887D */ u8 train_coming_flag; /* set when the train is coming */ + /* 0x02887D */ u8 train_approaching_flag; /* set when the train is coming */ /* 0x02887E */ u8 buried_treasure_flag; /* when set, treasure cannot be buried */ /* 0x02887F */ u8 spnpc_first_talk_flags; /* 0x028880 */ u8 needlework_first_talk_flags; diff --git a/include/m_field_info.h b/include/m_field_info.h index a71f5d76..0fc37efb 100644 --- a/include/m_field_info.h +++ b/include/m_field_info.h @@ -90,6 +90,16 @@ typedef struct location_info_s { /* 0x10 */ mActor_name_t* block_data; } mFI_unit_c; +typedef struct block_table_s { + s8 block_x; + s8 block_z; + + f32 pos_x; + f32 pos_z; + + mActor_name_t* items; +} mFI_block_tbl_c; + extern int mFI_CheckFieldData(); extern mActor_name_t mFI_GetFieldId(); extern int mFI_GetClimate(); diff --git a/include/m_name_table.h b/include/m_name_table.h index 91ab61b0..fa21cd0e 100644 --- a/include/m_name_table.h +++ b/include/m_name_table.h @@ -466,6 +466,10 @@ extern int mNT_check_unknown(mActor_name_t item_no); #define EXIT_DOOR 0x4080 +#define TRAIN_STATION 0x5809 +#define TRAIN0 0x580A +#define TRAIN1 0x580B + #define SP_NPC_START 0xD000 #define SP_NPC_ARTIST (SP_NPC_START + 0) // D000 #define SP_NPC_BROKER (SP_NPC_START + 1) // D001 diff --git a/include/m_play.h b/include/m_play.h index 887bb842..e931429f 100644 --- a/include/m_play.h +++ b/include/m_play.h @@ -8,6 +8,7 @@ #include "m_camera2.h" #include "m_submenu.h" #include "m_pause.h" +#include "m_field_info.h" #include "m_play_h.h" #ifdef __cplusplus @@ -18,12 +19,15 @@ extern "C" { struct game_play_s { /* 0x0000 */ GAME game; // TODO: finish - /* 0x00E0 */ u8 _00E0[0x1A68 - 0x00E0]; + /* 0x00E0 */ int _00E0; + /* 0x00E4 */ mFI_block_tbl_c block_table; + /* 0x00F4 */ mFI_block_tbl_c last_block_table; + /* 0x0104 */ u8 _0104[0x1A68 - 0x0104]; /* 0x1A68 */ View view; /* 0x1B88 */ Camera2 camera; /* 0x1CC0 */ u8 _1CC0[0x1DA0 - 0x1CC0]; /* 0x1DA0 */ pause_t pause; - /* 0x1DA8 */ u8 _1DA8[0x1DEC - 0x1DA8]; + /* 0x1DA8 */ Actor_info actor_info; /* 0x1DEC */ Submenu submenu; /* 0x1FA4 */ u8 _1FA4[0x200C - 0x1FA4]; /* 0x200C */ MtxF matrix; diff --git a/include/m_scene_table.h b/include/m_scene_table.h index dc4876af..0133fd53 100644 --- a/include/m_scene_table.h +++ b/include/m_scene_table.h @@ -7,6 +7,15 @@ extern "C" { #endif +enum field_draw_type { + FIELD_DRAW_TYPE_OUTDOORS, + FIELD_DRAW_TYPE_INDOORS, + FIELD_DRAW_TYPE_TRAIN, + FIELD_DRAW_TYPE_PLAYER_SELECT, + + FIELD_DRAW_TYPE_NUM +}; + /* TODO: is this right? I assume so based on file names but there may be a better place for this diff --git a/include/m_time.h b/include/m_time.h index 79e7112e..4dbe640b 100644 --- a/include/m_time.h +++ b/include/m_time.h @@ -61,6 +61,8 @@ enum { #define mTM_SECONDS_IN_HALFDAY 12*60*60 #define mTM_SECONDS_IN_DAY 24*60*60 +#define mTM_MINUTES_IN_HOUR 60 + typedef struct time_calendar_term_s { lbRTC_month_t month; lbRTC_day_t day; diff --git a/include/m_train_control.h b/include/m_train_control.h new file mode 100644 index 00000000..0b7b4b9d --- /dev/null +++ b/include/m_train_control.h @@ -0,0 +1,35 @@ +#ifndef M_TRAIN_CONTROL_H +#define M_TRAIN_CONTROL_H + +#include "types.h" +#include "m_play_h.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define mTRC_SLOW_SPEED 2.0f // speed stopping/starting +#define mTRC_FAST_SPEED 6.0f // speed when farther away from station + +enum { + mTRC_ACTION_NONE, + mTRC_ACTION_SPAWN_MOVING, + mTRC_ACTION_BEGIN_SLOWDOWN, + mTRC_ACTION_BEGIN_STOP, + mTRC_ACTION_SIGNAL_STOPPED, + mTRC_ACTION_WAIT_STOPPED, + mTRC_ACTION_SIGNAL_STARTING, + mTRC_ACTION_BEGIN_PULL_OUT, + mTRC_ACTION_SPEED_UP, + + mTRC_ACTION_NUM +}; + +extern void mTRC_init(); +extern void mTRC_move(GAME_PLAY* play); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/m_vibctl.h b/include/m_vibctl.h index 874bf34f..dce81525 100644 --- a/include/m_vibctl.h +++ b/include/m_vibctl.h @@ -7,9 +7,84 @@ extern "C" { #endif +#define mVibctl_FLAG_FORCE_STOP_NONE 0 +#define mVibctl_FLAG_FORCE_STOP0 (1 << 0) // 1 +#define mVibctl_FLAG_FORCE_STOP1 (1 << 1) // 2 +#define mVibctl_FLAG_FORCE_STOP2 (1 << 2) // 4 +#define mVibctl_FLAG_FORCE_STOP3 (1 << 3) // 8 +#define mVibctl_FLAG_FORCE_STOP_ALL (mVibctl_FLAG_FORCE_STOP0 | mVibctl_FLAG_FORCE_STOP1 | mVibctl_FLAG_FORCE_STOP2 | mVibctl_FLAG_FORCE_STOP3) // 15 + +enum { + mVibctl_ELEM_ENTRY_ATTACK, // fade-in program + mVibctl_ELEM_ENTRY_SUSTAIN, // sustain program + mVibctl_ELEM_ENTRY_RELEASE, // fade-out program + mVibctl_ELEM_ENTRY_END, // program has finished + + mVibctl_ELEM_ENTRY_NUM = mVibctl_ELEM_ENTRY_END +}; + +enum { + mVibctl_VIB_PROG_NON, // rumble config attack, shake tree attack + mVibctl_VIB_PROG_FFF, // fish touch bobber, fishing rod cast, rumble config sustain, Mouth of Truth furniture interaction attack & sustain, shovel hits soft object, net swing, axe cut attack & release, trip attack, pick weed sustain & release + mVibctl_VIB_PROG_F, // unused? + mVibctl_VIB_PROG_MF, // unused? + mVibctl_VIB_PROG_MP, // unused? + mVibctl_VIB_PROG_P, // rumble config release, shake tree release + mVibctl_VIB_PROG_FUNBARI, // unused? + mVibctl_VIB_PROG_ANAHORI, // digging with shovel + mVibctl_VIB_PROG_ANAUME, // filling hole with shovel + mVibctl_VIB_PROG_IMPACT, // fish bite bobber, shovel hits hard object + mVibctl_VIB_PROG_KI_GA_TAORERU, // axe cut sustain + mVibctl_VIB_PROG_KI_WO_YUSURU, // shake tree sustain + mVibctl_VIB_PROG_KORONODA, // trip sustain + mVibctl_VIB_PROG_SURPRISE, // Mouth of Truth furniture interaction release + mVibctl_VIB_PROG_DUMMY_B, // unused? + mVibctl_VIB_PROG_SAMPLE, // unused? + + mVibctl_VIB_PROG_NUM +}; + +#define mVibctl_ELEM_NUM 4 + +typedef struct vibration_element_entry_s { + int type; + int frames; + f32 step; +} mVibInfo_elem_entry_c; + +typedef struct vibration_element_s { + mVibInfo_elem_entry_c entries[mVibctl_ELEM_ENTRY_NUM]; + f32 step0; + f32 step1; + int now_entry; + int state_idx; + f32 frame_intensity; + int entry_frame; + f32 now_intensity; + int command; // for padmgr, PAD_MOTOR_* +} mVibElem_c; + +typedef struct vibration_info_s { + mVibElem_c* target_elem; + mVibElem_c elements[mVibctl_ELEM_NUM]; + int num_elements; + int force_stop; + int last_force_stop; +} mVibInfo_c; + +typedef struct vibration_work_data_S { + const u8* data; + int count; +} mVibWorkData_c; + extern void mVibctl_ct(); -extern void mVibctl_reset(); extern void mVibctl_init0(); +extern void mVibctl_init(); +extern void mVibctl_reset(); +extern void mVibctl_entry(int total_frames, int attack_type, int sustain_type, int release_type, int attack_frames, int sustain_frames, int release_frames, f32 step); +extern void mVibctl_simple_entry(int total_frames, int type, int attack_frames, int sustain_frames, int release_frames, f32 step); +extern void mVibctl_set_force_stop(int force_stop); +extern void mVibctl_clr_force_stop(int force_stop); #ifdef __cplusplus } diff --git a/include/padmgr.h b/include/padmgr.h index 436a81d4..205f1389 100644 --- a/include/padmgr.h +++ b/include/padmgr.h @@ -22,8 +22,8 @@ enum pads { }; typedef struct { - u8 last_intensity; - u8 now_intensity; + u8 last_command; // PAD_MOTOR_* + u8 now_command; // PAD_MOTOR_* u8 frames; u8 _pad; } Motor_t; @@ -64,18 +64,21 @@ typedef struct { extern padmgr padmgr_class; extern int padmgr_isConnectedController(int pad); +extern void padmgr_force_stop_ON(); +extern void padmgr_force_stop_OFF(); +extern void padmgr_RumbleSet(int pad, int intensity); -#define padmgr_setClient(callback, param) \ +#define padmgr_setClient(callback_proc, param) \ do { \ padmgr* mgr = &padmgr_class; \ - mgr->callback = callback; \ + mgr->callback = callback_proc; \ mgr->callback_param = param; \ } while (0) -#define padmgr_removeClient(callback, param) \ +#define padmgr_removeClient(callback_proc, param) \ do { \ padmgr* mgr = &padmgr_class; \ - if (mgr->callback == (callback) && mgr->callback_param == (param)) { \ + if (mgr->callback == (callback_proc) && mgr->callback_param == (param)) { \ mgr->callback = NULL; \ mgr->callback_param = NULL; \ } \ diff --git a/rel/m_train_control.c b/rel/m_train_control.c new file mode 100644 index 00000000..89d695d6 --- /dev/null +++ b/rel/m_train_control.c @@ -0,0 +1,530 @@ +#include "m_train_control.h" + +#include "m_common_data.h" +#include "m_play.h" +#include "m_player.h" +#include "m_player_lib.h" +#include "sys_math.h" +#include "sys_math3d.h" +#include "audio.h" +#include "m_event.h" +#include "m_scene.h" +#include "m_lib.h" +#include "m_name_table.h" +#include "m_collision_bg.h" + +#define mTRC_RTC_TIME_SECONDS(rtc_time) (rtc_time->sec + (rtc_time->min + rtc_time->hour * mTM_MINUTES_IN_HOUR) * mTM_SECONDS_IN_MINUTE) + +static void mTRC_SetMicPos(GAME_PLAY* play, xyz_t* mic_pos) { + xyz_t pos; + PLAYER_ACTOR* player = get_player_actor_withoutCheck(play); + Door_data_c* door = Common_GetPointer(structure_exit_door_data); + f32 z = cosf_table(0.0f) * 77.0f; // cos(0) = 1, 1.0f * 77.0f = 77.0f + f32 x = sinf_table(0.0f) * 77.0f; // sin(0) = 0, 0.0f * 77.0f = 0.0f + + if (Common_Get(field_type) != mFI_FIELDTYPE2_FG) { + xyz_t_move_s_xyz(&pos, &door->exit_position); + } + else { + if (player != NULL) { + xyz_t_move(&pos, &player->actor_class.world_position); + } + else { + xyz_t_move(&pos, &ZeroVec); + } + } + + mic_pos->x = pos.x + x; // equates to pos.x + 0.0f = pos.x + mic_pos->y = pos.y + 240.0f; + mic_pos->z = pos.z + z; // equates to pos.z + 77.0f +} + +static void mTRC_KishaStatusLevel(GAME_PLAY* play, xyz_t pos, f32 speed) { + xyz_t mic_pos; + + f32 x; + f32 y; + f32 z; + + s16 angle; + u16 unsigned_angle; + f32 distance; + + s16 angle2; + u16 unsigned_angle2; + f32 distance2; + + mTRC_SetMicPos(play, &mic_pos); + x = pos.x - mic_pos.x; + y = pos.y - mic_pos.y; + z = pos.z - mic_pos.z; + + angle = atans_table(z, x); + distance = sqrtf(x * x + y * y + z * z); + unsigned_angle = (int)angle; + + x = (pos.x - 250.0f) - mic_pos.x; + y = pos.y - mic_pos.y; + z = pos.z - mic_pos.z; + + angle2 = atans_table(z, x); + distance2 = sqrtf(x * x + y * y + z * z); + unsigned_angle2 = (int)angle2; + + sAdos_KishaStatusLevel(speed, Common_GetPointer(train_coming_flag), unsigned_angle, distance, Common_GetPointer(train_exists_flag), unsigned_angle2, distance2); +} + +static void mTRC_KishaStatusTrg(u8 state) { + if (state == 1) { + Common_Set(train_approaching_flag, TRUE); + } + + sAdos_KishaStatusTrg(state); +} + +static int aTRC_area_check(GAME_PLAY* play, xyz_t pos) { + int block_x; + int block_z; + int x_diff; + + mFI_Wpos2BlockNum(&block_x, &block_z, pos); + x_diff = (block_x - play->block_table.block_x) >= 0 ? (block_x - play->block_table.block_x) : -(block_x - play->block_table.block_x); + + if (x_diff >= 2 || block_z != play->block_table.block_z) { + return FALSE; + } + return TRUE; +} + +static int mTRC_go_process() { + int res = FALSE; + int demo_no = mEv_CheckTitleDemo(); + + if (((int)demo_no == 0 || demo_no == 1 || demo_no == -9) && Common_Get(field_draw_type) != FIELD_DRAW_TYPE_TRAIN && Common_Get(field_draw_type) != FIELD_DRAW_TYPE_PLAYER_SELECT) { + res = TRUE; + } + + return res; +} + +#define HOUR_MIN_SEC_TO_SECS(h, m, s) (h * 3600 + m * 60 + s) +static u32 mTRC_get_depart_time() { + static u32 time_table[25] = { + HOUR_MIN_SEC_TO_SECS( 0, 19, 0), + HOUR_MIN_SEC_TO_SECS( 1, 19, 0), + HOUR_MIN_SEC_TO_SECS( 2, 19, 0), + HOUR_MIN_SEC_TO_SECS( 3, 19, 0), + HOUR_MIN_SEC_TO_SECS( 4, 19, 0), + HOUR_MIN_SEC_TO_SECS( 5, 19, 0), + HOUR_MIN_SEC_TO_SECS( 6, 19, 0), + HOUR_MIN_SEC_TO_SECS( 7, 19, 0), + HOUR_MIN_SEC_TO_SECS( 8, 19, 0), + HOUR_MIN_SEC_TO_SECS( 9, 19, 0), + HOUR_MIN_SEC_TO_SECS(10, 19, 0), + HOUR_MIN_SEC_TO_SECS(11, 19, 0), + HOUR_MIN_SEC_TO_SECS(12, 19, 0), + HOUR_MIN_SEC_TO_SECS(13, 19, 0), + HOUR_MIN_SEC_TO_SECS(14, 19, 0), + HOUR_MIN_SEC_TO_SECS(15, 19, 0), + HOUR_MIN_SEC_TO_SECS(16, 19, 0), + HOUR_MIN_SEC_TO_SECS(17, 19, 0), + HOUR_MIN_SEC_TO_SECS(18, 19, 0), + HOUR_MIN_SEC_TO_SECS(19, 19, 0), + HOUR_MIN_SEC_TO_SECS(20, 19, 0), + HOUR_MIN_SEC_TO_SECS(21, 19, 0), + HOUR_MIN_SEC_TO_SECS(22, 19, 0), + HOUR_MIN_SEC_TO_SECS(23, 19, 0), + HOUR_MIN_SEC_TO_SECS(24, 19, 0) + }; + + int i = 0; + u32 depart_time; + lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time); + u32 now_sec = mTRC_RTC_TIME_SECONDS(rtc_time); + int day; + + while (TRUE) { + if (time_table[i] >= now_sec) { + depart_time = time_table[i] - HOUR_MIN_SEC_TO_SECS(0, 4, 10); + break; + } + i++; + } + + Common_Set(train_day, rtc_time->day); + return depart_time; +} + +static int mTRC_time_check() { + u32 now_sec = mTRC_RTC_TIME_SECONDS(Common_GetPointer(time.rtc_time)); + return now_sec >= Common_Get(train_start_timer); +} + +static void mTRC_mati_init() { + xyz_t pos; + + Common_Set(train_action, mTRC_ACTION_WAIT_STOPPED); + Common_Set(train_flag, TRUE); + Common_Set(train_signal, TRUE); + Common_Set(train_control_state, 1); + Common_Set(train_last_control_state, 1); + + pos.x = 2376.0f; + pos.z = 740.0f; + pos.y = 180.0f; + Common_Set(train_position, pos); +} + +static void mTRC_demo_init() { + xyz_t pos; + lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time); + + Common_Set(train_action, mTRC_ACTION_BEGIN_SLOWDOWN); + Common_Set(train_speed, mTRC_SLOW_SPEED); + Common_Set(train_flag, TRUE); + Common_Set(train_start_timer, mTRC_RTC_TIME_SECONDS(rtc_time) - HOUR_MIN_SEC_TO_SECS(0, 4, 50)); + Common_Set(train_day, Common_Get(time.rtc_time.day)); + Common_Set(train_control_state, 0); + Common_Set(train_last_control_state, 0); + + pos.x = 2037.0f; + pos.z = 740.0f; + pos.y = 180.0f; + Common_Set(train_position, pos); +} + +static void mTRC_call_init() { + xyz_t pos; + + Common_Set(train_action, mTRC_ACTION_BEGIN_SLOWDOWN); + Common_Set(train_speed, 0.0f); + Common_Set(train_flag, TRUE); + Common_Set(train_control_state, 1); + Common_Set(train_last_control_state, 1); + + pos.x = 1904.0f; + pos.z = 740.0f; + pos.y = 180.0f; + Common_Set(train_position, pos); +} + +static void mTRC_norm_init() { + xyz_t pos; + + Common_Set(train_action, mTRC_ACTION_SPAWN_MOVING); + Common_Set(train_speed, 0.0f); + Common_Set(train_flag, TRUE); + Common_Set(train_control_state, 0); + Common_Set(train_last_control_state, 0); + + pos.x = 320.0f; + pos.z = 740.0f; + pos.y = 180.0f; + Common_Set(train_position, pos); +} + +static int mTRC_schedule(GAME_PLAY* play) { + int res = -1; + + if (mEv_CheckTitleDemo() == 1) { + if (Common_Get(train_action) == mTRC_ACTION_NONE) { + mTRC_mati_init(); + } + + return -1; + } + else { + switch (Common_Get(train_coming_flag)) { + case 3: + { + Common_Set(train_coming_flag, 0); + mTRC_demo_init(); + res = 0; + break; + } + + case 2: + { + if (!aTRC_area_check(play, Common_Get(train_position)) && Common_Get(train_action) >= mTRC_ACTION_SIGNAL_STARTING) { + mTRC_call_init(); + res = 1; + break; + } + + if (Common_Get(train_action) < mTRC_ACTION_SIGNAL_STARTING && Common_Get(train_action) != mTRC_ACTION_NONE) { + Common_Set(train_coming_flag, FALSE); + Common_Set(train_control_state, 1); + Common_Set(train_last_control_state, 1); + break; + } + + if (Common_Get(train_action) == mTRC_ACTION_NONE) { + mTRC_call_init(); + res = 1; + break; + } + break; + } + + case 4: + { + break; + } + + default: + { + if (Common_Get(train_action) == mTRC_ACTION_NONE && mEv_CheckArbeit() == FALSE && mTRC_time_check()) { + mTRC_norm_init(); + res = 1; + } + break; + } + } + } + + return res; +} + +/* TODO: @nonmatching something to do with common data loading at start of function */ +static void mTRC_trainControl(GAME_PLAY* play, int state) { + u8 signal = Common_Get(train_signal); + u8 action = Common_Get(train_action); + u8 timer = Common_Get(train_timer); + u32 start_timer = Common_Get(train_start_timer); + f32 speed = Common_Get(train_speed); + xyz_t pos = Common_Get(train_position); + u8 day = Common_Get(time.rtc_time.day); + + if (Common_Get(train_day) != day) { + if (start_timer >= mTM_SECONDS_IN_DAY) { + start_timer = start_timer - mTM_SECONDS_IN_DAY; + } + + Common_Set(train_day, day); + } + + switch (action) { + case mTRC_ACTION_SPAWN_MOVING: + { + int block_x; + int block_z; + + speed = mTRC_FAST_SPEED; + mFI_Wpos2BlockNum(&block_x, &block_z, pos); + + if (block_x >= 2) { + action = mTRC_ACTION_BEGIN_SLOWDOWN; + } + break; + } + + case mTRC_ACTION_BEGIN_SLOWDOWN: + { + chase_f(&speed, mTRC_SLOW_SPEED, 0.01f); + if (pos.x > 2165.0f) { + action = mTRC_ACTION_BEGIN_STOP; + speed = mTRC_SLOW_SPEED; + } + break; + } + + case mTRC_ACTION_BEGIN_STOP: + { + chase_f(&speed, 0.0f, 0.005f); + if (fabsf(speed) < 0.008f) { + signal = TRUE; + timer = 48; + action = mTRC_ACTION_SIGNAL_STOPPED; + state = 2; + speed = 0.0f; + } + break; + } + + case mTRC_ACTION_SIGNAL_STOPPED: + { + if (timer == 0) { + action = mTRC_ACTION_WAIT_STOPPED; + start_timer += 310; + } + else { + timer--; + } + break; + } + + case mTRC_ACTION_WAIT_STOPPED: + { + if (Common_Get(train_control_state) != Common_Get(train_last_control_state)) { + Common_Set(train_control_state, Common_Get(train_last_control_state)); + signal = FALSE; + Common_Set(train_signal, FALSE); + } + else { + if (Common_Get(train_control_state) == 0 && mTRC_time_check()) { + signal = FALSE; + Common_Set(train_signal, FALSE); + } + } + + if (signal == FALSE) { + timer = 84; + action = mTRC_ACTION_SIGNAL_STARTING; + } + break; + } + + case mTRC_ACTION_SIGNAL_STARTING: + { + if (timer == 0) { + timer = 180; + action = mTRC_ACTION_BEGIN_PULL_OUT; + state = 3; + } + else { + timer--; + } + break; + } + + case mTRC_ACTION_BEGIN_PULL_OUT: + { + chase_f(&speed, mTRC_SLOW_SPEED, 0.00345f); + + if (timer == 0) { + action = mTRC_ACTION_SPEED_UP; + } + else { + timer--; + } + break; + } + + case mTRC_ACTION_SPEED_UP: + { + chase_f(&speed, mTRC_FAST_SPEED, 0.00345); + if (pos.x > 4400.0f) { + start_timer = mTRC_get_depart_time(); + action = mTRC_ACTION_NONE; + Common_Set(train_flag, FALSE); + state = 4; + } + break; + } + } + + if (action != mTRC_ACTION_NONE) { + ACTOR* train_actor = Actor_info_fgName_search(&play->actor_info, TRAIN0, ACTOR_PART_ITEM); + if (Common_Get(train_flag) == FALSE && train_actor == NULL) { + Common_Set(train_flag, TRUE); + } + + pos.x += 0.5f * speed; + mTRC_KishaStatusLevel(play, pos, speed); + } + + if (state >= 0) { + mTRC_KishaStatusTrg(state); + } + + Common_Set(train_signal, signal); + Common_Set(train_action, action); + Common_Set(train_timer, timer); + Common_Set(train_start_timer, start_timer); + Common_Set(train_speed, speed); + Common_Set(train_position, pos); +} + +static void mTRC_trainSet(GAME_PLAY* play) { + ACTOR* train_actor; + ACTOR* caboose_actor; + xyz_t pos = Common_Get(train_position); + xyz_t xz_pos; + xyz_t xyz_pos; + + if (Common_Get(field_type) != mFI_FIELDTYPE2_FG || !Common_Get(train_flag)) { + return; + } + else if (Common_Get(clip).structure_clip != NULL) { + f32 x = pos.x; + xz_pos.x = pos.x; + xz_pos.z = 740.0f; + xz_pos.y = 0.0f; + + if (aTRC_area_check(play, xz_pos)) { + train_actor = Actor_info_fgName_search(&play->actor_info, TRAIN0, ACTOR_PART_ITEM); + if (train_actor == NULL) { + /* spawn train actor since it doesn't exist */ + train_actor = (*Common_Get(clip.structure_clip)->setup_actor_proc)(play, TRAIN0, -1, x, 740.0f); + + /* for whatever reason, train actor didn't spawn, so exit */ + if (train_actor == NULL) { + return; + } + } + else { + xyz_pos.x = xz_pos.x; + xyz_pos.z = xz_pos.z; + xyz_pos.y = mCoBG_GetBgY_OnlyCenter_FromWpos2(xyz_pos, 0.0f); + + xyz_t_move(&train_actor->world_position, &xyz_pos); + } + + x -= 250.0f; + caboose_actor = Actor_info_fgName_search(&play->actor_info, TRAIN1, ACTOR_PART_ITEM); + if (caboose_actor == NULL) { + caboose_actor = (*Common_Get(clip.structure_clip)->setup_actor_proc)(play, TRAIN1, -1, x, 740.0f); + if (caboose_actor == NULL) { + Actor_delete(train_actor); + return; + } + } + else { + xyz_pos.x = x; + xyz_pos.z = 740.0f; + xyz_pos.y = mCoBG_GetBgY_OnlyCenter_FromWpos2(xyz_pos, 0.0f); + + xyz_t_move(&caboose_actor->world_position, &xyz_pos); + } + + Common_Set(train_flag, FALSE); + + train_actor->block_x = -1; + train_actor->block_z = -1; + train_actor->child_actor = caboose_actor; + + caboose_actor->block_x = -1; + caboose_actor->block_z = -1; + caboose_actor->parent_actor = train_actor; + } + } +} + +extern void mTRC_init() { + Common_Set(train_start_timer, mTRC_get_depart_time()); + Common_Set(train_coming_flag, 0); + Common_Set(train_exists_flag, FALSE); + Common_Set(train_control_state, 0); + Common_Set(train_last_control_state, 0); + Common_Set(train_signal, FALSE); + Common_Set(train_action, 0); + Common_Set(train_timer, 0); + Common_Set(train_speed, 0.0f); + Common_Set(train_position, ZeroVec); + Common_Set(train_approaching_flag, FALSE); + Common_Set(train_flag, FALSE); +} + +extern void mTRC_move(GAME_PLAY* play) { + PLAYER_ACTOR* player = get_player_actor_withoutCheck(play); + int state; + Common_Set(train_approaching_flag, FALSE); + + if (!mTRC_go_process() || player == NULL) { + return; + } + + state = mTRC_schedule(play); + mTRC_trainControl(play, state); + mTRC_trainSet(play); +} diff --git a/rel/m_vibctl.c b/rel/m_vibctl.c new file mode 100644 index 00000000..71dffb6b --- /dev/null +++ b/rel/m_vibctl.c @@ -0,0 +1,490 @@ +#include "m_vibctl.h" + +#include "libultra/libultra.h" +#include "padmgr.h" +#include "m_common_data.h" +#include "m_field_info.h" +#include "m_event.h" +#include "dolphin/pad.h" + +static const u8 mVW_Non[1] = { PAD_MOTOR_STOP }; + +static const u8 mVW_FFF[1] = { PAD_MOTOR_RUMBLE }; + +static const u8 mVW_F[2] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP +}; + +static const u8 mVW_MF[3] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP +}; + +static const u8 mVW_MP[4] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP +}; + +static const u8 mVW_P[5] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP +}; + +static const u8 mVW_funbari[8] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD +}; + +static const u8 mVW_anahori[8] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP +}; + +static const u8 mVW_anaume[60] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD +}; + +static const u8 mVW_impact[9] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD +}; + +static const u8 mVW_ki_ga_taoreru[36] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE +}; + +static const u8 mVW_ki_wo_yusuru[13] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP +}; + +static const u8 mVW_koronoda[14] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP +}; + +static const u8 mVW_surprise[7] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP +}; + +static const u8 mVW_dummy_b[1] = { PAD_MOTOR_RUMBLE }; + +static const u8 mVW_sample[13] = { + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_RUMBLE, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP_HARD, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP, + PAD_MOTOR_STOP +}; + +#define VIB_PROG(name) { mVW_##name, ARRAY_SIZE(mVW_##name, u8) } +static const mVibWorkData_c mVW_data[mVibctl_VIB_PROG_NUM] = { + VIB_PROG(Non), + VIB_PROG(FFF), + VIB_PROG(F), + VIB_PROG(MF), + VIB_PROG(MP), + VIB_PROG(P), + VIB_PROG(funbari), + VIB_PROG(anahori), + VIB_PROG(anaume), + VIB_PROG(impact), + VIB_PROG(ki_ga_taoreru), + VIB_PROG(ki_wo_yusuru), + VIB_PROG(koronoda), + VIB_PROG(surprise), + VIB_PROG(dummy_b), + VIB_PROG(sample) +}; + +static mVibInfo_c mVib_info; + +static void mVibElem_move(mVibElem_c* elem) { + mVibInfo_elem_entry_c* now_entry = elem->entries + elem->now_entry; + int type = now_entry->type; + const mVibWorkData_c* work_data = &mVW_data[type]; + + elem->entry_frame++; + elem->frame_intensity = elem->step1 * elem->step0; + + if (elem->now_entry == mVibctl_ELEM_ENTRY_ATTACK) { + elem->frame_intensity *= (f32)elem->entry_frame * now_entry->step; + } + else if (elem->now_entry == mVibctl_ELEM_ENTRY_RELEASE) { + elem->frame_intensity *= now_entry->step * (now_entry->frames - elem->entry_frame); + } + + if (work_data->data[elem->state_idx] == PAD_MOTOR_RUMBLE) { + /* only rumble when our current intensity has reached or surpassed 1.0f */ + elem->now_intensity += elem->frame_intensity; + + if (elem->now_intensity >= 1.0f) { + elem->command = PAD_MOTOR_RUMBLE; + elem->now_intensity -= 1.0f; + } + else { + elem->command = PAD_MOTOR_STOP; + } + } + else { + elem->command = work_data->data[elem->state_idx]; + } + + { + /* increment current program state index */ + int state_count = work_data->count; + int now_state = elem->state_idx + 1; + elem->state_idx = now_state; + + /* check if program state index has rolled over */ + if (now_state >= state_count) { + elem->state_idx = 0; + } + + /* check if current stage frames have ended, and if so, advanced to next one */ + if (elem->entry_frame >= now_entry->frames) { + elem->now_entry++; + elem->entry_frame = 0; + elem->state_idx = 0; + } + } +} + +static void mVibInfo_elem_entry(mVibInfo_c* vib_info, int total_frames, int attack_type, int sustain_type, int release_type, int attack_frames, int sustain_frames, int release_frames, f32 step) { + if ((attack_frames != 0 || sustain_frames != 0 || release_frames != 0) && (step < 640.0f && vib_info->num_elements < mVibctl_ELEM_NUM)) { + mVibElem_c* elem = vib_info->elements + vib_info->num_elements; + bzero(elem, sizeof(mVibElem_c)); + + /* set attack phase configuration */ + elem->entries[mVibctl_ELEM_ENTRY_ATTACK].type = attack_type; + elem->entries[mVibctl_ELEM_ENTRY_ATTACK].frames = attack_frames; + elem->entries[mVibctl_ELEM_ENTRY_ATTACK].step = (attack_frames > 0) ? (1.0f / (f32)attack_frames) : -1.0f; + + /* set sustain phase configuration */ + elem->entries[mVibctl_ELEM_ENTRY_SUSTAIN].type = sustain_type; + elem->entries[mVibctl_ELEM_ENTRY_SUSTAIN].frames = sustain_frames; + elem->entries[mVibctl_ELEM_ENTRY_SUSTAIN].step = (sustain_frames > 0) ? (1.0f / (f32)sustain_frames) : -1.0f; + + /* set release phase configuration */ + elem->entries[mVibctl_ELEM_ENTRY_RELEASE].type = release_type; + elem->entries[mVibctl_ELEM_ENTRY_RELEASE].frames = release_frames; + elem->entries[mVibctl_ELEM_ENTRY_RELEASE].step = (release_frames > 0) ? (1.0f / (f32)release_frames) : -1.0f; + + if (attack_frames == 0) { + if (sustain_frames == 0) { + elem->now_entry = mVibctl_ELEM_ENTRY_RELEASE; + } + else { + elem->now_entry = mVibctl_ELEM_ENTRY_SUSTAIN; + } + } + + elem->step1 = total_frames * 0.01f; + + if (step < 41.0f) { + elem->step0 = 1.0f; + } + else { + elem->step0 = 1.0f / (step - 40.0f); + } + + vib_info->num_elements++; + } +} + +static void mVibInfo_elem_delete(mVibInfo_c* vib_info, int elem_no) { + int i; + int n_elems = vib_info->num_elements - 1; + + for (i = elem_no; i < n_elems; i++) { + mVibElem_c* dst_elem = vib_info->elements + (i + 1); + mVibElem_c* src_elem = vib_info->elements + i; + bcopy(dst_elem, src_elem, sizeof(mVibElem_c)); + } + + bzero(vib_info->elements + n_elems, sizeof(mVibElem_c)); + vib_info->num_elements--; +} + +static void mVibInfo_set_force_stop(mVibInfo_c* vib_info, int force_stop) { + vib_info->force_stop |= force_stop; +} + +static void mVibInfo_clr_force_stop(mVibInfo_c* vib_info, int force_stop) { + vib_info->force_stop &= ~force_stop; +} + +static void mVibInfo_set_target_elem(mVibInfo_c* vib_info) { + int n_elems = vib_info->num_elements; + f32 max = -10000.0f; + mVibElem_c* elem = vib_info->elements; + mVibElem_c* selected = NULL; + int i; + + for (i = 0; i < n_elems; i++) { + if (elem->frame_intensity > max) { + max = elem->frame_intensity; + selected = elem; + } + elem++; + } + + vib_info->target_elem = selected; +} + +static void mVibInfo_force(mVibInfo_c* vib_info) { + if (Save_Get(config.vibration_enabled)) { + mVibInfo_set_force_stop(vib_info, mVibctl_FLAG_FORCE_STOP0); + } + else { + mVibInfo_clr_force_stop(vib_info, mVibctl_FLAG_FORCE_STOP0); + } + + if (vib_info->force_stop != mVibctl_FLAG_FORCE_STOP_NONE && vib_info->last_force_stop == mVibctl_FLAG_FORCE_STOP_NONE) { + padmgr_force_stop_ON(); + } + else if (vib_info->force_stop == mVibctl_FLAG_FORCE_STOP_NONE && vib_info->last_force_stop != mVibctl_FLAG_FORCE_STOP_NONE) { + padmgr_force_stop_OFF(); + } + + vib_info->last_force_stop = vib_info->force_stop; +} + +static void mVibInfo_set_motor(mVibInfo_c* vib_info) { + if (vib_info->target_elem != NULL) { + padmgr_RumbleSet(PAD0, vib_info->target_elem->command); + } + else { + padmgr_RumbleSet(PAD0, PAD_MOTOR_STOP); + } +} + +static void mVibInfo_move(mVibInfo_c* vib_info) { + int i; + + mVibInfo_force(vib_info); + + for (i = 0; i < vib_info->num_elements; i++) { + mVibElem_move(vib_info->elements + i); + } + + for (i = vib_info->num_elements - 1; i >= 0; i--) { + if (vib_info->elements[i].now_entry >= mVibctl_ELEM_ENTRY_END) { + mVibInfo_elem_delete(vib_info, i); + } + } + + mVibInfo_set_target_elem(vib_info); + + if (vib_info->force_stop == mVibctl_FLAG_FORCE_STOP_NONE) { + mVibInfo_set_motor(vib_info); + } +} + +static void mVibctl_check_title_demo() { + if (mFI_CheckFieldData() && mFI_GET_TYPE(mFI_GetFieldId()) == mFI_FIELDTYPE_FG && mEv_CheckTitleDemo() > 0) { + mVibctl_set_force_stop(mVibctl_FLAG_FORCE_STOP2); + } + else { + mVibctl_clr_force_stop(mVibctl_FLAG_FORCE_STOP2); + } +} + +static void mVibctl_callback(void* arg) { + mVibctl_check_title_demo(); + mVibInfo_move(&mVib_info); +} + +extern void mVibctl_ct() { + bzero(&mVib_info, sizeof(mVibInfo_c)); +} + +extern void mVibctl_init0() { + mVibctl_ct(); +} + +extern void mVibctl_init() { + padmgr_setClient(&mVibctl_callback, NULL); +} + +extern void mVibctl_cleanup() { + padmgr_removeClient(&mVibctl_callback, NULL); +} + +extern void mVibctl_reset() { + mVibctl_clr_force_stop(mVibctl_FLAG_FORCE_STOP_ALL); + mVibctl_set_force_stop(mVibctl_FLAG_FORCE_STOP3); +} + +extern void mVibctl_entry(int total_frames, int attack_type, int sustain_type, int release_type, int attack_frames, int sustain_frames, int release_frames, f32 step) { + mVibInfo_elem_entry(&mVib_info, total_frames, attack_type, sustain_type, release_type, attack_frames, sustain_frames, release_frames, step); +} + +extern void mVibctl_simple_entry(int total_frames, int type, int attack_frames, int sustain_frames, int release_frames, f32 step) { + mVibInfo_elem_entry(&mVib_info, total_frames, type, type, type, attack_frames, sustain_frames, release_frames, step); +} + +extern void mVibctl_set_force_stop(int force_stop) { + mVibInfo_set_force_stop(&mVib_info, force_stop); +} + +extern void mVibctl_clr_force_stop(int force_stop) { + mVibInfo_clr_force_stop(&mVib_info, force_stop); +}