diff --git a/Documentation/Changes.txt b/Documentation/Changes.txt index 49a241f14..dcf38bfd1 100644 --- a/Documentation/Changes.txt +++ b/Documentation/Changes.txt @@ -1,6 +1,9 @@ Version 1.0.2 ============= +* Removing Pistols with TakeItem and SetItemCount now works correctly. +* Vec3s can now be saved and loaded in LevelVars and GameVars. +* Support volume triggers made with node editor. * Adjust max turn rate of idle state. * Align Lara on slopes when crouching, crawling, and dying. * Better slope alignment for large, flat enemies (i.e. big scorpion and crocodile). @@ -18,8 +21,18 @@ Version 1.0.2 * Fix occasional leave event calls when moving closer to volumes. * Fix incorrect viewport size in windowed mode. * Fix late landing animation dispatch in rare cases. -* Fix Lara's subway train death so that she no longer slides slowly after the animation has finished. +* Fix Lara's subway train death so that she no longer slides slowly after the animation has finished. +* Fix horseman's axe attack using his left foot as the damaging joint. +* Fix stargate blades needlessly pushing the player around while hardly doing any damage. +Lua API changes: + +* Timer.lua, EventSequence.lua and Util.lua have been moved to a subfolder, Engine. +* LevelFuncs can now contain tables as well as functions. These tables can contain functions and other tables, and so forth. +* Moveable functions SetOnHit, SetOnKilled, SetOnCollidedWithObject and SetOnCollidedWithRoom no longer take strings, and instead take function objects themselves. +* EventSequence and Timer no longer require you to call Timer.UpdateAll in OnControlPhase. +* TEN.Logic.AddCallback and TEN.Logic.RemoveCallback have been added. +* GiveItem, TakeItem, and SetItemCount have been reworked (e.g. SetItemCount with a value of -1 can give infinite ammo/consumables). Version 1.0.1 ============= diff --git a/Documentation/config.ld b/Documentation/config.ld index 75fce7222..3be1dfb60 100644 --- a/Documentation/config.ld +++ b/Documentation/config.ld @@ -12,7 +12,7 @@ new_type("luautil", "5 Lua utility modules", true) not_luadoc = true -local version = "1.0.1" +local version = "1.0.2" project = "TombEngine" title = "TombEngine " .. version .. " Lua API" description = "TombEngine " .. version .. " scripting interface" @@ -24,7 +24,7 @@ Other than the "special tables" (GameVars, LevelVars and LevelFuncs), every modu For example, to call GetMoveableByName, you would have to do: local door = TEN.Objects.GetMoveableByName("door_type4_14") To save on typing, you can put the following at the start of a Lua file: - local Util = require("Util") + local Util = require("Engine.Util") Util.ShortenTENCalls() This will put the modules and classes in the global table. In other words, it means you can do the following: local door = GetMoveableByName("door_type4_14") diff --git a/Documentation/doc/1 modules/Effects.html b/Documentation/doc/1 modules/Effects.html index acb3a242b..7bf7efdbe 100644 --- a/Documentation/doc/1 modules/Effects.html +++ b/Documentation/doc/1 modules/Effects.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -511,7 +511,7 @@
generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
diff --git a/Documentation/doc/1 modules/Flow.html b/Documentation/doc/1 modules/Flow.html index c80e697c8..8b0d52a9b 100644 --- a/Documentation/doc/1 modules/Flow.html +++ b/Documentation/doc/1 modules/Flow.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -102,6 +102,14 @@ Add a level to the Flow. + GetLevel(id) + GetLevel. + + + GetCurrentLevel() + GetCurrentLevel. + + SetIntroImagePath(path) Image to show when loading the game. @@ -173,6 +181,55 @@ ambient tracks. + +
+ + GetLevel(id) +
+
+ GetLevel. +Returns the level indicated by the parameter id. + + +

Parameters:

+ + +

Returns:

+
    + + Level + the level indicated by the id +
+ + + + +
+
+ + GetCurrentLevel() +
+
+ GetCurrentLevel. +Returns the level that the game control is running in that moment. + + + +

Returns:

+
    + + Level + the current level +
+ + + +
@@ -353,7 +410,7 @@ Specify which translations in the strings table correspond to which languages.
generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
diff --git a/Documentation/doc/1 modules/Inventory.html b/Documentation/doc/1 modules/Inventory.html index 327be00e9..06117ac3e 100644 --- a/Documentation/doc/1 modules/Inventory.html +++ b/Documentation/doc/1 modules/Inventory.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -96,17 +96,21 @@

Functions

- + - - + + + + + +
GiveItem(item, count)GiveItem(item[, count]) Add x of an item to the inventory.
GetItemCount(item)Get the amount the player holds of an item.TakeItem(item[, count])Remove x of a certain item from the inventory.
SetItemCount(item, count) Set the amount of a certain item the player has in the inventory.
GetItemCount(item)Get the amount the player holds of an item.

@@ -118,14 +122,15 @@
- GiveItem(item, count) + GiveItem(item[, count])
Add x of an item to the inventory. -A count of 0 will add the "default" amount of that item +Omitting the second argument will give the "default" amount of the item (i.e. the amount the player would get from a pickup of that type). -For example, giving "zero" crossbow ammo would give the player -10 instead, whereas giving "zero" medkits would give the player 1 medkit. +For example, giving crossbow ammo without specifying the number would give the player +10 instead. +Has no effect if the player has an infinite number of that item.

Parameters:

@@ -136,7 +141,62 @@ For example, giving "zero" crossbow ammo would give the player
  • count int - the number of items to add (default: 0) + the number of items to add (default: the amount you would get from a pickup) + (optional) +
  • + + + + + + +
    +
    + + TakeItem(item[, count]) +
    +
    + Remove x of a certain item from the inventory. +As in GiveItem, omitting the count will remove the "default" amount of that item. +Has no effect if the player has an infinite number of the item. + + +

    Parameters:

    +
      +
    • item + InvID + the item to be removed +
    • +
    • count + int + the number of items to remove (default: the amount you would get from a pickup) + (optional) +
    • +
    + + + + + +
    +
    + + SetItemCount(item, count) +
    +
    + Set the amount of a certain item the player has in the inventory. +Similar to GiveItem but replaces with the new amount instead of adding it. + + +

    Parameters:

    +
      +
    • item + InvID + the ID of the item to be set. +
    • +
    • count + int + the number of items the player will have. A value of -1 will give an infinite amount of that item.
    @@ -165,37 +225,12 @@ For example, giving "zero" crossbow ammo would give the player
      int - the amount of the item the player has in the inventory + the amount of the item the player has in the inventory. -1 indicates an infinite amount of that item.
    -
    -
    - - SetItemCount(item, count) -
    -
    - Set the amount of a certain item the player has in the inventory. -Similar to GiveItem but replaces with the new amount instead of adding it. - - -

    Parameters:

    -
      -
    • item - the ID of the item to be set -
    • -
    • count - int - the number of items the player will have -
    • -
    - - - - -
    @@ -204,7 +239,7 @@ Similar to GiveItem but repla
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/1 modules/Logic.html b/Documentation/doc/1 modules/Logic.html index 4475152b2..e624351e2 100644 --- a/Documentation/doc/1 modules/Logic.html +++ b/Documentation/doc/1 modules/Logic.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -32,6 +32,7 @@

    Contents

    @@ -94,6 +95,17 @@

    +

    Functions

    + + + + + + + + + +
    AddCallback(CallbackPoint, func)Register a function as a callback.
    RemoveCallback(CallbackPoint, LevelFunc)Deregister a function as a callback.

    Special objects

    @@ -113,7 +125,7 @@ - +
    LevelFuncsA table with level-specific functions.A table nested table system for level-specific functions.
    @@ -121,6 +133,79 @@
    +

    Functions

    + +
    +
    + + AddCallback(CallbackPoint, func) +
    +
    + Register a function as a callback. +Possible values for CallbackPoint:

    +
    PRECONTROLPHASE -- will be called immediately before OnControlPhase
    +POSTCONTROLPHASE -- will be called immediately after OnControlPhase
    +
    + +

    The order in which two functions with the same CallbackPoint are called is undefined. +i.e. if you register MyFunc and MyFunc2 with PRECONTROLPHASE, both will be called before OnControlPhase, but there is no guarantee whether MyFunc will be called before MyFunc2, or vice-versa.

    + +

    Any returned value will be discarded. + + +

    Parameters:

    +
      +
    • CallbackPoint + point + When should the callback be called? +
    • +
    • func + function + The function to be called (must be in the LevelFuncs hierarchy). Will receive, as an argument, the time in seconds since the last frame. +
    • +
    + + + + +

    Usage:

    +
      +
      LevelFuncs.MyFunc = function(dt) print(dt) end
      +TEN.Logic.AddCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, LevelFuncs.MyFunc)
      +
    + +
    +
    + + RemoveCallback(CallbackPoint, LevelFunc) +
    +
    + Deregister a function as a callback. +Will have no effect if the function was not registered as a callback + + +

    Parameters:

    +
      +
    • CallbackPoint + point + The callback point the function was registered with. See AddCallback +
    • +
    • LevelFunc + func + the function to remove; must be in the LevelFuncs hierarchy. +
    • +
    + + + + +

    Usage:

    +
      +
      TEN.Logic.RemoveCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, "MyFunc")
      +
    + +
    +

    Special objects

    @@ -165,7 +250,9 @@ some time later, the values 3 will be put back into LevelVars

    This table is emptied when a level is finished. If the player needs to be able to return to the level (like in the Karnak and Alexandria levels in The Last Revelation), -you will need to use the GameVars table, below. +you will need to use the GameVars table, below.

    + +

    LevelVars.Engine is a reserved table used internally by TombEngine's libs. Do not modify, overwrite, or add to it. @@ -197,7 +284,9 @@ write:

    end
    -

    Unlike LevelVars, this table will remain intact for the entirety of the game. +

    Unlike LevelVars, this table will remain intact for the entirety of the game.

    + +

    GameVars.Engine is a reserved table used internally by TombEngine's libs. Do not modify, overwrite, or add to it. @@ -212,9 +301,9 @@ end

    -

    A table with level-specific functions.

    +

    A table nested table system for level-specific functions.

    -

    This serves two purposes: it holds the level callbacks (listed below) as well as +

    This serves a few purposes: it holds the level callbacks (listed below) as well as any trigger functions you might have specified. For example, if you give a trigger a Lua name of "my_trigger" in Tomb Editor, you will have to implement it as a member of this table:

    @@ -224,6 +313,25 @@ of this table:

    end +

    You can organise functions into tables within the hierarchy:

    + +
    LevelFuncs.enemyFuncs = {}
    +
    +LevelFuncs.enemyFuncs.makeBaddyRunAway = function()
    +    -- implementation goes here
    +end
    +
    +LevelFuncs.enemyFuncs.makeBaddyUseMedkit = function()
    +    -- implementation goes here
    +end
    +
    + +

    There are two special subtables which you should not overwrite:

    + +
    LevelFuncs.Engine -- this is for 'first-party' functions, i.e. ones that come with TombEngine.
    +LevelFuncs.External -- this is for 'third-party' functions. If you write a library providing LevelFuncs functions for other builders to use in their levels, put those functions in LevelFuncs.External.YourLibraryNameHere
    +
    +

    The following are the level callbacks. They are optional; if your level has no special behaviour for a particular scenario, you do not need to implement the function. For example, if your level does not need any special initialisation when it is loaded, @@ -279,7 +387,7 @@ and provides the delta time (a float representing game time since last call) via

    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/1 modules/Misc.html b/Documentation/doc/1 modules/Misc.html index 86d155089..f525e59d3 100644 --- a/Documentation/doc/1 modules/Misc.html +++ b/Documentation/doc/1 modules/Misc.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -124,6 +124,10 @@ Set and play an ambient track + PlaySound(sound[, position]) + Play sound effect + + CalculateDistance(posA, posB) Calculate the distance between two positions. @@ -144,10 +148,6 @@ Vibrate gamepad, if possible. - PlaySound(sound, position) - Play sound effect - - KeyIsHeld(action) Check if particular action key is held @@ -355,6 +355,32 @@ eyes of the creatures would be. +
    +
    + + PlaySound(sound[, position]) +
    +
    + Play sound effect + + +

    Parameters:

    + + + + + +
    @@ -451,6 +477,17 @@ To be used with local halfwayX, halfwayY = PercentToScreen(50, 50) +local baddy +local spawnLocationNullmesh = GetMoveableByName("position_behind_left_pillar") +local str1 = DisplayString("You spawned a baddy!", halfwayX, halfwayY, Color(255, 100, 100), false, {DisplayStringOption.SHADOW, DisplayStringOption.CENTER}) + +LevelFuncs.triggerOne = function(obj) + ShowString(str1, 4) +end +
    @@ -514,33 +551,6 @@ To be used with - PlaySound(sound, position) -
    -
    - Play sound effect - - -

    Parameters:

    - - - - - -
    @@ -633,7 +643,7 @@ To be used with
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-08 19:16:14
    diff --git a/Documentation/doc/1 modules/Objects.html b/Documentation/doc/1 modules/Objects.html index 0aa9b0508..7d8b1ae03 100644 --- a/Documentation/doc/1 modules/Objects.html +++ b/Documentation/doc/1 modules/Objects.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -297,7 +297,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/1 modules/Strings.html b/Documentation/doc/1 modules/Strings.html index d07a6c74d..34beb1d6a 100644 --- a/Documentation/doc/1 modules/Strings.html +++ b/Documentation/doc/1 modules/Strings.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -169,7 +169,7 @@ with a call to ShowString, or
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Flow.Animations.html b/Documentation/doc/2 classes/Flow.Animations.html index 25b6ecaa5..8da244820 100644 --- a/Documentation/doc/2 classes/Flow.Animations.html +++ b/Documentation/doc/2 classes/Flow.Animations.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -100,7 +100,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Flow.Fog.html b/Documentation/doc/2 classes/Flow.Fog.html index 1aeaeeaef..42069ed14 100644 --- a/Documentation/doc/2 classes/Flow.Fog.html +++ b/Documentation/doc/2 classes/Flow.Fog.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -112,7 +112,7 @@

    Functions

    - + @@ -185,8 +185,8 @@
    - - Fog.new(color, Min, Max) + + Fog(color, Min, Max)
    @@ -227,7 +227,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Flow.InventoryItem.html b/Documentation/doc/2 classes/Flow.InventoryItem.html index 8a35205c0..96426d73b 100644 --- a/Documentation/doc/2 classes/Flow.InventoryItem.html +++ b/Documentation/doc/2 classes/Flow.InventoryItem.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -182,7 +182,7 @@ EXAMINE
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Flow.Level.html b/Documentation/doc/2 classes/Flow.Level.html index e43c230be..63fd5278f 100644 --- a/Documentation/doc/2 classes/Flow.Level.html +++ b/Documentation/doc/2 classes/Flow.Level.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -120,8 +120,7 @@
    - + @@ -132,10 +131,6 @@ - - - - @@ -175,7 +170,7 @@

    Functions

    Fog.new(color, Min, Max)Fog(color, Min, Max)
    layer2(Flow.SkyLayer) Secondary sky layer - (not yet implemented)(Flow.SkyLayer) Secondary sky layer
    fog(bool) Draw sky layer?
    colAddHorizon(bool) Enable smooth transition from horizon graphic to sky layer.
    storm (bool) Enable flickering lightning in the sky.
    - +
    Level.new()Level() Make a new Level object.
    @@ -282,7 +277,6 @@
    (Flow.SkyLayer) Secondary sky layer - (not yet implemented) @@ -320,23 +314,6 @@ -
    -
    - - colAddHorizon -
    -
    - (bool) Enable smooth transition from horizon graphic to sky layer. - If set to false, there will be a black band between the two.

    - -

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

    @@ -494,8 +471,8 @@ Must be at least 4.

    - - Level.new() + + Level()
    Make a new Level object. @@ -519,7 +496,7 @@ Must be at least 4.

    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Flow.Mirror.html b/Documentation/doc/2 classes/Flow.Mirror.html index 8847b143e..8cc80d69e 100644 --- a/Documentation/doc/2 classes/Flow.Mirror.html +++ b/Documentation/doc/2 classes/Flow.Mirror.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -100,7 +100,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Flow.Settings.html b/Documentation/doc/2 classes/Flow.Settings.html index 96ef22346..99e9f061a 100644 --- a/Documentation/doc/2 classes/Flow.Settings.html +++ b/Documentation/doc/2 classes/Flow.Settings.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -115,19 +115,19 @@
    How should the application respond to script errors? Must be one of the following: -ErrorMode.TERMINATE - print to the log file and terminate the application when any script error is hit. +ErrorMode.TERMINATE - print to the log file and return to the title level when any script error is hit. This is the one you will want to go for if you want to know IMMEDIATELY if something has gone wrong.

    ErrorMode.WARN - print to the log file and continue running the application when a recoverable script error is hit. -Choose this one if terminating the application is too much for you. Note that unrecoverable errors will still terminate -the application.

    +Choose this one if booting to the title level is too much for you.

    ErrorMode.SILENT - do nothing when a recoverable script error is hit. Think very carefully before using this setting. These error modes are here to help you to keep your scripts working properly, but if you opt to ignore errors, you won't be alerted if you've misused a function or passed an invalid argument.

    -

    As with ErrorMode.WARN, unrecoverable errors will still terminate the application. +

    In all of these modes, an unrecoverable error will boot you to the title level. If the title level itself +has an unrecoverable error, the game will close. @@ -143,7 +143,7 @@ an invalid argument.

    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Flow.SkyLayer.html b/Documentation/doc/2 classes/Flow.SkyLayer.html index 69b0dd699..4e4d6b59f 100644 --- a/Documentation/doc/2 classes/Flow.SkyLayer.html +++ b/Documentation/doc/2 classes/Flow.SkyLayer.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -106,7 +106,7 @@

    Functions

    - + @@ -161,8 +161,8 @@ Less is more. City of The Dead, for example, uses a speed value of 16.
    - - SkyLayer.new(color, speed) + + SkyLayer(color, speed)
    @@ -199,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-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Objects.AIObject.html b/Documentation/doc/2 classes/Objects.AIObject.html index 290626a04..c6a4ae64c 100644 --- a/Documentation/doc/2 classes/Objects.AIObject.html +++ b/Documentation/doc/2 classes/Objects.AIObject.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -372,7 +372,7 @@ aiObj:SetObjectID(TEN.Objects.ObjID.AI_PATROL1)
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Objects.Camera.html b/Documentation/doc/2 classes/Objects.Camera.html index 2b81eb147..51bd92538 100644 --- a/Documentation/doc/2 classes/Objects.Camera.html +++ b/Documentation/doc/2 classes/Objects.Camera.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -260,7 +260,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Objects.Moveable.html b/Documentation/doc/2 classes/Objects.Moveable.html index 324c4e627..7ea645ded 100644 --- a/Documentation/doc/2 classes/Objects.Moveable.html +++ b/Documentation/doc/2 classes/Objects.Moveable.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -95,7 +95,7 @@ pickups, and Lara herself.

    Functions

    SkyLayer.new(color, speed)SkyLayer(color, speed)
    - + @@ -124,39 +124,6 @@ associated getters and setters. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -197,6 +164,10 @@ associated getters and setters. + + + + @@ -205,6 +176,14 @@ associated getters and setters. + + + + + + + + @@ -270,27 +249,8 @@ associated getters and setters. - - - - - - - - - - - - - - - - @@ -318,6 +278,44 @@ associated getters and setters. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Moveable(object, name, position, rotation, room, animNumber, frameNumber, hp, OCB, AIBits)Moveable(object, name, position[, rotation[, room[, animNumber=0[, frameNumber=0[, hp=10[, OCB=0[, AIBits]]]]]]]) For more information on each parameter, see the associated getters and setters.
    Get the status of object.
    Moveable:SetOnHit(name)Set the name of the function to be called when the moveable is shot by Lara - Note that this will be triggered twice when shot with both pistols at once.
    Moveable:GetOnHit()Get the name of the function called when this moveable is shot
    Moveable:SetOnCollidedWithObject(name)Set the name of the function called when this moveable collides with another moveable
    Moveable:GetOnCollidedWithObject()Get the name of the function called when this moveable collides with another moveable
    Moveable:SetOnCollidedWithRoom(name)Set the name of the function called when this moveable collides with room geometry (e.g.
    Moveable:GetOnCollidedWithRoom()Get the name of the function called when this moveable collides with room geometry (e.g.
    Moveable:SetOnKilled(callback)Set the name of the function to be called when the moveable is destroyed/killed
    Moveable:GetOnKilled()Get the name of the function called when this moveable is killed
    Moveable:GetObjectID() Retrieve the object ID
    Set current HP (hit points/health points)
    Moveable:GetSlotHP(ID)Get HP definded for that object type (hit points/health points) (Read Only).
    Moveable:GetOCB() Get OCB (object code bit) of the moveable
    Set OCB (object code bit) of the moveable
    Moveable:GetItemFlags()Get the value stored in ItemFlags[x] (x is the value of the parameter)
    Moveable:SetItemFlags(value)Stores the value of the first parameter in the ItemFlags[x] (x is the value of the second parameter)
    Moveable:GetColor() Get the moveable's color
    Determine whether the moveable is active or not
    Moveable:GetRoom()Get the current room of the object
    Moveable:SetRoom(ID)Set room of object - This is used in conjunction with SetPosition to teleport an item to a new room.
    Moveable:GetPosition()Get the object's position
    Moveable:GetJointPosition() Get the object's joint position
    Moveable:SetPosition(position)Set the moveable's position - If you are moving a moveable whose behaviour involves knowledge of room geometry, - (e.g.
    Moveable:GetRotation() Moveable:Destroy() Destroy the moveable.
    Moveable:SetOnHit(callback)Set the name of the function to be called when the moveable is shot by Lara + Note that this will be triggered twice when shot with both pistols at once.
    Moveable:SetOnKilled(callback)Set the name of the function to be called when the moveable is destroyed/killed + Note that enemy death often occurs at the end of an animation, and not at the exact moment + the enemy's HP becomes zero.
    Moveable:SetOnCollidedWithObject(func)Set the function to be called called when this moveable collides with another moveable
    Moveable:SetOnCollidedWithRoom(func)Set the function called when this moveable collides with room geometry (e.g.
    Moveable:GetPosition()Get the object's position
    Moveable:SetPosition(position[, updateRoom])Set the moveable's position + If you are moving a moveable whose behaviour involves knowledge of room geometry, + (e.g.
    Moveable:GetRoom()Get the current room of the object
    Moveable:SetRoom(ID)Set room of object + Use this if you are not using SetPosition's automatic room update - for example, when dealing with overlapping rooms.

    @@ -329,7 +327,7 @@ associated getters and setters.
    - Moveable(object, name, position, rotation, room, animNumber, frameNumber, hp, OCB, AIBits) + Moveable(object, name, position[, rotation[, room[, animNumber=0[, frameNumber=0[, hp=10[, OCB=0[, AIBits]]]]]]])
    For more information on each parameter, see the @@ -354,30 +352,37 @@ most can just be ignored (see usage).
  • rotation Rotation rotation about x, y, and z axes (default Rotation(0, 0, 0)) + (optional)
  • room int - room ID item is in + room ID item is in (default: calculated automatically) + (optional)
  • animNumber int - anim number (default 0) + anim number + (default 0)
  • frameNumber int - frame number (default 0) + frame number + (default 0)
  • hp int - HP of item (default 10) + HP of item + (default 10)
  • OCB int ocb of item (default 0) + (default 0)
  • AIBits table table with AI bits (default {0,0,0,0,0,0}) + (optional)
  • @@ -394,10 +399,8 @@ most can just be ignored (see usage).
    local item = Moveable(
     	TEN.ObjID.PISTOLS_ITEM, -- object id
     	"test", -- name
    -	Vec3(18907, 0, 21201),
    -	Rotation(0,0,0),
    -	0, -- room
    -	)
    + Vec3(18907, 0, 21201) + )
    @@ -495,176 +498,6 @@ most can just be ignored (see usage). -
    -
    - - Moveable:SetOnHit(name) -
    -
    - Set the name of the function to be called when the moveable is shot by Lara - Note that this will be triggered twice when shot with both pistols at once. - - -

    Parameters:

    -
      -
    • name - string - of callback function to be called -
    • -
    - - - - - -
    -
    - - Moveable:GetOnHit() -
    -
    - Get the name of the function called when this moveable is shot - - - -

    Returns:

    -
      - - string - name of the function -
    - - - - -
    -
    - - Moveable:SetOnCollidedWithObject(name) -
    -
    - Set the name of the function called when this moveable collides with another moveable - - -

    Parameters:

    -
      -
    • name - string - of callback function to be called -
    • -
    - - - - - -
    -
    - - Moveable:GetOnCollidedWithObject() -
    -
    - Get the name of the function called when this moveable collides with another moveable - - - -

    Returns:

    -
      - - string - name of the function -
    - - - - -
    -
    - - Moveable:SetOnCollidedWithRoom(name) -
    -
    - Set the name of the function called when this moveable collides with room geometry (e.g. a wall or floor) - - -

    Parameters:

    -
      -
    • name - string - of callback function to be called -
    • -
    - - - - - -
    -
    - - Moveable:GetOnCollidedWithRoom() -
    -
    - Get the name of the function called when this moveable collides with room geometry (e.g. a wall or floor) - - - -

    Returns:

    -
      - - string - name of the function -
    - - - - -
    -
    - - Moveable:SetOnKilled(callback) -
    -
    - Set the name of the function to be called when the moveable is destroyed/killed - - -

    Parameters:

    -
      -
    • callback - string - name of function to be called -
    • -
    - - - - -

    Usage:

    -
      -
      LevelFuncs.baddyKilled = function(theBaddy) print("You killed a baddy!") end
      -baddy:SetOnKilled("baddyKilled")
      -
    - -
    -
    - - Moveable:GetOnKilled() -
    -
    - Get the name of the function called when this moveable is killed - - - -

    Returns:

    -
      - - string - name of the function -
    - - - -
    @@ -886,6 +719,27 @@ shiva:SetObjectID(TEN.Objects.ObjID.BIGMEDI_ITEM) + +
    + + Moveable:GetSlotHP(ID) +
    +
    + Get HP definded for that object type (hit points/health points) (Read Only). + + +

    Parameters:

    +
      +
    • ID + int + of the moveable slot type. +
    • +
    + + + + +
    @@ -927,6 +781,47 @@ shiva:SetObjectID(TEN.Objects.ObjID.BIGMEDI_ITEM) + +
    + + Moveable:GetItemFlags() +
    +
    + Get the value stored in ItemFlags[x] (x is the value of the parameter) + + + +

    Returns:

    +
      + + short + id of the ItemFlags array +
    + + + + +
    +
    + + Moveable:SetItemFlags(value) +
    +
    + Stores the value of the first parameter in the ItemFlags[x] (x is the value of the second parameter) + + +

    Parameters:

    +
      +
    • value + short + to store in the moveable's ItemFlags[x], short id of ItemFlags array to store the value. +
    • +
    + + + + +
    @@ -1242,74 +1137,6 @@ sas:SetAIBits({1, 0, -
    - - Moveable:GetRoom() -
    -
    - Get the current room of the object - - - -

    Returns:

    -
      - - int - number representing the current room of the object -
    - - - - -
    -
    - - Moveable:SetRoom(ID) -
    -
    - Set room of object - This is used in conjunction with SetPosition to teleport an item to a new room. - - -

    Parameters:

    -
      -
    • ID - int - the ID of the new room -
    • -
    - - - - -

    Usage:

    -
      -
      local sas = TEN.Objects.GetMoveableByName("sas_enemy")
      -sas:SetRoom(destinationRoom)
      -sas:SetPosition(destinationPosition)
      -
    - -
    -
    - - Moveable:GetPosition() -
    -
    - Get the object's position - - - -

    Returns:

    -
      - - Vec3 - a copy of the moveable's position -
    - - - -
    @@ -1330,30 +1157,6 @@ sas:SetPosition(destinationPosition) - -
    - - Moveable:SetPosition(position) -
    -
    - Set the moveable's position - If you are moving a moveable whose behaviour involves knowledge of room geometry, - (e.g. a BADDY1, which uses it for pathfinding), then you must use this in conjunction - with Moveable:SetRoom. Otherwise, said moveable will not behave correctly. - - -

    Parameters:

    -
      -
    • position - Vec3 - the new position of the moveable -
    • -
    - - - - -
    @@ -1480,6 +1283,195 @@ sas:SetPosition(destinationPosition) + +
    + + Moveable:SetOnHit(callback) +
    +
    + Set the name of the function to be called when the moveable is shot by Lara + Note that this will be triggered twice when shot with both pistols at once. + + +

    Parameters:

    +
      +
    • callback + function + function in LevelFuncs hierarchy to call when moveable is shot +
    • +
    + + + + + +
    +
    + + Moveable:SetOnKilled(callback) +
    +
    + Set the name of the function to be called when the moveable is destroyed/killed + Note that enemy death often occurs at the end of an animation, and not at the exact moment + the enemy's HP becomes zero. + + +

    Parameters:

    +
      +
    • callback + function + function in LevelFuncs hierarchy to call when enemy is killed +
    • +
    + + + + +

    Usage:

    +
      +
      LevelFuncs.baddyKilled = function(theBaddy) print("You killed a baddy!") end
      +baddy:SetOnKilled(LevelFuncs.baddyKilled)
      +
    + +
    +
    + + Moveable:SetOnCollidedWithObject(func) +
    +
    + Set the function to be called called when this moveable collides with another moveable + + +

    Parameters:

    +
      +
    • func + function + callback function to be called (must be in LevelFuncs hierarchy) +
    • +
    + + + + + +
    +
    + + Moveable:SetOnCollidedWithRoom(func) +
    +
    + Set the function called when this moveable collides with room geometry (e.g. a wall or floor) + + +

    Parameters:

    +
      +
    • func + function + callback function to be called (must be in LevelFuncs hierarchy) +
    • +
    + + + + + +
    +
    + + Moveable:GetPosition() +
    +
    + Get the object's position + + + +

    Returns:

    +
      + + Vec3 + a copy of the moveable's position +
    + + + + +
    +
    + + Moveable:SetPosition(position[, updateRoom]) +
    +
    + Set the moveable's position + If you are moving a moveable whose behaviour involves knowledge of room geometry, + (e.g. a BADDY1, which uses it for pathfinding), then the second argument should + be true (or omitted, as true is the default). Otherwise, said moveable will not behave correctly. + + +

    Parameters:

    +
      +
    • position + Vec3 + the new position of the moveable +
    • +
    • updateRoom + bool + Will room changes be automatically detected? Set to false if you are using overlapping rooms (default: true) + (optional) +
    • +
    + + + + + +
    +
    + + Moveable:GetRoom() +
    +
    + Get the current room of the object + + + +

    Returns:

    +
      + + int + number representing the current room of the object +
    + + + + +
    +
    + + Moveable:SetRoom(ID) +
    +
    + Set room of object + Use this if you are not using SetPosition's automatic room update - for example, when dealing with overlapping rooms. + + +

    Parameters:

    +
      +
    • ID + int + the ID of the new room +
    • +
    + + + + +

    Usage:

    +
      +
      local sas = TEN.Objects.GetMoveableByName("sas_enemy")
      +sas:SetRoom(destinationRoom)
      +sas:SetPosition(destinationPosition, false)
      +
    +
    @@ -1488,7 +1480,7 @@ sas:SetPosition(destinationPosition)
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Objects.Sink.html b/Documentation/doc/2 classes/Objects.Sink.html index 329da6b12..616bf6071 100644 --- a/Documentation/doc/2 classes/Objects.Sink.html +++ b/Documentation/doc/2 classes/Objects.Sink.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -262,7 +262,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Objects.SoundSource.html b/Documentation/doc/2 classes/Objects.SoundSource.html index cfb423da8..e500057ee 100644 --- a/Documentation/doc/2 classes/Objects.SoundSource.html +++ b/Documentation/doc/2 classes/Objects.SoundSource.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -260,7 +260,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Objects.Static.html b/Documentation/doc/2 classes/Objects.Static.html index e79e73406..5a418e622 100644 --- a/Documentation/doc/2 classes/Objects.Static.html +++ b/Documentation/doc/2 classes/Objects.Static.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -112,6 +112,14 @@ Set the static's rotation + Static:GetScale() + Get the static's scale + + + Static:SetScale(scale) + Set the static's scale + + Static:GetName() Get the static's unique string identifier @@ -226,6 +234,47 @@ + +
    + + Static:GetScale() +
    +
    + Get the static's scale + + + +

    Returns:

    +
      + + float + current static scale +
    + + + + +
    +
    + + Static:SetScale(scale) +
    +
    + Set the static's scale + + +

    Parameters:

    + + + + + +
    @@ -358,7 +407,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/2 classes/Strings.DisplayString.html b/Documentation/doc/2 classes/Strings.DisplayString.html index d73bbcdd6..ff200ed44 100644 --- a/Documentation/doc/2 classes/Strings.DisplayString.html +++ b/Documentation/doc/2 classes/Strings.DisplayString.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -307,7 +307,7 @@ TEN.Strings.DisplayStringOption.SHADOW -- will give the text a small shadow
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/3 primitive classes/Color.html b/Documentation/doc/3 primitive classes/Color.html index 1c762a147..8f3cc8700 100644 --- a/Documentation/doc/3 primitive classes/Color.html +++ b/Documentation/doc/3 primitive classes/Color.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -312,7 +312,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/3 primitive classes/Rotation.html b/Documentation/doc/3 primitive classes/Rotation.html index 4dcb6d4fa..16168f482 100644 --- a/Documentation/doc/3 primitive classes/Rotation.html +++ b/Documentation/doc/3 primitive classes/Rotation.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -250,7 +250,7 @@ All values will be clamped to [-32768, 32767].

    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/3 primitive classes/Vec3.html b/Documentation/doc/3 primitive classes/Vec3.html index 3e37fa21e..f4548ddf3 100644 --- a/Documentation/doc/3 primitive classes/Vec3.html +++ b/Documentation/doc/3 primitive classes/Vec3.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -277,7 +277,7 @@ However, this function would return it as (0, 1, 1).
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/4 enums/Effects.BlendID.html b/Documentation/doc/4 enums/Effects.BlendID.html index 4ec654dd5..a9fc3635c 100644 --- a/Documentation/doc/4 enums/Effects.BlendID.html +++ b/Documentation/doc/4 enums/Effects.BlendID.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -149,7 +149,7 @@ ALPHABLEND
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/4 enums/Flow.InvID.html b/Documentation/doc/4 enums/Flow.InvID.html index fde91a227..6730933b2 100644 --- a/Documentation/doc/4 enums/Flow.InvID.html +++ b/Documentation/doc/4 enums/Flow.InvID.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -358,7 +358,7 @@ EXAMINE_ITEM8_COMBO2
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/4 enums/Objects.ObjID.html b/Documentation/doc/4 enums/Objects.ObjID.html index 74f8fea1f..ea092e640 100644 --- a/Documentation/doc/4 enums/Objects.ObjID.html +++ b/Documentation/doc/4 enums/Objects.ObjID.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -1096,7 +1096,7 @@ PANEL_MIDDLE_CORNER
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/5 lua utility modules/EventSequence.html b/Documentation/doc/5 lua utility modules/EventSequence.html index be2753a3d..de2ccbfc4 100644 --- a/Documentation/doc/5 lua utility modules/EventSequence.html +++ b/Documentation/doc/5 lua utility modules/EventSequence.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -90,10 +90,13 @@

    Event sequence - a chain of functions to call at specified times, modeled after TRNG's organizers.

    + +

    Works atop the Timer, and so is updated automatically pre-OnControlPhase, and saved automatically when the game is saved.

    +

    Example usage:

    -local EventSequence = require("EventSequence")
    +local EventSequence = require("Engine.EventSequence")
     
     -- These will be called by the sequence
     LevelFuncs.HealLara = function()
    @@ -115,22 +118,17 @@ LevelFuncs.SpawnBaddy = function(baddy, name, pos)
             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
    +        LevelFuncs.HealLara, -- first function to call. If we don't need to pass any arguments, we can just pass the function
             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
    +        {LevelFuncs.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 function (LevelFuncs.SpawnBaddy in this case) followed by the args to pass to it
             0.5,
    -        {"SpawnBaddy", TEN.Objects.ObjID.SAS_CAIRO, "chris", posChris},
    +        {LevelFuncs.SpawnBaddy, TEN.Objects.ObjID.SAS_CAIRO, "chris", posChris},
             1,
    -        "HealLara")
    +        LevelFuncs.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
     

    @@ -333,7 +331,7 @@ LevelFuncs.SpawnBaddy = function(baddy, name, pos)
    generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
    diff --git a/Documentation/doc/5 lua utility modules/Timer.html b/Documentation/doc/5 lua utility modules/Timer.html index c5d37ad0c..4202feb5b 100644 --- a/Documentation/doc/5 lua utility modules/Timer.html +++ b/Documentation/doc/5 lua utility modules/Timer.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -90,10 +90,13 @@

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

    + +

    Timers are updated automatically every frame before OnControlPhase.

    +

    Example usage:

    -local Timer = require("Timer")
    +local Timer = require("Engine.Timer")
     
     -- This will be called when the timer runs out
     LevelFuncs.FinishTimer = function(healthWhenStarted, victoryMessage)
    @@ -107,15 +110,11 @@
             5.0,
             false,
             {minutes = false, seconds = true, deciseconds = true},
    -        "FinishTimer",
    +        LevelFuncs.FinishTimer,
             Lara:GetHP(),
             "Well done!")
         myTimer:Start()
     end
    -
    -LevelFuncs.OnControlPhase = function(dt)
    -    Timer.UpdateAll(dt)
    -end
     

    @@ -124,7 +123,7 @@ LevelFuncs.OnControlPhase = function(dt)

    Functions

    - + @@ -132,10 +131,6 @@ LevelFuncs.OnControlPhase = function(dt) - - - - @@ -189,8 +184,8 @@ LevelFuncs.OnControlPhase = function(dt)
    - - Create(name, totalTime, loop, timerFormat, func[, ...]) + + func(name, totalTime, loop, timerFormat[, ...])
    Create (but do not start) a new timer.

    @@ -214,7 +209,10 @@ local myTimeFormat4 = {seconds = true}

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

    +

    Do not give your timers a name beginning with __TEN, as this is reserved for timers used by other internal libaries.

    +

    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. + The LevelFunc function to call when the time is up

    Parameters:

    @@ -235,10 +233,6 @@ local myTimeFormat4 = {seconds = true} 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 upssss -
  • ... a variable number of arguments with which the above function will be called (optional) @@ -280,28 +274,6 @@ local myTimeFormat4 = {seconds = true} -
  • -
    - - UpdateAll(dt) -
    -
    - Update all active timers. - Should be called in LevelFuncs.OnControlPhase - - -

    Parameters:

    -
      -
    • dt - number - The time in seconds since the last frame -
    • -
    - - - - -
    @@ -314,8 +286,8 @@ local myTimeFormat4 = {seconds = true}

    Parameters:

    • func - string - The name of the LevelFunc member to call when the time is up + function + The LevelFunc member to call when the time is up
    • ... a variable number of arguments with which the above function will be called @@ -524,7 +496,7 @@ local myTimeFormat4 = {seconds = true}
      generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
      diff --git a/Documentation/doc/5 lua utility modules/Util.html b/Documentation/doc/5 lua utility modules/Util.html index 5f54cb8a0..3d5ae7bb3 100644 --- a/Documentation/doc/5 lua utility modules/Util.html +++ b/Documentation/doc/5 lua utility modules/Util.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -138,7 +138,7 @@
      generated by LDoc 1.4.6 -Last updated 2022-08-11 22:43:52 +Last updated 2022-09-07 20:56:06
      diff --git a/Documentation/doc/index.html b/Documentation/doc/index.html index af9e6cbea..e6d470ea8 100644 --- a/Documentation/doc/index.html +++ b/Documentation/doc/index.html @@ -3,7 +3,7 @@ - TombEngine 1.0.1 Lua API + TombEngine 1.0.2 Lua API @@ -80,7 +80,7 @@
      -

      TombEngine 1.0.1 scripting interface

      +

      TombEngine 1.0.2 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 here.

      @@ -90,7 +90,7 @@ For example, to call GetMoveableByName, you would have to do:

      local door = TEN.Objects.GetMoveableByName("door_type4_14")
       

      To save on typing, you can put the following at the start of a Lua file:

      -
      local Util = require("Util")
      +
      local Util = require("Engine.Util")
       Util.ShortenTENCalls()
       

      This will put the modules and classes in the global table. In other words, it means you can do the following:

      @@ -248,7 +248,7 @@ Util.ShortenTENCalls()
      generated by LDoc 1.4.6 -Last updated 2022-08-14 20:33:42 +Last updated 2022-09-07 20:56:06
      diff --git a/Scripts/EventSequence.lua b/Scripts/Engine/EventSequence.lua similarity index 77% rename from Scripts/EventSequence.lua rename to Scripts/Engine/EventSequence.lua index 6fde9d7bc..ae1646195 100644 --- a/Scripts/EventSequence.lua +++ b/Scripts/Engine/EventSequence.lua @@ -1,7 +1,10 @@ ----- --- Event sequence - a chain of functions to call at specified times, modeled after TRNG's organizers. +-- +-- Works atop the Timer, and so is updated automatically pre-OnControlPhase, and saved automatically when the game is saved. +-- -- Example usage: --- local EventSequence = require("EventSequence") +-- local EventSequence = require("Engine.EventSequence") -- -- -- These will be called by the sequence -- LevelFuncs.HealLara = function() @@ -23,34 +26,30 @@ -- 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 +-- LevelFuncs.HealLara, -- first function to call. If we don't need to pass any arguments, we can just pass the function -- 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 +-- {LevelFuncs.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 function (LevelFuncs.SpawnBaddy in this case) followed by the args to pass to it -- 0.5, --- {"SpawnBaddy", TEN.Objects.ObjID.SAS_CAIRO, "chris", posChris}, +-- {LevelFuncs.SpawnBaddy, TEN.Objects.ObjID.SAS_CAIRO, "chris", posChris}, -- 1, --- "HealLara") +-- LevelFuncs.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.Engine.EventSequence = {} +LevelVars.Engine.EventSequence = {sequences = {}} -LevelFuncs.__TEN_eventSequence_callNext = function(sequenceName, nextTimerName, func, ...) - local thisES = LevelVars.__TEN_eventSequence.sequences[sequenceName] - LevelFuncs[func](...) +LevelFuncs.Engine.EventSequence.CallNext = function(sequenceName, nextTimerName, func, ...) + local thisES = LevelVars.Engine.EventSequence.sequences[sequenceName] + func(...) thisES.currentTimer = thisES.currentTimer + 1 if thisES.currentTimer <= #thisES.timers then @@ -82,9 +81,8 @@ EventSequence = { setmetatable(obj, mt) obj.name = name - - LevelVars.__TEN_eventSequence.sequences[name] = {} - local thisES = LevelVars.__TEN_eventSequence.sequences[name] + LevelVars.Engine.EventSequence.sequences[name] = {} + local thisES = LevelVars.Engine.EventSequence.sequences[name] thisES.name = name thisES.timesFuncsAndArgs = {...} thisES.loop = loop @@ -99,7 +97,6 @@ EventSequence = { local nextTimer = i + 2 local timerIndex = #thisES.timers + 1 - local funcName = "__TEN_eventSequence_" .. name .. "_func" .. timerIndex local timerName = "__TEN_eventSequence_" .. name .. "_timer" .. timerIndex local nextTimerName = "__TEN_eventSequence_" .. name .. "_timer" .. timerIndex + 1 @@ -110,7 +107,7 @@ EventSequence = { thisES.firstTimerName = timerName end - if type(funcAndArgs) == "string" then + if type(funcAndArgs) == "userdata" then -- we only have a function func = funcAndArgs funcAndArgs = {} @@ -123,7 +120,7 @@ EventSequence = { tfa[i], -- time false, timerFormat, - "__TEN_eventSequence_callNext", + LevelFuncs.Engine.EventSequence.CallNext, name, nextTimerName, func, @@ -140,7 +137,7 @@ EventSequence = { -- @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 + if LevelVars.Engine.EventSequence.sequences[name] then local obj = {} local mt = {} mt.__index = EventSequence @@ -155,7 +152,7 @@ EventSequence = { -- @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] + local thisES = LevelVars.Engine.EventSequence.sequences[t.name] Timer.Get(thisES.timers[thisES.currentTimer]):SetPaused(p) end; @@ -163,21 +160,21 @@ EventSequence = { -- @function mySequence:IsPaused -- @return true if the timer is paused, false if otherwise IsPaused = function(t) - local thisES = LevelVars.__TEN_eventSequence.sequences[t.name] + local thisES = LevelVars.Engine.EventSequence.sequences[t.name] 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] + local thisES = LevelVars.Engine.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] + local thisES = LevelVars.Engine.EventSequence.sequences[t.name] Timer.Get(thisES.timers[thisES.currentTimer]):Stop() end; @@ -185,7 +182,7 @@ EventSequence = { -- @function mySequence:IsActive -- @return true if the sequence is active, false if otherwise IsActive = function(t) - local thisES = LevelVars.__TEN_eventSequence.sequences[t.name] + local thisES = LevelVars.Engine.EventSequence.sequences[t.name] return Timer.Get(thisES.timers[thisES.currentTimer]):IsActive() end; } diff --git a/Scripts/Timer.lua b/Scripts/Engine/Timer.lua similarity index 80% rename from Scripts/Timer.lua rename to Scripts/Engine/Timer.lua index 66567506e..e0ab99df2 100644 --- a/Scripts/Timer.lua +++ b/Scripts/Engine/Timer.lua @@ -1,7 +1,10 @@ ----- --- Basic timer - after a specified number of seconds, the specified thing happens. +-- +-- Timers are updated automatically every frame before OnControlPhase. +-- -- Example usage: --- local Timer = require("Timer") +-- local Timer = require("Engine.Timer") -- -- -- This will be called when the timer runs out -- LevelFuncs.FinishTimer = function(healthWhenStarted, victoryMessage) @@ -15,19 +18,16 @@ -- 5.0, -- false, -- {minutes = false, seconds = true, deciseconds = true}, --- "FinishTimer", +-- LevelFuncs.FinishTimer, -- Lara:GetHP(), -- "Well done!") -- myTimer:Start() -- end -- --- LevelFuncs.OnControlPhase = function(dt) --- Timer.UpdateAll(dt) --- end --- -- @luautil Timer -LevelVars.__TEN_timer = {timers = {}} +LevelFuncs.Engine.Timer = {} +LevelVars.Engine.Timer = {timers = {}} local Timer @@ -35,6 +35,7 @@ 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. -- @@ -56,13 +57,15 @@ Timer = { -- --__At any given time, only one timer can show its countdown__. -- + --__Do not give your timers a name beginning with __TEN, as this is reserved for timers used by other internal libaries__. + -- --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 -- @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 + -- @function func The LevelFunc function to call when the time is up -- @param[opt] ... a variable number of arguments with which the above function will be called -- @return The timer in its paused state -- @@ -73,8 +76,13 @@ Timer = { setmetatable(obj, mt) obj.name = name - LevelVars.__TEN_timer.timers[name] ={} - local thisTimer = LevelVars.__TEN_timer.timers[name] + + if LevelVars.Engine.Timer.timers[name] then + print("Warning: a timer with name " .. name .. " already exists.") + end + + LevelVars.Engine.Timer.timers[name] ={} + local thisTimer = LevelVars.Engine.Timer.timers[name] thisTimer.name = name thisTimer.totalTime = totalTime thisTimer.remainingTime = totalTime @@ -95,7 +103,7 @@ Timer = { -- @string name The label that was given to the timer when it was created -- @return The timer Get = function(name) - if LevelVars.__TEN_timer.timers[name] then + if LevelVars.Engine.Timer.timers[name] then local obj = {} local mt = {} mt.__index = Timer @@ -112,8 +120,7 @@ Timer = { t.remainingTime = t.remainingTime - dt if t.remainingTime <= 0 then - LevelFuncs[t.func](table.unpack(t.funcArgs)) - + t.func(table.unpack(t.funcArgs)) if not t.loop then t.active = false else @@ -190,21 +197,16 @@ Timer = { 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 + print("Timer.UpdateAll is deprecated; timers and event sequences now get updated automatically pre-control phase.") 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 + -- @tparam function func 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] + local thisTimer = LevelVars.Engine.Timer.timers[t.name] thisTimer.func = func thisTimer.funcArgs = {...} end; @@ -212,7 +214,7 @@ Timer = { --- 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] + local thisTimer = LevelVars.Engine.Timer.timers[t.name] if not thisTimer.active then thisTimer.active = true end @@ -227,21 +229,21 @@ Timer = { --- Stop the timer. -- @function myTimer:Stop Stop = function(t) - LevelVars.__TEN_timer.timers[t.name].active = false + LevelVars.Engine.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 + return LevelVars.Engine.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] + local thisTimer = LevelVars.Engine.Timer.timers[t.name] thisTimer.paused = p if thisTimer.timerFormat then if p then @@ -256,21 +258,21 @@ Timer = { -- @function myTimer:IsPaused -- @return true if the timer is paused, false if otherwise IsPaused = function(t) - return LevelVars.__TEN_timer.timers[t.name].paused + return LevelVars.Engine.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 + return LevelVars.Engine.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 + LevelVars.Engine.Timer.timers[t.name].remainingTime = remainingTime end; --- Get the total time for a timer. @@ -278,23 +280,31 @@ Timer = { -- @function myTimer:GetRemainingTime -- @return the timer's total time GetTotalTime = function(t) - return LevelVars.__TEN_timer.timers[t.name].totalTime + return LevelVars.Engine.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 + LevelVars.Engine.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 + LevelVars.Engine.Timer.timers[t.name].loop = looping end; } +LevelFuncs.Engine.Timer.UpdateAll = function(dt) + for _, t in pairs(LevelVars.Engine.Timer.timers) do + Timer.Update(t, dt) + end +end + +TEN.Logic.AddCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, LevelFuncs.Engine.Timer.UpdateAll) + return Timer diff --git a/Scripts/Util.lua b/Scripts/Engine/Util.lua similarity index 100% rename from Scripts/Util.lua rename to Scripts/Engine/Util.lua diff --git a/Scripts/New_Level.lua b/Scripts/New_Level.lua index 417714e2e..eee9d00bf 100644 --- a/Scripts/New_Level.lua +++ b/Scripts/New_Level.lua @@ -1,7 +1,7 @@ -- New level script file. -- To include other script files, you can use require("filename") command. -local Util = require("Util") +local Util = require("Engine.Util") Util.ShortenTENCalls() -- Called when entering a level, either after leveljump, new game or loading game @@ -27,4 +27,4 @@ LevelFuncs.OnEnd = function() end LevelFuncs.PrintText = function(Triggerer, Argument) local TestText = DisplayString(Argument, 100, 100, Color.new(250,250,250)) ShowString(TestText, 1) -end \ No newline at end of file +end diff --git a/Scripts/Title.lua b/Scripts/Title.lua index 7f448b583..e6afcd88e 100644 --- a/Scripts/Title.lua +++ b/Scripts/Title.lua @@ -1,10 +1,10 @@ -- Title script file -local Util = require("Util") +local Util = require("Engine.Util") Util.ShortenTENCalls() LevelFuncs.OnLoad = function() end LevelFuncs.OnSave = function() end LevelFuncs.OnEnd = function() end - -LevelFuncs.OnControlPhase = function() end \ No newline at end of file +LevelFuncs.OnStart = function() end +LevelFuncs.OnControlPhase = function(dt) end diff --git a/TombEngine/Game/Lara/lara_fire.cpp b/TombEngine/Game/Lara/lara_fire.cpp index 31fab1baa..1e9f22750 100644 --- a/TombEngine/Game/Lara/lara_fire.cpp +++ b/TombEngine/Game/Lara/lara_fire.cpp @@ -570,7 +570,9 @@ void LaraGun(ItemInfo* laraItem) { if (!GetAmmo(laraItem, lara->Control.Weapon.GunType)) { - lara->Control.Weapon.RequestGunType = Objects[ID_PISTOLS_ITEM].loaded ? LaraWeaponType::Pistol : LaraWeaponType::None; + bool hasPistols = lara->Weapons[(int)LaraWeaponType::Pistol].Present && Objects[ID_PISTOLS_ITEM].loaded; + + lara->Control.Weapon.RequestGunType = hasPistols ? LaraWeaponType::Pistol : LaraWeaponType::None; return; } } @@ -1186,7 +1188,7 @@ HolsterSlot HolsterSlotForWeapon(LaraWeaponType weaponType) return HolsterSlot::Harpoon; case LaraWeaponType::Crossbow: - return HolsterSlot::Crowssbow; + return HolsterSlot::Crossbow; case LaraWeaponType::GrenadeLauncher: return HolsterSlot::GrenadeLauncher; diff --git a/TombEngine/Game/Lara/lara_one_gun.cpp b/TombEngine/Game/Lara/lara_one_gun.cpp index e82d949ab..6e9bd052f 100644 --- a/TombEngine/Game/Lara/lara_one_gun.cpp +++ b/TombEngine/Game/Lara/lara_one_gun.cpp @@ -436,8 +436,12 @@ void UndrawShotgunMeshes(ItemInfo* laraItem, LaraWeaponType weaponType) { auto* lara = GetLaraInfo(laraItem); - lara->Control.Weapon.HolsterInfo.BackHolster = HolsterSlotForWeapon(weaponType); lara->MeshPtrs[LM_RHAND] = Objects[ID_LARA_SKIN].meshIndex + LM_RHAND; + + if (lara->Weapons[(int)weaponType].Present) + lara->Control.Weapon.HolsterInfo.BackHolster = HolsterSlotForWeapon(weaponType); + else + lara->Control.Weapon.HolsterInfo.BackHolster = HolsterSlot::Empty; } void FireHarpoon(ItemInfo* laraItem) @@ -621,10 +625,6 @@ void HarpoonBoltControl(short itemNumber) auto pos = PHD_3DPOS(currentMesh->pos.Position.x, currentMesh->pos.Position.y - 128, currentMesh->pos.Position.z, 0, currentMesh->pos.Orientation.y, 0); TriggerShockwave(&pos, 40, 176, 64, 0, 96, 128, 16, 0, 0); ShatterObject(nullptr, currentMesh, -128, item->RoomNumber, 0); - SmashedMeshRoom[SmashedMeshCount] = item->RoomNumber; - SmashedMesh[SmashedMeshCount] = currentMesh; - SmashedMeshCount++; - currentMesh->flags &= ~StaticMeshFlags::SM_VISIBLE; } } @@ -1009,10 +1009,6 @@ void GrenadeControl(short itemNumber) auto pos = PHD_3DPOS(currentMesh->pos.Position.x, currentMesh->pos.Position.y - 128, currentMesh->pos.Position.z, 0, currentMesh->pos.Orientation.y, 0); TriggerShockwave(&pos, 40, 176, 64, 0, 96, 128, 16, 0, 0); ShatterObject(nullptr, currentMesh, -128, item->RoomNumber, 0); - SmashedMeshRoom[SmashedMeshCount] = item->RoomNumber; - SmashedMesh[SmashedMeshCount] = currentMesh; - SmashedMeshCount++; - currentMesh->flags &= ~StaticMeshFlags::SM_VISIBLE; } } @@ -1316,10 +1312,6 @@ void RocketControl(short itemNumber) auto pose = PHD_3DPOS(currentMesh->pos.Position.x, currentMesh->pos.Position.y - 128, currentMesh->pos.Position.z, 0, currentMesh->pos.Orientation.y, 0); TriggerShockwave(&pose, 40, 176, 64, 0, 96, 128, 16, 0, 0); ShatterObject(nullptr, currentMesh, -128, item->RoomNumber, 0); - SmashedMeshRoom[SmashedMeshCount] = item->RoomNumber; - SmashedMesh[SmashedMeshCount] = currentMesh; - SmashedMeshCount++; - currentMesh->flags &= ~StaticMeshFlags::SM_VISIBLE; } } @@ -1598,13 +1590,7 @@ void CrossbowBoltControl(short itemNumber) { currentMesh->HitPoints -= Weapons[(int)LaraWeaponType::Crossbow].Damage; if (currentMesh->HitPoints <= 0) - { ShatterObject(nullptr, currentMesh, -128, item->RoomNumber, 0); - SmashedMeshRoom[SmashedMeshCount] = item->RoomNumber; - SmashedMesh[SmashedMeshCount] = currentMesh; - SmashedMeshCount++; - currentMesh->flags &= ~StaticMeshFlags::SM_VISIBLE; - } } k++; diff --git a/TombEngine/Game/Lara/lara_struct.h b/TombEngine/Game/Lara/lara_struct.h index 4a1e01fba..d251f129e 100644 --- a/TombEngine/Game/Lara/lara_struct.h +++ b/TombEngine/Game/Lara/lara_struct.h @@ -891,7 +891,7 @@ enum class HolsterSlot Shotgun = ID_SHOTGUN_ANIM, HK = ID_HK_ANIM, Harpoon = ID_HARPOON_ANIM, - Crowssbow = ID_CROSSBOW_ANIM, + Crossbow = ID_CROSSBOW_ANIM, GrenadeLauncher = ID_GRENADE_ANIM, RocketLauncher = ID_ROCKET_ANIM }; diff --git a/TombEngine/Game/Lara/lara_two_guns.cpp b/TombEngine/Game/Lara/lara_two_guns.cpp index 2bc720fde..4b93d3add 100644 --- a/TombEngine/Game/Lara/lara_two_guns.cpp +++ b/TombEngine/Game/Lara/lara_two_guns.cpp @@ -490,7 +490,10 @@ void UndrawPistolMeshRight(ItemInfo* laraItem, LaraWeaponType weaponType) auto* lara = GetLaraInfo(laraItem); lara->MeshPtrs[LM_RHAND] = Objects[ID_LARA_SKIN].meshIndex + LM_RHAND; - lara->Control.Weapon.HolsterInfo.RightHolster = HolsterSlotForWeapon(weaponType); + if (lara->Weapons[(int)weaponType].Present) + lara->Control.Weapon.HolsterInfo.RightHolster = HolsterSlotForWeapon(weaponType); + else + lara->Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty; } void UndrawPistolMeshLeft(ItemInfo* laraItem, LaraWeaponType weaponType) @@ -500,6 +503,10 @@ void UndrawPistolMeshLeft(ItemInfo* laraItem, LaraWeaponType weaponType) if (weaponType != LaraWeaponType::Revolver) { lara->MeshPtrs[LM_LHAND] = Objects[ID_LARA_SKIN].meshIndex + LM_LHAND; - lara->Control.Weapon.HolsterInfo.LeftHolster = HolsterSlotForWeapon(weaponType); + + if (lara->Weapons[(int)weaponType].Present) + lara->Control.Weapon.HolsterInfo.LeftHolster = HolsterSlotForWeapon(weaponType); + else + lara->Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty; } } diff --git a/TombEngine/Game/animation.cpp b/TombEngine/Game/animation.cpp index b3fe9013e..fac0174c8 100644 --- a/TombEngine/Game/animation.cpp +++ b/TombEngine/Game/animation.cpp @@ -476,7 +476,9 @@ BOUNDING_BOX* GetBoundsAccurate(ItemInfo* item) InterpolatedBounds.Y2 = framePtr[0]->boundingBox.Y2 + (framePtr[1]->boundingBox.Y2 - framePtr[0]->boundingBox.Y2) * frac / rate; InterpolatedBounds.Z1 = framePtr[0]->boundingBox.Z1 + (framePtr[1]->boundingBox.Z1 - framePtr[0]->boundingBox.Z1) * frac / rate; InterpolatedBounds.Z2 = framePtr[0]->boundingBox.Z2 + (framePtr[1]->boundingBox.Z2 - framePtr[0]->boundingBox.Z2) * frac / rate; - return &InterpolatedBounds; + { + return &InterpolatedBounds; + } } } diff --git a/TombEngine/Game/collision/collide_item.cpp b/TombEngine/Game/collision/collide_item.cpp index 29be4477c..5682153de 100644 --- a/TombEngine/Game/collision/collide_item.cpp +++ b/TombEngine/Game/collision/collide_item.cpp @@ -271,10 +271,10 @@ bool TestWithGlobalCollisionBounds(ItemInfo* item, ItemInfo* laraItem, Collision { auto* framePtr = GetBestFrame(laraItem); - if (item->Pose.Position.y + GlobalCollisionBounds.Y2 <= laraItem->Pose.Position.y + framePtr->boundingBox.Y1) + if ((item->Pose.Position.y + GlobalCollisionBounds.Y2) <= (laraItem->Pose.Position.y + framePtr->boundingBox.Y1)) return false; - if (item->Pose.Position.y + GlobalCollisionBounds.Y1 >= framePtr->boundingBox.Y2) + if ((item->Pose.Position.y + GlobalCollisionBounds.Y1) >= framePtr->boundingBox.Y2) return false; float sinY = phd_sin(item->Pose.Orientation.y); @@ -286,10 +286,10 @@ bool TestWithGlobalCollisionBounds(ItemInfo* item, ItemInfo* laraItem, Collision int x = (dx * cosY) - (dz * sinY); int z = (dz * cosY) + (dx * sinY); - if (x < GlobalCollisionBounds.X1 - coll->Setup.Radius || - x > GlobalCollisionBounds.X2 + coll->Setup.Radius || - z < GlobalCollisionBounds.Z1 - coll->Setup.Radius || - z > GlobalCollisionBounds.Z2 + coll->Setup.Radius) + if (x < (GlobalCollisionBounds.X1 - coll->Setup.Radius) || + x > (GlobalCollisionBounds.X2 + coll->Setup.Radius) || + z < (GlobalCollisionBounds.Z1 - coll->Setup.Radius) || + z > (GlobalCollisionBounds.Z2 + coll->Setup.Radius)) { return false; } @@ -331,7 +331,7 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll) continue; } - if (phd_Distance(&item->Pose, &item2->Pose) < COLLISION_CHECK_DISTANCE) + if (Vector3Int::Distance(item->Pose.Position, item2->Pose.Position) < COLLISION_CHECK_DISTANCE) { auto box = TO_DX_BBOX(item2->Pose, GetBoundsAccurate(item2)); float distance; @@ -346,16 +346,14 @@ void TestForObjectOnLedge(ItemInfo* item, CollisionInfo* coll) itemNumber = item2->NextItem; } - for (int j = 0; j < g_Level.Rooms[i].mesh.size(); j++) + for (auto& mesh : g_Level.Rooms[i].mesh) { - auto* mesh = &g_Level.Rooms[i].mesh[j]; - - if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE)) + if (!(mesh.flags & StaticMeshFlags::SM_VISIBLE)) continue; - if (phd_Distance(&item->Pose, &mesh->pos) < COLLISION_CHECK_DISTANCE) + if (phd_Distance(&item->Pose, &mesh.pos) < COLLISION_CHECK_DISTANCE) { - auto box = TO_DX_BBOX(mesh->pos, GetBoundsAccurate(mesh, false)); + auto box = TO_DX_BBOX(mesh.pos, GetBoundsAccurate(&mesh, false)); float distance; if (box.Intersects(origin, direction, distance) && distance < coll->Setup.Radius * 2) @@ -453,7 +451,7 @@ bool MoveLaraPosition(Vector3Int* offset, ItemInfo* item, ItemInfo* laraItem) { auto* lara = GetLaraInfo(laraItem); - Matrix matrix = Matrix::CreateFromYawPitchRoll( + auto matrix = Matrix::CreateFromYawPitchRoll( TO_RAD(item->Pose.Orientation.y), TO_RAD(item->Pose.Orientation.x), TO_RAD(item->Pose.Orientation.z) @@ -491,12 +489,12 @@ static bool ItemInRange(int x, int z, int radius) return ((SQUARE(x) + SQUARE(z)) <= SQUARE(radius)); } -bool ItemNearLara(PHD_3DPOS* pos, int radius) +bool ItemNearLara(Vector3Int* origin, int radius) { auto target = GameVector( - pos->Position.x - LaraItem->Pose.Position.x, - pos->Position.y - LaraItem->Pose.Position.y, - pos->Position.z - LaraItem->Pose.Position.z + origin->x - LaraItem->Pose.Position.x, + origin->y - LaraItem->Pose.Position.y, + origin->z - LaraItem->Pose.Position.z ); if (!ItemCollide(target.y, ITEM_RADIUS_YMAX)) @@ -515,9 +513,9 @@ bool ItemNearLara(PHD_3DPOS* pos, int radius) return false; } -bool ItemNearTarget(PHD_3DPOS* origin, ItemInfo* target, int radius) +bool ItemNearTarget(Vector3Int* origin, ItemInfo* targetEntity, int radius) { - auto pos = origin->Position - target->Pose.Position; + auto pos = *origin - targetEntity->Pose.Position; if (!ItemCollide(pos.y, ITEM_RADIUS_YMAX)) return false; @@ -528,31 +526,31 @@ bool ItemNearTarget(PHD_3DPOS* origin, ItemInfo* target, int radius) if (!ItemInRange(pos.x, pos.z, radius)) return false; - auto* bounds = GetBoundsAccurate(target); + auto* bounds = GetBoundsAccurate(targetEntity); if (pos.y >= bounds->Y1 && pos.y <= bounds->Y2) return true; return false; } -bool Move3DPosTo3DPos(PHD_3DPOS* origin, PHD_3DPOS* target, int velocity, short angleAdd) +bool Move3DPosTo3DPos(PHD_3DPOS* fromPose, PHD_3DPOS* toPose, int velocity, short angleAdd) { - auto direction = target->Position - origin->Position; - int distance = sqrt(SQUARE(direction.x) + SQUARE(direction.y) + SQUARE(direction.z)); + auto direction = toPose->Position - fromPose->Position; + float distance = Vector3Int::Distance(fromPose->Position, toPose->Position); if (velocity < distance) - origin->Position += direction * velocity / distance; + fromPose->Position += direction * (velocity / distance); else - origin->Position = target->Position; + fromPose->Position = toPose->Position; if (!Lara.Control.IsMoving) { - bool shouldAnimate = (distance - velocity) > (velocity * ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD); + bool shouldAnimate = ((distance - velocity) > (velocity * ANIMATED_ALIGNMENT_FRAME_COUNT_THRESHOLD)); if (shouldAnimate && Lara.Control.WaterStatus != WaterStatus::Underwater) { - int angle = mGetAngle(target->Position.x, target->Position.z, origin->Position.x, origin->Position.z); - int direction = (GetQuadrant(angle) - GetQuadrant(target->Orientation.y)) & 3; + int angle = mGetAngle(toPose->Position.x, toPose->Position.z, fromPose->Position.x, fromPose->Position.z); + int direction = (GetQuadrant(angle) - GetQuadrant(toPose->Orientation.y)) & 3; switch (direction) { @@ -583,31 +581,31 @@ bool Move3DPosTo3DPos(PHD_3DPOS* origin, PHD_3DPOS* target, int velocity, short Lara.Control.Count.PositionAdjust = 0; } - short deltaAngle = target->Orientation.x - origin->Orientation.x; + short deltaAngle = toPose->Orientation.x - fromPose->Orientation.x; if (deltaAngle > angleAdd) - origin->Orientation.x += angleAdd; + fromPose->Orientation.x += angleAdd; else if (deltaAngle < -angleAdd) - origin->Orientation.x -= angleAdd; + fromPose->Orientation.x -= angleAdd; else - origin->Orientation.x = target->Orientation.x; + fromPose->Orientation.x = toPose->Orientation.x; - deltaAngle = target->Orientation.y - origin->Orientation.y; + deltaAngle = toPose->Orientation.y - fromPose->Orientation.y; if (deltaAngle > angleAdd) - origin->Orientation.y += angleAdd; + fromPose->Orientation.y += angleAdd; else if (deltaAngle < -angleAdd) - origin->Orientation.y -= angleAdd; + fromPose->Orientation.y -= angleAdd; else - origin->Orientation.y = target->Orientation.y; + fromPose->Orientation.y = toPose->Orientation.y; - deltaAngle = target->Orientation.z - origin->Orientation.z; + deltaAngle = toPose->Orientation.z - fromPose->Orientation.z; if (deltaAngle > angleAdd) - origin->Orientation.z += angleAdd; + fromPose->Orientation.z += angleAdd; else if (deltaAngle < -angleAdd) - origin->Orientation.z -= angleAdd; + fromPose->Orientation.z -= angleAdd; else - origin->Orientation.z = target->Orientation.z; + fromPose->Orientation.z = toPose->Orientation.z; - return (origin->Position == target->Position && origin->Orientation == target->Orientation); + return (fromPose->Position == toPose->Position && fromPose->Orientation == toPose->Orientation); } bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius) @@ -626,13 +624,13 @@ bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius) int x = laraItem->Pose.Position.x - item->Pose.Position.x; int z = laraItem->Pose.Position.z - item->Pose.Position.z; - int dx = (cosY * x) - (sinY * z); - int dz = (cosY * z) + (sinY * x); + int dx = (x * cosY) - (z * sinY); + int dz = (z * cosY) + (x * sinY); - if (dx >= bounds->X1 - radius && - dx <= radius + bounds->X2 && - dz >= bounds->Z1 - radius && - dz <= radius + bounds->Z2) + if (dx >= (bounds->X1 - radius) && + dx <= (radius + bounds->X2) && + dz >= (bounds->Z1 - radius) && + dz <= (radius + bounds->Z2)) { return true; } @@ -688,11 +686,7 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa int rx = (dx * cosY) - (dz * sinY); int rz = (dz * cosY) + (dx * sinY); - BOUNDING_BOX* bounds; - if (bigPush & 2) - bounds = &GlobalCollisionBounds; - else - bounds = (BOUNDING_BOX*)GetBestFrame(item); + auto* bounds = (bigPush & 2) ? &GlobalCollisionBounds : (BOUNDING_BOX*)GetBestFrame(item); int minX = bounds->X1; int maxX = bounds->X2; @@ -729,8 +723,8 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa else rz -= bottom; - item2->Pose.Position.x = item->Pose.Position.x + cosY * rx + sinY * rz; - item2->Pose.Position.z = item->Pose.Position.z + cosY * rz - sinY * rx; + item2->Pose.Position.x = item->Pose.Position.x + (rx * cosY) + (rz * sinY); + item2->Pose.Position.z = item->Pose.Position.z + (rz * cosY) - (rx * sinY); auto* lara = item2->IsLara() ? GetLaraInfo(item2) : nullptr; @@ -755,12 +749,12 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa coll->Setup.LowerCeilingBound = 0; coll->Setup.UpperCeilingBound = MAX_HEIGHT; - auto facing = coll->Setup.ForwardAngle; + auto headingAngle = coll->Setup.ForwardAngle; coll->Setup.ForwardAngle = phd_atan(item2->Pose.Position.z - coll->Setup.OldPosition.z, item2->Pose.Position.x - coll->Setup.OldPosition.x); GetCollisionInfo(coll, item2); - coll->Setup.ForwardAngle = facing; + coll->Setup.ForwardAngle = headingAngle; if (coll->CollisionType == CT_NONE) { @@ -778,8 +772,8 @@ bool ItemPushItem(ItemInfo* item, ItemInfo* item2, CollisionInfo* coll, bool spa // If Lara is in the process of aligning to an object, cancel it. if (lara != nullptr && lara->Control.Count.PositionAdjust > (LARA_POSITION_ADJUST_MAX_TIME / 6)) { - Lara.Control.IsMoving = false; - Lara.Control.HandStatus = HandStatus::Free; + lara->Control.IsMoving = false; + lara->Control.HandStatus = HandStatus::Free; } return true; @@ -868,7 +862,7 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll) { auto* mesh = &g_Level.Rooms[i].mesh[j]; - // Only process meshes which are visible and solid + // Only process meshes which are visible and solid. if ((mesh->flags & StaticMeshFlags::SM_VISIBLE) && (mesh->flags & StaticMeshFlags::SM_SOLID)) { if (phd_Distance(&item->Pose, &mesh->pos) < COLLISION_CHECK_DISTANCE) @@ -882,39 +876,39 @@ void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll) } } -bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, CollisionInfo* coll) +bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pose, CollisionInfo* coll) { bool result = false; - // Get DX static bounds in global coords - auto staticBounds = TO_DX_BBOX(pos, box); + // Get DX static bounds in global coordinates. + auto staticBounds = TO_DX_BBOX(pose, box); - // Get local TR bounds and DX item bounds in global coords + // Get local TR bounds and DX item bounds in global coordinates. auto itemBBox = GetBoundsAccurate(item); auto itemBounds = TO_DX_BBOX(item->Pose, itemBBox); - // Extend bounds a bit for visual testing - itemBounds.Extents = itemBounds.Extents + Vector3(WALL_SIZE); + // Extend bounds a bit for visual testing. + itemBounds.Extents = itemBounds.Extents + Vector3(SECTOR(1)); - // Filter out any further checks if static isn't nearby + // Filter out any further checks if static isn't nearby. if (!staticBounds.Intersects(itemBounds)) return false; - // Bring back extents - itemBounds.Extents = itemBounds.Extents - Vector3(WALL_SIZE); + // Bring back extents. + itemBounds.Extents = itemBounds.Extents - Vector3(SECTOR(1)); - // Draw static bounds + // Draw static bounds. g_Renderer.AddDebugBox(staticBounds, Vector4(1, 0.3f, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS); - // Calculate horizontal item coll bounds according to radius + // Calculate horizontal item collision bounds according to radius. BOUNDING_BOX collBox; collBox.X1 = -coll->Setup.Radius; collBox.X2 = coll->Setup.Radius; collBox.Z1 = -coll->Setup.Radius; collBox.Z2 = coll->Setup.Radius; - // Calculate vertical item coll bounds according to either height (land mode) or precise bounds (water mode). - // Water mode needs special processing because height calc in original engines is inconsistent in such cases. + // Calculate vertical item collision bounds according to either height (land mode) or precise bounds (water mode). + // Water mode needs special processing because height calculation in original engines is inconsistent in such cases. if (TestEnvironment(ENV_FLAG_WATER, item)) { collBox.Y1 = itemBBox->Y1; @@ -926,18 +920,18 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis collBox.Y2 = 0; } - // Get and test DX item coll bounds + // Get and test DX item collision bounds. auto collBounds = TO_DX_BBOX(PHD_3DPOS(item->Pose.Position), &collBox); bool intersects = staticBounds.Intersects(collBounds); - // Check if previous item horizontal position intersects bounds + // Check if previous item horizontal position intersects bounds. auto oldCollBounds = TO_DX_BBOX(PHD_3DPOS(coll->Setup.OldPosition.x, item->Pose.Position.y, coll->Setup.OldPosition.z), &collBox); bool oldHorIntersects = staticBounds.Intersects(oldCollBounds); - // Draw item coll bounds + // Draw item coll bounds. g_Renderer.AddDebugBox(collBounds, intersects ? Vector4(1, 0, 0, 1) : Vector4(0, 1, 0, 1), RENDERER_DEBUG_PAGE::LOGIC_STATS); - // Decompose static bounds into top/bottom plane vertices + // Decompose static bounds into top/bottom plane vertices. Vector3 corners[8]; staticBounds.GetCorners(corners); Vector3 planeVertices[4][3] = @@ -948,7 +942,7 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis { corners[3], corners[6], corners[2] } }; - // Determine collision box vertical dimensions + // Determine collision box vertical dimensions. auto height = collBox.Height(); auto center = item->Pose.Position.y - height / 2; @@ -960,24 +954,23 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis for (int i = 0; i < 4; i++) { - // Calculate ray direction + // Calculate ray direction. auto mxR = Matrix::CreateFromYawPitchRoll(TO_RAD(item->Pose.Orientation.y), TO_RAD(item->Pose.Orientation.x + (ANGLE(90 * i))), 0); auto mxT = Matrix::CreateTranslation(Vector3::UnitY); auto direction = (mxT * mxR).Translation(); - // Make a ray and do ray tests against all decomposed planes + // Make a ray and do ray tests against all decomposed planes. auto ray = Ray(collBounds.Center, direction); - // Determine if top/bottom planes are closest ones or not + // Determine if top/bottom planes are closest ones or not. for (int p = 0; p < 4; p++) { - // No plane intersection, quickly discard + // No plane intersection, quickly discard. float d = 0.0f; if (!ray.Intersects(planeVertices[p][0], planeVertices[p][1], planeVertices[p][2], d)) continue; - // Process plane intersection only if distance is smaller - // than already found minimum + // Process plane intersection only if distance is smaller than already found minimum. if (d < minDistance) { closestRay = ray; @@ -987,24 +980,25 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis } } - if (closestPlane != -1) // Top/bottom plane found + // Top/bottom plane found. + if (closestPlane != -1) { auto bottom = closestPlane >= 2; auto yPoint = abs((closestRay.direction * minDistance).y); auto distanceToVerticalPlane = height / 2 - yPoint; - // Correct position according to top/bottom bounds, if collided and plane is nearby + // Correct position according to top/bottom bounds, if collided and plane is nearby. if (intersects && oldHorIntersects && minDistance < height) { if (bottom) { - // HACK: additionally subtract 2 from bottom plane, or else false positives may occur. + // HACK: Additionally subtract 2 from bottom plane, otherwise false positives may occur. item->Pose.Position.y += distanceToVerticalPlane + 2; coll->CollisionType = CT_TOP; } else { - // Set collision type only if dry room (in water rooms it causes stucking) + // Set collision type only if dry room (in water rooms the player can get stuck). item->Pose.Position.y -= distanceToVerticalPlane; coll->CollisionType = (g_Level.Rooms[item->RoomNumber].flags & 1) ? coll->CollisionType : CT_CLAMP; } @@ -1020,29 +1014,29 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis if (!intersects) return false; - // Check if bounds still collide after top/bottom position correction + // Check if bounds still collide after top/bottom position correction. if (!staticBounds.Intersects(TO_DX_BBOX(PHD_3DPOS(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z), &collBox))) return result; - // Determine identity rotation/distance - auto distance = Vector3(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z); - auto sinY = phd_sin(pos.Orientation.y); - auto cosY = phd_cos(pos.Orientation.y); + // Determine identity orientation/distance. + auto distance = (item->Pose.Position - pose.Position).ToVector3(); + auto sinY = phd_sin(pose.Orientation.y); + auto cosY = phd_cos(pose.Orientation.y); - // Rotate item to collision bounds identity - auto x = round(distance.x * cosY - distance.z * sinY) + pos.Position.x; + // Rotate item to collision bounds identity. + auto x = round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x; auto y = item->Pose.Position.y; - auto z = round(distance.x * sinY + distance.z * cosY) + pos.Position.z; + auto z = round((distance.x * sinY) + (distance.z * cosY)) + pose.Position.z; - // Determine identity static collision bounds - auto XMin = pos.Position.x + box->X1; - auto XMax = pos.Position.x + box->X2; - auto YMin = pos.Position.y + box->Y1; - auto YMax = pos.Position.y + box->Y2; - auto ZMin = pos.Position.z + box->Z1; - auto ZMax = pos.Position.z + box->Z2; + // Determine identity static collision bounds. + auto XMin = pose.Position.x + box->X1; + auto XMax = pose.Position.x + box->X2; + auto YMin = pose.Position.y + box->Y1; + auto YMax = pose.Position.y + box->Y2; + auto ZMin = pose.Position.z + box->Z1; + auto ZMax = pose.Position.z + box->Z2; - // Determine item collision bounds + // Determine item collision bounds. auto inXMin = x + collBox.X1; auto inXMax = x + collBox.X2; auto inYMin = y + collBox.Y1; @@ -1050,15 +1044,15 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis auto inZMin = z + collBox.Z1; auto inZMax = z + collBox.Z2; - // Don't calculate shifts if not in bounds + // Don't calculate shifts if not in bounds. if (inXMax <= XMin || inXMin >= XMax || inYMax <= YMin || inYMin >= YMax || inZMax <= ZMin || inZMin >= ZMax) return result; - // Calculate shifts + // Calculate shifts. - Vector3Int rawShift = {}; + auto rawShift = Vector3Int::Zero; auto shiftLeft = inXMax - XMin; auto shiftRight = XMax - inXMin; @@ -1076,13 +1070,13 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis else rawShift.z = shiftRight; - // Rotate previous collision position to identity - distance = Vector3(coll->Setup.OldPosition.x, coll->Setup.OldPosition.y, coll->Setup.OldPosition.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z); - auto ox = round(distance.x * cosY - distance.z * sinY) + pos.Position.x; - auto oz = round(distance.x * sinY + distance.z * cosY) + pos.Position.z; + // Rotate previous collision position to identity. + distance = (coll->Setup.OldPosition - pose.Position).ToVector3(); + auto ox = round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x; + auto oz = round((distance.x * sinY) + (distance.z * cosY)) + pose.Position.z; - // Calculate collisison type based on identity rotation - switch (GetQuadrant(coll->Setup.ForwardAngle - pos.Orientation.y)) + // Calculate collisison type based on identity orientation. + switch (GetQuadrant(coll->Setup.ForwardAngle - pose.Orientation.y)) { case NORTH: if (rawShift.x > coll->Setup.Radius || rawShift.x < -coll->Setup.Radius) @@ -1173,19 +1167,19 @@ bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, Collis break; } - // Determine final shifts rotation/distance - distance = Vector3(x + coll->Shift.x, y, z + coll->Shift.z) - Vector3(pos.Position.x, pos.Position.y, pos.Position.z); - sinY = phd_sin(-pos.Orientation.y); - cosY = phd_cos(-pos.Orientation.y); + // Determine final shifts orientation/distance. + distance = Vector3(x + coll->Shift.x, y, z + coll->Shift.z) - pose.Position.ToVector3(); + sinY = phd_sin(-pose.Orientation.y); + cosY = phd_cos(-pose.Orientation.y); - // Calculate final shifts rotation/distance - coll->Shift.x = (round(distance.x * cosY - distance.z * sinY) + pos.Position.x) - item->Pose.Position.x; - coll->Shift.z = (round(distance.x * sinY + distance.z * cosY) + pos.Position.z) - item->Pose.Position.z; + // Calculate final shifts orientation/distance. + coll->Shift.x = (round((distance.x * cosY) - (distance.z * sinY)) + pose.Position.x) - item->Pose.Position.x; + coll->Shift.z = (round((distance.x * sinY) + (distance.z * cosY)) + pose.Position.z) - item->Pose.Position.z; if (coll->Shift.x == 0 && coll->Shift.z == 0) - coll->CollisionType = CT_NONE; // Paranoid + coll->CollisionType = CT_NONE; // Paranoid. - // Set splat state flag if item is Lara and bounds are taller than Lara's headroom + // Set splat state flag if item is Lara and bounds are taller than Lara's headroom. if (item == LaraItem && coll->CollisionType == CT_FRONT) coll->HitTallObject = (YMin <= inYMin + LARA_HEADROOM); @@ -1197,39 +1191,39 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, { auto* item = &g_Level.Items[itemNumber]; - auto prevCollResult = GetCollision(x, y, z, item->RoomNumber); - auto collResult = GetCollision(item); + auto prevPointProbe = GetCollision(x, y, z, item->RoomNumber); + auto pointProbe = GetCollision(item); auto* bounds = GetBoundsAccurate(item); int radius = bounds->Height(); item->Pose.Position.y += radius; - if (item->Pose.Position.y >= collResult.Position.Floor) + if (item->Pose.Position.y >= pointProbe.Position.Floor) { int bs = 0; - if (collResult.Position.FloorSlope && prevCollResult.Position.Floor < collResult.Position.Floor) + if (pointProbe.Position.FloorSlope && prevPointProbe.Position.Floor < pointProbe.Position.Floor) { int yAngle = (long)((unsigned short)item->Pose.Orientation.y); - if (collResult.FloorTilt.x < 0) + if (pointProbe.FloorTilt.x < 0) { if (yAngle >= ANGLE(180.0f)) bs = 1; } - else if (collResult.FloorTilt.x > 0) + else if (pointProbe.FloorTilt.x > 0) { if (yAngle <= ANGLE(180.0f)) bs = 1; } - if (collResult.FloorTilt.y < 0) + if (pointProbe.FloorTilt.y < 0) { if (yAngle >= ANGLE(90.0f) && yAngle <= ANGLE(270.0f)) bs = 1; } - else if (collResult.FloorTilt.y > 0) + else if (pointProbe.FloorTilt.y > 0) { if (yAngle <= ANGLE(90.0f) || yAngle >= ANGLE(270.0f)) bs = 1; @@ -1238,7 +1232,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, // If last position of item was also below this floor height, we've hit a wall, else we've hit a floor. - if (y > (collResult.Position.Floor + 32) && bs == 0 && + if (y > (pointProbe.Position.Floor + 32) && bs == 0 && (((x / SECTOR(1)) != (item->Pose.Position.x / SECTOR(1))) || ((z / SECTOR(1)) != (item->Pose.Position.z / SECTOR(1))))) { @@ -1246,8 +1240,8 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, long xs; - if ((x & (~(WALL_SIZE - 1))) != (item->Pose.Position.x & (~(WALL_SIZE - 1))) && // X crossed boundary? - (z & (~(WALL_SIZE - 1))) != (item->Pose.Position.z & (~(WALL_SIZE - 1)))) // Z crossed boundary as well? + if ((x & (~WALL_MASK)) != (item->Pose.Position.x & (~WALL_MASK)) && // X crossed boundary? + (z & (~WALL_MASK)) != (item->Pose.Position.z & (~WALL_MASK))) // Z crossed boundary as well? { if (abs(x - item->Pose.Position.x) < abs(z - item->Pose.Position.z)) xs = 1; // X has travelled the shortest, so (maybe) hit first. (Seems to work ok). @@ -1257,7 +1251,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, else xs = 1; - if ((x & (~(WALL_SIZE - 1))) != (item->Pose.Position.x & (~(WALL_SIZE - 1))) && xs) // X crossed boundary? + if ((x & (~WALL_MASK)) != (item->Pose.Position.x & (~WALL_MASK)) && xs) // X crossed boundary? { // Hit angle = ANGLE(270.0f). if (xv <= 0) @@ -1278,14 +1272,14 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, item->Pose.Position.z = z; } // Hit a steep slope? - else if (collResult.Position.FloorSlope) + else if (pointProbe.Position.FloorSlope) { // Need to know which direction the slope is. item->Animation.Velocity.z -= (item->Animation.Velocity.z / 4); // Hit angle = ANGLE(90.0f) - if (collResult.FloorTilt.x < 0 && ((abs(collResult.FloorTilt.x)) - (abs(collResult.FloorTilt.y)) >= 2)) + if (pointProbe.FloorTilt.x < 0 && ((abs(pointProbe.FloorTilt.x)) - (abs(pointProbe.FloorTilt.y)) >= 2)) { if (((unsigned short)item->Pose.Orientation.y) > ANGLE(180.0f)) { @@ -1297,7 +1291,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, { if (item->Animation.Velocity.z < 32) { - item->Animation.Velocity.z -= collResult.FloorTilt.x * 2; + item->Animation.Velocity.z -= pointProbe.FloorTilt.x * 2; if ((unsigned short)item->Pose.Orientation.y > ANGLE(90.0f) && (unsigned short)item->Pose.Orientation.y < ANGLE(270.0f)) { item->Pose.Orientation.y -= ANGLE(22.5f); @@ -1319,7 +1313,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, } } // Hit angle = ANGLE(270.0f) - else if (collResult.FloorTilt.x > 0 && ((abs(collResult.FloorTilt.x)) - (abs(collResult.FloorTilt.y)) >= 2)) + else if (pointProbe.FloorTilt.x > 0 && ((abs(pointProbe.FloorTilt.x)) - (abs(pointProbe.FloorTilt.y)) >= 2)) { if (((unsigned short)item->Pose.Orientation.y) < ANGLE(180.0f)) { @@ -1331,7 +1325,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, { if (item->Animation.Velocity.z < 32) { - item->Animation.Velocity.z += collResult.FloorTilt.x * 2; + item->Animation.Velocity.z += pointProbe.FloorTilt.x * 2; if ((unsigned short)item->Pose.Orientation.y > ANGLE(270.0f) || (unsigned short)item->Pose.Orientation.y < ANGLE(90.0f)) { item->Pose.Orientation.y -= ANGLE(22.5f); @@ -1353,7 +1347,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, } } // Hit angle = 0 - else if (collResult.FloorTilt.y < 0 && ((abs(collResult.FloorTilt.y)) - (abs(collResult.FloorTilt.x)) >= 2)) + else if (pointProbe.FloorTilt.y < 0 && ((abs(pointProbe.FloorTilt.y)) - (abs(pointProbe.FloorTilt.x)) >= 2)) { if (((unsigned short)item->Pose.Orientation.y) > ANGLE(90.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(270.0f)) { @@ -1365,7 +1359,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, { if (item->Animation.Velocity.z < 32) { - item->Animation.Velocity.z -= collResult.FloorTilt.y * 2; + item->Animation.Velocity.z -= pointProbe.FloorTilt.y * 2; if ((unsigned short)item->Pose.Orientation.y < ANGLE(180.0f)) { @@ -1388,7 +1382,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, } } // Hit angle = ANGLE(180.0f) - else if (collResult.FloorTilt.y > 0 && ((abs(collResult.FloorTilt.y)) - (abs(collResult.FloorTilt.x)) >= 2)) + else if (pointProbe.FloorTilt.y > 0 && ((abs(pointProbe.FloorTilt.y)) - (abs(pointProbe.FloorTilt.x)) >= 2)) { if (((unsigned short)item->Pose.Orientation.y) > ANGLE(270.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(90.0f)) { @@ -1400,7 +1394,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, { if (item->Animation.Velocity.z < 32) { - item->Animation.Velocity.z += collResult.FloorTilt.y * 2; + item->Animation.Velocity.z += pointProbe.FloorTilt.y * 2; if ((unsigned short)item->Pose.Orientation.y > ANGLE(180.0f)) { @@ -1422,7 +1416,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, item->Animation.Velocity.y = 0; } } - else if (collResult.FloorTilt.x < 0 && collResult.FloorTilt.y < 0) // Hit angle = 0x2000 + else if (pointProbe.FloorTilt.x < 0 && pointProbe.FloorTilt.y < 0) // Hit angle = 0x2000 { if (((unsigned short)item->Pose.Orientation.y) > ANGLE(135.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(315.0f)) { @@ -1434,7 +1428,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, { if (item->Animation.Velocity.z < 32) { - item->Animation.Velocity.z += -collResult.FloorTilt.x + -collResult.FloorTilt.y; + item->Animation.Velocity.z += -pointProbe.FloorTilt.x + -pointProbe.FloorTilt.y; if ((unsigned short)item->Pose.Orientation.y > ANGLE(45.0f) && (unsigned short)item->Pose.Orientation.y < ANGLE(225.0f)) { item->Pose.Orientation.y -= ANGLE(22.5f); @@ -1456,7 +1450,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, } } // Hit angle = ANGLE(135.0f) - else if (collResult.FloorTilt.x < 0 && collResult.FloorTilt.y > 0) + else if (pointProbe.FloorTilt.x < 0 && pointProbe.FloorTilt.y > 0) { if (((unsigned short)item->Pose.Orientation.y) > ANGLE(225.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(45.0f)) { @@ -1468,7 +1462,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, { if (item->Animation.Velocity.z < 32) { - item->Animation.Velocity.z += (-collResult.FloorTilt.x) + collResult.FloorTilt.y; + item->Animation.Velocity.z += (-pointProbe.FloorTilt.x) + pointProbe.FloorTilt.y; if ((unsigned short)item->Pose.Orientation.y < ANGLE(315.0f) && (unsigned short)item->Pose.Orientation.y > ANGLE(135.0f)) { item->Pose.Orientation.y -= ANGLE(22.5f); @@ -1490,7 +1484,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, } } // Hit angle = ANGLE(225.5f) - else if (collResult.FloorTilt.x > 0 && collResult.FloorTilt.y > 0) + else if (pointProbe.FloorTilt.x > 0 && pointProbe.FloorTilt.y > 0) { if (((unsigned short)item->Pose.Orientation.y) > ANGLE(315.0f) || ((unsigned short)item->Pose.Orientation.y) < ANGLE(135.0f)) { @@ -1502,7 +1496,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, { if (item->Animation.Velocity.z < 32) { - item->Animation.Velocity.z += collResult.FloorTilt.x + collResult.FloorTilt.y; + item->Animation.Velocity.z += pointProbe.FloorTilt.x + pointProbe.FloorTilt.y; if ((unsigned short)item->Pose.Orientation.y < ANGLE(45.0f) || (unsigned short)item->Pose.Orientation.y > ANGLE(225.5f)) { item->Pose.Orientation.y -= ANGLE(22.5f); @@ -1524,7 +1518,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, } } // Hit angle = ANGLE(315.0f) - else if (collResult.FloorTilt.x > 0 && collResult.FloorTilt.y < 0) + else if (pointProbe.FloorTilt.x > 0 && pointProbe.FloorTilt.y < 0) { if (((unsigned short)item->Pose.Orientation.y) > ANGLE(45.0f) && ((unsigned short)item->Pose.Orientation.y) < ANGLE(225.5f)) { @@ -1536,7 +1530,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, { if (item->Animation.Velocity.z < 32) { - item->Animation.Velocity.z += collResult.FloorTilt.x + (-collResult.FloorTilt.y); + item->Animation.Velocity.z += pointProbe.FloorTilt.x + (-pointProbe.FloorTilt.y); if ((unsigned short)item->Pose.Orientation.y < ANGLE(135.0f) || (unsigned short)item->Pose.Orientation.y > ANGLE(315.0f)) { item->Pose.Orientation.y -= ANGLE(22.5f); @@ -1558,7 +1552,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, } } - // Put item back in its last position. + // Move item back to its previous position. item->Pose.Position.x = x; item->Pose.Position.y = y; item->Pose.Position.z = z; @@ -1597,7 +1591,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, } } - item->Pose.Position.y = collResult.Position.Floor; + item->Pose.Position.y = pointProbe.Position.Floor; } } // Check for on top of object. @@ -1605,8 +1599,8 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, { if (yv >= 0) { - prevCollResult = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber); - collResult = GetCollision(item); + prevPointProbe = GetCollision(item->Pose.Position.x, y, item->Pose.Position.z, item->RoomNumber); + pointProbe = GetCollision(item); // Bounce off floor. @@ -1614,7 +1608,7 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, // was always set to 0 by GetHeight() function which was called before the check. // Possibly a mistake or unfinished feature by Core? -- Lwmte, 27.08.21 - if (item->Pose.Position.y >= prevCollResult.Position.Floor) + if (item->Pose.Position.y >= prevPointProbe.Position.Floor) { // Hit the floor; bounce and slow down. if (item->Animation.Velocity.y > 0) @@ -1648,17 +1642,17 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, } } - item->Pose.Position.y = prevCollResult.Position.Floor; + item->Pose.Position.y = prevPointProbe.Position.Floor; } } // else { // Bounce off ceiling. - collResult = GetCollision(item); + pointProbe = GetCollision(item); - if (item->Pose.Position.y < collResult.Position.Ceiling) + if (item->Pose.Position.y < pointProbe.Position.Ceiling) { - if (y < collResult.Position.Ceiling && + if (y < pointProbe.Position.Ceiling && (((x / SECTOR(1)) != (item->Pose.Position.x / SECTOR(1))) || ((z / SECTOR(1)) != (item->Pose.Position.z / SECTOR(1))))) { @@ -1681,13 +1675,13 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, else item->Animation.Velocity.z /= 2; - // Put item back in its last position. + // Move item back to its previous position. item->Pose.Position.x = x; item->Pose.Position.y = y; item->Pose.Position.z = z; } else - item->Pose.Position.y = collResult.Position.Ceiling; + item->Pose.Position.y = pointProbe.Position.Ceiling; if (item->Animation.Velocity.y < 0) item->Animation.Velocity.y = -item->Animation.Velocity.y; @@ -1695,14 +1689,14 @@ void DoProjectileDynamics(short itemNumber, int x, int y, int z, int xv, int yv, } } - collResult = GetCollision(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber); + pointProbe = GetCollision(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber); - if (collResult.RoomNumber != item->RoomNumber) + if (pointProbe.RoomNumber != item->RoomNumber) { - if (item->ObjectNumber == ID_GRENADE && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, collResult.RoomNumber)) + if (item->ObjectNumber == ID_GRENADE && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, pointProbe.RoomNumber)) Splash(item); - ItemNewRoom(itemNumber, collResult.RoomNumber); + ItemNewRoom(itemNumber, pointProbe.RoomNumber); } item->Pose.Position.y -= radius; @@ -1791,8 +1785,8 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll) else { DoDamage(item, INT_MAX); - - DoLotsOfBlood(item->Pose.Position.x, + DoLotsOfBlood( + item->Pose.Position.x, laraItem->Pose.Position.y - CLICK(1), item->Pose.Position.z, laraItem->Animation.Velocity.z, @@ -1832,10 +1826,6 @@ void DoObjectCollision(ItemInfo* laraItem, CollisionInfo* coll) { SoundEffect(GetShatterSound(mesh->staticNumber), (PHD_3DPOS*)mesh); ShatterObject(nullptr, mesh, -128, laraItem->RoomNumber, 0); - SmashedMeshRoom[SmashedMeshCount] = laraItem->RoomNumber; - SmashedMesh[SmashedMeshCount] = mesh; - SmashedMeshCount++; - mesh->flags &= ~StaticMeshFlags::SM_VISIBLE; } else if (coll->Setup.EnableObjectPush) ItemPushStatic(laraItem, mesh, coll); @@ -1901,7 +1891,7 @@ void CreatureCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll int rx = (frame->boundingBox.X1 + frame->boundingBox.X2) / 2; int rz = (frame->boundingBox.X2 + frame->boundingBox.Z2) / 2; - if (frame->boundingBox.Height() > STEP_SIZE) + if (frame->boundingBox.Height() > CLICK(1)) { auto* lara = GetLaraInfo(laraItem); @@ -1909,7 +1899,7 @@ void CreatureCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll lara->HitDirection = (short)angle; - // TODO: check if a second Lara.hitFrame++; is required there ! + // TODO: Check if a second Lara.hitFrame++ is required. -- TokyoSU lara->HitFrame++; if (lara->HitFrame > 30) lara->HitFrame = 30; diff --git a/TombEngine/Game/collision/collide_item.h b/TombEngine/Game/collision/collide_item.h index 55ce3b6ac..1c7545b37 100644 --- a/TombEngine/Game/collision/collide_item.h +++ b/TombEngine/Game/collision/collide_item.h @@ -2,15 +2,15 @@ #include "Specific/phd_global.h" #include "Specific/trmath.h" -struct ItemInfo; -struct CollisionInfo; class FloorInfo; +struct CollisionInfo; +struct ItemInfo; struct MESH_INFO; constexpr auto MAX_COLLIDED_OBJECTS = 1024; constexpr auto ITEM_RADIUS_YMAX = SECTOR(3); -constexpr auto VEHICLE_COLLISION_TERMINAL_VELOCITY = 30; +constexpr auto VEHICLE_COLLISION_TERMINAL_VELOCITY = 30.0f; extern BOUNDING_BOX GlobalCollisionBounds; extern ItemInfo* CollidedItems[MAX_COLLIDED_OBJECTS]; @@ -36,17 +36,17 @@ bool TestLaraPosition(OBJECT_COLLISION_BOUNDS* bounds, ItemInfo* item, ItemInfo* bool AlignLaraPosition(Vector3Int* offset, ItemInfo* item, ItemInfo* laraItem); bool MoveLaraPosition(Vector3Int* pos, ItemInfo* item, ItemInfo* laraItem); -bool ItemNearLara(PHD_3DPOS* pos, int radius); -bool ItemNearTarget(PHD_3DPOS* origin, ItemInfo* target, int radius); +bool ItemNearLara(Vector3Int* origin, int radius); +bool ItemNearTarget(Vector3Int* origin, ItemInfo* targetEntity, int radius); -bool Move3DPosTo3DPos(PHD_3DPOS* origin, PHD_3DPOS* target, int velocity, short angleAdd); +bool Move3DPosTo3DPos(PHD_3DPOS* fromPose, PHD_3DPOS* toPose, int velocity, short angleAdd); bool TestBoundsCollide(ItemInfo* item, ItemInfo* laraItem, int radius); bool TestBoundsCollideStatic(ItemInfo* item, MESH_INFO* mesh, int radius); bool ItemPushItem(ItemInfo* item, ItemInfo* laraItem, CollisionInfo* coll, bool spasmEnabled, char bigPush); bool ItemPushStatic(ItemInfo* laraItem, MESH_INFO* mesh, CollisionInfo* coll); -bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pos, CollisionInfo* coll); +bool CollideSolidBounds(ItemInfo* item, BOUNDING_BOX* box, PHD_3DPOS pose, CollisionInfo* coll); void CollideSolidStatics(ItemInfo* item, CollisionInfo* coll); void AIPickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); diff --git a/TombEngine/Game/collision/sphere.cpp b/TombEngine/Game/collision/sphere.cpp index 017cd77ad..77dd40087 100644 --- a/TombEngine/Game/collision/sphere.cpp +++ b/TombEngine/Game/collision/sphere.cpp @@ -78,7 +78,7 @@ int TestCollision(ItemInfo* item, ItemInfo* laraItem) int dz = z1 - z2; int r = r1 + r2; - if ((pow(dx, 2) + pow(dy, 2) + pow(dz, 2)) < pow(r, 2)) + if ((SQUARE(dx) + SQUARE(dy) + SQUARE(dz)) < SQUARE(r)) { item->SetBits(JointBitType::Touch, i); laraItem->SetBits(JointBitType::Touch, j); diff --git a/TombEngine/Game/control/box.cpp b/TombEngine/Game/control/box.cpp index b2042eb80..5b79c749b 100644 --- a/TombEngine/Game/control/box.cpp +++ b/TombEngine/Game/control/box.cpp @@ -92,7 +92,7 @@ void DrawNearbyPathfinding(int boxIndex) void DropEntityPickups(ItemInfo* item) { - ItemInfo* pickup = NULL; + ItemInfo* pickup = nullptr; for (short pickupNumber = item->CarriedItem; pickupNumber != NO_ITEM; pickupNumber = pickup->CarriedItem) { @@ -157,7 +157,7 @@ void CreatureYRot2(PHD_3DPOS* srcPos, short angle, short angleAdd) bool SameZone(CreatureInfo* creature, ItemInfo* target) { - int* zone = g_Level.Zones[creature->LOT.Zone][FlipStatus].data(); + int* zone = g_Level.Zones[(int)creature->LOT.Zone][FlipStatus].data(); auto* item = &g_Level.Items[creature->ItemNumber]; auto* room = &g_Level.Rooms[item->RoomNumber]; @@ -449,12 +449,13 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) short top; auto* item = &g_Level.Items[itemNumber]; - if (!item->Data) + + if (!item->IsCreature()) return false; auto* creature = GetCreatureInfo(item); auto* LOT = &creature->LOT; - int* zone = g_Level.Zones[LOT->Zone][FlipStatus].data(); + int* zone = g_Level.Zones[(int)LOT->Zone][FlipStatus].data(); int boxHeight; if (item->BoxNumber != NO_BOX) @@ -462,7 +463,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) else boxHeight = item->Floor; - auto oldPos = item->Pose.Position; + auto prevPos = item->Pose.Position; AnimateItem(item); @@ -477,7 +478,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) int y = item->Pose.Position.y + bounds->Y1; short roomNumber = item->RoomNumber; - GetFloor(oldPos.x, y, oldPos.z, &roomNumber); + GetFloor(prevPos.x, y, prevPos.z, &roomNumber); FloorInfo* floor = GetFloor(item->Pose.Position.x, y, item->Pose.Position.z, &roomNumber); // TODO: Check why some blocks have box = -1 assigned to them -- Lwmte, 10.11.21 @@ -506,18 +507,18 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) { xPos = item->Pose.Position.x / SECTOR(1); zPos = item->Pose.Position.z / SECTOR(1); - shiftX = oldPos.x / SECTOR(1); - shiftZ = oldPos.z / SECTOR(1); + shiftX = prevPos.x / SECTOR(1); + shiftZ = prevPos.z / SECTOR(1); if (xPos < shiftX) - item->Pose.Position.x = oldPos.x & (~(SECTOR(1) - 1)); + item->Pose.Position.x = prevPos.x & (~WALL_MASK); else if (xPos > shiftX) - item->Pose.Position.x = oldPos.x | (SECTOR(1) - 1); + item->Pose.Position.x = prevPos.x | WALL_MASK; if (zPos < shiftZ) - item->Pose.Position.z = oldPos.z & (~(SECTOR(1) - 1)); + item->Pose.Position.z = prevPos.z & (~WALL_MASK); else if (zPos > shiftZ) - item->Pose.Position.z = oldPos.z | (SECTOR(1) - 1); + item->Pose.Position.z = prevPos.z | (WALL_MASK); floor = GetFloor(item->Pose.Position.x, y, item->Pose.Position.z, &roomNumber); height = g_Level.Boxes[floor->Box].height; @@ -538,8 +539,8 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) int x = item->Pose.Position.x; int z = item->Pose.Position.z; - xPos = x & (SECTOR(1) - 1); - zPos = z & (SECTOR(1) - 1); + xPos = x & WALL_MASK; + zPos = z & WALL_MASK; short radius = Objects[item->ObjectNumber].radius; shiftX = 0; shiftZ = 0; @@ -669,8 +670,8 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) { if (item->Pose.Position.y + top < ceiling) { - item->Pose.Position.x = oldPos.x; - item->Pose.Position.z = oldPos.z; + item->Pose.Position.x = prevPos.x; + item->Pose.Position.z = prevPos.z; dy = LOT->Fly; } else @@ -689,13 +690,13 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) } else if (item->Pose.Position.y <= height) { - dy = 0; item->Pose.Position.y = height; + dy = 0; } else { - item->Pose.Position.x = oldPos.x; - item->Pose.Position.z = oldPos.z; + item->Pose.Position.x = prevPos.x; + item->Pose.Position.z = prevPos.z; dy = -LOT->Fly; } @@ -732,7 +733,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) if (item->Pose.Position.y > item->Floor) { if (item->Pose.Position.y > (item->Floor + CLICK(1))) - item->Pose.Position = oldPos; + item->Pose.Position = prevPos; else item->Pose.Position.y = item->Floor; } @@ -749,7 +750,7 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) top = bounds->Y1; // TODO: check if Y1 or Y2 if (item->Pose.Position.y + top < ceiling) - item->Pose.Position = oldPos; + item->Pose.Position = prevPos; floor = GetFloor(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, &roomNumber); item->Floor = GetFloorHeight(floor, item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z); @@ -765,7 +766,6 @@ int CreatureAnimation(short itemNumber, short angle, short tilt) } CreatureSwitchRoom(itemNumber); - return true; } @@ -884,7 +884,7 @@ int ValidBox(ItemInfo* item, short zoneNumber, short boxNumber) return false; auto* creature = GetCreatureInfo(item); - int* zone = g_Level.Zones[creature->LOT.Zone][FlipStatus].data(); + int* zone = g_Level.Zones[(int)creature->LOT.Zone][FlipStatus].data(); if (creature->LOT.Fly == NO_FLYING && zone[boxNumber] != zoneNumber) return false; @@ -970,7 +970,7 @@ int UpdateLOT(LOTInfo* LOT, int depth) int SearchLOT(LOTInfo* LOT, int depth) { - int* zone = g_Level.Zones[LOT->Zone][FlipStatus].data(); + int* zone = g_Level.Zones[(int)LOT->Zone][FlipStatus].data(); int searchZone = zone[LOT->Head]; if (depth <= 0) @@ -1084,7 +1084,7 @@ int CreatureActive(short itemNumber) if (item->Flags & IFLAG_KILLED) return false; // Object is already dead - if (item->Status == ITEM_INVISIBLE || !item->Data.is()) + if (item->Status == ITEM_INVISIBLE || !item->IsCreature()) { if (!EnableEntityAI(itemNumber, 0)) return false; // AI couldn't be activated @@ -1162,7 +1162,7 @@ int CreatureVault(short itemNumber, short angle, int vault, int shift) vault = 0; else if (item->Floor > y + CHECK_CLICK(7)) vault = -4; - // FIXME: edit assets adding climb down animations for Von Croy and baddys? + // FIXME: edit assets adding climb down animations for Von Croy and baddies? else if (item->Floor > y + CHECK_CLICK(5) && item->ObjectNumber != ID_VON_CROY && item->ObjectNumber != ID_BADDY1 && @@ -1367,7 +1367,7 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber) if (g_Level.AIObjects.size() > 0) { - AI_OBJECT* foundObject = NULL; + AI_OBJECT* foundObject = nullptr; for (int i = 0; i < g_Level.AIObjects.size(); i++) { @@ -1375,7 +1375,7 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber) if (aiObject->objectNumber == objectNumber && aiObject->triggerFlags == item->ItemFlags[3] && aiObject->roomNumber != NO_ROOM) { - int* zone = g_Level.Zones[creature->LOT.Zone][FlipStatus].data(); + int* zone = g_Level.Zones[(int)creature->LOT.Zone][FlipStatus].data(); auto* room = &g_Level.Rooms[item->RoomNumber]; item->BoxNumber = GetSector(room, item->Pose.Position.x - room->x, item->Pose.Position.z - room->z)->Box; @@ -1394,7 +1394,7 @@ void FindAITargetObject(CreatureInfo* creature, short objectNumber) } } - if (foundObject != NULL) + if (foundObject != nullptr) { auto* aiItem = creature->AITarget; @@ -1435,7 +1435,7 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI) creature->Enemy = LaraItem; } - int* zone = g_Level.Zones[creature->LOT.Zone][FlipStatus].data(); + int* zone = g_Level.Zones[(int)creature->LOT.Zone][FlipStatus].data(); auto* room = &g_Level.Rooms[item->RoomNumber]; item->BoxNumber = NO_BOX; @@ -1457,8 +1457,8 @@ void CreatureAIInfo(ItemInfo* item, AI_INFO* AI) // This prevents enemies from running to Lara and attacking nothing when she is hanging or shimmying. -- Lwmte, 27.06.22 bool reachable = false; - if (object->zoneType == ZoneType::ZONE_FLYER || - (object->zoneType == ZoneType::ZONE_WATER && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, item->RoomNumber))) + if (object->ZoneType == ZoneType::Flyer || + (object->ZoneType == ZoneType::Water && TestEnvironment(RoomEnvFlags::ENV_FLAG_WATER, item->RoomNumber))) { reachable = true; // If NPC is flying or swimming in water, always reach Lara } diff --git a/TombEngine/Game/control/control.cpp b/TombEngine/Game/control/control.cpp index 68d0fd2d2..b962313aa 100644 --- a/TombEngine/Game/control/control.cpp +++ b/TombEngine/Game/control/control.cpp @@ -444,9 +444,8 @@ GameStatus DoTitle(int index, std::string const& ambient) g_Gui.SetMenuToDisplay(Menu::Title); g_Gui.SetSelectedOption(0); - // Initialise ponytails InitialiseHair(); - + InitialiseNodeScripts(); InitialiseItemBoxData(); g_GameScript->OnStart(); @@ -570,9 +569,8 @@ GameStatus DoLevel(int index, std::string const& ambient, bool loadFromSavegame) // Initialise flyby cameras InitSpotCamSequences(); - // Initialise ponytails InitialiseHair(); - + InitialiseNodeScripts(); InitialiseItemBoxData(); if (loadFromSavegame) diff --git a/TombEngine/Game/control/los.cpp b/TombEngine/Game/control/los.cpp index 5a4db8b71..76a221612 100644 --- a/TombEngine/Game/control/los.cpp +++ b/TombEngine/Game/control/los.cpp @@ -121,10 +121,6 @@ bool GetTargetOnLOS(GameVector* origin, GameVector* target, bool drawTarget, boo ShatterImpactData.impactDirection = direction; ShatterImpactData.impactLocation = Vector3(mesh->pos.Position.x, mesh->pos.Position.y, mesh->pos.Position.z); ShatterObject(nullptr, mesh, 128, target2.roomNumber, 0); - SmashedMeshRoom[SmashedMeshCount] = target2.roomNumber; - SmashedMesh[SmashedMeshCount] = mesh; - ++SmashedMeshCount; - mesh->flags &= ~StaticMeshFlags::SM_VISIBLE; SoundEffect(GetShatterSound(mesh->staticNumber), (PHD_3DPOS*)mesh); } diff --git a/TombEngine/Game/control/lot.cpp b/TombEngine/Game/control/lot.cpp index 712558575..83cf70a2d 100644 --- a/TombEngine/Game/control/lot.cpp +++ b/TombEngine/Game/control/lot.cpp @@ -112,15 +112,15 @@ void DisableEntityAI(short itemNumber) item->Data = nullptr; } -void InitialiseSlot(short itemNum, short slot, bool makeTarget) +void InitialiseSlot(short itemNumber, short slot, bool makeTarget) { - auto* item = &g_Level.Items[itemNum]; - auto* obj = &Objects[item->ObjectNumber]; - + auto* item = &g_Level.Items[itemNumber]; + auto* object = &Objects[item->ObjectNumber]; item->Data = CreatureInfo(); auto* creature = GetCreatureInfo(item); - InitialiseLOTarray(itemNum); - creature->ItemNumber = itemNum; + + InitialiseLOTarray(itemNumber); + creature->ItemNumber = itemNumber; creature->Mood = MoodType::Bored; creature->JointRotation[0] = 0; creature->JointRotation[1] = 0; @@ -136,12 +136,12 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget) creature->MonkeySwingAhead = false; creature->LOT.CanJump = false; creature->LOT.CanMonkey = false; - creature->LOT.IsAmphibious = false; // only the crocodile can go water and land. (default: true) + creature->LOT.IsAmphibious = false; // True for crocodile by default as the only the crocodile that can move in water and on land. creature->LOT.IsJumping = false; creature->LOT.IsMonkeying = false; creature->MaxTurn = ANGLE(1); creature->Flags = 0; - creature->Enemy = NULL; + creature->Enemy = nullptr; creature->LOT.Fly = NO_FLYING; creature->LOT.BlockMask = BLOCKED; @@ -155,117 +155,116 @@ void InitialiseSlot(short itemNum, short slot, bool makeTarget) else creature->AITarget = nullptr; - switch (obj->zoneType) + switch (object->ZoneType) { default: - case ZONE_NULL: + case ZoneType::None: creature->LOT.Step = CLICK(1); creature->LOT.Drop = -CLICK(1); - obj->zoneType = ZONE_BASIC; // only entity that use CreatureActive() will reach InitialiseSlot() ! + object->ZoneType = ZoneType::Basic; // Only entities that use CreatureActive() will reach InitialiseSlot(). break; - case ZONE_SKELLY: - // Can jump + // Can jump. + case ZoneType::Skeleton: creature->LOT.Step = CLICK(1); creature->LOT.Drop = -CLICK(1); creature->LOT.CanJump = true; - creature->LOT.Zone = ZONE_SKELLY; + creature->LOT.Zone = ZoneType::Skeleton; break; - case ZONE_BASIC: + case ZoneType::Basic: creature->LOT.Step = CLICK(1); creature->LOT.Drop = -CLICK(1); - creature->LOT.Zone = ZONE_BASIC; + creature->LOT.Zone = ZoneType::Basic; break; - case ZONE_FLYER: - // Can fly + // Can fly. + case ZoneType::Flyer: creature->LOT.Step = SECTOR(20); creature->LOT.Drop = -SECTOR(20); creature->LOT.Fly = DEFAULT_FLY_UPDOWN_SPEED; - creature->LOT.Zone = ZONE_FLYER; + creature->LOT.Zone = ZoneType::Flyer; break; - case ZONE_WATER: - // Can swim + // Can swim. + case ZoneType::Water: creature->LOT.Step = SECTOR(20); creature->LOT.Drop = -SECTOR(20); - creature->LOT.Zone = ZONE_WATER; + creature->LOT.Zone = ZoneType::Water; if (item->ObjectNumber == ID_CROCODILE) { - creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED / 2; // is more slow than the other underwater entity - creature->LOT.IsAmphibious = true; // crocodile can walk and swim. + creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED / 2; // Slower than the other underwater creatures. + creature->LOT.IsAmphibious = true; // Can walk and swim. } else if (item->ObjectNumber == ID_BIG_RAT) { - creature->LOT.Fly = NO_FLYING; // dont want the bigrat to be able to go in water (just the surface !) - creature->LOT.IsAmphibious = true; // bigrat can walk and swim. + creature->LOT.Fly = NO_FLYING; // Can't swim underwater, only on the surface. + creature->LOT.IsAmphibious = true; // Can walk and swim. } else - { creature->LOT.Fly = DEFAULT_SWIM_UPDOWN_SPEED; - } + break; - case ZONE_HUMAN_CLASSIC: - // Can climb + // Can climb. + case ZoneType::HumanClassic: creature->LOT.Step = SECTOR(1); creature->LOT.Drop = -SECTOR(1); - creature->LOT.Zone = ZONE_HUMAN_CLASSIC; + creature->LOT.Zone = ZoneType::HumanClassic; break; - case ZONE_HUMAN_JUMP: - // Can climb and jump + // Can climb and jump. + case ZoneType::HumanJump: creature->LOT.Step = SECTOR(1); creature->LOT.Drop = -SECTOR(1); creature->LOT.CanJump = true; - creature->LOT.Zone = ZONE_HUMAN_CLASSIC; + creature->LOT.Zone = ZoneType::HumanClassic; break; - case ZONE_HUMAN_JUMP_AND_MONKEY: - // Can climb, jump, monkey + // Can climb, jump, monkeyswing. + case ZoneType::HumanJumpAndMonkey: creature->LOT.Step = SECTOR(1); creature->LOT.Drop = -SECTOR(1); creature->LOT.CanJump = true; creature->LOT.CanMonkey = true; - creature->LOT.Zone = ZONE_HUMAN_CLASSIC; + creature->LOT.Zone = ZoneType::HumanClassic; break; - case ZONE_HUMAN_LONGJUMP_AND_MONKEY: - // Can climb, jump, monkey, long jump + // Can climb, jump, monkey swing, long jump. + case ZoneType::HumanLongJumpAndMonkey: creature->LOT.Step = SECTOR(1) + CLICK(3); creature->LOT.Drop = -(SECTOR(1) + CLICK(3)); creature->LOT.CanJump = true; creature->LOT.CanMonkey = true; - creature->LOT.Zone = ZONE_VON_CROY; + creature->LOT.Zone = ZoneType::VonCroy; break; - case ZONE_SPIDER: + case ZoneType::Spider: creature->LOT.Step = SECTOR(1) - CLICK(2); creature->LOT.Drop = -(SECTOR(1) - CLICK(2)); - creature->LOT.Zone = ZONE_HUMAN_CLASSIC; + creature->LOT.Zone = ZoneType::HumanClassic; break; - case ZONE_BLOCKABLE: + case ZoneType::Blockable: creature->LOT.BlockMask = BLOCKABLE; - creature->LOT.Zone = ZONE_BASIC; + creature->LOT.Zone = ZoneType::Basic; break; - case ZONE_APE: + case ZoneType::Ape: creature->LOT.Step = CLICK(2); creature->LOT.Drop = -SECTOR(1); break; - case ZONE_SOPHIALEE: + case ZoneType::SophiaLee: creature->LOT.Step = SECTOR(1); creature->LOT.Drop = -CLICK(3); - creature->LOT.Zone = ZONE_HUMAN_CLASSIC; + creature->LOT.Zone = ZoneType::HumanClassic; break; } ClearLOT(&creature->LOT); - if (itemNum != Lara.ItemNumber) + if (itemNumber != Lara.ItemNumber) CreateZone(item); SlotsUsed++; @@ -304,16 +303,16 @@ void ClearLOT(LOTInfo* LOT) void CreateZone(ItemInfo* item) { auto* creature = GetCreatureInfo(item); - auto* r = &g_Level.Rooms[item->RoomNumber]; + auto* room = &g_Level.Rooms[item->RoomNumber]; - item->BoxNumber = GetSector(r, item->Pose.Position.x - r->x, item->Pose.Position.z - r->z)->Box; + item->BoxNumber = GetSector(room, item->Pose.Position.x - room->x, item->Pose.Position.z - room->z)->Box; if (creature->LOT.Fly) { - BOX_NODE* node = creature->LOT.Node.data(); + auto* node = creature->LOT.Node.data(); creature->LOT.ZoneCount = 0; - for (int i = 0; i < g_Level.Boxes.size(); i++) + for (size_t i = 0; i < g_Level.Boxes.size(); i++) { node->boxNumber = i; node++; @@ -322,8 +321,8 @@ void CreateZone(ItemInfo* item) } else { - int* zone = g_Level.Zones[creature->LOT.Zone][0].data(); - int* flippedZone = g_Level.Zones[creature->LOT.Zone][1].data(); + int* zone = g_Level.Zones[(int)creature->LOT.Zone][0].data(); + int* flippedZone = g_Level.Zones[(int)creature->LOT.Zone][1].data(); int zoneNumber = zone[item->BoxNumber]; int flippedZoneNumber = flippedZone[item->BoxNumber]; @@ -331,7 +330,7 @@ void CreateZone(ItemInfo* item) auto* node = creature->LOT.Node.data(); creature->LOT.ZoneCount = 0; - for (int i = 0; i < g_Level.Boxes.size(); i++) + for (size_t i = 0; i < g_Level.Boxes.size(); i++) { if (*zone == zoneNumber || *flippedZone == flippedZoneNumber) { diff --git a/TombEngine/Game/control/trigger.cpp b/TombEngine/Game/control/trigger.cpp index 727409ec9..ec63353d7 100644 --- a/TombEngine/Game/control/trigger.cpp +++ b/TombEngine/Game/control/trigger.cpp @@ -56,6 +56,7 @@ int TriggerActive(ItemInfo* item) flag = !flag; } } + return flag; } diff --git a/TombEngine/Game/control/volume.cpp b/TombEngine/Game/control/volume.cpp index 0f0b651b4..2a0cf73ca 100644 --- a/TombEngine/Game/control/volume.cpp +++ b/TombEngine/Game/control/volume.cpp @@ -1,6 +1,7 @@ #include "framework.h" #include "Game/control/volume.h" +#include #include "Game/animation.h" #include "Game/items.h" #include "Game/Lara/lara.h" @@ -79,7 +80,7 @@ namespace TEN::Control::Volumes volume->Status = TriggerStatus::Entering; if (!set->OnEnter.Function.empty() && set->OnEnter.CallCounter != 0) { - g_GameScript->ExecuteFunction(set->OnEnter.Function, triggerer, set->OnEnter.Argument); + g_GameScript->ExecuteFunction(set->OnEnter.Function, triggerer, set->OnEnter.Data); if (set->OnEnter.CallCounter != NO_CALL_COUNTER) set->OnEnter.CallCounter--; } @@ -89,7 +90,7 @@ namespace TEN::Control::Volumes volume->Status = TriggerStatus::Inside; if (!set->OnInside.Function.empty() && set->OnInside.CallCounter != 0) { - g_GameScript->ExecuteFunction(set->OnInside.Function, triggerer, set->OnInside.Argument); + g_GameScript->ExecuteFunction(set->OnInside.Function, triggerer, set->OnInside.Data); if (set->OnInside.CallCounter != NO_CALL_COUNTER) set->OnInside.CallCounter--; } @@ -108,7 +109,7 @@ namespace TEN::Control::Volumes volume->Status = TriggerStatus::Leaving; if (!set->OnLeave.Function.empty() && set->OnLeave.CallCounter != 0) { - g_GameScript->ExecuteFunction(set->OnLeave.Function, triggerer, set->OnLeave.Argument); + g_GameScript->ExecuteFunction(set->OnLeave.Function, triggerer, set->OnLeave.Data); if (set->OnLeave.CallCounter != NO_CALL_COUNTER) set->OnLeave.CallCounter--; } @@ -155,4 +156,53 @@ namespace TEN::Control::Volumes else TestVolumes(item->RoomNumber, bbox, TriggerVolumeActivators::Movable, itemNumber); } + + void InitialiseNodeScripts() + { + static const std::string nodeScriptPath = "Scripts/Engine/NodeCatalogs/"; + + if (!std::filesystem::exists(nodeScriptPath)) + return; + + std::vector nodeCatalogs; + for (auto& path : std::filesystem::recursive_directory_iterator(nodeScriptPath)) + if (path.path().extension() == ".lua") + nodeCatalogs.push_back(path.path().filename().string()); + + if (nodeCatalogs.size() == 0) + return; + + TENLog("Loading node scripts...", LogLevel::Info); + + std::sort(nodeCatalogs.rbegin(), nodeCatalogs.rend()); + for (auto& file : nodeCatalogs) + g_GameScript->ExecuteScriptFile(nodeScriptPath + file); + + TENLog(std::to_string(nodeCatalogs.size()) + " node catalogs were found and loaded.", LogLevel::Info); + + int nodeCount = 0; + for (auto& set : g_Level.EventSets) + { + if ((set.OnEnter.Mode == VolumeEventMode::Nodes) && (set.OnEnter.Data.size() > 0)) + { + g_GameScript->ExecuteString(set.OnEnter.Data); + nodeCount++; + } + + if ((set.OnInside.Mode == VolumeEventMode::Nodes) && (set.OnInside.Data.size() > 0)) + { + g_GameScript->ExecuteString(set.OnInside.Data); + nodeCount++; + } + + if ((set.OnLeave.Mode == VolumeEventMode::Nodes) && (set.OnLeave.Data.size() > 0)) + { + g_GameScript->ExecuteString(set.OnLeave.Data); + nodeCount++; + } + } + + if (nodeCount != 0) + TENLog(std::to_string(nodeCount) + " node scripts were found and loaded.", LogLevel::Info); + } } diff --git a/TombEngine/Game/control/volume.h b/TombEngine/Game/control/volume.h index c56c2c364..5cabe9baf 100644 --- a/TombEngine/Game/control/volume.h +++ b/TombEngine/Game/control/volume.h @@ -61,4 +61,6 @@ namespace TEN::Control::Volumes void TestVolumes(short itemNum); void TestVolumes(short roomNumber, MESH_INFO* mesh); void TestVolumes(CAMERA_INFO* camera); + + void InitialiseNodeScripts(); } diff --git a/TombEngine/Game/control/volumetriggerer.h b/TombEngine/Game/control/volumetriggerer.h index a88abe2f8..1050bc801 100644 --- a/TombEngine/Game/control/volumetriggerer.h +++ b/TombEngine/Game/control/volumetriggerer.h @@ -15,14 +15,14 @@ namespace TEN::Control::Volumes enum class VolumeEventMode { LevelScript, - Constructor + Nodes }; struct VolumeEvent { VolumeEventMode Mode; std::string Function; - std::string Argument; + std::string Data; int CallCounter; }; diff --git a/TombEngine/Game/effects/debris.cpp b/TombEngine/Game/effects/debris.cpp index d7409941b..4b3319063 100644 --- a/TombEngine/Game/effects/debris.cpp +++ b/TombEngine/Game/effects/debris.cpp @@ -69,11 +69,19 @@ void ShatterObject(SHATTER_ITEM* item, MESH_INFO* mesh, int num, short roomNumbe if (mesh) { + if (!(mesh->flags & StaticMeshFlags::SM_VISIBLE)) + return; + isStatic = true; meshIndex = StaticObjects[mesh->staticNumber].meshNumber; yRot = mesh->pos.Orientation.y; pos = Vector3(mesh->pos.Position.x, mesh->pos.Position.y, mesh->pos.Position.z); scale = mesh->scale; + + mesh->flags &= ~StaticMeshFlags::SM_VISIBLE; + SmashedMeshRoom[SmashedMeshCount] = roomNumber; + SmashedMesh[SmashedMeshCount] = mesh; + SmashedMeshCount++; } else { diff --git a/TombEngine/Game/gui.cpp b/TombEngine/Game/gui.cpp index 7eaa1bd2b..f5f0ec7a2 100644 --- a/TombEngine/Game/gui.cpp +++ b/TombEngine/Game/gui.cpp @@ -1495,7 +1495,7 @@ void GuiController::SetupAmmoSelector() { num++; ammo_object_list[0].invitem = INV_OBJECT_PISTOLS_AMMO; - ammo_object_list[0].amount = -1; + ammo_object_list[0].amount = AmountPistolsAmmo; num_ammo_slots = num; current_ammo_type = &CurrentPistolsAmmoType; } diff --git a/TombEngine/Game/health.cpp b/TombEngine/Game/health.cpp index a106b100d..c955d436a 100644 --- a/TombEngine/Game/health.cpp +++ b/TombEngine/Game/health.cpp @@ -265,7 +265,7 @@ void AddDisplayPickup(GAME_OBJECT_ID objectNumber) } // No free slot found; pickup the object without displaying it. - PickedUpObject(objectNumber, 0); + PickedUpObject(objectNumber); } void InitialisePickupDisplay() diff --git a/TombEngine/Game/itemdata/creature_info.h b/TombEngine/Game/itemdata/creature_info.h index 85cf640ff..fd8c710e7 100644 --- a/TombEngine/Game/itemdata/creature_info.h +++ b/TombEngine/Game/itemdata/creature_info.h @@ -1,7 +1,8 @@ #pragma once -#include #include "Specific/phd_global.h" +using std::vector; + struct ItemInfo; struct BOX_NODE @@ -12,34 +13,37 @@ struct BOX_NODE int boxNumber; }; -enum ZoneType : char +enum class ZoneType { - ZONE_NULL = -1, // default zone - ZONE_SKELLY = 0, - ZONE_BASIC, - ZONE_FLYER, - ZONE_HUMAN_CLASSIC, - ZONE_VON_CROY, - ZONE_WATER, - ZONE_MAX, - /// custom zone (using zone above for LOT.zone): - ZONE_HUMAN_JUMP_AND_MONKEY, - ZONE_HUMAN_JUMP, - ZONE_SPIDER, - ZONE_BLOCKABLE, // for trex, shiva, etc.. - ZONE_SOPHIALEE, // dont want sophia to go down again ! - ZONE_APE, // only 2 click climb - ZONE_HUMAN_LONGJUMP_AND_MONKEY, + None = -1, + Skeleton, + Basic, + Flyer, + HumanClassic, + VonCroy, + Water, + Max, + + // Custom zones (above zones are used for LOT.zone): + HumanJumpAndMonkey, + HumanJump, + Spider, + Blockable, // For large creatures such as trex and shiva. + SophiaLee, // Prevents Sophia from going to lower levels again. + Ape, // Only 0.5 block climb. + HumanLongJumpAndMonkey, }; struct LOTInfo { bool Initialised; - std::vector Node; + vector Node; int Head; int Tail; + ZoneType Zone = ZoneType::None; + Vector3Int Target = Vector3Int::Zero; int SearchNumber; int BlockMask; short Step; @@ -49,18 +53,16 @@ struct LOTInfo int RequiredBox; short Fly; - bool CanJump; - bool CanMonkey; - bool IsJumping; - bool IsMonkeying; - bool IsAmphibious; - - Vector3Int Target; - ZoneType Zone; + bool CanJump = false; + bool CanMonkey = false; + bool IsJumping = false; + bool IsMonkeying = false; + bool IsAmphibious = false; }; enum class MoodType { + None, Bored, Attack, Escape, @@ -70,45 +72,43 @@ enum class MoodType enum class CreatureAIPriority { None, - High, + Low, Medium, - Low + High }; struct CreatureInfo { - short ItemNumber; + short ItemNumber = -1; - short MaxTurn; - short JointRotation[4]; - bool HeadLeft; - bool HeadRight; + LOTInfo LOT = {}; + MoodType Mood = MoodType::None; + ItemInfo* Enemy = nullptr; + ItemInfo* AITarget = nullptr; + short AITargetNumber = -1; + Vector3Int Target = Vector3Int::Zero; - bool Patrol; // Unused? - bool Alerted; - bool Friendly; - bool HurtByLara; - bool Poisoned; - bool JumpAhead; - bool MonkeySwingAhead; - bool ReachedGoal; + short MaxTurn = 0; + short JointRotation[4] = {}; + bool HeadLeft = false; + bool HeadRight = false; + bool Patrol = false; // Unused? + bool Alerted = false; + bool Friendly = false; + bool HurtByLara = false; + bool Poisoned = false; + bool JumpAhead = false; + bool MonkeySwingAhead = false; + bool ReachedGoal = false; + + short FiredWeapon; short Tosspad; short LocationAI; - short FiredWeapon; - - LOTInfo LOT; - MoodType Mood; - ItemInfo* Enemy; - short AITargetNumber; - ItemInfo* AITarget; - short Pad; // Unused? - Vector3Int Target; + short Flags = 0; #ifdef CREATURE_AI_PRIORITY_OPTIMIZATION - CreatureAIPriority Priority; - size_t FramesSinceLOTUpdate; + CreatureAIPriority Priority = CreatureAIPriority::None; + size_t FramesSinceLOTUpdate = 0; #endif - - short Flags; }; diff --git a/TombEngine/Game/itemdata/itemdata.h b/TombEngine/Game/itemdata/itemdata.h index fa34c7a83..cf44da884 100644 --- a/TombEngine/Game/itemdata/itemdata.h +++ b/TombEngine/Game/itemdata/itemdata.h @@ -26,7 +26,7 @@ template struct visitor : Ts... { using Ts::operator()...; }; template visitor(Ts...)->visitor; // line not needed in C++20... using namespace TEN::Entities::TR4; -using namespace TEN::Entities::TR5; +using namespace TEN::Entities::Creatures::TR5; using namespace TEN::Entities::Vehicles; struct ItemInfo; diff --git a/TombEngine/Game/items.cpp b/TombEngine/Game/items.cpp index 9e85e0d00..f4f2b4f8a 100644 --- a/TombEngine/Game/items.cpp +++ b/TombEngine/Game/items.cpp @@ -570,14 +570,17 @@ short CreateItem() void InitialiseItemArray(int totalItem) { - auto* item = &g_Level.Items[g_Level.NumItems]; + g_Level.Items.clear(); + g_Level.Items.resize(totalItem); - NextItemActive = NO_ITEM; - NextItemFree = g_Level.NumItems; + for (int i = 0; i < totalItem; i++) + g_Level.Items[i].Index = i; + + auto* item = &g_Level.Items[g_Level.NumItems]; if (g_Level.NumItems + 1 < totalItem) { - for(int i = g_Level.NumItems + 1; i < totalItem; i++, item++) + for (int i = g_Level.NumItems + 1; i < totalItem; i++, item++) { item->NextItem = i; item->Active = false; @@ -586,6 +589,8 @@ void InitialiseItemArray(int totalItem) } item->NextItem = NO_ITEM; + NextItemActive = NO_ITEM; + NextItemFree = g_Level.NumItems; } short SpawnItem(ItemInfo* item, GAME_OBJECT_ID objectNumber) @@ -629,7 +634,6 @@ int GlobalItemReplace(short search, GAME_OBJECT_ID replace) } // Offset values may be used to account for the quirk of room traversal only being able to occur at portals. -// Note: may not work for dynamic items because of FindItem. void UpdateItemRoom(ItemInfo* item, int height, int xOffset, int zOffset) { float sinY = phd_sin(item->Pose.Orientation.y); @@ -643,7 +647,7 @@ void UpdateItemRoom(ItemInfo* item, int height, int xOffset, int zOffset) item->Floor = GetFloorHeight(item->Location, x, z).value_or(NO_HEIGHT); if (item->RoomNumber != item->Location.roomNumber) - ItemNewRoom(FindItem(item), item->Location.roomNumber); + ItemNewRoom(item->Index, item->Location.roomNumber); } std::vector FindAllItems(short objectNumber) diff --git a/TombEngine/Game/items.h b/TombEngine/Game/items.h index 5cbb98514..78307a36e 100644 --- a/TombEngine/Game/items.h +++ b/TombEngine/Game/items.h @@ -70,8 +70,11 @@ struct EntityAnimationData struct ItemInfo { GAME_OBJECT_ID ObjectNumber; + int Status; // ItemStatus enum. bool Active; + + short Index; short NextItem; short NextActive; diff --git a/TombEngine/Game/missile.cpp b/TombEngine/Game/missile.cpp index c12ae482e..85c0021ab 100644 --- a/TombEngine/Game/missile.cpp +++ b/TombEngine/Game/missile.cpp @@ -57,7 +57,7 @@ void ControlMissile(short fxNumber) fx->pos.Position.x += velocity * phd_sin(fx->pos.Orientation.y); auto probe = GetCollision(fx->pos.Position.x, fx->pos.Position.y, fx->pos.Position.z, fx->roomNumber); - auto hitLara = ItemNearLara(&fx->pos, 200); + auto hitLara = ItemNearLara(&fx->pos.Position, 200); // Check for hitting something. if (fx->pos.Position.y >= probe.Position.Floor || diff --git a/TombEngine/Game/people.cpp b/TombEngine/Game/people.cpp index d0d4ee188..e4b3805d7 100644 --- a/TombEngine/Game/people.cpp +++ b/TombEngine/Game/people.cpp @@ -17,45 +17,45 @@ bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, in auto* creature = GetCreatureInfo(item); auto* enemy = creature->Enemy; - bool hit = false; - bool targetable = false; + bool hasHit = false; + bool isTargetable = false; - if (AI->distance <= pow(MAX_VISIBILITY_DISTANCE, 2) && Targetable(item, AI)) + if (AI->distance <= SQUARE(MAX_VISIBILITY_DISTANCE) && Targetable(item, AI)) { int distance = phd_sin(AI->enemyFacing) * enemy->Animation.Velocity.z * pow(MAX_VISIBILITY_DISTANCE, 2) / 300; - distance = pow(distance, 2) + AI->distance; - if (distance <= pow(MAX_VISIBILITY_DISTANCE, 2)) + distance = SQUARE(distance) + AI->distance; + if (distance <= SQUARE(MAX_VISIBILITY_DISTANCE)) { - int random = (pow(MAX_VISIBILITY_DISTANCE, 2) - AI->distance) / (pow(MAX_VISIBILITY_DISTANCE, 2) / 0x5000) + 8192; - hit = GetRandomControl() < random; + int random = (SQUARE(MAX_VISIBILITY_DISTANCE) - AI->distance) / (SQUARE(MAX_VISIBILITY_DISTANCE) / 0x5000) + 8192; + hasHit = GetRandomControl() < random; } else - hit = false; + hasHit = false; - targetable = true; + isTargetable = true; } else { - hit = false; - targetable = false; + hasHit = false; + isTargetable = false; } if (damage) { if (enemy->IsLara()) { - if (hit) + if (hasHit) { DoDamage(enemy, damage); CreatureEffect(item, gun, &GunHit); } - else if (targetable) + else if (isTargetable) CreatureEffect(item, gun, &GunMiss); } else { CreatureEffect(item, gun, &GunShot); - if (hit) + if (hasHit) { enemy->HitStatus = true; enemy->HitPoints += damage / -10; @@ -74,7 +74,7 @@ bool ShotLara(ItemInfo* item, AI_INFO* AI, BiteInfo gun, short extraRotation, in // TODO: smash objects - return targetable; + return isTargetable; } short GunMiss(int x, int y, int z, short velocity, short yRot, short roomNumber) @@ -145,32 +145,32 @@ bool TargetVisible(ItemInfo* item, AI_INFO* AI, float maxAngle) return false; // Check just in case. - auto* creatureInfo = GetCreatureInfo(item); - if (creatureInfo == nullptr) + auto* creature = GetCreatureInfo(item); + if (creature == nullptr) return false; - auto* enemy = creatureInfo->Enemy; + auto* enemy = creature->Enemy; if (enemy == nullptr || enemy->HitPoints == 0) return false; - short angle = AI->angle - creatureInfo->JointRotation[2]; - if (angle > -ANGLE(maxAngle) && angle < ANGLE(maxAngle)) + short angle = AI->angle - creature->JointRotation[2]; + if (angle > ANGLE(-maxAngle) && angle < ANGLE(maxAngle)) { - GameVector start; - GameVector target; auto& bounds = GetBestFrame(enemy)->boundingBox; - start.x = item->Pose.Position.x; - start.y = item->Pose.Position.y - CLICK(3); - start.z = item->Pose.Position.z; - start.roomNumber = item->RoomNumber; - - target.x = enemy->Pose.Position.x; - target.y = enemy->Pose.Position.y + ((((bounds.Y1 * 2) + bounds.Y1) + bounds.Y2) / 4); - target.z = enemy->Pose.Position.z; - target.roomNumber = enemy->RoomNumber; // TODO: Check why this line didn't exist before. -- TokyoSU, 10/8/2022 - - return LOS(&start, &target); + auto origin = GameVector( + item->Pose.Position.x, + item->Pose.Position.y - CLICK(3), + item->Pose.Position.z, + item->RoomNumber + ); + auto target = GameVector( + enemy->Pose.Position.x, + enemy->Pose.Position.y + ((((bounds.Y1 * 2) + bounds.Y1) + bounds.Y2) / 4), + enemy->Pose.Position.z, + enemy->RoomNumber // TODO: Check why this line didn't exist before. -- TokyoSU, 10/8/2022 + ); + return LOS(&origin, &target); } return false; diff --git a/TombEngine/Game/pickup/pickup.cpp b/TombEngine/Game/pickup/pickup.cpp index a2daf182b..f2f4df0dc 100644 --- a/TombEngine/Game/pickup/pickup.cpp +++ b/TombEngine/Game/pickup/pickup.cpp @@ -1,6 +1,7 @@ #include "framework.h" #include "Game/pickup/pickup.h" +#include "pickuputil.h" #include "Game/animation.h" #include "Game/camera.h" #include "Game/collision/collide_item.h" @@ -127,10 +128,23 @@ short RPickups[16]; short getThisItemPlease = NO_ITEM; Vector3Int OldPickupPos; -void PickedUpObject(GAME_OBJECT_ID objectID, int count) +bool SetInventoryCount(GAME_OBJECT_ID objectID, int count) +{ + if (!TryModifyWeapon(Lara, objectID, count, ModificationType::Set) && + !TryModifyingAmmo(Lara, objectID, count, ModificationType::Set) && + !TryModifyingKeyItem(Lara, objectID, count, ModificationType::Set) && + !TryModifyingConsumable(Lara, objectID, count, ModificationType::Set) && + !TryModifyMiscCount(Lara, objectID, count, ModificationType::Set)) + { + return false; + } + return true; +} + +void PickedUpObject(GAME_OBJECT_ID objectID, std::optional count) { // See if the items fit into one of these easy groups. - if (!TryAddingWeapon(Lara, objectID, count) && + if (!TryAddingWeapon(Lara, objectID) && !TryAddingAmmo(Lara, objectID, count) && !TryAddingKeyItem(Lara, objectID, count) && !TryAddingConsumable(Lara, objectID, count) && @@ -165,10 +179,10 @@ int GetInventoryCount(GAME_OBJECT_ID objectID) return 0; } -void RemoveObjectFromInventory(GAME_OBJECT_ID objectID, int count) +void RemoveObjectFromInventory(GAME_OBJECT_ID objectID, std::optional count) { // See if the items fit into one of these easy groups. - if (!TryRemovingWeapon(Lara, objectID, count) && + if (!TryRemovingWeapon(Lara, objectID) && !TryRemovingAmmo(Lara, objectID, count) && !TryRemovingKeyItem(Lara, objectID, count) && !TryRemovingConsumable(Lara, objectID, count) && diff --git a/TombEngine/Game/pickup/pickup.h b/TombEngine/Game/pickup/pickup.h index 452df5de1..c8e8e187e 100644 --- a/TombEngine/Game/pickup/pickup.h +++ b/TombEngine/Game/pickup/pickup.h @@ -11,8 +11,9 @@ extern short RPickups[16]; extern Vector3Int OldPickupPos; void InitialisePickup(short itemNumber); -void PickedUpObject(GAME_OBJECT_ID objectID, int count); -void RemoveObjectFromInventory(GAME_OBJECT_ID objectID, int count); +bool SetInventoryCount(GAME_OBJECT_ID objectID, int count); +void PickedUpObject(GAME_OBJECT_ID objectID, std::optional count = std::nullopt); +void RemoveObjectFromInventory(GAME_OBJECT_ID objectID, std::optional count = std::nullopt); int GetInventoryCount(GAME_OBJECT_ID objectID); void CollectCarriedItems(ItemInfo* item); void PickupCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); diff --git a/TombEngine/Game/pickup/pickup_ammo.cpp b/TombEngine/Game/pickup/pickup_ammo.cpp index c10853f9d..7dfd2d45e 100644 --- a/TombEngine/Game/pickup/pickup_ammo.cpp +++ b/TombEngine/Game/pickup/pickup_ammo.cpp @@ -20,8 +20,8 @@ static constexpr std::array kAmmo { { ID_PISTOLS_AMMO_ITEM, LaraWeaponType::Pistol, WeaponAmmoType::Ammo1, 0 }, { ID_UZI_AMMO_ITEM, LaraWeaponType::Uzi, WeaponAmmoType::Ammo1, 30 }, - { ID_SHOTGUN_AMMO1_ITEM, LaraWeaponType::Shotgun, WeaponAmmoType::Ammo1, 36 }, - { ID_SHOTGUN_AMMO2_ITEM, LaraWeaponType::Shotgun, WeaponAmmoType::Ammo2, 36 }, + { ID_SHOTGUN_AMMO1_ITEM, LaraWeaponType::Shotgun, WeaponAmmoType::Ammo1, 6 }, + { ID_SHOTGUN_AMMO2_ITEM, LaraWeaponType::Shotgun, WeaponAmmoType::Ammo2, 6 }, { ID_CROSSBOW_AMMO1_ITEM, LaraWeaponType::Crossbow, WeaponAmmoType::Ammo1, 10 }, { ID_CROSSBOW_AMMO2_ITEM, LaraWeaponType::Crossbow, WeaponAmmoType::Ammo2, 10 }, { ID_CROSSBOW_AMMO3_ITEM, LaraWeaponType::Crossbow, WeaponAmmoType::Ammo3, 10 }, @@ -35,7 +35,7 @@ static constexpr std::array kAmmo } }; -static bool TryModifyingAmmo(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount, bool add) +bool TryModifyingAmmo(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount, ModificationType modType) { int arrayPos = GetArraySlot(kAmmo, objectID); if (-1 == arrayPos) @@ -43,26 +43,40 @@ static bool TryModifyingAmmo(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount AmmoPickupInfo info = kAmmo[arrayPos]; - auto currentAmmo = lara.Weapons[(int)info.LaraWeaponType].Ammo[(int)info.AmmoType]; - if (!currentAmmo.hasInfinite()) + auto & currentWeapon = lara.Weapons[(int)info.LaraWeaponType]; + auto & currentAmmo = currentWeapon.Ammo[(int)info.AmmoType]; + + switch(modType) { - int defaultModify = add ? info.Amount : -info.Amount; - int newVal = int{ currentAmmo.getCount() } + (amount ? amount : defaultModify); - lara.Weapons[(int)info.LaraWeaponType].Ammo[(int)info.AmmoType] = std::max(0, newVal); - } + case ModificationType::Set: + currentAmmo = amount.value(); + currentAmmo.setInfinite(amount == -1); + break; + + default: + if (!currentAmmo.hasInfinite()) + { + int defaultModify = modType == ModificationType::Add ? info.Amount : -info.Amount; + int newVal = int{ currentAmmo.getCount() } + (amount.has_value() ? amount.value() : defaultModify); + currentAmmo = std::max(0, newVal); + } + break; + }; return true; } -// We need the extra bool because amount might be zero to signify the default amount. -bool TryAddingAmmo(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount) +bool TryAddingAmmo(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount) { - return TryModifyingAmmo(lara, objectID, amount, true); + return TryModifyingAmmo(lara, objectID, amount, ModificationType::Add); } -bool TryRemovingAmmo(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount) +bool TryRemovingAmmo(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount) { - return TryModifyingAmmo(lara, objectID, -amount, false); + if (amount.has_value()) + return TryModifyingAmmo(lara, objectID, -amount.value(), ModificationType::Remove); + else + return TryModifyingAmmo(lara, objectID, amount, ModificationType::Remove); } std::optional GetAmmoCount(LaraInfo& lara, GAME_OBJECT_ID objectID) diff --git a/TombEngine/Game/pickup/pickup_ammo.h b/TombEngine/Game/pickup/pickup_ammo.h index 83e685fed..4a05e92b2 100644 --- a/TombEngine/Game/pickup/pickup_ammo.h +++ b/TombEngine/Game/pickup/pickup_ammo.h @@ -1,8 +1,10 @@ #pragma once +enum class ModificationType; enum GAME_OBJECT_ID : short; struct LaraInfo; -bool TryAddingAmmo(LaraInfo&, GAME_OBJECT_ID objectID, int amount = 0); -bool TryRemovingAmmo(LaraInfo&, GAME_OBJECT_ID objectID, int amount = 0); +bool TryAddingAmmo(LaraInfo&, GAME_OBJECT_ID objectID, std::optional amount = std::nullopt); +bool TryRemovingAmmo(LaraInfo&, GAME_OBJECT_ID objectID, std::optional amount = std::nullopt); +bool TryModifyingAmmo(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount, ModificationType modType); std::optional GetAmmoCount(LaraInfo&, GAME_OBJECT_ID objectID); diff --git a/TombEngine/Game/pickup/pickup_consumable.cpp b/TombEngine/Game/pickup/pickup_consumable.cpp index 1adb8b80c..4612e52c7 100644 --- a/TombEngine/Game/pickup/pickup_consumable.cpp +++ b/TombEngine/Game/pickup/pickup_consumable.cpp @@ -26,32 +26,44 @@ static constexpr std::array kConsumables = } }; -static bool TryModifyingConsumable(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount, bool add) +bool TryModifyingConsumable(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount, ModificationType modType) { int arrayPos = GetArraySlot(kConsumables, objectID); if (-1 == arrayPos) return false; ConsumablePickupInfo info = kConsumables[arrayPos]; - - if (lara.Inventory.*(info.Count) != -1) + auto & currentAmt = lara.Inventory.*(info.Count); + switch (modType) { - int defaultModify = add ? info.Amount : -info.Amount; - int newVal = lara.Inventory.*(info.Count) + (amount ? amount : defaultModify); - lara.Inventory.*(info.Count) = std::max(0, newVal); + case ModificationType::Set: + currentAmt = amount.value(); + break; + + default: + if (currentAmt != -1) + { + int defaultModify = ModificationType::Add == modType ? info.Amount : -info.Amount; + int newVal = currentAmt + (amount.has_value() ? amount.value() : defaultModify); + currentAmt = std::max(0, newVal); + } + break; } return true; } -bool TryAddingConsumable(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount) +bool TryAddingConsumable(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount) { - return TryModifyingConsumable(lara, objectID, amount, true); + return TryModifyingConsumable(lara, objectID, amount, ModificationType::Add); } -bool TryRemovingConsumable(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount) +bool TryRemovingConsumable(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount) { - return TryModifyingConsumable(lara, objectID, -amount, false); + if (amount.has_value()) + return TryModifyingConsumable(lara, objectID, -amount.value(), ModificationType::Remove); + else + return TryModifyingConsumable(lara, objectID, amount, ModificationType::Remove); } std::optional GetConsumableCount(LaraInfo& lara, GAME_OBJECT_ID objectID) diff --git a/TombEngine/Game/pickup/pickup_consumable.h b/TombEngine/Game/pickup/pickup_consumable.h index e2c2d56d9..fcf3acf5b 100644 --- a/TombEngine/Game/pickup/pickup_consumable.h +++ b/TombEngine/Game/pickup/pickup_consumable.h @@ -1,8 +1,10 @@ #pragma once +enum class ModificationType; enum GAME_OBJECT_ID : short; struct LaraInfo; -bool TryAddingConsumable(LaraInfo&, GAME_OBJECT_ID objectID, int amount = 0); -bool TryRemovingConsumable(LaraInfo&, GAME_OBJECT_ID objectID, int amount = 0); +bool TryAddingConsumable(LaraInfo&, GAME_OBJECT_ID objectID, std::optional amount = 0); +bool TryRemovingConsumable(LaraInfo&, GAME_OBJECT_ID objectID, std::optional amount = 0); +bool TryModifyingConsumable(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount, ModificationType modType); std::optional GetConsumableCount(LaraInfo&, GAME_OBJECT_ID objectID); diff --git a/TombEngine/Game/pickup/pickup_key_items.cpp b/TombEngine/Game/pickup/pickup_key_items.cpp index aa2a16572..0d1ca734e 100644 --- a/TombEngine/Game/pickup/pickup_key_items.cpp +++ b/TombEngine/Game/pickup/pickup_key_items.cpp @@ -2,7 +2,7 @@ #include "Game/pickup/pickup_key_items.h" #include "Game/Lara/lara_struct.h" -#include "Game/pickup/pickup_misc_items.h" +#include "Game/pickup/pickuputil.h" #include "Objects/objectslist.h" template struct KeyPickupInfo @@ -64,29 +64,42 @@ template<> static std::pair GetArrayInternal<0>(LaraInfo& lara, GA return TestAgainstRange<0>(lara, objectID); } -static bool TryModifyingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount, bool add) +bool TryModifyingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount, ModificationType modType) { // kick off the recursion starting at the last element auto result = GetArrayInternal(lara, objectID); + if (result.first) { - int defaultModify = add ? kDefaultPickupAmount : -kDefaultPickupAmount; - int newVal = int{result.first[result.second]} + (amount ? amount : defaultModify); - result.first[result.second] = std::max(0, newVal); + auto& amt = result.first[result.second]; + switch (modType) + { + case ModificationType::Set: + // infinite key items not yet implemented + amt = amount.value(); + break; + default: + int defaultModify = modType == ModificationType::Add ? kDefaultPickupAmount : -kDefaultPickupAmount; + int newVal = int{ result.first[result.second] } + (amount.has_value() ? amount.value() : defaultModify); + result.first[result.second] = std::max(0, newVal); + } return true; } return false; } -bool TryAddingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, int count) +bool TryAddingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount) { - return TryModifyingKeyItem(lara, objectID, count, true); + return TryModifyingKeyItem(lara, objectID, amount, ModificationType::Add); } -bool TryRemovingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, int count) +bool TryRemovingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount) { - return TryModifyingKeyItem(lara, objectID, -count, false); + if(amount.has_value()) + return TryModifyingKeyItem(lara, objectID, -amount.value(), ModificationType::Remove); + else + return TryModifyingKeyItem(lara, objectID, amount, ModificationType::Remove); } std::optional GetKeyItemCount(LaraInfo& lara, GAME_OBJECT_ID objectID) diff --git a/TombEngine/Game/pickup/pickup_key_items.h b/TombEngine/Game/pickup/pickup_key_items.h index 5160d0c96..4b17fe653 100644 --- a/TombEngine/Game/pickup/pickup_key_items.h +++ b/TombEngine/Game/pickup/pickup_key_items.h @@ -1,8 +1,10 @@ #pragma once +enum class ModificationType; enum GAME_OBJECT_ID : short; struct LaraInfo; -bool TryAddingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, int count); -bool TryRemovingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, int count); +bool TryAddingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount = std::nullopt); +bool TryRemovingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount = std::nullopt); +bool TryModifyingKeyItem(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount, ModificationType modType); std::optional GetKeyItemCount(LaraInfo& lara, GAME_OBJECT_ID objectID); diff --git a/TombEngine/Game/pickup/pickup_misc_items.cpp b/TombEngine/Game/pickup/pickup_misc_items.cpp index 9afdf18e0..e145ebcbb 100644 --- a/TombEngine/Game/pickup/pickup_misc_items.cpp +++ b/TombEngine/Game/pickup/pickup_misc_items.cpp @@ -1,8 +1,6 @@ #include "framework.h" #include "Game/pickup/pickup_misc_items.h" -#include - #include "Game/Lara/lara_struct.h" #include "Game/pickup/pickuputil.h" #include "Objects/objectslist.h" @@ -34,11 +32,14 @@ auto LaserSightIsEquipped(LaraInfo& lara) return false; }; -static bool TryModifyMiscCount(LaraInfo& lara, GAME_OBJECT_ID objectID, bool add) +bool TryModifyMiscCount(LaraInfo & lara, GAME_OBJECT_ID objectID, std::optional amount, ModificationType modType) { // If adding, replace the small/large waterskin with one of the requested // capacity. If removing, only remove the waterskin if it contains the given // capacity. + + bool add = ModificationType::Add == modType || ((ModificationType::Set == modType) && amount != 0); + auto modifyWaterSkinAmount = [&](byte& currentFlag, byte newFlag) { if (add) @@ -141,12 +142,12 @@ static bool TryModifyMiscCount(LaraInfo& lara, GAME_OBJECT_ID objectID, bool add bool TryAddMiscItem(LaraInfo& lara, GAME_OBJECT_ID objectID) { - return TryModifyMiscCount(lara, objectID, true); + return TryModifyMiscCount(lara, objectID, std::nullopt, ModificationType::Add); } bool TryRemoveMiscItem(LaraInfo& lara, GAME_OBJECT_ID objectID) { - return TryModifyMiscCount(lara, objectID, false); + return TryModifyMiscCount(lara, objectID, std::nullopt, ModificationType::Remove); } std::optional HasMiscItem(LaraInfo& lara, GAME_OBJECT_ID objectID) diff --git a/TombEngine/Game/pickup/pickup_misc_items.h b/TombEngine/Game/pickup/pickup_misc_items.h index 9b1784418..6d03ef06b 100644 --- a/TombEngine/Game/pickup/pickup_misc_items.h +++ b/TombEngine/Game/pickup/pickup_misc_items.h @@ -1,8 +1,10 @@ #pragma once +enum class ModificationType; enum GAME_OBJECT_ID : short; struct LaraInfo; bool TryAddMiscItem(LaraInfo& lara, GAME_OBJECT_ID objectID); bool TryRemoveMiscItem(LaraInfo& lara, GAME_OBJECT_ID objectID); +bool TryModifyMiscCount(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional amount, ModificationType modType); std::optional HasMiscItem(LaraInfo& lara, GAME_OBJECT_ID objectID); diff --git a/TombEngine/Game/pickup/pickup_weapon.cpp b/TombEngine/Game/pickup/pickup_weapon.cpp index 105abe96d..d2141b934 100644 --- a/TombEngine/Game/pickup/pickup_weapon.cpp +++ b/TombEngine/Game/pickup/pickup_weapon.cpp @@ -3,30 +3,38 @@ #include +#include "lara_fire.h" #include "Game/Lara/lara_struct.h" #include "Game/pickup/pickuputil.h" #include "Game/pickup/pickup_ammo.h" #include "Objects/objectslist.h" +enum class HolsterType +{ + Hips, + Back +}; + struct WeaponPickupInfo { GAME_OBJECT_ID ObjectID; GAME_OBJECT_ID AmmoID; // When the player picks up a weapon, they get one clip's worth of this ammo. LaraWeaponType LaraWeaponType; + HolsterType Holster; }; static constexpr std::array kWeapons { { - { ID_PISTOLS_ITEM, ID_PISTOLS_AMMO_ITEM, LaraWeaponType::Pistol }, - { ID_UZI_ITEM, ID_UZI_AMMO_ITEM, LaraWeaponType::Uzi }, - { ID_SHOTGUN_ITEM, ID_SHOTGUN_AMMO1_ITEM, LaraWeaponType::Shotgun }, - { ID_CROSSBOW_ITEM, ID_CROSSBOW_AMMO1_ITEM, LaraWeaponType::Crossbow }, - { ID_REVOLVER_ITEM, ID_REVOLVER_AMMO_ITEM, LaraWeaponType::Revolver }, - { ID_HK_ITEM, ID_HK_AMMO_ITEM, LaraWeaponType::HK }, - { ID_GRENADE_GUN_ITEM, ID_GRENADE_AMMO1_ITEM, LaraWeaponType::GrenadeLauncher }, - { ID_ROCKET_LAUNCHER_ITEM, ID_ROCKET_LAUNCHER_AMMO_ITEM, LaraWeaponType::RocketLauncher }, - { ID_HARPOON_ITEM, ID_HARPOON_AMMO_ITEM, LaraWeaponType::HarpoonGun } + { ID_PISTOLS_ITEM, ID_PISTOLS_AMMO_ITEM, LaraWeaponType::Pistol, HolsterType::Hips }, + { ID_UZI_ITEM, ID_UZI_AMMO_ITEM, LaraWeaponType::Uzi, HolsterType::Hips}, + { ID_SHOTGUN_ITEM, ID_SHOTGUN_AMMO1_ITEM, LaraWeaponType::Shotgun, HolsterType::Back }, + { ID_CROSSBOW_ITEM, ID_CROSSBOW_AMMO1_ITEM, LaraWeaponType::Crossbow, HolsterType::Back}, + { ID_REVOLVER_ITEM, ID_REVOLVER_AMMO_ITEM, LaraWeaponType::Revolver, HolsterType::Hips}, + { ID_HK_ITEM, ID_HK_AMMO_ITEM, LaraWeaponType::HK, HolsterType::Back}, + { ID_GRENADE_GUN_ITEM, ID_GRENADE_AMMO1_ITEM, LaraWeaponType::GrenadeLauncher, HolsterType::Back }, + { ID_ROCKET_LAUNCHER_ITEM, ID_ROCKET_LAUNCHER_AMMO_ITEM, LaraWeaponType::RocketLauncher, HolsterType::Back }, + { ID_HARPOON_ITEM, ID_HARPOON_AMMO_ITEM, LaraWeaponType::HarpoonGun, HolsterType::Back } } }; @@ -41,7 +49,7 @@ static int GetWeapon(GAME_OBJECT_ID objectID) return -1; } -static bool TryModifyWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID, int ammoAmount, bool add) +bool TryModifyWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional count, ModificationType type) { int arrayPos = GetArraySlot(kWeapons, objectID); if (-1 == arrayPos) @@ -51,25 +59,56 @@ static bool TryModifyWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID, int ammoAmo // Set the SelectedAmmo type to WeaponAmmoType::Ammo1 (0) if adding the weapon for the first time. // Note that this refers to the index of the weapon's ammo array, and not the weapon's actual ammunition count. - if (!lara.Weapons[(int)info.LaraWeaponType].Present) - lara.Weapons[(int)info.LaraWeaponType].SelectedAmmo = WeaponAmmoType::Ammo1; - - lara.Weapons[(int)info.LaraWeaponType].Present = add; - auto ammoID = info.AmmoID; - return add ? TryAddingAmmo(lara, ammoID, ammoAmount) : TryRemovingAmmo(lara, ammoID, ammoAmount); + auto& currWeapon = lara.Weapons[(int)info.LaraWeaponType]; + + if (!currWeapon.Present) + currWeapon.SelectedAmmo = WeaponAmmoType::Ammo1; + + bool add = ModificationType::Add == type || ((ModificationType::Set == type) && count != 0); + currWeapon.Present = add; + + if(!add) + { + if (info.LaraWeaponType == lara.Control.Weapon.GunType || info.LaraWeaponType == lara.Control.Weapon.LastGunType) + { + lara.Control.Weapon.RequestGunType = LaraWeaponType::None; + + // If Lara has pistols and it's not the pistols we're removing, set them + // as the "next weapon" so that Lara equips them next. + if (LaraWeaponType::Pistol == info.LaraWeaponType || !lara.Weapons[(int)LaraWeaponType::Pistol].Present) + { + lara.Control.Weapon.LastGunType = LaraWeaponType::None; + } + else + { + lara.Control.Weapon.LastGunType = LaraWeaponType::Pistol; + } + } + + if (HolsterType::Hips == info.Holster && lara.Control.Weapon.HolsterInfo.LeftHolster == HolsterSlotForWeapon(info.LaraWeaponType)) + { + lara.Control.Weapon.HolsterInfo.LeftHolster = HolsterSlot::Empty; + lara.Control.Weapon.HolsterInfo.RightHolster = HolsterSlot::Empty; + } + else if (HolsterType::Back == info.Holster && lara.Control.Weapon.HolsterInfo.BackHolster == HolsterSlotForWeapon(info.LaraWeaponType)) + { + lara.Control.Weapon.HolsterInfo.BackHolster = HolsterSlot::Empty; + } + } + + return true; } -// Adding a weapon will either give the player the weapon + an amount of ammo, or, -// if they already have the weapon, simply the ammo. -bool TryAddingWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount) +// Adding a weapon will not give the player any ammo even if they already have the weapon +bool TryAddingWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID) { - return TryModifyWeapon(lara, objectID, amount, true); + return TryModifyWeapon(lara, objectID, 1, ModificationType::Add); } -// Removing a weapon is the reverse of the above; it will remove the weapon (if it's there) and the amount of ammo. -bool TryRemovingWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount) +// Removing a weapon is the reverse of the above; it will remove the weapon (if it's there). +bool TryRemovingWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID) { - return TryModifyWeapon(lara, objectID, amount, false); + return TryModifyWeapon(lara, objectID, 1, ModificationType::Remove); } std::optional HasWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID) diff --git a/TombEngine/Game/pickup/pickup_weapon.h b/TombEngine/Game/pickup/pickup_weapon.h index d61f86dd9..7ae6ace52 100644 --- a/TombEngine/Game/pickup/pickup_weapon.h +++ b/TombEngine/Game/pickup/pickup_weapon.h @@ -1,8 +1,10 @@ #pragma once +enum class ModificationType; enum GAME_OBJECT_ID : short; struct LaraInfo; -bool TryAddingWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount = 0); -bool TryRemovingWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID, int amount = 0); +bool TryAddingWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID); +bool TryRemovingWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID); +bool TryModifyWeapon(LaraInfo& lara, GAME_OBJECT_ID objectID, std::optional count, ModificationType type); std::optional HasWeapon(LaraInfo&, GAME_OBJECT_ID objectID); diff --git a/TombEngine/Game/pickup/pickuputil.h b/TombEngine/Game/pickup/pickuputil.h index 6904d15f5..9f1217459 100644 --- a/TombEngine/Game/pickup/pickuputil.h +++ b/TombEngine/Game/pickup/pickuputil.h @@ -2,6 +2,13 @@ enum GAME_OBJECT_ID : short; +enum class ModificationType +{ + Add, + Remove, + Set +}; + // Given an array and an Object ID, iterate through the array until we find // an ID that matches the ID we've passed in. template int GetArraySlot(std::array const& arr, GAME_OBJECT_ID objectID) diff --git a/TombEngine/Game/room.h b/TombEngine/Game/room.h index e5e1ab8ae..2157ed34d 100644 --- a/TombEngine/Game/room.h +++ b/TombEngine/Game/room.h @@ -42,6 +42,7 @@ struct ROOM_LIGHT struct MESH_INFO { PHD_3DPOS pos; + int roomNumber; float scale; short staticNumber; short flags; diff --git a/TombEngine/Game/savegame.cpp b/TombEngine/Game/savegame.cpp index 2bacfd725..c1f30802b 100644 --- a/TombEngine/Game/savegame.cpp +++ b/TombEngine/Game/savegame.cpp @@ -818,7 +818,7 @@ bool SaveGame::Save(int slot) staticMesh.add_flags(room->mesh[j].flags); staticMesh.add_hit_points(room->mesh[j].HitPoints); - staticMesh.add_room_number(i); + staticMesh.add_room_number(room->mesh[j].roomNumber); staticMesh.add_number(j); staticMeshes.push_back(staticMesh.Finish()); } @@ -1027,43 +1027,45 @@ bool SaveGame::Save(int slot) alternatePendulumOffset = alternatePendulumInfo.Finish(); } - - std::vector> levelTableVec; - std::vector> levelStringVec2; - - //std::vector savedTables; - std::vector savedStrings; std::vector savedVars; g_GameScript->GetVariables(savedVars); - std::vector> varsVec; for (auto const& s : savedVars) { - flatbuffers::Offset scriptTableOffset; - flatbuffers::Offset strOffset; - flatbuffers::Offset doubleOffset; - flatbuffers::Offset boolOffset; + auto putDataInVec = [&varsVec, &fbb](Save::VarUnion type, auto const & offsetVar) + { + Save::UnionTableBuilder ut{ fbb }; + ut.add_u_type(type); + ut.add_u(offsetVar.Union()); + varsVec.push_back(ut.Finish()); + }; if (std::holds_alternative(s)) { auto strOffset2 = fbb.CreateString(std::get(s)); Save::stringTableBuilder stb{ fbb }; stb.add_str(strOffset2); - strOffset = stb.Finish(); + auto strOffset = stb.Finish(); + + putDataInVec(Save::VarUnion::str, strOffset); } else if (std::holds_alternative(s)) { Save::doubleTableBuilder dtb{ fbb }; dtb.add_scalar(std::get(s)); - doubleOffset = dtb.Finish(); + auto doubleOffset = dtb.Finish(); + + putDataInVec(Save::VarUnion::num, doubleOffset); } else if (std::holds_alternative(s)) { Save::boolTableBuilder btb{ fbb }; btb.add_scalar(std::get(s)); - boolOffset = btb.Finish(); + auto boolOffset = btb.Finish(); + + putDataInVec(Save::VarUnion::boolean, boolOffset); } else if (std::holds_alternative(s)) { @@ -1077,37 +1079,43 @@ bool SaveGame::Save(int slot) auto vecOffset = fbb.CreateVectorOfStructs(keyValVec); Save::ScriptTableBuilder stb{ fbb }; stb.add_keys_vals(vecOffset); - scriptTableOffset = stb.Finish(); - } + auto scriptTableOffset = stb.Finish(); - Save::UnionTableBuilder ut{ fbb }; - if (std::holds_alternative(s)) - { - ut.add_u_type(Save::VarUnion::str); - ut.add_u(strOffset.Union()); + putDataInVec(Save::VarUnion::tab, scriptTableOffset); } - else if (std::holds_alternative(s)) + else if (std::holds_alternative(s)) { - ut.add_u_type(Save::VarUnion::num); - ut.add_u(doubleOffset.Union()); + std::string data = std::get(s).name; + auto strOffset = fbb.CreateString(data); + Save::funcNameTableBuilder ftb{ fbb }; + ftb.add_str(strOffset); + auto funcNameOffset = ftb.Finish(); + + putDataInVec(Save::VarUnion::funcName, funcNameOffset); } - else if (std::holds_alternative(s)) + else if (std::holds_alternative(s)) { - ut.add_u_type(Save::VarUnion::boolean); - ut.add_u(boolOffset.Union()); + Save::vec3TableBuilder vtb{ fbb }; + Vector3Int data = std::get(s); + Save::Vector3 saveVec = FromVector3(std::get(s)); + vtb.add_vec(&saveVec); + auto vec3Offset = vtb.Finish(); + + putDataInVec(Save::VarUnion::vec3, vec3Offset); } - else if (std::holds_alternative(s)) - { - ut.add_u_type(Save::VarUnion::tab); - ut.add_u(scriptTableOffset.Union()); - } - varsVec.push_back(ut.Finish()); } auto unionVec = fbb.CreateVector(varsVec); Save::UnionVecBuilder uvb{ fbb }; uvb.add_members(unionVec); auto unionVecOffset = uvb.Finish(); + std::vector callbackVecPreControl; + std::vector callbackVecPostControl; + g_GameScript->GetCallbackStrings(callbackVecPreControl, callbackVecPostControl); + + auto stringsCallbackPreControl = fbb.CreateVectorOfStrings(callbackVecPreControl); + auto stringsCallbackPostControl = fbb.CreateVectorOfStrings(callbackVecPostControl); + Save::SaveGameBuilder sgb{ fbb }; sgb.add_header(headerOffset); @@ -1151,6 +1159,8 @@ bool SaveGame::Save(int slot) } sgb.add_script_vars(unionVecOffset); + sgb.add_callbacks_pre_control(stringsCallbackPreControl); + sgb.add_callbacks_post_control(stringsCallbackPostControl); auto sg = sgb.Finish(); fbb.Finish(sg); @@ -1235,6 +1245,7 @@ bool SaveGame::Load(int slot) int number = staticMesh->number(); room->mesh[number].pos = ToPHD(staticMesh->pose()); + room->mesh[number].roomNumber = staticMesh->room_number(); room->mesh[number].scale = staticMesh->scale(); room->mesh[number].color = ToVector4(staticMesh->color()); @@ -1294,9 +1305,6 @@ bool SaveGame::Load(int slot) ZeroMemory(&Lara, sizeof(LaraInfo)); - // Items - InitialiseItemArray(NUM_ITEMS); - NextItemFree = s->next_item_free(); NextItemActive = s->next_item_active(); @@ -1918,17 +1926,38 @@ bool SaveGame::Load(int slot) { auto tab = var->u_as_tab()->keys_vals(); auto& loadedTab = loadedVars.emplace_back(IndexTable{}); - + for (auto const& p : *tab) { std::get(loadedTab).push_back(std::make_pair(p->key(), p->val())); } } + else if (var->u_type() == Save::VarUnion::vec3) + { + loadedVars.push_back(ToVector3Int(var->u_as_vec3()->vec())); + } + else if (var->u_type() == Save::VarUnion::funcName) + { + loadedVars.push_back(FuncName{var->u_as_funcName()->str()->str()}); + } + } } g_GameScript->SetVariables(loadedVars); + std::vector callbacksPreControlVec; + auto callbacksPreControlOffsetVec = s->callbacks_pre_control(); + for (auto const& s : *callbacksPreControlOffsetVec) + callbacksPreControlVec.push_back(s->str()); + + std::vector callbacksPostControlVec; + auto callbacksPostControlOffsetVec = s->callbacks_post_control(); + for (auto const& s : *callbacksPostControlOffsetVec) + callbacksPostControlVec.push_back(s->str()); + + g_GameScript->SetCallbackStrings(callbacksPreControlVec, callbacksPostControlVec); + return true; } diff --git a/TombEngine/Objects/Effects/enemy_missile.cpp b/TombEngine/Objects/Effects/enemy_missile.cpp index fec8ecee0..77edcf0e1 100644 --- a/TombEngine/Objects/Effects/enemy_missile.cpp +++ b/TombEngine/Objects/Effects/enemy_missile.cpp @@ -259,7 +259,7 @@ namespace TEN::Entities::Effects return; } - if (ItemNearLara(&fx->pos, 200)) + if (ItemNearLara(&fx->pos.Position, 200)) { LaraItem->HitStatus = true; if (fx->flag1 != 6) diff --git a/TombEngine/Objects/Effects/flame_emitters.cpp b/TombEngine/Objects/Effects/flame_emitters.cpp index fb2e113a9..0d81a2fb8 100644 --- a/TombEngine/Objects/Effects/flame_emitters.cpp +++ b/TombEngine/Objects/Effects/flame_emitters.cpp @@ -154,7 +154,7 @@ namespace TEN::Entities::Effects SoundEffect(SFX_TR4_LOOP_FOR_SMALL_FIRES, &item->Pose); if (!Lara.Burn && - ItemNearLara(&item->Pose, 600) && + ItemNearLara(&item->Pose.Position, 600) && (pow(LaraItem->Pose.Position.x - item->Pose.Position.x, 2) + pow(LaraItem->Pose.Position.z - item->Pose.Position.z, 2) < pow(SECTOR(0.5f), 2)) && Lara.Control.WaterStatus != WaterStatus::FlyCheat) @@ -625,8 +625,7 @@ namespace TEN::Entities::Effects TriggerDynamicLight(x, item->Pose.Position.y, z, 12, (GetRandomControl() & 0x3F) + 192, ((GetRandomControl() >> 4) & 0x1F) + 96, 0); - auto pos = PHD_3DPOS(item->Pose.Position); - + auto pos = item->Pose.Position; if (ItemNearLara(&pos, 600)) { if ((!Lara.Burn) && Lara.Control.WaterStatus != WaterStatus::FlyCheat) diff --git a/TombEngine/Objects/Effects/tr4_locusts.cpp b/TombEngine/Objects/Effects/tr4_locusts.cpp index fe66325f8..9b1bf5f34 100644 --- a/TombEngine/Objects/Effects/tr4_locusts.cpp +++ b/TombEngine/Objects/Effects/tr4_locusts.cpp @@ -191,7 +191,7 @@ namespace TEN::Entities::TR4 locust->pos.Position.y += locust->randomRotation * phd_sin(-locust->pos.Orientation.x); locust->pos.Position.z += locust->randomRotation * phd_cos(locust->pos.Orientation.x) * phd_cos(locust->pos.Orientation.y); - if (ItemNearTarget(&locust->pos, LaraItem, CLICK(1) / 2)) + if (ItemNearTarget(&locust->pos.Position, LaraItem, CLICK(1) / 2)) { TriggerBlood(locust->pos.Position.x, locust->pos.Position.y, locust->pos.Position.z, 2 * GetRandomControl(), 2); DoDamage(LaraItem, LOCUST_LARA_DAMAGE); diff --git a/TombEngine/Objects/TR1/Entity/tr1_ape.cpp b/TombEngine/Objects/TR1/Entity/tr1_ape.cpp index 274645477..c0c62dbcb 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_ape.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_ape.cpp @@ -15,19 +15,23 @@ using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { constexpr auto APE_ATTACK_DAMAGE = 200; constexpr auto APE_ATTACK_RANGE = SQUARE(SECTOR(0.42f)); constexpr auto APE_PANIC_RANGE = SQUARE(SECTOR(2)); - constexpr auto APE_JUMP_CHANCE = 0xA0; - constexpr auto APE_POUND_CHEST_CHANCE = APE_JUMP_CHANCE + 0xA0; - constexpr auto APE_POUND_GROUND_CHANCE = APE_POUND_CHEST_CHANCE + 0xA0; - constexpr auto APE_RUN_LEFT_CHANCE = APE_POUND_GROUND_CHANCE + 0xA0; + constexpr auto APE_IDLE_JUMP_CHANCE = 1.0f / 6; + constexpr auto APE_IDLE_POUND_CHEST_CHANCE = 1.0f / 3; + constexpr auto APE_IDLE_POUND_GROUND_CHANCE = 1.0f / 2; + constexpr auto APE_IDLE_RUN_LEFT_CHANCE = 1.0f / 2; + constexpr auto APE_RUN_JUMP_CHANCE = APE_IDLE_JUMP_CHANCE / 32; + constexpr auto APE_RUN_POUND_CHEST_CHANCE = APE_IDLE_POUND_CHEST_CHANCE / 32; + constexpr auto APE_RUN_POUND_GROUND_CHANCE = APE_IDLE_POUND_GROUND_CHANCE / 32; + constexpr auto APE_RUN_RUN_LEFT_CHANCE = APE_IDLE_RUN_LEFT_CHANCE / 32; - constexpr auto SHIFT = 75; + constexpr auto APE_SHIFT = 75; #define APE_RUN_TURN_RATE_MAX ANGLE(5.0f) #define APE_DISPLAY_ANGLE ANGLE(45.0f) @@ -118,12 +122,12 @@ namespace TEN::Entities::TR1 if (yy < yFloor) { - item->Pose.Position.x = (yFloor * SECTOR(1)) - SHIFT; + item->Pose.Position.x = (yFloor * SECTOR(1)) - APE_SHIFT; item->Pose.Orientation.y = ANGLE(90.0f); } else { - item->Pose.Position.x = (yy * SECTOR(1)) + SHIFT; + item->Pose.Position.x = (yy * SECTOR(1)) + APE_SHIFT; item->Pose.Orientation.y = -ANGLE(90.0f); } } @@ -131,12 +135,12 @@ namespace TEN::Entities::TR1 { if (xx < xFloor) { - item->Pose.Position.z = (xFloor * SECTOR(1)) - SHIFT; + item->Pose.Position.z = (xFloor * SECTOR(1)) - APE_SHIFT; item->Pose.Orientation.y = 0; } else { - item->Pose.Position.z = (xx * SECTOR(1)) + SHIFT; + item->Pose.Position.z = (xx * SECTOR(1)) + APE_SHIFT; item->Pose.Orientation.y = -ANGLE(180.0f); } } @@ -145,7 +149,7 @@ namespace TEN::Entities::TR1 // diagonal } - if (CreatureVault(itemNumber, angle, 2, SHIFT) == 2) + if (CreatureVault(itemNumber, angle, 2, APE_SHIFT) == 2) { item->Pose.Position.y = y; SetAnimation(item, APE_ANIM_VAULT); @@ -184,8 +188,6 @@ namespace TEN::Entities::TR1 if (item->HitStatus || AI.distance < APE_PANIC_RANGE) creatureInfo->Flags |= APE_FLAG_ATTACK; - short random; - switch (item->Animation.ActiveState) { case APE_STATE_IDLE: @@ -207,14 +209,13 @@ namespace TEN::Entities::TR1 else if (!(creatureInfo->Flags & APE_FLAG_ATTACK) && AI.zoneNumber == AI.enemyZone && AI.ahead) { - random = (short)(GetRandomControl() / 32); - if (random < APE_JUMP_CHANCE) + if (TestProbability(APE_IDLE_JUMP_CHANCE)) item->Animation.TargetState = APE_STATE_JUMP; - else if (random < APE_POUND_CHEST_CHANCE) + else if (TestProbability(APE_IDLE_POUND_CHEST_CHANCE)) item->Animation.TargetState = APE_STATE_POUND_CHEST; - else if (random < APE_POUND_GROUND_CHANCE) + else if (TestProbability(APE_IDLE_POUND_GROUND_CHANCE)) item->Animation.TargetState = APE_STATE_POUND_GROUND; - else if (random < APE_RUN_LEFT_CHANCE) + else if (TestProbability(APE_IDLE_RUN_LEFT_CHANCE)) { item->Animation.TargetState = APE_STATE_RUN_LEFT; creatureInfo->MaxTurn = 0; @@ -246,18 +247,17 @@ namespace TEN::Entities::TR1 } else if (creatureInfo->Mood != MoodType::Escape) { - random = (short)GetRandomControl(); - if (random < APE_JUMP_CHANCE) + if (TestProbability(APE_RUN_JUMP_CHANCE)) { item->Animation.RequiredState = APE_STATE_JUMP; item->Animation.TargetState = APE_STATE_IDLE; } - else if (random < APE_POUND_CHEST_CHANCE) + else if (TestProbability(APE_RUN_POUND_CHEST_CHANCE)) { item->Animation.RequiredState = APE_STATE_POUND_CHEST; item->Animation.TargetState = APE_STATE_IDLE; } - else if (random < APE_POUND_GROUND_CHANCE) + else if (TestProbability(APE_RUN_POUND_GROUND_CHANCE)) { item->Animation.RequiredState = APE_STATE_POUND_GROUND; item->Animation.TargetState = APE_STATE_IDLE; diff --git a/TombEngine/Objects/TR1/Entity/tr1_ape.h b/TombEngine/Objects/TR1/Entity/tr1_ape.h index 9edc287ab..081f18fdf 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_ape.h +++ b/TombEngine/Objects/TR1/Entity/tr1_ape.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { void ApeControl(short itemNumber); } diff --git a/TombEngine/Objects/TR1/Entity/tr1_bear.cpp b/TombEngine/Objects/TR1/Entity/tr1_bear.cpp index 0038f9e06..5f673a50f 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_bear.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_bear.cpp @@ -9,11 +9,13 @@ #include "Game/Lara/lara.h" #include "Game/misc.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/setup.h" +using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { constexpr auto BEAR_RUN_DAMAGE = 3; constexpr auto BEAR_ATTACK_DAMAGE = 200; @@ -24,10 +26,10 @@ namespace TEN::Entities::TR1 constexpr auto BEAR_REAR_RANGE = SECTOR(2); constexpr auto BEAR_REAR_SWIPE_ATTACK_RANGE = SECTOR(0.6f); constexpr auto BEAR_EAT_RANGE = CLICK(3); - - constexpr auto BEAR_ROAR_CHANCE = 0x50; - constexpr auto BEAR_REAR_CHANCE = 0x300; - constexpr auto BEAR_DROP_CHANCE = 0x600; + + constexpr auto BEAR_ROAR_CHANCE = 1.0f / 400; + constexpr auto BEAR_REAR_CHANCE = 1.0f / 40; + constexpr auto BEAR_DROP_CHANCE = 1.0f / 22; #define BEAR_WALK_TURN_RATE_MAX ANGLE(2.0f) #define BEAR_RUN_TURN_RATE_MAX ANGLE(5.0f) @@ -81,8 +83,8 @@ namespace TEN::Entities::TR1 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short head = 0; short angle = 0; + short head = 0; if (item->HitPoints <= 0) { @@ -117,8 +119,8 @@ namespace TEN::Entities::TR1 { if (creature->Flags && item->TestBits(JointBitType::Touch, BearAttackJoints)) { - creature->Flags = 0; DoDamage(creature->Enemy, BEAR_SLAM_DAMAGE); + creature->Flags = 0; } break; @@ -141,12 +143,12 @@ namespace TEN::Entities::TR1 if (item->HitStatus) creature->Flags = 1; - const bool laraDead = LaraItem->HitPoints <= 0; + bool isLaraDead = LaraItem->HitPoints <= 0; switch (item->Animation.ActiveState) { case BEAR_STATE_IDLE: - if (laraDead) + if (isLaraDead) { if (AI.bite && AI.distance < pow(BEAR_EAT_RANGE, 2)) item->Animation.TargetState = BEAR_STATE_EAT; @@ -165,7 +167,7 @@ namespace TEN::Entities::TR1 case BEAR_STATE_STROLL: creature->MaxTurn = BEAR_WALK_TURN_RATE_MAX; - if (laraDead && item->TestBits(JointBitType::Touch, BearAttackJoints) && AI.ahead) + if (isLaraDead && item->TestBits(JointBitType::Touch, BearAttackJoints) && AI.ahead) item->Animation.TargetState = BEAR_STATE_IDLE; else if (creature->Mood != MoodType::Bored) { @@ -174,10 +176,10 @@ namespace TEN::Entities::TR1 if (creature->Mood == MoodType::Escape) item->Animation.RequiredState = BEAR_STATE_STROLL; } - else if (GetRandomControl() < BEAR_ROAR_CHANCE) + else if (TestProbability(BEAR_ROAR_CHANCE)) { - item->Animation.RequiredState = BEAR_STATE_ROAR; item->Animation.TargetState = BEAR_STATE_IDLE; + item->Animation.RequiredState = BEAR_STATE_ROAR; } break; @@ -186,16 +188,14 @@ namespace TEN::Entities::TR1 creature->MaxTurn = BEAR_RUN_TURN_RATE_MAX; if (item->TestBits(JointBitType::Touch, BearAttackJoints)) - { DoDamage(creature->Enemy, BEAR_RUN_DAMAGE); - } - if (creature->Mood == MoodType::Bored || laraDead) + if (creature->Mood == MoodType::Bored || isLaraDead) item->Animation.TargetState = BEAR_STATE_IDLE; else if (AI.ahead && !item->Animation.RequiredState) { if (AI.distance < pow(BEAR_REAR_RANGE, 2) && - GetRandomControl() < BEAR_REAR_CHANCE && + TestProbability(BEAR_REAR_CHANCE) && !creature->Flags) { item->Animation.RequiredState = BEAR_STATE_REAR; @@ -227,8 +227,8 @@ namespace TEN::Entities::TR1 case BEAR_STATE_WALK_FORWARD: if (creature->Flags) { - item->Animation.RequiredState = BEAR_STATE_STROLL; item->Animation.TargetState = BEAR_STATE_REAR; + item->Animation.RequiredState = BEAR_STATE_STROLL; } else if (AI.ahead && item->TestBits(JointBitType::Touch, BearAttackJoints)) item->Animation.TargetState = BEAR_STATE_REAR; @@ -237,15 +237,15 @@ namespace TEN::Entities::TR1 item->Animation.TargetState = BEAR_STATE_REAR; item->Animation.RequiredState = BEAR_STATE_STROLL; } - else if (creature->Mood == MoodType::Bored || GetRandomControl() < BEAR_ROAR_CHANCE) + else if (creature->Mood == MoodType::Bored || TestProbability(BEAR_ROAR_CHANCE)) { + item->Animation.TargetState = BEAR_STATE_REAR; item->Animation.RequiredState = BEAR_STATE_ROAR; - item->Animation.TargetState = BEAR_STATE_REAR; } - else if (AI.distance > pow(BEAR_REAR_RANGE, 2) || GetRandomControl() < BEAR_DROP_CHANCE) + else if (AI.distance > pow(BEAR_REAR_RANGE, 2) || TestProbability(BEAR_DROP_CHANCE)) { - item->Animation.RequiredState = BEAR_STATE_IDLE; item->Animation.TargetState = BEAR_STATE_REAR; + item->Animation.RequiredState = BEAR_STATE_IDLE; } break; @@ -264,8 +264,8 @@ namespace TEN::Entities::TR1 if (!item->Animation.RequiredState && item->TestBits(JointBitType::Touch, BearAttackJoints)) { - CreatureEffect(item, BearBite, DoBloodSplat); DoDamage(creature->Enemy, BEAR_ATTACK_DAMAGE); + CreatureEffect(item, BearBite, DoBloodSplat); item->Animation.RequiredState = BEAR_STATE_IDLE; } diff --git a/TombEngine/Objects/TR1/Entity/tr1_bear.h b/TombEngine/Objects/TR1/Entity/tr1_bear.h index dcfd85eb9..24107b33d 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_bear.h +++ b/TombEngine/Objects/TR1/Entity/tr1_bear.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { void BearControl(short itemNumber); } diff --git a/TombEngine/Objects/TR1/Entity/tr1_big_rat.cpp b/TombEngine/Objects/TR1/Entity/tr1_big_rat.cpp index 69e96d258..54c449a61 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_big_rat.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_big_rat.cpp @@ -16,7 +16,7 @@ using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { constexpr auto BIG_RAT_BITE_ATTACK_DAMAGE = 20; constexpr auto BIG_RAT_POUNCE_ATTACK_DAMAGE = 25; @@ -27,7 +27,7 @@ namespace TEN::Entities::TR1 constexpr auto BIG_RAT_POUNCE_ATTACK_RANGE = SQUARE(SECTOR(0.5f)); constexpr auto BIG_RAT_WATER_BITE_ATTACK_RANGE = SQUARE(SECTOR(0.3f)); - constexpr auto BIG_RAT_REAR_POSE_CHANCE = 0.008f; + constexpr auto BIG_RAT_REAR_POSE_CHANCE = 1.0f / 128; constexpr auto BIG_RAT_SWIM_UP_DOWN_SPEED = 32; constexpr auto BIG_RAT_WATER_SURFACE_OFFSET = 10; @@ -115,10 +115,11 @@ namespace TEN::Entities::TR1 auto* creature = GetCreatureInfo(item); int waterHeight = GetRatWaterHeight(item); - short head = 0; - short angle = 0; bool isOnWater = waterHeight != NO_HEIGHT; + short angle = 0; + short head = 0; + if (item->HitPoints <= 0) { if (item->Animation.ActiveState != BIG_RAT_STATE_LAND_DEATH && @@ -183,9 +184,9 @@ namespace TEN::Entities::TR1 if (!item->Animation.RequiredState && AI.ahead && item->TestBits(JointBitType::Touch, BigRatBite.meshNum)) { - item->Animation.RequiredState = BIG_RAT_STATE_IDLE; DoDamage(creature->Enemy, BIG_RAT_BITE_ATTACK_DAMAGE); CreatureEffect(item, BigRatBite, DoBloodSplat); + item->Animation.RequiredState = BIG_RAT_STATE_IDLE; } break; @@ -194,9 +195,9 @@ namespace TEN::Entities::TR1 if (!item->Animation.RequiredState && AI.ahead && item->TestBits(JointBitType::Touch, BigRatBite.meshNum)) { - item->Animation.RequiredState = BIG_RAT_STATE_RUN_FORWARD; DoDamage(creature->Enemy, BIG_RAT_POUNCE_ATTACK_DAMAGE); CreatureEffect(item, BigRatBite, DoBloodSplat); + item->Animation.RequiredState = BIG_RAT_STATE_RUN_FORWARD; } break; diff --git a/TombEngine/Objects/TR1/Entity/tr1_big_rat.h b/TombEngine/Objects/TR1/Entity/tr1_big_rat.h index bdf2afe97..d0e38f9a6 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_big_rat.h +++ b/TombEngine/Objects/TR1/Entity/tr1_big_rat.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { void InitialiseBigRat(short itemNumber); void BigRatControl(short itemNumber); diff --git a/TombEngine/Objects/TR1/Entity/tr1_centaur.cpp b/TombEngine/Objects/TR1/Entity/tr1_centaur.cpp index 27f76ce0a..765df9f7a 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_centaur.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_centaur.cpp @@ -15,21 +15,23 @@ #include "Game/people.h" #include "Sound/sound.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/setup.h" +using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { - constexpr auto CENTAUR_REAR_DAMAGE = 200; - constexpr auto CENTAUR_REAR_RANGE = SECTOR(1.5f); - constexpr auto CENTAUR_REAR_CHANCE = 0x60; + constexpr auto CENTAUR_REAR_DAMAGE = 200; + constexpr auto CENTAUR_REAR_RANGE = SECTOR(1.5f); + constexpr auto CENTAUR_REAR_CHANCE = 1.0f / 340; constexpr auto CENTAUR_BOMB_VELOCITY = 20; #define CENTAUR_TURN_RATE_MAX ANGLE(4.0f) const auto CentaurRocketBite = BiteInfo(Vector3(11.0f, 415.0f, 41.0f), 13); - const auto CentaurRearBite = BiteInfo(Vector3(50.0f, 30.0f, 0.0f), 5); + const auto CentaurRearBite = BiteInfo(Vector3(50.0f, 30.0f, 0.0f), 5); const vector CentaurAttackJoints = { 0, 3, 4, 7, 8, 16, 17 }; enum CentaurState @@ -56,17 +58,14 @@ namespace TEN::Entities::TR1 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short head = 0; + short angle = 0; + short head = 0; if (item->HitPoints <= 0) { if (item->Animation.ActiveState != CENTAUR_STATE_DEATH) - { - item->Animation.AnimNumber = Objects[ID_CENTAUR_MUTANT].animIndex + CENTAUR_ANIM_DEATH; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = CENTAUR_STATE_DEATH; - } + SetAnimation(item, CENTAUR_ANIM_DEATH); } else { @@ -98,18 +97,18 @@ namespace TEN::Entities::TR1 case CENTAUR_STATE_RUN_FORWARD: if (AI.bite && AI.distance < pow(CENTAUR_REAR_RANGE, 2)) { - item->Animation.RequiredState = CENTAUR_STATE_WARNING; item->Animation.TargetState = CENTAUR_STATE_IDLE; + item->Animation.RequiredState = CENTAUR_STATE_WARNING; } else if (Targetable(item, &AI)) { + item->Animation.TargetState = CENTAUR_STATE_IDLE; item->Animation.RequiredState = CENTAUR_STATE_AIM; - item->Animation.TargetState = CENTAUR_STATE_IDLE; } - else if (GetRandomControl() < CENTAUR_REAR_CHANCE) + else if (TestProbability(CENTAUR_REAR_CHANCE)) { - item->Animation.RequiredState = CENTAUR_STATE_WARNING; item->Animation.TargetState = CENTAUR_STATE_IDLE; + item->Animation.RequiredState = CENTAUR_STATE_WARNING; } break; @@ -137,8 +136,8 @@ namespace TEN::Entities::TR1 if (!item->Animation.RequiredState && item->TestBits(JointBitType::Touch, CentaurAttackJoints)) { - CreatureEffect(item, CentaurRearBite, DoBloodSplat); DoDamage(creature->Enemy, CENTAUR_REAR_DAMAGE); + CreatureEffect(item, CentaurRearBite, DoBloodSplat); item->Animation.RequiredState = CENTAUR_STATE_IDLE; } diff --git a/TombEngine/Objects/TR1/Entity/tr1_centaur.h b/TombEngine/Objects/TR1/Entity/tr1_centaur.h index ebf3a4984..cf34267d2 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_centaur.h +++ b/TombEngine/Objects/TR1/Entity/tr1_centaur.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { constexpr auto SHARD_VELOCITY = 250; constexpr auto BOMB_VELOCITY = 220; diff --git a/TombEngine/Objects/TR1/Entity/tr1_doppelganger.cpp b/TombEngine/Objects/TR1/Entity/tr1_doppelganger.cpp index 828dbc715..29e45ce8e 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_doppelganger.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_doppelganger.cpp @@ -14,7 +14,7 @@ // - Bacon Lara cannot be targeted. // - Bacon Lara cannot move like Lara. -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { // Original: void InitialiseDoppelganger(short itemNumber) @@ -80,26 +80,24 @@ namespace TEN::Entities::TR1 int laraFloorHeight = GetCollision(LaraItem).Position.Floor; // Animate bacon Lara, mirroring Lara's position. - item->Animation.FrameNumber = LaraItem->Animation.FrameNumber; item->Animation.AnimNumber = LaraItem->Animation.AnimNumber; - item->Pose.Position.x = pos.x; - item->Pose.Position.y = pos.y; - item->Pose.Position.z = pos.z; + item->Animation.FrameNumber = LaraItem->Animation.FrameNumber; + item->Pose.Position = pos; item->Pose.Orientation.x = LaraItem->Pose.Orientation.x; item->Pose.Orientation.y = LaraItem->Pose.Orientation.y - ANGLE(180.0f); item->Pose.Orientation.z = LaraItem->Pose.Orientation.z; ItemNewRoom(itemNumber, LaraItem->RoomNumber); // Compare floor heights. - if (item->Floor >= laraFloorHeight + SECTOR(1) + 1 && // Add 1 to avoid bacon Lara dying when exiting water. + if (item->Floor >= (laraFloorHeight + SECTOR(1) + 1) && // Add 1 to avoid bacon Lara dying when exiting water. !LaraItem->Animation.IsAirborne) { SetAnimation(item, LA_JUMP_WALL_SMASH_START); - item->Animation.Velocity.z = 0; - item->Animation.Velocity.y = 0; item->Animation.IsAirborne = true; - item->Data = -1; + item->Animation.Velocity.y = 0.0f; + item->Animation.Velocity.z = 0.0f; item->Pose.Position.y += 50; + item->Data = -1; } } @@ -114,8 +112,8 @@ namespace TEN::Entities::TR1 item->Pose.Position.y = item->Floor; TestTriggers(item, true); - item->Animation.Velocity.y = 0; item->Animation.IsAirborne = false; + item->Animation.Velocity.y = 0.0f; item->Animation.TargetState = LS_DEATH; item->Animation.RequiredState = LS_DEATH; } diff --git a/TombEngine/Objects/TR1/Entity/tr1_doppelganger.h b/TombEngine/Objects/TR1/Entity/tr1_doppelganger.h index 0c44a7406..d0b6df5ed 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_doppelganger.h +++ b/TombEngine/Objects/TR1/Entity/tr1_doppelganger.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { void InitialiseDoppelganger(short itemNumber); void DoppelgangerControl(short itemNumber); diff --git a/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.cpp b/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.cpp index 7061059ec..5039b4b4f 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.cpp @@ -12,11 +12,13 @@ #include "Game/misc.h" #include "Sound/sound.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/setup.h" +using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { constexpr auto MUTANT_ATTACK_DAMAGE = 500; constexpr auto MUTANT_CONTACT_DAMAGE = 6; @@ -24,16 +26,17 @@ namespace TEN::Entities::TR1 constexpr auto MUTANT_ATTACK_RANGE = SQUARE(SECTOR(2.5f)); constexpr auto MUTANT_CLOSE_RANGE = SQUARE(SECTOR(2.2f)); - constexpr auto MUTANT_ATTACK_1_CHANCE = 0x2AF8; - constexpr auto MUTANT_ATTACK_2_CHANCE = 0x55F0; + // TODO: Unused. + constexpr auto MUTANT_ATTACK_1_CHANCE = 1.0f / 3.0f; + constexpr auto MUTANT_ATTACK_2_CHANCE = MUTANT_ATTACK_1_CHANCE * 2; #define MUTANT_NEED_TURN ANGLE(45.0f) #define MUTANT_TURN ANGLE(3.0f) #define LARA_GIANT_MUTANT_DEATH 6 // TODO: Not 13? Check this. - const vector MutantAttackJoints = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; - const vector MutantAttackLeftJoints = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; + const vector MutantAttackJoints = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; + const vector MutantAttackLeftJoint = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; const vector MutantAttackRightJoints = { 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; enum GiantMutantState @@ -66,17 +69,13 @@ namespace TEN::Entities::TR1 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short head = 0; short angle = 0; + short head = 0; if (item->HitPoints <= 0) { if (item->Animation.ActiveState != MUTANT_STATE_DEATH) - { - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + MUTANT_ANIM_DEATH; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = MUTANT_STATE_DEATH; - } + SetAnimation(item, MUTANT_ANIM_DEATH); } else { @@ -92,9 +91,7 @@ namespace TEN::Entities::TR1 angle = (short)phd_atan(creature->Target.z - item->Pose.Position.z, creature->Target.x - item->Pose.Position.x) - item->Pose.Orientation.y; if (item->TouchBits) - { DoDamage(creature->Enemy, MUTANT_CONTACT_DAMAGE); - } switch (item->Animation.ActiveState) { @@ -122,7 +119,7 @@ namespace TEN::Entities::TR1 else item->Animation.TargetState = MUTANT_STATE_FORWARD; } - else if (GetRandomControl() < 0x4000) + else if (TestProbability(0.5f)) item->Animation.TargetState = MUTANT_STATE_ATTACK_1; else item->Animation.TargetState = MUTANT_STATE_ATTACK_2; @@ -178,8 +175,8 @@ namespace TEN::Entities::TR1 case MUTANT_STATE_ATTACK_1: if (!creature->Flags && item->TestBits(JointBitType::Touch, MutantAttackRightJoints)) { - creature->Flags = 1; DoDamage(creature->Enemy, MUTANT_ATTACK_DAMAGE); + creature->Flags = 1; } break; @@ -187,8 +184,8 @@ namespace TEN::Entities::TR1 case MUTANT_STATE_ATTACK_2: if (!creature->Flags && item->TestBits(JointBitType::Touch, MutantAttackJoints)) { - creature->Flags = 1; DoDamage(creature->Enemy, MUTANT_ATTACK_DAMAGE); + creature->Flags = 1; } break; @@ -202,14 +199,11 @@ namespace TEN::Entities::TR1 LaraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_GIANT_MUTANT_DEATH; LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase; - LaraItem->Animation.ActiveState = LaraItem->Animation.TargetState = 46; - LaraItem->RoomNumber = item->RoomNumber; - LaraItem->Pose.Position.x = item->Pose.Position.x; - LaraItem->Pose.Position.y = item->Pose.Position.y; - LaraItem->Pose.Position.z = item->Pose.Position.z; - LaraItem->Pose.Orientation.y = item->Pose.Orientation.y; - LaraItem->Pose.Orientation.x = LaraItem->Pose.Orientation.z = 0; + LaraItem->Animation.ActiveState = 46; + LaraItem->Animation.TargetState = 46; LaraItem->Animation.IsAirborne = false; + LaraItem->Pose = PHD_3DPOS(item->Pose.Position, 0, item->Pose.Orientation.y, 0); + LaraItem->RoomNumber = item->RoomNumber; LaraItem->HitPoints = -1; Lara.Air = -1; Lara.Control.HandStatus = HandStatus::Busy; diff --git a/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.h b/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.h index 67c8af943..7c34920d3 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.h +++ b/TombEngine/Objects/TR1/Entity/tr1_giant_mutant.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { void GiantMutantControl(short itemNumber); } diff --git a/TombEngine/Objects/TR1/Entity/tr1_natla.cpp b/TombEngine/Objects/TR1/Entity/tr1_natla.cpp index 503b169d5..31aaf4f5f 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_natla.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_natla.cpp @@ -10,9 +10,12 @@ #include "Game/people.h" #include "Sound/sound.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/trmath.h" -namespace TEN::Entities::TR1 +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR1 { // TODO: Organise. constexpr auto NATLA_SHOT_DAMAGE = 100; @@ -20,9 +23,10 @@ namespace TEN::Entities::TR1 constexpr auto NATLA_DEATH_TIME = (FPS * 16); // 16 seconds. constexpr auto NATLA_FLYMODE = 0x8000; constexpr auto NATLA_TIMER = 0x7FFF; - constexpr auto NATLA_LAND_CHANCE = 0x100; constexpr auto NATLA_GUN_VELOCITY = 400; + constexpr auto NATLA_LAND_CHANCE = 0.008f; + #define NATLA_TURN_NEAR_DEATH_SPEED ANGLE(6.0f) #define NATLA_TURN_SPEED ANGLE(5.0f) #define NATLA_FLY_ANGLE_SPEED ANGLE(5.0f) @@ -183,7 +187,7 @@ namespace TEN::Entities::TR1 if (item->Animation.ActiveState == NATLA_STATE_FLY && (creature->Flags & NATLA_FLYMODE)) { - if (creature->Flags & NATLA_FLYMODE && shoot && GetRandomControl() < NATLA_LAND_CHANCE) + if (creature->Flags & NATLA_FLYMODE && shoot && TestProbability(NATLA_LAND_CHANCE)) creature->Flags -= NATLA_FLYMODE; if (!(creature->Flags & NATLA_FLYMODE)) diff --git a/TombEngine/Objects/TR1/Entity/tr1_natla.h b/TombEngine/Objects/TR1/Entity/tr1_natla.h index 1140021da..1cbebf286 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_natla.h +++ b/TombEngine/Objects/TR1/Entity/tr1_natla.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { void NatlaControl(short itemNumber); } diff --git a/TombEngine/Objects/TR1/Entity/tr1_winged_mutant.cpp b/TombEngine/Objects/TR1/Entity/tr1_winged_mutant.cpp index 1e84f91f0..7cfa58e28 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_winged_mutant.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_winged_mutant.cpp @@ -13,11 +13,13 @@ #include "Game/people.h" #include "Sound/sound.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/trmath.h" +using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { constexpr auto WINGED_MUTANT_IDLE_JUMP_ATTACK_DAMAGE = 150; constexpr auto WINGED_MUTANT_RUN_JUMP_ATTACK_DAMAGE = 100; @@ -29,8 +31,8 @@ namespace TEN::Entities::TR1 constexpr auto WINGED_MUTANT_IDLE_JUMP_ATTACK_RANGE = SQUARE(SECTOR(2.5f)); constexpr auto WINGED_MUTANT_ATTACK_RANGE = SQUARE(SECTOR(3.75f)); - constexpr auto WINGED_MUTANT_POSE_CHANCE = 85; - constexpr auto WINGED_MUTANT_UNPOSE_CHANCE = 200; + constexpr auto WINGED_MUTANT_POSE_CHANCE = 1.0f / 400; + constexpr auto WINGED_MUTANT_UNPOSE_CHANCE = 1.0f / 164; constexpr auto WINGED_MUTANT_FLY_VELOCITY = CLICK(1) / 8; constexpr auto WINGED_MUTANT_SHARD_VELOCITY = 250; @@ -337,7 +339,7 @@ namespace TEN::Entities::TR1 if (AI.distance < WINGED_MUTANT_WALK_RANGE) { if (AI.zoneNumber == AI.enemyZone || - GetRandomControl() < WINGED_MUTANT_UNPOSE_CHANCE) + TestProbability(WINGED_MUTANT_UNPOSE_CHANCE)) { item->Animation.TargetState = WMUTANT_STATE_WALK_FORWARD; } @@ -345,7 +347,7 @@ namespace TEN::Entities::TR1 else item->Animation.TargetState = WMUTANT_STATE_IDLE; } - else if (creature->Mood == MoodType::Bored && GetRandomControl() < WINGED_MUTANT_UNPOSE_CHANCE) + else if (creature->Mood == MoodType::Bored && TestProbability(WINGED_MUTANT_UNPOSE_CHANCE)) item->Animation.TargetState = WMUTANT_STATE_WALK_FORWARD; else if (creature->Mood == MoodType::Attack || creature->Mood == MoodType::Escape) @@ -363,7 +365,7 @@ namespace TEN::Entities::TR1 else if (creature->Mood == MoodType::Bored || (creature->Mood == MoodType::Stalk && AI.zoneNumber != AI.enemyZone)) { - if (GetRandomControl() < WINGED_MUTANT_POSE_CHANCE) + if (TestProbability(WINGED_MUTANT_POSE_CHANCE)) item->Animation.TargetState = WMUTANT_STATE_POSE; } else if (creature->Mood == MoodType::Stalk && diff --git a/TombEngine/Objects/TR1/Entity/tr1_winged_mutant.h b/TombEngine/Objects/TR1/Entity/tr1_winged_mutant.h index d8d9f8688..e637dead4 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_winged_mutant.h +++ b/TombEngine/Objects/TR1/Entity/tr1_winged_mutant.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { void InitialiseWingedMutant(short itemNumber); void WingedMutantControl(short itemNumber); diff --git a/TombEngine/Objects/TR1/Entity/tr1_wolf.cpp b/TombEngine/Objects/TR1/Entity/tr1_wolf.cpp index 20ab213b8..554a59c87 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_wolf.cpp +++ b/TombEngine/Objects/TR1/Entity/tr1_wolf.cpp @@ -9,11 +9,13 @@ #include "Game/Lara/lara.h" #include "Game/misc.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/setup.h" +using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { constexpr auto WOLF_BITE_DAMAGE = 100; constexpr auto WOLF_LUNGE_DAMAGE = 50; @@ -21,9 +23,9 @@ namespace TEN::Entities::TR1 constexpr auto WOLF_ATTACK_RANGE = SQUARE(SECTOR(1.5f)); constexpr auto WOLF_STALK_RANGE = SQUARE(SECTOR(2)); - constexpr auto WOLF_WAKE_CHANCE = 0x20; - constexpr auto WOLF_SLEEP_CHANCE = 0x20; - constexpr auto WOLF_HOWL_CHANCE = 0x180; + constexpr auto WOLF_WAKE_CHANCE = 1.0f / 1000; + constexpr auto WOLF_SLEEP_CHANCE = 1.0f / 1000; + constexpr auto WOLF_HOWL_CHANCE = 1.0f / 85; constexpr auto WOLF_SLEEP_FRAME = 96; @@ -110,7 +112,7 @@ namespace TEN::Entities::TR1 item->Animation.RequiredState = WOLF_STATE_CROUCH; item->Animation.TargetState = WOLF_STATE_IDLE; } - else if (GetRandomControl() < WOLF_WAKE_CHANCE) + else if (TestProbability(WOLF_WAKE_CHANCE)) { item->Animation.RequiredState = WOLF_STATE_WALK; item->Animation.TargetState = WOLF_STATE_IDLE; @@ -133,7 +135,7 @@ namespace TEN::Entities::TR1 item->Animation.TargetState = WOLF_STATE_STALK; item->Animation.RequiredState = WOLF_STATE_NONE; } - else if (GetRandomControl() < WOLF_SLEEP_CHANCE) + else if (TestProbability(WOLF_SLEEP_CHANCE)) { item->Animation.RequiredState = WOLF_STATE_SLEEP; item->Animation.TargetState = WOLF_STATE_IDLE; @@ -174,7 +176,7 @@ namespace TEN::Entities::TR1 item->Animation.TargetState = WOLF_STATE_RUN; } } - else if (GetRandomControl() < WOLF_HOWL_CHANCE) + else if (TestProbability(WOLF_HOWL_CHANCE)) { item->Animation.RequiredState = WOLF_STATE_HOWL; item->Animation.TargetState = WOLF_STATE_CROUCH; diff --git a/TombEngine/Objects/TR1/Entity/tr1_wolf.h b/TombEngine/Objects/TR1/Entity/tr1_wolf.h index c9a4ec7ec..38b83ab1c 100644 --- a/TombEngine/Objects/TR1/Entity/tr1_wolf.h +++ b/TombEngine/Objects/TR1/Entity/tr1_wolf.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR1 +namespace TEN::Entities::Creatures::TR1 { void InitialiseWolf(short itemNumber); void WolfControl(short itemNumber); diff --git a/TombEngine/Objects/TR1/tr1_objects.cpp b/TombEngine/Objects/TR1/tr1_objects.cpp index bd5211126..7a8b0c68f 100644 --- a/TombEngine/Objects/TR1/tr1_objects.cpp +++ b/TombEngine/Objects/TR1/tr1_objects.cpp @@ -1,7 +1,6 @@ #include "framework.h" #include "Objects/TR1/tr1_objects.h" -/// necessary import #include "Game/control/box.h" #include "Game/collision/collide_item.h" #include "Game/itemdata/creature_info.h" @@ -9,7 +8,7 @@ #include "Specific/setup.h" #include "Specific/level.h" -/// entities +// Creatures #include "Objects/TR1/Entity/tr1_ape.h" // OK #include "Objects/TR1/Entity/tr1_bear.h" // OK #include "Objects/TR1/Entity/tr1_doppelganger.h" // OK @@ -21,7 +20,7 @@ #include "Objects/TR1/Entity/tr1_winged_mutant.h" #include "Objects/Utils/object_helper.h" -using namespace TEN::Entities::TR1; +using namespace TEN::Entities::Creatures::TR1; static void StartEntity(ObjectInfo* obj) { @@ -78,7 +77,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_APE; + obj->ZoneType = ZoneType::Ape; } obj = &Objects[ID_BIG_RAT]; @@ -98,7 +97,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveAnim = true; obj->saveFlags = true; obj->waterCreature = true; - obj->zoneType = ZONE_WATER; + obj->ZoneType = ZoneType::Water; obj->SetBoneRotation(1, ROT_Y); // head } @@ -174,7 +173,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_BLOCKABLE; + obj->ZoneType = ZoneType::Blockable; obj->SetBoneRotation(10, ROT_X | ROT_Y); } @@ -194,7 +193,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveFlags = true; obj->saveHitpoints = true; obj->savePosition = true; - obj->zoneType = ZONE_FLYER; + obj->ZoneType = ZoneType::Flyer; obj->SetBoneRotation(1, ROT_Y); // torso obj->SetBoneRotation(2, ROT_Y); // head } diff --git a/TombEngine/Objects/TR2/Entity/tr2_barracuda.cpp b/TombEngine/Objects/TR2/Entity/tr2_barracuda.cpp index 4346a201c..99b759bbf 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_barracuda.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_barracuda.cpp @@ -12,7 +12,7 @@ using std::vector; -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { constexpr auto BARRACUDA_ATTACK_DAMAGE = 100; constexpr auto BARRACUDA_IDLE_ATTACK_RANGE = SQUARE(SECTOR(0.67f)); @@ -23,7 +23,7 @@ namespace TEN::Entities::TR2 enum BarracudaState { - BARRACUDA_STATE_NONE = 0, + // No state 0. BARRACUDA_STATE_IDLE = 1, BARRACUDA_STATE_SWIM_SLOW = 2, BARRACUDA_STATE_SWIM_FAST = 3, diff --git a/TombEngine/Objects/TR2/Entity/tr2_barracuda.h b/TombEngine/Objects/TR2/Entity/tr2_barracuda.h index dfde88ccf..64ddf1939 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_barracuda.h +++ b/TombEngine/Objects/TR2/Entity/tr2_barracuda.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void BarracudaControl(short itemNumber); } diff --git a/TombEngine/Objects/TR2/Entity/tr2_bird_monster.cpp b/TombEngine/Objects/TR2/Entity/tr2_bird_monster.cpp index 1d56afbcf..7c98cdb70 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_bird_monster.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_bird_monster.cpp @@ -14,7 +14,7 @@ using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { constexpr auto BIRD_MONSTER_ATTACK_DAMAGE = 200; constexpr auto BIRD_MONSTER_SLAM_CRUSH_ATTACK_RANGE = SQUARE(SECTOR(1)); @@ -29,7 +29,7 @@ namespace TEN::Entities::TR2 enum BirdMonsterState { - BMONSTER_STATE_NONE = 0, + // No state 0. BMONSTER_STATE_IDLE = 1, BMONSTER_STATE_WALK_FORWARD = 2, BMONSTER_STATE_SLAM_ATTACK_START = 3, diff --git a/TombEngine/Objects/TR2/Entity/tr2_bird_monster.h b/TombEngine/Objects/TR2/Entity/tr2_bird_monster.h index 5756af247..3c97478ce 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_bird_monster.h +++ b/TombEngine/Objects/TR2/Entity/tr2_bird_monster.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void BirdMonsterControl(short itemNumber); } diff --git a/TombEngine/Objects/TR2/Entity/tr2_dragon.cpp b/TombEngine/Objects/TR2/Entity/tr2_dragon.cpp index 8bb783680..129af7a09 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_dragon.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_dragon.cpp @@ -18,28 +18,23 @@ #include "Specific/setup.h" using namespace TEN::Input; +using std::vector; -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { - const auto DragonMouthBite = BiteInfo(Vector3(35.0f, 171.0f, 1168.0f), 12); - constexpr auto DRAGON_SWIPE_ATTACK_DAMAGE = 250; - constexpr auto DRAGON_TOUCH_DAMAGE = 10; - + constexpr auto DRAGON_CONTACT_DAMAGE = 10; + const auto DragonMouthBite = BiteInfo(Vector3(35.0f, 171.0f, 1168.0f), 12); + const vector DragonSwipeAttackJointsLeft = { 24, 25, 26, 27, 28, 29, 30 }; + const vector DragonSwipeAttackJointsRight = { 1, 2, 3, 4, 5, 6, 7 }; // TODO: Organise. - #define DRAGON_SWIPE_DAMAGE 250 - #define DRAGON_TOUCH_DAMAGE 10 - #define DRAGON_LIVE_TIME (30 * 11) #define DRAGON_CLOSE_RANGE pow(SECTOR(3), 2) #define DRAGON_STATE_IDLE_RANGE pow(SECTOR(6), 2) #define DRAGON_FLAME_SPEED 200 - #define DRAGON_TOUCH_R 0x0fe - #define DRAGON_TOUCH_L 0x7f000000 - #define DRAGON_ALMOST_LIVE 100 #define BOOM_TIME 130 #define BOOM_TIME_MIDDLE 140 @@ -58,7 +53,7 @@ namespace TEN::Entities::TR2 enum DragonState { - DRAGON_STATE_NONE = 0, + // No state 0. DRAGON_STATE_WALK = 1, DRAGON_STATE_LEFT = 2, DRAGON_STATE_RIGHT = 3, @@ -255,8 +250,8 @@ namespace TEN::Entities::TR2 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short head = 0; short angle = 0; + short head = 0; bool ahead; @@ -318,9 +313,7 @@ namespace TEN::Entities::TR2 ahead = (AI.ahead && AI.distance > DRAGON_CLOSE_RANGE && AI.distance < DRAGON_STATE_IDLE_RANGE); if (item->TouchBits) - { - DoDamage(creature->Enemy, DRAGON_TOUCH_DAMAGE); - } + DoDamage(creature->Enemy, DRAGON_CONTACT_DAMAGE); switch (item->Animation.ActiveState) { @@ -350,19 +343,19 @@ namespace TEN::Entities::TR2 break; case DRAGON_STATE_SWIPE_LEFT: - if (item->TouchBits & DRAGON_TOUCH_L) + if (item->TestBits(JointBitType::Touch, DragonSwipeAttackJointsLeft)) { + DoDamage(creature->Enemy, DRAGON_SWIPE_ATTACK_DAMAGE); creature->Flags = 0; - DoDamage(creature->Enemy, DRAGON_SWIPE_DAMAGE); } break; case DRAGON_STATE_SWIPE_RIGHT: - if (item->TouchBits & DRAGON_TOUCH_R) + if (item->TestBits(JointBitType::Touch, DragonSwipeAttackJointsRight)) { + DoDamage(creature->Enemy, DRAGON_SWIPE_ATTACK_DAMAGE); creature->Flags = 0; - DoDamage(creature->Enemy, DRAGON_SWIPE_DAMAGE); } break; @@ -404,13 +397,11 @@ namespace TEN::Entities::TR2 case DRAGON_STATE_TURN_LEFT: item->Pose.Orientation.y += -(ANGLE(1.0f) - angle); creature->Flags = 0; - break; case DRAGON_STATE_TURN_RIGHT: item->Pose.Orientation.y += (ANGLE(1.0f) - angle); creature->Flags = 0; - break; case DRAGON_STATE_AIM_1: @@ -434,12 +425,11 @@ namespace TEN::Entities::TR2 case DRAGON_STATE_FIRE_1: item->Pose.Orientation.y -= angle; + SoundEffect(SFX_TR2_DRAGON_FIRE, &item->Pose); if (AI.ahead) head = -AI.angle; - SoundEffect(SFX_TR2_DRAGON_FIRE, &item->Pose); - if (creature->Flags) { if (AI.ahead) @@ -459,12 +449,7 @@ namespace TEN::Entities::TR2 back->Animation.ActiveState = item->Animation.ActiveState; back->Animation.AnimNumber = Objects[ID_DRAGON_BACK].animIndex + (item->Animation.AnimNumber - Objects[ID_DRAGON_FRONT].animIndex); back->Animation.FrameNumber = g_Level.Anims[back->Animation.AnimNumber].frameBase + (item->Animation.FrameNumber - g_Level.Anims[item->Animation.AnimNumber].frameBase); - back->Pose.Position.x = item->Pose.Position.x; - back->Pose.Position.y = item->Pose.Position.y; - back->Pose.Position.z = item->Pose.Position.z; - back->Pose.Orientation.x = item->Pose.Orientation.x; - back->Pose.Orientation.y = item->Pose.Orientation.y; - back->Pose.Orientation.z = item->Pose.Orientation.z; + back->Pose = item->Pose; if (back->RoomNumber != item->RoomNumber) ItemNewRoom(backItemNumber, item->RoomNumber); @@ -484,9 +469,7 @@ namespace TEN::Entities::TR2 { auto* back = &g_Level.Items[backItem]; back->ObjectNumber = ID_DRAGON_BACK; - back->Pose.Position.x = item->Pose.Position.x; - back->Pose.Position.y = item->Pose.Position.y; - back->Pose.Position.z = item->Pose.Position.z; + back->Pose.Position = item->Pose.Position; back->Pose.Orientation.y = item->Pose.Orientation.y; back->RoomNumber = item->RoomNumber; back->Status = ITEM_INVISIBLE; @@ -500,9 +483,7 @@ namespace TEN::Entities::TR2 auto* front = &g_Level.Items[frontItem]; front->ObjectNumber = ID_DRAGON_FRONT; - front->Pose.Position.x = item->Pose.Position.x; - front->Pose.Position.y = item->Pose.Position.y; - front->Pose.Position.z = item->Pose.Position.z; + front->Pose.Position = item->Pose.Position; front->Pose.Orientation.y = item->Pose.Orientation.y; front->RoomNumber = item->RoomNumber; front->Status = ITEM_INVISIBLE; diff --git a/TombEngine/Objects/TR2/Entity/tr2_dragon.h b/TombEngine/Objects/TR2/Entity/tr2_dragon.h index 0809af93a..d501de501 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_dragon.h +++ b/TombEngine/Objects/TR2/Entity/tr2_dragon.h @@ -2,7 +2,7 @@ #include "Game/collision/collide_room.h" #include "Game/items.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void DragonCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); void DragonControl(short backNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.cpp b/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.cpp index a5b078769..b59911c68 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.cpp @@ -10,7 +10,7 @@ #include "Specific/level.h" #include "Specific/setup.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { const auto EagleBite = BiteInfo(Vector3(15.0f, 46.0f, 21.0f), 6); const auto CrowBite = BiteInfo(Vector3(2.0f, 10.0f, 60.0f), 14); diff --git a/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.h b/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.h index 4b704e256..828b15bf8 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.h +++ b/TombEngine/Objects/TR2/Entity/tr2_eagle_or_crow.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void InitialiseEagle(short itemNumber); void EagleControl(short itemNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_knife_thrower.cpp b/TombEngine/Objects/TR2/Entity/tr2_knife_thrower.cpp index bde2bff9c..2e9957b89 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_knife_thrower.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_knife_thrower.cpp @@ -18,7 +18,7 @@ using namespace TEN::Math::Random; -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { constexpr auto KNIFE_PROJECTILE_DAMAGE = 50; @@ -29,7 +29,7 @@ namespace TEN::Entities::TR2 enum KnifeThrowerState { - KTHROWER_STATE_NONE = 0, + // No state 0. KTHROWER_STATE_IDLE = 1, KTHROWER_STATE_WALK_FORWARD = 2, KTHROWER_STATE_RUN_FORWARD = 3, @@ -101,7 +101,7 @@ namespace TEN::Entities::TR2 fx->pos.Orientation.z += ANGLE(30.0f); - if (ItemNearLara(&fx->pos, 200)) + if (ItemNearLara(&fx->pos.Position, 200)) { DoDamage(LaraItem, KNIFE_PROJECTILE_DAMAGE); diff --git a/TombEngine/Objects/TR2/Entity/tr2_knife_thrower.h b/TombEngine/Objects/TR2/Entity/tr2_knife_thrower.h index 640373f24..2204a999c 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_knife_thrower.h +++ b/TombEngine/Objects/TR2/Entity/tr2_knife_thrower.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void KnifeControl(short fxNumber); void KnifeThrowerControl(short itemNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_mercenary.cpp b/TombEngine/Objects/TR2/Entity/tr2_mercenary.cpp index b166e57f0..c312e5002 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_mercenary.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_mercenary.cpp @@ -11,7 +11,7 @@ #include "Specific/setup.h" #include "Specific/trmath.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { const auto MercenaryUziBite = BiteInfo(Vector3(0.0f, 150.0f, 19.0f), 17); const auto MercenaryAutoPistolBite = BiteInfo(Vector3(0.0f, 230.0f, 9.0f), 17); diff --git a/TombEngine/Objects/TR2/Entity/tr2_mercenary.h b/TombEngine/Objects/TR2/Entity/tr2_mercenary.h index 5199716ad..80f95100d 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_mercenary.h +++ b/TombEngine/Objects/TR2/Entity/tr2_mercenary.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void MercenaryUziControl(short itemNumber); void MercenaryAutoPistolControl(short itemNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_monk.cpp b/TombEngine/Objects/TR2/Entity/tr2_monk.cpp index 6dd61eb4d..1308c90e5 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_monk.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_monk.cpp @@ -11,7 +11,7 @@ #include "Specific/level.h" #include "Specific/setup.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { const auto MonkBite = BiteInfo(Vector3(-23.0f, 16.0f, 265.0f), 14); diff --git a/TombEngine/Objects/TR2/Entity/tr2_monk.h b/TombEngine/Objects/TR2/Entity/tr2_monk.h index dcf98f279..5fa99a252 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_monk.h +++ b/TombEngine/Objects/TR2/Entity/tr2_monk.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void MonkControl(short itemNumber); } diff --git a/TombEngine/Objects/TR2/Entity/tr2_rat.cpp b/TombEngine/Objects/TR2/Entity/tr2_rat.cpp index 473e071b2..8f9843212 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_rat.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_rat.cpp @@ -14,7 +14,7 @@ using namespace TEN::Math::Random; -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { constexpr auto RAT_ATTACK_DAMAGE = 20; constexpr auto RAT_ATTACK_RANGE = SQUARE(CLICK(0.7f)); @@ -29,7 +29,7 @@ namespace TEN::Entities::TR2 enum RatState { - RAT_STATE_NONE = 0, + // No state 0. RAT_STATE_WALK_FORWARD = 1, RAT_STATE_IDLE = 2, RAT_STATE_SQUEAK = 3, diff --git a/TombEngine/Objects/TR2/Entity/tr2_rat.h b/TombEngine/Objects/TR2/Entity/tr2_rat.h index 1b4ebadcc..9afbf9b8a 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_rat.h +++ b/TombEngine/Objects/TR2/Entity/tr2_rat.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void RatControl(short itemNumber); } diff --git a/TombEngine/Objects/TR2/Entity/tr2_shark.cpp b/TombEngine/Objects/TR2/Entity/tr2_shark.cpp index 6d1e71a10..ba8a93458 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_shark.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_shark.cpp @@ -11,9 +11,14 @@ #include "Specific/level.h" #include "Specific/setup.h" -namespace TEN::Entities::TR2 +using std::vector; + +namespace TEN::Entities::Creatures::TR2 { + constexpr auto SHARK_BITE_ATTACK_DAMAGE = 400; + const auto SharkBite = BiteInfo(Vector3(17.0f, -22.0f, 344.0f), 12); + const vector SharkBiteAttackJoints = { 10, 12, 13 }; void SharkControl(short itemNumber) { @@ -21,7 +26,7 @@ namespace TEN::Entities::TR2 return; auto* item = &g_Level.Items[itemNumber]; - auto* info = GetCreatureInfo(item); + auto* creature = GetCreatureInfo(item); short angle = 0; short head = 0; @@ -29,11 +34,7 @@ namespace TEN::Entities::TR2 if (item->HitPoints <= 0) { if (item->Animation.ActiveState != 5) - { - item->Animation.AnimNumber = Objects[ID_SHARK].animIndex + 4; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 5; - } + SetAnimation(item, 4); CreatureFloat(itemNumber); return; @@ -46,39 +47,40 @@ namespace TEN::Entities::TR2 GetCreatureMood(item, &AI, true); CreatureMood(item, &AI, true); - angle = CreatureTurn(item, info->MaxTurn); + angle = CreatureTurn(item, creature->MaxTurn); switch (item->Animation.ActiveState) { case 0: - info->Flags = 0; - info->MaxTurn = 0; + creature->MaxTurn = 0; + creature->Flags = 0; if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2) && AI.zoneNumber == AI.enemyZone) item->Animation.TargetState = 3; else item->Animation.TargetState = 1; + break; case 1: - info->MaxTurn = ANGLE(0.5f); + creature->MaxTurn = ANGLE(0.5f); - if (info->Mood == MoodType::Bored) + if (creature->Mood == MoodType::Bored) break; else if (AI.ahead && AI.distance < pow(SECTOR(0.75f), 2)) item->Animation.TargetState = 0; - else if (info->Mood == MoodType::Escape || AI.distance > pow(SECTOR(3), 2) || !AI.ahead) + else if (creature->Mood == MoodType::Escape || AI.distance > pow(SECTOR(3), 2) || !AI.ahead) item->Animation.TargetState = 2; break; case 2: - info->MaxTurn = ANGLE(2.0f); - info->Flags = 0; + creature->MaxTurn = ANGLE(2.0f); + creature->Flags = 0; - if (info->Mood == MoodType::Bored) + if (creature->Mood == MoodType::Bored) item->Animation.TargetState = 1; - else if (info->Mood == MoodType::Escape) + else if (creature->Mood == MoodType::Escape) break; else if (AI.ahead && AI.distance < pow(1365, 2) && AI.zoneNumber == AI.enemyZone) { @@ -95,11 +97,11 @@ namespace TEN::Entities::TR2 if (AI.ahead) head = AI.angle; - if (!info->Flags && item->TouchBits & 0x3400) + if (!creature->Flags && item->TestBits(JointBitType::Touch, SharkBiteAttackJoints)) { + DoDamage(creature->Enemy, SHARK_BITE_ATTACK_DAMAGE); CreatureEffect(item, SharkBite, DoBloodSplat); - DoDamage(info->Enemy, 400); - info->Flags = 1; + creature->Flags = 1; } break; diff --git a/TombEngine/Objects/TR2/Entity/tr2_shark.h b/TombEngine/Objects/TR2/Entity/tr2_shark.h index 1f2d76299..5788ee192 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_shark.h +++ b/TombEngine/Objects/TR2/Entity/tr2_shark.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void SharkControl(short itemNumber); } diff --git a/TombEngine/Objects/TR2/Entity/tr2_silencer.cpp b/TombEngine/Objects/TR2/Entity/tr2_silencer.cpp index c5f124b5f..8e6a1ca5f 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_silencer.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_silencer.cpp @@ -8,22 +8,70 @@ #include "Game/misc.h" #include "Game/people.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/setup.h" -namespace TEN::Entities::TR2 +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR2 { + constexpr auto SILENCER_SHOOT_ATTACK_DAMAGE = 50; + constexpr auto SILENCER_RUN_RANGE = SQUARE(SECTOR(2)); + const auto SilencerGunBite = BiteInfo(Vector3(3.0f, 331.0f, 56.0f), 10); - // TODO + #define SILENCER_WALK_TURN_RATE_MAX ANGLE(5.0f) + #define SILENCER_RUN_TURN_RATE_MAX ANGLE(5.0f) + enum SilencerState { - + // No state 0. + SILENCER_STATE_WALK_FORWARD = 1, + SILENCER_STATE_RUN_FORWARD = 2, + SILENCER_STATE_IDLE_FRAME = 3, + SILENCER_STATE_IDLE = 4, + SILENCER_STATE_POSE = 5, + SILENCER_STATE_AIM_1 = 6, + SILENCER_STATE_SHOOT_1 = 7, + // No state 8. + SILENCER_STATE_RUN_SHOOT = 9, + SILENCER_STATE_AIM_2 = 10, + SILENCER_STATE_SHOOT_2 = 11, + SILENCER_STATE_DEATH_1 = 12, + SILENCER_STATE_DEATH_2 = 13 }; - // TODO enum SilencerAnim { - + SILENCER_ANIM_IDLE_FRAME = 0, + SILENCER_ANIM_IDLE_TO_WALK_FORWARD = 1, + SILENCER_ANIM_WALK_FORWARD = 2, + SILENCER_ANIM_WALK_FORWARD_TO_IDLE = 3, + SILENCER_ANIM_WALK_FORWARD_TO_POSE = 4, + SILENCER_ANIM_POSE = 5, + SILENCER_ANIM_WALK_FORWARD_TO_RUN_FORWARD = 6, + SILENCER_ANIM_RUN_FORWARD = 7, + SILENCER_ANIM_RUN_FORWARD_TO_IDLE = 8, + SILENCER_ANIM_IDLE_TO_RUN_FORWARD = 9, + SILENCER_ANIM_POSE_TO_IDLE = 10, + SILENCER_ANIM_RUN_FORWARD_AIM_LEFT = 11, + SILENCER_ANIM_RUN_FORWARD_SHOOT_LEFT = 12, + SILENCER_ANIM_RUN_FORWARD_UNAIM_LEFT = 13, + SILENCER_ANIM_AIM_1_START = 14, + SILENCER_ANIM_AIM_1_CONTINUE = 15, + SILENCER_ANIM_SHOOT_1 = 16, + SILENCER_ANIM_UNAIM_1 = 17, + SILENCER_ANIM_POSE_TO_AIM_1 = 18, + SILENCER_ANIM_IDLE = 19, + SILENCER_ANIM_DEATH_1 = 20, + SILENCER_ANIM_DEATH_2 = 21, // Unused. + SILENCER_ANIM_AIM_2_START = 22, + SILENCER_ANIM_AIM_2_CONTINUE = 23, + SILENCER_ANIM_SHOOT_2 = 24, + SILENCER_ANIM_UNAIM_2 = 25, + SILENCER_ANIM_RUN_FORWARD_AIM_RIGHT = 26, + SILENCER_ANIM_RUN_FORWARD_SHOOT_RIGHT = 27, + SILENCER_ANIM_RUN_FORWARD_UNAIM_RIGHT = 28 }; void SilencerControl(short itemNumber) @@ -32,21 +80,19 @@ namespace TEN::Entities::TR2 return; auto* item = &g_Level.Items[itemNumber]; - auto* info = GetCreatureInfo(item); + auto* creature = GetCreatureInfo(item); short angle = 0; - short torsoX = 0; - short torsoY = 0; - short head = 0; short tilt = 0; + auto extraHeadRot = Vector3Shrt::Zero; + auto extraTorsoRot = Vector3Shrt::Zero; if (item->HitPoints <= 0) { - if (item->Animation.ActiveState != 12 && item->Animation.ActiveState != 13) + if (item->Animation.ActiveState != SILENCER_STATE_DEATH_1 && + item->Animation.ActiveState != SILENCER_STATE_DEATH_2) { - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 20; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 13; + SetAnimation(item, SILENCER_ANIM_DEATH_1); } } else @@ -57,205 +103,207 @@ namespace TEN::Entities::TR2 GetCreatureMood(item, &AI, true); CreatureMood(item, &AI, true); - angle = CreatureTurn(item, info->MaxTurn); + angle = CreatureTurn(item, creature->MaxTurn); switch (item->Animation.ActiveState) { - case 3: + case SILENCER_STATE_IDLE_FRAME: + creature->MaxTurn = 0; + if (AI.ahead) - head = AI.angle; - info->MaxTurn = 0; + extraHeadRot.y = AI.angle; if (item->Animation.RequiredState) item->Animation.TargetState = item->Animation.RequiredState; break; - case 4: - if (AI.ahead) - head = AI.angle; - info->MaxTurn = 0; + case SILENCER_STATE_IDLE: + creature->MaxTurn = 0; - if (info->Mood == MoodType::Escape) + if (AI.ahead) + extraHeadRot.y = AI.angle; + + if (creature->Mood == MoodType::Escape) { - item->Animation.RequiredState = 2; - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; + item->Animation.RequiredState = SILENCER_STATE_RUN_FORWARD; } else { if (Targetable(item, &AI)) { - item->Animation.RequiredState = (GetRandomControl() >= 0x4000 ? 10 : 6); - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; + item->Animation.RequiredState = TestProbability(0.5f) ? SILENCER_STATE_AIM_1 : SILENCER_STATE_AIM_2; } - if (info->Mood == MoodType::Attack || !AI.ahead) + if (creature->Mood == MoodType::Attack || !AI.ahead) { - if (AI.distance >= pow(SECTOR(2), 2)) + if (AI.distance >= SILENCER_RUN_RANGE) { - item->Animation.RequiredState = 2; - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; + item->Animation.RequiredState = SILENCER_STATE_RUN_FORWARD; } else { - item->Animation.RequiredState = 1; - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; + item->Animation.RequiredState = SILENCER_STATE_WALK_FORWARD; } } else { - if (GetRandomControl() >= 1280) + if (TestProbability(0.96f)) { - if (GetRandomControl() < 2560) + if (TestProbability(0.08f)) { - item->Animation.RequiredState = 1; - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; + item->Animation.RequiredState = SILENCER_STATE_WALK_FORWARD; } } else { - item->Animation.RequiredState = 5; - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; + item->Animation.RequiredState = SILENCER_STATE_POSE; } } } break; - case 1: + case SILENCER_STATE_WALK_FORWARD: + creature->MaxTurn = SILENCER_WALK_TURN_RATE_MAX; + if (AI.ahead) - head = AI.angle; + extraHeadRot.y = AI.angle; - info->MaxTurn = 910; - - if (info->Mood == MoodType::Escape) - item->Animation.TargetState = 2; + if (creature->Mood == MoodType::Escape) + item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD; else if (Targetable(item, &AI)) { - item->Animation.RequiredState = (GetRandomControl() >= 0x4000 ? 10 : 6); - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; + item->Animation.RequiredState = TestProbability(0.5f) ? SILENCER_STATE_AIM_1 : SILENCER_STATE_AIM_2; } else { - if (AI.distance > pow(SECTOR(2), 2) || !AI.ahead) - item->Animation.TargetState = 2; - if (info->Mood == MoodType::Bored && GetRandomControl() < 0x300) - item->Animation.TargetState = 3; + if (AI.distance > SILENCER_RUN_RANGE || !AI.ahead) + item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD; + if (creature->Mood == MoodType::Bored && TestProbability(0.025f)) + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; } break; - case 2: - if (AI.ahead) - head = AI.angle; - - info->MaxTurn = ANGLE(5.0f); - info->Flags = 0; + case SILENCER_STATE_RUN_FORWARD: + creature->MaxTurn = SILENCER_RUN_TURN_RATE_MAX; + creature->Flags = 0; tilt = angle / 4; - if (info->Mood == MoodType::Escape) + if (AI.ahead) + extraHeadRot.y = AI.angle; + + if (creature->Mood == MoodType::Escape) { if (Targetable(item, &AI)) - item->Animation.TargetState = 9; + item->Animation.TargetState = SILENCER_STATE_RUN_SHOOT; break; - } if (Targetable(item, &AI)) { - if (AI.distance >= pow(SECTOR(2), 2) && AI.zoneNumber == AI.enemyZone) - item->Animation.TargetState = 9; + if (AI.distance >= SILENCER_RUN_RANGE && AI.zoneNumber == AI.enemyZone) + item->Animation.TargetState = SILENCER_STATE_RUN_SHOOT; break; } - else if (info->Mood == MoodType::Attack) - item->Animation.TargetState = (GetRandomControl() >= 0x4000) ? 3 : 2; + else if (creature->Mood == MoodType::Attack) + item->Animation.TargetState = TestProbability(0.5f) ? SILENCER_STATE_RUN_FORWARD : SILENCER_STATE_IDLE_FRAME; else - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; break; - case 5: - if (AI.ahead) - head = AI.angle; + case SILENCER_STATE_POSE: + creature->MaxTurn = 0; - info->MaxTurn = 0; + if (AI.ahead) + extraHeadRot.y = AI.angle; if (Targetable(item, &AI)) { - item->Animation.RequiredState = 6; - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; + item->Animation.RequiredState = SILENCER_STATE_AIM_1; } else { - if (info->Mood == MoodType::Attack || GetRandomControl() < 0x100) - item->Animation.TargetState = 3; + if (creature->Mood == MoodType::Attack || TestProbability(1.0f / 128)) + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; + if (!AI.ahead) - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; } break; - case 6: - case 10: - info->MaxTurn = 0; - info->Flags = 0; + case SILENCER_STATE_AIM_1: + case SILENCER_STATE_AIM_2: + creature->MaxTurn = 0; + creature->Flags = 0; if (AI.ahead) { - torsoY = AI.angle; - torsoX = AI.xAngle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } else - head = AI.angle; + extraHeadRot.y = AI.angle; - if (info->Mood == MoodType::Escape) - item->Animation.TargetState = 3; + if (creature->Mood == MoodType::Escape) + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; else if (Targetable(item, &AI)) - item->Animation.TargetState = item->Animation.ActiveState != 6 ? 11 : 7; + item->Animation.TargetState = (item->Animation.ActiveState != SILENCER_STATE_AIM_1) ? SILENCER_STATE_SHOOT_2 : SILENCER_STATE_SHOOT_1; else - item->Animation.TargetState = 3; + item->Animation.TargetState = SILENCER_STATE_IDLE_FRAME; break; - case 7: - case 11: - info->MaxTurn = 0; + case SILENCER_STATE_SHOOT_1: + case SILENCER_STATE_SHOOT_2: + creature->MaxTurn = 0; if (AI.ahead) { - torsoY = AI.angle; - torsoX = AI.xAngle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } else - head = AI.angle; + extraHeadRot.y = AI.angle; - if (!info->Flags) + if (!creature->Flags) { - ShotLara(item, &AI, SilencerGunBite, torsoY, 50); - info->Flags = 1; + ShotLara(item, &AI, SilencerGunBite, extraTorsoRot.y, SILENCER_SHOOT_ATTACK_DAMAGE); + creature->Flags = 1; } break; - case 9: - info->MaxTurn = ANGLE(5.0f); + case SILENCER_STATE_RUN_SHOOT: + creature->MaxTurn = SILENCER_RUN_TURN_RATE_MAX; if (AI.ahead) { - torsoY = AI.angle; - torsoX = AI.xAngle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } else - head = AI.angle; + extraHeadRot.y = AI.angle; if (!item->Animation.RequiredState) { - if (!ShotLara(item, &AI, SilencerGunBite, torsoY, 50)) - item->Animation.TargetState = 2; + if (!ShotLara(item, &AI, SilencerGunBite, extraTorsoRot.y, SILENCER_SHOOT_ATTACK_DAMAGE)) + item->Animation.TargetState = SILENCER_STATE_RUN_FORWARD; - item->Animation.RequiredState = 9; + item->Animation.RequiredState = SILENCER_STATE_RUN_SHOOT; } break; @@ -263,9 +311,9 @@ namespace TEN::Entities::TR2 } CreatureTilt(item, tilt); - CreatureJoint(item, 0, torsoY); - CreatureJoint(item, 1, torsoX); - CreatureJoint(item, 2, head); + CreatureJoint(item, 0, extraTorsoRot.y); + CreatureJoint(item, 1, extraTorsoRot.x); + CreatureJoint(item, 2, extraHeadRot.y); CreatureAnimation(itemNumber, angle, tilt); } } diff --git a/TombEngine/Objects/TR2/Entity/tr2_silencer.h b/TombEngine/Objects/TR2/Entity/tr2_silencer.h index 3b91c1f71..69e903dfc 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_silencer.h +++ b/TombEngine/Objects/TR2/Entity/tr2_silencer.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void SilencerControl(short itemNumber); } diff --git a/TombEngine/Objects/TR2/Entity/tr2_skidman.cpp b/TombEngine/Objects/TR2/Entity/tr2_skidman.cpp index dec4daf4e..a075d43ec 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_skidman.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_skidman.cpp @@ -17,7 +17,7 @@ #include "Specific/level.h" #include "Specific/setup.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { #define SMAN_MIN_TURN (ANGLE(2.0f)) #define SMAN_TARGET_ANGLE ANGLE(15.0f) @@ -28,7 +28,7 @@ namespace TEN::Entities::TR2 enum SnowmobileManState { - SMAN_STATE_NONE = 0, + // No state 0. SMAN_STATE_WAIT = 1, SMAN_STATE_MOVING = 2, SMAN_STATE_START_LEFT = 3, diff --git a/TombEngine/Objects/TR2/Entity/tr2_skidman.h b/TombEngine/Objects/TR2/Entity/tr2_skidman.h index 3a79818c5..bc6147943 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_skidman.h +++ b/TombEngine/Objects/TR2/Entity/tr2_skidman.h @@ -2,7 +2,7 @@ #include "Game/collision/collide_room.h" #include "Game/items.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void InitialiseSkidooMan(short itemNumber); void SkidooManCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); diff --git a/TombEngine/Objects/TR2/Entity/tr2_spear_guardian.cpp b/TombEngine/Objects/TR2/Entity/tr2_spear_guardian.cpp index 3ad9912e7..053305727 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_spear_guardian.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_spear_guardian.cpp @@ -10,9 +10,12 @@ #include "Game/misc.h" #include "Sound/sound.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/setup.h" -namespace TEN::Entities::TR2 +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR2 { const auto SpearBiteLeft = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 11); const auto SpearBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 18); @@ -29,7 +32,7 @@ namespace TEN::Entities::TR2 }; - static void XianDamage(ItemInfo* item, int damage) + void XianDamage(ItemInfo* item, int damage) { auto* creature = GetCreatureInfo(item); @@ -76,7 +79,7 @@ namespace TEN::Entities::TR2 short neck = 0; short tilt = 0; - bool laraAlive = LaraItem->HitPoints > 0; + bool isLaraAlive = LaraItem->HitPoints > 0; if (item->HitPoints <= 0) { @@ -125,10 +128,9 @@ namespace TEN::Entities::TR2 if (creature->Mood == MoodType::Bored) { - int random = GetRandomControl(); - if (random < 0x200) + if (TestProbability(1.0f / 64)) item->Animation.TargetState = 2; - else if (random < 0x400) + else if (TestProbability(1.0f / 30)) item->Animation.TargetState = 3; } else if (AI.ahead && AI.distance < pow(SECTOR(1), 2)) @@ -148,10 +150,9 @@ namespace TEN::Entities::TR2 item->Animation.TargetState = 3; else if (creature->Mood == MoodType::Bored) { - int random = GetRandomControl(); - if (random < 0x200) + if (TestProbability(1.0f / 64)) item->Animation.TargetState = 1; - else if (random < 0x400) + else if (TestProbability(1.0f / 30)) item->Animation.TargetState = 3; } else if (AI.ahead && AI.distance < pow(SECTOR(1), 2)) @@ -171,17 +172,16 @@ namespace TEN::Entities::TR2 item->Animation.TargetState = 4; else if (creature->Mood == MoodType::Bored) { - int random = GetRandomControl(); - if (random < 0x200) + if (TestProbability(1.0f / 64)) item->Animation.TargetState = 1; - else if (random < 0x400) + else if (TestProbability(1.0f / 30)) item->Animation.TargetState = 2; } else if (AI.ahead && AI.distance < pow(SECTOR(2), 2)) { if (AI.distance < pow(SECTOR(1.5f), 2)) item->Animation.TargetState = 7; - else if (GetRandomControl() < 0x4000) + else if (TestProbability(0.5f)) item->Animation.TargetState = 9; else item->Animation.TargetState = 11; @@ -201,7 +201,7 @@ namespace TEN::Entities::TR2 break; else if (creature->Mood == MoodType::Bored) { - if (GetRandomControl() < 0x4000) + if (TestProbability(0.5f)) item->Animation.TargetState = 1; else item->Animation.TargetState = 2; @@ -301,7 +301,7 @@ namespace TEN::Entities::TR2 if (AI.ahead && AI.distance < pow(SECTOR(1), 2)) { - if (GetRandomControl() < 0x4000) + if (TestProbability(0.5f)) item->Animation.TargetState = 1; else item->Animation.TargetState = 2; @@ -332,7 +332,7 @@ namespace TEN::Entities::TR2 if (AI.ahead && AI.distance < pow(SECTOR(1), 2)) { - if (GetRandomControl() < 0x4000) + if (TestProbability(0.5f)) item->Animation.TargetState = 1; else item->Animation.TargetState = 2; @@ -346,7 +346,7 @@ namespace TEN::Entities::TR2 } } - if (laraAlive && LaraItem->HitPoints <= 0) + if (isLaraAlive && LaraItem->HitPoints <= 0) { CreatureKill(item, 49, 19, 2); return; diff --git a/TombEngine/Objects/TR2/Entity/tr2_spear_guardian.h b/TombEngine/Objects/TR2/Entity/tr2_spear_guardian.h index d7965b761..e46bb4ea1 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_spear_guardian.h +++ b/TombEngine/Objects/TR2/Entity/tr2_spear_guardian.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void InitialiseSpearGuardian(short itemNumber); void SpearGuardianControl(short itemNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_spider.cpp b/TombEngine/Objects/TR2/Entity/tr2_spider.cpp index 7043d2fc6..6b5a3be87 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_spider.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_spider.cpp @@ -14,11 +14,11 @@ #include "Specific/level.h" #include "Specific/setup.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { const auto SpiderBite = BiteInfo(Vector3(0.0f, 0.0f, 41.0f), 1); - static void S_SpiderBite(ItemInfo* item) + void S_SpiderBite(ItemInfo* item) { auto pos = Vector3Int((int)round(SpiderBite.Position.x), (int)round(SpiderBite.Position.y), (int)round(SpiderBite.Position.z)); GetJointAbsPosition(item, &pos, SpiderBite.meshNum); @@ -26,7 +26,7 @@ namespace TEN::Entities::TR2 DoBloodSplat(pos.x, pos.y, pos.z, 10, item->Pose.Position.y, item->RoomNumber); } - static void SpiderLeap(short itemNumber, ItemInfo* item, short angle) + void SpiderLeap(short itemNumber, ItemInfo* item, short angle) { auto vec = GameVector( item->Pose.Position.x, @@ -40,9 +40,7 @@ namespace TEN::Entities::TR2 if (item->Pose.Position.y > (vec.y - CLICK(1.5f))) return; - item->Pose.Position.x = vec.x; - item->Pose.Position.y = vec.y; - item->Pose.Position.z = vec.z; + item->Pose.Position = Vector3Int(vec.x, vec.y, vec.z); if (item->RoomNumber != vec.roomNumber) ItemNewRoom(item->RoomNumber, vec.roomNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_spider.h b/TombEngine/Objects/TR2/Entity/tr2_spider.h index 23d72cc40..9b5876eec 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_spider.h +++ b/TombEngine/Objects/TR2/Entity/tr2_spider.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void SmallSpiderControl(short itemNumber); void BigSpiderControl(short itemNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.cpp b/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.cpp index 155e51721..a0aab17aa 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.cpp @@ -11,9 +11,12 @@ #include "Game/Lara/lara.h" #include "Game/misc.h" #include "Sound/sound.h" +#include "Specific/prng.h" #include "Specific/level.h" -namespace TEN::Entities::TR2 +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR2 { const auto SwordBite = BiteInfo(Vector3(0.0f, 37.0f, 550.0f), 15); @@ -24,7 +27,7 @@ namespace TEN::Entities::TR2 ClearItem(itemNumber); } - static void SwordGuardianFly(ItemInfo* item) + void SwordGuardianFly(ItemInfo* item) { Vector3Int pos; pos.x = (GetRandomControl() * 256 / 32768) + item->Pose.Position.x - 128; @@ -47,7 +50,7 @@ namespace TEN::Entities::TR2 short head = 0; short torso = 0; - bool laraAlive = LaraItem->HitPoints > 0; + bool isLaraAlive = LaraItem->HitPoints > 0; if (item->HitPoints <= 0) { @@ -68,7 +71,7 @@ namespace TEN::Entities::TR2 creature->LOT.Step = STEP_SIZE; creature->LOT.Drop = -STEP_SIZE; creature->LOT.Fly = NO_FLYING; - creature->LOT.Zone = ZONE_BASIC; + creature->LOT.Zone = ZoneType::Basic; AI_INFO AI; CreatureAIInfo(item, &AI); @@ -80,7 +83,7 @@ namespace TEN::Entities::TR2 creature->LOT.Step = WALL_SIZE * 20; creature->LOT.Drop = -WALL_SIZE * 20; creature->LOT.Fly = STEP_SIZE / 4; - creature->LOT.Zone = ZONE_FLYER; + creature->LOT.Zone = ZoneType::Flyer; CreatureAIInfo(item, &AI); } } @@ -114,15 +117,10 @@ namespace TEN::Entities::TR2 if (AI.ahead) head = AI.angle; - if (laraAlive) + if (isLaraAlive) { if (AI.bite && AI.distance < pow(SECTOR(1), 2)) - { - if (GetRandomControl() >= 0x4000) - item->Animation.TargetState = 5; - else - item->Animation.TargetState = 3; - } + item->Animation.TargetState = TestProbability(0.5f) ? 3 : 5; else { if (AI.zoneNumber == AI.enemyZone) @@ -142,7 +140,7 @@ namespace TEN::Entities::TR2 if (AI.ahead) head = AI.angle; - if (laraAlive) + if (isLaraAlive) { if (AI.bite && AI.distance < pow(SECTOR(2), 2)) item->Animation.TargetState = 10; diff --git a/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.h b/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.h index 9c837688b..588df13b8 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.h +++ b/TombEngine/Objects/TR2/Entity/tr2_sword_guardian.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void InitialiseSwordGuardian(short itemNumber); void SwordGuardianControl(short itemNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_worker_dualrevolver.cpp b/TombEngine/Objects/TR2/Entity/tr2_worker_dualrevolver.cpp index 66e424bf2..5ab9a903b 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_worker_dualrevolver.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_worker_dualrevolver.cpp @@ -10,7 +10,7 @@ #include "Specific/level.h" #include "Specific/setup.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { const auto WorkerDualGunBiteLeft = BiteInfo(Vector3(-2.0f, 275.0f, 23.0f), 6); const auto WorkerDualGunBiteRight = BiteInfo(Vector3(2.0f, 275.0f, 23.0f), 10); diff --git a/TombEngine/Objects/TR2/Entity/tr2_worker_dualrevolver.h b/TombEngine/Objects/TR2/Entity/tr2_worker_dualrevolver.h index fd95395f8..d4a1761d3 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_worker_dualrevolver.h +++ b/TombEngine/Objects/TR2/Entity/tr2_worker_dualrevolver.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void WorkerDualGunControl(short itemNumber); } diff --git a/TombEngine/Objects/TR2/Entity/tr2_worker_flamethrower.cpp b/TombEngine/Objects/TR2/Entity/tr2_worker_flamethrower.cpp index a1ed233eb..ee7738029 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_worker_flamethrower.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_worker_flamethrower.cpp @@ -16,7 +16,7 @@ #include "Specific/level.h" #include "Specific/trmath.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { const auto WorkerFlamethrowerOffset = Vector3Int(0, 140, 0); const auto WorkerFlamethrowerBite = BiteInfo(Vector3(0.0f, 250.0f, 32.0f), 9); diff --git a/TombEngine/Objects/TR2/Entity/tr2_worker_flamethrower.h b/TombEngine/Objects/TR2/Entity/tr2_worker_flamethrower.h index 89fa4122b..f8b851421 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_worker_flamethrower.h +++ b/TombEngine/Objects/TR2/Entity/tr2_worker_flamethrower.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void InitialiseWorkerFlamethrower(short itemNumber); void WorkerFlamethrower(short itemNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_worker_machinegun.cpp b/TombEngine/Objects/TR2/Entity/tr2_worker_machinegun.cpp index 6a69c3e15..2a198c8f5 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_worker_machinegun.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_worker_machinegun.cpp @@ -11,7 +11,7 @@ #include "Specific/level.h" #include "Specific/setup.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { const auto WorkerMachineGunBite = BiteInfo(Vector3(0.0f, 308.0f, 32.0f), 9); diff --git a/TombEngine/Objects/TR2/Entity/tr2_worker_machinegun.h b/TombEngine/Objects/TR2/Entity/tr2_worker_machinegun.h index fef58f199..1ba9b187a 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_worker_machinegun.h +++ b/TombEngine/Objects/TR2/Entity/tr2_worker_machinegun.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void InitialiseWorkerMachineGun(short itemNumber); void WorkerMachineGunControl(short itemNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_worker_shotgun.cpp b/TombEngine/Objects/TR2/Entity/tr2_worker_shotgun.cpp index b5bb9e0d1..9f44426e1 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_worker_shotgun.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_worker_shotgun.cpp @@ -12,7 +12,7 @@ #include "Specific/level.h" #include "Specific/setup.h" -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { const auto WorkerShotgunBite = BiteInfo(Vector3(0.0f, 281.0f, 40.0f), 9); diff --git a/TombEngine/Objects/TR2/Entity/tr2_worker_shotgun.h b/TombEngine/Objects/TR2/Entity/tr2_worker_shotgun.h index a6ad5f5a6..5022fcbd7 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_worker_shotgun.h +++ b/TombEngine/Objects/TR2/Entity/tr2_worker_shotgun.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void InitialiseWorkerShotgun(short itemNumber); void WorkerShotgunControl(short itemNumber); diff --git a/TombEngine/Objects/TR2/Entity/tr2_yeti.cpp b/TombEngine/Objects/TR2/Entity/tr2_yeti.cpp index 0a80c62bb..425414747 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_yeti.cpp +++ b/TombEngine/Objects/TR2/Entity/tr2_yeti.cpp @@ -13,11 +13,14 @@ #include "Specific/setup.h" using namespace TEN::Math::Random; +using std::vector; -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { - const auto YetiBiteLeft = BiteInfo(Vector3(12.0f, 101.0f, 19.0f), 13); + const auto YetiBiteLeft = BiteInfo(Vector3(12.0f, 101.0f, 19.0f), 13); const auto YetiBiteRight = BiteInfo(Vector3(12.0f, 101.0f, 19.0f), 10); + const vector YetiAttackJoints1 = { 10, 12 }; // TODO: Rename. + const vector YetiAttackJoints2 = { 8, 9, 10 }; // TODO enum YetiState @@ -50,9 +53,9 @@ namespace TEN::Entities::TR2 bool isLaraAlive = LaraItem->HitPoints > 0; short angle = 0; + short tilt = 0; short torso = 0; short head = 0; - short tilt = 0; if (item->HitPoints <= 0) { @@ -88,9 +91,9 @@ namespace TEN::Entities::TR2 item->Animation.TargetState = item->Animation.RequiredState; else if (info->Mood == MoodType::Bored) { - if (TestProbability(0.008f) || !isLaraAlive) + if (TestProbability(1.0f / 128) || !isLaraAlive) item->Animation.TargetState = 7; - else if (TestProbability(0.015f)) + else if (TestProbability(1.0f / 64)) item->Animation.TargetState = 9; else if (TestProbability(0.025f)) item->Animation.TargetState = 3; @@ -116,9 +119,9 @@ namespace TEN::Entities::TR2 { if (isLaraAlive) { - if (TestProbability(0.008f)) + if (TestProbability(1.0f / 128)) item->Animation.TargetState = 2; - else if (TestProbability(0.015f)) + else if (TestProbability(1.0f / 64)) item->Animation.TargetState = 9; else if (TestProbability(0.025f)) { @@ -127,7 +130,7 @@ namespace TEN::Entities::TR2 } } } - else if (TestProbability(0.015f)) + else if (TestProbability(1.0f / 64)) item->Animation.TargetState = 2; break; @@ -140,9 +143,9 @@ namespace TEN::Entities::TR2 item->Animation.TargetState = 2; else if (info->Mood == MoodType::Bored) { - if (TestProbability(0.008f) || !isLaraAlive) + if (TestProbability(1.0f / 128) || !isLaraAlive) item->Animation.TargetState = 7; - else if (TestProbability(0.015f)) + else if (TestProbability(1.0f / 64)) item->Animation.TargetState = 2; else if (TestProbability(0.025f)) { @@ -150,7 +153,7 @@ namespace TEN::Entities::TR2 item->Animation.RequiredState = 3; } } - else if (TestProbability(0.015f)) + else if (TestProbability(1.0f / 64)) item->Animation.TargetState = 2; break; @@ -165,12 +168,12 @@ namespace TEN::Entities::TR2 item->Animation.TargetState = 1; else if (info->Mood == MoodType::Bored) { - if (TestProbability(0.008f) || !isLaraAlive) + if (TestProbability(1.0f / 128) || !isLaraAlive) { item->Animation.TargetState = 2; item->Animation.RequiredState = 7; } - else if (TestProbability(0.015f)) + else if (TestProbability(1.0f / 64)) { item->Animation.TargetState = 2; item->Animation.RequiredState = 9; @@ -213,8 +216,7 @@ namespace TEN::Entities::TR2 if (AI.ahead) torso = AI.angle; - if (!info->Flags && - item->TouchBits & 0x1400) + if (!info->Flags && item->TestBits(JointBitType::Touch, YetiAttackJoints1)) { CreatureEffect(item, YetiBiteRight, DoBloodSplat); DoDamage(info->Enemy, 100); @@ -230,11 +232,12 @@ namespace TEN::Entities::TR2 torso = AI.angle; if (!info->Flags && - item->TouchBits & (0x0700 | 0x1400)) + (item->TestBits(JointBitType::Touch, YetiAttackJoints1) || item->TestBits(JointBitType::Touch, YetiAttackJoints2))) { - if (item->TouchBits & 0x0700) + if (item->TestBits(JointBitType::Touch, YetiAttackJoints2)) CreatureEffect(item, YetiBiteLeft, DoBloodSplat); - if (item->TouchBits & 0x1400) + + if (item->TestBits(JointBitType::Touch, YetiAttackJoints1)) CreatureEffect(item, YetiBiteRight, DoBloodSplat); DoDamage(info->Enemy, 150); @@ -248,11 +251,12 @@ namespace TEN::Entities::TR2 torso = AI.angle; if (!info->Flags && - item->TouchBits & (0x0700 | 0x1400)) + (item->TestBits(JointBitType::Touch, YetiAttackJoints1) || item->TestBits(JointBitType::Touch, YetiAttackJoints2))) { - if (item->TouchBits & 0x0700) + if (item->TestBits(JointBitType::Touch, YetiAttackJoints2)) CreatureEffect(item, YetiBiteLeft, DoBloodSplat); - if (item->TouchBits & 0x1400) + + if (item->TestBits(JointBitType::Touch, YetiAttackJoints1)) CreatureEffect(item, YetiBiteRight, DoBloodSplat); DoDamage(info->Enemy, 200); diff --git a/TombEngine/Objects/TR2/Entity/tr2_yeti.h b/TombEngine/Objects/TR2/Entity/tr2_yeti.h index aa6ae5f16..f77c73350 100644 --- a/TombEngine/Objects/TR2/Entity/tr2_yeti.h +++ b/TombEngine/Objects/TR2/Entity/tr2_yeti.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR2 +namespace TEN::Entities::Creatures::TR2 { void InitialiseYeti(short itemNumber); void YetiControl(short itemNumber); diff --git a/TombEngine/Objects/TR2/tr2_objects.cpp b/TombEngine/Objects/TR2/tr2_objects.cpp index 67c07965b..33375d613 100644 --- a/TombEngine/Objects/TR2/tr2_objects.cpp +++ b/TombEngine/Objects/TR2/tr2_objects.cpp @@ -37,7 +37,7 @@ #include "Objects/TR2/Vehicles/speedboat.h" #include "Objects/TR2/Vehicles/skidoo.h" -using namespace TEN::Entities::TR2; +using namespace TEN::Entities::Creatures::TR2; static void StartEntity(ObjectInfo* obj) { @@ -57,7 +57,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_WATER; + obj->ZoneType = ZoneType::Water; g_Level.Bones[obj->boneIndex + 9 * 4] |= ROT_Y; } @@ -78,7 +78,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_WATER; + obj->ZoneType = ZoneType::Water; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; } @@ -99,7 +99,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveAnim = true; obj->saveFlags = true; obj->pivotLength = 0; - obj->zoneType = ZONE_FLYER; + obj->ZoneType = ZoneType::Flyer; } obj = &Objects[ID_CROW]; @@ -118,7 +118,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveAnim = true; obj->saveFlags = true; obj->pivotLength = 0; - obj->zoneType = ZONE_FLYER; + obj->ZoneType = ZoneType::Flyer; } obj = &Objects[ID_RAT]; @@ -154,7 +154,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveFlags = true; obj->saveHitpoints = true; obj->savePosition = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= (ROT_Y); g_Level.Bones[obj->boneIndex + 14 * 4] |= (ROT_Y); } diff --git a/TombEngine/Objects/TR3/Entity/tr3_civvy.cpp b/TombEngine/Objects/TR3/Entity/tr3_civvy.cpp index aad4a59a4..9c2f66591 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_civvy.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_civvy.cpp @@ -16,7 +16,7 @@ using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { constexpr auto CIVVY_ATTACK_DAMAGE = 40; constexpr auto CIVVY_SWIPE_DAMAGE = 50; @@ -42,7 +42,6 @@ namespace TEN::Entities::TR3 // TODO enum CivvyState { - CIVVY_STATE_NONE, CIVVY_STATE_IDLE, CIVVY_STATE_WALK_FORWARD, CIVVY_PUNCH2, @@ -89,11 +88,10 @@ namespace TEN::Entities::TR3 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short torsoX = 0; - short torsoY = 0; - short head = 0; short angle = 0; short tilt = 0; + auto extraHeadRot = Vector3Shrt::Zero; + auto extraTorsoRot = Vector3Shrt::Zero; if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED)) { @@ -119,18 +117,18 @@ namespace TEN::Entities::TR3 AI_INFO AI; CreatureAIInfo(item, &AI); - AI_INFO laraAiInfo; + AI_INFO laraAI; if (creature->Enemy == LaraItem) { - laraAiInfo.angle = AI.angle; - laraAiInfo.distance = AI.distance; + laraAI.angle = AI.angle; + laraAI.distance = AI.distance; } else { int laraDz = LaraItem->Pose.Position.z - item->Pose.Position.z; int laraDx = LaraItem->Pose.Position.x - item->Pose.Position.x; - laraAiInfo.angle = phd_atan(laraDz, laraDx) - item->Pose.Orientation.y; - laraAiInfo.distance = pow(laraDx, 2) + pow(laraDz, 2); + laraAI.angle = phd_atan(laraDz, laraDx) - item->Pose.Orientation.y; + laraAI.distance = pow(laraDx, 2) + pow(laraDz, 2); } GetCreatureMood(item, &AI, true); @@ -150,7 +148,7 @@ namespace TEN::Entities::TR3 auto* realEnemy = creature->Enemy; creature->Enemy = LaraItem; - if ((laraAiInfo.distance < CIVVY_AWARE_RANGE || item->HitStatus || TargetVisible(item, &laraAiInfo)) && + if ((laraAI.distance < CIVVY_AWARE_RANGE || item->HitStatus || TargetVisible(item, &laraAI)) && !(item->AIBits & FOLLOW)) { if (!creature->Alerted) @@ -170,13 +168,13 @@ namespace TEN::Entities::TR3 } case CIVVY_STATE_IDLE: - head = laraAiInfo.angle; creature->MaxTurn = 0; creature->Flags = 0; + extraHeadRot.y = laraAI.angle; if (item->AIBits & GUARD) { - head = AIGuard(creature); + extraHeadRot.y = AIGuard(creature); if (!(GetRandomControl() & 0xFF)) { if (item->Animation.ActiveState == CIVVY_STATE_IDLE) @@ -199,7 +197,7 @@ namespace TEN::Entities::TR3 item->Animation.TargetState = CIVVY_STATE_RUN_FORWARD; } else if (creature->Mood == MoodType::Bored || - (item->AIBits & FOLLOW && (creature->ReachedGoal || laraAiInfo.distance > pow(SECTOR(2), 2)))) + (item->AIBits & FOLLOW && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2)))) { if (item->Animation.RequiredState) item->Animation.TargetState = item->Animation.RequiredState; @@ -221,11 +219,11 @@ namespace TEN::Entities::TR3 case CIVVY_STATE_WALK_FORWARD: creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX; - head = laraAiInfo.angle; + extraHeadRot.y = laraAI.angle; if (item->AIBits & PATROL1) { - head = 0; + extraHeadRot.y = 0; item->Animation.TargetState = CIVVY_STATE_WALK_FORWARD; } else if (creature->Mood == MoodType::Escape) @@ -252,7 +250,7 @@ namespace TEN::Entities::TR3 tilt = angle / 2; if (AI.ahead) - head = AI.angle; + extraHeadRot.y = AI.angle; if (item->AIBits & GUARD) item->Animation.TargetState = CIVVY_WAIT; @@ -262,7 +260,7 @@ namespace TEN::Entities::TR3 item->Animation.TargetState = CIVVY_STATE_IDLE; break; } - else if ((item->AIBits & FOLLOW) && (creature->ReachedGoal || laraAiInfo.distance > pow(SECTOR(2), 2))) + else if ((item->AIBits & FOLLOW) && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2))) item->Animation.TargetState = CIVVY_STATE_IDLE; else if (creature->Mood == MoodType::Bored) item->Animation.TargetState = CIVVY_STATE_WALK_FORWARD; @@ -273,11 +271,12 @@ namespace TEN::Entities::TR3 case CIVVY_AIM0: creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX; + creature->Flags = 0; if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (AI.bite && AI.distance < CIVVY_ATTACK0_RANGE) @@ -285,16 +284,16 @@ namespace TEN::Entities::TR3 else item->Animation.TargetState = CIVVY_STATE_IDLE; - creature->Flags = 0; break; case CIVVY_AIM1: creature->MaxTurn = CIVVY_WALK_TURN_RATE_MAX; + creature->Flags = 0; if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (AI.ahead && AI.distance < CIVVY_ATTACK1_RANGE) @@ -302,7 +301,6 @@ namespace TEN::Entities::TR3 else item->Animation.TargetState = CIVVY_STATE_IDLE; - creature->Flags = 0; break; case CIVVY_AIM2: @@ -311,8 +309,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (AI.bite && AI.distance < CIVVY_ATTACK2_RANGE) @@ -327,8 +325,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (!creature->Flags && item->TestBits(JointBitType::Touch, CivvyAttackJoints)) @@ -346,8 +344,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (!creature->Flags && item->TestBits(JointBitType::Touch, CivvyAttackJoints)) @@ -368,8 +366,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (creature->Flags != 2 && item->TestBits(JointBitType::Touch, CivvyAttackJoints)) @@ -385,9 +383,9 @@ namespace TEN::Entities::TR3 } CreatureTilt(item, tilt); - CreatureJoint(item, 0, torsoY); - CreatureJoint(item, 1, torsoX); - CreatureJoint(item, 2, head); + CreatureJoint(item, 0, extraTorsoRot.y); + CreatureJoint(item, 1, extraTorsoRot.x); + CreatureJoint(item, 2, extraHeadRot.y); if (item->Animation.ActiveState < CIVVY_DEATH) { diff --git a/TombEngine/Objects/TR3/Entity/tr3_civvy.h b/TombEngine/Objects/TR3/Entity/tr3_civvy.h index 0bb46d8b8..f541c16e5 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_civvy.h +++ b/TombEngine/Objects/TR3/Entity/tr3_civvy.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void InitialiseCivvy(short itemNumber); void CivvyControl(short itemNumber); diff --git a/TombEngine/Objects/TR3/Entity/tr3_cobra.cpp b/TombEngine/Objects/TR3/Entity/tr3_cobra.cpp index 899c40376..fdd54f640 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_cobra.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_cobra.cpp @@ -14,7 +14,7 @@ using std::vector; -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { constexpr auto COBRA_BITE_ATTACK_DAMAGE = 80; constexpr auto COBRA_BITE_POISON_POTENCY = 8; @@ -23,8 +23,7 @@ namespace TEN::Entities::TR3 constexpr auto COBRA_AWARE_RANGE = SQUARE(SECTOR(1.5f)); constexpr auto COBRA_SLEEP_RANGE = SQUARE(SECTOR(2.5f)); - constexpr auto PLAYER_DISTURB_VELOCITY = 15; - + constexpr auto COBRA_DISTURBANCE_VELOCITY = 15; constexpr auto COBRA_SLEEP_FRAME = 45; const auto CobraBite = BiteInfo(Vector3::Zero, 13); @@ -42,7 +41,7 @@ namespace TEN::Entities::TR3 enum CobraAnim { COBRA_ANIM_IDLE = 0, - COBRA_ANIM_WAKE_UP = 1, + COBRA_ANIM_SLEEP_TO_IDLE = 1, COBRA_ANIM_IDLE_TO_SLEEP = 2, COBRA_ANIM_BITE_ATTACK = 3, COBRA_ANIM_DEATH = 4 @@ -65,9 +64,9 @@ namespace TEN::Entities::TR3 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short head = 0; short angle = 0; short tilt = 0; + short head = 0; if (item->HitPoints <= 0 && item->HitPoints != NOT_TARGETABLE) { @@ -84,19 +83,23 @@ namespace TEN::Entities::TR3 GetCreatureMood(item, &AI, 1); CreatureMood(item, &AI, 1); - bool enemyMoving = false; - bool enemyVisible = false; - if (creature->Enemy && (GlobalCounter & 2)) - { - auto src = GameVector(creature->Enemy->Pose.Position, creature->Enemy->RoomNumber); - auto dest = GameVector(item->Pose.Position, item->RoomNumber); - enemyVisible = LOS(&src, &dest); + bool isEnemyMoving = false; + bool isEnemyVisible = false; - enemyMoving = creature->Enemy->Animation.Velocity.z > PLAYER_DISTURB_VELOCITY || - abs(creature->Enemy->Animation.Velocity.y) > PLAYER_DISTURB_VELOCITY; + if (creature->Enemy != nullptr && (GlobalCounter & 2)) + { + auto origin = GameVector(creature->Enemy->Pose.Position, creature->Enemy->RoomNumber); + auto target = GameVector(item->Pose.Position, item->RoomNumber); + isEnemyVisible = LOS(&origin, &target); + + if (creature->Enemy->Animation.Velocity.z > COBRA_DISTURBANCE_VELOCITY || + abs(creature->Enemy->Animation.Velocity.y) > COBRA_DISTURBANCE_VELOCITY) + { + isEnemyMoving = true; + } } - if (enemyVisible && item->Animation.ActiveState != COBRA_STATE_SLEEP) + if (isEnemyVisible && item->Animation.ActiveState != COBRA_STATE_SLEEP) { creature->Target.x = creature->Enemy->Pose.Position.x; creature->Target.z = creature->Enemy->Pose.Position.z; @@ -119,12 +122,10 @@ namespace TEN::Entities::TR3 creature->Flags = 0; if (AI.distance > COBRA_SLEEP_RANGE) - { item->Animation.TargetState = COBRA_STATE_SLEEP; - } - else if (creature->Enemy->HitPoints > 0 && enemyVisible && - ((AI.ahead && AI.distance < COBRA_ATTACK_RANGE && AI.verticalDistance <= GetBoundsAccurate(item)->Height()) || - item->HitStatus || enemyMoving)) + else if (creature->Enemy->HitPoints > 0 && isEnemyVisible && + ((AI.ahead && AI.distance < COBRA_ATTACK_RANGE && AI.verticalDistance <= GetBoundsAccurate(item)->Height()) || + item->HitStatus || isEnemyMoving)) { item->Animation.TargetState = COBRA_STATE_ATTACK; } @@ -149,12 +150,12 @@ namespace TEN::Entities::TR3 break; case COBRA_STATE_ATTACK: - if (creature->Flags != 1 && + if (!(creature->Flags & 1) && // 1 = is attacking. item->TestBits(JointBitType::Touch, CobraAttackJoints)) { DoDamage(creature->Enemy, COBRA_BITE_ATTACK_DAMAGE); CreatureEffect(item, CobraBite, DoBloodSplat); - creature->Flags = 1; + creature->Flags |= 1; // 1 = is attacking. if (creature->Enemy->IsLara()) GetLaraInfo(creature->Enemy)->PoisonPotency += COBRA_BITE_POISON_POTENCY; diff --git a/TombEngine/Objects/TR3/Entity/tr3_cobra.h b/TombEngine/Objects/TR3/Entity/tr3_cobra.h index 53e817328..261d28bd3 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_cobra.h +++ b/TombEngine/Objects/TR3/Entity/tr3_cobra.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void InitialiseCobra(short itemNum); void CobraControl(short itemNum); diff --git a/TombEngine/Objects/TR3/Entity/tr3_fish_emitter.cpp b/TombEngine/Objects/TR3/Entity/tr3_fish_emitter.cpp index 66a437818..c3b67735f 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_fish_emitter.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_fish_emitter.cpp @@ -9,7 +9,7 @@ #include "Objects/TR3/fish.h" #include "Specific/level.h" -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { int PirahnaHitWait = false; int CarcassItem = NO_ITEM; diff --git a/TombEngine/Objects/TR3/Entity/tr3_fish_emitter.h b/TombEngine/Objects/TR3/Entity/tr3_fish_emitter.h index c8168a1c7..0f3f8cc2a 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_fish_emitter.h +++ b/TombEngine/Objects/TR3/Entity/tr3_fish_emitter.h @@ -1,7 +1,7 @@ #pragma once #include "Game/items.h" -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void SetupShoal(int shoalNumber); void ControlFish(short itemNumber); diff --git a/TombEngine/Objects/TR3/Entity/tr3_flamethrower.cpp b/TombEngine/Objects/TR3/Entity/tr3_flamethrower.cpp index cdda7e9c1..02039738b 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_flamethrower.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_flamethrower.cpp @@ -14,9 +14,12 @@ #include "Game/people.h" #include "Sound/sound.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/setup.h" -namespace TEN::Entities::TR3 +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR3 { const auto FlamethrowerOffset = Vector3Int(0, 340, 0); const auto FlamethrowerBite = BiteInfo(Vector3(0.0f, 340.0f, 64.0f), 7); @@ -41,34 +44,27 @@ namespace TEN::Entities::TR3 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short torsoX = 0; - short torsoY = 0; short angle = 0; short tilt = 0; - short head = 0; + auto extraHeadRot = Vector3Shrt::Zero; + auto extraTorsoRot = Vector3Shrt::Zero; auto pos = Vector3Int(FlamethrowerBite.Position); GetJointAbsPosition(item, &pos, FlamethrowerBite.meshNum); - int random = GetRandomControl(); + int randomInt = GetRandomControl(); if (item->Animation.ActiveState != 6 && item->Animation.ActiveState != 11) { - TriggerDynamicLight(pos.x, pos.y, pos.z, (random & 3) + 6, 24 - ((random / 16) & 3), 16 - ((random / 64) & 3), random & 3); + TriggerDynamicLight(pos.x, pos.y, pos.z, (randomInt & 3) + 6, 24 - ((randomInt / 16) & 3), 16 - ((randomInt / 64) & 3), randomInt & 3); TriggerPilotFlame(itemNumber, 9); } else - { - TriggerDynamicLight(pos.x, pos.y, pos.z, (random & 3) + 10, 31 - ((random / 16) & 3), 24 - ((random / 64) & 3), random & 7); - } + TriggerDynamicLight(pos.x, pos.y, pos.z, (randomInt & 3) + 10, 31 - ((randomInt / 16) & 3), 24 - ((randomInt / 64) & 3), randomInt & 7); if (item->HitPoints <= 0) { if (item->Animation.ActiveState != 7) - { - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 19; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 7; - } + SetAnimation(item, 19); } else { @@ -83,9 +79,8 @@ namespace TEN::Entities::TR3 ItemInfo* target = nullptr; int minDistance = INT_MAX; - for (int i = 0; i < ActiveCreatures.size(); i++) + for (auto& currentCreature : ActiveCreatures) { - auto* currentCreature = ActiveCreatures[i]; if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber) continue; @@ -148,13 +143,13 @@ namespace TEN::Entities::TR3 case 1: creature->MaxTurn = 0; creature->Flags = 0; - head = laraAI.angle; + extraHeadRot.y = laraAI.angle; if (item->AIBits & GUARD) { - head = AIGuard(creature); + extraHeadRot.y = AIGuard(creature); - if (!(GetRandomControl() & 0xFF)) + if (TestProbability(1.0f / 128)) item->Animation.TargetState = 4; break; @@ -170,21 +165,21 @@ namespace TEN::Entities::TR3 else item->Animation.TargetState = 2; } - else if (creature->Mood == MoodType::Bored && AI.ahead && !(GetRandomControl() & 0xFF)) + else if (creature->Mood == MoodType::Bored && AI.ahead && TestProbability(1.0f / 128)) item->Animation.TargetState = 4; - else if (creature->Mood == MoodType::Attack || !(GetRandomControl() & 0xFF)) + else if (creature->Mood == MoodType::Attack || TestProbability(1.0f / 128)) item->Animation.TargetState = 2; break; case 4: - head = laraAI.angle; + extraHeadRot.y = laraAI.angle; if (item->AIBits & GUARD) { - head = AIGuard(creature); + extraHeadRot.y = AIGuard(creature); - if (!(GetRandomControl() & 0xFF)) + if (TestProbability(1.0f / 128)) item->Animation.TargetState = 1; break; @@ -193,7 +188,7 @@ namespace TEN::Entities::TR3 AI.distance < pow(SECTOR(4), 2) && (realEnemy != LaraItem || creature->HurtByLara) || creature->Mood != MoodType::Bored || - !(GetRandomControl() & 0xFF))) + TestProbability(1.0f / 128))) { item->Animation.TargetState = 1; } @@ -201,17 +196,12 @@ namespace TEN::Entities::TR3 break; case 2: - creature->Flags = 0; creature->MaxTurn = ANGLE(5.0f); - head = laraAI.angle; + creature->Flags = 0; + extraHeadRot.y = laraAI.angle; if (item->AIBits & GUARD) - { - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 12; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 1; - item->Animation.TargetState = 1; - } + SetAnimation(item, 12); else if (item->AIBits & PATROL1) item->Animation.TargetState = 2; else if (creature->Mood == MoodType::Escape) @@ -236,8 +226,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; if (Targetable(item, &AI) && AI.distance < pow(SECTOR(4), 2) && @@ -256,8 +246,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; if (Targetable(item, &AI) && AI.distance < pow(SECTOR(4), 2) && @@ -277,8 +267,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; if (Targetable(item, &AI) && AI.distance < pow(SECTOR(4), 2) && @@ -312,12 +302,12 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; if (Targetable(item, &AI) && AI.distance < pow(SECTOR(4), 2) && - (realEnemy != LaraItem || creature->HurtByLara)) + (!realEnemy->IsLara() || creature->HurtByLara)) { item->Animation.TargetState = 6; } @@ -344,9 +334,9 @@ namespace TEN::Entities::TR3 } CreatureTilt(item, tilt); - CreatureJoint(item, 0, torsoY); - CreatureJoint(item, 1, torsoX); - CreatureJoint(item, 2, head); + CreatureJoint(item, 0, extraTorsoRot.y); + CreatureJoint(item, 1, extraTorsoRot.x); + CreatureJoint(item, 2, extraHeadRot.y); CreatureAnimation(itemNumber, angle, 0); } diff --git a/TombEngine/Objects/TR3/Entity/tr3_flamethrower.h b/TombEngine/Objects/TR3/Entity/tr3_flamethrower.h index 61044cf99..761df41cf 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_flamethrower.h +++ b/TombEngine/Objects/TR3/Entity/tr3_flamethrower.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void FlameThrowerControl(short itemNumber); } diff --git a/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp b/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp index d01d4a5fb..e17b60300 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_monkey.cpp @@ -10,25 +10,88 @@ #include "Game/Lara/lara.h" #include "Game/misc.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/setup.h" +using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { - const vector MonkeyAttackJoints = { 10, 13 }; + // TODO: Work out damage constants. + constexpr auto MONKEY_SWIPE_ATTACK_PLAYER_DAMAGE = 40; + constexpr auto MONKEY_SWIPE_ATTACK_CREATURE_DAMAGE = 20; + + // TODO: Range constants. + const auto MonkeyBite = BiteInfo(Vector3(10.0f, 10.0f, 11.0f), 13); + const vector MonkeyAttackJoints = { 10, 13 }; + + enum MonkeyState + { + // No states 0-1. + MONKEY_STATE_WALK_FORWARD = 2, + MONKEY_STATE_IDLE = 3, + MONKEY_STATE_RUN_FORWARD = 4, + MONKEY_STATE_BITE_ATTACK = 5, // Check. + MONKEY_STATE_SIT = 6, + MONKEY_STATE_SIT_EAT = 7, + MONKEY_STATE_SIT_SCRATCH = 8, + MONKEY_STATE_RUN_FORWARD_ROLL = 9, + MONKEY_STATE_POUND_GROUND = 10, + MONKEY_STATE_DEATH = 11, + MONKEY_STATE_SWIPE_ATTACK = 12, + MONKEY_STATE_JUMP_ATTACK = 13, + MONKEY_STATE_HIGH_JUMP_ATTACK = 14, + MONKEY_STATE_VAULT_UP_1_BLOCK = 15, + MONKEY_STATE_VAULT_UP_0_POINT_3_BLOCKS = 16, + MONKEY_STATE_VAULT_UP_0_POINT_2_BLOCKS = 17, + MONKEY_STATE_VAULT_DOWN_1_BLOCK = 18, + MONKEY_STATE_VAULT_DOWN_0_POINT_3_BLOCKS = 19, + MONKEY_STATE_VAULT_DOWN_0_POINT_2_BLOCKS = 20 + }; + + enum MonkeyAnim + { + MONKEY_ANIM_WALK_FORWARD = 0, + MONKEY_ANIM_WALK_FORWARD_TO_SIT = 1, + MONKEY_ANIM_SIT = 2, + MONKEY_ANIM_SIT_TO_WALK_FORWARD = 3, + MONKEY_ANIM_SIT_EAT = 4, + MONKEY_ANIM_SIT_SCRATCH = 5, + MONKEY_ANIM_RUN_FORWARD = 6, + MONKEY_ANIM_RUN_FORWARD_ROLL = 7, + MONKEY_ANIM_IDLE_POUND_GROUND = 8, + MONKEY_ANIM_IDLE = 9, + MONKEY_ANIM_IDLE_TO_RUN_FORWARD = 10, + MONKEY_ANIM_WALK_FORWARD_TO_RUN_FORWARD = 11, + MONKEY_ANIM_RUN_FORWARD_TO_IDLE = 12, + MONKEY_ANIM_SIT_TO_IDLE = 13, + MONKEY_ANIM_DEATH = 14, + MONKEY_ANIM_RUN_FORWARD_TO_WALK_FORWARD = 15, + MONKEY_ANIM_IDLE_TO_SIT = 16, + MONKEY_ANIM_VAULT_UP_1_BLOCK = 17, + MONKEY_ANIM_VAULT_UP_0_POINT_3_BLOCKS = 18, + MONKEY_ANIM_VAULT_UP_0_POINT_2_BLOCKS = 19, + MONKEY_ANIM_VAULT_DOWN_1_BLOCK = 20, + MONKEY_ANIM_VAULT_DOWN_0_POINT_3_BLOCKS = 21, + MONKEY_ANIM_VAULT_DOWN_0_POINT_2_BLOCKS = 22, + MONKEY_ANIM_SWIPE_ATTACK = 23, + MONKEY_ANIM_JUMP_ATTACK = 24, + MONKEY_ANIM_BITE_ATTACK = 25, + MONKEY_ANIM_HIGH_JUMP_ATTACK_START = 26, + MONKEY_ANIM_HIGH_JUMP_ATTACK_CONTINUE = 27, + MONKEY_ANIM_HIGH_JUMP_ATTACK_END = 28, + MONKEY_ANIM_IDLE_TO_WALK_FORWARD = 29, + MONKEY_ANIM_WALK_FORWARD_TO_IDLE = 30 + }; void InitialiseMonkey(short itemNumber) { auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - - item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 2; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 6; - item->Animation.TargetState = 6; + SetAnimation(item, MONKEY_ANIM_SIT); } void MonkeyControl(short itemNumber) @@ -39,20 +102,17 @@ namespace TEN::Entities::TR3 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short headX = 0; - short headY = 0; - short torsoY = 0; short angle = 0; short tilt = 0; + auto extraHeadRot = Vector3Shrt::Zero; + auto extraTorsoRot = Vector3Shrt::Zero; if (item->HitPoints <= 0) { - if (item->Animation.ActiveState != 11) + if (item->Animation.ActiveState != MONKEY_STATE_DEATH) { + SetAnimation(item, MONKEY_ANIM_DEATH); item->MeshBits = ALL_JOINT_BITS; - item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 14; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 11; } } else @@ -63,12 +123,11 @@ namespace TEN::Entities::TR3 creature->Enemy = LaraItem; else { - int minDistance = 0x7FFFFFFF; creature->Enemy = nullptr; + int minDistance = INT_MAX; - for (int i = 0; i < ActiveCreatures.size(); i++) + for (auto& currentCreature : ActiveCreatures) { - auto* currentCreature = ActiveCreatures[i]; if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber) continue; @@ -109,11 +168,11 @@ namespace TEN::Entities::TR3 AI_INFO AI; CreatureAIInfo(item, &AI); - if (!creature->HurtByLara && creature->Enemy == LaraItem) + if (!creature->HurtByLara && creature->Enemy->IsLara()) creature->Enemy = nullptr; AI_INFO laraAI; - if (creature->Enemy == LaraItem) + if (creature->Enemy->IsLara()) { laraAI.angle = AI.angle; laraAI.distance = AI.distance; @@ -146,144 +205,147 @@ namespace TEN::Entities::TR3 switch (item->Animation.ActiveState) { - case 6: - creature->Flags = 0; + case MONKEY_STATE_SIT: creature->MaxTurn = 0; - torsoY = laraAI.angle; + creature->Flags = 0; + extraTorsoRot.y = laraAI.angle; if (item->AIBits & GUARD) { - torsoY = AIGuard(creature); - if (!(GetRandomControl() & 0xF)) + extraTorsoRot.y = AIGuard(creature); + if (TestProbability(0.06f)) { - if (GetRandomControl() & 0x1) - item->Animation.TargetState = 8; + if (TestProbability(0.5f)) + item->Animation.TargetState = MONKEY_STATE_SIT_EAT; else - item->Animation.TargetState = 7; + item->Animation.TargetState = MONKEY_STATE_SIT_SCRATCH; } break; } - else if (item->AIBits & PATROL1) - item->Animation.TargetState = 2; + item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD; else if (creature->Mood == MoodType::Escape) - item->Animation.TargetState = 3; + item->Animation.TargetState = MONKEY_STATE_IDLE; else if (creature->Mood == MoodType::Bored) { if (item->Animation.RequiredState) item->Animation.TargetState = item->Animation.RequiredState; - else if (!(GetRandomControl() & 0xF)) - item->Animation.TargetState = 2; - else if (!(GetRandomControl() & 0xF)) + else if (TestProbability(0.06f)) + item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD; + else if (TestProbability(0.06f)) { - if (GetRandomControl() & 0x1) - item->Animation.TargetState = 8; + if (TestProbability(0.5f)) + item->Animation.TargetState = MONKEY_STATE_SIT_EAT; else - item->Animation.TargetState = 7; + item->Animation.TargetState = MONKEY_STATE_SIT_SCRATCH; } } - else if ((item->AIBits & FOLLOW) && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2))) + else if ((item->AIBits & FOLLOW) && + (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2))) { if (item->Animation.RequiredState) item->Animation.TargetState = item->Animation.RequiredState; else if (AI.ahead) - item->Animation.TargetState = 6; + item->Animation.TargetState = MONKEY_STATE_SIT; else - item->Animation.TargetState = 3; + item->Animation.TargetState = MONKEY_STATE_IDLE; } else if (AI.bite && AI.distance < pow(682, 2)) - item->Animation.TargetState = 3; + item->Animation.TargetState = MONKEY_STATE_IDLE; else if (AI.bite && AI.distance < pow(682, 2)) - item->Animation.TargetState = 2; + item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD; else - item->Animation.TargetState = 3; + item->Animation.TargetState = MONKEY_STATE_IDLE; break; - case 3: + case MONKEY_STATE_IDLE: creature->MaxTurn = 0; creature->Flags = 0; - torsoY = laraAI.angle; + extraTorsoRot.y = laraAI.angle; if (item->AIBits & GUARD) { - torsoY = AIGuard(creature); + extraTorsoRot.y = AIGuard(creature); - if (!(GetRandomControl() & 15)) + if (TestProbability(0.06f)) { - if (GetRandomControl() & 1) - item->Animation.TargetState = 10; + if (TestProbability(0.5f)) + item->Animation.TargetState = MONKEY_STATE_POUND_GROUND; else - item->Animation.TargetState = 6; + item->Animation.TargetState = MONKEY_STATE_SIT; } break; } else if (item->AIBits & PATROL1) - item->Animation.TargetState = 2; + item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD; else if (creature->Mood == MoodType::Escape) { if (Lara.TargetEntity != item && AI.ahead) - item->Animation.TargetState = 3; + item->Animation.TargetState = MONKEY_STATE_IDLE; else - item->Animation.TargetState = 4; + item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD; } else if (creature->Mood == MoodType::Bored) { if (item->Animation.RequiredState) item->Animation.TargetState = item->Animation.RequiredState; - else if (!(GetRandomControl() & 15)) - item->Animation.TargetState = 2; - else if (!(GetRandomControl() & 15)) + else if (TestProbability(0.06f)) + item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD; + else if (TestProbability(0.06f)) { - if (GetRandomControl() & 1) - item->Animation.TargetState = 10; + if (TestProbability(0.5f)) + item->Animation.TargetState = MONKEY_STATE_POUND_GROUND; else - item->Animation.TargetState = 6; + item->Animation.TargetState = MONKEY_STATE_SIT; } } - else if (item->AIBits & FOLLOW && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2))) + else if (item->AIBits & FOLLOW && + (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2))) { if (item->Animation.RequiredState) item->Animation.TargetState = item->Animation.RequiredState; else if (AI.ahead) - item->Animation.TargetState = 6; + item->Animation.TargetState = MONKEY_STATE_SIT; else - item->Animation.TargetState = 4; + item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD; } else if (AI.bite && AI.distance < pow(341, 2)) { if (LaraItem->Pose.Position.y < item->Pose.Position.y) - item->Animation.TargetState = 13; + item->Animation.TargetState = MONKEY_STATE_JUMP_ATTACK; else - item->Animation.TargetState = 12; + item->Animation.TargetState = MONKEY_STATE_SWIPE_ATTACK; } else if (AI.bite && AI.distance < pow(682, 2)) - item->Animation.TargetState = 14; + item->Animation.TargetState = MONKEY_STATE_HIGH_JUMP_ATTACK; else if (AI.bite && AI.distance < pow(682, 2)) - item->Animation.TargetState = 2; - else if (AI.distance < pow(682, 2) && creature->Enemy != LaraItem && creature->Enemy != nullptr && - creature->Enemy->ObjectNumber != ID_AI_PATROL1 && creature->Enemy->ObjectNumber != ID_AI_PATROL2 && - abs(item->Pose.Position.y - creature->Enemy->Pose.Position.y) < 256) + item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD; + else if (AI.distance < pow(682, 2) && + !creature->Enemy->IsLara() && creature->Enemy != nullptr && + creature->Enemy->ObjectNumber != ID_AI_PATROL1 && + creature->Enemy->ObjectNumber != ID_AI_PATROL2 && + abs(item->Pose.Position.y - creature->Enemy->Pose.Position.y) < CLICK(1)) { - item->Animation.TargetState = 5; + item->Animation.TargetState = MONKEY_STATE_BITE_ATTACK; } else if (AI.bite && AI.distance < pow(SECTOR(1), 2)) - item->Animation.TargetState = 9; + item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD_ROLL; else - item->Animation.TargetState = 4; + item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD; break; - case 5: + case MONKEY_STATE_BITE_ATTACK: creature->ReachedGoal = true; if (creature->Enemy == nullptr) break; else if ((creature->Enemy->ObjectNumber == ID_SMALLMEDI_ITEM || creature->Enemy->ObjectNumber == ID_KEY_ITEM4) && - item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 12) + item->Animation.FrameNumber == (g_Level.Anims[item->Animation.AnimNumber].frameBase + 12)) { if (creature->Enemy->RoomNumber == NO_ROOM || creature->Enemy->Status == ITEM_INVISIBLE || @@ -298,9 +360,8 @@ namespace TEN::Entities::TR3 creature->Enemy->RoomNumber = NO_ROOM; creature->Enemy->CarriedItem = NO_ITEM; - for (int i = 0; i < ActiveCreatures.size(); i++) + for (auto& currentCreature : ActiveCreatures) { - auto* currentCreature = ActiveCreatures[i]; if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber) continue; @@ -318,15 +379,14 @@ namespace TEN::Entities::TR3 } } } - else if (creature->Enemy->ObjectNumber == ID_AI_AMBUSH && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 12) + else if (creature->Enemy->ObjectNumber == ID_AI_AMBUSH && + item->Animation.FrameNumber == (g_Level.Anims[item->Animation.AnimNumber].frameBase + 12)) { item->AIBits = 0; auto* carriedItem = &g_Level.Items[item->CarriedItem]; - carriedItem->Pose.Position.x = item->Pose.Position.x; - carriedItem->Pose.Position.y = item->Pose.Position.y; - carriedItem->Pose.Position.z = item->Pose.Position.z; + carriedItem->Pose.Position = item->Pose.Position; ItemNewRoom(item->CarriedItem, item->RoomNumber); item->CarriedItem = NO_ITEM; @@ -348,60 +408,106 @@ namespace TEN::Entities::TR3 break; - case 2: + case MONKEY_STATE_WALK_FORWARD: creature->MaxTurn = ANGLE(7.0f); - torsoY = laraAI.angle; + extraTorsoRot.y = laraAI.angle; if (item->AIBits & PATROL1) { - item->Animation.TargetState = 2; - torsoY = 0; + item->Animation.TargetState = MONKEY_STATE_WALK_FORWARD; + extraTorsoRot.y = 0; } else if (creature->Mood == MoodType::Escape) - item->Animation.TargetState = 4; + item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD; else if (creature->Mood == MoodType::Bored) { - if (GetRandomControl() < 256) - item->Animation.TargetState = 6; + if (TestProbability(1.0f / 128)) + item->Animation.TargetState = MONKEY_STATE_SIT; } else if (AI.bite && AI.distance < pow(682, 2)) - item->Animation.TargetState = 3; + item->Animation.TargetState = MONKEY_STATE_IDLE; break; - case 4: + case MONKEY_STATE_RUN_FORWARD: creature->MaxTurn = ANGLE(11.0f); tilt = angle / 2; if (AI.ahead) - torsoY = AI.angle; + extraTorsoRot.y = AI.angle; if (item->AIBits & GUARD) - item->Animation.TargetState = 3; + item->Animation.TargetState = MONKEY_STATE_IDLE; else if (creature->Mood == MoodType::Escape) { if (Lara.TargetEntity != item && AI.ahead) - item->Animation.TargetState = 3; + item->Animation.TargetState = MONKEY_STATE_IDLE; + break; } - else if ((item->AIBits & FOLLOW) && (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2))) - item->Animation.TargetState = 3; + else if ((item->AIBits & FOLLOW) && + (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2))) + { + item->Animation.TargetState = MONKEY_STATE_IDLE; + } else if (creature->Mood == MoodType::Bored) - item->Animation.TargetState = 9; + item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD_ROLL; else if (AI.distance < pow(682, 2)) - item->Animation.TargetState = 3; + item->Animation.TargetState = MONKEY_STATE_IDLE; else if (AI.bite && AI.distance < pow(SECTOR(1), 2)) - item->Animation.TargetState = 9; + item->Animation.TargetState = MONKEY_STATE_RUN_FORWARD_ROLL; break; - case 12: + case MONKEY_STATE_SWIPE_ATTACK: creature->MaxTurn = 0; if (AI.ahead) { - headY = AI.angle; - headX = AI.xAngle; + extraHeadRot.x = AI.xAngle; + extraHeadRot.y = AI.angle; + } + + if (abs(AI.angle) < ANGLE(7.0f)) + item->Pose.Orientation.y += AI.angle; + else if (AI.angle < 0) + item->Pose.Orientation.y -= ANGLE(7.0f); + else + item->Pose.Orientation.y += ANGLE(7.0f); + + if (enemy->IsLara()) + { + if (!creature->Flags && item->TestBits(JointBitType::Touch, MonkeyAttackJoints)) + { + DoDamage(enemy, MONKEY_SWIPE_ATTACK_PLAYER_DAMAGE); + CreatureEffect(item, MonkeyBite, DoBloodSplat); + creature->Flags = 1; + } + } + else + { + if (!creature->Flags && enemy) + { + if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(1) && + abs(enemy->Pose.Position.y - item->Pose.Position.y) <= CLICK(1) && + abs(enemy->Pose.Position.z - item->Pose.Position.z) < CLICK(1)) + { + DoDamage(enemy, MONKEY_SWIPE_ATTACK_CREATURE_DAMAGE); + CreatureEffect(item, MonkeyBite, DoBloodSplat); + creature->Flags = 1; + } + } + } + + break; + + case MONKEY_STATE_JUMP_ATTACK: + creature->MaxTurn = 0; + + if (AI.ahead) + { + extraHeadRot.x = AI.xAngle; + extraHeadRot.y = AI.angle; } if (abs(AI.angle) < ANGLE(7.0f)) @@ -422,11 +528,9 @@ namespace TEN::Entities::TR3 } else { - if (!creature->Flags && enemy) + if (!creature->Flags && enemy != nullptr) { - if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(1) && - abs(enemy->Pose.Position.y - item->Pose.Position.y) <= CLICK(1) && - abs(enemy->Pose.Position.z - item->Pose.Position.z) < CLICK(1)) + if (Vector3Int::Distance(item->Pose.Position, enemy->Pose.Position) <= CLICK(1)) { DoDamage(enemy, 20); CreatureEffect(item, MonkeyBite, DoBloodSplat); @@ -437,55 +541,13 @@ namespace TEN::Entities::TR3 break; - case 13: + case MONKEY_STATE_HIGH_JUMP_ATTACK: creature->MaxTurn = 0; if (AI.ahead) { - headY = AI.angle; - headX = AI.xAngle; - } - - if (abs(AI.angle) < ANGLE(7.0f)) - item->Pose.Orientation.y += AI.angle; - else if (AI.angle < 0) - item->Pose.Orientation.y -= ANGLE(7.0f); - else - item->Pose.Orientation.y += ANGLE(7.0f); - - if (enemy->IsLara()) - { - if (!creature->Flags && item->TestBits(JointBitType::Touch, MonkeyAttackJoints)) - { - DoDamage(enemy, 40); - CreatureEffect(item, MonkeyBite, DoBloodSplat); - creature->Flags = 1; - } - } - else - { - if (!creature->Flags && enemy) - { - if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(1) && - abs(enemy->Pose.Position.y - item->Pose.Position.y) <= CLICK(1) && - abs(enemy->Pose.Position.z - item->Pose.Position.z) < CLICK(1)) - { - DoDamage(enemy, 20); - CreatureEffect(item, MonkeyBite, DoBloodSplat); - creature->Flags = 1; - } - } - } - - break; - - case 14: - creature->MaxTurn = 0; - - if (AI.ahead) - { - headX = AI.xAngle; - headY = AI.angle; + extraHeadRot.x = AI.xAngle; + extraHeadRot.y = AI.angle; } if (abs(AI.angle) < ANGLE(7.0f)) @@ -508,9 +570,7 @@ namespace TEN::Entities::TR3 { if (creature->Flags != 1 && enemy) { - if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < CLICK(1) && - abs(enemy->Pose.Position.y - item->Pose.Position.y) <= CLICK(1) && - abs(enemy->Pose.Position.z - item->Pose.Position.z) < CLICK(1)) + if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= CLICK(1)) { DoDamage(enemy, 25); CreatureEffect(item, MonkeyBite, DoBloodSplat); @@ -524,54 +584,42 @@ namespace TEN::Entities::TR3 } CreatureTilt(item, tilt); - CreatureJoint(item, 0, headY); - CreatureJoint(item, 1, headX); - CreatureJoint(item, 2, torsoY); + CreatureJoint(item, 0, extraHeadRot.y); + CreatureJoint(item, 1, extraHeadRot.x); + CreatureJoint(item, 2, extraTorsoRot.y); - if (item->Animation.ActiveState < 15) + if (item->Animation.ActiveState < MONKEY_STATE_VAULT_UP_1_BLOCK) { - switch (CreatureVault(itemNumber, angle, 2, 128)) + switch (CreatureVault(itemNumber, angle, 2, CLICK(0.5f))) { case 2: + SetAnimation(item, MONKEY_ANIM_VAULT_UP_0_POINT_2_BLOCKS); creature->MaxTurn = 0; - item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 19; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 17; break; case 3: + SetAnimation(item, MONKEY_ANIM_VAULT_UP_0_POINT_3_BLOCKS); creature->MaxTurn = 0; - item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 18; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 16; break; case 4: + SetAnimation(item, MONKEY_ANIM_VAULT_UP_1_BLOCK); creature->MaxTurn = 0; - item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 17; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 15; break; case -2: + SetAnimation(item, MONKEY_ANIM_VAULT_DOWN_0_POINT_2_BLOCKS); creature->MaxTurn = 0; - item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 22; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 20; break; case -3: + SetAnimation(item, MONKEY_ANIM_VAULT_DOWN_0_POINT_3_BLOCKS); creature->MaxTurn = 0; - item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 21; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 19; break; case -4: + SetAnimation(item, MONKEY_ANIM_VAULT_DOWN_1_BLOCK); creature->MaxTurn = 0; - item->Animation.AnimNumber = Objects[ID_MONKEY].animIndex + 20; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 18; break; } } diff --git a/TombEngine/Objects/TR3/Entity/tr3_monkey.h b/TombEngine/Objects/TR3/Entity/tr3_monkey.h index e136456b9..8b617e255 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_monkey.h +++ b/TombEngine/Objects/TR3/Entity/tr3_monkey.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void InitialiseMonkey(short itemNumber); void MonkeyControl(short itemNumber); diff --git a/TombEngine/Objects/TR3/Entity/tr3_mp_gun.cpp b/TombEngine/Objects/TR3/Entity/tr3_mp_gun.cpp index 2526236f9..edce48023 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_mp_gun.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_mp_gun.cpp @@ -14,15 +14,17 @@ #include "Game/people.h" #include "Sound/sound.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/setup.h" -namespace TEN::Entities::TR3 +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR3 { const auto MPGunBite = BiteInfo(Vector3(0.0f, 160.0f, 40.0f), 13); enum MPGunState { - MPGUN_STATE_NONE = 0, MPGUN_STATE_WAIT = 1, MPGUN_STATE_WALK = 2, MPGUN_STATE_RUN = 3, @@ -59,11 +61,10 @@ namespace TEN::Entities::TR3 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short head = 0; short angle = 0; short tilt = 0; - short torsoX = 0; - short torsoY = 0; + short head = 0; + auto extraTorsoRot = Vector3Shrt::Zero; if (creature->FiredWeapon) { @@ -76,8 +77,8 @@ namespace TEN::Entities::TR3 if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED)) { - DoLotsOfBlood(item->Pose.Position.x, item->Pose.Position.y - (GetRandomControl() & 255) - 32, item->Pose.Position.z, (GetRandomControl() & 127) + 128, GetRandomControl() * 2, item->RoomNumber, 3); DoDamage(item, 20); + DoLotsOfBlood(item->Pose.Position.x, item->Pose.Position.y - (GetRandomControl() & 255) - 32, item->Pose.Position.z, (GetRandomControl() & 127) + 128, GetRandomControl() * 2, item->RoomNumber, 3); } AI_INFO AI; @@ -91,7 +92,7 @@ namespace TEN::Entities::TR3 item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = 13; } - else if (!(GetRandomControl() & 3) && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 1) + else if (TestProbability(0.25f) && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 1) { CreatureAIInfo(item, &AI); @@ -101,8 +102,8 @@ namespace TEN::Entities::TR3 AI.angle < ANGLE(45.0f)) { head = AI.angle; - torsoY = AI.angle; - ShotLara(item, &AI, MPGunBite, torsoY, 32); + extraTorsoRot.y = AI.angle; + ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32); SoundEffect(SFX_TR3_OIL_SMG_FIRE, &item->Pose, SoundEnvironment::Land, 1.0f, 0.7f); creature->FiredWeapon = 1; } @@ -220,10 +221,9 @@ namespace TEN::Entities::TR3 item->Animation.TargetState = MPGUN_STATE_RUN; else if (Targetable(item, &AI)) { - int random = GetRandomControl(); - if (random < 0x2000) + if (TestProbability(0.25f)) item->Animation.TargetState = MPGUN_STATE_SHOOT_1; - else if (random < 0x4000) + else if (TestProbability(0.5f)) item->Animation.TargetState = MPGUN_STATE_SHOOT_2; else item->Animation.TargetState = MPGUN_STATE_AIM_3; @@ -305,17 +305,18 @@ namespace TEN::Entities::TR3 case MPGUN_STATE_AIM_1: if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 12 || - (item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 1 && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 10)) + (item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 1 && + item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 10)) { - if (!ShotLara(item, &AI, MPGunBite, torsoY, 32)) + if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32)) item->Animation.RequiredState = MPGUN_STATE_WAIT; } - else if (item->HitStatus && !(GetRandomControl() & 0x3) && cover) + else if (item->HitStatus && TestProbability(0.25f) && cover) { item->Animation.RequiredState = MPGUN_STATE_CROUCH; item->Animation.TargetState = MPGUN_STATE_WAIT; @@ -326,8 +327,8 @@ namespace TEN::Entities::TR3 case MPGUN_STATE_SHOOT_1: if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (item->Animation.RequiredState == MPGUN_STATE_WAIT) @@ -338,16 +339,16 @@ namespace TEN::Entities::TR3 case MPGUN_STATE_SHOOT_2: if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase) { - if (!ShotLara(item, &AI, MPGunBite, torsoY, 32)) + if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32)) item->Animation.TargetState = MPGUN_STATE_WAIT; } - else if (item->HitStatus && !(GetRandomControl() & 0x3) && cover) + else if (item->HitStatus && TestProbability(0.25f) && cover) { item->Animation.RequiredState = MPGUN_STATE_CROUCH; item->Animation.TargetState = MPGUN_STATE_WAIT; @@ -359,17 +360,17 @@ namespace TEN::Entities::TR3 case MPGUN_STATE_SHOOT_3B: if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase || item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 11) { - if (!ShotLara(item, &AI, MPGunBite, torsoY, 32)) + if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32)) item->Animation.TargetState = MPGUN_STATE_WAIT; } - else if (item->HitStatus && !(GetRandomControl() & 0x3) && cover) + else if (item->HitStatus && TestProbability(0.25f) && cover) { item->Animation.RequiredState = MPGUN_STATE_CROUCH; item->Animation.TargetState = MPGUN_STATE_WAIT; @@ -380,17 +381,19 @@ namespace TEN::Entities::TR3 case MPGUN_STATE_AIM_4: if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } - if ((item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 18 && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 17) || - (item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 19 && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 6)) + if ((item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 18 && + item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 17) || + (item->Animation.AnimNumber == Objects[ID_MP_WITH_GUN].animIndex + 19 && + item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 6)) { - if (!ShotLara(item, &AI, MPGunBite, torsoY, 32)) + if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32)) item->Animation.RequiredState = MPGUN_STATE_WALK; } - else if (item->HitStatus && !(GetRandomControl() & 0x3) && cover) + else if (item->HitStatus && TestProbability(0.25f) && cover) { item->Animation.RequiredState = MPGUN_STATE_CROUCH; item->Animation.TargetState = MPGUN_STATE_WAIT; @@ -405,8 +408,8 @@ namespace TEN::Entities::TR3 case MPGUN_STATE_SHOOT_4B: if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (item->Animation.RequiredState == MPGUN_STATE_WALK) @@ -414,7 +417,7 @@ namespace TEN::Entities::TR3 if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + 16) { - if (!ShotLara(item, &AI, MPGunBite, torsoY, 32)) + if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32)) item->Animation.TargetState = MPGUN_STATE_WALK; } @@ -431,7 +434,7 @@ namespace TEN::Entities::TR3 if (Targetable(item, &AI)) item->Animation.TargetState = MPGUN_STATE_CROUCH_AIM; - else if (item->HitStatus || !cover || (AI.ahead && !(GetRandomControl() & 0x1F))) + else if (item->HitStatus || !cover || (AI.ahead && TestProbability(1.0f / 30))) item->Animation.TargetState = MPGUN_STATE_STAND; else item->Animation.TargetState = MPGUN_STATE_CROUCH_WALK; @@ -442,7 +445,7 @@ namespace TEN::Entities::TR3 creature->MaxTurn = ANGLE(1.0f); if (AI.ahead) - torsoY = AI.angle; + extraTorsoRot.y = AI.angle; if (Targetable(item, &AI)) item->Animation.TargetState = MPGUN_STATE_CROUCH_SHOT; @@ -453,11 +456,11 @@ namespace TEN::Entities::TR3 case MPGUN_STATE_CROUCH_SHOT: if (AI.ahead) - torsoY = AI.angle; + extraTorsoRot.y = AI.angle; if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase) { - if (!ShotLara(item, &AI, MPGunBite, torsoY, 32) || !(GetRandomControl() & 0x7)) + if (!ShotLara(item, &AI, MPGunBite, extraTorsoRot.y, 32) || TestProbability(0.125f)) item->Animation.TargetState = MPGUN_STATE_CROUCHED; } @@ -469,7 +472,7 @@ namespace TEN::Entities::TR3 if (AI.ahead) head = AI.angle; - if (Targetable(item, &AI) || item->HitStatus || !cover || (AI.ahead && !(GetRandomControl() & 0x1F))) + if (Targetable(item, &AI) || item->HitStatus || !cover || (AI.ahead && TestProbability(1.0f / 30))) item->Animation.TargetState = MPGUN_STATE_CROUCHED; break; @@ -477,8 +480,8 @@ namespace TEN::Entities::TR3 } CreatureTilt(item, tilt); - CreatureJoint(item, 0, torsoY); - CreatureJoint(item, 1, torsoX); + CreatureJoint(item, 0, extraTorsoRot.y); + CreatureJoint(item, 1, extraTorsoRot.x); CreatureJoint(item, 2, head); CreatureAnimation(itemNumber, angle, tilt); } diff --git a/TombEngine/Objects/TR3/Entity/tr3_mp_gun.h b/TombEngine/Objects/TR3/Entity/tr3_mp_gun.h index 9c768f11b..47a21c4e1 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_mp_gun.h +++ b/TombEngine/Objects/TR3/Entity/tr3_mp_gun.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void MPGunControl(short itemNumber); } diff --git a/TombEngine/Objects/TR3/Entity/tr3_mp_stick.cpp b/TombEngine/Objects/TR3/Entity/tr3_mp_stick.cpp index 94676c6f2..56c162637 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_mp_stick.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_mp_stick.cpp @@ -11,20 +11,21 @@ #include "Game/people.h" #include "Sound/sound.h" #include "Specific/level.h" +#include "Specific/prng.h" #include "Specific/setup.h" +using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { const auto MPStickBite1 = BiteInfo(Vector3(247.0f, 10.0f, 11.0f), 13); const auto MPStickBite2 = BiteInfo(Vector3(0.0f, 0.0f, 100.0f), 6); const vector MPStickPunchAttackJoints = { 10, 13 }; - const vector MPStickKickAttackJoints = { 5, 6 }; + const vector MPStickKickAttackJoints = { 5, 6 }; enum MPStickState { - MPSTICK_STATE_NONE, MPSTICK_STATE_STOP, MPSTICK_STATE_WALK, MPSTICK_STATE_PUNCH2, @@ -54,10 +55,7 @@ namespace TEN::Entities::TR3 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - - item->Animation.AnimNumber = Objects[ID_MP_WITH_STICK].animIndex + 6; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = item->Animation.TargetState = MPSTICK_STATE_STOP; + SetAnimation(item, 6); } void MPStickControl(short itemNumber) @@ -68,11 +66,10 @@ namespace TEN::Entities::TR3 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short head = 0; short angle = 0; short tilt = 0; - short torsoX = 0; - short torsoY = 0; + short head = 0; + auto extraTorsoRot = Vector3Shrt::Zero; if (item->BoxNumber != NO_BOX && (g_Level.Boxes[item->BoxNumber].flags & BLOCKED)) { @@ -85,9 +82,7 @@ namespace TEN::Entities::TR3 { if (item->Animation.ActiveState != MPSTICK_STATE_DEATH) { - item->Animation.AnimNumber = Objects[ID_MP_WITH_STICK].animIndex + 26; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = MPSTICK_STATE_DEATH; + SetAnimation(item, 26); creature->LOT.Step = 256; } } @@ -103,7 +98,7 @@ namespace TEN::Entities::TR3 int dz = LaraItem->Pose.Position.z - item->Pose.Position.z; laraAI.distance = pow(dx, 2) + pow(dx, 2); - int bestDistance = 0x7fffffff; + int bestDistance = INT_MAX; for (int slot = 0; slot < ActiveCreatures.size(); slot++) { auto* currentCreature = ActiveCreatures[slot]; @@ -184,7 +179,7 @@ namespace TEN::Entities::TR3 if (item->AIBits & GUARD) { head = AIGuard(creature); - if (!(GetRandomControl() & 0xFF)) + if (TestProbability(1.0f / 256)) { if (item->Animation.ActiveState == MPSTICK_STATE_STOP) item->Animation.TargetState = MPSTICK_STATE_WAIT; @@ -194,7 +189,6 @@ namespace TEN::Entities::TR3 break; } - else if (item->AIBits & PATROL1) item->Animation.TargetState = MPSTICK_STATE_WALK; @@ -240,7 +234,7 @@ namespace TEN::Entities::TR3 item->Animation.TargetState = MPSTICK_STATE_RUN; else if (creature->Mood == MoodType::Bored) { - if (GetRandomControl() < 0x100) + if (TestProbability(1.0f / 128)) { item->Animation.RequiredState = MPSTICK_STATE_WAIT; item->Animation.TargetState = MPSTICK_STATE_STOP; @@ -288,8 +282,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (AI.bite && AI.distance < pow(SECTOR(0.5f), 2)) @@ -305,8 +299,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (AI.ahead && AI.distance < pow(SECTOR(1), 2)) @@ -322,8 +316,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2)) @@ -338,32 +332,30 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } - if (enemy->IsLara()) + if (creature->Enemy->IsLara()) { if (!creature->Flags && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints)) { - CreatureEffect(item, MPStickBite1, DoBloodSplat); DoDamage(enemy, 80); + CreatureEffect(item, MPStickBite1, DoBloodSplat); SoundEffect(SFX_TR4_LARA_THUD, &item->Pose); creature->Flags = 1; } } else { - if (!creature->Flags && enemy) + if (!creature->Flags && enemy != nullptr) { - if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) && - abs(enemy->Pose.Position.y - item->Pose.Position.y) <= SECTOR(0.25f) && - abs(enemy->Pose.Position.z - item->Pose.Position.z) < SECTOR(0.25f)) + if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.25f)) { - creature->Flags = 1; - CreatureEffect(item, MPStickBite1, DoBloodSplat); DoDamage(enemy, 5); + CreatureEffect(item, MPStickBite1, DoBloodSplat); SoundEffect(SFX_TR4_LARA_THUD, &item->Pose); + creature->Flags = 1; } } } @@ -375,32 +367,30 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } - if (enemy->IsLara()) + if (creature->Enemy->IsLara()) { if (!creature->Flags && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints)) { + DoDamage(creature->Enemy, 80); CreatureEffect(item, MPStickBite1, DoBloodSplat); - DoDamage(enemy, 80); SoundEffect(SFX_TR4_LARA_THUD, &item->Pose); creature->Flags = 1; } } else { - if (!creature->Flags && enemy) + if (!creature->Flags && creature->Enemy != nullptr) { - if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) && - abs(enemy->Pose.Position.y - item->Pose.Position.y) <= SECTOR(0.25f) && - abs(enemy->Pose.Position.z - item->Pose.Position.z) < SECTOR(0.25f)) + if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.25f)) { - creature->Flags = 1; + DoDamage(creature->Enemy, 5); CreatureEffect(item, MPStickBite1, DoBloodSplat); - DoDamage(enemy, 5); SoundEffect(SFX_TR4_LARA_THUD, &item->Pose); + creature->Flags = 1; } } } @@ -415,32 +405,30 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } - if (enemy->IsLara()) + if (creature->Enemy->IsLara()) { if (creature->Flags != 2 && item->TestBits(JointBitType::Touch, MPStickPunchAttackJoints)) { + DoDamage(creature->Enemy, 100); CreatureEffect(item, MPStickBite1, DoBloodSplat); - DoDamage(enemy, 100); - creature->Flags = 2; SoundEffect(70, &item->Pose); + creature->Flags = 2; } } else { - if (creature->Flags != 2 && enemy) + if (creature->Flags != 2 && creature->Enemy) { - if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) && - abs(enemy->Pose.Position.y - item->Pose.Position.y) <= SECTOR(0.25f) && - abs(enemy->Pose.Position.z - item->Pose.Position.z) < SECTOR(0.25f)) + if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.25f)) { - creature->Flags = 2; + DoDamage(creature->Enemy, 6); CreatureEffect(item, MPStickBite1, DoBloodSplat); - DoDamage(enemy, 6); SoundEffect(SFX_TR4_LARA_THUD, &item->Pose); + creature->Flags = 2; } } } @@ -451,32 +439,30 @@ namespace TEN::Entities::TR3 creature->MaxTurn = ANGLE(6.0f); if (AI.ahead) - torsoY = AI.angle; + extraTorsoRot.y = AI.angle; - if (enemy->IsLara()) + if (creature->Enemy->IsLara()) { if (creature->Flags != 1 && item->TestBits(JointBitType::Touch, MPStickKickAttackJoints) && item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 8) { + DoDamage(creature->Enemy, 150); CreatureEffect(item, MPStickBite2, DoBloodSplat); - DoDamage(enemy, 150); SoundEffect(SFX_TR4_LARA_THUD, &item->Pose); creature->Flags = 1; } } else { - if (!creature->Flags != 1 && enemy && + if (!creature->Flags != 1 && creature->Enemy && item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 8) { - if (abs(enemy->Pose.Position.x - item->Pose.Position.x) < SECTOR(0.25f) && - abs(enemy->Pose.Position.y - item->Pose.Position.y) <= SECTOR(0.25f) && - abs(enemy->Pose.Position.z - item->Pose.Position.z) < SECTOR(0.25f)) + if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.25f)) { - creature->Flags = 1; + DoDamage(creature->Enemy, 9); CreatureEffect(item, MPStickBite2, DoBloodSplat); - DoDamage(enemy, 9); SoundEffect(SFX_TR4_LARA_THUD, &item->Pose); + creature->Flags = 1; } } } @@ -486,8 +472,8 @@ namespace TEN::Entities::TR3 } CreatureTilt(item, tilt); - CreatureJoint(item, 0, torsoY); - CreatureJoint(item, 1, torsoX); + CreatureJoint(item, 0, extraTorsoRot.y); + CreatureJoint(item, 1, extraTorsoRot.x); CreatureJoint(item, 2, head); if (item->Animation.ActiveState < MPSTICK_STATE_DEATH) diff --git a/TombEngine/Objects/TR3/Entity/tr3_mp_stick.h b/TombEngine/Objects/TR3/Entity/tr3_mp_stick.h index 75d1addcb..8848a7f72 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_mp_stick.h +++ b/TombEngine/Objects/TR3/Entity/tr3_mp_stick.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void InitialiseMPStick(short itemNumber); void MPStickControl(short itemNumber); diff --git a/TombEngine/Objects/TR3/Entity/tr3_raptor.cpp b/TombEngine/Objects/TR3/Entity/tr3_raptor.cpp index 9f1829f04..4233a7a10 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_raptor.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_raptor.cpp @@ -16,10 +16,17 @@ using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { constexpr auto RAPTOR_ATTACK_DAMAGE = 100; + constexpr auto RAPTOR_BITE_ATTACK_RANGE = SQUARE(585); + constexpr auto RAPTOR_JUMP_ATTACK_RANGE = SQUARE(SECTOR(1.5f)); + constexpr auto RAPTOR_RUN_ATTACK_RANGE = SQUARE(SECTOR(1.5f)); + + constexpr auto RAPTOR_ROAR_CHANCE = 1.0f / 256; + constexpr auto RAPTOR_SWITCH_TARGET_CHANCE = 1.0f / 128; + #define RAPTOR_WALK_TURN_RATE_MAX ANGLE(2.0f) #define RAPTOR_RUN_TURN_RATE_MAX ANGLE(2.0f) #define RAPTOR_ATTACK_TURN_RATE_MAX ANGLE(2.0f) @@ -74,10 +81,10 @@ namespace TEN::Entities::TR3 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short head = 0; - short neck = 0; short angle = 0; short tilt = 0; + short head = 0; + short neck = 0; if (item->HitPoints <= 0) { @@ -86,14 +93,13 @@ namespace TEN::Entities::TR3 } else { - if (creature->Enemy == nullptr || !(GetRandomControl() & 0x7F)) // TODO: Probability is 0.004f or 0.996f? + if (creature->Enemy == nullptr || TestProbability(RAPTOR_SWITCH_TARGET_CHANCE)) { ItemInfo* nearestItem = nullptr; int minDistance = INT_MAX; - for (int i = 0; i < ActiveCreatures.size(); i++) + for (auto* currentCreature : ActiveCreatures) { - auto* currentCreature = ActiveCreatures[i]; if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber) { currentCreature++; @@ -106,7 +112,7 @@ namespace TEN::Entities::TR3 int y = (targetItem->Pose.Position.y - item->Pose.Position.y) / 64; int z = (targetItem->Pose.Position.z - item->Pose.Position.z) / 64; - int distance = pow(x, 2) + pow(y, 2) + pow(z, 2); + int distance = SQUARE(x) + SQUARE(y) + SQUARE(z); if (distance < minDistance && item->HitPoints > 0) { nearestItem = targetItem; @@ -118,7 +124,7 @@ namespace TEN::Entities::TR3 if (nearestItem != nullptr && (nearestItem->ObjectNumber != ID_RAPTOR || - (TestProbability(0.03f) && minDistance < pow(SECTOR(2), 2)))) + (TestProbability(1.0f / 30) && minDistance < SQUARE(SECTOR(2))))) { creature->Enemy = nearestItem; } @@ -127,7 +133,7 @@ namespace TEN::Entities::TR3 int y = (LaraItem->Pose.Position.y - item->Pose.Position.y) / 64; int z = (LaraItem->Pose.Position.z - item->Pose.Position.z) / 64; - int distance = pow(x, 2) + pow(y, 2) + pow(z, 2); + int distance = SQUARE(x) + SQUARE(y) + SQUARE(z); if (distance <= minDistance) creature->Enemy = LaraItem; } @@ -164,11 +170,11 @@ namespace TEN::Entities::TR3 item->Animation.TargetState = RAPTOR_STATE_ROAR; } else if (item->TestBits(JointBitType::Touch, RaptorAttackJoints) || - (AI.distance < pow(585, 2) && AI.bite)) + (AI.distance < RAPTOR_BITE_ATTACK_RANGE && AI.bite)) { item->Animation.TargetState = RAPTOR_STATE_BITE_ATTACK; } - else if (AI.bite && AI.distance < pow(SECTOR(1.5f), 2)) + else if (AI.bite && AI.distance < RAPTOR_JUMP_ATTACK_RANGE) item->Animation.TargetState = RAPTOR_STATE_JUMP_ATTACK; else if (creature->Mood == MoodType::Escape && Lara.TargetEntity != item && AI.ahead && !item->HitStatus) @@ -188,7 +194,7 @@ namespace TEN::Entities::TR3 if (creature->Mood != MoodType::Bored) item->Animation.TargetState = RAPTOR_STATE_IDLE; - else if (AI.ahead && TestProbability(0.004f)) + else if (AI.ahead && TestProbability(RAPTOR_ROAR_CHANCE)) { item->Animation.TargetState = RAPTOR_STATE_IDLE; item->Animation.RequiredState = RAPTOR_STATE_ROAR; @@ -210,7 +216,7 @@ namespace TEN::Entities::TR3 item->Animation.RequiredState = RAPTOR_STATE_ROAR; creature->Flags &= ~2; } - else if (AI.bite && AI.distance < pow(SECTOR(1.5f), 2)) + else if (AI.bite && AI.distance < RAPTOR_RUN_ATTACK_RANGE) { if (item->Animation.TargetState == RAPTOR_STATE_RUN_FORWARD) { @@ -221,7 +227,7 @@ namespace TEN::Entities::TR3 } } else if (AI.ahead && creature->Mood != MoodType::Escape && - TestProbability(0.004f)) + TestProbability(RAPTOR_ROAR_CHANCE)) { item->Animation.TargetState = RAPTOR_STATE_IDLE; item->Animation.RequiredState = RAPTOR_STATE_ROAR; @@ -257,10 +263,7 @@ namespace TEN::Entities::TR3 { if (!(creature->Flags & 1) && creature->Enemy != nullptr) { - auto direction = creature->Enemy->Pose.Position - item->Pose.Position; - if (abs(direction.x) < SECTOR(0.5f) && - abs(direction.y) < SECTOR(0.5f) && - abs(direction.z) < SECTOR(0.5f)) + if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f)) { if (creature->Enemy->HitPoints <= 0) creature->Flags |= 2; @@ -297,10 +300,7 @@ namespace TEN::Entities::TR3 { if (!(creature->Flags & 1) && creature->Enemy != nullptr) { - auto direction = creature->Enemy->Pose.Position - item->Pose.Position; - if (abs(direction.x) < SECTOR(0.5f) && - abs(direction.y) < SECTOR(0.5f) && - abs(direction.z) < SECTOR(0.5f)) + if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f)) { if (creature->Enemy->HitPoints <= 0) creature->Flags |= 2; @@ -336,10 +336,7 @@ namespace TEN::Entities::TR3 { if (!(creature->Flags & 1) && creature->Enemy != nullptr) { - auto direction = creature->Enemy->Pose.Position - item->Pose.Position; - if (abs(direction.x) < SECTOR(0.5f) && - abs(direction.y) < SECTOR(0.5f) && - abs(direction.z) < SECTOR(0.5f)) + if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f)) { if (creature->Enemy->HitPoints <= 0) creature->Flags |= 2; diff --git a/TombEngine/Objects/TR3/Entity/tr3_raptor.h b/TombEngine/Objects/TR3/Entity/tr3_raptor.h index a9424ca89..640e202ec 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_raptor.h +++ b/TombEngine/Objects/TR3/Entity/tr3_raptor.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void RaptorControl(short itemNumber); } diff --git a/TombEngine/Objects/TR3/Entity/tr3_scuba_diver.cpp b/TombEngine/Objects/TR3/Entity/tr3_scuba_diver.cpp index f432647a3..c4c7c77d2 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_scuba_diver.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_scuba_diver.cpp @@ -13,7 +13,7 @@ #include "Specific/level.h" #include "Specific/setup.h" -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { constexpr auto SCUBA_DIVER_ATTACK_DAMAGE = 50; @@ -23,7 +23,6 @@ namespace TEN::Entities::TR3 enum ScubaDiverState { - SDIVER_STATE_NONE = 0, SDIVER_STATE_SWIM = 1, SDIVER_STATE_TREAD_WATER_IDLE = 2, SDIVER_STATE_SWIM_SHOOT = 3, @@ -60,23 +59,23 @@ namespace TEN::Entities::TR3 static void ShootHarpoon(ItemInfo* item, Vector3Int pos, short velocity, short yRot, short roomNumber) { short harpoonItemNumber = CreateItem(); - if (harpoonItemNumber != NO_ITEM) - { - auto* harpoonItem = &g_Level.Items[harpoonItemNumber]; + if (harpoonItemNumber == NO_ITEM) + return; - harpoonItem->ObjectNumber = ID_SCUBA_HARPOON; - harpoonItem->RoomNumber = item->RoomNumber; - harpoonItem->Pose.Position = pos; + auto* harpoonItem = &g_Level.Items[harpoonItemNumber]; - InitialiseItem(harpoonItemNumber); + harpoonItem->ObjectNumber = ID_SCUBA_HARPOON; + harpoonItem->RoomNumber = item->RoomNumber; + harpoonItem->Pose.Position = pos; - harpoonItem->Animation.Velocity.z = 150; - harpoonItem->Pose.Orientation.x = 0; - harpoonItem->Pose.Orientation.y = yRot; + InitialiseItem(harpoonItemNumber); - AddActiveItem(harpoonItemNumber); - harpoonItem->Status = ITEM_ACTIVE; - } + harpoonItem->Animation.Velocity.z = 150.0f; + harpoonItem->Pose.Orientation.x = 0; + harpoonItem->Pose.Orientation.y = yRot; + + AddActiveItem(harpoonItemNumber); + harpoonItem->Status = ITEM_ACTIVE; } void ScubaHarpoonControl(short itemNumber) @@ -91,14 +90,7 @@ namespace TEN::Entities::TR3 } else { - int ox = item->Pose.Position.x; - int oz = item->Pose.Position.z; - - int velocity = item->Animation.Velocity.z * phd_cos(item->Pose.Orientation.x); - - item->Pose.Position.z += velocity * phd_cos(item->Pose.Orientation.y); - item->Pose.Position.x += velocity * phd_sin(item->Pose.Orientation.y); - item->Pose.Position.y += -item->Animation.Velocity.z * phd_sin(item->Pose.Orientation.x); + TranslateItem(item, item->Pose.Orientation, item->Animation.Velocity.z); auto probe = GetCollision(item); @@ -123,7 +115,8 @@ namespace TEN::Entities::TR3 short head = 0; short neck = 0; - int waterHeight; + int waterHeight = 0; + if (item->HitPoints <= 0) { if (item->Animation.ActiveState != SDIVER_STATE_DEATH) @@ -140,22 +133,23 @@ namespace TEN::Entities::TR3 GetCreatureMood(item, &AI, false); CreatureMood(item, &AI, false); - GameVector origin; - GameVector target; bool shoot = false; - if (Lara.Control.WaterStatus == WaterStatus::Dry) { - origin.x = item->Pose.Position.x; - origin.y = item->Pose.Position.y - CLICK(1); - origin.z = item->Pose.Position.z; - origin.roomNumber = item->RoomNumber; - - target.x = LaraItem->Pose.Position.x; - target.y = LaraItem->Pose.Position.y - (LARA_HEIGHT - 150); - target.z = LaraItem->Pose.Position.z; + auto origin = GameVector( + item->Pose.Position.x, + item->Pose.Position.y - CLICK(1), + item->Pose.Position.z, + item->RoomNumber + ); + auto target = GameVector( + LaraItem->Pose.Position.x, + LaraItem->Pose.Position.y - (LARA_HEIGHT - 150), + LaraItem->Pose.Position.z + ); shoot = LOS(&origin, &target); + if (shoot) creature->Target = LaraItem->Pose.Position; @@ -164,19 +158,11 @@ namespace TEN::Entities::TR3 } else if (AI.angle > -ANGLE(45.0f) && AI.angle < ANGLE(45.0f)) { - origin.x = item->Pose.Position.x; - origin.y = item->Pose.Position.y; - origin.z = item->Pose.Position.z; - origin.roomNumber = item->RoomNumber; - - target.x = LaraItem->Pose.Position.x; - target.y = LaraItem->Pose.Position.y; - target.z = LaraItem->Pose.Position.z; - + auto origin = GameVector(item->Pose.Position, item->RoomNumber); + auto target = GameVector(LaraItem->Pose.Position); + shoot = LOS(&origin, &target); } - else - shoot = false; angle = CreatureTurn(item, creature->MaxTurn); waterHeight = GetWaterSurface(item->Pose.Position.x, item->Pose.Position.y, item->Pose.Position.z, item->RoomNumber) + SECTOR(0.5f); @@ -202,7 +188,7 @@ namespace TEN::Entities::TR3 break; case SDIVER_STATE_SWIM_AIM: - creature->Flags = NULL; + creature->Flags = 0; if (shoot) neck = -AI.angle; @@ -270,7 +256,6 @@ namespace TEN::Entities::TR3 } break; - } } diff --git a/TombEngine/Objects/TR3/Entity/tr3_scuba_diver.h b/TombEngine/Objects/TR3/Entity/tr3_scuba_diver.h index d5bc5b60d..23f9b6167 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_scuba_diver.h +++ b/TombEngine/Objects/TR3/Entity/tr3_scuba_diver.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void ScubaHarpoonControl(short itemNumber); void ScubaControl(short itemNumber); diff --git a/TombEngine/Objects/TR3/Entity/tr3_shiva.cpp b/TombEngine/Objects/TR3/Entity/tr3_shiva.cpp index 2d618700a..0db04d15f 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_shiva.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_shiva.cpp @@ -19,7 +19,7 @@ using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { constexpr auto SHIVA_GRAB_ATTACK_DAMAGE = 150; constexpr auto SHIVA_DOWNWARD_ATTACK_DAMAGE = 180; @@ -29,10 +29,10 @@ namespace TEN::Entities::TR3 #define SHIVA_WALK_TURN_RATE_MAX ANGLE(4.0f) #define SHIVA_ATTACK_TURN_RATE_MAX ANGLE(4.0f) - const vector ShivaAttackLeftJoints = { 10, 13 }; - const vector ShivaAttackRightJoints = { 22, 25 }; const auto ShivaBiteLeft = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 13); const auto ShivaBiteRight = BiteInfo(Vector3(0.0f, 0.0f, 920.0f), 22); + const vector ShivaAttackLeftJoints = { 10, 13 }; + const vector ShivaAttackRightJoints = { 22, 25 }; enum ShivaState { @@ -81,7 +81,7 @@ namespace TEN::Entities::TR3 }; - static void TriggerShivaSmoke(long x, long y, long z, long uw) + void TriggerShivaSmoke(long x, long y, long z, long uw) { long dx = LaraItem->Pose.Position.x - x; long dz = LaraItem->Pose.Position.z - z; @@ -169,7 +169,7 @@ namespace TEN::Entities::TR3 sptr->dSize = size; } - static void ShivaDamage(ItemInfo* item, CreatureInfo* creature, int damage) + void ShivaDamage(ItemInfo* item, CreatureInfo* creature, int damage) { if (!(creature->Flags) && item->TestBits(JointBitType::Touch, ShivaAttackRightJoints)) { @@ -208,15 +208,15 @@ namespace TEN::Entities::TR3 return; auto* item = &g_Level.Items[itemNumber]; - auto* shiva = GetCreatureInfo(item); + auto* creature = GetCreatureInfo(item); - auto pos = Vector3Int(0, 0, 256); - bool laraAlive = LaraItem->HitPoints > 0; + auto pos = Vector3Int(0, 0, CLICK(1)); + bool isLaraAlive = LaraItem->HitPoints > 0; - Vector3Shrt extraHeadRot; - Vector3Shrt extraTorsoRot; short angle = 0; short tilt = 0; + Vector3Shrt extraHeadRot = Vector3Shrt::Zero; + Vector3Shrt extraTorsoRot = Vector3Shrt::Zero; if (item->HitPoints <= 0) { @@ -235,13 +235,13 @@ namespace TEN::Entities::TR3 GetCreatureMood(item, &AI, true); CreatureMood(item, &AI, true); - if (shiva->Mood == MoodType::Escape) + if (creature->Mood == MoodType::Escape) { - shiva->Target.x = LaraItem->Pose.Position.x; - shiva->Target.z = LaraItem->Pose.Position.z; + creature->Target.x = LaraItem->Pose.Position.x; + creature->Target.z = LaraItem->Pose.Position.z; } - angle = CreatureTurn(item, shiva->MaxTurn); + angle = CreatureTurn(item, creature->MaxTurn); if (item->Animation.ActiveState != SHIVA_STATE_INACTIVE) item->MeshBits = ALL_JOINT_BITS; @@ -251,15 +251,15 @@ namespace TEN::Entities::TR3 switch (item->Animation.ActiveState) { case SHIVA_STATE_INACTIVE: - shiva->MaxTurn = 0; + creature->MaxTurn = 0; - if (!shiva->Flags) + if (!creature->Flags) { if (!item->MeshBits) effectMesh = 0; item->MeshBits = (item->MeshBits * 2) + 1; - shiva->Flags = 1; + creature->Flags = 1; GetJointAbsPosition(item, &pos, effectMesh++); TriggerExplosionSparks(pos.x, pos.y, pos.z, 2, 0, 0, item->RoomNumber); @@ -267,45 +267,45 @@ namespace TEN::Entities::TR3 } else - shiva->Flags--; + creature->Flags--; if (item->MeshBits == 0x7FFFFFFF) { item->Animation.TargetState = SHIVA_STATE_IDLE; - shiva->Flags = -45; + creature->Flags = -45; effectMesh = 0; } break; case SHIVA_STATE_IDLE: - shiva->MaxTurn = 0; + creature->MaxTurn = 0; if (AI.ahead) extraHeadRot.y = AI.angle; - if (shiva->Flags < 0) + if (creature->Flags < 0) { - shiva->Flags++; + creature->Flags++; TriggerShivaSmoke(item->Pose.Position.x + (GetRandomControl() & 0x5FF) - 0x300, pos.y - (GetRandomControl() & 0x5FF), item->Pose.Position.z + (GetRandomControl() & 0x5FF) - 0x300, 1); break; } - if (shiva->Flags == 1) - shiva->Flags = 0; + if (creature->Flags == 1) + creature->Flags = 0; - if (shiva->Mood == MoodType::Escape) + if (creature->Mood == MoodType::Escape) { int x = item->Pose.Position.x + SECTOR(1) * phd_sin(item->Pose.Orientation.y + ANGLE(180.0f)); int z = item->Pose.Position.z + SECTOR(1) * phd_cos(item->Pose.Orientation.y + ANGLE(180.0f)); auto box = GetCollision(x, item->Pose.Position.y, z, item->RoomNumber).BottomBlock->Box; - if (box != NO_BOX && !(g_Level.Boxes[box].flags & BLOCKABLE) && !shiva->Flags) + if (box != NO_BOX && !(g_Level.Boxes[box].flags & BLOCKABLE) && !creature->Flags) item->Animation.TargetState = SHIVA_STATE_WALK_BACK; else item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE; } - else if (shiva->Mood == MoodType::Bored) + else if (creature->Mood == MoodType::Bored) { if (TestProbability(0.0325f)) item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD; @@ -313,17 +313,17 @@ namespace TEN::Entities::TR3 else if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2)) { item->Animation.TargetState = SHIVA_STATE_GRAB_ATTACK; - shiva->Flags = 0; + creature->Flags = 0; } else if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2)) { item->Animation.TargetState = SHIVA_STATE_DOWNWARD_ATTACK; - shiva->Flags = 0; + creature->Flags = 0; } else if (item->HitStatus && AI.ahead) { item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE; - shiva->Flags = 4; + creature->Flags = 4; } else item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD; @@ -331,103 +331,103 @@ namespace TEN::Entities::TR3 break; case SHIVA_STATE_GUARD_IDLE: - shiva->MaxTurn = 0; + creature->MaxTurn = 0; if (AI.ahead) extraHeadRot.y = AI.angle; - if (item->HitStatus || shiva->Mood == MoodType::Escape) - shiva->Flags = 4; + if (item->HitStatus || creature->Mood == MoodType::Escape) + creature->Flags = 4; if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2) || (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase && - !shiva->Flags) || + !creature->Flags) || !AI.ahead) { item->Animation.TargetState = SHIVA_STATE_IDLE; - shiva->Flags = 0; + creature->Flags = 0; } - else if (shiva->Flags) + else if (creature->Flags) item->Animation.TargetState = SHIVA_STATE_GUARD_IDLE; if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase && - shiva->Flags > 1) + creature->Flags > 1) { - shiva->Flags -= 2; + creature->Flags -= 2; } break; case SHIVA_STATE_WALK_FORWARD: - shiva->MaxTurn = SHIVA_WALK_TURN_RATE_MAX; + creature->MaxTurn = SHIVA_WALK_TURN_RATE_MAX; if (AI.ahead) extraHeadRot.y = AI.angle; - if (shiva->Mood == MoodType::Escape) + if (creature->Mood == MoodType::Escape) item->Animation.TargetState = SHIVA_STATE_IDLE; - else if (shiva->Mood == MoodType::Bored) + else if (creature->Mood == MoodType::Bored) item->Animation.TargetState = SHIVA_STATE_IDLE; else if (AI.bite && AI.distance < pow(SECTOR(4) / 3, 2)) { item->Animation.TargetState = SHIVA_STATE_IDLE; - shiva->Flags = 0; + creature->Flags = 0; } else if (item->HitStatus) { item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD_GUARDING; - shiva->Flags = 4; + creature->Flags = 4; } break; case SHIVA_STATE_WALK_FORWARD_GUARDING: - shiva->MaxTurn = SHIVA_WALK_TURN_RATE_MAX; + creature->MaxTurn = SHIVA_WALK_TURN_RATE_MAX; if (AI.ahead) extraHeadRot.y = AI.angle; if (item->HitStatus) - shiva->Flags = 4; + creature->Flags = 4; if (AI.bite && AI.distance < pow(SECTOR(1.25f), 2) || (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase && - !shiva->Flags)) + !creature->Flags)) { item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD; - shiva->Flags = 0; + creature->Flags = 0; } - else if (shiva->Flags) + else if (creature->Flags) item->Animation.TargetState = SHIVA_STATE_WALK_FORWARD_GUARDING; if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase) - shiva->Flags = 0; + creature->Flags = 0; break; case SHIVA_STATE_WALK_BACK: - shiva->MaxTurn = SHIVA_WALK_TURN_RATE_MAX; + creature->MaxTurn = SHIVA_WALK_TURN_RATE_MAX; if (AI.ahead) extraHeadRot.y = AI.angle; if (AI.ahead && AI.distance < pow(SECTOR(4) / 3, 2) || (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase && - !shiva->Flags)) + !creature->Flags)) { item->Animation.TargetState = SHIVA_STATE_IDLE; } else if (item->HitStatus) { item->Animation.TargetState = SHIVA_STATE_IDLE; - shiva->Flags = 4; + creature->Flags = 4; } break; case SHIVA_STATE_GRAB_ATTACK: - shiva->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX; + creature->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX; if (AI.ahead) { @@ -435,24 +435,24 @@ namespace TEN::Entities::TR3 extraTorsoRot = Vector3Shrt(AI.xAngle, AI.angle, 0); } - ShivaDamage(item, shiva, SHIVA_GRAB_ATTACK_DAMAGE); + ShivaDamage(item, creature, SHIVA_GRAB_ATTACK_DAMAGE); break; case SHIVA_STATE_DOWNWARD_ATTACK: - shiva->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX; + creature->MaxTurn = SHIVA_ATTACK_TURN_RATE_MAX; extraHeadRot.y = AI.angle; extraTorsoRot.y = AI.angle; if (AI.xAngle > 0) extraTorsoRot.x = AI.xAngle; - ShivaDamage(item, shiva, SHIVA_DOWNWARD_ATTACK_DAMAGE); + ShivaDamage(item, creature, SHIVA_DOWNWARD_ATTACK_DAMAGE); break; case SHIVA_STATE_KILL: - shiva->MaxTurn = 0; - extraHeadRot = Vector3Shrt(); - extraTorsoRot = Vector3Shrt(); + creature->MaxTurn = 0; + extraHeadRot = Vector3Shrt::Zero; + extraTorsoRot = Vector3Shrt::Zero; if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + SHIVA_ANIM_WALK_FORWARD_TO_GUARDED_LEFT_1 || item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase + SHIVA_ANIM_WALK_BACK_RIGHT || @@ -466,23 +466,21 @@ namespace TEN::Entities::TR3 } } - // Dispatch kill animation - if (laraAlive && LaraItem->HitPoints <= 0) + // Dispatch kill animation. + if (isLaraAlive && LaraItem->HitPoints <= 0) { item->Animation.TargetState = SHIVA_STATE_KILL; if (LaraItem->RoomNumber != item->RoomNumber) ItemNewRoom(Lara.ItemNumber, item->RoomNumber); - LaraItem->Pose.Position = item->Pose.Position; - LaraItem->Pose.Orientation = Vector3Shrt(0, item->Pose.Orientation.y, 0); - LaraItem->Animation.IsAirborne = false; - LaraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_ANIM_SHIVA_DEATH; LaraItem->Animation.FrameNumber = g_Level.Anims[LaraItem->Animation.AnimNumber].frameBase; LaraItem->Animation.ActiveState = LS_DEATH; LaraItem->Animation.TargetState = LS_DEATH; - + LaraItem->Animation.IsAirborne = false; + LaraItem->Pose.Position = item->Pose.Position; + LaraItem->Pose.Orientation = Vector3Shrt(0, item->Pose.Orientation.y, 0); LaraItem->HitPoints = NOT_TARGETABLE; Lara.Air = -1; Lara.Control.HandStatus = HandStatus::Special; diff --git a/TombEngine/Objects/TR3/Entity/tr3_shiva.h b/TombEngine/Objects/TR3/Entity/tr3_shiva.h index 2aa7cac52..4f208c955 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_shiva.h +++ b/TombEngine/Objects/TR3/Entity/tr3_shiva.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void InitialiseShiva(short itemNumber); void ShivaControl(short itemNumber); diff --git a/TombEngine/Objects/TR3/Entity/tr3_sophia.cpp b/TombEngine/Objects/TR3/Entity/tr3_sophia.cpp index f28c82d3e..123e255da 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_sophia.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_sophia.cpp @@ -9,7 +9,7 @@ #include "Sound/sound.h" #include "Specific/level.h" -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { static BOSS_STRUCT BossData; diff --git a/TombEngine/Objects/TR3/Entity/tr3_sophia.h b/TombEngine/Objects/TR3/Entity/tr3_sophia.h index 52778680c..5a96a3725 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_sophia.h +++ b/TombEngine/Objects/TR3/Entity/tr3_sophia.h @@ -1,7 +1,7 @@ #pragma once #include "Game/items.h" -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void ControlLaserBolts(short itemNumber); void ControlLondBossPlasmaBall(short fxNumber); diff --git a/TombEngine/Objects/TR3/Entity/tr3_tiger.cpp b/TombEngine/Objects/TR3/Entity/tr3_tiger.cpp index 5a142f497..936d50807 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_tiger.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_tiger.cpp @@ -15,23 +15,52 @@ using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { constexpr auto TIGER_ATTACK_DAMAGE = 90; + constexpr auto TIGER_BITE_ATTACK_RANGE = SQUARE(SECTOR(0.33f)); + constexpr auto TIGER_POUNCE_ATTACK_RANGE = SQUARE(SECTOR(1)); + constexpr auto TIGER_RUN_ATTACK_RANGE = SQUARE(SECTOR(1.5f)); + + constexpr auto TIGER_WALK_CHANCE = 0.035f; + constexpr auto TIGER_ROAR_CHANCE = 1.0f / 340; + const auto TigerBite = BiteInfo(Vector3(19.0f, -13.0f, 3.0f), 26); const vector TigerAttackJoints = { 14, 15, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26 }; - // TODO + #define TIGER_WALK_TURN_RATE_MAX ANGLE(3.0f) + #define TIGER_RUN_TURN_RATE_MAX ANGLE(6.0f) + #define TIGER_POUNCE_ATTACK_TURN_RATE_MAX ANGLE(3.0f) + enum TigerState { - + TIGER_STATE_DEATH = 0, + TIGER_STATE_IDLE = 1, + TIGER_STATE_WALK_FORWARD = 2, + TIGER_STATE_RUN_FORWARD = 3, + // No state 4. + TIGER_STATE_ROAR = 5, + TIGER_STATE_BITE_ATTACK = 6, + TIGER_STATE_RUN_SWIPE_ATTACK = 7, + TIGER_STATE_POUNCE_ATTACK = 8 }; - // TODO enum TigerAnim { - + TIGER_ANIM_IDLE_TO_RUN_FORWARD = 0, + TIGER_ANIM_BITE_ATTACK = 1, + TIGER_ANIM_RUN_SWIPE_ATTACK = 2, + TIGER_ANIM_POUNCE_ATTACK_START = 3, + TIGER_ANIM_ROAR = 4, + TIGER_ANIM_RUN_FORWARD = 5, + TIGER_ANIM_RUN_FORWARD_TO_IDLE = 6, + TIGER_ANIM_IDLE = 7, + TIGER_ANIM_WALK_FORWARD = 8, + TIGER_ANIM_IDLE_TO_WALK_FORWARD = 9, + TIGER_ANIM_WALK_FORWARD_TO_IDLE = 10, + TIGER_ANIM_DEATH = 11, + TIGER_ANIM_POUNCE_ATTACK_END = 12 }; void TigerControl(short itemNumber) @@ -40,20 +69,16 @@ namespace TEN::Entities::TR3 return; auto* item = &g_Level.Items[itemNumber]; - auto* info = GetCreatureInfo(item); + auto* creature = GetCreatureInfo(item); - short head = 0; short angle = 0; short tilt = 0; + auto extraHeadRot = Vector3Shrt::Zero; if (item->HitPoints <= 0) { - if (item->Animation.ActiveState != 9) - { - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 11; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 9; - } + if (item->Animation.ActiveState != TIGER_STATE_DEATH) + SetAnimation(item, TIGER_ANIM_DEATH); } else { @@ -61,99 +86,105 @@ namespace TEN::Entities::TR3 CreatureAIInfo(item, &AI); if (AI.ahead) - head = AI.angle; + extraHeadRot.y = AI.angle; - GetCreatureMood(item, &AI, 1); + GetCreatureMood(item, &AI, true); - if (info->Alerted && AI.zoneNumber != AI.enemyZone) - info->Mood = MoodType::Escape; + if (creature->Alerted && AI.zoneNumber != AI.enemyZone) + creature->Mood = MoodType::Escape; - CreatureMood(item, &AI, 1); + CreatureMood(item, &AI, true); - angle = CreatureTurn(item, info->MaxTurn); + angle = CreatureTurn(item, creature->MaxTurn); switch (item->Animation.ActiveState) { - case 1: - info->MaxTurn = 0; - info->Flags = 0; + case TIGER_STATE_IDLE: + creature->MaxTurn = 0; + creature->Flags = 0; - if (info->Mood == MoodType::Escape) + if (creature->Mood == MoodType::Escape) { if (Lara.TargetEntity != item && AI.ahead) - item->Animation.TargetState = 1; + item->Animation.TargetState = TIGER_STATE_IDLE; else - item->Animation.TargetState = 3; + item->Animation.TargetState = TIGER_STATE_RUN_FORWARD; } - else if (info->Mood == MoodType::Bored) + else if (creature->Mood == MoodType::Bored) { - if (TestProbability(0.003f)) - item->Animation.TargetState = 5; - else if (TestProbability(0.035f)) - item->Animation.TargetState = 2; + if (TestProbability(TIGER_ROAR_CHANCE)) + item->Animation.TargetState = TIGER_STATE_ROAR; + else if (TestProbability(TIGER_WALK_CHANCE)) + item->Animation.TargetState = TIGER_STATE_WALK_FORWARD; } - else if (AI.bite && AI.distance < pow(340, 2)) - item->Animation.TargetState = 6; - else if (AI.bite && AI.distance < pow(SECTOR(1), 2)) + else if (AI.bite && AI.distance < TIGER_BITE_ATTACK_RANGE) + item->Animation.TargetState = TIGER_STATE_BITE_ATTACK; + else if (AI.bite && AI.distance < TIGER_POUNCE_ATTACK_RANGE) { - info->MaxTurn = ANGLE(3.0f); - item->Animation.TargetState = 8; + item->Animation.TargetState = TIGER_STATE_POUNCE_ATTACK; + creature->MaxTurn = TIGER_POUNCE_ATTACK_TURN_RATE_MAX; } else if (item->Animation.RequiredState) item->Animation.TargetState = item->Animation.RequiredState; - else if (info->Mood != MoodType::Attack && TestProbability(0.003f)) - item->Animation.TargetState = 5; + else if (creature->Mood != MoodType::Attack && TestProbability(TIGER_ROAR_CHANCE)) + item->Animation.TargetState = TIGER_STATE_ROAR; else - item->Animation.TargetState = 3; + item->Animation.TargetState = TIGER_STATE_RUN_FORWARD; break; - case 2: - info->MaxTurn = ANGLE(3.0f); + case TIGER_STATE_WALK_FORWARD: + creature->MaxTurn = TIGER_WALK_TURN_RATE_MAX; - if (info->Mood == MoodType::Escape || info->Mood == MoodType::Attack) - item->Animation.TargetState = 3; - else if (TestProbability(0.003f)) + if (creature->Mood == MoodType::Escape || + creature->Mood == MoodType::Attack) { - item->Animation.TargetState = 1; - item->Animation.RequiredState = 5; + item->Animation.TargetState = TIGER_STATE_RUN_FORWARD; + } + else if (TestProbability(TIGER_ROAR_CHANCE)) + { + item->Animation.TargetState = TIGER_STATE_IDLE; + item->Animation.RequiredState = TIGER_STATE_ROAR; } break; - case 3: - info->MaxTurn = ANGLE(6.0f); + case TIGER_STATE_RUN_FORWARD: + creature->MaxTurn = TIGER_RUN_TURN_RATE_MAX; - if (info->Mood == MoodType::Bored) - item->Animation.TargetState = 1; - else if (info->Flags && AI.ahead) - item->Animation.TargetState = 1; - else if (AI.bite && AI.distance < pow(SECTOR(1.5f), 2)) + if (creature->Mood == MoodType::Bored) + item->Animation.TargetState = TIGER_STATE_IDLE; + else if (creature->Flags && AI.ahead) + item->Animation.TargetState = TIGER_STATE_IDLE; + else if (AI.bite && AI.distance < TIGER_RUN_ATTACK_RANGE) { - if (LaraItem->Animation.Velocity.z == 0) - item->Animation.TargetState = 1; + if (LaraItem->Animation.Velocity.z == 0.0f) + item->Animation.TargetState = TIGER_STATE_IDLE; else - item->Animation.TargetState = 7; + item->Animation.TargetState = TIGER_STATE_RUN_SWIPE_ATTACK; } - else if (info->Mood != MoodType::Attack && TestProbability(0.003f)) + else if (creature->Mood != MoodType::Attack && TestProbability(TIGER_ROAR_CHANCE)) { - item->Animation.TargetState = 1; - item->Animation.RequiredState = 5; + item->Animation.TargetState = TIGER_STATE_IDLE; + item->Animation.RequiredState = TIGER_STATE_ROAR; + } + else if (creature->Mood == MoodType::Escape && + Lara.TargetEntity != item && AI.ahead) + { + item->Animation.TargetState = TIGER_STATE_IDLE; } - else if (info->Mood == MoodType::Escape && Lara.TargetEntity != item && AI.ahead) - item->Animation.TargetState = 1; - info->Flags = 0; + creature->Flags = 0; break; - case 6: - case 7: - case 8: - if (!info->Flags && item->TestBits(JointBitType::Touch, TigerAttackJoints)) + case TIGER_STATE_BITE_ATTACK: + case TIGER_STATE_RUN_SWIPE_ATTACK: + case TIGER_STATE_POUNCE_ATTACK: + if (!creature->Flags && item->TestBits(JointBitType::Touch, TigerAttackJoints)) { - DoDamage(info->Enemy, TIGER_ATTACK_DAMAGE); + DoDamage(creature->Enemy, TIGER_ATTACK_DAMAGE); CreatureEffect(item, TigerBite, DoBloodSplat); - info->Flags = 1; + creature->Flags = 1; // 1 = is attacking. } break; @@ -161,7 +192,7 @@ namespace TEN::Entities::TR3 } CreatureTilt(item, tilt); - CreatureJoint(item, 0, head); + CreatureJoint(item, 0, extraHeadRot.y); CreatureAnimation(itemNumber, angle, tilt); } } diff --git a/TombEngine/Objects/TR3/Entity/tr3_tiger.h b/TombEngine/Objects/TR3/Entity/tr3_tiger.h index d1966b8fb..4f002d68a 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_tiger.h +++ b/TombEngine/Objects/TR3/Entity/tr3_tiger.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void TigerControl(short itemNumber); } diff --git a/TombEngine/Objects/TR3/Entity/tr3_tony.cpp b/TombEngine/Objects/TR3/Entity/tr3_tony.cpp index 762c1685d..e79d620fd 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_tony.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_tony.cpp @@ -20,7 +20,7 @@ using namespace TEN::Effects::Lara; -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { static BOSS_STRUCT BossData; @@ -467,7 +467,7 @@ namespace TEN::Entities::TR3 if (!Lara.Burn) { - if (ItemNearLara(&fx->pos, 200)) + if (ItemNearLara(&fx->pos.Position, 200)) { LaraItem->HitStatus = true; KillEffect(fxNumber); diff --git a/TombEngine/Objects/TR3/Entity/tr3_tony.h b/TombEngine/Objects/TR3/Entity/tr3_tony.h index 398cb37e5..2606a52a2 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_tony.h +++ b/TombEngine/Objects/TR3/Entity/tr3_tony.h @@ -1,7 +1,7 @@ #pragma once #include "Game/items.h" -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void InitialiseTony(short itemNumber); void TonyControl(short itemNumber); diff --git a/TombEngine/Objects/TR3/Entity/tr3_trex.cpp b/TombEngine/Objects/TR3/Entity/tr3_trex.cpp index d68260be4..867ecd344 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_trex.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_trex.cpp @@ -15,15 +15,20 @@ using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { + constexpr auto TREX_CONTACT_DAMAGE = 1; + constexpr auto TREX_RUN_CONTACT_DAMAGE = 10; + constexpr auto TREX_ROAR_CHANCE = 0.015f; constexpr auto LARA_ANIM_TREX_DEATH_ANIM = 4; const vector TRexAttackJoints = { 12, 13 }; + #define TREX_WALK_TURN_RATE_MAX ANGLE(2.0f) + #define TREX_RUN_TURN_RATE_MAX ANGLE(4.0f) + enum TRexState { - TREX_STATE_NONE = 0, TREX_STATE_IDLE = 1, TREX_STATE_WALK_FORWARD = 2, TREX_STATE_RUN_FORWARD = 3, @@ -57,8 +62,9 @@ namespace TEN::Entities::TR3 if (laraItem->RoomNumber != tRexItem->RoomNumber) ItemNewRoom(Lara.ItemNumber, tRexItem->RoomNumber); - laraItem->Pose.Position = tRexItem->Pose.Position; - laraItem->Pose.Orientation = Vector3Shrt(0, tRexItem->Pose.Orientation.y, 0); + laraItem->Pose = PHD_3DPOS( + tRexItem->Pose.Position, + Vector3Shrt(0, tRexItem->Pose.Orientation.y, 0)); laraItem->Animation.IsAirborne = false; laraItem->Animation.AnimNumber = Objects[ID_LARA_EXTRA_ANIMS].animIndex + LARA_ANIM_TREX_DEATH_ANIM; @@ -82,10 +88,10 @@ namespace TEN::Entities::TR3 return; auto* item = &g_Level.Items[itemNumber]; - auto* info = GetCreatureInfo(item); + auto* creature = GetCreatureInfo(item); - short head = 0; short angle = 0; + short head = 0; if (item->HitPoints <= 0) { @@ -105,18 +111,18 @@ namespace TEN::Entities::TR3 GetCreatureMood(item, &AI, true); CreatureMood(item, &AI, true); - angle = CreatureTurn(item, info->MaxTurn); + angle = CreatureTurn(item, creature->MaxTurn); if (item->TouchBits) - DoDamage(LaraItem, (item->Animation.ActiveState == TREX_STATE_RUN_FORWARD) ? 10 : 1); + DoDamage(LaraItem, (item->Animation.ActiveState == TREX_STATE_RUN_FORWARD) ? TREX_RUN_CONTACT_DAMAGE : TREX_CONTACT_DAMAGE); - info->Flags = (info->Mood != MoodType::Escape && !AI.ahead && AI.enemyFacing > -FRONT_ARC && AI.enemyFacing < FRONT_ARC); + creature->Flags = (creature->Mood != MoodType::Escape && !AI.ahead && AI.enemyFacing > -FRONT_ARC && AI.enemyFacing < FRONT_ARC); if (AI.distance > pow(1500, 2) && AI.distance < pow(SECTOR(4), 2) && - AI.bite && !info->Flags) + AI.bite && !creature->Flags) { - info->Flags = 1; + creature->Flags = 1; } switch (item->Animation.ActiveState) @@ -126,7 +132,7 @@ namespace TEN::Entities::TR3 item->Animation.TargetState = item->Animation.RequiredState; else if (AI.distance < pow(1500, 2) && AI.bite) item->Animation.TargetState = TREX_STATE_ATTACK; - else if (info->Mood == MoodType::Bored || info->Flags) + else if (creature->Mood == MoodType::Bored || creature->Flags) item->Animation.TargetState = TREX_STATE_WALK_FORWARD; else item->Animation.TargetState = TREX_STATE_RUN_FORWARD; @@ -134,32 +140,32 @@ namespace TEN::Entities::TR3 break; case TREX_STATE_WALK_FORWARD: - info->MaxTurn = ANGLE(2.0f); + creature->MaxTurn = TREX_WALK_TURN_RATE_MAX; - if (info->Mood != MoodType::Bored || !info->Flags) + if (creature->Mood != MoodType::Bored || !creature->Flags) item->Animation.TargetState = TREX_STATE_IDLE; - else if (AI.ahead && TestProbability(0.015f)) + else if (AI.ahead && TestProbability(TREX_ROAR_CHANCE)) { - item->Animation.RequiredState = TREX_STATE_ROAR; item->Animation.TargetState = TREX_STATE_IDLE; + item->Animation.RequiredState = TREX_STATE_ROAR; } break; case TREX_STATE_RUN_FORWARD: - info->MaxTurn = ANGLE(4.0f); + creature->MaxTurn = TREX_RUN_TURN_RATE_MAX; if (AI.distance < pow(SECTOR(5), 2) && AI.bite) item->Animation.TargetState = TREX_STATE_IDLE; - else if (info->Flags) + else if (creature->Flags) item->Animation.TargetState = TREX_STATE_IDLE; - else if (info->Mood != MoodType::Escape && - AI.ahead && TestProbability(0.015f)) + else if (creature->Mood != MoodType::Escape && + AI.ahead && TestProbability(TREX_ROAR_CHANCE)) { - item->Animation.RequiredState = TREX_STATE_ROAR; item->Animation.TargetState = TREX_STATE_IDLE; + item->Animation.RequiredState = TREX_STATE_ROAR; } - else if (info->Mood == MoodType::Bored) + else if (creature->Mood == MoodType::Bored) item->Animation.TargetState = TREX_STATE_IDLE; break; @@ -178,8 +184,8 @@ namespace TEN::Entities::TR3 } } - CreatureJoint(item, 0, (short)(head * 2)); - info->JointRotation[1] = info->JointRotation[0]; + CreatureJoint(item, 0, head * 2); + creature->JointRotation[1] = creature->JointRotation[0]; CreatureAnimation(itemNumber, angle, 0); diff --git a/TombEngine/Objects/TR3/Entity/tr3_trex.h b/TombEngine/Objects/TR3/Entity/tr3_trex.h index 7975f0b52..af6ba80f7 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_trex.h +++ b/TombEngine/Objects/TR3/Entity/tr3_trex.h @@ -1,7 +1,7 @@ #pragma once #include "Game/items.h" -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void LaraTRexDeath(ItemInfo* tRexItem, ItemInfo* laraItem); void TRexControl(short itemNumber); diff --git a/TombEngine/Objects/TR3/Entity/tr3_tribesman.cpp b/TombEngine/Objects/TR3/Entity/tr3_tribesman.cpp index 7eac71702..83ce466b8 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_tribesman.cpp +++ b/TombEngine/Objects/TR3/Entity/tr3_tribesman.cpp @@ -20,7 +20,7 @@ using namespace TEN::Entities::Traps; using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { const auto TribesmanAxeBite = BiteInfo(Vector3(0.0f, 16.0f, 265.0f), 13); const auto TribesmanDartBite1 = BiteInfo(Vector3(0.0f, 0.0f, -200.0f), 13); @@ -47,7 +47,6 @@ namespace TEN::Entities::TR3 enum TribesmanState { - TRIBESMAN_STATE_NONE = 0, TRIBESMAN_STATE_CROUCH_IDLE = 1, TRIBESMAN_STATE_WALK_FORWARD = 2, TRIBESMAN_STATE_RUN_FORWARD = 3, @@ -105,9 +104,9 @@ namespace TEN::Entities::TR3 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short head = 0; short angle = 0; short tilt = 0; + short head = 0; if (item->HitPoints <= 0) { @@ -153,7 +152,7 @@ namespace TEN::Entities::TR3 { creature->MaxTurn = 0; - if (TestProbability(0.008f)) + if (TestProbability(1.0f / 128)) item->Animation.TargetState = TRIBESMAN_STATE_WALK_FORWARD; } else if (creature->Mood == MoodType::Escape) @@ -192,7 +191,7 @@ namespace TEN::Entities::TR3 { creature->MaxTurn = 0; - if (TestProbability(0.008f)) + if (TestProbability(1.0f / 128)) item->Animation.TargetState = TRIBESMAN_STATE_WALK_FORWARD; } else if (creature->Mood == MoodType::Escape) @@ -217,15 +216,15 @@ namespace TEN::Entities::TR3 break; case TRIBESMAN_STATE_WALK_FORWARD: - tilt = angle / 8; creature->MaxTurn = ANGLE(9.0f); creature->Flags = 0; + tilt = angle / 8; if (creature->Mood == MoodType::Bored) { creature->MaxTurn /= 4; - if (TestProbability(0.008f)) + if (TestProbability(1.0f / 128)) { if (TestProbability(0.25f)) item->Animation.TargetState = TRIBESMAN_STATE_CROUCH_IDLE; @@ -248,15 +247,15 @@ namespace TEN::Entities::TR3 break; case TRIBESMAN_STATE_RUN_FORWARD: - tilt = angle / 4; creature->MaxTurn = ANGLE(6.0f); creature->Flags = 0; + tilt = angle / 4; if (creature->Mood == MoodType::Bored) { creature->MaxTurn /= 4; - if (TestProbability(0.008f)) + if (TestProbability(1.0f / 128)) { if (TestProbability(0.5f)) item->Animation.TargetState = TRIBESMAN_STATE_CROUCH_IDLE; @@ -280,6 +279,7 @@ namespace TEN::Entities::TR3 case TRIBESMAN_STATE_AXE_ATTACK_HIGH_START: creature->MaxTurn = ANGLE(4.0f); + if (AI.bite || AI.distance < pow(682, 2)) item->Animation.TargetState = TRIBESMAN_STATE_AXE_ATTACK_HIGH_CONTINUE; else @@ -311,12 +311,9 @@ namespace TEN::Entities::TR3 } else { - if (creature->Enemy) + if (creature->Enemy != nullptr) { - auto direction = creature->Enemy->Pose.Position - item->Pose.Position; - if (abs(direction.x) < SECTOR(0.5f) && - abs(direction.y) < SECTOR(0.5f) && - abs(direction.z) < SECTOR(0.5f) && + if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f) && creature->Flags >= TribesmanAxeHit[item->Animation.ActiveState][0] && creature->Flags <= TribesmanAxeHit[item->Animation.ActiveState][1]) { @@ -386,10 +383,8 @@ namespace TEN::Entities::TR3 short angle = 0; short tilt = 0; - short headX = 0; - short headY = 0; - short torsoX = 0; - short torsoY = 0; + auto extraHeadRot = Vector3Shrt::Zero; + auto extraTorsoRot = Vector3Shrt::Zero; if (item->HitPoints <= 0) { @@ -422,8 +417,8 @@ namespace TEN::Entities::TR3 angle = CreatureTurn(item, creature->Mood == MoodType::Bored ? ANGLE(2.0f) : creature->MaxTurn); if (AI.ahead) { - headY = AI.angle / 2; - torsoY = AI.angle / 2; + extraHeadRot.y = AI.angle / 2; + extraTorsoRot.y = AI.angle / 2; } if (item->HitStatus || @@ -441,18 +436,18 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoY = AI.angle; - torsoX = AI.xAngle / 2; + extraTorsoRot.x = AI.xAngle / 2; + extraTorsoRot.y = AI.angle; } if (item->AIBits & GUARD) { creature->MaxTurn = 0; - headY = AIGuard(creature); - torsoX = 0; - torsoY = 0; + extraHeadRot.y = AIGuard(creature); + extraTorsoRot.x = 0; + extraTorsoRot.y = 0; - if (!(GetRandomControl() & 0xFF)) + if (TestProbability(1.0f / 256)) item->Animation.TargetState = TRIBESMAN_STATE_IDLE; break; @@ -472,7 +467,7 @@ namespace TEN::Entities::TR3 item->Animation.TargetState = TRIBESMAN_STATE_DART_ATTACK; else if (creature->Mood == MoodType::Bored) { - if (TestProbability(0.015f)) + if (TestProbability(1.0f / 64)) item->Animation.TargetState = TRIBESMAN_STATE_WALK_FORWARD; else break; @@ -482,18 +477,18 @@ namespace TEN::Entities::TR3 break; - case 11: + case TRIBESMAN_STATE_IDLE: creature->MaxTurn = ANGLE(2.0f); creature->Flags &= 0x0FFF; if (item->AIBits & GUARD) { creature->MaxTurn = 0; - headY = AIGuard(creature); - torsoX = 0; - torsoY = 0; + extraHeadRot.y = AIGuard(creature); + extraTorsoRot.x = 0; + extraTorsoRot.y = 0; - if (!(GetRandomControl() & 0xFF)) + if (TestProbability(1.0f / 256)) item->Animation.TargetState = TRIBESMAN_STATE_CROUCH_IDLE; break; @@ -511,7 +506,7 @@ namespace TEN::Entities::TR3 item->Animation.TargetState = TRIBESMAN_STATE_WALK_FORWARD; else if (Targetable(item, &AI) && AI.distance < pow(MAX_VISIBILITY_DISTANCE, 2)) item->Animation.TargetState = TRIBESMAN_STATE_CROUCH_IDLE; - else if (creature->Mood == MoodType::Bored && TestProbability(0.015f)) + else if (creature->Mood == MoodType::Bored && TestProbability(1.0f / 64)) item->Animation.TargetState = TRIBESMAN_STATE_WALK_FORWARD; else item->Animation.TargetState = TRIBESMAN_STATE_RUN_FORWARD; @@ -543,7 +538,7 @@ namespace TEN::Entities::TR3 break; - case 3: + case TRIBESMAN_STATE_RUN_FORWARD: creature->MaxTurn = ANGLE(6.0f); creature->Flags &= 0x0FFF; tilt = angle / 4; @@ -575,8 +570,8 @@ namespace TEN::Entities::TR3 if (AI.ahead) { - torsoX = AI.xAngle; - torsoY = AI.angle; + extraTorsoRot.x = AI.xAngle; + extraTorsoRot.y = AI.angle; } if (abs(AI.angle) < ANGLE(2.0f)) @@ -610,9 +605,7 @@ namespace TEN::Entities::TR3 { if (creature->Enemy != nullptr && !(creature->Flags & 0xf000)) { - if (abs(creature->Enemy->Pose.Position.x - item->Pose.Position.x) < pow(SECTOR(0.5f), 2) && - abs(creature->Enemy->Pose.Position.y - item->Pose.Position.y) < pow(SECTOR(0.5f), 2) && - abs(creature->Enemy->Pose.Position.z - item->Pose.Position.z) < pow(SECTOR(0.5f), 2)) + if (Vector3Int::Distance(item->Pose.Position, creature->Enemy->Pose.Position) <= SECTOR(0.5f)) { DoDamage(creature->Enemy, 5); SoundEffect(SFX_TR4_LARA_THUD, &item->Pose); @@ -627,12 +620,12 @@ namespace TEN::Entities::TR3 CreatureTilt(item, tilt); - headY -= torsoY; + extraHeadRot.y -= extraTorsoRot.y; - CreatureJoint(item, 0, torsoY); - CreatureJoint(item, 1, torsoX); - CreatureJoint(item, 2, headY); - CreatureJoint(item, 3, headX); + CreatureJoint(item, 0, extraTorsoRot.y); + CreatureJoint(item, 1, extraTorsoRot.x); + CreatureJoint(item, 2, extraHeadRot.y); + CreatureJoint(item, 3, extraHeadRot.x); CreatureAnimation(itemNumber, angle, 0); } diff --git a/TombEngine/Objects/TR3/Entity/tr3_tribesman.h b/TombEngine/Objects/TR3/Entity/tr3_tribesman.h index 72743252a..696518dea 100644 --- a/TombEngine/Objects/TR3/Entity/tr3_tribesman.h +++ b/TombEngine/Objects/TR3/Entity/tr3_tribesman.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR3 +namespace TEN::Entities::Creatures::TR3 { void TribemanAxeControl(short itemNumber); void TribemanDartsControl(short itemNumber); diff --git a/TombEngine/Objects/TR3/Trap/train.cpp b/TombEngine/Objects/TR3/Trap/train.cpp index 2f3756025..f5832279f 100644 --- a/TombEngine/Objects/TR3/Trap/train.cpp +++ b/TombEngine/Objects/TR3/Trap/train.cpp @@ -21,14 +21,16 @@ long TrainTestHeight(ItemInfo* item, long x, long z, short* roomNumber) { - float s = phd_sin(item->Pose.Orientation.y); - float c = phd_cos(item->Pose.Orientation.y); - - Vector3Int pos; - pos.x = round(item->Pose.Position.x + z * s + x * c); - pos.y = round(item->Pose.Position.y - z * phd_sin(item->Pose.Orientation.x) + x * phd_sin(item->Pose.Orientation.z)); - pos.z = round(item->Pose.Position.z + z * c - x * s); + float sinX = phd_sin(item->Pose.Orientation.x); + float sinY = phd_sin(item->Pose.Orientation.y); + float cosY = phd_cos(item->Pose.Orientation.y); + float sinZ = phd_sin(item->Pose.Orientation.z); + auto pos = Vector3Int( + round(item->Pose.Position.x + ((z * sinY) + (x * cosY))), + round(item->Pose.Position.y - ((z * sinX) + (x * sinZ))), + round(item->Pose.Position.z + ((z * cosY) - (x * sinY))) + ); auto probe = GetCollision(pos.x, pos.y, pos.z, item->RoomNumber); *roomNumber = probe.RoomNumber; @@ -45,11 +47,11 @@ void TrainControl(short itemNumber) if (item->ItemFlags[0] == 0) item->ItemFlags[0] = item->ItemFlags[1] = TRAIN_VEL; - float s = phd_sin(item->Pose.Orientation.y); - float c = phd_cos(item->Pose.Orientation.y); + float sinY = phd_sin(item->Pose.Orientation.y); + float cosY = phd_cos(item->Pose.Orientation.y); - item->Pose.Position.x += item->ItemFlags[1] * s; - item->Pose.Position.z += item->ItemFlags[1] * c; + item->Pose.Position.x += item->ItemFlags[1] * sinY; + item->Pose.Position.z += item->ItemFlags[1] * cosY; short roomNumber; long rh = TrainTestHeight(item, 0, SECTOR(5), &roomNumber); @@ -70,7 +72,7 @@ void TrainControl(short itemNumber) item->Pose.Orientation.x = -(rh - floorHeight) * 2; - TriggerDynamicLight(item->Pose.Position.x + SECTOR(3) * s, item->Pose.Position.y, item->Pose.Position.z + SECTOR(3) * c, 16, 31, 31, 31); + TriggerDynamicLight(item->Pose.Position.x + SECTOR(3) * sinY, item->Pose.Position.y, item->Pose.Position.z + SECTOR(3) * cosY, 16, 31, 31, 31); if (item->ItemFlags[1] != TRAIN_VEL) { @@ -80,8 +82,8 @@ void TrainControl(short itemNumber) if (!UseForcedFixedCamera) { - ForcedFixedCamera.x = item->Pose.Position.x + SECTOR(8) * s; - ForcedFixedCamera.z = item->Pose.Position.z + SECTOR(8) * c; + ForcedFixedCamera.x = item->Pose.Position.x + SECTOR(8) * sinY; + ForcedFixedCamera.z = item->Pose.Position.z + SECTOR(8) * cosY; ForcedFixedCamera.y = GetCollision(ForcedFixedCamera.x, item->Pose.Position.y - CLICK(2), ForcedFixedCamera.z, item->RoomNumber).Position.Floor; @@ -95,12 +97,13 @@ void TrainControl(short itemNumber) void TrainCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) { + auto* item = &g_Level.Items[itemNumber]; auto* laraInfo = GetLaraInfo(laraItem); - auto* trainItem = &g_Level.Items[itemNumber]; - if (!TestBoundsCollide(trainItem, laraItem, coll->Setup.Radius)) + if (!TestBoundsCollide(item, laraItem, coll->Setup.Radius)) return; - if (!TestCollision(trainItem, laraItem)) + + if (!TestCollision(item, laraItem)) return; SoundEffect(SFX_TR4_LARA_GENERAL_DEATH, &laraItem->Pose, SoundEnvironment::Always); @@ -111,10 +114,10 @@ void TrainCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) laraItem->Animation.FrameNumber = g_Level.Anims[laraItem->Animation.AnimNumber].frameBase; // laraItem->Animation.ActiveState = EXTRA_TRAINKILL; // laraItem->Animation.TargetState = EXTRA_TRAINKILL; - laraItem->Pose.Orientation.y = trainItem->Pose.Orientation.y; - laraItem->Animation.Velocity.z = 0; - laraItem->Animation.Velocity.y = 0; laraItem->Animation.IsAirborne = false; + laraItem->Animation.Velocity.y = 0.0f; + laraItem->Animation.Velocity.z = 0.0f; + laraItem->Pose.Orientation.y = item->Pose.Orientation.y; DoDamage(laraItem, INT_MAX); @@ -126,13 +129,13 @@ void TrainCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) laraInfo->HitDirection = -1; laraInfo->Air = -1; - trainItem->ItemFlags[1] = 160; + item->ItemFlags[1] = 160; - float s = phd_sin(trainItem->Pose.Orientation.y); - float c = phd_cos(trainItem->Pose.Orientation.y); + float sinY = phd_sin(item->Pose.Orientation.y); + float cosY = phd_cos(item->Pose.Orientation.y); - long x = laraItem->Pose.Position.x + CLICK(1) * s; - long z = laraItem->Pose.Position.z + CLICK(1) * c; + long x = laraItem->Pose.Position.x + CLICK(1) * sinY; + long z = laraItem->Pose.Position.z + CLICK(1) * cosY; - DoLotsOfBlood(x, laraItem->Pose.Position.y - CLICK(2), z, SECTOR(1), trainItem->Pose.Orientation.y, laraItem->RoomNumber, 15); + DoLotsOfBlood(x, laraItem->Pose.Position.y - CLICK(2), z, SECTOR(1), item->Pose.Orientation.y, laraItem->RoomNumber, 15); } diff --git a/TombEngine/Objects/TR3/tr3_objects.cpp b/TombEngine/Objects/TR3/tr3_objects.cpp index c9dde913f..4a0503d97 100644 --- a/TombEngine/Objects/TR3/tr3_objects.cpp +++ b/TombEngine/Objects/TR3/tr3_objects.cpp @@ -7,7 +7,7 @@ #include "Specific/level.h" #include "Specific/setup.h" -// Entities +// Creatures #include "Objects/TR3/Entity/tr3_civvy.h" // OK #include "Objects/TR3/Entity/tr3_cobra.h" // OK #include "Objects/TR3/Entity/tr3_fish_emitter.h" // OK @@ -35,7 +35,7 @@ #include "Objects/TR3/Vehicles/upv.h" #include "Objects/TR3/Vehicles/rubber_boat.h" -using namespace TEN::Entities::TR3; +using namespace TEN::Entities::Creatures::TR3; static void StartEntity(ObjectInfo* obj) { @@ -198,7 +198,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveAnim = true; obj->saveFlags = true; obj->pivotLength = 50; - obj->zoneType = ZONE_WATER; + obj->ZoneType = ZoneType::Water; g_Level.Bones[obj->boneIndex + 10 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 14 * 4] |= ROT_Z; @@ -293,7 +293,7 @@ static void StartEntity(ObjectInfo* obj) obj->saveAnim = true; obj->saveFlags = true; obj->pivotLength = 0; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; diff --git a/TombEngine/Objects/TR4/Entity/tr4_ahmet.cpp b/TombEngine/Objects/TR4/Entity/tr4_ahmet.cpp index 9c2dc71a3..f4e8d5fcc 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_ahmet.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_ahmet.cpp @@ -44,7 +44,7 @@ namespace TEN::Entities::TR4 enum AhmetState { - AHMET_STATE_NONE = 0, + // No state 0. AHMET_STATE_IDLE = 1, AHMET_STATE_WALK_FORWARD = 2, AHMET_STATE_RUN_FORWARD = 3, @@ -131,7 +131,7 @@ namespace TEN::Entities::TR4 auto* creature = GetCreatureInfo(item); short angle = 0; - short headY = 0; + auto extraHeadRot = Vector3Shrt::Zero; if (item->HitPoints <= 0) { @@ -147,14 +147,14 @@ namespace TEN::Entities::TR4 else { SetAnimation(item, AHMET_ANIM_DEATH); - Lara.InteractedItem = itemNumber; // TODO: Check if it's really required! -- TokyoSU, 3/8/2022 + Lara.InteractedItem = itemNumber; // TODO: Check if it's really required! -- TokyoSU 3/8/2022 } TriggerAhmetDeathEffect(item); } else { - if (item->AIBits != 0) // Does this entity have AI object? NOTE: Previous one checked "& ALL_AIOBJ" -- TokyoSU, 3/8/2022 + if (item->AIBits != 0) // Does this entity have AI object? NOTE: Previous one checked "& ALL_AIOBJ" -- TokyoSU 3/8/2022 GetAITarget(creature); AI_INFO AI, laraAI; @@ -167,7 +167,7 @@ namespace TEN::Entities::TR4 } else { - int dx = LaraItem->Pose.Position.x - item->Pose.Position.x; // TODO: Make ahmet to not use LaraItem global -- TokyoSU, 3/8/2022 + int dx = LaraItem->Pose.Position.x - item->Pose.Position.x; // TODO: Make ahmet to not use LaraItem global -- TokyoSU 3/8/2022 int dz = LaraItem->Pose.Position.z - item->Pose.Position.z; laraAI.angle = short(phd_atan(dx, dz)) - item->Pose.Orientation.y; laraAI.distance = SQUARE(dx) + SQUARE(dz); @@ -186,7 +186,7 @@ namespace TEN::Entities::TR4 } if (AI.ahead) - headY = AI.angle; + extraHeadRot.y = AI.angle; switch (item->Animation.ActiveState) { @@ -197,16 +197,16 @@ namespace TEN::Entities::TR4 if (item->AIBits & GUARD) { item->Animation.TargetState = AHMET_STATE_IDLE; - headY = AIGuard(creature); + extraHeadRot.y = AIGuard(creature); } else if (item->AIBits & PATROL1) { item->Animation.TargetState = AHMET_STATE_WALK_FORWARD; - headY = 0; + extraHeadRot.y = 0; } else if (creature->Mood == MoodType::Bored || creature->Mood == MoodType::Escape) { - if (Lara.TargetEntity == item || !AI.ahead) // TODO: Make ahmet not use LaraInfo global. -- TokyoSU, 3/8/2022 + if (Lara.TargetEntity == item || !AI.ahead) // TODO: Make ahmet not use LaraInfo global. -- TokyoSU 3/8/2022 item->Animation.TargetState = AHMET_STATE_RUN_FORWARD; else item->Animation.TargetState = AHMET_STATE_IDLE; @@ -239,7 +239,7 @@ namespace TEN::Entities::TR4 if (item->AIBits & PATROL1) { item->Animation.TargetState = AHMET_STATE_WALK_FORWARD; - headY = 0; + extraHeadRot.y = 0; } else if (AI.bite && AI.distance < AHMET_IDLE_RANGE) item->Animation.TargetState = AHMET_STATE_IDLE; @@ -323,7 +323,8 @@ namespace TEN::Entities::TR4 if (!(creature->Flags & 1) && item->Animation.AnimNumber == (Objects[item->ObjectNumber].animIndex + AHMET_ANIM_JUMP_BITE_ATTACK_CONTINUE)) { - if (item->Animation.FrameNumber > (g_Level.Anims[item->Animation.AnimNumber].frameBase + 11) && item->TestBits(JointBitType::Touch, AhmetSwipeAttackLeftJoints)) + if (item->Animation.FrameNumber > (g_Level.Anims[item->Animation.AnimNumber].frameBase + 11) && + item->TestBits(JointBitType::Touch, AhmetSwipeAttackLeftJoints)) { DoDamage(creature->Enemy, AHMET_BITE_ATTACK_DAMAGE); CreatureEffect2(item, AhmetBiteJaw, 10, -1, DoBloodSplat); @@ -351,13 +352,17 @@ namespace TEN::Entities::TR4 } else { - if (!(creature->Flags & 1) && item->Animation.FrameNumber > (g_Level.Anims[item->Animation.AnimNumber].frameBase + 14) && item->TestBits(JointBitType::Touch, AhmetSwipeAttackLeftJoints)) + if (!(creature->Flags & 1) && + item->Animation.FrameNumber > (g_Level.Anims[item->Animation.AnimNumber].frameBase + 14) && + item->TestBits(JointBitType::Touch, AhmetSwipeAttackLeftJoints)) { DoDamage(creature->Enemy, AHMET_SWIPE_ATTACK_DAMAGE); CreatureEffect2(item, AhmetBiteLeft, 10, -1, DoBloodSplat); creature->Flags |= 1; } - else if (!(creature->Flags & 2) && item->Animation.FrameNumber > (g_Level.Anims[item->Animation.AnimNumber].frameBase + 22) && item->TestBits(JointBitType::Touch, AhmetSwipeAttackRightJoints)) + else if (!(creature->Flags & 2) && + item->Animation.FrameNumber > (g_Level.Anims[item->Animation.AnimNumber].frameBase + 22) && + item->TestBits(JointBitType::Touch, AhmetSwipeAttackRightJoints)) { DoDamage(creature->Enemy, AHMET_SWIPE_ATTACK_DAMAGE); CreatureEffect2(item, AhmetBiteRight, 10, -1, DoBloodSplat); @@ -371,7 +376,7 @@ namespace TEN::Entities::TR4 TestTriggers(item, true); CreatureTilt(item, 0); - CreatureJoint(item, 0, headY); + CreatureJoint(item, 0, extraHeadRot.y); CreatureAnimation(itemNumber, angle, 0); } diff --git a/TombEngine/Objects/TR4/Entity/tr4_baboon.cpp b/TombEngine/Objects/TR4/Entity/tr4_baboon.cpp index b6c3c037b..575fc5f5f 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_baboon.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_baboon.cpp @@ -49,8 +49,7 @@ namespace TEN::Entities::TR4 enum BaboonState { - BABOON_STATE_NULL = 0, - BABOON_STATE_NONE = 1, + // No states 0-1. BABOON_STATE_WALK_FORWARD = 2, BABOON_STATE_IDLE = 3, BABOON_STATE_RUN_FORWARD = 4, @@ -437,7 +436,7 @@ namespace TEN::Entities::TR4 { if (item->AIBits & FOLLOW) item->Animation.TargetState = BABOON_STATE_WALK_FORWARD; - else if (TestProbability(0.008f)) + else if (TestProbability(1.0f / 128)) item->Animation.TargetState = BABOON_STATE_SIT_IDLE; } else if (creature->Mood == MoodType::Escape) @@ -447,7 +446,7 @@ namespace TEN::Entities::TR4 if (AI.bite && AI.distance < BABOON_ATTACK_READY_RANGE) item->Animation.TargetState = BABOON_STATE_IDLE; } - else if (TestProbability(0.008f)) + else if (TestProbability(1.0f / 128)) item->Animation.TargetState = BABOON_STATE_SIT_IDLE; break; diff --git a/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp b/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp index 81e2377ef..ec5f9af76 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_baddy.cpp @@ -1215,7 +1215,7 @@ namespace TEN::Entities::TR4 case BADDY_STATE_BLIND: if (!FlashGrenadeAftershockTimer) { - if (TestProbability(0.008f)) + if (TestProbability(1.0f / 128)) item->Animation.TargetState = BADDY_STATE_IDLE; } diff --git a/TombEngine/Objects/TR4/Entity/tr4_bat.cpp b/TombEngine/Objects/TR4/Entity/tr4_bat.cpp index a7736ea16..b5295a7a1 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_bat.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_bat.cpp @@ -29,7 +29,7 @@ namespace TEN::Entities::TR4 enum BatState { - BAT_STATE_NONE = 0, + // No state 0. BAT_STATE_DROP_FROM_CEILING = 1, BAT_STATE_FLY = 2, BAT_STATE_ATTACK = 3, @@ -102,7 +102,7 @@ namespace TEN::Entities::TR4 break; case BAT_STATE_FLY: - if (AI.distance < BAT_ATTACK_RANGE || TestProbability(0.015f)) + if (AI.distance < BAT_ATTACK_RANGE || TestProbability(1.0f / 64)) creature->Flags = 0; if (!creature->Flags) diff --git a/TombEngine/Objects/TR4/Entity/tr4_big_beetle.cpp b/TombEngine/Objects/TR4/Entity/tr4_big_beetle.cpp index bf15fe9c9..011bc2aa3 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_big_beetle.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_big_beetle.cpp @@ -28,7 +28,7 @@ namespace TEN::Entities::TR4 enum BigBeetleState { - BBEETLE_STATE_NONE = 0, + // No state 0. BBEETLE_STATE_IDLE = 1, BBEETLE_STATE_TAKE_OFF = 2, BBEETLE_STATE_FLY_FORWARD = 3, @@ -122,7 +122,7 @@ namespace TEN::Entities::TR4 angle = CreatureTurn(item, creature->MaxTurn); if (item->HitStatus || AI.distance > BIG_BEETLE_AWARE_RANGE || - TestProbability(0.008f)) + TestProbability(1.0f / 128)) { creature->Flags = 0; } diff --git a/TombEngine/Objects/TR4/Entity/tr4_big_scorpion.cpp b/TombEngine/Objects/TR4/Entity/tr4_big_scorpion.cpp index ea93ae035..3d1cef69a 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_big_scorpion.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_big_scorpion.cpp @@ -22,7 +22,7 @@ namespace TEN::Entities::TR4 { constexpr auto BIG_SCORPION_ATTACK_DAMAGE = 120; constexpr auto BIG_SCORPION_TROOP_ATTACK_DAMAGE = 15; - constexpr auto BIG_SCORPION_STINGER_POISON_POTENCY = 8; + constexpr auto BIG_SCORPION_STINGER_POISON_POTENCY = 16; constexpr auto BIG_SCORPION_ATTACK_RANGE = SQUARE(SECTOR(1.35)); constexpr auto BIG_SCORPION_RUN_RANGE = SQUARE(SECTOR(2)); @@ -35,7 +35,7 @@ namespace TEN::Entities::TR4 enum BigScorpionState { - BSCORPION_STATE_NONE = 0, + // No state 0. BSCORPION_STATE_IDLE = 1, BSCORPION_STATE_WALK_FORWARD = 2, BSCORPION_STATE_RUN_FORWARD = 3, diff --git a/TombEngine/Objects/TR4/Entity/tr4_crocodile.cpp b/TombEngine/Objects/TR4/Entity/tr4_crocodile.cpp index 8d2164cd9..6d646e16e 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_crocodile.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_crocodile.cpp @@ -30,9 +30,9 @@ namespace TEN::Entities::TR4 constexpr auto CROC_SWIM_SPEED = 16; - #define CROC_STATE_WALK_FORWARD_ANGLE ANGLE(3.0f) - #define CROC_SWIM_ANGLE ANGLE(3.0f) - #define CROC_STATE_RUN_FORWARD_ANGLE ANGLE(5.0f) + #define CROC_STATE_WALK_TURN_RATE_MAX ANGLE(3.0f) + #define CROC_STATE_RUN_TURN_RATE_MAX ANGLE(5.0f) + #define CROC_STATE_SWIM_TURN_RATE_MAX ANGLE(3.0f) const auto CrocodileBite = BiteInfo(Vector3(0.0f, -100.0f, 500.0f), 9); const vector CrocodileBiteAttackJoints = { 8, 9 }; @@ -49,7 +49,7 @@ namespace TEN::Entities::TR4 CROC_STATE_DEATH = 7, CROC_STATE_SWIM_FORWARD = 8, CROC_STATE_WATER_BITE_ATTACK = 9, - CROC_STATE_WATER_DEATH = 10, + CROC_STATE_WATER_DEATH = 10 }; enum CrocodileAnim @@ -119,12 +119,12 @@ namespace TEN::Entities::TR4 return; auto* item = &g_Level.Items[itemNumber]; - auto* creature = GetCreatureInfo(item); auto* object = &Objects[item->ObjectNumber]; + auto* creature = GetCreatureInfo(item); - AI_INFO AI; short angle = 0; short boneAngle = 0; + AI_INFO AI; if (item->HitPoints <= 0) { @@ -178,7 +178,7 @@ namespace TEN::Entities::TR4 item->Animation.TargetState = CROC_STATE_IDLE; item->ItemFlags[0] += item->ItemFlags[1]; - if (TestProbability(0.03f)) + if (TestProbability(1.0f / 30)) { if (TestProbability(0.5f)) item->ItemFlags[1] = 0; @@ -203,7 +203,7 @@ namespace TEN::Entities::TR4 break; case CROC_STATE_WALK_FORWARD: - creature->MaxTurn = CROC_STATE_WALK_FORWARD_ANGLE; + creature->MaxTurn = CROC_STATE_WALK_TURN_RATE_MAX; // Land to water transition. if (IsCrocodileInWater(item) && !item->Animation.RequiredState) @@ -222,7 +222,7 @@ namespace TEN::Entities::TR4 break; case CROC_STATE_RUN_FORWARD: - creature->MaxTurn = CROC_STATE_RUN_FORWARD_ANGLE; + creature->MaxTurn = CROC_STATE_RUN_TURN_RATE_MAX; // Land to water transition. if (IsCrocodileInWater(item)) @@ -243,7 +243,7 @@ namespace TEN::Entities::TR4 case CROC_STATE_BITE_ATTACK: if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase) - item->Animation.RequiredState = 0; + item->Animation.RequiredState = CROC_STATE_NONE_1; if (AI.bite && item->TestBits(JointBitType::Touch, CrocodileBiteAttackJoints)) @@ -261,7 +261,7 @@ namespace TEN::Entities::TR4 break; case CROC_STATE_SWIM_FORWARD: - creature->MaxTurn = CROC_SWIM_ANGLE; + creature->MaxTurn = CROC_STATE_SWIM_TURN_RATE_MAX; // Water to land transition. if (!IsCrocodileInWater(item)) diff --git a/TombEngine/Objects/TR4/Entity/tr4_dog.cpp b/TombEngine/Objects/TR4/Entity/tr4_dog.cpp index 707fec832..171ad0847 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_dog.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_dog.cpp @@ -169,7 +169,7 @@ namespace TEN::Entities::TR4 creature->Flags++; creature->MaxTurn = 0; - if (creature->Flags > 300 && TestProbability(0.004f)) + if (creature->Flags > 300 && TestProbability(1.0f / 256)) item->Animation.TargetState = DOG_STATE_IDLE; } @@ -202,7 +202,7 @@ namespace TEN::Entities::TR4 else { if (item->Animation.ActiveState == DOG_STATE_STALK_IDLE && - TestProbability(0.004f)) + TestProbability(1.0f / 256)) { item->Animation.TargetState = DOG_STATE_IDLE; break; @@ -243,7 +243,7 @@ namespace TEN::Entities::TR4 creature->MaxTurn = ANGLE(1.0f); creature->Flags = 0; - if (TestProbability(0.008f)) + if (TestProbability(1.0f / 128)) { if (item->AIBits & MODIFY) { @@ -258,7 +258,7 @@ namespace TEN::Entities::TR4 if (TestProbability(0.875f)) { - if (TestProbability(0.03f)) + if (TestProbability(1.0f / 30)) item->Animation.TargetState = DOG_STATE_HOWL; break; @@ -279,7 +279,7 @@ namespace TEN::Entities::TR4 if (item->AIBits & PATROL1) item->Animation.TargetState = DOG_STATE_WALK_FORWARD; - else if (creature->Mood == MoodType::Bored && TestProbability(0.008f)) + else if (creature->Mood == MoodType::Bored && TestProbability(1.0f / 128)) item->Animation.TargetState = DOG_STATE_IDLE; else item->Animation.TargetState = DOG_STATE_STALK; diff --git a/TombEngine/Objects/TR4/Entity/tr4_hammerhead.cpp b/TombEngine/Objects/TR4/Entity/tr4_hammerhead.cpp index 53c67e5b3..029fe7793 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_hammerhead.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_hammerhead.cpp @@ -16,6 +16,7 @@ using std::vector; namespace TEN::Entities::TR4 { constexpr auto HAMMERHEAD_BITE_ATTACK_DAMAGE = 120; + constexpr auto HAMMERHEAD_ATTACK_RANGE = SQUARE(SECTOR(0.66f)); const auto HammerheadBite = BiteInfo(Vector3::Zero, 12); const vector HammerheadBiteAttackJoints = { 10, 12, 13 }; @@ -60,109 +61,100 @@ namespace TEN::Entities::TR4 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + HAMMERHEAD_ANIM_IDLE; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = HAMMERHEAD_STATE_IDLE; - item->Animation.TargetState = HAMMERHEAD_STATE_IDLE; + SetAnimation(item, HAMMERHEAD_ANIM_IDLE); } void HammerheadControl(short itemNumber) { - if (CreatureActive(itemNumber)) + if (!CreatureActive(itemNumber)) + return; + + auto* item = &g_Level.Items[itemNumber]; + auto* creature = GetCreatureInfo(item); + + if (item->HitPoints <= 0) { - auto* item = &g_Level.Items[itemNumber]; - auto* creature = GetCreatureInfo(item); + if (item->Animation.ActiveState != HAMMERHEAD_STATE_DEATH) + SetAnimation(item, HAMMERHEAD_ANIM_DEATH_START); - if (item->HitPoints > 0) + item->HitPoints = 0; + CreatureFloat(itemNumber); + } + else + { + if (item->AIBits) + GetAITarget(creature); + else if (creature->HurtByLara) + creature->Enemy = LaraItem; + + AI_INFO AI; + CreatureAIInfo(item, &AI); + + if (!creature->Enemy->IsLara()) + phd_atan(LaraItem->Pose.Position.z - item->Pose.Position.z, LaraItem->Pose.Position.x - item->Pose.Position.x); + + GetCreatureMood(item, &AI, true); + CreatureMood(item, &AI, true); + + short angle = CreatureTurn(item, creature->MaxTurn); + + switch (item->Animation.ActiveState) { - if (item->AIBits) - GetAITarget(creature); - else if (creature->HurtByLara) - creature->Enemy = LaraItem; + case HAMMERHEAD_STATE_IDLE: + item->Animation.TargetState = HAMMERHEAD_STATE_SWIM_SLOW; + creature->Flags = 0; + break; - AI_INFO AI; - CreatureAIInfo(item, &AI); + case HAMMERHEAD_STATE_SWIM_SLOW: + creature->MaxTurn = ANGLE(7.0f); - if (creature->Enemy != LaraItem) - phd_atan(LaraItem->Pose.Position.z - item->Pose.Position.z, LaraItem->Pose.Position.x - item->Pose.Position.x); - - GetCreatureMood(item, &AI, true); - CreatureMood(item, &AI, true); - - short angle = CreatureTurn(item, creature->MaxTurn); - - switch (item->Animation.ActiveState) + if (AI.distance <= pow(SECTOR(1), 2)) { - case HAMMERHEAD_STATE_IDLE: - item->Animation.TargetState = HAMMERHEAD_STATE_SWIM_SLOW; - creature->Flags = 0; - break; - - case HAMMERHEAD_STATE_SWIM_SLOW: - creature->MaxTurn = ANGLE(7.0f); - - if (AI.distance <= pow(SECTOR(1), 2)) - { - if (AI.distance < pow(682, 2)) - item->Animation.TargetState = HAMMERHEAD_STATE_IDLE_BITE_ATTACK; - } - else - item->Animation.TargetState = HAMMERHEAD_STATE_SWIM_FAST; - - break; - - case HAMMERHEAD_STATE_SWIM_FAST: - if (AI.distance < pow(SECTOR(1), 2)) - item->Animation.TargetState = HAMMERHEAD_STATE_SWIM_SLOW; - - break; - - case HAMMERHEAD_STATE_IDLE_BITE_ATTACK: - if (!creature->Flags) - { - if (item->TestBits(JointBitType::Touch, HammerheadBiteAttackJoints)) - { - DoDamage(creature->Enemy, HAMMERHEAD_BITE_ATTACK_DAMAGE); - CreatureEffect(item, HammerheadBite, DoBloodSplat); - creature->Flags = 1; - } - } - - break; - - default: - break; + if (AI.distance < HAMMERHEAD_ATTACK_RANGE) + item->Animation.TargetState = HAMMERHEAD_STATE_IDLE_BITE_ATTACK; } - - CreatureTilt(item, 0); - CreatureJoint(item, 0, angle * -2); - CreatureJoint(item, 1, angle * -2); - CreatureJoint(item, 2, angle * -2); - CreatureJoint(item, 3, angle * 2); - - // NOTE: in TR2 shark there was a call to CreatureKill with special kill anim - // Hammerhead seems to not have it in original code but this check is still there as a leftover - if (item->Animation.ActiveState == HAMMERHEAD_STATE_KILL) - AnimateItem(item); else + item->Animation.TargetState = HAMMERHEAD_STATE_SWIM_FAST; + + break; + + case HAMMERHEAD_STATE_SWIM_FAST: + if (AI.distance < pow(SECTOR(1), 2)) + item->Animation.TargetState = HAMMERHEAD_STATE_SWIM_SLOW; + + break; + + case HAMMERHEAD_STATE_IDLE_BITE_ATTACK: + if (!creature->Flags) { - CreatureAnimation(itemNumber, angle, 0); - CreatureUnderwater(item, 341); + if (item->TestBits(JointBitType::Touch, HammerheadBiteAttackJoints)) + { + DoDamage(creature->Enemy, HAMMERHEAD_BITE_ATTACK_DAMAGE); + CreatureEffect(item, HammerheadBite, DoBloodSplat); + creature->Flags = 1; + } } + + break; + + default: + break; } + + CreatureTilt(item, 0); + CreatureJoint(item, 0, angle * -2); + CreatureJoint(item, 1, angle * -2); + CreatureJoint(item, 2, angle * -2); + CreatureJoint(item, 3, angle * 2); + + // NOTE: in TR2 shark there was a call to CreatureKill with special kill anim + // Hammerhead seems to not have it in original code but this check is still there as a leftover + if (item->Animation.ActiveState == HAMMERHEAD_STATE_KILL) + AnimateItem(item); else { - item->HitPoints = 0; - - if (item->Animation.ActiveState != HAMMERHEAD_STATE_DEATH) - { - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + HAMMERHEAD_ANIM_DEATH_START; - item->Animation.ActiveState = HAMMERHEAD_STATE_DEATH; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.FrameNumber].frameBase; - } - - CreatureFloat(itemNumber); + CreatureAnimation(itemNumber, angle, 0); + CreatureUnderwater(item, 341); } } } diff --git a/TombEngine/Objects/TR4/Entity/tr4_harpy.cpp b/TombEngine/Objects/TR4/Entity/tr4_harpy.cpp index ee80886b1..f23c2476a 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_harpy.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_harpy.cpp @@ -75,31 +75,32 @@ namespace TEN::Entities::TR4 HARPY_ANIM_GLIDE = 18 }; - static void TriggerHarpyMissile(PHD_3DPOS* pose, short roomNumber, short mesh) + void TriggerHarpyMissile(PHD_3DPOS* pose, short roomNumber, short mesh) { short fxNumber = CreateNewEffect(roomNumber); - if (fxNumber != -1) - { - auto* fx = &EffectList[fxNumber]; + if (fxNumber == -1) + return; - fx->pos.Position.x = pose->Position.x; - fx->pos.Position.y = pose->Position.y - (GetRandomControl() & 0x3F) - 32; - fx->pos.Position.z = pose->Position.z; - fx->pos.Orientation.x = pose->Orientation.x; - fx->pos.Orientation.y = pose->Orientation.y; - fx->pos.Orientation.z = 0; - fx->roomNumber = roomNumber; - fx->counter = short(2 * GetRandomControl() + 0x8000); - fx->objectNumber = ID_ENERGY_BUBBLES; - fx->speed = (GetRandomControl() & 0x1F) + 96; - fx->flag1 = mesh; - fx->frameNumber = Objects[fx->objectNumber].meshIndex + mesh * 2; - } + auto* fx = &EffectList[fxNumber]; + + fx->pos.Position.x = pose->Position.x; + fx->pos.Position.y = pose->Position.y - (GetRandomControl() & 0x3F) - 32; + fx->pos.Position.z = pose->Position.z; + fx->pos.Orientation.x = pose->Orientation.x; + fx->pos.Orientation.y = pose->Orientation.y; + fx->pos.Orientation.z = 0; + fx->roomNumber = roomNumber; + fx->counter = short(2 * GetRandomControl() + 0x8000); + fx->objectNumber = ID_ENERGY_BUBBLES; + fx->speed = (GetRandomControl() & 0x1F) + 96; + fx->flag1 = mesh; + fx->frameNumber = Objects[fx->objectNumber].meshIndex + mesh * 2; } - static void TriggerHarpyFlame(short itemNumber, ItemInfo* target, byte nodeNumber, short size) + void TriggerHarpyFlame(short itemNumber, ItemInfo* target, byte nodeNumber, short size) { auto* item = &g_Level.Items[itemNumber]; + int dx = target->Pose.Position.x - item->Pose.Position.x; int dz = target->Pose.Position.z - item->Pose.Position.z; @@ -143,7 +144,7 @@ namespace TEN::Entities::TR4 } } - static void TriggerHarpySparks(ItemInfo* target, int x, int y, int z, short xv, short yv, short zv) + void TriggerHarpySparks(ItemInfo* target, int x, int y, int z, short xv, short yv, short zv) { int dx = target->Pose.Position.x - x; int dz = target->Pose.Position.z - z; @@ -180,12 +181,13 @@ namespace TEN::Entities::TR4 } } - static void DoHarpyEffects(ItemInfo* item, CreatureInfo* creature, short itemNumber) + void DoHarpyEffects(ItemInfo* item, CreatureInfo* creature, short itemNumber) { item->ItemFlags[0]++; auto rh = Vector3Int(HarpyAttack1.Position); GetJointAbsPosition(item, &rh, HarpyAttack1.meshNum); + auto lr = Vector3Int(HarpyAttack2.Position); GetJointAbsPosition(item, &lr, HarpyAttack2.meshNum); @@ -269,9 +271,9 @@ namespace TEN::Entities::TR4 if (item->HitPoints <= 0) { - int state = item->Animation.ActiveState - 9; item->HitPoints = 0; + int state = item->Animation.ActiveState - 9; if (state) { state--; @@ -307,7 +309,7 @@ namespace TEN::Entities::TR4 { item->Animation.TargetState = HARPY_STATE_DEATH_END; item->Animation.IsAirborne = false; - item->Animation.Velocity.y = 0; + item->Animation.Velocity.y = 0.0f; item->Pose.Position.y = item->Floor; } @@ -322,10 +324,8 @@ namespace TEN::Entities::TR4 creature->Enemy = nullptr; - for (int i = 0; i < ActiveCreatures.size(); i++) + for (auto& currentCreature : ActiveCreatures) { - auto* currentCreature = ActiveCreatures[i]; - if (currentCreature->ItemNumber == NO_ITEM || currentCreature->ItemNumber == itemNumber) continue; @@ -348,7 +348,7 @@ namespace TEN::Entities::TR4 AI_INFO AI; CreatureAIInfo(item, &AI); - if (creature->Enemy != LaraItem) + if (!creature->Enemy->IsLara()) phd_atan(LaraItem->Pose.Position.z - item->Pose.Position.z, LaraItem->Pose.Position.x - item->Pose.Position.x); GetCreatureMood(item, &AI, true); @@ -437,10 +437,9 @@ namespace TEN::Entities::TR4 { if (AI.distance >= pow(341, 2)) { - if (AI.ahead && + if (AI.ahead && TestProbability(0.5f) && AI.distance >= pow(SECTOR(2), 2) && - AI.distance > pow(SECTOR(3.5f), 2) && - TestProbability(0.5f)) + AI.distance > pow(SECTOR(3.5f), 2)) { item->Animation.TargetState = HARPY_STATE_FLAME_ATTACK; item->ItemFlags[0] = 0; @@ -507,7 +506,7 @@ namespace TEN::Entities::TR4 creature->MaxTurn = ANGLE(2.0f); if (item->TestBits(JointBitType::Touch, HarpySwoopAttackJoints) || - creature->Enemy && !creature->Enemy->IsLara() && + creature->Enemy != nullptr && !creature->Enemy->IsLara() && abs(creature->Enemy->Pose.Position.y - item->Pose.Position.y) <= SECTOR(1) && AI.distance < pow(SECTOR(2), 2)) { @@ -526,7 +525,7 @@ namespace TEN::Entities::TR4 if (creature->Flags == 0 && (item->TestBits(JointBitType::Touch, HarpyStingerAttackJoints) || - creature->Enemy && !creature->Enemy->IsLara() && + creature->Enemy != nullptr && !creature->Enemy->IsLara() && abs(creature->Enemy->Pose.Position.y - item->Pose.Position.y) <= SECTOR(1) && AI.distance < pow(SECTOR(2), 2))) { diff --git a/TombEngine/Objects/TR4/Entity/tr4_horseman.cpp b/TombEngine/Objects/TR4/Entity/tr4_horseman.cpp index 55d173b21..fb40ecc75 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_horseman.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_horseman.cpp @@ -18,12 +18,17 @@ #include "Specific/trmath.h" using namespace TEN::Math::Random; +using std::vector; namespace TEN::Entities::TR4 { const auto HorsemanBite1 = BiteInfo(Vector3::Zero, 6); const auto HorsemanBite2 = BiteInfo(Vector3::Zero, 14); const auto HorsemanBite3 = BiteInfo(Vector3::Zero, 10); + const vector HorsemanAxeAttackJoints = { 5, 6 }; + const vector HorsemanKickAttackJoints = { 14 }; + const vector HorsemanMountedAttackJoints = { 5, 6, 10 }; + const vector HorsemanShieldAttackJoints = { 10 }; const auto HorseBite1 = BiteInfo(Vector3::Zero, 13); const auto HorseBite2 = BiteInfo(Vector3::Zero, 17); @@ -550,7 +555,7 @@ namespace TEN::Entities::TR4 case HORSEMAN_STATE_MOUNTED_ATTACK_RIGHT: if (!creature->Flags) { - if (item->TouchBits & 0x60) + if (item->TestBits(JointBitType::Touch, HorsemanAxeAttackJoints)) { DoDamage(creature->Enemy, 250); CreatureEffect2(item, HorsemanBite1, 10, item->Pose.Orientation.y, DoBloodSplat); @@ -566,7 +571,7 @@ namespace TEN::Entities::TR4 case HORSEMAN_STATE_MOUNTED_ATTACK_LEFT: if (!creature->Flags) { - if (item->TouchBits & 0x4000) + if (item->TestBits(JointBitType::Touch, HorsemanKickAttackJoints)) { DoDamage(creature->Enemy, 100); CreatureEffect2(item, HorsemanBite2, 3, item->Pose.Orientation.y, DoBloodSplat); @@ -655,7 +660,7 @@ namespace TEN::Entities::TR4 if (!creature->Flags) { - if (item->TouchBits & 0x4000) + if (item->TestBits(JointBitType::Touch, HorsemanAxeAttackJoints)) { DoDamage(creature->Enemy, 100); CreatureEffect2(item, HorsemanBite2, 3, item->Pose.Orientation.y, DoBloodSplat); @@ -696,16 +701,16 @@ namespace TEN::Entities::TR4 if (!creature->Flags) { - if (item->TouchBits & 0x460) + if (item->TestBits(JointBitType::Touch, HorsemanMountedAttackJoints)) { LaraItem->HitStatus = true; - if (item->TouchBits & 0x60) + if (item->TestBits(JointBitType::Touch, HorsemanAxeAttackJoints)) { DoDamage(creature->Enemy, 250); CreatureEffect2(horseItem, HorsemanBite1, 20, -1, DoBloodSplat); } - else if (item->TouchBits & 0x400) + else if (item->TestBits(JointBitType::Touch, HorsemanShieldAttackJoints)) { DoDamage(creature->Enemy, 150); CreatureEffect2(horseItem, HorsemanBite3, 10, -1, DoBloodSplat); diff --git a/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp b/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp index f211923c9..e5aed5aa6 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_knight_templar.cpp @@ -29,7 +29,7 @@ namespace TEN::Entities::TR4 enum KnightTemplarState { - KTEMPLAR_STATE_NONE = 0, + // No state 0. KTEMPLAR_STATE_IDLE = 1, KTEMPLAR_STATE_WALK_FORWARD = 2, KTEMPLAR_STATE_SWORD_ATTACK_1 = 3, @@ -62,10 +62,7 @@ namespace TEN::Entities::TR4 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - item->Animation.AnimNumber = Objects[ID_KNIGHT_TEMPLAR].animIndex + KTEMPLAR_ANIM_IDLE; - item->Animation.TargetState = KTEMPLAR_STATE_IDLE; - item->Animation.ActiveState = KTEMPLAR_STATE_IDLE; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; + SetAnimation(item, KTEMPLAR_ANIM_IDLE); item->MeshBits &= 0xF7FF; } @@ -75,8 +72,14 @@ namespace TEN::Entities::TR4 return; auto* item = &g_Level.Items[itemNumber]; - auto* creature = GetCreatureInfo(item); auto* object = &Objects[item->ObjectNumber]; + auto* creature = GetCreatureInfo(item); + + short angle = 0; + short tilt = 0; + short joint0 = 0; + short joint1 = 0; + short joint2 = 0; if (item->Animation.AnimNumber == object->animIndex || (item->Animation.AnimNumber - object->animIndex) == KTEMPLAR_ANIM_WALK_FORWARD_RIGHT_1 || @@ -92,12 +95,6 @@ namespace TEN::Entities::TR4 } } - short tilt = 0; - short angle = 0; - short joint0 = 0; - short joint1 = 0; - short joint2 = 0; - // Knight is immortal. if (item->HitPoints < object->HitPoints) item->HitPoints = object->HitPoints; @@ -198,7 +195,6 @@ namespace TEN::Entities::TR4 ShatterObject(nullptr, mesh, -64, LaraItem->RoomNumber, 0); SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose); - mesh->flags &= ~StaticMeshFlags::SM_VISIBLE; currentFloor->Stopper = false; TestTriggers(pos.x, pos.y, pos.z, item->RoomNumber, true); diff --git a/TombEngine/Objects/TR4/Entity/tr4_mummy.cpp b/TombEngine/Objects/TR4/Entity/tr4_mummy.cpp index bd09df3ed..57f753546 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_mummy.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_mummy.cpp @@ -17,7 +17,7 @@ using std::vector; namespace TEN::Entities::TR4 { - constexpr auto MUMMY_ATTACK_DAMAGE = 100; + constexpr auto MUMMY_SWIPE_ATTACK_DAMAGE = 100; constexpr auto MUMMY_IDLE_SWIPE_ATTACK_RANGE = SQUARE(SECTOR(0.5f)); constexpr auto MUMMY_WALK_SWIPE_ATTACK_RANGE = SQUARE(SECTOR(0.67f)); @@ -26,11 +26,12 @@ namespace TEN::Entities::TR4 constexpr auto MUMMY_ARMS_UP_RANGE = SQUARE(SECTOR(3)); constexpr auto MUMMY_AWARE_RANGE = SQUARE(SECTOR(7)); - #define MUMMY_WALK_TURN_ANGLE ANGLE(7.0f) + #define MUMMY_WALK_TURN_RATE_MAX ANGLE(7.0f) + #define MUMMY_ATTACK_TURN_RATE_MAX ANGLE(7.0f) const auto MummyBite1 = BiteInfo(Vector3::Zero, 11); const auto MummyBite2 = BiteInfo(Vector3::Zero, 14); - const vector MummyAttackJoints { 11, 14 }; + const vector MummySwipeAttackJoints { 11, 14 }; enum MummyState { @@ -81,16 +82,10 @@ namespace TEN::Entities::TR4 if (item->TriggerFlags == 2) { SetAnimation(item, MUMMY_ANIM_COLLAPSE_END); - item->Animation.TargetState = MUMMY_STATE_INACTIVE_LYING_DOWN; // TODO: Check if needed. -- Sezz - item->Animation.ActiveState = MUMMY_STATE_INACTIVE_LYING_DOWN; item->Status -= ITEM_INVISIBLE; } else - { SetAnimation(item, MUMMY_ANIM_ARMS_CROSSED); - item->Animation.TargetState = MUMMY_STATE_INACTIVE_ARMS_CROSSED; // TODO: Check if needed. -- Sezz - item->Animation.ActiveState = MUMMY_STATE_INACTIVE_ARMS_CROSSED; - } } void MummyControl(short itemNumber) @@ -101,8 +96,8 @@ namespace TEN::Entities::TR4 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short tilt = 0; short angle = 0; + short tilt = 0; short joint0 = 0; short joint1 = 0; short joint2 = 0; @@ -209,7 +204,7 @@ namespace TEN::Entities::TR4 } else { - creature->MaxTurn = MUMMY_WALK_TURN_ANGLE; + creature->MaxTurn = MUMMY_WALK_TURN_RATE_MAX; if (AI.distance >= MUMMY_ARMS_UP_RANGE) { @@ -223,7 +218,7 @@ namespace TEN::Entities::TR4 break; case MUMMY_STATE_WALK_FORWARD_ARMS_UP: - creature->MaxTurn = MUMMY_WALK_TURN_ANGLE; + creature->MaxTurn = MUMMY_WALK_TURN_RATE_MAX; creature->Flags = 0; if (AI.distance < MUMMY_IDLE_SWIPE_ATTACK_RANGE) @@ -260,7 +255,7 @@ namespace TEN::Entities::TR4 joint1 = 0; joint2 = 0; - if (AI.distance < MUMMY_ACTIVATE_RANGE || TestProbability(0.008f)) + if (AI.distance < MUMMY_ACTIVATE_RANGE || TestProbability(1.0f / 128)) { item->Animation.TargetState = MUMMY_STATE_COLLAPSED_TO_IDLE; item->HitPoints = Objects[item->ObjectNumber].HitPoints; @@ -272,26 +267,26 @@ namespace TEN::Entities::TR4 case MUMMY_STATE_IDLE_SWIPE_ATTACK: creature->MaxTurn = 0; - if (abs(AI.angle) >= ANGLE(7.0f)) + if (abs(AI.angle) >= MUMMY_ATTACK_TURN_RATE_MAX) { if (AI.angle >= 0) - item->Pose.Orientation.y += ANGLE(7.0f); + item->Pose.Orientation.y += MUMMY_ATTACK_TURN_RATE_MAX; else - item->Pose.Orientation.y -= ANGLE(7.0f); + item->Pose.Orientation.y -= MUMMY_ATTACK_TURN_RATE_MAX; } else item->Pose.Orientation.y += AI.angle; if (!creature->Flags) { - if (item->TestBits(JointBitType::Touch, MummyAttackJoints)) + if (item->TestBits(JointBitType::Touch, MummySwipeAttackJoints)) { if (item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase && item->Animation.FrameNumber < g_Level.Anims[item->Animation.AnimNumber].frameEnd) { - DoDamage(creature->Enemy, MUMMY_ATTACK_DAMAGE); + DoDamage(creature->Enemy, MUMMY_SWIPE_ATTACK_DAMAGE); - if (item->Animation.AnimNumber == Objects[item->ObjectNumber].animIndex + MUMMY_ANIM_IDLE_SWIPE_ATTACK_LEFT) + if (item->Animation.AnimNumber == (Objects[item->ObjectNumber].animIndex + MUMMY_ANIM_IDLE_SWIPE_ATTACK_LEFT)) CreatureEffect2(item, MummyBite1, 5, -1, DoBloodSplat); else CreatureEffect2(item, MummyBite2, 5, -1, DoBloodSplat); @@ -309,11 +304,9 @@ namespace TEN::Entities::TR4 } CreatureTilt(item, 0); - CreatureJoint(item, 0, joint0); CreatureJoint(item, 1, joint1); CreatureJoint(item, 2, joint2); - CreatureAnimation(itemNumber, angle, 0); } } diff --git a/TombEngine/Objects/TR4/Entity/tr4_sas.cpp b/TombEngine/Objects/TR4/Entity/tr4_sas.cpp index 41949f888..0c483217b 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_sas.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_sas.cpp @@ -629,7 +629,7 @@ namespace TEN::Entities::TR4 if (item->Animation.ActiveState == 1) { - if (TestProbability(0.008f)) + if (TestProbability(1.0f / 128)) { item->Animation.TargetState = 2; AnimateItem(item); @@ -638,7 +638,7 @@ namespace TEN::Entities::TR4 item->Animation.TargetState = 3; } else if (item->Animation.ActiveState == 4 && - TestProbability(0.008f)) + TestProbability(1.0f / 128)) { item->Animation.TargetState = 5; AnimateItem(item); diff --git a/TombEngine/Objects/TR4/Entity/tr4_setha.cpp b/TombEngine/Objects/TR4/Entity/tr4_setha.cpp index 0dac11758..292c98829 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_setha.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_setha.cpp @@ -29,10 +29,7 @@ namespace TEN::Entities::TR4 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 4; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = 12; - item->Animation.TargetState = 12; + SetAnimation(item, 4); } void SethaControl(short itemNumber) @@ -113,8 +110,7 @@ namespace TEN::Entities::TR4 else if (LaraItem->Pose.Position.y >= (item->Pose.Position.y - SECTOR(1))) { if (AI.distance < pow(SECTOR(2.5f), 2) && - AI.ahead && - TestProbability(0.5f) && + AI.ahead && TestProbability(0.5f) && Targetable(item, &AI)) { item->Animation.TargetState = 11; @@ -195,8 +191,7 @@ namespace TEN::Entities::TR4 if (AI.bite && AI.distance < pow(SECTOR(4), 2) || - canJump || - creature->ReachedGoal) + canJump || creature->ReachedGoal) { item->Animation.TargetState = 1; } @@ -210,8 +205,7 @@ namespace TEN::Entities::TR4 if (AI.bite && AI.distance < pow(SECTOR(4), 2) || - canJump || - creature->ReachedGoal) + canJump || creature->ReachedGoal) { item->Animation.TargetState = 1; } @@ -659,9 +653,7 @@ namespace TEN::Entities::TR4 ); TriggerSethaSparks1( - pos.x, - pos.y, - pos.z, + pos.x, pos.y, pos.z, (pos1.x - pos.x), (pos1.y - pos.y), (SECTOR(1) - (GetRandomControl() & 0x7FF))); @@ -673,9 +665,7 @@ namespace TEN::Entities::TR4 ); TriggerSethaSparks1( - pos.x, - pos.y, - pos.z, + pos.x, pos.y, pos.z, (pos2.x - pos.x), (pos2.y - pos.y), (SECTOR(1) - (GetRandomControl() & 0x7FF))); diff --git a/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp b/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp index 5f3e68d0c..374f5fa07 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_skeleton.cpp @@ -29,6 +29,7 @@ namespace TEN::Entities::TR4 const auto SkeletonBite = BiteInfo(Vector3(0.0f, -16.0f, 200.0f), 11); const vector SkeletonSwordAttackJoints = { 15, 16 }; + // TODO: Fill in missign states. enum SkeletonState { SKELETON_STATE_SUBTERRANEAN = 0, @@ -72,36 +73,28 @@ namespace TEN::Entities::TR4 void InitialiseSkeleton(short itemNumber) { auto* item = &g_Level.Items[itemNumber]; - auto* object = &Objects[item->ObjectNumber]; ClearItem(itemNumber); + // TODO: Check cases 0 and 3. switch (item->TriggerFlags) { case 0: - item->Animation.AnimNumber = object->animIndex; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; + SetAnimation(item, SKELETON_ANIM_EMERGE); item->Animation.ActiveState = SKELETON_STATE_SUBTERRANEAN; item->Animation.TargetState = SKELETON_STATE_SUBTERRANEAN; break; case 1: - item->Animation.AnimNumber = object->animIndex + SKELETON_ANIM_JUMP_RIGHT_START; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = SKELETON_STATE_JUMP_RIGHT; - item->Animation.TargetState = SKELETON_STATE_JUMP_RIGHT; + SetAnimation(item, SKELETON_ANIM_JUMP_RIGHT_START); break; case 2: - item->Animation.AnimNumber = object->animIndex + SKELETON_ANIM_JUMP_LEFT_START; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = SKELETON_STATE_JUMP_LEFT; - item->Animation.TargetState = SKELETON_STATE_JUMP_LEFT; + SetAnimation(item, SKELETON_ANIM_JUMP_LEFT_START); break; case 3: - item->Animation.AnimNumber = object->animIndex; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; + SetAnimation(item, SKELETON_ANIM_EMERGE); item->Animation.ActiveState = SKELETON_STATE_JUMP_LIE_DOWN; item->Animation.TargetState = SKELETON_STATE_JUMP_LIE_DOWN; //item->status = ITEM_DEACTIVATED; @@ -175,15 +168,15 @@ namespace TEN::Entities::TR4 bool jumpLeft = false; bool jumpRight = false; - int distance = 0; - short tilt = 0; short angle = 0; + short tilt = 0; short joint1 = 0; short joint2 = 0; short joint3 = 0; short rotation = 0; + int distance = 0; - // Can skeleton jump? Check for a distance of 1 and 2 sectors. + // Can skeleton jump? Check for a distance of 1 and 2 blocks. int x = item->Pose.Position.x; int y = item->Pose.Position.y; int z = item->Pose.Position.z; @@ -204,24 +197,24 @@ namespace TEN::Entities::TR4 int height3 = GetCollision(x, y, z, item->RoomNumber).Position.Floor; int height = 0; - bool canJump1sector = true; - if (enemyItem && item->BoxNumber == LaraItem->BoxNumber && item->MeshBits & 0x200 || - y >= height1 - CLICK(1.5f) || - y >= height2 + CLICK(2) || - y <= height2 - CLICK(2)) + bool canJump1Block = true; + if (enemyItem && item->BoxNumber == LaraItem->BoxNumber && (item->MeshBits & 0x200) || + y >= (height1 - CLICK(1.5f)) || + y >= (height2 + CLICK(2)) || + y <= (height2 - CLICK(2))) { height = height2; - canJump1sector = false; + canJump1Block = false; } - bool canJump2sectors = true; - if (enemyItem && item->BoxNumber == LaraItem->BoxNumber && item->MeshBits & 0x200 || - y >= height1 - CLICK(1.5f) || - y >= height - CLICK(1.5f) || - y >= height3 + CLICK(2) || - y <= height3 - CLICK(2)) + bool canJump2Blocks = true; + if (enemyItem && item->BoxNumber == LaraItem->BoxNumber && (item->MeshBits & 0x200) || + y >= (height1 - CLICK(1.5f)) || + y >= (height - CLICK(1.5f)) || + y >= (height3 + CLICK(2)) || + y <= (height3 - CLICK(2))) { - canJump2sectors = false; + canJump2Blocks = false; } if (item->AIBits) @@ -235,17 +228,17 @@ namespace TEN::Entities::TR4 if (item->HitStatus && Lara.Control.Weapon.GunType == LaraWeaponType::Shotgun && AI.distance < pow(SECTOR(3.5f), 2) && - item->Animation.ActiveState != 7 && + item->Animation.ActiveState != SKELETON_STATE_USE_SHIELD && item->Animation.ActiveState != 17 && item->Animation.ActiveState != SKELETON_STATE_HURT_BY_SHOTGUN_1 && item->Animation.ActiveState != SKELETON_STATE_HURT_BY_SHOTGUN_2 && - item->Animation.ActiveState != 25) + item->Animation.ActiveState != SKELETON_STATE_JUMP_LIE_DOWN) { if (AI.angle >= ANGLE(67.5f) || AI.angle <= -ANGLE(67.5f)) { item->Animation.ActiveState = SKELETON_STATE_HURT_BY_SHOTGUN_2; item->Animation.AnimNumber = Objects[ID_SKELETON].animIndex + 33; - item->Pose.Orientation.y += AI.angle + -32768; + item->Pose.Orientation.y += AI.angle - ANGLE(180.0f); } else { @@ -365,7 +358,7 @@ namespace TEN::Entities::TR4 creature->Flags = 0; if (item->AIBits & GUARD || - TestProbability(0.03f) && + TestProbability(1.0f / 30) && (AI.distance > pow(SECTOR(1), 2) || creature->Mood != MoodType::Attack)) { @@ -381,14 +374,14 @@ namespace TEN::Entities::TR4 { if (item->AIBits & PATROL1) item->Animation.TargetState = 15; - else if (canJump1sector || canJump2sectors) + else if (canJump1Block || canJump2Blocks) { item->Animation.AnimNumber = Objects[ID_SKELETON].animIndex + 40; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = SKELETON_STATE_JUMP_LEFT; creature->MaxTurn = 0; - if (!canJump2sectors) + if (!canJump2Blocks) { item->Animation.TargetState = SKELETON_STATE_JUMP_FORWARD_1_BLOCK; creature->LOT.IsJumping = true; @@ -400,26 +393,15 @@ namespace TEN::Entities::TR4 } } else if (jumpLeft) - { - item->Animation.AnimNumber = Objects[ID_SKELETON].animIndex + 34; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = SKELETON_STATE_JUMP_LEFT; - item->Animation.TargetState = SKELETON_STATE_JUMP_LEFT; - } + SetAnimation(item, SKELETON_ANIM_JUMP_LEFT_START); else if (jumpRight) - { - item->Animation.AnimNumber = Objects[ID_SKELETON].animIndex + 37; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = SKELETON_STATE_JUMP_RIGHT; - item->Animation.TargetState = SKELETON_STATE_JUMP_RIGHT; - } + SetAnimation(item, SKELETON_ANIM_JUMP_RIGHT_START); else { if (creature->Mood == MoodType::Escape) { if (Lara.TargetEntity == item || - !AI.ahead || - item->HitStatus || + !AI.ahead || item->HitStatus || !(item->MeshBits & 0x200)) { item->Animation.TargetState = 15; @@ -430,8 +412,7 @@ namespace TEN::Entities::TR4 } else if (creature->Mood == MoodType::Bored || item->AIBits & FOLLOW && - (creature->ReachedGoal || - laraAI.distance > pow(SECTOR(2), 2))) + (creature->ReachedGoal || laraAI.distance > pow(SECTOR(2), 2))) { if (item->Animation.RequiredState) item->Animation.TargetState = item->Animation.RequiredState; @@ -439,8 +420,7 @@ namespace TEN::Entities::TR4 item->Animation.TargetState = 15; } else if (Lara.TargetEntity == item && - laraAI.angle && - laraAI.distance < pow(SECTOR(2), 2) && + laraAI.angle && laraAI.distance < pow(SECTOR(2), 2) && TestProbability(0.5f) && (Lara.Control.Weapon.GunType == LaraWeaponType::Shotgun || TestProbability(0.06f)) && item->MeshBits == -1) @@ -509,7 +489,7 @@ namespace TEN::Entities::TR4 { if (AI.bite && AI.distance < pow(SECTOR(1), 2)) item->Animation.TargetState = 18; - else if (canJump1sector || canJump2sectors) + else if (canJump1Block || canJump2Blocks) { item->Animation.TargetState = 2; creature->MaxTurn = 0; @@ -530,7 +510,7 @@ namespace TEN::Entities::TR4 creature->MaxTurn = ANGLE(7.0f); creature->LOT.IsJumping = false; - if (item->AIBits & GUARD || canJump1sector || canJump2sectors) + if (item->AIBits & GUARD || canJump1Block || canJump2Blocks) { if (item->MeshBits & 0x200) { @@ -633,7 +613,6 @@ namespace TEN::Entities::TR4 { ShatterObject(0, staticMesh, -128, LaraItem->RoomNumber, 0); SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose); - staticMesh->flags &= ~StaticMeshFlags::SM_VISIBLE; floor->Stopper = false; TestTriggers(item, true); break; @@ -669,7 +648,7 @@ namespace TEN::Entities::TR4 else item->Animation.TargetState = 2; } - else if (Lara.TargetEntity != item || item->MeshBits != -1 || Lara.Control.Weapon.GunType != LaraWeaponType::Shotgun || TestProbability(0.008f)) + else if (Lara.TargetEntity != item || item->MeshBits != -1 || Lara.Control.Weapon.GunType != LaraWeaponType::Shotgun || TestProbability(1.0f / 128)) item->Animation.TargetState = 2; break; @@ -729,7 +708,7 @@ namespace TEN::Entities::TR4 if (GetCollision(item).Position.Floor <= (item->Pose.Position.y + SECTOR(1))) { - if (TestProbability(0.03f)) + if (TestProbability(1.0f / 30)) item->Animation.TargetState = 14; } else diff --git a/TombEngine/Objects/TR4/Entity/tr4_small_scorpion.cpp b/TombEngine/Objects/TR4/Entity/tr4_small_scorpion.cpp index 04f9b176a..959ead06e 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_small_scorpion.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_small_scorpion.cpp @@ -57,11 +57,7 @@ namespace TEN::Entities::TR4 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - - item->Animation.AnimNumber = Objects[ID_SMALL_SCORPION].animIndex + SSCORPION_ANIM_IDLE; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.TargetState = SSCORPION_STATE_IDLE; - item->Animation.ActiveState = SSCORPION_STATE_IDLE; + SetAnimation(item, SSCORPION_ANIM_IDLE); } void SmallScorpionControl(short itemNumber) @@ -73,9 +69,9 @@ namespace TEN::Entities::TR4 auto* creature = GetCreatureInfo(item); short angle = 0; + short tilt = 0; short head = 0; short neck = 0; - short tilt = 0; short joint0 = 0; short joint1 = 0; short joint2 = 0; @@ -87,9 +83,7 @@ namespace TEN::Entities::TR4 if (item->Animation.ActiveState != SSCORPION_STATE_DEATH_1 && item->Animation.ActiveState != SSCORPION_STATE_DEATH_2) { - item->Animation.AnimNumber = Objects[ID_SMALL_SCORPION].animIndex + SSCORPION_ANIM_DEATH; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = SSCORPION_STATE_DEATH_1; + SetAnimation(item, SSCORPION_ANIM_DEATH); } } else @@ -118,6 +112,7 @@ namespace TEN::Entities::TR4 else if (AI.bite) { creature->MaxTurn = ANGLE(6.0f); + if (TestProbability(0.5f)) item->Animation.TargetState = SSCORPION_STATE_ATTACK_1; else diff --git a/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp b/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp index 6d309dca1..d8aab5f4b 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_sphinx.cpp @@ -103,7 +103,6 @@ namespace TEN::Entities::TR4 ShatterObject(nullptr, mesh, -64, item->RoomNumber, 0); SoundEffect(SFX_TR4_SMASH_ROCK, &item->Pose); - mesh->flags &= ~StaticMeshFlags::SM_VISIBLE; probe.Block = false; TestTriggers(x, y, z, item->RoomNumber, true); diff --git a/TombEngine/Objects/TR4/Entity/tr4_troops.cpp b/TombEngine/Objects/TR4/Entity/tr4_troops.cpp index 68102849d..9d12d8ecc 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_troops.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_troops.cpp @@ -455,7 +455,7 @@ namespace TEN::Entities::TR4 break; case TROOP_STATE_FLASHED: - if (!FlashGrenadeAftershockTimer && TestProbability(0.008f)) + if (!FlashGrenadeAftershockTimer && TestProbability(1.0f / 128)) item->Animation.TargetState = TROOP_STATE_GUARD; break; diff --git a/TombEngine/Objects/TR4/Entity/tr4_von_croy.cpp b/TombEngine/Objects/TR4/Entity/tr4_von_croy.cpp index a90133027..bccb3d500 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_von_croy.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_von_croy.cpp @@ -770,10 +770,10 @@ namespace TEN::Entities::TR4 creature->MaxTurn = 0; ClampRotation(&item->Pose, AI.angle, ANGLE(6.0f)); - if ((enemy == NULL || enemy->Flags != NULL) || + if ((enemy == nullptr || enemy->Flags != 0) || item->Animation.FrameNumber <= g_Level.Anims[item->Animation.AnimNumber].frameBase + 21) { - if (creature->Flags == NULL && enemy != nullptr) + if (creature->Flags == 0 && enemy != nullptr) { if (item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 15 && item->Animation.FrameNumber < g_Level.Anims[item->Animation.AnimNumber].frameBase + 26) @@ -849,7 +849,7 @@ namespace TEN::Entities::TR4 item->Animation.TargetState = VON_CROY_STATE_WALK; item->Animation.RequiredState = VON_CROY_STATE_RUN; - item->ItemFlags[2] = NULL; + item->ItemFlags[2] = 0; //if (sVar3 == -1) goto LAB_0041a991; if (!flags) { @@ -878,38 +878,38 @@ namespace TEN::Entities::TR4 CreatureJoint(item, 2, joint2); CreatureJoint(item, 3, joint3); - if ((item->Animation.ActiveState < VON_CROY_STATE_JUMP) && (item->Animation.ActiveState != VON_CROY_STATE_MONKEY)) + if (item->Animation.ActiveState < VON_CROY_STATE_JUMP && + item->Animation.ActiveState != VON_CROY_STATE_MONKEY) { switch (CreatureVault(itemNumber, angle, 2, 260)) { - case VON_CROY_STATE_WALK: + case 2: item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + VON_CROY_ANIM_CLIMB_2_BLOCKS; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = VON_CROY_STATE_CLIMB_2_BLOCKS; creature->MaxTurn = 0; break; - case VON_CROY_STATE_RUN: + case 3: item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + VON_CROY_ANIM_CLIMB_3_BLOCKS; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = VON_CROY_STATE_CLIMB_3_BLOCKS; creature->MaxTurn = 0; break; - case VON_CROY_STATE_START_MONKEY: + case 4: item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + VON_CROY_ANIM_CLIMB_4_BLOCKS; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = VON_CROY_STATE_CLIMB_4_BLOCKS; creature->MaxTurn = 0; break; - case VON_CROY_STATE_LOOK_BEFORE_JUMP: + case 7: item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + VON_CROY_ANIM_JUMP_TO_HANG; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; item->Animation.ActiveState = VON_CROY_STATE_GRAB_LADDER; creature->MaxTurn = 0; break; - // I am not sure what negative states are (probably the inverse of the above), I will leave them alone - Kubsy 18/06/2022 case -7: item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + VON_CROY_ANIM_CLIMB_DOWN_2_SECTORS; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; diff --git a/TombEngine/Objects/TR4/Entity/tr4_wild_boar.cpp b/TombEngine/Objects/TR4/Entity/tr4_wild_boar.cpp index 1070394eb..1c92e02c9 100644 --- a/TombEngine/Objects/TR4/Entity/tr4_wild_boar.cpp +++ b/TombEngine/Objects/TR4/Entity/tr4_wild_boar.cpp @@ -18,14 +18,13 @@ using namespace TEN::Math::Random; namespace TEN::Entities::TR4 { constexpr auto WILD_BOAR_ATTACK_DAMAGE = 30; - constexpr auto WILD_BOAR_ATTACK_RANGE = SQUARE(CLICK(1)); const auto WildBoarBite = BiteInfo(Vector3::Zero, 14); enum WildBoarState { - BOAR_STATE_NONE = 0, + // No state 0. BOAR_STATE_IDLE = 1, BOAR_STATE_RUN_FORWARD = 2, BOAR_STATE_GRAZE = 3, @@ -51,11 +50,7 @@ namespace TEN::Entities::TR4 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - - item->Animation.AnimNumber = Objects[ID_WILD_BOAR].animIndex + BOAR_ANIM_IDLE; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = BOAR_STATE_IDLE; - item->Animation.TargetState = BOAR_STATE_IDLE; + SetAnimation(item, BOAR_ANIM_IDLE); } void WildBoarControl(short itemNumber) @@ -67,9 +62,9 @@ namespace TEN::Entities::TR4 auto* creature = GetCreatureInfo(item); short angle = 0; + short tilt = 0; short head = 0; short neck = 0; - short tilt = 0; short joint0 = 0; short joint1 = 0; short joint2 = 0; @@ -153,7 +148,7 @@ namespace TEN::Entities::TR4 if (AI.ahead && AI.distance) item->Animation.TargetState = BOAR_STATE_IDLE; - else if (TestProbability(0.008f)) + else if (TestProbability(1.0f / 128)) item->Animation.TargetState = BOAR_STATE_IDLE; break; @@ -192,11 +187,7 @@ namespace TEN::Entities::TR4 item->HitPoints = 0; if (item->Animation.ActiveState != BOAR_STATE_DEATH) - { - item->Animation.AnimNumber = Objects[ID_WILD_BOAR].animIndex + BOAR_ANIM_DEATH; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = BOAR_STATE_DEATH; - } + SetAnimation(item, BOAR_ANIM_DEATH); } CreatureJoint(item, 0, joint0); diff --git a/TombEngine/Objects/TR4/Object/tr4_sarcophagus.cpp b/TombEngine/Objects/TR4/Object/tr4_sarcophagus.cpp index de7c8f3ae..62a14b29f 100644 --- a/TombEngine/Objects/TR4/Object/tr4_sarcophagus.cpp +++ b/TombEngine/Objects/TR4/Object/tr4_sarcophagus.cpp @@ -86,7 +86,7 @@ void SarcophagusCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* c { if (Objects[currentItem->ObjectNumber].isPickup) { - PickedUpObject(static_cast(currentItem->ObjectNumber), 0); + PickedUpObject(currentItem->ObjectNumber); currentItem->Status = ITEM_ACTIVE; currentItem->ItemFlags[3] = 1; AddDisplayPickup(currentItem->ObjectNumber); diff --git a/TombEngine/Objects/TR4/Trap/tr4_joby_spikes.cpp b/TombEngine/Objects/TR4/Trap/tr4_joby_spikes.cpp index 6bdf4f566..ea609102e 100644 --- a/TombEngine/Objects/TR4/Trap/tr4_joby_spikes.cpp +++ b/TombEngine/Objects/TR4/Trap/tr4_joby_spikes.cpp @@ -15,7 +15,7 @@ namespace TEN::Entities::TR4 { auto* item = &g_Level.Items[itemNumber]; - // Set bone mutators to 0 by default + // Set bone mutators to 0 by default. for (int i = 0; i < item->Animation.Mutator.size(); i++) item->Animation.Mutator[i].Scale.y = 0.0f; @@ -24,7 +24,7 @@ namespace TEN::Entities::TR4 auto probe = GetCollision(item); - // TODO: check this optimized division + // TODO: Check this optimized division. //v6 = 1321528399i64 * ((probe.Position.Floor - probe.Position.Ceiling) << 12); //item->itemFlags[3] = (HIDWORD(v6) >> 31) + (SHIDWORD(v6) >> 10); @@ -78,7 +78,7 @@ namespace TEN::Entities::TR4 item->Pose.Orientation.y += item->ItemFlags[0]; - // Update bone mutators + // Update bone mutators. if (item->ItemFlags[1]) { for (int i = 0; i < item->Animation.Mutator.size(); i++) diff --git a/TombEngine/Objects/TR4/Trap/tr4_stargate.cpp b/TombEngine/Objects/TR4/Trap/tr4_stargate.cpp index 515db8e47..92e570fd7 100644 --- a/TombEngine/Objects/TR4/Trap/tr4_stargate.cpp +++ b/TombEngine/Objects/TR4/Trap/tr4_stargate.cpp @@ -1,46 +1,45 @@ #include "framework.h" -#include "tr4_stargate.h" -#include "Specific/level.h" -#include "Game/control/control.h" -#include "Sound/sound.h" -#include "Game/collision/collide_room.h" -#include "Game/collision/collide_item.h" -#include "Game/collision/sphere.h" -#include "Game/Lara/lara.h" -#include "Game/effects/effects.h" -#include "Game/animation.h" -#include "Game/items.h" +#include "Objects/TR4/Trap/tr4_stargate.h" -namespace TEN::Entities::TR4 +#include "Game/animation.h" +#include "Game/collision/collide_item.h" +#include "Game/collision/collide_room.h" +#include "Game/collision/sphere.h" +#include "Game/control/control.h" +#include "Game/effects/effects.h" +#include "Game/items.h" +#include "Game/Lara/lara.h" +#include "Sound/sound.h" +#include "Specific/level.h" + +using std::vector; + +namespace TEN::Entities::Traps { - short StargateBounds[24] = + constexpr auto STARGATE_HARM_DAMAGE = 100; + + const vector StargateHarmJoints = { 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25 }; + const vector StargateBounds = { - -512, 512, -1024, - -896, -96, 96, - -512, 512, -128, - 0, -96, 96, - -512, -384, -1024, - 0, -96, 96, - 384, 512, -1024, - 0, -96, 96 + Vector3Int(-CLICK(2), CLICK(2), -SECTOR(2)), + Vector3Int(-896, -96, 96), + Vector3Int(-CLICK(2), CLICK(2), -128), + Vector3Int(0, -96, 96), + Vector3Int(-CLICK(2), -384, -SECTOR(2)), + Vector3Int(0, -96, 96), + Vector3Int(384, CLICK(2), -SECTOR(2)), + Vector3Int(0, -96, 96) }; void StargateControl(short itemNumber) { auto* item = &g_Level.Items[itemNumber]; - item->ItemFlags[3] = 50; if (TriggerActive(item)) { - int touchBits = 0x036DB600; - item->ItemFlags[0] = short(touchBits & 0xFFFF); - item->ItemFlags[1] = short((touchBits >> 16) & 0xFFFF); - SoundEffect(SFX_TR4_STARGATE_SWIRL, &item->Pose); AnimateItem(item); } - else - item->ItemFlags[0] = item->ItemFlags[1] = 0; } void StargateCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll) @@ -50,67 +49,38 @@ namespace TEN::Entities::TR4 if (item->Status == ITEM_INVISIBLE) return; - if (TestBoundsCollide(item, laraItem, coll->Setup.Radius)) + // TODO: Border collision. + /*for (auto& bounds : StargateBounds) { - for (int i = 0; i < 8; i++) + GlobalCollisionBounds.X1 = bounds.x; + GlobalCollisionBounds.Y1 = bounds.y; + GlobalCollisionBounds.Z1 = bounds.z; + + if (TestWithGlobalCollisionBounds(item, laraItem, coll)) + ItemPushItem(item, laraItem, coll, 0, 2); + }*/ + + if (!TestBoundsCollide(item, laraItem, coll->Setup.Radius)) + return; + + if (TestCollision(item, laraItem) && + TriggerActive(item) && + item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 20 && // Hardcoded frame range. + item->Animation.FrameNumber < g_Level.Anims[item->Animation.AnimNumber].frameBase + 60) + { + // Blades deal damage cumulatively. + for (int i = 0; i < StargateHarmJoints.size(); i++) { - GlobalCollisionBounds.X1 = StargateBounds[3 * i + 0]; - GlobalCollisionBounds.Y1 = StargateBounds[3 * i + 1]; - GlobalCollisionBounds.Z1 = StargateBounds[3 * i + 2]; - - if (TestWithGlobalCollisionBounds(item, laraItem, coll)) - ItemPushItem(item, laraItem, coll, 0, 2); - } - - int result = TestCollision(item, laraItem); - if (result) - { - int flags = item->ItemFlags[0] | item->ItemFlags[1] << 16; - result &= flags; - - if (result) + if (item->TestBits(JointBitType::Touch, StargateHarmJoints[i])) { - int j = 0; - do - { - if (result & 1) - { - GlobalCollisionBounds.X1 = CreatureSpheres[j].x - CreatureSpheres[j].r - item->Pose.Position.x; - GlobalCollisionBounds.Y1 = CreatureSpheres[j].y - CreatureSpheres[j].r - item->Pose.Position.y; - GlobalCollisionBounds.Z1 = CreatureSpheres[j].z - CreatureSpheres[j].r - item->Pose.Position.z; - GlobalCollisionBounds.X2 = CreatureSpheres[j].x + CreatureSpheres[j].r - item->Pose.Position.x; - GlobalCollisionBounds.Y2 = CreatureSpheres[j].y + CreatureSpheres[j].r - item->Pose.Position.y; - GlobalCollisionBounds.Z2 = CreatureSpheres[j].z + CreatureSpheres[j].r - item->Pose.Position.z; - - int oldX = LaraItem->Pose.Position.x; - int oldY = LaraItem->Pose.Position.y; - int oldZ = LaraItem->Pose.Position.z; - - if (ItemPushItem(item, laraItem, coll, flags & 1, 2)) - { - if ((flags & 1) && - (oldX != LaraItem->Pose.Position.x || - oldY != LaraItem->Pose.Position.y || - oldZ != LaraItem->Pose.Position.z) && - TriggerActive(item)) - { - DoBloodSplat((GetRandomControl() & 0x3F) + laraItem->Pose.Position.x - 32, - (GetRandomControl() & 0x1F) + CreatureSpheres[j].y - 16, - (GetRandomControl() & 0x3F) + laraItem->Pose.Position.z - 32, - (GetRandomControl() & 3) + 2, - 2 * GetRandomControl(), - laraItem->RoomNumber); - - DoDamage(laraItem, 100); - } - } - } - - result /= 2; - j++; - flags /= 2; - - } while (result); + DoDamage(laraItem, STARGATE_HARM_DAMAGE); + DoBloodSplat( + (GetRandomControl() & 0x3F) + laraItem->Pose.Position.x - 32, + (GetRandomControl() & 0x1F) + CreatureSpheres[i].y - 16, + (GetRandomControl() & 0x3F) + laraItem->Pose.Position.z - 32, + (GetRandomControl() & 3) + 2, + GetRandomControl() * 2, + laraItem->RoomNumber); } } } diff --git a/TombEngine/Objects/TR4/Trap/tr4_stargate.h b/TombEngine/Objects/TR4/Trap/tr4_stargate.h index 7fe4566d3..0d64e613e 100644 --- a/TombEngine/Objects/TR4/Trap/tr4_stargate.h +++ b/TombEngine/Objects/TR4/Trap/tr4_stargate.h @@ -1,9 +1,9 @@ #pragma once -struct ItemInfo; struct CollisionInfo; +struct ItemInfo; -namespace TEN::Entities::TR4 +namespace TEN::Entities::Traps { void StargateControl(short itemNumber); void StargateCollision(short itemNumber, ItemInfo* laraItem, CollisionInfo* coll); diff --git a/TombEngine/Objects/TR4/Trap/tr4_teethspike.cpp b/TombEngine/Objects/TR4/Trap/tr4_teethspike.cpp index a07c64af2..1130e9806 100644 --- a/TombEngine/Objects/TR4/Trap/tr4_teethspike.cpp +++ b/TombEngine/Objects/TR4/Trap/tr4_teethspike.cpp @@ -13,8 +13,8 @@ namespace TEN::Entities::TR4 { - constexpr auto TEETH_SPIKE_HARM_CONSTANT = 8; - constexpr auto TEETH_SPIKE_HARM_EMERGING = 30; + constexpr auto TEETH_SPIKE_HARM_DAMAGE_CONSTANT = 8; + constexpr auto TEETH_SPIKE_HARM_DAMAGE_EMERGING = 30; constexpr auto TEETH_SPIKES_DEFAULT_INTERVAL = 64; constexpr auto TEETH_SPIKE_BOUNDS_TOLERANCE_RATIO = 0.95f; @@ -37,7 +37,7 @@ namespace TEN::Entities::TR4 auto spikeBox = TO_DX_BBOX(item->Pose, GetBoundsAccurate(item)); auto itemBox = TO_DX_BBOX(collidingItem->Pose, GetBoundsAccurate(collidingItem)); - // Make intersection a bit more forgiving by reducing spike bounds a bit. + // Make intersection more forgiving by slightly reducing spike bounds. spikeBox.Extents = spikeBox.Extents * TEETH_SPIKE_BOUNDS_TOLERANCE_RATIO; return spikeBox.Contains(itemBox); } @@ -96,16 +96,16 @@ namespace TEN::Entities::TR4 if ((item->ItemFlags[0] >= 1024 || LaraItem->Animation.IsAirborne) && (angle > PI * 0.25f && angle < PI * 0.75f)) { - if (LaraItem->Animation.Velocity.y > 6 || item->ItemFlags[0] > 1024) + if (LaraItem->Animation.Velocity.y > 6.0f || item->ItemFlags[0] > 1024) { LaraItem->HitPoints = -1; bloodCount = 20; } } // Spikes are emerging or already fully protruded (in latter case, only damage Lara if she runs). - else if ((item->TriggerFlags != 1) || LaraItem->Animation.Velocity.z >= 30) + else if ((item->TriggerFlags != 1) || LaraItem->Animation.Velocity.z >= 30.0f) { - int damage = item->ItemFlags[0] == 1024 ? TEETH_SPIKE_HARM_EMERGING : TEETH_SPIKE_HARM_CONSTANT; + int damage = item->ItemFlags[0] == 1024 ? TEETH_SPIKE_HARM_DAMAGE_EMERGING : TEETH_SPIKE_HARM_DAMAGE_CONSTANT; DoDamage(LaraItem, damage); bloodCount = (GetRandomControl() & 3) + 2; } @@ -117,15 +117,15 @@ namespace TEN::Entities::TR4 int yTop = laraBounds->Y1 + LaraItem->Pose.Position.y; int yBottom = laraBounds->Y2 + LaraItem->Pose.Position.y; + // Spikes are downward; move blood origin to top. if (angle < PI * 0.125f || angle > PI * 0.825f) { - // Spikes are downward; move blood origin to top. y1 = -bounds->Y2; y2 = -bounds->Y1; } + // Spikes are upward; leave origin as is. else { - // Spikes are upward; leave origin as is. y1 = bounds->Y1; y2 = bounds->Y2; } diff --git a/TombEngine/Objects/TR4/tr4_objects.cpp b/TombEngine/Objects/TR4/tr4_objects.cpp index 5116b5006..68c5c733c 100644 --- a/TombEngine/Objects/TR4/tr4_objects.cpp +++ b/TombEngine/Objects/TR4/tr4_objects.cpp @@ -9,7 +9,7 @@ #include "Specific/setup.h" #include "Specific/level.h" -// Entities +// Creatures #include "tr4_enemy_jeep.h" #include "tr4_ahmet.h" // OK #include "tr4_baddy.h" // OK @@ -77,8 +77,10 @@ #include "tr4_teethspike.h" // Vehicles -#include "Objects/TR4/Vehicles/motorbike.h" #include "Objects/TR4/Vehicles/jeep.h" +#include "Objects/TR4/Vehicles/motorbike.h" + +using namespace TEN::Entities::Traps; namespace TEN::Entities { @@ -100,7 +102,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_BIG_SCORPION]; @@ -119,7 +121,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveFlags = true; obj->saveAnim = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_HAMMERHEAD]; @@ -139,7 +141,7 @@ namespace TEN::Entities obj->saveAnim = true; obj->saveFlags = true; obj->waterCreature = true; - obj->zoneType = ZONE_WATER; + obj->ZoneType = ZoneType::Water; } obj = &Objects[ID_WILD_BOAR]; @@ -158,7 +160,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 48 * 4] |= ROT_Z; g_Level.Bones[obj->boneIndex + 48 * 4] |= ROT_Y; @@ -182,7 +184,7 @@ namespace TEN::Entities obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 19 * 4] |= ROT_Y; } @@ -202,7 +204,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_FLYER; + obj->ZoneType = ZoneType::Flyer; } obj = &Objects[ID_AHMET]; @@ -221,7 +223,7 @@ namespace TEN::Entities obj->saveFlags = true; obj->saveHitpoints = true; obj->savePosition = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 9 * 4] |= ROT_Y; } @@ -244,7 +246,7 @@ namespace TEN::Entities obj->saveAnim = true; obj->saveFlags = true; obj->meshSwapSlot = ID_MESHSWAP_BADDY1; - obj->zoneType = ZONE_HUMAN_JUMP_AND_MONKEY; + obj->ZoneType = ZoneType::HumanJumpAndMonkey; g_Level.Bones[obj->boneIndex + 28 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 28 * 4] |= ROT_X; @@ -270,7 +272,7 @@ namespace TEN::Entities obj->saveAnim = true; obj->saveFlags = true; obj->meshSwapSlot = ID_MESHSWAP_BADDY2; - obj->zoneType = ZONE_HUMAN_JUMP_AND_MONKEY; + obj->ZoneType = ZoneType::HumanJumpAndMonkey; g_Level.Bones[obj->boneIndex + 28 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 28 * 4] |= ROT_X; @@ -295,7 +297,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex] |= ROT_Y; g_Level.Bones[obj->boneIndex] |= ROT_X; @@ -319,7 +321,7 @@ namespace TEN::Entities obj->saveAnim = true; obj->saveFlags = true; obj->undead = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 7 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 7 * 4] |= ROT_X; @@ -344,7 +346,7 @@ namespace TEN::Entities obj->saveAnim = true; obj->saveFlags = true; obj->undead = true; - obj->zoneType = ZONE_SKELLY; + obj->ZoneType = ZoneType::Skeleton; } obj = &Objects[ID_KNIGHT_TEMPLAR]; @@ -363,7 +365,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveFlags = true; obj->saveAnim = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X | ROT_Y; g_Level.Bones[obj->boneIndex + 7 * 4] |= ROT_Y; @@ -386,7 +388,7 @@ namespace TEN::Entities obj->saveAnim = true; obj->savePosition = true; obj->undead = false; - obj->zoneType = ZONE_FLYER; + obj->ZoneType = ZoneType::Flyer; } obj = &Objects[ID_SETHA]; @@ -406,7 +408,7 @@ namespace TEN::Entities obj->saveFlags = true; obj->saveAnim = true; obj->undead = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_DEMIGOD1]; @@ -426,7 +428,7 @@ namespace TEN::Entities obj->saveFlags = true; obj->saveAnim = true; obj->undead = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 4 * 4] |= ROT_X | ROT_Y | ROT_Z; g_Level.Bones[obj->boneIndex + 5 * 4] |= ROT_Y; @@ -448,7 +450,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveFlags = true; obj->saveAnim = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 4 * 4] |= ROT_X | ROT_Y | ROT_Z; g_Level.Bones[obj->boneIndex + 5 * 4] |= ROT_Y; } @@ -469,7 +471,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveFlags = true; obj->saveAnim = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 4 * 4] |= ROT_X | ROT_Y | ROT_Z; g_Level.Bones[obj->boneIndex + 5 * 4] |= ROT_Y; } @@ -483,7 +485,7 @@ namespace TEN::Entities obj->hitEffect = HIT_BLOOD; obj->nonLot = true; obj->savePosition = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_TROOPS]; @@ -502,7 +504,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex] |= ROT_X | ROT_Y; g_Level.Bones[obj->boneIndex + 7 * 4] |= ROT_X | ROT_Y; @@ -525,7 +527,7 @@ namespace TEN::Entities obj->saveAnim = true; obj->saveFlags = true; obj->explodableMeshbits = 64; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 0] |= ROT_Y; g_Level.Bones[obj->boneIndex + 1 * 4] |= ROT_X; @@ -548,7 +550,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_FLYER; + obj->ZoneType = ZoneType::Flyer; } obj = &Objects[ID_GUIDE]; @@ -567,7 +569,7 @@ namespace TEN::Entities obj->saveAnim = true; obj->saveFlags = true; obj->meshSwapSlot = ID_MESHSWAP2; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X | ROT_Y; g_Level.Bones[obj->boneIndex + 20 * 4] |= ROT_X | ROT_Y; @@ -589,7 +591,7 @@ namespace TEN::Entities obj->saveAnim = true; obj->saveFlags = true; obj->waterCreature = true; - obj->zoneType = ZONE_WATER; + obj->ZoneType = ZoneType::Water; g_Level.Bones[obj->boneIndex] |= ROT_Y; g_Level.Bones[obj->boneIndex + 7 * 4] |= ROT_Y; @@ -612,7 +614,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveAnim = true; obj->saveFlags = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_HORSE]; @@ -642,7 +644,7 @@ namespace TEN::Entities obj->saveFlags = true; obj->savePosition = true; obj->saveMesh = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_BABOON_NORMAL]; @@ -661,7 +663,7 @@ namespace TEN::Entities obj->saveFlags = true; obj->saveHitpoints = true; obj->savePosition = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_BABOON_INV]; @@ -680,7 +682,7 @@ namespace TEN::Entities obj->saveFlags = true; obj->saveHitpoints = true; obj->savePosition = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; if (Objects[ID_BABOON_NORMAL].loaded) Objects[ID_BABOON_INV].animIndex = Objects[ID_BABOON_NORMAL].animIndex; @@ -702,7 +704,7 @@ namespace TEN::Entities obj->saveFlags = true; obj->saveHitpoints = true; obj->savePosition = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; if (Objects[ID_BABOON_NORMAL].loaded) Objects[ID_BABOON_SILENT].animIndex = Objects[ID_BABOON_NORMAL].animIndex; @@ -725,7 +727,7 @@ namespace TEN::Entities obj->saveMesh = true; obj->savePosition = true; obj->undead = true; - obj->zoneType = ZONE_WATER; + obj->ZoneType = ZoneType::Water; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y | ROT_X; g_Level.Bones[obj->boneIndex + 7 * 4] |= ROT_Y | ROT_X; } @@ -737,7 +739,7 @@ namespace TEN::Entities obj->control = TEN::Entities::TR4::LocustControl; obj->drawRoutine = NULL; obj->saveFlags = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_WRAITH1]; @@ -780,7 +782,7 @@ namespace TEN::Entities obj->control = TEN::Entities::TR4::BeetleSwarmControl; obj->drawRoutine = NULL; obj->saveFlags = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_SAS_DYING]; @@ -793,7 +795,7 @@ namespace TEN::Entities obj->saveFlags = true; obj->savePosition = true; obj->saveAnim = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_SAS_DRAG_BLOKE]; @@ -805,7 +807,7 @@ namespace TEN::Entities obj->saveFlags = true; obj->savePosition = true; obj->saveAnim = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_ENEMY_JEEP]; @@ -823,7 +825,7 @@ namespace TEN::Entities obj->shadowType = ShadowMode::All; obj->radius = 512; obj->HitPoints = 40; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 4 * 8] |= ROT_X; g_Level.Bones[obj->boneIndex + 4 * 9] |= ROT_X; @@ -847,7 +849,7 @@ namespace TEN::Entities obj->savePosition = true; obj->saveHitpoints = true; obj->saveMesh = true; - obj->zoneType = ZONE_HUMAN_LONGJUMP_AND_MONKEY; + obj->ZoneType = ZoneType::HumanLongJumpAndMonkey; g_Level.Bones[obj->boneIndex + 4 * 6] |= ROT_X; g_Level.Bones[obj->boneIndex + 4 * 6] |= ROT_Y; @@ -1176,7 +1178,7 @@ namespace TEN::Entities obj->saveHitpoints = true; obj->saveFlags = true; obj->saveAnim = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; } obj = &Objects[ID_TEETH_SPIKES]; diff --git a/TombEngine/Objects/TR5/Entity/tr5_autoguns.cpp b/TombEngine/Objects/TR5/Entity/tr5_autoguns.cpp index b56f70de7..c11ab4975 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_autoguns.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_autoguns.cpp @@ -1,16 +1,20 @@ #include "framework.h" -#include "tr5_autoguns.h" -#include "Game/collision/sphere.h" -#include "Game/Lara/lara.h" +#include "Objects/TR5/Entity/tr5_autoguns.h" + #include "Game/animation.h" +#include "Game/collision/sphere.h" #include "Game/control/los.h" #include "Game/effects/effects.h" #include "Game/effects/tomb4fx.h" +#include "Game/items.h" +#include "Game/Lara/lara.h" #include "Specific/level.h" #include "Sound/sound.h" -#include "Game/items.h" +#include "Specific/prng.h" -namespace TEN::Entities::TR5 +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR5 { void InitialiseAutoGuns(short itemNumber) { @@ -64,10 +68,10 @@ namespace TEN::Entities::TR5 item->MeshBits = 1664; - GameVector pos1 = { 0, 0, -64 }; + auto pos1 = GameVector(0, 0, -64); GetJointAbsPosition(item, (Vector3Int*)&pos1, 8); - GameVector pos2 = { 0, 0, 0 }; + auto pos2 = GameVector(); GetLaraJointPosition((Vector3Int*)&pos2, 0); pos1.roomNumber = item->RoomNumber; @@ -95,7 +99,7 @@ namespace TEN::Entities::TR5 data[1] = item->ItemFlags[1]; data[2] += item->ItemFlags[2]; - if (abs(angle1) < 1024 && abs(angle2) < 1024 && los) + if (abs(angle1) < ANGLE(5.6f) && abs(angle2) < ANGLE(5.6f) && los) { SoundEffect(SFX_TR4_HK_FIRE, &item->Pose, SoundEnvironment::Land, 0.8f); @@ -105,10 +109,10 @@ namespace TEN::Entities::TR5 TriggerDynamicLight(pos1.x, pos1.y, pos1.z, 10, (GetRandomControl() & 0x1F) + 192, (GetRandomControl() & 0x1F) + 128, 0); - if (GetRandomControl() & 3) + if (TestProbability(0.75f)) { - auto pos2 = Vector3Int(); - GetLaraJointPosition((Vector3Int*)&pos2, GetRandomControl() % 15); + auto pos2 = Vector3Int::Zero; + GetLaraJointPosition(&pos2, GetRandomControl() % 15); DoBloodSplat(pos2.x, pos2.y, pos2.z, (GetRandomControl() & 3) + 3, 2 * GetRandomControl(), LaraItem->RoomNumber); DoDamage(LaraItem, 20); diff --git a/TombEngine/Objects/TR5/Entity/tr5_autoguns.h b/TombEngine/Objects/TR5/Entity/tr5_autoguns.h index d9d344551..3c37772dd 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_autoguns.h +++ b/TombEngine/Objects/TR5/Entity/tr5_autoguns.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseAutoGuns(short itemNumber); void AutoGunsControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_brownbeast.cpp b/TombEngine/Objects/TR5/Entity/tr5_brownbeast.cpp index ebd6f23b6..d71b9241b 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_brownbeast.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_brownbeast.cpp @@ -14,13 +14,16 @@ #include "Specific/level.h" using namespace TEN::Math::Random; +using std::vector; -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { constexpr auto BROWN_BEAST_ATTACK_DAMAGE = 150; const auto BrownBeastBite1 = BiteInfo(Vector3::Zero, 16); const auto BrownBeastBite2 = BiteInfo(Vector3::Zero, 22); + const vector BrownBeastAttackJoints1 = { 14, 15, 16, 17 }; + const vector BrownBeastAttackJoints2 = { 20, 21, 22, 23 }; // TODO enum BrownBeastState @@ -39,11 +42,7 @@ namespace TEN::Entities::TR5 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.TargetState = 1; - item->Animation.ActiveState = 1; + SetAnimation(item, 0); } void ControlBrowsBeast(short itemNumber) @@ -99,6 +98,7 @@ namespace TEN::Entities::TR5 { case 1: creature->Flags = 0; + if (creature->Mood == MoodType::Attack) { if (distance <= pow(SECTOR(1), 2)) @@ -143,7 +143,7 @@ namespace TEN::Entities::TR5 if (creature->Flags) break; - if (item->TouchBits & 0x3C000) + if (item->TestBits(JointBitType::Touch, BrownBeastAttackJoints1)) { if (item->Animation.AnimNumber == Objects[ID_BROWN_BEAST].animIndex + 8) { @@ -157,7 +157,7 @@ namespace TEN::Entities::TR5 } } - if (item->Animation.AnimNumber == Objects[ID_BROWN_BEAST].animIndex + 2) + if (item->Animation.AnimNumber == (Objects[ID_BROWN_BEAST].animIndex + 2)) { if (item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 6 && item->Animation.FrameNumber < g_Level.Anims[item->Animation.AnimNumber].frameBase + 16) @@ -170,13 +170,13 @@ namespace TEN::Entities::TR5 } } - if (!(item->TouchBits & 0xF00000)) + if (!item->TestBits(JointBitType::Touch, BrownBeastAttackJoints2)) break; - if (item->Animation.AnimNumber == Objects[ID_BROWN_BEAST].animIndex + 8) + if (item->Animation.AnimNumber == (Objects[ID_BROWN_BEAST].animIndex + 8)) { - if (item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 13 && - item->Animation.FrameNumber < g_Level.Anims[item->Animation.AnimNumber].frameBase + 20) + if (item->Animation.FrameNumber > (g_Level.Anims[item->Animation.AnimNumber].frameBase + 13) && + item->Animation.FrameNumber < (g_Level.Anims[item->Animation.AnimNumber].frameBase + 20)) { DoDamage(creature->Enemy, BROWN_BEAST_ATTACK_DAMAGE); CreatureEffect2(item, BrownBeastBite2, 20, item->Pose.Orientation.y, DoBloodSplat); @@ -185,10 +185,10 @@ namespace TEN::Entities::TR5 } } - if (item->Animation.AnimNumber == Objects[ID_BROWN_BEAST].animIndex + 2) + if (item->Animation.AnimNumber == (Objects[ID_BROWN_BEAST].animIndex + 2)) { - if (item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 33 && - item->Animation.FrameNumber < g_Level.Anims[item->Animation.AnimNumber].frameBase + 43) + if (item->Animation.FrameNumber > (g_Level.Anims[item->Animation.AnimNumber].frameBase + 33) && + item->Animation.FrameNumber < (g_Level.Anims[item->Animation.AnimNumber].frameBase + 43)) { DoDamage(creature->Enemy, BROWN_BEAST_ATTACK_DAMAGE); CreatureEffect2(item, BrownBeastBite2, 20, item->Pose.Orientation.y, DoBloodSplat); diff --git a/TombEngine/Objects/TR5/Entity/tr5_brownbeast.h b/TombEngine/Objects/TR5/Entity/tr5_brownbeast.h index aad8f18a9..fa5cab857 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_brownbeast.h +++ b/TombEngine/Objects/TR5/Entity/tr5_brownbeast.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseBrownBeast(short itemNumber); void ControlBrowsBeast(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_chef.cpp b/TombEngine/Objects/TR5/Entity/tr5_chef.cpp index d61157779..2085c9a1f 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_chef.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_chef.cpp @@ -1,18 +1,19 @@ #include "framework.h" -#include "tr5_chef.h" +#include "Objects/TR5/Entity/tr5_chef.h" + #include "Game/items.h" #include "Game/control/box.h" #include "Game/effects/effects.h" #include "Game/effects/tomb4fx.h" -#include "Game/people.h" -#include "Specific/setup.h" -#include "Specific/level.h" +#include "Game/itemdata/creature_info.h" #include "Game/Lara/lara.h" #include "Game/misc.h" +#include "Game/people.h" #include "Sound/sound.h" -#include "Game/itemdata/creature_info.h" +#include "Specific/level.h" +#include "Specific/setup.h" -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { const auto ChefBite = BiteInfo(Vector3(0.0f, 200.0f, 0.0f), 13); diff --git a/TombEngine/Objects/TR5/Entity/tr5_chef.h b/TombEngine/Objects/TR5/Entity/tr5_chef.h index 46b2aaaf3..da801ab5d 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_chef.h +++ b/TombEngine/Objects/TR5/Entity/tr5_chef.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseChef(short itemNumber); void ControlChef(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_cyborg.cpp b/TombEngine/Objects/TR5/Entity/tr5_cyborg.cpp index a9cfe4c9a..0c543d0d9 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_cyborg.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_cyborg.cpp @@ -19,7 +19,7 @@ using namespace TEN::Effects::Lara; using namespace TEN::Effects::Lightning; -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { const auto CyborgGunBite = BiteInfo(Vector3(0.0f, 300.0f, 64.0f), 7); byte HitmanJoints[12] = { 15, 14, 13, 6, 5, 12, 7, 4, 10, 11, 19 }; @@ -124,11 +124,7 @@ namespace TEN::Entities::TR5 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 4; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.TargetState = CYBORG_STATE_IDLE; - item->Animation.ActiveState = CYBORG_STATE_IDLE; + SetAnimation(item, CYBORG_ANIM_IDLE); } static void TriggerHitmanSparks(int x, int y, int z, short xv, short yv, short zv) @@ -174,13 +170,13 @@ namespace TEN::Entities::TR5 if (CreatureActive(itemNumber)) { auto* item = &g_Level.Items[itemNumber]; - auto* creature = GetCreatureInfo(item); auto* object = &Objects[item->ObjectNumber]; + auto* creature = GetCreatureInfo(item); short angle = 0; - short joint2 = 0; - short joint1 = 0; short joint0 = 0; + short joint1 = 0; + short joint2 = 0; int x = item->Pose.Position.x; int z = item->Pose.Position.z; diff --git a/TombEngine/Objects/TR5/Entity/tr5_cyborg.h b/TombEngine/Objects/TR5/Entity/tr5_cyborg.h index b81f7a942..80ec07d97 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_cyborg.h +++ b/TombEngine/Objects/TR5/Entity/tr5_cyborg.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseCyborg(short itemNumber); void CyborgControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_doberman.cpp b/TombEngine/Objects/TR5/Entity/tr5_doberman.cpp index 9447b3418..c6e0445db 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_doberman.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_doberman.cpp @@ -1,16 +1,20 @@ #include "framework.h" -#include "tr5_doberman.h" -#include "Game/control/box.h" -#include "Game/effects/effects.h" -#include "Specific/setup.h" -#include "Specific/level.h" -#include "Game/Lara/lara.h" -#include "Game/itemdata/creature_info.h" -#include "Game/control/control.h" -#include "Game/items.h" -#include "Game/misc.h" +#include "Objects/TR5/Entity/tr5_doberman.h" -namespace TEN::Entities::TR5 +#include "Game/control/box.h" +#include "Game/control/control.h" +#include "Game/effects/effects.h" +#include "Game/itemdata/creature_info.h" +#include "Game/items.h" +#include "Game/Lara/lara.h" +#include "Game/misc.h" +#include "Specific/level.h" +#include "Specific/prng.h" +#include "Specific/setup.h" + +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR5 { const auto DobermanBite = BiteInfo(Vector3(0.0f, 30.0f, 141.0f), 20); @@ -75,196 +79,198 @@ namespace TEN::Entities::TR5 void DobermanControl(short itemNumber) { - if (CreatureActive(itemNumber)) + auto* item = &g_Level.Items[itemNumber]; + auto* creature = GetCreatureInfo(item); + + if (!CreatureActive(itemNumber)) + return; + + short angle = 0; + short tilt = 0; + short joint = 0; + + if (item->HitPoints > 0) { - short angle = 0; - short tilt = 0; - short joint = 0; + AI_INFO AI; + CreatureAIInfo(item, &AI); - auto* item = &g_Level.Items[itemNumber]; - auto* creature = GetCreatureInfo(item); + if (AI.ahead) + joint = AI.angle; - if (item->HitPoints > 0) + GetCreatureMood(item, &AI, false); + CreatureMood(item, &AI, false); + + angle = CreatureTurn(item, creature->MaxTurn); + + switch (item->Animation.ActiveState) { - AI_INFO AI; - CreatureAIInfo(item, &AI); + case DOBERMAN_STATE_WALK_FORWARD: + creature->MaxTurn = ANGLE(3.0f); - if (AI.ahead) - joint = AI.angle; - - GetCreatureMood(item, &AI, false); - CreatureMood(item, &AI, false); - - angle = CreatureTurn(item, creature->MaxTurn); - - switch (item->Animation.ActiveState) + if (creature->Mood != MoodType::Bored) + item->Animation.TargetState = DOBERMAN_STATE_RUN_FORWARD; + else { - case DOBERMAN_STATE_WALK_FORWARD: - creature->MaxTurn = ANGLE(3.0f); - - if (creature->Mood != MoodType::Bored) - item->Animation.TargetState = DOBERMAN_STATE_RUN_FORWARD; - else - { - int random = GetRandomControl(); - if (random < 768) - { - item->Animation.RequiredState = DOBERMAN_STATE_STAND_LOW_BITE_ATTACK; - item->Animation.TargetState = DOBERMAN_STATE_STOP; - break; - } - if (random < 1536) - { - item->Animation.RequiredState = DOBERMAN_STATE_SIT_IDLE; - item->Animation.TargetState = DOBERMAN_STATE_STOP; - break; - } - if (random < 2816) - { - item->Animation.TargetState = DOBERMAN_STATE_STOP; - break; - } - } - - break; - - case DOBERMAN_STATE_RUN_FORWARD: - tilt = angle; - creature->MaxTurn = ANGLE(6.0f); - - if (creature->Mood == MoodType::Bored) + if (TestProbability(0.025f)) { + item->Animation.RequiredState = DOBERMAN_STATE_STAND_LOW_BITE_ATTACK; item->Animation.TargetState = DOBERMAN_STATE_STOP; break; } - if (AI.distance < pow(768, 2)) - item->Animation.TargetState = DOBERMAN_STATE_JUMP_BITE_ATTACK; - - break; - - case DOBERMAN_STATE_STOP: - creature->MaxTurn = 0; - creature->Flags = 0; - if (creature->Mood != MoodType::Bored) + if (TestProbability(0.045f)) { - if (creature->Mood != MoodType::Escape && - AI.distance < pow(341, 2) && - AI.ahead) - { - item->Animation.TargetState = DOBERMAN_STATE_STAND_HIGH_BITE_ATTACK; - } - else - item->Animation.TargetState = DOBERMAN_STATE_RUN_FORWARD; - } - else - { - if (item->Animation.RequiredState) - item->Animation.TargetState = item->Animation.RequiredState; - else - { - int random = GetRandomControl(); - if (random >= 768) - { - if (random >= 1536) - { - if (random < 9728) - item->Animation.TargetState = DOBERMAN_STATE_WALK_FORWARD; - } - else - item->Animation.TargetState = DOBERMAN_STATE_SIT_IDLE; - } - else - item->Animation.TargetState = DOBERMAN_STATE_STAND_LOW_BITE_ATTACK; - } - } - break; - - case DOBERMAN_STATE_STAND_LOW_BITE_ATTACK: - if (creature->Mood != MoodType::Bored || GetRandomControl() < 1280) + item->Animation.RequiredState = DOBERMAN_STATE_SIT_IDLE; item->Animation.TargetState = DOBERMAN_STATE_STOP; + break; + } - break; - - case DOBERMAN_STATE_SIT_IDLE: - if (creature->Mood != MoodType::Bored || GetRandomControl() < 256) + if (TestProbability(0.085f)) + { item->Animation.TargetState = DOBERMAN_STATE_STOP; - - break; - - case DOBERMAN_STATE_STAND_IDLE: - if (creature->Mood != MoodType::Bored || GetRandomControl() < 512) - item->Animation.TargetState = DOBERMAN_STATE_STOP; - - break; - - case DOBERMAN_STATE_STAND_HIGH_BITE_ATTACK: - creature->MaxTurn = ANGLE(0.5f); - - if (creature->Flags != 1 && - AI.ahead && - item->TouchBits & 0x122000) - { - DoDamage(creature->Enemy, 30); - CreatureEffect(item, DobermanBite, DoBloodSplat); - creature->Flags = 1; + break; } + } - if (AI.distance <= pow(341, 2) || AI.distance >= pow(682, 2)) - item->Animation.TargetState = DOBERMAN_STATE_STOP; - else - item->Animation.TargetState = DOBERMAN_STATE_LEAP_BITE_ATTACK; + break; - break; + case DOBERMAN_STATE_RUN_FORWARD: + creature->MaxTurn = ANGLE(6.0f); + tilt = angle; - case DOBERMAN_STATE_JUMP_BITE_ATTACK: - if (creature->Flags != 2 && item->TouchBits & 0x122000) - { - DoDamage(creature->Enemy, 80); - CreatureEffect(item, DobermanBite, DoBloodSplat); - creature->Flags = 2; - } - - if (AI.distance >= pow(341, 2)) - { - if (AI.distance < pow(682, 2)) - item->Animation.TargetState = DOBERMAN_STATE_LEAP_BITE_ATTACK; - } - else - item->Animation.TargetState = DOBERMAN_STATE_STAND_HIGH_BITE_ATTACK; - - break; - - case DOBERMAN_STATE_LEAP_BITE_ATTACK: - creature->MaxTurn = ANGLE(6.0f); - - if (creature->Flags != 3 && item->TouchBits & 0x122000) - { - DoDamage(creature->Enemy, 50); - CreatureEffect(item, DobermanBite, DoBloodSplat); - creature->Flags = 3; - } - if (AI.distance < pow(341, 2)) - item->Animation.TargetState = DOBERMAN_STATE_STAND_HIGH_BITE_ATTACK; - - break; - - default: + if (creature->Mood == MoodType::Bored) + { + item->Animation.TargetState = DOBERMAN_STATE_STOP; break; } - } - else if (item->Animation.ActiveState != DOBERMAN_STATE_DEATH) - { - item->Animation.AnimNumber = Objects[ID_DOBERMAN].animIndex + DOBERMAN_ANIM_DEATH; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = DOBERMAN_STATE_DEATH; - } - CreatureTilt(item, tilt); - CreatureJoint(item, 0, 0); - CreatureJoint(item, 1, joint); - CreatureJoint(item, 2, 0); - CreatureAnimation(itemNumber, angle, tilt); + if (AI.distance < pow(768, 2)) + item->Animation.TargetState = DOBERMAN_STATE_JUMP_BITE_ATTACK; + + break; + + case DOBERMAN_STATE_STOP: + creature->MaxTurn = 0; + creature->Flags = 0; + + if (creature->Mood != MoodType::Bored) + { + if (creature->Mood != MoodType::Escape && + AI.distance < pow(341, 2) && + AI.ahead) + { + item->Animation.TargetState = DOBERMAN_STATE_STAND_HIGH_BITE_ATTACK; + } + else + item->Animation.TargetState = DOBERMAN_STATE_RUN_FORWARD; + } + else + { + if (item->Animation.RequiredState) + item->Animation.TargetState = item->Animation.RequiredState; + else + { + if (TestProbability(0.975f)) + { + if (TestProbability(0.95f)) + { + if (TestProbability(0.3f)) + item->Animation.TargetState = DOBERMAN_STATE_WALK_FORWARD; + } + else + item->Animation.TargetState = DOBERMAN_STATE_SIT_IDLE; + } + else + item->Animation.TargetState = DOBERMAN_STATE_STAND_LOW_BITE_ATTACK; + } + } + + break; + + case DOBERMAN_STATE_STAND_LOW_BITE_ATTACK: + if (creature->Mood != MoodType::Bored || TestProbability(0.04f)) + item->Animation.TargetState = DOBERMAN_STATE_STOP; + + break; + + case DOBERMAN_STATE_SIT_IDLE: + if (creature->Mood != MoodType::Bored || TestProbability(1.0f / 128)) + item->Animation.TargetState = DOBERMAN_STATE_STOP; + + break; + + case DOBERMAN_STATE_STAND_IDLE: + if (creature->Mood != MoodType::Bored || TestProbability(1.0f / 64)) + item->Animation.TargetState = DOBERMAN_STATE_STOP; + + break; + + case DOBERMAN_STATE_STAND_HIGH_BITE_ATTACK: + creature->MaxTurn = ANGLE(0.5f); + + if (creature->Flags != 1 && AI.ahead && + item->TouchBits & 0x122000) + { + DoDamage(creature->Enemy, 30); + CreatureEffect(item, DobermanBite, DoBloodSplat); + creature->Flags = 1; + } + + if (AI.distance <= pow(341, 2) || AI.distance >= pow(682, 2)) + item->Animation.TargetState = DOBERMAN_STATE_STOP; + else + item->Animation.TargetState = DOBERMAN_STATE_LEAP_BITE_ATTACK; + + break; + + case DOBERMAN_STATE_JUMP_BITE_ATTACK: + if (creature->Flags != 2 && item->TouchBits & 0x122000) + { + DoDamage(creature->Enemy, 80); + CreatureEffect(item, DobermanBite, DoBloodSplat); + creature->Flags = 2; + } + + if (AI.distance >= pow(341, 2)) + { + if (AI.distance < pow(682, 2)) + item->Animation.TargetState = DOBERMAN_STATE_LEAP_BITE_ATTACK; + } + else + item->Animation.TargetState = DOBERMAN_STATE_STAND_HIGH_BITE_ATTACK; + + break; + + case DOBERMAN_STATE_LEAP_BITE_ATTACK: + creature->MaxTurn = ANGLE(6.0f); + + if (creature->Flags != 3 && item->TouchBits & 0x122000) + { + DoDamage(creature->Enemy, 50); + CreatureEffect(item, DobermanBite, DoBloodSplat); + creature->Flags = 3; + } + + if (AI.distance < pow(341, 2)) + item->Animation.TargetState = DOBERMAN_STATE_STAND_HIGH_BITE_ATTACK; + + break; + + default: + break; + } } + else if (item->Animation.ActiveState != DOBERMAN_STATE_DEATH) + { + item->Animation.AnimNumber = Objects[ID_DOBERMAN].animIndex + DOBERMAN_ANIM_DEATH; + item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; + item->Animation.ActiveState = DOBERMAN_STATE_DEATH; + } + + CreatureTilt(item, tilt); + CreatureJoint(item, 0, 0); + CreatureJoint(item, 1, joint); + CreatureJoint(item, 2, 0); + CreatureAnimation(itemNumber, angle, tilt); } } diff --git a/TombEngine/Objects/TR5/Entity/tr5_doberman.h b/TombEngine/Objects/TR5/Entity/tr5_doberman.h index 2edd5c50b..bba3b5fb1 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_doberman.h +++ b/TombEngine/Objects/TR5/Entity/tr5_doberman.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseDoberman(short itemNumber); void DobermanControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_dog.cpp b/TombEngine/Objects/TR5/Entity/tr5_dog.cpp index 6fa38a571..dec3abace 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_dog.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_dog.cpp @@ -11,7 +11,7 @@ #include "Game/items.h" #include "Game/misc.h" -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { const auto DogBite = BiteInfo(Vector3(0.0f, 0.0f, 100.0f), 3); static BYTE DogAnims[] = { 20, 21, 22, 20 }; diff --git a/TombEngine/Objects/TR5/Entity/tr5_dog.h b/TombEngine/Objects/TR5/Entity/tr5_dog.h index 3021da3b5..82e278d2c 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_dog.h +++ b/TombEngine/Objects/TR5/Entity/tr5_dog.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseTr5Dog(short itemNumber); void Tr5DogControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_ghost.cpp b/TombEngine/Objects/TR5/Entity/tr5_ghost.cpp index af812e63f..52b543a91 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_ghost.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_ghost.cpp @@ -10,7 +10,7 @@ #include "Sound/sound.h" #include "Game/itemdata/creature_info.h" -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { const auto InvisibleGhostBite = BiteInfo(Vector3::Zero, 17); diff --git a/TombEngine/Objects/TR5/Entity/tr5_ghost.h b/TombEngine/Objects/TR5/Entity/tr5_ghost.h index 40ef99ba3..62ccb4596 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_ghost.h +++ b/TombEngine/Objects/TR5/Entity/tr5_ghost.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseInvisibleGhost(short itemNumber); void InvisibleGhostControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp b/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp index abe2c8b45..2f7a69a98 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_gladiator.cpp @@ -18,14 +18,14 @@ using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { constexpr auto GLADIATOR_ATTACK_DAMAGE = 120; // TODO: Ranges. - const vector GladiatorAttackJoints = { 13, 14 }; const auto GladiatorBite = BiteInfo(Vector3::Zero, 16); + const vector GladiatorAttackJoints = { 13, 14 }; enum GladiatorState { @@ -88,8 +88,8 @@ namespace TEN::Entities::TR5 auto* item = &g_Level.Items[itemNumber]; auto* creature = GetCreatureInfo(item); - short tilt = 0; short angle = 0; + short tilt = 0; short joint0 = 0; short joint1 = 0; short joint2 = 0; @@ -99,11 +99,7 @@ namespace TEN::Entities::TR5 item->HitPoints = 0; if (item->Animation.ActiveState != GLADIATOR_STATE_DEATH) - { - item->Animation.AnimNumber = Objects[ID_GLADIATOR].animIndex + GLADIATOR_ANIM_DEATH; - item->Animation.ActiveState = GLADIATOR_STATE_DEATH; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - } + SetAnimation(item, GLADIATOR_ANIM_DEATH); } else { @@ -116,21 +112,21 @@ namespace TEN::Entities::TR5 CreatureAIInfo(item, &AI); int unknown = true; - short rot; + short deltaAngle; int distance; if (creature->Enemy == LaraItem) { distance = AI.distance; - rot = AI.angle; + deltaAngle = AI.angle; } else { int dx = LaraItem->Pose.Position.x - item->Pose.Position.x; int dz = LaraItem->Pose.Position.z - item->Pose.Position.z; - rot = phd_atan(dz, dx) - item->Pose.Orientation.y; - if (rot <= -ANGLE(90.0f) || rot >= ANGLE(90.0f)) + deltaAngle = phd_atan(dz, dx) - item->Pose.Orientation.y; + if (deltaAngle <= -ANGLE(90.0f) || deltaAngle >= ANGLE(90.0f)) unknown = false; distance = pow(dx, 2) + pow(dz, 2); @@ -151,12 +147,12 @@ namespace TEN::Entities::TR5 switch (item->Animation.ActiveState) { case GLADIATOR_STATE_IDLE: - joint2 = rot; + joint2 = deltaAngle; creature->MaxTurn = (-(int)(creature->Mood != MoodType::Bored)) & 0x16C; creature->Flags = 0; if (item->AIBits & GUARD || - !(GetRandomControl() & 0x1F) && + TestProbability(1.0f / 30) && (AI.distance > pow(SECTOR(1), 2) || creature->Mood != MoodType::Attack)) { joint2 = AIGuard(creature); @@ -169,8 +165,7 @@ namespace TEN::Entities::TR5 { if (creature->Mood == MoodType::Escape) { - if (Lara.TargetEntity != item && - AI.ahead && !item->HitStatus) + if (Lara.TargetEntity != item && AI.ahead && !item->HitStatus) { item->Animation.TargetState = GLADIATOR_STATE_IDLE; break; @@ -179,13 +174,11 @@ namespace TEN::Entities::TR5 else { if (creature->Mood == MoodType::Bored || - item->AIBits & FOLLOW && - (creature->ReachedGoal || - distance > pow(SECTOR(2), 2))) + (item->AIBits & FOLLOW && (creature->ReachedGoal || distance > pow(SECTOR(2), 2)))) { if (item->Animation.RequiredState) item->Animation.TargetState = item->Animation.RequiredState; - else if (!(GetRandomControl() & 0x3F)) + else if (TestProbability(1.0f / 64)) item->Animation.TargetState = GLADIATOR_STATE_IDLE; break; @@ -194,7 +187,7 @@ namespace TEN::Entities::TR5 if (Lara.TargetEntity == item && unknown && distance < pow(SECTOR(1.5f), 2) && TestProbability(0.5f) && - (Lara.Control.Weapon.GunType == LaraWeaponType::Shotgun || !(GetRandomControl() & 0xF)) && + (Lara.Control.Weapon.GunType == LaraWeaponType::Shotgun || TestProbability(0.06f)) && item->MeshBits == -1) { item->Animation.TargetState = GLADIATOR_STATE_GUARD_START; @@ -218,7 +211,7 @@ namespace TEN::Entities::TR5 break; case GLADIATOR_STATE_WALK_FORWARD: - joint2 = rot; + joint2 = deltaAngle; creature->MaxTurn = creature->Mood != MoodType::Bored ? ANGLE(7.0f) : ANGLE(2.0f); creature->Flags = 0; @@ -242,7 +235,7 @@ namespace TEN::Entities::TR5 else if (!AI.ahead || AI.distance > pow(SECTOR(1.5f), 2)) item->Animation.TargetState = GLADIATOR_STATE_RUN_FORWARD; } - else if (!(GetRandomControl() & 0x3F)) + else if (TestProbability(1.0f / 64)) { item->Animation.TargetState = GLADIATOR_STATE_IDLE; break; @@ -308,8 +301,7 @@ namespace TEN::Entities::TR5 break; } } - else if (Lara.TargetEntity != item || - !(GetRandomControl() & 0x7F)) + else if (Lara.TargetEntity != item || TestProbability(1.0f / 128)) { item->Animation.TargetState = GLADIATOR_STATE_IDLE; break; @@ -343,7 +335,7 @@ namespace TEN::Entities::TR5 { auto* room = &g_Level.Rooms[item->RoomNumber]; - auto pos = Vector3Int(); + auto pos = Vector3Int::Zero; GetJointAbsPosition(item, &pos, 16); auto* floor = GetSector(room, pos.x - room->x, pos.z - room->z); @@ -361,7 +353,6 @@ namespace TEN::Entities::TR5 { ShatterObject(0, mesh, -64, LaraItem->RoomNumber, 0); //SoundEffect(ShatterSounds[gfCurrentLevel - 5][*(v28 + 18)], v28); - mesh->flags &= ~StaticMeshFlags::SM_VISIBLE; TestTriggers(pos.x, pos.y, pos.z, item->RoomNumber, true); } diff --git a/TombEngine/Objects/TR5/Entity/tr5_gladiator.h b/TombEngine/Objects/TR5/Entity/tr5_gladiator.h index a28b30371..752637ef9 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_gladiator.h +++ b/TombEngine/Objects/TR5/Entity/tr5_gladiator.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseGladiator(short itemNumber); void ControlGladiator(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_guard.cpp b/TombEngine/Objects/TR5/Entity/tr5_guard.cpp index 33f3b89f9..1d22a8734 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_guard.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_guard.cpp @@ -1,21 +1,25 @@ #include "framework.h" -#include "tr5_guard.h" -#include "Game/items.h" +#include "Objects/TR5/Entity/tr5_guard.h" + +#include "Game/animation.h" #include "Game/collision/collide_room.h" #include "Game/control/box.h" -#include "Game/people.h" +#include "Game/control/los.h" #include "Game/effects/effects.h" #include "Game/effects/tomb4fx.h" -#include "Game/control/los.h" -#include "Specific/setup.h" -#include "Game/animation.h" -#include "Specific/level.h" +#include "Game/itemdata/creature_info.h" +#include "Game/items.h" #include "Game/Lara/lara.h" #include "Game/misc.h" +#include "Game/people.h" #include "Sound/sound.h" -#include "Game/itemdata/creature_info.h" +#include "Specific/level.h" +#include "Specific/prng.h" +#include "Specific/setup.h" -namespace TEN::Entities::TR5 +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR5 { const auto SwatGunBite = BiteInfo(Vector3(80.0f, 200.0f, 13.0f), 0); const auto SniperGunBite = BiteInfo(Vector3(0.0f, 480.0f, 110.0f), 13); @@ -354,7 +358,7 @@ namespace TEN::Entities::TR5 if (item->ObjectNumber == ID_SWAT_PLUS) { item->ItemFlags[0]++; - if (item->ItemFlags[0] > 60 && !(GetRandomControl() & 0xF)) + if (item->ItemFlags[0] > 60 && TestProbability(0.06f)) { SoundEffect(SFX_TR5_BIO_BREATHE_OUT, &item->Pose); item->ItemFlags[0] = 0; @@ -839,7 +843,7 @@ namespace TEN::Entities::TR5 case GUARD_STATE_USE_COMPUTER: if ((item->ObjectNumber != ID_SCIENTIST || item != Lara.TargetEntity) && - (GetRandomControl() & 0x7F || item->TriggerFlags >= 10 || item->TriggerFlags == 9)) + (TestProbability(0.992f) || item->TriggerFlags >= 10 || item->TriggerFlags == 9)) { if (item->AIBits & GUARD) { @@ -859,7 +863,7 @@ namespace TEN::Entities::TR5 break; case GUARD_STATE_SURRENDER: - if (item != Lara.TargetEntity && !(GetRandomControl() & 0x3F)) + if (item != Lara.TargetEntity && TestProbability(1.0f / 64)) { if (item->TriggerFlags == 7 || item->TriggerFlags == 9) item->Animation.RequiredState = GUARD_STATE_USE_COMPUTER; @@ -1044,11 +1048,11 @@ namespace TEN::Entities::TR5 creature->Flags = 0; if (!TargetVisible(item, &AI) || item->HitStatus && - GetRandomControl() & 1) + TestProbability(0.5f)) { item->Animation.TargetState = SNIPER_STATE_COVER; } - else if (!(GetRandomControl() & 0x1F)) + else if (TestProbability(1.0f / 30)) item->Animation.TargetState = SNIPER_STATE_FIRE; break; diff --git a/TombEngine/Objects/TR5/Entity/tr5_guard.h b/TombEngine/Objects/TR5/Entity/tr5_guard.h index 847958397..68aa3e723 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_guard.h +++ b/TombEngine/Objects/TR5/Entity/tr5_guard.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseGuard(short itemNumber); void GuardControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp b/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp index 782e180bf..9796e2a0a 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_gunship.cpp @@ -12,7 +12,7 @@ #include "Game/items.h" #include "Game/Lara/lara.h" -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { int GunShipCounter = 0; @@ -107,7 +107,6 @@ namespace TEN::Entities::TR5 if (StaticObjects[hitMesh->staticNumber].shatterType != SHT_NONE) { ShatterObject(0, hitMesh, 64, end.roomNumber, 0); - hitMesh->flags &= ~StaticMeshFlags::SM_VISIBLE; TestTriggers(hitMesh->pos.Position.x, hitMesh->pos.Position.y, hitMesh->pos.Position.z, end.roomNumber, true); SoundEffect(GetShatterSound(hitMesh->staticNumber), &hitMesh->pos); } diff --git a/TombEngine/Objects/TR5/Entity/tr5_gunship.h b/TombEngine/Objects/TR5/Entity/tr5_gunship.h index 60ba90808..9f862512a 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_gunship.h +++ b/TombEngine/Objects/TR5/Entity/tr5_gunship.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void ControlGunShip(short itemNumber); } diff --git a/TombEngine/Objects/TR5/Entity/tr5_hydra.cpp b/TombEngine/Objects/TR5/Entity/tr5_hydra.cpp index e04af88b4..05e51a6ff 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_hydra.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_hydra.cpp @@ -1,20 +1,23 @@ #include "framework.h" -#include "tr5_hydra.h" +#include "Objects/TR5/Entity/tr5_hydra.h" -#include "Game/items.h" +#include "Game/Lara/lara.h" +#include "Game/animation.h" #include "Game/collision/collide_room.h" #include "Game/control/box.h" #include "Game/effects/debris.h" #include "Game/effects/effects.h" -#include "Specific/setup.h" -#include "Game/animation.h" -#include "Specific/level.h" -#include "Game/Lara/lara.h" +#include "Game/itemdata/creature_info.h" +#include "Game/items.h" #include "Game/misc.h" #include "Sound/sound.h" -#include "Game/itemdata/creature_info.h" +#include "Specific/level.h" +#include "Specific/prng.h" +#include "Specific/setup.h" -namespace TEN::Entities::TR5 +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR5 { const auto HydraBite = BiteInfo(Vector3::Zero, 11); @@ -103,7 +106,7 @@ namespace TEN::Entities::TR5 spark->flags = SP_EXPDEF | SP_ROTATE | SP_DEF | SP_SCALE; spark->rotAng = GetRandomControl() & 0xFFF; - if (GetRandomControl() & 1) + if (TestProbability(0.5f)) spark->rotAdd = -32 - (GetRandomControl() & 0x1F); else spark->rotAdd = (GetRandomControl() & 0x1F) + 32; @@ -227,11 +230,11 @@ namespace TEN::Entities::TR5 else if (item->TriggerFlags == 2) tilt = ANGLE(2.8f); - if (AI.distance >= pow(CLICK(7), 2) && GetRandomControl() & 0x1F) + if (AI.distance >= pow(CLICK(7), 2) && TestProbability(0.97f)) { - if (AI.distance >= pow(SECTOR(2), 2) && GetRandomControl() & 0x1F) + if (AI.distance >= pow(SECTOR(2), 2) && TestProbability(0.97f)) { - if (!(GetRandomControl() & 0xF)) + if (TestProbability(0.06f)) item->Animation.TargetState = HYDRA_STATE_AIM; } else @@ -290,7 +293,8 @@ namespace TEN::Entities::TR5 if (Lara.Control.Weapon.GunType == LaraWeaponType::Shotgun) damage *= 3; - if ((GetRandomControl() & 0xF) < damage && AI.distance < SQUARE(10240) && damage > 0) + if ((GetRandomControl() & 0xF) < damage && + AI.distance < SQUARE(SECTOR(10)) && damage > 0) { item->Animation.TargetState = 4; DoDamage(item, damage); diff --git a/TombEngine/Objects/TR5/Entity/tr5_hydra.h b/TombEngine/Objects/TR5/Entity/tr5_hydra.h index 0567421b4..324ca600d 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_hydra.h +++ b/TombEngine/Objects/TR5/Entity/tr5_hydra.h @@ -1,7 +1,7 @@ #pragma once #include "Specific/phd_global.h" -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseHydra(short itemNumber); void HydraControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_imp.cpp b/TombEngine/Objects/TR5/Entity/tr5_imp.cpp index 63f206f68..51ba31d45 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_imp.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_imp.cpp @@ -14,7 +14,7 @@ using namespace TEN::Entities::Generic; -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { const auto ImpBite = BiteInfo(Vector3(0.0f, 100.0f, 0.0f), 9); diff --git a/TombEngine/Objects/TR5/Entity/tr5_imp.h b/TombEngine/Objects/TR5/Entity/tr5_imp.h index f32d76ab6..2b3f5882e 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_imp.h +++ b/TombEngine/Objects/TR5/Entity/tr5_imp.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseImp(short itemNumber); void ImpControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_lagoon_witch.cpp b/TombEngine/Objects/TR5/Entity/tr5_lagoon_witch.cpp index 1cea7a9f8..e73250c6f 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_lagoon_witch.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_lagoon_witch.cpp @@ -1,19 +1,25 @@ #include "framework.h" -#include "tr5_lagoon_witch.h" -#include "Game/items.h" +#include "Objects/TR5/Entity/tr5_lagoon_witch.h" + #include "Game/control/box.h" #include "Game/effects/effects.h" #include "Game/effects/tomb4fx.h" #include "Game/gui.h" -#include "Specific/setup.h" -#include "Specific/level.h" -#include "Game/Lara/lara.h" #include "Game/itemdata/creature_info.h" +#include "Game/items.h" +#include "Game/Lara/lara.h" #include "Game/misc.h" +#include "Specific/level.h" +#include "Specific/setup.h" -namespace TEN::Entities::TR5 +using std::vector; + +namespace TEN::Entities::Creatures::TR5 { + constexpr auto LAGOON_WITCH_ATTACK_DAMAGE = 100; + const auto LagoonWitchBite = BiteInfo(Vector3::Zero, 7); + const vector LagoonWitchAttackJoints = { 6, 7, 8, 9, 14, 15, 16, 17 }; enum LagoonWitchState { @@ -34,11 +40,7 @@ namespace TEN::Entities::TR5 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex + 1; - item->Animation.TargetState = WITCH_STATE_IDLE; - item->Animation.ActiveState = WITCH_STATE_IDLE; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; + SetAnimation(item, 1); item->Pose.Position.y += CLICK(2); } @@ -53,17 +55,17 @@ namespace TEN::Entities::TR5 short joint2 = 0; auto* item = &g_Level.Items[itemNumber]; - auto* creature = GetCreatureInfo(item); auto* object = &Objects[item->ObjectNumber]; + auto* creature = GetCreatureInfo(item); if (item->HitPoints <= 0) { if (item->Animation.ActiveState != WITCH_STATE_DEATH) { - item->HitPoints = 0; item->Animation.ActiveState = WITCH_STATE_DEATH; item->Animation.AnimNumber = object->animIndex + WITCH_ANIM_DEATH; item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; + item->HitPoints = 0; } } else @@ -126,10 +128,10 @@ namespace TEN::Entities::TR5 creature->MaxTurn = ANGLE(2.0f); if (!creature->Flags && - item->TouchBits & 0x3C3C0 && + item->TestBits(JointBitType::Touch, LagoonWitchAttackJoints) && item->Animation.FrameNumber > g_Level.Anims[item->Animation.AnimNumber].frameBase + 29) { - DoDamage(creature->Enemy, 100); + DoDamage(creature->Enemy, LAGOON_WITCH_ATTACK_DAMAGE); CreatureEffect2(item, LagoonWitchBite, 10, item->Pose.Orientation.y, DoBloodSplat); creature->Flags = WITCH_STATE_SWIM; } @@ -148,7 +150,7 @@ namespace TEN::Entities::TR5 item->ItemFlags[3]++; creature->ReachedGoal = false; - creature->Enemy = 0; + creature->Enemy = nullptr; } } } diff --git a/TombEngine/Objects/TR5/Entity/tr5_lagoon_witch.h b/TombEngine/Objects/TR5/Entity/tr5_lagoon_witch.h index f4808d67e..b43431ba4 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_lagoon_witch.h +++ b/TombEngine/Objects/TR5/Entity/tr5_lagoon_witch.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseLagoonWitch(short itemNumber); void LagoonWitchControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_larson.cpp b/TombEngine/Objects/TR5/Entity/tr5_larson.cpp index 5b5f28a08..a7a5c964a 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_larson.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_larson.cpp @@ -1,45 +1,46 @@ #include "framework.h" -#include "tr5_larson.h" -#include "Game/items.h" -#include "Game/control/box.h" -#include "Game/effects/effects.h" -#include "Game/people.h" -#include "Game/Lara/lara.h" -#include "Specific/setup.h" -#include "Specific/level.h" -#include "Game/itemdata/creature_info.h" -#include "Game/control/control.h" +#include "Objects/TR5/Entity/tr5_larson.h" + #include "Game/animation.h" +#include "Game/control/box.h" +#include "Game/control/control.h" +#include "Game/effects/effects.h" +#include "Game/itemdata/creature_info.h" +#include "Game/items.h" +#include "Game/Lara/lara.h" +#include "Game/misc.h" +#include "Game/people.h" +#include "Specific/level.h" +#include "Specific/prng.h" +#include "Specific/setup.h" -namespace TEN::Entities::TR5 +using namespace TEN::Math::Random; + +namespace TEN::Entities::Creatures::TR5 { -#define STATE_TR5_LARSON_STOP 1 -#define STATE_TR5_LARSON_WALK 2 -#define STATE_TR5_LARSON_RUN 3 -#define STATE_TR5_LARSON_AIM 4 -#define STATE_TR5_LARSON_DIE 5 -#define STATE_TR5_LARSON_IDLE 6 -#define STATE_TR5_LARSON_ATTACK 7 + #define STATE_TR5_LARSON_STOP 1 + #define STATE_TR5_LARSON_WALK 2 + #define STATE_TR5_LARSON_RUN 3 + #define STATE_TR5_LARSON_AIM 4 + #define STATE_TR5_LARSON_DIE 5 + #define STATE_TR5_LARSON_IDLE 6 + #define STATE_TR5_LARSON_ATTACK 7 -#define ANIMATION_TR5_PIERRE_DIE 12 -#define ANIMATION_TR5_LARSON_DIE 15 + #define ANIMATION_TR5_PIERRE_DIE 12 + #define ANIMATION_TR5_LARSON_DIE 15 -#define TR5_LARSON_MIN_HP 40 + #define TR5_LARSON_MIN_HP 40 - const auto LarsonGun = BiteInfo(Vector3(-55, 200, 5), 14); - const auto PierreGun1 = BiteInfo(Vector3(60, 200, 0), 11); - const auto PierreGun2 = BiteInfo(Vector3(-57, 200, 0), 14); + const auto LarsonGun = BiteInfo(Vector3(-55.0f, 200.0f, 5.0f), 14); + const auto PierreGun1 = BiteInfo(Vector3(60.0f, 200.0f, 0.0f), 11); + const auto PierreGun2 = BiteInfo(Vector3(-57.0f, 200.0f, 0.0f), 14); - void InitialiseLarson(short itemNum) + void InitialiseLarson(short itemNumber) { - ItemInfo* item = &g_Level.Items[itemNum]; + auto* item = &g_Level.Items[itemNumber]; - ClearItem(itemNum); - - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.TargetState = STATE_TR5_LARSON_STOP; - item->Animation.ActiveState = STATE_TR5_LARSON_STOP; + ClearItem(itemNumber); + SetAnimation(item, 0); if (!item->TriggerFlags) return; @@ -47,22 +48,14 @@ namespace TEN::Entities::TR5 item->ItemFlags[3] = item->TriggerFlags; short rotY = item->Pose.Orientation.y; - if (rotY > ANGLE(22.5f) && rotY < 28672) - { + if (rotY > ANGLE(22.5f) && rotY < ANGLE(157.5f)) item->Pose.Position.x += STEPUP_HEIGHT; - } - else if (rotY < -ANGLE(22.5f) && rotY > -28672) - { + else if (rotY < ANGLE(-22.5f) && rotY > ANGLE(-157.5f)) item->Pose.Position.x -= STEPUP_HEIGHT; - } - else if (rotY < -20480 || rotY > 20480) - { + else if (rotY < ANGLE(-112.5f) || rotY > ANGLE(112.5f)) item->Pose.Position.z -= STEPUP_HEIGHT; - } - else if (rotY > -8192 || rotY < 8192) - { + else if (rotY > ANGLE(-45.0f) || rotY < ANGLE(45.0f)) item->Pose.Position.z += STEPUP_HEIGHT; - } } void LarsonControl(short itemNumber) @@ -70,23 +63,23 @@ namespace TEN::Entities::TR5 if (!CreatureActive(itemNumber)) return; - short tilt = 0; + auto* item = &g_Level.Items[itemNumber]; + auto* creature = GetCreatureInfo(item); + short angle = 0; + short tilt = 0; short joint0 = 0; short joint1 = 0; short joint2 = 0; - auto* item = &g_Level.Items[itemNumber]; - CreatureInfo* creature = (CreatureInfo*)item->Data; - - // In Streets of Rome when Larson HP are below 40 he runs way + // TODO: When Larson's HP is below 40, he runs away in Streets of Rome. Keeping block commented for reference. /*if (item->HitPoints <= TR5_LARSON_MIN_HP && !(item->flags & IFLAG_INVISIBLE)) { item->HitPoints = TR5_LARSON_MIN_HP; creature->flags++; }*/ - // Fire weapon effects + // Fire weapon effects. if (creature->FiredWeapon) { auto pos = Vector3Int(LarsonGun.Position); @@ -100,19 +93,20 @@ namespace TEN::Entities::TR5 { if (CurrentLevel == 2) { - item->ItemFlags[3] = 1; item->Animation.IsAirborne = false; - item->HitStatus = false; - item->Collidable = false; item->Status = ITEM_DEACTIVATED; + item->Collidable = false; + item->HitStatus = false; + item->ItemFlags[3] = 1; } else { item->Animation.IsAirborne = false; - item->HitStatus = false; - item->Collidable = false; item->Status = ITEM_ACTIVE; + item->Collidable = false; + item->HitStatus = false; } + item->TriggerFlags = 0; } @@ -123,30 +117,30 @@ namespace TEN::Entities::TR5 else creature->Enemy = LaraItem; - AI_INFO info; - CreatureAIInfo(item, &info); + AI_INFO AI; + CreatureAIInfo(item, &AI); - if (info.ahead) - joint2 = info.angle; + if (AI.ahead) + joint2 = AI.angle; - // FIXME: this should make Larson running away, but it's broken + // FIXME: This should make Larson run away, but it doesn't work. /*if (creature->flags) { item->HitPoints = 60; item->IsAirborne = false; - item->hitStatus = false; - item->collidable = false; - item->status = ITEM_DESACTIVATED; - creature->flags = 0; + item->HitStatus = false; + item->Collidable = false; + item->Status = ITEM_DESACTIVATED; + creature->Flags = 0; }*/ - GetCreatureMood(item, &info, true); - CreatureMood(item, &info, true); + GetCreatureMood(item, &AI, true); + CreatureMood(item, &AI, true); - if (info.distance < SQUARE(2048) - && LaraItem->Animation.Velocity.z > 20 - || item->HitStatus - || TargetVisible(item, &info) != 0) + if (AI.distance < SQUARE(SECTOR(2)) && + LaraItem->Animation.Velocity.z > 20.0f || + item->HitStatus || + TargetVisible(item, &AI) != 0) { item->Status &= ~ITEM_ACTIVE; creature->Alerted = true; @@ -157,40 +151,34 @@ namespace TEN::Entities::TR5 switch (item->Animation.ActiveState) { case STATE_TR5_LARSON_STOP: - joint0 = info.angle / 2; - joint2 = info.angle / 2; - if (info.ahead) - joint1 = info.xAngle; + joint0 = AI.angle / 2; + joint2 = AI.angle / 2; + + if (AI.ahead) + joint1 = AI.xAngle; if (item->Animation.RequiredState) - { item->Animation.TargetState = item->Animation.RequiredState; - } else if (item->AIBits & AMBUSH) - { item->Animation.TargetState = STATE_TR5_LARSON_RUN; - } - else if (Targetable(item, &info)) - { + else if (Targetable(item, &AI)) item->Animation.TargetState = STATE_TR5_LARSON_AIM; - } else { if (item->AIBits & GUARD || CurrentLevel == 2 || item->ItemFlags[3]) { - creature->MaxTurn = 0; item->Animation.TargetState = STATE_TR5_LARSON_STOP; - if (abs(info.angle) >= ANGLE(2)) + creature->MaxTurn = 0; + + if (abs(AI.angle) >= ANGLE(2.0f)) { - if (info.angle > 0) - item->Pose.Orientation.y += ANGLE(2); + if (AI.angle > 0) + item->Pose.Orientation.y += ANGLE(2.0f); else - item->Pose.Orientation.y -= ANGLE(2); + item->Pose.Orientation.y -= ANGLE(2.0f); } else - { - item->Pose.Orientation.y += info.angle; - } + item->Pose.Orientation.y += AI.angle; } else { @@ -202,175 +190,177 @@ namespace TEN::Entities::TR5 item->Animation.TargetState = STATE_TR5_LARSON_WALK; } else - { - item->Animation.TargetState = GetRandomControl() >= 96 ? 2 : 6; - } + item->Animation.TargetState = TestProbability(0.997f) ? 2 : 6; } } + break; case STATE_TR5_LARSON_WALK: - if (info.ahead) - joint2 = info.angle; + creature->MaxTurn = ANGLE(7.0f); - creature->MaxTurn = ANGLE(7); - if (creature->Mood == MoodType::Bored && GetRandomControl() < 96) + if (AI.ahead) + joint2 = AI.angle; + + if (creature->Mood == MoodType::Bored && TestProbability(1.0f / 340)) { - item->Animation.RequiredState = STATE_TR5_LARSON_IDLE; item->Animation.TargetState = STATE_TR5_LARSON_STOP; + item->Animation.RequiredState = STATE_TR5_LARSON_IDLE; break; } if (creature->Mood == MoodType::Escape || item->AIBits & AMBUSH) { - item->Animation.RequiredState = STATE_TR5_LARSON_RUN; item->Animation.TargetState = STATE_TR5_LARSON_STOP; + item->Animation.RequiredState = STATE_TR5_LARSON_RUN; } - else if (Targetable(item, &info)) + else if (Targetable(item, &AI)) { + item->Animation.TargetState = STATE_TR5_LARSON_STOP; item->Animation.RequiredState = STATE_TR5_LARSON_AIM; - item->Animation.TargetState = STATE_TR5_LARSON_STOP; } - else if (!info.ahead || info.distance > SQUARE(3072)) + else if (!AI.ahead || AI.distance > SQUARE(SECTOR(3))) { - item->Animation.RequiredState = STATE_TR5_LARSON_RUN; item->Animation.TargetState = STATE_TR5_LARSON_STOP; + item->Animation.RequiredState = STATE_TR5_LARSON_RUN; } + break; case STATE_TR5_LARSON_RUN: - if (info.ahead) - joint2 = info.angle; - creature->MaxTurn = ANGLE(11); + creature->MaxTurn = ANGLE(11.0f); tilt = angle / 2; + if (AI.ahead) + joint2 = AI.angle; + if (creature->ReachedGoal) - { item->Animation.TargetState = STATE_TR5_LARSON_STOP; - } else if (item->AIBits & AMBUSH) - { item->Animation.TargetState = STATE_TR5_LARSON_RUN; - } - else if (creature->Mood != MoodType::Bored || GetRandomControl() >= 96) + else if (creature->Mood != MoodType::Bored || TestProbability(0.997f)) { - if (Targetable(item, &info)) + if (Targetable(item, &AI)) { - item->Animation.RequiredState = STATE_TR5_LARSON_AIM; item->Animation.TargetState = STATE_TR5_LARSON_STOP; + item->Animation.RequiredState = STATE_TR5_LARSON_AIM; } - else if (info.ahead) + else if (AI.ahead) { - if (info.distance <= SQUARE(3072)) + if (AI.distance <= SQUARE(SECTOR(3))) { - item->Animation.RequiredState = STATE_TR5_LARSON_WALK; item->Animation.TargetState = STATE_TR5_LARSON_STOP; + item->Animation.RequiredState = STATE_TR5_LARSON_WALK; } } } else { - item->Animation.RequiredState = STATE_TR5_LARSON_IDLE; item->Animation.TargetState = STATE_TR5_LARSON_STOP; + item->Animation.RequiredState = STATE_TR5_LARSON_IDLE; } + break; case STATE_TR5_LARSON_AIM: - joint0 = info.angle / 2; - joint2 = info.angle / 2; - if (info.ahead) - joint1 = info.xAngle; creature->MaxTurn = 0; - if (abs(info.angle) >= ANGLE(2)) + joint0 = AI.angle / 2; + joint2 = AI.angle / 2; + + if (AI.ahead) + joint1 = AI.xAngle; + + if (abs(AI.angle) >= ANGLE(2.0f)) { - if (info.angle > 0) - item->Pose.Orientation.y += ANGLE(2); + if (AI.angle > 0) + item->Pose.Orientation.y += ANGLE(2.0f); else - item->Pose.Orientation.y -= ANGLE(2); + item->Pose.Orientation.y -= ANGLE(2.0f); } else - { - item->Pose.Orientation.y += info.angle; - } + item->Pose.Orientation.y += AI.angle; - if (Targetable(item, &info)) + if (Targetable(item, &AI)) item->Animation.TargetState = STATE_TR5_LARSON_ATTACK; else item->Animation.TargetState = STATE_TR5_LARSON_STOP; + break; case STATE_TR5_LARSON_IDLE: - joint0 = info.angle / 2; - joint2 = info.angle / 2; - if (info.ahead) - joint1 = info.xAngle; + joint0 = AI.angle / 2; + joint2 = AI.angle / 2; + + if (AI.ahead) + joint1 = AI.xAngle; if (creature->Mood != MoodType::Bored) - { item->Animation.TargetState = STATE_TR5_LARSON_STOP; - } else { - if (GetRandomControl() <= 96) + if (TestProbability(1.0f / 340)) { - item->Animation.RequiredState = STATE_TR5_LARSON_WALK; item->Animation.TargetState = STATE_TR5_LARSON_STOP; + item->Animation.RequiredState = STATE_TR5_LARSON_WALK; } } + break; case STATE_TR5_LARSON_ATTACK: - joint0 = info.angle / 2; - joint2 = info.angle / 2; - if (info.ahead) - joint1 = info.xAngle; creature->MaxTurn = 0; - if (abs(info.angle) >= ANGLE(2)) + joint0 = AI.angle / 2; + joint2 = AI.angle / 2; + + if (AI.ahead) + joint1 = AI.xAngle; + + if (abs(AI.angle) >= ANGLE(2.0f)) { - if (info.angle > 0) - item->Pose.Orientation.y += ANGLE(2); + if (AI.angle > 0) + item->Pose.Orientation.y += ANGLE(2.0f); else - item->Pose.Orientation.y -= ANGLE(2); + item->Pose.Orientation.y -= ANGLE(2.0f); } else - { - item->Pose.Orientation.y += info.angle; - } + item->Pose.Orientation.y += AI.angle; + if (item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameBase) { if (item->ObjectNumber == ID_PIERRE) { - ShotLara(item, &info, PierreGun1, joint0, 20); - ShotLara(item, &info, PierreGun2, joint0, 20); + ShotLara(item, &AI, PierreGun1, joint0, 20); + ShotLara(item, &AI, PierreGun2, joint0, 20); } else - { - ShotLara(item, &info, LarsonGun, joint0, 20); - } + ShotLara(item, &AI, LarsonGun, joint0, 20); + creature->FiredWeapon = 2; } - if (creature->Mood == MoodType::Escape && GetRandomControl() > 0x2000) + + if (creature->Mood == MoodType::Escape && TestProbability(0.75f)) item->Animation.RequiredState = STATE_TR5_LARSON_STOP; + break; default: break; - } } else if (item->Animation.ActiveState == STATE_TR5_LARSON_DIE) { // When Larson dies, it activates trigger at start position - if (item->ObjectNumber == ID_LARSON - && item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameEnd) + if (item->ObjectNumber == ID_LARSON && + item->Animation.FrameNumber == g_Level.Anims[item->Animation.AnimNumber].frameEnd) { short roomNumber = item->ItemFlags[2] & 0xFF; short floorHeight = item->ItemFlags[2] & 0xFF00; - ROOM_INFO* r = &g_Level.Rooms[roomNumber]; - int x = r->x + (creature->Tosspad / 256 & 0xFF) * SECTOR(1) + 512; - int y = r->minfloor + floorHeight; - int z = r->z + (creature->Tosspad & 0xFF) * SECTOR(1) + 512; + auto* room = &g_Level.Rooms[roomNumber]; + + int x = room->x + (creature->Tosspad / 256 & 0xFF) * SECTOR(1) + 512; + int y = room->minfloor + floorHeight; + int z = room->z + (creature->Tosspad & 0xFF) * SECTOR(1) + 512; TestTriggers(x, y, z, roomNumber, true); @@ -379,13 +369,14 @@ namespace TEN::Entities::TR5 } else { - // Die + // Death. if (item->ObjectNumber == ID_PIERRE) item->Animation.AnimNumber = Objects[ID_PIERRE].animIndex + ANIMATION_TR5_PIERRE_DIE; else item->Animation.AnimNumber = Objects[ID_LARSON].animIndex + ANIMATION_TR5_LARSON_DIE; - item->Animation.ActiveState = STATE_TR5_LARSON_DIE; + item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; + item->Animation.ActiveState = STATE_TR5_LARSON_DIE; } CreatureTilt(item, tilt); @@ -400,12 +391,12 @@ namespace TEN::Entities::TR5 { item->TargetState = STATE_TR5_LARSON_STOP; item->RequiredState = STATE_TR5_LARSON_STOP; - creature->reachedGoal = false; + creature->ReachedGoal = false; item->IsAirborne = false; - item->hitStatus = false; - item->collidable = false; - item->status = ITEM_NOT_ACTIVE; - item->triggerFlags = 0; + item->HitStatus = false; + item->Collidable = false; + item->Status = ITEM_NOT_ACTIVE; + item->TriggerFlags = 0; } else { diff --git a/TombEngine/Objects/TR5/Entity/tr5_larson.h b/TombEngine/Objects/TR5/Entity/tr5_larson.h index 15c75470b..4e86b61c9 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_larson.h +++ b/TombEngine/Objects/TR5/Entity/tr5_larson.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseLarson(short itemNumber); void LarsonControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_laser_head.cpp b/TombEngine/Objects/TR5/Entity/tr5_laser_head.cpp index a1a848331..a889a960e 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_laser_head.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_laser_head.cpp @@ -19,7 +19,7 @@ using namespace TEN::Effects::Lara; using namespace TEN::Effects::Lightning; -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { struct LaserHeadStruct { diff --git a/TombEngine/Objects/TR5/Entity/tr5_laser_head.h b/TombEngine/Objects/TR5/Entity/tr5_laser_head.h index 5990de0b3..abd3542b4 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_laser_head.h +++ b/TombEngine/Objects/TR5/Entity/tr5_laser_head.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseLaserHead(short itemNumber); void LaserHeadControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_laserhead_info.h b/TombEngine/Objects/TR5/Entity/tr5_laserhead_info.h index a0b038821..52adc215f 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_laserhead_info.h +++ b/TombEngine/Objects/TR5/Entity/tr5_laserhead_info.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { struct LaserHeadInfo { diff --git a/TombEngine/Objects/TR5/Entity/tr5_lion.cpp b/TombEngine/Objects/TR5/Entity/tr5_lion.cpp index a5443ddfd..9a2cb2bed 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_lion.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_lion.cpp @@ -16,16 +16,16 @@ using namespace TEN::Math::Random; using std::vector; -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { constexpr auto LION_POUNCE_ATTACK_DAMAGE = 200; constexpr auto LION_BITE_ATTACK_DAMAGE = 60; constexpr auto LION_POUNCE_ATTACK_RANGE = SQUARE(SECTOR(1)); - const vector LionAttackJoints = { 3, 6, 21 }; const auto LionBite1 = BiteInfo(Vector3(2.0f, -10.0f, 250.0f), 21); const auto LionBite2 = BiteInfo(Vector3(-2.0f, -10.0f, 132.0f), 21); + const vector LionAttackJoints = { 3, 6, 21 }; enum LionState { @@ -142,7 +142,7 @@ namespace TEN::Entities::TR5 if (creature->Mood == MoodType::Bored) { - if (TestProbability(0.004f)) + if (TestProbability(1.0f / 256)) { item->Animation.TargetState = LION_STATE_IDLE; item->Animation.RequiredState = LION_STATE_ROAR; @@ -165,7 +165,7 @@ namespace TEN::Entities::TR5 item->Animation.TargetState = LION_STATE_IDLE; else if (creature->Mood != MoodType::Escape) { - if (TestProbability(0.004f)) + if (TestProbability(1.0f / 256)) { item->Animation.TargetState = LION_STATE_IDLE; item->Animation.RequiredState = LION_STATE_ROAR; @@ -181,9 +181,9 @@ namespace TEN::Entities::TR5 if (!item->Animation.RequiredState && item->TestBits(JointBitType::Touch, LionAttackJoints)) { - item->Animation.RequiredState = LION_STATE_IDLE; DoDamage(creature->Enemy, LION_POUNCE_ATTACK_DAMAGE); CreatureEffect2(item, LionBite1, 10, item->Pose.Orientation.y, DoBloodSplat); + item->Animation.RequiredState = LION_STATE_IDLE; } break; @@ -194,9 +194,9 @@ namespace TEN::Entities::TR5 if (!item->Animation.RequiredState && item->TestBits(JointBitType::Touch, LionAttackJoints)) { - item->Animation.RequiredState = LION_STATE_IDLE; DoDamage(creature->Enemy, LION_BITE_ATTACK_DAMAGE); CreatureEffect2(item, LionBite2, 10, item->Pose.Orientation.y, DoBloodSplat); + item->Animation.RequiredState = LION_STATE_IDLE; } break; diff --git a/TombEngine/Objects/TR5/Entity/tr5_lion.h b/TombEngine/Objects/TR5/Entity/tr5_lion.h index 85d223a2d..4218188f4 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_lion.h +++ b/TombEngine/Objects/TR5/Entity/tr5_lion.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseLion(short itemNumber); void LionControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_reaper.cpp b/TombEngine/Objects/TR5/Entity/tr5_reaper.cpp index f8f1ba0ac..4018b8e2a 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_reaper.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_reaper.cpp @@ -9,7 +9,7 @@ #include "Game/itemdata/creature_info.h" #include "Game/control/control.h" -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseReaper(short itemNumber) { diff --git a/TombEngine/Objects/TR5/Entity/tr5_reaper.h b/TombEngine/Objects/TR5/Entity/tr5_reaper.h index 464d8dae5..cebde76ff 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_reaper.h +++ b/TombEngine/Objects/TR5/Entity/tr5_reaper.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseReaper(short itemNumber); void ReaperControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp index a7d242b5e..6f4954ba5 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.cpp @@ -18,7 +18,7 @@ using namespace TEN::Effects::Lightning; -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { const auto RomanStatueBite = BiteInfo(Vector3::Zero, 15); @@ -595,7 +595,6 @@ namespace TEN::Entities::TR5 ShatterObject(0, mesh, -64, LaraItem->RoomNumber, 0); SoundEffect(GetShatterSound(mesh->staticNumber), (PHD_3DPOS*)mesh); - mesh->flags &= ~StaticMeshFlags::SM_VISIBLE; floor->Stopper = false; TestTriggers(pos.x, pos.y, pos.z, item->RoomNumber, true); diff --git a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.h b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.h index d2b9ee411..0b35f6e09 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_roman_statue.h +++ b/TombEngine/Objects/TR5/Entity/tr5_roman_statue.h @@ -1,7 +1,7 @@ #pragma once #include "Specific/phd_global.h" -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseRomanStatue(short itemNumber); void RomanStatueControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_submarine.cpp b/TombEngine/Objects/TR5/Entity/tr5_submarine.cpp index 208386092..5ee97fef8 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_submarine.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_submarine.cpp @@ -1,24 +1,26 @@ #include "framework.h" -#include "tr5_submarine.h" -#include "Game/items.h" -#include "Game/control/box.h" -#include "Game/people.h" +#include "Objects/TR5/Entity/tr5_submarine.h" + +#include "Game/animation.h" #include "Game/collision/collide_item.h" #include "Game/collision/collide_room.h" +#include "Game/control/box.h" #include "Game/control/los.h" #include "Game/effects/effects.h" #include "Game/effects/tomb4fx.h" #include "Game/itemdata/creature_info.h" -#include "Game/animation.h" +#include "Game/items.h" #include "Game/Lara/lara.h" #include "Game/Lara/lara_one_gun.h" -#include "Specific/setup.h" -#include "Specific/level.h" +#include "Game/misc.h" +#include "Game/people.h" #include "Sound/sound.h" +#include "Specific/level.h" +#include "Specific/setup.h" -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { - static void TriggerSubmarineSparks(short itemNumber) + void TriggerSubmarineSparks(short itemNumber) { auto* spark = GetFreeParticle(); @@ -50,7 +52,7 @@ namespace TEN::Entities::TR5 spark->dSize = spark->sSize = spark->size = (GetRandomControl() & 7) + 192; } - static void TriggerTorpedoBubbles(Vector3Int* pos1, Vector3Int* pos2, char factor) + void TriggerTorpedoBubbles(Vector3Int* pos1, Vector3Int* pos2, char factor) { auto* spark = GetFreeParticle(); @@ -83,7 +85,7 @@ namespace TEN::Entities::TR5 spark->dSize = spark->size * 2; } - static void TriggerTorpedoSparks2(Vector3Int* pos1, Vector3Int* pos2, char scale) + void TriggerTorpedoSparks2(Vector3Int* pos1, Vector3Int* pos2, char scale) { auto* spark = GetFreeParticle(); @@ -115,55 +117,56 @@ namespace TEN::Entities::TR5 spark->dSize = spark->size * 2; } - static void SubmarineAttack(ItemInfo* item) + void SubmarineAttack(ItemInfo* item) { short itemNumber = CreateItem(); + if (itemNumber == NO_ITEM) + return; - if (itemNumber != NO_ITEM) + auto* torpedoItem = &g_Level.Items[itemNumber]; + + SoundEffect(SFX_TR5_UNDERWATER_TORPEDO, &torpedoItem->Pose, SoundEnvironment::Always); + + torpedoItem->ObjectNumber = ID_TORPEDO; + torpedoItem->Color = Vector4(0.5f, 0.5f, 0.5f, 1.0f); + + Vector3Int pos1; + Vector3Int pos2; + + for (int i = 0; i < 8; i++) { - auto* torpedoItem = &g_Level.Items[itemNumber]; + auto pos1 = Vector3Int( + (GetRandomControl() & 0x7F) - 414, + -320, + 352 + ); + GetJointAbsPosition(item, &pos1, 4); - SoundEffect(SFX_TR5_UNDERWATER_TORPEDO, &torpedoItem->Pose, SoundEnvironment::Always); + auto pos2 = Vector3Int( + (GetRandomControl() & 0x3FF) - 862, + -320 - (GetRandomControl() & 0x3FF), + (GetRandomControl() & 0x3FF) - 160 + ); + GetJointAbsPosition(item, &pos2, 4); - torpedoItem->ObjectNumber = ID_TORPEDO; - torpedoItem->Color = Vector4(0.5f, 0.5f, 0.5f, 1.0f); - - Vector3Int pos1; - Vector3Int pos2; - - for (int i = 0; i < 8; i++) - { - pos1.x = (GetRandomControl() & 0x7F) - 414; - pos1.y = -320; - pos1.z = 352; - GetJointAbsPosition(item, &pos1, 4); - - pos2.x = (GetRandomControl() & 0x3FF) - 862; - pos2.y = -320 - (GetRandomControl() & 0x3FF); - pos2.z = (GetRandomControl() & 0x3FF) - 160; - GetJointAbsPosition(item, &pos2, 4); - - TriggerTorpedoSparks2(&pos1, &pos2, 0); - } - - torpedoItem->RoomNumber = item->RoomNumber; - GetFloor(pos1.x, pos1.y, pos1.z, &torpedoItem->RoomNumber); - - torpedoItem->Pose.Position.x = pos1.x; - torpedoItem->Pose.Position.y = pos1.y; - torpedoItem->Pose.Position.z = pos1.z; - - InitialiseItem(itemNumber); - - torpedoItem->Pose.Orientation.x = 0; - torpedoItem->Pose.Orientation.y = item->Pose.Orientation.y; - torpedoItem->Pose.Orientation.z = 0; - torpedoItem->Animation.Velocity.z = 0; - torpedoItem->Animation.Velocity.y = 0; - torpedoItem->ItemFlags[0] = -1; - - AddActiveItem(itemNumber); + TriggerTorpedoSparks2(&pos1, &pos2, 0); } + + torpedoItem->RoomNumber = item->RoomNumber; + GetFloor(pos1.x, pos1.y, pos1.z, &torpedoItem->RoomNumber); + + torpedoItem->Pose.Position = pos1; + + InitialiseItem(itemNumber); + + torpedoItem->Animation.Velocity.y = 0.0f; + torpedoItem->Animation.Velocity.z = 0.0f; + torpedoItem->Pose.Orientation.x = 0; + torpedoItem->Pose.Orientation.y = item->Pose.Orientation.y; + torpedoItem->Pose.Orientation.z = 0; + torpedoItem->ItemFlags[0] = -1; + + AddActiveItem(itemNumber); } void InitialiseSubmarine(short itemNumber) @@ -171,10 +174,7 @@ namespace TEN::Entities::TR5 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.TargetState = 0; - item->Animation.ActiveState = 0; + SetAnimation(item, 0); if (!item->TriggerFlags) item->TriggerFlags = 120; @@ -186,14 +186,14 @@ namespace TEN::Entities::TR5 return; auto* item = &g_Level.Items[itemNumber]; - auto* creature = (CreatureInfo*)item->Data; + auto* creature = GetCreatureInfo(item); if (item->AIBits) GetAITarget(creature); else creature->Enemy = LaraItem; - AI_INFO AI, laraInfo; + AI_INFO AI, laraAI; CreatureAIInfo(item, &AI); GetCreatureMood(item, &AI, true); @@ -203,34 +203,34 @@ namespace TEN::Entities::TR5 if (creature->Enemy == LaraItem) { - laraInfo.angle = AI.angle; - laraInfo.distance = AI.distance; + laraAI.angle = AI.angle; + laraAI.distance = AI.distance; } else { int dx = LaraItem->Pose.Position.x - item->Pose.Position.x; int dz = LaraItem->Pose.Position.z - item->Pose.Position.z; - laraInfo.angle = phd_atan(dz, dx) - item->Pose.Orientation.y; - laraInfo.distance = pow(dx, 2) + pow(dz, 2); - laraInfo.ahead = true; + laraAI.angle = phd_atan(dz, dx) - item->Pose.Orientation.y; + laraAI.distance = pow(dx, 2) + pow(dz, 2); + laraAI.ahead = true; } int tilt = item->ItemFlags[0] + (angle / 2); - if (tilt > 2048) - tilt = 2048; - else if (tilt < -2048) - tilt = -2048; + if (tilt > ANGLE(11.25f)) + tilt = ANGLE(11.25f); + else if (tilt < ANGLE(-11.25f)) + tilt = ANGLE(-11.25f); item->ItemFlags[0] = tilt; - if (abs(tilt) >= 64) + if (abs(tilt) >= ANGLE(0.35f)) { if (tilt > 0) - item->ItemFlags[0] -= 64; + item->ItemFlags[0] -= ANGLE(0.35f); else - item->ItemFlags[0] += 64; + item->ItemFlags[0] += ANGLE(0.35f); } else item->ItemFlags[0] = 0; @@ -245,17 +245,17 @@ namespace TEN::Entities::TR5 auto* enemy = creature->Enemy; creature->Enemy = LaraItem; - if (Targetable(item, &laraInfo)) + if (Targetable(item, &laraAI)) { if (creature->Flags >= item->TriggerFlags && - laraInfo.angle > -ANGLE(90.0f) && - laraInfo.angle < ANGLE(90.0f)) + laraAI.angle > -ANGLE(90.0f) && + laraAI.angle < ANGLE(90.0f)) { SubmarineAttack(item); creature->Flags = 0; } - if (laraInfo.distance >= pow(SECTOR(3), 2)) + if (laraAI.distance >= pow(SECTOR(3), 2)) { item->Animation.TargetState = 1; SoundEffect(SFX_TR5_VEHICLE_DIVESUIT_LOOP, &item->Pose, SoundEnvironment::Always); @@ -266,15 +266,15 @@ namespace TEN::Entities::TR5 if (AI.distance < pow(SECTOR(1), 2)) { creature->MaxTurn = 0; - if (abs(laraInfo.angle) >= ANGLE(2.0f)) + if (abs(laraAI.angle) >= ANGLE(2.0f)) { - if (laraInfo.angle >= 0) + if (laraAI.angle >= 0) item->Pose.Orientation.y += ANGLE(2.0f); else item->Pose.Orientation.y -= ANGLE(2.0f); } else - item->Pose.Orientation.y += laraInfo.angle; + item->Pose.Orientation.y += laraAI.angle; } } else @@ -288,36 +288,36 @@ namespace TEN::Entities::TR5 if (GlobalCounter & 1) { - Vector3Int pos1 = { 200, 320, 90 }; + auto pos1 = Vector3Int(200, 320, 90); GetJointAbsPosition(item, &pos1, 1); - Vector3Int pos2 = { 200, 1280, 90 }; + auto pos2 = Vector3Int(200, 1280, 90); GetJointAbsPosition(item, &pos2, 1); TriggerTorpedoBubbles(&pos1, &pos2, 0); - pos1 = { 200, 320, -100 }; + pos1 = Vector3Int(200, 320, -100); GetJointAbsPosition(item, &pos1, 1); - pos2 = { 200, 1280, -100 }; + pos2 = Vector3Int(200, 1280, -100); GetJointAbsPosition(item, &pos2, 1); TriggerTorpedoBubbles(&pos1, &pos2, 0); } else { - Vector3Int pos1 = { -200, 320, 90 }; + auto pos1 = Vector3Int(-200, 320, 90); GetJointAbsPosition(item, &pos1, 2); - Vector3Int pos2 = { -200, 1280, 90 }; + auto pos2 = Vector3Int(-200, 1280, 90); GetJointAbsPosition(item, &pos2, 2); TriggerTorpedoBubbles(&pos1, &pos2, 0); - pos1 = { -200, 320, -100 }; + pos1 = Vector3Int(-200, 320, -100); GetJointAbsPosition(item, &pos1, 2); - pos2 = { -200, 1280, -100 }; + pos2 = Vector3Int(-200, 1280, -100); GetJointAbsPosition(item, &pos2, 2); TriggerTorpedoBubbles(&pos1, &pos2, 0); @@ -384,8 +384,8 @@ namespace TEN::Entities::TR5 if (TestEnvironment(ENV_FLAG_WATER, item->RoomNumber)) { - item->Animation.Velocity.z += (5 - item->Animation.Velocity.z) / 2; - item->Animation.Velocity.y+= (5 - item->Animation.Velocity.y) / 2; + item->Animation.Velocity.y += (5.0f - item->Animation.Velocity.y) / 2.0f; + item->Animation.Velocity.z += (5.0f - item->Animation.Velocity.z) / 2.0f; } else item->Animation.Velocity.y += GRAVITY; @@ -440,20 +440,14 @@ namespace TEN::Entities::TR5 if (searchItem->ObjectNumber == ID_CHAFF && searchItem->Active) { item->ItemFlags[0] = i; - pos.x = searchItem->Pose.Position.x; - pos.y = searchItem->Pose.Position.y; - pos.z = searchItem->Pose.Position.z; + pos = searchItem->Pose.Position; found = true; break; } } if (!found) - { - pos.x = LaraItem->Pose.Position.x; - pos.y = LaraItem->Pose.Position.y; - pos.z = LaraItem->Pose.Position.z; - } + pos = LaraItem->Pose.Position; } else { @@ -461,9 +455,7 @@ namespace TEN::Entities::TR5 if (chaffItem->Active && chaffItem->ObjectNumber == ID_CHAFF) { - pos.x = chaffItem->Pose.Position.x; - pos.y = chaffItem->Pose.Position.y; - pos.z = chaffItem->Pose.Position.z; + pos = chaffItem->Pose.Position; item->Animation.ActiveState = pos.x / 4; item->Animation.TargetState = pos.y / 4; item->Animation.RequiredState = pos.z / 4; @@ -478,13 +470,13 @@ namespace TEN::Entities::TR5 auto angles = GetVectorAngles(pos.x - item->Pose.Position.x, pos.y - item->Pose.Position.y, pos.z - item->Pose.Position.z); - if (item->Animation.Velocity.z >= 48) + if (item->Animation.Velocity.z >= 48.0f) { - if (item->Animation.Velocity.z < 192) + if (item->Animation.Velocity.z < 192.0f) item->Animation.Velocity.z++; } else - item->Animation.Velocity.z += 4; + item->Animation.Velocity.z += 4.0f; item->ItemFlags[1]++; @@ -539,7 +531,7 @@ namespace TEN::Entities::TR5 item->Pose.Position.y > probe.Position.Ceiling && TestEnvironment(ENV_FLAG_WATER, probe.RoomNumber)) { - if (ItemNearLara(&item->Pose, 200)) + if (ItemNearLara(&item->Pose.Position, 200)) { LaraItem->HitStatus = true; KillItem(itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_submarine.h b/TombEngine/Objects/TR5/Entity/tr5_submarine.h index ab76d34ca..7a3db051e 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_submarine.h +++ b/TombEngine/Objects/TR5/Entity/tr5_submarine.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseSubmarine(short itemNumber); void SubmarineControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Entity/tr5_willowwisp.cpp b/TombEngine/Objects/TR5/Entity/tr5_willowwisp.cpp index f2c8e6a04..d99d28674 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_willowwisp.cpp +++ b/TombEngine/Objects/TR5/Entity/tr5_willowwisp.cpp @@ -5,7 +5,7 @@ #include "Specific/level.h" #include "Specific/setup.h" -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { enum WillowWispState { @@ -17,9 +17,6 @@ namespace TEN::Entities::TR5 auto* item = &g_Level.Items[itemNumber]; ClearItem(itemNumber); - item->Animation.AnimNumber = Objects[item->ObjectNumber].animIndex; - item->Animation.FrameNumber = g_Level.Anims[item->Animation.AnimNumber].frameBase; - item->Animation.ActiveState = WWISP_STATE_UNK; - item->Animation.TargetState = WWISP_STATE_UNK; + SetAnimation(item, 0); } } diff --git a/TombEngine/Objects/TR5/Entity/tr5_willowwisp.h b/TombEngine/Objects/TR5/Entity/tr5_willowwisp.h index 778c3ea29..6271a9c6f 100644 --- a/TombEngine/Objects/TR5/Entity/tr5_willowwisp.h +++ b/TombEngine/Objects/TR5/Entity/tr5_willowwisp.h @@ -1,6 +1,6 @@ #pragma once -namespace TEN::Entities::TR5 +namespace TEN::Entities::Creatures::TR5 { void InitialiseLightingGuide(short itemNumber); } diff --git a/TombEngine/Objects/TR5/Object/tr5_missile.cpp b/TombEngine/Objects/TR5/Object/tr5_missile.cpp index 959c168cd..bb75d362d 100644 --- a/TombEngine/Objects/TR5/Object/tr5_missile.cpp +++ b/TombEngine/Objects/TR5/Object/tr5_missile.cpp @@ -21,6 +21,7 @@ int DebrisFlags; void MissileControl(short itemNumber) { auto* fx = &EffectList[itemNumber]; + if (fx->flag1 == 2) { fx->pos.Orientation.z += 16 * fx->speed; @@ -118,9 +119,9 @@ void MissileControl(short itemNumber) { TriggerExplosionSparks(x, y, z, 3, -2, 2, fx->roomNumber); fx->pos.Position.y -= 64; - TriggerShockwave((PHD_3DPOS*)fx, 48, 256, 64, 64, 128, 0, 24, 0, 1); + TriggerShockwave(&fx->pos, 48, 256, 64, 64, 128, 0, 24, 0, 1); fx->pos.Position.y -= 128; - TriggerShockwave((PHD_3DPOS*)fx, 48, 256, 48, 64, 128, 0, 24, 0, 1); + TriggerShockwave(&fx->pos, 48, 256, 48, 64, 128, 0, 24, 0, 1); } else if (fx->flag1 == 2) { @@ -131,12 +132,12 @@ void MissileControl(short itemNumber) else { TriggerExplosionSparks(x, y, z, 3, -2, 0, fx->roomNumber); - TriggerShockwave((PHD_3DPOS*)fx, 48, 240, 48, 0, 96, 128, 24, 0, 2); + TriggerShockwave(&fx->pos, 48, 240, 48, 0, 96, 128, 24, 0, 2); } KillEffect(itemNumber); } - else if (ItemNearLara((PHD_3DPOS*)fx, 200)) + else if (ItemNearLara(&fx->pos.Position, 200)) { if (fx->flag1) { @@ -145,9 +146,9 @@ void MissileControl(short itemNumber) // ROMAN_GOD hit effect TriggerExplosionSparks(x, y, z, 3, -2, 2, fx->roomNumber); fx->pos.Position.y -= 64; - TriggerShockwave((PHD_3DPOS*)fx, 48, 256, 64, 0, 128, 64, 24, 0, 1); + TriggerShockwave(&fx->pos, 48, 256, 64, 0, 128, 64, 24, 0, 1); fx->pos.Position.y -= 128; - TriggerShockwave((PHD_3DPOS*)fx, 48, 256, 48, 0, 128, 64, 24, 0, 1); + TriggerShockwave(&fx->pos, 48, 256, 48, 0, 128, 64, 24, 0, 1); KillEffect(itemNumber); DoDamage(LaraItem, 200); } @@ -170,7 +171,7 @@ void MissileControl(short itemNumber) { // HYDRA hit effect TriggerExplosionSparks(x, y, z, 3, -2, 0, fx->roomNumber); - TriggerShockwave((PHD_3DPOS*)fx, 48, 240, 48, 0, 96, 128, 24, 0, 0); + TriggerShockwave(&fx->pos, 48, 240, 48, 0, 96, 128, 24, 0, 0); if (LaraItem->HitPoints >= 500) DoDamage(LaraItem, 300); else @@ -185,8 +186,7 @@ void MissileControl(short itemNumber) if (GlobalCounter & 1) { - Vector3Int pos = { x, y, z }; - + auto pos = Vector3Int(x, y, z); int xv = x - fx->pos.Position.x; int yv = y - fx->pos.Position.y; int zv = z - fx->pos.Position.z; @@ -196,7 +196,7 @@ void MissileControl(short itemNumber) else { TriggerHydraMissileSparks(&pos, 4 * xv, 4 * yv, 4 * zv); - TriggerHydraMissileSparks((Vector3Int*)&fx, 4 * xv, 4 * yv, 4 * zv); + TriggerHydraMissileSparks(&fx->pos.Position, 4 * xv, 4 * yv, 4 * zv); } } } diff --git a/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.cpp b/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.cpp index 274bb64bb..7f725661a 100644 --- a/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.cpp +++ b/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.cpp @@ -1,21 +1,21 @@ #include "framework.h" #include "tr5_crowdove_switch.h" + +#include "Game/animation.h" +#include "Game/collision/collide_item.h" #include "Game/control/control.h" -#include "Specific/input.h" -#include "Specific/level.h" +#include "Game/effects/debris.h" +#include "Game/items.h" #include "Game/Lara/lara.h" #include "Game/Lara/lara_helpers.h" #include "Objects/Generic/Switches/generic_switch.h" #include "Sound/sound.h" -#include "Game/animation.h" -#include "Game/items.h" -#include "Game/collision/collide_item.h" -#include "Game/effects/debris.h" +#include "Specific/input.h" +#include "Specific/level.h" using namespace TEN::Input; -using namespace TEN::Entities::Switches; -namespace TEN::Entities::TR5 +namespace TEN::Entities::Switches { OBJECT_COLLISION_BOUNDS CrowDoveBounds = { diff --git a/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.h b/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.h index 8aeeea90f..c32c73df8 100644 --- a/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.h +++ b/TombEngine/Objects/TR5/Switch/tr5_crowdove_switch.h @@ -1,9 +1,8 @@ #pragma once - -#include "Game/items.h" #include "Game/collision/collide_room.h" +#include "Game/items.h" -namespace TEN::Entities::TR5 +namespace TEN::Entities::Switches { void InitialiseCrowDoveSwitch(short itemNumber); void CrowDoveSwitchControl(short itemNumber); diff --git a/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp b/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp index 16f2e1ce2..ee9ad7597 100644 --- a/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp +++ b/TombEngine/Objects/TR5/Trap/tr5_explosion.cpp @@ -153,10 +153,6 @@ void ExplosionControl(short itemNumber) CollidedMeshes[i]->pos.Position.y += 128; SoundEffect(GetShatterSound(CollidedMeshes[i]->staticNumber), &CollidedMeshes[i]->pos); ShatterObject(NULL, CollidedMeshes[i], -128, item->RoomNumber, 0); - SmashedMeshRoom[SmashedMeshCount] = item->RoomNumber; - SmashedMesh[SmashedMeshCount] = CollidedMeshes[i]; - ++SmashedMeshCount; - CollidedMeshes[i]->flags &= ~StaticMeshFlags::SM_VISIBLE; } ++i; diff --git a/TombEngine/Objects/TR5/tr5_objects.cpp b/TombEngine/Objects/TR5/tr5_objects.cpp index 1efa9b1dd..5c7b5261f 100644 --- a/TombEngine/Objects/TR5/tr5_objects.cpp +++ b/TombEngine/Objects/TR5/tr5_objects.cpp @@ -1,7 +1,20 @@ #include "framework.h" #include "Objects/TR5/tr5_objects.h" -/// Entities +#include "Game/collision/collide_item.h" +#include "Game/control/box.h" +#include "Game/itemdata/creature_info.h" +#include "Game/Lara/lara_flare.h" +#include "Game/Lara/lara_initialise.h" +#include "Game/Lara/lara_one_gun.h" +#include "Game/pickup/pickup.h" +#include "Objects/Generic/Object/objects.h" +#include "Objects/Generic/Switches/switch.h" +#include "Objects/Utils/object_helper.h" +#include "Specific/level.h" +#include "Specific/setup.h" + +// Creatures #include "tr5_autoguns.h" // OK #include "tr5_brownbeast.h" // OK #include "tr5_chef.h" // OK @@ -23,13 +36,13 @@ #include "tr5_submarine.h" // OK #include "tr5_willowwisp.h" // OK -/// Emitters +// Emitters #include "Objects/TR5/Emitter/tr5_rats_emitter.h" #include "Objects/TR5/Emitter/tr5_bats_emitter.h" #include "Objects/TR5/Emitter/tr5_spider_emitter.h" #include "tr5_smoke_emitter.h" -/// Objects +// Objects #include "Objects/TR5/Object/tr5_pushableblock.h" #include "tr5_twoblockplatform.h" #include "tr5_raisingcog.h" @@ -42,7 +55,7 @@ #include "tr5_missile.h" #include "tr5_genslot.h" -/// Traps +// Traps #include "tr5_ventilator.h" #include "tr5_deathslide.h" #include "Objects/Effects/tr5_electricity.h" @@ -55,24 +68,11 @@ // Switches #include "tr5_crowdove_switch.h" -/// shatter +// Shatters #include "Objects/TR5/Shatter/tr5_smashobject.h" -/// necessary import -#include "Game/collision/collide_item.h" -#include "Game/Lara/lara_one_gun.h" -#include "Game/Lara/lara_flare.h" -#include "Game/Lara/lara_initialise.h" -#include "Game/pickup/pickup.h" -#include "Specific/setup.h" -#include "Objects/Generic/Switches/switch.h" -#include "Objects/Generic/Object/objects.h" -#include "Specific/level.h" -/// register objects -#include "Objects/Utils/object_helper.h" -#include "Game/itemdata/creature_info.h" -#include "Game/control/box.h" -using namespace TEN::Entities::TR5; +using namespace TEN::Entities::Creatures::TR5; +using namespace TEN::Entities::Switches; static void StartEntity(ObjectInfo *obj) { @@ -105,7 +105,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 13 * 4] |= ROT_Y; @@ -130,7 +130,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 13 * 4] |= ROT_Y; @@ -157,7 +157,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 13 * 4] |= ROT_Y; @@ -187,7 +187,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 13 * 4] |= ROT_Y; @@ -218,7 +218,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 13 * 4] |= ROT_Y; @@ -247,7 +247,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[Objects[69].boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[Objects[69].boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[Objects[69].boneIndex + 13 * 4] |= ROT_Y; @@ -278,7 +278,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 13 * 4] |= ROT_Y; @@ -310,7 +310,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 13 * 4] |= ROT_Y; @@ -334,7 +334,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveHitpoints = true; obj->savePosition = true; obj->waterCreature = true; - obj->zoneType = ZONE_FLYER; + obj->ZoneType = ZoneType::Flyer; obj->undead = true; g_Level.Bones[obj->boneIndex] |= ROT_X; g_Level.Bones[obj->boneIndex + 4] |= ROT_X; @@ -357,7 +357,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 4 * 6] |= ROT_Y; g_Level.Bones[obj->boneIndex + 4 * 6] |= ROT_X; @@ -381,7 +381,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 19 * 4] |= ROT_Y; } @@ -402,7 +402,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 19 * 4] |= ROT_Y; } @@ -422,7 +422,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 19 * 4] |= ROT_Y; } @@ -443,7 +443,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveHitpoints = true; obj->savePosition = true; obj->waterCreature = true; - obj->zoneType = ZONE_FLYER; + obj->ZoneType = ZoneType::Flyer; } obj = &Objects[ID_MAFIA2]; @@ -464,7 +464,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; obj->meshSwapSlot = ID_MESHSWAP_MAFIA2; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; @@ -490,7 +490,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 7 * 4] |= ROT_Y; @@ -514,7 +514,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 7 * 4] |= ROT_Y; @@ -539,7 +539,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveAnim = true; obj->saveHitpoints = true; obj->undead = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; obj->meshSwapSlot = ID_MESHSWAP_HITMAN; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; @@ -591,7 +591,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveAnim = true; obj->saveHitpoints = true; obj->undead = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex] |= ROT_Y; g_Level.Bones[obj->boneIndex] |= ROT_X; g_Level.Bones[obj->boneIndex + 4] |= ROT_Y; @@ -616,7 +616,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveAnim = true; obj->saveHitpoints = true; obj->undead = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 0] |= ROT_Y; g_Level.Bones[obj->boneIndex + 8 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 8 * 4] |= ROT_X; @@ -641,7 +641,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveAnim = true; obj->saveHitpoints = true; obj->meshSwapSlot = ID_MESHSWAP_IMP; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->meshIndex + 4 * 4] |= ROT_Z; g_Level.Bones[obj->meshIndex + 4 * 4] |= ROT_X; @@ -665,7 +665,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveHitpoints = true; obj->savePosition = true; - obj->zoneType = ZONE_FLYER; + obj->ZoneType = ZoneType::Flyer; g_Level.Bones[obj->boneIndex + 4 * 4] |= ROT_Z; g_Level.Bones[obj->boneIndex + 4 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 9 * 4] |= ROT_Z; @@ -689,7 +689,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveHitpoints = true; obj->savePosition = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 4 * 4] |= ROT_Z; g_Level.Bones[obj->boneIndex + 4 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 9 * 4] |= ROT_Z; @@ -714,7 +714,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveHitpoints = true; obj->savePosition = true; obj->waterCreature = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; g_Level.Bones[obj->boneIndex + 4 * 4] |= ROT_Z; g_Level.Bones[obj->boneIndex + 4 * 4] |= ROT_X; @@ -739,7 +739,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 8 * 4] |= ROT_Y; @@ -790,7 +790,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveFlags = true; obj->saveAnim = true; obj->saveHitpoints = true; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_Y; g_Level.Bones[obj->boneIndex + 6 * 4] |= ROT_X; g_Level.Bones[obj->boneIndex + 13 * 4] |= ROT_Y; @@ -817,7 +817,7 @@ static void StartEntity(ObjectInfo *obj) obj->saveAnim = true; obj->saveHitpoints = true; obj->meshSwapSlot = ID_MESHSWAP_ROMAN_GOD1 + i; - obj->zoneType = ZONE_HUMAN_CLASSIC; + obj->ZoneType = ZoneType::HumanClassic; g_Level.Bones[obj->boneIndex + 24] |= ROT_Y; g_Level.Bones[obj->boneIndex + 24] |= ROT_X; @@ -876,7 +876,7 @@ static void StartEntity(ObjectInfo *obj) obj->initialise = InitialiseSubmarine; obj->control = SubmarineControl; obj->saveAnim = true; - obj->zoneType = ZONE_BASIC; + obj->ZoneType = ZoneType::Basic; obj->hitEffect = HIT_RICOCHET; obj->shadowType = ShadowMode::All; obj->HitPoints = 100; diff --git a/TombEngine/Resources.rc b/TombEngine/Resources.rc index dd1606941..4f29cb8e1 100644 --- a/TombEngine/Resources.rc +++ b/TombEngine/Resources.rc @@ -26,8 +26,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_NEUTRAL // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,0,1,0 - PRODUCTVERSION 1,5,2,0 + FILEVERSION 1,0,2,0 + PRODUCTVERSION 1,6,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -43,12 +43,12 @@ BEGIN BLOCK "000904b0" BEGIN VALUE "CompanyName", "TRLE Dev Community" - VALUE "FileVersion", "1.0.1.0" + VALUE "FileVersion", "1.0.2.0" VALUE "InternalName", "TombEngine.exe" VALUE "LegalCopyright", "Copyright (C) 2022" VALUE "OriginalFilename", "TombEngine.exe" VALUE "ProductName", "Tomb Engine" - VALUE "ProductVersion", "1.5.2.0" + VALUE "ProductVersion", "1.6.0.0" END END BLOCK "VarFileInfo" diff --git a/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h b/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h index 872adfb79..917ce4575 100644 --- a/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h +++ b/TombEngine/Scripting/Include/Flow/ScriptInterfaceFlowHandler.h @@ -32,7 +32,7 @@ public: virtual bool HasOverhangClimb() const = 0; virtual bool HasSlideExtended() const = 0; virtual ScriptInterfaceLevel * GetLevel(int level) = 0; - virtual int GetLevelNumber(std::string const& flieName) = 0; + virtual int GetLevelNumber(std::string const& fileName) = 0; virtual bool CanPlayAnyLevel() const = 0; virtual bool DoFlow() = 0; }; diff --git a/TombEngine/Scripting/Include/ScriptInterfaceGame.h b/TombEngine/Scripting/Include/ScriptInterfaceGame.h index 33b7b3344..010afc4eb 100644 --- a/TombEngine/Scripting/Include/ScriptInterfaceGame.h +++ b/TombEngine/Scripting/Include/ScriptInterfaceGame.h @@ -19,7 +19,12 @@ using VarSaveType = std::variant; using IndexTable = std::vector>; -using SavedVar = std::variant; +struct FuncName +{ + std::string name; +}; + +using SavedVar = std::variant; class ScriptInterfaceGame { public: @@ -36,11 +41,15 @@ public: virtual void FreeLevelScripts() = 0; virtual void ResetScripts(bool clearGameVars) = 0; virtual void ExecuteScriptFile(std::string const& luaFileName) = 0; + virtual void ExecuteString(std::string const& command) = 0; virtual void ExecuteFunction(std::string const& luaFuncName, TEN::Control::Volumes::VolumeTriggerer, std::string const& arguments) = 0; virtual void ExecuteFunction(std::string const& luaFuncName, short idOne, short idTwo = 0) = 0; virtual void GetVariables(std::vector & vars) = 0; virtual void SetVariables(std::vector const& vars) = 0; + + virtual void GetCallbackStrings(std::vector & preControl, std::vector & postControl) const = 0; + virtual void SetCallbackStrings(std::vector const & preControl, std::vector const & postControl) = 0; }; extern ScriptInterfaceGame* g_GameScript; diff --git a/TombEngine/Scripting/Internal/LuaHandler.cpp b/TombEngine/Scripting/Internal/LuaHandler.cpp index 6b5b73aba..78bd0bf4f 100644 --- a/TombEngine/Scripting/Internal/LuaHandler.cpp +++ b/TombEngine/Scripting/Internal/LuaHandler.cpp @@ -4,7 +4,6 @@ LuaHandler::LuaHandler(sol::state* lua) : m_lua{ lua } { - ResetGlobals(); } void LuaHandler::ResetGlobals() @@ -26,7 +25,7 @@ void LuaHandler::ExecuteScript(std::string const& luaFilename) { } } -void LuaHandler::ExecuteString(std::string const & command) { +void LuaHandler::ExecuteString(std::string const& command) { auto result = m_lua->safe_script(command, sol::environment(m_lua->lua_state(), sol::create, m_lua->globals()), sol::script_pass_on_error); if (!result.valid()) { diff --git a/TombEngine/Scripting/Internal/ReservedScriptNames.h b/TombEngine/Scripting/Internal/ReservedScriptNames.h index 830593fb1..0eba94c41 100644 --- a/TombEngine/Scripting/Internal/ReservedScriptNames.h +++ b/TombEngine/Scripting/Internal/ReservedScriptNames.h @@ -19,6 +19,8 @@ static constexpr char ScriptReserved_SoundSource[] = "SoundSource"; static constexpr char ScriptReserved_AIObject[] = "AIObject"; static constexpr char ScriptReserved_DisplayString[] = "DisplayString"; static constexpr char ScriptReserved_Vec3[] = "Vec3"; +static constexpr char ScriptReserved_Rotation[] = "Rotation"; +static constexpr char ScriptReserved_LevelFunc[] = "LevelFunc"; // Member functions static constexpr char ScriptReserved_New[] = "New"; @@ -99,6 +101,10 @@ static constexpr char ScriptReserved_SetTitleScreenImagePath[] = "SetTitleScreen static constexpr char ScriptReserved_SetFarView[] = "SetFarView"; static constexpr char ScriptReserved_SetSettings[] = "SetSettings"; static constexpr char ScriptReserved_SetAnimations[] = "SetAnimations"; +static constexpr char ScriptReserved_EndLevel[] = "EndLevel"; +static constexpr char ScriptReserved_GetSecretCount[] = "GetSecretCount"; +static constexpr char ScriptReserved_SetSecretCount[] = "SetSecretCount"; +static constexpr char ScriptReserved_AddSecret[] = "AddSecret"; // Flow Functions static constexpr char ScriptReserved_SetStrings[] = "SetStrings"; @@ -136,6 +142,9 @@ static constexpr char ScriptReserved_ScreenToPercent[] = "ScreenToPercent"; static constexpr char ScriptReserved_PercentToScreen[] = "PercentToScreen"; static constexpr char ScriptReserved_HasLineOfSight[] = "HasLineOfSight"; +static constexpr char ScriptReserved_AddCallback[] = "AddCallback"; +static constexpr char ScriptReserved_RemoveCallback[] = "RemoveCallback"; + static constexpr char ScriptReserved_EmitParticle[] = "EmitParticle"; static constexpr char ScriptReserved_EmitLightningArc[] = "EmitLightningArc"; static constexpr char ScriptReserved_EmitShockwave[] = "EmitShockwave"; @@ -161,8 +170,9 @@ static constexpr char ScriptReserved_KeyClear[] = "KeyClear"; static constexpr char ScriptReserved_ObjID[] = "ObjID"; static constexpr char ScriptReserved_BlendID[] = "BlendID"; static constexpr char ScriptReserved_DisplayStringOption[] = "DisplayStringOption"; +static constexpr char ScriptReserved_CallbackPoint[] = "CallbackPoint"; static constexpr char ScriptReserved_LevelVars[] = "LevelVars"; static constexpr char ScriptReserved_GameVars[] = "GameVars"; static constexpr char ScriptReserved_LevelFuncs[] = "LevelFuncs"; - +static constexpr char ScriptReserved_Engine[] = "Engine"; diff --git a/TombEngine/Scripting/Internal/ScriptUtil.h b/TombEngine/Scripting/Internal/ScriptUtil.h index 49715c7f1..0700dfbeb 100644 --- a/TombEngine/Scripting/Internal/ScriptUtil.h +++ b/TombEngine/Scripting/Internal/ScriptUtil.h @@ -37,17 +37,19 @@ template using TypeOrNil = std::variant -void MakeSpecialTable(sol::state * state, std::string const & name, funcIndex const & fi, funcNewindex const & fni) +sol::table MakeSpecialTable(sol::state * state, std::string const & name, funcIndex const & fi, funcNewindex const & fni) { auto meta = MakeSpecialTableBase(state, name); meta.set_function("__index", fi); meta.set_function("__newindex", fni); + return (*state)[name]; } template -void MakeSpecialTable(sol::state * state, std::string const & name, funcIndex const & fi, funcNewindex const & fni, ObjPtr objPtr) +sol::table MakeSpecialTable(sol::state * state, std::string const & name, funcIndex const & fi, funcNewindex const & fni, ObjPtr objPtr) { auto meta = MakeSpecialTableBase(state, name); meta.set_function("__index", fi, objPtr); meta.set_function("__newindex", fni, objPtr); + return (*state)[name]; } diff --git a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp index d80965a6a..daa8f948c 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.cpp @@ -8,8 +8,10 @@ #include "Flow/InventoryItem/InventoryItem.h" #include "InventorySlots.h" #include "Game/gui.h" +#include "Logic/LevelFunc.h" #include "Vec3/Vec3.h" #include "Objects/ScriptInterfaceObjectsHandler.h" +#include "Strings/ScriptInterfaceStringsHandler.h" #include "Specific/trutils.h" /*** @@ -61,6 +63,35 @@ Returns the level that the game control is running in that moment. */ table_flow.set_function(ScriptReserved_GetCurrentLevel, &FlowHandler::GetCurrentLevel, this); +/*** EndLevel. +Finish level, with optional level index provided. If level index is not provided or is zero, jumps +to next level. If level index is more than level count, jumps to title. +@function EndLevel +@tparam int index (optional) level index. +*/ + table_flow.set_function(ScriptReserved_EndLevel, &FlowHandler::EndLevel, this); + +/*** GetSecretCount. +Returns current game secret count. +@function GetSecretCount +@treturn Current game secret count. +*/ + table_flow.set_function(ScriptReserved_GetSecretCount, &FlowHandler::GetSecretCount, this); + +/*** SetSecretCount. +Sets current secret count, overwriting existing one. +@function SetSecretCount +@tparam int count new secret count. +*/ + table_flow.set_function(ScriptReserved_SetSecretCount, &FlowHandler::SetSecretCount, this); + +/*** AddSecret. +Adds one secret to game secret count and also plays secret music track. +@function AddSecret +@tparam int index an index of current level's secret (must be from 0 to 7). +*/ + table_flow.set_function(ScriptReserved_AddSecret, &FlowHandler::AddSecret, this); + /*** Image to show when loading the game. Must be a .jpg or .png image. @function SetIntroImagePath @@ -127,7 +158,7 @@ Specify which translations in the strings table correspond to which languages. Animations::Register(table_flow); Settings::Register(table_flow); Fog::Register(table_flow); - + m_handler.MakeReadOnlyTable(table_flow, ScriptReserved_WeatherType, kWeatherTypes); m_handler.MakeReadOnlyTable(table_flow, ScriptReserved_LaraType, kLaraTypes); m_handler.MakeReadOnlyTable(table_flow, ScriptReserved_InvItem, kInventorySlots); @@ -231,6 +262,47 @@ int FlowHandler::GetLevelNumber(std::string const& fileName) return -1; } +void FlowHandler::EndLevel(std::optional nextLevel) +{ + int index = (nextLevel.has_value() && nextLevel.value() != 0) ? nextLevel.value() : CurrentLevel + 1; + LevelComplete = index; +} + +int FlowHandler::GetSecretCount() const +{ + return Statistics.Game.Secrets; +} + +void FlowHandler::SetSecretCount(int secretsNum) +{ + if (secretsNum > UCHAR_MAX) + return; + + Statistics.Game.Secrets = secretsNum; +} + +void FlowHandler::AddSecret(int levelSecretIndex) +{ + if (levelSecretIndex > 7) + { + TENLog("Current maximum amount of secrets per level is 7.", LogLevel::Warning); + return; + } + + if (!(Statistics.Level.Secrets & (1 << levelSecretIndex))) + { + if (Statistics.Game.Secrets >= UCHAR_MAX) + { + TENLog("Maximum amount of game secrets is already reached!", LogLevel::Warning); + return; + } + + PlaySecretTrack(); + Statistics.Level.Secrets |= (1 << levelSecretIndex); + Statistics.Game.Secrets++; + } +} + bool FlowHandler::IsFlyCheatEnabled() const { return FlyCheat; @@ -264,8 +336,9 @@ bool FlowHandler::DoFlow() } catch (TENScriptException const& e) { - std::string msg = std::string{ "An unrecoverable error occurred in " } + __func__ + ": " + e.what(); + std::string msg = std::string{ "A Lua error occurred while running the title level; " } + __func__ + ": " + e.what(); TENLog(msg, LogLevel::Error, LogConfig::All); + ShutdownTENLog(); throw; } } @@ -297,7 +370,7 @@ bool FlowHandler::DoFlow() } catch (TENScriptException const& e) { - std::string msg = std::string{ "An unrecoverable error occurred in " } + __func__ + ": " + e.what(); + std::string msg = std::string{ "A Lua error occurred while running a level; " } + __func__ + ": " + e.what(); TENLog(msg, LogLevel::Error, LogConfig::All); status = GameStatus::ExitToTitle; } diff --git a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h index 53a510e46..e0386cfd9 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h +++ b/TombEngine/Scripting/Internal/TEN/Flow/FlowHandler.h @@ -49,6 +49,10 @@ public: Level* GetCurrentLevel(); int GetLevelNumber(std::string const& flieName); int GetNumLevels() const; + void EndLevel(std::optional nextLevel); + int GetSecretCount() const; + void SetSecretCount(int secretsNum); + void AddSecret(int levelSecretIndex); void SetIntroImagePath(std::string const& path); void SetTitleScreenImagePath(std::string const& path); bool IsFlyCheatEnabled() const; diff --git a/TombEngine/Scripting/Internal/TEN/Flow/Fog/Fog.cpp b/TombEngine/Scripting/Internal/TEN/Flow/Fog/Fog.cpp index cae08ef39..cd6bbc0d2 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/Fog/Fog.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/Fog/Fog.cpp @@ -40,7 +40,7 @@ void Fog::Register(sol::table & parent) @tparam int Min Distance fog starts (in Sectors) @tparam int Max Distance fog ends (in Sectors) @return A fog object. -@function Fog.new +@function Fog */ Fog::Fog(ScriptColor const& col, short minDistance, short maxDistance) { diff --git a/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp b/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp index 132e5f5d1..9bbcdbf4a 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.cpp @@ -11,7 +11,7 @@ These are things things which aren't present in the compiled level file itself. */ /*** Make a new Level object. - @function Level.new + @function Level @return a Level object */ void Level::Register(sol::table & parent) @@ -62,13 +62,6 @@ void Level::Register(sol::table & parent) //@mem horizon "horizon", &Level::Horizon, -/// (bool) Enable smooth transition from horizon graphic to sky layer. -// If set to false, there will be a black band between the two. -// -// __(not yet implemented)__ -//@mem colAddHorizon - "colAddHorizon", &Level::ColAddHorizon, - /// (bool) Enable flickering lightning in the sky. // Equivalent to classic TRLE's LIGHTNING setting. As in the TRC Ireland levels. // diff --git a/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.h b/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.h index eba71e1b2..d0a4717b4 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.h +++ b/TombEngine/Scripting/Internal/TEN/Flow/Level/FlowLevel.h @@ -29,7 +29,6 @@ struct Level : public ScriptInterfaceLevel std::string AmbientTrack; SkyLayer Layer1; SkyLayer Layer2; - bool ColAddHorizon{ false }; Fog Fog; bool Storm{ false }; WeatherType Weather{ WeatherType::None }; diff --git a/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.cpp b/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.cpp index 269e554a0..bcb0e5de2 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/Settings/Settings.cpp @@ -15,19 +15,20 @@ void Settings::Register(sol::table & parent) /*** How should the application respond to script errors? Must be one of the following: -`ErrorMode.TERMINATE` - print to the log file and terminate the application when any script error is hit. +`ErrorMode.TERMINATE` - print to the log file and return to the title level when any script error is hit. This is the one you will want to go for if you want to know IMMEDIATELY if something has gone wrong. `ErrorMode.WARN` - print to the log file and continue running the application when a recoverable script error is hit. -Choose this one if terminating the application is too much for you. Note that unrecoverable errors will still terminate -the application. +Choose this one if booting to the title level is too much for you. `ErrorMode.SILENT` - do nothing when a recoverable script error is hit. Think __very__ carefully before using this setting. These error modes are here to help you to keep your scripts working properly, but if you opt to ignore errors, you won't be alerted if you've misused a function or passed an invalid argument. -As with `ErrorMode.WARN`, unrecoverable errors will still terminate the application. +In all of these modes, an *unrecoverable* error will boot you to the title level. If the title level itself +has an unrecoverable error, the game will close. + @mem errorMode */ "errorMode", &Settings::ErrorMode diff --git a/TombEngine/Scripting/Internal/TEN/Flow/SkyLayer/SkyLayer.cpp b/TombEngine/Scripting/Internal/TEN/Flow/SkyLayer/SkyLayer.cpp index a46442134..3bc4271ee 100644 --- a/TombEngine/Scripting/Internal/TEN/Flow/SkyLayer/SkyLayer.cpp +++ b/TombEngine/Scripting/Internal/TEN/Flow/SkyLayer/SkyLayer.cpp @@ -37,7 +37,7 @@ Less is more. City of The Dead, for example, uses a speed value of 16. @tparam Color color RGB color @tparam int speed cloud speed @return A SkyLayer object. -@function SkyLayer.new +@function SkyLayer */ SkyLayer::SkyLayer(ScriptColor const& col, short speed) { diff --git a/TombEngine/Scripting/Internal/TEN/Inventory/InventoryHandler.cpp b/TombEngine/Scripting/Internal/TEN/Inventory/InventoryHandler.cpp index 33e4f699b..51330685c 100644 --- a/TombEngine/Scripting/Internal/TEN/Inventory/InventoryHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Inventory/InventoryHandler.cpp @@ -13,30 +13,60 @@ Inventory manipulation namespace InventoryHandler { + ///Add x of an item to the inventory. + //Omitting the second argument will give the "default" amount of the item + //(i.e. the amount the player would get from a pickup of that type). + //For example, giving crossbow ammo without specifying the number would give the player + //10 instead. + //Has no effect if the player has an infinite number of that item. + //@function GiveItem + //@tparam InvID item the item to be added + //@int[opt] count the number of items to add (default: the amount you would get from a pickup) static void InventoryAdd(ItemEnumPair slot, sol::optional count) { - // If 0 is passed in, then the amount added will be the default amount + // If nil is passed in, then the amount added will be the default amount // for that pickup - i.e. the amount you would get from picking up the // item in-game (e.g. 1 for medipacks, 12 for flares). - PickedUpObject(slot.m_pair.first, count.value_or(0)); + + // can't use value_or(std::nullopt) here because nullopt isn't an int + if (count.has_value()) + PickedUpObject(slot.m_pair.first, count.value()); + else + PickedUpObject(slot.m_pair.first, std::nullopt); } + ///Remove x of a certain item from the inventory. + //As in @{GiveItem}, omitting the count will remove the "default" amount of that item. + //Has no effect if the player has an infinite number of the item. + //@function TakeItem + //@tparam InvID item the item to be removed + //@int[opt] count the number of items to remove (default: the amount you would get from a pickup) static void InventoryRemove(ItemEnumPair slot, sol::optional count) { - // 0 is default for the same reason as in InventoryAdd. - RemoveObjectFromInventory(slot.m_pair.first, count.value_or(0)); + //can't use value_or(std::nullopt) here because nullopt isn't an int + if (count.has_value()) + RemoveObjectFromInventory(slot.m_pair.first, count.value()); + else + RemoveObjectFromInventory(slot.m_pair.first, std::nullopt); } + ///Set the amount of a certain item the player has in the inventory. + //Similar to @{GiveItem} but replaces with the new amount instead of adding it. + //@function SetItemCount + //@tparam InvID item the ID of the item to be set. + //@tparam int count the number of items the player will have. A value of -1 will give an infinite amount of that item. static int InventoryGetCount(ItemEnumPair slot) { return GetInventoryCount(slot.m_pair.first); } + ///Get the amount the player holds of an item. + //@function GetItemCount + //@tparam InvID item the ID item to check + //@treturn int the amount of the item the player has in the inventory. -1 indicates an infinite amount of that item. static void InventorySetCount(ItemEnumPair slot, int count) { - // add the amount we'd need to add to get to count - int currAmt = GetInventoryCount(slot.m_pair.first); - InventoryAdd(slot, count - currAmt); + SetInventoryCount(slot.m_pair.first, count); } static void InventoryCombine(int slot1, int slot2) @@ -54,37 +84,9 @@ namespace InventoryHandler sol::table table_inventory{ state->lua_state(), sol::create }; parent.set(ScriptReserved_Inventory, table_inventory); - ///Add x of an item to the inventory. - //A count of 0 will add the "default" amount of that item - //(i.e. the amount the player would get from a pickup of that type). - //For example, giving "zero" crossbow ammo would give the player - //10 instead, whereas giving "zero" medkits would give the player 1 medkit. - //@function GiveItem - //@tparam InvID item the item to be added - //@tparam int count the number of items to add (default: 0) table_inventory.set_function(ScriptReserved_GiveInvItem, &InventoryAdd); - - - //Remove x of a certain item from the inventory. - //As in @{GiveItem}, a count of 0 will remove the "default" amount of that item. - //@function TakeItem - //@tparam InvID item the item to be removed - //@tparam int count the number of items to remove (default: 0) table_inventory.set_function(ScriptReserved_TakeInvItem, &InventoryRemove); - - - ///Get the amount the player holds of an item. - //@function GetItemCount - //@tparam InvID item the ID item to check - //@treturn int the amount of the item the player has in the inventory table_inventory.set_function(ScriptReserved_GetInvItemCount, &InventoryGetCount); - - - ///Set the amount of a certain item the player has in the inventory. - //Similar to @{GiveItem} but replaces with the new amount instead of adding it. - //@function SetItemCount - //@tparam @{InvID} item the ID of the item to be set - //@tparam int count the number of items the player will have table_inventory.set_function(ScriptReserved_SetInvItemCount, &InventorySetCount); } } diff --git a/TombEngine/Scripting/Internal/TEN/Logic/LevelFunc.h b/TombEngine/Scripting/Internal/TEN/Logic/LevelFunc.h new file mode 100644 index 000000000..e7399e1ee --- /dev/null +++ b/TombEngine/Scripting/Internal/TEN/Logic/LevelFunc.h @@ -0,0 +1,33 @@ +#pragma once +#include "LogicHandler.h" +#include "ReservedScriptNames.h" + +// Why do we need this class? +// We need a way to save and load functions in a way that remembers exactly what "path" they have in the LevelFuncs table hierarchy. +// Thus, even if we use Lua to put a LevelFuncs function in a variable with another, shorter name, we can still pass it into +// LevelVars and have it remember the right function name when loaded. +// This is needed for things like Timers, which call a certain function after a certain amount of time. If we save and then load, +// the FuncNameHolder will be able to use its path as the key to find the actual Lua function to call at the end of the timer. + +// The alternative would be to pass in a full string, but then we would need to split the string at runtime to find +// the exact tables to look in, which seems like it would take longer. + +class LevelFunc { +public: + std::string m_funcName; + LogicHandler* m_handler; + sol::protected_function_result Call(sol::variadic_args vs) + { + return m_handler->CallLevelFunc(m_funcName, vs); + } + sol::protected_function_result CallDT(float dt) + { + return m_handler->CallLevelFunc(m_funcName, dt); + } + + static void Register(sol::table & parent) + { + parent.new_usertype(ScriptReserved_LevelFunc, sol::no_constructor, sol::meta_function::call, &Call); + } +}; + diff --git a/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp b/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp index 2d7f28eaf..4338ca1b4 100644 --- a/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp +++ b/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.cpp @@ -1,6 +1,7 @@ #include "framework.h" #include "LogicHandler.h" +#include #include "ScriptAssert.h" #include "Game/savegame.h" #include "Sound/sound.h" @@ -8,6 +9,8 @@ #include "Game/effects/lightning.h" #include "ScriptUtil.h" #include "Objects/Moveable/MoveableObject.h" +#include "Vec3/Vec3.h" +#include "LevelFunc.h" using namespace TEN::Effects::Lightning; @@ -17,6 +20,20 @@ Saving data, triggering functions, and callbacks for level-specific scripts. @pragma nostrip */ +enum class CallbackPoint +{ + PreControl, + PostControl, +}; + +static const std::unordered_map kCallbackPoints +{ + {"PRECONTROLPHASE", CallbackPoint::PreControl}, + {"POSTCONTROLPHASE", CallbackPoint::PostControl}, +}; + +static constexpr char const* strKey = "__internal_name"; + void SetVariable(sol::table tab, sol::object key, sol::object value) { switch (value.get_type()) @@ -63,42 +80,180 @@ sol::object GetVariable(sol::table tab, sol::object key) LogicHandler::LogicHandler(sol::state* lua, sol::table & parent) : m_handler{ lua } { m_handler.GetState()->set_function("print", &LogicHandler::LogPrint, this); + + sol::table table_logic{ m_handler.GetState()->lua_state(), sol::create }; + + parent.set(ScriptReserved_Logic, table_logic); + + table_logic.set_function(ScriptReserved_AddCallback, &LogicHandler::AddCallback, this); + table_logic.set_function(ScriptReserved_RemoveCallback, &LogicHandler::RemoveCallback, this); + + m_handler.MakeReadOnlyTable(table_logic, ScriptReserved_CallbackPoint, kCallbackPoints); + + LevelFunc::Register(table_logic); + ResetScripts(true); } void LogicHandler::ResetGameTables() { - MakeSpecialTable(m_handler.GetState(), ScriptReserved_GameVars, &GetVariable, &SetVariable); + auto state = m_handler.GetState(); + MakeSpecialTable(state, ScriptReserved_GameVars, &GetVariable, &SetVariable); + + (*state)[ScriptReserved_GameVars][ScriptReserved_Engine] = sol::table{ *state, sol::create }; } +/*** Register a function as a callback. +Possible values for CallbackPoint: + PRECONTROLPHASE -- will be called immediately before OnControlPhase + POSTCONTROLPHASE -- will be called immediately after OnControlPhase + +The order in which two functions with the same CallbackPoint are called is undefined. +i.e. if you register `MyFunc` and `MyFunc2` with `PRECONTROLPHASE`, both will be called before `OnControlPhase`, but there is no guarantee whether `MyFunc` will be called before `MyFunc2`, or vice-versa. + +Any returned value will be discarded. + +@function AddCallback +@tparam point CallbackPoint When should the callback be called? +@tparam function func The function to be called (must be in the LevelFuncs hierarchy). Will receive, as an argument, the time in seconds since the last frame. +@usage + LevelFuncs.MyFunc = function(dt) print(dt) end + TEN.Logic.AddCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, LevelFuncs.MyFunc) +*/ +void LogicHandler::AddCallback(CallbackPoint point, LevelFunc const & lf) +{ + switch(point) + { + case CallbackPoint::PreControl: + m_callbacksPreControl.insert(lf.m_funcName); + break; + + case CallbackPoint::PostControl: + m_callbacksPostControl.insert(lf.m_funcName); + break; + } +} + +/*** Deregister a function as a callback. +Will have no effect if the function was not registered as a callback + +@function RemoveCallback +@tparam point CallbackPoint The callback point the function was registered with. See @{AddCallback} +@tparam func LevelFunc the function to remove; must be in the LevelFuncs hierarchy. +@usage + TEN.Logic.RemoveCallback(TEN.Logic.CallbackPoint.PRECONTROLPHASE, "MyFunc") +*/ +void LogicHandler::RemoveCallback(CallbackPoint point, LevelFunc const & lf) +{ + switch(point) + { + case CallbackPoint::PreControl: + m_callbacksPreControl.erase(lf.m_funcName); + break; + + case CallbackPoint::PostControl: + m_callbacksPostControl.erase(lf.m_funcName); + break; + } +} void LogicHandler::ResetLevelTables() { - MakeSpecialTable(m_handler.GetState(), ScriptReserved_LevelFuncs, &LogicHandler::GetLevelFunc, &LogicHandler::SetLevelFunc, this); - MakeSpecialTable(m_handler.GetState(), ScriptReserved_LevelVars, &GetVariable, &SetVariable); + auto state = m_handler.GetState(); + MakeSpecialTable(state, ScriptReserved_LevelVars, &GetVariable, &SetVariable); + + (*state)[ScriptReserved_LevelVars][ScriptReserved_Engine] = sol::table{ *state, sol::create }; } -sol::protected_function LogicHandler::GetLevelFunc(sol::table tab, std::string const& luaName) +sol::object LogicHandler::GetLevelFuncsMember(sol::table tab, std::string const& luaName) { - if (m_levelFuncs.find(luaName) == m_levelFuncs.end()) - return sol::lua_nil; + std::string partName = tab.raw_get(strKey); + auto & theMap = m_levelFuncs_tablesOfNames[partName]; - return m_levelFuncs.at(luaName); -} - -bool LogicHandler::SetLevelFunc(sol::table tab, std::string const& luaName, sol::object value) -{ - switch (value.get_type()) + auto fullNameIt = theMap.find(luaName); + if (fullNameIt != std::cend(theMap)) { - case sol::type::lua_nil: - m_levelFuncs.erase(luaName); - break; - case sol::type::function: - m_levelFuncs.insert_or_assign(luaName, value.as()); - break; - default: - std::string error{ "Could not assign LevelFuncs." }; - error += luaName + "; it must be a function (or nil)."; + std::string_view key = fullNameIt->second; + if (m_levelFuncs_levelFuncObjects[key].valid()) + return m_levelFuncs_levelFuncObjects[key]; + } + return sol::nil; +} + +sol::protected_function_result LogicHandler::CallLevelFunc(std::string const & name, float dt) +{ + sol::protected_function f = m_levelFuncs_luaFunctions[name]; + auto r = f.call(dt); + + if (!r.valid()) + { + sol::error err = r; + ScriptAssertF(false, "Could not execute function {}: {}", name, err.what()); + } + + return r; +} + +sol::protected_function_result LogicHandler::CallLevelFunc(std::string const & name, sol::variadic_args va) +{ + sol::protected_function f = m_levelFuncs_luaFunctions[name]; + auto r = f.call(va); + if (!r.valid()) + { + sol::error err = r; + ScriptAssertF(false, "Could not execute function {}: {}", name, err.what()); + } + + return r; +} + +bool LogicHandler::SetLevelFuncsMember(sol::table tab, std::string const& luaName, sol::object value) +{ + if (sol::type::lua_nil == value.get_type()) + { + std::string error{ "Tried to set LevelFuncs member " }; + error += luaName + " to nil; this not permitted at this time."; + return ScriptAssert(false, error); + } + else if (sol::type::function == value.get_type()) + { + // Add the name to the table of names + auto partName = tab.raw_get(strKey); + auto fullName = partName + "." + luaName; + auto& parentNameTab = m_levelFuncs_tablesOfNames[partName]; + parentNameTab.insert_or_assign(luaName, fullName); + + // Create a LevelFunc userdata and add that too + LevelFunc levelFuncObject; + levelFuncObject.m_funcName = fullName; + levelFuncObject.m_handler = this; + m_levelFuncs_levelFuncObjects[fullName] = levelFuncObject; + + // Add the function itself + m_levelFuncs_luaFunctions[fullName] = value; + } + else if (sol::type::table == value.get_type()) + { + // Create and add a new name map + std::unordered_map newNameMap; + auto fullName = tab.raw_get(strKey) + "." + luaName; + m_levelFuncs_tablesOfNames.insert_or_assign(fullName, newNameMap); + + // Create a new table to put in the LevelFuncs hierarchy + auto newLevelFuncsTab = MakeSpecialTable(m_handler.GetState(), luaName, &LogicHandler::GetLevelFuncsMember, &LogicHandler::SetLevelFuncsMember, this); + newLevelFuncsTab.raw_set(strKey, fullName); + tab.raw_set(luaName, newLevelFuncsTab); + + // "populate" the new table. This will trigger the __newindex metafunction and will + // thus call this function recursively, handling all subtables and functions. + for (auto& [key, val] : value.as()) + { + newLevelFuncsTab[key] = val; + } + } + else{ + std::string error{ "Failed to add " }; + error += luaName + " to LevelFuncs or one of its tables; it must be a function or a table of functions."; return ScriptAssert(false, error); } return true; @@ -121,21 +276,36 @@ void LogicHandler::ResetScripts(bool clearGameVars) { FreeLevelScripts(); + m_callbacksPreControl.clear(); + m_callbacksPostControl.clear(); + auto currentPackage = m_handler.GetState()->get("package"); auto currentLoaded = currentPackage.get("loaded"); - for(auto & [first, second] : currentLoaded) + for (auto& [first, second] : currentLoaded) currentLoaded[first] = sol::nil; - if(clearGameVars) + if (clearGameVars) ResetGameTables(); m_handler.ResetGlobals(); + + m_handler.GetState()->collect_garbage(); } void LogicHandler::FreeLevelScripts() { - m_levelFuncs.clear(); + m_levelFuncs = MakeSpecialTable(m_handler.GetState(), ScriptReserved_LevelFuncs, &LogicHandler::GetLevelFuncsMember, &LogicHandler::SetLevelFuncsMember, this); + m_levelFuncs.raw_set(strKey, ScriptReserved_LevelFuncs); + + m_levelFuncs[ScriptReserved_Engine] = sol::table{ *m_handler.GetState(), sol::create }; + + m_levelFuncs_tablesOfNames.clear(); + m_levelFuncs_luaFunctions.clear(); + m_levelFuncs_levelFuncObjects = sol::table{ *m_handler.GetState(), sol::create }; + + m_levelFuncs_tablesOfNames.emplace(std::make_pair(ScriptReserved_LevelFuncs, std::unordered_map{})); + ResetLevelTables(); m_onStart = sol::nil; m_onLoad = sol::nil; @@ -145,34 +315,6 @@ void LogicHandler::FreeLevelScripts() m_handler.GetState()->collect_garbage(); } -void JumpToLevel(int levelNum) -{ - if (levelNum >= g_GameFlow->GetNumLevels()) - return; - - LevelComplete = levelNum; -} - -int GetSecretsCount() -{ - return Statistics.Level.Secrets; -} - -void SetSecretsCount(int secretsNum) -{ - if (secretsNum > 255) - return; - Statistics.Level.Secrets = secretsNum; -} - -void AddOneSecret() -{ - if (Statistics.Level.Secrets >= 255) - return; - Statistics.Level.Secrets++; - PlaySecretTrack(); -} - void LogicHandler::SetVariables(std::vector const & vars) { ResetGameTables(); @@ -212,6 +354,18 @@ void LogicHandler::SetVariables(std::vector const & vars) solTables[i][vars[first]] = vars[second]; } } + else if (std::holds_alternative(vars[second])) + { + auto theVec = Vec3{ std::get(vars[second]) }; + solTables[i][vars[first]] = theVec; + } + else if (std::holds_alternative(vars[second])) + { + LevelFunc fnh; + fnh.m_funcName = std::get(vars[second]).name; + fnh.m_handler = this; + solTables[i][vars[first]] = fnh; + } else { solTables[i][vars[first]] = vars[second]; @@ -244,12 +398,20 @@ void LogicHandler::GetVariables(std::vector & vars) std::unordered_map varsMap; std::unordered_map numMap; std::unordered_map boolMap; + size_t nVars = 0; + + // The following functions will all try to put their values in a map. If it succeeds + // then the value was not already in the map, so we can put it into the var vector. + // If it fails, the value is in the map, and thus will also be in the var vector. + // We then return the value's position in the var vector. + + // The purpose of this is to only store each value once, and to fill our tables with + // indices to the values rather than copies of the values. auto handleNum = [&](double num) { - auto [first, second] = numMap.insert(std::pair(num, nVars)); + auto [first, second] = numMap.insert(std::make_pair(num, nVars)); - // true if the var was inserted if (second) { vars.push_back(num); @@ -261,9 +423,8 @@ void LogicHandler::GetVariables(std::vector & vars) auto handleBool = [&](bool num) { - auto [first, second] = boolMap.insert(std::pair(num, nVars)); + auto [first, second] = boolMap.insert(std::make_pair(num, nVars)); - // true if the var was inserted if (second) { vars.push_back(num); @@ -276,9 +437,8 @@ void LogicHandler::GetVariables(std::vector & vars) auto handleStr = [&](sol::object const& obj) { auto str = obj.as(); - auto [first, second] = varsMap.insert(std::pair(str.data(), nVars)); + auto [first, second] = varsMap.insert(std::make_pair(str.data(), nVars)); - // true if the string was inserted if (second) { vars.push_back(std::string{ str.data() }); @@ -288,11 +448,37 @@ void LogicHandler::GetVariables(std::vector & vars) return first->second; }; + auto handleVec3 = [&](Vec3 const & vec) + { + auto [first, second] = varsMap.insert(std::make_pair(&vec, nVars)); + + if (second) + { + vars.push_back(vec); + ++nVars; + } + + return first->second; + }; + + auto handleFuncName = [&](LevelFunc const& fnh) + { + //auto str = obj.as(); + auto [first, second] = varsMap.insert(std::make_pair(&fnh, nVars)); + + if (second) + { + vars.push_back(FuncName{ std::string{ fnh.m_funcName } }); + ++nVars; + } + + return first->second; + }; + std::function populate = [&](sol::table const & obj) { - auto [first, second] = varsMap.insert(std::pair(obj.pointer(), nVars)); + auto [first, second] = varsMap.insert(std::make_pair(obj.pointer(), nVars)); - // true if the table was inserted if(second) { ++nVars; @@ -316,24 +502,35 @@ void LogicHandler::GetVariables(std::vector & vars) ScriptAssert(false, "Tried saving an unsupported type as a key"); } + auto putInVars = [&vars, id, keyIndex](uint32_t valIndex) + { + std::get(vars[id]).push_back(std::make_pair(keyIndex, valIndex)); + }; + uint32_t valIndex = 0; switch (second.get_type()) { case sol::type::table: - valIndex = populate(second.as()); - std::get(vars[id]).push_back(std::make_pair(keyIndex, valIndex)); + putInVars(populate(second.as())); break; case sol::type::string: - valIndex = handleStr(second); - std::get(vars[id]).push_back(std::make_pair(keyIndex, valIndex)); + putInVars(handleStr(second)); break; case sol::type::number: - valIndex = handleNum(second.as()); - std::get(vars[id]).push_back(std::make_pair(keyIndex, valIndex)); + putInVars(handleNum(second.as())); break; case sol::type::boolean: - valIndex = handleBool(second.as()); - std::get(vars[id]).push_back(std::make_pair(keyIndex, valIndex)); + putInVars(handleBool(second.as())); + break; + case sol::type::userdata: + { + if(second.is()) + putInVars(handleVec3(second.as())); + else if(second.is()) + putInVars(handleFuncName(second.as())); + else + ScriptAssert(false, "Tried saving an unsupported userdata as a value"); + } break; default: ScriptAssert(false, "Tried saving an unsupported type as a value"); @@ -345,6 +542,24 @@ void LogicHandler::GetVariables(std::vector & vars) populate(tab); } +void LogicHandler::GetCallbackStrings(std::vector& preControl, std::vector& postControl) const +{ + for (auto const& s : m_callbacksPreControl) + preControl.push_back(s); + + for (auto const& s : m_callbacksPostControl) + postControl.push_back(s); +} + +void LogicHandler::SetCallbackStrings(std::vector const & preControl, std::vector const & postControl) +{ + for (auto const& s : preControl) + m_callbacksPreControl.insert(s); + + for (auto const& s : postControl) + m_callbacksPostControl.insert(s); +} + template std::unique_ptr GetByName(std::string const & type, std::string const & name, mapType const & map) { @@ -369,35 +584,30 @@ void LogicHandler::ExecuteScriptFile(const std::string & luaFilename) m_handler.ExecuteScript(luaFilename); } +void LogicHandler::ExecuteString(const std::string& command) +{ + m_handler.ExecuteString(command); +} + +// These wind up calling CallLevelFunc, which is where all the error checking is. void LogicHandler::ExecuteFunction(std::string const& name, short idOne, short idTwo) { - sol::protected_function_result r; - sol::protected_function func = (*m_handler.GetState())["LevelFuncs"][name.c_str()]; - r = func(std::make_unique(idOne), std::make_unique(idTwo)); - if (!r.valid()) - { - sol::error err = r; - ScriptAssertF(false, "Could not execute function {}: {}", name, err.what()); - } + sol::protected_function func = m_levelFuncs_luaFunctions[name]; + + func(std::make_unique(idOne), std::make_unique(idTwo)); + } void LogicHandler::ExecuteFunction(std::string const& name, TEN::Control::Volumes::VolumeTriggerer triggerer, std::string const& arguments) { - sol::protected_function_result r; sol::protected_function func = (*m_handler.GetState())["LevelFuncs"][name.c_str()]; if (std::holds_alternative(triggerer)) { - r = func(std::make_unique(std::get(triggerer), true), arguments); + func(std::make_unique(std::get(triggerer), true), arguments); } else { - r = func(nullptr, arguments); - } - - if (!r.valid()) - { - sol::error err = r; - ScriptAssertF(false, "Could not execute function {}: {}", name, err.what()); + func(nullptr, arguments); } } @@ -426,9 +636,25 @@ void LogicHandler::OnLoad() void LogicHandler::OnControlPhase(float dt) { + auto tryCall = [this, dt](std::string const& name) + { + auto func = m_handler.GetState()->script("return " + name); + + if (!func.valid()) + ScriptAssertF(false, "Callback {} not valid", name); + else + func.get().CallDT(dt); + }; + + for (auto& name : m_callbacksPreControl) + tryCall(name); + lua_gc(m_handler.GetState()->lua_state(), LUA_GCCOLLECT, 0); if(m_onControlPhase.valid()) doCallback(m_onControlPhase, dt); + + for (auto& name : m_callbacksPostControl) + tryCall(name); } void LogicHandler::OnSave() @@ -465,6 +691,9 @@ some time later, the values `3` will be put back into `LevelVars.enemiesKilled.` __This table is emptied when a level is finished.__ If the player needs to be able to return to the level (like in the Karnak and Alexandria levels in *The Last Revelation*), you will need to use the @{GameVars} table, below. + +__LevelVars.Engine is a reserved table used internally by TombEngine's libs. Do not modify, overwrite, or add to it.__ + @table LevelVars */ @@ -485,12 +714,15 @@ And in the script file for the level with the boss, you could write: end Unlike @{LevelVars}, this table will remain intact for the entirety of the game. + +__GameVars.Engine is a reserved table used internally by TombEngine's libs. Do not modify, overwrite, or add to it.__ + @table GameVars */ -/*** A table with level-specific functions. +/*** A table nested table system for level-specific functions. -This serves two purposes: it holds the level callbacks (listed below) as well as +This serves a few purposes: it holds the level callbacks (listed below) as well as any trigger functions you might have specified. For example, if you give a trigger a Lua name of "my_trigger" in Tomb Editor, you will have to implement it as a member of this table: @@ -499,6 +731,23 @@ of this table: -- implementation goes here end +You can organise functions into tables within the hierarchy: + + LevelFuncs.enemyFuncs = {} + + LevelFuncs.enemyFuncs.makeBaddyRunAway = function() + -- implementation goes here + end + + LevelFuncs.enemyFuncs.makeBaddyUseMedkit = function() + -- implementation goes here + end + +There are two special subtables which you should __not__ overwrite: + + LevelFuncs.Engine -- this is for 'first-party' functions, i.e. ones that come with TombEngine. + LevelFuncs.External -- this is for 'third-party' functions. If you write a library providing LevelFuncs functions for other builders to use in their levels, put those functions in LevelFuncs.External.YourLibraryNameHere + The following are the level callbacks. They are optional; if your level has no special behaviour for a particular scenario, you do not need to implement the function. For example, if your level does not need any special initialisation when it is loaded, @@ -525,10 +774,24 @@ void LogicHandler::InitCallbacks() { auto assignCB = [this](sol::protected_function& func, std::string const & luaFunc) { std::string fullName = "LevelFuncs." + luaFunc; - func = (*m_handler.GetState())["LevelFuncs"][luaFunc]; - std::string err{ "Level's script does not define callback " + fullName}; - if (!ScriptAssert(func.valid(), err)) { - ScriptWarn("Defaulting to no " + fullName + " behaviour."); + + sol::object theData = (*m_handler.GetState())["LevelFuncs"][luaFunc]; + + std::string msg{ "Level's script does not define callback " + fullName + + ". Defaulting to no " + fullName + " behaviour."}; + + if(!theData.valid()) + { + TENLog(msg); + return; + } + + LevelFunc fnh = (*m_handler.GetState())["LevelFuncs"][luaFunc]; + + func = m_levelFuncs_luaFunctions[fnh.m_funcName]; + + if (!func.valid()) { + TENLog(msg); } }; diff --git a/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.h b/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.h index db58965f6..24a4a74bf 100644 --- a/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.h +++ b/TombEngine/Scripting/Internal/TEN/Logic/LogicHandler.h @@ -3,41 +3,60 @@ #include "Game/items.h" #include "LuaHandler.h" #include -#include "Strings/StringsHandler.h" -struct LuaFunction { - std::string Name; - std::string Code; - bool Executed; -}; - -struct GameScriptVector3 { - float x; - float y; - float z; -}; - -struct LuaVariable -{ - bool IsGlobal; - std::string Name; - int Type; - float FloatValue; - int IntValue; - std::string StringValue; - bool BoolValue; -}; +class LevelFunc; +enum class CallbackPoint; class LogicHandler : public ScriptInterfaceGame { private: - std::unordered_map m_levelFuncs{}; + // Hierarchy of tables. + // + // For example: + // LevelFuncs + // |- Engine + // |- Timer + // |- Util + // |- External + // |- EnemiesBySteve + // |- BetterEnemiesByChris + // |- SubTable + // + // Each of these tables can only contain other tables as well as a string with their "path". + // For example, the SubTable table will have a path of "LevelFuncs.Ext.MySecondLib.SubTable". + // It uses this to construct the full path name of any functions that end up in m_levelFuncs_luaFunctions. + // + // Each of these has a metatable whose __index metamethod looks in m_levelFuncsTables, using the path + // as the key, for the full name of the function. It then gets the FuncNameHolder from m_levelFuncsFakeFuncs, + // and that FuncNameHolder's __call metamethod looks in m_levelFuncs_luaFunctions for the real function. + sol::table m_levelFuncs{}; + + // Maps full function paths into Lua functions. + std::unordered_map m_levelFuncs_luaFunctions{}; + + // Maps full function paths to LevelFunc objects. + // This is a table instead of a C++ container to more easily interface with Sol. + sol::table m_levelFuncs_levelFuncObjects{}; + + // Contains tables; each table refers to a table in the LevelFuncs hierarchy, and contains the full names + // of the functions to index in m_levelFuncs_luaFunctions. + // Tables are non-nested, so the following are all at the base level of m_levelFuncsTables. + // "LevelFuncs" + // "LevelFuncs.Engine" + // "LevelFuncs.Engine.Util" + // "LevelFuncs.MyLevel" + // "LevelFuncs.MyLevel.CoolFuncs" + std::unordered_map> m_levelFuncs_tablesOfNames{}; + sol::protected_function m_onStart{}; sol::protected_function m_onLoad{}; sol::protected_function m_onControlPhase{}; sol::protected_function m_onSave{}; sol::protected_function m_onEnd{}; + std::unordered_set m_callbacksPreControl; + std::unordered_set m_callbacksPostControl; + void ResetLevelTables(); void ResetGameTables(); LuaHandler m_handler; @@ -45,27 +64,39 @@ private: public: LogicHandler(sol::state* lua, sol::table & parent); + sol::protected_function_result CallLevelFunc(std::string const &, sol::variadic_args); + sol::protected_function_result CallLevelFunc(std::string const &, float dt); + void FreeLevelScripts() override; void LogPrint(sol::variadic_args va); - bool SetLevelFunc(sol::table tab, std::string const& luaName, sol::object value); + bool SetLevelFuncsMember(sol::table tab, std::string const& luaName, sol::object value); + + void AddCallback(CallbackPoint point, LevelFunc const & lf); + void RemoveCallback(CallbackPoint point, LevelFunc const & lf); + void ResetScripts(bool clearGameVars) override; - sol::protected_function GetLevelFunc(sol::table tab, std::string const& luaName); + sol::object GetLevelFuncsMember(sol::table tab, std::string const& luaName); void ExecuteScriptFile(const std::string& luaFilename) override; + void ExecuteString(const std::string& command) override; void ExecuteFunction(std::string const& name, TEN::Control::Volumes::VolumeTriggerer, std::string const& arguments) override; void ExecuteFunction(std::string const& name, short idOne, short idTwo) override; void GetVariables(std::vector& vars) override; + void SetVariables(std::vector const& vars) override; void ResetVariables(); - void SetVariables(std::vector const& vars) override; + void SetCallbackStrings(std::vector const& preControl, std::vector const& postControl) override; + void GetCallbackStrings(std::vector& preControl, std::vector& postControl) const override; + void InitCallbacks() override; void OnStart() override; void OnLoad() override; void OnControlPhase(float dt) override; void OnSave() override; void OnEnd() override; + }; diff --git a/TombEngine/Scripting/Internal/TEN/Misc/Miscellanous.cpp b/TombEngine/Scripting/Internal/TEN/Misc/Miscellanous.cpp index a36abe198..00feff3f5 100644 --- a/TombEngine/Scripting/Internal/TEN/Misc/Miscellanous.cpp +++ b/TombEngine/Scripting/Internal/TEN/Misc/Miscellanous.cpp @@ -48,10 +48,16 @@ namespace Misc pos1.StoreInGameVector(vec1); vec1.roomNumber = roomNumber1; pos2.StoreInGameVector(vec2); - return LOS(&vec1, &vec2); + + MESH_INFO* mesh; + Vector3Int vector; + return LOS(&vec1, &vec2) && (ObjectOnLOS2(&vec1, &vec2, &vector, &mesh) == NO_LOS_ITEM); } - + ///Vibrate game controller, if function is available and setting is on. + //@function Vibrate + //@tparam float strength Strength of the vibration + //@tparam float time __(default 0.3)__ Time of the vibration, in seconds static void Vibrate(float strength, sol::optional time) { Rumble(strength, time.value_or(0.3f), RumbleMode::Both); @@ -120,6 +126,10 @@ namespace Misc PlaySoundTrack(trackName, SoundTrackType::BGM); } + /// Play sound effect + //@function PlaySound + //@tparam int sound ID to play. Corresponds to the value in the sound XML file or Tomb Editor's "Sound Infos" window. + ////@tparam[opt] Vec3 position The 3D position of the sound, i.e. where the sound "comes from". If not given, the sound will not be positional. static void PlaySoundEffect(int id, sol::optional p) { SoundEffect(id, p.has_value() ? &PHD_3DPOS(p.value().x, p.value().y, p.value().z) : nullptr, SoundEnvironment::Always); @@ -174,6 +184,15 @@ namespace Misc //@tparam float y percent value to translate to y-coordinate //@treturn int x coordinate in pixels //@treturn int y coordinate in pixels + //@usage + //local halfwayX, halfwayY = PercentToScreen(50, 50) + //local baddy + //local spawnLocationNullmesh = GetMoveableByName("position_behind_left_pillar") + //local str1 = DisplayString("You spawned a baddy!", halfwayX, halfwayY, Color(255, 100, 100), false, {DisplayStringOption.SHADOW, DisplayStringOption.CENTER}) + // + //LevelFuncs.triggerOne = function(obj) + // ShowString(str1, 4) + //end static std::tuple PercentToScreen(double x, double y) { auto fWidth = static_cast(g_Configuration.Width); @@ -222,10 +241,6 @@ namespace Misc table_misc.set_function(ScriptReserved_PlayAudioTrack, &PlayAudioTrack); - /// Play sound effect - //@function PlaySound - //@tparam int sound ID to play - //@tparam Vec3 position table_misc.set_function(ScriptReserved_PlaySound, &PlaySoundEffect); /// Check if particular action key is held diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp index 041ae0f38..9e22c144f 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp +++ b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp @@ -15,6 +15,7 @@ #include "Objects/ObjectsHandler.h" #include "ReservedScriptNames.h" #include "Color/Color.h" +#include "Logic/LevelFunc.h" #include "Rotation/Rotation.h" #include "Vec3/Vec3.h" @@ -72,31 +73,27 @@ most can just be ignored (see usage). @tparam ObjID object ID @tparam string name Lua name of the item @tparam Vec3 position position in level - @tparam Rotation rotation rotation about x, y, and z axes (default Rotation(0, 0, 0)) - @tparam int room room ID item is in - @tparam int animNumber anim number (default 0) - @tparam int frameNumber frame number (default 0) - @tparam int hp HP of item (default 10) - @tparam int OCB ocb of item (default 0) - @tparam table AIBits table with AI bits (default {0,0,0,0,0,0}) + @tparam[opt] Rotation rotation rotation about x, y, and z axes (default Rotation(0, 0, 0)) + @int[opt] room room ID item is in (default: calculated automatically) + @int[opt=0] animNumber anim number + @int[opt=0] frameNumber frame number + @int[opt=10] hp HP of item + @int[opt=0] OCB ocb of item (default 0) + @tparam[opt] table AIBits table with AI bits (default {0,0,0,0,0,0}) @return reference to new Moveable object @usage local item = Moveable( TEN.ObjID.PISTOLS_ITEM, -- object id "test", -- name - Vec3(18907, 0, 21201), - Rotation(0,0,0), - 0, -- room + Vec3(18907, 0, 21201) ) */ - - static std::unique_ptr Create( GAME_OBJECT_ID objID, std::string const & name, Vec3 const & pos, TypeOrNil const & rot, - short room, + TypeOrNil room, TypeOrNil animNumber, TypeOrNil frameNumber, TypeOrNil hp, @@ -110,9 +107,15 @@ static std::unique_ptr Create( if (ScriptAssert(ptr->SetName(name), "Could not set name for Moveable; returning an invalid object.")) { ItemInfo* item = &g_Level.Items[num]; - ptr->SetPos(pos); + if (std::holds_alternative(room)) + { + ptr->SetPos(pos, false); + ptr->SetRoom(std::get(room)); + } + else + ptr->SetPos(pos, true); + ptr->SetRot(USE_IF_HAVE(Rotation, rot, Rotation{})); - ptr->SetRoom(room); ptr->SetObjectID(objID); ptr->Init(); @@ -134,7 +137,6 @@ static std::unique_ptr Create( void Moveable::Register(sol::table & parent) { parent.new_usertype(LUA_CLASS_NAME, - ScriptReserved_New, Create, sol::call_constructor, Create, sol::meta_function::index, index_error, sol::meta_function::new_index, newindex_error, @@ -170,50 +172,14 @@ void Moveable::Register(sol::table & parent) // @treturn int a number representing the status of the object ScriptReserved_GetStatus, &Moveable::GetStatus, -/// Set the name of the function to be called when the moveable is shot by Lara -// Note that this will be triggered twice when shot with both pistols at once. -// @function Moveable:SetOnHit -// @tparam string name of callback function to be called ScriptReserved_SetOnHit, &Moveable::SetOnHit, -/// Get the name of the function called when this moveable is shot -// @function Moveable:GetOnHit -// @treturn string name of the function - ScriptReserved_GetOnHit, &Moveable::GetOnHit, - -/// Set the name of the function called when this moveable collides with another moveable -// @function Moveable:SetOnCollidedWithObject -// @tparam string name of callback function to be called ScriptReserved_SetOnCollidedWithObject, &Moveable::SetOnCollidedWithObject, -/// Get the name of the function called when this moveable collides with another moveable -// @function Moveable:GetOnCollidedWithObject -// @treturn string name of the function - ScriptReserved_GetOnCollidedWithObject, &Moveable::GetOnCollidedWithObject, - -/// Set the name of the function called when this moveable collides with room geometry (e.g. a wall or floor) -// @function Moveable:SetOnCollidedWithRoom -// @tparam string name of callback function to be called ScriptReserved_SetOnCollidedWithRoom, &Moveable::SetOnCollidedWithRoom, -/// Get the name of the function called when this moveable collides with room geometry (e.g. a wall or floor) -// @function Moveable:GetOnCollidedWithRoom -// @treturn string name of the function - ScriptReserved_GetOnCollidedWithRoom, &Moveable::GetOnCollidedWithRoom, - -/// Set the name of the function to be called when the moveable is destroyed/killed -// @function Moveable:SetOnKilled -// @tparam string callback name of function to be called -// @usage -// LevelFuncs.baddyKilled = function(theBaddy) print("You killed a baddy!") end -// baddy:SetOnKilled("baddyKilled") ScriptReserved_SetOnKilled, &Moveable::SetOnKilled, -/// Get the name of the function called when this moveable is killed -// @function Moveable:GetOnKilled -// @treturn string name of the function - ScriptReserved_GetOnKilled, &Moveable::GetOnKilled, - /// Retrieve the object ID // @function Moveable:GetObjectID // @treturn int a number representing the ID of the object @@ -396,24 +362,10 @@ ScriptReserved_GetSlotHP, & Moveable::GetSlotHP, // @treturn bool true if the moveable is active ScriptReserved_GetActive, &Moveable::GetActive, -/// Get the current room of the object -// @function Moveable:GetRoom -// @treturn int number representing the current room of the object ScriptReserved_GetRoom, &Moveable::GetRoom, -/// Set room of object -// This is used in conjunction with SetPosition to teleport an item to a new room. -// @function Moveable:SetRoom -// @tparam int ID the ID of the new room -// @usage -// local sas = TEN.Objects.GetMoveableByName("sas_enemy") -// sas:SetRoom(destinationRoom) -// sas:SetPosition(destinationPosition) ScriptReserved_SetRoom, &Moveable::SetRoom, -/// Get the object's position -// @function Moveable:GetPosition -// @treturn Vec3 a copy of the moveable's position ScriptReserved_GetPosition, & Moveable::GetPos, /// Get the object's joint position @@ -421,12 +373,6 @@ ScriptReserved_GetSlotHP, & Moveable::GetSlotHP, // @treturn Vec3 a copy of the moveable's position ScriptReserved_GetJointPosition, & Moveable::GetJointPos, -/// Set the moveable's position -// If you are moving a moveable whose behaviour involves knowledge of room geometry, -// (e.g. a BADDY1, which uses it for pathfinding), then you *must* use this in conjunction -// with @{Moveable:SetRoom}. Otherwise, said moveable will not behave correctly. -// @function Moveable:SetPosition -// @tparam Vec3 position the new position of the moveable ScriptReserved_SetPosition, & Moveable::SetPos, /// Get the moveable's rotation @@ -490,54 +436,63 @@ void Moveable::SetObjectID(GAME_OBJECT_ID id) m_item->ObjectNumber = id; } -void Moveable::SetOnHit(std::string const & cbName) +void SetLevelFuncCallback(TypeOrNil const & cb, std::string const & callerName, Moveable & mov, std::string & toModify) { - m_item->LuaCallbackOnHitName = cbName; -} - -void Moveable::SetOnKilled(std::string const & cbName) -{ - m_item->LuaCallbackOnKilledName = cbName; -} - -void Moveable::SetOnCollidedWithObject(std::string const & cbName) -{ - m_item->LuaCallbackOnCollidedWithObjectName = cbName; - - if(cbName.empty()) - dynamic_cast(g_GameScriptEntities)->TryRemoveColliding(m_num); + if (std::holds_alternative(cb)) + { + toModify = std::get(cb).m_funcName; + dynamic_cast(g_GameScriptEntities)->TryAddColliding(mov.m_num); + } + else if (std::holds_alternative(cb)) + { + toModify = std::string{}; + dynamic_cast(g_GameScriptEntities)->TryRemoveColliding(mov.m_num); + } else - dynamic_cast(g_GameScriptEntities)->TryAddColliding(m_num); + { + ScriptAssert(false, "Tried giving " + mov.m_item->LuaName + + " a non-LevelFunc object as an arg to " + + callerName); + } + } -void Moveable::SetOnCollidedWithRoom(std::string const & cbName) +/// Set the name of the function to be called when the moveable is shot by Lara +// Note that this will be triggered twice when shot with both pistols at once. +// @function Moveable:SetOnHit +// @tparam function callback function in LevelFuncs hierarchy to call when moveable is shot +void Moveable::SetOnHit(TypeOrNil const & cb) { - m_item->LuaCallbackOnCollidedWithRoomName = cbName; - - if(cbName.empty()) - dynamic_cast(g_GameScriptEntities)->TryRemoveColliding(m_num); - else - dynamic_cast(g_GameScriptEntities)->TryAddColliding(m_num); + SetLevelFuncCallback(cb, ScriptReserved_SetOnHit, *this, m_item->LuaCallbackOnHitName); } -std::string Moveable::GetOnHit() const +/// Set the name of the function to be called when the moveable is destroyed/killed +// Note that enemy death often occurs at the end of an animation, and not at the exact moment +// the enemy's HP becomes zero. +// @function Moveable:SetOnKilled +// @tparam function callback function in LevelFuncs hierarchy to call when enemy is killed +// @usage +// LevelFuncs.baddyKilled = function(theBaddy) print("You killed a baddy!") end +// baddy:SetOnKilled(LevelFuncs.baddyKilled) +void Moveable::SetOnKilled(TypeOrNil const & cb) { - return m_item->LuaCallbackOnHitName; + SetLevelFuncCallback(cb, ScriptReserved_SetOnKilled, *this, m_item->LuaCallbackOnKilledName); } -std::string Moveable::GetOnKilled() const +/// Set the function to be called called when this moveable collides with another moveable +// @function Moveable:SetOnCollidedWithObject +// @tparam function func callback function to be called (must be in LevelFuncs hierarchy) +void Moveable::SetOnCollidedWithObject(TypeOrNil const & cb) { - return m_item->LuaCallbackOnKilledName; + SetLevelFuncCallback(cb, ScriptReserved_SetOnCollidedWithObject, *this, m_item->LuaCallbackOnCollidedWithObjectName); } -std::string Moveable::GetOnCollidedWithObject() const +/// Set the function called when this moveable collides with room geometry (e.g. a wall or floor) +// @function Moveable:SetOnCollidedWithRoom +// @tparam function func callback function to be called (must be in LevelFuncs hierarchy) +void Moveable::SetOnCollidedWithRoom(TypeOrNil const & cb) { - return m_item->LuaCallbackOnCollidedWithObjectName; -} - -std::string Moveable::GetOnCollidedWithRoom() const -{ - return m_item->LuaCallbackOnCollidedWithRoomName; + SetLevelFuncCallback(cb, ScriptReserved_SetOnCollidedWithRoom, *this, m_item->LuaCallbackOnCollidedWithRoomName); } std::string Moveable::GetName() const @@ -572,14 +527,28 @@ bool Moveable::SetName(std::string const & id) return true; } +/// Get the object's position +// @function Moveable:GetPosition +// @treturn Vec3 a copy of the moveable's position Vec3 Moveable::GetPos() const { return Vec3(m_item->Pose); } -void Moveable::SetPos(Vec3 const& pos) +/// Set the moveable's position +// If you are moving a moveable whose behaviour involves knowledge of room geometry, +// (e.g. a BADDY1, which uses it for pathfinding), then the second argument should +// be true (or omitted, as true is the default). Otherwise, said moveable will not behave correctly. +// @function Moveable:SetPosition +// @tparam Vec3 position the new position of the moveable +// @bool[opt] updateRoom Will room changes be automatically detected? Set to false if you are using overlapping rooms (default: true) +void Moveable::SetPos(Vec3 const& pos, sol::optional updateRoom) { pos.StoreInPHDPos(m_item->Pose); + + bool willUpdate = !updateRoom.has_value() || updateRoom.value(); + if(m_initialised && willUpdate) + UpdateItemRoom(m_item, pos.y); } Vec3 Moveable::GetJointPos(int jointIndex) const @@ -612,7 +581,7 @@ void Moveable::SetRot(Rotation const& rot) short Moveable::GetHP() const { - return(m_item->HitPoints); + return m_item->HitPoints; } void Moveable::SetHP(short hp) @@ -755,11 +724,22 @@ bool Moveable::GetHitStatus() const return m_item->HitStatus; } +/// Get the current room of the object +// @function Moveable:GetRoom +// @treturn int number representing the current room of the object short Moveable::GetRoom() const { return m_item->RoomNumber; } +/// Set room of object +// Use this if you are not using SetPosition's automatic room update - for example, when dealing with overlapping rooms. +// @function Moveable:SetRoom +// @tparam int ID the ID of the new room +// @usage +// local sas = TEN.Objects.GetMoveableByName("sas_enemy") +// sas:SetRoom(destinationRoom) +// sas:SetPosition(destinationPosition, false) void Moveable::SetRoom(short room) { const size_t nRooms = g_Level.Rooms.size(); diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h index b433fc44b..cf6ea4807 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h +++ b/TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h @@ -1,7 +1,10 @@ #pragma once +#include "ScriptUtil.h" #include "Objects/NamedBase.h" +class LevelFunc; + namespace sol { class state; template struct as_table_t; @@ -42,7 +45,7 @@ public: [[nodiscard]] Vec3 GetPos() const; [[nodiscard]] Vec3 GetJointPos(int index) const; - void SetPos(Vec3 const& pos); + void SetPos(Vec3 const& pos, sol::optional updateRoom); [[nodiscard]] Rotation GetRot() const; void SetRot(Rotation const& rot); @@ -96,20 +99,18 @@ public: void Explode(); void Shatter(); - [[nodiscard]] std::string GetOnHit() const; - void SetOnHit(std::string const &); - [[nodiscard]] std::string GetOnKilled() const; - void SetOnKilled(std::string const &); - [[nodiscard]] std::string GetOnCollidedWithObject() const; - void SetOnCollidedWithObject(std::string const &); - [[nodiscard]] std::string GetOnCollidedWithRoom() const; - void SetOnCollidedWithRoom(std::string const &); + void SetOnHit(TypeOrNil const& cb); + void SetOnKilled(TypeOrNil const& cb); + void SetOnCollidedWithObject(TypeOrNil const& cb); + void SetOnCollidedWithRoom(TypeOrNil const& cb); [[nodiscard]] short GetStatus() const; void Init(); friend bool operator==(Moveable const&, Moveable const&); + friend void SetLevelFuncCallback(TypeOrNil const& cb, std::string const & callerName, Moveable& mov, std::string& toModify); + private: ItemInfo* m_item; short m_num; diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Static/StaticObject.cpp b/TombEngine/Scripting/Internal/TEN/Objects/Static/StaticObject.cpp index 8e9660df3..7c07079fe 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Static/StaticObject.cpp +++ b/TombEngine/Scripting/Internal/TEN/Objects/Static/StaticObject.cpp @@ -1,6 +1,7 @@ #pragma once #include "framework.h" +#include "Game/effects/debris.h" #include "ScriptAssert.h" #include "StaticObject.h" #include "Vec3/Vec3.h" @@ -28,6 +29,14 @@ void Static::Register(sol::table & parent) sol::meta_function::index, index_error, sol::meta_function::new_index, newindex_error, + /// Enable the static, e.g. in cases when it was shattered or manually disabled before. + // @function Static:Enable + ScriptReserved_Enable, &Static::Enable, + + /// Disable the static + // @function Static:Disable + ScriptReserved_Disable, &Static::Disable, + /// Get the static's position // @function Static:GetPosition // @treturn Vec3 a copy of the static's position @@ -87,7 +96,21 @@ void Static::Register(sol::table & parent) /// Set the static's color // @function Static:SetColor // @tparam Color color the new color of the static - ScriptReserved_SetColor, &Static::SetColor); + ScriptReserved_SetColor, &Static::SetColor, + + /// Shatter static mesh + // @function Static:Shatter + ScriptReserved_Shatter, &Static::Shatter); +} + +void Static::Enable() +{ + m_mesh.flags |= StaticMeshFlags::SM_VISIBLE; +} + +void Static::Disable() +{ + m_mesh.flags &= ~StaticMeshFlags::SM_VISIBLE; } Vec3 Static::GetPos() const @@ -175,4 +198,9 @@ ScriptColor Static::GetColor() const void Static::SetColor(ScriptColor const& col) { m_mesh.color = col; +} + +void Static::Shatter() +{ + ShatterObject(nullptr, &m_mesh, -128, m_mesh.roomNumber, 0); } \ No newline at end of file diff --git a/TombEngine/Scripting/Internal/TEN/Objects/Static/StaticObject.h b/TombEngine/Scripting/Internal/TEN/Objects/Static/StaticObject.h index c5ebba767..265705bfe 100644 --- a/TombEngine/Scripting/Internal/TEN/Objects/Static/StaticObject.h +++ b/TombEngine/Scripting/Internal/TEN/Objects/Static/StaticObject.h @@ -23,6 +23,8 @@ public: static void Register(sol::table & parent); + void Enable(); + void Disable(); Rotation GetRot() const; void SetRot(Rotation const& rot); Vec3 GetPos() const; @@ -35,6 +37,7 @@ public: void SetSlot(int slot); ScriptColor GetColor() const; void SetColor(ScriptColor const & col); + void Shatter(); private: MESH_INFO & m_mesh; diff --git a/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp b/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp index 88f796cb4..479e9c9f1 100644 --- a/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp +++ b/TombEngine/Scripting/Internal/TEN/Rotation/Rotation.cpp @@ -1,5 +1,6 @@ #include "framework.h" #include "Rotation.h" +#include "ReservedScriptNames.h" #include "Specific/phd_global.h" /*** Represents a rotation. @@ -13,7 +14,7 @@ All values will be clamped to [-32768, 32767]. void Rotation::Register(sol::table & parent) { using ctors = sol::constructors; - parent.new_usertype("Rotation", + parent.new_usertype(ScriptReserved_Rotation, ctors(), sol::call_constructor, ctors(), sol::meta_function::to_string, &Rotation::ToString, diff --git a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp index 1b0cc600a..7da684e54 100644 --- a/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp +++ b/TombEngine/Scripting/Internal/TEN/Strings/DisplayString/DisplayString.cpp @@ -93,7 +93,6 @@ void DisplayString::Register(sol::table & parent) { parent.new_usertype( ScriptReserved_DisplayString, - ScriptReserved_New, &CreateString, sol::call_constructor, &CreateString, /// Get the display string's color diff --git a/TombEngine/Scripting/Internal/TEN/Vec3/Vec3.cpp b/TombEngine/Scripting/Internal/TEN/Vec3/Vec3.cpp index 5c7de764f..c435f8dd7 100644 --- a/TombEngine/Scripting/Internal/TEN/Vec3/Vec3.cpp +++ b/TombEngine/Scripting/Internal/TEN/Vec3/Vec3.cpp @@ -63,6 +63,15 @@ Vec3::Vec3(PHD_3DPOS const& pos) : x{pos.Position.x}, y{pos.Position.y}, z{pos.P { } +Vec3::Vec3(Vector3Int const& pos) : x{pos.x}, y{pos.y}, z{pos.z} +{ +} + +Vec3::operator Vector3Int() const +{ + return Vector3Int{ x, y, z }; +}; + void Vec3::StoreInPHDPos(PHD_3DPOS& pos) const { pos.Position.x = x; diff --git a/TombEngine/Scripting/Internal/TEN/Vec3/Vec3.h b/TombEngine/Scripting/Internal/TEN/Vec3/Vec3.h index 0d51b62a9..4713b3cff 100644 --- a/TombEngine/Scripting/Internal/TEN/Vec3/Vec3.h +++ b/TombEngine/Scripting/Internal/TEN/Vec3/Vec3.h @@ -1,5 +1,7 @@ #pragma once +struct Vector3Int; + namespace sol { class state; } @@ -15,6 +17,9 @@ public: Vec3(int x, int y, int z); Vec3(PHD_3DPOS const& pos); + Vec3(Vector3Int const& pos); + + operator Vector3Int() const; [[nodiscard]] std::string ToString() const; diff --git a/TombEngine/Sound/sound.cpp b/TombEngine/Sound/sound.cpp index 81e14e776..1e925596c 100644 --- a/TombEngine/Sound/sound.cpp +++ b/TombEngine/Sound/sound.cpp @@ -419,7 +419,7 @@ void PlaySoundTrack(std::string track, SoundTrackType mode, QWORD position) fullTrackName = TRACKS_PATH + track + ".wav"; if (!std::filesystem::exists(fullTrackName)) { - TENLog("No soundtrack files with name '" + track + "' were found", LogLevel::Error); + TENLog("No soundtrack files with name '" + track + "' were found", LogLevel::Warning); return; } } diff --git a/TombEngine/Specific/level.cpp b/TombEngine/Specific/level.cpp index 7efd7219c..211ec8e68 100644 --- a/TombEngine/Specific/level.cpp +++ b/TombEngine/Specific/level.cpp @@ -163,8 +163,6 @@ void LoadItems() if (g_Level.NumItems == 0) return; - g_Level.Items.resize(NUM_ITEMS); - InitialiseItemArray(NUM_ITEMS); if (g_Level.NumItems > 0) @@ -787,6 +785,7 @@ void ReadRooms() mesh.HitPoints = ReadInt16(); mesh.luaName = ReadString(); + mesh.roomNumber = i; g_GameScriptEntities->AddName(mesh.luaName, mesh); } @@ -995,17 +994,17 @@ void LoadEventSets() eventSet.OnEnter.Mode = (VolumeEventMode)ReadInt32(); eventSet.OnEnter.Function = ReadString(); - eventSet.OnEnter.Argument = ReadString(); + eventSet.OnEnter.Data = ReadString(); eventSet.OnEnter.CallCounter = ReadInt32(); eventSet.OnInside.Mode = (VolumeEventMode)ReadInt32(); eventSet.OnInside.Function = ReadString(); - eventSet.OnInside.Argument = ReadString(); + eventSet.OnInside.Data = ReadString(); eventSet.OnInside.CallCounter = ReadInt32(); eventSet.OnLeave.Mode = (VolumeEventMode)ReadInt32(); eventSet.OnLeave.Function = ReadString(); - eventSet.OnLeave.Argument = ReadString(); + eventSet.OnLeave.Data = ReadString(); eventSet.OnLeave.CallCounter = ReadInt32(); g_Level.EventSets.push_back(eventSet); @@ -1091,7 +1090,7 @@ unsigned int _stdcall LoadLevel(void* data) { if (assemblyVersion[i] < version[i]) { - TENLog("Level version is higher than TEN version. Please update TEN.", LogLevel::Warning); + TENLog("Level version is different from TEN version.", LogLevel::Warning); break; } } @@ -1321,11 +1320,11 @@ void LoadSprites() spr->y4 = ReadFloat(); } - g_Level.NumSpritesSequences = ReadInt32(); + int numSequences = ReadInt32(); - TENLog("Num sprite sequences: " + std::to_string(g_Level.NumSpritesSequences), LogLevel::Info); + TENLog("Num sprite sequences: " + std::to_string(numSequences), LogLevel::Info); - for (int i = 0; i < g_Level.NumSpritesSequences; i++) + for (int i = 0; i < numSequences; i++) { int spriteID = ReadInt32(); short negLength = ReadInt16(); diff --git a/TombEngine/Specific/level.h b/TombEngine/Specific/level.h index 66f51f7c0..d23b7d4bb 100644 --- a/TombEngine/Specific/level.h +++ b/TombEngine/Specific/level.h @@ -117,7 +117,6 @@ struct LEVEL std::vector AnimatedTexturesSequences; std::vector EventSets; int NumItems; - int NumSpritesSequences; }; extern std::vector MoveablesIds; diff --git a/TombEngine/Specific/phd_global.cpp b/TombEngine/Specific/phd_global.cpp index 1696bc4bc..9e9e88dc3 100644 --- a/TombEngine/Specific/phd_global.cpp +++ b/TombEngine/Specific/phd_global.cpp @@ -5,21 +5,21 @@ const Vector2Int Vector2Int::Zero = Vector2Int(0, 0); const Vector3Int Vector3Int::Zero = Vector3Int(0, 0, 0); const Vector3Shrt Vector3Shrt::Zero = Vector3Shrt(0, 0, 0); -BOUNDING_BOX operator+(const BOUNDING_BOX& box, const PHD_3DPOS& vec) +BOUNDING_BOX operator+(const BOUNDING_BOX& box, const PHD_3DPOS& pose) { - BOUNDING_BOX box2 = box; - box2.X1 += vec.Position.x; - box2.X2 += vec.Position.x; - box2.Y1 += vec.Position.y; - box2.Y2 += vec.Position.y; - box2.Z1 += vec.Position.z; - box2.Z2 += vec.Position.z; + auto box2 = box; + box2.X1 += pose.Position.x; + box2.X2 += pose.Position.x; + box2.Y1 += pose.Position.y; + box2.Y2 += pose.Position.y; + box2.Z1 += pose.Position.z; + box2.Z2 += pose.Position.z; return box2; } BOUNDING_BOX operator*(const BOUNDING_BOX& box, const float scale) { - BOUNDING_BOX box2 = box; + auto box2 = box; box2.X1 *= scale; box2.X2 *= scale; box2.Y1 *= scale; diff --git a/TombEngine/Specific/phd_global.h b/TombEngine/Specific/phd_global.h index 3c9573609..f2c99fc67 100644 --- a/TombEngine/Specific/phd_global.h +++ b/TombEngine/Specific/phd_global.h @@ -46,11 +46,21 @@ struct Vector3Int this->z = z; } - Vector3Int(Vector3 v) + Vector3Int(Vector3 vector) { - this->x = int(v.x); - this->y = int(v.y); - this->z = int(v.z); + this->x = int(vector.x); + this->y = int(vector.y); + this->z = int(vector.z); + } + + static float Distance(const Vector3Int& origin, const Vector3Int& target) + { + return Vector3::Distance(origin.ToVector3(), target.ToVector3()); + } + + static float DistanceSquared(const Vector3Int& origin, const Vector3Int& target) + { + return Vector3::DistanceSquared(origin.ToVector3(), target.ToVector3()); } Vector3 ToVector3() const @@ -361,6 +371,15 @@ struct GameVector this->boxNumber = boxNumber; } + GameVector(Vector3Int& pos) + { + this->x = pos.x; + this->y = pos.y; + this->z = pos.z; + this->roomNumber = 0; + this->boxNumber = 0; + } + GameVector(Vector3Int& pos, short roomNumber) { this->x = pos.x; @@ -591,5 +610,5 @@ struct BOUNDING_BOX int Height() { return abs(Y2 - Y1); } }; -BOUNDING_BOX operator+(const BOUNDING_BOX& box, const PHD_3DPOS& vec); -BOUNDING_BOX operator*(const BOUNDING_BOX& box, const float scale); \ No newline at end of file +BOUNDING_BOX operator+(const BOUNDING_BOX& box, const PHD_3DPOS& pose); +BOUNDING_BOX operator*(const BOUNDING_BOX& box, const float scale); diff --git a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h index a6a0f3c30..14be600e6 100644 --- a/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h +++ b/TombEngine/Specific/savegame/flatbuffers/ten_savegame_generated.h @@ -139,6 +139,14 @@ struct boolTable; struct boolTableBuilder; struct boolTableT; +struct vec3Table; +struct vec3TableBuilder; +struct vec3TableT; + +struct funcNameTable; +struct funcNameTableBuilder; +struct funcNameTableT; + struct UnionTable; struct UnionTableBuilder; struct UnionTableT; @@ -201,35 +209,41 @@ enum class VarUnion : uint8_t { tab = 2, num = 3, boolean = 4, + vec3 = 5, + funcName = 6, MIN = NONE, - MAX = boolean + MAX = funcName }; -inline const VarUnion (&EnumValuesVarUnion())[5] { +inline const VarUnion (&EnumValuesVarUnion())[7] { static const VarUnion values[] = { VarUnion::NONE, VarUnion::str, VarUnion::tab, VarUnion::num, - VarUnion::boolean + VarUnion::boolean, + VarUnion::vec3, + VarUnion::funcName }; return values; } inline const char * const *EnumNamesVarUnion() { - static const char * const names[6] = { + static const char * const names[8] = { "NONE", "str", "tab", "num", "boolean", + "vec3", + "funcName", nullptr }; return names; } inline const char *EnumNameVarUnion(VarUnion e) { - if (flatbuffers::IsOutRange(e, VarUnion::NONE, VarUnion::boolean)) return ""; + if (flatbuffers::IsOutRange(e, VarUnion::NONE, VarUnion::funcName)) return ""; const size_t index = static_cast(e); return EnumNamesVarUnion()[index]; } @@ -254,6 +268,14 @@ template<> struct VarUnionTraits { static const VarUnion enum_value = VarUnion::boolean; }; +template<> struct VarUnionTraits { + static const VarUnion enum_value = VarUnion::vec3; +}; + +template<> struct VarUnionTraits { + static const VarUnion enum_value = VarUnion::funcName; +}; + struct VarUnionUnion { VarUnion type; void *value; @@ -318,6 +340,22 @@ struct VarUnionUnion { return type == VarUnion::boolean ? reinterpret_cast(value) : nullptr; } + TEN::Save::vec3TableT *Asvec3() { + return type == VarUnion::vec3 ? + reinterpret_cast(value) : nullptr; + } + const TEN::Save::vec3TableT *Asvec3() const { + return type == VarUnion::vec3 ? + reinterpret_cast(value) : nullptr; + } + TEN::Save::funcNameTableT *AsfuncName() { + return type == VarUnion::funcName ? + reinterpret_cast(value) : nullptr; + } + const TEN::Save::funcNameTableT *AsfuncName() const { + return type == VarUnion::funcName ? + reinterpret_cast(value) : nullptr; + } }; bool VerifyVarUnion(flatbuffers::Verifier &verifier, const void *obj, VarUnion type); @@ -5372,6 +5410,132 @@ struct boolTable::Traits { flatbuffers::Offset CreateboolTable(flatbuffers::FlatBufferBuilder &_fbb, const boolTableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +struct vec3TableT : public flatbuffers::NativeTable { + typedef vec3Table TableType; + std::unique_ptr vec{}; +}; + +struct vec3Table FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef vec3TableT NativeTableType; + typedef vec3TableBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_VEC = 4 + }; + const TEN::Save::Vector3 *vec() const { + return GetStruct(VT_VEC); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_VEC) && + verifier.EndTable(); + } + vec3TableT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(vec3TableT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const vec3TableT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct vec3TableBuilder { + typedef vec3Table Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_vec(const TEN::Save::Vector3 *vec) { + fbb_.AddStruct(vec3Table::VT_VEC, vec); + } + explicit vec3TableBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset Createvec3Table( + flatbuffers::FlatBufferBuilder &_fbb, + const TEN::Save::Vector3 *vec = 0) { + vec3TableBuilder builder_(_fbb); + builder_.add_vec(vec); + return builder_.Finish(); +} + +struct vec3Table::Traits { + using type = vec3Table; + static auto constexpr Create = Createvec3Table; +}; + +flatbuffers::Offset Createvec3Table(flatbuffers::FlatBufferBuilder &_fbb, const vec3TableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + +struct funcNameTableT : public flatbuffers::NativeTable { + typedef funcNameTable TableType; + std::string str{}; +}; + +struct funcNameTable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef funcNameTableT NativeTableType; + typedef funcNameTableBuilder Builder; + struct Traits; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_STR = 4 + }; + const flatbuffers::String *str() const { + return GetPointer(VT_STR); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_STR) && + verifier.VerifyString(str()) && + verifier.EndTable(); + } + funcNameTableT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; + void UnPackTo(funcNameTableT *_o, const flatbuffers::resolver_function_t *_resolver = nullptr) const; + static flatbuffers::Offset Pack(flatbuffers::FlatBufferBuilder &_fbb, const funcNameTableT* _o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); +}; + +struct funcNameTableBuilder { + typedef funcNameTable Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_str(flatbuffers::Offset str) { + fbb_.AddOffset(funcNameTable::VT_STR, str); + } + explicit funcNameTableBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreatefuncNameTable( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset str = 0) { + funcNameTableBuilder builder_(_fbb); + builder_.add_str(str); + return builder_.Finish(); +} + +struct funcNameTable::Traits { + using type = funcNameTable; + static auto constexpr Create = CreatefuncNameTable; +}; + +inline flatbuffers::Offset CreatefuncNameTableDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const char *str = nullptr) { + auto str__ = str ? _fbb.CreateString(str) : 0; + return TEN::Save::CreatefuncNameTable( + _fbb, + str__); +} + +flatbuffers::Offset CreatefuncNameTable(flatbuffers::FlatBufferBuilder &_fbb, const funcNameTableT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); + struct UnionTableT : public flatbuffers::NativeTable { typedef UnionTable TableType; TEN::Save::VarUnionUnion u{}; @@ -5404,6 +5568,12 @@ struct UnionTable FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const TEN::Save::boolTable *u_as_boolean() const { return u_type() == TEN::Save::VarUnion::boolean ? static_cast(u()) : nullptr; } + const TEN::Save::vec3Table *u_as_vec3() const { + return u_type() == TEN::Save::VarUnion::vec3 ? static_cast(u()) : nullptr; + } + const TEN::Save::funcNameTable *u_as_funcName() const { + return u_type() == TEN::Save::VarUnion::funcName ? static_cast(u()) : nullptr; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_U_TYPE) && @@ -5432,6 +5602,14 @@ template<> inline const TEN::Save::boolTable *UnionTable::u_as inline const TEN::Save::vec3Table *UnionTable::u_as() const { + return u_as_vec3(); +} + +template<> inline const TEN::Save::funcNameTable *UnionTable::u_as() const { + return u_as_funcName(); +} + struct UnionTableBuilder { typedef UnionTable Table; flatbuffers::FlatBufferBuilder &fbb_; @@ -5860,6 +6038,8 @@ struct SaveGameT : public flatbuffers::NativeTable { std::vector> volume_states{}; std::vector> call_counters{}; std::unique_ptr script_vars{}; + std::vector callbacks_pre_control{}; + std::vector callbacks_post_control{}; }; struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { @@ -5902,7 +6082,9 @@ struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_ALTERNATE_PENDULUM = 68, VT_VOLUME_STATES = 70, VT_CALL_COUNTERS = 72, - VT_SCRIPT_VARS = 74 + VT_SCRIPT_VARS = 74, + VT_CALLBACKS_PRE_CONTROL = 76, + VT_CALLBACKS_POST_CONTROL = 78 }; const TEN::Save::SaveGameHeader *header() const { return GetPointer(VT_HEADER); @@ -6012,6 +6194,12 @@ struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const TEN::Save::UnionVec *script_vars() const { return GetPointer(VT_SCRIPT_VARS); } + const flatbuffers::Vector> *callbacks_pre_control() const { + return GetPointer> *>(VT_CALLBACKS_PRE_CONTROL); + } + const flatbuffers::Vector> *callbacks_post_control() const { + return GetPointer> *>(VT_CALLBACKS_POST_CONTROL); + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_HEADER) && @@ -6090,6 +6278,12 @@ struct SaveGame FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { verifier.VerifyVectorOfTables(call_counters()) && VerifyOffset(verifier, VT_SCRIPT_VARS) && verifier.VerifyTable(script_vars()) && + VerifyOffset(verifier, VT_CALLBACKS_PRE_CONTROL) && + verifier.VerifyVector(callbacks_pre_control()) && + verifier.VerifyVectorOfStrings(callbacks_pre_control()) && + VerifyOffset(verifier, VT_CALLBACKS_POST_CONTROL) && + verifier.VerifyVector(callbacks_post_control()) && + verifier.VerifyVectorOfStrings(callbacks_post_control()) && verifier.EndTable(); } SaveGameT *UnPack(const flatbuffers::resolver_function_t *_resolver = nullptr) const; @@ -6209,6 +6403,12 @@ struct SaveGameBuilder { void add_script_vars(flatbuffers::Offset script_vars) { fbb_.AddOffset(SaveGame::VT_SCRIPT_VARS, script_vars); } + void add_callbacks_pre_control(flatbuffers::Offset>> callbacks_pre_control) { + fbb_.AddOffset(SaveGame::VT_CALLBACKS_PRE_CONTROL, callbacks_pre_control); + } + void add_callbacks_post_control(flatbuffers::Offset>> callbacks_post_control) { + fbb_.AddOffset(SaveGame::VT_CALLBACKS_POST_CONTROL, callbacks_post_control); + } explicit SaveGameBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -6257,10 +6457,14 @@ inline flatbuffers::Offset CreateSaveGame( flatbuffers::Offset alternate_pendulum = 0, flatbuffers::Offset>> volume_states = 0, flatbuffers::Offset>> call_counters = 0, - flatbuffers::Offset script_vars = 0) { + flatbuffers::Offset script_vars = 0, + flatbuffers::Offset>> callbacks_pre_control = 0, + flatbuffers::Offset>> callbacks_post_control = 0) { SaveGameBuilder builder_(_fbb); builder_.add_oneshot_position(oneshot_position); builder_.add_ambient_position(ambient_position); + builder_.add_callbacks_post_control(callbacks_post_control); + builder_.add_callbacks_pre_control(callbacks_pre_control); builder_.add_script_vars(script_vars); builder_.add_call_counters(call_counters); builder_.add_volume_states(volume_states); @@ -6340,7 +6544,9 @@ inline flatbuffers::Offset CreateSaveGameDirect( flatbuffers::Offset alternate_pendulum = 0, const std::vector> *volume_states = nullptr, const std::vector> *call_counters = nullptr, - flatbuffers::Offset script_vars = 0) { + flatbuffers::Offset script_vars = 0, + const std::vector> *callbacks_pre_control = nullptr, + const std::vector> *callbacks_post_control = nullptr) { auto items__ = items ? _fbb.CreateVector>(*items) : 0; auto room_items__ = room_items ? _fbb.CreateVector(*room_items) : 0; auto fxinfos__ = fxinfos ? _fbb.CreateVector>(*fxinfos) : 0; @@ -6360,6 +6566,8 @@ inline flatbuffers::Offset CreateSaveGameDirect( auto cd_flags__ = cd_flags ? _fbb.CreateVector(*cd_flags) : 0; auto volume_states__ = volume_states ? _fbb.CreateVector>(*volume_states) : 0; auto call_counters__ = call_counters ? _fbb.CreateVector>(*call_counters) : 0; + auto callbacks_pre_control__ = callbacks_pre_control ? _fbb.CreateVector>(*callbacks_pre_control) : 0; + auto callbacks_post_control__ = callbacks_post_control ? _fbb.CreateVector>(*callbacks_post_control) : 0; return TEN::Save::CreateSaveGame( _fbb, header, @@ -6397,7 +6605,9 @@ inline flatbuffers::Offset CreateSaveGameDirect( alternate_pendulum, volume_states__, call_counters__, - script_vars); + script_vars, + callbacks_pre_control__, + callbacks_post_control__); } flatbuffers::Offset CreateSaveGame(flatbuffers::FlatBufferBuilder &_fbb, const SaveGameT *_o, const flatbuffers::rehasher_function_t *_rehasher = nullptr); @@ -7940,6 +8150,58 @@ inline flatbuffers::Offset CreateboolTable(flatbuffers::FlatBufferBui _scalar); } +inline vec3TableT *vec3Table::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = std::make_unique(); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void vec3Table::UnPackTo(vec3TableT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = vec(); if (_e) _o->vec = std::unique_ptr(new TEN::Save::Vector3(*_e)); } +} + +inline flatbuffers::Offset vec3Table::Pack(flatbuffers::FlatBufferBuilder &_fbb, const vec3TableT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return Createvec3Table(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset Createvec3Table(flatbuffers::FlatBufferBuilder &_fbb, const vec3TableT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const vec3TableT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _vec = _o->vec ? _o->vec.get() : 0; + return TEN::Save::Createvec3Table( + _fbb, + _vec); +} + +inline funcNameTableT *funcNameTable::UnPack(const flatbuffers::resolver_function_t *_resolver) const { + auto _o = std::make_unique(); + UnPackTo(_o.get(), _resolver); + return _o.release(); +} + +inline void funcNameTable::UnPackTo(funcNameTableT *_o, const flatbuffers::resolver_function_t *_resolver) const { + (void)_o; + (void)_resolver; + { auto _e = str(); if (_e) _o->str = _e->str(); } +} + +inline flatbuffers::Offset funcNameTable::Pack(flatbuffers::FlatBufferBuilder &_fbb, const funcNameTableT* _o, const flatbuffers::rehasher_function_t *_rehasher) { + return CreatefuncNameTable(_fbb, _o, _rehasher); +} + +inline flatbuffers::Offset CreatefuncNameTable(flatbuffers::FlatBufferBuilder &_fbb, const funcNameTableT *_o, const flatbuffers::rehasher_function_t *_rehasher) { + (void)_rehasher; + (void)_o; + struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const funcNameTableT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; + auto _str = _o->str.empty() ? _fbb.CreateSharedString("") : _fbb.CreateString(_o->str); + return TEN::Save::CreatefuncNameTable( + _fbb, + _str); +} + inline UnionTableT *UnionTable::UnPack(const flatbuffers::resolver_function_t *_resolver) const { auto _o = std::make_unique(); UnPackTo(_o.get(), _resolver); @@ -8131,6 +8393,8 @@ inline void SaveGame::UnPackTo(SaveGameT *_o, const flatbuffers::resolver_functi { auto _e = volume_states(); if (_e) { _o->volume_states.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->volume_states[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); } } } { auto _e = call_counters(); if (_e) { _o->call_counters.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->call_counters[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); } } } { auto _e = script_vars(); if (_e) _o->script_vars = std::unique_ptr(_e->UnPack(_resolver)); } + { auto _e = callbacks_pre_control(); if (_e) { _o->callbacks_pre_control.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->callbacks_pre_control[_i] = _e->Get(_i)->str(); } } } + { auto _e = callbacks_post_control(); if (_e) { _o->callbacks_post_control.resize(_e->size()); for (flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { _o->callbacks_post_control[_i] = _e->Get(_i)->str(); } } } } inline flatbuffers::Offset SaveGame::Pack(flatbuffers::FlatBufferBuilder &_fbb, const SaveGameT* _o, const flatbuffers::rehasher_function_t *_rehasher) { @@ -8177,6 +8441,8 @@ inline flatbuffers::Offset CreateSaveGame(flatbuffers::FlatBufferBuild auto _volume_states = _fbb.CreateVector> (_o->volume_states.size(), [](size_t i, _VectorArgs *__va) { return CreateVolumeState(*__va->__fbb, __va->__o->volume_states[i].get(), __va->__rehasher); }, &_va ); auto _call_counters = _fbb.CreateVector> (_o->call_counters.size(), [](size_t i, _VectorArgs *__va) { return CreateEventSetCallCounters(*__va->__fbb, __va->__o->call_counters[i].get(), __va->__rehasher); }, &_va ); auto _script_vars = _o->script_vars ? CreateUnionVec(_fbb, _o->script_vars.get(), _rehasher) : 0; + auto _callbacks_pre_control = _fbb.CreateVectorOfStrings(_o->callbacks_pre_control); + auto _callbacks_post_control = _fbb.CreateVectorOfStrings(_o->callbacks_post_control); return TEN::Save::CreateSaveGame( _fbb, _header, @@ -8214,7 +8480,9 @@ inline flatbuffers::Offset CreateSaveGame(flatbuffers::FlatBufferBuild _alternate_pendulum, _volume_states, _call_counters, - _script_vars); + _script_vars, + _callbacks_pre_control, + _callbacks_post_control); } inline bool VerifyVarUnion(flatbuffers::Verifier &verifier, const void *obj, VarUnion type) { @@ -8238,6 +8506,14 @@ inline bool VerifyVarUnion(flatbuffers::Verifier &verifier, const void *obj, Var auto ptr = reinterpret_cast(obj); return verifier.VerifyTable(ptr); } + case VarUnion::vec3: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } + case VarUnion::funcName: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } default: return true; } } @@ -8272,6 +8548,14 @@ inline void *VarUnionUnion::UnPack(const void *obj, VarUnion type, const flatbuf auto ptr = reinterpret_cast(obj); return ptr->UnPack(resolver); } + case VarUnion::vec3: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } + case VarUnion::funcName: { + auto ptr = reinterpret_cast(obj); + return ptr->UnPack(resolver); + } default: return nullptr; } } @@ -8294,6 +8578,14 @@ inline flatbuffers::Offset VarUnionUnion::Pack(flatbuffers::FlatBufferBuil auto ptr = reinterpret_cast(value); return CreateboolTable(_fbb, ptr, _rehasher).Union(); } + case VarUnion::vec3: { + auto ptr = reinterpret_cast(value); + return Createvec3Table(_fbb, ptr, _rehasher).Union(); + } + case VarUnion::funcName: { + auto ptr = reinterpret_cast(value); + return CreatefuncNameTable(_fbb, ptr, _rehasher).Union(); + } default: return 0; } } @@ -8316,6 +8608,14 @@ inline VarUnionUnion::VarUnionUnion(const VarUnionUnion &u) : type(u.type), valu value = new TEN::Save::boolTableT(*reinterpret_cast(u.value)); break; } + case VarUnion::vec3: { + FLATBUFFERS_ASSERT(false); // TEN::Save::vec3TableT not copyable. + break; + } + case VarUnion::funcName: { + value = new TEN::Save::funcNameTableT(*reinterpret_cast(u.value)); + break; + } default: break; } @@ -8343,6 +8643,16 @@ inline void VarUnionUnion::Reset() { delete ptr; break; } + case VarUnion::vec3: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } + case VarUnion::funcName: { + auto ptr = reinterpret_cast(value); + delete ptr; + break; + } default: break; } value = nullptr; diff --git a/TombEngine/Specific/savegame/schema/ten_savegame.fbs b/TombEngine/Specific/savegame/schema/ten_savegame.fbs index a2415385b..d734b53f2 100644 --- a/TombEngine/Specific/savegame/schema/ten_savegame.fbs +++ b/TombEngine/Specific/savegame/schema/ten_savegame.fbs @@ -386,11 +386,21 @@ table boolTable { scalar: bool; } +table vec3Table { + vec: Vector3; +} + +table funcNameTable { + str: string; +} + union VarUnion { str: stringTable, tab: ScriptTable, num: doubleTable, - boolean: boolTable + boolean: boolTable, + vec3: vec3Table, + funcName: funcNameTable } table UnionTable{ @@ -459,6 +469,8 @@ table SaveGame { volume_states: [VolumeState]; call_counters: [EventSetCallCounters]; script_vars: UnionVec; + callbacks_pre_control: [string]; + callbacks_post_control: [string]; } root_type TEN.Save.SaveGame; diff --git a/TombEngine/Specific/setup.cpp b/TombEngine/Specific/setup.cpp index d30ba5659..d56cd088d 100644 --- a/TombEngine/Specific/setup.cpp +++ b/TombEngine/Specific/setup.cpp @@ -408,7 +408,7 @@ void InitialiseObjects() obj->usingDrawAnimatingItem = true; obj->semiTransparent = false; obj->undead = false; - obj->zoneType = ZONE_NULL; + obj->ZoneType = ZoneType::None; obj->biteOffset = -1; obj->meshSwapSlot = NO_ITEM; obj->isPickup = false; diff --git a/TombEngine/Specific/setup.h b/TombEngine/Specific/setup.h index 550ebf858..d8fa8efe3 100644 --- a/TombEngine/Specific/setup.h +++ b/TombEngine/Specific/setup.h @@ -1,12 +1,12 @@ #pragma once #include "Objects/objectslist.h" -#include "Specific/phd_global.h" -#include "Specific/level.h" #include "Renderer/Renderer11Enums.h" +#include "Specific/level.h" +#include "Specific/phd_global.h" -struct ItemInfo; +enum class ZoneType; struct CollisionInfo; -enum ZoneType : char; +struct ItemInfo; constexpr auto DEFAULT_RADIUS = 10; constexpr auto ROT_X = 0x0004; @@ -44,7 +44,7 @@ struct ObjectInfo std::function ceilingBorder; std::function drawRoutine; std::function collision; - ZoneType zoneType; + ZoneType ZoneType; int animIndex; short HitPoints; short pivotLength; diff --git a/TombEngine/Specific/trmath.cpp b/TombEngine/Specific/trmath.cpp index 4733bb5af..602d7fd10 100644 --- a/TombEngine/Specific/trmath.cpp +++ b/TombEngine/Specific/trmath.cpp @@ -188,7 +188,7 @@ BoundingOrientedBox TO_DX_BBOX(PHD_3DPOS pos, BOUNDING_BOX* box) auto rotation = Quaternion::CreateFromYawPitchRoll(TO_RAD(pos.Orientation.y), TO_RAD(pos.Orientation.x), TO_RAD(pos.Orientation.z)); BoundingOrientedBox result; - BoundingOrientedBox(boxCentre, boxExtent, Vector4::UnitY).Transform(result, 1, rotation, Vector3(pos.Position.x, pos.Position.y, pos.Position.z)); + BoundingOrientedBox(boxCentre, boxExtent, Vector4::UnitY).Transform(result, 1, rotation, pos.Position.ToVector3()); return result; } diff --git a/TombEngine/Specific/winmain.cpp b/TombEngine/Specific/winmain.cpp index 41f1de856..6f2de8410 100644 --- a/TombEngine/Specific/winmain.cpp +++ b/TombEngine/Specific/winmain.cpp @@ -281,16 +281,22 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine // Initialise scripting try { - // TODO: make sure the right objects are deleted at the end g_GameFlow = ScriptInterfaceState::CreateFlow(); g_GameFlow->LoadFlowScript(); - g_GameScript = ScriptInterfaceState::CreateGame(); g_GameScriptEntities = ScriptInterfaceState::CreateObjectsHandler(); g_GameStringsHandler = ScriptInterfaceState::CreateStringsHandler(); + + // This must be loaded last as it adds metafunctions to the global + // table so that every global variable added henceforth gets put + // into a special hidden table which we can clean up. + // By doing this last, we ensure that all built-in usertypes + // are added to a hierarchy in the REAL global table, not the fake + // hidden one. + g_GameScript = ScriptInterfaceState::CreateGame(); } catch (TENScriptException const& e) { - std::string msg = std::string{ "An unrecoverable error occurred in " } + __func__ + ": " + e.what(); + std::string msg = std::string{ "A Lua error occurred while setting up scripts; " } + __func__ + ": " + e.what(); TENLog(msg, LogLevel::Error, LogConfig::All); ShutdownTENLog(); return 0; diff --git a/TombEngine/TombEngine.vcxproj b/TombEngine/TombEngine.vcxproj index 4054d6eea..7800af8e1 100644 --- a/TombEngine/TombEngine.vcxproj +++ b/TombEngine/TombEngine.vcxproj @@ -238,6 +238,7 @@ CALL gen.bat +
    Create(name, totalTime, loop, timerFormat, func[, ...])func(name, totalTime, loop, timerFormat[, ...]) Create (but do not start) a new timer.
    Get a timer by its name.
    UpdateAll(dt)Update all active timers.
    myTimer:SetFunction(func[, ...]) Give the timer a new function and args