diff --git a/include/ac_snowman.h b/include/ac_snowman.h index 34cfc033..88a8e597 100644 --- a/include/ac_snowman.h +++ b/include/ac_snowman.h @@ -8,25 +8,34 @@ extern "C" { #endif +#define aSMAN_SCALE_MAX (6400.0f) + +typedef void (*aSMAN_PROC)(ACTOR* actorx, GAME* game); + typedef struct snowman_actor_s { - ACTOR actor_class; - ClObjPipe_c _174; - int _190; - ACTOR* head_actor_p; - xyz_t _198; - u8 _1A4[0x1C4 - 0x1A4]; - xyz_t snowball_scale; - f32 normalized_scale; /* normalized ball scale, [0, 1.0f] */ - f32 _1D4; - f32 ball_scale; /* [0, 6400.0f] */ - f32 _1DC; - int scale_result; - int msg_info; - int snowman_part; - s_xyz head_vec; - u8 _1F2[0x1F8 - 0x1F2]; - s16 unk_1F8; - u8 _1FA[0x2]; + /* 0x000 */ ACTOR actor_class; + /* 0x174 */ ClObjPipe_c col_pipe; + /* 0x190 */ aSMAN_PROC process; + /* 0x194 */ ACTOR* col_actor; + /* 0x198 */ xyz_t _198; + /* 0x1A4 */ xyz_t rev_pos; + /* 0x1B0 */ xyz_t fg_pos; + /* 0x1BC */ f32 y_ofs; + /* 0x1C0 */ f32 _1C0; + /* 0x1C4 */ f32 base_speed; + /* 0x1C8 */ f32 accel; + /* 0x1CC */ f32 roll_speed; + /* 0x1D0 */ f32 normalized_scale; /* normalized ball scale, [0, 1.0f] */ + /* 0x1D4 */ f32 body_scale; + /* 0x1D8 */ f32 move_dist; /* [0, 6400.0f] */ + /* 0x1DC */ f32 _1DC; + /* 0x1E0 */ int result; + /* 0x1E4 */ int msg_no; + /* 0x1E8 */ int snowman_part; + /* 0x1EC */ s_xyz head_vec; + /* 0x1F2 */ s_xyz ground_angle; + /* 0x1F8 */ s16 flags; + /* 0x1FA */ s16 timer; } SNOWMAN_ACTOR; extern ACTOR_PROFILE Snowman_Profile; @@ -36,4 +45,3 @@ extern ACTOR_PROFILE Snowman_Profile; #endif #endif - diff --git a/include/libc64/math64.h b/include/libc64/math64.h index 829736cb..c569f0b8 100644 --- a/include/libc64/math64.h +++ b/include/libc64/math64.h @@ -18,7 +18,7 @@ extern "C" { s16 sins(u16); s16 coss(u16); f32 fatan2(f32, f32); -f64 fsqrt(f32); +f32 fsqrt(f32); f32 facos(f32); #ifdef __cplusplus diff --git a/include/m_lib.h b/include/m_lib.h index d69c695b..1ccdfab8 100644 --- a/include/m_lib.h +++ b/include/m_lib.h @@ -20,6 +20,11 @@ extern "C" { #define CLAMP(x, min, max) ((x) < (min) ? (min) : (((x) > (max) ? (max) : (x)))) #define CLAMP2(x, min, max) ((min) < (x) ? (((x) < (max) ? (x) : (max))) : (min)) +// clamp macro specifically for Animal Crossing +#ifndef M_CLAMP +#define M_CLAMP(v, l, h) MAX(MIN((v), (l)), (h)) +#endif + /* Float modulo operator */ #define MOD_F(a, m) (a - (int)((a) * (1.0f / (m))) * (m)) @@ -47,6 +52,9 @@ extern "C" { /* degrees -> radians */ #define DEG2RAD(deg) ((F_PI / 180.0f) * (deg)) +// short angle difference +#define DIFF_SHORT_ANGLE(x, y) ((s16)((x) - (y))) + typedef struct rgba_t { // can be put in other place u8 r, g, b, a; } rgba_t; diff --git a/include/m_name_table.h b/include/m_name_table.h index 4a98fb63..6cb179ce 100644 --- a/include/m_name_table.h +++ b/include/m_name_table.h @@ -1504,6 +1504,19 @@ extern int mNT_check_unknown(mActor_name_t item_no); #define FTR_DINO_TRILOBITE_WEST 0x1F4F #define FTR_DINO_END FTR_DINO_TRILOBITE_WEST +#define FTR_MODERN_LAMP 0x1F50 + +#define FTR_SNOWMAN_FRIDGE 0x1F54 +#define FTR_SNOWMAN_TABLE 0x1F58 +#define FTR_SNOWMAN_BED 0x1F5C +#define FTR_SNOWMAN_CHAIR 0x1F60 +#define FTR_SNOWMAN_LAMP 0x1F64 +#define FTR_SNOWMAN_SOFA 0x1F68 +#define FTR_SNOWMAN_TV 0x1F6C +#define FTR_SNOWMAN_DRESSER 0x1F70 +#define FTR_SNOWMAN_WARDROBE 0x1F74 +#define FTR_SNOWMAN_CLOCK 0x1F78 + #define FTR_DINO_DISP_TRICERA 0x1F7C #define FTR_DINO_DISP_TREX 0x1F80 #define FTR_DINO_DISP_BRONTO 0x1F84 diff --git a/include/m_snowman.h b/include/m_snowman.h index 82150c85..5469f043 100644 --- a/include/m_snowman.h +++ b/include/m_snowman.h @@ -26,7 +26,7 @@ typedef struct snowman_save_data_s { /* sizeof(mSN_snowman_info_c) == 0xC */ typedef struct snowman_info_s{ - /* 0x00 */ int scale; + /* 0x00 */ mSN_snowman_data_c data; /* 0x04 */ xyz_t pos; } mSN_snowman_info_c; diff --git a/src/actor/ac_snowman.c b/src/actor/ac_snowman.c new file mode 100644 index 00000000..3f94543e --- /dev/null +++ b/src/actor/ac_snowman.c @@ -0,0 +1,791 @@ +#include "ac_snowman.h" + +#include "m_common_data.h" +#include "m_player_lib.h" +#include "m_rcp.h" +#include "sys_matrix.h" +#include "m_actor_shadow.h" +#include "m_roll_lib.h" +#include "m_handbill.h" +#include "m_malloc.h" + +enum { + aSMAN_PART0, + aSMAN_PART1, + + aSMAN_PART_NUM +}; + +static void aSMAN_actor_ct(ACTOR* actorx, GAME* game); +static void aSMAN_actor_dt(ACTOR* actorx, GAME* game); +static void aSMAN_actor_move(ACTOR* actorx, GAME* game); +static void aSMAN_actor_draw(ACTOR* actorx, GAME* game); + +// clang-format off +ACTOR_PROFILE Snowman_Profile = { + mAc_PROFILE_SNOWMAN, + ACTOR_PART_BG, + ACTOR_STATE_NO_MOVE_WHILE_CULLED, + ETC_SNOWMAN_BALL_A, + ACTOR_OBJ_BANK_PSNOWMAN, + sizeof(SNOWMAN_ACTOR), + aSMAN_actor_ct, + aSMAN_actor_dt, + aSMAN_actor_move, + aSMAN_actor_draw, + NULL, +}; +// clang-format on + +// clang-format off +static ClObjPipeData_c aSMAN_CoInfoData = { + { 0x39, 0x20, ClObj_TYPE_PIPE }, + { 0x01 }, + { 5, 5, 0, { 0, 0, 0 } } +}; +// clang-format on + +// clang-format off +static StatusData_c aSMAN_StatusData = { + 0, + 5, + 5, + 0, + 196, +}; +// clang-format on + +static void aSMAN_process_normal_init(ACTOR* actorx); +static void aSMAN_process_combine_head_jump_init(ACTOR* actorx, GAME* game); +static void aSMAN_process_combine_body_init(ACTOR* actorx); + +static void aSMAN_process_player_push(ACTOR* actorx, GAME* game); +static void aSMAN_process_player_push_scroll(ACTOR* actorx, GAME* game); +static void aSMAN_process_combine_body(ACTOR* actorx, GAME* game); +static void aSMAN_process_combine_head_jump(ACTOR* actorx, GAME* game); +static void aSMAN_process_swim(ACTOR* actorx, GAME* game); +static void aSMAN_process_air(ACTOR* actorx, GAME* game); + +static void aSMAN_actor_ct(ACTOR* actorx, GAME* game) { + static int part_tbl[] = { aSMAN_PART0, aSMAN_PART1 }; + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + + if (actorx->npc_id == ETC_SNOWMAN_BALL_A) { + f32* move_dist = (f32*)mEv_get_common_area(mEv_EVENT_SNOWMAN_SEASON, aSMAN_PART0); + + if (move_dist != NULL) { + actor->move_dist = *move_dist; + } else { + mEv_reserve_common_area(mEv_EVENT_SNOWMAN_SEASON, aSMAN_PART0); + actor->move_dist = 0.0f; + } + } else { + f32* move_dist = (f32*)mEv_get_common_area(mEv_EVENT_SNOWMAN_SEASON, aSMAN_PART1); + + if (move_dist != NULL) { + actor->move_dist = *move_dist; + } else { + mEv_reserve_common_area(mEv_EVENT_SNOWMAN_SEASON, aSMAN_PART1); + actor->move_dist = 0.0f; + } + } + + Shape_Info_init(actorx, 0.0f, mAc_ActorShadowEllipse, 10.0f, 10.0f); + ClObjPipe_ct(game, &actor->col_pipe); + ClObjPipe_set5(game, &actor->col_pipe, actorx, &aSMAN_CoInfoData); + CollisionCheck_Status_set3(&actorx->status_data, &aSMAN_StatusData); + actor->col_actor = NULL; + actor->timer = 0; + actor->snowman_part = part_tbl[actorx->npc_id != ETC_SNOWMAN_BALL_A]; + actorx->max_velocity_y = -30.0f; + actorx->gravity = 0.8f; + actorx->speed = 0.0f; + actor->base_speed = 0.0f; + actor->accel = 0.1f; + actor->roll_speed = 0.0f; + aSMAN_process_normal_init(actorx); + actorx->scale.x = 0.01f; + actorx->scale.y = 0.01f; + actorx->scale.z = 0.01f; +} + +static void aSNOWMAN_Set_PSnowman_info(SNOWMAN_ACTOR* actor) { + mSN_snowman_info_c sman_info; + + xyz_t_move(&sman_info.pos, &actor->fg_pos); + sman_info.data.exists = TRUE; + sman_info.data.head_size = actor->normalized_scale * 255; + sman_info.data.body_size = actor->body_scale * 255; + sman_info.data.score = actor->result; + mCoBG_SetPlussOffset(actor->actor_class.world.position, 0, mCoBG_ATTRIBUTE_NONE); + if ((actor->flags & 0x80) == 0) { + mSN_regist_snowman_society(&sman_info); + } +} + +static void aSMAN_actor_dt(ACTOR* actorx, GAME* game) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + + ClObjPipe_dt(game, &actor->col_pipe); + if (actor->snowman_part == aSMAN_PART0) { + if (actor->flags & 0x20) { + if (actor->flags & 1) { + if (actor->fg_pos.x != 0.0f || actor->fg_pos.z != 0.0f) { + aSNOWMAN_Set_PSnowman_info(actor); + } else { + mCoBG_SetPlussOffset(actorx->world.position, 0, mCoBG_ATTRIBUTE_NONE); + } + } + + mEv_clear_common_place(mEv_EVENT_SNOWMAN_SEASON, 100 + aSMAN_PART0); + mEv_clear_common_area(mEv_EVENT_SNOWMAN_SEASON, aSMAN_PART0); + } else { + if ((actor->flags & 8) || (actor->flags & 4) || !mRlib_Set_Position_Check(actorx)) { + mEv_clear_common_place(mEv_EVENT_SNOWMAN_SEASON, 100 + aSMAN_PART0); + mEv_clear_common_area(mEv_EVENT_SNOWMAN_SEASON, aSMAN_PART0); + } else { + f32* move_dist = (f32*)mEv_get_common_area(mEv_EVENT_SNOWMAN_SEASON, aSMAN_PART0); + + if (move_dist != NULL) { + *move_dist = actor->move_dist; + } + } + } + } else { + if (actor->flags & 0x20) { + if (actor->flags & 1) { + if (actor->fg_pos.x != 0.0f || actor->fg_pos.z != 0.0f) { + aSNOWMAN_Set_PSnowman_info(actor); + } else { + mCoBG_SetPlussOffset(actorx->world.position, 0, mCoBG_ATTRIBUTE_NONE); + } + } + + mEv_clear_common_place(mEv_EVENT_SNOWMAN_SEASON, 100 + aSMAN_PART1); + mEv_clear_common_area(mEv_EVENT_SNOWMAN_SEASON, aSMAN_PART1); + } else { + if ((actor->flags & 8) || (actor->flags & 4) || !mRlib_Set_Position_Check(actorx)) { + mEv_clear_common_place(mEv_EVENT_SNOWMAN_SEASON, 100 + aSMAN_PART1); + mEv_clear_common_area(mEv_EVENT_SNOWMAN_SEASON, aSMAN_PART1); + } else { + f32* move_dist = (f32*)mEv_get_common_area(mEv_EVENT_SNOWMAN_SEASON, aSMAN_PART1); + + if (move_dist != NULL) { + *move_dist = actor->move_dist; + } + } + } + } + + mEv_actor_dying_message(mEv_EVENT_SNOWMAN_SEASON, actorx); +} + +static void aSMAN_GetSnowmanPresentMail(Mail_c* mail) { + PersonalID_c* pid = &Now_Private->player_ID; + int mail_idx = 0x202; + int rnd = RANDOM(12); + mActor_name_t item; + u8 item_name[mIN_ITEM_NAME_LEN]; + int header_back_start; + // clang-format off + static mActor_name_t snow_item_table[] = { + FTR_SNOWMAN_FRIDGE, + FTR_SNOWMAN_TABLE, + FTR_SNOWMAN_BED, + FTR_SNOWMAN_CHAIR, + FTR_SNOWMAN_LAMP, + FTR_SNOWMAN_SOFA, + FTR_SNOWMAN_TV, + FTR_SNOWMAN_DRESSER, + FTR_SNOWMAN_WARDROBE, + FTR_SNOWMAN_CLOCK, + ITM_CARPET25, + ITM_WALL25, + }; + // clang-format on + + // one chance at a re-roll if the player already has the item + if (mSP_CollectCheck(snow_item_table[rnd])) { + rnd = RANDOM(12); + } + + item = snow_item_table[rnd]; + mail_idx += rnd; + mIN_copy_name_str(item_name, item); + mHandbill_Set_free_str(mHandbill_FREE_STR0, item_name, sizeof(item_name)); + mHandbill_Load_HandbillFromRom(mail->content.header, &header_back_start, mail->content.footer, mail->content.body, mail_idx); + mail->content.header_back_start = header_back_start; + mail->content.font = mMl_FONT_RECV; + mail->content.mail_type = mMl_TYPE_SNOWMAN; + mail->present = item; + mail->content.paper_type = (u8)ITM_PAPER12; + mPr_CopyPersonalID(&mail->header.recipient.personalID, pid); + mail->header.recipient.type = mMl_NAME_TYPE_PLAYER; +} + +static void aSMAN_SendPresentMail(void) { + Mail_c* mail = (Mail_c*)zelda_malloc(sizeof(Mail_c)); + + if (!mLd_PlayerManKindCheck() && mail != NULL && mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE) { + mMl_clear_mail(mail); + aSMAN_GetSnowmanPresentMail(mail); + mPO_receipt_proc(mail, mPO_SENDTYPE_MAIL); + } + + zelda_free(mail); +} + +static int aSNOWMAN_player_block_check(ACTOR* actorx, GAME* game) { + ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game); + int pbx; + int pbz; + int bx; + int bz; + + mFI_Wpos2BlockNum(&pbx, &pbz, playerx->world.position); + mFI_Wpos2BlockNum(&bx, &bz, actorx->world.position); + if (pbx == bx && pbz == bz) { + return TRUE; + } + + return FALSE; +} + +static int aSMAN_FG_Position_Get(ACTOR* actorx) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + // clang-format off + static xy_t chk_pos_tbl[] = { + { 0.0f, 0.0f}, + { mFI_UNIT_BASE_SIZE_F, 0.0f}, + {-mFI_UNIT_BASE_SIZE_F, 0.0f}, + { 0.0f, mFI_UNIT_BASE_SIZE_F}, + { 0.0f, -mFI_UNIT_BASE_SIZE_F}, + { mFI_UNIT_BASE_SIZE_F, mFI_UNIT_BASE_SIZE_F}, + {-mFI_UNIT_BASE_SIZE_F, mFI_UNIT_BASE_SIZE_F}, + { mFI_UNIT_BASE_SIZE_F, -mFI_UNIT_BASE_SIZE_F}, + {-mFI_UNIT_BASE_SIZE_F, -mFI_UNIT_BASE_SIZE_F}, + }; + // clang-format on + int i; + + for (i = 0; i < ARRAY_COUNT(chk_pos_tbl); i++) { + mActor_name_t item; + u32 attr; + xyz_t pos; + f32 ground_y; + + pos = actorx->world.position; + pos.x += chk_pos_tbl[i].x; + pos.z += chk_pos_tbl[i].y; + pos.y = mCoBG_GetBgY_AngleS_FromWpos(NULL, pos, 0.0f); + // @optimization - function call inside of ABS macro... + ground_y = ABS(pos.y - mCoBG_GetBgY_AngleS_FromWpos(NULL, actorx->world.position, 0.0f)); + item = *mFI_GetUnitFG(pos); // @BUG - they don't check for NULL here, and mFI_GetUnitFG can return a NULL pointer. + attr = mCoBG_Wpos2Attribute(pos, NULL); + + if (!mCoBG_ExistHeightGap_KeepAndNow(pos) && item != RSV_NO && item != RSV_WALL_NO && attr != mCoBG_ATTRIBUTE_STONE && attr != mCoBG_ATTRIBUTE_WOOD && ground_y < 60.0f) { + actor->fg_pos = pos; + return TRUE; + } + } + + actor->fg_pos = ZeroVec; + return FALSE; +} + +static void aSMAN_House_Tree_Rev_Check(ACTOR* actorx) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + + if (mRlib_HeightGapCheck_And_ReversePos(actorx) != TRUE) { + actor->flags |= 4; + Actor_delete(actorx); + } +} + +static void aSMAN_Make_Effect_Ground(ACTOR* actorx, GAME* game) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + + if (actor->normalized_scale < 0.2f) { + return; + } + + if ((game->frame_counter & 0xF) == 0 && actorx->bg_collision_check.result.unit_attribute == mCoBG_ATTRIBUTE_BUSH && actorx->speed > 1.0f) { + xyz_t pos = actorx->world.position; + s16 arg; + s16 angle; + + angle = DEG2SHORT_ANGLE2(45.0f + RANDOM_F(45.0f)); + if (game->frame_counter & 0x10) { + angle = -angle; + } + + arg = actorx->speed > 4.0f ? 1 : 0; + pos.x += (actor->normalized_scale * 20.0f + 10.0f) * sin_s(angle); + pos.z += (actor->normalized_scale * 20.0f + 10.0f) * cos_s(angle); + pos.y -= (actor->normalized_scale * 20.0f + 10.0f); + eEC_CLIP->effect_make_proc(eEC_EFFECT_BUSH_HAPPA, pos, 1, actorx->world.angle.y, game, actorx->npc_id, 0, arg); + eEC_CLIP->effect_make_proc(eEC_EFFECT_BUSH_YUKI, pos, 1, actorx->world.angle.y, game, actorx->npc_id, 0, 0); + } +} + +static void aSMAN_get_hole_offset(ACTOR* actorx, f32 hole_dist) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + static f32 small_offset = -875.0f; + static f32 middle_offset = -750.0f; + static f32 large_offset = -250.0f; + f32 ofs; + f32 rate; + f32 clamped_rate; + + rate = (hole_dist * 2.0f) / mFI_UNIT_BASE_SIZE_F; + clamped_rate = M_CLAMP(rate, 0.0f, 1.0f); + + if (actor->normalized_scale < 0.2f) { + ofs = small_offset + actor->normalized_scale * 3.0f * (middle_offset - small_offset); + } else { + ofs = middle_offset + (actor->normalized_scale * 1.5f - 0.5f) * (large_offset - middle_offset); + } + + ofs *= clamped_rate; + add_calc(&actor->y_ofs, ofs, 1.0f - sqrtf(0.5f), 100.0f, 2.5f); +} + +static int aSMAN_get_ground_norm(ACTOR* actorx, xyz_t* norm, xyz_t* pos) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + + mCoBG_GetBgNorm_FromWpos(norm, *pos); + if (Math3d_normalizeXyz_t(norm)) { + f32 hole_dist; + s16 norm_rate; + s16 angle; + + if (mRlib_Get_ground_norm_inHole(actorx, norm, &hole_dist, &angle, &norm_rate, 1.0f - actor->normalized_scale * 0.5f)) { + aSMAN_get_hole_offset(actorx, hole_dist); + if ((actor->flags & 8) == 0) { + s16 angle_mod; + f32 cos = cos_s(angle); + f32 sin = sin_s(angle); + f32 norm_f; + + angle_mod = norm_rate + (int)(actor->ground_angle.z * sin * sin + actor->ground_angle.x * cos * cos); + norm_rate = (int)angle_mod; + norm_rate = M_CLAMP((int)angle_mod, DEG2SHORT_ANGLE(90.0f), DEG2SHORT_ANGLE(-90.0f)); + + if (actor->normalized_scale < 0.5f) { + actorx->position_speed.x *= 0.9f; + actorx->position_speed.z *= 0.9f; + } else if (actor->ground_angle.x == 0 && actor->ground_angle.z == 0) { + actorx->position_speed.x *= 0.95f; + actorx->position_speed.z *= 0.95f; + } + + norm_f = -cos_s(norm_rate); + mRlib_spdF_Angle_to_spdXZ(norm, &norm_f, &angle); + norm->y = sin_s(norm_rate); + if (hole_dist < 1.0f) { + if (ABS(actorx->position_speed.x) < 1.0f && ABS(actorx->position_speed.z) < 1.0f) { + actor->flags |= 8; + actorx->state_bitfield &= ~ACTOR_STATE_NO_MOVE_WHILE_CULLED; + actorx->status_data.weight = MASSTYPE_HEAVY; + actorx->speed = 0.0f; + } + } + } + } else { + mRlib_Get_norm_Clif(actorx, norm); + add_calc0(&actor->y_ofs, 1.0f - sqrtf(0.5f), 50.0f); + } + + return TRUE; + } + + return FALSE; +} + +static void aSMAN_player_push_free(GAME* game) { + ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game); + + playerx->speed = 0.0f; + mPlib_request_main_push_snowball_end_type1(game); +} + +static int aSMAN_player_push_HitWallCheck(ACTOR* actorx, GAME* game) { + ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game); + u32 hit_wall = playerx->bg_collision_check.result.hit_wall; + u32 hit_attr_wall = playerx->bg_collision_check.result.hit_attribute_wall; + s16 wall_angle; + s16 dAngle; + + if ((hit_wall & mCoBG_HIT_WALL) || (hit_attr_wall & mCoBG_HIT_WALL)) { + wall_angle = mRlib_Get_HitWallAngleY(actorx); + dAngle = (wall_angle - playerx->world.angle.y) + DEG2SHORT_ANGLE2(180.0f); + if (ABS(dAngle) > DEG2SHORT_ANGLE2(45.0f)) { + return TRUE; + } + } + + return FALSE; +} + +static s16 aSMAN_get_norm_push_angle_distance(ACTOR* actorx, xyz_t* norm, s16 push_angle) { + s16 norm_angle; + + mCoBG_GetBgNorm_FromWpos(norm, actorx->world.position); + norm_angle = atans_table(norm->z, norm->x); + return norm_angle - push_angle; +} + +static void aSMAN_player_push_scroll_request(ACTOR* actorx, GAME* game) { + ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game); + int dir; + xyz_t snowball_ofs; + xyz_t rev; + + rev = mCoBG_UniqueWallCheck(playerx, 18.0f, 0.0f); + dir = mPlib_CheckCondition_forWadeSnowball(&playerx->world.position, playerx->world.angle.y); + if (dir != mFI_MOVEDIR_NONE) { + xyz_t_sub(&actorx->world.position, &playerx->world.position, &snowball_ofs); + mPlib_Set_ScrollDemo_forWade_snowball(dir, &snowball_ofs); + } else if (!F32_IS_ZERO(rev.x) || !F32_IS_ZERO(rev.z)) { + aSMAN_player_push_free(game); + aSMAN_process_normal_init(actorx); + } +} + +static void aSMAN_set_speed_relations_norm_player_push(ACTOR* actorx, GAME* game) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game); + s16 dAngle = ABS(actorx->world.angle.y - playerx->world.angle.y); + xyz_t norm; + + if (aSMAN_get_ground_norm(actorx, &norm, &actorx->world.position) && (!F32_IS_ZERO(norm.x) || !F32_IS_ZERO(norm.z))) { + if (dAngle < DEG2SHORT_ANGLE2(90.0f)) { + f32 accel = 0.3f + actor->normalized_scale * 0.099999994f; + f32 norm_speed; + + accel *= 0.2f; + actorx->position_speed.x += norm.x * accel; + actorx->position_speed.z += norm.z * accel; + mRlib_spdXZ_to_spdF_Angle(&actorx->position_speed, &norm_speed, &actorx->world.angle.y); + actor->base_speed = norm_speed + (actor->normalized_scale * 0.02f + 0.01f) * (actor->base_speed - norm_speed); + } else if (actorx->speed > 0.5f) { + actor->base_speed = 0.0f; + actor->accel = 0.0f; + } + } +} + +static ACTOR* aSMAN_get_oc_actor(SNOWMAN_ACTOR* actor) { + if (ClObj_DID_COLLIDE(actor->col_pipe.collision_obj)) { + ACTOR* oc_actor = actor->col_pipe.collision_obj.collided_actor; + + if (oc_actor != NULL) { + return oc_actor; + } + } + + return NULL; +} + +static void aSMAN_MakeBreakEffect(SNOWMAN_ACTOR* actor, GAME* game) { + xyz_t pos = actor->actor_class.world.position; + s16 arg = (s16)(actor->normalized_scale * 100.0f); + + pos.y -= actor->normalized_scale * 20.0f + 10.0f; + eEC_CLIP->effect_make_proc(eEC_EFFECT_YUKIDARUMA, pos, 1, 0, game, actor->actor_class.npc_id, arg, 0); + sAdo_OngenTrgStart(0x143, &actor->actor_class.world.position); +} + +static int aSMAN_status_check_in_move(SNOWMAN_ACTOR* actor, GAME* game) { + int ret = FALSE; + + if ((actor->flags & 2) != 0 && (actor->flags & 0x20) == 0) { + actor->flags |= 4; + aSMAN_MakeBreakEffect(actor, game); + Actor_delete((ACTOR*)actor); + ret = TRUE; + } + + if (actor->flags & 0x20) { + ret = TRUE; + } + + if (actor->flags & 8) { + ret = TRUE; + } + + if (ret == TRUE && (actor->process == aSMAN_process_player_push || actor->process == aSMAN_process_player_push_scroll)) { + aSMAN_player_push_free(game); + } + + return ret; +} + +static void aSMAN_position_move(ACTOR* actorx) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + + mCoBG_GetBgY_AngleS_FromWpos(&actor->ground_angle, actorx->world.position, 0.0f); + chase_f(&actorx->speed, actor->base_speed, actor->accel); + + if ((actor->flags & 0x20) != 0 || (actor->flags & 8) != 0 || + actor->process == aSMAN_process_combine_body || actor->process == aSMAN_process_player_push_scroll) { + return; + } + + Actor_position_speed_set(actorx); + mRlib_position_move_for_sloop(actorx, &actor->ground_angle); +} + +static int aSMAN_BGcheck(ACTOR* actorx, GAME* game) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + f32 r = actor->normalized_scale * 20.0f + 10.0f; + + if ((actor->flags & 0x20) != 0 || actor->process == aSMAN_process_combine_head_jump || actor->process == aSMAN_process_combine_body) { + return FALSE; + } + + if (actor->process == aSMAN_process_swim) { + mCoBG_BgCheckControll(&actor->rev_pos, actorx, r, -r, mCoBG_WALL_TYPE0, mCoBG_REVERSE_TYPE_NO_REVERSE, mCoBG_CHECK_TYPE_NORMAL); + actorx->world.position.x += actor->rev_pos.x; + actorx->world.position.z += actor->rev_pos.z; + } else { + mCoBG_BgCheckControll(&actor->rev_pos, actorx, r, -r, mCoBG_WALL_TYPE0, mCoBG_REVERSE_TYPE_REVERSE, mCoBG_CHECK_TYPE_NORMAL); + mRlib_Station_step_modify_to_wall(actorx); + + if (actorx->world.position.y - actorx->last_world_position.y > 60.0f) { + actorx->world.position = actorx->last_world_position; + actor->flags |= 0x4; + actor->flags |= 0x2; + return FALSE; + } + } + + if ((actorx->bg_collision_check.result.hit_wall & mCoBG_HIT_WALL) != 0) { + s16 angle = mRlib_Get_HitWallAngleY(actorx); + s16 colAngle = (angle - DEG2SHORT_ANGLE2(90.0f)) - actorx->world.angle.y; + int diff; + f32 speed = actorx->speed * sin_s(colAngle); + + if (ABS(speed) > 5.0f) { + actor->flags |= 0x4; + actor->flags |= 0x2; + return FALSE; + } + + + diff = actorx->world.angle.y - (angle - DEG2SHORT_ANGLE2(-180.0f)); + if (speed > 0.0f) { + f32 speed2 = actorx->speed * cos_s(colAngle); + + actorx->world.angle.y = angle - diff; + speed *= 0.7f; + actorx->speed = sqrtf(SQ(speed) + SQ(speed2)); + + if (actor->process != aSMAN_process_player_push && !actorx->bg_collision_check.result.is_in_water && actorx->speed > 0.5f) { + sAdo_OngenTrgStartSpeed(actorx->speed, 0x103, &actorx->world.position); + } + } else { + if (actor->process == aSMAN_process_air) { + sAdo_OngenTrgStartSpeed(actorx->speed, 0x103, &actorx->world.position); + actorx->world.angle.y = angle; + actorx->speed *= 1.2f; + actorx->position_speed.y = 0.0f; + } + } + + mRlib_spdF_Angle_to_spdXZ(&actorx->position_speed, &actorx->speed, &actorx->world.angle.y); + } + + return TRUE; +} + +static int aSMAN_snowman_hit_check(SNOWMAN_ACTOR* actor, GAME* game, ACTOR* oc_actorx) { + int b_ux; + int b_uz; + + actor->col_actor = oc_actorx; + if (oc_actorx->id == mAc_PROFILE_SNOWMAN) { + SNOWMAN_ACTOR* oc_actor = (SNOWMAN_ACTOR*)oc_actorx; + u32 attr; + xyz_t center_pos; + + if ((F32_IS_ZERO(actor->actor_class.speed) && F32_IS_ZERO(oc_actorx->speed))) { + return FALSE; + } + + if (actor->actor_class.speed >= oc_actorx->speed) { + mFI_Wpos2UtCenterWpos(¢er_pos, oc_actorx->world.position); + } else { + mFI_Wpos2UtCenterWpos(¢er_pos, actor->actor_class.world.position); + } + + attr = mCoBG_Wpos2Attribute(center_pos, NULL); + mFI_Wpos2UtNum_inBlock(&b_ux, &b_uz, center_pos); + if (attr == mCoBG_ATTRIBUTE_STONE || attr == mCoBG_ATTRIBUTE_WOOD || mCoBG_Wpos2CheckSlateCol(¢er_pos, FALSE) || b_ux == 0 || b_ux == (UT_X_NUM - 1) || b_uz == 0 || b_uz == (UT_Z_NUM - 1) || (oc_actor->flags & 0x8) != 0 || oc_actor->ground_angle.x != 0 || oc_actor->ground_angle.z != 0 || !aSNOWMAN_player_block_check((ACTOR*)actor, game)) { + return FALSE; + } + + if (actor->snowman_part != oc_actor->snowman_part && actor->normalized_scale > 0.2f && oc_actor->normalized_scale > 0.2f) { + actor->actor_class.status_data.weight = MASSTYPE_HEAVY; + oc_actorx->status_data.weight = MASSTYPE_HEAVY; + + if (oc_actor->process == aSMAN_process_player_push || actor->process == aSMAN_process_player_push) { + aSMAN_player_push_free(game); + } + + if (actor->actor_class.speed >= oc_actorx->speed) { + oc_actor->col_actor = (ACTOR*)actor; + aSMAN_process_combine_head_jump_init((ACTOR*)actor, game); + aSMAN_process_combine_body_init(oc_actorx); + } else { + oc_actor->col_actor = (ACTOR*)actor; + aSMAN_process_combine_body_init((ACTOR*)actor); + aSMAN_process_combine_head_jump_init(oc_actorx, game); + } + + return TRUE; + } + + return FALSE; + } + + return FALSE; +} + +static void aSMAN_OBJcheck(SNOWMAN_ACTOR* actor, GAME* game) { + if ((actor->flags & 0x20) != 0) { + return; + } + + if (ClObj_DID_COLLIDE(actor->col_pipe.collision_obj)) { + ACTOR* oc_actorx = actor->col_pipe.collision_obj.collided_actor; + + actor->col_pipe.collision_obj.collision_flags0 &= ~ClObj_FLAG_COLLIDED; + if (oc_actorx != NULL) { + xyz_t tmp; + + if (actor->col_actor != oc_actorx && !aSMAN_snowman_hit_check(actor, game, oc_actorx)) { + f32 rate = (1.0f - actor->normalized_scale) * 0.4f; + + tmp = oc_actorx->position_speed; + xyz_t_mult_v(&tmp, rate); + if (oc_actorx->speed > 0.5f) { + sAdo_OngenTrgStartSpeed(oc_actorx->speed, 0x102, &actor->actor_class.world.position); + } + } else { + f32 add = 0.16f - actor->normalized_scale * 0.14f; + s16 angle = oc_actorx->world.angle.y; + + if (actor->normalized_scale > 0.2f) { + add *= 0.2f; + } + + mRlib_spdF_Angle_to_spdXZ(&tmp, &add, &angle); + tmp.y = 0.0f; + } + + if (tmp.x * (actor->actor_class.world.position.x - oc_actorx->world.position.x) > 0.0f) { + actor->actor_class.position_speed.x += tmp.x; + } + + if (tmp.z * (actor->actor_class.world.position.z - oc_actorx->world.position.z) > 0.0f) { + actor->actor_class.position_speed.z += tmp.z; + } + } else { + actor->col_actor = NULL; + } + } else { + actor->col_actor = NULL; + } +} + +static void aSMAN_set_speed_relations_norm(ACTOR* actorx) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + xyz_t norm; + + if (aSMAN_get_ground_norm(actorx, &norm, &actorx->world.position) && (!F32_IS_ZERO(norm.x) || !F32_IS_ZERO(norm.z))) { + f32 vel = actor->normalized_scale * 0.099999994f + 0.3f; + + actorx->position_speed.x += norm.x * vel; + actorx->position_speed.z += norm.z * vel; + actorx->world.angle.y = atans_table(actorx->position_speed.z, actorx->position_speed.x); + actor->base_speed = fsqrt(SQ(actorx->position_speed.z) + SQ(actorx->position_speed.x)); + actor->accel = 0.0f; + } else { + actor->base_speed = 0.0f; + actor->accel = 0.1f - actor->normalized_scale * 0.05f; + } + + actorx->max_velocity_y = -30.0f; + actorx->gravity = 0.8f; + actor->roll_speed = actorx->speed; +} + +static void aSMAN_set_speed_relations_swim(ACTOR* actorx) { + static s16 angl_add_table[] = { DEG2SHORT_ANGLE2(1.40625f), DEG2SHORT_ANGLE2(5.625f) }; + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + xyz_t flow; + s16 flow_angle; + f32 water_height; + + water_height = mCoBG_GetWaterHeight_File(actorx->world.position, __FILE__, 1362); + mCoBG_GetWaterFlow(&flow, actorx->bg_collision_check.result.unit_attribute); + flow_angle = atans_table(flow.z, flow.x); + chase_angle(&actorx->world.angle.y, flow_angle, angl_add_table[ABS(DIFF_SHORT_ANGLE(actorx->world.angle.y, flow_angle)) > DEG2SHORT_ANGLE2(90.0f)]); + if (actorx->world.position.y < water_height) { + actorx->max_velocity_y = 1.0f; + } else { + actorx->max_velocity_y = -1.0f; + } + + actorx->gravity = 0.1f; + actor->base_speed = 1.0f; + actor->accel = 0.1f; + add_calc0(&actor->roll_speed, 1.0f - sqrtf(0.5f), 0.25f); +} + +static void aSMAN_calc_axis(ACTOR* actorx) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + + if (!F32_IS_ZERO(actor->roll_speed) && actor->process != aSMAN_process_player_push_scroll) { + s16 dAngle = (s16)(actor->roll_speed * (DEG2SHORT_ANGLE3(180.0f) / (((actor->normalized_scale * 20.0f + 10.0f) * 2.0f) * 3.14f))); + + if (actor->process == aSMAN_process_swim) { + f32 rate = (-1.0f - actorx->position_speed.y) / -2.0f; + dAngle *= rate; + } + + mRlib_Roll_Matrix_to_s_xyz(actorx, &actor->head_vec, dAngle); + } +} + +static void aSMAN_calc_scale(ACTOR* actorx) { + SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx; + f32 scale; + u32 attr = mCoBG_Wpos2Attribute(actorx->world.position, NULL); + + if (attr <= mCoBG_ATTRIBUTE_GRASS3) { + actor->move_dist += actorx->speed; + } else { + actor->move_dist -= actorx->speed * 0.75f; + } + + if (actor->move_dist > aSMAN_SCALE_MAX) { + actor->move_dist = aSMAN_SCALE_MAX; + } else if (actor->move_dist < 0.0f) { + actor->move_dist = 0.0f; + } + + actor->normalized_scale = actor->move_dist / aSMAN_SCALE_MAX; + scale = actor->normalized_scale * 0.02f + 0.01f; + + if (scale > 0.03f) { + scale = 0.03f; + } + + actorx->scale.x = actorx->scale.y = actorx->scale.z = scale; + actorx->shape_info.shadow_size_x = actorx->shape_info.shadow_size_z = actor->normalized_scale * 20.0f + 10.0f; +} + +static void aSMAN_actor_move(ACTOR* actorx, GAME* game); +static void aSMAN_actor_draw(ACTOR* actorx, GAME* game); diff --git a/src/game/m_player_main_reflect_axe.c_inc b/src/game/m_player_main_reflect_axe.c_inc index d2bdb93e..e438220f 100644 --- a/src/game/m_player_main_reflect_axe.c_inc +++ b/src/game/m_player_main_reflect_axe.c_inc @@ -94,7 +94,7 @@ static void Player_actor_SetOrderNPC_Reflect_axe(ACTOR* actor, mPlayer_main_refl if (reflect_actor != NULL) { switch (reflect_actor->id) { case mAc_PROFILE_SNOWMAN: - ((SNOWMAN_ACTOR*)reflect_actor)->unk_1F8 |= 2; + ((SNOWMAN_ACTOR*)reflect_actor)->flags |= 2; return; case mAc_PROFILE_BALL: diff --git a/src/game/m_player_main_reflect_scoop.c_inc b/src/game/m_player_main_reflect_scoop.c_inc index 1dd418a5..b1d1709f 100644 --- a/src/game/m_player_main_reflect_scoop.c_inc +++ b/src/game/m_player_main_reflect_scoop.c_inc @@ -104,7 +104,7 @@ static void Player_actor_SetEffectHit_Reflect_scoop(ACTOR* actor, GAME* game) { if (reflected) { switch (reflected->id) { case mAc_PROFILE_SNOWMAN: - ((SNOWMAN_ACTOR*)reflected)->unk_1F8 |= 2; + ((SNOWMAN_ACTOR*)reflected)->flags |= 2; break; case mAc_PROFILE_BALL: diff --git a/src/game/m_snowman.c b/src/game/m_snowman.c index d9d58fe3..d2cb5a2f 100644 --- a/src/game/m_snowman.c +++ b/src/game/m_snowman.c @@ -94,7 +94,7 @@ extern void mSN_regist_snowman_society(mSN_snowman_info_c* info){ xyz_t npos; xyz_t ypos; if(snowId != -1){ - mem_copy((u8*)Save_GetPointer(snowmen.snowmen_data[snowId]), (u8*)info, 4); + mem_copy((u8*)Save_GetPointer(snowmen.snowmen_data[snowId]), (u8*)&info->data, sizeof(info->data)); if(ac != 0){ mPB_keep_item(ac); npos = info->pos; @@ -115,4 +115,4 @@ extern void mSN_snowman_init(){ bzero(Save_GetPointer(snowmen), sizeof(mSN_snowman_save_c)); mSN_decide_msg(); -} \ No newline at end of file +} diff --git a/src/static/libc64/math64.c b/src/static/libc64/math64.c index 6f4087f9..ce72be2e 100644 --- a/src/static/libc64/math64.c +++ b/src/static/libc64/math64.c @@ -5,7 +5,7 @@ f32 fatan2(f32 x, f32 y) { return atan2(x, y); } -f64 fsqrt(f32 x) { +f32 fsqrt(f32 x) { return sqrtf(x); }