mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 12:47:58 +03:00
misc: import tr2 codebase
This commit is contained in:
parent
6f115aa8b4
commit
23f3ed4d56
205 changed files with 50085 additions and 0 deletions
BIN
data/tr2/icon.ico
Normal file
BIN
data/tr2/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
BIN
data/tr2/icon.png
Normal file
BIN
data/tr2/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
BIN
data/tr2/icon.psd
Normal file
BIN
data/tr2/icon.psd
Normal file
Binary file not shown.
1
data/tr2/icon.rc
Normal file
1
data/tr2/icon.rc
Normal file
|
@ -0,0 +1 @@
|
|||
id ICON "{icon_path}"
|
BIN
data/tr2/logo-dark-theme.png
Normal file
BIN
data/tr2/logo-dark-theme.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6 KiB |
BIN
data/tr2/logo-light-theme.png
Normal file
BIN
data/tr2/logo-light-theme.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6 KiB |
1489
data/tr2/logo.ai
Normal file
1489
data/tr2/logo.ai
Normal file
File diff suppressed because it is too large
Load diff
BIN
data/tr2/ship/Tomb2.exe
Normal file
BIN
data/tr2/ship/Tomb2.exe
Normal file
Binary file not shown.
410
data/tr2/ship/cfg/TR2X_gameflow.json5
Normal file
410
data/tr2/ship/cfg/TR2X_gameflow.json5
Normal file
|
@ -0,0 +1,410 @@
|
|||
{
|
||||
// NOTE: bad changes to this file may result in crashes.
|
||||
// Lines starting with double slashes are comments and are ignored.
|
||||
|
||||
"levels": [
|
||||
// 0. Lara's Home
|
||||
{},
|
||||
|
||||
// 1. The Great Wall
|
||||
{},
|
||||
|
||||
// 2. Venice
|
||||
{},
|
||||
|
||||
// 3. Bartoli's Hideout
|
||||
{},
|
||||
|
||||
// 4. Opera House
|
||||
{},
|
||||
|
||||
// 5. Offshore Rig
|
||||
{},
|
||||
|
||||
// 6. Diving Area
|
||||
{},
|
||||
|
||||
// 7. 40 Fathoms
|
||||
{},
|
||||
|
||||
// 8. The Wreck of Maria Doria
|
||||
{},
|
||||
|
||||
// 9. Living Quarters
|
||||
{},
|
||||
|
||||
// 10. The Deck
|
||||
{},
|
||||
|
||||
// 11. Tibetan Foothills
|
||||
{
|
||||
"object_strings": {
|
||||
"O_TIGER": "Snow Leopard",
|
||||
},
|
||||
},
|
||||
|
||||
// 12. Barkhang Monastery
|
||||
{},
|
||||
|
||||
// 13. Catacombs of the Talion
|
||||
{
|
||||
"object_strings": {
|
||||
"O_TIGER": "Snow Leopard",
|
||||
},
|
||||
},
|
||||
|
||||
// 14. Ice Palace
|
||||
{
|
||||
"object_strings": {
|
||||
"O_TIGER": "Snow Leopard",
|
||||
},
|
||||
},
|
||||
|
||||
// 15. Temple of Xian
|
||||
{},
|
||||
|
||||
// 16. Floating Islands
|
||||
{},
|
||||
|
||||
// 17. The Dragon's Lair
|
||||
{},
|
||||
|
||||
// 18. Home Sweet Home
|
||||
{},
|
||||
|
||||
// 19. Demo 1
|
||||
{},
|
||||
|
||||
// 20. Demo 2
|
||||
{},
|
||||
|
||||
// 21. Demo 3
|
||||
{},
|
||||
],
|
||||
|
||||
"object_strings": {
|
||||
"O_LARA": "Lara",
|
||||
"O_LARA_PISTOLS": "Pistols Animation",
|
||||
"O_LARA_HAIR": "Braid",
|
||||
"O_LARA_SHOTGUN": "Shotgun Animation",
|
||||
"O_LARA_MAGNUMS": "Magnums Animation",
|
||||
"O_LARA_UZIS": "Uzis Animation",
|
||||
"O_LARA_M16": "M16 Animation",
|
||||
"O_LARA_GRENADE": "Grenade Launcher Animation",
|
||||
"O_LARA_HARPOON": "Harpoon Animation",
|
||||
"O_LARA_FLARE": "Flare Animation",
|
||||
"O_LARA_SKIDOO": "Snowmobile Animation",
|
||||
"O_LARA_BOAT": "Boat Animation",
|
||||
"O_LARA_EXTRA": "Lara's Extra Animation",
|
||||
"O_SKIDOO_FAST": "Red Snowmobile",
|
||||
"O_BOAT": "Boat",
|
||||
"O_DOG": "Dog",
|
||||
"O_CULT_1": "Masked Goon 1",
|
||||
"O_CULT_1A": "Masked Goon 2",
|
||||
"O_CULT_1B": "Masked Goon 3",
|
||||
"O_CULT_2": "Knife Thrower",
|
||||
"O_CULT_3": "Shotgun Goon",
|
||||
"O_MOUSE": "Rat",
|
||||
"O_DRAGON_FRONT": "Dragon Front",
|
||||
"O_DRAGON_BACK": "Dragon Back",
|
||||
"O_GONDOLA": "Gondola",
|
||||
"O_SHARK": "Shark",
|
||||
"O_EEL": "Eel",
|
||||
"O_BIG_EEL": "Big Eel",
|
||||
"O_BARRACUDA": "Barracuda",
|
||||
"O_DIVER": "Scuba Diver",
|
||||
"O_WORKER_1": "Gunman Goon 1",
|
||||
"O_WORKER_2": "Gunman Goon 2",
|
||||
"O_WORKER_3": "Stick Wielding Goon 1",
|
||||
"O_WORKER_4": "Stick Wielding Goon 2",
|
||||
"O_WORKER_5": "Flamethrower Goon",
|
||||
"O_JELLY": "Jellyfish",
|
||||
"O_SPIDER": "Spider",
|
||||
"O_BIG_SPIDER": "Giant Spider",
|
||||
"O_CROW": "Crow",
|
||||
"O_TIGER": "Tiger",
|
||||
"O_BARTOLI": "Marco Bartoli",
|
||||
"O_XIAN_SPEARMAN": "Xian Spearman",
|
||||
"O_XIAN_SPEARMAN_STATUE": "Xian Spearman Statue",
|
||||
"O_XIAN_KNIGHT": "Xian Knight",
|
||||
"O_XIAN_KNIGHT_STATUE": "Xian Knight",
|
||||
"O_YETI": "Yeti",
|
||||
"O_GIANT_YETI": "Bird Monster",
|
||||
"O_EAGLE": "Eagle",
|
||||
"O_BANDIT_1": "Mercenary 1",
|
||||
"O_BANDIT_2": "Mercenary 2",
|
||||
"O_BANDIT_2B": "Mercenary 3",
|
||||
"O_SKIDOO_ARMED": "Black Snowmobile",
|
||||
"O_SKIDMAN": "Black Snowmobile Driver",
|
||||
"O_MONK_1": "Monk 1",
|
||||
"O_MONK_2": "Monk 2",
|
||||
"O_FALLING_BLOCK_1": "Falling Block 1",
|
||||
"O_FALLING_BLOCK_2": "Falling Block 2",
|
||||
"O_FALLING_BLOCK_3": "Loose Boards",
|
||||
"O_PENDULUM_1": "Sandbag",
|
||||
"O_SPIKES": "Spikes",
|
||||
"O_ROLLING_BALL_1": "Boulder 1",
|
||||
"O_DARTS": "Disc",
|
||||
"O_DART_EMITTER": "Disc Emitter",
|
||||
"O_DRAWBRIDGE": "Drawbridge",
|
||||
"O_TEETH_TRAP": "Teeth Trap",
|
||||
"O_LIFT": "Lift",
|
||||
"O_GENERAL": "Minisub",
|
||||
"O_MOVABLE_BLOCK_1": "Push Block 1",
|
||||
"O_MOVABLE_BLOCK_2": "Push Block 2",
|
||||
"O_MOVABLE_BLOCK_3": "Push Block 3",
|
||||
"O_MOVABLE_BLOCK_4": "Push Block 4",
|
||||
"O_BIG_BOWL": "Lava Bowl",
|
||||
"O_WINDOW_1": "Breakable Window 1",
|
||||
"O_WINDOW_2": "Breakable Window 2",
|
||||
"O_WINDOW_3": "Breakable Window 3",
|
||||
"O_WINDOW_4": "Breakable Window 4",
|
||||
"O_PROPELLER_1": "Airplane Propeller",
|
||||
"O_POWER_SAW": "Power Saw",
|
||||
"O_HOOK": "Hook",
|
||||
"O_FALLING_CEILING": "Falling Ceiling",
|
||||
"O_SPINNING_BLADE": "Spinning Blade",
|
||||
"O_BLADE": "Wall-mounted Blade",
|
||||
"O_KILLER_STATUE": "Statue with Sword",
|
||||
"O_ROLLING_BALL_2": "Boulder 2",
|
||||
"O_ICICLE": "Icicles",
|
||||
"O_SPIKE_WALL": "Spike Wall",
|
||||
"O_SPRING_BOARD": "Springboard",
|
||||
"O_CEILING_SPIKES": "Spiky Ceiling",
|
||||
"O_BELL": "Bell",
|
||||
"O_WATER_SPRITE": "Boat Wake",
|
||||
"O_SNOW_SPRITE": "Snowmobile Wale",
|
||||
"O_SKIDOO_LARA": "Snowmobile Animation",
|
||||
"O_SWITCH_TYPE_1": "Switch 1",
|
||||
"O_SWITCH_TYPE_2": "Switch 2",
|
||||
"O_PROPELLER_2": "Underwater Propeller",
|
||||
"O_PROPELLER_3": "Air Fan",
|
||||
"O_PENDULUM_2": "Swinging Box",
|
||||
"O_MESH_SWAP_1": "Mesh Swap 1",
|
||||
"O_MESH_SWAP_2": "Mesh Swap 2",
|
||||
"O_LARA_SWAP": "Lara Mesh Swap",
|
||||
"O_TEXT_BOX": "UI Frame",
|
||||
"O_ROLLING_BALL_3": "Boulder 3",
|
||||
"O_DEATH_SLIDE": "Zipline Handle",
|
||||
"O_SWITCH_TYPE_3": "Switch 3",
|
||||
"O_SWITCH_TYPE_4": "Switch 4",
|
||||
"O_SWITCH_TYPE_5": "Switch 5",
|
||||
"O_DOOR_TYPE_1": "Door 1",
|
||||
"O_DOOR_TYPE_2": "Door 2",
|
||||
"O_DOOR_TYPE_3": "Door 3",
|
||||
"O_DOOR_TYPE_4": "Door 4",
|
||||
"O_DOOR_TYPE_5": "Door 5",
|
||||
"O_DOOR_TYPE_6": "Door 6",
|
||||
"O_DOOR_TYPE_7": "Door 7",
|
||||
"O_DOOR_TYPE_8": "Door 8",
|
||||
"O_TRAPDOOR_TYPE_1": "Trapdoor 1",
|
||||
"O_TRAPDOOR_TYPE_2": "Trapdoor 2",
|
||||
"O_TRAPDOOR_TYPE_3": "Trapdoor 3",
|
||||
"O_BRIDGE_FLAT": "Bridge Flat",
|
||||
"O_BRIDGE_TILT_1": "Bridge Tilt 1",
|
||||
"O_BRIDGE_TILT_2": "Bridge Tilt 2",
|
||||
"O_PASSPORT_OPTION": "Passport Option",
|
||||
"O_COMPASS_OPTION": "Compass Option",
|
||||
"O_PHOTO_OPTION": "Photo Option",
|
||||
"O_PLAYER_1": "Cutscene Actor 1",
|
||||
"O_PLAYER_2": "Cutscene Actor 2",
|
||||
"O_PLAYER_3": "Cutscene Actor 3",
|
||||
"O_PLAYER_4": "Cutscene Actor 4",
|
||||
"O_PLAYER_5": "Cutscene Actor 5",
|
||||
"O_PLAYER_6": "Cutscene Actor 6",
|
||||
"O_PLAYER_7": "Cutscene Actor 7",
|
||||
"O_PLAYER_8": "Cutscene Actor 8",
|
||||
"O_PLAYER_9": "Cutscene Actor 9",
|
||||
"O_PLAYER_10": "Cutscene Actor 10",
|
||||
"O_PASSPORT_CLOSED": "Passport Closed",
|
||||
"O_COMPASS_ITEM": "Compass",
|
||||
"O_PISTOL_ITEM": "Pistols",
|
||||
"O_SHOTGUN_ITEM": "Shotgun",
|
||||
"O_MAGNUM_ITEM": "Automatic Pistols",
|
||||
"O_UZI_ITEM": "Uzis",
|
||||
"O_HARPOON_ITEM": "Harpoon",
|
||||
"O_M16_ITEM": "M16",
|
||||
"O_GRENADE_ITEM": "Grenade Launcher",
|
||||
"O_PISTOL_AMMO_ITEM": "Pistol Ammo",
|
||||
"O_SHOTGUN_AMMO_ITEM": "Shotgun Ammo",
|
||||
"O_MAGNUM_AMMO_ITEM": "Magnum Ammo",
|
||||
"O_UZI_AMMO_ITEM": "Uzi Ammo",
|
||||
"O_HARPOON_AMMO_ITEM": "Harpoon Ammo",
|
||||
"O_M16_AMMO_ITEM": "M16 Ammo",
|
||||
"O_GRENADE_AMMO_ITEM": "Grenade Launcher Ammo",
|
||||
"O_SMALL_MEDIPACK_ITEM": "Small Medipack",
|
||||
"O_LARGE_MEDIPACK_ITEM": "Large Medipack",
|
||||
"O_FLARES_ITEM": "Flares Box",
|
||||
"O_FLARE_ITEM": "Flare",
|
||||
"O_DETAIL_OPTION": "Detail Option",
|
||||
"O_SOUND_OPTION": "Sound Option",
|
||||
"O_CONTROL_OPTION": "Controls Option",
|
||||
"O_GAMMA_OPTION": "Gamma Option",
|
||||
"O_PISTOL_OPTION": "Pistol Option",
|
||||
"O_SHOTGUN_OPTION": "Shotgun Option",
|
||||
"O_MAGNUM_OPTION": "Magnums Option",
|
||||
"O_UZI_OPTION": "Uzis Option",
|
||||
"O_HARPOON_OPTION": "Harpoon Option",
|
||||
"O_M16_OPTION": "M16 Option",
|
||||
"O_GRENADE_OPTION": "Grenade Launcher Option",
|
||||
"O_PISTOL_AMMO_OPTION": "Pistol Ammo Option",
|
||||
"O_SHOTGUN_AMMO_OPTION": "Shotgun Ammo Option",
|
||||
"O_MAGNUM_AMMO_OPTION": "Magnum Ammo Option",
|
||||
"O_UZI_AMMO_OPTION": "Uzi Ammo Option",
|
||||
"O_HARPOON_AMMO_OPTION": "Harpoon Ammo Option",
|
||||
"O_M16_AMMO_OPTION": "M16 Ammo Option",
|
||||
"O_GRENADE_AMMO_OPTION": "Grenade Launcher Ammo Option",
|
||||
"O_SMALL_MEDIPACK_OPTION": "Small Medipack Option",
|
||||
"O_LARGE_MEDIPACK_OPTION": "Large Medipack Option",
|
||||
"O_FLARES_OPTION": "Flare Option",
|
||||
"O_PUZZLE_ITEM_1": "Puzzle Item 1",
|
||||
"O_PUZZLE_ITEM_2": "Puzzle Item 2",
|
||||
"O_PUZZLE_ITEM_3": "Puzzle Item 3",
|
||||
"O_PUZZLE_ITEM_4": "Puzzle Item 4",
|
||||
"O_PUZZLE_OPTION_1": "Puzzle Option 1",
|
||||
"O_PUZZLE_OPTION_2": "Puzzle Option 2",
|
||||
"O_PUZZLE_OPTION_3": "Puzzle Option 3",
|
||||
"O_PUZZLE_OPTION_4": "Puzzle Option 4",
|
||||
"O_PUZZLE_HOLE_1": "Puzzle Hole 1 (Empty)",
|
||||
"O_PUZZLE_HOLE_2": "Puzzle Hole 2 (Empty)",
|
||||
"O_PUZZLE_HOLE_3": "Puzzle Hole 3 (Empty)",
|
||||
"O_PUZZLE_HOLE_4": "Puzzle Hole 4 (Empty)",
|
||||
"O_PUZZLE_DONE_1": "Puzzle Hole 1 (Done)",
|
||||
"O_PUZZLE_DONE_2": "Puzzle Hole 2 (Done)",
|
||||
"O_PUZZLE_DONE_3": "Puzzle Hole 3 (Done)",
|
||||
"O_PUZZLE_DONE_4": "Puzzle Hole 4 (Done)",
|
||||
"O_SECRET_1": "Secret 1",
|
||||
"O_SECRET_2": "Secret 2",
|
||||
"O_SECRET_3": "Secret 3",
|
||||
"O_KEY_ITEM_1": "Key 1",
|
||||
"O_KEY_ITEM_2": "Key 2",
|
||||
"O_KEY_ITEM_3": "Key 3",
|
||||
"O_KEY_ITEM_4": "Key 4",
|
||||
"O_KEY_OPTION_1": "Key Option 1",
|
||||
"O_KEY_OPTION_2": "Key Option 2",
|
||||
"O_KEY_OPTION_3": "Key Option 3",
|
||||
"O_KEY_OPTION_4": "Key Option 4",
|
||||
"O_KEY_HOLE_1": "Key Hole 1",
|
||||
"O_KEY_HOLE_2": "Key Hole 2",
|
||||
"O_KEY_HOLE_3": "Key Hole 3",
|
||||
"O_KEY_HOLE_4": "Key Hole 4",
|
||||
"O_PICKUP_ITEM_1": "Pickup Item 1",
|
||||
"O_PICKUP_ITEM_2": "Pickup Item 2",
|
||||
"O_PICKUP_OPTION_1": "Pickup Option 1",
|
||||
"O_PICKUP_OPTION_2": "Pickup Option 2",
|
||||
"O_SPHERE_OF_DOOM_1": "Dragon Explosion 1",
|
||||
"O_SPHERE_OF_DOOM_2": "Dragon Explosion 2",
|
||||
"O_SPHERE_OF_DOOM_3": "Dragon Explosion 3",
|
||||
"O_ALARM_SOUND": "Alarm",
|
||||
"O_BIRD_TWEETER_1": "Dripping Water",
|
||||
"O_DINO": "T-Rex",
|
||||
"O_BIRD_TWEETER_2": "Singing Birds",
|
||||
"O_CLOCK_CHIMES": "Bartoli Hideout clock",
|
||||
"O_DRAGON_BONES_1": "Placeholder",
|
||||
"O_DRAGON_BONES_2": "Dragon Bones Front",
|
||||
"O_DRAGON_BONES_3": "Dragon Bones Back",
|
||||
"O_HOT_LIQUID": "Extra Fire",
|
||||
"O_BOAT_BITS": "Boat Bits",
|
||||
"O_MINE": "Aquatic Mine",
|
||||
"O_INV_BACKGROUND": "Menu Background",
|
||||
"O_FX_RESERVED": "Gray disk",
|
||||
"O_GONG_BONGER": "Gong Stick",
|
||||
"O_DETONATOR_1": "Gong",
|
||||
"O_DETONATOR_2": "Detonator Box",
|
||||
"O_COPTER": "Helicopter",
|
||||
"O_EXPLOSION": "Explosion",
|
||||
"O_SPLASH": "Water Ripples",
|
||||
"O_BUBBLES": "Bubbles",
|
||||
"O_BUBBLE_EMITTER": "Bubbles Emitter",
|
||||
"O_BLOOD": "Blood",
|
||||
"O_DART_EFFECT": "Dart Effect",
|
||||
"O_FLARE_FIRE": "Flare sparks",
|
||||
"O_GLOW": "Glow",
|
||||
"O_GLOW_RESERVED": "Map Glow",
|
||||
"O_RICOCHET": "Ricochet",
|
||||
"O_TWINKLE": "Sparkles",
|
||||
"O_GUN_FLASH": "Gun Flash",
|
||||
"O_M16_FLASH": "M16 Flash",
|
||||
"O_BODY_PART": "Body Part",
|
||||
"O_CAMERA_TARGET": "Camera Target",
|
||||
"O_WATERFALL": "Waterfall Mist",
|
||||
"O_MISSILE_HARPOON": "Missile Harpoon",
|
||||
"O_MISSILE_FLAME": "Missile Flame",
|
||||
"O_MISSILE_KNIFE": "Missile Knife",
|
||||
"O_GRENADE": "Grenade",
|
||||
"O_HARPOON_BOLT": "Harpoon Bolt",
|
||||
"O_EMBER": "Ember",
|
||||
"O_EMBER_EMITTER": "Ember Emitter",
|
||||
"O_FLAME": "Flame",
|
||||
"O_FLAME_EMITTER": "Flame Emitter",
|
||||
"O_SKYBOX": "Skybox",
|
||||
"O_ALPHABET": "Alphabet",
|
||||
"O_DYING_MONK": "Dying monk",
|
||||
"O_DING_DONG": "Doorbell",
|
||||
"O_LARA_ALARM": "Alarm Bell",
|
||||
"O_MINI_COPTER": "Helicopter 2",
|
||||
"O_WINSTON": "Winston",
|
||||
"O_ASSAULT_DIGITS": "Assault Digits",
|
||||
"O_FINAL_LEVEL_COUNTER": "Final Level Counter",
|
||||
"O_CUT_SHOTGUN": "Shotgun Shower Animation",
|
||||
"O_EARTHQUAKE": "Earthquake",
|
||||
},
|
||||
|
||||
"game_strings": {
|
||||
"MISC_OFF": "Off",
|
||||
"MISC_ON": "On",
|
||||
"OSD_AMBIGUOUS_INPUT_2": "Ambiguous input: %s and %s",
|
||||
"OSD_AMBIGUOUS_INPUT_3": "Ambiguous input: %s, %s, ...",
|
||||
"OSD_COMMAND_BAD_INVOCATION": "Invalid invocation: %s",
|
||||
"OSD_COMMAND_UNAVAILABLE": "This command is not currently available",
|
||||
"OSD_COMPLETE_LEVEL": "Level complete!",
|
||||
"OSD_CONFIG_OPTION_GET": "%s is currently set to %s",
|
||||
"OSD_CONFIG_OPTION_SET": "%s changed to %s",
|
||||
"OSD_CONFIG_OPTION_UNKNOWN_OPTION": "Unknown option: %s",
|
||||
"OSD_CURRENT_HEALTH_GET": "Current Lara's health: %d",
|
||||
"OSD_CURRENT_HEALTH_SET": "Lara's health set to %d",
|
||||
"OSD_DOOR_CLOSE": "Close Sesame!",
|
||||
"OSD_DOOR_OPEN": "Open Sesame!",
|
||||
"OSD_DOOR_OPEN_FAIL": "No doors in Lara's proximity",
|
||||
"OSD_FLIPMAP_FAIL_ALREADY_OFF": "Flipmap is already OFF",
|
||||
"OSD_FLIPMAP_FAIL_ALREADY_ON": "Flipmap is already ON",
|
||||
"OSD_FLIPMAP_OFF": "Flipmap set to OFF",
|
||||
"OSD_FLIPMAP_ON": "Flipmap set to ON",
|
||||
"OSD_FLY_MODE_OFF": "Fly mode disabled",
|
||||
"OSD_FLY_MODE_ON": "Fly mode enabled",
|
||||
"OSD_GIVE_ITEM": "Added %s to Lara's inventory",
|
||||
"OSD_GIVE_ITEM_ALL_GUNS": "Lock'n'load - Lara's armed to the teeth!",
|
||||
"OSD_GIVE_ITEM_ALL_KEYS": "Surprise! Every key item Lara needs is now in her backpack.",
|
||||
"OSD_GIVE_ITEM_CHEAT": "Lara's backpack just got way heavier!",
|
||||
"OSD_HEAL_ALREADY_FULL_HP": "Lara's already at full health",
|
||||
"OSD_HEAL_SUCCESS": "Healed Lara back to full health",
|
||||
"OSD_INVALID_ITEM": "Unknown item: %s",
|
||||
"OSD_INVALID_LEVEL": "Invalid level",
|
||||
"OSD_INVALID_OBJECT": "Invalid object",
|
||||
"OSD_INVALID_ROOM": "Invalid room: %d. Valid rooms are 0-%d",
|
||||
"OSD_INVALID_SAMPLE": "Invalid sound: %d",
|
||||
"OSD_INVALID_SAVE_SLOT": "Invalid save slot %d",
|
||||
"OSD_KILL": "Bye-bye!",
|
||||
"OSD_KILL_ALL": "Poof! %d enemies gone!",
|
||||
"OSD_KILL_ALL_FAIL": "Uh-oh, there are no enemies left to kill...",
|
||||
"OSD_KILL_FAIL": "No enemy nearby...",
|
||||
"OSD_LOAD_GAME": "Loaded game from save slot %d",
|
||||
"OSD_LOAD_GAME_FAIL_INVALID_SLOT": "Invalid save slot %d",
|
||||
"OSD_LOAD_GAME_FAIL_UNAVAILABLE_SLOT": "Save slot %d is not available",
|
||||
"OSD_OBJECT_NOT_FOUND": "Object not found",
|
||||
"OSD_PLAY_LEVEL": "Loading %s",
|
||||
"OSD_POS_GET": "Room: %d\nPosition: %.3f, %.3f, %.3f\nRotation: %.3f,%.3f,%.3f",
|
||||
"OSD_POS_SET_ITEM": "Teleported to object: %s",
|
||||
"OSD_POS_SET_ITEM_FAIL": "Failed to teleport to object: %s",
|
||||
"OSD_POS_SET_POS": "Teleported to position: %.3f %.3f %.3f",
|
||||
"OSD_POS_SET_POS_FAIL": "Failed to teleport to position: %.3f %.3f %.3f",
|
||||
"OSD_POS_SET_ROOM": "Teleported to room: %d",
|
||||
"OSD_POS_SET_ROOM_FAIL": "Failed to teleport to room: %d",
|
||||
"OSD_SAVE_GAME": "Saved game to save slot %d",
|
||||
"OSD_SAVE_GAME_FAIL": "Cannot save the game in the current state",
|
||||
"OSD_SAVE_GAME_FAIL_INVALID_SLOT": "Invalid save slot %d",
|
||||
"OSD_SOUND_AVAILABLE_SAMPLES": "Available sounds: %s",
|
||||
"OSD_SOUND_PLAYING_SAMPLE": "Playing sound %d",
|
||||
"OSD_UNKNOWN_COMMAND": "Unknown command: %s",
|
||||
},
|
||||
}
|
22
data/tr2/version.rc
Normal file
22
data/tr2/version.rc
Normal file
|
@ -0,0 +1,22 @@
|
|||
1 VERSIONINFO
|
||||
FILEVERSION 0,0,0,0
|
||||
PRODUCTVERSION 0,0,0,0
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "080904E4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "rr-"
|
||||
VALUE "FileDescription", "Tomb Raider II: Community Edition"
|
||||
VALUE "FileVersion", "{version}"
|
||||
VALUE "InternalName", "TR2X"
|
||||
VALUE "OriginalFilename", "TR2X.exe"
|
||||
VALUE "ProductName", "Tomb Raider II: Community Edition"
|
||||
VALUE "ProductVersion", "{version}"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x809, 1252
|
||||
END
|
||||
END
|
82
docs/tr2/CHANGELOG.md
Normal file
82
docs/tr2/CHANGELOG.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
## [Unreleased](https://github.com/LostArtefacts/TR2X/compare/stable...develop) - ××××-××-××
|
||||
- added `/sfx` command
|
||||
- added `/nextlevel` alias to `/endlevel` console command
|
||||
- added `/quit` alias to `/exit` console command
|
||||
- changed `/set` console command to do fuzzy matching (LostArtefacts/libtrx#38)
|
||||
- fixed crash in the `/set` console command (regression from 0.3)
|
||||
- fixed using console in cutscenes immediately exiting the game (regression from 0.3)
|
||||
- fixed Lara remaining tilted when teleporting off a vehicle while on a slope (#275, regression from 0.3)
|
||||
- fixed `/endlevel` displaying a success message in the title screen
|
||||
|
||||
## [0.3](https://github.com/LostArtefacts/TR2X/compare/0.2-460-g4721b93...0.3) - 2024-09-20
|
||||
- added new console commands:
|
||||
- `/endlevel`
|
||||
- `/demo`
|
||||
- `/title`
|
||||
- `/play [level]`
|
||||
- `/load [slot]`
|
||||
- `/save [slot]`
|
||||
- `/exit`
|
||||
- `/fly`
|
||||
- `/give`
|
||||
- `/kill`
|
||||
- `/flip`
|
||||
- `/set`
|
||||
- added an ability to remap the console key (#163)
|
||||
- added `/tp` console command's ability to teleport to specific items
|
||||
- added `/fly` console command's ability to open nearest doors
|
||||
- added an option to fix M16 accuracy while running (#45)
|
||||
- added a .NET-based configuration tool (#197)
|
||||
- changed the default flare key from `/` to `.` to avoid conflicts with the console (#163)
|
||||
- fixed numeric keys interfering with the demos (#172)
|
||||
- fixed explosions sometimes being drawn too dark (#187)
|
||||
- fixed killing the T-Rex with a grenade launcher crashing the game (#168)
|
||||
- fixed secret rewards not displaying shotgun ammo (#159)
|
||||
- fixed controls dialog remapping being too sensitive (#5)
|
||||
- fixed `/tp` console command during special animations in HSH and Offshore Rig (#178, regression from 0.2)
|
||||
- fixed `/hp` console command taking arbitrary integers
|
||||
- fixed console commands being able to interfere with demos, cutscenes and the title screen (#182, #179, regression from 0.2)
|
||||
- fixed console registering key inputs too eagerly (regression from 0.2)
|
||||
- fixed console not being drawn in cutscenes (#180, regression from 0.2)
|
||||
- fixed sounds not playing under certain circumstances (#113, regression from 0.2)
|
||||
- fixed the excessive pitch and playback speed correction for music files with sampling rate other than 44100 Hz (LostArtefacts/TR1X#1417, regression from 0.2)
|
||||
- fixed a crash potential with certain music files (regression from 0.2)
|
||||
- fixed enemy movement patterns in demo 1 and demo 3 (#98, regression from 0.1)
|
||||
- fixed underwater creatures dying (#98, regression from 0.1)
|
||||
- fixed a crash when spawning enemy drops (#125, regression from 0.1)
|
||||
- fixed how sprites are shaded (#134, regression from 0.1.1)
|
||||
- fixed enemies unable to climb (#138, regression from 0.1)
|
||||
- fixed items not being reset between level loads (#142, regression from 0.1)
|
||||
- fixed pulling the dagger from the dragon not activating triggers (#148, regression from 0.1)
|
||||
- fixed the music at the beginning of Offshore Rig not playing (#150, regression from 0.1)
|
||||
- fixed wade animation when moving from deep to shallow water (#231, regression from 0.1)
|
||||
- fixed the distorted skybox in room 5 of Barkhang Monastery (#196)
|
||||
- improved initial level load time by lazy-loading audio samples (#114)
|
||||
- improved crash debug information (#137)
|
||||
- improved the console caret sprite (#91)
|
||||
|
||||
## [0.2](https://github.com/LostArtefacts/TR2X/compare/0.1.1...0.2) - 2024-05-07
|
||||
- added dev console with the following commands:
|
||||
- `/pos`
|
||||
- `/tp [room_num]`
|
||||
- `/tp [x] [y] [z]`
|
||||
- `/hp`
|
||||
- `/hp [num]`
|
||||
- `/heal`
|
||||
- changed the music backend from WinMM to libtrx (SDL + libav)
|
||||
- changed the sound backend from DirectX to libtrx (SDL + libav)
|
||||
- fixed seams around underwater portals (#76, regression from 0.1)
|
||||
- fixed Lara's climb down camera angle (#78, regression from 0.1)
|
||||
- fixed healthbar and airbar flashing the wrong way when at low values (#82, regression from 0.1)
|
||||
|
||||
## [0.1.1](https://github.com/LostArtefacts/TR2X/compare/0.1...0.1.1) - 2024-04-27
|
||||
- fixed Lara's shadow with z-buffer option on (#64, regression from 0.1)
|
||||
- fixed rare camera issues (#65, regression from 0.1)
|
||||
- fixed flat rectangle colors (#70, regression from 0.1)
|
||||
- fixed medpacks staying open after use in Lara's inventory (#69, regression from 0.1)
|
||||
- fixed pickup sprites UI drawn forever in Lara's Home (#68, regression from 0.1)
|
||||
|
||||
## [0.1](https://github.com/rr-/TR2X/compare/...0.1) - 2024-04-26
|
||||
- added version string to the inventory
|
||||
- fixed CDAudio not playing on certain versions (uses PaulD patch)
|
||||
- fixed TGA screenshots crashing the game
|
65
docs/tr2/COMMANDS.md
Normal file
65
docs/tr2/COMMANDS.md
Normal file
|
@ -0,0 +1,65 @@
|
|||
# Commands
|
||||
TR2X introduces a developer console, by default accessible with the <kbd>/</kbd> key.
|
||||
Currently supported commands:
|
||||
|
||||
- `/pos`
|
||||
Retrieves precise information about Lara's coordinates. Knowledge is power!
|
||||
|
||||
- `/tp {room_number}`
|
||||
`/tp {x} {y} {z}`
|
||||
Instant travel! Teleports Lara into a random spot within the specified room or the specified X,Y,Z coordinates.
|
||||
|
||||
- `/hp`
|
||||
`/hp {health}`
|
||||
Sets Lara's health points to the specified value. Tougher trials await!
|
||||
|
||||
- `/heal`
|
||||
Tough day, Lara? Heals our girl back to full health.
|
||||
|
||||
- `/fly`
|
||||
Turns on the fly cheat. Why even walk?
|
||||
|
||||
- `/give {item_name}`
|
||||
`/give {num} {item_name}`
|
||||
`/give all`
|
||||
`/give guns`
|
||||
`/give keys`
|
||||
Gives Lara an item. Try `/give guns` to arm her to the teeth, and `/give keys` to get her all important puzzle items. Ain't nobody got time for searching!
|
||||
|
||||
- `/kill`
|
||||
`/kill all`
|
||||
`/kill {enemy_type}`
|
||||
Tired of rats, spiders, tigers, Italian mafia and literally every living thing trying to spoil your adventure? This command instantly disposes of the nearest enemy, or kills them all at once.
|
||||
|
||||
- `/flip`
|
||||
`/flip off`
|
||||
`/flip on`
|
||||
Switches the global flipmap on or off, turning the reality around you on its head.
|
||||
|
||||
- `/endlevel`
|
||||
Ends the current level. Ideal for speedruns.
|
||||
|
||||
- `/level {num}`
|
||||
`/level {name}`
|
||||
`/play {num}`
|
||||
`/play {name}`
|
||||
Plays the specified level.
|
||||
|
||||
- `/load {slot_num}`
|
||||
Loads the specified savegame slot.
|
||||
|
||||
- `/save {slot_num}`
|
||||
Saves the game to the specified savegame slot.
|
||||
|
||||
- `/demo`
|
||||
Starts the demo.
|
||||
|
||||
- `/title`
|
||||
Exits the game to main screen.
|
||||
|
||||
- `/exit`
|
||||
Closes the game.
|
||||
|
||||
- `/sfx`
|
||||
- `/sfx {sound}`
|
||||
Plays a given sound sample.
|
81
docs/tr2/README.md
Normal file
81
docs/tr2/README.md
Normal file
|
@ -0,0 +1,81 @@
|
|||
<p align="center">
|
||||
<img alt="TR2X logo" src="data/logo-light-theme.png#gh-light-mode-only" width="400"/>
|
||||
<img alt="TR2X logo" src="data/logo-dark-theme.png#gh-dark-mode-only" width="400"/>
|
||||
</p>
|
||||
|
||||
TR2X is an open-source decompilation project for Tomb Raider 2, created as a
|
||||
sequel to the successful [TR1X](https://github.com/LostArtefacts/TR1X/) project
|
||||
for Tomb Raider 1. Our project is in the early stages, and our main focus is on
|
||||
decompiling as much of the game as possible.
|
||||
|
||||
## The situation
|
||||
|
||||
TR2X draws inspiration from existing decompilation efforts, including the
|
||||
achievements of [TR2Main](https://github.com/Arsunt/TR2Main) developed by
|
||||
[Arsunt](https://github.com/Arsunt/). However, we emphasize that TR2X is an
|
||||
independent project and does not directly copy code from TR2Main.
|
||||
|
||||
## Our Mission
|
||||
|
||||
Our mission is to fully decompile Tomb Raider 2 and to eventually enhance the
|
||||
overall gameplay experience.
|
||||
|
||||
### Key Goals
|
||||
|
||||
- **Comprehensive Decompilation:**
|
||||
Our primary objective is to achieve a thorough decompilation of Tomb Raider
|
||||
2, enabling a deeper understanding of its inner workings.
|
||||
|
||||
- **Enhancement of UK Box Version:**
|
||||
Our work is based on the UK Box version of Tomb Raider 2. Eventually we hope
|
||||
to reach feature parity with the Steam/multipatch version, and possibly
|
||||
TR2Main as well.
|
||||
|
||||
- **Cross-platform Compatibility:**
|
||||
We are committed to making Tomb Raider 2 run natively on Linux, with the
|
||||
possibility of supporting Mac systems in the future.
|
||||
|
||||
- **Transparent Development Process:**
|
||||
We value transparency and aim to maintain a transparent development process,
|
||||
ensuring regular updates and engagement with the community.
|
||||
|
||||
- **Changelog and Progress Updates:**
|
||||
We strive to provide detailed and reliable changelogs, documenting
|
||||
significant updates and changes made to TR2X. Additionally, we will keep
|
||||
the community informed about the progress of the project.
|
||||
|
||||
TR2X is currently in the early stages of development, focusing on the
|
||||
decompilation process. We recognize that there is much work to be done.
|
||||
|
||||
## Decompilation progress
|
||||
|
||||

|
||||
|
||||
## Improvements over original game
|
||||
|
||||
#### Gameplay
|
||||
|
||||
- added an option to fix M16 accuracy while running
|
||||
- fixed killing the T-Rex with a grenade launcher crashing the game
|
||||
- fixed secret rewards not displaying shotgun ammo
|
||||
|
||||
#### Visuals
|
||||
|
||||
- fixed TGA screenshots crashing the game
|
||||
|
||||
#### Audio
|
||||
|
||||
- fixed music not playing with certain game versions
|
||||
|
||||
#### Mods
|
||||
- added developer console (accessible with `/`, see [COMMANDS.md] for details)
|
||||
|
||||
## Contributions
|
||||
|
||||
Please refer to our [CONTRIBUTING.md](CONTRIBUTING.md) file for more
|
||||
information on how to contribute to the project.
|
||||
|
||||
## License
|
||||
|
||||
TR2X follows the GPL license. Please refer to the [COPYING.md](COPYING.md) file
|
||||
for further details.
|
2528
docs/tr2/progress.svg
Normal file
2528
docs/tr2/progress.svg
Normal file
File diff suppressed because it is too large
Load diff
After Width: | Height: | Size: 361 KiB |
4695
docs/tr2/progress.txt
Normal file
4695
docs/tr2/progress.txt
Normal file
File diff suppressed because it is too large
Load diff
37
src/tr2/config.c
Normal file
37
src/tr2/config.c
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "config.h"
|
||||
|
||||
#include "config_map.h"
|
||||
|
||||
#include <libtrx/config/file.h>
|
||||
|
||||
CONFIG g_Config = { 0 };
|
||||
|
||||
static const char *m_ConfigPath = "cfg/TR2X.json5";
|
||||
|
||||
const char *Config_GetPath(void)
|
||||
{
|
||||
return m_ConfigPath;
|
||||
}
|
||||
|
||||
void Config_LoadFromJSON(JSON_OBJECT *root_obj)
|
||||
{
|
||||
ConfigFile_LoadOptions(root_obj, g_ConfigOptionMap);
|
||||
}
|
||||
|
||||
void Config_DumpToJSON(JSON_OBJECT *root_obj)
|
||||
{
|
||||
ConfigFile_DumpOptions(root_obj, g_ConfigOptionMap);
|
||||
}
|
||||
|
||||
void Config_Sanitize(void)
|
||||
{
|
||||
}
|
||||
|
||||
void Config_ApplyChanges(void)
|
||||
{
|
||||
}
|
||||
|
||||
const CONFIG_OPTION *Config_GetOptionMap(void)
|
||||
{
|
||||
return g_ConfigOptionMap;
|
||||
}
|
15
src/tr2/config.h
Normal file
15
src/tr2/config.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <libtrx/config.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
bool loaded;
|
||||
|
||||
struct {
|
||||
bool fix_m16_accuracy;
|
||||
} gameplay;
|
||||
} CONFIG;
|
||||
|
||||
extern CONFIG g_Config;
|
15
src/tr2/config_map.c
Normal file
15
src/tr2/config_map.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "config_map.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
// import order guard
|
||||
#include <libtrx/config/map.h>
|
||||
// import order guard
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
const CONFIG_OPTION g_ConfigOptionMap[] = {
|
||||
#include "config_map.def"
|
||||
// guard
|
||||
{ 0 },
|
||||
};
|
1
src/tr2/config_map.def
Normal file
1
src/tr2/config_map.def
Normal file
|
@ -0,0 +1 @@
|
|||
CFG_BOOL(g_Config, gameplay.fix_m16_accuracy, true)
|
5
src/tr2/config_map.h
Normal file
5
src/tr2/config_map.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <libtrx/config/option.h>
|
||||
|
||||
extern const CONFIG_OPTION g_ConfigOptionMap[];
|
3058
src/tr2/decomp/decomp.c
Normal file
3058
src/tr2/decomp/decomp.c
Normal file
File diff suppressed because it is too large
Load diff
136
src/tr2/decomp/decomp.h
Normal file
136
src/tr2/decomp/decomp.h
Normal file
|
@ -0,0 +1,136 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
int32_t __cdecl GameInit(void);
|
||||
int32_t __stdcall WinMain(
|
||||
HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine,
|
||||
int32_t nShowCmd);
|
||||
const char *__cdecl DecodeErrorMessage(int32_t error_code);
|
||||
int32_t __cdecl RenderErrorBox(int32_t error_code);
|
||||
void __cdecl ScreenshotPCX(void);
|
||||
size_t __cdecl CompPCX(
|
||||
uint8_t *bitmap, int32_t width, int32_t height, RGB_888 *palette,
|
||||
uint8_t **pcx_data);
|
||||
size_t __cdecl EncodeLinePCX(const uint8_t *src, int32_t width, uint8_t *dst);
|
||||
size_t __cdecl EncodePutPCX(uint8_t value, uint8_t num, uint8_t *buffer);
|
||||
void __cdecl Screenshot(LPDDS screen);
|
||||
bool __cdecl DInputCreate(void);
|
||||
void __cdecl DInputRelease(void);
|
||||
void __cdecl WinInReadKeyboard(uint8_t *input_data);
|
||||
int32_t __cdecl WinGameStart(void);
|
||||
void __cdecl Shell_Shutdown(void);
|
||||
int16_t __cdecl TitleSequence(void);
|
||||
void __cdecl WinVidSetMinWindowSize(int32_t width, int32_t height);
|
||||
void __cdecl WinVidSetMaxWindowSize(int32_t width, int32_t height);
|
||||
void __cdecl WinVidClearMinWindowSize(void);
|
||||
void __cdecl WinVidClearMaxWindowSize(void);
|
||||
int32_t __cdecl CalculateWindowWidth(int32_t width, int32_t height);
|
||||
int32_t __cdecl CalculateWindowHeight(int32_t width, int32_t height);
|
||||
bool __cdecl WinVidGetMinMaxInfo(LPMINMAXINFO info);
|
||||
HWND __cdecl WinVidFindGameWindow(void);
|
||||
bool __cdecl WinVidSpinMessageLoop(bool need_wait);
|
||||
void __cdecl WinVidShowGameWindow(int32_t cmd_show);
|
||||
void __cdecl WinVidHideGameWindow(void);
|
||||
void __cdecl WinVidSetGameWindowSize(int32_t width, int32_t height);
|
||||
bool __cdecl ShowDDrawGameWindow(bool active);
|
||||
bool __cdecl HideDDrawGameWindow(void);
|
||||
HRESULT __cdecl DDrawSurfaceCreate(LPDDSDESC dsp, LPDDS *surface);
|
||||
HRESULT __cdecl DDrawSurfaceRestoreLost(
|
||||
LPDDS surface1, LPDDS surface2, bool blank);
|
||||
bool __cdecl WinVidClearBuffer(LPDDS surface, LPRECT rect, DWORD fill_color);
|
||||
HRESULT __cdecl WinVidBufferLock(LPDDS surface, LPDDSDESC desc, DWORD flags);
|
||||
HRESULT __cdecl WinVidBufferUnlock(LPDDS surface, LPDDSDESC desc);
|
||||
bool __cdecl WinVidCopyBitmapToBuffer(LPDDS surface, const BYTE *bitmap);
|
||||
DWORD __cdecl GetRenderBitDepth(uint32_t rgb_bit_count);
|
||||
void __thiscall WinVidGetColorBitMasks(
|
||||
COLOR_BIT_MASKS *bm, LPDDPIXELFORMAT pixel_format);
|
||||
void __cdecl BitMaskGetNumberOfBits(
|
||||
uint32_t bit_mask, uint32_t *bit_depth, uint32_t *bit_offset);
|
||||
DWORD __cdecl CalculateCompatibleColor(
|
||||
const COLOR_BIT_MASKS *mask, int32_t red, int32_t green, int32_t blue,
|
||||
int32_t alpha);
|
||||
bool __cdecl WinVidGetDisplayMode(DISPLAY_MODE *disp_mode);
|
||||
bool __cdecl WinVidGoFullScreen(DISPLAY_MODE *disp_mode);
|
||||
bool __cdecl WinVidGoWindowed(
|
||||
int32_t width, int32_t height, DISPLAY_MODE *disp_mode);
|
||||
void __cdecl WinVidSetDisplayAdapter(DISPLAY_ADAPTER *disp_adapter);
|
||||
void __cdecl Game_SetCutsceneTrack(int32_t track);
|
||||
int32_t __cdecl Game_Cutscene_Start(int32_t level_num);
|
||||
void __cdecl Misc_InitCinematicRooms(void);
|
||||
int32_t __cdecl Game_Cutscene_Control(int32_t nframes);
|
||||
void __cdecl CutscenePlayer_Control(int16_t item_num);
|
||||
void __cdecl Lara_Control_Cutscene(int16_t item_num);
|
||||
void __cdecl CutscenePlayer1_Initialise(int16_t item_num);
|
||||
void __cdecl CutscenePlayerGen_Initialise(int16_t item_num);
|
||||
int32_t __cdecl Level_Initialise(
|
||||
int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type);
|
||||
void __cdecl CreateScreenBuffers(void);
|
||||
void __cdecl CreatePrimarySurface(void);
|
||||
void __cdecl CreateBackBuffer(void);
|
||||
void __cdecl CreateClipper(void);
|
||||
void __cdecl CreateWindowPalette(void);
|
||||
void __cdecl CreateZBuffer(void);
|
||||
int32_t __cdecl GetZBufferDepth(void);
|
||||
void __cdecl CreateRenderBuffer(void);
|
||||
void __cdecl CreatePictureBuffer(void);
|
||||
void __cdecl ClearBuffers(DWORD flags, DWORD fill_color);
|
||||
void __cdecl UpdateFrame(bool need_run_message_loop, LPRECT rect);
|
||||
void __cdecl RestoreLostBuffers(void);
|
||||
void __cdecl WaitPrimaryBufferFlip(void);
|
||||
bool __cdecl RenderInit(void);
|
||||
void __cdecl RenderStart(bool is_reset);
|
||||
void __cdecl RenderFinish(bool need_to_clear_textures);
|
||||
bool __cdecl ApplySettings(const APP_SETTINGS *new_settings);
|
||||
void __cdecl FmvBackToGame(void);
|
||||
void __cdecl GameApplySettings(APP_SETTINGS *new_settings);
|
||||
void __cdecl UpdateGameResolution(void);
|
||||
bool __cdecl D3DCreate(void);
|
||||
void __cdecl D3DRelease(void);
|
||||
void __cdecl Enumerate3DDevices(DISPLAY_ADAPTER *adapter);
|
||||
HRESULT __stdcall Enum3DDevicesCallback(
|
||||
GUID FAR *lpGuid, LPTSTR lpDeviceDescription, LPTSTR lpDeviceName,
|
||||
LPD3DDEVICEDESC_V2 lpD3DHWDeviceDesc, LPD3DDEVICEDESC_V2 lpD3DHELDeviceDesc,
|
||||
LPVOID lpContext);
|
||||
bool __cdecl D3DIsSupported(LPD3DDEVICEDESC_V2 desc);
|
||||
bool __cdecl D3DSetViewport(void);
|
||||
void __cdecl D3DDeviceCreate(LPDDS lpBackBuffer);
|
||||
void __cdecl Direct3DRelease(void);
|
||||
bool __cdecl Direct3DInit(void);
|
||||
bool __cdecl DDrawCreate(LPGUID lpGUID);
|
||||
void __cdecl DDrawRelease(void);
|
||||
void __cdecl GameWindowCalculateSizeFromClient(int32_t *width, int32_t *height);
|
||||
void __cdecl GameWindowCalculateSizeFromClientByZero(
|
||||
int32_t *width, int32_t *height);
|
||||
bool __thiscall CompareVideoModes(
|
||||
const DISPLAY_MODE *mode1, const DISPLAY_MODE *mode2);
|
||||
bool __cdecl WinVidGetDisplayModes(void);
|
||||
HRESULT __stdcall EnumDisplayModesCallback(
|
||||
LPDDSDESC lpDDSurfaceDesc, LPVOID lpContext);
|
||||
bool __cdecl WinVidInit(void);
|
||||
bool __cdecl WinVidGetDisplayAdapters(void);
|
||||
bool __cdecl EnumerateDisplayAdapters(
|
||||
DISPLAY_ADAPTER_LIST *display_adapter_list);
|
||||
bool __cdecl WinVidRegisterGameWindowClass(void);
|
||||
LRESULT CALLBACK
|
||||
WinVidGameWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
|
||||
BOOL WINAPI EnumDisplayAdaptersCallback(
|
||||
GUID FAR *lpGUID, LPTSTR lpDriverDescription, LPTSTR lpDriverName,
|
||||
LPVOID lpContext);
|
||||
void __cdecl WinVidResizeGameWindow(HWND hWnd, int32_t edge, LPRECT rect);
|
||||
bool __cdecl WinVidCheckGameWindowPalette(HWND hWnd);
|
||||
bool __cdecl WinVidCreateGameWindow(void);
|
||||
void __cdecl WinVidFreeWindow(void);
|
||||
void __cdecl WinVidExitMessage(void);
|
||||
DISPLAY_ADAPTER_NODE *__cdecl WinVidGetDisplayAdapter(const GUID *guid_ptr);
|
||||
void __cdecl WinVidStart(void);
|
||||
void __cdecl WinVidFinish(void);
|
||||
int32_t __cdecl Misc_Move3DPosTo3DPos(
|
||||
PHD_3DPOS *src_pos, const PHD_3DPOS *dst_pos, int32_t velocity,
|
||||
PHD_ANGLE ang_add);
|
||||
int32_t __cdecl LevelCompleteSequence(void);
|
||||
void __cdecl S_LoadSettings(void);
|
||||
void __cdecl S_SaveSettings(void);
|
109
src/tr2/decomp/effects.c
Normal file
109
src/tr2/decomp/effects.c
Normal file
|
@ -0,0 +1,109 @@
|
|||
#include "decomp/effects.h"
|
||||
|
||||
#include "game/effects.h"
|
||||
#include "game/items.h"
|
||||
#include "game/matrix.h"
|
||||
#include "game/random.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/types.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
int32_t __cdecl Effect_ExplodingDeath(
|
||||
const int16_t item_num, const int32_t mesh_bits, const int16_t damage)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
const OBJECT *const object = &g_Objects[item->object_id];
|
||||
|
||||
S_CalculateLight(item->pos.x, item->pos.y, item->pos.z, item->room_num);
|
||||
|
||||
const FRAME_INFO *const best_frame = Item_GetBestFrame(item);
|
||||
|
||||
Matrix_PushUnit();
|
||||
g_MatrixPtr->_03 = 0;
|
||||
g_MatrixPtr->_13 = 0;
|
||||
g_MatrixPtr->_23 = 0;
|
||||
g_MatrixPtr->_23 = 0;
|
||||
|
||||
Matrix_RotYXZ(item->rot.y, item->rot.x, item->rot.z);
|
||||
Matrix_TranslateRel(
|
||||
best_frame->offset.x, best_frame->offset.y, best_frame->offset.z);
|
||||
|
||||
const int16_t *mesh_rots = best_frame->mesh_rots;
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
|
||||
// main mesh
|
||||
int32_t bit = 1;
|
||||
if ((mesh_bits & bit) && (item->mesh_bits & bit)) {
|
||||
const int16_t fx_num = Effect_Create(item->room_num);
|
||||
if (fx_num != NO_ITEM) {
|
||||
FX *const fx = &g_Effects[fx_num];
|
||||
fx->pos.x = item->pos.x + (g_MatrixPtr->_03 >> W2V_SHIFT);
|
||||
fx->pos.y = item->pos.y + (g_MatrixPtr->_13 >> W2V_SHIFT);
|
||||
fx->pos.z = item->pos.z + (g_MatrixPtr->_23 >> W2V_SHIFT);
|
||||
fx->rot.y = (Random_GetControl() - 0x4000) * 2;
|
||||
fx->room_num = item->room_num;
|
||||
fx->speed = Random_GetControl() >> 8;
|
||||
fx->fall_speed = -Random_GetControl() >> 8;
|
||||
fx->counter = damage;
|
||||
fx->object_id = O_BODY_PART;
|
||||
fx->frame_num = object->mesh_idx;
|
||||
fx->shade = g_LsAdder - 0x300;
|
||||
}
|
||||
item->mesh_bits &= ~bit;
|
||||
}
|
||||
|
||||
// additional meshes
|
||||
const int32_t *bone = &g_AnimBones[object->bone_idx];
|
||||
const int16_t *extra_rotation = (int16_t *)item->data;
|
||||
for (int32_t i = 1; i < object->mesh_count; i++) {
|
||||
uint32_t bone_flags = *bone++;
|
||||
if (bone_flags & BF_MATRIX_POP) {
|
||||
Matrix_Pop();
|
||||
}
|
||||
if (bone_flags & BF_MATRIX_PUSH) {
|
||||
Matrix_Push();
|
||||
}
|
||||
|
||||
Matrix_TranslateRel(bone[0], bone[1], bone[2]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
|
||||
if (extra_rotation != NULL
|
||||
&& bone_flags & (BF_ROT_X | BF_ROT_Y | BF_ROT_Z)) {
|
||||
if (bone_flags & BF_ROT_Y) {
|
||||
Matrix_RotY(*extra_rotation++);
|
||||
}
|
||||
if (bone_flags & BF_ROT_X) {
|
||||
Matrix_RotX(*extra_rotation++);
|
||||
}
|
||||
if (bone_flags & BF_ROT_Z) {
|
||||
Matrix_RotZ(*extra_rotation++);
|
||||
}
|
||||
}
|
||||
|
||||
bit <<= 1;
|
||||
if ((mesh_bits & bit) && (item->mesh_bits & bit)) {
|
||||
const int16_t fx_num = Effect_Create(item->room_num);
|
||||
if (fx_num != NO_ITEM) {
|
||||
FX *const fx = &g_Effects[fx_num];
|
||||
fx->pos.x = item->pos.x + (g_MatrixPtr->_03 >> W2V_SHIFT);
|
||||
fx->pos.y = item->pos.y + (g_MatrixPtr->_13 >> W2V_SHIFT);
|
||||
fx->pos.z = item->pos.z + (g_MatrixPtr->_23 >> W2V_SHIFT);
|
||||
fx->rot.y = (Random_GetControl() - 0x4000) * 2;
|
||||
fx->room_num = item->room_num;
|
||||
fx->speed = Random_GetControl() >> 8;
|
||||
fx->fall_speed = -Random_GetControl() >> 8;
|
||||
fx->counter = damage;
|
||||
fx->object_id = O_BODY_PART;
|
||||
fx->frame_num = object->mesh_idx + i;
|
||||
fx->shade = g_LsAdder - 0x300;
|
||||
}
|
||||
item->mesh_bits &= ~bit;
|
||||
}
|
||||
|
||||
bone += 3;
|
||||
}
|
||||
|
||||
Matrix_Pop();
|
||||
|
||||
return !(item->mesh_bits & (INT32_MAX >> (31 - object->mesh_count)));
|
||||
}
|
6
src/tr2/decomp/effects.h
Normal file
6
src/tr2/decomp/effects.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int32_t __cdecl Effect_ExplodingDeath(
|
||||
int16_t item_num, int32_t mesh_bits, int16_t damage);
|
422
src/tr2/decomp/stats.c
Normal file
422
src/tr2/decomp/stats.c
Normal file
|
@ -0,0 +1,422 @@
|
|||
#include "decomp/stats.h"
|
||||
|
||||
#include "game/input.h"
|
||||
#include "game/music.h"
|
||||
#include "game/overlay.h"
|
||||
#include "game/requester.h"
|
||||
#include "game/text.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/types.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/log.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
// TODO: consolidate with STATISTICS_INFO
|
||||
typedef struct {
|
||||
uint32_t timer;
|
||||
uint32_t ammo_used;
|
||||
uint32_t ammo_hits;
|
||||
uint32_t distance;
|
||||
uint32_t kills;
|
||||
uint8_t found_secrets; // this is no longer a bitmask
|
||||
uint8_t total_secrets; // this is not present in STATISTICS_INFO
|
||||
uint8_t medipacks;
|
||||
} STATS;
|
||||
|
||||
static STATS M_GetEndGameStats(void)
|
||||
{
|
||||
STATS result = { 0 };
|
||||
|
||||
const int32_t total_levels = g_GameFlow.num_levels - g_GameFlow.num_demos;
|
||||
for (int32_t i = LV_FIRST; i < total_levels; i++) {
|
||||
result.timer += g_SaveGame.start[i].statistics.timer;
|
||||
result.ammo_used += g_SaveGame.start[i].statistics.shots;
|
||||
result.ammo_hits += g_SaveGame.start[i].statistics.hits;
|
||||
result.kills += g_SaveGame.start[i].statistics.kills;
|
||||
result.distance += g_SaveGame.start[i].statistics.distance;
|
||||
result.medipacks += g_SaveGame.start[i].statistics.medipacks;
|
||||
|
||||
// TODO: #170, consult GFE_NUM_SECRETS rather than hardcoding this
|
||||
if (i < total_levels - 2) {
|
||||
for (int32_t j = 0; j < 3; j++) {
|
||||
if (g_SaveGame.start[i].statistics.secrets & (1 << j)) {
|
||||
result.found_secrets++;
|
||||
}
|
||||
result.total_secrets++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void __cdecl ShowGymStatsText(const char *const time_str, const int32_t type)
|
||||
{
|
||||
char text1[32];
|
||||
char text2[32];
|
||||
|
||||
if (g_StatsRequester.ready) {
|
||||
if (!Requester_Display(&g_StatsRequester, 1, 1)) {
|
||||
g_InputDB = 0;
|
||||
g_Input = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
g_StatsRequester.no_selector = 1;
|
||||
Requester_SetSize(&g_StatsRequester, 7, -32);
|
||||
g_StatsRequester.line_height = 18;
|
||||
g_StatsRequester.items_count = 0;
|
||||
g_StatsRequester.selected = 0;
|
||||
g_StatsRequester.line_offset = 0;
|
||||
g_StatsRequester.line_old_offset = 0;
|
||||
g_StatsRequester.pix_width = 304;
|
||||
g_StatsRequester.x_pos = 0;
|
||||
g_StatsRequester.z_pos = 0;
|
||||
g_StatsRequester.pitem_strings1 = g_ValidLevelStrings1;
|
||||
g_StatsRequester.pitem_strings2 = g_ValidLevelStrings2;
|
||||
g_StatsRequester.item_string_len = MAX_LEVEL_NAME_SIZE;
|
||||
|
||||
Requester_Init(&g_StatsRequester);
|
||||
Requester_SetHeading(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_BEST_TIMES],
|
||||
REQ_CENTER, NULL, 0);
|
||||
|
||||
int32_t i;
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (!g_Assault.best_time[i]) {
|
||||
break;
|
||||
}
|
||||
|
||||
sprintf(
|
||||
text1, "%2d: %s %d", i + 1, g_GF_GameStrings[GF_S_GAME_MISC_FINISH],
|
||||
g_Assault.best_finish[i]);
|
||||
const int32_t sec = g_Assault.best_time[i] / FRAMES_PER_SECOND;
|
||||
sprintf(
|
||||
text2, "%02d:%02d.%-2d", sec / 60, sec % 60,
|
||||
g_Assault.best_time[i] % FRAMES_PER_SECOND
|
||||
/ (FRAMES_PER_SECOND / 10));
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, text1, REQ_ALIGN_LEFT, text2, REQ_ALIGN_RIGHT);
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_NO_TIMES_SET],
|
||||
REQ_CENTER, NULL, REQ_CENTER);
|
||||
}
|
||||
|
||||
g_StatsRequester.ready = 1;
|
||||
}
|
||||
|
||||
void __cdecl ShowStatsText(const char *const time_str, const int32_t type)
|
||||
{
|
||||
char buffer[32];
|
||||
|
||||
if (g_StatsRequester.ready) {
|
||||
Requester_ChangeItem(
|
||||
&g_StatsRequester, 0, g_GF_GameStrings[GF_S_GAME_MISC_TIME_TAKEN],
|
||||
REQ_ALIGN_LEFT, time_str, REQ_ALIGN_RIGHT);
|
||||
if (!Requester_Display(&g_StatsRequester, type, 1)) {
|
||||
g_InputDB = 0;
|
||||
g_Input = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
g_StatsRequester.no_selector = 1;
|
||||
Requester_SetSize(&g_StatsRequester, 7, -32);
|
||||
g_StatsRequester.line_height = 18;
|
||||
g_StatsRequester.items_count = 0;
|
||||
g_StatsRequester.selected = 0;
|
||||
g_StatsRequester.line_offset = 0;
|
||||
g_StatsRequester.line_old_offset = 0;
|
||||
g_StatsRequester.pix_width = 304;
|
||||
g_StatsRequester.x_pos = 0;
|
||||
g_StatsRequester.z_pos = 0;
|
||||
g_StatsRequester.pitem_strings1 = g_ValidLevelStrings1;
|
||||
g_StatsRequester.pitem_strings2 = g_ValidLevelStrings2;
|
||||
g_StatsRequester.item_string_len = MAX_LEVEL_NAME_SIZE;
|
||||
|
||||
Requester_Init(&g_StatsRequester);
|
||||
Requester_SetHeading(
|
||||
&g_StatsRequester, g_GF_LevelNames[g_CurrentLevel], REQ_CENTER, NULL,
|
||||
0);
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_TIME_TAKEN],
|
||||
REQ_ALIGN_LEFT, time_str, REQ_ALIGN_RIGHT);
|
||||
|
||||
if (g_GF_NumSecrets) {
|
||||
char *ptr = buffer;
|
||||
int32_t num_secrets = 0;
|
||||
for (int32_t i = 0; i < 3; i++) {
|
||||
if (g_SaveGame.statistics.secrets & (1 << i)) {
|
||||
*ptr++ = 127 + i;
|
||||
num_secrets++;
|
||||
} else {
|
||||
*ptr++ = ' ';
|
||||
*ptr++ = ' ';
|
||||
*ptr++ = ' ';
|
||||
}
|
||||
}
|
||||
*ptr++ = '\0';
|
||||
if (num_secrets == 0) {
|
||||
sprintf(buffer, g_GF_GameStrings[GF_S_GAME_MISC_NONE]);
|
||||
}
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_SECRETS_FOUND],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
}
|
||||
|
||||
sprintf(buffer, "%d", g_SaveGame.statistics.kills);
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_KILLS],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
sprintf(buffer, "%d", g_SaveGame.statistics.shots);
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_AMMO_USED],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
sprintf(buffer, "%d", g_SaveGame.statistics.hits);
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_HITS],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
if ((g_SaveGame.statistics.medipacks & 1) != 0) {
|
||||
sprintf(buffer, "%d.5", g_SaveGame.statistics.medipacks >> 1);
|
||||
} else {
|
||||
sprintf(buffer, "%d.0", g_SaveGame.statistics.medipacks >> 1);
|
||||
}
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_HEALTH_PACKS_USED],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
const int32_t distance = g_SaveGame.statistics.distance / 445;
|
||||
if (distance < 1000) {
|
||||
sprintf(buffer, "%dm", distance);
|
||||
} else {
|
||||
sprintf(buffer, "%d.%02dkm", distance / 1000, distance % 100);
|
||||
}
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_DISTANCE_TRAVELLED],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
g_StatsRequester.ready = 1;
|
||||
}
|
||||
|
||||
void __cdecl ShowEndStatsText(void)
|
||||
{
|
||||
char buffer[32];
|
||||
|
||||
if (g_StatsRequester.ready) {
|
||||
if (!Requester_Display(&g_StatsRequester, 0, 1)) {
|
||||
g_InputDB = 0;
|
||||
g_Input = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
g_StatsRequester.no_selector = 1;
|
||||
Requester_SetSize(&g_StatsRequester, 7, -32);
|
||||
g_StatsRequester.line_height = 18;
|
||||
g_StatsRequester.items_count = 0;
|
||||
g_StatsRequester.selected = 0;
|
||||
g_StatsRequester.line_offset = 0;
|
||||
g_StatsRequester.line_old_offset = 0;
|
||||
g_StatsRequester.pix_width = 304;
|
||||
g_StatsRequester.x_pos = 0;
|
||||
g_StatsRequester.z_pos = 0;
|
||||
g_StatsRequester.pitem_strings1 = g_ValidLevelStrings1;
|
||||
g_StatsRequester.pitem_strings2 = g_ValidLevelStrings2;
|
||||
g_StatsRequester.item_string_len = MAX_LEVEL_NAME_SIZE;
|
||||
|
||||
Requester_Init(&g_StatsRequester);
|
||||
Requester_SetHeading(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_FINAL_STATISTICS], 0,
|
||||
0, 0);
|
||||
|
||||
const STATS stats = M_GetEndGameStats();
|
||||
|
||||
const int32_t sec = stats.timer / FRAMES_PER_SECOND;
|
||||
sprintf(
|
||||
buffer, "%02d:%02d:%02d", (sec / 60) / 60, (sec / 60) % 60, sec % 60);
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_TIME_TAKEN],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
sprintf(
|
||||
buffer, "%d %s %d", stats.found_secrets,
|
||||
g_GF_GameStrings[GF_S_GAME_MISC_OF], stats.total_secrets);
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_SECRETS_FOUND],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
sprintf(buffer, "%d", stats.kills);
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_KILLS],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
sprintf(buffer, "%d", stats.ammo_used);
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_AMMO_USED],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
sprintf(buffer, "%d", stats.ammo_hits);
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_HITS],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
if ((stats.medipacks & 1) != 0) {
|
||||
sprintf(buffer, "%d.5", stats.medipacks >> 1);
|
||||
} else {
|
||||
sprintf(buffer, "%d.0", stats.medipacks >> 1);
|
||||
}
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_HEALTH_PACKS_USED],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
const int32_t distance = stats.distance / 445;
|
||||
if (distance < 1000) {
|
||||
sprintf(buffer, "%dm", distance);
|
||||
} else {
|
||||
sprintf(buffer, "%d.%02dkm", distance / 1000, distance % 100);
|
||||
}
|
||||
Requester_AddItem(
|
||||
&g_StatsRequester, g_GF_GameStrings[GF_S_GAME_MISC_DISTANCE_TRAVELLED],
|
||||
REQ_ALIGN_LEFT, buffer, REQ_ALIGN_RIGHT);
|
||||
|
||||
g_StatsRequester.ready = 1;
|
||||
}
|
||||
|
||||
int32_t __cdecl LevelStats(const int32_t level_num)
|
||||
{
|
||||
START_INFO *const start = &g_SaveGame.start[level_num];
|
||||
start->statistics.timer = g_SaveGame.statistics.timer;
|
||||
start->statistics.shots = g_SaveGame.statistics.shots;
|
||||
start->statistics.hits = g_SaveGame.statistics.hits;
|
||||
start->statistics.distance = g_SaveGame.statistics.distance;
|
||||
start->statistics.kills = g_SaveGame.statistics.kills;
|
||||
start->statistics.secrets = g_SaveGame.statistics.secrets;
|
||||
start->statistics.medipacks = g_SaveGame.statistics.medipacks;
|
||||
|
||||
const int32_t sec = g_SaveGame.statistics.timer / FRAMES_PER_SECOND;
|
||||
char buffer[100];
|
||||
sprintf(buffer, "%02d:%02d:%02d", sec / 3600, (sec / 60) % 60, sec % 60);
|
||||
|
||||
Music_Play(g_GameFlow.level_complete_track, false);
|
||||
|
||||
TempVideoAdjust(g_HiRes, 1.0);
|
||||
FadeToPal(30, g_GamePalette8);
|
||||
Overlay_HideGameInfo();
|
||||
S_CopyScreenToBuffer();
|
||||
|
||||
while (g_Input & IN_SELECT) {
|
||||
Input_Update();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
S_InitialisePolyList(0);
|
||||
S_CopyBufferToScreen();
|
||||
|
||||
Input_Update();
|
||||
|
||||
if (g_GF_OverrideDir != (GAME_FLOW_DIR)-1) {
|
||||
break;
|
||||
}
|
||||
|
||||
ShowStatsText(buffer, 0);
|
||||
Text_Draw();
|
||||
S_OutputPolyList();
|
||||
S_DumpScreen();
|
||||
|
||||
if (g_InputDB & IN_SELECT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Requester_Shutdown(&g_StatsRequester);
|
||||
|
||||
CreateStartInfo(level_num + 1);
|
||||
g_SaveGame.current_level = level_num + 1;
|
||||
start->available = 0;
|
||||
S_FadeToBlack();
|
||||
TempVideoRemove();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t __cdecl GameStats(const int32_t level_num)
|
||||
{
|
||||
START_INFO *const start = &g_SaveGame.start[level_num];
|
||||
start->statistics.timer = g_SaveGame.statistics.timer;
|
||||
start->statistics.shots = g_SaveGame.statistics.shots;
|
||||
start->statistics.hits = g_SaveGame.statistics.hits;
|
||||
start->statistics.distance = g_SaveGame.statistics.distance;
|
||||
start->statistics.kills = g_SaveGame.statistics.kills;
|
||||
start->statistics.secrets = g_SaveGame.statistics.secrets;
|
||||
start->statistics.medipacks = g_SaveGame.statistics.medipacks;
|
||||
|
||||
Overlay_HideGameInfo();
|
||||
while (g_Input & IN_SELECT) {
|
||||
Input_Update();
|
||||
}
|
||||
|
||||
while (true) {
|
||||
S_InitialisePolyList(0);
|
||||
S_CopyBufferToScreen();
|
||||
|
||||
Input_Update();
|
||||
|
||||
if (g_GF_OverrideDir != (GAME_FLOW_DIR)-1) {
|
||||
break;
|
||||
}
|
||||
|
||||
ShowEndStatsText();
|
||||
Text_Draw();
|
||||
S_OutputPolyList();
|
||||
S_DumpScreen();
|
||||
|
||||
if (g_InputDB & IN_SELECT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Requester_Shutdown(&g_StatsRequester);
|
||||
|
||||
g_SaveGame.bonus_flag = 1;
|
||||
for (int32_t level = LV_FIRST; level <= g_GameFlow.num_levels; level++) {
|
||||
ModifyStartInfo(level);
|
||||
}
|
||||
g_SaveGame.current_level = LV_FIRST;
|
||||
|
||||
S_DontDisplayPicture();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t __cdecl AddAssaultTime(uint32_t time)
|
||||
{
|
||||
ASSAULT_STATS *const stats = &g_Assault;
|
||||
|
||||
int32_t insert_idx = -1;
|
||||
for (int32_t i = 0; i < MAX_ASSAULT_TIMES; i++) {
|
||||
if (stats->best_time[i] == 0 || time < stats->best_time[i]) {
|
||||
insert_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (insert_idx == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32_t i = MAX_ASSAULT_TIMES - 1; i > insert_idx; i--) {
|
||||
stats->best_finish[i] = stats->best_finish[i - 1];
|
||||
stats->best_time[i] = stats->best_time[i - 1];
|
||||
}
|
||||
|
||||
stats->finish_count++;
|
||||
stats->best_time[insert_idx] = time;
|
||||
stats->best_finish[insert_idx] = stats->finish_count;
|
||||
return true;
|
||||
}
|
11
src/tr2/decomp/stats.h
Normal file
11
src/tr2/decomp/stats.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void __cdecl ShowGymStatsText(const char *time_str, int32_t type);
|
||||
void __cdecl ShowStatsText(const char *time_str, int32_t type);
|
||||
void __cdecl ShowEndStatsText(void);
|
||||
|
||||
int32_t __cdecl LevelStats(int32_t level_num);
|
||||
int32_t __cdecl GameStats(int32_t level_num);
|
||||
int32_t __cdecl AddAssaultTime(uint32_t time);
|
293
src/tr2/game/background.c
Normal file
293
src/tr2/game/background.c
Normal file
|
@ -0,0 +1,293 @@
|
|||
#include "game/background.h"
|
||||
|
||||
#include "game/hwr.h"
|
||||
#include "global/const.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define TEXTURE_WIDTH 256
|
||||
#define TEXTURE_HEIGHT 256
|
||||
|
||||
void __cdecl BGND_Make640x480(uint8_t *bitmap, RGB_888 *palette)
|
||||
{
|
||||
if (g_TextureFormat.bpp >= 16) {
|
||||
g_BGND_PaletteIndex = -1;
|
||||
} else {
|
||||
g_BGND_PaletteIndex = CreateTexturePalette(palette);
|
||||
}
|
||||
|
||||
const int32_t buf_size = 640 * 480 * 2;
|
||||
uint8_t *buf = game_malloc(buf_size, GBUF_TEMP_ALLOC);
|
||||
UT_MemBlt(buf, 0, 0, 256, 256, 256, bitmap, 0, 0, 640);
|
||||
BGND_AddTexture(0, buf, g_BGND_PaletteIndex, palette);
|
||||
|
||||
UT_MemBlt(buf, 0, 0, 256, 256, 256, bitmap, 256, 0, 640);
|
||||
BGND_AddTexture(1, buf, g_BGND_PaletteIndex, palette);
|
||||
|
||||
UT_MemBlt(buf, 0, 0, 128, 256, 256, bitmap, 512, 0, 640);
|
||||
UT_MemBlt(buf, 128, 0, 128, 224, 256, bitmap, 512, 256, 640);
|
||||
BGND_AddTexture(2, buf, g_BGND_PaletteIndex, palette);
|
||||
|
||||
UT_MemBlt(buf, 0, 0, 256, 224, 256, bitmap, 0, 256, 640);
|
||||
BGND_AddTexture(3, buf, g_BGND_PaletteIndex, palette);
|
||||
|
||||
UT_MemBlt(buf, 0, 0, 256, 224, 256, bitmap, 256, 256, 640);
|
||||
BGND_AddTexture(4, buf, g_BGND_PaletteIndex, palette);
|
||||
|
||||
game_free(buf_size);
|
||||
|
||||
BGND_GetPageHandles();
|
||||
|
||||
g_BGND_PictureIsReady = true;
|
||||
}
|
||||
|
||||
int32_t __cdecl BGND_AddTexture(
|
||||
const int32_t tile_idx, uint8_t *const bitmap, const int32_t pal_index,
|
||||
const RGB_888 *const bmp_pal)
|
||||
{
|
||||
int32_t page_index;
|
||||
if (pal_index < 0) {
|
||||
uint8_t *bmp_src = &bitmap[TEXTURE_WIDTH * TEXTURE_HEIGHT];
|
||||
uint16_t *bmp_dst =
|
||||
&((uint16_t *)bitmap)[TEXTURE_WIDTH * TEXTURE_HEIGHT];
|
||||
for (int32_t i = 0; i < TEXTURE_WIDTH * TEXTURE_HEIGHT; i++) {
|
||||
bmp_src--;
|
||||
bmp_dst--;
|
||||
|
||||
const RGB_888 *const color = &bmp_pal[*bmp_src];
|
||||
|
||||
*bmp_dst = (1 << 15) | (((uint16_t)color->red >> 3) << 10)
|
||||
| (((uint16_t)color->green >> 3) << 5)
|
||||
| (((uint16_t)color->blue >> 3));
|
||||
}
|
||||
|
||||
page_index = AddTexturePage16(256, 256, bmp_src);
|
||||
} else {
|
||||
page_index = AddTexturePage8(256, 256, bitmap, pal_index);
|
||||
}
|
||||
|
||||
g_BGND_TexturePageIndexes[tile_idx] = page_index >= 0 ? page_index : -1;
|
||||
return page_index;
|
||||
}
|
||||
|
||||
void __cdecl BGND_GetPageHandles(void)
|
||||
{
|
||||
for (int32_t i = 0; i < BGND_MAX_TEXTURE_PAGES; i++) {
|
||||
g_BGND_PageHandles[i] = g_BGND_TexturePageIndexes[i] >= 0
|
||||
? GetTexturePageHandle(g_BGND_TexturePageIndexes[i])
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl BGND_DrawInGameBlack(void)
|
||||
{
|
||||
HWR_EnableZBuffer(false, false);
|
||||
DrawQuad(
|
||||
(float)g_PhdWinMinX, (float)g_PhdWinMinY, (float)g_PhdWinWidth,
|
||||
(float)g_PhdWinHeight, 0);
|
||||
HWR_EnableZBuffer(true, true);
|
||||
}
|
||||
|
||||
void __cdecl DrawQuad(
|
||||
const float sx, const float sy, const float width, const float height,
|
||||
const D3DCOLOR color)
|
||||
{
|
||||
D3DTLVERTEX vertex[4];
|
||||
|
||||
vertex[0].sx = sx;
|
||||
vertex[0].sy = sy;
|
||||
|
||||
vertex[1].sx = sx + width;
|
||||
vertex[1].sy = sy;
|
||||
|
||||
vertex[2].sx = sx;
|
||||
vertex[2].sy = sy + height;
|
||||
|
||||
vertex[3].sx = sx + width;
|
||||
vertex[3].sy = sy + height;
|
||||
|
||||
for (int32_t i = 0; i < 4; i++) {
|
||||
vertex[i].sz = 0;
|
||||
vertex[i].rhw = g_FltRhwONearZ;
|
||||
vertex[i].color = RGBA_SETALPHA(color, 0xFF);
|
||||
vertex[i].specular = 0;
|
||||
}
|
||||
|
||||
HWR_TexSource(0);
|
||||
HWR_EnableColorKey(false);
|
||||
HWR_DrawPrimitive(D3DPT_TRIANGLESTRIP, &vertex, 4, true);
|
||||
}
|
||||
|
||||
void __cdecl BGND_DrawInGameBackground(void)
|
||||
{
|
||||
const OBJECT *const obj = &g_Objects[O_INV_BACKGROUND];
|
||||
if (!obj->loaded) {
|
||||
BGND_DrawInGameBlack();
|
||||
return;
|
||||
}
|
||||
|
||||
const int16_t *mesh_ptr = g_Meshes[obj->mesh_idx];
|
||||
mesh_ptr += 5;
|
||||
const int32_t num_vertices = *mesh_ptr++;
|
||||
mesh_ptr += num_vertices * 3;
|
||||
|
||||
const int32_t num_normals = *mesh_ptr++;
|
||||
if (num_normals >= 0) {
|
||||
mesh_ptr += num_normals * 3;
|
||||
} else {
|
||||
mesh_ptr -= num_normals;
|
||||
}
|
||||
const int32_t num_quads = *mesh_ptr++;
|
||||
if (num_quads < 1) {
|
||||
BGND_DrawInGameBlack();
|
||||
return;
|
||||
}
|
||||
|
||||
mesh_ptr += 4;
|
||||
const int32_t texture_idx = *mesh_ptr++;
|
||||
|
||||
const PHD_TEXTURE *texture = &g_PhdTextureInfo[texture_idx];
|
||||
const HWR_TEXTURE_HANDLE tex_source = g_HWR_PageHandles[texture->tex_page];
|
||||
const int32_t tu = texture->uv[0].u / PHD_HALF;
|
||||
const int32_t tv = texture->uv[0].v / PHD_HALF;
|
||||
const int32_t t_width = texture->uv[2].u / PHD_HALF - tu + 1;
|
||||
const int32_t t_height = texture->uv[2].v / PHD_HALF - tv + 1;
|
||||
|
||||
HWR_EnableZBuffer(false, false);
|
||||
|
||||
const int32_t y_count = 6;
|
||||
const int32_t x_count = 8;
|
||||
|
||||
int32_t y_current = 0;
|
||||
for (int32_t y = 0; y < y_count; y++) {
|
||||
const int32_t y_next = g_PhdWinHeight + y_current;
|
||||
|
||||
int32_t x_current = 0;
|
||||
for (int32_t x = 0; x < x_count; x++) {
|
||||
const int32_t x_next = x_current + g_PhdWinWidth;
|
||||
|
||||
const int32_t y0 = g_PhdWinMinY + y_current / y_count;
|
||||
const int32_t x0 = g_PhdWinMinX + x_current / x_count;
|
||||
const int32_t x1 = g_PhdWinMinX + x_next / x_count;
|
||||
const int32_t y1 = g_PhdWinMinY + y_next / y_count;
|
||||
|
||||
const D3DCOLOR color[4] = {
|
||||
BGND_CenterLighting(x0, y0, g_PhdWinWidth, g_PhdWinHeight),
|
||||
BGND_CenterLighting(x1, y0, g_PhdWinWidth, g_PhdWinHeight),
|
||||
BGND_CenterLighting(x0, y1, g_PhdWinWidth, g_PhdWinHeight),
|
||||
BGND_CenterLighting(x1, y1, g_PhdWinWidth, g_PhdWinHeight),
|
||||
};
|
||||
|
||||
DrawTextureTile(
|
||||
x0, y0, x1 - x0, y1 - y0, tex_source, tu, tv, t_width, t_height,
|
||||
color[0], color[1], color[2], color[3]);
|
||||
|
||||
x_current = x_next;
|
||||
}
|
||||
y_current = y_next;
|
||||
}
|
||||
|
||||
HWR_EnableZBuffer(true, true);
|
||||
}
|
||||
|
||||
void __cdecl DrawTextureTile(
|
||||
const int32_t sx, const int32_t sy, const int32_t width,
|
||||
const int32_t height, const HWR_TEXTURE_HANDLE tex_source, const int32_t tu,
|
||||
const int32_t tv, const int32_t t_width, const int32_t t_height,
|
||||
const D3DCOLOR color0, const D3DCOLOR color1, const D3DCOLOR color2,
|
||||
const D3DCOLOR color3)
|
||||
{
|
||||
const D3DVALUE sx0 = sx;
|
||||
const D3DVALUE sy0 = sy;
|
||||
const D3DVALUE sx1 = sx + width;
|
||||
const D3DVALUE sy1 = sy + height;
|
||||
|
||||
const double uv_adjust = g_UVAdd / (double)PHD_ONE;
|
||||
const D3DVALUE tu0 = tu / 256.0 + uv_adjust;
|
||||
const D3DVALUE tv0 = tv / 256.0 + uv_adjust;
|
||||
const D3DVALUE tu1 = (tu + t_width) / 256.0 - uv_adjust;
|
||||
const D3DVALUE tv1 = (tv + t_height) / 256.0 - uv_adjust;
|
||||
|
||||
D3DTLVERTEX vertex[4] = {
|
||||
{
|
||||
.sx = sx0,
|
||||
.sy = sy0,
|
||||
.tu = tu0,
|
||||
.tv = tv0,
|
||||
.color = color0,
|
||||
},
|
||||
{
|
||||
.sx = sx1,
|
||||
.sy = sy0,
|
||||
.tu = tu1,
|
||||
.tv = tv0,
|
||||
.color = color1,
|
||||
},
|
||||
{
|
||||
.sx = sx0,
|
||||
.sy = sy1,
|
||||
.tu = tu0,
|
||||
.tv = tv1,
|
||||
.color = color2,
|
||||
},
|
||||
{
|
||||
.sx = sx1,
|
||||
.sy = sy1,
|
||||
.tu = tu1,
|
||||
.tv = tv1,
|
||||
.color = color3,
|
||||
},
|
||||
};
|
||||
|
||||
for (int32_t i = 0; i < 4; i++) {
|
||||
vertex[i].sz = 0.995;
|
||||
vertex[i].rhw = g_RhwFactor / g_FltFarZ;
|
||||
vertex[i].specular = 0;
|
||||
}
|
||||
|
||||
HWR_TexSource(tex_source);
|
||||
HWR_EnableColorKey(0);
|
||||
HWR_DrawPrimitive(D3DPT_TRIANGLESTRIP, &vertex, 4, true);
|
||||
}
|
||||
|
||||
D3DCOLOR __cdecl BGND_CenterLighting(
|
||||
const int32_t x, const int32_t y, const int32_t width, const int32_t height)
|
||||
{
|
||||
const double x_dist = (double)(x - (width / 2)) / (double)width;
|
||||
const double y_dist = (double)(y - (height / 2)) / (double)height;
|
||||
|
||||
int32_t light = 256 - (sqrt(x_dist * x_dist + y_dist * y_dist) * 300.0);
|
||||
CLAMP(light, 0, 255);
|
||||
|
||||
return RGBA_MAKE(light, light, light, 0xFF);
|
||||
}
|
||||
|
||||
void __cdecl BGND_Free(void)
|
||||
{
|
||||
for (int32_t i = 0; i < BGND_MAX_TEXTURE_PAGES; i++) {
|
||||
if (g_BGND_TexturePageIndexes[i] >= 0) {
|
||||
SafeFreeTexturePage(g_BGND_TexturePageIndexes[i]);
|
||||
g_BGND_TexturePageIndexes[i] = -1;
|
||||
}
|
||||
g_BGND_PageHandles[i] = 0;
|
||||
}
|
||||
|
||||
if (g_BGND_PaletteIndex >= 0) {
|
||||
SafeFreePalette(g_BGND_PaletteIndex);
|
||||
g_BGND_PaletteIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool __cdecl BGND_Init(void)
|
||||
{
|
||||
g_BGND_PictureIsReady = false;
|
||||
g_BGND_PaletteIndex = -1;
|
||||
for (int32_t i = 0; i < BGND_MAX_TEXTURE_PAGES; i++) {
|
||||
g_BGND_TexturePageIndexes[i] = -1;
|
||||
}
|
||||
return true;
|
||||
}
|
25
src/tr2/game/background.h
Normal file
25
src/tr2/game/background.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
#define BGND_MAX_TEXTURE_PAGES 5
|
||||
|
||||
bool __cdecl BGND_Init(void);
|
||||
void __cdecl BGND_Free(void);
|
||||
|
||||
void __cdecl BGND_Make640x480(uint8_t *bitmap, RGB_888 *palette);
|
||||
int32_t __cdecl BGND_AddTexture(
|
||||
int32_t tile_idx, uint8_t *bitmap, int32_t pal_index,
|
||||
const RGB_888 *bmp_pal);
|
||||
void __cdecl BGND_GetPageHandles(void);
|
||||
void __cdecl BGND_DrawInGameBlack(void);
|
||||
void __cdecl DrawQuad(
|
||||
float sx, float sy, float width, float height, D3DCOLOR color);
|
||||
void __cdecl BGND_DrawInGameBackground(void);
|
||||
void __cdecl DrawTextureTile(
|
||||
int32_t sx, int32_t sy, int32_t width, int32_t height,
|
||||
HWR_TEXTURE_HANDLE tex_source, int32_t tu, int32_t tv, int32_t t_width,
|
||||
int32_t t_height, D3DCOLOR color0, D3DCOLOR color1, D3DCOLOR color2,
|
||||
D3DCOLOR color3);
|
||||
D3DCOLOR __cdecl BGND_CenterLighting(
|
||||
int32_t x, int32_t y, int32_t width, int32_t height);
|
8
src/tr2/game/backpack.c
Normal file
8
src/tr2/game/backpack.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "game/inventory/backpack.h"
|
||||
|
||||
#include <libtrx/game/backpack.h>
|
||||
|
||||
bool Backpack_AddItem(const GAME_OBJECT_ID object_id)
|
||||
{
|
||||
return Inv_AddItem(object_id);
|
||||
}
|
422
src/tr2/game/box.c
Normal file
422
src/tr2/game/box.c
Normal file
|
@ -0,0 +1,422 @@
|
|||
#include "game/box.h"
|
||||
|
||||
#include "game/random.h"
|
||||
#include "game/room.h"
|
||||
#include "global/const.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#define BOX_OVERLAP_BITS 0x3FFF
|
||||
#define BOX_SEARCH_NUM 0x7FFF
|
||||
#define BOX_END_BIT 0x8000
|
||||
#define BOX_NUM_BITS (~BOX_END_BIT) // = 0x7FFF
|
||||
#define BOX_STALK_DIST 3 // tiles
|
||||
#define BOX_ESCAPE_DIST 5 // tiles
|
||||
#define BOX_MAX_EXPANSION 5
|
||||
|
||||
#define BOX_BIFF (WALL_L / 2) // = 0x200 = 512
|
||||
#define BOX_CLIP_LEFT 1
|
||||
#define BOX_CLIP_RIGHT 2
|
||||
#define BOX_CLIP_TOP 4
|
||||
#define BOX_CLIP_BOTTOM 8
|
||||
#define BOX_CLIP_ALL \
|
||||
(BOX_CLIP_LEFT | BOX_CLIP_RIGHT | BOX_CLIP_TOP | BOX_CLIP_BOTTOM) // = 15
|
||||
#define BOX_CLIP_SECONDARY 16
|
||||
|
||||
int32_t __cdecl Box_SearchLOT(LOT_INFO *const lot, const int32_t expansion)
|
||||
{
|
||||
int16_t *zone;
|
||||
if (lot->fly) {
|
||||
zone = g_FlyZone[g_FlipStatus];
|
||||
} else {
|
||||
zone = g_GroundZone[BOX_ZONE(lot->step)][g_FlipStatus];
|
||||
}
|
||||
|
||||
const int16_t search_zone = zone[lot->head];
|
||||
for (int32_t i = 0; i < expansion; i++) {
|
||||
if (lot->head == NO_BOX) {
|
||||
lot->tail = NO_BOX;
|
||||
return false;
|
||||
}
|
||||
|
||||
BOX_NODE *node = &lot->node[lot->head];
|
||||
const BOX_INFO *box = &g_Boxes[lot->head];
|
||||
|
||||
bool done = false;
|
||||
int32_t index = box->overlap_index & BOX_OVERLAP_BITS;
|
||||
while (!done) {
|
||||
int16_t box_num = g_Overlap[index++];
|
||||
if ((box_num & BOX_END_BIT) != 0) {
|
||||
done = true;
|
||||
box_num &= BOX_NUM_BITS;
|
||||
}
|
||||
|
||||
if (search_zone != zone[box_num]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32_t change = g_Boxes[box_num].height - box->height;
|
||||
if (change > lot->step || change < lot->drop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BOX_NODE *const expand = &lot->node[box_num];
|
||||
if ((node->search_num & BOX_SEARCH_NUM)
|
||||
< (expand->search_num & BOX_SEARCH_NUM)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((node->search_num & BOX_BLOCKED_SEARCH) != 0) {
|
||||
if ((expand->search_num & BOX_SEARCH_NUM)
|
||||
== (node->search_num & BOX_SEARCH_NUM)) {
|
||||
continue;
|
||||
}
|
||||
expand->search_num = node->search_num;
|
||||
} else {
|
||||
if ((expand->search_num & BOX_SEARCH_NUM)
|
||||
== (node->search_num & BOX_SEARCH_NUM)
|
||||
&& (expand->search_num & BOX_BLOCKED_SEARCH) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((g_Boxes[box_num].overlap_index & lot->block_mask) != 0) {
|
||||
expand->search_num = node->search_num | BOX_BLOCKED_SEARCH;
|
||||
} else {
|
||||
expand->search_num = node->search_num;
|
||||
expand->exit_box = lot->head;
|
||||
}
|
||||
}
|
||||
|
||||
if (expand->next_expansion == NO_BOX && box_num != lot->tail) {
|
||||
lot->node[lot->tail].next_expansion = box_num;
|
||||
lot->tail = box_num;
|
||||
}
|
||||
}
|
||||
|
||||
lot->head = node->next_expansion;
|
||||
node->next_expansion = NO_BOX;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t __cdecl Box_UpdateLOT(LOT_INFO *const lot, const int32_t expansion)
|
||||
{
|
||||
if (lot->required_box == NO_BOX || lot->required_box == lot->target_box) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
lot->target_box = lot->required_box;
|
||||
BOX_NODE *const expand = &lot->node[lot->target_box];
|
||||
if (expand->next_expansion == NO_BOX && lot->tail != lot->target_box) {
|
||||
expand->next_expansion = lot->head;
|
||||
if (lot->head == NO_BOX) {
|
||||
lot->tail = lot->target_box;
|
||||
}
|
||||
lot->head = lot->target_box;
|
||||
}
|
||||
lot->search_num++;
|
||||
expand->search_num = lot->search_num;
|
||||
expand->exit_box = NO_BOX;
|
||||
|
||||
end:
|
||||
return Box_SearchLOT(lot, expansion);
|
||||
}
|
||||
|
||||
void __cdecl Box_TargetBox(LOT_INFO *const lot, const int16_t box_num)
|
||||
{
|
||||
const BOX_INFO *const box = &g_Boxes[box_num & BOX_NUM_BITS];
|
||||
|
||||
lot->target.z = ((box->right - box->left - 1) >> (15 - WALL_SHIFT))
|
||||
* Random_GetControl()
|
||||
+ (box->left << WALL_SHIFT) + WALL_L / 2;
|
||||
lot->target.x = ((box->bottom - box->top - 1) >> (15 - WALL_SHIFT))
|
||||
* Random_GetControl()
|
||||
+ (box->top << WALL_SHIFT) + WALL_L / 2;
|
||||
lot->required_box = box_num & BOX_NUM_BITS;
|
||||
|
||||
if (lot->fly != 0) {
|
||||
lot->target.y = box->height - STEP_L * 3 / 2;
|
||||
} else {
|
||||
lot->target.y = box->height;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __cdecl Box_StalkBox(
|
||||
const ITEM *const item, const ITEM *const enemy, const int16_t box_num)
|
||||
{
|
||||
const BOX_INFO *const box = &g_Boxes[box_num];
|
||||
|
||||
const int32_t z =
|
||||
((box->left + box->right) << (WALL_SHIFT - 1)) - enemy->pos.z;
|
||||
const int32_t x =
|
||||
((box->top + box->bottom) << (WALL_SHIFT - 1)) - enemy->pos.x;
|
||||
|
||||
const int32_t x_range = (box->bottom - box->top + BOX_STALK_DIST)
|
||||
<< WALL_SHIFT;
|
||||
const int32_t z_range = (box->right - box->left + BOX_STALK_DIST)
|
||||
<< WALL_SHIFT;
|
||||
if (x > x_range || x < -x_range || z > z_range || z < -z_range) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32_t enemy_quad = (enemy->rot.y >> 14) + 2;
|
||||
const int32_t box_quad = (z > 0) ? ((x > 0) ? DIR_SOUTH : DIR_EAST)
|
||||
: ((x > 0) ? DIR_WEST : DIR_NORTH);
|
||||
if (enemy_quad == box_quad) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32_t baddie_quad = item->pos.z > enemy->pos.z
|
||||
? (item->pos.x > x ? DIR_SOUTH : DIR_EAST)
|
||||
: (item->pos.x > x ? DIR_WEST : DIR_NORTH);
|
||||
|
||||
return enemy_quad != baddie_quad || ABS(enemy_quad - box_quad) != 2;
|
||||
}
|
||||
|
||||
int32_t __cdecl Box_EscapeBox(
|
||||
const ITEM *const item, const ITEM *const enemy, const int16_t box_num)
|
||||
{
|
||||
const BOX_INFO *const box = &g_Boxes[box_num];
|
||||
const int32_t x =
|
||||
((box->bottom + box->top) << (WALL_SHIFT - 1)) - enemy->pos.x;
|
||||
const int32_t z =
|
||||
((box->left + box->right) << (WALL_SHIFT - 1)) - enemy->pos.z;
|
||||
|
||||
const int32_t x_range = BOX_ESCAPE_DIST << WALL_SHIFT;
|
||||
const int32_t z_range = BOX_ESCAPE_DIST << WALL_SHIFT;
|
||||
if (x > -x_range && x < x_range && z > -z_range && z < z_range) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((z > 0) == (item->pos.z > enemy->pos.z))
|
||||
|| ((x > 0) == (item->pos.x > enemy->pos.x));
|
||||
}
|
||||
|
||||
int32_t __cdecl Box_ValidBox(
|
||||
const ITEM *const item, const int16_t zone_num, const int16_t box_num)
|
||||
{
|
||||
const CREATURE *const creature = item->data;
|
||||
int16_t *zone;
|
||||
if (creature->lot.fly) {
|
||||
zone = g_FlyZone[g_FlipStatus];
|
||||
} else {
|
||||
zone = g_GroundZone[BOX_ZONE(creature->lot.step)][g_FlipStatus];
|
||||
}
|
||||
|
||||
if (zone[box_num] != zone_num) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const BOX_INFO *const box = &g_Boxes[box_num];
|
||||
if ((creature->lot.block_mask & box->overlap_index) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !(
|
||||
item->pos.z > (box->left << WALL_SHIFT)
|
||||
&& item->pos.z < (box->right << WALL_SHIFT)
|
||||
&& item->pos.x > (box->top << WALL_SHIFT)
|
||||
&& item->pos.x < (box->bottom << WALL_SHIFT));
|
||||
}
|
||||
|
||||
TARGET_TYPE __cdecl Box_CalculateTarget(
|
||||
XYZ_32 *const target, const ITEM *const item, LOT_INFO *const lot)
|
||||
{
|
||||
Box_UpdateLOT(lot, BOX_MAX_EXPANSION);
|
||||
|
||||
*target = item->pos;
|
||||
|
||||
int32_t box_num = item->box_num;
|
||||
if (box_num == NO_BOX) {
|
||||
return TARGET_NONE;
|
||||
}
|
||||
|
||||
int32_t bottom = 0;
|
||||
int32_t top = 0;
|
||||
int32_t right = 0;
|
||||
int32_t left = 0;
|
||||
|
||||
BOX_INFO *box = NULL;
|
||||
int32_t box_left = 0;
|
||||
int32_t box_right = 0;
|
||||
int32_t box_top = 0;
|
||||
int32_t box_bottom = 0;
|
||||
|
||||
int32_t prime_free = BOX_CLIP_ALL;
|
||||
do {
|
||||
box = &g_Boxes[box_num];
|
||||
int32_t height = box->height;
|
||||
if (lot->fly != 0) {
|
||||
height -= WALL_L;
|
||||
}
|
||||
if (target->y > height - WALL_L) {
|
||||
target->y = height - WALL_L;
|
||||
}
|
||||
|
||||
box_left = box->left << WALL_SHIFT;
|
||||
box_right = (box->right << WALL_SHIFT) - 1;
|
||||
box_top = box->top << WALL_SHIFT;
|
||||
box_bottom = (box->bottom << WALL_SHIFT) - 1;
|
||||
|
||||
if (item->pos.z >= box_left && item->pos.z <= box_right
|
||||
&& item->pos.x >= box_top && item->pos.x <= box_bottom) {
|
||||
left = box_left;
|
||||
right = box_right;
|
||||
top = box_top;
|
||||
bottom = box_bottom;
|
||||
} else {
|
||||
if (item->pos.z < box_left) {
|
||||
if ((prime_free & BOX_CLIP_LEFT) != 0 && item->pos.x >= box_top
|
||||
&& item->pos.x <= box_bottom) {
|
||||
CLAMPL(target->z, box_left + BOX_BIFF);
|
||||
if ((prime_free & BOX_CLIP_SECONDARY) != 0) {
|
||||
return TARGET_SECONDARY;
|
||||
}
|
||||
CLAMPL(top, box_top);
|
||||
CLAMPG(bottom, box_bottom);
|
||||
prime_free = BOX_CLIP_LEFT;
|
||||
} else if (prime_free != BOX_CLIP_LEFT) {
|
||||
target->z = right - BOX_BIFF;
|
||||
if (prime_free != BOX_CLIP_ALL) {
|
||||
return TARGET_SECONDARY;
|
||||
}
|
||||
prime_free |= BOX_CLIP_SECONDARY;
|
||||
}
|
||||
} else if (item->pos.z > box_right) {
|
||||
if ((prime_free & BOX_CLIP_RIGHT) != 0 && item->pos.x >= box_top
|
||||
&& item->pos.x <= box_bottom) {
|
||||
CLAMPG(target->z, box_right - BOX_BIFF);
|
||||
if ((prime_free & BOX_CLIP_SECONDARY) != 0) {
|
||||
return TARGET_SECONDARY;
|
||||
}
|
||||
CLAMPL(top, box_top);
|
||||
CLAMPG(bottom, box_bottom);
|
||||
prime_free = BOX_CLIP_RIGHT;
|
||||
} else if (prime_free != BOX_CLIP_RIGHT) {
|
||||
target->z = left + BOX_BIFF;
|
||||
if (prime_free != BOX_CLIP_ALL) {
|
||||
return TARGET_SECONDARY;
|
||||
}
|
||||
prime_free |= BOX_CLIP_SECONDARY;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->pos.x < box_top) {
|
||||
if ((prime_free & BOX_CLIP_TOP) != 0
|
||||
&& (item->pos.z >= box_left) && item->pos.z <= box_right) {
|
||||
CLAMPL(target->x, box_top + BOX_BIFF);
|
||||
if ((prime_free & BOX_CLIP_SECONDARY) != 0) {
|
||||
return TARGET_SECONDARY;
|
||||
}
|
||||
CLAMPL(left, box_left);
|
||||
CLAMPG(right, box_right);
|
||||
prime_free = BOX_CLIP_TOP;
|
||||
} else if (prime_free != BOX_CLIP_TOP) {
|
||||
target->x = bottom - BOX_BIFF;
|
||||
if (prime_free != BOX_CLIP_ALL) {
|
||||
return TARGET_SECONDARY;
|
||||
}
|
||||
prime_free |= BOX_CLIP_SECONDARY;
|
||||
}
|
||||
} else if (item->pos.x > box_bottom) {
|
||||
if ((prime_free & BOX_CLIP_BOTTOM) != 0
|
||||
&& item->pos.z >= box_left && item->pos.z <= box_right) {
|
||||
CLAMPG(target->x, box_bottom - BOX_BIFF);
|
||||
if ((prime_free & BOX_CLIP_SECONDARY) != 0) {
|
||||
return TARGET_SECONDARY;
|
||||
}
|
||||
CLAMPL(left, box_left);
|
||||
CLAMPG(right, box_right);
|
||||
prime_free = BOX_CLIP_BOTTOM;
|
||||
} else if (prime_free != BOX_CLIP_BOTTOM) {
|
||||
target->x = top + BOX_BIFF;
|
||||
if (prime_free != BOX_CLIP_ALL) {
|
||||
return TARGET_SECONDARY;
|
||||
}
|
||||
prime_free |= BOX_CLIP_SECONDARY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (box_num == lot->target_box) {
|
||||
if ((prime_free & (BOX_CLIP_LEFT | BOX_CLIP_RIGHT)) != 0) {
|
||||
target->z = lot->target.z;
|
||||
} else if ((prime_free & BOX_CLIP_SECONDARY) == 0) {
|
||||
CLAMP(target->z, box_left + BOX_BIFF, box_right - BOX_BIFF);
|
||||
}
|
||||
|
||||
if ((prime_free & (BOX_CLIP_TOP | BOX_CLIP_BOTTOM)) != 0) {
|
||||
target->x = lot->target.x;
|
||||
} else if ((prime_free & BOX_CLIP_SECONDARY) == 0) {
|
||||
CLAMP(target->x, box_top + BOX_BIFF, box_bottom - BOX_BIFF);
|
||||
}
|
||||
|
||||
target->y = lot->target.y;
|
||||
return TARGET_PRIMARY;
|
||||
}
|
||||
|
||||
box_num = lot->node[box_num].exit_box;
|
||||
if (box_num != NO_BOX
|
||||
&& (g_Boxes[box_num].overlap_index & lot->block_mask) != 0) {
|
||||
break;
|
||||
}
|
||||
} while (box_num != NO_BOX);
|
||||
|
||||
if ((prime_free & (BOX_CLIP_LEFT | BOX_CLIP_RIGHT)) != 0) {
|
||||
target->z =
|
||||
(((box_right - box_left - WALL_L) * Random_GetControl()) >> 15)
|
||||
+ box_left + WALL_L / 2;
|
||||
} else if ((prime_free & BOX_CLIP_SECONDARY) == 0) {
|
||||
CLAMP(target->z, box_left + BOX_BIFF, box_right - BOX_BIFF);
|
||||
}
|
||||
|
||||
if ((prime_free & (BOX_CLIP_TOP | BOX_CLIP_BOTTOM)) != 0) {
|
||||
target->x =
|
||||
(((box_bottom - box_top - WALL_L) * Random_GetControl()) >> 15)
|
||||
+ box_top + WALL_L / 2;
|
||||
} else if ((prime_free & BOX_CLIP_SECONDARY) == 0) {
|
||||
CLAMP(target->x, box_top + BOX_BIFF, box_bottom - BOX_BIFF);
|
||||
}
|
||||
|
||||
if (lot->fly != 0) {
|
||||
target->y = box->height - STEP_L * 3 / 2;
|
||||
} else {
|
||||
target->y = box->height;
|
||||
}
|
||||
|
||||
return TARGET_NONE;
|
||||
}
|
||||
|
||||
int32_t __cdecl Box_BadFloor(
|
||||
const int32_t x, const int32_t y, const int32_t z, const int32_t box_height,
|
||||
const int32_t next_height, int16_t room_num, const LOT_INFO *const lot)
|
||||
{
|
||||
const SECTOR *const sector = Room_GetSector(x, y, z, &room_num);
|
||||
const int16_t box_num = sector->box;
|
||||
if (box_num == NO_BOX) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const BOX_INFO *const box = &g_Boxes[box_num];
|
||||
if ((box->overlap_index & lot->block_mask) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (box_height - box->height > lot->step
|
||||
|| box_height - box->height < lot->drop) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (box_height - box->height < -lot->step && box->height > next_height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lot->fly != 0 && y > lot->fly + box->height) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
23
src/tr2/game/box.h
Normal file
23
src/tr2/game/box.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
#define BOX_BLOCKED 0x4000
|
||||
#define BOX_BLOCKED_SEARCH 0x8000
|
||||
#define BOX_BLOCKABLE 0x8000
|
||||
#define BOX_ZONE(num) (((num) / STEP_L) - 1)
|
||||
|
||||
int32_t __cdecl Box_SearchLOT(LOT_INFO *lot, int32_t expansion);
|
||||
int32_t __cdecl Box_UpdateLOT(LOT_INFO *lot, int32_t expansion);
|
||||
void __cdecl Box_TargetBox(LOT_INFO *lot, int16_t box_num);
|
||||
int32_t __cdecl Box_StalkBox(
|
||||
const ITEM *item, const ITEM *enemy, int16_t box_num);
|
||||
int32_t __cdecl Box_EscapeBox(
|
||||
const ITEM *item, const ITEM *enemy, int16_t box_num);
|
||||
int32_t __cdecl Box_ValidBox(
|
||||
const ITEM *item, int16_t zone_num, int16_t box_num);
|
||||
TARGET_TYPE __cdecl Box_CalculateTarget(
|
||||
XYZ_32 *target, const ITEM *item, LOT_INFO *lot);
|
||||
int32_t __cdecl Box_BadFloor(
|
||||
int32_t x, int32_t y, int32_t z, int32_t box_height, int32_t next_height,
|
||||
int16_t room_num, const LOT_INFO *lot);
|
939
src/tr2/game/camera.c
Normal file
939
src/tr2/game/camera.c
Normal file
|
@ -0,0 +1,939 @@
|
|||
#include "game/camera.h"
|
||||
|
||||
#include "decomp/decomp.h"
|
||||
#include "game/items.h"
|
||||
#include "game/los.h"
|
||||
#include "game/math.h"
|
||||
#include "game/matrix.h"
|
||||
#include "game/music.h"
|
||||
#include "game/output.h"
|
||||
#include "game/random.h"
|
||||
#include "game/room.h"
|
||||
#include "game/sound.h"
|
||||
#include "global/const.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define CHASE_SPEED 10
|
||||
#define CHASE_ELEVATION (WALL_L * 3 / 2) // = 1536
|
||||
|
||||
#define COMBAT_SPEED 8
|
||||
#define COMBAT_DISTANCE (WALL_L * 5 / 2) // = 2560
|
||||
|
||||
#define LOOK_DISTANCE (WALL_L * 3 / 2) // = 1536
|
||||
#define LOOK_CLAMP (STEP_L + 50) // = 296
|
||||
#define LOOK_SPEED 4
|
||||
|
||||
#define MAX_ELEVATION (85 * PHD_DEGREE) // = 15470
|
||||
|
||||
void __cdecl Camera_Initialise(void)
|
||||
{
|
||||
Camera_ResetPosition();
|
||||
Output_AlterFOV(GAME_FOV * PHD_DEGREE);
|
||||
Camera_Update();
|
||||
}
|
||||
|
||||
void __cdecl Camera_ResetPosition(void)
|
||||
{
|
||||
assert(g_LaraItem);
|
||||
g_Camera.shift = g_LaraItem->pos.y - WALL_L;
|
||||
|
||||
g_Camera.target.x = g_LaraItem->pos.x;
|
||||
g_Camera.target.y = g_Camera.shift;
|
||||
g_Camera.target.z = g_LaraItem->pos.z;
|
||||
g_Camera.target.room_num = g_LaraItem->room_num;
|
||||
|
||||
g_Camera.pos.x = g_Camera.target.x;
|
||||
g_Camera.pos.y = g_Camera.target.y;
|
||||
g_Camera.pos.z = g_Camera.target.z - 100;
|
||||
g_Camera.pos.room_num = g_Camera.target.room_num;
|
||||
|
||||
g_Camera.target_distance = WALL_L * 3 / 2;
|
||||
g_Camera.item = NULL;
|
||||
|
||||
g_Camera.num_frames = 1;
|
||||
if (!g_Lara.extra_anim) {
|
||||
g_Camera.type = CAM_CHASE;
|
||||
}
|
||||
|
||||
g_Camera.speed = 1;
|
||||
g_Camera.flags = CF_NORMAL;
|
||||
g_Camera.bounce = 0;
|
||||
g_Camera.num = NO_CAMERA;
|
||||
g_Camera.fixed_camera = 0;
|
||||
}
|
||||
|
||||
void __cdecl Camera_Move(const GAME_VECTOR *target, int32_t speed)
|
||||
{
|
||||
g_Camera.pos.x += (target->x - g_Camera.pos.x) / speed;
|
||||
g_Camera.pos.z += (target->z - g_Camera.pos.z) / speed;
|
||||
g_Camera.pos.y += (target->y - g_Camera.pos.y) / speed;
|
||||
g_Camera.pos.room_num = target->room_num;
|
||||
|
||||
g_IsChunkyCamera = 0;
|
||||
|
||||
const SECTOR *sector = Room_GetSector(
|
||||
g_Camera.pos.x, g_Camera.pos.y, g_Camera.pos.z, &g_Camera.pos.room_num);
|
||||
int32_t height =
|
||||
Room_GetHeight(sector, g_Camera.pos.x, g_Camera.pos.y, g_Camera.pos.z)
|
||||
- STEP_L;
|
||||
|
||||
if (g_Camera.pos.y >= height && target->y >= height) {
|
||||
LOS_Check(&g_Camera.target, &g_Camera.pos);
|
||||
sector = Room_GetSector(
|
||||
g_Camera.pos.x, g_Camera.pos.y, g_Camera.pos.z,
|
||||
&g_Camera.pos.room_num);
|
||||
height = Room_GetHeight(
|
||||
sector, g_Camera.pos.x, g_Camera.pos.y, g_Camera.pos.z)
|
||||
- STEP_L;
|
||||
}
|
||||
|
||||
int32_t ceiling =
|
||||
Room_GetCeiling(sector, g_Camera.pos.x, g_Camera.pos.y, g_Camera.pos.z)
|
||||
+ STEP_L;
|
||||
if (height < ceiling) {
|
||||
ceiling = (height + ceiling) >> 1;
|
||||
height = ceiling;
|
||||
}
|
||||
|
||||
if (g_Camera.bounce > 0) {
|
||||
g_Camera.pos.y += g_Camera.bounce;
|
||||
g_Camera.target.y += g_Camera.bounce;
|
||||
g_Camera.bounce = 0;
|
||||
} else if (g_Camera.bounce < 0) {
|
||||
XYZ_32 shake = {
|
||||
.x = g_Camera.bounce * (Random_GetControl() - 0x4000) / 0x7FFF,
|
||||
.y = g_Camera.bounce * (Random_GetControl() - 0x4000) / 0x7FFF,
|
||||
.z = g_Camera.bounce * (Random_GetControl() - 0x4000) / 0x7FFF,
|
||||
};
|
||||
g_Camera.pos.x += shake.x;
|
||||
g_Camera.pos.y += shake.y;
|
||||
g_Camera.pos.z += shake.z;
|
||||
g_Camera.target.y += shake.x;
|
||||
g_Camera.target.y += shake.y;
|
||||
g_Camera.target.z += shake.z;
|
||||
g_Camera.bounce += 5;
|
||||
}
|
||||
|
||||
if (g_Camera.pos.y > height) {
|
||||
g_Camera.shift = height - g_Camera.pos.y;
|
||||
} else if (g_Camera.pos.y < ceiling) {
|
||||
g_Camera.shift = ceiling - g_Camera.pos.y;
|
||||
} else {
|
||||
g_Camera.shift = 0;
|
||||
}
|
||||
|
||||
Room_GetSector(
|
||||
g_Camera.pos.x, g_Camera.pos.y + g_Camera.shift, g_Camera.pos.z,
|
||||
&g_Camera.pos.room_num);
|
||||
|
||||
Matrix_LookAt(
|
||||
g_Camera.pos.x, g_Camera.shift + g_Camera.pos.y, g_Camera.pos.z,
|
||||
g_Camera.target.x, g_Camera.target.y, g_Camera.target.z, 0);
|
||||
|
||||
if (g_Camera.is_lara_mic) {
|
||||
g_Camera.actual_angle =
|
||||
g_Lara.torso_y_rot + g_Lara.head_y_rot + g_LaraItem->rot.y;
|
||||
g_Camera.mic_pos.x = g_LaraItem->pos.x;
|
||||
g_Camera.mic_pos.y = g_LaraItem->pos.y;
|
||||
g_Camera.mic_pos.z = g_LaraItem->pos.z;
|
||||
} else {
|
||||
g_Camera.actual_angle = Math_Atan(
|
||||
g_Camera.target.z - g_Camera.pos.z,
|
||||
g_Camera.target.x - g_Camera.pos.x);
|
||||
g_Camera.mic_pos.x = g_Camera.pos.x
|
||||
+ ((g_PhdPersp * Math_Sin(g_Camera.actual_angle)) >> W2V_SHIFT);
|
||||
g_Camera.mic_pos.z = g_Camera.pos.z
|
||||
+ ((g_PhdPersp * Math_Cos(g_Camera.actual_angle)) >> W2V_SHIFT);
|
||||
g_Camera.mic_pos.y = g_Camera.pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Camera_Clip(
|
||||
int32_t *x, int32_t *y, int32_t *h, int32_t target_x, int32_t target_y,
|
||||
int32_t target_h, int32_t left, int32_t top, int32_t right, int32_t bottom)
|
||||
{
|
||||
if ((right > left) != (target_x < left)) {
|
||||
*y = target_y + (left - target_x) * (*y - target_y) / (*x - target_x);
|
||||
*h = target_h + (left - target_x) * (*h - target_h) / (*x - target_x);
|
||||
*x = left;
|
||||
}
|
||||
|
||||
if ((bottom > top && target_y > top && (*y) < top)
|
||||
|| (bottom < top && target_y < top && (*y) > top)) {
|
||||
*x = target_x + (top - target_y) * (*x - target_x) / (*y - target_y);
|
||||
*h = target_h + (top - target_y) * (*h - target_h) / (*y - target_y);
|
||||
*y = top;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Camera_Shift(
|
||||
int32_t *x, int32_t *y, int32_t *h, int32_t target_x, int32_t target_y,
|
||||
int32_t target_h, int32_t left, int32_t top, int32_t right, int32_t bottom)
|
||||
{
|
||||
int32_t shift;
|
||||
|
||||
int32_t l_square = SQUARE(target_x - left);
|
||||
int32_t r_square = SQUARE(target_x - right);
|
||||
int32_t t_square = SQUARE(target_y - top);
|
||||
int32_t b_square = SQUARE(target_y - bottom);
|
||||
int32_t tl_square = t_square + l_square;
|
||||
int32_t tr_square = t_square + r_square;
|
||||
int32_t bl_square = b_square + l_square;
|
||||
|
||||
if (g_Camera.target_square < tl_square) {
|
||||
*x = left;
|
||||
shift = g_Camera.target_square - l_square;
|
||||
if (shift >= 0) {
|
||||
shift = Math_Sqrt(shift);
|
||||
*y = target_y + (top >= bottom ? shift : -shift);
|
||||
}
|
||||
} else if (tl_square > MIN_SQUARE) {
|
||||
*x = left;
|
||||
*y = top;
|
||||
} else if (g_Camera.target_square < bl_square) {
|
||||
*x = left;
|
||||
shift = g_Camera.target_square - l_square;
|
||||
if (shift >= 0) {
|
||||
shift = Math_Sqrt(shift);
|
||||
*y = target_y + (top < bottom ? shift : -shift);
|
||||
}
|
||||
} else if (2 * g_Camera.target_square < tr_square) {
|
||||
shift = 2 * g_Camera.target_square - t_square;
|
||||
if (shift >= 0) {
|
||||
shift = Math_Sqrt(shift);
|
||||
*x = target_x + (left < right ? shift : -shift);
|
||||
*y = top;
|
||||
}
|
||||
} else if (bl_square <= tr_square) {
|
||||
*x = right;
|
||||
*y = top;
|
||||
} else {
|
||||
*x = left;
|
||||
*y = bottom;
|
||||
}
|
||||
}
|
||||
|
||||
const SECTOR *__cdecl Camera_GoodPosition(
|
||||
int32_t x, int32_t y, int32_t z, int16_t room_num)
|
||||
{
|
||||
const SECTOR *sector = Room_GetSector(x, y, z, &room_num);
|
||||
int32_t height = Room_GetHeight(sector, x, y, z);
|
||||
int32_t ceiling = Room_GetCeiling(sector, x, y, z);
|
||||
if (y > height || y < ceiling) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sector;
|
||||
}
|
||||
|
||||
void __cdecl Camera_SmartShift(
|
||||
GAME_VECTOR *target,
|
||||
void(__cdecl *shift)(
|
||||
int32_t *x, int32_t *y, int32_t *h, int32_t target_x, int32_t target_y,
|
||||
int32_t target_h, int32_t left, int32_t top, int32_t right,
|
||||
int32_t bottom))
|
||||
{
|
||||
LOS_Check(&g_Camera.target, target);
|
||||
|
||||
const ROOM *r = &g_Rooms[g_Camera.target.room_num];
|
||||
int32_t z_sector = (g_Camera.target.z - r->pos.z) >> WALL_SHIFT;
|
||||
int32_t x_sector = (g_Camera.target.x - r->pos.x) >> WALL_SHIFT;
|
||||
int16_t item_box = r->sectors[z_sector + x_sector * r->size.z].box;
|
||||
const BOX_INFO *box = &g_Boxes[item_box];
|
||||
|
||||
int32_t left = (int32_t)box->left << WALL_SHIFT;
|
||||
int32_t top = (int32_t)box->top << WALL_SHIFT;
|
||||
int32_t right = ((int32_t)box->right << WALL_SHIFT) - 1;
|
||||
int32_t bottom = ((int32_t)box->bottom << WALL_SHIFT) - 1;
|
||||
|
||||
r = &g_Rooms[target->room_num];
|
||||
z_sector = (target->z - r->pos.z) >> WALL_SHIFT;
|
||||
x_sector = (target->x - r->pos.x) >> WALL_SHIFT;
|
||||
int16_t camera_box = r->sectors[z_sector + x_sector * r->size.z].box;
|
||||
|
||||
if (camera_box != NO_BOX
|
||||
&& (target->z < left || target->z > right || target->x < top
|
||||
|| target->x > bottom)) {
|
||||
box = &g_Boxes[camera_box];
|
||||
left = (int32_t)box->left << WALL_SHIFT;
|
||||
top = (int32_t)box->top << WALL_SHIFT;
|
||||
right = ((int32_t)box->right << WALL_SHIFT) - 1;
|
||||
bottom = ((int32_t)box->bottom << WALL_SHIFT) - 1;
|
||||
}
|
||||
|
||||
int32_t test;
|
||||
int32_t edge;
|
||||
|
||||
test = (target->z - WALL_L) | (WALL_L - 1);
|
||||
const SECTOR *good_left =
|
||||
Camera_GoodPosition(target->x, target->y, test, target->room_num);
|
||||
if (good_left) {
|
||||
camera_box = good_left->box;
|
||||
edge = (int32_t)g_Boxes[camera_box].left << WALL_SHIFT;
|
||||
if (camera_box != NO_BOX && edge < left) {
|
||||
left = edge;
|
||||
}
|
||||
} else {
|
||||
left = test;
|
||||
}
|
||||
|
||||
test = (target->z + WALL_L) & (~(WALL_L - 1));
|
||||
const SECTOR *good_right =
|
||||
Camera_GoodPosition(target->x, target->y, test, target->room_num);
|
||||
if (good_right) {
|
||||
camera_box = good_right->box;
|
||||
edge = ((int32_t)g_Boxes[camera_box].right << WALL_SHIFT) - 1;
|
||||
if (camera_box != NO_BOX && edge > right) {
|
||||
right = edge;
|
||||
}
|
||||
} else {
|
||||
right = test;
|
||||
}
|
||||
|
||||
test = (target->x - WALL_L) | (WALL_L - 1);
|
||||
const SECTOR *good_top =
|
||||
Camera_GoodPosition(test, target->y, target->z, target->room_num);
|
||||
if (good_top) {
|
||||
camera_box = good_top->box;
|
||||
edge = (int32_t)g_Boxes[camera_box].top << WALL_SHIFT;
|
||||
if (camera_box != NO_BOX && edge < top) {
|
||||
top = edge;
|
||||
}
|
||||
} else {
|
||||
top = test;
|
||||
}
|
||||
|
||||
test = (target->x + WALL_L) & (~(WALL_L - 1));
|
||||
const SECTOR *good_bottom =
|
||||
Camera_GoodPosition(test, target->y, target->z, target->room_num);
|
||||
if (good_bottom) {
|
||||
camera_box = good_bottom->box;
|
||||
edge = ((int32_t)g_Boxes[camera_box].bottom << WALL_SHIFT) - 1;
|
||||
if (camera_box != NO_BOX && edge > bottom) {
|
||||
bottom = edge;
|
||||
}
|
||||
} else {
|
||||
bottom = test;
|
||||
}
|
||||
|
||||
left += STEP_L;
|
||||
right -= STEP_L;
|
||||
top += STEP_L;
|
||||
bottom -= STEP_L;
|
||||
|
||||
GAME_VECTOR a = {
|
||||
.x = target->x,
|
||||
.y = target->y,
|
||||
.z = target->z,
|
||||
.room_num = target->room_num,
|
||||
};
|
||||
|
||||
GAME_VECTOR b = {
|
||||
.x = target->x,
|
||||
.y = target->y,
|
||||
.z = target->z,
|
||||
.room_num = target->room_num,
|
||||
};
|
||||
|
||||
int32_t noclip = 1;
|
||||
int32_t prefer_a;
|
||||
|
||||
if (ABS(target->z - g_Camera.target.z)
|
||||
> ABS(target->x - g_Camera.target.x)) {
|
||||
if (target->z < left && !good_left) {
|
||||
noclip = 0;
|
||||
prefer_a = g_Camera.pos.x < g_Camera.target.x;
|
||||
shift(
|
||||
&a.z, &a.x, &a.y, g_Camera.target.z, g_Camera.target.x,
|
||||
g_Camera.target.y, left, top, right, bottom);
|
||||
shift(
|
||||
&b.z, &b.x, &b.y, g_Camera.target.z, g_Camera.target.x,
|
||||
g_Camera.target.y, left, bottom, right, top);
|
||||
} else if (target->z > right && !good_right) {
|
||||
noclip = 0;
|
||||
prefer_a = g_Camera.pos.x < g_Camera.target.x;
|
||||
shift(
|
||||
&a.z, &a.x, &a.y, g_Camera.target.z, g_Camera.target.x,
|
||||
g_Camera.target.y, right, top, left, bottom);
|
||||
shift(
|
||||
&b.z, &b.x, &b.y, g_Camera.target.z, g_Camera.target.x,
|
||||
g_Camera.target.y, right, bottom, left, top);
|
||||
}
|
||||
|
||||
if (noclip) {
|
||||
if (target->x < top && !good_top) {
|
||||
noclip = 0;
|
||||
prefer_a = target->z < g_Camera.target.z;
|
||||
shift(
|
||||
&a.x, &a.z, &a.y, g_Camera.target.x, g_Camera.target.z,
|
||||
g_Camera.target.y, top, left, bottom, right);
|
||||
shift(
|
||||
&b.x, &b.z, &b.y, g_Camera.target.x, g_Camera.target.z,
|
||||
g_Camera.target.y, top, right, bottom, left);
|
||||
} else if (target->x > bottom && !good_bottom) {
|
||||
noclip = 0;
|
||||
prefer_a = target->z < g_Camera.target.z;
|
||||
shift(
|
||||
&a.x, &a.z, &a.y, g_Camera.target.x, g_Camera.target.z,
|
||||
g_Camera.target.y, bottom, left, top, right);
|
||||
shift(
|
||||
&b.x, &b.z, &b.y, g_Camera.target.x, g_Camera.target.z,
|
||||
g_Camera.target.y, bottom, right, top, left);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (target->x < top && !good_top) {
|
||||
noclip = 0;
|
||||
prefer_a = g_Camera.pos.z < g_Camera.target.z;
|
||||
shift(
|
||||
&a.x, &a.z, &a.y, g_Camera.target.x, g_Camera.target.z,
|
||||
g_Camera.target.y, top, left, bottom, right);
|
||||
shift(
|
||||
&b.x, &b.z, &b.y, g_Camera.target.x, g_Camera.target.z,
|
||||
g_Camera.target.y, top, right, bottom, left);
|
||||
} else if (target->x > bottom && !good_bottom) {
|
||||
noclip = 0;
|
||||
prefer_a = g_Camera.pos.z < g_Camera.target.z;
|
||||
shift(
|
||||
&a.x, &a.z, &a.y, g_Camera.target.x, g_Camera.target.z,
|
||||
g_Camera.target.y, bottom, left, top, right);
|
||||
shift(
|
||||
&b.x, &b.z, &b.y, g_Camera.target.x, g_Camera.target.z,
|
||||
g_Camera.target.y, bottom, right, top, left);
|
||||
}
|
||||
|
||||
if (noclip) {
|
||||
if (target->z < left && !good_left) {
|
||||
noclip = 0;
|
||||
prefer_a = target->x < g_Camera.target.x;
|
||||
shift(
|
||||
&a.z, &a.x, &a.y, g_Camera.target.z, g_Camera.target.x,
|
||||
g_Camera.target.y, left, top, right, bottom);
|
||||
shift(
|
||||
&b.z, &b.x, &b.y, g_Camera.target.z, g_Camera.target.x,
|
||||
g_Camera.target.y, left, bottom, right, top);
|
||||
} else if (target->z > right && !good_right) {
|
||||
noclip = 0;
|
||||
prefer_a = target->x < g_Camera.target.x;
|
||||
shift(
|
||||
&a.z, &a.x, &a.y, g_Camera.target.z, g_Camera.target.x,
|
||||
g_Camera.target.y, right, top, left, bottom);
|
||||
shift(
|
||||
&b.z, &b.x, &b.y, g_Camera.target.z, g_Camera.target.x,
|
||||
g_Camera.target.y, right, bottom, left, top);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (noclip) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefer_a) {
|
||||
prefer_a = LOS_Check(&g_Camera.target, &a) != 0;
|
||||
} else {
|
||||
prefer_a = LOS_Check(&g_Camera.target, &b) == 0;
|
||||
}
|
||||
|
||||
if (prefer_a) {
|
||||
target->x = a.x;
|
||||
target->y = a.y;
|
||||
target->z = a.z;
|
||||
} else {
|
||||
target->x = b.x;
|
||||
target->y = b.y;
|
||||
target->z = b.z;
|
||||
}
|
||||
|
||||
Room_GetSector(target->x, target->y, target->z, &target->room_num);
|
||||
}
|
||||
|
||||
void __cdecl Camera_Chase(const ITEM *item)
|
||||
{
|
||||
g_Camera.target_elevation += item->rot.x;
|
||||
g_Camera.target_elevation = MIN(g_Camera.target_elevation, MAX_ELEVATION);
|
||||
g_Camera.target_elevation = MAX(g_Camera.target_elevation, -MAX_ELEVATION);
|
||||
|
||||
int32_t distance =
|
||||
(g_Camera.target_distance * Math_Cos(g_Camera.target_elevation))
|
||||
>> W2V_SHIFT;
|
||||
int16_t angle = g_Camera.target_angle + item->rot.y;
|
||||
|
||||
g_Camera.target_square = SQUARE(distance);
|
||||
|
||||
const XYZ_32 offset = {
|
||||
.y = (g_Camera.target_distance * Math_Sin(g_Camera.target_elevation))
|
||||
>> W2V_SHIFT,
|
||||
.x = -((distance * Math_Sin(angle)) >> W2V_SHIFT),
|
||||
.z = -((distance * Math_Cos(angle)) >> W2V_SHIFT),
|
||||
};
|
||||
|
||||
GAME_VECTOR target = {
|
||||
.x = g_Camera.target.x + offset.x,
|
||||
.y = g_Camera.target.y + offset.y,
|
||||
.z = g_Camera.target.z + offset.z,
|
||||
.room_num = g_Camera.pos.room_num,
|
||||
};
|
||||
|
||||
Camera_SmartShift(&target, Camera_Shift);
|
||||
Camera_Move(&target, g_Camera.speed);
|
||||
}
|
||||
|
||||
int32_t __cdecl Camera_ShiftClamp(GAME_VECTOR *pos, int32_t clamp)
|
||||
{
|
||||
int32_t x = pos->x;
|
||||
int32_t y = pos->y;
|
||||
int32_t z = pos->z;
|
||||
const SECTOR *sector = Room_GetSector(x, y, z, &pos->room_num);
|
||||
const BOX_INFO *box = &g_Boxes[sector->box];
|
||||
|
||||
int32_t left = ((int32_t)box->left << WALL_SHIFT) + clamp;
|
||||
int32_t right = ((int32_t)box->right << WALL_SHIFT) - clamp - 1;
|
||||
if (z < left && !Camera_GoodPosition(x, y, z - clamp, pos->room_num)) {
|
||||
pos->z = left;
|
||||
} else if (
|
||||
z > right && !Camera_GoodPosition(x, y, z + clamp, pos->room_num)) {
|
||||
pos->z = right;
|
||||
}
|
||||
|
||||
int32_t top = ((int32_t)box->top << WALL_SHIFT) + clamp;
|
||||
int32_t bottom = ((int32_t)box->bottom << WALL_SHIFT) - clamp - 1;
|
||||
if (x < top && !Camera_GoodPosition(x - clamp, y, z, pos->room_num)) {
|
||||
pos->x = top;
|
||||
} else if (
|
||||
x > bottom && !Camera_GoodPosition(x + clamp, y, z, pos->room_num)) {
|
||||
pos->x = bottom;
|
||||
}
|
||||
|
||||
int32_t height = Room_GetHeight(sector, x, y, z) - clamp;
|
||||
int32_t ceiling = Room_GetCeiling(sector, x, y, z) + clamp;
|
||||
if (height < ceiling) {
|
||||
ceiling = (height + ceiling) >> 1;
|
||||
height = ceiling;
|
||||
}
|
||||
|
||||
if (y > height) {
|
||||
return height - y;
|
||||
}
|
||||
if (y < ceiling) {
|
||||
return ceiling - y;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __cdecl Camera_Combat(const ITEM *item)
|
||||
{
|
||||
g_Camera.target.z = item->pos.z;
|
||||
g_Camera.target.x = item->pos.x;
|
||||
|
||||
g_Camera.target_distance = COMBAT_DISTANCE;
|
||||
if (g_Lara.target) {
|
||||
g_Camera.target_angle = g_Lara.target_angles[0] + item->rot.y;
|
||||
g_Camera.target_elevation = g_Lara.target_angles[1] + item->rot.x;
|
||||
} else {
|
||||
g_Camera.target_angle =
|
||||
g_Lara.torso_y_rot + g_Lara.head_y_rot + item->rot.y;
|
||||
g_Camera.target_elevation =
|
||||
g_Lara.torso_x_rot + g_Lara.head_x_rot + item->rot.x;
|
||||
}
|
||||
|
||||
int32_t distance =
|
||||
(COMBAT_DISTANCE * Math_Cos(g_Camera.target_elevation)) >> W2V_SHIFT;
|
||||
int16_t angle = g_Camera.target_angle;
|
||||
|
||||
const XYZ_32 offset = {
|
||||
.y =
|
||||
+((g_Camera.target_distance * Math_Sin(g_Camera.target_elevation))
|
||||
>> W2V_SHIFT),
|
||||
.x = -((distance * Math_Sin(angle)) >> W2V_SHIFT),
|
||||
.z = -((distance * Math_Cos(angle)) >> W2V_SHIFT),
|
||||
};
|
||||
|
||||
GAME_VECTOR target = {
|
||||
.x = g_Camera.target.x + offset.x,
|
||||
.y = g_Camera.target.y + offset.y,
|
||||
.z = g_Camera.target.z + offset.z,
|
||||
.room_num = g_Camera.pos.room_num,
|
||||
};
|
||||
|
||||
if (g_Lara.water_status == LWS_UNDERWATER) {
|
||||
int32_t water_height = g_Lara.water_surface_dist + g_LaraItem->pos.y;
|
||||
if (g_Camera.target.y > water_height && water_height > target.y) {
|
||||
target.y = g_Lara.water_surface_dist + g_LaraItem->pos.y;
|
||||
target.z = g_Camera.target.z
|
||||
+ (water_height - g_Camera.target.y)
|
||||
* (target.z - g_Camera.target.z)
|
||||
/ (target.y - g_Camera.target.y);
|
||||
target.x = g_Camera.target.x
|
||||
+ (water_height - g_Camera.target.y)
|
||||
* (target.x - g_Camera.target.x)
|
||||
/ (target.y - g_Camera.target.y);
|
||||
}
|
||||
}
|
||||
|
||||
Camera_SmartShift(&target, Camera_Shift);
|
||||
Camera_Move(&target, g_Camera.speed);
|
||||
}
|
||||
|
||||
void __cdecl Camera_Look(const ITEM *item)
|
||||
{
|
||||
XYZ_32 old = {
|
||||
.x = g_Camera.target.x,
|
||||
.y = g_Camera.target.y,
|
||||
.z = g_Camera.target.z,
|
||||
};
|
||||
|
||||
g_Camera.target.z = item->pos.z;
|
||||
g_Camera.target.x = item->pos.x;
|
||||
g_Camera.target_angle =
|
||||
item->rot.y + g_Lara.torso_y_rot + g_Lara.head_y_rot;
|
||||
g_Camera.target_distance = LOOK_DISTANCE;
|
||||
g_Camera.target_elevation =
|
||||
g_Lara.torso_x_rot + g_Lara.head_x_rot + item->rot.x;
|
||||
|
||||
int32_t distance =
|
||||
(LOOK_DISTANCE * Math_Cos(g_Camera.target_elevation)) >> W2V_SHIFT;
|
||||
|
||||
g_Camera.shift =
|
||||
(-STEP_L * 2 * Math_Sin(g_Camera.target_elevation)) >> W2V_SHIFT;
|
||||
g_Camera.target.z += (g_Camera.shift * Math_Cos(item->rot.y)) >> W2V_SHIFT;
|
||||
g_Camera.target.x += (g_Camera.shift * Math_Sin(item->rot.y)) >> W2V_SHIFT;
|
||||
|
||||
if (!Camera_GoodPosition(
|
||||
g_Camera.target.x, g_Camera.target.y, g_Camera.target.z,
|
||||
g_Camera.target.room_num)) {
|
||||
g_Camera.target.x = item->pos.x;
|
||||
g_Camera.target.z = item->pos.z;
|
||||
}
|
||||
|
||||
g_Camera.target.y += Camera_ShiftClamp(&g_Camera.target, LOOK_CLAMP);
|
||||
|
||||
const XYZ_32 offset = {
|
||||
.y =
|
||||
+((g_Camera.target_distance * Math_Sin(g_Camera.target_elevation))
|
||||
>> W2V_SHIFT),
|
||||
.x = -((distance * Math_Sin(g_Camera.target_angle)) >> W2V_SHIFT),
|
||||
.z = -((distance * Math_Cos(g_Camera.target_angle)) >> W2V_SHIFT),
|
||||
};
|
||||
|
||||
GAME_VECTOR target = {
|
||||
.x = g_Camera.target.x + offset.x,
|
||||
.y = g_Camera.target.y + offset.y,
|
||||
.z = g_Camera.target.z + offset.z,
|
||||
.room_num = g_Camera.pos.room_num,
|
||||
};
|
||||
|
||||
Camera_SmartShift(&target, Camera_Clip);
|
||||
g_Camera.target.z = old.z + (g_Camera.target.z - old.z) / g_Camera.speed;
|
||||
g_Camera.target.x = old.x + (g_Camera.target.x - old.x) / g_Camera.speed;
|
||||
Camera_Move(&target, g_Camera.speed);
|
||||
}
|
||||
|
||||
void __cdecl Camera_Fixed(void)
|
||||
{
|
||||
const OBJECT_VECTOR *fixed = &g_Camera.fixed[g_Camera.num];
|
||||
GAME_VECTOR target = {
|
||||
.x = fixed->x,
|
||||
.y = fixed->y,
|
||||
.z = fixed->z,
|
||||
.room_num = fixed->data,
|
||||
};
|
||||
if (!LOS_Check(&g_Camera.target, &target)) {
|
||||
Camera_ShiftClamp(&target, STEP_L);
|
||||
}
|
||||
|
||||
g_Camera.fixed_camera = 1;
|
||||
Camera_Move(&target, g_Camera.speed);
|
||||
|
||||
if (g_Camera.timer) {
|
||||
g_Camera.timer--;
|
||||
if (!g_Camera.timer) {
|
||||
g_Camera.timer = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Camera_Update(void)
|
||||
{
|
||||
if (g_Rooms[g_Camera.pos.room_num].flags & RF_UNDERWATER) {
|
||||
Sound_Effect(SFX_UNDERWATER, NULL, SPM_ALWAYS);
|
||||
if (!g_Camera.underwater) {
|
||||
Music_SetVolume(0);
|
||||
g_Camera.underwater = 1;
|
||||
}
|
||||
} else if (g_Camera.underwater) {
|
||||
if (g_OptionMusicVolume) {
|
||||
Music_SetVolume(25 * g_OptionMusicVolume + 5);
|
||||
}
|
||||
g_Camera.underwater = 0;
|
||||
}
|
||||
|
||||
if (g_Camera.type == CAM_CINEMATIC) {
|
||||
Camera_LoadCutsceneFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_Camera.flags != CF_NO_CHUNKY) {
|
||||
g_IsChunkyCamera = 1;
|
||||
}
|
||||
|
||||
const int32_t fixed_camera = g_Camera.item != NULL
|
||||
&& (g_Camera.type == CAM_FIXED || g_Camera.type == CAM_HEAVY);
|
||||
const ITEM *const item = fixed_camera ? g_Camera.item : g_LaraItem;
|
||||
|
||||
const BOUNDS_16 *bounds = Item_GetBoundsAccurate(item);
|
||||
|
||||
int32_t y = item->pos.y;
|
||||
if (fixed_camera) {
|
||||
y += (bounds->min_y + bounds->max_y) / 2;
|
||||
} else {
|
||||
y += bounds->max_y
|
||||
+ (((int32_t)(bounds->min_y - bounds->max_y)) * 3 >> 2);
|
||||
}
|
||||
|
||||
if (g_Camera.item && !fixed_camera) {
|
||||
bounds = Item_GetBoundsAccurate(g_Camera.item);
|
||||
|
||||
const int32_t dx = g_Camera.item->pos.x - item->pos.x;
|
||||
const int32_t dz = g_Camera.item->pos.z - item->pos.z;
|
||||
const int32_t shift = Math_Sqrt(SQUARE(dx) + SQUARE(dz));
|
||||
int16_t angle = Math_Atan(dz, dx) - item->rot.y;
|
||||
|
||||
int16_t tilt = Math_Atan(
|
||||
shift,
|
||||
y - (bounds->min_y + bounds->max_y) / 2 - g_Camera.item->pos.y);
|
||||
angle >>= 1;
|
||||
tilt >>= 1;
|
||||
|
||||
if (angle > MIN_HEAD_ROTATION_CAM && angle < MAX_HEAD_ROTATION_CAM
|
||||
&& tilt > MIN_HEAD_TILT_CAM && tilt < MAX_HEAD_TILT_CAM) {
|
||||
int16_t change = angle - g_Lara.head_y_rot;
|
||||
if (change > HEAD_TURN_CAM) {
|
||||
g_Lara.head_y_rot += HEAD_TURN_CAM;
|
||||
} else if (change < -HEAD_TURN_CAM) {
|
||||
g_Lara.head_y_rot -= HEAD_TURN_CAM;
|
||||
} else {
|
||||
g_Lara.head_y_rot = angle;
|
||||
}
|
||||
|
||||
change = tilt - g_Lara.head_x_rot;
|
||||
if (change > HEAD_TURN_CAM) {
|
||||
g_Lara.head_x_rot += HEAD_TURN_CAM;
|
||||
} else if (change < -HEAD_TURN_CAM) {
|
||||
g_Lara.head_x_rot -= HEAD_TURN_CAM;
|
||||
} else {
|
||||
g_Lara.head_x_rot += change;
|
||||
}
|
||||
g_Lara.torso_x_rot = g_Lara.head_x_rot;
|
||||
g_Lara.torso_y_rot = g_Lara.head_y_rot;
|
||||
g_Camera.type = CAM_LOOK;
|
||||
g_Camera.item->looked_at = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_Camera.type == CAM_LOOK || g_Camera.type == CAM_COMBAT) {
|
||||
y -= STEP_L;
|
||||
g_Camera.target.room_num = item->room_num;
|
||||
if (g_Camera.fixed_camera) {
|
||||
g_Camera.target.y = y;
|
||||
g_Camera.speed = 1;
|
||||
} else {
|
||||
g_Camera.target.y += (y - g_Camera.target.y) >> 2;
|
||||
g_Camera.speed =
|
||||
g_Camera.type == CAM_LOOK ? LOOK_SPEED : COMBAT_SPEED;
|
||||
}
|
||||
g_Camera.fixed_camera = 0;
|
||||
if (g_Camera.type == CAM_LOOK) {
|
||||
Camera_Look(item);
|
||||
} else {
|
||||
Camera_Combat(item);
|
||||
}
|
||||
} else {
|
||||
g_Camera.target.x = item->pos.x;
|
||||
g_Camera.target.z = item->pos.z;
|
||||
|
||||
if (g_Camera.flags == CF_FOLLOW_CENTRE) {
|
||||
const int32_t shift = (bounds->min_z + bounds->max_z) / 2;
|
||||
g_Camera.target.z += (shift * Math_Cos(item->rot.y)) >> W2V_SHIFT;
|
||||
g_Camera.target.x += (shift * Math_Sin(item->rot.y)) >> W2V_SHIFT;
|
||||
}
|
||||
|
||||
g_Camera.target.room_num = item->room_num;
|
||||
if (g_Camera.fixed_camera != fixed_camera) {
|
||||
g_Camera.target.y = y;
|
||||
g_Camera.fixed_camera = 1;
|
||||
g_Camera.speed = 1;
|
||||
} else {
|
||||
g_Camera.fixed_camera = 0;
|
||||
g_Camera.target.y += (y - g_Camera.target.y) / 4;
|
||||
}
|
||||
|
||||
const SECTOR *const sector = Room_GetSector(
|
||||
g_Camera.target.x, y, g_Camera.target.z, &g_Camera.target.room_num);
|
||||
const int32_t height = Room_GetHeight(
|
||||
sector, g_Camera.target.x, g_Camera.target.y, g_Camera.target.z);
|
||||
if (g_Camera.target.y > height) {
|
||||
g_IsChunkyCamera = 0;
|
||||
}
|
||||
|
||||
if (g_Camera.type == CAM_CHASE || g_Camera.flags == CF_CHASE_OBJECT) {
|
||||
Camera_Chase(item);
|
||||
} else {
|
||||
Camera_Fixed();
|
||||
}
|
||||
}
|
||||
|
||||
g_Camera.last = g_Camera.num;
|
||||
g_Camera.fixed_camera = fixed_camera;
|
||||
if (g_Camera.type != CAM_HEAVY || g_Camera.timer == -1) {
|
||||
g_Camera.type = CAM_CHASE;
|
||||
g_Camera.speed = CHASE_SPEED;
|
||||
g_Camera.num = NO_CAMERA;
|
||||
g_Camera.last_item = g_Camera.item;
|
||||
g_Camera.item = NULL;
|
||||
g_Camera.target_elevation = 0;
|
||||
g_Camera.target_angle = 0;
|
||||
g_Camera.target_distance = CHASE_ELEVATION;
|
||||
g_Camera.flags = CF_NORMAL;
|
||||
}
|
||||
g_IsChunkyCamera = 0;
|
||||
}
|
||||
|
||||
void __cdecl Camera_LoadCutsceneFrame(void)
|
||||
{
|
||||
g_CineFrameIdx++;
|
||||
if (g_CineFrameIdx >= g_NumCineFrames) {
|
||||
g_CineFrameIdx = g_NumCineFrames - 1;
|
||||
}
|
||||
|
||||
const CINE_FRAME *frame = &g_CineData[g_CineFrameIdx];
|
||||
int32_t tx = frame->tx;
|
||||
int32_t ty = frame->ty;
|
||||
int32_t tz = frame->tz;
|
||||
int32_t cx = frame->cx;
|
||||
int32_t cy = frame->cy;
|
||||
int32_t cz = frame->cz;
|
||||
int32_t fov = frame->fov;
|
||||
int32_t roll = frame->roll;
|
||||
int32_t c = Math_Cos(g_CinePos.rot.y);
|
||||
int32_t s = Math_Sin(g_CinePos.rot.y);
|
||||
|
||||
g_Camera.target.x = g_CinePos.pos.x + ((tx * c + tz * s) >> W2V_SHIFT);
|
||||
g_Camera.target.y = g_CinePos.pos.y + ty;
|
||||
g_Camera.target.z = g_CinePos.pos.z + ((tz * c - tx * s) >> W2V_SHIFT);
|
||||
g_Camera.pos.x = g_CinePos.pos.x + ((cx * c + cz * s) >> W2V_SHIFT);
|
||||
g_Camera.pos.y = g_CinePos.pos.y + cy;
|
||||
g_Camera.pos.z = g_CinePos.pos.z + ((cz * c - cx * s) >> W2V_SHIFT);
|
||||
|
||||
Output_AlterFOV(fov);
|
||||
Matrix_LookAt(
|
||||
g_Camera.pos.x, g_Camera.pos.y, g_Camera.pos.z, g_Camera.target.x,
|
||||
g_Camera.target.y, g_Camera.target.z, roll);
|
||||
|
||||
Room_GetSector(
|
||||
g_Camera.pos.x, g_Camera.pos.y, g_Camera.pos.z, &g_Camera.pos.room_num);
|
||||
|
||||
if (g_Camera.is_lara_mic) {
|
||||
g_Camera.actual_angle =
|
||||
g_Lara.torso_y_rot + g_Lara.head_y_rot + g_LaraItem->rot.y;
|
||||
g_Camera.mic_pos.x = g_LaraItem->pos.x;
|
||||
g_Camera.mic_pos.y = g_LaraItem->pos.y;
|
||||
g_Camera.mic_pos.z = g_LaraItem->pos.z;
|
||||
} else {
|
||||
g_Camera.actual_angle = Math_Atan(
|
||||
g_Camera.target.z - g_Camera.pos.z,
|
||||
g_Camera.target.x - g_Camera.pos.x);
|
||||
g_Camera.mic_pos.x = g_Camera.pos.x
|
||||
+ ((g_PhdPersp * Math_Sin(g_Camera.actual_angle)) >> W2V_SHIFT);
|
||||
g_Camera.mic_pos.z = g_Camera.pos.z
|
||||
+ ((g_PhdPersp * Math_Cos(g_Camera.actual_angle)) >> W2V_SHIFT);
|
||||
g_Camera.mic_pos.y = g_Camera.pos.y;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Camera_UpdateCutscene(void)
|
||||
{
|
||||
const CINE_FRAME *frame = &g_CineData[g_CineFrameIdx];
|
||||
int32_t tx = frame->tx;
|
||||
int32_t ty = frame->ty;
|
||||
int32_t tz = frame->tz;
|
||||
int32_t cx = frame->cx;
|
||||
int32_t cy = frame->cy;
|
||||
int32_t cz = frame->cz;
|
||||
int32_t fov = frame->fov;
|
||||
int32_t roll = frame->roll;
|
||||
int32_t c = Math_Cos(g_Camera.target_angle);
|
||||
int32_t s = Math_Sin(g_Camera.target_angle);
|
||||
const XYZ_32 camtar = {
|
||||
.x = g_LaraItem->pos.x + ((tx * c + tz * s) >> W2V_SHIFT),
|
||||
.y = g_LaraItem->pos.y + ty,
|
||||
.z = g_LaraItem->pos.z + ((tz * c - tx * s) >> W2V_SHIFT),
|
||||
};
|
||||
const XYZ_32 campos = {
|
||||
.x = g_LaraItem->pos.x + ((cx * c + cz * s) >> W2V_SHIFT),
|
||||
.y = g_LaraItem->pos.y + cy,
|
||||
.z = g_LaraItem->pos.z + ((cz * c - cx * s) >> W2V_SHIFT),
|
||||
};
|
||||
int16_t room_num = Room_FindByPos(campos.x, campos.y, campos.z);
|
||||
if (room_num != NO_ROOM_NEG) {
|
||||
g_Camera.pos.room_num = room_num;
|
||||
}
|
||||
Output_AlterFOV(fov);
|
||||
Matrix_LookAt(
|
||||
campos.x, campos.y, campos.z, camtar.x, camtar.y, camtar.z, roll);
|
||||
}
|
||||
|
||||
void __cdecl Camera_RefreshFromTrigger(const int16_t type, const int16_t *fd)
|
||||
{
|
||||
int16_t target_ok = 2;
|
||||
|
||||
while (true) {
|
||||
const int16_t fd_cmd = *fd++;
|
||||
const int16_t value = TRIGGER_VALUE(fd_cmd);
|
||||
|
||||
switch (TRIGGER_TYPE(fd_cmd)) {
|
||||
case TO_CAMERA: {
|
||||
if (value == g_Camera.last) {
|
||||
g_Camera.num = value;
|
||||
if (g_Camera.timer < 0 || g_Camera.type == CAM_LOOK
|
||||
|| g_Camera.type == CAM_COMBAT) {
|
||||
g_Camera.timer = -1;
|
||||
target_ok = 0;
|
||||
} else {
|
||||
g_Camera.type = CAM_FIXED;
|
||||
target_ok = 1;
|
||||
}
|
||||
} else {
|
||||
target_ok = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TO_TARGET:
|
||||
if (g_Camera.type != CAM_LOOK && g_Camera.type != CAM_COMBAT) {
|
||||
g_Camera.item = &g_Items[value];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (FLOORDATA_IS_END(fd_cmd)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_Camera.item
|
||||
&& (!target_ok
|
||||
|| (target_ok == 2 && g_Camera.item->looked_at
|
||||
&& g_Camera.item != g_Camera.last_item))) {
|
||||
g_Camera.item = NULL;
|
||||
}
|
||||
|
||||
if (g_Camera.num == -1 && g_Camera.timer > 0) {
|
||||
g_Camera.timer = -1;
|
||||
}
|
||||
}
|
30
src/tr2/game/camera.h
Normal file
30
src/tr2/game/camera.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Camera_Initialise(void);
|
||||
void __cdecl Camera_ResetPosition(void);
|
||||
void __cdecl Camera_Move(const GAME_VECTOR *target, int32_t speed);
|
||||
void __cdecl Camera_Clip(
|
||||
int32_t *x, int32_t *y, int32_t *h, int32_t target_x, int32_t target_y,
|
||||
int32_t target_h, int32_t left, int32_t top, int32_t right, int32_t bottom);
|
||||
void __cdecl Camera_Shift(
|
||||
int32_t *x, int32_t *y, int32_t *h, int32_t target_x, int32_t target_y,
|
||||
int32_t target_h, int32_t left, int32_t top, int32_t right, int32_t bottom);
|
||||
const SECTOR *__cdecl Camera_GoodPosition(
|
||||
int32_t x, int32_t y, int32_t z, int16_t room_num);
|
||||
void __cdecl Camera_SmartShift(
|
||||
GAME_VECTOR *target,
|
||||
void(__cdecl *shift)(
|
||||
int32_t *x, int32_t *y, int32_t *h, int32_t target_x, int32_t target_y,
|
||||
int32_t target_h, int32_t left, int32_t top, int32_t right,
|
||||
int32_t bottom));
|
||||
void __cdecl Camera_Chase(const ITEM *item);
|
||||
int32_t __cdecl Camera_ShiftClamp(GAME_VECTOR *pos, int32_t clamp);
|
||||
void __cdecl Camera_Combat(const ITEM *item);
|
||||
void __cdecl Camera_Look(const ITEM *item);
|
||||
void __cdecl Camera_Fixed(void);
|
||||
void __cdecl Camera_Update(void);
|
||||
void __cdecl Camera_LoadCutsceneFrame(void);
|
||||
void __cdecl Camera_UpdateCutscene(void);
|
||||
void __cdecl Camera_RefreshFromTrigger(int16_t type, const int16_t *fd);
|
12
src/tr2/game/clock.c
Normal file
12
src/tr2/game/clock.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "game/clock.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
double Clock_GetHighPrecisionCounter(void)
|
||||
{
|
||||
LARGE_INTEGER frequency;
|
||||
LARGE_INTEGER counter;
|
||||
QueryPerformanceFrequency(&frequency);
|
||||
QueryPerformanceCounter(&counter);
|
||||
return counter.QuadPart * 1000.0 / frequency.QuadPart;
|
||||
}
|
3
src/tr2/game/clock.h
Normal file
3
src/tr2/game/clock.h
Normal file
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
double Clock_GetHighPrecisionCounter(void);
|
449
src/tr2/game/collide.c
Normal file
449
src/tr2/game/collide.c
Normal file
|
@ -0,0 +1,449 @@
|
|||
#include "game/collide.h"
|
||||
|
||||
#include "game/math.h"
|
||||
#include "game/room.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
void __cdecl Collide_GetCollisionInfo(
|
||||
COLL_INFO *const coll, const int32_t x_pos, const int32_t y_pos,
|
||||
const int32_t z_pos, int16_t room_num, const int32_t obj_height)
|
||||
{
|
||||
coll->coll_type = COLL_NONE;
|
||||
coll->shift.x = 0;
|
||||
coll->shift.y = 0;
|
||||
coll->shift.z = 0;
|
||||
coll->quadrant = Math_GetDirection(coll->facing);
|
||||
|
||||
const SECTOR *sector;
|
||||
int32_t height;
|
||||
int32_t ceiling;
|
||||
int32_t x;
|
||||
int32_t z;
|
||||
const int32_t y = y_pos - obj_height;
|
||||
const int32_t y_top = y - 160;
|
||||
|
||||
x = x_pos;
|
||||
z = z_pos;
|
||||
sector = Room_GetSector(x, y_top, z, &room_num);
|
||||
height = Room_GetHeight(sector, x, y_top, z);
|
||||
if (height != NO_HEIGHT) {
|
||||
height -= y_pos;
|
||||
}
|
||||
ceiling = Room_GetCeiling(sector, x, y_top, z);
|
||||
if (ceiling != NO_HEIGHT) {
|
||||
ceiling -= y;
|
||||
}
|
||||
|
||||
coll->side_mid.floor = height;
|
||||
coll->side_mid.ceiling = ceiling;
|
||||
coll->side_mid.type = g_HeightType;
|
||||
coll->trigger = g_TriggerIndex;
|
||||
|
||||
const int16_t tilt = Room_GetTiltType(sector, x, g_LaraItem->pos.y, z);
|
||||
coll->z_tilt = tilt >> 8;
|
||||
coll->x_tilt = (int8_t)tilt;
|
||||
|
||||
int32_t x_left;
|
||||
int32_t z_left;
|
||||
int32_t x_right;
|
||||
int32_t z_right;
|
||||
int32_t x_front;
|
||||
int32_t z_front;
|
||||
switch (coll->quadrant) {
|
||||
case DIR_NORTH:
|
||||
x_front = (coll->radius * Math_Sin(coll->facing)) >> W2V_SHIFT;
|
||||
z_front = coll->radius;
|
||||
x_left = -coll->radius;
|
||||
z_left = coll->radius;
|
||||
x_right = coll->radius;
|
||||
z_right = coll->radius;
|
||||
break;
|
||||
|
||||
case DIR_EAST:
|
||||
x_front = coll->radius;
|
||||
z_front = (coll->radius * Math_Cos(coll->facing)) >> W2V_SHIFT;
|
||||
x_left = coll->radius;
|
||||
z_left = coll->radius;
|
||||
x_right = coll->radius;
|
||||
z_right = -coll->radius;
|
||||
break;
|
||||
|
||||
case DIR_SOUTH:
|
||||
x_front = (coll->radius * Math_Sin(coll->facing)) >> W2V_SHIFT;
|
||||
z_front = -coll->radius;
|
||||
x_left = coll->radius;
|
||||
z_left = -coll->radius;
|
||||
x_right = -coll->radius;
|
||||
z_right = -coll->radius;
|
||||
break;
|
||||
|
||||
case DIR_WEST:
|
||||
x_front = -coll->radius;
|
||||
z_front = (coll->radius * Math_Cos(coll->facing)) >> W2V_SHIFT;
|
||||
x_left = -coll->radius;
|
||||
z_left = -coll->radius;
|
||||
x_right = -coll->radius;
|
||||
z_right = coll->radius;
|
||||
break;
|
||||
|
||||
default:
|
||||
x_front = 0;
|
||||
z_front = 0;
|
||||
x_left = 0;
|
||||
z_left = 0;
|
||||
x_right = 0;
|
||||
z_right = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
x = x_pos + x_front;
|
||||
z = z_pos + z_front;
|
||||
sector = Room_GetSector(x, y_top, z, &room_num);
|
||||
height = Room_GetHeight(sector, x, y_top, z);
|
||||
if (height != NO_HEIGHT) {
|
||||
height -= y_pos;
|
||||
}
|
||||
ceiling = Room_GetCeiling(sector, x, y_top, z);
|
||||
if (ceiling != NO_HEIGHT) {
|
||||
ceiling -= y;
|
||||
}
|
||||
|
||||
coll->side_front.floor = height;
|
||||
coll->side_front.ceiling = ceiling;
|
||||
coll->side_front.type = g_HeightType;
|
||||
if (coll->slopes_are_walls && coll->side_front.type == HT_BIG_SLOPE
|
||||
&& coll->side_front.floor < 0) {
|
||||
coll->side_front.floor = -32767;
|
||||
} else if (
|
||||
coll->slopes_are_pits && coll->side_front.type == HT_BIG_SLOPE
|
||||
&& coll->side_front.floor > 0) {
|
||||
coll->side_front.floor = 512;
|
||||
} else if (
|
||||
coll->lava_is_pit && coll->side_front.floor > 0
|
||||
&& g_TriggerIndex != NULL
|
||||
&& FLOORDATA_TYPE(g_TriggerIndex[0]) == FT_LAVA) {
|
||||
coll->side_front.floor = 512;
|
||||
}
|
||||
|
||||
x = x_pos + x_left;
|
||||
z = z_pos + z_left;
|
||||
sector = Room_GetSector(x, y_top, z, &room_num);
|
||||
height = Room_GetHeight(sector, x, y_top, z);
|
||||
if (height != NO_HEIGHT) {
|
||||
height -= y_pos;
|
||||
}
|
||||
ceiling = Room_GetCeiling(sector, x, y_top, z);
|
||||
if (ceiling != NO_HEIGHT) {
|
||||
ceiling -= y;
|
||||
}
|
||||
|
||||
coll->side_left.floor = height;
|
||||
coll->side_left.ceiling = ceiling;
|
||||
coll->side_left.type = g_HeightType;
|
||||
if (coll->slopes_are_walls && coll->side_left.type == HT_BIG_SLOPE
|
||||
&& coll->side_left.floor < 0) {
|
||||
coll->side_left.floor = -32767;
|
||||
} else if (
|
||||
coll->slopes_are_pits && coll->side_left.type == HT_BIG_SLOPE
|
||||
&& coll->side_left.floor > 0) {
|
||||
coll->side_left.floor = 512;
|
||||
} else if (
|
||||
coll->lava_is_pit && coll->side_left.floor > 0 && g_TriggerIndex != NULL
|
||||
&& FLOORDATA_TYPE(g_TriggerIndex[0]) == FT_LAVA) {
|
||||
coll->side_left.floor = 512;
|
||||
}
|
||||
|
||||
x = x_pos + x_right;
|
||||
z = z_pos + z_right;
|
||||
sector = Room_GetSector(x, y_top, z, &room_num);
|
||||
height = Room_GetHeight(sector, x, y_top, z);
|
||||
if (height != NO_HEIGHT) {
|
||||
height -= y_pos;
|
||||
}
|
||||
ceiling = Room_GetCeiling(sector, x, y_top, z);
|
||||
if (ceiling != NO_HEIGHT) {
|
||||
ceiling -= y;
|
||||
}
|
||||
|
||||
coll->side_right.floor = height;
|
||||
coll->side_right.ceiling = ceiling;
|
||||
coll->side_right.type = g_HeightType;
|
||||
if (coll->slopes_are_walls && coll->side_right.type == HT_BIG_SLOPE
|
||||
&& coll->side_right.floor < 0) {
|
||||
coll->side_right.floor = -32767;
|
||||
} else if (
|
||||
coll->slopes_are_pits && coll->side_right.type == HT_BIG_SLOPE
|
||||
&& coll->side_right.floor > 0) {
|
||||
coll->side_right.floor = 512;
|
||||
} else if (
|
||||
coll->lava_is_pit && coll->side_right.floor > 0
|
||||
&& g_TriggerIndex != NULL
|
||||
&& FLOORDATA_TYPE(g_TriggerIndex[0]) == FT_LAVA) {
|
||||
coll->side_right.floor = 512;
|
||||
}
|
||||
|
||||
if (Collide_CollideStaticObjects(
|
||||
coll, x_pos, y_pos, z_pos, room_num, obj_height)) {
|
||||
const XYZ_32 test_pos = {
|
||||
.x = x_pos + coll->shift.x,
|
||||
.y = y_pos,
|
||||
.z = z_pos + coll->shift.z,
|
||||
};
|
||||
sector = Room_GetSector(test_pos.x, test_pos.y, test_pos.z, &room_num);
|
||||
if (Room_GetHeight(sector, test_pos.x, test_pos.y, test_pos.z)
|
||||
< test_pos.y - WALL_L / 2
|
||||
|| Room_GetCeiling(sector, test_pos.x, test_pos.y, test_pos.z)
|
||||
> y) {
|
||||
coll->shift.x = -coll->shift.x;
|
||||
coll->shift.z = -coll->shift.z;
|
||||
}
|
||||
}
|
||||
|
||||
if (coll->side_mid.floor == NO_HEIGHT) {
|
||||
coll->shift.x = coll->old.x - x_pos;
|
||||
coll->shift.y = coll->old.y - y_pos;
|
||||
coll->shift.z = coll->old.z - z_pos;
|
||||
coll->coll_type = COLL_FRONT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (coll->side_mid.floor - coll->side_mid.ceiling <= 0) {
|
||||
coll->shift.x = coll->old.x - x_pos;
|
||||
coll->shift.y = coll->old.y - y_pos;
|
||||
coll->shift.z = coll->old.z - z_pos;
|
||||
coll->coll_type = COLL_CLAMP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (coll->side_mid.ceiling >= 0) {
|
||||
coll->shift.y = coll->side_mid.ceiling;
|
||||
coll->coll_type = COLL_TOP;
|
||||
}
|
||||
|
||||
if (coll->side_front.floor > coll->bad_pos
|
||||
|| coll->side_front.floor < coll->bad_neg
|
||||
|| coll->side_front.ceiling > coll->bad_ceiling) {
|
||||
switch (coll->quadrant) {
|
||||
case DIR_NORTH:
|
||||
case DIR_SOUTH:
|
||||
coll->shift.x = coll->old.x - x_pos;
|
||||
coll->shift.z = Room_FindGridShift(z_pos + z_front, z_pos);
|
||||
break;
|
||||
|
||||
case DIR_EAST:
|
||||
case DIR_WEST:
|
||||
coll->shift.x = Room_FindGridShift(x_pos + x_front, x_pos);
|
||||
coll->shift.z = coll->old.z - z_pos;
|
||||
break;
|
||||
}
|
||||
|
||||
coll->coll_type = COLL_FRONT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (coll->side_front.ceiling >= coll->bad_ceiling) {
|
||||
coll->shift.x = coll->old.x - x_pos;
|
||||
coll->shift.y = coll->old.y - y_pos;
|
||||
coll->shift.z = coll->old.z - z_pos;
|
||||
coll->coll_type = COLL_TOP_FRONT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (coll->side_left.floor > coll->bad_pos
|
||||
|| coll->side_left.floor < coll->bad_neg) {
|
||||
switch (coll->quadrant) {
|
||||
case DIR_NORTH:
|
||||
case DIR_SOUTH:
|
||||
coll->shift.x = Room_FindGridShift(x_pos + x_left, x_pos + x_front);
|
||||
break;
|
||||
|
||||
case DIR_EAST:
|
||||
case DIR_WEST:
|
||||
coll->shift.z = Room_FindGridShift(z_pos + z_left, z_pos + z_front);
|
||||
break;
|
||||
}
|
||||
|
||||
coll->coll_type = COLL_LEFT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (coll->side_right.floor > coll->bad_pos
|
||||
|| coll->side_right.floor < coll->bad_neg) {
|
||||
switch (coll->quadrant) {
|
||||
case DIR_NORTH:
|
||||
case DIR_SOUTH:
|
||||
coll->shift.x =
|
||||
Room_FindGridShift(x_pos + x_right, x_pos + x_front);
|
||||
break;
|
||||
|
||||
case DIR_EAST:
|
||||
case DIR_WEST:
|
||||
coll->shift.z =
|
||||
Room_FindGridShift(z_pos + z_right, z_pos + z_front);
|
||||
break;
|
||||
}
|
||||
|
||||
coll->coll_type = COLL_RIGHT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __cdecl Collide_CollideStaticObjects(
|
||||
COLL_INFO *const coll, const int32_t x, const int32_t y, const int32_t z,
|
||||
const int16_t room_num, const int32_t height)
|
||||
{
|
||||
coll->hit_static = 0;
|
||||
|
||||
const int32_t in_x_min = x - coll->radius;
|
||||
const int32_t in_x_max = x + coll->radius;
|
||||
const int32_t in_y_min = y - height;
|
||||
const int32_t in_y_max = y;
|
||||
const int32_t in_z_min = z - coll->radius;
|
||||
const int32_t in_z_max = z + coll->radius;
|
||||
XYZ_32 shifter = { .x = 0, .z = 0 };
|
||||
|
||||
Room_GetNearbyRooms(x, y, z, coll->radius + 50, height + 50, room_num);
|
||||
|
||||
for (int32_t i = 0; i < g_DrawRoomsCount; i++) {
|
||||
const ROOM *const room = &g_Rooms[g_DrawRoomsArray[i]];
|
||||
|
||||
for (int32_t j = 0; j < room->num_meshes; j++) {
|
||||
const MESH *const mesh = &room->meshes[j];
|
||||
const STATIC_INFO *const sinfo = &g_StaticObjects[mesh->static_num];
|
||||
|
||||
if (sinfo->flags & 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t x_min;
|
||||
int32_t x_max;
|
||||
int32_t z_min;
|
||||
int32_t z_max;
|
||||
const int32_t y_min = mesh->pos.y + sinfo->collision_bounds.min_y;
|
||||
const int32_t y_max = mesh->pos.y + sinfo->collision_bounds.max_y;
|
||||
switch (mesh->rot.y) {
|
||||
case PHD_90:
|
||||
x_min = mesh->pos.x + sinfo->collision_bounds.min_z;
|
||||
x_max = mesh->pos.x + sinfo->collision_bounds.max_z;
|
||||
z_min = mesh->pos.z - sinfo->collision_bounds.max_x;
|
||||
z_max = mesh->pos.z - sinfo->collision_bounds.min_x;
|
||||
break;
|
||||
|
||||
case -PHD_180:
|
||||
x_min = mesh->pos.x - sinfo->collision_bounds.max_x;
|
||||
x_max = mesh->pos.x - sinfo->collision_bounds.min_x;
|
||||
z_min = mesh->pos.z - sinfo->collision_bounds.max_z;
|
||||
z_max = mesh->pos.z - sinfo->collision_bounds.min_z;
|
||||
break;
|
||||
|
||||
case -PHD_90:
|
||||
x_min = mesh->pos.x - sinfo->collision_bounds.max_z;
|
||||
x_max = mesh->pos.x - sinfo->collision_bounds.min_z;
|
||||
z_min = mesh->pos.z + sinfo->collision_bounds.min_x;
|
||||
z_max = mesh->pos.z + sinfo->collision_bounds.max_x;
|
||||
break;
|
||||
|
||||
default:
|
||||
x_min = mesh->pos.x + sinfo->collision_bounds.min_x;
|
||||
x_max = mesh->pos.x + sinfo->collision_bounds.max_x;
|
||||
z_min = mesh->pos.z + sinfo->collision_bounds.min_z;
|
||||
z_max = mesh->pos.z + sinfo->collision_bounds.max_z;
|
||||
break;
|
||||
}
|
||||
|
||||
if (in_x_max <= x_min || in_x_min >= x_max || in_y_max <= y_min
|
||||
|| in_y_min >= y_max || in_z_max <= z_min
|
||||
|| in_z_min >= z_max) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t shl = in_x_max - x_min;
|
||||
int32_t shr = x_max - in_x_min;
|
||||
if (shl < shr) {
|
||||
shifter.x = -shl;
|
||||
} else {
|
||||
shifter.x = shr;
|
||||
}
|
||||
|
||||
shl = in_z_max - z_min;
|
||||
shr = z_max - in_z_min;
|
||||
if (shl < shr) {
|
||||
shifter.z = -shl;
|
||||
} else {
|
||||
shifter.z = shr;
|
||||
}
|
||||
|
||||
switch (coll->quadrant) {
|
||||
case DIR_NORTH:
|
||||
if (shifter.x > coll->radius || shifter.x < -coll->radius) {
|
||||
coll->coll_type = COLL_FRONT;
|
||||
coll->shift.x = coll->old.x - x;
|
||||
coll->shift.z = shifter.z;
|
||||
} else if (shifter.x > 0) {
|
||||
coll->coll_type = COLL_LEFT;
|
||||
coll->shift.x = shifter.x;
|
||||
coll->shift.z = 0;
|
||||
} else if (shifter.x < 0) {
|
||||
coll->coll_type = COLL_RIGHT;
|
||||
coll->shift.x = shifter.x;
|
||||
coll->shift.z = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case DIR_EAST:
|
||||
if (shifter.z > coll->radius || shifter.z < -coll->radius) {
|
||||
coll->coll_type = COLL_FRONT;
|
||||
coll->shift.x = shifter.x;
|
||||
coll->shift.z = coll->old.z - z;
|
||||
} else if (shifter.z > 0) {
|
||||
coll->coll_type = COLL_RIGHT;
|
||||
coll->shift.x = 0;
|
||||
coll->shift.z = shifter.z;
|
||||
} else if (shifter.z < 0) {
|
||||
coll->coll_type = COLL_LEFT;
|
||||
coll->shift.x = 0;
|
||||
coll->shift.z = shifter.z;
|
||||
}
|
||||
break;
|
||||
|
||||
case DIR_SOUTH:
|
||||
if (shifter.x > coll->radius || shifter.x < -coll->radius) {
|
||||
coll->coll_type = COLL_FRONT;
|
||||
coll->shift.x = coll->old.x - x;
|
||||
coll->shift.z = shifter.z;
|
||||
} else if (shifter.x > 0) {
|
||||
coll->coll_type = COLL_RIGHT;
|
||||
coll->shift.x = shifter.x;
|
||||
coll->shift.z = 0;
|
||||
} else if (shifter.x < 0) {
|
||||
coll->coll_type = COLL_LEFT;
|
||||
coll->shift.x = shifter.x;
|
||||
coll->shift.z = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case DIR_WEST:
|
||||
if (shifter.z > coll->radius || shifter.z < -coll->radius) {
|
||||
coll->coll_type = COLL_FRONT;
|
||||
coll->shift.x = shifter.x;
|
||||
coll->shift.z = coll->old.z - z;
|
||||
} else if (shifter.z > 0) {
|
||||
coll->coll_type = COLL_LEFT;
|
||||
coll->shift.x = 0;
|
||||
coll->shift.z = shifter.z;
|
||||
} else if (shifter.z < 0) {
|
||||
coll->coll_type = COLL_RIGHT;
|
||||
coll->shift.x = 0;
|
||||
coll->shift.z = shifter.z;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
coll->hit_static = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
11
src/tr2/game/collide.h
Normal file
11
src/tr2/game/collide.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Collide_GetCollisionInfo(
|
||||
COLL_INFO *coll, int32_t x_pos, int32_t y_pos, int32_t z_pos,
|
||||
int16_t room_num, int32_t obj_height);
|
||||
|
||||
int32_t __cdecl Collide_CollideStaticObjects(
|
||||
COLL_INFO *coll, int32_t x, int32_t y, int32_t z, int16_t room_num,
|
||||
int32_t height);
|
19
src/tr2/game/console/common.c
Normal file
19
src/tr2/game/console/common.c
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include "game/console/common.h"
|
||||
|
||||
#include "game/console/setup.h"
|
||||
#include "game/text.h"
|
||||
|
||||
int32_t Console_GetMaxLineLength(void)
|
||||
{
|
||||
return TEXT_MAX_STRING_SIZE - 1;
|
||||
}
|
||||
|
||||
CONSOLE_COMMAND **Console_GetCommands(void)
|
||||
{
|
||||
return g_ConsoleCommands;
|
||||
}
|
||||
|
||||
void Console_DrawBackdrop(void)
|
||||
{
|
||||
// TODO: implement me
|
||||
}
|
5
src/tr2/game/console/common.h
Normal file
5
src/tr2/game/console/common.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <libtrx/game/console/common.h>
|
||||
|
||||
void Console_Draw(void);
|
44
src/tr2/game/console/setup.c
Normal file
44
src/tr2/game/console/setup.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "game/console/setup.h"
|
||||
|
||||
#include <libtrx/game/console/cmd/config.h>
|
||||
#include <libtrx/game/console/cmd/die.h>
|
||||
#include <libtrx/game/console/cmd/end_level.h>
|
||||
#include <libtrx/game/console/cmd/exit_game.h>
|
||||
#include <libtrx/game/console/cmd/exit_to_title.h>
|
||||
#include <libtrx/game/console/cmd/flipmap.h>
|
||||
#include <libtrx/game/console/cmd/fly.h>
|
||||
#include <libtrx/game/console/cmd/give_item.h>
|
||||
#include <libtrx/game/console/cmd/heal.h>
|
||||
#include <libtrx/game/console/cmd/kill.h>
|
||||
#include <libtrx/game/console/cmd/load_game.h>
|
||||
#include <libtrx/game/console/cmd/play_demo.h>
|
||||
#include <libtrx/game/console/cmd/play_level.h>
|
||||
#include <libtrx/game/console/cmd/pos.h>
|
||||
#include <libtrx/game/console/cmd/save_game.h>
|
||||
#include <libtrx/game/console/cmd/set_health.h>
|
||||
#include <libtrx/game/console/cmd/sfx.h>
|
||||
#include <libtrx/game/console/cmd/teleport.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
CONSOLE_COMMAND *g_ConsoleCommands[] = {
|
||||
&g_Console_Cmd_Teleport,
|
||||
&g_Console_Cmd_Fly,
|
||||
&g_Console_Cmd_FlipMap,
|
||||
&g_Console_Cmd_Kill,
|
||||
&g_Console_Cmd_EndLevel,
|
||||
&g_Console_Cmd_PlayLevel,
|
||||
&g_Console_Cmd_LoadGame,
|
||||
&g_Console_Cmd_SaveGame,
|
||||
&g_Console_Cmd_PlayDemo,
|
||||
&g_Console_Cmd_ExitToTitle,
|
||||
&g_Console_Cmd_ExitGame,
|
||||
&g_Console_Cmd_Die,
|
||||
&g_Console_Cmd_Config,
|
||||
&g_Console_Cmd_Pos,
|
||||
&g_Console_Cmd_Heal,
|
||||
&g_Console_Cmd_SetHealth,
|
||||
&g_Console_Cmd_GiveItem,
|
||||
&g_Console_Cmd_SFX,
|
||||
NULL,
|
||||
};
|
5
src/tr2/game/console/setup.h
Normal file
5
src/tr2/game/console/setup.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <libtrx/game/console/common.h>
|
||||
|
||||
extern CONSOLE_COMMAND *g_ConsoleCommands[];
|
956
src/tr2/game/creature.c
Normal file
956
src/tr2/game/creature.c
Normal file
|
@ -0,0 +1,956 @@
|
|||
#include "game/creature.h"
|
||||
|
||||
#include "decomp/effects.h"
|
||||
#include "game/box.h"
|
||||
#include "game/items.h"
|
||||
#include "game/lara/misc.h"
|
||||
#include "game/los.h"
|
||||
#include "game/lot.h"
|
||||
#include "game/math.h"
|
||||
#include "game/objects/common.h"
|
||||
#include "game/objects/vars.h"
|
||||
#include "game/random.h"
|
||||
#include "game/room.h"
|
||||
#include "global/const.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#define FRONT_ARC PHD_90
|
||||
#define ESCAPE_CHANCE 2048
|
||||
#define RECOVER_CHANCE 256
|
||||
#define MAX_X_ROT (20 * PHD_DEGREE) // = 3640
|
||||
#define MAX_TILT (3 * PHD_DEGREE) // = 546
|
||||
#define MAX_HEAD_CHANGE (5 * PHD_DEGREE) // = 910
|
||||
#define HEAD_ARC 0x3000 // = 12288
|
||||
#define FLOAT_SPEED 32
|
||||
#define TARGET_TOLERANCE 0x400000
|
||||
|
||||
#define CREATURE_ATTACK_RANGE SQUARE(WALL_L * 3) // = 0x900000 = 9437184
|
||||
#define CREATURE_SHOOT_RANGE SQUARE(WALL_L * 8) // = 0x4000000 = 67108864
|
||||
|
||||
void __cdecl Creature_Initialise(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
item->rot.y += (Random_GetControl() - PHD_90) >> 1;
|
||||
item->collidable = 1;
|
||||
item->data = 0;
|
||||
}
|
||||
|
||||
int32_t __cdecl Creature_Activate(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
if (item->status != IS_INVISIBLE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!LOT_EnableBaddieAI(item_num, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
item->status = IS_ACTIVE;
|
||||
return true;
|
||||
}
|
||||
|
||||
void __cdecl Creature_AIInfo(ITEM *const item, AI_INFO *const info)
|
||||
{
|
||||
CREATURE *const creature = (CREATURE *)item->data;
|
||||
if (creature == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (item->object_id) {
|
||||
case O_BANDIT_1:
|
||||
case O_BANDIT_2:
|
||||
Creature_GetBaddieTarget(creature->item_num, false);
|
||||
break;
|
||||
|
||||
case O_MONK_1:
|
||||
case O_MONK_2:
|
||||
Creature_GetBaddieTarget(creature->item_num, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
creature->enemy = g_LaraItem;
|
||||
break;
|
||||
}
|
||||
|
||||
ITEM *enemy = creature->enemy;
|
||||
if (enemy == NULL) {
|
||||
enemy = g_LaraItem;
|
||||
}
|
||||
|
||||
int16_t *zone;
|
||||
if (creature->lot.fly) {
|
||||
zone = g_FlyZone[g_FlipStatus];
|
||||
} else {
|
||||
zone = g_GroundZone[BOX_ZONE(creature->lot.step)][g_FlipStatus];
|
||||
}
|
||||
|
||||
{
|
||||
const ROOM *const r = &g_Rooms[item->room_num];
|
||||
const int32_t z_sector = (item->pos.z - r->pos.z) >> WALL_SHIFT;
|
||||
const int32_t x_sector = (item->pos.x - r->pos.x) >> WALL_SHIFT;
|
||||
item->box_num = r->sectors[z_sector + x_sector * r->size.z].box;
|
||||
info->zone_num = zone[item->box_num];
|
||||
}
|
||||
|
||||
{
|
||||
const ROOM *const r = &g_Rooms[enemy->room_num];
|
||||
const int32_t z_sector = (enemy->pos.z - r->pos.z) >> WALL_SHIFT;
|
||||
const int32_t x_sector = (enemy->pos.x - r->pos.x) >> WALL_SHIFT;
|
||||
enemy->box_num = r->sectors[z_sector + x_sector * r->size.z].box;
|
||||
info->enemy_zone_num = zone[enemy->box_num];
|
||||
}
|
||||
|
||||
if (((g_Boxes[enemy->box_num].overlap_index & creature->lot.block_mask)
|
||||
!= 0)
|
||||
|| (creature->lot.node[item->box_num].search_num
|
||||
== (creature->lot.search_num | BOX_BLOCKED_SEARCH))) {
|
||||
info->enemy_zone_num |= BOX_BLOCKED;
|
||||
}
|
||||
|
||||
const OBJECT *const object = &g_Objects[item->object_id];
|
||||
const int32_t z = enemy->pos.z
|
||||
- ((object->pivot_length * Math_Cos(item->rot.y)) >> W2V_SHIFT)
|
||||
- item->pos.z;
|
||||
const int32_t x = enemy->pos.x
|
||||
- ((object->pivot_length * Math_Sin(item->rot.y)) >> W2V_SHIFT)
|
||||
- item->pos.x;
|
||||
PHD_ANGLE angle = Math_Atan(z, x);
|
||||
if (creature->enemy != NULL) {
|
||||
info->distance = SQUARE(x) + SQUARE(z);
|
||||
} else {
|
||||
info->distance = 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
info->angle = angle - item->rot.y;
|
||||
info->enemy_facing = angle - enemy->rot.y + PHD_180;
|
||||
info->ahead = info->angle > -FRONT_ARC && info->angle < FRONT_ARC;
|
||||
info->bite = info->ahead && enemy->hit_points > 0
|
||||
&& ABS(enemy->pos.y - item->pos.y) <= STEP_L;
|
||||
}
|
||||
|
||||
void __cdecl Creature_Mood(
|
||||
const ITEM *item, const AI_INFO *info, int32_t violent)
|
||||
{
|
||||
CREATURE *const creature = item->data;
|
||||
if (creature == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const LOT_INFO *const lot = &creature->lot;
|
||||
const ITEM *enemy = creature->enemy;
|
||||
if (creature->lot.node[item->box_num].search_num
|
||||
== (creature->lot.search_num | 0x8000)) {
|
||||
creature->lot.required_box = NO_BOX;
|
||||
}
|
||||
if (creature->mood != MOOD_ATTACK && creature->lot.required_box != NO_BOX
|
||||
&& !Box_ValidBox(item, info->zone_num, creature->lot.target_box)) {
|
||||
if (info->zone_num == info->enemy_zone_num) {
|
||||
creature->mood = MOOD_BORED;
|
||||
}
|
||||
creature->lot.required_box = NO_BOX;
|
||||
}
|
||||
|
||||
const MOOD_TYPE mood = creature->mood;
|
||||
if (enemy == NULL) {
|
||||
creature->mood = MOOD_BORED;
|
||||
enemy = g_LaraItem;
|
||||
} else if (enemy->hit_points <= 0) {
|
||||
creature->mood = MOOD_BORED;
|
||||
} else if (violent) {
|
||||
switch (mood) {
|
||||
case MOOD_BORED:
|
||||
case MOOD_STALK:
|
||||
if (info->zone_num == info->enemy_zone_num) {
|
||||
creature->mood = MOOD_ATTACK;
|
||||
} else if (item->hit_status != 0) {
|
||||
creature->mood = MOOD_ESCAPE;
|
||||
}
|
||||
break;
|
||||
|
||||
case MOOD_ATTACK:
|
||||
if (info->zone_num != info->enemy_zone_num) {
|
||||
creature->mood = MOOD_BORED;
|
||||
}
|
||||
break;
|
||||
|
||||
case MOOD_ESCAPE:
|
||||
if (info->zone_num == info->enemy_zone_num) {
|
||||
creature->mood = MOOD_ATTACK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (mood) {
|
||||
case MOOD_BORED:
|
||||
case MOOD_STALK:
|
||||
if (item->hit_status != 0
|
||||
&& (Random_GetControl() < ESCAPE_CHANCE
|
||||
|| info->zone_num != info->enemy_zone_num)) {
|
||||
creature->mood = MOOD_ESCAPE;
|
||||
} else if (info->zone_num == info->enemy_zone_num) {
|
||||
if (info->distance < CREATURE_ATTACK_RANGE
|
||||
|| (creature->mood == MOOD_STALK
|
||||
&& creature->lot.required_box == NO_BOX)) {
|
||||
creature->mood = MOOD_ATTACK;
|
||||
} else {
|
||||
creature->mood = MOOD_STALK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MOOD_ATTACK:
|
||||
if (item->hit_status != 0
|
||||
&& (Random_GetControl() < ESCAPE_CHANCE
|
||||
|| info->zone_num != info->enemy_zone_num)) {
|
||||
creature->mood = MOOD_ESCAPE;
|
||||
} else if (info->zone_num != info->enemy_zone_num) {
|
||||
creature->mood = MOOD_BORED;
|
||||
}
|
||||
break;
|
||||
|
||||
case MOOD_ESCAPE:
|
||||
if (info->zone_num == info->enemy_zone_num
|
||||
&& Random_GetControl() < RECOVER_CHANCE) {
|
||||
creature->mood = MOOD_STALK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mood != creature->mood) {
|
||||
if (mood == MOOD_ATTACK) {
|
||||
Box_TargetBox(&creature->lot, creature->lot.target_box);
|
||||
}
|
||||
creature->lot.required_box = NO_BOX;
|
||||
}
|
||||
|
||||
switch (creature->mood) {
|
||||
case MOOD_BORED: {
|
||||
const int16_t box_num =
|
||||
lot->node[(creature->lot.zone_count * Random_GetControl()) >> 15]
|
||||
.box_num;
|
||||
if (Box_ValidBox(item, info->zone_num, box_num)) {
|
||||
if (Box_StalkBox(item, enemy, box_num) && creature->enemy != NULL
|
||||
&& enemy->hit_points > 0) {
|
||||
Box_TargetBox(&creature->lot, box_num);
|
||||
creature->mood = MOOD_STALK;
|
||||
} else if (creature->lot.required_box == NO_BOX) {
|
||||
Box_TargetBox(&creature->lot, box_num);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MOOD_ATTACK:
|
||||
creature->lot.target = enemy->pos;
|
||||
creature->lot.required_box = enemy->box_num;
|
||||
if (creature->lot.fly != 0 && g_Lara.water_status == LWS_ABOVE_WATER) {
|
||||
creature->lot.target.y += Item_GetBestFrame(enemy)->bounds.min_y;
|
||||
}
|
||||
break;
|
||||
|
||||
case MOOD_ESCAPE: {
|
||||
const int16_t box_num =
|
||||
lot->node[(creature->lot.zone_count * Random_GetControl()) >> 15]
|
||||
.box_num;
|
||||
if (Box_ValidBox(item, info->zone_num, box_num)
|
||||
&& creature->lot.required_box == NO_BOX) {
|
||||
if (Box_EscapeBox(item, enemy, box_num)) {
|
||||
Box_TargetBox(&creature->lot, box_num);
|
||||
} else if (
|
||||
info->zone_num == info->enemy_zone_num
|
||||
&& Box_StalkBox(item, enemy, box_num)) {
|
||||
Box_TargetBox(&creature->lot, box_num);
|
||||
creature->mood = MOOD_STALK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MOOD_STALK: {
|
||||
int16_t box_num = creature->lot.required_box;
|
||||
if (box_num == NO_BOX || !Box_StalkBox(item, enemy, box_num)) {
|
||||
box_num =
|
||||
lot->node
|
||||
[(creature->lot.zone_count * Random_GetControl()) >> 15]
|
||||
.box_num;
|
||||
if (Box_ValidBox(item, info->zone_num, box_num)) {
|
||||
if (Box_StalkBox(item, enemy, box_num)) {
|
||||
Box_TargetBox(&creature->lot, box_num);
|
||||
} else if (creature->lot.required_box == NO_BOX) {
|
||||
Box_TargetBox(&creature->lot, box_num);
|
||||
if (info->zone_num != info->enemy_zone_num) {
|
||||
creature->mood = MOOD_BORED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (creature->lot.target_box == NO_BOX) {
|
||||
Box_TargetBox(&creature->lot, item->box_num);
|
||||
}
|
||||
Box_CalculateTarget(&creature->target, item, &creature->lot);
|
||||
}
|
||||
|
||||
int32_t __cdecl Creature_CheckBaddieOverlap(const int16_t item_num)
|
||||
{
|
||||
ITEM *item = &g_Items[item_num];
|
||||
|
||||
const int32_t x = item->pos.x;
|
||||
const int32_t y = item->pos.y;
|
||||
const int32_t z = item->pos.z;
|
||||
const int32_t radius = SQUARE(g_Objects[item->object_id].radius);
|
||||
|
||||
int16_t link = g_Rooms[item->room_num].item_num;
|
||||
while (link != NO_ITEM && link != item_num) {
|
||||
item = &g_Items[link];
|
||||
if (item != g_LaraItem && item->status == IS_ACTIVE
|
||||
&& item->speed != 0) {
|
||||
const int32_t distance =
|
||||
(SQUARE(item->pos.z - z) + SQUARE(item->pos.y - y)
|
||||
+ SQUARE(item->pos.x - x));
|
||||
if (distance < radius) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
link = item->next_item;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void __cdecl Creature_Die(const int16_t item_num, const bool explode)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
|
||||
if (item->object_id == O_DRAGON_FRONT) {
|
||||
item->hit_points = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->object_id == O_SKIDMAN) {
|
||||
if (explode) {
|
||||
Effect_ExplodingDeath(item_num, -1, 0);
|
||||
}
|
||||
const int16_t vehicle_item_num = (int16_t)(intptr_t)item->data;
|
||||
ITEM *const vehicle_item = &g_Items[vehicle_item_num];
|
||||
vehicle_item->hit_points = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
item->collidable = 0;
|
||||
item->hit_points = DONT_TARGET;
|
||||
if (explode) {
|
||||
Effect_ExplodingDeath(item_num, -1, 0);
|
||||
Item_Kill(item_num);
|
||||
} else {
|
||||
Item_RemoveActive(item_num);
|
||||
}
|
||||
|
||||
if (g_Objects[item->object_id].intelligent) {
|
||||
LOT_DisableBaddieAI(item_num);
|
||||
}
|
||||
item->flags |= IF_ONE_SHOT;
|
||||
|
||||
if (item->killed) {
|
||||
item->next_active = g_PrevItemActive;
|
||||
g_PrevItemActive = item_num;
|
||||
}
|
||||
|
||||
if (g_Objects[item->object_id].intelligent) {
|
||||
int16_t pickup_num = item->carried_item;
|
||||
while (pickup_num != NO_ITEM) {
|
||||
ITEM *const pickup = &g_Items[pickup_num];
|
||||
pickup->pos = item->pos;
|
||||
Item_NewRoom(pickup_num, item->room_num);
|
||||
pickup_num = pickup->carried_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __cdecl Creature_Animate(
|
||||
const int16_t item_num, const int16_t angle, const int16_t tilt)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
const CREATURE *const creature = item->data;
|
||||
const OBJECT *const object = &g_Objects[item->object_id];
|
||||
if (creature == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const LOT_INFO *const lot = &creature->lot;
|
||||
const XYZ_32 old = item->pos;
|
||||
|
||||
int16_t *zone;
|
||||
if (lot->fly) {
|
||||
zone = g_FlyZone[g_FlipStatus];
|
||||
} else {
|
||||
zone = g_GroundZone[BOX_ZONE(lot->step)][g_FlipStatus];
|
||||
}
|
||||
|
||||
if (!object->water_creature) {
|
||||
int16_t room_num = item->room_num;
|
||||
Room_GetSector(item->pos.x, item->pos.y, item->pos.z, &room_num);
|
||||
if (room_num != item->room_num) {
|
||||
Item_NewRoom(item_num, room_num);
|
||||
}
|
||||
}
|
||||
|
||||
Item_Animate(item);
|
||||
if (item->status == IS_DEACTIVATED) {
|
||||
Creature_Die(item_num, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
const BOUNDS_16 *const bounds = Item_GetBoundsAccurate(item);
|
||||
int32_t y = item->pos.y + bounds->min_y;
|
||||
|
||||
int16_t room_num = item->room_num;
|
||||
Room_GetSector(old.x, y, old.z, &room_num);
|
||||
const SECTOR *sector =
|
||||
Room_GetSector(item->pos.x, y, item->pos.z, &room_num);
|
||||
int32_t height = g_Boxes[sector->box].height;
|
||||
int16_t next_box = lot->node[sector->box].exit_box;
|
||||
int32_t next_height =
|
||||
next_box != NO_BOX ? g_Boxes[next_box].height : height;
|
||||
|
||||
const int32_t box_height = g_Boxes[item->box_num].height;
|
||||
if (sector->box == NO_BOX || zone[item->box_num] != zone[sector->box]
|
||||
|| box_height - height > lot->step || box_height - height < lot->drop) {
|
||||
const int32_t pos_x = item->pos.x >> WALL_SHIFT;
|
||||
const int32_t shift_x = old.x >> WALL_SHIFT;
|
||||
const int32_t shift_z = old.z >> WALL_SHIFT;
|
||||
|
||||
if (pos_x < shift_x) {
|
||||
item->pos.x = old.x & (~(WALL_L - 1));
|
||||
} else if (pos_x > shift_x) {
|
||||
item->pos.x = old.x | (WALL_L - 1);
|
||||
}
|
||||
|
||||
if (pos_x < shift_z) {
|
||||
item->pos.z = old.z & (~(WALL_L - 1));
|
||||
} else if (pos_x > shift_z) {
|
||||
item->pos.z = old.z | (WALL_L - 1);
|
||||
}
|
||||
|
||||
sector = Room_GetSector(item->pos.x, y, item->pos.z, &room_num);
|
||||
height = g_Boxes[sector->box].height;
|
||||
next_box = lot->node[sector->box].exit_box;
|
||||
next_height = next_box != NO_BOX ? g_Boxes[next_box].height : height;
|
||||
}
|
||||
|
||||
const int32_t x = item->pos.x;
|
||||
const int32_t z = item->pos.z;
|
||||
const int32_t pos_x = x & (WALL_L - 1);
|
||||
const int32_t pos_z = z & (WALL_L - 1);
|
||||
int32_t shift_x = 0;
|
||||
int32_t shift_z = 0;
|
||||
const int32_t radius = object->radius;
|
||||
|
||||
if (pos_z < radius) {
|
||||
if (Box_BadFloor(
|
||||
x, y, z - radius, height, next_height, room_num, lot)) {
|
||||
shift_z = radius - pos_z;
|
||||
}
|
||||
|
||||
if (pos_x < radius) {
|
||||
if (Box_BadFloor(
|
||||
x - radius, y, z, height, next_height, room_num, lot)) {
|
||||
shift_x = radius - pos_x;
|
||||
} else if (
|
||||
!shift_z
|
||||
&& Box_BadFloor(
|
||||
x - radius, y, z - radius, height, next_height, room_num,
|
||||
lot)) {
|
||||
if (item->rot.y > -PHD_135 && item->rot.y < PHD_45) {
|
||||
shift_z = radius - pos_z;
|
||||
} else {
|
||||
shift_x = radius - pos_x;
|
||||
}
|
||||
}
|
||||
} else if (pos_x > WALL_L - radius) {
|
||||
if (Box_BadFloor(
|
||||
x + radius, y, z, height, next_height, room_num, lot)) {
|
||||
shift_x = WALL_L - radius - pos_x;
|
||||
} else if (
|
||||
!shift_z
|
||||
&& Box_BadFloor(
|
||||
x + radius, y, z - radius, height, next_height, room_num,
|
||||
lot)) {
|
||||
if (item->rot.y > -PHD_45 && item->rot.y < PHD_135) {
|
||||
shift_z = radius - pos_z;
|
||||
} else {
|
||||
shift_x = WALL_L - radius - pos_x;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (pos_z > WALL_L - radius) {
|
||||
if (Box_BadFloor(
|
||||
x, y, z + radius, height, next_height, room_num, lot)) {
|
||||
shift_z = WALL_L - radius - pos_z;
|
||||
}
|
||||
|
||||
if (pos_x < radius) {
|
||||
if (Box_BadFloor(
|
||||
x - radius, y, z, height, next_height, room_num, lot)) {
|
||||
shift_x = radius - pos_x;
|
||||
} else if (
|
||||
!shift_z
|
||||
&& Box_BadFloor(
|
||||
x - radius, y, z + radius, height, next_height, room_num,
|
||||
lot)) {
|
||||
if (item->rot.y > -PHD_45 && item->rot.y < PHD_135) {
|
||||
shift_x = radius - pos_x;
|
||||
} else {
|
||||
shift_z = WALL_L - radius - pos_z;
|
||||
}
|
||||
}
|
||||
} else if (pos_x > WALL_L - radius) {
|
||||
if (Box_BadFloor(
|
||||
x + radius, y, z, height, next_height, room_num, lot)) {
|
||||
shift_x = WALL_L - radius - pos_x;
|
||||
} else if (
|
||||
!shift_z
|
||||
&& Box_BadFloor(
|
||||
x + radius, y, z + radius, height, next_height, room_num,
|
||||
lot)) {
|
||||
if (item->rot.y > -PHD_135 && item->rot.y < PHD_45) {
|
||||
shift_x = WALL_L - radius - pos_x;
|
||||
} else {
|
||||
shift_z = WALL_L - radius - pos_z;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (pos_x < radius) {
|
||||
if (Box_BadFloor(
|
||||
x - radius, y, z, height, next_height, room_num, lot)) {
|
||||
shift_x = radius - pos_x;
|
||||
}
|
||||
} else if (pos_x > WALL_L - radius) {
|
||||
if (Box_BadFloor(
|
||||
x + radius, y, z, height, next_height, room_num, lot)) {
|
||||
shift_x = WALL_L - radius - pos_x;
|
||||
}
|
||||
}
|
||||
|
||||
item->pos.x += shift_x;
|
||||
item->pos.z += shift_z;
|
||||
|
||||
if (shift_x || shift_z) {
|
||||
sector = Room_GetSector(item->pos.x, y, item->pos.z, &room_num);
|
||||
item->rot.y += angle;
|
||||
if (tilt != 0) {
|
||||
Creature_Tilt(item, tilt * 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (Creature_CheckBaddieOverlap(item_num)) {
|
||||
item->pos = old;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (lot->fly) {
|
||||
int32_t dy = creature->target.y - item->pos.y;
|
||||
CLAMP(dy, -lot->fly, lot->fly);
|
||||
|
||||
height = Room_GetHeight(sector, item->pos.x, y, item->pos.z);
|
||||
if (item->pos.y + dy <= height) {
|
||||
const int32_t ceiling =
|
||||
Room_GetCeiling(sector, item->pos.x, y, item->pos.z);
|
||||
const int32_t min_y =
|
||||
item->object_id == O_SHARK ? 128 : bounds->min_y;
|
||||
if (item->pos.y + min_y + dy < ceiling) {
|
||||
if (item->pos.y + min_y < ceiling) {
|
||||
item->pos.x = old.x;
|
||||
item->pos.z = old.z;
|
||||
dy = lot->fly;
|
||||
} else {
|
||||
dy = 0;
|
||||
}
|
||||
}
|
||||
} else if (item->pos.y <= height) {
|
||||
item->pos.y = height;
|
||||
dy = 0;
|
||||
} else {
|
||||
item->pos.x = old.x;
|
||||
item->pos.z = old.z;
|
||||
dy = -lot->fly;
|
||||
}
|
||||
|
||||
item->pos.y += dy;
|
||||
sector = Room_GetSector(item->pos.x, y, item->pos.z, &room_num);
|
||||
item->floor = Room_GetHeight(sector, item->pos.x, y, item->pos.z);
|
||||
|
||||
int16_t angle = item->speed != 0 ? Math_Atan(item->speed, -dy) : 0;
|
||||
CLAMP(angle, -MAX_X_ROT, MAX_X_ROT);
|
||||
|
||||
if (angle < item->rot.x - PHD_DEGREE) {
|
||||
item->rot.x -= PHD_DEGREE;
|
||||
} else if (angle > item->rot.x + PHD_DEGREE) {
|
||||
item->rot.x += PHD_DEGREE;
|
||||
} else {
|
||||
item->rot.x = angle;
|
||||
}
|
||||
} else {
|
||||
const SECTOR *const sector =
|
||||
Room_GetSector(item->pos.x, item->pos.y, item->pos.z, &room_num);
|
||||
item->floor =
|
||||
Room_GetHeight(sector, item->pos.x, item->pos.y, item->pos.z);
|
||||
|
||||
if (item->pos.y > item->floor) {
|
||||
item->pos.y = item->floor;
|
||||
} else if (item->floor - item->pos.y > STEP_L / 4) {
|
||||
item->pos.y += STEP_L / 4;
|
||||
} else if (item->pos.y < item->floor) {
|
||||
item->pos.y = item->floor;
|
||||
}
|
||||
item->rot.x = 0;
|
||||
}
|
||||
|
||||
if (!object->water_creature) {
|
||||
Room_GetSector(
|
||||
item->pos.x, item->pos.y - (STEP_L * 2), item->pos.z, &room_num);
|
||||
if (g_Rooms[room_num].flags & RF_UNDERWATER) {
|
||||
item->hit_points = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->room_num != room_num) {
|
||||
Item_NewRoom(item_num, room_num);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int16_t __cdecl Creature_Turn(ITEM *const item, int16_t max_turn)
|
||||
{
|
||||
const CREATURE *const creature = item->data;
|
||||
if (creature == NULL || item->speed == 0 || max_turn == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int32_t dx = creature->target.x - item->pos.x;
|
||||
const int32_t dz = creature->target.z - item->pos.z;
|
||||
|
||||
int16_t angle = Math_Atan(dz, dx) - item->rot.y;
|
||||
if (angle > FRONT_ARC || angle < -FRONT_ARC) {
|
||||
const int32_t range = (item->speed << 14) / max_turn;
|
||||
if (SQUARE(dx) + SQUARE(dz) < SQUARE(range)) {
|
||||
max_turn /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
CLAMP(angle, -max_turn, max_turn);
|
||||
item->rot.y += angle;
|
||||
return angle;
|
||||
}
|
||||
|
||||
void __cdecl Creature_Tilt(ITEM *const item, int16_t angle)
|
||||
{
|
||||
angle = 4 * angle - item->rot.z;
|
||||
CLAMP(angle, -MAX_TILT, MAX_TILT);
|
||||
item->rot.z += angle;
|
||||
}
|
||||
|
||||
void __cdecl Creature_Head(ITEM *item, int16_t required)
|
||||
{
|
||||
CREATURE *const creature = item->data;
|
||||
if (creature == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t change = required - creature->head_rotation;
|
||||
CLAMP(change, -MAX_HEAD_CHANGE, MAX_HEAD_CHANGE);
|
||||
|
||||
creature->head_rotation += change;
|
||||
CLAMP(creature->head_rotation, -HEAD_ARC, HEAD_ARC);
|
||||
}
|
||||
|
||||
void __cdecl Creature_Neck(ITEM *const item, const int16_t required)
|
||||
{
|
||||
CREATURE *const creature = item->data;
|
||||
if (creature == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t change = required - creature->neck_rotation;
|
||||
CLAMP(change, -MAX_HEAD_CHANGE, MAX_HEAD_CHANGE);
|
||||
|
||||
creature->neck_rotation += change;
|
||||
CLAMP(creature->neck_rotation, -HEAD_ARC, HEAD_ARC);
|
||||
}
|
||||
|
||||
void __cdecl Creature_Float(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
|
||||
item->hit_points = DONT_TARGET;
|
||||
item->rot.x = 0;
|
||||
|
||||
const int32_t wh = Room_GetWaterHeight(
|
||||
item->pos.x, item->pos.y, item->pos.z, item->room_num);
|
||||
if (item->pos.y > wh) {
|
||||
item->pos.y -= FLOAT_SPEED;
|
||||
} else if (item->pos.y < wh) {
|
||||
item->pos.y = wh;
|
||||
}
|
||||
|
||||
Item_Animate(item);
|
||||
|
||||
int16_t room_num = item->room_num;
|
||||
const SECTOR *const sector =
|
||||
Room_GetSector(item->pos.x, item->pos.y, item->pos.z, &room_num);
|
||||
item->floor = Room_GetHeight(sector, item->pos.x, item->pos.y, item->pos.z);
|
||||
if (room_num != item->room_num) {
|
||||
Item_NewRoom(item_num, room_num);
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Creature_Underwater(ITEM *const item, const int32_t depth)
|
||||
{
|
||||
const int32_t wh = Room_GetWaterHeight(
|
||||
item->pos.x, item->pos.y, item->pos.z, item->room_num);
|
||||
if (item->pos.y >= wh + depth) {
|
||||
return;
|
||||
}
|
||||
|
||||
item->pos.y = wh + depth;
|
||||
if (item->rot.x > 2 * PHD_DEGREE) {
|
||||
item->rot.x -= 2 * PHD_DEGREE;
|
||||
} else {
|
||||
CLAMPG(item->rot.x, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int16_t __cdecl Creature_Effect(
|
||||
const ITEM *const item, const BITE *const bite,
|
||||
int16_t(__cdecl *const spawn)(
|
||||
int32_t x, int32_t y, int32_t z, int16_t speed, int16_t y_rot,
|
||||
int16_t room_num))
|
||||
{
|
||||
XYZ_32 pos = bite->pos;
|
||||
Collide_GetJointAbsPosition(item, &pos, bite->mesh_num);
|
||||
return (*spawn)(
|
||||
pos.x, pos.y, pos.z, item->speed, item->rot.y, item->room_num);
|
||||
}
|
||||
|
||||
int32_t __cdecl Creature_Vault(
|
||||
const int16_t item_num, const int16_t angle, int32_t vault,
|
||||
const int32_t shift)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
const int16_t room_num = item->room_num;
|
||||
const XYZ_32 old = item->pos;
|
||||
|
||||
Creature_Animate(item_num, angle, 0);
|
||||
|
||||
if (item->floor > old.y + STEP_L * 7 / 2) {
|
||||
vault = -4;
|
||||
} else if (item->pos.y > old.y - STEP_L * 3 / 2) {
|
||||
return 0;
|
||||
} else if (item->pos.y > old.y - STEP_L * 5 / 2) {
|
||||
vault = 2;
|
||||
} else if (item->pos.y > old.y - STEP_L * 7 / 2) {
|
||||
vault = 3;
|
||||
} else {
|
||||
vault = 4;
|
||||
}
|
||||
|
||||
const int32_t old_x_sector = old.x >> WALL_SHIFT;
|
||||
const int32_t old_z_sector = old.z >> WALL_SHIFT;
|
||||
const int32_t x_sector = item->pos.x >> WALL_SHIFT;
|
||||
const int32_t z_sector = item->pos.z >> WALL_SHIFT;
|
||||
if (old_z_sector == z_sector) {
|
||||
if (old_x_sector == x_sector) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (old_x_sector >= x_sector) {
|
||||
item->rot.y = -PHD_90;
|
||||
item->pos.x = (old_x_sector << WALL_SHIFT) + shift;
|
||||
} else {
|
||||
item->rot.y = PHD_90;
|
||||
item->pos.x = (x_sector << WALL_SHIFT) - shift;
|
||||
}
|
||||
} else if (old_x_sector == x_sector) {
|
||||
if (old_z_sector >= z_sector) {
|
||||
item->rot.y = PHD_180;
|
||||
item->pos.z = (old_z_sector << WALL_SHIFT) + shift;
|
||||
} else {
|
||||
item->rot.y = 0;
|
||||
item->pos.z = (z_sector << WALL_SHIFT) - shift;
|
||||
}
|
||||
}
|
||||
|
||||
item->floor = old.y;
|
||||
item->pos.y = old.y;
|
||||
|
||||
if (room_num != item->room_num) {
|
||||
Item_NewRoom(item_num, room_num);
|
||||
}
|
||||
return vault;
|
||||
}
|
||||
|
||||
void __cdecl Creature_Kill(
|
||||
ITEM *const item, const int32_t kill_anim, const int32_t kill_state,
|
||||
const int32_t lara_kill_state)
|
||||
{
|
||||
item->anim_num = g_Objects[item->object_id].anim_idx + kill_anim;
|
||||
item->frame_num = g_Anims[item->anim_num].frame_base;
|
||||
item->current_anim_state = kill_state;
|
||||
|
||||
g_LaraItem->anim_num = g_Objects[O_LARA_EXTRA].anim_idx;
|
||||
g_LaraItem->frame_num = g_Anims[g_LaraItem->anim_num].frame_base;
|
||||
g_LaraItem->current_anim_state = LA_EXTRA_BREATH;
|
||||
g_LaraItem->goal_anim_state = lara_kill_state;
|
||||
g_LaraItem->pos = item->pos;
|
||||
g_LaraItem->rot = item->rot;
|
||||
g_LaraItem->fall_speed = 0;
|
||||
g_LaraItem->gravity = 0;
|
||||
g_LaraItem->speed = 0;
|
||||
|
||||
int16_t room_num = item->room_num;
|
||||
if (room_num != g_LaraItem->room_num) {
|
||||
Item_NewRoom(g_Lara.item_num, room_num);
|
||||
}
|
||||
|
||||
Item_Animate(g_LaraItem);
|
||||
|
||||
g_LaraItem->goal_anim_state = lara_kill_state;
|
||||
g_LaraItem->current_anim_state = lara_kill_state;
|
||||
g_Lara.extra_anim = 1;
|
||||
g_Lara.gun_status = LGS_HANDS_BUSY;
|
||||
g_Lara.gun_type = LGT_UNARMED;
|
||||
g_Lara.hit_direction = -1;
|
||||
g_Lara.air = -1;
|
||||
|
||||
g_Camera.pos.room_num = g_LaraItem->room_num;
|
||||
}
|
||||
|
||||
void __cdecl Creature_GetBaddieTarget(
|
||||
const int16_t item_num, const int32_t goody)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
CREATURE *const creature = item->data;
|
||||
|
||||
ITEM *best_item = NULL;
|
||||
int32_t best_distance = INT32_MAX;
|
||||
for (int32_t i = 0; i < NUM_SLOTS; i++) {
|
||||
const int16_t target_item_num = g_BaddieSlots[i].item_num;
|
||||
if (target_item_num == NO_ITEM || target_item_num == item_num) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ITEM *target = &g_Items[target_item_num];
|
||||
const GAME_OBJECT_ID object_id = target->object_id;
|
||||
if (goody && object_id != O_BANDIT_1 && object_id != O_BANDIT_2) {
|
||||
continue;
|
||||
} else if (!goody && object_id != O_MONK_1 && object_id != O_MONK_2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32_t dx = (target->pos.x - item->pos.x) >> 6;
|
||||
const int32_t dy = (target->pos.y - item->pos.y) >> 6;
|
||||
const int32_t dz = (target->pos.z - item->pos.z) >> 6;
|
||||
const int32_t distance = SQUARE(dx) + SQUARE(dy) + SQUARE(dz);
|
||||
if (distance < best_distance) {
|
||||
best_item = target;
|
||||
best_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_item == NULL) {
|
||||
if (!goody || g_IsMonkAngry) {
|
||||
creature->enemy = g_LaraItem;
|
||||
} else {
|
||||
creature->enemy = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!goody || g_IsMonkAngry) {
|
||||
const int32_t dx = (g_LaraItem->pos.x - item->pos.x) >> 6;
|
||||
const int32_t dy = (g_LaraItem->pos.y - item->pos.y) >> 6;
|
||||
const int32_t dz = (g_LaraItem->pos.z - item->pos.z) >> 6;
|
||||
const int32_t distance = SQUARE(dx) + SQUARE(dy) + SQUARE(dz);
|
||||
if (distance < best_distance) {
|
||||
best_item = g_LaraItem;
|
||||
best_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
const ITEM *const target = creature->enemy;
|
||||
if (target == NULL || target->status != IS_ACTIVE) {
|
||||
creature->enemy = best_item;
|
||||
} else {
|
||||
const int32_t dx = (target->pos.x - item->pos.x) >> 6;
|
||||
const int32_t dy = (target->pos.y - item->pos.y) >> 6;
|
||||
const int32_t dz = (target->pos.z - item->pos.z) >> 6;
|
||||
const int32_t distance = SQUARE(dz) + SQUARE(dy) + SQUARE(dx);
|
||||
if (distance < best_distance + TARGET_TOLERANCE) {
|
||||
creature->enemy = best_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Creature_Collision(
|
||||
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
if (!Item_TestBoundsCollide(item, lara_item, coll->radius)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Collide_TestCollision(item, lara_item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (coll->enable_baddie_push && g_Lara.water_status != LWS_UNDERWATER
|
||||
&& g_Lara.water_status != LWS_SURFACE) {
|
||||
Lara_Push(item, lara_item, coll, coll->enable_spaz, false);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __cdecl Creature_CanTargetEnemy(
|
||||
const ITEM *const item, const AI_INFO *const info)
|
||||
{
|
||||
const CREATURE *const creature = item->data;
|
||||
const ITEM *const enemy = creature->enemy;
|
||||
if (enemy->hit_points <= 0 || !info->ahead
|
||||
|| info->distance >= CREATURE_SHOOT_RANGE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
GAME_VECTOR start;
|
||||
start.pos.x = item->pos.x;
|
||||
start.pos.y = item->pos.y - STEP_L * 3;
|
||||
start.pos.z = item->pos.z;
|
||||
start.room_num = item->room_num;
|
||||
|
||||
GAME_VECTOR target;
|
||||
target.pos.x = enemy->pos.x;
|
||||
target.pos.y = enemy->pos.y - STEP_L * 3;
|
||||
target.pos.z = enemy->pos.z;
|
||||
target.room_num = enemy->room_num;
|
||||
|
||||
return LOS_Check(&start, &target);
|
||||
}
|
||||
|
||||
bool Creature_IsEnemy(const ITEM *const item)
|
||||
{
|
||||
return Object_IsObjectType(item->object_id, g_EnemyObjects)
|
||||
|| (g_IsMonkAngry
|
||||
&& (item->object_id == O_MONK_1 || item->object_id == O_MONK_2));
|
||||
}
|
||||
|
||||
bool Creature_IsAlly(const ITEM *const item)
|
||||
{
|
||||
return Object_IsObjectType(item->object_id, g_AllyObjects);
|
||||
}
|
35
src/tr2/game/creature.h
Normal file
35
src/tr2/game/creature.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void __cdecl Creature_Initialise(int16_t item_num);
|
||||
int32_t __cdecl Creature_Activate(int16_t item_num);
|
||||
void __cdecl Creature_AIInfo(ITEM *item, AI_INFO *info);
|
||||
void __cdecl Creature_Mood(
|
||||
const ITEM *item, const AI_INFO *info, int32_t violent);
|
||||
int32_t __cdecl Creature_CheckBaddieOverlap(int16_t item_num);
|
||||
void __cdecl Creature_Die(int16_t item_num, bool explode);
|
||||
int32_t __cdecl Creature_Animate(int16_t item_num, int16_t angle, int16_t tilt);
|
||||
int16_t __cdecl Creature_Turn(ITEM *item, int16_t max_turn);
|
||||
void __cdecl Creature_Tilt(ITEM *item, int16_t angle);
|
||||
void __cdecl Creature_Head(ITEM *item, int16_t required);
|
||||
void __cdecl Creature_Neck(ITEM *item, int16_t required);
|
||||
void __cdecl Creature_Float(int16_t item_num);
|
||||
void __cdecl Creature_Underwater(ITEM *item, int32_t depth);
|
||||
int16_t __cdecl Creature_Effect(
|
||||
const ITEM *item, const BITE *bite,
|
||||
int16_t(__cdecl *spawn)(
|
||||
int32_t x, int32_t y, int32_t z, int16_t speed, int16_t y_rot,
|
||||
int16_t room_num));
|
||||
int32_t __cdecl Creature_Vault(
|
||||
int16_t item_num, int16_t angle, int32_t vault, int32_t shift);
|
||||
void __cdecl Creature_Kill(
|
||||
ITEM *item, int32_t kill_anim, int32_t kill_state, int32_t lara_kill_state);
|
||||
void __cdecl Creature_GetBaddieTarget(int16_t item_num, int32_t goody);
|
||||
void __cdecl Creature_Collision(
|
||||
int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
|
||||
int32_t __cdecl Creature_CanTargetEnemy(const ITEM *item, const AI_INFO *info);
|
||||
bool Creature_IsEnemy(const ITEM *item);
|
||||
bool Creature_IsAlly(const ITEM *item);
|
137
src/tr2/game/demo.c
Normal file
137
src/tr2/game/demo.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
#include "game/demo.h"
|
||||
|
||||
#include "decomp/decomp.h"
|
||||
#include "game/game.h"
|
||||
#include "game/gameflow.h"
|
||||
#include "game/items.h"
|
||||
#include "game/lara/cheat.h"
|
||||
#include "game/random.h"
|
||||
#include "game/room.h"
|
||||
#include "game/shell.h"
|
||||
#include "game/text.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
static int32_t m_DemoLevel = 0;
|
||||
static int32_t m_DemoLevel2 = 0;
|
||||
static int32_t m_OldDemoInputDB = 0;
|
||||
|
||||
int32_t __cdecl Demo_Control(int32_t level_num)
|
||||
{
|
||||
if (level_num < 0 && !g_GameFlow.num_demos) {
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
|
||||
if (level_num < 0) {
|
||||
if (m_DemoLevel >= g_GameFlow.num_demos) {
|
||||
m_DemoLevel = 0;
|
||||
}
|
||||
level_num = g_GF_ValidDemos[m_DemoLevel];
|
||||
m_DemoLevel++;
|
||||
} else {
|
||||
m_DemoLevel = level_num;
|
||||
}
|
||||
|
||||
return GF_DoLevelSequence(level_num, GFL_DEMO);
|
||||
}
|
||||
|
||||
int32_t __cdecl Demo_Start(int32_t level_num)
|
||||
{
|
||||
if (level_num < 0 && !g_GameFlow.num_demos) {
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
|
||||
if (level_num >= 0) {
|
||||
m_DemoLevel2 = level_num;
|
||||
} else {
|
||||
if (m_DemoLevel2 >= g_GameFlow.num_demos) {
|
||||
m_DemoLevel2 = 0;
|
||||
}
|
||||
level_num = g_GF_ValidDemos[m_DemoLevel2];
|
||||
m_DemoLevel2++;
|
||||
}
|
||||
|
||||
START_INFO *const s = &g_SaveGame.start[level_num];
|
||||
START_INFO start = *s;
|
||||
s->available = 1;
|
||||
s->has_pistols = 1;
|
||||
s->pistol_ammo = 1000;
|
||||
s->gun_status = LGS_ARMLESS;
|
||||
s->gun_type = LGT_PISTOLS;
|
||||
Random_SeedDraw(0xD371F947);
|
||||
Random_SeedControl(0xD371F947);
|
||||
|
||||
g_IsTitleLoaded = false;
|
||||
if (!Level_Initialise(level_num, GFL_DEMO)) {
|
||||
return GFD_EXIT_GAME;
|
||||
}
|
||||
|
||||
g_LevelComplete = false;
|
||||
if (!g_IsDemoLoaded) {
|
||||
Shell_ExitSystemFmt(
|
||||
"Level '%s' has no demo data!", g_GF_LevelFileNames[level_num]);
|
||||
}
|
||||
|
||||
Demo_LoadLaraPos();
|
||||
Lara_Cheat_GetStuff();
|
||||
Random_SeedDraw(0xD371F947);
|
||||
Random_SeedControl(0xD371F947);
|
||||
|
||||
TEXTSTRING *const text = Text_Create(
|
||||
0, g_DumpHeight / 2 - 16, 0, g_GF_PCStrings[GF_S_PC_DEMO_MODE]);
|
||||
|
||||
Text_Flash(text, true, 20);
|
||||
Text_CentreV(text, true);
|
||||
Text_CentreH(text, true);
|
||||
|
||||
m_OldDemoInputDB = 0;
|
||||
g_Inv_DemoMode = true;
|
||||
GAME_FLOW_DIR dir = Game_Loop(true);
|
||||
g_Inv_DemoMode = false;
|
||||
|
||||
Text_Remove(text);
|
||||
|
||||
*s = start;
|
||||
S_FadeToBlack();
|
||||
if (dir == GFD_OVERRIDE) {
|
||||
dir = g_GF_OverrideDir;
|
||||
g_GF_OverrideDir = (GAME_FLOW_DIR)-1;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
void __cdecl Demo_LoadLaraPos(void)
|
||||
{
|
||||
g_LaraItem->pos.x = g_DemoPtr[0];
|
||||
g_LaraItem->pos.y = g_DemoPtr[1];
|
||||
g_LaraItem->pos.z = g_DemoPtr[2];
|
||||
g_LaraItem->rot.x = g_DemoPtr[3];
|
||||
g_LaraItem->rot.y = g_DemoPtr[4];
|
||||
g_LaraItem->rot.z = g_DemoPtr[5];
|
||||
int16_t room_num = g_DemoPtr[6];
|
||||
if (g_LaraItem->room_num != room_num) {
|
||||
Item_NewRoom(g_Lara.item_num, room_num);
|
||||
}
|
||||
|
||||
const SECTOR *const sector = Room_GetSector(
|
||||
g_LaraItem->pos.x, g_LaraItem->pos.y, g_LaraItem->pos.z, &room_num);
|
||||
g_LaraItem->floor = Room_GetHeight(
|
||||
sector, g_LaraItem->pos.x, g_LaraItem->pos.y, g_LaraItem->pos.z);
|
||||
g_Lara.last_gun_type = g_DemoPtr[7];
|
||||
|
||||
g_DemoCount += 8;
|
||||
}
|
||||
|
||||
void __cdecl Demo_GetInput(void)
|
||||
{
|
||||
if (g_DemoCount >= MAX_DEMO_SIZE) {
|
||||
g_Input = -1;
|
||||
} else {
|
||||
g_Input = g_DemoPtr[g_DemoCount];
|
||||
}
|
||||
if (g_Input != -1) {
|
||||
g_InputDB = g_Input & ~m_OldDemoInputDB;
|
||||
m_OldDemoInputDB = g_Input;
|
||||
g_DemoCount++;
|
||||
}
|
||||
}
|
8
src/tr2/game/demo.h
Normal file
8
src/tr2/game/demo.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
int32_t __cdecl Demo_Control(int32_t level_num);
|
||||
int32_t __cdecl Demo_Start(int32_t level_num);
|
||||
void __cdecl Demo_LoadLaraPos(void);
|
||||
void __cdecl Demo_GetInput(void);
|
155
src/tr2/game/effects.c
Normal file
155
src/tr2/game/effects.c
Normal file
|
@ -0,0 +1,155 @@
|
|||
#include "game/effects.h"
|
||||
|
||||
#include "game/matrix.h"
|
||||
#include "game/output.h"
|
||||
#include "global/const.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
static void M_RemoveActive(const int16_t fx_num);
|
||||
static void M_RemoveDrawn(const int16_t fx_num);
|
||||
|
||||
static void M_RemoveActive(const int16_t fx_num)
|
||||
{
|
||||
FX *const fx = &g_Effects[fx_num];
|
||||
int16_t link_num = g_NextEffectActive;
|
||||
if (link_num == fx_num) {
|
||||
g_NextEffectActive = fx->next_active;
|
||||
return;
|
||||
}
|
||||
|
||||
while (link_num != NO_ITEM) {
|
||||
if (g_Effects[link_num].next_active == fx_num) {
|
||||
g_Effects[link_num].next_active = fx->next_active;
|
||||
return;
|
||||
}
|
||||
link_num = g_Effects[link_num].next_active;
|
||||
}
|
||||
}
|
||||
|
||||
static void M_RemoveDrawn(const int16_t fx_num)
|
||||
{
|
||||
FX *const fx = &g_Effects[fx_num];
|
||||
int16_t link_num = g_Rooms[fx->room_num].fx_num;
|
||||
if (link_num == fx_num) {
|
||||
g_Rooms[fx->room_num].fx_num = fx->next_free;
|
||||
return;
|
||||
}
|
||||
|
||||
while (link_num != NO_ITEM) {
|
||||
if (g_Effects[link_num].next_free == fx_num) {
|
||||
g_Effects[link_num].next_free = fx->next_free;
|
||||
return;
|
||||
}
|
||||
link_num = g_Effects[link_num].next_free;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Effect_InitialiseArray(void)
|
||||
{
|
||||
g_NextEffectFree = 0;
|
||||
g_NextEffectActive = NO_ITEM;
|
||||
|
||||
for (int32_t i = 0; i < MAX_EFFECTS - 1; i++) {
|
||||
FX *const fx = &g_Effects[i];
|
||||
fx->next_free = i + 1;
|
||||
}
|
||||
g_Effects[MAX_EFFECTS - 1].next_free = NO_ITEM;
|
||||
}
|
||||
|
||||
int16_t __cdecl Effect_Create(const int16_t room_num)
|
||||
{
|
||||
int16_t fx_num = g_NextEffectFree;
|
||||
if (fx_num == NO_ITEM) {
|
||||
return NO_ITEM;
|
||||
}
|
||||
|
||||
FX *const fx = &g_Effects[fx_num];
|
||||
g_NextEffectFree = fx->next_free;
|
||||
|
||||
ROOM *const room = &g_Rooms[room_num];
|
||||
fx->room_num = room_num;
|
||||
fx->next_free = room->fx_num;
|
||||
room->fx_num = fx_num;
|
||||
|
||||
fx->next_active = g_NextEffectActive;
|
||||
g_NextEffectActive = fx_num;
|
||||
|
||||
fx->shade = 0x1000;
|
||||
|
||||
return fx_num;
|
||||
}
|
||||
|
||||
void __cdecl Effect_Kill(const int16_t fx_num)
|
||||
{
|
||||
FX *const fx = &g_Effects[fx_num];
|
||||
M_RemoveActive(fx_num);
|
||||
M_RemoveDrawn(fx_num);
|
||||
|
||||
fx->next_free = g_NextEffectFree;
|
||||
g_NextEffectFree = fx_num;
|
||||
}
|
||||
|
||||
void __cdecl Effect_NewRoom(const int16_t fx_num, const int16_t room_num)
|
||||
{
|
||||
FX *const fx = &g_Effects[fx_num];
|
||||
ROOM *room = &g_Rooms[fx->room_num];
|
||||
|
||||
int16_t link_num = room->fx_num;
|
||||
if (link_num == fx_num) {
|
||||
room->fx_num = fx->next_free;
|
||||
} else {
|
||||
while (link_num != NO_ITEM) {
|
||||
if (g_Effects[link_num].next_free == fx_num) {
|
||||
g_Effects[link_num].next_free = fx->next_free;
|
||||
break;
|
||||
}
|
||||
link_num = g_Effects[link_num].next_free;
|
||||
}
|
||||
}
|
||||
|
||||
fx->room_num = room_num;
|
||||
room = &g_Rooms[room_num];
|
||||
fx->next_free = room->fx_num;
|
||||
room->fx_num = fx_num;
|
||||
}
|
||||
|
||||
void __cdecl Effect_Draw(const int16_t fx_num)
|
||||
{
|
||||
const FX *const fx = &g_Effects[fx_num];
|
||||
const OBJECT *const object = &g_Objects[fx->object_id];
|
||||
if (!object->loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fx->object_id == O_GLOW) {
|
||||
Output_DrawSprite(
|
||||
(fx->rot.y << 16) | (unsigned __int16)fx->rot.x, fx->pos.x,
|
||||
fx->pos.y, fx->pos.z, g_Objects[O_GLOW].mesh_idx, fx->shade,
|
||||
fx->frame_num);
|
||||
return;
|
||||
}
|
||||
|
||||
if (object->mesh_count < 0) {
|
||||
Output_DrawSprite(
|
||||
SPRITE_ABS | (object->semi_transparent ? SPRITE_SEMITRANS : 0)
|
||||
| SPRITE_SHADE,
|
||||
fx->pos.x, fx->pos.y, fx->pos.z, object->mesh_idx - fx->frame_num,
|
||||
fx->shade, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_TranslateAbs(fx->pos.x, fx->pos.y, fx->pos.z);
|
||||
if (g_MatrixPtr->_23 > g_PhdNearZ && g_MatrixPtr->_23 < g_PhdFarZ) {
|
||||
Matrix_RotYXZ(fx->rot.y, fx->rot.x, fx->rot.z);
|
||||
if (object->mesh_count) {
|
||||
S_CalculateStaticLight(fx->shade);
|
||||
Output_InsertPolygons(g_Meshes[object->mesh_idx], -1);
|
||||
} else {
|
||||
S_CalculateStaticLight(fx->shade);
|
||||
Output_InsertPolygons(g_Meshes[fx->frame_num], -1);
|
||||
}
|
||||
}
|
||||
Matrix_Pop();
|
||||
}
|
9
src/tr2/game/effects.h
Normal file
9
src/tr2/game/effects.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void __cdecl Effect_InitialiseArray(void);
|
||||
int16_t __cdecl Effect_Create(int16_t room_num);
|
||||
void __cdecl Effect_Kill(int16_t fx_num);
|
||||
void __cdecl Effect_NewRoom(int16_t fx_num, int16_t room_num);
|
||||
void __cdecl Effect_Draw(int16_t fx_num);
|
319
src/tr2/game/game.c
Normal file
319
src/tr2/game/game.c
Normal file
|
@ -0,0 +1,319 @@
|
|||
#include "game/game.h"
|
||||
|
||||
#include "decomp/decomp.h"
|
||||
#include "game/camera.h"
|
||||
#include "game/demo.h"
|
||||
#include "game/gameflow/gameflow_new.h"
|
||||
#include "game/input.h"
|
||||
#include "game/inventory/common.h"
|
||||
#include "game/lara/control.h"
|
||||
#include "game/music.h"
|
||||
#include "game/overlay.h"
|
||||
#include "game/room_draw.h"
|
||||
#include "game/sound.h"
|
||||
#include "game/text.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
#include "specific/s_audio_sample.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
static int32_t m_FrameCount = 0;
|
||||
|
||||
int32_t __cdecl Game_Control(int32_t nframes, const bool demo_mode)
|
||||
{
|
||||
if (g_GF_OverrideDir != (GAME_FLOW_DIR)-1) {
|
||||
return GFD_OVERRIDE;
|
||||
}
|
||||
|
||||
CLAMPG(nframes, MAX_FRAMES);
|
||||
m_FrameCount += nframes;
|
||||
|
||||
if (m_FrameCount <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (m_FrameCount > 0) {
|
||||
// TODO: move me outta here
|
||||
if (g_CD_TrackID > 0) {
|
||||
Music_Init();
|
||||
}
|
||||
|
||||
if (!g_GameFlow.cheat_mode_check_disabled) {
|
||||
CheckCheatMode();
|
||||
}
|
||||
|
||||
if (g_LevelComplete) {
|
||||
return GFD_START_GAME | LV_FIRST;
|
||||
}
|
||||
|
||||
Input_Update();
|
||||
|
||||
if (demo_mode) {
|
||||
if (g_InputDB != 0) {
|
||||
return g_GameFlow.on_demo_interrupt;
|
||||
}
|
||||
Demo_GetInput();
|
||||
if (g_Input == -1) {
|
||||
g_Input = 0;
|
||||
return g_GameFlow.on_demo_end;
|
||||
}
|
||||
} else if (g_GameFlow.no_input_timeout) {
|
||||
if (g_InputDB != 0) {
|
||||
g_NoInputCounter = 0;
|
||||
} else {
|
||||
g_NoInputCounter++;
|
||||
if (g_NoInputCounter > g_GameFlow.no_input_time) {
|
||||
return GFD_START_DEMO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_Lara.death_timer > DEATH_WAIT
|
||||
|| (g_Lara.death_timer > DEATH_WAIT_INPUT && g_Input != 0)
|
||||
|| g_OverlayStatus == 2) {
|
||||
if (demo_mode) {
|
||||
return g_GameFlow.on_death_demo_mode;
|
||||
}
|
||||
if (g_CurrentLevel == LV_GYM) {
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
if (g_GameFlow.on_death_in_game) {
|
||||
return g_GameFlow.on_death_in_game;
|
||||
}
|
||||
if (g_OverlayStatus == 2) {
|
||||
g_OverlayStatus = 1;
|
||||
const GAME_FLOW_DIR dir = Inv_Display(INV_DEATH_MODE);
|
||||
if (dir != 0) {
|
||||
return dir;
|
||||
}
|
||||
} else {
|
||||
g_OverlayStatus = 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (((g_InputDB & (IN_LOAD | IN_SAVE | IN_OPTION))
|
||||
|| g_OverlayStatus <= 0)
|
||||
&& g_Lara.death_timer == 0 && !g_Lara.extra_anim) {
|
||||
if (g_OverlayStatus > 0) {
|
||||
if (g_GameFlow.load_save_disabled) {
|
||||
g_OverlayStatus = 0;
|
||||
} else if (g_Input & IN_LOAD) {
|
||||
g_OverlayStatus = -1;
|
||||
} else {
|
||||
g_OverlayStatus = g_Input & IN_SAVE ? -2 : 0;
|
||||
}
|
||||
} else {
|
||||
GAME_FLOW_DIR dir;
|
||||
if (g_OverlayStatus == -1) {
|
||||
dir = Inv_Display(INV_LOAD_MODE);
|
||||
} else if (g_OverlayStatus == -2) {
|
||||
dir = Inv_Display(INV_SAVE_MODE);
|
||||
} else {
|
||||
dir = Inv_Display(INV_GAME_MODE);
|
||||
}
|
||||
if (g_GF_OverrideDir != (GAME_FLOW_DIR)-1) {
|
||||
return GFD_OVERRIDE;
|
||||
}
|
||||
g_OverlayStatus = 1;
|
||||
|
||||
if (dir != 0) {
|
||||
if (g_Inv_ExtraData[0] == 1) {
|
||||
if (g_CurrentLevel == LV_GYM) {
|
||||
return GFD_START_GAME | LV_FIRST;
|
||||
}
|
||||
CreateSaveGameInfo();
|
||||
S_SaveGame(
|
||||
&g_SaveGame, sizeof(SAVEGAME_INFO),
|
||||
g_Inv_ExtraData[1]);
|
||||
S_SaveSettings();
|
||||
} else {
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_DynamicLightCount = 0;
|
||||
|
||||
{
|
||||
int16_t item_num = g_NextItemActive;
|
||||
while (item_num != NO_ITEM) {
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
const int16_t next = item->next_active;
|
||||
const OBJECT *object = &g_Objects[item->object_id];
|
||||
if (!(item->flags & IF_KILLED) && object->control != NULL) {
|
||||
object->control(item_num);
|
||||
}
|
||||
item_num = next;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int16_t fx_num = g_NextEffectActive;
|
||||
while (fx_num != NO_ITEM) {
|
||||
FX *const fx = &g_Effects[fx_num];
|
||||
const OBJECT *const object = &g_Objects[fx->object_id];
|
||||
const int32_t next = fx->next_active;
|
||||
if (object->control != NULL) {
|
||||
object->control(fx_num);
|
||||
}
|
||||
fx_num = next;
|
||||
}
|
||||
}
|
||||
|
||||
Lara_Control(0);
|
||||
HairControl(0);
|
||||
Camera_Update();
|
||||
Sound_UpdateEffects();
|
||||
|
||||
g_HealthBarTimer--;
|
||||
if (g_CurrentLevel || g_IsAssaultTimerActive) {
|
||||
g_SaveGame.statistics.timer++;
|
||||
}
|
||||
|
||||
m_FrameCount -= 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t __cdecl Game_Draw(void)
|
||||
{
|
||||
Room_DrawAllRooms(g_Camera.pos.room_num);
|
||||
Overlay_DrawGameInfo(true);
|
||||
S_OutputPolyList();
|
||||
g_Camera.num_frames = S_DumpScreen();
|
||||
S_AnimateTextures(g_Camera.num_frames);
|
||||
return g_Camera.num_frames;
|
||||
}
|
||||
|
||||
int32_t __cdecl Game_DrawCinematic(void)
|
||||
{
|
||||
g_CameraUnderwater = false;
|
||||
Room_DrawAllRooms(g_Camera.pos.room_num);
|
||||
Text_Draw();
|
||||
S_OutputPolyList();
|
||||
g_Camera.num_frames = S_DumpScreen();
|
||||
S_AnimateTextures(g_Camera.num_frames);
|
||||
return g_Camera.num_frames;
|
||||
}
|
||||
|
||||
int16_t __cdecl Game_Start(
|
||||
const int32_t level_num, const GAMEFLOW_LEVEL_TYPE level_type)
|
||||
{
|
||||
if (level_type == GFL_NORMAL || level_type == GFL_SAVED
|
||||
|| level_type == GFL_DEMO) {
|
||||
g_CurrentLevel = level_num;
|
||||
}
|
||||
if (level_type != GFL_SAVED) {
|
||||
ModifyStartInfo(level_num);
|
||||
}
|
||||
g_IsTitleLoaded = false;
|
||||
if (level_type != GFL_SAVED) {
|
||||
InitialiseLevelFlags();
|
||||
}
|
||||
if (!Level_Initialise(level_num, level_type)) {
|
||||
g_CurrentLevel = 0;
|
||||
return GFD_EXIT_GAME;
|
||||
}
|
||||
|
||||
GAME_FLOW_DIR dir = Game_Loop(false);
|
||||
if (dir == GFD_OVERRIDE) {
|
||||
dir = g_GF_OverrideDir;
|
||||
g_GF_OverrideDir = (GAME_FLOW_DIR)-1;
|
||||
return dir;
|
||||
}
|
||||
if (dir == GFD_EXIT_TO_TITLE || dir == GFD_START_DEMO) {
|
||||
return dir;
|
||||
}
|
||||
|
||||
if (dir == GFD_EXIT_GAME) {
|
||||
g_CurrentLevel = 0;
|
||||
return dir;
|
||||
}
|
||||
|
||||
if (g_LevelComplete) {
|
||||
if (g_GameFlow.demo_version && g_GameFlow.single_level) {
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
|
||||
if (g_CurrentLevel == LV_GYM) {
|
||||
S_FadeToBlack();
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
|
||||
S_FadeInInventory(1);
|
||||
return GFD_LEVEL_COMPLETE | g_CurrentLevel;
|
||||
}
|
||||
|
||||
S_FadeToBlack();
|
||||
if (!g_Inv_Chosen) {
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
|
||||
if (g_Inv_ExtraData[0] == 0) {
|
||||
S_LoadGame(&g_SaveGame, sizeof(SAVEGAME_INFO), g_Inv_ExtraData[1]);
|
||||
return GFD_START_SAVED_GAME | g_Inv_ExtraData[1];
|
||||
}
|
||||
|
||||
if (g_Inv_ExtraData[0] != 1) {
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
|
||||
if (g_GameFlow.play_any_level) {
|
||||
return g_Inv_ExtraData[1] + 1;
|
||||
}
|
||||
|
||||
return GFD_START_GAME | LV_FIRST;
|
||||
}
|
||||
|
||||
int32_t __cdecl Game_Loop(const bool demo_mode)
|
||||
{
|
||||
g_OverlayStatus = 1;
|
||||
Camera_Initialise();
|
||||
g_NoInputCounter = 0;
|
||||
g_GameMode = demo_mode ? GAMEMODE_IN_DEMO : GAMEMODE_IN_GAME;
|
||||
|
||||
GAME_FLOW_DIR dir = Game_Control(1, demo_mode);
|
||||
while (dir == 0) {
|
||||
const int32_t nframes = Game_Draw();
|
||||
if (g_IsGameToExit) {
|
||||
dir = GFD_EXIT_GAME;
|
||||
} else {
|
||||
dir = Game_Control(nframes, demo_mode);
|
||||
}
|
||||
}
|
||||
|
||||
g_GameMode = GAMEMODE_NOT_IN_GAME;
|
||||
|
||||
Overlay_HideGameInfo();
|
||||
S_Audio_Sample_OutCloseAllTracks();
|
||||
Music_Stop();
|
||||
|
||||
if (g_OptionMusicVolume) {
|
||||
Music_SetVolume(25 * g_OptionMusicVolume + 5);
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
GAMEFLOW_LEVEL_TYPE Game_GetCurrentLevelType(void)
|
||||
{
|
||||
return g_GameInfo.current_level.type;
|
||||
}
|
||||
|
||||
bool Game_IsPlayable(void)
|
||||
{
|
||||
if (g_GameInfo.current_level.type == GFL_TITLE
|
||||
|| g_GameInfo.current_level.type == GFL_DEMO
|
||||
|| g_GameInfo.current_level.type == GFL_CUTSCENE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_Objects[O_LARA].loaded || g_LaraItem == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
10
src/tr2/game/game.h
Normal file
10
src/tr2/game/game.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
int32_t __cdecl Game_Control(int32_t nframes, bool demo_mode);
|
||||
int32_t __cdecl Game_Draw(void);
|
||||
int32_t __cdecl Game_DrawCinematic(void);
|
||||
int16_t __cdecl Game_Start(int32_t level_num, GAMEFLOW_LEVEL_TYPE level_type);
|
||||
int32_t __cdecl Game_Loop(bool demo_mode);
|
||||
bool Game_IsPlayable(void);
|
15
src/tr2/game/game_string.c
Normal file
15
src/tr2/game/game_string.c
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "game_string.h"
|
||||
|
||||
#include <libtrx/game/game_string.h>
|
||||
|
||||
void GameString_Init(void)
|
||||
{
|
||||
#include "game_string.def"
|
||||
|
||||
#include <libtrx/game/game_string.def>
|
||||
}
|
||||
|
||||
void GameString_Shutdown(void)
|
||||
{
|
||||
GameString_Clear();
|
||||
}
|
20
src/tr2/game/game_string.def
Normal file
20
src/tr2/game/game_string.def
Normal file
|
@ -0,0 +1,20 @@
|
|||
GS_DEFINE(OSD_FLY_MODE_ON, "Fly mode enabled")
|
||||
GS_DEFINE(OSD_FLY_MODE_OFF, "Fly mode disabled")
|
||||
GS_DEFINE(OSD_GIVE_ITEM_ALL_KEYS, "Surprise! Every key item Lara needs is now in her backpack.")
|
||||
GS_DEFINE(OSD_GIVE_ITEM_ALL_GUNS, "Lock'n'load - Lara's armed to the teeth!")
|
||||
GS_DEFINE(OSD_GIVE_ITEM_CHEAT, "Lara's backpack just got way heavier!")
|
||||
GS_DEFINE(OSD_FLIPMAP_ON, "Flipmap set to ON")
|
||||
GS_DEFINE(OSD_FLIPMAP_OFF, "Flipmap set to OFF")
|
||||
GS_DEFINE(OSD_FLIPMAP_FAIL_ALREADY_ON, "Flipmap is already ON")
|
||||
GS_DEFINE(OSD_FLIPMAP_FAIL_ALREADY_OFF, "Flipmap is already OFF")
|
||||
GS_DEFINE(OSD_COMPLETE_LEVEL, "Level complete!")
|
||||
GS_DEFINE(OSD_PLAY_LEVEL, "Loading %s")
|
||||
GS_DEFINE(OSD_INVALID_LEVEL, "Invalid level")
|
||||
GS_DEFINE(OSD_INVALID_SAVE_SLOT, "Invalid save slot %d")
|
||||
GS_DEFINE(OSD_DOOR_OPEN, "Open Sesame!")
|
||||
GS_DEFINE(OSD_DOOR_CLOSE, "Close Sesame!")
|
||||
GS_DEFINE(OSD_DOOR_OPEN_FAIL, "No doors in Lara's proximity")
|
||||
GS_DEFINE(OSD_LOAD_GAME, "Loaded game from save slot %d")
|
||||
GS_DEFINE(OSD_LOAD_GAME_FAIL_UNAVAILABLE_SLOT, "Save slot %d is not available")
|
||||
GS_DEFINE(OSD_SAVE_GAME, "Saved game to save slot %d")
|
||||
GS_DEFINE(OSD_SAVE_GAME_FAIL, "Cannot save the game in the current state")
|
6
src/tr2/game/game_string.h
Normal file
6
src/tr2/game/game_string.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <libtrx/game/game_string.h>
|
||||
|
||||
void GameString_Init(void);
|
||||
void GameString_Shutdown(void);
|
637
src/tr2/game/gameflow.c
Normal file
637
src/tr2/game/gameflow.c
Normal file
|
@ -0,0 +1,637 @@
|
|||
#include "game/gameflow.h"
|
||||
|
||||
#include "decomp/decomp.h"
|
||||
#include "decomp/stats.h"
|
||||
#include "game/demo.h"
|
||||
#include "game/game.h"
|
||||
#include "game/gun/gun.h"
|
||||
#include "game/inventory/backpack.h"
|
||||
#include "game/overlay.h"
|
||||
#include "game/requester.h"
|
||||
#include "gameflow/gameflow_new.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/benchmark.h>
|
||||
#include <libtrx/game/objects/names.h>
|
||||
#include <libtrx/memory.h>
|
||||
#include <libtrx/virtual_file.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define GF_CURRENT_VERSION 3
|
||||
|
||||
static void M_ReadStringTable(
|
||||
VFILE *file, int32_t count, char ***table, char **buffer);
|
||||
|
||||
static GF_ADD_INV M_ModifyInventory_GetGunAdder(LARA_GUN_TYPE gun_type);
|
||||
static GF_ADD_INV M_ModifyInventory_GetAmmoAdder(LARA_GUN_TYPE gun_type);
|
||||
static GF_ADD_INV M_ModifyInventory_GetItemAdder(GAME_OBJECT_ID object_id);
|
||||
static void M_ModifyInventory_GunOrAmmo(
|
||||
START_INFO *start, int32_t type, LARA_GUN_TYPE gun_type);
|
||||
static void M_ModifyInventory_Item(int32_t type, GAME_OBJECT_ID object_id);
|
||||
|
||||
static void M_ReadStringTable(
|
||||
VFILE *const file, const int32_t count, char ***const table,
|
||||
char **const buffer)
|
||||
{
|
||||
VFile_Read(file, g_GF_LevelOffsets, sizeof(int16_t) * count);
|
||||
|
||||
const int16_t buf_size = VFile_ReadS16(file);
|
||||
*buffer = Memory_Alloc(buf_size);
|
||||
VFile_Read(file, *buffer, buf_size);
|
||||
|
||||
if (g_GameFlow.cyphered_strings) {
|
||||
for (int32_t i = 0; i < buf_size; i++) {
|
||||
(*buffer)[i] ^= g_GameFlow.cypher_code;
|
||||
}
|
||||
}
|
||||
|
||||
*table = Memory_Alloc(sizeof(char *) * count);
|
||||
for (int32_t i = 0; i < count; i++) {
|
||||
const int32_t offset = g_GF_LevelOffsets[i];
|
||||
(*table)[i] = &(*buffer)[offset];
|
||||
}
|
||||
}
|
||||
|
||||
static GF_ADD_INV M_ModifyInventory_GetGunAdder(const LARA_GUN_TYPE gun_type)
|
||||
{
|
||||
// clang-format off
|
||||
switch (gun_type) {
|
||||
case LGT_PISTOLS: return GF_ADD_INV_PISTOLS;
|
||||
case LGT_MAGNUMS: return GF_ADD_INV_MAGNUMS;
|
||||
case LGT_UZIS: return GF_ADD_INV_UZIS;
|
||||
case LGT_SHOTGUN: return GF_ADD_INV_SHOTGUN;
|
||||
case LGT_HARPOON: return GF_ADD_INV_HARPOON;
|
||||
case LGT_M16: return GF_ADD_INV_M16;
|
||||
case LGT_GRENADE: return GF_ADD_INV_GRENADE;
|
||||
default: return (GF_ADD_INV)-1;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
static GF_ADD_INV M_ModifyInventory_GetAmmoAdder(const LARA_GUN_TYPE gun_type)
|
||||
{
|
||||
// clang-format off
|
||||
switch (gun_type) {
|
||||
case LGT_PISTOLS: return GF_ADD_INV_PISTOL_AMMO;
|
||||
case LGT_MAGNUMS: return GF_ADD_INV_MAGNUM_AMMO;
|
||||
case LGT_UZIS: return GF_ADD_INV_UZI_AMMO;
|
||||
case LGT_SHOTGUN: return GF_ADD_INV_SHOTGUN_AMMO;
|
||||
case LGT_HARPOON: return GF_ADD_INV_HARPOON_AMMO;
|
||||
case LGT_M16: return GF_ADD_INV_M16_AMMO;
|
||||
case LGT_GRENADE: return GF_ADD_INV_GRENADE_AMMO;
|
||||
default: return (GF_ADD_INV)-1;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
static GF_ADD_INV M_ModifyInventory_GetItemAdder(const GAME_OBJECT_ID object_id)
|
||||
{
|
||||
// clang-format off
|
||||
switch (object_id) {
|
||||
case O_FLARE_ITEM: return GF_ADD_INV_FLARES;
|
||||
case O_SMALL_MEDIPACK_ITEM: return GF_ADD_INV_SMALL_MEDI;
|
||||
case O_LARGE_MEDIPACK_ITEM: return GF_ADD_INV_LARGE_MEDI;
|
||||
case O_PICKUP_ITEM_1: return GF_ADD_INV_PICKUP_1;
|
||||
case O_PICKUP_ITEM_2: return GF_ADD_INV_PICKUP_2;
|
||||
case O_PUZZLE_ITEM_1: return GF_ADD_INV_PUZZLE_1;
|
||||
case O_PUZZLE_ITEM_2: return GF_ADD_INV_PUZZLE_2;
|
||||
case O_PUZZLE_ITEM_3: return GF_ADD_INV_PUZZLE_3;
|
||||
case O_PUZZLE_ITEM_4: return GF_ADD_INV_PUZZLE_4;
|
||||
case O_KEY_ITEM_1: return GF_ADD_INV_KEY_1;
|
||||
case O_KEY_ITEM_2: return GF_ADD_INV_KEY_2;
|
||||
case O_KEY_ITEM_3: return GF_ADD_INV_KEY_3;
|
||||
case O_KEY_ITEM_4: return GF_ADD_INV_KEY_4;
|
||||
default: return (GF_ADD_INV)-1;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
static void M_ModifyInventory_GunOrAmmo(
|
||||
START_INFO *const start, const int32_t type, const LARA_GUN_TYPE gun_type)
|
||||
{
|
||||
const GAME_OBJECT_ID gun_item = Gun_GetGunObject(gun_type);
|
||||
const GAME_OBJECT_ID ammo_item = Gun_GetAmmoObject(gun_type);
|
||||
const int32_t ammo_qty = Gun_GetAmmoQuantity(gun_type);
|
||||
AMMO_INFO *const ammo_info = Gun_GetAmmoInfo(gun_type);
|
||||
|
||||
const GF_ADD_INV gun_adder = M_ModifyInventory_GetGunAdder(gun_type);
|
||||
const GF_ADD_INV ammo_adder = M_ModifyInventory_GetAmmoAdder(gun_type);
|
||||
|
||||
if (Inv_RequestItem(gun_item)) {
|
||||
if (type == 1) {
|
||||
ammo_info->ammo += ammo_qty * g_GF_SecretInvItems[ammo_adder];
|
||||
for (int32_t i = 0; i < g_GF_SecretInvItems[ammo_adder]; i++) {
|
||||
Overlay_AddDisplayPickup(ammo_item);
|
||||
}
|
||||
} else if (type == 0) {
|
||||
ammo_info->ammo += ammo_qty * g_GF_Add2InvItems[ammo_adder];
|
||||
}
|
||||
} else if (
|
||||
(type == 0 && g_GF_Add2InvItems[gun_adder])
|
||||
|| (type == 1 && g_GF_SecretInvItems[gun_adder])) {
|
||||
|
||||
// clang-format off
|
||||
// TODO: consider moving this to Inv_AddItem
|
||||
switch (gun_type) {
|
||||
case LGT_PISTOLS: start->has_pistols = 1; break;
|
||||
case LGT_MAGNUMS: start->has_magnums = 1; break;
|
||||
case LGT_UZIS: start->has_uzis = 1; break;
|
||||
case LGT_SHOTGUN: start->has_shotgun = 1; break;
|
||||
case LGT_HARPOON: start->has_harpoon = 1; break;
|
||||
case LGT_M16: start->has_m16 = 1; break;
|
||||
case LGT_GRENADE: start->has_grenade = 1; break;
|
||||
default: break;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
Inv_AddItem(gun_item);
|
||||
|
||||
if (type == 1) {
|
||||
ammo_info->ammo += ammo_qty * g_GF_SecretInvItems[ammo_adder];
|
||||
Overlay_AddDisplayPickup(gun_item);
|
||||
for (int32_t i = 0; i < g_GF_SecretInvItems[ammo_adder]; i++) {
|
||||
Overlay_AddDisplayPickup(ammo_item);
|
||||
}
|
||||
} else if (type == 0) {
|
||||
ammo_info->ammo += ammo_qty * g_GF_Add2InvItems[ammo_adder];
|
||||
}
|
||||
} else if (type == 1) {
|
||||
for (int32_t i = 0; i < g_GF_SecretInvItems[ammo_adder]; i++) {
|
||||
Inv_AddItem(ammo_item);
|
||||
Overlay_AddDisplayPickup(ammo_item);
|
||||
}
|
||||
} else if (type == 0) {
|
||||
for (int32_t i = 0; i < g_GF_Add2InvItems[ammo_adder]; i++) {
|
||||
Inv_AddItem(ammo_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void M_ModifyInventory_Item(
|
||||
const int32_t type, const GAME_OBJECT_ID object_id)
|
||||
{
|
||||
const GF_ADD_INV item_adder = M_ModifyInventory_GetItemAdder(object_id);
|
||||
int32_t qty = 0;
|
||||
if (type == 1) {
|
||||
qty = g_GF_SecretInvItems[item_adder];
|
||||
} else if (type == 0) {
|
||||
qty = g_GF_Add2InvItems[item_adder];
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < qty; i++) {
|
||||
Inv_AddItem(object_id);
|
||||
if (type == 1) {
|
||||
Overlay_AddDisplayPickup(object_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: inline me into GF_LoadScriptFile
|
||||
BOOL __cdecl GF_LoadFromFile(const char *const file_name)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
DWORD bytes_read;
|
||||
|
||||
const char *full_path = GetFullPath(file_name);
|
||||
VFILE *const file = VFile_CreateFromPath(full_path);
|
||||
if (file == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
g_GF_ScriptVersion = VFile_ReadS32(file);
|
||||
if (g_GF_ScriptVersion != GF_CURRENT_VERSION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VFile_Read(file, g_GF_Description, 256);
|
||||
|
||||
if (VFile_ReadS16(file) != sizeof(GAME_FLOW)) {
|
||||
return false;
|
||||
}
|
||||
g_GameFlow.first_option = VFile_ReadS32(file);
|
||||
g_GameFlow.title_replace = VFile_ReadS32(file);
|
||||
g_GameFlow.on_death_demo_mode = VFile_ReadS32(file);
|
||||
g_GameFlow.on_death_in_game = VFile_ReadS32(file);
|
||||
g_GameFlow.no_input_time = VFile_ReadS32(file);
|
||||
g_GameFlow.on_demo_interrupt = VFile_ReadS32(file);
|
||||
g_GameFlow.on_demo_end = VFile_ReadS32(file);
|
||||
VFile_Skip(file, 36);
|
||||
g_GameFlow.num_levels = VFile_ReadU16(file);
|
||||
g_GameFlow.num_pictures = VFile_ReadU16(file);
|
||||
g_GameFlow.num_titles = VFile_ReadU16(file);
|
||||
g_GameFlow.num_fmvs = VFile_ReadU16(file);
|
||||
g_GameFlow.num_cutscenes = VFile_ReadU16(file);
|
||||
g_GameFlow.num_demos = VFile_ReadU16(file);
|
||||
g_GameFlow.title_track = VFile_ReadU16(file);
|
||||
g_GameFlow.single_level = VFile_ReadS16(file);
|
||||
VFile_Skip(file, 32);
|
||||
|
||||
const uint16_t flags = VFile_ReadU16(file);
|
||||
// clang-format off
|
||||
g_GameFlow.demo_version = flags & 0x0001 ? 1 : 0;
|
||||
g_GameFlow.title_disabled = flags & 0x0002 ? 1 : 0;
|
||||
g_GameFlow.cheat_mode_check_disabled = flags & 0x0004 ? 1 : 0;
|
||||
g_GameFlow.no_input_timeout = flags & 0x0008 ? 1 : 0;
|
||||
g_GameFlow.load_save_disabled = flags & 0x0010 ? 1 : 0;
|
||||
g_GameFlow.screen_sizing_disabled = flags & 0x0020 ? 1 : 0;
|
||||
g_GameFlow.lockout_option_ring = flags & 0x0040 ? 1 : 0;
|
||||
g_GameFlow.dozy_cheat_enabled = flags & 0x0080 ? 1 : 0;
|
||||
g_GameFlow.cyphered_strings = flags & 0x0100 ? 1 : 0;
|
||||
g_GameFlow.gym_enabled = flags & 0x0200 ? 1 : 0;
|
||||
g_GameFlow.play_any_level = flags & 0x0400 ? 1 : 0;
|
||||
g_GameFlow.cheat_enable = flags & 0x0800 ? 1 : 0;
|
||||
// clang-format on
|
||||
VFile_Skip(file, 6);
|
||||
|
||||
g_GameFlow.cypher_code = VFile_ReadU8(file);
|
||||
g_GameFlow.language = VFile_ReadU8(file);
|
||||
g_GameFlow.secret_track = VFile_ReadU8(file);
|
||||
g_GameFlow.level_complete_track = VFile_ReadU8(file);
|
||||
VFile_Skip(file, 4);
|
||||
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_LevelNames, &g_GF_LevelNamesBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_pictures, &g_GF_PicFilenames,
|
||||
&g_GF_PicFilenamesBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_titles, &g_GF_TitleFileNames,
|
||||
&g_GF_TitleFileNamesBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_fmvs, &g_GF_FMVFilenames, &g_GF_FMVFilenamesBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_LevelFileNames,
|
||||
&g_GF_LevelFileNamesBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_cutscenes, &g_GF_CutsceneFileNames,
|
||||
&g_GF_CutsceneFileNamesBuf);
|
||||
|
||||
VFile_Read(
|
||||
file, &g_GF_LevelOffsets,
|
||||
sizeof(int16_t) * (g_GameFlow.num_levels + 1));
|
||||
{
|
||||
const int16_t size = VFile_ReadS16(file);
|
||||
g_GF_SequenceBuf = Memory_Alloc(size);
|
||||
VFile_Read(file, g_GF_SequenceBuf, size);
|
||||
}
|
||||
|
||||
g_GF_FrontendSequence = g_GF_SequenceBuf;
|
||||
for (int32_t i = 0; i < g_GameFlow.num_levels; i++) {
|
||||
g_GF_ScriptTable[i] = g_GF_SequenceBuf + (g_GF_LevelOffsets[i + 1] / 2);
|
||||
}
|
||||
|
||||
VFile_Read(file, g_GF_ValidDemos, sizeof(int16_t) * g_GameFlow.num_demos);
|
||||
|
||||
if (VFile_ReadS16(file) != GF_S_GAME_NUMBER_OF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
M_ReadStringTable(
|
||||
file, GF_S_GAME_NUMBER_OF, &g_GF_GameStrings, &g_GF_GameStringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, GF_S_PC_NUMBER_OF, &g_GF_PCStrings, &g_GF_PCStringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_Puzzle1Strings,
|
||||
&g_GF_Puzzle1StringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_Puzzle2Strings,
|
||||
&g_GF_Puzzle2StringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_Puzzle3Strings,
|
||||
&g_GF_Puzzle3StringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_Puzzle4Strings,
|
||||
&g_GF_Puzzle4StringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_Pickup1Strings,
|
||||
&g_GF_Pickup1StringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_Pickup2Strings,
|
||||
&g_GF_Pickup2StringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_Key1Strings, &g_GF_Key1StringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_Key2Strings, &g_GF_Key2StringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_Key3Strings, &g_GF_Key3StringsBuf);
|
||||
M_ReadStringTable(
|
||||
file, g_GameFlow.num_levels, &g_GF_Key4Strings, &g_GF_Key4StringsBuf);
|
||||
|
||||
VFile_Close(file);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t __cdecl GF_LoadScriptFile(const char *const fname)
|
||||
{
|
||||
g_GF_SunsetEnabled = false;
|
||||
|
||||
if (!GF_LoadFromFile(fname)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
g_GameFlow.level_complete_track = MX_END_OF_LEVEL;
|
||||
|
||||
Requester_SetHeading(
|
||||
&g_LoadGameRequester, g_GF_GameStrings[GF_S_GAME_PASSPORT_SELECT_LEVEL],
|
||||
0, 0, 0);
|
||||
Requester_SetHeading(
|
||||
&g_SaveGameRequester, g_GF_GameStrings[GF_S_GAME_PASSPORT_SELECT_LEVEL],
|
||||
0, 0, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t __cdecl GF_DoFrontendSequence(void)
|
||||
{
|
||||
GF_N_LoadStrings(-1);
|
||||
const GAME_FLOW_DIR dir =
|
||||
GF_InterpretSequence(g_GF_FrontendSequence, GFL_NORMAL, 1);
|
||||
return dir == GFD_EXIT_GAME;
|
||||
}
|
||||
|
||||
int32_t __cdecl GF_DoLevelSequence(
|
||||
const int32_t start_level, const GAMEFLOW_LEVEL_TYPE type)
|
||||
{
|
||||
GF_N_LoadStrings(start_level);
|
||||
|
||||
int32_t current_level = start_level;
|
||||
while (true) {
|
||||
if (current_level > g_GameFlow.num_levels - 1) {
|
||||
g_IsTitleLoaded = false;
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
|
||||
const int16_t *const ptr = g_GF_ScriptTable[current_level];
|
||||
const GAME_FLOW_DIR dir = GF_InterpretSequence(ptr, type, 0);
|
||||
current_level++;
|
||||
|
||||
if (g_GameFlow.single_level >= 0) {
|
||||
return dir;
|
||||
}
|
||||
if ((dir & ~0xFF) != GFD_LEVEL_COMPLETE) {
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __cdecl GF_InterpretSequence(
|
||||
const int16_t *ptr, GAMEFLOW_LEVEL_TYPE type, const int32_t seq_type)
|
||||
{
|
||||
g_GF_NoFloor = false;
|
||||
g_GF_DeadlyWater = false;
|
||||
g_GF_SunsetEnabled = false;
|
||||
g_GF_LaraStartAnim = 0;
|
||||
g_GF_Kill2Complete = false;
|
||||
g_GF_RemoveAmmo = false;
|
||||
g_GF_RemoveWeapons = false;
|
||||
|
||||
for (int32_t i = 0; i < GF_ADD_INV_NUMBER_OF; i++) {
|
||||
g_GF_SecretInvItems[i] = 0;
|
||||
g_GF_Add2InvItems[i] = 0;
|
||||
}
|
||||
|
||||
g_GF_MusicTracks[0] = 2;
|
||||
g_CineTargetAngle = PHD_90;
|
||||
g_GF_NumSecrets = 3;
|
||||
|
||||
int32_t ntracks = 0;
|
||||
GAME_FLOW_DIR dir = GFD_EXIT_TO_TITLE;
|
||||
|
||||
while (*ptr != GFE_END_SEQ) {
|
||||
switch (*ptr) {
|
||||
case GFE_PICTURE:
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_LIST_START:
|
||||
case GFE_LIST_END:
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case GFE_PLAY_FMV:
|
||||
if (type != GFL_SAVED) {
|
||||
if (ptr[2] == GFE_PLAY_FMV) {
|
||||
if (S_IntroFMV(
|
||||
g_GF_FMVFilenames[ptr[1]],
|
||||
g_GF_FMVFilenames[ptr[3]])) {
|
||||
return GFD_EXIT_GAME;
|
||||
}
|
||||
ptr += 2;
|
||||
} else if (S_PlayFMV(g_GF_FMVFilenames[ptr[1]])) {
|
||||
return GFD_EXIT_GAME;
|
||||
}
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_START_LEVEL:
|
||||
if (ptr[1] > g_GameFlow.num_levels) {
|
||||
dir = GFD_EXIT_TO_TITLE;
|
||||
} else if (type != GFL_STORY) {
|
||||
if (type == GFL_MID_STORY) {
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
dir = Game_Start(ptr[1], type);
|
||||
g_GF_StartGame = 0;
|
||||
if (type == GFL_SAVED) {
|
||||
type = GFL_NORMAL;
|
||||
}
|
||||
if ((dir & ~0xFF) != GFD_LEVEL_COMPLETE) {
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_CUTSCENE:
|
||||
if (type != GFL_SAVED) {
|
||||
const int16_t level = g_CurrentLevel;
|
||||
const int32_t result = Game_Cutscene_Start(ptr[1]);
|
||||
g_CurrentLevel = level;
|
||||
// TODO: make Game_Cutscene_Start return GAME_FLOW_DIR
|
||||
if (result == 2
|
||||
&& (type == GFL_STORY || type == GFL_MID_STORY)) {
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
if (result == 3) {
|
||||
return GFD_EXIT_GAME;
|
||||
}
|
||||
if (result == 4) {
|
||||
dir = g_GF_OverrideDir;
|
||||
g_GF_OverrideDir = (GAME_FLOW_DIR)-1;
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_LEVEL_COMPLETE:
|
||||
if (type != GFL_STORY && type != GFL_MID_STORY) {
|
||||
if (LevelStats(g_CurrentLevel)) {
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
dir = GFD_START_GAME | (g_CurrentLevel + 1);
|
||||
}
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case GFE_DEMO_PLAY:
|
||||
if (type != GFL_SAVED && type != GFL_STORY
|
||||
&& type != GFL_MID_STORY) {
|
||||
return Demo_Start(ptr[1]);
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_JUMP_TO_SEQ:
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_SET_TRACK:
|
||||
g_GF_MusicTracks[ntracks] = ptr[1];
|
||||
Game_SetCutsceneTrack(ptr[1]);
|
||||
ntracks++;
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_SUNSET:
|
||||
if (type != GFL_STORY && type != GFL_MID_STORY) {
|
||||
g_GF_SunsetEnabled = true;
|
||||
}
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case GFE_LOADING_PIC:
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_DEADLY_WATER:
|
||||
if (type != GFL_STORY && type != GFL_MID_STORY) {
|
||||
g_GF_DeadlyWater = true;
|
||||
}
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case GFE_REMOVE_WEAPONS:
|
||||
if (type != GFL_STORY && type != GFL_MID_STORY
|
||||
&& type != GFL_SAVED) {
|
||||
g_GF_RemoveWeapons = true;
|
||||
}
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case GFE_GAME_COMPLETE:
|
||||
DisplayCredits();
|
||||
if (GameStats(g_CurrentLevel)) {
|
||||
return GFD_EXIT_TO_TITLE;
|
||||
}
|
||||
dir = GFD_EXIT_TO_TITLE;
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case GFE_CUT_ANGLE:
|
||||
if (type != GFL_SAVED) {
|
||||
g_CineTargetAngle = ptr[1];
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_NO_FLOOR:
|
||||
if (type != GFL_STORY && type != GFL_MID_STORY) {
|
||||
g_GF_NoFloor = ptr[1];
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_ADD_TO_INV:
|
||||
if (type != GFL_STORY && type != GFL_MID_STORY) {
|
||||
if (ptr[1] < 1000) {
|
||||
g_GF_SecretInvItems[ptr[1]]++;
|
||||
} else if (type != GFL_SAVED) {
|
||||
g_GF_Add2InvItems[ptr[1] - 1000]++;
|
||||
}
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_START_ANIM:
|
||||
if (type != GFL_STORY && type != GFL_MID_STORY) {
|
||||
g_GF_LaraStartAnim = ptr[1];
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_NUM_SECRETS:
|
||||
if (type != GFL_STORY && type != GFL_MID_STORY) {
|
||||
g_GF_NumSecrets = ptr[1];
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case GFE_KILL_TO_COMPLETE:
|
||||
if (type != GFL_STORY && type != GFL_MID_STORY) {
|
||||
g_GF_Kill2Complete = true;
|
||||
}
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
case GFE_REMOVE_AMMO:
|
||||
if (type != GFL_STORY && type != GFL_MID_STORY
|
||||
&& type != GFL_SAVED) {
|
||||
g_GF_RemoveAmmo = true;
|
||||
}
|
||||
ptr++;
|
||||
break;
|
||||
|
||||
default:
|
||||
return GFD_EXIT_GAME;
|
||||
}
|
||||
}
|
||||
|
||||
if (type == GFL_STORY || type == GFL_MID_STORY) {
|
||||
return 0;
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
void __cdecl GF_ModifyInventory(const int32_t level, const int32_t type)
|
||||
{
|
||||
START_INFO *const start = &g_SaveGame.start[level];
|
||||
|
||||
if (!start->has_pistols && g_GF_Add2InvItems[GF_ADD_INV_PISTOLS]) {
|
||||
start->has_pistols = 1;
|
||||
Inv_AddItem(O_PISTOL_ITEM);
|
||||
}
|
||||
|
||||
M_ModifyInventory_GunOrAmmo(start, type, LGT_MAGNUMS);
|
||||
M_ModifyInventory_GunOrAmmo(start, type, LGT_UZIS);
|
||||
M_ModifyInventory_GunOrAmmo(start, type, LGT_SHOTGUN);
|
||||
M_ModifyInventory_GunOrAmmo(start, type, LGT_HARPOON);
|
||||
M_ModifyInventory_GunOrAmmo(start, type, LGT_M16);
|
||||
M_ModifyInventory_GunOrAmmo(start, type, LGT_GRENADE);
|
||||
|
||||
M_ModifyInventory_Item(type, O_FLARE_ITEM);
|
||||
M_ModifyInventory_Item(type, O_SMALL_MEDIPACK_ITEM);
|
||||
M_ModifyInventory_Item(type, O_LARGE_MEDIPACK_ITEM);
|
||||
M_ModifyInventory_Item(type, O_PICKUP_ITEM_1);
|
||||
M_ModifyInventory_Item(type, O_PICKUP_ITEM_2);
|
||||
M_ModifyInventory_Item(type, O_PUZZLE_ITEM_1);
|
||||
M_ModifyInventory_Item(type, O_PUZZLE_ITEM_2);
|
||||
M_ModifyInventory_Item(type, O_PUZZLE_ITEM_3);
|
||||
M_ModifyInventory_Item(type, O_PUZZLE_ITEM_4);
|
||||
M_ModifyInventory_Item(type, O_KEY_ITEM_1);
|
||||
M_ModifyInventory_Item(type, O_KEY_ITEM_2);
|
||||
M_ModifyInventory_Item(type, O_KEY_ITEM_3);
|
||||
M_ModifyInventory_Item(type, O_KEY_ITEM_4);
|
||||
|
||||
for (int32_t i = 0; i < GF_ADD_INV_NUMBER_OF; i++) {
|
||||
if (type == 1) {
|
||||
g_GF_SecretInvItems[i] = 0;
|
||||
} else if (type == 0) {
|
||||
g_GF_Add2InvItems[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
13
src/tr2/game/gameflow.h
Normal file
13
src/tr2/game/gameflow.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
BOOL __cdecl GF_LoadFromFile(const char *file_name);
|
||||
int32_t __cdecl GF_LoadScriptFile(const char *fname);
|
||||
int32_t __cdecl GF_DoFrontendSequence(void);
|
||||
int32_t __cdecl GF_DoLevelSequence(int32_t level, GAMEFLOW_LEVEL_TYPE type);
|
||||
int32_t __cdecl GF_InterpretSequence(
|
||||
const int16_t *ptr, GAMEFLOW_LEVEL_TYPE type, int32_t seq_type);
|
||||
void __cdecl GF_ModifyInventory(int32_t level, int32_t type);
|
272
src/tr2/game/gameflow/gameflow_new.c
Normal file
272
src/tr2/game/gameflow/gameflow_new.c
Normal file
|
@ -0,0 +1,272 @@
|
|||
#include "game/gameflow/gameflow_new.h"
|
||||
|
||||
#include "game/game_string.h"
|
||||
#include "global/types.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/enum_map.h>
|
||||
#include <libtrx/game/gameflow/types.h>
|
||||
#include <libtrx/game/objects/names.h>
|
||||
#include <libtrx/log.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
GAMEFLOW_NEW g_GameflowNew;
|
||||
GAME_INFO g_GameInfo;
|
||||
|
||||
static void M_LoadObjectString(const char *key, const char *value);
|
||||
static void M_LoadGameString(const char *key, const char *value);
|
||||
static void M_LoadObjectStrings(const int32_t level_num);
|
||||
static void M_LoadGameStrings(const int32_t level_num);
|
||||
|
||||
static void M_LoadObjectString(const char *const key, const char *const value)
|
||||
{
|
||||
const GAME_OBJECT_ID object_id =
|
||||
ENUM_MAP_GET(GAME_OBJECT_ID, key, NO_OBJECT);
|
||||
if (object_id != NO_OBJECT) {
|
||||
Object_SetName(object_id, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void M_LoadGameString(const char *const key, const char *const value)
|
||||
{
|
||||
if (!GameString_IsKnown(key)) {
|
||||
LOG_ERROR("Invalid game string key: %s", key);
|
||||
} else if (value == NULL) {
|
||||
LOG_ERROR("Invalid game string value: %s", key);
|
||||
} else {
|
||||
GameString_Define(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
static void M_LoadObjectStrings(const int32_t level_num)
|
||||
{
|
||||
const GAMEFLOW_NEW *const gf = &g_GameflowNew;
|
||||
|
||||
const GAMEFLOW_NEW_STRING_ENTRY *entry = gf->object_strings;
|
||||
while (entry != NULL && entry->key != NULL) {
|
||||
M_LoadObjectString(entry->key, entry->value);
|
||||
entry++;
|
||||
}
|
||||
|
||||
if (level_num >= 0) {
|
||||
assert(level_num < gf->level_count);
|
||||
const GAMEFLOW_NEW_LEVEL *const level = &gf->levels[level_num];
|
||||
entry = level->object_strings;
|
||||
while (entry != NULL && entry->key != NULL) {
|
||||
M_LoadObjectString(entry->key, entry->value);
|
||||
entry++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void M_LoadGameStrings(const int32_t level_num)
|
||||
{
|
||||
const GAMEFLOW_NEW *const gf = &g_GameflowNew;
|
||||
|
||||
const GAMEFLOW_NEW_STRING_ENTRY *entry = gf->game_strings;
|
||||
while (entry != NULL && entry->key != NULL) {
|
||||
M_LoadGameString(entry->key, entry->value);
|
||||
entry++;
|
||||
}
|
||||
|
||||
if (level_num >= 0) {
|
||||
assert(level_num < gf->level_count);
|
||||
const GAMEFLOW_NEW_LEVEL *const level = &gf->levels[level_num];
|
||||
entry = level->game_strings;
|
||||
while (entry != NULL && entry->key != NULL) {
|
||||
M_LoadGameString(entry->key, entry->value);
|
||||
entry++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GF_N_LoadStrings(const int32_t level_num)
|
||||
{
|
||||
Object_ResetNames();
|
||||
|
||||
M_LoadObjectStrings(level_num);
|
||||
M_LoadGameStrings(level_num);
|
||||
|
||||
struct {
|
||||
GAME_OBJECT_ID object_id;
|
||||
GF_GAME_STRING game_string;
|
||||
} game_string_defs[] = {
|
||||
{ O_COMPASS_OPTION, GF_S_GAME_INV_ITEM_STATISTICS },
|
||||
{ O_COMPASS_ITEM, GF_S_GAME_INV_ITEM_STATISTICS },
|
||||
{ O_PISTOL_ITEM, GF_S_GAME_INV_ITEM_PISTOLS },
|
||||
{ O_PISTOL_OPTION, GF_S_GAME_INV_ITEM_PISTOLS },
|
||||
{ O_FLARE_ITEM, GF_S_GAME_INV_ITEM_FLARE },
|
||||
{ O_FLARES_OPTION, GF_S_GAME_INV_ITEM_FLARE },
|
||||
{ O_SHOTGUN_ITEM, GF_S_GAME_INV_ITEM_SHOTGUN },
|
||||
{ O_SHOTGUN_OPTION, GF_S_GAME_INV_ITEM_SHOTGUN },
|
||||
{ O_MAGNUM_ITEM, GF_S_GAME_INV_ITEM_MAGNUMS },
|
||||
{ O_MAGNUM_OPTION, GF_S_GAME_INV_ITEM_MAGNUMS },
|
||||
{ O_UZI_ITEM, GF_S_GAME_INV_ITEM_UZIS },
|
||||
{ O_UZI_OPTION, GF_S_GAME_INV_ITEM_UZIS },
|
||||
{ O_HARPOON_ITEM, GF_S_GAME_INV_ITEM_HARPOON },
|
||||
{ O_HARPOON_OPTION, GF_S_GAME_INV_ITEM_HARPOON },
|
||||
{ O_M16_ITEM, GF_S_GAME_INV_ITEM_M16 },
|
||||
{ O_M16_OPTION, GF_S_GAME_INV_ITEM_M16 },
|
||||
{ O_GRENADE_ITEM, GF_S_GAME_INV_ITEM_GRENADE },
|
||||
{ O_GRENADE_OPTION, GF_S_GAME_INV_ITEM_GRENADE },
|
||||
{ O_PISTOL_AMMO_ITEM, GF_S_GAME_INV_ITEM_PISTOL_AMMO },
|
||||
{ O_PISTOL_AMMO_OPTION, GF_S_GAME_INV_ITEM_PISTOL_AMMO },
|
||||
{ O_SHOTGUN_AMMO_ITEM, GF_S_GAME_INV_ITEM_SHOTGUN_AMMO },
|
||||
{ O_SHOTGUN_AMMO_OPTION, GF_S_GAME_INV_ITEM_SHOTGUN_AMMO },
|
||||
{ O_MAGNUM_AMMO_ITEM, GF_S_GAME_INV_ITEM_MAGNUM_AMMO },
|
||||
{ O_MAGNUM_AMMO_OPTION, GF_S_GAME_INV_ITEM_MAGNUM_AMMO },
|
||||
{ O_UZI_AMMO_ITEM, GF_S_GAME_INV_ITEM_UZI_AMMO },
|
||||
{ O_UZI_AMMO_OPTION, GF_S_GAME_INV_ITEM_UZI_AMMO },
|
||||
{ O_HARPOON_AMMO_ITEM, GF_S_GAME_INV_ITEM_HARPOON_AMMO },
|
||||
{ O_HARPOON_AMMO_OPTION, GF_S_GAME_INV_ITEM_HARPOON_AMMO },
|
||||
{ O_M16_AMMO_ITEM, GF_S_GAME_INV_ITEM_M16_AMMO },
|
||||
{ O_M16_AMMO_OPTION, GF_S_GAME_INV_ITEM_M16_AMMO },
|
||||
{ O_GRENADE_AMMO_ITEM, GF_S_GAME_INV_ITEM_GRENADE_AMMO },
|
||||
{ O_GRENADE_AMMO_OPTION, GF_S_GAME_INV_ITEM_GRENADE_AMMO },
|
||||
{ O_SMALL_MEDIPACK_ITEM, GF_S_GAME_INV_ITEM_SMALL_MEDIPACK },
|
||||
{ O_SMALL_MEDIPACK_OPTION, GF_S_GAME_INV_ITEM_SMALL_MEDIPACK },
|
||||
{ O_LARGE_MEDIPACK_ITEM, GF_S_GAME_INV_ITEM_LARGE_MEDIPACK },
|
||||
{ O_LARGE_MEDIPACK_OPTION, GF_S_GAME_INV_ITEM_LARGE_MEDIPACK },
|
||||
{ O_PICKUP_ITEM_1, GF_S_GAME_INV_ITEM_PICKUP },
|
||||
{ O_PICKUP_OPTION_1, GF_S_GAME_INV_ITEM_PICKUP },
|
||||
{ O_PICKUP_ITEM_2, GF_S_GAME_INV_ITEM_PICKUP },
|
||||
{ O_PICKUP_OPTION_2, GF_S_GAME_INV_ITEM_PICKUP },
|
||||
{ O_PUZZLE_ITEM_1, GF_S_GAME_INV_ITEM_PUZZLE },
|
||||
{ O_PUZZLE_OPTION_1, GF_S_GAME_INV_ITEM_PUZZLE },
|
||||
{ O_PUZZLE_ITEM_2, GF_S_GAME_INV_ITEM_PUZZLE },
|
||||
{ O_PUZZLE_OPTION_2, GF_S_GAME_INV_ITEM_PUZZLE },
|
||||
{ O_PUZZLE_ITEM_3, GF_S_GAME_INV_ITEM_PUZZLE },
|
||||
{ O_PUZZLE_OPTION_3, GF_S_GAME_INV_ITEM_PUZZLE },
|
||||
{ O_PUZZLE_ITEM_4, GF_S_GAME_INV_ITEM_PUZZLE },
|
||||
{ O_PUZZLE_OPTION_4, GF_S_GAME_INV_ITEM_PUZZLE },
|
||||
{ O_KEY_ITEM_1, GF_S_GAME_INV_ITEM_KEY },
|
||||
{ O_KEY_OPTION_1, GF_S_GAME_INV_ITEM_KEY },
|
||||
{ O_KEY_ITEM_2, GF_S_GAME_INV_ITEM_KEY },
|
||||
{ O_KEY_OPTION_2, GF_S_GAME_INV_ITEM_KEY },
|
||||
{ O_KEY_ITEM_3, GF_S_GAME_INV_ITEM_KEY },
|
||||
{ O_KEY_OPTION_3, GF_S_GAME_INV_ITEM_KEY },
|
||||
{ O_KEY_ITEM_4, GF_S_GAME_INV_ITEM_KEY },
|
||||
{ O_KEY_OPTION_4, GF_S_GAME_INV_ITEM_KEY },
|
||||
{ O_PASSPORT_OPTION, GF_S_GAME_INV_ITEM_GAME },
|
||||
{ O_PASSPORT_CLOSED, GF_S_GAME_INV_ITEM_GAME },
|
||||
{ O_PHOTO_OPTION, GF_S_GAME_INV_ITEM_LARA_HOME },
|
||||
{ NO_OBJECT, -1 },
|
||||
};
|
||||
|
||||
for (int32_t i = 0; game_string_defs[i].object_id != NO_OBJECT; i++) {
|
||||
const char *const new_name =
|
||||
g_GF_GameStrings[game_string_defs[i].game_string];
|
||||
if (new_name != NULL) {
|
||||
Object_SetName(game_string_defs[i].object_id, new_name);
|
||||
}
|
||||
}
|
||||
|
||||
struct {
|
||||
GAME_OBJECT_ID object_id;
|
||||
GF_PC_STRING pc_string;
|
||||
} pc_string_defs[] = {
|
||||
{ O_DETAIL_OPTION, GF_S_PC_DETAIL_LEVELS },
|
||||
{ O_SOUND_OPTION, GF_S_PC_SOUND },
|
||||
{ O_CONTROL_OPTION, GF_S_PC_CONTROLS },
|
||||
{ NO_OBJECT, -1 },
|
||||
};
|
||||
|
||||
for (int32_t i = 0; pc_string_defs[i].object_id != NO_OBJECT; i++) {
|
||||
const char *const new_name =
|
||||
g_GF_PCStrings[pc_string_defs[i].pc_string];
|
||||
if (new_name != NULL) {
|
||||
Object_SetName(pc_string_defs[i].object_id, new_name);
|
||||
}
|
||||
}
|
||||
|
||||
struct {
|
||||
GAME_OBJECT_ID object_id;
|
||||
char **strings;
|
||||
} level_item_defs[] = {
|
||||
{ O_PUZZLE_ITEM_1, g_GF_Puzzle1Strings },
|
||||
{ O_PUZZLE_ITEM_2, g_GF_Puzzle2Strings },
|
||||
{ O_PUZZLE_ITEM_3, g_GF_Puzzle3Strings },
|
||||
{ O_PUZZLE_ITEM_4, g_GF_Puzzle4Strings },
|
||||
{ O_KEY_ITEM_1, g_GF_Key1Strings },
|
||||
{ O_KEY_ITEM_2, g_GF_Key2Strings },
|
||||
{ O_KEY_ITEM_3, g_GF_Key3Strings },
|
||||
{ O_KEY_ITEM_4, g_GF_Key4Strings },
|
||||
{ O_PICKUP_ITEM_1, g_GF_Pickup1Strings },
|
||||
{ O_PICKUP_ITEM_2, g_GF_Pickup2Strings },
|
||||
{ O_PUZZLE_OPTION_1, g_GF_Puzzle1Strings },
|
||||
{ O_PUZZLE_OPTION_2, g_GF_Puzzle2Strings },
|
||||
{ O_PUZZLE_OPTION_3, g_GF_Puzzle3Strings },
|
||||
{ O_PUZZLE_OPTION_4, g_GF_Puzzle4Strings },
|
||||
{ O_KEY_OPTION_1, g_GF_Key1Strings },
|
||||
{ O_KEY_OPTION_2, g_GF_Key2Strings },
|
||||
{ O_KEY_OPTION_3, g_GF_Key3Strings },
|
||||
{ O_KEY_OPTION_4, g_GF_Key4Strings },
|
||||
{ O_PICKUP_OPTION_1, g_GF_Pickup1Strings },
|
||||
{ O_PICKUP_OPTION_2, g_GF_Pickup2Strings },
|
||||
{ NO_OBJECT, NULL },
|
||||
};
|
||||
|
||||
if (level_num >= 0 && level_num < g_GameFlow.num_levels) {
|
||||
for (int32_t i = 0; level_item_defs[i].object_id != NO_OBJECT; i++) {
|
||||
const char *const new_name = level_item_defs[i].strings[level_num];
|
||||
if (new_name != NULL) {
|
||||
Object_SetName(level_item_defs[i].object_id, new_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Gameflow_GetLevelCount(void)
|
||||
{
|
||||
return g_GameflowNew.level_count;
|
||||
}
|
||||
|
||||
const char *Gameflow_GetLevelFileName(int32_t level_num)
|
||||
{
|
||||
return g_GF_LevelFileNames[level_num];
|
||||
}
|
||||
|
||||
const char *Gameflow_GetLevelTitle(int32_t level_num)
|
||||
{
|
||||
return g_GF_LevelNames[level_num];
|
||||
}
|
||||
|
||||
int32_t Gameflow_GetGymLevelNumber(void)
|
||||
{
|
||||
return g_GameFlow.gym_enabled ? LV_GYM : -1;
|
||||
}
|
||||
|
||||
void Gameflow_OverrideCommand(const GAMEFLOW_COMMAND command)
|
||||
{
|
||||
switch (command.action) {
|
||||
case GF_START_GAME:
|
||||
g_GF_OverrideDir = GFD_START_GAME | command.param;
|
||||
break;
|
||||
case GF_START_SAVED_GAME:
|
||||
g_GF_OverrideDir = GFD_START_SAVED_GAME | command.param;
|
||||
break;
|
||||
case GF_START_CINE:
|
||||
g_GF_OverrideDir = GFD_START_CINE;
|
||||
break;
|
||||
case GF_START_FMV:
|
||||
g_GF_OverrideDir = GFD_START_FMV;
|
||||
break;
|
||||
case GF_START_DEMO:
|
||||
g_GF_OverrideDir = GFD_START_DEMO;
|
||||
break;
|
||||
case GF_EXIT_TO_TITLE:
|
||||
g_GF_OverrideDir = GFD_EXIT_TO_TITLE;
|
||||
break;
|
||||
case GF_LEVEL_COMPLETE:
|
||||
g_GF_OverrideDir = GFD_LEVEL_COMPLETE;
|
||||
break;
|
||||
case GF_EXIT_GAME:
|
||||
g_GF_OverrideDir = GFD_EXIT_GAME;
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Not implemented");
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
32
src/tr2/game/gameflow/gameflow_new.h
Normal file
32
src/tr2/game/gameflow/gameflow_new.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
GAMEFLOW_LEVEL_TYPE type;
|
||||
int32_t num;
|
||||
} current_level;
|
||||
} GAME_INFO;
|
||||
|
||||
typedef struct {
|
||||
const char *key;
|
||||
const char *value;
|
||||
} GAMEFLOW_NEW_STRING_ENTRY;
|
||||
|
||||
typedef struct {
|
||||
GAMEFLOW_NEW_STRING_ENTRY *object_strings;
|
||||
GAMEFLOW_NEW_STRING_ENTRY *game_strings;
|
||||
} GAMEFLOW_NEW_LEVEL;
|
||||
|
||||
typedef struct {
|
||||
int32_t level_count;
|
||||
GAMEFLOW_NEW_LEVEL *levels;
|
||||
GAMEFLOW_NEW_STRING_ENTRY *object_strings;
|
||||
GAMEFLOW_NEW_STRING_ENTRY *game_strings;
|
||||
} GAMEFLOW_NEW;
|
||||
|
||||
extern GAMEFLOW_NEW g_GameflowNew;
|
||||
extern GAME_INFO g_GameInfo;
|
||||
|
||||
void GF_N_LoadStrings(int32_t level_num);
|
171
src/tr2/game/gameflow/reader.c
Normal file
171
src/tr2/game/gameflow/reader.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
#include "game/gameflow/reader.h"
|
||||
|
||||
#include "game/gameflow/gameflow_new.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/filesystem.h>
|
||||
#include <libtrx/json.h>
|
||||
#include <libtrx/log.h>
|
||||
#include <libtrx/memory.h>
|
||||
|
||||
static void M_StringTableShutdown(GAMEFLOW_NEW_STRING_ENTRY *dest);
|
||||
static bool M_LoadStringTable(
|
||||
JSON_OBJECT *root_obj, const char *key, GAMEFLOW_NEW_STRING_ENTRY **dest);
|
||||
static bool M_LoadScriptLevels(JSON_OBJECT *obj, GAMEFLOW_NEW *gf);
|
||||
|
||||
static void M_StringTableShutdown(GAMEFLOW_NEW_STRING_ENTRY *const dest)
|
||||
{
|
||||
if (dest == NULL) {
|
||||
return;
|
||||
}
|
||||
GAMEFLOW_NEW_STRING_ENTRY *cur = dest;
|
||||
while (cur->key != NULL) {
|
||||
Memory_FreePointer(&cur->key);
|
||||
Memory_FreePointer(&cur->value);
|
||||
cur++;
|
||||
}
|
||||
Memory_Free(dest);
|
||||
}
|
||||
|
||||
static bool M_LoadStringTable(
|
||||
JSON_OBJECT *const root_obj, const char *const key,
|
||||
GAMEFLOW_NEW_STRING_ENTRY **dest)
|
||||
{
|
||||
JSON_VALUE *const strings_value = JSON_ObjectGetValue(root_obj, key);
|
||||
if (strings_value == NULL) {
|
||||
// key is missing - rely on default strings
|
||||
return true;
|
||||
}
|
||||
|
||||
JSON_OBJECT *const strings_obj = JSON_ValueAsObject(strings_value);
|
||||
if (strings_obj == NULL) {
|
||||
LOG_ERROR("'%s' must be a dictionary", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
*dest = Memory_Alloc(
|
||||
sizeof(GAMEFLOW_NEW_STRING_ENTRY) * (strings_obj->length + 1));
|
||||
|
||||
GAMEFLOW_NEW_STRING_ENTRY *cur = *dest;
|
||||
JSON_OBJECT_ELEMENT *strings_elem = strings_obj->start;
|
||||
for (size_t i = 0; i < strings_obj->length;
|
||||
i++, strings_elem = strings_elem->next) {
|
||||
JSON_STRING *const value = JSON_ValueAsString(strings_elem->value);
|
||||
if (value == NULL) {
|
||||
LOG_ERROR("invalid string key %s", strings_elem->name->string);
|
||||
return NULL;
|
||||
}
|
||||
cur->key = Memory_DupStr(strings_elem->name->string);
|
||||
cur->value = Memory_DupStr(value->string);
|
||||
cur++;
|
||||
}
|
||||
|
||||
cur->key = NULL;
|
||||
cur->value = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool M_LoadScriptLevels(JSON_OBJECT *obj, GAMEFLOW_NEW *const gf)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
JSON_ARRAY *const jlvl_arr = JSON_ObjectGetArray(obj, "levels");
|
||||
if (jlvl_arr == NULL) {
|
||||
LOG_ERROR("'levels' must be a list");
|
||||
result = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
int32_t level_count = jlvl_arr->length;
|
||||
if (level_count != g_GameFlow.num_levels) {
|
||||
LOG_ERROR(
|
||||
"'levels' must have exactly %d levels, as we still rely on legacy "
|
||||
"tombpc.dat",
|
||||
g_GameFlow.num_levels);
|
||||
result = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
gf->level_count = level_count;
|
||||
gf->levels = Memory_Alloc(sizeof(GAMEFLOW_NEW_LEVEL) * level_count);
|
||||
|
||||
JSON_ARRAY_ELEMENT *jlvl_elem = jlvl_arr->start;
|
||||
for (size_t i = 0; i < jlvl_arr->length; i++, jlvl_elem = jlvl_elem->next) {
|
||||
GAMEFLOW_NEW_LEVEL *const level = &gf->levels[i];
|
||||
|
||||
JSON_OBJECT *const jlvl_obj = JSON_ValueAsObject(jlvl_elem->value);
|
||||
if (jlvl_obj == NULL) {
|
||||
LOG_ERROR("'levels' elements must be dictionaries");
|
||||
result = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
result &= M_LoadStringTable(
|
||||
jlvl_obj, "object_strings", &level->object_strings);
|
||||
result &=
|
||||
M_LoadStringTable(jlvl_obj, "game_strings", &level->game_strings);
|
||||
}
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
bool GF_N_Load(const char *const path)
|
||||
{
|
||||
GF_N_Shutdown();
|
||||
|
||||
bool result = true;
|
||||
|
||||
char *script_data = NULL;
|
||||
if (!File_Load(path, &script_data, NULL)) {
|
||||
LOG_ERROR("failed to open script file");
|
||||
result = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
JSON_PARSE_RESULT parse_result;
|
||||
JSON_VALUE *root = JSON_ParseEx(
|
||||
script_data, strlen(script_data), JSON_PARSE_FLAGS_ALLOW_JSON5, NULL,
|
||||
NULL, &parse_result);
|
||||
if (root == NULL) {
|
||||
LOG_ERROR(
|
||||
"failed to parse script file: %s in line %d, char %d",
|
||||
JSON_GetErrorDescription(parse_result.error),
|
||||
parse_result.error_line_no, parse_result.error_row_no, script_data);
|
||||
result = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
GAMEFLOW_NEW *const gf = &g_GameflowNew;
|
||||
JSON_OBJECT *root_obj = JSON_ValueAsObject(root);
|
||||
result &=
|
||||
M_LoadStringTable(root_obj, "object_strings", &gf->object_strings);
|
||||
result &= M_LoadStringTable(root_obj, "game_strings", &gf->game_strings);
|
||||
result &= M_LoadScriptLevels(root_obj, gf);
|
||||
|
||||
end:
|
||||
if (root) {
|
||||
JSON_ValueFree(root);
|
||||
root = NULL;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
GF_N_Shutdown();
|
||||
}
|
||||
|
||||
Memory_FreePointer(&script_data);
|
||||
return result;
|
||||
}
|
||||
|
||||
void GF_N_Shutdown(void)
|
||||
{
|
||||
GAMEFLOW_NEW *const gf = &g_GameflowNew;
|
||||
|
||||
for (int32_t i = 0; i < gf->level_count; i++) {
|
||||
M_StringTableShutdown(gf->levels[i].object_strings);
|
||||
M_StringTableShutdown(gf->levels[i].game_strings);
|
||||
}
|
||||
|
||||
M_StringTableShutdown(gf->object_strings);
|
||||
M_StringTableShutdown(gf->game_strings);
|
||||
}
|
7
src/tr2/game/gameflow/reader.h
Normal file
7
src/tr2/game/gameflow/reader.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool GF_N_Load(const char *path);
|
||||
void GF_N_Shutdown(void);
|
425
src/tr2/game/gun/gun.c
Normal file
425
src/tr2/game/gun/gun.c
Normal file
|
@ -0,0 +1,425 @@
|
|||
#include "game/gun/gun.h"
|
||||
|
||||
#include "game/gun/gun_misc.h"
|
||||
#include "game/gun/gun_pistols.h"
|
||||
#include "game/gun/gun_rifle.h"
|
||||
#include "game/input.h"
|
||||
#include "game/inventory/backpack.h"
|
||||
#include "game/lara/control.h"
|
||||
#include "game/sound.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
typedef enum {
|
||||
LF_FL_HOLD_FT = 1,
|
||||
LF_FL_THROW_FT = 32,
|
||||
LF_FL_DRAW_FT = 39,
|
||||
LF_FL_IGNITE_FT = 23,
|
||||
LF_FL_2_HOLD_FT = 15,
|
||||
|
||||
LF_FL_HOLD = 0,
|
||||
LF_FL_THROW = (LF_FL_HOLD + LF_FL_HOLD_FT), // = 1
|
||||
LF_FL_DRAW = (LF_FL_THROW + LF_FL_THROW_FT), // = 33
|
||||
LF_FL_IGNITE = (LF_FL_DRAW + LF_FL_DRAW_FT), // = 72
|
||||
LF_FL_2_HOLD = (LF_FL_IGNITE + LF_FL_IGNITE_FT), // = 95
|
||||
LF_FL_END = (LF_FL_2_HOLD + LF_FL_2_HOLD_FT), // = 110
|
||||
} LARA_FLARE_ANIMATION_FRAME;
|
||||
|
||||
void __cdecl Gun_Control(void)
|
||||
{
|
||||
if (g_Lara.left_arm.flash_gun > 0) {
|
||||
g_Lara.left_arm.flash_gun--;
|
||||
}
|
||||
|
||||
if (g_Lara.right_arm.flash_gun > 0) {
|
||||
g_Lara.right_arm.flash_gun--;
|
||||
}
|
||||
|
||||
if (g_LaraItem->hit_points <= 0) {
|
||||
g_Lara.gun_status = LGS_ARMLESS;
|
||||
} else if (g_Lara.gun_status == LGS_ARMLESS) {
|
||||
if (g_Input & IN_DRAW) {
|
||||
g_Lara.request_gun_type = g_Lara.last_gun_type;
|
||||
} else if (g_InputDB & IN_FLARE) {
|
||||
if (g_Lara.gun_type == LGT_FLARE) {
|
||||
g_Lara.gun_status = LGS_UNDRAW;
|
||||
} else if (Inv_RequestItem(O_FLARES_ITEM)) {
|
||||
g_Lara.request_gun_type = LGT_FLARE;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_Lara.request_gun_type != g_Lara.gun_type || (g_Input & IN_DRAW)) {
|
||||
if (g_Lara.request_gun_type == LGT_FLARE
|
||||
|| (g_Lara.skidoo == NO_ITEM && g_Lara.water_status != LWS_CHEAT
|
||||
&& (g_Lara.request_gun_type == LGT_HARPOON
|
||||
|| g_Lara.water_status == LWS_ABOVE_WATER
|
||||
|| (g_Lara.water_status == LWS_WADE
|
||||
&& g_Lara.water_surface_dist
|
||||
> -g_Weapons[g_Lara.gun_type].gun_height)))) {
|
||||
if (g_Lara.gun_type == LGT_FLARE) {
|
||||
Flare_Create(0);
|
||||
Flare_UndrawMeshes();
|
||||
g_Lara.flare_control_left = 0;
|
||||
}
|
||||
g_Lara.gun_type = g_Lara.request_gun_type;
|
||||
Gun_InitialiseNewWeapon();
|
||||
g_Lara.gun_status = LGS_DRAW;
|
||||
g_Lara.right_arm.frame_num = 0;
|
||||
g_Lara.left_arm.frame_num = 0;
|
||||
} else {
|
||||
g_Lara.last_gun_type = g_Lara.request_gun_type;
|
||||
if (g_Lara.gun_type == LGT_FLARE) {
|
||||
g_Lara.request_gun_type = LGT_FLARE;
|
||||
} else {
|
||||
g_Lara.gun_type = g_Lara.request_gun_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (g_Lara.gun_status == LGS_READY) {
|
||||
if ((g_InputDB & IN_FLARE) && Inv_RequestItem(O_FLARES_ITEM)) {
|
||||
g_Lara.request_gun_type = LGT_FLARE;
|
||||
}
|
||||
|
||||
if ((g_Input & IN_DRAW) || g_Lara.request_gun_type != g_Lara.gun_type) {
|
||||
g_Lara.gun_status = LGS_UNDRAW;
|
||||
} else if (
|
||||
g_Lara.gun_type == LGT_HARPOON
|
||||
&& g_Lara.water_status == LWS_CHEAT) {
|
||||
g_Lara.gun_status = LGS_UNDRAW;
|
||||
} else if (
|
||||
g_Lara.gun_type != LGT_HARPOON
|
||||
&& g_Lara.water_status != LWS_ABOVE_WATER
|
||||
&& (g_Lara.water_status != LWS_WADE
|
||||
|| g_Lara.water_surface_dist
|
||||
< -g_Weapons[g_Lara.gun_type].gun_height)) {
|
||||
g_Lara.gun_status = LGS_UNDRAW;
|
||||
}
|
||||
}
|
||||
|
||||
switch (g_Lara.gun_status) {
|
||||
case LGS_ARMLESS:
|
||||
if (g_Lara.gun_type == LGT_FLARE) {
|
||||
if (g_Lara.skidoo != NO_ITEM
|
||||
|| Gun_CheckForHoldingState(g_LaraItem->current_anim_state)) {
|
||||
if (!g_Lara.flare_control_left) {
|
||||
g_Lara.left_arm.frame_num = LF_FL_2_HOLD;
|
||||
g_Lara.flare_control_left = 1;
|
||||
} else if (g_Lara.left_arm.frame_num != LF_FL_HOLD) {
|
||||
g_Lara.left_arm.frame_num++;
|
||||
if (g_Lara.left_arm.frame_num == LF_FL_END) {
|
||||
g_Lara.left_arm.frame_num = LF_FL_HOLD;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
g_Lara.flare_control_left = 0;
|
||||
}
|
||||
Flare_DoInHand(g_Lara.flare_age);
|
||||
Flare_SetArm(g_Lara.left_arm.frame_num);
|
||||
}
|
||||
break;
|
||||
|
||||
case LGS_HANDS_BUSY:
|
||||
if (g_Lara.gun_type == LGT_FLARE) {
|
||||
g_Lara.flare_control_left = g_Lara.skidoo != NO_ITEM
|
||||
|| Gun_CheckForHoldingState(g_LaraItem->current_anim_state);
|
||||
Flare_DoInHand(g_Lara.flare_age);
|
||||
Flare_SetArm(g_Lara.left_arm.frame_num);
|
||||
}
|
||||
break;
|
||||
|
||||
case LGS_DRAW:
|
||||
if (g_Lara.gun_type != LGT_FLARE && g_Lara.gun_type != LGT_UNARMED) {
|
||||
g_Lara.last_gun_type = g_Lara.gun_type;
|
||||
}
|
||||
|
||||
switch (g_Lara.gun_type) {
|
||||
case LGT_PISTOLS:
|
||||
case LGT_MAGNUMS:
|
||||
case LGT_UZIS:
|
||||
if (g_Camera.type != CAM_CINEMATIC && g_Camera.type != CAM_LOOK) {
|
||||
g_Camera.type = CAM_COMBAT;
|
||||
}
|
||||
Gun_Pistols_Draw(g_Lara.gun_type);
|
||||
break;
|
||||
|
||||
case LGT_SHOTGUN:
|
||||
case LGT_M16:
|
||||
case LGT_GRENADE:
|
||||
case LGT_HARPOON:
|
||||
if (g_Camera.type != CAM_CINEMATIC && g_Camera.type != CAM_LOOK) {
|
||||
g_Camera.type = CAM_COMBAT;
|
||||
}
|
||||
Gun_Rifle_Draw(g_Lara.gun_type);
|
||||
break;
|
||||
|
||||
case LGT_FLARE:
|
||||
Flare_Draw();
|
||||
break;
|
||||
|
||||
default:
|
||||
g_Lara.gun_status = LGS_ARMLESS;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case LGS_UNDRAW:
|
||||
g_Lara.mesh_ptrs[LM_HEAD] =
|
||||
g_Meshes[g_Objects[O_LARA].mesh_idx + LM_HEAD];
|
||||
|
||||
switch (g_Lara.gun_type) {
|
||||
case LGT_PISTOLS:
|
||||
case LGT_MAGNUMS:
|
||||
case LGT_UZIS:
|
||||
Gun_Pistols_Undraw(g_Lara.gun_type);
|
||||
break;
|
||||
|
||||
case LGT_SHOTGUN:
|
||||
case LGT_M16:
|
||||
case LGT_GRENADE:
|
||||
case LGT_HARPOON:
|
||||
Gun_Rifle_Undraw(g_Lara.gun_type);
|
||||
break;
|
||||
|
||||
case LGT_FLARE:
|
||||
Flare_Undraw();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case LGS_READY:
|
||||
if (g_Lara.pistol_ammo.ammo && (g_Input & IN_ACTION)) {
|
||||
g_Lara.mesh_ptrs[LM_HEAD] =
|
||||
g_Meshes[g_Objects[O_LARA_UZIS].mesh_idx + LM_HEAD];
|
||||
} else {
|
||||
g_Lara.mesh_ptrs[LM_HEAD] =
|
||||
g_Meshes[g_Objects[O_LARA].mesh_idx + LM_HEAD];
|
||||
}
|
||||
|
||||
if (g_Camera.type != CAM_CINEMATIC && g_Camera.type != CAM_LOOK) {
|
||||
g_Camera.type = CAM_COMBAT;
|
||||
}
|
||||
|
||||
if (g_Input & IN_ACTION) {
|
||||
AMMO_INFO *const ammo = Gun_GetAmmoInfo(g_Lara.gun_type);
|
||||
assert(ammo != NULL);
|
||||
|
||||
if (ammo->ammo <= 0) {
|
||||
ammo->ammo = 0;
|
||||
Sound_Effect(SFX_CLICK, &g_LaraItem->pos, SPM_NORMAL);
|
||||
g_Lara.request_gun_type =
|
||||
Inv_RequestItem(O_PISTOL_ITEM) ? LGT_PISTOLS : LGT_UNARMED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (g_Lara.gun_type) {
|
||||
case LGT_PISTOLS:
|
||||
case LGT_MAGNUMS:
|
||||
case LGT_UZIS:
|
||||
Gun_Pistols_Control(g_Lara.gun_type);
|
||||
break;
|
||||
|
||||
case LGT_SHOTGUN:
|
||||
case LGT_M16:
|
||||
case LGT_GRENADE:
|
||||
case LGT_HARPOON:
|
||||
Gun_Rifle_Control(g_Lara.gun_type);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case LGS_SPECIAL:
|
||||
Flare_Draw();
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Gun_InitialiseNewWeapon(void)
|
||||
{
|
||||
g_Lara.left_arm.flash_gun = 0;
|
||||
g_Lara.left_arm.frame_num = 0;
|
||||
g_Lara.left_arm.lock = 0;
|
||||
g_Lara.left_arm.rot.x = 0;
|
||||
g_Lara.left_arm.rot.y = 0;
|
||||
g_Lara.left_arm.rot.z = 0;
|
||||
g_Lara.right_arm.flash_gun = 0;
|
||||
g_Lara.right_arm.frame_num = 0;
|
||||
g_Lara.right_arm.lock = 0;
|
||||
g_Lara.right_arm.rot.x = 0;
|
||||
g_Lara.right_arm.rot.y = 0;
|
||||
g_Lara.right_arm.rot.z = 0;
|
||||
g_Lara.target = NULL;
|
||||
|
||||
switch (g_Lara.gun_type) {
|
||||
case LGT_PISTOLS:
|
||||
case LGT_MAGNUMS:
|
||||
case LGT_UZIS:
|
||||
g_Lara.left_arm.frame_base = g_Objects[O_LARA_PISTOLS].frame_base;
|
||||
g_Lara.right_arm.frame_base = g_Objects[O_LARA_PISTOLS].frame_base;
|
||||
if (g_Lara.gun_status != LGS_ARMLESS) {
|
||||
Gun_Pistols_DrawMeshes(g_Lara.gun_type);
|
||||
}
|
||||
break;
|
||||
|
||||
case LGT_SHOTGUN:
|
||||
case LGT_M16:
|
||||
case LGT_GRENADE:
|
||||
case LGT_HARPOON:
|
||||
g_Lara.left_arm.frame_base =
|
||||
g_Objects[Gun_GetWeaponAnim(g_Lara.gun_type)].frame_base;
|
||||
g_Lara.right_arm.frame_base =
|
||||
g_Objects[Gun_GetWeaponAnim(g_Lara.gun_type)].frame_base;
|
||||
if (g_Lara.gun_status != LGS_ARMLESS) {
|
||||
Gun_Rifle_DrawMeshes(g_Lara.gun_type);
|
||||
}
|
||||
break;
|
||||
|
||||
case LGT_FLARE:
|
||||
g_Lara.left_arm.frame_base = g_Objects[O_LARA_FLARE].frame_base;
|
||||
g_Lara.right_arm.frame_base = g_Objects[O_LARA_FLARE].frame_base;
|
||||
if (g_Lara.gun_status != LGS_ARMLESS) {
|
||||
Flare_DrawMeshes();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_Lara.left_arm.frame_base = g_Anims[g_LaraItem->anim_num].frame_ptr;
|
||||
g_Lara.right_arm.frame_base = g_Anims[g_LaraItem->anim_num].frame_ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __cdecl Gun_GetWeaponAnim(const LARA_GUN_TYPE gun_type)
|
||||
{
|
||||
switch (gun_type) {
|
||||
case LGT_UNARMED:
|
||||
return O_LARA;
|
||||
case LGT_PISTOLS:
|
||||
return O_LARA_PISTOLS;
|
||||
case LGT_MAGNUMS:
|
||||
return O_LARA_MAGNUMS;
|
||||
case LGT_UZIS:
|
||||
return O_LARA_UZIS;
|
||||
case LGT_SHOTGUN:
|
||||
return O_LARA_SHOTGUN;
|
||||
case LGT_M16:
|
||||
return O_LARA_M16;
|
||||
case LGT_GRENADE:
|
||||
return O_LARA_GRENADE;
|
||||
case LGT_HARPOON:
|
||||
return O_LARA_HARPOON;
|
||||
default:
|
||||
return NO_OBJECT;
|
||||
}
|
||||
}
|
||||
|
||||
GAME_OBJECT_ID Gun_GetGunObject(const LARA_GUN_TYPE gun_type)
|
||||
{
|
||||
// clang-format off
|
||||
switch (gun_type) {
|
||||
case LGT_PISTOLS: return O_PISTOL_ITEM;
|
||||
case LGT_MAGNUMS: return O_MAGNUM_ITEM;
|
||||
case LGT_UZIS: return O_UZI_ITEM;
|
||||
case LGT_SHOTGUN: return O_SHOTGUN_ITEM;
|
||||
case LGT_HARPOON: return O_HARPOON_ITEM;
|
||||
case LGT_M16: return O_M16_ITEM;
|
||||
case LGT_GRENADE: return O_GRENADE_ITEM;
|
||||
default: return NO_OBJECT;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
GAME_OBJECT_ID Gun_GetAmmoObject(const LARA_GUN_TYPE gun_type)
|
||||
{
|
||||
// clang-format off
|
||||
switch (gun_type) {
|
||||
case LGT_PISTOLS: return O_PISTOL_AMMO_ITEM;
|
||||
case LGT_MAGNUMS: return O_MAGNUM_AMMO_ITEM;
|
||||
case LGT_UZIS: return O_UZI_AMMO_ITEM;
|
||||
case LGT_SHOTGUN: return O_SHOTGUN_AMMO_ITEM;
|
||||
case LGT_HARPOON: return O_HARPOON_AMMO_ITEM;
|
||||
case LGT_M16: return O_M16_AMMO_ITEM;
|
||||
case LGT_GRENADE: return O_GRENADE_AMMO_ITEM;
|
||||
default: return NO_OBJECT;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
int32_t Gun_GetAmmoQuantity(const LARA_GUN_TYPE gun_type)
|
||||
{
|
||||
// clang-format off
|
||||
switch (gun_type) {
|
||||
case LGT_PISTOLS: return 1;
|
||||
case LGT_MAGNUMS: return MAGNUM_AMMO_QTY;
|
||||
case LGT_UZIS: return UZI_AMMO_QTY;
|
||||
case LGT_SHOTGUN: return SHOTGUN_AMMO_QTY;
|
||||
case LGT_HARPOON: return HARPOON_AMMO_QTY;
|
||||
case LGT_M16: return M16_AMMO_QTY;
|
||||
case LGT_GRENADE: return GRENADE_AMMO_QTY;
|
||||
default: return -1;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
AMMO_INFO *Gun_GetAmmoInfo(const LARA_GUN_TYPE gun_type)
|
||||
{
|
||||
// clang-format off
|
||||
switch (gun_type) {
|
||||
case LGT_PISTOLS: return &g_Lara.pistol_ammo;
|
||||
case LGT_MAGNUMS: return &g_Lara.magnum_ammo;
|
||||
case LGT_UZIS: return &g_Lara.uzi_ammo;
|
||||
case LGT_SHOTGUN: return &g_Lara.shotgun_ammo;
|
||||
case LGT_HARPOON: return &g_Lara.harpoon_ammo;
|
||||
case LGT_M16: return &g_Lara.m16_ammo;
|
||||
case LGT_GRENADE: return &g_Lara.grenade_ammo;
|
||||
case LGT_SKIDOO: return &g_Lara.pistol_ammo;
|
||||
default: return NULL;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void Gun_SetLaraHandLMesh(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
const GAME_OBJECT_ID object_id = Gun_GetWeaponAnim(weapon_type);
|
||||
assert(object_id != NO_OBJECT);
|
||||
Lara_SwapSingleMesh(LM_HAND_L, object_id);
|
||||
}
|
||||
|
||||
void Gun_SetLaraHandRMesh(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
const GAME_OBJECT_ID object_id = Gun_GetWeaponAnim(weapon_type);
|
||||
assert(object_id != NO_OBJECT);
|
||||
Lara_SwapSingleMesh(LM_HAND_R, object_id);
|
||||
}
|
||||
|
||||
void Gun_SetLaraBackMesh(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
const GAME_OBJECT_ID object_id = Gun_GetWeaponAnim(weapon_type);
|
||||
assert(object_id != NO_OBJECT);
|
||||
Lara_SwapSingleMesh(LM_TORSO, object_id);
|
||||
g_Lara.back_gun = object_id;
|
||||
}
|
||||
|
||||
void Gun_SetLaraHolsterLMesh(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
const GAME_OBJECT_ID object_id = Gun_GetWeaponAnim(weapon_type);
|
||||
assert(object_id != NO_OBJECT);
|
||||
Lara_SwapSingleMesh(LM_THIGH_L, object_id);
|
||||
}
|
||||
|
||||
void Gun_SetLaraHolsterRMesh(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
const GAME_OBJECT_ID object_id = Gun_GetWeaponAnim(weapon_type);
|
||||
assert(object_id != NO_OBJECT);
|
||||
Lara_SwapSingleMesh(LM_THIGH_R, object_id);
|
||||
}
|
19
src/tr2/game/gun/gun.h
Normal file
19
src/tr2/game/gun/gun.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Gun_Control(void);
|
||||
void __cdecl Gun_InitialiseNewWeapon(void);
|
||||
int32_t __cdecl Gun_GetWeaponAnim(LARA_GUN_TYPE gun_type);
|
||||
|
||||
// TODO: make this a struct
|
||||
GAME_OBJECT_ID Gun_GetGunObject(LARA_GUN_TYPE gun_type);
|
||||
GAME_OBJECT_ID Gun_GetAmmoObject(LARA_GUN_TYPE gun_type);
|
||||
int32_t Gun_GetAmmoQuantity(LARA_GUN_TYPE gun_type);
|
||||
AMMO_INFO *Gun_GetAmmoInfo(LARA_GUN_TYPE gun_type);
|
||||
|
||||
void Gun_SetLaraBackMesh(LARA_GUN_TYPE weapon_type);
|
||||
void Gun_SetLaraHandLMesh(LARA_GUN_TYPE weapon_type);
|
||||
void Gun_SetLaraHandRMesh(LARA_GUN_TYPE weapon_type);
|
||||
void Gun_SetLaraHolsterLMesh(LARA_GUN_TYPE weapon_type);
|
||||
void Gun_SetLaraHolsterRMesh(LARA_GUN_TYPE weapon_type);
|
357
src/tr2/game/gun/gun_misc.c
Normal file
357
src/tr2/game/gun/gun_misc.c
Normal file
|
@ -0,0 +1,357 @@
|
|||
#include "game/gun/gun_misc.h"
|
||||
|
||||
#include "game/gun/gun.h"
|
||||
#include "game/items.h"
|
||||
#include "game/los.h"
|
||||
#include "game/math.h"
|
||||
#include "game/math_misc.h"
|
||||
#include "game/matrix.h"
|
||||
#include "game/random.h"
|
||||
#include "game/room.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#define NEAR_ANGLE (PHD_DEGREE * 15) // = 2730
|
||||
|
||||
static LARA_STATE m_HoldStates[] = {
|
||||
LS_WALK, LS_STOP, LS_POSE, LS_TURN_RIGHT, LS_TURN_LEFT,
|
||||
LS_BACK, LS_FAST_TURN, LS_STEP_LEFT, LS_STEP_RIGHT, LS_WADE,
|
||||
LS_PICKUP, LS_SWITCH_ON, LS_SWITCH_OFF, (LARA_STATE)-1,
|
||||
};
|
||||
|
||||
int32_t __cdecl Gun_CheckForHoldingState(const LARA_STATE state)
|
||||
{
|
||||
if (g_Lara.extra_anim) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const LARA_STATE *hold_state = m_HoldStates;
|
||||
while (*hold_state != (LARA_STATE)-1) {
|
||||
if (*hold_state == state) {
|
||||
return true;
|
||||
}
|
||||
hold_state++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void __cdecl Gun_TargetInfo(const WEAPON_INFO *const winfo)
|
||||
{
|
||||
if (!g_Lara.target) {
|
||||
g_Lara.left_arm.lock = 0;
|
||||
g_Lara.right_arm.lock = 0;
|
||||
g_Lara.target_angles[0] = 0;
|
||||
g_Lara.target_angles[1] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
GAME_VECTOR start;
|
||||
start.pos.x = g_LaraItem->pos.x;
|
||||
start.pos.y = g_LaraItem->pos.y - 650;
|
||||
start.pos.z = g_LaraItem->pos.z;
|
||||
start.room_num = g_LaraItem->room_num;
|
||||
|
||||
GAME_VECTOR target;
|
||||
Gun_FindTargetPoint(g_Lara.target, &target);
|
||||
|
||||
int16_t angles[2];
|
||||
// clang-format off
|
||||
Math_GetVectorAngles(
|
||||
target.pos.x - start.pos.x,
|
||||
target.pos.y - start.pos.y,
|
||||
target.pos.z - start.pos.z,
|
||||
angles);
|
||||
// clang-format on
|
||||
|
||||
angles[0] -= g_LaraItem->rot.y;
|
||||
angles[1] -= g_LaraItem->rot.x;
|
||||
|
||||
if (!LOS_Check(&start, &target)) {
|
||||
g_Lara.right_arm.lock = 0;
|
||||
g_Lara.left_arm.lock = 0;
|
||||
} else if (
|
||||
angles[0] >= winfo->lock_angles[0] && angles[0] <= winfo->lock_angles[1]
|
||||
&& angles[1] >= winfo->lock_angles[2]
|
||||
&& angles[1] <= winfo->lock_angles[3]) {
|
||||
g_Lara.right_arm.lock = 1;
|
||||
g_Lara.left_arm.lock = 1;
|
||||
} else {
|
||||
if (g_Lara.left_arm.lock
|
||||
&& (angles[0] < winfo->left_angles[0]
|
||||
|| angles[0] > winfo->left_angles[1]
|
||||
|| angles[1] < winfo->left_angles[2]
|
||||
|| angles[1] > winfo->left_angles[3])) {
|
||||
g_Lara.left_arm.lock = 0;
|
||||
}
|
||||
if (g_Lara.right_arm.lock
|
||||
&& (angles[0] < winfo->right_angles[0]
|
||||
|| angles[0] > winfo->right_angles[1]
|
||||
|| angles[1] < winfo->right_angles[2]
|
||||
|| angles[1] > winfo->right_angles[3])) {
|
||||
g_Lara.right_arm.lock = 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_Lara.target_angles[0] = angles[0];
|
||||
g_Lara.target_angles[1] = angles[1];
|
||||
}
|
||||
|
||||
void __cdecl Gun_GetNewTarget(const WEAPON_INFO *const winfo)
|
||||
{
|
||||
GAME_VECTOR start;
|
||||
start.pos.x = g_LaraItem->pos.x;
|
||||
start.pos.y = g_LaraItem->pos.y - 650;
|
||||
start.pos.z = g_LaraItem->pos.z;
|
||||
start.room_num = g_LaraItem->room_num;
|
||||
|
||||
int16_t best_y_rot = 0x7FFF;
|
||||
int32_t best_dist = 0x7FFFFFFF;
|
||||
ITEM *best_target = NULL;
|
||||
|
||||
const int16_t max_dist = winfo->target_dist;
|
||||
for (int32_t i = 0; i < NUM_SLOTS; i++) {
|
||||
const int16_t item_num = g_BaddieSlots[i].item_num;
|
||||
if (item_num == NO_ITEM || item_num == g_Lara.item_num) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
if (item->hit_points <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32_t dx = item->pos.x - start.pos.x;
|
||||
const int32_t dy = item->pos.y - start.pos.y;
|
||||
const int32_t dz = item->pos.z - start.pos.z;
|
||||
if (ABS(dx) > max_dist || ABS(dy) > max_dist || ABS(dz) > max_dist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32_t dist = SQUARE(dz) + SQUARE(dy) + SQUARE(dx);
|
||||
if (dist >= SQUARE(max_dist)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GAME_VECTOR target;
|
||||
Gun_FindTargetPoint(item, &target);
|
||||
if (!LOS_Check(&start, &target)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int16_t angles[2];
|
||||
Math_GetVectorAngles(
|
||||
target.pos.x - start.pos.x, target.pos.y - start.pos.y,
|
||||
target.pos.z - start.pos.z, angles);
|
||||
angles[0] -= g_Lara.torso_y_rot + g_LaraItem->rot.y;
|
||||
angles[1] -= g_Lara.torso_x_rot + g_LaraItem->rot.x;
|
||||
|
||||
if (angles[0] >= winfo->lock_angles[0]
|
||||
&& angles[0] <= winfo->lock_angles[1]
|
||||
&& angles[1] >= winfo->lock_angles[2]
|
||||
&& angles[1] <= winfo->lock_angles[3]) {
|
||||
const int16_t y_rot = ABS(angles[0]);
|
||||
if (y_rot < best_y_rot + NEAR_ANGLE && dist < best_dist) {
|
||||
best_dist = dist;
|
||||
best_y_rot = y_rot;
|
||||
best_target = item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_Lara.target = best_target;
|
||||
Gun_TargetInfo(winfo);
|
||||
}
|
||||
|
||||
void __cdecl Gun_AimWeapon(const WEAPON_INFO *const winfo, LARA_ARM *const arm)
|
||||
{
|
||||
const int16_t speed = winfo->aim_speed;
|
||||
|
||||
int16_t dest_y = 0;
|
||||
int16_t dest_x = 0;
|
||||
if (arm->lock) {
|
||||
dest_y = g_Lara.target_angles[0];
|
||||
dest_x = g_Lara.target_angles[1];
|
||||
}
|
||||
|
||||
if (arm->rot.y >= dest_y - speed && arm->rot.y <= dest_y + speed) {
|
||||
arm->rot.y = dest_y;
|
||||
} else if (arm->rot.y < dest_y) {
|
||||
arm->rot.y += speed;
|
||||
} else {
|
||||
arm->rot.y -= speed;
|
||||
}
|
||||
|
||||
if (arm->rot.x >= dest_x - speed && arm->rot.x <= dest_x + speed) {
|
||||
arm->rot.x = dest_x;
|
||||
} else if (arm->rot.x < dest_x) {
|
||||
arm->rot.x += speed;
|
||||
} else {
|
||||
arm->rot.x -= speed;
|
||||
}
|
||||
|
||||
arm->rot.z = 0;
|
||||
}
|
||||
|
||||
int32_t __cdecl Gun_FireWeapon(
|
||||
const LARA_GUN_TYPE weapon_type, ITEM *const target, const ITEM *const src,
|
||||
const PHD_ANGLE *const angles)
|
||||
{
|
||||
const WEAPON_INFO *const winfo = &g_Weapons[weapon_type];
|
||||
AMMO_INFO *const ammo = Gun_GetAmmoInfo(weapon_type);
|
||||
assert(ammo != NULL);
|
||||
|
||||
if (ammo == &g_Lara.pistol_ammo || g_SaveGame.bonus_flag) {
|
||||
ammo->ammo = 1000;
|
||||
}
|
||||
|
||||
if (ammo->ammo <= 0) {
|
||||
ammo->ammo = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ammo->ammo--;
|
||||
|
||||
const PHD_3DPOS view = {
|
||||
.pos = {
|
||||
.x = src->pos.x,
|
||||
.y = src->pos.y - winfo->gun_height,
|
||||
.z = src->pos.z,
|
||||
},
|
||||
.rot = {
|
||||
.x = angles[1] + winfo->shot_accuracy * (Random_GetControl() - PHD_90) / PHD_ONE,
|
||||
.y = angles[0] + winfo->shot_accuracy * (Random_GetControl() - PHD_90) / PHD_ONE,
|
||||
.z = 0,
|
||||
},
|
||||
};
|
||||
Matrix_GenerateW2V(&view);
|
||||
|
||||
SPHERE spheres[33];
|
||||
int32_t sphere_count = Collide_GetSpheres(target, spheres, false);
|
||||
int32_t best_sphere = -1;
|
||||
int32_t best_dist = 0x7FFFFFFF;
|
||||
|
||||
for (int32_t i = 0; i < sphere_count; i++) {
|
||||
const SPHERE *const sphere = &spheres[i];
|
||||
const int32_t r = sphere->r;
|
||||
if (ABS(sphere->x) < r && ABS(sphere->y) < r && sphere->z > r
|
||||
&& SQUARE(sphere->x) + SQUARE(sphere->y) <= SQUARE(r)) {
|
||||
const int32_t dist = sphere->z - r;
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
best_sphere = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_SaveGame.statistics.shots++;
|
||||
|
||||
GAME_VECTOR start;
|
||||
start.pos.x = view.pos.x;
|
||||
start.pos.y = view.pos.y;
|
||||
start.pos.z = view.pos.z;
|
||||
start.room_num = src->room_num;
|
||||
|
||||
if (best_sphere < 0) {
|
||||
const int32_t dist = winfo->target_dist;
|
||||
GAME_VECTOR hit_pos;
|
||||
hit_pos.pos.x = view.pos.x + ((dist * g_MatrixPtr->_20) >> W2V_SHIFT);
|
||||
hit_pos.pos.y = view.pos.y + ((dist * g_MatrixPtr->_21) >> W2V_SHIFT);
|
||||
hit_pos.pos.z = view.pos.z + ((dist * g_MatrixPtr->_22) >> W2V_SHIFT);
|
||||
hit_pos.room_num =
|
||||
Room_GetIndexFromPos(hit_pos.pos.x, hit_pos.pos.y, hit_pos.pos.z);
|
||||
const bool object_on_los = LOS_Check(&start, &hit_pos);
|
||||
const int16_t item_to_smash = LOS_CheckSmashable(&start, &hit_pos);
|
||||
if (item_to_smash == NO_ITEM) {
|
||||
if (!object_on_los) {
|
||||
Richochet(&hit_pos);
|
||||
}
|
||||
return -1;
|
||||
} else {
|
||||
Gun_SmashItem(item_to_smash, weapon_type);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
g_SaveGame.statistics.hits++;
|
||||
GAME_VECTOR hit_pos;
|
||||
hit_pos.pos.x =
|
||||
view.pos.x + ((best_dist * g_MatrixPtr->_20) >> W2V_SHIFT);
|
||||
hit_pos.pos.y =
|
||||
view.pos.y + ((best_dist * g_MatrixPtr->_21) >> W2V_SHIFT);
|
||||
hit_pos.pos.z =
|
||||
view.pos.z + ((best_dist * g_MatrixPtr->_22) >> W2V_SHIFT);
|
||||
hit_pos.room_num =
|
||||
Room_GetIndexFromPos(hit_pos.pos.x, hit_pos.pos.y, hit_pos.pos.z);
|
||||
const int16_t item_to_smash = LOS_CheckSmashable(&start, &hit_pos);
|
||||
if (item_to_smash != NO_ITEM) {
|
||||
Gun_SmashItem(item_to_smash, weapon_type);
|
||||
}
|
||||
Gun_HitTarget(target, &hit_pos, winfo->damage);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Gun_FindTargetPoint(
|
||||
const ITEM *const item, GAME_VECTOR *const target)
|
||||
{
|
||||
const BOUNDS_16 *const bounds = &Item_GetBestFrame(item)->bounds;
|
||||
const int32_t x = bounds->min_x + (bounds->max_x - bounds->min_x) / 2;
|
||||
const int32_t y = bounds->min_y + (bounds->max_y - bounds->min_y) / 3;
|
||||
const int32_t z = bounds->min_z + (bounds->max_z - bounds->min_z) / 2;
|
||||
const int32_t cy = Math_Cos(item->rot.y);
|
||||
const int32_t sy = Math_Sin(item->rot.y);
|
||||
target->pos.x = item->pos.x + ((cy * x + sy * z) >> W2V_SHIFT);
|
||||
target->pos.y = item->pos.y + y;
|
||||
target->pos.z = item->pos.z + ((cy * z - sy * x) >> W2V_SHIFT);
|
||||
target->room_num = item->room_num;
|
||||
}
|
||||
|
||||
void __cdecl Gun_HitTarget(
|
||||
ITEM *const item, const GAME_VECTOR *const hit_pos, const int32_t damage)
|
||||
{
|
||||
if (item->hit_points > 0 && item->hit_points <= damage) {
|
||||
g_SaveGame.statistics.kills++;
|
||||
}
|
||||
Item_TakeDamage(item, damage, true);
|
||||
|
||||
if (hit_pos != NULL) {
|
||||
DoBloodSplat(
|
||||
hit_pos->pos.x, hit_pos->pos.y, hit_pos->pos.z, item->speed,
|
||||
item->rot.y, item->room_num);
|
||||
}
|
||||
|
||||
if (!g_IsMonkAngry
|
||||
&& (item->object_id == O_MONK_1 || item->object_id == O_MONK_2)) {
|
||||
CREATURE *const creature = item->data;
|
||||
creature->flags += damage;
|
||||
if ((creature->flags & 0xFFF) > MONK_FRIENDLY_FIRE_THRESHOLD
|
||||
|| creature->mood == MOOD_BORED) {
|
||||
g_IsMonkAngry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Gun_SmashItem(
|
||||
const int16_t item_num, const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
|
||||
switch (item->object_id) {
|
||||
case O_WINDOW_1:
|
||||
SmashWindow(item_num);
|
||||
break;
|
||||
|
||||
case O_BELL:
|
||||
if (item->status != IS_ACTIVE) {
|
||||
item->status = IS_ACTIVE;
|
||||
Item_AddActive(item_num);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
42
src/tr2/game/gun/gun_misc.h
Normal file
42
src/tr2/game/gun/gun_misc.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
typedef enum {
|
||||
LF_G_AIM_START = 0,
|
||||
LF_G_AIM_BEND = 1,
|
||||
LF_G_AIM_EXTEND = 3,
|
||||
LF_G_AIM_END = 4,
|
||||
LF_G_UNDRAW_START = 5,
|
||||
LF_G_UNDRAW_BEND = 6,
|
||||
LF_G_UNDRAW_END = 12,
|
||||
LF_G_DRAW_START = 13,
|
||||
LF_G_DRAW_END = 23,
|
||||
LF_G_RECOIL_START = 24,
|
||||
LF_G_RECOIL_END = 32,
|
||||
} LARA_GUN_ANIMATION_FRAME;
|
||||
|
||||
typedef enum {
|
||||
LA_G_AIM = 0,
|
||||
LA_G_DRAW = 1,
|
||||
LA_G_RECOIL = 2,
|
||||
LA_G_UNDRAW = 3,
|
||||
LA_G_UNAIM = 4,
|
||||
LA_G_RELOAD = 5,
|
||||
LA_G_UAIM = 6,
|
||||
LA_G_UUNAIM = 7,
|
||||
LA_G_URECOIL = 8,
|
||||
LA_G_SURF_UNDRAW = 9,
|
||||
} LARA_GUN_ANIMATION;
|
||||
|
||||
int32_t __cdecl Gun_CheckForHoldingState(const LARA_STATE state);
|
||||
void __cdecl Gun_TargetInfo(const WEAPON_INFO *winfo);
|
||||
void __cdecl Gun_GetNewTarget(const WEAPON_INFO *winfo);
|
||||
void __cdecl Gun_AimWeapon(const WEAPON_INFO *winfo, LARA_ARM *arm);
|
||||
int32_t __cdecl Gun_FireWeapon(
|
||||
LARA_GUN_TYPE weapon_type, ITEM *target, const ITEM *src,
|
||||
const PHD_ANGLE *angles);
|
||||
void __cdecl Gun_FindTargetPoint(const ITEM *item, GAME_VECTOR *target);
|
||||
void __cdecl Gun_HitTarget(
|
||||
ITEM *item, const GAME_VECTOR *hit_pos, int32_t damage);
|
||||
void __cdecl Gun_SmashItem(int16_t item_num, LARA_GUN_TYPE weapon_type);
|
296
src/tr2/game/gun/gun_pistols.c
Normal file
296
src/tr2/game/gun/gun_pistols.c
Normal file
|
@ -0,0 +1,296 @@
|
|||
#include "game/gun/gun_pistols.h"
|
||||
|
||||
#include "game/gun/gun.h"
|
||||
#include "game/gun/gun_misc.h"
|
||||
#include "game/input.h"
|
||||
#include "game/math.h"
|
||||
#include "game/sound.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
static bool m_UziRight = false;
|
||||
static bool m_UziLeft = false;
|
||||
|
||||
void __cdecl Gun_Pistols_SetArmInfo(LARA_ARM *const arm, const int32_t frame)
|
||||
{
|
||||
const int16_t anim_idx = g_Objects[O_LARA_PISTOLS].anim_idx;
|
||||
|
||||
if (frame >= LF_G_AIM_START && frame <= LF_G_AIM_END) {
|
||||
arm->anim_num = anim_idx;
|
||||
} else if (frame >= LF_G_UNDRAW_START && frame <= LF_G_UNDRAW_END) {
|
||||
arm->anim_num = anim_idx + 1;
|
||||
} else if (frame >= LF_G_DRAW_START && frame <= LF_G_DRAW_END) {
|
||||
arm->anim_num = anim_idx + 2;
|
||||
} else if (frame >= LF_G_RECOIL_START && frame <= LF_G_RECOIL_END) {
|
||||
arm->anim_num = anim_idx + 3;
|
||||
}
|
||||
|
||||
arm->frame_num = frame;
|
||||
arm->frame_base = g_Anims[arm->anim_num].frame_ptr;
|
||||
}
|
||||
|
||||
void __cdecl Gun_Pistols_Draw(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
int16_t frame = g_Lara.left_arm.frame_num + 1;
|
||||
|
||||
if (!(frame >= LF_G_UNDRAW_START && frame <= LF_G_DRAW_END)) {
|
||||
frame = LF_G_UNDRAW_START;
|
||||
} else if (frame == LF_G_DRAW_START) {
|
||||
Gun_Pistols_DrawMeshes(weapon_type);
|
||||
Sound_Effect(SFX_LARA_DRAW, &g_LaraItem->pos, SPM_NORMAL);
|
||||
} else if (frame == LF_G_DRAW_END) {
|
||||
Gun_Pistols_Ready(weapon_type);
|
||||
frame = LF_G_AIM_START;
|
||||
}
|
||||
|
||||
Gun_Pistols_SetArmInfo(&g_Lara.right_arm, frame);
|
||||
Gun_Pistols_SetArmInfo(&g_Lara.left_arm, frame);
|
||||
}
|
||||
|
||||
void __cdecl Gun_Pistols_Undraw(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
int16_t frame_l = g_Lara.left_arm.frame_num;
|
||||
if (frame_l >= LF_G_RECOIL_START && frame_l <= LF_G_RECOIL_END) {
|
||||
frame_l = LF_G_AIM_END;
|
||||
} else if (frame_l >= LF_G_AIM_BEND && frame_l <= LF_G_AIM_END) {
|
||||
g_Lara.left_arm.rot.x -= g_Lara.left_arm.rot.x / frame_l;
|
||||
g_Lara.left_arm.rot.y -= g_Lara.left_arm.rot.y / frame_l;
|
||||
frame_l--;
|
||||
} else if (frame_l == LF_G_AIM_START) {
|
||||
g_Lara.left_arm.rot.x = 0;
|
||||
g_Lara.left_arm.rot.y = 0;
|
||||
g_Lara.left_arm.rot.z = 0;
|
||||
frame_l = LF_G_DRAW_END;
|
||||
} else if (frame_l == LF_G_DRAW_START) {
|
||||
Gun_Pistols_UndrawMeshLeft(weapon_type);
|
||||
Sound_Effect(SFX_LARA_HOLSTER, &g_LaraItem->pos, SPM_NORMAL);
|
||||
frame_l--;
|
||||
} else if (frame_l >= LF_G_UNDRAW_BEND && frame_l <= LF_G_DRAW_END) {
|
||||
frame_l--;
|
||||
}
|
||||
Gun_Pistols_SetArmInfo(&g_Lara.left_arm, frame_l);
|
||||
|
||||
int16_t frame_r = g_Lara.right_arm.frame_num;
|
||||
if (frame_r >= LF_G_RECOIL_START && frame_r <= LF_G_RECOIL_END) {
|
||||
frame_r = LF_G_AIM_END;
|
||||
} else if (frame_r >= LF_G_AIM_BEND && frame_r <= LF_G_AIM_END) {
|
||||
g_Lara.right_arm.rot.x -= g_Lara.right_arm.rot.x / frame_r;
|
||||
g_Lara.right_arm.rot.y -= g_Lara.right_arm.rot.y / frame_r;
|
||||
frame_r--;
|
||||
} else if (frame_r == LF_G_AIM_START) {
|
||||
g_Lara.right_arm.rot.x = 0;
|
||||
g_Lara.right_arm.rot.y = 0;
|
||||
g_Lara.right_arm.rot.z = 0;
|
||||
frame_r = LF_G_DRAW_END;
|
||||
} else if (frame_r == LF_G_DRAW_START) {
|
||||
Gun_Pistols_UndrawMeshRight(weapon_type);
|
||||
Sound_Effect(SFX_LARA_HOLSTER, &g_LaraItem->pos, SPM_NORMAL);
|
||||
frame_r--;
|
||||
} else if (frame_r >= LF_G_UNDRAW_BEND && frame_r <= LF_G_DRAW_END) {
|
||||
frame_r--;
|
||||
}
|
||||
Gun_Pistols_SetArmInfo(&g_Lara.right_arm, frame_r);
|
||||
|
||||
if (frame_l == LF_G_UNDRAW_START && frame_r == LF_G_UNDRAW_START) {
|
||||
g_Lara.gun_status = LGS_ARMLESS;
|
||||
g_Lara.target = NULL;
|
||||
g_Lara.left_arm.frame_num = LF_G_AIM_START;
|
||||
g_Lara.left_arm.lock = 0;
|
||||
g_Lara.right_arm.frame_num = LF_G_AIM_START;
|
||||
g_Lara.right_arm.lock = 0;
|
||||
}
|
||||
|
||||
if (!(g_Input & IN_LOOK)) {
|
||||
g_Lara.head_x_rot =
|
||||
(g_Lara.left_arm.rot.x + g_Lara.right_arm.rot.x) / 4;
|
||||
g_Lara.head_y_rot =
|
||||
(g_Lara.left_arm.rot.y + g_Lara.right_arm.rot.y) / 4;
|
||||
g_Lara.torso_x_rot = g_Lara.head_x_rot;
|
||||
g_Lara.torso_y_rot = g_Lara.head_y_rot;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Gun_Pistols_Ready(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
g_Lara.gun_status = LGS_READY;
|
||||
g_Lara.target = NULL;
|
||||
|
||||
g_Lara.left_arm.frame_base = g_Objects[O_LARA_PISTOLS].frame_base;
|
||||
g_Lara.left_arm.frame_num = LF_G_AIM_START;
|
||||
g_Lara.left_arm.lock = 0;
|
||||
g_Lara.left_arm.rot.x = 0;
|
||||
g_Lara.left_arm.rot.y = 0;
|
||||
g_Lara.left_arm.rot.z = 0;
|
||||
g_Lara.right_arm.frame_base = g_Objects[O_LARA_PISTOLS].frame_base;
|
||||
g_Lara.right_arm.frame_num = LF_G_AIM_START;
|
||||
g_Lara.right_arm.lock = 0;
|
||||
g_Lara.right_arm.rot.x = 0;
|
||||
g_Lara.right_arm.rot.y = 0;
|
||||
g_Lara.right_arm.rot.z = 0;
|
||||
}
|
||||
|
||||
void __cdecl Gun_Pistols_DrawMeshes(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
Gun_SetLaraHandLMesh(weapon_type);
|
||||
Gun_SetLaraHandRMesh(weapon_type);
|
||||
Gun_SetLaraHolsterLMesh(LGT_UNARMED);
|
||||
Gun_SetLaraHolsterRMesh(LGT_UNARMED);
|
||||
}
|
||||
|
||||
void Gun_Pistols_UndrawMeshLeft(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
Gun_SetLaraHandLMesh(LGT_UNARMED);
|
||||
Gun_SetLaraHolsterLMesh(weapon_type);
|
||||
}
|
||||
|
||||
void Gun_Pistols_UndrawMeshRight(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
Gun_SetLaraHandRMesh(LGT_UNARMED);
|
||||
Gun_SetLaraHolsterRMesh(weapon_type);
|
||||
}
|
||||
|
||||
void __cdecl Gun_Pistols_Control(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
const WEAPON_INFO *const winfo = &g_Weapons[weapon_type];
|
||||
|
||||
if (g_Input & IN_ACTION) {
|
||||
Gun_TargetInfo(winfo);
|
||||
} else {
|
||||
g_Lara.target = NULL;
|
||||
}
|
||||
|
||||
if (g_Lara.target == NULL) {
|
||||
Gun_GetNewTarget(winfo);
|
||||
}
|
||||
|
||||
Gun_AimWeapon(winfo, &g_Lara.left_arm);
|
||||
Gun_AimWeapon(winfo, &g_Lara.right_arm);
|
||||
|
||||
if (g_Lara.left_arm.lock && !g_Lara.right_arm.lock) {
|
||||
g_Lara.head_x_rot = g_Lara.left_arm.rot.x / 2;
|
||||
g_Lara.head_y_rot = g_Lara.left_arm.rot.y / 2;
|
||||
g_Lara.torso_x_rot = g_Lara.head_x_rot;
|
||||
g_Lara.torso_y_rot = g_Lara.head_y_rot;
|
||||
} else if (!g_Lara.left_arm.lock && g_Lara.right_arm.lock) {
|
||||
g_Lara.head_x_rot = g_Lara.right_arm.rot.x / 2;
|
||||
g_Lara.head_y_rot = g_Lara.right_arm.rot.y / 2;
|
||||
g_Lara.torso_x_rot = g_Lara.head_x_rot;
|
||||
g_Lara.torso_y_rot = g_Lara.head_y_rot;
|
||||
} else if (g_Lara.right_arm.lock) {
|
||||
g_Lara.head_x_rot =
|
||||
(g_Lara.right_arm.rot.x + g_Lara.left_arm.rot.x) / 4;
|
||||
g_Lara.head_y_rot =
|
||||
(g_Lara.right_arm.rot.y + g_Lara.left_arm.rot.y) / 4;
|
||||
g_Lara.torso_x_rot = g_Lara.head_x_rot;
|
||||
g_Lara.torso_y_rot = g_Lara.head_y_rot;
|
||||
}
|
||||
|
||||
Gun_Pistols_Animate(weapon_type);
|
||||
|
||||
if (g_Lara.left_arm.flash_gun || g_Lara.right_arm.flash_gun) {
|
||||
const int32_t c = Math_Cos(g_LaraItem->rot.y);
|
||||
const int32_t s = Math_Sin(g_LaraItem->rot.y);
|
||||
const int32_t x = g_LaraItem->pos.x + (s >> (W2V_SHIFT - 10));
|
||||
const int32_t y = g_LaraItem->pos.y - WALL_L / 2;
|
||||
const int32_t z = g_LaraItem->pos.z + (c >> (W2V_SHIFT - 10));
|
||||
AddDynamicLight(x, y, z, 12, 11);
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Gun_Pistols_Animate(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
const WEAPON_INFO *const winfo = &g_Weapons[weapon_type];
|
||||
|
||||
bool sound_already = false;
|
||||
int16_t angles[2];
|
||||
|
||||
int32_t frame_r = g_Lara.right_arm.frame_num;
|
||||
if (!g_Lara.right_arm.lock
|
||||
&& (!(g_Input & IN_ACTION) || g_Lara.target != NULL)) {
|
||||
if (frame_r >= LF_G_RECOIL_START && frame_r <= LF_G_RECOIL_END) {
|
||||
frame_r = LF_G_AIM_END;
|
||||
} else if (frame_r >= LF_G_AIM_BEND && frame_r <= LF_G_AIM_END) {
|
||||
frame_r--;
|
||||
}
|
||||
if (m_UziRight) {
|
||||
Sound_Effect(winfo->sample_num + 1, &g_LaraItem->pos, SPM_NORMAL);
|
||||
m_UziRight = false;
|
||||
}
|
||||
} else if (frame_r >= LF_G_AIM_START && frame_r <= LF_G_AIM_EXTEND) {
|
||||
frame_r++;
|
||||
} else if (frame_r == LF_G_AIM_END) {
|
||||
if (g_Input & IN_ACTION) {
|
||||
angles[0] = g_Lara.right_arm.rot.y + g_LaraItem->rot.y;
|
||||
angles[1] = g_Lara.right_arm.rot.x;
|
||||
if (Gun_FireWeapon(
|
||||
weapon_type, g_Lara.target, g_LaraItem, angles)) {
|
||||
g_Lara.right_arm.flash_gun = winfo->flash_time;
|
||||
Sound_Effect(winfo->sample_num, &g_LaraItem->pos, SPM_NORMAL);
|
||||
sound_already = true;
|
||||
if (weapon_type == LGT_UZIS) {
|
||||
m_UziRight = true;
|
||||
}
|
||||
}
|
||||
frame_r = LF_G_RECOIL_START;
|
||||
} else if (m_UziRight) {
|
||||
Sound_Effect(winfo->sample_num + 1, &g_LaraItem->pos, SPM_NORMAL);
|
||||
m_UziRight = false;
|
||||
}
|
||||
} else if (frame_r >= LF_G_RECOIL_START && frame_r <= LF_G_RECOIL_END) {
|
||||
frame_r++;
|
||||
if (frame_r == LF_G_RECOIL_START + winfo->recoil_frame) {
|
||||
frame_r = LF_G_AIM_END;
|
||||
}
|
||||
if (weapon_type == LGT_UZIS) {
|
||||
Sound_Effect(winfo->sample_num, &g_LaraItem->pos, SPM_NORMAL);
|
||||
m_UziRight = true;
|
||||
}
|
||||
}
|
||||
Gun_Pistols_SetArmInfo(&g_Lara.right_arm, frame_r);
|
||||
|
||||
int16_t frame_l = g_Lara.left_arm.frame_num;
|
||||
if (!g_Lara.left_arm.lock
|
||||
&& (!(g_Input & IN_ACTION) || g_Lara.target != NULL)) {
|
||||
if (frame_l >= LF_G_RECOIL_START && frame_l <= LF_G_RECOIL_END) {
|
||||
frame_l = LF_G_AIM_END;
|
||||
} else if (frame_l >= LF_G_AIM_BEND && frame_l <= LF_G_AIM_END) {
|
||||
frame_l--;
|
||||
}
|
||||
if (m_UziLeft) {
|
||||
Sound_Effect(winfo->sample_num + 1, &g_LaraItem->pos, SPM_NORMAL);
|
||||
m_UziLeft = false;
|
||||
}
|
||||
} else if (frame_l >= LF_G_AIM_START && frame_l <= LF_G_AIM_EXTEND) {
|
||||
frame_l++;
|
||||
} else if (frame_l == LF_G_AIM_END) {
|
||||
if (g_Input & IN_ACTION) {
|
||||
angles[0] = g_Lara.left_arm.rot.y + g_LaraItem->rot.y;
|
||||
angles[1] = g_Lara.left_arm.rot.x;
|
||||
if (Gun_FireWeapon(
|
||||
weapon_type, g_Lara.target, g_LaraItem, angles)) {
|
||||
g_Lara.left_arm.flash_gun = winfo->flash_time;
|
||||
if (!sound_already) {
|
||||
Sound_Effect(
|
||||
winfo->sample_num, &g_LaraItem->pos, SPM_NORMAL);
|
||||
}
|
||||
if (weapon_type == LGT_UZIS) {
|
||||
m_UziLeft = true;
|
||||
}
|
||||
}
|
||||
frame_l = LF_G_RECOIL_START;
|
||||
} else if (m_UziLeft) {
|
||||
Sound_Effect(winfo->sample_num + 1, &g_LaraItem->pos, SPM_NORMAL);
|
||||
m_UziLeft = false;
|
||||
}
|
||||
} else if (frame_l >= LF_G_RECOIL_START) {
|
||||
frame_l++;
|
||||
if (frame_l == LF_G_RECOIL_START + winfo->recoil_frame) {
|
||||
frame_l = LF_G_AIM_END;
|
||||
}
|
||||
if (weapon_type == LGT_UZIS) {
|
||||
Sound_Effect(winfo->sample_num, &g_LaraItem->pos, SPM_NORMAL);
|
||||
m_UziLeft = true;
|
||||
}
|
||||
}
|
||||
Gun_Pistols_SetArmInfo(&g_Lara.left_arm, frame_l);
|
||||
}
|
13
src/tr2/game/gun/gun_pistols.h
Normal file
13
src/tr2/game/gun/gun_pistols.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Gun_Pistols_SetArmInfo(LARA_ARM *arm, int32_t frame);
|
||||
void __cdecl Gun_Pistols_Draw(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Pistols_Undraw(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Pistols_Ready(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Pistols_DrawMeshes(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Pistols_UndrawMeshLeft(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Pistols_UndrawMeshRight(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Pistols_Control(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Pistols_Animate(LARA_GUN_TYPE weapon_type);
|
441
src/tr2/game/gun/gun_rifle.c
Normal file
441
src/tr2/game/gun/gun_rifle.c
Normal file
|
@ -0,0 +1,441 @@
|
|||
#include "game/gun/gun_rifle.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "game/gun/gun.h"
|
||||
#include "game/gun/gun_misc.h"
|
||||
#include "game/input.h"
|
||||
#include "game/items.h"
|
||||
#include "game/lara/misc.h"
|
||||
#include "game/math.h"
|
||||
#include "game/random.h"
|
||||
#include "game/sound.h"
|
||||
#include "global/const.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
static bool m_M16Firing = false;
|
||||
static bool m_HarpoonFired = false;
|
||||
|
||||
void __cdecl Gun_Rifle_DrawMeshes(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
Gun_SetLaraHandRMesh(weapon_type);
|
||||
g_Lara.back_gun = O_LARA;
|
||||
}
|
||||
|
||||
void __cdecl Gun_Rifle_UndrawMeshes(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
Gun_SetLaraHandRMesh(LGT_UNARMED);
|
||||
g_Lara.back_gun = Gun_GetWeaponAnim(weapon_type);
|
||||
}
|
||||
|
||||
void __cdecl Gun_Rifle_Ready(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
g_Lara.gun_status = LGS_READY;
|
||||
g_Lara.target = NULL;
|
||||
|
||||
g_Lara.left_arm.frame_base =
|
||||
g_Objects[Gun_GetWeaponAnim(weapon_type)].frame_base;
|
||||
g_Lara.left_arm.frame_num = LF_G_AIM_START;
|
||||
g_Lara.left_arm.lock = 0;
|
||||
g_Lara.left_arm.rot.x = 0;
|
||||
g_Lara.left_arm.rot.y = 0;
|
||||
g_Lara.left_arm.rot.z = 0;
|
||||
|
||||
g_Lara.right_arm.frame_base =
|
||||
g_Objects[Gun_GetWeaponAnim(weapon_type)].frame_base;
|
||||
g_Lara.right_arm.frame_num = LF_G_AIM_START;
|
||||
g_Lara.right_arm.lock = 0;
|
||||
g_Lara.right_arm.rot.x = 0;
|
||||
g_Lara.right_arm.rot.y = 0;
|
||||
g_Lara.right_arm.rot.z = 0;
|
||||
}
|
||||
|
||||
void __cdecl Gun_Rifle_Control(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
const WEAPON_INFO *const winfo = &g_Weapons[weapon_type];
|
||||
|
||||
if (g_Input & IN_ACTION) {
|
||||
Gun_TargetInfo(winfo);
|
||||
} else {
|
||||
g_Lara.target = NULL;
|
||||
}
|
||||
|
||||
if (g_Lara.target == NULL) {
|
||||
Gun_GetNewTarget(winfo);
|
||||
}
|
||||
|
||||
Gun_AimWeapon(winfo, &g_Lara.left_arm);
|
||||
|
||||
if (g_Lara.left_arm.lock) {
|
||||
g_Lara.head_x_rot = 0;
|
||||
g_Lara.head_y_rot = 0;
|
||||
g_Lara.torso_x_rot = g_Lara.left_arm.rot.x;
|
||||
g_Lara.torso_y_rot = g_Lara.left_arm.rot.y;
|
||||
}
|
||||
|
||||
Gun_Rifle_Animate(weapon_type);
|
||||
|
||||
if (g_Lara.right_arm.flash_gun
|
||||
&& (weapon_type == LGT_SHOTGUN || weapon_type == LGT_M16)) {
|
||||
const int32_t c = Math_Cos(g_LaraItem->rot.y);
|
||||
const int32_t s = Math_Sin(g_LaraItem->rot.y);
|
||||
const int32_t x = g_LaraItem->pos.x + (s >> (W2V_SHIFT - 10));
|
||||
const int32_t y = g_LaraItem->pos.y - WALL_L / 2;
|
||||
const int32_t z = g_LaraItem->pos.z + (c >> (W2V_SHIFT - 10));
|
||||
AddDynamicLight(x, y, z, 12, 11);
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Gun_Rifle_FireShotgun(void)
|
||||
{
|
||||
bool fired = false;
|
||||
|
||||
int16_t angles[2];
|
||||
angles[0] = g_Lara.left_arm.rot.y + g_LaraItem->rot.y;
|
||||
angles[1] = g_Lara.left_arm.rot.x;
|
||||
|
||||
for (int32_t i = 0; i < SHOTGUN_AMMO_CLIP; i++) {
|
||||
int16_t dangles[2];
|
||||
dangles[0] = angles[0]
|
||||
+ SHOTGUN_PELLET_SCATTER * (Random_GetControl() - 0x4000) / 0x10000;
|
||||
dangles[1] = angles[1]
|
||||
+ SHOTGUN_PELLET_SCATTER * (Random_GetControl() - 0x4000) / 0x10000;
|
||||
if (Gun_FireWeapon(LGT_SHOTGUN, g_Lara.target, g_LaraItem, dangles)) {
|
||||
fired = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fired) {
|
||||
g_Lara.right_arm.flash_gun = g_Weapons[LGT_SHOTGUN].flash_time;
|
||||
Sound_Effect(
|
||||
g_Weapons[LGT_SHOTGUN].sample_num, &g_LaraItem->pos, SPM_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Gun_Rifle_FireM16(const bool running)
|
||||
{
|
||||
int16_t angles[2];
|
||||
angles[0] = g_Lara.left_arm.rot.y + g_LaraItem->rot.y;
|
||||
angles[1] = g_Lara.left_arm.rot.x;
|
||||
|
||||
if (g_Config.gameplay.fix_m16_accuracy) {
|
||||
if (running) {
|
||||
g_Weapons[LGT_M16].shot_accuracy = PHD_DEGREE * 12;
|
||||
g_Weapons[LGT_M16].damage = 1;
|
||||
} else {
|
||||
g_Weapons[LGT_M16].shot_accuracy = PHD_DEGREE * 4;
|
||||
g_Weapons[LGT_M16].damage = 3;
|
||||
}
|
||||
}
|
||||
|
||||
if (Gun_FireWeapon(LGT_M16, g_Lara.target, g_LaraItem, angles)) {
|
||||
g_Lara.right_arm.flash_gun = g_Weapons[LGT_M16].flash_time;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Gun_Rifle_FireHarpoon(void)
|
||||
{
|
||||
if (g_Lara.harpoon_ammo.ammo <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int16_t item_num = Item_Create();
|
||||
if (item_num == NO_ITEM) {
|
||||
return;
|
||||
}
|
||||
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
item->object_id = O_HARPOON_BOLT;
|
||||
item->room_num = g_LaraItem->room_num;
|
||||
|
||||
XYZ_32 offset = {
|
||||
.x = -2,
|
||||
.y = 373,
|
||||
.z = 77,
|
||||
};
|
||||
|
||||
Lara_GetJointAbsPosition(&offset, LM_HAND_R);
|
||||
item->pos.x = offset.x;
|
||||
item->pos.y = offset.y;
|
||||
item->pos.z = offset.z;
|
||||
Item_Initialise(item_num);
|
||||
|
||||
if (g_Lara.target != NULL) {
|
||||
GAME_VECTOR lara_vec;
|
||||
Gun_FindTargetPoint(g_Lara.target, &lara_vec);
|
||||
const int32_t dx = lara_vec.pos.x - item->pos.x;
|
||||
const int32_t dz = lara_vec.pos.z - item->pos.z;
|
||||
const int32_t dy = lara_vec.pos.y - item->pos.y;
|
||||
const int32_t dxz = Math_Sqrt(SQUARE(dx) + SQUARE(dz));
|
||||
item->rot.y = Math_Atan(dz, dx);
|
||||
item->rot.x = -Math_Atan(dxz, dy);
|
||||
item->rot.z = 0;
|
||||
} else {
|
||||
item->rot.x = g_Lara.left_arm.rot.x + g_LaraItem->rot.x;
|
||||
item->rot.y = g_Lara.left_arm.rot.y + g_LaraItem->rot.y;
|
||||
item->rot.z = 0;
|
||||
}
|
||||
|
||||
item->fall_speed =
|
||||
(-HARPOON_BOLT_SPEED * Math_Sin(item->rot.x)) >> W2V_SHIFT;
|
||||
item->speed = (HARPOON_BOLT_SPEED * Math_Cos(item->rot.x)) >> W2V_SHIFT;
|
||||
Item_AddActive(item_num);
|
||||
|
||||
if (!g_SaveGame.bonus_flag) {
|
||||
g_Lara.harpoon_ammo.ammo--;
|
||||
}
|
||||
g_SaveGame.statistics.shots++;
|
||||
}
|
||||
|
||||
void __cdecl Gun_Rifle_FireGrenade(void)
|
||||
{
|
||||
if (g_Lara.grenade_ammo.ammo <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int16_t item_num = Item_Create();
|
||||
if (item_num == NO_ITEM) {
|
||||
return;
|
||||
}
|
||||
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
item->object_id = O_GRENADE;
|
||||
item->room_num = g_LaraItem->room_num;
|
||||
|
||||
XYZ_32 offset = {
|
||||
.x = -2,
|
||||
.y = 373,
|
||||
.z = 77,
|
||||
};
|
||||
Lara_GetJointAbsPosition(&offset, LM_HAND_R);
|
||||
item->pos.x = offset.x;
|
||||
item->pos.y = offset.y;
|
||||
item->pos.z = offset.z;
|
||||
Item_Initialise(item_num);
|
||||
|
||||
item->rot.x = g_Lara.left_arm.rot.x + g_LaraItem->rot.x;
|
||||
item->rot.y = g_Lara.left_arm.rot.y + g_LaraItem->rot.y;
|
||||
item->rot.z = 0;
|
||||
item->speed = GRENADE_SPEED;
|
||||
item->fall_speed = 0;
|
||||
Item_AddActive(item_num);
|
||||
|
||||
if (!g_SaveGame.bonus_flag) {
|
||||
g_Lara.grenade_ammo.ammo--;
|
||||
}
|
||||
g_SaveGame.statistics.shots++;
|
||||
}
|
||||
|
||||
void __cdecl Gun_Rifle_Draw(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
ITEM *item;
|
||||
if (g_Lara.weapon_item != NO_ITEM) {
|
||||
item = &g_Items[g_Lara.weapon_item];
|
||||
} else {
|
||||
g_Lara.weapon_item = Item_Create();
|
||||
item = &g_Items[g_Lara.weapon_item];
|
||||
item->object_id = Gun_GetWeaponAnim(weapon_type);
|
||||
if (weapon_type == LGT_GRENADE) {
|
||||
item->anim_num = g_Objects[O_LARA_GRENADE].anim_idx;
|
||||
} else {
|
||||
item->anim_num = g_Objects[item->object_id].anim_idx + 1;
|
||||
}
|
||||
item->frame_num = g_Anims[item->anim_num].frame_base;
|
||||
item->goal_anim_state = LA_G_DRAW;
|
||||
item->current_anim_state = LA_G_DRAW;
|
||||
item->status = IS_ACTIVE;
|
||||
item->room_num = NO_ROOM;
|
||||
g_Lara.right_arm.frame_base = g_Objects[item->object_id].frame_base;
|
||||
g_Lara.left_arm.frame_base = g_Objects[item->object_id].frame_base;
|
||||
}
|
||||
Item_Animate(item);
|
||||
|
||||
if (item->current_anim_state == LA_G_AIM
|
||||
|| item->current_anim_state == LA_G_UAIM) {
|
||||
Gun_Rifle_Ready(weapon_type);
|
||||
} else if (item->frame_num - g_Anims[item->anim_num].frame_base == 10) {
|
||||
Gun_Rifle_DrawMeshes(weapon_type);
|
||||
} else if (g_Lara.water_status == LWS_UNDERWATER) {
|
||||
item->goal_anim_state = LA_G_UAIM;
|
||||
}
|
||||
|
||||
g_Lara.left_arm.anim_num = item->anim_num;
|
||||
g_Lara.left_arm.frame_base = g_Anims[item->anim_num].frame_ptr;
|
||||
g_Lara.left_arm.frame_num =
|
||||
item->frame_num - g_Anims[item->anim_num].frame_base;
|
||||
g_Lara.right_arm.anim_num = item->anim_num;
|
||||
g_Lara.right_arm.frame_base = g_Anims[item->anim_num].frame_ptr;
|
||||
g_Lara.right_arm.frame_num =
|
||||
item->frame_num - g_Anims[item->anim_num].frame_base;
|
||||
}
|
||||
|
||||
void __cdecl Gun_Rifle_Undraw(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
ITEM *const item = &g_Items[g_Lara.weapon_item];
|
||||
if (g_Lara.water_status == LWS_SURFACE) {
|
||||
item->goal_anim_state = LA_G_SURF_UNDRAW;
|
||||
} else {
|
||||
item->goal_anim_state = LA_G_UNDRAW;
|
||||
}
|
||||
Item_Animate(item);
|
||||
|
||||
if (item->status == IS_DEACTIVATED) {
|
||||
Item_Kill(g_Lara.weapon_item);
|
||||
g_Lara.weapon_item = NO_ITEM;
|
||||
g_Lara.gun_status = LGS_ARMLESS;
|
||||
g_Lara.target = NULL;
|
||||
g_Lara.left_arm.frame_num = 0;
|
||||
g_Lara.left_arm.lock = 0;
|
||||
g_Lara.right_arm.frame_num = 0;
|
||||
g_Lara.right_arm.lock = 0;
|
||||
} else if (
|
||||
item->current_anim_state == LA_G_UNDRAW
|
||||
&& item->frame_num - g_Anims[item->anim_num].frame_base == 21) {
|
||||
Gun_Rifle_UndrawMeshes(weapon_type);
|
||||
}
|
||||
|
||||
g_Lara.left_arm.anim_num = item->anim_num;
|
||||
g_Lara.left_arm.frame_base = g_Anims[item->anim_num].frame_ptr;
|
||||
g_Lara.left_arm.frame_num =
|
||||
item->frame_num - g_Anims[item->anim_num].frame_base;
|
||||
g_Lara.right_arm.anim_num = item->anim_num;
|
||||
g_Lara.right_arm.frame_base = g_Anims[item->anim_num].frame_ptr;
|
||||
g_Lara.right_arm.frame_num =
|
||||
item->frame_num - g_Anims[item->anim_num].frame_base;
|
||||
}
|
||||
|
||||
void __cdecl Gun_Rifle_Animate(const LARA_GUN_TYPE weapon_type)
|
||||
{
|
||||
const bool running = weapon_type == LGT_M16 && g_LaraItem->speed != 0;
|
||||
ITEM *const item = &g_Items[g_Lara.weapon_item];
|
||||
|
||||
switch (item->current_anim_state) {
|
||||
case LA_G_AIM:
|
||||
m_M16Firing = false;
|
||||
if (m_HarpoonFired) {
|
||||
item->goal_anim_state = LA_G_RELOAD;
|
||||
m_HarpoonFired = false;
|
||||
} else if (g_Lara.water_status == LWS_UNDERWATER || running) {
|
||||
item->goal_anim_state = LA_G_UAIM;
|
||||
} else if (
|
||||
((g_Input & IN_ACTION) && g_Lara.target == NULL)
|
||||
|| g_Lara.left_arm.lock) {
|
||||
item->goal_anim_state = LA_G_RECOIL;
|
||||
} else {
|
||||
item->goal_anim_state = LA_G_UNAIM;
|
||||
}
|
||||
break;
|
||||
|
||||
case LA_G_UAIM:
|
||||
m_M16Firing = false;
|
||||
if (m_HarpoonFired) {
|
||||
item->goal_anim_state = LA_G_RELOAD;
|
||||
m_HarpoonFired = false;
|
||||
} else if (g_Lara.water_status != LWS_UNDERWATER && !running) {
|
||||
item->goal_anim_state = LA_G_AIM;
|
||||
} else if (
|
||||
((g_Input & IN_ACTION) && g_Lara.target == NULL)
|
||||
|| g_Lara.left_arm.lock) {
|
||||
item->goal_anim_state = LA_G_URECOIL;
|
||||
} else {
|
||||
item->goal_anim_state = LA_G_UUNAIM;
|
||||
}
|
||||
break;
|
||||
|
||||
case LA_G_RECOIL:
|
||||
if (item->frame_num - g_Anims[item->anim_num].frame_base == 0) {
|
||||
item->goal_anim_state = LA_G_UNAIM;
|
||||
if (g_Lara.water_status != LWS_UNDERWATER && !running
|
||||
&& !m_HarpoonFired) {
|
||||
if (g_Input & IN_ACTION) {
|
||||
if (g_Lara.target == NULL || g_Lara.left_arm.lock) {
|
||||
switch (weapon_type) {
|
||||
case LGT_HARPOON:
|
||||
Gun_Rifle_FireHarpoon();
|
||||
if ((g_Lara.harpoon_ammo.ammo % HARPOON_RECOIL)
|
||||
== 0) {
|
||||
m_HarpoonFired = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case LGT_GRENADE:
|
||||
Gun_Rifle_FireGrenade();
|
||||
break;
|
||||
|
||||
case LGT_M16:
|
||||
Gun_Rifle_FireM16(false);
|
||||
Sound_Effect(
|
||||
SFX_M16_FIRE, &g_LaraItem->pos, SPM_NORMAL);
|
||||
m_M16Firing = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
Gun_Rifle_FireShotgun();
|
||||
break;
|
||||
}
|
||||
|
||||
item->goal_anim_state = LA_G_RECOIL;
|
||||
}
|
||||
} else if (g_Lara.left_arm.lock) {
|
||||
item->goal_anim_state = LA_G_AIM;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->goal_anim_state != LA_G_RECOIL && m_M16Firing) {
|
||||
Sound_Effect(SFX_M16_STOP, &g_LaraItem->pos, SPM_NORMAL);
|
||||
m_M16Firing = false;
|
||||
}
|
||||
} else if (m_M16Firing) {
|
||||
Sound_Effect(SFX_M16_FIRE, &g_LaraItem->pos, SPM_NORMAL);
|
||||
} else if (
|
||||
weapon_type == LGT_SHOTGUN && !(g_Input & IN_ACTION)
|
||||
&& !g_Lara.left_arm.lock) {
|
||||
item->goal_anim_state = LA_G_UNAIM;
|
||||
}
|
||||
break;
|
||||
|
||||
case LA_G_URECOIL:
|
||||
if (item->frame_num - g_Anims[item->anim_num].frame_base == 0) {
|
||||
item->goal_anim_state = LA_G_UUNAIM;
|
||||
if ((g_Lara.water_status == LWS_UNDERWATER || running)
|
||||
&& !m_HarpoonFired) {
|
||||
if (g_Input & IN_ACTION) {
|
||||
if (g_Lara.target == NULL || g_Lara.left_arm.lock) {
|
||||
if (weapon_type == LGT_HARPOON) {
|
||||
Gun_Rifle_FireHarpoon();
|
||||
if ((g_Lara.harpoon_ammo.ammo % HARPOON_RECOIL)
|
||||
== 0) {
|
||||
m_HarpoonFired = true;
|
||||
}
|
||||
} else {
|
||||
Gun_Rifle_FireM16(true);
|
||||
}
|
||||
item->goal_anim_state = LA_G_URECOIL;
|
||||
}
|
||||
} else if (g_Lara.left_arm.lock) {
|
||||
item->goal_anim_state = LA_G_UAIM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (weapon_type == LGT_M16 && item->goal_anim_state == LA_G_URECOIL) {
|
||||
Sound_Effect(SFX_M16_FIRE, &g_LaraItem->pos, SPM_NORMAL);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Item_Animate(item);
|
||||
g_Lara.left_arm.anim_num = item->anim_num;
|
||||
g_Lara.left_arm.frame_base = g_Anims[item->anim_num].frame_ptr;
|
||||
g_Lara.left_arm.frame_num =
|
||||
item->frame_num - g_Anims[item->anim_num].frame_base;
|
||||
g_Lara.right_arm.anim_num = item->anim_num;
|
||||
g_Lara.right_arm.frame_base = g_Anims[item->anim_num].frame_ptr;
|
||||
g_Lara.right_arm.frame_num =
|
||||
item->frame_num - g_Anims[item->anim_num].frame_base;
|
||||
}
|
15
src/tr2/game/gun/gun_rifle.h
Normal file
15
src/tr2/game/gun/gun_rifle.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Gun_Rifle_DrawMeshes(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Rifle_UndrawMeshes(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Rifle_Ready(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Rifle_Control(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Rifle_FireShotgun(void);
|
||||
void __cdecl Gun_Rifle_FireM16(bool running);
|
||||
void __cdecl Gun_Rifle_FireHarpoon(void);
|
||||
void __cdecl Gun_Rifle_FireGrenade(void);
|
||||
void __cdecl Gun_Rifle_Draw(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Rifle_Undraw(LARA_GUN_TYPE weapon_type);
|
||||
void __cdecl Gun_Rifle_Animate(LARA_GUN_TYPE weapon_type);
|
284
src/tr2/game/hwr.c
Normal file
284
src/tr2/game/hwr.c
Normal file
|
@ -0,0 +1,284 @@
|
|||
#include "game/hwr.h"
|
||||
|
||||
#include "decomp/decomp.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <d3d.h>
|
||||
|
||||
HRESULT HWR_DrawPrimitive(
|
||||
D3DPRIMITIVETYPE primitive_type, LPVOID vertices, DWORD vtx_count,
|
||||
bool is_no_clip)
|
||||
{
|
||||
return g_D3DDev->lpVtbl->DrawPrimitive(
|
||||
g_D3DDev, primitive_type, D3DVT_TLVERTEX, vertices, vtx_count,
|
||||
is_no_clip ? D3DDP_DONOTUPDATEEXTENTS | D3DDP_DONOTCLIP : 0);
|
||||
}
|
||||
|
||||
void __cdecl HWR_InitState(void)
|
||||
{
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_FILLMODE, D3DFILL_SOLID);
|
||||
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_SHADEMODE, D3DSHADE_GOURAUD);
|
||||
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_CULLMODE, D3DCULL_NONE);
|
||||
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_TEXTUREPERSPECTIVE,
|
||||
g_SavedAppSettings.perspective_correct ? TRUE : FALSE);
|
||||
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_DITHERENABLE,
|
||||
g_SavedAppSettings.dither ? TRUE : FALSE);
|
||||
|
||||
g_AlphaBlendEnabler = g_CurrentDisplayAdapter.shade_restricted
|
||||
? D3DRENDERSTATE_STIPPLEDALPHA
|
||||
: D3DRENDERSTATE_ALPHABLENDENABLE;
|
||||
|
||||
const DWORD texture_filter = g_SavedAppSettings.bilinear_filtering
|
||||
? D3DFILTER_LINEAR
|
||||
: D3DFILTER_NEAREST;
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_TEXTUREMAG, texture_filter);
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_TEXTUREMIN, texture_filter);
|
||||
|
||||
const DWORD blend_mode =
|
||||
(g_CurrentDisplayAdapter.hw_device_desc.dpcTriCaps.dwTextureBlendCaps
|
||||
& D3DPTBLENDCAPS_MODULATEALPHA)
|
||||
? D3DTBLEND_MODULATEALPHA
|
||||
: D3DTBLEND_MODULATE;
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_TEXTUREMAPBLEND, blend_mode);
|
||||
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_TEXTUREADDRESS, D3DTADDRESS_CLAMP);
|
||||
|
||||
HWR_ResetTexSource();
|
||||
HWR_ResetColorKey();
|
||||
HWR_ResetZBuffer();
|
||||
}
|
||||
|
||||
void __cdecl HWR_ResetTexSource(void)
|
||||
{
|
||||
g_CurrentTexSource = 0;
|
||||
g_D3DDev->lpVtbl->SetRenderState(g_D3DDev, D3DRENDERSTATE_TEXTUREHANDLE, 0);
|
||||
g_D3DDev->lpVtbl->SetRenderState(g_D3DDev, D3DRENDERSTATE_FLUSHBATCH, 0);
|
||||
}
|
||||
|
||||
void __cdecl HWR_ResetColorKey(void)
|
||||
{
|
||||
g_ColorKeyState = FALSE;
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev,
|
||||
g_TexturesAlphaChannel ? D3DRENDERSTATE_ALPHABLENDENABLE
|
||||
: D3DRENDERSTATE_COLORKEYENABLE,
|
||||
FALSE);
|
||||
}
|
||||
|
||||
void __cdecl HWR_ResetZBuffer(void)
|
||||
{
|
||||
g_ZEnableState = FALSE;
|
||||
g_ZWriteEnableState = FALSE;
|
||||
if (g_ZBufferSurface != NULL) {
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_ZFUNC, D3DCMP_ALWAYS);
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_ZENABLE,
|
||||
g_SavedAppSettings.zbuffer ? TRUE : FALSE);
|
||||
} else {
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_ZFUNC, D3DCMP_LESSEQUAL);
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_ZENABLE, FALSE);
|
||||
}
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_ZWRITEENABLE, FALSE);
|
||||
}
|
||||
|
||||
void __cdecl HWR_TexSource(const HWR_TEXTURE_HANDLE tex_source)
|
||||
{
|
||||
if (g_CurrentTexSource != tex_source) {
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_TEXTUREHANDLE, tex_source);
|
||||
g_CurrentTexSource = tex_source;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl HWR_EnableColorKey(const bool state)
|
||||
{
|
||||
if (g_ColorKeyState != state) {
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev,
|
||||
g_TexturesAlphaChannel ? D3DRENDERSTATE_ALPHABLENDENABLE
|
||||
: D3DRENDERSTATE_COLORKEYENABLE,
|
||||
state ? TRUE : FALSE);
|
||||
g_ColorKeyState = state;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl HWR_EnableZBuffer(const bool z_write_enable, const bool z_enable)
|
||||
{
|
||||
if (!g_SavedAppSettings.zbuffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_ZWriteEnableState != z_write_enable) {
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_ZWRITEENABLE,
|
||||
z_write_enable ? TRUE : FALSE);
|
||||
g_ZWriteEnableState = z_write_enable;
|
||||
}
|
||||
|
||||
if (g_ZEnableState != z_enable) {
|
||||
if (g_ZBufferSurface != NULL) {
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_ZFUNC,
|
||||
z_enable ? D3DCMP_LESSEQUAL : D3DCMP_ALWAYS);
|
||||
} else {
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, D3DRENDERSTATE_ZENABLE, z_enable ? TRUE : FALSE);
|
||||
}
|
||||
g_ZEnableState = z_enable;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl HWR_BeginScene(void)
|
||||
{
|
||||
HWR_GetPageHandles();
|
||||
WaitPrimaryBufferFlip();
|
||||
g_D3DDev->lpVtbl->BeginScene(g_D3DDev);
|
||||
}
|
||||
|
||||
void __cdecl HWR_DrawPolyList(void)
|
||||
{
|
||||
HWR_EnableZBuffer(false, true);
|
||||
|
||||
for (int32_t i = 0; i < g_SurfaceCount; i++) {
|
||||
uint16_t *buf_ptr = (uint16_t *)g_SortBuffer[i]._0;
|
||||
|
||||
uint16_t poly_type = *buf_ptr++;
|
||||
uint16_t tex_page =
|
||||
(poly_type == POLY_HWR_GTMAP || poly_type == POLY_HWR_WGTMAP)
|
||||
? *buf_ptr++
|
||||
: 0;
|
||||
uint16_t vtx_count = *buf_ptr++;
|
||||
D3DTLVERTEX *vtx_ptr = *(D3DTLVERTEX **)buf_ptr;
|
||||
|
||||
switch (poly_type) {
|
||||
// triangle fan (texture)
|
||||
case POLY_HWR_GTMAP:
|
||||
// triangle fan (texture + colorkey)
|
||||
case POLY_HWR_WGTMAP:
|
||||
HWR_TexSource(g_HWR_PageHandles[tex_page]);
|
||||
HWR_EnableColorKey(poly_type == POLY_HWR_WGTMAP);
|
||||
HWR_DrawPrimitive(D3DPT_TRIANGLEFAN, vtx_ptr, vtx_count, true);
|
||||
break;
|
||||
|
||||
// triangle fan (color)
|
||||
case POLY_HWR_GOURAUD:
|
||||
HWR_TexSource(0);
|
||||
HWR_EnableColorKey(false);
|
||||
HWR_DrawPrimitive(D3DPT_TRIANGLEFAN, vtx_ptr, vtx_count, true);
|
||||
break;
|
||||
|
||||
// line strip (color)
|
||||
case POLY_HWR_LINE:
|
||||
HWR_TexSource(0);
|
||||
HWR_EnableColorKey(false);
|
||||
HWR_DrawPrimitive(D3DPT_LINESTRIP, vtx_ptr, vtx_count, true);
|
||||
break;
|
||||
|
||||
// triangle fan (color + semitransparent)
|
||||
case POLY_HWR_TRANS: {
|
||||
DWORD alpha_state;
|
||||
HWR_TexSource(0);
|
||||
g_D3DDev->lpVtbl->GetRenderState(
|
||||
g_D3DDev, g_AlphaBlendEnabler, &alpha_state);
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, g_AlphaBlendEnabler, TRUE);
|
||||
HWR_DrawPrimitive(D3DPT_TRIANGLEFAN, vtx_ptr, vtx_count, true);
|
||||
g_D3DDev->lpVtbl->SetRenderState(
|
||||
g_D3DDev, g_AlphaBlendEnabler, alpha_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl HWR_LoadTexturePages(
|
||||
const int32_t pages_count, const void *const pages_buffer,
|
||||
const RGB_888 *const palette)
|
||||
{
|
||||
int32_t page_idx = -1;
|
||||
const BYTE *buffer_ptr = (const BYTE *)pages_buffer;
|
||||
|
||||
HWR_FreeTexturePages();
|
||||
|
||||
if (palette != NULL) {
|
||||
g_PaletteIndex = CreateTexturePalette(palette);
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < pages_count; i++) {
|
||||
if (palette != NULL) {
|
||||
page_idx = AddTexturePage8(256, 256, buffer_ptr, g_PaletteIndex);
|
||||
buffer_ptr += 256 * 256 * 1;
|
||||
} else {
|
||||
page_idx = AddTexturePage16(256, 256, buffer_ptr);
|
||||
buffer_ptr += 256 * 256 * 2;
|
||||
}
|
||||
g_HWR_TexturePageIndexes[i] = page_idx < 0 ? -1 : page_idx;
|
||||
}
|
||||
|
||||
HWR_GetPageHandles();
|
||||
}
|
||||
|
||||
void __cdecl HWR_FreeTexturePages(void)
|
||||
{
|
||||
for (int32_t i = 0; i < MAX_TEXTURE_PAGES; i++) {
|
||||
if (g_HWR_TexturePageIndexes[i] >= 0) {
|
||||
SafeFreeTexturePage(g_HWR_TexturePageIndexes[i]);
|
||||
g_HWR_TexturePageIndexes[i] = -1;
|
||||
}
|
||||
g_HWR_PageHandles[i] = 0;
|
||||
}
|
||||
if (g_PaletteIndex >= 0) {
|
||||
SafeFreePalette(g_PaletteIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl HWR_GetPageHandles(void)
|
||||
{
|
||||
for (int32_t i = 0; i < MAX_TEXTURE_PAGES; i++) {
|
||||
if (g_HWR_TexturePageIndexes[i] < 0)
|
||||
g_HWR_PageHandles[i] = 0;
|
||||
else
|
||||
g_HWR_PageHandles[i] =
|
||||
GetTexturePageHandle(g_HWR_TexturePageIndexes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool __cdecl HWR_VertexBufferFull(void)
|
||||
{
|
||||
const int32_t index =
|
||||
(g_HWR_VertexPtr - g_HWR_VertexBuffer) / sizeof(D3DTLVERTEX);
|
||||
return index >= MAX_VERTICES;
|
||||
}
|
||||
|
||||
bool __cdecl HWR_Init(void)
|
||||
{
|
||||
memset(g_HWR_VertexBuffer, 0, sizeof(g_HWR_VertexBuffer));
|
||||
memset(
|
||||
g_HWR_TexturePageIndexes, (uint8_t)-1,
|
||||
sizeof(g_HWR_TexturePageIndexes));
|
||||
return true;
|
||||
}
|
27
src/tr2/game/hwr.h
Normal file
27
src/tr2/game/hwr.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
#include <d3dtypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <windows.h>
|
||||
|
||||
HRESULT HWR_DrawPrimitive(
|
||||
D3DPRIMITIVETYPE primitive_type, LPVOID vertices, DWORD vtx_count,
|
||||
bool is_no_clip);
|
||||
|
||||
void __cdecl HWR_InitState(void);
|
||||
void __cdecl HWR_ResetTexSource(void);
|
||||
void __cdecl HWR_ResetColorKey(void);
|
||||
void __cdecl HWR_ResetZBuffer(void);
|
||||
void __cdecl HWR_TexSource(HWR_TEXTURE_HANDLE tex_source);
|
||||
void __cdecl HWR_EnableColorKey(bool state);
|
||||
void __cdecl HWR_EnableZBuffer(bool z_write_enable, bool z_enable);
|
||||
void __cdecl HWR_BeginScene(void);
|
||||
void __cdecl HWR_DrawPolyList(void);
|
||||
void __cdecl HWR_LoadTexturePages(
|
||||
int32_t pages_count, const void *pages_buffer, const RGB_888 *palette);
|
||||
void __cdecl HWR_FreeTexturePages(void);
|
||||
void __cdecl HWR_GetPageHandles(void);
|
||||
bool __cdecl HWR_VertexBufferFull(void);
|
||||
bool __cdecl HWR_Init(void);
|
153
src/tr2/game/input.c
Normal file
153
src/tr2/game/input.c
Normal file
|
@ -0,0 +1,153 @@
|
|||
#include "game/input.h"
|
||||
|
||||
#include "game/console/common.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
#include "specific/s_input.h"
|
||||
|
||||
static const char *m_KeyNames[] = {
|
||||
NULL, "ESC", "1", "2", "3", "4", "5", "6",
|
||||
"7", "8", "9", "0", "-", "+", "BKSP", "TAB",
|
||||
"Q", "W", "E", "R", "T", "Y", "U", "I",
|
||||
"O", "P", "<", ">", "RET", "CTRL", "A", "S",
|
||||
"D", "F", "G", "H", "J", "K", "L", ";",
|
||||
"'", "`", "SHIFT", "#", "Z", "X", "C", "V",
|
||||
"B", "N", "M", ",", ".", "/", "SHIFT", "PADx",
|
||||
"ALT", "SPACE", "CAPS", NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, "NMLK", NULL, "PAD7",
|
||||
"PAD8", "PAD9", "PAD-", "PAD4", "PAD5", "PAD6", "PAD+", "PAD1",
|
||||
"PAD2", "PAD3", "PAD0", "PAD.", NULL, NULL, "\\", NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, "ENTER", "CTRL", NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, "SHIFT", NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, "PAD/", NULL, NULL,
|
||||
"ALT", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, "HOME",
|
||||
"UP", "PGUP", NULL, "LEFT", NULL, "RIGHT", NULL, "END",
|
||||
"DOWN", "PGDN", "INS", "DEL", NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
"JOY1", "JOY2", "JOY3", "JOY4", "JOY5", "JOY6", "JOY7", "JOY8",
|
||||
"JOY9", "JOY10", "JOY11", "JOY12", "JOY13", "JOY14", "JOY15", "JOY16",
|
||||
};
|
||||
|
||||
bool g_ConflictLayout[INPUT_ROLE_NUMBER_OF] = { false };
|
||||
bool m_ListenMode = false;
|
||||
|
||||
bool Input_Update(void)
|
||||
{
|
||||
bool result = S_Input_Update();
|
||||
|
||||
g_InputDB = Input_GetDebounced(g_Input);
|
||||
|
||||
if (m_ListenMode) {
|
||||
g_Input = 0;
|
||||
g_InputDB = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (g_InputDB & IN_CONSOLE) {
|
||||
Console_Open();
|
||||
g_Input = 0;
|
||||
g_InputDB = 0;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t __cdecl Input_GetDebounced(const int32_t input)
|
||||
{
|
||||
const int32_t result = input & ~g_OldInputDB;
|
||||
g_OldInputDB = input;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t Input_GetAssignedKey(const int32_t layout, const INPUT_ROLE role)
|
||||
{
|
||||
return g_Layout[layout].key[role];
|
||||
}
|
||||
|
||||
void Input_AssignKey(
|
||||
const int32_t layout, const INPUT_ROLE role, const uint16_t key)
|
||||
{
|
||||
g_Layout[layout].key[role] = key;
|
||||
}
|
||||
|
||||
const char *Input_GetLayoutName(const int32_t layout)
|
||||
{
|
||||
// clang-format off
|
||||
switch (layout) {
|
||||
case 0: return g_GF_PCStrings[GF_S_PC_DEFAULT_KEYS];
|
||||
case 1: return g_GF_PCStrings[GF_S_PC_USER_KEYS];
|
||||
default: return "";
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
const char *Input_GetRoleName(const INPUT_ROLE role)
|
||||
{
|
||||
// clang-format off
|
||||
switch (role) {
|
||||
case INPUT_ROLE_UP: return g_GF_GameStrings[GF_S_GAME_KEYMAP_RUN];
|
||||
case INPUT_ROLE_DOWN: return g_GF_GameStrings[GF_S_GAME_KEYMAP_BACK];
|
||||
case INPUT_ROLE_LEFT: return g_GF_GameStrings[GF_S_GAME_KEYMAP_LEFT];
|
||||
case INPUT_ROLE_RIGHT: return g_GF_GameStrings[GF_S_GAME_KEYMAP_RIGHT];
|
||||
case INPUT_ROLE_STEP_LEFT: return g_GF_GameStrings[GF_S_GAME_KEYMAP_STEP_LEFT];
|
||||
case INPUT_ROLE_STEP_RIGHT: return g_GF_GameStrings[GF_S_GAME_KEYMAP_STEP_RIGHT];
|
||||
case INPUT_ROLE_SLOW: return g_GF_GameStrings[GF_S_GAME_KEYMAP_WALK];
|
||||
case INPUT_ROLE_JUMP: return g_GF_GameStrings[GF_S_GAME_KEYMAP_JUMP];
|
||||
case INPUT_ROLE_ACTION: return g_GF_GameStrings[GF_S_GAME_KEYMAP_ACTION];
|
||||
case INPUT_ROLE_DRAW_WEAPON: return g_GF_GameStrings[GF_S_GAME_KEYMAP_DRAW_WEAPON];
|
||||
case INPUT_ROLE_FLARE: return g_GF_GameStrings[GF_S_GAME_KEYMAP_FLARE];
|
||||
case INPUT_ROLE_LOOK: return g_GF_GameStrings[GF_S_GAME_KEYMAP_LOOK];
|
||||
case INPUT_ROLE_ROLL: return g_GF_GameStrings[GF_S_GAME_KEYMAP_ROLL];
|
||||
case INPUT_ROLE_OPTION: return g_GF_GameStrings[GF_S_GAME_KEYMAP_INVENTORY];
|
||||
case INPUT_ROLE_CONSOLE: return "Console";
|
||||
default: return "";
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
const char *Input_GetKeyName(const uint16_t key)
|
||||
{
|
||||
return m_KeyNames[key];
|
||||
}
|
||||
|
||||
void __cdecl Input_CheckConflictsWithDefaults(void)
|
||||
{
|
||||
for (int32_t i = 0; i < INPUT_ROLE_NUMBER_OF; i++) {
|
||||
g_ConflictLayout[i] = false;
|
||||
for (int32_t j = 0; j < INPUT_ROLE_NUMBER_OF; j++) {
|
||||
const uint16_t key1 = Input_GetAssignedKey(0, i);
|
||||
const uint16_t key2 = Input_GetAssignedKey(1, j);
|
||||
if (key1 == key2) {
|
||||
g_ConflictLayout[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Input_EnterListenMode(void)
|
||||
{
|
||||
m_ListenMode = true;
|
||||
}
|
||||
|
||||
void Input_ExitListenMode(void)
|
||||
{
|
||||
m_ListenMode = false;
|
||||
S_Input_Update();
|
||||
g_OldInputDB = g_Input;
|
||||
g_InputDB = Input_GetDebounced(g_Input);
|
||||
}
|
80
src/tr2/game/input.h
Normal file
80
src/tr2/game/input.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
#define INPUT_MAX_LAYOUT 2
|
||||
|
||||
typedef enum {
|
||||
// clang-format off
|
||||
INPUT_ROLE_UP = 0,
|
||||
INPUT_ROLE_FORWARD = INPUT_ROLE_UP,
|
||||
INPUT_ROLE_DOWN = 1,
|
||||
INPUT_ROLE_BACK = INPUT_ROLE_DOWN,
|
||||
INPUT_ROLE_LEFT = 2,
|
||||
INPUT_ROLE_RIGHT = 3,
|
||||
INPUT_ROLE_STEP_LEFT = 4,
|
||||
INPUT_ROLE_STEP_RIGHT = 5,
|
||||
INPUT_ROLE_SLOW = 6,
|
||||
INPUT_ROLE_JUMP = 7,
|
||||
INPUT_ROLE_ACTION = 8,
|
||||
INPUT_ROLE_DRAW_WEAPON = 9,
|
||||
INPUT_ROLE_FLARE = 10,
|
||||
INPUT_ROLE_LOOK = 11,
|
||||
INPUT_ROLE_ROLL = 12,
|
||||
INPUT_ROLE_OPTION = 13,
|
||||
INPUT_ROLE_CONSOLE = 14,
|
||||
INPUT_ROLE_NUMBER_OF = 15,
|
||||
// clang-format on
|
||||
} INPUT_ROLE;
|
||||
|
||||
typedef enum {
|
||||
// clang-format off
|
||||
IN_FORWARD = 1 << 0,
|
||||
IN_BACK = 1 << 1,
|
||||
IN_LEFT = 1 << 2,
|
||||
IN_RIGHT = 1 << 3,
|
||||
IN_JUMP = 1 << 4,
|
||||
IN_DRAW = 1 << 5,
|
||||
IN_ACTION = 1 << 6,
|
||||
IN_SLOW = 1 << 7,
|
||||
IN_OPTION = 1 << 8,
|
||||
IN_LOOK = 1 << 9,
|
||||
IN_STEP_LEFT = 1 << 10,
|
||||
IN_STEP_RIGHT = 1 << 11,
|
||||
IN_ROLL = 1 << 12,
|
||||
IN_PAUSE = 1 << 13,
|
||||
IN_RESERVED1 = 1 << 14,
|
||||
IN_RESERVED2 = 1 << 15,
|
||||
IN_DOZY_CHEAT = 1 << 16,
|
||||
IN_STUFF_CHEAT = 1 << 17,
|
||||
IN_DEBUG_INFO = 1 << 18,
|
||||
IN_FLARE = 1 << 19,
|
||||
IN_SELECT = 1 << 20,
|
||||
IN_DESELECT = 1 << 21,
|
||||
IN_SAVE = 1 << 22,
|
||||
IN_LOAD = 1 << 23,
|
||||
IN_CONSOLE = 1 << 24,
|
||||
// clang-format on
|
||||
} INPUT_STATE;
|
||||
|
||||
typedef struct {
|
||||
uint16_t key[INPUT_ROLE_NUMBER_OF];
|
||||
} INPUT_LAYOUT;
|
||||
|
||||
extern INPUT_LAYOUT g_Layout[2];
|
||||
extern bool g_ConflictLayout[INPUT_ROLE_NUMBER_OF];
|
||||
|
||||
bool Input_Update(void);
|
||||
int32_t __cdecl Input_GetDebounced(int32_t input);
|
||||
|
||||
void Input_EnterListenMode(void);
|
||||
void Input_ExitListenMode(void);
|
||||
bool Input_IsAnythingPressed(void);
|
||||
void Input_AssignKey(int32_t layout, INPUT_ROLE role, uint16_t key);
|
||||
uint16_t Input_GetAssignedKey(int32_t layout, INPUT_ROLE role);
|
||||
|
||||
const char *Input_GetLayoutName(int32_t layout);
|
||||
const char *Input_GetRoleName(INPUT_ROLE role);
|
||||
const char *Input_GetKeyName(uint16_t key);
|
||||
|
||||
void __cdecl Input_CheckConflictsWithDefaults(void);
|
365
src/tr2/game/inventory/backpack.c
Normal file
365
src/tr2/game/inventory/backpack.c
Normal file
|
@ -0,0 +1,365 @@
|
|||
#include "game/inventory/backpack.h"
|
||||
|
||||
#include "game/inventory/common.h"
|
||||
#include "game/items.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
void __cdecl Inv_InsertItem(INVENTORY_ITEM *const inv_item)
|
||||
{
|
||||
int32_t n;
|
||||
|
||||
if (inv_item->inv_pos < 100) {
|
||||
for (n = 0; n < g_Inv_MainObjectsCount; n++) {
|
||||
if (g_Inv_MainList[n]->inv_pos > inv_item->inv_pos) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = g_Inv_MainObjectsCount; i > n - 1; i--) {
|
||||
g_Inv_MainList[i + 1] = g_Inv_MainList[i];
|
||||
g_Inv_MainQtys[i + 1] = g_Inv_MainQtys[i];
|
||||
}
|
||||
g_Inv_MainList[n] = inv_item;
|
||||
g_Inv_MainQtys[n] = 1;
|
||||
g_Inv_MainObjectsCount++;
|
||||
} else {
|
||||
for (n = 0; n < g_Inv_KeyObjectsCount; n++) {
|
||||
if (g_Inv_KeysList[n]->inv_pos > inv_item->inv_pos) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = g_Inv_KeyObjectsCount; i > n - 1; i--) {
|
||||
g_Inv_KeysList[i + 1] = g_Inv_KeysList[i];
|
||||
g_Inv_KeysQtys[i + 1] = g_Inv_KeysQtys[i];
|
||||
}
|
||||
g_Inv_KeysList[n] = inv_item;
|
||||
g_Inv_KeysQtys[n] = 1;
|
||||
g_Inv_KeyObjectsCount++;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __cdecl Inv_AddItem(const GAME_OBJECT_ID object_id)
|
||||
{
|
||||
const GAME_OBJECT_ID inv_object_id = Inv_GetItemOption(object_id);
|
||||
|
||||
for (int32_t i = 0; i < g_Inv_MainObjectsCount; i++) {
|
||||
const INVENTORY_ITEM *const inv_item = g_Inv_MainList[i];
|
||||
if (inv_item->object_id == inv_object_id) {
|
||||
const int32_t qty = object_id == O_FLARES_ITEM ? FLARE_AMMO_QTY : 1;
|
||||
g_Inv_MainQtys[i] += qty;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < g_Inv_KeyObjectsCount; i++) {
|
||||
const INVENTORY_ITEM *const inv_item = g_Inv_KeysList[i];
|
||||
if (inv_item->object_id == inv_object_id) {
|
||||
g_Inv_KeysQtys[i]++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_Objects[inv_object_id == NO_OBJECT ? object_id : inv_object_id]
|
||||
.loaded) {
|
||||
return false;
|
||||
}
|
||||
switch (object_id) {
|
||||
case O_COMPASS_OPTION:
|
||||
case O_COMPASS_ITEM:
|
||||
Inv_InsertItem(&g_Inv_Item_Stopwatch);
|
||||
return true;
|
||||
|
||||
case O_PISTOL_ITEM:
|
||||
case O_PISTOL_OPTION:
|
||||
Inv_InsertItem(&g_Inv_Item_Pistols);
|
||||
if (g_Lara.last_gun_type) {
|
||||
return true;
|
||||
}
|
||||
g_Lara.last_gun_type = 1;
|
||||
return true;
|
||||
|
||||
case O_SHOTGUN_ITEM:
|
||||
case O_SHOTGUN_OPTION:
|
||||
for (int32_t i = Inv_RequestItem(O_SHOTGUN_AMMO_ITEM); i > 0; i--) {
|
||||
Inv_RemoveItem(O_SHOTGUN_AMMO_ITEM);
|
||||
g_Lara.shotgun_ammo.ammo += SHOTGUN_AMMO_QTY;
|
||||
}
|
||||
g_Lara.shotgun_ammo.ammo += SHOTGUN_AMMO_QTY;
|
||||
Inv_InsertItem(&g_Inv_Item_Shotgun);
|
||||
if (g_Lara.last_gun_type == LGT_UNARMED) {
|
||||
g_Lara.last_gun_type = LGT_SHOTGUN;
|
||||
}
|
||||
if (!g_Lara.back_gun) {
|
||||
g_Lara.back_gun = O_LARA_SHOTGUN;
|
||||
}
|
||||
Item_GlobalReplace(O_SHOTGUN_ITEM, O_SHOTGUN_AMMO_ITEM);
|
||||
return false;
|
||||
|
||||
case O_MAGNUM_ITEM:
|
||||
case O_MAGNUM_OPTION:
|
||||
for (int32_t i = Inv_RequestItem(O_MAGNUM_AMMO_ITEM); i > 0; i--) {
|
||||
Inv_RemoveItem(O_MAGNUM_AMMO_ITEM);
|
||||
g_Lara.magnum_ammo.ammo += MAGNUM_AMMO_QTY;
|
||||
}
|
||||
g_Lara.magnum_ammo.ammo += MAGNUM_AMMO_QTY;
|
||||
Inv_InsertItem(&g_Inv_Item_Magnums);
|
||||
Item_GlobalReplace(O_MAGNUM_ITEM, O_MAGNUM_AMMO_ITEM);
|
||||
return false;
|
||||
|
||||
case O_UZI_ITEM:
|
||||
case O_UZI_OPTION:
|
||||
for (int32_t i = Inv_RequestItem(O_UZI_AMMO_ITEM); i > 0; i--) {
|
||||
Inv_RemoveItem(O_UZI_AMMO_ITEM);
|
||||
g_Lara.uzi_ammo.ammo += UZI_AMMO_QTY;
|
||||
}
|
||||
g_Lara.uzi_ammo.ammo += UZI_AMMO_QTY;
|
||||
Inv_InsertItem(&g_Inv_Item_Uzis);
|
||||
Item_GlobalReplace(O_UZI_ITEM, O_UZI_AMMO_ITEM);
|
||||
return false;
|
||||
|
||||
case O_HARPOON_ITEM:
|
||||
case O_HARPOON_OPTION:
|
||||
for (int32_t i = Inv_RequestItem(O_HARPOON_AMMO_ITEM); i > 0; i--) {
|
||||
Inv_RemoveItem(O_HARPOON_AMMO_ITEM);
|
||||
g_Lara.harpoon_ammo.ammo += HARPOON_AMMO_QTY;
|
||||
}
|
||||
g_Lara.harpoon_ammo.ammo += HARPOON_AMMO_QTY;
|
||||
Inv_InsertItem(&g_Inv_Item_Harpoon);
|
||||
Item_GlobalReplace(O_HARPOON_ITEM, O_HARPOON_AMMO_ITEM);
|
||||
return false;
|
||||
|
||||
case O_M16_ITEM:
|
||||
case O_M16_OPTION:
|
||||
for (int32_t i = Inv_RequestItem(O_M16_AMMO_ITEM); i > 0; i--) {
|
||||
Inv_RemoveItem(O_M16_AMMO_ITEM);
|
||||
g_Lara.m16_ammo.ammo += M16_AMMO_QTY;
|
||||
}
|
||||
g_Lara.m16_ammo.ammo += M16_AMMO_QTY;
|
||||
Inv_InsertItem(&g_Inv_Item_M16);
|
||||
Item_GlobalReplace(O_M16_ITEM, O_M16_AMMO_ITEM);
|
||||
return false;
|
||||
|
||||
case O_GRENADE_ITEM:
|
||||
case O_GRENADE_OPTION:
|
||||
for (int32_t i = Inv_RequestItem(O_GRENADE_AMMO_ITEM); i > 0; i--) {
|
||||
Inv_RemoveItem(O_GRENADE_AMMO_ITEM);
|
||||
g_Lara.grenade_ammo.ammo += GRENADE_AMMO_QTY;
|
||||
}
|
||||
g_Lara.grenade_ammo.ammo += GRENADE_AMMO_QTY;
|
||||
Inv_InsertItem(&g_Inv_Item_Grenade);
|
||||
Item_GlobalReplace(O_GRENADE_ITEM, O_GRENADE_AMMO_ITEM);
|
||||
return false;
|
||||
|
||||
case O_SHOTGUN_AMMO_ITEM:
|
||||
case O_SHOTGUN_AMMO_OPTION:
|
||||
if (Inv_RequestItem(O_SHOTGUN_ITEM)) {
|
||||
g_Lara.shotgun_ammo.ammo += 12;
|
||||
} else {
|
||||
Inv_InsertItem(&g_Inv_Item_ShotgunAmmo);
|
||||
}
|
||||
return false;
|
||||
|
||||
case O_MAGNUM_AMMO_ITEM:
|
||||
case O_MAGNUM_AMMO_OPTION:
|
||||
if (Inv_RequestItem(O_MAGNUM_ITEM)) {
|
||||
g_Lara.magnum_ammo.ammo += 40;
|
||||
} else {
|
||||
Inv_InsertItem(&g_Inv_Item_MagnumAmmo);
|
||||
}
|
||||
return false;
|
||||
|
||||
case O_UZI_AMMO_ITEM:
|
||||
case O_UZI_AMMO_OPTION:
|
||||
if (Inv_RequestItem(O_UZI_ITEM)) {
|
||||
g_Lara.uzi_ammo.ammo += 80;
|
||||
} else {
|
||||
Inv_InsertItem(&g_Inv_Item_UziAmmo);
|
||||
}
|
||||
return false;
|
||||
|
||||
case O_HARPOON_AMMO_ITEM:
|
||||
case O_HARPOON_AMMO_OPTION:
|
||||
if (Inv_RequestItem(O_HARPOON_ITEM)) {
|
||||
g_Lara.harpoon_ammo.ammo += 3;
|
||||
} else {
|
||||
Inv_InsertItem(&g_Inv_Item_HarpoonAmmo);
|
||||
}
|
||||
return false;
|
||||
|
||||
case O_M16_AMMO_ITEM:
|
||||
case O_M16_AMMO_OPTION:
|
||||
if (Inv_RequestItem(O_M16_ITEM)) {
|
||||
g_Lara.m16_ammo.ammo += 40;
|
||||
} else {
|
||||
Inv_InsertItem(&g_Inv_Item_M16Ammo);
|
||||
}
|
||||
return false;
|
||||
|
||||
case O_GRENADE_AMMO_ITEM:
|
||||
case O_GRENADE_AMMO_OPTION:
|
||||
if (Inv_RequestItem(O_GRENADE_ITEM)) {
|
||||
g_Lara.grenade_ammo.ammo += 2;
|
||||
} else {
|
||||
Inv_InsertItem(&g_Inv_Item_GrenadeAmmo);
|
||||
}
|
||||
return false;
|
||||
|
||||
case O_SMALL_MEDIPACK_ITEM:
|
||||
case O_SMALL_MEDIPACK_OPTION:
|
||||
Inv_InsertItem(&g_Inv_Item_SmallMedi);
|
||||
return true;
|
||||
|
||||
case O_LARGE_MEDIPACK_ITEM:
|
||||
case O_LARGE_MEDIPACK_OPTION:
|
||||
Inv_InsertItem(&g_Inv_Item_LargeMedi);
|
||||
return true;
|
||||
|
||||
case O_FLARES_ITEM:
|
||||
case O_FLARES_OPTION:
|
||||
for (int32_t i = 0; i < FLARE_AMMO_QTY; i++) {
|
||||
Inv_AddItem(O_FLARE_ITEM);
|
||||
}
|
||||
return true;
|
||||
|
||||
case O_FLARE_ITEM:
|
||||
Inv_InsertItem(&g_Inv_Item_Flare);
|
||||
return true;
|
||||
|
||||
case O_PUZZLE_ITEM_1:
|
||||
case O_PUZZLE_OPTION_1:
|
||||
Inv_InsertItem(&g_Inv_Item_Puzzle1);
|
||||
return true;
|
||||
|
||||
case O_PUZZLE_ITEM_2:
|
||||
case O_PUZZLE_OPTION_2:
|
||||
Inv_InsertItem(&g_Inv_Item_Puzzle2);
|
||||
return true;
|
||||
|
||||
case O_PUZZLE_ITEM_3:
|
||||
case O_PUZZLE_OPTION_3:
|
||||
Inv_InsertItem(&g_Inv_Item_Puzzle3);
|
||||
return true;
|
||||
|
||||
case O_PUZZLE_ITEM_4:
|
||||
case O_PUZZLE_OPTION_4:
|
||||
Inv_InsertItem(&g_Inv_Item_Puzzle4);
|
||||
return true;
|
||||
|
||||
case O_SECRET_1:
|
||||
g_SaveGame.statistics.secrets |= 1;
|
||||
return true;
|
||||
|
||||
case O_SECRET_2:
|
||||
g_SaveGame.statistics.secrets |= 2;
|
||||
return true;
|
||||
|
||||
case O_SECRET_3:
|
||||
g_SaveGame.statistics.secrets |= 4;
|
||||
return true;
|
||||
|
||||
case O_KEY_ITEM_1:
|
||||
case O_KEY_OPTION_1:
|
||||
Inv_InsertItem(&g_Inv_Item_Key1);
|
||||
return true;
|
||||
|
||||
case O_KEY_ITEM_2:
|
||||
case O_KEY_OPTION_2:
|
||||
Inv_InsertItem(&g_Inv_Item_Key2);
|
||||
return true;
|
||||
|
||||
case O_KEY_ITEM_3:
|
||||
case O_KEY_OPTION_3:
|
||||
Inv_InsertItem(&g_Inv_Item_Key3);
|
||||
return true;
|
||||
|
||||
case O_KEY_ITEM_4:
|
||||
case O_KEY_OPTION_4:
|
||||
Inv_InsertItem(&g_Inv_Item_Key4);
|
||||
return true;
|
||||
|
||||
case O_PICKUP_ITEM_1:
|
||||
case O_PICKUP_OPTION_1:
|
||||
Inv_InsertItem(&g_Inv_Item_Pickup1);
|
||||
return true;
|
||||
|
||||
case O_PICKUP_ITEM_2:
|
||||
case O_PICKUP_OPTION_2:
|
||||
Inv_InsertItem(&g_Inv_Item_Pickup2);
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Inv_AddItemNTimes(GAME_OBJECT_ID object_id, int32_t qty)
|
||||
{
|
||||
for (int32_t i = 0; i < qty; i++) {
|
||||
Inv_AddItem(object_id);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __cdecl Inv_RequestItem(const GAME_OBJECT_ID object_id)
|
||||
{
|
||||
const GAME_OBJECT_ID inv_object_id = Inv_GetItemOption(object_id);
|
||||
|
||||
for (int32_t i = 0; i < g_Inv_MainObjectsCount; i++) {
|
||||
if (g_Inv_MainList[i]->object_id == inv_object_id) {
|
||||
return g_Inv_MainQtys[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < g_Inv_KeyObjectsCount; i++) {
|
||||
if (g_Inv_KeysList[i]->object_id == inv_object_id) {
|
||||
return g_Inv_KeysQtys[i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __cdecl Inv_RemoveAllItems(void)
|
||||
{
|
||||
g_Inv_MainObjectsCount = 0;
|
||||
g_Inv_MainCurrent = 0;
|
||||
g_Inv_KeyObjectsCount = 0;
|
||||
g_Inv_KeysCurrent = 0;
|
||||
}
|
||||
|
||||
int32_t Inv_RemoveItem(const GAME_OBJECT_ID object_id)
|
||||
{
|
||||
const GAME_OBJECT_ID inv_object_id = Inv_GetItemOption(object_id);
|
||||
|
||||
for (int32_t i = 0; i < g_Inv_MainObjectsCount; i++) {
|
||||
if (g_Inv_MainList[i]->object_id == inv_object_id) {
|
||||
g_Inv_MainQtys[i]--;
|
||||
if (g_Inv_MainQtys[i] > 0) {
|
||||
return true;
|
||||
}
|
||||
g_Inv_MainObjectsCount--;
|
||||
for (int32_t j = i; j < g_Inv_MainObjectsCount; j++) {
|
||||
g_Inv_MainList[j] = g_Inv_MainList[j + 1];
|
||||
g_Inv_MainQtys[j] = g_Inv_MainQtys[j + 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < g_Inv_KeyObjectsCount; i++) {
|
||||
if (g_Inv_KeysList[i]->object_id == inv_object_id) {
|
||||
g_Inv_KeysQtys[i]--;
|
||||
if (g_Inv_KeysQtys[i] > 0) {
|
||||
return true;
|
||||
}
|
||||
g_Inv_KeyObjectsCount--;
|
||||
for (int32_t j = i; j < g_Inv_KeyObjectsCount; j++) {
|
||||
g_Inv_KeysList[j] = g_Inv_KeysList[j + 1];
|
||||
g_Inv_KeysQtys[j] = g_Inv_KeysQtys[j + 1];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
10
src/tr2/game/inventory/backpack.h
Normal file
10
src/tr2/game/inventory/backpack.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Inv_InsertItem(INVENTORY_ITEM *inv_item);
|
||||
int32_t __cdecl Inv_AddItem(GAME_OBJECT_ID object_id);
|
||||
void Inv_AddItemNTimes(GAME_OBJECT_ID object_id, int32_t qty);
|
||||
int32_t __cdecl Inv_RequestItem(GAME_OBJECT_ID object_id);
|
||||
void __cdecl Inv_RemoveAllItems(void);
|
||||
int32_t __cdecl Inv_RemoveItem(GAME_OBJECT_ID object_id);
|
1209
src/tr2/game/inventory/common.c
Normal file
1209
src/tr2/game/inventory/common.c
Normal file
File diff suppressed because it is too large
Load diff
18
src/tr2/game/inventory/common.h
Normal file
18
src/tr2/game/inventory/common.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Inv_InitColors(void);
|
||||
void __cdecl Inv_Construct(void);
|
||||
int32_t __cdecl Inv_Display(int32_t inventory_mode);
|
||||
void __cdecl Inv_SelectMeshes(INVENTORY_ITEM *inv_item);
|
||||
int32_t __cdecl Inv_AnimateInventoryItem(INVENTORY_ITEM *inv_item);
|
||||
void __cdecl Inv_DrawInventoryItem(INVENTORY_ITEM *inv_item);
|
||||
GAME_OBJECT_ID Inv_GetItemOption(GAME_OBJECT_ID object_id);
|
||||
void __cdecl Inv_DoInventoryPicture(void);
|
||||
void __cdecl Inv_DoInventoryBackground(void);
|
||||
void __cdecl Inv_RingIsOpen(RING_INFO *ring);
|
||||
void __cdecl Inv_RingIsNotOpen(RING_INFO *ring);
|
||||
void __cdecl Inv_RingNotActive(const INVENTORY_ITEM *inv_item);
|
||||
void __cdecl Inv_RingActive(void);
|
||||
void __cdecl Inv_RemoveInventoryText(void);
|
292
src/tr2/game/inventory/ring.c
Normal file
292
src/tr2/game/inventory/ring.c
Normal file
|
@ -0,0 +1,292 @@
|
|||
#include "game/inventory/ring.h"
|
||||
|
||||
#include "game/math_misc.h"
|
||||
#include "game/output.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#define RING_OPEN_FRAMES 32
|
||||
#define RING_OPEN_ROTATION PHD_180
|
||||
#define RING_ROTATE_DURATION 24
|
||||
#define RING_RADIUS 688
|
||||
#define RING_CAMERA_START_HEIGHT (-1536)
|
||||
#define RING_CAMERA_HEIGHT (-256)
|
||||
#define RING_CAMERA_Y_OFFSET (-96)
|
||||
|
||||
void __cdecl Inv_Ring_Init(
|
||||
RING_INFO *const ring, const RING_TYPE type, INVENTORY_ITEM **const list,
|
||||
const int16_t qty, const int16_t current, IMOTION_INFO *const imo)
|
||||
{
|
||||
ring->type = type;
|
||||
ring->list = list;
|
||||
ring->number_of_objects = qty;
|
||||
ring->current_object = current;
|
||||
ring->radius = 0;
|
||||
ring->angle_adder = 0x10000 / qty;
|
||||
|
||||
if (g_Inv_Mode == INV_TITLE_MODE) {
|
||||
ring->camera_pitch = 1024;
|
||||
} else {
|
||||
ring->camera_pitch = 0;
|
||||
}
|
||||
|
||||
ring->rotating = 0;
|
||||
ring->rot_count = 0;
|
||||
ring->target_object = 0;
|
||||
ring->rot_adder = 0;
|
||||
ring->rot_adder_l = 0;
|
||||
ring->rot_adder_r = 0;
|
||||
|
||||
ring->imo = imo;
|
||||
|
||||
ring->camera.pos.x = 0;
|
||||
ring->camera.pos.y = RING_CAMERA_START_HEIGHT;
|
||||
ring->camera.pos.z = 896;
|
||||
ring->camera.rot.x = 0;
|
||||
ring->camera.rot.y = 0;
|
||||
ring->camera.rot.z = 0;
|
||||
|
||||
Inv_Ring_MotionInit(ring, RING_OPEN_FRAMES, RNG_OPENING, RNG_OPEN);
|
||||
Inv_Ring_MotionRadius(ring, RING_RADIUS);
|
||||
Inv_Ring_MotionCameraPos(ring, RING_CAMERA_HEIGHT);
|
||||
Inv_Ring_MotionRotation(
|
||||
ring, RING_OPEN_ROTATION,
|
||||
-PHD_90 - ring->current_object * ring->angle_adder);
|
||||
|
||||
ring->ring_pos.pos.x = 0;
|
||||
ring->ring_pos.pos.y = 0;
|
||||
ring->ring_pos.pos.z = 0;
|
||||
ring->ring_pos.rot.x = 0;
|
||||
ring->ring_pos.rot.y = imo->rotate_target + RING_OPEN_ROTATION;
|
||||
ring->ring_pos.rot.z = 0;
|
||||
|
||||
ring->light.x = -1536;
|
||||
ring->light.y = 256;
|
||||
ring->light.z = 1024;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_GetView(
|
||||
const RING_INFO *const ring, PHD_3DPOS *const view)
|
||||
{
|
||||
int16_t angles[2];
|
||||
|
||||
Math_GetVectorAngles(
|
||||
-ring->camera.pos.x, RING_CAMERA_Y_OFFSET - ring->camera.pos.y,
|
||||
ring->radius - ring->camera.pos.z, angles);
|
||||
view->pos.x = ring->camera.pos.x;
|
||||
view->pos.y = ring->camera.pos.y;
|
||||
view->pos.z = ring->camera.pos.z;
|
||||
view->rot.x = angles[1] + ring->camera_pitch;
|
||||
view->rot.y = angles[0];
|
||||
view->rot.z = 0;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_Light(const RING_INFO *const ring)
|
||||
{
|
||||
g_LsDivider = 0x6000;
|
||||
int16_t angles[2];
|
||||
Math_GetVectorAngles(ring->light.x, ring->light.y, ring->light.z, angles);
|
||||
Output_RotateLight(angles[1], angles[0]);
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_CalcAdders(
|
||||
RING_INFO *const ring, const int16_t rotation_duration)
|
||||
{
|
||||
ring->angle_adder = PHD_360 / ring->number_of_objects;
|
||||
ring->rot_adder_l = ring->angle_adder / rotation_duration;
|
||||
ring->rot_adder_r = -ring->angle_adder / rotation_duration;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_DoMotions(RING_INFO *const ring)
|
||||
{
|
||||
IMOTION_INFO *const imo = ring->imo;
|
||||
|
||||
if (imo->count != 0) {
|
||||
ring->radius += imo->radius_rate;
|
||||
ring->camera.pos.y += imo->camera_y_rate;
|
||||
ring->ring_pos.rot.y += imo->rotate_rate;
|
||||
ring->camera_pitch += imo->camera_pitch_rate;
|
||||
|
||||
INVENTORY_ITEM *const inv_item = ring->list[ring->current_object];
|
||||
inv_item->x_rot_pt += imo->item_pt_x_rot_rate;
|
||||
inv_item->x_rot += imo->item_x_rot_rate;
|
||||
inv_item->y_trans += imo->item_y_trans_rate;
|
||||
inv_item->z_trans += imo->item_z_trans_rate;
|
||||
|
||||
imo->count--;
|
||||
if (imo->count == 0) {
|
||||
imo->status = imo->status_target;
|
||||
|
||||
if (imo->radius_rate != 0) {
|
||||
imo->radius_rate = 0;
|
||||
ring->radius = imo->radius_target;
|
||||
}
|
||||
if (imo->camera_y_rate != 0) {
|
||||
imo->camera_y_rate = 0;
|
||||
ring->camera.pos.y = imo->camera_y_target;
|
||||
}
|
||||
if (imo->rotate_rate != 0) {
|
||||
imo->rotate_rate = 0;
|
||||
ring->ring_pos.rot.y = imo->rotate_target;
|
||||
}
|
||||
if (imo->item_pt_x_rot_rate != 0) {
|
||||
imo->item_pt_x_rot_rate = 0;
|
||||
inv_item->x_rot_pt = imo->item_pt_x_rot_target;
|
||||
}
|
||||
if (imo->item_x_rot_rate != 0) {
|
||||
imo->item_x_rot_rate = 0;
|
||||
inv_item->x_rot = imo->item_x_rot_target;
|
||||
}
|
||||
if (imo->item_y_trans_rate != 0) {
|
||||
imo->item_y_trans_rate = 0;
|
||||
inv_item->y_trans = imo->item_y_trans_target;
|
||||
}
|
||||
if (imo->item_z_trans_rate != 0) {
|
||||
imo->item_z_trans_rate = 0;
|
||||
inv_item->z_trans = imo->item_z_trans_target;
|
||||
}
|
||||
if (imo->camera_pitch_rate != 0) {
|
||||
imo->camera_pitch_rate = 0;
|
||||
ring->camera_pitch = imo->camera_pitch_target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ring->rotating) {
|
||||
ring->ring_pos.rot.y += ring->rot_adder;
|
||||
ring->rot_count--;
|
||||
|
||||
if (ring->rot_count == 0) {
|
||||
ring->current_object = ring->target_object;
|
||||
ring->ring_pos.rot.y =
|
||||
-PHD_90 - ring->target_object * ring->angle_adder;
|
||||
ring->rotating = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_RotateLeft(RING_INFO *const ring)
|
||||
{
|
||||
ring->rotating = 1;
|
||||
if (ring->current_object == 0) {
|
||||
ring->target_object = ring->number_of_objects - 1;
|
||||
} else {
|
||||
ring->target_object = ring->current_object - 1;
|
||||
}
|
||||
ring->rot_count = RING_ROTATE_DURATION;
|
||||
ring->rot_adder = ring->rot_adder_l;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_RotateRight(RING_INFO *const ring)
|
||||
{
|
||||
ring->rotating = 1;
|
||||
if (ring->current_object + 1 >= ring->number_of_objects) {
|
||||
ring->target_object = 0;
|
||||
} else {
|
||||
ring->target_object = ring->current_object + 1;
|
||||
}
|
||||
ring->rot_count = RING_ROTATE_DURATION;
|
||||
ring->rot_adder = ring->rot_adder_r;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_MotionInit(
|
||||
RING_INFO *const ring, const int16_t frames, const RING_STATUS status,
|
||||
const RING_STATUS status_target)
|
||||
{
|
||||
IMOTION_INFO *const imo = ring->imo;
|
||||
imo->count = frames;
|
||||
imo->status = status;
|
||||
imo->status_target = status_target;
|
||||
|
||||
imo->radius_target = 0;
|
||||
imo->radius_rate = 0;
|
||||
imo->camera_y_target = 0;
|
||||
imo->camera_y_rate = 0;
|
||||
imo->camera_pitch_target = 0;
|
||||
imo->camera_pitch_rate = 0;
|
||||
imo->rotate_target = 0;
|
||||
imo->rotate_rate = 0;
|
||||
imo->item_pt_x_rot_target = 0;
|
||||
imo->item_pt_x_rot_rate = 0;
|
||||
imo->item_x_rot_target = 0;
|
||||
imo->item_x_rot_rate = 0;
|
||||
imo->item_y_trans_target = 0;
|
||||
imo->item_y_trans_rate = 0;
|
||||
imo->item_z_trans_target = 0;
|
||||
imo->item_z_trans_rate = 0;
|
||||
|
||||
imo->misc = 0;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_MotionSetup(
|
||||
RING_INFO *const ring, const RING_STATUS status,
|
||||
const RING_STATUS status_target, const int16_t frames)
|
||||
{
|
||||
IMOTION_INFO *const imo = ring->imo;
|
||||
imo->count = frames;
|
||||
imo->status = status;
|
||||
imo->status_target = status_target;
|
||||
imo->radius_rate = 0;
|
||||
imo->camera_y_rate = 0;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_MotionRadius(RING_INFO *const ring, const int16_t target)
|
||||
{
|
||||
IMOTION_INFO *const imo = ring->imo;
|
||||
imo->radius_target = target;
|
||||
imo->radius_rate = (target - ring->radius) / imo->count;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_MotionRotation(
|
||||
RING_INFO *const ring, const int16_t rotation, const int16_t target)
|
||||
{
|
||||
IMOTION_INFO *const imo = ring->imo;
|
||||
imo->rotate_target = target;
|
||||
imo->rotate_rate = rotation / imo->count;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_MotionCameraPos(
|
||||
RING_INFO *const ring, const int16_t target)
|
||||
{
|
||||
IMOTION_INFO *const imo = ring->imo;
|
||||
imo->camera_y_target = target;
|
||||
imo->camera_y_rate = (target - ring->camera.pos.y) / imo->count;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_MotionCameraPitch(
|
||||
RING_INFO *const ring, const int16_t target)
|
||||
{
|
||||
IMOTION_INFO *const imo = ring->imo;
|
||||
imo->camera_pitch_target = target;
|
||||
imo->camera_pitch_rate = target / imo->count;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_MotionItemSelect(
|
||||
RING_INFO *const ring, const INVENTORY_ITEM *const inv_item)
|
||||
{
|
||||
IMOTION_INFO *const imo = ring->imo;
|
||||
imo->item_pt_x_rot_target = inv_item->x_rot_pt_sel;
|
||||
imo->item_pt_x_rot_rate = inv_item->x_rot_pt_sel / imo->count;
|
||||
imo->item_x_rot_target = inv_item->x_rot_sel;
|
||||
imo->item_x_rot_rate =
|
||||
(inv_item->x_rot_sel - inv_item->x_rot_nosel) / imo->count;
|
||||
imo->item_y_trans_target = inv_item->y_trans_sel;
|
||||
imo->item_y_trans_rate = inv_item->y_trans_sel / imo->count;
|
||||
imo->item_z_trans_target = inv_item->z_trans_sel;
|
||||
imo->item_z_trans_rate = inv_item->z_trans_sel / imo->count;
|
||||
}
|
||||
|
||||
void __cdecl Inv_Ring_MotionItemDeselect(
|
||||
RING_INFO *const ring, const INVENTORY_ITEM *const inv_item)
|
||||
{
|
||||
IMOTION_INFO *const imo = ring->imo;
|
||||
imo->item_pt_x_rot_target = 0;
|
||||
imo->item_pt_x_rot_rate = -(inv_item->x_rot_pt_sel / imo->count);
|
||||
imo->item_x_rot_target = inv_item->x_rot_nosel;
|
||||
imo->item_x_rot_rate =
|
||||
(inv_item->x_rot_nosel - inv_item->x_rot_sel) / imo->count;
|
||||
imo->item_y_trans_target = 0;
|
||||
imo->item_y_trans_rate = -(inv_item->y_trans_sel / imo->count);
|
||||
imo->item_z_trans_target = 0;
|
||||
imo->item_z_trans_rate = -(inv_item->z_trans_sel / imo->count);
|
||||
}
|
28
src/tr2/game/inventory/ring.h
Normal file
28
src/tr2/game/inventory/ring.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Inv_Ring_Init(
|
||||
RING_INFO *ring, RING_TYPE type, INVENTORY_ITEM **list, int16_t qty,
|
||||
int16_t current, IMOTION_INFO *imo);
|
||||
void __cdecl Inv_Ring_GetView(const RING_INFO *ring, PHD_3DPOS *view);
|
||||
void __cdecl Inv_Ring_Light(const RING_INFO *ring);
|
||||
void __cdecl Inv_Ring_CalcAdders(RING_INFO *ring, int16_t rotation_duration);
|
||||
void __cdecl Inv_Ring_DoMotions(RING_INFO *ring);
|
||||
void __cdecl Inv_Ring_RotateLeft(RING_INFO *ring);
|
||||
void __cdecl Inv_Ring_RotateRight(RING_INFO *ring);
|
||||
void __cdecl Inv_Ring_MotionInit(
|
||||
RING_INFO *ring, int16_t frames, RING_STATUS status,
|
||||
RING_STATUS status_target);
|
||||
void __cdecl Inv_Ring_MotionSetup(
|
||||
RING_INFO *ring, RING_STATUS status, RING_STATUS status_target,
|
||||
int16_t frames);
|
||||
void __cdecl Inv_Ring_MotionRadius(RING_INFO *ring, int16_t target);
|
||||
void __cdecl Inv_Ring_MotionRotation(
|
||||
RING_INFO *ring, int16_t rotation, int16_t target);
|
||||
void __cdecl Inv_Ring_MotionCameraPos(RING_INFO *ring, int16_t target);
|
||||
void __cdecl Inv_Ring_MotionCameraPitch(RING_INFO *ring, int16_t target);
|
||||
void __cdecl Inv_Ring_MotionItemSelect(
|
||||
RING_INFO *ring, const INVENTORY_ITEM *inv_item);
|
||||
void __cdecl Inv_Ring_MotionItemDeselect(
|
||||
RING_INFO *ring, const INVENTORY_ITEM *inv_item);
|
12
src/tr2/game/inventory/vars.c
Normal file
12
src/tr2/game/inventory/vars.c
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "game/inventory/vars.h"
|
||||
|
||||
#include "global/vars.h"
|
||||
|
||||
int32_t g_Inv_OptionObjectsCount = 4;
|
||||
|
||||
INVENTORY_ITEM *g_Inv_OptionList[] = {
|
||||
&g_Inv_Item_Passport,
|
||||
&g_Inv_Item_Controls,
|
||||
&g_Inv_Item_Sound,
|
||||
&g_Inv_Item_Photo,
|
||||
};
|
6
src/tr2/game/inventory/vars.h
Normal file
6
src/tr2/game/inventory/vars.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
extern int32_t g_Inv_OptionObjectsCount;
|
||||
extern INVENTORY_ITEM *g_Inv_OptionList[];
|
735
src/tr2/game/items.c
Normal file
735
src/tr2/game/items.c
Normal file
|
@ -0,0 +1,735 @@
|
|||
#include "game/items.h"
|
||||
|
||||
#include "game/math.h"
|
||||
#include "game/matrix.h"
|
||||
#include "game/room.h"
|
||||
#include "game/sound.h"
|
||||
#include "global/const.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static int16_t m_MaxUsedItemCount = 0;
|
||||
static BOUNDS_16 m_InterpolatedBounds = { 0 };
|
||||
|
||||
static OBJECT_BOUNDS M_ConvertBounds(const int16_t *bounds_in);
|
||||
|
||||
static OBJECT_BOUNDS M_ConvertBounds(const int16_t *const bounds_in)
|
||||
{
|
||||
// TODO: remove this conversion utility once we gain control over its
|
||||
// incoming arguments
|
||||
return (OBJECT_BOUNDS) {
|
||||
.shift = {
|
||||
.min = {
|
||||
.x = bounds_in[0],
|
||||
.y = bounds_in[2],
|
||||
.z = bounds_in[4],
|
||||
},
|
||||
.max = {
|
||||
.x = bounds_in[1],
|
||||
.y = bounds_in[3],
|
||||
.z = bounds_in[5],
|
||||
},
|
||||
},
|
||||
.rot = {
|
||||
.min = {
|
||||
.x = bounds_in[6],
|
||||
.y = bounds_in[8],
|
||||
.z = bounds_in[10],
|
||||
},
|
||||
.max = {
|
||||
.x = bounds_in[7],
|
||||
.y = bounds_in[9],
|
||||
.z = bounds_in[11],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
void __cdecl Item_InitialiseArray(const int32_t num_items)
|
||||
{
|
||||
assert(num_items > 0);
|
||||
g_NextItemFree = g_LevelItemCount;
|
||||
g_PrevItemActive = NO_ITEM;
|
||||
g_NextItemActive = NO_ITEM;
|
||||
m_MaxUsedItemCount = g_LevelItemCount;
|
||||
for (int32_t i = g_NextItemFree; i < num_items - 1; i++) {
|
||||
ITEM *const item = &g_Items[i];
|
||||
item->active = 0;
|
||||
item->next_item = i + 1;
|
||||
}
|
||||
g_Items[num_items - 1].next_item = NO_ITEM;
|
||||
}
|
||||
|
||||
int32_t Item_GetTotalCount(void)
|
||||
{
|
||||
return m_MaxUsedItemCount;
|
||||
}
|
||||
|
||||
int16_t __cdecl Item_Create(void)
|
||||
{
|
||||
const int16_t item_num = g_NextItemFree;
|
||||
if (item_num != NO_ITEM) {
|
||||
g_Items[item_num].flags = 0;
|
||||
g_NextItemFree = g_Items[item_num].next_item;
|
||||
}
|
||||
m_MaxUsedItemCount = MAX(m_MaxUsedItemCount, item_num + 1);
|
||||
return item_num;
|
||||
}
|
||||
|
||||
void __cdecl Item_Kill(const int16_t item_num)
|
||||
{
|
||||
Item_RemoveActive(item_num);
|
||||
Item_RemoveDrawn(item_num);
|
||||
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
if (item == g_Lara.target) {
|
||||
g_Lara.target = NULL;
|
||||
}
|
||||
|
||||
if (item_num < g_LevelItemCount) {
|
||||
item->flags |= IF_KILLED;
|
||||
} else {
|
||||
item->next_item = g_NextItemFree;
|
||||
g_NextItemFree = item_num;
|
||||
}
|
||||
|
||||
while (m_MaxUsedItemCount > 0
|
||||
&& g_Items[m_MaxUsedItemCount - 1].flags & IF_KILLED) {
|
||||
m_MaxUsedItemCount--;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Item_Initialise(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
item->anim_num = g_Objects[item->object_id].anim_idx;
|
||||
item->frame_num = g_Anims[item->anim_num].frame_base;
|
||||
item->goal_anim_state = g_Anims[item->anim_num].current_anim_state;
|
||||
item->current_anim_state = item->goal_anim_state;
|
||||
item->required_anim_state = 0;
|
||||
item->rot.x = 0;
|
||||
item->rot.z = 0;
|
||||
item->speed = 0;
|
||||
item->fall_speed = 0;
|
||||
item->hit_points = g_Objects[item->object_id].hit_points;
|
||||
item->timer = 0;
|
||||
item->mesh_bits = 0xFFFFFFFF;
|
||||
item->touch_bits = 0;
|
||||
item->data = NULL;
|
||||
|
||||
item->active = 0;
|
||||
item->status = IS_INACTIVE;
|
||||
item->gravity = 0;
|
||||
item->hit_status = 0;
|
||||
item->collidable = 1;
|
||||
item->looked_at = 0;
|
||||
item->killed = 0;
|
||||
|
||||
if ((item->flags & IF_INVISIBLE) != 0) {
|
||||
item->status = IS_INVISIBLE;
|
||||
item->flags &= ~IF_INVISIBLE;
|
||||
} else if (g_Objects[item->object_id].intelligent) {
|
||||
item->status = IS_INVISIBLE;
|
||||
}
|
||||
|
||||
if (item->flags & IF_KILLED) {
|
||||
item->killed = 1;
|
||||
item->flags &= ~IF_KILLED;
|
||||
}
|
||||
|
||||
if ((item->flags & IF_CODE_BITS) == IF_CODE_BITS) {
|
||||
item->flags &= ~IF_CODE_BITS;
|
||||
item->flags |= IF_REVERSE;
|
||||
Item_AddActive(item_num);
|
||||
item->status = IS_ACTIVE;
|
||||
}
|
||||
|
||||
ROOM *const room = &g_Rooms[item->room_num];
|
||||
item->next_item = room->item_num;
|
||||
room->item_num = item_num;
|
||||
|
||||
const int32_t dx = (item->pos.x - room->pos.x) >> WALL_SHIFT;
|
||||
const int32_t dz = (item->pos.z - room->pos.z) >> WALL_SHIFT;
|
||||
const SECTOR *const sector = &room->sectors[dx * room->size.z + dz];
|
||||
item->floor = sector->floor << 8;
|
||||
|
||||
if (g_SaveGame.bonus_flag && !g_IsDemoLevelType) {
|
||||
item->hit_points *= 2;
|
||||
}
|
||||
|
||||
if (g_Objects[item->object_id].initialise != NULL) {
|
||||
g_Objects[item->object_id].initialise(item_num);
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Item_RemoveActive(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
if (!item->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
item->active = 0;
|
||||
|
||||
int16_t link_num = g_NextItemActive;
|
||||
if (link_num == item_num) {
|
||||
g_NextItemActive = item->next_active;
|
||||
return;
|
||||
}
|
||||
|
||||
while (link_num != NO_ITEM) {
|
||||
if (g_Items[link_num].next_active == item_num) {
|
||||
g_Items[link_num].next_active = item->next_active;
|
||||
return;
|
||||
}
|
||||
link_num = g_Items[link_num].next_active;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Item_RemoveDrawn(const int16_t item_num)
|
||||
{
|
||||
const ITEM *const item = &g_Items[item_num];
|
||||
if (item->room_num == NO_ROOM) {
|
||||
return;
|
||||
}
|
||||
|
||||
int16_t link_num = g_Rooms[item->room_num].item_num;
|
||||
if (link_num == item_num) {
|
||||
g_Rooms[item->room_num].item_num = item->next_item;
|
||||
return;
|
||||
}
|
||||
|
||||
while (link_num != NO_ITEM) {
|
||||
if (g_Items[link_num].next_item == item_num) {
|
||||
g_Items[link_num].next_item = item->next_item;
|
||||
return;
|
||||
}
|
||||
link_num = g_Items[link_num].next_item;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Item_AddActive(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
if (g_Objects[item->object_id].control == NULL) {
|
||||
item->status = IS_INACTIVE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
item->active = 1;
|
||||
item->next_active = g_NextItemActive;
|
||||
g_NextItemActive = item_num;
|
||||
}
|
||||
|
||||
void __cdecl Item_NewRoom(const int16_t item_num, const int16_t room_num)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
ROOM *room = NULL;
|
||||
|
||||
if (item->room_num != NO_ROOM) {
|
||||
room = &g_Rooms[item->room_num];
|
||||
|
||||
int16_t link_num = room->item_num;
|
||||
if (link_num == item_num) {
|
||||
room->item_num = item->next_item;
|
||||
} else {
|
||||
while (link_num != NO_ITEM) {
|
||||
if (g_Items[link_num].next_item == item_num) {
|
||||
g_Items[link_num].next_item = item->next_item;
|
||||
break;
|
||||
}
|
||||
link_num = g_Items[link_num].next_item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item->room_num = room_num;
|
||||
room = &g_Rooms[room_num];
|
||||
item->next_item = room->item_num;
|
||||
room->item_num = item_num;
|
||||
}
|
||||
|
||||
int32_t __cdecl Item_GlobalReplace(
|
||||
const GAME_OBJECT_ID src_object_id, const GAME_OBJECT_ID dst_object_id)
|
||||
{
|
||||
int32_t changed = 0;
|
||||
|
||||
for (int32_t i = 0; i < g_RoomCount; i++) {
|
||||
int16_t j = g_Rooms[i].item_num;
|
||||
while (j != NO_ITEM) {
|
||||
ITEM *const item = &g_Items[j];
|
||||
if (item->object_id == src_object_id) {
|
||||
item->object_id = dst_object_id;
|
||||
changed++;
|
||||
}
|
||||
j = item->next_item;
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
void __cdecl Item_ClearKilled(void)
|
||||
{
|
||||
// Remove corpses and other killed items. Part of OG performance
|
||||
// improvements, generously used in Opera House and Barkhang Monastery
|
||||
int16_t link_num = g_PrevItemActive;
|
||||
while (link_num != NO_ITEM) {
|
||||
ITEM *const item = &g_Items[link_num];
|
||||
Item_Kill(link_num);
|
||||
link_num = item->next_active;
|
||||
item->next_active = NO_ITEM;
|
||||
}
|
||||
g_PrevItemActive = NO_ITEM;
|
||||
}
|
||||
|
||||
bool Item_IsSmashable(const ITEM *item)
|
||||
{
|
||||
return (item->object_id == O_WINDOW_1 || item->object_id == O_BELL);
|
||||
}
|
||||
|
||||
void __cdecl Item_ShiftCol(ITEM *const item, COLL_INFO *const coll)
|
||||
{
|
||||
item->pos.x += coll->shift.x;
|
||||
item->pos.y += coll->shift.y;
|
||||
item->pos.z += coll->shift.z;
|
||||
coll->shift.z = 0;
|
||||
coll->shift.y = 0;
|
||||
coll->shift.x = 0;
|
||||
}
|
||||
|
||||
void __cdecl Item_UpdateRoom(ITEM *const item, const int32_t height)
|
||||
{
|
||||
int32_t x = item->pos.x;
|
||||
int32_t y = height + item->pos.y;
|
||||
int32_t z = item->pos.z;
|
||||
|
||||
int16_t room_num = item->room_num;
|
||||
const SECTOR *const sector = Room_GetSector(x, y, z, &room_num);
|
||||
item->floor = Room_GetHeight(sector, x, y, z);
|
||||
if (item->room_num != room_num) {
|
||||
Item_NewRoom(g_Lara.item_num, room_num);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t __cdecl Item_TestBoundsCollide(
|
||||
const ITEM *const src_item, const ITEM *const dst_item,
|
||||
const int32_t radius)
|
||||
{
|
||||
const BOUNDS_16 *const src_bounds = &Item_GetBestFrame(src_item)->bounds;
|
||||
const BOUNDS_16 *const dst_bounds = &Item_GetBestFrame(dst_item)->bounds;
|
||||
|
||||
if (src_item->pos.y + src_bounds->max_y
|
||||
<= dst_item->pos.y + dst_bounds->min_y
|
||||
|| src_item->pos.y + src_bounds->min_y
|
||||
>= dst_item->pos.y + dst_bounds->max_y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int32_t c = Math_Cos(src_item->rot.y);
|
||||
const int32_t s = Math_Sin(src_item->rot.y);
|
||||
const int32_t dx = dst_item->pos.x - src_item->pos.x;
|
||||
const int32_t dz = dst_item->pos.z - src_item->pos.z;
|
||||
const int32_t rx = (c * dx - s * dz) >> W2V_SHIFT;
|
||||
const int32_t rz = (c * dz + s * dx) >> W2V_SHIFT;
|
||||
|
||||
// clang-format off
|
||||
return (
|
||||
rx >= src_bounds->min_x - radius &&
|
||||
rx <= src_bounds->max_x + radius &&
|
||||
rz >= src_bounds->min_z - radius &&
|
||||
rz <= src_bounds->max_z + radius);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
int32_t __cdecl Item_TestPosition(
|
||||
const int16_t *const bounds_in, const ITEM *const src_item,
|
||||
const ITEM *const dst_item)
|
||||
{
|
||||
const OBJECT_BOUNDS bounds = M_ConvertBounds(bounds_in);
|
||||
|
||||
const XYZ_16 rot = {
|
||||
.x = dst_item->rot.x - src_item->rot.x,
|
||||
.y = dst_item->rot.y - src_item->rot.y,
|
||||
.z = dst_item->rot.z - src_item->rot.z,
|
||||
};
|
||||
const XYZ_32 dist = {
|
||||
.x = dst_item->pos.x - src_item->pos.x,
|
||||
.y = dst_item->pos.y - src_item->pos.y,
|
||||
.z = dst_item->pos.z - src_item->pos.z,
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
if (rot.x < bounds.rot.min.x ||
|
||||
rot.x > bounds.rot.max.x ||
|
||||
rot.y < bounds.rot.min.y ||
|
||||
rot.y > bounds.rot.max.y ||
|
||||
rot.z < bounds.rot.min.z ||
|
||||
rot.z > bounds.rot.max.z
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
Matrix_PushUnit();
|
||||
Matrix_RotYXZ(src_item->rot.y, src_item->rot.x, src_item->rot.z);
|
||||
const MATRIX *const m = g_MatrixPtr;
|
||||
const XYZ_32 shift = {
|
||||
.x = (dist.x * m->_00 + dist.y * m->_10 + dist.z * m->_20) >> W2V_SHIFT,
|
||||
.y = (dist.x * m->_01 + dist.y * m->_11 + dist.z * m->_21) >> W2V_SHIFT,
|
||||
.z = (dist.x * m->_02 + dist.y * m->_12 + dist.z * m->_22) >> W2V_SHIFT,
|
||||
};
|
||||
Matrix_Pop();
|
||||
|
||||
// clang-format off
|
||||
return (
|
||||
shift.x >= bounds.shift.min.x &&
|
||||
shift.x <= bounds.shift.max.x &&
|
||||
shift.y >= bounds.shift.min.y &&
|
||||
shift.y <= bounds.shift.max.y &&
|
||||
shift.z >= bounds.shift.min.z &&
|
||||
shift.z <= bounds.shift.max.z
|
||||
);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void __cdecl Item_AlignPosition(
|
||||
const XYZ_32 *const vec, const ITEM *const src_item, ITEM *const dst_item)
|
||||
{
|
||||
dst_item->rot = src_item->rot;
|
||||
Matrix_PushUnit();
|
||||
Matrix_RotYXZ(src_item->rot.y, src_item->rot.x, src_item->rot.z);
|
||||
const MATRIX *const m = g_MatrixPtr;
|
||||
const XYZ_32 shift = {
|
||||
.x = (vec->x * m->_00 + vec->y * m->_01 + vec->z * m->_02) >> W2V_SHIFT,
|
||||
.y = (vec->x * m->_10 + vec->y * m->_11 + vec->z * m->_12) >> W2V_SHIFT,
|
||||
.z = (vec->x * m->_20 + vec->y * m->_21 + vec->z * m->_22) >> W2V_SHIFT,
|
||||
};
|
||||
Matrix_Pop();
|
||||
|
||||
const XYZ_32 new_pos = {
|
||||
.x = src_item->pos.x + shift.x,
|
||||
.y = src_item->pos.y + shift.y,
|
||||
.z = src_item->pos.z + shift.z,
|
||||
};
|
||||
|
||||
int16_t room_num = dst_item->room_num;
|
||||
const SECTOR *const sector =
|
||||
Room_GetSector(new_pos.x, new_pos.y, new_pos.z, &room_num);
|
||||
const int32_t height =
|
||||
Room_GetHeight(sector, new_pos.x, new_pos.y, new_pos.z);
|
||||
const int32_t ceiling =
|
||||
Room_GetCeiling(sector, new_pos.x, new_pos.y, new_pos.z);
|
||||
|
||||
if (ABS(height - dst_item->pos.y) > STEP_L
|
||||
|| ABS(ceiling - dst_item->pos.y) < LARA_HEIGHT) {
|
||||
return;
|
||||
}
|
||||
|
||||
dst_item->pos.x = new_pos.x;
|
||||
dst_item->pos.y = new_pos.y;
|
||||
dst_item->pos.z = new_pos.z;
|
||||
}
|
||||
|
||||
void __cdecl Item_Animate(ITEM *const item)
|
||||
{
|
||||
item->hit_status = 0;
|
||||
item->touch_bits = 0;
|
||||
|
||||
const ANIM *anim = &g_Anims[item->anim_num];
|
||||
|
||||
item->frame_num++;
|
||||
|
||||
if (anim->num_changes > 0) {
|
||||
if (Item_GetAnimChange(item, anim)) {
|
||||
anim = &g_Anims[item->anim_num];
|
||||
item->current_anim_state = anim->current_anim_state;
|
||||
|
||||
if (item->required_anim_state == anim->current_anim_state) {
|
||||
item->required_anim_state = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item->frame_num > anim->frame_end) {
|
||||
if (anim->num_commands > 0) {
|
||||
const int16_t *cmd_ptr = &g_AnimCommands[anim->command_idx];
|
||||
|
||||
for (int32_t i = 0; i < anim->num_commands; i++) {
|
||||
const int16_t cmd = *cmd_ptr++;
|
||||
|
||||
switch (cmd) {
|
||||
case AC_MOVE_ORIGIN:
|
||||
Item_Translate(item, cmd_ptr[0], cmd_ptr[1], cmd_ptr[2]);
|
||||
cmd_ptr += 3;
|
||||
break;
|
||||
|
||||
case AC_JUMP_VELOCITY:
|
||||
item->fall_speed = cmd_ptr[0];
|
||||
item->speed = cmd_ptr[1];
|
||||
item->gravity = 1;
|
||||
cmd_ptr += 2;
|
||||
break;
|
||||
|
||||
case AC_DEACTIVATE:
|
||||
item->status = IS_DEACTIVATED;
|
||||
break;
|
||||
|
||||
case AC_SOUND_FX:
|
||||
case AC_EFFECT:
|
||||
cmd_ptr += 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item->anim_num = anim->jump_anim_num;
|
||||
item->frame_num = anim->jump_frame_num;
|
||||
anim = &g_Anims[item->anim_num];
|
||||
|
||||
if (item->current_anim_state != anim->current_anim_state) {
|
||||
item->current_anim_state = anim->current_anim_state;
|
||||
item->goal_anim_state = anim->current_anim_state;
|
||||
}
|
||||
|
||||
if (item->required_anim_state == item->current_anim_state) {
|
||||
item->required_anim_state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (anim->num_commands > 0) {
|
||||
const int16_t *cmd_ptr = &g_AnimCommands[anim->command_idx];
|
||||
for (int32_t i = 0; i < anim->num_commands; i++) {
|
||||
const int16_t cmd = *cmd_ptr++;
|
||||
switch (cmd) {
|
||||
case AC_MOVE_ORIGIN:
|
||||
cmd_ptr += 3;
|
||||
break;
|
||||
|
||||
case AC_JUMP_VELOCITY:
|
||||
cmd_ptr += 2;
|
||||
break;
|
||||
|
||||
case AC_SOUND_FX: {
|
||||
const int32_t frame = cmd_ptr[0];
|
||||
const SOUND_EFFECT_ID sound_id = cmd_ptr[1] & 0x3FFF;
|
||||
const ANIM_COMMAND_ENVIRONMENT type =
|
||||
(cmd_ptr[1] & 0xC000) >> 14;
|
||||
cmd_ptr += 2;
|
||||
|
||||
if (item->frame_num != frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (g_Objects[item->object_id].water_creature) {
|
||||
Sound_Effect(sound_id, &item->pos, SPM_UNDERWATER);
|
||||
} else if (item->room_num == NO_ROOM) {
|
||||
item->pos.x = g_LaraItem->pos.x;
|
||||
item->pos.y = g_LaraItem->pos.y - LARA_HEIGHT;
|
||||
item->pos.z = g_LaraItem->pos.z;
|
||||
Sound_Effect(
|
||||
sound_id, &item->pos,
|
||||
item->object_id == O_LARA_HARPOON ? SPM_ALWAYS
|
||||
: SPM_NORMAL);
|
||||
} else if (g_Rooms[item->room_num].flags & RF_UNDERWATER) {
|
||||
if (type == ACE_ALL || type == ACE_WATER) {
|
||||
Sound_Effect(sound_id, &item->pos, SPM_NORMAL);
|
||||
}
|
||||
} else if (type == ACE_ALL || type == ACE_LAND) {
|
||||
Sound_Effect(sound_id, &item->pos, SPM_NORMAL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case AC_EFFECT: {
|
||||
const int32_t frame = cmd_ptr[0];
|
||||
const int32_t fx_func_idx = cmd_ptr[1] & 0x3FFF;
|
||||
cmd_ptr += 2;
|
||||
|
||||
if (item->frame_num == frame) {
|
||||
g_EffectRoutines[fx_func_idx](item);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item->gravity) {
|
||||
item->fall_speed += item->fall_speed < FAST_FALL_SPEED ? GRAVITY : 1;
|
||||
item->pos.y += item->fall_speed;
|
||||
} else {
|
||||
int32_t speed = anim->velocity;
|
||||
if (anim->acceleration) {
|
||||
speed += anim->acceleration * (item->frame_num - anim->frame_base);
|
||||
}
|
||||
item->speed = speed >> 16;
|
||||
}
|
||||
|
||||
item->pos.x += (item->speed * Math_Sin(item->rot.y)) >> W2V_SHIFT;
|
||||
item->pos.z += (item->speed * Math_Cos(item->rot.y)) >> W2V_SHIFT;
|
||||
}
|
||||
|
||||
int32_t __cdecl Item_GetAnimChange(ITEM *const item, const ANIM *const anim)
|
||||
{
|
||||
if (item->current_anim_state == item->goal_anim_state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < anim->num_changes; i++) {
|
||||
const ANIM_CHANGE *const change = &g_AnimChanges[anim->change_idx + i];
|
||||
if (change->goal_anim_state != item->goal_anim_state) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32_t j = 0; j < change->num_ranges; j++) {
|
||||
const ANIM_RANGE *const range =
|
||||
&g_AnimRanges[change->range_idx + j];
|
||||
|
||||
if (item->frame_num >= range->start_frame
|
||||
&& item->frame_num <= range->end_frame) {
|
||||
item->anim_num = range->link_anim_num;
|
||||
item->frame_num = range->link_frame_num;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void __cdecl Item_Translate(
|
||||
ITEM *const item, const int32_t x, const int32_t y, const int32_t z)
|
||||
{
|
||||
const int32_t c = Math_Cos(item->rot.y);
|
||||
const int32_t s = Math_Sin(item->rot.y);
|
||||
item->pos.x += ((c * x + s * z) >> W2V_SHIFT);
|
||||
item->pos.y += y;
|
||||
item->pos.z += ((c * z - s * x) >> W2V_SHIFT);
|
||||
}
|
||||
|
||||
int32_t __cdecl Item_IsTriggerActive(ITEM *const item)
|
||||
{
|
||||
const bool ok = !(item->flags & IF_REVERSE);
|
||||
|
||||
if ((item->flags & IF_CODE_BITS) != IF_CODE_BITS) {
|
||||
return !ok;
|
||||
}
|
||||
|
||||
if (!item->timer) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
if (item->timer == -1) {
|
||||
return !ok;
|
||||
}
|
||||
|
||||
item->timer--;
|
||||
if (item->timer == 0) {
|
||||
item->timer = -1;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int32_t __cdecl Item_GetFrames(
|
||||
const ITEM *item, FRAME_INFO *frmptr[], int32_t *rate)
|
||||
{
|
||||
const ANIM *const anim = &g_Anims[item->anim_num];
|
||||
const int32_t cur_frame_num = item->frame_num - anim->frame_base;
|
||||
const int32_t size = anim->interpolation >> 8;
|
||||
const int32_t key_frame_span = anim->interpolation & 0xFF;
|
||||
const int32_t key_frame_shift = cur_frame_num % key_frame_span;
|
||||
const int32_t first_key_frame_num = cur_frame_num / key_frame_span * size;
|
||||
const int32_t second_key_frame_num = first_key_frame_num + size;
|
||||
|
||||
const int32_t numerator = key_frame_shift;
|
||||
int32_t denominator = key_frame_span;
|
||||
if (numerator != 0) {
|
||||
// TODO: ??
|
||||
const int32_t second_key_frame_num2 =
|
||||
(cur_frame_num / key_frame_span + 1) * key_frame_span;
|
||||
if (second_key_frame_num2 > anim->frame_end) {
|
||||
denominator += anim->frame_end - second_key_frame_num2;
|
||||
}
|
||||
}
|
||||
|
||||
frmptr[0] = (FRAME_INFO *)(anim->frame_ptr + first_key_frame_num);
|
||||
frmptr[1] = (FRAME_INFO *)(anim->frame_ptr + second_key_frame_num);
|
||||
*rate = denominator;
|
||||
return numerator;
|
||||
}
|
||||
|
||||
BOUNDS_16 *__cdecl Item_GetBoundsAccurate(const ITEM *const item)
|
||||
{
|
||||
int32_t rate;
|
||||
FRAME_INFO *frmptr[2];
|
||||
const int32_t frac = Item_GetFrames(item, frmptr, &rate);
|
||||
if (!frac) {
|
||||
return &frmptr[0]->bounds;
|
||||
}
|
||||
|
||||
#define CALC(target, b1, b2, prop) \
|
||||
target->prop = (b1)->prop + ((((b2)->prop - (b1)->prop) * frac) / rate);
|
||||
|
||||
BOUNDS_16 *const result = &m_InterpolatedBounds;
|
||||
CALC(result, &frmptr[0]->bounds, &frmptr[1]->bounds, min_x);
|
||||
CALC(result, &frmptr[0]->bounds, &frmptr[1]->bounds, max_x);
|
||||
CALC(result, &frmptr[0]->bounds, &frmptr[1]->bounds, min_y);
|
||||
CALC(result, &frmptr[0]->bounds, &frmptr[1]->bounds, max_y);
|
||||
CALC(result, &frmptr[0]->bounds, &frmptr[1]->bounds, min_z);
|
||||
CALC(result, &frmptr[0]->bounds, &frmptr[1]->bounds, max_z);
|
||||
return result;
|
||||
}
|
||||
|
||||
FRAME_INFO *__cdecl Item_GetBestFrame(const ITEM *const item)
|
||||
{
|
||||
FRAME_INFO *frmptr[2];
|
||||
int32_t rate;
|
||||
const int32_t frac = Item_GetFrames(item, frmptr, &rate);
|
||||
return frmptr[(frac > rate / 2) ? 1 : 0];
|
||||
}
|
||||
|
||||
bool __cdecl Item_IsNearItem(
|
||||
const ITEM *const item, const XYZ_32 *const pos, const int32_t distance)
|
||||
{
|
||||
const XYZ_32 d = {
|
||||
.x = pos->x - item->pos.x,
|
||||
.y = pos->y - item->pos.y,
|
||||
.z = pos->z - item->pos.z,
|
||||
};
|
||||
if (ABS(d.x) > distance || ABS(d.z) > distance || ABS(d.y) > WALL_L * 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SQUARE(d.x) + SQUARE(d.z) > SQUARE(distance)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const BOUNDS_16 *const bounds = Item_GetBoundsAccurate(item);
|
||||
return d.y >= bounds->min_y && d.y <= bounds->max_y + 100;
|
||||
}
|
||||
|
||||
int32_t Item_GetDistance(const ITEM *const item, const XYZ_32 *const target)
|
||||
{
|
||||
return XYZ_32_GetDistance(&item->pos, target);
|
||||
}
|
||||
|
||||
ITEM *Item_Get(const int16_t item_num)
|
||||
{
|
||||
return &g_Items[item_num];
|
||||
}
|
37
src/tr2/game/items.h
Normal file
37
src/tr2/game/items.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Item_InitialiseArray(int32_t num_items);
|
||||
int32_t Item_GetTotalCount(void);
|
||||
int16_t __cdecl Item_Create(void);
|
||||
void __cdecl Item_Kill(int16_t item_num);
|
||||
void __cdecl Item_Initialise(int16_t item_num);
|
||||
void __cdecl Item_RemoveActive(int16_t item_num);
|
||||
void __cdecl Item_RemoveDrawn(int16_t item_num);
|
||||
void __cdecl Item_AddActive(int16_t item_num);
|
||||
void __cdecl Item_NewRoom(int16_t item_num, int16_t room_num);
|
||||
int32_t __cdecl Item_GlobalReplace(
|
||||
GAME_OBJECT_ID src_object_id, GAME_OBJECT_ID dst_object_id);
|
||||
void __cdecl Item_ClearKilled(void);
|
||||
void __cdecl Item_ShiftCol(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Item_UpdateRoom(ITEM *item, int32_t height);
|
||||
int32_t __cdecl Item_TestBoundsCollide(
|
||||
const ITEM *src_item, const ITEM *dst_item, int32_t radius);
|
||||
int32_t __cdecl Item_TestPosition(
|
||||
const int16_t *bounds, const ITEM *src_item, const ITEM *dst_item);
|
||||
void __cdecl Item_AlignPosition(
|
||||
const XYZ_32 *vec, const ITEM *src_item, ITEM *dst_item);
|
||||
void __cdecl Item_Animate(ITEM *item);
|
||||
int32_t __cdecl Item_GetAnimChange(ITEM *item, const ANIM *anim);
|
||||
void __cdecl Item_Translate(ITEM *item, int32_t x, int32_t y, int32_t z);
|
||||
int32_t __cdecl Item_IsTriggerActive(ITEM *item);
|
||||
int32_t __cdecl Item_GetFrames(
|
||||
const ITEM *item, FRAME_INFO *frmptr[], int32_t *rate);
|
||||
BOUNDS_16 *__cdecl Item_GetBoundsAccurate(const ITEM *item);
|
||||
FRAME_INFO *__cdecl Item_GetBestFrame(const ITEM *item);
|
||||
bool __cdecl Item_IsNearItem(
|
||||
const ITEM *item, const XYZ_32 *pos, int32_t distance);
|
||||
|
||||
bool Item_IsSmashable(const ITEM *item);
|
||||
int32_t Item_GetDistance(const ITEM *item, const XYZ_32 *target);
|
437
src/tr2/game/lara/cheat.c
Normal file
437
src/tr2/game/lara/cheat.c
Normal file
|
@ -0,0 +1,437 @@
|
|||
#include "game/lara/cheat.h"
|
||||
|
||||
#include "decomp/effects.h"
|
||||
#include "game/camera.h"
|
||||
#include "game/console/common.h"
|
||||
#include "game/creature.h"
|
||||
#include "game/game_string.h"
|
||||
#include "game/gun/gun.h"
|
||||
#include "game/inventory/backpack.h"
|
||||
#include "game/items.h"
|
||||
#include "game/lara/control.h"
|
||||
#include "game/math.h"
|
||||
#include "game/objects/common.h"
|
||||
#include "game/objects/vars.h"
|
||||
#include "game/output.h"
|
||||
#include "game/room.h"
|
||||
#include "game/sound.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/utils.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
#include <libtrx/vector.h>
|
||||
|
||||
static void M_GiveAllGunsImpl(void);
|
||||
static void M_GiveAllMedpacksImpl(void);
|
||||
static void M_GiveAllKeysImpl(void);
|
||||
static void M_ReinitialiseGunMeshes(void);
|
||||
static void M_ResetGunStatus(void);
|
||||
|
||||
static void M_ReinitialiseGunMeshes(void)
|
||||
{
|
||||
// TODO: consider refactoring flare check once more is known about overall
|
||||
// flare control.
|
||||
const bool has_flare = g_Lara.mesh_ptrs[LM_HAND_L]
|
||||
== g_Meshes[g_Objects[O_LARA_FLARE].mesh_idx + LM_HAND_L];
|
||||
|
||||
Lara_InitialiseMeshes(g_CurrentLevel);
|
||||
Gun_InitialiseNewWeapon();
|
||||
if (has_flare) {
|
||||
Flare_DrawMeshes();
|
||||
}
|
||||
}
|
||||
|
||||
static void M_GiveAllGunsImpl(void)
|
||||
{
|
||||
Inv_AddItem(O_PISTOL_ITEM);
|
||||
Inv_AddItem(O_MAGNUM_ITEM);
|
||||
Inv_AddItem(O_UZI_ITEM);
|
||||
Inv_AddItem(O_SHOTGUN_ITEM);
|
||||
Inv_AddItem(O_HARPOON_ITEM);
|
||||
Inv_AddItem(O_M16_ITEM);
|
||||
Inv_AddItem(O_GRENADE_ITEM);
|
||||
g_Lara.magnum_ammo.ammo = 1000;
|
||||
g_Lara.uzi_ammo.ammo = 2000;
|
||||
g_Lara.shotgun_ammo.ammo = 300;
|
||||
g_Lara.harpoon_ammo.ammo = 300;
|
||||
g_Lara.m16_ammo.ammo = 300;
|
||||
g_Lara.grenade_ammo.ammo = 300;
|
||||
}
|
||||
|
||||
static void M_GiveAllMedpacksImpl(void)
|
||||
{
|
||||
Inv_AddItemNTimes(O_FLARES_ITEM, 10);
|
||||
Inv_AddItemNTimes(O_SMALL_MEDIPACK_ITEM, 10);
|
||||
Inv_AddItemNTimes(O_LARGE_MEDIPACK_ITEM, 10);
|
||||
}
|
||||
|
||||
static void M_GiveAllKeysImpl(void)
|
||||
{
|
||||
Inv_AddItem(O_PUZZLE_ITEM_1);
|
||||
Inv_AddItem(O_PUZZLE_ITEM_2);
|
||||
Inv_AddItem(O_PUZZLE_ITEM_3);
|
||||
Inv_AddItem(O_PUZZLE_ITEM_4);
|
||||
Inv_AddItem(O_KEY_ITEM_1);
|
||||
Inv_AddItem(O_KEY_ITEM_2);
|
||||
Inv_AddItem(O_KEY_ITEM_3);
|
||||
Inv_AddItem(O_KEY_ITEM_4);
|
||||
Inv_AddItem(O_PICKUP_ITEM_1);
|
||||
Inv_AddItem(O_PICKUP_ITEM_2);
|
||||
}
|
||||
|
||||
static void M_ResetGunStatus(void)
|
||||
{
|
||||
const bool has_flare = g_Lara.mesh_ptrs[LM_HAND_L]
|
||||
== g_Meshes[g_Objects[O_LARA_FLARE].mesh_idx + LM_HAND_L];
|
||||
if (has_flare) {
|
||||
g_Lara.gun_type = LGT_FLARE;
|
||||
return;
|
||||
}
|
||||
|
||||
g_Lara.gun_status = LGS_ARMLESS;
|
||||
g_Lara.gun_type = LGT_UNARMED;
|
||||
g_Lara.request_gun_type = LGT_UNARMED;
|
||||
g_Lara.weapon_item = NO_ITEM;
|
||||
g_Lara.gun_status = LGS_ARMLESS;
|
||||
g_Lara.left_arm.frame_num = 0;
|
||||
g_Lara.left_arm.lock = 0;
|
||||
g_Lara.right_arm.frame_num = 0;
|
||||
g_Lara.right_arm.lock = 0;
|
||||
g_Lara.left_arm.anim_num = g_LaraItem->anim_num;
|
||||
g_Lara.right_arm.anim_num = g_LaraItem->anim_num;
|
||||
g_Lara.left_arm.frame_base = g_Anims[g_LaraItem->anim_num].frame_ptr;
|
||||
g_Lara.right_arm.frame_base = g_Anims[g_LaraItem->anim_num].frame_ptr;
|
||||
}
|
||||
|
||||
void __cdecl Lara_Cheat_EndLevel(void)
|
||||
{
|
||||
g_LevelComplete = true;
|
||||
Console_Log(GS(OSD_COMPLETE_LEVEL));
|
||||
}
|
||||
|
||||
bool Lara_Cheat_EnterFlyMode(void)
|
||||
{
|
||||
if (g_LaraItem == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_Lara.extra_anim) {
|
||||
M_ResetGunStatus();
|
||||
}
|
||||
|
||||
Lara_GetOffVehicle();
|
||||
|
||||
if (g_Lara.water_status != LWS_UNDERWATER || g_LaraItem->hit_points <= 0) {
|
||||
g_LaraItem->pos.y -= STEP_L;
|
||||
g_LaraItem->current_anim_state = LS_SWIM;
|
||||
g_LaraItem->goal_anim_state = LS_SWIM;
|
||||
g_LaraItem->anim_num =
|
||||
g_Objects[O_LARA].anim_idx + LA_UNDERWATER_SWIM_FORWARD_DRIFT;
|
||||
g_LaraItem->frame_num = g_Anims[g_LaraItem->anim_num].frame_base;
|
||||
g_LaraItem->gravity = 0;
|
||||
g_LaraItem->rot.x = 30 * PHD_DEGREE;
|
||||
g_LaraItem->fall_speed = 30;
|
||||
g_Lara.head_x_rot = 0;
|
||||
g_Lara.head_y_rot = 0;
|
||||
g_Lara.torso_x_rot = 0;
|
||||
g_Lara.torso_y_rot = 0;
|
||||
}
|
||||
|
||||
g_Lara.water_status = LWS_CHEAT;
|
||||
g_Lara.spaz_effect_count = 0;
|
||||
g_Lara.spaz_effect = NULL;
|
||||
g_Lara.hit_frame = 0;
|
||||
g_Lara.hit_direction = -1;
|
||||
g_Lara.air = LARA_MAX_AIR;
|
||||
g_Lara.death_timer = 0;
|
||||
g_Lara.mesh_effects = 0;
|
||||
g_Lara.burn = 0;
|
||||
g_Lara.extra_anim = 0;
|
||||
|
||||
M_ReinitialiseGunMeshes();
|
||||
g_Camera.type = CAM_CHASE;
|
||||
Output_AlterFOV(GAME_FOV * PHD_DEGREE);
|
||||
|
||||
Console_Log(GS(OSD_FLY_MODE_ON));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lara_Cheat_ExitFlyMode(void)
|
||||
{
|
||||
if (g_LaraItem == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ROOM *const room = &g_Rooms[g_LaraItem->room_num];
|
||||
const bool room_submerged = (room->flags & RF_UNDERWATER) != 0;
|
||||
const int16_t water_height = Room_GetWaterHeight(
|
||||
g_LaraItem->pos.x, g_LaraItem->pos.y, g_LaraItem->pos.z,
|
||||
g_LaraItem->room_num);
|
||||
|
||||
if (room_submerged || (water_height != NO_HEIGHT && water_height > 0)) {
|
||||
g_Lara.water_status = LWS_UNDERWATER;
|
||||
} else {
|
||||
g_Lara.water_status = LWS_ABOVE_WATER;
|
||||
g_LaraItem->anim_num = g_Objects[O_LARA].anim_idx + LA_STAND_STILL;
|
||||
g_LaraItem->frame_num = g_Anims[g_LaraItem->anim_num].frame_base;
|
||||
g_LaraItem->rot.x = 0;
|
||||
g_LaraItem->rot.z = 0;
|
||||
g_Lara.head_x_rot = 0;
|
||||
g_Lara.head_y_rot = 0;
|
||||
g_Lara.torso_x_rot = 0;
|
||||
g_Lara.torso_y_rot = 0;
|
||||
}
|
||||
|
||||
if (g_Lara.weapon_item != NO_ITEM) {
|
||||
g_Lara.gun_status = LGS_UNDRAW;
|
||||
} else {
|
||||
g_Lara.gun_status = LGS_ARMLESS;
|
||||
M_ReinitialiseGunMeshes();
|
||||
}
|
||||
|
||||
Console_Log(GS(OSD_FLY_MODE_OFF));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lara_Cheat_OpenNearestDoor(void)
|
||||
{
|
||||
if (g_LaraItem == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t opened = 0;
|
||||
int32_t closed = 0;
|
||||
|
||||
const int32_t shift = 8; // constant shift to avoid overflow errors
|
||||
const int32_t max_dist = SQUARE((WALL_L * 2) >> shift);
|
||||
for (int item_num = 0; item_num < g_LevelItemCount; item_num++) {
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
if (!Object_IsObjectType(item->object_id, g_DoorObjects)
|
||||
&& !Object_IsObjectType(item->object_id, g_TrapdoorObjects)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32_t dx = (item->pos.x - g_LaraItem->pos.x) >> shift;
|
||||
const int32_t dy = (item->pos.y - g_LaraItem->pos.y) >> shift;
|
||||
const int32_t dz = (item->pos.z - g_LaraItem->pos.z) >> shift;
|
||||
const int32_t dist = SQUARE(dx) + SQUARE(dy) + SQUARE(dz);
|
||||
if (dist > max_dist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!item->active) {
|
||||
Item_AddActive(item_num);
|
||||
item->flags |= IF_CODE_BITS;
|
||||
opened++;
|
||||
} else if (item->flags & IF_CODE_BITS) {
|
||||
item->flags &= ~IF_CODE_BITS;
|
||||
closed++;
|
||||
} else {
|
||||
item->flags |= IF_CODE_BITS;
|
||||
opened++;
|
||||
}
|
||||
item->timer = 0;
|
||||
item->touch_bits = 0;
|
||||
}
|
||||
|
||||
if (opened > 0 || closed > 0) {
|
||||
Console_Log(opened > 0 ? GS(OSD_DOOR_OPEN) : GS(OSD_DOOR_CLOSE));
|
||||
return true;
|
||||
}
|
||||
Console_Log(GS(OSD_DOOR_OPEN_FAIL));
|
||||
return false;
|
||||
}
|
||||
|
||||
void __cdecl Lara_Cheat_GetStuff(void)
|
||||
{
|
||||
M_GiveAllGunsImpl();
|
||||
M_GiveAllMedpacksImpl();
|
||||
}
|
||||
|
||||
bool Lara_Cheat_GiveAllKeys(void)
|
||||
{
|
||||
if (g_LaraItem == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
M_GiveAllKeysImpl();
|
||||
|
||||
Sound_Effect(SFX_LARA_KEY, NULL, SPM_ALWAYS);
|
||||
Console_Log(GS(OSD_GIVE_ITEM_ALL_KEYS));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lara_Cheat_GiveAllGuns(void)
|
||||
{
|
||||
if (g_LaraItem == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
M_GiveAllGunsImpl();
|
||||
|
||||
Sound_Effect(SFX_LARA_RELOAD, NULL, SPM_ALWAYS);
|
||||
Console_Log(GS(OSD_GIVE_ITEM_ALL_GUNS));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lara_Cheat_GiveAllItems(void)
|
||||
{
|
||||
if (g_LaraItem == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
M_GiveAllGunsImpl();
|
||||
M_GiveAllKeysImpl();
|
||||
M_GiveAllMedpacksImpl();
|
||||
|
||||
Sound_Effect(SFX_LARA_HOLSTER, &g_LaraItem->pos, SPM_NORMAL);
|
||||
Console_Log(GS(OSD_GIVE_ITEM_CHEAT));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lara_Cheat_Teleport(int32_t x, int32_t y, int32_t z)
|
||||
{
|
||||
int16_t room_num = Room_GetIndexFromPos(x, y, z);
|
||||
if (room_num == NO_ROOM) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const SECTOR *sector = Room_GetSector(x, y, z, &room_num);
|
||||
int16_t height = Room_GetHeight(sector, x, y, z);
|
||||
|
||||
if (height == NO_HEIGHT) {
|
||||
// Sample a sphere of points around target x, y, z
|
||||
// and teleport to the first available location.
|
||||
VECTOR *const points = Vector_Create(sizeof(XYZ_32));
|
||||
|
||||
const int32_t radius = 10;
|
||||
const int32_t unit = STEP_L;
|
||||
for (int32_t dx = -radius; dx <= radius; dx++) {
|
||||
for (int32_t dz = -radius; dz <= radius; dz++) {
|
||||
if (SQUARE(dx) + SQUARE(dz) > SQUARE(radius)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const XYZ_32 point = {
|
||||
.x = ROUND_TO_SECTOR(x + dx * unit) + WALL_L / 2,
|
||||
.y = y,
|
||||
.z = ROUND_TO_SECTOR(z + dz * unit) + WALL_L / 2,
|
||||
};
|
||||
room_num = Room_GetIndexFromPos(point.x, point.y, point.z);
|
||||
if (room_num == NO_ROOM) {
|
||||
continue;
|
||||
}
|
||||
sector = Room_GetSector(point.x, point.y, point.z, &room_num);
|
||||
height = Room_GetHeight(sector, point.x, point.y, point.z);
|
||||
if (height == NO_HEIGHT) {
|
||||
continue;
|
||||
}
|
||||
Vector_Add(points, (void *)&point);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t best_distance = INT32_MAX;
|
||||
for (int32_t i = 0; i < points->count; i++) {
|
||||
const XYZ_32 *const point = (const XYZ_32 *)Vector_Get(points, i);
|
||||
const int32_t distance =
|
||||
XYZ_32_GetDistance(point, &g_LaraItem->pos);
|
||||
if (distance < best_distance) {
|
||||
best_distance = distance;
|
||||
x = point->x;
|
||||
y = point->y;
|
||||
z = point->z;
|
||||
}
|
||||
}
|
||||
|
||||
Vector_Free(points);
|
||||
if (best_distance == INT32_MAX) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
room_num = Room_GetIndexFromPos(x, y, z);
|
||||
if (room_num == NO_ROOM) {
|
||||
return false;
|
||||
}
|
||||
sector = Room_GetSector(x, y, z, &room_num);
|
||||
height = Room_GetHeight(sector, x, y, z);
|
||||
if (height == NO_HEIGHT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
g_LaraItem->pos.x = x;
|
||||
g_LaraItem->pos.y = y;
|
||||
g_LaraItem->pos.z = z;
|
||||
g_LaraItem->floor = height;
|
||||
|
||||
if (g_LaraItem->room_num != room_num) {
|
||||
const int16_t item_num = g_LaraItem - g_Items;
|
||||
Item_NewRoom(item_num, room_num);
|
||||
}
|
||||
|
||||
if (g_Lara.gun_status == LGS_HANDS_BUSY) {
|
||||
g_Lara.gun_status = LGS_ARMLESS;
|
||||
}
|
||||
|
||||
Lara_GetOffVehicle();
|
||||
|
||||
if (g_Lara.extra_anim) {
|
||||
const ROOM *const room = &g_Rooms[g_LaraItem->room_num];
|
||||
const bool room_submerged = (room->flags & RF_UNDERWATER) != 0;
|
||||
const int16_t water_height = Room_GetWaterHeight(
|
||||
g_LaraItem->pos.x, g_LaraItem->pos.y, g_LaraItem->pos.z,
|
||||
g_LaraItem->room_num);
|
||||
|
||||
if (room_submerged || (water_height != NO_HEIGHT && water_height > 0)) {
|
||||
g_Lara.water_status = LWS_UNDERWATER;
|
||||
g_LaraItem->current_anim_state = LS_SWIM;
|
||||
g_LaraItem->goal_anim_state = LS_SWIM;
|
||||
g_LaraItem->anim_num =
|
||||
g_Objects[O_LARA].anim_idx + LA_UNDERWATER_SWIM_FORWARD_DRIFT;
|
||||
g_LaraItem->frame_num = g_Anims[g_LaraItem->anim_num].frame_base;
|
||||
} else {
|
||||
g_Lara.water_status = LWS_ABOVE_WATER;
|
||||
g_LaraItem->current_anim_state = LS_STOP;
|
||||
g_LaraItem->goal_anim_state = LS_STOP;
|
||||
g_LaraItem->anim_num = g_Objects[O_LARA].anim_idx + LA_STAND_STILL;
|
||||
g_LaraItem->frame_num = g_Anims[g_LaraItem->anim_num].frame_base;
|
||||
g_LaraItem->rot.x = 0;
|
||||
g_LaraItem->rot.z = 0;
|
||||
g_Lara.head_x_rot = 0;
|
||||
g_Lara.head_y_rot = 0;
|
||||
g_Lara.torso_x_rot = 0;
|
||||
g_Lara.torso_y_rot = 0;
|
||||
}
|
||||
|
||||
g_Lara.extra_anim = 0;
|
||||
M_ResetGunStatus();
|
||||
M_ReinitialiseGunMeshes();
|
||||
}
|
||||
|
||||
g_Lara.spaz_effect_count = 0;
|
||||
g_Lara.spaz_effect = NULL;
|
||||
g_Lara.hit_frame = 0;
|
||||
g_Lara.hit_direction = -1;
|
||||
g_Lara.air = LARA_MAX_AIR;
|
||||
g_Lara.death_timer = 0;
|
||||
g_Lara.mesh_effects = 0;
|
||||
|
||||
g_Camera.type = CAM_CHASE;
|
||||
Output_AlterFOV(GAME_FOV * PHD_DEGREE);
|
||||
|
||||
Camera_ResetPosition();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Lara_Cheat_KillEnemy(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
if ((item->hit_points == DONT_TARGET && item->object_id != O_WINSTON)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Sound_Effect(SFX_EXPLOSION1, &item->pos, SPM_NORMAL);
|
||||
Creature_Die(item_num, true);
|
||||
return true;
|
||||
}
|
15
src/tr2/game/lara/cheat.h
Normal file
15
src/tr2/game/lara/cheat.h
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void __cdecl Lara_Cheat_EndLevel(void);
|
||||
bool Lara_Cheat_EnterFlyMode(void);
|
||||
bool Lara_Cheat_ExitFlyMode(void);
|
||||
void __cdecl Lara_Cheat_GetStuff(void);
|
||||
bool Lara_Cheat_GiveAllKeys(void);
|
||||
bool Lara_Cheat_GiveAllGuns(void);
|
||||
bool Lara_Cheat_GiveAllItems(void);
|
||||
bool Lara_Cheat_OpenNearestDoor(void);
|
||||
bool Lara_Cheat_Teleport(int32_t x, int32_t y, int32_t z);
|
||||
bool Lara_Cheat_KillEnemy(int16_t item_num);
|
1295
src/tr2/game/lara/col.c
Normal file
1295
src/tr2/game/lara/col.c
Normal file
File diff suppressed because it is too large
Load diff
57
src/tr2/game/lara/col.h
Normal file
57
src/tr2/game/lara/col.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
// TODO: make static
|
||||
void __cdecl Lara_CollideStop(ITEM *item, const COLL_INFO *coll);
|
||||
bool __cdecl Lara_Fallen(ITEM *item, const COLL_INFO *coll);
|
||||
bool __cdecl Lara_TestWaterClimbOut(ITEM *item, const COLL_INFO *coll);
|
||||
bool __cdecl Lara_TestWaterStepOut(ITEM *item, const COLL_INFO *coll);
|
||||
void __cdecl Lara_SurfaceCollision(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_Col_Walk(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Run(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Stop(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_ForwardJump(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_FastBack(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_TurnRight(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_TurnLeft(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Death(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_FastFall(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Hang(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Reach(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Splat(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Land(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Compress(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Back(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_StepRight(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_StepLeft(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Slide(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_BackJump(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_RightJump(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_LeftJump(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_UpJump(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Fallback(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_HangLeft(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_HangRight(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_SlideBack(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Null(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Roll(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Roll2(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_SwanDive(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_FastDive(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Wade(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Default(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Jumper(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_ClimbLeft(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_ClimbRight(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_ClimbStance(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Climbing(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_ClimbDown(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_SurfSwim(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_SurfBack(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_SurfLeft(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_SurfRight(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_SurfTread(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_Swim(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_Col_UWDeath(ITEM *item, COLL_INFO *coll);
|
13
src/tr2/game/lara/common.c
Normal file
13
src/tr2/game/lara/common.c
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/game/lara/common.h>
|
||||
|
||||
LARA_INFO *Lara_GetLaraInfo(void)
|
||||
{
|
||||
return &g_Lara;
|
||||
}
|
||||
|
||||
ITEM *Lara_GetItem(void)
|
||||
{
|
||||
return g_LaraItem;
|
||||
}
|
1017
src/tr2/game/lara/control.c
Normal file
1017
src/tr2/game/lara/control.c
Normal file
File diff suppressed because it is too large
Load diff
30
src/tr2/game/lara/control.h
Normal file
30
src/tr2/game/lara/control.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Lara_HandleAboveWater(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_HandleSurface(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_HandleUnderwater(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_Control(int16_t item_num);
|
||||
void __cdecl Lara_ControlExtra(int16_t item_num);
|
||||
|
||||
void __cdecl Lara_Animate(ITEM *item);
|
||||
|
||||
void __cdecl Lara_UseItem(GAME_OBJECT_ID object_id);
|
||||
|
||||
void __cdecl Lara_InitialiseLoad(int16_t item_num);
|
||||
|
||||
void __cdecl Lara_Initialise(GAMEFLOW_LEVEL_TYPE type);
|
||||
|
||||
void __cdecl Lara_InitialiseInventory(int32_t level_num);
|
||||
|
||||
void __cdecl Lara_InitialiseMeshes(int32_t level_num);
|
||||
|
||||
void Lara_GetOffVehicle(void);
|
||||
void Lara_SwapSingleMesh(LARA_MESH mesh, GAME_OBJECT_ID);
|
||||
|
||||
int16_t Lara_GetNearestEnemy(void);
|
||||
void Lara_TakeDamage(int16_t damage, bool hit_status);
|
644
src/tr2/game/lara/draw.c
Normal file
644
src/tr2/game/lara/draw.c
Normal file
|
@ -0,0 +1,644 @@
|
|||
#include "game/lara/draw.h"
|
||||
|
||||
#include "game/items.h"
|
||||
#include "game/matrix.h"
|
||||
#include "game/output.h"
|
||||
#include "game/random.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
void __cdecl Lara_Draw(const ITEM *const item)
|
||||
{
|
||||
FRAME_INFO *frame;
|
||||
MATRIX saved_matrix;
|
||||
|
||||
const int32_t top = g_PhdWinTop;
|
||||
const int32_t left = g_PhdWinLeft;
|
||||
const int32_t right = g_PhdWinRight;
|
||||
const int32_t bottom = g_PhdWinBottom;
|
||||
|
||||
g_PhdWinTop = 0;
|
||||
g_PhdWinLeft = 0;
|
||||
g_PhdWinBottom = g_PhdWinMaxY;
|
||||
g_PhdWinRight = g_PhdWinMaxX;
|
||||
|
||||
FRAME_INFO *frames[2];
|
||||
if (g_Lara.hit_direction < 0) {
|
||||
int32_t rate;
|
||||
const int32_t frac = Item_GetFrames(item, frames, &rate);
|
||||
if (frac) {
|
||||
Lara_Draw_I(item, frames[0], frames[1], frac, rate);
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
const OBJECT *const object = &g_Objects[item->object_id];
|
||||
if (g_Lara.hit_direction < 0) {
|
||||
frame = frames[0];
|
||||
} else {
|
||||
// clang-format off
|
||||
LARA_ANIMATION anim;
|
||||
switch (g_Lara.hit_direction) {
|
||||
case DIR_EAST: anim = LA_HIT_LEFT; break;
|
||||
case DIR_SOUTH: anim = LA_HIT_BACK; break;
|
||||
case DIR_WEST: anim = LA_HIT_RIGHT; break;
|
||||
default: anim = LA_HIT_FRONT; break;
|
||||
}
|
||||
// clang-format on
|
||||
frame = (FRAME_INFO *)(g_Anims[anim].frame_ptr
|
||||
+ g_Lara.hit_frame
|
||||
* (g_Anims[anim].interpolation >> 8));
|
||||
}
|
||||
|
||||
if (g_Lara.skidoo == NO_ITEM) {
|
||||
S_PrintShadow(object->shadow_size, &frame->bounds, item);
|
||||
}
|
||||
|
||||
saved_matrix = *g_MatrixPtr;
|
||||
|
||||
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(&frame->bounds);
|
||||
if (!clip) {
|
||||
Matrix_Pop();
|
||||
return;
|
||||
}
|
||||
|
||||
Matrix_Push();
|
||||
Output_CalculateObjectLighting(item, frame);
|
||||
|
||||
const int32_t *bone = &g_AnimBones[object->bone_idx];
|
||||
const int16_t *mesh_rots = frame->mesh_rots;
|
||||
const int16_t *mesh_rots_c;
|
||||
|
||||
Matrix_TranslateRel(frame->offset.x, frame->offset.y, frame->offset.z);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_HIPS], clip);
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_TranslateRel(bone[1], bone[2], bone[3]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_THIGH_L], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[5], bone[6], bone[7]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_CALF_L], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[9], bone[10], bone[11]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_FOOT_L], clip);
|
||||
Matrix_Pop();
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_TranslateRel(bone[13], bone[14], bone[15]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_THIGH_R], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[17], bone[18], bone[19]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_CALF_R], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[21], bone[22], bone[23]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_FOOT_R], clip);
|
||||
Matrix_Pop();
|
||||
|
||||
Matrix_TranslateRel(bone[25], bone[26], bone[27]);
|
||||
if (g_Lara.weapon_item != NO_ITEM && g_Lara.gun_type == LGT_M16
|
||||
&& (g_Items[g_Lara.weapon_item].current_anim_state == 0
|
||||
|| g_Items[g_Lara.weapon_item].current_anim_state == 2
|
||||
|| g_Items[g_Lara.weapon_item].current_anim_state == 4)) {
|
||||
mesh_rots =
|
||||
&g_Lara.right_arm.frame_base
|
||||
[g_Lara.right_arm.frame_num
|
||||
* (g_Anims[g_Lara.right_arm.anim_num].interpolation >> 8)
|
||||
+ FBBOX_ROT];
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 7);
|
||||
} else {
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
}
|
||||
|
||||
Matrix_RotYXZ(g_Lara.torso_y_rot, g_Lara.torso_x_rot, g_Lara.torso_z_rot);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_TORSO], clip);
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_TranslateRel(bone[53], bone[54], bone[55]);
|
||||
mesh_rots_c = mesh_rots;
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 6);
|
||||
mesh_rots = mesh_rots_c;
|
||||
Matrix_RotYXZ(g_Lara.head_y_rot, g_Lara.head_x_rot, g_Lara.head_z_rot);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_HEAD], clip);
|
||||
|
||||
*g_MatrixPtr = saved_matrix;
|
||||
DrawHair();
|
||||
|
||||
Matrix_Pop();
|
||||
|
||||
if (g_Lara.back_gun) {
|
||||
Matrix_Push();
|
||||
const int32_t *bone_c =
|
||||
&g_AnimBones[g_Objects[g_Lara.back_gun].bone_idx];
|
||||
Matrix_TranslateRel(bone_c[53], bone_c[54], bone_c[55]);
|
||||
mesh_rots_c = g_Objects[g_Lara.back_gun].frame_base + FBBOX_ROT;
|
||||
Matrix_RotYXZsuperpack(&mesh_rots_c, 14);
|
||||
Output_InsertPolygons(
|
||||
g_Meshes[g_Objects[g_Lara.back_gun].mesh_idx + LM_HEAD], clip);
|
||||
Matrix_Pop();
|
||||
}
|
||||
|
||||
LARA_GUN_TYPE gun_type = LGT_UNARMED;
|
||||
if (g_Lara.gun_status == LGS_READY || g_Lara.gun_status == LGS_SPECIAL
|
||||
|| g_Lara.gun_status == LGS_DRAW || g_Lara.gun_status == LGS_UNDRAW) {
|
||||
gun_type = g_Lara.gun_type;
|
||||
}
|
||||
|
||||
switch (gun_type) {
|
||||
case LGT_UNARMED:
|
||||
case LGT_FLARE:
|
||||
Matrix_Push();
|
||||
Matrix_TranslateRel(bone[29], bone[30], bone[31]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_UARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[33], bone[34], bone[35]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_LARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[37], bone[38], bone[39]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_HAND_R], clip);
|
||||
Matrix_Pop();
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_TranslateRel(bone[41], bone[42], bone[43]);
|
||||
if (g_Lara.flare_control_left) {
|
||||
mesh_rots =
|
||||
&g_Lara.left_arm.frame_base
|
||||
[(g_Anims[g_Lara.left_arm.anim_num].interpolation >> 8)
|
||||
* (g_Lara.left_arm.frame_num
|
||||
- g_Anims[g_Lara.left_arm.anim_num].frame_base)
|
||||
+ FBBOX_ROT];
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 11);
|
||||
} else {
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
}
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_UARM_L], clip);
|
||||
Matrix_TranslateRel(bone[45], bone[46], bone[47]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_LARM_L], clip);
|
||||
Matrix_TranslateRel(bone[49], bone[50], bone[51]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_HAND_L], clip);
|
||||
if (g_Lara.gun_type == LGT_FLARE && g_Lara.left_arm.flash_gun) {
|
||||
Gun_DrawFlash(LGT_FLARE, clip);
|
||||
}
|
||||
|
||||
Matrix_Pop();
|
||||
break;
|
||||
|
||||
case LGT_PISTOLS:
|
||||
case LGT_MAGNUMS:
|
||||
case LGT_UZIS:
|
||||
Matrix_Push();
|
||||
Matrix_TranslateRel(bone[29], bone[30], bone[31]);
|
||||
|
||||
g_MatrixPtr->_00 = g_MatrixPtr[-2]._00;
|
||||
g_MatrixPtr->_01 = g_MatrixPtr[-2]._01;
|
||||
g_MatrixPtr->_02 = g_MatrixPtr[-2]._02;
|
||||
g_MatrixPtr->_10 = g_MatrixPtr[-2]._10;
|
||||
g_MatrixPtr->_11 = g_MatrixPtr[-2]._11;
|
||||
g_MatrixPtr->_12 = g_MatrixPtr[-2]._12;
|
||||
g_MatrixPtr->_20 = g_MatrixPtr[-2]._20;
|
||||
g_MatrixPtr->_21 = g_MatrixPtr[-2]._21;
|
||||
g_MatrixPtr->_22 = g_MatrixPtr[-2]._22;
|
||||
|
||||
Matrix_RotYXZ(
|
||||
g_Lara.right_arm.rot.y, g_Lara.right_arm.rot.x,
|
||||
g_Lara.right_arm.rot.z);
|
||||
mesh_rots =
|
||||
&g_Lara.right_arm.frame_base
|
||||
[(g_Anims[g_Lara.right_arm.anim_num].interpolation >> 8)
|
||||
* (g_Lara.right_arm.frame_num
|
||||
- g_Anims[g_Lara.right_arm.anim_num].frame_base)
|
||||
+ FBBOX_ROT];
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 8);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_UARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[33], bone[34], bone[35]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_LARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[37], bone[38], bone[39]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_HAND_R], clip);
|
||||
|
||||
if (g_Lara.right_arm.flash_gun) {
|
||||
saved_matrix = *g_MatrixPtr;
|
||||
}
|
||||
Matrix_Pop();
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_TranslateRel(bone[41], bone[42], bone[43]);
|
||||
g_MatrixPtr->_00 = g_MatrixPtr[-2]._00;
|
||||
g_MatrixPtr->_01 = g_MatrixPtr[-2]._01;
|
||||
g_MatrixPtr->_02 = g_MatrixPtr[-2]._02;
|
||||
g_MatrixPtr->_10 = g_MatrixPtr[-2]._10;
|
||||
g_MatrixPtr->_11 = g_MatrixPtr[-2]._11;
|
||||
g_MatrixPtr->_12 = g_MatrixPtr[-2]._12;
|
||||
g_MatrixPtr->_20 = g_MatrixPtr[-2]._20;
|
||||
g_MatrixPtr->_21 = g_MatrixPtr[-2]._21;
|
||||
g_MatrixPtr->_22 = g_MatrixPtr[-2]._22;
|
||||
Matrix_RotYXZ(
|
||||
g_Lara.left_arm.rot.y, g_Lara.left_arm.rot.x,
|
||||
g_Lara.left_arm.rot.z);
|
||||
mesh_rots = &g_Lara.left_arm.frame_base
|
||||
[(g_Anims[g_Lara.left_arm.anim_num].interpolation >> 8)
|
||||
* (g_Lara.left_arm.frame_num
|
||||
- g_Anims[g_Lara.left_arm.anim_num].frame_base)
|
||||
+ FBBOX_ROT];
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 11);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_UARM_L], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[45], bone[46], bone[47]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_LARM_L], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[49], bone[50], bone[51]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_HAND_L], clip);
|
||||
|
||||
if (g_Lara.left_arm.flash_gun) {
|
||||
Gun_DrawFlash(gun_type, clip);
|
||||
}
|
||||
|
||||
if (g_Lara.right_arm.flash_gun) {
|
||||
*g_MatrixPtr = saved_matrix;
|
||||
Gun_DrawFlash(gun_type, clip);
|
||||
}
|
||||
Matrix_Pop();
|
||||
break;
|
||||
|
||||
case LGT_SHOTGUN:
|
||||
case LGT_M16:
|
||||
case LGT_GRENADE:
|
||||
case LGT_HARPOON:
|
||||
Matrix_Push();
|
||||
Matrix_TranslateRel(bone[29], bone[30], bone[31]);
|
||||
mesh_rots =
|
||||
&g_Lara.right_arm.frame_base
|
||||
[g_Lara.right_arm.frame_num
|
||||
* (g_Anims[g_Lara.right_arm.anim_num].interpolation >> 8)
|
||||
+ FBBOX_ROT];
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 8);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_UARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[33], bone[34], bone[35]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_LARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[37], bone[38], bone[39]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_HAND_R], clip);
|
||||
|
||||
if (g_Lara.right_arm.flash_gun) {
|
||||
saved_matrix = *g_MatrixPtr;
|
||||
}
|
||||
Matrix_Pop();
|
||||
|
||||
Matrix_Push();
|
||||
Matrix_TranslateRel(bone[41], bone[42], bone[43]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_UARM_L], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[45], bone[46], bone[47]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_LARM_L], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[49], bone[50], bone[51]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_HAND_L], clip);
|
||||
|
||||
if (g_Lara.right_arm.flash_gun) {
|
||||
*g_MatrixPtr = saved_matrix;
|
||||
Gun_DrawFlash(gun_type, clip);
|
||||
}
|
||||
|
||||
Matrix_Pop();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Matrix_Pop();
|
||||
Matrix_Pop();
|
||||
|
||||
finish:
|
||||
g_PhdWinLeft = left;
|
||||
g_PhdWinRight = right;
|
||||
g_PhdWinTop = top;
|
||||
g_PhdWinBottom = bottom;
|
||||
}
|
||||
|
||||
void __cdecl Lara_Draw_I(
|
||||
const ITEM *const item, const FRAME_INFO *const frame1,
|
||||
const FRAME_INFO *const frame2, const int32_t frac, const int32_t rate)
|
||||
{
|
||||
const OBJECT *const object = &g_Objects[item->object_id];
|
||||
const BOUNDS_16 *const bounds = Item_GetBoundsAccurate(item);
|
||||
|
||||
if (g_Lara.skidoo == NO_ITEM) {
|
||||
S_PrintShadow(object->shadow_size, bounds, item);
|
||||
}
|
||||
|
||||
MATRIX saved_matrix = *g_MatrixPtr;
|
||||
|
||||
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(&frame1->bounds);
|
||||
|
||||
if (!clip) {
|
||||
Matrix_Pop();
|
||||
return;
|
||||
}
|
||||
|
||||
Matrix_Push();
|
||||
Output_CalculateObjectLighting(item, frame1);
|
||||
|
||||
const int32_t *bone = &g_AnimBones[object->bone_idx];
|
||||
const int16_t *mesh_rots_1 = frame1->mesh_rots;
|
||||
const int16_t *mesh_rots_2 = frame2->mesh_rots;
|
||||
const int16_t *mesh_rots_1_c;
|
||||
const int16_t *mesh_rots_2_c;
|
||||
|
||||
Matrix_InitInterpolate(frac, rate);
|
||||
Matrix_TranslateRel_ID(
|
||||
frame1->offset.x, frame1->offset.y, frame1->offset.z, frame2->offset.x,
|
||||
frame2->offset.y, frame2->offset.z);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_HIPS], clip);
|
||||
|
||||
Matrix_Push_I();
|
||||
Matrix_TranslateRel_I(bone[1], bone[2], bone[3]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_THIGH_L], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[5], bone[6], bone[7]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_CALF_L], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[9], bone[10], bone[11]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_FOOT_L], clip);
|
||||
Matrix_Pop_I();
|
||||
|
||||
Matrix_Push_I();
|
||||
Matrix_TranslateRel_I(bone[13], bone[14], bone[15]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_THIGH_R], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[17], bone[18], bone[19]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_CALF_R], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[21], bone[22], bone[23]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_FOOT_R], clip);
|
||||
Matrix_Pop_I();
|
||||
|
||||
Matrix_TranslateRel_I(bone[25], bone[26], bone[27]);
|
||||
if (g_Lara.weapon_item != -1 && g_Lara.gun_type == 5
|
||||
&& ((g_Items[g_Lara.weapon_item].current_anim_state) == 0
|
||||
|| g_Items[g_Lara.weapon_item].current_anim_state == 2
|
||||
|| g_Items[g_Lara.weapon_item].current_anim_state == 4)) {
|
||||
mesh_rots_2 =
|
||||
&g_Lara.right_arm.frame_base
|
||||
[g_Lara.right_arm.frame_num
|
||||
* (g_Anims[g_Lara.right_arm.anim_num].interpolation >> 8)
|
||||
+ FBBOX_ROT];
|
||||
mesh_rots_1 = mesh_rots_2;
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 7);
|
||||
} else {
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
}
|
||||
Matrix_RotYXZ_I(g_Lara.torso_y_rot, g_Lara.torso_x_rot, g_Lara.torso_z_rot);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_TORSO], clip);
|
||||
|
||||
Matrix_Push_I();
|
||||
Matrix_TranslateRel_I(bone[53], bone[54], bone[55]);
|
||||
mesh_rots_1_c = mesh_rots_1;
|
||||
mesh_rots_2_c = mesh_rots_2;
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 6);
|
||||
mesh_rots_1 = mesh_rots_1_c;
|
||||
mesh_rots_2 = mesh_rots_2_c;
|
||||
Matrix_RotYXZ_I(g_Lara.head_y_rot, g_Lara.head_x_rot, g_Lara.head_z_rot);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_HEAD], clip);
|
||||
|
||||
*g_MatrixPtr = saved_matrix;
|
||||
DrawHair();
|
||||
Matrix_Pop_I();
|
||||
|
||||
if (g_Lara.back_gun) {
|
||||
Matrix_Push_I();
|
||||
const int32_t *bone_c =
|
||||
&g_AnimBones[g_Objects[g_Lara.back_gun].bone_idx];
|
||||
Matrix_TranslateRel_I(bone_c[53], bone_c[54], bone_c[55]);
|
||||
mesh_rots_1_c = g_Objects[g_Lara.back_gun].frame_base + FBBOX_ROT;
|
||||
mesh_rots_2_c = g_Objects[g_Lara.back_gun].frame_base + FBBOX_ROT;
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1_c, &mesh_rots_2_c, 14);
|
||||
Output_InsertPolygons_I(
|
||||
g_Meshes[g_Objects[g_Lara.back_gun].mesh_idx + LM_HEAD], clip);
|
||||
Matrix_Pop_I();
|
||||
}
|
||||
|
||||
LARA_GUN_TYPE gun_type = LGT_UNARMED;
|
||||
if (g_Lara.gun_status == LGS_READY || g_Lara.gun_status == LGS_SPECIAL
|
||||
|| g_Lara.gun_status == LGS_DRAW || g_Lara.gun_status == LGS_UNDRAW) {
|
||||
gun_type = g_Lara.gun_type;
|
||||
}
|
||||
|
||||
switch (gun_type) {
|
||||
case LGT_UNARMED:
|
||||
case LGT_FLARE:
|
||||
Matrix_Push_I();
|
||||
Matrix_TranslateRel_I(bone[29], bone[30], bone[31]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_UARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[33], bone[34], bone[35]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_LARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[37], bone[38], bone[39]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_HAND_R], clip);
|
||||
Matrix_Pop_I();
|
||||
|
||||
Matrix_Push_I();
|
||||
Matrix_TranslateRel_I(bone[41], bone[42], bone[43]);
|
||||
if (g_Lara.flare_control_left) {
|
||||
mesh_rots_1 =
|
||||
&g_Lara.left_arm.frame_base
|
||||
[(g_Anims[g_Lara.left_arm.anim_num].interpolation >> 8)
|
||||
* (g_Lara.left_arm.frame_num
|
||||
- g_Anims[g_Lara.left_arm.anim_num].frame_base)
|
||||
+ FBBOX_ROT];
|
||||
|
||||
mesh_rots_2 =
|
||||
&g_Lara.left_arm.frame_base
|
||||
[(g_Anims[g_Lara.left_arm.anim_num].interpolation >> 8)
|
||||
* (g_Lara.left_arm.frame_num
|
||||
- g_Anims[g_Lara.left_arm.anim_num].frame_base)
|
||||
+ FBBOX_ROT];
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 11);
|
||||
} else {
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
}
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_UARM_L], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[45], bone[46], bone[47]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_LARM_L], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[49], bone[50], bone[51]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_HAND_L], clip);
|
||||
|
||||
if (g_Lara.gun_type == LGT_FLARE && g_Lara.left_arm.flash_gun) {
|
||||
Matrix_TranslateRel_I(11, 32, 80);
|
||||
Matrix_RotX_I(-16380);
|
||||
Matrix_RotY_I(2 * Random_GetDraw());
|
||||
S_CalculateStaticLight(2048);
|
||||
Output_InsertPolygons_I(g_Meshes[g_Objects[235].mesh_idx], clip);
|
||||
}
|
||||
Matrix_Pop();
|
||||
break;
|
||||
|
||||
case LGT_PISTOLS:
|
||||
case LGT_MAGNUMS:
|
||||
case LGT_UZIS:
|
||||
Matrix_Push_I();
|
||||
Matrix_TranslateRel_I(bone[29], bone[30], bone[31]);
|
||||
Matrix_InterpolateArm();
|
||||
Matrix_RotYXZ(
|
||||
g_Lara.right_arm.rot.y, g_Lara.right_arm.rot.x,
|
||||
g_Lara.right_arm.rot.z);
|
||||
mesh_rots_1 =
|
||||
&g_Lara.right_arm.frame_base
|
||||
[(g_Anims[g_Lara.right_arm.anim_num].interpolation >> 8)
|
||||
* (g_Lara.right_arm.frame_num
|
||||
- g_Anims[g_Lara.right_arm.anim_num].frame_base)
|
||||
+ FBBOX_ROT];
|
||||
Matrix_RotYXZsuperpack(&mesh_rots_1, 8);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_UARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[33], bone[34], bone[35]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots_1, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_LARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[37], bone[38], bone[39]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots_1, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_HAND_R], clip);
|
||||
|
||||
if (g_Lara.right_arm.flash_gun) {
|
||||
saved_matrix = *g_MatrixPtr;
|
||||
}
|
||||
Matrix_Pop_I();
|
||||
|
||||
Matrix_Push_I();
|
||||
Matrix_TranslateRel_I(bone[41], bone[42], bone[43]);
|
||||
Matrix_InterpolateArm();
|
||||
Matrix_RotYXZ(
|
||||
g_Lara.left_arm.rot.y, g_Lara.left_arm.rot.x,
|
||||
g_Lara.left_arm.rot.z);
|
||||
mesh_rots_1 =
|
||||
&g_Lara.left_arm.frame_base
|
||||
[(g_Anims[g_Lara.left_arm.anim_num].interpolation >> 8)
|
||||
* (g_Lara.left_arm.frame_num
|
||||
- g_Anims[g_Lara.left_arm.anim_num].frame_base)
|
||||
+ FBBOX_ROT];
|
||||
Matrix_RotYXZsuperpack(&mesh_rots_1, 11);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_UARM_L], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[45], bone[46], bone[47]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots_1, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_LARM_L], clip);
|
||||
|
||||
Matrix_TranslateRel(bone[49], bone[50], bone[51]);
|
||||
Matrix_RotYXZsuperpack(&mesh_rots_1, 0);
|
||||
Output_InsertPolygons(g_Lara.mesh_ptrs[LM_HAND_L], clip);
|
||||
|
||||
if (g_Lara.left_arm.flash_gun) {
|
||||
Gun_DrawFlash((int32_t)gun_type, clip);
|
||||
}
|
||||
|
||||
if (g_Lara.right_arm.flash_gun) {
|
||||
*g_MatrixPtr = saved_matrix;
|
||||
Gun_DrawFlash(gun_type, clip);
|
||||
}
|
||||
Matrix_Pop();
|
||||
break;
|
||||
|
||||
case LGT_SHOTGUN:
|
||||
case LGT_M16:
|
||||
case LGT_GRENADE:
|
||||
case LGT_HARPOON:
|
||||
Matrix_Push_I();
|
||||
Matrix_TranslateRel_I(bone[29], bone[30], bone[31]);
|
||||
mesh_rots_1 =
|
||||
&g_Lara.right_arm.frame_base
|
||||
[g_Lara.right_arm.frame_num
|
||||
* (g_Anims[g_Lara.right_arm.anim_num].interpolation >> 8)
|
||||
+ FBBOX_ROT];
|
||||
mesh_rots_2 =
|
||||
&g_Lara.right_arm.frame_base
|
||||
[g_Lara.right_arm.frame_num
|
||||
* (g_Anims[g_Lara.right_arm.anim_num].interpolation >> 8)
|
||||
+ FBBOX_ROT];
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 8);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_UARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[33], bone[34], bone[35]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_LARM_R], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[37], bone[38], bone[39]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_HAND_R], clip);
|
||||
|
||||
if (g_Lara.right_arm.flash_gun) {
|
||||
saved_matrix = *g_MatrixPtr;
|
||||
}
|
||||
Matrix_Pop_I();
|
||||
|
||||
Matrix_Push_I();
|
||||
Matrix_TranslateRel_I(bone[41], bone[42], bone[43]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_UARM_L], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[45], bone[46], bone[47]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_LARM_L], clip);
|
||||
|
||||
Matrix_TranslateRel_I(bone[49], bone[50], bone[51]);
|
||||
Matrix_RotYXZsuperpack_I(&mesh_rots_1, &mesh_rots_2, 0);
|
||||
Output_InsertPolygons_I(g_Lara.mesh_ptrs[LM_HAND_L], clip);
|
||||
|
||||
if (g_Lara.right_arm.flash_gun) {
|
||||
*g_MatrixPtr = saved_matrix;
|
||||
Gun_DrawFlash(gun_type, clip);
|
||||
}
|
||||
|
||||
Matrix_Pop();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
Matrix_Pop();
|
||||
Matrix_Pop();
|
||||
}
|
8
src/tr2/game/lara/draw.h
Normal file
8
src/tr2/game/lara/draw.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Lara_Draw(const ITEM *item);
|
||||
void __cdecl Lara_Draw_I(
|
||||
const ITEM *item, const FRAME_INFO *frame1, const FRAME_INFO *frame2,
|
||||
int32_t frac, int32_t rate);
|
67
src/tr2/game/lara/look.c
Normal file
67
src/tr2/game/lara/look.c
Normal file
|
@ -0,0 +1,67 @@
|
|||
#include "game/lara/look.h"
|
||||
|
||||
#include "game/input.h"
|
||||
#include "global/const.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
void __cdecl Lara_LookUpDown(void)
|
||||
{
|
||||
g_Camera.type = CAM_LOOK;
|
||||
|
||||
if (g_Input & IN_FORWARD) {
|
||||
g_Input &= ~IN_FORWARD;
|
||||
if (g_Lara.head_x_rot > MIN_HEAD_TILT) {
|
||||
g_Lara.head_x_rot -= HEAD_TURN;
|
||||
}
|
||||
} else if (g_Input & IN_BACK) {
|
||||
g_Input &= ~IN_BACK;
|
||||
if (g_Lara.head_x_rot < MAX_HEAD_TILT) {
|
||||
g_Lara.head_x_rot += HEAD_TURN;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_Lara.gun_status != LGS_HANDS_BUSY) {
|
||||
g_Lara.torso_x_rot = g_Lara.head_x_rot;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Lara_LookLeftRight(void)
|
||||
{
|
||||
g_Camera.type = CAM_LOOK;
|
||||
|
||||
if (g_Input & IN_LEFT) {
|
||||
g_Input &= ~IN_LEFT;
|
||||
if (g_Lara.head_y_rot > MIN_HEAD_ROTATION)
|
||||
g_Lara.head_y_rot -= HEAD_TURN;
|
||||
} else if (g_Input & IN_RIGHT) {
|
||||
g_Input &= ~IN_RIGHT;
|
||||
if (g_Lara.head_y_rot < MAX_HEAD_ROTATION)
|
||||
g_Lara.head_y_rot += HEAD_TURN;
|
||||
}
|
||||
|
||||
if (g_Lara.gun_status != LGS_HANDS_BUSY && g_Lara.skidoo == NO_ITEM) {
|
||||
g_Lara.torso_y_rot = g_Lara.head_y_rot;
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl Lara_ResetLook(void)
|
||||
{
|
||||
if (g_Camera.type == CAM_LOOK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_Lara.head_x_rot <= -HEAD_TURN || g_Lara.head_x_rot >= HEAD_TURN) {
|
||||
g_Lara.head_x_rot -= g_Lara.head_x_rot / 8;
|
||||
} else {
|
||||
g_Lara.head_x_rot = 0;
|
||||
}
|
||||
|
||||
if (g_Lara.head_y_rot <= -HEAD_TURN || g_Lara.head_y_rot >= HEAD_TURN) {
|
||||
g_Lara.head_y_rot += g_Lara.head_y_rot / -8;
|
||||
} else {
|
||||
g_Lara.head_y_rot = 0;
|
||||
}
|
||||
|
||||
g_Lara.torso_x_rot = g_Lara.head_x_rot;
|
||||
g_Lara.torso_y_rot = g_Lara.head_y_rot;
|
||||
}
|
5
src/tr2/game/lara/look.h
Normal file
5
src/tr2/game/lara/look.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
void __cdecl Lara_LookUpDown(void);
|
||||
void __cdecl Lara_LookLeftRight(void);
|
||||
void __cdecl Lara_ResetLook(void);
|
1870
src/tr2/game/lara/misc.c
Normal file
1870
src/tr2/game/lara/misc.c
Normal file
File diff suppressed because it is too large
Load diff
84
src/tr2/game/lara/misc.h
Normal file
84
src/tr2/game/lara/misc.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
void __cdecl Lara_GetCollisionInfo(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_SlideSlope(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
int32_t __cdecl Lara_HitCeiling(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
int32_t __cdecl Lara_DeflectEdge(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_DeflectEdgeJump(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_SlideEdgeJump(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
int32_t __cdecl Lara_TestWall(
|
||||
ITEM *item, int32_t front, int32_t right, int32_t down);
|
||||
|
||||
int32_t __cdecl Lara_TestHangOnClimbWall(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
int32_t __cdecl Lara_TestClimbStance(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_HangTest(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
int32_t __cdecl Lara_TestEdgeCatch(ITEM *item, COLL_INFO *coll, int32_t *edge);
|
||||
|
||||
int32_t __cdecl Lara_TestHangJumpUp(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
int32_t __cdecl Lara_TestHangJump(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
int32_t __cdecl Lara_TestHangSwingIn(ITEM *item, PHD_ANGLE angle);
|
||||
|
||||
int32_t __cdecl Lara_TestVault(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
int32_t __cdecl Lara_TestSlide(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
int16_t __cdecl Lara_FloorFront(ITEM *item, int16_t ang, int32_t dist);
|
||||
|
||||
int32_t __cdecl Lara_LandedBad(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
int32_t __cdecl Lara_CheckForLetGo(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_GetJointAbsPosition(XYZ_32 *vec, int32_t joint);
|
||||
|
||||
void __cdecl Lara_GetJointAbsPosition_I(
|
||||
ITEM *item, XYZ_32 *vec, FRAME_INFO *frame1, FRAME_INFO *frame2,
|
||||
int32_t frac, int32_t rate);
|
||||
|
||||
void __cdecl Lara_BaddieCollision(ITEM *lara_item, COLL_INFO *coll);
|
||||
void __cdecl Lara_TakeHit(ITEM *lara_item, const COLL_INFO *coll);
|
||||
void __cdecl Lara_Push(
|
||||
const ITEM *item, ITEM *lara_item, COLL_INFO *coll, bool spaz_on,
|
||||
bool big_push);
|
||||
int32_t __cdecl Lara_MovePosition(XYZ_32 *vec, ITEM *item, ITEM *lara_item);
|
||||
int32_t __cdecl Lara_IsNearItem(const XYZ_32 *pos, int32_t distance);
|
||||
|
||||
int32_t __cdecl Lara_TestClimb(
|
||||
int32_t x, int32_t y, int32_t z, int32_t x_front, int32_t z_front,
|
||||
int32_t item_height, int16_t item_room, int32_t *shift);
|
||||
|
||||
int32_t __cdecl Lara_TestClimbPos(
|
||||
const ITEM *item, int32_t front, int32_t right, int32_t origin,
|
||||
int32_t height, int32_t *shift);
|
||||
|
||||
void __cdecl Lara_DoClimbLeftRight(
|
||||
ITEM *item, const COLL_INFO *coll, int32_t result, int32_t shift);
|
||||
|
||||
int32_t __cdecl Lara_TestClimbUpPos(
|
||||
const ITEM *item, int32_t front, int32_t right, int32_t *shift,
|
||||
int32_t *ledge);
|
||||
|
||||
int32_t __cdecl Lara_GetWaterDepth(
|
||||
int32_t x, int32_t y, int32_t z, int16_t room_num);
|
||||
|
||||
void __cdecl Lara_TestWaterDepth(ITEM *item, const COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_SwimCollision(ITEM *item, COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_WaterCurrent(COLL_INFO *coll);
|
||||
|
||||
void __cdecl Lara_CatchFire(void);
|
||||
|
||||
void __cdecl Lara_TouchLava(ITEM *item);
|
1133
src/tr2/game/lara/state.c
Normal file
1133
src/tr2/game/lara/state.c
Normal file
File diff suppressed because it is too large
Load diff
75
src/tr2/game/lara/state.h
Normal file
75
src/tr2/game/lara/state.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
// Lara state routines.
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
// TODO: make static
|
||||
void __cdecl Lara_SwimTurn(ITEM *item);
|
||||
|
||||
void __cdecl Lara_State_Walk(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Run(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Stop(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_ForwardJump(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_FastBack(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_TurnRight(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_TurnLeft(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Death(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_FastFall(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Hang(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Reach(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Splat(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Compress(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Back(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Null(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_FastTurn(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_StepRight(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_StepLeft(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Slide(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_BackJump(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_RightJump(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_LeftJump(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_UpJump(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Fallback(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_HangLeft(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_HangRight(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_SlideBack(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_PushBlock(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_PPReady(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Pickup(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_PickupFlare(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_SwitchOn(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_UseKey(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Special(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_SwanDive(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_FastDive(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_WaterOut(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Wade(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_DeathSlide(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Extra_Breath(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Extra_YetiKill(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Extra_SharkKill(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Extra_Airlock(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Extra_GongBong(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Extra_DinoKill(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Extra_PullDagger(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Extra_StartAnim(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Extra_StartHouse(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Extra_FinalAnim(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_ClimbLeft(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_ClimbRight(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_ClimbStance(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Climbing(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_ClimbEnd(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_ClimbDown(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_SurfSwim(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_SurfBack(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_SurfLeft(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_SurfRight(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_SurfTread(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Swim(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Glide(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Tread(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_Dive(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_UWDeath(ITEM *item, COLL_INFO *coll);
|
||||
void __cdecl Lara_State_UWTwist(ITEM *item, COLL_INFO *coll);
|
920
src/tr2/game/level.c
Normal file
920
src/tr2/game/level.c
Normal file
|
@ -0,0 +1,920 @@
|
|||
#include "game/level.h"
|
||||
|
||||
#include "game/hwr.h"
|
||||
#include "game/items.h"
|
||||
#include "game/shell.h"
|
||||
#include "global/const.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
#include "specific/s_audio_sample.h"
|
||||
|
||||
#include <libtrx/benchmark.h>
|
||||
#include <libtrx/log.h>
|
||||
#include <libtrx/memory.h>
|
||||
#include <libtrx/virtual_file.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
static void __cdecl M_LoadTexturePages(VFILE *file);
|
||||
static void __cdecl M_LoadRooms(VFILE *file);
|
||||
static void __cdecl M_LoadMeshBase(VFILE *file);
|
||||
static void __cdecl M_LoadMeshes(VFILE *file);
|
||||
static int32_t __cdecl M_LoadAnims(VFILE *file, int32_t **frame_pointers);
|
||||
static void __cdecl M_LoadAnimChanges(VFILE *file);
|
||||
static void __cdecl M_LoadAnimRanges(VFILE *file);
|
||||
static void __cdecl M_LoadAnimCommands(VFILE *file);
|
||||
static void __cdecl M_LoadAnimBones(VFILE *file);
|
||||
static void __cdecl M_LoadAnimFrames(VFILE *file);
|
||||
static void __cdecl M_LoadObjects(VFILE *file);
|
||||
static void __cdecl M_LoadStaticObjects(VFILE *file);
|
||||
static void __cdecl M_LoadTextures(VFILE *file);
|
||||
static void __cdecl M_LoadSprites(VFILE *file);
|
||||
static void __cdecl M_LoadItems(VFILE *file);
|
||||
static void __cdecl M_LoadDepthQ(VFILE *file);
|
||||
static void __cdecl M_LoadPalettes(VFILE *file);
|
||||
static void __cdecl M_LoadCameras(VFILE *file);
|
||||
static void __cdecl M_LoadSoundEffects(VFILE *file);
|
||||
static void __cdecl M_LoadBoxes(VFILE *file);
|
||||
static void __cdecl M_LoadAnimatedTextures(VFILE *file);
|
||||
static void __cdecl M_LoadCinematic(VFILE *file);
|
||||
static void __cdecl M_LoadDemo(VFILE *file);
|
||||
static void __cdecl M_LoadDemoExternal(const char *level_name);
|
||||
static void __cdecl M_LoadSamples(VFILE *file);
|
||||
|
||||
static void __cdecl M_LoadTexturePages(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
char *base = NULL;
|
||||
|
||||
const bool is_16_bit = g_TextureFormat.bpp >= 16;
|
||||
const int32_t texture_size = TEXTURE_PAGE_WIDTH * TEXTURE_PAGE_HEIGHT;
|
||||
const int32_t texture_size_8_bit = texture_size * sizeof(uint8_t);
|
||||
const int32_t texture_size_16_bit = texture_size * sizeof(uint16_t);
|
||||
|
||||
const int32_t num_pages = VFile_ReadS32(file);
|
||||
LOG_INFO("texture pages: %d", num_pages);
|
||||
|
||||
if (g_SavedAppSettings.render_mode == RM_SOFTWARE) {
|
||||
for (int32_t i = 0; i < num_pages; i++) {
|
||||
if (g_TexturePageBuffer8[i] == NULL) {
|
||||
g_TexturePageBuffer8[i] =
|
||||
game_malloc(texture_size, GBUF_TEXTURE_PAGES);
|
||||
}
|
||||
VFile_Read(file, g_TexturePageBuffer8[i], texture_size);
|
||||
}
|
||||
// skip 16-bit texture pages
|
||||
VFile_Skip(file, num_pages * texture_size_16_bit);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
base = Memory_Alloc(
|
||||
num_pages * (is_16_bit ? texture_size_16_bit : texture_size_8_bit));
|
||||
|
||||
if (is_16_bit) {
|
||||
VFile_Skip(file, num_pages * texture_size_8_bit);
|
||||
char *ptr = base;
|
||||
for (int32_t i = 0; i < num_pages; i++) {
|
||||
VFile_Read(file, ptr, texture_size_16_bit);
|
||||
ptr += texture_size_16_bit;
|
||||
}
|
||||
HWR_LoadTexturePages(num_pages, base, NULL);
|
||||
} else {
|
||||
char *ptr = base;
|
||||
for (int32_t i = 0; i < num_pages; i++) {
|
||||
VFile_Read(file, ptr, texture_size_8_bit);
|
||||
ptr += texture_size_8_bit;
|
||||
}
|
||||
VFile_Skip(file, num_pages * texture_size_16_bit);
|
||||
HWR_LoadTexturePages((int32_t)num_pages, base, g_GamePalette8);
|
||||
}
|
||||
|
||||
g_TexturePageCount = num_pages;
|
||||
|
||||
finish:
|
||||
Memory_FreePointer(&base);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadRooms(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
|
||||
g_RoomCount = VFile_ReadS16(file);
|
||||
LOG_INFO("rooms: %d", g_RoomCount);
|
||||
if (g_RoomCount > MAX_ROOMS) {
|
||||
Shell_ExitSystem("Too many rooms");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
g_Rooms = game_malloc(sizeof(ROOM) * g_RoomCount, GBUF_ROOMS);
|
||||
assert(g_Rooms != NULL);
|
||||
|
||||
for (int32_t i = 0; i < g_RoomCount; i++) {
|
||||
ROOM *const r = &g_Rooms[i];
|
||||
|
||||
r->pos.x = VFile_ReadS32(file);
|
||||
r->pos.y = 0;
|
||||
r->pos.z = VFile_ReadS32(file);
|
||||
|
||||
r->min_floor = VFile_ReadS32(file);
|
||||
r->max_ceiling = VFile_ReadS32(file);
|
||||
|
||||
const int32_t data_size = VFile_ReadS32(file);
|
||||
r->data = game_malloc(sizeof(int16_t) * data_size, GBUF_ROOM_MESH);
|
||||
VFile_Read(file, r->data, sizeof(int16_t) * data_size);
|
||||
|
||||
const int16_t num_doors = VFile_ReadS16(file);
|
||||
if (num_doors <= 0) {
|
||||
r->portals = NULL;
|
||||
} else {
|
||||
r->portals = game_malloc(
|
||||
sizeof(PORTAL) * num_doors + sizeof(PORTALS),
|
||||
GBUF_ROOM_PORTALS);
|
||||
r->portals->count = num_doors;
|
||||
VFile_Read(file, r->portals->portal, sizeof(PORTAL) * num_doors);
|
||||
}
|
||||
|
||||
r->size.z = VFile_ReadS16(file);
|
||||
r->size.x = VFile_ReadS16(file);
|
||||
|
||||
r->sectors = game_malloc(
|
||||
sizeof(SECTOR) * r->size.z * r->size.x, GBUF_ROOM_FLOOR);
|
||||
for (int32_t i = 0; i < r->size.z * r->size.x; i++) {
|
||||
SECTOR *const sector = &r->sectors[i];
|
||||
sector->idx = VFile_ReadU16(file);
|
||||
sector->box = VFile_ReadS16(file);
|
||||
sector->pit_room = VFile_ReadU8(file);
|
||||
sector->floor = VFile_ReadS8(file);
|
||||
sector->sky_room = VFile_ReadU8(file);
|
||||
sector->ceiling = VFile_ReadS8(file);
|
||||
}
|
||||
|
||||
r->ambient_1 = VFile_ReadS16(file);
|
||||
r->ambient_2 = VFile_ReadS16(file);
|
||||
r->light_mode = VFile_ReadS16(file);
|
||||
|
||||
r->num_lights = VFile_ReadS16(file);
|
||||
if (!r->num_lights) {
|
||||
r->lights = NULL;
|
||||
} else {
|
||||
r->lights =
|
||||
game_malloc(sizeof(LIGHT) * r->num_lights, GBUF_ROOM_LIGHTS);
|
||||
for (int32_t i = 0; i < r->num_lights; i++) {
|
||||
LIGHT *const light = &r->lights[i];
|
||||
light->pos.x = VFile_ReadS32(file);
|
||||
light->pos.y = VFile_ReadS32(file);
|
||||
light->pos.z = VFile_ReadS32(file);
|
||||
light->intensity_1 = VFile_ReadS16(file);
|
||||
light->intensity_2 = VFile_ReadS16(file);
|
||||
light->falloff_1 = VFile_ReadS32(file);
|
||||
light->falloff_2 = VFile_ReadS32(file);
|
||||
}
|
||||
}
|
||||
|
||||
r->num_meshes = VFile_ReadS16(file);
|
||||
if (!r->num_meshes) {
|
||||
r->meshes = NULL;
|
||||
} else {
|
||||
r->meshes = game_malloc(
|
||||
sizeof(MESH) * r->num_meshes, GBUF_ROOM_STATIC_MESHES);
|
||||
for (int32_t i = 0; i < r->num_meshes; i++) {
|
||||
MESH *const mesh = &r->meshes[i];
|
||||
mesh->pos.x = VFile_ReadS32(file);
|
||||
mesh->pos.y = VFile_ReadS32(file);
|
||||
mesh->pos.z = VFile_ReadS32(file);
|
||||
mesh->rot.y = VFile_ReadS16(file);
|
||||
mesh->shade_1 = VFile_ReadS16(file);
|
||||
mesh->shade_2 = VFile_ReadS16(file);
|
||||
mesh->static_num = VFile_ReadS16(file);
|
||||
}
|
||||
}
|
||||
|
||||
r->flipped_room = VFile_ReadS16(file);
|
||||
r->flags = VFile_ReadU16(file);
|
||||
|
||||
r->bound_active = 0;
|
||||
r->bound_left = g_PhdWinMaxX;
|
||||
r->bound_top = g_PhdWinMaxY;
|
||||
r->bound_bottom = 0;
|
||||
r->bound_right = 0;
|
||||
r->item_num = NO_ITEM;
|
||||
r->fx_num = NO_ITEM;
|
||||
}
|
||||
|
||||
const int32_t floor_data_size = VFile_ReadS32(file);
|
||||
g_FloorData =
|
||||
game_malloc(sizeof(int16_t) * floor_data_size, GBUF_FLOOR_DATA);
|
||||
VFile_Read(file, g_FloorData, sizeof(int16_t) * floor_data_size);
|
||||
|
||||
finish:
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadMeshBase(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_meshes = VFile_ReadS32(file);
|
||||
LOG_INFO("meshes: %d", num_meshes);
|
||||
g_MeshBase = game_malloc(sizeof(int16_t) * num_meshes, GBUF_MESHES);
|
||||
VFile_Read(file, g_MeshBase, sizeof(int16_t) * num_meshes);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadMeshes(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_mesh_ptrs = VFile_ReadS32(file);
|
||||
LOG_INFO("mesh pointers: %d", num_mesh_ptrs);
|
||||
int32_t *const mesh_indices =
|
||||
(int32_t *)Memory_Alloc(sizeof(int32_t) * num_mesh_ptrs);
|
||||
VFile_Read(file, mesh_indices, sizeof(int32_t) * num_mesh_ptrs);
|
||||
|
||||
g_Meshes =
|
||||
game_malloc(sizeof(int16_t *) * num_mesh_ptrs, GBUF_MESH_POINTERS);
|
||||
for (int32_t i = 0; i < num_mesh_ptrs; i++) {
|
||||
g_Meshes[i] = &g_MeshBase[mesh_indices[i] / 2];
|
||||
}
|
||||
|
||||
Memory_Free(mesh_indices);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static int32_t __cdecl M_LoadAnims(VFILE *const file, int32_t **frame_pointers)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_anims = VFile_ReadS32(file);
|
||||
LOG_INFO("anims: %d", num_anims);
|
||||
g_Anims = game_malloc(sizeof(ANIM) * num_anims, GBUF_ANIMS);
|
||||
if (frame_pointers != NULL) {
|
||||
*frame_pointers = Memory_Alloc(sizeof(int32_t) * num_anims);
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < num_anims; i++) {
|
||||
ANIM *const anim = &g_Anims[i];
|
||||
const int32_t frame_idx = VFile_ReadS32(file);
|
||||
if (frame_pointers != NULL) {
|
||||
(*frame_pointers)[i] = frame_idx;
|
||||
}
|
||||
anim->frame_ptr = NULL; // filled later by the animation frame loader
|
||||
anim->interpolation = VFile_ReadS16(file);
|
||||
anim->current_anim_state = VFile_ReadS16(file);
|
||||
anim->velocity = VFile_ReadS32(file);
|
||||
anim->acceleration = VFile_ReadS32(file);
|
||||
anim->frame_base = VFile_ReadS16(file);
|
||||
anim->frame_end = VFile_ReadS16(file);
|
||||
anim->jump_anim_num = VFile_ReadS16(file);
|
||||
anim->jump_frame_num = VFile_ReadS16(file);
|
||||
anim->num_changes = VFile_ReadS16(file);
|
||||
anim->change_idx = VFile_ReadS16(file);
|
||||
anim->num_commands = VFile_ReadS16(file);
|
||||
anim->command_idx = VFile_ReadS16(file);
|
||||
}
|
||||
Benchmark_End(benchmark, NULL);
|
||||
return num_anims;
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadAnimChanges(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_anim_changes = VFile_ReadS32(file);
|
||||
LOG_INFO("anim changes: %d", num_anim_changes);
|
||||
g_AnimChanges =
|
||||
game_malloc(sizeof(ANIM_CHANGE) * num_anim_changes, GBUF_STRUCTS);
|
||||
for (int32_t i = 0; i < num_anim_changes; i++) {
|
||||
ANIM_CHANGE *const change = &g_AnimChanges[i];
|
||||
change->goal_anim_state = VFile_ReadS16(file);
|
||||
change->num_ranges = VFile_ReadS16(file);
|
||||
change->range_idx = VFile_ReadS16(file);
|
||||
}
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadAnimRanges(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_anim_ranges = VFile_ReadS32(file);
|
||||
LOG_INFO("anim ranges: %d", num_anim_ranges);
|
||||
g_AnimRanges =
|
||||
game_malloc(sizeof(ANIM_RANGE) * num_anim_ranges, GBUF_ANIM_RANGES);
|
||||
for (int32_t i = 0; i < num_anim_ranges; i++) {
|
||||
ANIM_RANGE *const range = &g_AnimRanges[i];
|
||||
range->start_frame = VFile_ReadS16(file);
|
||||
range->end_frame = VFile_ReadS16(file);
|
||||
range->link_anim_num = VFile_ReadS16(file);
|
||||
range->link_frame_num = VFile_ReadS16(file);
|
||||
}
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadAnimCommands(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_anim_commands = VFile_ReadS32(file);
|
||||
LOG_INFO("anim commands: %d", num_anim_commands);
|
||||
g_AnimCommands =
|
||||
game_malloc(sizeof(int16_t) * num_anim_commands, GBUF_ANIM_COMMANDS);
|
||||
VFile_Read(file, g_AnimCommands, sizeof(int16_t) * num_anim_commands);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadAnimBones(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_anim_bones = VFile_ReadS32(file);
|
||||
LOG_INFO("anim bones: %d", num_anim_bones);
|
||||
g_AnimBones =
|
||||
game_malloc(sizeof(int32_t) * num_anim_bones, GBUF_ANIM_BONES);
|
||||
VFile_Read(file, g_AnimBones, sizeof(int32_t) * num_anim_bones);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadAnimFrames(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t anim_frame_data_size = VFile_ReadS32(file);
|
||||
LOG_INFO("anim frame data size: %d", anim_frame_data_size);
|
||||
g_AnimFrames =
|
||||
game_malloc(sizeof(int16_t) * anim_frame_data_size, GBUF_ANIM_FRAMES);
|
||||
// TODO: make me FRAME_INFO
|
||||
int16_t *ptr = (int16_t *)&g_AnimFrames[0];
|
||||
VFile_Read(file, ptr, sizeof(int16_t) * anim_frame_data_size);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadObjects(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_objects = VFile_ReadS32(file);
|
||||
LOG_INFO("objects: %d", num_objects);
|
||||
for (int32_t i = 0; i < num_objects; i++) {
|
||||
const GAME_OBJECT_ID object_id = VFile_ReadS32(file);
|
||||
OBJECT *const object = &g_Objects[object_id];
|
||||
object->mesh_count = VFile_ReadS16(file);
|
||||
object->mesh_idx = VFile_ReadS16(file);
|
||||
object->bone_idx = VFile_ReadS32(file);
|
||||
const int32_t frame_idx = VFile_ReadS32(file);
|
||||
object->frame_base = ((int16_t *)g_AnimFrames) + frame_idx / 2;
|
||||
object->anim_idx = VFile_ReadS16(file);
|
||||
object->loaded = 1;
|
||||
}
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadStaticObjects(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_static_objects = VFile_ReadS32(file);
|
||||
LOG_INFO("static objects: %d", num_static_objects);
|
||||
for (int32_t i = 0; i < num_static_objects; i++) {
|
||||
const int32_t static_num = VFile_ReadS32(file);
|
||||
STATIC_INFO *static_obj = &g_StaticObjects[static_num];
|
||||
static_obj->mesh_idx = VFile_ReadS16(file);
|
||||
static_obj->draw_bounds.min_x = VFile_ReadS16(file);
|
||||
static_obj->draw_bounds.max_x = VFile_ReadS16(file);
|
||||
static_obj->draw_bounds.min_y = VFile_ReadS16(file);
|
||||
static_obj->draw_bounds.max_y = VFile_ReadS16(file);
|
||||
static_obj->draw_bounds.min_z = VFile_ReadS16(file);
|
||||
static_obj->draw_bounds.max_z = VFile_ReadS16(file);
|
||||
static_obj->collision_bounds.min_x = VFile_ReadS16(file);
|
||||
static_obj->collision_bounds.max_x = VFile_ReadS16(file);
|
||||
static_obj->collision_bounds.min_y = VFile_ReadS16(file);
|
||||
static_obj->collision_bounds.max_y = VFile_ReadS16(file);
|
||||
static_obj->collision_bounds.min_z = VFile_ReadS16(file);
|
||||
static_obj->collision_bounds.max_z = VFile_ReadS16(file);
|
||||
static_obj->flags = VFile_ReadU16(file);
|
||||
}
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadTextures(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_textures = VFile_ReadS32(file);
|
||||
LOG_INFO("textures: %d", num_textures);
|
||||
if (num_textures > MAX_TEXTURES) {
|
||||
Shell_ExitSystem("Too many textures");
|
||||
return;
|
||||
}
|
||||
|
||||
g_TextureInfoCount = num_textures;
|
||||
for (int32_t i = 0; i < num_textures; i++) {
|
||||
PHD_TEXTURE *texture = &g_TextureInfo[i];
|
||||
texture->draw_type = VFile_ReadU16(file);
|
||||
texture->tex_page = VFile_ReadU16(file);
|
||||
for (int32_t j = 0; j < 4; j++) {
|
||||
texture->uv[j].u = VFile_ReadU16(file);
|
||||
texture->uv[j].v = VFile_ReadU16(file);
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < num_textures; i++) {
|
||||
uint16_t *const uv = &g_TextureInfo[i].uv[0].u;
|
||||
uint8_t byte = 0;
|
||||
for (int32_t j = 0; j < 8; j++) {
|
||||
if ((uv[j] & 0x80) != 0) {
|
||||
uv[j] |= 0xFF;
|
||||
byte |= 1 << j;
|
||||
} else {
|
||||
uv[j] &= 0xFF00;
|
||||
}
|
||||
}
|
||||
g_LabTextureUVFlag[i] = byte;
|
||||
}
|
||||
|
||||
AdjustTextureUVs(true);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadSprites(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_sprites = VFile_ReadS32(file);
|
||||
LOG_DEBUG("sprites: %d", num_sprites);
|
||||
for (int32_t i = 0; i < num_sprites; i++) {
|
||||
PHD_SPRITE *const sprite = &g_PhdSprites[i];
|
||||
sprite->tex_page = VFile_ReadU16(file);
|
||||
sprite->offset = VFile_ReadU16(file);
|
||||
sprite->width = VFile_ReadU16(file);
|
||||
sprite->height = VFile_ReadU16(file);
|
||||
sprite->x0 = VFile_ReadS16(file);
|
||||
sprite->y0 = VFile_ReadS16(file);
|
||||
sprite->x1 = VFile_ReadS16(file);
|
||||
sprite->y1 = VFile_ReadS16(file);
|
||||
}
|
||||
|
||||
const int32_t num_statics = VFile_ReadS32(file);
|
||||
LOG_DEBUG("statics: %d", num_statics);
|
||||
for (int32_t i = 0; i < num_statics; i++) {
|
||||
int32_t object_id = VFile_ReadS32(file);
|
||||
if (object_id >= O_NUMBER_OF) {
|
||||
object_id -= O_NUMBER_OF;
|
||||
STATIC_INFO *const static_object = &g_StaticObjects[object_id];
|
||||
VFile_Skip(file, sizeof(int16_t));
|
||||
static_object->mesh_idx = VFile_ReadS16(file);
|
||||
} else {
|
||||
OBJECT *const object = &g_Objects[object_id];
|
||||
object->mesh_count = VFile_ReadS16(file);
|
||||
object->mesh_idx = VFile_ReadS16(file);
|
||||
object->loaded = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadItems(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
|
||||
const int32_t num_items = VFile_ReadS32(file);
|
||||
LOG_DEBUG("items: %d", num_items);
|
||||
if (!num_items) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
if (num_items > MAX_ITEMS) {
|
||||
Shell_ExitSystem("Too many items");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
g_Items = game_malloc(sizeof(ITEM) * MAX_ITEMS, GBUF_ITEMS);
|
||||
g_LevelItemCount = num_items;
|
||||
|
||||
Item_InitialiseArray(MAX_ITEMS);
|
||||
|
||||
for (int32_t i = 0; i < num_items; i++) {
|
||||
ITEM *const item = &g_Items[i];
|
||||
item->object_id = VFile_ReadS16(file);
|
||||
item->room_num = VFile_ReadS16(file);
|
||||
item->pos.x = VFile_ReadS32(file);
|
||||
item->pos.y = VFile_ReadS32(file);
|
||||
item->pos.z = VFile_ReadS32(file);
|
||||
item->rot.y = VFile_ReadS16(file);
|
||||
item->shade_1 = VFile_ReadS16(file);
|
||||
item->shade_2 = VFile_ReadS16(file);
|
||||
item->flags = VFile_ReadS16(file);
|
||||
if (item->object_id < 0 || item->object_id >= O_NUMBER_OF) {
|
||||
Shell_ExitSystemFmt(
|
||||
"Bad object number (%d) on item %d", item->object_id, i);
|
||||
goto finish;
|
||||
}
|
||||
Item_Initialise(i);
|
||||
}
|
||||
|
||||
finish:
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadDepthQ(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
for (int32_t i = 0; i < 32; i++) {
|
||||
VFile_Read(file, g_DepthQTable[i].index, sizeof(uint8_t) * 256);
|
||||
g_DepthQTable[i].index[0] = 0;
|
||||
}
|
||||
|
||||
if (g_GameVid_IsWindowedVGA) {
|
||||
RGB_888 palette[256];
|
||||
CopyBitmapPalette(
|
||||
g_GamePalette8, g_DepthQTable[0].index, 32 * sizeof(DEPTHQ_ENTRY),
|
||||
palette);
|
||||
SyncSurfacePalettes(
|
||||
g_DepthQTable, 256, 32, 256, g_GamePalette8, g_DepthQTable, 256,
|
||||
palette, true);
|
||||
memcpy(g_GamePalette8, palette, sizeof(g_GamePalette8));
|
||||
|
||||
for (int32_t i = 0; i < 256; i++) {
|
||||
g_DepthQIndex[i] = S_COLOR(
|
||||
g_GamePalette8[i].red, g_GamePalette8[i].green,
|
||||
g_GamePalette8[i].blue);
|
||||
}
|
||||
} else {
|
||||
for (int32_t i = 0; i < 256; i++) {
|
||||
g_DepthQIndex[i] = g_DepthQTable[24].index[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < 32; i++) {
|
||||
for (int32_t j = 0; j < 256; j++) {
|
||||
g_GouraudTable[j].index[i] = g_DepthQTable[i].index[j];
|
||||
}
|
||||
}
|
||||
|
||||
g_IsWet = 0;
|
||||
for (int32_t i = 0; i < 256; i++) {
|
||||
g_WaterPalette[i].red = g_GamePalette8[i].red * 2 / 3;
|
||||
g_WaterPalette[i].green = g_GamePalette8[i].green * 2 / 3;
|
||||
g_WaterPalette[i].blue = g_GamePalette8[i].blue;
|
||||
}
|
||||
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadPalettes(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
VFile_Read(file, g_GamePalette8, sizeof(RGB_888) * 256);
|
||||
|
||||
g_GamePalette8[0].red = 0;
|
||||
g_GamePalette8[0].green = 0;
|
||||
g_GamePalette8[0].blue = 0;
|
||||
|
||||
for (int32_t i = 1; i < 256; i++) {
|
||||
RGB_888 *col = &g_GamePalette8[i];
|
||||
col->red = (col->red << 2) | (col->red >> 4);
|
||||
col->green = (col->green << 2) | (col->green >> 4);
|
||||
col->blue = (col->blue << 2) | (col->blue >> 4);
|
||||
}
|
||||
|
||||
VFile_Read(file, g_GamePalette16, sizeof(PALETTEENTRY) * 256);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadCameras(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
g_NumCameras = VFile_ReadS32(file);
|
||||
LOG_DEBUG("fixed cameras: %d", g_NumCameras);
|
||||
if (!g_NumCameras) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
g_Camera.fixed =
|
||||
game_malloc(sizeof(OBJECT_VECTOR) * g_NumCameras, GBUF_CAMERAS);
|
||||
for (int32_t i = 0; i < g_NumCameras; i++) {
|
||||
OBJECT_VECTOR *const camera = &g_Camera.fixed[i];
|
||||
camera->x = VFile_ReadS32(file);
|
||||
camera->y = VFile_ReadS32(file);
|
||||
camera->z = VFile_ReadS32(file);
|
||||
camera->data = VFile_ReadS16(file);
|
||||
camera->flags = VFile_ReadS16(file);
|
||||
}
|
||||
|
||||
finish:
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadSoundEffects(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
|
||||
g_SoundEffectCount = VFile_ReadS32(file);
|
||||
LOG_DEBUG("sound effects: %d", g_SoundEffectCount);
|
||||
if (!g_SoundEffectCount) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
g_SoundEffects =
|
||||
game_malloc(sizeof(OBJECT_VECTOR) * g_SoundEffectCount, GBUF_SOUND_FX);
|
||||
for (int32_t i = 0; i < g_SoundEffectCount; i++) {
|
||||
OBJECT_VECTOR *const effect = &g_SoundEffects[i];
|
||||
effect->x = VFile_ReadS32(file);
|
||||
effect->y = VFile_ReadS32(file);
|
||||
effect->z = VFile_ReadS32(file);
|
||||
effect->data = VFile_ReadS16(file);
|
||||
effect->flags = VFile_ReadS16(file);
|
||||
}
|
||||
|
||||
finish:
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadBoxes(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
g_BoxCount = VFile_ReadS32(file);
|
||||
g_Boxes = game_malloc(sizeof(BOX_INFO) * g_BoxCount, GBUF_BOXES);
|
||||
for (int32_t i = 0; i < g_BoxCount; i++) {
|
||||
BOX_INFO *const box = &g_Boxes[i];
|
||||
box->left = VFile_ReadU8(file);
|
||||
box->right = VFile_ReadU8(file);
|
||||
box->top = VFile_ReadU8(file);
|
||||
box->bottom = VFile_ReadU8(file);
|
||||
box->height = VFile_ReadS16(file);
|
||||
box->overlap_index = VFile_ReadS16(file);
|
||||
}
|
||||
|
||||
const int32_t num_overlaps = VFile_ReadS32(file);
|
||||
g_Overlap = game_malloc(sizeof(uint16_t) * num_overlaps, GBUF_OVERLAPS);
|
||||
VFile_Read(file, g_Overlap, sizeof(uint16_t) * num_overlaps);
|
||||
|
||||
for (int32_t i = 0; i < 2; i++) {
|
||||
for (int32_t j = 0; j < 4; j++) {
|
||||
const bool skip = j == 2
|
||||
|| (j == 1 && !g_Objects[O_SPIDER].loaded
|
||||
&& !g_Objects[O_SKIDOO_ARMED].loaded)
|
||||
|| (j == 3 && !g_Objects[O_YETI].loaded
|
||||
&& !g_Objects[O_WORKER_3].loaded);
|
||||
|
||||
if (skip) {
|
||||
VFile_Skip(file, sizeof(int16_t) * g_BoxCount);
|
||||
continue;
|
||||
}
|
||||
|
||||
g_GroundZone[j][i] =
|
||||
game_malloc(sizeof(int16_t) * g_BoxCount, GBUF_GROUND_ZONE);
|
||||
VFile_Read(file, g_GroundZone[j][i], sizeof(int16_t) * g_BoxCount);
|
||||
}
|
||||
|
||||
g_FlyZone[i] = game_malloc(sizeof(int16_t) * g_BoxCount, GBUF_FLY_ZONE);
|
||||
VFile_Read(file, g_FlyZone[i], sizeof(int16_t) * g_BoxCount);
|
||||
}
|
||||
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadAnimatedTextures(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
const int32_t num_ranges = VFile_ReadS32(file);
|
||||
g_AnimTextureRanges = game_malloc(
|
||||
sizeof(int16_t) * num_ranges, GBUF_ANIMATING_TEXTURE_RANGES);
|
||||
VFile_Read(file, g_AnimTextureRanges, sizeof(int16_t) * num_ranges);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadCinematic(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
g_NumCineFrames = VFile_ReadS16(file);
|
||||
if (g_NumCineFrames <= 0) {
|
||||
g_CineLoaded = false;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
g_CineData = game_malloc(
|
||||
sizeof(CINE_FRAME) * g_NumCineFrames, GBUF_CINEMATIC_FRAMES);
|
||||
for (int32_t i = 0; i < g_NumCineFrames; i++) {
|
||||
CINE_FRAME *const frame = &g_CineData[i];
|
||||
frame->tx = VFile_ReadS16(file);
|
||||
frame->ty = VFile_ReadS16(file);
|
||||
frame->tz = VFile_ReadS16(file);
|
||||
frame->cx = VFile_ReadS16(file);
|
||||
frame->cy = VFile_ReadS16(file);
|
||||
frame->cz = VFile_ReadS16(file);
|
||||
frame->fov = VFile_ReadS16(file);
|
||||
frame->roll = VFile_ReadS16(file);
|
||||
}
|
||||
g_CineLoaded = true;
|
||||
|
||||
finish:
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadDemo(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
g_DemoCount = 0;
|
||||
|
||||
// TODO: is the allocation necessary if there's no demo data?
|
||||
// TODO: do not hardcode the allocation size
|
||||
g_DemoPtr = game_malloc(36000, GBUF_LOAD_DEMO_BUFFER);
|
||||
|
||||
const int32_t demo_size = VFile_ReadU16(file);
|
||||
LOG_DEBUG("demo input size: %d", demo_size);
|
||||
if (!demo_size) {
|
||||
g_IsDemoLoaded = false;
|
||||
} else {
|
||||
g_IsDemoLoaded = true;
|
||||
VFile_Read(file, g_DemoPtr, demo_size);
|
||||
}
|
||||
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadDemoExternal(const char *const level_name)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
char file_name[MAX_PATH];
|
||||
strcpy(file_name, level_name);
|
||||
ChangeFileNameExtension(file_name, "DEM");
|
||||
|
||||
HANDLE handle = CreateFileA(
|
||||
file_name, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: do not hardcode the allocation size
|
||||
DWORD bytes_read;
|
||||
ReadFileSync(handle, g_DemoPtr, 36000, &bytes_read, 0);
|
||||
g_IsDemoLoaded = bytes_read != 0;
|
||||
CloseHandle(handle);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
static void __cdecl M_LoadSamples(VFILE *const file)
|
||||
{
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
int32_t *sample_offsets = NULL;
|
||||
|
||||
g_SoundIsActive = false;
|
||||
if (!S_Audio_Sample_IsEnabled()) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
S_Audio_Sample_CloseAllTracks();
|
||||
|
||||
VFile_Read(file, g_SampleLUT, sizeof(int16_t) * SFX_NUMBER_OF);
|
||||
g_NumSampleInfos = VFile_ReadS32(file);
|
||||
LOG_DEBUG("sample infos: %d", g_NumSampleInfos);
|
||||
if (!g_NumSampleInfos) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
g_SampleInfos =
|
||||
game_malloc(sizeof(SAMPLE_INFO) * g_NumSampleInfos, GBUF_SAMPLE_INFOS);
|
||||
for (int32_t i = 0; i < g_NumSampleInfos; i++) {
|
||||
SAMPLE_INFO *const sample_info = &g_SampleInfos[i];
|
||||
sample_info->number = VFile_ReadS16(file);
|
||||
sample_info->volume = VFile_ReadS16(file);
|
||||
sample_info->randomness = VFile_ReadS16(file);
|
||||
sample_info->flags = VFile_ReadS16(file);
|
||||
}
|
||||
|
||||
const int32_t num_samples = VFile_ReadS32(file);
|
||||
LOG_DEBUG("samples: %d", num_samples);
|
||||
if (!num_samples) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
sample_offsets = Memory_Alloc(sizeof(int32_t) * num_samples);
|
||||
VFile_Read(file, sample_offsets, sizeof(int32_t) * num_samples);
|
||||
|
||||
const char *const file_name = "data\\main.sfx";
|
||||
const char *const full_path = GetFullPath(file_name);
|
||||
LOG_DEBUG("Loading samples from %s", full_path);
|
||||
HANDLE sfx_handle = CreateFileA(
|
||||
full_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
if (sfx_handle == INVALID_HANDLE_VALUE) {
|
||||
Shell_ExitSystemFmt(
|
||||
"Could not open %s file: 0x%x", file_name, GetLastError());
|
||||
goto finish;
|
||||
}
|
||||
|
||||
// TODO: refactor these WAVE/RIFF shenanigans
|
||||
int32_t sample_id = 0;
|
||||
for (int32_t i = 0; sample_id < num_samples; i++) {
|
||||
char header[0x2C];
|
||||
ReadFileSync(sfx_handle, header, 0x2C, NULL, NULL);
|
||||
if (*(int32_t *)(header + 0) != 0x46464952
|
||||
|| *(int32_t *)(header + 8) != 0x45564157
|
||||
|| *(int32_t *)(header + 36) != 0x61746164) {
|
||||
LOG_ERROR("Unexpected sample header for sample %d", i);
|
||||
goto finish;
|
||||
}
|
||||
const int32_t data_size = *(int32_t *)(header + 0x28);
|
||||
const int32_t aligned_size = (data_size + 1) & ~1;
|
||||
|
||||
*(int16_t *)(header + 16) = 0;
|
||||
if (sample_offsets[sample_id] != i) {
|
||||
SetFilePointer(sfx_handle, aligned_size, NULL, FILE_CURRENT);
|
||||
continue;
|
||||
}
|
||||
|
||||
char *sample_data = Memory_Alloc(aligned_size);
|
||||
ReadFileSync(sfx_handle, sample_data, aligned_size, NULL, NULL);
|
||||
// TODO: do not reconstruct the header in S_Audio_Sample_Load, just
|
||||
// pass the entire sample directly
|
||||
const bool result = S_Audio_Sample_Load(
|
||||
sample_id, (LPWAVEFORMATEX)(header + 20), sample_data, data_size);
|
||||
Memory_FreePointer(&sample_data);
|
||||
if (!result) {
|
||||
goto finish;
|
||||
}
|
||||
|
||||
sample_id++;
|
||||
}
|
||||
CloseHandle(sfx_handle);
|
||||
|
||||
g_SoundIsActive = true;
|
||||
|
||||
finish:
|
||||
Memory_FreePointer(&sample_offsets);
|
||||
Benchmark_End(benchmark, NULL);
|
||||
}
|
||||
|
||||
bool __cdecl Level_Load(const char *const file_name, const int32_t level_num)
|
||||
{
|
||||
LOG_DEBUG("%s (num=%d)", g_GF_LevelNames[level_num], level_num);
|
||||
init_game_malloc();
|
||||
|
||||
BENCHMARK *const benchmark = Benchmark_Start();
|
||||
bool result = false;
|
||||
|
||||
const char *full_path = GetFullPath(file_name);
|
||||
strcpy(g_LevelFileName, full_path);
|
||||
|
||||
VFILE *file = VFile_CreateFromPath(full_path);
|
||||
|
||||
const int32_t version = VFile_ReadS32(file);
|
||||
if (version > 45) {
|
||||
Shell_ExitSystemFmt(
|
||||
"FATAL: Level %d (%s) requires a new TOMB2.EXE (version %d) to run",
|
||||
level_num, full_path, file_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version < 45) {
|
||||
Shell_ExitSystemFmt(
|
||||
"FATAL: Level %d (%s) is OUT OF DATE (version %d). COPY NEW "
|
||||
"EDITORS AND REMAKE LEVEL",
|
||||
level_num, full_path, file_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
g_LevelFilePalettesOffset = VFile_GetPos(file);
|
||||
M_LoadPalettes(file);
|
||||
|
||||
g_LevelFileTexPagesOffset = VFile_GetPos(file);
|
||||
M_LoadTexturePages(file);
|
||||
VFile_Skip(file, 4);
|
||||
|
||||
M_LoadRooms(file);
|
||||
|
||||
M_LoadMeshBase(file);
|
||||
M_LoadMeshes(file);
|
||||
|
||||
int32_t *frame_pointers = NULL;
|
||||
const int32_t num_anims = M_LoadAnims(file, &frame_pointers);
|
||||
M_LoadAnimChanges(file);
|
||||
M_LoadAnimRanges(file);
|
||||
M_LoadAnimCommands(file);
|
||||
M_LoadAnimBones(file);
|
||||
M_LoadAnimFrames(file);
|
||||
|
||||
for (int32_t i = 0; i < num_anims; i++) {
|
||||
ANIM *const anim = &g_Anims[i];
|
||||
// TODO: this is horrible
|
||||
anim->frame_ptr = ((int16_t *)g_AnimFrames) + frame_pointers[i] / 2;
|
||||
}
|
||||
Memory_FreePointer(&frame_pointers);
|
||||
|
||||
M_LoadObjects(file);
|
||||
InitialiseObjects();
|
||||
M_LoadStaticObjects(file);
|
||||
M_LoadTextures(file);
|
||||
|
||||
M_LoadSprites(file);
|
||||
M_LoadCameras(file);
|
||||
M_LoadSoundEffects(file);
|
||||
M_LoadBoxes(file);
|
||||
M_LoadAnimatedTextures(file);
|
||||
M_LoadItems(file);
|
||||
|
||||
g_LevelFileDepthQOffset = VFile_GetPos(file);
|
||||
M_LoadDepthQ(file);
|
||||
M_LoadCinematic(file);
|
||||
M_LoadDemo(file);
|
||||
M_LoadSamples(file);
|
||||
|
||||
M_LoadDemoExternal(full_path);
|
||||
VFile_Close(file);
|
||||
file = NULL;
|
||||
|
||||
Benchmark_End(benchmark, NULL);
|
||||
return true;
|
||||
}
|
5
src/tr2/game/level.h
Normal file
5
src/tr2/game/level.h
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
bool __cdecl Level_Load(const char *file_name, int32_t level_num);
|
391
src/tr2/game/los.c
Normal file
391
src/tr2/game/los.c
Normal file
|
@ -0,0 +1,391 @@
|
|||
#include "game/los.h"
|
||||
|
||||
#include "game/items.h"
|
||||
#include "game/math.h"
|
||||
#include "game/room.h"
|
||||
#include "global/const.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
int32_t __cdecl LOS_CheckX(
|
||||
const GAME_VECTOR *const start, GAME_VECTOR *const target)
|
||||
{
|
||||
const int32_t dx = target->x - start->x;
|
||||
if (dx == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const int32_t dy = ((target->y - start->y) << WALL_SHIFT) / dx;
|
||||
const int32_t dz = ((target->z - start->z) << WALL_SHIFT) / dx;
|
||||
|
||||
int16_t room_num = start->room_num;
|
||||
int16_t last_room_num = start->room_num;
|
||||
|
||||
g_LOSRooms[0] = room_num;
|
||||
g_LOSNumRooms = 1;
|
||||
|
||||
if (dx < 0) {
|
||||
int32_t x = start->x & (~(WALL_L - 1));
|
||||
int32_t y = start->y + ((dy * (x - start->x)) >> WALL_SHIFT);
|
||||
int32_t z = start->z + ((dz * (x - start->x)) >> WALL_SHIFT);
|
||||
|
||||
while (x > target->x) {
|
||||
{
|
||||
const SECTOR *const sector = Room_GetSector(x, y, z, &room_num);
|
||||
const int32_t height = Room_GetHeight(sector, x, y, z);
|
||||
const int32_t ceiling = Room_GetCeiling(sector, x, y, z);
|
||||
if (y > height || y < ceiling) {
|
||||
target->x = x;
|
||||
target->y = y;
|
||||
target->z = z;
|
||||
target->room_num = room_num;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (room_num != last_room_num) {
|
||||
last_room_num = room_num;
|
||||
g_LOSRooms[g_LOSNumRooms++] = room_num;
|
||||
}
|
||||
|
||||
{
|
||||
const SECTOR *const sector =
|
||||
Room_GetSector(x - 1, y, z, &room_num);
|
||||
const int32_t height = Room_GetHeight(sector, x - 1, y, z);
|
||||
const int32_t ceiling = Room_GetCeiling(sector, x - 1, y, z);
|
||||
if (y > height || y < ceiling) {
|
||||
target->x = x;
|
||||
target->y = y;
|
||||
target->z = z;
|
||||
target->room_num = last_room_num;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
x -= WALL_L;
|
||||
y -= dy;
|
||||
z -= dz;
|
||||
}
|
||||
} else {
|
||||
int32_t x = start->x | (WALL_L - 1);
|
||||
int32_t y = start->y + (((x - start->x) * dy) >> WALL_SHIFT);
|
||||
int32_t z = start->z + (((x - start->x) * dz) >> WALL_SHIFT);
|
||||
|
||||
while (x < target->x) {
|
||||
{
|
||||
const SECTOR *const sector = Room_GetSector(x, y, z, &room_num);
|
||||
const int32_t height = Room_GetHeight(sector, x, y, z);
|
||||
const int32_t ceiling = Room_GetCeiling(sector, x, y, z);
|
||||
if (y > height || y < ceiling) {
|
||||
target->z = z;
|
||||
target->y = y;
|
||||
target->x = x;
|
||||
target->room_num = room_num;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (room_num != last_room_num) {
|
||||
last_room_num = room_num;
|
||||
g_LOSRooms[g_LOSNumRooms++] = room_num;
|
||||
}
|
||||
|
||||
{
|
||||
const SECTOR *const sector =
|
||||
Room_GetSector(x + 1, y, z, &room_num);
|
||||
const int32_t height = Room_GetHeight(sector, x + 1, y, z);
|
||||
const int32_t ceiling = Room_GetCeiling(sector, x + 1, y, z);
|
||||
if (y > height || y < ceiling) {
|
||||
target->x = x;
|
||||
target->y = y;
|
||||
target->z = z;
|
||||
target->room_num = last_room_num;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
x += WALL_L;
|
||||
y += dy;
|
||||
z += dz;
|
||||
}
|
||||
}
|
||||
|
||||
target->room_num = room_num;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t __cdecl LOS_CheckZ(
|
||||
const GAME_VECTOR *const start, GAME_VECTOR *const target)
|
||||
{
|
||||
const int32_t dz = target->z - start->z;
|
||||
if (dz == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const int32_t dx = ((target->x - start->x) << WALL_SHIFT) / dz;
|
||||
const int32_t dy = ((target->y - start->y) << WALL_SHIFT) / dz;
|
||||
|
||||
int16_t room_num = start->room_num;
|
||||
int16_t last_room_num = start->room_num;
|
||||
|
||||
g_LOSRooms[0] = room_num;
|
||||
g_LOSNumRooms = 1;
|
||||
|
||||
if (dz < 0) {
|
||||
int32_t z = start->z & (~(WALL_L - 1));
|
||||
int32_t x = start->x + ((dx * (z - start->z)) >> WALL_SHIFT);
|
||||
int32_t y = start->y + ((dy * (z - start->z)) >> WALL_SHIFT);
|
||||
|
||||
while (z > target->z) {
|
||||
{
|
||||
const SECTOR *const sector = Room_GetSector(x, y, z, &room_num);
|
||||
const int32_t height = Room_GetHeight(sector, x, y, z);
|
||||
const int32_t ceiling = Room_GetCeiling(sector, x, y, z);
|
||||
if (y > height || y < ceiling) {
|
||||
target->x = x;
|
||||
target->y = y;
|
||||
target->z = z;
|
||||
target->room_num = room_num;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (room_num != last_room_num) {
|
||||
last_room_num = room_num;
|
||||
g_LOSRooms[g_LOSNumRooms++] = room_num;
|
||||
}
|
||||
|
||||
{
|
||||
const SECTOR *const sector =
|
||||
Room_GetSector(x, y, z - 1, &room_num);
|
||||
const int32_t height = Room_GetHeight(sector, x, y, z - 1);
|
||||
const int32_t ceiling = Room_GetCeiling(sector, x, y, z - 1);
|
||||
if (y > height || y < ceiling) {
|
||||
target->x = x;
|
||||
target->y = y;
|
||||
target->z = z;
|
||||
target->room_num = last_room_num;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
z -= WALL_L;
|
||||
x -= dx;
|
||||
y -= dy;
|
||||
}
|
||||
} else {
|
||||
int32_t z = start->z | (WALL_L - 1);
|
||||
int32_t x = start->x + ((dx * (z - start->z)) >> WALL_SHIFT);
|
||||
int32_t y = start->y + ((dy * (z - start->z)) >> WALL_SHIFT);
|
||||
|
||||
while (z < target->z) {
|
||||
{
|
||||
const SECTOR *const sector = Room_GetSector(x, y, z, &room_num);
|
||||
const int32_t height = Room_GetHeight(sector, x, y, z);
|
||||
const int32_t ceiling = Room_GetCeiling(sector, x, y, z);
|
||||
if (y > height || y < ceiling) {
|
||||
target->x = x;
|
||||
target->y = y;
|
||||
target->z = z;
|
||||
target->room_num = room_num;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (room_num != last_room_num) {
|
||||
last_room_num = room_num;
|
||||
g_LOSRooms[g_LOSNumRooms++] = room_num;
|
||||
}
|
||||
|
||||
{
|
||||
const SECTOR *const sector =
|
||||
Room_GetSector(x, y, z + 1, &room_num);
|
||||
const int32_t height = Room_GetHeight(sector, x, y, z + 1);
|
||||
const int32_t ceiling = Room_GetCeiling(sector, x, y, z + 1);
|
||||
if (y > height || y < ceiling) {
|
||||
target->x = x;
|
||||
target->y = y;
|
||||
target->z = z;
|
||||
target->room_num = last_room_num;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
z += WALL_L;
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
}
|
||||
|
||||
target->room_num = room_num;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t __cdecl LOS_ClipTarget(
|
||||
const GAME_VECTOR *const start, GAME_VECTOR *const target,
|
||||
const SECTOR *const sector)
|
||||
{
|
||||
const int32_t dx = target->x - start->x;
|
||||
const int32_t dy = target->y - start->y;
|
||||
const int32_t dz = target->z - start->z;
|
||||
|
||||
const int32_t height =
|
||||
Room_GetHeight(sector, target->x, target->y, target->z);
|
||||
if (target->y > height && start->y < height) {
|
||||
target->y = height;
|
||||
target->x = start->x + dx * (height - start->y) / dy;
|
||||
target->z = start->z + dz * (height - start->y) / dy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int32_t ceiling =
|
||||
Room_GetCeiling(sector, target->x, target->y, target->z);
|
||||
if (target->y < ceiling && start->y > ceiling) {
|
||||
target->y = ceiling;
|
||||
target->x = start->x + dx * (ceiling - start->y) / dy;
|
||||
target->z = start->z + dz * (ceiling - start->y) / dy;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t __cdecl LOS_Check(
|
||||
const GAME_VECTOR *const start, GAME_VECTOR *const target)
|
||||
{
|
||||
int32_t los1;
|
||||
int32_t los2;
|
||||
|
||||
const int32_t dx = ABS(target->x - start->x);
|
||||
const int32_t dz = ABS(target->z - start->z);
|
||||
|
||||
if (dz > dx) {
|
||||
los1 = LOS_CheckX(start, target);
|
||||
los2 = LOS_CheckZ(start, target);
|
||||
} else {
|
||||
los1 = LOS_CheckZ(start, target);
|
||||
los2 = LOS_CheckX(start, target);
|
||||
}
|
||||
|
||||
if (!los2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dx == 0 && dz == 0) {
|
||||
target->room_num = start->room_num;
|
||||
}
|
||||
|
||||
const SECTOR *const sector =
|
||||
Room_GetSector(target->x, target->y, target->z, &target->room_num);
|
||||
|
||||
if (!LOS_ClipTarget(start, target, sector)) {
|
||||
return 0;
|
||||
}
|
||||
if (los1 == 1 && los2 == 1) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t __cdecl LOS_CheckSmashable(
|
||||
const GAME_VECTOR *const start, const GAME_VECTOR *const target)
|
||||
{
|
||||
const int32_t dx = target->x - start->x;
|
||||
const int32_t dy = target->y - start->y;
|
||||
const int32_t dz = target->z - start->z;
|
||||
|
||||
for (int32_t i = 0; i < g_LOSNumRooms; i++) {
|
||||
for (int16_t item_num = g_Rooms[g_LOSRooms[i]].item_num;
|
||||
item_num != NO_ITEM; item_num = g_Items[item_num].next_item) {
|
||||
const ITEM *const item = &g_Items[item_num];
|
||||
if (item->status == IS_DEACTIVATED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Item_IsSmashable(item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const DIRECTION direction = Math_GetDirection(item->rot.y);
|
||||
const BOUNDS_16 *const bounds = Item_GetBoundsAccurate(item);
|
||||
const int16_t *x_extent;
|
||||
const int16_t *z_extent = NULL;
|
||||
switch (direction) {
|
||||
case DIR_EAST:
|
||||
case DIR_WEST:
|
||||
x_extent = &bounds->min_z;
|
||||
z_extent = &bounds->min_x;
|
||||
break;
|
||||
case DIR_NORTH:
|
||||
case DIR_SOUTH:
|
||||
x_extent = &bounds->min_x;
|
||||
z_extent = &bounds->min_z;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
assert(z_extent != NULL);
|
||||
|
||||
int32_t failure = 0;
|
||||
if (ABS(dz) > ABS(dx)) {
|
||||
int32_t distance = item->pos.z + z_extent[0] - start->z;
|
||||
for (int32_t j = 0; j < 2; j++) {
|
||||
if ((distance >= 0) == (dz >= 0)) {
|
||||
const int32_t y = dy * distance / dz;
|
||||
if (y <= item->pos.y + bounds->min_y - start->y
|
||||
|| y >= item->pos.y + bounds->max_y - start->y) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32_t x = dx * distance / dz;
|
||||
if (x < item->pos.x + x_extent[0] - start->x) {
|
||||
failure |= 1;
|
||||
} else if (x > item->pos.x + x_extent[1] - start->x) {
|
||||
failure |= 2;
|
||||
} else {
|
||||
return item_num;
|
||||
}
|
||||
}
|
||||
|
||||
distance = item->pos.z + z_extent[1] - start->z;
|
||||
}
|
||||
|
||||
if (failure == 3) {
|
||||
return item_num;
|
||||
}
|
||||
} else {
|
||||
int32_t distance = item->pos.x + x_extent[0] - start->x;
|
||||
for (int32_t j = 0; j < 2; j++) {
|
||||
if ((distance >= 0) == (dx >= 0)) {
|
||||
const int32_t y = dy * distance / dx;
|
||||
if (y <= item->pos.y + bounds->min_y - start->y
|
||||
|| y >= item->pos.y + bounds->max_y - start->y) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int32_t z = dz * distance / dx;
|
||||
if (z < item->pos.z + z_extent[0] - start->z) {
|
||||
failure |= 1;
|
||||
} else if (z > item->pos.z + z_extent[1] - start->z) {
|
||||
failure |= 2;
|
||||
} else {
|
||||
return item_num;
|
||||
}
|
||||
}
|
||||
|
||||
distance = item->pos.x + x_extent[1] - start->x;
|
||||
}
|
||||
|
||||
if (failure == 3) {
|
||||
return item_num;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NO_ITEM;
|
||||
}
|
11
src/tr2/game/los.h
Normal file
11
src/tr2/game/los.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "global/types.h"
|
||||
|
||||
int32_t __cdecl LOS_CheckX(const GAME_VECTOR *start, GAME_VECTOR *target);
|
||||
int32_t __cdecl LOS_CheckZ(const GAME_VECTOR *start, GAME_VECTOR *target);
|
||||
int32_t __cdecl LOS_ClipTarget(
|
||||
const GAME_VECTOR *start, GAME_VECTOR *target, const SECTOR *sector);
|
||||
int32_t __cdecl LOS_Check(const GAME_VECTOR *start, GAME_VECTOR *target);
|
||||
int32_t __cdecl LOS_CheckSmashable(
|
||||
const GAME_VECTOR *start, const GAME_VECTOR *target);
|
222
src/tr2/game/lot.c
Normal file
222
src/tr2/game/lot.c
Normal file
|
@ -0,0 +1,222 @@
|
|||
#include "game/lot.h"
|
||||
|
||||
#include "game/box.h"
|
||||
#include "global/const.h"
|
||||
#include "global/funcs.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void __cdecl LOT_InitialiseArray(void)
|
||||
{
|
||||
g_BaddieSlots =
|
||||
game_malloc(NUM_SLOTS * sizeof(CREATURE), GBUF_CREATURE_DATA);
|
||||
|
||||
for (int32_t i = 0; i < NUM_SLOTS; i++) {
|
||||
CREATURE *const creature = &g_BaddieSlots[i];
|
||||
creature->item_num = NO_ITEM;
|
||||
creature->lot.node =
|
||||
game_malloc(g_BoxCount * sizeof(BOX_NODE), GBUF_CREATURE_LOT);
|
||||
}
|
||||
|
||||
g_SlotsUsed = 0;
|
||||
}
|
||||
|
||||
void __cdecl LOT_DisableBaddieAI(const int16_t item_num)
|
||||
{
|
||||
CREATURE *creature;
|
||||
|
||||
if (item_num == g_Lara.item_num) {
|
||||
creature = g_Lara.creature;
|
||||
g_Lara.creature = NULL;
|
||||
} else {
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
creature = (CREATURE *)item->data;
|
||||
item->data = NULL;
|
||||
}
|
||||
|
||||
if (creature != NULL) {
|
||||
creature->item_num = NO_ITEM;
|
||||
g_SlotsUsed--;
|
||||
}
|
||||
}
|
||||
|
||||
bool __cdecl LOT_EnableBaddieAI(const int16_t item_num, const bool always)
|
||||
{
|
||||
if (g_Lara.item_num == item_num) {
|
||||
if (g_Lara.creature != NULL) {
|
||||
return true;
|
||||
}
|
||||
} else if (g_Items[item_num].data != NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (g_SlotsUsed < NUM_SLOTS) {
|
||||
for (int32_t slot = 0; slot < NUM_SLOTS; slot++) {
|
||||
if (g_BaddieSlots[slot].item_num == NO_ITEM) {
|
||||
LOT_InitialiseSlot(item_num, slot);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
int32_t worst_dist = 0;
|
||||
if (!always) {
|
||||
const ITEM *const item = &g_Items[item_num];
|
||||
const int32_t dx = (item->pos.x - g_Camera.pos.pos.x) >> 8;
|
||||
const int32_t dy = (item->pos.y - g_Camera.pos.pos.y) >> 8;
|
||||
const int32_t dz = (item->pos.z - g_Camera.pos.pos.z) >> 8;
|
||||
worst_dist = SQUARE(dx) + SQUARE(dy) + SQUARE(dz);
|
||||
}
|
||||
|
||||
int32_t worst_slot = -1;
|
||||
for (int32_t slot = 0; slot < NUM_SLOTS; slot++) {
|
||||
const int32_t item_num = g_BaddieSlots[slot].item_num;
|
||||
const ITEM *const item = &g_Items[item_num];
|
||||
const int32_t dx = (item->pos.x - g_Camera.pos.pos.x) >> 8;
|
||||
const int32_t dy = (item->pos.y - g_Camera.pos.pos.y) >> 8;
|
||||
const int32_t dz = (item->pos.z - g_Camera.pos.pos.z) >> 8;
|
||||
const int32_t dist = SQUARE(dx) + SQUARE(dy) + SQUARE(dz);
|
||||
if (dist > worst_dist) {
|
||||
worst_dist = dist;
|
||||
worst_slot = slot;
|
||||
}
|
||||
}
|
||||
|
||||
if (worst_slot < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const CREATURE *const creature = &g_BaddieSlots[worst_slot];
|
||||
g_Items[creature->item_num].status = IS_INVISIBLE;
|
||||
LOT_DisableBaddieAI(creature->item_num);
|
||||
LOT_InitialiseSlot(item_num, worst_slot);
|
||||
return true;
|
||||
}
|
||||
|
||||
void __cdecl LOT_InitialiseSlot(const int16_t item_num, const int32_t slot)
|
||||
{
|
||||
|
||||
CREATURE *const creature = &g_BaddieSlots[slot];
|
||||
ITEM *const item = &g_Items[item_num];
|
||||
|
||||
if (item_num == g_Lara.item_num) {
|
||||
g_Lara.creature = &g_BaddieSlots[slot];
|
||||
} else {
|
||||
item->data = creature;
|
||||
}
|
||||
|
||||
creature->item_num = item_num;
|
||||
creature->mood = MOOD_BORED;
|
||||
creature->neck_rotation = 0;
|
||||
creature->head_rotation = 0;
|
||||
creature->maximum_turn = PHD_DEGREE;
|
||||
creature->flags = 0;
|
||||
creature->enemy = 0;
|
||||
creature->lot.step = STEP_L;
|
||||
creature->lot.drop = -STEP_L * 2;
|
||||
creature->lot.block_mask = BOX_BLOCKED;
|
||||
creature->lot.fly = 0;
|
||||
|
||||
switch (item->object_id) {
|
||||
case O_LARA:
|
||||
creature->lot.step = WALL_L * 20;
|
||||
creature->lot.drop = -WALL_L * 20;
|
||||
creature->lot.fly = STEP_L;
|
||||
break;
|
||||
|
||||
case O_SHARK:
|
||||
case O_BARRACUDA:
|
||||
case O_DIVER:
|
||||
case O_JELLY:
|
||||
case O_CROW:
|
||||
case O_EAGLE:
|
||||
creature->lot.step = WALL_L * 20;
|
||||
creature->lot.drop = -WALL_L * 20;
|
||||
creature->lot.fly = STEP_L / 16;
|
||||
if (item->object_id == O_SHARK) {
|
||||
creature->lot.block_mask = BOX_BLOCKABLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case O_WORKER_3:
|
||||
case O_WORKER_4:
|
||||
case O_YETI:
|
||||
creature->lot.step = WALL_L;
|
||||
creature->lot.drop = -WALL_L;
|
||||
break;
|
||||
|
||||
case O_SPIDER:
|
||||
case O_SKIDOO_ARMED:
|
||||
creature->lot.step = WALL_L / 2;
|
||||
creature->lot.drop = -WALL_L;
|
||||
break;
|
||||
|
||||
case O_DINO:
|
||||
creature->lot.block_mask = BOX_BLOCKABLE;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
LOT_ClearLOT(&creature->lot);
|
||||
|
||||
if (item_num != g_Lara.item_num) {
|
||||
LOT_CreateZone(item);
|
||||
}
|
||||
|
||||
g_SlotsUsed++;
|
||||
}
|
||||
|
||||
void __cdecl LOT_CreateZone(ITEM *const item)
|
||||
{
|
||||
CREATURE *const creature = item->data;
|
||||
|
||||
int16_t *zone;
|
||||
int16_t *flip;
|
||||
if (creature->lot.fly) {
|
||||
zone = g_FlyZone[0];
|
||||
flip = g_FlyZone[1];
|
||||
} else {
|
||||
zone = g_GroundZone[BOX_ZONE(creature->lot.step)][0];
|
||||
flip = g_GroundZone[BOX_ZONE(creature->lot.step)][1];
|
||||
}
|
||||
|
||||
const ROOM *const r = &g_Rooms[item->room_num];
|
||||
const int32_t z_sector = (item->pos.z - r->pos.z) >> WALL_SHIFT;
|
||||
const int32_t x_sector = (item->pos.x - r->pos.x) >> WALL_SHIFT;
|
||||
item->box_num = r->sectors[z_sector + x_sector * r->size.z].box;
|
||||
|
||||
int16_t zone_num = zone[item->box_num];
|
||||
int16_t flip_num = flip[item->box_num];
|
||||
|
||||
creature->lot.zone_count = 0;
|
||||
BOX_NODE *node = creature->lot.node;
|
||||
for (int32_t i = 0; i < g_BoxCount; i++) {
|
||||
if (zone[i] == zone_num || flip[i] == flip_num) {
|
||||
node->box_num = i;
|
||||
node++;
|
||||
creature->lot.zone_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __cdecl LOT_ClearLOT(LOT_INFO *const lot)
|
||||
{
|
||||
lot->search_num = 0;
|
||||
lot->head = NO_BOX;
|
||||
lot->tail = NO_BOX;
|
||||
lot->target_box = NO_BOX;
|
||||
lot->required_box = NO_BOX;
|
||||
|
||||
for (int32_t i = 0; i < g_BoxCount; i++) {
|
||||
BOX_NODE *const node = &lot->node[i];
|
||||
node->next_expansion = NO_BOX;
|
||||
node->exit_box = NO_BOX;
|
||||
node->search_num = 0;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue