This commit is contained in:
Sezz 2023-10-06 15:19:56 +11:00
parent 52aeee6022
commit b37b4e8b55
72 changed files with 1302 additions and 638 deletions

View file

@ -6,12 +6,14 @@ Version 1.1.1
* Fix projectiles flying through animating objects.
* Fix harpoon gun doing enormous damage on enemies.
* Fix train death animation.
* Fix zipline not following correct trajectory.
* Fix TR1 wolf damage value inflicted on a close bite attack.
* Fix TR1 bear various original AI issues.
* Fix TR2 knife thrower AI.
* Fix TR2 doberman crashing the game when killed by explosive weapons.
* Fix volume change in settings not affecting voice track.
* Fix several lighting bugs.
* Fix savegame count not properly increasing.
* Overhaul look mode:
- Allow for consistent and wider viewing angles while crawling, crouching, and hanging.
- Improve movement and control.
@ -25,15 +27,20 @@ Version 1.1.1
- A negative value requires a trigger to activate.
Lua API changes:
* Add Flow::EnableLoadSave() function to disable savegames.
* Add Flow::EnablePointFilter() function to disable bilinear filtering.
* Add Lara::GetAmmoType() function to read the ammo that Lara is using.
* Add Lara::GetControlLock() and Lara::SetControlLock() functions to handle controls locking.
* Add Logic.HandleEvent() function to call node events.
* Add functions to load, save, delete and check existence of savegames.
* Add DisplayStringOption.RIGHT and DisplayStringOption.BLINK flags for DisplayString.
* Make Vec2 object float-based instead of integer-based.
* Add Vec2 arithmetic for division with a number and multiplication with another Vec2.
* Add log messages warnings to functions AddCallback and RemoveCallback.
* Fix InventoryItem constructor, now it will accept compound values in Item Action parameter.
* Fix Moveable constructor forcing initial animation to 0 and hit points to 10, even if not specified.
* Fix activation of a flipped room when using SetPos() script command or position change node.
* Add extra parameter to GiveItem() function to optionally display an inventory item given to the player in the pickup summary.
Version 1.1.0
==============

View file

@ -20,6 +20,10 @@ Flow.SetTotalSecretCount(5)
Flow.EnablePointFilter(false)
-- Enable/Disable saving and loading of savegames.
Flow.EnableLoadSave(true)
-- Disable/enable flycheat globally
Flow.EnableFlyCheat(true)

View file

@ -71,7 +71,7 @@ local strings =
examine = { "Examine" },
exit_game = { "Exit Game" },
exit_to_title = { "Exit to Title" },
general_actions = { "General Actions"},
general_actions = { "General Actions" },
high = { "High" },
level_secrets_found = { "Secrets Found in Level" },
load_game = { "Load Game" },
@ -115,6 +115,7 @@ local strings =
waiting_for_input = { "Waiting For Input" },
window_title = { "TombEngine" },
windowed = { "Windowed" },
unlimited = { "Unlimited %s" },
}
TEN.Flow.SetStrings(strings)

View file

@ -80,12 +80,13 @@ namespace TEN::Hud
Life = std::max(Life, round(LIFE_BUFFER * FPS));
}
void PickupSummaryController::AddDisplayPickup(GAME_OBJECT_ID objectID, const Vector3& targetPos)
void PickupSummaryController::AddDisplayPickup(GAME_OBJECT_ID objectID, const Vector2& origin, unsigned int count)
{
constexpr auto STRING_SCALAR_MAX = 0.6f;
// TODO: Call this elsewhere, maybe in pickup.cpp. -- Sezz 2023.02.06
PickedUpObject(objectID);
// No count; return early.
if (count == 0)
return;
float life = round(DisplayPickup::LIFE_MAX * FPS);
@ -98,7 +99,7 @@ namespace TEN::Hud
if (pickup.ObjectID == objectID)
{
pickup.Count++;
pickup.Count += count;
pickup.Life = life;
pickup.StringScalar = STRING_SCALAR_MAX;
return;
@ -108,14 +109,10 @@ namespace TEN::Hud
// Create new display pickup.
auto& pickup = GetNewDisplayPickup();
auto origin = g_Renderer.Get2DPosition(targetPos);
if (!origin.has_value())
origin = Vector2::Zero;
pickup.ObjectID = objectID;
pickup.Count = 1;
pickup.Count = count;
pickup.Position =
pickup.Origin = *origin;
pickup.Origin = origin;
pickup.Target = Vector2::Zero;
pickup.Life = life;
pickup.Scale = 0.0f;
@ -125,6 +122,14 @@ namespace TEN::Hud
pickup.StringScalar = 0.0f;
}
void PickupSummaryController::AddDisplayPickup(GAME_OBJECT_ID objectID, const Vector3& pos, unsigned int count)
{
// Project 3D position to 2D origin.
auto origin = g_Renderer.Get2DPosition(pos);
AddDisplayPickup(objectID, origin.value_or(Vector2::Zero), count);
}
void PickupSummaryController::Update()
{
if (DisplayPickups.empty())
@ -176,14 +181,14 @@ namespace TEN::Hud
constexpr auto SCREEN_SCALE = Vector2(SCREEN_SPACE_RES.x * SCREEN_SCALE_COEFF, SCREEN_SPACE_RES.y * SCREEN_SCALE_COEFF);
constexpr auto SCREEN_OFFSET = Vector2(SCREEN_SPACE_RES.y * SCREEN_OFFSET_COEFF);
// Calculate positions.
// Calculate stack positions.
auto stackPositions = std::vector<Vector2>{};
stackPositions.resize(DisplayPickups.size());
for (int i = 0; i < DisplayPickups.size(); i++)
{
auto relPos = (i < STACK_HEIGHT_MAX) ? (Vector2(0.0f, i) * SCREEN_SCALE) : Vector2(0.0f, SCREEN_SPACE_RES.y);
auto pos = (SCREEN_SPACE_RES - relPos) - SCREEN_OFFSET;
stackPositions[i] = (pos);
stackPositions[i] = pos;
}
return stackPositions;
@ -191,6 +196,8 @@ namespace TEN::Hud
DisplayPickup& PickupSummaryController::GetNewDisplayPickup()
{
assertion(DisplayPickups.size() <= DISPLAY_PICKUP_COUNT_MAX, "Display pickup overflow.");
// Add and return new display pickup.
if (DisplayPickups.size() < DISPLAY_PICKUP_COUNT_MAX)
return DisplayPickups.emplace_back();
@ -212,7 +219,6 @@ namespace TEN::Hud
void PickupSummaryController::DrawDebug() const
{
g_Renderer.PrintDebugMessage("PICKUP SUMMARY DEBUG");
g_Renderer.PrintDebugMessage("Display pickups: %d", DisplayPickups.size());
g_Renderer.PrintDebugMessage("Display pickups in summary: %d", DisplayPickups.size());
}
}

View file

@ -33,14 +33,16 @@ namespace TEN::Hud
{
private:
// Constants
static constexpr auto DISPLAY_PICKUP_COUNT_MAX = 64;
static constexpr auto DISPLAY_PICKUP_COUNT_MAX = 64;
static constexpr auto DISPLAY_PICKUP_COUNT_ARG_DEFAULT = 1;
// Members
std::vector<DisplayPickup> DisplayPickups = {};
public:
// Utilities
void AddDisplayPickup(GAME_OBJECT_ID objectID, const Vector3& targetPos);
void AddDisplayPickup(GAME_OBJECT_ID objectID, const Vector2& origin, unsigned int count = DISPLAY_PICKUP_COUNT_ARG_DEFAULT);
void AddDisplayPickup(GAME_OBJECT_ID objectID, const Vector3& pos, unsigned int count = DISPLAY_PICKUP_COUNT_ARG_DEFAULT);
void Update();
void Draw() const;

View file

@ -2,8 +2,8 @@
#include "Game/Hud/TargetHighlighter.h"
#include "Game/camera.h"
#include "Game/effects/DisplaySprite.h"
#include "Game/items.h"
#include "Game/effects/ScreenSprite.h"
#include "Game/lara/lara_fire.h"
#include "Game/lara/lara_helpers.h"
#include "Math/Math.h"
@ -11,7 +11,7 @@
#include "Specific/configuration.h"
#include "Specific/trutils.h"
using namespace TEN::Effects::ScreenSprite;
using namespace TEN::Effects::DisplaySprite;
using namespace TEN::Math;
using namespace TEN::Utils;
using TEN::Renderer::g_Renderer;
@ -196,19 +196,22 @@ namespace TEN::Hud
if (crosshair.IsOffscreen())
continue;
AddScreenSprite(
AddDisplaySprite(
ID_CROSSHAIR, CROSSHAIR_SPRITE_STATIC_ELEMENT_INDEX,
crosshair.Position, crosshair.Orientation,
Vector2(crosshair.Size), crosshair.Color, 0, BLEND_MODES::BLENDMODE_ALPHABLEND);
crosshair.Position, crosshair.Orientation, Vector2(crosshair.Size), crosshair.Color,
0, DisplaySpriteAlignMode::Center, DisplaySpriteScaleMode::Fit, BLEND_MODES::BLENDMODE_ALPHABLEND);
if (crosshair.RadiusScale > EPSILON)
{
for (const auto& segment : crosshair.Segments)
{
AddScreenSprite(
short orient = crosshair.Orientation + segment.OrientOffset;
auto scale = Vector2(crosshair.Size / 2);
AddDisplaySprite(
ID_CROSSHAIR, CROSSHAIR_SPRITE_SEGMENT_ELEMENT_INDEX,
crosshair.Position + segment.PosOffset, crosshair.Orientation + segment.OrientOffset,
Vector2(crosshair.Size / 2), crosshair.Color, 0, BLEND_MODES::BLENDMODE_ALPHABLEND);
crosshair.Position + segment.PosOffset, orient, scale, crosshair.Color,
0, DisplaySpriteAlignMode::Center, DisplaySpriteScaleMode::Fit, BLEND_MODES::BLENDMODE_ALPHABLEND);
}
}
}

View file

@ -13,6 +13,7 @@
#include "Game/effects/debris.h"
#include "Game/effects/Blood.h"
#include "Game/effects/Bubble.h"
#include "Game/effects/DisplaySprite.h"
#include "Game/effects/Drip.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
@ -20,7 +21,6 @@
#include "Game/effects/Footprint.h"
#include "Game/effects/Hair.h"
#include "Game/effects/Ripple.h"
#include "Game/effects/ScreenSprite.h"
#include "Game/effects/simple_particle.h"
#include "Game/effects/smoke.h"
#include "Game/effects/spark.h"
@ -63,6 +63,7 @@ using namespace std::chrono;
using namespace TEN::Effects;
using namespace TEN::Effects::Blood;
using namespace TEN::Effects::Bubble;
using namespace TEN::Effects::DisplaySprite;
using namespace TEN::Effects::Drip;
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Environment;
@ -70,7 +71,6 @@ using namespace TEN::Effects::Explosion;
using namespace TEN::Effects::Footprint;
using namespace TEN::Effects::Hair;
using namespace TEN::Effects::Ripple;
using namespace TEN::Effects::ScreenSprite;
using namespace TEN::Effects::Smoke;
using namespace TEN::Effects::Spark;
using namespace TEN::Effects::Streamer;
@ -95,7 +95,7 @@ bool ThreadEnded;
int RequiredStartPos;
int CurrentLevel;
int LevelComplete;
int NextLevel;
int SystemNameHash = 0;
@ -120,6 +120,9 @@ int DrawPhase(bool isTitle)
g_Renderer.Render();
}
// Clear display sprites.
ClearDisplaySprites();
Camera.numberFrames = g_Renderer.Synchronize();
return Camera.numberFrames;
}
@ -147,8 +150,6 @@ GameStatus ControlPhase(int numFrames)
for (framesCount += numFrames; framesCount > 0; framesCount -= 2)
{
ClearScreenSprites();
// Controls are polled before OnControlPhase, so input data could be
// overwritten by script API methods.
HandleControls(isTitle);
@ -420,11 +421,11 @@ void CleanUp()
StreamerEffect.Clear();
ClearUnderwaterBloodParticles();
ClearBubbles();
ClearDisplaySprites();
ClearFootprints();
ClearDrips();
ClearRipples();
ClearLaserBarrierEffects();
ClearScreenSprites();
DisableSmokeParticles();
DisableSparkParticles();
DisableDebris();
@ -619,16 +620,20 @@ GameStatus HandleMenuCalls(bool isTitle)
// Does the player want to enter inventory?
if (IsClicked(In::Save) && LaraItem->HitPoints > 0 &&
g_Gui.GetInventoryMode() != InventoryMode::Save)
g_Gui.GetInventoryMode() != InventoryMode::Save &&
g_GameFlow->IsLoadSaveEnabled())
{
SaveGame::LoadSavegameInfos();
g_Gui.SetInventoryMode(InventoryMode::Save);
if (g_Gui.CallInventory(LaraItem, false))
result = GameStatus::SaveGame;
}
else if (IsClicked(In::Load) &&
g_Gui.GetInventoryMode() != InventoryMode::Load)
g_Gui.GetInventoryMode() != InventoryMode::Load &&
g_GameFlow->IsLoadSaveEnabled())
{
SaveGame::LoadSavegameInfos();
g_Gui.SetInventoryMode(InventoryMode::Load);
if (g_Gui.CallInventory(LaraItem, false))
@ -673,8 +678,16 @@ GameStatus HandleGlobalInputEvents(bool isTitle)
}
// Check if level has been completed.
if (LevelComplete)
// Negative NextLevel indicates that a savegame must be loaded from corresponding slot.
if (NextLevel > 0)
{
return GameStatus::LevelComplete;
}
else if (NextLevel < 0)
{
g_GameFlow->SelectedSaveGame = -(NextLevel + 1);
return GameStatus::LoadGame;
}
return GameStatus::None;
}

View file

@ -57,7 +57,7 @@ extern bool ThreadEnded;
extern int RequiredStartPos;
extern int CurrentLevel;
extern int LevelComplete;
extern int NextLevel;
extern int SystemNameHash;
extern bool InItemControlLoop;

View file

@ -332,7 +332,7 @@ void Turn180(ItemInfo* item)
void FinishLevel(ItemInfo* item)
{
LevelComplete = CurrentLevel + 1;
NextLevel = CurrentLevel + 1;
}
void VoidEffect(ItemInfo* item)

View file

@ -751,7 +751,7 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, VolumeActivator activat
case TO_FINISH:
RequiredStartPos = false;
LevelComplete = CurrentLevel + 1;
NextLevel = CurrentLevel + 1;
break;
case TO_CD:
@ -785,20 +785,14 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, VolumeActivator activat
if (!((int)set.Activators & activatorType))
continue;
switch (trigger & TIMER_BITS)
int eventType = trigger & TIMER_BITS;
if (eventType >= (int)VolumeEventType::Count)
{
case 0:
HandleEvent(set.OnEnter, activator);
break;
case 1:
HandleEvent(set.OnInside, activator);
break;
case 2:
HandleEvent(set.OnLeave, activator);
break;
TENLog("Unknown volume event type encountered for legacy trigger " + std::to_string(eventType), LogLevel::Warning);
continue;
}
HandleEvent(set.Events[eventType], activator);
}
break;

View file

@ -66,6 +66,35 @@ namespace TEN::Control::Volumes
event.CallCounter--;
}
bool HandleEvent(const std::string& name, VolumeEventType eventType, VolumeActivator activator)
{
// Cache last used event sets so that whole list is not searched every time user calls this.
static VolumeEventSet* lastEventSetPtr = nullptr;
if (lastEventSetPtr != nullptr && lastEventSetPtr->Name != name)
lastEventSetPtr = nullptr;
if (lastEventSetPtr == nullptr)
{
for (auto& eventSet : g_Level.EventSets)
{
if (eventSet.Name == name)
{
lastEventSetPtr = &eventSet;
break;
}
}
}
if (lastEventSetPtr != nullptr)
{
HandleEvent(lastEventSetPtr->Events[(int)eventType], activator);
return true;
}
return false;
}
void TestVolumes(short roomNumber, const BoundingOrientedBox& box, VolumeActivatorFlags activatorFlag, VolumeActivator activator)
{
if (roomNumber == NO_ROOM)
@ -120,14 +149,14 @@ namespace TEN::Control::Volumes
GameTimer
});
HandleEvent(set.OnEnter, activator);
HandleEvent(set.Events[(int)VolumeEventType::Enter], activator);
}
else
{
entryPtr->Status = VolumeStateStatus::Inside;
entryPtr->Timestamp = GameTimer;
HandleEvent(set.OnInside, activator);
HandleEvent(set.Events[(int)VolumeEventType::Inside], activator);
}
}
else if (entryPtr != nullptr)
@ -140,7 +169,7 @@ namespace TEN::Control::Volumes
entryPtr->Status = VolumeStateStatus::Leaving;
entryPtr->Timestamp = GameTimer;
HandleEvent(set.OnLeave, activator);
HandleEvent(set.Events[(int)VolumeEventType::Leave], activator);
}
}
}
@ -223,21 +252,24 @@ namespace TEN::Control::Volumes
unsigned int nodeCount = 0;
for (const auto& set : g_Level.EventSets)
{
if ((set.OnEnter.Mode == VolumeEventMode::Nodes) && !set.OnEnter.Data.empty())
if ((set.Events[(int)VolumeEventType::Enter].Mode == VolumeEventMode::Nodes) &&
!set.Events[(int)VolumeEventType::Enter].Data.empty())
{
g_GameScript->ExecuteString(set.OnEnter.Data);
g_GameScript->ExecuteString(set.Events[(int)VolumeEventType::Enter].Data);
nodeCount++;
}
if ((set.OnInside.Mode == VolumeEventMode::Nodes) && !set.OnInside.Data.empty())
if ((set.Events[(int)VolumeEventType::Inside].Mode == VolumeEventMode::Nodes) &&
!set.Events[(int)VolumeEventType::Inside].Data.empty())
{
g_GameScript->ExecuteString(set.OnInside.Data);
g_GameScript->ExecuteString(set.Events[(int)VolumeEventType::Inside].Data);
nodeCount++;
}
if ((set.OnLeave.Mode == VolumeEventMode::Nodes) && !set.OnLeave.Data.empty())
if ((set.Events[(int)VolumeEventType::Leave].Mode == VolumeEventMode::Nodes) &&
!set.Events[(int)VolumeEventType::Leave].Data.empty())
{
g_GameScript->ExecuteString(set.OnLeave.Data);
g_GameScript->ExecuteString(set.Events[(int)VolumeEventType::Leave].Data);
nodeCount++;
}
}

View file

@ -44,6 +44,7 @@ namespace TEN::Control::Volumes
void TestVolumes(CAMERA_INFO* camera);
void HandleEvent(VolumeEvent& event, VolumeActivator& activator);
bool HandleEvent(const std::string& name, VolumeEventType eventType, VolumeActivator activator);
void InitializeNodeScripts();
}

View file

@ -20,6 +20,15 @@ namespace TEN::Control::Volumes
Nodes
};
enum class VolumeEventType
{
Enter,
Inside,
Leave,
Count
};
enum class VolumeActivatorFlags
{
None = (0 << 0),
@ -42,11 +51,8 @@ namespace TEN::Control::Volumes
struct VolumeEventSet
{
std::string Name = {};
std::string Name = {};
VolumeActivatorFlags Activators = VolumeActivatorFlags::None;
VolumeEvent OnEnter = {};
VolumeEvent OnLeave = {};
VolumeEvent OnInside = {};
std::array<VolumeEvent, int(VolumeEventType::Count)> Events = {};
};
};

View file

@ -0,0 +1,38 @@
#include "framework.h"
#include "Game/effects/DisplaySprite.h"
#include "Game/effects/effects.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/objectslist.h"
#include "Renderer/Renderer11.h"
using namespace TEN::Math;
namespace TEN::Effects::DisplaySprite
{
std::vector<DisplaySprite> DisplaySprites = {};
void AddDisplaySprite(GAME_OBJECT_ID objectID, int spriteID, const Vector2& pos, short orient, const Vector2& scale, const Vector4& color,
int priority, DisplaySpriteAlignMode alignMode, DisplaySpriteScaleMode scaleMode, BLEND_MODES blendMode)
{
auto displaySprite = DisplaySprite{};
displaySprite.ObjectID = objectID;
displaySprite.SpriteID = spriteID;
displaySprite.Position = pos;
displaySprite.Orientation = orient;
displaySprite.Scale = scale;
displaySprite.Color = color;
displaySprite.Priority = priority;
displaySprite.AlignMode = alignMode;
displaySprite.ScaleMode = scaleMode;
displaySprite.BlendMode = blendMode;
DisplaySprites.push_back(displaySprite);
}
void ClearDisplaySprites()
{
DisplaySprites.clear();
}
}

View file

@ -0,0 +1,48 @@
#pragma once
#include "Objects/game_object_ids.h"
#include "Renderer/Renderer11Enums.h"
namespace TEN::Effects::DisplaySprite
{
enum class DisplaySpriteAlignMode
{
Center,
CenterTop,
CenterBottom,
CenterLeft,
CenterRight,
TopLeft,
TopRight,
BottomLeft,
BottomRight
};
enum class DisplaySpriteScaleMode
{
Fit,
Fill,
Stretch
};
struct DisplaySprite
{
GAME_OBJECT_ID ObjectID = ID_DEFAULT_SPRITES;
int SpriteID = 0;
Vector2 Position = Vector2::Zero;
short Orientation = 0;
Vector2 Scale = Vector2::One;
Vector4 Color = Vector4::One;
int Priority = 0;
DisplaySpriteAlignMode AlignMode = DisplaySpriteAlignMode::Center;
DisplaySpriteScaleMode ScaleMode = DisplaySpriteScaleMode::Fit;
BLEND_MODES BlendMode = BLENDMODE_ALPHABLEND;
};
extern std::vector<DisplaySprite> DisplaySprites;
void AddDisplaySprite(GAME_OBJECT_ID objectID, int spriteID, const Vector2& pos, short orient, const Vector2& scale, const Vector4& color,
int priority, DisplaySpriteAlignMode alignMode, DisplaySpriteScaleMode scaleMode, BLEND_MODES blendMode);
void ClearDisplaySprites();
}

View file

@ -1,37 +0,0 @@
#include "framework.h"
#include "Game/effects/ScreenSprite.h"
#include "Game/effects/effects.h"
#include "Game/Setup.h"
#include "Math/Math.h"
#include "Objects/objectslist.h"
#include "Renderer/Renderer11.h"
using namespace TEN::Math;
namespace TEN::Effects::ScreenSprite
{
std::vector<ScreenSprite> ScreenSprites = {};
void AddScreenSprite(GAME_OBJECT_ID objectID, int spriteIndex, const Vector2& pos, short orient, const Vector2& size,
const Vector4& color, int priority, BLEND_MODES blendMode)
{
auto screenSprite = ScreenSprite{};
screenSprite.ObjectID = objectID;
screenSprite.SpriteIndex = spriteIndex;
screenSprite.Position = pos;
screenSprite.Orientation = orient;
screenSprite.Size = size;
screenSprite.Color = color;
screenSprite.Priority = priority;
screenSprite.BlendMode = blendMode;
ScreenSprites.push_back(screenSprite);
}
void ClearScreenSprites()
{
ScreenSprites.clear();
}
}

View file

@ -1,26 +0,0 @@
#pragma once
#include "Objects/game_object_ids.h"
#include "Renderer/Renderer11Enums.h"
namespace TEN::Effects::ScreenSprite
{
struct ScreenSprite
{
GAME_OBJECT_ID ObjectID = ID_DEFAULT_SPRITES;
int SpriteIndex = 0;
Vector2 Position = Vector2::Zero;
short Orientation = 0;
Vector2 Size = Vector2::One;
Vector4 Color = Vector4::One;
int Priority = 0;
BLEND_MODES BlendMode = BLENDMODE_ALPHABLEND;
};
extern std::vector<ScreenSprite> ScreenSprites;
void AddScreenSprite(GAME_OBJECT_ID objectID, int spriteIndex, const Vector2& pos, short orient, const Vector2& size,
const Vector4& color, int priority, BLEND_MODES blendMode);
void ClearScreenSprites();
}

View file

