Files
ac-decomp/src/actor/ac_snowman.c
T
Cuyler36 8620018ea9 Add furniture enum, use enum for defines & replace most constants
There are probably more hardcoded values I missed.
2025-07-09 06:02:59 -04:00

1442 lines
52 KiB
C

#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_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);
static int aSMAN_process_player_push_scroll(ACTOR* actorx, GAME* game);
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 };
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 & aSMAN_FLAG_DELETE) == 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 & 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 {
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 & 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 {
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 & 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 {
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 & 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 {
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_START(FTR_KON_SNOWFREEZER),
FTR_START(FTR_KON_SNOWTABLE),
FTR_START(FTR_KON_SNOWBED),
FTR_START(FTR_TAK_SNOWISU),
FTR_START(FTR_TAK_SNOWLAMP),
FTR_START(FTR_KON_SNOWSOFA),
FTR_START(FTR_KON_SNOWTV),
FTR_START(FTR_KON_SNOWTANSU),
FTR_START(FTR_KON_SNOWBOX),
FTR_START(FTR_KON_SNOWCLOCK),
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 |= aSMAN_FLAG_MOVED;
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 & aSMAN_FLAG_IN_HOLE) == 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 |= aSMAN_FLAG_IN_HOLE;
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 & 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 & aSMAN_FLAG_COMBINED) {
ret = TRUE;
}
if (actor->flags & aSMAN_FLAG_IN_HOLE) {
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 & 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;
}
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 & aSMAN_FLAG_COMBINED) != 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 |= aSMAN_FLAG_MOVED;
actor->flags |= aSMAN_FLAG_ON_GROUND;
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 |= aSMAN_FLAG_MOVED;
actor->flags |= aSMAN_FLAG_ON_GROUND;
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(&center_pos, oc_actorx->world.position);
} else {
mFI_Wpos2UtCenterWpos(&center_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(&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;
}
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 & aSMAN_FLAG_COMBINED) != 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_MOVE_DIST_MAX) {
actor->move_dist = aSMAN_MOVE_DIST_MAX;
} else if (actor->move_dist < 0.0f) {
actor->move_dist = 0.0f;
}
actor->normalized_scale = actor->move_dist / aSMAN_MOVE_DIST_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_calc_objChkRange(ACTOR* actorx) {
SNOWMAN_ACTOR* actor = (SNOWMAN_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 & 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 & 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;
actorx->status_data.weight = MIN(actor->normalized_scale * 375.0f, 250.0f);
}
}
static int aSMAN_Player_push_Request(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game);
f32 dist = actorx->player_distance_xz;
f32 y = actorx->world.position.y - playerx->world.position.y - (actor->normalized_scale * 20.0f + 10.0f);
f32 move_pR = gamePT->mcon.move_pR;
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);
if (mPlib_Check_Label_main_push_snowball(actorx)) {
aSMAN_process_player_push_init(actorx, game);
return TRUE;
}
if ((actor->flags & aSMAN_FLAG_IN_HOLE) != 0) {
aSMAN_process_hole_init(actorx);
return FALSE;
}
if (!actorx->bg_collision_check.result.on_ground) {
aSMAN_process_air_init(actorx);
return FALSE;
}
if (actor->normalized_scale > 0.2f && dist < (actor->normalized_scale * 20.0f + 10.0f) + 15.0f) {
int timer = actor->timer;
if (timer >= 16) {
timer = 16;
} else {
actor->timer++;
}
if (timer == 16 && y > -10.0f && y < 25.0f && move_pR > 0.0f && actorx->speed < 3.0f) {
if (ABS(diff_angle) < DEG2SHORT_ANGLE2(55.0f)) {
mPlib_request_main_push_snowball_type1(game, actorx);
}
}
}
return FALSE;
}
static void aSMAN_process_normal_init(ACTOR* actorx) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
actor->timer = 0;
actor->accel = 0.1f;
actor->process = aSMAN_process_normal;
}
static int aSMAN_process_normal(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
if (aSMAN_Player_push_Request(actorx, game)) {
return FALSE;
}
aSMAN_set_speed_relations_norm(actorx);
aSMAN_Make_Effect_Ground(actorx, game);
aSMAN_OBJcheck(actor, game);
aSMAN_House_Tree_Rev_Check(actorx);
mRlib_spdXZ_to_spdF_Angle(&actorx->position_speed, &actorx->speed, &actorx->world.angle.y);
aSMAN_calc_scale(actorx);
return TRUE;
}
static void aSMAN_process_player_push_init(ACTOR* actorx, GAME* game) {
SNOWMAN_ACTOR* actor = (SNOWMAN_ACTOR*)actorx;
ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game);
f32 rate;
f32 speed;
speed = playerx->speed / 7.5f;
rate = 3.0f - actor->normalized_scale * 1.5f;
if (actor->process != aSMAN_process_player_push_scroll) {
actorx->speed = (1.0f - actor->normalized_scale) * (rate * speed) * 1.15f;
actor->accel = 0.0f;
}
actor->timer = 0;
actor->process = aSMAN_process_player_push;
}
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 =
MSG_2209 + ((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[] = { MSG_2197, MSG_2200, MSG_2203, MSG_2206 };
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, MTX_MULT);
Matrix_rotateXYZ(actor->head_vec.x, actor->head_vec.y, actor->head_vec.z, MTX_MULT);
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);
}