Link ac_snowman

This commit is contained in:
Cuyler36
2025-06-06 17:43:48 -04:00
parent 13af38f27b
commit 0fafbe687f
8 changed files with 573 additions and 33 deletions
+1 -1
View File
@@ -1064,7 +1064,7 @@ config.libs = [
Object(Matching, "actor/ac_shop_umbrella.c"),
Object(Matching, "actor/ac_shrine.c"),
Object(Matching, "actor/ac_sign.c"),
Object(NonMatching, "actor/ac_snowman.c"),
Object(Matching, "actor/ac_snowman.c"),
Object(Matching, "actor/ac_station.c"),
Object(Matching, "actor/ac_structure.c"),
Object(Matching, "actor/ac_super.c"),
+7
View File
@@ -9,6 +9,13 @@
extern "C" {
#endif
enum {
aGYO_BALLCHECK_TYPE_NORMAL,
aGYO_BALLCHECK_TYPE_SNOWMAN,
aGYO_BALLCHECK_TYPE_NUM
};
/* sizeof(aGYO_Init_c) == 0x18 */
typedef struct gyoei_init_s {
/* 0x00 */ int fish_type;
-2
View File
@@ -29,8 +29,6 @@ enum {
#define MY_CLAMP(v, l, h) MIN(MY_MAX((l), (v)), (h))
#define ARRAY_LEN(a) (sizeof(a) / sizeof(*a))
#define CALC_EASE(x) (1.0f - sqrtf(1.0f - (x)))
#define CALC_EASE2(x) CALC_EASE(CALC_EASE(x))
#define SHORT2DEG_ANGLE2(x) ((x) * (360.0f / 65536.0f))
#define RANDOMF_RANGE(min, max) ((f32)(min) + (f32)RANDOM_F((f32)(max) - (f32)(min)))
#define RANDOM2F_RANGE(min, max) ((f32)(min) + (f32)RANDOM2_F((f32)(max) - (f32)(min)))
+13 -3
View File
@@ -3,6 +3,7 @@
#include "types.h"
#include "m_actor.h"
#include "m_snowman.h"
#ifdef __cplusplus
extern "C" {
@@ -10,6 +11,15 @@ extern "C" {
#define aSMAN_MOVE_DIST_MAX (6400.0f)
#define aSMAN_FLAG_HEAD_JUMP (1 << 0)
#define aSMAN_FLAG_ON_GROUND (1 << 1)
#define aSMAN_FLAG_MOVED (1 << 2)
#define aSMAN_FLAG_IN_HOLE (1 << 3)
#define aSMAN_FLAG_4 (1 << 4) // unused?
#define aSMAN_FLAG_COMBINED (1 << 5)
#define aSMAN_FLAG_NO_SPEAK (1 << 6)
#define aSMAN_FLAG_DELETE (1 << 7)
typedef int (*aSMAN_PROC)(ACTOR* actorx, GAME* game);
typedef struct snowman_actor_s {
@@ -17,18 +27,18 @@ typedef struct snowman_actor_s {
/* 0x174 */ ClObjPipe_c col_pipe;
/* 0x190 */ aSMAN_PROC process;
/* 0x194 */ ACTOR* col_actor;
/* 0x198 */ xyz_t _198;
/* 0x198 */ xyz_t combine_dist;
/* 0x1A4 */ xyz_t rev_pos;
/* 0x1B0 */ xyz_t fg_pos;
/* 0x1BC */ f32 y_ofs;
/* 0x1C0 */ f32 _1C0;
/* 0x1C0 */ f32 fall_height;
/* 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;
/* 0x1DC */ f32 impact_speed;
/* 0x1E0 */ int result;
/* 0x1E4 */ int msg_no;
/* 0x1E8 */ int snowman_part;
+2
View File
@@ -156,6 +156,8 @@ typedef enum audio_sound_effects {
NA_SE_KA_BUZZ = 0xCF,
NA_SE_104 = 0x104,
NA_SE_107 = 0x107,
NA_SE_108 = 0x108,
NA_SE_ROD_STROKE = 0x109,
+4
View File
@@ -25,6 +25,10 @@ extern "C" {
#define M_CLAMP(v, l, h) MAX(MIN((v), (l)), (h))
#endif
// Easing macros
#define CALC_EASE(x) (1.0f - sqrtf(1.0f - (x)))
#define CALC_EASE2(x) CALC_EASE(CALC_EASE(x))
/* Float modulo operator */
#define MOD_F(a, m) (a - (int)((a) * (1.0f / (m))) * (m))
+9
View File
@@ -11,6 +11,15 @@ extern "C" {
#define mSN_SAVE_COUNT 3
enum {
mSN_RESULT_PERFECT,
mSN_RESULT_GOOD,
mSN_RESULT_OK,
mSN_RESULT_BAD,
mSN_RESULT_NUM
};
/* sizeof(mSN_snowman_data_c) == 4 */
typedef struct snowman_data_s {
/* 0x00 */ u8 exists; /* when non-zero, snowman 'exists' */
+537 -27
View File
@@ -61,6 +61,9 @@ static void aSMAN_process_combine_body_init(ACTOR* actorx);
static void aSMAN_process_player_push_init(ACTOR* actorx, GAME* game);
static void aSMAN_process_air_init(ACTOR* actorx);
static void aSMAN_process_hole_init(ACTOR* actorx);
static void aSMAN_process_player_push_scroll_init(ACTOR* actorx);
static void aSMAN_process_swim_init(ACTOR* actorx);
static void aSMAN_process_combine_head_init(ACTOR* actorx);
static int aSMAN_process_normal(ACTOR* actorx, GAME* game);
static int aSMAN_process_player_push(ACTOR* actorx, GAME* game);
@@ -69,6 +72,8 @@ static int aSMAN_process_combine_body(ACTOR* actorx, GAME* game);
static int aSMAN_process_combine_head_jump(ACTOR* actorx, GAME* game);
static int aSMAN_process_swim(ACTOR* actorx, GAME* game);
static int aSMAN_process_air(ACTOR* actorx, GAME* game);
static int aSMAN_process_hole(ACTOR* actorx, GAME* game);
static int aSMAN_process_combine_head(ACTOR* actorx, GAME* game);
static void aSMAN_actor_ct(ACTOR* actorx, GAME* game) {
static int part_tbl[] = { aSMAN_PART0, aSMAN_PART1 };
@@ -122,7 +127,7 @@ static void aSNOWMAN_Set_PSnowman_info(SNOWMAN_ACTOR* actor) {
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) {
if ((actor->flags & aSMAN_FLAG_DELETE) == 0) {
mSN_regist_snowman_society(&sman_info);
}
}
@@ -132,8 +137,8 @@ static void aSMAN_actor_dt(ACTOR* actorx, GAME* game) {
ClObjPipe_dt(game, &actor->col_pipe);
if (actor->snowman_part == aSMAN_PART0) {
if (actor->flags & 0x20) {
if (actor->flags & 1) {
if (actor->flags & aSMAN_FLAG_COMBINED) {
if (actor->flags & aSMAN_FLAG_HEAD_JUMP) {
if (actor->fg_pos.x != 0.0f || actor->fg_pos.z != 0.0f) {
aSNOWMAN_Set_PSnowman_info(actor);
} else {
@@ -144,7 +149,7 @@ static void aSMAN_actor_dt(ACTOR* actorx, GAME* game) {
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)) {
if ((actor->flags & aSMAN_FLAG_IN_HOLE) || (actor->flags & aSMAN_FLAG_MOVED) || !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 {
@@ -156,8 +161,8 @@ static void aSMAN_actor_dt(ACTOR* actorx, GAME* game) {
}
}
} else {
if (actor->flags & 0x20) {
if (actor->flags & 1) {
if (actor->flags & aSMAN_FLAG_COMBINED) {
if (actor->flags & aSMAN_FLAG_HEAD_JUMP) {
if (actor->fg_pos.x != 0.0f || actor->fg_pos.z != 0.0f) {
aSNOWMAN_Set_PSnowman_info(actor);
} else {
@@ -168,7 +173,7 @@ static void aSMAN_actor_dt(ACTOR* actorx, GAME* game) {
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)) {
if ((actor->flags & aSMAN_FLAG_IN_HOLE) || (actor->flags & aSMAN_FLAG_MOVED) || !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 {
@@ -301,7 +306,7 @@ 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->flags |= aSMAN_FLAG_MOVED;
Actor_delete(actorx);
}
}
@@ -365,7 +370,7 @@ static int aSMAN_get_ground_norm(ACTOR* actorx, xyz_t* norm, xyz_t* pos) {
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) {
if ((actor->flags & aSMAN_FLAG_IN_HOLE) == 0) {
s16 angle_mod;
f32 cos = cos_s(angle);
f32 sin = sin_s(angle);
@@ -388,7 +393,7 @@ static int aSMAN_get_ground_norm(ACTOR* actorx, xyz_t* norm, xyz_t* pos) {
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;
actor->flags |= aSMAN_FLAG_IN_HOLE;
actorx->state_bitfield &= ~ACTOR_STATE_NO_MOVE_WHILE_CULLED;
actorx->status_data.weight = MASSTYPE_HEAVY;
actorx->speed = 0.0f;
@@ -503,18 +508,18 @@ static void aSMAN_MakeBreakEffect(SNOWMAN_ACTOR* actor, GAME* game) {
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;
if ((actor->flags & aSMAN_FLAG_ON_GROUND) != 0 && (actor->flags & aSMAN_FLAG_COMBINED) == 0) {
actor->flags |= aSMAN_FLAG_MOVED;
aSMAN_MakeBreakEffect(actor, game);
Actor_delete((ACTOR*)actor);
ret = TRUE;
}
if (actor->flags & 0x20) {
if (actor->flags & aSMAN_FLAG_COMBINED) {
ret = TRUE;
}
if (actor->flags & 8) {
if (actor->flags & aSMAN_FLAG_IN_HOLE) {
ret = TRUE;
}
@@ -531,7 +536,7 @@ static void aSMAN_position_move(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 ||
if ((actor->flags & aSMAN_FLAG_COMBINED) != 0 || (actor->flags & aSMAN_FLAG_IN_HOLE) != 0 ||
actor->process == aSMAN_process_combine_body || actor->process == aSMAN_process_player_push_scroll) {
return;
}
@@ -544,7 +549,7 @@ 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) {
if ((actor->flags & aSMAN_FLAG_COMBINED) != 0 || actor->process == aSMAN_process_combine_head_jump || actor->process == aSMAN_process_combine_body) {
return FALSE;
}
@@ -558,8 +563,8 @@ static int aSMAN_BGcheck(ACTOR* actorx, GAME* game) {
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;
actor->flags |= aSMAN_FLAG_MOVED;
actor->flags |= aSMAN_FLAG_ON_GROUND;
return FALSE;
}
}
@@ -571,8 +576,8 @@ static int aSMAN_BGcheck(ACTOR* actorx, GAME* game) {
f32 speed = actorx->speed * sin_s(colAngle);
if (ABS(speed) > 5.0f) {
actor->flags |= 0x4;
actor->flags |= 0x2;
actor->flags |= aSMAN_FLAG_MOVED;
actor->flags |= aSMAN_FLAG_ON_GROUND;
return FALSE;
}
@@ -625,7 +630,7 @@ static int aSMAN_snowman_hit_check(SNOWMAN_ACTOR* actor, GAME* game, ACTOR* oc_a
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(&center_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)) {
if (attr == mCoBG_ATTRIBUTE_STONE || attr == mCoBG_ATTRIBUTE_WOOD || mCoBG_Wpos2CheckSlateCol(&center_pos, FALSE) || b_ux == 0 || b_ux == (UT_X_NUM - 1) || b_uz == 0 || b_uz == (UT_Z_NUM - 1) || (oc_actor->flags & aSMAN_FLAG_IN_HOLE) != 0 || oc_actor->ground_angle.x != 0 || oc_actor->ground_angle.z != 0 || !aSNOWMAN_player_block_check((ACTOR*)actor, game)) {
return FALSE;
}
@@ -657,7 +662,7 @@ static int aSMAN_snowman_hit_check(SNOWMAN_ACTOR* actor, GAME* game, ACTOR* oc_a
}
static void aSMAN_OBJcheck(SNOWMAN_ACTOR* actor, GAME* game) {
if ((actor->flags & 0x20) != 0) {
if ((actor->flags & aSMAN_FLAG_COMBINED) != 0) {
return;
}
@@ -797,10 +802,10 @@ static void aSMAN_calc_objChkRange(ACTOR* actorx) {
actor->col_pipe.attribute.pipe.height = (actor->normalized_scale * 15.0f + 5.0f) * 2.5f;
actor->col_pipe.attribute.pipe.offset = -(actor->normalized_scale * 15.0f + 5.0f);
if ((actor->flags & 0x8) != 0) {
if ((actor->flags & aSMAN_FLAG_IN_HOLE) != 0) {
actor->col_pipe.attribute.pipe.radius = 20;
actorx->status_data.weight = MASSTYPE_HEAVY;
} else if (actor->process == aSMAN_process_combine_body && (actor->flags & 0x20) == 0) {
} else if (actor->process == aSMAN_process_combine_body && (actor->flags & aSMAN_FLAG_COMBINED) == 0) {
actor->col_pipe.attribute.pipe.radius = 20;
} else {
actor->col_pipe.attribute.pipe.radius = actor->normalized_scale * 15.0f + 5.0f;
@@ -823,7 +828,7 @@ static int aSMAN_Player_push_Request(ACTOR* actorx, GAME* game) {
return TRUE;
}
if ((actor->flags & 0x8) != 0) {
if ((actor->flags & aSMAN_FLAG_IN_HOLE) != 0) {
aSMAN_process_hole_init(actorx);
return FALSE;
}
@@ -893,5 +898,510 @@ static void aSMAN_process_player_push_init(ACTOR* actorx, GAME* game) {
actor->process = aSMAN_process_player_push;
}
static void aSMAN_actor_move(ACTOR* actorx, GAME* game);
static void aSMAN_actor_draw(ACTOR* actorx, GAME* game);
static int aSMAN_process_player_push(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game);
ACTOR* oc_actorx = aSMAN_get_oc_actor(actor);
s16 player_angle = search_position_angleY(&playerx->world.position, &actorx->world.position);
s16 move_angle = gamePT->mcon.move_angle + DEG2SHORT_ANGLE2(90.0f);
s16 diff_angle = DIFF_SHORT_ANGLE(move_angle, player_angle);
f32 move_pR = gamePT->mcon.move_pR;
add_calc0(&actor->y_ofs, 1.0f - sqrtf(0.5f), 50.0f);
add_calc(&actor->accel, 0.1f, 1.0f - sqrtf(0.8f), 0.005f, 0.0005f);
if (oc_actorx != NULL && aSMAN_snowman_hit_check(actor, game, oc_actorx) == TRUE) {
return FALSE;
}
if (mPlib_Check_Label_main_wade_snowball(actorx)) {
aSMAN_process_player_push_scroll_init(actorx);
} else if (!mPlib_Check_Label_main_push_snowball(actorx)) {
aSMAN_process_normal_init(actorx);
return FALSE;
} else {
xyz_t norm;
s16 norm_angle_diff = aSMAN_get_norm_push_angle_distance(actorx, &norm, player_angle);
{
if ((ABS(diff_angle) > DEG2SHORT_ANGLE2(55.0f) || actor->normalized_scale <= 0.2f || aSMAN_player_push_HitWallCheck(actorx, game)) || (((!F32_IS_ZERO(norm.x) || !F32_IS_ZERO(norm.z)) && ABS(norm_angle_diff) < DEG2SHORT_ANGLE2(120.0f)) || move_pR == 0.0f)) {
aSMAN_player_push_free(game);
aSMAN_process_normal_init(actorx);
return FALSE;
} else {
xyz_t aim_pos = playerx->world.position;
f32 dist;
f32 speed;
s16 player_angle_y;
speed = (actor->normalized_scale * 20.0f + 10.0f) + 10.0f;
player_angle_y = playerx->shape_info.rotation.y;
actor->base_speed = move_pR * (3.0f - actor->normalized_scale * 1.5f) * cos_s(diff_angle);
if (mPlib_CheckButtonOnly_forDush()) {
actor->base_speed *= 1.5f;
}
actorx->world.angle.y = move_angle;
mRlib_spdF_Angle_to_spdXZ(&actorx->position_speed, &actorx->speed, &actorx->world.angle.y);
aSMAN_set_speed_relations_norm_player_push(actorx, game);
mRlib_spdF_Angle_to_spdXZ(&aim_pos, &speed, &player_angle);
xyz_t_sub(&actorx->world.position, &aim_pos, &aim_pos);
aim_pos.y = mCoBG_GetBgY_AngleS_FromWpos(NULL, aim_pos, 0.0f);
add_calc_short_angle2(&player_angle_y, player_angle, CALC_EASE2(0.5f), 455, 45);
dist = search_position_distanceXZ(&actorx->world.position, &actorx->last_world_position);
dist *= 0.4f;
mPlib_SetParam_for_push_snowball(&aim_pos, player_angle_y, dist < 1.0f ? dist : 1.0f);
mCoBG_BgCheckControll(NULL, playerx, 18.0f, 0.0f, mCoBG_WALL_TYPE1, mCoBG_REVERSE_TYPE_REVERSE, mCoBG_CHECK_TYPE_PLAYER);
if ((actorx->bg_collision_check.result.hit_wall & mCoBG_HIT_WALL) != 0) {
s16 wall_angle = mRlib_Get_HitWallAngleY(actorx);
s16 wall_angle_diff = DIFF_SHORT_ANGLE(wall_angle - actorx->world.angle.y, DEG2SHORT_ANGLE(-180.0f));
if (wall_angle_diff < DEG2SHORT_ANGLE2(20.0f) && move_pR * cos_s(wall_angle_diff) > 0.7f) {
actor->timer++;
if (actor->timer > 120) {
actor->flags |= aSMAN_FLAG_MOVED;
actor->flags |= aSMAN_FLAG_ON_GROUND;
}
} else {
actor->timer = 0;
}
} else {
actor->timer = 0;
}
aSMAN_Make_Effect_Ground(actorx, game);
if (actorx->speed > 1.0f) {
sAdo_OngenPos((u32)actorx, 52, &actorx->world.position);
}
aSMAN_player_push_scroll_request(actorx, game);
}
}
}
if (!actorx->bg_collision_check.result.on_ground) {
aSMAN_process_air_init(actorx);
aSMAN_player_push_free(game);
}
actor->roll_speed = actorx->speed;
aSMAN_calc_scale(actorx);
return TRUE;
}
static void aSMAN_process_player_push_scroll_init(ACTOR* actorx) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
actor->process = aSMAN_process_player_push_scroll;
}
static int aSMAN_process_player_push_scroll(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
if (!mPlib_Check_Label_main_wade_snowball(actorx)) {
aSMAN_process_player_push_init(actorx, game);
} else {
xyz_t pos;
f32 rev_x = actor->rev_pos.x;
f32 rev_z = actor->rev_pos.z;
f32 rev_xz = sqrtf(SQ(rev_x) + SQ(rev_z));
mPlib_GetSnowballPos_forWadeSnowball(&pos);
actorx->world.position = pos;
if ((actor->flags & aSMAN_FLAG_ON_GROUND) != 0 || rev_xz > (actor->normalized_scale * 20.0f + 10.0f) * 0.5f) {
actor->flags |= aSMAN_FLAG_MOVED;
actor->flags |= aSMAN_FLAG_ON_GROUND;
mPlib_Set_crash_snowball_for_wade(TRUE);
return FALSE;
}
}
return TRUE;
}
static void aSMAN_process_swim_init(ACTOR* actorx) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
actorx->shape_info.draw_shadow = FALSE;
actor->timer = 0;
actor->flags |= aSMAN_FLAG_MOVED;
actor->process = aSMAN_process_swim;
}
static int aSMAN_process_swim(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
aSMAN_set_speed_relations_swim(actorx);
if (actorx->scale.x < 0.001f) {
Actor_delete(actorx);
return FALSE;
}
xyz_t_mult_v(&actorx->scale, 0.999f);
actor->normalized_scale = (actorx->scale.x - 0.01f) / 0.02f;
if (actorx->scale.x >= 0.01f) {
if (CLIP(gyo_clip) != NULL) {
CLIP(gyo_clip)->ballcheck_gyoei_proc(&actorx->world.position, (actorx->scale.x * 30.0f) / 0.03f, aGYO_BALLCHECK_TYPE_SNOWMAN);
}
}
if (!actorx->bg_collision_check.result.is_in_water && actorx->bg_collision_check.result.unit_attribute == mCoBG_ATTRIBUTE_WATERFALL) {
aSMAN_process_air_init(actorx);
}
if (actor->timer < 32) {
if (((game->frame_counter & 3) == 0 && actor->timer < 16) || (game->frame_counter & 7) == 0) {
xyz_t pos = actorx->world.position;
pos.y = mCoBG_GetWaterHeight_File(pos, __FILE__, 1855);
eEC_CLIP->effect_make_proc(eEC_EFFECT_TURI_HAMON, pos, 1, actorx->world.angle.y, game, actorx->npc_id, 5, (s16)(actor->normalized_scale * 100.0f));
}
actor->timer++;
}
return TRUE;
}
static void aSMAN_process_air_init(ACTOR* actorx) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
f32 bg_y = mCoBG_GetBgY_AngleS_FromWpos(NULL, actorx->world.position, 0.0f);
f32 min_speed = (actor->normalized_scale * 20.0f + 10.0f) * 0.1f;
if (actorx->world.position.y - bg_y > 20.0f) {
sAdo_OngenTrgStart(0x43D, &actorx->world.position);
}
actor->fall_height = actorx->world.position.y;
actorx->gravity = 0.8f;
actorx->max_velocity_y = -30.0f;
if (actorx->speed < min_speed) {
actorx->speed = min_speed;
}
actorx->position_speed.y = 3.2f;
actor->process = aSMAN_process_air;
}
static int aSMAN_process_air(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
add_calc0(&actor->y_ofs, CALC_EASE(0.5f), 50.0f);
if (actorx->bg_collision_check.result.hit_wall & mCoBG_HIT_WALL) {
s16 wall_angle = mRlib_Get_HitWallAngleY(actorx);
add_calc_short_angle2(&actorx->world.angle.y, wall_angle, CALC_EASE2(0.2f), DEG2SHORT_ANGLE2(22.5f), 45);
add_calc(&actorx->speed, 3.0f, CALC_EASE(0.5f), 0.25f, 0.05f);
}
if (actorx->bg_collision_check.result.is_in_water) {
xyz_t pos = actorx->world.position;
pos.y = mCoBG_GetWaterHeight_File(pos, __FILE__, 1934);
eEC_CLIP->effect_make_proc(eEC_EFFECT_AMI_MIZU, pos, 1, 0, game, actorx->npc_id, 2, (s16)(actor->normalized_scale * 100.0f));
if (CLIP(gyo_clip) != NULL) {
CLIP(gyo_clip)->ballcheck_gyoei_proc(&actorx->world.position, (actorx->scale.x * 30.0f) / 0.03f, aGYO_BALLCHECK_TYPE_NORMAL);
}
sAdo_OngenTrgStart(NA_SE_27, &actorx->world.position);
aSMAN_process_swim_init(actorx);
} else if (actorx->bg_collision_check.result.on_ground) {
if (actor->fall_height - actorx->world.position.y >= 55.0f || mCoBG_ExistHeightGap_KeepAndNow_Detail(actorx->world.position)) {
actor->flags |= aSMAN_FLAG_MOVED;
actor->flags |= aSMAN_FLAG_ON_GROUND;
} else {
aSMAN_process_normal_init(actorx);
}
}
return TRUE;
}
static void aSMAN_process_hole_init(ACTOR* actorx) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
actorx->gravity = 0.0f;
actorx->speed = 0.0f;
actor->roll_speed = 0.0f;
actor->accel = 0.1f;
actor->base_speed = 0.0f;
actor->process = aSMAN_process_hole;
}
static int aSMAN_process_hole(ACTOR* actorx, GAME* game) {
return TRUE;
}
static int aSMAN_decide_scale_result(f32 head_scale, f32 body_scale) {
f32 ratio = head_scale / body_scale;
f32 adjusted_ratio = ABS(ratio - 0.85f);
int result;
if (adjusted_ratio <= 0.05f) { // [0%, 5%] margin = perfect
result = mSN_RESULT_PERFECT;
} else if (adjusted_ratio <= 0.15f) { // (5%, 15%] margin = good
result = mSN_RESULT_GOOD;
} else if (adjusted_ratio <= 0.25f) { // (15%, 25%] margin = okay
result = mSN_RESULT_OK;
} else { // anything larger = bad
result = mSN_RESULT_BAD;
}
return result;
}
static int aSMAN_get_snowman_indx(void) {
mSN_snowman_data_c* snowman_p = Save_Get(snowmen).snowmen_data;
int i;
for (i = 0; i < mSN_SAVE_COUNT; i++) {
if (!snowman_p->exists) {
return i;
}
snowman_p++;
}
return -1;
}
static void aSMAN_process_combine_head_jump_init(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
ACTOR* oc_actorx = actor->col_actor;
SNOWMAN_ACTOR* oc_actor = (SNOWMAN_ACTOR*)oc_actorx;
xyz_t oc_pos = oc_actorx->world.position;
xyz_t pos;
f32 height = (actor->normalized_scale * 20.0f + 10.0f) + (oc_actor->normalized_scale * 20.0f + 10.0f);
mPlib_request_main_demo_wait_type1(game, FALSE, actorx);
actor->flags |= aSMAN_FLAG_COMBINED;
actor->flags |= aSMAN_FLAG_HEAD_JUMP;
actor->timer = 0;
actorx->parent_actor = oc_actorx;
actor->roll_speed = 0.0f;
mFI_Wpos2UtCenterWpos(&pos, oc_pos);
oc_pos.y += height * 0.6f;
oc_pos.x = pos.x;
oc_pos.z = pos.z;
xyz_t_sub(&actorx->world.position, &oc_pos, &actor->combine_dist);
xyz_t_mult_v(&actor->combine_dist, 1.0f/60.0f);
actorx->shape_info.draw_shadow = FALSE;
actor->result = aSMAN_decide_scale_result(actorx->scale.x, oc_actorx->scale.x);
actor->msg_no = 0x08A1 + ((Common_Get(snowman_msg_id) + aSMAN_get_snowman_indx()) % mSN_SAVE_COUNT) + actor->result * 3;
actor->body_scale = oc_actor->normalized_scale;
if (actor->result == mSN_RESULT_PERFECT) {
aSMAN_SendPresentMail();
}
Save_Set(snowman_year, Common_Get(time.rtc_time.year) % 100);
Save_Set(snowman_month, Common_Get(time.rtc_time.month));
Save_Set(snowman_day, Common_Get(time.rtc_time.day));
Save_Set(snowman_hour, Common_Get(time.rtc_time.hour));
mQst_NextSnowman(actorx->world.position);
sAdo_OngenTrgStart(NA_SE_104, &actorx->world.position);
actor->process = aSMAN_process_combine_head_jump;
}
static int aSMAN_process_combine_head_jump(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
if (actor->timer == 0 && mPlib_check_label_player_demo_wait(game, actorx)) {
mPlib_Set_able_force_speak_label(actorx);
}
if (actor->timer == 60) {
int i;
s16 angle = DEG2SHORT_ANGLE2(-90.0f);
xyz_t pos = actorx->world.position;
pos.y -= (actor->normalized_scale * 20.0f + 10.0f) * 0.7f;
for (i = 0; i < 5; i++) {
eEC_CLIP->effect_make_proc(eEC_EFFECT_DUST, pos, 1, angle, game, actorx->npc_id, 0, 9);
angle += DEG2SHORT_ANGLE2(45.0f);
}
actorx->shape_info.ofs_y = 0.0f;
aSMAN_FG_Position_Get(actorx);
aSMAN_process_combine_head_init(actorx);
return FALSE;
} else {
xyz_t_sub(&actorx->world.position, &actor->combine_dist, &actorx->world.position);
actorx->shape_info.ofs_y = (actor->timer * 8.0f + actor->timer * -(1.0f/7.5f) * actor->timer) * 25.0f;
add_calc_short_angle2(&actor->head_vec.x, 0, CALC_EASE(0.5f), DEG2SHORT_ANGLE2(22.5f), 91);
add_calc_short_angle2(&actor->head_vec.y, 0, CALC_EASE(0.5f), DEG2SHORT_ANGLE2(22.5f), 91);
add_calc_short_angle2(&actor->head_vec.z, 0, CALC_EASE(0.5f), DEG2SHORT_ANGLE2(22.5f), 91);
actor->timer++;
return TRUE;
}
}
static void aSMAN_set_talk_info_combine_head_init(ACTOR* actorx) {
static int base_msg_no[] = { 0x0895, 0x0898, 0x089B, 0x089E };
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
actor->head_vec = ZeroSVec;
mDemo_Set_msg_num(base_msg_no[actor->result] + RANDOM(3));
mDemo_Set_talk_turn(TRUE);
}
static void aSMAN_set_talk_info_normal_init(ACTOR* actorx) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
mDemo_Set_msg_num(actor->msg_no);
mDemo_Set_ListenAble();
mDemo_Set_camera(CAMERA2_PROCESS_NORMAL);
mPlib_Set_able_hand_all_item_in_demo(FALSE);
}
static void aSMAN_process_combine_head_init(ACTOR* actorx) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
ACTOR* parentx = actorx->parent_actor;
SNOWMAN_ACTOR* parent = (SNOWMAN_ACTOR*)parentx;
f32 ofs_y;
f32 ofs;
ofs_y = ((actor->normalized_scale * 20.0f + 10.0f) + (parent->normalized_scale * 20.0f + 10.0f));
ofs_y *= 0.6f;
actor->timer = 0;
ofs = ((actorx->scale.y + actorx->parent_actor->scale.y) * 10.0f);
Actor_world_to_eye(actorx, (ofs * 0.8f) * 100.0f);
actorx->world.position.x = parentx->world.position.x;
actorx->world.position.z = parentx->world.position.z;
actorx->world.position.y = parentx->world.position.y + ofs_y;
actorx->shape_info.rotation.x = DEG2SHORT_ANGLE2(-17.578125f);
mCoBG_SetPlussOffset(actorx->world.position, 3, mCoBG_ATTRIBUTE_NONE);
if ((actorx->state_bitfield & ACTOR_STATE_NO_CULL) == 0) {
actor->flags |= aSMAN_FLAG_NO_SPEAK;
} else {
mDemo_Request(mDemo_TYPE_SPEAK, actorx, aSMAN_set_talk_info_combine_head_init);
}
actor->process = aSMAN_process_combine_head;
}
static int aSMAN_process_combine_head(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
if ((actor->flags & aSMAN_FLAG_NO_SPEAK) == 0) {
if (mDemo_Check(mDemo_TYPE_SPEAK, actorx) == TRUE) {
if (!mDemo_Check_ListenAble()) {
mDemo_Set_ListenAble();
mDemo_Set_camera(CAMERA2_PROCESS_NORMAL);
mPlib_Set_able_hand_all_item_in_demo(FALSE);
}
} else {
actor->flags |= aSMAN_FLAG_NO_SPEAK;
}
} else if (!mRlib_PSnowman_NormalTalk(actorx, (GAME_PLAY*)game, &actor->impact_speed, aSMAN_set_talk_info_normal_init)) {
SNOWMAN_ACTOR* parent = (SNOWMAN_ACTOR*)actorx->parent_actor;
actor->flags |= aSMAN_FLAG_MOVED;
actor->flags |= aSMAN_FLAG_DELETE;
parent->flags |= aSMAN_FLAG_DELETE;
aSMAN_MakeBreakEffect(actor, game);
mQst_BackSnowman(actorx->world.position);
Actor_delete(actorx);
return FALSE;
}
return TRUE;
}
static void aSMAN_process_combine_body_init(ACTOR* actorx) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
xyz_t pos;
f32 speed_x;
f32 speed_z;
mFI_Wpos2UtCenterWpos(&pos, actorx->world.position);
xyz_t_sub(&actorx->world.position, &pos, &actor->combine_dist);
xyz_t_mult_v(&actor->combine_dist, 1.0f/60.0f);
speed_x = actor->combine_dist.x;
speed_z = actor->combine_dist.z;
actor->roll_speed = sqrtf(SQ(speed_x) + SQ(speed_z));
actorx->world.angle.y = atans_table(-speed_z, -speed_x);
actor->timer = 0;
actorx->speed = 0.0f;
actor->accel = 0.0f;
actor->base_speed = 0.0f;
actor->process = aSMAN_process_combine_body;
}
static int aSMAN_process_combine_body(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
if ((actor->flags & aSMAN_FLAG_COMBINED) == 0) {
if (actor->timer >= 60) {
actor->roll_speed = 0.0f;
actor->flags |= aSMAN_FLAG_COMBINED;
} else {
xyz_t_sub(&actorx->world.position, &actor->combine_dist, &actorx->world.position);
actor->timer++;
}
}
if ((actor->flags & aSMAN_FLAG_DELETE) != 0) {
actor->flags |= aSMAN_FLAG_MOVED;
aSMAN_MakeBreakEffect(actor, game);
Actor_delete(actorx);
}
return TRUE;
}
static void aSMAN_actor_move(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
GAME_PLAY* play = (GAME_PLAY*)game;
if ((actorx->state_bitfield & ACTOR_STATE_NO_CULL) == 0) {
if ((actor->flags & aSMAN_FLAG_COMBINED) != 0 && (actor->flags & aSMAN_FLAG_HEAD_JUMP) != 0 && !aSNOWMAN_player_block_check(actorx, game) && (actor->flags & aSMAN_FLAG_NO_SPEAK) != 0) {
Actor_delete(actorx);
Actor_delete(actorx->parent_actor);
return;
} else if (actorx->speed == 0.0f && actor->process != aSMAN_process_combine_head) {
actor->col_pipe.collision_obj.collision_flags0 &= ~ClObj_FLAG_COLLIDED;
actor->col_pipe.collision_obj.collided_actor = NULL;
return;
}
}
aSMAN_status_check_in_move(actor, game);
aSMAN_position_move(actorx);
aSMAN_BGcheck(actorx, game);
actor->process(actorx, game);
aSMAN_calc_objChkRange(actorx);
aSMAN_calc_axis(actorx);
if ((actor->flags & aSMAN_FLAG_COMBINED) == 0) {
CollisionCheck_Uty_ActorWorldPosSetPipeC(actorx, &actor->col_pipe);
CollisionCheck_setOC(game, &play->collision_check, &actor->col_pipe.collision_obj);
}
}
extern Gfx act_darumaA_model[];
extern Gfx act_darumaB_model[];
static void aSMAN_actor_draw(ACTOR* actorx, GAME* game) {
static Gfx* displayList[] = { act_darumaA_model, act_darumaB_model };
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
GRAPH* graph = game->graph;
int idx;
if ((actor->flags & aSMAN_FLAG_HEAD_JUMP) == 0 || actor->process != aSMAN_process_combine_head) {
Matrix_translate(0.0f, actor->y_ofs, 0.0, 1);
Matrix_rotateXYZ(actor->head_vec.x, actor->head_vec.y, actor->head_vec.z, 1);
idx = 1;
} else {
idx = 0;
mRlib_PSnowmanBreakNeckSwing(&actor->head_vec.y, actor->impact_speed, actor->normalized_scale);
}
_texture_z_light_fog_prim(graph);
OPEN_POLY_OPA_DISP(graph);
gDPPipeSync(POLY_OPA_DISP++);
gSPMatrix(POLY_OPA_DISP++, _Matrix_to_Mtx_new(graph), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, displayList[idx]);
CLOSE_POLY_OPA_DISP(graph);
}