1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-11-16 16:09:42 +01:00
VVVVVV/desktop_version/src/Script.h

139 lines
3.1 KiB
C
Raw Normal View History

2020-01-01 21:29:24 +01:00
#ifndef SCRIPT_H
#define SCRIPT_H
#include <map>
2020-01-01 21:29:24 +01:00
#include <string>
#include <vector>
#include <SDL.h>
#define filllines(lines) commands.insert(commands.end(), lines, lines + SDL_arraysize(lines))
#ifdef SCRIPT_DEFINITION
#define TEXT_COLOUR(a) textbox_colours[a]
#else
#define TEXT_COLOUR(a) script.textbox_colours[a]
#endif
2020-01-01 21:29:24 +01:00
struct Script
{
std::string name;
std::vector<std::string> contents;
};
#define NUM_SCRIPT_ARGS 40
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
enum StartMode
{
Start_MAINGAME,
Start_MAINGAME_TELESAVE,
Start_MAINGAME_QUICKSAVE,
Start_TIMETRIAL_SPACESTATION1,
Start_TIMETRIAL_LABORATORY,
Start_TIMETRIAL_TOWER,
Start_TIMETRIAL_SPACESTATION2,
Start_TIMETRIAL_WARPZONE,
Start_TIMETRIAL_FINALLEVEL,
Start_NODEATHMODE_WITHCUTSCENES,
Start_NODEATHMODE_NOCUTSCENES,
Start_SECRETLAB,
Start_INTERMISSION1_VITELLARY,
Start_INTERMISSION1_VERMILION,
Start_INTERMISSION1_VERDIGRIS,
Start_INTERMISSION1_VICTORIA,
Start_INTERMISSION2_VITELLARY,
Start_INTERMISSION2_VERMILION,
Start_INTERMISSION2_VERDIGRIS,
Start_INTERMISSION2_VICTORIA,
Start_EDITOR,
Start_EDITORPLAYTESTING,
Start_CUSTOM,
Start_CUSTOM_QUICKSAVE,
Start_QUIT,
Start_CUTSCENETEST,
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
Start_FIRST_NODEATHMODE = Start_NODEATHMODE_WITHCUTSCENES,
Start_LAST_NODEATHMODE = Start_NODEATHMODE_NOCUTSCENES,
Start_FIRST_INTERMISSION1 = Start_INTERMISSION1_VITELLARY,
Start_LAST_INTERMISSION1 = Start_INTERMISSION1_VICTORIA,
Start_FIRST_INTERMISSION2 = Start_INTERMISSION2_VITELLARY,
Start_LAST_INTERMISSION2 = Start_INTERMISSION2_VICTORIA,
Start_FIRST_TIMETRIAL = Start_TIMETRIAL_SPACESTATION1
};
2020-01-01 21:29:24 +01:00
class scriptclass
{
public:
scriptclass(void);
2020-01-01 21:29:24 +01:00
bool load(const std::string& name);
void loadother(const char* t);
bool loadcustom(const std::string& t);
void loadalts(const std::string& processed, const std::string& raw);
2020-01-01 21:29:24 +01:00
void add_test_line(const std::string& speaker, const std::string& english, char textcase, bool textbuttons);
void loadtest(const std::string& name);
void inline add(const std::string& t)
2020-01-01 21:29:24 +01:00
{
Make `commands`, `sb`, and `hooklist` not use separate length-trackers This is a refactor that turns the script-related arrays `ed.sb`, and `ed.hooklist` into C++ vectors (`script.commands` was already a vector, it was just misused). The code handling these vectors now looks more like idiomatic C++ than sloppily-pasted pseudo-ActionScript. This removes the variables `script.scriptlength`, `ed.sblength`, and `ed.numhooks`, too. This reduces the amount of code needed to e.g. simply remove something from any of these vectors. Previously the code had to manually shift the rest of the elements down one-by-one, and doing it manually is definitely error-prone and tedious. But now we can just use fancy functions like `std::vector::erase()` and `std::remove()` to do it all in one line! Don't worry, I checked and `std::remove()` is in the C++ standard since at least 1998. This patch makes it so the `commands` vector gets cleared when `scriptclass::load()` is ran. Previously, the `commands` vector never actually properly got cleared, so there could potentially be glitches that rely on the game indexing past the bounds set by `scriptlength` but still in-bounds in the eyes of C++, and people could potentially rely on such an exploit... However, I checked, and I'm pretty sure that no such glitch previously existed at all, because the only times the vector gets indexed are when `scriptlength` is either being incremented after starting from 0 (`add()`) or when it's underneath a `position < scriptlength` conditional. Furthermore, I'm unaware of anyone who has actually found or used such an exploit, and I've been in the custom level community for 6 years. So I think it's fine.
2020-02-20 18:43:52 +01:00
commands.push_back(t);
2020-01-01 21:29:24 +01:00
}
void add_default_colours(void);
void clearcustom(void);
2020-01-01 21:29:24 +01:00
void tokenize(const std::string& t);
2020-01-01 21:29:24 +01:00
void run(void);
2020-01-01 21:29:24 +01:00
void translate_dialogue(void);
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
void startgamemode(enum StartMode mode);
2020-01-01 21:29:24 +01:00
void teleport(void);
2020-01-01 21:29:24 +01:00
void hardreset(void);
2020-01-01 21:29:24 +01:00
//Script contents
std::vector<std::string> commands;
std::string words[NUM_SCRIPT_ARGS];
2020-01-01 21:29:24 +01:00
std::vector<std::string> txt;
std::string scriptname;
Make `commands`, `sb`, and `hooklist` not use separate length-trackers This is a refactor that turns the script-related arrays `ed.sb`, and `ed.hooklist` into C++ vectors (`script.commands` was already a vector, it was just misused). The code handling these vectors now looks more like idiomatic C++ than sloppily-pasted pseudo-ActionScript. This removes the variables `script.scriptlength`, `ed.sblength`, and `ed.numhooks`, too. This reduces the amount of code needed to e.g. simply remove something from any of these vectors. Previously the code had to manually shift the rest of the elements down one-by-one, and doing it manually is definitely error-prone and tedious. But now we can just use fancy functions like `std::vector::erase()` and `std::remove()` to do it all in one line! Don't worry, I checked and `std::remove()` is in the C++ standard since at least 1998. This patch makes it so the `commands` vector gets cleared when `scriptclass::load()` is ran. Previously, the `commands` vector never actually properly got cleared, so there could potentially be glitches that rely on the game indexing past the bounds set by `scriptlength` but still in-bounds in the eyes of C++, and people could potentially rely on such an exploit... However, I checked, and I'm pretty sure that no such glitch previously existed at all, because the only times the vector gets indexed are when `scriptlength` is either being incremented after starting from 0 (`add()`) or when it's underneath a `position < scriptlength` conditional. Furthermore, I'm unaware of anyone who has actually found or used such an exploit, and I've been in the custom level community for 6 years. So I think it's fine.
2020-02-20 18:43:52 +01:00
int position;
2020-01-01 21:29:24 +01:00
int looppoint, loopcount;
int scriptdelay;
bool running;
2020-01-01 21:29:24 +01:00
// Textbox stuff
std::map<std::string, SDL_Color> textbox_colours;
2020-01-01 21:29:24 +01:00
int textx;
int texty;
int r,g,b;
bool textflipme;
bool textcentertext;
size_t textpad_left;
size_t textpad_right;
size_t textpadtowidth;
char textcase;
bool textbuttons;
bool textlarge;
2023-04-20 14:53:30 +02:00
int textboxtimer;
2020-01-01 21:29:24 +01:00
//Misc
int i, j, k;
//Custom level stuff
std::vector<Script> customscripts;
2020-01-01 21:29:24 +01:00
};
#ifndef SCRIPT_DEFINITION
extern scriptclass script;
#endif
2020-01-01 21:29:24 +01:00
#endif /* SCRIPT_H */