diff --git a/configure.py b/configure.py index 7d23b7dc..72a70c35 100644 --- a/configure.py +++ b/configure.py @@ -959,7 +959,7 @@ config.libs = [ Object(NonMatching, "actor/ac_fieldm_draw.c"), Object(Matching, "actor/ac_flag.c"), Object(Matching, "actor/ac_fuusen.c"), - Object(NonMatching, "actor/ac_garagara.c"), + Object(Matching, "actor/ac_garagara.c"), Object(NonMatching, "actor/ac_ghog.c"), Object(Matching, "actor/ac_go_home_npc.c"), Object(Matching, "actor/ac_goza.c"), diff --git a/include/ac_garagara.h b/include/ac_garagara.h index 3f215c3c..40c8d1aa 100644 --- a/include/ac_garagara.h +++ b/include/ac_garagara.h @@ -3,11 +3,76 @@ #include "types.h" #include "m_actor.h" +#include "c_keyframe.h" #ifdef __cplusplus extern "C" { #endif +#define aGRGR_JOINT_NUM 3 + +enum { + aGRGR_MODE_NONE, + aGRGR_MODE_FUKUBIKI, + + aGRGR_MODE_NUM +}; + +enum { + aGRGR_BALL_FIRST_PLACE, + aGRGR_BALL_SECOND_PLACE, + aGRGR_BALL_THIRD_PLACE, + aGRGR_BALL_NOT_WINNER, + + aGRGR_BALL_TYPE_NUM +}; + +typedef int (*aGRGR_GARAGARA_ANIME_START_PROC)(int); +typedef int (*aGRGR_DELETE_BALL_PROC)(void); + +typedef struct garagara_clip_s { + ACTOR* actorx; + aGRGR_GARAGARA_ANIME_START_PROC garagara_anime_start_proc; + aGRGR_DELETE_BALL_PROC delete_ball_proc; +} aGRGR_clip_c; + +typedef struct garagara_ball_s { + xyz_t pos; + xyz_t old_pos; + int ball_type; + s16 status; + f32 vel_y; + f32 acc_y; + f32 max_vel_y; + f32 vel_xz; + s16 angle; + s16 remove_timer; + xyz_t src_pos; + s16 old_on_ground; + s16 on_ground; + s16 hit_wall; + s16 hit_wall_angle; +} aGRGR_ball_c; + +typedef struct garagara_actor_s GARAGARA_ACTOR; + +struct garagara_actor_s { + ACTOR actor_class; + int mode; + s16 status; + int ball_type; + int anime_frame; + cKF_SkeletonInfo_R_c keyframe; + s_xyz work[aGRGR_JOINT_NUM]; + s_xyz morph[aGRGR_JOINT_NUM]; + Mtx mtx[2][aGRGR_JOINT_NUM]; + aGRGR_clip_c clip; + u8* bank; + int request_flag; + s16 request_status; + aGRGR_ball_c ball; +}; + extern ACTOR_PROFILE Garagara_Profile; #ifdef __cplusplus @@ -15,4 +80,3 @@ extern ACTOR_PROFILE Garagara_Profile; #endif #endif - diff --git a/include/audio_defs.h b/include/audio_defs.h index 73f83c9f..fb936bed 100644 --- a/include/audio_defs.h +++ b/include/audio_defs.h @@ -71,7 +71,7 @@ typedef enum audio_sound_effects { NA_SE_TEMOCHI_KAZAGURUMA, NA_SE_REGISTER = 0x50, - + NA_SE_51 = 0x51, NA_SE_52 = 0x52, NA_SE_ITEM_HORIDASHI = 0x57, diff --git a/include/m_clip.h b/include/m_clip.h index 0ac9b03b..ca54a5ba 100644 --- a/include/m_clip.h +++ b/include/m_clip.h @@ -30,6 +30,7 @@ #include "ac_sign.h" #include "ac_boxTrick01.h" #include "ac_broker_design.h" +#include "ac_garagara.h" #ifdef __cplusplus extern "C" { @@ -80,7 +81,7 @@ typedef struct clip_s { /* 0x0B4 */ EffectBG_MAKE_EFFECTBG_PROC make_effect_bg_proc; /* 0x0B8 */ aShopUmbrella_Clip_c* shop_umbrella_clip; /* 0x0BC */ aAR_Clip_c* arrange_room_clip; - /* 0x0C0 */ void* _0C0; + /* 0x0C0 */ aGRGR_clip_c* garagara_clip; /* 0x0C4 */ void* _0C4; /* 0x0C8 */ void* shrine_clip; /* 0x0CC */ void* _0CC; diff --git a/src/actor/ac_garagara.c b/src/actor/ac_garagara.c new file mode 100644 index 00000000..3ddcbc44 --- /dev/null +++ b/src/actor/ac_garagara.c @@ -0,0 +1,248 @@ +#include "ac_garagara.h" + +#include "m_common_data.h" +#include "m_malloc.h" +#include "m_rcp.h" +#include "sys_matrix.h" +#include "libultra/libultra.h" + +enum { + aGRGR_STATUS_WAIT, + aGRGR_STATUS_ROTATE, + + aGRGR_STATUS_NUM +}; + +enum { + aGRBL_STATUS_NONE, + aGRBL_STATUS_START, + aGRBL_STATUS_KOROKORO, + aGRBL_STATUS_DELETE, + + aGRBL_STATUS_NUM +}; + +static void Garagara_Actor_ct(ACTOR* actorx, GAME* game); +static void Garagara_Actor_dt(ACTOR* actorx, GAME* game); +static void Garagara_Actor_move_dummy(ACTOR* actorx, GAME* game); +static void Garagara_Actor_draw_dummy(ACTOR* actorx, GAME* game); +static void Garagara_Actor_move(ACTOR* actorx, GAME* game); +static void Garagara_Actor_draw(ACTOR* actorx, GAME* game); + +// clang-format off +ACTOR_PROFILE Garagara_Profile = { + mAc_PROFILE_GARAGARA, + ACTOR_PART_FG, + ACTOR_STATE_NO_DRAW_WHILE_CULLED | ACTOR_STATE_NO_MOVE_WHILE_CULLED, + EMPTY_NO, + ACTOR_OBJ_BANK_KEEP, + sizeof(GARAGARA_ACTOR), + &Garagara_Actor_ct, + &Garagara_Actor_dt, + &Garagara_Actor_move_dummy, + &Garagara_Actor_draw_dummy, + NULL, +}; +// clang-format on + +typedef void (*aGRGR_INIT_PROC)(ACTOR*, GAME*); +typedef void (*aGRGR_MOVE_PROC)(ACTOR*, GAME*); + +static void aGaragara_WaitInit(ACTOR* actorx, GAME* game); +static void aGaragara_RotateInit(ACTOR* actorx, GAME* game); + +static void aGaragara_WaitMove(ACTOR* actorx, GAME* game); +static void aGaragara_RotateMove(ACTOR* actorx, GAME* game); + +static aGRGR_INIT_PROC aGaragara_init_proc_table[] = { &aGaragara_WaitInit, &aGaragara_RotateInit }; +static aGRGR_MOVE_PROC aGaragara_move_proc_table[] = { &aGaragara_WaitMove, &aGaragara_RotateMove }; + +#include "../src/actor/ac_garagara_ball.c_inc" + +static void aGRGR_AnimeCt(cKF_Skeleton_R_c* skeleton, cKF_Animation_R_c* animation, cKF_SkeletonInfo_R_c* kf, + s_xyz* work, s_xyz* morph, f32 speed) { + cKF_SkeletonInfo_R_ct(kf, skeleton, animation, work, morph); + cKF_SkeletonInfo_R_init_standard_stop(kf, animation, NULL); + kf->frame_control.speed = speed; + cKF_SkeletonInfo_R_play(kf); +} + +static int aGaragara_RequestStatus(ACTOR* actorx, s16 status) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + if (status >= 0 && status < aGRGR_STATUS_NUM) { + garagara->request_flag = TRUE; + garagara->request_status = status; + return TRUE; + } + + garagara->status = aGRGR_STATUS_WAIT; + return FALSE; +} + +static int aGaragara_GaragaraAnimeStart(int ball_type) { + if (CLIP(garagara_clip) != NULL) { + ACTOR* actorx = CLIP(garagara_clip)->actorx; + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + if (actorx != NULL) { + aGaragara_RequestStatus(actorx, aGRGR_STATUS_ROTATE); + garagara->ball_type = ball_type & 3; + return TRUE; + } + } + + return FALSE; +} + +static int aGaragara_DeleteBall(void) { + if (CLIP(garagara_clip) != NULL) { + ACTOR* actorx = CLIP(garagara_clip)->actorx; + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + garagara->ball.status = aGRBL_STATUS_NONE; + return TRUE; + } + + return FALSE; +} + +static void aGRGR_DecideGaragaraMode(ACTOR* actorx) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + if (Common_Get(tanuki_shop_status) == mSP_TANUKI_SHOP_STATUS_FUKUBIKI) { + garagara->mode = aGRGR_MODE_FUKUBIKI; + } else { + garagara->mode = aGRGR_MODE_NONE; + } +} + +static void aGRGR_CommonCt(ACTOR* actorx, GAME* game) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + garagara->clip.garagara_anime_start_proc = &aGaragara_GaragaraAnimeStart; + garagara->clip.delete_ball_proc = &aGaragara_DeleteBall; + garagara->clip.actorx = actorx; + CLIP(garagara_clip) = &garagara->clip; +} + +extern cKF_Skeleton_R_c cKF_bs_r_obj_gara; +extern cKF_Animation_R_c cKF_ba_r_obj_gara; + +static void aGRGR_FukubikiDayCt(ACTOR* actorx, GAME* game) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + aGRGR_AnimeCt(&cKF_bs_r_obj_gara, &cKF_ba_r_obj_gara, &garagara->keyframe, garagara->work, garagara->morph, 0.0f); + actorx->mv_proc = &Garagara_Actor_move; + actorx->dw_proc = &Garagara_Actor_draw; +} + +static void aGRGR_DummyCt(ACTOR* actorx, GAME* game) { + // nothing +} + +static void Garagara_Actor_ct(ACTOR* actorx, GAME* game) { + static const mActor_proc construct_table[] = { &aGRGR_DummyCt, &aGRGR_FukubikiDayCt }; + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + aGRGR_DecideGaragaraMode(actorx); + aGRGR_CommonCt(actorx, game); + (*construct_table[garagara->mode & 1])(actorx, game); +} + +static void Garagara_Actor_dt(ACTOR* actorx, GAME* game) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + if (garagara->bank != NULL) { + zelda_free(garagara->bank); + } + + CLIP(garagara_clip) = NULL; +} + +static void Garagara_Actor_draw_dummy(ACTOR* actorx, GAME* game) { + // nothing +} + +static void Garagara_Actor_draw(ACTOR* actorx, GAME* game) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + cKF_SkeletonInfo_R_c* kf_p = &garagara->keyframe; + int idx = game->frame_counter & 1; + Mtx* mtx = garagara->mtx[idx]; + xyz_t* pos = &actorx->world.position; + + _texture_z_light_fog_prim(game->graph); + Matrix_translate(pos->x, pos->y, pos->z, 0); + Matrix_scale(0.01f, 0.01f, 0.01f, 1); + + OPEN_DISP(game->graph); + + gSPMatrix(NEXT_POLY_OPA_DISP, _Matrix_to_Mtx_new(game->graph), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + CLOSE_DISP(game->graph); + + cKF_Si3_draw_R_SV(game, kf_p, mtx, NULL, NULL, actorx); + aGRBL_draw(&garagara->ball, game); +} + +static void aGaragara_WaitInit(ACTOR* actorx, GAME* game) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + aGRGR_AnimeCt(&cKF_bs_r_obj_gara, &cKF_ba_r_obj_gara, &garagara->keyframe, garagara->work, garagara->morph, 0.0f); +} + +static void aGaragara_RotateInit(ACTOR* actorx, GAME* game) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + aGRGR_AnimeCt(&cKF_bs_r_obj_gara, &cKF_ba_r_obj_gara, &garagara->keyframe, garagara->work, garagara->morph, 0.5f); + sAdo_OngenTrgStart(MONO(NA_SE_51), &actorx->world.position); +} + +static void aGaragara_WaitMove(ACTOR* actorx, GAME* game) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + cKF_SkeletonInfo_R_c* kf_p = &garagara->keyframe; + + cKF_SkeletonInfo_R_play(kf_p); +} + +static void aGaragara_RotateMove(ACTOR* actorx, GAME* game) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + cKF_SkeletonInfo_R_c* kf_p = &garagara->keyframe; + + if (garagara->anime_frame == 46) { + xyz_t pos; + + pos.x = actorx->world.position.x + 10.0f; + pos.y = actorx->world.position.y + 10.0f; + pos.z = actorx->world.position.z; + + aGRBL_make(&actorx->world.position, &garagara->ball, &pos, garagara->ball_type); + } + + if (cKF_SkeletonInfo_R_play(kf_p) == cKF_STATE_STOPPED) { + aGaragara_RequestStatus(actorx, aGRGR_STATUS_WAIT); + } +} + +static void Garagara_Actor_move_dummy(ACTOR* actorx, GAME* game) { + // nothing +} + +static void Garagara_Actor_move(ACTOR* actorx, GAME* game) { + GARAGARA_ACTOR* garagara = (GARAGARA_ACTOR*)actorx; + + aGRBL_move(&garagara->ball, game); + if (garagara->request_flag) { + (*aGaragara_init_proc_table[garagara->request_status])(actorx, game); + garagara->status = garagara->request_status; + garagara->request_flag = FALSE; + garagara->anime_frame = 0; + } + + if (garagara->status >= 0 && garagara->status < aGRGR_STATUS_NUM) { + (*aGaragara_move_proc_table[garagara->status])(actorx, game); + garagara->anime_frame++; + } else { + garagara->status = aGRGR_STATUS_WAIT; + } +} diff --git a/src/actor/ac_garagara_ball.c_inc b/src/actor/ac_garagara_ball.c_inc new file mode 100644 index 00000000..cceab36b --- /dev/null +++ b/src/actor/ac_garagara_ball.c_inc @@ -0,0 +1,152 @@ +static void aGRBL_make(const xyz_t* src_pos, aGRGR_ball_c* ball, const xyz_t* pos, int ball_type) { + bzero(ball, sizeof(aGRGR_ball_c)); + ball->pos = *pos; + ball->old_pos = *pos; + ball->ball_type = ball_type; + ball->status = aGRBL_STATUS_START; + ball->src_pos = *src_pos; + ball->old_on_ground = FALSE; + ball->on_ground = FALSE; + ball->hit_wall = FALSE; + ball->hit_wall_angle = 0; + ball->vel_y = -0.15f - RANDOM_F(0.1f); + ball->acc_y = -0.15f; + ball->max_vel_y = -1.0f; + ball->vel_xz = 0.225f + RANDOM_F(0.1f); + ball->angle = DEG2SHORT_ANGLE2(90.0f); + ball->angle += (int)(RANDOM_F((f32)DEG2SHORT_ANGLE2(22.5f)) - (f32)DEG2SHORT_ANGLE2(11.25f)); +} + +static void aGRBL_bgCheck(aGRGR_ball_c* ball) { + f32 ground_y = ball->src_pos.y; + f32 x_max = ball->src_pos.x + 20.0f; + f32 x_min = ball->src_pos.x - 20.0f; + f32 z_min = ball->src_pos.z - 7.0f; + f32 z_max = ball->src_pos.z + 7.0f; + + ball->old_on_ground = ball->on_ground; + ball->on_ground = FALSE; + + if (ball->pos.y - 2.0f < ground_y) { + ball->pos.y = 2.0f + ground_y; + ball->on_ground = TRUE; + } + + ball->hit_wall = FALSE; + if (x_max < ball->pos.x) { + ball->pos.x = x_max; + ball->hit_wall = TRUE; + ball->hit_wall_angle = DEG2SHORT_ANGLE2(-90.0f); + } + + if (ball->pos.x < x_min) { + ball->pos.x = x_min; + ball->hit_wall = TRUE; + ball->hit_wall_angle = DEG2SHORT_ANGLE2(90.0f); + } + + if (z_max < ball->pos.z) { + ball->pos.z = z_max; + ball->hit_wall = TRUE; + ball->hit_wall_angle = DEG2SHORT_ANGLE2(-180.0f); + } + + if (ball->pos.z < z_min) { + ball->pos.z = z_min; + ball->hit_wall = TRUE; + ball->hit_wall_angle = DEG2SHORT_ANGLE2(0.0f); + } +} + +static void aGRBL_move_position(aGRGR_ball_c* ball) { + ball->vel_y += ball->acc_y; + + if (ball->vel_y < ball->max_vel_y) { + ball->vel_y = ball->max_vel_y; + } + + ball->pos.y += ball->vel_y; + ball->pos.x += ball->vel_xz * sin_s(ball->angle); + ball->pos.z += ball->vel_xz * cos_s(ball->angle); +} + +static void aGRBL_NoneMove(aGRGR_ball_c* ball, GAME* game) { + // nothing +} + +static void aGRBL_StartMove(aGRGR_ball_c* ball, GAME* game) { + ball->status = aGRBL_STATUS_KOROKORO; + aGRBL_move_position(ball); + aGRBL_bgCheck(ball); +} + +static void aGRBL_KorokoroMove(aGRGR_ball_c* ball, GAME* game) { + if (ball->hit_wall) { + s16 diff; + + diff = (s16)(ball->angle + DEG2SHORT_ANGLE2(180.0f)) - ball->hit_wall_angle; + ball->angle -= diff; + ball->vel_xz *= 0.985f; + } + + if (ball->on_ground) { + if (ball->old_on_ground == TRUE) { + ball->vel_xz *= 0.99f; + } else { + ball->vel_y *= -0.4f; + } + } + + aGRBL_move_position(ball); + aGRBL_bgCheck(ball); +} + +static void aGRBL_DeleteMove(aGRGR_ball_c* ball, GAME* game) { + // nothing +} + +typedef void (*aGRBL_MOVE_PROC)(aGRGR_ball_c*, GAME*); + +static void aGRBL_move(aGRGR_ball_c* ball, GAME* game) { + static aGRBL_MOVE_PROC ball_move_table[] = { + &aGRBL_NoneMove, + &aGRBL_StartMove, + &aGRBL_KorokoroMove, + &aGRBL_DeleteMove, + }; + + (*ball_move_table[ball->status])(ball, game); + ball->old_pos = ball->pos; +} + +extern u8 act_fukutama1_tex[]; +extern u8 act_fukutama2_tex[]; +extern u8 act_fukutama3_tex[]; +extern u8 act_fukutama4_tex[]; + +static u8* aGRBL_texture_table[] = { + act_fukutama1_tex, + act_fukutama2_tex, + act_fukutama3_tex, + act_fukutama4_tex, +}; + +extern Gfx act_fukutama_modelT[]; + +static void aGRBL_draw(aGRGR_ball_c* ball, GAME* game) { + if (ball->status != aGRBL_STATUS_NONE) { + Mtx* mtx = GRAPH_ALLOC_TYPE(game->graph, Mtx, 1); + GAME_PLAY* play = (GAME_PLAY*)game; + + _texture_z_light_fog_prim(game->graph); + OPEN_DISP(game->graph); + + suMtxMakeTS(mtx, 0.01f, 0.01f, 0.01f, ball->pos.x, ball->pos.y, ball->pos.z); + gSPMatrix(NEXT_POLY_OPA_DISP, mtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPMatrix(NEXT_POLY_OPA_DISP, play->billboard_mtx_p, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_MODELVIEW); + gSPSegment(NEXT_POLY_OPA_DISP, ANIME_1_TXT_SEG, aGRBL_texture_table[ball->ball_type]); + gSPDisplayList(NEXT_POLY_OPA_DISP, act_fukutama_modelT); + + CLOSE_DISP(game->graph); + } +}