@ -181,7 +181,7 @@ namespace TEN::Gui
return false;
// Avoid Select or Action release interference when entering inventory.
if (GetActionTimeActive(In::Select) < TimeInMenu || GetActionTimeActive(In::Action) < TimeInMenu)
if (GetActionTimeActive(In::Select) < TimeInMenu && GetActionTimeActive(In::Action) < TimeInMenu)
return true;
return false;
@ -202,7 +202,7 @@ namespace TEN::Gui
return Rings[(int)ringType];
}
short GuiController::GetSelectedOption()
int GuiController::GetSelectedOption()
{
return SelectedOption;
}
@ -295,7 +295,7 @@ namespace TEN::Gui
switch (MenuToDisplay)
{
case Menu::Title:
OptionCount = numTitleOptions;
OptionCount = g_GameFlow->IsLoadSaveEnabled() ? numTitleOptions : (numTitleOptions - 1);
break;
case Menu::SelectLevel:
@ -378,7 +378,12 @@ namespace TEN::Gui
if (MenuToDisplay == Menu::Title)
{
switch (SelectedOption)
// Skip load game entry if loading and saving is disabled.
int realSelectedOption = SelectedOption;
if (!g_GameFlow->IsLoadSaveEnabled() && SelectedOption > TitleOption::NewGame)
realSelectedOption++;
switch (realSelectedOption)
{
case TitleOption::NewGame:
if (g_GameFlow->IsLevelSelectEnabled())
@ -388,7 +393,9 @@ namespace TEN::Gui
MenuToDisplay = Menu::SelectLevel;
}
else
{
inventoryResult = InventoryResult::NewGame;
}
break;
@ -396,6 +403,7 @@ namespace TEN::Gui
selectedOptionBackup = SelectedOption;
SelectedOption = 0;
MenuToDisplay = Menu::LoadGame;
SaveGame::LoadSavegameInfos();
break;
case TitleOption::Options:
@ -1687,7 +1695,7 @@ namespace TEN::Gui
if (lara->Inventory.Diary.Present)
InsertObjectIntoList(INV_OBJECT_DIARY);
if (g_GameFlow->EnableLoadSave)
if (g_GameFlow->IsLoadSaveEnabled())
{
InsertObjectIntoList(INV_OBJECT_LOAD_FLOPPY);
InsertObjectIntoList(INV_OBJECT_SAVE_FLOPPY);
@ -2539,12 +2547,12 @@ namespace TEN::Gui
break;
case MenuType::Load:
// fill_up_savegames_array // Maybe not?
SaveGame::LoadSavegameInfos();
SetInventoryMode(InventoryMode::Load);
break;
case MenuType::Save:
// fill_up_savegames_array
SaveGame::LoadSavegameInfos();
SetInventoryMode(InventoryMode::Save);
break;
@ -2709,7 +2717,7 @@ namespace TEN::Gui
if (AmmoObjectList[n].Amount == -1)
{
sprintf(&invTextBuffer[0], "Unlimited %s", g_GameFlow->GetString(InventoryObjectTable[AmmoObjectList[n].InventoryItem].ObjectName));
sprintf(&invTextBuffer[0], g_GameFlow->GetString(STRING_UNLIMITED), g_GameFlow->GetString(InventoryObjectTable[AmmoObjectList[n].InventoryItem].ObjectName));
}
else
{
@ -3032,7 +3040,7 @@ namespace TEN::Gui
{
if (numItems == -1)
{
sprintf(textBuffer, "Unlimited %s", g_GameFlow->GetString(invObject.ObjectName));
sprintf(textBuffer, g_GameFlow->GetString(STRING_UNLIMITED), g_GameFlow->GetString(invObject.ObjectName));
}
else
{
@ -3393,7 +3401,7 @@ namespace TEN::Gui
}
}
short GuiController::GetLoadSaveSelection()
int GuiController::GetLoadSaveSelection()
{
return SelectedSaveSlot;
}
@ -3463,7 +3471,6 @@ namespace TEN::Gui
if (GuiIsSelected())
{
SoundEffect(SFX_TR4_MENU_CHOOSE, nullptr, SoundEnvironment::Always);
g_GameScript->OnSave();
SaveGame::Save(SelectedSaveSlot);
return true;
}

View file

@ -133,7 +133,7 @@ namespace TEN::Gui
int OptionCount;
int SelectedSaveSlot;
float TimeInMenu = 0.0f;
float TimeInMenu = -1.0f;
SettingsData CurrentSettings;
// Inventory variables
@ -183,14 +183,14 @@ namespace TEN::Gui
// Getters
const InventoryRing& GetRing(RingTypes ringType);
short GetSelectedOption();
int GetSelectedOption();
Menu GetMenuToDisplay();
InventoryMode GetInventoryMode();
int GetInventoryItemChosen();
int GetEnterInventory();
int GetLastInventoryItem();
SettingsData& GetCurrentSettings();
short GetLoadSaveSelection();
int GetLoadSaveSelection();
// Setters
void SetSelectedOption(int menu);

View file

@ -203,6 +203,7 @@ void CollectCarriedItems(ItemInfo* item)
{
auto* pickupItem = &g_Level.Items[pickupNumber];
PickedUpObject(pickupItem->ObjectNumber);
g_Hud.PickupSummary.AddDisplayPickup(pickupItem->ObjectNumber, pickupItem->Pose.Position.ToVector3());
KillItem(pickupNumber);
@ -248,7 +249,9 @@ void CollectMultiplePickups(int itemNumber)
continue;
}
PickedUpObject(currentItem->ObjectNumber);
g_Hud.PickupSummary.AddDisplayPickup(currentItem->ObjectNumber, currentItem->Pose.Position.ToVector3());
if (currentItem->TriggerFlags & (1 << 8))
{
for (int i = 0; i < g_Level.NumItems; i++)
@ -282,7 +285,9 @@ void DoPickup(ItemInfo* laraItem)
if (pickupItem->ObjectNumber == ID_BURNING_TORCH_ITEM)
{
PickedUpObject(ID_BURNING_TORCH_ITEM);
g_Hud.PickupSummary.AddDisplayPickup(ID_BURNING_TORCH_ITEM, pickupItem->Pose.Position.ToVector3());
GetFlameTorch();
lara->Torch.IsLit = (pickupItem->ItemFlags[3] & 1);
@ -318,6 +323,7 @@ void DoPickup(ItemInfo* laraItem)
return;
}
PickedUpObject(pickupItem->ObjectNumber);
g_Hud.PickupSummary.AddDisplayPickup(pickupItem->ObjectNumber, pickupItem->Pose.Position.ToVector3());
HideOrDisablePickup(*pickupItem);
@ -329,9 +335,11 @@ void DoPickup(ItemInfo* laraItem)
{
if (laraItem->Animation.AnimNumber == LA_CROWBAR_PRY_WALL_SLOW)
{
PickedUpObject(ID_CROWBAR_ITEM);
g_Hud.PickupSummary.AddDisplayPickup(ID_CROWBAR_ITEM, pickupItem->Pose.Position.ToVector3());
lara->Inventory.HasCrowbar = true;
KillItem(pickupItemNumber);
lara->Inventory.HasCrowbar = true;
}
else if (laraItem->Animation.ActiveState == LS_PICKUP ||
laraItem->Animation.ActiveState == LS_PICKUP_FROM_CHEST ||
@ -344,7 +352,9 @@ void DoPickup(ItemInfo* laraItem)
return;
}
PickedUpObject(pickupItem->ObjectNumber);
g_Hud.PickupSummary.AddDisplayPickup(pickupItem->ObjectNumber, pickupItem->Pose.Position.ToVector3());
if (pickupItem->TriggerFlags & (1 << 8))
{
for (int i = 0; i < g_Level.NumItems; i++)
@ -1261,6 +1271,7 @@ void SearchObjectControl(short itemNumber)
if (Objects[item2->ObjectNumber].isPickup)
{
PickedUpObject(item2->ObjectNumber);
g_Hud.PickupSummary.AddDisplayPickup(item2->ObjectNumber, item2->Pose.Position.ToVector3());
KillItem(item->ItemFlags[1]);
}
@ -1280,7 +1291,6 @@ void SearchObjectControl(short itemNumber)
CollectCarriedItems(item);
}
}
if (item->Status == ITEM_DEACTIVATED)
{

View file

@ -45,7 +45,9 @@ using namespace TEN::Entities::TR4;
namespace Save = TEN::Save;
const std::string SAVEGAME_PATH = "Save//";
constexpr auto SAVEGAME_MAX_SLOT = 99;
constexpr auto SAVEGAME_PATH = "Save//";
constexpr auto SAVEGAME_FILE_MASK = "savegame.";
GameStats Statistics;
SaveGameHeader SavegameInfos[SAVEGAME_MAX];
@ -62,21 +64,20 @@ void SaveGame::LoadSavegameInfos()
if (!std::filesystem::is_directory(FullSaveDirectory))
return;
// Reset overall savegame count.
LastSaveGame = 0;
// Try loading savegame.
for (int i = 0; i < SAVEGAME_MAX; i++)
{
auto fileName = FullSaveDirectory + "savegame." + std::to_string(i);
auto savegamePtr = fopen(fileName.c_str(), "rb");
if (savegamePtr == nullptr)
if (!DoesSaveGameExist(i, true))
continue;
fclose(savegamePtr);
SavegameInfos[i].Present = true;
SaveGame::LoadHeader(i, &SavegameInfos[i]);
fclose(savegamePtr);
if (SavegameInfos[i].Count > LastSaveGame)
LastSaveGame = SavegameInfos[i].Count;
}
}
@ -163,6 +164,35 @@ Vector4 ToVector4(const Save::Vector4* vec)
return Vector4(vec->x(), vec->y(), vec->z(), vec->w());
}
bool SaveGame::IsSaveGameSlotValid(int slot)
{
if (slot < 0 || slot > SAVEGAME_MAX_SLOT)
{
TENLog("Attempted to access invalid savegame slot " + std::to_string(slot), LogLevel::Warning);
return false;
}
return true;
}
bool SaveGame::DoesSaveGameExist(int slot, bool silent)
{
if (!std::filesystem::is_regular_file(GetSavegameFilename(slot)))
{
if (!silent)
TENLog("Attempted to access missing savegame slot " + std::to_string(slot), LogLevel::Warning);
return false;
}
return true;
}
std::string SaveGame::GetSavegameFilename(int slot)
{
return (FullSaveDirectory + SAVEGAME_FILE_MASK + std::to_string(slot));
}
#define SaveVec(Type, Data, TableBuilder, UnionType, SaveType, ConversionFunc) \
auto data = std::get<(int)Type>(Data); \
TableBuilder vtb{ fbb }; \
@ -178,7 +208,15 @@ void SaveGame::Init(const std::string& gameDirectory)
bool SaveGame::Save(int slot)
{
auto fileName = FullSaveDirectory + "savegame." + std::to_string(slot);
if (!IsSaveGameSlotValid(slot))
return false;
g_GameScript->OnSave();
// Savegame infos need to be reloaded so that last savegame counter properly increases.
SaveGame::LoadSavegameInfos();
auto fileName = GetSavegameFilename(slot);
TENLog("Saving to savegame: " + fileName, LogLevel::Info);
ItemInfo itemToSerialize{};
@ -805,9 +843,9 @@ bool SaveGame::Save(int slot)
{
Save::EventSetCallCountersBuilder serializedEventSetCallCounter{ fbb };
serializedEventSetCallCounter.add_on_enter(set.OnEnter.CallCounter);
serializedEventSetCallCounter.add_on_inside(set.OnInside.CallCounter);
serializedEventSetCallCounter.add_on_leave(set.OnLeave.CallCounter);
serializedEventSetCallCounter.add_on_enter(set.Events[(int)VolumeEventType::Enter].CallCounter);
serializedEventSetCallCounter.add_on_inside(set.Events[(int)VolumeEventType::Inside].CallCounter);
serializedEventSetCallCounter.add_on_leave(set.Events[(int)VolumeEventType::Leave].CallCounter);
auto serializedEventSetCallCounterOffset = serializedEventSetCallCounter.Finish();
serializedEventSetCallCounters.push_back(serializedEventSetCallCounterOffset);
@ -1351,7 +1389,13 @@ bool SaveGame::Save(int slot)
bool SaveGame::Load(int slot)
{
auto fileName = FullSaveDirectory + "savegame." + std::to_string(slot);
if (!IsSaveGameSlotValid(slot))
return false;
if (!DoesSaveGameExist(slot))
return false;
auto fileName = GetSavegameFilename(slot);
TENLog("Loading from savegame: " + fileName, LogLevel::Info);
std::ifstream file;
@ -1366,7 +1410,6 @@ bool SaveGame::Load(int slot)
const Save::SaveGame* s = Save::GetSaveGame(buffer.get());
// Statistics
LastSaveGame = s->header()->count();
GameTimer = s->header()->timer();
Statistics.Game.AmmoHits = s->game()->ammo_hits();
@ -1868,9 +1911,9 @@ bool SaveGame::Load(int slot)
{
auto cc_saved = s->call_counters()->Get(i);
g_Level.EventSets[i].OnEnter.CallCounter = cc_saved->on_enter();
g_Level.EventSets[i].OnInside.CallCounter = cc_saved->on_inside();
g_Level.EventSets[i].OnLeave.CallCounter = cc_saved->on_leave();
g_Level.EventSets[i].Events[(int)VolumeEventType::Enter].CallCounter = cc_saved->on_enter();
g_Level.EventSets[i].Events[(int)VolumeEventType::Inside].CallCounter = cc_saved->on_inside();
g_Level.EventSets[i].Events[(int)VolumeEventType::Leave].CallCounter = cc_saved->on_leave();
}
}
@ -2206,7 +2249,13 @@ bool SaveGame::Load(int slot)
bool SaveGame::LoadHeader(int slot, SaveGameHeader* header)
{
auto fileName = FullSaveDirectory + "savegame." + std::to_string(slot);
if (!IsSaveGameSlotValid(slot))
return false;
if (!DoesSaveGameExist(slot))
return false;
auto fileName = GetSavegameFilename(slot);
std::ifstream file;
file.open(fileName, std::ios_base::app | std::ios_base::binary);
@ -2231,3 +2280,14 @@ bool SaveGame::LoadHeader(int slot, SaveGameHeader* header)
return true;
}
void SaveGame::Delete(int slot)
{
if (!IsSaveGameSlotValid(slot))
return;
if (!DoesSaveGameExist(slot))
return;
std::filesystem::remove(GetSavegameFilename(slot));
}

View file

@ -46,6 +46,9 @@ class SaveGame
private:
static FileStream* StreamPtr;
static std::string FullSaveDirectory;
static std::string SaveGame::GetSavegameFilename(int slot);
static bool IsSaveGameSlotValid(int slot);
public:
static int LastSaveGame;
@ -55,4 +58,7 @@ public:
static bool LoadHeader(int slot, SaveGameHeader* header);
static bool Save(int slot);
static void LoadSavegameInfos();
static void Delete(int slot);
static bool DoesSaveGameExist(int slot, bool silent = false);
};

View file

@ -360,9 +360,9 @@ void PuzzleDone(ItemInfo* item, short itemNumber)
void PuzzleHole(ItemInfo* item, short itemNumber)
{
// Display pickup object. TODO: Get offset.
auto objectID = GAME_OBJECT_ID(item->ObjectNumber - (ID_PUZZLE_DONE1 - ID_PUZZLE_ITEM1));
g_Hud.PickupSummary.AddDisplayPickup(objectID, item->Pose.Position.ToVector3());
PickedUpObject(objectID);
g_Hud.PickupSummary.AddDisplayPickup(objectID, item->Pose.Position.ToVector3()); // TODO: Get appealing position offset.
item->ItemFlags[1] = true;

View file

@ -72,7 +72,7 @@ namespace TEN::Entities::Creatures::TR2
InitializeItem(skidooItemNumber); g_Level.NumItems++;
// Register snowmobile gun for driver to control.
riderItem.Data = skidooItemNumber;
riderItem.ItemFlags[0] = skidooItemNumber;
}
void InitializeSkidooMan(short itemNumber)
@ -118,17 +118,17 @@ namespace TEN::Entities::Creatures::TR2
void SkidooManControl(short riderItemNumber)
{
auto& riderItem = g_Level.Items[riderItemNumber];
if (!riderItem.Data)
if (!riderItem.ItemFlags[0])
{
// Create snowmobile.
CreateSkidooGun(riderItem);
if (!riderItem.Data)
if (!riderItem.ItemFlags[0])
TENLog("Skidoo rider data does not contain skidoo item ID.", LogLevel::Error);
return;
}
int skidooItemNumber = (short)riderItem.Data;
int skidooItemNumber = (short)riderItem.ItemFlags[0];
auto* skidooItem = &g_Level.Items[skidooItemNumber];
if (!skidooItem->Data)

View file

@ -87,7 +87,6 @@ namespace TEN::Traps::TR5
{
constexpr auto VEL_ACCEL = 5.0f;
constexpr auto VEL_MAX = 100.0f;
constexpr auto SLOPE_ANGLE = -ANGLE(11.25f);
auto& zipLineItem = g_Level.Items[itemNumber];
auto& laraItem = *LaraItem;
@ -124,8 +123,10 @@ namespace TEN::Traps::TR5
zipLineItem.Animation.Velocity.y += VEL_ACCEL;
// Translate.
auto headingOrient = EulerAngles(SLOPE_ANGLE, zipLineItem.Pose.Orientation.y, 0);
// TODO: Use proper calculation of the trajectory instead of bitwise operation.
auto headingOrient = EulerAngles(0, zipLineItem.Pose.Orientation.y, 0);
TranslateItem(&zipLineItem, headingOrient, zipLineItem.Animation.Velocity.y);
zipLineItem.Pose.Position.y += ((int)zipLineItem.Animation.Velocity.y >> 2);
int vPos = zipLineItem.Pose.Position.y + CLICK(0.25f);
auto pointColl = GetCollision(&zipLineItem, zipLineItem.Pose.Orientation.y, zipLineItem.Animation.Velocity.y);

View file

@ -1,5 +1,5 @@
#include "framework.h"
#include "RenderView.h"
#include "Renderer/RenderView/RenderView.h"
namespace TEN::Renderer
{
@ -46,9 +46,9 @@ namespace TEN::Renderer
RoomsToDraw.clear();
LightsToDraw.clear();
SpritesToDraw.clear();
DisplaySpritesToDraw.clear();
SortedStaticsToDraw.clear();
FogBulbsToDraw.clear();
Sprites2DToDraw.clear();
}
RenderViewCamera::RenderViewCamera(CAMERA_INFO* cam, float roll, float fov, float n, float f, int w, int h)

View file

@ -1,16 +1,12 @@
#pragma once
#include <vector>
#include <d3d11.h>
#include <SimpleMath.h>
#include "Game/camera.h"
#include "Renderer/Frustum.h"
#include "Renderer/ConstantBuffers/CameraMatrixBuffer.h"
#include "Renderer/Frustum.h"
#include "Specific/memory/LinearArrayBuffer.h"
#include "Renderer/RendererSprite2D.h"
#include "Renderer/RendererSprites.h"
#include "Renderer/RendererTransparentFace.h"
#include "Renderer/Structures/RendererFogBulb.h"
#include "Specific/memory/LinearArrayBuffer.h"
#include "Renderer/RendererSprite2D.h"
namespace TEN::Renderer
{
@ -20,6 +16,7 @@ namespace TEN::Renderer
struct RendererEffect;
struct RendererRoom;
struct RendererTransparentFace;
constexpr auto MAX_ROOMS_DRAW = 256;
constexpr auto MAX_STATICS_DRAW = 128;
constexpr auto MAX_EFFECTS_DRAW = 16;
@ -27,9 +24,6 @@ namespace TEN::Renderer
constexpr auto MAX_LIGHTS_DRAW = 48;
constexpr auto MAX_FOG_BULBS_DRAW = 32;
constexpr auto MAX_SPRITES_DRAW = 512;
using DirectX::SimpleMath::Vector3;
using DirectX::SimpleMath::Vector2;
using DirectX::SimpleMath::Matrix;
struct RenderViewCamera
{
@ -52,16 +46,18 @@ namespace TEN::Renderer
struct RenderView
{
RenderViewCamera Camera;
D3D11_VIEWPORT Viewport;
std::vector<RendererRoom*> RoomsToDraw;
std::vector<RendererLight*> LightsToDraw;
std::vector<RendererFogBulb> FogBulbsToDraw;
std::vector<RendererSpriteToDraw> SpritesToDraw;
std::vector<RendererSprite2DToDraw> Sprites2DToDraw;
std::map<int, std::vector<RendererStatic*>> SortedStaticsToDraw;
D3D11_VIEWPORT Viewport;
std::vector<RendererRoom*> RoomsToDraw = {};
std::vector<RendererLight*> LightsToDraw = {};
std::vector<RendererFogBulb> FogBulbsToDraw = {};
std::vector<RendererSpriteToDraw> SpritesToDraw = {};
std::vector<RendererDisplaySpriteToDraw> DisplaySpritesToDraw = {};
std::map<int, std::vector<RendererStatic*>> SortedStaticsToDraw = {};
RenderView(CAMERA_INFO* cam, float roll, float fov, float nearPlane, float farPlane, int w, int h);
RenderView(const Vector3& pos, const Vector3& dir, const Vector3& up, int w, int h, int room, float nearPlane, float farPlane, float fov);
void fillConstantBuffer(CCameraMatrixBuffer& bufferToFill);
void clear();
};

View file

@ -555,7 +555,7 @@ namespace TEN::Renderer
void DrawEffect(RenderView& view, RendererEffect* effect, RendererPass rendererPass);
void DrawSplashes(RenderView& view);
void DrawSprites(RenderView& view);
void DrawScreenSprites(RenderView& view);
void DrawDisplaySprites(RenderView& view);
void DrawSortedFaces(RenderView& view);
void DrawLines3D(RenderView& view);
void DrawLinesIn2DSpace();
@ -746,9 +746,9 @@ namespace TEN::Renderer
std::optional<Vector2> Get2DPosition(const Vector3& pos) const;
Vector3 GetAbsEntityBonePosition(int itemNumber, int jointIndex, const Vector3& relOffset = Vector3::Zero);
void AddScreenSprite(RendererSprite* spritePtr, const Vector2& pos2D, short orient, const Vector2& size,
const Vector4& color, int priority, BLEND_MODES blendMode, RenderView& renderView);
void CollectScreenSprites(RenderView& renderView);
void AddDisplaySprite(RendererSprite* spritePtr, const Vector2& pos2D, short orient, const Vector2& size, const Vector4& color,
int priority, BLEND_MODES blendMode, RenderView& renderView);
void CollectDisplaySprites(RenderView& renderView);
};
extern Renderer11 g_Renderer;

View file

@ -609,71 +609,70 @@ namespace TEN::Renderer
void Renderer11::DrawScarabs(RenderView& view)
{
if (!Objects[ID_LITTLE_BEETLE].loaded)
{
return;
}
RendererMesh* mesh = GetMesh(Objects[ID_LITTLE_BEETLE].meshIndex + ((Wibble >> 2) % 2));
auto& mesh = *GetMesh(Objects[ID_LITTLE_BEETLE].meshIndex + ((Wibble >> 2) % 2));
int littleBeetlesCount = 0;
m_context->VSSetShader(m_vsInstancedStaticMeshes.Get(), nullptr, 0);
m_context->PSSetShader(m_psInstancedStaticMeshes.Get(), nullptr, 0);
UINT stride = sizeof(RendererVertex);
UINT offset = 0;
m_context->IASetVertexBuffers(0, 1, m_moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->IASetInputLayout(m_inputLayout.Get());
m_context->IASetIndexBuffer(m_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
SetAlphaTest(ALPHA_TEST_GREATER_THAN, ALPHA_TEST_THRESHOLD);
BindConstantBufferVS(CB_INSTANCED_STATICS, m_cbInstancedStaticMeshBuffer.get());
BindConstantBufferPS(CB_INSTANCED_STATICS, m_cbInstancedStaticMeshBuffer.get());
int beetleCount = 0;
for (int i = 0; i < TEN::Entities::TR4::NUM_BEETLES; i++)
{
auto* beetle = &TEN::Entities::TR4::BeetleSwarm[i];
auto& beetle = TEN::Entities::TR4::BeetleSwarm[i];
if (beetle->On)
if (beetle.On)
{
RendererRoom& room = m_rooms[beetle->RoomNumber];
auto& room = m_rooms[beetle.RoomNumber];
Matrix translation = Matrix::CreateTranslation(beetle->Pose.Position.x, beetle->Pose.Position.y, beetle->Pose.Position.z);
Matrix rotation = beetle->Pose.Orientation.ToRotationMatrix();
Matrix world = rotation * translation;
auto tmatrix = Matrix::CreateTranslation(beetle.Pose.Position.ToVector3());
auto rotMatrix = beetle.Pose.Orientation.ToRotationMatrix();
auto worldMatrix = rotMatrix * tmatrix;
m_stInstancedStaticMeshBuffer.StaticMeshes[littleBeetlesCount].World = world;
m_stInstancedStaticMeshBuffer.StaticMeshes[littleBeetlesCount].Ambient = room.AmbientLight;
m_stInstancedStaticMeshBuffer.StaticMeshes[littleBeetlesCount].Color = Vector4::One;
m_stInstancedStaticMeshBuffer.StaticMeshes[littleBeetlesCount].LightMode = mesh->LightMode;
BindInstancedStaticLights(room.LightsToDraw, littleBeetlesCount);
m_stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].World = worldMatrix;
m_stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].Ambient = room.AmbientLight;
m_stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].Color = Vector4::One;
m_stInstancedStaticMeshBuffer.StaticMeshes[beetleCount].LightMode = mesh.LightMode;
BindInstancedStaticLights(room.LightsToDraw, beetleCount);
littleBeetlesCount++;
}
}
beetleCount++;
if (littleBeetlesCount > 0)
{
m_context->VSSetShader(m_vsInstancedStaticMeshes.Get(), nullptr, 0);
m_context->PSSetShader(m_psInstancedStaticMeshes.Get(), nullptr, 0);
UINT stride = sizeof(RendererVertex);
UINT offset = 0;
m_context->IASetVertexBuffers(0, 1, m_moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->IASetInputLayout(m_inputLayout.Get());
m_context->IASetIndexBuffer(m_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
SetAlphaTest(ALPHA_TEST_GREATER_THAN, ALPHA_TEST_THRESHOLD);
m_cbInstancedStaticMeshBuffer.updateData(m_stInstancedStaticMeshBuffer, m_context.Get());
BindConstantBufferVS(CB_INSTANCED_STATICS, m_cbInstancedStaticMeshBuffer.get());
BindConstantBufferPS(CB_INSTANCED_STATICS, m_cbInstancedStaticMeshBuffer.get());
for (auto& bucket : mesh->Buckets)
{
if (bucket.NumVertices == 0 && bucket.BlendMode == BLEND_MODES::BLENDMODE_OPAQUE)
// If max bucket size reached or last beetle, draw beetles.
if (beetleCount == INSTANCED_STATIC_MESH_BUCKET_SIZE || i == (TEN::Entities::TR4::NUM_BEETLES - 1))
{
continue;
m_cbInstancedStaticMeshBuffer.updateData(m_stInstancedStaticMeshBuffer, m_context.Get());
for (auto& bucket : mesh.Buckets)
{
if (bucket.NumVertices == 0 && bucket.BlendMode == BLEND_MODES::BLENDMODE_OPAQUE)
continue;
SetBlendMode(bucket.BlendMode);
BindTexture(TEXTURE_COLOR_MAP, &std::get<0>(m_moveablesTextures[bucket.Texture]), SAMPLER_ANISOTROPIC_CLAMP);
BindTexture(TEXTURE_NORMAL_MAP, &std::get<1>(m_moveablesTextures[bucket.Texture]), SAMPLER_NONE);
DrawIndexedInstancedTriangles(bucket.NumIndices, beetleCount, bucket.StartIndex, 0);
m_numStaticsDrawCalls++;
}
beetleCount = 0;
}
SetBlendMode(bucket.BlendMode);
BindTexture(TEXTURE_COLOR_MAP, &std::get<0>(m_moveablesTextures[bucket.Texture]), SAMPLER_ANISOTROPIC_CLAMP);
BindTexture(TEXTURE_NORMAL_MAP, &std::get<1>(m_moveablesTextures[bucket.Texture]), SAMPLER_NONE);
DrawIndexedInstancedTriangles(bucket.NumIndices, littleBeetlesCount, bucket.StartIndex, 0);
m_numStaticsDrawCalls++;
}
}
}
@ -1545,7 +1544,15 @@ namespace TEN::Renderer
DrawSprites(view);
DrawLines3D(view);
// Immediately draw additive and unsorted blended faces, and collect all sorted blend modes faces for later.
// Collect all sorted blend modes faces for later.
DrawRooms(view, RendererPass::CollectSortedFaces);
DrawItems(view, RendererPass::CollectSortedFaces);
DrawStatics(view, RendererPass::CollectSortedFaces);
// Draw all sorted blend mode faces collected in previous steps.
DrawSortedFaces(view);
// Draw immediate transparent faces (i.e. additive)
DrawRooms(view, RendererPass::Transparent);
DrawItems(view, RendererPass::Transparent);
DrawStatics(view, RendererPass::Transparent);
@ -1554,9 +1561,6 @@ namespace TEN::Renderer
DrawGunFlashes(view);
DrawBaddyGunflashes(view);
// Draw all sorted blend mode faces collected in previous steps.
DrawSortedFaces(view);
// Draw post-process effects (cinematic bars, fade, flash, HDR, tone mapping, etc.).
DrawPostprocess(target, depthTarget, view);
@ -1566,9 +1570,9 @@ namespace TEN::Renderer
// Draw HUD.
g_Hud.Draw(*LaraItem);
// Draw screen sprites sorted by priority.
CollectScreenSprites(view);
DrawScreenSprites(view);
// Draw display sprites sorted by priority.
CollectDisplaySprites(view);
DrawDisplaySprites(view);
// Draw binoculars or lasersight.
DrawOverlays(view);
@ -1877,7 +1881,8 @@ namespace TEN::Renderer
for (auto& bucket : refMesh->Buckets)
{
if (!((bucket.BlendMode == BLENDMODE_OPAQUE || bucket.BlendMode == BLENDMODE_ALPHATEST) ^ (rendererPass == RendererPass::Transparent)))
if (!((bucket.BlendMode == BLENDMODE_OPAQUE || bucket.BlendMode == BLENDMODE_ALPHATEST) ^
(rendererPass == RendererPass::Transparent)))
{
continue;
}
@ -1899,9 +1904,8 @@ namespace TEN::Renderer
{
SetBlendMode(bucket.BlendMode);
SetAlphaTest(
bucket.BlendMode == BLENDMODE_ALPHATEST ? ALPHA_TEST_GREATER_THAN : ALPHA_TEST_NONE,
ALPHA_TEST_THRESHOLD
);
(bucket.BlendMode == BLENDMODE_ALPHATEST) ? ALPHA_TEST_GREATER_THAN : ALPHA_TEST_NONE,
ALPHA_TEST_THRESHOLD);
}
else
{
@ -1925,7 +1929,7 @@ namespace TEN::Renderer
}
// Collect sorted blend modes faces ordered by room, if transparent pass
if (rendererPass == RendererPass::Transparent)
if (rendererPass == RendererPass::CollectSortedFaces)
{
Vector3 cameraPosition = Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z);
@ -1941,11 +1945,6 @@ namespace TEN::Renderer
for (auto& bucket : mesh->Buckets)
{
if (!((bucket.BlendMode == BLENDMODE_OPAQUE || bucket.BlendMode == BLENDMODE_ALPHATEST) ^ (rendererPass == RendererPass::Transparent)))
{
continue;
}
if (bucket.NumVertices == 0)
{
continue;
@ -1990,19 +1989,19 @@ namespace TEN::Renderer
UINT stride = sizeof(RendererVertex);
UINT offset = 0;
// Bind vertex and index buffer
// Bind vertex and index buffer.
m_context->IASetVertexBuffers(0, 1, m_roomsVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->IASetInputLayout(m_inputLayout.Get());
m_context->IASetIndexBuffer(m_roomsIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
// Bind pixel shaders
// Bind pixel shaders.
m_context->PSSetShader(m_psRooms.Get(), nullptr, 0);
BindConstantBufferVS(CB_ROOM, m_cbRoom.get());
BindConstantBufferPS(CB_ROOM, m_cbRoom.get());
// Bind caustics texture
// Bind caustics texture.
if (!m_sprites.empty())
{
int nmeshes = -Objects[ID_CAUSTICS_TEXTURES].nmeshes;
@ -2010,13 +2009,13 @@ namespace TEN::Renderer
int causticsFrame = std::min(nmeshes ? meshIndex + ((GlobalCounter) % nmeshes) : meshIndex, (int)m_sprites.size());
BindTexture(TEXTURE_CAUSTICS, m_sprites[causticsFrame].Texture, SAMPLER_ANISOTROPIC_CLAMP);
// Strange packing due to particular HLSL 16 bytes alignment requirements
// NOTE: Strange packing due to particular HLSL 16 bytes alignment requirements.
RendererSprite* causticsSprite = &m_sprites[causticsFrame];
m_stRoom.CausticsStartUV = causticsSprite->UV[0];
m_stRoom.CausticsScale = Vector2(causticsSprite->Width / (float)causticsSprite->Texture->Width, causticsSprite->Height / (float)causticsSprite->Texture->Height);
}
// Set shadow map data and bind shadow map texture
// Set shadow map data and bind shadow map texture.
if (m_shadowLight != nullptr)
{
memcpy(&m_stShadowMap.Light, m_shadowLight, sizeof(ShaderLight));
@ -2072,14 +2071,15 @@ namespace TEN::Renderer
continue;
}
if (!((bucket.BlendMode == BLENDMODE_OPAQUE || bucket.BlendMode == BLENDMODE_ALPHATEST) ^ (rendererPass == RendererPass::Transparent)))
if (!((bucket.BlendMode == BLENDMODE_OPAQUE || bucket.BlendMode == BLENDMODE_ALPHATEST) ^
(rendererPass == RendererPass::Transparent || rendererPass == RendererPass::CollectSortedFaces)))
{
continue;
}
if (DoesBlendModeRequireSorting(bucket.BlendMode))
if (rendererPass == RendererPass::CollectSortedFaces && DoesBlendModeRequireSorting(bucket.BlendMode))
{
// Collect transparent faces
// Collect transparent faces.
for (int j = 0; j < bucket.Polygons.size(); j++)
{
RendererPolygon* p = &bucket.Polygons[j];
@ -2179,20 +2179,20 @@ namespace TEN::Renderer
void Renderer11::DrawHorizonAndSky(RenderView& renderView, ID3D11DepthStencilView* depthTarget)
{
ScriptInterfaceLevel* level = g_GameFlow->GetLevel(CurrentLevel);
auto* levelPtr = g_GameFlow->GetLevel(CurrentLevel);
bool anyOutsideRooms = false;
for (int k = 0; k < renderView.RoomsToDraw.size(); k++)
{
ROOM_INFO* nativeRoom = &g_Level.Rooms[renderView.RoomsToDraw[k]->RoomNumber];
if (nativeRoom->flags & ENV_FLAG_OUTSIDE)
const auto& nativeRoom = g_Level.Rooms[renderView.RoomsToDraw[k]->RoomNumber];
if (nativeRoom.flags & ENV_FLAG_OUTSIDE)
{
anyOutsideRooms = true;
break;
}
}
if (!level->Horizon || !anyOutsideRooms)
if (!levelPtr->Horizon || !anyOutsideRooms)
return;
if (Lara.Control.Look.OpticRange != 0)
@ -2202,7 +2202,7 @@ namespace TEN::Renderer
UINT offset = 0;
// Draw sky.
Matrix rotation = Matrix::CreateRotationX(PI);
auto rotation = Matrix::CreateRotationX(PI);
m_context->VSSetShader(m_vsSky.Get(), nullptr, 0);
m_context->PSSetShader(m_psSky.Get(), nullptr, 0);
@ -2222,9 +2222,9 @@ namespace TEN::Renderer
{
auto weather = TEN::Effects::Environment::Weather;
Matrix translation = Matrix::CreateTranslation(Camera.pos.x + weather.SkyPosition(s) - i * SKY_SIZE,
auto translation = Matrix::CreateTranslation(Camera.pos.x + weather.SkyPosition(s) - i * SKY_SIZE,
Camera.pos.y - 1536.0f, Camera.pos.z);
Matrix world = rotation * translation;
auto world = rotation * translation;
m_stSky.World = (rotation * translation);
m_stSky.Color = weather.SkyColor(s);
@ -2240,7 +2240,7 @@ namespace TEN::Renderer
m_context->ClearDepthStencilView(depthTarget, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1, 0);
// Draw horizon
// Draw horizon.
if (m_moveableObjects[ID_HORIZON].has_value())
{
m_context->IASetVertexBuffers(0, 1, m_moveablesVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
@ -2248,7 +2248,7 @@ namespace TEN::Renderer
m_context->IASetInputLayout(m_inputLayout.Get());
m_context->IASetIndexBuffer(m_moveablesIndexBuffer.Buffer.Get(), DXGI_FORMAT_R32_UINT, 0);
RendererObject& moveableObj = *m_moveableObjects[ID_HORIZON];
auto& moveableObj = *m_moveableObjects[ID_HORIZON];
m_stSky.World = Matrix::CreateTranslation(Camera.pos.x, Camera.pos.y, Camera.pos.z);
m_stSky.Color = Vector4::One;
@ -2260,9 +2260,9 @@ namespace TEN::Renderer
for (int k = 0; k < moveableObj.ObjectMeshes.size(); k++)
{
RendererMesh* mesh = moveableObj.ObjectMeshes[k];
auto* meshPtr = moveableObj.ObjectMeshes[k];
for (auto& bucket : mesh->Buckets)
for (auto& bucket : meshPtr->Buckets)
{
if (bucket.NumVertices == 0)
continue;
@ -2272,11 +2272,11 @@ namespace TEN::Renderer
BindTexture(TEXTURE_NORMAL_MAP, &std::get<1>(m_moveablesTextures[bucket.Texture]),
SAMPLER_ANISOTROPIC_CLAMP);
// Always render horizon as alpha-blended surface
// Always render horizon as alpha-blended surface.
SetBlendMode(bucket.BlendMode == BLEND_MODES::BLENDMODE_ALPHATEST ? BLEND_MODES::BLENDMODE_ALPHABLEND : bucket.BlendMode);
SetAlphaTest(ALPHA_TEST_NONE, ALPHA_TEST_THRESHOLD);
// Draw vertices
// Draw vertices.
DrawIndexedTriangles(bucket.NumIndices, bucket.StartIndex, 0);
m_numMoveablesDrawCalls++;
@ -2284,7 +2284,7 @@ namespace TEN::Renderer
}
}
// Clear just the Z-buffer so we can start drawing on top of the horizon
// Clear just the Z-buffer to start drawing on top of horizon.
m_context->ClearDepthStencilView(depthTarget, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
}
@ -2298,19 +2298,18 @@ namespace TEN::Renderer
void Renderer11::DrawMoveableMesh(RendererItem* itemToDraw, RendererMesh* mesh, RendererRoom* room, int boneIndex, RendererPass rendererPass)
{
Vector3 cameraPosition = Vector3(Camera.pos.x, Camera.pos.y, Camera.pos.z);
auto cameraPos = Camera.pos.ToVector3();
for (auto& bucket : mesh->Buckets)
{
if (!((bucket.BlendMode == BLENDMODE_OPAQUE || bucket.BlendMode == BLENDMODE_ALPHATEST) ^ (rendererPass == RendererPass::Transparent)))
if (!((bucket.BlendMode == BLENDMODE_OPAQUE || bucket.BlendMode == BLENDMODE_ALPHATEST) ^
(rendererPass == RendererPass::Transparent || rendererPass == RendererPass::CollectSortedFaces)))
{
continue;
}
if (bucket.NumVertices == 0)
{
continue;
}
if (rendererPass == RendererPass::ShadowMap)
{
@ -2321,21 +2320,21 @@ namespace TEN::Renderer
m_numMoveablesDrawCalls++;
}
else if (DoesBlendModeRequireSorting(bucket.BlendMode))
else if (rendererPass == RendererPass::CollectSortedFaces && DoesBlendModeRequireSorting(bucket.BlendMode))
{
// Collect transparent faces
for (int j = 0; j < bucket.Polygons.size(); j++)
{
RendererPolygon* p = &bucket.Polygons[j];
auto* polygonPtr = &bucket.Polygons[j];
// As polygon distance, for moveables, we use the averaged distance
Vector3 centre = Vector3::Transform(
p->centre, itemToDraw->AnimationTransforms[boneIndex] * itemToDraw->World);
int distance = (centre - cameraPosition).Length();
// Use averaged distance as polygon distance for moveables.
auto centre = Vector3::Transform(
polygonPtr->centre, itemToDraw->AnimationTransforms[boneIndex] * itemToDraw->World);
int distance = (centre - cameraPos).Length();
RendererTransparentFace face;
face.type = RendererTransparentFaceType::TRANSPARENT_FACE_MOVEABLE;
face.info.polygon = p;
face.info.polygon = polygonPtr;
face.distance = distance;
face.info.animated = bucket.Animated;
face.info.texture = bucket.Texture;
@ -2363,8 +2362,7 @@ namespace TEN::Renderer
SetBlendMode(bucket.BlendMode);
SetAlphaTest(
bucket.BlendMode == BLENDMODE_ALPHATEST ? ALPHA_TEST_GREATER_THAN : ALPHA_TEST_NONE,
ALPHA_TEST_THRESHOLD
);
ALPHA_TEST_THRESHOLD);
}
else
{

View file

@ -4,6 +4,7 @@
#include "Game/camera.h"
#include "Game/control/control.h"
#include "Game/spotcam.h"
#include "Game/effects/DisplaySprite.h"
#include "Game/effects/weather.h"
#include "Game/Lara/lara.h"
#include "Game/Setup.h"
@ -11,9 +12,8 @@
#include "Objects/game_object_ids.h"
#include "Objects/Utils/object_helper.h"
#include "Specific/trutils.h"
#include "Game/effects/ScreenSprite.h"
using namespace TEN::Effects::ScreenSprite;
using namespace TEN::Effects::DisplaySprite;
using namespace TEN::Effects::Environment;
using namespace TEN::Math;
@ -381,11 +381,11 @@ namespace TEN::Renderer
DrawFullScreenQuad(texture, Vector3(fade), true);
}
void Renderer11::DrawScreenSprites(RenderView& renderView)
void Renderer11::DrawDisplaySprites(RenderView& renderView)
{
constexpr auto VERTEX_COUNT = 4;
if (renderView.Sprites2DToDraw.empty())
if (renderView.DisplaySpritesToDraw.empty())
return;
m_context->VSSetShader(m_vsFullScreenQuad.Get(), nullptr, 0);
@ -394,23 +394,19 @@ namespace TEN::Renderer
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->IASetInputLayout(m_inputLayout.Get());
RendererSprite* currentSpritePtr = nullptr;
Texture2D* currentTexturePtr = nullptr;
for (const auto& spriteToDraw : renderView.Sprites2DToDraw)
Texture2D* texture2DPtr = nullptr;
for (const auto& spriteToDraw : renderView.DisplaySpritesToDraw)
{
if (currentTexturePtr == nullptr)
if (texture2DPtr == nullptr)
{
m_primitiveBatch->Begin();
BindTexture(TEXTURE_COLOR_MAP, spriteToDraw.SpritePtr->Texture, SAMPLER_ANISOTROPIC_CLAMP);
SetBlendMode(spriteToDraw.BlendMode);
}
else if (currentTexturePtr != spriteToDraw.SpritePtr->Texture ||
lastBlendMode != spriteToDraw.BlendMode)
else if (texture2DPtr != spriteToDraw.SpritePtr->Texture || lastBlendMode != spriteToDraw.BlendMode)
{
m_primitiveBatch->End();
m_primitiveBatch->Begin();
BindTexture(TEXTURE_COLOR_MAP, spriteToDraw.SpritePtr->Texture, SAMPLER_ANISOTROPIC_CLAMP);
@ -418,16 +414,16 @@ namespace TEN::Renderer
}
// Calculate vertex base.
auto halfSize = spriteToDraw.Size / 2;
auto vertices = std::array<Vector2, VERTEX_COUNT>
{
halfSize,
Vector2(-halfSize.x, halfSize.y),
-halfSize,
Vector2(halfSize.x, -halfSize.y)
spriteToDraw.Size,
Vector2(-spriteToDraw.Size.x, spriteToDraw.Size.y),
-spriteToDraw.Size,
Vector2(spriteToDraw.Size.x, -spriteToDraw.Size.y)
};
// Transform vertices. // NOTE: Must rotate 180 degrees to account for +Y being down.
// Transform vertices.
// NOTE: Must rotate 180 degrees to account for +Y being down.
auto rotMatrix = Matrix::CreateRotationZ(TO_RAD(spriteToDraw.Orientation + ANGLE(180.0f)));
for (auto& vertex : vertices)
{
@ -451,7 +447,7 @@ namespace TEN::Renderer
m_primitiveBatch->DrawQuad(rVertices[0], rVertices[1], rVertices[2], rVertices[3]);
currentTexturePtr = spriteToDraw.SpritePtr->Texture;
texture2DPtr = spriteToDraw.SpritePtr->Texture;
}
m_primitiveBatch->End();
@ -467,11 +463,11 @@ namespace TEN::Renderer
if (fit)
{
ID3D11Texture2D* texture2D;
texture->GetResource(reinterpret_cast<ID3D11Resource**>(&texture2D));
ID3D11Texture2D* texture2DPtr;
texture->GetResource(reinterpret_cast<ID3D11Resource**>(&texture2DPtr));
auto desc = D3D11_TEXTURE2D_DESC();
texture2D->GetDesc(&desc);
texture2DPtr->GetDesc(&desc);
float screenAspect = float(m_screenWidth) / float(m_screenHeight);
float imageAspect = float(desc.Width) / float(desc.Height);
@ -555,7 +551,7 @@ namespace TEN::Renderer
}
}
Vector2 scale = Vector2(sprite->Width / (float)sprite->Texture->Width, sprite->Height / (float)sprite->Texture->Height);
auto scale = Vector2(sprite->Width / (float)sprite->Texture->Width, sprite->Height / (float)sprite->Texture->Height);
uvStart.x = uvStart.x * scale.x + sprite->UV[0].x;
uvStart.y = uvStart.y * scale.y + sprite->UV[0].y;
uvEnd.x = uvEnd.x * scale.x + sprite->UV[0].x;
@ -595,7 +591,7 @@ namespace TEN::Renderer
m_context->PSSetShader(m_psFullScreenQuad.Get(), nullptr, 0);
m_context->PSSetShaderResources(0, 1, &texture);
ID3D11SamplerState* sampler = m_states->AnisotropicClamp();
auto* sampler = m_states->AnisotropicClamp();
m_context->PSSetSamplers(0, 1, &sampler);
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
@ -606,10 +602,10 @@ namespace TEN::Renderer
m_primitiveBatch->End();
}
void Renderer11::AddScreenSprite(RendererSprite* spritePtr, const Vector2& pos2D, short orient, const Vector2& size,
const Vector4& color, int priority, BLEND_MODES blendMode, RenderView& renderView)
void Renderer11::AddDisplaySprite(RendererSprite* spritePtr, const Vector2& pos2D, short orient, const Vector2& size, const Vector4& color,
int priority, BLEND_MODES blendMode, RenderView& renderView)
{
auto spriteToDraw = RendererSprite2DToDraw{};
auto spriteToDraw = RendererDisplaySpriteToDraw{};
spriteToDraw.SpritePtr = spritePtr;
spriteToDraw.Position = pos2D;
@ -619,33 +615,123 @@ namespace TEN::Renderer
spriteToDraw.Priority = priority;
spriteToDraw.BlendMode = blendMode;
renderView.Sprites2DToDraw.push_back(spriteToDraw);
renderView.DisplaySpritesToDraw.push_back(spriteToDraw);
}
void Renderer11::CollectScreenSprites(RenderView& renderView)
void Renderer11::CollectDisplaySprites(RenderView& renderView)
{
for (const auto& screenSprite : ScreenSprites)
// Get screen resolution, dimensions, and aspect ratio.
auto screenRes = GetScreenResolution().ToVector2();
float screenAspect = screenRes.x / screenRes.y;
float screenWidthMax = (SCREEN_SPACE_RES.x / screenRes.x) * screenRes.x;
float screenHeightMax = (SCREEN_SPACE_RES.y / screenRes.y) * screenRes.y;
for (const auto& displaySprite : DisplaySprites)
{
AddScreenSprite(
&m_sprites[Objects[screenSprite.ObjectID].meshIndex + screenSprite.SpriteIndex],
screenSprite.Position,
screenSprite.Orientation,
screenSprite.Size,
screenSprite.Color,
screenSprite.Priority,
screenSprite.BlendMode,
auto& texture = m_sprites[Objects[displaySprite.ObjectID].meshIndex + displaySprite.SpriteID];
// Calculate sprite aspect ratio.
float spriteAspect = (float)texture.Width / (float)texture.Height;
// Calculate size.
auto halfSize = Vector2::Zero;
switch (displaySprite.ScaleMode)
{
default:
case DisplaySpriteScaleMode::Fit:
if (screenAspect >= spriteAspect)
{
halfSize = (Vector2(screenHeightMax) * displaySprite.Scale) / 2;
halfSize.x *= spriteAspect;
}
else
{
halfSize = (Vector2(screenWidthMax) * displaySprite.Scale) / 2;
halfSize.y /= spriteAspect;
}
break;
case DisplaySpriteScaleMode::Fill:
if (screenAspect >= spriteAspect)
{
halfSize = (Vector2(screenWidthMax) * displaySprite.Scale) / 2;
halfSize.y /= spriteAspect;
}
else
{
halfSize = (Vector2(screenHeightMax) * displaySprite.Scale) / 2;
halfSize.x *= spriteAspect;
}
break;
case DisplaySpriteScaleMode::Stretch:
halfSize = Vector2(screenWidthMax, screenHeightMax) * displaySprite.Scale / 2;
break;
}
// Calculate position offset.
auto offset = Vector2::Zero;
switch (displaySprite.AlignMode)
{
default:
case DisplaySpriteAlignMode::Center:
break;
case DisplaySpriteAlignMode::CenterTop:
offset = Vector2(0.0f, -halfSize.y);
break;
case DisplaySpriteAlignMode::CenterBottom:
offset = Vector2(0.0f, halfSize.y);
break;
case DisplaySpriteAlignMode::CenterLeft:
offset = Vector2(-halfSize.x, 0.0f);
break;
case DisplaySpriteAlignMode::CenterRight:
offset = Vector2(halfSize.x, 0.0f);
break;
case DisplaySpriteAlignMode::TopLeft:
offset = Vector2(-halfSize.x, -halfSize.y);
break;
case DisplaySpriteAlignMode::TopRight:
offset = Vector2(halfSize.x, -halfSize.y);
break;
case DisplaySpriteAlignMode::BottomLeft:
offset = Vector2(-halfSize.x, halfSize.y);
break;
case DisplaySpriteAlignMode::BottomRight:
offset = Vector2(halfSize.x, halfSize.y);
break;
}
//offset = TEN::Utils::GetAspectCorrect2DPosition(offset); // TODO: Check if aspect correction is necessary.
AddDisplaySprite(
&texture,
displaySprite.Position + offset,
displaySprite.Orientation,
halfSize,
displaySprite.Color,
displaySprite.Priority,
displaySprite.BlendMode,
renderView);
}
std::sort(
renderView.Sprites2DToDraw.begin(), renderView.Sprites2DToDraw.end(),
[](const RendererSprite2DToDraw& spriteToDraw0, const RendererSprite2DToDraw& spriteToDraw1)
renderView.DisplaySpritesToDraw.begin(), renderView.DisplaySpritesToDraw.end(),
[](const RendererDisplaySpriteToDraw& spriteToDraw0, const RendererDisplaySpriteToDraw& spriteToDraw1)
{
// Same priority; sort by blend mode.
if (spriteToDraw0.Priority == spriteToDraw1.Priority)
return (spriteToDraw0.BlendMode < spriteToDraw1.BlendMode);
// Sort by priority.
return (spriteToDraw0.Priority < spriteToDraw1.Priority);
}
);
});
}
}

View file

@ -28,8 +28,8 @@
#include "Math/Math.h"
#include "Objects/TR5/Trap/LaserBarrier.h"
#include "Objects/Utils/object_helper.h"
#include "Renderer/RendererSprites.h"
#include "Renderer/RendererSprite2D.h"
#include "Renderer/RendererSprites.h"
#include "Renderer/Quad/RenderQuad.h"
#include "Specific/level.h"
@ -1420,15 +1420,18 @@ namespace TEN::Renderer
}
auto* meshPtr = effect->Mesh;
BLEND_MODES lastBlendMode = BLEND_MODES::BLENDMODE_UNSET;
auto lastBlendMode = BLEND_MODES::BLENDMODE_UNSET;
for (auto& bucket : meshPtr->Buckets)
{
if (bucket.NumVertices == 0)
continue;
if (!((bucket.BlendMode == BLENDMODE_OPAQUE || bucket.BlendMode == BLENDMODE_ALPHATEST) ^ (rendererPass == RendererPass::Transparent)))
if (!((bucket.BlendMode == BLENDMODE_OPAQUE || bucket.BlendMode == BLENDMODE_ALPHATEST) ^
(rendererPass == RendererPass::Transparent)))
{
continue;
}
BindTexture(TEXTURE_COLOR_MAP, &std::get<0>(m_moveablesTextures[bucket.Texture]), SAMPLER_ANISOTROPIC_CLAMP);
BindTexture(TEXTURE_NORMAL_MAP, &std::get<1>(m_moveablesTextures[bucket.Texture]), SAMPLER_ANISOTROPIC_CLAMP);
@ -1479,18 +1482,21 @@ namespace TEN::Renderer
extern std::vector<DebrisFragment> DebrisFragments;
std::vector<RendererVertex> vertices;
BLEND_MODES lastBlendMode = BLEND_MODES::BLENDMODE_UNSET;
auto lastBlendMode = BLEND_MODES::BLENDMODE_UNSET;
for (auto deb = DebrisFragments.begin(); deb != DebrisFragments.end(); deb++)
{
if (deb->active)
{
if (!((deb->mesh.blendMode == BLENDMODE_OPAQUE || deb->mesh.blendMode == BLENDMODE_ALPHATEST) ^ (rendererPass == RendererPass::Transparent)))
if (!((deb->mesh.blendMode == BLENDMODE_OPAQUE || deb->mesh.blendMode == BLENDMODE_ALPHATEST) ^
(rendererPass == RendererPass::Transparent)))
{
continue;
}
Matrix translation = Matrix::CreateTranslation(deb->worldPosition.x, deb->worldPosition.y, deb->worldPosition.z);
Matrix rotation = Matrix::CreateFromQuaternion(deb->rotation);
Matrix world = rotation * translation;
auto translation = Matrix::CreateTranslation(deb->worldPosition.x, deb->worldPosition.y, deb->worldPosition.z);
auto rotation = Matrix::CreateFromQuaternion(deb->rotation);
auto world = rotation * translation;
m_primitiveBatch->Begin();

View file

@ -461,30 +461,33 @@ namespace TEN::Renderer
void Renderer11::RenderTitleMenu(Menu menu)
{
int y = MenuVerticalBottomCenter;
auto titleOption = g_Gui.GetSelectedOption();
// HACK: Check if it works properly -- Lwmte, 07.06.22
if (menu == Menu::LoadGame && !g_GameFlow->EnableLoadSave)
menu = Menu::Title;
int titleOption = g_Gui.GetSelectedOption();
int selectedOption = 0;
switch (menu)
{
case Menu::Title:
// New game
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_NEW_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == 0));
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_NEW_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == selectedOption));
GetNextLinePosition(&y);
selectedOption++;
// Load game
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_LOAD_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == 1));
GetNextLinePosition(&y);
if (g_GameFlow->IsLoadSaveEnabled())
{
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_LOAD_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == selectedOption));
GetNextLinePosition(&y);
selectedOption++;
}
// Options
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_OPTIONS), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == 2));
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_OPTIONS), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == selectedOption));
GetNextLinePosition(&y);
selectedOption++;
// Exit game
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_EXIT_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == 3));
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_EXIT_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == selectedOption));
break;
case Menu::LoadGame:
@ -501,10 +504,10 @@ namespace TEN::Renderer
GetNextBlockPosition(&y);
// Level listing (starts with 1 because 0 is always title)
for (int i = 1; i < g_GameFlow->GetNumLevels(); i++)
for (int i = 1; i < g_GameFlow->GetNumLevels(); i++, selectedOption++)
{
AddString(MenuCenterEntry, y, g_GameFlow->GetString(g_GameFlow->GetLevel(i)->NameStringKey.c_str()),
PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == i - 1));
PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == selectedOption));
GetNextNarrowLinePosition(&y);
}
break;
@ -570,7 +573,7 @@ namespace TEN::Renderer
void Renderer11::RenderLoadSaveMenu()
{
if (!g_GameFlow->EnableLoadSave)
if (!g_GameFlow->IsLoadSaveEnabled())
{
g_Gui.SetInventoryMode(InventoryMode::InGame);
return;
@ -580,7 +583,6 @@ namespace TEN::Renderer
int y = MenuVerticalLineSpacing;
short selection = g_Gui.GetLoadSaveSelection();
char stringBuffer[255];
SaveGame::LoadSavegameInfos();
// Title
AddString(MenuCenterEntry, MenuVerticalNarrowLineSpacing, Str_LoadSave(g_Gui.GetInventoryMode() == InventoryMode::Save),
@ -693,7 +695,8 @@ namespace TEN::Renderer
void Renderer11::DrawDisplayPickup(const DisplayPickup& pickup)
{
static const auto COUNT_STRING_PREFIX = std::string(" ");
constexpr auto COUNT_STRING_INF = "Inf";
constexpr auto COUNT_STRING_OFFSET = Vector2(SCREEN_SPACE_RES.x / 40, 0.0f);
// Clear only Z-buffer to draw on top of the scene.
ID3D11DepthStencilView* dsv;
@ -704,11 +707,12 @@ namespace TEN::Renderer
DrawObjectIn2DSpace(pickup.ObjectID, pickup.Position, pickup.Orientation, pickup.Scale);
// Draw count string.
if (pickup.Count > 1)
if (pickup.Count != 1)
{
AddString(
COUNT_STRING_PREFIX + std::to_string(pickup.Count),
pickup.Position, Color(PRINTSTRING_COLOR_WHITE), pickup.StringScale, SF());
auto countString = (pickup.Count != -1) ? std::to_string(pickup.Count) : COUNT_STRING_INF;
auto countStringPos = pickup.Position + COUNT_STRING_OFFSET;
AddString(countString, countStringPos, Color(PRINTSTRING_COLOR_WHITE), pickup.StringScale, SF());
}
}

View file

@ -42,6 +42,8 @@ enum LIGHT_TYPES
enum BLEND_MODES
{
BLENDMODE_UNSET = -1,
BLENDMODE_OPAQUE = 0,
BLENDMODE_ALPHATEST = 1,
BLENDMODE_ADDITIVE = 2,
@ -51,8 +53,7 @@ enum BLEND_MODES
BLENDMODE_EXCLUDE = 8,
BLENDMODE_SCREEN = 9,
BLENDMODE_LIGHTEN = 10,
BLENDMODE_ALPHABLEND = 11,
BLENDMODE_UNSET = -1
BLENDMODE_ALPHABLEND = 11
};
enum CULL_MODES
@ -193,11 +194,20 @@ enum ALPHA_TEST_MODES
ALPHA_TEST_LESS_THAN = 2
};
enum PrintStringFlags
{
PRINTSTRING_CENTER = (1 << 0),
PRINTSTRING_BLINK = (1 << 1),
PRINTSTRING_RIGHT = (1 << 2),
PRINTSTRING_OUTLINE = (1 << 3)
};
enum RendererPass
{
ShadowMap,
Opaque,
Transparent
Transparent,
CollectSortedFaces
};
constexpr auto TEXTURE_HEIGHT = 256;
@ -222,9 +232,6 @@ constexpr auto TEXTURE_PAGE = (TEXTURE_HEIGHT * TEXTURE_WIDTH);
#define NUM_SPRITES_PER_BUCKET 4096
#define NUM_LINES_PER_BUCKET 4096
#define NUM_CAUSTICS_TEXTURES 16
#define PRINTSTRING_CENTER 1
#define PRINTSTRING_BLINK 2
#define PRINTSTRING_OUTLINE 8
#define PRINTSTRING_COLOR_ORANGE D3DCOLOR_ARGB(255, 216, 117, 49)
#define PRINTSTRING_COLOR_WHITE D3DCOLOR_ARGB(255, 255, 255, 255)
#define PRINTSTRING_COLOR_BLACK D3DCOLOR_ARGB(255, 0, 0, 0)

View file

@ -296,7 +296,7 @@ namespace TEN::Renderer
m_dotProducts++;
}
if (door->DotProduct <= 0)
if (door->DotProduct < 0)
{
door->InvisibleFromCamera = true;
continue;

View file

@ -55,8 +55,19 @@ namespace TEN::Renderer
// Measure string.
auto size = Vector2(m_gameFont->MeasureString(rString.String.c_str())) * rString.Scale;
if (flags & PRINTSTRING_CENTER)
{
rString.X = (pos.x * factor.x) - (size.x / 2.0f);
}
else if (flags & PRINTSTRING_RIGHT)
{
rString.X = (pos.x * factor.x) - size.x;
}
else
{
rString.X = pos.x * factor.x;
}
rString.X = (flags & PRINTSTRING_CENTER) ? ((pos.x * factor.x) - (size.x / 2.0f)) : (pos.x * factor.x);
rString.Y = (pos.y * uiScale) + yOffset;
if (flags & PRINTSTRING_BLINK)

View file

@ -5,7 +5,7 @@ namespace TEN::Renderer
{
struct RendererSprite;
struct RendererSprite2DToDraw
struct RendererDisplaySpriteToDraw
{
RendererSprite* SpritePtr = nullptr;
@ -14,7 +14,7 @@ namespace TEN::Renderer
Vector2 Size = Vector2::Zero;
Vector4 Color = Vector4::Zero;
int Priority = 0;
BLEND_MODES BlendMode = BLENDMODE_ALPHABLEND;
int Priority = 0;
BLEND_MODES BlendMode = BLENDMODE_ALPHABLEND;
};
}

View file

@ -1,23 +1,24 @@
#pragma once
enum class TITLE_TYPE
{
FLYBY,
BACKGROUND
};
class ScriptInterfaceLevel;
enum class TitleType
{
Flyby,
Background
};
class ScriptInterfaceFlowHandler
{
public:
std::string IntroImagePath{};
int SelectedLevelForNewGame{ 0 };
int SelectedSaveGame{ 0 };
bool EnableLoadSave{ true };
int TotalNumberOfSecrets{ 0 };
std::string TitleScreenImagePath{};
TITLE_TYPE TitleType{ TITLE_TYPE::FLYBY };
TitleType TitleType = TitleType::Flyby;
std::string IntroImagePath = {};
std::string TitleScreenImagePath = {};
int SelectedLevelForNewGame = 0;
int SelectedSaveGame = 0;
int TotalNumberOfSecrets = 0;
virtual ~ScriptInterfaceFlowHandler() = default;
@ -32,6 +33,7 @@ public:
virtual bool IsMassPickupEnabled() const = 0;
virtual bool IsPointFilterEnabled() const = 0;
virtual bool IsLaraInTitleEnabled() const = 0;
virtual bool IsLoadSaveEnabled() const = 0;
virtual bool HasCrawlExtended() const = 0;
virtual bool HasCrouchRoll() const = 0;
virtual bool HasCrawlspaceDive() const = 0;

View file

@ -168,3 +168,4 @@
#define STRING_MECHANICAL_SCARAB_1 "mechanical_scarab_1"
#define STRING_MECHANICAL_SCARAB_2 "mechanical_scarab_2"
#define STRING_VEHICLE_ACTIONS "vehicle_actions"
#define STRING_UNLIMITED "unlimited"

View file

@ -26,6 +26,12 @@ static constexpr char ScriptReserved_Vec3[] = "Vec3";
static constexpr char ScriptReserved_Rotation[] = "Rotation";
static constexpr char ScriptReserved_LevelFunc[] = "LevelFunc";
// DisplaySprite object
static constexpr char ScriptReserved_DisplaySprite[] = "DisplaySprite";
static constexpr char ScriptReserved_DisplaySpriteDraw[] = "Draw";
static constexpr char ScriptReserved_DisplaySpriteTableAlignMode[] = "AlignMode";
static constexpr char ScriptReserved_DisplaySpriteTableScaleMode[] = "ScaleMode";
// Built-in LevelFuncs
static constexpr char ScriptReserved_OnStart[] = "OnStart";
static constexpr char ScriptReserved_OnLoad[] = "OnLoad";
@ -51,6 +57,11 @@ static constexpr char ScriptReserved_PostLoad[] = "POSTLOAD";
static constexpr char ScriptReserved_PreControlPhase[] = "PRECONTROLPHASE";
static constexpr char ScriptReserved_PostControlPhase[] = "POSTCONTROLPHASE";
// Event types
static constexpr char ScriptReserved_OnEnter[] = "ENTER";
static constexpr char ScriptReserved_OnInside[] = "INSIDE";
static constexpr char ScriptReserved_OnLeave[] = "LEAVE";
// Member functions
static constexpr char ScriptReserved_New[] = "New";
static constexpr char ScriptReserved_Init[] = "Init";
@ -168,6 +179,10 @@ 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_SaveGame[] = "SaveGame";
static constexpr char ScriptReserved_LoadGame[] = "LoadGame";
static constexpr char ScriptReserved_DeleteSaveGame[] = "DeleteSaveGame";
static constexpr char ScriptReserved_DoesSaveGameExist[] = "DoesSaveGameExist";
static constexpr char ScriptReserved_GetSecretCount[] = "GetSecretCount";
static constexpr char ScriptReserved_SetSecretCount[] = "SetSecretCount";
static constexpr char ScriptReserved_SetTotalSecretCount[] = "SetTotalSecretCount";
@ -176,6 +191,7 @@ static constexpr char ScriptReserved_EnableFlyCheat[] = "EnableFlyCheat";
static constexpr char ScriptReserved_EnableMassPickup[] = "EnableMassPickup";
static constexpr char ScriptReserved_EnableLaraInTitle[] = "EnableLaraInTitle";
static constexpr char ScriptReserved_EnableLevelSelect[] = "EnableLevelSelect";
static constexpr char ScriptReserved_EnableLoadSave[] = "EnableLoadSave";
static constexpr char ScriptReserved_EnablePointFilter[] = "EnablePointFilter";
// Flow Functions
@ -230,8 +246,9 @@ static constexpr char ScriptReserved_HasLineOfSight[] = "HasLineOfSight";
static constexpr char ScriptReserved_AddCallback[] = "AddCallback";
static constexpr char ScriptReserved_RemoveCallback[] = "RemoveCallback";
static constexpr char ScriptReserved_HandleEvent[] = "HandleEvent";
static constexpr char ScriptReserved_DisplayScreenSprite[] = "DisplayScreenSprite";
static constexpr char ScriptReserved_DrawDisplaySprite[] = "DrawDisplaySprite";
static constexpr char ScriptReserved_EmitParticle[] = "EmitParticle";
static constexpr char ScriptReserved_EmitLightningArc[] = "EmitLightningArc";
static constexpr char ScriptReserved_EmitShockwave[] = "EmitShockwave";
@ -281,6 +298,7 @@ static constexpr char ScriptReserved_RoomReverb[] = "RoomReverb";
static constexpr char ScriptReserved_DisplayStringOption[] = "DisplayStringOption";
static constexpr char ScriptReserved_CallbackPoint[] = "CallbackPoint";
static constexpr char ScriptReserved_EndReason[] = "EndReason";
static constexpr char ScriptReserved_EventType[] = "EventType";
static constexpr char ScriptReserved_LevelVars[] = "LevelVars";
static constexpr char ScriptReserved_GameVars[] = "GameVars";

View file

@ -2,6 +2,7 @@
#include "Scripting/Include/ScriptInterfaceState.h"
#include "Scripting/Internal/ReservedScriptNames.h"
#include "Scripting/Internal/TEN/DisplaySprite/ScriptDisplaySprite.h"
#include "Scripting/Internal/TEN/Effects/EffectsFunctions.h"
#include "Scripting/Internal/TEN/Flow/FlowHandler.h"
#include "Scripting/Internal/TEN/Inventory/InventoryHandler.h"
@ -10,8 +11,10 @@
#include "Scripting/Internal/TEN/Objects/ObjectsHandler.h"
#include "Scripting/Internal/TEN/Strings/StringsHandler.h"
static sol::state s_solState;
static sol::table s_rootTable;
using namespace TEN::Scripting::DisplaySprite;
static sol::state SolState;
static sol::table RootTable;
int lua_exception_handler(lua_State* luaStatePtr, sol::optional<const std::exception&> exception, sol::string_view description)
{
@ -20,36 +23,40 @@ int lua_exception_handler(lua_State* luaStatePtr, sol::optional<const std::excep
ScriptInterfaceGame* ScriptInterfaceState::CreateGame()
{
return new LogicHandler(&s_solState, s_rootTable);
return new LogicHandler(&SolState, RootTable);
}
ScriptInterfaceFlowHandler* ScriptInterfaceState::CreateFlow()
{
return new FlowHandler(&s_solState, s_rootTable);
return new FlowHandler(&SolState, RootTable);
}
ScriptInterfaceObjectsHandler* ScriptInterfaceState::CreateObjectsHandler()
{
return new ObjectsHandler(&s_solState, s_rootTable);
return new ObjectsHandler(&SolState, RootTable);
}
ScriptInterfaceStringsHandler* ScriptInterfaceState::CreateStringsHandler()
{
return new StringsHandler(&s_solState, s_rootTable);
return new StringsHandler(&SolState, RootTable);
}
void ScriptInterfaceState::Init(const std::string& assetsDir)
{
s_solState.open_libraries(sol::lib::base, sol::lib::math, sol::lib::package, sol::lib::coroutine, sol::lib::table, sol::lib::string, sol::lib::debug);
SolState.open_libraries(
sol::lib::base, sol::lib::math, sol::lib::package, sol::lib::coroutine,
sol::lib::table, sol::lib::string, sol::lib::debug);
s_solState.script("package.path=\"" + assetsDir + "Scripts/?.lua\"");
s_solState.set_exception_handler(lua_exception_handler);
SolState.script("package.path=\"" + assetsDir + "Scripts/?.lua\"");
SolState.set_exception_handler(lua_exception_handler);
s_rootTable = sol::table{ s_solState.lua_state(), sol::create };
s_solState.set(ScriptReserved_TEN, s_rootTable);
RootTable = sol::table(SolState.lua_state(), sol::create);
SolState.set(ScriptReserved_TEN, RootTable);
// Misc. handlers not assigned above.
InventoryHandler::Register(&s_solState, s_rootTable);
Misc::Register(&s_solState, s_rootTable);
Effects::Register(&s_solState, s_rootTable);
InventoryHandler::Register(&SolState, RootTable);
Misc::Register(&SolState, RootTable);
Effects::Register(&SolState, RootTable);
ScriptDisplaySprite::RegisterTables(&SolState, RootTable);
}

View file

@ -28,9 +28,9 @@
// This alias is an effort to avoid the above problems.
template <typename ... Ts> using TypeOrNil = std::variant<Ts..., sol::nil_t, sol::object>;
// To be used with TypeOrNil to fill arguments with default values if said arguments weren't given by the script.
#define USE_IF_HAVE(Type, ifThere, ifNotThere) \
(std::holds_alternative<Type>(ifThere) ? std::get<Type>(ifThere) : ifNotThere)
// Used with TypeOrNil to fill arguments with default values if arguments in script not provided.
#define USE_IF_HAVE(Type, valueIfExists, valueIfMissing) \
(std::holds_alternative<Type>(valueIfExists) ? std::get<Type>(valueIfExists) : valueIfMissing)
sol::table MakeSpecialTableBase(sol::state* state, std::string const& name);

View file

@ -0,0 +1,50 @@
#pragma once
#include <string>
#include <unordered_map>
#include "Game/Effects/DisplaySprite.h"
using namespace TEN::Effects::DisplaySprite;
namespace TEN::Scripting::DisplaySprite
{
/***
Constants for display sprite align modes.
@enum DisplaySprite.AlignMode
@pragma nostrip
*/
/*** DisplaySprite.AlignMode constants.
The following constants are inside AlignMode.
CENTER
CENTER_TOP
CENTER_BOTTOM
CENTER_LEFT
CENTER_RIGHT
TOP_LEFT
TOP_RIGHT
BOTTOM_LEFT
BOTTOM_RIGHT
@section DisplaySprite.AlignMode
*/
/*** Table of display sprite align modes.
@table CONSTANT_STRING_HERE
*/
static const std::unordered_map<std::string, DisplaySpriteAlignMode> DISPLAY_SPRITE_ALIGN_MODES
{
{ "CENTER", DisplaySpriteAlignMode::Center },
{ "CENTER_TOP", DisplaySpriteAlignMode::CenterTop },
{ "CENTER_BOTTOM", DisplaySpriteAlignMode::CenterBottom },
{ "CENTER_LEFT", DisplaySpriteAlignMode::CenterLeft },
{ "CENTER_RIGHT", DisplaySpriteAlignMode::CenterRight },
{ "TOP_LEFT", DisplaySpriteAlignMode::TopLeft },
{ "TOP_RIGHT", DisplaySpriteAlignMode::TopRight },
{ "BOTTOM_LEFT", DisplaySpriteAlignMode::BottomLeft },
{ "BOTTOM_RIGHT", DisplaySpriteAlignMode::BottomRight }
};
}

View file

@ -0,0 +1,38 @@
#pragma once
#include <string>
#include <unordered_map>
#include "Game/effects/DisplaySprite.h"
using namespace TEN::Effects::DisplaySprite;
namespace TEN::Scripting::DisplaySprite
{
/***
Constants for display sprite scale modes.
@enum DisplaySprite.ScaleMode
@pragma nostrip
*/
/*** DisplaySprite.ScaleMode constants.
The following constants are inside DisplaySprite.ScaleMode.
FIT
FILL
STRETCH
@section DisplaySprite.ScaleMode
*/
/*** Table of display sprite scale modes.
@table CONSTANT_STRING_HERE
*/
static const std::unordered_map<std::string, DisplaySpriteScaleMode> DISPLAY_SPRITE_SCALE_MODES
{
{ "FIT", DisplaySpriteScaleMode::Fit },
{ "FILL", DisplaySpriteScaleMode::Fill },
{ "STRETCH", DisplaySpriteScaleMode::Stretch }
};
}

View file

@ -0,0 +1,138 @@
#include "framework.h"
#include "Scripting/Internal/TEN/DisplaySprite/ScriptDisplaySprite.h"
#include "Game/effects/DisplaySprite.h"
#include "Game/Setup.h"
#include "Objects/game_object_ids.h"
#include "Scripting/Internal/LuaHandler.h"
#include "Scripting/Internal/ReservedScriptNames.h"
#include "Scripting/Internal/TEN/Color/Color.h"
#include "Scripting/Internal/TEN/DisplaySprite/AlignModes.h"
#include "Scripting/Internal/TEN/DisplaySprite/ScaleModes.h"
#include "Scripting/Internal/TEN/Vec2/Vec2.h"
using namespace TEN::Effects::DisplaySprite;
namespace TEN::Scripting::DisplaySprite
{
void ScriptDisplaySprite::Register(sol::table& parent)
{
using ctors = sol::constructors<ScriptDisplaySprite(GAME_OBJECT_ID, int, const Vec2&, float, const Vec2&, sol::optional<const ScriptColor&>)>;
parent.new_usertype<ScriptDisplaySprite>(
ScriptReserved_DisplaySprite,
ctors(),
sol::call_constructor, ctors(),
ScriptReserved_DisplaySpriteDraw, &ScriptDisplaySprite::Draw,
/// (Objects.ObjID) ID of the sprite sequence object.
//@mem ObjectID
"ObjectID", &ScriptDisplaySprite::ObjectID,
/// (int) ID of the sprite in the sprite sequence object.
//@mem SpriteID
"SpriteID", &ScriptDisplaySprite::SpriteID,
/// (Vec2) Display space position of the display sprite in percent. Alignment determined by __DisplaySprite.AlignMode__
//@mem Position
"Position", &ScriptDisplaySprite::Position,
/// (float) Rotation of the display sprite in degrees.
//@mem Rotation
"Rotation", &ScriptDisplaySprite::Rotation,
/// (Vec2) Horizontal and vertical scale of the display sprite in percent. Relative to __DisplaySprite.ScaleMode__.
//@mem Scale
"Scale", &ScriptDisplaySprite::Scale,
/// (Color) Color of the display sprite.
//@mem Color
"Color", &ScriptDisplaySprite::Color);
}
void ScriptDisplaySprite::RegisterTables(sol::state* state, sol::table& parent)
{
auto table = sol::table(state->lua_state(), sol::create);
parent.set(ScriptReserved_DisplaySprite, table);
auto handler = LuaHandler(state);
handler.MakeReadOnlyTable(table, ScriptReserved_DisplaySpriteTableAlignMode, DISPLAY_SPRITE_ALIGN_MODES);
handler.MakeReadOnlyTable(table, ScriptReserved_DisplaySpriteTableScaleMode, DISPLAY_SPRITE_SCALE_MODES);
}
/***
@int objectID ID of the sprite sequence object.
@int spriteID ID of the sprite in the sprite sequence object.
@Vec2 pos Display space position of the display sprite in percent. Alignment determined by __DisplaySprite.AlignMode__
@float rot Rotation of the display sprite in degrees.
@Vec2 scale Horizontal and vertical scale of the display sprite in percent. Relative to __DisplaySprite.ScaleMode__.
@Color color[opt] Color of the display sprite. __Default: Color(255, 255, 255, 255)__
@treturn DisplaySprite A DisplaySprite object.
*/
ScriptDisplaySprite::ScriptDisplaySprite(GAME_OBJECT_ID objectID, int spriteID, const Vec2& pos, float rot, const Vec2& scale,
sol::optional<const ScriptColor&> color)
{
static const auto DEFAULT_COLOR = ScriptColor(255, 255, 255, 255);
ObjectID = objectID;
SpriteID = spriteID;
Position = pos;
Rotation = rot;
Scale = scale;
Color = color.value_or(DEFAULT_COLOR);
}
/*** Draw the display sprite in display space for the current frame.
@function DisplaySprite:Draw
@tparam Objects.ObjID[opt] priority Draw priority of the sprite. Can be thought of as a layer, with higher values having higher priority. __Default: 0__
@tparam DisplaySprite.AlignMode[opt] alignMode Align mode of the sprite. __Default: DisplaySprite.AlignMode.CENTER__
@tparam DisplaySprite.ScaleMode[opt] scaleMode Scale mode of the sprite. __Default: DisplaySprite.ScaleMode.FIT__
@tparam Effects.BlendID[opt] blendMode Blend mode of the sprite. __Default: Effects.BlendID.ALPHABLEND__
*/
void ScriptDisplaySprite::Draw(sol::optional<int> priority, sol::optional<DisplaySpriteAlignMode> alignMode,
sol::optional<DisplaySpriteScaleMode> scaleMode, sol::optional<BLEND_MODES> blendMode)
{
// NOTE: Conversion from more intuitive 100x100 screen space resolution to internal 800x600 is required.
// In a future refactor, everything will use 100x100 natively. -- Sezz 2023.08.31
constexpr auto POS_CONVERSION_COEFF = Vector2(SCREEN_SPACE_RES.x / 100, SCREEN_SPACE_RES.y / 100);
constexpr auto SCALE_CONVERSION_COEFF = 0.01f;
constexpr auto DEFAULT_PRIORITY = 0;
constexpr auto DEFAULT_ALIGN_MODE = DisplaySpriteAlignMode::Center;
constexpr auto DEFAULT_SCALE_MODE = DisplaySpriteScaleMode::Fit;
constexpr auto DEFAULT_BLEND_MODE = BLENDMODE_ALPHABLEND;
// Object is not a sprite object; return early.
if (ObjectID < GAME_OBJECT_ID::ID_HORIZON || ObjectID >= GAME_OBJECT_ID::ID_NUMBER_OBJECTS)
{
TENLog("Attempted to draw display sprite from non-sprite sequence object " + std::to_string(ObjectID), LogLevel::Warning);
return;
}
// Sprite missing or sequence not found; return early.
const auto& object = Objects[ObjectID];
if (!object.loaded || SpriteID >= abs(object.nmeshes))
{
TENLog(
"Attempted to draw missing sprite " + std::to_string(SpriteID) +
" from sprite sequence object " + std::to_string(ObjectID) +
" as display sprite.",
LogLevel::Warning);
return;
}
auto convertedPos = Vector2(Position.x, Position.y) * POS_CONVERSION_COEFF;
short convertedRot = ANGLE(Rotation);
auto convertedScale = Vector2(Scale.x, Scale.y) * SCALE_CONVERSION_COEFF;
auto convertedColor = Vector4(Color.GetR(), Color.GetG(), Color.GetB(), Color.GetA()) / UCHAR_MAX;
AddDisplaySprite(
ObjectID, SpriteID,
convertedPos, convertedRot, convertedScale, convertedColor,
priority.value_or(DEFAULT_PRIORITY),
alignMode.value_or(DEFAULT_ALIGN_MODE),
scaleMode.value_or(DEFAULT_SCALE_MODE),
blendMode.value_or(DEFAULT_BLEND_MODE));
}
}

View file

@ -0,0 +1,44 @@
#pragma once
#include "Game/effects/DisplaySprite.h"
#include "Objects/game_object_ids.h"
#include "Scripting/Internal/TEN/Color/Color.h"
#include "Scripting/Internal/TEN/Vec2/Vec2.h"
enum BLEND_MODE;
using namespace TEN::Effects::DisplaySprite;
// TODO:
// Check if align modes are applied correctly.
// Check if ScriptDisplaySprite object has been registered correctly.
// Check if its methods have been registered correctly.
// Register DISPLAY_SPRITE_SCALE_MODES and DISPLAY_SPRITE_ORIGIN_TYPES under the correct sub-thing.
// Test nested sprites in various modes and how they behave when the aspect ratio changes.
// Check with everyone if the mySprite.Draw(blah, blah, blah) syntax is sensible.
namespace TEN::Scripting::DisplaySprite
{
class ScriptDisplaySprite
{
public:
static void Register(sol::table& parent);
static void RegisterTables(sol::state* state, sol::table& parent);
// Members
GAME_OBJECT_ID ObjectID = GAME_OBJECT_ID::ID_DEFAULT_SPRITES;
int SpriteID = 0;
Vec2 Position = Vec2(0.0f, 0.0f);
float Rotation = 0.0f;
Vec2 Scale = Vec2(0.0f, 0.0f);
ScriptColor Color = ScriptColor(255, 255, 255, 255);
// Constructors
ScriptDisplaySprite(GAME_OBJECT_ID objectID, int spriteID, const Vec2& pos, float rot, const Vec2& scale,
sol::optional<const ScriptColor&> color);
// Utilities
void Draw(sol::optional<int> priority, sol::optional<DisplaySpriteAlignMode> alignMode,
sol::optional<DisplaySpriteScaleMode> scaleMode, sol::optional<BLEND_MODES> blendMode);
};
}

View file

@ -1,10 +1,8 @@
#pragma once
// Last generated on 31/7/2022.
#include <string>
#include <unordered_map>
#include "Renderer/Renderer11Enums.h"
#include <unordered_map>
#include <string>
/***
Constants for blend mode IDs.
@ -34,17 +32,16 @@ The following constants are inside BlendID.
@table CONSTANT_STRING_HERE
*/
static const std::unordered_map<std::string, BLEND_MODES> BLEND_IDS
{
{"OPAQUE", BLENDMODE_OPAQUE},
{"ALPHATEST", BLENDMODE_ALPHATEST},
{"ADDITIVE", BLENDMODE_ADDITIVE},
{"NOZTEST", BLENDMODE_NOZTEST},
{"SUBTRACTIVE", BLENDMODE_SUBTRACTIVE},
{"WIREFRAME", BLENDMODE_WIREFRAME},
{"EXCLUDE", BLENDMODE_EXCLUDE},
{"SCREEN", BLENDMODE_SCREEN},
{"LIGHTEN", BLENDMODE_LIGHTEN},
{"ALPHABLEND", BLENDMODE_ALPHABLEND}
{ "OPAQUE", BLENDMODE_OPAQUE },
{ "ALPHATEST", BLENDMODE_ALPHATEST },
{ "ADDITIVE", BLENDMODE_ADDITIVE },
{ "NOZTEST", BLENDMODE_NOZTEST },
{ "SUBTRACTIVE", BLENDMODE_SUBTRACTIVE },
{ "WIREFRAME", BLENDMODE_WIREFRAME },
{ "EXCLUDE", BLENDMODE_EXCLUDE },
{ "SCREEN", BLENDMODE_SCREEN },
{ "LIGHTEN", BLENDMODE_LIGHTEN },
{ "ALPHABLEND", BLENDMODE_ALPHABLEND }
};

View file

@ -32,11 +32,11 @@ The following constants are inside EffectID.
static const std::unordered_map<std::string, EffectType> EFFECT_IDS
{
{"NONE", EffectType::None},
{"FIRE", EffectType::Fire},
{"SPARKS", EffectType::Sparks},
{"SMOKE", EffectType::Smoke},
{"ELECTRICIGNITE", EffectType::ElectricIgnite},
{"REDIGNITE", EffectType::RedIgnite},
{"CUSTOM", EffectType::Custom}
{ "NONE", EffectType::None },
{ "FIRE", EffectType::Fire },
{ "SPARKS", EffectType::Sparks },
{ "SMOKE", EffectType::Smoke},
{ "ELECTRICIGNITE", EffectType::ElectricIgnite },
{ "REDIGNITE", EffectType::RedIgnite },
{ "CUSTOM", EffectType::Custom }
};

View file

@ -4,10 +4,10 @@
#include "Game/camera.h"
#include "Game/collision/collide_room.h"
#include "Game/control/los.h"
#include "Game/effects/DisplaySprite.h"
#include "Game/effects/effects.h"
#include "Game/effects/Electricity.h"
#include "Game/effects/explosion.h"
#include "Game/effects/ScreenSprite.h"
#include "Game/effects/spark.h"
#include "Game/effects/tomb4fx.h"
#include "Game/effects/weather.h"
@ -30,47 +30,14 @@ Functions to generate effects.
@pragma nostrip
*/
using namespace TEN::Effects::DisplaySprite;
using namespace TEN::Effects::Electricity;
using namespace TEN::Effects::Environment;
using namespace TEN::Effects::Explosion;
using namespace TEN::Effects::ScreenSprite;
using namespace TEN::Effects::Spark;
namespace Effects
{
/// Display a sprite on the screen.
//@function DisplayScreenSprite
//@tparam int objectID Object ID of the sprite.
//@tparam int spriteIndex Index of the sprite in the sprite object.
//@tparam Vec2 pos 2D position of the sprite. NOTE: Screen space resolution is 100x100.
//@tparam float rot Rotation of the sprite in degrees. Default is 0.
//@tparam Vec2 size Size of the sprite.
//@tparam Color color Color of the sprite. Default is Color(255, 255, 255, 255).
//@tparam int priority Render priority of the sprite. Higher values have higher priority. Default is 0.
//@tparam Effects.BlendID blendMode Blend mode of the sprite. Default is TEN.Effects.BlendID.ALPHABLEND.
static void DisplayScreenSprite(GAME_OBJECT_ID objectID, int spriteIndex, const Vec2& pos, TypeOrNil<float> rot, const Vec2& size,
TypeOrNil<ScriptColor> color, TypeOrNil<int> priority, TypeOrNil<BLEND_MODES> blendMode)
{
// NOTE: Conversion from more intuitive 100x100 screen space resolution to internal 800x600 is required.
// Later, everything will be natively 100x100. -- Sezz 2023.08.31
constexpr auto POS_CONVERSION_COEFF = Vector2(SCREEN_SPACE_RES.x / 100, SCREEN_SPACE_RES.y / 100);
auto scriptColor = USE_IF_HAVE(ScriptColor, color, ScriptColor(255, 255, 255, 255));
auto bMode = USE_IF_HAVE(BLEND_MODES, blendMode, BLENDMODE_ALPHABLEND);
bMode = BLEND_MODES(std::clamp((int)bMode, (int)BLEND_MODES::BLENDMODE_OPAQUE, (int)BLEND_MODES::BLENDMODE_ALPHABLEND));
AddScreenSprite(
objectID,
spriteIndex,
Vector2(pos.x, pos.y) * POS_CONVERSION_COEFF,
ANGLE(USE_IF_HAVE(float, rot, 0.0f)),
Vector2(size.x, size.y),
Vector4(scriptColor.GetR(), scriptColor.GetG(), scriptColor.GetB(), scriptColor.GetA()) / UCHAR_MAX,
USE_IF_HAVE(int, priority, 0),
bMode);
}
///Emit a lightning arc.
//@function EmitLightningArc
//@tparam Vec3 src
@ -354,12 +321,11 @@ namespace Effects
Weather.Flash(color.GetR(), color.GetG(), color.GetB(), (USE_IF_HAVE(float, speed, 1.0)) / (float)FPS);
}
void Register(sol::state* state, sol::table& parent)
void Register(sol::state* state, sol::table& parent)
{
sol::table tableEffects = { state->lua_state(), sol::create };
auto tableEffects = sol::table(state->lua_state(), sol::create);
parent.set(ScriptReserved_Effects, tableEffects);
tableEffects.set_function(ScriptReserved_DisplayScreenSprite, &DisplayScreenSprite);
tableEffects.set_function(ScriptReserved_EmitLightningArc, &EmitLightningArc);
tableEffects.set_function(ScriptReserved_EmitParticle, &EmitParticle);
tableEffects.set_function(ScriptReserved_EmitShockwave, &EmitShockwave);
@ -370,7 +336,7 @@ namespace Effects
tableEffects.set_function(ScriptReserved_FlashScreen, &FlashScreen);
tableEffects.set_function(ScriptReserved_MakeEarthquake, &Earthquake);
LuaHandler handler{ state };
auto handler = LuaHandler{ state };
handler.MakeReadOnlyTable(tableEffects, ScriptReserved_BlendID, BLEND_IDS);
handler.MakeReadOnlyTable(tableEffects, ScriptReserved_EffectID, EFFECT_IDS);
}

View file

@ -20,5 +20,5 @@ struct Animations
bool HasOverhangClimb; // Overhang functionality.
bool HasLedgeJumps; // Jump up or back from a ledge.
static void Register(sol::table &);
static void Register(sol::table&);
};

View file

@ -8,6 +8,7 @@
#include "Scripting/Include/Objects/ScriptInterfaceObjectsHandler.h"
#include "Scripting/Include/Strings/ScriptInterfaceStringsHandler.h"
#include "Scripting/Internal/ReservedScriptNames.h"
#include "Scripting/Internal/TEN/DisplaySprite/ScriptDisplaySprite.h"
#include "Scripting/Internal/TEN/Flow/InventoryItem/InventoryItem.h"
#include "Scripting/Internal/TEN/Logic/LevelFunc.h"
#include "Scripting/Internal/TEN/Vec2/Vec2.h"
@ -15,6 +16,8 @@
#include "Sound/sound.h"
#include "Specific/trutils.h"
using namespace TEN::Scripting::DisplaySprite;
/***
Functions that (mostly) don't directly impact in-game mechanics. Used for setup
in gameflow.lua, settings.lua and strings.lua; some can be used in level
@ -28,7 +31,8 @@ ScriptInterfaceObjectsHandler* g_GameScriptEntities;
ScriptInterfaceStringsHandler* g_GameStringsHandler;
ScriptInterfaceFlowHandler* g_GameFlow;
FlowHandler::FlowHandler(sol::state* lua, sol::table& parent) : m_handler(lua)
FlowHandler::FlowHandler(sol::state* lua, sol::table& parent) :
m_handler(lua)
{
/*** gameflow.lua.
These functions are called in gameflow.lua, a file loosely equivalent to winroomedit's SCRIPT.DAT.
@ -75,6 +79,12 @@ Must be true or false
*/
tableFlow.set_function(ScriptReserved_EnableLevelSelect, &FlowHandler::EnableLevelSelect, this);
/*** Enable or disable saving and loading of savegames.
@function EnableLoadSave
@tparam bool enabled true or false.
*/
tableFlow.set_function(ScriptReserved_EnableLoadSave, &FlowHandler::EnableLoadSave, this);
/*** gameflow.lua or level scripts.
@section FlowluaOrScripts
*/
@ -124,6 +134,35 @@ level count, jumps to title.
*/
tableFlow.set_function(ScriptReserved_EndLevel, &FlowHandler::EndLevel, this);
/***
Save the game to a savegame slot.
@function SaveGame
@tparam int slotID ID of the savegame slot to save to.
*/
tableFlow.set_function(ScriptReserved_SaveGame, &FlowHandler::SaveGame, this);
/***
Load the game from a savegame slot.
@function LoadGame
@tparam int slotID ID of the savegame slot to load from.
*/
tableFlow.set_function(ScriptReserved_LoadGame, &FlowHandler::LoadGame, this);
/***
Delete a savegame.
@function DeleteSaveGame
@tparam int slotID ID of the savegame slot to clear.
*/
tableFlow.set_function(ScriptReserved_DeleteSaveGame, &FlowHandler::DeleteSaveGame, this);
/***
Check if a savegame exists.
@function DoesSaveGameExist
@tparam int slotID ID of the savegame slot to check.
@treturn bool true if the savegame exists, false if not.
*/
tableFlow.set_function(ScriptReserved_DoesSaveGameExist, &FlowHandler::DoesSaveGameExist, this);
/***
Returns the player's current per-game secret count.
@function GetSecretCount
@ -195,6 +234,7 @@ Specify which translations in the strings table correspond to which languages.
tableFlow.set_function(ScriptReserved_SetLanguageNames, &FlowHandler::SetLanguageNames, this);
ScriptColor::Register(parent);
ScriptDisplaySprite::Register(parent);
Rotation::Register(parent);
Vec2::Register(parent);
Vec3::Register(parent);
@ -206,11 +246,11 @@ Specify which translations in the strings table correspond to which languages.
Settings::Register(tableFlow);
Fog::Register(tableFlow);
m_handler.MakeReadOnlyTable(tableFlow, ScriptReserved_WeatherType, kWeatherTypes);
m_handler.MakeReadOnlyTable(tableFlow, ScriptReserved_LaraType, kLaraTypes);
m_handler.MakeReadOnlyTable(tableFlow, ScriptReserved_WeatherType, WEATHER_TYPES);
m_handler.MakeReadOnlyTable(tableFlow, ScriptReserved_LaraType, PLAYER_TYPES);
m_handler.MakeReadOnlyTable(tableFlow, ScriptReserved_RotationAxis, ROTATION_AXES);
m_handler.MakeReadOnlyTable(tableFlow, ScriptReserved_ItemAction, ITEM_MENU_ACTIONS);
m_handler.MakeReadOnlyTable(tableFlow, ScriptReserved_ErrorMode, kErrorModes);
m_handler.MakeReadOnlyTable(tableFlow, ScriptReserved_ErrorMode, ERROR_MODES);
}
FlowHandler::~FlowHandler()
@ -280,7 +320,7 @@ void FlowHandler::SetTotalSecretCount(int secretsNumber)
void FlowHandler::LoadFlowScript()
{
m_handler.ExecuteScript(m_gameDir + "Scripts/Gameflow.lua");
m_handler.ExecuteScript(m_gameDir + "Scripts/SystemStrings.lua");
m_handler.ExecuteScript(m_gameDir + "Scripts/SystemStrings.lua", true);
m_handler.ExecuteScript(m_gameDir + "Scripts/Strings.lua", true);
m_handler.ExecuteScript(m_gameDir + "Scripts/Settings.lua", true);
@ -382,7 +422,30 @@ int FlowHandler::GetLevelNumber(const std::string& fileName)
void FlowHandler::EndLevel(std::optional<int> nextLevel)
{
int index = (nextLevel.has_value() && nextLevel.value() != 0) ? nextLevel.value() : CurrentLevel + 1;
LevelComplete = index;
NextLevel = index;
}
void FlowHandler::SaveGame(int slot)
{
SaveGame::Save(slot);
}
void FlowHandler::LoadGame(int slot)
{
if (!SaveGame::DoesSaveGameExist(slot))
return;
NextLevel = -(slot + 1);
}
void FlowHandler::DeleteSaveGame(int slot)
{
SaveGame::Delete(slot);
}
bool FlowHandler::DoesSaveGameExist(int slot)
{
return SaveGame::DoesSaveGameExist(slot, true);
}
int FlowHandler::GetSecretCount() const
@ -467,6 +530,16 @@ void FlowHandler::EnableLevelSelect(bool levelSelect)
LevelSelect = levelSelect;
}
bool FlowHandler::IsLoadSaveEnabled() const
{
return LoadSave;
}
void FlowHandler::EnableLoadSave(bool loadSave)
{
LoadSave = loadSave;
}
bool FlowHandler::DoFlow()
{
// We start with the title level, if no other index is specified
@ -565,21 +638,22 @@ bool FlowHandler::DoFlow()
// Load level
CurrentLevel = header.Level;
NextLevel = 0;
GameTimer = header.Timer;
loadFromSavegame = true;
break;
case GameStatus::LevelComplete:
if (LevelComplete >= Levels.size())
if (NextLevel >= Levels.size())
{
CurrentLevel = 0; // TODO: final credits
}
else
{
CurrentLevel = LevelComplete;
CurrentLevel = NextLevel;
}
LevelComplete = 0;
NextLevel = 0;
break;
}
}

View file

@ -26,17 +26,19 @@ private:
LuaHandler m_handler;
public:
int FogInDistance{ 0 };
int FogOutDistance{ 0 };
bool LevelSelect{ true };
bool FlyCheat{ true };
bool PointFilter{ false };
bool MassPickup{ true };
bool LaraInTitle{ false };
bool DebugMode{ false };
int FogInDistance = 0;
int FogOutDistance = 0;
// New animation flag table
Animations Anims{};
bool LevelSelect = true;
bool LoadSave = true;
bool FlyCheat = true;
bool PointFilter = false;
bool MassPickup = true;
bool LaraInTitle = false;
bool DebugMode = false;
// Table for movesets.
Animations Anims = {};
std::vector<Level*> Levels;
@ -58,6 +60,10 @@ public:
int GetLevelNumber(const std::string& flieName);
int GetNumLevels() const;
void EndLevel(std::optional<int> nextLevel);
void SaveGame(int slot);
void LoadGame(int slot);
void DeleteSaveGame(int slot);
bool DoesSaveGameExist(int slot);
int GetSecretCount() const;
void SetSecretCount(int secretsNum);
void AddSecret(int levelSecretIndex);
@ -74,6 +80,8 @@ public:
void EnableLaraInTitle(bool laraInTitle);
bool IsLevelSelectEnabled() const;
void EnableLevelSelect(bool laraInTitle);
bool IsLoadSaveEnabled() const;
void EnableLoadSave(bool loadSave);
bool HasCrawlExtended() const override { return Anims.HasCrawlExtended; }
bool HasCrouchRoll() const override { return Anims.HasCrouchRoll; }

View file

@ -8,7 +8,7 @@ Fog
@pragma nostrip
*/
void Fog::Register(sol::table & parent)
void Fog::Register(sol::table& parent)
{
using ctors = sol::constructors<Fog(ScriptColor const&, short, short)>;
parent.new_usertype<Fog>("Fog",

View file

@ -20,5 +20,5 @@ struct Fog
void SetColor(ScriptColor const& col);
ScriptColor GetColor() const;
static void Register(sol::table &);
static void Register(sol::table&);
};

View file

@ -14,7 +14,7 @@ These are things things which aren't present in the compiled level file itself.
@function Level
@treturn Level a Level object
*/
void Level::Register(sol::table & parent)
void Level::Register(sol::table& parent)
{
parent.new_usertype<Level>("Level",
sol::constructors<Level()>(),

View file

@ -6,7 +6,7 @@
#include "Scripting/Include/ScriptInterfaceLevel.h"
#include "Scripting/Internal/TEN/Flow/InventoryItem/InventoryItem.h"
static const std::unordered_map<std::string, WeatherType> kWeatherTypes
static const std::unordered_map<std::string, WeatherType> WEATHER_TYPES
{
{"None", WeatherType::None},
{"Rain", WeatherType::Rain},
@ -14,7 +14,7 @@ static const std::unordered_map<std::string, WeatherType> kWeatherTypes
};
static const std::unordered_map<std::string, LaraType> kLaraTypes
static const std::unordered_map<std::string, LaraType> PLAYER_TYPES
{
{"Normal", LaraType::Normal},
{"Young", LaraType::Young},
@ -50,7 +50,7 @@ struct Level : public ScriptInterfaceLevel
LaraType GetLaraType() const override;
void SetWeatherStrength(float val);
void SetLevelFarView(short val);
static void Register(sol::table & parent);
static void Register(sol::table& parent);
WeatherType GetWeatherType() const override;
short GetMirrorRoom() const override;
short GetFogMinDistance() const override;

View file

@ -12,7 +12,7 @@ struct Mirror
int StartZ{ 0 };
int EndZ{ 0 };
static void Register(sol::table & parent);
static void Register(sol::table& parent);
Mirror() = default;
Mirror(short room, int startX, int endX, int startZ, int endZ);

View file

@ -3,7 +3,7 @@
#include "Scripting/Internal/ScriptAssert.h"
#include <string>
static const std::unordered_map<std::string, ErrorMode> kErrorModes {
static const std::unordered_map<std::string, ErrorMode> ERROR_MODES {
{"SILENT", ErrorMode::Silent},
{"WARN", ErrorMode::Warn},
{"TERMINATE", ErrorMode::Terminate}

View file

@ -1,8 +1,12 @@
#include "framework.h"
#include "Scripting/Internal/TEN/Inventory/InventoryHandler.h"
#include "InventoryHandler.h"
#include "Scripting/Internal/ReservedScriptNames.h"
#include "Game/Hud/Hud.h"
#include "Game/Lara/lara.h"
#include "Game/pickup/pickup.h"
#include "Scripting/Internal/ReservedScriptNames.h"
using namespace TEN::Hud;
/***
Inventory manipulation
@ -12,70 +16,57 @@ 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.
/// Add an item to the player's inventory.
//@function GiveItem
//@tparam Objects.ObjID 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(GAME_OBJECT_ID slot, sol::optional<int> count)
//@tparam Objects.ObjID objectID Object ID of the item to add.
//@int[opt] count The amount of items to add. Default is the yield from a single pickup, e.g. 1 from a medipack, 12 from a flare pack.
//@bool[opt] addToPickupSummary If true, display the item in the pickup summary. Default is false.
static void GiveItem(GAME_OBJECT_ID objectID, sol::optional<int> count, sol::optional<bool> addToPickupSummary)
{
// 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).
// can't use value_or(std::nullopt) here because nullopt isn't an int
if (count.has_value())
PickedUpObject(slot, count.value());
else
PickedUpObject(slot, std::nullopt);
PickedUpObject(objectID, count.has_value() ? std::optional<int>(*count) : std::nullopt);
if (addToPickupSummary.value_or(false))
{
auto pos = GetJointPosition(LaraItem, LM_HIPS).ToVector3();
g_Hud.PickupSummary.AddDisplayPickup(objectID, pos, count.value_or(1));
}
}
///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.
/// Remove an item from the player's inventory.
//@function TakeItem
//@tparam Objects.ObjID 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(GAME_OBJECT_ID slot, sol::optional<int> count)
//@tparam Objects.ObjID Object ID of the item to remove.
//@int[opt] count The amount of items to remove. Default is the yield from a single pickup, e.g. 1 from a medipack, 12 from a flare pack.
static void TakeItem(GAME_OBJECT_ID objectID, sol::optional<int> count)
{
//can't use value_or(std::nullopt) here because nullopt isn't an int
if (count.has_value())
RemoveObjectFromInventory(slot, count.value());
else
RemoveObjectFromInventory(slot, std::nullopt);
RemoveObjectFromInventory(objectID, count.has_value() ? std::optional<int>(*count) : std::nullopt);
}
///Get the amount the player holds of an item.
/// Get the amount of an item held in the player's inventory.
//@function GetItemCount
//@tparam Objects.ObjID 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 int InventoryGetCount(GAME_OBJECT_ID slot)
//@tparam Objects.ObjID objectID Object ID of the item to check.
//@treturn int The amount of items. -1 indicates infinity.
static int GetItemCount(GAME_OBJECT_ID objectID)
{
return GetInventoryCount(slot);
return GetInventoryCount(objectID);
}
///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.
/// Set the amount of an item in the player's inventory.
//@function SetItemCount
//@tparam Objects.ObjID 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 void InventorySetCount(GAME_OBJECT_ID slot, int count)
//@tparam Objects.ObjID objectID Object ID of the item amount to set.
//@tparam int count The amount of items to set. -1 indicates infinity.
static void SetItemCount(GAME_OBJECT_ID objectID, int count)
{
SetInventoryCount(slot, count);
SetInventoryCount(objectID, count);
}
void Register(sol::state* state, sol::table& parent)
{
sol::table table_inventory{ state->lua_state(), sol::create };
parent.set(ScriptReserved_Inventory, table_inventory);
auto tableInventory = sol::table{ state->lua_state(), sol::create };
parent.set(ScriptReserved_Inventory, tableInventory);
table_inventory.set_function(ScriptReserved_GiveInvItem, &InventoryAdd);
table_inventory.set_function(ScriptReserved_TakeInvItem, &InventoryRemove);
table_inventory.set_function(ScriptReserved_GetInvItemCount, &InventoryGetCount);
table_inventory.set_function(ScriptReserved_SetInvItemCount, &InventorySetCount);
tableInventory.set_function(ScriptReserved_GiveInvItem, &GiveItem);
tableInventory.set_function(ScriptReserved_TakeInvItem, &TakeItem);
tableInventory.set_function(ScriptReserved_GetInvItemCount, &GetItemCount);
tableInventory.set_function(ScriptReserved_SetInvItemCount, &SetItemCount);
}
}

View file

@ -3,17 +3,18 @@
#include <filesystem>
#include "Game/savegame.h"
#include "Game/control/volume.h"
#include "Game/effects/Electricity.h"
#include "Game/savegame.h"
#include "Scripting/Internal/ReservedScriptNames.h"
#include "Scripting/Internal/ScriptAssert.h"
#include "Scripting/Internal/ScriptUtil.h"
#include "Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h"
#include "Scripting/Internal/TEN/Vec2/Vec2.h"
#include "Scripting/Internal/TEN/Vec3/Vec3.h"
#include "Scripting/Internal/TEN/Rotation/Rotation.h"
#include "Scripting/Internal/TEN/Color/Color.h"
#include "Scripting/Internal/TEN/Logic/LevelFunc.h"
#include "Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h"
#include "Scripting/Internal/TEN/Rotation/Rotation.h"
#include "Scripting/Internal/TEN/Vec2/Vec2.h"
#include "Scripting/Internal/TEN/Vec3/Vec3.h"
using namespace TEN::Effects::Electricity;
@ -37,7 +38,7 @@ enum class CallbackPoint
PostEnd
};
static const std::unordered_map<std::string, CallbackPoint> kCallbackPoints
static const std::unordered_map<std::string, CallbackPoint> CALLBACK_POINTS
{
{ ScriptReserved_PreStart, CallbackPoint::PreStart },
{ ScriptReserved_PostStart, CallbackPoint::PostStart },
@ -51,6 +52,13 @@ static const std::unordered_map<std::string, CallbackPoint> kCallbackPoints
{ ScriptReserved_PostEnd, CallbackPoint::PostEnd }
};
static const std::unordered_map<std::string, VolumeEventType> EVENT_TYPES
{
{ ScriptReserved_OnEnter, VolumeEventType::Enter },
{ ScriptReserved_OnInside, VolumeEventType::Inside },
{ ScriptReserved_OnLeave, VolumeEventType::Leave }
};
enum class LevelEndReason
{
LevelComplete,
@ -60,7 +68,7 @@ enum class LevelEndReason
Other
};
static const std::unordered_map<std::string, LevelEndReason> kLevelEndReasons
static const std::unordered_map<std::string, LevelEndReason> LEVEL_END_REASONS
{
{ ScriptReserved_EndReasonLevelComplete, LevelEndReason::LevelComplete },
{ ScriptReserved_EndReasonLoadGame, LevelEndReason::LoadGame },
@ -154,9 +162,11 @@ LogicHandler::LogicHandler(sol::state* lua, sol::table & parent) : m_handler{ lu
tableLogic.set_function(ScriptReserved_AddCallback, &LogicHandler::AddCallback, this);
tableLogic.set_function(ScriptReserved_RemoveCallback, &LogicHandler::RemoveCallback, this);
tableLogic.set_function(ScriptReserved_HandleEvent, &LogicHandler::HandleEvent, this);
m_handler.MakeReadOnlyTable(tableLogic, ScriptReserved_EndReason, kLevelEndReasons);
m_handler.MakeReadOnlyTable(tableLogic, ScriptReserved_CallbackPoint, kCallbackPoints);
m_handler.MakeReadOnlyTable(tableLogic, ScriptReserved_EndReason, LEVEL_END_REASONS);
m_handler.MakeReadOnlyTable(tableLogic, ScriptReserved_CallbackPoint, CALLBACK_POINTS);
m_handler.MakeReadOnlyTable(tableLogic, ScriptReserved_EventType, EVENT_TYPES);
m_callbacks.insert(std::make_pair(CallbackPoint::PreStart, &m_callbacksPreStart));
m_callbacks.insert(std::make_pair(CallbackPoint::PostStart, &m_callbacksPostStart));
@ -260,6 +270,24 @@ void LogicHandler::RemoveCallback(CallbackPoint point, const LevelFunc& levelFun
it->second->erase(levelFunc.m_funcName);
}
/*** Attempt to find an event set and exectute a particular event from it.
@function HandleEvent
@tparam name string Name of the event set to find.
@tparam type EventType Event to execute.
@tparam activator Moveable Optional activator. Default is the player object.
*/
void LogicHandler::HandleEvent(const std::string& name, VolumeEventType type, sol::optional<Moveable&> activator)
{
bool success = TEN::Control::Volumes::HandleEvent(name, type, activator.has_value() ?
(VolumeActivator)activator.value().GetIndex() : nullptr);
if (!success)
{
TENLog("Error: event " + name + " could not be executed. Check if event with such name exists in project.",
LogLevel::Error, LogConfig::All, false);
}
}
void LogicHandler::ResetLevelTables()
{
auto state = m_handler.GetState();

View file

@ -4,6 +4,8 @@
#include "Game/items.h"
#include "Scripting/Include/ScriptInterfaceGame.h"
#include "Scripting/Internal/LuaHandler.h"
#include "Scripting/Internal/ScriptUtil.h"
#include "Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h"
enum class CallbackPoint;
class LevelFunc;
@ -121,6 +123,7 @@ public:
void AddCallback(CallbackPoint point, const LevelFunc& levelFunc);
void RemoveCallback(CallbackPoint point, const LevelFunc& levelFunc);
void HandleEvent(const std::string& name, VolumeEventType type, sol::optional<Moveable&> activator);
void ResetScripts(bool clearGameVars) override;
void ShortenTENCalls() override;

View file

@ -310,11 +310,12 @@ namespace Misc
return (int)round(Vector3::Distance(p1, p2));
}
// TODO: Deprecated. Also should not use int!
///Calculate the horizontal distance between two positions.
//@function CalculateHorizontalDistance
//@tparam Vec3 posA first position
//@tparam Vec3 posB second position
//@treturn int the direct distance on the XZ plane from one position to the other
//@treturn float the direct distance on the XZ plane from one position to the other
static int CalculateHorizontalDistance(const Vec3& pos1, const Vec3& pos2)
{
auto p1 = Vector2(pos1.x, pos1.z);
@ -322,49 +323,49 @@ namespace Misc
return (int)round(Vector2::Distance(p1, p2));
}
///Translate a pair of percentages to screen-space pixel coordinates.
//To be used with @{Strings.DisplayString:SetPosition} and @{Strings.DisplayString}.
/// Translate a pair display position coordinates to pixel coordinates.
//To be used with @{ Strings.DisplayString:SetPosition } and @{ Strings.DisplayString }.
//@function PercentToScreen
//@tparam float x percent value to translate to x-coordinate
//@tparam float y percent value to translate to y-coordinate
//@treturn int x coordinate in pixels
//@treturn int y coordinate in pixels
//@tparam float x X component of the display position.
//@tparam float y Y component of the display position.
//@treturn int x X coordinate in pixels.
//@treturn int y 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})
//local str1 = DisplayString("You spawned an enemy!", halfwayX, halfwayY, Color(255, 100, 100), false, { DisplayStringOption.SHADOW, DisplayStringOption.CENTER })
//
//LevelFuncs.triggerOne = function(obj)
// ShowString(str1, 4)
//end
static std::tuple<int, int> PercentToScreen(double x, double y)
static std::tuple<int, int> PercentToScreen(float x, float y)
{
auto fWidth = (double)g_Configuration.ScreenWidth;
auto fHeight = (double)g_Configuration.ScreenHeight;
int resX = (int)std::round(fWidth / 100.0 * x);
int resY = (int)std::round(fHeight / 100.0 * y);
//todo this still assumes a resolution of 800/600. account for this somehow
float fWidth = g_Configuration.ScreenWidth;
float fHeight = g_Configuration.ScreenHeight;
int resX = (int)std::round(fWidth / 100.0f * x);
int resY = (int)std::round(fHeight / 100.0f * y);
return std::make_tuple(resX, resY);
}
/// Translate a pair of coordinates to percentages of window dimensions.
/// Translate a pair of pixel coordinates to display position coordinates.
//To be used with @{ Strings.DisplayString:GetPosition }.
//@function ScreenToPercent
//@tparam int x pixel value to translate to a percentage of the window width
//@tparam int y pixel value to translate to a percentage of the window height
//@treturn float x coordinate as percentage
//@treturn float y coordinate as percentage
static std::tuple<double, double> ScreenToPercent(int x, int y)
//@tparam int x X pixel coordinate to translate to display position.
//@tparam int y Y pixel coordinate to translate to display position.
//@treturn float x X component of display position.
//@treturn float y Y component of display position.
static std::tuple<float, float> ScreenToPercent(int x, int y)
{
auto fWidth = (double)g_Configuration.ScreenWidth;
auto fHeight = (double)g_Configuration.ScreenHeight;
double resX = x / fWidth * 100.0;
double resY = y / fHeight * 100.0;
float fWidth = g_Configuration.ScreenWidth;
float fHeight = g_Configuration.ScreenHeight;
float resX = x / fWidth * 100.0f;
float resY = y / fHeight * 100.0f;
return std::make_tuple(resX, resY);
}
/// Reset object camera back to Lara and deactivate object camera.
/// Reset object camera back to the player and deactivate object camera.
//@function ResetObjCamera
static void ResetObjCamera()
{
@ -457,6 +458,7 @@ namespace Misc
tableMisc.set_function(ScriptReserved_FlipMap, &FlipMap);
tableMisc.set_function(ScriptReserved_PlayFlyBy, &PlayFlyBy);
tableMisc.set_function(ScriptReserved_ResetObjCamera, &ResetObjCamera);
tableMisc.set_function(ScriptReserved_PrintLog, &PrintLog);
LuaHandler handler{ state };

View file

@ -19,7 +19,7 @@ static auto newindex_error = newindex_error_maker(AIObject, ScriptReserved_AIObj
AIObject::AIObject(AI_OBJECT & ref) : m_aiObject{ref}
{};
void AIObject::Register(sol::table & parent)
void AIObject::Register(sol::table& parent)
{
parent.new_usertype<AIObject>(ScriptReserved_AIObject,
sol::no_constructor, // ability to spawn new ones could be added later

View file

@ -20,7 +20,7 @@ public:
AIObject& operator=(AIObject const& other) = delete;
AIObject(AIObject const& other) = delete;
static void Register(sol::table & parent);
static void Register(sol::table& parent);
Vec3 GetPos() const;
void SetPos(Vec3 const& pos);

View file

@ -22,7 +22,7 @@ static auto newindex_error = newindex_error_maker(CameraObject, ScriptReserved_C
CameraObject::CameraObject(LevelCameraInfo & ref) : m_camera{ref}
{};
void CameraObject::Register(sol::table & parent)
void CameraObject::Register(sol::table& parent)
{
parent.new_usertype<CameraObject>(ScriptReserved_Camera,
sol::no_constructor, // ability to spawn new ones could be added later

View file

@ -22,7 +22,7 @@ public:
CameraObject& operator=(CameraObject const& other) = delete;
CameraObject(CameraObject const& other) = delete;
static void Register(sol::table &);
static void Register(sol::table&);
Vec3 GetPos() const;
void SetPos(Vec3 const& pos);

View file

@ -56,7 +56,7 @@ public:
void UndrawWeapon();
void ThrowAwayTorch();
bool TorchIsLit() const;
static void Register(sol::table & parent);
static void Register(sol::table& parent);
using Moveable::Moveable;
};

View file

@ -76,19 +76,18 @@ most can just be ignored (see usage).
@tparam string name Lua name of the item
@tparam Vec3 position position in level
@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] roomID 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})
@tparam[opt] table AIBits table with AI bits (default { 0, 0, 0, 0, 0, 0 })
@treturn Moveable A new Moveable object (a wrapper around the new object)
@usage
local item = Moveable(
TEN.Objects.ObjID.PISTOLS_ITEM, -- object id
"test", -- name
Vec3(18907, 0, 21201)
)
Vec3(18907, 0, 21201))
*/
static std::unique_ptr<Moveable> Create(
GAME_OBJECT_ID objID,

View file

@ -19,13 +19,13 @@ when you need to use screen-space coordinates.
@pragma nostrip
*/
UserDisplayString::UserDisplayString(const std::string& key, int x, int y, D3DCOLOR color, const FlagArray& flags, bool translated) :
m_key{ key },
m_x{ x },
m_y{ y },
m_color{ color },
m_flags{ flags },
m_isTranslated{ translated }
UserDisplayString::UserDisplayString(const std::string& key, int x, int y, D3DCOLOR color, const FlagArray& flags, bool isTranslated) :
m_key(key),
m_x(x),
m_y(y),
m_color(color),
m_flags(flags),
m_isTranslated(isTranslated)
{
}
@ -38,18 +38,19 @@ DisplayString::DisplayString()
}
/*** Create a DisplayString.
For use in @{Strings.ShowString|ShowString} and @{Strings.HideString|HideString}.
For use in @{ Strings.ShowString | ShowString } and @{ Strings.HideString | HideString }.
@function DisplayString
@tparam string str string to print or key of translated string
@tparam int x x-coordinate of top-left of string (or the center if DisplayStringOption.CENTER is given)
@tparam int y y-coordinate of top-left of string (or the center if DisplayStringOption.CENTER is given)
@tparam Color color the color of the text
@tparam bool translated if false or omitted, the str argument will be printed.
If true, the str argument will be the key of a translated string specified in
strings.lua. __Default: false__.
@tparam table flags a table of display options. Can be empty or omitted. The possible values and their effects are...
TEN.Strings.DisplayStringOption.CENTER -- see x and y parameters
TEN.Strings.DisplayStringOption.SHADOW -- will give the text a small shadow
@tparam string string The string to display or key of the translated string.
@tparam int x X component of the string.
@tparam int y Y component of the string.
@tparam Color color The color of the string.
@tparam bool[opt] translated If false or omitted, the input string argument will be displayed.
If true, the string argument will be the key of a translated string specified in strings.lua. __Default: false__.
@tparam table[opt] flags A table of string display options. Can be empty or omitted. The possible values and their effects are:
TEN.Strings.DisplayStringOption.CENTER: set the horizontal origin point to the center of the string.
TEN.Strings.DisplayStringOption.RIGHT: set the horizontal origin point to right of the string.
TEN.Strings.DisplayStringOption.SHADOW: give the string a small shadow.
TEN.Strings.DisplayStringOption.BLINK: blink the string.
__Default: empty__
@treturn DisplayString A new DisplayString object.
*/
@ -103,21 +104,21 @@ void DisplayString::Register(sol::table& parent)
/// Get the display string's color
// @function DisplayString:GetColor
// @treturn Color a copy of the display string's color
ScriptReserved_GetColor, &DisplayString::GetCol,
ScriptReserved_GetColor, &DisplayString::GetColor,
/// Set the display string's color
// @function DisplayString:SetColor
// @tparam Color color the new color of the display string
ScriptReserved_SetColor, &DisplayString::SetCol,
ScriptReserved_SetColor, &DisplayString::SetColor,
/// Get the string key to use. If `translated` is true when @{DisplayString}
/// Get the string key to use. If `isTranslated` is true when @{ DisplayString }
// is called, this will be the string key for the translation that will be displayed.
// If false or omitted, this will be the string that's displayed.
// @function DisplayString:GetKey
// @treturn string a string
ScriptReserved_GetKey, &DisplayString::GetKey,
/// Set the string key to use. If `translated` is true when @{DisplayString}
/// Set the string key to use. If `isTranslated` is true when @{ DisplayString }
// is called, this will be the string key for the translation that will be displayed.
// If false or omitted, this will be the string that's displayed.
// @function DisplayString:SetKey
@ -128,15 +129,15 @@ void DisplayString::Register(sol::table& parent)
/// Set the position of the string.
// Screen-space coordinates are expected.
// @function DisplayString:SetPosition
// @tparam int x x-coordinate of the string
// @tparam int y y-coordinate of the string
// @tparam int x X component.
// @tparam int y Y component.
ScriptReserved_SetPosition, &DisplayString::SetPos,
/// Get the position of the string.
// Screen-space coordinates are returned.
// @function DisplayString:GetPosition
// @treturn int x x-coordinate of the string
// @treturn int y y-coordinate of the string
// @treturn int x X component.
// @treturn int y Y component.
ScriptReserved_GetPosition, &DisplayString::GetPos,
/// Set the display string's flags
@ -157,8 +158,7 @@ void DisplayString::Register(sol::table& parent)
// @function DisplayString:SetTranslated
// @tparam bool shouldTranslate if true, the string's key will be used as the key for the translation that will be displayed.
// If false, the key itself will be displayed
ScriptReserved_SetTranslated, &DisplayString::SetTranslated
);
ScriptReserved_SetTranslated, &DisplayString::SetTranslated);
}
DisplayStringIDType DisplayString::GetID() const
@ -179,7 +179,7 @@ std::tuple<int, int> DisplayString::GetPos() const
return std::make_tuple(displayString.m_x, displayString.m_y);
}
void DisplayString::SetCol(const ScriptColor& color)
void DisplayString::SetColor(const ScriptColor& color)
{
UserDisplayString& displayString = s_getItemCallback(m_id).value();
displayString.m_color = color;
@ -188,10 +188,10 @@ void DisplayString::SetCol(const ScriptColor& color)
//s_addItemCallback(m_id, s);
}
ScriptColor DisplayString::GetCol()
ScriptColor DisplayString::GetColor()
{
UserDisplayString& s = s_getItemCallback(m_id).value();
return s.m_color;
UserDisplayString& displayString = s_getItemCallback(m_id).value();
return displayString.m_color;
}
void DisplayString::SetKey(const std::string& key)

View file

@ -4,20 +4,25 @@
#include "Scripting/Internal/TEN/Color/Color.h"
enum class DisplayStringOptions : size_t
enum class DisplayStringOptions
{
CENTER,
OUTLINE,
NUM_OPTIONS
Center,
Outline,
Right,
Blink,
Count
};
static const std::unordered_map<std::string, DisplayStringOptions> kDisplayStringOptionNames
static const std::unordered_map<std::string, DisplayStringOptions> DISPLAY_STRING_OPTION_NAMES
{
{"CENTER", DisplayStringOptions::CENTER},
{"SHADOW", DisplayStringOptions::OUTLINE}
{ "CENTER", DisplayStringOptions::Center },
{ "SHADOW", DisplayStringOptions::Outline },
{ "RIGHT", DisplayStringOptions::Right },
{ "BLINK", DisplayStringOptions::Blink }
};
using FlagArray = std::array<bool, static_cast<size_t>(DisplayStringOptions::NUM_OPTIONS)>;
using FlagArray = std::array<bool, (int)DisplayStringOptions::Count>;
// Used to store data used to render the string.
// This is separate from DisplayString because the lifetimes of the classes differ slightly.
@ -62,8 +67,8 @@ public:
void SetPos(int x, int y);
std::tuple<int, int> GetPos() const;
void SetCol(const ScriptColor&);
ScriptColor GetCol();
void SetColor(const ScriptColor&);
ScriptColor GetColor();
void SetKey(const std::string&);
std::string GetKey() const;

View file

@ -12,7 +12,8 @@ On-screen strings.
@pragma nostrip
*/
StringsHandler::StringsHandler(sol::state* lua, sol::table & parent) : LuaHandler{ lua }
StringsHandler::StringsHandler(sol::state* lua, sol::table& parent) :
LuaHandler{ lua }
{
sol::table table_strings{ m_lua->lua_state(), sol::create };
parent.set(ScriptReserved_Strings, table_strings);
@ -46,12 +47,11 @@ Checks if the string is shown
DisplayString::Register(table_strings);
DisplayString::SetCallbacks(
[this](auto && ... param) {return SetDisplayString(std::forward<decltype(param)>(param)...); },
[this](auto && ... param) {return ScheduleRemoveDisplayString(std::forward<decltype(param)>(param)...); },
[this](auto && ... param) {return GetDisplayString(std::forward<decltype(param)>(param)...); }
);
[this](auto && ... param) { return SetDisplayString(std::forward<decltype(param)>(param)...); },
[this](auto && ... param) { return ScheduleRemoveDisplayString(std::forward<decltype(param)>(param)...); },
[this](auto && ... param) { return GetDisplayString(std::forward<decltype(param)>(param)...); });
MakeReadOnlyTable(table_strings, ScriptReserved_DisplayStringOption, kDisplayStringOptionNames);
MakeReadOnlyTable(table_strings, ScriptReserved_DisplayStringOption, DISPLAY_STRING_OPTION_NAMES);
}
std::optional<std::reference_wrapper<UserDisplayString>> StringsHandler::GetDisplayString(DisplayStringIDType id)
@ -116,12 +116,18 @@ void StringsHandler::ProcessDisplayStrings(float deltaTime)
auto cstr = str.m_isTranslated ? g_GameFlow->GetString(str.m_key.c_str()) : str.m_key.c_str();
int flags = 0;
if (str.m_flags[static_cast<size_t>(DisplayStringOptions::CENTER)])
if (str.m_flags[(size_t)DisplayStringOptions::Center])
flags |= PRINTSTRING_CENTER;
if (str.m_flags[static_cast<size_t>(DisplayStringOptions::OUTLINE)])
if (str.m_flags[(size_t)DisplayStringOptions::Right])
flags |= PRINTSTRING_RIGHT;
if (str.m_flags[(size_t)DisplayStringOptions::Outline])
flags |= PRINTSTRING_OUTLINE;
if (str.m_flags[(size_t)DisplayStringOptions::Blink])
flags |= PRINTSTRING_BLINK;
m_callbackDrawSring(cstr, str.m_color, str.m_x, str.m_y, flags);
str.m_timeRemaining -= deltaTime;

View file

@ -1001,9 +1001,8 @@ void LoadEventSets()
eventSet.Name = ReadString();
eventSet.Activators = (VolumeActivatorFlags)ReadInt32();
LoadEvent(eventSet.OnEnter);
LoadEvent(eventSet.OnInside);
LoadEvent(eventSet.OnLeave);
for (int eventType = 0; eventType < (int)VolumeEventType::Count; eventType++)
LoadEvent(eventSet.Events[eventType]);
g_Level.EventSets.push_back(eventSet);
}

View file

@ -713,6 +713,9 @@ xcopy /Y "$(SolutionDir)Libs\zlib\x64\*.dll" "$(TargetDir)"</Command>
<ClInclude Include="Scripting\Internal\ScriptAssert.h" />
<ClInclude Include="Scripting\Internal\ScriptUtil.h" />
<ClInclude Include="Scripting\Internal\TEN\Color\Color.h" />
<ClInclude Include="Scripting\Internal\TEN\DisplaySprite\AlignModes.h" />
<ClInclude Include="Scripting\Internal\TEN\DisplaySprite\ScaleModes.h" />
<ClInclude Include="Scripting\Internal\TEN\DisplaySprite\ScriptDisplaySprite.h" />
<ClInclude Include="Scripting\Internal\TEN\Effects\BlendIDs.h" />
<ClInclude Include="Scripting\Internal\TEN\Effects\EffectIDs.h" />
<ClInclude Include="Scripting\Internal\TEN\Effects\EffectsFunctions.h" />
@ -1132,6 +1135,7 @@ xcopy /Y "$(SolutionDir)Libs\zlib\x64\*.dll" "$(TargetDir)"</Command>
<ClCompile Include="Scripting\Internal\ScriptInterfaceState.cpp" />
<ClCompile Include="Scripting\Internal\ScriptUtil.cpp" />
<ClCompile Include="Scripting\Internal\TEN\Color\Color.cpp" />
<ClCompile Include="Scripting\Internal\TEN\DisplaySprite\ScriptDisplaySprite.cpp" />
<ClCompile Include="Scripting\Internal\TEN\Effects\EffectsFunctions.cpp" />
<ClCompile Include="Scripting\Internal\TEN\Flow\Animations\Animations.cpp" />
<ClCompile Include="Scripting\Internal\TEN\Flow\FlowHandler.cpp" />