2021-04-19 08:23:44 +02:00
# include <SDL.h>
Add support for button glyph display
This adds a function that converts an action (such as interacting
in-game) to the corresponding button text ("ENTER", "E") or button
glyph (PlayStation triangle, Steam Deck Y, etc). This function
currently only gives the existing ENTERs or Es, because I don't know
how best to detect controller usage, or whether the game is running on
a Steam Deck, or what buttons need to be displayed there. Still, it
should now be really easy to adapt the rendering of keyboard keys to
consoles, controllers, or rebound keys.
To identify the actions that currently need to be displayed, this
commit also adds the initial enums for action sets as described by
Ethan in a comment in #834 (Jan 18, 2022).
2023-03-18 22:30:16 +01:00
# include "ActionSets.h"
2023-03-18 22:32:26 +01:00
# include "ButtonGlyphs.h"
2021-09-13 06:39:07 +02:00
# include "Constants.h"
2020-07-19 21:43:29 +02:00
# include "Credits.h"
2021-02-20 08:19:09 +01:00
# include "CustomLevels.h"
2021-02-21 00:40:11 +01:00
# include "Editor.h"
2020-01-01 21:29:24 +01:00
# include "Entity.h"
2020-07-19 21:43:29 +02:00
# include "FileSystemUtils.h"
Implement first font::print function, fix most fading of colored glyphs
There has always been a mess of different print functions that all had
slightly different specifics and called each other:
Print(x, y, text, r, g, b, cen)
nothing special here, just does what the arguments say
PrintAlpha(x, y, text, r, g, b, a, cen)
just Print but with an alpha argument
PrintWrap(x, y, text, r, g, b, cen, linespacing, maxwidth)
added for wordwrapping, heavily used now
bprint(x, y, text, r, g, b, cen)
prints an outline, then just PrintAlpha
bprintalpha(x, y, text, r, g, b, a, cen)
just bprint but with an alpha argument
bigprint(x, y, text, r, g, b, cen, sc)
nothing special here, just does what the arguments say
bigbprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigprint
bigrprint(x, y, text, r, g, b, cen, sc)
right-aligns text, unless cen is given in which case it just
centers text like other functions already do?
bigbrprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigrprint
We need even more specifics with the new font system: we need to be
able to specify whether CJK characters should be vertically centered or
stick out on the top/bottom, and we sometimes need to pass in
brightness variables for colored glyphs. And text printing functions
now fit better in Font.cpp anyway. So there's now a big overhaul of
print functions: all these functions will be replaced by font::print
and font::print_wrap (the former of which now exists). These take flags
as their first argument, which can be 0 for a basic left-aligned print,
PR_CEN for centered text (set X to -1!!!) PR_BOR for a border (instead
of functions like bprint and bigbprint), PR_2X, PR_3X etc for scaling,
and these can be combined with |.
Some text, for example [Press ESC to return to editor], fades in/out
using the alpha value, which is passed to the print function. In some
other places (like Press ENTER to teleport, textboxes, trophy text...)
text can fade in or out by direct changes to the RGB values. This means
regular color-adjusted white text can change color, but colored button
glyphs can't, since there's no way to know in the print system what the
maximum RGB values of a specific textbox are supposed to be, so the
only thing it can do is draw the button glyphs at full brightness,
which looks bad. Therefore, you can now also pass in the brightness
value via the flags, with PR_COLORGLYPH_BRI(255).
2023-01-06 04:43:21 +01:00
# include "Font.h"
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
# include "GlitchrunnerMode.h"
2020-07-19 21:43:29 +02:00
# include "Graphics.h"
2021-06-13 00:19:15 +02:00
# include "GraphicsUtil.h"
Optimize recompilation from changing commit hash
This reworks how the commit hash and date are compiled so that if
they're changed (and they're changed often), only one source file needs
to be recompiled in order to update it everywhere in the game, no matter
how many source files use the hash or date.
The commit hash and date are now declared in InterimVersion.h (and they
need `extern "C"` guards because otherwise it results in a link fail on
MSVC because MSVC is stupid).
To do this, what now happens is that upon every rebuild,
InterimVersion.in.c is processed to create InterimVersion.out.c, then
InterimVersion.out.c is compiled into its own static library that is
then linked with VVVVVV.
(Why didn't I just simply add it to the list of VVVVVV source files?
Well, doing it _now_ does nothing because at that point the horse is
already out of the barn, and the VVVVVV executable has already been
declared, so I can't modify its sources. And I can't do it before
either, because we depend on the VVVVVV executable existing to do the
interim version logic. I could probably work around this by cleverly
moving around lines, but that'd separate related logic from each other.)
And yes, the naming convention has changed. Not only did I rename
Version to InterimVersion (to clearly differentiate it from
ReleaseVersion, which I'll be adding later), I also named the files
InterimVersion.in.c and InterimVersion.out.c instead of
InterimVersion.c.in and InterimVersion.c.out. I needed to put the file
extension on the end because otherwise CMake wouldn't recognize what
kind of language it is, and I mean like yeah duh of course it doesn't,
my text editor doesn't recognize it either.
2022-08-23 06:21:23 +02:00
# include "InterimVersion.h"
2020-11-13 02:16:18 +01:00
# include "KeyPoll.h"
2023-07-02 04:02:25 +02:00
# include "LevelDebugger.h"
2022-12-30 22:57:24 +01:00
# include "Localization.h"
# include "LocalizationStorage.h"
2020-07-19 21:43:29 +02:00
# include "MakeAndPlay.h"
2020-01-01 21:29:24 +01:00
# include "Map.h"
2020-07-19 21:43:29 +02:00
# include "Maths.h"
2020-07-19 21:05:41 +02:00
# include "Music.h"
2022-08-23 06:22:57 +02:00
# include "ReleaseVersion.h"
2022-12-30 22:57:24 +01:00
# include "RoomnameTranslator.h"
Extern `gameScreen`, remove `screenbuffer`
I know earlier I removed the gameScreen extern in favor of using
screenbuffer, but that was only to be consistent. After further
consideration, I have found that it's actually really stupid.
There's no reason to be accessing it through screenbuffer, and it's
probably an artifact of 2.0-2.2 passing stack-allocated otherwise-global
classes everywhere through function arguments. Also, it leads to stupid
bugs where screenbuffer could potentially be NULL, which has already
resulted in various annoying crashes in the past. Although those could
be fixed by simply initializing screenbuffer at the very top of main(),
but, why not just scrap the whole thing anyway?
So that's what I'm doing.
As a nice side effect, I've removed the transitive include of Screen.h
from Graphics.h. This could've been done already since it only includes
it for the pointer anyway, but it's still good to do it now.
2021-12-25 08:56:47 +01:00
# include "Screen.h"
2020-01-01 21:29:24 +01:00
# include "Script.h"
2020-07-19 21:43:29 +02:00
# include "UtilityClass.h"
2022-12-30 22:57:24 +01:00
# include "VFormat.h"
2020-01-01 21:29:24 +01:00
2021-01-10 18:14:37 +01:00
static int tr ;
static int tg ;
static int tb ;
2020-01-01 21:29:24 +01:00
2022-12-30 23:43:24 +01:00
static inline void drawslowdowntext ( const int y )
2020-11-13 02:05:18 +01:00
{
switch ( game . slowdown )
{
case 30 :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , y , loc : : gettext ( " Game speed is normal. " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2020-11-13 02:05:18 +01:00
break ;
case 24 :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , y , loc : : gettext ( " Game speed is at 80% " ) , tr , tg , tb ) ;
2020-11-13 02:05:18 +01:00
break ;
case 18 :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , y , loc : : gettext ( " Game speed is at 60% " ) , tr , tg , tb ) ;
2020-11-13 02:05:18 +01:00
break ;
case 12 :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , y , loc : : gettext ( " Game speed is at 40% " ) , tr , tg , tb ) ;
2020-11-13 02:05:18 +01:00
break ;
}
}
2023-01-16 21:29:50 +01:00
static void slider_get ( char * buffer , size_t buffer_len , int position , int n_positions , int target_width )
2021-04-12 02:43:17 +02:00
{
2023-01-16 21:29:50 +01:00
/* Print a slider to the buffer for target_width in pixels.
*
* < - - target w - - >
* [ ] . . . . . . . . . . . .
* . . . . . . [ ] . . . . . .
* . . . . . . . . . . . . [ ]
* ^ ^ ^ ^ ^ ^ dots_per_position = 6
* ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ max_chars = 14
*/
if ( n_positions < 2 | | position < 0 | | position > = n_positions )
{
buffer [ 0 ] = ' \0 ' ;
return ;
}
2021-04-12 02:43:17 +02:00
2023-01-16 21:29:50 +01:00
int max_chars = ( ( target_width - font : : len ( 0 , " [] " ) ) / font : : len ( 0 , " . " ) ) + 2 ;
max_chars = SDL_min ( max_chars , buffer_len - 1 ) ;
2021-04-12 02:43:17 +02:00
2023-01-16 21:29:50 +01:00
int dots_per_position = ( max_chars - 2 ) / ( n_positions - 1 ) ;
max_chars = dots_per_position * ( n_positions - 1 ) + 2 ;
2021-04-12 02:43:17 +02:00
2023-01-16 21:29:50 +01:00
VVV_fillstring ( buffer , max_chars + 1 , ' . ' ) ;
if ( dots_per_position < 1 )
{
return ;
}
2021-04-12 02:43:17 +02:00
2023-01-16 21:29:50 +01:00
int handle_idx = position * dots_per_position ;
buffer [ handle_idx ] = ' [ ' ;
buffer [ handle_idx + 1 ] = ' ] ' ;
}
static void volumesliderrender ( void )
{
int volume_max_position = USER_VOLUME_MAX / USER_VOLUME_STEP ;
2021-04-12 02:43:17 +02:00
2023-01-16 21:29:50 +01:00
int volume ;
2021-04-12 02:43:17 +02:00
switch ( game . currentmenuoption )
{
case 0 :
2023-01-16 21:29:50 +01:00
volume = music . user_music_volume ;
2021-04-12 02:43:17 +02:00
break ;
case 1 :
2023-01-16 21:29:50 +01:00
volume = music . user_sound_volume ;
2021-04-12 02:43:17 +02:00
break ;
default :
SDL_assert ( 0 & & " Unhandled volume slider menu option! " ) ;
return ;
}
2023-01-16 21:29:50 +01:00
char slider [ 40 + 1 ] ;
slider_get ( slider , sizeof ( slider ) , volume_max_position * volume / USER_VOLUME_MAX , volume_max_position + 1 , 240 ) ;
2021-04-12 02:43:17 +02:00
2023-01-16 21:29:50 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
2021-04-12 02:43:17 +02:00
if ( game . slidermode = = SLIDER_NONE )
{
SDL_strlcpy ( buffer , slider , sizeof ( buffer ) ) ;
}
else
{
/* Draw selection brackets. */
2022-12-31 02:04:35 +01:00
vformat_buf ( buffer , sizeof ( buffer ) , loc : : get_langmeta ( ) - > menu_select . c_str ( ) , " label:str " , slider ) ;
2021-04-12 02:43:17 +02:00
}
2023-01-16 21:29:50 +01:00
font : : print ( PR_CEN , - 1 , 95 , buffer , tr , tg , tb ) ;
2021-04-12 02:43:17 +02:00
}
2022-12-30 23:43:24 +01:00
static void inline drawglitchrunnertext ( const int y )
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
{
int tempr = tr ;
int tempg = tg ;
int tempb = tb ;
2021-09-13 06:39:07 +02:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
const enum GlitchrunnerMode mode = GlitchrunnerMode_get ( ) ;
if ( mode = = GlitchrunnerNone )
{
tempr / = 2 ;
tempg / = 2 ;
tempb / = 2 ;
2022-12-30 23:43:24 +01:00
SDL_strlcpy ( buffer , loc : : gettext ( " Glitchrunner mode is OFF " ) , sizeof ( buffer ) ) ;
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
}
else
{
2022-12-30 23:43:24 +01:00
const char * mode_string = loc : : gettext ( GlitchrunnerMode_enum_to_string ( mode ) ) ;
vformat_buf ( buffer , sizeof ( buffer ) , loc : : gettext ( " Glitchrunner mode is {version} " ) , " version:str " , mode_string ) ;
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
}
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , y , buffer , tempr , tempg , tempb ) ;
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
}
2023-05-22 21:18:40 +02:00
static inline void draw_skip_message ( )
{
/* Unlock 18 is Flip Mode.
* If this is the first playthrough , 5 ( game completed ) will be unlocked
* but not Flip Mode until the player hits " play " on the title screen */
bool draw =
# ifndef MAKEANDPLAY
2023-06-06 02:34:27 +02:00
game . unlock [ Unlock_FLIPMODE ] & &
2023-05-22 21:18:40 +02:00
# endif
graphics . fademode = = FADE_NONE ;
if ( ! draw )
{
return ;
}
const int alpha = graphics . lerp (
game . old_skip_message_timer , game . skip_message_timer
) ;
draw = alpha > 100 ;
if ( ! draw )
{
return ;
}
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " - Press {button} to skip - " ) ,
" button:but " ,
vformat_button ( ActionSet_InGame , Action_InGame_Map )
) ;
font : : print (
PR_BRIGHTNESS ( alpha ) | PR_BOR | PR_CEN ,
- 1 , graphics . flipmode ? 20 : 210 , buffer ,
220 - help . glow , 220 - help . glow , 255 - help . glow / 2
) ;
}
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
static void menurender ( void )
2020-01-01 21:29:24 +01:00
{
2020-04-17 00:19:17 +02:00
switch ( game . currentmenuname )
2020-01-01 21:29:24 +01:00
{
2020-04-17 00:19:17 +02:00
case Menu : : mainmenu :
2022-12-30 23:43:24 +01:00
{
2022-12-30 07:23:48 +01:00
const int temp = 50 ;
2023-01-07 19:28:07 +01:00
graphics . draw_sprite ( ( 160 - 96 ) + 0 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 1 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 2 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 3 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 4 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 5 * 32 , temp , 23 , tr , tg , tb ) ;
2020-04-02 23:19:15 +02:00
# if defined(MAKEANDPLAY)
2023-01-16 21:29:50 +01:00
font : : print ( PR_RIGHT , 264 , temp + 35 , loc : : gettext ( " MAKE AND PLAY EDITION " ) , tr , tg , tb ) ;
2020-11-21 02:58:28 +01:00
# endif
Optimize recompilation from changing commit hash
This reworks how the commit hash and date are compiled so that if
they're changed (and they're changed often), only one source file needs
to be recompiled in order to update it everywhere in the game, no matter
how many source files use the hash or date.
The commit hash and date are now declared in InterimVersion.h (and they
need `extern "C"` guards because otherwise it results in a link fail on
MSVC because MSVC is stupid).
To do this, what now happens is that upon every rebuild,
InterimVersion.in.c is processed to create InterimVersion.out.c, then
InterimVersion.out.c is compiled into its own static library that is
then linked with VVVVVV.
(Why didn't I just simply add it to the list of VVVVVV source files?
Well, doing it _now_ does nothing because at that point the horse is
already out of the barn, and the VVVVVV executable has already been
declared, so I can't modify its sources. And I can't do it before
either, because we depend on the VVVVVV executable existing to do the
interim version logic. I could probably work around this by cleverly
moving around lines, but that'd separate related logic from each other.)
And yes, the naming convention has changed. Not only did I rename
Version to InterimVersion (to clearly differentiate it from
ReleaseVersion, which I'll be adding later), I also named the files
InterimVersion.in.c and InterimVersion.out.c instead of
InterimVersion.c.in and InterimVersion.c.out. I needed to put the file
extension on the end because otherwise CMake wouldn't recognize what
kind of language it is, and I mean like yeah duh of course it doesn't,
my text editor doesn't recognize it either.
2022-08-23 06:21:23 +02:00
# ifdef INTERIM_VERSION_EXISTS
2023-01-17 04:47:17 +01:00
font : : print ( PR_RIGHT | PR_FONT_8X8 , 310 , 200 , COMMIT_DATE , tr / 2 , tg / 2 , tb / 2 ) ;
font : : print ( PR_RIGHT | PR_FONT_8X8 , 310 , 210 , INTERIM_COMMIT , tr / 2 , tg / 2 , tb / 2 ) ;
font : : print ( PR_RIGHT | PR_FONT_8X8 , 310 , 220 , BRANCH_NAME , tr / 2 , tg / 2 , tb / 2 ) ;
2020-04-02 23:19:15 +02:00
# endif
2023-01-17 04:47:17 +01:00
font : : print ( PR_RIGHT , 310 , 230 , RELEASE_VERSION , tr / 2 , tg / 2 , tb / 2 ) ;
2020-01-01 21:29:24 +01:00
2023-08-30 16:45:04 +02:00
const char * left_msg = NULL ;
const bool fonts_error = ! FILESYSTEM_doesFontsDirExist ( ) ;
const bool lang_error = ! FILESYSTEM_doesLangDirExist ( ) ;
if ( fonts_error & & lang_error )
{
left_msg = " [No fonts&lang folders] " ;
}
else if ( fonts_error )
{
left_msg = " [No fonts folder] " ;
}
else if ( lang_error )
{
left_msg = " [No lang folder] " ;
}
else if ( music . mmmmmm )
{
left_msg = loc : : gettext ( " [MMMMMM Mod Installed] " ) ;
}
if ( left_msg ! = NULL )
{
font : : print ( 0 , 10 , 230 , left_msg , tr / 2 , tg / 2 , tb / 2 ) ;
2020-04-16 01:29:54 +02:00
}
2020-04-17 00:19:17 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2020-04-17 00:19:17 +02:00
case Menu : : levellist :
2020-01-01 21:29:24 +01:00
{
2023-01-20 20:50:11 +01:00
if ( cl . ListOfMetaData . size ( ) = = 0 )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 90 , loc : : gettext ( " ERROR: No levels found. " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
}
2023-01-20 20:50:11 +01:00
int tmp = game . currentmenuoption + ( game . levelpage * 8 ) ;
if ( INBOUNDS_VEC ( tmp , cl . ListOfMetaData ) )
{
const bool nextlastoptions = cl . ListOfMetaData . size ( ) > 8 ;
//Don't show next/previous page or return to menu options here!
if ( nextlastoptions & & game . menuoptions . size ( ) - game . currentmenuoption < = 3 )
{
}
else
{
2024-01-03 20:09:23 +01:00
uint32_t level_flags = PR_FONT_IDX (
cl . ListOfMetaData [ tmp ] . level_main_font_idx ,
cl . ListOfMetaData [ tmp ] . rtl
) ;
2023-01-20 20:50:11 +01:00
uint32_t title_flags = cl . ListOfMetaData [ tmp ] . title_is_gettext ? PR_FONT_INTERFACE : level_flags ;
uint32_t creator_flags = cl . ListOfMetaData [ tmp ] . creator_is_gettext ? PR_FONT_INTERFACE : level_flags ;
2024-02-03 00:58:46 +01:00
const char * title = cl . ListOfMetaData [ tmp ] . title . c_str ( ) ;
if ( cl . ListOfMetaData [ tmp ] . title_is_gettext )
{
title = loc : : gettext ( title ) ;
}
const char * creator = cl . ListOfMetaData [ tmp ] . creator . c_str ( ) ;
if ( cl . ListOfMetaData [ tmp ] . creator_is_gettext )
{
creator = loc : : gettext ( creator ) ;
}
2023-01-20 20:50:11 +01:00
2024-02-03 00:58:46 +01:00
font : : print ( title_flags | PR_2X | PR_CEN , - 1 , 15 , title , tr , tg , tb ) ;
2023-01-20 20:50:11 +01:00
int sp = SDL_max ( 10 , font : : height ( level_flags ) ) ;
2024-02-03 00:58:46 +01:00
graphics . print_level_creator ( creator_flags , 40 , creator , tr , tg , tb ) ;
2023-01-20 20:50:11 +01:00
font : : print ( level_flags | PR_CEN , - 1 , 40 + sp , cl . ListOfMetaData [ tmp ] . website , tr , tg , tb ) ;
font : : print ( level_flags | PR_CEN , - 1 , 40 + sp * 3 , cl . ListOfMetaData [ tmp ] . Desc1 , tr , tg , tb ) ;
font : : print ( level_flags | PR_CEN , - 1 , 40 + sp * 4 , cl . ListOfMetaData [ tmp ] . Desc2 , tr , tg , tb ) ;
if ( sp < = 10 )
{
font : : print ( level_flags | PR_CEN , - 1 , 40 + sp * 5 , cl . ListOfMetaData [ tmp ] . Desc3 , tr , tg , tb ) ;
}
}
}
break ;
2020-04-16 01:29:54 +02:00
}
2020-04-17 00:19:17 +02:00
case Menu : : errornostart :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " ERROR: This level has no start point! " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
break ;
case Menu : : gameplayoptions :
{
int gameplayoptionsoffset = 0 ;
2021-04-09 12:09:12 +02:00
# if !defined(MAKEANDPLAY)
2023-06-06 02:34:27 +02:00
if ( game . ingame_titlemode & & game . unlock [ Unlock_FLIPMODE ] )
2021-04-09 12:09:12 +02:00
# endif
2021-04-09 17:53:55 +02:00
{
gameplayoptionsoffset = 1 ;
if ( game . currentmenuoption = = 0 ) {
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Flip Mode " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Flip the entire game vertically. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
if ( graphics . setflipmode )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Currently ENABLED! " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Currently Disabled. " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2021-04-09 17:53:55 +02:00
}
}
}
if ( game . currentmenuoption = = gameplayoptionsoffset + 0 )
{
//Toggle FPS
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Toggle 30+ FPS " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Change whether the game runs at 30 or over 30 FPS. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
if ( ! game . over30mode )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: 30 FPS " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2021-04-09 17:53:55 +02:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: Over 30 FPS " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
}
break ;
}
else if ( game . currentmenuoption = = gameplayoptionsoffset + 1 )
{
//Speedrunner options
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Speedrunner Options " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Access some advanced settings that might be of interest to speedrunners. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
}
else if ( game . currentmenuoption = = gameplayoptionsoffset + 2 )
{
//Advanced options
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Advanced Options " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " All other gameplay settings. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
}
else if ( game . currentmenuoption = = gameplayoptionsoffset + 3 )
{
//Clear Data
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Clear Data " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Delete your main game save data and unlocked play modes. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
}
2021-08-12 05:54:02 +02:00
else if ( game . currentmenuoption = = gameplayoptionsoffset + 4 )
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Clear Data " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Delete your custom level save data and completion stars. " ) , tr , tg , tb ) ;
2021-08-12 05:54:02 +02:00
}
2021-04-09 17:53:55 +02:00
break ;
}
2020-04-17 00:19:17 +02:00
case Menu : : options :
2021-04-09 17:53:55 +02:00
switch ( game . currentmenuoption )
{
case 0 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Gameplay Options " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Adjust various gameplay settings. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
break ;
case 1 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Graphics Options " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Adjust screen settings. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
break ;
case 2 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Audio Options " ) , tr , tg , tb ) ;
2021-04-12 00:18:35 +02:00
if ( music . mmmmmm )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Adjust volume settings and soundtrack. " ) , tr , tg , tb ) ;
2021-04-12 00:18:35 +02:00
}
2021-08-20 02:17:36 +02:00
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Adjust volume settings. " ) , tr , tg , tb ) ;
2021-08-20 02:17:36 +02:00
}
2021-04-12 00:18:35 +02:00
break ;
case 3 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Game Pad Options " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Rebind your controller's buttons and adjust sensitivity. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
break ;
2021-04-12 00:18:35 +02:00
case 4 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Accessibility " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Disable screen effects, enable slowdown modes or invincibility. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
break ;
2022-12-30 22:57:24 +01:00
case 5 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Language " ) , tr , tg , tb ) ;
Save textbox state, allow lang switches w/ textbox
This allows switching languages while a text box is on screen by saving
the necessary state for a text box to be retranslated when the language
is switched.
This saves the state of the position and direction of the crewmate that
the text box position is based off of (if applicable), and the text
case of the text box, the script name of the script, and the original
(English) lines of the text box. I did not explicitly label the original
lines as English lines except in a main game context, because
technically, custom levels could have original lines in a different
language.
Unfortunately, this doesn't work for every text box in the game.
Notably, the Level Complete, Game Complete, number of crewmates
remaining, trinket collection, Intermission 1 guides, etc. text boxes
are special and require further fixes, but that will be coming in later
commits.
2024-01-19 05:21:02 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Change the language. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
}
break ;
2020-04-17 00:19:17 +02:00
case Menu : : graphicoptions :
2021-12-26 07:55:55 +01:00
{
int offset = 0 ;
if ( game . currentmenuoption = = offset + 0 & & ! gameScreen . isForcedFullscreen ( ) )
2020-04-16 04:52:49 +02:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Toggle Fullscreen " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Change to fullscreen/windowed mode. " ) , tr , tg , tb ) ;
2020-04-16 04:52:49 +02:00
Extern `gameScreen`, remove `screenbuffer`
I know earlier I removed the gameScreen extern in favor of using
screenbuffer, but that was only to be consistent. After further
consideration, I have found that it's actually really stupid.
There's no reason to be accessing it through screenbuffer, and it's
probably an artifact of 2.0-2.2 passing stack-allocated otherwise-global
classes everywhere through function arguments. Also, it leads to stupid
bugs where screenbuffer could potentially be NULL, which has already
resulted in various annoying crashes in the past. Although those could
be fixed by simply initializing screenbuffer at the very top of main(),
but, why not just scrap the whole thing anyway?
So that's what I'm doing.
As a nice side effect, I've removed the transitive include of Screen.h
from Graphics.h. This could've been done already since it only includes
it for the pointer anyway, but it's still good to do it now.
2021-12-25 08:56:47 +01:00
if ( gameScreen . isWindowed )
2020-11-13 01:45:51 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: WINDOWED " ) , tr , tg , tb ) ;
2020-11-13 01:45:51 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: FULLSCREEN " ) , tr , tg , tb ) ;
2020-04-02 23:19:15 +02:00
}
2021-12-26 07:55:55 +01:00
}
2020-04-16 04:52:49 +02:00
2021-12-26 07:55:55 +01:00
if ( gameScreen . isForcedFullscreen ( ) )
{
- - offset ;
}
if ( game . currentmenuoption = = offset + 1 )
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Scaling Mode " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Choose letterbox/stretch/integer mode. " ) , tr , tg , tb ) ;
2020-04-16 04:52:49 +02:00
Extern `gameScreen`, remove `screenbuffer`
I know earlier I removed the gameScreen extern in favor of using
screenbuffer, but that was only to be consistent. After further
consideration, I have found that it's actually really stupid.
There's no reason to be accessing it through screenbuffer, and it's
probably an artifact of 2.0-2.2 passing stack-allocated otherwise-global
classes everywhere through function arguments. Also, it leads to stupid
bugs where screenbuffer could potentially be NULL, which has already
resulted in various annoying crashes in the past. Although those could
be fixed by simply initializing screenbuffer at the very top of main(),
but, why not just scrap the whole thing anyway?
So that's what I'm doing.
As a nice side effect, I've removed the transitive include of Screen.h
from Graphics.h. This could've been done already since it only includes
it for the pointer anyway, but it's still good to do it now.
2021-12-25 08:56:47 +01:00
switch ( gameScreen . scalingMode )
2020-11-13 01:45:51 +01:00
{
2021-12-26 08:05:14 +01:00
case SCALING_INTEGER :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: INTEGER " ) , tr , tg , tb ) ;
2020-11-13 01:45:51 +01:00
break ;
2021-12-26 08:05:14 +01:00
case SCALING_STRETCH :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: STRETCH " ) , tr , tg , tb ) ;
2020-11-13 01:45:51 +01:00
break ;
2021-12-26 08:05:14 +01:00
case SCALING_LETTERBOX :
2020-11-13 01:45:51 +01:00
default :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: LETTERBOX " ) , tr , tg , tb ) ;
2020-11-13 01:45:51 +01:00
break ;
2020-04-16 04:52:49 +02:00
}
2021-12-26 07:55:55 +01:00
}
if ( game . currentmenuoption = = offset + 2 & & ! gameScreen . isForcedFullscreen ( ) )
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Resize to Nearest " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Resize to the nearest window size that is of an integer multiple. " ) , tr , tg , tb ) ;
Extern `gameScreen`, remove `screenbuffer`
I know earlier I removed the gameScreen extern in favor of using
screenbuffer, but that was only to be consistent. After further
consideration, I have found that it's actually really stupid.
There's no reason to be accessing it through screenbuffer, and it's
probably an artifact of 2.0-2.2 passing stack-allocated otherwise-global
classes everywhere through function arguments. Also, it leads to stupid
bugs where screenbuffer could potentially be NULL, which has already
resulted in various annoying crashes in the past. Although those could
be fixed by simply initializing screenbuffer at the very top of main(),
but, why not just scrap the whole thing anyway?
So that's what I'm doing.
As a nice side effect, I've removed the transitive include of Screen.h
from Graphics.h. This could've been done already since it only includes
it for the pointer anyway, but it's still good to do it now.
2021-12-25 08:56:47 +01:00
if ( ! gameScreen . isWindowed )
2020-06-30 22:30:59 +02:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " You must be in windowed mode to use this option. " ) , tr , tg , tb ) ;
2020-06-30 22:30:59 +02:00
}
2021-12-26 07:55:55 +01:00
}
if ( gameScreen . isForcedFullscreen ( ) )
{
- - offset ;
}
if ( game . currentmenuoption = = offset + 3 )
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Toggle Filter " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Change to nearest/linear filter. " ) , tr , tg , tb ) ;
2020-04-16 04:52:49 +02:00
Extern `gameScreen`, remove `screenbuffer`
I know earlier I removed the gameScreen extern in favor of using
screenbuffer, but that was only to be consistent. After further
consideration, I have found that it's actually really stupid.
There's no reason to be accessing it through screenbuffer, and it's
probably an artifact of 2.0-2.2 passing stack-allocated otherwise-global
classes everywhere through function arguments. Also, it leads to stupid
bugs where screenbuffer could potentially be NULL, which has already
resulted in various annoying crashes in the past. Although those could
be fixed by simply initializing screenbuffer at the very top of main(),
but, why not just scrap the whole thing anyway?
So that's what I'm doing.
As a nice side effect, I've removed the transitive include of Screen.h
from Graphics.h. This could've been done already since it only includes
it for the pointer anyway, but it's still good to do it now.
2021-12-25 08:56:47 +01:00
if ( gameScreen . isFiltered )
2020-11-13 01:45:51 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: LINEAR " ) , tr , tg , tb ) ;
2020-11-13 01:45:51 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: NEAREST " ) , tr , tg , tb ) ;
2020-04-16 04:52:49 +02:00
}
2021-12-26 07:55:55 +01:00
}
2020-04-16 04:52:49 +02:00
2021-12-26 07:55:55 +01:00
if ( game . currentmenuoption = = offset + 4 )
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Analogue Mode " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " There is nothing wrong with your television set. Do not attempt to adjust the picture. " ) , tr , tg , tb ) ;
2021-12-26 07:55:55 +01:00
}
if ( game . currentmenuoption = = offset + 5 )
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Toggle VSync " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Turn VSync on or off. " ) , tr , tg , tb ) ;
2020-05-04 22:19:47 +02:00
Extern `gameScreen`, remove `screenbuffer`
I know earlier I removed the gameScreen extern in favor of using
screenbuffer, but that was only to be consistent. After further
consideration, I have found that it's actually really stupid.
There's no reason to be accessing it through screenbuffer, and it's
probably an artifact of 2.0-2.2 passing stack-allocated otherwise-global
classes everywhere through function arguments. Also, it leads to stupid
bugs where screenbuffer could potentially be NULL, which has already
resulted in various annoying crashes in the past. Although those could
be fixed by simply initializing screenbuffer at the very top of main(),
but, why not just scrap the whole thing anyway?
So that's what I'm doing.
As a nice side effect, I've removed the transitive include of Screen.h
from Graphics.h. This could've been done already since it only includes
it for the pointer anyway, but it's still good to do it now.
2021-12-25 08:56:47 +01:00
if ( ! gameScreen . vsync )
2020-05-04 22:19:47 +02:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: VSYNC OFF " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2020-05-04 22:19:47 +02:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Current mode: VSYNC ON " ) , tr , tg , tb ) ;
2020-05-04 22:19:47 +02:00
}
2020-04-16 04:52:49 +02:00
}
2020-04-17 00:19:17 +02:00
break ;
2021-12-26 07:55:55 +01:00
}
2021-04-12 00:18:35 +02:00
case Menu : : audiooptions :
switch ( game . currentmenuoption )
{
case 0 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Music Volume " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Change the volume of the music. " ) , tr , tg , tb ) ;
2021-04-12 02:43:17 +02:00
volumesliderrender ( ) ;
2021-04-12 00:18:35 +02:00
break ;
case 1 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Sound Volume " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Change the volume of sound effects. " ) , tr , tg , tb ) ;
2021-04-12 02:43:17 +02:00
volumesliderrender ( ) ;
2021-04-12 00:18:35 +02:00
break ;
case 2 :
2022-12-30 23:43:24 +01:00
{
2021-04-12 00:18:35 +02:00
if ( ! music . mmmmmm )
{
break ;
}
2022-12-30 23:43:24 +01:00
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Soundtrack " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Toggle between MMMMMM and PPPPPP. " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
const char * soundtrack ;
2021-04-12 00:18:35 +02:00
if ( music . usingmmmmmm )
{
2022-12-30 23:43:24 +01:00
soundtrack = loc : : gettext ( " Current soundtrack: MMMMMM " ) ;
2021-04-12 00:18:35 +02:00
}
else
{
2022-12-30 23:43:24 +01:00
soundtrack = loc : : gettext ( " Current soundtrack: PPPPPP " ) ;
2021-04-12 00:18:35 +02:00
}
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , soundtrack , tr , tg , tb ) ;
2021-04-12 00:18:35 +02:00
break ;
}
}
break ;
2020-04-17 00:19:17 +02:00
case Menu : : credits :
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 50 , loc : : gettext ( " VVVVVV is a game by " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 65 , " Terry Cavanagh " , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-07 19:28:07 +01:00
graphics . drawimagecol ( IMAGE_SITE , - 1 , 86 , graphics . getRGB ( tr , tg , tb ) , true ) ;
2020-01-01 21:29:24 +01:00
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 120 , loc : : gettext ( " and features music by " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 135 , " Magnus Pålsson " , tr , tg , tb ) ;
2023-01-07 19:28:07 +01:00
graphics . drawimagecol ( IMAGE_SITE2 , - 1 , 156 , graphics . getRGB ( tr , tg , tb ) , true ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : credits2 :
2023-12-06 00:34:05 +01:00
font : : print ( PR_CEN , - 1 , 40 , loc : : gettext ( " Roomnames are by " ) , tr , tg , tb ) ;
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 55 , " Bennett Foddy " , tr , tg , tb ) ;
graphics . drawimagecol ( IMAGE_SITE3 , - 1 , 76 , graphics . getRGB ( tr , tg , tb ) , true ) ;
font : : print ( PR_CEN , - 1 , 100 , loc : : gettext ( " C++ version by " ) , tr , tg , tb ) ;
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 115 , " Simon Roth " , tr , tg , tb ) ;
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 135 , " Ethan Lee " , tr , tg , tb ) ;
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 155 , " Misa Kai " , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : credits25 :
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 40 , loc : : gettext ( " Beta Testing by " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 55 , " Sam Kaplan " , tr , tg , tb ) ;
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 75 , " Pauli Kohberger " , tr , tg , tb ) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 130 , loc : : gettext ( " Ending Picture by " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 145 , " Pauli Kohberger " , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
2023-12-06 00:34:05 +01:00
case Menu : : credits_localisations_implementation :
font : : print ( PR_CEN , - 1 , 30 , loc : : gettext ( " Localisation Project Led by " ) , tr , tg , tb ) ;
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 45 , " Dav999 " , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 75 , loc : : gettext ( " Pan-European Font Design by " ) , tr , tg , tb ) ;
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 90 , " Reese Rivers " , tr , tg , tb ) ;
font : : print_wrap ( PR_CEN , - 1 , 125 , loc : : gettext ( " With contributions on GitHub from " ) , tr , tg , tb ) ;
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 140 , " Alexandra Fox " , tr , tg , tb ) ;
font : : print ( PR_2X | PR_CEN | PR_FONT_8X8 , - 1 , 160 , " mothbeanie " , tr , tg , tb ) ;
break ;
case Menu : : credits_localisations_translations :
{
2023-12-08 23:02:35 +01:00
font : : print_wrap ( PR_2X | PR_CEN , - 1 , 15 , loc : : gettext ( " Translators " ) , tr , tg , tb ) ;
2023-12-06 00:34:05 +01:00
int startidx = game . current_credits_list_index ;
int endidx = game . current_credits_list_index ;
endidx + = Credits : : translator_pagesize [ game . translator_credits_pagenum ] ;
endidx = SDL_min ( endidx , ( int ) SDL_arraysize ( Credits : : translators ) ) ;
int maxheight = 110 ;
int totalheight = ( endidx - startidx ) * 10 ;
int emptyspace = maxheight - totalheight ;
int yofs = 50 + ( emptyspace / 2 ) ;
for ( int i = startidx ; i < endidx ; + + i )
{
2024-02-02 17:37:23 +01:00
if ( Credits : : translators [ i ] [ 0 ] = = ' > ' )
{
yofs + = 2 ;
font : : print ( PR_CJK_HIGH , 88 , yofs , loc : : gettext ( & Credits : : translators [ i ] [ 1 ] ) , tr , tg , tb ) ;
}
else if ( Credits : : translators [ i ] [ 0 ] ! = ' ' )
2023-12-06 00:34:05 +01:00
{
2023-12-08 23:28:44 +01:00
yofs + = 5 ;
font : : print ( PR_CJK_HIGH , 80 , yofs , loc : : gettext ( Credits : : translators [ i ] ) , tr , tg , tb ) ;
}
else
{
font : : print ( PR_FONT_8X8 , 80 , yofs , Credits : : translators [ i ] , tr , tg , tb ) ;
2023-12-06 00:34:05 +01:00
}
yofs + = 10 ;
}
break ;
}
2020-04-17 00:19:17 +02:00
case Menu : : credits3 :
2020-04-16 01:29:54 +02:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 20 , loc : : gettext ( " VVVVVV is supported by the following patrons " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
int startidx = game . current_credits_list_index ;
Remove `VVV_min/max` in favor of `SDL_min/max`
VVV_min/max are functions that only operate on ints, and SDL_min/max are
macros that operate on any type but double-evaluate everything.
I know I more-or-less said earlier that SDL_min/max were dumb but I've
changed my mind and think it's better to use them, taking care to make
sure you don't double-evaluate, rather than trying to generate your own
litany of functions with either your own hand-rolled generation macros,
C++ templates, C11 generics, or GCC extensions (that last one you'd
technically use in a macro but it doesn't really matter), all of which
have more downsides than just not double-evaluating.
And the upside of not double-evaluating is that you're disencouraged
from having really complicated single-line min/max expressions and
encouraged to precompute the values beforehand anyway so the final
min/max is more readable. And furthermore you'll notice when you
yourself end up doing double-evaluations anyway. I removed a couple
instances of Graphics::len() being double-evaluated in this commit (as
well as cleaned up some other min/max-using code). Although the only
downside to those double-evaluations was unnecessary computation,
rather than checking the wrong result or having multiple side effects,
thankfully, it's still good to minimize double-evaluations where
possible.
2021-12-23 01:43:31 +01:00
int endidx = SDL_min ( startidx + 9 , ( int ) SDL_arraysize ( Credits : : superpatrons ) ) ;
2020-04-16 01:29:54 +02:00
int xofs = 80 - 16 ;
int yofs = 40 + 20 ;
for ( int i = startidx ; i < endidx ; + + i )
2020-01-01 21:29:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_FONT_8X8 , xofs , yofs , Credits : : superpatrons [ i ] , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
xofs + = 4 ;
yofs + = 14 ;
2020-01-01 21:29:24 +01:00
}
2020-04-17 00:19:17 +02:00
break ;
2020-04-16 01:29:54 +02:00
}
2020-04-17 00:19:17 +02:00
case Menu : : credits4 :
2020-04-16 01:29:54 +02:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 20 , loc : : gettext ( " and also by " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
int startidx = game . current_credits_list_index ;
Remove `VVV_min/max` in favor of `SDL_min/max`
VVV_min/max are functions that only operate on ints, and SDL_min/max are
macros that operate on any type but double-evaluate everything.
I know I more-or-less said earlier that SDL_min/max were dumb but I've
changed my mind and think it's better to use them, taking care to make
sure you don't double-evaluate, rather than trying to generate your own
litany of functions with either your own hand-rolled generation macros,
C++ templates, C11 generics, or GCC extensions (that last one you'd
technically use in a macro but it doesn't really matter), all of which
have more downsides than just not double-evaluating.
And the upside of not double-evaluating is that you're disencouraged
from having really complicated single-line min/max expressions and
encouraged to precompute the values beforehand anyway so the final
min/max is more readable. And furthermore you'll notice when you
yourself end up doing double-evaluations anyway. I removed a couple
instances of Graphics::len() being double-evaluated in this commit (as
well as cleaned up some other min/max-using code). Although the only
downside to those double-evaluations was unnecessary computation,
rather than checking the wrong result or having multiple side effects,
thankfully, it's still good to minimize double-evaluations where
possible.
2021-12-23 01:43:31 +01:00
int endidx = SDL_min ( startidx + 14 , ( int ) SDL_arraysize ( Credits : : patrons ) ) ;
2020-04-16 01:29:54 +02:00
int maxheight = 10 * 14 ;
int totalheight = ( endidx - startidx ) * 10 ;
int emptyspace = maxheight - totalheight ;
int yofs = 40 + ( emptyspace / 2 ) ;
for ( int i = startidx ; i < endidx ; + + i )
2020-01-01 21:29:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_FONT_8X8 , 80 , yofs , Credits : : patrons [ i ] , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
yofs + = 10 ;
2020-01-01 21:29:24 +01:00
}
2020-04-17 00:19:17 +02:00
break ;
2020-04-16 01:29:54 +02:00
}
2020-04-17 00:19:17 +02:00
case Menu : : credits5 :
2020-04-16 01:29:54 +02:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 20 , loc : : gettext ( " With contributions on GitHub from " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2020-04-16 01:29:54 +02:00
int startidx = game . current_credits_list_index ;
Remove `VVV_min/max` in favor of `SDL_min/max`
VVV_min/max are functions that only operate on ints, and SDL_min/max are
macros that operate on any type but double-evaluate everything.
I know I more-or-less said earlier that SDL_min/max were dumb but I've
changed my mind and think it's better to use them, taking care to make
sure you don't double-evaluate, rather than trying to generate your own
litany of functions with either your own hand-rolled generation macros,
C++ templates, C11 generics, or GCC extensions (that last one you'd
technically use in a macro but it doesn't really matter), all of which
have more downsides than just not double-evaluating.
And the upside of not double-evaluating is that you're disencouraged
from having really complicated single-line min/max expressions and
encouraged to precompute the values beforehand anyway so the final
min/max is more readable. And furthermore you'll notice when you
yourself end up doing double-evaluations anyway. I removed a couple
instances of Graphics::len() being double-evaluated in this commit (as
well as cleaned up some other min/max-using code). Although the only
downside to those double-evaluations was unnecessary computation,
rather than checking the wrong result or having multiple side effects,
thankfully, it's still good to minimize double-evaluations where
possible.
2021-12-23 01:43:31 +01:00
int endidx = SDL_min ( startidx + 9 , ( int ) SDL_arraysize ( Credits : : githubfriends ) ) ;
2020-02-12 05:45:58 +01:00
2020-04-16 01:29:54 +02:00
int maxheight = 14 * 9 ;
int totalheight = ( endidx - startidx ) * 14 ;
int emptyspace = maxheight - totalheight ;
2020-02-12 05:45:58 +01:00
2021-03-31 08:43:05 +02:00
int xofs , yofs ;
2021-09-01 08:19:25 +02:00
xofs = 80 - 16 ;
yofs = 40 + 20 + ( emptyspace / 2 ) ;
2020-01-01 21:29:24 +01:00
2020-04-16 01:29:54 +02:00
for ( int i = startidx ; i < endidx ; + + i )
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_FONT_8X8 , xofs , yofs , Credits : : githubfriends [ i ] , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
xofs + = 4 ;
yofs + = 14 ;
}
2020-04-17 00:19:17 +02:00
break ;
2020-04-16 01:29:54 +02:00
}
2020-04-17 00:19:17 +02:00
case Menu : : credits6 :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 20 , loc : : gettext ( " and thanks also to: " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 60 , loc : : gettext ( " You! " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 100 , loc : : gettext ( " Your support makes it possible for me to continue making the games I want to make, now and into the future. " ) , tr , tg , tb ) ;
2020-02-12 05:45:58 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 150 , loc : : gettext ( " Thank you! " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : setinvincibility :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 100 , loc : : gettext ( " Are you sure you want to enable invincibility? " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
2020-04-17 01:02:01 +02:00
case Menu : : setslowdown :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Game Speed " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Select a new game speed below. " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
drawslowdowntext ( next_y ) ;
2020-04-17 00:19:17 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2020-04-17 00:19:17 +02:00
case Menu : : newgamewarning :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 100 , loc : : gettext ( " Are you sure? This will delete your current saves... " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : cleardatamenu :
2021-08-12 05:54:02 +02:00
case Menu : : clearcustomdatamenu :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 100 , loc : : gettext ( " Are you sure you want to delete all your saved data? " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
2021-08-12 05:26:58 +02:00
case Menu : : deletequicklevel :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 100 , loc : : gettext ( " Are you sure you want to delete your quicksave? " ) , tr , tg , tb ) ;
2021-08-12 05:26:58 +02:00
break ;
2020-04-17 00:19:17 +02:00
case Menu : : startnodeathmode :
2022-12-30 23:43:24 +01:00
{
int next_y ;
2023-01-20 21:11:18 +01:00
next_y = font : : print_wrap ( PR_CEN , - 1 , 45 , loc : : gettext ( " Good luck! " ) , tr , tg , tb ) ;
next_y = font : : print_wrap ( PR_CEN , - 1 , next_y + 15 , loc : : gettext ( " You cannot save in this mode. " ) , tr , tg , tb ) ;
font : : print_wrap ( PR_CEN , - 1 , next_y + 5 , loc : : gettext ( " Would you like to disable the cutscenes during the game? " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2020-04-17 00:19:17 +02:00
case Menu : : controller :
2023-07-12 22:58:47 +02:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Game Pad " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 55 , loc : : gettext ( " Change controller options. " ) , tr , tg , tb ) ;
2023-07-12 22:58:47 +02:00
int spacing = font : : height ( 0 ) ;
spacing = SDL_max ( spacing , 10 ) ;
2020-04-16 05:10:11 +02:00
switch ( game . currentmenuoption )
2020-04-16 01:29:54 +02:00
{
2020-04-16 05:10:11 +02:00
case 0 :
2023-01-16 21:29:50 +01:00
{
2024-01-05 02:13:57 +01:00
font : : print ( PR_RTL_XFLIP , 32 , 75 , loc : : gettext ( " Low " ) , tr , tg , tb ) ;
2023-01-16 21:29:50 +01:00
font : : print ( PR_CEN , - 1 , 75 , loc : : gettext ( " Medium " ) , tr , tg , tb ) ;
2024-01-05 02:13:57 +01:00
font : : print ( PR_RIGHT | PR_RTL_XFLIP , 288 , 75 , loc : : gettext ( " High " ) , tr , tg , tb ) ;
2023-01-16 21:29:50 +01:00
char slider [ SCREEN_WIDTH_CHARS + 1 ] ;
slider_get ( slider , sizeof ( slider ) , key . sensitivity , 5 , 240 ) ;
2023-07-12 22:58:47 +02:00
font : : print ( PR_CEN , - 1 , 75 + spacing , slider , tr , tg , tb ) ;
2020-04-16 05:10:11 +02:00
break ;
2023-01-16 21:29:50 +01:00
}
2020-04-16 05:10:11 +02:00
case 1 :
case 2 :
case 3 :
2020-08-09 00:41:59 +02:00
case 4 :
2021-04-19 08:23:44 +02:00
case 5 :
2023-03-13 04:16:36 +01:00
{
char buffer_a [ SCREEN_WIDTH_CHARS + 1 ] ;
char buffer_b [ SCREEN_WIDTH_CHARS + 1 ] ;
SDL_snprintf ( buffer_a , sizeof ( buffer_a ) , " %s%s " ,
loc : : gettext ( " Flip is bound to: " ) ,
BUTTONGLYPHS_get_all_gamepad_buttons ( buffer_b , sizeof ( buffer_b ) , ActionSet_InGame , Action_InGame_ACTION )
) ;
font : : print ( PR_CEN , - 1 , 75 , buffer_a , tr , tg , tb ) ;
SDL_snprintf ( buffer_a , sizeof ( buffer_a ) , " %s%s " ,
loc : : gettext ( " Enter is bound to: " ) ,
BUTTONGLYPHS_get_all_gamepad_buttons ( buffer_b , sizeof ( buffer_b ) , ActionSet_InGame , Action_InGame_Map )
) ;
2023-07-12 22:58:47 +02:00
font : : print ( PR_CEN , - 1 , 75 + spacing , buffer_a , tr , tg , tb ) ;
2023-03-13 04:16:36 +01:00
SDL_snprintf ( buffer_a , sizeof ( buffer_a ) , " %s%s " ,
loc : : gettext ( " Menu is bound to: " ) ,
BUTTONGLYPHS_get_all_gamepad_buttons ( buffer_b , sizeof ( buffer_b ) , ActionSet_InGame , Action_InGame_Esc )
) ;
2023-07-12 22:58:47 +02:00
font : : print ( PR_CEN , - 1 , 75 + spacing * 2 , buffer_a , tr , tg , tb ) ;
2023-03-13 04:16:36 +01:00
SDL_snprintf ( buffer_a , sizeof ( buffer_a ) , " %s%s " ,
loc : : gettext ( " Restart is bound to: " ) ,
BUTTONGLYPHS_get_all_gamepad_buttons ( buffer_b , sizeof ( buffer_b ) , ActionSet_InGame , Action_InGame_Restart )
) ;
2023-07-12 22:58:47 +02:00
font : : print ( PR_CEN , - 1 , 75 + spacing * 3 , buffer_a , tr , tg , tb ) ;
2023-03-13 04:16:36 +01:00
SDL_snprintf ( buffer_a , sizeof ( buffer_a ) , " %s%s " ,
loc : : gettext ( " Interact is bound to: " ) ,
BUTTONGLYPHS_get_all_gamepad_buttons ( buffer_b , sizeof ( buffer_b ) , ActionSet_InGame , Action_InGame_Interact )
) ;
2023-07-12 22:58:47 +02:00
font : : print ( PR_CEN | PR_BRIGHTNESS ( game . separate_interact ? 255 : 128 ) , - 1 , 75 + spacing * 4 , buffer_a , tr , tg , tb ) ;
2020-04-16 05:10:11 +02:00
break ;
2020-04-16 01:29:54 +02:00
}
2023-03-13 04:16:36 +01:00
}
2020-01-01 21:29:24 +01:00
2020-02-12 05:45:58 +01:00
2022-12-30 22:57:24 +01:00
break ;
2023-07-12 22:58:47 +02:00
}
2022-12-30 22:57:24 +01:00
case Menu : : language :
if ( loc : : languagelist . empty ( ) )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 90 , loc : : gettext ( " ERROR: No language files found. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
}
else if ( ( unsigned ) game . currentmenuoption < loc : : languagelist . size ( ) )
{
2023-03-04 23:29:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 8 , loc : : languagelist [ game . currentmenuoption ] . credit . c_str ( ) , tr / 2 , tg / 2 , tb / 2 ) ;
2023-03-26 20:15:45 +02:00
const char * select_hint ;
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
if ( BUTTONGLYPHS_keyboard_is_active ( ) )
{
select_hint = loc : : languagelist [ game . currentmenuoption ] . action_hint . c_str ( ) ;
}
else
{
vformat_buf ( buffer , sizeof ( buffer ) ,
loc : : languagelist [ game . currentmenuoption ] . gamepad_hint . c_str ( ) ,
" button:but " ,
vformat_button ( ActionSet_Menu , Action_Menu_Accept )
) ;
select_hint = buffer ;
}
font : : print ( PR_CEN , - 1 , 230 , select_hint , tr / 2 , tg / 2 , tb / 2 ) ;
2022-12-30 22:57:24 +01:00
}
break ;
case Menu : : translator_main :
switch ( game . currentmenuoption )
{
case 0 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Translator options " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Some options that are useful for translators and developers. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
break ;
case 1 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Maintenance " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Sync all language files after adding new strings. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
break ;
}
{
if ( FILESYSTEM_isMainLangDirFromRepo ( ) )
{
// Just giving people who manually compiled the game some hint as to why this menu is here!
2023-01-20 20:24:41 +01:00
font : : print ( 0 , 8 , 208 , loc : : gettext ( " Repository language folder: " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2022-12-30 22:57:24 +01:00
}
else
{
2023-01-20 20:24:41 +01:00
font : : print ( 0 , 8 , 208 , loc : : gettext ( " Language folder: " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2022-12-30 22:57:24 +01:00
}
2023-01-17 04:41:12 +01:00
font : : print ( PR_RIGHT , 316 , 224 , FILESYSTEM_getUserMainLangDirectory ( ) , tr / 2 , tg / 2 , tb / 2 ) ;
2022-12-30 22:57:24 +01:00
}
break ;
case Menu : : translator_options :
switch ( game . currentmenuoption )
{
case 0 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Statistics " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Count the amount of untranslated strings for this language. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
break ;
case 1 :
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Translate rooms " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Enable room name translation mode, so you can translate room names in context. Press I for invincibility. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
if ( roomname_translator : : enabled )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Currently ENABLED! " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Currently Disabled. " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2022-12-30 22:57:24 +01:00
}
break ;
}
case 2 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Explore game " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Explore the rooms of any level in the game, to find all room names to translate. " ) , tr , tg , tb ) ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
break ;
case 3 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Menu test " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Cycle through most menus in the game. The menus will not actually work, all options take you to the next menu instead. Press Escape to stop. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
break ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
case 4 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Cutscene test " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Display all text boxes from cutscenes.xml. Only tests the basic appearance of each individual text box. " ) , tr , tg , tb ) ;
2022-12-24 04:16:56 +01:00
break ;
case 5 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Limits check " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Find translations that don't fit within their defined bounds. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
break ;
}
break ;
case Menu : : translator_options_limitscheck :
{
size_t of = loc : : limitscheck_current_overflow ;
if ( of > = loc : : text_overflows . size ( ) )
{
int next_y ;
if ( loc : : text_overflows . empty ( ) )
{
2023-01-20 21:11:18 +01:00
next_y = font : : print_wrap ( PR_CEN , - 1 , 20 , loc : : gettext ( " No text overflows found! " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
next_y = font : : print_wrap ( PR_CEN , - 1 , 20 , loc : : gettext ( " No text overflows left! " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
}
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Note that this detection isn't perfect. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
}
else
{
loc : : TextOverflow & overflow = loc : : text_overflows [ of ] ;
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf ( buffer , sizeof ( buffer ) ,
" {page}/{total} {max_w}*{max_h} ({max_w_px}x{max_h_px}) [{lang}] " ,
" page:int, total:int, max_w:int, max_h:int, max_w_px:int, max_h_px:int, lang:str " ,
( int ) of + 1 , ( int ) loc : : text_overflows . size ( ) ,
overflow . max_w , overflow . max_h ,
overflow . max_w_px , overflow . max_h_px ,
overflow . lang . c_str ( )
) ;
2023-01-16 21:29:50 +01:00
font : : print ( PR_FONT_8X8 , 10 , 10 , buffer , tr / 2 , tg / 2 , tb / 2 ) ;
2023-01-15 01:31:02 +01:00
2022-12-30 22:57:24 +01:00
int box_x = SDL_min ( 10 , ( 320 - overflow . max_w_px ) / 2 ) ;
2023-01-16 21:29:50 +01:00
graphics . fill_rect ( box_x - 1 , 30 - 1 , overflow . max_w_px + 2 , overflow . max_h_px + 2 , tr / 3 , tg / 3 , tb / 3 ) ;
2022-12-30 22:57:24 +01:00
int wraplimit ;
if ( overflow . multiline )
{
wraplimit = overflow . max_w_px ;
}
else
{
wraplimit = 320 - box_x ;
}
if ( overflow . text ! = NULL )
{
2023-01-16 21:29:50 +01:00
font : : print_wrap ( overflow . flags , box_x , 30 , overflow . text , tr , tg , tb , - 1 , wraplimit ) ;
2022-12-30 22:57:24 +01:00
}
}
break ;
}
case Menu : : translator_options_stats :
{
2023-01-20 20:24:41 +01:00
font : : print ( 0 , 16 , 16 , loc : : get_langmeta ( ) - > nativename , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
const char * line_template = " %4d " ;
char buffer [ 5 ] ;
int coldiv ;
# define stat_line(y, filename, untranslated_counter) \
SDL_snprintf ( buffer , sizeof ( buffer ) , line_template , \
untranslated_counter \
) ; \
coldiv = untranslated_counter > 0 ? 1 : 2 ; \
2023-01-20 20:24:41 +01:00
font : : print ( PR_FONT_8X8 , 16 , y , filename , tr / coldiv , tg / coldiv , tb / coldiv ) ; \
font : : print ( PR_FONT_8X8 , 272 , y , buffer , tr / coldiv , tg / coldiv , tb / coldiv )
2022-12-30 22:57:24 +01:00
stat_line ( 48 , " strings.xml " , loc : : n_untranslated [ loc : : UNTRANSLATED_STRINGS ] ) ;
stat_line ( 64 , " numbers.xml " , loc : : n_untranslated [ loc : : UNTRANSLATED_NUMBERS ] ) ;
stat_line ( 80 , " strings_plural.xml " , loc : : n_untranslated [ loc : : UNTRANSLATED_STRINGS_PLURAL ] ) ;
stat_line ( 96 , " cutscenes.xml " , loc : : n_untranslated [ loc : : UNTRANSLATED_CUTSCENES ] ) ;
stat_line ( 112 , " roomnames.xml " , loc : : n_untranslated_roomnames ) ;
stat_line ( 128 , " roomnames_special.xml " , loc : : n_untranslated [ loc : : UNTRANSLATED_ROOMNAMES_SPECIAL ] ) ;
# undef stat_line
break ;
}
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
case Menu : : translator_options_exploregame :
switch ( game . currentmenuoption )
{
case 0 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Space Station 1 " ) , tr , tg , tb ) ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
break ;
case 1 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " The Laboratory " ) , tr , tg , tb ) ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
break ;
case 2 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " The Tower " ) , tr , tg , tb ) ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
break ;
case 3 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Space Station 2 " ) , tr , tg , tb ) ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
break ;
case 4 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " The Warp Zone " ) , tr , tg , tb ) ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
break ;
case 5 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Intermission 1 " ) , tr , tg , tb ) ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
break ;
case 6 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Intermission 2 " ) , tr , tg , tb ) ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
break ;
case 7 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " The Final Level " ) , tr , tg , tb ) ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
break ;
}
2022-11-27 20:06:08 +01:00
if ( roomname_translator : : enabled )
{
if ( game . currentmenuoption > = 0 & & game . currentmenuoption < 8 )
{
int names_left = loc : : n_untranslated_roomnames_area [ game . currentmenuoption + 1 ] ;
int coldiv = names_left > 0 ? 1 : 2 ;
char buffer [ 4 * SCREEN_WIDTH_CHARS + 1 ] ;
loc : : gettext_plural_fill (
buffer , sizeof ( buffer ) ,
" {n} normal room names untranslated " ,
" {n} normal room name untranslated " ,
" n:int " ,
names_left
) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , buffer , tr / coldiv , tg / coldiv , tb / coldiv ) ;
2022-11-27 20:06:08 +01:00
}
}
else
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " You have not enabled room name translation mode! " ) , tr , tg , tb ) ;
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
}
break ;
2022-12-30 22:57:24 +01:00
case Menu : : translator_maintenance :
switch ( game . currentmenuoption )
{
case 0 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Sync language files " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Merge all new strings from the template files into the translation files, keeping existing translations. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
break ;
case 1 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Statistics " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Count the amount of untranslated strings for each language. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
break ;
case 2 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Limits check " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Find translations that don't fit within their defined bounds. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
}
break ;
case Menu : : translator_maintenance_sync :
{
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 20 , loc : : gettext ( " If new strings were added to the English template language files, this feature will insert them in the translation files for all languages. Make a backup, just in case. " ) , tr , tg , tb ) ;
2022-12-30 22:57:24 +01:00
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , next_y , loc : : gettext ( " Full syncing EN→All: " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
next_y = font : : print_wrap ( PR_CEN | PR_FONT_8X8 , - 1 , next_y + 10 , " meta.xml \n strings.xml \n strings_plural.xml \n cutscenes.xml \n roomnames.xml \n roomnames_special.xml " , tr / 2 , tg / 2 , tb / 2 ) ;
2022-12-30 22:57:24 +01:00
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , next_y , loc : : gettext ( " Syncing not supported: " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print_wrap ( PR_CEN | PR_FONT_8X8 , - 1 , next_y + 10 , " numbers.xml " , tr / 2 , tg / 2 , tb / 2 ) ;
2022-12-30 22:57:24 +01:00
break ;
}
case Menu : : translator_error_setlangwritedir :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 95 , loc : : gettext ( " ERROR: Could not write to language folder! Make sure there is no \" lang \" folder next to the regular saves. " ) , tr , tg , tb ) ;
2020-06-30 22:06:19 +02:00
break ;
2021-04-09 17:53:55 +02:00
case Menu : : speedrunneroptions :
switch ( game . currentmenuoption )
{
case 0 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Glitchrunner Mode " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Re-enable glitches that existed in previous versions of the game. " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
drawglitchrunnertext ( next_y ) ;
2021-04-09 17:53:55 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2021-04-09 17:53:55 +02:00
case 1 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Input Delay " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Re-enable the 1-frame input delay from previous versions of the game. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
if ( game . inputdelay )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Input delay is ON " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Input delay is OFF " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2021-04-09 17:53:55 +02:00
}
break ;
2022-12-30 23:43:24 +01:00
}
2021-04-09 17:53:55 +02:00
case 2 :
2021-04-19 08:23:44 +02:00
{
2021-09-13 06:39:07 +02:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
2021-04-19 08:23:44 +02:00
const char * button ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Interact Button " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Toggle whether you interact with prompts using ENTER or E. " ) , tr , tg , tb ) ;
2021-04-19 08:23:44 +02:00
if ( game . separate_interact )
{
2022-12-30 23:43:24 +01:00
button = loc : : gettext ( " E " ) ;
2021-04-19 08:23:44 +02:00
}
else
{
2022-12-30 23:43:24 +01:00
button = loc : : gettext ( " ENTER " ) ;
2021-04-19 08:23:44 +02:00
}
2022-12-30 23:43:24 +01:00
vformat_buf ( buffer , sizeof ( buffer ) , loc : : gettext ( " Interact button: {button} " ) , " button:str " , button ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , buffer , tr , tg , tb ) ;
2021-04-19 08:23:44 +02:00
break ;
}
case 3 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Fake Load Screen " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Disable the fake loading screen which appears on game launch. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
if ( game . skipfakeload )
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Fake loading screen is OFF " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2021-04-09 17:53:55 +02:00
else
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Fake loading screen is ON " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2021-08-05 23:31:20 +02:00
case 4 :
2023-10-02 02:44:13 +02:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " In-Game Timer " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Toggle the in-game timer outside of time trials. " ) , tr , tg , tb ) ;
2021-08-05 23:31:20 +02:00
if ( game . showingametimer )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " In-Game Timer is ON " ) , tr , tg , tb ) ;
2021-08-06 00:04:18 +02:00
}
else
2021-08-05 23:31:20 +02:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " In-Game Timer is OFF " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2021-08-05 23:31:20 +02:00
}
break ;
2021-04-09 17:53:55 +02:00
}
2023-10-02 02:44:13 +02:00
case 5 :
{
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " English Sprites " ) , tr , tg , tb ) ;
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Show the original English word enemies regardless of your language setting. " ) , tr , tg , tb ) ;
if ( loc : : english_sprites )
{
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Sprites are currently ALWAYS ENGLISH " ) , tr , tg , tb ) ;
}
else
{
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Sprites are currently translated " ) , tr / 2 , tg / 2 , tb / 2 ) ;
}
break ;
}
}
2021-04-09 17:53:55 +02:00
break ;
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
case Menu : : setglitchrunner :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Glitchrunner Mode " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Select a new glitchrunner version below. " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
drawglitchrunnertext ( next_y ) ;
Split glitchrunner mode into multiple versions
Previously, turning glitchrunner mode on essentially locked you to
emulating 2.0, and turning it off just meant normal 2.3 behavior. But
what if you wanted 2.2 behavior instead? Well, that's what I had to ask
when a TAS of mine would desync in 2.3 because of the two-frame delay
fix (glitchrunner off), but would also desync because of 2.0 warp lines
(glitchrunner on).
What I've done is made it so there are three states to glitchrunner mode
now: 2.0 (previously just the "on" state), 2.2 (previously a state you
couldn't use), and "off". Furthermore, I made it an enum, so in case
future versions of the game patch out more glitches, we can add them to
the enum (and the only other thing we have to update is a lookup table
in GlitchrunnerMode.c). Also, 2.2 glitches exist in 2.0, so you'll want
to use GlitchrunnerMode_less_than_or_equal() to check glitchrunner
version.
2021-08-05 02:09:49 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2020-06-30 22:06:19 +02:00
case Menu : : advancedoptions :
switch ( game . currentmenuoption )
{
case 0 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Unfocus Pause " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Toggle if the game will pause when the window is unfocused. " ) , tr , tg , tb ) ;
2020-06-30 22:06:19 +02:00
if ( game . disablepause )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Unfocus pause is OFF " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2020-06-30 22:06:19 +02:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Unfocus pause is ON " ) , tr , tg , tb ) ;
2020-06-30 22:06:19 +02:00
}
break ;
2022-12-30 23:43:24 +01:00
}
2021-04-17 07:00:33 +02:00
case 1 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Unfocus Audio " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Toggle if the audio will pause when the window is unfocused. " ) , tr , tg , tb ) ;
2021-08-05 21:20:05 +02:00
if ( game . disableaudiopause )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Unfocus audio pause is OFF " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2021-08-05 21:20:05 +02:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Unfocus audio pause is ON " ) , tr , tg , tb ) ;
2021-08-05 21:20:05 +02:00
}
break ;
2022-12-30 23:43:24 +01:00
}
2021-08-05 21:20:05 +02:00
case 2 :
2024-09-26 18:20:18 +02:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Room Name BG " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Lets you see through what is behind the name at the bottom of the screen. " ) , tr , tg , tb ) ;
2020-06-30 22:06:19 +02:00
if ( graphics . translucentroomname )
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Room name background is TRANSLUCENT " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2020-06-30 22:06:19 +02:00
else
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Room name background is OPAQUE " ) , tr , tg , tb ) ;
2020-06-30 22:06:19 +02:00
break ;
}
2024-09-26 18:20:18 +02:00
case 3 :
{
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Checkpoint Saving " ) , tr , tg , tb ) ;
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Toggle if checkpoints should save the game. " ) , tr , tg , tb ) ;
2024-09-26 18:32:46 +02:00
if ( ! game . checkpoint_saving )
2024-09-26 18:20:18 +02:00
{
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Checkpoint saving is OFF " ) , tr / 2 , tg / 2 , tb / 2 ) ;
}
else
{
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Checkpoint saving is ON " ) , tr , tg , tb ) ;
}
break ;
}
}
2020-04-17 00:19:17 +02:00
break ;
case Menu : : accessibility :
2021-04-09 17:53:55 +02:00
{
2021-04-09 18:00:52 +02:00
# ifdef MAKEANDPLAY
# define OFFSET 0
# else
# define OFFSET 1
# endif
switch ( game . currentmenuoption )
{
2021-04-09 12:09:12 +02:00
# if !defined(MAKEANDPLAY)
2021-04-09 18:00:52 +02:00
case 0 :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Unlock Play Modes " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Unlock parts of the game normally unlocked as you progress. " ) , tr , tg , tb ) ;
2021-04-09 18:00:52 +02:00
break ;
2021-04-09 12:09:12 +02:00
# endif
2021-04-09 18:00:52 +02:00
case OFFSET + 0 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Invincibility " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Explore the game freely without dying. (Can cause glitches.) " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
if ( map . invincibility )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Invincibility is ON. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Invincibility is OFF. " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2021-04-09 17:53:55 +02:00
}
2021-04-09 18:00:52 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2021-04-09 18:00:52 +02:00
case OFFSET + 1 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Slowdown " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Reduce the game speed. " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
drawslowdowntext ( next_y ) ;
2021-04-09 18:00:52 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2021-04-09 18:00:52 +02:00
case OFFSET + 2 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Backgrounds " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Disable animated backgrounds in menus and during gameplay. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
if ( ! game . colourblindmode )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Backgrounds are ON. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Backgrounds are OFF. " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2021-04-09 17:53:55 +02:00
}
2021-04-09 18:00:52 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2021-04-09 18:00:52 +02:00
case OFFSET + 3 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Screen Effects " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Disables screen shakes and flashes. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
if ( ! game . noflashingmode )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Screen Effects are ON. " ) , tr , tg , tb ) ;
2021-04-09 17:53:55 +02:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Screen Effects are OFF. " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2021-04-09 17:53:55 +02:00
}
2021-04-09 18:00:52 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2021-04-09 18:00:52 +02:00
case OFFSET + 4 :
2021-06-13 00:19:15 +02:00
{
const char * text ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Text Outline " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Disables outline on game text. " ) , tr , tg , tb ) ;
2021-06-13 00:19:15 +02:00
2023-01-07 19:28:07 +01:00
graphics . fill_rect ( 0 , next_y - 4 , 320 , 16 , tr , tg , tb ) ;
2021-06-13 00:19:15 +02:00
2021-04-09 17:53:55 +02:00
if ( ! graphics . notextoutline )
{
2022-12-30 23:43:24 +01:00
text = loc : : gettext ( " Text outlines are ON. " ) ;
2021-04-09 17:53:55 +02:00
}
else
{
2022-12-30 23:43:24 +01:00
text = loc : : gettext ( " Text outlines are OFF. " ) ;
2021-04-09 17:53:55 +02:00
}
2021-06-13 00:19:15 +02:00
2023-01-17 22:18:39 +01:00
font : : print ( PR_BOR | PR_CEN , - 1 , next_y , text , 255 , 255 , 255 ) ;
2021-04-09 18:00:52 +02:00
break ;
2021-04-09 17:53:55 +02:00
}
2021-06-13 00:19:15 +02:00
}
2021-04-09 17:53:55 +02:00
break ;
2021-04-09 18:00:52 +02:00
# undef OFFSET
2021-04-09 17:53:55 +02:00
}
2020-04-17 00:19:17 +02:00
case Menu : : playint1 :
case Menu : : playint2 :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Who do you want to play the level with? " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : playmodes :
2020-04-16 05:10:11 +02:00
switch ( game . currentmenuoption )
2020-04-02 23:19:15 +02:00
{
2020-04-16 05:10:11 +02:00
case 0 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Time Trials " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Replay any level in the game in a competitive time trial mode. " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
2021-05-04 03:29:23 +02:00
if ( game . nocompetitive ( ) )
2020-04-02 23:19:15 +02:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Time Trials are not available with slowdown or invincibility. " ) , tr , tg , tb ) ;
2020-04-02 23:19:15 +02:00
}
2020-04-16 05:10:11 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2020-04-16 05:10:11 +02:00
case 1 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Intermissions " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Replay the intermission levels. " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
2023-06-06 02:34:27 +02:00
if ( ! game . unlock [ Unlock_INTERMISSION_REPLAYS ] )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " TO UNLOCK: Complete the intermission levels in-game. " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-16 05:10:11 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2020-04-16 05:10:11 +02:00
case 2 :
2022-12-30 23:43:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " No Death Mode " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Play the entire game without dying once. " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
2021-05-04 03:29:23 +02:00
if ( game . nocompetitive ( ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " No Death Mode is not available with slowdown or invincibility. " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2023-06-06 02:34:27 +02:00
else if ( ! game . unlock [ Unlock_NODEATHMODE ] )
2020-01-17 18:37:53 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " TO UNLOCK: Achieve an S-rank or above in at least 4 time trials. " ) , tr , tg , tb ) ;
2020-01-17 18:37:53 +01:00
}
2020-04-16 05:10:11 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2020-04-16 05:10:11 +02:00
case 3 :
2020-06-30 05:45:57 +02:00
// WARNING: Partially duplicated in Menu::options
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Flip Mode " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " Flip the entire game vertically. Compatible with other game modes. " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
2023-06-06 02:34:27 +02:00
if ( game . unlock [ Unlock_FLIPMODE ] )
2020-01-01 21:29:24 +01:00
{
2020-04-16 01:29:54 +02:00
if ( graphics . setflipmode )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Currently ENABLED! " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " Currently Disabled. " ) , tr / 2 , tg / 2 , tb / 2 ) ;
2020-01-01 21:29:24 +01:00
}
2020-01-13 02:45:44 +01:00
}
2020-04-16 01:29:54 +02:00
else
2020-01-25 05:43:04 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , next_y , loc : : gettext ( " TO UNLOCK: Complete the game. " ) , tr , tg , tb ) ;
2020-01-25 05:43:04 +01:00
}
2020-04-16 05:10:11 +02:00
break ;
2020-01-01 21:29:24 +01:00
}
2020-04-17 00:19:17 +02:00
break ;
case Menu : : youwannaquit :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 75 , loc : : gettext ( " Are you sure you want to quit? " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : continuemenu :
2023-09-13 18:25:16 +02:00
{
const char * title = NULL ;
struct Game : : Summary * summary = NULL ;
2020-04-16 05:10:11 +02:00
switch ( game . currentmenuoption )
2020-01-01 21:29:24 +01:00
{
2020-04-16 05:10:11 +02:00
case 0 :
2023-09-13 18:25:16 +02:00
title = loc : : gettext ( " Tele Save " ) ;
summary = & game . last_telesave ;
2020-04-16 05:10:11 +02:00
break ;
case 1 :
2023-09-13 18:25:16 +02:00
title = loc : : gettext ( " Quick Save " ) ;
summary = & game . last_quicksave ;
break ;
}
if ( summary ! = NULL )
2020-08-03 00:11:58 +02:00
{
2021-09-04 00:26:57 +02:00
graphics . drawpixeltextbox ( 17 , 65 - 20 , 286 , 90 , 65 , 185 , 207 ) ;
2020-01-01 21:29:24 +01:00
2023-09-13 18:25:16 +02:00
font : : print ( PR_2X | PR_CEN , - 1 , 20 , title , tr , tg , tb ) ;
font : : print (
PR_CEN , - 1 , 80 - 20 ,
2023-09-13 18:47:20 +02:00
loc : : gettext_roomname_special ( map . currentarea ( summary - > saverx , summary - > savery ) ) ,
2023-09-13 18:25:16 +02:00
25 , 255 - ( help . glow / 2 ) , 255 - ( help . glow / 2 )
) ;
2020-04-16 01:29:54 +02:00
for ( int i = 0 ; i < 6 ; i + + )
{
2023-09-13 18:25:16 +02:00
graphics . drawcrewman ( 169 - ( 3 * 42 ) + ( i * 42 ) , 95 - 20 , i , summary - > crewstats [ i ] , true ) ;
2020-01-01 21:29:24 +01:00
}
2023-09-13 18:25:16 +02:00
font : : print (
0 , 59 , 132 - 20 ,
game . giventimestring (
summary - > hours ,
summary - > minutes ,
summary - > seconds
) ,
255 - ( help . glow / 2 ) , 255 - ( help . glow / 2 ) , 255 - ( help . glow / 2 )
) ;
2022-12-30 23:43:24 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf ( buffer , sizeof ( buffer ) ,
loc : : gettext ( " {savebox_n_trinkets|wordy} " ) ,
" savebox_n_trinkets:int " ,
2023-09-13 18:25:16 +02:00
summary - > trinkets
2022-12-30 23:43:24 +01:00
) ;
2023-01-17 04:41:12 +01:00
font : : print ( PR_RIGHT , 262 , 132 - 20 , buffer , 255 - ( help . glow / 2 ) , 255 - ( help . glow / 2 ) , 255 - ( help . glow / 2 ) ) ;
2020-04-16 01:29:54 +02:00
2023-01-07 19:28:07 +01:00
graphics . draw_sprite ( 34 , 126 - 20 , 50 , graphics . col_clock ) ;
graphics . draw_sprite ( 270 , 126 - 20 , 22 , graphics . col_trinket ) ;
2020-08-03 00:11:58 +02:00
}
2020-04-17 00:19:17 +02:00
break ;
2023-09-13 18:25:16 +02:00
}
2020-04-17 00:19:17 +02:00
case Menu : : gameover :
case Menu : : gameover2 :
2020-04-16 01:29:54 +02:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_3X | PR_CEN , - 1 , 25 , loc : : gettext ( " GAME OVER " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
2021-01-08 01:18:07 +01:00
for ( size_t i = 0 ; i < SDL_arraysize ( game . ndmresultcrewstats ) ; i + + )
2020-01-01 21:29:24 +01:00
{
2021-01-08 01:18:07 +01:00
graphics . drawcrewman ( 169 - ( 3 * 42 ) + ( i * 42 ) , 68 , i , game . ndmresultcrewstats [ i ] , true ) ;
2020-04-16 01:29:54 +02:00
}
2022-12-30 23:43:24 +01:00
char buffer [ 2 * SCREEN_WIDTH_CHARS + 1 ] ;
loc : : gettext_plural_fill (
buffer , sizeof ( buffer ) ,
" You rescued {n_crew|wordy} crewmates " ,
" You rescued {n_crew|wordy} crewmate " ,
" n_crew:int " ,
game . ndmresultcrewrescued
) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 100 , buffer , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
loc : : gettext_plural_fill (
buffer , sizeof ( buffer ) ,
" and found {n_trinkets|wordy} trinkets. " ,
" and found {n_trinkets|wordy} trinket. " ,
" n_trinkets:int " ,
game . ndmresulttrinkets
) ;
2023-01-20 20:24:41 +01:00
font : : print_wrap ( PR_CEN | PR_CJK_LOW , - 1 , 110 , buffer , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 145 , loc : : gettext ( " You managed to reach: " ) , tr , tg , tb ) ;
2024-01-22 03:32:18 +01:00
font : : print (
PR_CEN | PR_CJK_LOW , - 1 , 155 ,
loc : : gettext_roomname (
false ,
game . ndmresulthardestroom_x , game . ndmresulthardestroom_y ,
game . ndmresulthardestroom . c_str ( ) , game . ndmresulthardestroom_specialname
) ,
tr , tg , tb
) ;
2020-01-01 21:29:24 +01:00
2022-12-30 23:43:24 +01:00
const char * encouragement ;
2021-01-08 01:18:07 +01:00
switch ( game . ndmresultcrewrescued )
2020-04-16 01:29:54 +02:00
{
2020-04-16 05:10:11 +02:00
case 1 :
2022-12-30 23:43:24 +01:00
encouragement = loc : : gettext ( " Keep trying! You'll get there! " ) ;
2020-04-16 05:10:11 +02:00
break ;
case 2 :
2022-12-30 23:43:24 +01:00
encouragement = loc : : gettext ( " Nice one! " ) ;
2020-04-16 05:10:11 +02:00
break ;
case 3 :
2022-12-30 23:43:24 +01:00
encouragement = loc : : gettext ( " Wow! Congratulations! " ) ;
2020-04-16 05:10:11 +02:00
break ;
case 4 :
2022-12-30 23:43:24 +01:00
encouragement = loc : : gettext ( " Incredible! " ) ;
2020-04-16 05:10:11 +02:00
break ;
case 5 :
2022-12-30 23:43:24 +01:00
encouragement = loc : : gettext ( " Unbelievable! Well done! " ) ;
2020-04-16 05:10:11 +02:00
break ;
2022-12-30 23:43:24 +01:00
default :
encouragement = loc : : gettext ( " Er, how did you do that? " ) ;
2020-04-16 05:10:11 +02:00
break ;
2020-04-16 01:29:54 +02:00
}
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 190 , encouragement , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
2020-04-16 01:29:54 +02:00
}
2020-04-17 00:19:17 +02:00
case Menu : : nodeathmodecomplete :
case Menu : : nodeathmodecomplete2 :
2020-04-16 01:29:54 +02:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_4X | PR_CEN | PR_CJK_LOW , - 1 , 8 , loc : : gettext ( " WOW " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
2021-01-08 01:18:07 +01:00
for ( size_t i = 0 ; i < SDL_arraysize ( game . ndmresultcrewstats ) ; i + + )
2020-01-01 21:29:24 +01:00
{
2021-01-08 01:18:07 +01:00
graphics . drawcrewman ( 169 - ( 3 * 42 ) + ( i * 42 ) , 68 , i , game . ndmresultcrewstats [ i ] , true ) ;
2020-04-16 01:29:54 +02:00
}
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 100 , loc : : gettext ( " You rescued all the crewmates! " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
char buffer [ 3 * SCREEN_WIDTH_CHARS + 1 ] ;
loc : : gettext_plural_fill (
buffer , sizeof ( buffer ) ,
" And you found {n_trinkets|wordy} trinkets. " ,
" And you found {n_trinkets|wordy} trinket. " ,
" n_trinkets:int " ,
game . ndmresulttrinkets
) ;
2023-01-20 20:24:41 +01:00
font : : print_wrap ( PR_CEN | PR_CJK_LOW , - 1 , 110 , buffer , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 160 , loc : : gettext ( " A new trophy has been awarded and placed in the secret lab to acknowledge your achievement! " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
2020-04-16 01:29:54 +02:00
}
2020-04-17 00:19:17 +02:00
case Menu : : timetrialcomplete :
case Menu : : timetrialcomplete2 :
case Menu : : timetrialcomplete3 :
2020-04-16 01:29:54 +02:00
{
2023-01-17 04:41:12 +01:00
font : : print ( PR_3X | PR_CEN , - 1 , 20 , loc : : gettext ( " Results " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2022-12-30 23:43:24 +01:00
std : : string tempstring = game . resulttimestring ( ) + loc : : gettext ( " / " ) + game . timetstring ( game . timetrialresultpar ) + loc : : gettext ( " .99 " ) ;
2020-01-01 21:29:24 +01:00
2024-01-05 02:35:40 +01:00
uint32_t plus1_flags = PR_RIGHT | PR_CJK_LOW | PR_RTL_XFLIP ;
2023-08-02 00:45:09 +02:00
int plus1_offset = 0 ;
if ( font : : len ( 0 , tempstring . c_str ( ) ) + font : : len ( 0 , loc : : gettext ( " +1 Rank! " ) ) > ( 292 - 49 ) )
{
// Time and "+1 Rank!" don't fit together, so put it next to titles instead
plus1_flags = PR_RIGHT | PR_CJK_HIGH ;
plus1_offset = - 10 ;
}
2024-01-05 02:35:40 +01:00
/* sprite_x_1 is used for the clock and trinket,
* sprite_x_2 is used for the player . */
int sprite_x_1 , sprite_x_2 ;
if ( ! font : : is_rtl ( PR_FONT_INTERFACE ) )
{
sprite_x_1 = 22 ;
sprite_x_2 = 22 - 4 ;
}
else
{
sprite_x_1 = SCREEN_WIDTH_PIXELS - 22 - 16 ;
sprite_x_2 = SCREEN_WIDTH_PIXELS - 22 - 16 - 4 ;
}
graphics . drawspritesetcol ( sprite_x_1 , 80 - 15 , 50 , 22 ) ;
font : : print ( PR_CJK_HIGH | PR_RTL_XFLIP , 49 , 80 - 15 , loc : : gettext ( " TIME TAKEN: " ) , 255 , 255 , 255 ) ;
font : : print ( PR_CJK_LOW | PR_RTL_XFLIP , 49 , 90 - 15 , tempstring , tr , tg , tb ) ;
2021-01-08 01:02:45 +01:00
if ( game . timetrialresulttime < = game . timetrialresultpar )
2020-04-16 01:29:54 +02:00
{
2023-08-02 00:45:09 +02:00
font : : print ( plus1_flags , 292 , 90 - 15 + plus1_offset , loc : : gettext ( " +1 Rank! " ) , 255 , 255 , 255 ) ;
2020-04-16 01:29:54 +02:00
}
2020-01-01 21:29:24 +01:00
2021-01-08 01:02:45 +01:00
tempstring = help . String ( game . timetrialresultdeaths ) ;
2024-01-05 02:35:40 +01:00
graphics . drawspritesetcol ( sprite_x_2 , 80 + 20 - 4 , 12 , 22 ) ;
font : : print ( PR_CJK_HIGH | PR_RTL_XFLIP , 49 , 80 + 20 , loc : : gettext ( " NUMBER OF DEATHS: " ) , 255 , 255 , 255 ) ;
font : : print ( PR_CJK_LOW | PR_RTL_XFLIP , 49 , 90 + 20 , tempstring , tr , tg , tb ) ;
2021-01-08 01:02:45 +01:00
if ( game . timetrialresultdeaths = = 0 )
2020-04-16 01:29:54 +02:00
{
2023-08-02 00:45:09 +02:00
font : : print ( plus1_flags , 292 , 90 + 20 + plus1_offset , loc : : gettext ( " +1 Rank! " ) , 255 , 255 , 255 ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-16 01:29:54 +02:00
2022-12-30 23:43:24 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " {n_trinkets} of {max_trinkets} " ) ,
" n_trinkets:int, max_trinkets:int " ,
game . timetrialresulttrinkets , game . timetrialresultshinytarget
) ;
2024-01-05 02:35:40 +01:00
graphics . drawspritesetcol ( sprite_x_1 , 80 + 55 , 22 , 22 ) ;
font : : print ( PR_CJK_HIGH | PR_RTL_XFLIP , 49 , 80 + 55 , loc : : gettext ( " SHINY TRINKETS: " ) , 255 , 255 , 255 ) ;
font : : print ( PR_CJK_LOW | PR_RTL_XFLIP , 49 , 90 + 55 , buffer , tr , tg , tb ) ;
2021-01-08 01:02:45 +01:00
if ( game . timetrialresulttrinkets > = game . timetrialresultshinytarget )
2020-01-01 21:29:24 +01:00
{
2023-08-02 00:45:09 +02:00
font : : print ( plus1_flags , 292 , 90 + 55 + plus1_offset , loc : : gettext ( " +1 Rank! " ) , 255 , 255 , 255 ) ;
2020-04-16 01:29:54 +02:00
}
2020-01-01 21:29:24 +01:00
2022-12-30 23:43:24 +01:00
const char * rank = " " ;
switch ( game . timetrialrank )
{
case 0 :
rank = loc : : gettext ( " B " ) ;
break ;
case 1 :
rank = loc : : gettext ( " A " ) ;
break ;
case 2 :
rank = loc : : gettext ( " S " ) ;
break ;
case 3 :
rank = loc : : gettext ( " V " ) ;
break ;
}
2023-01-17 04:41:12 +01:00
int rankw = font : : len ( PR_4X , rank ) ;
int ranktextw = font : : len ( PR_2X , loc : : gettext ( " Rank: " ) ) + 16 + rankw ;
2022-12-30 23:43:24 +01:00
int ranktextx = ( 320 - ranktextw ) / 2 ;
int rankx = ranktextx + ranktextw - rankw ;
2020-04-16 06:53:36 +02:00
if ( game . currentmenuname = = Menu : : timetrialcomplete2 | | game . currentmenuname = = Menu : : timetrialcomplete3 )
2020-04-16 01:29:54 +02:00
{
2024-01-05 02:35:40 +01:00
font : : print ( PR_2X | PR_RTL_XFLIP , ranktextx , 175 , loc : : gettext ( " Rank: " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
}
2020-04-16 06:53:36 +02:00
if ( game . currentmenuname = = Menu : : timetrialcomplete3 )
2020-04-16 01:29:54 +02:00
{
2024-01-05 02:35:40 +01:00
font : : print ( PR_4X | PR_RTL_XFLIP , rankx , 165 , rank , 255 , 255 , 255 ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-17 00:19:17 +02:00
break ;
2020-04-16 01:29:54 +02:00
}
2020-04-17 00:19:17 +02:00
case Menu : : unlockmenutrials :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Unlock Time Trials " ) , tr , tg , tb ) ;
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " You can unlock each time trial separately. " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : timetrials :
2022-12-30 23:43:24 +01:00
{
bool unlocked = false ;
int id_trial = game . currentmenuoption ;
int par ;
int max_trinkets ;
2020-04-16 05:10:11 +02:00
switch ( game . currentmenuoption )
2020-01-01 21:29:24 +01:00
{
2020-04-16 05:10:11 +02:00
case 0 :
2023-06-06 02:34:27 +02:00
if ( game . unlock [ Unlock_TIMETRIAL_SPACESTATION1 ] )
2020-01-01 21:29:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Space Station 1 " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
unlocked = true ;
par = 75 ;
max_trinkets = 2 ;
2020-01-01 21:29:24 +01:00
}
2020-04-16 01:29:54 +02:00
else
2020-01-01 21:29:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " ??? " ) , tr , tg , tb ) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 60 , loc : : gettext ( " TO UNLOCK: " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 75 , loc : : gettext ( " Rescue Violet " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 87 , loc : : gettext ( " Find three trinkets " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-16 05:10:11 +02:00
break ;
case 1 :
2023-06-06 02:34:27 +02:00
if ( game . unlock [ Unlock_TIMETRIAL_LABORATORY ] )
2020-01-01 21:29:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " The Laboratory " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
unlocked = true ;
par = 165 ;
max_trinkets = 4 ;
2020-01-01 21:29:24 +01:00
}
2020-04-16 01:29:54 +02:00
else
2020-01-01 21:29:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " ??? " ) , tr , tg , tb ) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 60 , loc : : gettext ( " TO UNLOCK: " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 75 , loc : : gettext ( " Rescue Victoria " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 87 , loc : : gettext ( " Find six trinkets " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
}
2020-04-16 05:10:11 +02:00
break ;
case 2 :
2023-06-06 02:34:27 +02:00
if ( game . unlock [ Unlock_TIMETRIAL_TOWER ] )
2020-04-16 01:29:54 +02:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " The Tower " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
unlocked = true ;
par = 105 ;
max_trinkets = 2 ;
2020-04-16 01:29:54 +02:00
}
else
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " ??? " ) , tr , tg , tb ) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 60 , loc : : gettext ( " TO UNLOCK: " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 75 , loc : : gettext ( " Rescue Vermilion " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 87 , loc : : gettext ( " Find nine trinkets " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-16 05:10:11 +02:00
break ;
case 3 :
2023-06-06 02:34:27 +02:00
if ( game . unlock [ Unlock_TIMETRIAL_SPACESTATION2 ] )
2020-01-01 21:29:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Space Station 2 " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
unlocked = true ;
par = 200 ;
max_trinkets = 5 ;
2020-04-16 01:29:54 +02:00
}
else
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " ??? " ) , tr , tg , tb ) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 60 , loc : : gettext ( " TO UNLOCK: " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 75 , loc : : gettext ( " Rescue Vitellary " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 87 , loc : : gettext ( " Find twelve trinkets " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-16 05:10:11 +02:00
break ;
case 4 :
2023-06-06 02:34:27 +02:00
if ( game . unlock [ Unlock_TIMETRIAL_WARPZONE ] )
2020-01-01 21:29:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " The Warp Zone " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
unlocked = true ;
par = 120 ;
max_trinkets = 1 ;
2020-04-16 01:29:54 +02:00
}
else
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " ??? " ) , tr , tg , tb ) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 60 , loc : : gettext ( " TO UNLOCK: " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 75 , loc : : gettext ( " Rescue Verdigris " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 87 , loc : : gettext ( " Find fifteen trinkets " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-16 05:10:11 +02:00
break ;
case 5 :
2023-06-06 02:34:27 +02:00
if ( game . unlock [ Unlock_TIMETRIAL_FINALLEVEL ] )
2020-01-01 21:29:24 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " The Final Level " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
unlocked = true ;
par = 135 ;
max_trinkets = 1 ;
}
else
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " ??? " ) , tr , tg , tb ) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 60 , loc : : gettext ( " TO UNLOCK: " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 75 , loc : : gettext ( " Complete the game " ) , tr , tg , tb ) ;
font : : print ( PR_CEN , - 1 , 87 , loc : : gettext ( " Find eighteen trinkets " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
}
break ;
}
if ( unlocked )
{
if ( game . besttimes [ id_trial ] = = - 1 )
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 75 , loc : : gettext ( " Not yet attempted " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
}
else
{
2023-01-17 04:41:12 +01:00
int sp = SDL_max ( 10 , font : : height ( 0 ) ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 32 , 65 , loc : : gettext ( " RECORDS " ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
const char * label = loc : : gettext ( " TIME " ) ;
2023-01-17 04:41:12 +01:00
int label_len = font : : len ( 0 , label ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 32 , 65 + sp , label , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
label = loc : : gettext ( " SHINY " ) ;
2023-01-17 04:41:12 +01:00
label_len = SDL_max ( label_len , font : : len ( 0 , label ) ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 32 , 65 + sp * 2 , label , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
label = loc : : gettext ( " LIVES " ) ;
2023-01-17 04:41:12 +01:00
label_len = SDL_max ( label_len , font : : len ( 0 , label ) ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 32 , 65 + sp * 3 , label , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
2023-10-26 00:43:55 +02:00
help . format_time ( buffer , sizeof ( buffer ) , game . besttimes [ id_trial ] , game . bestframes [ id_trial ] , true ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , label_len + 48 , 65 + sp , buffer , tr , tg , tb ) ;
2023-10-26 00:43:55 +02:00
2022-12-30 23:43:24 +01:00
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " {n_trinkets}/{max_trinkets} " ) ,
" n_trinkets:int, max_trinkets:int " ,
game . besttrinkets [ id_trial ] , max_trinkets
) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , label_len + 48 , 65 + sp * 2 , buffer , tr , tg , tb ) ;
font : : print ( PR_RTL_XFLIP , label_len + 48 , 65 + sp * 3 , help . String ( game . bestlives [ id_trial ] ) , tr , tg , tb ) ;
2022-12-30 23:43:24 +01:00
const char * str_par_time = loc : : gettext ( " PAR TIME " ) ;
const std : : string par_time = game . timetstring ( par ) ;
const char * str_best_rank = loc : : gettext ( " BEST RANK " ) ;
const char * rank ;
switch ( game . bestrank [ id_trial ] )
2020-01-01 21:29:24 +01:00
{
2022-12-30 23:43:24 +01:00
case 0 :
rank = loc : : gettext ( " B " ) ;
break ;
case 1 :
rank = loc : : gettext ( " A " ) ;
break ;
case 2 :
rank = loc : : gettext ( " S " ) ;
break ;
case 3 :
rank = loc : : gettext ( " V " ) ;
break ;
default :
rank = " ? " ;
2020-01-01 21:29:24 +01:00
}
2020-04-16 01:29:54 +02:00
2022-12-30 23:43:24 +01:00
int w [ 4 ] = {
2023-01-17 04:41:12 +01:00
font : : len ( 0 , str_par_time ) ,
2023-03-05 00:32:58 +01:00
font : : len ( 0 , par_time . c_str ( ) ) ,
2023-01-17 04:41:12 +01:00
font : : len ( 0 , str_best_rank ) ,
font : : len ( PR_2X , rank )
2022-12-30 23:43:24 +01:00
} ;
int longest_w = 0 ;
for ( size_t i = 0 ; i < 4 ; i + + )
{
if ( w [ i ] > longest_w )
2020-04-16 01:29:54 +02:00
{
2022-12-30 23:43:24 +01:00
longest_w = w [ i ] ;
2020-04-16 01:29:54 +02:00
}
2020-01-01 21:29:24 +01:00
}
2022-12-30 23:43:24 +01:00
int center_x = 288 - longest_w / 2 ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_CEN | PR_RTL_XFLIP , center_x , 65 , str_par_time , tr , tg , tb ) ;
font : : print ( PR_CEN | PR_RTL_XFLIP , center_x , 65 + sp , par_time , tr , tg , tb ) ;
font : : print ( PR_CEN | PR_RTL_XFLIP , center_x , 65 + sp * 3 , str_best_rank , tr , tg , tb ) ;
2023-01-17 04:41:12 +01:00
font : : print (
2024-01-03 22:54:20 +01:00
PR_2X | PR_CEN | PR_RTL_XFLIP ,
2023-01-17 04:41:12 +01:00
center_x ,
66 + sp * 4 ,
2022-12-30 23:43:24 +01:00
rank ,
225 , 225 , 225
) ;
2020-04-16 01:29:54 +02:00
}
2020-01-01 21:29:24 +01:00
}
2022-12-30 23:43:24 +01:00
2020-04-17 00:19:17 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2020-04-17 00:19:17 +02:00
case Menu : : gamecompletecontinue :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN | PR_CJK_HIGH , - 1 , 25 , loc : : gettext ( " Congratulations! " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 45 , loc : : gettext ( " Your save files have been updated. " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 110 , loc : : gettext ( " If you want to keep exploring the game, select CONTINUE from the play menu. " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : unlockmenu :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 30 , loc : : gettext ( " Unlock Play Modes " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 65 , loc : : gettext ( " From here, you may unlock parts of the game that are normally unlocked as you play. " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : unlocktimetrial :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 45 , loc : : gettext ( " Congratulations! " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 125 , loc : : gettext ( " You have unlocked a new Time Trial. " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : unlocktimetrials :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 45 , loc : : gettext ( " Congratulations! " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 125 , loc : : gettext ( " You have unlocked some new Time Trials. " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : unlocknodeathmode :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 45 , loc : : gettext ( " Congratulations! " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 125 , loc : : gettext ( " You have unlocked No Death Mode. " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : unlockflipmode :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 45 , loc : : gettext ( " Congratulations! " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 125 , loc : : gettext ( " You have unlocked Flip Mode. " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : unlockintermission :
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_CEN , - 1 , 45 , loc : : gettext ( " Congratulations! " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 125 , loc : : gettext ( " You have unlocked the intermission levels. " ) , tr , tg , tb ) ;
2020-04-17 00:19:17 +02:00
break ;
case Menu : : playerworlds :
2023-08-23 19:51:11 +02:00
if ( game . editor_disabled )
{
2023-08-27 03:35:13 +02:00
if ( game . currentmenuoption = = 1 )
2023-08-24 16:11:43 +02:00
{
2023-08-27 03:35:13 +02:00
if ( SDL_GetHintBoolean ( " SteamDeck " , SDL_FALSE ) )
{
font : : print_wrap ( PR_CEN , - 1 , 180 , loc : : gettext ( " The level editor is not currently supported on Steam Deck, as it requires a keyboard and mouse to use. " ) , tr , tg , tb ) ;
}
else
{
font : : print_wrap ( PR_CEN , - 1 , 180 , loc : : gettext ( " The level editor is not currently supported on this device, as it requires a keyboard and mouse to use. " ) , tr , tg , tb ) ;
}
2023-08-24 16:11:43 +02:00
}
2023-08-23 19:51:11 +02:00
}
else
{
font : : print_wrap ( PR_CEN , - 1 , 180 , loc : : gettext ( " To install new player levels, copy the .vvvvvv files to the levels folder. " ) , tr , tg , tb ) ;
}
2021-12-22 09:58:27 +01:00
break ;
case Menu : : confirmshowlevelspath :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 80 , loc : : gettext ( " Are you sure you want to show the levels path? This may reveal sensitive information if you are streaming. " ) , tr , tg , tb ) ;
2021-12-22 09:58:27 +01:00
break ;
case Menu : : showlevelspath :
2022-12-30 23:43:24 +01:00
{
2023-01-20 21:11:18 +01:00
int next_y = font : : print_wrap ( PR_CEN , - 1 , 40 , loc : : gettext ( " The levels path is: " ) , tr , tg , tb ) ;
font : : print_wrap ( 0 , 0 , next_y , FILESYSTEM_getUserLevelDirectory ( ) , tr , tg , tb , 10 , 320 ) ;
2020-04-17 00:19:17 +02:00
break ;
2022-12-30 23:43:24 +01:00
}
2020-11-22 03:10:26 +01:00
case Menu : : errorsavingsettings :
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 95 , loc : : gettext ( " ERROR: Could not save settings file! " ) , tr , tg , tb ) ;
2020-11-22 04:14:52 +01:00
break ;
2021-08-07 05:57:34 +02:00
case Menu : : errorloadinglevel :
2021-08-18 19:02:06 +02:00
{
Use levelDirError for graphics errors too
This will actually do several things:
(1) Make the tile size checks apply to the appropriate graphics files
once again.
(2) Make the game print a fallback error message if the error message
hasn't been set on the levelDirError error screen.
(3) Use levelDirError for graphics errors too.
(4) Make the error message for tile size checks failing specify both
width and height, not just a square dimension.
(5) Make the error messages mentioned above translatable.
It turns out that (1) didn't happen after #923 was merged, since #923
removed needing to process a tilesheet into a vector of surfaces for all
graphics files except sprites.png and flipsprites.png. Thus, the game
ended up only checking the correct tile sizes for those files only.
In the process of fixing this, I also got rid of the PROCESS_TILESHEET
macros and turned them into two different functions: One to make the
array, and one to check the tile size of the tilesheet.
I also did (2) just in case FILESYSTEM_levelDirHasError() returns false
even though we know we have an error.
And (3) is needed so things are unified and we have one user-facing
error message system when users load levels. To facilitate this, I
removed the title string, since it's really not needed.
Unfortunately, (1) doesn't apply to font.png again, but that's because
of the new font stuff and I'm not sure what Dav999 has in store for
error checking. But that's also why I did (4), because it looks like
tile sizes in font.png files can be different (i.e. non-square).
2023-05-18 02:00:33 +02:00
const char * message ;
2021-08-18 19:02:06 +02:00
if ( FILESYSTEM_levelDirHasError ( ) )
{
Use levelDirError for graphics errors too
This will actually do several things:
(1) Make the tile size checks apply to the appropriate graphics files
once again.
(2) Make the game print a fallback error message if the error message
hasn't been set on the levelDirError error screen.
(3) Use levelDirError for graphics errors too.
(4) Make the error message for tile size checks failing specify both
width and height, not just a square dimension.
(5) Make the error messages mentioned above translatable.
It turns out that (1) didn't happen after #923 was merged, since #923
removed needing to process a tilesheet into a vector of surfaces for all
graphics files except sprites.png and flipsprites.png. Thus, the game
ended up only checking the correct tile sizes for those files only.
In the process of fixing this, I also got rid of the PROCESS_TILESHEET
macros and turned them into two different functions: One to make the
array, and one to check the tile size of the tilesheet.
I also did (2) just in case FILESYSTEM_levelDirHasError() returns false
even though we know we have an error.
And (3) is needed so things are unified and we have one user-facing
error message system when users load levels. To facilitate this, I
removed the title string, since it's really not needed.
Unfortunately, (1) doesn't apply to font.png again, but that's because
of the new font stuff and I'm not sure what Dav999 has in store for
error checking. But that's also why I did (4), because it looks like
tile sizes in font.png files can be different (i.e. non-square).
2023-05-18 02:00:33 +02:00
message = FILESYSTEM_getLevelDirError ( ) ;
2021-08-18 19:02:06 +02:00
}
else
{
2023-05-24 00:10:56 +02:00
message = loc : : gettext ( " Something went wrong, but we forgot the error message. " ) ;
2021-08-18 19:02:06 +02:00
}
2023-07-12 22:58:47 +02:00
font : : print ( PR_2X | PR_CEN | PR_CJK_HIGH , - 1 , 45 , loc : : gettext ( " ERROR " ) , tr , tg , tb ) ;
Use levelDirError for graphics errors too
This will actually do several things:
(1) Make the tile size checks apply to the appropriate graphics files
once again.
(2) Make the game print a fallback error message if the error message
hasn't been set on the levelDirError error screen.
(3) Use levelDirError for graphics errors too.
(4) Make the error message for tile size checks failing specify both
width and height, not just a square dimension.
(5) Make the error messages mentioned above translatable.
It turns out that (1) didn't happen after #923 was merged, since #923
removed needing to process a tilesheet into a vector of surfaces for all
graphics files except sprites.png and flipsprites.png. Thus, the game
ended up only checking the correct tile sizes for those files only.
In the process of fixing this, I also got rid of the PROCESS_TILESHEET
macros and turned them into two different functions: One to make the
array, and one to check the tile size of the tilesheet.
I also did (2) just in case FILESYSTEM_levelDirHasError() returns false
even though we know we have an error.
And (3) is needed so things are unified and we have one user-facing
error message system when users load levels. To facilitate this, I
removed the title string, since it's really not needed.
Unfortunately, (1) doesn't apply to font.png again, but that's because
of the new font stuff and I'm not sure what Dav999 has in store for
error checking. But that's also why I did (4), because it looks like
tile sizes in font.png files can be different (i.e. non-square).
2023-05-18 02:00:33 +02:00
font : : print_wrap ( PR_CEN , - 1 , 65 , message , tr , tg , tb ) ;
2021-08-07 05:57:34 +02:00
break ;
2021-08-18 19:02:06 +02:00
}
2021-08-07 07:26:48 +02:00
case Menu : : warninglevellist :
Use levelDirError for graphics errors too
This will actually do several things:
(1) Make the tile size checks apply to the appropriate graphics files
once again.
(2) Make the game print a fallback error message if the error message
hasn't been set on the levelDirError error screen.
(3) Use levelDirError for graphics errors too.
(4) Make the error message for tile size checks failing specify both
width and height, not just a square dimension.
(5) Make the error messages mentioned above translatable.
It turns out that (1) didn't happen after #923 was merged, since #923
removed needing to process a tilesheet into a vector of surfaces for all
graphics files except sprites.png and flipsprites.png. Thus, the game
ended up only checking the correct tile sizes for those files only.
In the process of fixing this, I also got rid of the PROCESS_TILESHEET
macros and turned them into two different functions: One to make the
array, and one to check the tile size of the tilesheet.
I also did (2) just in case FILESYSTEM_levelDirHasError() returns false
even though we know we have an error.
And (3) is needed so things are unified and we have one user-facing
error message system when users load levels. To facilitate this, I
removed the title string, since it's really not needed.
Unfortunately, (1) doesn't apply to font.png again, but that's because
of the new font stuff and I'm not sure what Dav999 has in store for
error checking. But that's also why I did (4), because it looks like
tile sizes in font.png files can be different (i.e. non-square).
2023-05-18 02:00:33 +02:00
{
const char * message ;
if ( FILESYSTEM_levelDirHasError ( ) )
{
message = FILESYSTEM_getLevelDirError ( ) ;
}
else
{
2023-05-24 00:10:56 +02:00
message = loc : : gettext ( " Something went wrong, but we forgot the error message. " ) ;
Use levelDirError for graphics errors too
This will actually do several things:
(1) Make the tile size checks apply to the appropriate graphics files
once again.
(2) Make the game print a fallback error message if the error message
hasn't been set on the levelDirError error screen.
(3) Use levelDirError for graphics errors too.
(4) Make the error message for tile size checks failing specify both
width and height, not just a square dimension.
(5) Make the error messages mentioned above translatable.
It turns out that (1) didn't happen after #923 was merged, since #923
removed needing to process a tilesheet into a vector of surfaces for all
graphics files except sprites.png and flipsprites.png. Thus, the game
ended up only checking the correct tile sizes for those files only.
In the process of fixing this, I also got rid of the PROCESS_TILESHEET
macros and turned them into two different functions: One to make the
array, and one to check the tile size of the tilesheet.
I also did (2) just in case FILESYSTEM_levelDirHasError() returns false
even though we know we have an error.
And (3) is needed so things are unified and we have one user-facing
error message system when users load levels. To facilitate this, I
removed the title string, since it's really not needed.
Unfortunately, (1) doesn't apply to font.png again, but that's because
of the new font stuff and I'm not sure what Dav999 has in store for
error checking. But that's also why I did (4), because it looks like
tile sizes in font.png files can be different (i.e. non-square).
2023-05-18 02:00:33 +02:00
}
2023-07-12 22:58:47 +02:00
font : : print ( PR_2X | PR_CEN | PR_CJK_HIGH , - 1 , 45 , loc : : gettext ( " WARNING " ) , tr , tg , tb ) ;
Use levelDirError for graphics errors too
This will actually do several things:
(1) Make the tile size checks apply to the appropriate graphics files
once again.
(2) Make the game print a fallback error message if the error message
hasn't been set on the levelDirError error screen.
(3) Use levelDirError for graphics errors too.
(4) Make the error message for tile size checks failing specify both
width and height, not just a square dimension.
(5) Make the error messages mentioned above translatable.
It turns out that (1) didn't happen after #923 was merged, since #923
removed needing to process a tilesheet into a vector of surfaces for all
graphics files except sprites.png and flipsprites.png. Thus, the game
ended up only checking the correct tile sizes for those files only.
In the process of fixing this, I also got rid of the PROCESS_TILESHEET
macros and turned them into two different functions: One to make the
array, and one to check the tile size of the tilesheet.
I also did (2) just in case FILESYSTEM_levelDirHasError() returns false
even though we know we have an error.
And (3) is needed so things are unified and we have one user-facing
error message system when users load levels. To facilitate this, I
removed the title string, since it's really not needed.
Unfortunately, (1) doesn't apply to font.png again, but that's because
of the new font stuff and I'm not sure what Dav999 has in store for
error checking. But that's also why I did (4), because it looks like
tile sizes in font.png files can be different (i.e. non-square).
2023-05-18 02:00:33 +02:00
font : : print_wrap ( PR_CEN , - 1 , 65 , message , tr , tg , tb ) ;
2021-08-07 07:26:48 +02:00
break ;
Use levelDirError for graphics errors too
This will actually do several things:
(1) Make the tile size checks apply to the appropriate graphics files
once again.
(2) Make the game print a fallback error message if the error message
hasn't been set on the levelDirError error screen.
(3) Use levelDirError for graphics errors too.
(4) Make the error message for tile size checks failing specify both
width and height, not just a square dimension.
(5) Make the error messages mentioned above translatable.
It turns out that (1) didn't happen after #923 was merged, since #923
removed needing to process a tilesheet into a vector of surfaces for all
graphics files except sprites.png and flipsprites.png. Thus, the game
ended up only checking the correct tile sizes for those files only.
In the process of fixing this, I also got rid of the PROCESS_TILESHEET
macros and turned them into two different functions: One to make the
array, and one to check the tile size of the tilesheet.
I also did (2) just in case FILESYSTEM_levelDirHasError() returns false
even though we know we have an error.
And (3) is needed so things are unified and we have one user-facing
error message system when users load levels. To facilitate this, I
removed the title string, since it's really not needed.
Unfortunately, (1) doesn't apply to font.png again, but that's because
of the new font stuff and I'm not sure what Dav999 has in store for
error checking. But that's also why I did (4), because it looks like
tile sizes in font.png files can be different (i.e. non-square).
2023-05-18 02:00:33 +02:00
}
2020-04-17 00:19:17 +02:00
default :
break ;
2020-04-16 01:29:54 +02:00
}
}
2021-03-19 05:20:05 +01:00
void titlerender ( void )
2020-04-16 01:29:54 +02:00
{
2023-01-07 19:28:07 +01:00
graphics . clear ( ) ;
2020-04-16 01:29:54 +02:00
if ( ! game . menustart )
{
2020-05-02 01:40:35 +02:00
tr = graphics . col_tr ;
tg = graphics . col_tg ;
tb = graphics . col_tb ;
2020-04-16 01:29:54 +02:00
int temp = 50 ;
2023-01-07 19:28:07 +01:00
graphics . draw_sprite ( ( 160 - 96 ) + 0 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 1 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 2 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 3 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 4 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 5 * 32 , temp , 23 , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
# if defined(MAKEANDPLAY)
2023-01-16 21:29:50 +01:00
font : : print ( PR_RIGHT , 264 , temp + 35 , loc : : gettext ( " MAKE AND PLAY EDITION " ) , tr , tg , tb ) ;
2020-04-16 01:29:54 +02:00
# endif
2023-03-18 22:32:26 +01:00
char buffer [ SCREEN_WIDTH_CHARS * 2 + 1 ] ;
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " [ Press {button} to Start ] " ) ,
" button:but " ,
vformat_button ( ActionSet_Menu , Action_Menu_Accept )
) ;
font : : print_wrap ( PR_CEN , - 1 , 175 , buffer , tr , tg , tb ) ;
if ( BUTTONGLYPHS_keyboard_is_active ( ) )
{
font : : print_wrap ( PR_CEN , - 1 , 195 , loc : : gettext ( " ACTION = Space, Z, or V " ) , int ( tr * 0.5f ) , int ( tg * 0.5f ) , int ( tb * 0.5f ) ) ;
}
2020-04-16 01:29:54 +02:00
}
else
{
2020-11-03 00:23:53 +01:00
if ( ! game . colourblindmode ) graphics . drawtowerbackground ( graphics . titlebg ) ;
2020-04-16 01:29:54 +02:00
2020-05-02 01:40:35 +02:00
tr = graphics . col_tr ;
tg = graphics . col_tg ;
tb = graphics . col_tb ;
2020-04-16 01:29:54 +02:00
menurender ( ) ;
2020-01-01 21:29:24 +01:00
tr = int ( tr * .8f ) ;
tg = int ( tg * .8f ) ;
tb = int ( tb * .8f ) ;
if ( tr < 0 ) tr = 0 ;
if ( tr > 255 ) tr = 255 ;
if ( tg < 0 ) tg = 0 ;
if ( tg > 255 ) tg = 255 ;
if ( tb < 0 ) tb = 0 ;
if ( tb > 255 ) tb = 255 ;
2022-12-30 22:57:24 +01:00
graphics . drawmenu ( tr , tg , tb , game . currentmenuname ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-01 23:59:19 +02:00
graphics . drawfade ( ) ;
2020-01-01 21:29:24 +01:00
2020-04-27 04:24:50 +02:00
graphics . renderwithscreeneffects ( ) ;
2020-01-01 21:29:24 +01:00
}
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
void gamecompleterender ( void )
2020-01-01 21:29:24 +01:00
{
2023-01-07 19:28:07 +01:00
graphics . clear ( ) ;
2020-01-01 21:29:24 +01:00
2020-11-03 00:23:53 +01:00
if ( ! game . colourblindmode ) graphics . drawtowerbackground ( graphics . titlebg ) ;
2020-01-01 21:29:24 +01:00
2020-05-02 01:40:35 +02:00
tr = graphics . col_tr ;
tg = graphics . col_tg ;
tb = graphics . col_tb ;
2020-01-01 21:29:24 +01:00
//rendering starts... here!
2020-04-30 19:56:27 +02:00
int position = graphics . lerp ( game . oldcreditposition , game . creditposition ) ;
if ( graphics . onscreen ( 220 + position ) )
2020-01-01 21:29:24 +01:00
{
2020-04-30 19:56:27 +02:00
int temp = 220 + position ;
2023-01-07 19:28:07 +01:00
graphics . draw_sprite ( ( 160 - 96 ) + 0 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 1 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 2 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 3 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 4 * 32 , temp , 23 , tr , tg , tb ) ;
graphics . draw_sprite ( ( 160 - 96 ) + 5 * 32 , temp , 23 , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2023-01-18 23:04:47 +01:00
if ( graphics . onscreen ( 290 + position ) ) font : : print ( PR_2X | PR_CEN , - 1 , 290 + position , loc : : gettext ( " Starring " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( 320 + position ) )
2020-01-01 21:29:24 +01:00
{
2020-04-30 19:56:27 +02:00
graphics . drawcrewman ( 70 , 320 + position , 0 , true ) ;
2023-01-20 20:24:41 +01:00
font : : print ( 0 , 100 , 330 + position , loc : : gettext ( " Captain Viridian " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( 350 + position ) )
2020-01-01 21:29:24 +01:00
{
2020-04-30 19:56:27 +02:00
graphics . drawcrewman ( 70 , 350 + position , 1 , true ) ;
2023-01-20 20:24:41 +01:00
font : : print ( 0 , 100 , 360 + position , loc : : gettext ( " Doctor Violet " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( 380 + position ) )
2020-01-01 21:29:24 +01:00
{
2020-04-30 19:56:27 +02:00
graphics . drawcrewman ( 70 , 380 + position , 2 , true ) ;
2023-01-20 20:24:41 +01:00
font : : print ( 0 , 100 , 390 + position , loc : : gettext ( " Professor Vitellary " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( 410 + position ) )
2020-01-01 21:29:24 +01:00
{
2020-04-30 19:56:27 +02:00
graphics . drawcrewman ( 70 , 410 + position , 3 , true ) ;
2023-01-20 20:24:41 +01:00
font : : print ( 0 , 100 , 420 + position , loc : : gettext ( " Officer Vermilion " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( 440 + position ) )
2020-01-01 21:29:24 +01:00
{
2020-04-30 19:56:27 +02:00
graphics . drawcrewman ( 70 , 440 + position , 4 , true ) ;
2023-01-20 20:24:41 +01:00
font : : print ( 0 , 100 , 450 + position , loc : : gettext ( " Chief Verdigris " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( 470 + position ) )
2020-01-01 21:29:24 +01:00
{
2020-04-30 19:56:27 +02:00
graphics . drawcrewman ( 70 , 470 + position , 5 , true ) ;
2023-01-20 20:24:41 +01:00
font : : print ( 0 , 100 , 480 + position , loc : : gettext ( " Doctor Victoria " ) , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2023-04-21 23:30:49 +02:00
if ( graphics . onscreen ( 520 + position ) )
{
uint32_t flag = PR_3X ;
const char * text = loc : : gettext ( " Credits " ) ;
if ( font : : len ( flag , text ) > SCREEN_WIDTH_PIXELS )
{
flag = PR_2X ;
}
font : : print ( flag | PR_CEN , - 1 , 520 + position , text , tr , tg , tb ) ;
}
2020-01-01 21:29:24 +01:00
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( 560 + position ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 20:24:41 +01:00
font : : print ( PR_CJK_HIGH , 40 , 560 + position , loc : : gettext ( " Created by " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_FONT_8X8 , 60 , 570 + position , " Terry Cavanagh " , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( 600 + position ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 20:24:41 +01:00
font : : print ( PR_CJK_HIGH , 40 , 600 + position , loc : : gettext ( " With Music by " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_FONT_8X8 , 60 , 610 + position , " Magnus Pålsson " , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( 640 + position ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 20:24:41 +01:00
font : : print ( PR_CJK_HIGH , 40 , 640 + position , loc : : gettext ( " Rooms Named by " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_FONT_8X8 , 60 , 650 + position , " Bennett Foddy " , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2021-09-01 08:19:25 +02:00
if ( graphics . onscreen ( 710 + position ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 20:24:41 +01:00
font : : print ( PR_CJK_HIGH , 40 , 680 + position , loc : : gettext ( " C++ Port by " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_FONT_8X8 , 60 , 690 + position , " Simon Roth " , tr , tg , tb ) ;
font : : print ( PR_2X | PR_FONT_8X8 , 60 , 710 + position , " Ethan Lee " , tr , tg , tb ) ;
font : : print ( PR_2X | PR_FONT_8X8 , 60 , 730 + position , " Misa Kai " , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2021-09-01 08:23:27 +02:00
if ( graphics . onscreen ( 770 + position ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 20:24:41 +01:00
font : : print ( PR_CJK_HIGH , 40 , 760 + position , loc : : gettext ( " Beta Testing by " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_FONT_8X8 , 60 , 770 + position , " Sam Kaplan " , tr , tg , tb ) ;
font : : print ( PR_2X | PR_FONT_8X8 , 60 , 790 + position , " Pauli Kohberger " , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2021-09-01 08:19:25 +02:00
if ( graphics . onscreen ( 820 + position ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 20:24:41 +01:00
font : : print ( PR_CJK_HIGH , 40 , 820 + position , loc : : gettext ( " Ending Picture by " ) , tr , tg , tb ) ;
2023-01-18 23:04:47 +01:00
font : : print ( PR_2X | PR_FONT_8X8 , 60 , 830 + position , " Pauli Kohberger " , tr , tg , tb ) ;
2020-01-01 21:29:24 +01:00
}
2023-11-28 20:43:04 +01:00
int creditOffset = 890 ;
2020-01-17 02:14:56 +01:00
2023-11-28 20:43:04 +01:00
if ( graphics . onscreen ( creditOffset + position ) )
{
2023-12-08 23:18:58 +01:00
font : : print ( PR_2X | PR_CJK_HIGH | PR_CEN , - 1 , creditOffset + position , loc : : gettext ( " Localisation " ) , tr , tg , tb ) ;
2023-11-28 20:43:04 +01:00
}
creditOffset + = 30 ;
if ( graphics . onscreen ( creditOffset + position ) )
{
2023-12-08 23:18:58 +01:00
const char * text = loc : : gettext ( " Localisation Project Led by " ) ;
int x = SCREEN_WIDTH_PIXELS - font : : len ( 0 , text ) ;
x = SDL_min ( x , 40 ) ;
font : : print ( PR_CJK_HIGH , x , creditOffset + position , text , tr , tg , tb ) ;
2023-11-28 20:43:04 +01:00
font : : print ( PR_2X | PR_FONT_8X8 , 60 , creditOffset + position + 10 , " Dav999 " , tr , tg , tb ) ;
}
creditOffset + = 40 ;
if ( graphics . onscreen ( creditOffset + position ) )
{
2023-12-08 23:18:58 +01:00
const char * text = loc : : gettext ( " Pan-European Font Design by " ) ;
int x = SCREEN_WIDTH_PIXELS - font : : len ( 0 , text ) ;
x = SDL_min ( x , 40 ) ;
font : : print ( PR_CJK_HIGH , x , creditOffset + position , text , tr , tg , tb ) ;
2023-11-28 20:43:04 +01:00
font : : print ( PR_2X | PR_FONT_8X8 , 60 , creditOffset + position + 10 , " Reese Rivers " , tr , tg , tb ) ;
}
creditOffset + = 40 ;
if ( graphics . onscreen ( creditOffset + position ) )
{
2023-12-08 23:18:58 +01:00
const char * text = loc : : gettext ( " With contributions on GitHub from " ) ;
int x = SCREEN_WIDTH_PIXELS - font : : len ( 0 , text ) ;
x = SDL_min ( x , 40 ) ;
font : : print ( PR_CJK_HIGH , x , creditOffset + position , text , tr , tg , tb ) ;
2023-12-06 00:34:05 +01:00
font : : print ( PR_2X | PR_FONT_8X8 , 60 , creditOffset + position + 10 , " Alexandra Fox " , tr , tg , tb ) ;
font : : print ( PR_2X | PR_FONT_8X8 , 60 , creditOffset + position + 30 , " mothbeanie " , tr , tg , tb ) ;
2023-11-28 20:43:04 +01:00
}
2023-12-06 00:34:05 +01:00
creditOffset + = 100 ;
if ( graphics . onscreen ( creditOffset + position ) )
{
font : : print ( PR_2X | PR_CJK_HIGH | PR_CEN , - 1 , creditOffset + position , loc : : gettext ( " Translators " ) , tr , tg , tb ) ;
}
creditOffset + = 40 ;
2023-11-28 20:43:04 +01:00
for ( size_t i = 0 ; i < SDL_arraysize ( Credits : : translators ) ; i + = 1 )
{
if ( graphics . onscreen ( creditOffset + position ) )
{
2024-02-02 18:24:49 +01:00
if ( Credits : : translators [ i ] [ 0 ] = = ' > ' )
{
// Category heading, remove the > character and translate the rest
font : : print ( 0 , 76 , creditOffset + position , loc : : gettext ( & Credits : : translators [ i ] [ 1 ] ) , tr , tg , tb ) ;
}
else if ( Credits : : translators [ i ] [ 0 ] ! = ' ' )
2023-11-28 20:43:04 +01:00
{
// Not prefixed with a space, so this line is the name of a language, display its localised string
font : : print ( 0 , 60 , creditOffset + position , loc : : gettext ( Credits : : translators [ i ] ) , tr , tg , tb ) ;
}
else
{
// Otherwise, this line is the name of a translator, indent it and display with the original 8x8 font always
font : : print ( PR_FONT_8X8 , 72 , creditOffset + position , Credits : : translators [ i ] , tr , tg , tb ) ;
}
}
creditOffset + = 12 ;
}
creditOffset + = 40 ;
if ( graphics . onscreen ( creditOffset + position ) )
{
font : : print ( PR_3X | PR_CEN , - 1 , creditOffset + position , loc : : gettext ( " Patrons " ) , tr , tg , tb ) ;
}
creditOffset + = 50 ;
2020-01-17 02:14:56 +01:00
Turn (super)patrons/githubfriends into arrays & move them to new file
So, originally, I wanted to keep them on Game, but it turns out that if
I initialize it in Game.cpp, the compiler will complain that other files
won't know what's actually inside the array. To do that, I'd have to
initialize it in Game.h. But I don't want to initialize it in Game.h
because that'd mean recompiling a lot of unnecessary files whenever
someone gets added to the credits.
So, I moved all the patrons, superpatrons, and GitHub contributors to a
new file, Credits.h, which only contains the list (and the credits max
position calculation). That way, whenever someone gets added, only the
minimal amount of files need to be recompiled.
2020-07-03 12:13:15 +02:00
for ( size_t i = 0 ; i < SDL_arraysize ( Credits : : superpatrons ) ; i + = 1 )
2020-01-17 02:14:56 +01:00
{
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( creditOffset + position ) )
2020-01-17 02:14:56 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_CEN | PR_FONT_8X8 , - 1 , creditOffset + position , Credits : : superpatrons [ i ] , tr , tg , tb ) ;
2020-01-17 02:14:56 +01:00
}
creditOffset + = 10 ;
}
creditOffset + = 10 ;
2023-11-28 20:43:04 +01:00
if ( graphics . onscreen ( creditOffset + position ) )
{
font : : print ( PR_CEN , - 1 , creditOffset + position , loc : : gettext ( " and " ) , tr , tg , tb ) ;
}
2020-01-17 02:14:56 +01:00
creditOffset + = 20 ;
Turn (super)patrons/githubfriends into arrays & move them to new file
So, originally, I wanted to keep them on Game, but it turns out that if
I initialize it in Game.cpp, the compiler will complain that other files
won't know what's actually inside the array. To do that, I'd have to
initialize it in Game.h. But I don't want to initialize it in Game.h
because that'd mean recompiling a lot of unnecessary files whenever
someone gets added to the credits.
So, I moved all the patrons, superpatrons, and GitHub contributors to a
new file, Credits.h, which only contains the list (and the credits max
position calculation). That way, whenever someone gets added, only the
minimal amount of files need to be recompiled.
2020-07-03 12:13:15 +02:00
for ( size_t i = 0 ; i < SDL_arraysize ( Credits : : patrons ) ; i + = 1 )
2020-01-17 02:14:56 +01:00
{
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( creditOffset + position ) )
2020-01-17 02:14:56 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_CEN | PR_FONT_8X8 , - 1 , creditOffset + position , Credits : : patrons [ i ] , tr , tg , tb ) ;
2020-01-17 02:14:56 +01:00
}
creditOffset + = 10 ;
}
creditOffset + = 20 ;
2023-11-28 20:43:04 +01:00
if ( graphics . onscreen ( creditOffset + position ) )
{
font : : print ( PR_2X | PR_CEN , - 1 , creditOffset + position , loc : : gettext ( " GitHub Contributors " ) , tr , tg , tb ) ;
}
2020-01-17 02:14:56 +01:00
creditOffset + = 30 ;
Turn (super)patrons/githubfriends into arrays & move them to new file
So, originally, I wanted to keep them on Game, but it turns out that if
I initialize it in Game.cpp, the compiler will complain that other files
won't know what's actually inside the array. To do that, I'd have to
initialize it in Game.h. But I don't want to initialize it in Game.h
because that'd mean recompiling a lot of unnecessary files whenever
someone gets added to the credits.
So, I moved all the patrons, superpatrons, and GitHub contributors to a
new file, Credits.h, which only contains the list (and the credits max
position calculation). That way, whenever someone gets added, only the
minimal amount of files need to be recompiled.
2020-07-03 12:13:15 +02:00
for ( size_t i = 0 ; i < SDL_arraysize ( Credits : : githubfriends ) ; i + = 1 )
2020-01-17 02:14:56 +01:00
{
2020-04-30 19:56:27 +02:00
if ( graphics . onscreen ( creditOffset + position ) )
2020-01-17 02:14:56 +01:00
{
2023-01-18 23:04:47 +01:00
font : : print ( PR_CEN | PR_FONT_8X8 , - 1 , creditOffset + position , Credits : : githubfriends [ i ] , tr , tg , tb ) ;
2020-01-17 02:14:56 +01:00
}
creditOffset + = 10 ;
}
creditOffset + = 140 ;
2022-12-31 01:22:39 +01:00
if ( graphics . onscreen ( creditOffset + position ) )
{
2024-04-01 07:46:56 +02:00
const char * line1 ;
const char * line2 ;
if ( graphics . flipmode )
{
line1 = loc : : gettext ( " playing! " ) ;
line2 = loc : : gettext ( " Thanks for " ) ;
}
else
{
line1 = loc : : gettext ( " Thanks for " ) ;
line2 = loc : : gettext ( " playing! " ) ;
}
font : : print ( PR_2X | PR_CEN | PR_CJK_HIGH , - 1 , creditOffset + position , line1 , tr , tg , tb ) ;
2022-12-31 01:22:39 +01:00
creditOffset + = 20 ;
2024-04-01 07:46:56 +02:00
font : : print ( PR_2X | PR_CEN | PR_CJK_LOW , - 1 , creditOffset + position , line2 , tr , tg , tb ) ;
2022-12-31 01:22:39 +01:00
}
2020-01-01 21:29:24 +01:00
2023-05-22 21:18:40 +02:00
draw_skip_message ( ) ;
2020-04-01 23:59:19 +02:00
graphics . drawfade ( ) ;
2020-01-01 21:29:24 +01:00
2020-04-27 04:29:40 +02:00
graphics . render ( ) ;
2020-01-01 21:29:24 +01:00
}
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
void gamecompleterender2 ( void )
2020-01-01 21:29:24 +01:00
{
2023-01-07 19:28:07 +01:00
graphics . clear ( ) ;
2020-01-01 21:29:24 +01:00
2023-01-07 19:28:07 +01:00
graphics . drawimage ( IMAGE_ENDING , 0 , 0 ) ;
2020-01-01 21:29:24 +01:00
for ( int j = 0 ; j < 30 ; j + + )
{
for ( int i = 0 ; i < 40 ; i + + )
{
if ( j = = game . creditposy )
{
if ( i > game . creditposx )
{
2023-01-07 19:28:07 +01:00
graphics . fill_rect ( i * 8 , j * 8 , 8 , 8 , 0 , 0 , 0 ) ;
2020-01-01 21:29:24 +01:00
}
}
if ( j > game . creditposy )
{
2023-01-07 19:28:07 +01:00
graphics . fill_rect ( i * 8 , j * 8 , 8 , 8 , 0 , 0 , 0 ) ;
2020-01-01 21:29:24 +01:00
}
}
}
2023-01-07 19:28:07 +01:00
graphics . fill_rect ( graphics . lerp ( game . oldcreditposx * 8 , game . creditposx * 8 ) + 8 , game . creditposy * 8 , 8 , 8 , 0 , 0 , 0 ) ;
2020-05-02 03:23:52 +02:00
2023-05-22 21:18:40 +02:00
draw_skip_message ( ) ;
2020-04-01 23:59:19 +02:00
graphics . drawfade ( ) ;
2020-01-01 21:29:24 +01:00
2020-04-27 04:29:40 +02:00
graphics . render ( ) ;
2020-01-01 21:29:24 +01:00
}
2021-04-19 08:23:44 +02:00
static const char * interact_prompt (
char * buffer ,
const size_t buffer_size ,
const char * raw
) {
Add support for button glyph display
This adds a function that converts an action (such as interacting
in-game) to the corresponding button text ("ENTER", "E") or button
glyph (PlayStation triangle, Steam Deck Y, etc). This function
currently only gives the existing ENTERs or Es, because I don't know
how best to detect controller usage, or whether the game is running on
a Steam Deck, or what buttons need to be displayed there. Still, it
should now be really easy to adapt the rendering of keyboard keys to
consoles, controllers, or rebound keys.
To identify the actions that currently need to be displayed, this
commit also adds the initial enums for action sets as described by
Ethan in a comment in #834 (Jan 18, 2022).
2023-03-18 22:30:16 +01:00
vformat_buf (
buffer , buffer_size ,
raw ,
" button:but " ,
vformat_button ( ActionSet_InGame , Action_InGame_Interact )
) ;
2021-04-19 08:23:44 +02:00
return buffer ;
}
2024-01-07 05:53:51 +01:00
static void mode_indicator_text ( const int alpha )
{
2024-01-23 23:29:03 +01:00
const uint32_t flags = PR_BRIGHTNESS ( alpha ) | PR_BOR | PR_RTL_XFLIP ;
2024-01-07 05:53:51 +01:00
const int r = 220 - help . glow ;
const int g = 220 - help . glow ;
const int b = 255 - help . glow / 2 ;
const int x = 5 ;
const int spacing = font : : height ( flags ) + 2 ;
int y = 5 ;
if ( game . advancetext )
{
/* Prevent clashing */
2024-01-10 02:06:33 +01:00
y = 20 ;
}
2024-01-10 02:48:53 +01:00
if ( ( game . act_fade > 5 | | game . prev_act_fade > 5 ) & & game . activity_y < 60 )
2024-01-10 02:06:33 +01:00
{
/* Prevent clashing */
2024-01-10 02:48:53 +01:00
y = game . activity_y + 37 ;
2024-01-07 05:53:51 +01:00
}
/* FIXME: Some strings have not yet been translated. In order to not have
* English text in other languages , they are substituted with existing
* ones . Remove all substitute text when they ' re fully translated . */
if ( map . invincibility )
{
const char * english = " Invincibility mode enabled " ;
const char * text = loc : : gettext ( english ) ;
if ( loc : : lang ! = " en " & & SDL_strcmp ( english , text ) = = 0 )
{
/* Substitute text */
text = loc : : gettext ( " Invincibility " ) ;
}
font : : print ( flags , x , y , text , r , g , b ) ;
y + = spacing ;
}
enum GlitchrunnerMode mode = GlitchrunnerMode_get ( ) ;
if ( mode ! = GlitchrunnerNone )
{
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
const char * english = " Glitchrunner mode enabled ({version}) " ;
const char * text = loc : : gettext ( english ) ;
if ( loc : : lang ! = " en " & & SDL_strcmp ( english , text ) = = 0 )
{
/* Substitute text */
SDL_strlcpy ( buffer , loc : : gettext ( " Glitchrunner Mode " ) , sizeof ( buffer ) ) ;
}
else
{
const char * mode_string = loc : : gettext ( GlitchrunnerMode_enum_to_string ( mode ) ) ;
vformat_buf ( buffer , sizeof ( buffer ) , text , " version:str " , mode_string ) ;
}
font : : print ( flags , x , y , buffer , r , g , b ) ;
y + = spacing ;
}
if ( graphics . flipmode )
{
const char * english = " Flip Mode enabled " ;
const char * text = loc : : gettext ( english ) ;
if ( loc : : lang ! = " en " & & SDL_strcmp ( english , text ) = = 0 )
{
/* Substitute text */
text = loc : : gettext ( " Flip Mode " ) ;
}
font : : print ( flags , x , y , text , r , g , b ) ;
y + = spacing ;
}
switch ( game . slowdown )
{
case 24 :
font : : print ( flags , x , y , loc : : gettext ( " Game speed is at 80% " ) , r , g , b ) ;
y + = spacing ;
break ;
case 18 :
font : : print ( flags , x , y , loc : : gettext ( " Game speed is at 60% " ) , r , g , b ) ;
y + = spacing ;
break ;
case 12 :
font : : print ( flags , x , y , loc : : gettext ( " Game speed is at 40% " ) , r , g , b ) ;
y + = spacing ;
}
}
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
void gamerender ( void )
2020-01-01 21:29:24 +01:00
{
2023-01-07 19:28:07 +01:00
graphics . set_render_target ( graphics . gameplayTexture ) ;
graphics . set_color ( 0 , 0 , 0 , 255 ) ;
2020-01-01 21:29:24 +01:00
if ( ! game . blackout )
{
2020-04-27 00:16:35 +02:00
if ( map . towermode )
2020-04-02 23:19:15 +02:00
{
2020-04-27 00:16:35 +02:00
if ( ! game . colourblindmode )
{
2020-11-03 00:05:24 +01:00
graphics . drawtowerbackground ( graphics . towerbg ) ;
2020-04-27 00:16:35 +02:00
}
else
{
2023-01-07 19:28:07 +01:00
graphics . clear ( ) ;
2020-04-27 00:16:35 +02:00
}
2020-06-30 20:10:00 +02:00
graphics . drawtowermap ( ) ;
2020-01-01 21:29:24 +01:00
}
else
2020-04-02 23:19:15 +02:00
{
2020-04-27 00:16:35 +02:00
if ( ! game . colourblindmode )
{
graphics . drawbackground ( map . background ) ;
}
else
{
2023-01-07 19:28:07 +01:00
graphics . clear ( ) ;
2020-04-27 00:16:35 +02:00
}
2021-03-26 01:40:50 +01:00
if ( ( map . finalmode | | map . custommode ) & & map . final_colormode )
2020-04-27 00:16:35 +02:00
{
graphics . drawfinalmap ( ) ;
}
else
{
graphics . drawmap ( ) ;
}
2020-01-01 21:29:24 +01:00
}
2020-04-01 23:59:19 +02:00
graphics . drawentities ( ) ;
2020-04-27 00:16:35 +02:00
if ( map . towermode )
{
graphics . drawtowerspikes ( ) ;
}
2020-01-01 21:29:24 +01:00
}
2024-01-07 00:45:40 +01:00
int return_editor_alpha = 0 ;
bool draw_return_editor_text = false ;
if ( map . custommode & & ! map . custommodeforreal & & ! game . advancetext )
{
return_editor_alpha = graphics . lerp (
ed . old_return_message_timer , ed . return_message_timer
) ;
draw_return_editor_text = return_editor_alpha > 100 ;
}
2024-01-07 05:53:51 +01:00
int mode_indicator_alpha = graphics . lerp (
game . old_mode_indicator_timer , game . mode_indicator_timer
) ;
2024-01-10 09:23:40 +01:00
bool any_mode_active = map . invincibility
| | GlitchrunnerMode_get ( ) ! = GlitchrunnerNone
| | graphics . flipmode
| | game . slowdown < 30 ;
bool draw_mode_indicator_text = mode_indicator_alpha > 100 & & any_mode_active ;
2024-01-07 05:53:51 +01:00
Enumify all fade modes
This removes the magic numbers previously used for controlling the fade
mode, which are really not readable at all unless you already know what
they mean.
0: FADE_NONE
1: FADE_FULLY_BLACK
2: FADE_START_FADEOUT
3: FADE_FADING_OUT
4: FADE_START_FADEIN
5: FADE_FADING_IN
There is also the macro FADEMODE_IS_FADING, which indicates when the
intention is to only check if the game is fading right now, which wasn't
clearly conveyed previously.
I also took the opportunity to clean up the style of any lines I
touched. This included rewriting if-else chains into case-switches,
turning one-liner if-then statements into proper blocks, fixing up
comments, and even commenting the `fademode == FADE_NONE` on the tower
spike checks (which, it was previously undocumented why that check was
there, but I think I know why it's there).
As for type safety, we already get some by transforming the variable
types into the enum. Assignment is prohibited without a cast. But,
apparently, comparison is perfectly legal and won't even give so much as
a warning. To work around this and make absolutely sure I made all
existing comparisons now use the enum, I temporarily changed it to be an
`enum class`, which is a C++11 feature that makes it so all comparisons
are illegal. Unfortunately, it scopes them in a namespace with the same
name as a class, so I had to temporarily define macros to make sure my
existing code worked. I also had to temporarily up the standard in
CMakeLists.txt to get it to compile. But after all that was done, I
found the rest of the places where a comparison to an integer was used,
and fixed them.
2022-04-25 09:57:47 +02:00
if ( graphics . fademode = = FADE_NONE
& & ! game . intimetrial
& & ! game . isingamecompletescreen ( )
2023-06-05 08:24:31 +02:00
& & ( ! game . swnmode | | game . swngame ! = SWN_SUPERGRAVITRON )
2022-12-30 22:57:24 +01:00
& & game . showingametimer
2024-01-07 00:45:40 +01:00
& & ! roomname_translator : : enabled
2024-01-07 05:27:32 +01:00
& & ( ! game . swnmode | | game . swngame ! = SWN_START_GRAVITRON_STEP_3 )
2024-01-10 01:55:37 +01:00
& & obj . trophytext < = 0 & & obj . oldtrophytext < = 0
2024-01-07 05:53:51 +01:00
& & ! draw_return_editor_text
& & ! draw_mode_indicator_text )
2021-08-05 23:31:20 +02:00
{
2022-12-31 01:13:10 +01:00
const char * tempstring = loc : : gettext ( " TIME: " ) ;
2023-01-17 04:41:12 +01:00
int label_len = font : : len ( 0 , tempstring ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 6 , 6 , tempstring , 255 , 255 , 255 ) ;
Simplify time formatting functions
Here's my notes on all the existing functions and what kind of time
formats they output:
- Game::giventimestring(int hrs, int min, int sec)
H:MM:SS
MM:SS
- Game::timestring()
// uses game.hours/minutes/seconds
H:MM:SS
MM:SS
- Game::partimestring()
// uses game.timetrialpar (seconds)
MM:SS
- Game::resulttimestring()
// uses game.timetrialresulttime (sec) + timetrialresultframes (1/30s)
MM:SS.CC
- Game::timetstring(int t)
// t = seconds
MM:SS
- Game::timestringcenti(char* buffer, const size_t buffer_size)
// uses game.hours/minutes/seconds/frames
H:MM:SS.CC
MM:SS.CC
- UtilityClass::timestring(int t)
// t = frames, 30 frames = 1 second
S:CC
M:SS:CC
This is kind of a mess, and there's a lot of functions that do the same
thing except using different variables. For localization, I also want
translators to be able to localize all these time formats - many
languages use the decimal comma instead of the decimal point (12:34,56)
maybe some languages really prefer something like 1時02分11秒44瞬...
Which I don't know to be correct, but it's good to be prepared for it
and not restrict translators arbitrarily to only changing ":" and "."
when we can start making the system better in the first place.
I added a new function, UtilityClass::format_time. This is the place
where all time formats come together, given the number of seconds and
optionally frames. I have simplified the above-mentioned functions
somewhat, but I haven't given them a complete refactor or renaming -
I mainly made sure that they all use the same backend so I can make the
formats consistent and properly localizable.
(And before we start shoving more temporary char buffers everywhere
just to get rid of the std::string's, maybe we need to think of a
globally used working buffer of size SCREEN_WIDTH_CHARS+1, as a
register of sorts, for when any line of text needs to be made or
processed, then printed, and then goes unused. Maybe help.textrow,
or something like that.)
As for this commit, the available time formats are now more consistent
and changed a little in some places. Leading zeroes for the first unit
are now no longer included, time trial results and the Super Gravitron
can now display hours when they went to 60 minutes before, and we now
always use .CC instead of :CC. These are the formats:
- H:MM:SS
- H:MM:SS.CC
- M:SS
- M:SS.CC
- S.CC (only used when always_minutes=false, for the Gravitrons)
Here's what changes to the current functions:
- Game::partimestring() is removed - it was used in two places, and
could be replaced by game.timetstring(game.timetrialpar)
- Game::giventimestring(h,m,s) and Game::timestring() are now wrappers
for the other functions
- The four remaining functions (Game::resulttimestring(),
Game::timetstring(t), Game::timestringcenti(buffer, buffer_size)
and UtilityClass::timestring(t)) are now wrappers for the "central
function", UtilityClass::format_time.
- UtilityClass::twodigits(int t) is now unused so it's also removed.
- I also added int UtilityClass::hms_to_seconds(int h, int m, int s)
2021-12-25 17:13:46 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
2021-12-21 02:32:12 +01:00
game . timestringcenti ( buffer , sizeof ( buffer ) ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 6 + label_len , 6 , buffer , 196 , 196 , 196 ) ;
2021-08-05 23:31:20 +02:00
}
2022-12-30 22:57:24 +01:00
bool force_roomname_hidden = false ;
2023-01-13 05:11:39 +01:00
bool roomname_untranslated = false ;
2022-12-30 22:57:24 +01:00
int roomname_r = 196 , roomname_g = 196 , roomname_b = 255 - help . glow ;
if ( roomname_translator : : enabled )
2020-01-01 21:29:24 +01:00
{
2022-12-30 22:57:24 +01:00
roomname_translator : : overlay_render (
& force_roomname_hidden ,
2023-01-13 05:11:39 +01:00
& roomname_untranslated ,
2022-12-30 22:57:24 +01:00
& roomname_r , & roomname_g , & roomname_b
) ;
}
2021-12-26 09:03:18 +01:00
2022-12-30 22:57:24 +01:00
if ( ( map . extrarow = = 0 | | ( map . custommode & & map . roomname [ 0 ] ! = ' \0 ' ) ) & & ! force_roomname_hidden )
{
2023-02-18 00:52:57 +01:00
const char * roomname = loc : : gettext_roomname ( map . custommode , game . roomx , game . roomy , map . roomname , map . roomname_special ) ;
2020-01-01 21:29:24 +01:00
2023-01-13 05:11:39 +01:00
graphics . render_roomname (
roomname_untranslated ? PR_FONT_8X8 : PR_FONT_LEVEL ,
roomname ,
roomname_r , roomname_g , roomname_b
) ;
2020-01-01 21:29:24 +01:00
}
if ( map . roomtexton )
{
//Draw room text!
Refactor roomtext to not use ad-hoc objects / separate length trackers
This refactors the roomtext code to (1) not use ad-hoc objects and (2)
not use a separate length-tracking variable to keep track of the actual
amount of roomtext in a room.
What I mean by ad-hoc object is, instead of formally creating a
fully-fledged struct or class and storing one vector containing that
object, this game instead hacks together an object by storing each
attribute of an object in different vectors.
In the case of roomtext, instead of making a Roomtext object that has
attributes 'x', 'y', and 'text', the 'text' attribute of each is stored
in the vector 'roomtext', the 'x' attribute of each is stored in the
vector 'roomtextx', and the 'y' attribute of each is stored in the
vector 'roomtexty'. It's only an object in the sense that you can grab
the attributes of each roomtext by using the same index across all three
vectors.
This makes it somewhat annoying to maintain and deal with, like when I
wanted add sub-tile positions to roomtext in VVVVVV: Community Edition.
Instead of being able to add attributes to an already-existing
formalized Roomtext object, I would instead have to add two more
vectors, which is inelegant. Or I could refactor the whole system, which
is what I decided to do instead.
Furthermore, this removes the separate length-tracking variable
'roomtextnumlines', which makes the code much more easy to maintain and
deal with, as the amount of roomtext is naturally tracked by C++ instead
of us having to keep track of the actual amount of roomtext manually.
2020-03-01 03:26:12 +01:00
for ( size_t i = 0 ; i < map . roomtext . size ( ) ; i + + )
2020-01-01 21:29:24 +01:00
{
2024-01-09 21:50:54 +01:00
graphics . print_roomtext ( map . roomtext [ i ] . x * 8 , map . roomtext [ i ] . y * 8 , map . roomtext [ i ] . text , map . roomtext [ i ] . rtl ) ;
2020-01-01 21:29:24 +01:00
}
}
2024-01-07 00:45:40 +01:00
if ( draw_return_editor_text )
{
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " [Press {button} to return to editor] " ) ,
" button:but " ,
vformat_button ( ActionSet_InGame , Action_InGame_Map )
) ;
font : : print (
PR_BRIGHTNESS ( return_editor_alpha ) | PR_BOR ,
5 , 5 , buffer ,
220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 )
) ;
Replace PR_COLORGLYPH_BRI(a) and PR_ALPHA(a) with PR_BRIGHTNESS(a)
There used to be two ways of fading in/out text in VVVVVV:
- Local code that modifies the R, G and B values of the text
- Keeping the RGB values the same and using the alpha channel
The latter approach is only used once, for [Press ENTER to return to
editor]. The former approach causes problems with colored (button)
glyphs: there's no way for the print function to tell from the RGB
values whether a color is "full Viridian-cyan" or "Viridian-cyan faded
out 50%", so I added the flag PR_COLORGLYPH_BRI(value) to tell the
print function that the color brightness is reduced to match the
brightness of colored glyphs to the brightness of the rest of the text.
However, there were already plans to make the single use of alpha
consistent with the rest of the game and the style, so PR_ALPHA(value)
could be removed, as well as the bit signifying whether the brightness
or alpha value is used. For the editor text, I simply copied the "Press
{button} to teleport" behavior of hiding the text completely if it
becomes darker than 100/255.
Another simplification is to make the print function handle not just
the brightness of the color glyphs while local code handled the
brightness of the normal text color, but to make the print function
handle both. That way, the callsite can simply pass in the full colors
and the brightness flag, and the flag name can be made a lot simpler as
well: PR_BRIGHTNESS(value).
2023-01-31 02:22:43 +01:00
}
2020-01-01 21:29:24 +01:00
2020-04-01 23:59:19 +02:00
graphics . cutscenebars ( ) ;
graphics . drawfade ( ) ;
2023-01-07 19:28:07 +01:00
2023-03-19 17:16:22 +01:00
graphics . drawgui ( ) ;
2024-01-07 05:53:51 +01:00
if ( draw_mode_indicator_text & & ! draw_return_editor_text )
{
mode_indicator_text ( mode_indicator_alpha ) ;
}
2023-01-07 19:28:07 +01:00
graphics . set_render_target ( graphics . gameTexture ) ;
graphics . copy_texture ( graphics . gameplayTexture , NULL , NULL ) ;
2020-01-01 21:29:24 +01:00
2023-03-18 22:32:26 +01:00
if ( game . advancetext )
2020-01-01 21:29:24 +01:00
{
2023-03-18 22:32:26 +01:00
char buffer_adv [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf (
buffer_adv , sizeof ( buffer_adv ) ,
loc : : gettext ( " - Press {button} to advance text - " ) ,
" button:but " ,
vformat_button ( ActionSet_InGame , Action_InGame_ACTION )
) ;
font : : print ( PR_CEN | PR_BOR , - 1 , graphics . flipmode ? 228 : 5 , buffer_adv , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
2020-08-03 06:02:27 +02:00
if ( game . readytotele > 100 | | game . oldreadytotele > 100 )
2020-01-01 21:29:24 +01:00
{
2021-09-13 06:39:07 +02:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
2021-04-19 08:23:44 +02:00
const char * final_string = interact_prompt (
buffer ,
sizeof ( buffer ) ,
2022-12-30 23:26:51 +01:00
loc : : gettext ( " - Press {button} to Teleport - " )
2021-04-19 08:23:44 +02:00
) ;
2020-04-30 02:04:25 +02:00
int alpha = graphics . lerp ( game . oldreadytotele , game . readytotele ) ;
2021-04-19 08:23:44 +02:00
Implement first font::print function, fix most fading of colored glyphs
There has always been a mess of different print functions that all had
slightly different specifics and called each other:
Print(x, y, text, r, g, b, cen)
nothing special here, just does what the arguments say
PrintAlpha(x, y, text, r, g, b, a, cen)
just Print but with an alpha argument
PrintWrap(x, y, text, r, g, b, cen, linespacing, maxwidth)
added for wordwrapping, heavily used now
bprint(x, y, text, r, g, b, cen)
prints an outline, then just PrintAlpha
bprintalpha(x, y, text, r, g, b, a, cen)
just bprint but with an alpha argument
bigprint(x, y, text, r, g, b, cen, sc)
nothing special here, just does what the arguments say
bigbprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigprint
bigrprint(x, y, text, r, g, b, cen, sc)
right-aligns text, unless cen is given in which case it just
centers text like other functions already do?
bigbrprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigrprint
We need even more specifics with the new font system: we need to be
able to specify whether CJK characters should be vertically centered or
stick out on the top/bottom, and we sometimes need to pass in
brightness variables for colored glyphs. And text printing functions
now fit better in Font.cpp anyway. So there's now a big overhaul of
print functions: all these functions will be replaced by font::print
and font::print_wrap (the former of which now exists). These take flags
as their first argument, which can be 0 for a basic left-aligned print,
PR_CEN for centered text (set X to -1!!!) PR_BOR for a border (instead
of functions like bprint and bigbprint), PR_2X, PR_3X etc for scaling,
and these can be combined with |.
Some text, for example [Press ESC to return to editor], fades in/out
using the alpha value, which is passed to the print function. In some
other places (like Press ENTER to teleport, textboxes, trophy text...)
text can fade in or out by direct changes to the RGB values. This means
regular color-adjusted white text can change color, but colored button
glyphs can't, since there's no way to know in the print system what the
maximum RGB values of a specific textbox are supposed to be, so the
only thing it can do is draw the button glyphs at full brightness,
which looks bad. Therefore, you can now also pass in the brightness
value via the flags, with PR_COLORGLYPH_BRI(255).
2023-01-06 04:43:21 +01:00
font : : print (
Replace PR_COLORGLYPH_BRI(a) and PR_ALPHA(a) with PR_BRIGHTNESS(a)
There used to be two ways of fading in/out text in VVVVVV:
- Local code that modifies the R, G and B values of the text
- Keeping the RGB values the same and using the alpha channel
The latter approach is only used once, for [Press ENTER to return to
editor]. The former approach causes problems with colored (button)
glyphs: there's no way for the print function to tell from the RGB
values whether a color is "full Viridian-cyan" or "Viridian-cyan faded
out 50%", so I added the flag PR_COLORGLYPH_BRI(value) to tell the
print function that the color brightness is reduced to match the
brightness of colored glyphs to the brightness of the rest of the text.
However, there were already plans to make the single use of alpha
consistent with the rest of the game and the style, so PR_ALPHA(value)
could be removed, as well as the bit signifying whether the brightness
or alpha value is used. For the editor text, I simply copied the "Press
{button} to teleport" behavior of hiding the text completely if it
becomes darker than 100/255.
Another simplification is to make the print function handle not just
the brightness of the color glyphs while local code handled the
brightness of the normal text color, but to make the print function
handle both. That way, the callsite can simply pass in the full colors
and the brightness flag, and the flag name can be made a lot simpler as
well: PR_BRIGHTNESS(value).
2023-01-31 02:22:43 +01:00
PR_BRIGHTNESS ( alpha ) | PR_CEN | PR_BOR ,
Implement first font::print function, fix most fading of colored glyphs
There has always been a mess of different print functions that all had
slightly different specifics and called each other:
Print(x, y, text, r, g, b, cen)
nothing special here, just does what the arguments say
PrintAlpha(x, y, text, r, g, b, a, cen)
just Print but with an alpha argument
PrintWrap(x, y, text, r, g, b, cen, linespacing, maxwidth)
added for wordwrapping, heavily used now
bprint(x, y, text, r, g, b, cen)
prints an outline, then just PrintAlpha
bprintalpha(x, y, text, r, g, b, a, cen)
just bprint but with an alpha argument
bigprint(x, y, text, r, g, b, cen, sc)
nothing special here, just does what the arguments say
bigbprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigprint
bigrprint(x, y, text, r, g, b, cen, sc)
right-aligns text, unless cen is given in which case it just
centers text like other functions already do?
bigbrprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigrprint
We need even more specifics with the new font system: we need to be
able to specify whether CJK characters should be vertically centered or
stick out on the top/bottom, and we sometimes need to pass in
brightness variables for colored glyphs. And text printing functions
now fit better in Font.cpp anyway. So there's now a big overhaul of
print functions: all these functions will be replaced by font::print
and font::print_wrap (the former of which now exists). These take flags
as their first argument, which can be 0 for a basic left-aligned print,
PR_CEN for centered text (set X to -1!!!) PR_BOR for a border (instead
of functions like bprint and bigbprint), PR_2X, PR_3X etc for scaling,
and these can be combined with |.
Some text, for example [Press ESC to return to editor], fades in/out
using the alpha value, which is passed to the print function. In some
other places (like Press ENTER to teleport, textboxes, trophy text...)
text can fade in or out by direct changes to the RGB values. This means
regular color-adjusted white text can change color, but colored button
glyphs can't, since there's no way to know in the print system what the
maximum RGB values of a specific textbox are supposed to be, so the
only thing it can do is draw the button glyphs at full brightness,
which looks bad. Therefore, you can now also pass in the brightness
value via the flags, with PR_COLORGLYPH_BRI(255).
2023-01-06 04:43:21 +01:00
- 1 ,
graphics . flipmode ? 20 : 210 ,
final_string ,
Replace PR_COLORGLYPH_BRI(a) and PR_ALPHA(a) with PR_BRIGHTNESS(a)
There used to be two ways of fading in/out text in VVVVVV:
- Local code that modifies the R, G and B values of the text
- Keeping the RGB values the same and using the alpha channel
The latter approach is only used once, for [Press ENTER to return to
editor]. The former approach causes problems with colored (button)
glyphs: there's no way for the print function to tell from the RGB
values whether a color is "full Viridian-cyan" or "Viridian-cyan faded
out 50%", so I added the flag PR_COLORGLYPH_BRI(value) to tell the
print function that the color brightness is reduced to match the
brightness of colored glyphs to the brightness of the rest of the text.
However, there were already plans to make the single use of alpha
consistent with the rest of the game and the style, so PR_ALPHA(value)
could be removed, as well as the bit signifying whether the brightness
or alpha value is used. For the editor text, I simply copied the "Press
{button} to teleport" behavior of hiding the text completely if it
becomes darker than 100/255.
Another simplification is to make the print function handle not just
the brightness of the color glyphs while local code handled the
brightness of the normal text color, but to make the print function
handle both. That way, the callsite can simply pass in the full colors
and the brightness flag, and the flag name can be made a lot simpler as
well: PR_BRIGHTNESS(value).
2023-01-31 02:22:43 +01:00
235 - ( help . glow / 2 ) ,
235 - ( help . glow / 2 ) ,
255
Implement first font::print function, fix most fading of colored glyphs
There has always been a mess of different print functions that all had
slightly different specifics and called each other:
Print(x, y, text, r, g, b, cen)
nothing special here, just does what the arguments say
PrintAlpha(x, y, text, r, g, b, a, cen)
just Print but with an alpha argument
PrintWrap(x, y, text, r, g, b, cen, linespacing, maxwidth)
added for wordwrapping, heavily used now
bprint(x, y, text, r, g, b, cen)
prints an outline, then just PrintAlpha
bprintalpha(x, y, text, r, g, b, a, cen)
just bprint but with an alpha argument
bigprint(x, y, text, r, g, b, cen, sc)
nothing special here, just does what the arguments say
bigbprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigprint
bigrprint(x, y, text, r, g, b, cen, sc)
right-aligns text, unless cen is given in which case it just
centers text like other functions already do?
bigbrprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigrprint
We need even more specifics with the new font system: we need to be
able to specify whether CJK characters should be vertically centered or
stick out on the top/bottom, and we sometimes need to pass in
brightness variables for colored glyphs. And text printing functions
now fit better in Font.cpp anyway. So there's now a big overhaul of
print functions: all these functions will be replaced by font::print
and font::print_wrap (the former of which now exists). These take flags
as their first argument, which can be 0 for a basic left-aligned print,
PR_CEN for centered text (set X to -1!!!) PR_BOR for a border (instead
of functions like bprint and bigbprint), PR_2X, PR_3X etc for scaling,
and these can be combined with |.
Some text, for example [Press ESC to return to editor], fades in/out
using the alpha value, which is passed to the print function. In some
other places (like Press ENTER to teleport, textboxes, trophy text...)
text can fade in or out by direct changes to the RGB values. This means
regular color-adjusted white text can change color, but colored button
glyphs can't, since there's no way to know in the print system what the
maximum RGB values of a specific textbox are supposed to be, so the
only thing it can do is draw the button glyphs at full brightness,
which looks bad. Therefore, you can now also pass in the brightness
value via the flags, with PR_COLORGLYPH_BRI(255).
2023-01-06 04:43:21 +01:00
) ;
2020-01-01 21:29:24 +01:00
}
if ( game . swnmode )
{
2023-06-05 08:24:31 +02:00
if ( game . swngame = = SWN_GRAVITRON )
2020-01-01 21:29:24 +01:00
{
2020-04-03 00:05:41 +02:00
std : : string tempstring = help . timestring ( game . swntimer ) ;
2023-01-17 05:19:17 +01:00
font : : print ( PR_2X | PR_CEN | PR_BOR , - 1 , 20 , tempstring , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
2023-06-05 08:24:31 +02:00
else if ( game . swngame = = SWN_SUPERGRAVITRON )
2020-01-01 21:29:24 +01:00
{
if ( game . swnmessage = = 0 )
{
2020-04-03 00:05:41 +02:00
std : : string tempstring = help . timestring ( game . swntimer ) ;
2023-01-17 04:41:12 +01:00
font : : print ( PR_BOR , 10 , 10 , loc : : gettext ( " Current Time " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2023-01-16 21:29:50 +01:00
font : : print ( PR_2X | PR_BOR | PR_FONT_8X8 , 25 , 24 , tempstring , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
tempstring = help . timestring ( game . swnrecord ) ;
2023-01-17 04:41:12 +01:00
font : : print ( PR_BOR | PR_RIGHT , 320 - 8 , 10 , loc : : gettext ( " Best Time " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2023-01-16 21:29:50 +01:00
font : : print ( PR_2X | PR_BOR | PR_FONT_8X8 | PR_RIGHT , 300 , 24 , tempstring , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
switch ( game . swnbestrank )
{
case 0 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 204 , loc : : gettext ( " Next Trophy at 5 seconds " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
break ;
case 1 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 204 , loc : : gettext ( " Next Trophy at 10 seconds " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
break ;
case 2 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 204 , loc : : gettext ( " Next Trophy at 15 seconds " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
break ;
case 3 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 204 , loc : : gettext ( " Next Trophy at 20 seconds " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
break ;
case 4 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 204 , loc : : gettext ( " Next Trophy at 30 seconds " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
break ;
case 5 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 204 , loc : : gettext ( " Next Trophy at 1 minute " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
break ;
case 6 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 204 , loc : : gettext ( " All Trophies collected! " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
break ;
}
}
else if ( game . swnmessage = = 1 )
{
2020-04-03 00:05:41 +02:00
std : : string tempstring = help . timestring ( game . swntimer ) ;
2023-01-17 04:41:12 +01:00
font : : print ( PR_BOR , 10 , 10 , loc : : gettext ( " Current Time " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
font : : print ( PR_2X | PR_BOR | PR_FONT_8X8 , 25 , 24 , tempstring , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
tempstring = help . timestring ( game . swnrecord ) ;
if ( int ( game . deathseq / 5 ) % 2 = = 1 )
{
2023-01-17 04:41:12 +01:00
font : : print ( PR_BOR | PR_RIGHT , 320 - 8 , 10 , loc : : gettext ( " Best Time " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
font : : print ( PR_2X | PR_BOR | PR_FONT_8X8 | PR_RIGHT , 300 , 24 , tempstring , 128 - ( help . glow ) , 220 - ( help . glow ) , 128 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
2023-01-17 04:41:12 +01:00
font : : print ( PR_2X | PR_BOR | PR_CEN , - 1 , 200 , loc : : gettext ( " New Record! " ) , 128 - ( help . glow ) , 220 - ( help . glow ) , 128 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
}
else if ( game . swnmessage > = 2 )
{
game . swnmessage - - ;
if ( game . swnmessage = = 2 ) game . swnmessage = 0 ;
2020-04-03 00:05:41 +02:00
std : : string tempstring = help . timestring ( game . swntimer ) ;
2023-01-17 04:41:12 +01:00
font : : print ( PR_BOR , 10 , 10 , loc : : gettext ( " Current Time " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
font : : print ( PR_2X | PR_BOR | PR_FONT_8X8 , 25 , 24 , tempstring , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
tempstring = help . timestring ( game . swnrecord ) ;
2023-01-17 04:41:12 +01:00
font : : print ( PR_BOR | PR_RIGHT , 320 - 8 , 10 , loc : : gettext ( " Best Time " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
font : : print ( PR_2X | PR_BOR | PR_FONT_8X8 | PR_RIGHT , 300 , 24 , tempstring , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
if ( int ( game . swnmessage / 5 ) % 2 = = 1 )
{
2023-01-17 04:41:12 +01:00
font : : print ( PR_2X | PR_BOR | PR_CEN , - 1 , 200 , loc : : gettext ( " New Trophy! " ) , 220 - ( help . glow ) , 128 - ( help . glow ) , 128 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
}
Add support for button glyph display
This adds a function that converts an action (such as interacting
in-game) to the corresponding button text ("ENTER", "E") or button
glyph (PlayStation triangle, Steam Deck Y, etc). This function
currently only gives the existing ENTERs or Es, because I don't know
how best to detect controller usage, or whether the game is running on
a Steam Deck, or what buttons need to be displayed there. Still, it
should now be really easy to adapt the rendering of keyboard keys to
consoles, controllers, or rebound keys.
To identify the actions that currently need to be displayed, this
commit also adds the initial enums for action sets as described by
Ethan in a comment in #834 (Jan 18, 2022).
2023-03-18 22:30:16 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " [Press {button} to stop] " ) ,
" button:but " ,
vformat_button ( ActionSet_InGame , Action_InGame_Map )
) ;
font : : print ( PR_BOR | PR_CEN , - 1 , 228 , buffer , 160 - ( help . glow / 2 ) , 160 - ( help . glow / 2 ) , 160 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
2023-06-05 08:24:31 +02:00
else if ( game . swngame = = SWN_START_GRAVITRON_STEP_3 )
2020-01-01 21:29:24 +01:00
{
if ( int ( game . swndelay / 15 ) % 2 = = 1 | | game . swndelay > = 120 )
{
2021-03-19 21:57:05 +01:00
int y1 ;
int y2 ;
2020-04-01 23:59:19 +02:00
if ( graphics . flipmode )
2020-01-01 21:29:24 +01:00
{
2021-03-19 21:57:05 +01:00
y1 = 30 ;
y2 = 10 ;
2020-01-01 21:29:24 +01:00
}
else
{
2021-03-19 21:57:05 +01:00
y1 = 10 ;
y2 = 30 ;
2020-01-01 21:29:24 +01:00
}
2023-01-13 05:11:39 +01:00
font : : print ( PR_2X | PR_CEN | PR_BOR | PR_CJK_HIGH , - 1 , y1 , loc : : gettext ( " Survive for " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
font : : print ( PR_2X | PR_CEN | PR_BOR , - 1 , y2 , loc : : gettext ( " 60 seconds! " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
}
2023-06-05 08:24:31 +02:00
else if ( game . swngame = = SWN_START_SUPERGRAVITRON_STEP_2 )
2020-01-01 21:29:24 +01:00
{
if ( game . swndelay > = 60 )
{
2023-01-17 05:19:17 +01:00
font : : print ( PR_2X | PR_BOR | PR_CEN , - 1 , 20 , loc : : gettext ( " SUPER GRAVITRON " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
2020-04-03 00:05:41 +02:00
std : : string tempstring = help . timestring ( game . swnrecord ) ;
2023-01-17 05:19:17 +01:00
font : : print ( PR_BOR | PR_CEN , - 1 , 190 , loc : : gettext ( " Best Time " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2023-07-12 22:58:47 +02:00
font : : print ( PR_2X | PR_BOR | PR_CEN | PR_CJK_LOW , - 1 , 205 , tempstring , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
2023-01-17 05:19:17 +01:00
else if ( int ( game . swndelay / 10 ) % 2 = = 1 )
2020-01-01 21:29:24 +01:00
{
2023-01-17 05:19:17 +01:00
font : : print ( PR_2X | PR_BOR | PR_CEN , - 1 , 20 , loc : : gettext ( " SUPER GRAVITRON " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
font : : print ( PR_3X | PR_BOR | PR_CEN , - 1 , 200 , loc : : gettext ( " GO! " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
}
}
Enumify all fade modes
This removes the magic numbers previously used for controlling the fade
mode, which are really not readable at all unless you already know what
they mean.
0: FADE_NONE
1: FADE_FULLY_BLACK
2: FADE_START_FADEOUT
3: FADE_FADING_OUT
4: FADE_START_FADEIN
5: FADE_FADING_IN
There is also the macro FADEMODE_IS_FADING, which indicates when the
intention is to only check if the game is fading right now, which wasn't
clearly conveyed previously.
I also took the opportunity to clean up the style of any lines I
touched. This included rewriting if-else chains into case-switches,
turning one-liner if-then statements into proper blocks, fixing up
comments, and even commenting the `fademode == FADE_NONE` on the tower
spike checks (which, it was previously undocumented why that check was
there, but I think I know why it's there).
As for type safety, we already get some by transforming the variable
types into the enum. Assignment is prohibited without a cast. But,
apparently, comparison is perfectly legal and won't even give so much as
a warning. To work around this and make absolutely sure I made all
existing comparisons now use the enum, I temporarily changed it to be an
`enum class`, which is a C++11 feature that makes it so all comparisons
are illegal. Unfortunately, it scopes them in a namespace with the same
name as a class, so I had to temporarily define macros to make sure my
existing code worked. I also had to temporarily up the standard in
CMakeLists.txt to get it to compile. But after all that was done, I
found the rest of the places where a comparison to an integer was used,
and fixed them.
2022-04-25 09:57:47 +02:00
if ( game . intimetrial & & graphics . fademode = = FADE_NONE )
2020-01-01 21:29:24 +01:00
{
//Draw countdown!
if ( game . timetrialcountdown > 0 )
{
if ( game . timetrialcountdown < 30 )
{
2023-01-17 04:41:12 +01:00
if ( int ( game . timetrialcountdown / 4 ) % 2 = = 0 )
{
font : : print ( PR_4X | PR_CEN | PR_BOR , - 1 , 100 , loc : : gettext ( " Go! " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
}
2020-01-01 21:29:24 +01:00
}
else if ( game . timetrialcountdown < 60 )
{
2023-01-17 04:41:12 +01:00
font : : print ( PR_4X | PR_CEN | PR_BOR , - 1 , 100 , " 1 " , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
else if ( game . timetrialcountdown < 90 )
{
2023-01-17 04:41:12 +01:00
font : : print ( PR_4X | PR_CEN | PR_BOR , - 1 , 100 , " 2 " , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
else if ( game . timetrialcountdown < 120 )
{
2023-01-17 04:41:12 +01:00
font : : print ( PR_4X | PR_CEN | PR_BOR , - 1 , 100 , " 3 " , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
}
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
else if ( ! roomname_translator : : is_pausing ( ) & & ! game . translator_exploring )
2020-01-01 21:29:24 +01:00
{
Simplify time formatting functions
Here's my notes on all the existing functions and what kind of time
formats they output:
- Game::giventimestring(int hrs, int min, int sec)
H:MM:SS
MM:SS
- Game::timestring()
// uses game.hours/minutes/seconds
H:MM:SS
MM:SS
- Game::partimestring()
// uses game.timetrialpar (seconds)
MM:SS
- Game::resulttimestring()
// uses game.timetrialresulttime (sec) + timetrialresultframes (1/30s)
MM:SS.CC
- Game::timetstring(int t)
// t = seconds
MM:SS
- Game::timestringcenti(char* buffer, const size_t buffer_size)
// uses game.hours/minutes/seconds/frames
H:MM:SS.CC
MM:SS.CC
- UtilityClass::timestring(int t)
// t = frames, 30 frames = 1 second
S:CC
M:SS:CC
This is kind of a mess, and there's a lot of functions that do the same
thing except using different variables. For localization, I also want
translators to be able to localize all these time formats - many
languages use the decimal comma instead of the decimal point (12:34,56)
maybe some languages really prefer something like 1時02分11秒44瞬...
Which I don't know to be correct, but it's good to be prepared for it
and not restrict translators arbitrarily to only changing ":" and "."
when we can start making the system better in the first place.
I added a new function, UtilityClass::format_time. This is the place
where all time formats come together, given the number of seconds and
optionally frames. I have simplified the above-mentioned functions
somewhat, but I haven't given them a complete refactor or renaming -
I mainly made sure that they all use the same backend so I can make the
formats consistent and properly localizable.
(And before we start shoving more temporary char buffers everywhere
just to get rid of the std::string's, maybe we need to think of a
globally used working buffer of size SCREEN_WIDTH_CHARS+1, as a
register of sorts, for when any line of text needs to be made or
processed, then printed, and then goes unused. Maybe help.textrow,
or something like that.)
As for this commit, the available time formats are now more consistent
and changed a little in some places. Leading zeroes for the first unit
are now no longer included, time trial results and the Super Gravitron
can now display hours when they went to 60 minutes before, and we now
always use .CC instead of :CC. These are the formats:
- H:MM:SS
- H:MM:SS.CC
- M:SS
- M:SS.CC
- S.CC (only used when always_minutes=false, for the Gravitrons)
Here's what changes to the current functions:
- Game::partimestring() is removed - it was used in two places, and
could be replaced by game.timetstring(game.timetrialpar)
- Game::giventimestring(h,m,s) and Game::timestring() are now wrappers
for the other functions
- The four remaining functions (Game::resulttimestring(),
Game::timetstring(t), Game::timestringcenti(buffer, buffer_size)
and UtilityClass::timestring(t)) are now wrappers for the "central
function", UtilityClass::format_time.
- UtilityClass::twodigits(int t) is now unused so it's also removed.
- I also added int UtilityClass::hms_to_seconds(int h, int m, int s)
2021-12-25 17:13:46 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
2021-12-21 02:32:12 +01:00
game . timestringcenti ( buffer , sizeof ( buffer ) ) ;
2020-01-01 21:29:24 +01:00
//Draw OSD stuff
2022-12-31 01:13:10 +01:00
const char * tempstring = loc : : gettext ( " TIME: " ) ;
2023-01-17 04:41:12 +01:00
int label_len = font : : len ( 0 , tempstring ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 6 , 18 , tempstring , 255 , 255 , 255 ) ;
2022-12-31 01:13:10 +01:00
tempstring = loc : : gettext ( " DEATH: " ) ;
2023-01-17 04:41:12 +01:00
label_len = SDL_max ( label_len , font : : len ( 0 , tempstring ) ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 6 , 30 , tempstring , 255 , 255 , 255 ) ;
2022-12-31 01:13:10 +01:00
tempstring = loc : : gettext ( " SHINY: " ) ;
2023-01-17 04:41:12 +01:00
label_len = SDL_max ( label_len , font : : len ( 0 , tempstring ) ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 6 , 42 , tempstring , 255 , 255 , 255 ) ;
2020-01-01 21:29:24 +01:00
if ( game . timetrialparlost )
{
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 8 + label_len , 18 , buffer , 196 , 80 , 80 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 8 + label_len , 18 , buffer , 196 , 196 , 196 ) ;
2020-01-01 21:29:24 +01:00
}
if ( game . deathcounts > 0 )
{
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 8 + label_len , 30 , help . String ( game . deathcounts ) , 196 , 80 , 80 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 8 + label_len , 30 , help . String ( game . deathcounts ) , 196 , 196 , 196 ) ;
2020-01-01 21:29:24 +01:00
}
2022-12-31 01:13:10 +01:00
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " {n_trinkets} of {max_trinkets} " ) ,
" n_trinkets:int, max_trinkets:int " ,
game . trinkets ( ) , game . timetrialshinytarget
) ;
2020-04-07 08:46:27 +02:00
if ( game . trinkets ( ) < game . timetrialshinytarget )
2020-01-01 21:29:24 +01:00
{
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 8 + label_len , 42 , buffer , 196 , 80 , 80 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP , 8 + label_len , 42 , buffer , 196 , 196 , 196 ) ;
2020-01-01 21:29:24 +01:00
}
2023-01-17 04:41:12 +01:00
std : : string time = game . timetstring ( game . timetrialpar ) ;
2023-03-05 00:32:58 +01:00
label_len = font : : len ( 0 , time . c_str ( ) ) ;
2020-01-01 21:29:24 +01:00
if ( game . timetrialparlost )
{
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP | PR_RIGHT , 307 - label_len - 8 , 214 , loc : : gettext ( " PAR TIME: " ) , 80 , 80 , 80 ) ;
font : : print ( PR_BOR | PR_RTL_XFLIP , 307 - label_len , 214 , time , 80 , 80 , 80 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2024-01-03 22:54:20 +01:00
font : : print ( PR_BOR | PR_RTL_XFLIP | PR_RIGHT , 307 - label_len - 8 , 214 , loc : : gettext ( " PAR TIME: " ) , 255 , 255 , 255 ) ;
font : : print ( PR_BOR | PR_RTL_XFLIP , 307 - label_len , 214 , time , 196 , 196 , 196 ) ;
2020-01-01 21:29:24 +01:00
}
}
}
2020-04-29 06:58:19 +02:00
float act_alpha = graphics . lerp ( game . prev_act_fade , game . act_fade ) / 10.0f ;
2020-08-25 03:13:57 +02:00
if ( game . act_fade > 5 | | game . prev_act_fade > 5 )
2020-01-01 21:29:24 +01:00
{
2023-08-30 20:37:16 +02:00
const char * prompt = game . activity_lastprompt . c_str ( ) ;
if ( game . activity_gettext )
{
prompt = loc : : gettext ( prompt ) ;
}
2021-09-13 06:39:07 +02:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
2021-04-19 08:23:44 +02:00
const char * final_string = interact_prompt (
buffer ,
sizeof ( buffer ) ,
2023-08-30 20:37:16 +02:00
prompt
2021-04-19 08:23:44 +02:00
) ;
2023-02-21 03:05:45 +01:00
uint8_t text_r , text_g , text_b ;
2023-08-30 20:37:16 +02:00
uint32_t text_flags = ( game . activity_gettext ? PR_FONT_INTERFACE : PR_FONT_LEVEL )
| PR_BRIGHTNESS ( act_alpha * 255 ) | PR_CJK_LOW | PR_CEN ;
2023-02-21 03:05:45 +01:00
Add `setactivityposition(x,y)`, add new textbox color `transparent` (#847)
* Add `setactivityposition(x,y)`, add new textbox color `transparent`
This commit adds a new internal command as a part of the visual activity zone changes I've been making.
This one allows the user to reposition the activity zone to anywhere on the screen.
In addition, this commit adds the textbox color `transparent`, which just sets r, g and b to 0.
rgb(0, 0, 0) normally creates the color black, however in VVVVVV textboxes, it makes the background
of them invisible, and makes the text the off-white color which the game uses elsewhere.
* add new variables to hardreset
* Fix unwanted text centering; offset position by 16, 4
It makes sense for `setactivityposition(0, 0)` to place the activity zone in the default position,
so the x has been offset by 16, and the y has been offset by 4.
Text was being automatically centered, meaning any activity zone which wasn't centered had misplaced text.
This has been fixed by calculating the center manually, and offsetting it by the passed value.
2021-10-14 00:38:51 +02:00
if ( game . activity_r = = 0 & & game . activity_g = = 0 & & game . activity_b = = 0 )
{
2023-02-21 03:05:45 +01:00
text_r = 196 ;
text_g = 196 ;
text_b = 255 - help . glow ;
Add `setactivityposition(x,y)`, add new textbox color `transparent` (#847)
* Add `setactivityposition(x,y)`, add new textbox color `transparent`
This commit adds a new internal command as a part of the visual activity zone changes I've been making.
This one allows the user to reposition the activity zone to anywhere on the screen.
In addition, this commit adds the textbox color `transparent`, which just sets r, g and b to 0.
rgb(0, 0, 0) normally creates the color black, however in VVVVVV textboxes, it makes the background
of them invisible, and makes the text the off-white color which the game uses elsewhere.
* add new variables to hardreset
* Fix unwanted text centering; offset position by 16, 4
It makes sense for `setactivityposition(0, 0)` to place the activity zone in the default position,
so the x has been offset by 16, and the y has been offset by 4.
Text was being automatically centered, meaning any activity zone which wasn't centered had misplaced text.
This has been fixed by calculating the center manually, and offsetting it by the passed value.
2021-10-14 00:38:51 +02:00
}
else
{
2023-02-21 03:26:16 +01:00
short lines ;
font : : string_wordwrap ( text_flags , final_string , 37 * 8 , & lines ) ;
2023-01-13 20:32:05 +01:00
graphics . drawpixeltextbox (
2023-02-18 05:38:05 +01:00
4 ,
Implement first font::print function, fix most fading of colored glyphs
There has always been a mess of different print functions that all had
slightly different specifics and called each other:
Print(x, y, text, r, g, b, cen)
nothing special here, just does what the arguments say
PrintAlpha(x, y, text, r, g, b, a, cen)
just Print but with an alpha argument
PrintWrap(x, y, text, r, g, b, cen, linespacing, maxwidth)
added for wordwrapping, heavily used now
bprint(x, y, text, r, g, b, cen)
prints an outline, then just PrintAlpha
bprintalpha(x, y, text, r, g, b, a, cen)
just bprint but with an alpha argument
bigprint(x, y, text, r, g, b, cen, sc)
nothing special here, just does what the arguments say
bigbprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigprint
bigrprint(x, y, text, r, g, b, cen, sc)
right-aligns text, unless cen is given in which case it just
centers text like other functions already do?
bigbrprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigrprint
We need even more specifics with the new font system: we need to be
able to specify whether CJK characters should be vertically centered or
stick out on the top/bottom, and we sometimes need to pass in
brightness variables for colored glyphs. And text printing functions
now fit better in Font.cpp anyway. So there's now a big overhaul of
print functions: all these functions will be replaced by font::print
and font::print_wrap (the former of which now exists). These take flags
as their first argument, which can be 0 for a basic left-aligned print,
PR_CEN for centered text (set X to -1!!!) PR_BOR for a border (instead
of functions like bprint and bigbprint), PR_2X, PR_3X etc for scaling,
and these can be combined with |.
Some text, for example [Press ESC to return to editor], fades in/out
using the alpha value, which is passed to the print function. In some
other places (like Press ENTER to teleport, textboxes, trophy text...)
text can fade in or out by direct changes to the RGB values. This means
regular color-adjusted white text can change color, but colored button
glyphs can't, since there's no way to know in the print system what the
maximum RGB values of a specific textbox are supposed to be, so the
only thing it can do is draw the button glyphs at full brightness,
which looks bad. Therefore, you can now also pass in the brightness
value via the flags, with PR_COLORGLYPH_BRI(255).
2023-01-06 04:43:21 +01:00
game . activity_y + 4 ,
2023-01-13 20:32:05 +01:00
39 * 8 ,
2023-02-21 03:26:16 +01:00
16 + font : : height ( text_flags ) * lines ,
Implement first font::print function, fix most fading of colored glyphs
There has always been a mess of different print functions that all had
slightly different specifics and called each other:
Print(x, y, text, r, g, b, cen)
nothing special here, just does what the arguments say
PrintAlpha(x, y, text, r, g, b, a, cen)
just Print but with an alpha argument
PrintWrap(x, y, text, r, g, b, cen, linespacing, maxwidth)
added for wordwrapping, heavily used now
bprint(x, y, text, r, g, b, cen)
prints an outline, then just PrintAlpha
bprintalpha(x, y, text, r, g, b, a, cen)
just bprint but with an alpha argument
bigprint(x, y, text, r, g, b, cen, sc)
nothing special here, just does what the arguments say
bigbprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigprint
bigrprint(x, y, text, r, g, b, cen, sc)
right-aligns text, unless cen is given in which case it just
centers text like other functions already do?
bigbrprint(x, y, text, r, g, b, cen, sc)
prints an outline, then just bigrprint
We need even more specifics with the new font system: we need to be
able to specify whether CJK characters should be vertically centered or
stick out on the top/bottom, and we sometimes need to pass in
brightness variables for colored glyphs. And text printing functions
now fit better in Font.cpp anyway. So there's now a big overhaul of
print functions: all these functions will be replaced by font::print
and font::print_wrap (the former of which now exists). These take flags
as their first argument, which can be 0 for a basic left-aligned print,
PR_CEN for centered text (set X to -1!!!) PR_BOR for a border (instead
of functions like bprint and bigbprint), PR_2X, PR_3X etc for scaling,
and these can be combined with |.
Some text, for example [Press ESC to return to editor], fades in/out
using the alpha value, which is passed to the print function. In some
other places (like Press ENTER to teleport, textboxes, trophy text...)
text can fade in or out by direct changes to the RGB values. This means
regular color-adjusted white text can change color, but colored button
glyphs can't, since there's no way to know in the print system what the
maximum RGB values of a specific textbox are supposed to be, so the
only thing it can do is draw the button glyphs at full brightness,
which looks bad. Therefore, you can now also pass in the brightness
value via the flags, with PR_COLORGLYPH_BRI(255).
2023-01-06 04:43:21 +01:00
game . activity_r * act_alpha ,
game . activity_g * act_alpha ,
game . activity_b * act_alpha
) ;
2023-02-21 03:05:45 +01:00
text_r = game . activity_r ;
text_g = game . activity_g ;
text_b = game . activity_b ;
Add `setactivityposition(x,y)`, add new textbox color `transparent` (#847)
* Add `setactivityposition(x,y)`, add new textbox color `transparent`
This commit adds a new internal command as a part of the visual activity zone changes I've been making.
This one allows the user to reposition the activity zone to anywhere on the screen.
In addition, this commit adds the textbox color `transparent`, which just sets r, g and b to 0.
rgb(0, 0, 0) normally creates the color black, however in VVVVVV textboxes, it makes the background
of them invisible, and makes the text the off-white color which the game uses elsewhere.
* add new variables to hardreset
* Fix unwanted text centering; offset position by 16, 4
It makes sense for `setactivityposition(0, 0)` to place the activity zone in the default position,
so the x has been offset by 16, and the y has been offset by 4.
Text was being automatically centered, meaning any activity zone which wasn't centered had misplaced text.
This has been fixed by calculating the center manually, and offsetting it by the passed value.
2021-10-14 00:38:51 +02:00
}
2023-02-21 03:05:45 +01:00
2023-02-21 03:26:16 +01:00
font : : print_wrap (
text_flags ,
2023-02-21 03:05:45 +01:00
- 1 ,
game . activity_y + 12 ,
final_string ,
text_r ,
text_g ,
2023-02-21 03:26:16 +01:00
text_b ,
8 ,
37 * 8
2023-02-21 03:05:45 +01:00
) ;
2020-01-01 21:29:24 +01:00
}
2020-08-03 06:02:27 +02:00
if ( obj . trophytext > 0 | | obj . oldtrophytext > 0 )
2020-01-01 21:29:24 +01:00
{
2020-04-01 23:59:19 +02:00
graphics . drawtrophytext ( ) ;
2020-01-01 21:29:24 +01:00
}
2023-07-02 04:02:25 +02:00
level_debugger : : render ( ) ;
2020-01-01 21:29:24 +01:00
2020-04-27 04:24:50 +02:00
graphics . renderwithscreeneffects ( ) ;
2020-01-01 21:29:24 +01:00
}
Unify drawing room name on map menus into one function
Previously, it was copy-pasted and slightly different, when really, they
ought to both be the exact same code.
It kind of pains me that the room name, glitch name, and hidden name
don't own their own memory, but, that's to be addressed later.
What's a bit annoying is that the `temp` variable used in
`teleporterrender` also ends up being reused later in the function. In
this case, I opted to just redeclare them when they are used anyway, to
make it clearer.
Apart from `teleporterrender` no longer calling `map.area` or caring
about `map.custommode`, it also no longer cares about
`graphics.fademode` being 0. I could never actually get this condition
to be false in practice, and I have absolutely no idea why it's there.
I'm guessing it could be some weird edge case rendering issue if the
screen is fully black? But I wouldn't know how to trigger that, and
anyway it should probably be fixed elsewhere. So I'm just going to
remove that conditional.
2022-04-25 09:53:13 +02:00
static void draw_roomname_menu ( void )
2020-01-01 21:29:24 +01:00
{
Unify drawing room name on map menus into one function
Previously, it was copy-pasted and slightly different, when really, they
ought to both be the exact same code.
It kind of pains me that the room name, glitch name, and hidden name
don't own their own memory, but, that's to be addressed later.
What's a bit annoying is that the `temp` variable used in
`teleporterrender` also ends up being reused later in the function. In
this case, I opted to just redeclare them when they are used anyway, to
make it clearer.
Apart from `teleporterrender` no longer calling `map.area` or caring
about `map.custommode`, it also no longer cares about
`graphics.fademode` being 0. I could never actually get this condition
to be false in practice, and I have absolutely no idea why it's there.
I'm guessing it could be some weird edge case rendering issue if the
screen is fully black? But I wouldn't know how to trigger that, and
anyway it should probably be fixed elsewhere. So I'm just going to
remove that conditional.
2022-04-25 09:53:13 +02:00
const char * name ;
2020-05-02 21:18:54 +02:00
2021-09-13 06:54:47 +02:00
if ( map . hiddenname [ 0 ] ! = ' \0 ' )
2020-01-01 21:29:24 +01:00
{
2022-12-30 22:57:24 +01:00
name = loc : : gettext_roomname_special ( map . hiddenname ) ;
Unify drawing room name on map menus into one function
Previously, it was copy-pasted and slightly different, when really, they
ought to both be the exact same code.
It kind of pains me that the room name, glitch name, and hidden name
don't own their own memory, but, that's to be addressed later.
What's a bit annoying is that the `temp` variable used in
`teleporterrender` also ends up being reused later in the function. In
this case, I opted to just redeclare them when they are used anyway, to
make it clearer.
Apart from `teleporterrender` no longer calling `map.area` or caring
about `map.custommode`, it also no longer cares about
`graphics.fademode` being 0. I could never actually get this condition
to be false in practice, and I have absolutely no idea why it's there.
I'm guessing it could be some weird edge case rendering issue if the
screen is fully black? But I wouldn't know how to trigger that, and
anyway it should probably be fixed elsewhere. So I'm just going to
remove that conditional.
2022-04-25 09:53:13 +02:00
}
2020-01-01 21:29:24 +01:00
else
{
2022-12-30 22:57:24 +01:00
name = loc : : gettext_roomname ( map . custommode , game . roomx , game . roomy , map . roomname , map . roomname_special ) ;
2020-01-01 21:29:24 +01:00
}
2023-01-20 20:24:41 +01:00
font : : print ( PR_FONT_LEVEL | PR_CEN , - 1 , 2 , name , 196 , 196 , 255 - help . glow ) ;
Unify drawing room name on map menus into one function
Previously, it was copy-pasted and slightly different, when really, they
ought to both be the exact same code.
It kind of pains me that the room name, glitch name, and hidden name
don't own their own memory, but, that's to be addressed later.
What's a bit annoying is that the `temp` variable used in
`teleporterrender` also ends up being reused later in the function. In
this case, I opted to just redeclare them when they are used anyway, to
make it clearer.
Apart from `teleporterrender` no longer calling `map.area` or caring
about `map.custommode`, it also no longer cares about
`graphics.fademode` being 0. I could never actually get this condition
to be false in practice, and I have absolutely no idea why it's there.
I'm guessing it could be some weird edge case rendering issue if the
screen is fully black? But I wouldn't know how to trigger that, and
anyway it should probably be fixed elsewhere. So I'm just going to
remove that conditional.
2022-04-25 09:53:13 +02:00
}
/* Used to keep some graphics positions on the map screen
* the same in Flip Mode . */
# define FLIP(y, h) (graphics.flipmode ? 220 - (y) - (h) : (y))
2023-07-12 22:58:47 +02:00
# define FLIP_PR_CJK_LOW (graphics.flipmode ? PR_CJK_HIGH : PR_CJK_LOW)
# define FLIP_PR_CJK_HIGH (graphics.flipmode ? PR_CJK_LOW : PR_CJK_HIGH)
Unify drawing room name on map menus into one function
Previously, it was copy-pasted and slightly different, when really, they
ought to both be the exact same code.
It kind of pains me that the room name, glitch name, and hidden name
don't own their own memory, but, that's to be addressed later.
What's a bit annoying is that the `temp` variable used in
`teleporterrender` also ends up being reused later in the function. In
this case, I opted to just redeclare them when they are used anyway, to
make it clearer.
Apart from `teleporterrender` no longer calling `map.area` or caring
about `map.custommode`, it also no longer cares about
`graphics.fademode` being 0. I could never actually get this condition
to be false in practice, and I have absolutely no idea why it's there.
I'm guessing it could be some weird edge case rendering issue if the
screen is fully black? But I wouldn't know how to trigger that, and
anyway it should probably be fixed elsewhere. So I'm just going to
remove that conditional.
2022-04-25 09:53:13 +02:00
2022-11-30 17:10:27 +01:00
static void rendermap ( void )
2022-11-07 01:51:07 +01:00
{
2023-01-26 20:52:29 +01:00
if ( map . custommode & & map . customshowmm )
2022-11-07 01:51:07 +01:00
{
2024-07-05 23:59:15 +02:00
const MapRenderData data = map . get_render_data ( ) ;
graphics . drawpixeltextbox ( 35 + data . xoff , 16 + data . yoff , data . pixelsx + 10 , data . pixelsy + 10 , 65 , 185 , 207 ) ;
if ( graphics . customminimaps [ map . currentregion ] ! = NULL )
{
graphics . draw_region_image ( map . currentregion , 40 + data . xoff , 21 + data . yoff , data . pixelsx , data . pixelsy ) ;
}
else if ( map . currentregion = = 0 & & graphics . minimap_mounted )
{
graphics . drawpartimage ( IMAGE_MINIMAP , 40 + data . xoff , 21 + data . yoff , data . pixelsx , data . pixelsy ) ;
}
else
{
graphics . drawpartimage ( IMAGE_CUSTOMMINIMAP , 40 + data . xoff , 21 + data . yoff , data . pixelsx , data . pixelsy ) ;
}
2022-11-07 01:51:07 +01:00
return ;
}
graphics . drawpixeltextbox ( 35 , 16 , 250 , 190 , 65 , 185 , 207 ) ;
2023-01-07 19:28:07 +01:00
graphics . drawimage ( IMAGE_MINIMAP , 40 , 21 , false ) ;
2022-11-07 01:51:07 +01:00
}
2022-11-30 17:10:27 +01:00
static void rendermapfog ( void )
2022-11-07 01:51:07 +01:00
{
2024-07-05 23:59:15 +02:00
const MapRenderData data = map . get_render_data ( ) ;
2022-11-07 01:51:07 +01:00
2024-07-05 23:59:15 +02:00
for ( int j = data . starty ; j < data . starty + data . height ; j + + )
2022-11-07 01:51:07 +01:00
{
2024-07-05 23:59:15 +02:00
for ( int i = data . startx ; i < data . startx + data . width ; i + + )
2022-11-07 01:51:07 +01:00
{
if ( ! map . isexplored ( i , j ) )
{
// Draw the fog, depending on the custom zoom size
2022-11-30 17:37:41 +01:00
for ( int x = 0 ; x < data . zoom ; x + + )
2022-11-07 01:51:07 +01:00
{
2022-11-30 17:37:41 +01:00
for ( int y = 0 ; y < data . zoom ; y + + )
2022-11-07 01:51:07 +01:00
{
2024-07-05 23:59:15 +02:00
graphics . drawimage ( IMAGE_COVERED , data . xoff + 40 + ( x * 12 ) + ( ( i - data . startx ) * ( 12 * data . zoom ) ) , data . yoff + 21 + ( y * 9 ) + ( ( j - data . starty ) * ( 9 * data . zoom ) ) , false ) ;
2022-11-07 01:51:07 +01:00
}
}
}
}
}
}
2022-11-30 17:10:27 +01:00
static void rendermaplegend ( void )
2022-11-07 01:51:07 +01:00
{
// Draw the map legend, aka teleports/targets/trinkets
2024-07-05 23:59:15 +02:00
const MapRenderData data = map . get_render_data ( ) ;
2022-11-07 01:51:07 +01:00
for ( size_t i = 0 ; i < map . teleporters . size ( ) ; i + + )
{
2024-07-05 23:59:15 +02:00
int x = map . teleporters [ i ] . x - data . startx ;
int y = map . teleporters [ i ] . y - data . starty ;
if ( x > = 0 & & y > = 0 & & x < data . width & & y < data . height )
2022-11-07 01:51:07 +01:00
{
2024-07-05 23:59:15 +02:00
if ( map . showteleporters & & map . isexplored ( x + data . startx , y + data . starty ) )
{
font : : print ( PR_FONT_8X8 | PR_FULLBOR , data . legendxoff + ( x * 12 * data . zoom ) , data . legendyoff + ( y * 9 * data . zoom ) , " 💿 " , 171 , 255 , 252 ) ;
}
else if ( map . showtargets & & ! map . isexplored ( x + data . startx , y + data . starty ) )
{
font : : print ( PR_FONT_8X8 | PR_FULLBOR , data . legendxoff + ( x * 12 * data . zoom ) , data . legendyoff + ( y * 9 * data . zoom ) , " ❓ " , 64 , 64 , 64 ) ;
}
2022-11-07 01:51:07 +01:00
}
}
if ( map . showtrinkets )
{
for ( size_t i = 0 ; i < map . shinytrinkets . size ( ) ; i + + )
{
if ( ! obj . collect [ i ] )
{
2024-07-05 23:59:15 +02:00
int x = map . shinytrinkets [ i ] . x - data . startx ;
int y = map . shinytrinkets [ i ] . y - data . starty ;
if ( x > = 0 & & y > = 0 & & x < data . width & & y < data . height )
{
font : : print ( PR_FONT_8X8 | PR_FULLBOR , data . legendxoff + ( x * 12 * data . zoom ) , data . legendyoff + ( y * 9 * data . zoom ) , " 🪙 " , 254 , 252 , 58 ) ;
}
2022-11-07 01:51:07 +01:00
}
}
}
}
2022-11-30 17:10:27 +01:00
static void rendermapcursor ( const bool flashing )
2022-11-07 01:51:07 +01:00
{
2024-07-05 23:59:15 +02:00
const MapRenderData data = map . get_render_data ( ) ;
int room_x = game . roomx - data . startx - 100 ;
int room_y = game . roomy - data . starty - 100 ;
int pixels_x = room_x * 12 ;
int pixels_y = room_y * 9 ;
2022-11-07 01:51:07 +01:00
if ( ! map . custommode & & game . roomx = = 109 )
{
// Draw the tower specially
2023-04-06 01:45:40 +02:00
if ( ! flashing | | game . noflashingmode )
2022-11-07 01:51:07 +01:00
{
2024-07-05 23:59:15 +02:00
graphics . draw_rect ( 40 + pixels_x + 2 , 21 + 2 , 12 - 4 , 180 - 4 , 16 , 245 - ( help . glow * 2 ) , 245 - ( help . glow * 2 ) ) ;
2022-11-07 01:51:07 +01:00
}
else if ( map . cursorstate = = 1 )
{
if ( int ( map . cursordelay / 4 ) % 2 = = 0 )
{
2024-07-05 23:59:15 +02:00
graphics . draw_rect ( 40 + pixels_x , 21 , 12 , 180 , 255 , 255 , 255 ) ;
graphics . draw_rect ( 40 + pixels_x + 2 , 21 + 2 , 12 - 4 , 180 - 4 , 255 , 255 , 255 ) ;
2022-11-07 01:51:07 +01:00
}
}
else if ( map . cursorstate = = 2 & & ( int ( map . cursordelay / 15 ) % 2 = = 0 ) )
{
2024-07-05 23:59:15 +02:00
graphics . draw_rect ( 40 + pixels_x + 2 , 21 + 2 , 12 - 4 , 180 - 4 , 16 , 245 - ( help . glow ) , 245 - ( help . glow ) ) ;
2022-11-07 01:51:07 +01:00
}
return ;
}
2024-07-05 23:59:15 +02:00
if ( room_x > = 0 & & room_y > = 0 & & room_x < data . width & & room_y < data . height )
2022-11-07 02:03:33 +01:00
{
2024-07-05 23:59:15 +02:00
if ( ! flashing | | ( ( map . cursorstate = = 2 & & int ( map . cursordelay / 15 ) % 2 = = 0 ) | | game . noflashingmode ) )
{
graphics . draw_rect ( 40 + ( pixels_x * data . zoom ) + 2 + data . xoff , 21 + ( pixels_y * data . zoom ) + 2 + data . yoff , ( 12 * data . zoom ) - 4 , ( 9 * data . zoom ) - 4 , 16 , 245 - ( help . glow ) , 245 - ( help . glow ) ) ;
}
else if ( map . cursorstate = = 1 & & int ( map . cursordelay / 4 ) % 2 = = 0 )
{
graphics . draw_rect ( 40 + ( pixels_x * data . zoom ) + data . xoff , 21 + ( pixels_y * data . zoom ) + data . yoff , 12 * data . zoom , 9 * data . zoom , 255 , 255 , 255 ) ;
graphics . draw_rect ( 40 + ( pixels_x * data . zoom ) + 2 + data . xoff , 21 + ( pixels_y * data . zoom ) + 2 + data . yoff , ( 12 * data . zoom ) - 4 , ( 9 * data . zoom ) - 4 , 255 , 255 , 255 ) ;
}
2022-11-07 01:51:07 +01:00
}
}
Unify drawing room name on map menus into one function
Previously, it was copy-pasted and slightly different, when really, they
ought to both be the exact same code.
It kind of pains me that the room name, glitch name, and hidden name
don't own their own memory, but, that's to be addressed later.
What's a bit annoying is that the `temp` variable used in
`teleporterrender` also ends up being reused later in the function. In
this case, I opted to just redeclare them when they are used anyway, to
make it clearer.
Apart from `teleporterrender` no longer calling `map.area` or caring
about `map.custommode`, it also no longer cares about
`graphics.fademode` being 0. I could never actually get this condition
to be false in practice, and I have absolutely no idea why it's there.
I'm guessing it could be some weird edge case rendering issue if the
screen is fully black? But I wouldn't know how to trigger that, and
anyway it should probably be fixed elsewhere. So I'm just going to
remove that conditional.
2022-04-25 09:53:13 +02:00
void maprender ( void )
{
2023-01-07 19:28:07 +01:00
graphics . set_render_target ( graphics . menuTexture ) ;
graphics . clear ( ) ;
Unify drawing room name on map menus into one function
Previously, it was copy-pasted and slightly different, when really, they
ought to both be the exact same code.
It kind of pains me that the room name, glitch name, and hidden name
don't own their own memory, but, that's to be addressed later.
What's a bit annoying is that the `temp` variable used in
`teleporterrender` also ends up being reused later in the function. In
this case, I opted to just redeclare them when they are used anyway, to
make it clearer.
Apart from `teleporterrender` no longer calling `map.area` or caring
about `map.custommode`, it also no longer cares about
`graphics.fademode` being 0. I could never actually get this condition
to be false in practice, and I have absolutely no idea why it's there.
I'm guessing it could be some weird edge case rendering issue if the
screen is fully black? But I wouldn't know how to trigger that, and
anyway it should probably be fixed elsewhere. So I'm just going to
remove that conditional.
2022-04-25 09:53:13 +02:00
draw_roomname_menu ( ) ;
2020-01-01 21:29:24 +01:00
//Background color
2023-01-07 19:28:07 +01:00
graphics . fill_rect ( 0 , 12 , 320 , 240 , 10 , 24 , 26 ) ;
2020-01-01 21:29:24 +01:00
//Menubar:
2023-01-13 20:32:05 +01:00
graphics . drawpixeltextbox ( - 10 , 212 , 43 * 8 , 16 + font : : height ( PR_FONT_INTERFACE ) , 65 , 185 , 207 ) ;
2020-06-21 03:47:25 +02:00
2022-11-25 22:32:16 +01:00
// Draw the selected page name at the bottom
2020-06-21 03:47:25 +02:00
// menupage 0 - 3 is the pause screen
2020-11-16 03:12:28 +01:00
if ( script . running & & game . menupage = = 3 )
{
// While in a cutscene, you can only save
2022-12-31 01:48:27 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf ( buffer , sizeof ( buffer ) , loc : : get_langmeta ( ) - > menu_select_tight . c_str ( ) , " label:str " , loc : : gettext ( " SAVE " ) ) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN | PR_CJK_LOW , - 1 , 220 , buffer , 196 , 196 , 255 - help . glow ) ;
2020-11-16 03:12:28 +01:00
}
else if ( game . menupage < = 3 )
2020-01-01 21:29:24 +01:00
{
2022-12-31 01:48:27 +01:00
const char * tab1 ;
2020-01-01 21:29:24 +01:00
if ( game . insecretlab )
{
2022-12-31 01:48:27 +01:00
tab1 = loc : : gettext ( " GRAV " ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-09 08:34:26 +02:00
else if ( obj . flags [ 67 ] & & ! map . custommode )
2020-01-01 21:29:24 +01:00
{
2022-12-31 01:48:27 +01:00
tab1 = loc : : gettext ( " SHIP " ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2022-12-31 01:48:27 +01:00
tab1 = loc : : gettext ( " CREW " ) ;
2020-01-01 21:29:24 +01:00
}
2020-06-21 03:47:25 +02:00
# define TAB(opt, text) graphics.map_tab(opt, text, game.menupage == opt)
2022-12-31 01:48:27 +01:00
TAB ( 0 , loc : : gettext ( " MAP " ) ) ;
2020-06-21 03:47:25 +02:00
TAB ( 1 , tab1 ) ;
2022-12-31 01:48:27 +01:00
TAB ( 2 , loc : : gettext ( " STATS " ) ) ;
TAB ( 3 , loc : : gettext ( " SAVE " ) ) ;
2020-06-21 03:47:25 +02:00
# undef TAB
}
2020-01-01 21:29:24 +01:00
2020-06-23 00:24:18 +02:00
// Draw menu header
switch ( game . menupage )
{
case 30 :
case 31 :
case 32 :
case 33 :
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN | PR_CJK_LOW , - 1 , 220 , loc : : gettext ( " [ PAUSE ] " ) , 196 , 196 , 255 - help . glow ) ;
2020-06-23 00:24:18 +02:00
}
// Draw menu options
if ( game . menupage > = 30 & & game . menupage < = 33 )
{
# define OPTION(opt, text) graphics.map_option(opt, 4, text, game.menupage - 30 == opt)
2022-12-31 01:48:27 +01:00
OPTION ( 0 , loc : : gettext ( " return to game " ) ) ;
OPTION ( 1 , loc : : gettext ( " options " ) ) ;
OPTION ( 2 , loc : : gettext ( " quit to menu " ) ) ;
2020-06-23 00:24:18 +02:00
# undef OPTION
}
2023-07-12 22:58:47 +02:00
/* FIXME: about the code below where this is used (case 10/11/20/21)... I've seen better code.
* We should rewrite it to use graphics : : map_option , but until then . . . */
int selection_offset ;
{
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf ( buffer , sizeof ( buffer ) , loc : : get_langmeta ( ) - > menu_select . c_str ( ) , " label:str " , " " ) ;
selection_offset = font : : len ( 0 , buffer ) / 2 ;
}
2020-06-21 03:47:25 +02:00
// Draw the actual menu
switch ( game . menupage )
{
case 0 :
2022-11-07 01:51:07 +01:00
rendermap ( ) ;
2020-01-01 21:29:24 +01:00
if ( map . finalmode | | ( map . custommode & & ! map . customshowmm ) )
{
2022-11-07 01:51:07 +01:00
// Cover the whole map
2020-01-01 21:29:24 +01:00
for ( int j = 0 ; j < 20 ; j + + )
{
for ( int i = 0 ; i < 20 ; i + + )
{
2023-01-07 19:28:07 +01:00
graphics . drawimage ( IMAGE_COVERED , 40 + ( i * 12 ) , 21 + ( j * 9 ) , false ) ;
2020-01-01 21:29:24 +01:00
}
}
2023-01-17 22:18:39 +01:00
font : : print ( PR_CEN | PR_BOR , - 1 , 105 , loc : : gettext ( " NO SIGNAL " ) , 245 , 245 , 245 ) ;
2022-11-30 17:10:27 +01:00
}
else
{
2022-11-07 01:51:07 +01:00
rendermapfog ( ) ;
rendermapcursor ( true ) ;
rendermaplegend ( ) ;
2020-01-01 21:29:24 +01:00
}
break ;
case 1 :
if ( game . insecretlab )
{
2020-04-01 23:59:19 +02:00
if ( graphics . flipmode )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 174 , loc : : gettext ( " SUPER GRAVITRON HIGHSCORE " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
2020-04-03 00:05:41 +02:00
std : : string tempstring = help . timestring ( game . swnrecord ) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 124 , loc : : gettext ( " Best Time " ) , 196 , 196 , 255 - help . glow ) ;
2023-07-12 22:58:47 +02:00
font : : print ( PR_2X | PR_CEN | PR_CJK_HIGH , - 1 , 102 , tempstring , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
switch ( game . swnbestrank )
{
case 0 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 40 , loc : : gettext ( " Next Trophy at 5 seconds " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 1 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 40 , loc : : gettext ( " Next Trophy at 10 seconds " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 2 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 40 , loc : : gettext ( " Next Trophy at 15 seconds " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 3 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 40 , loc : : gettext ( " Next Trophy at 20 seconds " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 4 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 40 , loc : : gettext ( " Next Trophy at 30 seconds " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 5 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 40 , loc : : gettext ( " Next Trophy at 1 minute " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 6 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 40 , loc : : gettext ( " All Trophies collected! " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
}
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 40 , loc : : gettext ( " SUPER GRAVITRON HIGHSCORE " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
2020-04-03 00:05:41 +02:00
std : : string tempstring = help . timestring ( game . swnrecord ) ;
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 90 , loc : : gettext ( " Best Time " ) , 196 , 196 , 255 - help . glow ) ;
2023-07-12 22:58:47 +02:00
font : : print ( PR_2X | PR_CEN | PR_CJK_LOW , - 1 , 104 , tempstring , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
switch ( game . swnbestrank )
{
case 0 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 174 , loc : : gettext ( " Next Trophy at 5 seconds " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 1 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 174 , loc : : gettext ( " Next Trophy at 10 seconds " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 2 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 174 , loc : : gettext ( " Next Trophy at 15 seconds " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 3 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 174 , loc : : gettext ( " Next Trophy at 20 seconds " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 4 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 174 , loc : : gettext ( " Next Trophy at 30 seconds " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 5 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 174 , loc : : gettext ( " Next Trophy at 1 minute " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
case 6 :
2023-01-17 04:41:12 +01:00
font : : print_wrap ( PR_CEN , - 1 , 174 , loc : : gettext ( " All Trophies collected! " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
break ;
}
}
}
2020-04-09 08:34:26 +02:00
else if ( obj . flags [ 67 ] & & ! map . custommode )
2020-01-01 21:29:24 +01:00
{
2023-03-18 22:32:26 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " Press {button} to warp to the ship. " ) ,
" button:but " ,
vformat_button ( ActionSet_InGame , Action_InGame_ACTION )
) ;
font : : print_wrap ( PR_CEN , - 1 , 105 , buffer , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
}
else if ( map . custommode ) {
2021-02-21 00:40:11 +01:00
LevelMetaData & meta = cl . ListOfMetaData [ game . playcustomlevel ] ;
2020-06-21 04:27:32 +02:00
2023-01-19 20:15:17 +01:00
uint32_t title_flags = meta . title_is_gettext ? PR_FONT_INTERFACE : PR_FONT_LEVEL ;
uint32_t creator_flags = meta . creator_is_gettext ? PR_FONT_INTERFACE : PR_FONT_LEVEL ;
font : : print ( title_flags | PR_2X | PR_CEN , - 1 , FLIP ( 45 , 8 ) , meta . title , 196 , 196 , 255 - help . glow ) ;
int sp = SDL_max ( 10 , font : : height ( PR_FONT_LEVEL ) ) ;
Replace "by" for level authors with happy face
"by {author}" is a string that will cause a lot of localization-related
problems, which then become much worse when different languages and
levels can also need different fonts:
- If the author name is set to something in English instead of a name,
then it'll come out a bit weird if your VVVVVV is set to a different
language: "de various people", "por various people", etc. It's the
same problem with Discord bots completing "playing" or "watching" in
their statuses.
- Translators can't always fit "by" in two letters, and level creators
have understandably always assumed, and will continue to assume, that
"by" is two letters. So if you have your VVVVVV set to a language that
translates "by" as something long, then:
| by Various People and Others |
...may suddenly show up as something like:
|thorer Various People and Othe|
- "by" and author may need mutually incompatible fonts. For example, a
Japanese level in a Korean VVVVVV needs to be displayed with "by" in
Korean characters and the author name with Japanese characters, which
would need some very special code since languages may want to add
text both before and after the name.
- It's very possible that some languages can't translate "by" without
knowing the gender of the name, and I know some languages even
inflect names in really interesting ways (adding and even replacing
letters in first names, surnames, and anything in between, depending
on gender and what else is in the sentence).
So to solve all of this, the "by" is now replaced by a 10x10 face from
sprites.png, like a :viridian: emote. See it as a kind of avatar next
to a username, to clarify and assert that this line is for the author's
name. It should be a fairly obvious/recognizable icon, it fixes all the
above problems, and it's a bonus that we now have more happy faces in
VVVVVV.
2023-01-31 00:41:46 +01:00
graphics . print_level_creator ( creator_flags , FLIP ( 70 , 8 ) , meta . creator , 196 , 196 , 255 - help . glow ) ;
2023-01-19 20:15:17 +01:00
font : : print ( PR_FONT_LEVEL | PR_CEN , - 1 , FLIP ( 70 + sp , 8 ) , meta . website , 196 , 196 , 255 - help . glow ) ;
font : : print ( PR_FONT_LEVEL | PR_CEN , - 1 , FLIP ( 70 + sp * 3 , 8 ) , meta . Desc1 , 196 , 196 , 255 - help . glow ) ;
font : : print ( PR_FONT_LEVEL | PR_CEN , - 1 , FLIP ( 70 + sp * 4 , 8 ) , meta . Desc2 , 196 , 196 , 255 - help . glow ) ;
if ( sp < = 10 )
{
font : : print ( PR_FONT_LEVEL | PR_CEN , - 1 , FLIP ( 70 + sp * 5 , 8 ) , meta . Desc3 , 196 , 196 , 255 - help . glow ) ;
}
2020-06-21 04:14:40 +02:00
2021-02-21 00:40:11 +01:00
int remaining = cl . numcrewmates ( ) - game . crewmates ( ) ;
2020-06-21 04:29:50 +02:00
Replace "by" for level authors with happy face
"by {author}" is a string that will cause a lot of localization-related
problems, which then become much worse when different languages and
levels can also need different fonts:
- If the author name is set to something in English instead of a name,
then it'll come out a bit weird if your VVVVVV is set to a different
language: "de various people", "por various people", etc. It's the
same problem with Discord bots completing "playing" or "watching" in
their statuses.
- Translators can't always fit "by" in two letters, and level creators
have understandably always assumed, and will continue to assume, that
"by" is two letters. So if you have your VVVVVV set to a language that
translates "by" as something long, then:
| by Various People and Others |
...may suddenly show up as something like:
|thorer Various People and Othe|
- "by" and author may need mutually incompatible fonts. For example, a
Japanese level in a Korean VVVVVV needs to be displayed with "by" in
Korean characters and the author name with Japanese characters, which
would need some very special code since languages may want to add
text both before and after the name.
- It's very possible that some languages can't translate "by" without
knowing the gender of the name, and I know some languages even
inflect names in really interesting ways (adding and even replacing
letters in first names, surnames, and anything in between, depending
on gender and what else is in the sentence).
So to solve all of this, the "by" is now replaced by a 10x10 face from
sprites.png, like a :viridian: emote. See it as a kind of avatar next
to a username, to clarify and assert that this line is for the author's
name. It should be a fairly obvious/recognizable icon, it fixes all the
above problems, and it's a bonus that we now have more happy faces in
VVVVVV.
2023-01-31 00:41:46 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
2022-12-31 01:48:27 +01:00
loc : : gettext_plural_fill (
buffer , sizeof ( buffer ) ,
" {n_crew|wordy} crewmates remain " ,
" {n_crew|wordy} crewmate remains " ,
" n_crew:int " ,
remaining
) ;
2023-09-21 02:28:11 +02:00
font : : print_wrap ( PR_CEN , - 1 , FLIP ( 165 , 8 ) , buffer , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2020-04-01 23:59:19 +02:00
if ( graphics . flipmode )
2020-01-01 21:29:24 +01:00
{
for ( int i = 0 ; i < 3 ; i + + )
{
2020-04-01 23:59:19 +02:00
graphics . drawcrewman ( 16 , 32 + ( i * 64 ) , 2 - i , game . crewstats [ 2 - i ] ) ;
2020-01-01 21:29:24 +01:00
if ( game . crewstats [ ( 2 - i ) ] )
{
2020-04-01 23:59:19 +02:00
graphics . printcrewname ( 44 , 32 + ( i * 64 ) + 4 + 10 , 2 - i ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2020-04-01 23:59:19 +02:00
graphics . printcrewnamedark ( 44 , 32 + ( i * 64 ) + 4 + 10 , 2 - i ) ;
2020-01-01 21:29:24 +01:00
}
Add support for string cases in strings.xml (gendered Rescued/Missing)
I wanted to not complicate the system with different string cases (like
cgettext) if possible, and I have been able to keep the main strings a
simple English=Translation mapping thus far, but apparently strings
like "Rescued!" (which are one string in English), have to be
translated for the correct gender in some languages. So this was a good
time to add support for string cases anyway.
It's a number that can be given to a string to specify the specific
case it's used, to disambiguate identical English keys. In the case of
"Rescued!" and "Missing...", male versions of the string are case 1,
female versions are case 2, and Viridian being missing is case 3. Of
course, if a language doesn't need to use different variants, it can
simply fill in the same string for the different cases.
If any other string needs to switch to different cases: distinguish
them in the English strings.xml with the case="N" attribute (N=1 and
higher), sync language files from the translator menu (existing
translations for the uncased string will simply be copied to all cases)
and change loc::gettext("...") to loc::gettext_case("...", 1),
loc::gettext_case("...", 2), etc.
2022-12-01 01:27:30 +01:00
graphics . printcrewnamestatus ( 44 , 32 + ( i * 64 ) + 4 , 2 - i , game . crewstats [ ( 2 - i ) ] ) ;
2020-01-01 21:29:24 +01:00
2020-04-01 23:59:19 +02:00
graphics . drawcrewman ( 16 + 160 , 32 + ( i * 64 ) , ( 2 - i ) + 3 , game . crewstats [ ( 2 - i ) + 3 ] ) ;
2020-01-01 21:29:24 +01:00
if ( game . crewstats [ ( 2 - i ) + 3 ] )
{
2020-04-01 23:59:19 +02:00
graphics . printcrewname ( 44 + 160 , 32 + ( i * 64 ) + 4 + 10 , ( 2 - i ) + 3 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2020-04-01 23:59:19 +02:00
graphics . printcrewnamedark ( 44 + 160 , 32 + ( i * 64 ) + 4 + 10 , ( 2 - i ) + 3 ) ;
2020-01-01 21:29:24 +01:00
}
Add support for string cases in strings.xml (gendered Rescued/Missing)
I wanted to not complicate the system with different string cases (like
cgettext) if possible, and I have been able to keep the main strings a
simple English=Translation mapping thus far, but apparently strings
like "Rescued!" (which are one string in English), have to be
translated for the correct gender in some languages. So this was a good
time to add support for string cases anyway.
It's a number that can be given to a string to specify the specific
case it's used, to disambiguate identical English keys. In the case of
"Rescued!" and "Missing...", male versions of the string are case 1,
female versions are case 2, and Viridian being missing is case 3. Of
course, if a language doesn't need to use different variants, it can
simply fill in the same string for the different cases.
If any other string needs to switch to different cases: distinguish
them in the English strings.xml with the case="N" attribute (N=1 and
higher), sync language files from the translator menu (existing
translations for the uncased string will simply be copied to all cases)
and change loc::gettext("...") to loc::gettext_case("...", 1),
loc::gettext_case("...", 2), etc.
2022-12-01 01:27:30 +01:00
graphics . printcrewnamestatus ( 44 + 160 , 32 + ( i * 64 ) + 4 , ( 2 - i ) + 3 , game . crewstats [ ( 2 - i ) + 3 ] ) ;
2020-01-01 21:29:24 +01:00
}
}
else
{
for ( int i = 0 ; i < 3 ; i + + )
{
2020-04-01 23:59:19 +02:00
graphics . drawcrewman ( 16 , 32 + ( i * 64 ) , i , game . crewstats [ i ] ) ;
2020-01-01 21:29:24 +01:00
if ( game . crewstats [ i ] )
{
2020-04-01 23:59:19 +02:00
graphics . printcrewname ( 44 , 32 + ( i * 64 ) + 4 , i ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2020-04-01 23:59:19 +02:00
graphics . printcrewnamedark ( 44 , 32 + ( i * 64 ) + 4 , i ) ;
2020-01-01 21:29:24 +01:00
}
Add support for string cases in strings.xml (gendered Rescued/Missing)
I wanted to not complicate the system with different string cases (like
cgettext) if possible, and I have been able to keep the main strings a
simple English=Translation mapping thus far, but apparently strings
like "Rescued!" (which are one string in English), have to be
translated for the correct gender in some languages. So this was a good
time to add support for string cases anyway.
It's a number that can be given to a string to specify the specific
case it's used, to disambiguate identical English keys. In the case of
"Rescued!" and "Missing...", male versions of the string are case 1,
female versions are case 2, and Viridian being missing is case 3. Of
course, if a language doesn't need to use different variants, it can
simply fill in the same string for the different cases.
If any other string needs to switch to different cases: distinguish
them in the English strings.xml with the case="N" attribute (N=1 and
higher), sync language files from the translator menu (existing
translations for the uncased string will simply be copied to all cases)
and change loc::gettext("...") to loc::gettext_case("...", 1),
loc::gettext_case("...", 2), etc.
2022-12-01 01:27:30 +01:00
graphics . printcrewnamestatus ( 44 , 32 + ( i * 64 ) + 4 + 10 , i , game . crewstats [ i ] ) ;
2020-01-01 21:29:24 +01:00
2020-04-01 23:59:19 +02:00
graphics . drawcrewman ( 16 + 160 , 32 + ( i * 64 ) , i + 3 , game . crewstats [ i + 3 ] ) ;
2020-01-01 21:29:24 +01:00
if ( game . crewstats [ i + 3 ] )
{
2020-04-01 23:59:19 +02:00
graphics . printcrewname ( 44 + 160 , 32 + ( i * 64 ) + 4 , i + 3 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2020-04-01 23:59:19 +02:00
graphics . printcrewnamedark ( 44 + 160 , 32 + ( i * 64 ) + 4 , i + 3 ) ;
2020-01-01 21:29:24 +01:00
}
Add support for string cases in strings.xml (gendered Rescued/Missing)
I wanted to not complicate the system with different string cases (like
cgettext) if possible, and I have been able to keep the main strings a
simple English=Translation mapping thus far, but apparently strings
like "Rescued!" (which are one string in English), have to be
translated for the correct gender in some languages. So this was a good
time to add support for string cases anyway.
It's a number that can be given to a string to specify the specific
case it's used, to disambiguate identical English keys. In the case of
"Rescued!" and "Missing...", male versions of the string are case 1,
female versions are case 2, and Viridian being missing is case 3. Of
course, if a language doesn't need to use different variants, it can
simply fill in the same string for the different cases.
If any other string needs to switch to different cases: distinguish
them in the English strings.xml with the case="N" attribute (N=1 and
higher), sync language files from the translator menu (existing
translations for the uncased string will simply be copied to all cases)
and change loc::gettext("...") to loc::gettext_case("...", 1),
loc::gettext_case("...", 2), etc.
2022-12-01 01:27:30 +01:00
graphics . printcrewnamestatus ( 44 + 160 , 32 + ( i * 64 ) + 4 + 10 , i + 3 , game . crewstats [ i + 3 ] ) ;
2020-01-01 21:29:24 +01:00
}
}
}
break ;
case 2 :
2021-09-26 02:16:52 +02:00
{
2022-12-31 01:48:27 +01:00
int max_trinkets ;
2021-09-26 02:16:52 +02:00
if ( map . custommode )
2020-04-09 06:56:47 +02:00
{
2022-12-31 01:48:27 +01:00
max_trinkets = cl . numtrinkets ( ) ;
2020-04-09 06:56:47 +02:00
}
else
{
2022-12-31 01:48:27 +01:00
max_trinkets = 20 ;
2021-09-26 02:16:52 +02:00
}
2020-01-01 21:29:24 +01:00
2021-09-26 02:16:52 +02:00
/* Stats. */
2023-07-12 22:58:47 +02:00
font : : print ( PR_CEN | FLIP_PR_CJK_HIGH , - 1 , FLIP ( 52 , 8 ) , loc : : gettext ( " [Trinkets found] " ) , 196 , 196 , 255 - help . glow ) ;
2022-12-31 01:48:27 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " {n_trinkets|wordy} out of {max_trinkets|wordy} " ) ,
" n_trinkets:int, max_trinkets:int " ,
game . trinkets ( ) , max_trinkets
) ;
2023-07-12 22:58:47 +02:00
font : : print ( PR_CEN | FLIP_PR_CJK_LOW , - 1 , FLIP ( 64 , 8 ) , buffer , 96 , 96 , 96 ) ;
2020-01-01 21:29:24 +01:00
2023-07-12 22:58:47 +02:00
font : : print ( PR_CEN | FLIP_PR_CJK_HIGH , - 1 , FLIP ( 102 , 8 ) , loc : : gettext ( " [Number of Deaths] " ) , 196 , 196 , 255 - help . glow ) ;
font : : print ( PR_CEN | FLIP_PR_CJK_LOW , - 1 , FLIP ( 114 , 8 ) , help . String ( game . deathcounts ) , 96 , 96 , 96 ) ;
2020-01-01 21:29:24 +01:00
2023-07-12 22:58:47 +02:00
font : : print ( PR_CEN | FLIP_PR_CJK_HIGH , - 1 , FLIP ( 152 , 8 ) , loc : : gettext ( " [Time Taken] " ) , 196 , 196 , 255 - help . glow ) ;
font : : print ( PR_CEN | FLIP_PR_CJK_LOW , - 1 , FLIP ( 164 , 8 ) , game . timestring ( ) , 96 , 96 , 96 ) ;
2020-01-01 21:29:24 +01:00
break ;
2021-09-26 02:16:52 +02:00
}
2020-01-01 21:29:24 +01:00
case 3 :
2021-10-02 06:06:31 +02:00
{
Add level exploring menu for translators
I would, of course, recommend translators to translate the roomnames
while playing the full game (optionally in invincibility) so they can
immediately get all the context and maybe the most inspiration. And if
you want to go back into a specific level, then there's always the time
trials and intermission replays which will give you full coverage of
all the room names.
However, the time trials weren't really made for room name translation.
They have some annoying features like the instant restart when you
press ENTER at the wrong time, they remove context clues like
teleporters and companions, but the worst problem is that the last room
in a level is often completely untranslatable inside the time trials
because you immediately get sent to the results screen...
So, I added a new menu in the translator options, "explore game", which
gives you access to all the time trials and the two intermissions, from
the same menu. All these time trials (which they're still based off of,
under the hood) are stripped of the annoying features that come with
time trials. These are the changes I made to time trial behavior in
translator exploring mode:
- No 3-2-1-Go! countdown
- No on-screen time/death/shiny/par
- ENTER doesn't restart, and the map menu works. The entire map is also
revealed.
- Prize for the Reckless is in its normal form
- The teleporters in Entanglement Generator, Wheeler's Wormhole and
Level Complete are restored as context for room names (actually, we
should probably restore them in time trials anyway? Their "press to
teleport" prompt is already blocked out in time trials and they do
nothing other than being a checkpoint. I guess the reason they were
removed was to stop people from opening the teleporter menu when that
was not specifically blocked out in time trials yet.)
- The companions are there at the end of levels, and behave like in no
death mode (become happy and follow you to the teleporter). Also for
context.
- At the end of each level, you're not suddenly sent to the menu, but
you can use the teleporter at your leisure just like in the
intermission replays. In the Final Level, you do get sent to the menu
automatically, but after a longer delay.
I made another mark on VVVVVV: don't be startled, I added gamestates.
I wanted all teleporters at the end of levels to behave like the ones
at the end of the intermission replays, and all handling for
teleporting with specific companions is already done in gamestates, so
rather than adding conditional blocks across 5 or so different
gamestates, it made more sense to make a single gamestate for
"teleporting in translator exploring mode" (3090). I also added an
alternative to having to use gamestate 3500 or 82 for the end of the
final level: 3091-3092.
One other thing I want to add to the "explore game" menu: a per-level
count of how many room names are left to translate. That shouldn't be
too difficult, and I'm planning that for the next commit.
2022-11-26 03:33:17 +01:00
if ( game . inintermission | | game . translator_exploring )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 115 , loc : : gettext ( " Cannot Save in Level Replay " ) , 146 , 146 , 180 ) ;
2021-10-02 06:06:31 +02:00
break ;
2020-01-01 21:29:24 +01:00
}
2021-10-02 06:06:31 +02:00
if ( game . nodeathmode )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 115 , loc : : gettext ( " Cannot Save in No Death Mode " ) , 146 , 146 , 180 ) ;
2021-10-02 06:06:31 +02:00
break ;
2020-01-01 21:29:24 +01:00
}
2021-10-02 06:06:31 +02:00
if ( game . intimetrial )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 115 , loc : : gettext ( " How'd you get here? " ) , 146 , 146 , 180 ) ;
2021-10-02 06:06:31 +02:00
break ;
2020-01-01 21:29:24 +01:00
}
2021-10-02 06:06:31 +02:00
if ( game . insecretlab )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 115 , loc : : gettext ( " Cannot Save in Secret Lab " ) , 146 , 146 , 180 ) ;
2021-10-02 06:06:31 +02:00
break ;
2020-01-01 21:29:24 +01:00
}
2021-10-02 06:06:31 +02:00
if ( game . gamesavefailed )
2020-11-04 03:45:33 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 115 , loc : : gettext ( " ERROR: Could not save game! " ) , 146 , 146 , 180 ) ;
2021-10-02 06:06:31 +02:00
break ;
2020-11-04 03:45:33 +01:00
}
2020-01-01 21:29:24 +01:00
2021-10-02 06:06:31 +02:00
/* We are not in a special case, so draw the save screen now... */
2020-01-01 21:29:24 +01:00
2021-10-02 06:06:31 +02:00
if ( ! map . custommode )
{
2022-12-31 01:48:27 +01:00
/* FIXME: The text here should be automatically "balance-wrapped" instead of hardcoding the width.
2023-01-20 21:11:18 +01:00
* In fact , maybe print_wrap should balance - wrap by default . */
font : : print_wrap ( PR_CEN , - 1 , 174 , loc : : gettext ( " (Note: The game is autosaved at every teleporter.) " ) , 146 , 146 , 180 , 12 ) ;
2020-01-01 21:29:24 +01:00
}
2021-10-02 06:06:31 +02:00
if ( ! game . gamesaved )
2020-01-01 21:29:24 +01:00
{
2023-03-18 22:32:26 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " [Press {button} to save your game] " ) ,
" button:but " ,
vformat_button ( ActionSet_InGame , Action_InGame_ACTION )
) ;
font : : print ( PR_CEN , - 1 , 80 , buffer , 255 - help . glow * 2 , 255 - help . glow * 2 , 255 - help . glow ) ;
2021-10-02 06:06:31 +02:00
Replace std::string Game::telesummary and Game::quicksummary by Summary
Game::telesummary and Game::quicksummary stored the summary string for
the save files - which is the <summary> tag that says something like
"Space Station, 10:30:59". The game only ever displays the quicksave
variant of these two, for "Last Save:" on the map menu's SAVE tab.
So the telesave has a <summary> too, but it's never displayed anywhere.
(In fact, the area is often set to "nowhere"...)
However, the summary strings have another function: detect that both
the telesave and quicksave exist. If a summary string for a save is
empty, then that save is considered not to exist.
I'm refactoring the summary string system, by making the new variables
Game::last_telesave and Game::last_quicksave of type struct
Game::Summary. This struct should have all data necessary to display
the summary string at runtime, and thus translate it at runtime (so
we don't store a summary in a certain language and then display it in
the wrong font later - the summary can always be in the current
language). It also has an `exists` member, to replace the need to
check for empty strings.
The <summary> tag is now completely unused, but is still written to
for older versions of the game to read.
(This commit does not add the new string to the language files, since
Terry now added it separately in his own branch)
2023-09-12 14:36:58 +02:00
if ( map . custommode | | ! game . last_quicksave . exists )
2020-01-01 21:29:24 +01:00
{
2021-10-02 06:06:31 +02:00
break ;
2020-01-01 21:29:24 +01:00
}
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , FLIP ( 100 , 8 ) , loc : : gettext ( " Last Save: " ) , 164 - help . glow / 4 , 164 - help . glow / 4 , 164 ) ;
Replace std::string Game::telesummary and Game::quicksummary by Summary
Game::telesummary and Game::quicksummary stored the summary string for
the save files - which is the <summary> tag that says something like
"Space Station, 10:30:59". The game only ever displays the quicksave
variant of these two, for "Last Save:" on the map menu's SAVE tab.
So the telesave has a <summary> too, but it's never displayed anywhere.
(In fact, the area is often set to "nowhere"...)
However, the summary strings have another function: detect that both
the telesave and quicksave exist. If a summary string for a save is
empty, then that save is considered not to exist.
I'm refactoring the summary string system, by making the new variables
Game::last_telesave and Game::last_quicksave of type struct
Game::Summary. This struct should have all data necessary to display
the summary string at runtime, and thus translate it at runtime (so
we don't store a summary in a certain language and then display it in
the wrong font later - the summary can always be in the current
language). It also has an `exists` member, to replace the need to
check for empty strings.
The <summary> tag is now completely unused, but is still written to
for older versions of the game to read.
(This commit does not add the new string to the language files, since
Terry now added it separately in his own branch)
2023-09-12 14:36:58 +02:00
struct Game : : Summary * last = & game . last_quicksave ;
vformat_buf (
buffer , sizeof ( buffer ) ,
loc : : gettext ( " {area}, {time} " ) ,
" area:str, time:str " ,
2023-09-13 18:47:20 +02:00
loc : : gettext_roomname_special ( map . currentarea ( last - > saverx , last - > savery ) ) ,
Replace std::string Game::telesummary and Game::quicksummary by Summary
Game::telesummary and Game::quicksummary stored the summary string for
the save files - which is the <summary> tag that says something like
"Space Station, 10:30:59". The game only ever displays the quicksave
variant of these two, for "Last Save:" on the map menu's SAVE tab.
So the telesave has a <summary> too, but it's never displayed anywhere.
(In fact, the area is often set to "nowhere"...)
However, the summary strings have another function: detect that both
the telesave and quicksave exist. If a summary string for a save is
empty, then that save is considered not to exist.
I'm refactoring the summary string system, by making the new variables
Game::last_telesave and Game::last_quicksave of type struct
Game::Summary. This struct should have all data necessary to display
the summary string at runtime, and thus translate it at runtime (so
we don't store a summary in a certain language and then display it in
the wrong font later - the summary can always be in the current
language). It also has an `exists` member, to replace the need to
check for empty strings.
The <summary> tag is now completely unused, but is still written to
for older versions of the game to read.
(This commit does not add the new string to the language files, since
Terry now added it separately in his own branch)
2023-09-12 14:36:58 +02:00
game . giventimestring ( last - > hours , last - > minutes , last - > seconds ) . c_str ( )
) ;
font : : print ( PR_CEN , - 1 , FLIP ( 112 , 8 ) , buffer , 164 - help . glow / 4 , 164 - help . glow / 4 , 164 ) ;
2021-10-02 06:06:31 +02:00
break ;
}
2020-01-01 21:29:24 +01:00
2021-10-02 06:06:31 +02:00
/* We are only still here if the game has been quicksaved... */
2020-01-01 21:29:24 +01:00
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 36 , loc : : gettext ( " Game saved ok! " ) , 255 - help . glow / 2 , 255 - help . glow / 2 , 255 - help . glow / 2 ) ;
2020-01-01 21:29:24 +01:00
2021-10-02 06:06:31 +02:00
graphics . drawpixeltextbox ( 17 , 65 , 286 , 90 , 65 , 185 , 207 ) ;
2020-01-01 21:29:24 +01:00
2021-10-02 06:06:31 +02:00
if ( map . custommode )
{
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN | PR_FONT_LEVEL , - 1 , FLIP ( 90 , 8 ) , game . customleveltitle , 25 , 255 - help . glow / 2 , 255 - help . glow / 2 ) ;
2021-10-02 06:06:31 +02:00
}
else
{
size_t i ;
2023-09-12 04:15:12 +02:00
font : : print (
PR_CEN , - 1 , FLIP ( 80 , 8 ) ,
2023-09-13 18:47:20 +02:00
loc : : gettext_roomname_special ( map . currentarea ( game . last_quicksave . saverx , game . last_quicksave . savery ) ) ,
2023-09-12 04:15:12 +02:00
25 , 255 - help . glow / 2 , 255 - help . glow / 2
) ;
2021-10-02 06:06:31 +02:00
for ( i = 0 ; i < SDL_arraysize ( game . crewstats ) ; + + i )
2020-01-01 21:29:24 +01:00
{
2021-10-02 06:06:31 +02:00
/* Crewmates are annoying. Their height is 21 pixels, but to flip them,
* we also have to account for their 2 - pixel y - offset ( and multiply it by 2 ) . */
graphics . drawcrewman ( 169 - 3 * 42 + i * 42 , FLIP ( 95 , 21 + 2 * 2 ) , i , game . crewstats [ i ] , true ) ;
2020-01-01 21:29:24 +01:00
}
}
2021-10-02 06:06:31 +02:00
2023-01-20 20:24:41 +01:00
font : : print ( 0 , 59 , FLIP ( 132 , 8 ) , game . savetime , 255 - help . glow / 2 , 255 - help . glow / 2 , 255 - help . glow / 2 ) ;
2022-12-31 01:48:27 +01:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf ( buffer , sizeof ( buffer ) ,
loc : : gettext ( " {savebox_n_trinkets|wordy} " ) ,
" savebox_n_trinkets:int " ,
game . savetrinkets
) ;
2023-01-17 04:41:12 +01:00
font : : print ( PR_RIGHT , 262 , FLIP ( 132 , 8 ) , buffer , 255 - help . glow / 2 , 255 - help . glow / 2 , 255 - help . glow / 2 ) ;
2021-10-02 06:06:31 +02:00
2023-06-04 01:00:30 +02:00
if ( graphics . flipmode )
{
graphics . draw_flipsprite ( 34 , FLIP ( 126 , 17 ) , 50 , graphics . col_clock ) ;
graphics . draw_flipsprite ( 270 , FLIP ( 126 , 17 ) , 22 , graphics . col_trinket ) ;
}
else
{
graphics . draw_sprite ( 34 , FLIP ( 126 , 17 ) , 50 , graphics . col_clock ) ;
graphics . draw_sprite ( 270 , FLIP ( 126 , 17 ) , 22 , graphics . col_trinket ) ;
}
2020-01-01 21:29:24 +01:00
break ;
2021-10-02 06:06:31 +02:00
}
2020-01-01 21:29:24 +01:00
case 10 :
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN | PR_CJK_LOW , - 1 , 220 , loc : : gettext ( " [ QUIT ] " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
2020-04-01 23:59:19 +02:00
if ( graphics . flipmode )
2020-01-01 21:29:24 +01:00
{
2020-06-22 23:40:12 +02:00
if ( game . inspecial ( ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 135 , loc : : gettext ( " Return to main menu? " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 142 , loc : : gettext ( " Do you want to quit? You will lose any unsaved progress. " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2020-01-01 21:29:24 +01:00
}
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 80 - selection_offset , 88 , loc : : gettext ( " [ NO, KEEP PLAYING ] " ) , 196 , 196 , 255 - help . glow ) ;
font : : print ( PR_RTL_XFLIP , 80 + 32 , 76 , loc : : gettext ( " yes, quit to menu " ) , 96 , 96 , 96 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2020-06-22 23:40:12 +02:00
if ( game . inspecial ( ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 80 , loc : : gettext ( " Return to main menu? " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 76 , loc : : gettext ( " Do you want to quit? You will lose any unsaved progress. " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2020-01-01 21:29:24 +01:00
}
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 80 - selection_offset , 130 , loc : : gettext ( " [ NO, KEEP PLAYING ] " ) , 196 , 196 , 255 - help . glow ) ;
font : : print ( PR_RTL_XFLIP , 80 + 32 , 142 , loc : : gettext ( " yes, quit to menu " ) , 96 , 96 , 96 ) ;
2020-01-01 21:29:24 +01:00
}
break ;
case 11 :
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN | PR_CJK_LOW , - 1 , 220 , loc : : gettext ( " [ QUIT ] " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
2020-04-01 23:59:19 +02:00
if ( graphics . flipmode )
2020-01-01 21:29:24 +01:00
{
2020-06-22 23:40:12 +02:00
if ( game . inspecial ( ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 135 , loc : : gettext ( " Return to main menu? " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 142 , loc : : gettext ( " Do you want to quit? You will lose any unsaved progress. " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2020-01-01 21:29:24 +01:00
}
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 80 , 88 , loc : : gettext ( " no, keep playing " ) , 96 , 96 , 96 ) ;
font : : print ( PR_RTL_XFLIP , 80 + 32 - selection_offset , 76 , loc : : gettext ( " [ YES, QUIT TO MENU ] " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2020-06-22 23:40:12 +02:00
if ( game . inspecial ( ) )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 80 , loc : : gettext ( " Return to main menu? " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 76 , loc : : gettext ( " Do you want to quit? You will lose any unsaved progress. " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2020-01-01 21:29:24 +01:00
}
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 80 , 130 , loc : : gettext ( " no, keep playing " ) , 96 , 96 , 96 ) ;
font : : print ( PR_RTL_XFLIP , 80 + 32 - selection_offset , 142 , loc : : gettext ( " [ YES, QUIT TO MENU ] " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
}
break ;
case 20 :
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN | PR_CJK_LOW , - 1 , 220 , loc : : gettext ( " [ GRAVITRON ] " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
2020-04-01 23:59:19 +02:00
if ( graphics . flipmode )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 88 , loc : : gettext ( " Do you want to return to the secret laboratory? " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 80 - selection_offset , 142 , loc : : gettext ( " [ NO, KEEP PLAYING ] " ) , 196 , 196 , 255 - help . glow ) ;
font : : print ( PR_RTL_XFLIP , 80 + 32 , 130 , loc : : gettext ( " yes, return " ) , 96 , 96 , 96 ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 76 , loc : : gettext ( " Do you want to return to the secret laboratory? " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 80 - selection_offset , 130 , loc : : gettext ( " [ NO, KEEP PLAYING ] " ) , 196 , 196 , 255 - help . glow ) ;
font : : print ( PR_RTL_XFLIP , 80 + 32 , 142 , loc : : gettext ( " yes, return " ) , 96 , 96 , 96 ) ;
2020-01-01 21:29:24 +01:00
}
break ;
case 21 :
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN | PR_CJK_LOW , - 1 , 220 , loc : : gettext ( " [ GRAVITRON ] " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
2020-04-01 23:59:19 +02:00
if ( graphics . flipmode )
2020-01-01 21:29:24 +01:00
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 88 , loc : : gettext ( " Do you want to return to the secret laboratory? " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 80 , 142 , loc : : gettext ( " no, keep playing " ) , 96 , 96 , 96 ) ;
font : : print ( PR_RTL_XFLIP , 80 + 32 - selection_offset , 130 , loc : : gettext ( " [ YES, RETURN ] " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2023-01-20 21:11:18 +01:00
font : : print_wrap ( PR_CEN , - 1 , 76 , loc : : gettext ( " Do you want to return to the secret laboratory? " ) , 196 , 196 , 255 - help . glow , 12 ) ;
2024-01-03 22:54:20 +01:00
font : : print ( PR_RTL_XFLIP , 80 , 130 , loc : : gettext ( " no, keep playing " ) , 96 , 96 , 96 ) ;
font : : print ( PR_RTL_XFLIP , 80 + 32 - selection_offset , 142 , loc : : gettext ( " [ YES, RETURN ] " ) , 196 , 196 , 255 - help . glow ) ;
2020-01-01 21:29:24 +01:00
}
}
2023-01-07 19:28:07 +01:00
graphics . set_render_target ( graphics . gameTexture ) ;
2020-01-01 21:29:24 +01:00
2023-01-07 19:28:07 +01:00
if ( graphics . resumegamemode | | graphics . menuoffset > 0 | | graphics . oldmenuoffset > 0 )
{
graphics . menuoffrender ( ) ;
}
else
{
graphics . copy_texture ( graphics . menuTexture , NULL , NULL ) ;
}
2020-01-01 21:29:24 +01:00
2020-06-25 23:41:44 +02:00
// We need to draw the black screen above the menu in order to disguise it
// being jankily brought down in glitchrunner mode when exiting to the title
// Otherwise, there's no reason to obscure the menu
2021-08-06 00:05:14 +02:00
if ( GlitchrunnerMode_less_than_or_equal ( Glitchrunner2_2 )
2023-01-07 19:28:07 +01:00
| | FADEMODE_IS_FADING ( graphics . fademode )
| | game . fadetomenu
| | game . fadetolab )
2020-05-08 00:53:12 +02:00
{
graphics . drawfade ( ) ;
}
2020-01-01 21:29:24 +01:00
2023-01-07 19:28:07 +01:00
graphics . renderwithscreeneffects ( ) ;
2020-01-01 21:29:24 +01:00
}
2023-07-12 22:58:47 +02:00
# undef FLIP_PR_CJK_HIGH
# undef FLIP_PR_CJK_LOW
2021-10-02 06:06:31 +02:00
# undef FLIP
Explicitly declare void for all void parameter functions (#628)
Apparently in C, if you have `void test();`, it's completely okay to do
`test(2);`. The function will take in the argument, but just discard it
and throw it away. It's like a trash can, and a rude one at that. If you
declare it like `void test(void);`, this is prevented.
This is not a problem in C++ - doing `void test();` and `test(2);` is
guaranteed to result in a compile error (this also means that right now,
at least in all `.cpp` files, nobody is ever calling a void parameter
function with arguments and having their arguments be thrown away).
However, we may not be using C++ in the future, so I just want to lay
down the precedent that if a function takes in no arguments, you must
explicitly declare it as such.
I would've added `-Wstrict-prototypes`, but it produces an annoying
warning message saying it doesn't work in C++ mode if you're compiling
in C++ mode. So it can be added later.
2021-02-25 23:23:59 +01:00
void teleporterrender ( void )
2020-01-01 21:29:24 +01:00
{
2023-01-07 19:28:07 +01:00
graphics . set_render_target ( graphics . menuTexture ) ;
graphics . clear ( ) ;
2022-12-30 07:23:48 +01:00
const int telex = map . teleporters [ game . teleport_to_teleporter ] . x ;
const int teley = map . teleporters [ game . teleport_to_teleporter ] . y ;
Unify drawing room name on map menus into one function
Previously, it was copy-pasted and slightly different, when really, they
ought to both be the exact same code.
It kind of pains me that the room name, glitch name, and hidden name
don't own their own memory, but, that's to be addressed later.
What's a bit annoying is that the `temp` variable used in
`teleporterrender` also ends up being reused later in the function. In
this case, I opted to just redeclare them when they are used anyway, to
make it clearer.
Apart from `teleporterrender` no longer calling `map.area` or caring
about `map.custommode`, it also no longer cares about
`graphics.fademode` being 0. I could never actually get this condition
to be false in practice, and I have absolutely no idea why it's there.
I'm guessing it could be some weird edge case rendering issue if the
screen is fully black? But I wouldn't know how to trigger that, and
anyway it should probably be fixed elsewhere. So I'm just going to
remove that conditional.
2022-04-25 09:53:13 +02:00
draw_roomname_menu ( ) ;
2020-01-01 21:29:24 +01:00
//Background color
2023-01-07 19:28:07 +01:00
graphics . fill_rect ( 0 , 12 , 320 , 240 , 10 , 24 , 26 ) ;
2020-01-01 21:29:24 +01:00
2022-11-07 01:51:07 +01:00
rendermap ( ) ;
rendermapfog ( ) ;
rendermapcursor ( false ) ;
2020-01-01 21:29:24 +01:00
2022-11-07 01:51:07 +01:00
// Draw a box around the currently selected teleporter
2024-07-05 23:59:15 +02:00
const MapRenderData data = map . get_render_data ( ) ;
2020-01-01 21:29:24 +01:00
if ( game . useteleporter )
{
//Draw the chosen destination coordinate!
2020-04-02 23:19:15 +02:00
//TODO
2020-01-01 21:29:24 +01:00
//draw the coordinates //destination
2023-05-22 21:09:32 +02:00
graphics . draw_rect ( 40 + data . xoff + ( telex * 12 * data . zoom ) + 1 , 21 + data . yoff + ( teley * 9 * data . zoom ) + 1 , 12 * data . zoom - 2 , 9 * data . zoom - 2 , 245 - ( help . glow * 2 ) , 16 , 16 ) ;
graphics . draw_rect ( 40 + data . xoff + ( telex * 12 * data . zoom ) + 3 , 21 + data . yoff + ( teley * 9 * data . zoom ) + 3 , 12 * data . zoom - 6 , 9 * data . zoom - 6 , 245 - ( help . glow * 2 ) , 16 , 16 ) ;
2020-01-01 21:29:24 +01:00
}
2022-11-07 01:51:07 +01:00
// Draw the legend itself
2020-01-01 21:29:24 +01:00
2022-11-07 01:51:07 +01:00
rendermaplegend ( ) ;
// Highlight the currently selected teleporter
2023-04-06 01:45:40 +02:00
if ( game . useteleporter & & ( help . slowsine % 16 > 8 | | game . noflashingmode ) )
2020-01-01 21:29:24 +01:00
{
2023-11-16 01:11:32 +01:00
font : : print ( PR_FONT_8X8 | PR_FULLBOR , data . legendxoff + data . xoff + ( telex * 12 * data . zoom ) , data . legendyoff + data . yoff + ( teley * 9 * data . zoom ) , " 💿 " , 255 , 0 , 0 ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-01 23:59:19 +02:00
graphics . cutscenebars ( ) ;
2020-01-01 21:29:24 +01:00
if ( game . useteleporter )
{
2021-09-13 06:39:07 +02:00
char buffer [ SCREEN_WIDTH_CHARS + 1 ] ;
2021-04-19 08:23:44 +02:00
const char * final_string = interact_prompt (
buffer ,
sizeof ( buffer ) ,
2022-12-30 23:26:51 +01:00
loc : : gettext ( " Press {button} to Teleport " )
2021-04-19 08:23:44 +02:00
) ;
2020-01-01 21:29:24 +01:00
//Instructions!
2023-01-20 20:24:41 +01:00
font : : print ( PR_CEN , - 1 , 210 , loc : : gettext ( " Press Left/Right to choose a Teleporter " ) , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
font : : print ( PR_CEN , - 1 , 225 , final_string , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
2020-04-01 23:59:19 +02:00
graphics . drawgui ( ) ;
2020-01-01 21:29:24 +01:00
2023-03-18 22:32:26 +01:00
if ( game . advancetext )
2020-01-01 21:29:24 +01:00
{
2023-03-18 22:32:26 +01:00
char buffer_adv [ SCREEN_WIDTH_CHARS + 1 ] ;
vformat_buf (
buffer_adv , sizeof ( buffer_adv ) ,
loc : : gettext ( " - Press {button} to advance text - " ) ,
" button:but " ,
vformat_button ( ActionSet_InGame , Action_InGame_ACTION )
) ;
font : : print ( PR_CEN | PR_BOR , - 1 , graphics . flipmode ? 228 : 5 , buffer_adv , 220 - ( help . glow ) , 220 - ( help . glow ) , 255 - ( help . glow / 2 ) ) ;
2020-01-01 21:29:24 +01:00
}
2023-01-07 19:28:07 +01:00
graphics . set_render_target ( graphics . gameTexture ) ;
2020-01-01 21:29:24 +01:00
2020-05-02 01:49:10 +02:00
if ( graphics . resumegamemode | | graphics . menuoffset > 0 | | graphics . oldmenuoffset > 0 )
2020-01-01 21:29:24 +01:00
{
2020-04-01 23:59:19 +02:00
graphics . menuoffrender ( ) ;
2020-01-01 21:29:24 +01:00
}
else
{
2023-01-07 19:28:07 +01:00
graphics . copy_texture ( graphics . menuTexture , NULL , NULL ) ;
2020-01-01 21:29:24 +01:00
}
2023-01-07 19:28:07 +01:00
graphics . render ( ) ;
2020-01-01 21:29:24 +01:00
}