mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 20:58:07 +03:00
Compare commits
47 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5e3fb42a25 | ||
![]() |
6d5bdd89a3 | ||
![]() |
a859d668f9 | ||
![]() |
e03c65ca0f | ||
![]() |
ff86b5e712 | ||
![]() |
cdfb5942c1 | ||
![]() |
8b78a7f001 | ||
![]() |
2cd998673f | ||
![]() |
1f89b14a46 | ||
![]() |
9c0c0160df | ||
![]() |
3e4017d338 | ||
![]() |
0c5b5dbb7b | ||
![]() |
a7269bbe8a | ||
![]() |
f27d0435a9 | ||
![]() |
88f41c5a75 | ||
![]() |
d97edaf1eb | ||
![]() |
820fc307d2 | ||
![]() |
24b81007ba | ||
![]() |
0ba717edd5 | ||
![]() |
3696e84925 | ||
![]() |
e439371c66 | ||
![]() |
864589bf0a | ||
![]() |
5a50de02ed | ||
![]() |
d6fc167749 | ||
![]() |
2f2f0c6842 | ||
![]() |
4035fe6411 | ||
![]() |
e32a4c270f | ||
![]() |
92ff6e12c7 | ||
![]() |
73da4c2945 | ||
![]() |
dc41197cd6 | ||
![]() |
890c7f76bb | ||
![]() |
83ac9514cb | ||
![]() |
96b86b1605 | ||
![]() |
607ac811f0 | ||
![]() |
15b758c57d | ||
![]() |
eec8f16d5f | ||
![]() |
2536ff55c1 | ||
![]() |
10b9bcc780 | ||
![]() |
c00d0627cf | ||
![]() |
a6ebaf5c38 | ||
![]() |
3acab0dc34 | ||
![]() |
c590824944 | ||
![]() |
f590c9243c | ||
![]() |
3030d694a5 | ||
![]() |
4d36177247 | ||
![]() |
b935707b5b | ||
![]() |
4d5040d15c |
98 changed files with 1403 additions and 1443 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -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/
|
||||
|
|
|
@ -369,6 +369,7 @@
|
|||
"DETAIL_TITLE": "Graphic Options",
|
||||
"DETAIL_TRAPEZOID_FILTER": "Trapezoid filter",
|
||||
"DETAIL_UI_BAR_SCALE": "UI bar scale",
|
||||
"DETAIL_UI_SCROLL_WRAPAROUND": "UI scroll wrap",
|
||||
"DETAIL_UI_TEXT_SCALE": "UI text scale",
|
||||
"DETAIL_VSYNC": "VSync",
|
||||
"DETAIL_WATER_COLOR_B": "Water color (B)",
|
||||
|
@ -530,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",
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
|
|
|
@ -496,6 +496,7 @@
|
|||
"DETAIL_TITLE": "Graphic Options",
|
||||
"DETAIL_TRAPEZOID_FILTER": "Trapezoid filter",
|
||||
"DETAIL_UI_BAR_SCALE": "UI bar scale",
|
||||
"DETAIL_UI_SCROLL_WRAPAROUND": "UI scroll wrap",
|
||||
"DETAIL_UI_TEXT_SCALE": "UI text scale",
|
||||
"DETAIL_USE_PSX_FOV": "Use PSX FOV",
|
||||
"DETAIL_WATER_COLOR_B": "Water color (B)",
|
||||
|
@ -654,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",
|
||||
|
|
BIN
data/tr2/ship/data/injections/living_fd.bin
Normal file
BIN
data/tr2/ship/data/injections/living_fd.bin
Normal file
Binary file not shown.
20
docs/COMMAND_LINE.md
Normal file
20
docs/COMMAND_LINE.md
Normal 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`.
|
|
@ -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
|
||||
|
|
|
@ -6,10 +6,11 @@
|
|||
|
||||
1. **Update fog configuration**
|
||||
If you wish to force your fog settings on player:
|
||||
- Rename `draw_distance_min` to `fog_start`
|
||||
- Rename `draw_distance_fade` to `fog_start`
|
||||
- Rename `draw_distance_max` to `fog_end`
|
||||
|
||||
If you wish to give the player agency to change the fog:
|
||||
- Remove `draw_distance_min` and `draw_distance_max`
|
||||
- Remove `draw_distance_fade` and `draw_distance_max`
|
||||
|
||||
|
||||
### Version 4.7 to 4.8
|
||||
|
|
|
@ -4,11 +4,17 @@
|
|||
- added support for a hex water color notation (eg. `#80FFFF`) in the game flow file
|
||||
- added support for antitriggers, like TR2+ (#2580)
|
||||
- added support for aspect ratio-specific images (#1840)
|
||||
- changed the `draw_distance_min` and `draw_distance_max` to `fog_start` and `fog_end`
|
||||
- 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)
|
||||
- fixed anisotropy filter causing black lines on certain GPUs (#902)
|
||||
|
@ -19,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)
|
||||
|
@ -33,6 +40,8 @@
|
|||
- fixed Story So Far not playing the opening FMV, `cafe.rpl` (#2779, regression from 2.10)
|
||||
- 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,4 +1,26 @@
|
|||
## [Unreleased](https://github.com/LostArtefacts/TRX/compare/tr2-1.0...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)
|
||||
- fixed Lara voiding if she stops on a tile with a closing door, and the door isn't on a portal (#2848)
|
||||
- fixed guns carried by enemies not being converted to ammo if Lara has picked up the same gun elsewhere in the same level (#2856)
|
||||
- fixed button mashing triggering load instead of save on a specific passport animation frame (#2863, regression from 1.0)
|
||||
- fixed guns carried by enemies not being converted to ammo if Lara starts the level with the gun and the game has later been reloaded (#2850, regression from 1.0)
|
||||
- fixed 1920x1080 screenshots in 16:9 aspect mode being saved as 1919x1080 (#2845, regression from 0.8)
|
||||
- fixed clicks in audio sounds (#2846, regression from 0.2)
|
||||
|
||||
## [1.0.1](https://github.com/LostArtefacts/TRX/compare/tr2-1.0...tr2-1.0.1) - 2025-04-24
|
||||
- added an option to wraparound when scrolling UI dialogs, such as save/load (#2834)
|
||||
- changed save to take priority over load when both inputs are held on the same frame, in line with OG (#2833)
|
||||
- fixed the selected keyboard/controller layout not being saved (#2830, regression from 1.0)
|
||||
- fixed toggling the PSX FOV option not having an immediate effect (#2831, regression from 1.0)
|
||||
- fixed changing the aspect ratio not updating the current background image (#2832, regression from 1.0)
|
||||
- improved graphic settings dialog sizing (#2841)
|
||||
|
||||
## [1.0](https://github.com/LostArtefacts/TRX/compare/tr2-0.10...tr2-1.0) - 2025-04-23
|
||||
- added support for The Golden Mask (#1621)
|
||||
|
|
|
@ -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)
|
||||
|
@ -254,7 +255,9 @@ However, you can easily download them manually from these urls:
|
|||
- fixed Floating Islands mystic plaque inventory rotation
|
||||
- fixed pushblocks being rotated when Lara grabs them, most noticeable if asymmetric textures have been used
|
||||
- fixed being able to use hotkeys in the end-level statistics screen
|
||||
- fixed guns carried by enemies not being converted to ammo if Lara has picked up the same gun elsewhere in the same level
|
||||
- fixed destroyed gondolas appearing embedded in the ground after loading a save
|
||||
- fixed Lara voiding if she stops on a tile with a closing door, and the door isn't on a portal
|
||||
- improved the animation of Lara's braid
|
||||
|
||||
#### Cheats
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "audio_internal.h"
|
||||
|
||||
#include "benchmark.h"
|
||||
#include "debug.h"
|
||||
#include "log.h"
|
||||
#include "memory.h"
|
||||
|
@ -21,12 +22,21 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
int32_t format;
|
||||
AVChannelLayout ch_layout;
|
||||
int32_t sample_rate;
|
||||
} src, dst;
|
||||
SwrContext *ctx;
|
||||
size_t working_buffer_size;
|
||||
uint8_t *working_buffer;
|
||||
} M_SWR_CONTEXT;
|
||||
|
||||
typedef struct {
|
||||
char *original_data;
|
||||
size_t original_size;
|
||||
|
||||
float *sample_data;
|
||||
int32_t channels;
|
||||
int32_t num_samples;
|
||||
|
@ -50,8 +60,8 @@ typedef struct {
|
|||
} AUDIO_SAMPLE_SOUND;
|
||||
|
||||
typedef struct {
|
||||
const char *data;
|
||||
const char *ptr;
|
||||
const uint8_t *data;
|
||||
const uint8_t *ptr;
|
||||
int32_t size;
|
||||
int32_t remaining;
|
||||
} AUDIO_AV_BUFFER;
|
||||
|
@ -64,7 +74,7 @@ static double M_DecibelToMultiplier(double db_gain);
|
|||
static bool M_RecalculateChannelVolumes(int32_t sound_id);
|
||||
static int32_t M_ReadAVBuffer(void *opaque, uint8_t *dst, int32_t dst_size);
|
||||
static int64_t M_SeekAVBuffer(void *opaque, int64_t offset, int32_t whence);
|
||||
static bool M_Convert(const int32_t sample_id);
|
||||
static bool M_ConvertSample(const int32_t sample_id);
|
||||
|
||||
static double M_DecibelToMultiplier(double db_gain)
|
||||
{
|
||||
|
@ -135,20 +145,98 @@ static int64_t M_SeekAVBuffer(void *opaque, int64_t offset, int32_t whence)
|
|||
return src->ptr - src->data;
|
||||
}
|
||||
|
||||
static bool M_Convert(const int32_t sample_id)
|
||||
static int32_t M_OutputAudioFrame(
|
||||
M_SWR_CONTEXT *const swr, AVFrame *const frame)
|
||||
{
|
||||
ASSERT(sample_id >= 0 && sample_id < m_LoadedSamplesCount);
|
||||
|
||||
bool result = false;
|
||||
AUDIO_SAMPLE *const sample = &m_LoadedSamples[sample_id];
|
||||
|
||||
if (sample->sample_data != nullptr) {
|
||||
return true;
|
||||
// Determine the maximum number of output samples this call can produce,
|
||||
// based on the current delay already inside the resampler plus the new
|
||||
// input. Using av_rescale_rnd() keeps everything in integer domain and
|
||||
// avoids cumulative rounding errors.
|
||||
const int64_t delay = swr_get_delay(swr->ctx, swr->src.sample_rate);
|
||||
const int32_t out_samples = (int32_t)av_rescale_rnd(
|
||||
delay + frame->nb_samples, swr->dst.sample_rate, swr->src.sample_rate,
|
||||
AV_ROUND_UP);
|
||||
if (out_samples <= 0) {
|
||||
return 0; // nothing to do
|
||||
}
|
||||
|
||||
const clock_t time_start = clock();
|
||||
size_t working_buffer_size = 0;
|
||||
float *working_buffer = nullptr;
|
||||
uint8_t *out_buffer = nullptr;
|
||||
if (av_samples_alloc(
|
||||
&out_buffer, nullptr, swr->dst.ch_layout.nb_channels, out_samples,
|
||||
swr->dst.format, 1)
|
||||
< 0) {
|
||||
return AVERROR(ENOMEM);
|
||||
}
|
||||
|
||||
// Convert – we do *not* drain the resampler here.
|
||||
const int32_t converted = swr_convert(
|
||||
swr->ctx, &out_buffer, out_samples, (const uint8_t **)frame->data,
|
||||
frame->nb_samples);
|
||||
|
||||
if (converted < 0) {
|
||||
av_freep(&out_buffer);
|
||||
return converted; // propagate error
|
||||
}
|
||||
|
||||
if (converted > 0) {
|
||||
const int32_t out_buffer_size = av_samples_get_buffer_size(
|
||||
nullptr, swr->dst.ch_layout.nb_channels, converted, swr->dst.format,
|
||||
1);
|
||||
if (out_buffer_size > 0) {
|
||||
swr->working_buffer = Memory_Realloc(
|
||||
swr->working_buffer,
|
||||
swr->working_buffer_size + out_buffer_size);
|
||||
memcpy(
|
||||
swr->working_buffer + swr->working_buffer_size, out_buffer,
|
||||
out_buffer_size);
|
||||
swr->working_buffer_size += out_buffer_size;
|
||||
}
|
||||
}
|
||||
|
||||
av_freep(&out_buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32_t M_DecodePacket(
|
||||
AVCodecContext *const dec, const AVPacket *const pkt, AVFrame *frame,
|
||||
M_SWR_CONTEXT *const swr)
|
||||
{
|
||||
// Submit the packet to the decoder
|
||||
int32_t ret = avcodec_send_packet(dec, pkt);
|
||||
if (ret < 0) {
|
||||
LOG_ERROR(
|
||||
"Error submitting a packet for decoding (%s)\n", av_err2str(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Get all the available frames from the decoder
|
||||
while (ret >= 0) {
|
||||
ret = avcodec_receive_frame(dec, frame);
|
||||
if (ret < 0) {
|
||||
// those two return values are special and mean there is no output
|
||||
// frame available, but there were no errors during decoding
|
||||
if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) {
|
||||
return 0;
|
||||
}
|
||||
LOG_ERROR(
|
||||
"Error receiving a frame for decoding (%s)\n", av_err2str(ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = M_OutputAudioFrame(swr, frame);
|
||||
av_frame_unref(frame);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool M_ConvertRawData(
|
||||
const uint8_t *const original_data, const int32_t original_size,
|
||||
const int32_t dst_sample_rate, const int32_t dst_format,
|
||||
const int32_t dst_channel_count, uint8_t **const out_sample_data,
|
||||
size_t *const out_size, size_t *const out_sample_count)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
struct {
|
||||
size_t read_buffer_size;
|
||||
|
@ -170,28 +258,20 @@ static bool M_Convert(const int32_t sample_id)
|
|||
.frame = nullptr,
|
||||
};
|
||||
|
||||
struct {
|
||||
struct {
|
||||
int32_t format;
|
||||
AVChannelLayout ch_layout;
|
||||
int32_t sample_rate;
|
||||
} src, dst;
|
||||
SwrContext *ctx;
|
||||
} swr = {};
|
||||
|
||||
M_SWR_CONTEXT swr = {};
|
||||
int32_t error_code;
|
||||
|
||||
unsigned char *read_buffer = av_malloc(av.read_buffer_size);
|
||||
if (!read_buffer) {
|
||||
uint8_t *const read_buffer = av_malloc(av.read_buffer_size);
|
||||
if (read_buffer == nullptr) {
|
||||
error_code = AVERROR(ENOMEM);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
AUDIO_AV_BUFFER av_buf = {
|
||||
.data = sample->original_data,
|
||||
.ptr = sample->original_data,
|
||||
.size = sample->original_size,
|
||||
.remaining = sample->original_size,
|
||||
.data = original_data,
|
||||
.ptr = original_data,
|
||||
.size = original_size,
|
||||
.remaining = original_size,
|
||||
};
|
||||
|
||||
av.avio_context = avio_alloc_context(
|
||||
|
@ -200,8 +280,7 @@ static bool M_Convert(const int32_t sample_id)
|
|||
|
||||
av.format_ctx = avformat_alloc_context();
|
||||
av.format_ctx->pb = av.avio_context;
|
||||
error_code =
|
||||
avformat_open_input(&av.format_ctx, "dummy_filename", nullptr, nullptr);
|
||||
error_code = avformat_open_input(&av.format_ctx, "mem:", nullptr, nullptr);
|
||||
if (error_code != 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -219,19 +298,19 @@ static bool M_Convert(const int32_t sample_id)
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!av.stream) {
|
||||
if (av.stream == nullptr) {
|
||||
error_code = AVERROR_STREAM_NOT_FOUND;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
av.codec = avcodec_find_decoder(av.stream->codecpar->codec_id);
|
||||
if (!av.codec) {
|
||||
if (av.codec == nullptr) {
|
||||
error_code = AVERROR_DEMUXER_NOT_FOUND;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
av.codec_ctx = avcodec_alloc_context3(av.codec);
|
||||
if (!av.codec_ctx) {
|
||||
if (av.codec_ctx == nullptr) {
|
||||
error_code = AVERROR(ENOMEM);
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -248,166 +327,134 @@ static bool M_Convert(const int32_t sample_id)
|
|||
}
|
||||
|
||||
av.packet = av_packet_alloc();
|
||||
if (!av.packet) {
|
||||
if (av.packet == nullptr) {
|
||||
error_code = AVERROR(ENOMEM);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
av.frame = av_frame_alloc();
|
||||
if (!av.frame) {
|
||||
if (av.frame == nullptr) {
|
||||
error_code = AVERROR(ENOMEM);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
error_code = av_read_frame(av.format_ctx, av.packet);
|
||||
if (error_code == AVERROR_EOF) {
|
||||
av_packet_unref(av.packet);
|
||||
error_code = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (error_code < 0) {
|
||||
av_packet_unref(av.packet);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error_code = avcodec_send_packet(av.codec_ctx, av.packet);
|
||||
if (error_code < 0) {
|
||||
av_packet_unref(av.packet);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (swr.ctx == nullptr) {
|
||||
swr.src.sample_rate = av.codec_ctx->sample_rate;
|
||||
swr.src.ch_layout = av.codec_ctx->ch_layout;
|
||||
swr.src.format = av.codec_ctx->sample_fmt;
|
||||
swr.dst.sample_rate = AUDIO_WORKING_RATE;
|
||||
av_channel_layout_default(&swr.dst.ch_layout, 1);
|
||||
swr.dst.format = Audio_GetAVAudioFormat(AUDIO_WORKING_FORMAT);
|
||||
swr_alloc_set_opts2(
|
||||
&swr.ctx, &swr.dst.ch_layout, swr.dst.format,
|
||||
swr.dst.sample_rate, &swr.src.ch_layout, swr.src.format,
|
||||
swr.src.sample_rate, 0, 0);
|
||||
if (swr.ctx == nullptr) {
|
||||
av_packet_unref(av.packet);
|
||||
error_code = AVERROR(ENOMEM);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error_code = swr_init(swr.ctx);
|
||||
if (error_code != 0) {
|
||||
av_packet_unref(av.packet);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
error_code = avcodec_receive_frame(av.codec_ctx, av.frame);
|
||||
if (error_code == AVERROR(EAGAIN)) {
|
||||
av_frame_unref(av.frame);
|
||||
break;
|
||||
}
|
||||
|
||||
if (error_code < 0) {
|
||||
av_packet_unref(av.packet);
|
||||
av_frame_unref(av.frame);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
uint8_t *out_buffer = nullptr;
|
||||
const int32_t out_samples =
|
||||
swr_get_out_samples(swr.ctx, av.frame->nb_samples);
|
||||
av_samples_alloc(
|
||||
&out_buffer, nullptr, swr.dst.ch_layout.nb_channels,
|
||||
out_samples, swr.dst.format, 1);
|
||||
int32_t resampled_size = swr_convert(
|
||||
swr.ctx, &out_buffer, out_samples,
|
||||
(const uint8_t **)av.frame->data, av.frame->nb_samples);
|
||||
while (resampled_size > 0) {
|
||||
int32_t out_buffer_size = av_samples_get_buffer_size(
|
||||
nullptr, swr.dst.ch_layout.nb_channels, resampled_size,
|
||||
swr.dst.format, 1);
|
||||
|
||||
if (out_buffer_size > 0) {
|
||||
working_buffer = Memory_Realloc(
|
||||
working_buffer, working_buffer_size + out_buffer_size);
|
||||
if (out_buffer) {
|
||||
memcpy(
|
||||
(uint8_t *)working_buffer + working_buffer_size,
|
||||
out_buffer, out_buffer_size);
|
||||
}
|
||||
working_buffer_size += out_buffer_size;
|
||||
}
|
||||
|
||||
resampled_size =
|
||||
swr_convert(swr.ctx, &out_buffer, out_samples, nullptr, 0);
|
||||
}
|
||||
|
||||
av_freep(&out_buffer);
|
||||
av_frame_unref(av.frame);
|
||||
}
|
||||
|
||||
swr.src.sample_rate = av.codec_ctx->sample_rate;
|
||||
swr.src.ch_layout = av.codec_ctx->ch_layout;
|
||||
swr.src.format = av.codec_ctx->sample_fmt;
|
||||
swr.dst.sample_rate = AUDIO_WORKING_RATE;
|
||||
av_channel_layout_default(&swr.dst.ch_layout, dst_channel_count);
|
||||
swr.dst.format = Audio_GetAVAudioFormat(AUDIO_WORKING_FORMAT);
|
||||
swr_alloc_set_opts2(
|
||||
&swr.ctx, &swr.dst.ch_layout, swr.dst.format, swr.dst.sample_rate,
|
||||
&swr.src.ch_layout, swr.src.format, swr.src.sample_rate, 0, 0);
|
||||
if (swr.ctx == nullptr) {
|
||||
av_packet_unref(av.packet);
|
||||
error_code = AVERROR(ENOMEM);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
int32_t sample_format_bytes = av_get_bytes_per_sample(swr.dst.format);
|
||||
sample->num_samples = working_buffer_size / sample_format_bytes
|
||||
/ swr.dst.ch_layout.nb_channels;
|
||||
sample->channels = swr.src.ch_layout.nb_channels;
|
||||
sample->sample_data = working_buffer;
|
||||
result = true;
|
||||
error_code = swr_init(swr.ctx);
|
||||
if (error_code != 0) {
|
||||
av_packet_unref(av.packet);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
const clock_t time_end = clock();
|
||||
const double time_delta =
|
||||
(((double)(time_end - time_start)) / CLOCKS_PER_SEC) * 1000.0f;
|
||||
LOG_DEBUG(
|
||||
"Sample %d decoded (%.0f ms)", sample_id, sample->original_size,
|
||||
time_delta);
|
||||
while ((error_code = av_read_frame(av.format_ctx, av.packet)) >= 0) {
|
||||
M_DecodePacket(av.codec_ctx, av.packet, av.frame, &swr);
|
||||
av_packet_unref(av.packet);
|
||||
if (error_code < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (av.codec_ctx != nullptr) {
|
||||
M_DecodePacket(av.codec_ctx, nullptr, av.frame, &swr);
|
||||
}
|
||||
|
||||
if (error_code == AVERROR_EOF) {
|
||||
error_code = 0;
|
||||
} else if (error_code < 0) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (out_size != nullptr) {
|
||||
*out_size = swr.working_buffer_size;
|
||||
}
|
||||
if (out_sample_count != nullptr) {
|
||||
*out_sample_count = (int32_t)swr.working_buffer_size
|
||||
/ av_get_bytes_per_sample(swr.dst.format)
|
||||
/ swr.dst.ch_layout.nb_channels;
|
||||
}
|
||||
if (out_sample_data != nullptr) {
|
||||
*out_sample_data = swr.working_buffer;
|
||||
} else {
|
||||
Memory_FreePointer(&swr.working_buffer);
|
||||
}
|
||||
result = true;
|
||||
|
||||
cleanup:
|
||||
if (error_code != 0) {
|
||||
LOG_ERROR(
|
||||
"Error while opening sample ID %d: %s", sample_id,
|
||||
av_err2str(error_code));
|
||||
LOG_ERROR("Error while decoding sample: %s", av_err2str(error_code));
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
if (out_size != nullptr) {
|
||||
*out_size = 0;
|
||||
}
|
||||
if (out_sample_count != nullptr) {
|
||||
*out_sample_count = 0;
|
||||
}
|
||||
if (out_sample_data != nullptr) {
|
||||
*out_sample_data = nullptr;
|
||||
}
|
||||
Memory_FreePointer(&swr.working_buffer);
|
||||
}
|
||||
|
||||
if (swr.ctx) {
|
||||
swr_free(&swr.ctx);
|
||||
}
|
||||
|
||||
if (av.frame) {
|
||||
av_frame_free(&av.frame);
|
||||
}
|
||||
|
||||
if (av.packet) {
|
||||
av_packet_free(&av.packet);
|
||||
}
|
||||
|
||||
av.codec = nullptr;
|
||||
|
||||
if (!result) {
|
||||
sample->sample_data = nullptr;
|
||||
sample->original_data = nullptr;
|
||||
sample->original_size = 0;
|
||||
sample->num_samples = 0;
|
||||
sample->channels = 0;
|
||||
Memory_FreePointer(&working_buffer);
|
||||
}
|
||||
|
||||
if (av.codec_ctx) {
|
||||
avcodec_free_context(&av.codec_ctx);
|
||||
}
|
||||
|
||||
if (av.format_ctx) {
|
||||
avformat_close_input(&av.format_ctx);
|
||||
}
|
||||
|
||||
if (av.avio_context) {
|
||||
av_freep(&av.avio_context->buffer);
|
||||
avio_context_free(&av.avio_context);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool M_ConvertSample(const int32_t sample_id)
|
||||
{
|
||||
ASSERT(sample_id >= 0 && sample_id < m_LoadedSamplesCount);
|
||||
AUDIO_SAMPLE *const sample = &m_LoadedSamples[sample_id];
|
||||
if (sample->sample_data != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t num_samples;
|
||||
BENCHMARK benchmark = Benchmark_Start();
|
||||
|
||||
const bool result = M_ConvertRawData(
|
||||
(uint8_t *)sample->original_data, sample->original_size,
|
||||
AUDIO_WORKING_RATE, Audio_GetAVAudioFormat(AUDIO_WORKING_FORMAT), 1,
|
||||
(uint8_t **)&sample->sample_data, nullptr, &num_samples);
|
||||
|
||||
char buffer[80];
|
||||
sprintf(buffer, "sample %d decoded", sample_id);
|
||||
Benchmark_End(&benchmark, buffer);
|
||||
|
||||
sample->channels = 1;
|
||||
sample->num_samples = num_samples;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -553,7 +600,7 @@ int32_t Audio_Sample_Play(
|
|||
continue;
|
||||
}
|
||||
|
||||
M_Convert(sample_id);
|
||||
M_ConvertSample(sample_id);
|
||||
|
||||
sound->is_used = true;
|
||||
sound->is_playing = true;
|
||||
|
|
|
@ -283,56 +283,56 @@ MYFILE *File_Open(const char *path, FILE_OPEN_MODE mode)
|
|||
return file;
|
||||
}
|
||||
|
||||
void File_ReadData(MYFILE *const file, void *const data, const size_t size)
|
||||
bool File_ReadData(MYFILE *const file, void *const data, const size_t size)
|
||||
{
|
||||
fread(data, size, 1, file->fp);
|
||||
return fread(data, size, 1, file->fp) == 1;
|
||||
}
|
||||
|
||||
void File_ReadItems(
|
||||
bool File_ReadItems(
|
||||
MYFILE *const file, void *data, const size_t count, const size_t item_size)
|
||||
{
|
||||
fread(data, item_size, count, file->fp);
|
||||
return fread(data, item_size, count, file->fp) == count;
|
||||
}
|
||||
|
||||
int8_t File_ReadS8(MYFILE *const file)
|
||||
{
|
||||
int8_t result;
|
||||
fread(&result, sizeof(result), 1, file->fp);
|
||||
File_ReadData(file, &result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
int16_t File_ReadS16(MYFILE *const file)
|
||||
{
|
||||
int16_t result;
|
||||
fread(&result, sizeof(result), 1, file->fp);
|
||||
File_ReadData(file, &result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t File_ReadS32(MYFILE *const file)
|
||||
{
|
||||
int32_t result;
|
||||
fread(&result, sizeof(result), 1, file->fp);
|
||||
File_ReadData(file, &result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t File_ReadU8(MYFILE *const file)
|
||||
{
|
||||
uint8_t result;
|
||||
fread(&result, sizeof(result), 1, file->fp);
|
||||
File_ReadData(file, &result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint16_t File_ReadU16(MYFILE *const file)
|
||||
{
|
||||
uint16_t result;
|
||||
fread(&result, sizeof(result), 1, file->fp);
|
||||
File_ReadData(file, &result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t File_ReadU32(MYFILE *const file)
|
||||
{
|
||||
uint32_t result;
|
||||
fread(&result, sizeof(result), 1, file->fp);
|
||||
File_ReadData(file, &result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -108,12 +108,11 @@ static void M_LoadCommonSettings(
|
|||
if (tmp_value != nullptr && tmp_value->type == JSON_TYPE_ARRAY) {
|
||||
const JSON_ARRAY *const tmp_arr = JSON_ValueAsArray(tmp_value);
|
||||
const RGB_F color = {
|
||||
JSON_ArrayGetDouble(tmp_arr, 0, JSON_INVALID_NUMBER),
|
||||
JSON_ArrayGetDouble(tmp_arr, 1, JSON_INVALID_NUMBER),
|
||||
JSON_ArrayGetDouble(tmp_arr, 2, JSON_INVALID_NUMBER),
|
||||
JSON_ArrayGetDouble(tmp_arr, 0, -1.0),
|
||||
JSON_ArrayGetDouble(tmp_arr, 1, -1.0),
|
||||
JSON_ArrayGetDouble(tmp_arr, 2, -1.0),
|
||||
};
|
||||
if (color.r != JSON_INVALID_NUMBER && color.g != JSON_INVALID_NUMBER
|
||||
&& color.b != JSON_INVALID_NUMBER) {
|
||||
if (color.r >= 0.0 && color.g >= 0.0 && color.b >= 0.0) {
|
||||
settings->water_color.is_present = true;
|
||||
settings->water_color.value = (RGB_888) {
|
||||
color.r * 255.0f,
|
||||
|
@ -515,7 +514,8 @@ static void M_LoadTitleLevel(JSON_OBJECT *obj, GAME_FLOW *const gf)
|
|||
JSON_OBJECT *title_obj = JSON_ObjectGetObject(obj, "title");
|
||||
if (title_obj != nullptr) {
|
||||
gf->title_level = Memory_Alloc(sizeof(GF_LEVEL));
|
||||
M_LoadLevel(title_obj, gf, gf->title_level, 0, GFL_TITLE);
|
||||
M_LoadLevel(
|
||||
title_obj, gf, gf->title_level, 0, (void *)(intptr_t)GFL_TITLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#include "debug.h"
|
||||
#include "filesystem.h"
|
||||
#include "game/game_flow.h"
|
||||
#include "game/game_string.h"
|
||||
#include "game/game_string_table.h"
|
||||
#include "game/game_string_table/priv.h"
|
||||
#include "game/objects/names.h"
|
||||
#include "game/shell.h"
|
||||
#include "log.h"
|
||||
#include "memory.h"
|
||||
|
||||
|
@ -11,8 +13,6 @@
|
|||
|
||||
typedef void (*M_LOAD_STRING_FUNC)(const char *, const char *);
|
||||
|
||||
GS_FILE g_GST_File = {};
|
||||
|
||||
static struct {
|
||||
GAME_OBJECT_ID target_object_id;
|
||||
GAME_OBJECT_ID source_object_id;
|
||||
|
@ -27,6 +27,8 @@ static struct {
|
|||
{ .target_object_id = NO_OBJECT },
|
||||
};
|
||||
|
||||
static VECTOR *m_GST_Layers = nullptr;
|
||||
|
||||
static void M_Apply(const GS_TABLE *table);
|
||||
static void M_ApplyLevelTitles(
|
||||
const GS_FILE *gs_file, GF_LEVEL_TABLE_TYPE level_table_type);
|
||||
|
@ -90,17 +92,19 @@ static void M_ApplyLevelTitles(
|
|||
GF_GetLevelTable(level_table_type);
|
||||
const GS_LEVEL_TABLE *const gs_level_table =
|
||||
&gs_file->level_tables[level_table_type];
|
||||
if (gs_level_table->count == 0) {
|
||||
return;
|
||||
}
|
||||
ASSERT(gs_level_table->count == level_table->count);
|
||||
for (int32_t i = 0; i < level_table->count; i++) {
|
||||
GF_SetLevelTitle(
|
||||
&level_table->levels[i], gs_level_table->entries[i].title);
|
||||
}
|
||||
}
|
||||
|
||||
void GameStringTable_Apply(const GF_LEVEL *const level)
|
||||
static void M_ApplyLayer(
|
||||
const GF_LEVEL *const level, const GS_FILE *const gs_file)
|
||||
{
|
||||
const GS_FILE *const gs_file = &g_GST_File;
|
||||
|
||||
Object_ResetNames();
|
||||
M_Apply(&gs_file->global);
|
||||
|
||||
for (int32_t i = 0; i < GFLT_NUMBER_OF; i++) {
|
||||
|
@ -120,16 +124,50 @@ void GameStringTable_Apply(const GF_LEVEL *const level)
|
|||
}
|
||||
}
|
||||
|
||||
if (gs_level_table != nullptr) {
|
||||
if (gs_level_table != nullptr && gs_level_table->count != 0) {
|
||||
ASSERT(level->num >= 0);
|
||||
ASSERT(level->num < gs_level_table->count);
|
||||
M_Apply(&gs_level_table->entries[level->num].table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameStringTable_Apply(const GF_LEVEL *const level)
|
||||
{
|
||||
Object_ResetNames();
|
||||
ASSERT(m_GST_Layers != nullptr);
|
||||
for (int32_t i = 0; i < m_GST_Layers->count; i++) {
|
||||
const GS_FILE *const gs_file = *(GS_FILE **)Vector_Get(m_GST_Layers, i);
|
||||
M_ApplyLayer(level, gs_file);
|
||||
}
|
||||
M_DoObjectAliases();
|
||||
}
|
||||
|
||||
void GameStringTable_Init(void)
|
||||
{
|
||||
m_GST_Layers = Vector_Create(sizeof(GS_FILE *));
|
||||
}
|
||||
|
||||
void GameStringTable_Shutdown(void)
|
||||
{
|
||||
GS_File_Free(&g_GST_File);
|
||||
if (m_GST_Layers != nullptr) {
|
||||
for (int32_t i = 0; i < m_GST_Layers->count; i++) {
|
||||
GS_FILE *const gs_file = *(GS_FILE **)Vector_Get(m_GST_Layers, i);
|
||||
GS_File_Free(gs_file);
|
||||
}
|
||||
Vector_Free(m_GST_Layers);
|
||||
m_GST_Layers = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GameStringTable_Load(const char *const path, const bool load_levels)
|
||||
{
|
||||
char *data = nullptr;
|
||||
if (!File_Load(path, &data, nullptr)) {
|
||||
Shell_ExitSystemFmt("failed to open strings file (path: %d)", path);
|
||||
}
|
||||
GS_FILE *gs_file = GS_File_CreateFromString(data, load_levels);
|
||||
ASSERT(m_GST_Layers != nullptr);
|
||||
Vector_Add(m_GST_Layers, &gs_file);
|
||||
Memory_FreePointer(&data);
|
||||
}
|
||||
|
|
|
@ -52,4 +52,5 @@ void GS_File_Free(GS_FILE *const gs_file)
|
|||
for (int32_t i = 0; i < GFLT_NUMBER_OF; i++) {
|
||||
M_FreeLevelsTable(&gs_file->level_tables[i]);
|
||||
}
|
||||
Memory_Free(gs_file);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "game/game_flow/enum.h"
|
||||
#include "vector.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -35,7 +36,7 @@ typedef struct {
|
|||
GS_LEVEL_TABLE level_tables[GFLT_NUMBER_OF];
|
||||
} GS_FILE;
|
||||
|
||||
extern GS_FILE g_GST_File;
|
||||
|
||||
void GS_Table_Free(GS_TABLE *gs_table);
|
||||
|
||||
GS_FILE *GS_File_CreateFromString(const char *data, bool load_levels);
|
||||
void GS_File_Free(GS_FILE *gs_file);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
#include "filesystem.h"
|
||||
#include "game/game_flow.h"
|
||||
#include "game/game_string_table.h"
|
||||
#include "game/game_string_table/priv.h"
|
||||
|
@ -130,24 +129,14 @@ static void M_LoadLevelsFromJSON(
|
|||
}
|
||||
}
|
||||
|
||||
void GameStringTable_LoadFromFile(const char *const path)
|
||||
GS_FILE *GS_File_CreateFromString(
|
||||
const char *const data, const bool load_levels)
|
||||
{
|
||||
char *data = nullptr;
|
||||
if (!File_Load(path, &data, nullptr)) {
|
||||
Shell_ExitSystemFmt("failed to open strings file (path: %d)", path);
|
||||
}
|
||||
GameStringTable_LoadFromString(data);
|
||||
Memory_FreePointer(&data);
|
||||
}
|
||||
|
||||
void GameStringTable_LoadFromString(const char *const data)
|
||||
{
|
||||
GameStringTable_Shutdown();
|
||||
|
||||
JSON_VALUE *root = nullptr;
|
||||
GS_FILE *const gs_file = Memory_Alloc(sizeof(GS_FILE));
|
||||
|
||||
JSON_PARSE_RESULT parse_result;
|
||||
root = JSON_ParseEx(
|
||||
|
||||
JSON_VALUE *root = JSON_ParseEx(
|
||||
data, strlen(data), JSON_PARSE_FLAGS_ALLOW_JSON5, nullptr, nullptr,
|
||||
&parse_result);
|
||||
if (root == nullptr) {
|
||||
|
@ -157,15 +146,16 @@ void GameStringTable_LoadFromString(const char *const data)
|
|||
parse_result.error_line_no, parse_result.error_row_no, data);
|
||||
}
|
||||
|
||||
GS_FILE *const gs_file = &g_GST_File;
|
||||
JSON_OBJECT *root_obj = JSON_ValueAsObject(root);
|
||||
M_LoadTableFromJSON(root_obj, &gs_file->global);
|
||||
M_LoadLevelsFromJSON(root_obj, gs_file, "levels", GFLT_MAIN);
|
||||
M_LoadLevelsFromJSON(root_obj, gs_file, "demos", GFLT_DEMOS);
|
||||
M_LoadLevelsFromJSON(root_obj, gs_file, "cutscenes", GFLT_CUTSCENES);
|
||||
|
||||
if (load_levels) {
|
||||
M_LoadLevelsFromJSON(root_obj, gs_file, "levels", GFLT_MAIN);
|
||||
M_LoadLevelsFromJSON(root_obj, gs_file, "demos", GFLT_DEMOS);
|
||||
M_LoadLevelsFromJSON(root_obj, gs_file, "cutscenes", GFLT_CUTSCENES);
|
||||
}
|
||||
if (root != nullptr) {
|
||||
JSON_ValueFree(root);
|
||||
root = nullptr;
|
||||
}
|
||||
return gs_file;
|
||||
}
|
||||
|
|
|
@ -219,15 +219,11 @@ int32_t Item_GlobalReplace(
|
|||
{
|
||||
int32_t changed = 0;
|
||||
|
||||
for (int32_t i = 0; i < Room_GetCount(); i++) {
|
||||
int16_t item_num = Room_Get(i)->item_num;
|
||||
while (item_num != NO_ITEM) {
|
||||
ITEM *const item = &m_Items[item_num];
|
||||
if (item->object_id == src_obj_id) {
|
||||
item->object_id = dst_obj_id;
|
||||
changed++;
|
||||
}
|
||||
item_num = item->next_item;
|
||||
for (int32_t item_num = 0; item_num < m_MaxUsedItemCount; item_num++) {
|
||||
ITEM *const item = &m_Items[item_num];
|
||||
if (item->object_id == src_obj_id) {
|
||||
item->object_id = dst_obj_id;
|
||||
changed++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
#include "game/box.h"
|
||||
#include "game/items.h"
|
||||
#include "game/objects/general/door.h"
|
||||
|
||||
#include "game/game_buf.h"
|
||||
#include "game/lara/common.h"
|
||||
#include "game/objects/common.h"
|
||||
#include "game/room.h"
|
||||
#include "global/vars.h"
|
||||
#include "game/pathing.h"
|
||||
#include "game/rooms.h"
|
||||
|
||||
#include <libtrx/game/collision.h>
|
||||
#include <libtrx/game/game_buf.h>
|
||||
#include <libtrx/game/objects/general/door.h>
|
||||
#include <libtrx/utils.h>
|
||||
typedef struct {
|
||||
SECTOR *sector;
|
||||
SECTOR old_sector;
|
||||
int16_t box_num;
|
||||
} DOORPOS_DATA;
|
||||
|
||||
typedef struct {
|
||||
DOORPOS_DATA d1;
|
||||
|
@ -22,7 +24,6 @@ static SECTOR *M_GetRoomRelSector(
|
|||
static void M_InitialisePortal(
|
||||
const ROOM *room, const ITEM *item, int32_t sector_dx, int32_t sector_dz,
|
||||
DOORPOS_DATA *door_pos);
|
||||
|
||||
static bool M_LaraDoorCollision(const SECTOR *sector);
|
||||
static void M_Check(DOORPOS_DATA *d);
|
||||
static void M_Shut(DOORPOS_DATA *d);
|
||||
|
@ -42,38 +43,17 @@ static SECTOR *M_GetRoomRelSector(
|
|||
return Room_GetUnitSector(room, sector.x, sector.z);
|
||||
}
|
||||
|
||||
static void M_InitialisePortal(
|
||||
const ROOM *const room, const ITEM *const item, const int32_t sector_dx,
|
||||
const int32_t sector_dz, DOORPOS_DATA *const door_pos)
|
||||
{
|
||||
door_pos->sector = M_GetRoomRelSector(room, item, sector_dx, sector_dz);
|
||||
|
||||
const SECTOR *sector = door_pos->sector;
|
||||
|
||||
const int16_t room_num = sector->portal_room.wall;
|
||||
if (room_num != NO_ROOM) {
|
||||
sector =
|
||||
M_GetRoomRelSector(Room_Get(room_num), item, sector_dx, sector_dz);
|
||||
}
|
||||
|
||||
int16_t box_num = sector->box;
|
||||
if (!(Box_GetBox(box_num)->overlap_index & BOX_BLOCKABLE)) {
|
||||
box_num = NO_BOX;
|
||||
}
|
||||
door_pos->block = box_num;
|
||||
door_pos->old_sector = *door_pos->sector;
|
||||
}
|
||||
|
||||
static bool M_LaraDoorCollision(const SECTOR *const sector)
|
||||
{
|
||||
// Check if Lara is on the same tile as the invisible block.
|
||||
if (g_LaraItem == nullptr) {
|
||||
const ITEM *const lara = Lara_GetItem();
|
||||
if (lara == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int16_t room_num = g_LaraItem->room_num;
|
||||
const SECTOR *const lara_sector = Room_GetSector(
|
||||
g_LaraItem->pos.x, g_LaraItem->pos.y, g_LaraItem->pos.z, &room_num);
|
||||
int16_t room_num = lara->room_num;
|
||||
const SECTOR *const lara_sector =
|
||||
Room_GetSector(lara->pos.x, lara->pos.y, lara->pos.z, &room_num);
|
||||
return lara_sector == sector;
|
||||
}
|
||||
|
||||
|
@ -90,22 +70,22 @@ static void M_Check(DOORPOS_DATA *const d)
|
|||
|
||||
static void M_Shut(DOORPOS_DATA *const d)
|
||||
{
|
||||
// Change the level geometry so that the door tile is impassable.
|
||||
SECTOR *const sector = d->sector;
|
||||
if (sector == nullptr) {
|
||||
if (d->sector == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
sector->idx = 0;
|
||||
sector->box = NO_BOX;
|
||||
sector->floor.height = NO_HEIGHT;
|
||||
sector->ceiling.height = NO_HEIGHT;
|
||||
sector->floor.height = NO_HEIGHT;
|
||||
sector->floor.tilt = 0;
|
||||
sector->ceiling.tilt = 0;
|
||||
sector->portal_room.sky = NO_ROOM;
|
||||
sector->portal_room.pit = NO_ROOM;
|
||||
sector->portal_room.sky = NO_ROOM_NEG;
|
||||
sector->portal_room.pit = NO_ROOM_NEG;
|
||||
sector->portal_room.wall = NO_ROOM;
|
||||
|
||||
const int16_t box_num = d->block;
|
||||
const int16_t box_num = d->box_num;
|
||||
if (box_num != NO_BOX) {
|
||||
Box_GetBox(box_num)->overlap_index |= BOX_BLOCKED;
|
||||
}
|
||||
|
@ -113,34 +93,55 @@ static void M_Shut(DOORPOS_DATA *const d)
|
|||
|
||||
static void M_Open(DOORPOS_DATA *const d)
|
||||
{
|
||||
// Restore the level geometry so that the door tile is passable.
|
||||
SECTOR *const sector = d->sector;
|
||||
if (!sector) {
|
||||
if (d->sector == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
*sector = d->old_sector;
|
||||
*d->sector = d->old_sector;
|
||||
|
||||
const int16_t box_num = d->block;
|
||||
const int16_t box_num = d->box_num;
|
||||
if (box_num != NO_BOX) {
|
||||
Box_GetBox(box_num)->overlap_index &= ~BOX_BLOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
static void M_InitialisePortal(
|
||||
const ROOM *const room, const ITEM *const item, const int32_t sector_dx,
|
||||
const int32_t sector_dz, DOORPOS_DATA *const door_pos)
|
||||
{
|
||||
door_pos->sector = M_GetRoomRelSector(room, item, sector_dx, sector_dz);
|
||||
|
||||
const SECTOR *sector = door_pos->sector;
|
||||
|
||||
const int16_t room_num = door_pos->sector->portal_room.wall;
|
||||
if (room_num != NO_ROOM) {
|
||||
sector =
|
||||
M_GetRoomRelSector(Room_Get(room_num), item, sector_dx, sector_dz);
|
||||
}
|
||||
|
||||
int16_t box_num = sector->box;
|
||||
const BOX_INFO *const box = Box_GetBox(box_num);
|
||||
if ((box->overlap_index & BOX_BLOCKABLE) == 0) {
|
||||
box_num = NO_BOX;
|
||||
}
|
||||
door_pos->box_num = box_num;
|
||||
door_pos->old_sector = *door_pos->sector;
|
||||
}
|
||||
|
||||
static void M_Setup(OBJECT *const obj)
|
||||
{
|
||||
obj->initialise_func = M_Initialise;
|
||||
obj->control_func = M_Control;
|
||||
obj->draw_func = Object_DrawUnclippedItem;
|
||||
obj->collision_func = Door_Collision;
|
||||
obj->save_anim = 1;
|
||||
obj->save_flags = 1;
|
||||
obj->save_anim = 1;
|
||||
}
|
||||
|
||||
static void M_Initialise(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = Item_Get(item_num);
|
||||
DOOR_DATA *const door = GameBuf_Alloc(sizeof(DOOR_DATA), GBUF_ITEM_DATA);
|
||||
DOOR_DATA *door = GameBuf_Alloc(sizeof(DOOR_DATA), GBUF_ITEM_DATA);
|
||||
item->data = door;
|
||||
|
||||
int32_t dx = 0;
|
||||
|
@ -159,7 +160,7 @@ static void M_Initialise(const int16_t item_num)
|
|||
const ROOM *room = Room_Get(room_num);
|
||||
M_InitialisePortal(room, item, dx, dz, &door->d1);
|
||||
|
||||
if (room->flipped_room == -1) {
|
||||
if (room->flipped_room == NO_ROOM_NEG) {
|
||||
door->d1flip.sector = nullptr;
|
||||
} else {
|
||||
room = Room_Get(room->flipped_room);
|
||||
|
@ -173,30 +174,29 @@ static void M_Initialise(const int16_t item_num)
|
|||
if (room_num == NO_ROOM) {
|
||||
door->d2.sector = nullptr;
|
||||
door->d2flip.sector = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
room = Room_Get(room_num);
|
||||
M_InitialisePortal(room, item, 0, 0, &door->d2);
|
||||
if (room->flipped_room == -1) {
|
||||
door->d2flip.sector = nullptr;
|
||||
} else {
|
||||
room = Room_Get(room->flipped_room);
|
||||
M_InitialisePortal(room, item, 0, 0, &door->d2flip);
|
||||
room = Room_Get(room_num);
|
||||
M_InitialisePortal(room, item, 0, 0, &door->d2);
|
||||
if (room->flipped_room == NO_ROOM_NEG) {
|
||||
door->d2flip.sector = nullptr;
|
||||
} else {
|
||||
room = Room_Get(room->flipped_room);
|
||||
M_InitialisePortal(room, item, 0, 0, &door->d2flip);
|
||||
}
|
||||
|
||||
M_Shut(&door->d2);
|
||||
M_Shut(&door->d2flip);
|
||||
|
||||
const int16_t prev_room = item->room_num;
|
||||
Item_NewRoom(item_num, room_num);
|
||||
item->room_num = prev_room;
|
||||
}
|
||||
|
||||
M_Shut(&door->d2);
|
||||
M_Shut(&door->d2flip);
|
||||
|
||||
const int16_t prev_room = item->room_num;
|
||||
Item_NewRoom(item_num, room_num);
|
||||
item->room_num = prev_room;
|
||||
}
|
||||
|
||||
static void M_Control(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = Item_Get(item_num);
|
||||
DOOR_DATA *door = item->data;
|
||||
DOOR_DATA *const door = item->data;
|
||||
|
||||
if (Item_IsTriggerActive(item)) {
|
||||
if (item->current_anim_state == DOOR_STATE_CLOSED) {
|
||||
|
@ -225,21 +225,25 @@ static void M_Control(const int16_t item_num)
|
|||
Item_Animate(item);
|
||||
}
|
||||
|
||||
void Door_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll)
|
||||
void Door_Collision(
|
||||
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
|
||||
{
|
||||
ITEM *const item = Item_Get(item_num);
|
||||
if (!Lara_TestBoundsCollide(item, coll->radius)) {
|
||||
|
||||
if (!Item_TestBoundsCollide(item, lara_item, coll->radius)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Collide_TestCollision(item, lara_item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (coll->enable_baddie_push) {
|
||||
if (item->current_anim_state != item->goal_anim_state) {
|
||||
Lara_Push(item, coll, coll->enable_hit, true);
|
||||
} else {
|
||||
Lara_Push(item, coll, false, true);
|
||||
}
|
||||
Lara_Push(
|
||||
item, coll,
|
||||
coll->enable_hit
|
||||
&& item->current_anim_state != item->goal_anim_state,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "debug.h"
|
||||
#include "filesystem.h"
|
||||
#include "game/output/common.h"
|
||||
#include "game/viewport.h"
|
||||
#include "log.h"
|
||||
#include "memory.h"
|
||||
|
@ -198,6 +199,18 @@ bool Output_LoadBackgroundFromFile(const char *const path)
|
|||
return result;
|
||||
}
|
||||
|
||||
void Output_ReloadBackgroundImage(void)
|
||||
{
|
||||
if (m_LastPath == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *prev = Memory_DupStr(m_LastPath);
|
||||
Output_UnloadBackground();
|
||||
Output_LoadBackgroundFromFile(prev);
|
||||
Memory_FreePointer(&prev);
|
||||
}
|
||||
|
||||
char *Output_GetLastBackgroundPath(void)
|
||||
{
|
||||
return m_LastPath;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
176
src/libtrx/game/ui/dialogs/sound_settings.c
Normal file
176
src/libtrx/game/ui/dialogs/sound_settings.c
Normal 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();
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -22,9 +22,9 @@ static void M_DownArrow(const UI_REQUESTER_STATE *s);
|
|||
static void M_UpArrow(const UI_REQUESTER_STATE *const s)
|
||||
{
|
||||
UI_BeginHide(s->vis_row == 0);
|
||||
UI_Spacer(0.0f, 4.0f);
|
||||
UI_Spacer(0.0f, TR_VERSION == 2 ? 6.0f : 4.0f);
|
||||
UI_BeginAnchor(0.5f, 0.5f);
|
||||
UI_BeginFixed(0.5f, 1.5f);
|
||||
UI_BeginFixed(0.5f, TR_VERSION == 2 ? 1.25f : 1.5f);
|
||||
UI_LabelEx("\\{arrow up}", (UI_LABEL_SETTINGS) { .scale = 0.7 });
|
||||
UI_EndFixed();
|
||||
UI_EndAnchor();
|
||||
|
@ -40,7 +40,7 @@ static void M_DownArrow(const UI_REQUESTER_STATE *const s)
|
|||
UI_EndFixed();
|
||||
UI_EndAnchor();
|
||||
UI_EndHide();
|
||||
UI_Spacer(0.0f, 4.0f);
|
||||
UI_Spacer(0.0f, TR_VERSION == 2 ? 6.0f : 4.0f);
|
||||
}
|
||||
|
||||
void UI_Requester_Init(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -39,8 +39,8 @@ char *File_GuessExtension(const char *path, const char **extensions);
|
|||
|
||||
MYFILE *File_Open(const char *path, FILE_OPEN_MODE mode);
|
||||
|
||||
void File_ReadData(MYFILE *file, void *data, size_t size);
|
||||
void File_ReadItems(MYFILE *file, void *data, size_t count, size_t item_size);
|
||||
bool File_ReadData(MYFILE *file, void *data, size_t size);
|
||||
bool File_ReadItems(MYFILE *file, void *data, size_t count, size_t item_size);
|
||||
int8_t File_ReadS8(MYFILE *file);
|
||||
int16_t File_ReadS16(MYFILE *file);
|
||||
int32_t File_ReadS32(MYFILE *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")
|
||||
|
@ -150,5 +152,6 @@ GS_DEFINE(DETAIL_TRAPEZOID_FILTER, "Trapezoid filter")
|
|||
GS_DEFINE(DETAIL_RENDER_MODE, "Render mode")
|
||||
GS_DEFINE(DETAIL_UI_TEXT_SCALE, "UI text scale")
|
||||
GS_DEFINE(DETAIL_UI_BAR_SCALE, "UI bar scale")
|
||||
GS_DEFINE(DETAIL_UI_SCROLL_WRAPAROUND, "UI scroll wrap")
|
||||
GS_DEFINE(PAGINATION_NAV, "%d / %d")
|
||||
GS_DEFINE(MISC_EMPTY_SLOT_FMT, "- EMPTY SLOT -")
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
void GameStringTable_LoadFromFile(const char *path);
|
||||
void GameStringTable_LoadFromString(const char *data);
|
||||
void GameStringTable_Apply(const GF_LEVEL *level);
|
||||
void GameStringTable_Init(void);
|
||||
void GameStringTable_Shutdown(void);
|
||||
|
||||
void GameStringTable_Load(const char *path, bool load_levels);
|
||||
void GameStringTable_Apply(const GF_LEVEL *level);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -36,6 +36,7 @@ void Object_SwapMesh(
|
|||
ANIM *Object_GetAnim(const OBJECT *obj, int32_t anim_idx);
|
||||
ANIM_BONE *Object_GetBone(const OBJECT *obj, int32_t bone_idx);
|
||||
|
||||
extern void Object_DrawUnclippedItem(const ITEM *item);
|
||||
extern void Object_DrawMesh(int32_t mesh_idx, int32_t clip, bool interpolated);
|
||||
|
||||
void Object_DrawInterpolatedObject(
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "../../engine/image.h"
|
||||
|
||||
bool Output_LoadBackgroundFromFile(const char *path);
|
||||
void Output_ReloadBackgroundImage(void);
|
||||
|
||||
extern bool Output_LoadBackgroundFromImage(const IMAGE *image);
|
||||
extern void Output_LoadBackgroundFromObject(void);
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
21
src/libtrx/include/libtrx/game/ui/dialogs/sound_settings.h
Normal file
21
src/libtrx/include/libtrx/game/ui/dialogs/sound_settings.h
Normal 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);
|
|
@ -176,6 +176,7 @@ sources = [
|
|||
'game/objects/general/bridge_flat.c',
|
||||
'game/objects/general/bridge_tilt1.c',
|
||||
'game/objects/general/bridge_tilt2.c',
|
||||
'game/objects/general/door.c',
|
||||
'game/objects/general/drawbridge.c',
|
||||
'game/objects/general/trapdoor.c',
|
||||
'game/objects/names.c',
|
||||
|
@ -216,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',
|
||||
|
|
|
@ -142,10 +142,10 @@ GF_COMMAND Game_Control(const bool demo_mode)
|
|||
if (g_Camera.type == CAM_CINEMATIC) {
|
||||
g_OverlayFlag = 0;
|
||||
} else if (g_OverlayFlag > 0) {
|
||||
if (g_Input.load) {
|
||||
g_OverlayFlag = -1;
|
||||
} else if (g_Input.save) {
|
||||
if (g_Input.save) {
|
||||
g_OverlayFlag = -2;
|
||||
} else if (g_Input.load) {
|
||||
g_OverlayFlag = -1;
|
||||
} else {
|
||||
g_OverlayFlag = 0;
|
||||
}
|
||||
|
|
|
@ -694,7 +694,6 @@ static GF_COMMAND M_Control(INV_RING *const ring)
|
|||
InvRing_MotionSetup(ring, RNG_CLOSING_ITEM, RNG_DESELECT, 0);
|
||||
g_Input = (INPUT_STATE) {};
|
||||
g_InputDB = (INPUT_STATE) {};
|
||||
|
||||
if (ring->mode == INV_LOAD_MODE || ring->mode == INV_SAVE_MODE
|
||||
|| ring->mode == INV_SAVE_CRYSTAL_MODE) {
|
||||
InvRing_MotionSetup(
|
||||
|
@ -713,7 +712,7 @@ static GF_COMMAND M_Control(INV_RING *const ring)
|
|||
}
|
||||
|
||||
if (ring->mode == INV_TITLE_MODE
|
||||
&& ((inv_item->object_id == O_DETAIL_OPTION)
|
||||
&& (inv_item->object_id == O_DETAIL_OPTION
|
||||
|| inv_item->object_id == O_SOUND_OPTION
|
||||
|| inv_item->object_id == O_CONTROL_OPTION
|
||||
|| inv_item->object_id == O_GAMMA_OPTION)) {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ void Object_DrawDummyItem(const ITEM *item);
|
|||
void Object_DrawSpriteItem(const ITEM *item);
|
||||
void Object_DrawPickupItem(const ITEM *item);
|
||||
void Object_DrawAnimatingItem(const ITEM *item);
|
||||
void Object_DrawUnclippedItem(const ITEM *item);
|
||||
void Object_SetMeshReflective(
|
||||
GAME_OBJECT_ID obj_id, int32_t mesh_idx, bool enabled);
|
||||
void Object_SetReflective(GAME_OBJECT_ID obj_id, bool enabled);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -50,6 +50,7 @@ typedef enum {
|
|||
OPTION_BRIGHTNESS,
|
||||
OPTION_UI_TEXT_SCALE,
|
||||
OPTION_UI_BAR_SCALE,
|
||||
OPTION_UI_SCROLL_WRAPAROUND,
|
||||
OPTION_RENDER_MODE,
|
||||
OPTION_RESOLUTION,
|
||||
OPTION_TRAPEZOID_FILTER,
|
||||
|
@ -95,6 +96,8 @@ static const GRAPHICS_OPTION_ROW m_GfxOptionRows[] = {
|
|||
GS_ID(DETAIL_FLOAT_FMT) },
|
||||
{ OPTION_UI_BAR_SCALE, GS_ID(DETAIL_UI_BAR_SCALE),
|
||||
GS_ID(DETAIL_FLOAT_FMT) },
|
||||
{ OPTION_UI_SCROLL_WRAPAROUND, GS_ID(DETAIL_UI_SCROLL_WRAPAROUND),
|
||||
GS_ID(MISC_ON) },
|
||||
{ OPTION_RENDER_MODE, GS_ID(DETAIL_RENDER_MODE), GS_ID(DETAIL_STRING_FMT) },
|
||||
{ OPTION_RESOLUTION, GS_ID(DETAIL_RESOLUTION),
|
||||
GS_ID(DETAIL_RESOLUTION_FMT) },
|
||||
|
@ -177,6 +180,8 @@ static void M_MenuUp(void)
|
|||
}
|
||||
m_GraphicsMenu.cur_option--;
|
||||
M_UpdateText();
|
||||
} else if (g_Config.ui.enable_wraparound) {
|
||||
M_Reinitialize(m_GfxOptionRows[OPTION_NUMBER_OF - 1].option_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -191,6 +196,8 @@ static void M_MenuDown(void)
|
|||
}
|
||||
m_GraphicsMenu.cur_option++;
|
||||
M_UpdateText();
|
||||
} else if (g_Config.ui.enable_wraparound) {
|
||||
M_Reinitialize(m_GfxOptionRows[0].option_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,6 +330,10 @@ static void M_UpdateArrows(
|
|||
m_HideArrowLeft = g_Config.ui.bar_scale <= CONFIG_MIN_BAR_SCALE;
|
||||
m_HideArrowRight = g_Config.ui.bar_scale >= CONFIG_MAX_BAR_SCALE;
|
||||
break;
|
||||
case OPTION_UI_SCROLL_WRAPAROUND:
|
||||
m_HideArrowLeft = !g_Config.ui.enable_wraparound;
|
||||
m_HideArrowRight = g_Config.ui.enable_wraparound;
|
||||
break;
|
||||
case OPTION_RENDER_MODE:
|
||||
local_right_arrow_offset = RIGHT_ARROW_OFFSET_MAX;
|
||||
m_HideArrowLeft = false;
|
||||
|
@ -483,6 +494,11 @@ static void M_ChangeTextOption(
|
|||
Text_ChangeText(value_text, buf);
|
||||
break;
|
||||
|
||||
case OPTION_UI_SCROLL_WRAPAROUND:
|
||||
bool is_enabled = g_Config.ui.enable_wraparound;
|
||||
Text_ChangeText(value_text, is_enabled ? GS(MISC_ON) : GS(MISC_OFF));
|
||||
break;
|
||||
|
||||
case OPTION_RENDER_MODE:
|
||||
sprintf(
|
||||
buf, GS(DETAIL_STRING_FMT),
|
||||
|
@ -636,6 +652,13 @@ void Option_Graphics_Control(INVENTORY_ITEM *inv_item, const bool is_busy)
|
|||
reset = OPTION_UI_BAR_SCALE;
|
||||
break;
|
||||
|
||||
case OPTION_UI_SCROLL_WRAPAROUND:
|
||||
if (!g_Config.ui.enable_wraparound) {
|
||||
g_Config.ui.enable_wraparound = true;
|
||||
reset = OPTION_UI_SCROLL_WRAPAROUND;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPTION_RENDER_MODE:
|
||||
if (g_Config.rendering.render_mode == GFX_RM_LEGACY) {
|
||||
g_Config.rendering.render_mode = GFX_RM_FRAMEBUFFER;
|
||||
|
@ -766,6 +789,13 @@ void Option_Graphics_Control(INVENTORY_ITEM *inv_item, const bool is_busy)
|
|||
reset = OPTION_UI_BAR_SCALE;
|
||||
break;
|
||||
|
||||
case OPTION_UI_SCROLL_WRAPAROUND:
|
||||
if (g_Config.ui.enable_wraparound) {
|
||||
g_Config.ui.enable_wraparound = false;
|
||||
reset = OPTION_UI_SCROLL_WRAPAROUND;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPTION_RENDER_MODE:
|
||||
if (g_Config.rendering.render_mode == GFX_RM_LEGACY) {
|
||||
g_Config.rendering.render_mode = GFX_RM_FRAMEBUFFER;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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)
|
||||
|
@ -152,6 +133,8 @@ void Shell_Shutdown(void)
|
|||
Console_Shutdown();
|
||||
GameBuf_Shutdown();
|
||||
Savegame_Shutdown();
|
||||
|
||||
GameStringTable_Shutdown();
|
||||
GF_Shutdown();
|
||||
|
||||
Output_Shutdown();
|
||||
|
@ -174,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();
|
||||
|
@ -200,13 +213,17 @@ void Shell_Main(void)
|
|||
|
||||
if (!Output_Init()) {
|
||||
Shell_ExitSystem("Could not initialise video system");
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
Screen_Init();
|
||||
|
||||
GF_Init();
|
||||
GF_LoadFromFile(m_ModPaths[m_Args.mod].game_flow_path);
|
||||
GameStringTable_LoadFromFile(m_ModPaths[m_Args.mod].game_strings_path);
|
||||
GameStringTable_Init();
|
||||
if (m_Args.mod != M_MOD_OG) {
|
||||
GameStringTable_Load(m_ModPaths[M_MOD_OG].game_strings_path, false);
|
||||
}
|
||||
GameStringTable_Load(m_ModPaths[m_Args.mod].game_strings_path, true);
|
||||
GameStringTable_Apply(nullptr);
|
||||
|
||||
Savegame_Init();
|
||||
|
@ -312,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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -123,12 +123,6 @@ typedef struct {
|
|||
};
|
||||
} PHD_VBUF;
|
||||
|
||||
typedef struct {
|
||||
SECTOR *sector;
|
||||
SECTOR old_sector;
|
||||
int16_t block;
|
||||
} DOORPOS_DATA;
|
||||
|
||||
typedef struct {
|
||||
PASSPORT_MODE passport_selection;
|
||||
int32_t select_save_slot;
|
||||
|
|
|
@ -197,7 +197,6 @@ sources = [
|
|||
'game/objects/general/cabin.c',
|
||||
'game/objects/general/camera_target.c',
|
||||
'game/objects/general/cog.c',
|
||||
'game/objects/general/door.c',
|
||||
'game/objects/general/earthquake.c',
|
||||
'game/objects/general/keyhole.c',
|
||||
'game/objects/general/moving_bar.c',
|
||||
|
@ -261,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',
|
||||
|
|
|
@ -114,10 +114,10 @@ GF_COMMAND Game_Control(const bool demo_mode)
|
|||
if (g_OverlayStatus > 0) {
|
||||
if (g_GameFlow.load_save_disabled) {
|
||||
g_OverlayStatus = 0;
|
||||
} else if (g_Input.load) {
|
||||
g_OverlayStatus = -1;
|
||||
} else if (g_Input.save) {
|
||||
g_OverlayStatus = -2;
|
||||
} else {
|
||||
g_OverlayStatus = g_Input.save ? -2 : 0;
|
||||
g_OverlayStatus = g_Input.load ? -1 : 0;
|
||||
}
|
||||
} else {
|
||||
GF_COMMAND gf_cmd;
|
||||
|
|
|
@ -639,10 +639,10 @@ static GF_COMMAND M_Control(INV_RING *const ring)
|
|||
|
||||
if (g_InputDB.menu_confirm) {
|
||||
g_Inv_Chosen = inv_item->object_id;
|
||||
if (ring->type != RT_MAIN) {
|
||||
g_InvRing_Source[RT_OPTION].current = ring->current_object;
|
||||
} else {
|
||||
if (ring->type == RT_MAIN) {
|
||||
g_InvRing_Source[RT_MAIN].current = ring->current_object;
|
||||
} else {
|
||||
g_InvRing_Source[RT_OPTION].current = ring->current_object;
|
||||
}
|
||||
if (ring->mode == INV_TITLE_MODE
|
||||
&& (inv_item->object_id == O_DETAIL_OPTION
|
||||
|
@ -783,7 +783,7 @@ INV_RING *InvRing_Open(const INVENTORY_MODE mode)
|
|||
}
|
||||
|
||||
for (int32_t i = 0; i < 8; i++) {
|
||||
g_Inv_ExtraData[i] = 0;
|
||||
g_Inv_ExtraData[i] = -1;
|
||||
}
|
||||
|
||||
g_InvRing_Source[RT_MAIN].current = 0;
|
||||
|
|
|
@ -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);
|
||||
|
@ -364,7 +241,7 @@ const BOUNDS_16 *Item_GetBoundsAccurate(const ITEM *const item)
|
|||
ANIM_FRAME *Item_GetBestFrame(const ITEM *const item)
|
||||
{
|
||||
ANIM_FRAME *frames[2];
|
||||
int32_t rate;
|
||||
int32_t rate = 0;
|
||||
const int32_t frac = Item_GetFrames(item, frames, &rate);
|
||||
return frames[(frac > rate / 2) ? 1 : 0];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -473,6 +473,13 @@ void Lara_Draw_I(
|
|||
Matrix_Rot16_ID(mesh_rots_1[LM_UARM_R], mesh_rots_2[LM_UARM_R]);
|
||||
Output_DrawObjectMesh_I(g_Lara.mesh_ptrs[LM_UARM_R], clip);
|
||||
|
||||
// NOTE: gcc wrongly complains about mesh_rots_1 possibly being NULL.
|
||||
// While this is not the case, it's curious how the pistols subtract the
|
||||
// frame_base from g_Lara.*_arm.frame_num to access the mesh_rots, and the
|
||||
// rifles do not.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
|
||||
M_DrawBodyPart(LM_LARM_R, bone, mesh_rots_1, mesh_rots_2, clip);
|
||||
M_DrawBodyPart(LM_HAND_R, bone, mesh_rots_1, mesh_rots_2, clip);
|
||||
|
||||
|
@ -486,6 +493,8 @@ void Lara_Draw_I(
|
|||
M_DrawBodyPart(LM_LARM_L, bone, mesh_rots_1, mesh_rots_2, clip);
|
||||
M_DrawBodyPart(LM_HAND_L, bone, mesh_rots_1, mesh_rots_2, clip);
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
if (g_Lara.right_arm.flash_gun) {
|
||||
*g_MatrixPtr = saved_matrix;
|
||||
Gun_DrawFlash(gun_type, clip);
|
||||
|
|
|
@ -143,22 +143,24 @@ static int32_t M_CompareSampleOffsets(const void *const a, const void *const b)
|
|||
static void M_InitialiseSoundEffects(const char *file_name)
|
||||
{
|
||||
BENCHMARK benchmark = Benchmark_Start();
|
||||
LEVEL_INFO *info = nullptr;
|
||||
SAMPLE_ENTRY *entries = nullptr;
|
||||
|
||||
if (file_name == nullptr) {
|
||||
file_name = g_GameFlow.settings.sfx_path;
|
||||
}
|
||||
const char *full_path =
|
||||
File_GetFullPath(file_name == nullptr ? DEFAULT_SFX_PATH : file_name);
|
||||
LOG_DEBUG("Loading samples from %s", full_path);
|
||||
|
||||
MYFILE *const fp = File_Open(full_path, FILE_OPEN_READ);
|
||||
Memory_FreePointer(&full_path);
|
||||
|
||||
if (fp == nullptr) {
|
||||
Shell_ExitSystemFmt("Could not open %s file", file_name);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
LEVEL_INFO *const info = Level_GetInfo();
|
||||
info = Level_GetInfo();
|
||||
const int32_t sample_count = info->samples.offset_count;
|
||||
entries = Memory_Alloc(sizeof(SAMPLE_ENTRY) * sample_count);
|
||||
for (int32_t i = 0; i < sample_count; i++) {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
void Object_DrawDummyItem(const ITEM *item);
|
||||
void Object_DrawAnimatingItem(const ITEM *item);
|
||||
void Object_DrawUnclippedItem(const ITEM *item);
|
||||
void Object_DrawSpriteItem(const ITEM *item);
|
||||
|
||||
void Object_Collision(int16_t item_num, ITEM *lara_item, COLL_INFO *coll);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,222 +0,0 @@
|
|||
#include "game/box.h"
|
||||
#include "game/items.h"
|
||||
#include "game/objects/common.h"
|
||||
#include "game/room.h"
|
||||
#include "global/vars.h"
|
||||
|
||||
#include <libtrx/game/collision.h>
|
||||
#include <libtrx/game/game_buf.h>
|
||||
#include <libtrx/game/lara/common.h>
|
||||
#include <libtrx/game/objects/general/door.h>
|
||||
|
||||
typedef struct {
|
||||
DOORPOS_DATA d1;
|
||||
DOORPOS_DATA d1flip;
|
||||
DOORPOS_DATA d2;
|
||||
DOORPOS_DATA d2flip;
|
||||
} DOOR_DATA;
|
||||
|
||||
static SECTOR *M_GetRoomRelSector(
|
||||
const ROOM *room, const ITEM *item, int32_t sector_dx, int32_t sector_dz);
|
||||
static void M_InitialisePortal(
|
||||
const ROOM *room, const ITEM *item, int32_t sector_dx, int32_t sector_dz,
|
||||
DOORPOS_DATA *door_pos);
|
||||
static void M_Shut(DOORPOS_DATA *d);
|
||||
static void M_Open(DOORPOS_DATA *d);
|
||||
static void M_Setup(OBJECT *obj);
|
||||
static void M_Initialise(int16_t item_num);
|
||||
static void M_Control(int16_t item_num);
|
||||
|
||||
static SECTOR *M_GetRoomRelSector(
|
||||
const ROOM *const room, const ITEM *item, const int32_t sector_dx,
|
||||
const int32_t sector_dz)
|
||||
{
|
||||
const XZ_32 sector = {
|
||||
.x = ((item->pos.x - room->pos.x) >> WALL_SHIFT) + sector_dx,
|
||||
.z = ((item->pos.z - room->pos.z) >> WALL_SHIFT) + sector_dz,
|
||||
};
|
||||
return Room_GetUnitSector(room, sector.x, sector.z);
|
||||
}
|
||||
|
||||
static void Door_Shut(DOORPOS_DATA *const d)
|
||||
{
|
||||
SECTOR *const sector = d->sector;
|
||||
if (d->sector == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
sector->idx = 0;
|
||||
sector->box = NO_BOX;
|
||||
sector->ceiling.height = NO_HEIGHT;
|
||||
sector->floor.height = NO_HEIGHT;
|
||||
sector->floor.tilt = 0;
|
||||
sector->ceiling.tilt = 0;
|
||||
sector->portal_room.sky = NO_ROOM_NEG;
|
||||
sector->portal_room.pit = NO_ROOM_NEG;
|
||||
sector->portal_room.wall = NO_ROOM;
|
||||
|
||||
const int16_t box_num = d->block;
|
||||
if (box_num != NO_BOX) {
|
||||
Box_GetBox(box_num)->overlap_index |= BOX_BLOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
static void Door_Open(DOORPOS_DATA *const d)
|
||||
{
|
||||
if (d->sector == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
*d->sector = d->old_sector;
|
||||
|
||||
const int16_t box_num = d->block;
|
||||
if (box_num != NO_BOX) {
|
||||
Box_GetBox(box_num)->overlap_index &= ~BOX_BLOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
static void M_InitialisePortal(
|
||||
const ROOM *const room, const ITEM *const item, const int32_t sector_dx,
|
||||
const int32_t sector_dz, DOORPOS_DATA *const door_pos)
|
||||
{
|
||||
door_pos->sector = M_GetRoomRelSector(room, item, sector_dx, sector_dz);
|
||||
|
||||
const SECTOR *sector = door_pos->sector;
|
||||
|
||||
const int16_t room_num = door_pos->sector->portal_room.wall;
|
||||
if (room_num != NO_ROOM) {
|
||||
sector =
|
||||
M_GetRoomRelSector(Room_Get(room_num), item, sector_dx, sector_dz);
|
||||
}
|
||||
|
||||
int16_t box_num = sector->box;
|
||||
if (!(Box_GetBox(box_num)->overlap_index & BOX_BLOCKABLE)) {
|
||||
box_num = NO_BOX;
|
||||
}
|
||||
door_pos->block = box_num;
|
||||
door_pos->old_sector = *door_pos->sector;
|
||||
}
|
||||
|
||||
static void M_Setup(OBJECT *const obj)
|
||||
{
|
||||
obj->initialise_func = M_Initialise;
|
||||
obj->control_func = M_Control;
|
||||
obj->draw_func = Object_DrawUnclippedItem;
|
||||
obj->collision_func = Door_Collision;
|
||||
obj->save_flags = 1;
|
||||
obj->save_anim = 1;
|
||||
}
|
||||
|
||||
static void M_Initialise(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = Item_Get(item_num);
|
||||
DOOR_DATA *door = GameBuf_Alloc(sizeof(DOOR_DATA), GBUF_ITEM_DATA);
|
||||
item->data = door;
|
||||
|
||||
int32_t dx = 0;
|
||||
int32_t dz = 0;
|
||||
if (item->rot.y == 0) {
|
||||
dz = -1;
|
||||
} else if (item->rot.y == -DEG_180) {
|
||||
dz = 1;
|
||||
} else if (item->rot.y == DEG_90) {
|
||||
dx = -1;
|
||||
} else {
|
||||
dx = 1;
|
||||
}
|
||||
|
||||
int16_t room_num = item->room_num;
|
||||
const ROOM *room = Room_Get(room_num);
|
||||
M_InitialisePortal(room, item, dx, dz, &door->d1);
|
||||
|
||||
if (room->flipped_room == NO_ROOM_NEG) {
|
||||
door->d1flip.sector = nullptr;
|
||||
} else {
|
||||
room = Room_Get(room->flipped_room);
|
||||
M_InitialisePortal(room, item, dx, dz, &door->d1flip);
|
||||
}
|
||||
|
||||
room_num = door->d1.sector->portal_room.wall;
|
||||
Door_Shut(&door->d1);
|
||||
Door_Shut(&door->d1flip);
|
||||
|
||||
if (room_num == NO_ROOM) {
|
||||
door->d2.sector = nullptr;
|
||||
door->d2flip.sector = nullptr;
|
||||
} else {
|
||||
room = Room_Get(room_num);
|
||||
M_InitialisePortal(room, item, 0, 0, &door->d2);
|
||||
if (room->flipped_room == NO_ROOM_NEG) {
|
||||
door->d2flip.sector = nullptr;
|
||||
} else {
|
||||
room = Room_Get(room->flipped_room);
|
||||
M_InitialisePortal(room, item, 0, 0, &door->d2flip);
|
||||
}
|
||||
|
||||
Door_Shut(&door->d2);
|
||||
Door_Shut(&door->d2flip);
|
||||
|
||||
const int16_t prev_room = item->room_num;
|
||||
Item_NewRoom(item_num, room_num);
|
||||
item->room_num = prev_room;
|
||||
}
|
||||
}
|
||||
|
||||
static void M_Control(const int16_t item_num)
|
||||
{
|
||||
ITEM *const item = Item_Get(item_num);
|
||||
DOOR_DATA *const data = item->data;
|
||||
|
||||
if (Item_IsTriggerActive(item)) {
|
||||
if (item->current_anim_state == DOOR_STATE_CLOSED) {
|
||||
item->goal_anim_state = DOOR_STATE_OPEN;
|
||||
} else {
|
||||
Door_Open(&data->d1);
|
||||
Door_Open(&data->d2);
|
||||
Door_Open(&data->d1flip);
|
||||
Door_Open(&data->d2flip);
|
||||
}
|
||||
} else {
|
||||
if (item->current_anim_state == DOOR_STATE_OPEN) {
|
||||
item->goal_anim_state = DOOR_STATE_CLOSED;
|
||||
} else {
|
||||
Door_Shut(&data->d1);
|
||||
Door_Shut(&data->d2);
|
||||
Door_Shut(&data->d1flip);
|
||||
Door_Shut(&data->d2flip);
|
||||
}
|
||||
}
|
||||
|
||||
Item_Animate(item);
|
||||
}
|
||||
|
||||
void Door_Collision(
|
||||
const int16_t item_num, ITEM *const lara_item, COLL_INFO *const coll)
|
||||
{
|
||||
ITEM *const item = Item_Get(item_num);
|
||||
|
||||
if (!Item_TestBoundsCollide(item, lara_item, coll->radius)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Collide_TestCollision(item, lara_item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (coll->enable_baddie_push) {
|
||||
Lara_Push(
|
||||
item, coll,
|
||||
item->current_anim_state != item->goal_anim_state ? coll->enable_hit
|
||||
: false,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_OBJECT(O_DOOR_TYPE_1, M_Setup)
|
||||
REGISTER_OBJECT(O_DOOR_TYPE_2, M_Setup)
|
||||
REGISTER_OBJECT(O_DOOR_TYPE_3, M_Setup)
|
||||
REGISTER_OBJECT(O_DOOR_TYPE_4, M_Setup)
|
||||
REGISTER_OBJECT(O_DOOR_TYPE_5, M_Setup)
|
||||
REGISTER_OBJECT(O_DOOR_TYPE_6, M_Setup)
|
||||
REGISTER_OBJECT(O_DOOR_TYPE_7, M_Setup)
|
||||
REGISTER_OBJECT(O_DOOR_TYPE_8, M_Setup)
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -44,10 +44,11 @@ static void M_HandleLayoutChange(const EVENT *event, void *user_data)
|
|||
const M_PRIV *const p = user_data;
|
||||
switch (p->ui.state.backend) {
|
||||
case INPUT_BACKEND_KEYBOARD:
|
||||
g_Config.input.keyboard_layout = p->ui.state.active_layout;
|
||||
g_Config.input.keyboard_layout = p->ui.state.editor_state.active_layout;
|
||||
break;
|
||||
case INPUT_BACKEND_CONTROLLER:
|
||||
g_Config.input.controller_layout = p->ui.state.active_layout;
|
||||
g_Config.input.controller_layout =
|
||||
p->ui.state.editor_state.active_layout;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -66,6 +66,8 @@ static void M_LoadGame(INVENTORY_ITEM *inv_item);
|
|||
static void M_SaveGame(INVENTORY_ITEM *inv_item);
|
||||
static void M_NewGame(void);
|
||||
static void M_PlayAnyLevel(INVENTORY_ITEM *inv_item);
|
||||
static int32_t M_GetCurrentPage(const INVENTORY_ITEM *inv_item);
|
||||
static bool M_IsFlipping(const INVENTORY_ITEM *inv_item);
|
||||
static void M_FlipLeft(INVENTORY_ITEM *inv_item);
|
||||
static void M_FlipRight(INVENTORY_ITEM *inv_item);
|
||||
static void M_Close(INVENTORY_ITEM *inv_item);
|
||||
|
@ -318,6 +320,17 @@ static void M_PlayAnyLevel(INVENTORY_ITEM *const inv_item)
|
|||
}
|
||||
}
|
||||
|
||||
static int32_t M_GetCurrentPage(const INVENTORY_ITEM *const inv_item)
|
||||
{
|
||||
const int32_t frame = inv_item->goal_frame - inv_item->open_frame;
|
||||
return frame % 5 == 0 ? frame / 5 : -1;
|
||||
}
|
||||
|
||||
static bool M_IsFlipping(const INVENTORY_ITEM *const inv_item)
|
||||
{
|
||||
return M_GetCurrentPage(inv_item) == -1;
|
||||
}
|
||||
|
||||
static void M_FlipLeft(INVENTORY_ITEM *const inv_item)
|
||||
{
|
||||
M_RemoveAllText();
|
||||
|
@ -413,18 +426,19 @@ void Option_Passport_Control(INVENTORY_ITEM *const item, const bool is_busy)
|
|||
|
||||
InvRing_RemoveAllText();
|
||||
|
||||
const int32_t frame = item->goal_frame - item->open_frame;
|
||||
const int32_t page = frame % 5 == 0 ? frame / 5 : -1;
|
||||
const bool is_flipping = page == -1;
|
||||
if (is_flipping) {
|
||||
if (M_IsFlipping(item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_State.current_page = page;
|
||||
m_State.current_page = M_GetCurrentPage(item);
|
||||
if (m_State.current_page < m_State.active_page) {
|
||||
M_FlipRight(item);
|
||||
g_Input = (INPUT_STATE) {};
|
||||
g_InputDB = (INPUT_STATE) {};
|
||||
} else if (m_State.current_page > m_State.active_page) {
|
||||
M_FlipLeft(item);
|
||||
g_Input = (INPUT_STATE) {};
|
||||
g_InputDB = (INPUT_STATE) {};
|
||||
} else {
|
||||
m_State.is_ready = true;
|
||||
M_ShowPage(item);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
@ -398,7 +381,7 @@ static void M_HandleConfigChange(const EVENT *const event, void *const data)
|
|||
if (CHANGED(window.is_fullscreen) || CHANGED(window.is_maximized)
|
||||
|| CHANGED(window.width) || CHANGED(window.height)
|
||||
|| CHANGED(rendering.scaler) || CHANGED(rendering.sizer)
|
||||
|| CHANGED(rendering.aspect_mode)) {
|
||||
|| CHANGED(rendering.aspect_mode) || CHANGED(visuals.use_psx_fov)) {
|
||||
LOG_DEBUG("Change in settings detected");
|
||||
M_SyncToWindow();
|
||||
M_RefreshRendererViewport();
|
||||
|
@ -427,12 +410,45 @@ static void M_HandleConfigChange(const EVENT *const event, void *const data)
|
|||
|| CHANGED(visuals.water_color.r)) {
|
||||
Output_ApplyLevelSettings();
|
||||
}
|
||||
|
||||
if (CHANGED(rendering.aspect_mode)) {
|
||||
Output_ReloadBackgroundImage();
|
||||
}
|
||||
}
|
||||
|
||||
bool Shell_ParseArgs(const int32_t arg_count, const char **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
|
||||
void Shell_Main(void)
|
||||
int32_t Shell_Main(void)
|
||||
{
|
||||
M_ParseArgs(&m_Args);
|
||||
LOG_INFO("Game directory: %s", File_GetGameDirectory());
|
||||
|
||||
if (m_Args.mod == M_MOD_GM) {
|
||||
Object_Get(O_MONK_3)->setup_func = Monk3_Setup;
|
||||
|
@ -461,7 +477,7 @@ void Shell_Main(void)
|
|||
|
||||
if (!M_CreateGameWindow()) {
|
||||
Shell_ExitSystem("Failed to create game window");
|
||||
return;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Random_Seed();
|
||||
|
@ -474,7 +490,12 @@ void Shell_Main(void)
|
|||
|
||||
GF_Init();
|
||||
GF_LoadFromFile(m_ModPaths[m_Args.mod].game_flow_path);
|
||||
GameStringTable_LoadFromFile(m_ModPaths[m_Args.mod].game_strings_path);
|
||||
|
||||
GameStringTable_Init();
|
||||
if (m_Args.mod != M_MOD_OG) {
|
||||
GameStringTable_Load(m_ModPaths[M_MOD_OG].game_strings_path, false);
|
||||
}
|
||||
GameStringTable_Load(m_ModPaths[m_Args.mod].game_strings_path, true);
|
||||
GameStringTable_Apply(nullptr);
|
||||
|
||||
GameBuf_Init();
|
||||
|
@ -552,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();
|
||||
|
@ -574,12 +595,14 @@ 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)
|
||||
{
|
||||
GameStringTable_Shutdown();
|
||||
GF_Shutdown();
|
||||
GameString_Shutdown();
|
||||
|
||||
Console_Shutdown();
|
||||
Render_Shutdown();
|
||||
Text_Shutdown();
|
||||
|
@ -587,6 +610,7 @@ void Shell_Shutdown(void)
|
|||
GameBuf_Shutdown();
|
||||
Config_Shutdown();
|
||||
EnumMap_Shutdown();
|
||||
GameString_Shutdown();
|
||||
}
|
||||
|
||||
const char *Shell_GetConfigPath(void)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -13,12 +13,15 @@
|
|||
#include <libtrx/game/ui/elements/resize.h>
|
||||
#include <libtrx/game/ui/elements/spacer.h>
|
||||
#include <libtrx/game/ui/elements/stack.h>
|
||||
#include <libtrx/game/viewport.h>
|
||||
#include <libtrx/memory.h>
|
||||
#include <libtrx/strings.h>
|
||||
#include <libtrx/utils.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define M_ARROW_SPACING 2.0f
|
||||
|
||||
typedef struct {
|
||||
CONFIG_OPTION_TYPE option_type;
|
||||
GAME_STRING_ID label_id;
|
||||
|
@ -219,6 +222,12 @@ static const M_OPTION m_Options[] = {
|
|||
.delta_fast = 10,
|
||||
},
|
||||
|
||||
{
|
||||
.option_type = COT_BOOL,
|
||||
.label_id = GS_ID(DETAIL_UI_SCROLL_WRAPAROUND),
|
||||
.target = &g_Config.ui.enable_wraparound,
|
||||
},
|
||||
|
||||
{
|
||||
.option_type = COT_INT32,
|
||||
.label_id = GS_ID(DETAIL_SCALER),
|
||||
|
@ -244,11 +253,28 @@ static const M_OPTION m_Options[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static int32_t M_GetVisibleRows(void);
|
||||
static uint8_t *M_GetColorComponent(const M_OPTION *option);
|
||||
static M_ENUM_LOOKUP M_GetEnumEntry(const M_OPTION *option);
|
||||
static char *M_FormatRowValue(int32_t row_idx);
|
||||
static bool M_CanChangeValue(int32_t row_idx, int32_t dir);
|
||||
static bool M_RequestChangeValue(int32_t row_idx, int32_t dir);
|
||||
static float M_GetValueWidth(const UI_GRAPHIC_SETTINGS_STATE *s);
|
||||
|
||||
static int32_t M_GetVisibleRows(void)
|
||||
{
|
||||
const int32_t res_h =
|
||||
Scaler_CalcInverse(Viewport_GetHeight(), SCALER_TARGET_TEXT);
|
||||
if (res_h <= 240) {
|
||||
return 5;
|
||||
} else if (res_h <= 384) {
|
||||
return 7;
|
||||
} else if (res_h < 480) {
|
||||
return 10;
|
||||
} else {
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t *M_GetColorComponent(const M_OPTION *const option)
|
||||
{
|
||||
|
@ -417,6 +443,26 @@ static bool M_RequestChangeValue(const int32_t row_idx, const int32_t dir)
|
|||
return true;
|
||||
}
|
||||
|
||||
static float M_GetValueWidth(const UI_GRAPHIC_SETTINGS_STATE *const s)
|
||||
{
|
||||
// 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 result = -1.0f;
|
||||
for (int32_t i = 0; i < s->req.max_rows; i++) {
|
||||
const char *const value = M_FormatRowValue(i);
|
||||
float value_w;
|
||||
UI_Label_Measure(value, &value_w, nullptr);
|
||||
result = MAX(result, value_w);
|
||||
}
|
||||
float arrow_w;
|
||||
UI_Label_Measure("\\{button left}", &arrow_w, nullptr);
|
||||
result += arrow_w;
|
||||
UI_Label_Measure("\\{button right}", &arrow_w, nullptr);
|
||||
result += arrow_w;
|
||||
result += M_ARROW_SPACING * 2;
|
||||
return result;
|
||||
}
|
||||
|
||||
void UI_GraphicSettings_Init(UI_GRAPHIC_SETTINGS_STATE *const s)
|
||||
{
|
||||
int32_t row_count = 0;
|
||||
|
@ -426,6 +472,7 @@ void UI_GraphicSettings_Init(UI_GRAPHIC_SETTINGS_STATE *const s)
|
|||
UI_Requester_Init(&s->req, row_count, row_count, true);
|
||||
s->req.row_pad = 2.0f;
|
||||
s->req.row_spacing = 0.0f;
|
||||
s->req.show_arrows = true;
|
||||
}
|
||||
|
||||
void UI_GraphicSettings_Free(UI_GRAPHIC_SETTINGS_STATE *const s)
|
||||
|
@ -435,17 +482,7 @@ void UI_GraphicSettings_Free(UI_GRAPHIC_SETTINGS_STATE *const s)
|
|||
|
||||
bool UI_GraphicSettings_Control(UI_GRAPHIC_SETTINGS_STATE *const s)
|
||||
{
|
||||
const int32_t scale = Scaler_GetScale(SCALER_TARGET_TEXT) * 100;
|
||||
if (scale >= 190) {
|
||||
UI_Requester_SetVisibleRows(&s->req, 6);
|
||||
} else if (scale >= 160) {
|
||||
UI_Requester_SetVisibleRows(&s->req, 8);
|
||||
} else if (scale >= 120) {
|
||||
UI_Requester_SetVisibleRows(&s->req, 12);
|
||||
} else {
|
||||
UI_Requester_SetVisibleRows(&s->req, 18);
|
||||
}
|
||||
|
||||
UI_Requester_SetVisibleRows(&s->req, M_GetVisibleRows());
|
||||
const int32_t choice = UI_Requester_Control(&s->req);
|
||||
if (choice == UI_REQUESTER_CANCEL) {
|
||||
return true;
|
||||
|
@ -465,6 +502,8 @@ void UI_GraphicSettings(UI_GRAPHIC_SETTINGS_STATE *const s)
|
|||
UI_BeginModal(0.5f, 0.6f);
|
||||
UI_BeginRequester(&s->req, GS(DETAIL_TITLE));
|
||||
|
||||
const float max_value_w = M_GetValueWidth(s) / g_Config.ui.text_scale;
|
||||
|
||||
for (int32_t i = 0; i < s->req.max_rows; i++) {
|
||||
if (!UI_Requester_IsRowVisible(&s->req, i)) {
|
||||
UI_BeginResize(-1.0f, 0.0f);
|
||||
|
@ -480,19 +519,25 @@ void UI_GraphicSettings(UI_GRAPHIC_SETTINGS_STATE *const s)
|
|||
UI_Label(GameString_Get(m_Options[i].label_id));
|
||||
UI_Spacer(20.0f, 0.0f);
|
||||
|
||||
UI_BeginResize(max_value_w, -1.0f);
|
||||
UI_BeginAnchor(1.0f, 0.5f);
|
||||
UI_BeginStackEx((UI_STACK_SETTINGS) {
|
||||
.orientation = UI_STACK_HORIZONTAL,
|
||||
.align = { .h = UI_STACK_H_ALIGN_DISTRIBUTE },
|
||||
.spacing = { .h = 5.0f },
|
||||
.spacing = { .h = M_ARROW_SPACING },
|
||||
});
|
||||
UI_BeginHide(i != sel_row || !M_CanChangeValue(i, -1));
|
||||
UI_Label("\\{button left}");
|
||||
UI_EndHide();
|
||||
|
||||
UI_Label(M_FormatRowValue(i));
|
||||
|
||||
UI_BeginHide(i != sel_row || !M_CanChangeValue(i, +1));
|
||||
UI_Label("\\{button right}");
|
||||
UI_EndHide();
|
||||
UI_EndStack();
|
||||
UI_EndAnchor();
|
||||
UI_EndResize();
|
||||
|
||||
UI_EndStack();
|
||||
UI_EndRequesterRow(&s->req, i);
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
|
||||
typedef struct {
|
||||
UI_REQUESTER_STATE req;
|
||||
float arrow_spacing;
|
||||
float value_w;
|
||||
} UI_GRAPHIC_SETTINGS_STATE;
|
||||
|
||||
void UI_GraphicSettings_Init(UI_GRAPHIC_SETTINGS_STATE *s);
|
||||
|
|
|
@ -125,20 +125,23 @@ void Viewport_Reset(void)
|
|||
VIEWPORT *const vp = &m_Viewport;
|
||||
switch (g_Config.rendering.aspect_mode) {
|
||||
case AM_4_3:
|
||||
vp->render_ar = 4.0 / 3.0;
|
||||
vp->render_ar.w = 4;
|
||||
vp->render_ar.h = 3;
|
||||
break;
|
||||
case AM_16_9:
|
||||
vp->render_ar = 16.0 / 9.0;
|
||||
vp->render_ar.w = 16;
|
||||
vp->render_ar.h = 9;
|
||||
break;
|
||||
case AM_ANY:
|
||||
vp->render_ar = size.w / (double)size.h;
|
||||
vp->render_ar.w = size.w;
|
||||
vp->render_ar.h = size.h;
|
||||
break;
|
||||
}
|
||||
|
||||
vp->width = size.w / g_Config.rendering.scaler;
|
||||
vp->height = size.h / g_Config.rendering.scaler;
|
||||
if (g_Config.rendering.aspect_mode != AM_ANY) {
|
||||
vp->width = vp->height * vp->render_ar;
|
||||
vp->width = vp->height * vp->render_ar.w / vp->render_ar.h;
|
||||
}
|
||||
|
||||
vp->near_z = Output_GetNearZ() >> W2V_SHIFT;
|
||||
|
|
|
@ -8,7 +8,10 @@ typedef struct {
|
|||
int32_t near_z;
|
||||
int32_t far_z;
|
||||
int16_t view_angle;
|
||||
double render_ar;
|
||||
struct {
|
||||
int32_t w;
|
||||
int32_t h;
|
||||
} render_ar;
|
||||
|
||||
// TODO: remove most of these variables if possible
|
||||
struct {
|
||||
|
|
|
@ -120,12 +120,6 @@ typedef enum {
|
|||
GFE_REMOVE_AMMO = 22,
|
||||
} GF_EVENTS;
|
||||
|
||||
typedef struct {
|
||||
SECTOR *sector;
|
||||
SECTOR old_sector;
|
||||
int16_t block;
|
||||
} DOORPOS_DATA;
|
||||
|
||||
typedef enum {
|
||||
TRAP_SET = 0,
|
||||
TRAP_ACTIVATE = 1,
|
||||
|
@ -157,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;
|
||||
|
|
|
@ -195,7 +195,6 @@ sources = [
|
|||
'game/objects/general/cutscene_player.c',
|
||||
'game/objects/general/detonator.c',
|
||||
'game/objects/general/ding_dong.c',
|
||||
'game/objects/general/door.c',
|
||||
'game/objects/general/earthquake.c',
|
||||
'game/objects/general/final_cutscene.c',
|
||||
'game/objects/general/final_level_counter.c',
|
||||
|
@ -267,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',
|
||||
|
|
|
@ -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__":
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue