2020-09-28 04:15:06 +02:00
# define SCRIPT_DEFINITION
2020-01-01 21:29:24 +01:00
# include "Script.h"
2021-02-16 03:49:24 +01:00
# include <limits.h>
2021-08-22 04:51:19 +02:00
# include <SDL_timer.h>
2021-02-16 03:49:24 +01:00
2023-03-18 22:31:13 +01:00
# include "Alloc.h"
2022-12-30 22:57:24 +01:00
# include "Constants.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:05:41 +02:00
# include "Enums.h"
2021-02-16 03:53:17 +01:00
# include "Exit.h"
2023-01-06 19:17:50 +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"
2020-01-01 21:29:24 +01:00
# include "KeyPoll.h"
2022-12-30 22:57:24 +01:00
# include "Localization.h"
2022-12-24 04:16:56 +01:00
# include "LocalizationMaint.h"
2022-12-30 22:57:24 +01:00
# include "LocalizationStorage.h"
2020-01-01 21:29:24 +01:00
# include "Map.h"
2020-07-19 21:43:29 +02:00
# include "Music.h"
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
# include "Unreachable.h"
2020-07-19 21:05:41 +02:00
# include "UtilityClass.h"
2022-12-30 22:57:24 +01:00
# include "VFormat.h"
2021-02-24 00:21:29 +01:00
# include "Vlogging.h"
2021-08-22 04:51:19 +02:00
# include "Xoshiro.h"
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
scriptclass : : scriptclass ( void )
2020-01-01 21:29:24 +01:00
{
2021-09-07 03:56:39 +02:00
position = 0 ;
scriptdelay = 0 ;
running = false ;
b = 0 ;
g = 0 ;
i = 0 ;
j = 0 ;
k = 0 ;
loopcount = 0 ;
looppoint = 0 ;
r = 0 ;
textx = 0 ;
texty = 0 ;
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
textcrewmateposition = TextboxCrewmatePosition ( ) ;
textoriginalcontext = TextboxOriginalContext ( ) ;
2023-03-18 22:48:20 +01:00
textbox_colours . clear ( ) ;
add_default_colours ( ) ;
2021-09-07 03:56:39 +02:00
textflipme = false ;
2023-04-20 14:53:30 +02:00
textboxtimer = 0 ;
2022-12-30 22:57:24 +01:00
textcase = 1 ;
2023-03-18 22:31:13 +01:00
textbuttons = false ;
2022-11-30 18:56:44 +01:00
textlarge = false ;
2023-08-06 00:34:15 +02:00
textbox_sprites . clear ( ) ;
2023-08-20 19:11:44 +02:00
textbox_image = TEXTIMAGE_NONE ;
2020-01-01 21:29:24 +01:00
}
2023-03-18 22:48:20 +01:00
void scriptclass : : add_default_colours ( void )
{
2023-03-18 23:11:49 +01:00
textbox_colours [ " player " ] = graphics . getRGB ( 164 , 164 , 255 ) ;
2023-03-18 22:48:20 +01:00
textbox_colours [ " cyan " ] = graphics . getRGB ( 164 , 164 , 255 ) ;
textbox_colours [ " red " ] = graphics . getRGB ( 255 , 60 , 60 ) ;
textbox_colours [ " green " ] = graphics . getRGB ( 144 , 255 , 144 ) ;
textbox_colours [ " yellow " ] = graphics . getRGB ( 255 , 255 , 134 ) ;
textbox_colours [ " blue " ] = graphics . getRGB ( 95 , 95 , 255 ) ;
textbox_colours [ " purple " ] = graphics . getRGB ( 255 , 134 , 255 ) ;
textbox_colours [ " white " ] = graphics . getRGB ( 244 , 244 , 244 ) ;
textbox_colours [ " gray " ] = graphics . getRGB ( 174 , 174 , 174 ) ;
textbox_colours [ " orange " ] = graphics . getRGB ( 255 , 130 , 20 ) ;
textbox_colours [ " transparent " ] = graphics . getRGB ( 0 , 0 , 0 ) ;
}
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 scriptclass : : clearcustom ( void )
2021-02-13 01:35:22 +01:00
{
2021-09-07 03:56:39 +02:00
customscripts . clear ( ) ;
2020-01-01 21:29:24 +01:00
}
2021-08-12 04:32:36 +02:00
static bool argexists [ NUM_SCRIPT_ARGS ] ;
2022-02-12 23:56:27 +01:00
static std : : string raw_words [ NUM_SCRIPT_ARGS ] ;
2021-08-12 04:32:36 +02:00
2020-07-05 01:01:35 +02:00
void scriptclass : : tokenize ( const std : : string & t )
2020-01-01 21:29:24 +01:00
{
2021-09-07 03:56:39 +02:00
j = 0 ;
std : : string tempword ;
2022-02-12 23:56:27 +01:00
std : : string temprawword ;
2021-09-07 03:56:39 +02:00
char currentletter ;
SDL_zeroa ( argexists ) ;
for ( size_t i = 0 ; i < t . length ( ) ; i + + )
{
currentletter = t [ i ] ;
if ( currentletter = = ' ( ' | | currentletter = = ' ) ' | | currentletter = = ' , ' )
{
words [ j ] = tempword ;
2022-02-12 23:56:27 +01:00
raw_words [ j ] = temprawword ;
2021-09-07 03:56:39 +02:00
argexists [ j ] = words [ j ] ! = " " ;
for ( size_t ii = 0 ; ii < words [ j ] . length ( ) ; ii + + )
{
words [ j ] [ ii ] = SDL_tolower ( words [ j ] [ ii ] ) ;
}
j + + ;
tempword = " " ;
2022-02-12 23:56:27 +01:00
temprawword = " " ;
2021-09-07 03:56:39 +02:00
}
else if ( currentletter = = ' ' )
{
2022-02-12 23:56:27 +01:00
/* Ignore spaces unless it's part of a script name. */
temprawword + = currentletter ;
2021-09-07 03:56:39 +02:00
}
else
{
tempword + = currentletter ;
2022-02-12 23:56:27 +01:00
temprawword + = currentletter ;
2021-09-07 03:56:39 +02:00
}
if ( j > = ( int ) SDL_arraysize ( words ) )
{
break ;
}
}
if ( j < ( int ) SDL_arraysize ( words ) )
{
const bool lastargexists = tempword ! = " " ;
if ( lastargexists )
{
words [ j ] = tempword ;
2022-02-12 23:56:27 +01:00
raw_words [ j ] = tempword ;
2021-09-07 03:56:39 +02:00
}
argexists [ j ] = lastargexists ;
}
2020-01-01 21:29:24 +01:00
}
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
static int getcolorfromname ( std : : string name )
{
2021-09-07 03:56:39 +02:00
if ( name = = " player " ) return CYAN ;
else if ( name = = " cyan " ) return CYAN ;
else if ( name = = " red " ) return RED ;
else if ( name = = " green " ) return GREEN ;
else if ( name = = " yellow " ) return YELLOW ;
else if ( name = = " blue " ) return BLUE ;
else if ( name = = " purple " ) return PURPLE ;
else if ( name = = " customcyan " ) return CYAN ;
else if ( name = = " gray " ) return GRAY ;
else if ( name = = " teleporter " ) return TELEPORTER ;
int color = help . Int ( name . c_str ( ) , - 1 ) ;
if ( color < 0 ) return - 1 ; // Not a number (or it's negative), so we give up
return color ; // Last effort to give a valid color, maybe they just input the color?
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
}
static int getcrewmanfromname ( std : : string name )
{
2021-09-07 03:56:39 +02:00
if ( name = = " player " ) return obj . getplayer ( ) ; // Return the player
int color = getcolorfromname ( name ) ; // Maybe they passed in a crewmate name, or an id?
if ( color = = - 1 ) return - 1 ; // ...Nope, return -1
return obj . getcrewman ( color ) ;
Refactor colors in internal commands
Originally this started as a "deduplicate a bunch of duplicated code in script commands" PR,
but as I was working on that, I discovered there's a lot more that needs to be done than
just deduplication.
Anything which needs a crewmate entity now calls `getcrewmanfromname(name)`, and anything which
just needs the crewmate's color calls `getcolorfromname(name)`. This was done to make sure that
everything works consistently and no copy/pasting is required. Next is the fallback; instead of
giving up and doing various things when it can't find a specific color, it now attempts to treat
the color name as an ID, and if it can't then it returns -1, where each individual command handles
that return value. This means we can keep around AEM -- a bug used in custom levels -- by not
doing anything with the return value if it's -1.
Also, for some reason, there were two `crewcolour` functions, so I stripped out the one in
entityclass and left (and modified) the one in the graphics class, since the graphics class also
has the `crewcolourreal` function.
2021-09-01 00:09:51 +02:00
}
2024-01-21 21:39:15 +01:00
/* Also used in gamestate 1001. */
void foundtrinket_textbox1 ( textboxclass * THIS ) ;
void foundtrinket_textbox2 ( textboxclass * THIS ) ;
2024-01-21 22:47:26 +01:00
static void foundlab_textbox1 ( textboxclass * THIS )
{
THIS - > lines . clear ( ) ;
THIS - > lines . push_back ( loc : : gettext ( " Congratulations! \n \n You have found the secret lab! " ) ) ;
THIS - > wrap ( 2 ) ;
THIS - > centertext ( ) ;
THIS - > pad ( 1 , 1 ) ;
}
static void foundlab_textbox2 ( textboxclass * THIS )
{
THIS - > lines . clear ( ) ;
THIS - > lines . push_back ( loc : : gettext ( " The secret lab is separate from the rest of the game. You can now come back here at any time by selecting the new SECRET LAB option in the play menu. " ) ) ;
THIS - > wrap ( 0 ) ;
}
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 scriptclass : : run ( void )
2020-01-01 21:29:24 +01:00
{
2021-09-07 03:56:39 +02:00
if ( ! running )
{
return ;
}
// This counter here will stop the function when it gets too high
short execution_counter = 0 ;
while ( running & & scriptdelay < = 0 & & ! game . pausescript )
{
if ( INBOUNDS_VEC ( position , commands ) )
{
//Let's split or command in an array of words
tokenize ( commands [ position ] ) ;
//For script assisted input
game . press_left = false ;
game . press_right = false ;
game . press_action = false ;
game . press_map = false ;
//Ok, now we run a command based on that string
if ( words [ 0 ] = = " moveplayer " )
{
//USAGE: moveplayer(x offset, y offset)
int player = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( player , obj . entities ) )
{
obj . entities [ player ] . xp + = ss_toi ( words [ 1 ] ) ;
obj . entities [ player ] . yp + = ss_toi ( words [ 2 ] ) ;
obj . entities [ player ] . lerpoldxp = obj . entities [ player ] . xp ;
obj . entities [ player ] . lerpoldyp = obj . entities [ player ] . yp ;
}
scriptdelay = 1 ;
}
2022-12-12 00:05:20 +01:00
if ( words [ 0 ] = = " setroomname " )
{
+ + position ;
if ( INBOUNDS_VEC ( position , commands ) )
{
map . roomname_special = true ;
map . roomnameset = true ;
map . setroomname ( commands [ position ] . c_str ( ) ) ;
}
}
2021-09-07 03:56:39 +02:00
if ( words [ 0 ] = = " warpdir " )
{
2023-01-07 19:28:07 +01:00
int temprx = ss_toi ( words [ 1 ] ) - 1 ;
int tempry = ss_toi ( words [ 2 ] ) - 1 ;
2021-09-07 03:56:39 +02:00
const RoomProperty * room ;
cl . setroomwarpdir ( temprx , tempry , ss_toi ( words [ 3 ] ) ) ;
room = cl . getroomprop ( temprx , tempry ) ;
//Do we update our own room?
2023-01-07 19:28:07 +01:00
if ( game . roomx - 100 = = temprx & & game . roomy - 100 = = tempry )
{
2021-09-07 03:56:39 +02:00
//If screen warping, then override all that:
graphics . backgrounddrawn = false ;
2023-01-07 19:28:07 +01:00
map . warpx = false ;
map . warpy = false ;
if ( room - > warpdir = = 0 )
{
2021-09-07 03:56:39 +02:00
map . background = 1 ;
//Be careful, we could be in a Lab or Warp Zone room...
2023-01-07 19:28:07 +01:00
if ( room - > tileset = = 2 )
{
2021-09-07 03:56:39 +02:00
//Lab
map . background = 2 ;
graphics . rcol = room - > tilecol ;
2023-01-07 19:28:07 +01:00
}
else if ( room - > tileset = = 3 )
{
2021-09-07 03:56:39 +02:00
//Warp Zone
map . background = 6 ;
}
2023-01-07 19:28:07 +01:00
}
else if ( room - > warpdir = = 1 )
{
map . warpx = true ;
map . background = 3 ;
graphics . rcol = cl . getwarpbackground ( temprx , tempry ) ;
}
else if ( room - > warpdir = = 2 )
{
map . warpy = true ;
map . background = 4 ;
graphics . rcol = cl . getwarpbackground ( temprx , tempry ) ;
}
else if ( room - > warpdir = = 3 )
{
map . warpx = true ;
map . warpy = true ;
2021-09-07 03:56:39 +02:00
map . background = 5 ;
2023-01-07 19:28:07 +01:00
graphics . rcol = cl . getwarpbackground ( temprx , tempry ) ;
2021-09-07 03:56:39 +02:00
}
}
}
if ( words [ 0 ] = = " ifwarp " )
{
const RoomProperty * const room = cl . getroomprop ( ss_toi ( words [ 1 ] ) - 1 , ss_toi ( words [ 2 ] ) - 1 ) ;
if ( room - > warpdir = = ss_toi ( words [ 3 ] ) )
{
2023-02-01 05:04:18 +01:00
loadalts ( " custom_ " + words [ 4 ] , " custom_ " + raw_words [ 4 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
if ( words [ 0 ] = = " destroy " )
{
2023-09-04 05:34:20 +02:00
if ( words [ 1 ] = = " gravitylines " )
{
for ( size_t edi = 0 ; edi < obj . entities . size ( ) ; edi + + )
{
if ( obj . entities [ edi ] . type = = 9 | | obj . entities [ edi ] . type = = 10 )
{
obj . disableentity ( edi ) ;
}
2021-09-07 03:56:39 +02:00
}
2023-09-04 05:34:20 +02:00
}
else if ( words [ 1 ] = = " warptokens " )
{
2023-09-10 02:54:42 +02:00
for ( size_t edi = 0 ; edi < obj . entities . size ( ) ; edi + + )
{
2023-09-04 05:34:20 +02:00
if ( obj . entities [ edi ] . type = = 11 )
{
obj . disableentity ( edi ) ;
}
2021-09-07 03:56:39 +02:00
}
2023-09-04 05:34:20 +02:00
}
else if ( words [ 1 ] = = " platforms " | | words [ 1 ] = = " moving " )
{
2023-09-10 03:13:26 +02:00
const bool fixed = words [ 1 ] = = " moving " ;
2023-09-04 05:34:20 +02:00
for ( size_t edi = 0 ; edi < obj . entities . size ( ) ; edi + + )
{
if ( obj . entities [ edi ] . rule = = 2 & & obj . entities [ edi ] . animate = = 100 )
{
2023-09-10 03:13:26 +02:00
if ( fixed )
2023-09-04 05:34:20 +02:00
{
obj . disableblockat ( obj . entities [ edi ] . xp , obj . entities [ edi ] . yp ) ;
}
obj . disableentity ( edi ) ;
}
2021-09-07 03:56:39 +02:00
}
2023-09-04 05:34:20 +02:00
}
else if ( words [ 1 ] = = " disappear " )
{
for ( size_t edi = 0 ; edi < obj . entities . size ( ) ; edi + + )
{
2021-09-07 03:56:39 +02:00
obj . disableblockat ( obj . entities [ edi ] . xp , obj . entities [ edi ] . yp ) ;
2023-09-04 05:34:20 +02:00
if ( obj . entities [ edi ] . type = = 2 & & obj . entities [ edi ] . rule = = 3 )
{
obj . disableentity ( edi ) ;
}
2021-09-07 03:56:39 +02:00
}
}
}
if ( words [ 0 ] = = " customiftrinkets " )
{
if ( game . trinkets ( ) > = ss_toi ( words [ 1 ] ) )
{
2023-02-01 05:04:18 +01:00
loadalts ( " custom_ " + words [ 2 ] , " custom_ " + raw_words [ 2 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
if ( words [ 0 ] = = " customiftrinketsless " )
{
if ( game . trinkets ( ) < ss_toi ( words [ 1 ] ) )
{
2023-02-01 05:04:18 +01:00
loadalts ( " custom_ " + words [ 2 ] , " custom_ " + raw_words [ 2 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
else if ( words [ 0 ] = = " customifflag " )
{
int flag = ss_toi ( words [ 1 ] ) ;
if ( INBOUNDS_ARR ( flag , obj . flags ) & & obj . flags [ flag ] )
{
2023-02-01 05:04:18 +01:00
loadalts ( " custom_ " + words [ 2 ] , " custom_ " + raw_words [ 2 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
if ( words [ 0 ] = = " custommap " )
{
if ( words [ 1 ] = = " on " ) {
map . customshowmm = true ;
} else if ( words [ 1 ] = = " off " ) {
map . customshowmm = false ;
}
}
if ( words [ 0 ] = = " delay " )
{
//USAGE: delay(frames)
scriptdelay = ss_toi ( words [ 1 ] ) ;
}
if ( words [ 0 ] = = " flag " )
{
int flag = ss_toi ( words [ 1 ] ) ;
if ( INBOUNDS_ARR ( flag , obj . flags ) )
{
if ( words [ 2 ] = = " on " )
{
obj . flags [ flag ] = true ;
}
else if ( words [ 2 ] = = " off " )
{
obj . flags [ flag ] = false ;
}
}
}
if ( words [ 0 ] = = " flash " )
{
//USAGE: flash(frames)
game . flashlight = ss_toi ( words [ 1 ] ) ;
}
if ( words [ 0 ] = = " shake " )
{
//USAGE: shake(frames)
game . screenshake = ss_toi ( words [ 1 ] ) ;
}
if ( words [ 0 ] = = " walk " )
{
//USAGE: walk(dir,frames)
if ( words [ 1 ] = = " left " )
{
game . press_left = true ;
}
else if ( words [ 1 ] = = " right " )
{
game . press_right = true ;
}
scriptdelay = ss_toi ( words [ 2 ] ) ;
}
if ( words [ 0 ] = = " flip " )
{
game . press_action = true ;
scriptdelay = 1 ;
}
if ( words [ 0 ] = = " tofloor " )
{
int player = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( player , obj . entities ) & & obj . entities [ player ] . onroof > 0 )
{
game . press_action = true ;
scriptdelay = 1 ;
}
}
if ( words [ 0 ] = = " playef " )
{
music . playef ( ss_toi ( words [ 1 ] ) ) ;
}
if ( words [ 0 ] = = " play " )
{
music . play ( ss_toi ( words [ 1 ] ) ) ;
}
if ( words [ 0 ] = = " stopmusic " )
{
music . haltdasmusik ( ) ;
}
if ( words [ 0 ] = = " resumemusic " )
{
music . resumefade ( 0 ) ;
}
if ( words [ 0 ] = = " musicfadeout " )
{
music . fadeout ( false ) ;
}
if ( words [ 0 ] = = " musicfadein " )
{
music . fadein ( ) ;
}
if ( words [ 0 ] = = " trinketscriptmusic " )
{
2023-05-24 03:37:32 +02:00
music . play ( Music_PASSIONFOREXPLORING ) ;
2021-09-07 03:56:39 +02:00
}
if ( words [ 0 ] = = " gotoposition " )
{
//USAGE: gotoposition(x position, y position, gravity position)
int player = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( player , obj . entities ) )
{
obj . entities [ player ] . xp = ss_toi ( words [ 1 ] ) ;
obj . entities [ player ] . yp = ss_toi ( words [ 2 ] ) ;
obj . entities [ player ] . lerpoldxp = obj . entities [ player ] . xp ;
obj . entities [ player ] . lerpoldyp = obj . entities [ player ] . yp ;
}
game . gravitycontrol = ss_toi ( words [ 3 ] ) ;
}
if ( words [ 0 ] = = " gotoroom " )
{
//USAGE: gotoroom(x,y) (manually add 100)
map . gotoroom ( ss_toi ( words [ 1 ] ) + 100 , ss_toi ( words [ 2 ] ) + 100 ) ;
}
if ( words [ 0 ] = = " cutscene " )
{
graphics . showcutscenebars = true ;
}
if ( words [ 0 ] = = " endcutscene " )
{
graphics . showcutscenebars = false ;
}
if ( words [ 0 ] = = " audiopause " )
{
if ( words [ 1 ] = = " on " )
{
game . disabletemporaryaudiopause = false ;
}
else if ( words [ 1 ] = = " off " )
{
game . disabletemporaryaudiopause = true ;
}
}
if ( words [ 0 ] = = " untilbars " )
{
if ( graphics . showcutscenebars )
{
if ( graphics . cutscenebarspos < 360 )
{
scriptdelay = 1 ;
position - - ;
}
}
else
{
if ( graphics . cutscenebarspos > 0 )
{
scriptdelay = 1 ;
position - - ;
}
}
}
else if ( words [ 0 ] = = " text " )
{
2023-03-18 22:48:20 +01:00
// oh boy
// first word is the colour.
if ( textbox_colours . count ( words [ 1 ] ) = = 0 )
2021-09-07 03:56:39 +02:00
{
2023-03-18 22:48:20 +01:00
// No colour named this, use gray
words [ 1 ] = " gray " ;
2021-09-07 03:56:39 +02:00
}
2023-03-18 22:48:20 +01:00
r = textbox_colours [ words [ 1 ] ] . r ;
g = textbox_colours [ words [ 1 ] ] . g ;
b = textbox_colours [ words [ 1 ] ] . b ;
2021-09-07 03:56:39 +02:00
//next are the x,y coordinates
textx = ss_toi ( words [ 2 ] ) ;
texty = ss_toi ( words [ 3 ] ) ;
2023-02-14 23:30:06 +01:00
textlarge = endsWith ( words [ 4 ] . c_str ( ) , " l " ) | | endsWith ( words [ 4 ] . c_str ( ) , " L " ) ;
int lines = ss_toi ( words [ 4 ] ) ;
2022-11-30 18:56:44 +01:00
2021-09-07 03:56:39 +02:00
//Number of lines for the textbox!
txt . clear ( ) ;
2022-11-30 18:56:44 +01:00
for ( int i = 0 ; i < lines ; i + + )
2021-09-07 03:56:39 +02:00
{
position + + ;
if ( INBOUNDS_VEC ( position , commands ) )
{
txt . push_back ( commands [ position ] ) ;
}
}
2022-12-30 22:57:24 +01:00
2023-04-20 14:53:30 +02:00
textboxtimer = 0 ;
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
textcrewmateposition = TextboxCrewmatePosition ( ) ;
2023-08-06 00:34:15 +02:00
textbox_sprites . clear ( ) ;
2023-08-20 19:11:44 +02:00
textbox_image = TEXTIMAGE_NONE ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 0 ] = = " position " )
{
//are we facing left or right? for some objects we don't care, default at 0.
j = 0 ;
//the first word is the object to position relative to
if ( words [ 1 ] = = " centerx " )
{
words [ 2 ] = " donothing " ;
j = - 1 ;
textx = - 500 ;
}
else if ( words [ 1 ] = = " centery " )
{
words [ 2 ] = " donothing " ;
j = - 1 ;
texty = - 500 ;
}
else if ( words [ 1 ] = = " center " )
{
words [ 2 ] = " donothing " ;
j = - 1 ;
textx = - 500 ;
texty = - 500 ;
}
else // Well, are they asking for a crewmate...?
{
i = getcrewmanfromname ( words [ 1 ] ) ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
j = obj . entities [ i ] . dir ;
}
}
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
if ( INBOUNDS_VEC ( i , obj . entities ) & & ( j = = 0 | | j = = 1 ) )
2021-09-07 03:56:39 +02:00
{
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
if ( words [ 2 ] = = " above " )
2021-09-07 03:56:39 +02:00
{
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
textcrewmateposition . text_above = true ;
2021-09-07 03:56:39 +02:00
}
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
2024-01-21 02:02:35 +01:00
textx = 0 ;
texty = 0 ;
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
textcrewmateposition . x = obj . entities [ i ] . xp ;
textcrewmateposition . override_x = true ;
textcrewmateposition . y = obj . entities [ i ] . yp ;
textcrewmateposition . override_y = true ;
textcrewmateposition . dir = j ;
2021-09-07 03:56:39 +02:00
}
}
else if ( words [ 0 ] = = " customposition " )
{
//are we facing left or right? for some objects we don't care, default at 0.
j = 0 ;
//the first word is the object to position relative to
if ( words [ 1 ] = = " player " )
{
i = obj . getcustomcrewman ( 0 ) ;
j = obj . entities [ i ] . dir ;
}
else if ( words [ 1 ] = = " cyan " )
{
i = obj . getcustomcrewman ( 0 ) ;
j = obj . entities [ i ] . dir ;
}
else if ( words [ 1 ] = = " purple " )
{
i = obj . getcustomcrewman ( 1 ) ;
j = obj . entities [ i ] . dir ;
}
else if ( words [ 1 ] = = " yellow " )
{
i = obj . getcustomcrewman ( 2 ) ;
j = obj . entities [ i ] . dir ;
}
else if ( words [ 1 ] = = " red " )
{
i = obj . getcustomcrewman ( 3 ) ;
j = obj . entities [ i ] . dir ;
}
else if ( words [ 1 ] = = " green " )
{
i = obj . getcustomcrewman ( 4 ) ;
j = obj . entities [ i ] . dir ;
}
else if ( words [ 1 ] = = " blue " )
{
i = obj . getcustomcrewman ( 5 ) ;
j = obj . entities [ i ] . dir ;
}
else if ( words [ 1 ] = = " centerx " )
{
words [ 2 ] = " donothing " ;
j = - 1 ;
textx = - 500 ;
}
else if ( words [ 1 ] = = " centery " )
{
words [ 2 ] = " donothing " ;
j = - 1 ;
texty = - 500 ;
}
else if ( words [ 1 ] = = " center " )
{
words [ 2 ] = " donothing " ;
j = - 1 ;
textx = - 500 ;
texty = - 500 ;
}
if ( i = = 0 & & words [ 1 ] ! = " player " & & words [ 1 ] ! = " cyan " ) {
//Requested crewmate is not actually on screen
words [ 2 ] = " donothing " ;
j = - 1 ;
textx = - 500 ;
texty = - 500 ;
}
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
if ( INBOUNDS_VEC ( i , obj . entities ) & & ( j = = 0 | | j = = 1 ) )
2021-09-07 03:56:39 +02:00
{
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
if ( words [ 2 ] = = " above " )
2021-09-07 03:56:39 +02:00
{
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
textcrewmateposition . text_above = true ;
2021-09-07 03:56:39 +02:00
}
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
2024-01-21 02:02:35 +01:00
textx = 0 ;
texty = 0 ;
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
textcrewmateposition . x = obj . entities [ i ] . xp ;
textcrewmateposition . override_x = true ;
2024-03-30 03:55:41 +01:00
textcrewmateposition . y = obj . entities [ i ] . yp ;
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
textcrewmateposition . override_y = true ;
textcrewmateposition . dir = j ;
2021-09-07 03:56:39 +02:00
}
}
else if ( words [ 0 ] = = " backgroundtext " )
{
game . backgroundtext = true ;
}
2023-04-20 14:53:30 +02:00
else if ( words [ 0 ] = = " textboxtimer " )
{
textboxtimer = ss_toi ( words [ 1 ] ) ;
}
2023-08-06 00:34:15 +02:00
else if ( words [ 0 ] = = " textsprite " )
{
TextboxSprite sprite ;
sprite . x = ss_toi ( words [ 1 ] ) ;
sprite . y = ss_toi ( words [ 2 ] ) ;
sprite . tile = ss_toi ( words [ 3 ] ) ;
sprite . col = ss_toi ( words [ 4 ] ) ;
textbox_sprites . push_back ( sprite ) ;
}
2023-08-20 19:11:44 +02:00
else if ( words [ 0 ] = = " textimage " )
{
if ( words [ 1 ] = = " levelcomplete " )
{
textbox_image = TEXTIMAGE_LEVELCOMPLETE ;
}
else if ( words [ 1 ] = = " gamecomplete " )
{
textbox_image = TEXTIMAGE_GAMECOMPLETE ;
}
else
{
textbox_image = TEXTIMAGE_NONE ;
}
}
2021-09-07 03:56:39 +02:00
else if ( words [ 0 ] = = " flipme " )
{
textflipme = ! textflipme ;
}
else if ( words [ 0 ] = = " speak_active " | | words [ 0 ] = = " speak " )
{
//Ok, actually display the textbox we've initilised now!
//If using "speak", don't make the textbox active (so we can use multiple textboxes)
if ( txt . empty ( ) )
{
txt . resize ( 1 ) ;
}
graphics . createtextboxreal ( txt [ 0 ] , textx , texty , r , g , b , textflipme ) ;
textflipme = false ;
2022-11-30 18:56:44 +01:00
graphics . setlarge ( textlarge ) ;
textlarge = false ;
2021-09-07 03:56:39 +02:00
if ( ( int ) txt . size ( ) > 1 )
{
for ( i = 1 ; i < ( int ) txt . size ( ) ; i + + )
{
graphics . addline ( txt [ i ] ) ;
}
}
2023-04-20 14:53:30 +02:00
if ( textboxtimer > 0 )
{
graphics . textboxtimer ( textboxtimer ) ;
}
2023-08-06 00:34:15 +02:00
for ( size_t i = 0 ; i < textbox_sprites . size ( ) ; i + + )
{
graphics . addsprite ( textbox_sprites [ i ] . x , textbox_sprites [ i ] . y , textbox_sprites [ i ] . tile , textbox_sprites [ i ] . col ) ;
}
2023-08-20 19:11:44 +02:00
graphics . setimage ( textbox_image ) ;
2021-09-07 03:56:39 +02:00
if ( textx = = - 500 | | textx = = - 1 )
{
graphics . textboxcenterx ( ) ;
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
textcrewmateposition . override_x = false ;
2021-09-07 03:56:39 +02:00
}
if ( texty = = - 500 )
{
graphics . textboxcentery ( ) ;
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
textcrewmateposition . override_y = false ;
2021-09-07 03:56:39 +02:00
}
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
TextboxOriginalContext context = TextboxOriginalContext ( ) ;
context . text_case = textcase ;
context . lines = std : : vector < std : : string > ( txt ) ;
context . script_name = scriptname ;
2024-01-21 23:56:08 +01:00
context . x = textx ;
context . y = texty ;
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
graphics . textboxcrewmateposition ( & textcrewmateposition ) ;
graphics . textboxoriginalcontext ( & context ) ;
graphics . textboxcase ( textcase ) ;
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
if ( map . custommode )
{
uint32_t flags = PR_FONT_IDX ( font : : font_idx_level , cl . rtl ) ;
if ( font : : font_idx_level_is_custom )
{
flags | = PR_FONT_IDX_IS_CUSTOM ;
}
graphics . textboxprintflags ( flags ) ;
}
Save special text box state using functions
This adds a way to save the text box state of the crew remaining, ACTION
prompt, etc. text boxes by just letting there be a function that is
called to retranslate the text box when needed.
It also adds a way to ignore translating a text box and to leave it
alone, in case there's actually no text in the text box, which is the
case with Level Complete and Game Complete.
Both ways are now in an enum, TextboxTranslate. The former is
TEXTTRANSLATE_FUNCTION and the latter is TEXTTRANSLATE_NONE. The
existing way of translating text boxes became TEXTTRANSLATE_CUTSCENE,
since it's only used for cutscene scripts.
Here's a quick guide to the three ways of creating a text box now.
- TEXTTRANSLATE_NONE: You must call
graphics.textboxoriginalcontextauto() to save the existing text to the
original context of the text box, as that will be copied back to the
text box after the text of the text box is updated due to not having a
translation.
- TEXTTRANSLATE_CUTSCENE: Translates the text from cutscenes.xml, and
overrides the spacing (padding and text centering). Shouldn't need to
be used outside of scriptclass.
- TEXTTRANSLATE_FUNCTION: You must pass in a function that takes in a
single parameter, a pointer to the textboxclass object to be modified.
General advice when retranslating text is to clear the `lines` vector
and then push_back the retranslated text. The function is also solely
responsible for spacing.
In most cases, you will also need to call
graphics.textboxapplyposition() or graphics.textboxadjust() afterwards.
(Some text boxes shouldn't use graphics.textboxadjust() as they are
within the 10-pixel inner border around the screen that
textboxclass::adjust tries to push the text box out of.)
This commit doesn't fix every text box just yet, though. But it fixes
the Level Complete, Game Complete, crew remaining, and ACTION prompt
text boxes, for a start.
2024-01-21 05:27:31 +01:00
graphics . textboxtranslate ( TEXTTRANSLATE_CUTSCENE , NULL ) ;
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
2024-01-21 19:48:39 +01:00
graphics . textboxapplyposition ( ) ;
2021-09-07 03:56:39 +02:00
if ( words [ 0 ] = = " speak_active " )
{
graphics . textboxactive ( ) ;
}
if ( ! game . backgroundtext )
{
game . advancetext = true ;
game . hascontrol = false ;
game . pausescript = true ;
if ( key . isDown ( 90 ) | | key . isDown ( 32 ) | | key . isDown ( 86 )
| | key . isDown ( KEYBOARD_UP ) | | key . isDown ( KEYBOARD_DOWN ) ) game . jumpheld = true ;
}
game . backgroundtext = false ;
2023-03-18 22:31:13 +01:00
if ( textbuttons )
{
graphics . textboxbuttons ( ) ;
}
textbuttons = false ;
2024-01-22 01:49:06 +01:00
textcase = 1 ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 0 ] = = " endtext " )
{
graphics . textboxremove ( ) ;
game . hascontrol = true ;
game . advancetext = false ;
}
else if ( words [ 0 ] = = " endtextfast " )
{
graphics . textboxremovefast ( ) ;
game . hascontrol = true ;
game . advancetext = false ;
}
else if ( words [ 0 ] = = " do " )
{
//right, loop from this point
looppoint = position ;
loopcount = ss_toi ( words [ 1 ] ) ;
}
else if ( words [ 0 ] = = " loop " )
{
//right, loop from this point
loopcount - - ;
if ( loopcount > 0 )
{
position = looppoint ;
}
}
else if ( words [ 0 ] = = " vvvvvvman " )
{
//Create the super VVVVVV combo!
i = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . xp = 30 ;
obj . entities [ i ] . yp = 46 ;
2023-04-06 04:51:58 +02:00
obj . entities [ i ] . lerpoldxp = obj . entities [ i ] . xp ;
obj . entities [ i ] . lerpoldyp = obj . entities [ i ] . yp ;
2021-09-07 03:56:39 +02:00
obj . entities [ i ] . size = 13 ;
obj . entities [ i ] . colour = 23 ;
obj . entities [ i ] . cx = 36 ; // 6;
obj . entities [ i ] . cy = 12 + 80 ; // 2;
obj . entities [ i ] . h = 126 - 80 ; // 21;
}
}
else if ( words [ 0 ] = = " undovvvvvvman " )
{
//Create the super VVVVVV combo!
i = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . xp = 100 ;
2023-04-06 04:51:58 +02:00
obj . entities [ i ] . lerpoldxp = obj . entities [ i ] . xp ;
2021-09-07 03:56:39 +02:00
obj . entities [ i ] . size = 0 ;
obj . entities [ i ] . colour = 0 ;
obj . entities [ i ] . cx = 6 ;
obj . entities [ i ] . cy = 2 ;
obj . entities [ i ] . h = 21 ;
}
}
else if ( words [ 0 ] = = " createentity " )
{
std : : string word6 = words [ 6 ] ;
std : : string word7 = words [ 7 ] ;
std : : string word8 = words [ 8 ] ;
std : : string word9 = words [ 9 ] ;
if ( ! argexists [ 6 ] ) words [ 6 ] = " 0 " ;
if ( ! argexists [ 7 ] ) words [ 7 ] = " 0 " ;
if ( ! argexists [ 8 ] ) words [ 8 ] = " 320 " ;
if ( ! argexists [ 9 ] ) words [ 9 ] = " 240 " ;
obj . createentity (
ss_toi ( words [ 1 ] ) ,
ss_toi ( words [ 2 ] ) ,
ss_toi ( words [ 3 ] ) ,
ss_toi ( words [ 4 ] ) ,
ss_toi ( words [ 5 ] ) ,
ss_toi ( words [ 6 ] ) ,
ss_toi ( words [ 7 ] ) ,
ss_toi ( words [ 8 ] ) ,
ss_toi ( words [ 9 ] )
) ;
words [ 6 ] = word6 ;
words [ 7 ] = word7 ;
words [ 8 ] = word8 ;
words [ 9 ] = word9 ;
}
else if ( words [ 0 ] = = " createcrewman " )
{
// Note: Do not change the "r" variable, it's used in custom levels
// to have glitchy textbox colors, where the game treats the value
// we set here as the red channel for the color.
r = getcolorfromname ( words [ 3 ] ) ;
if ( r = = - 1 ) r = 19 ;
//convert the command to the right index
if ( words [ 5 ] = = " followplayer " ) words [ 5 ] = " 10 " ;
if ( words [ 5 ] = = " followpurple " ) words [ 5 ] = " 11 " ;
if ( words [ 5 ] = = " followyellow " ) words [ 5 ] = " 12 " ;
if ( words [ 5 ] = = " followred " ) words [ 5 ] = " 13 " ;
if ( words [ 5 ] = = " followgreen " ) words [ 5 ] = " 14 " ;
if ( words [ 5 ] = = " followblue " ) words [ 5 ] = " 15 " ;
if ( words [ 5 ] = = " followposition " ) words [ 5 ] = " 16 " ;
if ( words [ 5 ] = = " faceleft " )
{
words [ 5 ] = " 17 " ;
words [ 6 ] = " 0 " ;
}
if ( words [ 5 ] = = " faceright " )
{
words [ 5 ] = " 17 " ;
words [ 6 ] = " 1 " ;
}
if ( words [ 5 ] = = " faceplayer " )
{
words [ 5 ] = " 18 " ;
words [ 6 ] = " 0 " ;
}
if ( words [ 5 ] = = " panic " )
{
words [ 5 ] = " 20 " ;
words [ 6 ] = " 0 " ;
}
if ( ss_toi ( words [ 5 ] ) > = 16 )
{
obj . createentity ( ss_toi ( words [ 1 ] ) , ss_toi ( words [ 2 ] ) , 18 , r , ss_toi ( words [ 4 ] ) , ss_toi ( words [ 5 ] ) , ss_toi ( words [ 6 ] ) ) ;
}
else
{
obj . createentity ( ss_toi ( words [ 1 ] ) , ss_toi ( words [ 2 ] ) , 18 , r , ss_toi ( words [ 4 ] ) , ss_toi ( words [ 5 ] ) ) ;
}
}
else if ( words [ 0 ] = = " changemood " )
{
int crewmate = getcrewmanfromname ( words [ 1 ] ) ;
if ( crewmate ! = - 1 ) i = crewmate ; // Ensure AEM is kept
if ( INBOUNDS_VEC ( i , obj . entities ) & & ss_toi ( words [ 2 ] ) = = 0 )
{
obj . entities [ i ] . tile = 0 ;
}
else if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . tile = 144 ;
}
}
else if ( words [ 0 ] = = " changecustommood " )
{
if ( words [ 1 ] = = " player " )
{
i = obj . getcustomcrewman ( 0 ) ;
obj . customcrewmoods [ 0 ] = ss_toi ( words [ 2 ] ) ;
}
else if ( words [ 1 ] = = " cyan " )
{
i = obj . getcustomcrewman ( 0 ) ;
obj . customcrewmoods [ 0 ] = ss_toi ( words [ 2 ] ) ;
}
else if ( words [ 1 ] = = " customcyan " )
{
i = obj . getcustomcrewman ( 0 ) ;
obj . customcrewmoods [ 0 ] = ss_toi ( words [ 2 ] ) ;
}
else if ( words [ 1 ] = = " red " )
{
i = obj . getcustomcrewman ( 3 ) ;
obj . customcrewmoods [ 3 ] = ss_toi ( words [ 2 ] ) ;
}
else if ( words [ 1 ] = = " green " )
{
i = obj . getcustomcrewman ( 4 ) ;
obj . customcrewmoods [ 4 ] = ss_toi ( words [ 2 ] ) ;
}
else if ( words [ 1 ] = = " yellow " )
{
i = obj . getcustomcrewman ( 2 ) ;
obj . customcrewmoods [ 2 ] = ss_toi ( words [ 2 ] ) ;
}
else if ( words [ 1 ] = = " blue " )
{
i = obj . getcustomcrewman ( 5 ) ;
obj . customcrewmoods [ 5 ] = ss_toi ( words [ 2 ] ) ;
}
else if ( words [ 1 ] = = " purple " )
{
i = obj . getcustomcrewman ( 1 ) ;
obj . customcrewmoods [ 1 ] = ss_toi ( words [ 2 ] ) ;
}
else if ( words [ 1 ] = = " pink " )
{
i = obj . getcustomcrewman ( 1 ) ;
obj . customcrewmoods [ 1 ] = ss_toi ( words [ 2 ] ) ;
}
if ( INBOUNDS_VEC ( i , obj . entities ) & & ss_toi ( words [ 2 ] ) = = 0 )
{
obj . entities [ i ] . tile = 0 ;
}
else if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . tile = 144 ;
}
}
else if ( words [ 0 ] = = " changetile " )
{
int crewmate = getcrewmanfromname ( words [ 1 ] ) ;
if ( crewmate ! = - 1 ) i = crewmate ; // Ensure AEM is kept
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . tile = ss_toi ( words [ 2 ] ) ;
}
}
else if ( words [ 0 ] = = " flipgravity " )
{
//not something I'll use a lot, I think. Doesn't need to be very robust!
if ( words [ 1 ] = = " player " )
{
game . gravitycontrol = ! game . gravitycontrol ;
+ + game . totalflips ;
}
else
{
int crewmate = getcrewmanfromname ( words [ 1 ] ) ;
if ( crewmate ! = - 1 ) i = crewmate ; // Ensure AEM is kept
if ( INBOUNDS_VEC ( i , obj . entities ) & & obj . entities [ i ] . rule = = 7 )
{
obj . entities [ i ] . rule = 6 ;
obj . entities [ i ] . tile = 0 ;
}
else if ( INBOUNDS_VEC ( i , obj . entities ) & & obj . getplayer ( ) ! = i ) // Don't destroy player entity
{
obj . entities [ i ] . rule = 7 ;
obj . entities [ i ] . tile = 6 ;
}
}
}
else if ( words [ 0 ] = = " changegravity " )
{
//not something I'll use a lot, I think. Doesn't need to be very robust!
int crewmate = getcrewmanfromname ( words [ 1 ] ) ;
if ( crewmate ! = - 1 ) i = crewmate ; // Ensure AEM is kept
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . tile + = 12 ;
}
}
else if ( words [ 0 ] = = " changedir " )
{
int crewmate = getcrewmanfromname ( words [ 1 ] ) ;
if ( crewmate ! = - 1 ) i = crewmate ; // Ensure AEM is kept
if ( INBOUNDS_VEC ( i , obj . entities ) & & ss_toi ( words [ 2 ] ) = = 0 )
{
obj . entities [ i ] . dir = 0 ;
}
else if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . dir = 1 ;
}
}
else if ( words [ 0 ] = = " alarmon " )
{
game . alarmon = true ;
game . alarmdelay = 0 ;
}
else if ( words [ 0 ] = = " alarmoff " )
{
game . alarmon = false ;
}
else if ( words [ 0 ] = = " changeai " )
{
int crewmate = getcrewmanfromname ( words [ 1 ] ) ;
if ( crewmate ! = - 1 ) i = crewmate ; // Ensure AEM is kept
if ( words [ 2 ] = = " followplayer " ) words [ 2 ] = " 10 " ;
if ( words [ 2 ] = = " followpurple " ) words [ 2 ] = " 11 " ;
if ( words [ 2 ] = = " followyellow " ) words [ 2 ] = " 12 " ;
if ( words [ 2 ] = = " followred " ) words [ 2 ] = " 13 " ;
if ( words [ 2 ] = = " followgreen " ) words [ 2 ] = " 14 " ;
if ( words [ 2 ] = = " followblue " ) words [ 2 ] = " 15 " ;
if ( words [ 2 ] = = " followposition " ) words [ 2 ] = " 16 " ;
if ( words [ 2 ] = = " faceleft " )
{
words [ 2 ] = " 17 " ;
words [ 3 ] = " 0 " ;
}
if ( words [ 2 ] = = " faceright " )
{
words [ 2 ] = " 17 " ;
words [ 3 ] = " 1 " ;
}
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . state = ss_toi ( words [ 2 ] ) ;
if ( obj . entities [ i ] . state = = 16 )
{
obj . entities [ i ] . para = ss_toi ( words [ 3 ] ) ;
}
else if ( obj . entities [ i ] . state = = 17 )
{
obj . entities [ i ] . dir = ss_toi ( words [ 3 ] ) ;
}
}
}
else if ( words [ 0 ] = = " activateteleporter " )
{
i = obj . getteleporter ( ) ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . tile = 6 ;
obj . entities [ i ] . colour = 102 ;
}
}
else if ( words [ 0 ] = = " changecolour " )
{
int crewmate = getcrewmanfromname ( words [ 1 ] ) ;
if ( crewmate ! = - 1 ) i = crewmate ; // Ensure AEM is kept
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . colour = getcolorfromname ( words [ 2 ] ) ;
}
}
else if ( words [ 0 ] = = " squeak " )
{
if ( words [ 1 ] = = " player " )
{
2023-06-04 00:29:02 +02:00
music . playef ( Sound_VIRIDIAN ) ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 1 ] = = " cyan " )
{
2023-06-04 00:29:02 +02:00
music . playef ( Sound_VIRIDIAN ) ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 1 ] = = " red " )
{
2023-06-04 00:29:02 +02:00
music . playef ( Sound_VERMILION ) ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 1 ] = = " green " )
{
2023-06-04 00:29:02 +02:00
music . playef ( Sound_VERDIGRIS ) ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 1 ] = = " yellow " )
{
2023-06-04 00:29:02 +02:00
music . playef ( Sound_VITELLARY ) ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 1 ] = = " blue " )
{
2023-06-04 00:29:02 +02:00
music . playef ( Sound_VICTORIA ) ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 1 ] = = " purple " )
{
2023-06-04 00:29:02 +02:00
music . playef ( Sound_VIOLET ) ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 1 ] = = " cry " )
{
2023-06-04 00:29:02 +02:00
music . playef ( Sound_CRY ) ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 1 ] = = " terminal " )
{
2023-06-04 00:29:02 +02:00
music . playef ( Sound_TERMINALTEXT ) ;
2021-09-07 03:56:39 +02:00
}
}
else if ( words [ 0 ] = = " blackout " )
{
game . blackout = true ;
}
else if ( words [ 0 ] = = " blackon " )
{
game . blackout = false ;
}
else if ( words [ 0 ] = = " setcheckpoint " )
{
i = obj . getplayer ( ) ;
game . savepoint = 0 ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
game . savex = obj . entities [ i ] . xp ;
game . savey = obj . entities [ i ] . yp ;
}
game . savegc = game . gravitycontrol ;
game . saverx = game . roomx ;
game . savery = game . roomy ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
game . savedir = obj . entities [ i ] . dir ;
}
2024-06-05 14:17:04 +02:00
if ( game . checkpoint_saving )
{
bool success = game . savequick ( ) ;
game . gamesaved = success ;
game . gamesavefailed = ! success ;
}
2021-09-07 03:56:39 +02:00
}
else if ( words [ 0 ] = = " gamestate " )
{
2022-12-07 18:32:28 +01:00
// Allow the gamestate command to bypass statelock, at least for now
2021-09-07 03:56:39 +02:00
game . state = ss_toi ( words [ 1 ] ) ;
game . statedelay = 0 ;
}
else if ( words [ 0 ] = = " textboxactive " )
{
graphics . textboxactive ( ) ;
}
else if ( words [ 0 ] = = " gamemode " )
{
if ( words [ 1 ] = = " teleporter " )
{
game . gamestate = GAMEMODE ; /* to set prevgamestate */
game . mapmenuchange ( TELEPORTERMODE , false ) ;
game . useteleporter = false ; //good heavens don't actually use it
}
else if ( words [ 1 ] = = " game " )
{
graphics . resumegamemode = true ;
game . prevgamestate = GAMEMODE ;
}
}
else if ( words [ 0 ] = = " ifexplored " )
{
if ( map . isexplored ( ss_toi ( words [ 1 ] ) , ss_toi ( words [ 2 ] ) ) )
{
2023-02-01 05:04:18 +01:00
loadalts ( words [ 3 ] , raw_words [ 3 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
else if ( words [ 0 ] = = " iflast " )
{
if ( game . lastsaved = = ss_toi ( words [ 1 ] ) )
{
2023-02-01 05:04:18 +01:00
loadalts ( words [ 2 ] , raw_words [ 2 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
else if ( words [ 0 ] = = " ifskip " )
{
if ( game . nocutscenes )
{
2023-02-01 05:04:18 +01:00
loadalts ( words [ 1 ] , raw_words [ 1 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
else if ( words [ 0 ] = = " ifflag " )
{
int flag = ss_toi ( words [ 1 ] ) ;
if ( INBOUNDS_ARR ( flag , obj . flags ) & & obj . flags [ flag ] )
{
2023-02-01 05:04:18 +01:00
loadalts ( words [ 2 ] , raw_words [ 2 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
else if ( words [ 0 ] = = " ifcrewlost " )
{
int crewmate = ss_toi ( words [ 1 ] ) ;
if ( INBOUNDS_ARR ( crewmate , game . crewstats ) & & ! game . crewstats [ crewmate ] )
{
2023-02-01 05:04:18 +01:00
loadalts ( words [ 2 ] , raw_words [ 2 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
else if ( words [ 0 ] = = " iftrinkets " )
{
if ( game . trinkets ( ) > = ss_toi ( words [ 1 ] ) )
{
2023-02-01 05:04:18 +01:00
loadalts ( words [ 2 ] , raw_words [ 2 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
else if ( words [ 0 ] = = " iftrinketsless " )
{
if ( game . stat_trinkets < ss_toi ( words [ 1 ] ) )
{
2023-02-01 05:04:18 +01:00
loadalts ( words [ 2 ] , raw_words [ 2 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
}
else if ( words [ 0 ] = = " hidecoordinates " )
{
map . setexplored ( ss_toi ( words [ 1 ] ) , ss_toi ( words [ 2 ] ) , false ) ;
}
else if ( words [ 0 ] = = " showcoordinates " )
{
map . setexplored ( ss_toi ( words [ 1 ] ) , ss_toi ( words [ 2 ] ) , true ) ;
}
else if ( words [ 0 ] = = " hideship " )
{
map . hideship ( ) ;
}
else if ( words [ 0 ] = = " showship " )
{
map . showship ( ) ;
}
else if ( words [ 0 ] = = " showsecretlab " )
{
map . setexplored ( 16 , 5 , true ) ;
map . setexplored ( 17 , 5 , true ) ;
map . setexplored ( 18 , 5 , true ) ;
map . setexplored ( 17 , 6 , true ) ;
map . setexplored ( 18 , 6 , true ) ;
map . setexplored ( 19 , 6 , true ) ;
map . setexplored ( 19 , 7 , true ) ;
map . setexplored ( 19 , 8 , true ) ;
}
else if ( words [ 0 ] = = " hidesecretlab " )
{
map . setexplored ( 16 , 5 , false ) ;
map . setexplored ( 17 , 5 , false ) ;
map . setexplored ( 18 , 5 , false ) ;
map . setexplored ( 17 , 6 , false ) ;
map . setexplored ( 18 , 6 , false ) ;
map . setexplored ( 19 , 6 , false ) ;
map . setexplored ( 19 , 7 , false ) ;
map . setexplored ( 19 , 8 , false ) ;
}
else if ( words [ 0 ] = = " showteleporters " )
{
map . showteleporters = true ;
}
else if ( words [ 0 ] = = " showtargets " )
{
map . showtargets = true ;
}
else if ( words [ 0 ] = = " showtrinkets " )
{
map . showtrinkets = true ;
}
else if ( words [ 0 ] = = " hideteleporters " )
{
map . showteleporters = false ;
}
else if ( words [ 0 ] = = " hidetargets " )
{
map . showtargets = false ;
}
else if ( words [ 0 ] = = " hidetrinkets " )
{
map . showtrinkets = false ;
}
else if ( words [ 0 ] = = " hideplayer " )
{
int player = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( player , obj . entities ) )
{
obj . entities [ player ] . invis = true ;
}
}
else if ( words [ 0 ] = = " showplayer " )
{
int player = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( player , obj . entities ) )
{
obj . entities [ player ] . invis = false ;
}
}
else if ( words [ 0 ] = = " teleportscript " )
{
game . teleportscript = words [ 1 ] ;
}
else if ( words [ 0 ] = = " clearteleportscript " )
{
game . teleportscript = " " ;
}
else if ( words [ 0 ] = = " nocontrol " )
{
game . hascontrol = false ;
}
else if ( words [ 0 ] = = " hascontrol " )
{
game . hascontrol = true ;
}
else if ( words [ 0 ] = = " companion " )
{
game . companion = ss_toi ( words [ 1 ] ) ;
}
else if ( words [ 0 ] = = " befadein " )
{
graphics . setfade ( 0 ) ;
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
graphics . fademode = FADE_NONE ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 0 ] = = " fadein " )
{
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
graphics . fademode = FADE_START_FADEIN ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 0 ] = = " fadeout " )
{
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
graphics . fademode = FADE_START_FADEOUT ;
2021-09-07 03:56:39 +02:00
}
else if ( words [ 0 ] = = " untilfade " )
{
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 ( FADEMODE_IS_FADING ( graphics . fademode ) )
2021-09-07 03:56:39 +02:00
{
scriptdelay = 1 ;
position - - ;
}
}
else if ( words [ 0 ] = = " entersecretlab " )
{
2023-06-06 02:34:27 +02:00
game . unlocknum ( Unlock_SECRETLAB ) ;
2021-09-07 03:56:39 +02:00
game . insecretlab = true ;
SDL_memset ( map . explored , true , sizeof ( map . explored ) ) ;
}
else if ( words [ 0 ] = = " leavesecretlab " )
{
game . insecretlab = false ;
}
else if ( words [ 0 ] = = " resetgame " )
{
map . resetmap ( ) ;
map . resetplayer ( ) ;
graphics . towerbg . tdrawback = true ;
obj . resetallflags ( ) ;
i = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . tile = 0 ;
}
for ( i = 0 ; i < 100 ; i + + )
{
obj . collect [ i ] = false ;
obj . customcollect [ i ] = false ;
}
game . deathcounts = 0 ;
game . advancetext = false ;
game . hascontrol = true ;
game . resetgameclock ( ) ;
game . gravitycontrol = 0 ;
game . teleport = false ;
game . companion = 0 ;
game . teleport_to_new_area = false ;
game . teleport_to_x = 0 ;
game . teleport_to_y = 0 ;
game . teleportscript = " " ;
//get out of final level mode!
map . finalmode = false ;
map . final_colormode = false ;
map . final_mapcol = 0 ;
map . final_colorframe = 0 ;
map . finalstretch = false ;
}
else if ( words [ 0 ] = = " loadscript " )
{
2023-02-01 05:04:18 +01:00
loadalts ( words [ 1 ] , raw_words [ 1 ] ) ;
2021-09-07 03:56:39 +02:00
position - - ;
}
else if ( words [ 0 ] = = " rollcredits " )
{
if ( map . custommode & & ! map . custommodeforreal )
{
game . returntoeditor ( ) ;
2023-03-05 19:59:36 +01:00
ed . show_note ( loc : : gettext ( " Rolled credits " ) ) ;
2021-09-07 03:56:39 +02:00
}
else
{
game . gamestate = GAMECOMPLETE ;
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
graphics . fademode = FADE_START_FADEIN ;
2021-09-07 03:56:39 +02:00
game . creditposition = 0 ;
2023-05-22 21:18:40 +02:00
game . skip_message_timer = 1000 ;
game . old_skip_message_timer = 1000 ;
2021-09-07 03:56:39 +02:00
}
}
else if ( words [ 0 ] = = " finalmode " )
{
map . finalmode = true ;
map . gotoroom ( ss_toi ( words [ 1 ] ) , ss_toi ( words [ 2 ] ) ) ;
}
else if ( words [ 0 ] = = " rescued " )
{
if ( words [ 1 ] = = " red " )
{
game . crewstats [ 3 ] = true ;
}
else if ( words [ 1 ] = = " green " )
{
game . crewstats [ 4 ] = true ;
}
else if ( words [ 1 ] = = " yellow " )
{
game . crewstats [ 2 ] = true ;
}
else if ( words [ 1 ] = = " blue " )
{
game . crewstats [ 5 ] = true ;
}
else if ( words [ 1 ] = = " purple " )
{
game . crewstats [ 1 ] = true ;
}
else if ( words [ 1 ] = = " player " )
{
game . crewstats [ 0 ] = true ;
}
else if ( words [ 1 ] = = " cyan " )
{
game . crewstats [ 0 ] = true ;
}
}
else if ( words [ 0 ] = = " missing " )
{
if ( words [ 1 ] = = " red " )
{
game . crewstats [ 3 ] = false ;
}
else if ( words [ 1 ] = = " green " )
{
game . crewstats [ 4 ] = false ;
}
else if ( words [ 1 ] = = " yellow " )
{
game . crewstats [ 2 ] = false ;
}
else if ( words [ 1 ] = = " blue " )
{
game . crewstats [ 5 ] = false ;
}
else if ( words [ 1 ] = = " purple " )
{
game . crewstats [ 1 ] = false ;
}
else if ( words [ 1 ] = = " player " )
{
game . crewstats [ 0 ] = false ;
}
else if ( words [ 1 ] = = " cyan " )
{
game . crewstats [ 0 ] = false ;
}
}
else if ( words [ 0 ] = = " face " )
{
int crewmate = getcrewmanfromname ( words [ 1 ] ) ;
if ( crewmate ! = - 1 ) i = crewmate ; // Ensure AEM is kept
crewmate = getcrewmanfromname ( words [ 2 ] ) ;
if ( crewmate ! = - 1 ) j = crewmate ; // Ensure AEM is kept
if ( INBOUNDS_VEC ( i , obj . entities ) & & INBOUNDS_VEC ( j , obj . entities ) & & obj . entities [ j ] . xp > obj . entities [ i ] . xp + 5 )
{
obj . entities [ i ] . dir = 1 ;
}
else if ( INBOUNDS_VEC ( i , obj . entities ) & & INBOUNDS_VEC ( j , obj . entities ) & & obj . entities [ j ] . xp < obj . entities [ i ] . xp - 5 )
{
obj . entities [ i ] . dir = 0 ;
}
}
else if ( words [ 0 ] = = " jukebox " )
{
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . type = = 13 )
{
obj . entities [ j ] . colour = 4 ;
}
}
if ( ss_toi ( words [ 1 ] ) = = 1 )
{
obj . createblock ( 5 , 88 - 4 , 80 , 20 , 16 , 25 ) ;
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . xp = = 88 & & obj . entities [ j ] . yp = = 80 )
{
obj . entities [ j ] . colour = 5 ;
}
}
}
else if ( ss_toi ( words [ 1 ] ) = = 2 )
{
obj . createblock ( 5 , 128 - 4 , 80 , 20 , 16 , 26 ) ;
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . xp = = 128 & & obj . entities [ j ] . yp = = 80 )
{
obj . entities [ j ] . colour = 5 ;
}
}
}
else if ( ss_toi ( words [ 1 ] ) = = 3 )
{
obj . createblock ( 5 , 176 - 4 , 80 , 20 , 16 , 27 ) ;
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . xp = = 176 & & obj . entities [ j ] . yp = = 80 )
{
obj . entities [ j ] . colour = 5 ;
}
}
}
else if ( ss_toi ( words [ 1 ] ) = = 4 )
{
obj . createblock ( 5 , 216 - 4 , 80 , 20 , 16 , 28 ) ;
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . xp = = 216 & & obj . entities [ j ] . yp = = 80 )
{
obj . entities [ j ] . colour = 5 ;
}
}
}
else if ( ss_toi ( words [ 1 ] ) = = 5 )
{
obj . createblock ( 5 , 88 - 4 , 128 , 20 , 16 , 29 ) ;
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . xp = = 88 & & obj . entities [ j ] . yp = = 128 )
{
obj . entities [ j ] . colour = 5 ;
}
}
}
else if ( ss_toi ( words [ 1 ] ) = = 6 )
{
obj . createblock ( 5 , 176 - 4 , 128 , 20 , 16 , 30 ) ;
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . xp = = 176 & & obj . entities [ j ] . yp = = 128 )
{
obj . entities [ j ] . colour = 5 ;
}
}
}
else if ( ss_toi ( words [ 1 ] ) = = 7 )
{
obj . createblock ( 5 , 40 - 4 , 40 , 20 , 16 , 31 ) ;
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . xp = = 40 & & obj . entities [ j ] . yp = = 40 )
{
obj . entities [ j ] . colour = 5 ;
}
}
}
else if ( ss_toi ( words [ 1 ] ) = = 8 )
{
obj . createblock ( 5 , 216 - 4 , 128 , 20 , 16 , 32 ) ;
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . xp = = 216 & & obj . entities [ j ] . yp = = 128 )
{
obj . entities [ j ] . colour = 5 ;
}
}
}
else if ( ss_toi ( words [ 1 ] ) = = 9 )
{
obj . createblock ( 5 , 128 - 4 , 128 , 20 , 16 , 33 ) ;
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . xp = = 128 & & obj . entities [ j ] . yp = = 128 )
{
obj . entities [ j ] . colour = 5 ;
}
}
}
else if ( ss_toi ( words [ 1 ] ) = = 10 )
{
obj . createblock ( 5 , 264 - 4 , 40 , 20 , 16 , 34 ) ;
for ( j = 0 ; j < ( int ) obj . entities . size ( ) ; j + + )
{
if ( obj . entities [ j ] . xp = = 264 & & obj . entities [ j ] . yp = = 40 )
{
obj . entities [ j ] . colour = 5 ;
}
}
}
}
else if ( words [ 0 ] = = " createactivityzone " )
{
int crew_color = i ; // stay consistent with past behavior!
if ( words [ 1 ] = = " red " )
{
i = 3 ;
crew_color = RED ;
}
else if ( words [ 1 ] = = " green " )
{
i = 4 ;
crew_color = GREEN ;
}
else if ( words [ 1 ] = = " yellow " )
{
i = 2 ;
crew_color = YELLOW ;
}
else if ( words [ 1 ] = = " blue " )
{
i = 5 ;
crew_color = BLUE ;
}
else if ( words [ 1 ] = = " purple " )
{
i = 1 ;
crew_color = PURPLE ;
}
int crewman = obj . getcrewman ( crew_color ) ;
if ( INBOUNDS_VEC ( crewman , obj . entities ) & & crew_color = = GREEN )
{
obj . createblock ( 5 , obj . entities [ crewman ] . xp - 32 , obj . entities [ crewman ] . yp - 20 , 96 , 60 , i , " " , ( i = = 35 ) ) ;
}
else if ( INBOUNDS_VEC ( crewman , obj . entities ) )
{
obj . createblock ( 5 , obj . entities [ crewman ] . xp - 32 , 0 , 96 , 240 , i , " " , ( i = = 35 ) ) ;
}
}
else if ( words [ 0 ] = = " setactivitycolour " )
{
obj . customactivitycolour = words [ 1 ] ;
}
else if ( words [ 0 ] = = " setactivitytext " )
{
+ + position ;
if ( INBOUNDS_VEC ( position , commands ) )
{
obj . customactivitytext = commands [ position ] ;
}
}
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 if ( words [ 0 ] = = " setactivityposition " )
{
2023-06-04 17:52:01 +02:00
obj . customactivitypositiony = ss_toi ( words [ 1 ] ) ;
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
}
2021-09-07 03:56:39 +02:00
else if ( words [ 0 ] = = " createrescuedcrew " )
{
//special for final level cutscene
//starting at 180, create the rescued crewmembers (ingoring violet, who's at 155)
i = 215 ;
if ( game . crewstats [ 2 ] & & game . lastsaved ! = 2 )
{
obj . createentity ( i , 153 , 18 , 14 , 0 , 17 , 0 ) ;
i + = 25 ;
}
if ( game . crewstats [ 3 ] & & game . lastsaved ! = 3 )
{
obj . createentity ( i , 153 , 18 , 15 , 0 , 17 , 0 ) ;
i + = 25 ;
}
if ( game . crewstats [ 4 ] & & game . lastsaved ! = 4 )
{
obj . createentity ( i , 153 , 18 , 13 , 0 , 17 , 0 ) ;
i + = 25 ;
}
if ( game . crewstats [ 5 ] & & game . lastsaved ! = 5 )
{
obj . createentity ( i , 153 , 18 , 16 , 0 , 17 , 0 ) ;
i + = 25 ;
}
}
else if ( words [ 0 ] = = " restoreplayercolour " )
{
i = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . colour = 0 ;
}
}
else if ( words [ 0 ] = = " changeplayercolour " )
{
i = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . colour = getcolorfromname ( words [ 1 ] ) ;
}
}
else if ( words [ 0 ] = = " changerespawncolour " )
{
game . savecolour = getcolorfromname ( words [ 1 ] ) ;
}
else if ( words [ 0 ] = = " altstates " )
{
obj . altstates = ss_toi ( words [ 1 ] ) ;
}
else if ( words [ 0 ] = = " activeteleporter " )
{
i = obj . getteleporter ( ) ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . colour = 101 ;
}
}
else if ( words [ 0 ] = = " foundtrinket " )
{
music . silencedasmusik ( ) ;
2023-06-04 00:29:02 +02:00
music . playef ( Sound_TRINKET ) ;
2021-09-07 03:56:39 +02:00
size_t trinket = ss_toi ( words [ 1 ] ) ;
if ( trinket < SDL_arraysize ( obj . collect ) )
{
obj . collect [ trinket ] = true ;
}
graphics . textboxremovefast ( ) ;
2024-01-21 21:39:15 +01:00
graphics . createtextboxflipme ( " " , 50 , 85 , TEXT_COLOUR ( " gray " ) ) ;
2023-01-13 05:11:39 +01:00
graphics . textboxprintflags ( PR_FONT_INTERFACE ) ;
2021-09-07 03:56:39 +02:00
graphics . textboxcenterx ( ) ;
2024-01-21 21:39:15 +01:00
graphics . textboxtranslate ( TEXTTRANSLATE_FUNCTION , foundtrinket_textbox1 ) ;
graphics . textboxapplyposition ( ) ;
2021-09-07 03:56:39 +02:00
2024-01-21 21:39:15 +01:00
graphics . createtextboxflipme ( " " , 50 , 95 , TEXT_COLOUR ( " gray " ) ) ;
2023-01-13 05:11:39 +01:00
graphics . textboxprintflags ( PR_FONT_INTERFACE ) ;
2021-09-07 03:56:39 +02:00
graphics . textboxcenterx ( ) ;
2024-01-21 21:39:15 +01:00
graphics . textboxindex ( graphics . textboxes . size ( ) - 2 ) ;
graphics . textboxtranslate ( TEXTTRANSLATE_FUNCTION , foundtrinket_textbox2 ) ;
graphics . textboxapplyposition ( ) ;
2021-09-07 03:56:39 +02:00
if ( ! game . backgroundtext )
{
game . advancetext = true ;
game . hascontrol = false ;
game . pausescript = true ;
if ( key . isDown ( 90 ) | | key . isDown ( 32 ) | | key . isDown ( 86 )
| | key . isDown ( KEYBOARD_UP ) | | key . isDown ( KEYBOARD_DOWN ) ) game . jumpheld = true ;
}
game . backgroundtext = false ;
}
else if ( words [ 0 ] = = " foundlab " )
{
2023-06-04 00:29:02 +02:00
music . playef ( Sound_TRINKET ) ;
2021-09-07 03:56:39 +02:00
graphics . textboxremovefast ( ) ;
2024-01-21 22:47:26 +01:00
graphics . createtextbox ( " " , 50 , 85 , TEXT_COLOUR ( " gray " ) ) ;
2023-01-13 05:11:39 +01:00
graphics . textboxprintflags ( PR_FONT_INTERFACE ) ;
2021-09-07 03:56:39 +02:00
graphics . textboxcenterx ( ) ;
graphics . textboxcentery ( ) ;
2024-01-21 22:47:26 +01:00
graphics . textboxtranslate ( TEXTTRANSLATE_FUNCTION , foundlab_textbox1 ) ;
graphics . textboxapplyposition ( ) ;
2021-09-07 03:56:39 +02:00
if ( ! game . backgroundtext )
{
game . advancetext = true ;
game . hascontrol = false ;
game . pausescript = true ;
if ( key . isDown ( 90 ) | | key . isDown ( 32 ) | | key . isDown ( 86 )
| | key . isDown ( KEYBOARD_UP ) | | key . isDown ( KEYBOARD_DOWN ) ) game . jumpheld = true ;
}
game . backgroundtext = false ;
}
else if ( words [ 0 ] = = " foundlab2 " )
{
graphics . textboxremovefast ( ) ;
2024-01-21 22:47:26 +01:00
graphics . createtextbox ( " " , 50 , 85 , TEXT_COLOUR ( " gray " ) ) ;
2023-01-13 05:11:39 +01:00
graphics . textboxprintflags ( PR_FONT_INTERFACE ) ;
2021-09-07 03:56:39 +02:00
graphics . textboxcenterx ( ) ;
graphics . textboxcentery ( ) ;
2024-01-21 22:47:26 +01:00
graphics . textboxtranslate ( TEXTTRANSLATE_FUNCTION , foundlab_textbox2 ) ;
graphics . textboxapplyposition ( ) ;
2021-09-07 03:56:39 +02:00
if ( ! game . backgroundtext )
{
game . advancetext = true ;
game . hascontrol = false ;
game . pausescript = true ;
if ( key . isDown ( 90 ) | | key . isDown ( 32 ) | | key . isDown ( 86 )
| | key . isDown ( KEYBOARD_UP ) | | key . isDown ( KEYBOARD_DOWN ) ) game . jumpheld = true ;
}
game . backgroundtext = false ;
}
else if ( words [ 0 ] = = " everybodysad " )
{
for ( i = 0 ; i < ( int ) obj . entities . size ( ) ; i + + )
{
if ( obj . entities [ i ] . rule = = 6 | | obj . entities [ i ] . rule = = 0 )
{
obj . entities [ i ] . tile = 144 ;
}
}
}
else if ( words [ 0 ] = = " startintermission2 " )
{
map . finalmode = true ; //Enable final level mode
game . savex = 228 ;
game . savey = 129 ;
game . saverx = 53 ;
game . savery = 49 ;
game . savegc = 0 ;
game . savedir = 0 ; //Intermission level 2
game . savepoint = 0 ;
game . gravitycontrol = 0 ;
map . gotoroom ( 46 , 54 ) ;
}
else if ( words [ 0 ] = = " telesave " )
{
if ( ! game . intimetrial & & ! game . nodeathmode & & ! game . inintermission ) game . savetele ( ) ;
}
else if ( words [ 0 ] = = " createlastrescued " )
{
r = graphics . crewcolour ( game . lastsaved ) ;
if ( r = = 0 | | r = = PURPLE )
{
r = GRAY ; // Default to gray if invalid color.
}
obj . createentity ( 200 , 153 , 18 , r , 0 , 19 , 30 ) ;
i = obj . getcrewman ( game . lastsaved ) ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . dir = 1 ;
}
}
else if ( words [ 0 ] = = " specialline " )
{
2022-12-30 22:57:24 +01:00
//Localization is handled with regular cutscene dialogue
2021-09-07 03:56:39 +02:00
switch ( ss_toi ( words [ 1 ] ) )
{
case 1 :
txt . resize ( 1 ) ;
txt [ 0 ] = " I'm worried about " + game . unrescued ( ) + " , Doctor! " ;
break ;
case 2 :
txt . resize ( 3 ) ;
if ( game . crewrescued ( ) < 5 )
{
txt [ 1 ] = " to helping you find the " ;
txt [ 2 ] = " rest of the crew! " ;
}
else
{
txt . resize ( 2 ) ;
txt [ 1 ] = " to helping you find " + game . unrescued ( ) + " ! " ;
}
break ;
}
}
else if ( words [ 0 ] = = " trinketbluecontrol " )
{
if ( game . trinkets ( ) = = 20 & & obj . flags [ 67 ] )
{
load ( " talkblue_trinket6 " ) ;
position - - ;
}
else if ( game . trinkets ( ) > = 19 & & ! obj . flags [ 67 ] )
{
load ( " talkblue_trinket5 " ) ;
position - - ;
}
else
{
load ( " talkblue_trinket4 " ) ;
position - - ;
}
}
else if ( words [ 0 ] = = " trinketyellowcontrol " )
{
if ( game . trinkets ( ) > = 19 )
{
load ( " talkyellow_trinket3 " ) ;
position - - ;
}
else
{
load ( " talkyellow_trinket2 " ) ;
position - - ;
}
}
else if ( words [ 0 ] = = " redcontrol " )
{
if ( game . insecretlab )
{
load ( " talkred_14 " ) ;
position - - ;
}
else if ( game . roomx ! = 104 )
{
if ( game . roomx = = 100 )
{
load ( " talkred_10 " ) ;
position - - ;
}
else if ( game . roomx = = 107 )
{
load ( " talkred_11 " ) ;
position - - ;
}
else if ( game . roomx = = 114 )
{
load ( " talkred_12 " ) ;
position - - ;
}
}
else if ( obj . flags [ 67 ] )
{
//game complete
load ( " talkred_13 " ) ;
position - - ;
}
else if ( obj . flags [ 35 ] & & ! obj . flags [ 52 ] )
{
//Intermission level
obj . flags [ 52 ] = true ;
load ( " talkred_9 " ) ;
position - - ;
}
else if ( ! obj . flags [ 51 ] )
{
//We're back home!
obj . flags [ 51 ] = true ;
load ( " talkred_5 " ) ;
position - - ;
}
else if ( ! obj . flags [ 48 ] & & game . crewstats [ 5 ] )
{
//Victoria's back
obj . flags [ 48 ] = true ;
load ( " talkred_6 " ) ;
position - - ;
}
else if ( ! obj . flags [ 49 ] & & game . crewstats [ 4 ] )
{
//Verdigris' back
obj . flags [ 49 ] = true ;
load ( " talkred_7 " ) ;
position - - ;
}
else if ( ! obj . flags [ 50 ] & & game . crewstats [ 2 ] )
{
//Vitellary's back
obj . flags [ 50 ] = true ;
load ( " talkred_8 " ) ;
position - - ;
}
else if ( ! obj . flags [ 45 ] & & ! game . crewstats [ 5 ] )
{
obj . flags [ 45 ] = true ;
load ( " talkred_2 " ) ;
position - - ;
}
else if ( ! obj . flags [ 46 ] & & ! game . crewstats [ 4 ] )
{
obj . flags [ 46 ] = true ;
load ( " talkred_3 " ) ;
position - - ;
}
else if ( ! obj . flags [ 47 ] & & ! game . crewstats [ 2 ] )
{
obj . flags [ 47 ] = true ;
load ( " talkred_4 " ) ;
position - - ;
}
else
{
obj . flags [ 45 ] = false ;
obj . flags [ 46 ] = false ;
obj . flags [ 47 ] = false ;
load ( " talkred_1 " ) ;
position - - ;
}
}
//TODO: Non Urgent fix compiler nesting errors without adding complexity
if ( words [ 0 ] = = " greencontrol " )
{
if ( game . insecretlab )
{
load ( " talkgreen_11 " ) ;
position - - ;
}
else if ( game . roomx = = 103 & & game . roomy = = 109 )
{
load ( " talkgreen_8 " ) ;
position - - ;
}
else if ( game . roomx = = 101 & & game . roomy = = 109 )
{
load ( " talkgreen_9 " ) ;
position - - ;
}
else if ( obj . flags [ 67 ] )
{
//game complete
load ( " talkgreen_10 " ) ;
position - - ;
}
else if ( obj . flags [ 34 ] & & ! obj . flags [ 57 ] )
{
//Intermission level
obj . flags [ 57 ] = true ;
load ( " talkgreen_7 " ) ;
position - - ;
}
else if ( ! obj . flags [ 53 ] )
{
//Home!
obj . flags [ 53 ] = true ;
load ( " talkgreen_6 " ) ;
position - - ;
}
else if ( ! obj . flags [ 54 ] & & game . crewstats [ 2 ] )
{
obj . flags [ 54 ] = true ;
load ( " talkgreen_5 " ) ;
position - - ;
}
else if ( ! obj . flags [ 55 ] & & game . crewstats [ 3 ] )
{
obj . flags [ 55 ] = true ;
load ( " talkgreen_4 " ) ;
position - - ;
}
else if ( ! obj . flags [ 56 ] & & game . crewstats [ 5 ] )
{
obj . flags [ 56 ] = true ;
load ( " talkgreen_3 " ) ;
position - - ;
}
else if ( ! obj . flags [ 58 ] )
{
obj . flags [ 58 ] = true ;
load ( " talkgreen_2 " ) ;
position - - ;
}
else
{
load ( " talkgreen_1 " ) ;
position - - ;
}
}
else if ( words [ 0 ] = = " bluecontrol " )
{
if ( game . insecretlab )
{
load ( " talkblue_9 " ) ;
position - - ;
}
else if ( obj . flags [ 67 ] )
{
//game complete, everything changes for victoria
if ( obj . flags [ 41 ] & & ! obj . flags [ 42 ] )
{
//second trinket conversation
obj . flags [ 42 ] = true ;
load ( " talkblue_trinket2 " ) ;
position - - ;
}
else if ( ! obj . flags [ 41 ] & & ! obj . flags [ 42 ] )
{
//Third trinket conversation
obj . flags [ 42 ] = true ;
load ( " talkblue_trinket3 " ) ;
position - - ;
}
else
{
//Ok, we've already dealt with the trinket thing; so either you have them all, or you don't. If you do:
if ( game . trinkets ( ) > = 20 )
{
load ( " startepilogue " ) ;
position - - ;
}
else
{
load ( " talkblue_8 " ) ;
position - - ;
}
}
}
else if ( obj . flags [ 33 ] & & ! obj . flags [ 40 ] )
{
//Intermission level
obj . flags [ 40 ] = true ;
load ( " talkblue_7 " ) ;
position - - ;
}
else if ( ! obj . flags [ 36 ] & & game . crewstats [ 5 ] )
{
//Back on the ship!
obj . flags [ 36 ] = true ;
load ( " talkblue_3 " ) ;
position - - ;
}
else if ( ! obj . flags [ 41 ] & & game . crewrescued ( ) < = 4 )
{
//First trinket conversation
obj . flags [ 41 ] = true ;
load ( " talkblue_trinket1 " ) ;
position - - ;
}
else if ( obj . flags [ 41 ] & & ! obj . flags [ 42 ] & & game . crewrescued ( ) = = 5 )
{
//second trinket conversation
obj . flags [ 42 ] = true ;
load ( " talkblue_trinket2 " ) ;
position - - ;
}
else if ( ! obj . flags [ 41 ] & & ! obj . flags [ 42 ] & & game . crewrescued ( ) = = 5 )
{
//Third trinket conversation
obj . flags [ 42 ] = true ;
load ( " talkblue_trinket3 " ) ;
position - - ;
}
else if ( ! obj . flags [ 37 ] & & game . crewstats [ 2 ] )
{
obj . flags [ 37 ] = true ;
load ( " talkblue_4 " ) ;
position - - ;
}
else if ( ! obj . flags [ 38 ] & & game . crewstats [ 3 ] )
{
obj . flags [ 38 ] = true ;
load ( " talkblue_5 " ) ;
position - - ;
}
else if ( ! obj . flags [ 39 ] & & game . crewstats [ 4 ] )
{
obj . flags [ 39 ] = true ;
load ( " talkblue_6 " ) ;
position - - ;
}
else
{
//if all else fails:
//if yellow is found
if ( game . crewstats [ 2 ] )
{
load ( " talkblue_2 " ) ;
position - - ;
}
else
{
load ( " talkblue_1 " ) ;
position - - ;
}
}
}
else if ( words [ 0 ] = = " yellowcontrol " )
{
if ( game . insecretlab )
{
load ( " talkyellow_12 " ) ;
position - - ;
}
else if ( obj . flags [ 67 ] )
{
//game complete
load ( " talkyellow_11 " ) ;
position - - ;
}
else if ( obj . flags [ 32 ] & & ! obj . flags [ 31 ] )
{
//Intermission level
obj . flags [ 31 ] = true ;
load ( " talkyellow_6 " ) ;
position - - ;
}
else if ( ! obj . flags [ 27 ] & & game . crewstats [ 2 ] )
{
//Back on the ship!
obj . flags [ 27 ] = true ;
load ( " talkyellow_10 " ) ;
position - - ;
}
else if ( ! obj . flags [ 43 ] & & game . crewrescued ( ) = = 5 & & ! game . crewstats [ 5 ] )
{
//If by chance we've rescued everyone except Victoria by the end, Vitellary provides you with
//the trinket information instead.
obj . flags [ 43 ] = true ;
obj . flags [ 42 ] = true ;
obj . flags [ 41 ] = true ;
load ( " talkyellow_trinket1 " ) ;
position - - ;
}
else if ( ! obj . flags [ 24 ] & & game . crewstats [ 5 ] )
{
obj . flags [ 24 ] = true ;
load ( " talkyellow_8 " ) ;
position - - ;
}
else if ( ! obj . flags [ 26 ] & & game . crewstats [ 4 ] )
{
obj . flags [ 26 ] = true ;
load ( " talkyellow_7 " ) ;
position - - ;
}
else if ( ! obj . flags [ 25 ] & & game . crewstats [ 3 ] )
{
obj . flags [ 25 ] = true ;
load ( " talkyellow_9 " ) ;
position - - ;
}
else if ( ! obj . flags [ 28 ] )
{
obj . flags [ 28 ] = true ;
load ( " talkyellow_3 " ) ;
position - - ;
}
else if ( ! obj . flags [ 29 ] )
{
obj . flags [ 29 ] = true ;
load ( " talkyellow_4 " ) ;
position - - ;
}
else if ( ! obj . flags [ 30 ] )
{
obj . flags [ 30 ] = true ;
load ( " talkyellow_5 " ) ;
position - - ;
}
else if ( ! obj . flags [ 23 ] )
{
obj . flags [ 23 ] = true ;
load ( " talkyellow_2 " ) ;
position - - ;
}
else
{
load ( " talkyellow_1 " ) ;
position - - ;
obj . flags [ 23 ] = false ;
}
}
else if ( words [ 0 ] = = " purplecontrol " )
{
//Controls Purple's conversion
//Crew rescued:
if ( game . insecretlab )
{
load ( " talkpurple_9 " ) ;
position - - ;
}
else if ( obj . flags [ 67 ] )
{
//game complete
load ( " talkpurple_8 " ) ;
position - - ;
}
else if ( ! obj . flags [ 17 ] & & game . crewstats [ 4 ] )
{
obj . flags [ 17 ] = true ;
load ( " talkpurple_6 " ) ;
position - - ;
}
else if ( ! obj . flags [ 15 ] & & game . crewstats [ 5 ] )
{
obj . flags [ 15 ] = true ;
load ( " talkpurple_4 " ) ;
position - - ;
}
else if ( ! obj . flags [ 16 ] & & game . crewstats [ 3 ] )
{
obj . flags [ 16 ] = true ;
load ( " talkpurple_5 " ) ;
position - - ;
}
else if ( ! obj . flags [ 18 ] & & game . crewstats [ 2 ] )
{
obj . flags [ 18 ] = true ;
load ( " talkpurple_7 " ) ;
position - - ;
}
else if ( obj . flags [ 19 ] & & ! obj . flags [ 20 ] & & ! obj . flags [ 21 ] )
{
//intermission one: if played one / not had first conversation / not played two [conversation one]
obj . flags [ 21 ] = true ;
load ( " talkpurple_intermission1 " ) ;
position - - ;
}
else if ( obj . flags [ 20 ] & & obj . flags [ 21 ] & & ! obj . flags [ 22 ] )
{
//intermission two: if played two / had first conversation / not had second conversation [conversation two]
obj . flags [ 22 ] = true ;
load ( " talkpurple_intermission2 " ) ;
position - - ;
}
else if ( obj . flags [ 20 ] & & ! obj . flags [ 21 ] & & ! obj . flags [ 22 ] )
{
//intermission two: if played two / not had first conversation / not had second conversation [conversation three]
obj . flags [ 22 ] = true ;
load ( " talkpurple_intermission3 " ) ;
position - - ;
}
else if ( ! obj . flags [ 12 ] )
{
//Intro conversation
obj . flags [ 12 ] = true ;
load ( " talkpurple_intro " ) ;
position - - ;
}
else if ( ! obj . flags [ 14 ] )
{
//Shorter intro conversation
obj . flags [ 14 ] = true ;
load ( " talkpurple_3 " ) ;
position - - ;
}
else
{
//if all else fails:
//if green is found
if ( game . crewstats [ 4 ] )
{
load ( " talkpurple_2 " ) ;
position - - ;
}
else
{
load ( " talkpurple_1 " ) ;
position - - ;
}
}
}
2023-03-18 22:31:13 +01:00
else if ( words [ 0 ] = = " textbuttons " )
{
// Parse buttons in the next textbox
textbuttons = true ;
}
2022-12-30 22:57:24 +01:00
else if ( words [ 0 ] = = " textcase " )
{
// Used to disambiguate identical textboxes for translations (1 by default)
2024-01-10 08:57:14 +01:00
const int number = ss_toi ( words [ 1 ] ) ;
if ( number > = 1 & & number < = 255 )
{
textcase = number ;
}
2022-12-30 22:57:24 +01:00
}
else if ( words [ 0 ] = = " loadtext " )
{
if ( map . custommode )
{
2023-07-23 00:06:26 +02:00
loc : : lang_custom = raw_words [ 1 ] ;
2022-12-30 22:57:24 +01:00
loc : : loadtext_custom ( NULL ) ;
}
}
else if ( words [ 0 ] = = " iflang " )
{
2023-07-23 00:06:26 +02:00
if ( loc : : lang = = raw_words [ 1 ] )
2022-12-30 22:57:24 +01:00
{
2023-02-01 05:04:18 +01:00
loadalts ( " custom_ " + words [ 2 ] , " custom_ " + raw_words [ 2 ] ) ;
2022-12-30 22:57:24 +01:00
position - - ;
}
}
2024-01-03 20:43:37 +01:00
else if ( words [ 0 ] = = " setfont " | | words [ 0 ] = = " setrtl " )
2023-01-20 04:57:06 +01:00
{
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
if ( words [ 0 ] = = " setrtl " )
2023-01-20 04:57:06 +01:00
{
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
if ( words [ 1 ] = = " on " )
2023-08-28 03:19:57 +02:00
{
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
cl . rtl = true ;
2023-08-28 03:19:57 +02:00
}
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
else if ( words [ 1 ] = = " off " )
2024-01-03 20:43:37 +01:00
{
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
cl . rtl = false ;
2024-01-03 20:43:37 +01:00
}
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
}
else if ( words [ 1 ] = = " " )
{
font : : set_level_font ( cl . level_font_name . c_str ( ) ) ;
}
else
{
font : : set_level_font ( raw_words [ 1 ] . c_str ( ) ) ;
}
2024-01-24 01:53:01 +01:00
if ( words [ 0 ] = = " setfont " & & argexists [ 2 ] & & words [ 2 ] = = " all " )
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
{
/* Immediately update all text boxes. */
uint32_t flags = PR_FONT_IDX ( font : : font_idx_level , cl . rtl ) ;
if ( font : : font_idx_level_is_custom )
2023-08-28 03:19:57 +02:00
{
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
flags | = PR_FONT_IDX_IS_CUSTOM ;
2023-08-28 03:19:57 +02:00
}
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
for ( size_t i = 0 ; i < graphics . textboxes . size ( ) ; i + + )
2023-08-28 03:19:57 +02:00
{
Properly fix setfont/setrtl in between text boxes
There used to be a problem with the setfont and setrtl script commands.
Namely, if you used them in between text boxes naïvely, without any
careful thought, then the fading out text box would suddenly gain the
font of the new one. A kludge solution to this was implemented by simply
blocking the script until the existing text box faded out before
switching the font or RTL, and shipped for 2.4.0.
However, a better solution is to simply bake the font flags in to the
text box, so that way, if the level font switches, then the text box
keeps its font.
This is only for custom levels, because in the main game, the font in a
text box needs to be able to change depending on language. But it seems
like custom level translations weren't much on the roadmap, and so even
the existing hack didn't support changing the font based on translation
(even though translation of custom level cutscenes is supported). So
baking the font flags into the text box here doesn't make things any
worse.
It also makes things better, arguably, by allowing multiple text boxes
to exist on screen at once with different fonts.
Maybe in the future we'll need a flag that specifies that the font
should change depending on language if a translation in said language
exists for the text box, or something like that.
For people that want to override the fonts of every existing text box on
screen, you can specify "all" as the second parameter of setfont or
setrtl to do so.
2024-01-22 07:09:35 +01:00
graphics . textboxes [ i ] . print_flags = flags ;
graphics . textboxes [ i ] . resize ( ) ;
2023-08-28 03:19:57 +02:00
}
2023-01-20 04:57:06 +01:00
}
}
2021-09-07 03:56:39 +02:00
position + + ;
}
else
{
running = false ;
}
// Don't increment if we're at the max, signed int overflow is UB
if ( execution_counter = = SHRT_MAX )
{
// We must be in an infinite loop
vlog_warn ( " Warning: execution counter got to %i, stopping script " , SHRT_MAX ) ;
running = false ;
}
else
{
execution_counter + + ;
}
}
if ( scriptdelay > 0 )
{
scriptdelay - - ;
}
2020-01-01 21:29:24 +01:00
}
2021-08-07 05:57:34 +02:00
static void gotoerrorloadinglevel ( void )
{
2023-05-17 20:45:21 +02:00
game . quittomenu ( ) ;
2021-09-07 03:56:39 +02:00
game . createmenu ( Menu : : errorloadinglevel ) ;
music . currentsong = - 1 ; /* otherwise music.play won't work */
2023-05-24 03:37:32 +02:00
music . play ( Music_PRESENTINGVVVVVV ) ;
2021-08-07 05:57:34 +02:00
}
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
# define DECLARE_MODE_FUNC(funcname, modename) \
static bool funcname ( const enum StartMode mode ) \
{ \
return mode > = Start_FIRST_ # # modename & & mode < = Start_LAST_ # # modename ; \
}
DECLARE_MODE_FUNC ( is_no_death_mode , NODEATHMODE )
DECLARE_MODE_FUNC ( is_intermission_1 , INTERMISSION1 )
DECLARE_MODE_FUNC ( is_intermission_2 , INTERMISSION2 )
# undef DECLARE_MODE_FUNC
void scriptclass : : startgamemode ( const enum StartMode mode )
2020-01-01 21:29:24 +01:00
{
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
if ( mode = = Start_QUIT )
2021-09-07 03:56:39 +02:00
{
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
VVV_exit ( 0 ) ;
}
2021-09-07 03:56:39 +02:00
2022-12-30 00:00:01 +01:00
struct
{
bool initialized ;
int size ;
int cx ;
int cy ;
int w ;
int h ;
}
player_hitbox ;
SDL_zero ( player_hitbox ) ;
if ( GlitchrunnerMode_less_than_or_equal ( Glitchrunner2_2 ) )
{
/* Preserve player hitbox */
const int player_idx = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( player_idx , obj . entities ) )
{
const entclass * player = & obj . entities [ player_idx ] ;
player_hitbox . initialized = true ;
player_hitbox . size = player - > size ;
player_hitbox . cx = player - > cx ;
player_hitbox . cy = player - > cy ;
player_hitbox . w = player - > w ;
player_hitbox . h = player - > h ;
}
}
2023-03-27 22:53:13 +02:00
/* Containers which need to be reset before gameplay starts
* ex . before custom levels get loaded */
switch ( mode )
{
case Start_EDITORPLAYTESTING :
break ;
default :
textbox_colours . clear ( ) ;
add_default_colours ( ) ;
break ;
}
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
hardreset ( ) ;
2021-09-07 03:56:39 +02:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
if ( mode = = Start_EDITOR )
{
game . gamestate = EDITORMODE ;
}
else
{
2021-09-07 03:56:39 +02:00
game . gamestate = GAMEMODE ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
}
2021-09-07 03:56:39 +02:00
2023-01-21 19:06:30 +01:00
// Font handling
2023-01-21 03:27:55 +01:00
switch ( mode )
{
case Start_EDITORPLAYTESTING :
case Start_CUSTOM :
case Start_CUSTOM_QUICKSAVE :
break ;
2023-01-21 19:06:30 +01:00
case Start_EDITOR :
font : : set_level_font_new ( ) ;
break ;
2023-01-21 03:27:55 +01:00
default :
font : : set_level_font_interface ( ) ;
}
2024-01-07 05:53:51 +01:00
/* Indicate invincibility, glitchrunner, etc. for all modes except these */
switch ( mode )
{
case Start_EDITOR :
case Start_CUTSCENETEST :
break ;
case Start_QUIT :
VVV_unreachable ( ) ;
default :
/* If there's editor return text, make this show up after it */
game . mode_indicator_timer = ed . return_message_timer + 2000 ;
game . old_mode_indicator_timer = game . mode_indicator_timer ;
}
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
game . jumpheld = true ;
2021-09-07 03:56:39 +02:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
switch ( mode )
{
case Start_MAINGAME :
case Start_MAINGAME_TELESAVE :
case Start_MAINGAME_QUICKSAVE :
case Start_NODEATHMODE_WITHCUTSCENES :
case Start_NODEATHMODE_NOCUTSCENES :
game . nodeathmode = is_no_death_mode ( mode ) ;
game . nocutscenes = ( mode = = Start_NODEATHMODE_NOCUTSCENES ) ;
2021-09-07 03:56:39 +02:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
game . start ( ) ;
2021-09-07 03:56:39 +02:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
switch ( mode )
2021-09-07 03:56:39 +02:00
{
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_MAINGAME_TELESAVE :
game . loadtele ( ) ;
graphics . fademode = FADE_START_FADEIN ;
break ;
case Start_MAINGAME_QUICKSAVE :
game . loadquick ( ) ;
graphics . fademode = FADE_START_FADEIN ;
break ;
default :
graphics . showcutscenebars = true ;
graphics . setbars ( 320 ) ;
load ( " intro " ) ;
2024-08-30 19:19:27 +02:00
if ( ! game . nocompetitive ( ) )
{
game . nodeatheligible = true ;
vlog_debug ( " NDM trophy is eligible. " ) ;
}
else
{
game . invalidate_ndm_trophy ( ) ;
}
2021-09-07 03:56:39 +02:00
}
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_TIMETRIAL_SPACESTATION1 :
case Start_TIMETRIAL_LABORATORY :
case Start_TIMETRIAL_TOWER :
case Start_TIMETRIAL_SPACESTATION2 :
case Start_TIMETRIAL_WARPZONE :
case Start_TIMETRIAL_FINALLEVEL :
2021-09-07 03:56:39 +02:00
music . fadeout ( ) ;
game . nocutscenes = true ;
game . intimetrial = true ;
game . timetrialcountdown = 150 ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
game . timetriallevel = mode - Start_FIRST_TIMETRIAL ;
2021-09-07 03:56:39 +02:00
2022-12-30 22:57:24 +01:00
if ( map . invincibility )
{
game . sabotage_time_trial ( ) ;
}
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
switch ( mode )
2021-09-07 03:56:39 +02:00
{
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_TIMETRIAL_SPACESTATION1 :
2021-09-07 03:56:39 +02:00
game . timetrialpar = 75 ;
game . timetrialshinytarget = 2 ;
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_TIMETRIAL_LABORATORY :
2021-09-07 03:56:39 +02:00
game . timetrialpar = 165 ;
game . timetrialshinytarget = 4 ;
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_TIMETRIAL_TOWER :
2021-09-07 03:56:39 +02:00
game . timetrialpar = 105 ;
game . timetrialshinytarget = 2 ;
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_TIMETRIAL_SPACESTATION2 :
2021-09-07 03:56:39 +02:00
game . timetrialpar = 200 ;
game . timetrialshinytarget = 5 ;
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_TIMETRIAL_WARPZONE :
2021-09-07 03:56:39 +02:00
game . timetrialpar = 120 ;
game . timetrialshinytarget = 1 ;
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_TIMETRIAL_FINALLEVEL :
2021-09-07 03:56:39 +02:00
game . timetrialpar = 135 ;
game . timetrialshinytarget = 1 ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
map . finalmode = true ;
2021-09-07 03:56:39 +02:00
map . final_colormode = false ;
map . final_mapcol = 0 ;
map . final_colorframe = 0 ;
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
default :
VVV_unreachable ( ) ;
2021-09-07 03:56:39 +02:00
}
game . starttrial ( game . timetriallevel ) ;
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 . translator_exploring )
{
game . timetrialcountdown = 0 ;
game . timetrialparlost = true ;
SDL_memset ( map . explored , true , sizeof ( map . explored ) ) ;
}
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
graphics . fademode = FADE_START_FADEIN ;
2021-09-07 03:56:39 +02:00
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_SECRETLAB :
2021-09-07 03:56:39 +02:00
game . startspecial ( 0 ) ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
/* Unlock the entire map */
2021-09-07 03:56:39 +02:00
SDL_memset ( obj . collect , true , sizeof ( obj . collect [ 0 ] ) * 20 ) ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
/* Give all 20 trinkets */
2021-09-07 03:56:39 +02:00
SDL_memset ( map . explored , true , sizeof ( map . explored ) ) ;
i = 400 ; /* previously a nested for-loop set this */
game . insecretlab = true ;
map . showteleporters = true ;
2023-05-24 03:37:32 +02:00
music . play ( Music_PIPEDREAM ) ;
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
graphics . fademode = FADE_START_FADEIN ;
2021-09-07 03:56:39 +02:00
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_INTERMISSION1_VITELLARY :
case Start_INTERMISSION1_VERMILION :
case Start_INTERMISSION1_VERDIGRIS :
case Start_INTERMISSION1_VICTORIA :
case Start_INTERMISSION2_VITELLARY :
case Start_INTERMISSION2_VERMILION :
case Start_INTERMISSION2_VERDIGRIS :
case Start_INTERMISSION2_VICTORIA :
2021-09-07 03:56:39 +02:00
music . fadeout ( ) ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
switch ( mode )
2021-09-07 03:56:39 +02:00
{
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_INTERMISSION1_VITELLARY :
case Start_INTERMISSION2_VITELLARY :
game . lastsaved = 2 ;
break ;
case Start_INTERMISSION1_VERMILION :
case Start_INTERMISSION2_VERMILION :
game . lastsaved = 3 ;
break ;
case Start_INTERMISSION1_VERDIGRIS :
case Start_INTERMISSION2_VERDIGRIS :
game . lastsaved = 4 ;
break ;
case Start_INTERMISSION1_VICTORIA :
case Start_INTERMISSION2_VICTORIA :
game . lastsaved = 5 ;
break ;
default :
VVV_unreachable ( ) ;
2021-09-07 03:56:39 +02:00
}
game . crewstats [ game . lastsaved ] = true ;
game . inintermission = true ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
if ( is_intermission_1 ( mode ) )
2021-09-07 03:56:39 +02:00
{
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
game . companion = 11 ;
game . supercrewmate = true ;
game . scmprogress = 0 ;
2021-09-07 03:56:39 +02:00
}
map . finalmode = true ;
map . final_colormode = false ;
map . final_mapcol = 0 ;
map . final_colorframe = 0 ;
game . startspecial ( 1 ) ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
if ( is_intermission_1 ( mode ) )
2021-09-07 03:56:39 +02:00
{
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
load ( " intermission_1 " ) ;
2021-09-07 03:56:39 +02:00
}
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
else if ( is_intermission_2 ( mode ) )
2021-09-07 03:56:39 +02:00
{
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
load ( " intermission_2 " ) ;
2021-09-07 03:56:39 +02:00
}
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_EDITOR :
2021-09-07 03:56:39 +02:00
cl . reset ( ) ;
ed . reset ( ) ;
music . fadeout ( ) ;
map . custommode = true ;
map . custommodeforreal = false ;
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
graphics . fademode = FADE_START_FADEIN ;
2021-09-07 03:56:39 +02:00
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_EDITORPLAYTESTING :
2021-09-07 03:56:39 +02:00
music . fadeout ( ) ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
2021-09-07 03:56:39 +02:00
//If warpdir() is used during playtesting, we need to set it back after!
for ( int j = 0 ; j < cl . maxheight ; j + + )
{
for ( int i = 0 ; i < cl . maxwidth ; i + + )
{
ed . kludgewarpdir [ i + ( j * cl . maxwidth ) ] = cl . roomproperties [ i + ( j * cl . maxwidth ) ] . warpdir ;
}
}
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
game . customstart ( ) ;
2021-09-07 03:56:39 +02:00
ed . ghosts . clear ( ) ;
map . custommode = true ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
map . custommodeforreal = false ;
2022-11-30 17:17:05 +01:00
map . customshowmm = true ;
if ( cl . levmusic > 0 )
{
2021-09-07 03:56:39 +02:00
music . play ( cl . levmusic ) ;
2022-11-30 17:17:05 +01:00
}
else
{
music . currentsong = - 1 ;
2021-09-07 03:56:39 +02:00
}
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_CUSTOM :
case Start_CUSTOM_QUICKSAVE :
2021-09-07 03:56:39 +02:00
{
2023-05-17 21:12:36 +02:00
map . custommodeforreal = true ;
map . custommode = true ;
2021-09-07 03:56:39 +02:00
std : : string filename = std : : string ( cl . ListOfMetaData [ game . playcustomlevel ] . filename ) ;
if ( ! cl . load ( filename ) )
{
gotoerrorloadinglevel ( ) ;
2023-05-17 17:42:50 +02:00
return ;
2021-09-07 03:56:39 +02:00
}
cl . findstartpoint ( ) ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
map . customshowmm = true ;
2021-09-07 03:56:39 +02:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
music . fadeout ( ) ;
game . customstart ( ) ;
2021-09-07 03:56:39 +02:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
switch ( mode )
2021-09-07 03:56:39 +02:00
{
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_CUSTOM :
if ( cl . levmusic > 0 )
{
music . play ( cl . levmusic ) ;
}
else
{
music . currentsong = - 1 ;
}
break ;
case Start_CUSTOM_QUICKSAVE :
game . customloadquick ( cl . ListOfMetaData [ game . playcustomlevel ] . filename ) ;
break ;
default :
VVV_unreachable ( ) ;
2021-09-07 03:56:39 +02: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
graphics . fademode = FADE_START_FADEIN ;
2021-09-07 03:56:39 +02:00
break ;
}
2022-12-24 04:16:56 +01:00
case Start_CUTSCENETEST :
music . fadeout ( ) ;
game . translator_exploring = true ;
game . translator_cutscene_test = true ;
game . startspecial ( 2 ) ;
game . mapheld = true ;
loadtest ( game . cutscenetest_menu_play_id ) ;
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
case Start_QUIT :
VVV_unreachable ( ) ;
2023-02-04 09:14:04 +01:00
break ;
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
}
2021-09-07 03:56:39 +02:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
game . gravitycontrol = game . savegc ;
graphics . flipmode = graphics . setflipmode ;
2021-09-07 03:56:39 +02:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
if ( ! map . custommode & & ! graphics . setflipmode )
{
/* Invalidate Flip Mode trophy */
obj . flags [ 73 ] = true ;
}
2022-12-30 00:00:01 +01:00
obj . entities . clear ( ) ;
obj . createentity ( game . savex , game . savey , 0 , 0 ) ;
if ( player_hitbox . initialized )
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
{
2022-12-30 00:00:01 +01:00
/* Restore player hitbox */
const int player_idx = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( player_idx , obj . entities ) )
{
entclass * player = & obj . entities [ player_idx ] ;
player - > size = player_hitbox . size ;
player - > cx = player_hitbox . cx ;
player - > cy = player_hitbox . cy ;
player - > w = player_hitbox . w ;
player - > h = player_hitbox . h ;
}
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
}
2022-12-30 00:00:01 +01:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
map . resetplayer ( ) ;
map . gotoroom ( game . saverx , game . savery ) ;
map . initmapdata ( ) ;
if ( map . custommode )
{
cl . generatecustomminimap ( ) ;
}
2021-09-07 03:56:39 +02:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
/* If we are spawning in a tower, ensure variables are set correctly */
if ( map . towermode )
{
map . resetplayer ( ) ;
2021-09-07 03:56:39 +02:00
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
i = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( i , obj . entities ) )
2021-09-07 03:56:39 +02:00
{
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
map . ypos = obj . entities [ i ] . yp - 120 ;
map . oldypos = map . ypos ;
2021-09-07 03:56:39 +02:00
}
Refactor scriptclass::startgamemode
This overhauls scriptclass::gamemode massively.
The first change is that it now uses an enum, and enforces using that
enum via using its type instead of an int. This is because whenever
you're reading any calls to startgamemode, you have no idea what magic
number actually corresponds to what unless you read startgamemode
itself. And when you do read it, not every case is commented adequately,
so you'd have to do more work to figure out what each case is. With the
enum, it's obvious and self-evident, and that also removes the need for
all the comments in the function too. Some math is still done on mode
variables (to simplify time trial code), but it's okay, we can just cast
between int and the enum as needed.
The second is that common code is now de-duplicated. There was a lot of
code that every case does, such as calling hardreset, setting Flip Mode,
resetting the player, calling gotoroom and so on.
Now some code may be duplicated between cases, so I've tried to group up
similar cases where possible (most notable example is grouping up the
main game and No Death Mode cases together). But some code still might
be duplicated in the end. Which is okay - I could've tried to
de-duplicate it further but that just results in logic relevant to a
specific case that's located far from the actual case itself. It's much
better to leave things like setting fademode or loading scripts in the
case itself.
This also fixes a bug since 2.3 where playing No Death Mode (and never
opening and closing the options menu) and beating it would also give you
the Flip Mode trophy, since turning on the flag to invalidate Flip Mode
in startgamemode only happened for the main game cases and in previous
versions the game relied upon this flag being set when using a
teleporter for some reason (which I removed in 2.3). Now instead of
specifying it per case, I just do a !map.custommode check instead so it
covers every single case at once.
2022-12-29 23:01:36 +01:00
map . setbgobjlerp ( graphics . towerbg ) ;
map . cameramode = 0 ;
map . colsuperstate = 0 ;
2021-09-07 03:56:39 +02:00
}
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 scriptclass : : teleport ( void )
2020-01-01 21:29:24 +01:00
{
2021-09-07 03:56:39 +02:00
//er, ok! Teleport to a new area, so!
//A general rule of thumb: if you teleport with a companion, get rid of them!
game . companion = 0 ;
i = obj . getplayer ( ) ; //less likely to have a serious collision error if the player is centered
if ( INBOUNDS_VEC ( i , obj . entities ) )
{
obj . entities [ i ] . xp = 150 ;
obj . entities [ i ] . yp = 110 ;
if ( game . teleport_to_x = = 17 & & game . teleport_to_y = = 17 ) obj . entities [ i ] . xp = 88 ; //prevent falling!
obj . entities [ i ] . lerpoldxp = obj . entities [ i ] . xp ;
obj . entities [ i ] . lerpoldyp = obj . entities [ i ] . yp ;
}
if ( game . teleportscript = = " levelonecomplete " )
{
game . teleport_to_x = 2 ;
game . teleport_to_y = 11 ;
}
else if ( game . teleportscript = = " gamecomplete " )
{
game . teleport_to_x = 2 ;
game . teleport_to_y = 11 ;
}
game . gravitycontrol = 0 ;
map . gotoroom ( 100 + game . teleport_to_x , 100 + game . teleport_to_y ) ;
j = obj . getteleporter ( ) ;
if ( INBOUNDS_VEC ( j , obj . entities ) )
{
obj . entities [ j ] . state = 2 ;
}
game . teleport_to_new_area = false ;
if ( INBOUNDS_VEC ( j , obj . entities ) )
{
game . savepoint = obj . entities [ j ] . para ;
game . savex = obj . entities [ j ] . xp + 44 ;
game . savey = obj . entities [ j ] . yp + 44 ;
}
game . savegc = 0 ;
game . saverx = game . roomx ;
game . savery = game . roomy ;
int player = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( player , obj . entities ) )
{
game . savedir = obj . entities [ player ] . dir ;
}
if ( game . teleport_to_x = = 0 & & game . teleport_to_y = = 0 )
{
2022-12-07 00:20:48 +01:00
game . setstate ( 4020 ) ;
2021-09-07 03:56:39 +02:00
}
else if ( game . teleport_to_x = = 0 & & game . teleport_to_y = = 16 )
{
2022-12-07 00:20:48 +01:00
game . setstate ( 4030 ) ;
2021-09-07 03:56:39 +02:00
}
else if ( game . teleport_to_x = = 7 & & game . teleport_to_y = = 9 )
{
2022-12-07 00:20:48 +01:00
game . setstate ( 4040 ) ;
2021-09-07 03:56:39 +02:00
}
else if ( game . teleport_to_x = = 8 & & game . teleport_to_y = = 11 )
{
2022-12-07 00:20:48 +01:00
game . setstate ( 4050 ) ;
2021-09-07 03:56:39 +02:00
}
else if ( game . teleport_to_x = = 14 & & game . teleport_to_y = = 19 )
{
2022-12-07 00:20:48 +01:00
game . setstate ( 4030 ) ;
2021-09-07 03:56:39 +02:00
}
else if ( game . teleport_to_x = = 17 & & game . teleport_to_y = = 12 )
{
2022-12-07 00:20:48 +01:00
game . setstate ( 4020 ) ;
2021-09-07 03:56:39 +02:00
}
else if ( game . teleport_to_x = = 17 & & game . teleport_to_y = = 17 )
{
2022-12-07 00:20:48 +01:00
game . setstate ( 4020 ) ;
2021-09-07 03:56:39 +02:00
}
else if ( game . teleport_to_x = = 18 & & game . teleport_to_y = = 7 )
{
2022-12-07 00:20:48 +01:00
game . setstate ( 4060 ) ;
2021-09-07 03:56:39 +02:00
}
else
{
2022-12-07 00:20:48 +01:00
game . setstate ( 4010 ) ;
2021-09-07 03:56:39 +02:00
}
if ( game . teleportscript ! = " " )
{
2022-12-07 00:20:48 +01:00
game . setstate ( 0 ) ;
2021-09-07 03:56:39 +02:00
load ( game . teleportscript ) ;
game . teleportscript = " " ;
}
else
{
//change music based on location
if ( game . teleport_to_x = = 2 & & game . teleport_to_y = = 11 )
{
/* Special case: Ship music needs to be set here;
* ship teleporter on music map is - 1 for jukebox . */
2023-05-24 03:37:32 +02:00
music . niceplay ( Music_PASSIONFOREXPLORING ) ;
2021-09-07 03:56:39 +02:00
}
game . savetele_textbox ( ) ;
}
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 scriptclass : : hardreset ( void )
2020-01-01 21:29:24 +01:00
{
2021-09-07 03:56:39 +02:00
const bool version2_2 = GlitchrunnerMode_less_than_or_equal ( Glitchrunner2_2 ) ;
2022-11-14 23:10:24 +01:00
if ( game . seed_use_sdl_getticks )
{
/* The RNG is 32-bit. We don't _really_ need 64-bit... */
xoshiro_seed ( ( Uint32 ) SDL_GetTicks64 ( ) ) ;
}
else
{
xoshiro_seed ( game . framecounter ) ;
}
2021-09-07 03:56:39 +02:00
//Game:
game . hascontrol = true ;
game . gravitycontrol = 0 ;
game . teleport = false ;
game . companion = 0 ;
if ( ! version2_2 )
{
// Ironically, resetting more variables makes the janky fadeout system in glitchrunnermode even more glitchy
game . roomx = 0 ;
game . roomy = 0 ;
}
game . prevroomx = 0 ;
game . prevroomy = 0 ;
game . teleport_to_new_area = false ;
game . teleport_to_x = 0 ;
game . teleport_to_y = 0 ;
game . teleportscript = " " ;
game . tapleft = 0 ;
game . tapright = 0 ;
game . startscript = false ;
game . newscript = " " ;
game . alarmon = false ;
game . alarmdelay = 0 ;
game . blackout = false ;
game . useteleporter = false ;
game . teleport_to_teleporter = 0 ;
game . nodeathmode = false ;
game . nocutscenes = false ;
2024-08-30 19:19:27 +02:00
game . nodeatheligible = false ;
2021-09-07 03:56:39 +02:00
for ( i = 0 ; i < ( int ) SDL_arraysize ( game . crewstats ) ; i + + )
{
game . crewstats [ i ] = false ;
}
game . crewstats [ 0 ] = true ;
game . lastsaved = 0 ;
game . deathcounts = 0 ;
game . gameoverdelay = 0 ;
game . resetgameclock ( ) ;
game . gamesaved = false ;
game . gamesavefailed = false ;
game . savetime = " 00:00 " ;
game . savetrinkets = 0 ;
if ( ! version2_2 )
{
// Ironically, resetting more variables makes the janky fadeout system in glitchrunnermode even more glitchy
game . saverx = 0 ;
game . savery = 0 ;
2022-12-29 21:15:30 +01:00
game . savex = 0 ;
game . savey = 0 ;
game . savegc = 0 ;
2021-09-07 03:56:39 +02:00
}
game . savecolour = 0 ;
game . intimetrial = false ;
game . timetrialcountdown = 0 ;
game . timetrialshinytarget = 0 ;
game . timetrialparlost = false ;
game . timetrialpar = 0 ;
2022-12-30 22:57:24 +01:00
game . timetrialcheater = false ;
2021-09-07 03:56:39 +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
game . translator_exploring = game . start_translator_exploring ;
game . start_translator_exploring = false ;
game . translator_exploring_allowtele = false ;
2022-12-24 04:16:56 +01:00
game . translator_cutscene_test = false ;
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
2021-09-07 03:56:39 +02:00
game . totalflips = 0 ;
2023-09-04 15:27:29 +02:00
game . hardestroom = " Welcome Aboard " ;
2021-09-07 03:56:39 +02:00
game . hardestroomdeaths = 0 ;
2023-09-04 15:27:29 +02:00
game . hardestroom_x = 13 ;
game . hardestroom_y = 5 ;
game . hardestroom_specialname = false ;
game . hardestroom_finalstretch = false ;
2021-09-07 03:56:39 +02:00
game . currentroomdeaths = 0 ;
game . swnmode = false ;
game . swntimer = 0 ;
2023-06-05 08:24:31 +02:00
game . swngame = SWN_NONE ; // Not playing sine wave ninja!
2021-09-07 03:56:39 +02:00
game . swnstate = 0 ;
game . swnstate2 = 0 ;
game . swnstate3 = 0 ;
game . swnstate4 = 0 ;
game . swndelay = 0 ;
game . swndeaths = 0 ;
game . supercrewmate = false ;
game . scmhurt = false ;
game . scmprogress = 0 ;
game . swncolstate = 0 ;
game . swncoldelay = 0 ;
game . swnrank = 0 ;
game . swnmessage = 0 ;
game . creditposx = 0 ;
game . creditposy = 0 ;
game . creditposdelay = 0 ;
game . inintermission = false ;
game . insecretlab = false ;
2022-12-07 00:20:48 +01:00
game . unlockstate ( ) ;
2021-09-07 03:56:39 +02:00
game . state = 0 ;
game . statedelay = 0 ;
game . hascontrol = true ;
if ( ! GlitchrunnerMode_less_than_or_equal ( Glitchrunner2_0 ) )
{
// Keep the "- Press ACTION to advance text -" prompt around,
// apparently the speedrunners call it the "text storage" glitch
game . advancetext = false ;
}
game . pausescript = false ;
game . completestop = false ;
game . flashlight = 0 ;
game . screenshake = 0 ;
game . activeactivity = - 1 ;
game . act_fade = 5 ;
game . disabletemporaryaudiopause = true ;
2021-12-18 08:39:26 +01:00
game . ingame_titlemode = false ;
2021-09-07 03:56:39 +02:00
//dwgraphicsclass
graphics . backgrounddrawn = false ;
2021-09-13 06:02:15 +02:00
graphics . textboxes . clear ( ) ;
2021-09-07 03:56:39 +02:00
graphics . flipmode = false ; //This will be reset if needs be elsewhere
graphics . showcutscenebars = false ;
graphics . setbars ( 0 ) ;
//mapclass
map . warpx = false ;
map . warpy = false ;
map . showteleporters = false ;
map . showtargets = false ;
map . showtrinkets = false ;
map . finalmode = false ;
map . finalstretch = false ;
map . final_colormode = false ;
map . final_colorframe = 0 ;
map . final_colorframedelay = 0 ;
map . final_mapcol = 0 ;
map . final_aniframe = 0 ;
map . final_aniframedelay = 0 ;
map . rcol = 0 ;
map . custommode = false ;
map . custommodeforreal = false ;
if ( ! version2_2 )
{
// Ironically, resetting more variables makes the janky fadeout system even more glitchy
map . towermode = false ;
}
map . cameraseekframe = 0 ;
map . resumedelay = 0 ;
graphics . towerbg . scrolldir = 0 ;
map . customshowmm = true ;
SDL_memset ( map . roomdeaths , 0 , sizeof ( map . roomdeaths ) ) ;
SDL_memset ( map . roomdeathsfinal , 0 , sizeof ( map . roomdeathsfinal ) ) ;
map . resetmap ( ) ;
//entityclass
obj . nearelephant = false ;
obj . upsetmode = false ;
obj . upset = 0 ;
obj . trophytext = 0 ;
obj . trophytype = 0 ;
obj . altstates = 0 ;
obj . resetallflags ( ) ;
for ( i = 0 ; i < ( int ) SDL_arraysize ( obj . customcrewmoods ) ; i + + ) {
obj . customcrewmoods [ i ] = true ;
}
SDL_memset ( obj . collect , false , sizeof ( obj . collect ) ) ;
SDL_memset ( obj . customcollect , false , sizeof ( obj . customcollect ) ) ;
i = 100 ; //previously a for-loop iterating over collect/customcollect set this to 100
int theplayer = obj . getplayer ( ) ;
if ( INBOUNDS_VEC ( theplayer , obj . entities ) ) {
obj . entities [ theplayer ] . tile = 0 ;
}
/* Disable duplicate player entities */
for ( int i = 0 ; i < ( int ) obj . entities . size ( ) ; i + + )
{
if ( obj . entities [ i ] . rule = = 0 & & i ! = theplayer )
{
obj . disableentity ( i ) ;
}
}
obj . customscript = " " ;
//Script Stuff
position = 0 ;
commands . clear ( ) ;
scriptdelay = 0 ;
scriptname = " null " ;
running = false ;
2022-02-12 23:56:27 +01:00
for ( size_t ii = 0 ; ii < NUM_SCRIPT_ARGS ; + + ii )
2021-09-07 03:56:39 +02:00
{
words [ ii ] = " " ;
2022-02-12 23:56:27 +01:00
raw_words [ ii ] = " " ;
2021-09-07 03:56:39 +02:00
}
2021-08-31 20:07:38 +02:00
obj . customactivitycolour = " " ;
obj . customactivitytext = " " ;
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
obj . customactivitypositiony = - 1 ;
2020-01-01 21:29:24 +01:00
}
2020-05-18 19:08:34 +02:00
2023-02-01 05:04:18 +01:00
bool scriptclass : : loadcustom ( const std : : string & t )
2020-05-18 19:08:34 +02:00
{
2021-09-07 03:56:39 +02:00
//this magic function breaks down the custom script and turns into real scripting!
std : : string cscriptname = " " ;
for ( size_t i = 0 ; i < t . length ( ) ; i + + ) {
if ( i > = 7 ) cscriptname + = t [ i ] ;
}
std : : string tstring ;
std : : vector < std : : string > * contents = NULL ;
for ( size_t i = 0 ; i < customscripts . size ( ) ; i + + ) {
Script & script_ = customscripts [ i ] ;
if ( script_ . name = = cscriptname ) {
contents = & script_ . contents ;
break ;
}
}
if ( contents = = NULL ) {
2023-02-01 05:04:18 +01:00
return false ;
2021-09-07 03:56:39 +02:00
}
std : : vector < std : : string > & lines = * contents ;
//Ok, we've got the relavent script segment, we do a pass to assess it, then run it!
int customcutscenemode = 0 ;
for ( size_t i = 0 ; i < lines . size ( ) ; i + + ) {
tokenize ( lines [ i ] ) ;
if ( words [ 0 ] = = " say " ) {
customcutscenemode = 1 ;
} else if ( words [ 0 ] = = " reply " ) {
customcutscenemode = 1 ;
}
}
if ( customcutscenemode = = 1 ) {
add ( " cutscene() " ) ;
add ( " untilbars() " ) ;
}
int customtextmode = 0 ;
int speakermode = 0 ; //0, terminal, numbers for crew
int squeakmode = 0 ; //default on
//Now run the script
for ( size_t i = 0 ; i < lines . size ( ) ; i + + ) {
words [ 0 ] = " nothing " ; //Default!
words [ 1 ] = " 1 " ; //Default!
tokenize ( lines [ i ] ) ;
for ( size_t ii = 0 ; ii < words [ 0 ] . length ( ) ; ii + + )
{
words [ 0 ] [ ii ] = SDL_tolower ( words [ 0 ] [ ii ] ) ;
}
if ( words [ 0 ] = = " music " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
if ( words [ 1 ] = = " 0 " ) {
tstring = " stopmusic() " ;
} else {
if ( words [ 1 ] = = " 11 " ) { tstring = " play(14) " ;
} else if ( words [ 1 ] = = " 10 " ) { tstring = " play(13) " ;
} else if ( words [ 1 ] = = " 9 " ) { tstring = " play(12) " ;
} else if ( words [ 1 ] = = " 8 " ) { tstring = " play(11) " ;
} else if ( words [ 1 ] = = " 7 " ) { tstring = " play(10) " ;
} else if ( words [ 1 ] = = " 6 " ) { tstring = " play(8) " ;
} else if ( words [ 1 ] = = " 5 " ) { tstring = " play(6) " ;
} else { tstring = " play( " + words [ 1 ] + " ) " ; }
}
add ( tstring ) ;
} else if ( words [ 0 ] = = " playremix " ) {
add ( " play(15) " ) ;
} else if ( words [ 0 ] = = " flash " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( " flash(5) " ) ;
add ( " shake(20) " ) ;
add ( " playef(9) " ) ;
} else if ( words [ 0 ] = = " sad " | | words [ 0 ] = = " cry " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
if ( words [ 1 ] = = " player " ) {
add ( " changemood(player,1) " ) ;
} else if ( words [ 1 ] = = " cyan " | | words [ 1 ] = = " viridian " | | words [ 1 ] = = " 1 " ) {
add ( " changecustommood(customcyan,1) " ) ;
} else if ( words [ 1 ] = = " purple " | | words [ 1 ] = = " violet " | | words [ 1 ] = = " pink " | | words [ 1 ] = = " 2 " ) {
add ( " changecustommood(purple,1) " ) ;
} else if ( words [ 1 ] = = " yellow " | | words [ 1 ] = = " vitellary " | | words [ 1 ] = = " 3 " ) {
add ( " changecustommood(yellow,1) " ) ;
} else if ( words [ 1 ] = = " red " | | words [ 1 ] = = " vermilion " | | words [ 1 ] = = " 4 " ) {
add ( " changecustommood(red,1) " ) ;
} else if ( words [ 1 ] = = " green " | | words [ 1 ] = = " verdigris " | | words [ 1 ] = = " 5 " ) {
add ( " changecustommood(green,1) " ) ;
} else if ( words [ 1 ] = = " blue " | | words [ 1 ] = = " victoria " | | words [ 1 ] = = " 6 " ) {
add ( " changecustommood(blue,1) " ) ;
} else if ( words [ 1 ] = = " all " | | words [ 1 ] = = " everybody " | | words [ 1 ] = = " everyone " ) {
add ( " changemood(player,1) " ) ;
add ( " changecustommood(customcyan,1) " ) ;
add ( " changecustommood(purple,1) " ) ;
add ( " changecustommood(yellow,1) " ) ;
add ( " changecustommood(red,1) " ) ;
add ( " changecustommood(green,1) " ) ;
add ( " changecustommood(blue,1) " ) ;
} else {
add ( " changemood(player,1) " ) ;
}
if ( squeakmode = = 0 ) add ( " squeak(cry) " ) ;
} else if ( words [ 0 ] = = " happy " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
if ( words [ 1 ] = = " player " ) {
add ( " changemood(player,0) " ) ;
if ( squeakmode = = 0 ) add ( " squeak(player) " ) ;
} else if ( words [ 1 ] = = " cyan " | | words [ 1 ] = = " viridian " | | words [ 1 ] = = " 1 " ) {
add ( " changecustommood(customcyan,0) " ) ;
if ( squeakmode = = 0 ) add ( " squeak(player) " ) ;
} else if ( words [ 1 ] = = " purple " | | words [ 1 ] = = " violet " | | words [ 1 ] = = " pink " | | words [ 1 ] = = " 2 " ) {
add ( " changecustommood(purple,0) " ) ;
if ( squeakmode = = 0 ) add ( " squeak(purple) " ) ;
} else if ( words [ 1 ] = = " yellow " | | words [ 1 ] = = " vitellary " | | words [ 1 ] = = " 3 " ) {
add ( " changecustommood(yellow,0) " ) ;
if ( squeakmode = = 0 ) add ( " squeak(yellow) " ) ;
} else if ( words [ 1 ] = = " red " | | words [ 1 ] = = " vermilion " | | words [ 1 ] = = " 4 " ) {
add ( " changecustommood(red,0) " ) ;
if ( squeakmode = = 0 ) add ( " squeak(red) " ) ;
} else if ( words [ 1 ] = = " green " | | words [ 1 ] = = " verdigris " | | words [ 1 ] = = " 5 " ) {
add ( " changecustommood(green,0) " ) ;
if ( squeakmode = = 0 ) add ( " squeak(green) " ) ;
} else if ( words [ 1 ] = = " blue " | | words [ 1 ] = = " victoria " | | words [ 1 ] = = " 6 " ) {
add ( " changecustommood(blue,0) " ) ;
if ( squeakmode = = 0 ) add ( " squeak(blue) " ) ;
} else if ( words [ 1 ] = = " all " | | words [ 1 ] = = " everybody " | | words [ 1 ] = = " everyone " ) {
add ( " changemood(player,0) " ) ;
add ( " changecustommood(customcyan,0) " ) ;
add ( " changecustommood(purple,0) " ) ;
add ( " changecustommood(yellow,0) " ) ;
add ( " changecustommood(red,0) " ) ;
add ( " changecustommood(green,0) " ) ;
add ( " changecustommood(blue,0) " ) ;
} else {
add ( " changemood(player,0) " ) ;
if ( squeakmode = = 0 ) add ( " squeak(player) " ) ;
}
} else if ( words [ 0 ] = = " squeak " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
if ( words [ 1 ] = = " player " ) {
add ( " squeak(player) " ) ;
} else if ( words [ 1 ] = = " cyan " | | words [ 1 ] = = " viridian " | | words [ 1 ] = = " 1 " ) {
add ( " squeak(player) " ) ;
} else if ( words [ 1 ] = = " purple " | | words [ 1 ] = = " violet " | | words [ 1 ] = = " pink " | | words [ 1 ] = = " 2 " ) {
add ( " squeak(purple) " ) ;
} else if ( words [ 1 ] = = " yellow " | | words [ 1 ] = = " vitellary " | | words [ 1 ] = = " 3 " ) {
add ( " squeak(yellow) " ) ;
} else if ( words [ 1 ] = = " red " | | words [ 1 ] = = " vermilion " | | words [ 1 ] = = " 4 " ) {
add ( " squeak(red) " ) ;
} else if ( words [ 1 ] = = " green " | | words [ 1 ] = = " verdigris " | | words [ 1 ] = = " 5 " ) {
add ( " squeak(green) " ) ;
} else if ( words [ 1 ] = = " blue " | | words [ 1 ] = = " victoria " | | words [ 1 ] = = " 6 " ) {
add ( " squeak(blue) " ) ;
} else if ( words [ 1 ] = = " cry " | | words [ 1 ] = = " sad " ) {
add ( " squeak(cry) " ) ;
} else if ( words [ 1 ] = = " on " ) {
squeakmode = 0 ;
} else if ( words [ 1 ] = = " off " ) {
squeakmode = 1 ;
}
} else if ( words [ 0 ] = = " delay " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( lines [ i ] ) ;
} else if ( words [ 0 ] = = " flag " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( lines [ i ] ) ;
} else if ( words [ 0 ] = = " map " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( " custom " + lines [ i ] ) ;
} else if ( words [ 0 ] = = " warpdir " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( lines [ i ] ) ;
} else if ( words [ 0 ] = = " ifwarp " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( lines [ i ] ) ;
} else if ( words [ 0 ] = = " iftrinkets " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( " custom " + lines [ i ] ) ;
} else if ( words [ 0 ] = = " ifflag " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( " custom " + lines [ i ] ) ;
} else if ( words [ 0 ] = = " iftrinketsless " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( " custom " + lines [ i ] ) ;
2022-12-30 22:57:24 +01:00
} else if ( words [ 0 ] = = " textcase " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( lines [ i ] ) ;
} else if ( words [ 0 ] = = " iflang " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( lines [ i ] ) ;
} else if ( words [ 0 ] = = " loadtext " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( lines [ i ] ) ;
2023-01-20 04:57:06 +01:00
} else if ( words [ 0 ] = = " setfont " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( lines [ i ] ) ;
2024-01-03 20:43:37 +01:00
} else if ( words [ 0 ] = = " setrtl " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( lines [ i ] ) ;
2021-09-07 03:56:39 +02:00
} else if ( words [ 0 ] = = " destroy " ) {
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
add ( lines [ i ] ) ;
} else if ( words [ 0 ] = = " speaker " ) {
speakermode = 0 ;
if ( words [ 1 ] = = " gray " | | words [ 1 ] = = " grey " | | words [ 1 ] = = " terminal " | | words [ 1 ] = = " 0 " ) speakermode = 0 ;
if ( words [ 1 ] = = " cyan " | | words [ 1 ] = = " viridian " | | words [ 1 ] = = " player " | | words [ 1 ] = = " 1 " ) speakermode = 1 ;
if ( words [ 1 ] = = " purple " | | words [ 1 ] = = " violet " | | words [ 1 ] = = " pink " | | words [ 1 ] = = " 2 " ) speakermode = 2 ;
if ( words [ 1 ] = = " yellow " | | words [ 1 ] = = " vitellary " | | words [ 1 ] = = " 3 " ) speakermode = 3 ;
if ( words [ 1 ] = = " red " | | words [ 1 ] = = " vermilion " | | words [ 1 ] = = " 4 " ) speakermode = 4 ;
if ( words [ 1 ] = = " green " | | words [ 1 ] = = " verdigris " | | words [ 1 ] = = " 5 " ) speakermode = 5 ;
if ( words [ 1 ] = = " blue " | | words [ 1 ] = = " victoria " | | words [ 1 ] = = " 6 " ) speakermode = 6 ;
} else if ( words [ 0 ] = = " say " ) {
//Speakers!
if ( words [ 2 ] = = " terminal " | | words [ 2 ] = = " gray " | | words [ 2 ] = = " grey " | | words [ 2 ] = = " 0 " ) speakermode = 0 ;
if ( words [ 2 ] = = " cyan " | | words [ 2 ] = = " viridian " | | words [ 2 ] = = " player " | | words [ 2 ] = = " 1 " ) speakermode = 1 ;
if ( words [ 2 ] = = " purple " | | words [ 2 ] = = " violet " | | words [ 2 ] = = " pink " | | words [ 2 ] = = " 2 " ) speakermode = 2 ;
if ( words [ 2 ] = = " yellow " | | words [ 2 ] = = " vitellary " | | words [ 2 ] = = " 3 " ) speakermode = 3 ;
if ( words [ 2 ] = = " red " | | words [ 2 ] = = " vermilion " | | words [ 2 ] = = " 4 " ) speakermode = 4 ;
if ( words [ 2 ] = = " green " | | words [ 2 ] = = " verdigris " | | words [ 2 ] = = " 5 " ) speakermode = 5 ;
if ( words [ 2 ] = = " blue " | | words [ 2 ] = = " victoria " | | words [ 2 ] = = " 6 " ) speakermode = 6 ;
switch ( speakermode ) {
case 0 :
if ( squeakmode = = 0 ) add ( " squeak(terminal) " ) ;
add ( " text(gray,0,114, " + words [ 1 ] + " ) " ) ;
break ;
case 1 : //NOT THE PLAYER
if ( squeakmode = = 0 ) add ( " squeak(cyan) " ) ;
add ( " text(cyan,0,0, " + words [ 1 ] + " ) " ) ;
break ;
case 2 :
if ( squeakmode = = 0 ) add ( " squeak(purple) " ) ;
add ( " text(purple,0,0, " + words [ 1 ] + " ) " ) ;
break ;
case 3 :
if ( squeakmode = = 0 ) add ( " squeak(yellow) " ) ;
add ( " text(yellow,0,0, " + words [ 1 ] + " ) " ) ;
break ;
case 4 :
if ( squeakmode = = 0 ) add ( " squeak(red) " ) ;
add ( " text(red,0,0, " + words [ 1 ] + " ) " ) ;
break ;
case 5 :
if ( squeakmode = = 0 ) add ( " squeak(green) " ) ;
add ( " text(green,0,0, " + words [ 1 ] + " ) " ) ;
break ;
case 6 :
if ( squeakmode = = 0 ) add ( " squeak(blue) " ) ;
add ( " text(blue,0,0, " + words [ 1 ] + " ) " ) ;
break ;
}
2023-02-14 23:30:06 +01:00
int ti = ss_toi ( words [ 1 ] ) ;
2022-11-30 18:56:44 +01:00
int nti = ti > = 0 ? ti : 1 ;
for ( int ti2 = 0 ; ti2 < nti ; ti2 + + )
{
2021-09-07 03:56:39 +02:00
i + + ;
2022-11-30 18:56:44 +01:00
if ( INBOUNDS_VEC ( i , lines ) )
{
2021-09-07 03:56:39 +02:00
add ( lines [ i ] ) ;
}
}
switch ( speakermode ) {
case 0 : add ( " customposition(center) " ) ; break ;
case 1 : add ( " customposition(cyan,above) " ) ; break ;
case 2 : add ( " customposition(purple,above) " ) ; break ;
case 3 : add ( " customposition(yellow,above) " ) ; break ;
case 4 : add ( " customposition(red,above) " ) ; break ;
case 5 : add ( " customposition(green,above) " ) ; break ;
case 6 : add ( " customposition(blue,above) " ) ; break ;
}
add ( " speak_active " ) ;
customtextmode = 1 ;
} else if ( words [ 0 ] = = " reply " ) {
//For this version, terminal only
if ( squeakmode = = 0 ) add ( " squeak(player) " ) ;
2024-02-29 03:20:55 +01:00
add ( " text(player,0,0, " + words [ 1 ] + " ) " ) ;
2021-09-07 03:56:39 +02:00
int ti = help . Int ( words [ 1 ] . c_str ( ) ) ;
2022-11-30 00:04:46 +01:00
int nti = ti > = 0 ? ti : 1 ;
2021-09-07 03:56:39 +02:00
for ( int ti2 = 0 ; ti2 < nti ; ti2 + + ) {
i + + ;
if ( INBOUNDS_VEC ( i , lines ) ) {
add ( lines [ i ] ) ;
}
}
add ( " position(player,above) " ) ;
add ( " speak_active " ) ;
customtextmode = 1 ;
}
}
if ( customtextmode = = 1 ) { add ( " endtext " ) ; customtextmode = 0 ; }
if ( customcutscenemode = = 1 ) {
add ( " endcutscene() " ) ;
add ( " untilbars() " ) ;
}
2023-02-01 05:04:18 +01:00
return true ;
}
void scriptclass : : loadalts ( const std : : string & processed , const std : : string & raw )
{
const bool exists = load ( processed ) ;
if ( ! exists )
{
load ( raw ) ;
}
2020-05-18 19:08:34 +02:00
}
2022-12-24 04:16:56 +01:00
2023-03-22 21:30:42 +01:00
void scriptclass : : add_test_line (
const std : : string & speaker ,
const std : : string & english ,
const char textcase ,
const bool textbuttons
) {
2022-12-24 04:16:56 +01:00
if ( speaker = = " gray " )
{
add ( " squeak(terminal) " ) ;
}
else
{
add ( " squeak( " + speaker + " ) " ) ;
}
add ( " textcase( " + help . String ( textcase ) + " ) " ) ;
add ( " text( " + speaker + " ,0,0,1) " ) ;
add ( english ) ;
add ( " position(center) " ) ;
2023-03-22 21:30:42 +01:00
if ( textbuttons )
{
add ( " textbuttons() " ) ;
}
2022-12-24 04:16:56 +01:00
add ( " speak_active " ) ;
}
void scriptclass : : loadtest ( const std : : string & name )
{
// Another magic function, that turns language files into a demo script
position = 0 ;
commands . clear ( ) ;
scriptname = name ;
running = true ;
loc : : populate_cutscene_test ( name . c_str ( ) ) ;
add ( " endtext " ) ;
add ( " gamestate(3100) " ) ;
}