Previously, the interim version indicators (commit, date, and branch)
would go away on development builds if git didn't exist. And if it did
exist but provided blank output for whatever reason, that was almost
exactly the same as not having them at all (save for the window title
saying "VVVVVV []" which can be easy to overlook). This was bad because
it complicates troubleshooting when you potentially have an unofficial
or in-development build since those get distributed around or compiled
by some people frequently.
Now, there will always be an interim version indicators unless the game
is compiled with -DOFFICIAL_BUILD=ON. And if the indicators are blank
for any reason, they will just be replaced with placeholder defaults so
they will still show up.
GCC on CentOS will default to C90, it seems. This means it needs C99
explicitly specified for C-Hashmap and FAudio, or it will fail on them
using C99 features (variable declaration in a `for`-loop and the
`restrict` keyword, respectively).
Due to a confluence of weird factors, it turns out that PhysFS is
compiling with implicit function definitions due to function definitions
that get hidden with -std=c99, but not with -std=gnu99 (or the default
GCC value of -std=gnu17).
Also, due to a recent GCC update (GCC 14), implicit function
declarations are actually prohibited with -std=c99 as the C99 standard
proscribes.
This meant that people started getting build errors in PhysFS code on
default settings, which wasn't ideal.
To fix this, we will make our -std= flags apply only to VVVVVV source
files. In CMake 2.8.12, this can be done with
set_source_files_properties. Additionally the flags to disable
exceptions and RTTI are scoped down too.
Thanks to leo60228 for helping debug and solve this issue.
Fixes#1167.
A minor gripe, but one thing I didn't notice with commit
b4579d88d3 is that it now results in two
dialogs if data.zip is missing: The first being the "data.zip is
missing" dialog, and the second is the generic "Unable to initialize
filesystem" dialog.
So just bail early if data.zip can't be found, it's going to take the
error path in main() and also bail regardless.
`/EHsc` does not actually disable exceptions on MSVC, it only makes the
compiler assume that `extern "C"` functions never throw C++ exceptions.
We had a discussion on Discord about actually disabling exceptions, and
from research, that requires defining `_HAS_EXCEPTIONS=0`, but it's
unsupported and undocumented so we deemed the benefits not worth it.
Thus, we will stay with `/EHsc`. But the comment still has to be
updated.
[skip ci]
lang/README-programmers.txt accidentally lists the name of the
font::print function twice, when the second one should've been
font::print_wrap instead. Oops.
[skip ci]
Commit 53d725f78a, intended to fix an
overzealous commit, was itself overzealous. This is because it applied
to all entities when it should only apply to entity-emitting entities.
To fix this, `entityclonefix` needs to no-op if the entity is not an
entity emitter.
Fixes#1176.
Commit 4f881b9e26 fixed a duplication bug
where enemy movement types 10 and 12 would keep duplicating itself on
every frame if it was spawned outside of the rooms they were supposed to
be used in the main game. The downside was that this was an overzealous
fix and unintentionally broke some cases that were working before.
As brought to my attention by Ally, you can no longer place an edentity
with a `p1` of 10 or 12 (translating to movement type 10 or 12) in the
proper rooms and have it spawn perfectly working entities (that don't
clone on themselves every frame), whereas you could in 2.2. This is
considered a regression from 2.3.
So the problem here is that the reason the two emitter entities were so
dangerous outside their respective rooms is because the entity they
spawned (`createentity` entry 1) checked if it was in the correct rooms,
and if so, it would call `setenemy`, and `setenemy` would set the
`behave` attribute (movement type) correctly, and so the new entity
would have a different `behave` that wouldn't be the exact same `behave`
as the previous one, so it wouldn't be a duplicate emitter entity.
The previous `entityclonefix` worked okay for entry 1, because it would
only be run if the room checks failed and `setenemy` wasn't called, but
it broke a previously-working case for entry 56, because it was always
run for entry 56.
So the best way to check if we have a dangerous entity is not by seeing
if it is still `behave` 10 or 12 at the end of entity creation - because
10 or 12 could be harmless under the right conditions - but by checking
if the right conditions were satisfied, and if not, then neutralize the
entity.
I considered making the emitter entities work everywhere - which would
be simpler - but I didn't want to go too far and add a new feature,
especially in a minor release.
This fixes a minor issue where if you had a zip in the levels list, but
then removed it, it would still show up in the levels list after
reloading it (if you also had a .vvvvvv file named the same as in the
zip) even though it shouldn't.
Thankfully, this didn't lead to a memory leak or duplicate zip mounts or
anything like that, because PhysFS ignores mounting a zip if it's
already mounted.
This also didn't result in a level entry from a zip persisting after
removal after reloading the levels list, because the entry would be gone
due to the .vvvvvv file not being found.
The intention of the `-console` argument was to enable seeing console
output on Windows without having to use workarounds. However, this
didn't actually work for arguments like `-addresses` and `-version`,
because the program would exit first before it could get the chance to
create the console.
The other issue is that the console closes too quickly before output can
be read by the user. So to fix that, we must hold it open and let the
user close it when they want to by waiting for an enter press from
STDIN.
This fixes a regression from 2.4 where the foreground wouldn't update
after using the G keybind to go to a room, requiring the user to touch a
tile to update the rendering.
This fixes a bug report from Elomavi that you could still softlock from
warping to ship and incrementing the gamestate by pressing ACTION, which
is diverging behavior from how it was in 2.3. Warping to ship and
incrementing by pressing ACTION is useful behavior for a couple niche
speedrun categories.
I had already fixed this earlier by ignoring state locking if
glitchrunner 2.2 or 2.0 was enabled, but softlocks could still happen
because having glitchrunner mode off still enabled you to increment the
gamestate when otherwise unintended. Softlocks shouldn't happen.
But without removing state locking entirely, I've chosen a middle ground
where it will only be disabled if you press ACTION. That signifies
intent that you still want to perform state incrementing glitches even
with glitchrunner mode off (but in the future it could be considered a
2.3/2.4 glitch that could be patched and made re-enable-able). That way,
casual players can't interrupt the warp to ship by accident (unless they
accidentally press ACTION) while softlocks will be removed.
For localization, the "Thanks for playing!" text was split into two
lines, when it was originally one line. Unfortunately, it was not
updated to account for Flip Mode, so in Flip Mode, it looked like
"playing! Thanks for".
This has been fixed.
If there was a scaling mode value (serialized in the XML as <stretch>
for legacy reasons) that was not 0 or 1 or 2, then the rectangle with
the stretch information would not be initialized by get_stretch_info,
which would lead to a crash, either from dividing by zero (most likely)
or from reading an uninitialized value.
To fix this, when reading <stretch>, normalize it to a sane default if
the value is otherwise bogus. And for good measure, an assertion is
added in get_stretch_info() if the value is still somehow bogus.
Fixes#1155.
This is just a small visual fix to an inconsistency with textbox
colors in simplified scripting. The `reply` command is meant to be
used for the player, and always correctly positions it above the
player, while the `say` command may be used to generate a cyan textbox
that's positioned above a cyan non-player crewmate. However, the color
for both textboxes is always `cyan`, so the `reply` command doesn't use
the (normally identical) `player` color even though all its other
behavior (squeak, position) does. Now that customized textbox colors
were added in 2.4 (#910), it's a shame that this distinction isn't
made between `cyan` and `player`, so this change addresses that (before
we're stuck with levels that change `cyan` but not `player`).
After some discussion about the previous commit, the usecase of
managing tons of basedirs and locking files in the filesystem might
mean it gets annoying to have the language screen show up again
whenever a new language is added, for a small group of people. The
solution to get the best of both worlds is to only re-ask for the
language in the default basedir. This means barely anyone will miss
their language having been newly added (especially since barely anyone
will use any custom basedirs, let alone ONLY custom ones).
Now that two new variants of Spanish have been added, it would be
a shame that many players from Latin-America/Argentina may stay on
Castilian or English because they don't realize the new versions
were added for them. So now, if you've set your language in 2.4.0,
the language screen will show up once more in 2.4.1. This is done by
simply incrementing the lang_set flag to 2 - so that if it's 0 or 1,
your language setting is considered to be possibly outdated.
This shouldn't inconvenience players who don't need to select a new
language - their existing language will still be pre-selected, so they
can just hit ACTION once.
Terry confirms he did the same thing with Dicey Dungeons and says
it's a good idea (and that nobody minds).
This fixes the possibility of the "resize to nearest" graphics option
resizing the game window to be bigger than the resolution of the user's
desktop monitor.
To fix this, just subtract multiples of 320x240 until the chosen
multiple is smaller than the dimensions of the desktop.
Discord user Dzhake discovered this issue.
All of them were changed except for the one in meta.xml. I think it's
safe to assume this is correct, because everywhere else, the same
"Oprime {button} para" pattern always became "Pulsa {button} para" too.
For future PRs, it'll be very nice to have full control over how VVVVVV
gets drawn to the window. This means we can use the entire window size
for things like touch input, drawing borders, or anything we want.
They were missing the latest strings and also still had strings that
had been deleted.
(Whoever commits the upcoming delivery should also sync that version)
This is so they will be updated when switching language with CTRL+F8.
Most of the editor notes are simple text that don't use any string
formatting. For the ones that aren't, some (saving and loading, changing
map size) reference variables that wouldn't change without initiating a
new note anyway. For the others, i.e. the ones that _do_ reference
variables that could easily be changed (tileset name, speed) by
switching the current room, we cache their values and use the cached
values when drawing the note. Unfortunately, this requires adding a
couple of ugly attributes to editorclass, but it'll be fine.
These are simple strings (no vformat), so we can just un-bake them to
make sure that cycling languages with one of them onscreen updates them
accordingly.
These weren't getting updated when cycling language with CTRL+F8. This
is because they would be already baked. Luckily, at least the bool
keeping track of whether or not to translate them in the first place
already exists, so we can just rely on that.
This makes it work pretty well. It basically just resets the state of
the limits check and starts from the first limit broken (if any), which
is behavior that makes sense to me.
Otherwise, without this, it seems to invalidate pointers and, on my
machine, start pulling strings from the language XML, which is
horrifying.
Not gonna lie, I am a bit disappointed at having to do this, because it
actually worked pretty well despite a few bugs depending on which
language you entered with. But that's only because I'm working with
the official translation files, which are in sync with each other.
With translation files that are completely arbitrary, it would be
apparent that switching languages during the cutscene test doesn't
really make sense. Like, at all. That's because the list of cutscenes is
populated entirely from language-specific XML and the cutscenes in them
are also from language-specific XML. So keeping the same position in the
menu doesn't really make sense, and keeping the same position in a
cutscene definitely doesn't make sense.
I saw that the only problem with cycling languages in a title screen
menu is that the menu options don't get updated. So I was like, we can
just recreate the menu, and then I was like "Sure, why not." So that's
what I did.
To accommodate the CTRL+F8 keybind in the language menu, it
automatically updates the menu option when you cycle it. This is because
otherwise using the keybind in the language menu wouldn't visibly update
the language, but it still actually does change your language, and that
can be seen by pressing Escape.
Also, the menucountdown needs to be preserved because otherwise
createmenu() resets it, even if it's the "same" menu (this behavior is
needed so that the menu that is shown during the countdown isn't added
as a stack frame which would make it a menu that could be returned to).
Originally, textcase was reset in scriptclass::translate_dialogue(),
which is called inside the `text` script command. However, this didn't
really work with the new on-the-fly text box translation system, and
that function is gone now, so I removed that and kind of forgot about
it.
Of course, this now causes a regression. Namely, that the text boxes
after the VVVVVV-Man sequence in the Secret Lab entrance cutscene are
not translated.
I can't reset the text case in `text`, as the scripts assume that they
can set the text case before `text`. So the next best thing is to reset
it in speak/speak_active.
This fixes a bug where some text boxes wouldn't update the displayed
button if the active input device changed from a keyboard to controller,
or vice versa. Namely, the "Press ACTION to continue" text boxes.
This removes Graphics::textboxwrap(), as it is now an unused function.
Additionally, this removes the return value of textboxclass::wrap(), as
it is also now unused.
This stores the original x-position and y-position of the text box, and
when a text box gets repositioned, it will use those unless a crewmate
position overrides it.
This is the original position of the text box, before centering or
crewmate position is considered.
This fixes a bug where a cutscene text box can be "shifted" from its
normal position via CTRL+F8 cycling if there is a translation that is
too long for the screen and thus gets pushed by adjust(). I tested this
with the text box in the Comms Relay cutscene that starts with "If YOU
can find a teleporter".
This is not applicable to function-based translations
(TEXTTRANSLATE_FUNCTION), because the responsibility of correctly
positioning the text box resides with the function.
This adds a debug keybind to cycle the current language forwards,
CTRL+F8. It also adds a debug keybind to cycle it backwards,
CTRL+SHIFT+F8.
This is only active if the translator menu is active (and so if the
regular F8 keybind is also active).
This is useful for quickly catching errors in translations and/or
inconsistencies between translations. In fact, I've already caught
several translation mistakes using this keybind which made me mildly
panic that I screwed something up in my own code, only to realize that
no, actually, it was the translation that was at fault.
For now, this is only meant to be used in-game, as text boxes get
retranslated instantly, whereas things like menu options don't. But menu
options will be retranslated on-the-fly in a later commit.
This fixes a problem where it would incorrectly format the text because
the width of the text box hadn't updated yet.
This fixes a bug where the jukebox informational terminal would
initially be created with too much padding in CJK languages, pushing the
text box offscreen, even though switching languages while the text box
is already open fixes it.
In order to be able to retranslate the game time text box in particular,
I had to create new variables to bake the saved time, since the existing
savetime variable is just an std::string. From there, the saved time can
be retranslated on-the-fly.
This adds an attribute to textboxclass to allow a text box to keep an
index that references another text box inside the graphics.textboxes
std::vector.
This is needed because the second text box of a "You have found a
shiny trinket!" or "You have found a lost crewmate!" pair of text boxes
explicitly relies on the height of the first text box. With this, I have
moved those text boxes over to the new text box translation system.
Since the update order now matters, I added a comment to
recomputetextboxes() that clarifies that the text boxes must be updated
in linear order, starting from 0.
This adds an assert to Graphics::textboxtranslate() to make sure that
callers don't accidentally provide a function when specifying a
translation type that isn't TEXTTRANSLATE_FUNCTION, because in that case
the function won't be used, and then it will make them scratch their
heads wondering why their function won't work.
And yes, I am stupid enough to blindly type TEXTTRANSLATE_CUTSCENE when
I meant to type TEXTTRANSLATE_FUNCTION. This assert has already caught
one of my mistakes. :)
These seemed annoying to do without copy-pasting, because I didn't want
to make a separate function for every single dialogue, and I didn't know
how to pass through the English text, until I realized that I can just
use the existing original.lines vector in the text box to store the
English text. After that, getting it translated on-the-fly isn't too
bad.
Just a small optimization.
For example, consider the calls in adjust(). After the first resize(),
the lines after only change the x-position and y-position of the text
box and depend on the x-position, y-position, width, and height.
However, resize() only changes the width and height if the contents of
the text box change, which after the first call, they don't. So remove
the second call to resize(), because it's completely unnecessary.
By similar reasoning, the second calls to resize() in centerx() and
centery() are unnecessary too.
This transfers the responsibility of the adjust() call to
applyposition().
This is because cutscene text boxes (TEXTTRANSLATE_CUTSCENE) will have
adjust() called, but all other text boxes won't. And I can't place the
adjust() call inside applyposition(), because adjust() also calls
applyposition(), and that leads to an infinite loop which leads to a
stack overflow, so I had to remove the applyposition() call from
adjust(), and replace the other existing call to
Graphics::textboxadjust() with Graphics::textboxapplyposition(), and
then remove Graphics::textboxadjust() function because it's no longer
used.
Several text boxes in the gamestate system are unused and are
untranslated. To prevent them from becoming empty when retranslating
text boxes, we need to save their original context by calling
graphics.textboxoriginalcontextauto() (which is just
graphics.textboxoriginalcontext() but automatically saving whatever is
already in the text box at the time).
With the new system of retranslating text boxes on-the-fly, this also
enables us to retranslate them whenever the player toggles Flip Mode.
This is relevant because the Intermission 1 instructional text boxes
refer to a floor when Flip Mode is off, but when it is on, it talks
about the ceiling.
This splits the text wrapping functionality of Graphics::textboxwrap to
a new function textboxclass::wrap, and with this new function, some more
text boxes can be moved to the new TEXTTRANSLATE_FUNCTION system.
Namely, Game Saved (specifically the game failing to save text box),
instructional text boxes in Space Station 1, and the Intermission 1
instructional text boxes.
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.
This is another piece of state that needs to be kept and re-played when
switching language, because a different language could change the
dimensions of the text box, which affects how it's centered.
Also, to make sure that crewmate positions override any text centering,
the scriptclass variables textx and texty should be reset in the
position and customposition commands.
Originally I did a straight deep copy of the original lines, but this
ignores the limit of either 12 or 26 lines in a text box. So we defer to
addline() which will enforce the limit accordingly, just like it would
do with the original text box.
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.
This is for consistency with all other functions dealing with the latest
created text box. There are several cases in custom levels where these
functions can be called even though there are no text boxes on screen.
It doesn't make sense to change the alignment of all existing text boxes
when you're not otherwise able to mutate the text. Whereas the point of
the 'all' argument in setfont is to be able to animate text boxes using
fonts.
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.
This fixes a regression where attempting to warp to ship with a trinket
text box open in glitchrunner 2.0, and then incrementing the gamestate
afterwards, would result in a softlock. This is a speedrunning strat
that speedrunners use.
The state lock wasn't ever intended to remove any strats or anything,
just fix warping to ship under normal circumstances. So it's okay to
re-enable interrupting the state by having glitchrunner enabled.
This bug was reported by mohoc in the VVVVVV Speedrunning Discord
server.
Just like I changed the number één to be capitalized as Eén instead
of Één (due to this being a special case), I forgot to do the same
for the onelifemode.
This involves a couple of dialog boxes ("Congratulations!" and the
jukebox) which were supposed to have blank lines, but they weren't
in the translation.
LodePNG always loads PNG in big-endian RGBA format. For this reason,
when loading PNG files VVVVVV was specifying the format as ABGR and the
conversion would then be performed by SDL. However, then running on
big-endian machines, this conversion should not be performed at all, and
the surface format should then be set to RGBA.
We recently had a user come in the VVVVVV Discord not knowing that,
after running CMake, you then need to compile the game in a separate
step. This clarifies the instructions.
This fixes a segmentation fault caused by an out-of-bounds indexing
caused by an attempt to unwordwrap a string that starts with two
newlines.
The problem here is that in the branch of the function
string_unwordwrap() where `consecutive_newlines == 1`, the function does
not check that the string `result` isn't empty before attempting to
index `result.size()-1`. If `result` is empty, then `result.size()` is
0, and `result.size()-1` becomes -1, and indexing a string at position
-1 is always undefined behavior.
Funnily enough, a similar indexing happens just a few lines down, but
this time, there is a check to make sure that the string isn't empty
first. I'm unsure of how Dav999 forgot that check a few lines earlier.
This situation can happen in practice, with custom level localizations.
I made a level with a filename of testloc.vvvvvv and created a file at
lang/fr/levels/testloc/custom_cutscenes.xml with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<cutscenes>
<cutscene id="test" explanation="">
<dialogue speaker="cyan" english="This is text..." translation="blarg"/>
</cutscene>
</cutscenes>
Then I switched to French, created a script named `test`, and created a
text box that started with two newlines (so in total, the text box must
be at least 3 lines in length). Running the script triggers the segfault
when the text box is created. (Well, technically, on my machine, it
triggers an assertion fail in libstdc++ and aborts, but that's basically
the same thing.)
To fix this while still preserving the exact amount of newlines, if
`result` is empty, we add a newline instead of attempting to index the
string.
These strings had been replaced over time and the original versions
marked ***OUTDATED*** to allow for the original wordings to be reused
by the translators who had only translated the original ones.
(See lang/README-programmers.txt.)
Now, these strings have all been updated in every language, so it's
time to clean them up!