diff --git a/common.py b/common.py index e161e9f9..5470c01c 100644 --- a/common.py +++ b/common.py @@ -363,6 +363,7 @@ TRK_CFLAGS = [ BASE_REL_CFLAGS = CFLAGS + [ "-sdata 0", f"-sdata2 {REL_SDATA2_SIZE}", + "-enum int", #"-pool off", "-sym on" ] + REL_DEFINES diff --git a/config/rel_disasm_overrides.yml b/config/rel_disasm_overrides.yml index d7e8a366..400efdc4 100644 --- a/config/rel_disasm_overrides.yml +++ b/config/rel_disasm_overrides.yml @@ -6,3 +6,4 @@ symbol_aligns: 0x8125a7c0 : 0x20 # construct_skip 0x81361820 : 0x20 # S_back_title_timer 0x806D4D40: 32 # align fFTR_myhome_off_pal_table to 32 bytes + 0x8064F860: 32 # data right after m_camera2 TU diff --git a/config/rel_slices.yml b/config/rel_slices.yml index dda16f0d..4105cc9a 100644 --- a/config/rel_slices.yml +++ b/config/rel_slices.yml @@ -40,6 +40,10 @@ m_bg_item.c: .bss: [0x81263080, 0x81263088] m_calendar.c: .text: [0x8037D190, 0x8037DA54] +m_camera2.c: + .text: [0x8037DA54, 0x8038344C] + .rodata: [0x80641438, 0x80641828] + .data: [0x8064F818, 0x8064F860] m_cockroach.c: .text: [0x80385430, 0x80385A80] m_collision_obj.c: diff --git a/config/symbols.yml b/config/symbols.yml index df056c81..fb072735 100644 --- a/config/symbols.yml +++ b/config/symbols.yml @@ -2629,6 +2629,9 @@ global: 0x8009ae38: _savefpr_28 0x8009ae3c: _savefpr_29 0x8009ae4c: __restore_fpr + 0x8009ae78: _restfpr_25 + 0x8009ae80: _restfpr_27 + 0x8009ae84: _restfpr_28 0x8009ae88: _restfpr_29 0x8009ae98: __save_gpr 0x8009ae9c: _savegpr_15 diff --git a/configure.py b/configure.py index 5cb73253..6e80bda9 100644 --- a/configure.py +++ b/configure.py @@ -144,7 +144,7 @@ n.rule( n.rule( "jumptable", - command = "$disassembler $in $out -j $addr -q $disassembler", + command = "$disassembler $in $out -j $addr -q $disasmflags", description = "Jumptable $addr" ) @@ -414,7 +414,7 @@ class FloatInclude(GeneratedInclude): # Build for inc in includes: sda = "--sda " if ctx.sdata2_threshold >= 4 else "" - asm = "" if inc.manual else "--asm" + asm = "" if inc.manual else "" #--asm n.build( inc.path, rule="orderfloats", diff --git a/include/MSL_C/math.h b/include/MSL_C/math.h index 72e16597..93370922 100644 --- a/include/MSL_C/math.h +++ b/include/MSL_C/math.h @@ -13,6 +13,10 @@ extern "C" { #define SQRTF(f) (__frsqrte(f)) +extern double sin(double deg); +extern double cos(double deg); +extern double tan(double deg); + #ifdef __cplusplus } #endif diff --git a/include/m_camera2.h b/include/m_camera2.h index 0dc93e6b..cd45203c 100644 --- a/include/m_camera2.h +++ b/include/m_camera2.h @@ -104,7 +104,7 @@ typedef struct camera_main_normal_s { typedef struct camera_main_simple_s { xyz_t center_pos; - xyz_t eye_pos; + s_xyz angle; f32 distance; int morph_counter; @@ -175,7 +175,6 @@ typedef struct camera_request_cust_talk_s { ACTOR* speaker_actor; ACTOR* listener_actor; f32 center_ratio; - f32 cull_timer; s16 angle_x; s16 angle_y; f32 distance; @@ -283,20 +282,23 @@ typedef union camera_request_data_u { u64 align; } CameraRequestData; -typedef struct camera_s { - struct { - xyz_t eye; - xyz_t center; - xyz_t up; - } lookat; +typedef struct camera_lookat_s { + xyz_t eye; + xyz_t center; + xyz_t up; +} CameraLookat; - struct { - f32 fov_y; - f32 aspect_ratio; - f32 near; - f32 far; - f32 scale; - } perspective; +typedef struct camera_perspective_s { + f32 fov_y; + f32 aspect_ratio; + f32 near; + f32 far; + f32 scale; +} CameraPerspective; + +typedef struct camera_s { + CameraLookat lookat; + CameraPerspective perspective; s_xyz direction; /* camera orientation */ s_xyz direction_velocity; /* camera orentation rate of change */ @@ -321,15 +323,46 @@ typedef struct camera_s { xyz_t mic_pos; /* mic position */ u32 flags; /* camera flags */ - xyz_t unused[2]; // potentially unused, idk if it's even xyz_t + xyz_t offset_eye; + xyz_t offset_center; } Camera2; - -xyz_t* Camera2_getMicPos_p(GAME_PLAY*); -void Camera2_ClearActorTalking_Cull(GAME_PLAY*); -void Camera2_process(GAME_PLAY*); -void Init_Camera2(GAME_PLAY*); -void Camera2_draw(GAME_PLAY*); +extern s16 getCamera2AngleY(GAME_PLAY* play); +extern int Camera2_change_priority(GAME_PLAY* play, int priority); +extern xyz_t* Camera2_getEyePos_p(); +extern xyz_t* Camera2_getCenterPos_p(); +extern int Camera2_CheckCullingMode(); +extern int Camera2_CheckEnterCullingArea(f32 pos_x, f32 pos_z, f32 width); +extern void Camera2_ClearActorTalking_Cull(GAME_PLAY* play); +extern int Camera2_Check_main_index(GAME_PLAY* play, int main_index); +extern xyz_t* Camera2_getMicPos_p(GAME_PLAY* play); +extern int Camera2NormalState_get(GAME_PLAY* play); +extern int Camera2_request_main_wade(GAME_PLAY* play, xyz_t* pos, int priority, f32 goal_time); +extern int Camera2_request_main_talk(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, int priority); +extern int Camera2_request_main_talk_pos(GAME_PLAY* play, ACTOR* speaker, xyz_t* listener_pos, int priority); +extern int Camera2_request_main_normal(GAME_PLAY* play, int flags, int priority); +extern int Camera2_request_main_demo(GAME_PLAY* play, const xyz_t* start_center, const f32 start_dist, const s_xyz* start_dir, const xyz_t* goal_center, const f32 goal_dist, const s_xyz* goal_dir, const f32 goal_delta, const f32 accel_delta, const f32 braking_delta, const int priority); +extern int Camera2_request_main_demo_fromNowPos(GAME_PLAY* play, const xyz_t* goal_center, const s_xyz* goal_dir, const f32 goal_dist, const f32 goal_delta, const f32 accel_delta, const f32 braking_delta, const int priority); +extern int Camera2_request_main_demo_fromNowPos2(GAME_PLAY* play, const xyz_t* goal_center, const f32 goal_delta, const f32 accel_delta, const f32 braking_delta, const int priority); +extern int Camera2_request_main_item(GAME_PLAY* play, int type, int priority); +extern int Camera2_request_main_lock(GAME_PLAY* play, xyz_t* center_pos, xyz_t* eye_pos, f32 fov_y, int morph_counter, f32 near, f32 far, int priority); +extern int Camera2_request_main_door(GAME_PLAY* play, ACTOR* door_actor, u32 flags, int priority); +extern int Camera2_request_main_simple2(GAME_PLAY* play, xyz_t* center, s_xyz* dir, f32 dist, int morph_counter, int mode, int priority); +extern int Camera2_request_main_simple_kirin(GAME_PLAY* play, const xyz_t* center, int priority); +extern int Camera2_request_main_simple_fishing(GAME_PLAY* play, const xyz_t* player_pos, const xyz_t* bobber_pos, int priority); +extern int Camera2_request_main_simple_fishing_return(GAME_PLAY* play, xyz_t* player_pos, int priority); +extern int Camera2_request_main_simple(GAME_PLAY* play, xyz_t* pos, s_xyz* dir, f32 dist, int morph_counter, int priority); +extern void Camera2_main_Simple_AngleDistStd(GAME_PLAY* play, s_xyz* angle, f32* dist); +extern int Camera2_request_main_listen_front_low_talk(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, int priority); +extern int Camera2_request_main_needlework_talk(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, int priority); +extern int Camera2_request_main_cust_talk(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, f32 center_ratio, int priority, s16 angle_x, s16 angle_y, f32 distance); +extern int Camera2_request_BuySikimono_WallPaper(GAME_PLAY* play, xyz_t* center, xyz_t* eye); +extern int Camera2_Inter_set_reverse_mode(GAME_PLAY* play); +extern int Camera2_request_main_inter(GAME_PLAY* play, xyz_t* start_center, xyz_t* start_eye, xyz_t* goal_center, xyz_t* goal_eye, f32 s0, f32 s1, u32 flags, int morph_counter, int priority); +extern int Camera2_request_main_staff_roll(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, int priority); +extern void Init_Camera2(GAME_PLAY* play); +extern void Camera2_process(GAME_PLAY* play); +extern void Camera2_draw(GAME_PLAY* play); #ifdef __cplusplus } diff --git a/include/m_collision_bg.h b/include/m_collision_bg.h index 024c0b0b..50cd14df 100644 --- a/include/m_collision_bg.h +++ b/include/m_collision_bg.h @@ -116,6 +116,9 @@ extern int mCoBG_Height2GetLayer(f32 height); extern void mCoBG_SetPlussOffset(xyz_t wpos, s16 offset, s16 new_attrib); extern int mCoBG_GetLayer(const xyz_t* wpos); extern int mCoBG_BnumUnum2HoleNumber(int block_x, int block_z, int ut_x, int ut_z); +extern u32 mCoBG_UtNum2BgAttr(int ut_x, int ut_z); +extern f32 mCoBG_UtNum2UtCenterY(int ut_x, int ut_z); +extern int mCoBG_CheckCliffAttr(u32 attribute); extern void mCoBG_InitMoveBgData(); extern void mCoBG_InitBlockBgCheckMode(); diff --git a/include/m_event.h b/include/m_event.h index e3cd5d7e..725e22c9 100644 --- a/include/m_event.h +++ b/include/m_event.h @@ -118,6 +118,7 @@ enum event_table { mEv_EVENT_HALLOWEEN = 49, mEv_EVENT_FISHING_TOURNEY_2 = 54, mEv_EVENT_GHOST = 64, + mEv_EVENT_KK_SLIDER = 71, mEv_EVENT_BROKER_SALE = 75, mEv_EVENT_SHOP_SALE = 78, }; @@ -238,6 +239,7 @@ extern int mEv_CheckArbeit(); extern int mEv_CheckTitleDemo(); extern int mEv_check_status(int event, s16 status); extern s8* mEv_get_common_area(int type, s8 id); +extern s8* mEv_get_save_area(int type, s8 id); extern int mEv_ArbeitPlayer(u32 player_no); extern u16 mEv_get_special_event_type(); extern int mEv_ClearEventSaveInfo(mEv_event_save_c* event_save_data); diff --git a/include/m_field_info.h b/include/m_field_info.h index 9b76dd7f..c92715c5 100644 --- a/include/m_field_info.h +++ b/include/m_field_info.h @@ -52,6 +52,10 @@ enum field_room { /* TODO: others */ mFI_FIELD_FG = mFI_TO_FIELD_ID(mFI_FIELDTYPE_FG, 0), + mFI_FIELD_ROOM0 = mFI_TO_FIELD_ID(mFI_FIELDTYPE_ROOM, 0), + + mFI_FIELD_NPCROOM0 = mFI_TO_FIELD_ID(mFI_FIELDTYPE_NPC_ROOM, 0), + mFI_FIELD_PLAYER0_ROOM = mFI_TO_FIELD_ID(mFI_FIELDTYPE_PLAYER_ROOM, 0), mFI_FIELD_PLAYER1_ROOM, mFI_FIELD_PLAYER2_ROOM, @@ -155,6 +159,10 @@ extern void mFI_BlockDepositOFF(u16* deposit, int ut_x, int ut_z); extern void mFI_PullTanukiPathTrees(); extern int mFI_CheckBlockKind_OR(int block_x, int block_z, u32 kind); extern void mFI_GetIslandBlockNumX(int* island_block_x_nums); +extern void mFI_LposInBKtoWpos(xyz_t* wpos, xyz_t lpos, int block_x, int block_z); +extern f32 mFI_GetBlockWidth(); +extern f32 mFI_GetBlockHeight(); +extern int mFI_Wpos2BkandUtNuminBlock(int* block_x, int* block_z, int* ut_x, int* ut_z, xyz_t wpos); extern void mFI_PrintNowBGNum(gfxprint_t* gfxprint); extern void mFI_PrintFgAttr(gfxprint_t* gfxprint); diff --git a/include/m_player_lib.h b/include/m_player_lib.h index 51980270..5b143fd4 100644 --- a/include/m_player_lib.h +++ b/include/m_player_lib.h @@ -25,6 +25,7 @@ extern void mPlib_Set_goal_player_demo_walk(f32 goal_x, f32 goal_z, f32 speed); extern void mPlib_Set_able_hand_all_item_in_demo(s8 enable); extern void mPlib_change_player_cloth_info_lv2(Private_c* priv, mActor_name_t cloth_name); extern int mPlib_Get_address_able_display(); +extern int mPlib_get_player_actor_main_index(GAME_PLAY* play); #ifdef __cplusplus } diff --git a/include/m_room_type.h b/include/m_room_type.h index 67e56356..fb59c9e4 100644 --- a/include/m_room_type.h +++ b/include/m_room_type.h @@ -122,5 +122,6 @@ extern int mRmTp_FurnitureIdx2FurnitureKind(int ftr_idx); extern int mRmTp_PleaseDrawLightSwitch(); extern mActor_name_t mRmTp_Item1ItemNo2FtrItemNo_AtPlayerRoom(mActor_name_t item_no, int flag); extern void mRmTp_SetDefaultLightSwitchData(int state); +extern int mRmTp_GetEntranceBasePosition(xyz_t* pos); #endif \ No newline at end of file diff --git a/include/sys_math3d.h b/include/sys_math3d.h index c3a1bb93..5301c3c5 100644 --- a/include/sys_math3d.h +++ b/include/sys_math3d.h @@ -34,6 +34,9 @@ extern f32 Math3DVecLength(xyz_t* vec); extern void sMath_RotateX(xyz_t* pos, f32 rad); extern void sMath_RotateY(xyz_t* pos, f32 rad); extern void sMath_RotateZ(xyz_t* pos, f32 rad); +extern f32 Math3d_normalizeXyz_t(xyz_t* vec); +extern f32 Math3DLength(xyz_t* v0, xyz_t* v1); +extern void Math3DInDivPos2(xyz_t* v0, xyz_t* v1, xyz_t* v2, f32 percent); extern void Math3DPlane(xyz_t* va, xyz_t* vb, xyz_t* vc, f32* nox, f32* noy, f32* noz, f32* odist); extern int Math3D_sphereCrossSphere_cl(Math3D_sphere_c* a, Math3D_sphere_c* b, f32* in); extern int Math3D_sphereVsPipe_cl(Math3D_sphere_c* s, Math3D_pipe_c* c, f32* in); @@ -41,7 +44,6 @@ extern int Math3D_pipeVsPipe_cl(Math3D_pipe_c* a, Math3D_pipe_c* b , f32* d); extern int Math3D_sphereCrossTriangle3_cp(Math3D_sphere_c* s, Math3D_triangle_c* tri, xyz_t* ip); extern int Math3D_pipeCrossTriangle_cp(Math3D_pipe_c* c,Math3D_triangle_c* tri, xyz_t* in); - extern xyz_t ZeroVec; extern s_xyz ZeroSVec; diff --git a/include/types.h b/include/types.h index f4f68dab..11101428 100644 --- a/include/types.h +++ b/include/types.h @@ -64,6 +64,8 @@ typedef u32 unknown; #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define F32_IS_ZERO(v) (fabsf(v) < 0.008f) + /* ARGB8 color format (32 bits) to RGB5A3 color format (16 bits) */ #define ARGB8_to_RGB5A3(argb8) \ ((u16)(((argb8) & 0xFF000000) >= 0xE0000000 ? \ diff --git a/rel/m_camera2.c b/rel/m_camera2.c new file mode 100644 index 00000000..47e1d3b6 --- /dev/null +++ b/rel/m_camera2.c @@ -0,0 +1,2870 @@ +#include "m_camera2.h" + +#include "m_play.h" +#include "sys_math3d.h" +#include "MSL_C/math.h" +#include "c_keyframe.h" +#include "m_player_lib.h" +#include "m_room_type.h" +#include "m_collision_bg.h" +#include "m_common_data.h" + +#ifdef MUST_MATCH +#include "ppcdis.h" +#endif + +static void Camera2_main_Normal_AdjustDistanceAndDirection(GAME_PLAY* play, f32* dist, s_xyz* dir); +static void Camera2_change_main_index(GAME_PLAY* play); + +static void Camera2_DirectionCalc(GAME_PLAY* play) { + Camera2* camera = &play->camera; + xyz_t eye_minus_center; + s_xyz dir; + f32 mag; + + xyz_t_sub(&camera->lookat.eye, &camera->lookat.center, &eye_minus_center); + dir = camera->direction; + mag = Math3d_normalizeXyz_t(&eye_minus_center); + + if (fabsf(mag) < 0.008f) { + camera->direction.x = 0; + camera->direction.y = 0; + camera->direction.z = 0; + } + else { + camera->direction.x = RAD2SHORTANGLE(facos(eye_minus_center.y)) + (u16)SHT_MIN_S; + camera->direction.y = atans_table(eye_minus_center.z, eye_minus_center.x) + (u16)SHT_MIN_S; + camera->direction.z = 0; + } + + camera->direction_velocity.x = camera->direction.x - dir.x; + camera->direction_velocity.y = camera->direction.y - dir.y; + camera->direction_velocity.z = camera->direction.z - dir.z; +} + +static int Camera2_InDoorCheck() { + mActor_name_t field_id = mFI_GetFieldId(); + int res = FALSE; + + if (mFI_GET_TYPE(field_id) == mFI_FIELD_NPCROOM0 || mFI_GET_TYPE(field_id) == mFI_FIELD_ROOM0 || mFI_GET_TYPE(field_id) == mFI_FIELD_PLAYER0_ROOM) { + res = TRUE; + } + + return res; +} + +static int Camera2_CheckInDoorNearFar(GAME_PLAY* play) { + if (play->camera.indoor_distance_addition_idx == 0 || (Save_Get(scene_no) == SCENE_BROKER_SHOP && play->camera.now_main_index == CAMERA2_PROCESS_TALK)) { + return TRUE; + } + + return FALSE; +} + +static f32 Camera2_GetUnderBorderAdjust() { + if (Camera2_InDoorCheck()) { + return 0.0f; + } + else { + return -15.0f; + } +} + +static void Camera2_PolaPosCalc(xyz_t* eye, s16 inv_dir_x, s16 inv_dir_y, xyz_t* center, f32 dist) { + //f32 dir_x = inv_dir_x; + //f32 dir_y = inv_dir_y; + + f32 dist_x; + f32 inv_dir_x_f; + f32 inv_dir_y_f; + f32 x; + f32 y; + f32 z; + + *eye = *center; + inv_dir_x_f = inv_dir_x * SHORTANGLE2RAD(1); + inv_dir_y_f = inv_dir_y * SHORTANGLE2RAD(1); + y = sin(inv_dir_x_f); + eye->y += dist * y; + x = cos(inv_dir_x_f); + dist_x = dist * x; + x = sin(inv_dir_y_f); + eye->x += dist_x * x; + z = cos(inv_dir_y_f); + eye->z += dist_x * z; +} + +static void Camera2_SetEyePos_fromCenterPosCalc(GAME_PLAY* play, xyz_t* center, xyz_t* eye) { + Camera2_PolaPosCalc(eye, play->camera.direction.x + (u16)SHT_MIN_S, play->camera.direction.y + (u16)SHT_MIN_S, center, play->camera.focus_distance); +} + +static void Camera2_SetEyePos_fromCenterPos(GAME_PLAY* play) { + Camera2* camera = &play->camera; + + Camera2_SetEyePos_fromCenterPosCalc(play, &camera->lookat.center, &camera->lookat.eye); +} + +static void Camera2_MoveDirectionAngleXYZ_morph(GAME_PLAY* play, const s_xyz* goal_dir, int delta) { + s_xyz* dir; + s_xyz* dir_vel; + Camera2* camera = &play->camera; + + dir = &camera->direction; + dir_vel = &camera->direction_velocity; + + if (delta == 0) { + dir->x = goal_dir->x; + dir->y = goal_dir->y; + dir->z = goal_dir->z; + + dir_vel->x = 0; + dir_vel->y = 0; + dir_vel->z = 0; + } + else { + f32 t = 1.0f / (f32)delta; + s_xyz last_dir; + last_dir.x = dir->x; + last_dir.y = dir->y; + last_dir.z = dir->z; + + cKF_SkeletonInfo_subRotInterpolation(t, &dir->x, last_dir.x, goal_dir->x); // order is wrong here + cKF_SkeletonInfo_subRotInterpolation(t, &dir->y, last_dir.y, goal_dir->y); + cKF_SkeletonInfo_subRotInterpolation(t, &dir->z, last_dir.z, goal_dir->z); + + dir_vel->x = dir->x - last_dir.x; + dir_vel->y = dir->y - last_dir.y; + dir_vel->z = dir->z - last_dir.z; + } +} + +static void Camera2_MoveDirectionAngleXYZ(GAME_PLAY* play, const s_xyz* goal_dir, int delta) { + Camera2* camera = &play->camera; + s_xyz* dir = &camera->direction; + s_xyz* dir_vel = &camera->direction_velocity; + + if (delta != 0) { + dir->x = goal_dir->x; + dir->y = goal_dir->y; + dir->z = goal_dir->z; + + dir_vel->x = 0; + dir_vel->y = 0; + dir_vel->z = 0; + } + else { + s_xyz last_dir; + s16 x = dir->x; + s16 y = dir->y; + s16 z = dir->z; + + add_calc_short_angle2(&dir->x, goal_dir->x, 0.051316679f, 11000, 100); + add_calc_short_angle2(&dir->y, goal_dir->y, 0.051316679f, 11000, 75); + add_calc_short_angle2(&dir->z, goal_dir->z, 0.051316679f, 11000, 150); + + dir_vel->x = dir->x - x; + dir_vel->y = dir->y - y; + dir_vel->z = dir->z - z; + } +} + +static void Camera2_SetDirectionAngleXYZ(GAME_PLAY* play, s_xyz* goal_dir, int delta) { + Camera2* camera = &play->camera; + s_xyz* dir = &camera->direction; + s_xyz* dir_vel = &camera->direction_velocity; + + if (delta != 0) { + *dir_vel = ZeroSVec; + } + else { + dir_vel->x = goal_dir->x - dir->x; + dir_vel->y = goal_dir->y - dir->y; + dir_vel->z = goal_dir->z - dir->z; + } + + *dir = *goal_dir; +} + +static void Camera2_SetView(GAME_PLAY* play) { + Camera2* camera = &play->camera; + xyz_t* eye; + View* view; + xyz_t* center; + xyz_t final_center; + xyz_t final_eye; + + eye = &camera->lookat.eye; + view = &play->view; + center = &camera->lookat.center; + + if (fabsf(Math3DLength(eye, center)) < 0.008f) { + eye->z = center->z + 1.0f; + } + + if (Camera2_CheckInDoorNearFar(play)) { + camera->perspective.near = 20.0f; + camera->perspective.far = 1600.0f; + } + else { + if (camera->now_main_index != CAMERA2_PROCESS_LOCK) { + f32 dist_from_player = Math3DLength(eye, &get_player_actor_withoutCheck(play)->actor_class.world.position); + + if (dist_from_player < 540.0f) { + chase_f(&camera->perspective.near, 145.0f, 4.0f); + } + else { + chase_f(&camera->perspective.near, 200.0f, 4.0f); + } + + camera->perspective.far = 1600.0f; + } + } + + if (fabsf(camera->perspective.fov_y) < 0.008f) { + camera->perspective.fov_y += 1.0f; + } + + setScaleView(view, camera->perspective.scale); + setPerspectiveView(view, camera->perspective.fov_y, camera->perspective.near, camera->perspective.far); + xyz_t_add(center, &camera->offset_center, &final_center); + xyz_t_add(eye, &camera->offset_eye, &final_eye); + setLookAtView(view, &final_eye, &final_center, &camera->lookat.up); +} + +static f32 Camera2_BorderSize() { + int type = mFI_GET_TYPE(mFI_GetFieldId()); + f32 size = 110.0f; + + switch (type) { + case mFI_FIELD_NPCROOM0: + size = 110.0f; + break; + case mFI_FIELD_ROOM0: + size = 110.0f; + break; + case mFI_FIELD_PLAYER0_ROOM: + size = 110.0f; + break; + } + + return size; +} + +static void Camera2_GetBorderScale(GAME_PLAY* play, f32 scale, f32* x_min, f32* x_max, f32* z_min, f32* z_max) { + static xyz_t local_border_base_pos = { 0.0f, 0.0f, 0.0f }; + + PLAYER_ACTOR* player = get_player_actor_withoutCheck(play); + xyz_t entrance_pos; + int block_x; + int block_z; + + if (mRmTp_GetEntranceBasePosition(&entrance_pos) == FALSE) { + mFI_Wpos2BlockNum(&block_x, &block_z, player->actor_class.world.position); + mFI_LposInBKtoWpos(&entrance_pos, local_border_base_pos, block_x, block_z); + } + + *x_min = entrance_pos.x + mFI_GetBlockWidth() - (scale * Camera2_BorderSize(play)); + *x_max = entrance_pos.x + (scale * Camera2_BorderSize(play)); + *z_max = mFI_GetBlockHeight() + (entrance_pos.z - (scale * (Camera2_BorderSize(play) + Camera2_GetUnderBorderAdjust()))); + *z_min = entrance_pos.z + (scale * (Camera2_BorderSize(play) + mFI_UT_WORLDSIZE_Z_F)); + + if (Camera2_InDoorCheck()) { + *z_min += -mFI_UT_WORLDSIZE_Z_F; + } +} + +static void Camera2_GetLongBorderScale(GAME_PLAY* play, f32* scale) { + if (play->camera.flags & 1) { + *scale = 1.5f; + } + else { + *scale = 1.0f; + } +} + +static void Camera2_GetBorder(GAME_PLAY* play, f32* x0, f32* x1, f32* z0, f32* z1) { + f32 scale; + + Camera2_GetLongBorderScale(play, &scale); + Camera2_GetBorderScale(play, scale, x0, x1, z0, z1); +} + +static void Camera2_MoveCenterPosAndSpeed_fromEndCenterPos(GAME_PLAY* play, const xyz_t* end_center_pos, int force) { + Camera2* camera = &play->camera; + xyz_t* move_vel = &camera->movement_velocity; + xyz_t* center = &camera->lookat.center; + + if (force) { + xyz_t_move(move_vel, &ZeroVec); + xyz_t_move(center, end_center_pos); + } + else { + xyz_t now_center = *center; + + add_calc(¢er->x, end_center_pos->x, 0.13397461f, 8.75f, 0.25f); + add_calc(¢er->y, end_center_pos->y, 0.13397461f, 8.75f, 0.25f); + add_calc(¢er->z, end_center_pos->z, 0.13397461f, 8.75f, 0.25f); + + xyz_t_sub(center, &now_center, move_vel); // set velocity + } +} + +static void Camera2_SetCenterPosAndSpeed(GAME_PLAY* play, xyz_t* end_center_pos, int force) { + Camera2* camera = &play->camera; + xyz_t* move_vel = &camera->movement_velocity; + xyz_t* center = &camera->lookat.center; + + if (force) { + xyz_t_move(move_vel, &ZeroVec); + } + else { + xyz_t_sub(end_center_pos, center, move_vel); + } + + xyz_t_move(center, end_center_pos); +} + +static void Camera2_SetDistancePosAndSpeed(GAME_PLAY* play, f32 end_distance, int force) { + Camera2* camera = &play->camera; + f32* distance = &camera->focus_distance; + f32* distance_vel = &camera->focus_distance_velocity; + + if (force) { + *distance_vel = 0.0f; + } + else { + *distance_vel = end_distance - *distance; + } + + *distance = end_distance; +} + +static f32 Camera2_MoveDistancePosAndSpeed(GAME_PLAY* play, f32 end_distance, int force) { + Camera2* camera = &play->camera; + f32* distance = &camera->focus_distance; + f32* distance_vel = &camera->focus_distance_velocity; + f32 ret = 0.0f; + + if (force) { + *distance = end_distance; + *distance_vel = 0.0f; + } + else { + f32 pre_dist = *distance; + + ret = add_calc(distance, end_distance, 0.13397461f, 8.75f, 0.25f); + *distance_vel = *distance - pre_dist; + } + + return ret; +} + +static void Camera2_Get_PosBlockRatio(GAME_PLAY* play, xyz_t* pos, f32* x_ratio, f32* z_ratio) { + f32 x0; + f32 x1; + f32 z1; + f32 z0; + + f32 x_center; + f32 z_center; + + f32 x_r; + f32 z_l; + + *z_ratio = 0.0f; + *x_ratio = 0.0f; + + Camera2_GetBorder(play, &x0, &x1, &z0, &z1); + + x_center = (x0 + x1) * 0.5f; + x_r = x_center - x1; + + z_center = (z0 + z1) * 0.5f; + z_l = z_center - z0; + + if (!F32_IS_ZERO(x_r)) { + *x_ratio = (pos->x - x_center) / x_r; + } + else { + *x_ratio = 0.0f; + } + + if (*x_ratio > 1.0f) { + *x_ratio = 1.0f; + } + + if (*x_ratio < -1.0f) { + *x_ratio = -1.0f; + } + + if (!F32_IS_ZERO(z_l)) { + *z_ratio = (pos->z - z_center) / z_l; + } + else { + *z_ratio = 0.0f; + } + + if (*z_ratio > 1.0f) { + *z_ratio = 1.0f; + } + + if (*z_ratio < -1.0f) { + *z_ratio = -1.0f; + } +} + +static void Camera2_Normal_Swing(GAME_PLAY* play, f32* distance, s_xyz* dir) { + xyz_t* pos = &get_player_actor_withoutCheck(play)->actor_class.eye.position; + f32 x_ratio; + f32 z_ratio; + int block_x; + int block_z; + + Camera2_Get_PosBlockRatio(play, pos, &x_ratio, &z_ratio); + mFI_Wpos2BlockNum(&block_x, &block_z, *pos); + + if (block_z == 1 && z_ratio < 0.0f) { + dir->x += (s16)(z_ratio * 250.0f); + } +} + +static const f32 add_distance_array[3] = { + -280.0f, + 0.0f, + 300.0f +}; + +static const s16 add_directionY_array[3] = { + DEG2SHORT_ANGLE(-16.1f), // -2930 + DEG2SHORT_ANGLE(0.0f), // 0 + DEG2SHORT_ANGLE(16.1f), // 2930 +}; + +static const s16 add_directionX_array[3] = { + DEG2SHORT_ANGLE(-23.24f), // -4230 + DEG2SHORT_ANGLE(0.0f), // 0 + DEG2SHORT_ANGLE(19.12f) // 3480 +}; + +static void Camera2_Get_GoalDistanceAndDirection(GAME_PLAY* play, f32* dist, s_xyz* dir) { + static const f32 distance_array[CAMERA2_PROCESS_NUM] = { + 620.0f, /* CAMERA2_PROCESS_STOP */ + 620.0f, /* CAMERA2_PROCESS_NORMAL */ + 620.0f, /* CAMERA2_PROCESS_WADE */ + 290.0f, /* CAMERA2_PROCESS_TALK */ + 620.0f, /* CAMERA2_PROCESS_DEMO */ + 620.0f, /* CAMERA2_PROCESS_ITEM */ + 620.0f, /* CAMERA2_PROCESS_LOCK */ + 620.0f, /* CAMERA2_PROCESS_DOOR */ + 620.0f, /* CAMERA2_PROCESS_SIMPLE */ + 290.0f, /* CAMERA2_PROCESS_CUST_TALK */ + 620.0f, /* CAMERA2_PROCESS_INTER */ + 620.0f, /* CAMERA2_PROCESS_STAFF_ROLL */ + 620.0f /* CAMERA2_PROCESS_INTER2 */ + }; + + static const s_xyz direction_array[CAMERA2_PROCESS_NUM] = { + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-164.114f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-165.2125f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, + }; + + Camera2* camera = &play->camera; + int main_index = camera->now_main_index; + int add_dist_idx = camera->indoor_distance_addition_idx; + int add_dir_idx = camera->indoor_direction_addition_idx; + + if (main_index < 0 || main_index >= CAMERA2_PROCESS_NUM) { + main_index = 0; + } + + *dist = distance_array[main_index]; + *dir = direction_array[main_index]; + + if (main_index == CAMERA2_PROCESS_NORMAL && Camera2_InDoorCheck()) { + if (add_dist_idx < 0 || add_dist_idx >= 3) { + add_dist_idx = 1; + } + + if (add_dir_idx < 0 || add_dir_idx >= 3) { + add_dir_idx = 1; + } + + *dist += add_distance_array[add_dist_idx]; + dir->y += add_directionY_array[add_dir_idx]; + dir->x += add_directionX_array[add_dist_idx]; // adjust by add_dist_idx since rotation on X axis should be defined by distance of camera + } + + if (main_index == CAMERA2_PROCESS_NORMAL || main_index == CAMERA2_PROCESS_WADE) { + if (Camera2_InDoorCheck() == FALSE && (camera->flags & 1) == 0) { + Camera2_Normal_Swing(play, dist, dir); + } + } + + Camera2_main_Normal_AdjustDistanceAndDirection(play, dist, dir); +} + +static void Camera2_SetPos_fromStartEnd(GAME_PLAY* play, const xyz_t* start, const xyz_t* end, f32 now_time, f32 end_time) { + Camera2* camera = &play->camera; + f32 percent = get_percent_forAccelBrake(now_time, 0.0f, end_time, 1.1999999f, 34.8f); + xyz_t center = camera->lookat.center; + xyz_t* center_p = &camera->lookat.center; + xyz_t* move_vel_p = &camera->movement_velocity; + xyz_t goal; + f32 dist; + s_xyz dir; + + if (percent >= 1.0f) { + goal = *end; + } + else { + f32 x = start->x; + f32 y = start->y; + f32 z = start->z; + + x = start->x + percent * (end->x - x); + y = start->y + percent * (end->y - y); + z = start->z + percent * (end->z - z); + + goal.x = x; + goal.y = y; + goal.z = z; + } + + *center_p = goal; + xyz_t_sub(&goal, ¢er, move_vel_p); + Camera2_Get_GoalDistanceAndDirection(play, &dist, &dir); + Camera2_MoveDistancePosAndSpeed(play, dist, FALSE); + Camera2_MoveDirectionAngleXYZ_morph(play, &dir, end_time - now_time); + Camera2_SetEyePos_fromCenterPos(play); + Camera2_SetView(play); +} + +static void Camera2_setup_main_Base(GAME_PLAY* play) { + play->camera.last_main_index = play->camera.now_main_index; + play->camera.now_main_index = play->camera.requested_main_index; + play->camera.requested_main_index_flag = FALSE; +} + +extern s16 getCamera2AngleY(GAME_PLAY* play) { + return play->camera.direction.y; +} + +static int Camera2_check_request_main_priority(GAME_PLAY* play, int priority) { + return priority - play->camera.requested_main_index_priority; +} + +extern int Camera2_change_priority(GAME_PLAY* play, int priority) { + play->camera.requested_main_index_priority = priority; + return TRUE; +} + +static void Camera2_request_main_index(GAME_PLAY* play, int main_index, int priority) { + play->camera.requested_main_index = main_index; + play->camera.requested_main_index_priority = priority; + play->camera.requested_main_index_flag = TRUE; +} + +static void Camera2_ChangeCameraPos_inBlock(GAME_PLAY* play, const xyz_t* pos, xyz_t* end_pos) { + f32 x0; + f32 x1; + f32 z0; + f32 z1; + + f32 x00; + f32 x01; + f32 z00; + f32 z01; + + f32 scale; + + Camera2_GetLongBorderScale(play, &scale); + Camera2_GetBorder(play, &x0, &x1, &z1, &z0); + Camera2_GetBorderScale(play, 1.0f, &x00, &x01, &z01, &z00); + + *end_pos = *pos; + + if (pos->x < x1) { + if (pos->x < x1 - scale * Camera2_BorderSize(play)) { + end_pos->x = x1 - (scale + 1.0f) * Camera2_BorderSize(play); + } + else if (end_pos->x < x01) { + end_pos->x = x01; + } + } + else if (pos->x > x0) { + if (pos->x > x0 + scale * Camera2_BorderSize(play)) { + end_pos->x = x0 + (scale + 1.0f) * Camera2_BorderSize(play); + } + else if (end_pos->x > x00) { + end_pos->x = x00; + } + } + + if (pos->z < z1) { + if (pos->z < z1 - scale * (Camera2_BorderSize(play) + mFI_UT_WORLDSIZE_Z_F)) { + end_pos->z = (z1 - ((scale + 1.0f) * Camera2_BorderSize(play)) - (scale * mFI_UT_WORLDSIZE_Z_F)) - Camera2_GetUnderBorderAdjust(); + } + else if (end_pos->z < z01) { + end_pos->z = z01; + } + } + else if (pos->z > z0) { + if (pos->z > scale * (Camera2_BorderSize(play) + Camera2_GetUnderBorderAdjust() + z0)) { + end_pos->z = (z0 + (scale + 1.0f) * Camera2_BorderSize(play) + mFI_UT_WORLDSIZE_Z_F) + scale * Camera2_GetUnderBorderAdjust(); + } + else if (end_pos->z > z00) { + end_pos->z = z00; + } + } +} + +extern xyz_t* Camera2_getEyePos_p() { + return &((GAME_PLAY*)gamePT)->camera.lookat.eye; +} + +extern xyz_t* Camera2_getCenterPos_p() { + return &((GAME_PLAY*)gamePT)->camera.lookat.center; +} + +static void Camera2_SetCameraParameter(GAME_PLAY* play, xyz_t* center_pos, s_xyz* dir, int force, f32 dist) { + Camera2_SetCenterPosAndSpeed(play, center_pos, force); + Camera2_SetDistancePosAndSpeed(play, dist, force); + Camera2_SetDirectionAngleXYZ(play, dir, force); + Camera2_SetEyePos_fromCenterPos(play); +} + +extern int Camera2_CheckCullingMode() { + GAME_PLAY* play = (GAME_PLAY*)gamePT; + Camera2* camera = &play->camera; + int main_index = camera->now_main_index; + + if (main_index == CAMERA2_PROCESS_TALK) { + if (camera->main_data.talk.cull_timer >= 15.0f) { + return TRUE; + } + } + else if (main_index == CAMERA2_PROCESS_ITEM) { + if (camera->main_data.item.cull_timer >= 6.0f) { + return TRUE; + } + } + else if (main_index == CAMERA2_PROCESS_DOOR) { + if (camera->main_data.door.morph_counter < 70) { + return TRUE; + } + } + else if (main_index == CAMERA2_PROCESS_CUST_TALK) { + if (camera->main_data.cust_talk.cull_timer >= 15.0f) { + return TRUE; + } + } + else if (main_index == CAMERA2_PROCESS_SIMPLE) { + CameraSimple* simple = &camera->main_data.simple; + + if (simple->mode == 1 && simple->cull_timer >= 6.0f) { + return TRUE; + } + } + + return FALSE; +} + +static int pos_cull_check(const xyz_t *pos, const f32 pos_x, const f32 pos_z, f32 width, const f32 size_x, const f32 size_z) { + GAME_PLAY *play = (GAME_PLAY *) gamePT; + f32 pos_left; + f32 x; + f32 eye_left; + f32 eye_right; + f32 pos_right; + + width += size_x; + + if ((play->camera.lookat.center.z + size_z) <= pos_z) { + eye_left = play->camera.lookat.eye.x - width; + eye_right = play->camera.lookat.eye.x + width; + + x = pos->x; + pos_left = x - width; + pos_right = x + width; + + if (pos_left > eye_left) { + pos_left = eye_left; + } + + if (pos_right < eye_right) { + pos_right = eye_right; + } + + if ((pos_left <= pos_x) && (pos_x <= pos_right)) { + return TRUE; + } + } + + return FALSE; +} + +extern int Camera2_CheckEnterCullingArea(f32 pos_x, f32 pos_z, f32 width) { + GAME_PLAY* play = (GAME_PLAY*)gamePT; + Camera2* camera = &play->camera; + int main_index = camera->now_main_index; + f32 center_z = camera->lookat.center.z; + + if (main_index == CAMERA2_PROCESS_SIMPLE) { + return pos_cull_check(&camera->main_data.simple.center_pos, pos_x, pos_z, width, 50.0f, -50.0f); + } + else { + ACTOR* actor0; + ACTOR* actor1; + + if (main_index == CAMERA2_PROCESS_TALK) { + actor0 = camera->main_data.talk.speaker_actor; + actor1 = camera->main_data.talk.listener_actor; + } + else { + actor1 = &get_player_actor_withoutCheck(play)->actor_class; + actor0 = actor1; + } + + if (center_z <= pos_z && (actor0 != NULL || actor1 != NULL)) { + f32 x0; + f32 x1; + f32 fx; + f32 eye_left = camera->lookat.eye.x - width; + f32 eye_right = camera->lookat.eye.x + width; + + f32 t0; + f32 t1; + + if (actor0 == NULL) { + x0 = actor1->world.position.x; + x1 = x0; + } + else if (actor1 == NULL) { + x1 = actor0->world.position.x; + x0 = x1; + } + else { + x0 = actor0->world.position.x; + x1 = actor1->world.position.x; + } + + if (main_index == CAMERA2_PROCESS_TALK) { + CameraTalk* talk = &camera->main_data.talk; + + if (talk->flags & 2) { + x1 = talk->listener_pos.x; + } + } + + if (x0 > x1) { + fx = x1; + } + else { + fx = x0; + x0 = x1; + } + + fx -= width; + x0 += width; + + if (fx > eye_left) { + fx = eye_left; + } + + if (x0 < eye_right) { + x0 = eye_right; + } + + if (fx <= pos_x && pos_x <= x0) { + return TRUE; + } + } + } + + return FALSE; +} + +extern void Camera2_ClearActorTalking_Cull(GAME_PLAY* play) { + Actor_list* actor_list; + ACTOR* actor; + int i; + + actor_list = play->actor_info.list; + for (i = 0; i < ACTOR_PART_NUM; i++) { + for (actor = actor_list->actor; actor != NULL; actor = actor->next_actor) { + actor->cull_while_talking = FALSE; + } + + actor_list++; + } +} + +static void Camera2_AddCullTimer(f32* cull_timer, f32 max) { + *cull_timer += 1.0f; + + if (*cull_timer > max) { + *cull_timer = max; + } +} + +extern int Camera2_Check_main_index(GAME_PLAY* play, int main_index) { + return play->camera.now_main_index == main_index; +} + +extern xyz_t* Camera2_getMicPos_p(GAME_PLAY* play) { + return &play->camera.mic_pos; +} + +static int Camera2_Check_MicPosFix() { + static const s8 data[SCENE_NUM] = { + TRUE, TRUE, TRUE, TRUE, + TRUE, TRUE, FALSE, TRUE, + TRUE, FALSE, TRUE, TRUE, + FALSE, FALSE, FALSE, TRUE, + TRUE, FALSE, FALSE, TRUE, + FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, TRUE, TRUE, + TRUE, FALSE, TRUE, FALSE, + TRUE, TRUE, TRUE, FALSE, + FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, + FALSE, TRUE, FALSE, FALSE + }; + + int scene = Save_Get(scene_no); + if (scene < 0 || scene >= SCENE_NUM) { + return TRUE; + } + + return data[scene]; +} + +#ifndef MUST_MATCH +/* @nonmatching - regswaps */ +static void Camera2_SetMicPos(GAME_PLAY* play) { + PLAYER_ACTOR* player = get_player_actor_withoutCheck(play); + Camera2* camera = &play->camera; + xyz_t* mic_pos; + + if (player != NULL) { + if (Camera2_Check_MicPosFix()) { + f32 z; + f32 x; + + z = cos_s(0) * 77.0f; + x = sin_s(0) * 77.0f; + mic_pos = Camera2_getMicPos_p(play); + + mic_pos->x = player->actor_class.world.position.x + x; + mic_pos->y = player->actor_class.world.position.y + 240.0f; + mic_pos->z = player->actor_class.world.position.z + z; + } + else { + s_xyz* dir = &camera->direction; + f32 x0; + f32 x; + f32 t; + f32 p; + f32 y; + f32 z; + s16 s; + s16 s2; + + x0 = (camera->lookat.eye.y - player->actor_class.world.position.y) - 472.0f; + t = (x0 * 0.796f) + 261.0f; + s = dir->x; + x = t * cos_s(s); + y = t * sin_s(s); + + switch (Save_Get(scene_no)) { + case SCENE_MY_ROOM_S: + p = 100.0f; + break; + + case SCENE_POLICE_BOX: + case SCENE_MY_ROOM_L: + case SCENE_CONVENI: + case SCENE_SUPER: + case SCENE_DEPART: + case SCENE_DEPART_2: + p = 190.0f; + break; + + default: + p = 150.0f; + break; + } + + p += x0 * -0.186f; + t = ABS(x) + p; + s2 = dir->y; + z = t * cos_s(s2); + x = t * sin_s(s2); + mic_pos = Camera2_getMicPos_p(play); + + mic_pos->x = camera->lookat.eye.x + x; + mic_pos->y = camera->lookat.eye.y + y; + mic_pos->z = camera->lookat.eye.z + z; + } + } +} +#else +#pragma force_active on + +extern f32 lbl_80641440; +REL_SYMBOL_AT(lbl_80641440, 0x80641440); + +/* +extern f32 lbl_8064158c; +REL_SYMBOL_AT(lbl_8064158c, 0x8064158c); + +extern f32 lbl_80641590; +REL_SYMBOL_AT(lbl_80641590, 0x80641590); + +extern f32 lbl_80641594; +REL_SYMBOL_AT(lbl_80641594, 0x80641594); + +extern f32 lbl_80641598; +REL_SYMBOL_AT(lbl_80641598, 0x80641598); + +extern f32 lbl_8064159c; +REL_SYMBOL_AT(lbl_8064159c, 0x8064159c); + +extern f32 lbl_806415a0; +REL_SYMBOL_AT(lbl_806415a0, 0x806415a0); + +extern f32 lbl_806415a4; +REL_SYMBOL_AT(lbl_806415a4, 0x806415a4); + +extern f32 lbl_806415a8; +REL_SYMBOL_AT(lbl_806415a8, 0x806415a8); + +extern f32 lbl_806415ac; +REL_SYMBOL_AT(lbl_806415ac, 0x806415ac); +*/ + +#pragma force_active reset + +#include "jumptable/8064f824.inc" +#include "orderfloats/8064158c_806415b0.inc" + +static asm void Camera2_SetMicPos(GAME_PLAY* play) { + #include "asm/8037f2e8.s" +} + +#endif + +extern int Camera2NormalState_get(GAME_PLAY* play) { + Camera2* camera = &play->camera; + const s16 inv_dir_y = camera->direction.y + 0x8000; + const s16 dir_vel_y = camera->direction_velocity.y; + const int scene = Save_Get(scene_no); + int state = 0; + + if (scene == SCENE_MUSEUM_ROOM_INSECT || scene == SCENE_MUSEUM_ROOM_FISH) { + if (camera->now_main_index == CAMERA2_PROCESS_NORMAL) { + if (dir_vel_y == 0) { + if (inv_dir_y == 0) { + state = 2; + } + else if (inv_dir_y > 0) { + state = 1; + } + else { + state = 3; + } + } + else if (dir_vel_y < 0) { + if (inv_dir_y <= 0) { + state = 7; + } + else { + state = 4; + } + } + else if (inv_dir_y < 0) { + state = 6; + } + else { + state = 5; + } + } + } + + return state; +} + +static void Camera2_sound_Set_Ground(GAME_PLAY* play) { } + +static void Camera2_process_other_func2(GAME_PLAY* play) { } + +static void Camera2_setup_main_Stop(GAME_PLAY* play) { } + +static void Camera2_main_Stop(GAME_PLAY* play) { } + +extern int Camera2_request_main_wade(GAME_PLAY* play, xyz_t* pos, int priority, f32 goal_time) { + Camera2* camera = &play->camera; + xyz_t goal_pos; + int res = FALSE; + + if (Camera2_check_request_main_priority(play, priority) > 0) { + Camera2_ChangeCameraPos_inBlock(play, pos, &goal_pos); + camera->request_data.wade.goal_pos = goal_pos; + camera->request_data.wade.goal_time = goal_time; + Camera2_request_main_index(play, CAMERA2_PROCESS_WADE, priority); + + return TRUE; + } + + return FALSE; +} + +static void Camera2_setup_main_Wade(GAME_PLAY* play) { + play->camera.main_data.wade.timer = 0.0f; + play->camera.main_data.wade.start_pos = play->camera.lookat.center; + play->camera.main_data.wade.goal_pos = play->camera.request_data.wade.goal_pos; + play->camera.main_data.wade.goal_time = play->camera.request_data.wade.goal_time; + + Camera2_setup_main_Base(play); + play->camera.requested_main_index_priority = 9; +} + +static void Camera2_SetPos_Wade(GAME_PLAY* play) { + Camera2_SetPos_fromStartEnd( + play, + &play->camera.main_data.wade.start_pos, &play->camera.main_data.wade.goal_pos, + play->camera.main_data.wade.timer, play->camera.main_data.wade.goal_time + ); +} + +static void Camera2_request_proc_index_fromWade(GAME_PLAY* play) { + play->camera.main_data.wade.timer += 1.0f; + + if (play->camera.main_data.wade.timer > play->camera.main_data.wade.goal_time) { + play->camera.requested_main_index_priority = 0; + Camera2_request_main_normal(play, 0, 5); + } +} + +static void Camera2_main_Wade(GAME_PLAY* play) { + Camera2_SetPos_Wade(play); + Camera2_sound_Set_Ground(play); + Camera2_SetMicPos(play); + Camera2_request_proc_index_fromWade(play); + Camera2_change_main_index(play); +} + +extern int Camera2_request_main_talk(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, int priority) { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.talk.speaker_actor = speaker; + play->camera.request_data.talk.listener_actor = listener; + + if (listener != NULL) { + xyz_t_move(&play->camera.request_data.talk.listener_pos, &listener->eye.position); + } + else { + xyz_t_move(&play->camera.request_data.talk.listener_pos, &ZeroVec); + } + + play->camera.request_data.talk.flags = 0; + Camera2_request_main_index(play, CAMERA2_PROCESS_TALK, priority); + return TRUE; + } + + return FALSE; +} + +extern int Camera2_request_main_talk_pos(GAME_PLAY* play, ACTOR* speaker, xyz_t* listener_pos, int priority) { + if ( + (play->camera.now_main_index == CAMERA2_PROCESS_TALK || play->camera.requested_main_index == CAMERA2_PROCESS_TALK) && + (Math3DLength(&play->camera.request_data.talk.listener_pos, listener_pos) < 1.0f) + ) { + return FALSE; + } + else { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.talk.speaker_actor = speaker; + play->camera.request_data.talk.listener_actor = NULL; + play->camera.request_data.talk.listener_pos = *listener_pos; + play->camera.request_data.talk.flags = 1; + Camera2_request_main_index(play, CAMERA2_PROCESS_TALK, priority); + return TRUE; + } + } + + return FALSE; +} + +static void Camera2_setup_main_Talk(GAME_PLAY* play) { + play->camera.main_data.talk.speaker_actor = play->camera.request_data.talk.speaker_actor; + play->camera.main_data.talk.listener_actor = play->camera.request_data.talk.listener_actor; + play->camera.main_data.talk.listener_pos = play->camera.request_data.talk.listener_pos; + play->camera.main_data.talk.cull_timer = 0.0f; + play->camera.main_data.talk.flags = (play->camera.request_data.talk.flags & 1) ? 2 : 0; + Camera2_setup_main_Base(play); + play->camera.requested_main_index_priority = 0; +} + +static void Camera2_Talk_ChangeCameraCenterPos_BetweenSpeakerToListener(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, f32 dist) { + xyz_t* pos0; + xyz_t* pos1; + xyz_t end_center_pos; + f32 y_adjust; + + if (speaker != NULL || listener != NULL) { + if (speaker == NULL) { + pos0 = &listener->eye.position; + pos1 = pos0; + } + else if (listener == NULL) { + pos1 = &speaker->eye.position; + pos0 = pos1; + } + else { + pos1 = &listener->eye.position; + pos0 = &speaker->eye.position; + } + + if (play->camera.main_data.talk.flags & 2) { + pos1 = &play->camera.main_data.talk.listener_pos; + } + + y_adjust = 17.0f; + if (!F32_IS_ZERO(dist)) { + y_adjust -= -60.0f / dist; + } + + end_center_pos.x = (pos0->x + pos1->x) * 0.5f; + end_center_pos.y = (pos0->y + pos1->y) * 0.5f - y_adjust; + end_center_pos.z = (pos0->z + pos1->z) * 0.5f; + + play->camera.main_data.talk.goal_center_pos = end_center_pos; + Camera2_MoveCenterPosAndSpeed_fromEndCenterPos(play, &end_center_pos, FALSE); + } +} + +static int Camera2_TalkCheckCliffLRRange(GAME_PLAY* play, int left_ut_x, int right_ut_x, int ut_z, f32 height) { + int i; + + for (i = left_ut_x; i < right_ut_x + 1; i++) { + u32 attribute = mCoBG_UtNum2BgAttr(i, ut_z); + f32 center_y = mCoBG_UtNum2UtCenterY(i, ut_z); + + if ((mCoBG_CheckCliffAttr(attribute) && center_y > height + 30.0f) || center_y > height + 230.0f) { + return TRUE; + } + } + + return FALSE; +} + +static int Camera2_Talk_CheckCliffLR(GAME_PLAY* play, const xyz_t* pos) { + int ut_x; + int ut_z; + + int l_ut_x; + int r_ut_x; + + mFI_Wpos2UtNum(&ut_x, &ut_z, *pos); // weirdness going on here with the struct copy + l_ut_x = ut_x - 4; + if (l_ut_x < 0) { + l_ut_x = 0; + } + + r_ut_x = ut_x + 4; + + if ( + Camera2_TalkCheckCliffLRRange(play, l_ut_x, r_ut_x, ut_z, pos->y) || + Camera2_TalkCheckCliffLRRange(play, l_ut_x, r_ut_x, ut_z + 3, pos->y) || + Camera2_TalkCheckCliffLRRange(play, l_ut_x, r_ut_x, ut_z + 6, pos->y) + ) { + return TRUE; + } + + return FALSE; +} + +static int Camera2_Talk_NoLowAngleCheck(GAME_PLAY* play, const xyz_t* pos) { + int block_x; + int block_z; + int ut_x; + int ut_z; + + if (Camera2_InDoorCheck()) { + return FALSE; + } + + mFI_Wpos2BkandUtNuminBlock(&block_x, &block_z, &ut_x, &ut_z, *pos); /* ??? why is this here */ + return Camera2_Talk_CheckCliffLR(play, pos) != FALSE; +} + +static void Camera2_Talk_GetAngleY(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, s_xyz* goal_dir, f32 dist_between) { + int angle; + s16 angle_y; + + if (play->camera.main_data.talk.flags & 2) { + angle_y = (s16)search_position_angleY(&speaker->world.position, &play->camera.main_data.talk.listener_pos); + } + else { + angle_y = (s16)search_position_angleY(&speaker->world.position, &listener->world.position); + } + + if ( + (angle_y < DEG2SHORT_ANGLE( 135.0f) && angle_y > DEG2SHORT_ANGLE( 45.0f)) || + (angle_y > DEG2SHORT_ANGLE(-135.0f) && angle_y < DEG2SHORT_ANGLE(-45.0f)) + ) { + angle = 0; + } + else { + f32 add_angle = cos_s(2 * angle_y) * 2730.0f; // cos_s(2 * y) * 15.0f (deg) + f32 sin = sin_s(angle_y); + f32 cos = cos_s(angle_y); + + if (cos * sin >= 0.0f) { + angle = -add_angle; // angle = [-15 deg, 0 deg] + } + else { + angle = add_angle; // angle = [0 deg, 15 deg] + } + } + + goal_dir->y += angle; +} + +static void Camera2_Talk_GetGoalDirectionAngleXYZAndSetDistance_BetweenSpeakerToListener(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, f32 dist_between, s_xyz* dir) { + xyz_t pos = play->camera.main_data.talk.goal_center_pos; + f32 dist; + f32 x_ratio = 0.0f; + f32 z_ratio = 0.0f; + + Camera2_Get_GoalDistanceAndDirection(play, &dist, dir); + dir->x += 1440; // just shy of 8 degrees (~7.915) + Camera2_Get_PosBlockRatio(play, &pos, &x_ratio, &z_ratio); + + if (Camera2_Talk_NoLowAngleCheck(play, &pos)) { + Camera2_main_Simple_AngleDistStd(play, dir, &dist); + dist *= 0.75f; + dir->x -= DEG2SHORT_ANGLE(2.75f); + play->camera.main_data.talk.flags |= 1; + } + else { + Camera2_Talk_GetAngleY(play, speaker, listener, dir, dist_between); + dist += dist_between * 1.46f; + play->camera.main_data.talk.flags &= ~1; + } + + Camera2_MoveDistancePosAndSpeed(play, dist, FALSE); +} + +static void Camera2_SetPos_Talk(GAME_PLAY* play) { + Camera2* camera = &play->camera; + ACTOR* speaker = camera->main_data.talk.speaker_actor; + ACTOR* listener = camera->main_data.talk.listener_actor; + CameraMainData* main_data = &camera->main_data; + f32 dist; + s_xyz dir; + + if (speaker != NULL || listener != NULL) { + if (speaker == NULL || listener == NULL) { + dist = 0.0f; + } + else { + dist = search_position_distance(&speaker->world.position, &listener->world.position); + } + + if (main_data->talk.flags & 2) { + dist = search_position_distance(&speaker->world.position, &main_data->talk.listener_pos); + } + + Camera2_Talk_ChangeCameraCenterPos_BetweenSpeakerToListener(play, speaker, listener, dist); + Camera2_Talk_GetGoalDirectionAngleXYZAndSetDistance_BetweenSpeakerToListener(play, speaker, listener, dist, &dir); + Camera2_MoveDirectionAngleXYZ(play, &dir, 0); + Camera2_SetEyePos_fromCenterPos(play); + Camera2_SetView(play); + } +} + +static void Camera2_Talk_AddCullTimer(GAME_PLAY* play) { + Camera2_AddCullTimer(&play->camera.main_data.talk.cull_timer, 15.0f); +} + +static void Camera2_Talk_SetTalking_Cull(GAME_PLAY* play) { + ACTOR* actor; + f32 width; + f32 x; + f32 z; + + if (Camera2_CheckCullingMode()) { + for (actor = play->actor_info.list[ACTOR_PART_ITEM].actor; actor != NULL; actor = actor->next_actor) { + s16 id = actor->id; + + if ( + (s16)id == mAc_PROFILE_BGITEM || + (s16)id == mAc_PROFILE_FALLS || (s16)id == mAc_PROFILE_FALLSESW || + (s16)id == mAc_PROFILE_TRAIN0 || (s16)id == mAc_PROFILE_TRAIN1 || + (s16)id == mAc_PROFILE_STATION || + (s16)id == mAc_PROFILE_DUMP || + (s16)id == mAc_PROFILE_GOZA || (s16)id == mAc_PROFILE_RADIO || + (s16)id == mAc_PROFILE_MIKUJI || + (s16)id == mAc_PROFILE_TUKIMI || + (s16)id == mAc_PROFILE_LOTUS || + (s16)id == mAc_PROFILE_BOAT || + (s16)id == mAc_PROFILE_BRIDGE_A || + (s16)id == mAc_PROFILE_ROOM_SUNSHINE_MINSECT || + (s16)id == mAc_PROFILE_S_CAR + ) { + continue; + } + + x = actor->world.position.x; + z = actor->world.position.z; + + if ((s16)id == mAc_PROFILE_MUSEUM) { + width = 142.0f; + } + else if ((s16)id == mAc_PROFILE_DEPART || (s16)id == mAc_PROFILE_SUPER) { + width = 130.0f; + } + else if ((s16)id == mAc_PROFILE_CONVENI) { + width = 110.0f; + } + else if ((s16)id == mAc_PROFILE_POST_OFFICE || (s16)id == mAc_PROFILE_SHOP || (s16)id == mAc_PROFILE_NEEDLEWORK_SHOP) { + width = 90.0f; + } + else { + width = 65.0f; + } + + if (Camera2_CheckEnterCullingArea(x, z, width)) { + actor->cull_while_talking = TRUE; + } + } + } +} + +static void Camera2_main_Talk(GAME_PLAY* play) { + Camera2_SetPos_Talk(play); + Camera2_sound_Set_Ground(play); + Camera2_SetMicPos(play); + Camera2_Talk_AddCullTimer(play); + Camera2_Talk_SetTalking_Cull(play); + Camera2_change_main_index(play); +} + +static void Camera2_main_Normal_SetEndCenterPos_fromPlayer(GAME_PLAY* play, xyz_t* end_center_pos) { + PLAYER_ACTOR* player = get_player_actor_withoutCheck(play); + f32 border_x0; + f32 border_x1; + f32 border_z1; + f32 border_z0; + f32 scale; + int block_x; + int block_z; + + Camera2_GetLongBorderScale(play, &scale); + + if (Camera2_InDoorCheck()) { + int add_dir_idx; + int add_dist_idx; + + add_dist_idx = play->camera.indoor_distance_addition_idx; + add_dir_idx = play->camera.indoor_direction_addition_idx; + + scale *= (add_distance_array[add_dist_idx] + 620.0f) / 620.0f; + + if (add_dir_idx == 0 || add_dir_idx == 2) { + scale *= 0.8f; + } + + if (add_dist_idx == 2) { + scale *= 0.88f; + } + } + + Camera2_GetBorderScale(play, scale, &border_x0, &border_x1, &border_z0, &border_z1); + *end_center_pos = player->actor_class.eye.position; + + if (mPlib_get_player_actor_main_index(play) == 114) { + if (mFI_Wpos2BlockNum(&block_x, &block_z, player->actor_class.world.position)) { + if (block_x == 5) { + if (block_z == 7) { + end_center_pos->z += 55.0f; // extra Z distance when travelling in Kapp'n's boat in the acre below the dock? + } + } + } + } + + if (play->camera.flags & 1) { + f32 z_midpoint = (border_z0 + border_z1) * 0.5f; + f32 z_left = z_midpoint - border_z0; + f32 z_scale; + + if (F32_IS_ZERO(z_left)) { + z_scale = 0.0f; + } + else { + z_scale = (end_center_pos->z - z_midpoint) / z_left; + } + + end_center_pos->z = z_midpoint + (z_scale * z_left) * 0.25f; + } + + if (mFI_GetBlockWidth() < scale * 330.0f || border_x0 < border_x1) { + end_center_pos->x = (border_x0 + border_x1) * 0.5f; + } + else if (end_center_pos->x > border_x0) { + end_center_pos->x = border_x0; + } + else if (end_center_pos->x < border_x1) { + end_center_pos->x = border_x1; + } + + if (mFI_GetBlockHeight() < scale * 250.0f || border_z1 < border_z0) { + end_center_pos->z = (border_z0 + border_z1) * 0.5f; + } + else if (end_center_pos->z < border_z0) { + end_center_pos->z = border_z0; + } + else if (end_center_pos->z > border_z1) { + end_center_pos->z = border_z1; + } + + if (Camera2_InDoorCheck()) { + end_center_pos->y = 73.0f; + } +} + +static int Camera2_MoveVparamCenter(GAME_PLAY* play, xyz_t* pos) { + xyz_t new_center; + Camera2* camera = &play->camera; + xyz_t* center_p = &camera->lookat.center; + xyz_t last_center = *center_p; + xyz_t* center_vel_p = &camera->movement_velocity; + + Math3DInDivPos2(center_p, pos, &new_center, 0.2f); // weirdness with the float loading + *center_p = new_center; + + xyz_t_sub(center_p, &last_center, center_vel_p); + return TRUE; +} + +static int Camera2_MoveDistancePosAndSpeedVParam(GAME_PLAY* play, f32 goal_dist) { + f32 dist = play->camera.focus_distance; + + play->camera.focus_distance = dist + (goal_dist - dist) * 0.2f; + play->camera.focus_distance_velocity = play->camera.focus_distance - dist; + return FALSE; +} + +static void Camera2_MoveDirectionAngleXYZVParam(GAME_PLAY* play, const s_xyz* goal_dir) { + s_xyz last_dir; + + Camera2* camera = &play->camera; + s_xyz* dir_vel = &camera->direction_velocity; + s16 x; + s16 y; + s16 z; + s_xyz* dir = &camera->direction; + + x = dir->x; + y = dir->y; + z = dir->z; + + add_calc_short_angle2(&dir->x, goal_dir->x, 0.10557282f, SHT_MAX_S, 2); + add_calc_short_angle2(&dir->y, goal_dir->y, 0.10557282f, SHT_MAX_S, 2); + add_calc_short_angle2(&dir->z, goal_dir->z, 0.10557282f, SHT_MAX_S, 2); + + #ifndef BUGFIXES + dir_vel->z = dir->x - x; // @BUG + #else + dir_vel->x = dir->x - x; + #endif + dir_vel->y = dir->y - y; + dir_vel->z = dir->z - z; +} + +static void Camera2_main_Normal_SetPos_fromPlayer(GAME_PLAY* play, int force) { + Camera2* camera = &play->camera; + CameraMainData* main_data = &camera->main_data; + f32 goal_dist; + s_xyz goal_dir; + + if ( + main_data->normal.last_indoor_distance_addition_idx != camera->indoor_distance_addition_idx || + main_data->normal.last_indoor_direction_addition_idx != camera->indoor_direction_addition_idx + ) { + main_data->normal.morph_counter = 80; + main_data->normal.last_indoor_distance_addition_idx = camera->indoor_distance_addition_idx; + main_data->normal.last_indoor_direction_addition_idx = camera->indoor_direction_addition_idx; + } + + Camera2_Get_GoalDistanceAndDirection(play, &goal_dist, &goal_dir); + + if (main_data->normal.morph_counter <= 0) { + Camera2_MoveDistancePosAndSpeed(play, goal_dist, FALSE); + Camera2_MoveDirectionAngleXYZ(play, &goal_dir, force); + } + else { + Camera2_MoveDistancePosAndSpeedVParam(play, goal_dist); + Camera2_MoveDirectionAngleXYZVParam(play, &goal_dir); + } + + if (main_data->normal.morph_counter <= 0) { + xyz_t end_center_pos; + + Camera2_main_Normal_SetEndCenterPos_fromPlayer(play, &end_center_pos); + Camera2_MoveCenterPosAndSpeed_fromEndCenterPos(play, &end_center_pos, force); + Camera2_SetEyePos_fromCenterPos(play); + } + else { + xyz_t end_center_pos; + + Camera2_main_Normal_SetEndCenterPos_fromPlayer(play, &end_center_pos); + Camera2_MoveVparamCenter(play, &end_center_pos); + Camera2_SetEyePos_fromCenterPos(play); + main_data->normal.morph_counter--; + } + + Camera2_SetView(play); +} + +static void Camera2_main_Normal_AdjustDistanceAndDirection(GAME_PLAY* play, f32* dist, s_xyz* dir) { + Camera2* camera = &play->camera; + CameraMainData* main_data = &camera->main_data; + + if ((main_data->normal.flags & 1) == 0 && (camera->flags & 1) == 0) { + return; + } + + *dist *= 1.5f; +} + +extern int Camera2_request_main_normal(GAME_PLAY* play, int flags, int priority) { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.normal.flags = flags; + Camera2_request_main_index(play, CAMERA2_PROCESS_NORMAL, priority); + return TRUE; + } + + return FALSE; +} + +static void Camera2_setup_main_Normal(GAME_PLAY* play) { + Camera2* camera = &play->camera; + CameraMainData* main_data = &camera->main_data; + + Camera2_setup_main_Base(play); + main_data->normal.flags = 0; + main_data->normal.last_indoor_distance_addition_idx = camera->indoor_distance_addition_idx; + main_data->normal.last_indoor_direction_addition_idx = camera->indoor_direction_addition_idx; + main_data->normal.morph_counter = 0; + + if (play->camera.request_data.normal.flags & 1) { + Camera2_main_Normal_SetPos_fromPlayer(play, TRUE); + } + else { + Camera2_main_Normal_SetPos_fromPlayer(play, FALSE); + } + + play->camera.requested_main_index_priority = 0; +} + +static void Camera2_main_Normal(GAME_PLAY* play) { + Camera2_main_Normal_SetPos_fromPlayer(play, FALSE); + Camera2_sound_Set_Ground(play); + Camera2_SetMicPos(play); + Camera2_change_main_index(play); +} + +extern int Camera2_request_main_demo( + GAME_PLAY* play, + const xyz_t* start_center, const f32 start_dist, const s_xyz* start_dir, + const xyz_t* goal_center, const f32 goal_dist, const s_xyz* goal_dir, + const f32 goal_delta, const f32 accel_delta, const f32 braking_delta, + const int priority +) { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.demo.starting_center_pos = *start_center; + play->camera.request_data.demo.starting_distance = start_dist; + play->camera.request_data.demo.starting_direction = *start_dir; + + play->camera.request_data.demo.goal_center_pos = *goal_center; + play->camera.request_data.demo.goal_distance = goal_dist; + play->camera.request_data.demo.goal_direction = *goal_dir; + + play->camera.request_data.demo.goal_delta = goal_delta; + play->camera.request_data.demo.acceleration_delta = accel_delta; + play->camera.request_data.demo.braking_delta = braking_delta; + + Camera2_request_main_index(play, CAMERA2_PROCESS_DEMO, priority); + return TRUE; + } + + return FALSE; +} + +extern int Camera2_request_main_demo_fromNowPos(GAME_PLAY* play, + const xyz_t* goal_center, const s_xyz* goal_dir, const f32 goal_dist, + const f32 goal_delta, const f32 accel_delta, const f32 braking_delta, + const int priority +) { + return Camera2_request_main_demo( + play, + &play->camera.lookat.center, play->camera.focus_distance, &play->camera.direction, + goal_center, goal_dist, goal_dir, + goal_delta, accel_delta, braking_delta, + priority + ); +} + +extern int Camera2_request_main_demo_fromNowPos2(GAME_PLAY* play, + const xyz_t* goal_center, + const f32 goal_delta, const f32 accel_delta, const f32 braking_delta, + const int priority +) { + static const s_xyz goal_direction = { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0) }; + + Camera2* camera = &play->camera; + + return Camera2_request_main_demo( + play, + &play->camera.lookat.center, play->camera.focus_distance, &play->camera.direction, + goal_center, 620.0f, &goal_direction, + goal_delta, accel_delta, braking_delta, + priority + ); +} + +static void Camera2_CalcPos_Demo(GAME_PLAY* play, xyz_t* pos, f32* dist, s_xyz* dir) { + f32 now_delta = play->camera.main_data.demo.now_delta; + f32 goal_delta = play->camera.main_data.demo.goal_delta; + f32 accel_delta = play->camera.main_data.demo.acceleration_delta; + f32 brake_delta = play->camera.main_data.demo.braking_delta; + f32 percent; + + int sx; + int dx; + int sy; + int dy; + + if (now_delta != goal_delta) { + now_delta += 1.0f; + + if (now_delta > goal_delta) { + now_delta = goal_delta; + } + + play->camera.main_data.demo.now_delta = now_delta; + } + + percent = get_percent_forAccelBrake(now_delta, 0.0f, goal_delta, accel_delta, brake_delta); + pos->x = play->camera.main_data.demo.starting_center_pos.x + percent * (play->camera.main_data.demo.goal_center_pos.x - play->camera.main_data.demo.starting_center_pos.x); + pos->y = play->camera.main_data.demo.starting_center_pos.y + percent * (play->camera.main_data.demo.goal_center_pos.y - play->camera.main_data.demo.starting_center_pos.y); + pos->z = play->camera.main_data.demo.starting_center_pos.z + percent * (play->camera.main_data.demo.goal_center_pos.z - play->camera.main_data.demo.starting_center_pos.z); + + *dist = play->camera.main_data.demo.starting_distance + percent * (play->camera.main_data.demo.goal_distance - play->camera.main_data.demo.starting_distance); + + sx = play->camera.main_data.demo.starting_direction.x; + dx = play->camera.main_data.demo.goal_direction.x - sx; + + if (ABS(dx) > (u16)SHT_MIN_S) { + if (dx > 0) { + dx -= 2 * (u16)SHT_MIN_S; + } + else { + dx += 2 * (u16)SHT_MIN_S; + } + } + + dir->x = sx + (s16)(percent * dx); + + sy = play->camera.main_data.demo.starting_direction.y; + dy = play->camera.main_data.demo.goal_direction.y - sy; + + if (ABS(dy) > (u16)SHT_MIN_S) { + if (dy > 0) { + dy -= 2 * (u16)SHT_MIN_S; + } + else { + dy += 2 * (u16)SHT_MIN_S; + } + } + + dir->y = sy + (s16)(percent * dy); + dir->z = 0; +} + +static void Camera2_SetPos_Demo(GAME_PLAY* play) { + xyz_t pos; + f32 dist; + s_xyz dir; + + Camera2_CalcPos_Demo(play, &pos, &dist, &dir); + Camera2_SetCameraParameter(play, &pos, &dir, FALSE, dist); + Camera2_SetView(play); +} + +static void Camera2_main_Demo(GAME_PLAY* play) { + Camera2_SetPos_Demo(play); + Camera2_change_main_index(play); +} + +static void Camera2_setup_main_Demo(GAME_PLAY* play) { + Camera2* camera = &play->camera; + CameraMainData* main_data = &camera->main_data; + + main_data->demo.now_delta = 0.0f; + + main_data->demo.starting_center_pos = play->camera.request_data.demo.starting_center_pos; + main_data->demo.starting_distance = play->camera.request_data.demo.starting_distance; + main_data->demo.starting_direction = play->camera.request_data.demo.starting_direction; + + main_data->demo.goal_center_pos = play->camera.request_data.demo.goal_center_pos; + main_data->demo.goal_distance = play->camera.request_data.demo.goal_distance; + main_data->demo.goal_direction = play->camera.request_data.demo.goal_direction; + + main_data->demo.goal_delta = play->camera.request_data.demo.goal_delta; + main_data->demo.acceleration_delta = play->camera.request_data.demo.acceleration_delta; + main_data->demo.braking_delta = play->camera.request_data.demo.braking_delta; + + Camera2_SetCameraParameter(play, &main_data->demo.starting_center_pos, &main_data->demo.starting_direction, TRUE, main_data->demo.starting_distance); + Camera2_SetView(play); + Camera2_setup_main_Base(play); + play->camera.requested_main_index_priority = 0; +} + +extern int Camera2_request_main_item(GAME_PLAY* play, int type, int priority) { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.item.type = type; + Camera2_request_main_index(play, CAMERA2_PROCESS_ITEM, priority); + + return TRUE; + } + + return FALSE; +} + +static void Camera2_SetPos_Item(GAME_PLAY* play) { + static const xyz_t offset = { 3.167068f, 28.884293f, 17.341202f }; + + Camera2* camera = &play->camera; + CameraMainData* main_data = &camera->main_data; + PLAYER_ACTOR* player = get_player_actor_withoutCheck(play); + int type = main_data->item.type; + xyz_t pos = player->actor_class.world.position; + s_xyz dir; + f32 dist; + + if (type == 0) { + pos.x += offset.x; + pos.y += offset.y; + pos.z += offset.z; + + pos.y += -15.0f; + } + + Camera2_MoveCenterPosAndSpeed_fromEndCenterPos(play, &pos, FALSE); + Camera2_Get_GoalDistanceAndDirection(play, &dist, &dir); + + dist += -250.0f; + dir.x += -1400; // approx. -7.7 deg + + Camera2_MoveDistancePosAndSpeed(play, dist, FALSE); + Camera2_MoveDirectionAngleXYZ(play, &dir, FALSE); + Camera2_SetEyePos_fromCenterPos(play); + Camera2_SetView(play); +} + +static void Camera2_Item_AddCullTimer(GAME_PLAY* play) { + Camera2_AddCullTimer(&play->camera.main_data.item.cull_timer, 6.0f); +} + +static void Camera2_Item_SetTalking_Cull(GAME_PLAY* play) { + Camera2_Talk_SetTalking_Cull(play); +} + +static void Camera2_main_Item(GAME_PLAY* play) { + Camera2_SetPos_Item(play); + Camera2_sound_Set_Ground(play); + Camera2_SetMicPos(play); + Camera2_Item_AddCullTimer(play); + Camera2_Item_SetTalking_Cull(play); + Camera2_change_main_index(play); +} + +static void Camera2_setup_main_Item(GAME_PLAY* play) { + play->camera.main_data.item.type = play->camera.request_data.item.type; + play->camera.main_data.item.cull_timer = 0.0f; + Camera2_setup_main_Base(play); + play->camera.requested_main_index_priority = 0; +} + +extern int Camera2_request_main_lock(GAME_PLAY* play, xyz_t* center_pos, xyz_t* eye_pos, f32 fov_y, int morph_counter, f32 near, f32 far, int priority) { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.lock.center_pos = *center_pos; + play->camera.request_data.lock.eye_pos = *eye_pos; + play->camera.request_data.lock.fov_y = fov_y; + play->camera.request_data.lock.morph_counter = morph_counter; + play->camera.request_data.lock.near = near; + play->camera.request_data.lock.far = far; + + Camera2_request_main_index(play, CAMERA2_PROCESS_LOCK, priority); + + return TRUE; + } + + return FALSE; +} + +static void Camera2_setup_main_Lock(GAME_PLAY* play) { + play->camera.main_data.lock.center_pos = play->camera.request_data.lock.center_pos; + play->camera.main_data.lock.eye_pos = play->camera.request_data.lock.eye_pos; + play->camera.main_data.lock.fov_y = play->camera.request_data.lock.fov_y; + play->camera.main_data.lock.morph_counter = play->camera.request_data.lock.morph_counter; + play->camera.main_data.lock.near = play->camera.request_data.lock.near; + play->camera.main_data.lock.far = play->camera.request_data.lock.far; + + Camera2_setup_main_Base(play); +} + +static void Camera2_Lock_SetCenterPos(GAME_PLAY* play, xyz_t* center_pos, int step) { + Camera2* camera = &play->camera; + + xyz_t* center_vel_p = &camera->movement_velocity; + xyz_t* center_p = &camera->lookat.center; + xyz_t pre_center = *center_p; + + inter_float(¢er_p->x, center_pos->x, step); + inter_float(¢er_p->y, center_pos->y, step); + inter_float(¢er_p->z, center_pos->z, step); + + center_vel_p->x = center_p->x - pre_center.x; + center_vel_p->y = center_p->y - pre_center.y; + center_vel_p->z = center_p->z - pre_center.z; +} + +static void Camera2_Lock_SetEyePos(GAME_PLAY* play, xyz_t* eye, int step) { + xyz_t* eye_p = &play->camera.lookat.eye; + + inter_float(&eye_p->x, eye->x, step); + inter_float(&eye_p->y, eye->y, step); + inter_float(&eye_p->z, eye->z, step); +} + +static void Camera2_Lock_SetFovy(GAME_PLAY* play, f32 fov_y, int step) { + inter_float(&play->camera.perspective.fov_y, fov_y, step); +} + +static void Camera2_Lock_SetNearFar(GAME_PLAY* play, f32 near, f32 far, int step) { + inter_float(&play->camera.perspective.near, near, step); + inter_float(&play->camera.perspective.far, far, step); +} + +static void Camera2_Lock_MorphCounterProc(GAME_PLAY* play) { + int morph_counter = play->camera.main_data.lock.morph_counter; + + if (morph_counter <= 0) { + play->camera.main_data.lock.morph_counter = 0; + } + else { + play->camera.main_data.lock.morph_counter = morph_counter - 1; + } +} + +static void Camera2_SetPos_Lock(GAME_PLAY* play) { + int morph_counter = play->camera.main_data.lock.morph_counter; + f32 fov_y = play->camera.main_data.lock.fov_y; + f32 near = play->camera.main_data.lock.near; + f32 far = play->camera.main_data.lock.far; + + Camera2_Lock_SetCenterPos(play, &play->camera.main_data.lock.center_pos, morph_counter); + Camera2_Lock_SetEyePos(play, &play->camera.main_data.lock.eye_pos, morph_counter); + Camera2_Lock_SetFovy(play, fov_y, morph_counter); + Camera2_Lock_SetNearFar(play, near, far, morph_counter); + Camera2_Lock_MorphCounterProc(play); + Camera2_DirectionCalc(play); + Camera2_SetView(play); +} + +static void Camera2_main_Lock(GAME_PLAY* play) { + Camera2_SetPos_Lock(play); + Camera2_sound_Set_Ground(play); + Camera2_SetMicPos(play); + Camera2_change_main_index(play); +} + +extern int Camera2_request_main_door(GAME_PLAY* play, ACTOR* door_actor, u32 flags, int priority) { + if (door_actor == NULL) { + return FALSE; + } + else if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.door.door_actor = door_actor; + play->camera.request_data.door.flags = flags; + + Camera2_request_main_index(play, CAMERA2_PROCESS_DOOR, priority); + + return TRUE; + } + else { + return FALSE; + } +} + +static void Camera2_setup_main_Door(GAME_PLAY* play) { + Camera2* camera = &play->camera; + CameraMainData* main_data = &camera->main_data; + + main_data->door.flags = camera->request_data.door.flags; + main_data->door.center_position = camera->request_data.door.door_actor->eye.position; + + if (main_data->door.flags & 1) { + main_data->door.morph_counter = 140; + } + else { + main_data->door.morph_counter = 0; + camera->requested_main_index_priority = 0; + } + + Camera2_setup_main_Base(play); +} + +static void Camera2_Door_SetCenterPos(GAME_PLAY* play, int step) { + xyz_t goal_center; + xyz_t start_center; + Camera2* camera = &play->camera; + xyz_t* center_vel_p = &camera->movement_velocity; + xyz_t* center_p = &camera->lookat.center; + + start_center = *center_p; + goal_center = camera->main_data.door.center_position; + + inter_float(¢er_p->x, goal_center.x, step); + inter_float(¢er_p->y, goal_center.y, step); + inter_float(¢er_p->z, goal_center.z, step); + + center_vel_p->x = center_p->x - start_center.x; + center_vel_p->y = center_p->y - start_center.y; + center_vel_p->z = center_p->z - start_center.z; +} + +#ifndef MUST_MATCH +/* @nonmatching - minor control flow order issue at beginning */ +static void Camera2_Door_SetEyePos(GAME_PLAY* play, int step) { + Camera2* camera = &play->camera; + xyz_t* center_p; + xyz_t* eye_p; + s_xyz* dir; + s_xyz* dir_vel; + f32* dist_p; + f32 last_dist; + s_xyz angle; + + dist_p = &camera->focus_distance; + last_dist = camera->focus_distance; + + angle.x = DEG2SHORT_ANGLE(-159.72f); + angle.y = DEG2SHORT_ANGLE(-180.00f); + angle.z = 0; + + center_p = &camera->lookat.center; + eye_p = &camera->lookat.eye; + + dir = &camera->direction; + dir_vel = &camera->direction_velocity; + + inter_float(dist_p, 350.0f, step); + camera->focus_distance_velocity = last_dist - *dist_p; // inverted velocity? + + if (step == 0) { + *dir = angle; + + dir_vel->x = 0.0f; + dir_vel->y = 0.0f; + dir_vel->z = 0.0f; + } + else { + f32 t = 1.5f / (f32)step; + + cKF_SkeletonInfo_subRotInterpolation(t, &dir->x, dir->x, angle.x); + cKF_SkeletonInfo_subRotInterpolation(t, &dir->y, dir->y, angle.y); + } + + Camera2_PolaPosCalc(eye_p, dir->x + (u16)SHT_MIN_S, dir->y + (u16)SHT_MIN_S, center_p, *dist_p); +} +#else + +#pragma push +#pragma force_active on + +//extern const f32 lbl_80641614; +//REL_SYMBOL_AT(lbl_80641614, 0x80641614); + +extern const f64 lbl_80641450; +REL_SYMBOL_AT(lbl_80641450, 0x80641450); + +extern const f32 lbl_80641484; +REL_SYMBOL_AT(lbl_80641484, 0x80641484); + +#pragma pop + +#include "orderfloats/80641614_80641618.inc" + +static asm void Camera2_Door_SetEyePos(GAME_PLAY* play, int step) { + #include "asm/803815c4.s" +} +#endif + +static void Camera2_Door_MorphCounterProc(GAME_PLAY* play) { + int morph_counter = play->camera.main_data.door.morph_counter; + + if (morph_counter <= 0) { + play->camera.main_data.door.morph_counter = 0; + } + else { + play->camera.main_data.door.morph_counter = morph_counter - 1; + } +} + +static void Camera2_SetPos_Door(GAME_PLAY* play) { + int morph_counter = play->camera.main_data.door.morph_counter; + + Camera2_Door_SetCenterPos(play, morph_counter); + Camera2_Door_SetEyePos(play, morph_counter); + Camera2_Door_MorphCounterProc(play); + Camera2_SetView(play); +} + +static void Camera2_Door_SetTalking_Cull(GAME_PLAY* play) { + Camera2_Talk_SetTalking_Cull(play); +} + +static void Camera2_main_Door(GAME_PLAY* play) { + Camera2_SetPos_Door(play); + Camera2_sound_Set_Ground(play); + Camera2_SetMicPos(play); + Camera2_Door_SetTalking_Cull(play); + Camera2_change_main_index(play); +} + +extern int Camera2_request_main_simple2(GAME_PLAY* play, xyz_t* center, s_xyz* dir, f32 dist, int morph_counter, int mode, int priority) { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.simple.center_pos = *center; + play->camera.request_data.simple.angle = *dir; + play->camera.request_data.simple.distance = dist; + play->camera.request_data.simple.morph_counter = morph_counter; + play->camera.request_data.simple.mode = mode; + + Camera2_request_main_index(play, CAMERA2_PROCESS_SIMPLE, priority); + + return TRUE; + } + + return FALSE; +} + +extern int Camera2_request_main_simple_kirin(GAME_PLAY* play, const xyz_t* center, int priority) { + xyz_t pos; + s_xyz dir; + f32 dist; + + Camera2_main_Simple_AngleDistStd(play, &dir, &dist); + dir.x += -1000; + dir.y += 2000; + + pos.x = center->x + -45.0f; + pos.y = center->y; + pos.z = center->z + -105.0f; + + return Camera2_request_main_simple2(play, &pos, &dir, dist * 0.8f, 20, 1, priority); +} + +extern int Camera2_request_main_simple_fishing(GAME_PLAY* play, const xyz_t* player_pos, const xyz_t* bobber_pos, int priority) { + f32 dist_mult = 1.0f; + + xyz_t center; + s_xyz dir; + f32 dist; + f32 x0; + f32 y0; + f32 z0; + f32 x1; + f32 y1; + f32 z1; + + Camera2_main_Simple_AngleDistStd(play, &dir, &dist); + + if (player_pos->y - bobber_pos->y > 70.0f) { + dist_mult = 1.15f; + center.x = player_pos->x * 0.55f + bobber_pos->x * 0.45f; + center.y = player_pos->y * 0.55f + bobber_pos->y * 0.45f; + center.z = player_pos->z * 0.55f + bobber_pos->z * 0.45f; + } + else { + center.x = player_pos->x * 0.65f + bobber_pos->x * 0.35f; + center.y = player_pos->y * 0.65f + bobber_pos->y * 0.35f; + center.z = player_pos->z * 0.65f + bobber_pos->z * 0.35f; + } + + return Camera2_request_main_simple(play, ¢er, &dir, dist * dist_mult, 40, priority); +} +extern int Camera2_request_main_simple_fishing_return(GAME_PLAY* play, xyz_t* player_pos, int priority) { + xyz_t center_pos; + s_xyz dir; + f32 dist; + + Camera2_main_Normal_SetEndCenterPos_fromPlayer(play, ¢er_pos); + Camera2_main_Simple_AngleDistStd(play, &dir, &dist); + return Camera2_request_main_simple(play, ¢er_pos, &dir, dist, 30, priority); +} + +extern int Camera2_request_main_simple(GAME_PLAY* play, xyz_t* pos, s_xyz* dir, f32 dist, int morph_counter, int priority) { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.simple.center_pos = *pos; + play->camera.request_data.simple.angle = *dir; + play->camera.request_data.simple.distance = dist; + play->camera.request_data.simple.morph_counter = morph_counter; + play->camera.request_data.simple.mode = 0; + Camera2_request_main_index(play, CAMERA2_PROCESS_SIMPLE, priority); + + return TRUE; + } + + return FALSE; +} + +static void Camera2_setup_main_Simple(GAME_PLAY* play) { + play->camera.main_data.simple.center_pos = play->camera.request_data.simple.center_pos; + play->camera.main_data.simple.angle = play->camera.request_data.simple.angle; + play->camera.main_data.simple.distance = play->camera.request_data.simple.distance; + play->camera.main_data.simple.morph_counter = play->camera.request_data.simple.morph_counter; + play->camera.main_data.simple.mode = play->camera.request_data.simple.mode; + play->camera.main_data.simple.cull_timer = 0.0f; + Camera2_setup_main_Base(play); +} + +extern void Camera2_main_Simple_AngleDistStd(GAME_PLAY* play, s_xyz* angle, f32* dist) { + static const s_xyz std_angle = { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }; + + *angle = std_angle; + *dist = 620.0f; + + if (Camera2_InDoorCheck() == FALSE && (play->camera.flags & 1) == 0) { + Camera2_Normal_Swing(play, dist, angle); + } +} + +static void Camera2_Simple_MorphDistance(GAME_PLAY* play, f32 goal_dist, int step) { + Camera2* camera = &play->camera; + f32* dist_p = &camera->focus_distance; + f32* dist_vel_p = &camera->focus_distance_velocity; + f32 last_dist = *dist_p; + + inter_float(dist_p, goal_dist, step); + if (step == 0) { + *dist_vel_p = 0; + } + else { + *dist_vel_p = *dist_p - last_dist; + } +} + +static void Camera2_Simple_AngleCalc(GAME_PLAY* play, const s_xyz* goal_angle, int morph_counter) { + Camera2* camera = &play->camera; + s_xyz* angle_p = &camera->direction; + s_xyz* angle_vel_p = &camera->direction_velocity; + s_xyz last_angle; + f32 t; + + if (morph_counter == 0) { + angle_p->x = goal_angle->x; + angle_p->y = goal_angle->y; + angle_p->z = goal_angle->z; + + angle_vel_p->x = 0; + angle_vel_p->y = 0; + angle_vel_p->z = 0; + } + else { + last_angle = *angle_p; + t = 1.0f / (f32)morph_counter; + + cKF_SkeletonInfo_subRotInterpolation(t, &angle_p->x, last_angle.x, goal_angle->x); + cKF_SkeletonInfo_subRotInterpolation(t, &angle_p->y, last_angle.y, goal_angle->y); + cKF_SkeletonInfo_subRotInterpolation(t, &angle_p->z, last_angle.z, goal_angle->z); + + #ifndef BUGFIXES + angle_vel_p->z = angle_p->x - last_angle.x; // BUG, it should be angle_vel_p->x + #else + angle_vel_p->x = angle_p->x - last_angle.x; + #endif + angle_vel_p->y = angle_p->y - last_angle.y; + angle_vel_p->z = angle_p->z - last_angle.z; + } +} + +static void Camera2_SetPos_Simple(GAME_PLAY* play) { + int morph_counter = play->camera.main_data.simple.morph_counter; + f32 goal_dist = play->camera.main_data.simple.distance; + + Camera2_Lock_SetCenterPos(play, &play->camera.main_data.simple.center_pos, morph_counter); + Camera2_Simple_MorphDistance(play, goal_dist, morph_counter); + Camera2_Simple_AngleCalc(play, &play->camera.main_data.simple.angle, morph_counter); + Camera2_SetEyePos_fromCenterPos(play); + Camera2_SetView(play); +} + +static void Camera2_Simple_AddCullTimer(GAME_PLAY* play) { + Camera2_AddCullTimer(&play->camera.main_data.simple.cull_timer, 6.0f); +} + +static void Camera2_main_Simple(GAME_PLAY* play) { + Camera2_SetPos_Simple(play); + Camera2_sound_Set_Ground(play); + Camera2_SetMicPos(play); + + if (play->camera.main_data.simple.mode == 1) { + Camera2_Simple_AddCullTimer(play); + Camera2_Talk_SetTalking_Cull(play); + } + + Camera2_change_main_index(play); +} + +extern int Camera2_request_main_listen_front_low_talk(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, int priority) { + const s16 y = listener->world.angle.y; + s16 angle_y; + + if (!(sin_s(y) < 0.0f)) { + angle_y = DEG2SHORT_ANGLE(-169.06f); + } + else { + angle_y = DEG2SHORT_ANGLE(169.06f); + } + + return Camera2_request_main_cust_talk(play, speaker, listener, 1.0f, priority, DEG2SHORT_ANGLE(5.495f), angle_y, 0.0f); +} + +extern int Camera2_request_main_needlework_talk(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, int priority) { + s16 angle_x; + s16 angle_y; + f32 diff_x = listener->eye.position.x - speaker->eye.position.x; + + angle_y = (s16)(diff_x * (20.0f/3.0f) + -32768.0f); + + if (angle_y > 0) { + angle_y = DEG2SHORT_ANGLE(-180.0f); + } + + angle_x = DEG2SHORT_ANGLE(5.495f); + if (fabsf(diff_x) < mFI_UT_WORLDSIZE_X_F) { + angle_x = DEG2SHORT_ANGLE(13.735f); + } + + return Camera2_request_main_cust_talk(play, speaker, listener, 0.5f, priority, angle_x, angle_y, 50.0f); +} + +extern int Camera2_request_main_cust_talk(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, f32 center_ratio, int priority, s16 angle_x, s16 angle_y, f32 distance) { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.cust_talk.speaker_actor = speaker; + play->camera.request_data.cust_talk.listener_actor = listener; + play->camera.request_data.cust_talk.center_ratio = center_ratio; + play->camera.request_data.cust_talk.angle_x = angle_x; + play->camera.request_data.cust_talk.angle_y = angle_y; + play->camera.request_data.cust_talk.distance = distance; + + Camera2_request_main_index(play, CAMERA2_PROCESS_CUST_TALK, priority); + return TRUE; + } + + return FALSE; +} + +static void Camera2_setup_main_Cust_talk(GAME_PLAY* play) { + play->camera.main_data.cust_talk.speaker_actor = play->camera.request_data.cust_talk.speaker_actor; + play->camera.main_data.cust_talk.listener_actor = play->camera.request_data.cust_talk.listener_actor; + play->camera.main_data.cust_talk.center_ratio = play->camera.request_data.cust_talk.center_ratio; + play->camera.main_data.cust_talk.angle_x = play->camera.request_data.cust_talk.angle_x; + play->camera.main_data.cust_talk.angle_y = play->camera.request_data.cust_talk.angle_y; + play->camera.main_data.cust_talk.distance = play->camera.request_data.cust_talk.distance; + play->camera.main_data.cust_talk.cull_timer = 0.0f; + + Camera2_setup_main_Base(play); + play->camera.requested_main_index_priority = 0; +} + +static void Camera2_Cust_Talk_ChangeCameraCenterPos_BetweenSpeakerToListener(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, f32 dist, f32 center_ratio) { + if (speaker != NULL || listener != NULL) { + xyz_t* pos0; + xyz_t* pos1; + xyz_t center; + + if (speaker == NULL) { + pos0 = &listener->eye.position; + pos1 = pos0; + } + else if (listener == NULL) { + pos1 = &speaker->eye.position; + pos0 = pos1; + } + else { + pos1 = &listener->eye.position; + pos0 = &speaker->eye.position; + } + + center.x = pos0->x + center_ratio * (pos1->x - pos0->x); + center.y = pos0->y + center_ratio * (pos1->y - pos0->y); + center.z = pos0->z + center_ratio * (pos1->z - pos0->z); + + Camera2_MoveCenterPosAndSpeed_fromEndCenterPos(play, ¢er, FALSE); + } +} + +static void Camera2_Cust_Talk_SetAngleDist(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, s_xyz* angle, f32 dist) { + f32 goal_dist; + + Camera2_Get_GoalDistanceAndDirection(play, &goal_dist, angle); + angle->x += play->camera.main_data.cust_talk.angle_x; + angle->y = play->camera.main_data.cust_talk.angle_y; + goal_dist += dist * 1.46f + play->camera.main_data.cust_talk.distance; + Camera2_MoveDistancePosAndSpeed(play, goal_dist, FALSE); +} + +static void Camera2_SetPos_Cust_Talk(GAME_PLAY* play) { + ACTOR* speaker = play->camera.main_data.cust_talk.speaker_actor; + ACTOR* listener = play->camera.main_data.cust_talk.listener_actor; + + if (speaker != NULL || listener != NULL) { + f32 dist; + f32 center_ratio = play->camera.main_data.cust_talk.center_ratio; + s_xyz angle; + + if (speaker == NULL || listener == NULL) { + dist = 0.0f; + } + else { + dist = search_position_distance(&speaker->world.position, &listener->world.position); + } + + Camera2_Cust_Talk_ChangeCameraCenterPos_BetweenSpeakerToListener(play, speaker, listener, dist, center_ratio); + Camera2_Cust_Talk_SetAngleDist(play, speaker, listener, &angle, dist); + Camera2_MoveDirectionAngleXYZ(play, &angle, 0); + Camera2_SetEyePos_fromCenterPos(play); + Camera2_SetView(play); + } +} + +static void Camera2_Cust_Talk_AddCullTimer(GAME_PLAY* play) { + Camera2_AddCullTimer(&play->camera.main_data.cust_talk.cull_timer, 15.0f); +} + +static void Camera2_main_Cust_talk(GAME_PLAY* play) { + Camera2_SetPos_Cust_Talk(play); + Camera2_sound_Set_Ground(play); + Camera2_SetMicPos(play); + Camera2_Cust_Talk_AddCullTimer(play); + Camera2_Talk_SetTalking_Cull(play); + Camera2_change_main_index(play); +} + +extern int Camera2_request_BuySikimono_WallPaper(GAME_PLAY* play, xyz_t* center, xyz_t* eye) { + return Camera2_request_main_inter(play, center, eye, center, eye, 0.6f, 0.3f, 1, 28, 5); +} + +extern int Camera2_Inter_set_reverse_mode(GAME_PLAY* play) { + switch (play->camera.now_main_index) { + case CAMERA2_PROCESS_INTER: + { + play->camera.main_data.inter.flags |= 2; + break; + } + + case CAMERA2_PROCESS_INTER2: + { + play->camera.main_data.inter.flags |= 4; + break; + } + } + + return TRUE; +} + +extern int Camera2_request_main_inter(GAME_PLAY* play, xyz_t* start_center, xyz_t* start_eye, xyz_t* goal_center, xyz_t* goal_eye, f32 s0, f32 s1, u32 flags, int morph_counter, int priority) { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.inter.starting_center_pos = *start_center; + play->camera.request_data.inter.starting_eye_pos = *start_eye; + play->camera.request_data.inter.goal_center_pos = *goal_center; + play->camera.request_data.inter.goal_eye_pos = *goal_eye; + play->camera.request_data.inter.slope0 = s0; + play->camera.request_data.inter.slope1 = s1; + play->camera.request_data.inter.flags = flags; + play->camera.request_data.inter.morph_counter = morph_counter; + + Camera2_request_main_index(play, CAMERA2_PROCESS_INTER, priority); + return TRUE; + } + + return FALSE; +} + +static void Camera2_setup_main_Inter(GAME_PLAY* play) { + Camera2* camera = &play->camera; + CameraRequestData* request_data = &camera->request_data; + CameraMainData* main_data = &camera->main_data; + + main_data->inter.flags = request_data->inter.flags; + + if (main_data->inter.flags & 1) { + main_data->inter.starting_center_pos = camera->lookat.center; + main_data->inter.starting_eye_pos = camera->lookat.eye; + } + else { + main_data->inter.starting_center_pos = request_data->inter.starting_center_pos; + main_data->inter.starting_eye_pos = request_data->inter.starting_eye_pos; + } + + main_data->inter.goal_center_pos = request_data->inter.goal_center_pos; + main_data->inter.goal_eye_pos = request_data->inter.goal_eye_pos; + main_data->inter.slope0 = request_data->inter.slope0; + main_data->inter.slope1 = request_data->inter.slope1; + main_data->inter.now_delta = 0; + main_data->inter.max_delta = request_data->inter.morph_counter; + + Camera2_setup_main_Base(play); + play->camera.requested_main_index_priority = 0; +} + +static void Camera2_Inter_SetPosCenter(GAME_PLAY* play, f32 center_ratio) { + Camera2* camera = &play->camera; + xyz_t* center_p = &camera->lookat.center; + const xyz_t last_center = *center_p; + + Math3DInDivPos2(&camera->main_data.inter.starting_center_pos, &camera->main_data.inter.goal_center_pos, center_p, center_ratio); + xyz_t_sub(center_p, &last_center, &camera->movement_velocity); +} + +static void Camera2_Inter_SetPosEye(GAME_PLAY* play, f32 eye_ratio) { + Camera2* camera = &play->camera; + + Math3DInDivPos2(&camera->main_data.inter.starting_eye_pos, &camera->main_data.inter.goal_eye_pos, &camera->lookat.eye, eye_ratio); +} + +static void Camera2_Inter_CounterProc(GAME_PLAY* play) { + if (play->camera.main_data.inter.flags & 2) { + int count = play->camera.main_data.inter.now_delta; + + if (count <= 0) { + play->camera.main_data.inter.now_delta = 0; + Camera2_request_main_normal(play, 0, 5); + } + else { + play->camera.main_data.inter.now_delta = count - 1; + } + } + else if (play->camera.main_data.inter.flags & 4) { + int count = play->camera.main_data.inter.now_delta; + xyz_t pos; + s_xyz angle; + f32 dist; + int block_x; + int block_z; + + if (count <= 0) { + PLAYER_ACTOR* player = get_player_actor_withoutCheck(play); + + play->camera.main_data.inter.now_delta = 0; + Camera2_main_Simple_AngleDistStd(play, &angle, &dist); + mFI_BlockKind2BkNum(&block_x, &block_z, mRF_BLOCKKIND_SHRINE); + mFI_BkNum2WposXZ(&pos.x, &pos.z, block_x, block_z); + pos.x += (UT_X_NUM / 2) * mFI_UT_WORLDSIZE_X_F; + pos.y = player->actor_class.world.position.y; + pos.z += (UT_Z_NUM / 2 + 2) * mFI_UT_WORLDSIZE_Z_F; + Camera2_request_main_simple(play, &pos, &angle, dist, 0, 6); + } + else { + play->camera.main_data.inter.now_delta = count - 1; + } + } + else { + int count = play->camera.main_data.inter.now_delta; + int max = play->camera.main_data.inter.max_delta; + + if (count >= max) { + play->camera.main_data.inter.now_delta = max; + } + else { + play->camera.main_data.inter.now_delta = count + 1; + } + } +} + +static void Camera2_SetPos_Inter(GAME_PLAY* play) { + Camera2* camera = &play->camera; + f32 ratio; + int now = camera->main_data.inter.now_delta; + int max = camera->main_data.inter.max_delta; + f32 slope0 = camera->main_data.inter.slope0; + f32 slope1 = camera->main_data.inter.slope1; + + if (max != 0) { + ratio = cKF_HermitCalc( + (f32)now / (f32)max, + 1.0f, + 0.0f, 1.0f, + slope0, slope1 + ); + } + else { + ratio = 0.0f; + } + + Camera2_Inter_SetPosCenter(play, ratio); + Camera2_Inter_SetPosEye(play, ratio); + Camera2_Inter_CounterProc(play); + Camera2_DirectionCalc(play); + Camera2_SetView(play); +} + +static void Camera2_main_Inter(GAME_PLAY* play) { + Camera2_SetPos_Inter(play); + Camera2_sound_Set_Ground(play); + Camera2_SetMicPos(play); + Camera2_change_main_index(play); +} + +extern int Camera2_request_main_staff_roll(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, int priority) { + if (Camera2_check_request_main_priority(play, priority) > 0) { + play->camera.request_data.staff_roll.speaker_actor = speaker; + play->camera.request_data.staff_roll.listener_actor = listener; + Camera2_request_main_index(play, CAMERA2_PROCESS_STAFF_ROLL, priority); + return TRUE; + } + + return FALSE; +} + +static void Camera2_setup_main_Staff_roll(GAME_PLAY* play) { + play->camera.main_data.staff_roll.last_center_pos = play->camera.lookat.center; + play->camera.main_data.staff_roll.last_eye_pos = play->camera.lookat.eye; + play->camera.main_data.staff_roll.last_distance = play->camera.focus_distance; + play->camera.main_data.staff_roll.last_direction = play->camera.direction; + play->camera.main_data.staff_roll.speaker_actor = play->camera.request_data.staff_roll.speaker_actor; + play->camera.main_data.staff_roll.listener_actor = play->camera.request_data.staff_roll.listener_actor; + play->camera.main_data.staff_roll.rotation_y_delta = 0; + play->camera.main_data.staff_roll.r_delta = 20; + play->camera.main_data.staff_roll.rotation_x_delta = 40; + play->camera.main_data.staff_roll.flags = 1; + play->camera.main_data.staff_roll.dist_counter = 0; + play->camera.main_data.staff_roll.morph_counter = 320; + + Camera2_setup_main_Base(play); + play->camera.requested_main_index_priority = 0; +} + +static void Camera2_Staff_Roll_Center(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, xyz_t* center, f32 distance, f32 t, int update) { + Camera2* camera = &play->camera; + + xyz_t* pos0; + xyz_t* pos1; + xyz_t goal_center; + f32 y_shift; + + f32 center_x; + f32 center_y; + f32 center_z; + + f32 h; + f32 r_rot; + f32 y_rot_x; + f32 y_rot_z; + int temp; + s16 temp2; + + *center = ZeroVec; + if (speaker != NULL || listener != NULL) { + if (speaker == NULL) { + pos0 = &listener->eye.position; + pos1 = pos0; + } + else if (listener == NULL) { + pos1 = &speaker->eye.position; + pos0 = pos1; + } + else { + pos1 = &listener->eye.position; + pos0 = &speaker->eye.position; + } + + y_shift = 17.0f; + if (!F32_IS_ZERO(distance)) { + y_shift -= -60.0f / distance; + } + + center_x = (pos0->x + pos1->x) / 2.0f; + center_y = (pos0->y + pos1->y) / 2.0f - y_shift; + center_z = (pos0->z + pos1->z) / 2.0f; + + h = cKF_HermitCalc(t, 1.0f, 0.0f, 1.0f, 0.49f, 0.0f); + temp2 = ((f32)camera->main_data.staff_roll.r_delta / 4600.0f) * 65535.0f; + h *= ((1.0f + sin_s(temp2) * 0.2f) - 0.2f) * 45.0f; + + // likely fakematch + temp = (int)((-(f32)camera->main_data.staff_roll.rotation_y_delta / 2600.0f) * 65535.0f) + 0x10000; + temp += camera->main_data.staff_roll.last_direction.y; + y_rot_x = sin_s(temp + -0x4000); + + goal_center.y = center_y; + goal_center.x = center_x + h * y_rot_x; + + // likely fakematch + temp = (int)((-(f32)camera->main_data.staff_roll.rotation_y_delta / 2600.0f) * 65535.0f) + 0x10000; + temp += camera->main_data.staff_roll.last_direction.y; + y_rot_z = cos_s(temp + -0x4000); + + goal_center.z = center_z + h * y_rot_z; + + *center = goal_center; + + if (update) { + Camera2_MoveCenterPosAndSpeed_fromEndCenterPos(play, &goal_center, FALSE); + } + } +} + +static void Camera2_Staff_Roll_DistAngle(GAME_PLAY* play, ACTOR* speaker, ACTOR* listener, s_xyz* angle, f32 distance) { + Camera2* camera = &play->camera; + s16 temp; + f32 dist; + + temp = ((f32)camera->main_data.staff_roll.r_delta / 4600.0f) * 65535.0f; + dist = ((sin_s(temp) * 0.2f + 1.0f) - 0.2f) * 575.0f; + temp = ((f32)camera->main_data.staff_roll.rotation_x_delta / 4000.0f) * 65536.0f; + + angle->x = (s16)(sin_s(temp) * 5000.0f); + angle->y = (s16)((-(f32)camera->main_data.staff_roll.rotation_y_delta / 2600.0f) * 65535.0f) + camera->main_data.staff_roll.last_direction.y + (u16)SHT_MIN_S; + angle->z = 0; + + Camera2_MoveDistancePosAndSpeed(play, dist, FALSE); +} + +static void Camera2_main_Staff_Roll_SetPos(GAME_PLAY* play) { + Camera2* camera = &play->camera; + ACTOR* speaker = camera->main_data.staff_roll.speaker_actor; + ACTOR* listener = camera->main_data.staff_roll.listener_actor; + CameraMainData* main_data = &camera->main_data; + f32 distance; + xyz_t center; + xyz_t eye; + xyz_t last_center; + s_xyz angle; + + if (speaker != NULL || listener != NULL) { + s8* kk_save_area = mEv_get_save_area(mEv_EVENT_KK_SLIDER, 10); + + if (speaker == NULL || listener == NULL) { + distance = 0.0f; + } + else { + distance = search_position_distance(&speaker->world.position, &listener->world.position); + } + + /* TODO: cast kk_save_area to correct struct type */ + if (((*(u16*)(kk_save_area + 2)) & 0x8000) && (main_data->staff_roll.flags & 2) == 0) { + /* Start camera sequence? */ + main_data->staff_roll.flags &= ~1; + main_data->staff_roll.flags |= 2; + main_data->staff_roll.morph_counter = 240; + } + + if (main_data->staff_roll.flags & 2) { + Camera2_Lock_SetCenterPos(play, &main_data->staff_roll.last_center_pos, main_data->staff_roll.morph_counter); + Camera2_Lock_SetEyePos(play, &main_data->staff_roll.last_eye_pos, main_data->staff_roll.morph_counter); + Camera2_DirectionCalc(play); + + if (main_data->staff_roll.morph_counter != 0) { + main_data->staff_roll.morph_counter--; + } + else { + camera->focus_distance = main_data->staff_roll.last_distance; + camera->direction = main_data->staff_roll.last_direction; + Camera2_change_priority(play, 0); + Camera2_request_main_normal(play, 0, 5); + } + } + else if (main_data->staff_roll.flags & 1) { + xyz_t* center_vel_p = &camera->movement_velocity; + xyz_t* center_p = &camera->lookat.center; + u32 morph_counter = main_data->staff_roll.morph_counter; + f32 t = (320.0f - (f32)morph_counter) / 320.0f; + + last_center = *center_p; + + + + Camera2_Staff_Roll_Center(play, speaker, listener, ¢er, distance, t, FALSE); + Camera2_Lock_SetCenterPos(play, ¢er, main_data->staff_roll.morph_counter); + center_vel_p->x = center_p->x - last_center.x; + center_vel_p->y = center_p->y - last_center.y; + center_vel_p->z = center_p->z - last_center.z; + Camera2_Staff_Roll_DistAngle(play, speaker, listener, &angle, distance); + Camera2_PolaPosCalc(&eye, angle.x + (u16)SHT_MIN_S, angle.y + (u16)SHT_MIN_S, ¢er, camera->focus_distance); + Camera2_Lock_SetEyePos(play, &eye, main_data->staff_roll.morph_counter); + + if (main_data->staff_roll.morph_counter != 0) { + main_data->staff_roll.morph_counter--; + } + else { + main_data->staff_roll.flags &= ~1; + } + + Camera2_DirectionCalc(play); + } + else { + Camera2_Staff_Roll_Center(play, speaker, listener, ¢er, distance, 1.0f, TRUE); + Camera2_Staff_Roll_DistAngle(play, speaker, listener, &angle, distance); + Camera2_MoveDirectionAngleXYZ_morph(play, &angle, 0); + Camera2_SetEyePos_fromCenterPos(play); + + main_data->staff_roll.r_delta++; + if (main_data->staff_roll.r_delta > 4600) { + main_data->staff_roll.r_delta = 0; + } + + main_data->staff_roll.rotation_x_delta++; + if (main_data->staff_roll.rotation_x_delta > 4000) { + main_data->staff_roll.rotation_x_delta = 0; + } + } + + main_data->staff_roll.rotation_y_delta++; + if (main_data->staff_roll.rotation_y_delta > 2600) { + main_data->staff_roll.rotation_y_delta = 0; + } + + Camera2_SetView(play); + } +} + +static void Camera2_main_Staff_roll(GAME_PLAY* play) { + Camera2_main_Staff_Roll_SetPos(play); + Camera2_sound_Set_Ground(play); + Camera2_SetMicPos(play); + Camera2_change_main_index(play); +} + +extern void Init_Camera2(GAME_PLAY* play) { + static const Camera2 init_camera_data = { + // lookat + { + { 0.0f, 876.81238f, 876.81238f }, // eye + { 0.0f, 0.0f, 0.0f }, // center + { 0.0f, 1.0f, 0.0f } // up + }, + // perspective + { + 20.0f, // fov_y + 4.0f/3.0f, // aspect_ratio + 200.0f, // near + 1600.0f, // far + 1.0f // scale + }, + { DEG2SHORT_ANGLE(-135.0f), DEG2SHORT_ANGLE(-180.0f), DEG2SHORT_ANGLE(0.0f) }, // direction + { 0, 0, 0 }, // direction_velocity + { 0.0f, 0.0f, 0.0f }, // movement_velocity + 620.0f, // focus_distance + 0.0f, // focus_distance_velocity + 1, // indoor_distance_addition_idx + 1, // indoor_direction_addition_idx + CAMERA2_PROCESS_NORMAL, // now_main_index + CAMERA2_PROCESS_NORMAL, // last_main_index + CAMERA2_PROCESS_NORMAL, // requested_main_index + -1, // requested_main_index_priority + FALSE, // requested_main_index_flag + { 0 }, // main_data + { 0 }, // request_data + { 0.0f, 876.81238f, 876.81238f }, // mic_pos + 0, // flags + { 0.0f, 0.0f, 0.0f }, // offset_eye + { 0.0f, 0.0f, 0.0f } // offset_center + }; + + play->camera = init_camera_data; +} + +typedef void (*CAMERA2_MAIN_INDEX_SETUP_PROC)(GAME_PLAY*); + +static void Camera2_change_main_index(GAME_PLAY* play) { + static const CAMERA2_MAIN_INDEX_SETUP_PROC proc[CAMERA2_PROCESS_NUM] = { + &Camera2_setup_main_Stop, + &Camera2_setup_main_Normal, + &Camera2_setup_main_Wade, + &Camera2_setup_main_Talk, + &Camera2_setup_main_Demo, + &Camera2_setup_main_Item, + &Camera2_setup_main_Lock, + &Camera2_setup_main_Door, + &Camera2_setup_main_Simple, + &Camera2_setup_main_Cust_talk, + &Camera2_setup_main_Inter, + &Camera2_setup_main_Staff_roll, + &Camera2_setup_main_Inter + }; + + if (play->camera.requested_main_index_flag != TRUE || + play->camera.requested_main_index < CAMERA2_PROCESS_STOP || + play->camera.requested_main_index >= CAMERA2_PROCESS_NUM || + proc[play->camera.requested_main_index] == NULL + ) { + return; + } + else { + CAMERA2_MAIN_INDEX_SETUP_PROC setup_proc = proc[play->camera.requested_main_index]; + (*setup_proc)(play); + } +} + +typedef void (*CAMERA2_MAIN_INDEX_PROC)(GAME_PLAY*); +extern void Camera2_process(GAME_PLAY* play) { + static const CAMERA2_MAIN_INDEX_PROC proc[CAMERA2_PROCESS_NUM] = { + &Camera2_main_Stop, + &Camera2_main_Normal, + &Camera2_main_Wade, + &Camera2_main_Talk, + &Camera2_main_Demo, + &Camera2_main_Item, + &Camera2_main_Lock, + &Camera2_main_Door, + &Camera2_main_Simple, + &Camera2_main_Cust_talk, + &Camera2_main_Inter, + &Camera2_main_Staff_roll, + &Camera2_main_Inter + }; + + if ( + play->camera.now_main_index < CAMERA2_PROCESS_STOP || + play->camera.now_main_index >= CAMERA2_PROCESS_NUM || + proc[play->camera.now_main_index] == NULL + ) { + return; + } + else { + CAMERA2_MAIN_INDEX_PROC main_proc = proc[play->camera.now_main_index]; + + (*main_proc)(play); + Camera2_process_other_func2(play); + } +} + +extern void Camera2_draw(GAME_PLAY* play) { }