mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
Fast reload (#1445)
* Initial commit * Fix crash on title * Update level.cpp * Update CHANGELOG.md * Do slight audiotrack fade-ins and fade-outs on leveljumps * Implement hash checks * Fixes * Rename rapid to fast * Fixed flipmaps and bridge reinit * Bypass reinitializing renderer for fast reload * Fix issue when title and last loaded level are the same files * Update CHANGELOG.md * Additional fixes * Wrap savegame loading code into a try-catch * Update CHANGELOG.md * Remove door collision on fast reload, restore stopper flag * Display log message if level file was not found * Update CHANGELOG.md * Clear blocked flag for boxes * Implement fast reload setting * Add defaults to settings * Consistent naming * Smooth level loading audio crossfades * Stop non-ambience soundtracks early * Minor formatting * Update Settings.lua --------- Co-authored-by: Jakub <80340234+Jakub768@users.noreply.github.com> Co-authored-by: Sezz <sezzary@outlook.com>
This commit is contained in:
parent
b79d662b13
commit
784f957596
25 changed files with 605 additions and 358 deletions
|
@ -6,7 +6,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
|
|||
## Version 1.6 - xxxx-xx-xx
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed engine performance around bridges.
|
||||
* Fixed engine performance if weather or bubble effects are active.
|
||||
* Fixed snow particles not always melting on the ground.
|
||||
|
@ -22,7 +21,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
|
|||
* Fixed Lens Flare object not functioning properly.
|
||||
|
||||
### Features/Amendments
|
||||
|
||||
* Added fast savegame reloading.
|
||||
* Added ricochet sounds and make the effect more prominent.
|
||||
|
||||
### Lua API changes
|
||||
|
@ -32,7 +31,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
|
|||
## [Version 1.5](https://github.com/TombEngine/TombEditorReleases/releases/tag/v1.7.2) - 2024-11-03
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed original issue with classic switch off trigger incorrectly activating some trigger actions.
|
||||
* Fixed moveable status after antitriggering.
|
||||
* Fixed leveljump vehicle transfer.
|
||||
|
@ -71,7 +69,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
|
|||
* Fixed young Lara hair drawing. https://tombengine.com/docs/level-settings/#young_lara
|
||||
|
||||
### Features/Amendments
|
||||
|
||||
* Added high framerate mode (also known as 60 FPS mode).
|
||||
* Added a customisable global lensflare effect. https://tombengine.com/docs/level-settings/#lensflare
|
||||
* Added a customisable starry sky and meteor effect. https://tombengine.com/docs/level-settings/#stars
|
||||
|
@ -100,7 +97,6 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
|
|||
* Removed original limit of 32 active Flame Emitters.
|
||||
|
||||
### Lua API changes
|
||||
|
||||
* Added Flow.EnableHomeLevel() function.
|
||||
* Added Flow.IsStringPresent() function.
|
||||
* Added Flow.LensFlare() and Flow.Starfield() classes.
|
||||
|
|
|
@ -114,6 +114,10 @@
|
|||
<td class="name" ><a href="#errorMode">errorMode</a></td>
|
||||
<td class="summary">How should the application respond to script errors?</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name" ><a href="#fastReload">fastReload</a></td>
|
||||
<td class="summary">Can game utilize fast reload feature?</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<br/>
|
||||
|
@ -129,6 +133,7 @@
|
|||
</dt>
|
||||
<dd>
|
||||
How should the application respond to script errors?
|
||||
<br>
|
||||
Must be one of the following:
|
||||
<code>ErrorMode.TERMINATE</code> - print to the log file and return to the title level when any script error is hit.
|
||||
This is the one you will want to go for if you want to know IMMEDIATELY if something has gone wrong.</p>
|
||||
|
@ -151,6 +156,25 @@ has an unrecoverable error, the game will close.
|
|||
|
||||
|
||||
|
||||
</dd>
|
||||
<dt>
|
||||
<a name = "fastReload"></a>
|
||||
<strong>fastReload</strong>
|
||||
</dt>
|
||||
<dd>
|
||||
Can game utilize fast reload feature?
|
||||
<br>
|
||||
When set to <code>True</code>, game will attempt to perform fast savegame reloading, if current level is the same as
|
||||
level loaded from the savegame. It will not work if level timestamp or checksum has changed (i.e. level was
|
||||
updated). If set to <code>False</code> this functionality is turned off.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ local Flow = TEN.Flow
|
|||
|
||||
local settings = Flow.Settings.new()
|
||||
settings.errorMode = Flow.ErrorMode.WARN
|
||||
settings.fastReload = true
|
||||
Flow.SetSettings(settings)
|
||||
|
||||
local anims = Flow.Animations.new()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include "Game/items.h"
|
||||
#include "Math/Math.h"
|
||||
#include "Specific/Clock.h"
|
||||
|
||||
struct CollisionInfo;
|
||||
|
||||
|
@ -67,7 +68,7 @@ enum CAMERA_FLAGS
|
|||
CF_CHASE_OBJECT = 3,
|
||||
};
|
||||
|
||||
constexpr auto FADE_SCREEN_SPEED = 16.0f / 255.0f;
|
||||
constexpr auto FADE_SCREEN_SPEED = 2.0f / FPS;
|
||||
constexpr auto DEFAULT_FOV = 80.0f;
|
||||
|
||||
extern CAMERA_INFO Camera;
|
||||
|
|
|
@ -105,8 +105,6 @@ int RequiredStartPos;
|
|||
int CurrentLevel;
|
||||
int NextLevel;
|
||||
|
||||
int SystemNameHash = 0;
|
||||
|
||||
bool InItemControlLoop;
|
||||
short ItemNewRoomNo;
|
||||
short ItemNewRooms[MAX_ROOMS];
|
||||
|
@ -508,10 +506,8 @@ void InitializeOrLoadGame(bool loadGame)
|
|||
g_Gui.SetEnterInventory(NO_VALUE);
|
||||
|
||||
// Restore game?
|
||||
if (loadGame)
|
||||
if (loadGame && SaveGame::Load(g_GameFlow->SelectedSaveGame))
|
||||
{
|
||||
SaveGame::Load(g_GameFlow->SelectedSaveGame);
|
||||
|
||||
InitializeGame = false;
|
||||
|
||||
g_GameFlow->SelectedSaveGame = 0;
|
||||
|
@ -596,7 +592,7 @@ void EndGameLoop(int levelIndex, GameStatus reason)
|
|||
DeInitializeScripting(levelIndex, reason);
|
||||
|
||||
StopAllSounds();
|
||||
StopSoundTracks();
|
||||
StopSoundTracks(SOUND_XFADETIME_LEVELJUMP, true);
|
||||
StopRumble();
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,6 @@ extern bool ThreadEnded;
|
|||
extern int RequiredStartPos;
|
||||
extern int CurrentLevel;
|
||||
extern int NextLevel;
|
||||
extern int SystemNameHash;
|
||||
|
||||
extern bool InItemControlLoop;
|
||||
extern short ItemNewRoomNo;
|
||||
|
|
|
@ -7,14 +7,16 @@
|
|||
#include "Game/control/lot.h"
|
||||
#include "Game/control/volume.h"
|
||||
#include "Game/items.h"
|
||||
#include "Renderer/Renderer.h"
|
||||
#include "Math/Math.h"
|
||||
#include "Objects/game_object_ids.h"
|
||||
#include "Objects/Generic/Doors/generic_doors.h"
|
||||
#include "Renderer/Renderer.h"
|
||||
#include "Specific/trutils.h"
|
||||
|
||||
using namespace TEN::Math;
|
||||
using namespace TEN::Collision::Floordata;
|
||||
using namespace TEN::Collision::Point;
|
||||
using namespace TEN::Entities::Doors;
|
||||
using namespace TEN::Renderer;
|
||||
using namespace TEN::Utils;
|
||||
|
||||
|
@ -73,6 +75,74 @@ static void RemoveRoomFlipItems(const ROOM_INFO& room)
|
|||
}
|
||||
}
|
||||
|
||||
static void FlipRooms(int roomNumber, ROOM_INFO& activeRoom, ROOM_INFO& flippedRoom)
|
||||
{
|
||||
RemoveRoomFlipItems(activeRoom);
|
||||
|
||||
// Swap rooms.
|
||||
std::swap(activeRoom, flippedRoom);
|
||||
activeRoom.flippedRoom = flippedRoom.flippedRoom;
|
||||
flippedRoom.flippedRoom = NO_VALUE;
|
||||
activeRoom.itemNumber = flippedRoom.itemNumber;
|
||||
activeRoom.fxNumber = flippedRoom.fxNumber;
|
||||
|
||||
AddRoomFlipItems(activeRoom);
|
||||
|
||||
// Update active room sectors.
|
||||
for (auto& sector : activeRoom.Sectors)
|
||||
sector.RoomNumber = roomNumber;
|
||||
|
||||
// Update flipped room sectors.
|
||||
for (auto& sector : flippedRoom.Sectors)
|
||||
sector.RoomNumber = activeRoom.flippedRoom;
|
||||
|
||||
// Update renderer data.
|
||||
g_Renderer.FlipRooms(roomNumber, activeRoom.flippedRoom);
|
||||
}
|
||||
|
||||
void ResetRoomData()
|
||||
{
|
||||
// Remove all door collisions.
|
||||
for (const auto& item : g_Level.Items)
|
||||
{
|
||||
if (item.ObjectNumber == NO_VALUE || !item.Data.is<DOOR_DATA>())
|
||||
continue;
|
||||
|
||||
auto& doorItem = g_Level.Items[item.Index];
|
||||
auto& door = *(DOOR_DATA*)doorItem.Data;
|
||||
|
||||
if (door.opened)
|
||||
continue;
|
||||
|
||||
OpenThatDoor(&door.d1, &door);
|
||||
OpenThatDoor(&door.d2, &door);
|
||||
OpenThatDoor(&door.d1flip, &door);
|
||||
OpenThatDoor(&door.d2flip, &door);
|
||||
door.opened = true;
|
||||
}
|
||||
|
||||
// Unflip all rooms and remove all bridges and stopper flags.
|
||||
for (int roomNumber = 0; roomNumber < g_Level.Rooms.size(); roomNumber++)
|
||||
{
|
||||
auto& room = g_Level.Rooms[roomNumber];
|
||||
if (room.flippedRoom != NO_VALUE && room.flipNumber != NO_VALUE && FlipStats[room.flipNumber])
|
||||
{
|
||||
auto& flippedRoom = g_Level.Rooms[room.flippedRoom];
|
||||
FlipRooms(roomNumber, room, flippedRoom);
|
||||
}
|
||||
|
||||
for (auto& sector : room.Sectors)
|
||||
{
|
||||
sector.Stopper = false;
|
||||
sector.BridgeItemNumbers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure no pathfinding boxes are blocked (either by doors or by other door-like objects).
|
||||
for (int pathfindingBoxID = 0; pathfindingBoxID < g_Level.PathfindingBoxes.size(); pathfindingBoxID++)
|
||||
g_Level.PathfindingBoxes[pathfindingBoxID].flags &= ~BLOCKED;
|
||||
}
|
||||
|
||||
void DoFlipMap(int group)
|
||||
{
|
||||
if (group >= MAX_FLIPMAP)
|
||||
|
@ -87,30 +157,10 @@ void DoFlipMap(int group)
|
|||
auto& room = g_Level.Rooms[roomNumber];
|
||||
|
||||
// Handle flipmap.
|
||||
if (room.flippedRoom >= 0 && room.flipNumber == group)
|
||||
if (room.flippedRoom != NO_VALUE && room.flipNumber == group)
|
||||
{
|
||||
auto& flippedRoom = g_Level.Rooms[room.flippedRoom];
|
||||
|
||||
RemoveRoomFlipItems(room);
|
||||
|
||||
// Swap rooms.
|
||||
std::swap(room, flippedRoom);
|
||||
room.flippedRoom = flippedRoom.flippedRoom;
|
||||
flippedRoom.flippedRoom = NO_VALUE;
|
||||
room.itemNumber = flippedRoom.itemNumber;
|
||||
room.fxNumber = flippedRoom.fxNumber;
|
||||
|
||||
AddRoomFlipItems(room);
|
||||
|
||||
g_Renderer.FlipRooms(roomNumber, room.flippedRoom);
|
||||
|
||||
// Update active room sectors.
|
||||
for (auto& sector : room.Sectors)
|
||||
sector.RoomNumber = roomNumber;
|
||||
|
||||
// Update flipped room sectors.
|
||||
for (auto& sector : flippedRoom.Sectors)
|
||||
sector.RoomNumber = room.flippedRoom;
|
||||
FlipRooms(roomNumber, room, flippedRoom);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -125,6 +125,7 @@ struct ROOM_INFO
|
|||
};
|
||||
|
||||
void DoFlipMap(int group);
|
||||
void ResetRoomData();
|
||||
bool IsObjectInRoom(int roomNumber, GAME_OBJECT_ID objectID);
|
||||
bool IsPointInRoom(const Vector3i& pos, int roomNumber);
|
||||
int FindRoomNumber(const Vector3i& pos, int startRoomNumber = NO_VALUE, bool onlyNeighbors = false);
|
||||
|
|
|
@ -247,6 +247,7 @@ const std::vector<byte> SaveGame::Build()
|
|||
|
||||
Save::SaveGameHeaderBuilder sghb{ fbb };
|
||||
sghb.add_level_name(levelNameOffset);
|
||||
sghb.add_level_hash(LastLevelHash);
|
||||
|
||||
auto gameTime = GetGameTime(GameTimer);
|
||||
sghb.add_days(gameTime.Days);
|
||||
|
@ -1582,50 +1583,68 @@ bool SaveGame::Save(int slot)
|
|||
bool SaveGame::Load(int slot)
|
||||
{
|
||||
if (!IsSaveGameSlotValid(slot))
|
||||
{
|
||||
TENLog("Savegame slot " + std::to_string(slot) + " is invalid, load is impossible.", LogLevel::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DoesSaveGameExist(slot))
|
||||
{
|
||||
TENLog("Savegame in slot " + std::to_string(slot) + " does not exist.", LogLevel::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fileName = GetSavegameFilename(slot);
|
||||
TENLog("Loading from savegame: " + fileName, LogLevel::Info);
|
||||
|
||||
std::ifstream file;
|
||||
file.open(fileName, std::ios_base::app | std::ios_base::binary);
|
||||
|
||||
int size;
|
||||
file.read(reinterpret_cast<char*>(&size), sizeof(size));
|
||||
|
||||
// Read current level save data.
|
||||
std::vector<byte> saveData(size);
|
||||
file.read(reinterpret_cast<char*>(saveData.data()), size);
|
||||
|
||||
// Reset hub data, as it's about to be replaced with saved one.
|
||||
ResetHub();
|
||||
|
||||
// Read hub data from savegame.
|
||||
int hubCount;
|
||||
file.read(reinterpret_cast<char*>(&hubCount), sizeof(hubCount));
|
||||
|
||||
TENLog("Hub count: " + std::to_string(hubCount), LogLevel::Info);
|
||||
|
||||
for (int i = 0; i < hubCount; i++)
|
||||
auto file = std::ifstream();
|
||||
try
|
||||
{
|
||||
int index;
|
||||
file.read(reinterpret_cast<char*>(&index), sizeof(index));
|
||||
file.open(fileName, std::ios_base::app | std::ios_base::binary);
|
||||
|
||||
int size = 0;
|
||||
file.read(reinterpret_cast<char*>(&size), sizeof(size));
|
||||
std::vector<byte> hubBuffer(size);
|
||||
file.read(reinterpret_cast<char*>(hubBuffer.data()), size);
|
||||
|
||||
Hub[index] = hubBuffer;
|
||||
// Read current level save data.
|
||||
auto saveData = std::vector<byte>(size);
|
||||
file.read(reinterpret_cast<char*>(saveData.data()), size);
|
||||
|
||||
// Reset hub data, as it's about to be replaced with saved one.
|
||||
ResetHub();
|
||||
|
||||
// Read hub data from savegame.
|
||||
int hubCount = 0;
|
||||
file.read(reinterpret_cast<char*>(&hubCount), sizeof(hubCount));
|
||||
|
||||
TENLog("Hub count: " + std::to_string(hubCount), LogLevel::Info);
|
||||
|
||||
for (int i = 0; i < hubCount; i++)
|
||||
{
|
||||
int index = 0;
|
||||
file.read(reinterpret_cast<char*>(&index), sizeof(index));
|
||||
|
||||
file.read(reinterpret_cast<char*>(&size), sizeof(size));
|
||||
auto hubBuffer = std::vector<byte>(size);
|
||||
file.read(reinterpret_cast<char*>(hubBuffer.data()), size);
|
||||
|
||||
Hub[index] = hubBuffer;
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
// Load save data for current level.
|
||||
Parse(saveData, false);
|
||||
return true;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
TENLog("Error while loading savegame: " + std::string(ex.what()), LogLevel::Error);
|
||||
|
||||
if (file.is_open())
|
||||
file.close();
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
// Load save data for current level.
|
||||
Parse(saveData, false);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ParseStatistics(const Save::SaveGame* s, bool isHub)
|
||||
|
@ -2049,7 +2068,7 @@ static void ParseEffects(const Save::SaveGame* s)
|
|||
TENAssert(i < (int)SoundTrackType::Count, "Soundtrack type count was changed");
|
||||
|
||||
auto track = s->soundtracks()->Get(i);
|
||||
PlaySoundTrack(track->name()->str(), (SoundTrackType)i, track->position());
|
||||
PlaySoundTrack(track->name()->str(), (SoundTrackType)i, track->position(), SOUND_XFADETIME_LEVELJUMP);
|
||||
}
|
||||
|
||||
// Load fish swarm.
|
||||
|
@ -2655,6 +2674,7 @@ bool SaveGame::LoadHeader(int slot, SaveGameHeader* header)
|
|||
|
||||
header->Level = s->header()->level();
|
||||
header->LevelName = s->header()->level_name()->str();
|
||||
header->LevelHash = s->header()->level_hash();
|
||||
header->Days = s->header()->days();
|
||||
header->Hours = s->header()->hours();
|
||||
header->Minutes = s->header()->minutes();
|
||||
|
|
|
@ -11,32 +11,33 @@ constexpr auto SAVEGAME_MAX = 16;
|
|||
|
||||
struct Stats
|
||||
{
|
||||
unsigned int Timer;
|
||||
unsigned int Distance;
|
||||
unsigned int AmmoHits;
|
||||
unsigned int AmmoUsed;
|
||||
unsigned int HealthUsed;
|
||||
unsigned int Kills;
|
||||
unsigned int Secrets;
|
||||
unsigned int Timer = 0;
|
||||
unsigned int Distance = 0;
|
||||
unsigned int AmmoHits = 0;
|
||||
unsigned int AmmoUsed = 0;
|
||||
unsigned int HealthUsed = 0;
|
||||
unsigned int Kills = 0;
|
||||
unsigned int Secrets = 0;
|
||||
};
|
||||
|
||||
struct GameStats
|
||||
{
|
||||
Stats Game;
|
||||
Stats Level;
|
||||
Stats Game = {};
|
||||
Stats Level = {};
|
||||
};
|
||||
|
||||
struct SaveGameHeader
|
||||
{
|
||||
std::string LevelName;
|
||||
int Days;
|
||||
int Hours;
|
||||
int Minutes;
|
||||
int Seconds;
|
||||
int Level;
|
||||
int Timer;
|
||||
int Count;
|
||||
bool Present;
|
||||
std::string LevelName = {};
|
||||
int LevelHash = 0;
|
||||
int Days = 0;
|
||||
int Hours = 0;
|
||||
int Minutes = 0;
|
||||
int Seconds = 0;
|
||||
int Level = 0;
|
||||
int Timer = 0;
|
||||
int Count = 0;
|
||||
bool Present = false;
|
||||
};
|
||||
|
||||
class SaveGame
|
||||
|
|
|
@ -92,7 +92,7 @@ namespace TEN::Entities::Doors
|
|||
doorData->d1.block = (boxNumber != NO_VALUE && g_Level.PathfindingBoxes[boxNumber].flags & BLOCKABLE) ? boxNumber : NO_VALUE;
|
||||
doorData->d1.data = *doorData->d1.floor;
|
||||
|
||||
if (r->flippedRoom != -1)
|
||||
if (r->flippedRoom != NO_VALUE)
|
||||
{
|
||||
r = &g_Level.Rooms[r->flippedRoom];
|
||||
doorData->d1flip.floor = GetSector(r, doorItem->Pose.Position.x - r->Position.x + xOffset, doorItem->Pose.Position.z - r->Position.z + zOffset);
|
||||
|
|
|
@ -22,6 +22,8 @@ namespace TEN::Renderer
|
|||
|
||||
bool Renderer::PrepareDataForTheRenderer()
|
||||
{
|
||||
TENLog("Preparing renderer...", LogLevel::Info);
|
||||
|
||||
_lastBlendMode = BlendMode::Unknown;
|
||||
_lastCullMode = CullMode::Unknown;
|
||||
_lastDepthState = DepthState::Unknown;
|
||||
|
|
|
@ -1013,10 +1013,6 @@ namespace TEN::Renderer
|
|||
_context->VSSetShader(_vsInventory.Get(), nullptr, 0);
|
||||
_context->PSSetShader(_psInventory.Get(), nullptr, 0);
|
||||
|
||||
// Set texture
|
||||
BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp);
|
||||
BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp);
|
||||
|
||||
if (CurrentLevel == 0)
|
||||
{
|
||||
auto titleMenu = g_Gui.GetMenuToDisplay();
|
||||
|
@ -1047,6 +1043,14 @@ namespace TEN::Renderer
|
|||
}
|
||||
else
|
||||
{
|
||||
if (g_Gui.GetInventoryMode() == InventoryMode::InGame ||
|
||||
g_Gui.GetInventoryMode() == InventoryMode::Examine)
|
||||
{
|
||||
// Set texture.
|
||||
BindTexture(TextureRegister::ColorMap, &std::get<0>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp);
|
||||
BindTexture(TextureRegister::NormalMap, &std::get<1>(_moveablesTextures[0]), SamplerStateRegister::AnisotropicClamp);
|
||||
}
|
||||
|
||||
switch (g_Gui.GetInventoryMode())
|
||||
{
|
||||
case InventoryMode::Load:
|
||||
|
@ -1103,26 +1107,30 @@ namespace TEN::Renderer
|
|||
|
||||
void Renderer::RenderLoadingScreen(float percentage)
|
||||
{
|
||||
// Set basic render states
|
||||
// Set basic render states.
|
||||
SetBlendMode(BlendMode::Opaque);
|
||||
SetCullMode(CullMode::CounterClockwise);
|
||||
|
||||
do
|
||||
{
|
||||
// Clear screen
|
||||
// Clear screen.
|
||||
_context->ClearRenderTargetView(_backBuffer.RenderTargetView.Get(), Colors::Black);
|
||||
_context->ClearDepthStencilView(_backBuffer.DepthStencilView.Get(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
|
||||
|
||||
// Bind the back buffer
|
||||
// Bind back buffer.
|
||||
_context->OMSetRenderTargets(1, _backBuffer.RenderTargetView.GetAddressOf(), _backBuffer.DepthStencilView.Get());
|
||||
_context->RSSetViewports(1, &_viewport);
|
||||
ResetScissor();
|
||||
|
||||
// Draw the full screen background
|
||||
// Draw fullscreen background. If unavailable, draw last dumped game scene.
|
||||
if (_loadingScreenTexture.Texture)
|
||||
DrawFullScreenQuad(
|
||||
_loadingScreenTexture.ShaderResourceView.Get(),
|
||||
Vector3(ScreenFadeCurrent, ScreenFadeCurrent, ScreenFadeCurrent));
|
||||
{
|
||||
DrawFullScreenQuad(_loadingScreenTexture.ShaderResourceView.Get(), Vector3(ScreenFadeCurrent, ScreenFadeCurrent, ScreenFadeCurrent));
|
||||
}
|
||||
else if (_dumpScreenRenderTarget.Texture)
|
||||
{
|
||||
DrawFullScreenQuad(_dumpScreenRenderTarget.ShaderResourceView.Get(), Vector3(ScreenFadeCurrent, ScreenFadeCurrent, ScreenFadeCurrent));
|
||||
}
|
||||
|
||||
if (ScreenFadeCurrent && percentage > 0.0f && percentage < 100.0f)
|
||||
DrawLoadingBar(percentage);
|
||||
|
|
|
@ -64,8 +64,10 @@ namespace TEN::Renderer
|
|||
texture = Texture2D();
|
||||
|
||||
if (std::filesystem::is_regular_file(path))
|
||||
{
|
||||
texture = Texture2D(_device.Get(), path);
|
||||
else
|
||||
}
|
||||
else if (!path.empty()) // Loading default texture without path may be intentional.
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
|
||||
TENLog("Texture file not found: " + converter.to_bytes(path), LogLevel::Warning);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "Game/control/control.h"
|
||||
#include "Scripting/Internal/TEN/Flow/Settings/Settings.h"
|
||||
|
||||
class ScriptInterfaceLevel;
|
||||
|
||||
|
@ -25,6 +27,7 @@ public:
|
|||
virtual ~ScriptInterfaceFlowHandler() = default;
|
||||
|
||||
virtual void LoadFlowScript() = 0;
|
||||
virtual Settings* GetSettings() = 0;
|
||||
|
||||
virtual void SetGameDir(const std::string& assetDir) = 0;
|
||||
virtual std::string GetGameDir() = 0;
|
||||
|
|
|
@ -238,6 +238,7 @@ static constexpr char ScriptReserved_LaraType[] = "LaraType";
|
|||
static constexpr char ScriptReserved_RotationAxis[] = "RotationAxis";
|
||||
static constexpr char ScriptReserved_ItemAction[] = "ItemAction";
|
||||
static constexpr char ScriptReserved_ErrorMode[] = "ErrorMode";
|
||||
static constexpr char ScriptReserved_FastReload[] = "FastReload";
|
||||
static constexpr char ScriptReserved_InventoryItem[] = "InventoryItem";
|
||||
static constexpr char ScriptReserved_LaraWeaponType[] = "LaraWeaponType";
|
||||
static constexpr char ScriptReserved_PlayerAmmoType[] = "PlayerAmmoType";
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
#include "framework.h"
|
||||
#include "Settings.h"
|
||||
#include "Scripting/Internal/TEN/Flow/Settings/Settings.h"
|
||||
|
||||
/***
|
||||
Settings that will be run on game startup.
|
||||
@tenclass Flow.Settings
|
||||
@pragma nostrip
|
||||
*/
|
||||
/// Settings that will be run on game startup.
|
||||
// @tenclass Flow.Settings
|
||||
// @pragma nostrip
|
||||
|
||||
void Settings::Register(sol::table & parent)
|
||||
void Settings::Register(sol::table& parent)
|
||||
{
|
||||
parent.new_usertype<Settings>("Settings",
|
||||
parent.new_usertype<Settings>(
|
||||
"Settings",
|
||||
sol::constructors<Settings()>(),
|
||||
sol::call_constructor, sol::constructors<Settings>(),
|
||||
|
||||
/*** How should the application respond to script errors?
|
||||
<br>
|
||||
Must be one of the following:
|
||||
`ErrorMode.TERMINATE` - print to the log file and return to the title level when any script error is hit.
|
||||
This is the one you will want to go for if you want to know IMMEDIATELY if something has gone wrong.
|
||||
|
@ -31,6 +31,14 @@ has an unrecoverable error, the game will close.
|
|||
|
||||
@mem errorMode
|
||||
*/
|
||||
"errorMode", &Settings::ErrorMode
|
||||
);
|
||||
"errorMode", &Settings::ErrorMode,
|
||||
|
||||
/// Can the game utilize the fast reload feature?
|
||||
// <br>
|
||||
// When set to `true`, the game will attempt to perform fast savegame reloading if current level is the same as
|
||||
// the level loaded from the savegame. It will not work if the level timestamp or checksum has changed
|
||||
// (i.e. level was updated). If set to `false`, this functionality is turned off.
|
||||
//
|
||||
// @mem fastReload
|
||||
"fastReload", &Settings::FastReload);
|
||||
}
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "Scripting/Internal/ScriptAssert.h"
|
||||
#include <string>
|
||||
|
||||
static const std::unordered_map<std::string, ErrorMode> ERROR_MODES {
|
||||
{"SILENT", ErrorMode::Silent},
|
||||
{"WARN", ErrorMode::Warn},
|
||||
{"TERMINATE", ErrorMode::Terminate}
|
||||
namespace sol { class state; }
|
||||
|
||||
static const std::unordered_map<std::string, ErrorMode> ERROR_MODES
|
||||
{
|
||||
{ "SILENT", ErrorMode::Silent },
|
||||
{ "WARN", ErrorMode::Warn },
|
||||
{ "TERMINATE", ErrorMode::Terminate }
|
||||
};
|
||||
|
||||
namespace sol {
|
||||
class state;
|
||||
}
|
||||
|
||||
struct Settings
|
||||
{
|
||||
ErrorMode ErrorMode;
|
||||
ErrorMode ErrorMode = ErrorMode::Warn;
|
||||
bool FastReload = true;
|
||||
|
||||
static void Register(sol::table & parent);
|
||||
static void Register(sol::table& parent);
|
||||
};
|
||||
|
||||
|
|
|
@ -490,7 +490,7 @@ std::optional<std::string> GetCurrentSubtitle()
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
void PlaySoundTrack(const std::string& track, SoundTrackType mode, QWORD position)
|
||||
void PlaySoundTrack(const std::string& track, SoundTrackType mode, QWORD position, int forceFadeInTime)
|
||||
{
|
||||
if (!g_Configuration.EnableSound)
|
||||
return;
|
||||
|
@ -565,11 +565,11 @@ void PlaySoundTrack(const std::string& track, SoundTrackType mode, QWORD positio
|
|||
// BGM tracks are crossfaded, and additionally shuffled a bit to make things more natural.
|
||||
// Think everybody are fed up with same start-up sounds of Caves ambience...
|
||||
|
||||
if (crossfade && BASS_ChannelIsActive(SoundtrackSlot[(int)SoundTrackType::BGM].Channel))
|
||||
if (forceFadeInTime > 0 || (crossfade && BASS_ChannelIsActive(SoundtrackSlot[(int)SoundTrackType::BGM].Channel)))
|
||||
{
|
||||
// Crossfade...
|
||||
BASS_ChannelSetAttribute(stream, BASS_ATTRIB_VOL, 0.0f);
|
||||
BASS_ChannelSlideAttribute(stream, BASS_ATTRIB_VOL, masterVolume, crossfadeTime);
|
||||
BASS_ChannelSlideAttribute(stream, BASS_ATTRIB_VOL, masterVolume, (forceFadeInTime > 0) ? forceFadeInTime : crossfadeTime);
|
||||
|
||||
// Shuffle...
|
||||
// Only activates if no custom position is passed as argument.
|
||||
|
@ -659,7 +659,7 @@ void PlaySoundTrack(int index, short mask)
|
|||
PlaySoundTrack(SoundTracks[index].Name, SoundTracks[index].Mode);
|
||||
}
|
||||
|
||||
void StopSoundTracks(bool excludeAmbience)
|
||||
void StopSoundTracks(int fadeoutTime, bool excludeAmbience)
|
||||
{
|
||||
for (int i = 0; i < (int)SoundTrackType::Count; i++)
|
||||
{
|
||||
|
@ -667,12 +667,15 @@ void StopSoundTracks(bool excludeAmbience)
|
|||
if (excludeAmbience && type == SoundTrackType::BGM)
|
||||
continue;
|
||||
|
||||
StopSoundTrack(type, SOUND_XFADETIME_ONESHOT);
|
||||
StopSoundTrack(type, fadeoutTime);
|
||||
}
|
||||
}
|
||||
|
||||
void StopSoundTrack(SoundTrackType mode, int fadeoutTime)
|
||||
{
|
||||
if (SoundtrackSlot[(int)mode].Channel == NULL)
|
||||
return;
|
||||
|
||||
// Do fadeout.
|
||||
BASS_ChannelSlideAttribute(SoundtrackSlot[(int)mode].Channel, BASS_ATTRIB_VOL | BASS_SLIDE_LOG, -1.0f, fadeoutTime);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ constexpr auto SOUND_MILLISECONDS_IN_SECOND = 1000.0f;
|
|||
constexpr auto SOUND_XFADETIME_BGM = 5000;
|
||||
constexpr auto SOUND_XFADETIME_BGM_START = 1500;
|
||||
constexpr auto SOUND_XFADETIME_ONESHOT = 200;
|
||||
constexpr auto SOUND_XFADETIME_LEVELJUMP = 400;
|
||||
constexpr auto SOUND_XFADETIME_CUTSOUND = 100;
|
||||
constexpr auto SOUND_XFADETIME_HIJACKSOUND = 50;
|
||||
constexpr auto SOUND_BGM_DAMP_COEFFICIENT = 0.5f;
|
||||
|
@ -164,11 +165,11 @@ void SayNo();
|
|||
void PlaySoundSources();
|
||||
int GetShatterSound(int shatterID);
|
||||
|
||||
void PlaySoundTrack(const std::string& trackName, SoundTrackType mode, QWORD position = 0);
|
||||
void PlaySoundTrack(const std::string& trackName, SoundTrackType mode, QWORD position = 0, int forceFadeInTime = 0);
|
||||
void PlaySoundTrack(const std::string& trackName, short mask = 0);
|
||||
void PlaySoundTrack(int index, short mask = 0);
|
||||
void StopSoundTrack(SoundTrackType mode, int fadeoutTime);
|
||||
void StopSoundTracks(bool excludeAmbience = false);
|
||||
void StopSoundTracks(int fadeoutTime = SOUND_XFADETIME_LEVELJUMP, bool excludeAmbience = false);
|
||||
void ClearSoundTrackMasks();
|
||||
void PlaySecretTrack();
|
||||
void EnumerateLegacyTracks();
|
||||
|
|
|
@ -77,44 +77,54 @@ const std::vector<GAME_OBJECT_ID> BRIDGE_OBJECT_IDS =
|
|||
ID_BRIDGE_CUSTOM
|
||||
};
|
||||
|
||||
char* LevelDataPtr;
|
||||
LEVEL g_Level;
|
||||
|
||||
std::vector<int> MoveablesIds;
|
||||
std::vector<int> StaticObjectsIds;
|
||||
std::vector<int> SpriteSequencesIds;
|
||||
LEVEL g_Level;
|
||||
|
||||
char* DataPtr;
|
||||
char* CurrentDataPtr;
|
||||
|
||||
bool FirstLevel = true;
|
||||
int SystemNameHash = 0;
|
||||
int LastLevelHash = 0;
|
||||
|
||||
std::filesystem::file_time_type LastLevelTimestamp;
|
||||
std::string LastLevelFilePath;
|
||||
|
||||
unsigned char ReadUInt8()
|
||||
{
|
||||
unsigned char value = *(unsigned char*)LevelDataPtr;
|
||||
LevelDataPtr += 1;
|
||||
unsigned char value = *(unsigned char*)CurrentDataPtr;
|
||||
CurrentDataPtr += 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
short ReadInt16()
|
||||
{
|
||||
short value = *(short*)LevelDataPtr;
|
||||
LevelDataPtr += 2;
|
||||
short value = *(short*)CurrentDataPtr;
|
||||
CurrentDataPtr += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
unsigned short ReadUInt16()
|
||||
{
|
||||
unsigned short value = *(unsigned short*)LevelDataPtr;
|
||||
LevelDataPtr += 2;
|
||||
unsigned short value = *(unsigned short*)CurrentDataPtr;
|
||||
CurrentDataPtr += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
int ReadInt32()
|
||||
{
|
||||
int value = *(int*)LevelDataPtr;
|
||||
LevelDataPtr += 4;
|
||||
int value = *(int*)CurrentDataPtr;
|
||||
CurrentDataPtr += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
float ReadFloat()
|
||||
{
|
||||
float value = *(float*)LevelDataPtr;
|
||||
LevelDataPtr += 4;
|
||||
float value = *(float*)CurrentDataPtr;
|
||||
CurrentDataPtr += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -152,8 +162,8 @@ bool ReadBool()
|
|||
|
||||
void ReadBytes(void* dest, int count)
|
||||
{
|
||||
memcpy(dest, LevelDataPtr, count);
|
||||
LevelDataPtr += count;
|
||||
memcpy(dest, CurrentDataPtr, count);
|
||||
CurrentDataPtr += count;
|
||||
}
|
||||
|
||||
long long ReadLEB128(bool sign)
|
||||
|
@ -188,9 +198,9 @@ std::string ReadString()
|
|||
return std::string();
|
||||
else
|
||||
{
|
||||
auto newPtr = LevelDataPtr + numBytes;
|
||||
auto result = std::string(LevelDataPtr, newPtr);
|
||||
LevelDataPtr = newPtr;
|
||||
auto newPtr = CurrentDataPtr + numBytes;
|
||||
auto result = std::string(CurrentDataPtr, newPtr);
|
||||
CurrentDataPtr = newPtr;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -205,54 +215,51 @@ void LoadItems()
|
|||
|
||||
InitializeItemArray(ITEM_COUNT_MAX);
|
||||
|
||||
if (g_Level.NumItems > 0)
|
||||
for (int i = 0; i < g_Level.NumItems; i++)
|
||||
{
|
||||
for (int i = 0; i < g_Level.NumItems; i++)
|
||||
{
|
||||
auto* item = &g_Level.Items[i];
|
||||
auto* item = &g_Level.Items[i];
|
||||
|
||||
item->Data = ItemData{};
|
||||
item->ObjectNumber = from_underlying(ReadInt16());
|
||||
item->RoomNumber = ReadInt16();
|
||||
item->Pose.Position.x = ReadInt32();
|
||||
item->Pose.Position.y = ReadInt32();
|
||||
item->Pose.Position.z = ReadInt32();
|
||||
item->Pose.Orientation.y = ReadInt16();
|
||||
item->Pose.Orientation.x = ReadInt16();
|
||||
item->Pose.Orientation.z = ReadInt16();
|
||||
item->Model.Color = ReadVector4();
|
||||
item->TriggerFlags = ReadInt16();
|
||||
item->Flags = ReadInt16();
|
||||
item->Name = ReadString();
|
||||
item->Data = ItemData{};
|
||||
item->ObjectNumber = from_underlying(ReadInt16());
|
||||
item->RoomNumber = ReadInt16();
|
||||
item->Pose.Position.x = ReadInt32();
|
||||
item->Pose.Position.y = ReadInt32();
|
||||
item->Pose.Position.z = ReadInt32();
|
||||
item->Pose.Orientation.y = ReadInt16();
|
||||
item->Pose.Orientation.x = ReadInt16();
|
||||
item->Pose.Orientation.z = ReadInt16();
|
||||
item->Model.Color = ReadVector4();
|
||||
item->TriggerFlags = ReadInt16();
|
||||
item->Flags = ReadInt16();
|
||||
item->Name = ReadString();
|
||||
|
||||
g_GameScriptEntities->AddName(item->Name, (short)i);
|
||||
g_GameScriptEntities->TryAddColliding((short)i);
|
||||
g_GameScriptEntities->AddName(item->Name, (short)i);
|
||||
g_GameScriptEntities->TryAddColliding((short)i);
|
||||
|
||||
memcpy(&item->StartPose, &item->Pose, sizeof(Pose));
|
||||
}
|
||||
memcpy(&item->StartPose, &item->Pose, sizeof(Pose));
|
||||
}
|
||||
|
||||
// Initialize items.
|
||||
for (int i = 0; i <= 1; i++)
|
||||
// Initialize items.
|
||||
for (int i = 0; i <= 1; i++)
|
||||
{
|
||||
// HACK: Initialize bridges first. Required because other items need final floordata to init properly.
|
||||
if (i == 0)
|
||||
{
|
||||
// HACK: Initialize bridges first. Required because other items need final floordata to init properly.
|
||||
if (i == 0)
|
||||
for (int j = 0; j < g_Level.NumItems; j++)
|
||||
{
|
||||
for (int j = 0; j < g_Level.NumItems; j++)
|
||||
{
|
||||
const auto& item = g_Level.Items[j];
|
||||
if (Contains(BRIDGE_OBJECT_IDS, item.ObjectNumber))
|
||||
InitializeItem(j);
|
||||
}
|
||||
const auto& item = g_Level.Items[j];
|
||||
if (Contains(BRIDGE_OBJECT_IDS, item.ObjectNumber))
|
||||
InitializeItem(j);
|
||||
}
|
||||
// Initialize non-bridge items second.
|
||||
else if (i == 1)
|
||||
}
|
||||
// Initialize non-bridge items second.
|
||||
else if (i == 1)
|
||||
{
|
||||
for (int j = 0; j < g_Level.NumItems; j++)
|
||||
{
|
||||
for (int j = 0; j < g_Level.NumItems; j++)
|
||||
{
|
||||
const auto& item = g_Level.Items[j];
|
||||
if (!item.IsBridge())
|
||||
InitializeItem(j);
|
||||
}
|
||||
const auto& item = g_Level.Items[j];
|
||||
if (!item.IsBridge())
|
||||
InitializeItem(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -674,7 +681,95 @@ static Plane ConvertFakePlaneToPlane(const Vector3& fakePlane, bool isFloor)
|
|||
return Plane(normal, dist);
|
||||
}
|
||||
|
||||
void ReadRooms()
|
||||
void LoadDynamicRoomData()
|
||||
{
|
||||
int roomCount = ReadInt32();
|
||||
|
||||
if (g_Level.Rooms.size() != roomCount)
|
||||
throw std::exception("Dynamic room data count is inconsistent with room count");
|
||||
|
||||
for (int i = 0; i < roomCount; i++)
|
||||
{
|
||||
auto& room = g_Level.Rooms[i];
|
||||
|
||||
room.Name = ReadString();
|
||||
|
||||
int tagCount = ReadInt32();
|
||||
room.Tags.resize(0);
|
||||
room.Tags.reserve(tagCount);
|
||||
|
||||
for (int j = 0; j < tagCount; j++)
|
||||
room.Tags.push_back(ReadString());
|
||||
|
||||
room.ambient = ReadVector3();
|
||||
|
||||
room.flippedRoom = ReadInt32();
|
||||
room.flags = ReadInt32();
|
||||
room.meshEffect = ReadInt32();
|
||||
room.reverbType = (ReverbType)ReadInt32();
|
||||
room.flipNumber = ReadInt32();
|
||||
|
||||
int staticCount = ReadInt32();
|
||||
room.mesh.resize(0);
|
||||
room.mesh.reserve(staticCount);
|
||||
|
||||
for (int j = 0; j < staticCount; j++)
|
||||
{
|
||||
auto& mesh = room.mesh.emplace_back();
|
||||
|
||||
mesh.roomNumber = i;
|
||||
mesh.pos.Position.x = ReadInt32();
|
||||
mesh.pos.Position.y = ReadInt32();
|
||||
mesh.pos.Position.z = ReadInt32();
|
||||
mesh.pos.Orientation.y = ReadUInt16();
|
||||
mesh.pos.Orientation.x = ReadUInt16();
|
||||
mesh.pos.Orientation.z = ReadUInt16();
|
||||
mesh.scale = ReadFloat();
|
||||
mesh.flags = ReadUInt16();
|
||||
mesh.color = ReadVector4();
|
||||
mesh.staticNumber = ReadUInt16();
|
||||
mesh.HitPoints = ReadInt16();
|
||||
mesh.Name = ReadString();
|
||||
|
||||
g_GameScriptEntities->AddName(mesh.Name, mesh);
|
||||
}
|
||||
|
||||
int triggerVolumeCount = ReadInt32();
|
||||
room.TriggerVolumes.resize(0);
|
||||
room.TriggerVolumes.reserve(triggerVolumeCount);
|
||||
|
||||
for (int j = 0; j < triggerVolumeCount; j++)
|
||||
{
|
||||
auto& volume = room.TriggerVolumes.emplace_back();
|
||||
|
||||
volume.Type = (VolumeType)ReadInt32();
|
||||
|
||||
auto pos = ReadVector3();
|
||||
auto orient = ReadVector4();
|
||||
auto scale = ReadVector3();
|
||||
|
||||
volume.Enabled = ReadBool();
|
||||
volume.DetectInAdjacentRooms = ReadBool();
|
||||
|
||||
volume.Name = ReadString();
|
||||
volume.EventSetIndex = ReadInt32();
|
||||
|
||||
volume.Box = BoundingOrientedBox(pos, scale, orient);
|
||||
volume.Sphere = BoundingSphere(pos, scale.x);
|
||||
|
||||
volume.StateQueue.reserve(VOLUME_STATE_QUEUE_SIZE);
|
||||
|
||||
g_GameScriptEntities->AddName(volume.Name, volume);
|
||||
}
|
||||
|
||||
g_GameScriptEntities->AddName(room.Name, room);
|
||||
|
||||
room.itemNumber = NO_VALUE;
|
||||
room.fxNumber = NO_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadStaticRoomData()
|
||||
{
|
||||
constexpr auto ILLEGAL_FLOOR_SLOPE_ANGLE = ANGLE(36.0f);
|
||||
constexpr auto ILLEGAL_CEILING_SLOPE_ANGLE = ANGLE(45.0f);
|
||||
|
@ -687,12 +782,6 @@ void ReadRooms()
|
|||
{
|
||||
auto& room = g_Level.Rooms.emplace_back();
|
||||
|
||||
room.Name = ReadString();
|
||||
|
||||
int tagCount = ReadInt32();
|
||||
for (int j = 0; j < tagCount; j++)
|
||||
room.Tags.push_back(ReadString());
|
||||
|
||||
room.Position.x = ReadInt32();
|
||||
room.Position.y = 0;
|
||||
room.Position.z = ReadInt32();
|
||||
|
@ -824,8 +913,6 @@ void ReadRooms()
|
|||
}
|
||||
}
|
||||
|
||||
room.ambient = ReadVector3();
|
||||
|
||||
int lightCount = ReadInt32();
|
||||
room.lights.reserve(lightCount);
|
||||
for (int j = 0; j < lightCount; j++)
|
||||
|
@ -851,67 +938,8 @@ void ReadRooms()
|
|||
|
||||
room.lights.push_back(light);
|
||||
}
|
||||
|
||||
int staticCount = ReadInt32();
|
||||
room.mesh.reserve(staticCount);
|
||||
for (int j = 0; j < staticCount; j++)
|
||||
{
|
||||
auto& mesh = room.mesh.emplace_back();
|
||||
|
||||
mesh.roomNumber = i;
|
||||
mesh.pos.Position.x = ReadInt32();
|
||||
mesh.pos.Position.y = ReadInt32();
|
||||
mesh.pos.Position.z = ReadInt32();
|
||||
mesh.pos.Orientation.y = ReadUInt16();
|
||||
mesh.pos.Orientation.x = ReadUInt16();
|
||||
mesh.pos.Orientation.z = ReadUInt16();
|
||||
mesh.scale = ReadFloat();
|
||||
mesh.flags = ReadUInt16();
|
||||
mesh.color = ReadVector4();
|
||||
mesh.staticNumber = ReadUInt16();
|
||||
mesh.HitPoints = ReadInt16();
|
||||
mesh.Name = ReadString();
|
||||
|
||||
g_GameScriptEntities->AddName(mesh.Name, mesh);
|
||||
}
|
||||
|
||||
int triggerVolumeCount = ReadInt32();
|
||||
room.TriggerVolumes.reserve(triggerVolumeCount);
|
||||
for (int j = 0; j < triggerVolumeCount; j++)
|
||||
{
|
||||
auto& volume = room.TriggerVolumes.emplace_back();
|
||||
|
||||
volume.Type = (VolumeType)ReadInt32();
|
||||
|
||||
auto pos = ReadVector3();
|
||||
auto orient = ReadVector4();
|
||||
auto scale = ReadVector3();
|
||||
|
||||
volume.Enabled = ReadBool();
|
||||
volume.DetectInAdjacentRooms = ReadBool();
|
||||
|
||||
volume.Name = ReadString();
|
||||
volume.EventSetIndex = ReadInt32();
|
||||
|
||||
volume.Box = BoundingOrientedBox(pos, scale, orient);
|
||||
volume.Sphere = BoundingSphere(pos, scale.x);
|
||||
|
||||
volume.StateQueue.reserve(VOLUME_STATE_QUEUE_SIZE);
|
||||
|
||||
g_GameScriptEntities->AddName(volume.Name, volume);
|
||||
}
|
||||
|
||||
room.flippedRoom = ReadInt32();
|
||||
room.flags = ReadInt32();
|
||||
room.meshEffect = ReadInt32();
|
||||
room.reverbType = (ReverbType)ReadInt32();
|
||||
room.flipNumber = ReadInt32();
|
||||
|
||||
room.itemNumber = NO_VALUE;
|
||||
room.fxNumber = NO_VALUE;
|
||||
room.RoomNumber = i;
|
||||
|
||||
g_GameScriptEntities->AddName(room.Name, room);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -921,7 +949,7 @@ void LoadRooms()
|
|||
|
||||
Wibble = 0;
|
||||
|
||||
ReadRooms();
|
||||
LoadStaticRoomData();
|
||||
BuildOutsideRoomsTable();
|
||||
|
||||
int numFloorData = ReadInt32();
|
||||
|
@ -929,15 +957,39 @@ void LoadRooms()
|
|||
ReadBytes(g_Level.FloorData.data(), numFloorData * sizeof(short));
|
||||
}
|
||||
|
||||
void FreeLevel()
|
||||
void FreeLevel(bool partial)
|
||||
{
|
||||
static bool firstLevel = true;
|
||||
if (firstLevel)
|
||||
if (FirstLevel)
|
||||
{
|
||||
firstLevel = false;
|
||||
FirstLevel = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Should happen before resetting items.
|
||||
if (partial)
|
||||
ResetRoomData();
|
||||
|
||||
g_Level.Items.resize(0);
|
||||
g_Level.AIObjects.resize(0);
|
||||
g_Level.Cameras.resize(0);
|
||||
g_Level.Sinks.resize(0);
|
||||
g_Level.SoundSources.resize(0);
|
||||
g_Level.VolumeEventSets.resize(0);
|
||||
g_Level.GlobalEventSets.resize(0);
|
||||
g_Level.LoopedEventSetIndices.resize(0);
|
||||
|
||||
g_GameScript->FreeLevelScripts();
|
||||
g_GameScriptEntities->FreeEntities();
|
||||
|
||||
if (partial)
|
||||
return;
|
||||
|
||||
g_Renderer.FreeRendererData();
|
||||
|
||||
MoveablesIds.resize(0);
|
||||
StaticObjectsIds.resize(0);
|
||||
SpriteSequencesIds.resize(0);
|
||||
|
||||
g_Level.RoomTextures.resize(0);
|
||||
g_Level.MoveablesTextures.resize(0);
|
||||
g_Level.StaticsTextures.resize(0);
|
||||
|
@ -947,8 +999,6 @@ void FreeLevel()
|
|||
g_Level.Rooms.resize(0);
|
||||
g_Level.Bones.resize(0);
|
||||
g_Level.Meshes.resize(0);
|
||||
MoveablesIds.resize(0);
|
||||
SpriteSequencesIds.resize(0);
|
||||
g_Level.PathfindingBoxes.resize(0);
|
||||
g_Level.Overlaps.resize(0);
|
||||
g_Level.Anims.resize(0);
|
||||
|
@ -960,14 +1010,6 @@ void FreeLevel()
|
|||
g_Level.SoundDetails.resize(0);
|
||||
g_Level.SoundMap.resize(0);
|
||||
g_Level.FloorData.resize(0);
|
||||
g_Level.Cameras.resize(0);
|
||||
g_Level.Sinks.resize(0);
|
||||
g_Level.SoundSources.resize(0);
|
||||
g_Level.AIObjects.resize(0);
|
||||
g_Level.VolumeEventSets.resize(0);
|
||||
g_Level.GlobalEventSets.resize(0);
|
||||
g_Level.LoopedEventSetIndices.resize(0);
|
||||
g_Level.Items.resize(0);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
|
@ -975,10 +1017,6 @@ void FreeLevel()
|
|||
g_Level.Zones[j][i].clear();
|
||||
}
|
||||
|
||||
g_Renderer.FreeRendererData();
|
||||
g_GameScript->FreeLevelScripts();
|
||||
g_GameScriptEntities->FreeEntities();
|
||||
|
||||
FreeSamples();
|
||||
}
|
||||
|
||||
|
@ -1161,46 +1199,88 @@ bool Decompress(byte* dest, byte* src, unsigned long compressedSize, unsigned lo
|
|||
return false;
|
||||
}
|
||||
|
||||
bool LoadLevel(int levelIndex)
|
||||
bool ReadCompressedBlock(FILE* filePtr, bool skip)
|
||||
{
|
||||
auto* level = g_GameFlow->GetLevel(levelIndex);
|
||||
int compressedSize = 0;
|
||||
int uncompressedSize = 0;
|
||||
|
||||
auto assetDir = g_GameFlow->GetGameDir();
|
||||
auto levelPath = assetDir + level->FileName;
|
||||
TENLog("Loading level file: " + levelPath, LogLevel::Info);
|
||||
ReadFileEx(&uncompressedSize, 1, 4, filePtr);
|
||||
ReadFileEx(&compressedSize, 1, 4, filePtr);
|
||||
|
||||
LevelDataPtr = nullptr;
|
||||
if (skip)
|
||||
{
|
||||
fseek(filePtr, compressedSize, SEEK_CUR);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto compressedBuffer = (char*)malloc(compressedSize);
|
||||
ReadFileEx(compressedBuffer, compressedSize, 1, filePtr);
|
||||
DataPtr = (char*)malloc(uncompressedSize);
|
||||
Decompress((byte*)DataPtr, (byte*)compressedBuffer, compressedSize, uncompressedSize);
|
||||
free(compressedBuffer);
|
||||
|
||||
CurrentDataPtr = DataPtr;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FinalizeBlock()
|
||||
{
|
||||
if (DataPtr == nullptr)
|
||||
return;
|
||||
|
||||
free(DataPtr);
|
||||
DataPtr = nullptr;
|
||||
CurrentDataPtr = nullptr;
|
||||
}
|
||||
|
||||
void UpdateProgress(float progress, bool skip = false)
|
||||
{
|
||||
if (skip)
|
||||
return;
|
||||
|
||||
g_Renderer.UpdateProgress(progress);
|
||||
}
|
||||
|
||||
bool LoadLevel(std::string path, bool partial)
|
||||
{
|
||||
FILE* filePtr = nullptr;
|
||||
char* dataPtr = nullptr;
|
||||
bool LoadedSuccessfully;
|
||||
|
||||
auto loadingScreenPath = TEN::Utils::ToWString(assetDir + level->LoadScreenFileName);
|
||||
g_Renderer.SetLoadingScreen(loadingScreenPath);
|
||||
|
||||
SetScreenFadeIn(FADE_SCREEN_SPEED, true);
|
||||
g_Renderer.UpdateProgress(0);
|
||||
bool loadedSuccessfully = false;
|
||||
|
||||
try
|
||||
{
|
||||
filePtr = FileOpen(levelPath.c_str());
|
||||
filePtr = FileOpen(path.c_str());
|
||||
|
||||
if (!filePtr)
|
||||
throw std::exception{ (std::string{ "Unable to read level file: " } + levelPath).c_str() };
|
||||
throw std::exception{ (std::string{ "Unable to read level file: " } + path).c_str() };
|
||||
|
||||
char header[4];
|
||||
unsigned char version[4];
|
||||
int compressedSize;
|
||||
int uncompressedSize;
|
||||
int systemHash;
|
||||
int systemHash = 0;
|
||||
int levelHash = 0;
|
||||
|
||||
// Read file header
|
||||
ReadFileEx(&header, 1, 4, filePtr);
|
||||
ReadFileEx(&version, 1, 4, filePtr);
|
||||
ReadFileEx(&systemHash, 1, 4, filePtr);
|
||||
ReadFileEx(&levelHash, 1, 4, filePtr);
|
||||
|
||||
// Check file header
|
||||
// Check file header.
|
||||
if (std::string(header) != "TEN")
|
||||
throw std::invalid_argument("Level file header is not valid! Must be TEN. Probably old level version?");
|
||||
|
||||
// Check level file integrity to allow or disallow fast reload.
|
||||
if (partial && levelHash != LastLevelHash)
|
||||
{
|
||||
TENLog("Level file has changed since the last load; fast reload is not possible.", LogLevel::Warning);
|
||||
partial = false;
|
||||
FreeLevel(false); // Erase all precached data.
|
||||
}
|
||||
|
||||
// Store information about last loaded level file.
|
||||
LastLevelFilePath = path;
|
||||
LastLevelHash = levelHash;
|
||||
LastLevelTimestamp = std::filesystem::last_write_time(path);
|
||||
|
||||
TENLog("Level compiler version: " + std::to_string(version[0]) + "." + std::to_string(version[1]) + "." + std::to_string(version[2]), LogLevel::Info);
|
||||
|
||||
|
@ -1225,95 +1305,105 @@ bool LoadLevel(int levelIndex)
|
|||
SystemNameHash = 0;
|
||||
}
|
||||
|
||||
// Read data sizes
|
||||
ReadFileEx(&uncompressedSize, 1, 4, filePtr);
|
||||
ReadFileEx(&compressedSize, 1, 4, filePtr);
|
||||
if (partial)
|
||||
{
|
||||
TENLog("Loading same level. Skipping media and geometry data.", LogLevel::Info);
|
||||
SetScreenFadeOut(FADE_SCREEN_SPEED * 2, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetScreenFadeIn(FADE_SCREEN_SPEED, true);
|
||||
}
|
||||
|
||||
// The entire level is ZLIB compressed
|
||||
auto compressedBuffer = (char*)malloc(compressedSize);
|
||||
dataPtr = (char*)malloc(uncompressedSize);
|
||||
LevelDataPtr = dataPtr;
|
||||
UpdateProgress(0);
|
||||
|
||||
ReadFileEx(compressedBuffer, compressedSize, 1, filePtr);
|
||||
Decompress((byte*)LevelDataPtr, (byte*)compressedBuffer, compressedSize, uncompressedSize);
|
||||
// Media block
|
||||
if (ReadCompressedBlock(filePtr, partial))
|
||||
{
|
||||
LoadTextures();
|
||||
UpdateProgress(30);
|
||||
|
||||
// Now the entire level is decompressed, we can close it
|
||||
free(compressedBuffer);
|
||||
FileClose(filePtr);
|
||||
filePtr = nullptr;
|
||||
LoadSamples();
|
||||
UpdateProgress(40);
|
||||
|
||||
LoadTextures();
|
||||
FinalizeBlock();
|
||||
}
|
||||
|
||||
g_Renderer.UpdateProgress(20);
|
||||
// Geometry block
|
||||
if (ReadCompressedBlock(filePtr, partial))
|
||||
{
|
||||
LoadRooms();
|
||||
UpdateProgress(50);
|
||||
|
||||
LoadRooms();
|
||||
g_Renderer.UpdateProgress(40);
|
||||
LoadObjects();
|
||||
UpdateProgress(60);
|
||||
|
||||
LoadObjects();
|
||||
g_Renderer.UpdateProgress(50);
|
||||
LoadSprites();
|
||||
LoadBoxes();
|
||||
LoadAnimatedTextures();
|
||||
UpdateProgress(70);
|
||||
|
||||
LoadSprites();
|
||||
LoadCameras();
|
||||
LoadSoundSources();
|
||||
g_Renderer.UpdateProgress(60);
|
||||
FinalizeBlock();
|
||||
}
|
||||
|
||||
LoadBoxes();
|
||||
// Dynamic data block
|
||||
if (ReadCompressedBlock(filePtr, false))
|
||||
{
|
||||
LoadDynamicRoomData();
|
||||
LoadItems();
|
||||
LoadAIObjects();
|
||||
LoadCameras();
|
||||
LoadSoundSources();
|
||||
LoadEventSets();
|
||||
UpdateProgress(80, partial);
|
||||
|
||||
//InitializeLOTarray(true);
|
||||
|
||||
LoadAnimatedTextures();
|
||||
g_Renderer.UpdateProgress(70);
|
||||
|
||||
LoadItems();
|
||||
LoadAIObjects();
|
||||
|
||||
LoadEventSets();
|
||||
|
||||
LoadSamples();
|
||||
g_Renderer.UpdateProgress(80);
|
||||
FinalizeBlock();
|
||||
}
|
||||
|
||||
TENLog("Initializing level...", LogLevel::Info);
|
||||
|
||||
// Initialize the game
|
||||
// Initialize game.
|
||||
InitializeGameFlags();
|
||||
InitializeLara(!InitializeGame && CurrentLevel > 0);
|
||||
InitializeNeighborRoomList();
|
||||
GetCarriedItems();
|
||||
GetAIPickups();
|
||||
g_GameScriptEntities->AssignLara();
|
||||
g_Renderer.UpdateProgress(90);
|
||||
UpdateProgress(90, partial);
|
||||
|
||||
TENLog("Preparing renderer...", LogLevel::Info);
|
||||
if (!partial)
|
||||
{
|
||||
g_Renderer.PrepareDataForTheRenderer();
|
||||
SetScreenFadeOut(FADE_SCREEN_SPEED, true);
|
||||
StopSoundTracks(SOUND_XFADETIME_BGM_START);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetScreenFadeIn(FADE_SCREEN_SPEED, true);
|
||||
StopSoundTracks(SOUND_XFADETIME_LEVELJUMP);
|
||||
}
|
||||
|
||||
g_Renderer.PrepareDataForTheRenderer();
|
||||
UpdateProgress(100, partial);
|
||||
|
||||
TENLog("Level loading complete.", LogLevel::Info);
|
||||
|
||||
SetScreenFadeOut(FADE_SCREEN_SPEED, true);
|
||||
g_Renderer.UpdateProgress(100);
|
||||
|
||||
LoadedSuccessfully = true;
|
||||
loadedSuccessfully = true;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
if (filePtr)
|
||||
{
|
||||
FileClose(filePtr);
|
||||
filePtr = nullptr;
|
||||
}
|
||||
FinalizeBlock();
|
||||
StopSoundTracks(SOUND_XFADETIME_LEVELJUMP);
|
||||
|
||||
TENLog("Error while loading level: " + std::string(ex.what()), LogLevel::Error);
|
||||
LoadedSuccessfully = false;
|
||||
loadedSuccessfully = false;
|
||||
SystemNameHash = 0;
|
||||
}
|
||||
|
||||
if (dataPtr)
|
||||
{
|
||||
free(dataPtr);
|
||||
dataPtr = LevelDataPtr = nullptr;
|
||||
}
|
||||
// Now the entire level is decompressed, we can close it
|
||||
FileClose(filePtr);
|
||||
filePtr = nullptr;
|
||||
|
||||
return LoadedSuccessfully;
|
||||
return loadedSuccessfully;
|
||||
}
|
||||
|
||||
void LoadSamples()
|
||||
|
@ -1386,7 +1476,7 @@ void LoadBoxes()
|
|||
int excessiveZoneGroups = numZoneGroups - j + 1;
|
||||
TENLog("Level file contains extra pathfinding data, number of excessive zone groups is " +
|
||||
std::to_string(excessiveZoneGroups) + ". These zone groups will be ignored.", LogLevel::Warning);
|
||||
LevelDataPtr += numBoxes * sizeof(int);
|
||||
CurrentDataPtr += numBoxes * sizeof(int);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1406,13 +1496,35 @@ void LoadBoxes()
|
|||
|
||||
bool LoadLevelFile(int levelIndex)
|
||||
{
|
||||
TENLog("Loading level file...", LogLevel::Info);
|
||||
const auto& level = *g_GameFlow->GetLevel(levelIndex);
|
||||
|
||||
auto assetDir = g_GameFlow->GetGameDir();
|
||||
auto levelPath = assetDir + level.FileName;
|
||||
|
||||
if (!std::filesystem::is_regular_file(levelPath))
|
||||
{
|
||||
TENLog("Level file not found: " + levelPath, LogLevel::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
TENLog("Loading level file: " + levelPath, LogLevel::Info);
|
||||
|
||||
auto timestamp = std::filesystem::last_write_time(levelPath);
|
||||
bool fastReload = (g_GameFlow->GetSettings()->FastReload && levelIndex == CurrentLevel && timestamp == LastLevelTimestamp && levelPath == LastLevelFilePath);
|
||||
|
||||
// Dumping game scene right after engine launch is impossible, as no scene exists.
|
||||
if (!FirstLevel && fastReload)
|
||||
g_Renderer.DumpGameScene();
|
||||
|
||||
auto loadingScreenPath = TEN::Utils::ToWString(assetDir + level.LoadScreenFileName);
|
||||
g_Renderer.SetLoadingScreen(fastReload ? std::wstring{} : loadingScreenPath);
|
||||
|
||||
BackupLara();
|
||||
StopAllSounds();
|
||||
CleanUp();
|
||||
FreeLevel();
|
||||
FreeLevel(fastReload);
|
||||
|
||||
LevelLoadTask = std::async(std::launch::async, LoadLevel, levelIndex);
|
||||
LevelLoadTask = std::async(std::launch::async, LoadLevel, levelPath, fastReload);
|
||||
|
||||
return LevelLoadTask.get();
|
||||
}
|
||||
|
|
|
@ -140,6 +140,8 @@ extern std::vector<int> MoveablesIds;
|
|||
extern std::vector<int> StaticObjectsIds;
|
||||
extern std::vector<int> SpriteSequencesIds;
|
||||
extern LEVEL g_Level;
|
||||
extern int SystemNameHash;
|
||||
extern int LastLevelHash;
|
||||
|
||||
inline std::future<bool> LevelLoadTask;
|
||||
|
||||
|
@ -149,7 +151,7 @@ void FileClose(FILE* ptr);
|
|||
bool Decompress(byte* dest, byte* src, unsigned long compressedSize, unsigned long uncompressedSize);
|
||||
|
||||
bool LoadLevelFile(int levelIndex);
|
||||
void FreeLevel();
|
||||
void FreeLevel(bool partial);
|
||||
|
||||
void LoadTextures();
|
||||
void LoadRooms();
|
||||
|
|
|
@ -7017,6 +7017,7 @@ flatbuffers::Offset<UnionVec> CreateUnionVec(flatbuffers::FlatBufferBuilder &_fb
|
|||
struct SaveGameHeaderT : public flatbuffers::NativeTable {
|
||||
typedef SaveGameHeader TableType;
|
||||
std::string level_name{};
|
||||
int32_t level_hash = 0;
|
||||
int32_t days = 0;
|
||||
int32_t hours = 0;
|
||||
int32_t minutes = 0;
|
||||
|
@ -7032,17 +7033,21 @@ struct SaveGameHeader FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
struct Traits;
|
||||
enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
|
||||
VT_LEVEL_NAME = 4,
|
||||
VT_DAYS = 6,
|
||||
VT_HOURS = 8,
|
||||
VT_MINUTES = 10,
|
||||
VT_SECONDS = 12,
|
||||
VT_LEVEL = 14,
|
||||
VT_TIMER = 16,
|
||||
VT_COUNT = 18
|
||||
VT_LEVEL_HASH = 6,
|
||||
VT_DAYS = 8,
|
||||
VT_HOURS = 10,
|
||||
VT_MINUTES = 12,
|
||||
VT_SECONDS = 14,
|
||||
VT_LEVEL = 16,
|
||||
VT_TIMER = 18,
|
||||
VT_COUNT = 20
|
||||
};
|
||||
const flatbuffers::String *level_name() const {
|
||||
return GetPointer<const flatbuffers::String *>(VT_LEVEL_NAME);
|
||||
}
|
||||
int32_t level_hash() const {
|
||||
return GetField<int32_t>(VT_LEVEL_HASH, 0);
|
||||
}
|
||||
int32_t days() const {
|
||||
return GetField<int32_t>(VT_DAYS, 0);
|
||||
}
|
||||
|
@ -7068,6 +7073,7 @@ struct SaveGameHeader FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
|
|||
return VerifyTableStart(verifier) &&
|
||||
VerifyOffset(verifier, VT_LEVEL_NAME) &&
|
||||
verifier.VerifyString(level_name()) &&
|
||||
VerifyField<int32_t>(verifier, VT_LEVEL_HASH) &&
|
||||
VerifyField<int32_t>(verifier, VT_DAYS) &&
|
||||
VerifyField<int32_t>(verifier, VT_HOURS) &&
|
||||
VerifyField<int32_t>(verifier, VT_MINUTES) &&
|
||||
|
@ -7089,6 +7095,9 @@ struct SaveGameHeaderBuilder {
|
|||
void add_level_name(flatbuffers::Offset<flatbuffers::String> level_name) {
|
||||
fbb_.AddOffset(SaveGameHeader::VT_LEVEL_NAME, level_name);
|
||||
}
|
||||
void add_level_hash(int32_t level_hash) {
|
||||
fbb_.AddElement<int32_t>(SaveGameHeader::VT_LEVEL_HASH, level_hash, 0);
|
||||
}
|
||||
void add_days(int32_t days) {
|
||||
fbb_.AddElement<int32_t>(SaveGameHeader::VT_DAYS, days, 0);
|
||||
}
|
||||
|
@ -7124,6 +7133,7 @@ struct SaveGameHeaderBuilder {
|
|||
inline flatbuffers::Offset<SaveGameHeader> CreateSaveGameHeader(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
flatbuffers::Offset<flatbuffers::String> level_name = 0,
|
||||
int32_t level_hash = 0,
|
||||
int32_t days = 0,
|
||||
int32_t hours = 0,
|
||||
int32_t minutes = 0,
|
||||
|
@ -7139,6 +7149,7 @@ inline flatbuffers::Offset<SaveGameHeader> CreateSaveGameHeader(
|
|||
builder_.add_minutes(minutes);
|
||||
builder_.add_hours(hours);
|
||||
builder_.add_days(days);
|
||||
builder_.add_level_hash(level_hash);
|
||||
builder_.add_level_name(level_name);
|
||||
return builder_.Finish();
|
||||
}
|
||||
|
@ -7151,6 +7162,7 @@ struct SaveGameHeader::Traits {
|
|||
inline flatbuffers::Offset<SaveGameHeader> CreateSaveGameHeaderDirect(
|
||||
flatbuffers::FlatBufferBuilder &_fbb,
|
||||
const char *level_name = nullptr,
|
||||
int32_t level_hash = 0,
|
||||
int32_t days = 0,
|
||||
int32_t hours = 0,
|
||||
int32_t minutes = 0,
|
||||
|
@ -7162,6 +7174,7 @@ inline flatbuffers::Offset<SaveGameHeader> CreateSaveGameHeaderDirect(
|
|||
return TEN::Save::CreateSaveGameHeader(
|
||||
_fbb,
|
||||
level_name__,
|
||||
level_hash,
|
||||
days,
|
||||
hours,
|
||||
minutes,
|
||||
|
@ -10245,6 +10258,7 @@ inline void SaveGameHeader::UnPackTo(SaveGameHeaderT *_o, const flatbuffers::res
|
|||
(void)_o;
|
||||
(void)_resolver;
|
||||
{ auto _e = level_name(); if (_e) _o->level_name = _e->str(); }
|
||||
{ auto _e = level_hash(); _o->level_hash = _e; }
|
||||
{ auto _e = days(); _o->days = _e; }
|
||||
{ auto _e = hours(); _o->hours = _e; }
|
||||
{ auto _e = minutes(); _o->minutes = _e; }
|
||||
|
@ -10263,6 +10277,7 @@ inline flatbuffers::Offset<SaveGameHeader> CreateSaveGameHeader(flatbuffers::Fla
|
|||
(void)_o;
|
||||
struct _VectorArgs { flatbuffers::FlatBufferBuilder *__fbb; const SaveGameHeaderT* __o; const flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va;
|
||||
auto _level_name = _o->level_name.empty() ? _fbb.CreateSharedString("") : _fbb.CreateString(_o->level_name);
|
||||
auto _level_hash = _o->level_hash;
|
||||
auto _days = _o->days;
|
||||
auto _hours = _o->hours;
|
||||
auto _minutes = _o->minutes;
|
||||
|
@ -10273,6 +10288,7 @@ inline flatbuffers::Offset<SaveGameHeader> CreateSaveGameHeader(flatbuffers::Fla
|
|||
return TEN::Save::CreateSaveGameHeader(
|
||||
_fbb,
|
||||
_level_name,
|
||||
_level_hash,
|
||||
_days,
|
||||
_hours,
|
||||
_minutes,
|
||||
|
|
|
@ -504,6 +504,7 @@ table UnionVec {
|
|||
|
||||
table SaveGameHeader {
|
||||
level_name: string;
|
||||
level_hash: int32;
|
||||
days: int32;
|
||||
hours: int32;
|
||||
minutes: int32;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <array>
|
||||
#include <d3d11.h>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <map>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue