diff --git a/data/tr2/ship/cfg/TR2X_gameflow.json5 b/data/tr2/ship/cfg/TR2X_gameflow.json5 index 352714261..e7488071d 100644 --- a/data/tr2/ship/cfg/TR2X_gameflow.json5 +++ b/data/tr2/ship/cfg/TR2X_gameflow.json5 @@ -135,7 +135,7 @@ "O_BANDIT_2": "Mercenary 2", "O_BANDIT_2B": "Mercenary 3", "O_SKIDOO_ARMED": "Black Snowmobile", - "O_SKIDMAN": "Black Snowmobile Driver", + "O_SKIDOO_DRIVER": "Black Snowmobile Driver", "O_MONK_1": "Monk 1", "O_MONK_2": "Monk 2", "O_FALLING_BLOCK_1": "Falling Block 1", @@ -174,7 +174,7 @@ "O_BELL": "Bell", "O_WATER_SPRITE": "Boat Wake", "O_SNOW_SPRITE": "Snowmobile Wale", - "O_SKIDOO_LARA": "Snowmobile Animation", + "O_SKIDOO_TRACK": "Snowmobile Track", "O_SWITCH_TYPE_AIRLOCK": "Airlock Switch", "O_SWITCH_TYPE_SMALL": "Small Switch", "O_PROPELLER_2": "Underwater Propeller", diff --git a/docs/tr2/progress.svg b/docs/tr2/progress.svg index f1e9c4602..90aa91d38 100644 --- a/docs/tr2/progress.svg +++ b/docs/tr2/progress.svg @@ -69,10 +69,10 @@ Tomb2.exe progress according to the physical function order: -65.84% (819) · 31.75% (395) · 0% (0) · 2.41% (30) +67.36% (838) · 30.23% (376) · 0% (0) · 2.41% (30) - - + + @@ -163,7 +163,7 @@ void __cdecl Bird_Initialise(int16_t item_num); void __cdecl Bird_Control(int16_t item_num); void __cdecl Boat_Initialise(int16_t item_num); -int32_t __cdecl Boat_CheckGeton(int16_t item_num, COLL_INFO *coll); +int32_t __cdecl Boat_CheckGetOn(int16_t item_num, COLL_INFO *coll); void __cdecl Boat_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); int32_t __cdecl Boat_TestWaterHeight(ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos); void __cdecl Boat_DoShift(int32_t boat_num); @@ -777,28 +777,28 @@ void __cdecl Jelly_Control(int16_t item_num); void __cdecl Baracudda_Control(int16_t item_num); void __cdecl Shark_Control(int16_t item_num); -void __cdecl InitialiseSkidoo(int16_t item_num); -int32_t __cdecl SkidooCheckGeton(int16_t item_num, COLL_INFO *coll); -void __cdecl SkidooCollision(int16_t item_num, ITEM *litem, COLL_INFO *coll); -void __cdecl SkidooBaddieCollision(ITEM *skidoo); -int32_t __cdecl TestHeight(ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos); +void __cdecl Skidoo_Initialise(int16_t item_num); +int32_t __cdecl Skidoo_CheckGetOn(int16_t item_num, COLL_INFO *coll); +void __cdecl Skidoo_Collision(int16_t item_num, ITEM *litem, COLL_INFO *coll); +void __cdecl Skidoo_BaddieCollision(const ITEM *skidoo); +int32_t __cdecl Skidoo_TestHeight(const ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos); int32_t __cdecl DoShift(ITEM *skidoo, XYZ_32 *pos, XYZ_32 *old); int32_t __cdecl DoDynamics(int32_t height, int32_t fall_speed, int32_t *y); int32_t __cdecl GetCollisionAnim(ITEM *skidoo, XYZ_32 *moved); -void __cdecl DoSnowEffect(ITEM *skidoo); -int32_t __cdecl SkidooDynamics(ITEM *skidoo); -int32_t __cdecl SkidooUserControl(ITEM *skidoo, int32_t height, int32_t *pitch); -int32_t __cdecl SkidooCheckGetOffOK(int32_t direction); -void __cdecl SkidooAnimation(ITEM *skidoo, int32_t collide, int32_t dead); -void __cdecl SkidooExplode(ITEM *skidoo); -int32_t __cdecl SkidooCheckGetOff(void); -void __cdecl SkidooGuns(void); -int32_t __cdecl SkidooControl(void); -void __cdecl SkidooArmed_Draw(const ITEM *item); -void __cdecl SkidooDriver_Initialise(int16_t item_num); -void __cdecl SkidooDriver_Control(int16_t rider_num); -void __cdecl SkidmanPush(ITEM *item, ITEM *lara_item, int32_t radius); -void __cdecl SkidooDriver_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); +void __cdecl Skidoo_DoSnowEffect(ITEM *skidoo); +int32_t __cdecl Skidoo_Dynamics(ITEM *skidoo); +int32_t __cdecl Skidoo_UserControl(ITEM *skidoo, int32_t height, int32_t *pitch); +int32_t __cdecl Skidoo_CheckGetOffOK(int32_t direction); +void __cdecl Skidoo_Animation(ITEM *skidoo, int32_t collide, int32_t dead); +void __cdecl Skidoo_Explode(const ITEM *skidoo); +int32_t __cdecl Skidoo_CheckGetOff(void); +void __cdecl Skidoo_Guns(void); +int32_t __cdecl Skidoo_Control(void); +void __cdecl Skidoo_Draw(const ITEM *item); +void __cdecl SkidooDriver_Initialise(int16_t item_num); +void __cdecl SkidooDriver_Control(int16_t rider_num); +void __cdecl SkidooArmed_Push(const ITEM *item, ITEM *lara_item, int32_t radius); +void __cdecl SkidooArmed_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); int32_t __cdecl Music_GetRealTrack(int32_t track); void __cdecl Sound_Effect(int32_t sample_id, const XYZ_32 *pos, uint32_t flags); void __cdecl Sound_StopEffect(int32_t sample_id); @@ -1324,10 +1324,10 @@ Tomb2.exe progress according to the function sizes: -70.23% · 29.45% · 0% · 0.33% +72.69% · 26.98% · 0% · 0.33% - - + + @@ -1375,14 +1375,14 @@ void __cdecl Output_InsertTrans8(const PHD_VBUF *vbuf, int16_t shade); void __cdecl Monk_Control(int16_t item_num); LRESULT __stdcall WinVidGameWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); -int32_t __cdecl SkidooDynamics(ITEM *skidoo); +int32_t __cdecl Skidoo_Dynamics(ITEM *skidoo); void __cdecl Option_Sound(INVENTORY_ITEM *item); void __cdecl Sound_Effect(int32_t sample_id, const XYZ_32 *pos, uint32_t flags); void __cdecl Pickup_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); int32_t __cdecl GF_InterpretSequence(int16_t *ptr, GAMEFLOW_LEVEL_TYPE type, int32_t seq_type); TARGET_TYPE __cdecl Box_CalculateTarget(XYZ_32 *target, ITEM *item, LOT_INFO *lot); void __cdecl Output_InsertGT4_ZBuffered(const PHD_VBUF *vtx0, const PHD_VBUF *vtx1, const PHD_VBUF *vtx2, const PHD_VBUF *vtx3, const PHD_TEXTURE *texture); -int32_t __cdecl SkidooControl(void); +int32_t __cdecl Skidoo_Control(void); void __cdecl Creature_Mood(ITEM *item, AI_INFO *info, int32_t violent); const int16_t *__cdecl Output_InsertObjectG4_Sorted(const int16_t *obj_ptr, int32_t num, SORT_TYPE sort_type); void __cdecl Worker2_Control(int16_t item_num); @@ -1394,7 +1394,7 @@ void __cdecl Cultist1_Control(int16_t item_num); void __cdecl Rocket_Control(int16_t item_num); void __cdecl XianKnight_Draw(const ITEM *item); -void __cdecl SkidooDriver_Control(int16_t rider_num); +void __cdecl SkidooDriver_Control(int16_t rider_num); void __cdecl InitialiseDoor(int16_t item_num); int32_t __cdecl Collide_CollideStaticObjects(COLL_INFO *coll, int32_t x, int32_t y, int32_t z, int16_t room_num, int32_t height); void __cdecl Room_Clip(ROOM *r); @@ -1446,7 +1446,7 @@ bool __cdecl SE_ShowSetupDialog(HWND hParent, bool isDefault); void __cdecl Option_Detail(INVENTORY_ITEM *item); int32_t __cdecl Collide_GetSpheres(const ITEM *item, SPHERE *spheres, bool world_space); -void __cdecl SkidooArmed_Draw(const ITEM *item); +void __cdecl Skidoo_Draw(const ITEM *item); const int16_t *__cdecl Output_InsertObjectG3_ZBuffered(const int16_t *obj_ptr, int32_t num, SORT_TYPE sort_type); int32_t __cdecl Output_XYClipper(int32_t vtx_count, VERTEX_INFO *vtx); void __cdecl ControlMissile(int16_t fx_num); @@ -1455,7 +1455,7 @@ void __cdecl GiantYeti_Control(int16_t item_num); void __cdecl Output_DrawSprite(uint32_t flags, int32_t x, int32_t y, int32_t z, int16_t sprite_idx, int16_t shade, int16_t scale); void __cdecl Lara_Initialise(int32_t type); -void __cdecl SkidooAnimation(ITEM *skidoo, int32_t collide, int32_t dead); +void __cdecl Skidoo_Animation(ITEM *skidoo, int32_t collide, int32_t dead); void __cdecl Room_DrawAllRooms(int16_t current_room); void __cdecl PuzzleHole_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); void __cdecl Zipline_Control(int16_t item_num); @@ -1485,7 +1485,7 @@ void __cdecl MovableBlock_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); int32_t __cdecl Lara_TestClimbUpPos(ITEM *item, int32_t front, int32_t right, int32_t *shift, int32_t *ledge); void __cdecl SE_OptionsDlgUpdate(HWND hwndDlg); -int32_t __cdecl SkidooCheckGetOff(void); +int32_t __cdecl Skidoo_CheckGetOff(void); void __cdecl DisplayCredits(void); void __cdecl Gun_Pistols_Undraw(LARA_GUN_TYPE weapon_type); int32_t __cdecl MovableBlock_TestPull(ITEM *item, int32_t block_height, uint16_t quadrant); @@ -1509,7 +1509,7 @@ void __cdecl CreateStartInfo(int32_t level_num); void __cdecl Bird_Control(int16_t item_num); void __cdecl BodyPart_Control(int16_t fx_num); -void __cdecl SkidooBaddieCollision(ITEM *skidoo); +void __cdecl Skidoo_BaddieCollision(const ITEM *skidoo); void __cdecl S_DrawAirBar(int32_t percent); void __cdecl Screenshot(LPDDS screen); void __cdecl MineControl(int16_t mine_num); @@ -1541,7 +1541,7 @@ BOOL __stdcall EnumDisplayAdaptersCallback(GUID *lpGUID, LPTSTR lpDriverDescription, LPTSTR lpDriverName, LPVOID lpContext); void __cdecl BGND_Make640x480(uint8_t *bitmap, RGB_888 *palette); void __cdecl D3DDeviceCreate(LPDDS lpBackBuffer); -int32_t __cdecl SkidooUserControl(ITEM *skidoo, int32_t height, int32_t *pitch); +int32_t __cdecl Skidoo_UserControl(ITEM *skidoo, int32_t height, int32_t *pitch); void __cdecl Item_Initialise(int16_t item_num); int32_t __cdecl Demo_Start(int32_t level_num); bool __cdecl LOT_EnableBaddieAI(int16_t item_num, bool always); @@ -1584,7 +1584,7 @@ void __cdecl Boat_DoWakeEffect(ITEM *boat); bool __cdecl TexturePageInit(TEXPAGE_DESC *page); void __cdecl Winston_Control(int16_t item_num); -int32_t __cdecl Boat_CheckGeton(int16_t item_num, COLL_INFO *coll); +int32_t __cdecl Boat_CheckGetOn(int16_t item_num, COLL_INFO *coll); void __cdecl Creature_Kill(ITEM *item, int32_t kill_anim, int32_t kill_state, int32_t lara_kill_state); int32_t __cdecl Game_Cutscene_Control(int32_t nframes); int32_t __cdecl Misc_Move3DPosTo3DPos(PHD_3DPOS *src_pos, const PHD_3DPOS *dest_pos, int32_t velocity, PHD_ANGLE ang_add); @@ -1621,7 +1621,7 @@ void __cdecl Lara_State_Run(ITEM *item, COLL_INFO *coll); int32_t __cdecl Output_ZedClipper(int32_t vtx_count, POINT_INFO *pts, VERTEX_INFO *vtx); void __cdecl Output_DrawClippedPoly_Textured(int32_t vtx_count); -void __cdecl DoSnowEffect(ITEM *skidoo); +void __cdecl Skidoo_DoSnowEffect(ITEM *skidoo); void __cdecl Lara_UseItem(GAME_OBJECT_ID object_id); void __cdecl Inv_Ring_DoMotions(RING_INFO *ring); void __cdecl DartEmitterControl(int16_t item_num); @@ -1656,10 +1656,10 @@ void __cdecl Item_Kill(int16_t item_num); void __cdecl Gun_InitialiseNewWeapon(void); void __cdecl Lara_Col_UpJump(ITEM *item, COLL_INFO *coll); -void __cdecl SkidooGuns(void); +void __cdecl Skidoo_Guns(void); void __cdecl SE_SoundDlgUpdate(HWND hwndDlg); HRESULT __stdcall EnumTextureFormatsCallback(LPDDSDESC lpDdsd, LPVOID lpContext); -void __cdecl SkidmanPush(ITEM *item, ITEM *lara_item, int32_t radius); +void __cdecl SkidooArmed_Push(const ITEM *item, ITEM *lara_item, int32_t radius); void __cdecl Jelly_Control(int16_t item_num); void __cdecl S_DrawScreenBox(int32_t sx, int32_t sy, int32_t z, int32_t width, int32_t height, BYTE color_idx, const GOURAUD_OUTLINE *gour, uint16_t flags); void __cdecl ControlCeilingSpikes(int16_t item_num); @@ -1682,7 +1682,7 @@ int32_t __cdecl Text_GetWidth(TEXTSTRING *string); int32_t __cdecl Diver_GetWaterSurface(int32_t x, int32_t y, int32_t z, int16_t room_num); void __cdecl LOT_InitialiseSlot(int16_t item_num, int32_t slot); -int32_t __cdecl SkidooCheckGetOffOK(int32_t direction); +int32_t __cdecl Skidoo_CheckGetOffOK(int32_t direction); INT_PTR __stdcall SE_ControlsDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); TEXTSTRING *__cdecl Text_Create(int32_t x, int32_t y, int32_t z, const char *text); void __cdecl FallingBlock(int16_t item_num); @@ -1700,7 +1700,7 @@ int32_t __cdecl Boat_TestWaterHeight(ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos); int32_t __cdecl Creature_CheckBaddieOverlap(int16_t item_num); void __cdecl Flare_DrawInAir(ITEM *item); -void __cdecl SkidooCollision(int16_t item_num, ITEM *litem, COLL_INFO *coll); +void __cdecl Skidoo_Collision(int16_t item_num, ITEM *litem, COLL_INFO *coll); void __cdecl Output_DrawPoly_Gouraud(int32_t vtx_count, int32_t red, int32_t green, int32_t blue); int32_t __cdecl Lara_TestHangOnClimbWall(ITEM *item, COLL_INFO *coll); void __cdecl Gun_Rifle_Control(LARA_GUN_TYPE weapon_type); @@ -1733,7 +1733,7 @@ bool __cdecl WinVidSpinMessageLoop(bool needWait); BOOL __stdcall S_Audio_Sample_DSoundEnumCallback(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context); int32_t __cdecl Lara_TestWall(ITEM *item, int32_t front, int32_t right, int32_t down); -int32_t __cdecl SkidooCheckGeton(int16_t item_num, COLL_INFO *coll); +int32_t __cdecl Skidoo_CheckGetOn(int16_t item_num, COLL_INFO *coll); void __cdecl FallingCeiling(int16_t item_num); BOOL __cdecl SelectDrive(void); void __cdecl ControlHotLiquid(int16_t fx_num); @@ -1819,7 +1819,7 @@ void __cdecl Lara_Col_FastBack(ITEM *item, COLL_INFO *coll); void __cdecl Lara_Col_Roll2(ITEM *item, COLL_INFO *coll); void __cdecl AssaultFinished(ITEM *item); -int32_t __cdecl TestHeight(ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos); +int32_t __cdecl Skidoo_TestHeight(const ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos); int32_t __cdecl Lara_TestHangSwingIn(ITEM *item, PHD_ANGLE angle); int32_t __cdecl OnDrawBridge(ITEM *item, int32_t x, int32_t y); void __cdecl S_AnimateTextures(int32_t ticks); @@ -1956,11 +1956,11 @@ int32_t __cdecl AddAssaultTime(uint32_t time); void __cdecl Lara_Col_Stop(ITEM *item, COLL_INFO *coll); void __cdecl Lara_Col_Roll(ITEM *item, COLL_INFO *coll); -void __cdecl SkidooDriver_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); +void __cdecl SkidooArmed_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); int32_t __cdecl Room_FindByPos(int32_t x, int32_t y, int32_t z); void __cdecl ControlExplosion1(int16_t fx_num); int32_t __cdecl Lara_DeflectEdge(ITEM *item, COLL_INFO *coll); -void __cdecl SkidooDriver_Initialise(int16_t item_num); +void __cdecl SkidooDriver_Initialise(int16_t item_num); bool __cdecl WinInputInit(void); void __cdecl Random_Seed(void); void __cdecl Option_DoInventory(INVENTORY_ITEM *item); @@ -1974,7 +1974,7 @@ void __cdecl Lara_State_StepLeft(ITEM *item, COLL_INFO *coll); void __cdecl Gun_Pistols_Draw(LARA_GUN_TYPE weapon_type); void __cdecl InitialiseDyingMonk(int16_t item_num); -void __cdecl SkidooExplode(ITEM *skidoo); +void __cdecl Skidoo_Explode(const ITEM *skidoo); int32_t __cdecl WinGameStart(void); void __cdecl Gun_Rifle_FireM16(bool running); void __cdecl Matrix_TranslateAbs(int32_t x, int32_t y, int32_t z); @@ -2191,7 +2191,7 @@ void __cdecl lara_normal_effect(ITEM *item); void __cdecl Inv_Ring_Light(RING_INFO *ring); void __cdecl Lara_State_FastFall(ITEM *item, COLL_INFO *coll); -void __cdecl InitialiseSkidoo(int16_t item_num); +void __cdecl Skidoo_Initialise(int16_t item_num); bool __cdecl WinVidInit(void); void __cdecl Option_Controls_DefaultConflict(void); void __cdecl UpdateTicks(void); diff --git a/docs/tr2/progress.txt b/docs/tr2/progress.txt index f3a1a6e5a..40b263fe5 100644 --- a/docs/tr2/progress.txt +++ b/docs/tr2/progress.txt @@ -1053,6 +1053,18 @@ typedef enum { GAMEMODE_IN_CUTSCENE } GAMEMODE; +typedef enum { + TRAP_SET = 0, + TRAP_ACTIVATE = 1, + TRAP_WORKING = 2, + TRAP_FINISHED = 3, +} TRAP_ANIM; + +typedef enum { + DOOR_CLOSED = 0, + DOOR_OPEN = 1, +} DOOR_ANIM; + typedef enum { GFD_START_GAME = 0x0000, GFD_START_SAVED_GAME = 0x0100, @@ -2017,7 +2029,7 @@ typedef enum { // decompiled LGT_M16 = 5, LGT_GRENADE = 6, LGT_HARPOON = 7, - LGT_FLARE = 8 , + LGT_FLARE = 8, LGT_SKIDOO = 9, NUM_WEAPONS = 10, } LARA_GUN_TYPE; @@ -2095,7 +2107,7 @@ typedef enum { // decompiled O_BANDIT_2 = 49, O_BANDIT_2B = 50, O_SKIDOO_ARMED = 51, - O_SKIDMAN = 52, + O_SKIDOO_DRIVER = 52, O_MONK_1 = 53, O_MONK_2 = 54, O_FALLING_BLOCK_1 = 55, @@ -2134,7 +2146,7 @@ typedef enum { // decompiled O_BELL = 88, O_WATER_SPRITE = 89, O_SNOW_SPRITE = 90, - O_SKIDOO_LARA = 91, + O_SKIDOO_TRACK = 91, O_SWITCH_TYPE_AIRLOCK = 92, O_SWITCH_TYPE_SMALL = 93, O_PROPELLER_2 = 94, @@ -2631,6 +2643,16 @@ typedef struct __unaligned { int32_t pitch; } BOAT_INFO; +typedef struct __unaligned { + int16_t track_mesh; + int32_t skidoo_turn; + int32_t left_fallspeed; + int32_t right_fallspeed; + int16_t momentum_angle; + int16_t extra_rotation; + int32_t pitch; +} SKIDOO_INFO; + typedef struct __unaligned { struct { XYZ_16 min; @@ -2940,7 +2962,7 @@ typedef enum { # game/boat.c 0x0040CB30 0x003C + void __cdecl Boat_Initialise(int16_t item_num); -0x0040CB70 0x0170 + int32_t __cdecl Boat_CheckGeton(int16_t item_num, COLL_INFO *coll); +0x0040CB70 0x0170 + int32_t __cdecl Boat_CheckGetOn(int16_t item_num, COLL_INFO *coll); 0x0040CCE0 0x015E + void __cdecl Boat_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); 0x0040CE40 0x00F8 + int32_t __cdecl Boat_TestWaterHeight(ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos); 0x0040CF40 0x01C1 + void __cdecl Boat_DoShift(int32_t boat_num); @@ -3634,28 +3656,28 @@ typedef enum { 0x0043CBA0 0x027C -R void __cdecl Shark_Control(int16_t item_num); # game/skidoo.c -0x0043CE30 0x0040 -R void __cdecl InitialiseSkidoo(int16_t item_num); -0x0043CE70 0x00E1 -R int32_t __cdecl SkidooCheckGeton(int16_t item_num, COLL_INFO *coll); -0x0043CF60 0x00F8 -R void __cdecl SkidooCollision(int16_t item_num, ITEM *litem, COLL_INFO *coll); -0x0043D060 0x01F9 -R void __cdecl SkidooBaddieCollision(ITEM *skidoo); -0x0043D260 0x00B2 -R int32_t __cdecl TestHeight(ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos); +0x0043CE30 0x0040 +R void __cdecl Skidoo_Initialise(int16_t item_num); +0x0043CE70 0x00E1 +R int32_t __cdecl Skidoo_CheckGetOn(int16_t item_num, COLL_INFO *coll); +0x0043CF60 0x00F8 +R void __cdecl Skidoo_Collision(int16_t item_num, ITEM *litem, COLL_INFO *coll); +0x0043D060 0x01F9 +R void __cdecl Skidoo_BaddieCollision(const ITEM *skidoo); +0x0043D260 0x00B2 +R int32_t __cdecl Skidoo_TestHeight(const ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos); 0x0043D320 0x027C -R int32_t __cdecl DoShift(ITEM *skidoo, XYZ_32 *pos, XYZ_32 *old); 0x0043D5A0 0x0054 -R int32_t __cdecl DoDynamics(int32_t height, int32_t fall_speed, int32_t *y); 0x0043D600 0x0090 -R int32_t __cdecl GetCollisionAnim(ITEM *skidoo, XYZ_32 *moved); -0x0043D690 0x0140 -R void __cdecl DoSnowEffect(ITEM *skidoo); -0x0043D7D0 0x049E -R int32_t __cdecl SkidooDynamics(ITEM *skidoo); -0x0043DC70 0x01B6 -R int32_t __cdecl SkidooUserControl(ITEM *skidoo, int32_t height, int32_t *pitch); -0x0043DE30 0x0106 -R int32_t __cdecl SkidooCheckGetOffOK(int32_t direction); -0x0043DF40 0x02B9 -R void __cdecl SkidooAnimation(ITEM *skidoo, int32_t collide, int32_t dead); -0x0043E220 0x007C -R void __cdecl SkidooExplode(ITEM *skidoo); -0x0043E2A0 0x0233 -R int32_t __cdecl SkidooCheckGetOff(void); -0x0043E4E0 0x011B -R void __cdecl SkidooGuns(void); -0x0043E600 0x0440 -R int32_t __cdecl SkidooControl(void); -0x0043EA60 0x02D5 -R void __cdecl SkidooArmed_Draw(const ITEM *item); -0x0043ED40 0x007F -R void __cdecl SkidooDriver_Initialise(int16_t item_num); -0x0043EDD0 0x03E2 - void __cdecl SkidooDriver_Control(int16_t rider_num); -0x0043F1D0 0x0119 -R void __cdecl SkidmanPush(ITEM *item, ITEM *lara_item, int32_t radius); -0x0043F2F0 0x0081 - void __cdecl SkidooDriver_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); +0x0043D690 0x0140 +R void __cdecl Skidoo_DoSnowEffect(ITEM *skidoo); +0x0043D7D0 0x049E +R int32_t __cdecl Skidoo_Dynamics(ITEM *skidoo); +0x0043DC70 0x01B6 +R int32_t __cdecl Skidoo_UserControl(ITEM *skidoo, int32_t height, int32_t *pitch); +0x0043DE30 0x0106 +R int32_t __cdecl Skidoo_CheckGetOffOK(int32_t direction); +0x0043DF40 0x02B9 +R void __cdecl Skidoo_Animation(ITEM *skidoo, int32_t collide, int32_t dead); +0x0043E220 0x007C +R void __cdecl Skidoo_Explode(const ITEM *skidoo); +0x0043E2A0 0x0233 +R int32_t __cdecl Skidoo_CheckGetOff(void); +0x0043E4E0 0x011B +R void __cdecl Skidoo_Guns(void); +0x0043E600 0x0440 +R int32_t __cdecl Skidoo_Control(void); +0x0043EA60 0x02D5 +R void __cdecl Skidoo_Draw(const ITEM *item); +0x0043ED40 0x007F + void __cdecl SkidooDriver_Initialise(int16_t item_num); +0x0043EDD0 0x03E2 + void __cdecl SkidooDriver_Control(int16_t rider_num); +0x0043F1D0 0x0119 + void __cdecl SkidooArmed_Push(const ITEM *item, ITEM *lara_item, int32_t radius); +0x0043F2F0 0x0081 + void __cdecl SkidooArmed_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll); # game/sound.c 0x0043F380 0x0031 * int32_t __cdecl Music_GetRealTrack(int32_t track); @@ -4720,6 +4742,8 @@ typedef enum { 0x004645A8 - const uint16_t g_Requester_UnselectionGour1[]; 0x005216E0 - uint16_t g_InvColors[17]; // INV_COLOR_NUMBER_OF 0x00464150 + BITE g_DragonMouth; +0x00466230 + BITE g_SkidooLeftGun; +0x00466240 + BITE g_SkidooRightGun; 0x00465F40 - int16_t g_MovableBlockBounds[]; 0x00465F58 - int16_t g_ZiplineHandleBounds[]; 0x00465FF0 - int16_t g_PickupBounds[]; diff --git a/src/libtrx/game/rooms/common.c b/src/libtrx/game/rooms/common.c new file mode 100644 index 000000000..d2a532651 --- /dev/null +++ b/src/libtrx/game/rooms/common.c @@ -0,0 +1,23 @@ +#include "game/rooms/common.h" + +#include + +int32_t Room_GetAdjoiningRooms( + int16_t init_room_num, int16_t out_room_nums[], + const int32_t max_room_num_count) +{ + int32_t count = 0; + if (max_room_num_count >= 1) { + out_room_nums[count++] = init_room_num; + } + + const PORTALS *const portals = Room_Get(init_room_num)->portals; + if (portals != NULL) { + for (int32_t i = 0; i < portals->count; i++) { + const int16_t room_num = portals->portal[i].room_num; + out_room_nums[count++] = room_num; + } + } + + return count; +} diff --git a/src/libtrx/include/libtrx/game/objects/ids_tr2.def b/src/libtrx/include/libtrx/game/objects/ids_tr2.def index b8fa8e92c..dc9d8653c 100644 --- a/src/libtrx/include/libtrx/game/objects/ids_tr2.def +++ b/src/libtrx/include/libtrx/game/objects/ids_tr2.def @@ -50,7 +50,7 @@ OBJ_ID_DEFINE(O_BANDIT_1, 48) OBJ_ID_DEFINE(O_BANDIT_2, 49) OBJ_ID_DEFINE(O_BANDIT_2B, 50) OBJ_ID_DEFINE(O_SKIDOO_ARMED, 51) -OBJ_ID_DEFINE(O_SKIDMAN, 52) +OBJ_ID_DEFINE(O_SKIDOO_DRIVER, 52) OBJ_ID_DEFINE(O_MONK_1, 53) OBJ_ID_DEFINE(O_MONK_2, 54) OBJ_ID_DEFINE(O_FALLING_BLOCK_1, 55) @@ -89,7 +89,7 @@ OBJ_ID_DEFINE(O_CEILING_SPIKES, 87) OBJ_ID_DEFINE(O_BELL, 88) OBJ_ID_DEFINE(O_WATER_SPRITE, 89) OBJ_ID_DEFINE(O_SNOW_SPRITE, 90) -OBJ_ID_DEFINE(O_SKIDOO_LARA, 91) +OBJ_ID_DEFINE(O_SKIDOO_TRACK, 91) OBJ_ID_DEFINE(O_SWITCH_TYPE_AIRLOCK, 92) OBJ_ID_DEFINE(O_SWITCH_TYPE_SMALL, 93) OBJ_ID_DEFINE(O_PROPELLER_2, 94) diff --git a/src/libtrx/include/libtrx/game/objects/names_tr2.def b/src/libtrx/include/libtrx/game/objects/names_tr2.def index a6f1e5a5d..4ee74fb1f 100644 --- a/src/libtrx/include/libtrx/game/objects/names_tr2.def +++ b/src/libtrx/include/libtrx/game/objects/names_tr2.def @@ -50,7 +50,7 @@ OBJ_NAME_DEFINE(O_BANDIT_1, "Mercenary 1") OBJ_NAME_DEFINE(O_BANDIT_2, "Mercenary 2") OBJ_NAME_DEFINE(O_BANDIT_2B, "Mercenary 3") OBJ_NAME_DEFINE(O_SKIDOO_ARMED, "Black Snowmobile") -OBJ_NAME_DEFINE(O_SKIDMAN, "Black Snowmobile Driver") +OBJ_NAME_DEFINE(O_SKIDOO_DRIVER, "Black Snowmobile Driver") OBJ_NAME_DEFINE(O_MONK_1, "Monk 1") OBJ_NAME_DEFINE(O_MONK_2, "Monk 2") OBJ_NAME_DEFINE(O_FALLING_BLOCK_1, "Falling Block 1") @@ -89,7 +89,7 @@ OBJ_NAME_DEFINE(O_CEILING_SPIKES, "Spiky Ceiling") OBJ_NAME_DEFINE(O_BELL, "Bell") OBJ_NAME_DEFINE(O_WATER_SPRITE, "Boat Wake") OBJ_NAME_DEFINE(O_SNOW_SPRITE, "Snowmobile Wale") -OBJ_NAME_DEFINE(O_SKIDOO_LARA, "Snowmobile Animation") +OBJ_NAME_DEFINE(O_SKIDOO_TRACK, "Snowmobile Track") OBJ_NAME_DEFINE(O_SWITCH_TYPE_AIRLOCK, "Airlock Switch") OBJ_NAME_DEFINE(O_SWITCH_TYPE_SMALL, "Small Switch") OBJ_NAME_DEFINE(O_PROPELLER_2, "Underwater Propeller") diff --git a/src/libtrx/include/libtrx/game/rooms/common.h b/src/libtrx/include/libtrx/game/rooms/common.h index c86e855a0..1a91b0adb 100644 --- a/src/libtrx/include/libtrx/game/rooms/common.h +++ b/src/libtrx/include/libtrx/game/rooms/common.h @@ -7,3 +7,7 @@ extern ROOM *Room_Get(int32_t room_num); extern void Room_FlipMap(void); extern bool Room_GetFlipStatus(void); + +int32_t Room_GetAdjoiningRooms( + int16_t init_room_num, int16_t out_room_nums[], + const int32_t max_room_num_count); diff --git a/src/libtrx/meson.build b/src/libtrx/meson.build index c0bf994fd..a341f262d 100644 --- a/src/libtrx/meson.build +++ b/src/libtrx/meson.build @@ -95,6 +95,7 @@ sources = [ 'game/objects/common.c', 'game/objects/names.c', 'game/objects/vars.c', + 'game/rooms/common.c', 'game/ui/common.c', 'game/ui/events.c', 'game/ui/widgets/console.c', diff --git a/src/tr1/game/lara/control.c b/src/tr1/game/lara/control.c index 7247a9bf2..af1a9eb6b 100644 --- a/src/tr1/game/lara/control.c +++ b/src/tr1/game/lara/control.c @@ -22,8 +22,6 @@ #include #include -#define MAX_BADDIE_COLLISION 12 - static int32_t m_OpenDoorsCheatCooldown = 0; static void M_WaterCurrent(COLL_INFO *coll); @@ -117,22 +115,11 @@ static void M_BaddieCollision(ITEM *lara_item, COLL_INFO *coll) return; } - int16_t numroom = 0; - int16_t roomies[MAX_BADDIE_COLLISION]; - - roomies[numroom++] = lara_item->room_num; - - PORTALS *portals = g_RoomInfo[lara_item->room_num].portals; - if (portals != NULL) { - for (int i = 0; i < portals->count; i++) { - if (numroom >= MAX_BADDIE_COLLISION) { - break; - } - roomies[numroom++] = portals->portal[i].room_num; - } - } + int16_t roomies[12]; + const int32_t roomies_count = + Room_GetAdjoiningRooms(lara_item->room_num, roomies, 12); - for (int i = 0; i < numroom; i++) { + for (int32_t i = 0; i < roomies_count; i++) { int16_t item_num = g_RoomInfo[roomies[i]].item_num; while (item_num != NO_ITEM) { ITEM *item = &g_Items[item_num]; diff --git a/src/tr2/decomp/skidoo.c b/src/tr2/decomp/skidoo.c new file mode 100644 index 000000000..648f79c7a --- /dev/null +++ b/src/tr2/decomp/skidoo.c @@ -0,0 +1,1037 @@ +#include "decomp/skidoo.h" + +#include "game/creature.h" +#include "game/effects.h" +#include "game/gun/gun_misc.h" +#include "game/input.h" +#include "game/items.h" +#include "game/lara/control.h" +#include "game/lara/look.h" +#include "game/math.h" +#include "game/matrix.h" +#include "game/music.h" +#include "game/objects/common.h" +#include "game/objects/vehicles/skidoo_armed.h" +#include "game/output.h" +#include "game/random.h" +#include "game/room.h" +#include "game/sound.h" +#include "global/funcs.h" +#include "global/vars.h" + +#include + +#define SKIDOO_RADIUS 500 +#define SKIDOO_SIDE 260 +#define SKIDOO_FRONT 550 +#define SKIDOO_SNOW 500 +#define SKIDOO_GET_OFF_DIST 330 +#define SKIDOO_TARGET_DIST (WALL_L * 2) // = 2048 + +#define SKIDOO_ACCELERATION 10 +#define SKIDOO_SLOWDOWN 2 + +#define SKIDOO_SLIP 100 +#define SKIDOO_SLIP_SIDE 50 + +#define SKIDOO_MAX_BACK -30 +#define SKIDOO_BRAKE 5 +#define SKIDOO_REVERSE (-5) +#define SKIDOO_UNDO_TURN (PHD_DEGREE * 2) // = 364 +#define SKIDOO_TURN (PHD_DEGREE / 2 + SKIDOO_UNDO_TURN) // = 455 +#define SKIDOO_MOMENTUM_TURN (PHD_DEGREE * 3) // = 546 +#define SKIDOO_MAX_MOMENTUM_TURN (PHD_DEGREE * 150) // = 27300 + +typedef enum { + SKIDOO_GET_ON_NONE = 0, + SKIDOO_GET_ON_LEFT = 1, + SKIDOO_GET_ON_RIGHT = 2, +} SKIDOO_GET_ON_SIDE; + +typedef enum { + // clang-format off + LARA_STATE_SKIDOO_SIT = 0, + LARA_STATE_SKIDOO_GET_ON = 1, + LARA_STATE_SKIDOO_LEFT = 2, + LARA_STATE_SKIDOO_RIGHT = 3, + LARA_STATE_SKIDOO_FALL = 4, + LARA_STATE_SKIDOO_HIT = 5, + LARA_STATE_SKIDOO_GET_ON_L = 6, + LARA_STATE_SKIDOO_GET_OFF_L = 7, + LARA_STATE_SKIDOO_STILL = 8, + LARA_STATE_SKIDOO_GET_OFF_R = 9, + LARA_STATE_SKIDOO_LET_GO = 10, + LARA_STATE_SKIDOO_DEATH = 11, + LARA_STATE_SKIDOO_FALLOFF = 12, + // clang-format on +} LARA_SKIDOO_STATE; + +typedef enum { + // clang-format off + LA_SKIDOO_GET_ON_L = 1, + LA_SKIDOO_FALL = 8, + LA_SKIDOO_HIT_LEFT = 11, + LA_SKIDOO_HIT_RIGHT = 12, + LA_SKIDOO_HIT_FRONT = 13, + LA_SKIDOO_HIT_BACK = 14, + LA_SKIDOO_DEAD = 15, + LA_SKIDOO_GET_ON_R = 18, + // clang-format on +} LARA_ANIM_SKIDOO; + +BITE g_Skidoo_LeftGun = { + .pos = { .x = 219, .y = -71, .z = SKIDOO_FRONT }, + .mesh_num = 0, +}; + +BITE g_Skidoo_RightGun = { + .pos = { .x = -235, .y = -71, .z = SKIDOO_FRONT }, + .mesh_num = 0, +}; + +static bool M_IsNearby(const ITEM *item_1, const ITEM *item_2); +static bool M_IsArmed(const SKIDOO_INFO *const skidoo_data); +static bool M_CheckBaddieCollision(ITEM *item, ITEM *skidoo); + +static bool M_IsNearby(const ITEM *const item_1, const ITEM *const item_2) +{ + const int32_t dx = item_1->pos.x - item_2->pos.x; + const int32_t dy = item_1->pos.y - item_2->pos.y; + const int32_t dz = item_1->pos.z - item_2->pos.z; + return dx > -SKIDOO_TARGET_DIST && dx < SKIDOO_TARGET_DIST + && dy > -SKIDOO_TARGET_DIST && dy < SKIDOO_TARGET_DIST + && dz > -SKIDOO_TARGET_DIST && dz < SKIDOO_TARGET_DIST; +} + +static bool M_IsArmed(const SKIDOO_INFO *const skidoo_data) +{ + return skidoo_data->track_mesh & SKIDOO_GUN_MESH; +} + +static bool M_CheckBaddieCollision(ITEM *const item, ITEM *const skidoo) +{ + if (!item->collidable || item->status == IS_INVISIBLE || item == g_LaraItem + || item == skidoo) { + return false; + } + + const OBJECT *const object = Object_GetObject(item->object_id); + const bool is_availanche = item->object_id == O_ROLLING_BALL_2; + if (object->collision == NULL || (!object->intelligent && !is_availanche)) { + return false; + } + + if (!M_IsNearby(item, skidoo)) { + return false; + } + + if (!Item_TestBoundsCollide(item, skidoo, SKIDOO_RADIUS)) { + return false; + } + + if (item->object_id == O_SKIDOO_ARMED) { + SkidooArmed_Push(item, skidoo, SKIDOO_RADIUS); + } else if (is_availanche) { + if (item->current_anim_state == TRAP_ACTIVATE) { + Lara_TakeDamage(100, true); + } + } else { + DoLotsOfBlood( + item->pos.x, skidoo->pos.y - STEP_L, item->pos.z, skidoo->speed, + skidoo->rot.y, item->room_num, 3); + item->hit_points = 0; + } + return true; +} + +void __cdecl Skidoo_Initialise(const int16_t item_num) +{ + ITEM *const item = &g_Items[item_num]; + + SKIDOO_INFO *const skidoo_data = + game_malloc(sizeof(SKIDOO_INFO), GBUF_SKIDOO_STUFF); + skidoo_data->skidoo_turn = 0; + skidoo_data->right_fallspeed = 0; + skidoo_data->left_fallspeed = 0; + skidoo_data->extra_rotation = 0; + skidoo_data->momentum_angle = item->rot.y; + skidoo_data->track_mesh = 0; + skidoo_data->pitch = 0; + + item->data = skidoo_data; +} + +int32_t __cdecl Skidoo_CheckGetOn(const int16_t item_num, COLL_INFO *const coll) +{ + if (!(g_Input & IN_ACTION) || g_Lara.gun_status != LGS_ARMLESS + || g_LaraItem->gravity) { + return SKIDOO_GET_ON_NONE; + } + + ITEM *const item = &g_Items[item_num]; + const int16_t angle = item->rot.y - g_LaraItem->rot.y; + + SKIDOO_GET_ON_SIDE get_on = SKIDOO_GET_ON_NONE; + if (angle > PHD_45 && angle < PHD_135) { + get_on = SKIDOO_GET_ON_LEFT; + } else if (angle > -PHD_135 && angle < -PHD_45) { + get_on = SKIDOO_GET_ON_RIGHT; + } + + if (!Item_TestBoundsCollide(item, g_LaraItem, coll->radius)) { + return SKIDOO_GET_ON_NONE; + } + + if (!Collide_TestCollision(item, g_LaraItem)) { + return SKIDOO_GET_ON_NONE; + } + + int16_t room_num = item->room_num; + const SECTOR *const sector = + Room_GetSector(item->pos.x, item->pos.y, item->pos.z, &room_num); + const int32_t height = + Room_GetHeight(sector, item->pos.x, item->pos.y, item->pos.z); + if (height < -32000) { + return SKIDOO_GET_ON_NONE; + } + + return get_on; +} + +void __cdecl Skidoo_Collision( + const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll) +{ + if (lara_item->hit_points < 0 || g_Lara.skidoo != NO_ITEM) { + return; + } + + const SKIDOO_GET_ON_SIDE get_on = Skidoo_CheckGetOn(item_num, coll); + if (get_on == SKIDOO_GET_ON_NONE) { + Object_Collision(item_num, lara_item, coll); + return; + } + + g_Lara.skidoo = item_num; + if (g_Lara.gun_type == LGT_FLARE) { + Flare_Create(false); + Flare_UndrawMeshes(); + g_Lara.flare_control_left = 0; + g_Lara.gun_type = LGT_UNARMED; + g_Lara.request_gun_type = LGT_UNARMED; + } + if (get_on == SKIDOO_GET_ON_LEFT) { + lara_item->anim_num = + g_Objects[O_LARA_SKIDOO].anim_idx + LA_SKIDOO_GET_ON_L; + } else if (get_on == SKIDOO_GET_ON_RIGHT) { + lara_item->anim_num = + g_Objects[O_LARA_SKIDOO].anim_idx + LA_SKIDOO_GET_ON_R; + } + lara_item->current_anim_state = LARA_STATE_SKIDOO_GET_ON; + lara_item->frame_num = g_Anims[lara_item->anim_num].frame_base; + g_Lara.gun_status = LGS_ARMLESS; + + ITEM *const item = &g_Items[item_num]; + lara_item->pos.x = item->pos.x; + lara_item->pos.y = item->pos.y; + lara_item->pos.z = item->pos.z; + lara_item->rot.y = item->rot.y; + item->hit_points = 1; +} + +void __cdecl Skidoo_BaddieCollision(ITEM *const skidoo) +{ + int16_t roomies[12]; + const int32_t roomies_count = + Room_GetAdjoiningRooms(skidoo->room_num, roomies, 12); + + for (int32_t i = 0; i < roomies_count; i++) { + const ROOM *const room = Room_Get(roomies[i]); + int16_t item_num = room->item_num; + while (item_num != NO_ITEM) { + ITEM *item = Item_Get(item_num); + M_CheckBaddieCollision(item, skidoo); + item_num = item->next_item; + } + } +} + +int32_t __cdecl Skidoo_TestHeight( + const ITEM *const item, const int32_t z_off, const int32_t x_off, + XYZ_32 *const out_pos) +{ + const int32_t sx = Math_Sin(item->rot.x); + const int32_t sz = Math_Sin(item->rot.z); + const int32_t cy = Math_Cos(item->rot.y); + const int32_t sy = Math_Sin(item->rot.y); + out_pos->x = item->pos.x + ((x_off * cy + z_off * sy) >> W2V_SHIFT); + out_pos->y = item->pos.y + ((x_off * sz - z_off * sx) >> W2V_SHIFT); + out_pos->z = item->pos.z + ((z_off * cy - x_off * sy) >> W2V_SHIFT); + int16_t room_num = item->room_num; + const SECTOR *const sector = + Room_GetSector(out_pos->x, out_pos->y, out_pos->z, &room_num); + return Room_GetHeight(sector, out_pos->x, out_pos->y, out_pos->z); +} + +void __cdecl Skidoo_DoSnowEffect(ITEM *const skidoo) +{ + const int16_t fx_num = Effect_Create(skidoo->room_num); + if (fx_num == NO_ITEM) { + return; + } + + const int32_t sx = Math_Sin(skidoo->rot.x); + const int32_t sy = Math_Sin(skidoo->rot.y); + const int32_t cy = Math_Cos(skidoo->rot.y); + const int32_t x = (SKIDOO_SIDE * (Random_GetDraw() - 0x4000)) >> 14; + FX *const fx = &g_Effects[fx_num]; + fx->pos.x = skidoo->pos.x - ((sy * SKIDOO_SNOW + cy * x) >> W2V_SHIFT); + fx->pos.y = skidoo->pos.y + ((sx * SKIDOO_SNOW) >> W2V_SHIFT); + fx->pos.z = skidoo->pos.z - ((cy * SKIDOO_SNOW - sy * x) >> W2V_SHIFT); + fx->room_num = skidoo->room_num; + fx->object_id = O_SNOW_SPRITE; + fx->frame_num = 0; + fx->speed = 0; + if (skidoo->speed < 64) { + fx->fall_speed = (Random_GetDraw() * ABS(skidoo->speed)) >> 15; + } else { + fx->fall_speed = 0; + } + + g_MatrixPtr->_23 = 0; + + S_CalculateLight(fx->pos.x, fx->pos.y, fx->pos.z, fx->room_num); + fx->shade = g_LsAdder - 512; + CLAMPL(fx->shade, 0); +} + +int32_t __cdecl Skidoo_Dynamics(ITEM *const skidoo) +{ + SKIDOO_INFO *const skidoo_data = skidoo->data; + + XYZ_32 fl_old; + XYZ_32 bl_old; + XYZ_32 br_old; + XYZ_32 fr_old; + int32_t hfl_old = + Skidoo_TestHeight(skidoo, SKIDOO_FRONT, -SKIDOO_SIDE, &fl_old); + int32_t hfr_old = + Skidoo_TestHeight(skidoo, SKIDOO_FRONT, SKIDOO_SIDE, &fr_old); + int32_t hbl_old = + Skidoo_TestHeight(skidoo, -SKIDOO_FRONT, -SKIDOO_SIDE, &bl_old); + int32_t hbr_old = + Skidoo_TestHeight(skidoo, -SKIDOO_FRONT, SKIDOO_SIDE, &br_old); + + XYZ_32 old = { + .z = skidoo->pos.z, + .x = skidoo->pos.x, + .y = skidoo->pos.y, + }; + + CLAMPG(bl_old.y, hbl_old); + CLAMPG(br_old.y, hbr_old); + CLAMPG(fl_old.y, hfl_old); + CLAMPG(fr_old.y, hfr_old); + + if (skidoo->pos.y <= skidoo->floor - STEP_L) { + skidoo->rot.y += skidoo_data->extra_rotation + skidoo_data->skidoo_turn; + } else { + if (skidoo_data->skidoo_turn < -SKIDOO_UNDO_TURN) { + skidoo_data->skidoo_turn += SKIDOO_UNDO_TURN; + } else if (skidoo_data->skidoo_turn > SKIDOO_UNDO_TURN) { + skidoo_data->skidoo_turn -= SKIDOO_UNDO_TURN; + } else { + skidoo_data->skidoo_turn = 0; + } + skidoo->rot.y += skidoo_data->skidoo_turn + skidoo_data->extra_rotation; + + int16_t rot = skidoo->rot.y - skidoo_data->momentum_angle; + if (rot < -SKIDOO_MOMENTUM_TURN) { + if (rot < -SKIDOO_MAX_MOMENTUM_TURN) { + rot = -SKIDOO_MAX_MOMENTUM_TURN; + skidoo_data->momentum_angle = skidoo->rot.y - rot; + } else { + skidoo_data->momentum_angle -= SKIDOO_MOMENTUM_TURN; + } + } else if (rot > SKIDOO_MOMENTUM_TURN) { + if (rot > SKIDOO_MAX_MOMENTUM_TURN) { + rot = SKIDOO_MAX_MOMENTUM_TURN; + skidoo_data->momentum_angle = skidoo->rot.y - rot; + } else { + skidoo_data->momentum_angle += SKIDOO_MOMENTUM_TURN; + } + } else { + skidoo_data->momentum_angle = skidoo->rot.y; + } + } + + skidoo->pos.z += + (skidoo->speed * Math_Cos(skidoo_data->momentum_angle)) >> W2V_SHIFT; + skidoo->pos.x += + (skidoo->speed * Math_Sin(skidoo_data->momentum_angle)) >> W2V_SHIFT; + + int32_t slip; + slip = (SKIDOO_SLIP * Math_Sin(skidoo->rot.x)) >> W2V_SHIFT; + if (ABS(slip) > SKIDOO_SLIP / 2) { + skidoo->pos.z -= (slip * Math_Cos(skidoo->rot.y)) >> W2V_SHIFT; + skidoo->pos.x -= (slip * Math_Sin(skidoo->rot.y)) >> W2V_SHIFT; + } + + slip = (SKIDOO_SLIP_SIDE * Math_Sin(skidoo->rot.z)) >> W2V_SHIFT; + if (ABS(slip) > SKIDOO_SLIP_SIDE / 2) { + skidoo->pos.z -= (slip * Math_Sin(skidoo->rot.y)) >> W2V_SHIFT; + skidoo->pos.x += (slip * Math_Cos(skidoo->rot.y)) >> W2V_SHIFT; + } + + XYZ_32 moved = { + .x = skidoo->pos.x, + .z = skidoo->pos.z, + }; + if (!(skidoo->flags & IF_ONE_SHOT)) { + Skidoo_BaddieCollision(skidoo); + } + + int32_t rot = 0; + + XYZ_32 br; + XYZ_32 fl; + XYZ_32 bl; + XYZ_32 fr; + const int32_t hbl = + Skidoo_TestHeight(skidoo, -SKIDOO_FRONT, -SKIDOO_SIDE, &bl); + if (hbl < bl_old.y - STEP_L) { + rot = DoShift(skidoo, &bl, &bl_old); + } + const int32_t hbr = + Skidoo_TestHeight(skidoo, -SKIDOO_FRONT, SKIDOO_SIDE, &br); + if (hbr < br_old.y - STEP_L) { + rot += DoShift(skidoo, &br, &br_old); + } + const int32_t hfl = + Skidoo_TestHeight(skidoo, SKIDOO_FRONT, -SKIDOO_SIDE, &fl); + if (hfl < fl_old.y - STEP_L) { + rot += DoShift(skidoo, &fl, &fl_old); + } + const int32_t hfr = + Skidoo_TestHeight(skidoo, SKIDOO_FRONT, SKIDOO_SIDE, &fr); + if (hfr < fr_old.y - STEP_L) { + rot += DoShift(skidoo, &fr, &fr_old); + } + + int16_t room_num = skidoo->room_num; + const SECTOR *const sector = + Room_GetSector(skidoo->pos.x, skidoo->pos.y, skidoo->pos.z, &room_num); + const int32_t height = + Room_GetHeight(sector, skidoo->pos.x, skidoo->pos.y, skidoo->pos.z); + if (height < skidoo->pos.y - STEP_L) { + DoShift(skidoo, &skidoo->pos, &old); + } + + skidoo_data->extra_rotation = rot; + + int32_t collide = GetCollisionAnim(skidoo, &moved); + if (collide != 0) { + const int32_t c = Math_Cos(skidoo_data->momentum_angle); + const int32_t s = Math_Sin(skidoo_data->momentum_angle); + const int32_t dx = skidoo->pos.x - old.x; + const int32_t dz = skidoo->pos.z - old.z; + const int32_t new_speed = (s * dx + c * dz) >> W2V_SHIFT; + + if (skidoo == Item_Get(g_Lara.skidoo) + && skidoo->speed > SKIDOO_MAX_SPEED + SKIDOO_ACCELERATION + && new_speed < skidoo->speed - SKIDOO_ACCELERATION) { + Lara_TakeDamage((skidoo->speed - new_speed) / 2, true); + } + + if (skidoo->speed > 0 && new_speed < skidoo->speed) { + skidoo->speed = new_speed < 0 ? 0 : new_speed; + } else if (skidoo->speed < 0 && new_speed > skidoo->speed) { + skidoo->speed = new_speed > 0 ? 0 : new_speed; + } + + if (skidoo->speed < SKIDOO_MAX_BACK) { + skidoo->speed = SKIDOO_MAX_BACK; + } + } + + return collide; +} + +int32_t __cdecl Skidoo_UserControl( + ITEM *const skidoo, const int32_t height, int32_t *const out_pitch) +{ + SKIDOO_INFO *const skidoo_data = skidoo->data; + + bool drive = false; + + if (skidoo->pos.y >= height - STEP_L) { + *out_pitch = skidoo->speed + (height - skidoo->pos.y); + + if (skidoo->speed == 0 && (g_Input & IN_LOOK)) { + Lara_LookUpDown(); + } + + if (((g_Input & IN_LEFT) && !(g_Input & IN_BACK)) + || ((g_Input & IN_RIGHT) && (g_Input & IN_BACK))) { + skidoo_data->skidoo_turn -= SKIDOO_TURN; + CLAMPL(skidoo_data->skidoo_turn, -SKIDOO_MAX_TURN); + } + + if (((g_Input & IN_RIGHT) && !(g_Input & IN_BACK)) + || ((g_Input & IN_LEFT) && (g_Input & IN_BACK))) { + skidoo_data->skidoo_turn += SKIDOO_TURN; + CLAMPG(skidoo_data->skidoo_turn, SKIDOO_MAX_TURN); + } + + if (g_Input & IN_BACK) { + if (skidoo->speed > 0) { + skidoo->speed -= SKIDOO_BRAKE; + } else { + if (skidoo->speed > SKIDOO_MAX_BACK) { + skidoo->speed += SKIDOO_REVERSE; + } + drive = true; + } + } else if (g_Input & IN_FORWARD) { + int32_t max_speed; + if ((g_Input & IN_ACTION) && !M_IsArmed(skidoo_data)) { + max_speed = SKIDOO_FAST_SPEED; + } else if (g_Input & IN_SLOW) { + max_speed = SKIDOO_SLOW_SPEED; + } else { + max_speed = SKIDOO_MAX_SPEED; + } + + if (skidoo->speed < max_speed) { + skidoo->speed += + SKIDOO_ACCELERATION * skidoo->speed / (2 * max_speed) + + SKIDOO_ACCELERATION / 2; + } else if (skidoo->speed > max_speed + SKIDOO_SLOWDOWN) { + skidoo->speed -= SKIDOO_SLOWDOWN; + } + + drive = true; + } else if ( + skidoo->speed >= 0 && skidoo->speed < SKIDOO_MIN_SPEED + && (g_Input & (IN_LEFT | IN_RIGHT))) { + skidoo->speed = SKIDOO_MIN_SPEED; + drive = true; + } else if (skidoo->speed > SKIDOO_SLOWDOWN) { + skidoo->speed -= SKIDOO_SLOWDOWN; + if ((Random_GetDraw() & 0x7F) < skidoo->speed) { + drive = true; + } + } else { + skidoo->speed = 0; + } + } else if (g_Input & (IN_FORWARD | IN_BACK)) { + drive = true; + *out_pitch = skidoo_data->pitch + 50; + } + + return drive; +} + +int32_t __cdecl Skidoo_CheckGetOffOK(int32_t direction) +{ + ITEM *const skidoo = Item_Get(g_Lara.skidoo); + + int16_t rot; + if (direction == LARA_STATE_SKIDOO_GET_OFF_L) { + rot = skidoo->rot.y + PHD_90; + } else { + rot = skidoo->rot.y - PHD_90; + } + + const int32_t c = Math_Cos(rot); + const int32_t s = Math_Sin(rot); + const int32_t x = skidoo->pos.x - ((SKIDOO_GET_OFF_DIST * s) >> W2V_SHIFT); + const int32_t z = skidoo->pos.z - ((SKIDOO_GET_OFF_DIST * c) >> W2V_SHIFT); + const int32_t y = skidoo->pos.y; + + int16_t room_num = skidoo->room_num; + const SECTOR *const sector = Room_GetSector(x, y, z, &room_num); + const int32_t height = Room_GetHeight(sector, x, y, z); + + if (g_HeightType == HT_BIG_SLOPE || height == NO_HEIGHT) { + return false; + } + + if (ABS(height - skidoo->pos.y) > WALL_L / 2) { + return false; + } + + const int32_t ceiling = Room_GetCeiling(sector, x, y, z); + if (ceiling - skidoo->pos.y > -LARA_HEIGHT) { + return false; + } + if (height - ceiling < LARA_HEIGHT) { + return false; + } + + return true; +} + +void __cdecl Skidoo_Animation( + ITEM *const skidoo, const int32_t collide, const int32_t dead) +{ + const SKIDOO_INFO *const skidoo_data = skidoo->data; + + if (skidoo->pos.y != skidoo->floor && skidoo->fall_speed > 0 + && g_LaraItem->current_anim_state != LARA_STATE_SKIDOO_FALL && !dead) { + g_LaraItem->anim_num = + g_Objects[O_LARA_SKIDOO].anim_idx + LA_SKIDOO_FALL; + g_LaraItem->frame_num = g_Anims[g_LaraItem->anim_num].frame_base; + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_FALL; + g_LaraItem->current_anim_state = LARA_STATE_SKIDOO_FALL; + return; + } + + if (collide != 0 && !dead + && g_LaraItem->current_anim_state != LARA_STATE_SKIDOO_FALL) { + if (g_LaraItem->current_anim_state != LARA_STATE_SKIDOO_HIT) { + if (collide == LA_SKIDOO_HIT_FRONT) { + Sound_Effect(SFX_CLATTER_2, &skidoo->pos, SPM_NORMAL); + } else { + Sound_Effect(SFX_CLATTER_1, &skidoo->pos, SPM_NORMAL); + } + g_LaraItem->anim_num = g_Objects[O_LARA_SKIDOO].anim_idx + collide; + g_LaraItem->frame_num = g_Anims[g_LaraItem->anim_num].frame_base; + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_HIT; + g_LaraItem->current_anim_state = LARA_STATE_SKIDOO_HIT; + } + return; + } + + switch (g_LaraItem->current_anim_state) { + case LARA_STATE_SKIDOO_SIT: + if (skidoo->speed == 0) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_STILL; + } + if (dead) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_FALLOFF; + } else if (g_Input & IN_LEFT) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_LEFT; + } else if (g_Input & IN_RIGHT) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_RIGHT; + } + break; + + case LARA_STATE_SKIDOO_LEFT: + if (!(g_Input & IN_LEFT)) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_SIT; + } + break; + + case LARA_STATE_SKIDOO_RIGHT: + if (!(g_Input & IN_RIGHT)) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_SIT; + } + break; + + case LARA_STATE_SKIDOO_FALL: + if (skidoo->fall_speed <= 0 || skidoo_data->left_fallspeed <= 0 + || skidoo_data->right_fallspeed <= 0) { + Sound_Effect(SFX_CLATTER_3, &skidoo->pos, SPM_NORMAL); + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_SIT; + } else if (skidoo->fall_speed > DAMAGE_START + DAMAGE_LENGTH) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_LET_GO; + } + break; + + case LARA_STATE_SKIDOO_STILL: { + const int32_t music_track = + M_IsArmed(skidoo_data) ? MX_BATTLE_THEME : MX_SKIDOO_THEME; + if (!(g_MusicTrackFlags[music_track] & IF_ONE_SHOT)) { + Music_Play(music_track, false); + g_LaraItem = g_LaraItem; + g_MusicTrackFlags[music_track] |= IF_ONE_SHOT; + } + + if (dead) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_DEATH; + return; + } + + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_STILL; + + if (g_Input & IN_JUMP) { + if ((g_Input & IN_RIGHT) + && Skidoo_CheckGetOffOK(LARA_STATE_SKIDOO_GET_OFF_R)) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_GET_OFF_R; + skidoo->speed = 0; + } else if ( + (g_Input & IN_LEFT) + && Skidoo_CheckGetOffOK(LARA_STATE_SKIDOO_GET_OFF_L)) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_GET_OFF_L; + skidoo->speed = 0; + } + } else if (g_Input & IN_LEFT) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_LEFT; + } else if (g_Input & IN_RIGHT) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_RIGHT; + } else if (g_Input & (IN_BACK | IN_FORWARD)) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_SIT; + } + break; + } + + default: + break; + } +} + +void __cdecl Skidoo_Explode(const ITEM *const skidoo) +{ + const int16_t fx_num = Effect_Create(skidoo->room_num); + if (fx_num != NO_ITEM) { + FX *const fx = &g_Effects[fx_num]; + fx->pos.x = skidoo->pos.x; + fx->pos.y = skidoo->pos.y; + fx->pos.z = skidoo->pos.z; + fx->speed = 0; + fx->frame_num = 0; + fx->counter = 0; + fx->object_id = O_EXPLOSION; + } + + Effect_ExplodingDeath(g_Lara.skidoo, ~(SKIDOO_GUN_MESH - 1), 0); + Sound_Effect(SFX_EXPLOSION1, NULL, SPM_NORMAL); + g_Lara.skidoo = NO_ITEM; +} + +int32_t __cdecl Skidoo_CheckGetOff(void) +{ + ITEM *const skidoo = &g_Items[g_Lara.skidoo]; + + if ((g_LaraItem->current_anim_state == LARA_STATE_SKIDOO_GET_OFF_R + || g_LaraItem->current_anim_state == LARA_STATE_SKIDOO_GET_OFF_L) + && g_LaraItem->frame_num == g_Anims[g_LaraItem->anim_num].frame_end) { + if (g_LaraItem->current_anim_state == LARA_STATE_SKIDOO_GET_OFF_L) { + g_LaraItem->rot.y += PHD_90; + } else { + g_LaraItem->rot.y -= PHD_90; + } + g_LaraItem->anim_num = LA_STAND_STILL; + g_LaraItem->frame_num = g_Anims[LA_STAND_STILL].frame_base; + g_LaraItem->goal_anim_state = LS_STOP; + g_LaraItem->current_anim_state = LS_STOP; + g_LaraItem->pos.x -= + (SKIDOO_GET_OFF_DIST * Math_Sin(g_LaraItem->rot.y)) >> W2V_SHIFT; + g_LaraItem->pos.z -= + (SKIDOO_GET_OFF_DIST * Math_Cos(g_LaraItem->rot.y)) >> W2V_SHIFT; + g_LaraItem->rot.x = 0; + g_LaraItem->rot.z = 0; + g_Lara.skidoo = NO_ITEM; + g_Lara.gun_status = LGS_ARMLESS; + return true; + } + + if (g_LaraItem->current_anim_state == LARA_STATE_SKIDOO_LET_GO + && (skidoo->pos.y == skidoo->floor + || g_LaraItem->frame_num + == g_Anims[g_LaraItem->anim_num].frame_end)) { + g_LaraItem->anim_num = LA_FREEFALL; + g_LaraItem->frame_num = g_Anims[g_LaraItem->anim_num].frame_base; + g_LaraItem->current_anim_state = LARA_STATE_SKIDOO_GET_OFF_R; + if (skidoo->pos.y == skidoo->floor) { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_STILL; + g_LaraItem->fall_speed = DAMAGE_START + DAMAGE_LENGTH; + g_LaraItem->speed = 0; + Skidoo_Explode(skidoo); + } else { + g_LaraItem->goal_anim_state = LARA_STATE_SKIDOO_GET_OFF_R; + g_LaraItem->pos.y -= 200; + g_LaraItem->fall_speed = skidoo->fall_speed; + g_LaraItem->speed = skidoo->speed; + Sound_Effect(SFX_LARA_FALL, &g_LaraItem->pos, SPM_NORMAL); + } + g_LaraItem->rot.x = 0; + g_LaraItem->rot.z = 0; + g_LaraItem->gravity = 1; + g_Lara.gun_status = LGS_ARMLESS; + g_Lara.move_angle = skidoo->rot.y; + skidoo->flags |= IF_ONE_SHOT; + skidoo->collidable = 0; + return false; + } + + return true; +} + +void __cdecl Skidoo_Guns(void) +{ + WEAPON_INFO *const winfo = &g_Weapons[LGT_SKIDOO]; + Gun_GetNewTarget(winfo); + Gun_AimWeapon(winfo, &g_Lara.right_arm); + + if (!(g_Input & IN_ACTION)) { + return; + } + + int16_t angles[2]; + angles[0] = g_Lara.right_arm.rot.y + g_LaraItem->rot.y; + angles[1] = g_Lara.right_arm.rot.x; + + if (!Gun_FireWeapon(LGT_SKIDOO, g_Lara.target, g_LaraItem, angles)) { + return; + } + + g_Lara.right_arm.flash_gun = winfo->flash_time; + Sound_Effect(winfo->sample_num, &g_LaraItem->pos, SPM_NORMAL); + + const int32_t cy = Math_Cos(g_LaraItem->rot.y); + const int32_t sy = Math_Sin(g_LaraItem->rot.y); + const int32_t x = g_LaraItem->pos.x + (sy >> 4); + const int32_t z = g_LaraItem->pos.z + (cy >> 4); + const int32_t y = g_LaraItem->pos.y - 512; + AddDynamicLight(x, y, z, 12, 11); + + ITEM *const skidoo = Item_Get(g_Lara.skidoo); + Creature_Effect(skidoo, &g_Skidoo_LeftGun, GunShot); + Creature_Effect(skidoo, &g_Skidoo_RightGun, GunShot); +} + +int32_t __cdecl Skidoo_Control(void) +{ + ITEM *const skidoo = Item_Get(g_Lara.skidoo); + SKIDOO_INFO *const skidoo_data = skidoo->data; + int32_t collide = Skidoo_Dynamics(skidoo); + + XYZ_32 fl; + XYZ_32 fr; + const int32_t hfl = + Skidoo_TestHeight(skidoo, SKIDOO_FRONT, -SKIDOO_SIDE, &fl); + const int32_t hfr = + Skidoo_TestHeight(skidoo, SKIDOO_FRONT, SKIDOO_SIDE, &fr); + + int16_t room_num = skidoo->room_num; + const SECTOR *const sector = + Room_GetSector(skidoo->pos.x, skidoo->pos.y, skidoo->pos.z, &room_num); + int32_t height = + Room_GetHeight(sector, skidoo->pos.x, skidoo->pos.y, skidoo->pos.z); + Room_TestTriggers(g_TriggerIndex, false); + + bool dead = false; + if (g_LaraItem->hit_points <= 0) { + dead = true; + g_Input &= ~IN_BACK; + g_Input &= ~IN_FORWARD; + g_Input &= ~IN_LEFT; + g_Input &= ~IN_RIGHT; + } else if (g_LaraItem->current_anim_state == LARA_STATE_SKIDOO_LET_GO) { + dead = true; + collide = 0; + } + + int32_t drive; + int32_t pitch; + if (skidoo->flags & IF_ONE_SHOT) { + drive = 0; + collide = 0; + } else { + switch (g_LaraItem->current_anim_state) { + case LARA_STATE_SKIDOO_GET_ON: + case LARA_STATE_SKIDOO_GET_OFF_L: + case LARA_STATE_SKIDOO_GET_OFF_R: + case LARA_STATE_SKIDOO_LET_GO: + drive = -1; + collide = 0; + break; + + default: + drive = Skidoo_UserControl(skidoo, height, &pitch); + break; + } + } + + const int32_t old_track_mesh = skidoo_data->track_mesh; + if (drive > 0) { + skidoo_data->track_mesh = (skidoo_data->track_mesh & 3) == 1 ? 2 : 1; + skidoo_data->pitch += (pitch - skidoo_data->pitch) >> 2; + + const int32_t pitch_delta = + (SKIDOO_MAX_SPEED - skidoo_data->pitch) * 100; + const int32_t pitch = (SOUND_DEFAULT_PITCH - pitch_delta) << 8; + + Sound_Effect(SFX_SKIDOO_MOVING, &skidoo->pos, SPM_PITCH | pitch); + } else { + skidoo_data->track_mesh = 0; + if (!drive) { + Sound_Effect(SFX_SKIDOO_IDLE, &skidoo->pos, SPM_NORMAL); + } + skidoo_data->pitch = 0; + } + skidoo_data->track_mesh |= old_track_mesh & SKIDOO_GUN_MESH; + + skidoo->floor = height; + + skidoo_data->left_fallspeed = + DoDynamics(hfl, skidoo_data->left_fallspeed, &fl.y); + skidoo_data->right_fallspeed = + DoDynamics(hfr, skidoo_data->right_fallspeed, &fr.y); + skidoo->fall_speed = DoDynamics(height, skidoo->fall_speed, &skidoo->pos.y); + + height = (fr.y + fl.y) / 2; + const int16_t x_rot = Math_Atan(SKIDOO_FRONT, skidoo->pos.y - height); + const int16_t z_rot = Math_Atan(SKIDOO_SIDE, height - fl.y); + skidoo->rot.x += (x_rot - skidoo->rot.x) >> 1; + skidoo->rot.z += (z_rot - skidoo->rot.z) >> 1; + + if (skidoo->flags & IF_ONE_SHOT) { + if (room_num != skidoo->room_num) { + Item_NewRoom(g_Lara.skidoo, room_num); + } + if (skidoo->pos.y == skidoo->floor) { + Skidoo_Explode(skidoo); + } + return 0; + } + + Skidoo_Animation(skidoo, collide, dead); + if (room_num != skidoo->room_num) { + Item_NewRoom(g_Lara.skidoo, room_num); + Item_NewRoom(g_Lara.item_num, room_num); + } + + if (g_LaraItem->current_anim_state == LARA_STATE_SKIDOO_FALLOFF) { + g_LaraItem->rot.x = 0; + g_LaraItem->rot.z = 0; + } else { + g_LaraItem->pos.x = skidoo->pos.x; + g_LaraItem->pos.y = skidoo->pos.y; + g_LaraItem->pos.z = skidoo->pos.z; + g_LaraItem->rot.y = skidoo->rot.y; + if (drive >= 0) { + g_LaraItem->rot.x = skidoo->rot.x; + g_LaraItem->rot.z = skidoo->rot.z; + } else { + g_LaraItem->rot.x = 0; + g_LaraItem->rot.z = 0; + } + } + + Item_Animate(g_LaraItem); + if (!dead && drive >= 0 && M_IsArmed(skidoo_data)) { + Skidoo_Guns(); + } + + if (dead) { + skidoo->anim_num = g_Objects[O_SKIDOO_FAST].anim_idx + LA_SKIDOO_DEAD; + skidoo->frame_num = g_Anims[skidoo->anim_num].frame_base; + } else { + skidoo->anim_num = g_Objects[O_SKIDOO_FAST].anim_idx + + g_LaraItem->anim_num - g_Objects[O_LARA_SKIDOO].anim_idx; + skidoo->frame_num = g_LaraItem->frame_num + + g_Anims[skidoo->anim_num].frame_base + - g_Anims[g_LaraItem->anim_num].frame_base; + } + + if (skidoo->speed != 0 && skidoo->floor == skidoo->pos.y) { + Skidoo_DoSnowEffect(skidoo); + if (skidoo->speed < SKIDOO_SLOW_SPEED) { + Skidoo_DoSnowEffect(skidoo); + } + } + + return Skidoo_CheckGetOff(); +} + +void __cdecl Skidoo_Draw(const ITEM *const item) +{ + int32_t track_mesh = 0; + const SKIDOO_INFO *const skidoo_data = item->data; + if (skidoo_data != NULL) { + track_mesh = skidoo_data->track_mesh; + } + + OBJECT *obj = &g_Objects[item->object_id]; + if ((track_mesh & SKIDOO_GUN_MESH) != 0) { + obj = &g_Objects[O_SKIDOO_ARMED]; + } + + int16_t *track_mesh_ptr = NULL; + if ((track_mesh & 3) == 1) { + track_mesh_ptr = g_Meshes[g_Objects[O_SKIDOO_TRACK].mesh_idx + 1]; + } else if ((track_mesh & 3) == 2) { + track_mesh_ptr = g_Meshes[g_Objects[O_SKIDOO_TRACK].mesh_idx + 7]; + } + int16_t **mesh_ptrs = &g_Meshes[obj->mesh_idx]; + + int16_t *old_track_mesh_ptr = mesh_ptrs[1]; + if (track_mesh_ptr != NULL) { + mesh_ptrs[1] = track_mesh_ptr; + } + + // TODO: merge common code parts down below with Object_DrawAnimatingItem. + + FRAME_INFO *frames[2]; + int32_t rate; + const int32_t frac = Item_GetFrames(item, frames, &rate); + + Matrix_Push(); + Matrix_TranslateAbs(item->pos.x, item->pos.y, item->pos.z); + Matrix_RotYXZ(item->rot.y, item->rot.x, item->rot.z); + + const int32_t clip = S_GetObjectBounds(&frames[0]->bounds); + if (!clip) { + Matrix_Pop(); + return; + } + + Output_CalculateObjectLighting(item, frames[0]); + + int32_t *bone = &g_AnimBones[obj->bone_idx]; + if (frac) { + const int16_t *mesh_rots_1 = frames[0]->mesh_rots; + const int16_t *mesh_rots_2 = frames[1]->mesh_rots; + + Matrix_InitInterpolate(frac, rate); + Matrix_TranslateRel_ID( + frames[0]->offset.x, frames[0]->offset.y, frames[0]->offset.z, + frames[1]->offset.x, frames[1]->offset.y, frames[1]->offset.z); + Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0); + + Output_InsertPolygons_I(mesh_ptrs[0], clip); + for (int32_t mesh_idx = 1; mesh_idx < obj->mesh_count; mesh_idx++) { + const int32_t bone_flags = bone[0]; + if (bone_flags & BF_MATRIX_POP) { + Matrix_Pop_I(); + } + if (bone_flags & BF_MATRIX_PUSH) { + Matrix_Push_I(); + } + + Matrix_TranslateRel_I(bone[1], bone[2], bone[3]); + Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0); + bone += 4; + + Output_InsertPolygons_I(mesh_ptrs[mesh_idx], clip); + } + } else { + const int16_t *mesh_rots = frames[0]->mesh_rots; + Matrix_TranslateRel( + frames[0]->offset.x, frames[0]->offset.y, frames[0]->offset.z); + Matrix_RotYXZsuperpack(&mesh_rots, 0); + + Output_InsertPolygons(mesh_ptrs[0], clip); + for (int32_t mesh_idx = 1; mesh_idx < obj->mesh_count; mesh_idx++) { + const int32_t bone_flags = bone[0]; + if (bone_flags & BF_MATRIX_POP) { + Matrix_Pop(); + } + if (bone_flags & BF_MATRIX_PUSH) { + Matrix_Push(); + } + + Matrix_TranslateRel(bone[1], bone[2], bone[3]); + Matrix_RotYXZsuperpack(&mesh_rots, 0); + bone += 4; + + Output_InsertPolygons(mesh_ptrs[mesh_idx], clip); + } + } + + Matrix_Pop(); + + mesh_ptrs[1] = old_track_mesh_ptr; +} diff --git a/src/tr2/decomp/skidoo.h b/src/tr2/decomp/skidoo.h new file mode 100644 index 000000000..077745448 --- /dev/null +++ b/src/tr2/decomp/skidoo.h @@ -0,0 +1,34 @@ +#pragma once + +#include "global/const.h" +#include "global/types.h" + +#define SKIDOO_MIN_SPEED 15 +#define SKIDOO_MAX_SPEED 100 +#define SKIDOO_SLOW_SPEED 50 +#define SKIDOO_FAST_SPEED 150 + +#define SKIDOO_MAX_TURN (PHD_DEGREE * 6) // = 1092 +#define SKIDOO_GUN_MESH 4 + +extern BITE g_Skidoo_LeftGun; +extern BITE g_Skidoo_RightGun; + +void __cdecl Skidoo_Initialise(int16_t item_num); +int32_t __cdecl Skidoo_CheckGetOn(int16_t item_num, COLL_INFO *coll); +void __cdecl Skidoo_Collision( + int16_t item_num, ITEM *lara_item, COLL_INFO *coll); +void __cdecl Skidoo_BaddieCollision(ITEM *skidoo); +int32_t __cdecl Skidoo_TestHeight( + const ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *out_pos); +void __cdecl Skidoo_DoSnowEffect(ITEM *skidoo); +int32_t __cdecl Skidoo_Dynamics(ITEM *const skidoo); +int32_t __cdecl Skidoo_UserControl( + ITEM *skidoo, int32_t height, int32_t *out_pitch); +int32_t __cdecl Skidoo_CheckGetOffOK(int32_t direction); +void __cdecl Skidoo_Animation(ITEM *skidoo, int32_t collide, int32_t dead); +void __cdecl Skidoo_Explode(const ITEM *skidoo); +int32_t __cdecl Skidoo_CheckGetOff(void); +void __cdecl Skidoo_Guns(void); +int32_t __cdecl Skidoo_Control(void); +void __cdecl Skidoo_Draw(const ITEM *item); diff --git a/src/tr2/game/creature.c b/src/tr2/game/creature.c index 9898ef7d9..e358e88ed 100644 --- a/src/tr2/game/creature.c +++ b/src/tr2/game/creature.c @@ -336,7 +336,7 @@ void __cdecl Creature_Die(const int16_t item_num, const bool explode) return; } - if (item->object_id == O_SKIDMAN) { + if (item->object_id == O_SKIDOO_DRIVER) { if (explode) { Effect_ExplodingDeath(item_num, -1, 0); } diff --git a/src/tr2/game/lara/control.c b/src/tr2/game/lara/control.c index 5c5e80b28..057b9ecdd 100644 --- a/src/tr2/game/lara/control.c +++ b/src/tr2/game/lara/control.c @@ -1,5 +1,6 @@ #include "game/lara/control.h" +#include "decomp/skidoo.h" #include "game/creature.h" #include "game/gun/gun.h" #include "game/input.h" @@ -46,7 +47,8 @@ void __cdecl Lara_HandleAboveWater(ITEM *const item, COLL_INFO *const coll) if (g_Lara.skidoo != NO_ITEM) { if (g_Items[g_Lara.skidoo].object_id == O_SKIDOO_FAST) { - if (SkidooControl()) { + // TODO: make this g_Objects[O_SKIDOO_FAST].control + if (Skidoo_Control()) { return; } } else { diff --git a/src/tr2/game/lara/draw.c b/src/tr2/game/lara/draw.c index 9bef0d73c..5911164d4 100644 --- a/src/tr2/game/lara/draw.c +++ b/src/tr2/game/lara/draw.c @@ -512,7 +512,8 @@ void __cdecl Lara_Draw_I( Matrix_RotX_I(-16380); Matrix_RotY_I(2 * Random_GetDraw()); S_CalculateStaticLight(2048); - Output_InsertPolygons_I(g_Meshes[g_Objects[235].mesh_idx], clip); + Output_InsertPolygons_I( + g_Meshes[g_Objects[O_FLARE_FIRE].mesh_idx], clip); } Matrix_Pop(); break; diff --git a/src/tr2/game/objects/creatures/skidoo_driver.c b/src/tr2/game/objects/creatures/skidoo_driver.c index 1d27b9d7c..9a2c246c0 100644 --- a/src/tr2/game/objects/creatures/skidoo_driver.c +++ b/src/tr2/game/objects/creatures/skidoo_driver.c @@ -1,38 +1,174 @@ #include "game/objects/creatures/skidoo_driver.h" +#include "decomp/skidoo.h" #include "game/creature.h" -#include "game/objects/common.h" -#include "global/const.h" +#include "game/items.h" +#include "game/lot.h" +#include "game/sound.h" #include "global/funcs.h" #include "global/vars.h" -#define SKIDOO_DRIVER_HITPOINTS 100 -#define SKIDOO_ARMED_RADIUS (WALL_L / 3) // = 341 +#include -void SkidooArmed_Setup(void) +#include + +#define SKIDOO_DRIVER_MIN_TURN (SKIDOO_MAX_TURN / 3) // = 364 +#define SKIDOO_DRIVER_TARGET_ANGLE (PHD_DEGREE * 15) // = 2730 +#define SKIDOO_DRIVER_WAIT_RANGE SQUARE(WALL_L * 4) // = 0x1000000 +#define SKIDOO_DRIVER_SHOT_DAMAGE 10 +#define SKIDOO_DRIVER_LARA_DAMAGE 50 + +typedef enum { + // clang-format off + SKIDOO_DRIVER_STATE_EMPTY = 0, + SKIDOO_DRIVER_STATE_WAIT = 1, + SKIDOO_DRIVER_STATE_MOVING = 2, + SKIDOO_DRIVER_STATE_START_LEFT = 3, + SKIDOO_DRIVER_STATE_START_RIGHT = 4, + SKIDOO_DRIVER_STATE_LEFT = 5, + SKIDOO_DRIVER_STATE_RIGHT = 6, + SKIDOO_DRIVER_STATE_DEATH = 7, + // clang-format on +} SKIDOO_DRIVER_STATE; + +typedef enum { + SKIDOO_DRIVER_ANIM_DEATH = 10, +} SKIDOO_DRIVER_ANIM; + +static void M_KillDriver(ITEM *driver_item); +static void M_MakeMountable(ITEM *skidoo_item); +static void M_ControlDead(ITEM *driver_item, ITEM *skidoo_item); +static int16_t M_ControlAlive(ITEM *driver_item, ITEM *skidoo_item); + +static void M_KillDriver(ITEM *const driver_item) { - OBJECT *const obj = &g_Objects[O_SKIDOO_ARMED]; - if (!obj->loaded) { - return; + const int32_t driver_item_num = driver_item - g_Items; + Item_RemoveActive(driver_item_num); + driver_item->collidable = 0; + driver_item->flags |= IF_ONE_SHOT; + driver_item->hit_points = DONT_TARGET; +} + +static void M_MakeMountable(ITEM *const skidoo_item) +{ + const int32_t skidoo_item_num = skidoo_item - g_Items; + LOT_DisableBaddieAI(skidoo_item_num); + skidoo_item->object_id = O_SKIDOO_FAST; + skidoo_item->status = IS_DEACTIVATED; + Skidoo_Initialise(skidoo_item_num); + + SKIDOO_INFO *const skidoo_data = skidoo_item->data; + skidoo_data->track_mesh = SKIDOO_GUN_MESH; +} + +static void M_ControlDead(ITEM *const driver_item, ITEM *const skidoo_item) +{ + if (driver_item->current_anim_state == SKIDOO_DRIVER_STATE_DEATH) { + Item_Animate(driver_item); + } else { + driver_item->pos.x = skidoo_item->pos.x; + driver_item->pos.y = skidoo_item->pos.y; + driver_item->pos.z = skidoo_item->pos.z; + driver_item->rot.y = skidoo_item->rot.y; + driver_item->room_num = skidoo_item->room_num; + driver_item->anim_num = + g_Objects[O_SKIDOO_DRIVER].anim_idx + SKIDOO_DRIVER_ANIM_DEATH; + driver_item->frame_num = g_Anims[driver_item->anim_num].frame_base; + driver_item->current_anim_state = SKIDOO_DRIVER_STATE_DEATH; + + if (g_Lara.target == skidoo_item) { + g_Lara.target = NULL; + } } - obj->collision = SkidooDriver_Collision; + switch (skidoo_item->current_anim_state) { + case SKIDOO_DRIVER_STATE_MOVING: + case SKIDOO_DRIVER_STATE_WAIT: + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_WAIT; + break; + default: + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_MOVING; + break; + } +} - obj->hit_points = SKIDOO_DRIVER_HITPOINTS; - obj->radius = SKIDOO_ARMED_RADIUS; - obj->shadow_size = UNIT_SHADOW / 2; - obj->pivot_length = 0; +static int16_t M_ControlAlive(ITEM *const driver_item, ITEM *const skidoo_item) +{ + CREATURE *const driver_data = skidoo_item->data; - obj->intelligent = 1; - obj->save_anim = 1; - obj->save_position = 1; - obj->save_hitpoints = 1; - obj->save_flags = 1; + AI_INFO info; + Creature_AIInfo(skidoo_item, &info); + Creature_Mood(skidoo_item, &info, MOOD_ATTACK); + int16_t angle = Creature_Turn(skidoo_item, SKIDOO_MAX_TURN / 2); + + switch (skidoo_item->current_anim_state) { + case SKIDOO_DRIVER_STATE_WAIT: + if (driver_data->mood != MOOD_BORED + && (ABS(info.angle) >= SKIDOO_DRIVER_TARGET_ANGLE + || info.distance >= SKIDOO_DRIVER_WAIT_RANGE)) { + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_MOVING; + } + break; + + case SKIDOO_DRIVER_STATE_MOVING: + if (driver_data->mood == MOOD_BORED) { + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_WAIT; + } else if ( + ABS(info.angle) < SKIDOO_DRIVER_TARGET_ANGLE + && info.distance < SKIDOO_DRIVER_WAIT_RANGE) { + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_WAIT; + } else if (angle < -SKIDOO_DRIVER_MIN_TURN) { + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_START_LEFT; + } else if (angle > SKIDOO_DRIVER_MIN_TURN) { + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_START_RIGHT; + } + break; + + case SKIDOO_DRIVER_STATE_START_LEFT: + case SKIDOO_DRIVER_STATE_LEFT: + if (angle >= -SKIDOO_DRIVER_MIN_TURN) { + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_MOVING; + } else { + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_LEFT; + } + break; + + case SKIDOO_DRIVER_STATE_START_RIGHT: + case SKIDOO_DRIVER_STATE_RIGHT: + if (angle >= -SKIDOO_DRIVER_MIN_TURN) { + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_MOVING; + } else { + skidoo_item->goal_anim_state = SKIDOO_DRIVER_STATE_LEFT; + } + break; + } + + if (driver_item->current_anim_state != SKIDOO_DRIVER_STATE_DEATH) { + if (driver_data->flags == 0 + && ABS(info.angle) < SKIDOO_DRIVER_TARGET_ANGLE + && g_LaraItem->hit_points > 0) { + const int32_t damage = g_Lara.skidoo != NO_ITEM + ? SKIDOO_DRIVER_SHOT_DAMAGE + : SKIDOO_DRIVER_LARA_DAMAGE; + + if (ShotLara(skidoo_item, &info, &g_Skidoo_RightGun, 0, damage) + + ShotLara(skidoo_item, &info, &g_Skidoo_LeftGun, 0, damage)) { + driver_data->flags = 5; + } + } + + if (driver_data->flags != 0) { + Sound_Effect(SFX_LARA_UZI_FIRE, &skidoo_item->pos, SPM_NORMAL); + driver_data->flags--; + } + } + + return angle; } void SkidooDriver_Setup(void) { - OBJECT *const obj = &g_Objects[O_SKIDMAN]; + OBJECT *const obj = &g_Objects[O_SKIDOO_DRIVER]; if (!obj->loaded) { return; } @@ -46,3 +182,85 @@ void SkidooDriver_Setup(void) obj->save_anim = 1; obj->save_flags = 1; } + +void __cdecl SkidooDriver_Initialise(const int16_t item_num) +{ + ITEM *const skidoo_driver = Item_Get(item_num); + + const int16_t skidoo_item_num = Item_Create(); + assert(skidoo_item_num != NO_ITEM); + + ITEM *const skidoo = Item_Get(skidoo_item_num); + skidoo->object_id = O_SKIDOO_ARMED; + skidoo->pos.x = skidoo_driver->pos.x; + skidoo->pos.y = skidoo_driver->pos.y; + skidoo->pos.z = skidoo_driver->pos.z; + skidoo->rot.y = skidoo_driver->rot.y; + skidoo->room_num = skidoo_driver->room_num; + skidoo->flags = IF_ONE_SHOT; + skidoo->shade_1 = -1; + Item_Initialise(skidoo_item_num); + + skidoo_driver->data = (void *)(intptr_t)skidoo_item_num; + g_LevelItemCount++; +} + +void __cdecl SkidooDriver_Control(const int16_t driver_item_num) +{ + ITEM *const driver_item = Item_Get(driver_item_num); + + const int16_t skidoo_item_num = (int16_t)(intptr_t)driver_item->data; + ITEM *const skidoo_item = Item_Get(skidoo_item_num); + + if (skidoo_item->data == NULL) { + LOT_EnableBaddieAI(skidoo_item_num, true); + skidoo_item->status = IS_ACTIVE; + } + + CREATURE *const driver_data = skidoo_item->data; + int16_t angle = 0; + + if (skidoo_item->hit_points <= 0) { + M_ControlDead(driver_item, skidoo_item); + } else { + angle = M_ControlAlive(driver_item, skidoo_item); + } + + if (skidoo_item->current_anim_state == SKIDOO_DRIVER_STATE_WAIT) { + driver_data->head_rotation = 0; + Sound_Effect(SFX_SKIDOO_IDLE, &skidoo_item->pos, SPM_NORMAL); + } else { + driver_data->head_rotation = driver_data->head_rotation == 1 ? 2 : 1; + Skidoo_DoSnowEffect(skidoo_item); + + const int32_t pitch_delta = + (SKIDOO_MAX_SPEED - skidoo_item->speed) * 100; + const int32_t pitch = (SOUND_DEFAULT_PITCH - pitch_delta) << 8; + Sound_Effect(SFX_SKIDOO_MOVING, &skidoo_item->pos, SPM_PITCH | pitch); + } + + Creature_Animate(skidoo_item_num, angle, 0); + + if (driver_item->current_anim_state == SKIDOO_DRIVER_STATE_DEATH) { + if (driver_item->status == IS_DEACTIVATED && skidoo_item->speed == 0 + && skidoo_item->fall_speed == 0) { + M_KillDriver(driver_item); + M_MakeMountable(skidoo_item); + } + } else { + driver_item->pos.x = skidoo_item->pos.x; + driver_item->pos.y = skidoo_item->pos.y; + driver_item->pos.z = skidoo_item->pos.z; + driver_item->rot.y = skidoo_item->rot.y; + const int16_t room_num = skidoo_item->room_num; + if (room_num != driver_item->room_num) { + Item_NewRoom(driver_item_num, room_num); + } + driver_item->anim_num = skidoo_item->anim_num + + g_Objects[O_SKIDOO_DRIVER].anim_idx + - g_Objects[O_SKIDOO_ARMED].anim_idx; + driver_item->frame_num = skidoo_item->frame_num + + g_Anims[driver_item->anim_num].frame_base + - g_Anims[skidoo_item->anim_num].frame_base; + } +} diff --git a/src/tr2/game/objects/creatures/skidoo_driver.h b/src/tr2/game/objects/creatures/skidoo_driver.h index ba6fa835a..363ea4ae9 100644 --- a/src/tr2/game/objects/creatures/skidoo_driver.h +++ b/src/tr2/game/objects/creatures/skidoo_driver.h @@ -1,4 +1,9 @@ #pragma once -void SkidooArmed_Setup(void); +#include + +#define SKIDOO_DRIVER_HITPOINTS 100 + void SkidooDriver_Setup(void); +void __cdecl SkidooDriver_Initialise(int16_t item_num); +void __cdecl SkidooDriver_Control(int16_t item_num); diff --git a/src/tr2/game/objects/setup.c b/src/tr2/game/objects/setup.c index 16cda60ea..e293ca6ac 100644 --- a/src/tr2/game/objects/setup.c +++ b/src/tr2/game/objects/setup.c @@ -27,6 +27,7 @@ #include "game/objects/creatures/xian_knight.h" #include "game/objects/creatures/xian_spearman.h" #include "game/objects/creatures/yeti.h" +#include "game/objects/vehicles/skidoo_armed.h" #include "global/funcs.h" #include "global/types.h" #include "global/vars.h" @@ -86,7 +87,6 @@ void __cdecl Object_SetupBaddyObjects(void) Monk2_Setup(); Bird_SetupEagle(); Bird_SetupCrow(); - SkidooArmed_Setup(); SkidooDriver_Setup(); Bartoli_Setup(); Dragon_SetupFront(); @@ -101,6 +101,9 @@ void __cdecl Object_SetupBaddyObjects(void) GiantYeti_Setup(); TRex_Setup(); Winston_Setup(); + + // TODO: move this to Object_SetupGeneralObjects + SkidooArmed_Setup(); } void __cdecl Object_SetupAllObjects(void) diff --git a/src/tr2/game/objects/vars.c b/src/tr2/game/objects/vars.c index 64a55eb4b..3c5f8298e 100644 --- a/src/tr2/game/objects/vars.c +++ b/src/tr2/game/objects/vars.c @@ -38,7 +38,7 @@ const GAME_OBJECT_ID g_EnemyObjects[] = { O_BANDIT_1, O_BANDIT_2, O_BANDIT_2B, - O_SKIDMAN, + O_SKIDOO_DRIVER, O_DINO, NO_OBJECT, // clang-format on diff --git a/src/tr2/game/objects/vehicles/boat.c b/src/tr2/game/objects/vehicles/boat.c index 7f280af3c..d80acbbef 100644 --- a/src/tr2/game/objects/vehicles/boat.c +++ b/src/tr2/game/objects/vehicles/boat.c @@ -17,10 +17,10 @@ #define BOAT_FALL_ANIM 15 #define BOAT_DEATH_ANIM 18 -#define BOAT_GETON_LW_ANIM 0 -#define BOAT_GETON_RW_ANIM 8 -#define BOAT_GETON_J_ANIM 6 -#define BOAT_GETON_START 1 +#define BOAT_GET_ON_LW_ANIM 0 +#define BOAT_GET_ON_RW_ANIM 8 +#define BOAT_GET_ON_J_ANIM 6 +#define BOAT_GET_ON_START 1 #define BOAT_RADIUS 500 #define BOAT_SIDE 300 @@ -44,7 +44,7 @@ #define GONDOLA_SINK_SPEED 50 typedef enum { - BOAT_GETON = 0, + BOAT_GET_ON = 0, BOAT_STILL = 1, BOAT_MOVING = 2, BOAT_JUMP_R = 3, @@ -77,7 +77,7 @@ void __cdecl Boat_Initialise(const int16_t item_num) boat->data = boat_data; } -int32_t __cdecl Boat_CheckGeton( +int32_t __cdecl Boat_CheckGetOn( const int16_t item_num, const COLL_INFO *const coll) { if (g_Lara.gun_status != LGS_ARMLESS) { @@ -95,7 +95,7 @@ int32_t __cdecl Boat_CheckGeton( return 0; } - int32_t geton = 0; + int32_t get_on = 0; const int16_t rot = boat->rot.y - lara->rot.y; if (g_Lara.water_status == LWS_SURFACE || g_Lara.water_status == LWS_WADE) { @@ -104,27 +104,27 @@ int32_t __cdecl Boat_CheckGeton( } if (rot > PHD_45 && rot < PHD_135) { - geton = 1; + get_on = 1; } else if (rot > -PHD_135 && rot < -PHD_45) { - geton = 2; + get_on = 2; } } else if (g_Lara.water_status == LWS_ABOVE_WATER) { int16_t fall_speed = lara->fall_speed; if (fall_speed > 0) { if (rot > -PHD_135 && rot < PHD_135 && lara->pos.y > boat->pos.y) { - geton = 3; + get_on = 3; } } else if (!fall_speed && rot > -PHD_135 && rot < PHD_135) { if (lara->pos.x == boat->pos.x && lara->pos.y == boat->pos.y && lara->pos.z == boat->pos.z) { - geton = 4; + get_on = 4; } else { - geton = 3; + get_on = 3; } } } - if (!geton) { + if (!get_on) { return 0; } @@ -136,7 +136,7 @@ int32_t __cdecl Boat_CheckGeton( return 0; } - return geton; + return get_on; } void __cdecl Boat_Collision( @@ -146,8 +146,8 @@ void __cdecl Boat_Collision( return; } - const int32_t geton = Boat_CheckGeton(item_num, coll); - if (!geton) { + const int32_t get_on = Boat_CheckGetOn(item_num, coll); + if (!get_on) { coll->enable_baddie_push = 1; Object_Collision(item_num, lara, coll); return; @@ -155,18 +155,18 @@ void __cdecl Boat_Collision( g_Lara.skidoo = item_num; - switch (geton) { + switch (get_on) { case 1: - lara->anim_num = g_Objects[O_LARA_BOAT].anim_idx + BOAT_GETON_RW_ANIM; + lara->anim_num = g_Objects[O_LARA_BOAT].anim_idx + BOAT_GET_ON_RW_ANIM; break; case 2: - lara->anim_num = g_Objects[O_LARA_BOAT].anim_idx + BOAT_GETON_LW_ANIM; + lara->anim_num = g_Objects[O_LARA_BOAT].anim_idx + BOAT_GET_ON_LW_ANIM; break; case 3: - lara->anim_num = g_Objects[O_LARA_BOAT].anim_idx + BOAT_GETON_J_ANIM; + lara->anim_num = g_Objects[O_LARA_BOAT].anim_idx + BOAT_GET_ON_J_ANIM; break; default: - lara->anim_num = g_Objects[O_LARA_BOAT].anim_idx + BOAT_GETON_START; + lara->anim_num = g_Objects[O_LARA_BOAT].anim_idx + BOAT_GET_ON_START; break; } @@ -652,7 +652,7 @@ void __cdecl Boat_Control(const int16_t item_num) if (g_Lara.skidoo == item_num && lara->hit_points > 0) { switch (lara->current_anim_state) { - case BOAT_GETON: + case BOAT_GET_ON: case BOAT_JUMP_R: case BOAT_JUMP_L: break; diff --git a/src/tr2/game/objects/vehicles/boat.h b/src/tr2/game/objects/vehicles/boat.h index 855e63774..e2221e1f8 100644 --- a/src/tr2/game/objects/vehicles/boat.h +++ b/src/tr2/game/objects/vehicles/boat.h @@ -5,7 +5,7 @@ #include void __cdecl Boat_Initialise(int16_t item_num); -int32_t __cdecl Boat_CheckGeton(int16_t item_num, const COLL_INFO *coll); +int32_t __cdecl Boat_CheckGetOn(int16_t item_num, const COLL_INFO *coll); void __cdecl Boat_Collision(int16_t item_num, ITEM *lara, COLL_INFO *coll); int32_t __cdecl Boat_TestWaterHeight( const ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos); diff --git a/src/tr2/game/objects/vehicles/skidoo_armed.c b/src/tr2/game/objects/vehicles/skidoo_armed.c new file mode 100644 index 000000000..c0486ea04 --- /dev/null +++ b/src/tr2/game/objects/vehicles/skidoo_armed.c @@ -0,0 +1,97 @@ +#include "game/objects/vehicles/skidoo_armed.h" + +#include "game/items.h" +#include "game/lara/control.h" +#include "game/lara/misc.h" +#include "game/math.h" +#include "game/objects/creatures/skidoo_driver.h" +#include "global/funcs.h" +#include "global/vars.h" + +#define SKIDOO_ARMED_RADIUS (WALL_L / 3) // = 341 + +void SkidooArmed_Setup(void) +{ + OBJECT *const obj = &g_Objects[O_SKIDOO_ARMED]; + if (!obj->loaded) { + return; + } + + obj->collision = SkidooArmed_Collision; + + obj->hit_points = SKIDOO_DRIVER_HITPOINTS; + obj->radius = SKIDOO_ARMED_RADIUS; + obj->shadow_size = UNIT_SHADOW / 2; + obj->pivot_length = 0; + + obj->intelligent = 1; + obj->save_anim = 1; + obj->save_position = 1; + obj->save_hitpoints = 1; + obj->save_flags = 1; +} + +void __cdecl SkidooArmed_Push( + const ITEM *const item, ITEM *const lara_item, const int32_t radius) +{ + const int32_t dx = lara_item->pos.x - item->pos.x; + const int32_t dz = lara_item->pos.z - item->pos.z; + const int32_t cy = Math_Cos(item->rot.y); + const int32_t sy = Math_Sin(item->rot.y); + + int32_t rx = (cy * dx - sy * dz) >> W2V_SHIFT; + int32_t rz = (sy * dx + cy * dz) >> W2V_SHIFT; + + const FRAME_INFO *const best_frame = Item_GetBestFrame(item); + BOUNDS_16 bounds = { + .min_x = best_frame->bounds.min_x - radius, + .max_x = best_frame->bounds.max_x + radius, + .min_z = best_frame->bounds.min_z - radius, + .max_z = best_frame->bounds.max_z + radius, + }; + + if (rx < bounds.min_x || rx > bounds.max_x || rz < bounds.min_z + || rz > bounds.max_z) { + return; + } + + const int32_t r = bounds.max_x - rx; + const int32_t l = rx - bounds.min_x; + const int32_t t = bounds.max_z - rz; + const int32_t b = rz - bounds.min_z; + if (l <= r && l <= t && l <= b) { + rx -= l; + } else if (r <= l && r <= t && r <= b) { + rx += r; + } else if (t <= l && t <= r && t <= b) { + rz += t; + } else { + rz -= b; + } + + lara_item->pos.x = item->pos.x + ((rz * sy + rx * cy) >> W2V_SHIFT); + lara_item->pos.z = item->pos.z + ((rz * cy - rx * sy) >> W2V_SHIFT); +} + +void __cdecl SkidooArmed_Collision( + const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll) +{ + ITEM *const item = Item_Get(item_num); + if (!Item_TestBoundsCollide(item, lara_item, coll->radius)) { + return; + } + + if (!Collide_TestCollision(item, lara_item)) { + return; + } + + if (coll->enable_baddie_push) { + Lara_Push( + item, lara_item, coll, item->speed > 0 ? coll->enable_spaz : false, + false); + } + + if (g_Lara.skidoo == NO_ITEM && item->speed > 0) { + Lara_TakeDamage(100, true); + } +} diff --git a/src/tr2/game/objects/vehicles/skidoo_armed.h b/src/tr2/game/objects/vehicles/skidoo_armed.h new file mode 100644 index 000000000..5a55efcdb --- /dev/null +++ b/src/tr2/game/objects/vehicles/skidoo_armed.h @@ -0,0 +1,11 @@ +#pragma once + +#include "global/types.h" + +void SkidooArmed_Setup(void); + +void __cdecl SkidooArmed_Push( + const ITEM *item, ITEM *lara_item, int32_t radius); + +void __cdecl SkidooArmed_Collision( + int16_t item_num, ITEM *lara_item, COLL_INFO *coll); diff --git a/src/tr2/game/sound.c b/src/tr2/game/sound.c index 98b10eb8b..23e6e0a31 100644 --- a/src/tr2/game/sound.c +++ b/src/tr2/game/sound.c @@ -36,8 +36,6 @@ typedef enum { // clang-format on } SAMPLE_FLAG; -#define SOUND_DEFAULT_PITCH 0x10000 - #define SOUND_RANGE 10 #define SOUND_RADIUS (SOUND_RANGE * WALL_L) // = 0x2800 = 10240 #define SOUND_RADIUS_SQRD SQUARE(SOUND_RADIUS) // = 0x6400000 diff --git a/src/tr2/game/sound.h b/src/tr2/game/sound.h index 689cf228a..9ca04ee92 100644 --- a/src/tr2/game/sound.h +++ b/src/tr2/game/sound.h @@ -2,6 +2,8 @@ #include "global/types.h" +#define SOUND_DEFAULT_PITCH 0x10000 + void __cdecl Sound_Init(void); void __cdecl Sound_Shutdown(void); diff --git a/src/tr2/global/funcs.h b/src/tr2/global/funcs.h index 4d961452d..b199212d7 100644 --- a/src/tr2/global/funcs.h +++ b/src/tr2/global/funcs.h @@ -168,28 +168,9 @@ #define Jelly_Control ((void __cdecl (*)(int16_t item_num))0x0043C850) #define Baracudda_Control ((void __cdecl (*)(int16_t item_num))0x0043C970) #define Shark_Control ((void __cdecl (*)(int16_t item_num))0x0043CBA0) -#define InitialiseSkidoo ((void __cdecl (*)(int16_t item_num))0x0043CE30) -#define SkidooCheckGeton ((int32_t __cdecl (*)(int16_t item_num, COLL_INFO *coll))0x0043CE70) -#define SkidooCollision ((void __cdecl (*)(int16_t item_num, ITEM *litem, COLL_INFO *coll))0x0043CF60) -#define SkidooBaddieCollision ((void __cdecl (*)(ITEM *skidoo))0x0043D060) -#define TestHeight ((int32_t __cdecl (*)(ITEM *item, int32_t z_off, int32_t x_off, XYZ_32 *pos))0x0043D260) #define DoShift ((int32_t __cdecl (*)(ITEM *skidoo, XYZ_32 *pos, XYZ_32 *old))0x0043D320) #define DoDynamics ((int32_t __cdecl (*)(int32_t height, int32_t fall_speed, int32_t *y))0x0043D5A0) #define GetCollisionAnim ((int32_t __cdecl (*)(ITEM *skidoo, XYZ_32 *moved))0x0043D600) -#define DoSnowEffect ((void __cdecl (*)(ITEM *skidoo))0x0043D690) -#define SkidooDynamics ((int32_t __cdecl (*)(ITEM *skidoo))0x0043D7D0) -#define SkidooUserControl ((int32_t __cdecl (*)(ITEM *skidoo, int32_t height, int32_t *pitch))0x0043DC70) -#define SkidooCheckGetOffOK ((int32_t __cdecl (*)(int32_t direction))0x0043DE30) -#define SkidooAnimation ((void __cdecl (*)(ITEM *skidoo, int32_t collide, int32_t dead))0x0043DF40) -#define SkidooExplode ((void __cdecl (*)(ITEM *skidoo))0x0043E220) -#define SkidooCheckGetOff ((int32_t __cdecl (*)(void))0x0043E2A0) -#define SkidooGuns ((void __cdecl (*)(void))0x0043E4E0) -#define SkidooControl ((int32_t __cdecl (*)(void))0x0043E600) -#define SkidooArmed_Draw ((void __cdecl (*)(const ITEM *item))0x0043EA60) -#define SkidooDriver_Initialise ((void __cdecl (*)(int16_t item_num))0x0043ED40) -#define SkidooDriver_Control ((void __cdecl (*)(int16_t rider_num))0x0043EDD0) -#define SkidmanPush ((void __cdecl (*)(ITEM *item, ITEM *lara_item, int32_t radius))0x0043F1D0) -#define SkidooDriver_Collision ((void __cdecl (*)(int16_t item_num, ITEM *lara_item, COLL_INFO *coll))0x0043F2F0) #define Music_GetRealTrack ((int32_t __cdecl (*)(int32_t track))0x0043F380) #define Collide_TestCollision ((int32_t __cdecl (*)(ITEM *item, const ITEM *lara_item))0x0043F9B0) #define Collide_GetSpheres ((int32_t __cdecl (*)(const ITEM *item, SPHERE *spheres, bool world_space))0x0043FAE0) diff --git a/src/tr2/global/types.h b/src/tr2/global/types.h index a33faac4e..98dd9a702 100644 --- a/src/tr2/global/types.h +++ b/src/tr2/global/types.h @@ -794,6 +794,18 @@ typedef enum { GAMEMODE_IN_CUTSCENE } GAMEMODE; +typedef enum { + TRAP_SET = 0, + TRAP_ACTIVATE = 1, + TRAP_WORKING = 2, + TRAP_FINISHED = 3, +} TRAP_ANIM; + +typedef enum { + DOOR_CLOSED = 0, + DOOR_OPEN = 1, +} DOOR_ANIM; + typedef enum { GFD_START_GAME = 0x0000, GFD_START_SAVED_GAME = 0x0100, @@ -1574,6 +1586,16 @@ typedef struct __unaligned { int32_t pitch; } BOAT_INFO; +typedef struct __unaligned { + int16_t track_mesh; + int32_t skidoo_turn; + int32_t left_fallspeed; + int32_t right_fallspeed; + int16_t momentum_angle; + int16_t extra_rotation; + int32_t pitch; +} SKIDOO_INFO; + typedef struct __unaligned { struct { XYZ_16 min; diff --git a/src/tr2/inject_exec.c b/src/tr2/inject_exec.c index 1058ee5d8..a56577060 100644 --- a/src/tr2/inject_exec.c +++ b/src/tr2/inject_exec.c @@ -3,6 +3,7 @@ #include "decomp/decomp.h" #include "decomp/effects.h" #include "decomp/fmv.h" +#include "decomp/skidoo.h" #include "decomp/stats.h" #include "game/background.h" #include "game/box.h" @@ -42,6 +43,7 @@ #include "game/objects/creatures/bird.h" #include "game/objects/creatures/diver.h" #include "game/objects/creatures/dragon.h" +#include "game/objects/creatures/skidoo_driver.h" #include "game/objects/effects/ember.h" #include "game/objects/effects/flame.h" #include "game/objects/effects/twinkle.h" @@ -60,6 +62,7 @@ #include "game/objects/traps/ember_emitter.h" #include "game/objects/traps/flame_emitter.h" #include "game/objects/vehicles/boat.h" +#include "game/objects/vehicles/skidoo_armed.h" #include "game/option/option.h" #include "game/output.h" #include "game/overlay.h" @@ -77,6 +80,7 @@ static void M_DecompGeneral(const bool enable); static void M_DecompFMV(const bool enable); +static void M_DecompSkidoo(const bool enable); static void M_DecompStats(const bool enable); static void M_DecompEffects(const bool enable); static void M_HWR(bool enable); @@ -240,6 +244,25 @@ static void M_DecompFMV(const bool enable) INJECT(enable, 0x0044C460, S_IntroFMV); } +static void M_DecompSkidoo(const bool enable) +{ + INJECT(enable, 0x0043CE30, Skidoo_Initialise); + INJECT(enable, 0x0043CE70, Skidoo_CheckGetOn); + INJECT(enable, 0x0043CF60, Skidoo_Collision); + INJECT(enable, 0x0043D060, Skidoo_BaddieCollision); + INJECT(enable, 0x0043D260, Skidoo_TestHeight); + INJECT(enable, 0x0043D690, Skidoo_DoSnowEffect); + INJECT(enable, 0x0043D7D0, Skidoo_Dynamics); + INJECT(enable, 0x0043DC70, Skidoo_UserControl); + INJECT(enable, 0x0043DE30, Skidoo_CheckGetOffOK); + INJECT(enable, 0x0043DF40, Skidoo_Animation); + INJECT(enable, 0x0043E220, Skidoo_Explode); + INJECT(enable, 0x0043E2A0, Skidoo_CheckGetOff); + INJECT(enable, 0x0043E4E0, Skidoo_Guns); + INJECT(enable, 0x0043E600, Skidoo_Control); + INJECT(enable, 0x0043EA60, Skidoo_Draw); +} + static void M_DecompStats(const bool enable) { INJECT(enable, 0x004262B0, AddAssaultTime); @@ -960,7 +983,7 @@ static void M_Objects(const bool enable) INJECT(enable, 0x0040C880, Bird_Initialise); INJECT(enable, 0x0040C910, Bird_Control); INJECT(enable, 0x0040CB30, Boat_Initialise); - INJECT(enable, 0x0040CB70, Boat_CheckGeton); + INJECT(enable, 0x0040CB70, Boat_CheckGetOn); INJECT(enable, 0x0040CCE0, Boat_Collision); INJECT(enable, 0x0040CE40, Boat_TestWaterHeight); INJECT(enable, 0x0040CF40, Boat_DoShift); @@ -1006,6 +1029,10 @@ static void M_Objects(const bool enable) INJECT(enable, 0x00438E80, Pickup_Trigger); INJECT(enable, 0x0043A480, Object_SetupBaddyObjects); INJECT(enable, 0x0043C710, Object_SetupAllObjects); + INJECT(enable, 0x0043ED40, SkidooDriver_Initialise); + INJECT(enable, 0x0043EDD0, SkidooDriver_Control); + INJECT(enable, 0x0043F1D0, SkidooArmed_Push); + INJECT(enable, 0x0043F2F0, SkidooArmed_Collision); INJECT(enable, 0x00442B30, FlameEmitter_Control); INJECT(enable, 0x00442BC0, Flame_Control); INJECT(enable, 0x00442E70, EmberEmitter_Control); @@ -1058,6 +1085,7 @@ void Inject_Exec(void) { M_DecompGeneral(true); M_DecompFMV(true); + M_DecompSkidoo(true); M_DecompStats(true); M_DecompEffects(true); M_HWR(true); diff --git a/src/tr2/meson.build b/src/tr2/meson.build index 3ba9a8f35..0b93084d6 100644 --- a/src/tr2/meson.build +++ b/src/tr2/meson.build @@ -87,6 +87,7 @@ dll_sources = [ 'decomp/decomp.c', 'decomp/fmv.c', 'decomp/effects.c', + 'decomp/skidoo.c', 'decomp/stats.c', 'game/background.c', 'game/backpack.c', @@ -177,6 +178,7 @@ dll_sources = [ 'game/objects/traps/flame_emitter.c', 'game/objects/vars.c', 'game/objects/vehicles/boat.c', + 'game/objects/vehicles/skidoo_armed.c', 'game/option/option.c', 'game/option/option_compass.c', 'game/option/option_controls.c', diff --git a/tools/shared/ida_progress.py b/tools/shared/ida_progress.py index a3abf244a..ef397ceb3 100644 --- a/tools/shared/ida_progress.py +++ b/tools/shared/ida_progress.py @@ -21,7 +21,7 @@ function_name = identifier parameters = ("(" _ "void" _ ")") / ("(" _ (parameter_list?) _ ")") parameter_list = parameter _ ("," _ parameter _)* - parameter = function_decl / var_decl + parameter = decl / type qualifier = ~"const|__cdecl|__stdcall|__thiscall|__fastcall" array_subscript = (_ "[" number? "]")*