TombEngine/TombEngine/Game/control/control.cpp
TrainWrack f4570211a5 Squashed commit of the following:
commit 77d0865c8a
Author: Nemoel-Tomo <tomo_669@hotmail.com>
Date:   Sun Feb 16 08:20:58 2025 +0100

    Waterfall emitter formatting fix (#1570)

commit 92329741ad
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Feb 15 07:46:37 2025 +0100

    Clarify EmitSpotLight description

commit 562637f599
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Fri Feb 14 23:10:40 2025 +0100

    Fixed #1574

commit a64825b6f1
Author: Sezz <sezzary@outlook.com>
Date:   Fri Feb 14 04:30:00 2025 +1100

    Add missing shift in EmitParticle()

commit 455d547de7
Author: Sezz <sezzary@outlook.com>
Date:   Fri Feb 14 04:25:55 2025 +1100

    Add lock to parallel task class

commit 918237113f
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 13 14:59:16 2025 +1100

    Make script utils more idiomatic to C++

commit b78376b0ab
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 13 12:44:06 2025 +1100

    Use correct angle conversion in EmitPatricle()

commit 3e00302ade
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 13 05:38:56 2025 +1100

    Update Rotation.cpp

commit f1c1fd2f63
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 13 04:12:34 2025 +1100

    Add Lerp() method to script Rotation class

commit 6ef9675bcb
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Feb 12 09:08:45 2025 +0100

    Update CHANGELOG.md

commit ca56f62f54
Author: Sezz <sezzary@outlook.com>
Date:   Wed Feb 12 18:59:54 2025 +1100

    Multithreading (#1541)

    * Create Worker class for multithreading

    * Update TombEngine.vcxproj

    * Rename GetWorkerCount() to GetThreadCount()

    * Add ProcessInParallel template for vectors

    * Add multiThreaded flag to settings, process sprites in parallel

    * Update Flow.Settings.html

    * Refine WorkerManager class conventions; deinit threads properly

    * Don't require explicit destruction

    * Address basic PR notes

    * Update Worker.cpp

    * Simplify ThreadManager class

    * Add method for running single task

    * Use singleton pattern; use more appropriate Controller suffix

    * Update WorkerController template method

    * Revise method

    * Handle exception in ~WorkerController()

    * Grammar

    * Correctly init single-threaded mode

    * Update CHANGELOG.md

    * Defer thread init until g_GameFlow is valid

    * unsigned int -> int

    * Rename class

    ---------

    Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>

commit d37ac17a39
Author: Sezz <sezzary@outlook.com>
Date:   Wed Feb 12 17:36:12 2025 +1100

    Formatting

commit 62ce2f043d
Author: Sezz <sezzary@outlook.com>
Date:   Tue Feb 11 18:05:07 2025 +1100

    Fix bridges moving the player when the player is underwater

commit 0bb9af9894
Author: Sezz <sezzary@outlook.com>
Date:   Tue Feb 11 17:31:51 2025 +1100

    puzzle_keys.cpp formatting

commit 7d18d7506f
Author: davidmarr <116632612+davidmarr@users.noreply.github.com>
Date:   Sun Feb 9 15:58:15 2025 +0100

    Update Type module (#1569)

    * Update VolumeObject.cpp

    fixed Volume:GetActive() method

    * Update CHANGELOG.md

    * function description LevelFuncs.OnUseItem

    * Revert "function description LevelFuncs.OnUseItem"

    This reverts commit 2478afca68.

    * Update Type.lua

commit 909f631c2f
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Feb 8 11:04:40 2025 +0100

    Fixed mistake in electricity rendering

commit a840c2200c
Merge: 2c6331f58 a31faffec
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 8 02:58:13 2025 +1100

    Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop

commit 2c6331f583
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 8 02:58:03 2025 +1100

    Deprecate CalculateDistance() script function

commit a31faffec5
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Fri Feb 7 08:31:14 2025 +0100

    Update CHANGELOG.md

commit 1f81ccf44d
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Thu Feb 6 18:05:25 2025 -0500

    Diary module (TEN side) (#1509)

    * Update to bug report form

    * Update bug_report.yaml

    * Update AUTHORS.md

    - Tomo (general coding, special FX coding, bug fixing)

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Update bug_report.yaml

    * Add files via upload

    * Add files via upload

    * Add ID_DIARY_SPRITES (1384) and DIARY_ENTRY_SPRITES (1385)

    * Delete Scripts/Engine/Diarymodule.lua

    * Add files via upload

    * Add files via upload

    * Add files via upload

    * Add files via upload

    * Delete Scripts/Engine/CustomBar.lua

    * Add files via upload

    * Update CustomDiary.lua

    * Add files via upload

    * Add files via upload

    * Add files via upload

    * Update CustomDiary.lua

    * Upload

    * Update CHANGELOG.md

    * Revisions

    * Doc revisions

    * Remove GameVars.Engine everywhere.

    * Added error warnings by integrating type module

    * Update bug_report.yaml

    * Fixed the bug with GameVars resetting each level.

    * Added .Engine back.

    * Added missing checks.

    * Removed nil from textOptions loop

    * Added full TEN name for printlogs.

    * LatestChanges

    * Added section for import.

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>

commit 34ff933e5b
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 18:48:45 2025 +1100

    Update LogicHandler.cpp

commit 5500b13659
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 18:47:33 2025 +1100

    Update LogicHandler.cpp

commit acb1bb1518
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 18:46:16 2025 +1100

    Enforce proper convention for the few Lua table constants that didn't use it yet

commit 89d5b74298
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Thu Feb 6 02:09:53 2025 -0500

    Fix Trigger Triggerer (#1565)

    * Fix

    * Update CHANGELOG.md

    * Update trigger.cpp

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>

commit 8316062e3a
Merge: 5f447d95c 599a651b6
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Thu Feb 6 06:03:41 2025 +0000

    Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop

commit 5f447d95c5
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Thu Feb 6 06:03:18 2025 +0000

    Fix stopwatch display in inventory (due to removal of trademarked item)

    Now the trademarked item from Tomb Raider Chronicles has been removed and replaced with the stopwatch from Tomb Raider III

commit 599a651b6f
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 16:56:19 2025 +1100

    Update lens flare and starfield Lua docs

commit 94ede801bc
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 16:35:59 2025 +1100

    Misc. script doc tidying; move some script classes to namespaces

commit be8048407e
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Wed Feb 5 23:51:35 2025 -0500

    Underwater keys/puzzles (#1529)

    * Update to bug report form

    * Update bug_report.yaml

    * Update AUTHORS.md

    - Tomo (general coding, special FX coding, bug fixing)

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Update bug_report.yaml

    * First Push

    * Updae LaraStruct to use animation 280

    * Update puzzles_keys.cpp

    * Update CHANGELOG.md

    * Squashed commit of the following:

    commit e50a4f8c27
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 20:07:56 2024 -0500

        Doc Revision

    commit 39a6e713ce
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 20:02:47 2024 -0500

        Doc revisions

    commit c330343820
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 19:52:59 2024 -0500

        Update CHANGELOG.md

    commit ad62d7b605
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 19:42:32 2024 -0500

        Check

    commit e135195641
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 19:26:23 2024 -0500

        First Commit

    commit 4a6c6ee270
    Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
    Date:   Wed Dec 25 19:19:05 2024 -0500

        Update EffectsFunctions.cpp

    * Revert "Squashed commit of the following:"

    This reverts commit ff2e49c6ed.

    * Formatting.

    * Formatting.

    * Update CHANGELOG.md

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
    Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
    Co-authored-by: Jakub <kubabilinski03@gmail.com>
    Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
    Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Co-authored-by: TrainWreck <barry@DESKTOP-HC53CPN>

commit 106787d911
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 04:34:28 2025 +1100

    Doc corrections

commit 737f5aa742
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 04:28:58 2025 +1100

    Make getters const

commit 2b41ae743a
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 02:35:16 2025 +1100

    Tidy up Lua doc for Static class

commit 437ce7c139
Author: Sezz <sezzary@outlook.com>
Date:   Thu Feb 6 01:22:59 2025 +1100

    Complete short -> int conversions for Lua

commit 237ceca0f4
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Wed Feb 5 08:04:23 2025 +0100

    Fixed custom shatter sounds

commit 64e0c303ba
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Feb 4 20:58:09 2025 +0100

    Update CHANGELOG.md

commit d2b692cb3b
Author: Sezz <sezzary@outlook.com>
Date:   Mon Feb 3 16:15:41 2025 +1100

    ShadowMode::Lara -> ShadowMode::Player

commit ebb20121ac
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 23:57:54 2025 +1100

    -1 -> NO_VALUE and other formatting

commit 4a6f30a152
Author: Jakub <80340234+Jakub768@users.noreply.github.com>
Date:   Sat Feb 1 12:07:10 2025 +0000

    Update LICENSE

commit 45d46e0e6b
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 22:55:35 2025 +1100

    Fix display pickup string not being interpolated in 60FPS mode

commit 903fdf288f
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 22:40:50 2025 +1100

    Optimise BitField class (#1511)

    * Optimise BitField class

    * DebugBuild -> DEBUG_BUILD

    * Fix merge error

commit 48902b00a9
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Sat Feb 1 06:23:04 2025 -0500

    Emit air bubble exposed (#1537)

    * Update to bug report form

    * Update bug_report.yaml

    * Update AUTHORS.md

    - Tomo (general coding, special FX coding, bug fixing)

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Update bug_report.yaml

    * Update EffectsFunctions.cpp

    * First Commit

    * Check

    * Update CHANGELOG.md

    * Doc revisions

    * Doc Revision

    * Update EffectsFunctions.cpp

    * Remove room from arguements.

    * Doc clarification.

    * Make Size and Amplitude Optional

    * Update EffectsFunctions.cpp

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
    Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
    Co-authored-by: Jakub <kubabilinski03@gmail.com>
    Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
    Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Co-authored-by: Sezz <sezzary@outlook.com>

commit fa0e125f59
Author: TrainWrack <120750885+TrainWrack@users.noreply.github.com>
Date:   Sat Feb 1 06:22:49 2025 -0500

    Emit particle upgrade (#1542)

    * Update to bug report form

    * Update bug_report.yaml

    * Update AUTHORS.md

    - Tomo (general coding, special FX coding, bug fixing)

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Update bug_report.yaml

    * Expose ObjectSlot

    * Docs

    * Code Update

    * FinalPush

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Update CHANGELOG.md

    * Expose startRot

    * Expose startRot

    * Remove Space.

    * Code cleanup, revise doc comment

    * Nicer defaults in doc

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
    Co-authored-by: Nemoel-Tomo <tomo_669@hotmail.com>
    Co-authored-by: Jakub <kubabilinski03@gmail.com>
    Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com>
    Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
    Co-authored-by: Sezz <sezzary@outlook.com>

commit bcbe216508
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 20:17:11 2025 +1100

    Update Vec3.cpp

commit 06c33908d9
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 20:13:34 2025 +1100

    Explicit conversion

commit c99b8abf25
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 20:01:55 2025 +1100

    Add Translate() functions to script Vec2 and Vec3 classes

commit d8c646fabd
Author: Sezz <sezzary@outlook.com>
Date:   Sat Feb 1 17:47:29 2025 +1100

    Start restructuring script Moveable class

commit 98b01ff64a
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Thu Jan 30 22:25:57 2025 +0100

    Fixed #1562

commit d163d1ec30
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Mon Jan 27 19:27:08 2025 +0100

    Fixed #1558

commit 6ef8a562e5
Author: Sezz <sezzary@outlook.com>
Date:   Tue Jan 28 04:10:12 2025 +1100

    Update doc comment for moveable

commit d919e16790
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sat Jan 25 23:03:28 2025 +0100

    Fixed player model submerging into the floor while swimming underwater

commit faf17cd03a
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Fri Jan 24 23:18:14 2025 +0100

    Fixed #1557

commit 5f402e380b
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Fri Jan 24 22:55:08 2025 +0100

    Fixed #1556

commit ae20a49eb2
Author: Sezz <sezzary@outlook.com>
Date:   Thu Jan 23 17:02:26 2025 +1100

    Update VS hint comments for various classes

commit fb97628ac9
Author: Sezz <sezzary@outlook.com>
Date:   Thu Jan 23 16:48:46 2025 +1100

    Add `Scale` field to `Pose` class (#1546)

    * Reapply "Add functional Scale field to Pose class"

    This reverts commit 92305a5d25.

    * Update lara.cpp

commit fc0c260ea1
Author: Sezz <sezzary@outlook.com>
Date:   Thu Jan 23 15:25:09 2025 +1100

    Update script Rotation class and its doc

commit 0dd0061a94
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Jan 21 20:55:57 2025 +0100

    Update CHANGELOG.md

commit 7735f660ab
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Jan 21 20:49:32 2025 +0100

    Fixed #1553

commit 5764965230
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Jan 21 20:47:06 2025 +0100

    Fixed #1554

commit b55675b9ce
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Tue Jan 21 20:46:47 2025 +0100

    Fixed #1552

commit 7be096f86a
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Sun Jan 19 05:41:05 2025 +0100

    Fixed dynamic light shadows not being handled correctly

commit db2649e936
Author: Sezz <sezzary@outlook.com>
Date:   Sat Jan 18 19:54:04 2025 +1100

    Fix two block platform sometimes not traversing room portals correctly; cleanup

commit ff41900bd9
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 12 16:35:44 2025 +0000

    Updated to reflect develop branch after merging Pull Request

commit 2a89abe66d
Author: Nemoel-Tomo <tomo_669@hotmail.com>
Date:   Sun Jan 12 17:15:45 2025 +0100

    Tr1 electric ball (#1413)

    Implementation of the Electric Ball from Tomb Raider I, inside Level 5: St Francis Folly

commit a97548467e
Author: Nemoel-Tomo <tomo_669@hotmail.com>
Date:   Fri Jan 10 23:27:48 2025 +0100

    Waterfall_Emitter (#1359)

    * waterfall emitter. warning for the test I changed waterfallmist object

    * test

    * update

    * update

    * update

    * formatting

    * formatting

    * formatting

    * update mist color

    * import develop

    * bugfix sprite

    * added mor sprite particles

    * waterfall intensity

    * update waterfall to be more filled

    * update

    * Create a new sprite sequence for waterfall

    * Fixed texture order

    * removed GetParticleDistanceFade() from waterfall

    * Order

    * Update Particle struct; update waterfall

    * Update Particle struct

    * Simplify waterfall emitter

    * Simplify waterfall emitter

    * Simplify waterfall emitter

    * Simplify

    * Fix waterfall density; move sprite slot

    * Update Waterfall.cpp

    * fixed some small bugs with the waterfall after simplify

    * commit

    * imported develop 60fps branch

    * fixed waterfall errors

    * update

    * commit

    * update/test

    * update Waterfall

    * fixed last bugs

    * formatting

    * added option for sound

    * update

    * update, fixing errors

    * update

    * formatting

    * Formatting

    * fixed color issue, now only savegame is left

    * formatting

    * added new fields to savegame

    * targetpos is now Vector3

    * import develop

    * Fixes according to GH notes + move waterfall-specific code out of effects.cpp

    * Rollback object ID rename, add missing enum

    * renamed ID_WATERFALL_ into ID_WATERFALL_SPRITES

    * fixed SpriteSeqID for spark in spark.cpp

    * Update CHANGELOG.md

    * rewritten waterfall code

    * Update waterfall impact point

    ---------

    Co-authored-by: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
    Co-authored-by: Sezz <sezzary@outlook.com>
    Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>

commit bfe07f6e21
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 5 16:45:30 2025 +0000

    Bump version in Lua API for development

commit 4dce157665
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 5 16:27:38 2025 +0000

    Bump Tomb Editor version for next dev cycle

    Amended also in the Development branch for TE

commit c25c7a1c96
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 5 15:10:17 2025 +0000

    Update CHANGELOG.md

commit 2da7401c28
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sun Jan 5 15:09:41 2025 +0000

    Update changelog with template for the next version.

commit 66d7e51f5d
Author: Stranger1992 <84292688+Stranger1992@users.noreply.github.com>
Date:   Sat Jan 4 18:41:11 2025 +0000

    Bump dev version to 1.7.2

    This has been done to draw a line post-release and any commits after this to form the next release. Version number may change.

commit cebc0175c9
Author: Sezz <sezzary@outlook.com>
Date:   Sat Jan 4 17:03:46 2025 +1100

    Move splash effect to its own file

commit b81b28039c
Author: Sezz <sezzary@outlook.com>
Date:   Sat Jan 4 16:03:07 2025 +1100

    Fix gravity being applied when vaulting on the same frame as the player lands

commit 5444ede2a8
Author: Sezz <sezzary@outlook.com>
Date:   Sat Jan 4 15:51:52 2025 +1100

    Reliably stop at edge when running at it while holding Walk

commit 43d5bb9639
Author: Sezz <sezzary@outlook.com>
Date:   Fri Jan 3 23:31:59 2025 +1100

    Update CHANGELOG.md

commit fb7e4dce70
Merge: 9baf5222f 12ac1219e
Author: Sezz <sezzary@outlook.com>
Date:   Fri Jan 3 23:06:28 2025 +1100

    Merge branch 'develop' of https://github.com/MontyTRC89/TombEngine into develop

commit 9baf5222f8
Author: Sezz <sezzary@outlook.com>
Date:   Fri Jan 3 23:06:25 2025 +1100

    Reset IsAirborne flag properly when exiting fly cheat

commit 12ac1219e9
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Thu Jan 2 13:10:57 2025 +0100

    Update spark.cpp

commit 7f071b5ec5
Author: Lwmte <3331699+Lwmte@users.noreply.github.com>
Date:   Thu Jan 2 11:15:04 2025 +0100

    Port additional ricochet effect from tomb4
2025-02-20 20:07:05 -05:00

812 lines
20 KiB
C++

#include "framework.h"
#include "Game/control/control.h"
#include "Game/camera.h"
#include "Game/collision/collide_room.h"
#include "Game/control/flipeffect.h"
#include "Game/control/lot.h"
#include "Game/control/volume.h"
#include "Game/effects/debris.h"
#include "Game/effects/Blood.h"
#include "Game/effects/Bubble.h"
#include "Game/effects/DisplaySprite.h"
#include "Game/effects/Drip.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/explosion.h"
#include "Game/effects/Footprint.h"
#include "Game/effects/Hair.h"
#include "Game/effects/Ripple.h"
#include "Game/effects/simple_particle.h"
#include "Game/effects/smoke.h"
#include "Game/effects/spark.h"
#include "Game/effects/Splash.h"
#include "Game/effects/Streamer.h"
#include "Game/effects/tomb4fx.h"
#include "Game/effects/weather.h"
#include "Game/Gui.h"
#include "Game/Hud/Hud.h"
#include "Game/Lara/lara.h"
#include "Game/Lara/lara_cheat.h"
#include "Game/Lara/lara_helpers.h"
#include "Game/Lara/lara_one_gun.h"
#include "Game/items.h"
#include "Game/pickup/pickup.h"
#include "Game/room.h"
#include "Game/savegame.h"
#include "Game/Setup.h"
#include "Game/spotcam.h"
#include "Math/Math.h"
#include "Objects/Effects/LensFlare.h"
#include "Objects/Effects/tr4_locusts.h"
#include "Objects/Generic/Object/objects.h"
#include "Objects/Generic/Object/rope.h"
#include "Objects/Generic/Switches/generic_switch.h"
#include "Objects/TR3/Entity/FishSwarm.h"
#include "Objects/TR4/Entity/tr4_beetle_swarm.h"
#include "Objects/TR5/Emitter/tr5_bats_emitter.h"
#include "Objects/TR5/Emitter/tr5_rats_emitter.h"
#include "Objects/TR5/Emitter/tr5_spider_emitter.h"
#include "Objects/TR5/Trap/LaserBarrier.h"
#include "Objects/TR5/Trap/LaserBeam.h"
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
#include "Scripting/Include/Objects/ScriptInterfaceObjectsHandler.h"
#include "Scripting/Include/ScriptInterfaceGame.h"
#include "Scripting/Include/Strings/ScriptInterfaceStringsHandler.h"
#include "Sound/sound.h"
#include "Specific/clock.h"
#include "Specific/Input/Input.h"
#include "Specific/level.h"
#include "Specific/winmain.h"
using namespace std::chrono;
using namespace TEN::Effects;
using namespace TEN::Effects::Blood;
using namespace TEN::Effects::Bubble;
using namespace TEN::Effects::DisplaySprite;
using namespace TEN::Effects::Drip;
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Explosion;
using namespace TEN::Effects::Footprint;
using namespace TEN::Effects::Hair;
using namespace TEN::Effects::Ripple;
using namespace TEN::Effects::Smoke;
using namespace TEN::Effects::Spark;
using namespace TEN::Effects::Splash;
using namespace TEN::Effects::Streamer;
using namespace TEN::Entities::Creatures::TR3;
using namespace TEN::Entities::Generic;
using namespace TEN::Entities::Switches;
using namespace TEN::Entities::Traps;
using namespace TEN::Entities::TR4;
using namespace TEN::Collision::Floordata;
using namespace TEN::Control::Volumes;
using namespace TEN::Hud;
using namespace TEN::Input;
using namespace TEN::Math;
using namespace TEN::Renderer;
using namespace TEN::Entities::Creatures::TR3;
using namespace TEN::Entities::Effects;
constexpr auto DEATH_NO_INPUT_TIMEOUT = 10 * FPS;
constexpr auto DEATH_INPUT_TIMEOUT = 3 * FPS;
int GlobalCounter = 0;
bool InitializeGame = false;
bool DoTheGame = false;
bool JustLoaded = false;
bool ThreadEnded = false;
int RequiredStartPos;
int CurrentLevel;
int NextLevel;
bool InItemControlLoop;
short ItemNewRoomNo;
short ItemNewRooms[MAX_ROOMS];
short NextItemActive;
short NextItemFree;
short NextFxActive;
short NextFxFree;
int ControlPhaseTime;
void DrawPhase(bool isTitle, float interpolationFactor)
{
if (isTitle)
{
g_Renderer.RenderTitle(interpolationFactor);
}
else if (g_GameFlow->CurrentFreezeMode == FreezeMode::None)
{
g_Renderer.Render(interpolationFactor);
}
else
{
g_Renderer.RenderFreezeMode(interpolationFactor, g_GameFlow->CurrentFreezeMode == FreezeMode::Full);
}
g_Renderer.Lock();
}
GameStatus GamePhase(bool insideMenu)
{
auto time1 = std::chrono::high_resolution_clock::now();
bool isTitle = (CurrentLevel == 0);
g_Renderer.PrepareScene();
g_Renderer.SaveOldState();
ClearFires();
ClearLensFlares();
ClearAllDisplaySprites();
SetupInterpolation();
PrepareCamera();
RegeneratePickups();
g_GameStringsHandler->ProcessDisplayStrings(DELTA_TIME);
// Controls are polled before OnLoop to allow input data to be overwritten by script API methods.
HandleControls(isTitle);
// Pre-loop script and event handling.
g_GameScript->OnLoop(DELTA_TIME, false); // TODO: Don't use DELTA_TIME constant with high framerate.
HandleAllGlobalEvents(EventType::Loop, (Activator)short(LaraItem->Index));
// Queued input actions are read again after OnLoop, so that remaining control loop can immediately register
// emulated keypresses from the script.
ApplyActionQueue();
// Control lock is processed after handling scripts because builder may want to process input externally
// while locking player from input.
if (!isTitle && Lara.Control.IsLocked)
ClearAllActions();
// Item update should happen before camera update, so potential flyby/track camera triggers
// are processed correctly.
UpdateAllItems();
UpdateAllEffects();
UpdateLara(LaraItem, isTitle);
g_GameScriptEntities->TestCollidingObjects();
// Smash shatters and clear stopper flags under them.
UpdateShatters();
// Clear last selected item in inventory (must be after on loop event handling, so they can detect that).
g_Gui.CancelInventorySelection();
// Update weather.
Weather.Update();
// Update effects.
StreamerEffect.Update();
UpdateWibble();
UpdateSparks();
UpdateFireSparks();
UpdateSmoke();
UpdateBlood();
UpdateBubbles();
UpdateDebris();
UpdateGunShells();
UpdateFootprints();
UpdateSplashes();
UpdateElectricityArcs();
UpdateHelicalLasers();
UpdateDrips();
UpdateRats();
UpdateRipples();
UpdateBats();
UpdateSpiders();
UpdateSparkParticles();
UpdateSmokeParticles();
UpdateSimpleParticles();
UpdateExplosionParticles();
UpdateShockwaves();
UpdateBeetleSwarm();
UpdateLocusts();
UpdateUnderwaterBloodParticles();
UpdateFishSwarm();
UpdateGlobalLensFlare();
// Update HUD.
g_Hud.Update(*LaraItem);
UpdateFadeScreenAndCinematicBars();
// Rumble screen (like in submarine level of TRC).
if (g_GameFlow->GetLevel(CurrentLevel)->Rumble)
RumbleScreen();
DoFlipEffect(FlipEffect, LaraItem);
PlaySoundSources();
Sound_UpdateScene();
auto gameStatus = GameStatus::Normal;
if (!insideMenu)
{
// Handle inventory, pause, load, save screens.
gameStatus = HandleMenuCalls(isTitle);
// Handle global input events.
if (gameStatus == GameStatus::Normal)
gameStatus = HandleGlobalInputEvents(isTitle);
}
if (gameStatus != GameStatus::Normal)
{
// Call post-loop callbacks last time and end level.
g_GameScript->OnLoop(DELTA_TIME, true);
g_GameScript->OnEnd(gameStatus);
HandleAllGlobalEvents(EventType::End, (Activator)short(LaraItem->Index));
}
else
{
// Post-loop script and event handling.
g_GameScript->OnLoop(DELTA_TIME, true);
}
UpdateCamera();
// Clear savegame loaded flag.
JustLoaded = false;
// Update timers.
SaveGame::Statistics.Game.TimeTaken++;
SaveGame::Statistics.Level.TimeTaken++;
GlobalCounter++;
auto time2 = std::chrono::high_resolution_clock::now();
ControlPhaseTime = (std::chrono::duration_cast<std::chrono::nanoseconds>(time2 - time1)).count() / 1000000;
return gameStatus;
}
GameStatus FreezePhase()
{
// If needed when first entering freeze mode, do initialization.
if (g_GameFlow->LastFreezeMode == FreezeMode::None)
{
// Capture the screen for drawing it as a background.
if (g_GameFlow->CurrentFreezeMode == FreezeMode::Full)
g_Renderer.DumpGameScene(SceneRenderMode::NoHud);
StopRumble();
}
// Update last freeze mode here, so that items won't update inside freeze loop.
g_GameFlow->LastFreezeMode = g_GameFlow->CurrentFreezeMode;
g_Renderer.PrepareScene();
g_Renderer.SaveOldState();
ClearLensFlares();
ClearAllDisplaySprites();
SetupInterpolation();
PrepareCamera();
g_GameStringsHandler->ProcessDisplayStrings(DELTA_TIME);
// Track previous player animation to queue hair update if needed.
int lastAnimNumber = LaraItem->Animation.AnimNumber;
// Poll controls and call scripting events.
HandleControls(false);
g_GameScript->OnFreeze();
HandleAllGlobalEvents(EventType::Freeze, (Activator)short(LaraItem->Index));
// Partially update scene if not using full freeze mode.
if (g_GameFlow->LastFreezeMode != FreezeMode::Full)
{
if (g_GameFlow->LastFreezeMode == FreezeMode::Player)
UpdateLara(LaraItem, false);
UpdateAllItems();
UpdateGlobalLensFlare();
UpdateCamera();
PlaySoundSources();
Sound_UpdateScene();
}
// HACK: Update player hair if animation was switched in spectator mode.
// Needed for photo mode and other similar functionality.
if (g_GameFlow->LastFreezeMode == FreezeMode::Spectator &&
lastAnimNumber != LaraItem->Animation.AnimNumber)
{
lastAnimNumber = LaraItem->Animation.AnimNumber;
for (int i = 0; i < FPS; i++)
HairEffect.Update(*LaraItem);
}
// Update last freeze mode again, as it may have been changed in a script.
g_GameFlow->LastFreezeMode = g_GameFlow->CurrentFreezeMode;
return GameStatus::Normal;
}
GameStatus ControlPhase(bool insideMenu)
{
// For safety, only allow to break game loop in non-title levels.
if (g_GameFlow->CurrentFreezeMode == FreezeMode::None || CurrentLevel == 0)
{
return GamePhase(insideMenu);
}
else
{
return FreezePhase();
}
}
unsigned CALLBACK GameMain(void *)
{
TENLog("Starting GameMain()...", LogLevel::Info);
TimeInit();
// Do fixed-time title image.
if (g_GameFlow->IntroImagePath.empty())
{
TENLog("Intro image path not set.", LogLevel::Warning);
}
else
{
g_Renderer.RenderTitleImage();
}
// Execute Lua gameflow and play game.
g_GameFlow->DoFlow();
DoTheGame = false;
// Finish thread.
PostMessage(WindowsHandle, WM_CLOSE, NULL, NULL);
EndThread();
return true;
}
GameStatus DoLevel(int levelIndex, bool loadGame)
{
bool isTitle = !levelIndex;
TENLog(isTitle ? "DoTitle" : "DoLevel", LogLevel::Info);
// Load level. Fall back to title if unsuccessful.
if (!LoadLevelFile(levelIndex))
return (isTitle ? GameStatus::ExitGame : GameStatus::ExitToTitle);
// Initialize items, effects, lots, and cameras.
HairEffect.Initialize();
InitializeFXArray();
InitializeCamera();
InitializeSpotCamSequences(isTitle);
InitializeItemBoxData();
// Initialize scripting.
InitializeScripting(levelIndex, loadGame);
InitializeNodeScripts();
// Initialize menu and inventory state.
g_Gui.Initialize();
// Initialize game variables and optionally load game.
InitializeOrLoadGame(loadGame);
// DoGameLoop() returns only when level has ended.
return DoGameLoop(levelIndex);
}
void UpdateShatters()
{
if (!SmashedMeshCount)
return;
do
{
SmashedMeshCount--;
auto* floor = GetFloor(
SmashedMesh[SmashedMeshCount]->pos.Position.x,
SmashedMesh[SmashedMeshCount]->pos.Position.y,
SmashedMesh[SmashedMeshCount]->pos.Position.z,
&SmashedMeshRoom[SmashedMeshCount]);
TestTriggers(SmashedMesh[SmashedMeshCount]->pos.Position.x,
SmashedMesh[SmashedMeshCount]->pos.Position.y,
SmashedMesh[SmashedMeshCount]->pos.Position.z,
SmashedMeshRoom[SmashedMeshCount], true);
TestVolumes(SmashedMeshRoom[SmashedMeshCount], SmashedMesh[SmashedMeshCount]);
floor->Stopper = false;
SmashedMesh[SmashedMeshCount] = 0;
}
while (SmashedMeshCount != 0);
}
void KillMoveItems()
{
if (ItemNewRoomNo > 0)
{
for (int i = 0; i < ItemNewRoomNo; i++)
{
int itemNumber = ItemNewRooms[i * 2];
if (itemNumber >= 0)
{
ItemNewRoom(itemNumber, ItemNewRooms[(i * 2) + 1]);
}
else
{
KillItem(itemNumber & 0x7FFF);
}
}
}
ItemNewRoomNo = 0;
}
void KillMoveEffects()
{
if (ItemNewRoomNo > 0)
{
for (int i = 0; i < ItemNewRoomNo; i++)
{
int itemNumber = ItemNewRooms[i * 2];
if (itemNumber >= 0)
{
EffectNewRoom(itemNumber, ItemNewRooms[(i * 2) + 1]);
}
else
{
KillEffect(itemNumber & 0x7FFF);
}
}
}
ItemNewRoomNo = 0;
}
// NOTE: No one should use this ever again.
int GetRandomControl()
{
return Random::GenerateInt();
}
int GetRandomDraw()
{
return Random::GenerateInt();
}
void CleanUp()
{
// Reset oscillator seed.
Wibble = 0;
// Clear player lock, otherwise controls will lock if user exits to title while playing flyby with locked controls.
Lara.Control.IsLocked = false;
// Resets lightning and wind parameters to avoid holding over previous weather to new level.
Weather.Clear();
// Clear creatures, otherwise list of active creatures from previous level will spill into new level.
ActiveCreatures.clear();
// Clear ropes.
Ropes.clear();
// Clear camera data.
ClearSpotCamSequences();
ClearCinematicBars();
// Clear effects.
StreamerEffect.Clear();
ClearUnderwaterBloodParticles();
ClearBubbles();
ClearAllDisplaySprites();
ClearFootprints();
ClearDrips();
ClearRipples();
ClearLaserBarrierEffects();
ClearLaserBeamEffects();
DisableSmokeParticles();
DisableSparkParticles();
DisableDebris();
// Clear swarm enemies.
ClearSwarmEnemies(nullptr);
// Clear HUD.
g_Hud.Clear();
// Clear soundtrack masks.
ClearSoundTrackMasks();
// Clear all remaining renderer data.
g_Renderer.ClearScene();
g_Renderer.SetPostProcessMode(PostProcessMode::None);
g_Renderer.SetPostProcessStrength(1.0f);
g_Renderer.SetPostProcessTint(Vector3::One);
// Reset Itemcamera
ClearObjCamera();
}
void InitializeScripting(int levelIndex, bool loadGame)
{
TENLog("Loading level script...", LogLevel::Info);
g_GameStringsHandler->ClearDisplayStrings();
g_GameScript->ResetScripts(!levelIndex || loadGame);
const auto& level = *g_GameFlow->GetLevel(levelIndex);
// Run level script if it exists.
if (!level.ScriptFileName.empty())
{
auto levelScriptName = g_GameFlow->GetGameDir() + level.ScriptFileName;
if (std::filesystem::is_regular_file(levelScriptName))
{
g_GameScript->ExecuteScriptFile(levelScriptName);
}
else
{
TENLog("Level script not found: " + levelScriptName, LogLevel::Warning);
}
g_GameScript->InitCallbacks();
g_GameStringsHandler->SetCallbackDrawString([](const std::string& key, D3DCOLOR color, const Vec2& pos, float scale, int flags)
{
g_Renderer.AddString(
key,
Vector2(
(pos.x / g_Configuration.ScreenWidth) * DISPLAY_SPACE_RES.x,
(pos.y / g_Configuration.ScreenHeight) * DISPLAY_SPACE_RES.y),
Color(color), scale, flags);
});
}
// Play default background music.
if (!loadGame)
PlaySoundTrack(level.GetAmbientTrack(), SoundTrackType::BGM, 0, SOUND_XFADETIME_LEVELJUMP);
}
void DeInitializeScripting(int levelIndex, GameStatus reason)
{
g_GameScript->FreeLevelScripts();
g_GameScriptEntities->FreeEntities();
if (levelIndex == 0)
g_GameScript->ResetScripts(true);
}
void InitializeOrLoadGame(bool loadGame)
{
g_Gui.SetInventoryItemChosen(NO_VALUE);
g_Gui.SetEnterInventory(NO_VALUE);
// Restore game?
if (loadGame)
{
if (!SaveGame::Load(g_GameFlow->SelectedSaveGame))
{
NextLevel = g_GameFlow->GetNumLevels();
return;
}
InitializeGame = false;
g_GameFlow->SelectedSaveGame = 0;
g_GameScript->OnLoad();
HandleAllGlobalEvents(EventType::Load, (Activator)short(LaraItem->Index));
}
else
{
// If not loading savegame, clear all info.
SaveGame::Statistics.Level = {};
SaveGame::Statistics.SecretBits = 0;
if (InitializeGame)
{
// Clear all game info as well.
SaveGame::Statistics.Game = {};
InitializeGame = false;
SaveGame::ResetHub();
TENLog("Starting new game.", LogLevel::Info);
}
else
{
SaveGame::LoadHub(CurrentLevel);
TENLog("Starting new level.", LogLevel::Info);
}
g_GameScript->OnStart();
HandleAllGlobalEvents(EventType::Start, (Activator)short(LaraItem->Index));
}
}
GameStatus DoGameLoop(int levelIndex)
{
int frameCount = LOOP_FRAME_COUNT;
auto& status = g_GameFlow->LastGameStatus;
// Before entering actual game loop, GamePhase() must be called once to sort out
// various runtime shenanigangs (e.g. hair or freeze mode initialization).
status = GamePhase(false);
g_Synchronizer.Init();
bool legacy30FpsDoneDraw = false;
while (DoTheGame)
{
g_Synchronizer.Sync();
while (g_Synchronizer.Synced())
{
status = ControlPhase(false);
g_Synchronizer.Step();
legacy30FpsDoneDraw = false;
}
if (status != GameStatus::Normal)
break;
if (g_GameFlow->LastFreezeMode != g_GameFlow->CurrentFreezeMode)
continue;
if (!g_Configuration.EnableHighFramerate)
{
if (!legacy30FpsDoneDraw)
{
DrawPhase(!levelIndex, 0.0f);
legacy30FpsDoneDraw = true;
}
}
else
{
DrawPhase(!levelIndex, g_Synchronizer.GetInterpolationFactor());
}
}
EndGameLoop(levelIndex, status);
return status;
}
void EndGameLoop(int levelIndex, GameStatus reason)
{
// Save last screenshot for loading screen.
g_Renderer.DumpGameScene();
SaveGame::SaveHub(levelIndex);
DeInitializeScripting(levelIndex, reason);
StopAllSounds();
StopSoundTracks(SOUND_XFADETIME_LEVELJUMP, true);
StopRumble();
}
void SetupInterpolation()
{
for (auto& item : g_Level.Items)
item.DisableInterpolation = false;
}
void HandleControls(bool isTitle)
{
// Poll input devices and update input variables.
// TODO: To allow cutscene skipping later, don't clear Deselect action.
UpdateInputActions(LaraItem, true);
if (isTitle)
ClearAction(In::Look);
}
GameStatus HandleMenuCalls(bool isTitle)
{
auto gameStatus = GameStatus::Normal;
if (ScreenFading)
return gameStatus;
if (isTitle)
{
auto invStatus = g_Gui.TitleOptions(LaraItem);
switch (invStatus)
{
case InventoryResult::NewGame:
case InventoryResult::NewGameSelectedLevel:
return GameStatus::NewGame;
case InventoryResult::HomeLevel:
return GameStatus::HomeLevel;
break;
case InventoryResult::LoadGame:
return GameStatus::LoadGame;
case InventoryResult::ExitGame:
return GameStatus::ExitGame;
}
return gameStatus;
}
bool playerAlive = LaraItem->HitPoints > 0;
bool doLoad = IsClicked(In::Load) ||
(!IsClicked(In::Inventory) && !NoAction() && SaveGame::IsLoadGamePossible() && Lara.Control.Count.Death > DEATH_INPUT_TIMEOUT);
bool doSave = IsClicked(In::Save) && playerAlive;
bool doPause = IsClicked(In::Pause) && playerAlive;
bool doInventory = (IsClicked(In::Inventory) || g_Gui.GetEnterInventory() != NO_VALUE) && playerAlive;
// Handle inventory.
if (doSave && g_GameFlow->IsLoadSaveEnabled() && Lara.Inventory.HasSave && g_Gui.GetInventoryMode() != InventoryMode::Save)
{
SaveGame::LoadHeaders();
g_Gui.SetInventoryMode(InventoryMode::Save);
g_Gui.CallInventory(LaraItem, false);
}
else if (doLoad && g_GameFlow->IsLoadSaveEnabled() && Lara.Inventory.HasLoad && g_Gui.GetInventoryMode() != InventoryMode::Load)
{
SaveGame::LoadHeaders();
g_Gui.SetInventoryMode(InventoryMode::Load);
if (g_Gui.CallInventory(LaraItem, false))
gameStatus = GameStatus::LoadGame;
}
else if (doPause && g_Gui.GetInventoryMode() != InventoryMode::Pause)
{
if (g_Gui.CallPause())
gameStatus = GameStatus::ExitToTitle;
}
else if (doInventory && LaraItem->HitPoints > 0 && !Lara.Control.Look.IsUsingBinoculars)
{
if (g_Gui.CallInventory(LaraItem, true))
gameStatus = GameStatus::LoadGame;
}
if (gameStatus != GameStatus::Normal)
{
StopAllSounds();
StopRumble();
}
return gameStatus;
}
GameStatus HandleGlobalInputEvents(bool isTitle)
{
if (isTitle)
return GameStatus::Normal;
// Check if player dead.
if (Lara.Control.Count.Death > DEATH_NO_INPUT_TIMEOUT ||
Lara.Control.Count.Death > DEATH_INPUT_TIMEOUT && !NoAction())
{
// TODO: Maybe do game over menu like some PSX versions have?
return GameStatus::LaraDead;
}
// Check if level has been completed.
// Negative NextLevel indicates that a savegame must be loaded from corresponding slot.
if (NextLevel > 0)
{
return GameStatus::LevelComplete;
}
else if (NextLevel < 0)
{
g_GameFlow->SelectedSaveGame = -(NextLevel + 1);
return GameStatus::LoadGame;
}
return GameStatus::Normal;
}