diff --git a/config/rel_slices.yml b/config/rel_slices.yml index b6ed1821..bec6fb1e 100644 --- a/config/rel_slices.yml +++ b/config/rel_slices.yml @@ -143,6 +143,10 @@ ac_sample.c: .rodata: [0x80643860, 0x80643868] .data: [0x8065FA40, 0x8065FAA0] .bss: [0x812F96A0, 0x812F96E0] +ac_airplane.c: + .text: [0x8040F614, 0x804104AC] + .rodata: [0x80643868, 0x80643918] + .data: [0x8065FAA0, 0x8065FB68] ac_animal_logo.c: .text: [0x804104AC, 0x804117D4] .rodata: [0x80643918, 0x80643A88] diff --git a/include/ac_airplane.h b/include/ac_airplane.h new file mode 100644 index 00000000..27d8af1f --- /dev/null +++ b/include/ac_airplane.h @@ -0,0 +1,62 @@ +#ifndef AC_AIRPLANE_H +#define AC_AIRPLANE_H + +#include "types.h" +#include "m_actor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + aAp_STATUS_FREE_FLY_MOVE, + aAp_STATUS_FALL_FLY_MOVE, + aAp_STATUS_FALL_FLY_MOVE2, + aAp_STATUS_SOMER_FLY_MOVE, + aAp_STATUS_START_FLY_MOVE, + aAp_STATUS_STOP_FLY_MOVE, + aAp_STATUS_PLAYER_CATCH, + + aAp_STATUS_NUM +}; + +enum { + aAp_TILT_DOWN, + aAp_TILT_UP, + + aAp_TILT_NUM +}; + +typedef struct airplane_s { + ACTOR actor_class; + + s16 status; + s16 tilt_status; + f32 speed; + f32 y_speed; + f32 rotY_goal; + f32 rotY_min; + + f32 rotY; + f32 rotX; + f32 rotZ; + + int joystick_y; + int joystick_x; + + s16 ground_timer; + + int wind_frame; + int wind_change_frame; + xyz_t* wind; + + u8 _1AC[28]; // unused +} AIRPLANE_ACTOR; + +extern ACTOR_PROFILE Airplane_Profile; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/ef_effect_control.h b/include/ef_effect_control.h new file mode 100644 index 00000000..c47be7a4 --- /dev/null +++ b/include/ef_effect_control.h @@ -0,0 +1,41 @@ +#ifndef EF_EFFECT_CONTROL_H +#define EF_EFFECT_CONTROL_H + +#include "types.h" +#include "m_lib.h" +#include "m_actor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum effect_type { + eEC_EFFECT_SHOCK, + eEC_EFFECT_DUST, + eEC_EFFECT_MUKA, + eEC_EFFECT_WARAU, + /* TODO: finish */ + + eEC_EFFECT_NUM = 126 +}; + +typedef void (*eEC_NAME2EFFECTMAKE_PROC)(int, xyz_t, int, short, GAME*, u16, s16, s16); + +typedef struct effect_control_clip_s { + eEC_NAME2EFFECTMAKE_PROC effect_make_proc; + /* TODO: finish */ +} eEC_EffectControl_Clip_c; + +typedef struct effect_control_s EFFECT_CONTROL_ACTOR; + +struct effect_control_s { + ACTOR actor_class; + eEC_EffectControl_Clip_c clip; +}; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/m_actor.h b/include/m_actor.h index b44f11fc..b9eab3e0 100644 --- a/include/m_actor.h +++ b/include/m_actor.h @@ -5,6 +5,8 @@ #include "m_actor_type.h" #include "game.h" #include "m_lib.h" +#include "m_lights.h" +#include "m_collision_bg.h" #ifdef __cplusplus extern "C" { @@ -18,6 +20,7 @@ typedef void (*mActor_proc)(ACTOR*, GAME*); #define ACTOR_OBJ_BANK_NONE 0 #define ACTOR_OBJ_BANK_3 3 /* TODO: rename, also likely an enum */ +#define ACTOR_OBJ_BANK_7 7 #define ACTOR_OBJ_BANK_12 12 enum actor_part { @@ -48,7 +51,7 @@ typedef struct actor_profile_s { /* 0x20 */ mActor_proc sv_proc; /* save */ } ACTOR_PROFILE; -typedef void (*mActor_shadow_proc)(ACTOR*, void*, GAME_PLAY*); /* TODO: void* is actually LightsN*, see m_lights */ +typedef void (*mActor_shadow_proc)(ACTOR*, LightsN*, GAME_PLAY*); /* sizeof(Shape_Info) == 0x48 */ typedef struct actor_shape_info_s { @@ -95,8 +98,7 @@ struct actor_s { /* 0x078 */ f32 gravity; /* gravity acting on actor */ /* 0x07C */ f32 max_velocity_y; /* maximum y velocity possible due to gravity, usually -20.0f */ /* 0x080 */ f32 ground_y; /* vertical position of ground underneath actor */ - /* 0x084 */ u8 bg_collision_data[0xB2-0x84]; /* actor collision data, TODO: implement, mCoBG */ - /* 0x0B2 */ u8 unused_b2[2]; + /* 0x084 */ mCoBG_Check_c bg_collision_check; /* background object collision info with actor */ /* 0x0B4 */ u8 unknown_b4; /* some sort of flag */ /* 0x0B5 */ u8 drawn; /* was drawn flag, TRUE = actor was drawn, FALSE = actor was not drawn */ /* 0x0B6 */ s16 player_angle_y; /* Y angle (yaw) between actor and player actor */ @@ -141,6 +143,11 @@ typedef struct actor_info_s { extern void Actor_delete(ACTOR* actor); extern ACTOR* Actor_info_fgName_search(Actor_info* actor_info, mActor_name_t fg_name, int part); extern void Actor_world_to_eye(ACTOR* actor, f32 eye_height); +extern void Shape_Info_init(ACTOR* actor, f32 y_ofs, mActor_shadow_proc shadow_proc, f32 shadow_sizeX, f32 shadow_sizeZ); +extern void Actor_position_moveF(ACTOR* actor); + +extern void mAc_ActorShadowCircle(ACTOR* actor, LightsN* lightsN, GAME_PLAY* play); +extern void mAc_ActorShadowEllipse(ACTOR* actor, LightsN* lightsN, GAME_PLAY* play); #ifdef __cplusplus } diff --git a/include/m_clip.h b/include/m_clip.h index bba386d6..46249122 100644 --- a/include/m_clip.h +++ b/include/m_clip.h @@ -6,6 +6,7 @@ #include "ac_insect_h.h" #include "ac_structure.h" #include "ac_animal_logo.h" +#include "ef_effect_control.h" #ifdef __cplusplus extern "C" { @@ -17,7 +18,8 @@ typedef struct clip_s { /* 0x07C */ aINS_Clip_c* insect_clip; /* 0x080 */ void* _080[(0x08C - 0x080) / sizeof(void*)]; /* 0x08C */ aSTR_Clip_c* structure_clip; - /* 0x090 */ void* _090[(0x0AC - 0x090) / sizeof(void*)]; + /* 0x090 */ eEC_EffectControl_Clip_c* effect_clip; + /* 0x094 */ void* _094[(0x0AC - 0x094) / sizeof(void*)]; /* 0x0AC */ aGYO_Clip_c* gyo_clip; /* 0x0B0 */ void* _0B0[(0x0DC - 0x0B0) / sizeof(void*)]; /* 0x0DC */ aAL_Clip_c* animal_logo_clip; diff --git a/include/m_collision_bg.h b/include/m_collision_bg.h index f161d66e..3a3ae1ec 100644 --- a/include/m_collision_bg.h +++ b/include/m_collision_bg.h @@ -51,14 +51,47 @@ typedef union collision_bg_u { mCoBG_CollisionData_c data; } mCoBG_Collision_u; +typedef struct collision_bg_check_result_s { + u32 on_ground:1; + u32 hit_attribute_wall:5; + u32 hit_wall:5; + u32 hit_wall_count:3; + u32 unk_flag0:1; + u32 unit_attribute:6; + u32 is_on_move_bg_obj:1; + u32 is_in_water:1; + u32 unk_flag1:1; + u32 unk_flag2:1; + u32 unk_flag3:1; + u32 unk_flag4:1; + u32 unk_flag5:1; + u32 unk_flag6:4; +} mCoBG_CheckResult_c; + +typedef struct wall_info_s { + s16 angleY; + s16 type; +} mCoBG_WallInfo_c; + +typedef struct collision_bg_check_s { + mCoBG_Collision_u collision_units[5]; + mCoBG_CheckResult_c result; + f32 wall_top_y; + f32 wall_bottom_y; + f32 ground_y; + mCoBG_WallInfo_c wall_info[2]; + s16 in_front_wall_angle_y; +} mCoBG_Check_c; + extern u32 mCoBG_Wpos2BgAttribute_Original(xyz_t wpos); extern u32 mCoBG_Wpos2Attribute(xyz_t wpos, char* is_diggable); extern int mCoBG_CheckWaterAttribute(u32 attribute); -extern f32 mCoBG_GetBgY_AngleS_FromWpos(s_xyz* angle_to_ground, xyz_t wpos, f32 offset); +extern f32 mCoBG_GetBgY_AngleS_FromWpos(s_xyz* angle_to_ground, xyz_t wpos, f32 offset_y); 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 int mCoBG_Attribute2CheckPlant(u32 attribute, const xyz_t* wpos); +extern void mCoBG_BgCheckControll(xyz_t* reverse_pos, ACTOR* actor, f32 check_range, f32 offset_y, s16 wall_attr_check, s16 no_reverse, s16 check_type); 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_lib.h b/include/m_lib.h index bdad8af6..e36ad015 100644 --- a/include/m_lib.h +++ b/include/m_lib.h @@ -24,9 +24,11 @@ extern "C" { /* radians -> short angle */ #define RAD2SHORT_ANGLE(rad) ((s16)(int)((rad) * (65536.0f / (2.0f * F_PI)))) +#define RAD2SHORTANGLE(rad) ((s16)((32768.0f / F_PI) * ((f32)(rad)))) /* short angle -> radians */ #define SHORT2RAD_ANGLE(s) ((((f32)(s)) / (65536.0f / (2.0f * F_PI)))) +#define SHORTANGLE2RAD(sangle) ((F_PI / 32768.0f) * ((f32)(sangle))) /* degrees -> short angle */ #define DEG2SHORT_ANGLE(deg) ((s16)((deg) * (65536.0f / 360.0f))) @@ -34,6 +36,12 @@ extern "C" { /* short angle -> degrees */ #define SHORT2DEG_ANGLE(s) ((((f32)(s)) / (65536.0f / 360.0f))) +/* radians -> degrees */ +#define RAD2DEG(rad) ((180.0f / F_PI) * (rad)) + +/* degrees -> radians */ +#define DEG2RAD(deg) ((F_PI / 180.0f) * (deg)) + typedef struct rgba_t { //can be put in other place u8 r, g, b, a; } rgba_t; diff --git a/include/m_lights.h b/include/m_lights.h new file mode 100644 index 00000000..827d13f6 --- /dev/null +++ b/include/m_lights.h @@ -0,0 +1,20 @@ +#ifndef M_LIGHTS_H +#define M_LIGHTS_H + +#include "types.h" +#include "PR/mbi.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct lightsn_s { + u8 diffuse_count; + Lights7 lights; +} LightsN; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/m_name_table.h b/include/m_name_table.h index f68574e3..cee631f9 100644 --- a/include/m_name_table.h +++ b/include/m_name_table.h @@ -472,6 +472,9 @@ extern int mNT_check_unknown(mActor_name_t item_no); #define TRAIN0 0x580A #define TRAIN1 0x580B +#define ETC_START 0x8000 +#define ETC_AIRPLANE ETC_START + #define MISC_ACTOR_START 0x9000 #define MISC_ACTOR_SAMPLE MISC_ACTOR_START diff --git a/include/m_player_lib.h b/include/m_player_lib.h index 37cec152..1a467617 100644 --- a/include/m_player_lib.h +++ b/include/m_player_lib.h @@ -14,6 +14,8 @@ extern void mPlib_SetData2_controller_data_for_title_demo(mActor_name_t tool); extern void mPlib_request_main_invade_type1(GAME_PLAY* play); extern mActor_name_t mPlib_Get_itemNo_forWindow(); extern int mPlib_check_able_change_camera_normal_index(); +extern void mPlib_request_main_refuse_type1(GAME_PLAY* play); +extern void mPlib_request_main_wait_type3(GAME_PLAY* play); #ifdef __cplusplus } diff --git a/include/sys_math3d.h b/include/sys_math3d.h index a81ed50b..e4ca3501 100644 --- a/include/sys_math3d.h +++ b/include/sys_math3d.h @@ -16,6 +16,9 @@ typedef struct math_3d_pipe_s { } Math3D_pipe_c; extern f32 Math3DVecLength(xyz_t* vec); +extern void sMath_RotateX(xyz_t* pos, f32 rad); +extern void sMath_RotateY(xyz_t* pos, f32 rad); +extern void sMath_RotateZ(xyz_t* pos, f32 rad); extern xyz_t ZeroVec; diff --git a/rel/ac_airplane.c b/rel/ac_airplane.c new file mode 100644 index 00000000..a746b128 --- /dev/null +++ b/rel/ac_airplane.c @@ -0,0 +1,387 @@ +#include "ac_airplane.h" + +#include "m_collision_bg.h" +#include "m_player.h" +#include "m_player_lib.h" +#include "sys_matrix.h" +#include "sys_math3d.h" +#include "sys_math.h" +#include "libc64/qrand.h" +#include "m_controller.h" +#include "m_name_table.h" +#include "m_common_data.h" + +static void Airplane_Actor_ct(ACTOR* actor, GAME* game); +static void Airplane_Actor_dt(ACTOR* actor, GAME* game); +static void Airplane_Actor_move(ACTOR* actor, GAME* game); +static void Airplane_Actor_draw(ACTOR* actor, GAME* game); + +/* TODO: ct, dt, & draw go in this TU, while all others go in a separate TU. Probably something like ac_airplane_move.c_inc */ + +ACTOR_PROFILE Airplane_Profile = { + 0x05, // TODO: enum + ACTOR_PART_BG, + ACTOR_STATE_NO_MOVE_WHILE_CULLED | ACTOR_STATE_NO_DRAW_WHILE_CULLED, + ETC_AIRPLANE, + ACTOR_OBJ_BANK_7, + sizeof(AIRPLANE_ACTOR), + + &Airplane_Actor_ct, + &Airplane_Actor_dt, + &Airplane_Actor_move, + &Airplane_Actor_draw, + NULL +}; + +static void Airplane_Actor_ct(ACTOR* actor, GAME* game) { + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + + Shape_Info_init(actor, 0.0f, &mAc_ActorShadowEllipse, 6.5f, 6.5f); + airplane->status = 5; + + airplane->speed = 6.2f; + airplane->y_speed = 0.0f; + airplane->actor_class.speed = airplane->speed; + + airplane->rotY = RAD2DEG(SHORTANGLE2RAD((f32)actor->world_rotation.y)); + airplane->rotY_goal = airplane->rotY; + airplane->rotY_min = airplane->rotY; + airplane->rotZ = 0.0f; + airplane->rotX = 0.0f; + + airplane->ground_timer = 0; + airplane->wind_frame = 0; + airplane->wind_change_frame = 0; + airplane->wind = NULL; + + actor->world_position.y = mCoBG_GetBgY_AngleS_FromWpos(NULL, actor->world_position, -5.5f); +} + +static void Airplane_Actor_dt(ACTOR* actor, GAME* game) { } + +extern Gfx glider_model[]; + +static void Airplane_Actor_draw(ACTOR* actor, GAME* game) { + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + + if (airplane->status != aAp_STATUS_PLAYER_CATCH && airplane->status != aAp_STATUS_START_FLY_MOVE) { + GRAPH* g = game->graph; + + _texture_z_light_fog_prim(g); + Matrix_RotateX(RAD2SHORTANGLE(DEG2RAD(airplane->rotX)), 1); + Matrix_RotateZ(RAD2SHORTANGLE(DEG2RAD(airplane->rotZ)), 1); + Matrix_scale(50.0f, 50.0f, 50.0f, 1); + + OPEN_DISP(g); + gSPMatrix(NOW_POLY_OPA_DISP++, _Matrix_to_Mtx_new(game->graph), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(NOW_POLY_OPA_DISP++, glider_model); + CLOSE_DISP(g); + } +} + +static void aAp_RubberMove(f32* now, f32 target, f32 step) { + *now += step * (target - *now); +} + +static void aAp_FallByWall(ACTOR* actor) { + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + if (actor->bg_collision_check.result.hit_wall == 0) { + return; + } + + airplane->status = aAp_STATUS_FALL_FLY_MOVE; +} + +static void aAp_GroundFriction(ACTOR* actor, GAME* game) { + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + if (actor->bg_collision_check.result.on_ground) { + /* slow down speed since we're on the ground */ + aAp_RubberMove(&airplane->speed, 0.0f, 0.6f); + + if (airplane->speed < 1.0f) { + airplane->speed = 0.0f; + airplane->status = aAp_STATUS_STOP_FLY_MOVE; + + if (airplane->ground_timer >= 5) { + airplane->ground_timer = 0; + + (*Common_Get(clip.effect_clip)->effect_make_proc)(eEC_EFFECT_DUST, actor->world_position, 1, actor->world_rotation.y, game, actor->npc_id, 0, 8); + } + + airplane->ground_timer++; + } + } +} + +static void aAp_FreeFlyMove(ACTOR* actor, GAME* game) { + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + + aAp_RubberMove(&airplane->speed, 3.0f, 0.01f); + aAp_RubberMove(&airplane->rotX, 19.0f, 0.045f); + aAp_RubberMove(&airplane->rotZ, 0.0f, 0.05f); + aAp_GroundFriction(actor, game); + aAp_FallByWall(actor); +} + +static void aAp_SomerFlyMove(ACTOR* actor, GAME* game) { + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + switch (airplane->tilt_status) { + case aAp_TILT_DOWN: + { + aAp_RubberMove(&airplane->rotX, -89.0f, 0.2f); + aAp_RubberMove(&airplane->speed, 9.0f, 0.1f); + + if (airplane->rotX < -87.0f) { + airplane->tilt_status = aAp_TILT_UP; + airplane->rotX = 60.0f; + } + + break; + } + + case aAp_TILT_UP: + { + aAp_RubberMove(&airplane->rotX, 0.0f, 0.04f); + aAp_RubberMove(&airplane->speed, 10.0f, 0.1f); + + if (ABS(airplane->rotX) < 3.0f) { + airplane->status = aAp_STATUS_FREE_FLY_MOVE; + airplane->tilt_status = aAp_TILT_DOWN; + } + + aAp_GroundFriction(actor, game); + break; + } + } + + aAp_FallByWall(actor); +} + +static int aAp_SelectFly(AIRPLANE_ACTOR* actor, int stick_y) { + int mode = (int)(fqrand() * 7.0f) & 1; + if (stick_y > 67) { + if (mode != 0) { + actor->status = aAp_STATUS_FALL_FLY_MOVE2; + } + else { + actor->status = aAp_STATUS_SOMER_FLY_MOVE; + actor->tilt_status = aAp_TILT_DOWN; + } + } + else { + actor->status = aAp_STATUS_FREE_FLY_MOVE; + } + + return TRUE; +} + +static void aAp_StartFlyMove(ACTOR* actor, GAME* game) { + static s16 goal_angle_random[8] = { 2366, 2002, -1092, -2366, 0, 4368, -4186, 4186 }; + static s16 goal_angle_random2[8] = { 12740, 8190, -9100, -8190, -10920, 10920, 9100, 0 }; + + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + int now_stick_y = getJoystick_Y(); + PLAYER_ACTOR* player = get_player_actor_withoutCheck((GAME_PLAY*)game); + int goal = fqrand() * 8.0f; + GAME_PLAY* play = (GAME_PLAY*)game; + int last_stick_y = airplane->joystick_y; + s16* goal_angle_random_tbl; + + if (now_stick_y - last_stick_y > 30) { + f32 stick_y = ABS((f32)last_stick_y); + int adjusted_y = ABS(last_stick_y); + + airplane->speed = ABS((f32)last_stick_y) * 0.0875f + 3.0f; + aAp_SelectFly(airplane, adjusted_y); + + goal_angle_random_tbl = adjusted_y > 50 ? goal_angle_random2 : goal_angle_random; + + actor->world_position.y = player->actor_class.world_position.y + 25.0f; + actor->world_position.x = player->actor_class.world_position.x; + actor->world_position.z = player->actor_class.world_position.z; + + airplane->rotY = RAD2DEG(SHORTANGLE2RAD(player->actor_class.shape_info.rotation.y)); + airplane->rotY_goal = airplane->rotY + (RAD2DEG(SHORTANGLE2RAD(goal_angle_random_tbl[goal]))); + airplane->rotX = airplane->speed * RAD2DEG(-0.14959965f) + 30.0f; + airplane->rotZ = 0.0f; + airplane->y_speed = 0.0f; + + mPlib_request_main_wait_type3(play); + } +} + +static void aAp_PlayerCatch(ACTOR* actor, GAME* game) { } + +static void aAp_StopFlyMove(ACTOR* actor, GAME* game) { + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + airplane->speed = 0.0f; + + if (actor->player_distance_xz <= 30.0f && chkTrigger(BUTTON_A)) { + airplane->status = aAp_STATUS_PLAYER_CATCH; // player is now "holding" the airplane + } +} + +static void aAp_FallFlyMove(ACTOR* actor, GAME* game) { + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + aAp_RubberMove(&airplane->rotX, 60.0f, 0.04f); + aAp_RubberMove(&airplane->speed, 5.0f, 0.02f); + + actor->world_position.y -= 0.15f; + + if (actor->bg_collision_check.result.on_ground) { + Common_Get(clip.effect_clip)->effect_make_proc(eEC_EFFECT_DUST, actor->world_position, 1, actor->world_rotation.y, game, actor->npc_id, 0, 8); + airplane->status = aAp_STATUS_STOP_FLY_MOVE; + } +} + +static void aAp_FallFlyMove2(ACTOR* actor, GAME* game) { + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + aAp_RubberMove(&airplane->rotX, 70.0f, 0.15f); + aAp_RubberMove(&airplane->speed, 10.0f, 0.02f); + + actor->world_position.y -= 0.15f; + + if (actor->bg_collision_check.result.on_ground) { + Common_Get(clip.effect_clip)->effect_make_proc(eEC_EFFECT_DUST, actor->world_position, 1, actor->world_rotation.y, game, actor->npc_id, 0, 8); + airplane->status = aAp_STATUS_STOP_FLY_MOVE; + } +} + +static void aAp_LeanAirplane(AIRPLANE_ACTOR* airplane) { + if (airplane->status == aAp_STATUS_FREE_FLY_MOVE) { + aAp_RubberMove(&airplane->rotZ, 0.0f, 0.05f); + } + else if (airplane->rotY_min - airplane->rotY > 0.0f) { + aAp_RubberMove(&airplane->rotZ, 50.0f, 0.05f); + } + else { + aAp_RubberMove(&airplane->rotZ, -50.0f, 0.05f); + } +} + +static xyz_t wind1 = { 2.0f, 1.0f, 4.0f }; +static xyz_t wind2 = { -3.0f, -4.0f, -7.0f }; +static xyz_t wind3 = { 4.0f, -3.0f, 1.0f }; +static xyz_t wind4 = { -1.0f, 3.0f, -4.0f }; + +static xyz_t* wind_table[4] = { &wind1, &wind2, &wind3, &wind4 }; + +static int wind_change_frame_table[10] = { + 300, 20, 500, 1000, 100, + 700, 20, 200, 80, 70 +}; + +static void aAp_WindSystem(ACTOR* actor, GAME* game) { + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + xyz_t dir = { 0.0f, 1.0f, 0.0f }; + + airplane->wind_frame++; + if (airplane->wind_frame > airplane->wind_change_frame) { + int table_rng = fqrand() * 4.0f; + int change_frame_rng = fqrand() * 10.0f; + + airplane->wind_frame = 0; + airplane->wind_change_frame = wind_change_frame_table[change_frame_rng]; + airplane->wind = wind_table[table_rng]; + } + + if (airplane->status != aAp_STATUS_STOP_FLY_MOVE && + airplane->status != aAp_STATUS_START_FLY_MOVE && + airplane->status != aAp_STATUS_PLAYER_CATCH && + airplane->wind != NULL + ) { + sMath_RotateX(&dir, DEG2RAD(airplane->rotX)); + sMath_RotateZ(&dir, DEG2RAD(airplane->rotZ)); + sMath_RotateY(&dir, DEG2RAD(airplane->rotY)); + } +} + +static void aAp_CommonHandle(ACTOR* actor, AIRPLANE_ACTOR* airplane, GAME* game) { + aAp_LeanAirplane(airplane); + + actor->speed = airplane->speed * cosf_table(DEG2RAD(airplane->rotX)); + airplane->y_speed = airplane->speed * sinf_table(DEG2RAD(airplane->rotX)); + + actor->world_position.y -= airplane->y_speed; + actor->world_rotation.y = RAD2SHORTANGLE(DEG2RAD(airplane->rotY)); + actor->shape_info.rotation.y = RAD2SHORTANGLE(DEG2RAD(airplane->rotY)); + + Actor_position_moveF(actor); + + airplane->joystick_x = getJoystick_X(); + airplane->joystick_y = getJoystick_Y(); + + switch (airplane->status) { + case aAp_STATUS_START_FLY_MOVE: + case aAp_STATUS_PLAYER_CATCH: + { + actor->shape_info.draw_shadow = FALSE; + break; + } + + case aAp_STATUS_FREE_FLY_MOVE: + case aAp_STATUS_FALL_FLY_MOVE: + case aAp_STATUS_FALL_FLY_MOVE2: + case aAp_STATUS_SOMER_FLY_MOVE: + { + aAp_RubberMove(&airplane->rotY, airplane->rotY_goal, 0.07f); + actor->shape_info.draw_shadow = TRUE; + break; + } + + default: + { + actor->shape_info.draw_shadow = TRUE; + break; + } + } + + aAp_WindSystem(actor, game); +} + +static void aAp_ZbuttonChangeStatus(AIRPLANE_ACTOR* actor, GAME* game) { + GAME_PLAY* play = (GAME_PLAY*)game; + + if (chkButton(BUTTON_Z)) { + if (actor->status == aAp_STATUS_PLAYER_CATCH) { + mPlib_request_main_refuse_type1(play); + actor->status = aAp_STATUS_START_FLY_MOVE; + } + } + else if (actor->status == aAp_STATUS_START_FLY_MOVE) { + mPlib_request_main_wait_type3(play); + actor->status = aAp_STATUS_PLAYER_CATCH; + } +} + +static f32 aAp_GetBgCheckOffsetY(s16 status) { + if (status == aAp_STATUS_FALL_FLY_MOVE || status == aAp_STATUS_STOP_FLY_MOVE) { + return -5.5f; + } + + return 0.0f; +} + +typedef void (*AIRPLANE_ACTOR_MOVE_PROC)(ACTOR*, GAME*); +static void Airplane_Actor_move(ACTOR* actor, GAME* game) { + static AIRPLANE_ACTOR_MOVE_PROC AirplaneMove[aAp_STATUS_NUM] = { + &aAp_FreeFlyMove, + &aAp_FallFlyMove, + &aAp_FallFlyMove2, + &aAp_SomerFlyMove, + &aAp_StartFlyMove, + &aAp_StopFlyMove, + &aAp_PlayerCatch + }; + + AIRPLANE_ACTOR* airplane = (AIRPLANE_ACTOR*)actor; + f32 offset_y; + + airplane->rotY_min = airplane->rotY; + (*AirplaneMove[airplane->status])(actor, game); + aAp_ZbuttonChangeStatus(airplane, game); + aAp_CommonHandle(actor, airplane, game); + + offset_y = aAp_GetBgCheckOffsetY(airplane->status); + mCoBG_BgCheckControll(NULL, actor, 15.0f, offset_y, FALSE, FALSE, 0); +}