diff --git a/Documentation/Changes.txt b/Documentation/Changes.txt index 82b1be14a..d0afb4a13 100644 --- a/Documentation/Changes.txt +++ b/Documentation/Changes.txt @@ -1,12 +1,26 @@ Version 1.0.1 ============= +- Added antialiasing support. +- Fixed some issues with shimmying between diagonal ledges and walls. - Fixed rope transparency. +- Fixed objects disappearing under certain angles at the edges of the screen. - Fixed incorrect polerope grabbing. - Fixed camera behaviour with pushable blocks. - Fixed minecart unduck on inclines. +- Fixed quadbike dismount with jump key and allow to shoot big gun with action key. +- Fixed rollingball incorrectly killing Lara in water and in jump. +- Fixed resurfacing on underwater death. +- Fixed ripples not appearing on water connections higher than room bottom. +- Prevent title music audio from starting in a random place. +- Update harpoon speed on room change. +- Timer.Create now lets you choose the units to display remaining time. +- Fatal script errors now boot you to the title (it will crash if the title itself has these errors). +- SetFarView has been removed, and Flow.Level.farView is now uncapped. +- DisplayString text will now be cleared when a level is exited or reloaded. +- EventSequence.lua has been added and documented. Version 1.0 =========== -First beta release. \ No newline at end of file +First beta release. diff --git a/Documentation/config.ld b/Documentation/config.ld index 0ccfa96d2..b8d6f9397 100644 --- a/Documentation/config.ld +++ b/Documentation/config.ld @@ -9,9 +9,13 @@ new_type("tenclass", "2 Classes", true) new_type("tenprimitive", "3 Primitive Classes", true) new_type("enum", "4 Enums", true) new_type("luautil", "5 Lua utility modules", true) + +not_luadoc = true + +local version = "1.0.1" project = "TombEngine" -title = "TombEngine Lua API" -description = "TombEngine scripting interface" +title = "TombEngine " .. version .. " Lua API" +description = "TombEngine " .. version .. " scripting interface" full_description = [[Welcome to the TombEngine scripting API. This is a work in progress and some information might be wrong or outdated. Please also note that this is primarily a reference document, not a tutorial, so expect descriptions to be fairly sparse. At the time of writing, there is a tutorial describing the basics of Lua, as well as a number of example scripts, on the wiki at https://github.com/MontyTRC89/TombEngine/wiki. ####Module Hierarchy (boring but important) Other than the "special tables" (GameVars, LevelVars and LevelFuncs), every module described herein is held in a master table called TEN. @@ -24,7 +28,7 @@ This will put the modules and classes in the global table. In other words, it me local door = GetMoveableByName("door_type4_14") ####Always check logs/TENLog.txt -If you are scripting levels, TombEngine will often crash, even if `errorMode` (see Flow.Settings) is set to `ErrorMode.WARN` or `ErrorMode.SILENT`. +If you are scripting levels, TombEngine will often kick you back to the title screen, even if `errorMode` (see Flow.Settings) is set to `ErrorMode.WARN` or `ErrorMode.SILENT`. This might get annoying, but it's on purpose. If your Lua script contains a syntax error (e.g. you're missing `end` at the end of a function), the Lua interpreter will not be able to continue running the script. If it tried to keep running, you'd probably see some pretty strange behaviour, and would possibly get a crash regardless. @@ -34,10 +38,11 @@ Enjoy. \- _squidshire_ ]] + style = true wrap = true template = true -format = 'markdown' +format='markdown' no_space_before_args = true custom_display_name_handler = function(item, default_handler) diff --git a/Documentation/doc/1 modules/Effects.html b/Documentation/doc/1 modules/Effects.html index be67759aa..acb3a242b 100644 --- a/Documentation/doc/1 modules/Effects.html +++ b/Documentation/doc/1 modules/Effects.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

5 Lua utility modules

@@ -244,7 +245,7 @@
  • blendMode BlendID - (default TEN.Misc.BlendID.ALPHABLEND) How will we blend this with its surroundings? + (default TEN.Effects.BlendID.ALPHABLEND) How will we blend this with its surroundings?
  • startSize int @@ -281,7 +282,7 @@ -2, -- rot Color(255, 0, 0), -- startColor Color(0, 255, 0), -- endColor - TEN.Misc.BlendID.ADDITIVE, -- blendMode + TEN.Effects.BlendID.ADDITIVE, -- blendMode 15, -- startSize 50, -- endSize 20, -- lifetime @@ -510,7 +511,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/1 modules/Flow.html b/Documentation/doc/1 modules/Flow.html index 307f38f1d..c80e697c8 100644 --- a/Documentation/doc/1 modules/Flow.html +++ b/Documentation/doc/1 modules/Flow.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -79,6 +79,7 @@

    5 Lua utility modules

    @@ -108,10 +109,6 @@ SetTitleScreenImagePath(path) Image to show in the background of the title screen. - - SetFarView(farview) - Maximum draw distance. -

    settings.lua

    @@ -221,30 +218,6 @@ Must be a .jpg or .png image. - -
    - - SetFarView(farview) -
    -
    - Maximum draw distance. -The maximum draw distance, in sectors (blocks), of any level in the game. -This is equivalent to TRNG's WorldFarView variable. -(not yet implemented) - - -

    Parameters:

    -
      -
    • farview - byte - Number of sectors. Must be in the range [1, 127]. -
    • -
    - - - - -

    settings.lua

    @@ -380,7 +353,7 @@ Specify which translations in the strings table correspond to which languages.
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/1 modules/Inventory.html b/Documentation/doc/1 modules/Inventory.html index d0eac8dd0..327be00e9 100644 --- a/Documentation/doc/1 modules/Inventory.html +++ b/Documentation/doc/1 modules/Inventory.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -203,7 +204,7 @@ Similar to GiveItem but repla
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/1 modules/Logic.html b/Documentation/doc/1 modules/Logic.html index 6bfc2b64e..4475152b2 100644 --- a/Documentation/doc/1 modules/Logic.html +++ b/Documentation/doc/1 modules/Logic.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -78,6 +78,7 @@

    5 Lua utility modules

    @@ -278,7 +279,7 @@ and provides the delta time (a float representing game time since last call) via
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/1 modules/Misc.html b/Documentation/doc/1 modules/Misc.html index 584a94ad5..86d155089 100644 --- a/Documentation/doc/1 modules/Misc.html +++ b/Documentation/doc/1 modules/Misc.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -632,7 +633,7 @@ To be used with
    generated by LDoc 1.4.6 -Last updated 2022-08-02 21:24:26 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/1 modules/Objects.html b/Documentation/doc/1 modules/Objects.html index d743673f6..0aa9b0508 100644 --- a/Documentation/doc/1 modules/Objects.html +++ b/Documentation/doc/1 modules/Objects.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -296,7 +297,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/1 modules/Strings.html b/Documentation/doc/1 modules/Strings.html index e929cf175..d07a6c74d 100644 --- a/Documentation/doc/1 modules/Strings.html +++ b/Documentation/doc/1 modules/Strings.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -168,7 +169,7 @@ with a call to ShowString, or
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Flow.Animations.html b/Documentation/doc/2 classes/Flow.Animations.html index d566a7eb1..25b6ecaa5 100644 --- a/Documentation/doc/2 classes/Flow.Animations.html +++ b/Documentation/doc/2 classes/Flow.Animations.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -73,6 +73,7 @@

    5 Lua utility modules

    @@ -99,7 +100,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Flow.Fog.html b/Documentation/doc/2 classes/Flow.Fog.html index 94a91efe4..1aeaeeaef 100644 --- a/Documentation/doc/2 classes/Flow.Fog.html +++ b/Documentation/doc/2 classes/Flow.Fog.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -78,6 +78,7 @@

    5 Lua utility modules

    @@ -226,7 +227,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Flow.InventoryItem.html b/Documentation/doc/2 classes/Flow.InventoryItem.html index f97c423be..8a35205c0 100644 --- a/Documentation/doc/2 classes/Flow.InventoryItem.html +++ b/Documentation/doc/2 classes/Flow.InventoryItem.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -181,7 +182,7 @@ EXAMINE
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Flow.Level.html b/Documentation/doc/2 classes/Flow.Level.html index 25758aed6..e43c230be 100644 --- a/Documentation/doc/2 classes/Flow.Level.html +++ b/Documentation/doc/2 classes/Flow.Level.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -78,6 +78,7 @@

    5 Lua utility modules

    @@ -159,10 +160,6 @@ - - - - @@ -170,6 +167,10 @@ + + + +
    (Flow.Mirror) Location and size of the level's mirror, if present.
    farView(byte) The maximum draw distance for level.
    unlimitedAir (bool) Enable unlimited oxygen supply when in water.
    objects (table of Flow.InventoryItems) table of inventory object overrides
    farView(int) The maximum draw distance for level.

    Functions

    @@ -297,9 +298,7 @@
    (Flow.Fog) omni fog RGB color and distance. As seen in TR4's Desert Railroad. - If not provided, distance fog will be black.

    - -

    (not yet implemented) + If not provided, distance fog will be black. @@ -441,26 +440,6 @@ Invisible -

    -
    - - farView -
    -
    - (byte) The maximum draw distance for level. -Given in sectors (blocks). -Must be in the range [1, 127], and equal to or less than the value passed to SetGameFarView.

    - -

    This is equivalent to TRNG's LevelFarView variable.

    - -

    (not yet implemented) - - - - - - -

    @@ -491,6 +470,24 @@ Must be in the range [1, 127], and equal to or less than the value passed to Set + +
    + + farView +
    +
    + (int) The maximum draw distance for level. +Given in sectors (blocks). +Must be at least 4.

    + +

    This is equivalent to TRNG's LevelFarView variable. + + + + + + +

    Functions

    @@ -522,7 +519,7 @@ Must be in the range [1, 127], and equal to or less than the value passed to Set
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Flow.Mirror.html b/Documentation/doc/2 classes/Flow.Mirror.html index 964b4565a..8847b143e 100644 --- a/Documentation/doc/2 classes/Flow.Mirror.html +++ b/Documentation/doc/2 classes/Flow.Mirror.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -73,6 +73,7 @@

    5 Lua utility modules

    @@ -99,7 +100,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Flow.Settings.html b/Documentation/doc/2 classes/Flow.Settings.html index 085ba6017..96ef22346 100644 --- a/Documentation/doc/2 classes/Flow.Settings.html +++ b/Documentation/doc/2 classes/Flow.Settings.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -142,7 +143,7 @@ an invalid argument.

    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Flow.SkyLayer.html b/Documentation/doc/2 classes/Flow.SkyLayer.html index fe57d84cd..69b0dd699 100644 --- a/Documentation/doc/2 classes/Flow.SkyLayer.html +++ b/Documentation/doc/2 classes/Flow.SkyLayer.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -78,6 +78,7 @@

    5 Lua utility modules

    @@ -198,7 +199,7 @@ Less is more. City of The Dead, for example, uses a speed value of 16.
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Objects.AIObject.html b/Documentation/doc/2 classes/Objects.AIObject.html index 56acc9a99..290626a04 100644 --- a/Documentation/doc/2 classes/Objects.AIObject.html +++ b/Documentation/doc/2 classes/Objects.AIObject.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -371,7 +372,7 @@ aiObj:SetObjectID(TEN.Objects.ObjID.AI_PATROL1)
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Objects.Camera.html b/Documentation/doc/2 classes/Objects.Camera.html index 71dd5d9c7..2b81eb147 100644 --- a/Documentation/doc/2 classes/Objects.Camera.html +++ b/Documentation/doc/2 classes/Objects.Camera.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -259,7 +260,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Objects.Moveable.html b/Documentation/doc/2 classes/Objects.Moveable.html index a9b3cd8a9..324c4e627 100644 --- a/Documentation/doc/2 classes/Objects.Moveable.html +++ b/Documentation/doc/2 classes/Objects.Moveable.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -99,11 +100,11 @@ pickups, and Lara herself.

    associated getters and setters. - + - + @@ -401,8 +402,8 @@ most can just be ignored (see usage).
    - - Moveable:EnableItem() + + Moveable:Enable()
    Enable the item, as if a trigger for it had been stepped on. @@ -415,8 +416,8 @@ most can just be ignored (see usage).
    - - Moveable:DisableItem() + + Moveable:Disable()
    Disable the item @@ -1487,7 +1488,7 @@ sas:SetPosition(destinationPosition)
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Objects.Sink.html b/Documentation/doc/2 classes/Objects.Sink.html index e1b434af7..329da6b12 100644 --- a/Documentation/doc/2 classes/Objects.Sink.html +++ b/Documentation/doc/2 classes/Objects.Sink.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -261,7 +262,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Objects.SoundSource.html b/Documentation/doc/2 classes/Objects.SoundSource.html index 8eaca9aaa..cfb423da8 100644 --- a/Documentation/doc/2 classes/Objects.SoundSource.html +++ b/Documentation/doc/2 classes/Objects.SoundSource.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -259,7 +260,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Objects.Static.html b/Documentation/doc/2 classes/Objects.Static.html index 8c026c10c..e79e73406 100644 --- a/Documentation/doc/2 classes/Objects.Static.html +++ b/Documentation/doc/2 classes/Objects.Static.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -357,7 +358,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/2 classes/Strings.DisplayString.html b/Documentation/doc/2 classes/Strings.DisplayString.html index 5a052aac1..d73bbcdd6 100644 --- a/Documentation/doc/2 classes/Strings.DisplayString.html +++ b/Documentation/doc/2 classes/Strings.DisplayString.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -306,7 +307,7 @@ TEN.Strings.DisplayStringOption.SHADOW -- will give the text a small shadow
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/3 primitive classes/Color.html b/Documentation/doc/3 primitive classes/Color.html index 44504b1ab..1c762a147 100644 --- a/Documentation/doc/3 primitive classes/Color.html +++ b/Documentation/doc/3 primitive classes/Color.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -78,6 +78,7 @@

    5 Lua utility modules

    @@ -311,7 +312,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/3 primitive classes/Rotation.html b/Documentation/doc/3 primitive classes/Rotation.html index 381beeb24..4dcb6d4fa 100644 --- a/Documentation/doc/3 primitive classes/Rotation.html +++ b/Documentation/doc/3 primitive classes/Rotation.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -78,6 +78,7 @@

    5 Lua utility modules

    @@ -249,7 +250,7 @@ All values will be clamped to [-32768, 32767].

    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/3 primitive classes/Vec3.html b/Documentation/doc/3 primitive classes/Vec3.html index d429506c9..3e37fa21e 100644 --- a/Documentation/doc/3 primitive classes/Vec3.html +++ b/Documentation/doc/3 primitive classes/Vec3.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -78,6 +78,7 @@

    5 Lua utility modules

    @@ -276,7 +277,7 @@ However, this function would return it as (0, 1, 1).
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/4 enums/Effects.BlendID.html b/Documentation/doc/4 enums/Effects.BlendID.html index c19957715..4ec654dd5 100644 --- a/Documentation/doc/4 enums/Effects.BlendID.html +++ b/Documentation/doc/4 enums/Effects.BlendID.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -148,7 +149,7 @@ ALPHABLEND
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/4 enums/Flow.InvID.html b/Documentation/doc/4 enums/Flow.InvID.html index bc54d2ef2..fde91a227 100644 --- a/Documentation/doc/4 enums/Flow.InvID.html +++ b/Documentation/doc/4 enums/Flow.InvID.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -357,7 +358,7 @@ EXAMINE_ITEM8_COMBO2
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/4 enums/Objects.ObjID.html b/Documentation/doc/4 enums/Objects.ObjID.html index d50857495..74f8fea1f 100644 --- a/Documentation/doc/4 enums/Objects.ObjID.html +++ b/Documentation/doc/4 enums/Objects.ObjID.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -77,6 +77,7 @@

    5 Lua utility modules

    @@ -1095,7 +1096,7 @@ PANEL_MIDDLE_CORNER
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/5 lua utility modules/Timer.html b/Documentation/doc/5 lua utility modules/Timer.html index 6d65368b7..c5d37ad0c 100644 --- a/Documentation/doc/5 lua utility modules/Timer.html +++ b/Documentation/doc/5 lua utility modules/Timer.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -38,6 +38,7 @@

    5 Lua utility modules

    @@ -89,17 +90,26 @@

    Basic timer - after a specified number of seconds, the specified thing happens.

    -

    Usage:

    +

    Example usage:

     local Timer = require("Timer")
     
    -LevelFuncs.FinishTimer = function(healthWhenStarted, victoryMessage)
    -    DoSomething(healthWhenStarted, victoryMessage)
    +-- This will be called when the timer runs out
    +LevelFuncs.FinishTimer = function(healthWhenStarted, victoryMessage)
    +    -- Open a door, display a message, make an explosion... whatever you wish
    +    DoSomething(healthWhenStarted, victoryMessage)
     end
     
    -LevelFuncs.TriggerTimer = function(obj)
    -    local myTimer = Timer.Create("my_timer", 5.0, false, true, "FinishTimer", Lara:GetHP(), "Well done!")
    +-- This function triggers the timer
    +LevelFuncs.TriggerTimer = function(obj)
    +    local myTimer = Timer.Create("my_timer",
    +        5.0,
    +        false,
    +        {minutes = false, seconds = true, deciseconds = true},
    +        "FinishTimer",
    +        Lara:GetHP(),
    +        "Well done!")
         myTimer:Start()
     end
     
    @@ -114,7 +124,7 @@ LevelFuncs.OnControlPhase = function(dt)
     

    Functions

    Moveable:EnableItem()Moveable:Enable() Enable the item, as if a trigger for it had been stepped on.
    Moveable:DisableItem()Moveable:Disable() Disable the item
    - + @@ -122,45 +132,53 @@ LevelFuncs.OnControlPhase = function(dt) - + + + + + - + - - - - - + - + + + + + + + + + + + + + - + - + - + - + - - - -
    Create(name, totalTime, loop, showString, func, funcArgs)Create(name, totalTime, loop, timerFormat, func[, ...]) Create (but do not start) a new timer.
    Get a timer by its name.
    SetFunction(t, func, funcArgs)UpdateAll(dt)Update all active timers.
    myTimer:SetFunction(func[, ...]) Give the timer a new function and args
    Start(t)myTimer:Start() Begin or unpause a timer.
    Pause(t)Pause the timer.
    Stop(t)myTimer:Stop() Stop the timer.
    GetRemainingTime(t)myTimer:IsActive()Get whether or not the timer is active
    myTimer:SetPaused(p)Pause or unpause the timer.
    myTimer:IsPaused()Get whether or not the timer is paused
    myTimer:GetRemainingTime() Get the remaining time for a timer.
    SetRemainingTime(t, remainingTime)myTimer:SetRemainingTime(remainingTime) Set the remaining time for a timer
    GetTotalTime(t)myTimer:GetRemainingTime() Get the total time for a timer.
    SetTotalTime(t, totalTime)myTimer:SetTotalTime(totalTime) Set the total time for a timer
    SetLooping(t, looping)myTimer:SetLooping(looping) Set whether or not the timer loops
    UpdateAll(dt)Update all active timers.

    @@ -172,10 +190,31 @@ LevelFuncs.OnControlPhase = function(dt)
    - Create(name, totalTime, loop, showString, func, funcArgs) + Create(name, totalTime, loop, timerFormat, func[, ...])
    - Create (but do not start) a new timer. + Create (but do not start) a new timer.

    + +

    You have the option of displaying the remaining time on the clock. Timer format details:

    + +
    -- deciseconds are 1/10th of a second
    +
    +-- mins:secs
    +local myTimeFormat1 = {minutes = true, seconds = true, deciseconds = false}
    +
    +-- also mins:secs
    +local myTimeFormat2 = {minutes = true, seconds = true}
    +
    +-- secs:decisecs
    +local myTimeFormat3 = {seconds = true, deciseconds = true}
    +
    +-- secs; also what is printed if you pass true instead of a table
    +local myTimeFormat4 = {seconds = true}
    +
    + +

    At any given time, only one timer can show its countdown.

    + +

    Use this sparingly; in the classics, timed challenges did not have visible countdowns. For shorter timers, the gameplay benefit from showing the remaining time might not be necessary, and could interfere with the atmosphere of the level.

    Parameters:

    @@ -192,16 +231,17 @@ LevelFuncs.OnControlPhase = function(dt) bool if true, the timer will start again immediately after the time has elapsed
  • -
  • showString - bool - if true, the remaining time, rounded up, will show at the bottom of the screen. At any given time, only one timer can show its remaining time. +
  • timerFormat + table or bool + If a table is given, the remaining time will be shown as a string, formatted according to the values in the table. If true, the remaining seconds, rounded up, will show at the bottom of the screen. If false, the remaining time will not be shown on screen.
  • func string - The name of the LevelFunc member to call when the time is up + The name of the LevelFunc member to call when the time is upssss
  • -
  • funcArgs - the arguments with which the above function will be called +
  • ... + a variable number of arguments with which the above function will be called + (optional)
  • @@ -240,216 +280,6 @@ LevelFuncs.OnControlPhase = function(dt) - -
    - - SetFunction(t, func, funcArgs) -
    -
    - Give the timer a new function and args - - -

    Parameters:

    - - - - - - -
    -
    - - Start(t) -
    -
    - Begin or unpause a timer. If showing the remaining time on-screen, its color will be set to white. - - -

    Parameters:

    - - - - - - -
    -
    - - Pause(t) -
    -
    - Pause the timer. If showing the remaining time on-screen, its color will be set to yellow. - - -

    Parameters:

    - - - - - - -
    -
    - - Stop(t) -
    -
    - Stop the timer. - - -

    Parameters:

    - - - - - - -
    -
    - - GetRemainingTime(t) -
    -
    - Get the remaining time for a timer. - - -

    Parameters:

    - - -

    Returns:

    -
      - - the time in seconds remaining on the clock -
    - - - - -
    -
    - - SetRemainingTime(t, remainingTime) -
    -
    - Set the remaining time for a timer - - -

    Parameters:

    - - - - - - -
    -
    - - GetTotalTime(t) -
    -
    - Get the total time for a timer. - This is the amount of time the timer will start with, as well as when starting a new loop - - -

    Parameters:

    - - -

    Returns:

    -
      - - the timer's total time -
    - - - - -
    -
    - - SetTotalTime(t, totalTime) -
    -
    - Set the total time for a timer - - -

    Parameters:

    - - - - - - -
    -
    - - SetLooping(t, looping) -
    -
    - Set whether or not the timer loops - - -

    Parameters:

    - - - - - -
    @@ -472,6 +302,220 @@ LevelFuncs.OnControlPhase = function(dt) + +
    + + myTimer:SetFunction(func[, ...]) +
    +
    + Give the timer a new function and args + + +

    Parameters:

    + + + + + + +
    +
    + + myTimer:Start() +
    +
    + Begin or unpause a timer. If showing the remaining time on-screen, its color will be set to white. + + + + + + + +
    +
    + + myTimer:Stop() +
    +
    + Stop the timer. + + + + + + + +
    +
    + + myTimer:IsActive() +
    +
    + Get whether or not the timer is active + + + +

    Returns:

    +
      + + true if the timer is active, false if otherwise +
    + + + + +
    +
    + + myTimer:SetPaused(p) +
    +
    + Pause or unpause the timer. If showing the remaining time on-screen, its color will be set to yellow (paused) or white (unpaused). + + +

    Parameters:

    + + + + + + +
    +
    + + myTimer:IsPaused() +
    +
    + Get whether or not the timer is paused + + + +

    Returns:

    +
      + + true if the timer is paused, false if otherwise +
    + + + + +
    +
    + + myTimer:GetRemainingTime() +
    +
    + Get the remaining time for a timer. + + + +

    Returns:

    +
      + + the time in seconds remaining on the clock +
    + + + + +
    +
    + + myTimer:SetRemainingTime(remainingTime) +
    +
    + Set the remaining time for a timer + + +

    Parameters:

    + + + + + + +
    +
    + + myTimer:GetRemainingTime() +
    +
    + Get the total time for a timer. + This is the amount of time the timer will start with, as well as when starting a new loop + + + +

    Returns:

    +
      + + the timer's total time +
    + + + + +
    +
    + + myTimer:SetTotalTime(totalTime) +
    +
    + Set the total time for a timer + + +

    Parameters:

    + + + + + + +
    +
    + + myTimer:SetLooping(looping) +
    +
    + Set whether or not the timer loops + + +

    Parameters:

    + + + + + +
    @@ -480,7 +524,7 @@ LevelFuncs.OnControlPhase = function(dt)
    generated by LDoc 1.4.6 -Last updated 2022-08-01 20:38:26 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/5 lua utility modules/Util.html b/Documentation/doc/5 lua utility modules/Util.html index 906b34b1c..5f54cb8a0 100644 --- a/Documentation/doc/5 lua utility modules/Util.html +++ b/Documentation/doc/5 lua utility modules/Util.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -38,6 +38,7 @@

    5 Lua utility modules

    @@ -137,7 +138,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/index.html b/Documentation/doc/index.html index 34f3029cb..dae1ca9fd 100644 --- a/Documentation/doc/index.html +++ b/Documentation/doc/index.html @@ -3,7 +3,7 @@ - TombEngine Lua API + TombEngine 1.0.1 Lua API @@ -70,6 +70,7 @@

    5 Lua utility modules

    @@ -79,7 +80,7 @@
    -

    TombEngine scripting interface

    +

    TombEngine 1.0.1 scripting interface

    Welcome to the TombEngine scripting API. This is a work in progress and some information might be wrong or outdated. Please also note that this is primarily a reference document, not a tutorial, so expect descriptions to be fairly sparse. At the time of writing, there is a tutorial describing the basics of Lua, as well as a number of example scripts, on the wiki at https://github.com/MontyTRC89/TombEngine/wiki.

    Module Hierarchy (boring but important)

    Other than the "special tables" (GameVars, LevelVars and LevelFuncs), every module described herein is held in a master table called TEN. @@ -95,7 +96,7 @@ Util.ShortenTENCalls()

    Always check logs/TENLog.txt

    -

    If you are scripting levels, TombEngine will often crash, even if errorMode (see Flow.Settings) is set to ErrorMode.WARN or ErrorMode.SILENT.

    +

    If you are scripting levels, TombEngine will often kick you back to the title screen, even if errorMode (see Flow.Settings) is set to ErrorMode.WARN or ErrorMode.SILENT.

    This might get annoying, but it's on purpose. If your Lua script contains a syntax error (e.g. you're missing end at the end of a function), the Lua interpreter will not be able to continue running the script. If it tried to keep running, you'd probably see some pretty strange behaviour, and would possibly get a crash regardless.

    @@ -227,6 +228,10 @@ Util.ShortenTENCalls()

    5 Lua utility modules

    + + + + @@ -241,7 +246,7 @@ Util.ShortenTENCalls()
    generated by LDoc 1.4.6 -Last updated 2022-07-31 20:38:24 +Last updated 2022-08-11 22:43:52
    diff --git a/Documentation/doc/ldoc.css b/Documentation/doc/ldoc.css index 2001a950b..0b5c91f50 100644 --- a/Documentation/doc/ldoc.css +++ b/Documentation/doc/ldoc.css @@ -1,3 +1,20 @@ + +/* styles for prettification of source */ +pre .comment { color: #558817; } +pre .constant { color: #a8660d; } +pre .escape { color: #844631; } +pre .keyword { color: #aa5050; font-weight: bold; } +pre .library { color: #0e7c6b; } +pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +pre .string { color: #8080ff; } +pre .number { color: #f8660d; } +pre .operator { color: #2239a8; font-weight: bold; } +pre .preprocessor, pre .prepro { color: #a33243; } +pre .global { color: #800080; } +pre .user-keyword { color: #800080; } +pre .prompt { color: #558817; } +pre .url { color: #272fc2; text-decoration: underline; } + body { margin-left: 1em; margin-right: 1em; diff --git a/Documentation/ldoc.css b/Documentation/ldoc.css index 2001a950b..f2da1eacf 100644 --- a/Documentation/ldoc.css +++ b/Documentation/ldoc.css @@ -1,3 +1,20 @@ + +/* styles for prettification of source */ +pre .comment { color: #558817; } +pre .constant { color: #a8660d; } +pre .escape { color: #844631; } +pre .keyword { color: #aa5050; font-weight: bold; } +pre .library { color: #0e7c6b; } +pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } +pre .string { color: #8080ff; } +pre .number { color: #f8660d; } +pre .operator { color: #2239a8; font-weight: bold; } +pre .preprocessor, pre .prepro { color: #a33243; } +pre .global { color: #800080; } +pre .user-keyword { color: #800080; } +pre .prompt { color: #558817; } +pre .url { color: #272fc2; text-decoration: underline; } + body { margin-left: 1em; margin-right: 1em; @@ -19,7 +36,10 @@ p, ul { margin: 10px 0 0 0px;} strong { font-weight: bold;} em { font-style: italic;} - +address,caption,cite,code,dfn,em,strong,th,var,optgroup { + font-style: inherit; + font-weight: inherit; +} h1 { font-size: 1.5em; margin: 20px 0 20px 0; diff --git a/Scripts/EventSequence.lua b/Scripts/EventSequence.lua index 9e3187597..6fde9d7bc 100644 --- a/Scripts/EventSequence.lua +++ b/Scripts/EventSequence.lua @@ -1,11 +1,81 @@ +----- +--- Event sequence - a chain of functions to call at specified times, modeled after TRNG's organizers. +-- Example usage: +-- local EventSequence = require("EventSequence") +-- +-- -- These will be called by the sequence +-- LevelFuncs.HealLara = function() +-- Lara:SetHP(Lara:GetHP()+10) +-- end +-- +-- local nSpawned = 0 +-- LevelFuncs.SpawnBaddy = function(baddy, name, pos) +-- local myBaddy = TEN.Objects.Moveable(baddy, name..nSpawned, pos, nil, 0) +-- myBaddy:Enable() +-- nSpawned = nSpawned + 1 +-- end +-- +-- -- This function triggers the sequence +-- LevelFuncs.TriggerSequence = function(obj) +-- local posSteve = TEN.Objects.GetMoveableByName("stevePosNullmesh"):GetPosition() +-- local posChris = TEN.Objects.GetMoveableByName("chrisPosNullmesh"):GetPosition() +-- local mySeq = EventSequence.Create("my_seq", +-- false, -- does not loop +-- {seconds = true, deciseconds = true}, -- timer format, see Timer for details +-- 6, -- seconds until call the function specified in next arg +-- "HealLara", -- first function to call. If we don't need to pass any arguments, we can just give the func name as a string +-- 2.1, -- seconds until the next function, after the previous one has been called +-- {"SpawnBaddy", TEN.Objects.ObjID.BADDY1, "steve", posSteve}, -- if we DO want to pass arguments to the function to be called, we give a table with the name of the function ("SpawnBaddy" in this case) followed by the args to pass to it +-- 0.5, +-- {"SpawnBaddy", TEN.Objects.ObjID.SAS_CAIRO, "chris", posChris}, +-- 1, +-- "HealLara") +-- +-- -- event sequences are inactive to begin with and so need to be started +-- mySeq:Start() +-- end +-- +-- -- EventSequence runs on Timer, so this call is required +-- LevelFuncs.OnControlPhase = function(dt) +-- Timer.UpdateAll(dt) +-- end +-- +-- @luautil EventSequence + local Timer = require("Timer") local EventSequence LevelVars.__TEN_eventSequence = {sequences = {}} +LevelFuncs.__TEN_eventSequence_callNext = function(sequenceName, nextTimerName, func, ...) + local thisES = LevelVars.__TEN_eventSequence.sequences[sequenceName] + LevelFuncs[func](...) + + thisES.currentTimer = thisES.currentTimer + 1 + if thisES.currentTimer <= #thisES.timers then + local theTimer = Timer.Get(nextTimerName) + theTimer:SetRemainingTime(theTimer:GetTotalTime()) + theTimer:Start() + elseif thisES.loop then + local theTimer = Timer.Get(thisES.firstTimerName) + theTimer:SetRemainingTime(theTimer:GetTotalTime()) + theTimer:Start() + thisES.currentTimer = 1 + else + thisES.currentTimer = 1 + end +end + EventSequence = { - Create = function(name, showString, ...) + --- Create (but do not start) a new event sequence. + -- + -- @string name A label to give the sequence; used to retrieve the timer later as well as internally by TEN. + -- @bool loop if true, the sequence will start again from its first timer once its final function has been called + -- @tparam ?table|bool timerFormat same as in Timer. This is mainly for debugging. __This will not work properly if another sequence or timer is showing a countdown.__ + -- @param[opt] ... a variable number of pairs of arguments - a time in seconds, followed by the function (must be defined in the LevelFuncs table) to call once the time has elapsed, followed by another duration in seconds, another function name, etc. You can specify a function either by its name as a string, or by a table with the function name as the first member, followed by its arguments (see above example). + -- @return The inactive sequence. + Create = function(name, loop, timerFormat, ...) local obj = {} local mt = {} mt.__index = EventSequence @@ -13,10 +83,11 @@ EventSequence = { obj.name = name - LevelVars.__TEN_eventSequence.sequences[name] ={} + LevelVars.__TEN_eventSequence.sequences[name] = {} local thisES = LevelVars.__TEN_eventSequence.sequences[name] thisES.name = name thisES.timesFuncsAndArgs = {...} + thisES.loop = loop local tfa = thisES.timesFuncsAndArgs thisES.timers = {} @@ -35,6 +106,10 @@ EventSequence = { local funcAndArgs = tfa[i+1] local func + if i == 1 then + thisES.firstTimerName = timerName + end + if type(funcAndArgs) == "string" then -- we only have a function func = funcAndArgs @@ -44,29 +119,15 @@ EventSequence = { func = table.remove(funcAndArgs, 1) end - if nextTimer < #tfa then - -- This function must start next timer - -- AND do its function - LevelFuncs[funcName] = function(...) - LevelFuncs[func](...) - Timer.Get(nextTimerName):Start() - thisES.currentTimer = timerIndex + 1 - end - else - -- final timer - LevelFuncs[funcName] = function(...) - LevelFuncs[func](...) - Timer.Get(timerName):Stop() - thisES.currentTimer = 1 - end - end - local thisTimer = Timer.Create(timerName, tfa[i], -- time false, - showString, - funcName, - funcAndArgs -- now with func removed + timerFormat, + "__TEN_eventSequence_callNext", + name, + nextTimerName, + func, + table.unpack(funcAndArgs) -- now with func removed ) thisES.timers[timerIndex] = timerName @@ -75,6 +136,9 @@ EventSequence = { return obj end; + --- Get an event sequence by its name. + -- @string name The label that was given to the sequence when it was created + -- @return The sequence Get = function(name) if LevelVars.__TEN_eventSequence.sequences[name] then local obj = {} @@ -85,22 +149,45 @@ EventSequence = { return obj end return nil - end, + end; - Pause = function(t) + --- Pause or unpause the sequence. If showing the remaining time on-screen, its color will be set to yellow (paused) or white (unpaused). + -- @function mySequence:SetPaused + -- @bool p if true, the sequence will be paused; if false, it will be unpaused + SetPaused = function(t, p) local thisES = LevelVars.__TEN_eventSequence.sequences[t.name] - Timer.Get(thisES.timers[thisES.currentTimer]):Pause() - end, + Timer.Get(thisES.timers[thisES.currentTimer]):SetPaused(p) + end; - Stop = function(t) + --- Get whether or not the sequence is paused + -- @function mySequence:IsPaused + -- @return true if the timer is paused, false if otherwise + IsPaused = function(t) local thisES = LevelVars.__TEN_eventSequence.sequences[t.name] - Timer.Get(thisES.timers[thisES.currentTimer]):Stop() - end, - + return Timer.Get(thisES.timers[thisES.currentTimer]):IsPaused() + end; + + --- Begin or unpause a sequence. If showing the remaining time on-screen, its color will be set to white. + -- @function mySequence:Start Start = function(t) local thisES = LevelVars.__TEN_eventSequence.sequences[t.name] Timer.Get(thisES.timers[thisES.currentTimer]):Start() end; + + --- Stop the sequence. + --@function mySequence:Stop + Stop = function(t) + local thisES = LevelVars.__TEN_eventSequence.sequences[t.name] + Timer.Get(thisES.timers[thisES.currentTimer]):Stop() + end; + + --- Get whether or not the sequence is active + -- @function mySequence:IsActive + -- @return true if the sequence is active, false if otherwise + IsActive = function(t) + local thisES = LevelVars.__TEN_eventSequence.sequences[t.name] + return Timer.Get(thisES.timers[thisES.currentTimer]):IsActive() + end; } return EventSequence diff --git a/Scripts/Gameflow.lua b/Scripts/Gameflow.lua index 7f2945716..fbc50aed6 100644 --- a/Scripts/Gameflow.lua +++ b/Scripts/Gameflow.lua @@ -17,10 +17,6 @@ local ItemAction = Flow.ItemAction Flow.SetIntroImagePath("Screens\\main.jpg") Flow.SetTitleScreenImagePath("Screens\\main.jpg") --- Flow.SetFarView sets global far view distance in blocks. --- It will be overwritten by level.farView value, if it is specified. - -Flow.SetFarView(20) -------------------------------------------------- @@ -71,7 +67,6 @@ test.objects = { -1, ItemAction.USE ), - myObj, InventoryItem.new( "tut1_ba_cartouche2", InvID.PUZZLE_ITEM3_COMBO2, @@ -114,4 +109,4 @@ test.objects = { ) } -Flow.AddLevel(test) \ No newline at end of file +Flow.AddLevel(test) diff --git a/Scripts/Strings.lua b/Scripts/Strings.lua index fb0919468..1b2d5ee19 100644 --- a/Scripts/Strings.lua +++ b/Scripts/Strings.lua @@ -1109,6 +1109,46 @@ local strings = { "", "" }, + antialiasing = { + "Antialiasing", + "", + "", + "", + "", + "", + "", + "" + }, + low = { + "Low", + "", + "", + "", + "", + "", + "", + "" + }, + medium = { + "Medium", + "", + "", + "", + "", + "", + "", + "" + }, + high = { + "High", + "", + "", + "", + "", + "", + "", + "" + }, waiting_for_key = { "Waiting For Key", "", diff --git a/Scripts/TestLevel.lua b/Scripts/TestLevel.lua index 3eb79daa3..96377870c 100644 --- a/Scripts/TestLevel.lua +++ b/Scripts/TestLevel.lua @@ -3,9 +3,17 @@ local Util = require("Util") Util.ShortenTENCalls() +-- Called when entering a level, not called when loading from a save +LevelFuncs.OnStart = function() end + +-- Called only when loading from a save LevelFuncs.OnLoad = function() end + LevelFuncs.OnSave = function() end -LevelFuncs.OnControlPhase = function() end + +-- dt stands for "delta time", and holds the time in seconds since the last call to OnControlPhase +LevelFuncs.OnControlPhase = function(dt) end + LevelFuncs.OnEnd = function() end -- An example function which prints a string and leaves it on screen for 1 second. @@ -114,4 +122,4 @@ LevelFuncs.EmitHaloOnActionPush = function(Triggerer) Effects.EmitParticle(pos, velocity, 2, 1, rot, Color.new(color * 0.5, color * 0.5, color), Color.new(color * 0.2, color * 0.1, color), 2, 16, 64, 1, false, false) Effects.EmitLight(pos, Color.new(color * 0.5, color * 0.5, color), 7) -end \ No newline at end of file +end diff --git a/Scripts/Timer.lua b/Scripts/Timer.lua index b4b98ae38..66567506e 100644 --- a/Scripts/Timer.lua +++ b/Scripts/Timer.lua @@ -1,14 +1,23 @@ ----- --- Basic timer - after a specified number of seconds, the specified thing happens. --- Usage: +-- Example usage: -- local Timer = require("Timer") -- +-- -- This will be called when the timer runs out -- LevelFuncs.FinishTimer = function(healthWhenStarted, victoryMessage) +-- -- Open a door, display a message, make an explosion... whatever you wish -- DoSomething(healthWhenStarted, victoryMessage) -- end -- +-- -- This function triggers the timer -- LevelFuncs.TriggerTimer = function(obj) --- local myTimer = Timer.Create("my_timer", 5.0, false, true, "FinishTimer", Lara:GetHP(), "Well done!") +-- local myTimer = Timer.Create("my_timer", +-- 5.0, +-- false, +-- {minutes = false, seconds = true, deciseconds = true}, +-- "FinishTimer", +-- Lara:GetHP(), +-- "Well done!") -- myTimer:Start() -- end -- @@ -22,20 +31,42 @@ LevelVars.__TEN_timer = {timers = {}} local Timer -local unpausedColor = Color(255, 255, 255) -local pausedColor = Color(255, 255, 0) +local unpausedColor = TEN.Color(255, 255, 255) +local pausedColor = TEN.Color(255, 255, 0) local str = TEN.Strings.DisplayString("TIMER", 0, 0, unpausedColor, false, {TEN.Strings.DisplayStringOption.CENTER, TEN.Strings.DisplayStringOption.SHADOW} ) Timer = { --- Create (but do not start) a new timer. + -- + -- You have the option of displaying the remaining time on the clock. Timer format details: + -- + -- -- deciseconds are 1/10th of a second + -- + -- -- mins:secs + -- local myTimeFormat1 = {minutes = true, seconds = true, deciseconds = false} + -- + -- -- also mins:secs + -- local myTimeFormat2 = {minutes = true, seconds = true} + -- + -- -- secs:decisecs + -- local myTimeFormat3 = {seconds = true, deciseconds = true} + -- + -- -- secs; also what is printed if you pass true instead of a table + -- local myTimeFormat4 = {seconds = true} + -- + --__At any given time, only one timer can show its countdown__. + -- + --Use this sparingly; in the classics, timed challenges did not have visible countdowns. For shorter timers, the gameplay benefit from showing the remaining time might not be necessary, and could interfere with the atmosphere of the level. + -- -- @string name A label to give this timer; used to retrieve the timer later -- @number totalTime The duration of the timer, in seconds -- @bool loop if true, the timer will start again immediately after the time has elapsed - -- @bool showString if true, the remaining time, rounded up, will show at the bottom of the screen. __At any given time, only one timer can show its remaining time__. - -- @string func The name of the LevelFunc member to call when the time is up - -- @param funcArgs the arguments with which the above function will be called + -- @tparam ?table|bool timerFormat If a table is given, the remaining time will be shown as a string, formatted according to the values in the table. If true, the remaining seconds, rounded up, will show at the bottom of the screen. If false, the remaining time will not be shown on screen. + -- @string func The name of the LevelFunc member to call when the time is upssss + -- @param[opt] ... a variable number of arguments with which the above function will be called -- @return The timer in its paused state - Create = function(name, totalTime, loop, showString, func, ...) + -- + Create = function(name, totalTime, loop, timerFormat, func, ...) local obj = {} local mt = {} mt.__index = Timer @@ -45,7 +76,6 @@ Timer = { LevelVars.__TEN_timer.timers[name] ={} local thisTimer = LevelVars.__TEN_timer.timers[name] thisTimer.name = name - thisTimer.showString = showString thisTimer.totalTime = totalTime thisTimer.remainingTime = totalTime thisTimer.func = func @@ -53,6 +83,11 @@ Timer = { thisTimer.loop = loop thisTimer.active = false thisTimer.paused = true + if type(timerFormat) == "table" then + thisTimer.timerFormat = timerFormat + elseif timerFormat then + thisTimer.timerFormat = {seconds = true} + end return obj end; @@ -69,92 +104,6 @@ Timer = { return obj end return nil - end, - - --- Give the timer a new function and args - -- @param t the timer in question - -- @string func The name of the LevelFunc member to call when the time is up - -- @param funcArgs the arguments with which the above function will be called - SetFunction = function(t, func, ...) - local thisTimer = LevelVars.__TEN_timer.timers[t.name] - thisTimer.func = func - thisTimer.funcArgs = {...} - end, - - --- Begin or unpause a timer. If showing the remaining time on-screen, its color will be set to white. - -- @param t the timer in question - Start = function(t) - local thisTimer = LevelVars.__TEN_timer.timers[t.name] - if not thisTimer.active then - thisTimer.active = true - end - - LevelVars.__TEN_timer.timers[t.name].paused = false - - if thisTimer.showString then - str:SetColor(unpausedColor) - end - end; - - --- Pause the timer. If showing the remaining time on-screen, its color will be set to yellow. - -- @param t the timer in question - Pause = function(t) - local thisTimer = LevelVars.__TEN_timer.timers[t.name] - thisTimer.paused = true - if thisTimer.showString then - str:SetColor(pausedColor) - end - end, - - --- Stop the timer. - -- @param t the timer in question - Stop = function(t) - LevelVars.__TEN_timer.timers[t.name].active = false - end, - - --- Get the remaining time for a timer. - -- @param t the timer in question - -- @return the time in seconds remaining on the clock - GetRemainingTime = function(t) - return LevelVars.__TEN_timer.timers[t.name].remainingTime - end, - - --- Set the remaining time for a timer - -- @param t the timer in question - -- @number remainingTime the new time remaining for the timer - SetRemainingTime = function(t, remainingTime) - LevelVars.__TEN_timer.timers[t.name].remainingTime = remainingTime - end, - - --- Get the total time for a timer. - -- This is the amount of time the timer will start with, as well as when starting a new loop - -- @param t the timer in question - -- @return the timer's total time - GetTotalTime = function(t) - return LevelVars.__TEN_timer.timers[t.name].totalTime - end, - - --- Set the total time for a timer - -- @param t the timer in question - -- @number totalTime timer's new total time - SetTotalTime = function(t, totalTime) - LevelVars.__TEN_timer.timers[t.name].totalTime = totalTime - end, - - --- Set whether or not the timer loops - -- @param t the timer in question - -- @bool looping whether or not the timer loops - SetLooping = function(t, looping) - LevelVars.__TEN_timer.timers[t.name].loop = looping - end, - - --- Update all active timers. - -- Should be called in LevelFuncs.OnControlPhase - -- @number dt The time in seconds since the last frame - UpdateAll = function(dt) - for _, t in pairs(LevelVars.__TEN_timer.timers) do - Timer.Update(t, dt) - end end; Update = function(t, dt) @@ -173,17 +122,178 @@ Timer = { end end - if t.showString then + if t.timerFormat then TEN.Strings.HideString(str) - str:SetKey(tostring(math.ceil(t.remainingTime))) + + local fmt = "" + local remaining = math.max(t.remainingTime, 0) + + local round = math.floor + local subSecond = remaining - math.floor(remaining) + + local fmtBefore = false + + -- deciseconds + if t.timerFormat.deciseconds then + fmt = math.floor(10*subSecond) + fmtBefore = true + end + + -- seconds + if t.timerFormat.seconds then + if not fmtBefore then + round = math.ceil + else + round = math.floor + fmt = ":" .. fmt + end + local roundedSeconds = round(remaining) + local toBeDisplayed = roundedSeconds + if t.timerFormat.minutes then + toBeDisplayed = roundedSeconds % 60 + end + fmt = string.format("%02d", toBeDisplayed) .. fmt + + remaining = roundedSeconds + fmtBefore = true + end + + -- minutes + if t.timerFormat.minutes then + if not fmtBefore then + round = math.ceil + else + round = math.floor + fmt = ":" .. fmt + end + + local roundedMinutes = round(remaining/60) + local toBeDisplayed = roundedMinutes + + fmt = string.format("%02d", toBeDisplayed) .. fmt + fmtBefore = true + end + + str:SetKey(fmt) local myX, myY = PercentToScreen(50, 90) str:SetPosition(myX, myY) + + -- Do this again in case the player has loaded while the timer was paused already + -- Need a better solution for this + if t.paused then + str:SetColor(pausedColor) + end + TEN.Strings.ShowString(str, 1) end end end; + --- Update all active timers. + -- Should be called in LevelFuncs.OnControlPhase + -- @number dt The time in seconds since the last frame + UpdateAll = function(dt) + for _, t in pairs(LevelVars.__TEN_timer.timers) do + Timer.Update(t, dt) + end + end; + + --- Give the timer a new function and args + -- @function myTimer:SetFunction + -- @string func The name of the LevelFunc member to call when the time is up + -- @param[opt] ... a variable number of arguments with which the above function will be called + SetFunction = function(t, func, ...) + local thisTimer = LevelVars.__TEN_timer.timers[t.name] + thisTimer.func = func + thisTimer.funcArgs = {...} + end; + + --- Begin or unpause a timer. If showing the remaining time on-screen, its color will be set to white. + -- @function myTimer:Start + Start = function(t) + local thisTimer = LevelVars.__TEN_timer.timers[t.name] + if not thisTimer.active then + thisTimer.active = true + end + + thisTimer.paused = false + + if thisTimer.timerFormat then + str:SetColor(unpausedColor) + end + end; + + --- Stop the timer. + -- @function myTimer:Stop + Stop = function(t) + LevelVars.__TEN_timer.timers[t.name].active = false + end; + + --- Get whether or not the timer is active + -- @function myTimer:IsActive + -- @return true if the timer is active, false if otherwise + IsActive = function(t) + return LevelVars.__TEN_timer.timers[t.name].active + end; + + --- Pause or unpause the timer. If showing the remaining time on-screen, its color will be set to yellow (paused) or white (unpaused). + -- @function myTimer:SetPaused + -- @bool p if true, the timer will be paused; if false, it would be unpaused + SetPaused = function(t, p) + local thisTimer = LevelVars.__TEN_timer.timers[t.name] + thisTimer.paused = p + if thisTimer.timerFormat then + if p then + str:SetColor(pausedColor) + else + str:SetColor(unpausedColor) + end + end + end; + + --- Get whether or not the timer is paused + -- @function myTimer:IsPaused + -- @return true if the timer is paused, false if otherwise + IsPaused = function(t) + return LevelVars.__TEN_timer.timers[t.name].paused + end; + + --- Get the remaining time for a timer. + -- @function myTimer:GetRemainingTime + -- @return the time in seconds remaining on the clock + GetRemainingTime = function(t) + return LevelVars.__TEN_timer.timers[t.name].remainingTime + end; + + --- Set the remaining time for a timer + -- @function myTimer:SetRemainingTime + -- @number remainingTime the new time remaining for the timer + SetRemainingTime = function(t, remainingTime) + LevelVars.__TEN_timer.timers[t.name].remainingTime = remainingTime + end; + + --- Get the total time for a timer. + -- This is the amount of time the timer will start with, as well as when starting a new loop + -- @function myTimer:GetRemainingTime + -- @return the timer's total time + GetTotalTime = function(t) + return LevelVars.__TEN_timer.timers[t.name].totalTime + end; + + --- Set the total time for a timer + -- @function myTimer:SetTotalTime + -- @number totalTime timer's new total time + SetTotalTime = function(t, totalTime) + LevelVars.__TEN_timer.timers[t.name].totalTime = totalTime + end; + + --- Set whether or not the timer loops + -- @function myTimer:SetLooping + -- @bool looping whether or not the timer loops + SetLooping = function(t, looping) + LevelVars.__TEN_timer.timers[t.name].loop = looping + end; } return Timer diff --git a/TombEngine/Game/Lara/lara.cpp b/TombEngine/Game/Lara/lara.cpp index c84f9f1ba..9c42093da 100644 --- a/TombEngine/Game/Lara/lara.cpp +++ b/TombEngine/Game/Lara/lara.cpp @@ -558,6 +558,16 @@ void LaraControl(ItemInfo* item, CollisionInfo* coll) break; case WaterStatus::Underwater: + + // Disable potential Lara resurfacing if her health is zero or below. + // For some reason, originals worked without this condition, but TEN does not. -- Lwmte, 11.08.22 + + if (item->HitPoints <= 0) + break; + + // Determine if Lara's head is above water surface. This is needed to prevent + // pre-TR5 bug where Lara would keep submerged until her root mesh (butt) is above water level. + isWaterOnHeadspace = TestEnvironment(ENV_FLAG_WATER, item->Pose.Position.x, item->Pose.Position.y - CLICK(1), item->Pose.Position.z, GetCollision(item->Pose.Position.x, item->Pose.Position.y - CLICK(1), item->Pose.Position.z, item->RoomNumber).RoomNumber); diff --git a/TombEngine/Game/Lara/lara_one_gun.cpp b/TombEngine/Game/Lara/lara_one_gun.cpp index 5d50a4f83..165ca3128 100644 --- a/TombEngine/Game/Lara/lara_one_gun.cpp +++ b/TombEngine/Game/Lara/lara_one_gun.cpp @@ -506,7 +506,6 @@ void HarpoonBoltControl(short itemNumber) // Store old position for later auto oldPos = item->Pose.Position; - short roomNumber = item->RoomNumber; bool aboveWater = false; // Update speed and check if above water @@ -527,10 +526,9 @@ void HarpoonBoltControl(short itemNumber) else { // Create bubbles - if ((Wibble & 15) == 0) - CreateBubble((Vector3Int*)&item->Pose, item->RoomNumber, 0, 0, BUBBLE_FLAG_CLUMP | BUBBLE_FLAG_HIGH_AMPLITUDE, 0, 0, 0); // CHECK + if (Wibble & 4) + CreateBubble((Vector3Int*)&item->Pose, item->RoomNumber, 0, 0, BUBBLE_FLAG_CLUMP | BUBBLE_FLAG_HIGH_AMPLITUDE, 0, 0, 0); - TriggerRocketSmoke(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, 64); item->Animation.VerticalVelocity = -HARPOON_VELOCITY * phd_sin(item->Pose.Orientation.x) / 2; item->Animation.Velocity = HARPOON_VELOCITY * phd_cos(item->Pose.Orientation.x) / 2; aboveWater = false; @@ -563,8 +561,8 @@ void HarpoonBoltControl(short itemNumber) } // Has harpoon changed room? - if (item->RoomNumber != roomNumber) - ItemNewRoom(itemNumber, roomNumber); + if (item->RoomNumber != probe.RoomNumber) + ItemNewRoom(itemNumber, probe.RoomNumber); // If now in water and before in land, add a ripple if (TestEnvironment(ENV_FLAG_WATER, item) && aboveWater) diff --git a/TombEngine/Game/camera.cpp b/TombEngine/Game/camera.cpp index 38f49ec7d..d1a52dfc3 100644 --- a/TombEngine/Game/camera.cpp +++ b/TombEngine/Game/camera.cpp @@ -95,10 +95,9 @@ void LookAt(CAMERA_INFO* cam, short roll) float fov = TO_RAD(CurrentFOV / 1.333333f); float r = TO_RAD(roll); - float gameFarView = g_GameFlow->GetGameFarView() * float(SECTOR(1)); float levelFarView = g_GameFlow->GetLevel(CurrentLevel)->GetFarView() * float(SECTOR(1)); - g_Renderer.UpdateCameraMatrices(cam, r, fov, std::min(gameFarView, levelFarView)); + g_Renderer.UpdateCameraMatrices(cam, r, fov, levelFarView); } void AlterFOV(int value) diff --git a/TombEngine/Game/collision/collide_room.cpp b/TombEngine/Game/collision/collide_room.cpp index 33d10990d..f3b3e8cd3 100644 --- a/TombEngine/Game/collision/collide_room.cpp +++ b/TombEngine/Game/collision/collide_room.cpp @@ -946,6 +946,12 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance) auto ffpX = eX + frontFloorProbeOffset * s; auto ffpZ = eZ + frontFloorProbeOffset * c; + // Calculate block min/max points to filter out out-of-bounds checks. + float minX = floor(ffpX / WALL_SIZE) * WALL_SIZE - 1.0f; + float minZ = floor(ffpZ / WALL_SIZE) * WALL_SIZE - 1.0f; + float maxX = ceil(ffpX / WALL_SIZE) * WALL_SIZE + 1.0f; + float maxZ = ceil(ffpZ / WALL_SIZE) * WALL_SIZE + 1.0f; + // Get front floor block auto room = GetRoom(item->Location, ffpX, y, ffpZ).roomNumber; auto block = GetCollision(ffpX, y, ffpZ, room).Block; @@ -1072,6 +1078,11 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance) if (!ray.Intersects(plane[i], distance)) continue; + // Intersection point is out of block bounds, discard + auto cPoint = ray.position + ray.direction * distance; + if (cPoint.x < minX || cPoint.x > maxX || cPoint.z < minZ || cPoint.z > maxZ) + continue; + // Process plane intersection only if distance is smaller // than already found minimum if (distance < closestDistance[p]) @@ -1122,8 +1133,8 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance) // Remember distance to the closest plane with same angle (it happens sometimes with bridges) float dist1 = FLT_MAX; float dist2 = FLT_MAX; - auto r1 = originRay.Intersects(closestPlane[p], dist1); - auto r2 = originRay.Intersects(closestPlane[firstEqualAngle], dist2); + bool r1 = originRay.Intersects(closestPlane[p], dist1); + bool r2 = originRay.Intersects(closestPlane[firstEqualAngle], dist2); finalDistance[h] = (dist1 > dist2 && r2) ? dist2 : (r1 ? dist1 : dist2); finalResult[h] = result[p]; @@ -1131,16 +1142,41 @@ short GetNearestLedgeAngle(ItemInfo* item, CollisionInfo* coll, float& distance) } } - // Store first result in case all 3 results are different (no priority) or prioritized result if long-distance misfire occured + // A case when all 3 results are different (no priority) or prioritized result + // is a long-distance misfire + if (finalDistance[h] == FLT_MAX || finalDistance[h] > WALL_SIZE / 2) { - finalDistance[h] = closestDistance[0]; - finalResult[h] = result[0]; + // Prioritize angle which is similar to coll setup's forward angle. + // This helps to solve some borderline cases with diagonal shimmying, + // when centerpoint probe lands exactly on a block which has no + // diagonal split. If no angle similarity exists, just use centerpoint angle. + + auto itr = std::find(result, result + 3, coll->Setup.ForwardAngle); + int prioritizedAngle = (itr != std::end(result)) ? std::distance(result, itr) : 0; + + finalDistance[h] = closestDistance[prioritizedAngle]; + finalResult[h] = result[prioritizedAngle]; } } + int usedProbe = 0; // 0 = upper probe + // Return upper probe result in case it returned lower distance or has hit a bridge. - auto usedProbe = ((finalDistance[0] < finalDistance[1]) || hitBridge) ? 0 : 1; + // For unique case when both distances are equal, again make a comparison to current + // forward angle and return prioritized result. + + if (!hitBridge) + { + if (floor(finalDistance[0]) == floor(finalDistance[1])) + { + auto itr = std::find(finalDistance, finalDistance + 2, coll->Setup.ForwardAngle); + usedProbe = (itr != std::end(finalDistance)) ? std::distance(finalDistance, itr) : 0; + } + else if (finalDistance[1] < finalDistance[0]) + usedProbe = 1; + } + distance = finalDistance[usedProbe] - (coll->Setup.Radius - frontalOffset); return finalResult[usedProbe]; } diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 954b3ef9c..38d4e83d5 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -366,33 +366,24 @@ GameStatus ControlPhase(int numFrames, int demoMode) unsigned CALLBACK GameMain(void *) { - try - { - TENLog("Starting GameMain...", LogLevel::Info); + TENLog("Starting GameMain...", LogLevel::Info); - TimeInit(); + TimeInit(); - if (g_GameFlow->IntroImagePath.empty()) - throw TENScriptException("Intro image path is not set."); + if (g_GameFlow->IntroImagePath.empty()) + throw TENScriptException("Intro image path is not set."); - // Do a fixed time title image - g_Renderer.RenderTitleImage(); + // Do a fixed time title image + g_Renderer.RenderTitleImage(); - // Execute the LUA gameflow and play the game - g_GameFlow->DoFlow(); + // Execute the LUA gameflow and play the game + g_GameFlow->DoFlow(); - DoTheGame = false; + DoTheGame = false; - // Finish the thread - PostMessage(WindowsHandle, WM_CLOSE, NULL, NULL); - EndThread(); - } - catch (TENScriptException const& e) - { - std::string msg = std::string{ "An unrecoverable error occurred in " } + __func__ + ": " + e.what(); - TENLog(msg, LogLevel::Error, LogConfig::All); - throw; - } + // Finish the thread + PostMessage(WindowsHandle, WM_CLOSE, NULL, NULL); + EndThread(); return true; } @@ -407,6 +398,9 @@ GameStatus DoTitle(int index, std::string const& ambient) InventoryResult inventoryResult; + g_GameStringsHandler->ClearDisplayStrings(); + g_GameScript->ResetScripts(true); + if (g_GameFlow->TitleType == TITLE_TYPE::FLYBY) { // Initialise items, effects, lots, camera @@ -414,8 +408,6 @@ GameStatus DoTitle(int index, std::string const& ambient) InitialisePickupDisplay(); InitialiseCamera(); - g_GameScript->ResetScripts(true); - // Run the level script ScriptInterfaceLevel* level = g_GameFlow->GetLevel(index); @@ -518,6 +510,7 @@ GameStatus DoLevel(int index, std::string const& ambient, bool loadFromSavegame) InitialisePickupDisplay(); InitialiseCamera(); + g_GameStringsHandler->ClearDisplayStrings(); g_GameScript->ResetScripts(loadFromSavegame); // Run the level script diff --git a/TombEngine/Game/control/volume.h b/TombEngine/Game/control/volume.h index 5d4ef2f81..60902d36d 100644 --- a/TombEngine/Game/control/volume.h +++ b/TombEngine/Game/control/volume.h @@ -37,6 +37,7 @@ enum TriggerVolumeActivators struct TriggerVolume { TriggerVolumeType Type; + std::string LuaName; int EventSetIndex; Vector3 Position; diff --git a/TombEngine/Game/debug/debug.cpp b/TombEngine/Game/debug/debug.cpp index 775e0b15a..35184e980 100644 --- a/TombEngine/Game/debug/debug.cpp +++ b/TombEngine/Game/debug/debug.cpp @@ -22,11 +22,11 @@ void InitTENLog() logger->set_pattern("[%Y-%b-%d %T] [%^%l%$] %v"); } -void TENLog(std::string_view str, LogLevel level, LogConfig config) +void TENLog(std::string_view str, LogLevel level, LogConfig config, bool allowSpam) { static std::string lastString = {}; - if (lastString == str) + if (lastString == str && !allowSpam) return; if constexpr (!DebugBuild) @@ -40,7 +40,7 @@ void TENLog(std::string_view str, LogLevel level, LogConfig config) { case LogLevel::Error: logger->error(str); - break; + break; case LogLevel::Warning: logger->warn(str); break; diff --git a/TombEngine/Game/debug/debug.h b/TombEngine/Game/debug/debug.h index f0f35130c..3ede96dd7 100644 --- a/TombEngine/Game/debug/debug.h +++ b/TombEngine/Game/debug/debug.h @@ -21,7 +21,7 @@ enum class LogConfig All }; -void TENLog(std::string_view str, LogLevel level = LogLevel::Info, LogConfig config = LogConfig::All); +void TENLog(std::string_view str, LogLevel level = LogLevel::Info, LogConfig config = LogConfig::All, bool allowSpam = false); void ShutdownTENLog(); void InitTENLog(); diff --git a/TombEngine/Game/effects/effects.cpp b/TombEngine/Game/effects/effects.cpp index 7f8b9b7ac..fe396384e 100644 --- a/TombEngine/Game/effects/effects.cpp +++ b/TombEngine/Game/effects/effects.cpp @@ -1214,19 +1214,11 @@ void SetupRipple(int x, int y, int z, int size, int flags) void WadeSplash(ItemInfo* item, int wh, int wd) { - short roomNumber = item->RoomNumber; - GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber); - - auto* room = &g_Level.Rooms[roomNumber]; - if (!TestEnvironment(ENV_FLAG_WATER, room)) - return; - - short roomNumber2 = item->RoomNumber; - GetFloor(item->Pose.Position.x, room->y - 128, item->Pose.Position.z, &roomNumber2); - - auto* room2 = &g_Level.Rooms[roomNumber2]; - - if (TestEnvironment(ENV_FLAG_WATER, room2)) + auto probe1 = GetCollision(item); + auto probe2 = GetCollision(probe1.Block, item->Pose.Position.x, probe1.Position.Ceiling, item->Pose.Position.z); + + if (!TestEnvironment(ENV_FLAG_WATER, probe1.RoomNumber) || + TestEnvironment(ENV_FLAG_WATER, probe1.RoomNumber) == TestEnvironment(ENV_FLAG_WATER, probe2.RoomNumber)) return; auto* frame = GetBestFrame(item); @@ -1256,7 +1248,7 @@ void WadeSplash(ItemInfo* item, int wh, int wd) SplashSetup.z = item->Pose.Position.z; SplashSetup.innerRadius = 16; SplashSetup.splashPower = item->Animation.Velocity; - SetupSplash(&SplashSetup, roomNumber); + SetupSplash(&SplashSetup, probe1.RoomNumber); SplashCount = 16; } } @@ -1390,52 +1382,6 @@ void TriggerRocketFire(int x, int y, int z) void TriggerRocketSmoke(int x, int y, int z, int bodyPart) { - /*auto* sptr = GetFreeParticle(); - - sptr->on = true; - sptr->sR = 0; - sptr->sG = 0; - sptr->sB = 0; - - sptr->dR = 64 + bodyPart; - sptr->dG = 64 + bodyPart; - sptr->dB = 64 + bodyPart; - - sptr->colFadeSpeed = 4 + (GetRandomControl() & 3); - sptr->fadeToBlack = 12; - sptr->sLife = sptr->life = (GetRandomControl() & 3) + 20; - sptr->blendMode = BLEND_MODES::BLENDMODE_ADDITIVE; - sptr->extras = 0; - sptr->dynamic = -1; - - sptr->x = x + ((GetRandomControl() & 15) - 8); - sptr->y = y + ((GetRandomControl() & 15) - 8); - sptr->z = z + ((GetRandomControl() & 15) - 8); - sptr->xVel = ((GetRandomControl() & 255) - 128); - sptr->yVel = -(GetRandomControl() & 3) - 4; - sptr->zVel = ((GetRandomControl() & 255) - 128); - sptr->friction = 4; - - if (GetRandomControl() & 1) - { - sptr->flags = SP_SCALE | SP_DEF | SP_ROTATE | SP_EXPDEF; - sptr->rotAng = GetRandomControl() & 4095; - if (GetRandomControl() & 1) - sptr->rotAdd = -(GetRandomControl() & 15) - 16; - else - sptr->rotAdd = (GetRandomControl() & 15) + 16; - } - else - sptr->flags = SP_SCALE | SP_DEF | SP_EXPDEF; - - // TODO: right sprite - sptr->def = Objects[ID_DEFAULT_SPRITES].meshIndex; - sptr->scalar = 15; - sptr->gravity = -(GetRandomControl() & 3) - 4; - sptr->maxYvel = -(GetRandomControl() & 3) - 4; - - int size = (GetRandomControl() & 7) + 32; - sptr->size = sptr->sSize = size >> 2;*/ TEN::Effects::Smoke::TriggerRocketSmoke(x, y, z, 0); } diff --git a/TombEngine/Game/gui.cpp b/TombEngine/Game/gui.cpp index d53db7d11..7eaa1bd2b 100644 --- a/TombEngine/Game/gui.cpp +++ b/TombEngine/Game/gui.cpp @@ -803,7 +803,10 @@ void GuiController::HandleDisplaySettingsInput(bool pause) case 4: SoundEffect(SFX_TR4_MENU_CHOOSE, nullptr, SoundEnvironment::Always); - CurrentSettings.conf.EnableVolumetricFog = !CurrentSettings.conf.EnableVolumetricFog; + if (CurrentSettings.conf.Antialiasing == AntialiasingMode::None) + CurrentSettings.conf.Antialiasing = AntialiasingMode::High; + else + CurrentSettings.conf.Antialiasing = AntialiasingMode(int(CurrentSettings.conf.Antialiasing) - 1); break; } } @@ -838,7 +841,10 @@ void GuiController::HandleDisplaySettingsInput(bool pause) case 4: SoundEffect(SFX_TR4_MENU_CHOOSE, nullptr, SoundEnvironment::Always); - CurrentSettings.conf.EnableVolumetricFog = !CurrentSettings.conf.EnableVolumetricFog; + if (CurrentSettings.conf.Antialiasing == AntialiasingMode::High) + CurrentSettings.conf.Antialiasing = AntialiasingMode::None; + else + CurrentSettings.conf.Antialiasing = AntialiasingMode(int(CurrentSettings.conf.Antialiasing) + 1); break; } } diff --git a/TombEngine/Objects/TR3/Vehicles/big_gun.cpp b/TombEngine/Objects/TR3/Vehicles/big_gun.cpp index ecf69fb93..dbcdcfff9 100644 --- a/TombEngine/Objects/TR3/Vehicles/big_gun.cpp +++ b/TombEngine/Objects/TR3/Vehicles/big_gun.cpp @@ -195,7 +195,7 @@ namespace TEN::Entities::Vehicles bigGun->Flags = BGUN_FLAG_AUTO_ROT; else { - if (TrInput & VEHICLE_IN_FIRE && !bigGun->FireCount) + if (TrInput & (VEHICLE_IN_ACCELERATE | VEHICLE_IN_FIRE) && !bigGun->FireCount) { BigGunFire(bigGunItem, laraItem); bigGun->FireCount = BGUN_RECOIL_TIME; diff --git a/TombEngine/Objects/TR3/Vehicles/quad_bike.cpp b/TombEngine/Objects/TR3/Vehicles/quad_bike.cpp index 950a4e3d9..e3d251891 100644 --- a/TombEngine/Objects/TR3/Vehicles/quad_bike.cpp +++ b/TombEngine/Objects/TR3/Vehicles/quad_bike.cpp @@ -959,7 +959,7 @@ namespace TEN::Entities::Vehicles } // Driving back / braking. - if (TrInput & (VEHICLE_IN_REVERSE | VEHICLE_IN_BRAKE)) + if (TrInput & VEHICLE_IN_REVERSE) { if (TrInput & VEHICLE_IN_SPEED && (quadBike->CanStartDrift || quadBike->DriftStarting)) diff --git a/TombEngine/Objects/TR5/Object/tr5_rollingball.cpp b/TombEngine/Objects/TR5/Object/tr5_rollingball.cpp index 8531fbef2..213d11b6b 100644 --- a/TombEngine/Objects/TR5/Object/tr5_rollingball.cpp +++ b/TombEngine/Objects/TR5/Object/tr5_rollingball.cpp @@ -4,6 +4,7 @@ #include "Game/camera.h" #include "Game/control/control.h" #include "Game/Lara/lara.h" +#include "Game/Lara/lara_helpers.h" #include "Specific/setup.h" #include "Sound/sound.h" #include "Game/effects/effects.h" @@ -19,11 +20,14 @@ void RollingBallCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* c { auto* ballItem = &g_Level.Items[itemNumber]; - if (TestBoundsCollide(ballItem, laraItem, coll->Setup.Radius)) + if (TestBoundsCollide(ballItem, laraItem, coll->Setup.Radius) && + TestCollision(ballItem, laraItem)) { - if (TestCollision(ballItem, laraItem)) + if (TriggerActive(ballItem) && (ballItem->ItemFlags[0] || ballItem->Animation.VerticalVelocity)) { - if (TriggerActive(ballItem) && (ballItem->ItemFlags[0] || ballItem->Animation.VerticalVelocity)) + if (laraItem->Animation.IsAirborne || TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, laraItem)) + laraItem->HitPoints = 0; + else { laraItem->Animation.AnimNumber = LA_BOULDER_DEATH; laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; @@ -31,9 +35,9 @@ void RollingBallCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* c laraItem->Animation.ActiveState = LS_DEATH; laraItem->Animation.IsAirborne = false; } - else - ObjectCollision(itemNumber, laraItem, coll); } + else + ObjectCollision(itemNumber, laraItem, coll); } } diff --git a/TombEngine/Renderer/ConstantBuffers/PostProcessBuffer.h b/TombEngine/Renderer/ConstantBuffers/PostProcessBuffer.h index 77172ae4e..85a863d60 100644 --- a/TombEngine/Renderer/ConstantBuffers/PostProcessBuffer.h +++ b/TombEngine/Renderer/ConstantBuffers/PostProcessBuffer.h @@ -4,4 +4,5 @@ struct alignas(16) CPostProcessBuffer float ScreenFadeFactor; int ViewportWidth; int ViewportHeight; + int FXAA; }; diff --git a/TombEngine/Renderer/RenderTarget2D/RenderTarget2D.cpp b/TombEngine/Renderer/RenderTarget2D/RenderTarget2D.cpp index 1236eeb0d..24db5403b 100644 --- a/TombEngine/Renderer/RenderTarget2D/RenderTarget2D.cpp +++ b/TombEngine/Renderer/RenderTarget2D/RenderTarget2D.cpp @@ -1,6 +1,7 @@ #include "framework.h" #include "RenderTarget2D.h" #include "Utils.h" +#include "Specific/configuration.h" namespace TEN::Renderer { @@ -8,13 +9,36 @@ namespace TEN::Renderer RenderTarget2D::RenderTarget2D(ID3D11Device* device, int w, int h, DXGI_FORMAT colorFormat, DXGI_FORMAT depthFormat) { + // Check if antialiasing quality is available, and set it if it is. + + D3D11_SRV_DIMENSION srvDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + D3D11_DSV_DIMENSION dsvDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + D3D11_RTV_DIMENSION rtvDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + int sampleCount = 1; + + if (g_Configuration.Antialiasing > AntialiasingMode::Low) + { + int potentialSampleCount = (g_Configuration.Antialiasing == AntialiasingMode::Medium) ? 2 : 4; + unsigned int qualityLevels = 0; + if (device->CheckMultisampleQualityLevels(colorFormat, potentialSampleCount, &qualityLevels) == S_OK && + qualityLevels > 0) + { + sampleCount = potentialSampleCount; + dsvDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + rtvDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + srvDimension = D3D11_SRV_DIMENSION_TEXTURE2DMS; + } + } + + // Now set up render target. + D3D11_TEXTURE2D_DESC desc = {}; desc.Width = w; desc.Height = h; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = colorFormat; - desc.SampleDesc.Count = 1; + desc.SampleDesc.Count = sampleCount; desc.SampleDesc.Quality = 0; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; @@ -26,7 +50,7 @@ namespace TEN::Renderer D3D11_RENDER_TARGET_VIEW_DESC viewDesc; viewDesc.Format = desc.Format; - viewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + viewDesc.ViewDimension = rtvDimension; viewDesc.Texture2D.MipSlice = 0; res = device->CreateRenderTargetView(Texture.Get(), &viewDesc, &RenderTargetView); @@ -35,7 +59,7 @@ namespace TEN::Renderer // Setup the description of the shader resource view. D3D11_SHADER_RESOURCE_VIEW_DESC shaderDesc; shaderDesc.Format = desc.Format; - shaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + shaderDesc.ViewDimension = srvDimension; shaderDesc.Texture2D.MostDetailedMip = 0; shaderDesc.Texture2D.MipLevels = 1; @@ -47,7 +71,7 @@ namespace TEN::Renderer depthTexDesc.Height = h; depthTexDesc.MipLevels = 1; depthTexDesc.ArraySize = 1; - depthTexDesc.SampleDesc.Count = 1; + depthTexDesc.SampleDesc.Count = sampleCount; depthTexDesc.SampleDesc.Quality = 0; depthTexDesc.Format = depthFormat; depthTexDesc.Usage = D3D11_USAGE_DEFAULT; @@ -61,7 +85,7 @@ namespace TEN::Renderer D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; dsvDesc.Format = depthTexDesc.Format; dsvDesc.Flags = 0; - dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + dsvDesc.ViewDimension = dsvDimension; dsvDesc.Texture2D.MipSlice = 0; res = device->CreateDepthStencilView(DepthStencilTexture.Get(), &dsvDesc, &DepthStencilView); diff --git a/TombEngine/Renderer/Renderer11.h b/TombEngine/Renderer/Renderer11.h index 9b90f8520..34a7a3d43 100644 --- a/TombEngine/Renderer/Renderer11.h +++ b/TombEngine/Renderer/Renderer11.h @@ -524,7 +524,7 @@ namespace TEN::Renderer void DrawSimpleParticles(RenderView& view); void DrawFootprints(RenderView& view); void DrawLoadingBar(float percent); - void DrawFadeAndBars(ID3D11RenderTargetView* target, ID3D11DepthStencilView* depthTarget, RenderView& view); + void DrawPostprocess(ID3D11RenderTargetView* target, ID3D11DepthStencilView* depthTarget, RenderView& view); void RenderInventoryScene(ID3D11RenderTargetView* target, ID3D11DepthStencilView* depthTarget, ID3D11ShaderResourceView* background); diff --git a/TombEngine/Renderer/Renderer11Draw.cpp b/TombEngine/Renderer/Renderer11Draw.cpp index ddb9b5b95..18a7aae33 100644 --- a/TombEngine/Renderer/Renderer11Draw.cpp +++ b/TombEngine/Renderer/Renderer11Draw.cpp @@ -357,56 +357,54 @@ namespace TEN::Renderer void Renderer11::DrawRopes(RenderView& view) { - for (int n = 0; n < Ropes.size(); n++) + for (auto& rope : Ropes) { - ROPE_STRUCT* rope = &Ropes[n]; + if (!rope.active) + continue; - if (rope->active) + Matrix world = Matrix::CreateTranslation( + rope.position.x, + rope.position.y, + rope.position.z + ); + + Vector3 absolute[24]; + + for (int n = 0; n < ROPE_SEGMENTS; n++) { - Matrix world = Matrix::CreateTranslation( - rope->position.x, - rope->position.y, - rope->position.z - ); + Vector3Int* s = &rope.meshSegment[n]; + Vector3 t; + Vector3 output; - Vector3 absolute[24]; + t.x = s->x >> FP_SHIFT; + t.y = s->y >> FP_SHIFT; + t.z = s->z >> FP_SHIFT; - for (int n = 0; n < ROPE_SEGMENTS; n++) - { - Vector3Int* s = &rope->meshSegment[n]; - Vector3 t; - Vector3 output; + output = Vector3::Transform(t, world); - t.x = s->x >> FP_SHIFT; - t.y = s->y >> FP_SHIFT; - t.z = s->z >> FP_SHIFT; + Vector3 absolutePosition = Vector3(output.x, output.y, output.z); + absolute[n] = absolutePosition; + } - output = Vector3::Transform(t, world); + for (int n = 0; n < ROPE_SEGMENTS - 1; n++) + { + Vector3 pos1 = Vector3(absolute[n].x, absolute[n].y, absolute[n].z); + Vector3 pos2 = Vector3(absolute[n + 1].x, absolute[n + 1].y, absolute[n + 1].z); - Vector3 absolutePosition = Vector3(output.x, output.y, output.z); - absolute[n] = absolutePosition; - } + Vector3 d = pos2 - pos1; + d.Normalize(); - for (int n = 0; n < ROPE_SEGMENTS - 1; n++) - { - Vector3 pos1 = Vector3(absolute[n].x, absolute[n].y, absolute[n].z); - Vector3 pos2 = Vector3(absolute[n + 1].x, absolute[n + 1].y, absolute[n + 1].z); + Vector3 c = (pos1 + pos2) / 2.0f; - Vector3 d = pos2 - pos1; - d.Normalize(); - - Vector3 c = (pos1 + pos2) / 2.0f; - - AddSpriteBillboardConstrained(&m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_EMPTY1], - c, - m_rooms[rope->room].AmbientLight, - (PI / 2), - 1.0f, - { 32, - Vector3::Distance(pos1, pos2) }, - BLENDMODE_ALPHABLEND, - d, view); - } + AddSpriteBillboardConstrained(&m_sprites[Objects[ID_DEFAULT_SPRITES].meshIndex + SPR_EMPTY1], + c, + m_rooms[rope.room].AmbientLight, + (PI / 2), + 1.0f, + { 32, + Vector3::Distance(pos1, pos2) }, + BLENDMODE_ALPHATEST, + d, view); } } } @@ -1550,6 +1548,8 @@ namespace TEN::Renderer DrawTransparentFaces(view); + DrawPostprocess(target, depthTarget, view); + // Draw GUI stuff at the end DrawLines2D(); @@ -1565,7 +1565,6 @@ namespace TEN::Renderer DrawDebugInfo(view); DrawAllStrings(); - DrawFadeAndBars(target, depthTarget, view); ClearScene(); } @@ -2281,8 +2280,9 @@ namespace TEN::Renderer BindTexture(TEXTURE_NORMAL_MAP, &std::get<1>(m_moveablesTextures[bucket.Texture]), SAMPLER_NONE); - SetBlendMode(bucket.BlendMode); - SetAlphaTest(bucket.BlendMode == BLEND_MODES::BLENDMODE_ALPHATEST ? ALPHA_TEST_GREATER_THAN : ALPHA_TEST_NONE, ALPHA_TEST_THRESHOLD); + // Always render horizon as alpha-blended surface + SetBlendMode(bucket.BlendMode == BLEND_MODES::BLENDMODE_ALPHATEST ? BLEND_MODES::BLENDMODE_ALPHABLEND : bucket.BlendMode); + SetAlphaTest(ALPHA_TEST_NONE, ALPHA_TEST_THRESHOLD); // Draw vertices DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0); diff --git a/TombEngine/Renderer/Renderer11Draw2D.cpp b/TombEngine/Renderer/Renderer11Draw2D.cpp index bbb5ff15c..b7e1752f5 100644 --- a/TombEngine/Renderer/Renderer11Draw2D.cpp +++ b/TombEngine/Renderer/Renderer11Draw2D.cpp @@ -251,8 +251,7 @@ namespace TEN::Renderer } } - void Renderer11::DrawFadeAndBars(ID3D11RenderTargetView* target, ID3D11DepthStencilView* depthTarget, - RenderView& view) + void Renderer11::DrawPostprocess(ID3D11RenderTargetView* target, ID3D11DepthStencilView* depthTarget, RenderView& view) { SetBlendMode(BLENDMODE_OPAQUE); SetCullMode(CULL_MODE_CCW); @@ -299,6 +298,7 @@ namespace TEN::Renderer m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetInputLayout(m_inputLayout.Get()); + m_stPostProcessBuffer.FXAA = g_Configuration.Antialiasing == AntialiasingMode::Low ? 1 : 0; m_stPostProcessBuffer.ViewportWidth = m_screenWidth; m_stPostProcessBuffer.ViewportHeight = m_screenHeight; m_stPostProcessBuffer.ScreenFadeFactor = ScreenFadeCurrent; diff --git a/TombEngine/Renderer/Renderer11DrawEffect.cpp b/TombEngine/Renderer/Renderer11DrawEffect.cpp index ed6688e28..65b4d3689 100644 --- a/TombEngine/Renderer/Renderer11DrawEffect.cpp +++ b/TombEngine/Renderer/Renderer11DrawEffect.cpp @@ -808,9 +808,20 @@ namespace TEN::Renderer void Renderer11::DrawSprites(RenderView& view) { const int numSpritesToDraw = view.spritesToDraw.size(); + + if (numSpritesToDraw == 0) + return; BindRenderTargetAsTexture(TEXTURE_DEPTH_MAP, &m_depthMap, SAMPLER_LINEAR_CLAMP); + SetDepthState(DEPTH_STATE_READ_ONLY_ZBUFFER); + SetCullMode(CULL_MODE_NONE); + + m_context->VSSetShader(m_vsSprites.Get(), NULL, 0); + m_context->PSSetShader(m_psSprites.Get(), NULL, 0); + + BindConstantBufferVS(CB_SPRITE, m_cbSprite.get()); + std::sort( view.spritesToDraw.begin(), view.spritesToDraw.end(), @@ -853,9 +864,8 @@ namespace TEN::Renderer spriteMatrix = Matrix::Identity; } - if (DoesBlendModeRequireSorting(spr.BlendMode)) + if (DoesBlendModeRequireSorting(spr.BlendMode)) // Collect sprites { - // Collect sprites int distance = (spr.pos - Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z)).Length(); RendererTransparentFace face; face.type = RendererTransparentFaceType::TRANSPARENT_FACE_SPRITE; @@ -867,36 +877,31 @@ namespace TEN::Renderer RendererRoom& room = m_rooms[FindRoomNumber(Vector3Int(spr.pos))]; room.TransparentFacesToDraw.push_back(face); } - else + else // Draw sprites immediately { - // Draw sprites immediately - SetBlendMode(spr.BlendMode); + // Set up vertex buffer and parameters + UINT stride = sizeof(RendererVertex); + UINT offset = 0; + m_context->IASetInputLayout(m_inputLayout.Get()); + m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + m_context->IASetVertexBuffers(0, 1, quadVertexBuffer.GetAddressOf(), &stride, &offset); + SetBlendMode(spr.BlendMode); BindTexture(TEXTURE_COLOR_MAP, spr.Sprite->Texture, SAMPLER_LINEAR_CLAMP); + if (spr.BlendMode == BLEND_MODES::BLENDMODE_ALPHATEST) + SetAlphaTest(ALPHA_TEST_GREATER_THAN, ALPHA_TEST_THRESHOLD, true); + else + SetAlphaTest(ALPHA_TEST_NONE, 0); + Matrix scale = Matrix::CreateScale((spr.Width) * spr.Scale, (spr.Height) * spr.Scale, spr.Scale); if (spr.Type == RENDERER_SPRITE_TYPE::SPRITE_TYPE_BILLBOARD) { - UINT stride = sizeof(RendererVertex); - UINT offset = 0; - - SetDepthState(DEPTH_STATE_READ_ONLY_ZBUFFER); - SetCullMode(CULL_MODE_NONE); - SetAlphaTest(ALPHA_TEST_GREATER_THAN, ALPHA_TEST_THRESHOLD); - - m_context->VSSetShader(m_vsSprites.Get(), NULL, 0); - m_context->PSSetShader(m_psSprites.Get(), NULL, 0); - - m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - m_context->IASetInputLayout(m_inputLayout.Get()); - m_context->IASetVertexBuffers(0, 1, quadVertexBuffer.GetAddressOf(), &stride, &offset); - m_stSprite.billboardMatrix = spriteMatrix; m_stSprite.color = spr.color; m_stSprite.isBillboard = true; m_cbSprite.updateData(m_stSprite, m_context.Get()); - BindConstantBufferVS(CB_SPRITE, m_cbSprite.get()); m_context->Draw(4, 0); m_numDrawCalls++; @@ -906,26 +911,10 @@ namespace TEN::Renderer } else if (spr.Type == RENDERER_SPRITE_TYPE::SPRITE_TYPE_BILLBOARD_CUSTOM) { - UINT stride = sizeof(RendererVertex); - UINT offset = 0; - - SetDepthState(DEPTH_STATE_READ_ONLY_ZBUFFER); - SetCullMode(CULL_MODE_NONE); - - m_context->VSSetShader(m_vsSprites.Get(), NULL, 0); - m_context->PSSetShader(m_psSprites.Get(), NULL, 0); - - SetAlphaTest(ALPHA_TEST_GREATER_THAN, ALPHA_TEST_THRESHOLD); - - m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - m_context->IASetInputLayout(m_inputLayout.Get()); - m_context->IASetVertexBuffers(0, 1, quadVertexBuffer.GetAddressOf(), &stride, &offset); - m_stSprite.billboardMatrix = spriteMatrix; m_stSprite.color = spr.color; m_stSprite.isBillboard = true; m_cbSprite.updateData(m_stSprite, m_context.Get()); - BindConstantBufferVS(CB_SPRITE, m_cbSprite.get()); m_context->Draw(4, 0); m_numDrawCalls++; @@ -935,25 +924,10 @@ namespace TEN::Renderer } else if (spr.Type == RENDERER_SPRITE_TYPE::SPRITE_TYPE_BILLBOARD_LOOKAT) { - UINT stride = sizeof(RendererVertex); - UINT offset = 0; - SetDepthState(DEPTH_STATE_READ_ONLY_ZBUFFER); - SetCullMode(CULL_MODE_NONE); - - m_context->VSSetShader(m_vsSprites.Get(), NULL, 0); - m_context->PSSetShader(m_psSprites.Get(), NULL, 0); - - SetAlphaTest(ALPHA_TEST_GREATER_THAN, ALPHA_TEST_THRESHOLD); - - m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - m_context->IASetInputLayout(m_inputLayout.Get()); - m_context->IASetVertexBuffers(0, 1, quadVertexBuffer.GetAddressOf(), &stride, &offset); - m_stSprite.billboardMatrix = spriteMatrix; m_stSprite.color = spr.color; m_stSprite.isBillboard = true; m_cbSprite.updateData(m_stSprite, m_context.Get()); - BindConstantBufferVS(CB_SPRITE, m_cbSprite.get()); m_context->Draw(4, 0); m_numDrawCalls++; @@ -963,19 +937,6 @@ namespace TEN::Renderer } else if (spr.Type == RENDERER_SPRITE_TYPE::SPRITE_TYPE_3D) { - UINT stride = sizeof(RendererVertex); - UINT offset = 0; - SetDepthState(DEPTH_STATE_READ_ONLY_ZBUFFER); - SetCullMode(CULL_MODE_NONE); - - m_context->VSSetShader(m_vsSprites.Get(), NULL, 0); - m_context->PSSetShader(m_psSprites.Get(), NULL, 0); - - SetAlphaTest(ALPHA_TEST_GREATER_THAN, ALPHA_TEST_THRESHOLD); - - m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); - m_context->IASetInputLayout(m_inputLayout.Get()); - m_context->IASetVertexBuffers(0, 1, quadVertexBuffer.GetAddressOf(), &stride, &offset); Vector3 p0t = spr.vtx1; Vector3 p1t = spr.vtx2; Vector3 p2t = spr.vtx3; @@ -1016,7 +977,6 @@ namespace TEN::Renderer m_stSprite.color = spr.color; m_stSprite.isBillboard = false; m_cbSprite.updateData(m_stSprite, m_context.Get()); - BindConstantBufferVS(CB_SPRITE, m_cbSprite.get()); m_primitiveBatch->Begin(); m_primitiveBatch->DrawTriangle(v0, v1, v3); diff --git a/TombEngine/Renderer/Renderer11DrawMenu.cpp b/TombEngine/Renderer/Renderer11DrawMenu.cpp index 91b61ff08..1d1d832b9 100644 --- a/TombEngine/Renderer/Renderer11DrawMenu.cpp +++ b/TombEngine/Renderer/Renderer11DrawMenu.cpp @@ -101,6 +101,27 @@ namespace TEN::Renderer auto* shadowMode = g_Gui.GetCurrentSettings().conf.ShadowType != ShadowMode::None ? (g_Gui.GetCurrentSettings().conf.ShadowType == ShadowMode::Lara ? STRING_SHADOWS_PLAYER : STRING_SHADOWS_ALL) : STRING_SHADOWS_NONE; + const char* antialiasMode; + switch (g_Gui.GetCurrentSettings().conf.Antialiasing) + { + default: + case AntialiasingMode::None: + antialiasMode = STRING_ANTIALIASING_NONE; + break; + + case AntialiasingMode::Low: + antialiasMode = STRING_ANTIALIASING_LOW; + break; + + case AntialiasingMode::Medium: + antialiasMode = STRING_ANTIALIASING_MEDIUM; + break; + + case AntialiasingMode::High: + antialiasMode = STRING_ANTIALIASING_HIGH; + break; + } + switch (menu) { case Menu::Options: @@ -149,9 +170,9 @@ namespace TEN::Renderer AddString(MenuRightSideEntry, y, Str_Enabled(g_Gui.GetCurrentSettings().conf.EnableCaustics), PRINTSTRING_COLOR_WHITE, SF(title_option == 3)); GetNextLinePosition(&y); - // Enable volumetric fog - AddString(MenuLeftSideEntry, y, g_GameFlow->GetString(STRING_VOLUMETRIC_FOG), PRINTSTRING_COLOR_ORANGE, SF(title_option == 4)); - AddString(MenuRightSideEntry, y, Str_Enabled(g_Gui.GetCurrentSettings().conf.EnableVolumetricFog), PRINTSTRING_COLOR_WHITE, SF(title_option == 4)); + // Enable antialiasing + AddString(MenuLeftSideEntry, y, g_GameFlow->GetString(STRING_ANTIALIASING), PRINTSTRING_COLOR_ORANGE, SF(title_option == 4)); + AddString(MenuRightSideEntry, y, g_GameFlow->GetString(antialiasMode), PRINTSTRING_COLOR_WHITE, SF(title_option == 4)); GetNextBlockPosition(&y); // Apply @@ -468,6 +489,7 @@ namespace TEN::Renderer g_Gui.DrawAmmoSelector(); g_Gui.FadeAmmoSelector(); g_Gui.DrawCompass(); + DrawAllStrings(); } diff --git a/TombEngine/Renderer/Renderer11Enums.h b/TombEngine/Renderer/Renderer11Enums.h index e717876ad..a12b35921 100644 --- a/TombEngine/Renderer/Renderer11Enums.h +++ b/TombEngine/Renderer/Renderer11Enums.h @@ -69,6 +69,14 @@ enum class ShadowMode All }; +enum class AntialiasingMode +{ + None, + Low, + Medium, + High +}; + enum LIGHT_MODES { LIGHT_MODE_DYNAMIC, diff --git a/TombEngine/Renderer/Renderer11Frame.cpp b/TombEngine/Renderer/Renderer11Frame.cpp index 1847c2063..7c3bc1f81 100644 --- a/TombEngine/Renderer/Renderer11Frame.cpp +++ b/TombEngine/Renderer/Renderer11Frame.cpp @@ -363,10 +363,8 @@ namespace TEN::Renderer if (obj.ShadowType == ShadowMode::None) { auto bounds = TO_DX_BBOX(item->Pose, GetBoundsAccurate(item)); - Vector3 min = bounds.Center - bounds.Extents; - Vector3 max = bounds.Center + bounds.Extents; - - if (!renderView.camera.frustum.AABBInFrustum(min, max)) + auto length = Vector3(bounds.Extents).Length(); + if (!renderView.camera.frustum.SphereInFrustum(bounds.Center, length)) continue; } @@ -419,10 +417,8 @@ namespace TEN::Renderer auto stat = &StaticObjects[mesh->staticNumber]; auto bounds = TO_DX_BBOX(mesh->pos, &stat->visibilityBox); - Vector3 min = bounds.Center - bounds.Extents; - Vector3 max = bounds.Center + bounds.Extents; - - if (!renderView.camera.frustum.AABBInFrustum(min, max)) + auto length = Vector3(bounds.Extents).Length(); + if (!renderView.camera.frustum.SphereInFrustum(bounds.Center, length)) continue; std::vector lights; diff --git a/TombEngine/Resources.aps b/TombEngine/Resources.aps index 5df3b48f2..82126b3c4 100644 Binary files a/TombEngine/Resources.aps and b/TombEngine/Resources.aps differ diff --git a/TombEngine/Resources.rc b/TombEngine/Resources.rc index 3e96a5167..067fbad65 100644 --- a/TombEngine/Resources.rc +++ b/TombEngine/Resources.rc @@ -27,7 +27,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,0 - PRODUCTVERSION 1,5,1,0 + PRODUCTVERSION 1,5,2,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -48,7 +48,7 @@ BEGIN VALUE "LegalCopyright", "Copyright (C) 2022" VALUE "OriginalFilename", "TombEngine.exe" VALUE "ProductName", "Tomb Engine" - VALUE "ProductVersion", "1.5.1.0" + VALUE "ProductVersion", "1.5.2.0" END END BLOCK "VarFileInfo" @@ -82,7 +82,7 @@ BEGIN CONTROL "TXT_Windowed",IDC_WINDOWED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,103,115,10 CONTROL "TXT Shadows",IDC_SHADOWS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,155,79,159,10 CONTROL "TXT Caustics",IDC_CAUSTICS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,155,94,160,10 - CONTROL "TXT Volumetric Fog",IDC_VOLUMETRIC_FOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,155,109,160,10 + CONTROL "TXT Antialiasing",IDC_ANTIALIASING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,155,109,160,10 GROUPBOX "Txt_Sound",IDC_GROUP_SOUND,7,151,320,33 CONTROL "TXT_Enable",IDC_ENABLE_SOUNDS,"Button",BS_AUTOCHECKBOX | BS_LEFTTEXT | WS_TABSTOP,254,150,67,10 COMBOBOX IDC_SNDADAPTER,13,164,308,71,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP diff --git a/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h b/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h index 0fec2ec70..872adfb79 100644 --- a/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h +++ b/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h @@ -33,7 +33,6 @@ public: virtual bool HasSlideExtended() const = 0; virtual ScriptInterfaceLevel * GetLevel(int level) = 0; virtual int GetLevelNumber(std::string const& flieName) = 0; - virtual short GetGameFarView() const = 0; virtual bool CanPlayAnyLevel() const = 0; virtual bool DoFlow() = 0; }; diff --git a/TombEngine/Scripting/Include/Strings/ScriptInterfaceStringsHandler.h b/TombEngine/Scripting/Include/Strings/ScriptInterfaceStringsHandler.h index e163af258..146ebb739 100644 --- a/TombEngine/Scripting/Include/Strings/ScriptInterfaceStringsHandler.h +++ b/TombEngine/Scripting/Include/Strings/ScriptInterfaceStringsHandler.h @@ -10,6 +10,7 @@ class ScriptInterfaceStringsHandler { public: virtual ~ScriptInterfaceStringsHandler() = default; virtual void ProcessDisplayStrings(float dt) = 0; + virtual void ClearDisplayStrings() = 0; virtual void SetCallbackDrawString(CallbackDrawString) = 0; }; diff --git a/TombEngine/Scripting/Internal/LanguageScript.h b/TombEngine/Scripting/Internal/LanguageScript.h index b9d660c9b..52217c114 100644 --- a/TombEngine/Scripting/Internal/LanguageScript.h +++ b/TombEngine/Scripting/Internal/LanguageScript.h @@ -68,6 +68,11 @@ #define STRING_SHADOWS_NONE "none" #define STRING_CAUSTICS "caustics" #define STRING_VOLUMETRIC_FOG "volumetric_fog" +#define STRING_ANTIALIASING "antialiasing" +#define STRING_ANTIALIASING_NONE "none" +#define STRING_ANTIALIASING_LOW "low" +#define STRING_ANTIALIASING_MEDIUM "medium" +#define STRING_ANTIALIASING_HIGH "high" #define STRING_APPLY "apply" #define STRING_BACK "back" #define STRING_SOUND "sound" diff --git a/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp b/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp index 0d7e198bf..c70207aaa 100644 --- a/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp +++ b/TombEngine/Scripting/Internal/TEN/Effects/EffectsFunctions.cpp @@ -107,7 +107,7 @@ namespace Effects @tparam float rot (default 0) specifies a speed with which it will rotate (0 = no rotation, negative = anticlockwise rotation, positive = clockwise rotation). @tparam Color startColor (default Color(255, 255, 255)) color at start of life @tparam Color endColor (default Color(255, 255, 255)) color to fade to - at the time of writing this fade will finish long before the end of the particle's life due to internal maths - @tparam BlendID blendMode (default TEN.Misc.BlendID.ALPHABLEND) How will we blend this with its surroundings? + @tparam BlendID blendMode (default TEN.Effects.BlendID.ALPHABLEND) How will we blend this with its surroundings? @tparam int startSize (default 10) Size on spawn. A value of 15 is approximately the size of Lara's head. @tparam int endSize (default 0) Size on death - the particle will linearly shrink or grow to this size during its lifespan @tparam float lifetime (default 2) Lifespan in seconds @@ -122,7 +122,7 @@ namespace Effects -2, -- rot Color(255, 0, 0), -- startColor Color(0, 255, 0), -- endColor - TEN.Misc.BlendID.ADDITIVE, -- blendMode + TEN.Effects.BlendID.ADDITIVE, -- blendMode 15, -- startSize 50, -- endSize 20, -- lifetime diff --git a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp index 1bdcef58e..b1828248e 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp @@ -61,15 +61,6 @@ __(not yet implemented)__ */ table_flow.set_function(ScriptReserved_SetTitleScreenImagePath, &FlowHandler::SetTitleScreenImagePath, this); -/*** Maximum draw distance. -The maximum draw distance, in sectors (blocks), of any level in the game. -This is equivalent to TRNG's WorldFarView variable. -__(not yet implemented)__ -@function SetFarView -@tparam byte farview Number of sectors. Must be in the range [1, 127]. -*/ - table_flow.set_function(ScriptReserved_SetFarView, &FlowHandler::SetGameFarView, this); - /*** settings.lua. These functions are called in settings.lua, a file which holds your local settings. settings.lua shouldn't be bundled with any finished levels/games. @@ -171,19 +162,6 @@ void FlowHandler::SetTitleScreenImagePath(std::string const& path) TitleScreenImagePath = path; } -void FlowHandler::SetGameFarView(short val) -{ - bool cond = val <= 127 && val >= 1; - std::string msg{ "Game far view value must be in the range [1, 255]." }; - if (!ScriptAssert(cond, msg)) - { - ScriptWarn("Setting game far view to 32."); - GameFarView = 32; - } - else - GameFarView = val; -} - void FlowHandler::LoadFlowScript() { m_handler.ExecuteScript("Scripts/Gameflow.lua"); @@ -260,7 +238,16 @@ bool FlowHandler::DoFlow() if (CurrentLevel == 0) { - status = DoTitle(0, level->AmbientTrack); + try + { + status = DoTitle(0, level->AmbientTrack); + } + catch (TENScriptException const& e) + { + std::string msg = std::string{ "An unrecoverable error occurred in " } + __func__ + ": " + e.what(); + TENLog(msg, LogLevel::Error, LogConfig::All); + throw; + } } else { @@ -284,7 +271,16 @@ bool FlowHandler::DoFlow() } } - status = DoLevel(CurrentLevel, level->AmbientTrack, loadFromSavegame); + try + { + status = DoLevel(CurrentLevel, level->AmbientTrack, loadFromSavegame); + } + catch (TENScriptException const& e) + { + std::string msg = std::string{ "An unrecoverable error occurred in " } + __func__ + ": " + e.what(); + TENLog(msg, LogLevel::Error, LogConfig::All); + status = GameStatus::ExitToTitle; + } loadFromSavegame = false; } @@ -329,7 +325,3 @@ bool FlowHandler::CanPlayAnyLevel() const return PlayAnyLevel; } -short FlowHandler::GetGameFarView() const -{ - return GameFarView; -} \ No newline at end of file diff --git a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h index 05709ecad..5c29c2571 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h +++ b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h @@ -27,7 +27,6 @@ public: bool PlayAnyLevel{ true }; bool FlyCheat{ true }; bool DebugMode{ false }; - short GameFarView{ 0 }; // New animation flag table Animations Anims{}; @@ -51,8 +50,6 @@ public: int GetNumLevels() const; void SetIntroImagePath(std::string const& path); void SetTitleScreenImagePath(std::string const& path); - void SetGameFarView(short val); - short GetGameFarView() const; bool IsFlyCheatEnabled() const; bool CanPlayAnyLevel() const; diff --git a/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp b/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp index 3a6c9a3ba..37d8abb75 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp @@ -56,8 +56,6 @@ void Level::Register(sol::table & parent) /// (@{Flow.Fog}) omni fog RGB color and distance. // As seen in TR4's Desert Railroad. // If not provided, distance fog will be black. -// -// __(not yet implemented)__ //@mem fog "fog", &Level::Fog, @@ -117,15 +115,6 @@ e.g. `myLevel.laraType = LaraType.Divesuit` //@mem mirror "mirror", &Level::Mirror, -/*** (byte) The maximum draw distance for level. -Given in sectors (blocks). -Must be in the range [1, 255], and equal to or less than the value passed to SetGameFarView. - -This is equivalent to TRNG's LevelFarView variable. - -__(not yet implemented)__ -@mem farView -*/ "farView", sol::property(&Level::SetLevelFarView), /*** (bool) Enable unlimited oxygen supply when in water. @@ -156,14 +145,25 @@ void Level::SetWeatherStrength(float val) } } +/*** (int) The maximum draw distance for level. +Given in sectors (blocks). +Must be at least 4. + +This is equivalent to TRNG's LevelFarView variable. + +@mem farView +*/ void Level::SetLevelFarView(short val) { - bool cond = val <= 255 && val >= 1; - std::string msg{ "levelFarView value must be in the range [1, 127]." }; + static_assert(MIN_FAR_VIEW == 3200.0f, "Please update the comment, docs, and warning message if this number changes."); + const short min = std::ceil(MIN_FAR_VIEW / SECTOR(1)); + bool cond = val >= min; + + std::string msg{ "farView value must be 4 or greater." }; if (!ScriptAssert(cond, msg)) { - ScriptWarn("Setting levelFarView view to 32."); - LevelFarView = 32; + // Will be set to default by the renderer + LevelFarView = 0; } else { diff --git a/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp b/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp index 6e48bd509..2d7f28eaf 100644 --- a/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp @@ -114,7 +114,7 @@ void LogicHandler::LogPrint(sol::variadic_args va) str += strPart; str += "\t"; } - TENLog(str); + TENLog(str, LogLevel::Info, LogConfig::All, true); } void LogicHandler::ResetScripts(bool clearGameVars) diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp index 8ca51717d..f2354ea10 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp +++ b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp @@ -141,11 +141,11 @@ void Moveable::Register(sol::table & parent) sol::meta_function::equal_to, std::equal_to(), /// Enable the item, as if a trigger for it had been stepped on. -// @function Moveable:EnableItem +// @function Moveable:Enable ScriptReserved_Enable, &Moveable::EnableItem, /// Disable the item -// @function Moveable:DisableItem +// @function Moveable:Disable ScriptReserved_Disable, &Moveable::DisableItem, /// Make the item invisible. Use EnableItem to make it visible again. diff --git a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp index 6829858b7..f04521cf2 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.cpp @@ -88,7 +88,7 @@ void StringsHandler::ProcessDisplayStrings(float dt) while (it != std::end(m_userDisplayStrings)) { auto& str = it->second; - bool endOfLife = (0.0f >= str.m_timeRemaining); + bool endOfLife = 0.0f >= str.m_timeRemaining; if (str.m_deleteWhenZero && endOfLife) { ScriptAssertF(!str.m_isInfinite, "The infinite string {} (key \"{}\") went out of scope without being hidden.", it->first, str.m_key); @@ -116,3 +116,9 @@ void StringsHandler::ProcessDisplayStrings(float dt) } } +void StringsHandler::ClearDisplayStrings() +{ + m_userDisplayStrings.clear(); +} + + diff --git a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.h b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.h index a2f3b3cd2..4c35609dc 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.h +++ b/TombEngine/Scripting/Internal/TEN/Strings/StringsHandler.h @@ -16,6 +16,7 @@ public: StringsHandler(sol::state* lua, sol::table & parent); void SetCallbackDrawString(CallbackDrawString cb) override; void ProcessDisplayStrings(float dt) override; + void ClearDisplayStrings() override; bool SetDisplayString(DisplayStringIDType id, UserDisplayString const& ds); std::optional> GetDisplayString(DisplayStringIDType id); diff --git a/TombEngine/Shaders/DX11_FinalPass.fx b/TombEngine/Shaders/DX11_FinalPass.fx index cb2396784..08cfc5555 100644 --- a/TombEngine/Shaders/DX11_FinalPass.fx +++ b/TombEngine/Shaders/DX11_FinalPass.fx @@ -1,4 +1,9 @@ #include "./VertexInput.hlsli" +#include "./Math.hlsli" + +#define FXAA_SPAN_MAX 8.0 +#define FXAA_REDUCE_MUL 1.0/4.0 +#define FXAA_REDUCE_MIN 1.0/64.0 cbuffer PostProcessBuffer : register(b7) { @@ -6,6 +11,7 @@ cbuffer PostProcessBuffer : register(b7) float ScreenFadeFactor; int ViewportWidth; int ViewportHeight; + int FXAA; }; struct PixelShaderInput @@ -18,6 +24,53 @@ struct PixelShaderInput Texture2D Texture : register(t0); SamplerState Sampler : register(s0); +float4 Antialias(PixelShaderInput input) +{ + float2 add = float2(1.0f, 1.0f) / float2(ViewportWidth, ViewportHeight); + + float3 rgbNW = Texture.Sample(Sampler, input.UV + float2(-add.x, -add.y)); + float3 rgbNE = Texture.Sample(Sampler, input.UV + float2(add.x, -add.y)); + float3 rgbSW = Texture.Sample(Sampler, input.UV + float2(-add.x, add.y)); + float3 rgbSE = Texture.Sample(Sampler, input.UV + float2(add.x, add.y)); + float3 rgbM = Texture.Sample(Sampler, input.UV); + + float lumaNW = Luma(rgbNW); + float lumaNE = Luma(rgbNE); + float lumaSW = Luma(rgbSW); + float lumaSE = Luma(rgbSE); + float lumaM = Luma(rgbM); + + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + float2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + float dirReduce = max( + (lumaNW + lumaNE + lumaSW + lumaSE) * (0.25f * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + + float rcpDirMin = 1.0f / (min(abs(dir.x), abs(dir.y)) + dirReduce); + + dir = min(float2( FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(float2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * add; + + float3 rgbA = (1.0f / 2.0f) * + (Texture.Sample(Sampler, input.UV + dir * (1.0f / 3.0f - 0.5f)) + + Texture.Sample(Sampler, input.UV + dir * (2.0f / 2.0f - 0.5f))); + + float3 rgbB = rgbA * (1.0f / 2.0f) + (1.0f / 4.0f) * + (Texture.Sample(Sampler, input.UV + dir * (0.0f / 3.0f - 0.5f)) + + Texture.Sample(Sampler, input.UV + dir * (3.0f / 3.0f - 0.5f))); + + float lumaB = Luma(rgbB); + + if ((lumaB < lumaMin) || (lumaB > lumaMax)) + return float4(rgbA, 1.0f); + else + return float4(rgbB, 1.0f); +} + PixelShaderInput VS(VertexShaderInput input) { PixelShaderInput output; @@ -31,7 +84,8 @@ PixelShaderInput VS(VertexShaderInput input) float4 PS(PixelShaderInput input) : SV_TARGET { - float4 output = Texture.Sample(Sampler, input.UV); + float4 output = FXAA ? Antialias(input) : Texture.Sample(Sampler, input.UV); + float3 colorMul = min(input.Color.xyz, 1.0f); float y = input.Position.y / ViewportHeight; diff --git a/TombEngine/Shaders/DX11_Sprites.fx b/TombEngine/Shaders/DX11_Sprites.fx index 5cd263bf8..f15ffb322 100644 --- a/TombEngine/Shaders/DX11_Sprites.fx +++ b/TombEngine/Shaders/DX11_Sprites.fx @@ -60,6 +60,8 @@ float4 PS(PixelShaderInput input) : SV_TARGET { float4 output = Texture.Sample(Sampler, input.UV) * input.Color; + DoAlphaTest(output); + float particleDepth = input.PositionCopy.z / input.PositionCopy.w; input.PositionCopy.xy /= input.PositionCopy.w; float2 texCoord = 0.5f * (float2(input.PositionCopy.x, -input.PositionCopy.y) + 1); diff --git a/TombEngine/Sound/sound.cpp b/TombEngine/Sound/sound.cpp index 378808fb6..34a405bfb 100644 --- a/TombEngine/Sound/sound.cpp +++ b/TombEngine/Sound/sound.cpp @@ -444,7 +444,7 @@ void PlaySoundTrack(std::string track, SoundTrackType mode, QWORD position) // BGM tracks are crossfaded, and additionally shuffled a bit to make things more natural. // Think everybody are fed up with same start-up sounds of Caves ambience... - if (crossfade) + if (crossfade && BASS_ChannelIsActive(BASS_Soundtrack[(int)SoundTrackType::BGM].Channel)) { // Crossfade... BASS_ChannelSetAttribute(stream, BASS_ATTRIB_VOL, 0.0f); diff --git a/TombEngine/Specific/configuration.cpp b/TombEngine/Specific/configuration.cpp index f78b62cc5..7af3d6b87 100644 --- a/TombEngine/Specific/configuration.cpp +++ b/TombEngine/Specific/configuration.cpp @@ -78,7 +78,7 @@ BOOL CALLBACK DialogProc(HWND handle, UINT msg, WPARAM wParam, LPARAM lParam) SendMessageA(GetDlgItem(handle, IDC_GROUP_RENDER_OPTIONS), WM_SETTEXT, 0, (LPARAM)g_GameFlow->GetString(STRING_RENDER_OPTIONS)); SendMessageA(GetDlgItem(handle, IDC_SHADOWS), WM_SETTEXT, 0, (LPARAM)g_GameFlow->GetString(STRING_SHADOWS)); SendMessageA(GetDlgItem(handle, IDC_CAUSTICS), WM_SETTEXT, 0, (LPARAM)g_GameFlow->GetString(STRING_CAUSTICS)); - SendMessageA(GetDlgItem(handle, IDC_VOLUMETRIC_FOG), WM_SETTEXT, 0, (LPARAM)g_GameFlow->GetString(STRING_VOLUMETRIC_FOG)); + SendMessageA(GetDlgItem(handle, IDC_ANTIALIASING), WM_SETTEXT, 0, (LPARAM)g_GameFlow->GetString(STRING_ANTIALIASING)); LoadResolutionsInCombobox(handle); LoadSoundDevicesInCombobox(handle); @@ -86,8 +86,8 @@ BOOL CALLBACK DialogProc(HWND handle, UINT msg, WPARAM wParam, LPARAM lParam) // Set some default values g_Configuration.AutoTarget = true; - g_Configuration.EnableVolumetricFog = true; - SendDlgItemMessage(handle, IDC_VOLUMETRIC_FOG, BM_SETCHECK, 1, 0); + g_Configuration.Antialiasing = AntialiasingMode::Low; + SendDlgItemMessage(handle, IDC_ANTIALIASING, BM_SETCHECK, 1, 0); g_Configuration.ShadowType = ShadowMode::Lara; SendDlgItemMessage(handle, IDC_SHADOWS, BM_SETCHECK, 1, 0); @@ -116,7 +116,7 @@ BOOL CALLBACK DialogProc(HWND handle, UINT msg, WPARAM wParam, LPARAM lParam) g_Configuration.Windowed = (SendDlgItemMessage(handle, IDC_WINDOWED, BM_GETCHECK, 0, 0)); g_Configuration.ShadowType = (ShadowMode)(SendDlgItemMessage(handle, IDC_SHADOWS, BM_GETCHECK, 0, 0)); g_Configuration.EnableCaustics = (SendDlgItemMessage(handle, IDC_CAUSTICS, BM_GETCHECK, 0, 0)); - g_Configuration.EnableVolumetricFog = (SendDlgItemMessage(handle, IDC_VOLUMETRIC_FOG, BM_GETCHECK, 0, 0)); + g_Configuration.Antialiasing = (AntialiasingMode)(SendDlgItemMessage(handle, IDC_ANTIALIASING, BM_GETCHECK, 0, 0)); g_Configuration.EnableSound = (SendDlgItemMessage(handle, IDC_ENABLE_SOUNDS, BM_GETCHECK, 0, 0)); selectedMode = (SendDlgItemMessage(handle, IDC_RESOLUTION, CB_GETCURSEL, 0, 0)); mode = g_Configuration.SupportedScreenResolutions[selectedMode]; @@ -210,7 +210,7 @@ bool SaveConfiguration() return false; } - if (SetBoolRegKey(rootKey, REGKEY_VOLUMETRIC_FOG, g_Configuration.EnableVolumetricFog) != ERROR_SUCCESS) + if (SetDWORDRegKey(rootKey, REGKEY_ANTIALIASING, DWORD(g_Configuration.Antialiasing)) != ERROR_SUCCESS) { RegCloseKey(rootKey); return false; @@ -299,7 +299,7 @@ void InitDefaultConfiguration() g_Configuration.EnableCaustics = true; g_Configuration.ShadowType = ShadowMode::Lara; g_Configuration.EnableSound = true; - g_Configuration.EnableVolumetricFog = true; + g_Configuration.Antialiasing = AntialiasingMode::Low; g_Configuration.MusicVolume = 100; g_Configuration.SfxVolume = 100; g_Configuration.Width = currentScreenResolution.x; @@ -345,8 +345,8 @@ bool LoadConfiguration() return false; } - bool volumetricFog = false; - if (GetBoolRegKey(rootKey, REGKEY_VOLUMETRIC_FOG, &volumetricFog, true) != ERROR_SUCCESS) + DWORD antialiasing = 1; + if (GetDWORDRegKey(rootKey, REGKEY_ANTIALIASING, &antialiasing, true) != ERROR_SUCCESS) { RegCloseKey(rootKey); return false; @@ -452,7 +452,7 @@ bool LoadConfiguration() g_Configuration.ShadowType = ShadowMode(shadowMode); g_Configuration.ShadowMaxBlobs = shadowBlobs; g_Configuration.EnableCaustics = caustics; - g_Configuration.EnableVolumetricFog = volumetricFog; + g_Configuration.Antialiasing = AntialiasingMode(antialiasing); g_Configuration.ShadowMapSize = shadowMapSize; g_Configuration.EnableSound = enableSound; diff --git a/TombEngine/Specific/configuration.h b/TombEngine/Specific/configuration.h index 943b2127d..f0bd5dcf6 100644 --- a/TombEngine/Specific/configuration.h +++ b/TombEngine/Specific/configuration.h @@ -13,7 +13,7 @@ #define REGKEY_SHADOW_MAP "ShadowMap" #define REGKEY_SHADOW_BLOBS "ShadowBlobs" #define REGKEY_CAUSTICS "Caustics" -#define REGKEY_VOLUMETRIC_FOG "VolumetricFog" +#define REGKEY_ANTIALIASING "Antialiasing" #define REGKEY_SOUND_DEVICE "SoundDevice" #define REGKEY_ENABLE_SOUND "EnableSound" @@ -39,7 +39,7 @@ struct GameConfiguration int SfxVolume; bool EnableCaustics; - bool EnableVolumetricFog; + AntialiasingMode Antialiasing; ShadowMode ShadowType; int ShadowMapSize = 1024; int ShadowMaxBlobs = 16; diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 48e88788e..bc7c2ed4e 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -34,19 +34,16 @@ using std::string; using namespace TEN::Input; using namespace TEN::Entities::Doors; -uintptr_t hLoadLevel; -unsigned int ThreadId; char* LevelDataPtr; bool IsLevelLoading; bool LoadedSuccessfully; vector MoveablesIds; vector StaticObjectsIds; -ChunkReader* g_levelChunkIO; LEVEL g_Level; -short ReadInt8() +unsigned char ReadUInt8() { - byte value = *(byte*)LevelDataPtr; + unsigned char value = *(unsigned char*)LevelDataPtr; LevelDataPtr += 1; return value; } @@ -106,22 +103,54 @@ Vector4 ReadVector4() return value; } +bool ReadBool() +{ + return bool(ReadUInt8()); +} + void ReadBytes(void* dest, int count) { memcpy(dest, LevelDataPtr, count); LevelDataPtr += count; } +long long ReadLEB128(bool sign) +{ + long long result = 0; + int currentShift = 0; + + unsigned char currentByte; + do + { + currentByte = ReadUInt8(); + + result |= (long long)(currentByte & 0x7F) << currentShift; + currentShift += 7; + } while ((currentByte & 0x80) != 0); + + if (sign) // Sign extend + { + int shift = 64 - currentShift; + if (shift > 0) + result = (long long)(result << shift) >> shift; + } + + return result; +} + std::string ReadString() { - byte numBytes = ReadInt8(); // FIXME: incorrect, should be read in LEB128 format + auto numBytes = ReadLEB128(false); if (!numBytes) return std::string(); - - char buffer[255]; - ReadBytes(buffer, numBytes); - return std::string(buffer, buffer + numBytes); + else + { + auto newPtr = LevelDataPtr + numBytes; + auto result = std::string(LevelDataPtr, newPtr); + LevelDataPtr = newPtr; + return result; + } } void LoadItems() @@ -178,7 +207,7 @@ void LoadObjects() { MESH mesh; - mesh.lightMode = (LIGHT_MODES)ReadInt8(); + mesh.lightMode = (LIGHT_MODES)ReadUInt8(); mesh.sphere.Center.x = ReadFloat(); mesh.sphere.Center.y = ReadFloat(); @@ -206,8 +235,8 @@ void LoadObjects() BUCKET bucket; bucket.texture = ReadInt32(); - bucket.blendMode = (BLEND_MODES)ReadInt8(); - bucket.animated = ReadInt8(); + bucket.blendMode = (BLEND_MODES)ReadUInt8(); + bucket.animated = ReadBool(); bucket.numQuads = 0; bucket.numTriangles = 0; @@ -442,7 +471,7 @@ void LoadTextures() texture.colorMapData.resize(size); ReadBytes(texture.colorMapData.data(), size); - byte hasNormalMap = ReadInt8(); + bool hasNormalMap = ReadBool(); if (hasNormalMap) { size = ReadInt32(); @@ -468,7 +497,7 @@ void LoadTextures() texture.colorMapData.resize(size); ReadBytes(texture.colorMapData.data(), size); - bool hasNormalMap = ReadInt8(); + bool hasNormalMap = ReadBool(); if (hasNormalMap) { size = ReadInt32(); @@ -494,7 +523,7 @@ void LoadTextures() texture.colorMapData.resize(size); ReadBytes(texture.colorMapData.data(), size); - bool hasNormalMap = ReadInt8(); + bool hasNormalMap = ReadBool(); if (hasNormalMap) { size = ReadInt32(); @@ -520,7 +549,7 @@ void LoadTextures() texture.colorMapData.resize(size); ReadBytes(texture.colorMapData.data(), size); - bool hasNormalMap = ReadInt8(); + bool hasNormalMap = ReadBool(); if (hasNormalMap) { size = ReadInt32(); @@ -591,8 +620,8 @@ void ReadRooms() BUCKET bucket; bucket.texture = ReadInt32(); - bucket.blendMode = (BLEND_MODES)ReadInt8(); - bucket.animated = ReadInt8(); + bucket.blendMode = (BLEND_MODES)ReadUInt8(); + bucket.animated = ReadBool(); bucket.numQuads = 0; bucket.numTriangles = 0; @@ -687,15 +716,15 @@ void ReadRooms() floor.CeilingCollision.Planes[1].z = ReadFloat(); floor.WallPortal = ReadInt32(); - floor.Flags.Death = ReadInt8(); - floor.Flags.Monkeyswing = ReadInt8(); - floor.Flags.ClimbNorth = ReadInt8(); - floor.Flags.ClimbSouth = ReadInt8(); - floor.Flags.ClimbEast = ReadInt8(); - floor.Flags.ClimbWest = ReadInt8(); - floor.Flags.MarkTriggerer = ReadInt8(); + floor.Flags.Death = ReadBool(); + floor.Flags.Monkeyswing = ReadBool(); + floor.Flags.ClimbNorth = ReadBool(); + floor.Flags.ClimbSouth = ReadBool(); + floor.Flags.ClimbEast = ReadBool(); + floor.Flags.ClimbWest = ReadBool(); + floor.Flags.MarkTriggerer = ReadBool(); floor.Flags.MarkTriggererActive = 0; // TODO: IT NEEDS TO BE WRITTEN/READ FROM SAVEGAMES! - floor.Flags.MarkBeetle = ReadInt8(); + floor.Flags.MarkBeetle = ReadBool(); floor.Room = i; @@ -726,8 +755,8 @@ void ReadRooms() light.out = ReadFloat(); light.length = ReadFloat(); light.cutoff = ReadFloat(); - light.type = ReadInt8(); - light.castShadows = ReadInt8(); + light.type = ReadUInt8(); + light.castShadows = ReadBool(); room.lights.push_back(light); } @@ -772,6 +801,7 @@ void ReadRooms() volume.Scale.y = ReadFloat(); volume.Scale.z = ReadFloat(); + //volume.LuaName = ReadString(); // TODO: Uncomment when lua API for volumes is implemented -- Lwmte, 09.08.22 volume.EventSetIndex = ReadInt32(); volume.Status = TriggerStatus::Outside; @@ -1057,14 +1087,12 @@ unsigned int _stdcall LoadLevel(void* data) else TENLog("Level compiler version: " + std::to_string(version[0]) + "." + std::to_string(version[1]) + "." + std::to_string(version[2]), LogLevel::Info); + // Check if level version is higher than engine version auto assemblyVersion = TEN::Utils::GetProductOrFileVersion(true); for (int i = 0; i < assemblyVersion.size(); i++) { - if (assemblyVersion[i] != version[i]) - { - TENLog("Level compiler version does not match with TEN version. Errors or crashes may be possible.", LogLevel::Warning); - break; - } + if (assemblyVersion[i] < version[i]) + throw std::exception("Level version is higher than TEN version. Please update TEN."); } // Check system name hash and reset it if it's valid (because we use build & play feature only once) @@ -1152,6 +1180,7 @@ unsigned int _stdcall LoadLevel(void* data) TENLog("Error while loading level: " + std::string(ex.what()), LogLevel::Error); LoadedSuccessfully = false; + SystemNameHash = 0; } if (dataPtr) @@ -1254,13 +1283,14 @@ int LoadLevelFile(int levelIndex) // Loading level is done is two threads, one for loading level and one for drawing loading screen IsLevelLoading = true; - hLoadLevel = _beginthreadex( + + _beginthreadex( nullptr, 0, LoadLevel, reinterpret_cast(levelIndex), 0, - &ThreadId); + NULL); while (IsLevelLoading); diff --git a/TombEngine/resource.h b/TombEngine/resource.h index c129573d5..b3516a3d4 100644 --- a/TombEngine/resource.h +++ b/TombEngine/resource.h @@ -18,8 +18,7 @@ #define IDC_BILINEAR 1032 #define IDC_CAUSTICS 1032 #define IDC_TEXRESTXT 1033 -#define IDC_LOWTEXT 1034 -#define IDC_VOLUMETRIC_FOG 1034 +#define IDC_ANTIALIASING 1034 #define IDC_LOWBUMP 1035 #define IDC_BUMPMAP 1036 #define IDC_SNDADAPTERTXT 1037
    EventSequenceEvent sequence - a chain of functions to call at specified times, modeled after TRNG's organizers.
    Timer Basic timer - after a specified number of seconds, the specified thing happens.