Compare commits

...

25 commits

Author SHA1 Message Date
Marcin Kurczewski
5e3fb42a25 docs: improve release process documentation
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
Publish a pre-release / TR1 (Linux) (push) Waiting to run
Publish a pre-release / TR1 (Windows) (push) Waiting to run
Publish a pre-release / TR1 (Mac) (push) Waiting to run
Publish a pre-release / TR2 (Linux) (push) Waiting to run
Publish a pre-release / TR2 (Windows) (push) Waiting to run
Publish a pre-release / TR2 (Mac) (push) Waiting to run
Publish a pre-release / Create a prerelease (push) Blocked by required conditions
2025-04-28 14:49:50 +02:00
Marcin Kurczewski
6d5bdd89a3 ui: improve modal sizing
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
Publish a pre-release / TR1 (Linux) (push) Waiting to run
Publish a pre-release / TR1 (Windows) (push) Waiting to run
Publish a pre-release / TR1 (Mac) (push) Waiting to run
Publish a pre-release / TR2 (Linux) (push) Waiting to run
Publish a pre-release / TR2 (Windows) (push) Waiting to run
Publish a pre-release / TR2 (Mac) (push) Waiting to run
Publish a pre-release / Create a prerelease (push) Blocked by required conditions
2025-04-28 11:46:57 +02:00
lahm86
a859d668f9 lara: test alignment position height validity in TR1
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
Publish a pre-release / TR1 (Windows) (push) Waiting to run
Publish a pre-release / TR1 (Mac) (push) Waiting to run
Publish a pre-release / TR2 (Linux) (push) Waiting to run
Publish a pre-release / TR2 (Windows) (push) Waiting to run
Publish a pre-release / Create a prerelease (push) Blocked by required conditions
Publish a pre-release / TR1 (Linux) (push) Waiting to run
Publish a pre-release / TR2 (Mac) (push) Waiting to run
This applies a fix introduced in TR2 to TR1 so that Lara doesn't become
clamped under a steeply sloped ceiling if picking up an item there.

Resolves #2879.
2025-04-27 21:53:55 +01:00
lahm86
e03c65ca0f lara/common: move Lara_AlignPosition to TRX
This merges Lara_AlignPosition (Item_AlignPosition) into TRX.
2025-04-27 18:57:03 +01:00
lahm86
ff86b5e712 tr2/objects: use common OBJECT_BOUNDS
This updates all relevant TR2 objects to use OBJECT_BOUNDS rather than
an arbitrary array of values for testing bounds. The approach is
equivalent to TR1. Lara_TestPosition has also been moved to TRX, and
kept in Lara's module rather than in items.c, as it's only used by
Lara.
2025-04-27 18:57:03 +01:00
lahm86
cdfb5942c1 tr2/data: fix Living Quarters flame deactivation
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
Publish a pre-release / TR2 (Linux) (push) Waiting to run
Publish a pre-release / TR2 (Windows) (push) Waiting to run
Publish a pre-release / TR2 (Mac) (push) Waiting to run
Publish a pre-release / Create a prerelease (push) Blocked by required conditions
Publish a pre-release / TR1 (Linux) (push) Waiting to run
Publish a pre-release / TR1 (Windows) (push) Waiting to run
Publish a pre-release / TR1 (Mac) (push) Waiting to run
Resolves #2851.
2025-04-27 15:03:53 +01:00
lahm86
8b78a7f001 room: fix abyss height check
As Lara's position is stored as int32, we need to use the same in the
abyss height check function, otherwise internal casting yields invalid
results.

Resolves #2874.
2025-04-27 13:30:38 +01:00
Marcin Kurczewski
2cd998673f
Merge branch 'stable' into develop
Some checks are pending
Publish a pre-release / TR2 (Windows) (push) Waiting to run
Run code linters / Run code linters (push) Waiting to run
Publish a pre-release / TR1 (Linux) (push) Waiting to run
Publish a pre-release / TR1 (Windows) (push) Waiting to run
Publish a pre-release / TR1 (Mac) (push) Waiting to run
Publish a pre-release / TR2 (Linux) (push) Waiting to run
Publish a pre-release / TR2 (Mac) (push) Waiting to run
Publish a pre-release / Create a prerelease (push) Blocked by required conditions
2025-04-26 19:57:08 +02:00
Marcin Kurczewski
a7269bbe8a game-strings: fix memory leak 2025-04-26 20:53:15 +03:00
Marcin Kurczewski
f27d0435a9 game-strings: fix crash with -l/--level 2025-04-26 20:53:15 +03:00
Marcin Kurczewski
88f41c5a75 tr2/inventory: fix button mashing loading game instead of saving
Resolves #2863.
2025-04-26 20:43:42 +03:00
Marcin Kurczewski
0ba717edd5 audio: fix clicks in sample decoding
Resolves #2846.
2025-04-26 20:17:21 +03:00
Marcin Kurczewski
3696e84925 audio: split into smaller functions 2025-04-26 20:17:21 +03:00
Marcin Kurczewski
e439371c66 audio: fix wrong benchmark times
The decoder benchmark was formatting the wrong variable and produced
meaningless results in the logs.
2025-04-26 20:17:21 +03:00
Marcin Kurczewski
92ff6e12c7 game-strings: use OG JSON as a fallback in expansions
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
Publish a pre-release / TR1 (Linux) (push) Waiting to run
Publish a pre-release / TR1 (Windows) (push) Waiting to run
Publish a pre-release / TR1 (Mac) (push) Waiting to run
Publish a pre-release / TR2 (Linux) (push) Waiting to run
Publish a pre-release / TR2 (Windows) (push) Waiting to run
Publish a pre-release / TR2 (Mac) (push) Waiting to run
Publish a pre-release / Create a prerelease (push) Blocked by required conditions
Resolves #2847.
2025-04-26 17:55:25 +03:00
Marcin Kurczewski
73da4c2945 shell: do not create logs with --help 2025-04-26 17:53:26 +03:00
Marcin Kurczewski
dc41197cd6 shell: improve support for CLI options
- Adds documentation
- Adds a --help switch
- Tidies short/long option form conventions
2025-04-26 17:53:26 +03:00
lahm86
890c7f76bb door: move door module to TRX
This moves the door module fully to TRX as the logic is identical in
both games.
2025-04-26 12:39:42 +01:00
lahm86
83ac9514cb tr2/objects/door: prevent Lara voiding in closed doors
This uses the same approach as TR1 to avoid Lara voiding in closing/
closed doors that are not placed on portals.

Resovles #2848.
2025-04-26 12:39:42 +01:00
Marcin Kurczewski
96b86b1605
docs/tr2: fix changelog merge mistake 2025-04-26 13:18:45 +02:00
Marcin Kurczewski
607ac811f0 tr2/viewport: fix screenshots at wrong resolution
Resolves #2845.
2025-04-26 14:07:54 +03:00
lahm86
15b758c57d items: replace items by index rather than room
Carried items use NO_ROOM so were not included when replacing guns
with ammo. This ensures everything is checked when replacing IDs.

Resolves #2850.
Resolves #2856.
2025-04-26 11:34:52 +01:00
Marcin Kurczewski
eec8f16d5f tools: download GM assets too 2025-04-26 13:06:08 +03:00
Marcin Kurczewski
2536ff55c1 docs: fix formatting and mistyped option name 2025-04-26 12:26:50 +03:00
Marcin Kurczewski
10b9bcc780 option: port sound settings dialog to ui
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
Publish a pre-release / Create a prerelease (push) Blocked by required conditions
Publish a pre-release / TR2 (Windows) (push) Waiting to run
Publish a pre-release / TR2 (Mac) (push) Waiting to run
Publish a pre-release / TR1 (Linux) (push) Waiting to run
Publish a pre-release / TR1 (Windows) (push) Waiting to run
Publish a pre-release / TR1 (Mac) (push) Waiting to run
Publish a pre-release / TR2 (Linux) (push) Waiting to run
2025-04-26 10:31:36 +03:00
65 changed files with 817 additions and 865 deletions

7
.gitignore vendored
View file

@ -38,4 +38,11 @@ src/tr2/subprojects/dwarfstack.wrap
data/tr1/ship/data/images/
data/tr2/ship/data/images/
data/tr2/ship/data/level1.tr2
data/tr2/ship/data/level2.tr2
data/tr2/ship/data/level3.tr2
data/tr2/ship/data/level4.tr2
data/tr2/ship/data/level5.tr2
data/tr2/ship/data/main_gm.sfx
data/tr2/ship/data/title_gm.tr2
data/tr2/ship/music/

View file

@ -531,7 +531,9 @@
"PHOTO_MODE_ROTATE_PROMPT": "Rotate camera",
"PHOTO_MODE_SNAP_PROMPT": "Take picture",
"PHOTO_MODE_TITLE": "Photo Mode",
"SOUND_SET_VOLUMES": "Set Volumes",
"SOUND_DIALOG_MUSIC": "\\{icon sound} Sound",
"SOUND_DIALOG_SOUND": "\\{icon music} Music",
"SOUND_DIALOG_TITLE": "Set Volumes",
"STATS_AMMO": "AMMO HITS/USED",
"STATS_BASIC_FMT": "%d",
"STATS_BONUS_STATISTICS": "Bonus Statistics",

View file

@ -217,6 +217,7 @@
],
"injections": [
"data/injections/living_deck_goon_sfx.bin",
"data/injections/living_fd.bin",
"data/injections/living_pickup_meshes.bin",
"data/injections/seaweed_collision.bin",
],

View file

@ -655,7 +655,9 @@
"PHOTO_MODE_ROTATE_PROMPT": "Rotate camera",
"PHOTO_MODE_SNAP_PROMPT": "Take picture",
"PHOTO_MODE_TITLE": "Photo Mode",
"SOUND_SET_VOLUMES": "Set Volumes",
"SOUND_DIALOG_MUSIC": "\\{icon sound} Sound",
"SOUND_DIALOG_SOUND": "\\{icon music} Music",
"SOUND_DIALOG_TITLE": "Set Volumes",
"STATS_AMMO_HITS": "Hits",
"STATS_AMMO_USED": "Ammo Used",
"STATS_ASSAULT_FINISH": "Finish",

Binary file not shown.

20
docs/COMMAND_LINE.md Normal file
View file

@ -0,0 +1,20 @@
# Command line options
Currently the following command line interface options are available:
`-g/--gold` (legacy: `-gold`):
Runs the Unfinished Business or the Golden Mask expansion pack, depending
on the game.
`--demo-pc` (TR1X only, legacy: `-demo_pc`):
Runs the PC demo level.
`-l/--level <path>`:
Runs the game immediately launching it into the specified level.
The path should be absolute. Internally, this option uses
`TR*X_gameflow_level.json5` as a template instructing it how to run the
game.
`-s/--save <num>`:
Runs the game immediately loading a specific save slot. The first save
starts at `num=1`. This option can be combined with `-l/--level`.

View file

@ -266,30 +266,63 @@ request number, but it's important to carefully review the body field, as it
often includes unwanted content.
### Branching model
We have two branches: `develop` and `stable`. `develop` is where all changes
about to be published in the next release land. `stable` is the latest release.
We avoid creating merge commits between these two they should always point to
the same HEAD when applicable. This means that any hotfixes that need to be
released ahead of unpublished work in `develop` are merged directly to
`stable`, and `develop` needs to be then rebased on top of the now-patched
`stable`.
### Tooling
Internal tools are typically coded in a reasonably recent version of Python,
while avoiding the use of bash, shell, and similar languages.
### Branching model
We have two branches: `develop` and `stable`. `develop` is where all changes
about to be published in the next release land. `stable` is the latest release.
### Releasing a new version
New version releases happen automatically whenever a new tag is pushed to the
`stable` branch with the help of GitHub actions. In general this is accompanied
with a special commit `docs: release X.Y.Z` that also adjusts the changelog.
See git history for details.
New version releases are published automatically whenever a new tag is pushed
to the `stable` branch with the help of GitHub actions.
The general workflow is this:
```console
TR_VERSION=...
RELEASE_VERSION=...
# Switch to the stable branch.
git checkout stable
# Merge `develop` into it.
git merge develop
# Create a special commit `docs: release X.Y.Z` marking the release in the
# relevant changelog file. Then tag it with `tr1-X.Y.Z` or `tr2-X.Y.Z`.
# You can do that by hand, or run the command below:
tools/release commit ${TR_VERSION} ${RELEASE_VERSION}
# Review the changelog content.
# Switch back to develop.
git checkout develop
# Merge stable using fast-forward.
git merge --ff stable
# Review both branches and changes. If everything is okay, push to GitHub.
# You can do this by hand: git push origin develop stable tr1-X.Y.Z, or:
# tools/release push ${TR_VERSION} ${RELEASE_VERSION}
```
### Hotfixes
Hotfix releases are a bit different as we try to not include non-bugfix changes
in them. Here instead of merging `develop` to `stable` we cherry-pick relevant
changes, resolving conflicts along the way.
### Versioning
We increase the major version for significant releases based on judgment,
typically defaulting to increasing the minor version. Hotfixes increase the
patch version.
## Glossary

View file

@ -5,12 +5,15 @@
- added support for antitriggers, like TR2+ (#2580)
- added support for aspect ratio-specific images (#1840)
- added an option to wraparound when scrolling UI dialogs, such as save/load (#2834)
- added aliases to CLI options (`-gold` becomes `-g/--gold`, `-demo_pc` becomes `--demo-pc`)
- added a `--help` CLI option (may not output anything on Windows machines  OS bug)
- changed the `draw_distance_fade` and `draw_distance_max` to `fog_start` and `fog_end`
- changed `Select Detail` dialog title to `Graphic Options`
- changed the number of static mesh slots from 50 to 256 (#2734)
- changed the "enable EIDOS logo" option to disable the Core Design and Bink Video Codec FMVs as well; renamed to "enable legal" (#2741)
- changed sprite pickups to respect the water tint if placed underwater (#2673)
- changed save to take priority over load when both inputs are held on the same frame, in line with OG (#2833)
- changed the sound dialog appearance (repositioned and added text labels)
- changed The Unfinished Business strings to default to the OG strings file for the main tables (#2847)
- fixed the bilinear filter to not readjust the UVs (#2258)
- fixed disabling the cutscenes causing the game to exit (#2743, regression from 4.8)
@ -22,6 +25,7 @@
- fixed the scale of the four keys in St. Francis' Folly (#2652)
- fixed the panther at times not making a sound when it dies, and restored Skate Kid's death SFX (#2647)
- fixed pushblocks being rotated when Lara grabs them, most noticeable if asymmetric textures have been used (#2776)
- fixed Lara becoming clamped if she picks up an item under a steeply sloped ceiling (#2879)
- fixed a crash when 3D pickups are disabled and Lara crosses a trigger to look at a pickup item (#2711, regression from 4.8)
- fixed trapezoid filter warping on faces close to the camera (#2629, regression from 4.9)
- fixed Mac builds crashing upon start (regression from 4.9)
@ -37,6 +41,7 @@
- fixed Lara at times ending up in incorrect rooms when using the teleport cheat (#2486, regression from 3.0)
- fixed the `/pos` console command reporting the base room number when Lara is actually in a flipped room (#2487, regression from 3.0)
- fixed clicks in audio sounds (#2846, regression from 2.0)
- fixed Lara being killed if she enters the void in a level that uses the `disable_floor` sequence in the game flow (#2874, regression from 4.9)
- improved bubble appearance (#2672)
- improved rendering performance
- improved pause exit dialog - it can now be canceled with escape

View file

@ -485,6 +485,7 @@ Not all options are turned on by default. Refer to `TR1X_ConfigTool.exe` for det
- fixed being able to shoot the scion multiple times if save/load is used while it blows up
- fixed the game crashing if a cinematic is triggered but the level contains no cinematic frames
- fixed pushblocks being rotated when Lara grabs them, most noticeable if asymmetric textures have been used
- fixed Lara becoming clamped if she picks up an item under a steeply sloped ceiling
#### Cheats
- added a fly cheat

View file

@ -1,4 +1,9 @@
## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr2-1.0.2...develop) - ××××-××-××
## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr2-1.0.1...develop) - ××××-××-××
- added aliases to CLI options (`-gold` becomes `-g/--gold`)
- added a `--help` CLI option (may not output anything on Windows machines  OS bug)
- changed the sound dialog appearance (repositioned, added text labels and arrows)
- fixed Lara being killed if she enters the void in a level that uses the `disable_floor` sequence in the game flow (#2874, regression from 0.10)
- fixed flame emitter 23 in room 6 not being deactivated when the lever in room 1 is used (#2851)
## [1.0.2](https://github.com/LostArtefacts/TRX/compare/tr2-1.0.1...tr2-1.0.2) - 2025-04-26
- changed The Golden Mask strings to default to the OG strings file for the main tables (#2847)

View file

@ -221,6 +221,7 @@ However, you can easily download them manually from these urls:
- fixed the following floor data issues:
- **Opera House**: fixed the trigger under item 203 to trigger it rather than item 204
- **Wreck of the Maria Doria**: fixed room 98 not having water
- **Living Quarters** - fixed flame emitter 23 in room 6 not being deactivated when the lever in room 1 is used
- **The Deck**: fixed invalid portals between rooms 17 and 104, which could result in Lara seeing enemies in disconnected rooms
- **Tibetan Foothills**: added missing triggers for the drawbridge in room 96 (after the flipmap)
- **Catacombs of the Talion**: changed some music triggers to pads near the first yeti, and added missing triggers and ladder in room 116 (after the flipmap)

View file

@ -2,7 +2,9 @@
#include "game/const.h"
#include "game/item_actions.h"
#include "game/rooms/const.h"
#include "game/lara/const.h"
#include "game/matrix.h"
#include "game/rooms.h"
void Lara_Animate(ITEM *const item)
{
@ -156,3 +158,88 @@ bool Lara_TestBoundsCollide(const ITEM *const item, const int32_t radius)
{
return Item_TestBoundsCollide(item, Lara_GetItem(), radius);
}
bool Lara_TestPosition(
const ITEM *const item, const OBJECT_BOUNDS *const bounds)
{
const ITEM *const lara = Lara_GetItem();
const XYZ_16 rot = {
.x = lara->rot.x - item->rot.x,
.y = lara->rot.y - item->rot.y,
.z = lara->rot.z - item->rot.z,
};
const XYZ_32 dist = {
.x = lara->pos.x - item->pos.x,
.y = lara->pos.y - item->pos.y,
.z = lara->pos.z - item->pos.z,
};
// clang-format off
if (rot.x < bounds->rot.min.x ||
rot.x > bounds->rot.max.x ||
rot.y < bounds->rot.min.y ||
rot.y > bounds->rot.max.y ||
rot.z < bounds->rot.min.z ||
rot.z > bounds->rot.max.z
) {
return false;
}
// clang-format on
Matrix_PushUnit();
Matrix_Rot16(item->rot);
const MATRIX *const m = g_MatrixPtr;
const XYZ_32 shift = {
.x = (dist.x * m->_00 + dist.y * m->_10 + dist.z * m->_20) >> W2V_SHIFT,
.y = (dist.x * m->_01 + dist.y * m->_11 + dist.z * m->_21) >> W2V_SHIFT,
.z = (dist.x * m->_02 + dist.y * m->_12 + dist.z * m->_22) >> W2V_SHIFT,
};
Matrix_Pop();
// clang-format off
return (
shift.x >= bounds->shift.min.x &&
shift.x <= bounds->shift.max.x &&
shift.y >= bounds->shift.min.y &&
shift.y <= bounds->shift.max.y &&
shift.z >= bounds->shift.min.z &&
shift.z <= bounds->shift.max.z
);
// clang-format on
}
void Lara_AlignPosition(const ITEM *const item, const XYZ_32 *const vec)
{
ITEM *const lara = Lara_GetItem();
lara->rot = item->rot;
Matrix_PushUnit();
Matrix_Rot16(item->rot);
const MATRIX *const m = g_MatrixPtr;
const XYZ_32 shift = {
.x = (vec->x * m->_00 + vec->y * m->_01 + vec->z * m->_02) >> W2V_SHIFT,
.y = (vec->x * m->_10 + vec->y * m->_11 + vec->z * m->_12) >> W2V_SHIFT,
.z = (vec->x * m->_20 + vec->y * m->_21 + vec->z * m->_22) >> W2V_SHIFT,
};
Matrix_Pop();
const XYZ_32 new_pos = {
.x = item->pos.x + shift.x,
.y = item->pos.y + shift.y,
.z = item->pos.z + shift.z,
};
int16_t room_num = lara->room_num;
const SECTOR *const sector =
Room_GetSector(new_pos.x, new_pos.y, new_pos.z, &room_num);
const int32_t height =
Room_GetHeight(sector, new_pos.x, new_pos.y, new_pos.z);
const int32_t ceiling =
Room_GetCeiling(sector, new_pos.x, new_pos.y, new_pos.z);
if (ABS(height - lara->pos.y) > STEP_L
|| ABS(ceiling - lara->pos.y) < LARA_HEIGHT) {
return;
}
lara->pos = new_pos;
}

View file

@ -2,6 +2,16 @@
static uint16_t m_MusicTrackFlags[MAX_MUSIC_TRACKS] = {};
int32_t Music_GetMinVolume(void)
{
return 0;
}
int32_t Music_GetMaxVolume(void)
{
return 10;
}
void Music_ResetTrackFlags(void)
{
for (int32_t i = 0; i < MAX_MUSIC_TRACKS; i++) {

View file

@ -550,7 +550,7 @@ void Room_SetAbyssHeight(const int16_t height)
CLAMPG(m_AbyssMaxHeight, MAX_HEIGHT - STEP_L);
}
bool Room_IsAbyssHeight(const int16_t height)
bool Room_IsAbyssHeight(const int32_t height)
{
return m_AbyssMinHeight != 0 && height >= m_AbyssMinHeight;
}

View file

@ -6,28 +6,18 @@
#include <string.h>
static int m_ArgCount = 0;
static const char **m_ArgStrings = nullptr;
void Shell_GetCommandLine(int *arg_count, const char ***args)
{
*arg_count = m_ArgCount;
*args = m_ArgStrings;
}
int main(int argc, char *argv[])
{
if (!Shell_ParseArgs(argc, (const char **)argv)) {
return 0;
}
char *log_path = File_GetFullPath(PROJECT_NAME ".log");
Log_Init(log_path);
Memory_FreePointer(&log_path);
LOG_INFO("Game directory: %s", File_GetGameDirectory());
m_ArgCount = argc;
m_ArgStrings = (const char **)argv;
Shell_Setup();
Shell_Main();
Shell_Terminate(0);
return 0;
int32_t exit_code = Shell_Main();
Shell_Terminate(exit_code);
return exit_code;
}

View file

@ -4,8 +4,10 @@
#include "debug.h"
#include "game/console/common.h"
#include "game/game_string.h"
#include "game/scaler.h"
#include "game/ui/elements/anchor.h"
#include "game/ui/events.h"
#include "game/viewport.h"
#include "memory.h"
#include <SDL2/SDL.h>
@ -201,3 +203,23 @@ void UI_HandleTextEdit(const char *const text)
UI_FireEvent((EVENT) {
.name = "text_edit", .sender = nullptr, .data = (void *)text });
}
int32_t UI_GetCanvasWidth(void)
{
return Scaler_CalcInverse(Viewport_GetWidth(), SCALER_TARGET_GENERIC);
}
int32_t UI_GetCanvasHeight(void)
{
return Scaler_CalcInverse(Viewport_GetHeight(), SCALER_TARGET_GENERIC);
}
float UI_ScaleX(const float x)
{
return Scaler_Calc(x, SCALER_TARGET_GENERIC);
}
float UI_ScaleY(const float y)
{
return Scaler_Calc(y, SCALER_TARGET_GENERIC);
}

View file

@ -21,7 +21,7 @@ void UI_PhotoMode(void)
char tmp[50];
UI_BeginModal(0.0f, 0.0f);
UI_BeginPad(8.0f, 10.0f);
UI_BeginPad(8.0f, 8.0f);
UI_BeginFrame(UI_FRAME_DIALOG_BACKGROUND);
UI_BeginPad(8.0, 6.0);

View file

@ -0,0 +1,176 @@
#include "game/ui/dialogs/sound_settings.h"
#include "config.h"
#include "game/game_string.h"
#include "game/input.h"
#include "game/music.h"
#include "game/sound.h"
#include "game/ui/elements/anchor.h"
#include "game/ui/elements/hide.h"
#include "game/ui/elements/label.h"
#include "game/ui/elements/modal.h"
#include "game/ui/elements/requester.h"
#include "game/ui/elements/resize.h"
#include "game/ui/elements/spacer.h"
#include "game/ui/elements/stack.h"
#include "memory.h"
#include "strings.h"
#include "utils.h"
typedef struct UI_SOUND_SETTINGS_STATE {
UI_REQUESTER_STATE req;
} UI_SOUND_SETTINGS_STATE;
typedef enum {
M_ROW_MUSIC = 0,
M_ROW_SOUND = 1,
M_ROW_COUNT = 2,
} M_ROW;
static const GAME_STRING_ID m_Labels[M_ROW_COUNT] = {
GS_ID(SOUND_DIALOG_SOUND),
GS_ID(SOUND_DIALOG_MUSIC),
};
static char *M_FormatRowValue(int32_t row);
static bool M_CanChange(int32_t row, int32_t dir);
static bool M_RequestChange(int32_t row, int32_t dir);
static char *M_FormatRowValue(const int32_t row)
{
switch (row) {
case M_ROW_MUSIC:
return String_Format("%2d", g_Config.audio.music_volume);
case M_ROW_SOUND:
return String_Format("%2d", g_Config.audio.sound_volume);
default:
return nullptr;
}
}
static bool M_CanChange(const int32_t row, const int32_t dir)
{
switch (row) {
case M_ROW_MUSIC:
if (dir < 0) {
return g_Config.audio.music_volume > Music_GetMinVolume();
} else if (dir > 0) {
return g_Config.audio.music_volume < Music_GetMaxVolume();
}
break;
case M_ROW_SOUND:
if (dir < 0) {
return g_Config.audio.sound_volume > Sound_GetMinVolume();
} else if (dir > 0) {
return g_Config.audio.sound_volume < Sound_GetMaxVolume();
}
break;
}
return false;
}
static bool M_RequestChange(const int32_t row, const int32_t dir)
{
if (!M_CanChange(row, dir)) {
return false;
}
switch (row) {
case M_ROW_MUSIC:
g_Config.audio.music_volume += dir;
Music_SetVolume(g_Config.audio.music_volume);
break;
case M_ROW_SOUND:
g_Config.audio.sound_volume += dir;
Sound_SetMasterVolume(g_Config.audio.sound_volume);
break;
default:
return false;
}
Config_Write();
Sound_Effect(SFX_MENU_PASSPORT, nullptr, SPM_ALWAYS);
return true;
}
UI_SOUND_SETTINGS_STATE *UI_SoundSettings_Init(void)
{
UI_SOUND_SETTINGS_STATE *s = Memory_Alloc(sizeof(UI_SOUND_SETTINGS_STATE));
UI_Requester_Init(&s->req, M_ROW_COUNT, M_ROW_COUNT, true);
s->req.row_pad = 2.0f;
return s;
}
void UI_SoundSettings_Free(UI_SOUND_SETTINGS_STATE *const s)
{
UI_Requester_Free(&s->req);
Memory_Free(s);
}
bool UI_SoundSettings_Control(UI_SOUND_SETTINGS_STATE *const s)
{
const int32_t choice = UI_Requester_Control(&s->req);
if (choice == UI_REQUESTER_CANCEL) {
return true;
}
const int32_t sel = UI_Requester_GetCurrentRow(&s->req);
if (g_InputDB.menu_left && sel >= 0) {
M_RequestChange(sel, -1);
} else if (g_InputDB.menu_right && sel >= 0) {
M_RequestChange(sel, +1);
}
return false;
}
void UI_SoundSettings(UI_SOUND_SETTINGS_STATE *const s)
{
const int32_t sel = UI_Requester_GetCurrentRow(&s->req);
UI_BeginModal(0.5f, 0.6f);
UI_BeginRequester(&s->req, GS(SOUND_DIALOG_TITLE));
// Measure the maximum width of the value label to prevent the entire
// dialog from changing its size as the player changes the sound levels.
float value_w = -1.0f;
UI_Label_Measure("10", &value_w, nullptr);
for (int32_t i = 0; i < s->req.max_rows; ++i) {
if (!UI_Requester_IsRowVisible(&s->req, i)) {
UI_BeginResize(-1.0f, 0.0f);
} else {
UI_BeginResize(-1.0f, -1.0f);
}
UI_BeginRequesterRow(&s->req, i);
UI_BeginStackEx((UI_STACK_SETTINGS) {
.orientation = UI_STACK_HORIZONTAL,
.align = { .h = UI_STACK_H_ALIGN_DISTRIBUTE },
});
UI_Label(GameString_Get(m_Labels[i]));
UI_Spacer(20.0f, 0.0f);
UI_BeginStackEx((UI_STACK_SETTINGS) {
.orientation = UI_STACK_HORIZONTAL,
.align = { .h = UI_STACK_H_ALIGN_DISTRIBUTE },
.spacing = { .h = 5.0f },
});
UI_BeginHide(i != sel || !M_CanChange(i, -1));
UI_Label("\\{button left}");
UI_EndHide();
UI_BeginResize(value_w, -1.0f);
UI_BeginAnchor(0.5f, 0.5f);
UI_Label(M_FormatRowValue(i));
UI_EndAnchor();
UI_EndResize();
UI_BeginHide(i != sel || !M_CanChange(i, +1));
UI_Label("\\{button right}");
UI_EndHide();
UI_EndStack();
UI_EndStack();
UI_EndRequesterRow(&s->req, i);
UI_EndResize();
}
UI_EndRequester(&s->req);
UI_EndModal();
}

View file

@ -15,7 +15,7 @@ static const UI_WIDGET_OPS m_Ops = {
static void M_Measure(UI_NODE *const node)
{
node->measure_w = UI_GetCanvasWidth();
node->measure_h = UI_GetCanvasHeight() - TEXT_HEIGHT_FIXED;
node->measure_h = UI_GetCanvasHeight();
}
void UI_BeginModal(const float x, const float y)

View file

@ -114,7 +114,7 @@ static void M_Layout(
int32_t child_count = 0;
float total_child_main_size = 0.0f;
UI_NODE *child = node->first_child;
while (child != NULL) {
while (child != nullptr) {
switch (data->settings.orientation) {
case UI_STACK_HORIZONTAL:
total_child_main_size += child->measure_w;

View file

@ -132,7 +132,9 @@ GS_DEFINE(PASSPORT_MODE_NEW_GAME_JP_PLUS, "Japanese NG+")
GS_DEFINE(PASSPORT_STORY_SO_FAR, "Story so far...")
GS_DEFINE(PASSPORT_LEGACY_SELECT_LEVEL_1, "Legacy saves do not")
GS_DEFINE(PASSPORT_LEGACY_SELECT_LEVEL_2, "support this feature.")
GS_DEFINE(SOUND_SET_VOLUMES, "Set Volumes")
GS_DEFINE(SOUND_DIALOG_TITLE, "Set Volumes")
GS_DEFINE(SOUND_DIALOG_SOUND, "\\{icon music} Music")
GS_DEFINE(SOUND_DIALOG_MUSIC, "\\{icon sound} Sound")
GS_DEFINE(OSD_TRAPEZOID_FILTER_ON, "Trapezoid filter enabled")
GS_DEFINE(OSD_TRAPEZOID_FILTER_OFF, "Trapezoid filter disabled")
GS_DEFINE(DETAIL_INTEGER_FMT, "%d")

View file

@ -15,3 +15,5 @@ void Lara_TakeDamage(int16_t damage, bool hit_status);
bool Lara_TestBoundsCollide(const ITEM *item, int32_t radius);
void Lara_Push(const ITEM *item, COLL_INFO *coll, bool hit_on, bool big_push);
bool Lara_TestPosition(const ITEM *item, const OBJECT_BOUNDS *bounds);
void Lara_AlignPosition(const ITEM *item, const XYZ_32 *vec);

View file

@ -31,3 +31,15 @@ extern void Music_Unpause(void);
void Music_ResetTrackFlags(void);
uint16_t Music_GetTrackFlags(int32_t track_idx);
void Music_SetTrackFlags(int32_t track, uint16_t flags);
// Gets the minimum possible game volume.
extern int32_t Music_GetMinVolume(void);
// Gets the maximum possible game volume.
extern int32_t Music_GetMaxVolume(void);
// Gets the game volume.
extern int32_t Music_GetVolume(void);
// Sets the game volume.
extern void Music_SetVolume(int32_t volume);

View file

@ -38,14 +38,12 @@ typedef struct {
bool disable_lighting;
} OBJECT_MESH;
#if TR_VERSION == 1
typedef struct {
struct {
XYZ_16 min;
XYZ_16 max;
} shift, rot;
} OBJECT_BOUNDS;
#endif
typedef struct OBJECT {
int16_t mesh_count;
@ -66,8 +64,8 @@ typedef struct OBJECT {
void (*activate_func)(ITEM *item);
void (*handle_flip_func)(ITEM *item, ROOM_FLIP_STATUS flip_status);
void (*handle_save_func)(ITEM *item, SAVEGAME_STAGE stage);
#if TR_VERSION == 1
const OBJECT_BOUNDS *(*bounds_func)(void);
#if TR_VERSION == 1
bool (*is_usable_func)(int16_t item_num);
#endif

View file

@ -43,7 +43,7 @@ SECTOR *Room_GetPitSector(const SECTOR *sector, int32_t x, int32_t z);
SECTOR *Room_GetSkySector(const SECTOR *sector, int32_t x, int32_t z);
void Room_SetAbyssHeight(int16_t height);
bool Room_IsAbyssHeight(int16_t height);
bool Room_IsAbyssHeight(int32_t height);
HEIGHT_TYPE Room_GetHeightType(void);
int16_t Room_GetHeight(const SECTOR *sector, int32_t x, int32_t y, int32_t z);
int16_t Room_GetHeightEx(

View file

@ -11,13 +11,13 @@ typedef struct {
extern void Shell_Shutdown(void);
extern SDL_Window *Shell_GetWindow(void);
extern bool Shell_ParseArgs(int32_t arg_count, const char **args);
void Shell_Setup(void);
extern void Shell_Main(void);
extern int32_t Shell_Main(void);
void Shell_Terminate(int32_t exit_code);
void Shell_ExitSystem(const char *message);
void Shell_ExitSystemFmt(const char *fmt, ...);
void Shell_GetCommandLine(int *arg_count, const char ***args);
void Shell_ScheduleExit(void);
bool Shell_IsExiting(void);

View file

@ -14,6 +14,11 @@ int16_t *Sound_GetSampleLUT(void);
SAMPLE_INFO *Sound_GetSampleInfo(SOUND_EFFECT_ID sfx_num);
SAMPLE_INFO *Sound_GetSampleInfoByIdx(int32_t info_idx);
extern int32_t Sound_GetMinVolume(void);
extern int32_t Sound_GetMaxVolume(void);
extern int32_t Sound_GetMasterVolume(void);
extern void Sound_SetMasterVolume(int32_t volume);
void Sound_ResetSources(void);
void Sound_PauseAll(void);
void Sound_UnpauseAll(void);

View file

@ -10,6 +10,7 @@
#include "./ui/dialogs/play_any_level.h"
#include "./ui/dialogs/save_slot.h"
#include "./ui/dialogs/select_level.h"
#include "./ui/dialogs/sound_settings.h"
#include "./ui/dialogs/stats.h"
#include "./ui/elements/anchor.h"
#include "./ui/elements/fade.h"

View file

@ -50,10 +50,10 @@ typedef struct UI_NODE {
// Dimensions in virtual pixels of the screen area
// (640x480 for any 4:3 resolution on 1.00 text scaling)
extern int32_t UI_GetCanvasWidth(void);
extern int32_t UI_GetCanvasHeight(void);
extern float UI_ScaleX(float x);
extern float UI_ScaleY(float y);
int32_t UI_GetCanvasWidth(void);
int32_t UI_GetCanvasHeight(void);
float UI_ScaleX(float x);
float UI_ScaleY(float y);
// Public API for scene management
void UI_BeginScene(void);

View file

@ -0,0 +1,21 @@
// UI dialog for adjusting music and sound volumes
#pragma once
#include "../common.h"
typedef struct UI_SOUND_SETTINGS_STATE UI_SOUND_SETTINGS_STATE;
// state functions
// Initialize the sound settings dialog state
UI_SOUND_SETTINGS_STATE *UI_SoundSettings_Init(void);
// Free resources used by the sound settings dialog
void UI_SoundSettings_Free(UI_SOUND_SETTINGS_STATE *s);
// Handle input/control for the sound settings dialog
// Returns true if the dialog should be closed
bool UI_SoundSettings_Control(UI_SOUND_SETTINGS_STATE *s);
// draw functions
// Render the sound settings dialog
void UI_SoundSettings(UI_SOUND_SETTINGS_STATE *s);

View file

@ -217,6 +217,7 @@ sources = [
'game/ui/dialogs/play_any_level.c',
'game/ui/dialogs/save_slot.c',
'game/ui/dialogs/select_level.c',
'game/ui/dialogs/sound_settings.c',
'game/ui/dialogs/stats.c',
'game/ui/elements/anchor.c',
'game/ui/elements/fade.c',

View file

@ -196,70 +196,6 @@ bool Item_Test3DRange(int32_t x, int32_t y, int32_t z, int32_t range)
&& (SQUARE(x) + SQUARE(y) + SQUARE(z) < SQUARE(range));
}
bool Item_TestPosition(
const ITEM *const src_item, const ITEM *const dst_item,
const OBJECT_BOUNDS *const bounds)
{
const XYZ_16 rot = {
.x = src_item->rot.x - dst_item->rot.x,
.y = src_item->rot.y - dst_item->rot.y,
.z = src_item->rot.z - dst_item->rot.z,
};
if (rot.x < bounds->rot.min.x || rot.x > bounds->rot.max.x
|| rot.y < bounds->rot.min.y || rot.y > bounds->rot.max.y
|| rot.z < bounds->rot.min.z || rot.z > bounds->rot.max.z) {
return false;
}
const XYZ_32 dist = {
.x = src_item->pos.x - dst_item->pos.x,
.y = src_item->pos.y - dst_item->pos.y,
.z = src_item->pos.z - dst_item->pos.z,
};
Matrix_PushUnit();
Matrix_Rot16(dst_item->rot);
MATRIX *mptr = g_MatrixPtr;
const XYZ_32 shift = {
.x = (mptr->_00 * dist.x + mptr->_10 * dist.y + mptr->_20 * dist.z)
>> W2V_SHIFT,
.y = (mptr->_01 * dist.x + mptr->_11 * dist.y + mptr->_21 * dist.z)
>> W2V_SHIFT,
.z = (mptr->_02 * dist.x + mptr->_12 * dist.y + mptr->_22 * dist.z)
>> W2V_SHIFT,
};
Matrix_Pop();
if (shift.x < bounds->shift.min.x || shift.x > bounds->shift.max.x
|| shift.y < bounds->shift.min.y || shift.y > bounds->shift.max.y
|| shift.z < bounds->shift.min.z || shift.z > bounds->shift.max.z) {
return false;
}
return true;
}
void Item_AlignPosition(ITEM *src_item, ITEM *dst_item, XYZ_32 *vec)
{
src_item->rot.x = dst_item->rot.x;
src_item->rot.y = dst_item->rot.y;
src_item->rot.z = dst_item->rot.z;
Matrix_PushUnit();
Matrix_Rot16(dst_item->rot);
MATRIX *mptr = g_MatrixPtr;
src_item->pos.x = dst_item->pos.x
+ ((mptr->_00 * vec->x + mptr->_01 * vec->y + mptr->_02 * vec->z)
>> W2V_SHIFT);
src_item->pos.y = dst_item->pos.y
+ ((mptr->_10 * vec->x + mptr->_11 * vec->y + mptr->_12 * vec->z)
>> W2V_SHIFT);
src_item->pos.z = dst_item->pos.z
+ ((mptr->_20 * vec->x + mptr->_21 * vec->y + mptr->_22 * vec->z)
>> W2V_SHIFT);
Matrix_Pop();
}
bool Item_MovePosition(
ITEM *item, const ITEM *ref_item, const XYZ_32 *vec, int32_t velocity)
{

View file

@ -11,9 +11,6 @@ int16_t Item_Spawn(const ITEM *item, GAME_OBJECT_ID obj_id);
bool Item_IsNearItem(const ITEM *item, const XYZ_32 *pos, int32_t distance);
bool Item_Test3DRange(int32_t x, int32_t y, int32_t z, int32_t range);
bool Item_TestPosition(
const ITEM *src_item, const ITEM *dst_item, const OBJECT_BOUNDS *bounds);
void Item_AlignPosition(ITEM *src_item, ITEM *dst_item, XYZ_32 *vec);
bool Item_MovePosition(
ITEM *src_item, const ITEM *dst_item, const XYZ_32 *vec, int32_t velocity);
void Item_ShiftCol(ITEM *item, COLL_INFO *coll);

View file

@ -701,16 +701,6 @@ bool Lara_IsNearItem(const XYZ_32 *pos, int32_t distance)
return Item_IsNearItem(g_LaraItem, pos, distance);
}
bool Lara_TestPosition(const ITEM *item, const OBJECT_BOUNDS *const bounds)
{
return Item_TestPosition(g_LaraItem, item, bounds);
}
void Lara_AlignPosition(ITEM *item, XYZ_32 *vec)
{
Item_AlignPosition(g_LaraItem, item, vec);
}
bool Lara_MovePosition(ITEM *item, XYZ_32 *vec)
{
int32_t velocity = g_Config.gameplay.enable_walk_to_items

View file

@ -26,8 +26,6 @@ void Lara_SwapMeshExtra(void);
bool Lara_IsNearItem(const XYZ_32 *pos, int32_t distance);
void Lara_UseItem(GAME_OBJECT_ID obj_id);
bool Lara_TestPosition(const ITEM *item, const OBJECT_BOUNDS *bounds);
void Lara_AlignPosition(ITEM *item, XYZ_32 *vec);
bool Lara_MovePosition(ITEM *item, XYZ_32 *vec);
void Lara_RevertToPistolsIfNeeded(void);

View file

@ -182,12 +182,12 @@ void Music_Unmute(void)
M_SyncVolume(m_AudioStreamID);
}
int16_t Music_GetVolume(void)
int32_t Music_GetVolume(void)
{
return m_Volume;
}
void Music_SetVolume(int16_t volume)
void Music_SetVolume(int32_t volume)
{
if (volume != m_Volume) {
m_Volume = volume;
@ -195,16 +195,6 @@ void Music_SetVolume(int16_t volume)
}
}
int16_t Music_GetMinVolume(void)
{
return 0;
}
int16_t Music_GetMaxVolume(void)
{
return 10;
}
void Music_Pause(void)
{
if (m_AudioStreamID < 0) {

View file

@ -19,18 +19,6 @@ void Music_Mute(void);
// Unmutes the game music. Doesn't change the music volume.
void Music_Unmute(void);
// Gets the game volume.
int16_t Music_GetVolume(void);
// Sets the game volume. Value can be 0-10.
void Music_SetVolume(int16_t volume);
// Gets the minimum possible game volume.
int16_t Music_GetMinVolume(void);
// Gets the maximum possible game volume.
int16_t Music_GetMaxVolume(void);
// Returns the currently playing track. Includes looped music.
MUSIC_TRACK_ID Music_GetCurrentPlayingTrack(void);

View file

@ -168,6 +168,9 @@ void Option_Draw(INVENTORY_ITEM *const inv_item)
case O_COMPASS_OPTION:
Option_Compass_Draw();
break;
case O_SOUND_OPTION:
Option_Sound_Draw(inv_item);
break;
case O_PICKUP_OPTION_1:
case O_PICKUP_OPTION_2:

View file

@ -1,170 +1,48 @@
#include "game/option/option_sound.h"
#include "game/game_string.h"
#include "game/input.h"
#include "game/music.h"
#include "game/sound.h"
#include "game/text.h"
#include "global/vars.h"
#include <libtrx/config.h>
#include <libtrx/game/ui.h>
#include <stdio.h>
typedef struct {
UI_SOUND_SETTINGS_STATE *ui;
} M_PRIV;
typedef enum {
TEXT_MUSIC_VOLUME = 0,
TEXT_SOUND_VOLUME = 1,
TEXT_TITLE = 2,
TEXT_TITLE_BORDER = 3,
TEXT_LEFT_ARROW = 4,
TEXT_RIGHT_ARROW = 5,
TEXT_NUMBER_OF = 6,
TEXT_OPTION_MIN = TEXT_MUSIC_VOLUME,
TEXT_OPTION_MAX = TEXT_SOUND_VOLUME,
} SOUND_TEXT;
static M_PRIV m_Priv = {};
static TEXTSTRING *m_Text[TEXT_NUMBER_OF] = {};
static void M_InitText(void);
static void M_InitText(void)
static void M_Init(M_PRIV *const p)
{
char buf[20];
p->ui = UI_SoundSettings_Init();
}
m_Text[TEXT_LEFT_ARROW] = Text_Create(-45, 0, "\\{button left}");
m_Text[TEXT_RIGHT_ARROW] = Text_Create(40, 0, "\\{button right}");
m_Text[TEXT_TITLE_BORDER] = Text_Create(0, -32, " ");
m_Text[TEXT_TITLE] = Text_Create(0, -30, GS(SOUND_SET_VOLUMES));
if (g_Config.audio.music_volume > 10) {
g_Config.audio.music_volume = 10;
}
sprintf(buf, "\\{icon music} %2d", g_Config.audio.music_volume);
m_Text[TEXT_MUSIC_VOLUME] = Text_Create(0, 0, buf);
if (g_Config.audio.sound_volume > 10) {
g_Config.audio.sound_volume = 10;
}
sprintf(buf, "\\{icon sound} %2d", g_Config.audio.sound_volume);
m_Text[TEXT_SOUND_VOLUME] = Text_Create(0, 25, buf);
Text_AddBackground(m_Text[g_OptionSelected], 128, 0, 0, 0, TS_REQUESTED);
Text_AddOutline(m_Text[g_OptionSelected], TS_REQUESTED);
Text_AddBackground(m_Text[TEXT_TITLE], 136, 0, 0, 0, TS_HEADING);
Text_AddOutline(m_Text[TEXT_TITLE], TS_HEADING);
Text_AddBackground(m_Text[TEXT_TITLE_BORDER], 140, 85, 0, 0, TS_BACKGROUND);
Text_AddOutline(m_Text[TEXT_TITLE_BORDER], TS_BACKGROUND);
for (int i = 0; i < TEXT_NUMBER_OF; i++) {
Text_CentreH(m_Text[i], 1);
Text_CentreV(m_Text[i], 1);
static void M_Shutdown(M_PRIV *const p)
{
if (p->ui != nullptr) {
UI_SoundSettings_Free(p->ui);
p->ui = nullptr;
}
}
void Option_Sound_Control(INVENTORY_ITEM *inv_item, const bool is_busy)
void Option_Sound_Control(INVENTORY_ITEM *const inv_item, const bool is_busy)
{
M_PRIV *const p = &m_Priv;
if (is_busy) {
return;
}
char buf[20];
if (!m_Text[TEXT_MUSIC_VOLUME]) {
M_InitText();
if (p->ui == nullptr) {
M_Init(p);
}
UI_SoundSettings_Control(p->ui);
}
if (g_InputDB.menu_up && g_OptionSelected > TEXT_OPTION_MIN) {
Text_RemoveOutline(m_Text[g_OptionSelected]);
Text_RemoveBackground(m_Text[g_OptionSelected]);
--g_OptionSelected;
Text_AddBackground(
m_Text[g_OptionSelected], 128, 0, 0, 0, TS_REQUESTED);
Text_AddOutline(m_Text[g_OptionSelected], TS_REQUESTED);
Text_SetPos(m_Text[TEXT_LEFT_ARROW], -45, 0);
Text_SetPos(m_Text[TEXT_RIGHT_ARROW], 40, 0);
}
if (g_InputDB.menu_down && g_OptionSelected < TEXT_OPTION_MAX) {
Text_RemoveOutline(m_Text[g_OptionSelected]);
Text_RemoveBackground(m_Text[g_OptionSelected]);
++g_OptionSelected;
Text_AddBackground(
m_Text[g_OptionSelected], 128, 0, 0, 0, TS_REQUESTED);
Text_AddOutline(m_Text[g_OptionSelected], TS_REQUESTED);
Text_SetPos(m_Text[TEXT_LEFT_ARROW], -45, 25);
Text_SetPos(m_Text[TEXT_RIGHT_ARROW], 40, 25);
}
switch (g_OptionSelected) {
case TEXT_MUSIC_VOLUME:
if (g_InputDB.menu_left
&& g_Config.audio.music_volume > Music_GetMinVolume()) {
g_Config.audio.music_volume--;
Config_Write();
Music_SetVolume(g_Config.audio.music_volume);
Sound_Effect(SFX_MENU_PASSPORT, nullptr, SPM_ALWAYS);
sprintf(buf, "\\{icon music} %2d", g_Config.audio.music_volume);
Text_ChangeText(m_Text[TEXT_MUSIC_VOLUME], buf);
} else if (
g_InputDB.menu_right
&& g_Config.audio.music_volume < Music_GetMaxVolume()) {
g_Config.audio.music_volume++;
Config_Write();
Music_SetVolume(g_Config.audio.music_volume);
Sound_Effect(SFX_MENU_PASSPORT, nullptr, SPM_ALWAYS);
sprintf(buf, "\\{icon music} %2d", g_Config.audio.music_volume);
Text_ChangeText(m_Text[TEXT_MUSIC_VOLUME], buf);
}
Text_Hide(
m_Text[TEXT_LEFT_ARROW],
g_Config.audio.music_volume == Music_GetMinVolume());
Text_Hide(
m_Text[TEXT_RIGHT_ARROW],
g_Config.audio.music_volume == Music_GetMaxVolume());
break;
case TEXT_SOUND_VOLUME:
if (g_InputDB.menu_left
&& g_Config.audio.sound_volume > Sound_GetMinVolume()) {
g_Config.audio.sound_volume--;
Config_Write();
Sound_SetMasterVolume(g_Config.audio.sound_volume);
Sound_Effect(SFX_MENU_PASSPORT, nullptr, SPM_ALWAYS);
sprintf(buf, "\\{icon sound} %2d", g_Config.audio.sound_volume);
Text_ChangeText(m_Text[TEXT_SOUND_VOLUME], buf);
} else if (
g_InputDB.menu_right
&& g_Config.audio.sound_volume < Sound_GetMaxVolume()) {
g_Config.audio.sound_volume++;
Config_Write();
Sound_SetMasterVolume(g_Config.audio.sound_volume);
Sound_Effect(SFX_MENU_PASSPORT, nullptr, SPM_ALWAYS);
sprintf(buf, "\\{icon sound} %2d", g_Config.audio.sound_volume);
Text_ChangeText(m_Text[TEXT_SOUND_VOLUME], buf);
}
Text_Hide(
m_Text[TEXT_LEFT_ARROW],
g_Config.audio.sound_volume == Sound_GetMinVolume());
Text_Hide(
m_Text[TEXT_RIGHT_ARROW],
g_Config.audio.sound_volume == Sound_GetMaxVolume());
break;
}
if (g_InputDB.menu_confirm || g_InputDB.menu_back) {
Option_Sound_Shutdown();
void Option_Sound_Draw(INVENTORY_ITEM *const inv_item)
{
M_PRIV *const p = &m_Priv;
if (p->ui != nullptr) {
UI_SoundSettings(p->ui);
}
}
void Option_Sound_Shutdown(void)
{
for (int i = 0; i < TEXT_NUMBER_OF; i++) {
Text_Remove(m_Text[i]);
m_Text[i] = nullptr;
}
M_Shutdown(&m_Priv);
}

View file

@ -3,4 +3,5 @@
#include <libtrx/game/inventory_ring/types.h>
void Option_Sound_Control(INVENTORY_ITEM *inv_item, bool is_busy);
void Option_Sound_Draw(INVENTORY_ITEM *inv_item);
void Option_Sound_Shutdown(void);

View file

@ -49,6 +49,7 @@ typedef struct {
int32_t thickness;
} LIGHTNING;
static bool m_Initialized = false;
static int32_t m_LightningCount = 0;
static LIGHTNING m_LightningTable[MAX_LIGHTNINGS];
static int32_t m_TextureMap[GFX_MAX_TEXTURES] = { GFX_NO_TEXTURE };
@ -385,6 +386,11 @@ static void M_DrawSprite(
bool Output_Init(void)
{
if (m_Initialized) {
return true;
}
m_Initialized = true;
for (int32_t i = 0; i < GFX_MAX_TEXTURES; i++) {
m_TextureMap[i] = GFX_NO_TEXTURE;
m_TextureSurfaces[i] = nullptr;
@ -406,6 +412,11 @@ bool Output_Init(void)
void Output_Shutdown(void)
{
if (!m_Initialized) {
return;
}
m_Initialized = false;
Output_Meshes_Shutdown();
Output_Sprites_Shutdown();
Output_Textures_Shutdown();

View file

@ -81,37 +81,18 @@ static SHELL_ARGS m_Args = {
static const char *m_CurrentGameFlowPath;
static void M_ParseArgs(SHELL_ARGS *out_args);
static void M_ShowHelp(void);
static void M_LoadConfig(void);
static void M_HandleConfigChange(const EVENT *event, void *data);
static void M_ParseArgs(SHELL_ARGS *const out_args)
static void M_ShowHelp(void)
{
const char **args = nullptr;
int32_t arg_count = 0;
Shell_GetCommandLine(&arg_count, &args);
out_args->mod = M_MOD_OG;
for (int32_t i = 0; i < arg_count; i++) {
if (!strcmp(args[i], "-gold")) {
out_args->mod = M_MOD_UB;
}
if (!strcmp(args[i], "-demo_pc")) {
out_args->mod = M_MOD_DEMO_PC;
}
if ((!strcmp(args[i], "-l") || !strcmp(args[i], "--level"))
&& i + 1 < arg_count) {
out_args->level_to_play = args[i + 1];
out_args->mod = M_MOD_CUSTOM_LEVEL;
}
if ((!strcmp(args[i], "-s") || !strcmp(args[i], "--save"))
&& i + 1 < arg_count) {
if (String_ParseInteger(args[i + 1], &out_args->save_to_load)) {
out_args->save_to_load--;
}
}
}
puts("Currently available options:");
puts("");
puts("-g/--gold: launch The Unfinished Business expansion pack.");
puts(" --demo-pc: launch the PC demo level file.");
puts("-l/--level <PATH>: launch a specific level file.");
puts("-s/--save <NUM>: launch from a specific save slot (starts at 1).");
}
static void M_HandleConfigChange(const EVENT *const event, void *const data)
@ -176,10 +157,40 @@ const char *Shell_GetGameFlowPath(void)
return m_ModPaths[m_Args.mod].game_flow_path;
}
void Shell_Main(void)
bool Shell_ParseArgs(const int32_t arg_count, const char **args)
{
M_ParseArgs(&m_Args);
SHELL_ARGS *const out_args = &m_Args;
out_args->mod = M_MOD_OG;
for (int32_t i = 0; i < arg_count; i++) {
if (!strcmp(args[i], "-h") || !strcmp(args[i], "--help")) {
M_ShowHelp();
return false;
}
if (!strcmp(args[i], "-g") || !strcmp(args[i], "--gold")
|| !strcmp(args[i], "-gold")) {
out_args->mod = M_MOD_UB;
}
if (!strcmp(args[i], "--demo-pc") || !strcmp(args[i], "-demo_pc")) {
out_args->mod = M_MOD_DEMO_PC;
}
if ((!strcmp(args[i], "-l") || !strcmp(args[i], "--level"))
&& i + 1 < arg_count) {
out_args->level_to_play = args[i + 1];
out_args->mod = M_MOD_CUSTOM_LEVEL;
}
if ((!strcmp(args[i], "-s") || !strcmp(args[i], "--save"))
&& i + 1 < arg_count) {
if (String_ParseInteger(args[i + 1], &out_args->save_to_load)) {
out_args->save_to_load--;
}
}
}
return true;
}
int32_t Shell_Main(void)
{
GameString_Init();
EnumMap_Init();
Config_Init();
@ -202,7 +213,7 @@ void Shell_Main(void)
if (!Output_Init()) {
Shell_ExitSystem("Could not initialise video system");
return;
return 1;
}
Screen_Init();
@ -318,6 +329,7 @@ void Shell_Main(void)
if (m_Args.level_to_play != nullptr) {
Memory_FreePointer(&g_GameFlow.level_tables[GFLT_MAIN].levels[0].path);
}
return 0;
}
void Shell_ProcessInput(void)

View file

@ -528,18 +528,6 @@ void Sound_StopAll(void)
Audio_Sample_CloseAll();
}
void Sound_SetMasterVolume(int8_t volume)
{
int8_t raw_volume = volume ? 6 * volume + 3 : 0;
m_MasterVolumeDefault = raw_volume & 0x3F;
m_MasterVolume = raw_volume & 0x3F;
}
int8_t Sound_GetMasterVolume(void)
{
return (m_MasterVolume - 3) / 6;
}
int32_t Sound_GetMinVolume(void)
{
return 0;
@ -550,6 +538,18 @@ int32_t Sound_GetMaxVolume(void)
return 10;
}
void Sound_SetMasterVolume(int32_t volume)
{
int8_t raw_volume = volume ? 6 * volume + 3 : 0;
m_MasterVolumeDefault = raw_volume & 0x3F;
m_MasterVolume = raw_volume & 0x3F;
}
int32_t Sound_GetMasterVolume(void)
{
return (m_MasterVolume - 3) / 6;
}
void Sound_ResetAmbient(void)
{
M_ResetAmbientLoudness();

View file

@ -13,10 +13,6 @@ bool Sound_StopEffect(SOUND_EFFECT_ID sfx_num, const XYZ_32 *pos);
void Sound_UpdateEffects(void);
void Sound_ResetEffects(void);
void Sound_StopAmbientSounds(void);
int8_t Sound_GetMasterVolume(void);
void Sound_SetMasterVolume(int8_t volume);
int32_t Sound_GetMinVolume(void);
int32_t Sound_GetMaxVolume(void);
void Sound_LoadSamples(
size_t num_samples, const char **sample_pointers, size_t *sizes);
int32_t Sound_GetMaxSamples(void);

View file

@ -1,24 +0,0 @@
#include "game/screen.h"
#include <libtrx/config.h>
#include <libtrx/game/ui/common.h>
int32_t UI_GetCanvasWidth(void)
{
return Screen_GetResHeightDownscaled(RSR_GENERIC) * 16 / 9;
}
int32_t UI_GetCanvasHeight(void)
{
return Screen_GetResHeightDownscaled(RSR_GENERIC);
}
float UI_ScaleX(const float x)
{
return Screen_GetRenderScale(x * 0x10000, RSR_GENERIC) / (float)0x10000;
}
float UI_ScaleY(const float y)
{
return Screen_GetRenderScale(y * 0x10000, RSR_GENERIC) / (float)0x10000;
}

View file

@ -260,7 +260,6 @@ sources = [
'game/spawn.c',
'game/stats/common.c',
'game/text.c',
'game/ui/common.c',
'game/ui/dialogs/stats.c',
'game/viewport.c',
'global/enum_map.c',

View file

@ -20,40 +20,6 @@
static BOUNDS_16 m_NullBounds = {};
static BOUNDS_16 m_InterpolatedBounds = {};
static OBJECT_BOUNDS M_ConvertBounds(const int16_t *bounds_in);
static OBJECT_BOUNDS M_ConvertBounds(const int16_t *const bounds_in)
{
// TODO: remove this conversion utility once we gain control over its
// incoming arguments
return (OBJECT_BOUNDS) {
.shift = {
.min = {
.x = bounds_in[0],
.y = bounds_in[2],
.z = bounds_in[4],
},
.max = {
.x = bounds_in[1],
.y = bounds_in[3],
.z = bounds_in[5],
},
},
.rot = {
.min = {
.x = bounds_in[6],
.y = bounds_in[8],
.z = bounds_in[10],
},
.max = {
.x = bounds_in[7],
.y = bounds_in[9],
.z = bounds_in[11],
},
},
};
}
void Item_Control(void)
{
int16_t item_num = Item_GetNextActive();
@ -188,95 +154,6 @@ int16_t Item_GetHeight(const ITEM *const item)
return height;
}
int32_t Item_TestPosition(
const int16_t *const bounds_in, const ITEM *const src_item,
const ITEM *const dst_item)
{
const OBJECT_BOUNDS bounds = M_ConvertBounds(bounds_in);
const XYZ_16 rot = {
.x = dst_item->rot.x - src_item->rot.x,
.y = dst_item->rot.y - src_item->rot.y,
.z = dst_item->rot.z - src_item->rot.z,
};
const XYZ_32 dist = {
.x = dst_item->pos.x - src_item->pos.x,
.y = dst_item->pos.y - src_item->pos.y,
.z = dst_item->pos.z - src_item->pos.z,
};
// clang-format off
if (rot.x < bounds.rot.min.x ||
rot.x > bounds.rot.max.x ||
rot.y < bounds.rot.min.y ||
rot.y > bounds.rot.max.y ||
rot.z < bounds.rot.min.z ||
rot.z > bounds.rot.max.z
) {
return false;
}
// clang-format on
Matrix_PushUnit();
Matrix_Rot16(src_item->rot);
const MATRIX *const m = g_MatrixPtr;
const XYZ_32 shift = {
.x = (dist.x * m->_00 + dist.y * m->_10 + dist.z * m->_20) >> W2V_SHIFT,
.y = (dist.x * m->_01 + dist.y * m->_11 + dist.z * m->_21) >> W2V_SHIFT,
.z = (dist.x * m->_02 + dist.y * m->_12 + dist.z * m->_22) >> W2V_SHIFT,
};
Matrix_Pop();
// clang-format off
return (
shift.x >= bounds.shift.min.x &&
shift.x <= bounds.shift.max.x &&
shift.y >= bounds.shift.min.y &&
shift.y <= bounds.shift.max.y &&
shift.z >= bounds.shift.min.z &&
shift.z <= bounds.shift.max.z
);
// clang-format on
}
void Item_AlignPosition(
const XYZ_32 *const vec, const ITEM *const src_item, ITEM *const dst_item)
{
dst_item->rot = src_item->rot;
Matrix_PushUnit();
Matrix_Rot16(src_item->rot);
const MATRIX *const m = g_MatrixPtr;
const XYZ_32 shift = {
.x = (vec->x * m->_00 + vec->y * m->_01 + vec->z * m->_02) >> W2V_SHIFT,
.y = (vec->x * m->_10 + vec->y * m->_11 + vec->z * m->_12) >> W2V_SHIFT,
.z = (vec->x * m->_20 + vec->y * m->_21 + vec->z * m->_22) >> W2V_SHIFT,
};
Matrix_Pop();
const XYZ_32 new_pos = {
.x = src_item->pos.x + shift.x,
.y = src_item->pos.y + shift.y,
.z = src_item->pos.z + shift.z,
};
int16_t room_num = dst_item->room_num;
const SECTOR *const sector =
Room_GetSector(new_pos.x, new_pos.y, new_pos.z, &room_num);
const int32_t height =
Room_GetHeight(sector, new_pos.x, new_pos.y, new_pos.z);
const int32_t ceiling =
Room_GetCeiling(sector, new_pos.x, new_pos.y, new_pos.z);
if (ABS(height - dst_item->pos.y) > STEP_L
|| ABS(ceiling - dst_item->pos.y) < LARA_HEIGHT) {
return;
}
dst_item->pos.x = new_pos.x;
dst_item->pos.y = new_pos.y;
dst_item->pos.z = new_pos.z;
}
int32_t Item_GetFrames(const ITEM *item, ANIM_FRAME *frames[], int32_t *rate)
{
const ANIM *const anim = Item_GetAnim(item);

View file

@ -8,10 +8,6 @@ void Item_Control(void);
void Item_ClearKilled(void);
void Item_ShiftCol(ITEM *item, COLL_INFO *coll);
void Item_UpdateRoom(ITEM *item, int32_t height);
int32_t Item_TestPosition(
const int16_t *bounds, const ITEM *src_item, const ITEM *dst_item);
void Item_AlignPosition(
const XYZ_32 *vec, const ITEM *src_item, ITEM *dst_item);
int32_t Item_GetFrames(const ITEM *item, ANIM_FRAME *frmptr[], int32_t *rate);
bool Item_IsNearItem(const ITEM *item, const XYZ_32 *pos, int32_t distance);

View file

@ -10,33 +10,37 @@
#include "game/sound.h"
#include "global/vars.h"
#include <libtrx/game/lara.h>
#define EXPLOSION_START_FRAME 76
#define EXPLOSION_END_FRAME 99
#define EXPLOSION_ACTION_FRAME 80
static XYZ_32 m_DetonatorPosition = { .x = 0, .y = 0, .z = 0 };
static int16_t m_GongBounds[12] = {
-WALL_L / 2,
+WALL_L,
-100,
+100,
-WALL_L / 2 - 300,
-WALL_L / 2 + 100,
-30 * DEG_1,
+30 * DEG_1,
+0,
+0,
+0,
+0,
static const OBJECT_BOUNDS m_GongBounds = {
.shift = {
.min = { .x = -WALL_L / 2, .y = -100, .z = -WALL_L / 2 - 300, },
.max = { .x = +WALL_L, .y = +100, .z = -WALL_L / 2 + 100, },
},
.rot = {
.min = { .x = -30 * DEG_1, .y = 0, .z = 0, },
.max = { .x = +30 * DEG_1, .y = 0, .z = 0, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static void M_CreateGongBonger(ITEM *lara_item);
static void M_Setup1(OBJECT *obj);
static void M_Setup2(OBJECT *obj);
static void M_Control(int16_t item_num);
static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_GongBounds;
}
static void M_CreateGongBonger(ITEM *const lara_item)
{
const int16_t item_gong_bonger_num = Item_Create();
@ -64,12 +68,14 @@ static void M_CreateGongBonger(ITEM *const lara_item)
static void M_Setup1(OBJECT *const obj)
{
obj->collision_func = M_Collision;
obj->bounds_func = M_Bounds;
}
static void M_Setup2(OBJECT *const obj)
{
obj->collision_func = M_Collision;
obj->control_func = M_Control;
obj->bounds_func = Pickup_Bounds;
obj->save_flags = 1;
obj->save_anim = 1;
}
@ -102,6 +108,7 @@ static void M_Collision(
}
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
const XYZ_16 old_rot = item->rot;
const int16_t x = item->rot.x;
const int16_t y = item->rot.y;
@ -117,16 +124,11 @@ static void M_Collision(
goto normal_collision;
}
if (item->object_id == O_DETONATOR_2) {
if (!Item_TestPosition(g_PickupBounds, item, lara_item)) {
goto normal_collision;
}
} else {
if (!Item_TestPosition(m_GongBounds, item, lara_item)) {
goto normal_collision;
} else {
item->rot = old_rot;
}
if (!Lara_TestPosition(item, obj->bounds_func())) {
goto normal_collision;
}
if (item->object_id == O_DETONATOR_1) {
item->rot = old_rot;
}
if (g_Inv_Chosen == NO_OBJECT) {
@ -138,7 +140,7 @@ static void M_Collision(
}
Inv_RemoveItem(O_KEY_OPTION_2);
Item_AlignPosition(&m_DetonatorPosition, item, lara_item);
Lara_AlignPosition(item, &m_DetonatorPosition);
Item_SwitchToObjAnim(lara_item, LA_EXTRA_BREATH, 0, O_LARA_EXTRA);
lara_item->current_anim_state = LA_EXTRA_BREATH;
if (item->object_id == O_DETONATOR_2) {

View file

@ -6,6 +6,7 @@ static void M_Setup(OBJECT *obj);
static void M_Setup(OBJECT *const obj)
{
obj->collision_func = Pickup_Collision;
obj->bounds_func = Pickup_Bounds;
obj->control_func = Flare_Control;
obj->draw_func = Flare_DrawInAir;
obj->save_position = 1;

View file

@ -17,29 +17,29 @@ static XYZ_32 m_KeyholePosition = {
.z = WALL_L / 2 - LARA_RADIUS - 50,
};
static int16_t m_KeyholeBounds[12] = {
// clang-format off
-200,
+200,
+0,
+0,
+WALL_L / 2 - 200,
+WALL_L / 2,
-10 * DEG_1,
+10 * DEG_1,
-30 * DEG_1,
+30 * DEG_1,
-10 * DEG_1,
+10 * DEG_1,
// clang-format on
static const OBJECT_BOUNDS m_KeyHoleBounds = {
.shift = {
.min = { .x = -200, .y = +0, .z = +WALL_L / 2 - 200, },
.max = { .x = +200, .y = +0, .z = +WALL_L / 2, },
},
.rot = {
.min = { .x = -10 * DEG_1, .y = -30 * DEG_1, .z = -10 * DEG_1, },
.max = { .x = +10 * DEG_1, .y = +30 * DEG_1, .z = +10 * DEG_1, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static void M_Consume(
ITEM *lara_item, ITEM *keyhole_item, GAME_OBJECT_ID key_obj_id);
static void M_Refuse(const ITEM *lara_item);
static void M_Setup(OBJECT *obj);
static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_KeyHoleBounds;
}
static void M_Refuse(const ITEM *const lara_item)
{
if (lara_item->pos.x == g_InteractPosition.x
@ -57,7 +57,7 @@ static void M_Consume(
const GAME_OBJECT_ID key_obj_id)
{
Inv_RemoveItem(key_obj_id);
Item_AlignPosition(&m_KeyholePosition, keyhole_item, lara_item);
Lara_AlignPosition(keyhole_item, &m_KeyholePosition);
lara_item->goal_anim_state = LS_USE_KEY;
do {
Lara_Animate(lara_item);
@ -71,6 +71,7 @@ static void M_Consume(
static void M_Setup(OBJECT *const obj)
{
obj->collision_func = M_Collision;
obj->bounds_func = M_Bounds;
obj->save_flags = 1;
}
@ -82,12 +83,13 @@ static void M_Collision(
}
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
if ((g_Inv_Chosen == NO_OBJECT && !g_Input.action)
|| g_Lara.gun_status != LGS_ARMLESS || lara_item->gravity) {
return;
}
if (!Item_TestPosition(m_KeyholeBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}

View file

@ -20,21 +20,18 @@ typedef enum {
MOVABLE_BLOCK_STATE_PULL = 3,
} MOVABLE_BLOCK_STATE;
static int16_t m_MovableBlockBounds[12] = {
-300,
+300,
+0,
+0,
-WALL_L / 2 - LARA_RADIUS - 80,
-WALL_L / 2,
-10 * DEG_1,
+10 * DEG_1,
-30 * DEG_1,
+30 * DEG_1,
-10 * DEG_1,
+10 * DEG_1,
static const OBJECT_BOUNDS m_MovableBlockBounds = {
.shift = {
.min = { .x = -300, .y = 0, .z = -WALL_L / 2 - LARA_RADIUS - 80, },
.max = { .x = +300, .y = 0, .z = -WALL_L / 2, },
},
.rot = {
.min = { .x = -10 * DEG_1, .y = -30 * DEG_1, .z = -10 * DEG_1, },
.max = { .x = +10 * DEG_1, .y = +30 * DEG_1, .z = +10 * DEG_1, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static bool M_TestDestination(const ITEM *item, int32_t block_height);
static bool M_TestPush(
const ITEM *item, int32_t block_height, DIRECTION quadrant);
@ -47,6 +44,11 @@ static void M_Draw(const ITEM *item);
static void M_Control(int16_t item_num);
static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_MovableBlockBounds;
}
static bool M_TestDestination(
const ITEM *const item, const int32_t block_height)
{
@ -199,6 +201,7 @@ static void M_Setup(OBJECT *const obj)
obj->handle_save_func = M_HandleSave;
obj->control_func = M_Control;
obj->collision_func = M_Collision;
obj->bounds_func = M_Bounds;
obj->draw_func = M_Draw;
obj->save_position = 1;
obj->save_flags = 1;
@ -303,7 +306,7 @@ static void M_Collision(
break;
}
if (!Item_TestPosition(m_MovableBlockBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}
@ -346,7 +349,7 @@ static void M_Collision(
} else if (
Item_TestAnimEqual(lara_item, LA_PUSHABLE_GRAB)
&& Item_TestFrameEqual(lara_item, LF_PPREADY)) {
if (!Item_TestPosition(m_MovableBlockBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}

View file

@ -26,41 +26,29 @@
#define LF_PICKUP_FLARE_UW 20
#define LF_PICKUP_UW 18
int16_t g_PickupBounds[12] = {
// clang-format off
-WALL_L / 4,
+WALL_L / 4,
-100,
+100,
-WALL_L / 4,
+WALL_L / 4,
-10 * DEG_1,
+10 * DEG_1,
+0,
+0,
+0,
+0,
// clang-format on
};
static XYZ_32 m_PickupPosition = { .x = 0, .y = 0, .z = -100 };
static XYZ_32 m_PickupPositionUW = { .x = 0, .y = -200, .z = -350 };
static int16_t m_PickupBoundsUW[12] = {
// clang-format off
-WALL_L / 2,
+WALL_L / 2,
-WALL_L / 2,
+WALL_L / 2,
-WALL_L / 2,
+WALL_L / 2,
-45 * DEG_1,
+45 * DEG_1,
-45 * DEG_1,
+45 * DEG_1,
-45 * DEG_1,
+45 * DEG_1,
// clang-format on
static const OBJECT_BOUNDS m_PickUpBounds = {
.shift = {
.min = { .x = -WALL_L / 4, .y = -100, .z = -WALL_L / 4, },
.max = { .x = +WALL_L / 4, .y = +100, .z = +WALL_L / 4, },
},
.rot = {
.min = { .x = -10 * DEG_1, .y = 0, .z = 0, },
.max = { .x = +10 * DEG_1, .y = 0, .z = 0, },
},
};
static const OBJECT_BOUNDS m_PickUpBoundsUW = {
.shift = {
.min = { .x = -WALL_L / 2, .y = -WALL_L / 2, .z = -WALL_L / 2, },
.max = { .x = +WALL_L / 2, .y = +WALL_L / 2, .z = +WALL_L / 2, },
},
.rot = {
.min = { .x = -45 * DEG_1, .y = -45 * DEG_1, .z = -45 * DEG_1, },
.max = { .x = +45 * DEG_1, .y = +45 * DEG_1, .z = +45 * DEG_1, },
},
};
static void M_DoPickup(int16_t item_num);
@ -107,13 +95,14 @@ static void M_DoFlarePickup(const int16_t item_num)
static void M_DoAboveWater(const int16_t item_num, ITEM *const lara_item)
{
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
const XYZ_16 old_rot = item->rot;
item->rot.x = 0;
item->rot.y = lara_item->rot.y;
item->rot.z = 0;
if (!Item_TestPosition(g_PickupBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
goto cleanup;
}
@ -145,7 +134,7 @@ static void M_DoAboveWater(const int16_t item_num, ITEM *const lara_item)
lara_item->goal_anim_state = LS_STOP;
g_Lara.gun_status = LGS_HANDS_BUSY;
} else {
Item_AlignPosition(&m_PickupPosition, item, lara_item);
Lara_AlignPosition(item, &m_PickupPosition);
lara_item->goal_anim_state = LS_PICKUP;
do {
Lara_Animate(lara_item);
@ -163,13 +152,14 @@ cleanup:
static void M_DoUnderwater(const int16_t item_num, ITEM *const lara_item)
{
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
const XYZ_16 old_rot = item->rot;
item->rot.x = -25 * DEG_1;
item->rot.y = lara_item->rot.y;
item->rot.z = 0;
if (!Item_TestPosition(m_PickupBoundsUW, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
goto cleanup;
}
@ -224,6 +214,7 @@ static void M_Setup(OBJECT *const obj)
obj->handle_save_func = M_HandleSave;
obj->activate_func = M_Activate;
obj->collision_func = Pickup_Collision;
obj->bounds_func = Pickup_Bounds;
obj->draw_func = M_Draw;
obj->save_position = 1;
obj->save_flags = 1;
@ -343,6 +334,15 @@ static void M_Draw(const ITEM *const item)
Matrix_Pop();
}
const OBJECT_BOUNDS *Pickup_Bounds(void)
{
if (g_Lara.water_status == LWS_UNDERWATER) {
return &m_PickUpBoundsUW;
} else {
return &m_PickUpBounds;
}
}
void Pickup_Collision(
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
{

View file

@ -2,7 +2,6 @@
#include "global/types.h"
extern int16_t g_PickupBounds[];
void Pickup_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
bool Pickup_Trigger(int16_t item_num);
const OBJECT_BOUNDS *Pickup_Bounds(void);

View file

@ -17,23 +17,18 @@ static XYZ_32 m_PuzzleHolePosition = {
.z = WALL_L / 2 - LARA_RADIUS - 85,
};
static int16_t m_PuzzleHoleBounds[12] = {
// clang-format off
-200,
+200,
+0,
+0,
+WALL_L / 2 - 200,
+WALL_L / 2,
-10 * DEG_1,
+10 * DEG_1,
-30 * DEG_1,
+30 * DEG_1,
-10 * DEG_1,
+10 * DEG_1,
// clang-format on
static const OBJECT_BOUNDS m_PuzzleHoleBounds = {
.shift = {
.min = { .x = -200, .y = 0, .z = WALL_L / 2 - 200, },
.max = { .x = +200, .y = 0, .z = WALL_L / 2, },
},
.rot = {
.min = { .x = -10 * DEG_1, .y = -30 * DEG_1, .z = -10 * DEG_1, },
.max = { .x = +10 * DEG_1, .y = +30 * DEG_1, .z = +10 * DEG_1, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static void M_Refuse(const ITEM *lara_item);
static void M_Consume(
ITEM *lara_item, ITEM *puzzle_hole_item, GAME_OBJECT_ID puzzle_obj_id);
@ -43,6 +38,11 @@ static void M_SetupDone(OBJECT *obj);
static void M_HandleSave(ITEM *item, SAVEGAME_STAGE stage);
static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_PuzzleHoleBounds;
}
static void M_Refuse(const ITEM *const lara_item)
{
if (lara_item->pos.x != g_InteractPosition.x
@ -58,7 +58,7 @@ static void M_Consume(
const GAME_OBJECT_ID puzzle_obj_id)
{
Inv_RemoveItem(puzzle_obj_id);
Item_AlignPosition(&m_PuzzleHolePosition, puzzle_hole_item, lara_item);
Lara_AlignPosition(puzzle_hole_item, &m_PuzzleHolePosition);
lara_item->goal_anim_state = LS_USE_PUZZLE;
do {
Lara_Animate(lara_item);
@ -82,6 +82,7 @@ static void M_SetupEmpty(OBJECT *const obj)
{
obj->collision_func = M_Collision;
obj->handle_save_func = M_HandleSave;
obj->bounds_func = M_Bounds;
obj->save_flags = 1;
}
@ -103,10 +104,11 @@ static void M_Collision(
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
{
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
if (lara_item->current_anim_state != LS_STOP) {
if (lara_item->current_anim_state != LS_USE_PUZZLE
|| !Item_TestPosition(m_PuzzleHoleBounds, item, lara_item)
|| !Lara_TestPosition(item, obj->bounds_func())
|| !Item_TestFrameEqual(lara_item, LF_USE_PUZZLE)) {
return;
}
@ -120,7 +122,7 @@ static void M_Collision(
return;
}
if (!Item_TestPosition(m_PuzzleHoleBounds, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}

View file

@ -17,40 +17,30 @@ static XYZ_32 g_PushSwitchPosition = { .x = 0, .y = 0, .z = 292 };
static XYZ_32 m_AirlockPosition = { .x = 0, .y = 0, .z = 212 };
static XYZ_32 m_SwitchUWPosition = { .x = 0, .y = 0, .z = 108 };
static int16_t m_SwitchBounds[12] = {
// clang-format off
-220,
+220,
+0,
+0,
+WALL_L / 2 - 220,
+WALL_L / 2,
-10 * DEG_1,
+10 * DEG_1,
-30 * DEG_1,
+30 * DEG_1,
-10 * DEG_1,
+10 * DEG_1,
// clang-format on
static const OBJECT_BOUNDS m_SwitchBounds = {
.shift = {
.min = { .x = -220, .y = +0, .z = +WALL_L / 2 - 220, },
.max = { .x = +220, .y = +0, .z = +WALL_L / 2, },
},
.rot = {
.min = { .x = -10 * DEG_1, .y = -30 * DEG_1, .z = -10 * DEG_1, },
.max = { .x = +10 * DEG_1, .y = +30 * DEG_1, .z = +10 * DEG_1, },
},
};
static int16_t m_SwitchBoundsUW[12] = {
// clang-format off
-WALL_L,
+WALL_L,
-WALL_L,
+WALL_L,
-WALL_L,
+WALL_L / 2,
-80 * DEG_1,
+80 * DEG_1,
-80 * DEG_1,
+80 * DEG_1,
-80 * DEG_1,
+80 * DEG_1,
// clang-format on
static const OBJECT_BOUNDS m_SwitchBoundsUW = {
.shift = {
.min = { .x = -WALL_L, .y = -WALL_L, .z = -WALL_L, },
.max = { .x = +WALL_L, .y = +WALL_L, .z = +WALL_L / 2, },
},
.rot = {
.min = { .x = -80 * DEG_1, .y = -80 * DEG_1, .z = -80 * DEG_1, },
.max = { .x = +80 * DEG_1, .y = +80 * DEG_1, .z = +80 * DEG_1, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static const OBJECT_BOUNDS *M_BoundsUW(void);
static void M_AlignLara(ITEM *lara_item, ITEM *switch_item);
static void M_SwitchOn(ITEM *switch_item, ITEM *lara_item);
static void M_SwitchOff(ITEM *switch_item, ITEM *lara_item);
@ -62,19 +52,29 @@ static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static void M_CollisionUW(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static void M_Control(int16_t item_num);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_SwitchBounds;
}
static const OBJECT_BOUNDS *M_BoundsUW(void)
{
return &m_SwitchBoundsUW;
}
static void M_AlignLara(ITEM *const lara_item, ITEM *const switch_item)
{
switch (switch_item->object_id) {
case O_SWITCH_TYPE_AIRLOCK:
Item_AlignPosition(&m_AirlockPosition, switch_item, lara_item);
Lara_AlignPosition(switch_item, &m_AirlockPosition);
break;
case O_SWITCH_TYPE_SMALL:
Item_AlignPosition(&g_SmallSwitchPosition, switch_item, lara_item);
Lara_AlignPosition(switch_item, &g_SmallSwitchPosition);
break;
case O_SWITCH_TYPE_BUTTON:
Item_AlignPosition(&g_PushSwitchPosition, switch_item, lara_item);
Lara_AlignPosition(switch_item, &g_PushSwitchPosition);
break;
}
}
@ -139,28 +139,32 @@ static void M_Setup(OBJECT *const obj)
{
M_SetupBase(obj);
obj->collision_func = M_Collision;
obj->bounds_func = M_Bounds;
}
static void M_SetupPushButton(OBJECT *const obj)
{
M_Setup(obj);
obj->enable_interpolation = false;
obj->bounds_func = M_Bounds;
}
static void M_SetupUW(OBJECT *const obj)
{
M_SetupBase(obj);
obj->collision_func = M_CollisionUW;
obj->bounds_func = M_BoundsUW;
}
static void M_Collision(
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
{
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
if (!g_Input.action || item->status != IS_INACTIVE
|| g_Lara.gun_status != LGS_ARMLESS || lara_item->gravity
|| lara_item->current_anim_state != LS_STOP
|| !Item_TestPosition(m_SwitchBounds, item, lara_item)) {
|| !Lara_TestPosition(item, obj->bounds_func())) {
return;
}
@ -193,6 +197,7 @@ static void M_CollisionUW(
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
{
ITEM *const item = Item_Get(item_num);
const OBJECT *const obj = Object_Get(item->object_id);
if (!g_Input.action || item->status != IS_INACTIVE
|| g_Lara.water_status != LWS_UNDERWATER
@ -201,7 +206,7 @@ static void M_CollisionUW(
return;
}
if (!Item_TestPosition(m_SwitchBoundsUW, item, lara_item)) {
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}

View file

@ -22,33 +22,35 @@ static XYZ_32 m_ZiplineHandlePosition = {
.y = 0,
.z = WALL_L / 2 - 141,
};
static int16_t m_ZiplineHandleBounds[12] = {
// clang-format off
-WALL_L / 4,
+WALL_L / 4,
-100,
+100,
+WALL_L / 4,
+WALL_L / 2,
+0,
+0,
-25 * DEG_1,
+25 * DEG_1,
+0,
+0,
// clang-format on
static const OBJECT_BOUNDS m_ZiplineHandleBounds = {
.shift = {
.min = { .x = -WALL_L / 4, .y = -100, .z = +WALL_L / 4, },
.max = { .x = +WALL_L / 4, .y = +100, .z = +WALL_L / 2, },
},
.rot = {
.min = { .x = +0, .y = -25 * DEG_1, .z = +0, },
.max = { .x = +0, .y = +25 * DEG_1, .z = +0, },
},
};
static const OBJECT_BOUNDS *M_Bounds(void);
static void M_Setup(OBJECT *obj);
static void M_Initialise(int16_t item_num);
static void M_Control(int16_t item_num);
static void M_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
static const OBJECT_BOUNDS *M_Bounds(void)
{
return &m_ZiplineHandleBounds;
}
static void M_Setup(OBJECT *const obj)
{
obj->initialise_func = M_Initialise;
obj->control_func = M_Control;
obj->collision_func = M_Collision;
obj->bounds_func = M_Bounds;
obj->save_position = 1;
obj->save_flags = 1;
obj->save_anim = 1;
@ -149,11 +151,12 @@ static void M_Collision(
return;
}
if (!Item_TestPosition(m_ZiplineHandleBounds, item, lara_item)) {
const OBJECT *const obj = Object_Get(item->object_id);
if (!Lara_TestPosition(item, obj->bounds_func())) {
return;
}
Item_AlignPosition(&m_ZiplineHandlePosition, item, lara_item);
Lara_AlignPosition(item, &m_ZiplineHandlePosition);
g_Lara.gun_status = LGS_HANDS_BUSY;
lara_item->goal_anim_state = LS_ZIPLINE;

View file

@ -1,132 +1,48 @@
#include "game/game_string.h"
#include "game/input.h"
#include "game/inventory_ring.h"
#include "game/music.h"
#include "game/option/option.h"
#include "game/sound.h"
#include "game/text.h"
#include "global/vars.h"
#include <libtrx/config.h>
#include <libtrx/utils.h>
#include <libtrx/game/ui.h>
#include <stdio.h>
typedef struct {
UI_SOUND_SETTINGS_STATE *ui;
} M_PRIV;
static TEXTSTRING *m_SoundText[4];
static M_PRIV m_Priv = {};
static void M_InitText(void);
static void M_ShutdownText(void);
static void M_InitText(void)
static void M_Init(M_PRIV *const p)
{
CLAMPG(g_Config.audio.music_volume, 10);
CLAMPG(g_Config.audio.sound_volume, 10);
p->ui = UI_SoundSettings_Init();
}
char text[32];
sprintf(text, "\\{icon music} %2d", g_Config.audio.music_volume);
m_SoundText[0] = Text_Create(0, 0, text);
Text_AddBackground(m_SoundText[0], 128, 0, 0, 0, TS_REQUESTED);
Text_AddOutline(m_SoundText[0], TS_REQUESTED);
sprintf(text, "\\{icon sound} %2d", g_Config.audio.sound_volume);
m_SoundText[1] = Text_Create(0, 25, text);
m_SoundText[2] = Text_Create(0, -32, " ");
Text_AddBackground(m_SoundText[2], 140, 85, 0, 0, TS_BACKGROUND);
Text_AddOutline(m_SoundText[2], TS_BACKGROUND);
m_SoundText[3] = Text_Create(0, -30, GS(SOUND_SET_VOLUMES));
Text_AddBackground(m_SoundText[3], 136, 0, 0, 0, TS_HEADING);
Text_AddOutline(m_SoundText[3], TS_HEADING);
for (int32_t i = 0; i < 4; i++) {
Text_CentreH(m_SoundText[i], true);
Text_CentreV(m_SoundText[i], true);
static void M_Shutdown(M_PRIV *const p)
{
if (p->ui != nullptr) {
UI_SoundSettings_Free(p->ui);
p->ui = nullptr;
}
}
static void M_ShutdownText(void)
void Option_Sound_Control(INVENTORY_ITEM *const inv_item, const bool is_busy)
{
for (int32_t i = 0; i < 4; i++) {
Text_Remove(m_SoundText[i]);
m_SoundText[i] = nullptr;
M_PRIV *const p = &m_Priv;
if (is_busy) {
return;
}
if (p->ui == nullptr) {
M_Init(p);
}
UI_SoundSettings_Control(p->ui);
}
void Option_Sound_Draw(INVENTORY_ITEM *const inv_item)
{
M_PRIV *const p = &m_Priv;
if (p->ui != nullptr) {
UI_SoundSettings(p->ui);
}
}
void Option_Sound_Shutdown(void)
{
M_ShutdownText();
}
void Option_Sound_Control(INVENTORY_ITEM *const item, const bool is_busy)
{
if (is_busy) {
return;
}
char text[32];
if (m_SoundText[0] == nullptr) {
M_InitText();
}
if (g_InputDB.menu_up && g_SoundOptionLine > 0) {
Text_RemoveOutline(m_SoundText[g_SoundOptionLine]);
Text_RemoveBackground(m_SoundText[g_SoundOptionLine]);
g_SoundOptionLine--;
Text_AddBackground(
m_SoundText[g_SoundOptionLine], 128, 0, 0, 0, TS_REQUESTED);
Text_AddOutline(m_SoundText[g_SoundOptionLine], TS_REQUESTED);
}
if (g_InputDB.menu_down && g_SoundOptionLine < 1) {
Text_RemoveOutline(m_SoundText[g_SoundOptionLine]);
Text_RemoveBackground(m_SoundText[g_SoundOptionLine]);
g_SoundOptionLine++;
Text_AddBackground(
m_SoundText[g_SoundOptionLine], 128, 0, 0, 0, TS_REQUESTED);
Text_AddOutline(m_SoundText[g_SoundOptionLine], TS_REQUESTED);
}
if (g_SoundOptionLine) {
bool changed = false;
if (g_InputDB.menu_left && g_Config.audio.sound_volume > 0) {
g_Config.audio.sound_volume--;
changed = true;
} else if (g_InputDB.menu_right && g_Config.audio.sound_volume < 10) {
g_Config.audio.sound_volume++;
changed = true;
}
if (changed) {
sprintf(text, "\\{icon sound} %2d", g_Config.audio.sound_volume);
Text_ChangeText(m_SoundText[1], text);
Sound_SetMasterVolume(g_Config.audio.sound_volume);
Sound_Effect(SFX_MENU_PASSPORT, nullptr, SPM_ALWAYS);
}
} else {
bool changed = false;
if (g_InputDB.menu_left && g_Config.audio.music_volume > 0) {
g_Config.audio.music_volume--;
changed = true;
} else if (g_InputDB.menu_right && g_Config.audio.music_volume < 10) {
g_Config.audio.music_volume++;
changed = true;
}
if (changed) {
sprintf(text, "\\{icon music} %2d", g_Config.audio.music_volume);
Text_ChangeText(m_SoundText[0], text);
Music_SetVolume(g_Config.audio.music_volume);
Sound_Effect(SFX_MENU_PASSPORT, nullptr, SPM_ALWAYS);
}
}
if (g_InputDB.menu_confirm || g_InputDB.menu_back) {
Option_Sound_Shutdown();
}
}
void Option_Sound_Draw(INVENTORY_ITEM *const item)
{
M_Shutdown(&m_Priv);
}

View file

@ -45,7 +45,6 @@ static RENDERER *M_GetRenderer(void)
} else if (g_Config.rendering.render_mode == RM_HARDWARE) {
r = &m_Renderer_HW;
}
ASSERT(r != nullptr);
return r;
}
@ -124,7 +123,6 @@ void Render_Init(void)
void Render_Shutdown(void)
{
LOG_DEBUG("");
RENDERER *const r = M_GetRenderer();
if (r != nullptr) {
r->Close(r);

View file

@ -99,7 +99,7 @@ static void M_HandleQuit(void);
static void M_ConfigureOpenGL(void);
static bool M_CreateGameWindow(void);
static void M_ParseArgs(SHELL_ARGS *out_args);
static void M_ShowHelp(void);
static void M_LoadConfig(void);
static void M_HandleConfigChange(const EVENT *event, void *data);
@ -342,30 +342,13 @@ static bool M_CreateGameWindow(void)
return true;
}
static void M_ParseArgs(SHELL_ARGS *const out_args)
static void M_ShowHelp(void)
{
const char **args = nullptr;
int32_t arg_count = 0;
Shell_GetCommandLine(&arg_count, &args);
out_args->mod = M_MOD_OG;
for (int32_t i = 0; i < arg_count; i++) {
if (!strcmp(args[i], "-gold")) {
out_args->mod = M_MOD_GM;
}
if ((!strcmp(args[i], "-l") || !strcmp(args[i], "--level"))
&& i + 1 < arg_count) {
out_args->level_to_play = args[i + 1];
out_args->mod = M_MOD_CUSTOM_LEVEL;
}
if ((!strcmp(args[i], "-s") || !strcmp(args[i], "--save"))
&& i + 1 < arg_count) {
if (String_ParseInteger(args[i + 1], &out_args->save_to_load)) {
out_args->save_to_load--;
}
}
}
puts("Currently available options:");
puts("");
puts("-g/--gold: launch The Golden Mask expansion pack.");
puts("-l/--level <PATH>: launch a specific level file.");
puts("-s/--save <NUM>: launch from a specific save slot (starts at 1).");
}
static void M_LoadConfig(void)
@ -433,10 +416,39 @@ static void M_HandleConfigChange(const EVENT *const event, void *const data)
}
}
// TODO: refactor the hell out of me
void Shell_Main(void)
bool Shell_ParseArgs(const int32_t arg_count, const char **args)
{
M_ParseArgs(&m_Args);
SHELL_ARGS *const out_args = &m_Args;
out_args->mod = M_MOD_OG;
for (int32_t i = 0; i < arg_count; i++) {
if (!strcmp(args[i], "-h") || !strcmp(args[i], "--help")) {
M_ShowHelp();
return false;
}
if (!strcmp(args[i], "-g") || !strcmp(args[i], "--gold")
|| !strcmp(args[i], "-gold")) {
out_args->mod = M_MOD_GM;
}
if ((!strcmp(args[i], "-l") || !strcmp(args[i], "--level"))
&& i + 1 < arg_count) {
out_args->level_to_play = args[i + 1];
out_args->mod = M_MOD_CUSTOM_LEVEL;
}
if ((!strcmp(args[i], "-s") || !strcmp(args[i], "--save"))
&& i + 1 < arg_count) {
if (String_ParseInteger(args[i + 1], &out_args->save_to_load)) {
out_args->save_to_load--;
}
}
}
return true;
}
// TODO: refactor the hell out of me
int32_t Shell_Main(void)
{
LOG_INFO("Game directory: %s", File_GetGameDirectory());
if (m_Args.mod == M_MOD_GM) {
Object_Get(O_MONK_3)->setup_func = Monk3_Setup;
@ -465,7 +477,7 @@ void Shell_Main(void)
if (!M_CreateGameWindow()) {
Shell_ExitSystem("Failed to create game window");
return;
return 1;
}
Random_Seed();
@ -561,7 +573,7 @@ void Shell_Main(void)
if (gf_cmd.action == GF_NOOP
|| gf_cmd.action == GF_EXIT_TO_TITLE) {
Shell_ExitSystem("Title disabled & no replacement");
return;
return 1;
}
} else {
gf_cmd = GF_RunTitle();
@ -583,6 +595,7 @@ void Shell_Main(void)
if (m_Args.level_to_play != nullptr) {
Memory_FreePointer(&g_GameFlow.level_tables[GFLT_MAIN].levels[0].path);
}
return 0;
}
void Shell_Shutdown(void)

View file

@ -9,9 +9,6 @@
void Sound_Init(void);
void Sound_Shutdown(void);
void Sound_SetMasterVolume(int32_t volume);
void Sound_UpdateEffects(void);
void Sound_StopEffect(SOUND_EFFECT_ID sample_id);
void Sound_EndScene(void);
int32_t Sound_GetMinVolume(void);
int32_t Sound_GetMaxVolume(void);

View file

@ -1,25 +0,0 @@
#include "global/vars.h"
#include <libtrx/config.h>
#include <libtrx/game/scaler.h>
#include <libtrx/game/ui/common.h>
int32_t UI_GetCanvasWidth(void)
{
return Scaler_CalcInverse(g_PhdWinWidth, SCALER_TARGET_GENERIC);
}
int32_t UI_GetCanvasHeight(void)
{
return Scaler_CalcInverse(g_PhdWinHeight, SCALER_TARGET_GENERIC);
}
float UI_ScaleX(const float x)
{
return Scaler_Calc(x, SCALER_TARGET_GENERIC);
}
float UI_ScaleY(const float y)
{
return Scaler_Calc(y, SCALER_TARGET_GENERIC);
}

View file

@ -151,13 +151,6 @@ typedef struct {
int32_t pitch;
} SKIDOO_INFO;
typedef struct {
struct {
XYZ_16 min;
XYZ_16 max;
} shift, rot;
} OBJECT_BOUNDS;
typedef struct {
int32_t xv;
int32_t yv;

View file

@ -266,7 +266,6 @@ sources = [
'game/spawn.c',
'game/stats.c',
'game/text.c',
'game/ui/common.c',
'game/ui/dialogs/graphic_settings.c',
'game/ui/dialogs/stats.c',
'game/viewport.c',

View file

@ -42,41 +42,33 @@ def extract_zip(zip_path: Path, dest_dir: Path) -> None:
z.extractall(dest_dir)
def download_assets(assets: list[tuple[str, Path]]) -> None:
def download_assets(asset_urls: list[str], target_dir: Path) -> None:
with tempfile.TemporaryDirectory() as tmpdir_str:
tmpdir = Path(tmpdir_str)
for url, dest in assets:
for url in asset_urls:
filename = Path(url).name
local_zip = tmpdir / filename
download_to_file(url, local_zip)
extract_zip(local_zip, dest)
extract_zip(local_zip, target_dir)
print("Asset download and extraction complete.")
def main() -> None:
args = parse_args()
assets: dict[int, list[tuple[str, Path]]] = {
1: [
(
"https://lostartefacts.dev/aux/tr1x/main.zip",
Path("data/tr1/ship"),
)
],
asset_urls_map: dict[int, list[str]] = {
1: ["https://lostartefacts.dev/aux/tr1x/main.zip"],
2: [
(
"https://lostartefacts.dev/aux/tr2x/main.zip",
Path("data/tr2/ship"),
)
"https://lostartefacts.dev/aux/tr2x/main.zip",
"https://lostartefacts.dev/aux/tr2x/trgm.zip",
],
}
match str(args.game_version):
case "1":
download_assets(assets[1])
case "2":
download_assets(assets[2])
case "all":
download_assets(assets[1])
download_assets(assets[2])
versions = {"1": [1], "2": [2], "all": [1, 2]}[args.game_version]
for version in versions:
download_assets(
asset_urls_map[version],
target_dir=PROJECT_PATHS[version].shipped_data_dir,
)
if __name__ == "__main__":