Compare commits

...

1209 commits

Author SHA1 Message Date
Ethan Lee
cd26810e9a CMake: Use rpath, not runpath
Some checks failed
CI (Android) / Build (Android) (push) Has been cancelled
CI / Build (macos-latest) (push) Has been cancelled
CI / Build (Steam Linux Runtime Sniper) (push) Has been cancelled
CI / Build (windows-latest) (push) Has been cancelled
2025-07-05 18:08:15 -04:00
leo60228
6ae4de1f94 Fix TARGET_OS_IPHONE checks
Some checks failed
CI (Android) / Build (Android) (push) Has been cancelled
CI / Build (macos-latest) (push) Has been cancelled
CI / Build (Steam Linux Runtime Sniper) (push) Has been cancelled
CI / Build (windows-latest) (push) Has been cancelled
2025-06-19 13:10:16 -04:00
Dav999
65b024a9a3 Fix vertical position of Comms Relay textbox
Some checks failed
CI / Build (macos-latest) (push) Has been cancelled
CI / Build (Steam Linux Runtime Sniper) (push) Has been cancelled
CI / Build (windows-latest) (push) Has been cancelled
Fixes #1242.

Turns out it was a really simple fix - the X positions were good, but
the Y positions were always at the top of the screen regardless of the
height of the textbox. Now they're vertically centered respective to
the speaker.
2025-06-15 21:15:00 -04:00
Dav999
d53861553e Update SheenBidi to 2.9.0
Some checks failed
CI / Build (macos-latest) (push) Has been cancelled
CI / Build (Steam Linux Runtime Sniper) (push) Has been cancelled
CI / Build (windows-latest) (push) Has been cancelled
This fixes our problem with Valgrind reporting a jump based on an
uninitialized value; see https://github.com/Tehreer/SheenBidi/issues/19

Just to be sure nothing unexpected happens, I tested that this doesn't
cause any changes in behavior by outputting bidi-transformed versions
of all strings in strings.xml to a file for both our Arabic and Persian
localizations before and after the update, and confirming that the
files are the same.
2025-06-11 15:27:45 -04:00
Dav999
7dccc2d328 Apply translations for 2.5 strings
Some checks failed
CI / Build (windows-latest) (push) Has been cancelled
CI / Build (macos-latest) (push) Has been cancelled
CI / Build (Steam Linux Runtime Sniper) (push) Has been cancelled
This includes translations for checkpoint saving and enemy speed in the
editor.
2025-06-08 17:05:32 -04:00
Dav999
26d27c7df5 Apply translations for 2.4 strings
This includes translations that were missing translations, with varying
extent between different languages, for the following things:
- "X mode is enabled" in-game warnings
- "Press {button} to freeze/unfreeze gameplay" for the level debugger
- Some credits strings for the post-2.4.0 extra Spanish options, the
  PT_BR proofread, and Persian
- The recent gamepad menu changes (#1229)

Furthermore:
- "TAB" (used in the level debugger string) is now a separate string
  instead of being hardcoded, because some languages needed it
  translated
- Added missing arrows to Arabic/Persian font (needed for the gamepad
  menu, and also a translator menu actually)
2025-06-08 17:05:32 -04:00
Ethan Lee
70cbde18fe
CI: Move Sniper SDK from beta to latest.
This was a carryover from FNA when I was doing the initial move to SDL3; VVV won't have to care about this now that 3.2.0 has been out for a while.
2025-06-08 17:03:46 -04:00
Ethan Lee
37bb4f30fd
CI: Remove CMake caching.
Seems like the build root can change and CMake rightfully gets confused at the conflicting paths, so let's just use the minutes to do this from scratch.
2025-06-08 17:01:45 -04:00
David Fisher
0d16cd1369 feat(ci): Enhance GitHub Actions workflow
Some checks failed
CI / Build (macos-latest) (push) Has been cancelled
CI / Build (Steam Linux Runtime Sniper) (push) Has been cancelled
CI / Build (windows-latest) (push) Has been cancelled
- Update actions/checkout to v4

- Add build caching for macOS and Linux jobs

- Implement concurrency control to cancel redundant runs

- Restrict GITHUB_TOKEN permissions for security

These changes improve CI performance, stability, and security.
2025-05-13 13:27:26 -04:00
Terry Cavanagh
902acdd2ac
Added PortMaster project to License exceptions 2025-05-09 14:40:46 +01:00
Nate Eagleson
8bf6b25b5b Fix typo in README.md 2025-05-06 22:06:48 -04:00
NyakoFox
0e31253876 Fix mouse coordinates being wrong on HiDPI displays
When writing the initial stretch mode code, the existence of HiDPI
displays completely slipped my mind -- or at least I didn't realize
that they'd be a problem.

We implement scaling modes ourselves, so transforming the mouse
coordinates to our 320x240 viewport is done manually. Unfortunately,
that code did not take into account HiDPI scaling whatsoever, meaning
that all of the math which assumes the window size and the renderer
size is wrong.

To fix this, we use `SDL_GetWindowSizeInPixels` and `SDL_GetWindow` to
find the scaling factor, and then apply that to the mouse coordinates
to get the mouse coordinates in the pixel-space instead, and then we do
all of our normal logic after.

Due to our usage of `SDL_GetWindowSizeInPixels`, this bumps the minimum
SDL version to 2.26.0.

Closes #1235.
2025-05-03 15:15:03 -04:00
Dav999
1de2078014 Sync new gamepad strings into all language files
Also including all the localizations I already have right now.
2025-05-03 01:35:25 -04:00
Dav999
37b788c1c8 Add check for converting negative SDL_GameControllerButton to glyph
A SDL_GameControllerButton below 0 is out of array bounds, so that
should trip the assert and return GLYPH_UNKNOWN just like when the
value is too high.
2025-05-03 01:35:25 -04:00
Dav999
d730528118 Add confirmation and removal to bindings menu
This makes the following improvements to the gamepad bindings menu:

- The menu now shows a hint that you can press a button while any of
  the bind options are selected (or that you can navigate away from
  those options)
- Instead of button presses immediately setting a binding, they now
  ask for confirmation: press the same button a second time to confirm
- You can now remove a binding, the same way you add it (this has the
  same type of confirmation)
- This menu used to be inconsistent with pretty much every other menu
  in the game by showing a permanent title and description for the menu
  itself ("Game Pad", "Change controller options.") rather than showing
  a title and description for the currently selected option.
  This inconsistency is now fixed.
2025-05-03 01:35:25 -04:00
Dav999
75035047ad Update name of Arabic/Persian font
The level font menu now shows both names, since the font is used for
both Arabic and Persian.
2025-05-02 22:31:29 -04:00
Dav999
1a1bf2a4f9 Add new strings for Persian
New strings were delivered already today, so let's add them immediately
in this PR so we have a little less on our checklist later!
2025-05-02 22:31:29 -04:00
Dav999
48c3e175a3 Fix save summary sometimes getting misordered in Arabic
The Persian localizers noticed an issue where the translation for
"Dimension VVVVVV, 12:34:56" would become "VVVVVV, 12:34:56 noisnemiD",
rather than "12:34:56 ,VVVVVV noisnemiD" as expected. This was fixed
for Persian but the same issue also affected Arabic, so I added a
RIGHT-TO-LEFT MARK (U+200F) to fix it there as well.
2025-05-02 22:31:29 -04:00
Dav999
374ce11425 Sync language files
This adds "Persian" to all the languages, and brings the Persian files
up-to-date (mainly removing outdated strings and adding some late 2.4
stuff that we'll contact all translators for soon)
2025-05-02 22:31:29 -04:00
Dav999
0b00415ab3 Add Persian to credits screen 2025-05-02 22:31:29 -04:00
Dav999
3eb88e0ac0 Add files for Persian localization
A bit overdue since this was delivered a few weeks ago (oops),
but here it is!

Thanks to Amir Arzani and Masoud Varaste.
2025-05-02 22:31:29 -04:00
NyakoFox
719ed9a67b Fix uncommon division-by-zero
Every now and then, the game crashes for me because of a division by
zero, due to the rect returned by `Graphics::get_stretch_info`. I don't
fully know how the width and height get set to 0, but this should
protect against it.

As I always use integer scaling, my guess is that
`Screen::GetScreenSize` (which later calls `SDL_GetRendererOutputSize`)
returns 0 sometimes, and the code trusts that -- but I know that
windows and things can be finicky, so the clamp is probably a good
idea.
2025-05-01 18:33:02 -04:00
NyakoFox
6810c5fa8c Fix #1223 (analogue mode segfault)
When `SDL_RenderReadPixels` fails for some reason, the game tries to
free the temporary source surface it creates. Unfortunately, it never
sets it to `NULL` after, so the next time the game tries to render the
filter, it'll try to work with a memory region that was already freed.

To fix this, I just replaced `SDL_FreeSurface(*src);` with
`VVV_freefunc(SDL_FreeSurface, *src);` which is a helper macro which
sets the pointer to NULL after freeing.

Now, there's a new issue -- since the temporary buffer is now NULL,
next frame we'll try to remake it! So I've introduced a static bool
which disables the filter entirely if `SDL_RenderReadPixels` fails.
Without this, it'd create and destroy a surface every frame, which
could lead to slowdown. (Not as slow as the filter when it DOES work,
but still...)

I also added a line which frees the second temporary surface... it's
weird that was missing in the first place, but I think reimplementing
analogue mode was one of the last things I did for the renderer
rewrite anyways.

Resolves #1223.
2025-04-29 20:54:11 -04:00
NyakoFox
2ee5aef3fa Fix the campaign trinket count missing from stats
PR #1226 tweaks both the crew and the stats screens. When there's no
trinkets in the level, it removes the trinket count from the STATS
screen in the menu. This, however, is missing a `custommode` check,
meaning that the main game is ALSO missing the trinket count.

This PR fixes that oversight.
2025-04-29 20:53:05 -04:00
NyakoFox
c1eaeca9f6 Fix "invalid" activity zones not spawning
There's a problem in #1224 where it breaks spawning custom activity
zones. After a bit of confusion after this was reported, I realized
that I removed the fallback from `getcrewman` and changed the return
value to `-1` if the entity isn't found, to avoid returning the wrong
entity (entity 0, aka probably the player).

Unfortunately, it seems like a ton of levels (including my older ones)
rely on this behavior.

Creating custom activity zones is a long process which uses a bunch of
unintended behaviour, which includes targeting a crewmate with color
35. With the change I mentioned earlier, the `getcrewman` function
would return `-1` instead, which was out of bounds of the entity array,
so the game avoided spawning the activity zone at all. The prior
behaviour of falling back to entity 0 (most likely the player) would
spawn the activity zone around the player instead.

Nowadays, I try to spawn a crewmate with color 35 anyways so I can
control where the box spawns (instead of on the player always), however
most people don't (and haven't) so reverting this change seems best for
now.

If we wanted to reintroduce the `-1` fallback in the future, things
that call `getcrewman` would have to check for `-1` and use `0`
instead, but that would require a lot more testing and studying where
it's used, and I'd rather squash this bug quickly and worry about
cleanliness later.
2025-04-28 08:04:51 -04:00
NyakoFox
0e9c4f98a6 Replace colour IDs with an enum
Entity colors are just integers. Their colour ID gets passed through a
big switch, returning different RGB values depending on the colour ID
(and can also get affected by randomness or other game state.)

But because of this, there's a bunch of random numbers floating around,
with no actual description on what they are other than comments which,
while most of the time are accurate, only exist in the switch.

To fix this, this commit adds a new enum which labels every colour.
While we can't use it as a type (as we need to allow colours outside of
what are defined, in case people want a "pure white", and scripting can
set any colour ID they want), colours have to stay as `int`.
2025-04-21 20:09:57 -04:00
NyakoFox
b8bcdf39df Move saving outside of MetaData (oops!) 2025-04-12 16:43:50 -04:00
NyakoFox
6809aa0135 Attempt to fix external playtesting not respecting colour 2025-04-12 16:43:50 -04:00
NyakoFox
5816e6f51a Add player colour as a level property
This PR adds a new XML property for the player's colour. It is 0 by
default, but you can change it to any colour ID. For example, making
the player use the trinket color is `<PlayerColour>3</PlayerColour>`.

This is mostly a quality-of-life addition, as the player's colour is
always 0 unless changed by scripts. A lot of levels which use different
player colours use an intro script which both changes the player's
colour and sets their respawn colour, which works great for finished,
completed levels, but makes playtesting a little more annoying as they
will spawn in as the wrong colour. Adding a level property for the
default player colour fixes this annoyance.

Additionally, this changes the behavior of `restoreplayercolour`. This
command used to set the player's colour to 0 (ignoring the respawn
colour, so when Viridian would die, they would revert to the respawn
colour). Now, it sets both Viridian's colour AND the respawn colour to
what was present in the level file. This way, you can temporarily
change the player colour using the script commands, and then use
`restoreplayercolour` to revert back to what the player colour
normally is.

The start point colour has also changed, to show the player colour
instead of always colour 0.

Like most changes like this, a way to change this in-editor does not
yet exist, but is planned for the future.
2025-04-12 16:43:50 -04:00
mothbeanie
b0d53e85a0
Crew screen tweaks for custom levels.
See #1226 for a full overview with comparison images.
2025-04-12 16:41:30 -04:00
NyakoFox
d419c6ed5b Remove extra unnecessary palettes
This merges the colors from other palettes into the general entity
palette function.
2025-02-18 14:14:16 -05:00
Dav999
343790f12b Change name of Silesian option (part 2)
Turns out there was a little miscommunication with the translator -
they said the option needed to say "Ślōnsko" but were only talking
about correcting the first word, where I thought the whole thing needed
to be replaced.
2025-02-18 09:51:40 -05:00
Dav999
e318df24bc Change name of Silesian option by request of translator
Our Silesian translator asked for "Ślonsko godka" to be changed to
"Ślōnsko".
2025-02-05 11:05:01 -05:00
Dav999
d709db4b45 Fix squeak and save spam in gamepad menu
If you push a button to set a controller binding, you may either hear
one Viridian squeak, two Viridian squeaks (a louder one), or Viridian
doesn't stop squeaking until you let go of the button. While you hear
the continuous squeaking, your save file is also repeatedly saved.

There are two small bugs at play here:
- the squeak is actually played in two different places at the same
  time (both in titleinput() whenever a button is pressed, and in
  updatebuttonmappings() when a mapping is succesfully changed)
- titleinput() doesn't register that a button is held down and applies
  the button (and saves to file) every frame for as long as the button
  is held

This commit fixes both these issues. Now a single button press always
causes one squeak, and only if the bindings actually changed. Your save
file is also no longer saved repeatedly from holding down the button.
2024-12-22 15:53:04 -05:00
mothbeanie
f9f9f076b4 musicVolume -> controlVolume for clarity 2024-12-17 09:52:06 -05:00
mothbeanie
d90f3c3c08 :3 2024-12-17 09:52:06 -05:00
mothbeanie
420683a80f introduce functions to modulate user volume 2024-12-17 09:52:06 -05:00
NyakoFox
d4e472db1b Make 0-length gravity lines invisible again 2024-11-17 11:46:24 -05:00
Ally
63880169e6
Convert entity types to an enum (#1007)
In an effort to remove magic numbers, I've given every entity type a
name. Hopefully I didn't miss anywhere.

Also, add `createentity` case 100 for backwards compatibility.

Co-authored-by: NyakoFox <nyakowofox@gmail.com>
Co-authored-by: Dav999 <dav999.tolp@gmail.com>
2024-11-17 11:45:57 -05:00
Reese Rivers
9c45dfb845 Tweak one word in the Esperanto localisation
"Rekordo" (record) flows better than "altpoentaro", which is a literal translation of "high score".
2024-11-15 21:22:01 -05:00
leo60228
fa8517a521
iOS port (for desktop_version) (#1137) 2024-11-15 20:33:10 -05:00
NyakoFox
6174d62f6f position(force) -> position(absolute) 2024-11-07 09:53:45 -08:00
NyakoFox
98a0931225 Add textoutline(on/off/default)
This commit adds a new scripting command for textbox visual control,
where you're able to force the transparent textbox's outline on or off.
2024-11-07 09:53:45 -08:00
NyakoFox
45e60fa69c Add position(force)
This argument forces the textbox position, meaning it won't be moved
to be inside of the bounds of the screen (nor have the 10 pixel padding
on each side.)
2024-11-07 09:53:45 -08:00
mothbeanie
d8a8e44afa fix flipped inequality sign
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2024-11-03 21:54:44 -08:00
mothbeanie
b572e2164b fix flipped inequality sign
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2024-11-03 21:54:44 -08:00
mothbeanie
2875af0492 Fix coordinates being allowed in the wrong order 2024-11-03 21:54:44 -08:00
mothbeanie
54b2aaae96 Region system PR review changes
Fixes errors or oversights with the region system for the PR review
2024-11-03 21:54:44 -08:00
mothbeanie
1fb0afb99d Apply PR review changes
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2024-11-03 21:54:44 -08:00
mothbeanie
dedf941b25 Update region system to current codebase due to PR rot 2024-11-03 21:54:44 -08:00
Ethan Lee
94620d809e CI: Migrate to SLR Sniper SDK for Linux builds 2024-11-03 16:57:17 -08:00
leo60228
11d0e7f91e correctly set VVV_CXX_SRC flags (set_source_files_properties isn't cumulative) 2024-11-03 16:52:48 -08:00
leo60228
4587fa46b0 update android sdl2 to 2.30.8 2024-10-05 16:48:47 -04:00
NyakoFox
c2642dbdef Add more checks for checkpoint saving 2024-10-02 00:37:25 -07:00
Misa
a0bd2f3da4 Refactor: Use fullmap() everywhere
This replaces all instances of unlocking all rooms on the map with calls
to map.fullmap(), for consistency.

This also fixes two comments that got swapped around in startgamemode().
I don't know how that happened.

[skip ci]
2024-09-28 00:52:19 +00:00
mothbeanie
8a00ea7aab Add mapexplored(), mapreveal() commands 2024-09-27 17:47:19 -07:00
NyakoFox
c04c6bc552 Abstract checkpoint saving to its own function
This also makes the save failed textbox not appear in special modes,
and allows custom levels to quicksave from checkpoints as well.
2024-09-27 17:14:57 -07:00
NyakoFox
41d5e688e9 Fix menu option, show textbox when save failed
In my last commit, I accidentally inverted whether the description says
ON or OFF.
2024-09-27 17:14:57 -07:00
NyakoFox
e58fd606cf add menu option for checkpoint saving 2024-09-27 17:14:57 -07:00
NyakoFox
8aec83daec Checkpoint autosaving 2024-09-27 17:14:57 -07:00
Misa
c870df4e5b Remove slowdown during death animation
Some discussion on the Discord server resulted in this change. It's a
quality-of-life improvement where, if the game is in slowdown mode, it
will return to 100% speed for the duration of the death animation.

The reasoning is obvious. There is nothing to do during the death
animation, so making it take longer during slowdown is just an annoyance
to the player, almost a penalty for them using an accessibility option.
This is the same reason why slowdown no longer applies in menus, etc.
2024-09-25 21:14:02 -07:00
Misa
32562f03a9 Give MotU trophy in normal mode
This makes it so that it is possible to obtain the Master of the
Universe trophy/achievement, usually unlocked by beating No Death Mode,
outside of NDM.

There are several conditions that need to be met:

1. The game needs to be started from a new game and cannot be from
   loading a save.
2. Accessibility modes (invincibility and slowdown) must never be
   enabled.

If either condition is violated, then the boolean that keeps track of
NDM eligibility will be set to false.
2024-09-23 22:23:45 -07:00
NyakoFox
d8b2b3542a Add enemy speed as a room property
Currently, you can change platform speed, but not enemy speed, which is
always hardcoded to be 4. This commit fixes that, by adding the
"enemyv" property, which is an offset to the speed of 4. Since it
defaults to 0, older levels are not broken by this change.
2024-09-23 18:56:41 -07:00
Misa
8b62bb1d65 Bump version to v2.5
I consider the 2.4 development cycle over now, after the most important
bugs (and regressions) were fixed in 2.4.2.

[skip ci]
2024-08-27 15:13:24 -07:00
Dav999
149f5fbebc Tweak IME selection background left bound
Just extending the selection background left by one pixel so there's
not one pixel of black background to the left of a selection that
starts at the beginning of the text, and so some characters being
selected show up better (particularly where there's a long vertical bar
at the first pixel). We shouldn't be overlapping any part of the
previous character, since every character normally has a pixel of
spacing on the right.
2024-08-06 18:16:46 -07:00
Dav999
9ec8d8b637 Add selection background to IME rendering
Decided to implement it anyway since the broken behavior (selection
length always being 0, at least on Windows) may get fixed later in SDL,
so let's do it right in one go.
2024-08-02 22:25:00 -07:00
Dav999
8b8f827b70 Add IME text rendering
This shows the uncommitted text in a box in the bottom left corner.
This doesn't show the selection (defined by the start and length fields
in the event) yet, but this is already much better than it was on its
own, and I don't know how urgent the selection is since it's broken on
Windows anyway.
2024-08-02 22:25:00 -07:00
Dav999
a9ca63b367 Add buffer for IME editing events
When inputting uncommitted text from an IME, this is now stored in a
std::string imebuffer, just like keybuffer. It also enables extended
editing events, so text longer than what fits in the standard editing
event is also supported. This commit does not yet display the text
onscreen.
2024-08-02 22:25:00 -07:00
Misa
345eca5e10 Fix regression: Escape from conveyor spikes
This fixes a regression from 2.3. Consider the following diagram:

       CC
    X  CC
      <<<<

"C" indicates one tile of a checkpoint entity, "X" indicates a spike
tile, and "<" indicates one tile of a conveyor entity that has the
default speed (4 pixels per frame) going leftwards.

Now consider if the player were to touch the checkpoint and die. In 2.2,
they would be able to escape from the spike by holding right. But in
2.3, they would not be able to, and would die from the spike
continuously with no escape.

This happens because in 2.2, the player would spawn a couple pixels off
the surface, and this gained them an extra frame of movement to move
away from the conveyor. 2.3 places the player directly on the ground,
moving them one frame earlier and thus forcing them to their doom.

Now consider the following diagram:

      CC
    X CC
     <<<<

The difference with the previous diagram is that this time, the spike is
one tile closer. This time, there is no escape in 2.2 and you will
always die no matter what.

By the way, both diagrams have the same behavior if the conveyor is
rightwards and if everything is flipped upside-down. Thankfully, it
doesn't seem to be direction-dependent.

The reason 2.3 lowered the player onto the surface was for consistency
(see PR #502), and I don't want to undo that. So I think the best
solution here is to re-add the extra frame of control by conveyors only
moving the player after lifeseq has advanced enough.
2024-07-31 22:17:50 -07:00
Misa
6930bde91b Use variable timestep for SDL_Delay
This fixes a bug where fast-forward wouldn't work in 30-FPS-only mode.

This is because the 30-FPS-only code has a hardcoded check for the
number 34, as in 34 milliseconds must pass before the next frame can
advance. This is why slowdown still worked, because slowdown means
you're waiting longer than 34 ms anyways, but fast-forward tries to wait
for only 1 ms, which wouldn't work if the 34 limit was still enforced.

So instead, swap out the 34 with game.get_timestep() and this will be
fixed.

Fixes #1185.
2024-07-20 18:40:16 -07:00
mothbeanie
487e0c6b2d fix music ear blast bug 2024-07-13 13:11:05 -07:00
Misa
c636713a43 Use add_custom_command over add_custom_target
This fixes an issue where the CentOS CI kept failing because it couldn't
find the generated InterimVersion output file.

It seems like using the BYPRODUCTS statement in add_custom_target
didn't work because BYPRODUCTS was only added in CMake 3.2, so then
add_custom_target never ran, which is obviously a problem.

The solution is to use add_custom_command instead, and to solve the
problem that the interim version needs to be regenerated every time no
matter what (which is what BYPRODUCTS was supposed to do) we just add a
dummy output instead.
2024-07-12 21:34:53 -07:00
Misa
e3876feb3b Style: Lowercase commands in version.cmake
We made the lowercase names be a consistent style in the main CMake file
but we missed this one.

[skip ci]
2024-07-12 21:34:53 -07:00
Misa
d4425ed762 Always have interim version indicators
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.
2024-07-12 21:34:53 -07:00
Misa
b0d2a6a372 Add -std=c99 for C-Hashmap and FAudio
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).
2024-07-12 21:33:47 -07:00
Misa
ff611a56ff CMake: Scope C99/C++98 flags to VVVVVV only
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.
2024-07-12 21:33:47 -07:00
Misa
7d01c6bdb0 Fix double error dialog if data.zip is missing
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.
2024-07-11 11:47:59 -07:00
Misa
a990f8d87d Fix wrong comment on disabling MSVC exceptions
`/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]
2024-07-11 11:44:49 -07:00
Dav999
6df7d5df8a Fix font::print_wrap() name in README-programmers
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]
2024-06-28 08:03:55 -07:00
Misa
64c554261e Fix regression: Entities not moving
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.
2024-06-09 19:41:48 -07:00
Misa
53d725f78a Fix regression: Overzealous emitter dupe fix
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.
2024-06-07 14:20:28 -07:00
Misa
c20db02f15 Unload zips before loading zips
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.
2024-06-04 15:42:39 -07:00
Misa
dd15d67e62 Fix info args not working with -console
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.
2024-06-03 21:42:57 -07:00
Misa
a9d43b543f Fix regression: Foreground redraw after G keybind
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.
2024-06-03 20:58:52 -07:00
Misa
16d75d2da8 Disable state locking if inc'ing state w/ ACTION
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.
2024-05-25 23:34:03 -07:00
Misa
ff6bb68f3a Fix "Thanks for playing!" reversed in Flip Mode
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.
2024-05-25 23:33:45 -07:00
leo60228
4b2b4fb7c9 CONTRIBUTORS.txt: leo60228 -> leo vriska 2024-05-21 20:57:19 -07:00
Terry Cavanagh
d678bd59ff
Added license exception for Recalbox project
https://shop.recalbox.com/
2024-05-02 17:12:36 +01:00
Misa
ff785aaa8a Bump version to 2.4.2
We still need to fix a couple bugs from 2.4.0.
2024-03-29 21:18:39 -07:00
NyakoFox
3361e71036 Add MSVC version check 2024-03-29 20:31:00 -07:00
NyakoFox
4bba26280f Add /utf-8 to MVSC 2024-03-29 20:31:00 -07:00
Misa
217996b134 Fix UB from out-of-range <stretch>
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.
2024-03-29 20:22:00 -07:00
Misa
8640ead937 Fix copy-paste error in customposition
This would otherwise result in text boxes for custom crewmates being
improperly positioned.
2024-03-29 19:55:41 -07:00
Dav999
a9d438968d Change reply scripting command to player color
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`).
2024-03-29 19:47:46 -07:00
TerryCavanagh
91e144736b whoops 2024-02-09 13:48:11 +01:00
TerryCavanagh
b48cdb0003 minor updates to Spanish 2024-02-09 10:13:17 +01:00
Dav999
99a1562d87 Only re-show language screen in default basedir
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).
2024-02-08 10:28:27 -08:00
Dav999
fbc3bd4d5a Make language screen show up once more
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).
2024-02-08 10:28:27 -08:00
KabanFriends
a301ef7c90 Add translation for "Editing and LQA" 2024-02-07 09:08:54 -08:00
Reese Rivers
35ef329575 Translated missing strings into Esperanto
This includes the Spanish variant names, "Editing and LQA",
the "mode enabled" bug text, and the debug mode freeze prompts.
2024-02-07 09:08:32 -08:00
TerryCavanagh
f9e71f1622 added "Editing and LQA" string to es_AR and es_419 2024-02-06 18:08:48 +01:00
TerryCavanagh
003f76464c minor updates to es_419, es_AR and pt_BR 2024-02-06 17:48:45 +01:00
TerryCavanagh
df559e64a9 minor updates to Brazilian Portuguese 2024-02-06 16:21:08 +01:00
Misa
365ee963eb Fix resize-to-nearest could be larger than desktop
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.
2024-02-05 18:03:32 -08:00
Dav999
929aaebc84 Align stability generator in es_419 and es_AR 2024-02-05 18:03:08 -08:00
Dav999
d6ec4ae72a Change LATAM Spanish meta.xml Oprime to Pulsa
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.
2024-02-05 18:03:08 -08:00
Dav999
139612b319 Fix placeholders in Arabic strings_plural
For some reason all of us overlooked that all of them were {n_crew},
even those which required a different placeholder key...

https://steamcommunity.com/sharedfiles/filedetails/?id=3154670529
2024-02-05 18:03:08 -08:00
TerryCavanagh
2ca912889d minor updates to Latin American Spanish and Argentinian Spanish 2024-02-05 12:04:47 +01:00
KabanFriends
c1a6a1d1df Improve translation for Down Under 2024-02-04 21:37:21 -08:00
KabanFriends
098e356500 Fix typo for Bilinear 2024-02-04 21:37:21 -08:00
KabanFriends
2e633578fd Fix translator credit 2024-02-04 21:37:21 -08:00
KabanFriends
05a3b16f7e Add missing string translations 2024-02-04 21:37:21 -08:00
KabanFriends
55ed42e34c Don't use wordy for numbers 2024-02-04 21:37:21 -08:00
TerryCavanagh
9d933f6c2f minor fixes for Silesian 2024-02-04 20:49:40 +01:00
Ally
3e57d6620c Update desktop_version/src/Graphics.cpp
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2024-02-03 18:11:11 -08:00
AllyTally
77a571017d Implement scaling modes manually
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.
2024-02-03 18:11:11 -08:00
Misa
935db27d39 Revert "Translate editor notes on-the-fly"
This reverts commit ec3de52970.

The complexity is not worth it for something that only lasts less than
two seconds anyway. *shrug*
2024-02-02 19:13:59 -08:00
Dav999
a770f3e126 Add missing spaces to script editor title in Welsh 2024-02-02 19:03:52 -08:00
Dav999
18b532cd9f Translate the current 4 proxy strings into Dutch
These are the 3 variants of Spanish and "Editing and LQA".
2024-02-02 19:03:52 -08:00
Dav999
df5607959b Sync Spanish LATAM and Spanish ARG language files
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)
2024-02-02 19:03:52 -08:00
Misa
ec3de52970 Translate editor notes on-the-fly
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.
2024-02-02 18:57:24 -08:00
Misa
67f41a780c Translate editor prompt text on-the-fly
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.
2024-02-02 18:57:24 -08:00
Misa
a7acf4e177 Fix cycling menus in editor not updating
While there's a check to recreate the menu if you cycle the language
while in a menu, editor menus are a special case and need specific
handling.
2024-02-02 18:57:24 -08:00
Misa
53ed33039f Translate level title and creator on-the-fly
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.
2024-02-02 18:57:24 -08:00
Misa
0aea27f237 Fix limits check not updating with CTRL+F8
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.
2024-02-02 18:57:24 -08:00
Misa
ad6e31aa12 Disable switching languages during cutscene tests
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.
2024-02-02 18:57:24 -08:00
Misa
3d61f9067b Translate NDM hardest room on-the-fly
Otherwise, cycling languages through CTRL+F8 would result in mismatched
languages.
2024-02-02 18:57:24 -08:00
Misa
531b151d12 Translate menu options on-the-fly
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).
2024-02-02 18:57:24 -08:00
Misa
c173dec8f9 Reset textcase in speak/speak_active
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.
2024-02-02 18:57:24 -08:00
Misa
861f724d90 Recompute textboxes on active input device change
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.
2024-02-02 18:57:24 -08:00
Misa
505956ae83 Remove textboxwrap() and wrap() return value
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.
2024-02-02 18:57:24 -08:00
Misa
c50da88ad4 Store original position of text box
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.
2024-02-02 18:57:24 -08:00
Misa
9b56a53d98 Add debug keybind CTRL+(SHIFT)+F8 to cycle lang
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.
2024-02-02 18:57:24 -08:00
Misa
84daa8fbc2 Call resize() before formatting text in cutscene
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.
2024-02-02 18:57:24 -08:00
Misa
3de2c543a5 Move all other text boxes to new system
These include the room name translator text boxes (which aren't
translated) and the foundlab and foundlab2 text boxes.
2024-02-02 18:57:24 -08:00
Misa
b14ca5e366 Move Game Complete sequence to new text box system
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.
2024-02-02 18:57:24 -08:00
Misa
7088858957 Add textbox indexes, update trinket/crew textboxes
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.
2024-02-02 18:57:24 -08:00
Misa
668d0c744d Add assert for giving func if it won't be used
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. :)
2024-02-02 18:57:24 -08:00
Misa
76483f96ef Move Comms Relay text boxes to new system
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.
2024-02-02 18:57:24 -08:00
Misa
82150fd3a9 Remove unnecessary calls to resize()
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.
2024-02-02 18:57:24 -08:00
Misa
28df0148b1 Transfer adjust call to applyposition
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.
2024-02-02 18:57:24 -08:00
Misa
3ef089ed3d Save context of untranslated text boxes
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).
2024-02-02 18:57:24 -08:00
Misa
d989c232af Recompute text boxes in Flip Mode
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.
2024-02-02 18:57:24 -08:00
Misa
94b9722b7b Split textwraps, move more textboxes to new system
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.
2024-02-02 18:57:24 -08:00
Misa
de00dd4031 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-02-02 18:57:24 -08:00
Misa
0ea0b8e00b Save text box centering state
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.
2024-02-02 18:57:24 -08:00
Misa
e8a231f2e2 Fix translating text box ignoring line limit
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.
2024-02-02 18:57:24 -08:00
Misa
8b03fbd9f4 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-02-02 18:57:24 -08:00
Misa
3b0757bd82 Add bounds check to setlinegap
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.
2024-02-02 15:00:44 -08:00
Misa
2b476bb4c1 Remove trailing whitespace on getlinegap decl 2024-02-02 14:59:52 -08:00
TerryCavanagh
69684994be added translation category support to ending credits also 2024-02-02 18:24:49 +01:00
TerryCavanagh
5f9e326b67 Added proxy string for "Editing and LQA"
I checked the pt_BR one with our translators, so that one's correct
2024-02-02 18:15:45 +01:00
TerryCavanagh
62ab594976 added Ivan Lopes and Lucas Nunes to credits 2024-02-02 17:37:23 +01:00
TerryCavanagh
9986696604 updated pt_BR translation with new proofread version 2024-02-02 16:04:24 +01:00
TerryCavanagh
2afcf85ad6 added placeholder translations for the three Spanish variants in the credits
these are proxy strings for now, will need to be replaced properly later
2024-02-01 20:25:00 +01:00
TerryCavanagh
1db33d9cb8 added Guido Di Carlo and LocQuest to credits 2024-02-01 20:23:39 +01:00
TerryCavanagh
8b73a38ed2 don't apply linegap to certain special textboxes
level complete, game complete and "You have rescued a crew member!"
2024-02-01 20:03:17 +01:00
TerryCavanagh
cc1528aacc add a one pixel gap between each line in textboxes (main game only) 2024-02-01 20:03:17 +01:00
Terry Cavanagh
2807524c78
Added link to Wii port github in license exceptions 2024-02-01 18:23:30 +01:00
Alberto Mardegan
a5575933b2 Update to latest FAudio
This fixes audio playback on big-endian machines.
2024-02-01 11:08:02 -05:00
Terry Cavanagh
1b70fc5b15
Added Wii port to License exceptions 2024-02-01 12:07:23 +01:00
TerryCavanagh
b67ceb2c0f Merge branch 'master' of https://github.com/TerryCavanagh/VVVVVV 2024-02-01 11:34:01 +01:00
TerryCavanagh
319ed3a22e renamed es_AR and es_419 folders 2024-02-01 11:33:51 +01:00
Ethan Lee
5523d5597d CI: Replace -o with -OutFile, works around Invoke-WebRequest regression 2024-01-31 18:19:22 -05:00
TerryCavanagh
b1f378de60 Added Argentine Spanish localisation 2024-01-31 18:37:31 +01:00
TerryCavanagh
a6b8b90f3c Added Latin American Spanish localisation 2024-01-31 18:36:57 +01:00
Dav999
448c4a5514 Apply updates to Arabic language files
This update was delivered earlier today!
2024-01-25 16:16:00 -08:00
Terry Cavanagh
1f7c9fa9ff
added link to ARMHF port 2024-01-24 11:00:46 +01:00
Misa
1c6cfcd2a5 Remove 'all' argument from setrtl
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.
2024-01-23 16:53:01 -08:00
Misa
a9f0d81804 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-23 15:33:38 -08:00
Misa
ea8633514d Fix using int for mode indicator flags
For consistency. Print flags are supposed to be uint32_t.
2024-01-23 14:29:03 -08:00
Misa
8d2a0c2457 Allow overriding state lock in glitchrunner <= 2.2
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.
2024-01-23 13:55:21 -08:00
Terry Cavanagh
ac42e7f774
typo 2024-01-23 20:30:44 +01:00
Terry Cavanagh
df1a95a85d
Added license exception for armhf Port 2024-01-23 20:29:26 +01:00
Dav999
7cd1cae55a Rename capitalized no death mode in Dutch
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.
2024-01-22 16:31:15 -08:00
Dav999
e7267c6eb6 Add missing blank lines in Irish congrats boxes
The three "Congratulations!" boxes which were supposed to have a blank
line in them didn't.
2024-01-22 16:31:15 -08:00
Dav999
5345d46c3d Fix a few spacing problems in Welsh
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.
2024-01-22 16:31:15 -08:00
Dav999
1bc3e8e666 Fix Esperanto [button?] in Violet cutscene 2024-01-22 16:31:15 -08:00
Misa
67f56985eb PR template: Clarify compensation
Translators have been getting compensated for localization. Sometimes
they submit pull requests for their changes, but just because they do
doesn't mean that they won't get compensated.

The intent of this line was to make sure that any random contributor
wouldn't expect any compensation for their changes, so adding another
clause here still keeps that expectation while making it clear that it's
not like Terry _never_ compensates people. Even if, as a Swedish man
once sung, "Terry is a monster, I think we all know that".
2024-01-22 20:11:42 +01:00
Alberto Mardegan
b5abc3956c Fix loading of PNG resources on big-endian machines
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.
2024-01-22 10:48:38 -05:00
Misa
32e6ab6ecd README: Add step for compiling
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.
2024-01-22 00:18:20 -08:00
Misa
2f217dad56 Fix segfault: unwordwrap string w/ 2 start \ns
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.
2024-01-20 17:52:17 -08:00
Dav999
ebd4fa8ad8 Clean up outdated strings
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!
2024-01-19 16:31:39 -08:00
Dav999
403136df46 Translate new strings to Dutch 2024-01-19 16:31:39 -08:00
Dav999
5a91d09a20 Add in-game mode indications (#1096) to language files
These were not in the English or any other language files. They should
be though, so that they can be translated and generally kept track of.
These aren't urgent either, since we have proxy strings that are used
if these are untranslated.
2024-01-19 16:31:39 -08:00
Dav999
3849ed7a24 Sync language files
This hasn't been done since before we got some deliveries for 2.4,
so there are a few languages which added apostrophes as ' instead of
&apos; in the XML (which is not wrong, but it gives diff noise whenever
there's a sync since VVVVVV writes them back as &apos;...)

Also, we never synced "[Press {button} to toggle gameplay]" across
language files (now two strings with unfreeze/freeze), but that was
also a pretty last-minute string as far as I remember. Arabic did have
it because that language was added after the string was added, so it
got copied from English. I don't think this one is that urgent to
translate into every language for 2.4.1 since it's pretty well hidden
for most people, and it's surrounded by things that have to be English,
so it's as if it's supposed to be like that. Let's just include these
with whatever the next batch of strings is.
2024-01-19 16:31:39 -08:00
Dav999
60fd0bc0c2 Replace [Press {button} to toggle gameplay] by freeze/unfreeze
This is both easier for translators ("toggle" can be an annoying word)
and is useful in general because you can tell if gameplay is frozen
without having to have anything in the room that should normally be
moving but isn't.

I didn't follow the rule in lang/README-programmers.txt to keep the
original string around as ***OUTDATED*** in this case, since I know
only Arabic has it translated - we can just tell the Arabic translators
on Discord that this string was replaced.
2024-01-19 16:31:39 -08:00
Ethan Lee
afbcb3f867 Add support for GameCube glyphs.
It's kind of a bummer that L/R don't actually do anything... we should add ZL/ZR support at some point.

Also note that GameCube binds X to 'back' rather than B, this will be fixed by SDL_ActionSet for 2.5.
2024-01-18 00:10:20 -05:00
TerryCavanagh
ceaaea1597 fix Arabic credits
Eternal Dream Arabization team name was missing from the main credits page
2024-01-17 15:59:16 +01:00
Dav999
d299b42ee8 Replace Chinese font
The original Chinese font (which we call "the Indienova font") was
received from the Chinese translators directly, and didn't come with
any license or copyright information other than that it was made by
Indienova. Questions have now been raised about the actual origin of
the characters in the font, and while we do have confirmation from the
translators that we're probably in the clear, they did suggest another
font for us to use, which we're switching to to be sure.

Some background information: the ideal font would probably be Ark Pixel
(https://github.com/TakWolf/ark-pixel-font/), but this font is not
finished yet. Therefore, the creators of Ark Pixel have made a font
that can be used as a placeholder to use in the meantime, Fusion Pixel
(https://github.com/TakWolf/fusion-pixel-font), which combines some
other fonts together in order to get full coverage. This is the font
we're now switching to.

It's not _that_ simple though - the ASCII part of Fusion Pixel is kinda
bad for us using it as a monospaced font. Normally I just replace the
ASCII set by the fullwidth characters, but in this font they were
almost entirely the same. So I instead picked the fullwidth characters
from Galmuri 12px, which is one of the "fusioned" fonts. Interestingly,
we happen to also use the 10px version of this as our Korean font, and
I like these Latin letters, so yay.

I also made the call to split the Chinese font into separate variants
for Simplified and Traditional Chinese. I was aware of the problem with
the Han Unification, but the Traditional Chinese translator said the
Indienova font also contains all the Traditional Chinese characters,
and they proofread the translation, so it was probably fine. Apparently
the difference between Simplified and Traditional Chinese variants of
the same characters are not that big, and it's acceptable. But
Fusion Pixel gives us separate versions of the font for Simplified and
Traditional Chinese, so this is a chance to get it right. Just kidding,
Fusion Pixel's Traditional variant switches out many characters that
were shared between Simplified and Traditional Chinese to Japanese
variants which are noticeably different. So it would be better to keep
using the SC font for TC, just like the Indienova font is SC only.
However:  Ark Pixel does have a version with correct characters for
Traditional Chinese! So for the TC version of our font, I just took all
Chinese characters from the TC version of Ark Pixel where available.
That way, all characters I checked have changed to TC variants
correctly.
2024-01-16 16:54:28 -08:00
Dav999
52b099f7d0 Replace U+2013 (–) by standard dash in Ukrainian
The Ukrainian strings.xml turns out to have two en-dashes which are
not in the font. This commit replaces them with standard dashes.
2024-01-16 16:46:24 -08:00
TerryCavanagh
9c656fd5e1 Added SnDream to contributors
for this pull request: https://github.com/TerryCavanagh/VVVVVV/pull/1119
2024-01-16 12:05:45 +01:00
TerryCavanagh
82576c73ca Copied new localised sprites to zh_TW 2024-01-16 12:05:45 +01:00
sndream
f70eb0db86 Simplified Chinese graphics fine-tuning 2024-01-16 12:05:45 +01:00
TerryCavanagh
8dd5414e8d minor fixes for Welsh
as suggested from player feedback, confirmed by translator
2024-01-14 00:35:34 +01:00
TerryCavanagh
4acc96e707 added Space-G to contributors
for this pull request: https://github.com/TerryCavanagh/VVVVVV/pull/1114
2024-01-13 15:47:09 +01:00
Space-G
47561d44f0 Fixes some pt_BR mistranslations noted by zaratustra on Cohost 2024-01-13 15:44:46 +01:00
Space-G
e51ae2a3c5 Fixes a handful of pt_BR mistranslations 2024-01-13 15:44:46 +01:00
David Galiev
3483893a70 Add Russian translations for jukebox songs in the "NEXT UNLOCK" section 2024-01-13 15:39:03 +01:00
David Galiev
c550deca4d Fix Russian translation for Pushing Onwards editor string
This string is mistakenly using the translation for "Potential for Anything" instead
2024-01-13 15:39:03 +01:00
Misa
c0faa856e8 Japanese: Fix wrong abbreviation period in credit
"Nicalis. Inc" is incorrect, and it should be "Nicalis Inc.". The "Inc."
stands for "Incorporated".
2024-01-12 01:06:24 -08:00
Misa
84322755f2 Remove number translations that are numerals
This removes every single translation of a wordy number that just
replaces it with the numeral.

This is because the documentation in README-translators.txt specifically
says

    It's also possible to leave the translations for all the numbers
    empty. In that case, numeric forms will always be used.

However, the translators for Japanese, Korean, and European Portuguese
clearly either didn't read this, or forgot to do so.

There is a very good reason to leave them alone if you want numerals;
namely that if you fill them in, you are prone to making errors. Like,
say, Japanese translating "Twelve" as "23", which is exactly what
happened. By blanking every translation, that error is fixed.
2024-01-12 01:02:55 -08:00
TerryCavanagh
f27374d92e typo in european portuguese 2024-01-11 11:22:46 +01:00
mothbeanie
7ff2e818cd Revert text image fading
This reverts the following commits:

- 29f05c41b1
- f1bf1f683c
- a7b22919ae
- 2ed1aac67d

Recently, text images were changed to fade in with textboxes, where
before they previously appeared after the fade. This created a charming
effect where the images would appear to "load in" once the textbox
finishes fading in. This behavior really complements the retro
aesthetic the game is going for. Changing it to a fade is a needless
change in direction, as it was not a bug in the first place and looked
good already.

Additionally, custom levels have used text images (levelcomplete and
gamecomplete) in creative ways by replacing them with something else
to show as 'foreground' or as a cutscene image. Changing text images
to fade has unintended consequences for levels that have utilized
them in this fashion.
2024-01-10 17:43:03 -08:00
Misa
5e1a1f80ac Bump version to 2.4.1
The plan is for 2.4 development to continue a bit with some minor new
features. 2.4.0 was the grand release containing most of the new stuff.
2024-01-10 17:40:39 -08:00
Misa
29f05c41b1 Fix warnings when calculating image color
This fixes warnings about narrowing and missing a component of the
struct.
2024-01-10 08:27:34 -08:00
AllyTally
23915e0d22 Fix typing pipes in save/load prompts in editor
13d6b2d64c adds a check where you can't
type a pipe in script/terminal input fields anymore. It does this
completely incorrectly, checking if certain variables are set instead
of checking what's actually trying to be done.

This commit fixes that, simplifying the check a lot in the process.
2024-01-10 08:23:20 -08:00
Dav999
48a71f4c22 Last update to Arabic font 2024-01-10 08:00:04 -08:00
Misa
13d6b2d64c Disable typing pipes in script names
This disables typing the pipe character in the data fields of terminals
and script boxes. Care has been taken to make sure that it's still
possible to type pipes in room text.

This is because pipes are the line separator in the big XML tag that
stores every single script line, and thus a script name with pipes would
end up being split up after the level file has been saved and loaded
again.
2024-01-10 07:59:49 -08:00
AllyTally
f1bf1f683c Fix text images not fading
While a7b22919ae makes text sprites
modulate their RGB values, text images continued using alpha,
despite alpha blending not even being enabled, so the initial
commit didn't work right either.
2024-01-10 07:57:54 -08:00
Dav999
ff25e18945 Move author face to the right for RTL languages
This is quite a last-minute thing that was almost getting called off by
me discovering a critical segfault just now in testing this (whew) but
this shouldn't hurt.
2024-01-10 07:57:40 -08:00
Dav999
95c5bd80e1 Revert "Disable typing pipes in script names"
This reverts commit a806b072bd.

It causes an instant segfault if there's no entities or if you're not
editing terminals/script boxes or something, whatever it's not
crucial as a last-minute fix.
2024-01-10 16:42:01 +01:00
TerryCavanagh
8bfcb40a17 updates to roomnames for Arabic 2024-01-10 15:15:26 +01:00
Ethan Lee
b619f38086 Fix Switch button glyphs being double swapped 2024-01-10 09:02:09 -05:00
Ethan Lee
07d4bdbc14 Update PhysicsFS to 3.2.0 2024-01-10 08:55:59 -05:00
TerryCavanagh
99a17e7d5c added Esperanto translation for "Arabic" 2024-01-10 14:54:46 +01:00
TerryCavanagh
f0760b0192 added translation of the word "Arabic" to all* languages
*missing Esperanto, but I'll check that in later
2024-01-10 12:10:15 +01:00
TerryCavanagh
9ef531634a minor proofreading changes for Simplified Chinese 2024-01-10 11:30:25 +01:00
Misa
5948168a40 Fix delayed notification of NDM unlock
No Death Mode is intended to be unlocked by getting at least S-rank in
at least 4 time trials. Before 2.3, completing a time trial put you at
the main menu, so you would always be notified of having unlocked No
Death Mode once you went to the play menu again. But since 2.3,
completing a time trial puts you back at the Time Trial selection
screen, which isn't the play menu, so you would need to back all the way
out first in order to get the notification. And since you don't actually
unlock No Death Mode until you see the notification, this would be
required to be able to play No Death Mode.

To fix this, I decided to do something a bit kludge-y and just re-use
the code to check and unlock No Death Mode when the player presses
ACTION on the Time Trial complete screen (and there's also another path
by pressing Escape). At least I put it in a function, so it's not a pure
copy-paste, although it might as well be. I don't have time to think of
a proper solution, but it would probably involve disentangling unlock
notifications from Menu::play, for starters. But that's for later.
2024-01-10 01:33:10 -08:00
Misa
a806b072bd Disable typing pipes in script names
This disables typing the pipe character in the data fields of terminals
and script boxes. Care has been taken to make sure that it's still
possible to type pipes in room text.

This is because pipes are the line separator in the big XML tag that
stores every single script line, and thus a script name with pipes would
end up being split up after the level file has been saved and loaded
again.
2024-01-10 00:33:39 -08:00
Misa
a1c7291bc4 Don't draw mode indicator text if there is none
This makes it so that the boolean to draw mode indicator text is false
if there aren't any modes active.

Otherwise, when loading in, the in-game timer would only come in after a
few seconds instead of appearing when the fade-in finishes.
2024-01-10 00:23:40 -08:00
Misa
5e28567009 Fix pressing ESC in some menus not playing music
This fixes a bug where pressing Escape in the following menus would not
play Presenting VVVVVV (the title screen music) and would instead leave
you with silence: Game Complete, Time Trial complete, Game Over, and No
Death Mode complete.
2024-01-10 00:19:16 -08:00
Misa
67df8a9679 Add bounds check to textcase() command
This makes it so that only inputs between 1 and 255 inclusive will be
accepted. Otherwise, the command has no effect.

This is because the text case is stored as one byte in a string, and a
value of zero would be the null terminator.

We also want to minimize potential weirdness with integer wrapping if we
accept inputs from outside those bounds. While the textcase variable as
used throughout the codebase is plain unqualified `char` (which, unlike
other integers, exists in a quantum superposition of being signed and
unsigned depending on compiler, machine, and various other stuff) and so
there still might be issues there, we definitely don't want anything
higher than 255.
2024-01-09 23:57:14 -08:00
Misa
34e7b1af61 Update FAudio to 24.01
FAudio is usually very stable, after all, our very own Ethan Lee
maintains it. This should be safe to bump.
2024-01-09 23:15:57 -08:00
Misa
9bb463ab0e Add third_party/ to CI workflow paths
This adds the third_party/ directory to the list of paths that will
trigger a CI workflow when changed.

Not all updates to third-party dependencies will necessarily change
code, but we bump dependencies infrequently so there's not much of a
problem with triggering CI on every change to third_party/.
2024-01-09 23:13:39 -08:00
Misa
2a9003ae45 Reverse left & right nav keys in editor for RTL
If the language is RTL, then the left and right menu navigation keys
should be reversed, because the menu layout goes from right to left.

This is to be consistent with the other menus in the game. The editor is
just a special case so it was overlooked.
2024-01-09 21:05:26 -08:00
Misa
163f3a0fde Allow using W and S in editor menus
There was an inconsistency where W and S couldn't be used in place of
the Up and Down arrow keys, but this has been fixed.

This only applies where W and S otherwise are not bound to anything
else. E.g. not the main editor (where W changes the warp direction and S
saves the level) and not the script editor (where W and S can be typed
inside a script), but the script list is fine.
2024-01-09 20:54:11 -08:00
Misa
0e40892eb0 Arabic: Fix translation for Vertigo roomname
By Montassar.

Just adds a U+200F (RTL mark).
2024-01-09 20:25:15 -08:00
Dav999
ef13775456 Add Arabic language files
Here are all the language files for Arabic!
Thanks to Montassar and Seif for their hard work to localize the
entire game in such a short timespan.
2024-01-09 20:20:14 -08:00
Dav999
7b92e7af4d Update Arabic font 2024-01-09 20:20:14 -08:00
Misa
a7b22919ae Modulate text sprite RGB instead of using alpha
Using actual alpha is prone to bugs with doubling alpha, etc. if there
are multiple sprites on top of each other.
2024-01-09 19:22:40 -08:00
Misa
2ed1aac67d Fade text box overlays with text box
Currently, it's a bit jarring that text box overlays (which are text box
images, e.g. Level Complete and Game Complete, and sprites, e.g. the
crewmates) will suddenly appear when their text box has fully faded in
and suddenly disappear once it starts fading out.

This makes it so that text box overlays will be faded in and out
smoothly along with the rest of the text box fading in and out.

Transparent text boxes are not affected, as they do not fade in and out
at all. Thus, text box overlays in transparent text boxes will still
suddenly appear and disappear as usual.
2024-01-09 18:38:40 -08:00
Dav999
ba92174c71 Use wordy2 in Dutch to capitalize één as Eén
The number "one" in Dutch is "één" (silly, I know :P). Capital letters
can have accents, but there's an exception where for this specific
word, the first accent is much more often left off than not. So I'm
now using wordy2 as the uppercase variants of all the numbers, and
using that instead of the |upper flag.
2024-01-09 18:05:15 -08:00
Misa
40b0f9ec36 Account for position of prompt in mode text
I forgot that the position of the activity zone can vary based on
setactivityposition() in custom levels. So account for that.
Additionally, if the activity zone prompt is far down enough, then we
don't need to move the mode text at all.
2024-01-09 17:48:53 -08:00
Misa
18730b465e Allow taking multiple screenshots in same second
Dav999 notified me that if multiple screenshots are taken in the same
second, the second screenshot has `_2` appended to it and so on. We do
the same here by storing the current timestamp and a counter.

This doesn't prevent overwriting files if you have system time that
changes, or have multiple instances of VVVVVV running at the same time,
but my position on those cases is as follows: Don't do that.
2024-01-09 17:21:18 -08:00
Misa
d8b97db6a8 Make sure mode text is visible above act prompts
This makes the mode indicator text be visible even if there is an
activity zone prompt on screen, by making it so that it gets moved if an
activity prompt is being rendered.

This is to make sure that it's visible no matter what, even if e.g. a
custom level starts the player on an activity zone.
2024-01-09 17:06:33 -08:00
Misa
9676bf0006 Fix visual clash between timer and trophy text
Trophy text can overlap with the timer. How bad it is depends on the
localization but in English some text definitely overlaps.

Simple fix is to disable rendering the timer if we are rendering any
trophy text.
2024-01-09 16:56:38 -08:00
Misa
b3b001c2a8 Place GraphicsResources functions in header file
These are functions used in other files that are not on the
GraphicsResources class but are implemented inside the
GraphicsResources.cpp file. For some reason they were never put in the
GraphicsResources.h file until now, even though it's a perfectly good
header file to put them in.
2024-01-09 16:03:05 -08:00
Misa
10030a4340 Save screenshots with proper filenames
Filenames are timestamped now, down to the second. If you take multiple
screenshots in the same second, then the last one will overwrite the
others. This seems to be how other screenshot programs operate so I
don't think it matters if you can't take more than one per second.

Additionally, 1x screenshots (320x240) will go in the 1x/ subdirectory,
and 2x screenshots (640x480) will go in the 2x/ subdirectory.
2024-01-09 15:48:41 -08:00
Misa
40f6f83328 Add border to indicate taking screenshot
Originally, I was thinking of adding a notification text that you took a
screenshot, but this is better because it is language-agnostic and it
doesn't contribute to potential UI clutter/clashing.

It flashes yellow if the screenshot successfully saved, and red if it
didn't.
2024-01-09 15:48:41 -08:00
Misa
93ec2c6cca Upscale screenshots 2x
The plan is to have Steam screenshots always be 2x, but in the VVVVVV
screenshots directory (for F6 keybind) save both 1x and 2x.

Again, just for now, the 2x screenshot is being saved to a temporary
location for testing and will get proper timestamps later.
2024-01-09 15:48:41 -08:00
Misa
20f0fafa5e Fix screenshots in Flip Mode
One problem with internal screenshot capture is that we rely on SDL's
render subsystem to flip the screen in Flip Mode, while leaving our
actual screen untouched. Since we source the screenshot from the screen
and not what SDL renders, we need to flip the screenshot ourselves when
saving an internal capture.

To do this, we need to support 24-bit colors in DrawPixel() and
ReadPixel(). Luckily, this isn't too hard to do. A 24-bit color is just
a tuple of three bytes, and we just need to do a small amount of bitwise
math to pack/unpack them to a single integer for SDL_GetRGB() and
SDL_MapRGB().
2024-01-09 15:48:41 -08:00
Misa
ae5ef9753c Hook Steam screenshots and send internal capture
Using the Steamworks API, we can hook the screenshot function and listen
for a screenshot request callback to send in our own screenshot. This
applies the screenshot improvements to Steam screenshots as well.

Doing this requires adding some C wrapper functions, as our interface
with the Steam API is only conducted through C.
2024-01-09 15:48:41 -08:00
Misa
f05827f268 Add support for internal screenshots
"But people already have screenshot tools", you might protest. The
rationale is simple: If you play with any video setting other than 1x
windowed (no stretching and no letterbox), then your screenshot will be
too big if you want the internal resolution of 320x240, and downscaling
will be an inconvenience.

The point is to make screenshots based off of internal resolution so
they are always pixel perfect and ideally never have to be altered once
taken.

I've added the keybind of F6 to do this.

Right now it saves to a temporary test location with the same filename;
future commits will save to properly-timestamped filenames.
2024-01-09 15:48:41 -08:00
Dav999
060fe6938d Add support for right-aligned roomtext
This is mostly so people making levels in an RTL language have a more
pleasant and logical experience. If roomtext is placed in a level set
to RTL, it will get p1=1, which makes that roomtext right-aligned.
Because, imagine for English you click to place roomtext, and the text
runs left of where you clicked, which wouldn't be logical.

Since it's an entity-bound property, switching RTL on and off either in
the editor or via a script does not affect existing entities.
2024-01-09 13:13:04 -08:00
Dav999
7b46087077 Update README-translators.txt for some recent additions
These things hadn't been added to the documentation yet:
- <font> in a language's meta.xml
- <gamepad_hint>
- <rtl>
- wordy2/translation2
2024-01-09 12:12:54 -08:00
Misa
fe21be87a2 Remap lang reload keybind from F12 to F8
This remaps the keybind to reload language files from F12 to F8.

This is because the F12 keybind conflicts with the default Steam keybind
to take a Steam screenshot.

I chose F8 because it is next to another keybind that reloads stuff, F9
(which reloads assets in the editor).

Fixes #1089.
2024-01-09 11:14:11 -08:00
Misa
b077f24a50 CWrappers.cpp: Extern "C"
While working on adding a screenshots keybind, I encountered a link
error with these functions. Wrapping them in `extern "C"` fixed it. It's
most likely due to the fact that they were `extern "C"` in the header,
but not in the `.cpp` file. They should be both `extern "C"`'d
regardless.
2024-01-09 11:11:15 -08:00
TerryCavanagh
4b2b3f2a08 another spanish wordy2 fix 2024-01-09 15:26:55 +01:00
TerryCavanagh
607bfd89a7 more spanish wordy2 changes 2024-01-09 13:58:11 +01:00
TerryCavanagh
c0d6002d44 added wordy2 support to spanish
uno/una in e.g. "Y has encontrado {n_trinkets|wordy2} baratijas."
2024-01-09 13:27:32 +01:00
Misa
858c2cb081 Indicate modes when loading in to gameplay
If you load in to gameplay with invincibility mode, glitchrunner mode,
Flip Mode, or slowdown enabled, then there will be text displayed on
screen for a few seconds that says so.

This is to serve as a useful reminder. A common pitfall with using
invincibility is forgetting to turn it off when you don't want it
anymore. What usually happens is that players forget that they have it
on until they encounter a hazard. Now, they can realize it as soon as
they load in.

See #1091.
2024-01-08 20:01:27 -08:00
Dav999
f846ba5d59 Inverse centered text screen border for RTL
If text is set to be centered, but is so long that it starts running
offscreen on both sides, the print function instead makes the text
start no further left than the left border of the screen (x=0).
This is because text running offscreen at the end only is more readable
and looks less sloppy than running offscreen at both sides.

For RTL, the opposite applies, so it now also works oppositely for RTL
prints, where centered strings will only run offscreen on the left side
of the screen.
2024-01-08 19:17:44 -08:00
Dav999
d78338f9ef XFLIP prompts in the editor 2024-01-08 19:17:44 -08:00
Dav999
1b55c6501c Swap definitions of left and right textbox padding
Spaces on the left and right would end up on the other side in RTL,
which made the "You have rescued a crewmate!" text overlap with the
crewmate sprite, and makes the [C[C[C[C[Captain!] dialogs have spaces
on the left instead of on the right. So, best thing is to just swap
the directions so that they match.
2024-01-08 19:17:44 -08:00
Dav999
945f0edaae Ignore directional control chars in font::len()
They're invisible in font::print(), but they were still considered
characters with widths in the width function. This change made the
levels screen look better in RTL too - I was wondering why the level
options were too far left.
2024-01-08 19:17:44 -08:00
Dav999
18dfcff985 XFLIP cursor in roomname translator mode 2024-01-08 19:17:44 -08:00
Dav999
fd0aefe0ee XFLIP LOADING... screen 2024-01-08 19:17:44 -08:00
Dav999
96fa932a8a Don't pass newline characters to bidi algorithm
If you copy-paste a newline character where it's not interpreted, such
as in a level title, the print function wouldn't treat it any special.
font::print_wrap() would, but that's not used here.

However, now that bidi is involved, the newline is passed straight to
SheenBidi which interprets it as a new line (which would need a new
SBLine to be created, or maybe even a new SBParagraph if there's two).
All while we're still treating it as a single line. This means the text
would just stop being displayed after the first newline. This is now
fixed by treating all newlines as spaces.
2024-01-08 19:17:44 -08:00
Dav999
c8dbdc890e Flip Game Complete screen
Obvious "Key: value" things here, this one was easy.
2024-01-08 19:17:44 -08:00
Dav999
3d04d0acbc Mirror time trial results screen
This has a lot of reading-orientation stuff on it like "Key: value",
so easiest is to just flip the whole design of the screen rather than
trying to flip individual strings.
2024-01-08 19:17:44 -08:00
Dav999
79a0ad8493 XFLIP analog stick sensitivity slider properly
The slider itself was getting mirrored, but not the labels
(Low/Medium/High). This fixes that.
2024-01-08 19:17:44 -08:00
Dav999
5620edeb6b Fix orientation of levels list
I forgot to add the PR_RTL_XFLIP flag to these menu options, so they
were always left-aligned, no matter what.

What actually took me a bit to figure out was how to make the level
completion stars work regardless of the contents of the title - the
stars should always be to the left of the title in an LTR language, and
always to the right of the title in an RTL language. Level titles can
contain bidi characters regardless of the level's rtl flag being set,
so I just let bidi handle all the level menu options, with some control
characters to make sure everything always appears in the correct order.
2024-01-08 19:17:44 -08:00
Dav999
3f8333400f Base text box padding/centering on font width instead of codepoints
Stuff like centertext="1" and padtowidth="264" in cutscene translations
looked wrong in RTL mode, both with Arabic and English text. For Arabic
text, I could easily fix the problem by not counting the number of
codepoints (and assuming they all have the same glyph width), but by
instead taking the width of the string as reported for the font, and
dividing it by the glyph width. This leaves English text still looking
weird in RTL mode. But this shouldn't be a problem either: the Arabic
translations will probably be in Arabic (where the problem doesn't
happen), and I can get English text to show up fine by wrapping it in
U+2066 LEFT-TO-RIGHT ISOLATE and U+2069 POP DIRECTIONAL ISOLATE. So it
looks like an inherent quirk of bidi, that translators familiar with
bidi can easily grasp and fix.

This is main-game only functionality, so it shouldn't break existing
custom levels. We should just make sure textboxes in other languages
aren't broken, but from my testing, it's completely fine - in fact, it
should've improved if it was broken.
2024-01-08 19:17:44 -08:00
Dav999
25bdf0866a Make left/right key input correct for RTL menus
Instead of just up/down, you can also control menus with left/right.
Which is illogical in Arabic... No big deal, I imagined this code
to become much worse than it did. (And action sets is probably gonna
refactor the whole thing anyway)
2024-01-08 19:17:44 -08:00
Dav999
2b22f7cda2 Add PR_RTL_XFLIP to some important places
Okay, the "Font:" thing needed some local code after all, because both
the interface font as well as the level font are used there. But it's
good enough - all the other places can just use the flag.

Notably, I also used this for the menus, since the existing ones are
kinda LTR-oriented, and it's something that we don't *really* have to
do, but I think it shows we care!
2024-01-08 19:17:44 -08:00
Dav999
eac612c309 Add PR_RTL_XFLIP print flag
This lets you mirror the X axis specifically in RTL languages, so the
left border is 320 and the right border is 0, and invert the meaning of
PR_LEFT (0) and PR_RIGHT. Most of the time this is not necessary,
it's just for stuff where a label is followed by a different print,
like "Font: " followed by the font name, time trial time displays, etc
2024-01-08 19:17:44 -08:00
Dav999
3588168c4d Right-align textboxes in RTL languages
Most of this diff is just moving some existing code around, and
changing inline things to variables that could be changed more easily.
2024-01-08 19:17:44 -08:00
Dav999
27e83b6008 Add font::is_rtl(uint32_t flags)
This will return if the given flags indicate RTL properties
(such as textboxes being right-aligned).
2024-01-08 19:17:44 -08:00
Dav999
ed0c9b6b1f Add setrtl(on/off) scripting command
With the <font> tag (which doesn't indicate RTL-ness as explained),
we've had a setfont(font) scripting command. Now we have an <rtl>
tag, so we need a setrtl(on/off) command too to control that.
2024-01-08 19:17:44 -08:00
Dav999
29e2b19698 Add RTL level property and print flag
Again, the RTL property controls whether textboxes will be
right-aligned, and that kind of stuff. It can't be font-bound, since
Space Station supports Hebrew characters and we want to be able to
support, say, a Hebrew translation or Hebrew levels in the future
without having to make a dedicated (or duplicated) font for it.
Therefore it's a property of both the language pack as well as custom
levels - like custom levels already had a <font> tag, they now also
have an <rtl> tag that sets this property.

Right now, we'll have to hardcode it so the menu option for the Arabic
font sets the <rtl> property to 1, and all the other options set it to
0. But it's future-proof in that we can later decide to split the
option for Space Station into an LTR option and an RTL option (so both
"english/..." and "עברית" would select Space Station, but one sets the
RTL property to 0 and the other sets it to 1).
2024-01-08 19:17:44 -08:00
Dav999
37c4f76988 Add RTL language property
This doesn't have an effect yet, but it'll do things like
right-alignment in textboxes and other design-flipping.
2024-01-08 19:17:44 -08:00
Dav999
2003eed2a5 Hide unicode directional control characters
They shouldn't be looked up in the font and displayed under any
circumstances.
2024-01-08 19:17:44 -08:00
Dav999
45ec77973b Implement bidi_should_transform()
This now returns true if any of the characters in the text belong to
the Arabic or Hebrew alphabet, or are one of the Unicode directional
formatting characters. This is just so the bidi machinery doesn't have
to run 100% of the time for 100% of the languages. I will also make it
so the Arabic language pack, as well as custom levels, have an RTL
attribute that always enables bidi (and does things like
right-alignment in textboxes and other design-flipping)
2024-01-08 19:17:44 -08:00
Dav999
822755a75f Add Arabic ligature support
Montassar prepared a list of all the ligatures that needed to be
supported, which was a simple A+B->C table, so that one was not too
difficult either!
2024-01-08 19:17:44 -08:00
Dav999
30c68f70bd Add Arabic font
The Arabic part was made by Montassar Ghanmi, it didn't have its own
Latin part so I just copied 00-FF from Space Station.
2024-01-08 19:17:44 -08:00
Dav999
ef712f9f0c Add support for Arabic reshaping
This code can probably be polished a bit more, but the hard part is
over!

This part was written with guidance of this code:
https://github.com/TerryCavanagh/hx_arabic_shaper
2024-01-08 19:17:44 -08:00
Dav999
e011273eb3 Add Arabic reshaping lookup table
This adds the lookup table and an accompanying hashmap that will be
used for reshaping Arabic - it's not yet used though.
2024-01-08 19:17:44 -08:00
Dav999
59ccdbea00 Implement bidi reordering at display time
I'm now using SheenBidi to reorder RTL and bidirectional text properly
at text rendering time! For Arabic this is still missing reshaping, but
everything's looking really promising now!

The code changes are really non-invasive. The changes to Font.cpp are
absolutely minimal:

     1305+    if (bidi_should_transform(text))
     1306+    {
     1307+        text = bidi_transform(text);
     1308+    }

There's now a FontBidi.cpp, which implements these two functions,
notably bidi_transform(), which takes a UTF-8 encoded string and
returns another UTF-8 encoded string that has bidi reorderings and
reshapings applied.

In that function, SheenBidi gives us information about where in the
input string runs start and end, and on a basic level, all we need to
do there is to concatenate the parts together in the order that we're
given them, and to reverse the RTL runs (recognizable by odd levels).

As this is a proof-of-concept, bidi_should_transform() still always
returns true, applying the bidi algorithm to all languages and all
strings. I'm thinking of enabling bidi only when the language/font
metadata enables RTL (which could be for the interface or for a custom
level), or outside of that, at least when RTL characters are detected
(such as Arabic or Hebrew Unicode blocks).
2024-01-08 19:17:44 -08:00
Dav999
5766e6c426 Add SheenBidi to CMakeLists
Not much to see here, just making sure SheenBidi is compiled with the
game and we can include its headers.
2024-01-08 19:17:44 -08:00
Dav999
a9a1d7e1d6 Add SheenBidi submodule
I'm going to give it a shot to use this for bidi text support, it looks
like it's a pretty lightweight, compatible and low-dependency library
which is definitely a plus. We'll still need to do reshaping ourselves,
but that's the easy part compared to bidi.
2024-01-08 19:17:44 -08:00
AllyTally
8af57a072a Fix another incorrect autotile 2024-01-08 13:17:59 -08:00
TerryCavanagh
6e436880ab minor fix to Portuguese (pt)
from translator's reply to Kokasgui's feedback
2024-01-08 16:07:27 +01:00
Misa
47eb7acb44 Only trigger CI workflows for code or CI changes
This makes it so that the main CI workflow will only trigger if a change
is made to a code file in desktop_version/ (as the CI is only for
desktop_version/), or if the CI file itself is changed.

The CI workflow for Android will only trigger if Android-specific code
_could have_ changed. This includes all code that is definitely
Android-specific (e.g. Java files), but also C/C++ files that have
__ANDROID__ ifdefs.

Unfortunately, it's not possible to reuse the same list of paths across
two different event trigger types[1]. So we have to copy-paste here.

[1]: https://github.com/orgs/community/discussions/37645
2024-01-07 17:27:37 -08:00
Dav999
5f7cb46a35 Remove hair spaces from Japanese translation
I had added 1px spaces in some Japanese strings with buttons in them,
to avoid the button glyphs touching the rest of the text. However, the
Japanese translator later ended up putting full spaces in, not noticing
the hair spaces. So now the space was 1 pixel wider than it should've
been, and it's better to remove them.
2024-01-07 16:31:42 -08:00
Misa
4922fa4599 Disable debugger logic when not active
This fixes a bug where you could still drag an entity around with the
debugger inactive if you were holding the entity while disabling the
debugger with Y. Furthermore, you couldn't even drop the entity even if
you wanted to.
2024-01-06 20:51:59 -08:00
Misa
9e2d6e921c Fix clash between Gravitron start text and timer
There is a clash between the timer text and the "Survive for 60
seconds!" text. It's minor in English but it can be worse in other
languages (e.g. Polish).

So make the timer go away when that text is onscreen.
2024-01-06 20:38:25 -08:00
Misa
44fb76ba90 Fix clash between timer and return editor text
The "[Press {button} to return to editor]" and the "TIME:" text
overlapped, which resulted in an ugly clash.

To fix this, make the return editor text take priority over the timer
text. This involves a minor refactor to first calculate whether or not
we should draw the return editor text before we check if we should draw
the timer text.
2024-01-06 15:45:40 -08:00
Dav999
f4bdea7d6d Add a system for selecting between wordy/wordy2
Some languages have different spellings of wordy numbers based on the
gender of the things they're counting (uno crewmate versus una trinket)
or what a number's role is in the sentence (e.g. twenta out of twentu).
We've always had the idea we couldn't support such complex differences
though, because the game can't be adapted to know what gender each
object will have and what word classes might exist in other languages,
so translators would in those cases just have to forgo the wordy
numbers and just let the game use "20 out of 20".

A solution we came up semi-recently though (after all translations were
finished except for Arabic), was to allow the translator to define
however many classes of wordy numbers they need, and fill them all out.
This would not need the game to be *adapted* for every language's
specific grammar and word genders/classes. Instead, the translator
would just choose their correct self-defined class at the time they use
`wordy` in the VFormat placeholder. Something like
{n|wordy|class=feminine}, or {n|wordy_feminine}.

So this would benefit several languages, but we came up with the
solution a little late for all languages to benefit from it. The Arabic
translators asked for two separate classes of wordy numbers though, so
my plan is to first just have a second list of wordy numbers
(translation2 in numbers.xml), which can be accessed by passing the
`wordy2` flag to VFormat, instead of `wordy`.

Once 2.4 is released, we can take our time to do it properly. This
would involve the ability for translators to define however many
classes they need, to name them what they want, and this name would
then be useable in VFormat placeholders. We can convert all existing
translations to have one class defined by default, such as "wordy", or
"translation" depending on implementation, but there's not so much
concern for maintaining backwards compatibility here, so we can do a
mass-switchover for all language files. That said, it wouldn't be too
hard to add a special case for "translation" being "wordy" either.
We can then ask translators if they would like to change anything with
the new system in place.

For now, we can use this system for Arabic, maybe Spanish since there
were complaints about uno/una, and *maybe* Dutch (it has a thing where
the number "one" is often capitalized differently, but it's not
mandatory per se)
2024-01-06 14:11:40 -08:00
Dav999
ce1327f37a Enable SDL_HINT_IME_SHOW_UI to make typing CJK not guesswork
For some reason, the default behavior of SDL and/or Windows(?) (I only
tested this on Windows) seems to result in the fact that if any SDL app
doesn't account for it, there is no way for Japanese and Chinese
speakers to know what they're typing in.

How IMEs are supposed to work is that you can type words as sort of
WIP versions, and then select out of a list of candidates what the
final result should be. The app may display the WIP text and tell the
IME where the text field is so that the IME's menu can be displayed
around it. But if the app doesn't say where the text field is, then the
candidate list can also be displayed at the corner of the screen, which
is done in Minecraft.

By default, however, SDL apps don't get a candidate list at all, which
means you're basically flying blind as to what you're typing in, and
you would have to basically open notepad and copy-paste everything from
there - unless I'm missing something.

This commit sets the SDL_HINT_IME_SHOW_UI hint (added in SDL 2.0.18
apparently), so that the candidate list is at least shown in the corner.
We can probably deal with positioning and uncommitted text later.
2024-01-05 15:24:27 -08:00
Misa
95c6dc58d3 Fix TAB bind conflict with translator & debugger
The TAB bind is used in both roomname translator mode and the level
debugger. To fix this, the TAB keybind will prioritize roomname
translator mode, unless the debugger text is enabled (with the Y
keybind), in which case the debugger takes priority. Additionally, the
roomname translator text will not render when the debugger text is
shown.

Fixes #1094.
2024-01-05 11:42:10 -08:00
Terry Cavanagh
7bd281ee3d updated credits to include the Eternal Dream Arabization team 2024-01-04 16:29:01 +01:00
Misa
fc51c2219d README: Reword to "Viki", wordwrap line 2024-01-02 15:33:35 -08:00
TerryCavanagh
eb13e7962a updated Italian localisation credits 2024-01-02 22:27:05 +01:00
Terry Cavanagh
e99c2b7487 reverted note at end of desktop_version/Readme about data.zip and distribution 2024-01-02 22:27:05 +01:00
Terry Cavanagh
8683a9f853 Fixed broken table in License exceptions.md 2024-01-02 22:27:05 +01:00
Terry Cavanagh
e71c8714ba Added a link in the readme directly to the desktop_version folder 2024-01-02 22:27:05 +01:00
Terry Cavanagh
f1a5857ffb Update README.md
general cleanup, added a link to Misa's very helpful guide on the vsix wiki
2024-01-02 22:27:05 +01:00
Terry Cavanagh
49df15ebb5 added translators to main readme 2024-01-02 22:27:05 +01:00
Terry Cavanagh
d94328c6cc added localisation credits to repo 2024-01-02 22:27:05 +01:00
Terry Cavanagh
d6a8858643 Update readme.MD 2024-01-02 22:27:05 +01:00
Terry Cavanagh
da2573f87a Update readme.MD 2024-01-02 22:27:05 +01:00
Terry Cavanagh
41a605f8e6 Update readme.MD
typo
2024-01-02 22:27:05 +01:00
Terry Cavanagh
06f4c7efea Update LICENSE.md
typo
2024-01-02 22:27:05 +01:00
Terry Cavanagh
f175b52d55 Update License exceptions.md
general cleanup, made the Make and Play exception clearer
2024-01-02 22:27:05 +01:00
Terry Cavanagh
5bf313afa7 Update LICENSE.md
Just added a quick paragraph at the top summarising the rules
2024-01-02 22:27:05 +01:00
Terry Cavanagh
fde9882402 Update README.md
- Added a logo
 - Cleaned up the description, answering the number one question I get asked about the source code
 - Added link to the discord to direct potential contributors to
 - Updated credits
 - fixed some links in the credits (Magnus' site is currently down, but this is temporary)
2024-01-02 22:27:05 +01:00
TerryCavanagh
7a569deabe Update logo.gif 2024-01-02 22:27:05 +01:00
TerryCavanagh
6fab933683 Create logo.gif 2024-01-02 22:27:05 +01:00
Misa
b4579d88d3 Add SimpleMessageBox for early filesystem bail
This ensures that the game won't silently fail to start if it can't
initialize the filesystem. Instead, it will fail loudly by popping open
a message box (using SDL_ShowSimpleMessageBox).

The motivation for this comes from issue #1010 where this is likely to
occur if the user has Controlled Folder Access enabled on Windows, but I
didn't want to put in the work to specifically detect CFA (and not sure
if it's even possible if it turns out that the OS just gives a standard
"permission denied" in this case). At least any message box is better
than silently failing but printing to console when most users don't know
what a console is.

Fixes #1010.
2023-12-31 16:54:53 -08:00
Reese Rivers
76882e9a13 Update "It's Not Easy Being Green" Esperanto roomname translation 2023-12-30 11:37:43 -08:00
Misa
e318d6f176 Fix regression: Tower BG lerps in reverse direction
This is the same as commit 70357a65bf
("Fix regression: Warp BG lerps in reverse direction"), but for the
tower background.

This bug is most visible when moving the camera in a tower using
invincibility, or holding down ACTION during the credits scroll.
2023-12-29 14:11:08 -08:00
Misa
e6b1b54214 Add a fast-forward keybind to level debugger
This lets you hold down F to fast-forward the game if you have the level
debugger interface open (with Y) and the game isn't paused.

This is most useful for quickly skipping through cutscenes to test
something.
2023-12-29 13:56:30 -08:00
Misa
b8fdbe53b9 Fix loading plain font.png Unicode indexing out of bounds
This code was introduced by Dav999 in
abf12632bb (PR #1077), but it contains a
memory error. I spotted this with Valgrind.

The problem comes from the fact that `max_codepoint` is calculated from
the width and height of the surface (which will have the same width and
height as the source `font.png` from the filesystem). Let's work through
an example using a typical 128 by 128 `font.png` and an 8 by 8 glyph.

`chars_per_line` is calculated by dividing the width of the image
(`temp_surface->w`, or 128) by `f->glyph_w` (8), yielding 16.
`max_codepoint` is calculated by first calculating the height of the
image divided by the height of the glyph - which here just happens to be
the same as `chars_per_line` (16) since we have a square `font.png` -
and then multiplying the result by `chars_per_line`. 16 times 16 is 256.

Now it is important to recognize here that this is the _amount_ of
glyphs in `font.png`. It is _not_ the last codepoint in the image. To
see why, consider the fact that codepoint 0 is contained in the image.
If we have codepoint 0, then we can't have codepoint 256, because that
would imply that we have 257 codepoints, but clearly, we don't. If we
try to read codepoint 256, then after working through the calculations
to read the glyphs, we would be trying to read from pixel columns 0
through 7 and pixel rows 128 through 135... in a 128 by 128 image...
which is clearly incorrect.

Therefore, it's incorrect to write the upper bound of the for-loop
iterating over every codepoint as `codepoint <= max_codepoint` instead
of `codepoint < max_codepoint`.
2023-12-29 11:55:09 -08:00
Misa
6f435661e7 Add inbounds pixel assertion to DrawPixel/ReadPixel
I was running the game through Valgrind and I noticed a memory error
where the game was attempting to read a pixel that was just outside the
image. Since this is an error that doesn't immediately result in a
segfault, I figured that it would be prudent to put in an assertion to
make it loud and clear that a memory error is, in fact, happening here.

Similarly, drawing to a pixel just outside the surface wouldn't result
in a crash, so I copy-pasted the check there too (with changes).
2023-12-29 11:54:19 -08:00
AllyTally
e1e5e43089 Fix another incorrect tile in autotiler 2023-12-24 12:39:22 -08:00
AllyTally
1d130acc20 Fix resizing the map not changing the current room
If you're in (5, 5) (1-indexed) and you resize the map to (4,5), the
editor stays in (5, 5). This has no real consequences, other than
possibly confusing the user, but it should probably be fixed anyway.
2023-12-24 12:36:16 -08:00
Dav999
a97ba5e768 Fix minor [Press ACTION to save your game] overflow in German
Turns out the string I fixed in the previous commit was also never
noticed in German. For that one, I simply used the wording that was
used in the old hardcoded-ACTION string (with my German knowledge,
I'm confident that's still correct).
2023-12-23 10:50:12 -08:00
Dav999
1dc7ed4d65 Fix minor [Press ACTION to save your game] overflow in Turkish
When ACTION is filled in, the closing ] will go offscreen.
To fix it, I simply removed the square brackets.
2023-12-23 10:50:12 -08:00
Ethan Lee
148b518745
Steam now shares device info with SDL! 2023-12-20 23:17:28 -05:00
TerryCavanagh
30c9438f17 minor fixes for Spanish 2023-12-18 16:59:12 +01:00
Dav999
827417fec8 Restore gravity/warp lines sticking out a tile offscreen
I just discovered this: whereas 2.3 and older versions make gravity
and warp lines - when placed in the editor - stick out one tile
offscreen, the latest version stops the lines at the room border.

This change restores the old behavior, and it's a simple fix: the
refactored code was written to let tiles outside the room block
gravity/warp lines. Instead of all offscreen tiles blocking lines, now
there's a 1-tile padding around the room that will let them through.
2023-12-15 20:03:34 -08:00
Dav999
82aef30649 Minor indentation style fix in Render.cpp
This used 2-space indentation for one level, as well as }else{ on a
single line.
2023-12-15 20:00:05 -08:00
Dav999
e638544665 Add limits to the translator credits strings
Luckily, seems like no language has broken the limit when it is 40!
2023-12-15 20:00:05 -08:00
Dav999
bf85e10219 Try to prevent some lines in rolling credits going offscreen
The new localization-related credits are placed 5 characters from
the left border in the rolling credits (at x=40), which means the
limit was 35 8x8 characters. Which was broken by several languages.
So instead, move the string leftward a bit if it would run offscreen
otherwise.
2023-12-15 20:00:05 -08:00
Dav999
c1fea5f55a Fix current_credits_list_index not always being reset
If you go into the middle of the list of translators in the main menu
credits, then press Escape, and then go into the credits again, the
first page of the list may start at the wrong place, because while
game.translator_credits_pagenum was reset to 0,
game.current_credits_list_index wasn't. This is fixed now.
2023-12-15 20:00:05 -08:00
Dav999
f420b08a0b Fix translator credits headers being forced to 8x8 font
The header "Translators", as well as the language names, were using
PR_FONT_8X8, even though it was translatable text. This is now fixed.
(Also, the CJK spacing for the language names is now higher because
that looked nicer)
2023-12-15 20:00:05 -08:00
Dav999
abf12632bb Load plain font.png beyond U+007F
VVVVVV 2.2 only supported displaying characters 00-7F with its font
system. VVVVVV 2.3 added support for unicode, by supplying a font.txt
with all the characters that are in the font image. But 2.3 made
another change that I didn't immediately realize, even after reading
the code: if font.txt is not present, then the font is not assumed to
have _only_ 00-7F, but _all_ of unicode, as far as the image dimensions
allow.

However, an inconsistency I _did_ notice is how unknown characters
would be rendered in 2.3. If a font had a font.txt, then any unknown
characters would be shown as a '?'. If a font had no font.txt however,
then suddenly any unknown characters would just come out as a space.
I fixed this behavior with the new font system; but what was actually
happening for characters to come out blank is that characters up to
U+00FF, which _were_ technically in the font image but as fully
transparent, would be shown as they were in the image, and characters
beyond U+00FF wouldn't be shown since they were outside of the image.

I don't really want to show blank characters for any character between
80-FF if it is technically inside the image, because pretty much every
single ASCII-only font.png in existence (including the one in data.zip)
contains a blank lower half, just because the font in the game had
always had this specific resolution. (We didn't want to do things that
might crash the game because something was different from what it
expected...)

We have had some confusing occasions before with the old behavior where
the fonts weren't correctly packaged or something (like when the
Catalan translator was sent the first version of the translator pack,
or when people customize their fonts wrong) and special characters were
just blank spaces.

So, instead, for characters beyond 7F, I decided to consider them part
of the font, as long as they are not blank. That means, if a character
beyond the ASCII range has any (non-alpha-0) pixels, then it will be
added, otherwise it won't be. This is just to handle legacy fonts, and
the case where all fonts are missing and the one from data.zip is used;
new fonts should just use .fontmeta or .txt to define their characters.
2023-12-15 19:54:33 -08:00
Dav999
c2ad3d3b97 Clarify translators and programmers READMEs
The top of the programmers readme now says that you need the
translators readme to translate the game into a new language. Also,
since language file syncing now works to populate an empty language
folder, document that in the translators readme as well.
2023-12-15 19:48:55 -08:00
Dav999
6377fd5e73 Make language sync support creating blank language files
Two translators thus far have tried to populate initial language files
by creating a blank folder and then using the in-game sync option. For
example, see #1078.

That is not how the sync option was intended to be used, but it's
really close to getting everything, so I decided to just complete the
support by making sure numbers.xml is copied from English, and making
sure meta.xml is filled in with English text and not text from an
arbitrary language. Also, minor detail on plural form 1 being set to 1
by default if reset, so strings_plural.xml is fully consistent too.
2023-12-15 19:48:55 -08:00
Dav999
66ba557c4d Fix binding menu allowing you to lock yourself out of the game
Or well, lock yourself out if you don't have (easy) access to a
keyboard, like on Steam Deck.

In 2.3, this problem used to be much worse, since you could bind any
button to "menu" - which is actually also "return" in menus - and that
button could then no longer be bound to any other action, because
exiting the bindings menu had priority over assigning a different
binding. The result would be that people could have all their buttons
bound to "escape" with no way of undoing it or using their controllers
at all other than manually going into their config file to change it.

In 2.4, the most important bugs in the bindings menu are fixed, but
it's still possible to remove all your bindings from the "flip"
(confirm) action, meaning you can't navigate the menus anymore with a
controller to fix your bindings or even do anything.

There is one interesting part to all this: if an action has no buttons
bound to it at all when the game is started, then that action is
populated with the default button for that action. This is done for
each action separately, without accounting for the case where the
default button was already bound to another action which was not empty.
(This is something that the binding menu does try to prevent).
Therefore, having no buttons bound to "flip" while having A and B bound
to "menu", would result in A being bound to "flip" and A and B bound to
"menu".

That would still make you unable to enter the gamepad menu, since both
"confirm" and "return" are pressed in a row.

This commit fixes the specific situation where flip/confirm buttons are
also bound to menu/return, by removing all buttons that are in the flip
button list from the menu list. This means that, on Steam Deck, you can
still go to your bindings menu.
2023-12-15 19:44:51 -08:00
AllyTally
9782b450ef Fix broken warp background check
Seems like I made a mistake while originally writing the "make
autotiling base" code. This commit fixes the warp background turning
into solid tiles when you switch to a different tileset.
2023-12-15 19:40:07 -08:00
AllyTally
03bc9566fb Make outside background tiles ignore solid walls
This was the behavior in the old autotiling system, so this brings that
behavior back.
2023-12-15 19:40:07 -08:00
TerryCavanagh
d314672614 whoops 2023-12-12 15:19:21 +01:00
TerryCavanagh
b11d33f353 updated Italian localisation credits 2023-12-12 10:22:44 +01:00
TerryCavanagh
7e000a9668 minor updates to Italian 2023-12-12 10:17:22 +01:00
TerryCavanagh
4005fe9725 updates to Korean
from our translator, based on TARI1237KR's feedback
2023-12-11 10:42:45 +01:00
TerryCavanagh
912e621259 minor updates to Welsh, German, Japanese, Polish and Silesian 2023-12-06 19:20:28 +01:00
TerryCavanagh
9a40993b5f Add localisation credits to main menu credits
In addition, this adds Ally and mothbeanie to the Localisation
Implementation page credits. Also updated the game complete credits!
2023-12-05 16:14:06 -08:00
TerryCavanagh
e754654926 Change Spanish name in meta.xml to Español (es)
might consider adding an Español (latam) edit next year, but this is
enough for 2.4. We're using "Español (es)" instead of "Castellano"
because our translator prefers it
2023-12-05 16:14:01 -08:00
Reese Rivers
79376ae82e Initial implementation of localisation credits
This commit adds translation credits to the game's end credits
screen. Note that this is not implemented into the menu credits
screen yet. The translator name list is subject to tweaks, and
additionally some localised strings ("Localisation Project Led by"
and "Pan-European Font Design by") run off the screen in some
languages (Catalan, Spanish, Irish, Italian, Dutch, European
Portuguese and Ukrainian) and will need to be addressed later.
2023-12-05 16:13:03 -08:00
TerryCavanagh
c660000292 updated İZLE sprite for turkish
as approved by our translator
2023-12-05 16:46:41 +01:00
Dav999
8f7d0f4913 Make some minor refinements to Dutch translation
I put a main focus on the first cutscenes in the game, changing the
first "Uh oh..." from something like "Oh dear..." to "Oh no..." to make
sure it always sounds right. (The real translation of "Uh oh" is "O-o",
but that seemed too easy to read wrong for the first line in the game
that I wanted to avoid it altogether.)
2023-12-05 16:41:05 +01:00
Dav999
d745bf2e6c Restore correct LIES/TRUTH for Korean
This reverts the Korean sprites in commit
6a8db6a22d.

How many times did we accidentally flip these around now lol
2023-12-05 15:33:20 +01:00
Dav999
2533e144b6 Fix centering of game complete texts if level font != interface font
Textboxes created with graphics.createtextboxflipme() use PR_FONT_LEVEL
by default, but can be overridden with graphics.textboxprintflags() to,
for example, set PR_FONT_INTERFACE. This happens for the textboxes on
the Game Complete screen, which use interface text. The textboxes are
centered by setting the X position to -1 though, which means they're
solely centered based on the width of the first line, in the level
font (because the font hasn't been changed to the interface font yet).

Normally, this isn't a problem, because in the main game (where the
Game Complete screen usually appears), the level font is always equal
to the interface font. However, in custom levels you can still get it
(by calling gamestate 3500) and in that case some of the text may be
misaligned. This change fixes that by adding graphics.textboxcenterx()
to these textboxes.

As far as I can tell, these are the only textboxes that are centered
by just x=-1 despite changing the font afterwards.
2023-12-04 19:49:17 -08:00
Dav999
3c49be7d81 Fix ACCIÓN having changed to ACTION in Spanish 2023-12-04 19:45:01 -08:00
Ethan Lee
2d1009e815 Add "type:" override check for virtual Sony gamepads.
Fixes #1056
2023-12-01 12:08:26 -05:00
AllyTally
b23983c0b8 Fix BG tile bug from switching from pink SS
If you had a pink space station background, and switched to a different
tileset, some solid tiles would be placed instead. This commit fixes
that by transforming the room into the basic autotiling tiles before
changing the tileset itself. The reason why I chose this solution is
because it will help with a future change, being unhardcoding warp zone
backgrounds (which'll help with custom autotiling, if that becomes a
thing.)
2023-11-30 19:17:52 -08:00
AllyTally
96d36f86f0 Change 0s to 713 for lab background
This is so it gets treated as a background tile, so background tiles
don't get thrown away when you shift tilesets.
2023-11-30 19:17:52 -08:00
Dav999
bafd494a67 Polish: Change roomname Ostrzeżenie to Przestroga 2023-11-30 14:59:30 -08:00
Dav999
10ed1079ae Fix "he'd be a a big help" typo
Now that the language files are fairly stable, we should be able to do
this without any accidental reverts taking place (if any do happen, it
should be easy to see and prevent)
2023-11-30 08:38:40 -08:00
Dav999
a111576a98 Correct one letter's capitalization in German
It's a menu option, so it should be uncapitalized, like all other menu
options.
2023-11-29 17:56:58 -08:00
Reese Rivers
80626d358c Fixed duplicated/shifted characters 2023-11-29 10:24:07 -08:00
Dav999
02e15ed829 Italian: Fix in-game timer ON/OFF being reversed 2023-11-29 09:52:14 -08:00
Dav999
1ae9370a72 Japanese: apply change from 凄い to すごい 2023-11-29 09:52:14 -08:00
Misa
af35be5bb6 Name conditionals in text box loop
With the recent change to drawing overlays (images and sprites) from
PR #1058, it's starting to get a bit hairy. This names the conditionals
responsible for determining if the text box is transparent (checking
that all of its RGB is 0) and if overlays should be drawn or not (which
is now either when it's opaque or transparent).
2023-11-28 19:07:49 -08:00
Reese Rivers
44a889efeb Changed timing of textsprites in transparent textboxes
Textsprites and textimages no longer wait for the opacity
value in order to display within transparent textboxes.
Text sprites in normal opaque textboxes are not affected
by this change.
2023-11-28 19:07:49 -08:00
Reese Rivers
2e950fc935 Update Space Station font to latest version 2023-11-28 18:11:42 -08:00
AllyTally
041a81d8de Fix incorrect tiles in outside BG autotiling 2023-11-28 17:21:02 -08:00
Dav999
133adba10e Fix remaining audio speedup/slowdown bug
Fixes #1057.

Based on Ethan's hunch, I simply removed the format comparison that
decides whether to halt and restart, or reuse the voice. Voices are
now always restarted when playing a new track.

This also simplifies the code somewhat: `MusicTrack::musicVoiceFormat`
was now no longer used, and an `if (!IsHalted())` was no longer
necessary because `Halt()` already does that. So those are now removed
as well.
2023-11-28 20:16:38 -05:00
Dav999
456a81df67 Add spaces at the end of "TEMPS :" string (and MORTS and BLINGS)
This string is used both in time trials (alongside "MORTS :" and
"BLINGS :") as well as outside time trials if you enable the in-game
timer. In English, this looks like "TIME:1:23.45". Since French adds
a space before the colon, it will look like "TEMPS :1:23.45" instead.
Therefore, I've added a space after the colon as well.
2023-11-27 18:45:09 -08:00
Dav999
d49fb7eb2e Apply changes to two terminal textboxes in Japanese
These are to make the textboxes not overlap with the header, by making
them wrap to less lines.
2023-11-27 15:22:22 -08:00
Dav999
61c5e18413 Fix position of crewmate in "You have rescued a crew member!" box
At first my CJK changes also misaligned this sprite, and my solution
that time was to position the textbox higher depending on the height
of the textbox, so it would be centered around the crewmate sprite
(which stayed at a hardcoded place onscreen). Recently, #987 changed
these sprites to be relative to the position of the textbox instead of
relative to the screen, which is much more logical, but it stopped
centering these sprites again. But it's an easy fix: simply account for
the extra-added height when adding the sprite in.
2023-11-27 15:02:47 -08:00
Misa
c9fd9e7924 README: Delete section on compiler quirks
This hasn't been relevant for years now. Even in 2.3, this wasn't
relevant, but we added a disclaimer saying that it only applies to 2.2.

But now issue #1052 has been opened specifically pointing to this
section as something that should be removed. Therefore, I'm removing it.
2023-11-27 13:43:23 -08:00
Misa
70357a65bf Fix regression: Warp BG lerps in reverse direction
This fixes a regression caused by PR #923 (the PR that moved rendering
to be GPU-based) where the interpolation of the horizontal and vertical
warp backgrounds (in over-30-FPS mode) was in the wrong direction, which
makes them look blurry.

This happens because the arguments to the `lerp` function were in the
wrong, reverse order.

On the VVVVVV Discord server, Ally raised the argument that they were in
the same order before she made the changes; therefore the previous code
was also incorrect and it wasn't her fault. However, this argument is
incorrect, because in that case, the reverse order _is_ the correct
order.

The reason that it's now the wrong order is because the output of `lerp`
is now being used as the argument to a source rectangle. Previously, the
output of `lerp` was being used as the offset argument to
`ScrollSurface`, which is analogous to being a destination rectangle.

Fixes #1038.
2023-11-27 13:29:06 -08:00
Ethan Lee
8426e0930d VS2010 buildfixes.
The main issue was mostly that we have to build C files as C++ in some
cases, and extern "C" wasn't being used everywhere, so linker errors
popped up. The rest is the usual tedious VS2010 stuff like casting void*
to other stuff, so this commit as a whole is pretty boring!
2023-11-27 12:09:42 -05:00
TerryCavanagh
4725bc4d5e minor change to SIM enemy sprite in pt_BR and pt_PT 2023-11-25 23:31:51 +01:00
TerryCavanagh
0d13a1d38b small update to Polish (changed roomname Aleja Woronicza -> Taśmy prawdy) 2023-11-25 17:08:36 +01:00
TerryCavanagh
46df77e837 minor fixes for Turkish and Esperanto 2023-11-25 16:07:29 +01:00
David Galiev
c27f35b15d Fix some Russian strings 2023-11-25 15:21:47 +01:00
TerryCavanagh
6bbc649269 minor fixes for Polish and Silesian 2023-11-25 15:21:47 +01:00
TerryCavanagh
432487b2d9 Final strings for Polish and Silesian 2023-11-25 15:21:47 +01:00
TerryCavanagh
081b8938b6 minor fixes for Irish
fixed checkpoint letter S being wrong upside down, changed the "big C thing" cutscene
2023-11-25 15:21:47 +01:00
TerryCavanagh
6a8db6a22d Enemy graphics for all other languages*
*subject to changes

Also, Traditional Chinese is current using the Simplified Chinese graphics, which is acceptable but not ideal:

Obey -> 服從 (ok to use simplified 服从)
Lies -> 謊言 (ok to use simplified 谎言)

The other words are the same for Simplified Chinese and Traditional Chinese.
2023-11-25 15:21:47 +01:00
TerryCavanagh
268564cadc minor fix for Esperanto (checkpoint c -> k) 2023-11-25 15:21:47 +01:00
TerryCavanagh
e1eaaaed32 minor fix for Spanish
whoops
2023-11-25 15:21:47 +01:00
TerryCavanagh
b2d15c0bba Final strings for Spanish 2023-11-25 15:21:47 +01:00
TerryCavanagh
5a95040a4d Enemy graphics for Korean 2023-11-25 15:21:47 +01:00
TerryCavanagh
83b9db71ee Final strings for French 2023-11-25 15:21:47 +01:00
TerryCavanagh
f5b9c3b09e Final strings for European Portuguese 2023-11-25 15:21:47 +01:00
TerryCavanagh
43cb7a5cde Final strings for Welsh 2023-11-25 15:21:47 +01:00
TerryCavanagh
085b2d5987 minor fix for Japanese 2023-11-25 15:21:47 +01:00
TerryCavanagh
61d14ce2af Enemy graphics for Japanese
These depend on pull request https://github.com/TerryCavanagh/VVVVVV/pull/1031 - if the format or implementation changes, I'll update the branch to match!
2023-11-25 15:21:47 +01:00
TerryCavanagh
99dd10b158 Final strings for Japanese 2023-11-25 15:21:47 +01:00
TerryCavanagh
48d9ab3083 Final strings for Brazilian Portuguese 2023-11-25 15:21:47 +01:00
TerryCavanagh
195b375d2e minor fixes for Ukrainian 2023-11-25 15:21:47 +01:00
Dav999
2db9eaa9a2 Final strings for Esperanto (credits part) 2023-11-25 15:21:47 +01:00
TerryCavanagh
7ad96c6d43 Final strings for Ukrainian 2023-11-25 15:21:47 +01:00
TerryCavanagh
9bea54578e Squashed commit of the following:
commit 3d6802add8
Author: Dav999 <dav999.tolp@gmail.com>
Date:   Thu Oct 19 17:16:01 2023 +0200

    Change AVOID to FAINIC in Irish

commit 21fd84f479
Author: Dav999 <dav999.tolp@gmail.com>
Date:   Thu Oct 19 17:04:27 2023 +0200

    Partial final strings for Esperanto

    This does not yet include the new localization credits, but I already
    had all the other strings.

commit 45382a358c
Author: Dav999 <dav999.tolp@gmail.com>
Date:   Thu Oct 19 17:01:30 2023 +0200

    Final strings for Dutch

    I also decided to change AVOID from ONTWIJKEN to ONTWIJK, to make it
    a bit more fitting as if it's an actual word enemy with length
    restrictions, heh. (Not that it's an abbreviation - it's just an
    imperative instead of an infinitive. And those terms I had to look up)
2023-11-25 15:21:47 +01:00
TerryCavanagh
dcb447b4f3 Final strings for Italian 2023-11-25 15:21:47 +01:00
TerryCavanagh
0cfce45ec6 Final strings for Turkish 2023-11-25 15:21:47 +01:00
TerryCavanagh
faa4266eb7 Final strings for Traditional Chinese 2023-11-25 15:21:47 +01:00
TerryCavanagh
3c4337eca6 Minor fixes for German and Irish 2023-11-25 15:21:47 +01:00
TerryCavanagh
ea7f6982db Final strings for Irish 2023-11-25 15:21:47 +01:00
TerryCavanagh
822aec9d10 Final strings for German 2023-11-25 15:21:47 +01:00
TerryCavanagh
0ae927eb3b Changed to Simplified Chinese to match enemy graphic changes 2023-11-25 15:21:47 +01:00
TerryCavanagh
5ca49e607e Final strings for Simplified Chinese 2023-11-25 15:21:47 +01:00
TerryCavanagh
e30dd618d2 Final strings for Catalan, Russian and Korean
more incoming
2023-11-25 15:21:47 +01:00
AllyTally
b7a8bb138c Unhardcode TAB in translation strings
In case we want to make it rebindable in the future, we shouldn't make
translators retranslate these strings.
2023-11-19 17:34:23 -08:00
AllyTally
4df35da0e4 Fix debugging in the tower, update wording
Fixes debugging mouse targets and rendering in the tower. This also
changes "movement" to "gameplay" in the TAB hint.
2023-11-19 17:34:23 -08:00
AllyTally
0804f2d0c0 Add warp background as general information 2023-11-19 17:34:23 -08:00
AllyTally
f7ca850017 Update to latest commit 2023-11-19 17:34:23 -08:00
AllyTally
6b7bf44402 Add gravity line true hitbox visuals 2023-11-19 17:34:23 -08:00
AllyTally
55a05342e9 Replace manual check with SDL_PointInRect 2023-11-19 17:34:23 -08:00
AllyTally
63620efac8 Fix moving entities flashing in over 30fps mode 2023-11-19 17:34:23 -08:00
Ally
689768b8ca Fix issues
Co-authored-by: Dav999 <44736680+Dav999-v@users.noreply.github.com>
2023-11-19 17:34:23 -08:00
AllyTally
aff4d2012c Add -leveldebugger flag
This commit adds the `-leveldebugger` flag so you can use it while
playtesting from a different editor program, such as Ved.
2023-11-19 17:34:23 -08:00
AllyTally
c62da1c9a0 Add flag display by holding U 2023-11-19 17:34:23 -08:00
AllyTally
fd2f738145 Debug lines while not hovering
This commit adds new debug lines while you're NOT hovering over an
entity or a block. Additionally, coordinates are now displayed smaller,
to not take up as much vertical space.
2023-11-19 17:34:23 -08:00
AllyTally
be77047440 Add more info, add lang string 2023-11-19 17:34:23 -08:00
AllyTally
91f87fa126 Add level debugger screen
The level debugger is toggleable in playtesting mode by pressing Y.
You can toggle whether or not the game is paused inside of the debugger
by pressing TAB. The debugger screen allows you to see entity and block
properties, and allows you to move them around.
2023-11-19 17:34:23 -08:00
Dav999
3c4ed36418 Translate hardest room at display time instead of at time of death
The hardest room used to be stored as a room name in whatever language
it was in when you last died enough times to break the record (before
localization, that was always English). Even after localization became
a thing we could get away with this since we only had a single font,
but now we might have actual question marks appearing when the new font
doesn't support characters from the old language.

Therefore, this commit adds more info about the hardest room to save
files - everything that is needed to know in order to do the
translation at display time. These are hardestroom_x and hardestroom_y
for the room coordinates, as well as hardestroom_specialname to mark
special names, in addition to changing the stored room name back to
English. I've also added hardestroom_finalstretch in case we later
decide to drop the English name as a key and rely on just the
coordinates (even though I think that change itself would be more
complicated than any simplification it would accomplish, and I don't
think it's necessary, but better to have it if we do need it later)
2023-11-19 16:47:52 -08:00
Dav999
e0e902d717 Change levelstats data type from vector to map
As described in #1016, there used to be a bug that inflated
levelstats.vvv in 2.3, which was fixed in 2.4, but there was no way
for inflated files to get smaller yet.

This commit changes the storage of levelstats from a std::vector of
structs to a std::map, so that uniqueness is guaranteed and thus the
stats can be optimized automatically. And it also simplifies *and*
optimizes the code that handles the levelstats - no more big loops that
iterated over every element to find the matching level.
(Farewell to the "life optimisation and all that" comment, too)

I tested this with both my own levelstats.vvv, as well as some inflated
ones (including Balneor's 93 MB one) and saw this code correctly reduce
the filesize and speed up the levels list.

Fixes #1016.
2023-11-19 16:18:54 -08:00
Dav999
82240d262b Game::loadcustomlevelstats: move some declarations to later in function
The declarations of `std::vector<std::string> customlevelnames` and
`std::vector<int> customlevelscores` are made quite early in the
function, commented with "Old system", but the place where the old
system is processed is after a big chunk of code that processes the new
system (and indeed never uses these vectors). So for readability,
they're now closer to where they're used.
2023-11-19 16:18:54 -08:00
mothbeanie
58c006f61f decrease map cursor margin on smaller map sizes 2023-11-19 15:59:27 -08:00
mothbeanie
94ece095ed PR_BOR8 -> PR_FULLBOR, border8 -> full_border 2023-11-19 15:59:27 -08:00
mothbeanie
947d716be6 remove tile_offset (no longer used) 2023-11-19 15:59:27 -08:00
mothbeanie
3a3ec659d6 Add translingual map legend code + border8 2023-11-19 15:59:27 -08:00
mothbeanie
7840f72389 Add translingual map legend characters to font 2023-11-19 15:59:27 -08:00
AllyTally
103b4d36a1 Add textimage for levelcomplete and gamecomplete
`levelcomplete` and `gamecomplete` were hardcoded using textbox colors
which were offset by 1. This PR fixes that, no longer requiring
slightly-off colors, and instead adding a new property to textboxes
which tell the game to display either level complete or game complete.
2023-11-19 15:07:25 -08:00
AllyTally
b5c9508dd4 Finish implementing sprites in textboxes
This commit adjusts the Y position for flip-mode, and makes the main
game use this new system.
2023-11-19 15:07:25 -08:00
AllyTally
76ea4488af Initial implementation of textbox sprites
This commit adds a system for displaying sprites in textboxes, meant to
replace the hardcoded system in the main game. This does not support
levelcomplete.png and gamecomplete.png yet, which will most likely just
be special cases.
2023-11-19 15:07:25 -08:00
Dav999
187fd85e14 Change saved <summary> tag back to English
This ensures loading a 2.4 save in the English-only 2.3 or earlier
doesn't result in missing characters because a translated area name
appears in the save file. We are not reading from <summary> anymore
in 2.4.

The way this is done is by not translating the area names inside
mapclass::currentarea(), but at the callsites other than the one which
saves the <summary>.
2023-11-19 13:49:59 -08:00
Dav999
ac7fe4475c Remove more tele/quick variables duplicating info in struct Summary
For both `tele` and `quick`, I removed these attributes of class Game:
- std::string *_gametime
- int *_trinkets
- std::string *_currentarea
- bool *_crewstats[numcrew]

All this info can now be gotten from members of Game::last_telesave and
Game::last_telesave. I've also cleaned up the continue menu to not have
all the display code appear twice (once for telesave and once for
quicksave).

RIP "Error! Error!" though lol
2023-11-19 13:49:59 -08:00
Dav999
f23ffc0457 Get rid of Game::savearea (std::string)
This is what got saved to the area part of the <summary> tags, and it
was specifically set upon pressing ACTION to save in the map menu.
Which meant tsave.vvv may not get an accurate area name (notably
"nowhere" if you hadn't quicksaved before in that session) even though
it's not displayed anywhere so it didn't really matter. But this
variable can be removed - there's only one place where <summary> is
written for both quicksaves and telesaves, so that now gets the area
at saving time.

Fun fact: custom level quicksaves also have a <summary> tag, and it's
even less functional than the one in tsave.vvv, because it stores
whatever main-game area name applies to your current coordinates.
So I simply filled in the level's name instead (just like what the
actual save box says).
2023-11-19 13:49:59 -08:00
Dav999
0ea41e7913 Replace std::string Game::telesummary and Game::quicksummary by Summary
Game::telesummary and Game::quicksummary stored the summary string for
the save files - which is the <summary> tag that says something like
"Space Station, 10:30:59". The game only ever displays the quicksave
variant of these two, for "Last Save:" on the map menu's SAVE tab.
So the telesave has a <summary> too, but it's never displayed anywhere.
(In fact, the area is often set to "nowhere"...)

However, the summary strings have another function: detect that both
the telesave and quicksave exist. If a summary string for a save is
empty, then that save is considered not to exist.

I'm refactoring the summary string system, by making the new variables
Game::last_telesave and Game::last_quicksave of type struct
Game::Summary. This struct should have all data necessary to display
the summary string at runtime, and thus translate it at runtime (so
we don't store a summary in a certain language and then display it in
the wrong font later - the summary can always be in the current
language). It also has an `exists` member, to replace the need to
check for empty strings.

The <summary> tag is now completely unused, but is still written to
for older versions of the game to read.

(This commit does not add the new string to the language files, since
Terry now added it separately in his own branch)
2023-11-19 13:49:59 -08:00
Dav999
64bad7d67f Remove double NULL check in loadthissummary() 2023-11-19 13:49:59 -08:00
Dav999
61adffe6eb Remove finalmode from struct Summary
It's unused (other than some map.finalmode assignments which should not
be there) and we don't need it either. If we do, we can always readd
it.
2023-11-19 13:49:59 -08:00
Dav999
50a560dc5f Change savex and savey in struct Summary to saverx and savery
This is a bit misleading otherwise, since it is the room coordinates,
not pixel coordinates.
2023-11-19 13:49:59 -08:00
Dav999
4e5126a596 Move struct Summary from Game.cpp to Game.h
This will allow it to be used in other source files in a later commit.
2023-11-19 13:49:59 -08:00
Dav999
89a165722e Simplify mapclass::currentarea()
It used to take a single int: the area number returned by
mapclass::area(roomx, roomy). All uses of currentarea() were called
with an extra area() call as its argument. Additionally, there's a
good reason why currentarea() should have the room coordinates: in one
of the cases that it's called, there's a special case for the ship's
coordinates. This results in the SAVE screen in the map menu being able
to show "The Ship", while the continue screen shows "Dimension VVVVVV"
instead. Therefore, why not put that exception inside currentarea()
instead, and remove a few callsite map.area() wrappers by making
currentarea() take the room x and y coordinates?
2023-11-19 13:49:59 -08:00
Dav999
73911a7ada Remove game.customquicksummary
It's completely unused (apart from the variable being set in one
place). So let's get this out of the way.
2023-11-19 13:49:59 -08:00
Misa
0a72cc6614 Android: Update README.md for Maven package
Since #1047 was merged, we now make the user build the SDL prefab
themselves (as SDL does not publish Maven packages yet). Here are some
instructions for doing that.
2023-11-14 17:29:34 -08:00
leo60228
ca71410f14 Use Android SDL2 via Maven 2023-11-14 17:18:08 -08:00
Dav999
1d3173f5c1 Fix %cs showing instead of commit date on Windows (or older git?)
Whenever I'd compile on Windows, I'd see the literal text "%cs" in the
main menu instead of the commit date. I never thought much of it (at
least it runs, and the date only shows up in development builds). Now
that I've also seen a screenshot from Terry with it, I decided to look
into it further. Looks like it's a format string that our gits on
Windows aren't recognizing for whatever reason - probably because
they're too old. I have git version 2.23.0.windows.1, and checking its
help page for `git log`, under PRETTY FORMATS, %cs is missing as an
option, while some other options are still there. So the option was
probably added sometime between that version and 2.34.1, which is the
one I have on Linux, where %cs does work.

Luckily, %cd with --date=short seems equivalent, and better supported,
so we can just use that instead.
2023-11-13 14:42:48 -08:00
Misa
1e5a737089 CMake: Silence deprecation warning
Recent versions of CMake emit the following:

    CMake Deprecation Warning at CMakeLists.txt:4 (cmake_minimum_required):
      Compatibility with CMake < 3.5 will be removed from a future version of
      CMake.

      Update the VERSION argument <min> value or use a ...<max> suffix to tell
      CMake that the project does not need compatibility with older versions.

Reading the documentation further, adding a max refers to the max
version compatibility of CMake _policies_. Adding a max of 3.5 makes the
warning go away, so it seems that the warning is more about policies
than anything else.

This will still work on 2.8.12 as the extra dots will be seen as a
version component separator, ignoring the max version.
2023-10-31 22:36:11 -07:00
Dav999
b3810b686d Update README-translators.txt with info about sprites translation 2023-10-31 22:31:41 -07:00
Dav999
5c7be6c1de Add Dutch translated sprites
These should be pretty finalized, and serve as a demo for this PR.
2023-10-31 22:31:41 -07:00
Dav999
9045e26d3e Add support for translatable sprites
Language folders can now have a graphics folder, with these files:
- sprites.png and flipsprites.png: spritesheets which contain
  translated versions of the word enemies and checkpoints
- spritesmask.xml: an XML file containing all the sprites that should
  be copied from the translated sprites and flipsprites images to
  the original sprites/flipsprites.

This means that the translated spritesheets don't have to contain ALL
sprites - they only have to contain the translated ones. When loading
them, the game assembles a combined spritesheet with translated sprites
replacing English ones as needed, and this sheet is used to visually
substitute the normal sprites at rendering time.

It's important to note that even if 32x32 enemies have pixel-perfect
hitboxes, this is only a visual change. This has been discussed several
times on Discord - basically we don't want to give people unfair
advantages or disadvantages because of their language setting, or
change existing gameplay and speedruns tactics, which may depend on the
exact pixel arrangements of the enemies. Therefore, the hitboxes are
still based on the English sprites. This should be basically
unnoticeable for casual players, especially with some thought from
translators and artists, but there will be an option in the speedrunner
menu to display the original sprites all the time.

I removed the `VVV_freefunc(SDL_FreeSurface, *tilesheet)` in
make_array() in Graphics.cpp, which frees grphx.im_sprites_surf and
grphx.im_flipsprites_surf. Since GraphicsResources::destroy() already
frees these, it looks like the only purpose the one in make_array()
serves is to do it earlier. But now we need them again later (when
switching languages) so let's just not free them early.
2023-10-31 22:31:41 -07:00
Dav999
8ef000554d Add "English sprites" setting
It'll start working in the next commit... See the description there.

(This commit does not add the new strings to the language files, since
Terry now added them separately in his own branch)
2023-10-31 22:31:41 -07:00
Dav999
92f9940464 Update my GitHub username and Ved repo link
I recently changed my GitHub username from Dav999-v to Daaaav, and
today I moved the Ved repo from GitGud.io to GitHub. This commit
updates the references to both my username and the Ved repository.
2023-10-28 19:04:44 -07:00
Misa
ea1a014145 Fix memory leak with ApplyFilter
The intention of the recent refactor was to make it so that the
temporary surfaces would be allocated only once, when the mode is
enabled, and be freed upon exit.

To do this, Graphics.cpp owns the pointers, and passes them to
ApplyFilter to modify. Except ApplyFilter doesn't actually modify the
pointers, because it's only a single pointer, not a pointer-to-pointer.
So every frame of rendering it would actually be creating a new surface
and leaking memory.

To fix this, they need to be pointer-to-pointer variables that get
modified.

I also added error logs in case the surface creation failed.
2023-10-27 10:25:42 -07:00
Misa
791310fa5d Provide error code if SetConsoleOutputCP fails
According to the Win32 documentation, GetLastError provides extended
error information if SetConsoleOutputCP fails.
2023-10-25 17:21:11 -07:00
Dav999
98d1a38d7f Allow UTF-8 console output on Windows (for -console)
Right now, Windows assumes all our console output is code page ????.
That means our UTF-8 output appears mangled. (I ran into this while
testing IME text input by outputting strings to the console)

For a moment I was scared we'd need to do UTF-16 conversions and call
Windows-specific print functions like WriteConsoleW() in our vlog
functions, but fortunately SetConsoleOutputCP(CP_UTF8) works just fine.
2023-10-25 17:17:49 -07:00
Dav999
36dbb0f73a Show centiseconds for best time trial times (if available)
Since VVVVVV 2.3, time trial best times are stored not just with the
number of seconds, but also the number of frames. However, there was
no room to display it with the old design of the time trials screen.
Now there is, so it can easily be displayed now with a small change!

Unset frames are stored as -1, which fits perfectly into the frames
argument of help.format_time(), because in that case the amount of
centiseconds is not shown.

It should be noted that opening VVVVVV 2.2 will instantly wipe your
frames records, as described by #1030. But many people will likely
never open 2.2 anymore.
2023-10-25 17:06:50 -07:00
Misa
a46c817c69 Clarify references to Make and Play in README
Some people might be confused by the reference to M&P in the
instructions referring to downloading data.zip (#1026).

data.zip is the same between M&P and full versions of the game and is
orthogonal to which version of the game is built. Building M&P just
requires uncommenting `#define MAKEANDPLAY` in `MakeAndPlay.h`.

So clarify that you can grab data.zip from your existing copy of the
game, or from the Make and Play _page_ (not necessarily the Make and
Play edition of the game), and add instructions for building the M&P
version.

Closes #1027.
2023-10-25 16:47:25 -07:00
Misa
9a9fb5b237 Android: README: Fix swapped h1/h2 headers
The ones I meant to be h1 appeared as h2 and vice versa. Whoops.
2023-10-25 16:44:23 -07:00
Misa
75a693347b vlog: Slim SDL.h include down to SDL_log.h
The entirety of SDL doesn't need to be included, only the SDL_log header
does.
2023-10-25 15:48:04 -07:00
Misa
f83b63142d Android: Add README.md
This serves as a file to help others in building the C++ Android version
for themselves.

These instructions are what I figured out to get it to work for me, and
should be kept up to date.
2023-10-25 15:04:12 -07:00
leo60228
d6a43fad90 Fix compiler warnings on Android 2023-10-25 14:44:34 -07:00
leo60228
246a91c45e Use existing package name to allow updates 2023-10-25 14:44:34 -07:00
leo60228
e170d19e16 make more tasks depend on zipRepoAssets 2023-10-25 14:44:34 -07:00
leo60228
c84bc14122 Android CI 2023-10-25 14:44:34 -07:00
leo60228
6d88f8b1d6 Ignore output-metadata.json 2023-10-25 14:44:34 -07:00
leo60228
dae5f7bbd7 Use adaptive icon (legacy was chosen by mistake) 2023-10-25 14:44:34 -07:00
leo60228
4512c13507 Fix release builds (Gradle why) 2023-10-25 14:44:34 -07:00
leo60228
2986dc01d9 Add DocumentsProvider 2023-10-25 14:44:34 -07:00
leo60228
9d1d994f57 Disable editor on Android (like Steam Deck) 2023-10-25 14:44:34 -07:00
leo60228
945823ca30 Build and mount repo.zip with fonts/ and lang/ 2023-10-25 14:44:34 -07:00
leo60228
3e0cf57a99 Address review 2023-10-25 14:44:34 -07:00
leo60228
f3f9d1b523 Enable ABI splits
This is somewhat convenient for development, as it means that Android
Studio will only do a native build for the architecture of the device
being used for testing.

This is ignored for AABs, so it won't affect release builds (at least
for Google Play).
2023-10-25 14:44:34 -07:00
leo60228
4229372c2e Android port 2023-10-25 14:44:34 -07:00
Terry Cavanagh
6e18cddc69
Merge pull request #1035 from TerryCavanagh/mobile-version-update
Add missing files that the mobile version needs to compile
2023-10-16 09:53:13 +01:00
Dav999
1254209a22 Apply updates to Polish and Silesian room names 2023-10-11 15:55:36 -07:00
TerryCavanagh
c00c521802 add missing files to mobile_version data folder 2023-10-07 18:48:36 +01:00
Dav999
e847fc2d43 Add missing string in Japanese 2023-10-02 19:03:39 -07:00
Dav999
08ff09005b Apply updates to Polish and Silesian 2023-09-30 16:48:35 -07:00
Dav999
122fb53574 Apply latest fixes to some Turkish strings
This fixes some translations that were too long for the newly
translated strings.
2023-09-29 10:08:51 -07:00
Dav999
2773dd3537 Improve some Dutch menu strings
I took a very critical look at all the menus, to make sure they're all
clear and easy to read. I mainly simplified some explanations and
solved some small issues.
2023-09-28 23:00:16 -07:00
Dav999
b5032ec0c1 Bring Catalan, Dutch and Esperanto language files 100% up-to-date again
Well, 100% up-to-date with current upstream at least, there's some more
strings to be added soon, including "{area}, {time}" from #1018, a new
menu option related to translatable graphics files, and definitely some
credits stuff.
2023-09-28 23:00:16 -07:00
Dav999
ed02cac3b4 Add Silesian language files
This is the last planned language for 2.4 I think!
2023-09-28 22:54:53 -07:00
Dav999
eeec428d0b Raise limits on three strings in translation files
These were causing false alarms in translations for one reason or
another (either to force translations to not wordwrap for style
reasons, or to stay on the safe side if an adjacent string was also
long), so they can be raised now.
2023-09-20 18:38:16 -07:00
Dav999
1c934688a3 Make "{n_crew|wordy} crewmates remain" string wordwrap
It used to have a limit of 40 8x8 characters, but there's room for it
to wrap.
2023-09-20 18:38:16 -07:00
Dav999
cbdf888a40 Sync new string from #1002 into languages added by #989
These PRs were open in parallel so this simply syncs the language files
back up.
2023-09-20 18:38:16 -07:00
Dav999
5e2e9e7ce8 Update French to near-latest strings (99.9%), fix issues 2023-09-20 16:54:43 -07:00
Dav999
fcf034583b Fix possible title screen from in-game if command-line playtesting
In 8484b36198, I fixed the title screen
showing up if you go to the language screen from in-game, while not
having any language files. There was also one other possible way to get
this to happen that I missed though: if you do have language files, and
you have not set your language yet, and you start a playtest via the
command line (e.g. by using Ved), and you then change the language
from the in-game options. That is now fixed.
2023-09-20 16:20:24 -07:00
Dav999
4ae6c77110 Prevent changing language if a textbox is displayed
I really thought I was going to need to block changing the language
in-game altogether, but activity zone prompts are now fixed and the
only obvious problem I can think of right now is having a dialogue
open, so I just disable the language option if a textbox is displayed.
(like how the map menu only has the save option if a script is running)
2023-09-20 16:20:24 -07:00
Dav999
700aa4aaa0 Make activity zone prompts loc::gettext'ed at display time
The translations for the prompts used to be looked up at creation time
(when the room is loaded or the activity zone is otherwise spawned),
which meant they would persist through changing the language while
in-game. This would look especially weird when the languages you switch
between use different fonts, as the prompt would show up in the old
language in the new language's font.

This problem is now fixed by letting the activity zone block keep
around the English prompt instead of the translated prompt, and letting
the prompt be translated at display time. This fixes a big part of the
reason I was going to disable changing the language while in-game; I
might only need to do it while textboxes are active now! :)
2023-09-20 16:20:24 -07:00
Dav999
fd84922a92 Add warning messages for missing fonts/lang folders
If someone makes a build of the game without copying the correct
folders, their version will have no translations, and display some text
wrong (like credits or button glyphs, or any custom levels that rely on
characters in the fonts being there). So I added a message in the
bottom left corner of the title screen to warn for that.
2023-09-20 16:20:24 -07:00
Dav999
da13f39f5d Fix "no language files found" title screen bug
If you've never set a language before (<lang_set> is not 1), then the
language screen will show up before the title screen. Selecting the
language will then make the title screen show up.

If no language files are present, the old logic for handling this was
to simply show the language screen at startup anyway, and let it
display the error message that language files are missing, as a warning
that the game is not packaged correctly. However, this logic has two
flaws:

- If the user has ever had language files and set a language before
  (in a VVVVVV on that computer), the warning element is gone because
  the language screen is not shown in that case - the game is simply in
  English

- If the user has never set a language before, and then goes to the
  language screen later via the menu, they will be sent to the title
  screen, even if they were in-game. The main menu will also be broken.

The new way is to not show the language screen at startup if language
files are missing, and to change the logic so that you will only be
sent to the title screen if you actually haven't seen the title screen
yet.

I will also add a proper warning that fonts or language files are
missing by adding a message in the bottom left corner (in place of the
MMMMMM installed message).
2023-09-20 16:20:24 -07:00
Dav999
365ac514a6 Sync Polish strings.xml, fix small space issue
strings.xml in Polish didn't yet contain the Steam Deck strings, and a
few limits changes.

Also, "Font: " was translated as "Czcionka:", missing the space. This
is fixed now.
2023-09-20 16:10:16 -07:00
Dav999
c04549541f Apply consistency fix to "FINAL LEVEL MASTERED" in Japanese 2023-09-20 15:58:38 -07:00
Dav999
683f66b4d1 Add Polish language files
One of the last languages for 2.4!
2023-09-20 15:40:50 -07:00
Dav999
b5d13d362f Add edits to Japanese translation by KabanFriends
Japanese is 100% complete now, woo!
2023-09-20 15:29:23 -07:00
Dav999
a78b248aaf Update Turkish translation to near-latest strings (99.9%) 2023-09-20 15:29:23 -07:00
Dav999
74a94ae1a8 Reference both cases for "Complete the game" in code
We had two separate cases for translators for this string (a
"TO UNLOCK:" one and a secret lab trophy one) but I forgot to use
the latter in the code, so both places in the game were using the
former. This is now fixed.
2023-09-20 15:29:23 -07:00
Dav999
f240fc517b Update Italian translation to near-latest strings (99.9%) 2023-09-20 15:29:23 -07:00
Dav999
afc2e72897 Update European Portuguese to near-latest strings (99.9%), fix issues
Also, bumped the limit for "Font: " from 14 to 15, the Portuguese
translation is 15 characters and it just fits...
2023-09-20 15:29:23 -07:00
Dav999
4c4a745c56 Update Ukrainian translation to near-latest strings (99.9%) 2023-09-20 15:29:23 -07:00
Dav999
ce8fdc3bbb Update Esperanto language files 2023-09-20 15:29:23 -07:00
Dav999
adf56bdb93 Update Esperanto and Dutch translations to latest strings (100%)
This includes the Steam Deck editor strings.
2023-09-20 15:29:23 -07:00
Dav999
83eaba5436 Add Traditional Chinese language files
And another new language!

This uses the same font as Simplified Chinese. As such, I changed the
displayed name of the font (in the level editor) from 简体中文 to 中文.
2023-09-20 15:29:23 -07:00
Dav999
52b5b47964 Update Brazilian Portuguese translation to near-latest strings (99.9%) 2023-09-20 15:29:23 -07:00
Dav999
1fd37c3276 Add Japanese language files
Another new language! And this is a very interesting one, since it's
based on Nicalis' translation for 3DS and Switch (with their go-ahead).
Which means I had to convert between two completely different
language file formats, which was some work, but it's totally worth it!

Naturally, there are a lot of missing strings, so a translator will
still need to fill in all the blanks (and maintain the translation for
new strings of course)
2023-09-20 15:29:23 -07:00
Dav999
fb0bae7293 Apply Japanese font edits by KabanFriends
- shifted certain "small" variants of katakanas by 1px (ェ, ュ)
- made the ツ and ヅ 1px taller
2023-09-20 15:29:23 -07:00
Dav999
b2b1e47e72 Add Japanese font (8x12)
The next commit will be the initial commit adding the Japanese
translation, so here's the font!

Specifically, it's k8x12L (2021-05-05), and it can be downloaded (in
non-VVVVVV format) from https://littlelimit.net/k8x12.htm
2023-09-20 15:29:23 -07:00
Dav999
c573eb4866 Update Welsh translation to near-latest strings (99.9%), fix issues 2023-09-20 15:29:23 -07:00
Dav999
63a1380595 Update Spanish to near-latest strings (99.9%), fix activity zones 2023-09-20 15:29:23 -07:00
Dav999
711b2e1bb5 Update Russian translation to near-latest strings (99.9%)
Also, the song names are now subtitled.
2023-09-20 15:29:23 -07:00
Dav999
b45d0d8d9d Update German translation to near-latest strings (99.9%), fix issues 2023-09-20 15:29:23 -07:00
Dav999
a200a762c3 Add Korean language files
New language! This uses the 10x10 font added in the previous commit.

This has some minor changes from the delivered version:
- Synced language files, and thus added max_local attributes
- Removed leftover Catalan strings in strings_plural (and reduced to
  just one form)
- Aligned terminal_finallevel
2023-09-20 15:29:23 -07:00
Dav999
68fd718ef8 Add Korean font (10x10)
The Korean localization has been delivered (next commit), so this
commit adds the font for it! The chosen font is Galmuri 9 (specifically
GalmuriMono9, version v2.38.7). It's licensed OFL, and since I had to
convert it to VVVVVV's bespoke font format and shift characters around,
I think we are now bundling a Modified Version of the font, and it has
to use the same license. Including it as font_ko_license.txt and
clearly indicating that the copyright came from the Original Version
should be more than enough.

This version is a bit more polished than the placeholder one posted on
Discord, namely (non-CJK) characters were shifted to fit into their
10x10 bounds as much as possible, and notably the , and . characters
were shifted 2 pixels to the right.
2023-09-20 15:29:23 -07:00
Dav999
5c05c1af48 Sync language files
This adds the following new strings from #993:
- The level editor is not currently supported on Steam Deck, as it
  requires a keyboard and mouse to use.
- The level editor is not currently supported on this device, as it
  requires a keyboard and mouse to use.

Unfortunately this means most languages won't be quite 100% anymore
for a bit, and updates come in which don't have this string yet.
But at least we can track it really well. In the next couple of
commits, when a language is updated with all new strings except for
these, I'll call them 99.9% instead of 100% (I did not get an actual
percentage).
2023-09-20 15:29:23 -07:00
Dav999
d741b3aa27 Fix double return when pressing Esc in editor font menu
The line that checked if the current menu is the font menu ended up
below the code that returned a menu, so this was an easy fix.

Closes #1017.
2023-09-13 23:58:34 -07:00
Terry Cavanagh
a073597ccf
Merge pull request #1014 from cmcaine/patch-1
Update link to FamilyJules' site
2023-09-10 16:44:30 +01:00
Colin Caine
2b7ce9be28
Update link to FamilyJules'
Their website is dead / parked.
2023-09-10 11:44:53 +01:00
AllyTally
3f69b64a09 Fix yellow lab autotiling
A few autotile entries were missing.
2023-09-09 23:04:47 -07:00
Misa
8de93cb0f4 Axe VSync hint in favor of SDL_RenderSetVSync
Originally, we were using just the hint, but this didn't work well for
toggling VSync (see #831). Then I added SDL_RenderSetVSync to SDL, and
used that instead for toggling, but we were still setting the hint on
game startup.

Now, to keep things consistent, and just to make sure we don't get any
surprising behavior should things change in the future, this makes it so
game startup uses SDL_RenderSetVSync too.
2023-09-09 20:07:55 -07:00
Misa
1bf0d11c9e Fix regression: Linear filter persistence
This fixes #1013 by axing the use of SDL_HINT_RENDER_SCALE_QUALITY and
instead using SDL_SetTextureScaleMode.

The hint is unwieldy to use, and since #923, has resulted in a
regression where starting the game in filtered mode then switching to
nearest results in scaled textures still being filtered.

The proper solution is to use SDL_SetTextureScaleMode on the two
textures that are drawn to the final screen: gameTexture and
tempShakeTexture.
2023-09-09 20:07:49 -07:00
Misa
5467dbe3d8 Remove trailing whitespace in textboxcommsrelay 2023-09-09 19:59:36 -07:00
Ally
811d2bdcf6 Re-add fixed bool
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2023-09-09 19:19:57 -07:00
Ally
a72966426b Apply review suggestion from InfoTeddy
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2023-09-09 19:19:57 -07:00
AllyTally
bcc302bcc2 Fix bug with destroy(moving)/destroy(disappear)
This commit fixes an obscure bug with `destroy(moving)` and
`destroy(disappear)` where, when looping through entities, the code
doesn't actually check what the entity is before trying to destroy the
block underneath it.

To fix this, we just put the block-destroying code *inside* of the
check, instead of being outside of it.

I also fixed the code style because it was horrible.
2023-09-09 19:19:57 -07:00
AllyTally
5b2fbeb6b4 Fix gravity line edgeguides 2023-09-09 16:34:26 -07:00
Dav999
d84ce6b2e8 Add limits check to roomnames and roomnames_special
I kinda forgot about these, but they should be checked.
2023-09-09 16:29:38 -07:00
Ethan Lee
c881f621a3 Handle WiiU/Switch GameCube adapter product ID 2023-09-06 09:56:49 -04:00
Dav999
135934289e Fix setfont() changing font of fading out text boxes
Closes #925.

My fix here is to delay the font change until all fading-out textboxes
have disappeared. See it as adding a sort of `untilbars` or `untilfade`
for text box fadeout, into setfont.

This doesn't prevent every possible way to change the font of an
existing textbox, but you would need to use internal scripting to still
do it (and basically be doing it on purpose) - the problem in
simplified scripting when you simply do textbox-setfont-textbox is
gone.
2023-08-30 16:19:25 -07:00
Ethan Lee
68b6bd07ba Add 8BitDo wireless Xbox product ID to detector 2023-08-28 11:25:17 -04:00
Dav999
4e7bf86722 Change editor unsupported message to only appear when editor is selected
Showing the option on the "play a level" option feels to me as though
inexperienced players would think they're not supposed to open the
player levels, because the message says editor levels are unsupported,
right? But the message is only referring to the level editor, so in my
opinion, it's clearer to only show it there.
2023-08-26 22:53:46 -07:00
AllyTally
6549dc0113 Change lab autotiling 2023-08-25 09:55:17 -07:00
AllyTally
03ee60aea6 Update editor_disabled more often, remove -enable-editor 2023-08-25 09:50:27 -07:00
AllyTally
3ca28a5944 Add check for keyboard_is_active 2023-08-25 09:50:27 -07:00
AllyTally
7f03b00635 Remove unused Unused.h includes 2023-08-25 09:50:27 -07:00
AllyTally
8f3c587f7a Replace no level editor string 2023-08-25 09:50:27 -07:00
AllyTally
4830a6e8c1 Re-add accidentally removed address lines 2023-08-25 09:50:27 -07:00
AllyTally
c4db7ca51a Check SteamDeck in keyboard_is_available 2023-08-25 09:50:27 -07:00
AllyTally
a537492d9c Remove NO_EDITOR/NO_CUSTOM_LEVELS, disable editor on Steam Deck
This commit removes the `NO_EDITOR` and `NO_CUSTOM_LEVELS` defines,
which cleans up the code a lot, and they weren't really needed anyways.

This commit also disables the editor on the Steam Deck, and adds a
program argument to re-enable the editor, `-enable-editor`.
2023-08-25 09:50:27 -07:00
Misa
880c7ad8e6 Re-add main menu credits button in M&P
For some reason, the credits button was always specifically removed from
M&P builds. After some discussion with Terry Cavanagh on the VVVVVV
Discord server, we agreed that there was no reason this should be
removed. So, it's getting put back in.
2023-08-24 08:43:04 -07:00
Ethan Lee
c44e8d056a Fix glyphs for 8BitDo Ultimate Wired Controller for Xbox 2023-08-24 09:58:22 -04:00
AllyTally
c3b0a60aa4 Only spawn activity zone if the script exists 2023-08-23 20:16:52 -07:00
AllyTally
02ace10df9 options to spawn entity 21, and no activity zone 2023-08-23 20:16:52 -07:00
AllyTally
b7da899674 Fix UB where image loading fails in certain cases 2023-08-23 17:54:21 -07:00
AllyTally
4148234225 Change if to elseif and remove return 2023-08-23 09:23:10 -07:00
AllyTally
a8bf43adcc Recache textures when returning to ingame from map
Fixes #977.
2023-08-23 09:23:10 -07:00
AllyTally
6952c58878 Fix texture cache missing in certain situations after resizing the window 2023-08-23 09:23:10 -07:00
Ethan Lee
7b40a052ed Controller layout detection 2023-08-22 15:34:03 -04:00
Misa
8e3e29a14c Generalize stretch mode mouse scaling fix
This puts the code to fix mouse coordinates in stretch mode directly
inside KeyPoll::Poll, preventing the need for any other instances of
mouse coordinate usage to copy-paste code.
2023-08-17 19:57:54 -07:00
Dav999
100662612b Apply changes to Irish language files (mainly roomnames)
- Gravity lines are now called "Línte Imtharraingthe"
- There are no longer two rooms called "V"
- Missing character in Rear Vindow fixed
2023-08-12 15:52:58 -07:00
Dav999
04f61f1bcc Update c-hashmap submodule [2023-08-02]
1 new commit:
- remove arbitrary cap of 1000 iterations in hashmap_iterate
2023-08-12 15:52:58 -07:00
Dav999
6fd0b19175 Fix "+1 Rank!" possibly overlapping with time trial time in CJK font
If this text on the time trial results screen would overlap with the
time value, all rank labels will be displayed on the header line
instead ("TIME TAKEN:" etc). This works because the overlap with the
time most likely only happens with CJK fonts (where the time will be
very wide because of the font size) while strings like "TIME TAKEN"
take up very little space due to only needing 4 characters or so for
the same information.
2023-08-12 15:52:58 -07:00
Dav999
e82e1ee236 Update Chinese with new strings
Also realigned the dimensional stability generator terminal, and
reduced the plural forms to just one.
2023-08-12 15:52:58 -07:00
Dav999
1b9ce57622 Add Irish translation
This is up-to-date with the newest strings.
2023-08-12 15:52:58 -07:00
Dav999
b9ef6bfc2f Fix iflang, loadtext and setfont not working with uppercase
For example, pt_PT/pt_BR needs to work as well, and now it does.
2023-08-12 15:52:58 -07:00
Dav999
8d8a5eb605 Make some minor improvements to Dutch translation
Just a few changes mainly to make some things sound more natural.
2023-08-12 15:52:58 -07:00
Dav999
3d7e9a47dd Fix various CJK positioning problems
- ERROR/WARNING screen title was overlapping with message
- Crewmate screen names and rescued statuses were overlapping with each
  other
- Textboxes on Level Complete screen were overlapping with each other
  and the crewmate was not vertically centered in the box
- Some strings were running into each other in flip mode, instead of
  being moved out of each other (PR_CJK_HIGH and PR_CJK_LOW worked the
  wrong way around because of FLIP macros being applied to Y coords)
- In-game esc menu was "bouncy" with selected menu options because of a
  hardcoded 16 pixel offset
- Bindings in the gamepad menu were overlapping with each other
- Some Super Gravitron "Best Time" labels and values were a little too
  close
2023-08-12 15:52:58 -07:00
Dav999
176555c448 Add Simplified Chinese language files
Another new language! And it's a special one, because this is the first
language that uses a non-8x8 font (see previous commit)
2023-08-12 15:52:58 -07:00
Dav999
5b16bde8a7 Add Simplified Chinese font
This will be needed for the Simplified Chinese translation, of which
the first version has just been delivered!

This is the first language with a font bigger than 8x8 (this is 12x12),
so it might be a little rough in some places. Most of the game is
already prepared for it, though!

The only changes I made from the previous version (which was uploaded
on Discord a few times and also sent to the translator) was the …
character - it's often used twice in a row, and it was a little uneven
(looking like -   -  - -   -  - instead of -  -  -  -  -  -); and the
semicolon, which was missing some pixels.
2023-08-12 15:52:58 -07:00
Dav999
37f367af85 Update Catalan language files to latest version (100%) 2023-07-01 20:08:26 -07:00
Dav999
b8dc4a0103 Accommodate for some limit breaks in Welsh
Two changes:
- The labels on the Game Complete! screen for number of trinkets/deaths
  /time etc have been moved two pixels to the right, and had their
  limits increased by 1 character
- The inaccuate limit for "quit to main menu" has been increased
2023-07-01 20:08:26 -07:00
Dav999
9335421783 Made technical fixes to several translations
These are errors and issues that have been reported, but are fixable by
us without needing to involve the translator in the fix, without too
much risk of accidentally breaking grammar rules.

The full list of fixes:
- Fixed some menus going offscreen by removing unneeded words (I'm
  fairly confident after some cross-referencing and research) in
  Portuguese BR, Portuguese PT, Spanish, and French
- Set 0 to singular in numbers.xml in Portuguese BR and French
- Removed the ** from dimensional stability generator to make it not go
  offscreen, and aligned the text better, in Portuguese BR and Spanish
- Fixed some small casing and spacing errors in Portuguese BR, French
  and Welsh. Think of misplaced spaces or sentences starting with a
  lowercase letter
- Fixed a limit break in Spanish (the menu button already drops the
  word "de" in "retraso de la entrada", so the title can too, right?)
- Corrected some typos in French and German (je continuer->je continue,
  9: Finde->9: Feinde and NENÜ->MENU)
- Fixed spacing and alignment in teletype terminals in Welsh (like the
  dimensional stability generator)
2023-07-01 20:08:26 -07:00
Dav999
2667a8a14e Add warning to volunteer translators to README-translators.txt
923efe54d6 added a note to the PR
template, but the best place is probably in the translators readme.
Hopefully this will make sure we no longer get people eager to start
a translation because nobody else had started one yet, and then have to
be disappointed with a reply from us saying we can't accept voluntary
translation contributions.
2023-07-01 20:08:26 -07:00
Dav999
9479a34904 Add Welsh language files
Another new language! It's based off of the old translator pack so
it'll need to be updated later. This commit also includes some fixes
to the originally submitted version, mainly:

- "2.0" and ".99" were broken by excel(?), now fixed
- Removed traces from extraneous rows and columns
- Small human errors

This includes the update received 2023-06-09.
2023-07-01 20:08:26 -07:00
AllyTally
fd4232e9fd Make gravity lines render when screen effects are off 2023-06-15 10:44:40 -07:00
Misa
923efe54d6 PR template: Add note on translations
For now, Terry has decided that translations can't be done by just
anyone. This note here should discourage at least some people from
making pull requests to add their own translations.
2023-06-12 14:06:23 -07:00
Misa
c1a25aa4c2 CLI: Allow no music to be played if save pos used
If you provided any one of -playx, -playy, -playrx, -playry, -playgc, or
-playmusic in command-line arguments for command-line playtesting, then
the game would always try to play music, even if you passed a negative
-playmusic. This wouldn't do anything in that case, unless you had
MMMMMM installed, in which case it would play MMMMMM track 15
(Predestined Fate Final Level) due to the legacy wraparound bug.

To fix this, only play music if the track provided is greater than -1.
Additionally, to prevent it from playing Path Complete by default if you
specify any of the other save position arguments but n ot -playmusic,
it's now initialized to -1 instead of 0.
2023-06-10 11:41:32 -07:00
AllyTally
a6ff75d32e Remove unused drawing overloads 2023-06-08 15:47:39 -07:00
AllyTally
254c46b846 Fix missing parentheses 2023-06-08 15:47:39 -07:00
AllyTally
aa8e731eb9 Fix a couple issues 2023-06-08 15:47:39 -07:00
AllyTally
b533731108 More misc cleanup 2023-06-08 15:47:39 -07:00
AllyTally
8b1dcecd5a Replace drawrect with draw_rect
Turns out `graphics.drawrect` exists. Well, not anymore!
This was another function from before the renderer rewrite which tried
to draw a rectangle by using four filled rectangles. We can draw
outline rectangles properly now, so let's make sure everywhere does it!
2023-06-08 15:47:39 -07:00
AllyTally
d1b9f4f410 Some formatting fixes 2023-06-08 15:47:39 -07:00
AllyTally
d93d773a8a Remove line_rect and prect
`prect` just wasn't needed as it was used in one place, and
`line_rect` isn't used anymore.
2023-06-08 15:47:39 -07:00
AllyTally
81ad7ed9d6 Clean up background drawing code
As #974 states, the lab background only ever uses the first generated
value from `backboxint`, so let's change it to a normal variable
instead of an array. Also, stars don't need their width/height set to
2 constantly... they never change in the first place, Terry!
2023-06-08 15:47:39 -07:00
AllyTally
5089bc373e Add more draw functions, make less code use rectangles
Some code still used rectangles to draw things like lines and pixels,
so this commit adds more draw functions to support drawing lines and
pixels without directly using the renderer.

Aside from making generated minimaps draw points instead of 1x1
rectangles, this commit also batches the point drawing for an
additional speed increase.
2023-06-08 15:47:39 -07:00
Misa
dc9c51dbf3 Implement haltedsong
This fixes a bug where if a track was resumed, pausing it by unfocusing
the window (if enabled, of course) would not resume it after refocusing
the window.

This happens because resuming the music doesn't change currentsong back
from -1, and the window refocusing code checks that currentsong isn't -1
before resuming music.

haltedsong is only used when resuming music. It is set back to -1 when
resuming music or when playing a new track.
2023-06-08 15:38:26 -07:00
AllyTally
e36c2764fb Add separate tilecol max for direct mode
This commit readds the ability to select the rainbow BG in lab tileset
in direct mode.
2023-06-06 21:46:19 -07:00
AllyTally
17e0d6c330 Don't trigger UB with autotile type none 2023-06-06 21:46:19 -07:00
AllyTally
a37cb4aa5f Rewrite autotiling completely
Autotiling was a mess of functions and if chains and switch statements.
This commit makes autotiling better by assigning each direction to one
bit in a byte, giving each different combination its own value. This
value is then fed into a lookup table to give fine control on which
tiles get placed where.

The lab tileset can now use the single tiles which were before unused
in the autotiler, and the warp zone's background tool now places the
fill used in the main game.
2023-06-06 21:46:19 -07:00
Dav999
84a26986e6 Re-add chars 0x00 - 0x1F to the font
- Some levels used chars < 0x20 as non-collapsible spaces, those would
  now show up as [?]
- I recently found out how making characters < 0x20 6 pixels wide
  doesn't work if they're missing from the font altogether

Therefore, the font now starts at 0x00 again instead of 0x20, like it
used to.

Arguably it was an advantage that the game would look extremely messed
up if you made a mistake with the fonts. In particular, a common
mistake could be to copy the new Unicode font.png, but forget to copy
the corresponding font.txt. However, 2.3 didn't come with the unicode
font, 2.4 will, so it'll be a lot less common for people to need to
manually copy the font. And if they do, it's probably for their own
level, and they have something in mind for the font, and if it doesn't
work they'll know fast enough when whatever they're planning doesn't
work (and it would only affect their own level's text, not any menus).
2023-06-05 19:42:29 -07:00
Reese Rivers
d7fbab9198 Update Esperanto strings to latest version (100%) 2023-06-05 19:42:29 -07:00
Dav999
dde9cbf4c3 Update Dutch language files to latest version (100%) 2023-06-05 19:42:29 -07:00
Dav999
bd2bea5640 Add Ukrainian language files
New language! It's still based off the old translator pack so the
newest strings haven't been translated yet, but the language files are
synced and we still need to get these updated in most other languages
either way.

This does include the update delivered on 2023-05-24.
2023-06-05 19:42:29 -07:00
Dav999
8093874e84 Reword "we forgot to print an error message" error message
It now says "we forgot the error message" to be less technical.
2023-06-05 19:42:29 -07:00
Reese Rivers
a21a4c3c00 Fixed minor translation regression for Esperanto
A little while ago the term "AGOKLAVO" (action-key) was chosen to
replace the older "AGBUTONO" (action-button). However, in a recent
language update, I mistakenly used the older term in a new string.
This has been fixed.

Also note that it's written in the accusative case for this string
(with an "N" suffixed) since it is always used as the object of the
sentence where it appears ("Premu AGOKLAVON..." = "Press ACTION...").
2023-06-05 19:42:29 -07:00
Dav999
dfebb5c82c Update Catalan language files to more recent version
Some more strings have already been added since the update, but it's
easy to track.
2023-06-05 19:42:29 -07:00
Misa
cd713ebc71 Fix regression: Showing teles in customloadquick
This fixes a regression where main game teleporter icons (which would be
target icons if flag 12 was on) would be rendered on the minimap after
loading from a custom quicksave.

This is because this was always enabled when loading from a custom
quicksave, but the game didn't start rendering them until PR #898, which
de-duplicated the minimap rendering code.

The best fix here is to just not enable the teleporters when loading
from custom quicksaves.
2023-06-05 19:21:07 -07:00
Misa
404204172c Fix macOS not liking unconsequential fallthroughs
This is technically a fallthrough, but it is literally inconsequential.
Apparently, though, Apple Clang hates it.
2023-06-05 18:02:24 -07:00
Misa
8e3ec9aeef Use enums for time trial indexes
This adds an anonymous enum for time trial indexes (e.g. the bestrank,
bestlives, etc. arrays and timetriallevel), and replaces all integer
literals with them.

Just like the unlock arrays, these are indexes to an array in XML save
files, so the numbers matter, and therefore should not use strict
typechecking.
2023-06-05 17:57:23 -07:00
Misa
e931f2a4a1 Use enums for unlock, unlocknotify, unlocknum
This adds an anonymous enum for the unlock and unlocknotify arrays and
unlocknum function, and replaces all integer literals with them.

This is not named and thus cannot be used for strict typechecking
because these are actually indexes into an array in XML save files, so
the numbers themselves matter a lot.
2023-06-05 17:57:23 -07:00
Misa
14d034e4c6 Use enums for swngame
This replaces the swngame int variable with a named enum and enforces
strict typechecking on it.

Strict typechecking is okay here as the swngame variable is not part of
the API surface of the game in any way and is completely internal.

And just to make things clear, I've added a SWN_NONE enum to use for
initialization, because previously it was being initialized to 0, even
though 0 was the Gravitron.
2023-06-05 17:57:23 -07:00
Misa
414b0647aa Correct orientation of Game Saved clock in Flip Mode
The clock on the Game Saved quicksave screen has always been upside-down
in Flip Mode. And technically, the trinket was too, but this was
unnoticeable because the default trinket sprite is symmetrical.

To fix this, draw flipsprites.png if these sprites are being drawn in
Flip Mode instead of sprites.png.
2023-06-05 17:57:23 -07:00
Misa
4ea26617b8 Initialize currentsong to -1
There's not any ill effects of it being initialized to 0 that I am aware
of (because in most cases, it either gets overwritten anyways or there
isn't a track playing in the first place), but it shouldn't be 0,
because that's Path Complete, so fixing this just in case.
2023-06-05 17:57:23 -07:00
Misa
4058975ce9 Use enums for sound effects
This adds an anonymous enum for sound effects and replaces all calls to
music.playef that use integer literals.

This is not a named enum (that can be used for strict typechecking)
because sound effect IDs are essentially part of the API of the game -
many custom levels use these numbers. This is just to make the source
code more readable without needing a comment to denote what number is
what sound.
2023-06-05 17:57:23 -07:00
Misa
cdeca65be7 Use enums for music tracks
This adds an anonymous enum for music tracks and replaces all calls to
music.play and music.niceplay that use integer literals. Additionally,
this is also done for integer literals for cl.levmusic (except 0) and
music.currentsong where appropriate, but _not_ the music areamap because
that would not make it look very aesthetically pleasing in the code.

This is not a named enum (that can be used for strict typechecking)
because music track IDs are essentially part of the API of the game -
almost every custom level uses these numbers. This is just to make the
source code more readable without needing a comment to denote what
number is what track.
2023-06-05 17:57:23 -07:00
Misa
e9eeaa4186 Remove unneeded play()s in intermission menu
For some reason these were here, but they don't do anything, because the
music here should be playing Presenting VVVVVV already.
2023-06-05 17:57:23 -07:00
Misa
ef46dadb68 Show skip prompt during credits and ending picture
This adds a "- Press {button} to skip -" prompt to both the credits and
ending picture sequences.

It was always possible to skip them by pressing Enter, but not many
people knew this. In fact, even I didn't know this until I saw Elomavi
do it a year or so ago. So it's not really intuitive that this is
possible.

The prompt only shows up if you've completed the game before, and
disappears after two seconds similar to the "[Press {button} to return
to editor]" text.

Unfortunately, given how the game works, game completion is detected
based on if you have unlocked Flip Mode or not. At this point, the
unlock for the game being completed (unlock 5) will already be set to
true no matter what during the Plenary fanfare, but the Flip Mode unlock
(unlock 18) won't be until the player hits "play" on the main menu. As a
special case, the prompt will always show up in M&P (because Flip Mode
is always unlocked in M&P).
2023-06-05 17:57:23 -07:00
Misa
d841612be2 Prevent deleting saves in special or customs
This prevents deleting telesaves and quicksaves in special modes and
custom levels.

Otherwise, rolling credits in a custom level would still delete the main
game quicksave.
2023-06-05 17:57:23 -07:00
Misa
41126c2097 Fix bug: Unable to skip credits in certain cases
If you had the map button held before the game transitioned to the
credits and ending picture sequences, then you wouldn't be able to skip
them, because those gamemodes don't have logic to detect when you've
released the map button.

To fix this, just implement the map button release logic.

We do need a better input system soon...
2023-06-05 17:57:23 -07:00
Dav999
6148550472 Fix setactivityposition still taking two arguments
This command was changed from setactivityposition(x,y) to
setactivityposition(y), but there's a small problem here:

```diff
             else if (words[0] == "setactivityposition")
             {
-                obj.customactivitypositionx = ss_toi(words[1]);
                 obj.customactivitypositiony = ss_toi(words[2]);
             }
```

This meant that the function still took two arguments, the first of
which was unused and the second of which was the Y position of the
activity zone. This is now fixed.
2023-06-04 08:59:40 -07:00
Misa
d09b0d6d84 Fix bug: Playing music during Game Over screen
This fixes a long-standing bug where it's possible to play music during
the Game Over screen in No Death Mode. All you have to do is die while
music is fading out from one area to the next.

The easiest way to do this is in the entrance to Space Station 2, since
there's a music change to Passion for Exploring in Outer Hull (you will
need to go into the zone far enough to activate Pushing Onwards first),
which also contains spikes to die on.

Basically, it's a simple oversight because the nicefade system relies on
music fading out to start playing the next track, but in this case, No
Death Mode fades the music out without accounting for that. It's best to
just disable nicefade entirely when dying in No Death Mode.

Thanks to KSS for reporting this bug.
2023-05-21 23:31:18 -07:00
Misa
7ff4949066 Call savestatsandsettings after unlocking MotU
This calls Game::savestatsandsettings() after unlocking Master of the
Universe (the achievement for completing No Death Mode), instead of
before.

This is not a big deal since Game::savestatsandsettings() is called
anyway whenever the game is gracefully closed since 2.3, but it helps to
make sure people won't lose their achievement data.
2023-05-21 23:08:09 -07:00
Misa
25b4361563 Allow quitting to menu if gamestate is EDITORMODE
Otherwise you wouldn't be able to quit from the editor normally.
2023-05-20 15:22:08 -07:00
Misa
24ef59c65e Prevent other cases of losing unsaved levels by returning to menu
2.3 already made it so that if you ran the `rollcredits` command during
in-editor playtesting, you wouldn't be returned to the title screen
while losing unsaved level changes. But there are plenty of other ways
to go back to the title screen from in-editor playtesting too. Namely,
gamestate 1015 (the gamestate after completing a level) and 82 (time
trial complete).

So just add the appropriate checks to those gamestates, and add a
catch-all check in Game::quittomenu(). Additionally,
Game::updatecustomlevelstats() should not update custom level stats
during in-editor playtesting (otherwise it would still happen even if
the game didn't bring you back to the title screen).

Editor notes will also be shown if the game prevents you from going to
the title screen.

Also, just to make things clear, I also added a level note for when the
level is completed during in-editor playtesting. This is just to make it
clear in cases where it might not be obvious that the game returned you
to the editor for this reason. E.g. you have a terminal that calls
gamestate(1013) in a level with 0 custom crewmates, but when you
activate it, it looks like the terminal didn't work for some reason and
just brought you back to the editor. But that's just only because you
literally just completed the level.
2023-05-18 20:01:03 -07:00
Misa
dea77e98fa Defer hardreset to end of frame in quittomenu
This fixes a bug where the wrong music can play on the title screen, as
reported by AllyTally on Discord.

The bug can be triggered by triggering a room transition right as
game.quittomenu() is called (which is easiest to achieve by placing the
player on an oscillating/"out of bounds" room border in a custom level
so they go back and forth between two rooms every frame, and triggering
gamestate 1013, which starts a fadeout to menu if all custom crewmates
are rescued).

When this happens, game.quittomenu() calls script.hardreset(), but the
rest of the frame still executes, even though we set game.gamestate to
TITLEMODE too (because game.quittomenu() was called by
game.updatestate() which was called by gamelogic(), and game.gamestate
is only checked at the start of the frame). This ends up triggering a
room transition, and since map.custommode is guaranteed to now be off
(because of script.hardreset()), the main game music area code kicks in,
and plays something that isn't Presenting VVVVVV.

The bug here is that we're resetting too early when we still have the
rest of an in-game frame to execute. So, instead, we should only reset
at the end of the frame, and this can be achieved with a defer callback.
2023-05-18 19:11:04 -07:00
Misa
474cdc092e Use levelDirError for graphics errors too
This will actually do several things:

(1) Make the tile size checks apply to the appropriate graphics files
    once again.
(2) Make the game print a fallback error message if the error message
    hasn't been set on the levelDirError error screen.
(3) Use levelDirError for graphics errors too.
(4) Make the error message for tile size checks failing specify both
    width and height, not just a square dimension.
(5) Make the error messages mentioned above translatable.

It turns out that (1) didn't happen after #923 was merged, since #923
removed needing to process a tilesheet into a vector of surfaces for all
graphics files except sprites.png and flipsprites.png. Thus, the game
ended up only checking the correct tile sizes for those files only.

In the process of fixing this, I also got rid of the PROCESS_TILESHEET
macros and turned them into two different functions: One to make the
array, and one to check the tile size of the tilesheet.

I also did (2) just in case FILESYSTEM_levelDirHasError() returns false
even though we know we have an error.

And (3) is needed so things are unified and we have one user-facing
error message system when users load levels. To facilitate this, I
removed the title string, since it's really not needed.

Unfortunately, (1) doesn't apply to font.png again, but that's because
of the new font stuff and I'm not sure what Dav999 has in store for
error checking. But that's also why I did (4), because it looks like
tile sizes in font.png files can be different (i.e. non-square).
2023-05-17 17:11:26 -07:00
Misa
68199396bd Set custommode and custommodeforreal before loading level
This fixes a bug where if a level errors upon loading, it wouldn't take
you back to the levels list.
2023-05-17 12:12:36 -07:00
Misa
28ef3dd7bf Use quittomenu in gotoerrorloadinglevel
game.quittomenu() correctly resets state, as it's the function that's
always used when quitting to menu. This fixes a bug where if a level
with assets failed to load, it wouldn't unload the assets.
2023-05-17 11:45:21 -07:00
Misa
9387186083 Use setLevelDirError for level not found & parse errors
This exports the previously-internal setLevelDirError function in
FileSystemUtils and uses it for if a level is not found or there was a
parsing error. Previously, if a level failed to load in these ways, it
would take you to the error screen with no error, while printing it to
the console. But this makes it more user-friendly.

As a bonus, the text is localizable, just like the existing usage of
FILESYSTEM_setLevelDirError for if a path couldn't be mounted.
2023-05-17 10:09:59 -07:00
Misa
a5eb361448 Set TITLEMODE if loading level results in error
After the scriptclass::startgamemode refactor, a lot of common code is
still being executed even if the level loading failed. This sets the
game-gamestate to TITLEMODE in gotoerrorloadinglevel(), and also returns
early just in case.

Fixes #975.
2023-05-17 08:42:50 -07:00
Misa
27aa10a18c Fix regression: Editing script disables mute buttons
This is quite a simple bug: If you edit a script, then close it, you
will no longer be able to use the mute buttons (N and M).

The problem here is that in 2.3, key.disabletextentry() was called when
closing a script. However, #944 removed the call. Therefore, a
regression.
2023-05-15 18:25:43 -07:00
Misa
ec587fc37e Remove bogus warning if CLI playtesting zip level
If you used command-line playtesting to load a level in a zip, the game
would print a warning saying the level wasn't found. This is because the
warning is printed when it tries to load a level before it loads zips,
inside the metadata load function itself.

To fix this, just move the responsibility for printing the error outside
the function, and put it on the caller.
2023-05-15 18:12:28 -07:00
Misa
0f45e0c52e Print binary blob check fails and remove EOF check
This prints all binary blob check fails. It's an error if the game
rejects the header and will refuse to load it at all, and a warning if
the game continues on.

This also removes the EOF check (`offset + header->size > size`) as a
fatal error. It will only print a warning now. If the last header goes
past the end of file, it will be handled gracefully by PhysFS, which is
the same case in VVVVVV 2.2. This actually fixes a regression from 2.3
where certain custom level tracks that were working perfectly fine in
2.2 (e.g. Summer Spooktacular's track 15) refused to play since 2.3.
2023-05-15 18:04:37 -07:00
Misa
8036471e76 Handle cases where less bytes are read than expected
This checks the return value of PHYSFS_readBytes() in all cases, and
uses a wrapper to not duplicate common logic. If the read fails, then it
will vlog an error, else if the amount of bytes read was less than
expected, it will vlog a warning.

Additionally, the space allocated for a file in
FILESYSTEM_loadFileToMemory is SDL_calloc()ed instead of SDL_malloc()ed
so if there are less bytes than expected, the memory will at least be
zeroed. This also means we don't have to do the null termination,
because the last byte will already be zeroed.

The return value of PHYSFS_readBytes() when reading the headers of
binary blobs now assigns to `header->size`, so the call has been placed
after the increment to `offset` because `offset` needs the correct (i.e.
intended) size of the header.
2023-05-15 17:37:07 -07:00
Misa
90c1a39231 Remove UTF8-CPP from non-bundled dependencies
UTF8-CPP was removed but Dav forgot to also remove it from dependencies
if BUNDLE_DEPENDENCIES was disabled.
2023-05-15 16:26:31 -07:00
Reese Rivers
6158c27205 Update Esperanto language files for new strings 2023-05-08 12:59:16 -07:00
Dav999
d072998995 Add section on editing existing strings to README-programmers.txt
In the past I was thinking we could use some kind of feature in VVVVVV
to track outdated strings across language files. But as it turns out, a
manual solution is actually *perfect* (in combination with automatic
syncing): duplicating strings and marking the old one as outdated. We
started doing it in recent PRs, so let's make it official by adding it
to the readme.
2023-05-08 12:59:16 -07:00
Dav999
bfe068096f Update Dutch language files for new strings
These are strings for #944 and #963.
2023-05-08 12:59:16 -07:00
Dav999
943362559c Adapt font size as needed for "Credits" title in rolling credits
In Italian, "Credits" is "Riconoscimenti", which runs offscreen with
the 3x font size that this title uses in the rolling credits at the end
of the game. I'm not sure if the translators saw that specific
instance, or thought the limit complaint was about the main menu button
all along (which is more prominent and *does* stick out far enough that
the complaint could plausibly have been about that, from a translator's
perspective!)

Either way, it's solved now: this string's width is now checked, and if
it will run offscreen at 3x size, it will now be displayed at 2x size
instead. The limit has been increased from 13 to 20 in the language
files accordingly.
2023-05-08 12:59:16 -07:00
Dav999
3b9d3a687b Add |upper to Italian number strings
The translators indicated that they wanted to use |upper here but it
didn't seem to work, therefore they left it as-is because it was minor.
2023-05-08 12:59:16 -07:00
Dav999
4aa576cd66 Update Italian language files
These updates were delivered 14 Apr, with further updates on 18 Apr.
2023-05-08 12:59:16 -07:00
Dav999
78a3dc23d4 Update Portuguese (BR) language files
This already happened on 2023-03-16, but I held off on updating the
repo's version a bit long: I wanted to wait a little to batch it up
with the next update, but the next update only arrived today, so...
2023-05-08 12:59:16 -07:00
AllyTally
6abb76238d Add textboxtimer command 2023-05-08 12:49:05 -07:00
Misa
fb38668182 Fix regression with controller binds on first launch
I noticed that in 2.3, the game would launch with default controller
binds upon first launch (e.g. no pre-existing unlock.vvv or
settings.vvv), but in 2.4, this wasn't the case, and the default binds
would only be set the next time the game was launched. This would result
in you being essentially unable to use the controller on first launch
save for the analogue stick and D-pad to move between menu selections
and move the player.

Bisecting pointed to commit 3ef5248db9 as
the cause of the regression. It turns out returning early upon error or
a file not existing didn't set the default controller binds, because
they were done at the end of Game::deserializesettings(). But the binds
would be set on the next launch because if the file didn't exist, a new
file would be written, not with the default binds, but then the next
launch would read the file, see there were no binds, and then set the
default binds accordingly.

To fix this, I made it so that the default controller binds are set when
Game is initialized. That way, it covers all cases where the game can't
read a settings file.
2023-04-15 11:20:48 -07:00
Dav999-v
4f2fe163bc Fix rebinding "menu" (esc) bindings to different actions
Ever since 2.3, if you bind a controller button to the "menu" action
(i.e. back/escape) you won't be able to change that button to any other
action, because pressing it anywhere in the binding menu will exit to
the previous menu, without applying the binding.

I know action sets will overhaul everything, but 2.4 may (probably
"should") come out before we have action sets. This part is very
broken, and the fix is very easy: just move the bind-assigning code to
above the menu-return-on-esc code, and add a return.
2023-04-15 10:58:57 -07:00
AllyTally
c6ebf5aeda Disable more flashing elements if a11y is on
For some reason, the accessibility option that was meant to disable
flashes doesn't disable ALL flashes, only screen flashes and screen
shaking. This commit disables a lot more, most importantly randomness
in colors, the player flashing on death/respawn, and teleporters
flashing.
2023-04-05 20:02:29 -07:00
Misa
73aee381ad Update lerp positions during VVVVVV-Man transforms
This updates the interpolation positions of the player when transforming
into and out of VVVVVV-Man.

Otherwise, it can be seen that the player "zips" quickly during these
transformations if the Secret Lab entrance cutscene is played with
screen effects off.
2023-04-05 19:51:58 -07:00
Misa
5fbdf2b663 Fix regression with music fade transitions not working
Unfortunately, 1de459d9b4 caused a
regression where musicclass::niceplay() didn't work, because fading out
the music would cause haltdasmusik() to be called, which would reset the
fade variables.

To fix this, haltdasmusik() will now only reset the fade variables if
it's not called from a fade, which is signaled with a function
parameter.
2023-04-05 19:32:42 -07:00
Misa
c88f249f48 Sync language files after merging #963
This syncs the language files to include the new string about changing
platform speed.
2023-04-02 23:50:11 -07:00
AllyTally
f3cf771cc8 CTRL+, and CTRL+. to modify platv
`platv` is a room property that controls platform speed, and it has
always worked (other than some weird storage issues due to a bug).
However, the editor has no way to edit it currently, so people had to
resort to editing the level file by hand, or with a third-party tool.
This commit simply adds an easy way to modify platform speed.
2023-04-02 23:48:09 -07:00
AllyTally
d328be2a03 Add fill bucket subtool
VED has a fill bucket subtool for tiles and backgrounds, which is
really useful when creating rooms. This commit adds a fill bucket as
well, with an adaptive tool highlight, unlike VED.
2023-04-02 23:48:09 -07:00
Misa
1de459d9b4 Fix nice fade variables persisting when they shouldn't
This fixes a regression from 2.3 where the very beginning of A New
Dimension isn't silent.

A New Dimension's level music is set to Predestined Fate, but there is a
script box the player touches right upon spawning that stops playing
music. Then after the player ascends upwards, they touch a script box
that plays Predestined Fate. But in 2.2 and before, the very beginning
is silent due to the script box that stops music.

However in 2.3, due to the changes made to playing music during a fade,
the initial level music trying to play Predestined Fate during a music
fadeout from the main menu resulted in Predestined Fate being stored in
the nice fade variables, which kicked in after halting the music since
halting didn't reset those variables.

This resets those variables whenever music is halted, and now the
beginning of A New Dimension is back to how it was in 2.2 and before.
2023-03-31 21:39:29 -07:00
Misa
8f23261134 Whiten texture only after loading surface
This fixes a regression where the red channel 0 glitch didn't work,
because the surface was always whitened, because LoadSprites would
whiten the image before converting it to surface.

This regression happened because of #923.

Fixes #962.
2023-03-30 19:54:46 -07:00
Dav999-v
78128222e9 Make textbuttons() work for transparent text boxes
Misa asked me if this should only work for non-transparent textboxes,
and it shouldn't - that was kind of an oversight.

To make it work for transparent textboxes as well, I made a little
restructuring to avoid duplicating the code - fill_buttons() is now
called textbox_line(), and it replaces the direct accessing of the
textbox lines in the printing loops. The code that checks the width
of the textbox does not need to be copied, since the text box is
naturally not drawn for transparent text boxes.
2023-03-30 00:44:33 -07:00
Misa
71dbe95dcb Separate CustomEntity global positions internally
This makes it so that `CustomEntity`s, at least internally, do not use
global tile position. Instead, they will use room-x and room-y
coordinates, which will be separate from their x- and y- positions.

This makes it much easier to deal with `CustomEntity`s, because you
don't have to divide and modulo everywhere to use them.

Since editorclass::add_entity and editorclass::get_entity_at expect
global tile position in their arguments, I've added room-x and room-y
arguments to these functions too.

Of course, due to compatibility reasons, the XML files will still have
to use global tile position. For the same reason, warp token
destinations are still using global tile position too.
2023-03-29 13:49:08 -07:00
Misa
98feeade02 Fix gray entities applying in main game
As reported by Lilithtreasure on the VVVVVV Discord server, it is
possible to get gray moving platforms and enemies in the main game.

This happens if you play the main game after loading a custom level with
a room that is gray at the same coordinates. E.g. if you play a custom
level with a gray room at (12, 4), then exit and go to Gantry and Dolly
in the main game which is also at (12, 4), then the platforms there
would be gray too.

This is because there is a missing map.custommode check.
2023-03-29 13:49:08 -07:00
Misa
09c07d8ad4 Copy rest of music code to OGG sound effects
The missing piece from sound effects was handling what to do when the
buffer ran out. Which seems to happen often when decoding from OGG,
unlike WAV. This handling involves callbacks to functions named
refillReserve and swapBuffers.

Without this code,some sound effects would be cut off early, as
documented in #953. This might also explain the division by 20 - which
I've copied too, just in case.

Now OGG sound effect tracks should be identical to music tracks (except
I've stripped the looping code out).

Fixes #953.
2023-03-29 13:49:08 -07:00
Misa
c36655d5c7 Don't use FAudioVoice_GetVoiceDetails
We were using this function to check if the format of the existing voice
is different from the format of the voice we intended to play. However,
whereas the format we use is the FAudioWaveFormatEx struct, the only
details we get from FAudioVoice_GetVoiceDetails is the
FAudioVoiceDetails struct, which has much less details and is missing
important things like whether the format is 8-bit or 16-bit or something
else.

And of course, if we don't check that the number of bits matches, then
it still leads to playback issues as described in #953.

There are no other functions in FAudio that operate on
FAudioWaveFormatEx structs. So instead, we'll just have to do it
ourselves, and store the format of the existing voice alongside the
format of the intended voice. And then use SDL_memcmp to make sure the
formats are the same before playing, and if not, then destroy and
re-create the voice.

Fixes #953.
2023-03-29 13:49:08 -07:00
AllyTally
07e48565ed Reset textbox colors properly 2023-03-29 13:48:44 -07:00
AllyTally
efa1bad449 Unhardcode textbox colors in textbox arguments
There's a few places where textboxes are constructed through code, but
they pass in the color's RGB values in manually. This commit
unhardcodes most of them them, replacing them with a color lookup.

The ones that weren't changed are special cases, like `175, 174, 174`.
2023-03-29 13:48:44 -07:00
AllyTally
bd34af32de Remove hardcoded textbox colours
We have a custom textbox colour system, why not use it? This also moves
the map of colours from CustomLevels to Script.
2023-03-29 13:48:44 -07:00
Dav999-v
c3e251fea9 Show correct button glyph for interact if Enter/E are not split
I thought 2.2 already had separate map and interact gamepad bindings,
and they simply got neglected and broken with 2.3's split Enter/E key
option. But actually, the new split Enter/E option also applied to
gamepad buttons, and a separate interact binding was added, without
really indicating anything if Enter and E are not split. And I guess
using the same button for map and interact by default also makes sense
for simplicity...

This commit makes sure the button glyph displayed in-game is at least
the correct button. The gamepad bindings menu is also slightly modified
to darken the interact option - the button glyphs code now
automatically causes them to show equal buttons anyway, so it wasn't
too big of a change to also darken the line and disable the binding
option. To me this says: "the interact key is fixed to be the same as
enter right now, but there is a way to change it."

It's still not ideal of course, and I know a similar change to the
gamepad menu to hide the interact option was rejected a year ago
because action sets would already fix it, but it's a year later now,
and showing misleading button glyphs should be fixed in 2.4, whether it
will already have action sets or not (And at this point I think the
plan already is to keep the existing input system for 2.4)

And it's a 3 line diff to darken and disable the option, compared to
fully hiding the option.
2023-03-26 17:56:10 -07:00
Dav999-v
a2c3f47748 Add button glyphs support to selection hint on language screen
The language screen has a "Press Space, Z, or V to select" hint, which
I forgot to update for supporting button glyphs in #943, so this commit
does.

    <action_hint>Press Space, Z, or V to select</action_hint>
    <gamepad_hint>Press {button} to select</gamepad_hint>
2023-03-26 17:54:47 -07:00
Dav999-v
0984e06546 CI: Include CMakeLists.txt in the cache
> CMake is re-running because D:/a/VVVVVV/VVVVVV/desktop_version
> /build_default/CMakeFiles/generate.stamp is out-of-date.
>   the file 'D:/a/VVVVVV/VVVVVV/desktop_version/CMakeLists.txt'
>   is newer than 'D:/a/VVVVVV/VVVVVV/desktop_version/build_default
>   /CMakeFiles/generate.stamp.depend'
>   result='-1'

"newer" probably means "the last edit date is newer because it was just
cloned", so let's see if including it in the cache means the last edit
date will be kept the same too... We can freely do this, as the cache
key includes the hash of the file itself.
2023-03-24 16:56:26 -07:00
Dav999-v
78c3696651 CI: Cache build folder on Windows
Since the initial `cmake -G "Visual Studio 17 2022" ..` seems to be a
real bottleneck on the Windows CI (sometimes taking multiple minutes
for seemingly no reason), cache the build folder based on the hash
of CMakeLists.txt. It should be possible to reuse the build folder if
CMakeLists hasn't changed, and if it does change (because someone adds
a source file or library or something), we'll just do that initial
cmake with -G once, and cache that version as well.

https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows
2023-03-24 16:56:26 -07:00
Dav999-v
62449dfa9a CI: Skip "Download SDL" step instead of using Test-Path, use cache@v3
For some reason, this test takes a dozen seconds or more. Maybe because
it needs to boot up powershell or something like that? No idea, but
it's just sitting there. The cache action has a return value that
indicates whether there was a cache hit, so there's no need to use
Test-Path, saving CI time.

EDIT: Maybe the SDL cache hasn't ever been working. It seems to cache
30 bytes (huh?) and the folder it caches is C:\SDL, not C:\SDL2-2.24.0
as the folder would look like. So it was probably downloading SDL every
time anyway?

Also, I upgraded the cache@v2 action to cache@v3. I added a cache@v3
myself and noticed this was still using cache@v2, so I figured, why not
future-proof it? And that's when I notice this warning: "Node.js 12
actions are deprecated. Please update the following actions to use
Node.js 16: actions/cache@v2." So it's pretty good timing.
2023-03-24 16:56:26 -07:00
AllyTally
0961084ff9 Fix missing leftbutton check 2023-03-24 11:13:16 -07:00
Dav999-v
5d2e477f06 Add /MP to CMakeLists for Windows
This should make compilation on Windows faster because it uses multiple
processors. The Windows CI is the slowest, so let's make it faster.
From my experimentation, adding this flag does seem to reduce compile
times, especially after the first build (default version) is done.

https://learn.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes?view=msvc-170
2023-03-22 17:52:40 -07:00
Dav999-v
65d9d9a0d8 Make Violet's button dialogue work in cutscene test
This was easier than I expected - just add an optional buttons="1"
attribute to cutscenes.xml. It's treated like the speaker attribute -
it's only there as context for the translator, and for the cutscene
test.
2023-03-22 16:20:44 -07:00
Dav999-v
bc156137d8 Sync language files for button glyphs changes
Due to rebasing messiness and diff noise, it's probably best if pull
requests either don't sync all the language files at all (and only
modify the English ones) OR only do it as a final commit. It's still
something we need to figure out, lol.
2023-03-21 19:59:48 -07:00
Dav999-v
01200b5e64 Switch between controller glyphs and keyboard keys automatically
If a controller button is pressed, a controller is connected (even at
startup!) or an axis is moved, the game will switch to displaying
controller glyphs. If a keyboard key is pressed or the last controller
is removed, the game will switch to displaying keyboard keys.
2023-03-21 19:59:48 -07:00
Dav999-v
e55e9efd9b Add controller button layout mappings
This adds mappings from SDL's Xbox-based SDL_GameControllerButton
constants, to glyphs for the following layouts:

- LAYOUT_NINTENDO_SWITCH_PRO,
- LAYOUT_NINTENDO_SWITCH_JOYCON_L,
- LAYOUT_NINTENDO_SWITCH_JOYCON_R,
- LAYOUT_DECK,
- LAYOUT_PLAYSTATION,
- LAYOUT_XBOX,
- LAYOUT_GENERIC,

There may still be errors in these, but they should be mostly correct.
I'm leaving it up to Ethan to make it show the correct button glyphs
for the correct controllers being connected (and possibly to fix these
mappings where needed).
2023-03-21 19:59:48 -07:00
Dav999-v
09365347b6 Replace ACTION in texts by {button} placeholders
Gamepads and the Steam Deck need "ACTION" to be replaced by a
controller glyph, so that's now possible.
2023-03-21 19:59:48 -07:00
Dav999-v
620365614d Add textbuttons() script command, make Violet's ENTER dialogue dynamic
Violet's dialogue now looks like this:

squeak(purple)
text(purple,0,0,2)
Remember that you can press {b_map}
to check where you are on the map!
position(purple,above)
textbuttons()
speak_active

The new textbuttons() command sets the next textbox to replace {b_map}
with the map button, and {b_int} with the interact button. The
remaining keys would be added as soon as they need to be added to
ActionSets.h as well.
2023-03-21 19:59:48 -07:00
Dav999-v
3354a1a352 Add support for button glyph display
This adds a function that converts an action (such as interacting
in-game) to the corresponding button text ("ENTER", "E") or button
glyph (PlayStation triangle, Steam Deck Y, etc). This function
currently only gives the existing ENTERs or Es, because I don't know
how best to detect controller usage, or whether the game is running on
a Steam Deck, or what buttons need to be displayed there. Still, it
should now be really easy to adapt the rendering of keyboard keys to
consoles, controllers, or rebound keys.

To identify the actions that currently need to be displayed, this
commit also adds the initial enums for action sets as described by
Ethan in a comment in #834 (Jan 18, 2022).
2023-03-21 19:59:48 -07:00
Misa
5180e430a2 Sync language files after #944 merged
This syncs language files after the merge of PR #944, since that PR
obsoleted English strings, but didn't sync them.
2023-03-21 18:46:41 -07:00
Ally
0edbf6398a Add suggestions from style pass
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2023-03-21 15:41:49 -07:00
AllyTally
234776bbec Fix macOS build errors/warnings 2023-03-21 15:41:49 -07:00
AllyTally
045ce3a0fc Allow placing warp token destination in walls
Staying consistent with how entities can now be placed in walls, the
warp token destination should follow this as well.
2023-03-21 15:41:49 -07:00
AllyTally
08084e5a97 Add edge-guides for horizontal gravity lines
These visualize the horizontal gravity line kludge for rooms beside
eachother. When you enter another room, gravity lines which look like
they're connected between the rooms try to have the same activated
state.

Basically, if you're in room (1,4) and you go into (2,4), if a
gravity line in (1,4) is activated (gray, on cooldown) and it's
touching the gravity line in (2,4), that gravity line will also be
activated.
2023-03-21 15:41:49 -07:00
AllyTally
d152730510 Add interpolation to tile tools
This uses DDA (https://w.wiki/6RSQ) to draw a line between the previous
frame's mouse position, and the current frame's mouse position. This
means that there will no longer be gaps in lines of tiles if you move
your mouse fast enough (which is actually rather slow, so it gets
annoying quickly).

The editor's timestep is no longer hardcoded to 24, as I assume that
was only done so there would be less gaps in lines of tiles drawn.
With interpolation, that is no longer an issue, so I've removed the
editor's special case for the timestep.
2023-03-21 15:41:49 -07:00
AllyTally
6cae666c76 Clean up scripts in the editor
Scripts used a weird "hook" system, where script names were extracted
into their own list. This was completely unneeded, so it has been
replaced with using the script.customscripts vector directly.

The script editor has been cleaned up, so the cursor's Y position is
relative to the entire script, rather than what's just displaying on
the screen currently. This simplifies a lot of code, and I don't know
why it was done the other way in the first place.

The script selector and script editor cursors have been sped up, since
both lists can be massive, and waiting 6 frames per line is extremely
slow and boring. This is still slow and boring, but we don't have
proper input repetition yet.
2023-03-21 15:41:49 -07:00
AllyTally
9c26f51931 Update English language file
The English language file should always be in sync with the code.
2023-03-21 15:41:49 -07:00
AllyTally
7ac405c831 Move everything to the editor state system
This commit moves everything left out of the previous commit to the
state system. This means a bunch of new functions were added as well,
to avoid the code in each function becoming too huge. A lot of cleanup
was done as well, simplifying logic, merging duplicated code, etc.

This commit does NOT touch "script hooks", script editor logic and
autotiling, as those seem to be their own separate beasts.
2023-03-21 15:41:49 -07:00
AllyTally
7b5ef40926 Fix gravity/warp lines being modified during draw
While warp lines were being drawn, they also got resized to
automatically fit between collision. In renderfixed, gravity lines are
resized the same way. Doing logic while drawing is very poor practice,
so resizing of these has been moved into logic, and merged together.

Aside from some more cleanup, this commit also removes the very poorly
done right click emulation, when you hold CTRL and click. It never
worked well in the past, and even requires a right click to use, so
there's not really any point to keeping it around.
2023-03-21 15:41:49 -07:00
AllyTally
84c6d44c52 Clean code for direct mode drawer
The drawer could definitely be improved further, however I cleaned up a
little bit of the code duplication. I'll have to take a closer look
some other time, but I'm pretty sure that the duplicated code at the
bottom can be removed with a few tweaks, but I'll do that carefully
in a different commit.
2023-03-21 15:41:49 -07:00
AllyTally
34cc15505b Cleanup tools & main rendering
Tools were a mess, spread all over the code with hundreds of `else-if`
statements. Instead of magic numbers denoting tools, an enum has been
created, and logic has been extracted into simple switch/cases, shared
logic being deduplicated.

The base of a state system for the editor has been created as well,
laying a good path for further organization improvements. Because of
this, the entire editor no longer gets drawn underneath the menus,
except for a few pieces which I haven't extracted yet. Either way,
this should be good for performance, if that was a concern.
2023-03-21 15:41:49 -07:00
Misa
e3612afbd3 Store display index of window to settings
I have this annoying issue where the game will open on the wrong monitor
in fullscreen mode, because that monitor is considered to be display 0,
whereas the primary monitor I want is display 1.

To mitigate this somewhat, the game now stores the display index that it
was closed on, and will save it to settings. Then the next time the game
opens, it will open on that display index. This should work pretty well
as long as you aren't changing your monitor setup constantly.

Of course, none of this applies if your window manager is busted. For
example, on GNOME Wayland, which is what I use, in windowed mode the
game will always open on the monitor the cursor is on, and it won't even
be centered in the monitor. But it works fine if I use XWayland via
SDL_VIDEODRIVER=x11.
2023-03-21 00:23:42 -07:00
Misa
54990638fd Persist windowed mode size through fullscreen mode
Previously, the game would not store the size of the window itself, and
would always call SDL_GetRendererOutputSize() (via
Screen::GetWindowSize()) to figure out the size of the window. The only
problem is, this would return the size of the whole monitor if the game
was in fullscreen mode. And the only place where the original windowed
mode size was stored would be in SDL itself, but that wouldn't persist
after the game was closed.

So, if you exited the game while in fullscreen mode, then your window
size would get set to the size of your monitor (1920 by 1080 in my
case). Then when you opened the game and toggled fullscreen off, it
would go back to the default window size, which is 640 by 480.

This is made worse, however, if you were in forced fullscreen mode when
you previously exited the game in windowed mode. In that case, the game
saves the size of 1920 by 1080, but doesn't save that you were in
fullscreen mode, so opening the game not in forced fullscreen mode would
result in you having a 1920 by 1080 window, but in windowed mode.
Meaning that not even fullscreening and unfullscreening would put the
game window back to normal size.

The solution, of course, is to just store the window size ourselves,
like any other screen setting, and only use GetWindowSize() if needed.
And just to make things clear, I've also renamed the GetWindowSize()
function to GetScreenSize(), because if it was named "window" it could
lead one to think that it would always return the size of the screen in
windowed mode, when in fact it returns the size of the screen whatever
mode it is in - fullscreen size if in fullscreen mode and window size if
in windowed mode.

And doing this also fixes the FIXME above Screen::isForcedFullscreen().
2023-03-20 20:59:37 -07:00
Misa
e1e9e47d95 Always update scaling before rendering
This fixes the following bug that only occurs on Wayland: If the game is
configured to be fullscreened and in stretch mode, on startup, it won't
be in stretch mode. It will appear to be in letterbox mode, but the game
still thinks it's in stretch mode.

This is because during the ResizeScreen() call on startup, for whatever
reason, the window size will be reported to be the default size (640 by
480) instead of the screen resolution of the monitor, as one would
expect from being in fullscreen. It seems like when the game queries the
window size, the window isn't actually in fullscreen at that time, even
though this is after fullscreen has been set to true.

To fix this, I decided to always update the logical size before
SDL_RenderPresent() is called. To make this neater, I put the scaling
code in its own function named UpdateScaling().

This bug has existed since 2.3 and does not occur on X11. I tested this
on GNOME Wayland, and for testing it on X11, I used Openbox in a Xephyr
session while running VVVVVV with SDL_VIDEODRIVER=x11.
2023-03-20 20:56:15 -07:00
Misa
64874065eb Rename menuoffTexture to tempShakeTexture
It turns out this texture is only used as a temporary texture to draw
the screen with an offset before rendering it to the output target.

I thought it was used for drawing the map menu animation, but that was
only true of `tempBuffer`, and is no longer true of the new render
system.
2023-03-20 17:05:57 -07:00
AllyTally
371404f393 Fix textboxes fully fading out during blackout
When `blackout` is active, the screen (to simplify) stops getting drawn
to. The excecption is textboxes, which draw anyway. But since the
screen isn't being cleared, removed textboxes stay on screen, since
that texture isn't being cleared. In the SDL_Renderer PR, I
accidentally broke this behavior, so this commit fixes it.

Fixes #951.
2023-03-19 15:15:52 -07:00
Misa
397d7f21b4 Add length check to OGG check
Dav999 pointed out this potential issue on Discord.

While basically all memcmp implementations will terminate early here if
there's a null byte (because it's mismatched), it doesn't hurt to add
the check here.
2023-03-19 14:05:51 -07:00
Misa
2dc7f0b5e8 Remove dialogue and cutscene bars if disabling completestop
This fixes a bug where the trinket collection text boxes, along with the
cutscene bars, would stay on-screen if the player warped to the ship
while they were up.

This only happens during the gamestate 0 anti-softlock checks, and only
if completestop is active in the first place, so text boxes aren't
cleared if the player is doing something that wouldn't lead to a
softlock otherise.

Fixes #921.
2023-03-19 13:48:01 -07:00
Misa
88d49547d4 Evaluate flipping eligibility per-entity
This fixes a regression where the behavior of duplicate player entities
is different, causing a gameplay section in Vespera Scientifica to be
impossible, as described in #903.

In the level, you are allowed to flip in mid-air. Vespera accomplishes
this by having two duplicate player entities stuck in a platform. One of
them is responsible for letting the player flip in one direction, and
one of them is responsible for letting them flip in the other.

In 2.3, this works because in order for a player entity to flip,
`game.jumppressed` is checked, and the entity will flip if
`game.jumppressed` is greater than 0, then `game.jumppressed` will be
set to 0. In this way, whenever a player entity flips, it will set
`game.jumppressed` to 0, so whenever the next player entity is
evaluated, `game.jumppressed` is 0 and thus _that_ entity will not flip.

This is because the for-loop surrounds both the `game.jumppressed` check
and flipping the entity. In 2.4 after #609 and subsequent patches,
however, this is not the case. Here, the for-loop only surrounds
flipping the entity. Therefore, the `game.jumppressed` check is
evaluated only once. So, it will end up flipping every player entity if
the entities are eligible. In this case, one of them is eligible twice,
resulting in the game flipping gravitycontrol four times, which is
essentially the same as not flipping at all (except for all the sound
effects).

Hence, the fix here is to make it so the for-loop surrounds the
`game.jumppressed` check.

Now, this doesn't mean that the intent of #609 - that duplicate player
entities have the same initial velocity applied to them when flipping -
has to be removed. We can just put the for-loops back in. But I
carefully implemented them in such a way that the overall function is
not quadratic, i.e. O(n²). Instead, there's a pass done over the
`obj.entities` vector beforehand to store all indexes of a player entity
in a temporary vector, and then that vector is used to update all the
player entities. In this manner, the function is still linear - O(n) -
over the number of entities in the room.

I tested this to make sure that no previous regressions popped up again,
including #839, #855, and #887. And #484 is still resolved.

Fixes #903.
2023-03-18 21:30:23 -07:00
Misa
ba7519106f Add support for sound OGG files
This adds support for OGG files as sound effects (via renaming them to
the wrong .wav file extension), because in previous versions of the
game, SDL_mixer didn't care what the file extension was, and so some
people relied on this, as described in #900.

This is accomplished by copy-pasting the OGG loading code for music
tracks. For a bit of cleanliness, I put the WAV and OGG loading code in
separate functions.

This is mostly the same code, except that because sound effects don't
loop and can't be paused or resumed, there's no reserve buffer, and
there's no data for loop points.

Also, for some reason, the music loading code divided by 20 in the
`size` calculation. I found that this prematurely cut off sound effects,
and that it made more sense to just not do the division. I don't know
why it was there, but removing it works.

Also also, some OGG files don't work with this. Namely, ones produced by
FFmpeg. To test this, I just extracted 0levelcomplete.ogg from
vvvvvvmusic.vvv and replaced terminal.wav with it. And it works, so
hopefully I won't have to touch audio code again, although I might if
someone complains about this. But either way, I'm committing this
because it's better than it was before.

Fixes #900.
2023-03-18 18:24:30 -07:00
Misa
05ed7e041c Use VVV_freefunc to call stb_vorbis_close
I noticed that this call wasn't using VVV_freefunc. I missed it earlier
when going through Music.cpp and checking for instances when
VVV_freefunc should have been used
(a926ce9851).
2023-03-18 17:51:55 -07:00
Misa
d6bc319535 Add sample rate mismatch check to sound effects
Sound effects already get recreated if the number of channels
mismatches, but the same could be true if the sample rate mismatches
too, which was the case with music tracks as described in #886.

So, just to be sure - and to be consistent with music tracks - sound
effects now check that the sample rate matches, too, and if not, will be
recreated.
2023-03-18 16:40:24 -07:00
Misa
d54e98200f Fix loading sounds with non-16 bit depths
This fixes an issue where sound effects of bit depths that weren't 16,
such as 8, were being played incorrectly, as described in #886.

The problem is that when loading the sound effect, we would always set
the bit depth to 16 no matter what! Instead, we should set the bit depth
to the actual bit depth of the file.

Fixes #886.
2023-03-18 16:38:28 -07:00
Misa
671c90f7dc Fix music playing wrong if played after track of different format
As described in #886, if a track was played when an existing track was
already playing, and the sample rates of the two tracks differ, then the
second track would play wrong and distorted.

This is because the second track would play with the sample rate of the
first. To fix this, halt the track if the sample rate is mismatched,
which destroys the voice. This results in the voice being recreated
later in the Play() function. The track is also halted if the number of
channels or bit depth is mismatched.

Fixes #886.
2023-03-18 16:30:19 -07:00
Misa
9a87d23719 Style: Fixup instances of void arguments
The style we have here is that functions with no arguments are to have
explicit `void` arguments, even though this doesn't apply in C++,
because it does apply in C.

Unfortunately, some have slipped through the cracks. This commit fixes
them.
2023-03-18 15:28:33 -07:00
Misa
63bc71b796 Always add null terminator when loading files
This removes the `addnull` argument from `FILESYSTEM_loadFileToMemory`
and `FILESYSTEM_loadAssetToMemory`, and makes it so a null terminator is
always appended no matter what.

This simplifies things and removes the need for callers to make the
decision about null termination and what its implications are. Then you
get cases where null termination might not happen when it should be,
such as the one df577c59ef (#947) fixed.

When FIQ added the `addnull` argument in
5862af4445 (#117), I'm guessing he did it
because he wanted to be cautious about adding the null terminator to
every file, so he only did it for XML files, which was the only case
needed at the time. But really, there's no downsides to always appending
a null terminator. In fact, it's already always done whenever the STDIN
buffer is loaded.
2023-03-18 15:12:24 -07:00
AllyTally
fb15a0b515 Fix the game texture clearing during menu shaking
Because of how `blackout` works, screen shaking must clear the gameplay
buffer. `blackout` simply pauses rendering, so if the gameplay buffer
gets cleared, then the screen will just be black, otherwise it'll look
like the game is "frozen". VVVVVV only uses `blackout` during screen
shaking, so it works as intended. However, when reimplementing this
behavior in the move to using the SDL_Renderer system, I failed to
notice that since my implementation always clears the gameplay buffer
when shaking, if you open the menu during a shake, instead of seeing
gameplay during the transition animation, you only see black. This has
been fixed with a simple `game.blackout` check before clearing the
gameplay buffer.
2023-03-18 13:53:36 -07:00
Misa
acfa085fd1 Remove reference from customlevelclass::load arg
For some reason, originally, this function mutated the std::string
passed into it by reference. So calling the function could potentially
mutate whatever got passed in, and callers potentially could have relied
on that behavior.

Now that the surrounding callsites have all been cleaned up, though
(especially scriptclass::startgamemode), it's clear that it's only used
in two places: Loading the level in the editor, and loading the level in
live gameplay. In both cases, the passed-in string isn't used ever again
afterwards.

So, it's safe to delete the mutable reference without any undesirable
effects, making the code cleaner and easier to understand. The function
now mutates a copy instead of mutating whatever the caller has.
2023-03-16 21:09:41 -07:00
Dav999-v
79f5e7a05c Fix loading font .txt files that contain null bytes
An example is Maximally Misleading Miserable Misadventure, which has a
font.txt which includes all ASCII characters starting with a 0x00 byte.
This would accidentally null-terminate the string too early.

Instead, we now use the total length of the file again, and keep
getting the next UTF-8 codepoint until the file ends. We still need to
null-terminate it - it protects against incomplete sequences getting
the UTF-8 decoder to read out of bounds.
2023-03-16 14:01:31 -07:00
Dav999-v
df577c59ef Fix font .txt files not being null-terminated
This was an oversight when we migrated to the new UTF-8 system - it
expects a null-terminated string, but the utfcpp implementation worked
with a pointer to the end of the file instead.

I also added an assert in FILESYSTEM_loadFileToMemory() so this is less
likely to happen again - because there should be no valid reason to
have a NULL pointer for the total file size, as well as not wanting a
null terminator to be added at the end of the file.
2023-03-16 12:42:54 -07:00
Dav999-v
4398861d50 Update font.png
Courtesy of Reese. Mainly the accented characters are updated -
uppercase letters are now mostly a pixel higher to make them higher
than lowercase letters (and only a single pixel lower than uppercase
letters without accents, instead of two pixels lower). Accents for
lowercase letters have also been made thicker overall and changed in
appearance.

Also: this font image is converted to indexed grayscale instead of full
RGBA, which makes the file only 4.5 KB instead of about 10 KB.
2023-03-15 17:29:55 -07:00
Dav999-v
975d555a09 Sync language files
This updates all language files to the latest version.

- Some minor errors are also fixed - for example, a small number of
  changes were made to the English string instead of the translation.
  Alignment of the dimensional stability generator terminal is also
  improved in several languages.

- I also discovered that the string "Complete the game" appears twice -
  and has, to be consistent with adjacent strings, two separate
  translations in Portuguese (PT). So this string now properly has two
  different cases so it can be translated separately.

- The limit for TIME/SHINY/LIVES has been bumped from 7 to 8
2023-03-15 17:29:55 -07:00
Dav999-v
ae98a857d2 Add new language files
The following languages are new:
- French
- German
- Italian
- Portuguese (BR)
- Portuguese (PT)
- Russian
- Spanish
- Turkish

Esperanto has also received some updates.
2023-03-15 17:29:55 -07:00
Dav999-v
310f3489d2 Remove key casts from hashmap function calls
We don't need the (char*) or (void*) casts anymore. You may need to
`git submodule update --init`.
2023-03-05 13:21:43 -08:00
Dav999-v
074d54b42b Update c-hashmap submodule [2023-03-05]
Hashmap keys are now const everywhere except for callbacks, meaning we
need less casts.
2023-03-05 13:21:43 -08:00
AllyTally
188fc996bd Fix hardestroom not being translated
`hardestroom` currently stores the current roomname, but it was missing
a call to get the translated string. This has been added, fixing the
hardest room appearing as untranslated.
2023-03-05 11:33:16 -08:00
Dav999-v
d112dee72c Change font::len text argument from std::string to const char*
See the previous two commits, a lot of the time we don't need
std::string objects to be passed to these functions because we already
have C strings.

   Commit 1/3: font::print_wrap
   Commit 2/3: font::print
-> Commit 3/3: font::len
2023-03-04 16:10:17 -08:00
Dav999-v
5e3a4e69ce Overload font::print text argument for both std::string and const char*
Turns out I was overplaying my hand a little when changing font::print
from std::string to const char*, so instead, I'll overload the
function: it can take either a const char* (the main function) or a
std::string (a wrapper). This means any C string that's printed
everywhere else (which is common, especially because loc::gettext gives
them) no longer needs to be converted to a std::string object each call.

   Commit 1/3: font::print_wrap
-> Commit 2/3: font::print
   Commit 3/3: font::len
2023-03-04 16:10:17 -08:00
Dav999-v
264b6474be Change font::print_wrap text argument from std::string to const char*
We no longer need to pass a std::string object to the print and len
functions - in fact, we often only have a C string that we want to
print or get the visual width of (that C string most often comes from
loc::gettext), and it's a bit wasteful to wrap it in a new std::string
object on every print/len call.

This does mean adding a few more .c_str()s, but there's not many places
where a std::string is being passed to these functions, and we already
use .c_str() sometimes.

-> Commit 1/3: font::print_wrap
   Commit 2/3: font::print
   Commit 3/3: font::len
2023-03-04 16:10:17 -08:00
Misa
58d21e956b Fix analogue filter allocating/freeing surfaces every frame
This removes memory churn caused by using analogue mode.

The surfaces are only allocated if analogue mode is turned on, and kept
after they are initialized. Otherwise, if analogue mode is never turned
on (which will be the case for the vast majority of the time the game is
played), then no extra memory is used.
2023-03-04 14:02:47 -08:00
Misa
5eecc2a21d Don't draw texture onto itself for scrolling
Drawing a texture onto itself seems to produce issues on Metal.

To fix this, use a temporary texture instead, that then gets drawn onto
the original texture.

Fixes #927.
2023-03-03 20:21:53 -08:00
Dav999-v
8c697487fd Remove old Spanish language files
These are from a fan translation that was originally made in 2020.
The files were kept around as a possible base for the future Spanish
translators, and now that a first version of the new Spanish
translation is being tested, it's time to remove this one. These files
are making it very annoying for me to test all the new translations and
then jump around between different commits (and they're already not in
the round 2 translator pack).

Actually, this won't really help with the jumping around different
commits part... But the sooner it's removed, the less confusing it will
be when different versions of the language pack are floating around and
the latest version needs to be added, and the less "Changes not staged
for commit" problems you'll get when testing the new language packs.
(Or two different españols being on the language screen)
2023-03-03 18:07:17 -08:00
Misa
33fa0750be Destroy gameScreen after all other graphics are destroyed
This is because destroying the renderer causes use-after-frees since the
renderer destroys all textures when it gets destroyed.

This fixes a Valgrind error where an invalid read occurs because the
font textures get destroyed again after the renderer is destroyed.
2023-03-03 16:46:15 -08:00
Dav999-v
faff8bba5f Fix possible dangling pointers in FontContainer.map_name_idx
The hashmap would get populated with the name of each font, as each
font was being added. Unfortunately, adding a font would also realloc
the storage for fonts, in which the names are also stored... Possibly
invalidating the pointers to the names. This is now fixed by populating
the hashmap after all the fonts are added.
2023-03-03 16:38:53 -08:00
Misa
04743abe91 Destroy towerbg and titlebg textures in destroy_buffers
For consistency, since they are created in create_buffers as well. I
checked with Valgrind (which is very noisy on Wayland, it turns out),
but I didn't see anything about them not being freed. It doesn't hurt to
use VVV_freefunc here anyway, though, since it does a NULL check and
nulls the pointer afterwards, which should prevent double-freeing and
use-after-frees.
2023-03-03 15:34:13 -08:00
Misa
d1e9bdc284 Rename tempTexture to menuoffTexture
I'm going to soon be creating an actually temporary texture, so having
two textures named "temp" would get confusing. This is also a good
chance to correct the name of this texture, because it's not really
temporary, but it's used for map menu animation rendering.
2023-03-03 15:25:15 -08:00
Misa
e31344c68c Remove trailing whitespace in create_buffers
This was added in by AllyTally in
19b2a317f1.
2023-03-03 15:23:55 -08:00
AllyTally
5beaf973ce Strip out old special roomname system
This commit replaces the old system with the new one, making it much
easier to edit the transforming and glitchy roomnames. Additionally,
this syncs flag 72 to finalstretch.

Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2023-03-02 22:58:12 -08:00
AllyTally
dd108a035f Animated roomnames, setroomname command
This commit adds a better system for animated roomnames.

The old system, like many other systems, were very hardcoded, and can be
described as mostly else-if chains, with some fun string comparisons.
The new system uses lists of text for transformations and glitchy names,
making it much easier to add new cases if needeed.

This commit implements the system but does not replace the old system,
where that is done in the next commit.

The settings for special roomnames can be read from level XML, and
`setroomname()` can be used from commands to set a new, static name.
2023-03-02 22:58:12 -08:00
Misa
27da7fe935 Update BUNDLE_DEPENDENCIES description with removal of UTF-CPP
We no longer use UTF8-CPP, so the mention of it should be removed. Also,
I conjoined the two phrases with a semicolon; it flows better.
2023-03-01 22:58:15 -08:00
Dav999-v
368b9f1b3d Replace use of PHYSFS UTF-8 functions by UTF8.h
This is a massive simplification of the UTF-8 work there, lol
2023-02-27 23:00:41 -08:00
Dav999-v
a91c85d92e Fix UTF-8 handling in next_wrap
Finally this FIXME can be removed!
2023-02-27 23:00:41 -08:00
Dav999-v
b02e4737d4 Remove utfcpp submodule
It's no longer used now that we have our own UTF-8 functions.
2023-02-27 23:00:41 -08:00
Dav999-v
f34aa65faa Replace utfcpp by UTF8.h in Font.cpp
I'm also planning to change the argument types of font::len,
font::print and font::print_wrap from const std::string&s to
const char*s, but I'll do that separately.
2023-02-27 23:00:41 -08:00
Dav999-v
8a011c3061 Replace utfcpp by UTF8.h in Textbox.cpp 2023-02-27 23:00:41 -08:00
Dav999-v
736ce3ecbf Replace utfcpp by UTF8.h in Localization.cpp 2023-02-27 23:00:41 -08:00
Dav999-v
a545384677 Replace utfcpp by UTF8.h in KeyPoll.cpp 2023-02-27 23:00:41 -08:00
Dav999-v
c5a48776c9 Replace utfcpp by UTF8.h in Editor.cpp 2023-02-27 23:00:41 -08:00
Dav999-v
393c5ac4fe Replace utfcpp by UTF8.h in CustomLevels.cpp 2023-02-27 23:00:41 -08:00
Dav999-v
2474623b59 Remove #include <utf8/unchecked.h> from Graphics.cpp
Turns out the last utfcpp function had already been removed from this
file, so the include can simply be removed.
2023-02-27 23:00:41 -08:00
Dav999-v
3ce4735d50 Add UTF8.c
This is a small library I wrote to handle UTF-8.

Usage is meant to be as simple as possible - see for example decoding
a UTF-8 string:

  const char* str = "asdf";
  uint32_t codepoint;
  while ((codepoint = UTF8_next(&str)))
  {
      // you have a codepoint congrats
  }

Or encoding a single codepoint to add it to a string:

  std::string result;
  result.append(UTF8_encode(0x1234).bytes);

There are some other functions (UTF8_total_codepoints() to get the
total number of codepoints in a string, UTF8_backspace() to get the
length of a string after backspacing one character, and
UTF8_peek_next() as a slightly less fancy version of UTF8_next()), but
more functions could always be added if we need them.

This will allow us to replace utfcpp (utf8::unchecked) and also fix
some less-than-ideal code:

- Some places have to resort to ignoring UTF-8 (next_wrap) or using
  UCS-4→UTF-8 functions (VFormat had to use PHYSFS ones, and one other
  place has four lines of code including a std::back_inserter just for
  one character)

- The iterator stuff is kinda confusing and verbose anyway
2023-02-27 23:00:41 -08:00
Fussmatte
22f1a18fe7 Update Esperanto translation 2023-02-24 20:57:17 -08:00
Dav999-v
32e14755dd Fix centering of translated "Level Complete!" and "Game Complete!"
Operator precedence meant that `sc == 2 ? PR_2X : PR_1X | PR_CEN`
didn't work how I expected it to. So I added some parentheses.
2023-02-24 18:48:24 -08:00
Fussmatte
00279c0e04 Fix minor visual issue in int1yellow_5
Originally the changedir command was used here, making
Vitellary look left and then immediately snap back to
looking right. Now the changeai command is used instead
to make him actually look left, and then look back to
the right on his last textbox.
2023-02-24 17:54:29 -08:00
Misa
ede52ccc48 Add Jules de Sartiges to Credits.h
I noticed this name was present in CONTRIBUTORS.txt but missing in
Credits.h. Their contribution was PR #782.
2023-02-21 12:28:57 -08:00
Misa
abab6864d7 Add iliana etaoin to contributor list
PR #931 was merged, so this adds xer to CONTRIBUTORS.txt and Credits.h.
2023-02-21 12:27:50 -08:00
iliana etaoin
5d719d3e90 Add magic string to the start of the game global
The `-addresses` command-line option added in 64be99d4 helps
autosplitters on platforms where VVVVVV is not built as a
position-independent executable. macOS has made it increasingly
difficult, or impossible, to build binaries without PIE.

Adding an obvious string to search for will help tools that need to deal
with versions of VVVVVV built with PIE. The bytestring to search for is
`[vVvVvV]game`, followed by four null bytes (to avoid finding it in the
program code section). This identifies the beginning of the game object;
addresses to other objects can be figured out by relative offsets
printed by `-addresses`, since ASLR can only change where the globals
begin.

Partially fixes #928; it may still be advisable to figure out how to
explicitly disable PIE on Windows/Linux.
2023-02-21 12:24:20 -08:00
Dav999-v
2201cfe1e9 Increase max="" value for jukebox song names in language files
This makes it possible to use another line for a subtitle without it
being flagged as a string that is too long.
2023-02-20 19:43:39 -08:00
Dav999-v
086b157152 Allow multiple lines and wordwrapping in activity zone prompts
Activity zone prompts have always been limited to a single line,
because the text box had a hardcoded size. A translator requested for
the possibility to add a subtitle under music names for the jukebox,
and the easiest solution is to make it possible to translate a prompt
with multiple lines. This is possible now, and the textbox even
wordwraps automatically.

(I wouldn't really like to see translations using multiple lines for
stuff like "Press ENTER to talk to Vitellary", especially if it wraps
with one name and not with another, but if a string is too long,
wordwrapping will look better than text running out of the box.)
2023-02-20 19:43:39 -08:00
Dav999-v
a320ee3b4d Deduplicate font::print for activity zone prompts
There were two print calls, one for the transparent case, and one for
a regular textbox. The print calls were nearly the same except for the
color, and for some reason the transparent case didn't have PR_CJK_LOW
(that one is on me).
2023-02-20 19:43:39 -08:00
Dav999-v
28e9df7f47 Move line width addition in next_wrap to be after switch block
There is no overlap in side effects between this line and the switch
statement after it, but it did result in adding the width of a final
null terminator or newline to the width of the current line, which is
a waste because those widths both 1) require trying to find
non-existent characters in the font and 2) will not be used.

I found this out because I added a debug print in find_glyphinfo(), and
something was requesting lots of codepoint 0 from the font.
2023-02-20 17:00:06 -08:00
Dav999-v
2934462410 Update fonts/README.txt for button glyphs
Also added some things to it that I forgot about last time.
2023-02-20 17:00:06 -08:00
Dav999-v
d883ff6938 Add support for button glyph fallback fonts
In a button glyph font (like buttons_8x8.fontmeta) you can now specify
<type>buttons</type> to indicate that it's a button glyphs font. In a
normal font, you can specify <fallback>buttons_8x8</fallback>. This
will make it such that if a character is not found in the main font,
it will instead be looked for in buttons_8x8. If not found there
either, the main font's U+FFFD or '?' will be used as before.
2023-02-20 17:00:06 -08:00
Dav999-v
7a06b61f5d Add name->idx hashmap in FontContainer
This makes find_font_by_name() not O(n). It's not really a big deal,
because there won't be many fonts, but it'd make a function in the next
commit (finding the given fallback font for each font by name) O(n^2).
It's easy enough to add the hashmap.
2023-02-20 17:00:06 -08:00
Dav999-v
6cf63359d3 Add button glyph font files
They are not used yet in this commit - this just adds the graphics and
data for the glyphs. It also adds a <fallback> tag to font.fontmeta to
use buttons_8x8.

The icons themselves are made by Reese Rivers - see #859.
2023-02-20 17:00:06 -08:00
Misa
2ac85e6929 Fix editor selection box not showing up
Whoops.

Also outlined the text for page 2 too.
2023-02-19 12:31:58 -08:00
Misa
6e6cf1bfc3 Draw text outline on editor hotkeys
This makes them stand out more.

The border around the tool has also been moved to be drawn first.
Otherwise, it would be drawn on top of the outline of the text, which
would look bad.
2023-02-19 12:12:02 -08:00
Misa
64be99d496 Add -addresses command-line argument
This prints the address of every global class to the console, and then
exits.

This is useful for autosplitters, which read memory addresses directly.
Any time a new version of the game is shipped, this makes updating the
autosplitters much easier as people don't have to find the addresses of
the global classes themselves.
2023-02-17 20:47:32 -08:00
Misa
acca4747f7 Remove x-position from setactivityposition
After discussing with Ally and Dav, we came to the agreement that this
is basically useless since the prompt will always be centered and take
up most of the horizontal space of the screen.

And the x-position was only added as an offset because at some point,
there was a missing space from the side of the "- Press ENTER to
Teleport -" prompt, and the offset was there so people could mimic the
prompt accordingly. But that was fixed at some point, so it's useless
now.
2023-02-17 20:47:32 -08:00
AllyTally
3d5ba95b96 custom textbox colors
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2023-02-15 10:39:20 -08:00
AllyTally
de43005676 Fix screen shake during flip mode not flipping the screen
This fixes a regression introduced by #923 where flip mode no longer flips the screen while the screen is shaking.
2023-02-14 15:33:29 -08:00
Ally
73a80a9b4f Apply suggestions from code review
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2023-02-14 14:56:27 -08:00
AllyTally
1183083355 Allow a maximum of 26 lines, with L suffix 2023-02-14 14:56:27 -08:00
Misa
299ed90493 static-qualify mount_pre_datazip
This function is used nowhere else, so it should be made static and
unable to be linked to from other files.
2023-02-14 09:44:31 -08:00
Dav999-v
4b34602eec Improve ordering of font charset loading
Even though it would be a bizarre combination, declaring no character
set (neither via <chars> nor via font.txt) meant that <special>
couldn't be used because the ASCII fallback charset would be loaded
after special ranges were processed. Now, all the methods of loading
the charset are attempted sequentially, and only afterwards, the
special ranges are loaded.
2023-02-13 23:27:00 -08:00
Dav999-v
716a241b79 Replace PR_COLORGLYPH_BRI(a) and PR_ALPHA(a) with PR_BRIGHTNESS(a)
There used to be two ways of fading in/out text in VVVVVV:
- Local code that modifies the R, G and B values of the text
- Keeping the RGB values the same and using the alpha channel

The latter approach is only used once, for [Press ENTER to return to
editor]. The former approach causes problems with colored (button)
glyphs: there's no way for the print function to tell from the RGB
values whether a color is "full Viridian-cyan" or "Viridian-cyan faded
out 50%", so I added the flag PR_COLORGLYPH_BRI(value) to tell the
print function that the color brightness is reduced to match the
brightness of colored glyphs to the brightness of the rest of the text.

However, there were already plans to make the single use of alpha
consistent with the rest of the game and the style, so PR_ALPHA(value)
could be removed, as well as the bit signifying whether the brightness
or alpha value is used. For the editor text, I simply copied the "Press
{button} to teleport" behavior of hiding the text completely if it
becomes darker than 100/255.

Another simplification is to make the print function handle not just
the brightness of the color glyphs while local code handled the
brightness of the normal text color, but to make the print function
handle both. That way, the callsite can simply pass in the full colors
and the brightness flag, and the flag name can be made a lot simpler as
well: PR_BRIGHTNESS(value).
2023-02-13 23:27:00 -08:00
Dav999-v
d1f6c1adf2 Replace "by" for level authors with happy face
"by {author}" is a string that will cause a lot of localization-related
problems, which then become much worse when different languages and
levels can also need different fonts:

- If the author name is set to something in English instead of a name,
  then it'll come out a bit weird if your VVVVVV is set to a different
  language: "de various people", "por various people", etc. It's the
  same problem with Discord bots completing "playing" or "watching" in
  their statuses.

- Translators can't always fit "by" in two letters, and level creators
  have understandably always assumed, and will continue to assume, that
  "by" is two letters. So if you have your VVVVVV set to a language that
  translates "by" as something long, then:
  | by Various People and Others |
  ...may suddenly show up as something like:
  |thorer Various People and Othe|

- "by" and author may need mutually incompatible fonts. For example, a
  Japanese level in a Korean VVVVVV needs to be displayed with "by" in
  Korean characters and the author name with Japanese characters, which
  would need some very special code since languages may want to add
  text both before and after the name.

- It's very possible that some languages can't translate "by" without
  knowing the gender of the name, and I know some languages even
  inflect names in really interesting ways (adding and even replacing
  letters in first names, surnames, and anything in between, depending
  on gender and what else is in the sentence).

So to solve all of this, the "by" is now replaced by a 10x10 face from
sprites.png, like a :viridian: emote. See it as a kind of avatar next
to a username, to clarify and assert that this line is for the author's
name. It should be a fairly obvious/recognizable icon, it fixes all the
above problems, and it's a bonus that we now have more happy faces in
VVVVVV.
2023-02-13 23:27:00 -08:00
Dav999-v
ed7379c41b Position CJK room name correctly in editor 2023-02-13 23:27:00 -08:00
Dav999-v
f896a964fa Update readmes for new font system
There were still some TODOs left open for the font changes, and I also
made a dedicated README.txt in the fonts directory.
2023-02-13 23:27:00 -08:00
Dav999-v
17d3c756c7 Write max_local to language files during sync
If your language has a bigger font, the max attribute isn't really
helpful to you as a translator, so the sync feature adds a special
max_local attribute which is accurate to the font size. This was
already documented in advance.

If used, we now also write an attribute in the root tag of strings.xml
and strings_plural.xml, that looks like max_local_for="12x12". I
decided to add this attribute after finding out the Excel macros would
be really hard to change to only show a max_local column if it is ever
used (it'd need to look ahead through the strings until it finds a
string with a max, or remove the column if no string has used it), but
it's also a convenience for translators.
2023-02-13 23:27:00 -08:00
Dav999-v
11b372c741 Harden next_wrap against getting stuck on a single character
If, somehow, a single character is wider than the limit, next_wrap
would get you stuck in an infinite loop by refusing to update the start
index and giving a line length of 0. Now, it just gives you a line with
that single character.

I also made some small readability improvements: I renamed next_wrap_s
to next_wrap_buf, and added comments at the top of both functions
explaining what they do.
2023-02-13 23:27:00 -08:00
Dav999-v
9747843c18 Add menu for selecting the level font
By default, when you open the level editor to start a new level, the
level font will now match your VVVVVV language; so if you're, say,
Japanese, then you can make Japanese levels from the get-go. If you
want to make levels for a different target audience, you can change the
font via a new menu (map settings > change description > change font).
The game will remember this choice and it will become the new initial
level font.
2023-02-13 23:27:00 -08:00
Dav999-v
b030ce568f Make main game content use interface font instead of 8x8 font
If a custom level doesn't specify a font, it should be the 8x8 font.
But the main game can't specify a font, it's just the interface font
because that's for the language that the game is in.
2023-02-13 23:27:00 -08:00
Dav999-v
25feb9dbb5 Make wordwrapping functions take font arguments
They need to know how wide the text is going to be in a particular
font, so font::string_wordwrap and font::string_wordwrap_balanced now
take a flags argument like all the printing and dimensions-getting
functions. next_wrap and next_wrap_s take a Font* now, they're internal
to Font.cpp so they can take a Font and avoid double flag-parsing. But
if any non-Font.cpp code needs next_wrap/next_wrap_s in the future, I'd
just make a public wrapper that takes a uint32_t flags and passes the
Font* to the internal functions.
2023-02-13 23:27:00 -08:00
Dav999-v
0eaceed0a2 Completely remove Graphics::PrintWrap
All print calls are font:: ones now and all the old Graphics:: print
functions have been removed, the migration is complete!
2023-02-13 23:27:00 -08:00
Dav999-v
0e30087f3b Fix indentation style of case Menu::levellist in menurender
This is one of the few places still using 2-space indentation instead
of 4 spaces, and it makes it very annoying to work with when your tab
key inserts four spaces - so I could either just mimic it for the time
being, or I could just fix it while I was at it.
2023-02-13 23:27:00 -08:00
Dav999-v
7ecff42e0e Completely remove Graphics::Print, make many CJK positioning fixes
Only Graphics::PrintWrap is left of the old print functions!
2023-02-13 23:27:00 -08:00
Dav999-v
cc6b00a711 Add setfont scripting command
The <font> in the level file is basically the starting font, but it can
be changed at any time via scripting.
2023-02-13 23:27:00 -08:00
Dav999-v
7db0e73109 Allow levels to select a font via XML, show correct font in levels list
There still needs to be a menu for selecting a font, but it can now be
loaded and saved correctly in the XML!
2023-02-13 23:27:00 -08:00
Dav999-v
8d5e3b1a8a Improve level metadata display and font handling
- If the level font is higher than 10 pixels, the third description
  line (Desc3) is disabled and unavailable. CJK languages require less
  characters to convey the same message (140 characters caused people
  to cram tweets in all languages except CJK) and this gives us enough
  room in the levels list without having to cram the metadata even more
  than it already was or showing less levels per page.
- The "Untitled Level" and "by Unknown" now selectively show up in the
  interface font instead of the level font.
2023-02-13 23:27:00 -08:00
Dav999-v
653eee505b Clean up Font.h, move structs into Font.cpp
None of the structs in the new font system ended up being "publicly"
accessible, they were all treated as implementation details for
Font.cpp to use, so these structs are now fully defined in Font.cpp
only.
2023-02-13 23:27:00 -08:00
Dav999-v
d2461c90ce Completely remove Graphics::bigprint
The last two deprecated functions are:
- Graphics::Print
- Graphics::PrintWrap

These are used a lot, but they're relatively easy to replace, since the
only flag I probably have to immediately worry about is PR_CEN. I do
often need to add PR_FONT_* flags but I don't need to add any
PR_2X/PR_3X/PR_4X anymore.
2023-02-13 23:27:00 -08:00
Dav999-v
ddaabb3efe Completely remove Graphics::bprint
Only three deprecated functions remain:
- Graphics::Print
- Graphics::PrintWrap
- Graphics::bigprint

I also fixed multiline transparent textboxes having their outlines
overlap the text itself, and fixed textboxclass::padtowidth assuming
glyph widths of 8 (it made the hints at the start of intermission 1
run offscreen for example)
2023-02-13 23:27:00 -08:00
Dav999-v
d784f7f61a Completely remove Graphics::PrintAlpha and Graphics::bprintalpha
Only four deprecated functions remain:
- Graphics::Print
- Graphics::PrintWrap
- Graphics::bigprint
- Graphics::bprint

Graphics::bprint is the least-used one of them, and after that, the
other functions are used a LOT, but it'll be a lot faster to go through
them, since I have less and less flags to worry about. I'll probably
start using Vim macros again like I did for loc::gettext()ing strings,
or maybe I'll automate this completely.
2023-02-13 23:27:00 -08:00
Dav999-v
9749858365 Completely remove Graphics::bigbprint, bigrprint and bigbrprint
I migrated all of them to font::print, so they can now be removed.
Six deprecated print functions left! (Of which some are used a whole
lot, it's simpler if the lesser-used ones are gone first.)
2023-02-13 23:27:00 -08:00
Dav999-v
c82e1f2902 Use PR_RIGHT for interim version info in main menu
This used some constants counting numbers of characters
(LEN_INTERIM_COMMIT and LEN_BRANCH_NAME) and even an SDL_arraysize,
all multiplied by 8, to get the length of the displayed text.
Now it just uses the new PR_RIGHT flag of font::print.

I did also force the 8x8 font for all the interim information, since
the date kinda overlapped with the menu options... And all of these
lines only show up in interim versions anyway, except for the version
number - which is left in the interface font for consistency with the
rest of the menu in non-interim versions. The inconsistency in interim
versions doesn't really matter all that much I think, it's just some
technical/debug info.
2023-02-13 23:27:00 -08:00
Dav999-v
da6be09993 Completely remove Graphics::len
I migrated all graphics.len calls over to font::len (and also migrated
prints mainly surrounding those graphics.len's) so the old len function
is now completely removed.
2023-02-13 23:27:00 -08:00
Dav999-v
a706fb249a Migrate more prints and graphics.len calls to font::
I especially focused on graphics.len and the print calls around them,
because graphics.len calls appear a bit less often, might be overlooked
when migrating print calls (thus possibly using different fonts by
accident) and are often used for some kind of right-alignment or
centering which can be changed into PR_RIGHT or PR_CEN with a different
X anyway.

Notably, I also added a new function to generate these kinds of
sliders: ....[]............

Different languages means that the slider for analogue stick
sensitivity needs to be longer to fit possibly long words for
Low/Medium/High, and then different font sizes means that the longer
slider won't fit onscreen in a language that needs a 12-wide font. So
slider_get() can take a "target width", which dynamically changes the
number of characters depending on the width of them in the interface
font.

I kinda forgot that I could force the 8x8 font instead of adapting the
characters in the slider to the font, and other ideas (like using
different characters or a more graphical progress bar) have been
brought up on Discord, so this might all change again sooner or later.
2023-02-13 23:27:00 -08:00
Dav999-v
7c55ea7832 Fix pause screen font on language screen with other language selected
If you for example have your VVVVVV set to English, have the option for
Chinese selected, and then the window loses focus, the English pause
screen would show up in the Chinese font. This is now fixed.
2023-02-13 23:27:00 -08:00
Dav999-v
df4e351b30 Use languages' fonts for options on language screen 2023-02-13 23:27:00 -08:00
Dav999-v
5dad6b38be Add language-specific font configuration
meta.xml can now have a <font> tag, which gives the name of the font
that the language needs. This will directly control the interface font
when the language is active, and will soon also control the font used
for each option on the language screen.
2023-02-13 23:27:00 -08:00
Dav999-v
dadb7f2623 Complete font::print_wrap flag handling
Also added some borders to more of the text in room name translator
mode, fixed a positioning issue if the interface font is not 8x8, and
migrated the trophy texts to font::print_wrap (including
PR_COLORGLYPH_BRI that still needed to be done)
2023-02-13 23:27:00 -08:00
Dav999-v
689d6e3e97 Print activity zone text in correct font, remove Graphics::drawtextbox
Activity zones need to be in the interface font if the message is from
the system (like Press ENTER to activate terminal, which may be in a
different language) and in the level font if it's a customized message
(setactivitytext).

Graphics::drawtextbox was counting the textbox width and height in
8x8 characters, even including the borders as characters, so it'd need
to be told what the font for the textbox is, and then probably only the
height needs to be adapted to the font and not the width because that's
adapted to the screen width... So just call Graphics::drawpixeltextbox
directly instead. It was already weird enough how actual cutscene
textboxes called Graphics::drawtextbox with x/8, y/8 before the
previous commit, (when you already have the pixel width and height!)
only to have that be a wrapper for drawpixeltextbox by doing x*8, y*8.
2023-02-13 23:27:00 -08:00
Dav999-v
48a4e19635 Migrate more prints to font::, determine font for most textboxes
Some textboxes need to be in the level font (like room names, cutscene
dialogue, etc - even in the main game), and some need to be in the
interface font (like when you collect a shiny trinket or crewmate). So
most of these textboxes now have graphics.textboxprintflags(font_flag)
as appropriate.

RoomnameTranslator.cpp is now also migrated to the new print system -
in room name translator mode, the room name is now displayed in the 8x8
font if it's untranslated and the level font if it is.
2023-02-13 23:27:00 -08:00
Dav999-v
6ca83114bc Start using level-specific font where needed, make CJK tweaks
Level text such as room names, text box content, and the contents of
the script editor need to be displayed in the level-specific font, and
tweaked to look right. This involves displaying less lines in the
script editor, making text boxes bigger, displaying some text higher
and some text lower. This is still unfinished, but it's the real start
of a migration to font::print functions!
2023-02-13 23:27:00 -08:00
Dav999-v
29a3789dec Add some more preparation for multi-font support and proper unloading
find_font_by_name() just finds the index of a given font name. This
index is supposed to be stored and reused, because the font (for a
language/level) won't be changed very often. So this function would
only run when getting the language metadata, when loading a level, etc.
2023-02-13 23:27:00 -08:00
Dav999-v
83d645c8e3 Add font containers for global and custom fonts
All global fonts and all custom fonts in a level are now loaded, and
added to their respective "vectors". The selected font is still always
as the global font.png, and the custom level font also isn't selected
yet, but it's now easier to implement that.

Also, I added FILESYSTEM_enumerateAssets, which #902 already has but I
needed it now. I also rewrote it to not use std::vector<std::string>.
That was my idea, it's also how FILESYSTEM_getLanguageCodes worked,
so for symmetry, that function is getting changed as well.
2023-02-13 23:27:00 -08:00
Dav999-v
22dcc29d45 Handle more flags in print and len functions
The font::len function now handles the printing scale, so it can
immediately return the scaled length instead of having the caller
calculate it. The print function now handles CJK low/high flags and
vertically centers CJK text by default (instead of letting it stick
out on the bottom).
2023-02-13 23:27:00 -08:00
Dav999-v
159c70dade Move wordwrapping functions and len to Font.cpp/font:: namespace
The following functions were moved directly:
- next_wrap
- next_wrap_s
- string_wordwrap
- string_wordwrap_balanced
- string_unwordwrap

These ones will probably still need get a flags argument, except for
string_unwordwrap (since they need to know what font we're talking
about.

The implementation of graphics.len has also been moved to Font.cpp,
but graphics.len still exists for now and is deprecated.
2023-02-13 23:27:00 -08:00
Dav999-v
1d8494db8d Add initial version of font::print_wrap
graphics.PrintWrap is now also deprecated. An advantage of the new
version (with flags) is that it'll be possible to do things like put
a border around wrapped text, wrap text at larger scales, etc, but
these things don't work perfectly yet.

This commit also has some other fixes, like the default advance of
6 pixels for characters 0x00-0x1F in 8x8 fonts.
2023-02-13 23:27:00 -08:00
Dav999-v
0475539075 Implement first font::print function, fix most fading of colored glyphs
There has always been a mess of different print functions that all had
slightly different specifics and called each other:

Print(x, y, text, r, g, b, cen)
    nothing special here, just does what the arguments say

PrintAlpha(x, y, text, r, g, b, a, cen)
    just Print but with an alpha argument

PrintWrap(x, y, text, r, g, b, cen, linespacing, maxwidth)
    added for wordwrapping, heavily used now

bprint(x, y, text, r, g, b, cen)
    prints an outline, then just PrintAlpha

bprintalpha(x, y, text, r, g, b, a, cen)
    just bprint but with an alpha argument

bigprint(x, y, text, r, g, b, cen, sc)
    nothing special here, just does what the arguments say

bigbprint(x, y, text, r, g, b, cen, sc)
    prints an outline, then just bigprint

bigrprint(x, y, text, r, g, b, cen, sc)
    right-aligns text, unless cen is given in which case it just
    centers text like other functions already do?

bigbrprint(x, y, text, r, g, b, cen, sc)
    prints an outline, then just bigrprint

We need even more specifics with the new font system: we need to be
able to specify whether CJK characters should be vertically centered or
stick out on the top/bottom, and we sometimes need to pass in
brightness variables for colored glyphs. And text printing functions
now fit better in Font.cpp anyway. So there's now a big overhaul of
print functions: all these functions will be replaced by font::print
and font::print_wrap (the former of which now exists). These take flags
as their first argument, which can be 0 for a basic left-aligned print,
PR_CEN for centered text (set X to -1!!!) PR_BOR for a border (instead
of functions like bprint and bigbprint), PR_2X, PR_3X etc for scaling,
and these can be combined with |.

Some text, for example [Press ESC to return to editor], fades in/out
using the alpha value, which is passed to the print function. In some
other places (like Press ENTER to teleport, textboxes, trophy text...)
text can fade in or out by direct changes to the RGB values. This means
regular color-adjusted white text can change color, but colored button
glyphs can't, since there's no way to know in the print system what the
maximum RGB values of a specific textbox are supposed to be, so the
only thing it can do is draw the button glyphs at full brightness,
which looks bad. Therefore, you can now also pass in the brightness
value via the flags, with PR_COLORGLYPH_BRI(255).
2023-02-13 23:27:00 -08:00
Dav999-v
9879fb2809 Draw outlines for colored characters correctly
If we try to draw a colored character, and the color is fully black,
it means we're drawing the outline.
2023-02-13 23:27:00 -08:00
Dav999-v
4675238fa8 Switch font.txt over to font.fontmeta
Since there's now a new XML-based font metadata file format to obsolete
the .txt file with all the glyphs, this commit switches the built-in
font to that new format.
2023-02-13 23:27:00 -08:00
Dav999-v
794f081530 Start rewrite of font system
This is still a work in progress, but the existing font system has been
removed and replaced by a new one, in Font.cpp.

Design goals of the new font system include supporting colored button
glyphs, different fonts for different languages, and larger fonts than
8x8 for Chinese, Japanese and Korean, while being able to support their
30000+ characters without hiccups, slowdowns or high memory usage. And
to have more flexibility with fonts in general. Plus, Graphics.cpp was
long enough as-is, so it's good to have a dedicated file for font
storage.

The old font system worked with a std::vector<SDL_Surface*> to store
8x8 surfaces for each character, and a std::map<int,int> to store
mappings between codepoints and vector indexes.

The new system has a per-font collection of pages for every block of
0x1000 (4096) codepoints, that may be allocated as needed. A glyph on
a page contains the index of the glyph in the image (giving its
coordinates), the advance (how much the cursor should advance, so the
width of that glyph) and some flags which would be at least whether the
glyph exists and whether it is colored.

Most of the *new* features aren't implemented yet; it's currently
hardcoded to the regular 8x8 font.png, but it should be functionally
equivalent to the previous behavior. The only thing that doesn't really
work yet is level-specific font.png, but that'll be supported again
soon enough.

This commit also adds fontmeta (xml) support.

Since the fonts folder is mounted at graphics/, there are two main
options for recognizing non-font.png fonts: the font files have to be
prefixed with font (or font_) or some special file extension is
involved to signal what files are fonts. I always had a font.xml in
mind (so font_cn.xml, font_ja.xml, etc) but if there's ever gonna be
a need for further xml files inside the graphics folder, we have a
problem. So I named them .fontmeta instead.

A .fontmeta file looks somewhat like this:

<?xml version="1.0" encoding="UTF-8"?>
<font_metadata>
    <width>12</width>
    <height>12</height>
    <white_teeth>1</white_teeth>
    <chars>
        <range start="0x20" end="0x7E"/>
        <range start="0x80" end="0x80"/>
        <range start="0xA0" end="0xDF"/>
        <range start="0x250" end="0x2A8"/>
        <range start="0x2AD" end="0x2AD"/>
        <range start="0x2C7" end="0x2C7"/>
        <range start="0x2C9" end="0x2CB"/>
        ...
    </chars>
    <special>
        <range start="0x00" end="0x1F" advance="6"/>
        <range start="0x61" end="0x66" color="1"/>
        <range start="0x63" end="0x63" color="0"/>
    </special>
</font_metadata>

The <chars> tag can be used to specify characters instead of in a .txt.
The original idea was to just always use the existing .txt system for
specifying the font charset, and only use the XML for the other stuff
that the .txt doesn't cover. However, it's probably better to keep it
simple if possible - having to only have a .png and a .fontmeta seems
simpler than having the data spread out over three files. And a major
advantage: Chinese fonts can have about 30000 characters! It's more
efficient to be able to have a tag saying "now there's 20902 characters
starting at U+4E00" than to include them all in a text file and having
to UTF-8 decode every single one of them.

If a font.txt exists, it takes priority over the <chars> tag, and in
that case, there's no reason to include the <chars> tag in the XML.
But font.txt has to be in the same directory as font.png, otherwise it
is rejected. Same for font.fontmeta. If neither font.txt nor <chars>
exist, then the font is seen as a 2.2-and-below-style ASCII font.

In <special>: advance is the number of pixels the cursor advances after
drawing the character (so the width of the character, without affecting
the grid in the source image), color is whether the character should
have its original colors retained when printed (for button glyphs).

As for <white_teeth>:

The renderer PR has replaced draw-time whitening of sprites/etc
(using BlitSurfaceColoured) by load-time whitening of entire images
(using LoadImage with TEX_WHITE as an argument).

This means we have a problem: fonts have always had their glyphs
whitened at printing time, and since I'm adding support for colored
button glyphs, I changed it so glyphs would sometimes not be whitened.
But if we can't whiten at print time, then we'd need to whiten at load
time, and if we whiten the entire font, any colored glyphs will get
destroyed too. If you whiten the image selectively, well, we need more
code to target specific squares in the image, and it's kind of a waste
when you need to whiten 30000 12x12 Chinese characters when you're only
going to need a handful, but you don't know which ones.

The solution: Whitening fonts is useless if all the non-colored glyphs
are already white, so we don't need to do it anyway! However, any
existing fonts that have non-white glyphs (and I know of at least one
level like that) will still need to be whitened. So there is now a
font property <white_teeth> that can be specified in the fontmeta,
which indicates that the font is already pre-whitened. If not
specified, traditional whitening behavior will be used, and the font
cannot use colored glyphs.
2023-02-13 23:27:00 -08:00
Misa
b29f3e2fae Silence various warnings in builds removing content
The MAKEANDPLAY, NO_CUSTOM_LEVELS, and NO_EDITOR defines remove content
or features. However, they then raise several warnings because of some
cases, functions, or variables that end up not being used.

This silences them by using the UNUSED macro, or by adding a default
catch-all case if the define is defined (so unhandled cases will still
raise warnings in a build that doesn't have these defines).
2023-02-04 00:14:04 -08:00
Misa
f2089c954f Silence warnings about using long long in C++03
We get these warnings because of the typedefs for 64-bit integers in
PhysFS's header files. The compiler will treat them as extensions and
will still compile it fine but it does mean we aren't strictly standards
conforming. Which really isn't a problem anyway. Probably.
2023-02-04 00:13:02 -08:00
Misa
113fbb0fdb Add build type to -version
This adds the build type in brackets in `-version` output after e.g.
"VVVVVV v2.4". The build type is MAKEANDPLAY, NO_CUSTOM_LEVELS, or
NO_EDITOR (which are not necessarily mutually exclusive).

This is appended on to the end of the first line so as to not break
Ved's existing `-version` check which only checks if the beginning of
STDOUT is "VVVVVV" followed by any version number.
2023-02-02 13:10:25 -08:00
Misa
6665f4f8f6 Prioritize loading processed script names
This makes it so that whenever the game loads a script as directed by a
script command, it will first try to load the script from the processed
argument, and if that fails only then will it try to load the script
from the raw argument.

This fixes a regression reported by Dav999 in the custom level "Vungeon"
created by Dynaboom, where a script `ifflag`s to `aselectP1.1` even
though the actual script name is `aselectp1.1`. In 2.3, it would
lowercase `aselectP1.1` and load the script properly, but previous to
this commit it would try to load the script with a capital name and then
fail.
2023-01-31 20:09:44 -08:00
Misa
fbe613ce5c Abort and print error if window/renderer cannot be created
This aborts and prints the error from SDL_GetError() if
SDL_CreateWindow() or SDL_CreateRenderer() fails.

We abort because there's not much point in continuing if the window or
renderer can't be created. There might be a use case for running the
game in headless mode, but let's code that in explicitly if we ever want
it.
2023-01-28 23:44:07 -08:00
Misa
2525017990 Set minimum window size (to 320 x 240)
This sets the minimum window size (to 320 x 240), so that the window
cannot be resized to lower than that.

This is because there's no utility in having a game window smaller than
that, and it provides a bonus convenience of being able to resize the
game to exactly 320x240 without needing to be exactly precise about it.

This idea was suggested by Dav999.
2023-01-28 23:37:24 -08:00
Misa
fbc9b3ddd7 Use SDL_Point instead of rolling our own point struct
The `point` struct was a relic of ActionScript and was added because of
the Flash 'point' object. However, it seems like Simon Roth didn't
realize that SDL has its own point struct.

With this, `Maths.h` can be un-included from a couple headers, which
exposes the fact that `preloader.cpp` was relying on `Maths.h` being
transitively included from `Graphics.h`.
2023-01-28 23:32:14 -08:00
Misa
c7098f84e5 Fix alphabetical ordering of includes in preloader.cpp
Dav999 added `#include "Localization.h"` before `#include "KeyPoll.h"`,
when it should go after instead, because the letter L comes after the
letter K in the English alphabet.
2023-01-28 23:29:04 -08:00
AllyTally
d9859d4a98 Fix screenshake not clearing the gameplay cache 2023-01-28 17:48:15 -08:00
AllyTally
19b2a317f1 Move from surfaces to the SDL render system
Ever since VVVVVV was initially ported to C++ in 2.0, it has used surfaces from SDL. The downside is, that's all software rendering. This commit moves most things off of surfaces, and all into GPU, by using textures and SDL_Renderer.

Pixel-perfect collision has been kept by keeping a copy of sprites as surfaces. There's plans for pixel-perfect collision to use masks instead of reading pixel data directly, but that's out of scope for this commit.

- `graphics.reloadresources()` is now called later in `main`, because textures cannot be created without a renderer.

- This commit also removes a bunch of surface functions which are no longer needed.

- This also recaches target textures in certain places for d3d9.

- graphics.images was converted to a fixed-size array.

- fillbox and fillboxabs use SDL_RenderDrawRect instead of drawing an outline using four filled rectangles

- Update my name in the credits
2023-01-28 14:36:28 -08:00
AllyTally
556e3a110a Fix small bug with map being off in custom levels
The pixel border around the map fits to map size normally. However, when
the map is off, it's always the dimensions of the full size map, and the
border didn't reflect that, so if the custom minimap was off, and the
map wasn't the full size, it wouldn't fit correctly.

This bug was introduced in PR #898.
2023-01-26 12:23:23 -08:00
Misa
172e3a8985 Add branch name to window title in brackets
The branch name will be added to the window title if it is an interim
version, e.g. "VVVVVV [master]".

This makes it easier for developers to tell at a glance which build of
the game they're running.
2023-01-16 12:59:48 -08:00
Misa
19a83853b8 Default width and height settings to 640x480
While the window is initialized with 640x480, the screen settings
defaulted to 320x240, which is a tiny window. The screen settings take
priority over the initialized window, so people with no previous
settings file will get 320x240. This makes it so they get 640x480
instead.

The window is still initialized to 640x480 (constants used for clarity)
just in case there's some weirdness if it's initialized to a potentially
odd resolution from the user's settings file.
2023-01-16 12:45:40 -08:00
Misa
d2b6fb2d06 Add branch name to interim version information
This is useful for developers who may have multiple builds of the game
from various different branches and may easily forget which build of the
game is what.

This shows up in the bottom-right corner of the title screen and also
with the `-version` command-line option, and in the status message
printed when building the game.
2023-01-07 19:18:28 -08:00
Misa
f35618999f Allocate to the size of source surface instead
Otherwise it will slow down since the destination surface is usually
pretty big.
2023-01-05 12:23:34 -08:00
Misa
2e03f2b03d Re-add temporary allocation in BlitSurfaceTransform
Unfortunately there needs to be an intermediate surface for proper alpha
color blending to happen via SDL_BlitSurface. The exact SDL blending
logic seems complicated and unclear for me to implement at the moment,
and my attempts kind of failed, so this is just a stopgap measure to at
least get the game rendering how it was before I screwed it up.
2023-01-04 15:33:56 -08:00
Misa
91339c77db Refactor BlitSurfaceColoured and BlitSurfaceTinted to not allocate
This refactors them to not allocate a temporary surface by instead
simply drawing directly to the destination surface.

This means re-implementing the original semantics of SDL_BlitSurface in
them, which is the function signature they (and BlitSurfaceStandard)
were based off of. So now if src_rect or dest_rect are NULL, it
automatically uses a rect of the entirety of the corresponding surface,
and other things like that. And also some other optimizations like
no-opping if the alpha is 0 (because then nothing will be drawn), and
critical checks (not drawing if the destination pixel is out of bounds,
because then otherwise it would wrap around, or at least that's what it
did when I tested it).

This resulted in a bunch of code that would really suck to copy-paste
because then you'd have to remember to update the other copy, so I
refactored them further and put the common code into one function, while
separating the different code (the exact transformation they do) into
different functions that get passed in through function pointers.
2023-01-02 18:01:42 -08:00
Misa
e5d32c653b Rename tempBuffer to menuoffbuffer
In general, "temp" is a bad name because it could mean anything. In this
case the buffer isn't really temporary and it's only used for drawing
the menu with a certain offset, so I made it use a better name. But also
because I'm going to be adding temporary buffers so I don't want the
names to be confused.
2023-01-02 11:19:08 -08:00
Misa
0a51141720 Use named constants in Graphics::create_buffers
I'm gonna be doing work on these and I took the opportunity to clean up
the code style a bit.
2023-01-02 11:17:02 -08:00
Misa
38c3b4ab41 Move warpfcol, warpbcol, and warprect off of Graphics
These are all temporary variables only used when drawing the all-sides
warp background.

warpskip isn't though, it's a persistent variable.
2023-01-02 11:16:03 -08:00
Misa
b0ca322b3f Use SDL_GetRGBA and SDL_MapRGBA to read and draw pixels
Honestly not too sure why we ever wrote the mask handling logic
ourselves instead of using SDL functions. Hell, we even used SDL_MapRGB
for Graphics::getRGB before.
2023-01-02 10:44:35 -08:00
Misa
8dc088896f Axe Graphics::ct and Graphics::setcolreal
`ct` was used to be a variable that a color was temporarily stored in
before being passed to a draw function. But this is unnecessary and you
might as well just have a temporary of the color directly. I guess this
was the practice used because temporaries were apparently really bad in
Flash.

setcolreal() was added in 2.3 to do basically the same thing (set it
directly from entities' realcol attributes). But it's no longer needed.

Correspondingly, Graphics::setcol has been renamed to Graphics::getcol
and now returns an SDL_Color, and Graphics::huetilesetcol has been
renamed to Graphics::huetilegetcol for the same reason.

Some functions (notably Graphics::drawimagecol and
Graphics::drawhuetile) were relying on the `ct` to be implicitly set and
weren't ever having it passed in directly. They have been corrected
accordingly.
2023-01-01 20:16:08 -08:00
Misa
351a022ebd Use SDL_Color for colors instead of colourTransform
colourTransform is a struct with only one member, a Uint32. The issue
with `Uint32`s is that it requires a bunch of bit shifting logic to edit
the colors. The issue with bit shifting logic is that people have a
tendency to hardcode the shift amounts instead of using the shift amount
variables of the SDL_PixelFormat, which makes it annoying to change the
color masks of surfaces.

This commit fixes both issues by unhardcoding the bit shift amounts in
DrawPixel and ReadPixel, and by axing the `Uint32`s in favor of using
SDL_Color.

According to the SDL_PixelFormat documentation (
https://wiki.libsdl.org/SDL2/SDL_PixelFormat ), the logic to read and
draw to pixels from colors below 32-bit was just wrong. Specifically,
for 8-bit, there's a color palette used instead of some intrinsic color
information stored in the pixel itself. But we shouldn't need that logic
anyways because we don't use colors below 32-bit. So I axed that too.
2023-01-01 16:36:43 -08:00
Misa
f24265f0fb Fix up temporary variables being in topmost scope and bad style
This makes it so temporary variables have their scopes reduced (if
possible). I also didn't hesitate to fix style issues, such as their
names ("temp" is such a bad name), making them const if possible, and
any code it touched too.
2022-12-31 20:46:01 -08:00
Misa
5a6bc8bb9b De-duplicate room tile drawing code in editor
It previously duplicated the for-loop twice, once for tiles.png and
tiles2.png, which just made me sad. Now it doesn't do that.

Also it previously had an alternate tileset == 10 condition for
tiles.png, which didn't seem to do anything because there's no such
thing as tileset 10, and anyways it's useless in-game because when
playing in the actual game it won't draw tiles.png, so I removed it. I
don't know why it was there in the first place.

Since I removed the temp variable from the outer scope, the other usage
of it has to be updated.
2022-12-31 20:45:56 -08:00
Misa
e889a4a9b1 Make Catalan language files not executable
For some reason they all have their executable bits set. Presumably this
is because they were made on an NTFS system where every file is
executable (which doesn't sound secure at all but that's another story).
2022-12-31 20:23:04 -08:00
Dav999-v
2f770e9b5a Use setstate(n)/incstate()/setstatedelay(n) in localization changes
This mirrors PR #917.
2022-12-31 20:04:56 -08:00
Dav999-v
2b84384606 Replace all localization SDL_free with VVV_free
This mirrors a926ce9851 upstream, which
replaces all other SDL_free calls.
2022-12-31 20:04:56 -08:00
Dav999-v
7a52dc9586 Update Esperanto and Catalan translations 2022-12-31 20:04:56 -08:00
Dav999-v
3937a12a85 Add cutscene test menu
This allows translators to test all text boxes in the scripts. It
doesn't run the scripts themselves - it only shows the basic appearance
of each text box individually, so context may be lost but it's good to
have a way to see any text boxes that might otherwise not be easily
seen because they require specific circumstances to appear.
2022-12-31 20:04:56 -08:00
Dav999-v
0ed7717dd5 Add Catalan translation by Eduard Ereza Martínez
As Terry said on Discord, this translation is completed now, so it
sounds like it's ready to officially add it to the language folder!
2022-12-31 20:04:56 -08:00
Dav999-v
5eb20aa467 Make some minor changes in the Dutch translation
Did another complete proofread of all the non-roomnames (hadn't looked
through *everything* in a while), and it's just three little things
that I felt would be important enough to tweak.
2022-12-31 20:04:56 -08:00
Dav999-v
7aec2c2242 Remove "COPY TILES" strings
This feature was never implemented, so these strings are making
translators wonder where to find them so they can be tested.
2022-12-31 20:04:56 -08:00
Dav999-v
6a1ddad8f8 Add cases for intermission replay options, button fillers in editor
The strings "Vitellary"/"Vermilion"/"Verdigris"/"Victoria" now have two
cases to support changing them for the intermission replay menu options
(like "with Vitellary").

Also, the string "< and > keys change tool" is now "{button1} and
{button2} keys change tool", so it can be changed dynamically without
having to retranslate the string.
2022-12-31 20:04:56 -08:00
Reese Rivers
968e731178 Adjusted some more roomnames 2022-12-31 20:04:56 -08:00
Reese Rivers
1bcb1df5d2 Changed instances of "pasejo" to "pasaĵo" 2022-12-31 20:04:56 -08:00
Dav999-v
e441d0d312 Complete the Dutch room name translations
Overall, I'm more proud of the Dutch room names than I could've hoped
for.
2022-12-31 20:04:56 -08:00
Dav999-v
489963fdc6 Add line about string cases to README-translators.txt 2022-12-31 20:04:56 -08:00
Dav999-v
795bdf886b Add support for string cases in strings.xml (gendered Rescued/Missing)
I wanted to not complicate the system with different string cases (like
cgettext) if possible, and I have been able to keep the main strings a
simple English=Translation mapping thus far, but apparently strings
like "Rescued!" (which are one string in English), have to be
translated for the correct gender in some languages. So this was a good
time to add support for string cases anyway.

It's a number that can be given to a string to specify the specific
case it's used, to disambiguate identical English keys. In the case of
"Rescued!" and "Missing...", male versions of the string are case 1,
female versions are case 2, and Viridian being missing is case 3. Of
course, if a language doesn't need to use different variants, it can
simply fill in the same string for the different cases.

If any other string needs to switch to different cases: distinguish
them in the English strings.xml with the case="N" attribute (N=1 and
higher), sync language files from the translator menu (existing
translations for the uncased string will simply be copied to all cases)
and change loc::gettext("...") to loc::gettext_case("...", 1),
loc::gettext_case("...", 2), etc.
2022-12-31 20:04:56 -08:00
Dav999-v
d147206c54 Split localization README.txt in -translators and -programmers versions
With a single README.txt for both translators and maintainers, both
have to read through info that's not relevant for them, because
translators don't need to worry about the specifics of adding new
English strings and recompiling the game, and programmers don't need to
worry about the specifics of how to translate things. Now it's split
into README-translators.txt and README-programmers.txt.
2022-12-31 20:04:56 -08:00
Dav999-v
baf1e6d57f Update font to latest version
This includes the Catalan · and some other Latin-1 supplement
characters.
2022-12-31 20:04:56 -08:00
Dav999-v
88c8ad5a57 Add per-area untranslated roomname counters
This makes it easy from the "explore game" menu to see which levels
still have untranslated room names and how many.
2022-12-31 20:04:56 -08:00
Reese Rivers
46af7ecdfc Added missing strings for Esperanto 2022-12-31 20:04:56 -08:00
Dav999-v
80b9bcf0dd 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-12-31 20:04:56 -08:00
Dav999-v
34cb42a19f Add Ctrl+X for roomname translator mode
This is a single block of code so it was easy to split from the
foundation commit.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
df76145314 Make some hardcoded room names (and some special ones) translatable
This involves loc::gettext_roomname and loc::gettext_roomname_special.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
b548783df2 preloader.cpp: make loading screen translatable
This mainly adds loc::gettext calls and replaces SDL_snprintf by
VFormat for the percentage.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
56f812a7e9 main.cpp: make pause screen translatable
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
93a3c40c39 UtilityClass.cpp: make time formats and numbers translatable
This replaces SDL_snprintf by VFormat for the time strings, and makes
number_words get translated numbers.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
70a6cf407c Script.cpp: make whole file translatable (except for a Welcome Aboard)
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
3203f99938 Render.cpp: make remaining strings translatable
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
949f0fa2e2 Render.cpp: make maprender translatable
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
52e847d5c1 Render.cpp: make titlerender and gamecompleterender translatable
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
17c11f7e6c Render.cpp: make gamerender translatable
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
0c9b54f819 Render.cpp: make menurender translatable
This mainly adds loc::gettext calls for all the menu titles and
explanations. It also redesigns the time trial screen.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
45b564b4fb Graphics.cpp: make whole file translatable
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
e90cc86a3c Game.cpp: make remaining strings translatable except gethardestroom
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
e96a0344e7 Game.cpp: Make Game::updatestate translatable
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
713fc00034 Game.cpp: make menu options (in Game::createmenu) translatable
This mainly adds loc::gettext calls for menu option labels.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
1e45e263f5 FileSystemUtils.cpp: make translatable
setLevelDirError was changed from snprintf-style to VFormat, but it's
only used in that file so...

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
c142b3bb3b Entity.cpp: make all activity prompts translatable
This adds loc::gettext for all "Press {button} to explode" and friends,
and also changes the interact_prompt function in Render.cpp to expect
{button} instead of %s.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
2210863e73 Editor.cpp: make remaining strings translatable
The affected functions are:
- editormenuactionpress
- editorinput
- editorclass::switch_tileset
- editorclass::switch_tilecol
- editorclass::switch_enemy
- editorclass::switch_warpdir

This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
17f246912d Editor.cpp: make editorrender translatable
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
d09cab2a38 Editor.cpp: make editormenurender translatable
This mainly adds loc::gettext calls.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
1d46e17286 Add textcase(n) commands to scripts
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
1ead8885ba Use loc::toupper_ch instead of SDL_toupper in VFormat
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
7ffbf0b115 Use UTF-8 characters for names in credits instead of {|}~
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
ec611ffa9d Add localization "foundation" (many code changes)
This commit adds most of the code changes necessary for making the game
translatable, but does not yet "unhardcode" nearly all of the strings
(except in a few cases where it was hard to separate added
loc::gettexts from foundational code changes, or all the localization-
related menus which were also added by this commit.)

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
35d92e8e64 Add RoomnameTranslator.cpp/h (not compiled yet)
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
97d905e8bf Add LocalizationMaint.cpp/h (not compiled yet)
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
8e33815c97 Add LocalizationStorage.cpp/h (not compiled yet)
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
98742ed852 Add Localization.cpp/h (not compiled yet)
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
72f030fd8e Support underscore (_) in VFormat arg name
This is needed for the limits check in the translator menu.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
f8fd51fc95 Indicate what special roomnames are in the levels
This just adds booleans roomname_special to the level classes in
preparation for the localization system to use them.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
0ed2cb1bc0 Add Textbook
A relevant paragraph copied from the original commit history:

The idea is that we store all strings somewhere managed, and then the
hashmap only needs pointers to those strings. For storing strings, I
created a `Textbook` structure, which consists of one or more 50 KB
"pages" (allocated as needed) on which you can simply write strings in
both languages back-to-back with `textbook_store(textbook, text)` and
get pointers to each of them. (I was originally going to just use one
big buffer and realloc to double the size when filled up, but then the
hashmap would be full of dangling pointers...) When needed, like when
switching to a different language, an entire textbook can be freed at
once.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
b148950367 Add C-HashMap to CMakeLists.txt
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
19275326ce Add c-hashmap submodule (not compiled yet)
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
7cb6176898 Add Space Station font
This has a lot of characters that different languages need.

This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
679619e475 Add (2020) Spanish language files
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
77ac19e4f6 Add Esperanto language files
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
684ccb9ccd Add Dutch language files
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Dav999-v
925fc76d56 Add English language files and README
This commit is part of rewritten history of the localization branch.
The original (unsquashed) commit history can be found here:
https://github.com/Dav999-v/VVVVVV/tree/localization-orig
2022-12-31 20:04:56 -08:00
Misa
aa2cf3ab4b Use _SDL_HAS_BUILTIN
I think this is because if you both check that __has_builtin is defined
and use it in the same 'if' preprocessor statement, it can error because
there's no equivalent to short-circuiting in preprocessor statements.
_SDL_HAS_BUILTIN should be safer.
2022-12-29 16:13:54 -08:00
Misa
3758d61fe9 Axe scriptclass::resetgametomenu
This creates the game over screen for dying in No Death Mode. It's three
lines long and it's only called once. There's no reason it has to be a
separate function. From the name it sounds like it was meant to be a
generic function but it's anything but that. So just inline it in to
where it's called.
2022-12-29 15:51:16 -08:00
Misa
593641ce82 Always destroy and create new player in startgamemode
This fixes a bug where players could flip in mid-air at the start of
custom levels whose start points were in mid-air, because
onground/onroof were not being reset. This could also be done when
loading in to a save with a checkpoint in mid-air. All that's required
is to exit the game while standing on a surface (or otherwise having a
nonzero onground/onroof).

This reminded me that there were other variables on the player entity
persisting through that made loading in to a level not have a totally
clean slate, such as walkingframe (which could affect sequencing
individual TASes together into one TAS), so it's better to just destroy
the player entity and recreate it.

Except some attributes still have to be persisted for 2.2 and 2.0
glitchrunner mode. But it's better that this is done via a whitelist
rather than a blacklist.

The player duplicate removal code in hardreset is mostly redundant now
(except for some very unlikely corner cases), but there's nothing wrong
with having redundant code as long as it's not harmful. I had a
paragraph here justifying why it could be removed but decided it was
simpler to just keep it.
2022-12-29 15:51:16 -08:00
Misa
97deda2675 Add debug print to unlockAchievement
This print is useful to know if an achievement (one that's not already
unlocked) would actually be unlocked in an end user environment, while
running the game in a dev environment.

Also fixed up the style of the function because it was definitely
inconsistent with the surrounding code.
2022-12-29 15:51:14 -08:00
Misa
69e9a32e1b 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 14:01:36 -08:00
Misa
6768a33f2d Reset savex, savey, and savegc in hardreset
While refactoring scriptclass::startgamemode, I noticed that these
variables weren't being reset. Fortunately, this doesn't seem to have
affected anything because they get overwritten one way or another in
startgamemode. But it's good just to be defensive and reset them anyway.

They are not reset in 2.2 or 2.0 glitchrunner mode because dying during
exiting to the menu is needed for credits warp, and that means the
checkpoint position needs to be maintained through.
2022-12-29 12:21:18 -08:00
Misa
7a48d0a53e Add Unreachable.h
This is to indicate when a code path is absolutely, for certain, 100%
unreachable. Useful as the default case inside a case-switch that is for
sure 100% exhaustive because it's inside the case of another case-switch
(and the default case is there to suppress compiler warnings about the
case-switch not being exhaustive), which is a situation coming up in my
scriptclass::startgamemode refactor.

It does this by deliberately invoking undefined behavior, either using a
compiler builtin that does the same thing or being a noreturn function
that returns. (And undefined behavior is not undefined behavior if it is
not executed in a code path, otherwise all NULL checks would be useless
because it'd dereference something that could be NULL in another code
path.)
2022-12-29 12:10:25 -08:00
Misa
c177f456f4 Unexport getgridpoint
It's a method of entityclass even though it doesn't use anything on the
class and it's not used outside of Entity.cpp.
2022-12-11 12:40:45 -08:00
Misa
7347975cc5 Unexport yline
It's a method of entityclass, even though it doesn't use anything on the
class and it's not used outside of Entity.cpp.
2022-12-11 12:37:55 -08:00
Misa
52e29b68bc Unexport gridmatch
It's a method of entityclass, but it has no reason to be because it
doesn't use anything on entityclass and it's not used outside of
Entity.cpp.
2022-12-11 12:37:43 -08:00
Misa
ec520838df Retype testwallsy's tx and ty to be ints, not floats
I have no idea why they were floats in the first place. They are
coordinates, and coordinates are integer positions in this game. They
get converted to float only to be explicitly `static_cast`ed back to
ints in `testwallsy`.
2022-12-11 11:00:20 -08:00
Misa
12820473db Retype dr to be int instead of float
This variable passes along the rule of the entity, which is an int. No
idea why it was converted to a float. Thankfully this is only used for
an unused block type, so it doesn't really matter.
2022-12-11 10:55:25 -08:00
Misa
babcadc99f Remove unnecessary consts from Entity.h
`const`s don't mean anything in a prototype declaration unless it's a
pointer or a reference. Remove them to make the header a bit more
readable.
2022-12-11 10:52:12 -08:00
Ally
3e0954b9b8 Update desktop_version/src/Game.cpp
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2022-12-07 17:40:15 -08:00
Ally
85b54dac17 Fix accidental replacement 2022-12-07 17:40:15 -08:00
AllyTally
f659eca818 remove two-argument setstate 2022-12-07 17:40:15 -08:00
AllyTally
af3e5be09b Convert more statedelay assignments to function call 2022-12-07 17:40:15 -08:00
AllyTally
d6c38f8d5c Small fixes 2022-12-07 17:40:15 -08:00
Ally
e4d02feffa Apply suggestions from code review
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2022-12-07 17:40:15 -08:00
AllyTally
86604c5748 Also lock statedelay 2022-12-07 17:40:15 -08:00
AllyTally
4b66920865 state locking 2022-12-07 17:40:15 -08:00
Terry Cavanagh
01d2cdd42a Merge branch 'master' of https://github.com/TerryCavanagh/VVVVVV 2022-12-02 18:20:21 +01:00
Terry Cavanagh
72d018ea04 Update mobile version to mobile v2.2.1
The android version just got a much needed update to fix some resolution issues on devices with cutouts.

It turns out the mobile source was actually pretty out of date, like 3 versions out of date! This commit brings it up to date.

All the changes have just been about keeping the game running on modern devices, though. The biggest change was adding the Starling library to the project, which made the game GPU powered and sped the whole thing up.
2022-12-02 18:19:58 +01:00
Misa
43bebecf3b Make room names own their own memory
This makes it so room names are no longer pointers to someone else's
memory, and instead to set them you use `mapclass::setroomname`. If the
string is short enough to fit in a static, no-alloc buffer, then it gets
copied there. Otherwise, a new heap allocation is made that duplicates
the string, and the new pointer is used instead.

This makes it possible for room names to contain arbitrary data whose
origin is temporary (e.g. from a script command that could be added in
the future).
2022-11-30 22:53:36 -08:00
Misa
a926ce9851 Replace all free calls with VVV_free[func]
This replaces all calls to SDL_free with a new macro, VVV_free, that
nulls the pointer afterwards. This mitigates any use-after-frees and
also completely eliminates double-frees. The same is done for any
function to free specific objects such as SDL_FreeSurface, with the
VVV_freefunc macro.

No exceptions for any of these calls, even if the pointer is discarded
or zeroed afterwards anyway. Better safe than sorry.

This is a macro rather than a function that takes in a
pointer-to-pointer because such a function would have type issues that
require casting and that's just not safe.

Even though SDL_free and other SDL functions already check for NULL, the
macro has a NULL check for other functions that don't. For example,
FAudioVoice_DestroyVoice does not check for NULL.

FILESYSTEM_freeMemory has been axed in favor of VVV_free because it
functionally does the same thing except for `unsigned char*` only.
2022-11-30 22:50:08 -08:00
Misa
6e583d949b Don't set legends if out of bounds
Trinket and teleporter legends would be drawn even if they were out of
bounds. Trinket legends in particular were easy to do because you can
just place a trinket in a custom level and resize the map to not include
the room of the trinket.

Now, there are checks added so they won't be added if they are out of
bounds. This is in line with the fact that, since 2.3, if a trinket
exists outside of the map in custom levels, it won't count towards the
number of trinkets.
2022-11-30 13:42:10 -08:00
Misa
de38b6b55c Unify all queries to map size to map.getwidth and map.getheight
It's becoming pretty clear that the size of the map is important enough
to be queried a lot, but each time it's something like `map.custommode ?
map.customwidth : 20` and `map.custommode ? map.customheight : 20` which
is not ideal because of copy-pasting.

Furthermore, even `map.customwidth` and `map.customheight` are just
duplicates of `cl.mapwidth` and `cl.mapheight`, which are only set in
`customlevelclass::generatecustomminimap`. This is a bit annoying if you
want to, say, add checks that depend on the width and height of the
custom map in `mapclass::initcustommapdata`, but `map.customwidth` and
`map.customheight` are out of date because `generatecustomminimap`
hasn't been called yet. And doing the ternary there requires a `#ifndef
NO_CUSTOM_LEVELS` to reference `cl.mapwidth` and `cl.mapheight` which is
just awful.

So I'm axing `map.customwidth` and `map.customheight`, and I'm axing all
the ternaries that are duplicating the source of truth in
`MapRenderData`. Instead, there will just be one function to call for
the width and height, `mapclass::getwidth` and `mapclass::getheight`,
and everyone can simply call those without needing to do ternaries or
duplication.
2022-11-30 13:35:14 -08:00
Misa
d183ea6367 Clean up the code style of generatecustomminimap
The existing code was allergic to putting spaces between tokens, and had
some minor code duplication that I took the time to clean up as well.
The logic should be easier to follow now that the for-loops are no
longer duplicated for each of the map zoom levels.

I tested this by temporarily disabling map fog entirely and going
through a couple different custom levels to compare their minimap with
the existing code. These were A New Dimension and 333333 for 1x, Golden
Spiral and VVVV 4k for 2x, and VVVVVV is NP-Hard for 4x. There was no
difference in the output, not even a single pixel.
2022-11-30 13:25:28 -08:00
AllyTally
88142ea839 Fix legend positions 2022-11-30 12:33:56 -08:00
Ally
cbfef2eb53 Apply suggestions from code review
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2022-11-30 12:33:56 -08:00
AllyTally
c16fe04519 Deduplicate map render data calcuations 2022-11-30 12:33:56 -08:00
AllyTally
7cdfe6e9a3 Move generatecustomminimap to case 21 2022-11-30 12:33:56 -08:00
Ally
0bf1e1494b Apply suggestions from code review
Co-authored-by: Misa Elizabeth Kai <infoteddy@infoteddy.info>
2022-11-30 12:33:56 -08:00
AllyTally
f7bbf4670c Fix copy-paste error 2022-11-30 12:33:56 -08:00
AllyTally
1837fe8abf Remove accidental comment change 2022-11-30 12:33:56 -08:00
AllyTally
1cf8f1503a Generate the minimap when entering editor playtesting 2022-11-30 12:33:56 -08:00
AllyTally
ca506a7bb5 Fix a missing mapzoom multiplication 2022-11-30 12:33:56 -08:00
AllyTally
f2e2ae591a dedupe another piece of code 2022-11-30 12:33:56 -08:00
AllyTally
a5939a888a deduplicate a lot of map code 2022-11-30 12:33:56 -08:00
Misa
145355e603 Add fraZ0R to the list of contributors
fraZ0R's PR #915 was merged.
2022-11-30 11:22:50 -08:00
fraZ0R
63b8c71264 Actually fix #913 oops 2022-11-30 11:21:37 -08:00
AllyTally
ef6a2886e9 Fix #892 2022-11-25 13:10:16 -08:00
Misa
86d90a1296 Add color support to Windows console output, properly
This adds color support to the output of the console on Windows. Now if
you're using Windows 10 build 1511 or later (I think it's build 1511
anyway; they added more VT sequence support in later versions), you will
see colors by default. This isn't due to Windows helping in any way;
this commit has to specifically enable it with SetConsoleMode() because
by default, Windows won't enable color support unless we enable it. (Or
if it's enabled in the registry, but having to go through the registry
to enable basic shit like that is completely fucking stupid.)

I tested this in my Windows 10 virtual machine and it's completely
working.
2022-11-14 21:57:01 -08:00
Misa
40dd2571c2 Add error checks to freopen calls on Windows
I don't know if they could fail but it's still good to attempt to print
_something_ if they do end up failing.
2022-11-14 21:56:02 -08:00
Misa
0a181d8c3d Separate color being supported from being enabled
Previously, we were using `color_enabled` to mean both that the color
was supported and that it was enabled by the user (which it is enabled
by default). But this logic doesn't work well if the color check
function is called again and ends up enabling color after the user
disabled it. To fix this, just separate the two so the user controls one
`color_supported` variable and the `color_enabled` variable is separate.
Check both of them in order to print color, of course.
2022-11-14 21:51:53 -08:00
Misa
3fc23f2b2c Add -console option on Windows
This adds the `-console` command-line option (for Win32 only) so the
game can spawn an attached console window which will contain all console
output.

This is to make it easier for people to debug on Windows systems.
Otherwise, the only way to get console output would be to either compile
the application as a console app (i.e. switch the subsystem to console)
- which is undesirable for regular users as this makes it so a console
is always spawned even when unwanted - or launch the game with shell
arguments that make it so output is redirected to a file.

As a result, color checking support is factored out of vlog_init() into
its own function, even though we don't support colors on Windows.
2022-11-14 19:40:23 -08:00
Misa
5bb3768782 Fix indentation style of #defines in Vlogging.c
This makes it so the hash is always in the first column while the rest
of the line follows normal indentation rules.
2022-11-14 19:40:23 -08:00
Misa
9def8fd704 Seed with frame counter instead of SDL_GetTicks
Using SDL_GetTicks() to seed the Gravitron RNG caused many
reproducibility issues while syncing https://tasvideos.org/7575S . To
fix this, add a frame counter, which is a number that is incremented
every frame and never resets, and use it instead.

If someone needs to switch back to SDL_GetTicks() for old TASes, then
provide the -seed-use-sdl-getticks command-line option for them.
2022-11-14 14:10:24 -08:00
Misa
e6a3df6ca6 Move xoshiro_seed debug print to top
In its previous location, it would only print the value of `s` after it
had been mutated by `splitmix32` four times, and it doesn't get used
after that, so the print isn't very useful.

Mixing code and declarations here is fine because starting from a few
months ago, we compile with C99 and if we ever need to compile with C90
then it's trivial to add braces surrounding the declarations.
2022-11-14 13:14:25 -08:00
Misa
876362365b Ignore comments of tracks with a negative loop comment
If a music track has a loop comment with a negative value, ignore all
comments of the track. This is just to prevent any weirdness from
happening as it's safer to just let the track loop improperly. Also log
to the console to let users know.

This is the same thing that SDL_mixer does now:
libsdl-org/SDL_mixer@e819489459

This commit happened as a result of discussion on the VVVVVV Discord
server about SDL_mixer 2.0.4 behavior with weird loop comment values
(e.g. octal input with leading zeroes). This is simply updating the code
to be in line with what newer versions of SDL_mixer do.
2022-11-13 19:51:56 -08:00
Misa
fb13e652fa Check for SDL_strdup failure in parseComments
Just in case it happens. Comments aren't really important to the game
(at worst a track will just loop in the wrong place) so it's fine to
carry on here and ignore all comments if this happens.
2022-11-13 19:41:32 -08:00
Misa
27e04e9dbf Fix up style in parseComments
This does the following:

- Wrap lines that are too long (around 80 columns)
- Place the asterisk with the type instead of the variable name
2022-11-13 19:31:01 -08:00
Misa
6dd9200503 Fix up style in _Mix_ParseTime
This does the following:

- Const-qualify variables if they are not modified
- Place each statement onto their own separate lines
- Place the asterisk with the type instead of the variable name
- Combine declarations and initializations where possible
2022-11-13 19:11:50 -08:00
Dav999-v
2d6b2e685b Clarify submodules in desktop_version/README.md
VVVVVV uses submodules now, so you need to know how to initialize them.

I'm explicitly not including `git clone --recurse-submodules`. Usage of
submodules in git projects is kinda rare in my experience, so people
are used to doing simple clones, and that instruction would just result
in people being annoyed thinking they have to delete the repo they
already cloned, and clone it again except slightly differently.

It also doesn't help you if you need submodules that aren't in the
master branch (for example, if you clone my fork recursively and then
checkout the localization branch, you won't have C-HashMap and you'll
need the update command anyway). And you also need it whenever VVVVVV
updates its submodules. So teaching people just the update command is
better.
2022-10-16 09:56:34 -07:00
Misa
d69c88e447 Update FAudio to 22.09.01
You can blame Ethan for this.
2022-09-01 20:49:21 -07:00
Misa
6148c631d3 Update third-party submodules [2022-09-01]
This updates FAudio, LodePNG, and PhysFS. All other submodules do not
have updates.

The most notable is FAudio, which updates to SDL 2.24.0 with many fixes
that, as Ethan put it, you definitely want.
2022-09-01 17:48:29 -07:00
Misa
ac11b91540 Remove return values from vlog_* functions
They weren't ever being used, and nobody really ever uses the return
value from the printf family of functions anyway. They return how many
bytes were actually printed, but if it's less than you expected then
there's not much you can really do about them. Also the vlog_* functions
were computing them inaccurately because I only set the return value to
the return value of vprintf when there's other print functions being
called, but regardless there's no reason to have a return value here
anyway.
2022-08-29 10:48:21 -07:00
Misa
3bb3976e7e vlog_init: Fix brace style
Whoops.
2022-08-29 10:46:05 -07:00
Misa
3f9d30855f Define WIN32_LEAN_AND_MEAN when including windows.h
The hilariously-named WIN32_LEAN_AND_MEAN slims down the number of
header files included in the already-massive `windows.h`. I know people
say Moore's law and precompiled headers and all that (well, we don't use
precompiled headers), but they kinda forgot about virtual machines, and
there's no reason not to define this and slim down the number of headers
anyway.
2022-08-29 09:42:26 -07:00
Misa
aca1e8695c Remove attempt to autoenable color output on Win32
This started when I saw the warning that GetVersionExW was deprecated,
then looked it up and found StackOverflow answers saying that you should
basically detect the feature directly instead of checking the version,
which makes sense to me. Then I found that I could probably detect color
support by using GetConsoleMode and GetStdHandle. But then I asked
myself what the point was unless you could get color output directly in
the terminal, which it seems like you really can't if your app is a GUI
app. (I have no idea why Windows makes this pointless distinction
between console and GUI apps...)

I tested Command Prompt, PowerShell, Windows Terminal (which is just
PowerShell again), and even Git Bash (MINGW64), but none of them will
ever give the console output of a GUI app such as VVVVVV. The closest I
got is that Git Bash doesn't seem to detach the process, but it will
simply produce no output.

At this point I feel like it's not worth it keeping this code around if
it didn't even work in the first place, so I'm removing it. People can
always enable color by using the -forcecolor command-line argument
anyway.
2022-08-29 09:37:30 -07:00
Misa
e7e74bb8ab Add commit hash & date to -version flag output
This prints the commit hash and date in the same order as they are
printed on the title screen.
2022-08-23 00:00:39 -07:00
Misa
52124f7010 De-duplicate release version
I'm fine with putting the release version in a header file, thus
necessitating the need to recompile every file that includes it if it's
changed, simply because it's not supposed to be changed that often.

The SDL_arraysize is necessary because sometimes we'll have subreleases
(e.g. 2.4.1, 2.4.2, 2.4.3), and who knows, maybe we'll get to 2.10
someday.
2022-08-23 00:00:39 -07:00
Misa
4bf5e5e6a0 Optimize recompilation from changing commit hash
This reworks how the commit hash and date are compiled so that if
they're changed (and they're changed often), only one source file needs
to be recompiled in order to update it everywhere in the game, no matter
how many source files use the hash or date.

The commit hash and date are now declared in InterimVersion.h (and they
need `extern "C"` guards because otherwise it results in a link fail on
MSVC because MSVC is stupid).

To do this, what now happens is that upon every rebuild,
InterimVersion.in.c is processed to create InterimVersion.out.c, then
InterimVersion.out.c is compiled into its own static library that is
then linked with VVVVVV.

(Why didn't I just simply add it to the list of VVVVVV source files?
Well, doing it _now_ does nothing because at that point the horse is
already out of the barn, and the VVVVVV executable has already been
declared, so I can't modify its sources. And I can't do it before
either, because we depend on the VVVVVV executable existing to do the
interim version logic. I could probably work around this by cleverly
moving around lines, but that'd separate related logic from each other.)

And yes, the naming convention has changed. Not only did I rename
Version to InterimVersion (to clearly differentiate it from
ReleaseVersion, which I'll be adding later), I also named the files
InterimVersion.in.c and InterimVersion.out.c instead of
InterimVersion.c.in and InterimVersion.c.out. I needed to put the file
extension on the end because otherwise CMake wouldn't recognize what
kind of language it is, and I mean like yeah duh of course it doesn't,
my text editor doesn't recognize it either.
2022-08-23 00:00:38 -07:00
Misa
b4226631b9 Remove use of add_definitions
I thought all of these were removed earlier but apparently not. Anyways,
add_definitions is bad because it pollutes the definitions of every
single target, we should be using target_compile_definitions instead.
2022-08-23 00:00:38 -07:00
Misa
69a31731ee Update SDL to 2.24.0 in Windows CI
I missed this because to check for all instances of 2.0.22, I did `rg -F
'2.0.22'`. But ripgrep doesn't search in hidden directories by default,
so the actual command to run is `rg -F. '2.0.22'`.
2022-08-22 23:47:49 -07:00
Misa
16ad8c531e Update to SDL 2.24.0
This updates all references to SDL 2.0.22 to SDL 2.24.0, including the
Docker container that I maintain for Linux CI.

Be warned, this release of SDL updates the versioning scheme to be less
dumb. The previous version is 2.0.22, this release is 2.24.0 (so the
last number can be properly used for patch-level version releases).
2022-08-21 16:07:51 -07:00
Misa
cf4511f5d1 Add REMOVE_ABSOLUTE_PATHS CMake option
This option is enabled by default and will replace absolute paths of all
source directory file paths with relative paths in the compiled binary,
if the compiler supports it. Of course, this isn't needed if you compile
with all paths removed anyways (e.g. in Release mode).

The purpose is to help make builds reproducible and to remove any
potentially sensitive information about the user or the user's system
from the compiled binary.

Both Clang and GCC support -fdebug-prefix-map, -fmacro-prefix-map, and
-ffile-prefix-map. In particular, -ffile-prefix-map is just a flag that
does both -fdebug-prefix-map and -fmacro-prefix-map.

According to https://reproducible-builds.org/docs/build-path/ ,
-fdebug-prefix-map is available in all GCC versions but only available
starting from Clang 3.8, and -fmacro-prefix-map and -ffile-prefix-map
are available since GCC 8 and Clang 10. So we check the compiler version
and use the available flags depending on if the compiler supports it or
not.

This does make debugging a bit more annoying, but there are a couple
ways to rectify this. Either disable it with
-DREMOVE_ABSOLUTE_PATHS=OFF, or add a `.gdbinit` that consists of

    set substitute-path . ../..

so that `.` is considered to be `../..`. Of course, if you need to,
replace `../..` with the actual source directory path (in my case it's
`../../..` because I place my build folders in another subdirectory to
have multiple build folders in one directory).

This doesn't need to be a global `.gdbinit`, it can be in a
directory-specific `.gdbinit` (similar to how `.gitignore`s can also be
directory-specific). But then you need to add `add-auto-load-safe-path`
to your `.gdbinit` to load any directory-specific `.gdbinit`s.

The above is for GDB; I don't know what (if anything) needs to be done
for LLDB; I don't use LLDB.

Fixes #889.
2022-08-21 15:31:11 -07:00
Misa
c4301cf4ec Disable FAudio debug configuration in Release mode
Whereas all `SDL_assert`s will go away when compiling with optimization
flags and all plain `assert` calls (used in PhysFS) will go away when
compiling in Release mode, FAudio has a bunch of debug stuff that needs
to be explicitly disabled with its own `FAUDIO`-prefixed flag.

To do this in Release mode, we need to use generator expressions for
dumb CMake reasons. Basically, if checking the CMAKE_BUILD_TYPE variable
will not work for certain generators (Ninja, Visual Studio) because they
only specify the build type at build time, not generation/configuration
time.
2022-08-21 15:08:19 -07:00
Misa
32d41684ad Add FAudio to BUNDLE_DEPENDENCIES option description
When FAudio was added, we forgot to update the description of
BUNDLE_DEPENDENCIES.
2022-08-21 13:49:42 -07:00
Misa
712a319973 De-duplicate list of static libraries and flags applied to all libraries
This is so flags that apply globally (i.e. to the game and all static
libraries it's compiled with), such as /MT on MSVC, can be put in a
list, and along with putting all static libraries in a list, we remove
the need for each flag to be repeated for each static library and we can
just use a foreach loop instead.

(Global compile flags of course don't apply to us meddling with
CMAKE_C_FLAGS and CMAKE_CXX_FLAGS directly, because we need to do that
in order to make sure the C and C++ standards are set properly.)
2022-08-21 13:28:58 -07:00
Misa
67d350de05 Only process tapleft/tapright if has_control
This fixes a regression where the game ignored the amount of frames you
held down a direction if you released the direction during death.

Previously, the game only checked the amount of frames you held down a
direction if you were able to control the player. If you weren't able to
control the player (e.g. during the death animation), then the number of
frames it counted didn't change. This also meant that if you were
holding a direction before you died, but released it during death, the
game wouldn't zero out the number of frames you held it.

This behavior was useful because it meant you could keep the
deceleration momentum that you normally get by holding a direction for 5
frames just by holding a direction for less than 5 frames after dying,
if you had the rest of the hold frames before you died. This behavior is
what's used in https://tasvideos.org/7575S at around frame 7200.

Unfortunately, #609 made it so that the direction hold processing
happened even if the player didn't have control, meaning that it would
zero the hold frames during the death animation in the TAS, thus
desyncing it when it performed the maneuver it relied on the extra
momentum for after Viridian respawns.

The solution here is to just add the check back in again.

Fixes #887.
2022-08-05 07:38:35 -07:00
Misa
c3750e3b34 Fix special/stdin.vvvvvv being saved to levelstats.vvv
While fixing #885, I noticed that I had a bunch of
`special/stdin.vvvvvv` entries saved in my `levelstats.vvv`. At once I
knew that the dumb `special/stdin` hack that actually checks if the
filename passed is `special/stdin` was to blame.

STDIN playtesting was first merged, I knew in the back of my mind that
it was a bit of a dumb hack, but I didn't know it would cause
consequences like showing up in `levelstats.vvv`. For now, I'll just
have to patch it, but hopefully in the future I'll remove the dumb hack
entirely. Commenting both instances of the dumb hack with instructions
to grep for it should help maintainers out.
2022-08-03 17:30:23 -07:00
Misa
550dfe676a Clear level stats vector before loading them into the vector
Otherwise this results in what is basically a memory leak that can be
triggered repeatedly by selecting "play a level" over and over again.

Fixes #885.
2022-08-03 17:21:35 -07:00
Misa
424c8c80be Add debug statements to print xoshiro RNG values
This is useful to investigate any TAS desync/reproducibility issues
relating to RNG, because even though I specifically separated the
Gravitron RNG away from other RNG and made it not dependent on the
system libc rand() function, there's still apparently some differences
in RNG execution between systems, resulting in TASVideos submission 7575
( https://tasvideos.org/7575S ) not syncing for everyone except the
author.

It seems that SDL_GetTicks(), which is used to seed the xoshiro RNG, is
not reliably consistent between systems, so in the future I will
probably replace it with a counter that is incremented each frame
starting from game startup, which is probably better.
2022-07-28 16:55:29 -07:00
Misa
058fb9fff0 Fix warning: use of non-static data member initialization
This fixes the following warnings:

desktop_version/src/Music.cpp: At global scope:
desktop_version/src/Music.cpp:240:23: warning: non-static data member initializers only available with ‘-std=c++11’ or ‘-std=gnu++11’ [-Wc++11-extensions]
  240 |     Uint8 *wav_buffer = NULL;
      |                       ^
desktop_version/src/Music.cpp:414:32: warning: non-static data member initializers only available with ‘-std=c++11’ or ‘-std=gnu++11’ [-Wc++11-extensions]
  414 |     Uint8* decoded_buf_playing = NULL;
      |                                ^
desktop_version/src/Music.cpp:415:32: warning: non-static data member initializers only available with ‘-std=c++11’ or ‘-std=gnu++11’ [-Wc++11-extensions]
  415 |     Uint8* decoded_buf_reserve = NULL;
      |                                ^
desktop_version/src/Music.cpp:416:21: warning: non-static data member initializers only available with ‘-std=c++11’ or ‘-std=gnu++11’ [-Wc++11-extensions]
  416 |     Uint8* read_buf = NULL;
      |                     ^

These warnings are because the non-static data members (i.e. data
members that will be different for each instance of a class) are being
initialized at the same time as they're being declared, which means
that's what their value will be when the class instance is initialized.

However, this is only a C++11 feature, and we don't use C++11. Luckily,
we don't need to do this, and this is in fact redundant because we
already zero out the class instance in its constructor using
SDL_zerop(). Therefore, we should remove these initializers to fix
compliance with C++03.
2022-07-28 16:48:58 -07:00
Misa
8ca53fa5d2 Remove "Lots" and "???" from number_words
Instead, for any number that isn't in the list of number words, just
return the regular Arabic numerical representation (i.e. just convert it
to string). It's better than having "Lots" or "???", neither of which
really tell you what the number actually is.
2022-07-04 23:14:44 -07:00
Misa
5e25161a10 Add /MT flag for MSVC
This flag makes it so the MSVC runtime libraries are statically linked.
This avoids needing Windows users to have these libraries installed.

Apparently /MT stands for "MultiThreaded", and there's a bit of a
history there where originally by default you could only have a
single-threaded library, and then the multi-threaded flags were added in
later.

First I tried doing target_compile_options on VVVVVV, but then got a
linker error. Then I tried doing add_compile_options because I figured
/MT had to be applied everywhere, and it seemed to work, but it still
linked to the runtime libraries. Apparently it was being overridden.
Then I tried target_compile_options again but this time did it to
everything, and that linked correctly and also removed the runtime
dependency. I would've tried using the MSVC_RUNTIME_LIBRARY property
- along with the CMP0091 policy - but those were only introduced in
CMake 3.15.

You can verify that a binary is built without dependencies by installing
LLVM and running llvm-readobj --needed-libs path/to/binary. This is the
output for a binary with runtime dependencies:

    infoteddy@fedorarune  ~/d  llvm-readobj --needed-libs VVVVVV.exe

    File: VVVVVV.exe
    Format: COFF-i386
    Arch: i386
    AddressSize: 32bit
    NeededLibraries [
      ADVAPI32.dll
      KERNEL32.dll
      MSVCP140.dll
      SDL2.dll
      SHELL32.dll
      USER32.dll
      VCRUNTIME140.dll
      api-ms-win-crt-heap-l1-1-0.dll
      api-ms-win-crt-locale-l1-1-0.dll
      api-ms-win-crt-math-l1-1-0.dll
      api-ms-win-crt-runtime-l1-1-0.dll
      api-ms-win-crt-stdio-l1-1-0.dll
      api-ms-win-crt-string-l1-1-0.dll
      api-ms-win-crt-time-l1-1-0.dll
      api-ms-win-crt-utility-l1-1-0.dll
    ]

And this is the output for a binary with those dependencies having been
statically-linked in:

     infoteddy@fedorarune  ~/d  llvm-readobj --needed-libs VVVVVV.exe

    File: VVVVVV.exe
    Format: COFF-i386
    Arch: i386
    AddressSize: 32bit
    NeededLibraries [
      ADVAPI32.dll
      KERNEL32.dll
      SDL2.dll
      SHELL32.dll
      USER32.dll
    ]
2022-06-28 17:49:03 -07:00
Dav999-v
10acd67377 Add support for start position via level XML for CLI playtesting
As already described in cc61194bed, as
well as Ved's commits from the last almost two weeks, starting VVVVVV
from Ved for playtesting could be made a lot faster by "preloading" the
game - letting it do all its asset loading in the background without
creating a window - and then waiting until the level is passed in via
stdin. There's only one problem left with this approach: VVVVVV
currently expects the starting position to be passed via command line
arguments, which isn't known yet at the time we'd like to start VVVVVV.
Therefore, this commit allows passing the starting position via the
level XML, instead of via arguments.

The extra XML looks like this, and is added next to the <Data> tag:

    <Playtest>
        <playx>214</playx>
        <playy>112</playy>
        <playrx>100</playrx>
        <playry>100</playry>
        <playgc>0</playgc>
        <playmusic>4</playmusic>
    </Playtest>

This is handled similarly to how the equivalent arguments are handled:
when the level metadata is loaded for CLI playtesting, we also try to
find this tag, and if it exists, it sets the same variables that the
arguments would have otherwise set.
2022-06-19 15:21:36 -07:00
Misa
dca3c9600c Explicitly initialize SoundTrack::volume
While this is not needed because it's a static variable, it doesn't hurt
to be explicit, especially if it's going to be refactored in the future.
2022-06-10 11:17:38 -07:00
N00byKing
c077500d16 Fix semi random volume jumps 2022-06-10 11:16:45 -07:00
Misa
f0aa1a8cae Don't treat spikes as solid for non-humanoid entities
There's always been a bit of an inconsistency in the game where enabling
invincibility would make spikes so solid that enemies and moving
platforms would treat spikes as solid and bounces off of them.

This fixes that by adding an `invincible` parameter to collision
functions, so the functions will only treat spikes as solid if that
parameter is true, and the parameter passed will only be true if it's
called for an entity that is a humanoid and invincibility mode is
enabled.

Also, for clarity, `spikecollide` is renamed to `towerspikecollide`
because it's only used for tower spikes. And as a small optimization,
`checktowerspikes` returns early if invincibility mode is enabled.
2022-06-05 20:21:51 -07:00
Misa
cc61194bed Move SDL_ShowWindow to after assets are loaded
This is a minor optimization to streamline the experience of Ved
playtesting. Previously, the user would have to wait for all the assets
to load when launching playtesting (most of the time, I suspect, is
taken up by loading music from a vvvvvvmusic blob). With this
optimization, however, the game can be launched in the background and
its assets can be loaded, while it blocks on STDIN input. During this
time, the user in Ved will be choosing where to start playtesting. After
Ved provides STDIN input, then the window will be created and appears
instantaneously.

This also fixes a related issue in which providing an invalid
playtesting level name would result in a brief window flash that gets
instantly destroyed. With this, if the level is invalid then no window
is ever shown at all.
2022-06-01 16:42:22 -07:00
Misa
a46c49c89a Add -version command-line argument
Probably should have done this earlier in 2.3, but better late than
never.

This makes it easier for third-party programs like Ved to detect what
version of the game this is.

Slightly quick-n-dirty for now, I'll de-duplicate the version number
later, and add commit hash and date if applicable.
2022-05-23 14:34:36 -07:00
Misa
f6ede079fb Update FAudio, PhysFS, and TinyXML2 submodules
Doesn't hurt to keep things up to date. (The other submodules, UTF-CPP
and LodePNG, haven't been updated since then.) In particular, PhysFS has
worked around a bug with Windows Explorer, so people should no longer
have issues modifying their data.zip with Explorer and then being unable
to have assets in their game (as reported in icculus/physfs#24 ).
2022-05-20 22:39:58 -07:00
Misa
e77fad5db8 Fix potential NULL dereference of images[t]
Without this, entering in-game and opening the map with missing graphics
will result in a segfault. This is because even if the image doesn't
exist, it's still pushed into the `images` std::vector as a NULL
pointer. And it segfaults because we dereference it (to get things like
their width and height). In contrast, passing them to SDL is fine
because SDL always checks for NULL first.
2022-05-17 12:07:51 -07:00
Misa
a23a4cbbd0 Improve vlog statements when PHYSFS_openRead fails
There are three different places where we call PHYSFS_openRead. This
commit makes sure all of them print a statement upon failure along with
the PhysFS reason for failure, and assigns the log level of each print
as so:

- FILESYSTEM_loadFileToMemory: Debug print (previously no
  print existed in the first place), because some files (such as
  font.txt) may or may not be needed, but if it isn't then no need to
  print and worry the user. The game will error anyway if a critical
  file like a graphics file is missing.

- FILESYSTEM_loadBinaryBlob: Debug print (previously info print),
  because sometimes it's not needed, such as mmmmmm.vvv. I remember one
  user being worried that the game printed "Unable to open file
  mmmmmm.vvv" when it's not critical unlike vvvvvvmusic.vvv (and that
  file is assumed to exist if data.zip exists anyways). Though maybe we
  should move to loose-leaf files to save on memory usage (and so we
  don't have to use special tools to modify a binary blob)...

- FILESYSTEM_loadZip: Error print. If we're calling this function, we
  really do expect the zip to be loaded, and if it fails because we
  can't open the file in the first place, then it would be good to know
  why.
2022-05-17 11:52:45 -07:00
Dav999-v
ea4302b41e Implement new string formatting system (VFormat)
This commit adds a new string formatting system to replace uses of
`SDL_snprintf` and string concatenation.

Making our own string formatting system has been briefly discussed
during the review of the localization branch, and on the VVVVVV
Discord. It's inspired by Python's format strings, but simpler.

This is primarily to benefit localization - strings will be easier to
understand (`Now using %s Tileset` → `Now using {area} Tileset`,
`"%s remain"` → `"{n_crewmates|wordy} remain"`), translators can change
the word order for their language's grammar (`%1$s` is a POSIX
extension), and this system is also less error-prone (making the format
string not align with the actual arguments won't result in a crash or
UB).

It also integrates our needs better - particularly the "wordy" numbers
without having to have a `help.number_words(n).c_str()` at the
callsite, translators can opt in and out of wordy numbers per string,
and this should also make it easier to solve #859.

This commit adds the formatting system itself, and changes one
`SDL_snprintf` in the code to use it as a small demo (the rest should
probably be done in the localization branch to avoid more unneeded
work).

The system is described in full detail in VFormat.h and in the pull
request description.
2022-05-06 00:19:30 -07:00
Misa
eb46143098 Update SDL version to 2.0.22
2.0.22 just released 40 minutes ago.

This also updates the `Dockerfile` to use the URL from the GitHub
releases page, instead of SDL's servers.

I've also pushed a new Docker container to
`ghcr.io/infoteddy/vvvvvv-build`.
2022-04-25 13:09:57 -07:00
Misa
8bece4f6aa Add a missing break;
Whoops. Now I wonder why the compile didn't fail for me locally, even
though I *should* have -Werror=implicit-fallthrough enabled...
2022-04-25 01:21:43 -07:00
Misa
98cb415675 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 00:57:47 -07:00
Misa
af1cebf7a1 Unify drawing room name on map menus into one function
Previously, it was copy-pasted and slightly different, when really, they
ought to both be the exact same code.

It kind of pains me that the room name, glitch name, and hidden name
don't own their own memory, but, that's to be addressed later.

What's a bit annoying is that the `temp` variable used in
`teleporterrender` also ends up being reused later in the function. In
this case, I opted to just redeclare them when they are used anyway, to
make it clearer.

Apart from `teleporterrender` no longer calling `map.area` or caring
about `map.custommode`, it also no longer cares about
`graphics.fademode` being 0. I could never actually get this condition
to be false in practice, and I have absolutely no idea why it's there.
I'm guessing it could be some weird edge case rendering issue if the
screen is fully black? But I wouldn't know how to trigger that, and
anyway it should probably be fixed elsewhere. So I'm just going to
remove that conditional.
2022-04-25 00:53:13 -07:00
N00byKing
e16c1557fa Fix music changes between areas 2022-04-10 14:17:46 -07:00
Misa
b8553107ff Fix a rare chance that finalstretch displays glitchy cycle (color 7)
It's quite rare, though possible, that during finalstretch you could see
a glitchy tileset that looked like this:

https://i.imgur.com/V7cYKDW.png

This happened because final_mapcol, the variable that controls which
color of finalstretch is rendered, could end up being 7. Normally, it's
in the range of 1..6, which perfectly correlates with the Warp Zone
tilesets in tiles2.png, and the higher the number the farther back in
the tileset it goes from the gray Warp Zone tileset. However, if it's 7,
then it'll start grabbing tiles from the Ship plus some unused blank
tiles, which does not look pretty in the slightest.

This happened because it's possible, though exceedingly unlikely, that
fRandom(), a function which returns a float between 0..1, could return
exactly 1. fRandom() calls rand(), which returns a result between 0 and
RAND_MAX, and divides it by RAND_MAX. This value is implementation
dependent, but required to be at least 32767, and on most systems is
2147483647. Even taking the value of 32767, that means there's a 0.003%
chance that you could get this glitchy tileset when the game cycled the
color in finalstretch. But of course, playing the game for long periods
of time will eventually increase this chance - cycling the color 1,000
times (around 17 minutes of playing) will result in the chance being 3%.

Then as the calculations in the finalstretch color cycling logic calls
fRandom(), then multiplies by 6 and adds 1, it was possible for
fRandom() to return exactly 1, then have
6 added to it, resulting in final_mapcol being 7.

To fix this, just decrement the multiplication by fRandom() to multiply
by 5 instead of 6. Now the only possible numbers that calculation can
produce would be 1..6.
2022-04-09 17:31:17 -07:00
Misa
e47781f92f SoundTrack::Init: Remove unused audio_channels arg
Missed this when reviewing the FAudio PR, so I'll just remove it now.
2022-03-31 11:13:57 -07:00
Misa
828aca2c8e Load zips using real dir instead of filename
This fixes a limitation where the level filename had to be the exact
same name as the name of the zip, because the game used the name of the
level to identify the zip of which to load assets, and this also made it
impossible to use assets for more than one level in a zip.

Instead, we just look up where the level came from, so we can always
load its assets regardless of its filename.

Additionally, the zip structure checks can go away too, simplifying the
code further.
2022-03-30 13:12:56 -07:00
Misa
7b53c1289d Remove .data.zip assets
This WOULD be a huge breaking change, if it weren't for the fact that no
one uses them. Which is why I'm removing them, to simplify the code.

I asked on the VVVVVV Discord whether anyone used them or was even aware
of them and basically the answer was no. I go on Distractionware and no
one uses them. And why would they, when they'd have to distribute the
level .vvvvvv file separately? Better to just distribute everything in
one zip. And it's quite a bit obscure that you have to suffix the file
with .data.zip anyway.
2022-03-30 13:12:56 -07:00
Ethan Lee
f88ed0dc1b Remove SDL2_mixer dependencies from Dockerfile 2022-03-29 02:27:58 -04:00
Ethan Lee
8980b2e546 Remove SDL2_mixer line from fixupMac.sh 2022-03-29 02:27:15 -04:00
Misa
400763b7d7 CI: Use new container without SDL_mixer
Now that SDL_mixer is gone, I've built a new container without it. Just
to get rid of unused files in the CI system.
2022-03-24 17:52:09 -07:00
Ethan Lee
b3f645d84c
README: Minor adjustments to dependencies text 2022-03-24 19:28:03 -04:00
N00byKing
f877eb3b56 Port to FAudio 2022-03-24 16:19:29 -07:00
Misa
a8feba029f Clean up and harden music loading code
During review of #869, I looked at this part of the codebase again. I
have no idea how or why, but during the course of 2.4 this whole area
just became a mess.

The issues I fixed (in no particular order):
- Copy-pasting the code that loads from the binary blobs
- Making sure SDL_RWFromConstMem is used over SDL_RWFromMem wherever
  possible
- Adding checks to make sure the index from the binary blob is valid
  (it's possible it could not exist)
- Adding checks to make sure we gracefully handle
  SDL_RWFromConstMem/PHYSFSRWOPS_openRead returning NULL
- Moving the pointer asterisk to the type instead of the name :)
2022-03-24 09:38:47 -07:00
Dav999-v
c61c4fab6f Fix C/C++ standards being unset for VVVVVV target if CMake is >= 3.1.3
So, it turns out we weren't quite done fighting CMake yet...

To accommodate #869 (and actually also #272), the C standard was raised
from C90 to C99. This turned out to require a bit of a fight with the
CentOS CI's CMake version to get it to set the flags we wanted (and to
not overwrite them later). Eventually the fix was to move the block
that sets the standards to later in the file, which was done in
24353a54bb.

As it apparently turns out, if your CMake is at least 3.1.3 and
`CMAKE_<LANG>_STANDARD` is used instead of the workaround, the standard
setting now has an effect on the third party libraries, but not on
VVVVVV itself. The cause is (probably) the phrase "if it is set when a
target is created" in the CMake documentation - the
`CMAKE_<LANG>_STANDARD` values have to come before the VVVVVV target is
defined. In other words, the compiler's default C/C++ standard will be
used, probably something like C17 and C++17. As I can confirm with
`__cplusplus` and `__STDC_VERSION__` with my recent-enough CMake. If I
force the pre-3.1.3 workaround to be used, everything is compiled with
C99/C++98 as expected; and the `-fno-exceptions` `-fno-rtti` flags
appear everywhere regardless of version.

So my fix is to make the CMakeLists a little less complex by
simplifying away the `CMAKE_<LANG>_STANDARD` and
`CMAKE_<LANG>_EXTENSIONS`, and always using the workaround regardless
of CMake version. There's nothing wrong with the workaround, the same
thing is also done for `-fno-exceptions` `-fno-rtti`, and it's good to
have a less complicated CMakeLists that doesn't do different and
unexpected things for different versions.
2022-03-22 13:03:55 -07:00
Misa
24353a54bb Move -std= flags to before -fno-rtti/-fno-exceptions
This fixes the issue where the `-std=` flags keep getting cleared,
apparently.
2022-03-22 07:26:41 -07:00
Misa
705864a32a Up the standard to C99
The previous commit f6d7a214f8 ended up
breaking CI because the workaround ended up breaking the PhysFS build
too, which was previously relying on extensions to compile.

Since #869 is going to require C99 anyways, I might as well just up the
standard now. That way the PR won't have to fight it too.
2022-03-21 20:27:15 -07:00
Misa
f6d7a214f8 CMake: Add workaround for setting -std= below 3.1.3
Previously, if the user had a CMake version below 3.1.3, we told them to
set `-std` themselves.

However, we are going to go to C99 soon (because of FAudio, see #869),
and CentOS 7's CMake is too old to set `-std=` automatically, defaulting
to C90. This is bad because it fails the CI.

To work around this, we set `-std=` ourselves, but first we have to
clear any existing `-std=` flag in C_FLAGS or CXX_FLAGS. Amusingly
enough, MSVC does not have `/std:` switches for either C90 or C++98, so
we just get to do nothing.
2022-03-21 20:13:08 -07:00
Misa
226b5610b0 CMake: Don't use regex if unneeded
If it's a straight find-and-replace with no regex, then don't say
`REGEX`.
2022-03-21 20:13:03 -07:00
Misa
84279354e5 cleanup: Don't savestatsandsettings if filesystem not init
This isn't necessary, but it does silence these annoying logs if you
pass an invalid argument or don't have data.zip:

    [ERROR] Could not get window size: Invalid renderer
    [WARN] Stats not loaded! Not writing unlock.vvv.
    [ERROR] Could not get window size: Invalid renderer
    [WARN] Settings not loaded! Not writing settings.vvv.

To do this, I've added FILESYSTEM_isInit().
2022-03-14 10:45:19 -07:00
Misa
a168f73a67 Tell CI to clone submodules
Otherwise it will not find the source files, as they wouldn't exist if
they're not cloned.
2022-03-13 23:50:37 -07:00
Misa
3dd40c48cf Migrate UTF-CPP to submodule
We are no longer copy-pasting UTF-CPP source files directly.
2022-03-13 23:50:37 -07:00
Misa
c39857fbc6 Migrate TinyXML2 to submodule
We are no longer copy-pasting TinyXML2 source files directly.
2022-03-13 23:50:37 -07:00
Misa
7a4dff2d75 Migrate PhysFS to submodule
This means we are no longer copy-pasting PhysFS source files directly.

Since the source files reside in a src/ subdirectory, the paths in the
CMakeLists.txt have to be adjusted.
2022-03-13 23:50:37 -07:00
Misa
7a0d3046a5 Migrate LodePNG to submodule
We are no longer copy-pasting LodePNG source files directly.

As we can't rename lodepng.cpp to lodepng.c in the submodule itself, we
need to make a wrapper file, lodepng_wrapper.c, that #includes
lodepng.cpp, but gets compiled as C.
2022-03-13 23:50:37 -07:00
Misa
5bd7dce075 Prevent writing stats/settings if they're not loaded
This prevents writing to unlock.vvv or settings.vvv if the game hasn't
made an attempt to load them yet. Otherwise, if the game aborted via
VVV_exit() because of, say, failure to parse a graphics file, it would
overwrite perfectly existing valid save data since it hasn't loaded it
yet.

Fixes #870.
2022-03-13 22:40:59 -07:00
Misa
75ee657612 Explicitly prevent writing to saves if filesystem is not init
Another cause of #870 is d0ffafe117, as a
bisect tells me. What that commit did is remove screenbuffer as a
pointer, since it's a statically-allocated object that _should_ always
exist, and it removed the `screenbuffer == NULL` guards in savestats()
and savesettings(). Unfortunately, those guards did something very
important - namely, they prevented writing to the save files when the
filesystem wasn't initialized. But that wasn't made clear, because it
seemed like the point of those guards was to prevent dereferencing NULL.

So instead, explicitly make it clear that
FILESYSTEM_saveTiXml2Document() needs to fail if the filesystem isn't
initialized. I've done this by adding an isInit bool to
FileSystemUtils.cpp.
2022-03-12 16:50:32 -08:00
Misa
997363ce56 GetWindowSize: Initialize out values if GetRendererOutput fails
Issue #870 showed one of the problems that this game has, namely that it
only sometimes checks SDL return values, and did not do so in this case.
Part of the cause of #870 is that Screen::GetWindowSize does not check
the return value of SDL_GetRendererOutputSize, so when that function
fails (as in the case where m_renderer is NULL and does not exist), it
does not initialize the out values, so it ends up writing uninitialized
values to the save files.

We need to make sure every function's return value is checked, not just
SDL functions, but that will have to be done later.
2022-03-12 16:49:55 -08:00
Misa
726b149fbb Refactor Screen.cpp to use named constants
No more hardcoded 320s and 240s here.
2022-03-12 16:46:58 -08:00
Misa
6fffa5c11d Make basePath and pathSep global variables
While reviewing #272, I noticed that the PR was passing these two
arguments through a helper function, even though they really shouldn't
ever change. To obviate the need to pass these through, I'm making them
global variables.

pathSep is just a string literal from PhysFS, while basePath is a whole
complicated calculation from SDL and needs to be freed. It will be freed
upon filesystem deinit (as is done with PhysFS and the STDIN buffer).

Additionally the logic in FILESYSTEM_init is simplified by no longer
needing to keep a retval variable or use gotos to free basePath in
there.
2022-03-09 11:55:38 -08:00
Yussur Mustafa Oraji
0622035424 Update VS2019 to VS2022
Required for Actions workflow due to windows-latest Version Change
2022-03-08 07:16:46 -08:00
Misa
9c698c084e Add Yussur Mustafa Oraji (N00byKing) to contributors list
Their PR #865 just got merged, so add them to CONTRIBUTORS.txt and
Credits.h.
2022-02-14 12:34:07 -08:00
N00byKing
adf2e5e3bd Build out-of-tree in workflows 2022-02-14 12:32:59 -08:00
Misa
f3797ff866 Allow spaces and capitals in script names when loading
This lets any script name use capitals and spaces all they want, while
still being able to jump to them via iftrinkets() or similar.

The issue is that whenever tokenize() is ran, all spaces are stripped
and every argument is lowercased before being put into `words`. So, the
solution here is to create a raw_words array that doesn't perform space
stripping or lowercasing, and to refer to that whenever there's a script
command that loads a script. We keep the lowercasing and space removal
elsewhere to be more forgiving to newcomers.

This is technically a forwards compatibility break, but it's only a
minor one, and all levels that utilize it can still be easily modified
to work on older versions anyway.
2022-02-12 14:56:27 -08:00
Misa
e93d8989d3 Revert "Fix Secret Lab Time Trial trophies having wrong colors"
As reported by Dav999, Victoria and Vermilion's trophy colors are
swapped again in 2.4. He points to
37b7615b71, the commit where I fixed the
color masks of every single surface to always be RGB or RGBA.

It sounded plausible to me, because it did have to do with colors, after
all. However, it didn't make sense to me, because I was like, I didn't
touch the trophy colors at all after I originally fixed them.

After I ruled out the RGBf() function as a confounder, I decided to see
whether intentionally reversing the color order in RGBf() to be BGR
would do anything, and to my surprise it actually swapped the colors
back around and it didn't actually look bad.

And then I realized: Swapping the trophy colors between RGB and BGR
ordering results in similar colors that still look good, but are simply
wrong, but not so wrong that they take on a color that no crewmate uses,
so it'd appear as if the crewmates were swapped, when in reality the
only thing that was swapped was actually the color order of the colors.

Trying to fix this by swapping the colors again, I actively confused
colors 33 and 35 (Vermilion and Victoria) with colors 32 and 34
(Vitellary and Viridian), so I was confused when Vermilion and Victoria
weren't swapping. Then as a debugging step, I only changed 34 to 32
without swapping 32 as well, and then finally noticed that I was
swapping Vitellary and Viridian, because there were now two Vitellarys.
And then I was reminded that Vitellary and Viridian were also wrongly
swapped since 2.0 as well.

And so then I finally realized: The original comments accompanying the
colors were correct after all. The only problem was that they were fed
into a function, RGBf(), that read the colors backwards, because the
codebase habitually changed the color order on a whim and it was really
hard to reason out which color order should be used at a given time, so
it ended up reading RGB colors as BGR, while it looked like it was
passing them through as-is.

So what happened was that in the first place, RGBf() was swapping RGB to
BGR. Then I came and swapped Vermilion and Victoria, and Vitellary and
Viridian around. Then later I fixed all the color masks, so RGBf()
stopped swapping RGB and BGR around. But then this ended up swapping the
colors of Vermilion and Victoria, and Vitellary and Viridian once again!

Therefore, swapping Vermilion and Victoria, and Vitellary and Viridian
was incorrect. Or at least, not the fix to the root cause. The root
cause would be to swap the colors in RGBf(), but this would be sort of
confusing to reason about - at least if I didn't bother to just type the
RGB values into an image editor. But that doesn't fix the real issue,
which is that the game kept swapping RGB and BGR around in every corner
of the codebase.

I further confirmed that there was no more RGB or BGR swapping by
deleting the plus-one-divide-by-three transformation in RGBf() and
seeing if the colors looked okay. Now with the colors being brighter, I
could see that passing it straight through looked fine, but
intentionally reversing it to be BGR resulted in colors that at a
distance looked okay, but were either washed out or too bright. At least
finally I could use my 8 years of playing this game for something.

So in conclusion, actually, 37b7615b71
("Fix surface color masks") was the real fix, and
d271907f8c ("Fix Secret Lab Time Trial
trophies having wrong colors") was the real regression. It's just that
the regression came first, but it wasn't really a regression until I did
the other fix, so the fix isn't the regression, the regression is...
this is hurting my brain. Or the real regression was the friends we made
along the way, or something like that.

This is the most trivial bug ever caused by the technical debt of those
god-awful reversed color masks.

---

This reverts commit d271907f8c.

Fixes #862.
2022-02-12 00:41:02 -08:00
Misa
23f91005d6 Windows CI build: Ditch vcpkg
This replaces vcpkg with simply downloading the pre-compiled
dependencies from official upstream releases. The rationale is that
vcpkg is sometimes really slow to update to the latest SDL version when
it releases, and also that it requires the runner to compile SDL every
single time it's instantiated, which is slow and wasteful.

Instead, download the pre-compiled binaries of SDL from its release page
on GitHub. This way, we don't have to compile it ourselves, and we
aren't waiting on vcpkg whenever SDL releases a new version. And for
good measure, cache it so we aren't downloading it _every_ time, which
is even more efficient.

The same can't be done for SDL_mixer, though, because it doesn't have a
GitHub release page with pre-compiled binaries. Instead, we'll download
them from libsdl.org, which is an infrastructure that takes more strain
than if we used GitHub instead. But, it shouldn't matter anyways,
because we cahe this too. And we are going to ditch SDL_mixer for FAudio
in 2.4 anyways, so it's a moot point either way.
2022-02-11 23:49:02 -08:00
Misa
a43f5e1140 Update CI to CentOS container with SDL 2.0.20
I just built and pushed a new CentOS container that has SDL 2.0.20, so
updating it now.
2022-02-11 17:31:41 -05:00
Misa
cb8ce4d487 Update Dockerfile to SDL 2.0.20
Now that it is the minimum version, our CentOS container needs this
updated version too.
2022-02-11 17:31:41 -05:00
Misa
1d3ff5fbba Update README.md to refer to SDL 2.0.20
It's now the minimum version, so it needs to be updated.
2022-02-11 17:31:41 -05:00
Misa
ef03c2a54a Remove clamp in favor of SDL_clamp
For the same reasons as I removed VVV_min/max in favor of SDL_min/max in
aa7b63fa5f, I'm doing the same thing here.
2022-02-11 17:31:41 -05:00
Misa
e40f54f06b Remove temporary SDL fallthrough
We don't need a temporary fallback if we just start using SDL 2.0.18 or
later.
2022-02-11 17:31:41 -05:00
Misa
aa343bc334 Remove SDL_GetTicks64() ifdefs
We can now use the function that doesn't wrap after ~49 days since
SDL 2.0.18 released.
2022-02-11 17:31:41 -05:00
Misa
470a4358ef Remove VSync toggle ifdefs
These ifdefs can go away now that our minimum SDL version is 2.0.20.
2022-02-11 17:31:41 -05:00
Misa
38d25c0850 Update LodePNG to 20220109
This updates LodePNG to the commit pushed on January 9, 2022.
2022-02-11 11:55:39 -08:00
Misa
ed4d3d0fa8 Update TinyXML2 to 9.0.0
This updates TinyXML2 to the major release tagged on June 6, 2021.
2022-02-11 11:55:39 -08:00
Ethan Lee
84f9bb6dd6 Point to SDL_LoadWAV for SoundTrack FAudio suggestion 2022-01-15 01:02:24 -05:00
Ethan Lee
3b18a475dd Move MusicTrack below SoundTrack.
It's very likely that MusicTrack will be pulling from the SoundTrack FAudio
context, so make it so forward declarations are unnecessary.
2022-01-14 17:24:22 -05:00
Ethan Lee
adcabb9483 Add notes for FAudio implementation 2022-01-14 17:11:16 -05:00
Ethan Lee
5202b80a3d Remove unused m_isValid value from MusicTrack 2022-01-14 16:56:00 -05:00
Ethan Lee
d36741fa07 Move the Mix_OpenAudio call to SoundTrack, from MusicTrack.
In hindsight, the FAudio pointer will likely be in SoundTrack since we will
want to keep the mastering voice closer to the sounds and their source voice
arrays, while the MusicTrack will likely just be one source voice that gets
PCM from different streams.
2022-01-14 16:52:52 -05:00
Ethan Lee
df618e6d22 Isolate all SDL_mixer references to SoundTrack/MusicTrack.
This looks redundant but will actually help in the transition to FAudio; we
mostly want to keep the game logic the same while reimplementing the current
mixer, weirdness and all. Once that's done and confirmed to be stable and
consistent we can start cutting out the workarounds and quirks.
2022-01-14 16:46:04 -05:00
Misa
017d54adb0 Don't use function pointer to print room name
This improves the readability of the code.
2021-12-26 10:08:21 -08:00
Ethan Lee
81aa02e29b SDL_mixer is now entirely contained in Music.cpp.
This meant making the track vectors static, but that's kind of what we do with musicclass anyway?

In any case, this will make the transition to FAudio MUCH less invasive.
2021-12-26 08:57:38 -05:00
Ethan Lee
1eda3647ff Move the mute logic to musicclass.
This moves the last of the SDL_mixer calls to Music.cpp.
2021-12-26 08:48:23 -05:00
Ethan Lee
579f0f763a Update docs for MusicTrack/SoundTrack 2021-12-26 08:41:57 -05:00
Ethan Lee
230859f8f9 Inline SoundSystem into musicclass constructor 2021-12-26 08:41:01 -05:00
Ethan Lee
c87f0e1a0c Consolidate SoundSystem into Music.
It's just some small wrappers, and SoundSystem can be inlined trivially.
2021-12-26 08:38:19 -05:00
Ethan Lee
f723e03871 Remove unused MusicTrack constructor.
This wouldn't work anyway since music would need to be loaded via physfs.
2021-12-26 08:31:40 -05:00
Misa
dfb1e31d78 Optimization: Don't outline if room name BG opaque
This is quite simple. Just use a function pointer that switches out
which function we're going to use.

...Or not. C++ syntax makes this a bit awful since the function is a
member of a class. Did I mention how much I don't like C++?
2021-12-26 00:04:20 -08:00
Misa
8f226ced84 De-duplicate finalmode glitchname printing
Instead of copy-pasting the call twice, just use a variable to switch
between the two names.
2021-12-26 00:03:18 -08:00
Misa
276aab1209 Default to integer scaling mode
Issue #849 suggested making integer be the default on Big Picture and
Steam Deck, but after thinking about it more, I think it's better and
more simple to just default to integer mode in general.

Reason being that people in Big Picture shouldn't expect the picture to
look different if they're out of Big Picture but still in fullscreen, or
have the picture look different in fullscreen depending on if they
launched the game for the first time in Big Picture or not. And besides,
the less lines of code, the better. So I'm just making integer mode the
default.
2021-12-25 23:14:43 -08:00
Misa
550e76a6dc Add and use scaling mode enum
This enum is to just make each mode be readable, instead of mysterious
0/1/2 values. It's not a strictly-typed enum because we still have to
serialize it as ints in the XML, but it's better than just leaving them
as ints.

This also adds a NUM_SCALING_MODES enum, so we don't have to hardcode
that 3 when cycling scaling modes anymore.
2021-12-25 23:14:12 -08:00
Misa
f5166c437e Add forced fullscreen mode
This is mainly to make sure the game is definitely set to fullscreen in
Big Picture and on the Steam Deck, and to also remove windowed options
that wouldn't make sense if you're not on a desktop (toggling
fullscreen, resize to nearest). Those options would also be removed on
console and mobile too.

There's a bit of an annoying bug where if you launch the game in forced
fullscreen mode, but then exit and relaunch in normal mode, your game
will have fullscreen window sizes but it won't be fullscreen. This is
because forced fullscreen mode tries to preserve your non-forced
fullscreen setting, but due to the way window sizes are stored and
queried, it can't preserve the non-forced window size. This is a bit
difficult to work around, so I'm just putting in a FIXME here because we
can fix it later and I'd rather have a slightly buggy forced fullscreen
mode than not have one at all.

Closes #849.
2021-12-25 23:01:45 -08:00
Dav999-v
3e36bfd56f Simplify time formatting functions
Here's my notes on all the existing functions and what kind of time
formats they output:

- Game::giventimestring(int hrs, int min, int sec)
	H:MM:SS
	MM:SS

- Game::timestring()
// uses game.hours/minutes/seconds
	H:MM:SS
	MM:SS

- Game::partimestring()
// uses game.timetrialpar (seconds)
	MM:SS

- Game::resulttimestring()
// uses game.timetrialresulttime (sec) + timetrialresultframes (1/30s)
	MM:SS.CC

- Game::timetstring(int t)
// t = seconds
	MM:SS

- Game::timestringcenti(char* buffer, const size_t buffer_size)
// uses game.hours/minutes/seconds/frames
	H:MM:SS.CC
	MM:SS.CC

- UtilityClass::timestring(int t)
// t = frames, 30 frames = 1 second
	S:CC
	M:SS:CC

This is kind of a mess, and there's a lot of functions that do the same
thing except using different variables. For localization, I also want
translators to be able to localize all these time formats - many
languages use the decimal comma instead of the decimal point (12:34,56)
maybe some languages really prefer something like 1時02分11秒44瞬...
Which I don't know to be correct, but it's good to be prepared for it
and not restrict translators arbitrarily to only changing ":" and "."
when we can start making the system better in the first place.

I added a new function, UtilityClass::format_time. This is the place
where all time formats come together, given the number of seconds and
optionally frames. I have simplified the above-mentioned functions
somewhat, but I haven't given them a complete refactor or renaming -
I mainly made sure that they all use the same backend so I can make the
formats consistent and properly localizable.

(And before we start shoving more temporary char buffers everywhere
just to get rid of the std::string's, maybe we need to think of a
globally used working buffer of size SCREEN_WIDTH_CHARS+1, as a
register of sorts, for when any line of text needs to be made or
processed, then printed, and then goes unused. Maybe help.textrow,
or something like that.)

As for this commit, the available time formats are now more consistent
and changed a little in some places. Leading zeroes for the first unit
are now no longer included, time trial results and the Super Gravitron
can now display hours when they went to 60 minutes before, and we now
always use .CC instead of :CC. These are the formats:
- H:MM:SS
- H:MM:SS.CC
- M:SS
- M:SS.CC
- S.CC  (only used when always_minutes=false, for the Gravitrons)

Here's what changes to the current functions:
- Game::partimestring() is removed - it was used in two places, and
  could be replaced by game.timetstring(game.timetrialpar)
- Game::giventimestring(h,m,s) and Game::timestring() are now wrappers
  for the other functions
- The four remaining functions (Game::resulttimestring(),
  Game::timetstring(t), Game::timestringcenti(buffer, buffer_size)
  and UtilityClass::timestring(t)) are now wrappers for the "central
  function", UtilityClass::format_time.
- UtilityClass::twodigits(int t) is now unused so it's also removed.
- I also added int UtilityClass::hms_to_seconds(int h, int m, int s)
2021-12-25 11:38:12 -08:00
Misa
dd24343141 Use LoadImage in LoadIcon
This de-duplicates the code, simplifying the codebase and reducing the
number of code paths that needs to be maintained. It also adds
robustness checks to LoadIcon that weren't there before (checking that
loading the file succeeded and that decoding the file also succeeded).

Now, you might think that loading the image with alpha will change
things in some way. But actually, I tested it, and I'm pretty sure it
doesn't. Since my window manager, i3, doesn't display icons, I've had to
resort to this hacky multi-liner
( https://unix.stackexchange.com/a/48866 ) to dump the icon to a PAM
file. I don't know what a PAM file is and all my various attempts to
convert it into something readable failed. But what I did instead was
just grab the icon of the game before this commit (on 2.3, just to be
extra sure), and `diff`ed it with the grabbed icon now, and they end up
being the exact same file. So there's literally no difference.

The only other consideration is that LoadImage needs to be exported,
since it's implemented in GraphicsResources.cpp. I just opted to
forward-declare it right before LoadIcon in Screen.cpp, since it's
really the only other time it's used. No need to create a new header
file for it or anything.
2021-12-25 01:29:24 -08:00
Misa
a5c3bd97a0 Remove noAlpha argument from LoadImage
This is just to simplify the function. I really don't see any point in
taking away the alpha for some images, other than to disappoint people
who mod the game assets. It just complicates loading the image with no
real gain. To reduce maintenance costs, let's remove this alternate code
path.

Also it's a default argument and I don't like default arguments.
2021-12-25 01:29:12 -08:00
Misa
3108178c53 Remove noBlend argument from LoadImage
This argument... doesn't do anything.

First off, setting it to true explicitly enables blending on the
resulting surface, which is kind of the exact opposite of the variable
name and is misleading to say the least? And secondly, SDL surfaces have
blending enabled by default anyways, so it still doesn't even do
anything.

It's also a default argument, and I'm not one to shy away from removing
such default arguments.
2021-12-25 01:26:42 -08:00
Misa
a6b076e234 Explicitly zero declared struct ScreenSettingss
Performance cost is negligible and well worth being safe in case there
are more members added in the future but we forget to initialize them.
2021-12-25 00:30:10 -08:00
Misa
1e157f3cc9 De-C++-ify struct ScreenSettings
This includes:
- Removing the constructor in favor of actually being able to see that
  there's an actual function called being made initializing the struct
- Removing the use of a reference in Screen::init() in favor of using a
  pointer
- Adding the struct qualifier everywhere (it's not much typing),
  although technically you could typedef it in C, but I'd rather much
  not typedef just to remove a tag qualifier
2021-12-25 00:30:10 -08:00
Misa
d0ffafe117 Extern gameScreen, remove screenbuffer
I know earlier I removed the gameScreen extern in favor of using
screenbuffer, but that was only to be consistent. After further
consideration, I have found that it's actually really stupid.

There's no reason to be accessing it through screenbuffer, and it's
probably an artifact of 2.0-2.2 passing stack-allocated otherwise-global
classes everywhere through function arguments. Also, it leads to stupid
bugs where screenbuffer could potentially be NULL, which has already
resulted in various annoying crashes in the past. Although those could
be fixed by simply initializing screenbuffer at the very top of main(),
but, why not just scrap the whole thing anyway?

So that's what I'm doing.

As a nice side effect, I've removed the transitive include of Screen.h
from Graphics.h. This could've been done already since it only includes
it for the pointer anyway, but it's still good to do it now.
2021-12-25 00:29:28 -08:00
Misa
b7cbdfe8f9 Fix char overflow in Analogue Mode
In aa7b63fa5f, I didn't notice that the
result was implicitly being converted to int by the min/max from before.
I instead added it to the existing char, but that resulted in a char
overflow (it's unsigned, so thankfully not undefined behavior).

But of course the entire point of that commit is to make it explicitly
clear when you are converting between types, intentionally or otherwise,
in min/max comparisons. So despite causing a regression (which I have
now fixed), at least it did its job.
2021-12-22 21:49:08 -08:00
Misa
816a0b9eb7 Move filterSubrect off of Screen
It's only used in FlipScreen.
2021-12-22 20:39:11 -08:00
Misa
f7b4ac8322 Rename stretch mode to scaling mode internally
It's been long overdue that this variable be named properly. 2.2 added
integer scaling mode (thanks Ethan), 2.3 renamed it to scaling mode. Now
2.4 will properly call it what it is so people won't be confused by it.

The ScreenSettings struct member is renamed from stretch to scalingMode
along with the Screen class member being renamed, as well as the
toggleStretchMode function being renamed to toggleScalingMode as well.
Unfortunately, due to compatibility, we can't change the <stretch> XML
tag.
2021-12-22 19:54:59 -08:00
Misa
aa7b63fa5f Remove VVV_min/max in favor of SDL_min/max
VVV_min/max are functions that only operate on ints, and SDL_min/max are
macros that operate on any type but double-evaluate everything.

I know I more-or-less said earlier that SDL_min/max were dumb but I've
changed my mind and think it's better to use them, taking care to make
sure you don't double-evaluate, rather than trying to generate your own
litany of functions with either your own hand-rolled generation macros,
C++ templates, C11 generics, or GCC extensions (that last one you'd
technically use in a macro but it doesn't really matter), all of which
have more downsides than just not double-evaluating.

And the upside of not double-evaluating is that you're disencouraged
from having really complicated single-line min/max expressions and
encouraged to precompute the values beforehand anyway so the final
min/max is more readable. And furthermore you'll notice when you
yourself end up doing double-evaluations anyway. I removed a couple
instances of Graphics::len() being double-evaluated in this commit (as
well as cleaned up some other min/max-using code). Although the only
downside to those double-evaluations was unnecessary computation,
rather than checking the wrong result or having multiple side effects,
thankfully, it's still good to minimize double-evaluations where
possible.
2021-12-22 16:43:31 -08:00
Misa
f7454baffa Hide level path by default
You will now need to go through another confirm menu in order to print
your level path. The confirm menu warns you may leak sensitive
information if you are streaming.

Screenshots:
https://i.imgur.com/0Dc9jsZ.png
https://i.imgur.com/UhDgXqj.png
https://i.imgur.com/Z0ftQnH.png

Fixes #853.
2021-12-22 00:58:27 -08:00
Misa
8ba1325d0f Fix regression with wall stuck flipping behavior exactly reversed
The reason why the wall stuck flipping behavior happened in the first
place was because the code went like this:

    if (jumppressed)
    {
        if (onground && gravitycontrol == 0)
        {
            gravitycontrol = 1;
        }
        if (onroof && gravitycontrol == 1)
        {
            gravitycontrol = 0;
        }
    }

Basically, if you were both on ground and on a roof (i.e. stuck in a
wall), you would flip, but then due to code order and the fact that the
statement is not connected to the previous one, you would immediately
unflip afterwards. But if you were already flipped then the only path
that can be taken is to unflip you, since it's the statement that
appears last.

52fceb3f69 replaces the onground/onroof
conditionals with any_onground/any_onroof, so any player entity would
allow you to flip. But otherwise the code is the same. So is that the
problem?

No; tracing it through with GDB reveals that when you flip,
gravitycontrol is being set to 1, but never being set to 0. And it turns
out that's because any_onroof is not getting set. And that happens
because of another thing that 52fceb3f69
did - which was to set any_onground/any_onroof to true if indeed any
player entity was on ground or on a roof.

Unfortunately, the way Leo did it was to make the two statements
mutually exclusive - an 'if'-'else if' instead of two separate
statements. So a single entity could not mark both any_onground and
any_onroof as true (and the majority of the time, you will be a single
entity).

Thus, the solution is to just drop that 'else'.

Fixes #855.
2021-12-22 00:25:19 -08:00
Misa
caebde9e33 Fix warp sprites of big sprites sometimes not being drawn
I noticed when going frame-by-frame in Vertigo that sometimes the
wrapping enemies at the top sometimes just "popped" in frame. This is
because the sprite warp code only draws the warping sprite of sprites at
the bottom of the screen if they're below y=210. However, the warp point
starts at y=232, and warp sprites can be at most 32x32, which is exactly
the case with the Vertigo sprites, which are exactly 32x32. So the warp
code should start warping sprites if they're below y=200 (232 - 32)
instead.

Horizontal warping also has this problem; it warps at x=320 and
starts drawing warp sprites at x=300, even though it should start
drawing at x=288 (320 - 32). I've gone ahead and fixed that as well.
2021-12-20 20:18:24 -08:00
Misa
44ebb19d77 Outline "NO SIGNAL"
This is just in case the background gets changed by a custom level or
something to be something that would otherwise result in bad contrast.
Also if it needs to go outside the box for some reason. And I just like
the look of the outline.
2021-12-20 20:07:38 -08:00
Misa
9cddae8cc3 Outline trophy text
Whew, look at all those copy-pasted print statements!

Doing this because of the in-game timer feature. The text would
otherwise clash harshly with the timer otherwise. Even with the outline
it still clashes, but at least there's an outline so it's not as harsh.
2021-12-20 20:02:07 -08:00
Misa
1d6a808cbd Add centiseconds to timer overlays
This adds centiseconds to the in-game timer, as well as the time trial
timer.

This is to aid speedrun moderators in determining when exactly a run was
completed, which they can't easily do if the timer only has a precision
up to a second.
2021-12-20 19:26:01 -08:00
Misa
51fac68d3a Fix in-game timer going away after playing Super Gravitron
The problem was that it also needed to check that game.swnmode was true,
in addition to game.swngame being 1, to actually check that the Super
Gravitron was being played.
2021-12-20 17:44:34 -08:00
Misa
b7b9caacfc Remove unused game-gamestates
These were `CLICKTOSTART` and `FOCUSMODE`.
2021-12-18 00:01:32 -08:00
Misa
119e25d0bb Change all game-gamestates to use an enum type
Currently, all game-gamestate variables are just ints. This is not
particularly type-safe, in case the number of enums changes. To verify
that all current uses of the game-gamestate variables actually use the
enums, change them to be typed with the enum instead.

(As an aside, we should probably rename this so that it can't be
confused with Terry's state machine that has several different ways to
exploit to warp you to the credits, but that's something to do later.)
2021-12-17 23:57:55 -08:00
Misa
b67386894c hardreset: Reset ingame_titlemode
You'll note that getting in to the glitchy state of the game (the state
where you could play the game after it had hardreset() called on it)
required the player to quit to menu with ingame_titlemode set to true.
Well, quitting to menu calls hardreset(). So if hardreset() is called
when quitting, then you can no longer preserve ingame_titlemode that
way. This is a bit overkill, but I'm just taking precautions.
2021-12-17 23:39:26 -08:00
Misa
7f9247b0c7 Add asserts if ingame_titlemode in unexpected places
The game will now assert if the main menu is created while
ingame_titlemode is true, or if we attempt to load into a mode while
it's true. And if assertions are disabled then it just stops doing it
anyway.

I don't think there's any way to get a glitched ingame_titlemode again,
ever since I removed save data deletion taking you back to the main
menu. But I've had enough bugs with the fact that we more-or-less use
the same state for main menu options and in-game options, and that
glitched ingame_titlemode bug DID just happen, so I'm taking
precautions.
2021-12-17 23:36:13 -08:00
Misa
5ebc65d1a2 Pull out fade mode handling into separate function
The next commit will add logic that more-or-less quits the whole block
if ingame_titlemode, and instead of adding another layer of indentation
I will just pull this into its own function so we can use a return
statement.
2021-12-17 23:35:08 -08:00
Misa
cc9c71a94a deletestats: Properly reset bestgamedeaths
While I was testing deleting data while you were in-game, I noticed that
deleting data gave you all the "Win with less than X deaths" trophies,
even if you never got any of them before deleting data. Well, it turns
out that if you have the best game death count of 0, then you win every
trophy, and if you have the best game death count of -1 then that means
you haven't completed the game yet.

This reset was added in e3bfc79d4a, so at
least it's not in 2.3, but I only have myself to blame for making this
mistake. Whoops.
2021-12-17 23:34:44 -08:00
Misa
2770353142 Don't go back to main menu when deleting main game save data
Going back to the main menu allowed for glitchiness to occur if you
deleted your save data while in in-game options. This meant you could
then load back in to the game, and then quit to the menu, then open the
options and then jump back in-game, exploring the state of the game
after hardreset() had been called on it. Which is: pretty glitchy.

For example, this meant having your room coordinates be 0,0 (which is
different from 100,100, which is the actual 0,0, thanks for the
100-indexing Terry), which caused some of the room transitions to be
disabled because room transitions were disabled if the
game.door_up/down/left/right variables were -2 or less, and they were
computed based on room coordinates, which meant some of them went
negative if you were 0,0 and not 100,100. At least this was the case
until I removed those variables for, at best, doing nothing, and at
worst, being actively harmful.

Anyways, so deleting your save data now just takes you back to the
previous menu, much like deleting custom level data does. I don't know
why deleting save data put you back on the main menu in the first place.
It's not like the options menu needed to be reloaded or anything. I
checked and this was the behavior in 2.0 as well, so it was probably
added for a dumb reason.

I considered prohibiting data deletion if you were ingame_titlemode, but
as of the moment it seems to be okay (if albeit weird, e.g. returning to
menu while in Secret Lab doesn't place your cursor on the "play"
button), and I can always add such a prohibition later if it was really
causing problems. Can't think of anything bad off of the top of my head,
though.

Btw thanks to Elomavi for discovering that you could do this glitch.
2021-12-17 23:34:25 -08:00
Misa
1924ca53ac Remove game.door_left/right/up/down variables
These don't do anything, and in fact are actively harmful by disabling
room transitions if your roomx/roomy is glitched.
2021-12-17 19:43:29 -08:00
Misa
a345cf93b8 Fix elephant placement across rooms
Okay, so, this is the elephant sprite, right?

https://i.imgur.com/dtS70zk.png

This is how it looks in the actual game, when you stitch all the rooms
together:

https://i.imgur.com/aztVnFT.png

Looks kind of messed-up, doesn't it?

Okay, so, in the bottom two rooms (11,9) and (12,9), the elephant is
placed at y-position -152. But in (11,8) and (12,8), it's placed at
y-position 96. This is despite the fact that -152 plus 240 is 88, not
96.

Similarly, in the left two rooms (11,8) and (11,9), the elephant is
placed at x-position 64, but in the right two rooms (12,8) and (12,9),
the elephant is placed at -264. This is despite the fact that 64 minus
320 is -256, not -264.

All of this stems from the calculations in Otherlevel.cpp using offsets
of -248 and -328 instead of -240 and -320.

So there's an 8-pixel offset that causes the elephant to be chopped off
when viewed with all the rooms stitched together. Simple enough to fix.
For the y-position fixes, I decremented the initial 8-pixel multiplier
as well, else the elephant would sink into the floor.

And this is what the elephant looks like now after stitching:

https://i.imgur.com/27ePLm1.png

Thanks to Tzann for pointing this out.
2021-12-08 16:25:18 -08:00
Dav999-v
f60d2a2964 Fix -Wformat-security warnings
These warnings are kinda spammy, and they make sense in principle.
vlog_error takes a format string, so passing it an arbitrary string
(even error messages from libraries) isn't a good idea.
2021-12-05 10:45:36 -08:00
Misa
6e832cae20 LoadImage: Check LodePNG return value and print errors
Dvoid from Discord just reported a crash when trying to load a
custom tiles2.png that was encoded weirdly.

The problem is that we don't check the return value from LodePNG, so
LodePNG gives us a null pointer, and then
SDL_CreateRGBSurfaceWithFormatFrom doesn't check this null pointer,
which then propagates until we crash in SDL_ConvertSurfaceFormat (or
rather, one of its sub-functions), and we would probably crash somewhere
else anyway if it continued.

After properly checking LodePNG's return value, along with printing the
error, it turns out that Dvoid's custom tiles2.png had an "invalid CRC".
I don't know what this means but it sounds worrying. `feh` can read the
file correctly but it also reports a "CRC error".
2021-11-14 14:02:51 -08:00
Misa
a6728c4648 README.md: Link to #618 for old Ubuntu versions
While we can't fix Canonical, we can at least work around them, and help
people on Ubuntu out by linking them to my comment listing the
currently-known workarounds.
2021-11-14 10:11:33 -08:00
Misa
cd15ae0fdc Use SDL_FALLTHROUGH if available
The SDL_FALLTHROUGH macro has been added to SDL 2.0.18. Until 2.0.18 is
released, use it if it's available.
2021-11-11 23:48:41 -08:00
Misa
0c1f756af8 Switch over to using SDL_GetTicks64()
SDL_GetTicks64() is a function that got added in SDL 2.0.18, which is
just an SDL_GetTicks() without a value that wraps every ~49 days,
instead wrapping after the sun explodes and kills us all. Oh sorry,
didn't mean to get existential.

For now, put this behind an SDL_VERSION_ATLEAST guard, which will be
removed when SDL 2.0.18 officially releases and we can update to it.
2021-11-01 11:37:50 -07:00
Misa
75e031cef0 Don't toggle VSync twice
The vsync variable is already toggled in toggleVSync(). Whoops.
2021-10-29 12:24:10 -07:00
Misa
d0992e18a4 Fix regression with rainbow lab BG in editor
731fb89c90 was partially reverted by #624
because of my bad rebase.
2021-10-28 17:27:13 -07:00
Misa
1bc1149ab5 Fix regression with counting out-of-bounds custom entities
My latest rebase of #624 (refactoring/splitting editor.cpp) accidentally
overwrote #787 and essentially reverted it entirely. So, add it back in.

This is the same as #787 except it uses the new names, uses SDL_INLINE
to inline the function, and uses named constants.
2021-10-27 16:49:57 -07:00
Misa
58c518c856 Silence GCC warnings about void*-to-function-pointer casts
GCC warns on casting `void*` to function pointers. This is because the C
standard makes a clear distinction between pointers to objects (`void*`)
and pointers to functions (function pointers), and does not specify
anything related to being able to cast object pointers to function
pointers.

The warning message is factually wrong, though - it states that it is
forbidden by ISO C, when in fact it is not, and is actually just
unspecified.

We can't get rid of the cast entirely, because we need the explicit cast
(the C standard _does_ mandate you need an explicit cast when converting
between object pointers and function pointers), and at the end of the
day, this is simply how `SDL_LoadFunction()` works (and more
importantly, how `dlsym()` works), so we can't get rid of it, and we
have no reason to anyways since it means we don't have a hard runtime
dependency on Steam (unlike some other games) and casting `void*` to
function pointers always behaves well on every single platform we ship
on that supports Steam.

Unfortunately, this warning seems to be a part of -Wpedantic, and
there's no way to disable this warning specifically without disabling
-Wpedantic. Luckily, I've found a workaround - just cast to `intptr_t`
before casting to the function pointer. Hopefully the compiler doesn't
get smarter in the future and this ends up breaking or anything...
2021-10-21 01:00:06 -07:00
Ally
f3786a8e3f
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-13 15:38:51 -07:00
Misa
700be11137 Remove space at end of Press %s to Teleport
This wasn't there in 2.2 and previous. I accidentally introduced it in.
2021-10-12 16:59:36 -07:00
Misa
449526bb4f Fix regression with per-pixel collision colors
In previous versions, the game mistakenly checked the wrong color
channel of sprites, checking the red channel instead of the alpha
channel. I abuse this in some of my levels. Then I broke it when
refactoring masks so the game now no longer checks the red channel but
seems to check the blue channel instead. So re-fix this to the previous
bug, and preserve the previous bug with a comment explaining why.
2021-10-06 23:18:58 -07:00
Misa
2f25ab77b1 Fix regression with tile 10 not having nothing behind it
This broke when I was refactoring things earlier, because we no longer
have a direct reference to the contents array, instead using a copied
int. But we have a settile() function anyway, so why not use it?
2021-10-06 17:00:50 -07:00
Misa
038f15f4a6 Remove outdated FIXMEs from Screen.cpp
The VSync renderer workaround is no longer a thing, so these comments
should go away.
2021-10-03 13:23:12 -07:00
Misa
7eea59a7e8 Make impossible time trial save screen message less verbose
Ally asked me why it was so verbose, and recommended wording it like
this instead. Not that it should matter much, since it _is_ impossible,
but...
2021-10-02 09:24:44 -07:00
Misa
0ed0892977 Add impossible message for quicksave screen in time trials
It is impossible to get on the quicksave screen in time trials, because
Enter is always bound to restarting time trials in a time trial, and
there's no way to open the map screen otherwise.

So, I've decided to add a fun little message in case someone somehow
manages to get to this screen in a time trial.
2021-10-01 21:13:51 -07:00
Misa
db8e0cd70a De-duplicate map menu quicksave screen
As is typical, the code was copy-pasted to account for Flip Mode, and
then copy-pasted again to account for custom levels, leading to four
instances of the same code.

I clean this up while also improving code style. This is where the new
FLIP macro and the fixed PrintWrap help a lot - otherwise the "Game
saved ok!" screen would look really wrong without the height
corrections.
2021-10-01 21:06:31 -07:00
Misa
b26ccd914d Simplify flipme text box position correction
It now looks more like the FLIP macro in Render.cpp: The y-position is
simply the height of the area the object is being flipped in, minus the
y-position itself, minus the height of the object. So:

    flipped_yp = constant - yp - height

This is just a mathematical simplification of the existing statement,
which is:

    flipped_yp = yp + 2 * (constant/2 - yp) - height

Using algebra, the 2 distributes into the parentheses, so

    flipped_yp = yp + constant - 2 * yp - height

And the two `yp`s add together, so

    flipped_yp = constant - yp - height

It's more readable this way.

Also I am using a named constant instead of a hardcoded one.
2021-10-01 20:59:55 -07:00
Misa
98b392197c PrintWrap: Account for height in Flip Mode
Otherwise, the text will be in the wrong position compared to normal
mode.

PrintWrap is not used in Flip Mode yet, but it will be used on the map
screen in an upcoming change of mine. The FLIP macro in Render.cpp can't
help us there, since it would need to know the height of the wrapped
text at compile time, when the height is only figured out at runtime
based off of the string (or, well, right _now_ the string _is_ known,
but we are going to merge localization for 2.4, and it's better to
future-proof...), and only PrintWrap itself can figure out the height of
the text. (Or, well, I suppose you could call it from outside the
function, but that's not very separation-of-concernsy style.)
2021-10-01 20:49:27 -07:00
Misa
517e20cecb Account for heights in the FLIP macro
Flipping objects in Flip Mode needs to account for the heights of those
objects (that's why flipme text boxes in Flip Mode in 2.2 were
positioned wrongly).

Also, turn it into a macro instead of an inline function.

This changes the positions of all existing de-duplicated map menu text
in Flip Mode, but it'll be more correct.
2021-10-01 20:39:24 -07:00
Misa
3a911a9a9b Fix SCREEN_HEIGHT_PIXELS being wrong constant
Whoops.
2021-10-01 09:35:28 -07:00
Misa
a346379413 Remove unused math.h include from Maths.h
Apparently this was in here, just completely unused.
2021-09-27 23:11:20 -07:00
Misa
3decf54dbc Mark all vlog functions with printf attributes
This ensures that compiler warnings about format strings will apply to
all calls of these functions as well.
2021-09-27 20:49:09 -07:00
Misa
5533215019 Use SDL_NORETURN
I misread SDL's code and thought that SDL's `begin_code.h` was internal
only to SDL. It turns out you get it when you include basically any
header, such as `SDL_stdinc.h`. So use it directly instead of copying it
for our own.
2021-09-27 10:32:23 -07:00
Misa
c3dfd4a4b1 De-duplicate map menu stats screen rendering code
Between accounting for Flip Mode and custom levels, this code was
copy-pasted three times, leading to _four_ instances of one code!

Anyways, I've cleaned it up. The position of the text in Flip Mode is
going to differ by 4 pixels from how it was previously, but that really
shouldn't matter.
2021-09-25 17:16:52 -07:00
Misa
e9351b4a00 Fix winning in No Death Mode saying "One trinkets"
While dying in No Death Mode was fixed to no longer say "One trinkets"
in 2.3, if you win in No Death Mode with one trinket, the game would say
"One trinkets".

So to fix this, just slot a ternary in there. The code is already kind
of bad anyways and is going to be refactored/de-STLed in the future
regardless, so I'm not feeling too badly about shoving a ternary in
there like that.
2021-09-25 17:06:59 -07:00
Misa
38b2213745 Rename number to number_words
This is to clarify that it returns the word forms of numbers, not
numbers themselves.
2021-09-25 15:08:13 -07:00
Misa
409f7b078f Update SDL version in README.md to 2.0.16
The game uses SDL 2.0.16 now. In fact, it already uses SDL 2.0.17 as
well, and will be definitely using SDL 2.0.18 when it releases.
2021-09-24 21:33:10 -07:00
Misa
cace685020 Add POS_MOD macro and use for all positive modulos
This macro is to make it so it won't be error-prone to write the
semi-confusing `(a % b + b) % b` statement, and you can just use an easy
macro instead.

Currently, the only places a positive modulo is needed is when switching
tilesets, enemies, and warp directions in the editor, as well as when
getting a tile in the tower, since towers just repeat themselves
vertically. Towers used this weird while-loop to sort of emulate a
modulo, which isn't half-bad, but is unnecessary, and I don't think any
compiler would recognize it as a modulo. (And if it's not optimized to a
proper modulo... what happens if the number being moduloed is really,
really big?)
2021-09-24 17:48:15 -07:00
Misa
891ca527f9 Remove overcomplicated integer divisions
Believe it or not, there are still some remnants of the ActionScript
coding standards in the codebase! And one of them sometimes pops up
whenever an integer division happens.

As it so happens, it seems like division in ActionScript automatically
produces a decimal number. So to prevent that, the game sometimes
subtracts off the remainder of the number to be divided before
performing the division on it.

Thus, we get statements that look like

    (a - (a % b)) / b

And probably more parentheses surrounding it too, since it would be
copy-pasted into yet another larger expression, because of course it
would.

`(a % b)` here is subtracting the remainder of `a` divided by `b`, using
the modulo operator, before it gets divided by `b`. Thus, the number
will always be divisible by `b`, so dividing it will mathematically not
produce a decimal number.

Needless to say, this is unnecessary, and very unreadable. In fact, when
I saw these for the first time, I thought they were overcomplicated
_modulos_, _not_ integer division! In C and C++, dividing an integer by
an integer will always result in an integer, so there's no need to do
all this runaround just to divide two integers.

To find all of these, I used the command

    rg --pcre2 '(.+?).+?-.+?(?=\1).+?%.+?([\d]+?).+?\/.+?(?=\2)'

which basically matches expressions of the form 'a - a % b / b', where
'a' and 'b' are identical and there could be any characters in the
spaces.
2021-09-24 17:39:31 -07:00
Misa
6192269128 Remove vmult lookup tables
There's really no need to put the y-multiplication in a lookup table.
The compiler will optimize the multiplication better than putting it in
a lookup table will.

To improve readability and to hardcode things less, the new
SCREEN_WIDTH_TILES and SCREEN_HEIGHT_TILES constant names are used, as
well as adding a new TILE_IDX macro to calculate the index of a tile in
a concatenated-rows (row-major in formal parlance) array. Also, tile
numbers are stored in a temporary variable to improve readability as
well (no more copy-pasting `contents[i + vmult[j]]` over and over
again).
2021-09-24 16:37:27 -07:00
Misa
491f3732aa Fix trailing whitespace in CustomLevels.h
Really not sure how they got there. I think it was because of my naive
indentation fix, so.
2021-09-24 16:36:54 -07:00
Misa
f9573a036d Remove commented-out old spike block code
This seems to be code for creating spike blocks in a previous version of
the game. It's unused and commented out, so, remove it.
2021-09-24 16:26:11 -07:00
Misa
3fcab3a395 Remove splitseconds lookup table and inline it
There's really no reason for this simple multiplication plus division to
be in a lookup table. The compiler will optimize it faster than putting
it in a lookup table will, I'm sure.
2021-09-24 16:03:14 -07:00
Misa
f48e385e68 Remove Ethan's binary-or comment
This comment was referring to a now-deleted variable named mkdirResult
that was binary-"or"ed with all mkdir() results... except for the saves
directory. That variable was only used for save file migration, which is
now axed, so this comment is referring to nothing now.

I don't really know the answer to Ethan's question, but it doesn't
matter now.
2021-09-23 23:26:28 -07:00
Misa
1b7a1248a8 Declare Windows mkdir as static
It's not going to be used in any other files so... best to declare it
static.
2021-09-23 23:26:00 -07:00
Misa
eabb3941fd Mark VVV_exit implementation as VVV_NORETURN
For consistency. And in case it matters that much (which I don't really
think it does).
2021-09-23 23:02:46 -07:00
Misa
32bd6c41bc Fix regression with VVV_COMPILEMUSIC aborting
So, it turns out freeing everything in binaryBlob::clear() without
checking for NULL results in an abort() because clear() gets called on
musicWriteBlob after it attempts to write the compiled music. It's just
that no one's using VVV_COMPILEMUSIC, so no one's ran into this.

I'm keeping VVV_COMPILEMUSIC around so in the future people can compile
music directly from the game (and probably half the existing
VVV_COMPILEMUSIC code is going to be thrown out, but oh well).
2021-09-23 22:35:52 -07:00
Misa
0c5024f7e7 Use fixed-size int types for resourceheader
Since this refers to specific exported file data, let's make sure this
is portable. I'm not sure if we'll ever ship on systems where
sizeof(int) != 4 or sizeof(bool) != 1, but better to be safer and
future-proof than not.
2021-09-23 22:21:49 -07:00
Misa
51bfed2032 Ifdef numberofHeaders out if not compiling music
This variable is only used when compiling music. Since it doesn't
actually keep track of the number of headers otherwise, ifdef it behind
VVV_COMPILEMUSIC.
2021-09-23 21:55:58 -07:00
Misa
1297b09c47 Fix regression from 2.3 with destroy(platforms)
2.3 introduced a regression with destroy(platforms). The problem was
that isplatform wasn't being set to false when the entity got disabled,
so if the platform was moving, it would keep moving until it hit a wall,
instead of stopping immediately.
2021-09-23 13:16:57 -07:00
Misa
b677e7e39f Update CentOS CI container used to Misa's container
I have just built a new CentOS container based off of the updated
Dockerfile, which now has SDL 2.0.16 installed. This updates the CI to
point to the new container.
2021-09-23 12:26:50 -07:00
Misa
9926d15465 Update Dockerfile to SDL 2.0.16
This is so future CentOS images will be generated with the new SDL
version.
2021-09-23 12:26:50 -07:00
Misa
c94d04a932 Use SDL_isxdigit() in favor of VVV_isxdigit()
SDL_isxdigit() was added in SDL 2.0.16, so we no longer have to
implement isxdigit() ourselves.
2021-09-23 12:26:50 -07:00
Misa
b74dcdc7ee Remove unused Steam GetStat/SetStat
Forgot these when I was cleaning up the unused achievement percentages
functions...
2021-09-22 20:04:34 -07:00
Misa
bf2f33f1ca Add noreturn qualifier to VVV_exit
This function doesn't return, so we mark it as noreturn if the compiler
supports it.
2021-09-22 19:58:31 -07:00
Misa
b2d7a0b4b6 Rewrite STDIN loading to not use STL
Previously, loading STDIN used std::istreambuf_iterator and std::vector
and whatnot because... I guess it was less typing? But this isn't 1989;
we have the disk space to spare and we don't need to use fancy stuff
just to save on typing. It's not that hard to implement an array that
regrows to the nearest power of two every time.
2021-09-22 19:55:28 -07:00
Misa
dbe9f7c2a0 Declare emscriptenloop as static
It's not going to link with anything in a different translation unit, so
just to make sure, we declare it as static.
2021-09-20 13:48:49 -07:00
Misa
1d0401ef83 Move Emscripten includes to top of main.cpp
All system header includes should come before project-specific includes
(includes specific to this game), while coming after the include
specific to the given file (if any; main.cpp doesn't have any).
2021-09-20 13:47:53 -07:00
Misa
4eb7f973ef Axe NETWORK_[set/get]AchievementProgress()
These are unused.

Ethan originally added them in case Terry wanted achievement
percentages. But he didn't add them, and I don't think the achievements
are changing anytime soon, so it's safe to remove this dead code.
2021-09-19 21:49:54 -07:00
Misa
e13d3af2eb Use stub types instead of intptr_t
This is so there are at least compiler warnings raised if one of the
pointers mismatch their types.
2021-09-17 21:11:25 -07:00
Misa
1a0e720be8 Don't use print formatting for hardcoded strings
If the string is hardcoded, then use compile-time string literal
concatenation instead.

I don't know if compilers are smart enough to recognize when you're
passing in hardcoded strings and to concatenate them into the string
literal at compile time instead. I also don't know that if compilers are
smart enough to recognize that, that further they recognize all the
logging functions are just wrappers around printf, and so they can
perform the same optimization at those function call sites, too. So it's
better to just do the string concatenation explicitly instead.
2021-09-17 14:05:23 -07:00
Misa
32f30020fa Name the return types rettype instead of retval
Minor code style fix. They are return types, not return values.
2021-09-17 13:58:44 -07:00
Misa
9d1659c3a4 De-duplicate Steam network function list
Instead of having three separate copies of the function list, use macro
magic to make it so there is only one list that we use in three
different cases.
2021-09-17 13:55:16 -07:00
Misa
135ff36409 Remove <stdio.h> include from SteamNetwork.c
It's unused.
2021-09-17 13:55:16 -07:00
Misa
6ba7058a0e Fix VSync renderer workaround
SDL just got an API to toggle VSync without having to tear down the
renderer ( libsdl-org/SDL#4157 ). We can remove the workaround and use
that instead. For now, we are putting it behind an ifdef until SDL
2.0.18 officially releases in November.

Fixes #831.
2021-09-14 20:23:22 -07:00
Misa
8d0a90a588 Avoid function call to check empty room name
Instead, a simple comparison of the first element will do.
2021-09-12 21:54:47 -07:00
Misa
ddff461a6c Replace hardcoded temp buffer sizes with a named constant
Constants.h will house constants like the screen size and others. But
basically only the screen size for now.

Now we don't have to type that "4 bytes per 40 chars (whole screen)"
comment everywhere...
2021-09-12 21:40:20 -07:00
Misa
ffe53746bc Rename textbox to textboxes and textbox line to lines
It's really dumb that these array names aren't plural when they should
be, because they contain more than one thing.
2021-09-12 21:06:27 -07:00
Misa
a50e8ecf48 Replace roomnames/hiddennames/glitchnames with const char*
Since those are all downstream recipients of either static storage or
memory that doesn't move for the duration of the custom level, it's okay
to make these be `const char*`s without having to redo any of the RAII
memory management.

mapclass::currentarea() is included in this as well. I also cleaned up
Tower.cpp's headers to fix some transitive includes because I was
removing UtilityClass.h includes from all other level files too.

The "Untitled room" names no longer show any coordinates, because doing
so would require complicated memory management that's completely
unneeded. No one will ever see them, and if they do they already know
they have a problem anyway. The only time they might be able to see them
is if they corrupted the areamap, but this was only possible in 2.2 and
previous by dying outside the room deaths array in Outside Dimension
VVVVVV, which has since been patched out. Besides, sometimes the
"Untitled room" gets overwritten by something else anyway (especially in
Finalclass.cpp), so it really, really doesn't matter.
2021-09-12 21:06:26 -07:00
Misa
a10342f5e6 Replace setblockcolour() argument with const char*
There's no reason it needs to be an std::string here.

Although, realistically, we should be using an enum instead of
string-typing, but, eh, that can be fixed later.
2021-09-12 21:06:26 -07:00
Misa
2991b2341a Fix regression with companions not spawning
Companions would not spawn if you didn't load the current room via a
room transition. This meant that companions wouldn't spawn if you loaded
a save file with a companion, at least not until you moved to a
different room and triggered a screen transition. But most importantly,
it meant that the Intermission 1 supercrewmate would never spawn,
because going to Intermission 1 does a straight gotoroom, and does not
do a room transition.

Turns out the roomchange refactor broke things, because of course it
did. The companion logic was implicitly relying on that bool to be set,
because...? Either way, it doesn't make sense. Using roomchange implied
that the code wanted to be ran only when doing a room transition, which
is clearly not the case here. The best thing to do here is to just move
it to a separate function that gets called at the end of
mapclass::gotoroom().
2021-09-11 22:53:07 -07:00
Misa
a7ae3e0fb0 Remove scmmoveme
So, I ended up breaking supercrewmate spawning with that roomchange
refactor. However, upon investigating how to fix it, I was running into
a weird interpolation issue due to scmmoveme, as well as the companion
spawning in the ground in "Very Good". And I was wondering why I or no
one else ended up running into them.

Well, as it turns out, scmmoveme ends up doing absolutely nothing. There
are only two instances where scmmoveme is used. The first is if you
respawn in "Very Good", and somehow have your scmprogress set to that
room. But that's impossible, because whenever you respawn, your
scmprogress is always set to the one after the room you respawn in. Even
if you respawned in the room previous to "Very Good" (which is "Don't
Get Ahead of Yourself!"), it still wouldn't work, since the logic always
kicks in when a gotoroom happens, and not only when a supercrewmate is
actually spawned. Since the scmprogress doesn't match, that case never
gets triggered, and we get to the second time scmmoveme is used, which
is in the catch-all case that always executes.

This second instance... also does nothing, because since we just
respawned, and our scmprogress got set to the room ahead of us, there is
no supercrewmate on screen. Then getscm() returns 0, and the player is
always indice 0, so the only thing we end up doing is setting the
player's x-position to their own x-position. Brilliant.

Anyway, this code results in interpolation issues and the supercrewmate
spawning in the ground on "Very Good" if you die, when my fix is
applied, because my fix moves this logic around to a different frame
order, and that actually ends up making scmmoveme no longer dead code.

So to recap: we have dead code, which looks like it does something, but
doesn't. But if you move it around in a certain way, it ends up having
harmful effects. One of the joys of working on this game...

It's also hilarious that it gets saved to the save file. Why? The only
time this variable is true, it is for literally less than a frame,
because it always gets set to false, because you always respawn using a
gotoroom whenever the supercrewmate dies, because you never respawn in
the same room as a supercrewmate, because Intermission 1 was
deliberately designed that way (else you'd keep continually dying since
the supercrewmate wouldn't move out of the way).
2021-09-11 22:23:47 -07:00
Misa
029463ad47 Remove unused or useless SDL_Rects from Graphics
These were bfont_rect, bg_rect, foot_rect, and images_rect.

bg_rect was only used once to draw the ghost buffer in the editor, but
that was only because Ally didn't know you could just pass NULL in, cuz
the ghost buffer is the same size as the backbuffer.
2021-09-11 02:24:55 -07:00
Misa
d3a868b566 Axe RGBflip() in favor of getRGB()
RGBflip() does the exact same thing as getRGB(), now that all the
surface masks have been fixed. This axes RGBflip() and changes all
callers to use getRGB() instead. It is more readable that way.

By doing this, there is less copy-pasting. Additionally, it is now
easier to search for RGBf() - which is an ENTIRELY different function
than RGBflip() - now that the name of RGBf is no longer the first four
characters of some different, unrelated function. Previously I would've
had to do `rg 'RGBf[^\w]'` which was stupid and awful and I hated it.
2021-09-11 02:15:20 -07:00
Misa
f237f41d8e Remove useless arguments from drawimagecol()
Turns out, the r, g, and b arguments don't actually do anything!

There was a call to RGBf() in the function. RGBf() is just getRGB() but
first adds 128 and then divides by 3 to each of the color channels
beforehand. Unfortunately, RGBf() does not have any side effects, and
the function threw away the return value. Bravo.

This also reveals that the website images drawn in the credits in the
main menu are only recolored because of a stale `ct` set by the previous
graphics.bigprint(), and not because any color values were passed in to
drawimagecol()... What fun surprises the game has in store for me every
day.
2021-09-11 02:12:03 -07:00
Misa
58ae93c4cc Music: Do not do fades if not playing
This fixes a regression where entering playtesting while a track was
fading out (by exiting out of playtesting with a track playing and then
immediately entering back in with the level start music set) would
result in no music.

The cause is the game doing fades even though nothing is playing, which
puts it in a confusing state.
2021-09-10 19:37:33 -07:00
Misa
8e02b90b76 Move Mix_PausedMusic() call into wrapper function
This wrapper function is for (a) future-proofing (b) proactive
prevention of future copy-pasting (c) to clarify that we never actually
halt music in the SDL_mixer sense, we only pause it, so to check if the
music is halted we actually check if the music is paused instead. This
is important because Mix_PlayingMusic() does not check if the music is
paused and Mix_PausedMusic() does not check if the music is halted.
2021-09-10 19:37:31 -07:00
Misa
06a88eff39 Kludge-fix being able to play music in editor
When you're on the music changing screen in the editor, it plays the
current track. When you return, it stops playing the track. However, if
you press escape, it doesn't stop playing the track. This is because
pressing escape just returns to the previous menu without stopping
playing the track.

To fix this, I just added some kludge in the return menu function. This
is kinda super bad but it works for now and is just something to clean
up later. Maybe like each menu having exit callbacks or something, I
dunno.

This is kinda a regression, kinda sorta not. In 2.2 and previous,
pressing escape would just close the settings menu entirely, which also
bypassed the music fadeout. 2.3 made it so pressing escape doesn't
entirely close the settings menu, and just returns to the previous menu,
which fails in a different way. But the intended way is definitely to
select the return option and having the music fade out.
2021-09-10 18:56:12 -07:00
Misa
e3bfc79d4a Reset some stats that weren't being reset in deletestats
This function now properly deletes the Super Gravitron record, the Super
Gravitron rank, and the best game deaths. They were not being properly
reset previously, meaning you would have to go into your save file to
properly clean out your save data.
2021-09-10 18:02:52 -07:00
Misa
74b7ce9d80 Reset fade booleans when silencing music
This fixes a bug where the music would keep playing when a collection
prompt appeared if the music was still fading in at that time.
2021-09-10 17:02:24 -07:00
Misa
6f499abef0 Fix platv values outside map size being saved as 67372036
If the map size was less than 20x20, platv values outside the map would
end up being saved as 67372036.

This happens because SDL_memset() operates on the byte level, and not
the multi-byte level. So it takes only the lower 8 bits of 4 and repeats
it for each byte in each integer, creating 67372036.
2021-09-10 16:58:53 -07:00
Misa
07bbc5b2de Don't check !muted when fading music after completion prompt
This was done in 2.2 and previous probably to fix the fact that there
were multiple conflicting audio controls (the player wants to mute the
audio but the game wants to fade in the audio), but is now actively
harmful since 2.3, because muting the game while finishing the
completion prompt means the music will never come back in, even after
unmuting.

I also notice that when collecting a custom crewmate, the game checks
for the level's start music instead of if there's actually a current
song playing right now. I don't know why this was done, because it
would've been better to copy-paste the trinket collection logic here.
It's entirely possible for the audio to just be muted and never come
back if the level has no start music but plays a song by using a script.
Anyways, leaving it alone because it's quite possible that a level might
be intentionally designed around this, I can't really tell the
intentions of every level creator, and it's easy to work around (either
don't use custom crewmates, which every modern level basically does
nowadays, or just set the start music).
2021-09-10 15:48:18 -07:00
Misa
3185d88776 Don't touch music when completing custom level
For some reason, when completing a custom level and fading to the menu,
the game attempts to fade the music in and also fade the music out at
the same time. This results in nothing happening at all, and in 2.2 and
previous, results in audio fading out from max volume while the game is
frozen on a black screen after the fadeout.

To avoid any potential badness, just remove these.
2021-09-10 15:42:25 -07:00
Misa
91b63e7f88 Fix dying during collection prompt persisting effects
In the main game, if you press R during the trinket collection prompt
after collecting a trinket, AND you have never entered Comms Relay, and
you respawn in a different room, the trinket collection gamestate will
be interrupted, but you will still be left with the advance text prompt,
cutscene bars, and muted music.

The previous workaround to fix the music would be to mute and then
unmute the game, but due to the new music changes, this workaround
(which in and of itself is a bug) no longer works. Instead, the music
would have to be restarted by going into another zone on the map.

Having an advance text prompt outside of a cutscene results in the
player being unable to flip, but they can still move around left and
right.

Speedrunners previously used the no-Comms-Relay interrupting behavior to
skip certain trinket collection prompts entirely with a frame-perfect R
press, so I can't patch that out. Having an advance text prompt outside
of a cutscene is (ab)used in custom levels to intentionally prevent the
player from flipping, and furthermore, it's also used in credits warp
runs of the main game to increment the gamestate; so I cannot patch that
out. The ability to press R everywhere even during cutscenes was added
for good reason - to make it less likely that a softlock can happen - so
I don't want to revert it.

But I still think this is worth fixing because previously, the
punishment for missing the frame-perfect window late was simply not
skipping the trinket prompt (since the R-press would be ignored), but
now the punishment is basically having to reset because of the advance
text prompt.

I would usually handle this in gamestate 0, but awful custom levels
might want to intentionally interrupt the gamestate to do, I don't know,
something. No level does that so far, but I'd like to do the least
invasive thing.

So what I've done is made it so the effects of interruption are undone
if you press R and the gamestate is interrupted. This is handled in
mapclass::resetplayer().
2021-09-10 11:49:46 -07:00
Misa
c83a360dd3 Fix unused variable warning on non-Emscripten
Otherwise, the compiler would warn that `sync` is unused.
2021-09-08 11:43:23 -07:00
leo60228
15b6fb0edd Remove -sFORCE_FILESYSTEM=1 from compile options
The latest Emscripten will warn that it should only be passed as a
linker option.
2021-09-07 09:43:48 -07:00
leo60228
7560b8b60b Handle lost focus on Emscripten
This was previously disabled, since it didn't seem to work. However, the
previous commit fixes the root issue.
2021-09-07 09:43:48 -07:00
leo60228
d40af63aa3 Split Func_input and Func_delta when unfocused
Without this, `fixedloop` will loop infinitely until focus is regained.
However, Emscripten won't actually know that focus is regained until
`fixedloop` returns.
2021-09-07 09:43:48 -07:00
Misa
7430be69e3 Remove getBGR
getBGR, when used in FillRect, was actually passing colors in RGB order.
But now the masks are fixed, so remove it, and fix up all existing
getBGR colors to use getRGB instead.
2021-09-06 20:12:48 -07:00
Misa
f6c9ff848f Fix all FillRect getRGB calls to be passed in RGB order
Due to the mask inconsistencies, getRGB calls that were passed to
FillRect ended up actually being passed in BGR order. But now that the
masks are fixed, all these BGR colors look wrong. So, fix up all of them
(...that's a _lot_ of copy-pasted code...) to be passed in RGB order.
2021-09-06 20:12:48 -07:00
Misa
d0b81d8eff Simplify clearing surface in preloader with ClearSurface()
It conveys intent better.
2021-09-06 20:12:48 -07:00
Misa
37b7615b71 Fix surface color masks
This fixes the color ordering of every SDL_Surface in the game.

Basically, images need to be loaded in ABGR format (except if they don't
have alpha, then you use RGB? I'm not sure what's going on here), and
then they will be converted to RGB/RGBA afterwards.

Due to the surfaces actually being BGR/BGRA, the game used to use
getRGBA/getRGB to swap the colors back around to BGRA/BGR, but I've
fixed those too.
2021-09-06 20:12:48 -07:00
Misa
2c0d6920e8 bprintalpha: Call PrintAlpha with a false cen
Whoops.

This fixes it so the outlines actually show up on the horizontal sides
of the outlined text.
2021-09-06 19:50:24 -07:00
Misa
c64fd89325 Untabify every single file
YOLO.

This is a repeat of #642. As before, I just did

    rg -l '\t' | xargs -n 1 sed -i -e 's/\t/    /g'

inside the desktop_version/ folder.
2021-09-06 18:56:39 -07:00
Misa
b3f437fe55 Rename respawncolour to savecolour
Since it's a variable like saverx/savery/savex/savey, it should be
renamed to savecolour and placed with all of them.
2021-09-06 18:28:28 -07:00
Misa
8e61a04937 Add changerespawncolour() script command
This command simply changes the color that the player respawns with upon
death. The respawn color also persists through custom save files.

Closes #830.
2021-09-06 16:11:19 -07:00
Misa
33c5b8b7c0 Use const std::string& where possible in function params
If it's at all possible to use `const std::string&` when passing
`std::string`s around, then we use it. This is to limit the amount of
memory usage as a result of the frequent use of `std::string`s, so the
game no longer unnecessarily copies strings when it doesn't need to.
2021-09-06 15:43:59 -07:00
Misa
ff07f9c268 De-duplicate text printing functions
I've made a new function, Graphics::do_print(), that does the actual
text printing itself. All the interfaces of the other functions have
been left alone, but now just call do_print() instead.

I also removed PrintOffAlpha() and just calculated the center x-position
in bprintalpha() itself (like bigbprint() does) to make it easier to
de-duplicate code.
2021-09-06 15:43:59 -07:00
Misa
730c935218 Textboxes: Don't use separate RGB variables
Text boxes have `r`, `g`, and `b`, and `tr`, `tg`, and `tb`. `tr`, `tg`,
and `tb` are the real colors of the text box, and `r`, `g`, and `b` are
merely the colors of the text box as the text box's alpha value is
applied to them.

Compare this with, say, activity zones (which are drawn like text boxes
but aren't text boxes): There is `activity_r`, `activity_g`, and
`activity_b`, and when they're drawn they're all multiplied by
`act_alpha`.

So just do the same thing here. Ditch the `tr`, `tg`, and `tb`
variables, and make `r`, `g`, and `b` the new `tr`, `tg`, and `tb`
variables. That way, there's simply less state to have to update
separately. So we can get rid of `textboxclass::setcol()` as well.
2021-09-06 00:56:49 -07:00
Misa
13a0c1282d drawgui: Don't declare loop vars in for initializers
Sneaky no-code-and-declarations-mixing fix.
2021-09-06 00:56:49 -07:00
Misa
a7aa92232f Remove redundant FillRect when drawing text boxes
This was already done in the drawtextbox function... so we were just
double-drawing the text box backing for absolutely no reason.
2021-09-06 00:56:49 -07:00
Misa
e9c3b03eba Remove unused SDL.h include from Textbox.h
Really unsure why it was included here. Not even any downstream users of
Textbox.h use any SDL functions from this include.
2021-09-06 00:56:49 -07:00
Misa
69fcdf9217 Remove useless variable lw from textboxclass
This variable is only assigned to, but never read from. Hence, it is
useless, and should be removed to make code analysis less complicated.
2021-09-06 00:56:49 -07:00
Misa
e47ff8131b Move max off of textboxclass
This is a variable that's only used in one method, and it's always
initialized beforehand. No need to carry it around, taking up memory,
and making code analysis more complicated.
2021-09-06 00:56:49 -07:00
Misa
9a637d0c0f Clean up style of drawcoloredtile
All unmutated parameters have been made const.

Declarations and code are no longer mixed.

Spacing has been made consistent.
2021-09-06 00:56:49 -07:00
Misa
1b236d5ec7 Clean up style of drawtextbox/drawpixeltextbox
All parameters are now made const, to aid in the reader in knowing that
they aren't ever changed.

Useless comments have been removed and been replaced with helpful
comments.

Useless parentheses have been removed.

Spacing has been made consistent.

Declarations and code are no longer mixed.
2021-09-06 00:56:49 -07:00
Misa
36d0056c2c drawtextbox: Use drawpixeltextbox
Since these two are so similar, why not just use this one for the other?
Saves on copy-pasting code.
2021-09-06 00:56:49 -07:00
Misa
9767eb91f4 drawpixeltextbox: Remove now-unused parameters
They go bye-bye.

This is a friendly reminder that the map menu rendering code is heavily
copy-pasted, dear god...
2021-09-06 00:56:49 -07:00
Misa
31844eabc6 Axe drawcustompixeltextbox in favor of drawpixeltextbox
I'm honestly not too sure why drawcustompixeltextbox ever existed? All
it seemed to do was draw even more horizontal/vertical tiles to finish
any gaps in the tiling... which was all completely unnecessary and
wasteful, because even the previous drawpixeltextbox implementation
covered all gaps in all custom level map sizes that I tried.

Anyway, that at least gets rid of one copy-pasted function.
2021-09-06 00:56:48 -07:00
Misa
02b1fedeb1 drawpixeltextbox: Draw remaining horz/vert tile if non-multiple-8
This draws the remaining horizontal/vertical tile just beside the final
corner if the width/height is not a multiple of 8. (It'd be wasteful to
draw it if the width/height was a perfect multiple of 8, and result in
double-drawing translucent pixels if there were any.)

This has an advantage over the previous system of shifting the
horizontal/vertical tiling, in that custom corner textures don't look
weird due to overlapping like this. Now, custom horizontal/vertical
tiles _can_ look weird if they don't completely tile correctly (or if
they have translucent pixels), but that's better than mucking up the
corners.
2021-09-06 00:56:48 -07:00
Misa
1eb3e73c00 drawpixeltextbox: Don't use unneeded variables
`w` and `h` are provided alongside `w2` and `h2`. `w2` and `h2` are in
blocks of 8, while `w` and `h` are in pixels. Therefore, `w2` and `h2`
can just be figured out by diving `w` and `h` by 8.

Also, `xo` and `yo` were used to slide the horizontal/vertical tiling of
the text box a bit into one set of corners, so the horizontal/vertical
tiling wouldn't visibly overlap with the other corners, if using default
textures. This requires hardcoding it for each width/height of text box,
which isn't something that's generalizable. Also, it results in corners
that look weird if the corners have custom textures that don't adhere to
the same shape as default textures.

In the next commit I'll fix the non-multiple-of-8 text box dimensions
differently. Can't do it in this commit or the diff looks weird (at
least with my diff algorithm).
2021-09-06 00:56:48 -07:00
Misa
651cd4b674 Use drawtextbox() to draw text boxes
I have no idea why this perfectly good function was unused in favor of
copy-pasting all of its code.
2021-09-06 00:56:48 -07:00
Misa
fc2de88b03 Use SDL_BlitScaled in ScaleSurface
Why do all this error-prone per-pixel work when you can just use an SDL
function instead?
2021-09-05 20:07:18 -07:00
Misa
77696c0d55 Don't check map.extrarow when opening map
This is just to make sure there aren't any more inconsistencies with
regards to the value of graphics.menuoffset. Can't hurt to be sure.
2021-09-05 17:04:26 -07:00
Misa
edf949bd9c Use resumegamemode to track menu animation
This fixes a bug where the player could bring up the map on the very
first frame of a gamemode(game) animation. This is because the menu
animation checked graphics.menuoffset, but graphics.menuoffset wouldn't
have changed at that point because it only set graphics.resumegamemode.

Instead, just check for graphics.resumegamemode directly. We also need
to assign it to false whenever the map is closed so the player won't be
prevented from using the map screen again.
2021-09-05 16:59:05 -07:00
Misa
da6c524db5 Don't use map.extrarow for menu animations
This fixes all the headaches about map.extrarow having to be the correct
value and which way it should be and whatnot. The latest headache was
the detection that prevent user-initiated menu animations while an
animation was already happening being tripped because
graphics.menuoffset would be 230 (due to closing the menu while being in
a room without a room name), but then going to a room with a room name
would check for 240 instead, and 230 is less than 240. (The numbers are
the wrong way round because I got the ternaries the wrong way round, but
even if the numbers are the correct way round, the bug would still
happen, but it would just be reversed.)

So instead, I've just made it 240 for both. This doesn't change the
duration of the menu animation (because the animation moves in
increments of 25, and 230 / 25 == 240 / 25 under integer division). It
might change the animation slightly, but it was already inconsistent
anyway because map.extrarow was always set to be 1 in custom levels, and
I legitimately would not be able to tell the difference without
recording the animations and nitpicking it frame-by-frame.

Fixes #841.
2021-09-03 17:13:03 -07:00
Misa
727400ff27 Only reset fade booleans when music is actually played
Otherwise, the block that fades existing music out if m_doFadeOutVol is
true will never execute, because m_doFadeOutVol would always be false!
2021-09-03 16:38:34 -07:00
Ethan Lee
aba3a8ee9d Updated .ico 2021-09-03 15:57:16 -04:00
Misa
4339bbadbb Add message when player is kicked out of Super Gravitron
The player gets kicked out of the Super Gravitron if they have
invincibility or slowdown enabled. However, this can be confusing if no
message pops up
( 3039355280/ )
. So I've made it so that a text box will pop up when they get kicked
out.
2021-09-03 12:08:31 -07:00
leo60228
d64608ecaa Correct onground/onroof check 2021-09-02 16:09:06 -07:00
leo60228
72ade2ce49 Only execute flip logic when has_control 2021-09-02 16:09:06 -07:00
leo60228
58098dc748 Only apply flip momentum to player entities on ground/roof 2021-09-02 16:09:06 -07:00
Misa
e30fc4500e gamemode(teleporter): Set gamestate to GAMEMODE
This makes it so gamemode(teleporter) will always do an animation, even
if the game is already in TELEPORTERMODE.

I used this script to test:

    gamemode(teleporter)
    delay(5)
    gamemode(teleporter)
    delay(5)
    gamemode(teleporter)

In 2.2, this script starts the map menu bringing-up animation three
times.

In previous 2.3, this script starts the map menu bringing-up animation
once, but then the next gamemode(teleporter) immediately finishes the
animation, and the third gamemode(teleporter) does nothing.

This commit restores it to 2.2 behavior.
2021-09-02 12:23:17 -07:00
Misa
5b10164659 Prevent user-initiated map menu changes during menu animations
This makes it so it's not even possible to stay on the TELEPORTERMODE
screen by opening the map while it's being brought down. It also makes
it so the map animation is able to be canceled when being brought up
just by opening the map and closing it.

Fixes #833.
2021-09-02 12:21:46 -07:00
Misa
e9ec34e1f5 Call teleporterrenderfixed in TELEPORTERMODE
This restores it to 2.2 behavior, where the cutscene bars timer also
ticked in TELEPORTERMODE. It was a 2.3 regression that the cutscene bars
timer didn't tick there.

This makes it so if you manage to get stuck in TELEPORTERMODE when a
cutscene ends, the cutscene won't be stuck on untilbars() waiting for
the cutscene bars to go away, since the cutscene bars timer now ticks.
2021-09-02 11:46:56 -07:00
leo60228
be2b1564a8
Call FS.syncfs on Emscripten (#838)
Also, add a sync parameter to avoid calling syncfs too often.

Calling syncfs twice in a row is both inefficient and leads to errors
displaying twice. This allows us to bypass it when saving unlock.vvv as
part of savestatsandsettings.
2021-09-02 13:19:51 -04:00
Misa
3c318814a4 Remove EditorData and put its attributes on customlevelclass
This object basically had no reason to exist... it was just more verbose
to use, which really reminded me of Java. Anyway, this is the last thing
named after the editor for no reason when it should be a part of the
customlevelclass, so I moved its attributes to customlevelclass.
2021-09-01 15:30:02 -07:00
Misa
8bff287907 Rename edentity to customentities
This fixes the fact that the name of the singular type is plural, but
the name of the plural array is singular. Which has always annoyed me,
too. Also this makes it more clear that custom entities don't have much
to do with the editor.
2021-09-01 15:30:02 -07:00
Misa
47d01277bf Rename edentities to CustomEntity
That's what it is - it's an entity in a custom level. Not something to
do with the editor, necessarily. Like before, the name of the XML
element will remain the same.
2021-09-01 15:30:02 -07:00
Misa
3c30d9b7f0 Rename cl.level to cl.roomproperties
I mean, that's what they are. Room properties are within a level, not a
level themselves. So...
2021-09-01 15:30:02 -07:00
Misa
d549a535e0 Rename edlevelclass to RoomProperty
That's what edlevelclass is... so that's what it should be named. (Also
removes that "ed", too, making this less coupled to the in-game editor.)

Unfortunately, for compatibility reasons, the name of the XML element
will still remain the same.
2021-09-01 15:30:02 -07:00
Misa
86b47878f9 Fix number of spaces of indentation of CustomLevels.h
CustomLevels.h now uses 4-space indents - like all other space-indented
files - instead of 2-space indents. This has bugged me for a while and I
decided to just fix it now.
2021-09-01 15:30:02 -07:00
Misa
a23014350f Move all editor-specific attributes to a new editorclass
This is a pretty hefty commit! But essentially, I made a new editorclass
object, and moved all functions and variables that only get used in the
in-game level editor to that class. This cleanly demarcates which things
are in the editor and which things are just general custom level stuff.

Then I fixed up all the callers. I also fixed up some NO_CUSTOM_LEVELS
and NO_EDITOR ifdefs, too, in several places.
2021-09-01 15:30:02 -07:00
Misa
987ae88909 Remove comment from top of editorclass
There's no need for this comment; it's quite obvious that editorclass is
a class about the editor and will contain editor variables.
2021-09-01 15:30:02 -07:00
Misa
6df182b38d Remove getLevelDirFileNames() from editorclass
As far as I can tell, this function has never been implemented, and only
existed in this header file. FILESYSTEM_getLevelDirFileNames() already
exists (well, used to exist; it's been changed and renamed to
FILESYSTEM_enumerateLevelDirFileNames()), so I'm removing this now.
2021-09-01 15:30:02 -07:00
Misa
83cccbed07 Remove contents memset from editorclass constructor
It gets (re-) initialized in editorclass::reset() anyway, so I'm just
removing this for code clarity.
2021-09-01 15:30:02 -07:00
Misa
6f0177ec04 Move editor functions to separate header
Editor functions now live in Editor.h, which is a new file - the
existing editor.h has been renamed to CustomLevels.h.
2021-09-01 15:30:02 -07:00
Misa
3e380e23fb Rename editor.h to CustomLevels.h
This accompanies the editor.cpp -> CustomLevels.cpp change; I'll be
splitting out the editor functions in the next commit. The name of the
include guard has been changed as well, but not anything else.
2021-09-01 15:30:02 -07:00
Misa
3fe0f01bcc Move editor functions to separate file
This moves editorrenderfixed(), editorrender(), editorinput(),
editorlogic(), and their associated functions to a new file named
Editor.cpp - which is exactly what it says on the tin; it stores all the
functions related to the actual in-game editor loop. Also, the existing
editor.cpp has been renamed to CustomLevels.cpp.
2021-09-01 15:30:02 -07:00
Misa
3ef5248db9 Simplify and print XML errors from TinyXML-2
All XML functions now check the return value of
tinyxml2::XMLDocument::Error() after each document gets loaded in to
TinyXML-2. If there's an error, then all functions return. This isn't
strictly necessary, but printing the error message that TinyXML-2 is the
bare minimum we could do to be useful.

Additionally, I've standardized the error messages of missing or
corrupted XML files.

Also, the way the game went about making the XML handles was... a bit
roundabout. There were two XML handles, one for the document and one for
the root element - although only one XML handle suffices. So I've
cleaned that up too.

I could've gone further and added error checking for a whole bunch of
things (e.g. missing elements, missing attributes), but this is good
enough.

Also, if unlock.vvv or settings.vvv don't exist yet, the game is
guaranteed to no-op instead of continuing with the function. Nothing bad
seems to happen if the function continues, but the return statements
should be there anyway to clearly indicate intent.
2021-09-01 15:09:40 -07:00
Misa
cbc84edb0e Remove superfluous message if settings.vvv/unlock.vvv don't exist
If settings.vvv doesn't exist, loadsettings() calls savesettings(), but
savesettings() already prints a message if settings.vvv doesn't exist.
So then the output would look like

    No settings.vvv found. Creating new file
    No settings.vvv found

Which is clearly redundant.

The same thing happens with unlock.vvv, but in that case the following
prints instead

    No unlock.vvv found. Creating new file
    No Stats found. Assuming a new player
2021-09-01 15:09:40 -07:00
Misa
73535b4342 De-duplicate loadsummary parsing and move to different function
I will need to be able to return from this function if there's an XML
error, otherwise writing out the control flow manually gets really
nasty. And while I'm at it, it's some a nice de-duplication as well.

To do this, we create a temporary struct that bundles up all the
information we want for the summary, and pass it in to the intermediate
load function.

Furthermore, we can get rid of reading map.finalstretch - it affects
nothing. map.finalmode is still needed, however, because of the usage of
map.area().
2021-09-01 15:09:40 -07:00
Misa
bbc2f06d81 De-duplicate quick/telesummary fetching in Game::init()
The less copy-pasted code, the less work for me later.
2021-09-01 15:09:40 -07:00
Misa
c58c357a81 Simplify Flip Mode rendering code with SDL_RenderCopyEx
Previously, Flip Mode rendering had to be complicated and allocate
another buffer to call FlipSurfaceVerticle, and it was just a mess.

Instead, why not just do SDL_RenderCopyEx, and let SDL flip the screen
for us? This ends up pretty massively simplifying the rendering code.
2021-09-01 14:44:59 -07:00
Misa
23a2f57a70 Replace some info logs with debug logs
These are a bit noisy, so I've replaced them instead.
2021-09-01 14:34:55 -07:00
Misa
b652d327dc Add debug logs
These are disabled by default, because they're noisy. To enable them,
pass -debug.
2021-09-01 14:34:55 -07:00
Misa
2eb9e23ecc Allow disabling output/fine-tuning output
-nooutput disables output completely (both STDOUT and STDERR). -noinfo
disables INFO lines.  -nowarn disables WARN lines. -noerror disables
ERROR lines.
2021-09-01 14:34:55 -07:00
Misa
c68a274c4f Add command-line options to force-enable/disable color
`-forcecolor` will force color to be on. `-nocolor` will force color to
be off.

And just because I'm a nice person, I've also added British versions of
those flags. As a treat.
2021-09-01 14:34:55 -07:00
Misa
ac85f57441 Add colors to logging
This includes the bold as well.

INFO is just default, WARN is yellow, ERROR is red.

We try to automatically detect if the output is a TTY (and thus supports
colors), and don't emit colors if so. Windows 10 supports ANSI color
codes starting with a specific build, but we don't care to emit whatever
garbage Microsoft invented for builds older than that.
2021-09-01 14:34:55 -07:00
Misa
96539f891c Replace all print calls with vlog calls
This is pretty straight-forward to do.
2021-09-01 14:34:55 -07:00
Misa
d9737589de Add logging functions
Named "vlogs" because they're logs for VVVVVV. Also it's a funny name.
2021-09-01 14:34:55 -07:00
Ethan Lee
ac0644a70a Added my quick script to fixup Mac dylib paths 2021-09-01 12:07:57 -04:00
Misa
5abe615507 Fix Pauli Kohberger's name stopping being drawn too early
This is because the y-position of the graphics.onscreen() check was a
little too high. Then their name (under Beta Testing) would suddenly
disappear too early. You'd have to look real close to spot it, but it
does happen. It's cuz the credits are all kinda hardcoded, which is
probably bad, but fixing that would have to come later...
2021-08-31 23:23:27 -07:00
Misa
4c69281738 Move Misa to C++ credits
I talked with Ethan earlier about this. For 2.3, he wanted me in GitHub
contributors (well, still separate from the rest), to really highlight
the source-code-release community-driven nature of 2.3, but he said it'd
be fine to put me in C++ credits in 2.4.
2021-08-31 23:22:22 -07:00
Misa
0028e314e4 Suppress unused i warning if NUM_BACKENDS is 0
Else this warning would pop up if not compiling an OFFICIAL_BUILD.
2021-08-31 23:12:38 -07:00
Misa
146678aac4 Make PhysFS RWops work with -DBUNDLE_DEPENDENCIES=OFF
The RWops stuff isn't a part of any standard PhysFS package (and given
that it explicitly wraps around SDL I'm not sure how you _would_ package
it). So we need to get the physfsrwops.h include in if
BUNDLE_DEPENDENCIES is off, otherwise this results in a compile-time
include-not-found failure.

Additionally, I've placed the PhysFS RWops stuff in their own extras/
folder, so none of the other PhysFS stuff gets included in a
-DBUNDLE_DEPENDENCIES=OFF build.
2021-08-31 23:09:13 -07:00
Ethan Lee
342dfcb48d Minor VS buildfix 2021-08-31 19:07:24 -04:00
leo60228
14c24069fd Fix indentation consistency 2021-08-31 15:37:52 -07:00
leo60228
52fceb3f69 Allow any player entity to be on the ground/roof to flip
This re-adds the midair flip glitch.
2021-08-31 15:37:52 -07:00
leo60228
1e8b5a6ba7 Actually move checks out of loop
How did this ever work?
2021-08-31 15:37:52 -07:00
leo60228
cd4ce05cb3 Split out tap_left and tap_right from player input loop
The game will freeze the player immediately if they release a
directional button within 3 frames of pressing it. Similar to flipping,
this involves global state, and will only apply to the first player
entity.
2021-08-31 15:37:52 -07:00
leo60228
1eb8570329 Split flip logic from player input logic
Closes #484

Flipping only applies momentum to the player entity currently being
processed. This normally wouldn't be a problem. However, flipping
involves global state, and only one flip can occur per frame. This means
that additional player entities don't get this boost of momentum, which
feels somewhat unnatural during gameplay.

This commit fixes this by splitting flip logic out of the loop over
player entities, and applying the flip momentum to all player entities.
2021-08-31 15:37:52 -07:00
Misa
416fe00c9d Fix not-Flip-Mode flag turning off when returning from options menu
We need to check for graphics.setflipmode, not graphics.flipmode,
because graphics.flipmode only gets assigned at the end of the frame
(due to the deferred callback). Otherwise, returning from the options
menu would always turn flag 73 on, which would make you ineligible to
get the Flip Mode trophy, even if you're in Flip Mode.
2021-08-31 15:33:20 -07:00
leo60228
b132f4fc6c Change physfsrwops.h SDL.h include, again 2021-08-31 15:30:23 -07:00
leo60228
de0998ca34 Compile physfsrwops with VVVVVV because of SDL dependency 2021-08-31 15:30:23 -07:00
leo60228
668c3d91d6 Return false from unPackBinary if no tracks are valid 2021-08-31 15:30:23 -07:00
leo60228
f86a67456b Remove data/ from track name list, refactor music loading, and support loose ogg music
(these should be separate commits but they're annoying to split after
the fact, oh well)
2021-08-31 15:30:23 -07:00
leo60228
5b6c4b6b28 Add physfsrwops 2021-08-31 15:30:23 -07:00
Ally
64be7dbd53
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-08-31 15:09:51 -07:00
Ethan Lee
8ebf8a21e4 Default to VSync being enabled 2021-08-31 15:08:46 -04:00
AllyTally
1c34dc2533 Reset new variables in hardreset 2021-08-31 11:17:00 -07:00
AllyTally
656941202a Fix formatting and add an INBOUNDS_VEC
If `setactivitytext` was the last line in a script,
the command would index the vector out of bounds.

I also modified the formatting to keep consistent
with the rest of the codebase.
2021-08-31 11:17:00 -07:00
AllyTally
b15e25f106 Add setactivitycolour(colour) and setactivitytext() commands
These commands will change the colour and text of the next
activity zone that gets spawned. `setactivitycolour` takes all
textbox colors, and `setactivitytext` will take the text on
the next line. These commands were designed this way
to avoid breaking forwards compatibility.
2021-08-31 11:17:00 -07:00
AllyTally
0685cade69 Change the text of custom activity zones
When an activity zone is spawned through the
use of `createactivityzone`, and `i` is 35,
then it'll change the activity zone text to
"Press ENTER to interact".
2021-08-31 11:17:00 -07:00
leo60228
1cbc3bdc7c Treat __unix__ define as generic Unix 2021-08-31 10:35:20 -07:00
leo60228
6abf87f1e4 Explicitly use void in emscriptenloop declaration
Co-authored-by: Misa <infoteddy@infoteddy.info>
2021-08-31 10:28:55 -07:00
leo60228
46ee84d6ef Disable unfocus on Emscripten 2021-08-31 10:28:55 -07:00
leo60228
0063e1c3bc Use Emscripten's main loop
On Emscripten, SDL_Delay is implemented as a busy loop. In addition,
everything happens on a single thread. This effectively means that
you have to let Emscripten manage the main loop, since if you do it
yourself the browser will just be frozen.
2021-08-31 10:28:55 -07:00
leo60228
7baf143a6b Add necessary Emscripten linker flags 2021-08-31 10:28:55 -07:00
leo60228
6d57ccc25b Support Emscripten in FileSystemUtils 2021-08-31 10:28:55 -07:00
leo60228
a689c3ab96 Support Emscripten's SDL2
Emscripten defaults to using SDL 2.0.10, but
`export EMCC_LOCAL_PORTS=sdl2=/path/to/SDL2` seems to work.
2021-08-31 10:28:55 -07:00
Misa
62cff254b7 Bump version to 2.4 2021-08-31 10:25:29 -07:00
Misa
c97b28f238 Don't hardcode destroy() arguments in loadcustom()
Otherwise, the new arguments to destroy(), which are 'moving' and
'disappear', would be thrown away by the simplified parser. Let's create
less work for ourselves to do and simply not have a hardcoded list of
allowed arguments for destroy() in the parser.
2021-08-31 09:26:39 -07:00
Misa
08971b3311 Fix destroy(platforms), implement under different names
destroy(platforms) has been bugged since 2.0. The problem with it is
that it removes the platform entity, but doesn't remove its block. This
results in essentially turning the platorm invisible and stopping it
from moving.

This error should be fixed, but some levels (including my own) rely on
the invisible platform trick. So instead, the fixed version will be
implemented under a different name, destroy(moving).

There's also another problem with destroy(platforms), which is that the
name is misleading and it doesn't additionally destroy disappearing
platforms. I would also fix this, but in order to not run the risk of
breakage, it will have to be implemented under a different name, too. So
this will be destroy(disappear). As an added benefit, it's also more
granular to have platform-destroying functions under different names
than it is to consolidate them under the same name.
2021-08-31 09:26:39 -07:00
Misa
bca8d39bd7 Axe save file migration
The chances of someone using 2.0, then directly jumping to 2.4, are
pretty low - 2.0 is almost a decade old at this point.
2021-08-31 09:26:14 -07:00
Misa
85dc6db85b Axe screen_transition variable in favor of roomchange
When I added the two-frame delay fix, I didn't realize that Game had a
roomchange variable that was being used as a temporary variable here.
Now that it's fully spelled out and obvious (just look at the top of
gamelogic()), I realize that the variable exists and is being used, and
other readers will realize it's being used too - so now that I know it
exists, I can axe the screen_transition variable I added in favor of
using roomchange instead.
2021-08-31 09:25:47 -07:00
Misa
7a598f5811 Move roomchange off of Game
The purpose of this variable was to keep track of if gamelogic() called
map.gotoroom() at any point during its execution. So map.gotoroom()
always unconditionally set it to true, and then gamelogic() would check
it later.

Well, there's no need to put that in a global variable and do it like
that! It makes it less clear when you do that.

So what I've done instead is made a temporary macro wrapper around
map.gotoroom() that also sets roomchange to true. I've also made it so
any attempt to use map.gotoroom() directly results in failure (and since
then using map.gotoroom() in the wrapper macro would also fail, I've had
to make a gotoroom wrapper function around map.gotoroom() so the wrapper
macro itself doesn't fail).
2021-08-31 09:25:47 -07:00
Misa
75ed9f9631 Move linecrosskludge off of entityclass
This is a temporary vector that only gets used in mapclass::gotoroom().
It's always guaranteed to be cleared, so it's safe to move it off.

I'm fine with using references here because, like, it's a C++ STL vector
anyway - when we switch away from the STL (which is a precondition for
moving to C), we'll be passing around raw pointers here instead, and
won't be using references here anyway.
2021-08-31 09:25:47 -07:00
Misa
382b83a54d Move roomchangedir off of Game
This is a temporary variable that doesn't need to be on Game. It is
guaranteed to be initialized every time mapclass::gotoroom() gets
called, so it's safe to move it off.
2021-08-31 09:25:47 -07:00
624 changed files with 162519 additions and 76955 deletions

View file

@ -13,6 +13,8 @@ things for you to read through first:
- New platforms are acceptable if they use SDL + PhysicsFS and don't mess with
the game source too much.
- (No homebrew console targets, sorry! Maybe do the work in SDL instead?)
- Translations and localizations of the game are not a community effort. If you
want to translate the game, you should contact Terry.
- Pull requests that do not fill out the Legal Stuff will be closed
automatically.
@ -32,4 +34,4 @@ By submitting this pull request, I confirm that...
- [ ] My changes may be used in a future commercial release of VVVVVV
- [ ] I will be credited in a `CONTRIBUTORS` file and the "GitHub Friends"
section of the credits for all of said releases, but will NOT be compensated
for these changes
for these changes unless there is a prior written agreement

81
.github/workflows/android.yml vendored Normal file
View file

@ -0,0 +1,81 @@
name: CI (Android)
# Only trigger workflow when Android-specific code could have changed.
# This includes C/C++ files that have __ANDROID__ ifdefs.
# If adding new ifdefs, make sure to update these lists.
on:
push:
paths:
- "desktop_version/CMakeLists.txt"
- "desktop_version/src/ButtonGlyphs.cpp"
- "desktop_version/src/FileSystemUtils.cpp"
- "desktop_version/src/Screen.cpp"
- "desktop_version/src/Vlogging.c"
- "desktop_version/VVVVVV-android/gradlew"
- "desktop_version/VVVVVV-android/gradlew.bat"
- "desktop_version/VVVVVV-android/**/CMakeLists.txt"
- "desktop_version/VVVVVV-android/**.java"
- "desktop_version/VVVVVV-android/**.xml"
- "desktop_version/VVVVVV-android/**.pro"
- "desktop_version/VVVVVV-android/**.mk"
- "desktop_version/VVVVVV-android/**.gradle"
- "desktop_version/VVVVVV-android/**.jar"
- "desktop_version/VVVVVV-android/**.properties"
- ".github/workflows/android.yml"
pull_request:
paths:
- "desktop_version/CMakeLists.txt"
- "desktop_version/src/ButtonGlyphs.cpp"
- "desktop_version/src/FileSystemUtils.cpp"
- "desktop_version/src/Screen.cpp"
- "desktop_version/src/Vlogging.c"
- "desktop_version/VVVVVV-android/gradlew"
- "desktop_version/VVVVVV-android/gradlew.bat"
- "desktop_version/VVVVVV-android/**/CMakeLists.txt"
- "desktop_version/VVVVVV-android/**.java"
- "desktop_version/VVVVVV-android/**.xml"
- "desktop_version/VVVVVV-android/**.pro"
- "desktop_version/VVVVVV-android/**.mk"
- "desktop_version/VVVVVV-android/**.gradle"
- "desktop_version/VVVVVV-android/**.jar"
- "desktop_version/VVVVVV-android/**.properties"
- ".github/workflows/android.yml"
env:
SRC_DIR_PATH: VVVVVV/desktop_version/VVVVVV-android
jobs:
build:
name: Build (Android)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
path: 'VVVVVV'
- uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
cache: 'gradle'
- uses: actions/checkout@v4
with:
repository: libsdl-org/SDL
ref: release-2.30.8
path: 'SDL'
- name: Build SDL
run: |
sudo apt-get -y install ninja-build
cd SDL
./build-scripts/android-prefab.sh
mvn install:install-file -Dfile=build-android-prefab/prefab-2.30.8/SDL2-2.30.8.aar -DpomFile=build-android-prefab/prefab-2.30.8/SDL2-2.30.8.pom
- name: Build
run: |
cd ${SRC_DIR_PATH}
./gradlew build

View file

@ -1,7 +1,32 @@
name: CI
# Trigger this workflow on push or pull request
on: [push, pull_request]
# Only trigger workflow when code changes, or this file is changed.
# Android has a different workflow and different rules.
on:
push:
paths:
- "desktop_version/CMakeLists.txt"
- "desktop_version/src/**.cpp"
- "desktop_version/src/**.c"
- "desktop_version/src/**.h"
- "third_party/**"
- ".github/workflows/ci.yml"
pull_request:
paths:
- "desktop_version/CMakeLists.txt"
- "desktop_version/src/**.cpp"
- "desktop_version/src/**.c"
- "desktop_version/src/**.h"
- "third_party/**"
- ".github/workflows/ci.yml"
permissions:
contents: read
statuses: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
SRC_DIR_PATH: desktop_version
@ -15,17 +40,31 @@ jobs:
env:
CXXFLAGS: -I/usr/local/include/SDL2
LDFLAGS: -L/usr/local/lib
HOMEBREW_NO_ENV_HINTS: 1 # Suppress brew update hints
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
with:
submodules: true
- name: Cache Homebrew packages
id: cache-brew
uses: actions/cache@v3
with:
path: |
/usr/local/Cellar/ninja
/usr/local/Cellar/sdl2
/usr/local/opt/sdl2 # Symlink often used
key: ${{ runner.os }}-brew-${{ hashFiles('/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/ninja.rb', '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/Formula/sdl2.rb') }} # Using hash of formula files if available, or a fixed key for simplicity if not easily determined
- name: Install dependencies
run: brew install ninja sdl2 sdl2_mixer
if: steps.cache-brew.outputs.cache-hit != 'true'
run: brew install ninja sdl2
- name: CMake configure (default version)
run: |
mkdir ${SRC_DIR_PATH}/build && cd ${SRC_DIR_PATH}/build
cmake -GNinja ..
mkdir -p ${SRC_DIR_PATH}/build && cd ${SRC_DIR_PATH}/build
cmake -G Ninja ..
- name: Build (default version)
run: ninja -C ${SRC_DIR_PATH}/build
@ -44,126 +83,98 @@ jobs:
- name: Build (M&P)
run: ninja -C ${SRC_DIR_PATH}/build
- name: CMake configure (no custom levels)
run: |
cd ${SRC_DIR_PATH}/build
cmake -DMAKEANDPLAY=OFF -DCUSTOM_LEVEL_SUPPORT=DISABLED ..
- name: Build (no custom levels)
run: ninja -C ${SRC_DIR_PATH}/build
- name: CMake configure (no editor)
run: |
cd ${SRC_DIR_PATH}/build
cmake -DCUSTOM_LEVEL_SUPPORT=NO_EDITOR ..
- name: Build (no editor)
run: ninja -C ${SRC_DIR_PATH}/build
build-lin:
name: Build (CentOS 7)
name: Build (Steam Linux Runtime Sniper)
runs-on: ubuntu-latest
container: ghcr.io/leo60228/vvvvvv-build@sha256:485448ad437653d47ceb068a0f2b910f4eb336107d340a1f0d828ca95c987985
env:
CXXFLAGS: -I/usr/local/include/SDL2
LDFLAGS: -L/usr/local/lib
container: registry.gitlab.steamos.cloud/steamrt/sniper/sdk:latest
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
with:
submodules: true
- name: CMake configure (default version)
run: |
mkdir ${SRC_DIR_PATH}/build && cd ${SRC_DIR_PATH}/build
cmake ..
mkdir -p ${SRC_DIR_PATH}/build && cd ${SRC_DIR_PATH}/build
cmake -G Ninja ..
- name: Build (default version)
run: make -j $(nproc) -C ${SRC_DIR_PATH}/build
run: ninja -C ${SRC_DIR_PATH}/build
- name: CMake configure (official)
run: |
cd ${SRC_DIR_PATH}/build
cmake -DOFFICIAL_BUILD=ON ..
cmake -G Ninja -DOFFICIAL_BUILD=ON ..
- name: Build (official)
run: |
make -j $(nproc) -C ${SRC_DIR_PATH}/build
run: ninja -C ${SRC_DIR_PATH}/build
- name: CMake configure (M&P)
run: |
cd ${SRC_DIR_PATH}/build
cmake -DOFFICIAL_BUILD=OFF -DMAKEANDPLAY=ON ..
cmake -G Ninja -DOFFICIAL_BUILD=OFF -DMAKEANDPLAY=ON ..
- name: Build (M&P)
run: make -j $(nproc) -C ${SRC_DIR_PATH}/build
- name: CMake configure (no custom levels)
run: |
cd ${SRC_DIR_PATH}/build
cmake -DMAKEANDPLAY=OFF -DCUSTOM_LEVEL_SUPPORT=DISABLED ..
- name: Build (no custom levels)
run: make -j $(nproc) -C ${SRC_DIR_PATH}/build
- name: CMake configure (no editor)
run: |
cd ${SRC_DIR_PATH}/build
cmake -DCUSTOM_LEVEL_SUPPORT=NO_EDITOR ..
- name: Build (no editor)
run: make -j $(nproc) -C ${SRC_DIR_PATH}/build
run: ninja -C ${SRC_DIR_PATH}/build
build-win:
name: Build (windows-latest)
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
env:
SDL_VERSION: 2.26.0
- name: Install dependencies (Windows)
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Cache SDL
id: cache-windows-sdl
uses: actions/cache@v3
env:
cache-name: cache-sdl
with:
path: C:\SDL2-*
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ env.SDL_VERSION }}
- if: ${{ steps.cache-windows-sdl.outputs.cache-hit != 'true' }}
name: Download SDL if not cached
run: |
vcpkg install sdl2 sdl2-mixer
Invoke-WebRequest "https://github.com/libsdl-org/SDL/releases/download/release-$env:SDL_VERSION/SDL2-devel-$env:SDL_VERSION-VC.zip" -OutFile C:\SDL.zip
Expand-Archive C:\SDL.zip -DestinationPath C:\
- name: CMake initial configure/generate
run: |
mkdir $env:SRC_DIR_PATH/build
cd $env:SRC_DIR_PATH/build
$env:LDFLAGS = "/LIBPATH:C:\SDL2-$env:SDL_VERSION\lib\x86 "
cmake -G "Visual Studio 17 2022" -A Win32 `
-DSDL2_INCLUDE_DIRS="C:\SDL2-$env:SDL_VERSION\include" `
-DSDL2_LIBRARIES="SDL2;SDL2main" ..
- name: CMake configure (default version)
run: |
cd $env:SRC_DIR_PATH
$env:LDFLAGS = "/LIBPATH:$env:VCPKG_INSTALLATION_ROOT\installed\x86-windows\lib "
$env:LDFLAGS += "/LIBPATH:$env:VCPKG_INSTALLATION_ROOT\installed\x86-windows\lib\manual-link"
cmake -G "Visual Studio 16 2019" -A Win32 `
-DSDL2_INCLUDE_DIRS="$env:VCPKG_INSTALLATION_ROOT\installed\x86-windows\include\SDL2" `
-DSDL2_LIBRARIES="SDL2;SDL2main;SDL2_mixer" .
cd $env:SRC_DIR_PATH/build
cmake ..
- name: Build (default version)
run: |
cd $env:SRC_DIR_PATH
cd $env:SRC_DIR_PATH/build
cmake --build .
- name: CMake configure (official)
run: |
cd $env:SRC_DIR_PATH
cmake -DOFFICIAL_BUILD=ON .
cd $env:SRC_DIR_PATH/build
cmake -DOFFICIAL_BUILD=ON ..
- name: Build (official)
run: |
cd $env:SRC_DIR_PATH
cd $env:SRC_DIR_PATH/build
cmake --build .
- name: CMake configure (M&P)
run: |
cd $env:SRC_DIR_PATH
cmake -DOFFICIAL_BUILD=OFF -DMAKEANDPLAY=ON .
cd $env:SRC_DIR_PATH/build
cmake -DOFFICIAL_BUILD=OFF -DMAKEANDPLAY=ON ..
- name: Build (M&P)
run: |
cd $env:SRC_DIR_PATH
cmake --build .
- name: CMake configure (no custom levels)
run: |
cd $env:SRC_DIR_PATH
cmake -DMAKEANDPLAY=OFF -DCUSTOM_LEVEL_SUPPORT=DISABLED .
- name: Build (no custom levels)
run: |
cd $env:SRC_DIR_PATH
cmake --build .
- name: CMake configure (no editor)
run: |
cd $env:SRC_DIR_PATH
cmake -DCUSTOM_LEVEL_SUPPORT=NO_EDITOR .
- name: Build (no editor)
run: |
cd $env:SRC_DIR_PATH
cd $env:SRC_DIR_PATH/build
cmake --build .

18
.gitmodules vendored Normal file
View file

@ -0,0 +1,18 @@
[submodule "third_party/lodepng"]
path = third_party/lodepng
url = https://github.com/lvandeve/lodepng
[submodule "third_party/physfs"]
path = third_party/physfs
url = https://github.com/icculus/physfs/
[submodule "third_party/tinyxml2"]
path = third_party/tinyxml2
url = https://github.com/leethomason/tinyxml2/
[submodule "third_party/FAudio"]
path = third_party/FAudio
url = https://github.com/FNA-XNA/FAudio
[submodule "third_party/c-hashmap"]
path = third_party/c-hashmap
url = https://github.com/Mashpoe/c-hashmap
[submodule "third_party/SheenBidi"]
path = third_party/SheenBidi
url = https://github.com/Tehreer/SheenBidi

View file

@ -1,3 +1,5 @@
VVVVVV's source code is made available under a custom license. Basically, you can compile yourself a copy, for free, for personal use. But if you want to distribute a compiled version of the game, you might need permission first. See the [License exceptions](License%20exceptions.md) page for more information.
VVVVVV Source Code License v1.0
-------
Last updated on January 7th, 2020.

View file

@ -1,24 +1,26 @@
VVVVVV's source code is made available under a [custom license](LICENSE.md), which states that you must not distribute any materials from the game (i.e. the game's assets) which are not included in this repo unless approved by us in writing. In general, if you're interested in creating something that falls outside the license terms, get in touch with [Terry](http://distractionware.com/email/)!
There is an important general exception: if you're compiling a [Make
and Play](https://thelettervsixtim.es/makeandplay/) version of the game, then you can you freely distribute the game's assets.
The following is a list of projects which have been given permission by Terry to distribute the assets with distributions of the game, and under what conditions.
Exceptions granted to the VVVVVV source code license
-------
Last updated on November 22nd, 2020.
VVVVVV's source code is made available under a [custom license](LICENSE.md), which states that you must not distribute any materials from the game (i.e. the game's assets) which are not included in this repo unless approved by us in writing. The following is a list of projects which have been given permission by Terry to distribute the assets with distributions of the game, and under what conditions.
Last updated on January 23rd, 2024.
| Project | Creator | Description | Conditions | Link |
|---|---|---|---|---|
| Any distribution of the VVVVVV: Make and Play edition | | All distributions and packages of the Make and Play edition can freely distribute the data assets, so long as they compile with the makeandplay define and do not distribute the original levels.| Must compile with the makeandplay define set, cannot distribute the original levels. | |
| VVVVVV: Make and Play Edition |[Terry Cavanagh](http://distractionware.com/)|The free and official version of VVVVVV that includes player levels, and the tools to create your own levels, but does not include the original levels from the game.| Must compile with the makeandplay define set, cannot distribute the original levels. | [download](https://thelettervsixtim.es/makeandplay/) |
| Ved | [Dav999 and InfoTeddy](https://gitgud.io/Dav999/ved/-/graphs/master) | An external editor for VVVVVV levels. | No conditions. | [download](https://tolp.nl/ved/), [gitlab](https://gitgud.io/Dav999/ved) |
| Ved | [Dav999 and InfoTeddy](https://github.com/Daaaav/Ved/graphs/contributors) | An external editor for VVVVVV levels. | No conditions. | [download](https://tolp.nl/ved/), [github repo](https://github.com/Daaaav/Ved) |
| VVVVVV: Community Edition | https://github.com/v6cord/VVVVVV-CE/graphs/contributors | Community fork of VVVVVV focused on expanding the capabilities of player levels. | Must compile with the makeandplay define set, cannot distribute the original levels. | [github repo](https://github.com/v6cord/VVVVVV-CE) |
| VVVVVVwasm|kotborealis|Web Assembly port of VVVVVV| Must compile with the makeandplay define set, cannot distribute the original levels. | [github repo](https://github.com/kotborealis/VVVVVVwasm) |
| cursed_vvvvvv.exe | [MustardBucket](https://twitter.com/mustard_bucket/) | Modified version of VVVVVV where instead of flipping gravity you jump normally, can jump multiple times, and wall jump. | Make it impossible to revert to ordinary flipping behaviour. | [download](https://mustardbucket.itch.io/cursed-vvvvvv?secret=O0KvS02wD473pXBF9avreZsww), [twitter gif](https://twitter.com/mustard_bucket/status/1216272971779670016) |
| Haiku Port | [Julius C. Enriquez](https://github.com/win8linux) | Port for the Haiku operating system. | Display the following text in the Haiku package to make it clear that this is an exception: "VVVVVV is a commercial game! The author has given special permission to make this Haiku version available for free. If you enjoy the game, please consider purchasing a copy at [thelettervsixtim.es](http://thelettervsixtim.es)." | [haiku recipe](https://github.com/haikuports/haikuports/tree/master/games-arcade/vvvvvv), [haiku data.zip recipe](https://github.com/haikuports/haikuports/tree/master/games-arcade/vvvvvv_data) |
| Dreamcast Port | [Gustavo Aranda](https://github.com/gusarba/) | Port for the Sega Dreamcast. | Permission is given to distribute a ready-to-use CD image file for the Sega Dreamcast containing the data.zip assets for non commercial use only. | [github repo](https://github.com/gusarba/VVVVVVDC)|
| XBox One/UWP Port | [tunip3](https://github.com/tunip3) | Port for XBOX ONE (DURANGO) via UWP. | Permission is given to distribute a pre-compiled package (containing the data.zip assets) for people to run on development mode xboxes, for non commercial use only. | [github repo](https://github.com/tunip3/DURANGO-V6)|
In addition, the following exceptions apply for any projects that match these descriptions:
| Project | Description | Conditions |
|---|---|---|
| Any distribution of the VVVVVV: Make and Play edition | All distributions and packages of the Make and Play edition can freely distribute the data assets, so long as they compile with the makeandplay define and do not distribute the original levels.| Must compile with the makeandplay define set, cannot distribute the original levels. |
In general, if you're interested in creating something that falls outside the license terms, [get in touch with Terry!](http://distractionware.com/email/)
| armhf Port | [johnnyonFlame](https://github.com/johnnyonFlame/) | Armhf port for Raspberry PI and other SBC devices| Permission is for non commercial use only. Display the following text in the readme to make it clear that this is an exception: "VVVVVV is a commercial game! The author has given special permission to make this port available for free. If you enjoy the game, please consider purchasing a copy at [thelettervsixtim.es](http://thelettervsixtim.es)."| [github release](https://github.com/JohnnyonFlame/VVVVVV/releases/tag/v2.4-r1) |
| PortMaster distributions of the game for Linux Handheld devices | [portmaster](https://portmaster.games/) | A port manager GUI for Linux handheld devices | Permission is for non commercial use only. Display the following text in the readme to make it clear that this is an exception: "VVVVVV is a commercial game! The author has given special permission to make this port available for free. If you enjoy the game, please consider purchasing a copy at [thelettervsixtim.es](http://thelettervsixtim.es)."| [website](https://portmaster.games/detail.html?name=vvvvvv) |
| Wii Port | [Alberto Mardegan](https://github.com/mardy/) | Port for the Nintendo Wii. | Permission is given to distribute a ready-to-use build for the Nintendo Wii containing the data.zip assets for non commercial use only. | [github repo](https://github.com/mardy/VVVVVV/tree/wii) |
| Recalbox Port | [digitalLumberjack](https://gitlab.com/recalbox/recalbox) | Port for Recalbox project. | Display the following text in the readme to make it clear that this is an exception: "VVVVVV is a commercial game! The author has given special permission to make this port available for free. If you enjoy the game, please consider purchasing a copy at [thelettervsixtim.es](http://thelettervsixtim.es)." | [website](https://recalbox.com/) |

View file

@ -1,22 +1,23 @@
This is the source code to VVVVVV, version 2.0+. For more context about this release, see the [announcement](http://distractionware.com/blog/2020/01/vvvvvv-is-now-open-source/) on Terry's blog!
![logo](logo.gif "VVVVVV")
License
-------
VVVVVV's source code is made available under a custom license. See [LICENSE.md](LICENSE.md) for more details.
This is the source code to VVVVVV, the 2010 indie game by [Terry Cavanagh](http://distractionware.com/), with music by [Magnus Pålsson](http://souleye.madtracker.net/). You can read the [announcement](http://distractionware.com/blog/2020/01/vvvvvv-is-now-open-source/) of the source code release on Terry's blog!
In general, if you're interested in creating something that falls outside the license terms, get in touch with Terry and we'll talk about it!
The source code for the desktop version is in [this folder](desktop_version).
Authors
VVVVVV is still commercially available at [thelettervsixtim.es](https://thelettervsixtim.es/) if you'd like to support it, but you are completely free to compile the game for your own personal use. If you're interested in distributing a compiled version of the game, see [LICENSE.md](LICENSE.md) for more information.
Discussion about VVVVVV updates mainly happens on the "unofficial" [VVVVVV discord](https://discord.gg/Zf7Nzea), in the `vvvvvv-code` channel.
Credits
-------
- Created by [Terry Cavanagh](http://distractionware.com/)
- Room Names by [Bennett Foddy](http://www.foddy.net)
- Music by [Magnus Pålsson](http://souleye.madtracker.net/)
- Metal Soundtrack by [FamilyJules](http://familyjules7x.com/)
- Music by [Magnus Pålsson](https://magnuspalsson.com/)
- Metal Soundtrack by [FamilyJules](https://link.space/@familyjules)
- 2.0 Update (C++ Port) by [Simon Roth](http://www.machinestudios.co.uk)
- 2.2 Update (SDL2/PhysicsFS/Steamworks port) by [Ethan Lee](http://www.flibitijibibo.com/)
- Additional coding by [Misa Kai](https://infoteddy.info/)
- Beta Testing by Sam Kaplan and Pauli Kohberger
- Ending Picture by Pauli Kohberger
Versions
------------
There are two versions of the VVVVVV source code available - the [desktop version](https://github.com/TerryCavanagh/VVVVVV/tree/master/desktop_version) (based on the C++ port, and currently live on Steam), and the [mobile version](https://github.com/TerryCavanagh/VVVVVV/tree/master/mobile_version) (based on a fork of the original flash source code, and currently live on iOS and Android).
- Localisations by [our localisation teams](desktop_version/TRANSLATORS.txt)
- With additional contributions by [many others here on github](desktop_version/CONTRIBUTORS.txt) <3

View file

@ -10,6 +10,7 @@ VVVVVV
*.a
*.gch
src/Version.h.out
src/InterimVersion.out.c
# Game data
data.zip

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

View file

@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "AppIcon.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -1,16 +1,13 @@
# CMake File for VVVVVV
# Written by Ethan "flibitijibibo" Lee
cmake_minimum_required(VERSION 2.8.12)
cmake_minimum_required(VERSION 2.8.12...3.5)
# CMake Options
option(ENABLE_WARNINGS "Enable compilation warnings" ON)
option(ENABLE_WERROR "Treat compilation warnings as errors" OFF)
option(BUNDLE_DEPENDENCIES "Use bundled TinyXML-2, PhysicsFS, and UTF8-CPP (if disabled, TinyXML-2 and PhysicsFS will be dynamically linked, LodePNG and UTF8-CPP will still be statically linked)" ON)
set(CUSTOM_LEVEL_SUPPORT ENABLED CACHE STRING "Optionally disable playing and/or editing of custom levels")
set_property(CACHE CUSTOM_LEVEL_SUPPORT PROPERTY STRINGS ENABLED NO_EDITOR DISABLED)
option(BUNDLE_DEPENDENCIES "Use bundled TinyXML-2, PhysicsFS, and FAudio (if disabled, TinyXML-2, PhysicsFS, and FAudio will be dynamically linked; LodePNG, C-HashMap and SheenBidi will still be statically linked)" ON)
option(STEAM "Use the Steam API" OFF)
option(GOG "Use the GOG API" OFF)
@ -24,18 +21,11 @@ if(OFFICIAL_BUILD AND NOT MAKEANDPLAY)
set(GOG ON)
endif()
if(${CMAKE_VERSION} VERSION_LESS "3.1.3")
message(WARNING "Your CMake version is too old; set -std=c90 -std=c++98 yourself!")
else()
set(CMAKE_C_STANDARD 90)
set(CMAKE_C_EXTENSIONS OFF)
option(REMOVE_ABSOLUTE_PATHS "If supported by the compiler, replace all absolute paths to source directories compiled into the binary (if any) with relative paths" ON)
set(CMAKE_CXX_STANDARD 98)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
# Architecture Flags
if(APPLE)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# Wow, Apple is a huge jerk these days huh?
set(OSX_10_9_SDK_PATH /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk)
if(NOT CMAKE_OSX_SYSROOT)
@ -48,6 +38,8 @@ if(APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.9)
link_directories(/usr/local/lib)
add_compile_options(-Werror=partial-availability)
elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0) # SDL goes back to iOS 8.0, but modern Xcode doesn't
endif()
project(VVVVVV)
@ -58,12 +50,16 @@ endif()
# RPATH
if(NOT WIN32)
if(APPLE)
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(BIN_LIBROOT "Frameworks")
set(BIN_RPATH "@executable_path/Frameworks")
elseif(APPLE)
set(BIN_LIBROOT "osx")
set(BIN_RPATH "@executable_path/osx")
elseif(CMAKE_SIZEOF_VOID_P MATCHES "8")
set(BIN_LIBROOT "lib64")
set(BIN_RPATH "\$ORIGIN/lib64")
set(CMAKE_EXE_LINKER_FLAGS "-Wl,--disable-new-dtags")
else()
set(BIN_LIBROOT "lib")
set(BIN_RPATH "\$ORIGIN/lib")
@ -75,20 +71,31 @@ if(NOT WIN32)
endif()
# Source Lists
set(VVV_SRC
set(VVV_CXX_SRC
src/BinaryBlob.cpp
src/BlockV.cpp
src/ButtonGlyphs.cpp
src/CustomLevels.cpp
src/CWrappers.cpp
src/Editor.cpp
src/Ent.cpp
src/Entity.cpp
src/FileSystemUtils.cpp
src/Finalclass.cpp
src/Font.cpp
src/FontBidi.cpp
src/Game.cpp
src/Graphics.cpp
src/GraphicsResources.cpp
src/GraphicsUtil.cpp
src/IMERender.cpp
src/Input.cpp
src/KeyPoll.cpp
src/Labclass.cpp
src/LevelDebugger.cpp
src/Localization.cpp
src/LocalizationMaint.cpp
src/LocalizationStorage.cpp
src/Logic.cpp
src/Map.cpp
src/Music.cpp
@ -96,10 +103,10 @@ set(VVV_SRC
src/preloader.cpp
src/Render.cpp
src/RenderFixed.cpp
src/RoomnameTranslator.cpp
src/Screen.cpp
src/Script.cpp
src/Scripts.cpp
src/SoundSystem.cpp
src/Spacestation2.cpp
src/TerminalScripts.cpp
src/Textbox.cpp
@ -108,25 +115,60 @@ set(VVV_SRC
src/WarpClass.cpp
src/XMLUtils.cpp
src/main.cpp
)
set(VVV_C_SRC
src/DeferCallbacks.c
src/GlitchrunnerMode.c
src/Network.c
src/Textbook.c
src/ThirdPartyDeps.c
src/UTF8.c
src/VFormat.c
src/Vlogging.c
src/Xoshiro.c
../third_party/physfs/extras/physfsrwops.c
)
if(NOT CUSTOM_LEVEL_SUPPORT STREQUAL "DISABLED")
list(APPEND VVV_SRC src/editor.cpp)
endif()
if(STEAM)
list(APPEND VVV_SRC src/SteamNetwork.c)
list(APPEND VVV_C_SRC src/SteamNetwork.c)
endif()
if(GOG)
list(APPEND VVV_SRC src/GOGNetwork.c)
list(APPEND VVV_C_SRC src/GOGNetwork.c)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
list(APPEND VVV_C_SRC src/SDL_uikit_main.c)
endif()
set(VVV_SRC ${VVV_CXX_SRC} ${VVV_C_SRC})
# Executable information
if(WIN32)
add_executable(VVVVVV WIN32 ${VVV_SRC} icon.rc)
elseif(ANDROID)
add_library(VVVVVV SHARED ${VVV_SRC})
elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS")
file(GLOB_RECURSE REPO_RESOURCES "fonts/*" "lang/*")
add_executable(VVVVVV MACOSX_BUNDLE ${VVV_SRC} ${DATA_ZIP} AppIcon.xcassets ${REPO_RESOURCES})
set_target_properties(VVVVVV PROPERTIES
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.distractionware.vvvvvvmobile"
XCODE_ATTRIBUTE_PRODUCT_NAME "VVVVVV"
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2" # iPhone, iPad
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "2.5"
XCODE_ATTRIBUTE_MARKETING_VERSION "2.5"
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME AppIcon
XCODE_ATTRIBUTE_GENERATE_INFOPLIST_FILE YES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist"
XCODE_ATTRIBUTE_INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace YES
XCODE_ATTRIBUTE_INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents YES
RESOURCE "${DATA_ZIP};AppIcon.xcassets"
)
foreach(REPO_FILE ${REPO_RESOURCES})
file(RELATIVE_PATH REPO_FILE_REL "${CMAKE_CURRENT_SOURCE_DIR}" ${REPO_FILE})
get_filename_component(REPO_FILE_DIR ${REPO_FILE_REL} DIRECTORY)
set_property(SOURCE ${REPO_FILE} PROPERTY MACOSX_PACKAGE_LOCATION "Resources/${REPO_FILE_DIR}")
source_group("Resources/${REPO_FILE_DIR}" FILES "${REPO_FILE}")
endforeach()
else()
add_executable(VVVVVV ${VVV_SRC})
endif()
@ -136,16 +178,26 @@ if(BUNDLE_DEPENDENCIES)
target_include_directories(
VVVVVV PRIVATE
src
../third_party
../third_party/tinyxml2
../third_party/physfs
../third_party/physfs/src
../third_party/physfs/extras
../third_party/lodepng
../third_party/utfcpp/source
../third_party/c-hashmap
../third_party/FAudio/include
../third_party/FAudio/src
../third_party/SheenBidi/Headers
)
else()
target_include_directories(
VVVVVV PRIVATE
src
../third_party
../third_party/lodepng
../third_party/physfs/extras
../third_party/c-hashmap
../third_party/FAudio/src
../third_party/SheenBidi/Headers
)
endif()
@ -163,54 +215,57 @@ endif()
set(XML2_SRC
../third_party/tinyxml2/tinyxml2.cpp
)
set(FAUDIO_SRC
../third_party/FAudio/src/FAudio.c
../third_party/FAudio/src/FAudio_internal.c
../third_party/FAudio/src/FAudio_internal_simd.c
../third_party/FAudio/src/FAudio_operationset.c
../third_party/FAudio/src/FAudio_platform_sdl2.c
)
set(PFS_SRC
../third_party/physfs/physfs.c
../third_party/physfs/physfs_archiver_dir.c
../third_party/physfs/physfs_archiver_unpacked.c
../third_party/physfs/physfs_archiver_zip.c
../third_party/physfs/physfs_byteorder.c
../third_party/physfs/physfs_unicode.c
../third_party/physfs/physfs_platform_posix.c
../third_party/physfs/physfs_platform_unix.c
../third_party/physfs/physfs_platform_windows.c
../third_party/physfs/physfs_platform_haiku.cpp
../third_party/physfs/src/physfs.c
../third_party/physfs/src/physfs_archiver_dir.c
../third_party/physfs/src/physfs_archiver_unpacked.c
../third_party/physfs/src/physfs_archiver_zip.c
../third_party/physfs/src/physfs_byteorder.c
../third_party/physfs/src/physfs_unicode.c
../third_party/physfs/src/physfs_platform_posix.c
../third_party/physfs/src/physfs_platform_unix.c
../third_party/physfs/src/physfs_platform_windows.c
../third_party/physfs/src/physfs_platform_haiku.cpp
../third_party/physfs/src/physfs_platform_android.c
)
if(APPLE)
# Are you noticing a pattern with this Apple crap yet?
set(PFS_SRC ${PFS_SRC} ../third_party/physfs/physfs_platform_apple.m)
set(PFS_SRC ${PFS_SRC} ../third_party/physfs/src/physfs_platform_apple.m)
endif()
set(PNG_SRC ../third_party/lodepng/lodepng.c)
set(PNG_SRC src/lodepng_wrapper.c)
set(CHM_SRC ../third_party/c-hashmap/map.c)
set(SBIDI_SRC ../third_party/SheenBidi/Source/SheenBidi.c)
if(NOT OFFICIAL_BUILD)
# Add interim commit hash and its date to the build
# find_package sets GIT_FOUND and GIT_EXECUTABLE
find_package(Git)
if(GIT_FOUND)
# These filenames have to be qualified, because when we run
# the CMake script, its work dir gets set to the build folder
set(VERSION_INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.in)
set(VERSION_OUTPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.out)
set(VERSION_INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/InterimVersion.in.c)
set(VERSION_OUTPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/src/InterimVersion.out.c)
add_custom_target(
GenerateVersion ALL
# This BYPRODUCTS line is required for this to be ran every time
BYPRODUCTS ${VERSION_OUTPUT_FILE}
add_custom_command(
# This OUTPUT line is required for this to be ran every time
OUTPUT ${VERSION_OUTPUT_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/src/_dummy.c
COMMAND ${CMAKE_COMMAND}
# These args have to be passed through, otherwise the script can't see them
# Also, these args have to come BEFORE `-P`! (Otherwise it fails with an unclear error)
-DGIT_EXECUTABLE=${GIT_EXECUTABLE}
-DINPUT_FILE=${VERSION_INPUT_FILE}
-DOUTPUT_FILE=${VERSION_OUTPUT_FILE}
-P ${CMAKE_CURRENT_SOURCE_DIR}/version.cmake
)
add_dependencies(VVVVVV GenerateVersion)
target_compile_definitions(VVVVVV PRIVATE -DINTERIM_VERSION_EXISTS)
# This lets Version.h know that Version.h.out exists
add_definitions(-DVERSION_H_OUT_EXISTS)
endif()
add_library(InterimVersion STATIC src/InterimVersion.out.c)
list(APPEND STATIC_LIBRARIES InterimVersion)
endif()
# Build options
@ -224,12 +279,6 @@ if(ENABLE_WARNINGS)
/W4 $<$<BOOL:${ENABLE_WERROR}>:/WX>>)
endif()
if(CUSTOM_LEVEL_SUPPORT STREQUAL "NO_EDITOR")
add_definitions(-DNO_EDITOR)
elseif(CUSTOM_LEVEL_SUPPORT STREQUAL "DISABLED")
add_definitions(-DNO_CUSTOM_LEVELS -DNO_EDITOR)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(SUPPORTS_IMPLICIT_FALLTHROUGH TRUE)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
@ -252,22 +301,44 @@ if(MSVC)
target_compile_options(VVVVVV PRIVATE /wd4244)
endif()
# Disable warnings about `long long` in C++03 (from including PhysFS)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(VVVVVV PRIVATE -Wno-long-long)
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(VVVVVV PRIVATE -Wno-c++11-long-long)
endif()
# Disable warnings about flexible array members in C++ (from including FAudio)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(VVVVVV PRIVATE -Wno-c99-extensions)
endif()
# Set standards version, disable exceptions and RTTI
if(MSVC)
# Disable exceptions
# MSVC doesn't have /std:c99 or /std:c++98 switches!
# MSVC does not officially support disabling exceptions,
# so this is as far as we are willing to go to disable them.
string(REGEX REPLACE "/EH[a-z]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc")
string(REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set_source_files_properties(${VVV_CXX_SRC} PROPERTIES COMPILE_FLAGS "/EHsc /GR-")
# Disable RTTI
string(REGEX REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR-")
if(MSVC_VERSION GREATER 1900)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8")
endif()
else()
# Disable exceptions
string(REGEX REPLACE "-fexceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
string(REGEX REPLACE "-std=[a-z0-9]+" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
set_source_files_properties(${VVV_C_SRC} PROPERTIES COMPILE_FLAGS -std=c99)
# Disable RTTI
string(REGEX REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
string(REGEX REPLACE "-std=[a-z0-9+]+" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "-fexceptions" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "-frtti" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
set_source_files_properties(${VVV_CXX_SRC} PROPERTIES COMPILE_FLAGS "-std=c++98 -fno-exceptions -fno-rtti")
# Dependencies (as needed)
set_source_files_properties(${FAUDIO_SRC} PROPERTIES COMPILE_FLAGS -std=c99)
set_source_files_properties(${CHM_SRC} PROPERTIES COMPILE_FLAGS -std=c99)
endif()
# Unfortunately, it doesn't seem like distros package LodePNG
@ -276,48 +347,143 @@ add_library(lodepng-static STATIC ${PNG_SRC})
target_compile_definitions(lodepng-static PRIVATE
-DLODEPNG_NO_COMPILE_ALLOCATORS
-DLODEPNG_NO_COMPILE_DISK
-DLODEPNG_NO_COMPILE_ENCODER
)
add_library(c-hashmap-static STATIC ${CHM_SRC})
add_library(sheenbidi-static STATIC ${SBIDI_SRC})
target_compile_definitions(sheenbidi-static PRIVATE
-DSB_CONFIG_UNITY
)
target_include_directories(sheenbidi-static PRIVATE
../third_party/SheenBidi/Headers
)
if(BUNDLE_DEPENDENCIES)
list(APPEND STATIC_LIBRARIES physfs-static tinyxml2-static lodepng-static c-hashmap-static faudio-static sheenbidi-static)
else()
list(APPEND STATIC_LIBRARIES lodepng-static c-hashmap-static sheenbidi-static)
endif()
if(BUNDLE_DEPENDENCIES)
add_library(tinyxml2-static STATIC ${XML2_SRC})
add_library(physfs-static STATIC ${PFS_SRC})
target_compile_definitions(physfs-static PRIVATE
-DPHYSFS_SUPPORTS_DEFAULT=0 -DPHYSFS_SUPPORTS_ZIP=1
)
add_library(faudio-static STATIC ${FAUDIO_SRC})
target_include_directories(
faudio-static PRIVATE
../third_party/FAudio/include
)
# Disable FAudio debug stuff in release mode. This needs a generator expression for CMake reasons(TM)
target_compile_definitions(faudio-static PRIVATE $<$<CONFIG:Release>:FAUDIO_DISABLE_DEBUGCONFIGURATION>)
# PhysFS needs some extensions...
if(${CMAKE_VERSION} VERSION_GREATER "3.1.3"
OR ${CMAKE_VERSION} VERSION_EQUAL "3.1.3")
set_property(TARGET physfs-static PROPERTY C_EXTENSIONS ON)
target_link_libraries(VVVVVV ${STATIC_LIBRARIES})
else()
target_link_libraries(VVVVVV ${STATIC_LIBRARIES} physfs tinyxml2 FAudio)
endif()
if(MSVC)
# Statically link Microsoft's runtime library so end users don't have to install it (/MT)
# Also, build with multiple processors (/MP)
list(APPEND GLOBAL_COMPILE_FLAGS /MT /MP)
endif()
if(REMOVE_ABSOLUTE_PATHS)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 3.8)
set(SUPPORTS_DEBUG_PREFIX_MAP TRUE)
else()
set(SUPPORTS_DEBUG_PREFIX_MAP FALSE)
endif()
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 10.0)
set(SUPPORTS_FILE_PREFIX_MAP TRUE)
else()
set(SUPPORTS_FILE_PREFIX_MAP FALSE)
endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(SUPPORTS_DEBUG_PREFIX_MAP TRUE)
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0)
set(SUPPORTS_FILE_PREFIX_MAP TRUE)
else()
set(SUPPORTS_FILE_PREFIX_MAP FALSE)
endif()
else()
set(SUPPORTS_DEBUG_PREFIX_MAP FALSE)
set(SUPPORTS_FILE_PREFIX_MAP FALSE)
endif()
target_link_libraries(VVVVVV physfs-static tinyxml2-static lodepng-static)
else()
find_package(utf8cpp CONFIG)
get_filename_component(REPO_DIR ../ ABSOLUTE)
target_link_libraries(VVVVVV physfs tinyxml2 utf8cpp lodepng-static)
# Remove absolute source paths from compiled binary
if(SUPPORTS_FILE_PREFIX_MAP)
list(APPEND GLOBAL_COMPILE_FLAGS -ffile-prefix-map=${REPO_DIR}=.)
elseif(SUPPORTS_DEBUG_PREFIX_MAP)
list(APPEND GLOBAL_COMPILE_FLAGS -fdebug-prefix-map=${REPO_DIR}=.)
endif()
endif()
target_compile_options(VVVVVV PRIVATE ${GLOBAL_COMPILE_FLAGS})
foreach(static_library IN LISTS STATIC_LIBRARIES)
target_compile_options(${static_library} PRIVATE ${GLOBAL_COMPILE_FLAGS})
endforeach(static_library)
# SDL2 Dependency (Detection pulled from FAudio)
if(DEFINED SDL2_INCLUDE_DIRS AND DEFINED SDL2_LIBRARIES)
message(STATUS "Using pre-defined SDL2 variables SDL2_INCLUDE_DIRS and SDL2_LIBRARIES")
target_include_directories(VVVVVV SYSTEM PRIVATE "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
target_link_libraries(VVVVVV ${SDL2_LIBRARIES})
if(BUNDLE_DEPENDENCIES)
target_include_directories(faudio-static SYSTEM PRIVATE "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
target_link_libraries(faudio-static ${SDL2_LIBRARIES})
endif()
elseif (EMSCRIPTEN)
message(STATUS "Using Emscripten SDL2")
target_compile_options(VVVVVV PUBLIC -sUSE_SDL=2)
target_link_libraries(VVVVVV -sUSE_SDL=2)
if(BUNDLE_DEPENDENCIES)
target_compile_options(faudio-static PUBLIC -sUSE_SDL=2)
target_link_libraries(faudio-static -sUSE_SDL=2)
endif()
elseif(DEFINED SDL2_FRAMEWORK)
message(STATUS "Using pre-defined SDL2 variable SDL2_FRAMEWORK")
target_include_directories(VVVVVV SYSTEM PRIVATE "$<BUILD_INTERFACE:${SDL2_FRAMEWORK}/Headers>")
target_link_libraries(VVVVVV ${SDL2_FRAMEWORK})
if(BUNDLE_DEPENDENCIES)
target_include_directories(faudio-static SYSTEM PRIVATE "$<BUILD_INTERFACE:${SDL2_FRAMEWORK}/Headers>")
target_link_libraries(faudio-static ${SDL2_FRAMEWORK})
endif()
set_target_properties(VVVVVV PROPERTIES XCODE_EMBED_FRAMEWORKS ${SDL2_FRAMEWORK})
else()
# Only try to autodetect if both SDL2 variables aren't explicitly set
find_package(SDL2 CONFIG)
if(TARGET SDL2::SDL2)
message(STATUS "Using TARGET SDL2::SDL2")
target_link_libraries(VVVVVV SDL2::SDL2 SDL2_mixer)
target_link_libraries(VVVVVV SDL2::SDL2)
if(BUNDLE_DEPENDENCIES)
target_link_libraries(faudio-static SDL2::SDL2)
endif()
elseif(TARGET SDL2)
message(STATUS "Using TARGET SDL2")
target_link_libraries(VVVVVV SDL2 SDL2_mixer)
target_link_libraries(VVVVVV SDL2)
if(BUNDLE_DEPENDENCIES)
target_link_libraries(faudio-static SDL2)
endif()
else()
message(STATUS "No TARGET SDL2::SDL2, or SDL2, using variables")
find_path(SDL2_MIXER_INCLUDE_DIRS NAMES SDL_mixer.h PATH_SUFFIXES SDL2)
target_include_directories(VVVVVV SYSTEM PRIVATE "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>" ${SDL2_MIXER_INCLUDE_DIRS})
target_link_libraries(VVVVVV ${SDL2_LIBRARIES} SDL2_mixer)
target_include_directories(VVVVVV SYSTEM PRIVATE "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
target_link_libraries(VVVVVV ${SDL2_LIBRARIES})
if(BUNDLE_DEPENDENCIES)
target_include_directories(faudio-static SYSTEM PRIVATE "$<BUILD_INTERFACE:${SDL2_INCLUDE_DIRS}>")
target_link_libraries(faudio-static ${SDL2_LIBRARIES})
endif()
endif()
endif()
@ -333,3 +499,7 @@ if(HAIKU)
find_library(ROOT_LIBRARY root)
target_link_libraries(VVVVVV ${BE_LIBRARY} ${ROOT_LIBRARY})
endif()
if(EMSCRIPTEN)
# 256MB is enough for everybody
target_link_libraries(VVVVVV -sFORCE_FILESYSTEM=1 -sTOTAL_MEMORY=256MB)
endif()

View file

@ -3,22 +3,24 @@ Contributors
(Ordered alphabetically by first name.)
* Alexandra Fox
* AlexApps99 (@AlexApps99)
* Allison Fleischer (AllisonFleischer)
* AllyTally (@AllyTally)
* Brian Callahan (@ibara)
* Charlie Bruce (@charliebruce)
* Christoph Böhmwalder (@chrboe)
* Daniel Lee (@ddm999)
* Dav999 (@Dav999-v)
* Dav999 (@Daaaav)
* Elijah Stone (@moon-chilled)
* Elliott Saltar (@eboyblue3)
* Emmanuel Vadot (@evadot)
* fraZ0R (@f-raZ0R)
* Fredrik Ljungdahl (@FredrIQ)
* iliana etaoin (@iliana)
* Jules de Sartiges (@strikersh)
* Keith Stellyes (@keithstellyes)
* KyoZM (@lsaa)
* leo60228 (@leo60228)
* leo vriska (@leo60228)
* MAO3J1m0Op (@MAO3J1m0Op)
* Malte Grimm (@trelbutate)
* Marvin Scholz (@ePirat)
@ -29,8 +31,11 @@ Contributors
* Pierre-Alain TORET (@daftaupe)
* Reese Rivers (@ReeseTheRivers)
* Rémi Verschelde (@akien-mga)
* SnDream (@SnDream)
* Space-G (@Space-G)
* Thomas Sänger (@HorayNarea)
* Tynan Richards (@tzann)
* Wouter (@Xesxen)
* viri (viri.moe)
* Vittorio Romeo (@SuperV1234)
* Yussur Mustafa Oraji (@N00byKing)

View file

@ -2,12 +2,9 @@ FROM centos:7
# run first to improve caching (other things update more often than SDL2)
WORKDIR /tmp
RUN curl -O https://www.libsdl.org/release/SDL2-2.0.14.tar.gz
RUN tar -xf SDL2-2.0.14.tar.gz
RUN mkdir SDL2-2.0.14/build
RUN curl -O https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-2.0.4.tar.gz
RUN tar -xf SDL2_mixer-2.0.4.tar.gz
RUN mkdir SDL2_mixer-2.0.4/build
RUN curl -LJO https://github.com/libsdl-org/SDL/releases/download/release-2.24.0/SDL2-2.24.0.tar.gz
RUN tar -xf SDL2-2.24.0.tar.gz
RUN mkdir SDL2-2.24.0/build
# add EPEL (for SDL2)
RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
@ -16,8 +13,6 @@ RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.n
RUN yum -y install \
# used below
yum-utils \
# SDL2_mixer dependencies
libogg-devel libvorbis-devel \
# VVVVVV dependencies
gcc-c++ cmake make
@ -25,17 +20,12 @@ RUN yum-builddep -y SDL2
RUN yum clean all
WORKDIR /tmp/SDL2-2.0.14/build
RUN ../configure
RUN make -j $(nproc)
RUN make install
WORKDIR /tmp/SDL2_mixer-2.0.4/build
WORKDIR /tmp/SDL2-2.24.0/build
RUN ../configure
RUN make -j $(nproc)
RUN make install
WORKDIR /tmp
RUN rm -rf SDL2-2.0.14.tar.gz SDL2-2.0.14/ SDL2_mixer-2.0.4.tar.gz SDL2_mixer-2.0.4/
RUN rm -rf SDL2-2.24.0.tar.gz SDL2-2.24.0/
WORKDIR /

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIFileSharingEnabled</key>
<true/>
</dict>
</plist>

View file

@ -6,83 +6,56 @@ VVVVVV's official desktop versions are built with the following environments:
- macOS: Xcode CLT, currently targeting 10.9 SDK
- GNU/Linux: CentOS 7
The engine depends solely on [SDL2](https://libsdl.org/) 2.0.14+ and
[SDL2_mixer](https://www.libsdl.org/projects/SDL_mixer/). All other dependencies
The engine depends solely on [SDL2](https://libsdl.org/) 2.24.0+. All other dependencies
are statically linked into the engine. The development libraries for Windows can
be downloaded from their respective websites, Linux developers can find the dev
libraries from their respective repositories, and macOS developers should
compile and install from source (including libogg/libvorbis/libvorbisfile).
be downloaded from SDL's website, Linux developers can find the dev libraries from their
respective repositories, and macOS developers should compile and install from source.
(If you're on Ubuntu and your Ubuntu is too old to have this SDL version, then
[see here](https://github.com/TerryCavanagh/VVVVVV/issues/618#issuecomment-968338212)
for workarounds.)
Since VVVVVV 2.4, git submodules are used for the
[third party libraries](https://github.com/TerryCavanagh/VVVVVV/tree/master/third_party).
After cloning, run `git submodule update --init` to set all of these up.
You can also use this command whenever the submodules need to be updated.
Steamworks support is included and the DLL is loaded dynamically, you do not
need the SDK headers and there is no special Steam or non-Steam version. The
current implementation has been tested with Steamworks SDK v1.46.
To build the Make and Play edition of the game, uncomment `#define MAKEANDPLAY`
in `MakeAndPlay.h`.
To generate the projects on Windows:
```
# Put your SDL2/SDL2_mixer folders somewhere nice!
mkdir flibitBuild
cd flibitBuild
cmake -A Win32 -G "Visual Studio 10 2010" .. -DSDL2_INCLUDE_DIRS="C:\SDL2-2.0.14\include;C:\SDL2_mixer-2.0.4\include" -DSDL2_LIBRARIES="C:\SDL2-2.0.14\lib\x86\SDL2;C:\SDL2-2.0.14\lib\x86\SDL2main;C:\SDL2_mixer-2.0.4\lib\x86\SDL2_mixer"
# Put your SDL2 folders somewhere nice!
mkdir build
cd build
cmake -A Win32 -G "Visual Studio 10 2010" .. -DSDL2_INCLUDE_DIRS="C:\SDL2-2.24.0\include" -DSDL2_LIBRARIES="C:\SDL2-2.24.0\lib\x86\SDL2;C:\SDL2-2.24.0\lib\x86\SDL2main"
```
Note that on some systems, the `SDL2_LIBRARIES` list on Windows may need
SDL2/SDL2main/SDL2_mixer to have `.lib` at the end of them. The reason for this
inconsistency is unknown.
Then to compile the game, open the solution and click Build.
Also note that if you're using a Visual Studio later than 2010, you will need to
change the `-G` string accordingly; otherwise you will get a weird cryptic
error. Refer to the list below:
- VS 2012: `"Visual Studio 11 2012"`
- VS 2013: `"Visual Studio 12 2013"`
- VS 2015: `"Visual Studio 14 2015"`
- VS 2017: `"Visual Studio 15 2017"`
- VS 2019: `"Visual Studio 16 2019"`
- VS 2022: `"Visual Studio 17 2022"`
For more detailed information and troubleshooting, see the [Compiling VVVVVV
Guide](https://vsix.dev/wiki/Guide:Compiling_VVVVVV_on_Windows_with_Visual_Studio)
on the Viki.
To generate everywhere else:
```
mkdir flibitBuild
cd flibitBuild
mkdir build
cd build
cmake ..
```
macOS may be fussy about the SDK version. How to fix this is up to the whims of
however Apple wants to make CMAKE_OSX_SYSROOT annoying to configure and retain
each time Xcode updates.
Then to compile the game, type `make`.
Including data.zip
------------
You'll need the data.zip file from VVVVVV to actually run the game! It's
available to download separately for free in the
[Make and Play](https://thelettervsixtim.es/makeandplay/)
edition of the game. Put this file next to your executable and the game should
run.
You'll need the data.zip file from VVVVVV to actually run the game! You can grab
it from your copy of the game, or you can just download it for free from the [Make
and Play](https://thelettervsixtim.es/makeandplay/) page. Put this file next to
your executable and the game should run.
This is intended for personal use only - our license doesn't allow you to
actually distribute this data.zip file with your own forks without getting
permission from us first. See [LICENSE.md](../LICENSE.md) for more details. (If
you've got a project in mind that requires distributing this
file, [get in touch](http://distractionware.com/email/)!)
A Word About Compiler Quirks
----------------------------
_(Note: This section only applies to version 2.2 of the source code, which is
the initial commit of this repository. Since then, much hard work has been put
in to fix many undefined behaviors. If you're compiling the latest version of
the source code, ignore this section.)_
This engine is _super_ fussy about optimization levels and runtime checks. In
particular, the Windows version _absolutely positively must_ be compiled in
Debug mode, with /RTC enabled. If you build in Release mode, or have /RTC
disabled, the game behaves dramatically different in ways that were never fully
documented (bizarre softlocks, out-of-bounds issues that don't show up in tools
like Valgrind, stuff like that). There are lots of things about this old code
that could be cleaned up, polished, rewritten, and so on, but this is the one
that will probably bite you the hardest when setting up your own build,
regardless of platform.
We hope you'll enjoy messing with the source anyway!
Love, flibit
permission from us first. See [LICENSE.md](../LICENSE.md) for more details.

View file

@ -0,0 +1,87 @@
Translators
------------
Arabic (Eternal Dream Arabization):
- Montassar Ghanmi
- Mohammed Seif Eddine Chaib
Catalan:
- Eduard Ereza Martínez
Welsh:
- Morgan Roberts
German:
- Thomas Faust
Esperanto:
- Reese Rivers
Spanish (ES):
- Felipe Mercader
- Sara Marín
Spanish (LATAM):
- LocQuest
- Guido Di Carlo
Spanish (ARG.):
- LocQuest
- Guido Di Carlo
French:
- Words of Magic
Irish:
- Úna-Minh Kavanagh
- Seaghán Ó Modhráin
- Davis Sandefur
Italian (gloc.team):
- Alain Dellepiane
- Matteo Scarabelli
- Lorenzo Bertolucci
- Fabio Bortolotti
Japanese:
- Nicalis, Inc.
- KabanFriends
Korean:
- Bada Im
- Hyungseok Cho
Dutch:
- Dav999
Polish:
- Kuba Kallus
Brazilian Portuguese:
Translators:
- Lucas Araujo
- Thiago Araujo
Editing and LQA:
- Ivan Lopes
- Lucas Nunes
European Portuguese:
- Locsmiths
Russian:
- TheMysticSword
Silesian:
- Kuba Kallus
Turkish:
- Engin İlkiz
Ukrainian:
- Olya Sushytska
Chinese (Simplified):
- Sound of Mystery
Chinese (Traditional):
- Sound of Mystery / craft

View file

@ -0,0 +1,34 @@
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Log/OS Files
*.log
# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.apk
output.json
output-metadata.json
# IntelliJ
*.iml
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
google-services.json
# Android Profiling
*.hprof

View file

@ -0,0 +1,37 @@
Android port for C++ version
============================
This is _not_ the other existing mobile version (which is a fork of the original Flash
version). This is a port of the C++ desktop version, because the C++ version has gotten
many improvements since the source release (along with Flash being deprecated in
general).
How to Build
------------
The recommended way is to install Android Studio and Maven. These instructions are for
SDL 2.30.8; adapt your SDL version accordingly.
1. Place a copy of `data.zip` in `desktop_version/VVVVVV-android/app/src/main/assets/`.
(If the `assets/` folder doesn't exist, then create it.)
2. Obtain the SDL 2.30.8 Maven package. As of writing, SDL currently does not publish
Maven packages, so here is one way to obtain them (other methods are possible):
1. Download the SDL 2.30.8 source code.
2. Run the `build-scripts/android-prefab.sh` script in the SDL repository.
3. After building, run `mvn install:install-file
-Dfile=build-android-prefab/prefab-2.30.8/SDL2-2.30.8.aar
-DpomFile=build-android-prefab/prefab-2.30.8/SDL2-2.30.8.pom` to install it to
Maven Local.
3. Open the `desktop_version/VVVVVV-android/` folder in Android Studio.
4. Click 'Build'.
How to Install and Play
-----------------------
After building, go to `desktop_version/VVVVVV-android/app/build/outputs/apk/`, copy the
APK of the appropriate architecture to your device, and install it.
Touchscreen support is currently not implemented, so you will need to connect a keyboard
or controller to your device in order to play the game.

View file

@ -0,0 +1,114 @@
def buildAsLibrary = project.hasProperty('BUILD_AS_LIBRARY');
def buildAsApplication = !buildAsLibrary
if (buildAsApplication) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
android {
if (buildAsApplication) {
namespace "com.distractionware.vvvvvv"
}
compileSdkVersion 34
defaultConfig {
minSdkVersion 29
targetSdkVersion 34
versionCode 20005000
versionName "2.5"
applicationId "air.com.distractionware.vvvvvvmobile"
externalNativeBuild {
cmake {
arguments "-DANDROID_STL=c++_shared"
}
}
}
buildFeatures {
prefab true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
applicationVariants.all { variant ->
tasks["merge${variant.name.capitalize()}Assets"]
.dependsOn("externalNativeBuild${variant.name.capitalize()}")
}
if (!project.hasProperty('EXCLUDE_NATIVE_LIBS')) {
sourceSets.main {
jniLibs.srcDir 'libs'
}
externalNativeBuild {
cmake {
path 'jni/CMakeLists.txt'
}
}
}
lint {
abortOnError false
}
aaptOptions {
noCompress 'zip'
}
if (buildAsLibrary) {
libraryVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith(".aar")) {
def fileName = "org.libsdl.app.aar";
output.outputFile = new File(outputFile.parent, fileName);
}
}
}
}
splits {
abi {
enable true
}
}
sourceSets {
main {
assets {
srcDir layout.buildDirectory.dir("generated/main/assets")
}
}
}
}
def zipRepoAssetsTask = tasks.register("zipRepoAssets", Zip) {
from('../../fonts') { spec ->
spec.into('graphics')
}
from('../../lang') { spec ->
spec.into('lang')
}
archiveFileName.set('repo.zip')
destinationDirectory.value(layout.buildDirectory.dir("generated/main/assets"))
}
project.android.applicationVariants.configureEach { variant ->
def compressAssetsTask = project.tasks.named("merge${variant.name.capitalize()}Assets")
compressAssetsTask.configure { task ->
task.dependsOn zipRepoAssetsTask
}
}
afterEvaluate {
[lintReportDebug, lintAnalyzeDebug, lintReportRelease, lintAnalyzeRelease, lintVitalReportRelease, lintVitalAnalyzeRelease].each { task ->
task.dependsOn zipRepoAssetsTask
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'org.jetbrains:annotations:15.0'
implementation 'androidx.core:core:1.10.1'
implementation 'androidx.exifinterface:exifinterface:1.3.6'
implementation 'org.libsdl.android:SDL2:2.30.8'
}

View file

@ -0,0 +1 @@
include $(call all-subdir-makefiles)

View file

@ -0,0 +1,10 @@
# Uncomment this if you're using STL in your project
# You can find more information here:
# https://developer.android.com/ndk/guides/cpp-support
# APP_STL := c++_shared
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
# Min runtime API level
APP_PLATFORM=android-16

View file

@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.6)
project(VVVVVV_android)
# armeabi-v7a requires cpufeatures library
# include(AndroidNdkModules)
# android_ndk_import_module_cpufeatures()
# Compilation of companion libraries
#add_subdirectory(SDL_image)
#add_subdirectory(SDL_mixer)
#add_subdirectory(SDL_ttf)
add_subdirectory(../../.. desktop_version)

View file

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in [sdk]/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View file

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Replace com.test.game with the identifier of your game below, e.g.
com.gamemaker.game
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
android:installLocation="auto">
<!-- OpenGL ES 2.0 -->
<uses-feature android:glEsVersion="0x00020000" />
<!-- Touchscreen support -->
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<!-- Game controller support -->
<uses-feature
android:name="android.hardware.bluetooth"
android:required="false" />
<uses-feature
android:name="android.hardware.gamepad"
android:required="false" />
<uses-feature
android:name="android.hardware.usb.host"
android:required="false" />
<!-- External mouse input events -->
<uses-feature
android:name="android.hardware.type.pc"
android:required="false" />
<!-- Audio recording support -->
<!-- if you want to capture audio, uncomment this. -->
<!-- <uses-feature
android:name="android.hardware.microphone"
android:required="false" /> -->
<!-- Allow downloading to the external storage on Android 5.1 and older -->
<!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="22" /> -->
<!-- Allow access to Bluetooth devices -->
<!-- Currently this is just for Steam Controller support and requires setting SDL_HINT_JOYSTICK_HIDAPI_STEAM -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> -->
<!-- <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> -->
<!-- Allow access to the vibrator -->
<uses-permission android:name="android.permission.VIBRATE" />
<!-- if you want to capture audio, uncomment this. -->
<!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->
<!-- Create a Java class extending SDLActivity and place it in a
directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java
then replace "SDLActivity" with the name of your class (e.g. "MyGame")
in the XML below.
An example Java class can be found in README-android.md
-->
<application android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:hardwareAccelerated="true" >
<!-- Example of setting SDL hints from AndroidManifest.xml:
<meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/>
-->
<meta-data android:name="SDL_ENV.SDL_IOS_ORIENTATIONS" android:value="LandscapeLeft LandscapeRight"/>
<activity android:name="VVVVVV"
android:label="@string/app_name"
android:alwaysRetainTaskState="true"
android:launchMode="singleInstance"
android:configChanges="layoutDirection|locale|orientation|uiMode|screenLayout|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
android:preferMinimalPostProcessing="true"
android:screenOrientation="landscape"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Let Android know that we can handle some USB devices and should receive this event -->
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<!-- Drop file event -->
<!--
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
-->
</activity>
<provider
android:name="com.distractionware.vvvvvv.BaseDirProvider"
android:authorities="com.distractionware.vvvvvv.basedir"
android:grantUriPermissions="true"
android:exported="true"
android:permission="android.permission.MANAGE_DOCUMENTS">
<intent-filter>
<action android:name="android.content.action.DOCUMENTS_PROVIDER" />
</intent-filter>
</provider>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

View file

@ -0,0 +1,85 @@
package com.distractionware.vvvvvv;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import java.io.File;
import java.io.FileNotFoundException;
public class BaseDirProvider extends FileSystemProvider {
private static final String AUTHORITY = "com.distractionware.vvvvvv.basedir";
private static final String DOC_ID_ROOT = "vvvvvv";
private static final String[] DEFAULT_ROOT_PROJECTION = new String[]{
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
Root.COLUMN_DOCUMENT_ID,
};
private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[]{
Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
};
private File mRoot;
@Override
public boolean onCreate() {
super.onCreate(DEFAULT_DOCUMENT_PROJECTION);
mRoot = getContext().getExternalFilesDir(null);
return true;
}
@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
final RowBuilder row = result.newRow();
row.add(Root.COLUMN_ROOT_ID, DOC_ID_ROOT);
row.add(Root.COLUMN_FLAGS,
Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_SEARCH |
Root.FLAG_SUPPORTS_IS_CHILD);
row.add(Root.COLUMN_ICON, R.mipmap.ic_launcher);
row.add(Root.COLUMN_TITLE, "VVVVVV");
row.add(Root.COLUMN_DOCUMENT_ID, DOC_ID_ROOT);
return result;
}
@Override
protected Uri buildNotificationUri(String docId) {
return DocumentsContract.buildChildDocumentsUri(AUTHORITY, docId);
}
private static String[] resolveRootProjection(String[] projection) {
return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
}
private static String[] resolveDocumentProjection(String[] projection) {
return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
}
@Override
protected String getDocIdForFile(File file) {
return "raw:" + file.getAbsolutePath();
}
@Override
protected File getFileForDocId(String documentId, boolean visible)
throws FileNotFoundException {
if (DOC_ID_ROOT.equals(documentId)) {
return mRoot;
} else if (documentId.startsWith("raw:")) {
final String name = documentId.substring(4);
final File file = new File(name);
if (!file.exists()) {
throw new FileNotFoundException("File not found: " + name);
}
return file;
} else {
throw new FileNotFoundException("Invalid document ID: " + documentId);
}
}
}

View file

@ -0,0 +1,936 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.distractionware.vvvvvv;
import static android.provider.DocumentsContract.EXTRA_ORIENTATION;
import static android.provider.DocumentsContract.QUERY_ARG_DISPLAY_NAME;
import static android.provider.DocumentsContract.QUERY_ARG_EXCLUDE_MEDIA;
import static android.provider.DocumentsContract.QUERY_ARG_FILE_SIZE_OVER;
import static android.provider.DocumentsContract.QUERY_ARG_LAST_MODIFIED_AFTER;
import static android.provider.DocumentsContract.QUERY_ARG_MIME_TYPES;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.graphics.Point;
import android.media.MediaScannerConnection;
import android.net.Uri;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.FileObserver;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsProvider;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.webkit.MimeTypeMap;
import androidx.core.content.MimeTypeFilter;
import androidx.exifinterface.media.ExifInterface;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Predicate;
/**
* A helper class for {@link android.provider.DocumentsProvider} to perform file operations on local
* files.
*/
public abstract class FileSystemProvider extends DocumentsProvider {
private static final String TAG = "FileSystemProvider";
private static final boolean LOG_INOTIFY = false;
protected static final String SUPPORTED_QUERY_ARGS = joinNewline(
QUERY_ARG_DISPLAY_NAME,
QUERY_ARG_FILE_SIZE_OVER,
QUERY_ARG_LAST_MODIFIED_AFTER,
QUERY_ARG_MIME_TYPES);
private static String joinNewline(String... args) {
return TextUtils.join("\n", args);
}
private String[] mDefaultProjection;
private final ArrayMap<File, DirectoryObserver> mObservers = new ArrayMap<>();
private Handler mHandler;
protected abstract File getFileForDocId(String docId, boolean visible)
throws FileNotFoundException;
protected abstract String getDocIdForFile(File file) throws FileNotFoundException;
protected abstract Uri buildNotificationUri(String docId);
/**
* Callback indicating that the given document has been modified. This gives the provider a hook
* to invalidate cached data, such as {@code sdcardfs}.
*/
protected void onDocIdChanged(String docId) {
// Default is no-op
}
/**
* Callback indicating that the given document has been deleted or moved. This gives the
* provider a hook to revoke the uri permissions.
*/
protected void onDocIdDeleted(String docId) {
// Default is no-op
}
@Override
public boolean onCreate() {
throw new UnsupportedOperationException(
"Subclass should override this and call onCreate(defaultDocumentProjection)");
}
protected void onCreate(String[] defaultProjection) {
mHandler = new Handler();
mDefaultProjection = defaultProjection;
}
private static boolean contains(String dirPath, String filePath) {
if (dirPath.equals(filePath)) {
return true;
}
if (!dirPath.endsWith("/")) {
dirPath += "/";
}
return filePath.startsWith(dirPath);
}
private static boolean contains(File dir, File file) {
if (dir == null || file == null) return false;
return contains(dir.getAbsolutePath(), file.getAbsolutePath());
}
@Override
public boolean isChildDocument(String parentDocId, String docId) {
try {
final File parent = getFileForDocId(parentDocId).getCanonicalFile();
final File doc = getFileForDocId(docId).getCanonicalFile();
return contains(parent, doc);
} catch (IOException e) {
throw new IllegalArgumentException(
"Failed to determine if " + docId + " is child of " + parentDocId + ": " + e);
}
}
protected final List<String> findDocumentPath(File parent, File doc)
throws FileNotFoundException {
if (!doc.exists()) {
throw new FileNotFoundException(doc + " is not found.");
}
if (!contains(parent, doc)) {
throw new FileNotFoundException(doc + " is not found under " + parent);
}
LinkedList<String> path = new LinkedList<>();
while (doc != null && contains(parent, doc)) {
path.addFirst(getDocIdForFile(doc));
doc = doc.getParentFile();
}
return path;
}
private static boolean isValidFatFilenameChar(char c) {
if ((0x00 <= c && c <= 0x1f)) {
return false;
}
switch (c) {
case '"':
case '*':
case '/':
case ':':
case '<':
case '>':
case '?':
case '\\':
case '|':
case 0x7F:
return false;
default:
return true;
}
}
private static String buildValidFatFilename(String name) {
if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
return "(invalid)";
}
final StringBuilder res = new StringBuilder(name.length());
for (int i = 0; i < name.length(); i++) {
final char c = name.charAt(i);
if (isValidFatFilenameChar(c)) {
res.append(c);
} else {
res.append('_');
}
}
// Even though vfat allows 255 UCS-2 chars, we might eventually write to
// ext4 through a FUSE layer, so use that limit.
trimFilename(res, 255);
return res.toString();
}
private static String trimFilename(String str, int maxBytes) {
final StringBuilder res = new StringBuilder(str);
trimFilename(res, maxBytes);
return res.toString();
}
private static void trimFilename(StringBuilder res, int maxBytes) {
byte[] raw = res.toString().getBytes(StandardCharsets.UTF_8);
if (raw.length > maxBytes) {
maxBytes -= 3;
while (raw.length > maxBytes) {
res.deleteCharAt(res.length() / 2);
raw = res.toString().getBytes(StandardCharsets.UTF_8);
}
res.insert(res.length() / 2, "...");
}
}
private static File buildFile(File parent, String name, String ext) {
if (TextUtils.isEmpty(ext)) {
return new File(parent, name);
} else {
return new File(parent, name + "." + ext);
}
}
private static File buildUniqueFileWithExtension(File parent, String name, String ext)
throws FileNotFoundException {
File file = buildFile(parent, name, ext);
// If conflicting file, try adding counter suffix
int n = 0;
while (file.exists()) {
if (n++ >= 32) {
throw new FileNotFoundException("Failed to create unique file");
}
file = buildFile(parent, name + " (" + n + ")", ext);
}
return file;
}
private static String[] splitFileName(String mimeType, String displayName) {
String name;
String ext;
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
name = displayName;
ext = null;
} else {
String mimeTypeFromExt;
// Extract requested extension from display name
final int lastDot = displayName.lastIndexOf('.');
if (lastDot >= 0) {
name = displayName.substring(0, lastDot);
ext = displayName.substring(lastDot + 1);
mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
ext.toLowerCase());
} else {
name = displayName;
ext = null;
mimeTypeFromExt = null;
}
if (mimeTypeFromExt == null) {
mimeTypeFromExt = "application/octet-stream";
}
final String extFromMimeType;
if ("application/octet-stream".equals(mimeType)) {
extFromMimeType = null;
} else {
extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
}
if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
// Extension maps back to requested MIME type; allow it
} else {
// No match; insist that create file matches requested MIME
name = displayName;
ext = extFromMimeType;
}
}
if (ext == null) {
ext = "";
}
return new String[]{name, ext};
}
private static File buildUniqueFile(File parent, String mimeType, String displayName)
throws FileNotFoundException {
final String[] parts = splitFileName(mimeType, displayName);
return buildUniqueFileWithExtension(parent, parts[0], parts[1]);
}
@Override
public String createDocument(String docId, String mimeType, String displayName)
throws FileNotFoundException {
displayName = buildValidFatFilename(displayName);
final File parent = getFileForDocId(docId);
if (!parent.isDirectory()) {
throw new IllegalArgumentException("Parent document isn't a directory");
}
final File file = buildUniqueFile(parent, mimeType, displayName);
final String childId;
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
if (!file.mkdir()) {
throw new IllegalStateException("Failed to mkdir " + file);
}
childId = getDocIdForFile(file);
onDocIdChanged(childId);
} else {
try {
if (!file.createNewFile()) {
throw new IllegalStateException("Failed to touch " + file);
}
childId = getDocIdForFile(file);
onDocIdChanged(childId);
} catch (IOException e) {
throw new IllegalStateException("Failed to touch " + file + ": " + e);
}
}
updateMediaStore(getContext(), file);
return childId;
}
private static File buildUniqueFile(File parent, String displayName)
throws FileNotFoundException {
final String name;
final String ext;
// Extract requested extension from display name
final int lastDot = displayName.lastIndexOf('.');
if (lastDot >= 0) {
name = displayName.substring(0, lastDot);
ext = displayName.substring(lastDot + 1);
} else {
name = displayName;
ext = null;
}
return buildUniqueFileWithExtension(parent, name, ext);
}
@Override
public String renameDocument(String docId, String displayName) throws FileNotFoundException {
// Since this provider treats renames as generating a completely new
// docId, we're okay with letting the MIME type change.
displayName = buildValidFatFilename(displayName);
final File before = getFileForDocId(docId);
final File beforeVisibleFile = getFileForDocId(docId, true);
final File after = buildUniqueFile(before.getParentFile(), displayName);
if (!before.renameTo(after)) {
throw new IllegalStateException("Failed to rename to " + after);
}
final String afterDocId = getDocIdForFile(after);
onDocIdChanged(docId);
onDocIdDeleted(docId);
onDocIdChanged(afterDocId);
final File afterVisibleFile = getFileForDocId(afterDocId, true);
updateMediaStore(getContext(), beforeVisibleFile);
updateMediaStore(getContext(), afterVisibleFile);
if (!TextUtils.equals(docId, afterDocId)) {
return afterDocId;
} else {
return null;
}
}
@Override
public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
String targetParentDocumentId)
throws FileNotFoundException {
final File before = getFileForDocId(sourceDocumentId);
final File after = new File(getFileForDocId(targetParentDocumentId), before.getName());
final File visibleFileBefore = getFileForDocId(sourceDocumentId, true);
if (after.exists()) {
throw new IllegalStateException("Already exists " + after);
}
if (!before.renameTo(after)) {
throw new IllegalStateException("Failed to move to " + after);
}
final String docId = getDocIdForFile(after);
onDocIdChanged(sourceDocumentId);
onDocIdDeleted(sourceDocumentId);
onDocIdChanged(docId);
// update the database
updateMediaStore(getContext(), visibleFileBefore);
updateMediaStore(getContext(), getFileForDocId(docId, true));
return docId;
}
private static void updateMediaStore(@NotNull Context context, File file) {
if (file != null) {
final ContentResolver resolver = context.getContentResolver();
final String noMedia = ".nomedia";
// For file, check whether the file name is .nomedia or not.
// If yes, scan the parent directory to update all files in the directory.
if (!file.isDirectory() && file.getName().toLowerCase(Locale.ROOT).endsWith(noMedia)) {
MediaScannerConnection.scanFile(context, new String[]{file.getParent()}, null,
null);
} else {
MediaScannerConnection.scanFile(context, new String[]{file.getParent()}, null,
null);
}
}
}
private static boolean deleteContents(File dir) {
File[] files = dir.listFiles();
boolean success = true;
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
success &= deleteContents(file);
}
if (!file.delete()) {
Log.w(TAG, "Failed to delete " + file);
success = false;
}
}
}
return success;
}
@Override
public void deleteDocument(String docId) throws FileNotFoundException {
final File file = getFileForDocId(docId);
final File visibleFile = getFileForDocId(docId, true);
final boolean isDirectory = file.isDirectory();
if (isDirectory) {
deleteContents(file);
}
// We could be deleting pending media which doesn't have any content yet, so only throw
// if the file exists and we fail to delete it.
if (file.exists() && !file.delete()) {
throw new IllegalStateException("Failed to delete " + file);
}
onDocIdChanged(docId);
onDocIdDeleted(docId);
updateMediaStore(getContext(), visibleFile);
}
@Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
includeFile(result, documentId, null);
return result;
}
/**
* This method is similar to
* {@link DocumentsProvider#queryChildDocuments(String, String[], String)}. This method returns
* all children documents including hidden directories/files.
*
* <p>
* In a scoped storage world, access to "Android/data" style directories are hidden for privacy
* reasons. This method may show privacy sensitive data, so its usage should only be in
* restricted modes.
*
* @param parentDocumentId the directory to return children for.
* @param projection list of {@link Document} columns to put into the cursor. If
* {@code null} all supported columns should be included.
* @param sortOrder how to order the rows, formatted as an SQL {@code ORDER BY} clause
* (excluding the ORDER BY itself). Passing {@code null} will use the
* default sort order, which may be unordered. This ordering is a hint
* that can be used to prioritize how data is fetched from the network,
* but UI may always enforce a specific ordering
* @throws FileNotFoundException when parent document doesn't exist or query fails
*/
protected Cursor queryChildDocumentsShowAll(
String parentDocumentId, String[] projection, String sortOrder)
throws FileNotFoundException {
return queryChildDocuments(parentDocumentId, projection, sortOrder, File -> true);
}
@Override
public Cursor queryChildDocuments(
String parentDocumentId, String[] projection, String sortOrder)
throws FileNotFoundException {
// Access to some directories is hidden for privacy reasons.
return queryChildDocuments(parentDocumentId, projection, sortOrder, this::shouldShow);
}
private static @NotNull File[] listFilesOrEmpty(@Nullable File dir) {
if (dir == null) {
return new File[]{};
} else {
File[] files = dir.listFiles();
if (files == null) {
return new File[]{};
} else {
return files;
}
}
}
private Cursor queryChildDocuments(
String parentDocumentId, String[] projection, String sortOrder,
@NotNull Predicate<File> filter) throws FileNotFoundException {
final File parent = getFileForDocId(parentDocumentId);
final MatrixCursor result = new DirectoryCursor(
resolveProjection(projection), parentDocumentId, parent);
if (!filter.test(parent)) {
Log.w(TAG, "No permission to access parentDocumentId: " + parentDocumentId);
return result;
}
if (parent.isDirectory()) {
for (File file : listFilesOrEmpty(parent)) {
if (filter.test(file)) {
includeFile(result, null, file);
}
}
} else {
Log.w(TAG, "parentDocumentId '" + parentDocumentId + "' is not Directory");
}
return result;
}
private static String[] getHandledQueryArguments(Bundle queryArgs) {
if (queryArgs == null) {
return new String[0];
}
final ArrayList<String> args = new ArrayList<>();
if (queryArgs.keySet().contains(QUERY_ARG_EXCLUDE_MEDIA)) {
args.add(QUERY_ARG_EXCLUDE_MEDIA);
}
if (queryArgs.keySet().contains(QUERY_ARG_DISPLAY_NAME)) {
args.add(QUERY_ARG_DISPLAY_NAME);
}
if (queryArgs.keySet().contains(QUERY_ARG_FILE_SIZE_OVER)) {
args.add(QUERY_ARG_FILE_SIZE_OVER);
}
if (queryArgs.keySet().contains(QUERY_ARG_LAST_MODIFIED_AFTER)) {
args.add(QUERY_ARG_LAST_MODIFIED_AFTER);
}
if (queryArgs.keySet().contains(QUERY_ARG_MIME_TYPES)) {
args.add(QUERY_ARG_MIME_TYPES);
}
return args.toArray(new String[0]);
}
/**
* Searches documents under the given folder.
* <p>
* To avoid runtime explosion only returns the at most 23 items.
*
* @param folder the root folder where recursive search begins
* @param projection projection of the returned cursor
* @param exclusion absolute file paths to exclude from result
* @param queryArgs the query arguments for search
* @return cursor containing search result. Include {@link ContentResolver#EXTRA_HONORED_ARGS}
* in {@link Cursor} extras {@link Bundle} when any QUERY_ARG_* value was honored during the
* preparation of the results.
* @throws FileNotFoundException when root folder doesn't exist or search fails
* @see ContentResolver#EXTRA_HONORED_ARGS
*/
protected final Cursor querySearchDocuments(
File folder, String[] projection, Set<String> exclusion, Bundle queryArgs)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
final LinkedList<File> pending = new LinkedList<>();
pending.add(folder);
while (!pending.isEmpty() && result.getCount() < 24) {
final File file = pending.removeFirst();
if (shouldHide(file)) continue;
if (file.isDirectory()) {
for (File child : listFilesOrEmpty(file)) {
pending.add(child);
}
}
if (!exclusion.contains(file.getAbsolutePath()) && matchSearchQueryArguments(file,
queryArgs)) {
includeFile(result, null, file);
}
}
final String[] handledQueryArgs = getHandledQueryArguments(queryArgs);
if (handledQueryArgs.length > 0) {
final Bundle extras = new Bundle();
extras.putStringArray(ContentResolver.EXTRA_HONORED_ARGS, handledQueryArgs);
result.setExtras(extras);
}
return result;
}
@Override
public String getDocumentType(String documentId) throws FileNotFoundException {
return getDocumentType(documentId, getFileForDocId(documentId));
}
private String getDocumentType(final String documentId, final File file)
throws FileNotFoundException {
if (file.isDirectory()) {
return Document.MIME_TYPE_DIR;
} else {
final int lastDot = documentId.lastIndexOf('.');
if (lastDot >= 0) {
final String extension = documentId.substring(lastDot + 1).toLowerCase();
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mime != null) {
return mime;
}
}
return "application/octet-stream";
}
}
@Override
public ParcelFileDescriptor openDocument(
String documentId, String mode, CancellationSignal signal)
throws FileNotFoundException {
final File file = getFileForDocId(documentId);
final File visibleFile = getFileForDocId(documentId, true);
final int pfdMode = ParcelFileDescriptor.parseMode(mode);
if (visibleFile == null || pfdMode == ParcelFileDescriptor.MODE_READ_ONLY) {
return ParcelFileDescriptor.open(file, pfdMode);
} else {
try {
// When finished writing, kick off media scanner
return ParcelFileDescriptor.open(
file, pfdMode, mHandler, (IOException e) -> {
onDocIdChanged(documentId);
scanFile(visibleFile);
});
} catch (IOException e) {
throw new FileNotFoundException("Failed to open for writing: " + e);
}
}
}
public static boolean matchSearchQueryArguments(Bundle queryArgs, String displayName,
String mimeType, long lastModified, long size) {
if (queryArgs == null) {
return true;
}
final String argDisplayName = queryArgs.getString(QUERY_ARG_DISPLAY_NAME, "");
if (!argDisplayName.isEmpty()) {
// TODO (118795812) : Enhance the search string handled in DocumentsProvider
if (!displayName.toLowerCase().contains(argDisplayName.toLowerCase())) {
return false;
}
}
final long argFileSize = queryArgs.getLong(QUERY_ARG_FILE_SIZE_OVER, -1 /* defaultValue */);
if (argFileSize != -1 && size < argFileSize) {
return false;
}
final long argLastModified = queryArgs.getLong(QUERY_ARG_LAST_MODIFIED_AFTER,
-1 /* defaultValue */);
if (argLastModified != -1 && lastModified < argLastModified) {
return false;
}
final String[] argMimeTypes = queryArgs.getStringArray(QUERY_ARG_MIME_TYPES);
if (argMimeTypes != null && argMimeTypes.length > 0) {
mimeType = Intent.normalizeMimeType(mimeType);
for (String type : argMimeTypes) {
if (MimeTypeFilter.matches(mimeType, Intent.normalizeMimeType(type))) {
return true;
}
}
return false;
}
return true;
}
/**
* Test if the file matches the query arguments.
*
* @param file the file to test
* @param queryArgs the query arguments
*/
private boolean matchSearchQueryArguments(File file, Bundle queryArgs) {
if (file == null) {
return false;
}
final String fileMimeType;
final String fileName = file.getName();
if (file.isDirectory()) {
fileMimeType = DocumentsContract.Document.MIME_TYPE_DIR;
} else {
int dotPos = fileName.lastIndexOf('.');
if (dotPos < 0) {
return false;
}
final String extension = fileName.substring(dotPos + 1);
fileMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return matchSearchQueryArguments(queryArgs, fileName, fileMimeType,
file.lastModified(), file.length());
}
private void scanFile(File visibleFile) {
final Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(visibleFile));
getContext().sendBroadcast(intent);
}
private static AssetFileDescriptor openImageThumbnail(File file) throws FileNotFoundException {
final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
file, ParcelFileDescriptor.MODE_READ_ONLY);
try {
final ExifInterface exif = new ExifInterface(file.getAbsolutePath());
final long[] thumb = exif.getThumbnailRange();
if (thumb != null) {
// If we use thumb to decode, we need to handle the rotation by ourselves.
Bundle extras = null;
switch (exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1)) {
case ExifInterface.ORIENTATION_ROTATE_90:
extras = new Bundle(1);
extras.putInt(EXTRA_ORIENTATION, 90);
break;
case ExifInterface.ORIENTATION_ROTATE_180:
extras = new Bundle(1);
extras.putInt(EXTRA_ORIENTATION, 180);
break;
case ExifInterface.ORIENTATION_ROTATE_270:
extras = new Bundle(1);
extras.putInt(EXTRA_ORIENTATION, 270);
break;
}
return new AssetFileDescriptor(pfd, thumb[0], thumb[1], extras);
}
} catch (IOException ignored) {
}
// Do full file decoding, we don't need to handle the orientation
return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH, null);
}
@Override
public AssetFileDescriptor openDocumentThumbnail(
String documentId, Point sizeHint, CancellationSignal signal)
throws FileNotFoundException {
final File file = getFileForDocId(documentId);
return openImageThumbnail(file);
}
protected RowBuilder includeFile(final MatrixCursor result, String docId, File file)
throws FileNotFoundException {
final String[] columns = result.getColumnNames();
final RowBuilder row = result.newRow();
if (docId == null) {
docId = getDocIdForFile(file);
} else {
file = getFileForDocId(docId);
}
final String mimeType = getDocumentType(docId, file);
row.add(Document.COLUMN_DOCUMENT_ID, docId);
row.add(Document.COLUMN_MIME_TYPE, mimeType);
final int flagIndex = Arrays.asList(columns).indexOf(Document.COLUMN_FLAGS);
if (flagIndex != -1) {
int flags = 0;
if (file.canWrite()) {
if (mimeType.equals(Document.MIME_TYPE_DIR)) {
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
flags |= Document.FLAG_SUPPORTS_DELETE;
flags |= Document.FLAG_SUPPORTS_RENAME;
flags |= Document.FLAG_SUPPORTS_MOVE;
} else {
flags |= Document.FLAG_SUPPORTS_WRITE;
flags |= Document.FLAG_SUPPORTS_DELETE;
flags |= Document.FLAG_SUPPORTS_RENAME;
flags |= Document.FLAG_SUPPORTS_MOVE;
}
}
if (mimeType.startsWith("image/")) {
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
}
if (typeSupportsMetadata(mimeType)) {
flags |= Document.FLAG_SUPPORTS_METADATA;
}
row.add(Document.COLUMN_FLAGS, flags);
}
final int displayNameIndex = Arrays.asList(columns).indexOf(Document.COLUMN_DISPLAY_NAME);
if (displayNameIndex != -1) {
row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
}
final int lastModifiedIndex = Arrays.asList(columns).indexOf(Document.COLUMN_LAST_MODIFIED);
if (lastModifiedIndex != -1) {
final long lastModified = file.lastModified();
// Only publish dates reasonably after epoch
if (lastModified > 31536000000L) {
row.add(Document.COLUMN_LAST_MODIFIED, lastModified);
}
}
final int sizeIndex = Arrays.asList(columns).indexOf(Document.COLUMN_SIZE);
if (sizeIndex != -1) {
row.add(Document.COLUMN_SIZE, file.length());
}
// Return the row builder just in case any subclass want to add more stuff to it.
return row;
}
protected boolean shouldHide(@NotNull File file) {
return false;
}
private boolean shouldShow(@NotNull File file) {
return !shouldHide(file);
}
protected boolean typeSupportsMetadata(String mimeType) {
return Document.MIME_TYPE_DIR.equals(mimeType);
}
protected final File getFileForDocId(String docId) throws FileNotFoundException {
return getFileForDocId(docId, false);
}
private String[] resolveProjection(String[] projection) {
return projection == null ? mDefaultProjection : projection;
}
private void startObserving(File file, Uri notifyUri, DirectoryCursor cursor) {
synchronized (mObservers) {
DirectoryObserver observer = mObservers.get(file);
if (observer == null) {
observer =
new DirectoryObserver(file, getContext().getContentResolver(), notifyUri);
observer.startWatching();
mObservers.put(file, observer);
}
observer.mCursors.add(cursor);
if (LOG_INOTIFY) Log.d(TAG, "after start: " + observer);
}
}
private void stopObserving(File file, DirectoryCursor cursor) {
synchronized (mObservers) {
DirectoryObserver observer = mObservers.get(file);
if (observer == null) return;
observer.mCursors.remove(cursor);
if (observer.mCursors.size() == 0) {
mObservers.remove(file);
observer.stopWatching();
}
if (LOG_INOTIFY) Log.d(TAG, "after stop: " + observer);
}
}
private static class DirectoryObserver extends FileObserver {
private static final int NOTIFY_EVENTS = ATTRIB | CLOSE_WRITE | MOVED_FROM | MOVED_TO
| CREATE | DELETE | DELETE_SELF | MOVE_SELF;
private final File mFile;
private final ContentResolver mResolver;
private final Uri mNotifyUri;
private final CopyOnWriteArrayList<DirectoryCursor> mCursors;
DirectoryObserver(File file, ContentResolver resolver, Uri notifyUri) {
super(file.getAbsolutePath(), NOTIFY_EVENTS);
mFile = file;
mResolver = resolver;
mNotifyUri = notifyUri;
mCursors = new CopyOnWriteArrayList<>();
}
@Override
public void onEvent(int event, String path) {
if ((event & NOTIFY_EVENTS) != 0) {
if (LOG_INOTIFY) Log.d(TAG, "onEvent() " + event + " at " + path);
for (DirectoryCursor cursor : mCursors) {
cursor.notifyChanged();
}
mResolver.notifyChange(mNotifyUri, null, false);
}
}
@Override
public String toString() {
String filePath = mFile.getAbsolutePath();
return "DirectoryObserver{file=" + filePath + ", ref=" + mCursors.size() + "}";
}
}
private class DirectoryCursor extends MatrixCursor {
private final File mFile;
public DirectoryCursor(String[] columnNames, String docId, File file) {
super(columnNames);
final Uri notifyUri = buildNotificationUri(docId);
boolean registerSelfObserver = false; // Our FileObserver sees all relevant changes.
setNotificationUris(getContext().getContentResolver(), Arrays.asList(notifyUri));
mFile = file;
startObserving(mFile, notifyUri, this);
}
public void notifyChanged() {
onChange(false);
}
@Override
public void close() {
super.close();
stopObserving(mFile, this);
}
}
}

View file

@ -0,0 +1,21 @@
package com.distractionware.vvvvvv;
import org.libsdl.app.SDLActivity;
public class VVVVVV extends SDLActivity {
@Override
protected String[] getLibraries() {
return new String[] {
"SDL2",
"VVVVVV"
};
}
@Override
protected String[] getArguments() {
// always pass -debug, logcat has level filtering anyway
return new String[] {
"-debug"
};
}
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#000000</color>
</resources>

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name">VVVVVV</string>
</resources>

View file

@ -0,0 +1,8 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

View file

@ -0,0 +1,26 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
mavenCentral()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.1.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenCentral()
google()
mavenLocal()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View file

@ -0,0 +1,20 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m -XX:-UseContainerSupport
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
android.useAndroidX=true
android.enableJetifier=true

Binary file not shown.

View file

@ -0,0 +1,6 @@
#Thu Nov 11 18:20:34 PST 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-rc-2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

160
desktop_version/VVVVVV-android/gradlew vendored Executable file
View file

@ -0,0 +1,160 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|grep -E -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|grep -E -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

View file

@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View file

@ -0,0 +1 @@
include ':app'

7
desktop_version/fixupMac.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/bash
# flibit has this solely for convenience when cleaning up Mac binaries.
install_name_tool -change /usr/local/lib/libSDL2-2.0.0.dylib @rpath/libSDL2-2.0.0.dylib VVVVVV
strip -S VVVVVV
otool -L VVVVVV

View file

@ -0,0 +1,57 @@
=== I N T R O D U C T I O N ===
This file will explain the font format.
If you need a font (like a TTF) converted into the format that the game can read, for now you might want to ask Dav, who has tools for it.
=== F O N T F O R M A T ===
Fonts consist of two files: a .png and a .fontmeta. The .png contains all the "images" for all glyphs, and the .fontmeta is an XML document containing all information about which characters are in the file and other metadata.
For example, a font for Japanese might be called font_ja.png and font_ja.fontmeta.
The fontmeta file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<font_metadata>
<display_name>日本語</display_name>
<width>12</width>
<height>12</height>
<white_teeth>1</white_teeth>
<chars>
<range start="0x20" end="0x7F"/>
<range start="0xA0" end="0x17F"/>
<range start="0x18F" end="0x18F"/>
<range start="0x218" end="0x21B"/>
<range start="0x259" end="0x25A"/>
<!-- ... -->
</chars>
<special>
<range start="0x00" end="0x1F" advance="6"/>
<range start="0xEB00" end="0xEBFF" color="1"/>
</special>
<fallback>buttons_12x12</fallback>
</font_metadata>
* type: not specified for normal fonts. <type>buttons</type> is used in button glyph fonts.
* display_name: the name of the language the font is specifically meant for - in the language itself. Users will see this in the level editor when choosing a font to use. If this font is used equally by multiple translations, this could be set to a combination like "繁體中文/한국어". (If you are creating a custom player level: don't worry about this)
* width/height: the width and height of each glyph in the font. Every character will always be drawn as a rectangle of this size. VVVVVV is rated to support fonts up to 12 pixels high - anything higher may cause text overlapping or not fitting in place.
* white_teeth: indicates that all characters in the font are white, so the game itself doesn't have to remove all color from the image and make all pixels white like it would in old versions of the game. If this is not set to 1, this font cannot have colored (button) glyphs, and the game has to specifically process the font every time it is loaded, so 1 is highly recommended.
* chars: defines which characters are in the image. Starting at the top left of the image, each character is simply a rectangle of equal size (defined in <width> and <height>) from left to right, top to bottom. In the example given above, the image first has Unicode every character from U+0020 up to and including U+007F, then every character from U+00A0 to U+017F, and so on. To include a single character, simply use a range with equal start and end attributes equal.
* special: defines special attributes that will be applied to a range of characters. One or more of the following attributes can be used:
- color: set to 1 if these glyphs should be drawn with its original colors (for button glyphs, or even emoji...)
- advance: instead of <width>, the cursor (for drawing the next character) should be advanced this amount of pixels to the right. This controls the width of a character, but it does not affect how characters are arranged in the image, and the full glyph will still be drawn. While this means the font system has support for variable-width fonts, it's recommended to not use this option. There are some problems arising from using a variable-width font (especially in text boxes), so please consider copying the font's fullwidth forms (U+FF01-U+FF5E) to ASCII U+0021-U+007E instead. One may argue that a monospaced font also fits more with the game's style.
* fallback: specifies the button glyphs font to use. Make sure to choose one that fits fully within your [width]x[height] rectangle, so for an 8x12 font, choose buttons_8x8, not buttons_12x12.

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<font_metadata>
<type>buttons</type>
<width>10</width>
<height>10</height>
<white_teeth>1</white_teeth>
<chars>
<range start="0xEB00" end="0xEB36"/>
</chars>
<special>
<range start="0xEB00" end="0xEB36" color="1"/>
</special>
</font_metadata>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<font_metadata>
<type>buttons</type>
<width>12</width>
<height>12</height>
<white_teeth>1</white_teeth>
<chars>
<range start="0xEB00" end="0xEB36"/>
</chars>
<special>
<range start="0xEB00" end="0xEB36" color="1"/>
</special>
</font_metadata>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<font_metadata>
<type>buttons</type>
<width>8</width>
<height>8</height>
<white_teeth>1</white_teeth>
<chars>
<range start="0xEB00" end="0xEB36"/>
</chars>
<special>
<range start="0xEB00" end="0xEB36" color="1"/>
</special>
</font_metadata>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8"?>
<font_metadata>
<display_name>english/…</display_name>
<width>8</width>
<height>8</height>
<white_teeth>1</white_teeth>
<chars>
<range start="0x00" end="0x7F"/>
<range start="0xA0" end="0x17F"/>
<range start="0x18F" end="0x18F"/>
<range start="0x218" end="0x21B"/>
<range start="0x259" end="0x25A"/>
<range start="0x37E" end="0x37E"/>
<range start="0x386" end="0x386"/>
<range start="0x388" end="0x38A"/>
<range start="0x38C" end="0x38C"/>
<range start="0x38E" end="0x3A1"/>
<range start="0x3A3" end="0x3CE"/>
<range start="0x400" end="0x45F"/>
<range start="0x490" end="0x491"/>
<range start="0x49A" end="0x49B"/>
<range start="0x4A2" end="0x4A3"/>
<range start="0x5D0" end="0x5EA"/>
<range start="0x5F0" end="0x5F4"/>
<range start="0x16A0" end="0x16F0"/>
<range start="0x1E9E" end="0x1E9E"/>
<range start="0x2014" end="0x2014"/>
<range start="0x2018" end="0x2019"/>
<range start="0x201C" end="0x201E"/>
<range start="0x2026" end="0x2026"/>
<range start="0x203C" end="0x203D"/>
<range start="0x2070" end="0x2070"/>
<range start="0x2074" end="0x2079"/>
<range start="0x20AC" end="0x20AC"/>
<range start="0x2122" end="0x2122"/>
<range start="0x2190" end="0x2199"/>
<range start="0x21B0" end="0x21B3"/>
<range start="0x21B6" end="0x21B7"/>
<range start="0x21BA" end="0x21C3"/>
<range start="0x21C5" end="0x21C6"/>
<range start="0x21CB" end="0x21CC"/>
<range start="0x2318" end="0x2318"/>
<range start="0x2325" end="0x2325"/>
<range start="0x237C" end="0x237C"/>
<range start="0x23B2" end="0x23B3"/>
<range start="0x23CE" end="0x23CE"/>
<range start="0x23E9" end="0x23EF"/>
<range start="0x23F4" end="0x23FA"/>
<range start="0x2500" end="0x25A2"/>
<range start="0x25B2" end="0x25B3"/>
<range start="0x25B6" end="0x25B7"/>
<range start="0x25BC" end="0x25BD"/>
<range start="0x25C0" end="0x25C1"/>
<range start="0x2600" end="0x2606"/>
<range start="0x2609" end="0x2609"/>
<range start="0x260E" end="0x260F"/>
<range start="0x261C" end="0x2620"/>
<range start="0x2622" end="0x2623"/>
<range start="0x262E" end="0x262E"/>
<range start="0x263A" end="0x2640"/>
<range start="0x2642" end="0x2653"/>
<range start="0x2660" end="0x2660"/>
<range start="0x2663" end="0x2663"/>
<range start="0x2665" end="0x2666"/>
<range start="0x2669" end="0x266B"/>
<range start="0x2690" end="0x2691"/>
<range start="0x2693" end="0x2693"/>
<range start="0x269B" end="0x269B"/>
<range start="0x26A0" end="0x26A1"/>
<range start="0x26A5" end="0x26A6"/>
<range start="0x26B3" end="0x26B6"/>
<range start="0x26B8" end="0x26B8"/>
<range start="0x26C4" end="0x26C4"/>
<range start="0x26CF" end="0x26CF"/>
<range start="0x26D3" end="0x26D4"/>
<range start="0x26E2" end="0x26E2"/>
<range start="0x26EC" end="0x26EC"/>
<range start="0x26F6" end="0x26F6"/>
<range start="0x26FA" end="0x26FA"/>
<range start="0x2709" end="0x2709"/>
<range start="0x270F" end="0x270F"/>
<range start="0x2713" end="0x2713"/>
<range start="0x274C" end="0x274C"/>
<range start="0x2753" end="0x2753"/>
<range start="0x2962" end="0x296F"/>
<range start="0x2BD3" end="0x2BD3"/>
<range start="0x2E18" end="0x2E18"/>
<range start="0xE0A0" end="0xE0A3"/>
<range start="0xFFFD" end="0xFFFD"/>
<range start="0x1F311" end="0x1F318"/>
<range start="0x1F451" end="0x1F451"/>
<range start="0x1F4BF" end="0x1F4BF"/>
<range start="0x1F700" end="0x1F705"/>
<range start="0x1F707" end="0x1F707"/>
<range start="0x1F709" end="0x1F709"/>
<range start="0x1F70B" end="0x1F70B"/>
<range start="0x1F70D" end="0x1F70D"/>
<range start="0x1F713" end="0x1F716"/>
<range start="0x1F71A" end="0x1F71A"/>
<range start="0x1F728" end="0x1F728"/>
<range start="0x1F741" end="0x1F741"/>
<range start="0x1FA99" end="0x1FA99"/>
</chars>
<special>
<range start="0x00" end="0x1F" advance="6"/>
</special>
<fallback>buttons_8x8</fallback>
</font_metadata>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<font_metadata>
<display_name>العربية/فارسی</display_name>
<width>8</width>
<height>10</height>
<white_teeth>1</white_teeth>
<chars>
<range start="0x00" end="0x7F"/>
<range start="0xA0" end="0xFF"/>
<range start="0xFE80" end="0xFEFF"/>
<range start="0x0600" end="0x06FF"/>
<range start="0xFB50" end="0xFBFF"/>
<range start="0x2190" end="0x2193"/>
</chars>
<special>
<range start="0x064B" end="0x065F" advance="0"/>
<range start="0xFBB2" end="0xFBC1" advance="0"/>
</special>
<fallback>buttons_8x8</fallback>
</font_metadata>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View file

@ -0,0 +1,94 @@
This is a Modified Version.
Original Version: Copyright (c) 2019-2023 Minseo Lee (itoupluk427@gmail.com)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

View file

@ -0,0 +1,426 @@
This is a Modified Version.
Original Version: Copyright (c) 2022, TakWolf (https://takwolf.com),
with Reserved Font Name 'Fusion Pixel'.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
The following font is part of the Original Version of the font
with Reserved Font Name 'Fusion Pixel' above:
Copyright (c) 2021, TakWolf (https://takwolf.com),
with Reserved Font Name 'Ark Pixel'.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
The following font is part of the Original Version of the font
with Reserved Font Name 'Fusion Pixel' above:
Copyright (c) 2019-2023 Minseo Lee (itoupluk427@gmail.com)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
The following font is part of the Original Version of the font
with Reserved Font Name 'Fusion Pixel' above:
[Cubic 11]
These fonts are free software.
Unlimited permission is granted to use, copy, and distribute them, with or without modification, either commercially or noncommercially.
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
此字型是免費的。
無論您是否進行對本字型進行商業或非商業性修改,均可無限制地使用,複製和分發它們。
本字型的衍生品之授權必須與此字型相同,且不作任何擔保。
[JF Dot M+H 12]
Copyright(c) 2005 M+ FONTS PROJECT
[M+ BITMAP FONTS]
Copyright (C) 2002-2004 COZ
These fonts are free software.
Unlimited permission is granted to use, copy, and distribute it, with or without modification, either commercially and noncommercially.
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
これらのフォントはフリー(自由な)ソフトウエアです。
あらゆる改変の有無に関わらず、また商業的な利用であっても、自由にご利用、複製、再配布することができますが、全て無保証とさせていただきます。
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before After
Before After

View file

@ -0,0 +1,177 @@
=== I N T R O D U C T I O N ===
This file will explain what you need to know when maintaining translations of VVVVVV (like adding new strings to the game and syncing them across languages).
For making new translations of the game, read README-translators.txt instead.
=== A D D I N G N E W S T R I N G S ===
If you want to add some new text to the game, all you generally need to do to make it translatable is wrap loc::gettext() around your raw strings in the code (you may need to add an #include "Localization.h"), and add the same strings to the English language file. The new strings can be automatically synced from English to all other language files using the translator menu.
For example, "Game paused" can be made translatable by changing it to loc::gettext("Game paused"). Its entry in the English language file could look like this:
<string english="Game paused" translation="" explanation="pause screen" max="40"/>
The max value indicates how many characters of space there is for the text, and is further described below. It looks like "40" for single-line text, and "38*5" for multi-line text. The max value may not be applicable or may be hard to define, so this attribute can be completely left out. For example, when it's a diagonally-placed menu option, or because the limit depends on the lengths of other strings (like Low/Medium/High in the joystick menu), or a string looks progressively worse the longer it gets. As a general rule: if defining a hard limit would be misleading, then it can be exempt from having a limit.
=== E D I T I N G E X I S T I N G S T R I N G S ===
Sometimes you need to make a partial change to text that has already been translated.
For example: you need to change the string "Press ENTER to stop" to "Press {button} to stop" (because you made that hotkey configurable).
Please do *not* simply find-and-replace the text in the language files, nor remove the old string when you add the new one. (Whether you keep the translations or not.)
Instead, duplicate the string in the English language file, putting your version underneath the old version, and mark the old version with the explanation "***OUTDATED***".
For example:
<string english="Press ENTER to stop" translation="" explanation="stop super gravitron"/>
↓ ↓ ↓
<string english="Press ENTER to stop" translation="" explanation="***OUTDATED***"/>
<string english="Press {button} to stop" translation="" explanation="stop super gravitron"/>
The game won't be using the outdated string anymore - it's only still in the language files to carry the existing translations for reference. Once a translator updates the language files, they can reuse parts of the old translation for the new version of the string.
english | translation | explanation
=================================================================================
Press ENTER to stop | Appuyez sur ENTRÉE pour arrêter | ***OUTDATED***
---------------------------------------------------------------------------------
Press {button} to stop | ? ? ? | stop super gravitron
Eventually, once all languages are up-to-date, these outdated strings can be cleaned up.
Of course, when you remove strings without replacement, you can simply remove them from the (English) language file.
=== T E X T P R I N T I N G ===
These are the text printing functions:
font::print(flags, x, y, text, r, g, b)
font::print_wrap(flags, x, y, text, r, g, b, linespacing = -1, maxwidth = -1)
The flags argument can be 0, or a set of flags that do things like centering, enlarging, etc.
Some examples (can also be found in Font.h):
Standard print
font::print(0, 50, 50, "Hello world!", 255, 255, 255);
Centered text
font::print(PR_CEN, -1, 50, "Hello world!", 255, 255, 255);
(set X to -1, unless you want to center *around* X)
2x scale
font::print(PR_2X, 50, 50, "V", 255, 255, 255);
Centered 2x scale
font::print(PR_CEN | PR_2X, -1, 50, "V", 255, 255, 255);
Right-aligned 3x scale with a border around it
font::print(PR_RIGHT | PR_3X | PR_BOR, 320, 50, "V", 255, 255, 255);
Wordwrapped centered text
font::print_wrap(PR_CEN, -1, 50, "Hello world, this will wordwrap to the screen width", 255, 255, 255);
A not-technically-exhaustive list of all flags (which are defined in Font.h):
- PR_2X/PR_3X/.../PR_8X Print at larger scale (PR_1X is default)
- PR_FONT_INTERFACE [DEFAULT] Use interface (VVVVVV language) font
- PR_FONT_LEVEL Use level-specific font (room names, cutscenes, etc)
- PR_FONT_8X8 Use 8x8 font no matter what
- PR_BRIGHTNESS(value) Use this brightness 0-255 (this value is mixed with
r, g and b for an alpha effect, and accounts for
colored glyphs correctly)
- PR_BOR Draw a black border around the text
- PR_LEFT [DEFAULT] Left-align text/place at X coordinate
- PR_CEN Center-align text relative to X (X is center)
or to screen if X == -1
- PR_RIGHT Right-align text to X
(X is now the right border, not left border)
- PR_CJK_CEN [DEFAULT] Large fonts (Chinese/Japanese/Korean) should
stick out on top and bottom compared to 8x8 font
- PR_CJK_LOW Larger fonts should stick out fully on the bottom
(draw at Y)
- PR_CJK_HIGH Larger fonts should stick out fully on the top
- PR_RTL_XFLIP In RTL languages, mirror the X axis, so left is 320
and right is 0, and invert the meaning of PR_LEFT and
PR_RIGHT
=== S T R I N G F O R M A T T I N G ===
Instead of sprintf-family functions, it is preferred to use VVVVVV's own formatting system, VFormat.
Strings sometimes have placeholders, which look like {name} or {name|flags}. For example, "{n_trinkets} of {max_trinkets}".
Placeholders can also have "flags" that modify their behavior. These can be added or removed in the translation as needed. Flags are separated by | (pipe).
For more info, see the documentation at the top of VFormat.h.
Full example:
char buffer[100];
vformat_buf(buffer, sizeof(buffer),
"{crewmate} got {number} out of {total} trinkets in {m}:{s|digits=2}.{ms|digits=3}",
"number:int, total:int, crewmate:str, m:int, s:int, ms:int",
2, 20, "Vermilion", 2, 3, 1
);
=> "Vermilion got 2 out of 20 trinkets in 2:03.001"
=== T R A N S L A T O R M E N U ===
The translator menu has options for both translators and maintainers - it allows testing menus, translating room names within the game, syncing all language files with the English template files, getting statistics on translation progress, and more.
VVVVVV will show a "translator" menu in the main menu if either:
- The "lang" folder is NOT next to data.zip, and the game is running somewhere within a "desktop_version" folder, and desktop_version/lang IS found. This normally happens when compiling the game from source;
- The command line argument (or launch option) "-translator" is passed.
- ALWAYS_SHOW_TRANSLATOR_MENU is defined during compilation (see top of Localization.h)
To add new strings, add them to only the English strings.xml or strings_plural.xml, and use the option to sync all languages from the translator menu. This will copy the new strings to all translated language files.
The language file sync option has differing support for the language files. As indicated in the menu itself, it handles each file as follows:
[Full syncing EN→All]
For these files, the English version of the file is fully copied and overwrites every language's version, while all existing translations and customizations are inserted for every language. This means newly added strings are copied to every language, and removed strings are simultaneously removed from every language, bringing them fully up-to-date.
- meta.xml
- strings.xml
- strings_plural.xml
- cutscenes.xml
- roomnames.xml
- roomnames_special.xml
[Syncing not supported]
These files are untouched by the syncing feature.
- numbers.xml
=== F I L E S ===
* meta.xml: This file contains some general information about a translation.
* strings.xml: This file contains general strings for the interface and some parts of the game.
* strings_plural.xml: Similar usage to strings.xml, but for strings that need different plural forms ("1 trinket", "2 trinkets")
* numbers.xml: This file contains all numbers from 0 to 100 written out (Zero, One, etc). This file also allows you to define the plural forms used in strings_plural.xml.
* cutscenes.xml: This file contains nearly all the cutscenes that appear in the main game.
* roomnames.xml: This file contains nearly all the room names for the main game.
* roomnames_special.xml: This file contains some special cases for roomnames, some names for rooms that usually aren't displayed as regular (like The Ship), and some general area names.

View file

@ -0,0 +1,382 @@
=== I N T R O D U C T I O N ===
This file will explain everything you need to know when making translations of VVVVVV.
WARNING TO VOLUNTEERS: VVVVVV translation is not a community effort where anyone can submit translations! It's *possible* that, on a case-by-case basis, someone can volunteer to become an official translator. But you would need to get approval from Terry first (or you can express interest in there being an official translation into your language, it may already be planned!)
Likewise, you're welcome to report issues in existing translations (or to submit PRs to fix these issues), but it's not a good idea to rewrite significant parts of a translation, and then contribute it without warning. If you think there are errors or things that could be improved, please give an explanation as to why. (We may decide to discuss it with the official translator.)
It *is* possible to make a fan translation for fun, for sharing with others, etc. But it probably won't be distributed with the game officially.
=== A D D I N G A N E W L A N G U A G E ===
The English language files are basically templates for other languages (all the translations are empty).
To create a new language, simply copy the `en` folder, and start by filling out meta.xml (further explained below).
Alternatively, you can create an empty language folder, and then use the in-game sync tool (translator > maintenance > sync language files) to populate it.
=== E X C E L ===
The game uses XML files for storing the translations. If you prefer, there is an .xlsm file which can be used as an editor. This can load in all the XML files, and then save changes back as XML.
If you're an official translator, you should have received a version of this spreadsheet. If not, a blank version can be found here: https://github.com/Daaaav/TranslationEditor
=== T R A N S L A T O R M E N U ===
The translator menu has options for both translators and maintainers - it allows testing menus, translating room names within the game, syncing all language files with the English template files, getting statistics on translation progress, and more. The translator menu is hidden from players in regular versions of the game.
When the translator menu is unlocked, you can also press F8 anywhere in the game to reload the current language files. So you can save translations and immediately preview them (except for menu buttons and the current cutscene dialogue, which can't be reloaded on the fly). You will hear a coin sound when the language files have been reloaded via F8.
=== L O W E R C A S E A N D U P P E R C A S E ===
If lowercase and uppercase does not exist in your language (Chinese, Japanese and Korean for example), you can set toupper to 0 in meta.xml, and ignore any directions about using lowercase or uppercase.
VVVVVV's menu system has the style of using lowercase for unselected options and uppercase for selected options, for example:
play
levels
[ OPTIONS ]
translator
credits
quit
The menu options are stored as their full-lowercase version, and they're normally commented as "menu option" in the translation files. A built-in function (toupper in Localization.cpp) automatically converts the lowercase text to uppercase when needed. This function has support for a good number of accented characters, Cyrillic and Greek, but more could be added if needed. It also accounts for special cases in Turkish and Irish.
Turkish: The uppercase of i is İ, for example, "dil" becomes "DİL" and not "DIL". To enable this, set toupper_i_dot to 1 in meta.xml.
Irish: Specific letters may be kept in lowercase when making a string full-caps. For example, "mac tíre na hainnise" should be "MAC TÍRE NA hAINNISE" instead of "MAC TÍRE NA HAINNISE". If enabled, you can use the ~ character before the letter which should be forced in lowercase: "mac tíre na ~hainnise". This ~ character only has an effect in strings which are subject to automatic uppercasing (otherwise it'll be visible as å). This can be enabled by setting toupper_lower_escape_char to 1 in meta.xml.
=== W O R D W R A P P I N G A N D L E N G T H L I M I T S ===
For most languages, VVVVVV can automatically wordwrap based on spaces. This may not work for some languages (like Chinese, Japanese and Korean), so instead, newlines can be inserted manually (see below) and automatic wordwrapping can be disabled in meta.xml.
VVVVVV's resolution is 320x240, and the default font is 8x8, which means there is a 40x30 character grid (although we don't adhere to this grid for the UI, but it gives a good indication). Naturally, if the font has a different size like 12x12, less characters will fit on the screen too.
Strings are usually annotated with their limits (for example, max="38*3"). This can be formatted like one of the following:
(A) 33
(B) 33*3
(A) if it's a single number (for example "33"): the hard maximum number of characters that are known to fit. Being exactly on the limit may not look good, so try to go at least a character under it if possible.
(B) if X*Y (for example 33*3): the text should fit within an area of X characters wide and Y lines high. The text is automatically word-wrapped to fit (unless disabled in meta.xml). If automatic word-wrapping is disabled, you need to manually insert newlines with |, or possibly as a literal newline.
If your language uses a font with a different size than 8x8, there will be two limits given: `max`, which is the original limit based on the 8x8 font, and `max_local`, which is adapted to the size of your font. To get this notation, use the maintenance option to sync language files from within VVVVVV. Ensure the correct font is set in meta.xml first.
The translator menu has an option ("limits check") to automatically find strings that break the given limits. There may be a few cases where this detection isn't perfect, but it should be a helpful quality assurance tool.
The maximum lengths are not always given. Notoriously, menu option buttons are placed diagonally, thus they have maximums that are hard to look up. Even more so, making an option differ too much in length from the other options might make it look out of place. Best thing to do there is probably just translate as usual and then test all menus via the "menu test" option in the translator menu. However, menus do automatically reposition based on the text length, so worst-case scenario, if an option is 36 characters long, all options are displayed right underneath each other.
=== F O N T S ===
The game uses an 8x8 pixel font by default (font.png and font.fontmeta in the "fonts" folder). If your language can be represented in 8x8 characters, it is preferable to use this font, or for this font to be extended.
The fonts directory also has a README.txt file that explains how the font format works.
=== N U M B E R S A N D P L U R A L F O R M S ===
In certain places, VVVVVV (perhaps unconventionally) writes out numbers as full words. For example:
- One out of Fifteen
- Two crewmates remaining
- Two remaining
These words can be found in numbers.xml. The numbers Zero through Twenty will be the most commonly seen. It's always possible for numbers up to One Hundred to be seen though (players can put up to 100 trinkets and crewmates in a custom level).
Your language may not allow the same word to be used for the same number in different scenarios. For example, in Polish, "twenty out of twenty" may be "dwadzieścia z dwudziestu". Right now, you have two sets of wordy numbers to choose from, `translation` and `translation2`, but this will likely change to a more customizable system in the future. You can choose when these "wordy" numbers are used and when numeric forms (20 out of 20) are used (see "STRING FORMATTING" below). It's also possible to leave the translations for all the numbers empty. In that case, numeric forms will always be used.
In English, using Title Case is appropriate, but in most other languages, it probably isn't. Therefore, you may want to translate all numbers in lowercase, when it's more appropriate to use "twenty out of twenty" than "Twenty out of Twenty". You can then apply auto-uppercasing to any placeholder you choose (see "STRING FORMATTING" below), making it possible to display "Twenty out of twenty".
As for plural forms: English and some other languages have a singular (1 crewmate) and a plural (2 crewmates). Some languages may have different rules (like for 0, or numbers that end in 2, 3 and 4). VVVVVV can accommodate these rules and allows you to translate certain strings (strings_plural.xml) in different ways depending on the number. The different forms can be defined by changing the "form" attribute on each number in numbers.xml. For English, form "1" is used for singular, and form "0" is used for plural. You can set up any amount of plural forms you will need.
Numbers that identify the forms do not need to be sequential, you may use any number between 0 and 254 to identify the different forms. So instead of using forms 0, 1, 2 and 3, you could also name them 1, 2, 5 and 7.
Suppose you need a different form for the number 1, the numbers 2-4, and all other numbers. You could use "form 1" for the number 1, "form 2" for 2-4, and "form 0" for all other numbers:
<numbers>
<number value="0" form="0" ... />
<number value="1" form="1" ... />
<number value="2" form="2" ... />
<number value="3" form="2" ... />
<number value="4" form="2" ... />
<number value="5" form="0" ... />
<number value="6" form="0" ... />
...
When translating the plural strings, you can add translations for every unique form. For example:
<string english_plural="You rescued {n_crew} crewmates" english_singular="You rescued {n_crew} crewmate">
<translation form="0" translation="You saved {n_crew} crewmates"/>
<translation form="1" translation="You saved {n_crew} crewmate"/>
<translation form="2" translation="You saved {n_crew} crewmateys"/>
</string>
Plural forms can appear both for wordy numbers ("you saved one crewmate") as well as numbery numbers ("you died 136 times in this room"), so we need the plural forms to go further than 100.
For the numbers 100 and higher: as far as I can find (with information about plural rules across 160 languages) - the plural forms always repeat themselves every 100 numbers. So numbers 100-199 always have the same forms as 200-299, 300-399, and so on. However, 100-119 (200-219, etc) don't always work the same as 0-19 do (in English for example, it's not "101 trinket" despite ending in 01). Therefore, forms for 100-119 can also be filled in. The system will simply copy 20-99 for 120-199, and that should be enough to cover all numbers from 0 to infinity. Technically the system supports providing forms until 199, but it should never be necessary to go higher than 119, so they're not in the language files by default.
Numbers higher than 100 cannot have a written out translation ("one hundred and one" does not exist).
=== S T R I N G F O R M A T T I N G ===
Strings sometimes have placeholders, which look like {name} or {name|flags}. For example, "{n_trinkets} of {max_trinkets}".
Placeholders can also have "flags" that modify their behavior. These can be added or removed in the translation as needed. Flags are separated by | (pipe).
For example, "{n_trinkets|wordy}" makes the number of trinkets display as a "wordy" number (twenty instead of 20) (See "NUMBERS AND PLURAL FORMS"). "{n_trinkets|wordy|upper}" makes that word start with a capital letter (Twenty instead of twenty). So for example, "{n_trinkets|wordy|upper} of {max_trinkets|wordy}" may be displayed as "Twenty out of twenty" - assuming numbers.xml is translated all-lowercase.
The valid flags are:
- wordy [ints only] use number words (Twenty) of the first set (translation), instead of digits (20)
- wordy2 [ints only] use number words of the second set (translation2), instead of digits
- digits=n [ints only] force minimum n digits, like n=5 --> 00031
- spaces [only if using digits=n] use leading spaces instead of 0s
- upper uppercase the first character with loc::toupper_ch
=== S T O R Y A N D C H A R A C T E R I N F O R M A T I O N ===
This is a brief story and character overview for reference. Any further questions can be directed to Terry: https://distractionware.com/email/
== The Crewmates ==
VVVVVV is about a crew of curious and super-intelligent aliens exploring the universe. There are six crewmates onboard the ship, in six different colours. They are:
* Viridian (Cyan, the player character)
* Verdigris (Green)
* Vitellary (Yellow)
* Vermilion (Red)
* Victoria (Blue)
* Violet (Purple)
All six characters have names that start with V. In addition, each crewmate's name is an obscure word that suggests their colour - for example, Verdigris is the name of the green pigment that forms on copper when it oxidises. This might be hard to translate! (So, potentially it just makes sense not to translate the names at all, unless you have a good idea about how to do it - for example, if you can pull off the same colour/name beginning with V trick in your language.)
Each crewmate has the following "rank", i.e. their job on the ship. These are all basically just Star Trek inspired roles:
* "Captain" Viridian (Captain as in leader)
* "Chief" Verdigris (Chief as in Lead Engineer)
* "Professor" Vitellary (Kind of the senior scientist on board)
* "Officer" Vermilion (Officer as in Away-Officer, the one who usually goes out exploring)
* "Doctor" Victoria (Doctor in the scientific sense)
* "Doctor" Violet (Doctor in the medical sense)
Verdigris, Vitellary and Vermilion are male. Victoria and Violet are female. Viridian is deliberately unspecified - when translating cutscenes, if at all possible, try to avoid specifying their gender, even if that leads to some otherwise awkward phrasing or requires the use of some cutting edge grammar tricks in your language.
== Personalities ==
If it helps with tone: the running joke in VVVVVV's writing is that all six characters are hyper intelligent prodigies, who will nevertheless speak to each other as if they're small children. E.g. Viridian "we were just playing with it!", Vermilion "I'm helping!", Verdigris "I'm an engineer!", etc. More specifically:
* Viridian is a classic hero - unphased by danger, never worried, always completely certain that they'll be able to fix everything.
* Verdigris is a romantic - he has a huge (reciprocated!) crush on Violet, which he does a terrible job of keeping secret.
* Vitellary is an academic - he loves science, and has a dry and long winded science thing to tell you in almost every cutscene.
* Vermilion is bold and adventurous - after you rescue him, you'll find him exploring different parts of the world on his own quest to find the rest of the crew (he's not much help, though).
* Victoria is a worrier - she's quick to feelings of despair. Victoria's sprite is almost always sad!
* Violet is a caretaker - she's the ship's Doctor, and most of her cutscenes are status updates about the other crewmates.
== Dimension VVVVVV ==
The world you're exploring is filled with terminals, with text logs from the previous inhabitants, who we never see. We don't know much about them.
The ship you're all on is called the "D.S.S. Souleye", which is a minor easter egg. D.S.S. just stands for "Dimensional Space Ship" - a craft that warps between different dimensions. Souleye is the pseudonym for Magnus Pålsson, the game's composer.
=== S P R I T E S T R A N S L A T I O N ===
There are several enemies in the game which contain words: STOP, YES, OBEY, LIES and TRUTH. These, as well as the C on checkpoints, can be translated.
This may be a bit tricky - the sizes of the translated graphics should be as close to the original English as possible, which means that even though letters can be compressed a bit, they will quickly be too long. For example, if you'd like to translate LIES with 5 letters, it really doesn't help if one of those 5 letters is not an I (the most slim letter).
Fortunately, the translation does not have to be literal, as the words themselves are only referenced in room names at most, and the exact meanings aren't that important, only the spirit of them is. OBEY is a They Live reference, so there are a lot of other signs you could use. Some inspiration:
* STOP: this is on a stop sign, so may not need to be translated
* OBEY: maybe something like FOLLOW, or one of the other words in "They Live" (WORK, BUY, CONSUME, CONFORM, ...)
* LIES: maybe singular LIE, maybe some form of NONSENSE (as in BS but non-offensive), FALSE, FAKE, BLAH, FABLE
* TRUTH: maybe FACTS
== Implementation of translated sprites ==
If you'd like to pixel translated sprites yourself: Take sprites.png and flipsprites.png from the graphics folder in data.zip. These spritesheets are a 32x32 grid - for example, you cannot extend OBEY upwards or to the left.
The translated file does not have to contain any untranslated sprites - these can simply be made transparent, for optimization. So: the files should be transparent sheets with some translations in the middle.
The translated versions of sprites.png and flipsprites.png can be placed in a "graphics" subfolder of your language folder.
Then, put a file next to them called "spritesmask.xml", with the following contents:
<?xml version="1.0" encoding="UTF-8"?>
<spritesmask sprite_w="32" sprite_h="32">
<sprite x="8" y="1" w="2"/> <!-- Checkpoints -->
<sprite x="4" y="2" w="4"/> <!-- STOP -->
<sprite x="4" y="3" w="4"/> <!-- YES -->
<sprite x="3" y="4"/> <!-- OBEY -->
<sprite x="2" y="5" w="2"/> <!-- LIES receiver and LIES -->
<sprite x="4" y="5" w="2"/> <!-- TRUTH -->
</spritesmask>
This file defines which sprites in the translated files should overwrite the original ones. Remove any lines for sprites you did not translate. After that, your translated sprites should work!
For completeness: sprite_w and sprite_h on the <spritesmask> tag define the size of each unit in the attributes of <sprite>. The possible attributes of <sprite> are:
* x, y: the position of the sprite to copy (in blocks of sprite_w by sprite_h)
* w, h: the width and height of the sprite to copy (default 1 for both)
* dx, dy: the destination position of the sprite (default x,y; so the same position as copied from)
=== F I L E S ===
== meta.xml ==
This file contains some general information about this translation. It contains the following attributes:
* active: If 0, this language will not be shown in the languages menu
* nativename: The name of the language in itself, fully in lowercase (so not "spanish" or "Español", but "español"). A language name can be at most 16 characters wide (in the 8x8 font)
* credit: You can fill in credit here that will appear on the language screen, like "Spanish translation by X". May be in your language. Max 38*2 @8x8
* action_hint: This is displayed at the bottom of the language screen when your language is highlighted, to show that Space/Z/V sets the selected option as the language. Max 40 @8x8
* gamepad_hint: The same as action_hint, but now a gamepad button will be filled into {button}, such as (A), (X), etc
* autowordwrap: Whether automatic wordwrapping is enabled. Can be disabled for CJK (in which case newlines have to be inserted manually in text)
* toupper: Whether to enable automatic uppercasing of menu options (unselected, SELECTED). May be disabled for languages such as CJK that don't have lowercase and uppercase.
* toupper_i_dot: When automatically uppercasing, map i to İ, as in Turkish.
* toupper_lower_escape_char: When automatically uppercasing, allow ~ to be used to stop the next letter from being uppercased, for Irish.
* rtl: This should be enabled for languages that are written right-to-left. It will horizontally flip many parts of the interface and allows text to be right-aligned in places like textboxes.
* menu_select: The indication that a certain menu option or button is selected, in addition to the automatic uppercasing if "toupper" is enabled. For example, "[ {label} ]" looks like "[ SELECTED ]"
* menu_select_tight: Similar to menu_select, except used in cases where space is a bit more limited (like the map screen). "[{label}]" looks like "[SELECTED]"
* font: The filename of the font to use. The 8x8 font is called "font", the Japanese font is "font_ja", et cetera.
== strings.xml ==
This file contains general strings for the interface and some parts of the game. In the XML, the tag for one string looks like this:
<string english="Game paused" translation="" explanation="pause screen" max="40"/>
To translate a string, simply fill in the translation attribute, like this:
<string english="Game paused" translation="Spel gepauzeerd" explanation="pause screen" max="40"/>
If the translation is left blank, the English text will be used as a fallback. Translations should NOT be left blank on purpose if the text is the same however; the string will be counted as untranslated and it'll be harder to keep track of what's new. Always just copy-paste the English string into the translation in that case.
The following attributes may be found for each string:
* english: the English text.
* translation: the translation.
* case: a number (1, 2, etc) for separate strings that are identical in English but may need different translations.
* explanation: an explanation about the context, location and possibly the formatting.
* max: length restrictions, described above in "WORDWRAPPING AND LENGTH LIMITS"
== strings_plural.xml ==
See "NUMBERS AND PLURAL FORMS" above.
You can define the plural forms in numbers.xml.
Then, simply add translations for each form you set up in numbers.xml. For example:
<translation form="0" translation="Shows up for all numbers with form=0"/>
<translation form="1" translation="Shows up for all numbers with form=1"/>
<translation form="2" translation="Shows up for all numbers with form=2"/>
The "wordy" flag indicates a word will be filled in (like twelve), otherwise a number (12). As described above in "STRING FORMATTING", you can change this as needed in your translations.
The `var` attribute indicates which placeholder will be filled in, the `expect` attribute indicates how high the values are that you may expect to be filled in. For example, expect="20" means any value above 20 will probably not be used in this string. This is mainly needed so that the limits check knows not to worry about a number like "seventy seven" making the string too long, but it may also be a useful context clue.
== numbers.xml ==
This file contains all numbers from 0 to 100 written out (Zero, One, etc).
This will be filled in strings like:
- One out of Fifteen
- Two crewmates remaining
- Two remaining
If this can't work for your language, or wordy numbers are really unfitting, you can leave all of these empty, in which case numbers will be used (20 out of 20).
You may want to do it all-lowercase in order to not get English-style title casing. "Twenty out of Twenty" may be grammatically incorrect in MANY languages, and "twenty out of twenty" would be better. Translating the numbers all-lowercase allows you to apply context-specific uppercasing, like "Twenty out of twenty" (see "STRING FORMATTING" above)
This file also allows you to define the plural forms used in strings_plural.xml.
For more information, see "NUMBERS AND PLURAL FORMS" above.
== cutscenes.xml ==
This file contains nearly all the cutscenes that appear in the main game. Each line has a "speaker" attribute, which is not used by the game - it's just for translators to know who says what and to establish context.
The dialogues are automatically text-wrapped, except if automatic wrapping is disabled in meta.xml. In that case, the maximum line length is 36 8x8 characters (288 pixels) or 24 12x12 characters.
In the few cases where the same text appears multiple times in a cutscene, these have the attribute "case" added to them (for example case="1", case="2", etc), so they can be translated separately if needed. (These match up with textcase(x) commands in the scripts themselves)
You may find some additional formatting attributes on each <dialogue> tag. These are used to make spacing and formatting in translations consistent with the original English text (for example, centered text, padding on both sides, etc). You can change any of these if you need, and you can also add them yourself to ANY dialogue tag.
* tt: teletype. if "1", disable automatic word wrapping, even if autowordwrap is enabled in meta.xml. You will have to add newlines manually for this textbox, either with hard enters, or with |
* wraplimit: change the maximum width of the text before it wordwraps, in pixels. Only if tt is not enabled. Example:
[Hello world!] --[wraplimit="56"]--> [Hello ] (56=7*8)
[world!]
If autowordwrap is disabled in meta.xml, this also doesn't work, but it does give you an advisory maximum text width.
The default is 288 (36*8 or 24*12).
* centertext: center the text (but keep it aligned to the grid), for example:
[You have rescued] --[centertext="1"]--> [You have rescued]
[a crewmember!] [ a crewmember! ]
* pad: pad each line of text with a number of spaces (0 by default), for example:
[You have rescued] --[pad="2"]--> [ You have rescued ]
[ a crewmember! ] [ a crewmember! ]
This will automatically make the wrap limit smaller accordingly, unless a custom wraplimit is given.
* pad_left/pad_right: same as pad, but only affects the left or right side. For example:
[You have rescued] --[ pad_left="5"]--\ [ You have rescued ]
[ a crewmember! ] --[pad_right="2"]--/ [ a crewmember! ]
* padtowidth: pad the text on both sides if it's not this many pixels wide. For example:
[-= Personal Log =-] --[padtowidth="224"]--> [ -= Personal Log =- ] (224=28*8)
== roomnames.xml ==
This file contains nearly all the room names for the main game. The limit is always 40 8x8 characters (320 pixels) or 26 12x12 characters.
It's recommended to translate the room names in-game to see why all rooms are called what they are. To do this, enable room name translation mode in translator > translator options > translate room names.
== roomnames_special.xml ==
This file contains some special cases for roomnames, some names for rooms that usually aren't displayed as regular (like The Ship), and some general area names.
One room ("Prize for the Reckless") is intentionally missing spikes in a time trial and no death mode so the player does not have to die there, and the room is called differently in both cases (for time trial "Imagine Spikes There, if You Like", and for no death mode "I Can't Believe You Got This Far").
There are also some roomnames in the game which gradually transform into others or cycle through a few minor variations.

View file

@ -0,0 +1,948 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Please read README.txt for information about the language files -->
<cutscenes>
<cutscene id="intro" explanation="">
<dialogue speaker="cyan" english="Uh oh..." translation="أوه... أه لا..."/>
<dialogue speaker="purple" english="Is everything ok?" translation="هل الأوضاع بخير؟"/>
<dialogue speaker="cyan" english="No! We&apos;ve hit some kind of interference..." translation="كلا! اعترضنا تشويش ما..."/>
<dialogue speaker="cyan" english="Something&apos;s wrong! We&apos;re going to crash!" translation="نواجه مشكلة...! ارتطام السفينة وشيك!"/>
<dialogue speaker="cyan" english="Evacuate!" translation="علينا إخلاء المكان!"/>
<dialogue speaker="blue" english="Oh no!" translation="مصيبة!"/>
<dialogue speaker="red" english="Everyone off the ship!" translation="غادروا السفينة جميعا!"/>
<dialogue speaker="yellow" english="This shouldn&apos;t be happening!" translation="لا يفترض هذا... لا يعقل!"/>
<dialogue speaker="cyan" english="Phew! That was scary!" translation="هوف! خفت جدا قبل قليل!"/>
<dialogue speaker="cyan" english="At least we all escaped, right guys?" translation="على الأقل نجونا جميعا سالمين، أليس كذلك يا..."/>
<dialogue speaker="cyan" english="...guys?" translation="...جماعة؟"/>
</cutscene>
<cutscene id="firststeps" explanation="">
<dialogue speaker="cyan" english="I wonder why the ship teleported me here alone?" translation="يا ترى لماذا نقلتني السفينة إلى هنا لوحدي؟"/>
<dialogue speaker="cyan" english="I hope everyone else got out ok..." translation="أتمنى أن بقية الطاقم خرجوا دون أن يصيبهم أذى..."/>
</cutscene>
<cutscene id="communicationstation" explanation="">
<dialogue speaker="cyan" english="Violet! Is that you?" translation="فايولت! أليس كذلك؟"/>
<dialogue speaker="purple" english="Captain! You&apos;re ok!" translation="قبطان! يسعدني أنك بخير!"/>
<dialogue speaker="purple" english="Something has gone horribly wrong with the ship&apos;s teleporter!" translation="حصل خلل رهيب في آلة تنقيل السفينة!"/>
<dialogue speaker="purple" english="I think everyone has been teleported away randomly! They could be anywhere!" translation="أظن أنه بعثر أفراد طاقمنا إلى أماكن عشوائية! لا أستغرب أي مكان أرسلوا إليه!"/>
<dialogue speaker="cyan" english="Oh no!" translation="أه لا!"/>
<dialogue speaker="purple" english="I&apos;m on the ship - it&apos;s damaged badly, but it&apos;s still intact!" translation="أنا قرب السفينة. صحيح أنها تضررت كثيرا، لكنها لا تزال صالحة!"/>
<dialogue speaker="purple" english="Where are you, Captain?" translation="أين أنت يا قبطان؟"/>
<dialogue speaker="cyan" english="I&apos;m on some sort of space station... It seems pretty modern..." translation="أنا قرب محطة فضائية ما... تبدو لي حديثة بعض الشيء..."/>
<dialogue speaker="purple" english="There seems to be some sort of interference in this dimension..." translation="يبدو أن في هذا البعد نوعا ما من التشويش..."/>
<dialogue speaker="purple" english="I&apos;m broadcasting the coordinates of the ship to you now." translation="سأرسل إليك إحداثيات السفينة."/>
<dialogue speaker="purple" english="I can&apos;t teleport you back, but..." translation="لا يمكنني أن أجلبك بآلة التنقيل، لكن على الأقل..."/>
<dialogue speaker="purple" english="If YOU can find a teleporter anywhere nearby, you should be able to teleport back to me!" translation="لو عثرت بنفسك على آلة تنقيل قريبة منك، يمكنك التنقل والعودة إلي!"/>
<dialogue speaker="cyan" english="Ok! I&apos;ll try to find one!" translation="حاضر! سأحاول أن أجد أحد هذه الأجهزة!"/>
<dialogue speaker="purple" english="Good luck, Captain!" translation="حظا موفقا، يا قبطان!"/>
<dialogue speaker="purple" english="I&apos;ll keep trying to find the rest of the crew..." translation="أما أنا، سأواصل البحث عن بقية أفراد الطاقم..."/>
</cutscene>
<cutscene id="trenchwarfare" explanation="player finds Trench Warfare trinket, if no trinkets found yet">
<dialogue speaker="cyan" english="Ohh! I wonder what that is?" translation="أوووه! يا ترى ما هذا الشيء اللماع؟"/>
<dialogue speaker="cyan" english="I probably don&apos;t really need it, but it might be nice to take it back to the ship to study..." translation="لا أظنني بحاجة إليه، لكن لا بأس في العودة به للسفينة كي نتفحصه هناك..."/>
</cutscene>
<cutscene id="newtrenchwarfare" explanation="player finds Trench Warfare trinket, if other trinket already found">
<dialogue speaker="cyan" english="Oh! It&apos;s another one of those shiny things!" translation="أوه! شيء آخر من نفس نوع تلك الأشياء اللماعة!"/>
<dialogue speaker="cyan" english="I probably don&apos;t really need it, but it might be nice to take it back to the ship to study..." translation="لا أظنني بحاجة إليه، لكن لا بأس في العودة به للسفينة كي نتفحصه هناك..."/>
</cutscene>
<cutscene id="teleporterback" explanation="">
<dialogue speaker="cyan" english="A teleporter!" translation="آلة تنقيل!"/>
<dialogue speaker="cyan" english="I can get back to the ship with this!" translation="جميل، بهذا أستطيع العودة إلى السفينة!"/>
</cutscene>
<cutscene id="levelonecomplete_ending" explanation="">
<dialogue speaker="purple" english="Captain!" translation="قبطان!"/>
</cutscene>
<cutscene id="bigopenworld" explanation="">
<dialogue speaker="player" english="So, Doctor - have you any idea what caused the crash?" translation="أخبريني يا دكتورة - هل عندك فكرة عن سبب الحادث؟"/>
<dialogue speaker="purple" english="There&apos;s some sort of bizarre signal here that&apos;s interfering with our equipment..." translation="توجد هنا إشارة غريبة تتداخل مع سير عمل تجهيزاتنا..."/>
<dialogue speaker="purple" english="It caused the ship to lose its quantum position, collapsing us into this dimension!" translation="تسبب ذلك بضياع الإحداثية الكمية للسفينة، فوقع تسطيحنا إلى هذا البعد!"/>
<dialogue speaker="player" english="Oh no!" translation="أه لا!"/>
<dialogue speaker="purple" english="But I think we should be able to fix the ship and get out of here..." translation="مع هذا لن نعجز عن إصلاح السفينة والمغادرة..."/>
<dialogue speaker="purple" english="... as long as we can find the rest of the crew." translation="... بشرط أن نجمع شمل بقية الطاقم."/>
<dialogue speaker="purple" english="We really don&apos;t know anything about this place..." translation="لا نعرف عن هذا المكان شيئا..."/>
<dialogue speaker="purple" english="Our friends could be anywhere - they could be lost, or in danger!" translation="من يدري أين أصدقاؤنا - هل هم تائهون؟ هل يتعرضون للأخطار؟"/>
<dialogue speaker="player" english="Can they teleport back here?" translation="هل يمكنهم التنقيل والعودة إلينا؟"/>
<dialogue speaker="purple" english="Not unless they find some way to communicate with us!" translation="كلا، طالما لم يتواصلوا معنا بطريقة ما!"/>
<dialogue speaker="purple" english="We can&apos;t pick up their signal and they can&apos;t teleport here unless they know where the ship is..." translation="لم نلتقط إشارتهم، ولن يتنقلوا هنا إلا لو عرفوا مكان السفينة..."/>
<dialogue speaker="player" english="So what do we do?" translation="هكذا إذن، لكن ما الحل؟"/>
<dialogue speaker="purple" english="We need to find them! Head out into the dimension and look for anywhere they might have ended up..." translation="أن نجدهم! لو غامرت في هذا البعد الجديد وبحثت في كل مكان ربما نلقاهم..."/>
<dialogue speaker="player" english="Ok! Where do we start?" translation="هذا مقدور عليه... طيب! من أين نبدأ؟"/>
<dialogue speaker="purple" english="Well, I&apos;ve been trying to find them with the ship&apos;s scanners!" translation="حاولت أن أجدهم براصدات سفينتنا!"/>
<dialogue speaker="purple" english="It&apos;s not working, but I did find something..." translation="لم ينفع ذلك، لكن وجدت أمرا يستحق الذكر..."/>
<dialogue speaker="purple" english="These points show up on our scans as having high energy patterns!" translation="تلك النقاط في أرصادنا تشير إلى أنساق طاقة فائقة!"/>
<dialogue speaker="purple" english="There&apos;s a good chance they&apos;re teleporters - which means they&apos;re probably built near something important..." translation="من المرجح أنها آلات تنقيل - يدل هذا أن مكان بنائها قرب شيء مهم..."/>
<dialogue speaker="purple" english="They could be a very good place to start looking." translation="قد تكون تلك النقاط أفضل بداية لبحثنا."/>
<dialogue speaker="player" english="Ok! I&apos;ll head out and see what I can find!" translation="واضح! إذن سأخرج وأرى ماذا عساي أجد!"/>
<dialogue speaker="purple" english="I&apos;ll be right here if you need any help!" translation="سأنتظرك هنا في حال ما احتجت أي مساعدة!"/>
</cutscene>
<cutscene id="bigopenworldskip" explanation="">
<dialogue speaker="purple" english="I&apos;ll be right here if you need any help!" translation="سأنتظرك هنا في حال ما احتجت أي مساعدة!"/>
</cutscene>
<cutscene id="talkpurple_intro" explanation="">
<dialogue speaker="player" english="I&apos;m feeling a bit overwhelmed, Doctor." translation="المهمة ضخمة، يا دكتورة."/>
<dialogue speaker="player" english="Where do I begin?" translation="من أين أبدأ؟"/>
<dialogue speaker="purple" english="Remember that you can press {b_map} to check where you are on the map!" translation="هل نسيت؟ يمكنك بضغط {b_map} التأكد من مكانك في الخريطة!" buttons="1"/>
<dialogue speaker="purple" english="Look for areas where the rest of the crew might be..." translation="أقترح عليك البحث في الأماكن التي ربما نقل إليها بقية أفراد طاقمنا..."/>
<dialogue speaker="purple" english="If you get lost, you can get back to the ship from any teleporter." translation="ولو ضعت، يمكنك العودة للسفينة من أي آلة تنقيل."/>
<dialogue speaker="purple" english="And don&apos;t worry! We&apos;ll find everyone!" translation="ولا تشغلن بالك! مهما حصل سنعثر على الجميع!"/>
<dialogue speaker="purple" english="Everything will be ok!" translation="وكل شيء سيمر على ما يرام...!"/>
</cutscene>
<cutscene id="talkpurple_3" explanation="only one player string is shown">
<dialogue speaker="purple" english="Are you doing ok, Captain?" translation="هل أنت بخير يا قبطان؟"/>
<dialogue speaker="player" english="I&apos;m worried about Victoria, Doctor!" translation="يقلقني أمر فيكتوريا يا دكتورة!"/>
<dialogue speaker="player" english="I&apos;m worried about Vitellary, Doctor!" translation="يقلقني أمر فيتيلاري يا دكتورة!"/>
<dialogue speaker="player" english="I&apos;m worried about Verdigris, Doctor!" translation="يقلقني أمر فيرديغري يا دكتورة!"/>
<dialogue speaker="player" english="I&apos;m worried about Vermilion, Doctor!" translation="يقلقني أمر فارميليون يا دكتورة!"/>
<dialogue speaker="player" english="I&apos;m worried about you, Doctor!" translation="يقلقني أمرك يا دكتورة!"/>
<dialogue speaker="purple" english="Oh - well, don&apos;t worry, they&apos;ll show up!" translation="أوه، لا داع للقلق. عن قريب ستلتقيان بلا شك!"/>
<dialogue speaker="purple" english="Here! Have a lollipop!" translation="هاك! مصاصة هدية مني!"/>
</cutscene>
<cutscene id="trinketcollector" explanation="if no trinkets found yet">
<dialogue speaker="cyan" english="This seems like a good place to store anything I find out there..." translation="يبدو مكانا مناسبا لتخزين أي شيء ألقاه..."/>
<dialogue speaker="cyan" english="Victoria loves to study the interesting things we find on our adventures!" translation="كانت فيكتوريا مهتمة بدراسة ما نجلبه لها، كلما وجدنا ما يثير الاهتمام أثناء مغامراتنا!"/>
</cutscene>
<cutscene id="newtrinketcollector" explanation="if at least one trinket found">
<dialogue speaker="cyan" english="This seems like a good place to store those shiny things." translation="يبدو المكان مستودعا مناسبا لتلك الأغراض اللماعة."/>
<dialogue speaker="cyan" english="Victoria loves to study the interesting things we find on our adventures!" translation="كانت فيكتوريا مهتمة بدراسة ما نجلبه لها، كلما وجدنا ما يثير الاهتمام أثناء مغامراتنا!"/>
</cutscene>
<cutscene id="new2trinketcollector" explanation="">
<dialogue speaker="cyan" english="I hope she&apos;s ok..." translation="أرجو أنها بخير..."/>
</cutscene>
<cutscene id="rescuegreen" explanation="nuance: `she&apos;s BACK on the ship` means more `she&apos;s home` and not `she has returned to the ship`. She has never left the ship">
<dialogue speaker="green" english="Captain! I&apos;ve been so worried!" translation="قبطان! كم كنت قلقا!"/>
<dialogue speaker="player" english="Chief Verdigris! You&apos;re ok!" translation="القائد فيرديغري! أحوالك بخير!"/>
<dialogue speaker="green" english="I&apos;ve been trying to get out, but I keep going around in circles..." translation="حاولت الخروج، لكني كنت أدور وأدور وأعود من حيث بدأت..."/>
<dialogue speaker="player" english="I&apos;ve come from the ship. I&apos;m here to teleport you back to it." translation="جئتك من السفينة. أتيت كي أرجعك إليها بآلة التنقيل."/>
<dialogue speaker="green" english="Is everyone else alright? Is Violet..." translation="هل الجمع بخير؟ ماذا عن فايوليت، أهي..."/>
<dialogue speaker="player" english="She&apos;s fine - she&apos;s back on the ship!" translation="لا تقلق عليها، عادت سالمة معافاة للسفينة!"/>
<dialogue speaker="green" english="Oh! Great - Let&apos;s get going, then!" translation="أوه! عظيم إذن - فلننطلق دونما تأجيل!"/>
</cutscene>
<cutscene id="rescueblue" explanation="">
<dialogue speaker="blue" english="Oh no! Captain! Are you stuck here too?" translation="قبطان! مصيبة! حتى أنت علقت هنا؟"/>
<dialogue speaker="player" english="It&apos;s ok - I&apos;m here to rescue you!" translation="لا بأس - جئت لإنقاذك!"/>
<dialogue speaker="player" english="Let me explain everything..." translation="فلتسمحي لي أن أشرح لك كل شيء..."/>
<dialogue speaker="blue" english="What? I didn&apos;t understand any of that!" translation="كيف...؟ لم أفهم حرفا من كلامك!"/>
<dialogue speaker="player" english="Oh... well, don&apos;t worry." translation="أوه... معذرة، المهم لا تقلقي."/>
<dialogue speaker="player" english="Follow me! Everything will be alright!" translation="اتبعيني! ولا خوف عليك!"/>
<dialogue speaker="blue" english="Sniff... Really?" translation="إهئ... حقا حقا؟"/>
<dialogue speaker="blue" english="Ok then!" translation="حسنا. أثق بك يا قبطان!"/>
</cutscene>
<cutscene id="rescuered" explanation="">
<dialogue speaker="red" english="Captain!" translation="قبطان!!"/>
<dialogue speaker="red" english="Am I ever glad to see you! I thought I was the only one to escape the ship..." translation="ما أسعدني برؤيتك حقا! ظننت أني الناجي الوحيد من السفينة..."/>
<dialogue speaker="player" english="Vermilion! I knew you&apos;d be ok!" translation="فارميليون! لم يراودني شك أنك ستنجو!"/>
<dialogue speaker="red" english="So, what&apos;s the situation?" translation="لكن، كيف الأوضاع؟"/>
<dialogue speaker="red" english="I see! Well, we&apos;d better get back then." translation="فهمت... إذن فلنتحرك ونرجع."/>
<dialogue speaker="red" english="There&apos;s a teleporter in the next room." translation="توجد آلة تنقيل في الغرفة المجاورة."/>
</cutscene>
<cutscene id="rescueyellow" explanation="">
<dialogue speaker="yellow" english="Ah, Viridian! You got off the ship alright too?" translation="أه ، فيريديان! حتى أنت نجوت بدون إصابات من حادث السفينة؟"/>
<dialogue speaker="player" english="It&apos;s good to see you&apos;re alright, Professor!" translation="أستاذ؟ يسعدني أنك بخير!"/>
<dialogue speaker="yellow" english="Is the ship ok?" translation="هل السفينة بخير؟"/>
<dialogue speaker="player" english="It&apos;s badly damaged, but Violet&apos;s been working on fixing it." translation="تعرضت لأضرار بليغة، لكن فايولت تعمل على إصلاحها."/>
<dialogue speaker="player" english="We could really use your help..." translation="ستفيدنا مساعدتك جدا..."/>
<dialogue speaker="yellow" english="Ah, of course!" translation="أه، عن طيب خاطر!"/>
<dialogue speaker="yellow" english="The background interference in this dimension prevented the ship from finding a teleporter when we crashed!" translation="منع تشويش المحيط في نطاق هذا البعد سفينتنا من إيجاد آلة تنقيل قبيل الارتطام!"/>
<dialogue speaker="yellow" english="We&apos;ve all been teleported to different locations!" translation="تفرقنا ونقلنا إلى أماكن مختلفة!"/>
<dialogue speaker="player" english="Er, that sounds about right!" translation="همم، يبدو لي كلامك منطقيا!"/>
<dialogue speaker="yellow" english="Let&apos;s get back to the ship, then!" translation="فلنرجع إلى السفينة!"/>
<dialogue speaker="yellow" english="After you, Captain!" translation="من بعدك يا قبطان!"/>
</cutscene>
<cutscene id="int1blue_1" explanation="">
<dialogue speaker="blue" english="Waaaa!" translation="وااااء!" case="1"/>
<dialogue speaker="blue" english="Captain! Are you ok?" translation="قبطان! هل أنت بخير؟"/>
<dialogue speaker="player" english="I&apos;m ok... this... this isn&apos;t the ship..." translation="أنا بخير... لكن... لسنا في السفينة..."/>
<dialogue speaker="player" english="Where are we?" translation="يا ترى... أين نحن؟"/>
<dialogue speaker="blue" english="Waaaa!" translation="وااااء!" case="2"/>
<dialogue speaker="player" english="Something&apos;s gone wrong... We should look for a way back!" translation="حصلت مشكلة... فلنبحث عن طريق العودة!"/>
</cutscene>
<cutscene id="int1blue_2" explanation="">
<dialogue speaker="player" english="Follow me! I&apos;ll help you!" translation="اتبعيني! سأنقذك!"/>
<dialogue speaker="blue" english="Promise you won&apos;t leave without me!" translation="أرجوك! أريد منك وعدا بعدم المغادرة وتركي!"/>
<dialogue speaker="player" english="I promise! Don&apos;t worry!" translation="وعد! لا تقلقي!"/>
</cutscene>
<cutscene id="int1blue_3" explanation="">
<dialogue speaker="player" english="Are you ok down there, Doctor?" translation="هل نزلت على خير يا دكتورة؟"/>
<dialogue speaker="blue" english="I wanna go home!" translation="آه، أريد العودة للديار!"/>
<dialogue speaker="blue" english="Where are we? How did we even get here?" translation="أين نحن؟ كيف جئنا هنا؟"/>
<dialogue speaker="player" english="Well, Violet did say that the interference in the dimension we crashed in was causing problems with the teleporters..." translation="صحيح، قالت فايولت أن في هذا البعد تشويشا يعرقل عمل آلات التنقيل..."/>
<dialogue speaker="player" english="I guess something went wrong..." translation="فأظن مشكلة حصلت في الأثناء..."/>
<dialogue speaker="player" english="But if we can find another teleporter, I think we can get back to the ship!" translation="لكن لو عثرنا على آلة تنقيل بديلة، يمكننا العودة للسفينة على ما أظن!"/>
<dialogue speaker="blue" english="Sniff..." translation="إهئ..."/>
</cutscene>
<cutscene id="int1blue_4" explanation="the split paths merge">
<dialogue speaker="blue" english="Captain! Captain! Wait for me!" translation="قبطان! قبطان! أرجوك الانتظار!"/>
<dialogue speaker="blue" english="Please don&apos;t leave me behind! I don&apos;t mean to be a burden!" translation="أرجوك، لا أريد أن تتخلى عني! لا أقصد أن أكون عبئا عليك!"/>
<dialogue speaker="blue" english="I&apos;m scared!" translation="كل ما في الأمر... أني خائفة!"/>
<dialogue speaker="player" english="Oh... don&apos;t worry Victoria, I&apos;ll look after you!" translation="أوه... لا تقلقي يا فيكتوريا، سأعتني بك!"/>
</cutscene>
<cutscene id="int1blue_5" explanation="">
<dialogue speaker="blue" english="We&apos;re never going to get out of here, are we?" translation="لن نخرج من هنا أبدا، أليس كذلك؟"/>
<dialogue speaker="player" english="I.. I don&apos;t know..." translation="أنا... لا أدري..."/>
<dialogue speaker="player" english="I don&apos;t know where we are or how we&apos;re going to get out..." translation="لا أعرف أين نحن، ولا كيف سنخرج..."/>
</cutscene>
<cutscene id="int1blue_6" explanation="">
<dialogue speaker="blue" english="We&apos;re going to be lost forever!" translation="سنضيع فيها إلى الأبد!"/>
<dialogue speaker="player" english="Ok, come on... Things aren&apos;t that bad." translation="لحظة، صبرا... لا داع للتشاؤم إلى هذا الحد."/>
<dialogue speaker="player" english="I have a feeling that we&apos;re nearly home!" translation="عندي إحساس أننا اقتربنا من طريق العودة للديار!"/>
<dialogue speaker="player" english="We can&apos;t be too far from another teleporter!" translation="لسنا بعيدين عن آلة تنقيل أخرى بلا شك!"/>
<dialogue speaker="blue" english="I hope you&apos;re right, captain..." translation="أتمنى أنك على حق يا قبطان..."/>
</cutscene>
<cutscene id="int1blue_7" explanation="">
<dialogue speaker="blue" english="Captain! You were right! It&apos;s a teleporter!" translation="قبطان! كلامك كان صحيحا! توجد آلة تنقيل!"/>
<dialogue speaker="player" english="Phew! You had me worried for a while there... I thought we were never going to find one." translation="هوف! تنفست الصعداء بعد طول قلق... خشيت أننا لن نجد آلة تنقيل أخرى."/>
<dialogue speaker="blue" english="What? Really?" translation="كيف؟ حقا؟"/>
<dialogue speaker="player" english="Anyway, let&apos;s go back to the ship." translation="على كل! فلنعد إلى السفينة."/>
</cutscene>
<cutscene id="int1green_1" explanation="">
<dialogue speaker="green" english="Huh? This isn&apos;t the ship..." translation="هاه؟ ليست هذه السفينة..."/>
<dialogue speaker="green" english="Captain! What&apos;s going on?" translation="قبطان! ما الخطب؟"/>
<dialogue speaker="player" english="I... I don&apos;t know!" translation="أنا... لا أعرف السبب!"/>
<dialogue speaker="player" english="Where are we?" translation="أين نحن الآن؟"/>
<dialogue speaker="green" english="Uh oh, this isn&apos;t good... Something must have gone wrong with the teleporter!" translation="أوه لا، لا يسر هذا... حصلت مشكلة في آلة التنقيل بلا شك!"/>
<dialogue speaker="player" english="Ok... no need to panic!" translation="طيب... لا داعي للجزع!"/>
<dialogue speaker="player" english="Let&apos;s look for another teleporter!" translation="فلنبحث عن آلة تنقيل أخرى!"/>
</cutscene>
<cutscene id="int1green_2" explanation="">
<dialogue speaker="player" english="Let&apos;s go this way!" translation="من هذا الطريق!"/>
<dialogue speaker="green" english="After you, Captain!" translation="معك يا قبطان!"/>
</cutscene>
<cutscene id="int1green_3" explanation="just like in rescuegreen, `Violet is back on the ship` does not mean she was ever off the ship and has *returned* to it. Or maybe Verdigris thinks Violet was warped off the ship as well just like him and she *has* made her way back to the ship?">
<dialogue speaker="green" english="So Violet&apos;s back on the ship? She&apos;s really ok?" translation="إذن فايوليت عادت للسفينة؟ حقا هي بخير؟"/>
<dialogue speaker="player" english="She&apos;s fine! She helped me find my way back!" translation="اطمئن! هي من ساعدتني على الاهتداء لطريق العودة!"/>
<dialogue speaker="green" english="Oh, phew! I was worried about her." translation="أوه، هوف! خفت عليها."/>
<dialogue speaker="green" english="Captain, I have a secret..." translation="قبطان، سأبوح لك بسر..."/>
<dialogue speaker="green" english="I really like Violet!" translation="حقا أحب فايولت!"/>
<dialogue speaker="player" english="Is that so?" translation="أحقا هذا؟"/>
<dialogue speaker="green" english="Please promise you won&apos;t tell her!" translation="أترجاك عدم البوح بهذا السر أمامها!"/>
</cutscene>
<cutscene id="int1green_4" explanation="the split paths merge">
<dialogue speaker="green" english="Hey again!" translation="أهلا مجددا!"/>
<dialogue speaker="player" english="Hey!" translation="أهلا!"/>
<dialogue speaker="player" english="Are you doing ok?" translation="أأنت بخير؟"/>
<dialogue speaker="green" english="I think so! I really hope we can find a way back to the ship..." translation="أظن! أرجو حقا أن نجد طريق العودة للسفينة..."/>
</cutscene>
<cutscene id="int1green_5" explanation="">
<dialogue speaker="green" english="So, about Violet..." translation="أردت سؤالك يا قبطان، بخصوص فايولت..."/>
<dialogue speaker="player" english="Um, yeah?" translation="همم؟ عم السؤال؟"/>
<dialogue speaker="green" english="Do you have any advice?" translation="هل عندك نصائح لي؟"/>
<dialogue speaker="player" english="Oh!" translation="... أوه!"/>
<dialogue speaker="player" english="Hmm..." translation="هممم، أمهلني كي أفكر...."/>
<dialogue speaker="player" english="Um... you should... be yourself!" translation="اممم... أنصحك أن... تتصرف على طبيعتك!"/>
<dialogue speaker="green" english="Oh." translation="أوه."/>
<dialogue speaker="green" english="Thanks Captain!" translation="شكرا قبطاني!"/>
</cutscene>
<cutscene id="int1green_6" explanation="">
<dialogue speaker="player" english="So, do you think you&apos;ll be able to fix the ship?" translation="ب‍المناسبة، هل تقدر على إصلاح السفينة؟"/>
<dialogue speaker="green" english="Depends on how bad it is... I think so, though!" translation="حسب فداحة الأضرار... لكني أظن أني على قدر المهمة!"/>
<dialogue speaker="green" english="It&apos;s not very hard, really. The basic dimensional warping engine design is pretty simple, and if we can get that working we shouldn&apos;t have any trouble getting home." translation="ليس الأمر من الصعوبة بمكان. أما عن أساس فكرة التصميم لمحرك التنقيل ما بين الأبعاد، فمسألته بسيطة، وإن شغلناه فلن نلقى عقبة في العودة للديار."/>
<dialogue speaker="player" english="Oh! Good!" translation="أوه! جميل!"/>
</cutscene>
<cutscene id="int1green_7" explanation="">
<dialogue speaker="green" english="Finally! A teleporter!" translation="أخيرا! آلة تنقيل!"/>
<dialogue speaker="green" english="I was getting worried we wouldn&apos;t find one..." translation="بدأت أقلق وأتساءل ماذا لو لم نجدها..."/>
<dialogue speaker="player" english="Let&apos;s head back to the ship!" translation="فلنرجع إلى السفينة!"/>
</cutscene>
<cutscene id="int1red_1" explanation="">
<dialogue speaker="red" english="Wow! Where are we?" translation="واو! أين نحن؟"/>
<dialogue speaker="player" english="This... isn&apos;t right... Something must have gone wrong with the teleporter!" translation="لا... لا يصح هذا... حصلت مشكلة في آلة التنقيل بدون شك!"/>
<dialogue speaker="red" english="Oh well... We can work it out when we get back to the ship!" translation="حقا... لكن لا بأس، سنتوصل إلى جواب بعد أن نرجع إلى السفينة!"/>
<dialogue speaker="red" english="Let&apos;s go exploring!" translation="فلننطلق في رحلة استكشاف جديدة!"/>
<dialogue speaker="player" english="Ok then!" translation="هيا بنا إذن!"/>
</cutscene>
<cutscene id="int1red_2" explanation="">
<dialogue speaker="player" english="Follow me!" translation="فلتتبعني!"/>
<dialogue speaker="red" english="Aye aye, Captain!" translation="لا يأمر عليك ظالم، يا قبطان!"/>
</cutscene>
<cutscene id="int1red_3" explanation="">
<dialogue speaker="red" english="Hey Viridian... how did the crash happen, exactly?" translation="ها فيريديان... كيف حصل حادث الارتطام بالضبط؟"/>
<dialogue speaker="player" english="Oh, I don&apos;t really know - some sort of interference..." translation="أوه، لا أدري عن التفاصيل - كأنها إشارة تشويش..."/>
<dialogue speaker="player" english="...or something sciencey like that. It&apos;s not really my area." translation="...أو كلام علماء من هذا النوع. خارج عن تخصصي."/>
<dialogue speaker="red" english="Ah! Well, do you think we&apos;ll be able to fix the ship and go home?" translation="أه! طيب، هل تظننا سنتمكن من إصلاح السفينة والعودة للديار؟"/>
<dialogue speaker="player" english="Of course! Everything will be ok!" translation="طبعا بلا شك! ستحل كل الأمور!"/>
</cutscene>
<cutscene id="int1red_4" explanation="the split paths merge">
<dialogue speaker="red" english="Hi again! You doing ok?" translation="أهلا مجددا! أحوالك بخير؟"/>
<dialogue speaker="player" english="I think so! But I really want to get back to the ship..." translation="أظن! لكني بدأت أشتاق جدا للسفينة..."/>
<dialogue speaker="red" english="We&apos;ll be ok! If we can find a teleporter somewhere we should be able to get back!" translation="لا خوف علينا! لو عثرنا على آلة تنقيل سنتمكن من العودة!"/>
</cutscene>
<cutscene id="int1red_5" explanation="">
<dialogue speaker="red" english="Are we there yet?" translation="هل وصلنا أم ما زلنا؟"/>
<dialogue speaker="player" english="We&apos;re getting closer, I think..." translation="أظننا نقترب..."/>
<dialogue speaker="player" english="I hope..." translation="لعل وعسى..."/>
</cutscene>
<cutscene id="int1red_6" explanation="">
<dialogue speaker="player" english="I wonder where we are, anyway?" translation="يا ترى أين نحن أصلا؟"/>
<dialogue speaker="player" english="This seems different from that dimension we crashed in, somehow..." translation="يبدو هذا النطاق مختلفا عن البعد الذي سقطنا فيه، لا أدري كيف..."/>
<dialogue speaker="red" english="I dunno... But we must be close to a teleporter by now..." translation="ما أدراني... لكن لا شك أننا اقتربنا من آلة تنقيل بعد هذا المسير..."/>
</cutscene>
<cutscene id="int1red_7" explanation="">
<dialogue speaker="player" english="We&apos;re there!" translation="وصلنا!"/>
<dialogue speaker="red" english="See? I told you! Let&apos;s get back to the ship!" translation="رأيت؟ قلت لك! فلنرجع للسفينة!"/>
</cutscene>
<cutscene id="int1yellow_1" explanation="">
<dialogue speaker="yellow" english="Oooh! This is interesting..." translation="أوووه! مثير..."/>
<dialogue speaker="yellow" english="Captain! Have you been here before?" translation="قبطان! هل سبق وجئت إلى هنا؟"/>
<dialogue speaker="player" english="What? Where are we?" translation="ما هذا... أين نحن؟"/>
<dialogue speaker="yellow" english="I suspect something deflected our teleporter transmission! This is somewhere new..." translation="يخامرني شك أن شيئا ما أثر في بث تنقيلتنا وأحادها عن مسارها! هذا المكان جديد علينا..."/>
<dialogue speaker="player" english="Oh no!" translation="مصيبة!"/>
<dialogue speaker="player" english="We should try to find a teleporter and get back to the ship..." translation="فلنحاول أن نجد آلة تنقيل حتى نرجع للسفينة..."/>
</cutscene>
<cutscene id="int1yellow_2" explanation="">
<dialogue speaker="player" english="Follow me!" translation="اتبعني!"/>
<dialogue speaker="yellow" english="Right behind you, Captain!" translation="على خطاك نسير، يا قبطان!"/>
</cutscene>
<cutscene id="int1yellow_3" explanation="">
<dialogue speaker="player" english="What do you make of all this, Professor?" translation="...بروفيسور، ما استنتاجاتك؟"/>
<dialogue speaker="yellow" english="I&apos;m guessing this dimension has something to do with the interference that caused us to crash!" translation="أخمن أن لهذا البعد يدا في سبب التشويش الذي سبب حادث ارتطام سفينتنا!"/>
<dialogue speaker="yellow" english="Maybe we&apos;ll find the cause of it here?" translation="لعلنا نلقى أصل السبب هنا؟"/>
<dialogue speaker="player" english="Oh wow! Really?" translation="أوه واو! حقا؟"/>
<dialogue speaker="yellow" english="Well, it&apos;s just a guess. I&apos;ll need to get back to the ship before I can do any real tests..." translation="لكنه تخمين لا أكثر. أحتاج العودة للسفينة قبل أن أجري أي اختبارات جادة..."/>
</cutscene>
<cutscene id="int1yellow_4" explanation="the split paths merge, and Vitellary sees a checkpoint">
<dialogue speaker="yellow" english="Ohh! What was that?" translation="أووو! ما هذا؟"/>
<dialogue speaker="player" english="What was what?" translation="ما هذا الذي تقول عنده ما هذا؟"/>
<dialogue speaker="yellow" english="That big... C thing! I wonder what it does?" translation="تلك... الحاء الكبيرة! في ذلك الشيء! ماذا يفعل يا ترى؟"/>
<dialogue speaker="player" english="Em... I don&apos;t really know how to answer that question..." translation="احم... لا أعرف كيف أجيبك على سؤالك هذا..."/>
<dialogue speaker="player" english="It&apos;s probably best not to acknowledge that it&apos;s there at all." translation="الأفضل أن نتجاهل وجوده، ونمر عليه مرور الكرام."/>
<dialogue speaker="yellow" english="Maybe we should take it back to the ship to study it?" translation="ألا يجدر بنا جلبه للسفينة حتى نجري عليه دراسات؟"/>
<dialogue speaker="player" english="We really shouldn&apos;t think about it too much... Let&apos;s keep moving!" translation="حقا من الأفضل ألا نفرط في التفكير في أمره... ولنتحرك!"/>
</cutscene>
<cutscene id="int1yellow_5" explanation="">
<dialogue speaker="yellow" english="You know, there&apos;s something really odd about this dimension..." translation="لو علمت بم أفكر الآن... أحس أن في هذا البعد أمرا في غاية الغرابة..."/>
<dialogue speaker="player" english="Yeah?" translation="صحيح؟"/>
<dialogue speaker="yellow" english="We shouldn&apos;t really be able to move between dimensions with a regular teleporter..." translation="لا يفترض بنا أن نتمكن من التنقل بين الأبعاد بمجرد آلة تنقيل عادية..."/>
<dialogue speaker="yellow" english="Maybe this isn&apos;t a proper dimension at all?" translation="ألا يحتمل إذن أن هذا ليس ببعد قائم الذات؟"/>
<dialogue speaker="yellow" english="Maybe it&apos;s some kind of polar dimension? Something artificially created for some reason?" translation="ربما هو بعد قطبي، أو شيء من ذلك النوع؟ بمعنى أنه شيء كونوه اصطناعيا لسبب من الأسباب؟"/>
<dialogue speaker="yellow" english="I can&apos;t wait to get back to the ship. I have a lot of tests to run!" translation="لا أطيق الصبر على العودة للسفينة. كم عندي اختبارات كثيرة أجريها!"/>
</cutscene>
<cutscene id="int1yellow_6" explanation="">
<dialogue speaker="yellow" english="I wonder if there&apos;s anything else in this dimension worth exploring?" translation="أتساءل هل في هذا البعد مكانا آخرا يستحق الاستطلاع؟"/>
<dialogue speaker="player" english="Maybe... but we should probably just focus on finding the rest of the crew for now..." translation="ربما... لكن يجدر بنا أن نركز على إنجاد بقية أفراد طاقمنا، ثم للحديث بقية..."/>
</cutscene>
<cutscene id="int1yellow_7" explanation="">
<dialogue speaker="yellow" english="At last!" translation="أخيرا!"/>
<dialogue speaker="yellow" english="Let&apos;s go back to the ship!" translation="فلنرجع إلى السفينة!"/>
</cutscene>
<cutscene id="intermission_2" explanation="">
<dialogue speaker="player" english="Uh oh..." translation="أوه لا..."/>
<dialogue speaker="player" english="Not again!" translation="فاجأتنا مرة أخرى!"/>
</cutscene>
<cutscene id="int2intro_blue" explanation="">
<dialogue speaker="player" english="Victoria? Where are you?" translation="فيكتوريا؟ أين أنت؟"/>
<dialogue speaker="blue" english="Help!" translation="أنقذوني!"/>
<dialogue speaker="player" english="Hang on! I&apos;ll save you!" translation="اصبري! سأنقذك فورا!"/>
</cutscene>
<cutscene id="int2intro_green" explanation="">
<dialogue speaker="player" english="Verdigris? Where are you?" translation="فارديغري؟ أين أنت؟"/>
<dialogue speaker="green" english="Aaagghh!" translation="هاااااع!"/>
<dialogue speaker="player" english="Hang on! I&apos;ll save you!" translation="مهلا! جئت لأنقذك!"/>
</cutscene>
<cutscene id="int2intro_red" explanation="">
<dialogue speaker="player" english="Vermilion? Where are you?" translation="فارميليون؟ أين أنت؟"/>
<dialogue speaker="red" english="Wheeeee!" translation="ويييييه!"/>
<dialogue speaker="player" english="Hang on! I&apos;ll save you!" translation="صبرا! سأنقذك!"/>
</cutscene>
<cutscene id="int2intro_yellow" explanation="">
<dialogue speaker="player" english="Vitellary? Where are you?" translation="فيتيلاري؟ أين ذهبت؟"/>
<dialogue speaker="yellow" english="Captain!" translation="قبطان!"/>
<dialogue speaker="player" english="Hang on! I&apos;ll save you!" translation="تحمل! سأنقذك!"/>
</cutscene>
<cutscene id="int2_blue" explanation="after gravitron">
<dialogue speaker="blue" english="I think I&apos;m going to be sick..." translation="يكفيني، سأصاب بالغثيان..."/>
<dialogue speaker="player" english="I feel dizzy..." translation="آه، يا للدوار..."/>
</cutscene>
<cutscene id="int2_green" explanation="after gravitron">
<dialogue speaker="green" english="Phew! You&apos;re ok!" translation="هووف! أنت بخير!"/>
<dialogue speaker="player" english="I feel dizzy..." translation="آه، يا للدوار..."/>
</cutscene>
<cutscene id="int2_red" explanation="after gravitron">
<dialogue speaker="red" english="Again! Let&apos;s go again!" translation="مرة ثانية! لنجربها مرة ثانية!"/>
<dialogue speaker="player" english="I feel dizzy..." translation="آه، يا للدوار..."/>
</cutscene>
<cutscene id="int2_yellow" explanation="after gravitron">
<dialogue speaker="yellow" english="That was interesting, wasn&apos;t it?" translation="ألم تكن نزهة مثيرة للحماس؟"/>
<dialogue speaker="player" english="I feel dizzy..." translation="آه، يا للدوار..."/>
</cutscene>
<cutscene id="talkpurple_1" explanation="">
<dialogue speaker="purple" english="... I hope Verdigris is alright." translation="... أرجو أن فارديغري بخير."/>
<dialogue speaker="purple" english="If you can find him, he&apos;d be a big help fixing the ship!" translation="ليتك وجدته، لأنه سيكون عونا كبيرا في إصلاح السفينة!"/>
</cutscene>
<cutscene id="talkpurple_2" explanation="">
<dialogue speaker="purple" english="Chief Verdigris is so brave and ever so smart!" translation="كم القائد فيرديغري شجاع، وشهم، وذكي على الدوام!"/>
</cutscene>
<cutscene id="talkpurple_4" explanation="">
<dialogue speaker="purple" english="Welcome back, Captain!" translation="أهلا بعودتك بالسلامة يا قبطان!"/>
<dialogue speaker="purple" english="I think Victoria is quite happy to be back on the ship." translation="أحسب أن فيكتوريا سعيدة جدا بعودتها للسفينة."/>
<dialogue speaker="purple" english="She really doesn&apos;t like adventuring. She gets very homesick!" translation="لا تحب أجواء المغامرات كثيرا. لأنها سرعان ما تحن للبيت!"/>
</cutscene>
<cutscene id="talkpurple_5" explanation="only one of the last 6 strings is shown">
<dialogue speaker="purple" english="Vermilion called in to say hello!" translation="اتصل فارميليون، ويسلم عليك!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find the rest of the crew!" translation="يتوق حقا لمساعدتك في إيجاد بقية الطاقم!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find Victoria!" translation="يتوق حقا لمساعدتك في إيجاد فيكتوريا!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find Vitellary!" translation="يتوق حقا لمساعدتك في إيجاد فيتيلاري!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find Verdigris!" translation="يتوق حقا لمساعدتك في إيجاد فارديغري!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find Vermilion!" translation="يتوق حقا لمساعدتك في إيجاد فارميليون!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find you!" translation="يتوق حقا لمساعدتك في التوصل لإيجاد مكانك!"/>
</cutscene>
<cutscene id="talkpurple_6" explanation="">
<dialogue speaker="purple" english="Captain! You found Verdigris!" translation="قبطان! عثرت على فارديغري!"/>
<dialogue speaker="purple" english="Thank you so much!" translation="حقا أشكرك! شكرا جزيلا!"/>
</cutscene>
<cutscene id="talkpurple_7" explanation="">
<dialogue speaker="purple" english="I&apos;m glad Professor Vitellary is ok!" translation="سعيدة أن البروفيسور فيتيلاري بخير!"/>
<dialogue speaker="purple" english="He had lots of questions for me about this dimension." translation="سألني أسئلة كثيرة عن هذا البعد."/>
<dialogue speaker="purple" english="He&apos;s already gotten to work with his research!" translation="وسرعان ما عاد إلى العمل على أبحاثه!"/>
</cutscene>
<cutscene id="talkpurple_intermission1" explanation="">
<dialogue speaker="player" english="Doctor, something strange happened when we teleported back to the ship..." translation="دكتور، حصل شيء غريب عندما انتقلنا راجعين للسفينة..."/>
<dialogue speaker="player" english="We got lost in another dimension!" translation="تهنا في بعد آخر!"/>
<dialogue speaker="purple" english="Oh no!" translation="أوه لا!"/>
<dialogue speaker="purple" english="Maybe that dimension has something to do with the interference that caused us to crash here?" translation="ربما لذلك البعد دخل في التشويش الذي تسبب في حادث ارتطامنا؟"/>
<dialogue speaker="purple" english="I&apos;ll look into it..." translation="سأنظر في الموضوع..."/>
</cutscene>
<cutscene id="talkpurple_intermission2" explanation="">
<dialogue speaker="player" english="Doctor! Doctor! It happened again!" translation="دكتور! دكتور! تكرر الأمر!"/>
<dialogue speaker="player" english="The teleporter brought us to that weird dimension..." translation="أوصلتنا آلة التنقيل إلى ذلك البعد الغريب..."/>
<dialogue speaker="purple" english="Hmm, there&apos;s definitely something strange happening..." translation="همممم، بالتأكيد يحصل شيء غريب..."/>
<dialogue speaker="purple" english="If only we could find the source of that interference!" translation="ليتنا نتوصل إلى مصدر ذلك التشويش!"/>
</cutscene>
<cutscene id="talkpurple_intermission3" explanation="">
<dialogue speaker="player" english="Doctor, something strange has been happening when we teleport back to the ship..." translation="دكتور، حصل شيء غريب عندما انتقلنا راجعين للسفينة..."/>
<dialogue speaker="player" english="We keep getting brought to another weird dimension!" translation="يوجد بعد غريب، وأرغمنا مجددا على الانتقال إليه!"/>
<dialogue speaker="purple" english="Oh no!" translation="أوه لا!"/>
<dialogue speaker="purple" english="Maybe that dimension has something to do with the interference that caused us to crash here?" translation="ربما لذلك البعد دخل في التشويش الذي تسبب في حادث ارتطامنا؟"/>
<dialogue speaker="purple" english="Hmm, there&apos;s definitely something strange happening..." translation="همممم، بالتأكيد يحصل شيء غريب..."/>
<dialogue speaker="purple" english="If only we could find the source of that interference!" translation="ليتنا نتوصل إلى مصدر ذلك التشويش!"/>
</cutscene>
<cutscene id="talkpurple_8" explanation="">
<dialogue speaker="purple" english="Hey Captain! Now that you&apos;ve turned off the source of the interference, we can warp everyone back to the ship instantly, if we need to!" translation="هيه يا قبطان! بعد أن أغلقت مصدر التشويش، صرنا نستطيع أن ننقل الجميع إلى متن السفينة فور ما نشاء، وقتما نحتاج!"/>
<dialogue speaker="purple" english="Any time you want to come back to the ship, just select the new SHIP option in your menu!" translation="متى أردت العودة للسفينة، يمكنك اختيار خيار السفينة في القائمة!"/>
</cutscene>
<cutscene id="talkgreen_1" explanation="">
<dialogue speaker="green" english="I&apos;m an engineer!" translation="أنا مهندس!"/>
</cutscene>
<cutscene id="talkgreen_2" explanation="">
<dialogue speaker="green" english="I think I can get this ship moving again, but it&apos;s going to take a while..." translation="أستطيع جعل السفينة تتحرك ثانية، لكن عملي سيستغرق فترة..."/>
</cutscene>
<cutscene id="talkgreen_3" explanation="">
<dialogue speaker="green" english="Victoria mentioned something about a lab? I wonder if she found anything down there?" translation="حكت لك فايوليت عن مخبر؟ ترى هل وجدت فيه شيئا؟"/>
</cutscene>
<cutscene id="talkgreen_4" explanation="">
<dialogue speaker="green" english="Vermilion&apos;s back! Yey!" translation="فارميليون عاد! يا لفرحتي، ياي!!"/>
</cutscene>
<cutscene id="talkgreen_5" explanation="">
<dialogue speaker="green" english="The Professor had lots of questions about this dimension for me..." translation="سألني البروفيسور أسئلة كثيرة بخصوص هذا البعد..."/>
<dialogue speaker="green" english="We still don&apos;t really know that much, though." translation="ومع هذا لا زلنا لا نعرف الكثير."/>
<dialogue speaker="green" english="Until we work out what&apos;s causing that interference, we can&apos;t go anywhere." translation="إلى أن نتوصل لمعرفة ما الذي يتسبب بالتداخل، لن نستطيع التحرك قيد أنملة."/>
</cutscene>
<cutscene id="talkgreen_6" explanation="">
<dialogue speaker="green" english="I&apos;m so glad that Violet&apos;s alright!" translation="كم أنا سعيد أن فايوليت بخير!"/>
</cutscene>
<cutscene id="talkgreen_7" explanation="">
<dialogue speaker="green" english="That other dimension we ended up in must be related to this one, somehow..." translation="هل تذكرت ذلك البعد البديل الذي انتهى المطاف بنا إليه؟ أظن بينه وهذا البعد علاقة ما..."/>
</cutscene>
<cutscene id="talkgreen_8" explanation="">
<dialogue speaker="green" english="The antenna&apos;s broken! This is going to be very hard to fix..." translation="الهوائي مكسور! سيصعب التصليح جدا..."/>
</cutscene>
<cutscene id="talkgreen_9" explanation="">
<dialogue speaker="green" english="It looks like we were warped into solid rock when we crashed!" translation="يبدو أننا عندما ارتطمنا هنا، حصلت تنقيلة للسفينة إلى داخل كتلة صخرية!"/>
<dialogue speaker="green" english="Hmm. It&apos;s going to be hard to separate from this..." translation="هممم. سيصعب فصل السفينة عنها..."/>
</cutscene>
<cutscene id="talkgreen_10" explanation="">
<dialogue speaker="green" english="The ship&apos;s all fixed up. We can leave at a moment&apos;s notice!" translation="أصلحت السفينة بأكملها. يمكننا المغادرة في أي لحظة، والأمر رهن إشارتك!"/>
</cutscene>
<cutscene id="talkred_1" explanation="">
<dialogue speaker="red" english="Don&apos;t worry, Sir!" translation="لا قلق، يا فندم!"/>
<dialogue speaker="red" english="We&apos;ll find a way out of here!" translation="سنبحث عن طريق للخروج من هنا!"/>
</cutscene>
<cutscene id="talkred_2" explanation="">
<dialogue speaker="red" english="I hope Victoria is ok..." translation="أرجو أن فيكتوريا بخير..."/>
<dialogue speaker="red" english="She doesn&apos;t handle surprises very well..." translation="أعرفها، ولا تتحمل المفاجآت جيدا..."/>
</cutscene>
<cutscene id="talkred_3" explanation="">
<dialogue speaker="red" english="I don&apos;t know how we&apos;re going to get this ship working again!" translation="لا أدري كيف سنتصرف لتشغيل هذه السفينة مجددا!"/>
<dialogue speaker="red" english="Chief Verdigris would know what to do..." translation="لو كان القائد فارديغري هنا، لكان يعرف الحل..."/>
</cutscene>
<cutscene id="talkred_4" explanation="">
<dialogue speaker="red" english="I wonder what caused the ship to crash here?" translation="يا ترى ما الذي سبب ارتطام السفينة في هذا المكان؟"/>
<dialogue speaker="red" english="It&apos;s the shame the Professor isn&apos;t here, huh? I&apos;m sure he could work it out!" translation="خسارة أن البروفيسور ليس هنا. أليس كذلك؟ أنا متأكد أنه لن يحير في إيجاد الجواب!"/>
</cutscene>
<cutscene id="talkred_5" explanation="">
<dialogue speaker="red" english="It&apos;s great to be back!" translation="ما أحلى العودة!"/>
<dialogue speaker="red" english="I can&apos;t wait to help you find the rest of the crew!" translation="أتوق بفارغ الصبر لمساعدتك على إيجاد بقية الطاقم!"/>
<dialogue speaker="red" english="It&apos;ll be like old times, huh, Captain?" translation="مثل الأيام الخوالي، أليس كذلك يا قبطان؟"/>
</cutscene>
<cutscene id="talkred_6" explanation="">
<dialogue speaker="red" english="It&apos;s good to have Victoria back with us." translation="جيد أن فيكتوريا عادت إلينا."/>
<dialogue speaker="red" english="She really seems happy to get back to work in her lab!" translation="بدت لي في غاية السعادة عندما رجعت للعمل في مخبرها!"/>
</cutscene>
<cutscene id="talkred_7" explanation="">
<dialogue speaker="red" english="I think I saw Verdigris working on the outside of the ship!" translation="أظن أني رأيت فارديغري منهمكا في العمل خارج السفينة!"/>
</cutscene>
<cutscene id="talkred_8" explanation="">
<dialogue speaker="red" english="You found Professor Vitellary! All right!" translation="وجدت البروفيسور فيتيلاري! أحلى الأخبار!"/>
<dialogue speaker="red" english="We&apos;ll have this interference thing worked out in no time now!" translation="ما دام الأمر هكذا، فسرعان ما سنحل مشكلة التشويش هذه!"/>
</cutscene>
<cutscene id="talkred_9" explanation="">
<dialogue speaker="red" english="That other dimension was really strange, wasn&apos;t it?" translation="كان ذلك البعد البديل في غاية الغرابة، أليس كذلك؟"/>
<dialogue speaker="red" english="I wonder what caused the teleporter to send us there?" translation="ترى ما الذي جعل آلة التنقيل ترسلنا إلى هناك؟"/>
</cutscene>
<cutscene id="talkred_10" explanation="">
<dialogue speaker="red" english="Heya Captain!" translation="ويه يا قبطان!"/>
<dialogue speaker="red" english="This way looks a little dangerous..." translation="هذا الطريق يبدو لي خطيرا قليلا..."/>
</cutscene>
<cutscene id="talkred_11" explanation="">
<dialogue speaker="red" english="I&apos;m helping!" translation="سأساعدك!"/>
</cutscene>
<cutscene id="talkred_12" explanation="">
<dialogue speaker="red" english="Hey Captain!" translation="أهلا يا قبطان!"/>
<dialogue speaker="red" english="I found something interesting around here - the same warp signature I saw when I landed!" translation="عثرت على شيء مثير للاهتمام هنا - نفس بصمة الطاقة لآلة التنقيل التي رأيتها عندما نزلت!"/>
<dialogue speaker="red" english="Someone from the ship must be nearby..." translation="لا بد أن أحد أفراد طاقم السفينة قربنا..."/>
</cutscene>
<cutscene id="talkred_13" explanation="">
<dialogue speaker="red" english="This dimension is pretty exciting, isn&apos;t it?" translation="هذا البعد حافل بالتشويق والإثارة، صح أو لا؟"/>
<dialogue speaker="red" english="I wonder what we&apos;ll find?" translation="يا ترى ماذا سنلقى؟"/>
</cutscene>
<cutscene id="talkblue_1" explanation="">
<dialogue speaker="blue" english="Any signs of Professor Vitellary?" translation="هل من علامات تدل على مكان البروفيسور فيتيلاري؟"/>
<dialogue speaker="player" english="Sorry, not yet..." translation="معذرة، لم أجده بعد..."/>
<dialogue speaker="blue" english="I hope he&apos;s ok..." translation="أتمنى أنه بخير..."/>
</cutscene>
<cutscene id="talkblue_2" explanation="">
<dialogue speaker="blue" english="Thanks so much for saving me, Captain!" translation="شكرا من القلب لإنقاذي يا قبطان!"/>
</cutscene>
<cutscene id="talkblue_3" explanation="">
<dialogue speaker="blue" english="I&apos;m so glad to be back!" translation="كم أنا سعيدة بعودتي!"/>
<dialogue speaker="blue" english="That lab was so dark and scary! I didn&apos;t like it at all..." translation="كم كان ذلك المخبر مظلما... موحشا... مخيفا! لم أستلطفه إطلاقا..."/>
</cutscene>
<cutscene id="talkblue_4" explanation="">
<dialogue speaker="blue" english="Vitellary&apos;s back? I knew you&apos;d find him!" translation="عاد فيتيلاري؟ كنت واثقة أنكما ستلتقيان!"/>
<dialogue speaker="blue" english="I mean, I admit I was very worried that you wouldn&apos;t..." translation="بل أعترف... أني خفت جدا أنك ربما ذهبت ورجعت دون إيجاده..."/>
<dialogue speaker="blue" english="or that something might have happened to him..." translation="أو أن مكروها حصل له، حاشا وكلا..."/>
<dialogue speaker="blue" english="sniff..." translation="... إهئ!"/>
<dialogue speaker="player" english="Doctor Victoria? He&apos;s ok!" translation="دكتورة فيكتوريا؟ هوني عليك. لم يحصل شر له، فلا داعي للقلق!"/>
<dialogue speaker="blue" english="Oh! Sorry! I was just thinking about what if he wasn&apos;t?" translation="أوه! متأسفة! تخيلت أنه لم يكن بخير، ماذا كنت لأفعل وقتها؟"/>
<dialogue speaker="blue" english="Thank you, Captain!" translation="أشكرك جزيلا، يا قبطان!"/>
</cutscene>
<cutscene id="talkblue_5" explanation="">
<dialogue speaker="blue" english="You found Vermilion! Great!" translation="وجدت فارميليون! هذا خبر رائع!"/>
<dialogue speaker="blue" english="I wish he wasn&apos;t so reckless!" translation="ليته يقلل من طيشه!"/>
<dialogue speaker="blue" english="He&apos;ll get himself into trouble..." translation="سيودي به تهوره إلى مشكلة كبيرة..."/>
</cutscene>
<cutscene id="talkblue_6" explanation="">
<dialogue speaker="blue" english="Verdigris is ok! Violet will be so happy!" translation="فارديغري بخير! ستسعد فايولت كثيرا!"/>
<dialogue speaker="blue" english="I&apos;m happy!" translation="أنا سعيدة!"/>
<dialogue speaker="blue" english="Though I was very worried..." translation="مع ذلك كنت قلقة جدا..."/>
</cutscene>
<cutscene id="talkblue_7" explanation="">
<dialogue speaker="blue" english="Why did the teleporter send us to that scary dimension?" translation="لماذا أرسلتنا آلة التنقيل تلك إلى ذلك البعد الموحش؟"/>
<dialogue speaker="blue" english="What happened?" translation="ماذا حصل وقتها؟"/>
<dialogue speaker="player" english="I don&apos;t know, Doctor..." translation="لا أدري يا دكتورة..."/>
<dialogue speaker="blue" english="Why?" translation="لماذا؟"/>
</cutscene>
<cutscene id="talkblue_8" explanation="">
<dialogue speaker="blue" english="Heya Captain!" translation="يا أهلا يا قبطان!"/>
<dialogue speaker="blue" english="Are you going to try and find the rest of these shiny things?" translation="هل نويت محاولة جمع بقية هذه الأغراض اللماعة؟"/>
</cutscene>
<cutscene id="talkblue_trinket1" explanation="">
<dialogue speaker="blue" english="Hey Captain, I found this in that lab..." translation="أهلا يا قبطان، وجدت هذا في ذلك المخبر..."/>
<dialogue speaker="blue" english="Any idea what it does?" translation="هل عندك فكرة بم يفيدنا؟"/>
<dialogue speaker="player" english="Sorry, I don&apos;t know!" translation="أتأسف، لا أدري!"/>
<dialogue speaker="player" english="They seem important, though..." translation="لكنه يبدو مهما..."/>
<dialogue speaker="player" english="Maybe something will happen if we find them all?" translation="ربما لو جمعناهم كلهم يحصل شيء؟"/>
</cutscene>
<cutscene id="talkblue_trinket2" explanation="">
<dialogue speaker="blue" english="Captain! Come have a look at what I&apos;ve been working on!" translation="قبطان! عندي شيء كنت أعمل عليه، لو أردت رؤيته هيا!"/>
<dialogue speaker="blue" english="It looks like these shiny things are giving off a strange energy reading!" translation="يبدو أن تلك اللماعات الغريبات ترصد منها إشارة طاقة غريبة!"/>
<dialogue speaker="blue" english="So I analysed it..." translation="ولهذا السبب أجريت دراسة تحليلية عليها..."/>
</cutscene>
<cutscene id="talkblue_trinket3" explanation="">
<dialogue speaker="blue" english="Captain! Come have a look at what I&apos;ve been working on!" translation="قبطان! عندي شيء كنت أعمل عليه، لو أردت رؤيته هيا!"/>
<dialogue speaker="blue" english="I found this in that lab..." translation="وجدت هذا في ذلك المخبر..."/>
<dialogue speaker="blue" english="It seemed to be giving off a weird energy reading..." translation="مما بدا لي، صدر منه إشارة طاقة غريبة..."/>
<dialogue speaker="blue" english="So I analysed it..." translation="ولهذا السبب أجريت دراسة تحليلية عليه..."/>
</cutscene>
<cutscene id="talkblue_trinket4" explanation="">
<dialogue speaker="blue" english="...and I was able to find more of them with the ship&apos;s scanner!" translation="...واستطعت أن أجد المزيد منهم بفضل ماسحة السفينة!"/>
<dialogue speaker="blue" english="If you get a chance, it might be worth finding the rest of them!" translation="لو سنحت لك الفرصة، ربما يستحق العناء جمع بقية تلك الأغراض!"/>
<dialogue speaker="blue" english="Don&apos;t put yourself in any danger, though!" translation="لكن... إياك وتعريض نفسك للأخطار!"/>
</cutscene>
<cutscene id="talkblue_trinket5" explanation="">
<dialogue speaker="blue" english="...but it looks like you&apos;ve already found all of them in this dimension!" translation="...إلا أنك وجدتها كلها في هذا البعد قبل حديثنا!"/>
<dialogue speaker="player" english="Oh? Really?" translation="أوه؟ حقا تقولين؟"/>
<dialogue speaker="blue" english="Yeah, well done! That can&apos;t have been easy!" translation="أجل، أحسنت حقا! لا أظن ذلك كان بالمهمة السهلة!"/>
</cutscene>
<cutscene id="talkblue_trinket6" explanation="">
<dialogue speaker="blue" english="...and they&apos;re related. They&apos;re all a part of something bigger!" translation="...كما أن علاقة تجمع بينها. كل منها [بعض] من [كل]!"/>
<dialogue speaker="player" english="Oh? Really?" translation="أوه؟ حقا تقولين؟"/>
<dialogue speaker="blue" english="Yeah! There seem to be twenty variations of the fundamental energy signature..." translation="أجل! يبدو أن للإشارة الأساسية الطاقية عشرون نوعا بتغييرات طفيفة..."/>
<dialogue speaker="blue" english="Wait..." translation="صبرا لحظة..."/>
<dialogue speaker="blue" english="Does that mean you&apos;ve found all of them?" translation="هل عنيت بكلامك أنك وجدتها كلها؟"/>
</cutscene>
<cutscene id="talkyellow_1" explanation="">
<dialogue speaker="yellow" english="I&apos;m making some fascinating discoveries, captain!" translation="توصلت إلى اكتشافات مدهشة، يا قبطان!"/>
</cutscene>
<cutscene id="talkyellow_2" explanation="">
<dialogue speaker="yellow" english="This isn&apos;t like any other dimension we&apos;ve been to, Captain." translation="هذا البعد لا يشبه الأبعاد الأخرى التي زرناها يا قبطان."/>
<dialogue speaker="yellow" english="There&apos;s something strange about this place..." translation="في هذا المكان أمر غريب..."/>
</cutscene>
<cutscene id="talkyellow_3" explanation="">
<dialogue speaker="yellow" english="Captain, have you noticed that this dimension seems to wrap around?" translation="قبطان، هل لاحظت أن هذا البعد يبدو وكأنه يلتف حول نفسه؟"/>
<dialogue speaker="player" english="Yeah, it&apos;s strange..." translation="أجل، غريب ذلك..."/>
<dialogue speaker="yellow" english="It looks like this dimension is having the same stability problems as our own!" translation="يبدو أن هذا البعد يتشاطر مع بعدنا في ديارنا نفس مشاكل الاستقرار!"/>
<dialogue speaker="yellow" english="I hope we&apos;re not the ones causing it..." translation="أرجو أننا لسنا المتسببين بذلك..."/>
<dialogue speaker="player" english="What? Do you think we might be?" translation="ماذا؟ أتظننا سببا؟"/>
<dialogue speaker="yellow" english="No no... that&apos;s very unlikely, really..." translation="لا لا... ذلك احتمال ضئيل، لا يؤخذ به حقا..."/>
</cutscene>
<cutscene id="talkyellow_4" explanation="">
<dialogue speaker="yellow" english="My guess is that whoever used to live here was experimenting with ways to stop the dimension from collapsing." translation="حسب تخميناتي، فمن كانوا يعيشون هنا أجروا تجارب كثيرة وبحثوا في طرق إيقاف انهيار هذا البعد."/>
<dialogue speaker="yellow" english="It would explain why they&apos;ve wrapped the edges..." translation="يفسر هذا لماذا حاولوا ربط الحواف ببعضها..."/>
<dialogue speaker="yellow" english="Hey, maybe that&apos;s what&apos;s causing the interference?" translation="هيه، ربما ذلك ما يسبب التشويش؟"/>
</cutscene>
<cutscene id="talkyellow_5" explanation="">
<dialogue speaker="yellow" english="I wonder where the people who used to live here have gone?" translation="أتساءل إلى أين سافر الناس الذين كانوا يعيشون هنا؟"/>
</cutscene>
<cutscene id="talkyellow_6" explanation="">
<dialogue speaker="yellow" english="I think it&apos;s no coincidence that the teleporter was drawn to that dimension..." translation="لا أظنها صدفة أن آلة التنقيل استقطبتنا إلى هذا البعد..."/>
<dialogue speaker="yellow" english="There&apos;s something there. I think it might be causing the interference that&apos;s stopping us from leaving..." translation="يوجد هناك شيء. وأظنه السبب في التشويش الذي يمنعنا من المغادرة..."/>
</cutscene>
<cutscene id="talkyellow_7" explanation="">
<dialogue speaker="yellow" english="I&apos;m glad Verdigris is alright." translation="سعيد أن فارديغري بخير."/>
<dialogue speaker="yellow" english="It&apos;ll be a lot easier to find some way out of here now that we can get the ship working again!" translation="سيسهل كثيرا إيجاد مخرج من هنا بعد أن صارت السفينة تعمل!"/>
</cutscene>
<cutscene id="talkyellow_8" explanation="">
<dialogue speaker="yellow" english="Ah, you&apos;ve found Doctor Victoria? Excellent!" translation="أه، وجدت الدكتورة فيكتوريا! أخبار رائعة!"/>
<dialogue speaker="yellow" english="I have lots of questions for her!" translation="عندي أسئلة كثيرة أطرحها عليها!"/>
</cutscene>
<cutscene id="talkyellow_9" explanation="">
<dialogue speaker="yellow" english="Vermilion says that he was trapped in some sort of tunnel?" translation="قال فارميليون أنه كان عالقا في نفق ما؟"/>
<dialogue speaker="player" english="Yeah, it just seemed to keep going and going..." translation="أجل، بدا وكأنه يستمر بلا نهاية..."/>
<dialogue speaker="yellow" english="Interesting... I wonder why it was built?" translation="معلومة تفيد... يا ترى ما سبب بنائه على تلك الحالة؟"/>
</cutscene>
<cutscene id="talkyellow_10" explanation="">
<dialogue speaker="yellow" english="It&apos;s good to be back!" translation="ما أحلى العودة!"/>
<dialogue speaker="yellow" english="I&apos;ve got so much work to catch up on..." translation="عندي شغل كثير فاتني يجب أن أتداركه..."/>
</cutscene>
<cutscene id="talkyellow_11" explanation="">
<dialogue speaker="yellow" english="I know it&apos;s probably a little dangerous to stay here now that this dimension is collapsing..." translation="أعرف أن البقاء في هذا البعد، وهو آهل للانهيار، مجازفة بعض الشيء..."/>
<dialogue speaker="yellow" english="...but it&apos;s so rare to find somewhere this interesting!" translation="...لكن يندر أن تجد في الحياة أمرا مثيرا للاهتمام بنفس القدر!"/>
<dialogue speaker="yellow" english="Maybe we&apos;ll find the answers to our own problems here?" translation="ربما قد نجد هنا حلولا للمشاكل التي اعترضتنا في ديارنا؟"/>
</cutscene>
<cutscene id="talkyellow_trinket1" explanation="">
<dialogue speaker="yellow" english="Captain! I&apos;ve been meaning to give this to you..." translation="قبطان! أردت أن أعطيك هذا..."/>
<dialogue speaker="player" english="Professor! Where did you find this?" translation="بروفيسور! من أين لك هذا؟"/>
<dialogue speaker="yellow" english="Oh, it was just lying around that space station." translation="أوه، كان غرضا منسيا في تلك المحطة الفضائية."/>
<dialogue speaker="yellow" english="It&apos;s a pity Doctor Victoria isn&apos;t here, she loves studying that sort of thing..." translation="خسارة أن فيكتوريا ليست هنا، لأنها تعشق دراسة تلك الأشياء..."/>
<dialogue speaker="player" english="Any idea what it does?" translation="هل عندك فكرة عن فائدته؟"/>
<dialogue speaker="yellow" english="Nope! But it is giving off a strange energy reading..." translation="أبدا! لكنه يصدر إشارة طاقة غريبة.."/>
</cutscene>
<cutscene id="talkyellow_trinket2" explanation="">
<dialogue speaker="yellow" english="...so I used the ship&apos;s scanner to find more of them!" translation="...ولهذا السبب استغليت راصدات السفينة كي أبحث عن المزيد منه!"/>
<dialogue speaker="yellow" english="...Please don&apos;t let them distract you from finding Victoria, though!" translation="...لكن أتمنى ألا يشتت هذا اهتمامك عن إنقاذ فيكتوريا!"/>
<dialogue speaker="yellow" english="I hope she&apos;s ok..." translation="أرجو أنها بخير..."/>
</cutscene>
<cutscene id="talkyellow_trinket3" explanation="">
<dialogue speaker="yellow" english="Can&apos;t seem to detect any more of them nearby, though." translation="لم أعد أستطيع استكشاف المزيد منه على مقربة منا."/>
<dialogue speaker="yellow" english="Maybe you&apos;ve found them all?" translation="ربما يعني هذا أنك وجدتهم كلهم؟"/>
</cutscene>
<cutscene id="finallevel_teleporter" explanation="">
<dialogue speaker="purple" english="Welcome back!" translation="يا أهلا، يا أهلا بالعودة!"/>
<dialogue speaker="purple" english="..." translation="..."/>
<dialogue speaker="purple" english="Um, where&apos;s Captain Viridian?" translation="احم، أين القبطان فيريديان؟"/>
<dialogue speaker="player" english="... Hello?" translation="... يا جمع؟"/>
<dialogue speaker="player" english="Is anyone there?" translation="هل من أحد معي؟"/>
</cutscene>
<cutscene id="terminal_finallevel" explanation="">
<dialogue speaker="gray" english="* DIMENSIONAL STABILITY GENERATOR *
[ Currently Generating ]
Maximum Stability
[ Status ]
Online
READY _" translation=" * فارض استقرار البعد *
[ جار توليد الفرض ]
أشد درجات الاستقرار
[ الوضع الحالي ]
يـعـمـل
جاهز_" tt="1"/>
<dialogue speaker="cyan" english="Aha! This must be what&apos;s causing the interference!" translation="آ-هاه! هذا مسبب التشويش بلا شك!"/>
<dialogue speaker="cyan" english="I wonder if I can turn it off?" translation="هل يمكنني إطفاؤه يا ترى؟"/>
<dialogue speaker="gray" english="WARNING: Disabling the Dimensional Stability Generator may lead to instability! Are you sure you want to do this?" translation="تحذير: تعطيل عمل فارض استقرار البعد قد يؤدي لحصول اختلال في الاستقرار! أواثق أن هذه نيتك حقا؟
[ نعم / لا ]"/>
<dialogue speaker="cyan" english="Yes!" translation="نعم!" case="1"/>
<dialogue speaker="gray" english="Seriously! The whole dimension could collapse! Just think about this for a minute!
Are you really sure you want to do this?" translation="ويحك أرجوك! قد يودي هذا بهذا البعد برمته إلى الانهيار الشامل! هلا تعقلت؟ هلا فكرت في التبعات ببعض الجدية، ولو لبرهة؟ ما هو قرارك؟
[ نعم / لا ]"/>
<dialogue speaker="cyan" english="Yes!" translation="نعم!" case="2"/>
</cutscene>
<cutscene id="finalterminal_finish" explanation="">
<dialogue speaker="gray" english="-= WARNING =-
DIMENSIONAL STABILISER OFFLINE" translation="-= تحذير =-
فارض استقرار البعد معطل" centertext="1" pad="1"/>
<dialogue speaker="cyan" english="Uh oh..." translation="أه لا، أه لا..."/>
</cutscene>
<cutscene id="gamecomplete" explanation="">
<dialogue speaker="yellow" english="Any moment now..." translation="صبرا جميلا..."/>
</cutscene>
<cutscene id="gamecomplete_ending" explanation="">
<dialogue speaker="player" english="Hello!" translation="أهلا يا جماعة!"/>
<dialogue speaker="purple" english="Captain!" translation="قبطان!" case="1" pad_right="8"/>
<dialogue speaker="yellow" english="Captain!" translation="قبطان!" case="2" pad_right="6"/>
<dialogue speaker="red" english="Captain!" translation="قبطان!" case="3" pad_right="4"/>
<dialogue speaker="green" english="Captain!" translation="قبطان!" case="4" pad_right="2"/>
<dialogue speaker="blue" english="Captain!" translation="قبطان!" case="5"/>
<dialogue speaker="blue" english="You&apos;re alright!" translation="أنت بخير..."/>
<dialogue speaker="blue" english="I knew you&apos;d be ok!" translation="كنت على ثقة بنجاتك!"/>
<dialogue speaker="purple" english="We were very worried when you didn&apos;t come back..." translation="قلقنا كثيرا عندما تأخرت عودتك..."/>
<dialogue speaker="green" english="...but when you turned off the source of the interference..." translation="...لكن عندما أطفأت أنت مصدر التشويش..."/>
<dialogue speaker="yellow" english="...we were able to find you with the ship&apos;s scanners..." translation="...تمكننا براصدات السفينة من تحديد مكانك..."/>
<dialogue speaker="red" english="...and teleport you back on board!" translation="...وقمنا بتنقيلك على متن السفينة!"/>
<dialogue speaker="player" english="That was lucky!" translation="ذلك من حسن حظي!"/>
<dialogue speaker="player" english="Thanks guys!" translation="شكرا يا جماعة!"/>
<dialogue speaker="yellow" english="...it looks like this dimension is starting to destabilise, just like our own..." translation="...يبدو أن استقرار هذا البعد يختل، مثل ما حصل مع بعدنا..."/>
<dialogue speaker="red" english="...we can stay and explore for a little longer, but..." translation="...يمكننا أن نبقى قليلا بعد هنا لاستطلاع المكان..."/>
<dialogue speaker="yellow" english="...eventually, it&apos;ll collapse completely." translation="...لكن ستأتي لحظة ينهار فيها هذا البعد برمته."/>
<dialogue speaker="green" english="There&apos;s no telling exactly how long we have here. But the ship&apos;s fixed, so..." translation="لا سبيل لنحدد بالضبط كم المهلة الزمنية المتبقية لنا هنا. لكن سفينتنا أصلحت على كل حال..."/>
<dialogue speaker="blue" english="...as soon as we&apos;re ready, we can go home!" translation="...لذا، فور أن نستعد، نستطيع العودة لديارنا!"/>
<dialogue speaker="purple" english="What now, Captain?" translation="والآن يا قبطان، ماذا نحن فاعلون؟"/>
<dialogue speaker="player" english="Let&apos;s find a way to save this dimension!" translation="فلنبحث عن طريقة لإنقاذ هذا البعد!"/>
<dialogue speaker="player" english="And a way to save our home dimension too!" translation="وكذلك... عن طريقة لإنقاذ بعدنا الأصلي!"/>
<dialogue speaker="player" english="The answer is out there, somewhere!" translation="الجواب ينتظرنا في مكان ما!"/>
<dialogue speaker="player" english="Let&apos;s go!" translation="هيا، فلننطلق!"/>
</cutscene>
<cutscene id="startepilogue" explanation="">
<dialogue speaker="blue" english="Wow! You found all of them!" translation="واو! وجدتها كلها!"/>
<dialogue speaker="player" english="Really? Great!" translation="صحيح؟ هذا خبر عظيم!"/>
<dialogue speaker="blue" english="I&apos;ll run some tests and see if I can work out what they&apos;re for..." translation="سأجري بعض الاختبارات كي أحدد ما فائدتها..."/>
<dialogue speaker="player" english="That... that didn&apos;t sound good..." translation="ليس... ليس هذا مطمئنا..."/>
<dialogue speaker="blue" english="Run!" translation="اهربوا!"/>
<dialogue speaker="player" english="Oh no!" translation="أه لا!"/>
<dialogue speaker="red" english="Not again!" translation="آه ليس مجددا!"/>
<dialogue speaker="player" english="Wait! It&apos;s stopped!" translation="صبرا! توقف الصوت!"/>
<dialogue speaker="purple" english="This is where we were storing those shiny things? What happened?" translation="هنا كنا نخزن تلك الأغراض اللماعة؟ ماذا حصل لها؟"/>
<dialogue speaker="player" english="We were just playing with them, and..." translation="كنا نعبث بها فحسب، وإذ بها..."/>
<dialogue speaker="player" english="...they suddenly exploded!" translation="...تنفجر فجأة!"/>
<dialogue speaker="blue" english="But look what they made! Is that a teleporter?" translation="لكن انظروا ماذا صنعت! أليست آلة تنقيل؟"/>
<dialogue speaker="yellow" english="I think so, but..." translation="أظن، لكنها..."/>
<dialogue speaker="yellow" english="I&apos;ve never seen a teleporter like that before..." translation="مختلفة عن آلات التنقيل التي رأيتها..."/>
<dialogue speaker="red" english="We should investigate!" translation="ضروري أن نتحرى الأمر!"/>
<dialogue speaker="purple" english="What do you think, Captain?" translation="ما رأيك يا قبطان؟"/>
<dialogue speaker="purple" english="Should we find out where it leads?" translation="هل يجدر بنا أن نتحقق من وجهته؟"/>
<dialogue speaker="player" english="Let&apos;s go!" translation="هيا بنا!"/>
<dialogue speaker="blue" english="Oh no! We&apos;re trapped!" translation="أوه لا! احتجزنا!"/>
<dialogue speaker="yellow" english="Oh dear..." translation="يا ويلي..."/>
<dialogue speaker="player" english="Hmm... how should we get out of this?" translation="هممم... كيف سنفلت من هذا المأزق؟"/>
<dialogue speaker="player" english="COMBINE!" translation="اتحاد!" case="1"/>
<dialogue speaker="purple" english="COMBINE!" translation="اتحاد!" case="2"/>
<dialogue speaker="yellow" english="COMBINE!" translation="اتحاد!" case="3"/>
<dialogue speaker="red" english="COMBINE!" translation="اتحاد!" case="4"/>
<dialogue speaker="green" english="COMBINE!" translation="اتحاد!" case="5"/>
<dialogue speaker="blue" english="COMBINE!" translation="اتحاد!" case="6"/>
<dialogue speaker="purple" english="Or, you know... we could have just warped back to the ship..." translation="هل تعلمون... عوض هذا، كنا نستطيع التنقل للسفينة..."/>
<dialogue speaker="green" english="Wow! What is this?" translation="واو! ما هذا؟"/>
<dialogue speaker="yellow" english="It looks like another laboratory!" translation="كأنه مخبر آخر!"/>
<dialogue speaker="red" english="Let&apos;s have a look around!" translation="فلنلق نظرة في الأنحاء!"/>
</cutscene>
<cutscene id="talkpurple_9" explanation="">
<dialogue speaker="purple" english="Look at all this research! This is going to be a big help back home!" translation="يا سلام، هل رأيت كل هذه الأبحاث؟ ستفيدنا فائدة جمة لو رجعنا بها للوطن!"/>
</cutscene>
<cutscene id="talkgreen_11" explanation="">
<dialogue speaker="green" english="I wonder why they abandoned this dimension? They were so close to working out how to fix it..." translation="يا ترى لماذا هجروا هذا البعد؟ مع أنهم أوشكوا على اكتشاف الحل لإصلاحه..."/>
<dialogue speaker="green" english="Maybe we can fix it for them? Maybe they&apos;ll come back?" translation="ربما نصلحه نيابة عنهم؟ ربما يعودون بعد ذلك؟"/>
</cutscene>
<cutscene id="talkblue_9" explanation="">
<dialogue speaker="blue" english="This lab is amazing! The scientists who worked here know a lot more about warp technology than we do!" translation="مخبر رائع! علماؤه يعرفون أكثر منا بكثير عن تقنيات علوم التنقيل المكاني!"/>
</cutscene>
<cutscene id="talkyellow_12" explanation="">
<dialogue speaker="yellow" english="Captain! Have you seen this?" translation="قبطان! هل سبق ورأيت هذا؟"/>
<dialogue speaker="yellow" english="With their research and ours, we should be able to stabilise our own dimension!" translation="لو جمعنا بين أبحاثهم وأبحاثنا، سنتوصل للحل الذي يحقق استقرار بعد أوطاننا!"/>
<dialogue speaker="yellow" english="We&apos;re saved!" translation="حقا نجونا!"/>
</cutscene>
<cutscene id="talkred_14" explanation="super gravitron (difficult minigame)">
<dialogue speaker="red" english="Look what I found!" translation="شاهدوا ماذا وجدت!"/>
<dialogue speaker="red" english="It&apos;s pretty hard, I can only last for about 10 seconds..." translation="اللعبة صعبة جدا، لم أستطع الصمود أكثر من 10 ثوان..."/>
</cutscene>
<cutscene id="terminal_jukebox" explanation="">
<dialogue speaker="gray" english="-= JUKEBOX =-
Songs will continue to play until you leave the ship.
Collect trinkets to unlock new songs!" translation="-= مشغل الموسيقى =-
يستمر عزف الأغنية حتى مغادرة السفينة.
كلما جمعت تذكارات، فتحت أغان جديدة!" centertext="1" padtowidth="264"/>
</cutscene>
<cutscene id="terminal_jukeunlock1" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
5 Trinkets
Pushing Onwards" translation="الشرط: 5 تذكارات
يفتح الأغنية القادمة
Pushing Onwards" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock2" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
8 Trinkets
Positive Force" translation="الشرط: 8 تذكارات
يفتح الأغنية القادمة
Positive Force" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock3" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
10 Trinkets
Presenting VVVVVV" translation="الشرط: 10 تذكارات
يفتح الأغنية القادمة
Presenting VVVVVV" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock4" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
12 Trinkets
Potential for Anything" translation="الشرط: 12 تذكار
يفتح الأغنية القادمة
Potential for Anything" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock41" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
14 Trinkets
Pressure Cooker" translation="الشرط: 14 تذكار
يفتح الأغنية القادمة
Pressure Cooker" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock5" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
16 Trinkets
Predestined Fate" translation="الشرط: 16 تذكار
يفتح الأغنية القادمة
Predestined Fate" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock6" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
18 Trinkets
Popular Potpourri" translation="الشرط: 18 تذكار
يفتح الأغنية القادمة
Popular Potpourri" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock7" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
20 Trinkets
Pipe Dream" translation="الشرط: 20 تذكار
يفتح الأغنية القادمة
Pipe Dream" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_station_1" explanation="">
<dialogue speaker="gray" english="-= PERSONAL LOG =-" translation="-= مذكرات شخصية =-" padtowidth="280"/>
<dialogue speaker="gray" english="Almost everyone has been evacuated from the space station now. The rest of us are leaving in a couple of days, once our research has been completed." translation="أخلينا الجميع من المحطة الفضائية. سيغادر من تبقى منا بعد يومين، عقب انتهاء أبحاثنا. ؝" pad="1"/>
</cutscene>
<cutscene id="terminal_station_2" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= ملاحظات الباحثين =-" padtowidth="264"/>
<dialogue speaker="gray" english="...everything collapses, eventually. It&apos;s the way of the universe." translation="...كل شيء سائر إلى الانهيار، طال الزمان أو قصر. هكذا سنة الحياة في الكون. ؝" centertext="1" pad="1"/>
</cutscene>
<cutscene id="terminal_station_3" explanation="">
<dialogue speaker="gray" english="I wonder if the generator we set up in the polar dimension is what&apos;s affecting our teleporters?" translation="أتساءل... هل للمولد الذي شغلناه في البعد القطبي دخل في التأثير الطارئ على آلات تنقيلنا؟"/>
<dialogue speaker="gray" english="No, it&apos;s probably just a glitch." translation="لا أظن، ذلك مجرد عيب تقني خفيف على الأرجح. ؝"/>
</cutscene>
<cutscene id="terminal_station_4" explanation="a trinket that&apos;s difficult to get">
<dialogue speaker="gray" english="-= PERSONAL LOG =-" translation="-= مذكرات شخصية =-" padtowidth="280"/>
<dialogue speaker="gray" english="Hah! Nobody will ever get this one." translation="هاع! أتحدى أي مخلوق أن يفوز بهذه بالذات. ؝" pad="1"/>
</cutscene>
<cutscene id="terminal_warp_1" explanation="">
<dialogue speaker="gray" english="...The other day I was chased down a hallway by a giant cube with the word AVOID on it." translation="... قبل كم يوم، طاردني في أحد الأروقة مكعب عملاق يتدحرج وعليه كلمة [تفادى]."/>
<dialogue speaker="gray" english="These security measures go too far!" translation="بالغوا حقا في إجراءات الحماية!! ؝"/>
</cutscene>
<cutscene id="terminal_warp_2" explanation="">
<dialogue speaker="gray" english="The only way into my private lab anymore is by teleporter." translation="لم تبق أي طريقة لدخول مخبري الخاص سوى آلة التنقيل."/>
<dialogue speaker="gray" english="I&apos;ve made sure that it&apos;s difficult for unauthorised personnel to gain access." translation="حرصت أن أصعب الدخول على غير العمال المسموح لهم. ؝"/>
</cutscene>
<cutscene id="terminal_outside_1" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= ملاحظات الباحثين =-" padtowidth="264"/>
<dialogue speaker="gray" english="... our first breakthrough was the creation of the inversion plane, which creates a mirrored dimension beyond a given event horizon ..." translation="... إنجازنا وسبقنا الأول بدأ من إنشاء مستوي التعاكس، مما من شأنه إنشاء بعد معكوس جديد عند زمكان ما بعد نقطة أفق حدث معروفة في معطياتنا ..." pad="1"/>
</cutscene>
<cutscene id="terminal_outside_2" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= ملاحظات الباحثين =-" padtowidth="264"/>
<dialogue speaker="gray" english="...with just a small modification to the usual parameters, we were able to stabilise an infinite tunnel!" translation="...بإدخال تعديل بسيط على المتغيرات المعتادة، نجحنا في الحفاظ على استقرار نفق لا متناه! ؝"/>
</cutscene>
<cutscene id="terminal_outside_3" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= ملاحظات الباحثين =-" padtowidth="264"/>
<dialogue speaker="gray" english="... the final step in creating the dimensional stabiliser was to create a feedback loop ..." translation="... وآخر خطوة لإنشاء فارض استقرار البعد أن نصنع حلقة ارتجاع ..." pad="1"/>
</cutscene>
<cutscene id="terminal_outside_4" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= ملاحظات الباحثين =-" padtowidth="264"/>
<dialogue speaker="gray" english="...despite our best efforts, the dimensional stabiliser won&apos;t hold out forever. Its collapse is inevitable..." translation="... مهما بذلنا من جهود، لن يتحمل فارض توازن البعد إلى ما لا نهاية. انهياره محتوم عاجلا أم آجلا..." pad="1"/>
<dialogue speaker="cyan" english="Huh? These coordinates aren&apos;t even in this dimension!" translation="عجبا؟ هذه الإحداثيات خارجة عن نطاق هذا البعد!"/>
</cutscene>
<cutscene id="terminal_outside_5" explanation="">
<dialogue speaker="gray" english="-= Personal Log =-" translation="-= مذكرات شخصية =-" padtowidth="232"/>
<dialogue speaker="gray" english="... I&apos;ve had to seal off access to most of our research. Who knows what could happen if it fell into the wrong hands? ..." translation="... اضطررت لتعطيل الدخول إلى معظم أبحاثنا. من يدري ماذا قد يحصل لو وقعت في أيادي السوء؟ ..." centertext="1" pad="1"/>
</cutscene>
<cutscene id="terminal_outside_6" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= ملاحظات الباحثين =-" padtowidth="264"/>
<dialogue speaker="gray" english="... access to the control center is still possible through the main atmospheric filters ..." translation="... الدخول إلى مركز التحكم لا يزال ممكنا لو كان عبر مصافي الأغلفة الجوية الأساسية ..."/>
</cutscene>
<cutscene id="terminal_lab_1" explanation="">
<dialogue speaker="gray" english="... it turns out the key to stabilising this dimension was to create a balancing force outside of it!" translation="... اتضح أن الحل لاستقرار هذا البعد يكمن في إنشاء قوة خارجه لإحلال التوازن!"/>
<dialogue speaker="gray" english="Though it looks like that&apos;s just a temporary solution, at best." translation="إلا أن هذا مجرد حل وقتي، ولن يدوم حتى في أحسن الظروف."/>
<dialogue speaker="gray" english="I&apos;ve been working on something more permanent, but it seems it&apos;s going to be too late..." translation="حاولت العمل على حل دائم يرسخ مفعوله، لكن يبدو لي أن الأوان سيفوت قبل تطبيقه. ؝"/>
</cutscene>
<cutscene id="terminal_lab_2" explanation="">
<dialogue speaker="gray" english="?SYNTAX ERROR" translation="؟خطأ صياغة؟"/>
</cutscene>
<cutscene id="terminal_letsgo" explanation="">
<dialogue speaker="player" english="Now that the ship is fixed, we can leave anytime we want!" translation="بعد أن أصلحنا السفينة، يمكننا المغادرة متى شئنا!"/>
<dialogue speaker="player" english="We&apos;ve all agreed to keep exploring this dimension, though." translation="لكننا اتفقنا جميعا أن نواصل استكشاف هذا البعد."/>
<dialogue speaker="player" english="Who knows what we&apos;ll find?" translation="من يدري ماذا قد نجد؟"/>
</cutscene>
<cutscene id="terminal_radio" explanation="">
<dialogue speaker="gray" english="-= SHIP RADIO =-
[ Status ]
Broadcasting" translation="-= راديو السفينة =-
[ الوضع الحالي ]
الإرسال قيد البث" tt="1" centertext="1" pad="2"/>
</cutscene>
<cutscene id="terminal_secretlab" explanation="">
<dialogue speaker="gray" english="-= WARNING =-
The Super-Gravitron is intended for entertainment purposes only." translation="-= تحذير =-
أتون الجاذبية الخارق مجعول حصرا لأغراض ترفيهية." centertext="1" pad="1"/>
<dialogue speaker="gray" english="Anyone found using the Super Gravitron for educational purposes may be asked to stand in the naughty corner." translation="لو افتضح أمر شخص يستخدمه لأغراض تعليمية، سنطلب منه أن يبكي في زاوية العقاب."/>
</cutscene>
<cutscene id="terminal_shipcomputer" explanation="">
<dialogue speaker="gray" english="-= D.S.S. SOULEYE =-
Ship Navigation Controls" translation="-= سفينة D.S.S. سول آي =-
لوحة التحكم الملاحية للسفينة" centertext="1" pad="1"/>
<dialogue speaker="gray" english="Error! Error! Cannot isolate dimensional coordinates! Interference detected!" translation="خطأ! خطأ! يتعذر عزل الإحداثيات البعدية! لوحظ تشويش يتداخل مع الإشارة!"/>
</cutscene>
<cutscene id="alreadyvisited" explanation="">
<dialogue speaker="cyan" english="...oh, I&apos;ve already found this." translation="...أوه، سبق أن وجدته."/>
</cutscene>
<cutscene id="disableaccessibility" explanation="">
<dialogue speaker="gray" english="Please disable invincibility and/or slowdown before entering the Super Gravitron." translation="يرجى تعطيل الحصانة وتبطئة اللعبة قبل دخول غرفة أتون الجاذبية الخارق."/>
</cutscene>
</cutscenes>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<spritesmask sprite_w="32" sprite_h="32">
<sprite x="8" y="1" w="2"/> <!-- Checkpoints -->
<sprite x="4" y="2" w="4"/> <!-- STOP -->
<sprite x="4" y="3" w="4"/> <!-- YES -->
<sprite x="3" y="4"/> <!-- OBEY -->
<sprite x="2" y="5" w="2"/> <!-- LIES receiver and LIES -->
<sprite x="4" y="5" w="2"/> <!-- TRUTH -->
</spritesmask>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<langmeta>
<active>1</active>
<!-- should be lowercase because menu style, and should be in the language itself -->
<nativename>العربية</nativename>
<!-- English translation by X -->
<credit>الترجمة العربية من
Eternal Dream Arabization</credit>
<!-- On the language screen, hard limit 40 8x8 characters. Space/Z/V sets this as the language -->
<action_hint>اضغط مسافة، زر Z، أو زر V للاختيار</action_hint>
<!-- Same as above, but for a gamepad button (hard limit 40 8x8 characters) -->
<gamepad_hint>اضغط {button} للاختيار</gamepad_hint>
<!-- Enable automatic word wrapping instead of having to manually insert newlines -->
<autowordwrap>1</autowordwrap>
<!-- Enable automatic full-caps display of selected menu options ([SELECTED] not selected) -->
<toupper>1</toupper>
<!-- When automatically uppercasing, map i to İ (for Turkish) -->
<toupper_i_dot>0</toupper_i_dot>
<!-- When automatically uppercasing, allow ~ to be used to stop the next letter from being uppercased (for Irish) -->
<toupper_lower_escape_char>0</toupper_lower_escape_char>
<!-- Enable for RTL languages like Arabic or Hebrew -->
<rtl>1</rtl>
<!-- The indication that a certain menu option or button is selected -->
<menu_select>[ {label} ]</menu_select>
<menu_select_tight>[{label}]</menu_select_tight>
<!-- The filename of the font to use. For example, "font_cn" means font_cn.png and font_cn.fontmeta. -->
<font>font_ar</font>
</langmeta>

View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Please read README.txt for information about the language files -->
<numbers>
<number value="0" form="0" english="Zero" translation="صفر" translation2="صفر"/>
<number value="1" form="1" english="One" translation="واحد" translation2="واحدة"/>
<number value="2" form="2" english="Two" translation="اثنان" translation2="اثنتان"/>
<number value="3" form="3" english="Three" translation="ثلاثة" translation2="ثلاث"/>
<number value="4" form="3" english="Four" translation="أربعة" translation2="أربع"/>
<number value="5" form="3" english="Five" translation="خمسة" translation2="خمس"/>
<number value="6" form="3" english="Six" translation="ستة" translation2="ست"/>
<number value="7" form="3" english="Seven" translation="سبعة" translation2="سبع"/>
<number value="8" form="3" english="Eight" translation="ثمانية" translation2="ثمان"/>
<number value="9" form="3" english="Nine" translation="تسعة" translation2="تسع"/>
<number value="10" form="3" english="Ten" translation="عشرة" translation2="عشر"/>
<number value="11" form="11" english="Eleven" translation="أحد عشر" translation2="إحدى عشرة"/>
<number value="12" form="11" english="Twelve" translation="إثنا عشر" translation2="إثنتا عشرة"/>
<number value="13" form="11" english="Thirteen" translation="ثلاثة عشر" translation2="ثلاث عشرة"/>
<number value="14" form="11" english="Fourteen" translation="أربعة عشر" translation2="أربع عشرة"/>
<number value="15" form="11" english="Fifteen" translation="خمسة عشر" translation2="خمس عشرة"/>
<number value="16" form="11" english="Sixteen" translation="ستة عشر" translation2="ست عشرة"/>
<number value="17" form="11" english="Seventeen" translation="سبعة عشر" translation2="سبع عشرة"/>
<number value="18" form="11" english="Eighteen" translation="ثمانية عشر" translation2="ثماني عشرة"/>
<number value="19" form="11" english="Nineteen" translation="تسعة عشر" translation2="تسع عشرة"/>
<number value="20" form="11" english="Twenty" translation="عشرين" translation2="عشرين"/>
<number value="21" form="11" english="Twenty One" translation="واحدا وعشرين" translation2="واحدة وعشرين"/>
<number value="22" form="11" english="Twenty Two" translation="اثنان وعشرين" translation2="اثنتان وعشرين"/>
<number value="23" form="11" english="Twenty Three" translation="ثلاثة وعشرين" translation2="ثلاثا وعشرين"/>
<number value="24" form="11" english="Twenty Four" translation="أربعة وعشرين" translation2="أربعا وعشرين"/>
<number value="25" form="11" english="Twenty Five" translation="خمسة وعشرين" translation2="خمسة وعشرين"/>
<number value="26" form="11" english="Twenty Six" translation="ستة وعشرين" translation2="ستة وعشرين"/>
<number value="27" form="11" english="Twenty Seven" translation="سبعة وعشرين" translation2="سبعة وعشرين"/>
<number value="28" form="11" english="Twenty Eight" translation="ثمانية وعشرين" translation2="ثمانية وعشرين"/>
<number value="29" form="11" english="Twenty Nine" translation="تسعة وعشرين" translation2="تسعة وعشرين"/>
<number value="30" form="11" english="Thirty" translation="ثلاثين" translation2="ثلاثين"/>
<number value="31" form="11" english="Thirty One" translation="واحدا وثلاثين" translation2="واحدة وثلاثين"/>
<number value="32" form="11" english="Thirty Two" translation="اثنان وثلاثين" translation2="اثنتان وثلاثين"/>
<number value="33" form="11" english="Thirty Three" translation="ثلاثة وثلاثين" translation2="ثلاثا وثلاثين"/>
<number value="34" form="11" english="Thirty Four" translation="أربعة وثلاثين" translation2="أربعا وثلاثين"/>
<number value="35" form="11" english="Thirty Five" translation="خمسة وثلاثين" translation2="خمسة وثلاثين"/>
<number value="36" form="11" english="Thirty Six" translation="ستة وثلاثين" translation2="ستة وثلاثين"/>
<number value="37" form="11" english="Thirty Seven" translation="سبعة وثلاثين" translation2="سبعة وثلاثين"/>
<number value="38" form="11" english="Thirty Eight" translation="ثمانية وثلاثين" translation2="ثمانية وثلاثين"/>
<number value="39" form="11" english="Thirty Nine" translation="تسعة وثلاثين" translation2="تسعة وثلاثين"/>
<number value="40" form="11" english="Forty" translation="أربعين" translation2="أربعين"/>
<number value="41" form="11" english="Forty One" translation="واحدا وأربعين" translation2="واحدة وأربعين"/>
<number value="42" form="11" english="Forty Two" translation="اثنان وأربعين" translation2="اثنتان وأربعين"/>
<number value="43" form="11" english="Forty Three" translation="ثلاثة وأربعين" translation2="ثلاثا وأربعين"/>
<number value="44" form="11" english="Forty Four" translation="أربعة وأربعين" translation2="أربعا وأربعين"/>
<number value="45" form="11" english="Forty Five" translation="خمسة وأربعين" translation2="خمسة وأربعين"/>
<number value="46" form="11" english="Forty Six" translation="ستة وأربعين" translation2="ستة وأربعين"/>
<number value="47" form="11" english="Forty Seven" translation="سبعة وأربعين" translation2="سبعة وأربعين"/>
<number value="48" form="11" english="Forty Eight" translation="ثمانية وأربعين" translation2="ثمانية وأربعين"/>
<number value="49" form="11" english="Forty Nine" translation="تسعة وأربعين" translation2="تسعة وأربعين"/>
<number value="50" form="11" english="Fifty" translation="خمسين" translation2="خمسين"/>
<number value="51" form="11" english="Fifty One" translation="واحدا وخمسين" translation2="واحدة وخمسين"/>
<number value="52" form="11" english="Fifty Two" translation="اثنان وخمسين" translation2="اثنتان وخمسين"/>
<number value="53" form="11" english="Fifty Three" translation="ثلاثة وخمسين" translation2="ثلاثا وخمسين"/>
<number value="54" form="11" english="Fifty Four" translation="أربعة وخمسين" translation2="أربعا وخمسين"/>
<number value="55" form="11" english="Fifty Five" translation="خمسة وخمسين" translation2="خمسة وخمسين"/>
<number value="56" form="11" english="Fifty Six" translation="ستة وخمسين" translation2="ستة وخمسين"/>
<number value="57" form="11" english="Fifty Seven" translation="سبعة وخمسين" translation2="سبعة وخمسين"/>
<number value="58" form="11" english="Fifty Eight" translation="ثمانية وخمسين" translation2="ثمانية وخمسين"/>
<number value="59" form="11" english="Fifty Nine" translation="تسعة وخمسين" translation2="تسعة وخمسين"/>
<number value="60" form="11" english="Sixty" translation="ستين" translation2="ستين"/>
<number value="61" form="11" english="Sixty One" translation="واحدا وستين" translation2="واحدة وستين"/>
<number value="62" form="11" english="Sixty Two" translation="اثنان وستين" translation2="اثنتان وستين"/>
<number value="63" form="11" english="Sixty Three" translation="ثلاثة وستين" translation2="ثلاثا وستين"/>
<number value="64" form="11" english="Sixty Four" translation="أربعة وستين" translation2="أربعا وستين"/>
<number value="65" form="11" english="Sixty Five" translation="خمسة وستين" translation2="خمسة وستين"/>
<number value="66" form="11" english="Sixty Six" translation="ستة وستين" translation2="ستة وستين"/>
<number value="67" form="11" english="Sixty Seven" translation="سبعة وستين" translation2="سبعة وستين"/>
<number value="68" form="11" english="Sixty Eight" translation="ثمانية وستين" translation2="ثمانية وستين"/>
<number value="69" form="11" english="Sixty Nine" translation="تسعة وستين" translation2="تسعة وستين"/>
<number value="70" form="11" english="Seventy" translation="سبعين" translation2="سبعين"/>
<number value="71" form="11" english="Seventy One" translation="واحدا وسبعين" translation2="واحدة وسبعين"/>
<number value="72" form="11" english="Seventy Two" translation="اثنان وسبعين" translation2="اثنتان وسبعين"/>
<number value="73" form="11" english="Seventy Three" translation="ثلاثة وسبعين" translation2="ثلاثا وسبعين"/>
<number value="74" form="11" english="Seventy Four" translation="أربعة وسبعين" translation2="أربعا وسبعين"/>
<number value="75" form="11" english="Seventy Five" translation="خمسة وسبعين" translation2="خمسة وسبعين"/>
<number value="76" form="11" english="Seventy Six" translation="ستة وسبعين" translation2="ستة وسبعين"/>
<number value="77" form="11" english="Seventy Seven" translation="سبعة وسبعين" translation2="سبعة وسبعين"/>
<number value="78" form="11" english="Seventy Eight" translation="ثمانية وسبعين" translation2="ثمانية وسبعين"/>
<number value="79" form="11" english="Seventy Nine" translation="تسعة وسبعين" translation2="تسعة وسبعين"/>
<number value="80" form="11" english="Eighty" translation="ثمانين" translation2="ثمانين"/>
<number value="81" form="11" english="Eighty One" translation="واحدا وثمانين" translation2="واحدة وثمانين"/>
<number value="82" form="11" english="Eighty Two" translation="اثنان وثمانين" translation2="اثنتان وثمانين"/>
<number value="83" form="11" english="Eighty Three" translation="ثلاثة وثمانين" translation2="ثلاثا وثمانين"/>
<number value="84" form="11" english="Eighty Four" translation="أربعة وثمانين" translation2="أربعا وثمانين"/>
<number value="85" form="11" english="Eighty Five" translation="خمسة وثمانين" translation2="خمسة وثمانين"/>
<number value="86" form="11" english="Eighty Six" translation="ستة وثمانين" translation2="ستة وثمانين"/>
<number value="87" form="11" english="Eighty Seven" translation="سبعة وثمانين" translation2="سبعة وثمانين"/>
<number value="88" form="11" english="Eighty Eight" translation="ثمانية وثمانين" translation2="ثمانية وثمانين"/>
<number value="89" form="11" english="Eighty Nine" translation="تسعة وثمانين" translation2="تسعة وثمانين"/>
<number value="90" form="11" english="Ninety" translation="تسعين" translation2="تسعين"/>
<number value="91" form="11" english="Ninety One" translation="واحدا وتسعين" translation2="واحدة وتسعين"/>
<number value="92" form="11" english="Ninety Two" translation="اثنان وتسعين" translation2="اثنتان وتسعين"/>
<number value="93" form="11" english="Ninety Three" translation="ثلاثة وتسعين" translation2="ثلاثا وتسعين"/>
<number value="94" form="11" english="Ninety Four" translation="أربعة وتسعين" translation2="أربعا وتسعين"/>
<number value="95" form="11" english="Ninety Five" translation="خمسة وتسعين" translation2="خمسة وتسعين"/>
<number value="96" form="11" english="Ninety Six" translation="ستة وتسعين" translation2="ستة وتسعين"/>
<number value="97" form="11" english="Ninety Seven" translation="سبعة وتسعين" translation2="سبعة وتسعين"/>
<number value="98" form="11" english="Ninety Eight" translation="ثمانية وتسعين" translation2="ثمانية وتسعين"/>
<number value="99" form="11" english="Ninety Nine" translation="تسعة وتسعين" translation2="تسعة وتسعين"/>
<number value="100" form="11" english="One Hundred" translation="مئة" translation2="مئة"/>
<number value="101" form="11"/>
<number value="102" form="11"/>
<number value="103" form="11"/>
<number value="104" form="11"/>
<number value="105" form="11"/>
<number value="106" form="11"/>
<number value="107" form="11"/>
<number value="108" form="11"/>
<number value="109" form="11"/>
<number value="110" form="11"/>
<number value="111" form="11"/>
<number value="112" form="11"/>
<number value="113" form="11"/>
<number value="114" form="11"/>
<number value="115" form="11"/>
<number value="116" form="11"/>
<number value="117" form="11"/>
<number value="118" form="11"/>
<number value="119" form="11"/>
</numbers>

View file

@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You can translate these in-game to get better context! See README.txt -->
<roomnames>
<roomname x="0" y="18" english="Single-slit Experiment" translation="التجربة 1: ثغرة مفردة" explanation="(Many of the rooms in the Lab stage have science themed names.)"/>
<roomname x="0" y="19" english="Don&apos;t Flip Out" translation="لا ينقلبن مزاجك" explanation="Flip as in gravity flip, but also the expression in english, as in, keep your cool"/>
<roomname x="1" y="0" english="I&apos;m Sorry" translation="آسف" explanation="The room below this one is Please Forgive Me. There is also a secret path to the right which leads to the rooms &apos;Anomaly&apos; and &apos;Purest Unobtainium&apos;."/>
<roomname x="1" y="1" english="Please Forgive Me!" translation="سامحني أرجوك!" explanation="The room above this one is I&apos;m Sorry"/>
<roomname x="1" y="17" english="Rascasse" translation="الأسماك الشائكة" explanation="This is a type of fish with lots of thorny spikes on its back"/>
<roomname x="1" y="18" english="Keep Going" translation="نواصل" explanation="Literally just &apos;Keep Going&apos;, through the room"/>
<roomname x="1" y="19" english="Shuffled Hallway" translation="رواق منحاز" explanation="Just describes how the room looks. &apos;Shuffled&apos; as in offset."/>
<roomname x="2" y="0" english="Kids His Age Bounce" translation="الصبيان في عمره ينطون" explanation="An old saying for when e.g. a small child falls out of a tree. A trampoline joke in this case."/>
<roomname x="2" y="1" english="Playing Foosball" translation="كرة قدم الطاولة" explanation="This room resembles a Foosball table - https://en.wikipedia.org/wiki/Table_football"/>
<roomname x="2" y="4" english="Philadelphia Experiment" translation="تجربة فيلاديلفيا" explanation="There is a teleporter in this room. The Philadelphia Experiment is the name of 80&apos;s film about teleportation."/>
<roomname x="2" y="16" english="Get Ready To Bounce" translation="نستعد لننط" explanation="The first room in the Lab zone. In the next room, you immediately come into contact with a gravity line, and flip back up."/>
<roomname x="2" y="17" english="It&apos;s Perfectly Safe" translation="كله أمان فعليك بالاطمئنان" explanation="The second room in the lab zone. Don&apos;t worry, it&apos;s perfectly safe."/>
<roomname x="2" y="18" english="Young Man, It&apos;s Worth the Challenge" translation="بني، التحدي يستحق المجهود" explanation="Not a reference to anything in particular - Bennett explains that this is just something that his high school chemistry teacher used to say to students. He thinks the teacher was probably misquoting George Bernard Shaw, who said &apos;Life is not meant to be easy, my child - but take courage: it can be delightful.&apos;."/>
<roomname x="2" y="19" english="Double-slit Experiment" translation="التجربة 2: ثغرة مثناة" explanation="(Many of the rooms in the Lab stage have science themed names.)"/>
<roomname x="3" y="0" english="Merge" translation="التحام" explanation="A wide section connecting to a narrower section - merge as in traffic"/>
<roomname x="3" y="1" english="A Difficult Chord" translation="عزفت على الوتر الحساس" explanation="This room resembles a guitar chord"/>
<roomname x="3" y="4" english="Why So Blue?" translation="يا نهار أزرق! لماذا الحزن؟" explanation="The crewmate Victoria is found here. Victoria is always sad! She&apos;s feeling blue."/>
<roomname x="3" y="16" english="Brought to you by the letter G" translation="برعاية الحرف G" explanation="Reference to Seseme Street - also, this room resembles the letter G"/>
<roomname x="3" y="17" english="Thorny Exchange" translation="تراشق إبر الكلام" explanation="Two people having a polite argument could be described as having a Thorny Exchange of words."/>
<roomname x="3" y="18" english="Square Root" translation="الجذر التربيعي" explanation="This room resembles a Square Root symbol."/>
<roomname x="3" y="19" english="They Call Him Flipper" translation="صديق الإنسان، شقلوب" explanation="This is a line from an American TV show intro about a Dolphin - https://www.youtube.com/watch?v=azEOeTX1LqM"/>
<roomname x="4" y="0" english="Vibrating String Problem" translation="إشكالية الوتر المتذبذب" explanation="Another science themed room name, this one has two gravity lines that you bounce between."/>
<roomname x="4" y="1" english="The Living Dead End" translation="الطرف المستطرف المتطرف" explanation="&apos;The living end&apos; is an idiom meaning &apos;the most extreme form of something&apos;, here it&apos;s an extreme dead end, i.e. a cul-de-sac"/>
<roomname x="4" y="2" english="AAAAAA" translation="آآآآآآ" explanation="The player falls through this room without having time to stop - AAAAAA suggests a scream in English. Also, it&apos;s six As, like the title."/>
<roomname x="4" y="3" english="Diode" translation="صمام" explanation="This room can only be passed through in one direction. It resembles the electrical component."/>
<roomname x="4" y="4" english="I Smell Ozone" translation="شممت الأوزون" explanation="When you use a photocopier, it produces a distinctive smell (from the Ozone produced). This room has a background pattern that suggests a teleportation has recently happened here."/>
<roomname x="4" y="16" english="Free Your Mind" translation="حرر دماغك" explanation="Reference to the film The Matrix, where Morpheus jumps from the top of a Skyscaper - https://www.youtube.com/watch?v=ef_agVIvh0A"/>
<roomname x="4" y="17" english="I Changed My Mind, Thelma..." translation="بدلت رأيي يا ثيلما..." explanation="The room below Free Your Mind. Reference to the film Thelma and Louise, which ends with Thelma and Louise driving off a cliff, sorry, spoilers"/>
<roomname x="4" y="18" english="Hitting the Apex" translation="ذروة المنعطف" explanation="&apos;Hitting the Apex&apos; is the term used by race drivers for the optimal path around a corner"/>
<roomname x="4" y="19" english="Three&apos;s a Crowd" translation="سر الثلاثة سر الجمهرة" explanation="From the expression Two&apos;s Company, Three&apos;s a Crowd. This room has two challenges - the first has two gaps to cross, the second has three."/>
<roomname x="5" y="0" english="Spike Strip Deployed" translation="فلتنشر الأشواك" explanation="This room has some spikes on a gravity line. The name is a reference to the device that police might use to blow out the tyres of a speeding car."/>
<roomname x="5" y="1" english="Anomaly" translation="الظاهرة الغريبة" explanation="As in, a strange result in science. This room has lots of different colours, unlike other rooms in this stage."/>
<roomname x="5" y="16" english="In a Single Bound" translation="بوثبة واحدة" explanation="Superman is described as being able to leap tall buildings in a single bound."/>
<roomname x="5" y="17" english="Indirect Jump Vector" translation="متجه قفزة غير مباشرة" explanation="If you miss the gap in &apos;In a Single Bound&apos; above, you will end up back in this room - hence, your trajectory was off!"/>
<roomname x="6" y="0" english="Topsy Turvyism" translation="عاليها سافلها" explanation="Topsy Turvy is Australian slang for upside down"/>
<roomname x="6" y="1" english="Purest Unobtainium" translation="معدن المجهولنديوم الخالص" explanation="Unobtainium is a jokey made up term from science fiction for an impossible substance - https://en.wikipedia.org/wiki/Unobtainium"/>
<roomname x="6" y="16" english="Barani, Barani" translation="شقلبة، شقلبتان" explanation="A Barani is a technical term for doing a flip on a trampoline"/>
<roomname x="6" y="17" english="Safety Dance" translation="رقصة الأمان" explanation="Named after the 80s song by Men Without Hats."/>
<roomname x="7" y="0" english="Standing Wave" translation="الموجة الموقوفة" explanation="Many of the rooms in the Lab stage have science themed names. This one is at the beginning of a section with gravity lines above and below you, before the section begins."/>
<roomname x="7" y="15" english="Entanglement Generator" translation="كم تشابكا ستولد بآلتك؟" explanation="This room contains a teleporter. Entanglement is an idea from quantum mechanics."/>
<roomname x="7" y="16" english="Heady Heights" translation="الرأس الأعلى" explanation="Just below the highest point in the level."/>
<roomname x="7" y="17" english="Exhausted?" translation="النشاط العادم" explanation="This room has an exit that sort of suggests an Exhaust Pipe in a car."/>
<roomname x="7" y="18" english="The Tantalizing Trinket" translation="سأتذكر التذكار" explanation="You see this Trinket just out of reach as you fall through the room."/>
<roomname x="7" y="19" english="The Bernoulli Principle" translation="مبدأ بيرنولي" explanation="Many of the rooms in the Lab stage have science themed names - this one is just named after a formula relating to flight."/>
<roomname x="8" y="9" english="Teleporter Divot" translation="أثر التنقيلة" explanation="There is a pattern in the background of this room that indicates that a teleporter has sent someone to this room"/>
<roomname x="9" y="9" english="The Tower" translation="البرج" explanation="This room is a single vertically scrolling stage, about 20 rooms high. The name is from the Tarot card."/>
<roomname x="10" y="4" english="Seeing Red" translation="العين الحمراء" explanation="This is the room that you find the red crewmate in. (Seeing Red is an expression in English about being filled with rage, but that doesn&apos;t really apply here)"/>
<roomname x="10" y="5" english="Energize" translation="تنشيط" explanation="There is a teleporter in this Room. Energize is what they say on Star Trek when they use the teleporters."/>
<roomname x="10" y="6" english="Down Under" translation="تحت أم الدنيا في آخر الدنيا" explanation="Australia is sometimes called The Land Down Under because of its position on a globe. You complete this room by going down and under some moving platforms"/>
<roomname x="10" y="7" english="A Deception" translation="أخدوعة" explanation="This room appears trivial at first, but is connected to a difficult trinket challenge"/>
<roomname x="11" y="4" english="Building Apport" translation="توطيد الروابط الزمكانية" explanation="An &apos;Apport&apos; is a kind of paranormal teleportation. Building Apport is a pun on the concept of &apos;building rapport&apos; (except that to &apos;apport&apos; is to teleport)."/>
<roomname x="11" y="5" english="Frown Upside Down" translation="اقلب عبوس حواجبك" explanation="To &apos;Turn that Frown Upside Down&apos; is an expression in English, like &apos;cheer up&apos;, basically means to stop being unhappy"/>
<roomname x="11" y="6" english="Shenanigan" translation="ألعوبة" explanation="Shenanigan as in a prank, or practical joke. This room is connected to the &apos;Prize for the Reckless&apos; puzzle, and like the room &apos;A Deception&apos;, it appears trivial unless you know the secret"/>
<roomname x="11" y="7" english="Prize for the Reckless" translation="جائزة للمتهورين" explanation="This room contains a trinket that can only be collected by doing something difficult"/>
<roomname x="11" y="11" english="Conveying a New Idea" translation="توصيل الفكرة ببساط-ة" explanation="This is the first room you encounter that has conveyor belts in it"/>
<roomname x="11" y="12" english="One Way Room" translation="دخول الحمام ليس كخروجه" explanation="Can only be travelled through in one direction"/>
<roomname x="11" y="13" english="Boldly To Go" translation="بجرأة فلنمض" explanation="Star Trek reference, this rooms is near the entrance to the space station level"/>
<roomname x="11" y="14" english="The Filter" translation="الغربال" explanation="Like a filter from an air conditioning vent"/>
<roomname x="12" y="3" english="Security Sweep" translation="تمشيط أمني" explanation="Contains a single, fast moving enemy that moves up and down."/>
<roomname x="12" y="4" english="Gantry and Dolly" translation="الرافعة الجسرية" explanation="Gantry and Dolly are the names for types of cranes that move crates around. This room has two different types of platforms."/>
<roomname x="12" y="5" english="The Yes Men" translation="عشاق كلمة نعم" explanation="Contains a number of enemies with briefcases and the word -YES- for a head. An expression for people who work at large companies and agree a lot with their bosses"/>
<roomname x="12" y="6" english="Stop and Reflect" translation="وقفة تأمل" explanation="Expression meaning to take a moment and think about what you&apos;re doing. In this room, a small puzzle where you need to use the underside of a moving platform to progress."/>
<roomname x="12" y="7" english="V Stitch" translation="غرزة مثلثة" explanation="A V Stitch is a type of crochet stitch."/>
<roomname x="12" y="11" english="Upstream Downstream" translation="مع التيار، عكس التيار" explanation="As in swimming upstream or downstream, with or against a current in a river"/>
<roomname x="12" y="12" english="The High Road is Low" translation="أعلى الطريقين مقاما أدناهما" explanation="This room has two paths - a high path and a low path. The &apos;low&apos; path leads to a trinket, so the roomname is a sort of clue about which way to go."/>
<roomname x="12" y="13" english="Give Me A V" translation="صفحة! سبعة! صعبة!" explanation="Room is in the shape of a big letter V. The roomname suggests the common american cheerleading chant - e.g. Give me an L! Give me an O! Give me a C! Give me an A! Give me an L! Give me an I! Give me an S! Give me an A! Give me a T! Give me an I! Give me an O! Give me an N! What does it spell? LOCALISATION!"/>
<roomname x="12" y="14" english="Outer Hull" translation="المتن الخارجي" explanation="The entrance to the Space Station 2 level - the outer hull of a space station."/>
<roomname x="13" y="0" english="It&apos;s Not Easy Being Green" translation="الخضرة صعبة" explanation="References a song by Kermit from the Muppets. This room is where you find the green crewmate."/>
<roomname x="13" y="3" english="Linear Collider" translation="المصادم الخطي" explanation="An early room, name is just meant to suggest something sciency. Room contains long, wave like enemies."/>
<roomname x="13" y="4" english="Comms Relay" translation="ناقل البث" explanation="This room contains some communication equipment, like a radio."/>
<roomname x="13" y="5" english="Welcome Aboard" translation="مرحبا بك" explanation="The first room in the game"/>
<roomname x="13" y="6" english="Trench Warfare" translation="حرب الخنادق" explanation="Room contains a couple of pits with soldier-like enemies in them. Loosely references the 1983 videogame Hunchback."/>
<roomname x="13" y="7" english="B-B-B-Busted" translation="هههذه المصيدة حافلة" explanation="Room contains a large Bus. &apos;Bus&apos;ted as in &apos;Caught&apos;."/>
<roomname x="13" y="8" english="Level Complete!" translation="ختمت المستوى!" explanation="This room has a teleporter, which is normally found at the end of a level. However this room is midway through the stage."/>
<roomname x="13" y="9" english="Lighter Than Air" translation="أخف من الهواء" explanation="This room has clouds that rise from the bottom of the screen to the top, which the player is faster than, implying that the player is lighter than air."/>
<roomname x="13" y="10" english="The Solution is Dilution" translation="الحل في الانحلال" explanation="This room has a factory and pollution clouds in it. Apparently this phrase was once used by industrialists to advocate for not worrying too much about pollution."/>
<roomname x="13" y="11" english="The Cuckoo" translation="كوكو الكذوب" explanation="This room contains a speaker that emits the word &apos;LIES&apos; over and over. A cuckoo&apos;s call decieves other birds!"/>
<roomname x="13" y="12" english="Backsliders" translation="بساط الريح، بساط الرجع" explanation="A conveyor belt in this room pushes against you as you try to move, so you slide backwards."/>
<roomname x="13" y="13" english="Select Track" translation="مساران ولك الخيار" explanation="There are two paths you can pick between here"/>
<roomname x="14" y="0" english="Green Dudes Can&apos;t Flip" translation="الخضر لا يعكسون" explanation="You have a green crewmate with you in this room! A reference to the 90&apos;s film &apos;White Guy&apos;s Can&apos;t Jump&apos;."/>
<roomname x="14" y="1" english="This is how it is" translation="هكذا الحال" explanation="literally as in, this is how the mechanic of this stage works - also an expression as in &apos;this is the way things are&apos;"/>
<roomname x="14" y="2" english="That&apos;s Why I Have To Kill You" translation="لهذا سأضطر لقتلك" explanation="The follows the room named &apos;I love you&apos;. &apos;I love you, that&apos;s why I have to kill you&apos; is kind of a slasher horror trope."/>
<roomname x="14" y="3" english="Atmospheric Filtering Unit" translation="وحدة المصفاة الجوية" explanation="An early room, looks a bit like an air filter"/>
<roomname x="14" y="4" english="It&apos;s a Secret to Nobody" translation="سر مفضوح للجميع" explanation="A reference to the infamous Zelda quote &apos;It&apos;s a secret to everybody&apos;. This room contains the first trinket."/>
<roomname x="14" y="5" english="Conundrum" translation="حيرة" explanation="Conundrum as in puzzle, riddle, problem to be solved"/>
<roomname x="14" y="6" english="Boo! Think Fast!" translation="بعو! بسرعة فلنفكر!" explanation="Contains a challenge that you need to react very quickly to. You might say &apos;Boo, think fast&apos; if you threw something at someone, expecting them to catch it."/>
<roomname x="14" y="7" english="The Sensible Room" translation="الغرفة العقلانية" explanation="Early corridor room containing no challenges. Sensible as in the opposite of Foolish - you might call someone sensible in english if they are excessively cautious."/>
<roomname x="14" y="8" english="The Hanged Man, Reversed" translation="مشنوق، لكن بالعكس" explanation="Named after the Tarot Card, reversed as in Upside Down. The room contains a stationary enemy which resembles a Wheel of Fortune. The name is supposed to suggest a kind of out-of-place quality."/>
<roomname x="14" y="9" english="Green Grotto" translation="المغارة الخضراء" explanation="A peaceful green room."/>
<roomname x="14" y="10" english="Manic Mine" translation="المنجم المجنون" explanation="A reference to the 8-bit game Manic Miner."/>
<roomname x="14" y="11" english="Clarion Call" translation="نداء الكاذب" explanation="A &apos;Clarion Call&apos; is an idiom used when somebody makes a case for a course of action, for example in a politician&apos;s speech, or a call to battle. It sometimes has an association with dishonesty - in this room, the words &apos;LIES&apos; appear over and over."/>
<roomname x="14" y="12" english="Gordian Knot" translation="المعقودة الغوردية" explanation="As in the Gordian Knot from greek history. A complicated room that can be passed through twice."/>
<roomname x="14" y="13" english="You Chose... Poorly" translation="اختيارك... لم يكن في محله" explanation="This room comes right after a choice between two paths. It&apos;s a quote from an Indiana Jones film."/>
<roomname x="15" y="0" english="Murdering Twinmaker" translation="آلة التوأمة الذباحة" explanation="Room contains a teleporter. A &apos;Murdering Twinmaker&apos; is, uh, one way teleportation might work..."/>
<roomname x="15" y="1" english="A Bisected Spiral" translation="دوامة مشطورة" explanation="Room is a spiral, cut down the middle"/>
<roomname x="15" y="2" english="Take the Red Pill" translation="عليك بالحبة الحمراء" explanation="This is a Matrix reference that hasn&apos;t aged well, lol"/>
<roomname x="15" y="3" english="Traffic Jam" translation="زحمة مرور" explanation="The enemies in this room are Stop Signs"/>
<roomname x="15" y="4" english="Leap of Faith" translation="نتوكل ونقفز" explanation="To take a leap of faith means to do something without knowing how it&apos;s going to turn out."/>
<roomname x="15" y="5" english="Solitude" translation="عزلة" explanation="As in being alone, or in this case, lost by yourself"/>
<roomname x="15" y="6" english="Driller" translation="الكابتن حفار" explanation="Technically references the name of a C64 game, but that doesn&apos;t matter much"/>
<roomname x="15" y="7" english="Exhaust Chute" translation="مسقط النفايات" explanation="Like a factory exhaust chute for disposing of rubbish"/>
<roomname x="15" y="8" english="Sorrow" translation="يا للأحزان" explanation="A difficult room that you might die in a lot"/>
<roomname x="15" y="9" english="doomS" translation="قبر حرب في rbb nlSo" explanation="The room is above &apos;Swoop&apos;, and is a copy of the room rotated 180 degrees! The room name &apos;doomS&apos; is the word &apos;Swoop&apos; rotated 180 degrees. When localising this room, don&apos;t worry too much about trying to keep the meaning of the words Dooms and Swoop, because they&apos;re not that important - instead, focus on picking words that have this 180 degree flip quality!"/>
<roomname x="15" y="10" english="Swoop" translation="nrc rib في مكان قفر" explanation="See the note for room (15,9), doomS."/>
<roomname x="15" y="11" english="Chinese Rooms" translation="الغرف الصينية" explanation="This refers to a famous philosophical argument about artifical intelligence - https://en.wikipedia.org/wiki/Chinese_room."/>
<roomname x="15" y="12" english="You Just Keep Coming Back" translation="ستغيب، ستروح، وسترجع يا حياتي" explanation="You can pass through this room up to three times, depending on which route you take through the level."/>
<roomname x="15" y="13" english="Hyperspace Bypass 5" translation="متخطى الفضاء الفائق رقم 5" explanation="A conveyor belt will take you through this room without you needing to press any buttons, hench the bypass. The phrase Hyperspace Bypass is a reference to Hitchhiker&apos;s Guide to the Galaxy."/>
<roomname x="16" y="0" english="I Love You" translation="أحبك" explanation="The room contains a couple of heart shaped enemies"/>
<roomname x="16" y="1" english="As you like it" translation="كما شئت" explanation="This room can be approached in two different equivilent ways, whichever way you like it. &apos;As you like it&apos; is the name of a Shakespeare play."/>
<roomname x="16" y="2" english="Short Circuit" translation="دارة مقصورة" explanation="Probably named after the 80s film Short Circuit. Also works because you&apos;ll hit a dead end if you keep walking forwards."/>
<roomname x="16" y="3" english="Twisty Little Passages" translation="طرق تقلصت وتشعبت" explanation="A maze like room. Refers to the section from the 1976 text game Colossal Cave Adventure - you are in a maze of twisty little passages, all alike"/>
<roomname x="16" y="6" english="Quicksand" translation="الرمال المتحركة" explanation="Contains lots of dissolving platforms."/>
<roomname x="16" y="7" english="The Tomb of Mad Carew" translation="قبر كارو المجنون" explanation="A very obscure reference to the C64 game Dizzy"/>
<roomname x="16" y="8" english="Parabolica" translation="العدسة الشلجمية" explanation="This room contains a section of wall in the shape of a parabolic arch."/>
<roomname x="16" y="9" english="$eeing Dollar $ign$" translation="أرى ﺷ$ﻞ دولار" explanation="This is a green room that resembles a dollar sign shape"/>
<roomname x="16" y="10" english="What Lies Beneath?" translation="تحت الأكاذيب؟" explanation="The room below this contains enemies in the shape of the word &apos;Lies&apos;. So there&apos;s a double meaning here - as in, a question, &apos;what is below this room&apos;, and that the word LIES is literally beneath this room."/>
<roomname x="16" y="11" english="Spikes Do!" translation="تحصد الأشواك!" explanation="This rooms is below the room named &apos;What lies Beneath?&apos;, and answers the question: Spikes do!"/>
<roomname x="16" y="12" english="Ha Ha Ha Not Really" translation="هه هه هه صدقتني" explanation="This is a difficult room that follows one called &apos;Plain Sailing from here on&apos;. It&apos;s taunting the player. "/>
<roomname x="16" y="13" english="Plain Sailing from Here On" translation="طريق سالكة من الآن فصاعدا" explanation="This room is at the end of a long section, and promises &apos;Plain Sailing&apos; afterwards, as in, no further challenges. This is a lie"/>
<roomname x="17" y="0" english="As we go up, we go down" translation="صعودنا سقوط" explanation="Named after the 1995 song by Guided by Voices"/>
<roomname x="17" y="1" english="Maze With No Entrance" translation="متاهة بلا مدخل" explanation="This room is a maze which has no entrance, due to the nature of the warping mechanic."/>
<roomname x="17" y="2" english="The Brown Gate" translation="البوابة البنية" explanation="I think this is an Ultima 7 reference? A literal translation is fine here"/>
<roomname x="17" y="3" english="Edge Games" translation="نلعب على حافة الخطر" explanation="Contains a trinket that you get by navigating around the edge of the screen. The use of the word EDGE is deliberate, refering to the trademark of a notoriously litigious individual who sued an indie developer around the time VVVVVV was made"/>
<roomname x="17" y="7" english="Brass Sent Us Under The Top" translation="كتيبة المتسلقين" explanation="The enemies in this room look like little army guys"/>
<roomname x="17" y="8" english="The Warning" translation="من أنذر فقد أعذر" explanation="This room has lots of checkpoints in it. It&apos;s beside the game&apos;s most difficult challenge, the Veni, Vidi, Vici section. The checkpoints don&apos;t really do anything, but they&apos;re a warning of the challenge ahead."/>
<roomname x="17" y="9" english="Just Pick Yourself Down" translation="التقط نفسك من تحت" explanation="A two part room name. From the expression &apos;If you fall down, just pick yourself up&apos;."/>
<roomname x="17" y="10" english="If You Fall Up" translation="لو سقطت إلى فوق" explanation="A two part room name. From the expression &apos;If you fall down, just pick yourself up&apos;."/>
<roomname x="17" y="11" english="Chipper Cipher" translation="شفرة بهجة" explanation="Just some nice wordplay. Chipper means &apos;Jolly&apos; or &apos;Happy&apos;."/>
<roomname x="18" y="0" english="Time to get serious" translation="حان وقت الجد" explanation="Literal, this is the first room in the stage which is fairly difficult"/>
<roomname x="18" y="1" english="Wheeler&apos;s Wormhole" translation="الثقب الدودي، حسب ويلر" explanation="Apparently a scientist named John Wheeler coined the phrase &apos;Wormhole&apos;! I just found that out. Anyway, this room has a teleporter in it."/>
<roomname x="18" y="2" english="Sweeney&apos;s Maze" translation="متاهة سويني" explanation="Contains enemies that move strangly and resemble enemies from ZZT, an old game by Tim Sweeney"/>
<roomname x="18" y="3" english="Mind The Gap" translation="حذار من الفجوة" explanation="Refers to what train announcers say on the London Underground when you leave the train - mind the gap between the train and the station platform"/>
<roomname x="18" y="7" english="A Wrinkle in Time" translation="بين طيات الزمان" explanation="The name of a 60&apos;s science fiction book. There is a teleporter in this room."/>
<roomname x="18" y="8" english="Getting Here is Half the Fun" translation="الوصول نصف المتعة" explanation="The top of the Veni Vidi Vici sequence. When you get to the top, you have to go all the way back down - hence, this room is the halfway point of the challenge."/>
<roomname x="18" y="9" english="Your Bitter Tears... Delicious" translation="بكيت بمرارة... دموعك حلاوة" explanation="Part of the Veni Vidi Vici sequence. Taunting the player."/>
<roomname x="18" y="10" english="Easy Mode Unlocked" translation="فتحت الطور السهل" explanation="Part of the Veni Vidi Vici sequence. Taunting the player - the &apos;easy mode&apos; refers to the second passageway on the right side that is easier to take when going back through this room on the way down, but that passageway leads to death."/>
<roomname x="18" y="11" english="Vici!" translation="جئت!" explanation="The rooms Veni, Vidi, Vici! appear in sequence. Famous latin phrase attributed to Julius Caesar meaning I came, I saw, I conquered. The hardest challenge in the game."/>
<roomname x="18" y="12" english="Vidi" translation="نظرت!" explanation="The rooms Veni, Vidi, Vici! appear in sequence. Famous latin phrase attributed to Julius Caesar meaning I came, I saw, I conquered. The hardest challenge in the game."/>
<roomname x="18" y="13" english="Veni" translation="فزت!" explanation="The rooms Veni, Vidi, Vici! appear in sequence. Famous latin phrase attributed to Julius Caesar meaning I came, I saw, I conquered. The hardest challenge in the game."/>
<roomname x="18" y="14" english="Doing Things The Hard Way" translation="صعبت الطريق على نفسك" explanation="The starting room from the Veni, Vidi, Vici! challenge."/>
<roomname x="19" y="0" english="To The Batcave!" translation="إلى مغارة الوطواط!" explanation="The Batcave, as in Batman&apos;s hideout."/>
<roomname x="19" y="1" english="Ascending and Descending" translation="صواعد ونوازل" explanation="Just meant as in literally Ascending and Descending, going up and down. There is a room later in the game called Upstairs, Downstairs, which is a callback to this."/>
<roomname x="19" y="2" english="Shockwave Rider" translation="راكب الموجات" explanation="Named after a 70&apos;s Science Fiction novel"/>
<roomname x="19" y="3" english="This will make you flip" translation="سيقلب هذا مزاجك" explanation="Flip is used here in the sense &apos;Flip out&apos;, as in, to lose your temper"/>
<roomname x="41" y="51" english="1950 Silverstone Grand V" translation="كأvv سلفرستون 1950" explanation="Refers to 1950 Silverstone Grand Prix (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="41" y="52" english="DIY V Repair" translation="تصليح V بدون معلم" explanation="DIY TV repair - this room has a television you can interact with, which changes the theme from black and white to colour"/>
<roomname x="41" y="56" english="Now Take My Lead" translation="والآن ستمشي ورائي" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="42" y="52" english="Party Time!" translation="حفلة بلا ميعاد!" explanation="The first room after the black and white section. Doesn&apos;t refer to any TV show."/>
<roomname x="42" y="56" english="What Are You Waiting For?" translation="ماذا تنتظر؟" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="43" y="51" english="The Voon Show" translation="برنامج القلاﺑvv" explanation="Named after The Goon Show (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="43" y="52" english="Upstairs, Downstairs" translation="طالع، نازل" explanation="Named after the 70s TV show - but also, refers to the earlier room Ascending and Descending, which this room is an updated version of. (Second part of the final stage has references to Colour TV shows)"/>
<roomname x="43" y="56" english="Don&apos;t Get Ahead of Yourself!" translation="إياك واستباق الأمور!" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="44" y="51" english="Vertigo" translation="‏٧وار المرتفعات" explanation="Named after the Hitchcock film (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="44" y="52" english="Timeslip" translation="مخطوف الزمان" explanation="Named after the 70s TV show - name also suggests a connection to the earlier room Backsliders, which this room is an updated version of (Second part of the final stage has references to Colour TV shows)"/>
<roomname x="44" y="56" english="Very Good" translation="حسن جدا" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="45" y="52" english="Three&apos;s Company" translation="سر الثلاثة في حفظ الأمناء" explanation="Named after the 70s sitcom - but also, suggests a connection to the earlier room Two&apos;s Company, which this room is an updated version of (Second part of the final stage has references to Colour TV shows)"/>
<roomname x="45" y="56" english="Must I Do Everything For You?" translation="هل أساعدك في الصغيرة والكبيرة؟" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="46" y="54" english="Temporary Fault..." translation="عطل مؤقت..." explanation="Opening room of the final level."/>
<roomname x="46" y="56" english="Now Stay Close To Me..." translation="فلنمش قريبين من بعض..." explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="47" y="52" english="Cosmic Creepers" translation="المتلصص الفضائي" explanation="Named after the cat from the 70s film Bedknobs and Broomsticks. Not sure why!"/>
<roomname x="47" y="54" english="Do Not Adjust the V-hold" translation="لا حاجة لتحريك الهوائي ٧" explanation="I don&apos;t think V-hold is a real thing, it&apos;s supposed to just suggest &apos;Do not adjust your TV settings&apos;. (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="47" y="56" english="...But Not Too Close" translation="...لكن بدون قرب زائد" explanation="In this room, if you go too quickly, your crewmate will walk into spikes."/>
<roomname x="48" y="52" english="The Villi People" translation="معي غليظ" explanation="Bennett just thought this room looked &apos;intestinal&apos;. Villi as in part of the intestinal system. Also refers to the 80s band The Village People."/>
<roomname x="48" y="54" english="Regular Service Will Return Shortly" translation="سنعود بعد قليل" explanation="This is something you might hear on a TV station if they had lost reception. (The final stage has room names that suggest old black and white TV shows. A really, really good way to translate this level would be to use the names of black and white TV shows that are well known in your language, rather than trying to keep the exact meaning of the TV shows used here.)"/>
<roomname x="48" y="56" english="Don&apos;t Be Afraid" translation="لا داع للخوف" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="49" y="52" english="Panic Room" translation="غرفة الذعر" explanation="A panic room is a safe room that you can hide in during an emergency, but I don&apos;t think that was Bennett&apos;s intention with this name. This room suddenly starts scrolling as soon as you enter, causing a panic."/>
<roomname x="49" y="54" english="Origami Room" translation="غرفة في طي النسيان" explanation="As in folded paper - this room is mirrored around the center point."/>
<roomname x="49" y="56" english="Do as I Say..." translation="طالما سمعت كلامي..." explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="50" y="51" english="The V Stooges" translation="البطل الخماسي" explanation="Refers to the 3 Stooges (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="50" y="52" english="1954 World Cup Vinyl" translation="فينيل كأvv العالم 1954" explanation="Refers to the World Cup Final (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="50" y="56" english="...Not as I Do" translation="...بدل تقليد أفعالي" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="51" y="53" english="The Final Challenge" translation="التحدي النهائي" explanation="One of the last challenges in the game."/>
<roomname x="51" y="56" english="Mind Your Head" translation="حذار، رأسك والسقف" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="52" y="53" english="The Last Straw" translation="القشة التي قصمت" explanation="One more little challenge, just after the room called The Final Challenge"/>
<roomname x="52" y="56" english="Do Try To Keep Up" translation="هلا لحقتني، بدون تباطئ؟" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="53" y="48" english="Whee Sports" translation="`v´ ويهي ما أحلى الرياضة `v´" explanation="Refers to Wii Sports, the Nintendo Wii launch title. &apos;Whee&apos; as in what a child might say while going down a slide (this room has a long drop in it)"/>
<roomname x="53" y="49" english="Whizz Down The Shaft" translation="الصعوv إلى الهاوية" explanation="Whizz is Australian slang for doing something quickly, I think"/>
<roomname x="53" y="50" english="The Gravitron" translation="أتون الجاذبية" explanation="A special arcade section where you have to survive for 60 seconds. Bennett named this one so as to suggest a funfair ride (though not any one in particular)."/>
<roomname x="53" y="51" english="Tunnel of Terror" translation="أنفاق الأهوال" explanation="Another name inspired by funfairs."/>
<roomname x="53" y="52" english="House of Mirrors" translation="متاهة المرايا" explanation="Another name inspired by funfairs."/>
<roomname x="53" y="53" english="W" translation="W" explanation="This room has platforms in a W shape."/>
<roomname x="53" y="56" english="You&apos;re Falling Behind" translation="تأخرت في النزول!" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="54" y="48" english="VVVVVV" translation="VVVVVV" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 6/6"/>
<roomname x="54" y="49" english="VVVVV" translation="VVVVV" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 5/6"/>
<roomname x="54" y="50" english="VVVV" translation="VVVV" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 4/6"/>
<roomname x="54" y="51" english="VVV" translation="VVV" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 3/6"/>
<roomname x="54" y="52" english="VV" translation="VV" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 2/6"/>
<roomname x="54" y="53" english="V" translation="V" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 1/6"/>
<roomname x="54" y="56" english="Class Dismissed!" translation="انتهى الدرس!" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
</roomnames>

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Please read README.txt for information about the language files -->
<roomnames_special>
<roomname english="Outer Space" translation="الفضاء الخارجي" explanation="Literally Outer Space"/>
<roomname english="Dimension VVVVVV" translation="البعد VVVVVV" explanation="The overall area that the game takes place in."/>
<roomname english="The Ship" translation="السفينة" explanation="The dimension exploring spaceship"/>
<roomname english="Secret Lab" translation="المخبر السري" explanation="An endgame section containing achievement trophys"/>
<roomname english="Laboratory" translation="المخبر" explanation="One of the stages"/>
<roomname english="The Tower" translation="البرج" explanation="One of the stages"/>
<roomname english="Warp Zone" translation="منطقة النقلة" explanation="One of the stages"/>
<roomname english="Space Station" translation="المحطة الفضائية" explanation="One of the stages"/>
<roomname english="Outside Dimension VVVVVV" translation="خارج نطاق البعد VVVVVV" explanation="For rare rooms outside the normal area"/>
<roomname english="???" translation="؟؟؟" explanation=""/>
<roomname english="The Super Gravitron" translation="أتون الجاذبية الخارق" explanation="An expanded version of the room at 53, 50"/>
<roomname english="I Can&apos;t Believe You Got This Far" translation="لا أصدق وصولك هذا الحد" explanation="If you&apos;re playing No Death Mode, the room Prize for the Reckless has this roomname instead (the room is altered to make it possible to do this section without dying)"/>
<roomname english="Imagine Spikes There, if You Like" translation="يمكنك تخيل أشواك هناك لو شئت" explanation="If you&apos;re playing a time trial, the room Prize for the Reckless has this roomname instead (the room is altered to make it possible to do this section without dying)"/>
<!-- - -->
<roomname english="Rear Window" translation="النافذة الخلفية" explanation="Named after the Hitchcock film (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Rear Vindow" translation="ﺍﻟﻨﺎﻓ٧ﺓ الخلفية" explanation=""/>
<!-- - -->
<roomname english="On the Waterfront" translation="على جبهة البحر" explanation="Named after the 1954 film (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="On the Vaterfront" translation="على ٧بهة البحر" explanation=""/>
<!-- - -->
<roomname english="The Untouchables" translation="أناس فوق مستوى النقد" explanation="Before it was a film, the Untouchables was a TV series in 1959 (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="The Untouchavles" translation="أناس فوق مستوى ﺍﻟﻨﻘ٧" explanation=""/>
<!-- - -->
<roomname english="Television Newsveel" translation="نشرة الأنباء" explanation="Refers to Television Newsreel. (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Television Newsvel" translation="ثشرة الأثپاء" explanation=""/>
<roomname english="TelevisvonvNewsvel" translation="ثشژڤ‍ الأثپاء" explanation=""/>
<roomname english="TvlvvvsvonvNevsvel" translation="ث٧٧ژڤ‍ ا٧ثپا٧" explanation=""/>
<roomname english="vvvvvvsvovvNe svel" translation="٨‍‏٧٧٨‍‏٧ ا٧٨‍‏٧ا٧" explanation=""/>
<roomname english="vhv vvv&apos;vvovv vevl" translation="٨‍‏٧٧٨‍‏٧ا٧٨‍‏٧ا٧" explanation=""/>
<roomname english="vhv V v&apos;Cvovv vewv" translation="ا٨‍‏٧٨‍‏ا٨‍‏٧٨‍‏ا٧٧" explanation=""/>
<roomname english="vhe 9 v&apos;Cvovv vewv" translation="ا٨‍‏٧ا٨‍‏ا٨‍‏ا٨‍‏٨‍‏٧" explanation=""/>
<roomname english="vhe 9 v&apos;Cvovv Newv" translation="ا٨‍‏٧ا٨‍‏ ال‍٨‍‏ا٨‍‏٨‍‏٧" explanation=""/>
<roomname english="The 9 O&apos;Cvovk Newv" translation="أڅپاژ الثاشڄڤ‍" explanation=""/>
<roomname english="The 9 O&apos;Clock News" translation="أخبار التاسعة" explanation="(Second part of the final stage has references to Colour TV shows)"/>
<!-- - -->
<roomname english="Vwitched" translation="المسحورة" explanation="Reference to early black and white sitcom Bewitched (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Vwitvhed" translation="المشحو٧ الڤشچڤژڤ‍" explanation=""/>
<roomname english="vVwivcvedv" translation="ال‍٧٨‍‏٧٧٨‍‏٧ ال‍٧٨‍‏٧٧٨‍‏٧" explanation=""/>
<roomname english="vvvwMvcvMdvv" translation="ال‍٧٨‍‏٧٧٧ ال‍٧٨‍‏٧٧٧ ال‍٧" explanation=""/>
<roomname english="DvvvwMvfvvMdvvv" translation="ال‍٧٨‍‏٧٧٧ ٨‍‏٧٧ الكا٧ل‍٧" explanation=""/>
<roomname english="Dvav Mvfvr Mdvvvv" translation="الچژپڤڤ‍ شپه الكاڤلڤ‍" explanation=""/>
<roomname english="Diav M for Mdrver" translation="الجريمة شبه الكاملة" explanation=""/>
<roomname english="Dial M for Murder" translation="الجري‍[‌م]‌‍ة شبه الكاملة" explanation="Named after the Hitchcock film (Second part of the final stage has references to Colour TV shows)"/>
<!-- - -->
<roomname english="Gvnsmoke" translation="ويسترن" explanation="Gunsmoke was a black and white Western (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Gvnsmove" translation="ڤپشثرن" explanation=""/>
<roomname english="Gvnvmovevv" translation="٧٧٨‍‏٨‍‏٧٧" explanation=""/>
<roomname english="Gunvmove1vv6" translation="٨‍‏ا٨‍‏٨‍‏٧٧٨‍‏ " explanation=""/>
<roomname english="Vunsmoke 19v6" translation="ڄاثشڤڤك ١٨٧٧" explanation=""/>
<roomname english="Gunsmoke 1966" translation="غانسموك 1966" explanation="Gunsmoke changed to colour in 1966 (Second part of the final stage has references to Colour TV shows)"/>
<!-- - -->
<roomname english="Please enjoy these repeats" translation="استمتعوا بالإعادة" explanation="This stage also has a number of rooms which are harder versions of easier challenges, like this one. (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Please envoy theve repeats" translation="اشثڤثڄڤ‍ا پالإڄاڎڤ‍" explanation=""/>
<roomname english="Plse envoy tse rvpvas" translation="ا٨‍‏٨‍‏٧٨‍‏٨‍‏٧ا ٨‍‏ا٧٨‍‏ا٨‍‏٧" explanation=""/>
<roomname english="Vl envoy te rvevs" translation="٨‍‏ا٧٨‍‏٨‍‏ا٨‍‏٨‍‏ا٨‍‏٧٨‍‏" explanation=""/>
<roomname english="Vv evo tv vevs" translation="٨‍‏ل٧ ٨‍‏ل‍٧٧ا٧٨‍‏" explanation=""/>
<roomname english="Iv vhv Mvrvivs" translation="ڄلى الهڤا‍ڤش" explanation=""/>
<roomname english="In the Margins" translation="على الهوامش" explanation="Not sure if this has a TV show reference, might just be meant literally (Second part of the final stage has references to Colour TV shows)"/>
<!-- - -->
<roomname english="Try Jiggling the Antenna" translation="فلنجرب خرخشة الهوائي" explanation="(The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Try Viggling the Antenna" translation="ڤلثچژپ څژڅشڤ‍ الهڤائي" explanation=""/>
<roomname english="TryJivglvng theAvtevna" translation="٧ل‍٨‍‏٧٨‍‏٧ ٨‍‏٨‍‏٨‍‏٨‍‏٧ ٨‍‏ل‍٨‍‏٧‍‍‍ا٨‍‏٧" explanation=""/>
<roomname english="Tvvivglvng thAvtvvv" translation="٨‍‏٨‍‏٧٨‍‏٧ ٨‍‏٨‍‏٨‍‏٧ ٨‍‏٨‍‏٧٨‍‏٧" explanation=""/>
<roomname english="Vvvgglvnv tvnvva" translation="٨‍‏٨‍‏٧٨‍‏٧ ٨‍‏٨‍‏٧٨‍‏٧٨‍‏٧" explanation=""/>
<roomname english="Vvavvnvs vvtv" translation="ا٧٧ا٧ ٨‍‏ل‍٧٨‍‏٧" explanation=""/>
<roomname english="Veavvn&apos;s Gvte" translation="أپڤاپ الچثڤ‍" explanation="Named after the 1980&apos;s film (Second part of the final stage has references to Colour TV shows)"/>
<roomname english="Heaven&apos;s Gate" translation="أبواب الجنة" explanation="Named after the 1980&apos;s film (Second part of the final stage has references to Colour TV shows)"/>
</roomnames_special>

View file

@ -0,0 +1,819 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Please read README.txt for information about the language files -->
<strings max_local_for="8x10">
<string english="LOADING... {percent|digits=2|spaces}%" translation="جار التحميل...{percent|digits=2|spaces}%" explanation="on loading screen. {percent} = number from 0-100" max="26" max_local="26"/>
<string english="Game paused" translation="راحة" explanation="pause screen" max="40" max_local="40"/>
<string english="[click to resume]" translation="[نضغط أي زر للمواصلة]" explanation="pause screen" max="40" max_local="40"/>
<string english="Press M to mute in game" translation="نضغط M لكتم الصوت أثناء اللعب" explanation="pause screen" max="40" max_local="40"/>
<string english="Press N to mute music only" translation="نضغط N لكتم الموسيقى فقط" explanation="pause screen" max="40" max_local="40"/>
<string english="MAKE AND PLAY EDITION" translation="إصدار اصنع والعب" explanation="" max="27" max_local="27"/>
<string english="[MMMMMM Mod Installed]" translation="[تعديل MMMMMM منصب]" explanation="" max="32" max_local="32"/>
<string english="play" translation="لعب" explanation="main menu option"/>
<string english="levels" translation="مراحل" explanation="main menu option"/>
<string english="options" translation="خيارات" explanation="main menu option"/>
<string english="translator" translation="مترجم" explanation="main menu option"/>
<string english="credits" translation="فريق" explanation="main menu option"/>
<string english="quit" translation="خروج" explanation="main menu option"/>
<string english="gameplay" translation="اللعبة" explanation="options menu option"/>
<string english="Gameplay Options" translation="إعدادات اللعبة" explanation="title" max="20" max_local="20"/>
<string english="Adjust various gameplay settings." translation="لتغيير إعدادات مختلفة تخص اللعبة." explanation="" max="38*5" max_local="38*5"/>
<string english="graphics" translation="البصريات" explanation="options menu option"/>
<string english="Graphics Options" translation="إعدادات الصور" explanation="title" max="20" max_local="20"/>
<string english="Adjust screen settings." translation="لتغيير إعدادات الشاشة." explanation="" max="38*5" max_local="38*5"/>
<string english="audio" translation="الصوتيات" explanation="options menu option"/>
<string english="Audio Options" translation="إعدادات الصوت" explanation="title" max="20" max_local="20"/>
<string english="Adjust volume settings and soundtrack." translation="لضبط إعدادات مستوى الصوت والموسيقى." explanation="" max="38*5" max_local="38*5"/>
<string english="Adjust volume settings." translation="لضبط إعدادات مستوى الصوت." explanation="" max="38*5" max_local="38*5"/>
<string english="continue" translation="مواصلة" explanation="menu option"/>
<string english="continue from teleporter" translation="مواصلة من التنقيلة" explanation="menu option, game save that was made at a teleporter"/>
<string english="Tele Save" translation="تخزينة تنقيلة" explanation="title, game save that was made at a teleporter" max="20" max_local="20"/>
<string english="continue from quicksave" translation="مواصلة من التخزينة السريعة" explanation="menu option, game save that was made freely from the menu (at any checkpoint)"/>
<string english="Quick Save" translation="تخزينة سريعة" explanation="title, game save that was made freely from the menu (at any checkpoint)" max="20" max_local="20"/>
<string english="proceed" translation="استمرار" explanation="menu option"/>
<string english="new game" translation="لعبة جديدة" explanation="menu option"/>
<string english="start new game" translation="بدء لعبة جديدة" explanation="menu option, extra confirmation"/>
<string english="secret lab" translation="مخبر سري" explanation="menu option"/>
<string english="play modes" translation="أطوار اللعب" explanation="menu option"/>
<string english="ERROR: No levels found." translation="خطأ: لم نجد المراحل." explanation="" max="38*3" max_local="38*3"/>
<string english="ERROR: This level has no start point!" translation="خطأ: لم نجد نقطة بدء لهذه المرحلة!" explanation="" max="38*5" max_local="38*5"/>
<string english="ERROR" translation="خطأ" explanation="title" max="20" max_local="20"/>
<string english="WARNING" translation="تحذير" explanation="title" max="20" max_local="20"/>
<string english="unlock play modes" translation="فتح أطوار اللعب" explanation="menu option"/>
<string english="Unlock Play Modes" translation="فتح أطوار اللعب" explanation="title" max="20" max_local="20"/>
<string english="Unlock parts of the game normally unlocked as you progress." translation="فتح أطوار اللعب فتحا عاديا حسب تقدمك في اللعبة." explanation="" max="38*5" max_local="38*5"/>
<string english="From here, you may unlock parts of the game that are normally unlocked as you play." translation="يمكنك من هنا فتح أجزاء من اللعبة فورا، كان من المفروض أن تفتح تدريجيا أثناء اللعب العادي." explanation="" max="38*5" max_local="38*5"/>
<string english="unlock ship jukebox" translation="فتح صندوق ألحان السفينة" explanation="menu option"/>
<string english="unlock secret lab" translation="فتح المخبر السري" explanation="menu option"/>
<string english="game pad" translation="يد التحكم" explanation="menu option"/>
<string english="Game Pad Options" translation="إعدادات يد التحكم" explanation="title" max="20" max_local="20"/>
<string english="Rebind your controller&apos;s buttons and adjust sensitivity." translation="يمكنك تعيين أزرار يد التحكم
وضبط حساسيتها." explanation="" max="38*5" max_local="38*5"/>
<string english="language" translation="اللغة" explanation="menu option"/>
<string english="Language" translation="اللغة" explanation="title" max="20" max_local="20"/>
<string english="Change the language." translation="تغيير اللغة." explanation="" max="38*2" max_local="38*2"/>
<string english="Can not change the language while a textbox is displayed in-game." translation="لا يمكن تغيير اللغة أثناء ظهور نص في اللعبة." explanation="" max="38*3" max_local="38*3"/>
<string english="clear main game data" translation="حذف بيانات اللعبة الأساسية" explanation="menu option"/>
<string english="clear custom level data" translation="حذف بيانات المراحل الاختيارية" explanation="menu option"/>
<string english="Clear Data" translation="حذف البيانات" explanation="title" max="20" max_local="20"/>
<string english="Delete your main game save data and unlocked play modes." translation="حذف بيانات حفظ اللعبة الأساسية وما يخص أطوار اللعب المفتوحة." explanation="" max="38*5" max_local="38*5"/>
<string english="Delete your custom level save data and completion stars." translation="حذف بيانات المراحل الاختيارية وما يخص نجوم الإكمال." explanation="completion stars: when completing a level you get either 1 or 2 stars" max="38*5" max_local="38*5"/>
<string english="Are you sure? This will delete your current saves..." translation="أحقا قررت ذلك؟
ستحذف تخزيناتك الحالية..." explanation="" max="38*7" max_local="38*7"/>
<string english="Are you sure you want to delete all your saved data?" translation="هل تأكدت من قرارك بحذف كل بياناتك المحفوظة؟" explanation="" max="38*7" max_local="38*7"/>
<string english="Are you sure you want to delete your quicksave?" translation="هل تأكدت من قرارك بحذف تخزينتك السريعة؟" explanation="only the quicksave of a custom level" max="38*7" max_local="38*7"/>
<string english="no! don&apos;t delete" translation="لا! دعها ولا تحذفها" explanation="menu option"/>
<string english="yes, delete everything" translation="أجل، احذف كل شيء" explanation="menu option"/>
<string english="yes, delete save" translation="أجل، احذف التخزينة" explanation="menu option"/>
<string english="soundtrack" translation="الموسيقى" explanation="menu option"/>
<string english="Soundtrack" translation="الموسيقى" explanation="title" max="20" max_local="20"/>
<string english="Toggle between MMMMMM and PPPPPP." translation="التبديل بين MMMMMM و PPPPPP." explanation="" max="38*2" max_local="38*2"/>
<string english="Current soundtrack: PPPPPP" translation="الموسيقى الحالية: PPPPPP" explanation="" max="38*3" max_local="38*3"/>
<string english="Current soundtrack: MMMMMM" translation="الموسيقى الحالية: MMMMMM" explanation="" max="38*3" max_local="38*3"/>
<string english="toggle fullscreen" translation="تفعيل الشاشة الكاملة" explanation="menu option"/>
<string english="Toggle Fullscreen" translation="تفعيل الشاشة الكاملة" explanation="title" max="20" max_local="20"/>
<string english="Change to fullscreen/windowed mode." translation="التبديل بين وضع الشاشة الكاملة
ووضع النافذة." explanation="" max="38*3" max_local="38*3"/>
<string english="Current mode: FULLSCREEN" translation="الوضع الحالي: الشاشة الكاملة" explanation="" max="38*2" max_local="38*2"/>
<string english="Current mode: WINDOWED" translation="الوضع الحالي: النافذة" explanation="" max="38*2" max_local="38*2"/>
<string english="scaling mode" translation="وضع تكبير الصورة" explanation="menu option"/>
<string english="Scaling Mode" translation="وضع تكبير الصورة" explanation="title" max="20" max_local="20"/>
<string english="Choose letterbox/stretch/integer mode." translation="طريقة تكبير الصورة لتملأ الشاشة." explanation="See the `Current mode` explanations for more details on the modes" max="38*3" max_local="38*3"/>
<string english="Current mode: INTEGER" translation="الوضع الحالي: مضاعف صحيح" explanation="integer (whole number) mode only enlarges the game in exact multiples of 320x240" max="38*2" max_local="38*2"/>
<string english="Current mode: STRETCH" translation="الوضع الحالي: تمطيط الصورة" explanation="stretch mode just stretches the game to fill the window content" max="38*2" max_local="38*2"/>
<string english="Current mode: LETTERBOX" translation="الوضع الحالي: حواف سوداء" explanation="letterbox mode enlarges the game to the window, but adds black bars to make the aspect ratio 4:3" max="38*2" max_local="38*2"/>
<string english="resize to nearest" translation="التكبير لأقرب مضاعف" explanation="menu option. The game will be resized to the closest multiple of the normal resolution, 320x240, so for example if your window is 682x493, it will resize to 640x480"/>
<string english="Resize to Nearest" translation="تكبير لأقرب مضاعف" explanation="title. The game will be resized to the closest multiple of the normal resolution, 320x240, so for example if your window is 682x493, it will resize to 640x480" max="20" max_local="20"/>
<string english="Resize to the nearest window size that is of an integer multiple." translation="تكبير لأقرب حجم شاشة يكون عددا مضاعفا صحيحا للأبعاد الأصلية." explanation="The game will be resized to the closest multiple of the normal resolution, 320x240, so for example if your window is 682x493, it will resize to 640x480" max="38*3" max_local="38*3"/>
<string english="You must be in windowed mode to use this option." translation="يشترط هذا الخيار وضع النافذة." explanation="The game cannot be in fullscreen to resize the window" max="38*2" max_local="38*2"/>
<string english="toggle filter" translation="تفعيل فلتر الصورة" explanation="menu option"/>
<string english="Toggle Filter" translation="تفعيل فلتر الصورة" explanation="title" max="20" max_local="20"/>
<string english="Change to nearest/linear filter." translation="تغيير طريقة معالجة جودة الصورة." explanation="" max="38*3" max_local="38*3"/>
<string english="Current mode: LINEAR" translation="الوضع الحالي: خطي" explanation="" max="38*2" max_local="38*2"/>
<string english="Current mode: NEAREST" translation="الوضع الحالي: أقرب جار" explanation="nearest neighbor filter" max="38*2" max_local="38*2"/>
<string english="toggle analogue" translation="تفعيل صورة البث التناظري" explanation="menu option, analogue mode simulates distortion from bad signal"/>
<string english="Analogue Mode" translation="صورة البث التناظري" explanation="title, analogue mode simulates distortion from bad signal" max="20" max_local="20"/>
<string english="There is nothing wrong with your television set. Do not attempt to adjust the picture." translation="تلفازك بخير. لا تحاول تصحيح صورته." explanation="" max="38*5" max_local="38*5"/>
<string english="toggle fps" translation="تفعيل حد الإطارات" explanation="menu option, kind of a misnomer, toggle between 30 FPS and more than 30 FPS"/>
<string english="Toggle 30+ FPS" translation="تجاوز 30 إطار/ثانية" explanation="title" max="20" max_local="20"/>
<string english="Change whether the game runs at 30 or over 30 FPS." translation="هل تعرض صور اللعبة بنسق
30 إطار/ثانية أو أكثر." explanation="" max="38*3" max_local="38*3"/>
<string english="Current mode: 30 FPS" translation="الوضع الحالي: 30 إطار/ثانية" explanation="" max="38*2" max_local="38*2"/>
<string english="Current mode: Over 30 FPS" translation="الوضع الحالي: أكثر من 30 إطار/ثانية" explanation="" max="38*2" max_local="38*2"/>
<string english="toggle vsync" translation="تفعيل التزامن العمودي" explanation="menu option"/>
<string english="Toggle VSync" translation="تفعيل التزامن العمودي" explanation="title" max="20" max_local="20"/>
<string english="Turn VSync on or off." translation="تفعيل التزامن العمودي Vsync من عدمه." explanation="" max="38*2" max_local="38*2"/>
<string english="Current mode: VSYNC OFF" translation="الوضع الحالي: VSYNC OFF (لا)" explanation="" max="38*2" max_local="38*2"/>
<string english="Current mode: VSYNC ON" translation="الوضع الحالي: VSYNC ON (نعم)" explanation="" max="38*2" max_local="38*2"/>
<string english="music volume" translation="مستوى الموسيقى" explanation="audio menu option"/>
<string english="Music Volume" translation="مستوى الموسيقى" explanation="title" max="20" max_local="20"/>
<string english="Change the volume of the music." translation="تغيير مستوى صوت الموسيقى." explanation="" max="38*3" max_local="38*3"/>
<string english="sound volume" translation="مستوى الأصوات" explanation="audio menu option"/>
<string english="Sound Volume" translation="مستوى الأصوات" explanation="title" max="20" max_local="20"/>
<string english="Change the volume of sound effects." translation="تغيير مستوى مؤثرات الصوت." explanation="" max="38*3" max_local="38*3"/>
<string english="Credits" translation="فريق العمل" explanation="credits" max="20" max_local="20"/>
<string english="VVVVVV is a game by" translation="لعبة VVVVVV من" explanation="credits" max="40" max_local="40"/>
<string english="and features music by" translation="ومن تلحين" explanation="credits" max="40" max_local="40"/>
<string english="Roomnames are by" translation="وأسماء غرفها من إعداد" explanation="credits" max="40" max_local="40"/>
<string english="C++ version by" translation="ونسخة ++C من تطوير" explanation="credits" max="40" max_local="40"/>
<string english="Beta Testing by" translation="واختبار البيتا بفضل" explanation="credits" max="30" max_local="30"/>
<string english="Ending Picture by" translation="وصورة النهاية من رسم" explanation="credits" max="30" max_local="30"/>
<string english="Created by" translation="صنعها" explanation="credits" max="30" max_local="30"/>
<string english="With Music by" translation="موسيقاها من" explanation="credits" max="30" max_local="30"/>
<string english="Rooms Named by" translation="أسماء غرفها من" explanation="credits" max="30" max_local="30"/>
<string english="C++ Port by" translation="نقلها إلى ++C من" explanation="credits" max="30" max_local="30"/>
<string english="Patrons" translation="داعموها" explanation="credits, people who donated a certain amount before the game originally released" max="13" max_local="13"/>
<string english="VVVVVV is supported by the following patrons" translation="لعبة VVVVVV يدعمها المتبرعون المذكورون" explanation="credits" max="38*3" max_local="38*3"/>
<string english="and also by" translation="علاوة على" explanation="credits, VVVVVV is also supported by the following people" max="38*2" max_local="38*2"/>
<string english="and" translation="ومعهم" explanation="credits. This list of names, AND furthermore, this other list of names" max="38" max_local="38"/>
<string english="GitHub Contributors" translation="مساهمو غيتهب" explanation="credits. This doesn&apos;t _really_ need `GitHub` specifically, it could be replaced with `Code`" max="20" max_local="20"/>
<string english="With contributions on GitHub from" translation="مع مساهمات برمجة غيتهب من" explanation="credits" max="40" max_local="40"/>
<string english="and thanks also to:" translation="مع شكر خاص كذلك:" explanation="credits, and thanks also to ... you!" max="38*3" max_local="38*3"/>
<string english="You!" translation="لك أنت!" explanation="credits, and thanks also to ... you!" max="20" max_local="20"/>
<string english="Your support makes it possible for me to continue making the games I want to make, now and into the future." translation="دعمكم يمكنني من الاستمرار في صنع الألعاب التي أريدها اليوم ومستقبلا." explanation="credits" max="38*5" max_local="38*5"/>
<string english="Thank you!" translation="أشكركم!" explanation="credits" max="38*5" max_local="38*5"/>
<string english="Good luck!" translation="حظا موفقا!" explanation="" max="38*3" max_local="38*3"/>
<string english="You cannot save in this mode." translation="لا يمكنك الحفظ في هذا الطور." explanation="" max="38*3" max_local="38*3"/>
<string english="Would you like to disable the cutscenes during the game?" translation="هل تأكدت من قرارك بتعطيل المشاهد أثناء اللعبة؟" explanation="" max="38*6" max_local="38*6"/>
<string english="disable cutscenes" translation="تعطيل المشاهد" explanation="menu option"/>
<string english="enable cutscenes" translation="تفعيل المشاهد" explanation="menu option"/>
<string english="analog stick sensitivity" translation="حساسية عصا يد التحكم" explanation="menu option"/>
<string english="Stick Sensitivity" translation="حساسية العصا" explanation="title" max="20" max_local="20"/>
<string english="Change the sensitivity of the analog stick." translation="تغيير حساسية عصا التحكم." explanation="" max="38*3" max_local="38*3"/>
<string english="Low" translation="منخفضة" explanation="analog stick sensitivity, game pad menu"/>
<string english="Medium" translation="متوسطة" explanation="analog stick sensitivity, game pad menu"/>
<string english="High" translation="عالية" explanation="analog stick sensitivity, game pad menu"/>
<string english="bind flip" translation="تعيين العكس" explanation="menu option"/>
<string english="Bind Flip" translation="تعيين زر العكس" explanation="title" max="20" max_local="20"/>
<string english="bind enter" translation="تعيين الدخول" explanation="menu option"/>
<string english="Bind Enter" translation="تعيين زر الدخول" explanation="title" max="20" max_local="20"/>
<string english="bind menu" translation="تعيين القائمة" explanation="menu option"/>
<string english="Bind Menu" translation="تعيين زر القائمة" explanation="title" max="20" max_local="20"/>
<string english="bind restart" translation="تعيين إعادة الغرفة" explanation="menu option. In-game death key to restart at checkpoint"/>
<string english="Bind Restart" translation="تعيين زر الإعادة" explanation="title" max="20" max_local="20"/>
<string english="bind interact" translation="تعيين التفاعل" explanation="menu option"/>
<string english="Bind Interact" translation="تعيين زر التفاعل" explanation="title" max="20" max_local="20"/>
<string english="Flip is bound to: " translation="العكس معين للزر: " explanation="controller binds, bound to A, B, X, Y, etc. These strings end with a space!" max="32" max_local="32"/>
<string english="Enter is bound to: " translation="الدخول معين للزر: " explanation="controller binds, bound to A, B, X, Y, etc. These strings end with a space!" max="32" max_local="32"/>
<string english="Menu is bound to: " translation="القائمة معينة للزر: " explanation="controller binds, bound to A, B, X, Y, etc. These strings end with a space!" max="32" max_local="32"/>
<string english="Restart is bound to: " translation="إعادة الغرفة معينة للزر: " explanation="in-game death key to restart at checkpoint. Controller binds, bound to A, B, X, Y, etc. These strings end with a space!" max="32" max_local="32"/>
<string english="Interact is bound to: " translation="التفاعل معين للزر: " explanation="controller binds, bound to A, B, X, Y, etc. These strings end with a space!" max="32" max_local="32"/>
<string english="Press a button...|(or press ↑↓)" translation="يرجى ضغط زر...|(أو ضغط ↑↓)" explanation="the arrows represent up/down buttons, or stick movement... So: press a controller button, or navigate away" max="38*2" max_local="38*2"/>
<string english="Add {button}?|Press again to confirm" translation="إضافة {button}؟|اضغط ثانية للتأكيد" explanation="Bind the X button to this action? Press X again to really add it" max="38*2" max_local="38*2"/>
<string english="Remove {button}?|Press again to confirm" translation="إزالة {button}؟|اضغط ثانية للتأكيد" explanation="Remove the binding of the X button for this action? Press X again to really remove it" max="38*2" max_local="38*2"/>
<string english="Interact is currently Enter!|See speedrunner options." translation="زر التفاعل حاليا Enter !|راجع إعدادات التختيم السريع." explanation="the Interact action can&apos;t be configured now because it&apos;s the same as the Enter action. There&apos;s an option in the Speedrunner options to split it off" max="38*2" max_local="38*2"/>
<string english="ERROR: No language files found." translation="خطأ: لم نجد ملفات اللغة." explanation="" max="38*3" max_local="38*3"/>
<string english="Language folder:" translation="مجلد اللغة: " explanation="" max="39" max_local="39"/>
<string english="Repository language folder:" translation="مجلد اللغة في غيتهب: " explanation="Language folder from the Git repository" max="39" max_local="39"/>
<string english="translator options" translation="إعدادات المترجم" explanation="menu option"/>
<string english="Translator options" translation="إعدادات المترجم" explanation="title" max="20" max_local="20"/>
<string english="Some options that are useful for translators and developers." translation="بعض الاختيارات التي قد تفيد المترجمين والمطورين" explanation="" max="38*6" max_local="38*6"/>
<string english="maintenance" translation="صيانة" explanation="menu option, menu that allows you to apply maintenance to translations"/>
<string english="Maintenance" translation="صيانة" explanation="title" max="20" max_local="20"/>
<string english="open lang folder" translation="فتح مجلد اللغة" explanation="menu option. Button that opens the folder with language files (which is called lang) in a file explorer"/>
<string english="Sync all language files after adding new strings." translation="مزامنة كل ملفات اللغة بعد إضافة سطور جديدة." explanation="" max="38*6" max_local="38*6"/>
<string english="translate room names" translation="ترجمة أسماء الغرف" explanation="menu option"/>
<string english="Translate rooms" translation="ترجمة الغرف" explanation="title" max="20" max_local="20"/>
<string english="Enable room name translation mode, so you can translate room names in context. Press I for invincibility." translation="تفعيل وضع ترجمة أسماء الغرف، حتى تترجمها في سياقها. اضغط I للحصانة." explanation="" max="38*4" max_local="38*4"/>
<string english="You have not enabled room name translation mode!" translation="لم يفعل وضع ترجمة أسماء الغرف." explanation="" max="38*4" max_local="38*4"/>
<string english="menu test" translation="اختبار القوائم" explanation="menu option"/>
<string english="Menu test" translation="اختبار القوائم" explanation="title" max="20" max_local="20"/>
<string english="Cycle through most menus in the game. The menus will not actually work, all options take you to the next menu instead. Press Escape to stop." translation="يتفحص معظم قوائم اللعبة. لا تعمل عملها الأصلي، بل تنقلك للقائمة المختبرة الأخرى. اضغط ESC للتوقف." explanation="" max="38*6" max_local="38*6"/>
<string english="cutscene test" translation="اختبار المشاهد" explanation="menu option"/>
<string english="Cutscene test" translation="اختبار المشاهد" explanation="title" max="20" max_local="20"/>
<string english="Display all text boxes from cutscenes.xml. Only tests the basic appearance of each individual text box." translation="إظهار كل صناديق النصوص من cutscenes.xml لكنه لا يختبر إلا المظهر العام للنص دون سواه." explanation="" max="38*6" max_local="38*6"/>
<string english="from clipboard" translation="من ذاكرة النسخ" explanation="menu option, paste script name from clipboard"/>
<string english="explore game" translation="استطلاع اللعبة" explanation="menu option"/>
<string english="Explore game" translation="استطلاع اللعبة" explanation="title" max="20" max_local="20"/>
<string english="Explore the rooms of any level in the game, to find all room names to translate." translation="استطلاع غرف أي مستوى من اللعبة لكشف كل أسماء الغرف التي تحتاج ترجمات." explanation="" max="38*6" max_local="38*6"/>
<string english="limits check" translation="تحقق الحد" explanation="menu option"/>
<string english="global limits check" translation="تحقق الحد الشامل" explanation="menu option"/>
<string english="Limits check" translation="التحقق من الحدود" explanation="title" max="20" max_local="20"/>
<string english="Find translations that don&apos;t fit within their defined bounds." translation="البحث عن ترجمات جاوزت حدودها." explanation="" max="38*6" max_local="38*6"/>
<string english="No text overflows found!" translation="لم نجد فيضان نصوص!" explanation="limits check. no strings go outside their max bounds" max="38*6" max_local="38*6"/>
<string english="No text overflows left!" translation="لم نبق على فيضان نصوص!" explanation="limits check. we have seen all strings that go outside their max bounds" max="38*6" max_local="38*6"/>
<string english="Note that this detection isn&apos;t perfect." translation="مع الملاحظة أن هذا التحقق ليس متقنا." explanation="limits check" max="38*6" max_local="38*6"/>
<string english="sync language files" translation="مزامنة ملفات اللغة" explanation="menu option"/>
<string english="Sync language files" translation="مزامنة ملفات اللغة" explanation="title, translation maintenance menu" max="20" max_local="20"/>
<string english="sync" translation="مزامنة " explanation="menu option, verb"/>
<string english="Merge all new strings from the template files into the translation files, keeping existing translations." translation="توحيد كل السطور الجديدة من ملفات التمبليت إلى ملفات الترجمة بالحفاظ على السطور الكاملة السابقة." explanation="translation maintenance menu" max="38*6" max_local="38*6"/>
<string english="language statistics" translation="إحصائيات اللغة" explanation="menu option"/>
<string english="global statistics" translation="إحصائيات عامة" explanation="menu option"/>
<string english="Statistics" translation="إحصائيات" explanation="title, translation maintenance menu" max="20" max_local="20"/>
<string english="Count the amount of untranslated strings for this language." translation="عدد السطور التي لم تترجم لهذه اللغة" explanation="" max="38*6" max_local="38*6"/>
<string english="Count the amount of untranslated strings for each language." translation="عدد السطور التي لم تترجم لكل لغة" explanation="translation maintenance menu" max="38*6" max_local="38*6"/>
<string english="If new strings were added to the English template language files, this feature will insert them in the translation files for all languages. Make a backup, just in case." translation="لو أضيفت سطور إنكليزية، تضيفها هذه الميزة لنصوص اللغات الأخرى. احتياطا، احتفظ بنسخة سلفا." explanation="translation maintenance menu" max="38*7" max_local="38*7"/>
<string english="Full syncing EN→All:" translation="مزامنة شاملة من الانكليزية للكل:" explanation="translation maintenance menu. The following list of language files can be fully synced from English to all other languages by pressing this button - new strings will be inserted in all languages" max="40" max_local="40"/>
<string english="Syncing not supported:" translation="المزامنة غير مدعومة:" explanation="translation maintenance menu. The following list of language files are untouched by the sync button" max="40" max_local="40"/>
<string english="advanced options" translation="إعدادات متقدمة" explanation="menu option"/>
<string english="Advanced Options" translation="إعدادات متقدمة" explanation="title" max="20" max_local="20"/>
<string english="All other gameplay settings." translation="كل إعدادات اللعبة الأخرى" explanation="description for advanced options" max="38*5" max_local="38*5"/>
<string english="unfocus pause" translation="إزالة تركيز الراحة" explanation="menu option. Turns the pause screen on/off, which shows up when the window is unfocused/inactive (only one window is normally in focus/active at a time). Possible alternative: auto pause screen"/>
<string english="Unfocus Pause" translation="إزالة تركيز الراحة" explanation="title. Turns the pause screen on/off, which shows up when the window is unfocused/inactive (only one window is normally in focus/active at a time). Possible alternative: auto pause screen" max="20" max_local="20"/>
<string english="Toggle if the game will pause when the window is unfocused." translation="يحدد هل ستتوقف اللعبة لو زال التركيز عن النافذة." explanation="" max="38*3" max_local="38*3"/>
<string english="Unfocus pause is OFF" translation="تركيز الراحة معطل" explanation="Making another window active will not show the pause screen." max="38*2" max_local="38*2"/>
<string english="Unfocus pause is ON" translation="تركيز الراحة مفعل" explanation="Making another window active will show the pause screen." max="38*2" max_local="38*2"/>
<string english="unfocus audio pause" translation="الكتم عند زوال التركيز" explanation="menu option. Allows the user to choose whether the music should pause, or continue to play, when the window is unfocused/inactive (only one window is normally in focus/active at a time). Possible alternative: auto audio pause"/>
<string english="Unfocus Audio" translation="الكتم عند زوال التركيز" explanation="title. Allows the user to choose whether the music should pause, or continue to play, when the window is unfocused/inactive (only one window is normally in focus/active at a time). Possible alternative: auto audio pause" max="20" max_local="20"/>
<string english="Toggle if the audio will pause when the window is unfocused." translation="يحدد هل سيكتم صوت اللعبة لو زال التركيز عن النافذة." explanation="" max="38*3" max_local="38*3"/>
<string english="Unfocus audio pause is OFF" translation="الصمت عند زوال التركيز معطل" explanation="Making another window active will leave the music keep playing." max="38*2" max_local="38*2"/>
<string english="Unfocus audio pause is ON" translation="الصمت عند زوال التركيز مفعل" explanation="Making another window active will pause the music." max="38*2" max_local="38*2"/>
<string english="toggle in-game timer" translation="خيار إظهار المؤقت أثناء اللعبة" explanation="menu option"/>
<string english="In-Game Timer" translation="المؤقت أثناء اللعبة" explanation="title" max="20" max_local="20"/>
<string english="Toggle the in-game timer outside of time trials." translation="إظهار المؤقت في اللعبة حتى لغير التحدي ضد الساعة." explanation="" max="38*3" max_local="38*3"/>
<string english="In-Game Timer is ON" translation="المؤقت أثناء اللعبة مفعل" explanation="" max="38*2" max_local="38*2"/>
<string english="In-Game Timer is OFF" translation="المؤقت أثناء اللعبة معطل" explanation="" max="38*2" max_local="38*2"/>
<string english="english sprites" translation="الصور الإنكليزية" explanation="menu option"/>
<string english="English Sprites" translation="الصور الإنكليزية" explanation="title" max="20" max_local="20"/>
<string english="Show the original English word enemies regardless of your language setting." translation="تظهر صور الأعداء بنسختها الإنكليزية الأصلية، بصرف النظر عن اللغة المختارة." explanation="" max="38*3" max_local="38*3"/>
<string english="Sprites are currently translated" translation="صور الأعداء مترجمة الآن" explanation="" max="38*2" max_local="38*2"/>
<string english="Sprites are currently ALWAYS ENGLISH" translation="صور الأعداء دوما بالإنكليزية الآن" explanation="" max="38*2" max_local="38*2"/>
<string english="interact button" translation="زر التفاعل" explanation="menu option"/>
<string english="Interact Button" translation="زر التفاعل" explanation="title, lets the user change the key for interacting with objects or crewmates" max="20" max_local="20"/>
<string english="Toggle whether you interact with prompts using ENTER or E." translation="هل التفاعل مع النصوص بزر ENTER أو E." explanation="prompts: see the `Press {button} to talk to .../activate terminal/teleport` below" max="38*3" max_local="38*3"/>
<string english="E" translation="E" explanation="keyboard key E. Speedrunner options menu"/>
<string english="ENTER" translation="ENTER" explanation="keyboard key ENTER. Speedrunner options menu"/>
<string english="ESC" translation="ESC" explanation="keyboard key ESC"/>
<string english="TAB" translation="TAB" explanation="keyboard key TAB"/>
<string english="ACTION" translation="زر الفعل" explanation="the ACTION key is either the SPACE key, Z or V (this is explained on the title screen). It&apos;s used in strings like `Press ACTION to advance text`"/>
<string english="Interact button: {button}" translation="زر التفاعل: {button}" explanation="keyboard key (E or ENTER) is filled in for {button}. Speedrunner options menu" max="38*2" max_local="38*2"/>
<string english="fake load screen" translation="شاشة التحميل المزيفة" explanation="menu option"/>
<string english="Fake Load Screen" translation="شاشة التحميل المزيفة" explanation="title, allows the loading screen which counts to 100% to be turned off" max="20" max_local="20"/>
<string english="Disable the fake loading screen which appears on game launch." translation="تعطيل شاشة التحميل المزيفة التي تظهر قبل اللعبة." explanation="" max="38*3" max_local="38*3"/>
<string english="Fake loading screen is OFF" translation="شاشة التحميل المزيفة معطلة" explanation="allows the loading screen which counts to 100% to be turned off" max="38*2" max_local="38*2"/>
<string english="Fake loading screen is ON" translation="شاشة التحميل المزيفة تعمل" explanation="allows the loading screen which counts to 100% to be turned off" max="38*2" max_local="38*2"/>
<string english="room name background" translation="خلفية اسم الغرفة" explanation="menu option, background behind room names"/>
<string english="Room Name BG" translation="خلفية اسم الغرفة" explanation="title, background behind room names" max="20" max_local="20"/>
<string english="Lets you see through what is behind the name at the bottom of the screen." translation="وضوح رؤية ما وراء اسم الغرفة أسفل الشاشة." explanation="" max="38*3" max_local="38*3"/>
<string english="Room name background is TRANSLUCENT" translation="خلفية اسم الغرفة شفافة" explanation="" max="38*2" max_local="38*2"/>
<string english="Room name background is OPAQUE" translation="خلفية اسم الغرفة ليست شفافة" explanation="" max="38*2" max_local="38*2"/>
<string english="checkpoint saving" translation="تخزين نقطة الحفظ" explanation="menu option"/>
<string english="Checkpoint Saving" translation="تخزين نقطة الحفظ" explanation="title, makes checkpoints save the game" max="20" max_local="20"/>
<string english="Toggle if checkpoints should save the game." translation="تفعيل تخزين التقدم عند كل نقطة حفظ." explanation="" max="38*3" max_local="38*3"/>
<string english="Checkpoint saving is OFF" translation="نقاط الحفظ لا تخزن التقدم" explanation="makes checkpoints save the game" max="38*2" max_local="38*2"/>
<string english="Checkpoint saving is ON" translation="نقاط الحفظ تخزن التقدم" explanation="makes checkpoints save the game" max="38*2" max_local="38*2"/>
<string english="speedrun options" translation="إعدادات التختيم السريع" explanation="menu option"/>
<string english="Speedrunner Options" translation="إعدادات التختيم السريع" explanation="title" max="20" max_local="20"/>
<string english="Access some advanced settings that might be of interest to speedrunners." translation="دخول إعدادات متقدمة قد تحظى باهتمام
من يختمون الألعاب بسرعة." explanation="description for speedrunner options" max="38*5" max_local="38*5"/>
<string english="glitchrunner mode" translation="إعدادات صائد العيوب" explanation="menu option, a glitchrunner is a speedrunner who takes advantage of glitches to run through the game faster"/>
<string english="Glitchrunner Mode" translation="إعدادات صائد العيوب" explanation="title, a glitchrunner is a speedrunner who takes advantage of glitches to run through the game faster" max="20" max_local="20"/>
<string english="Re-enable glitches that existed in previous versions of the game." translation="صائد العيوب يستغلها ليختم اللعبة بسرعة. هذا الوضع يعيد تفعيل عيوب برمجية من إصدارات سابقة سبق إصلاحها." explanation="glitchrunner mode" max="38*3" max_local="38*3"/>
<string english="Glitchrunner mode is OFF" translation="وضع صائد العيوب السريع معطل." explanation="" max="38*2" max_local="38*2"/>
<string english="Glitchrunner mode is {version}" translation="وضع صائد العيوب السريع مفعل." explanation="a version number is filled in for {version}, such as 2.0 or 2.2" max="38*2" max_local="38*2"/>
<string english="Select a new glitchrunner version below." translation="يرجى اختيار إصدار جديد لصائد العيوب السريع." explanation="" max="38*3" max_local="38*3"/>
<string english="none" translation="بدون" explanation="menu option, do not emulate any older version"/>
<string english="2.0" translation="2.0" explanation="VVVVVV version number for glitchrunner mode"/>
<string english="2.2" translation="2.2" explanation="VVVVVV version number for glitchrunner mode"/>
<string english="input delay" translation="تأجيل الضغطة" explanation="menu option, enable 1 frame of delay after pressing input"/>
<string english="Input Delay" translation="تأجيل الضغطة" explanation="title, enable 1 frame of delay after pressing input" max="20" max_local="20"/>
<string english="Re-enable the 1-frame input delay from previous versions of the game." translation="إرجاع تأجيل الضغطة بإطار واحد كما كان الحال في تحديثات اللعبة السابقة" explanation="input delay" max="38*3" max_local="38*3"/>
<string english="Input delay is ON" translation="تأجيل الضغطة يعمل" explanation="" max="38*2" max_local="38*2"/>
<string english="Input delay is OFF" translation="تأجيل الضغطة معطل" explanation="" max="38*2" max_local="38*2"/>
<string english="accessibility" translation="تسهيل الاستعمال" explanation="menu option"/>
<string english="Accessibility" translation="تسهيل الاستعمال" explanation="title" max="20" max_local="20"/>
<string english="Disable screen effects, enable slowdown modes or invincibility." translation="تعطيل بعض مؤثرات الشاشة، تفعيل أطوار لإبطاء اللعبة أو للحصانة من الأذى." explanation="" max="38*5" max_local="38*5"/>
<string english="animated backgrounds" translation="الخلفيات المتحركة" explanation="menu option"/>
<string english="Backgrounds" translation="الخلفيات" explanation="title" max="20" max_local="20"/>
<string english="Disable animated backgrounds in menus and during gameplay." translation="تعطيل الخلفيات المتحركة في القوائم وأثناء اللعب." explanation="" max="38*3" max_local="38*3"/>
<string english="Backgrounds are ON." translation="الخلفيات تعمل." explanation="" max="38*2" max_local="38*2"/>
<string english="Backgrounds are OFF." translation="الخلفيات معطلة." explanation="" max="38*2" max_local="38*2"/>
<string english="screen effects" translation="تأثيرات الشاشة" explanation="menu option"/>
<string english="Screen Effects" translation="تأثيرات الشاشة" explanation="title" max="20" max_local="20"/>
<string english="Disables screen shakes and flashes." translation="تعطيل مؤثرات ارتجاج الشاشة وومضات الضوء." explanation="" max="38*2" max_local="38*2"/>
<string english="Screen Effects are ON." translation="تأثيرات الشاشة تعمل." explanation="" max="38*3" max_local="38*3"/>
<string english="Screen Effects are OFF." translation="تأثيرات الشاشة معطلة." explanation="" max="38*3" max_local="38*3"/>
<string english="text outline" translation="برواز الحروف" explanation="menu option"/>
<string english="Text Outline" translation="برواز الحروف" explanation="title" max="20" max_local="20"/>
<string english="Disables outline on game text." translation="تعطيل البرواز الذي يحيط بالنصوص." explanation="" max="38*2" max_local="38*2"/>
<string english="Text outlines are ON." translation="برواز الحروف يعمل." explanation="" max="40" max_local="40"/>
<string english="Text outlines are OFF." translation="برواز الحروف معطل." explanation="" max="40" max_local="40"/>
<string english="invincibility" translation="حصانة" explanation="menu option"/>
<string english="Invincibility" translation="حصانة" explanation="title" max="20" max_local="20"/>
<string english="Explore the game freely without dying. (Can cause glitches.)" translation="للتجول بحرية في اللعبة بدون أن تموت. قد تظهر عيوب برمجية." explanation="invincibility mode" max="38*3" max_local="38*3"/>
<string english="Invincibility is ON." translation="الحصانة تعمل." explanation="" max="38*2" max_local="38*2"/>
<string english="Invincibility is OFF." translation="الحصانة معطلة." explanation="" max="38*2" max_local="38*2"/>
<string english="Are you sure you want to enable invincibility?" translation="ستفعل الحصانة من الأضرار.
أحقا قررت ذلك؟" explanation="" max="38*7" max_local="38*7"/>
<string english="no, return to options" translation="لا، أريد العودة للخيارات" explanation="menu option"/>
<string english="yes, enable" translation="نعم، أريد تفعيلها" explanation="menu option"/>
<string english="slowdown" translation="تبطئة اللعبة" explanation="menu option"/>
<string english="Slowdown" translation="تبطئة اللعبة" explanation="title" max="20" max_local="20"/>
<string english="Game Speed" translation="سرعة اللعبة" explanation="title" max="20" max_local="20"/>
<string english="Reduce the game speed." translation="خفض سرعة اللعبة." explanation="" max="38*2" max_local="38*2"/>
<string english="Select a new game speed below." translation="يمكنك اختيار سرعة لعبة جديدة." explanation="" max="38*2" max_local="38*2"/>
<string english="Game speed is normal." translation="سرعة اللعبة عادية." explanation="" max="38*2" max_local="38*2"/>
<string english="Game speed is at 80%" translation="سرعة اللعبة 80%." explanation="" max="38*2" max_local="38*2"/>
<string english="Game speed is at 60%" translation="سرعة اللعبة 60%." explanation="" max="38*2" max_local="38*2"/>
<string english="Game speed is at 40%" translation="سرعة اللعبة 40%." explanation="" max="38*2" max_local="38*2"/>
<string english="normal speed" translation="سرعة عادية" explanation="menu option"/>
<string english="80% speed" translation="سرعة 80%" explanation="menu option"/>
<string english="60% speed" translation="سرعة 60%" explanation="menu option"/>
<string english="40% speed" translation="سرعة 40%" explanation="menu option"/>
<string english="play intermission 1" translation="لعب وصلة الفاصل 1" explanation="menu option"/>
<string english="play intermission 2" translation="لعب وصلة الفاصل 2" explanation="menu option"/>
<string english="Who do you want to play the level with?" translation="مع من أردت لعب هذا المستوى؟" explanation="choose your NPC companion" max="38*8" max_local="38*8"/>
<string english="time trials" translation="تحدي ضد الساعة" explanation="menu option"/>
<string english="Time Trials" translation="تحدي ضد الساعة" explanation="title" max="20" max_local="20"/>
<string english="Replay any level in the game in a competitive time trial mode." translation="يمكنك إعادة أي مستوى في اللعبة في طور تنافسي ضد الساعة." explanation="" max="38*4" max_local="38*4"/>
<string english="Time Trials are not available with slowdown or invincibility." translation="تحدي ضد الساعة لا يتوفر مع الحصانة أو التبطئة." explanation="" max="38*3" max_local="38*3"/>
<string english="unlock time trials" translation="فتح تحدي ضد الساعة" explanation="menu option"/>
<string english="Unlock Time Trials" translation="فتح تحدي ضد الساعة" explanation="title" max="20" max_local="20"/>
<string english="You can unlock each time trial separately." translation="يمكنك فتح كل تحدي ضد الساعة على حدة." explanation="" max="38*5" max_local="38*5"/>
<string english="intermissions" translation="وصلات الفواصل" explanation="menu option"/>
<string english="Intermissions" translation="وصلات الفواصل" explanation="title" max="20" max_local="20"/>
<string english="Replay the intermission levels." translation="إعادة لعب مراحل وصلات الفواصل" explanation="" max="38*3" max_local="38*3"/>
<string english="unlock intermissions" translation="فتح وصلات الفواصل" explanation="menu option"/>
<string english="TO UNLOCK: Complete the intermission levels in-game." translation="الشرط: ختم مستويات وصلات الفواصل أثناء اللعب." explanation="" max="38*4" max_local="38*4"/>
<string english="no death mode" translation="تحدي بدون الموت" explanation="menu option"/>
<string english="No Death Mode" translation="تحدي بدون الموت" explanation="title" max="20" max_local="20"/>
<string english="Play the entire game without dying once." translation="تختيم اللعبة بأكملها دون الموت ولا مرة." explanation="" max="38*4" max_local="38*4"/>
<string english="No Death Mode is not available with slowdown or invincibility." translation="تحدي بدون الموت لا يتوفر مع الحصانة أو التبطئة." explanation="" max="38*3" max_local="38*3"/>
<string english="unlock no death mode" translation="فتح تحدي بدون الموت" explanation="menu option"/>
<string english="TO UNLOCK: Achieve an S-rank or above in at least 4 time trials." translation="الشرط: تحقيق الرتبة S أو ما فوق في 4 تحديات ضد الساعة على الأقل." explanation="ranks are B A S V, see below" max="38*3" max_local="38*3"/>
<string english="flip mode" translation="الوضع المعكوس" explanation="menu option, mirrors the entire game vertically"/>
<string english="Flip Mode" translation="الوضع المعكوس" explanation="title, mirrors the entire game vertically" max="20" max_local="20"/>
<string english="Flip the entire game vertically." translation="يعكس اللعبة بأكملها عموديا." explanation="" max="38*2" max_local="38*2"/>
<string english="Flip the entire game vertically. Compatible with other game modes." translation="يعكس اللعبة بأكملها عموديا. يتوافق مع أطوار اللعبة الأخرى." explanation="" max="38*4" max_local="38*4"/>
<string english="unlock flip mode" translation="فتح الوضع المعكوس" explanation="menu option"/>
<string english="Currently ENABLED!" translation="حاليا مفعل!" explanation="flip mode" max="38*3" max_local="38*3"/>
<string english="Currently Disabled." translation="حاليا معطل." explanation="flip mode" max="38*3" max_local="38*3"/>
<string english="TO UNLOCK: Complete the game." translation="الشرط: ختم اللعبة." explanation="" max="38*3" max_local="38*3"/>
<string english="Invincibility mode enabled" translation="فعلت وضع الحصانة" explanation="in-game message" max="39" max_local="39"/>
<string english="Glitchrunner mode enabled ({version})" translation="فعلت وضع صائد العيوب ({version})" explanation="in-game message" max="39" max_local="39"/>
<string english="Flip Mode enabled" translation="فعلت الوضع المعكوس" explanation="in-game message" max="39" max_local="39"/>
<string english="Are you sure you want to quit?" translation="هل قررت المغادرة حقا؟" explanation="quit the program" max="38*4" max_local="38*4"/>
<string english="GAME OVER" translation="انتهت اللعبة" explanation="bigger title" max="13" max_local="13"/>
<string english="You managed to reach:" translation="نجحت في وصول الغرفة:" explanation="you managed to reach the following room" max="40" max_local="40"/>
<string english="Keep trying! You&apos;ll get there!" translation="فلنواصل المحاولة! سندرك هدفنا!" explanation="player died before managing to save anybody" max="38*2" max_local="38*2"/>
<string english="Nice one!" translation="جميل!" explanation="player died after saving one crewmate" max="38*2" max_local="38*2"/>
<string english="Wow! Congratulations!" translation="واو! مبارك، تهانينا!" explanation="player died after saving two crewmates" max="38*2" max_local="38*2"/>
<string english="Incredible!" translation="شيء مذهل!" explanation="player died after saving three crewmates" max="38*2" max_local="38*2"/>
<string english="Unbelievable! Well done!" translation="لا أصدق! أحسنت صنيعا!" explanation="player died after saving four crewmates" max="38*2" max_local="38*2"/>
<string english="Er, how did you do that?" translation="هع، أنى لك هذا؟" explanation="player died even though they were finished, lol" max="38*2" max_local="38*2"/>
<string english="WOW" translation="واو!" explanation="even bigger title" max="10" max_local="10"/>
<string english="You rescued all the crewmates!" translation="أنقذت كل أفراد الطاقم!" explanation="" max="40" max_local="40"/>
<string english="A new trophy has been awarded and placed in the secret lab to acknowledge your achievement!" translation="أهديت جائزة جديدة ووضعت في المخبر السري إشادة بإنجازك!" explanation="" max="38*4" max_local="38*4"/>
<string english="[Trinkets found]" translation="[ التذكارات التي وجدتها ]" explanation="amount of shiny trinkets found" max="40" max_local="40"/>
<string english="[Number of Deaths]" translation="[ الميتات التي تكبدتها ]" explanation="" max="40" max_local="40"/>
<string english="[Time Taken]" translation="[ الوقت الذي استغرقته ]" explanation="stopwatch time" max="40" max_local="40"/>
<string english="Trinkets Found:" translation="التذكارات التي وجدتها:" explanation="game complete screen" max="22" max_local="22"/>
<string english="Game Time:" translation="وقت اللعب:" explanation="game complete screen" max="22" max_local="22"/>
<string english="Total Flips:" translation="مجموع العكسات:" explanation="game complete screen" max="22" max_local="22"/>
<string english="Total Deaths:" translation="مجموع الميتات:" explanation="game complete screen" max="22" max_local="22"/>
<string english="Results" translation="النتائج:" explanation="bigger title" max="13" max_local="13"/>
<string english="TIME TAKEN:" translation="الوقت المستغرق:" explanation="time the player took playing the level" max="32" max_local="32"/>
<string english="NUMBER OF DEATHS:" translation="عدد الميتات:" explanation="amount of times the player died" max="32" max_local="32"/>
<string english="SHINY TRINKETS:" translation="التذكارات اللماعات:" explanation="amount of trinkets collected" max="32" max_local="32"/>
<string english=" / " translation="/" explanation="inserted between two times, ex: 0:46.90 / 1:15.99. Time trial results"/>
<string english="{n_trinkets}/{max_trinkets}" translation="{n_trinkets}/{max_trinkets}" explanation="ex: 2/5"/>
<string english="{n_trinkets} of {max_trinkets}" translation="{n_trinkets} من أصل {max_trinkets}" explanation="ex: 2 of 5"/>
<string english="{n_trinkets|wordy} out of {max_trinkets|wordy}" translation="{n_trinkets|wordy} من أصل {max_trinkets|wordy}" explanation="ex: One out of Twenty, see numbers.xml. You can add |upper for an uppercase letter." max="34" max_local="34"/>
<string english="{savebox_n_trinkets|wordy}" translation="{savebox_n_trinkets|wordy}" explanation="trinket count in telesave/quicksave information box. You can add |upper for an uppercase letter."/>
<string english="{gamecomplete_n_trinkets|wordy}" translation="{gamecomplete_n_trinkets|wordy}" explanation="trinket count on Game Complete screen (after Trinkets Found:) You can add |upper for an uppercase letter."/>
<string english="+1 Rank!" translation="+1 للرتبة!" explanation="time trial rank was upgraded (B → A → S → V). B is minimum, which is purposefully high (see it as 7/10) - S is a popular rank above A in a lot of games, so VVVVVV added a rank above that." max="12" max_local="12"/>
<string english="Rank:" translation="الرتبة:" explanation="time trial rank" max="9" max_local="9"/>
<string english="B" translation="B" explanation="time trial rank" max="5" max_local="5"/>
<string english="A" translation="A" explanation="time trial rank" max="5" max_local="5"/>
<string english="S" translation="S" explanation="time trial rank" max="5" max_local="5"/>
<string english="V" translation="V" explanation="time trial rank" max="5" max_local="5"/>
<string english="space station 1" translation="المحطة الفضائية 1" explanation="area name as menu option"/>
<string english="Space Station 1" translation="المحطة الفضائية 1" explanation="area name as title" max="20" max_local="20"/>
<string english="space station 2" translation="المحطة الفضائية 2" explanation="area name as menu option"/>
<string english="Space Station 2" translation="المحطة الفضائية 2" explanation="area name as title" max="20" max_local="20"/>
<string english="the laboratory" translation="المخبر" explanation="area name as menu option"/>
<string english="The Laboratory" translation="المخبر" explanation="area name as title" max="20" max_local="20"/>
<string english="the tower" translation="البرج" explanation="area name as menu option"/>
<string english="The Tower" translation="البرج" explanation="area name as title" max="20" max_local="20"/>
<string english="the warp zone" translation="منطقة التنقيل" explanation="area name as menu option"/>
<string english="The Warp Zone" translation="منطقة التنقيل" explanation="area name as title" max="20" max_local="20"/>
<string english="the final level" translation="المستوى الأخير" explanation="area name as menu option"/>
<string english="The Final Level" translation="المستوى الأخير" explanation="area name as title" max="20" max_local="20"/>
<string english="intermission 1" translation="وصلة الفاصل 1" explanation="area name as menu option"/>
<string english="Intermission 1" translation="وصلة الفاصل 1" explanation="area name as title" max="20" max_local="20"/>
<string english="intermission 2" translation="وصلة الفاصل 2" explanation="area name as menu option"/>
<string english="Intermission 2" translation="وصلة الفاصل 2" explanation="area name as title" max="20" max_local="20"/>
<string english="???" translation="؟؟؟" explanation="locked area" max="20" max_local="20"/>
<string english="Not yet attempted" translation="لم تجرب من قبل" explanation="" max="38*2" max_local="38*2"/>
<string english="TO UNLOCK:" translation="شرط الفتح:" explanation="followed by `Rescue XX`/`Complete the game`, and then `Find XX trinkets`" max="40" max_local="40"/>
<string english="Rescue Violet" translation="إنقاذ فايولت" explanation="" max="40" max_local="40"/>
<string english="Rescue Victoria" translation="إنقاذ فيكتوريا" explanation="" max="40" max_local="40"/>
<string english="Rescue Vermilion" translation="إنقاذ فارميون" explanation="" max="40" max_local="40"/>
<string english="Rescue Vitellary" translation="إنقاذ فيتلاري" explanation="" max="40" max_local="40"/>
<string english="Rescue Verdigris" translation="إنقاذ فارديغري" explanation="" max="40" max_local="40"/>
<string english="Complete the game" translation="ختم اللعبة" case="0" explanation="" max="40" max_local="40"/>
<string english="Find three trinkets" translation="إيجاد ثلاث تذكارات" explanation="" max="40" max_local="40"/>
<string english="Find six trinkets" translation="إيجاد ست تذكارات" explanation="" max="40" max_local="40"/>
<string english="Find nine trinkets" translation="إيجاد تسع تذكارات" explanation="" max="40" max_local="40"/>
<string english="Find twelve trinkets" translation="إيجاد إثنتا عشر تذكار" explanation="" max="40" max_local="40"/>
<string english="Find fifteen trinkets" translation="إيجاد خمسة عشر تذكار" explanation="" max="40" max_local="40"/>
<string english="Find eighteen trinkets" translation="إيجاد ثمانية عشر تذكار" explanation="" max="40" max_local="40"/>
<string english="RECORDS" translation="السجلات" explanation="followed by a list of personal bests for TIME, SHINY and LIVES. So `records` as in `world records`, except for yourself" max="15" max_local="15"/>
<string english="TIME" translation="الوقت" explanation="record time" max="8" max_local="8"/>
<string english="SHINY" translation="اللماعات" explanation="record number of trinkets" max="8" max_local="8"/>
<string english="LIVES" translation="الحيوات" explanation="record lowest number of deaths" max="8" max_local="8"/>
<string english="PAR TIME" translation="وقت الهدف" explanation="followed by the goal time for this time trial" max="14" max_local="14"/>
<string english="TIME:" translation="الوقت:" explanation="in time trial. Stopwatch time, not too long"/>
<string english="DEATH:" translation="الميتات:" explanation="in time trial. Number of times player died, not too long"/>
<string english="SHINY:" translation="اللماعات:" explanation="in time trial. Number of shiny trinkets collected, not too long"/>
<string english="PAR TIME:" translation="وقت الهدف:" explanation="in time trial. Goal time for time trial"/>
<string english="BEST RANK" translation="أفضل رتبة" explanation="ranks are B A S V" max="17" max_local="17"/>
<string english="GO!" translation="هيا!" explanation="3, 2, 1, GO!" max="13" max_local="13"/>
<string english="Go!" translation="هيا!" explanation="3, 2, 1, Go!" max="10" max_local="10"/>
<string english="Congratulations!" translation="تهانينا!" explanation="title" max="20" max_local="20"/>
<string english="Your save files have been updated." translation="حدثت بيانات تخزيناتك." explanation="player completed game" max="38*6" max_local="38*6"/>
<string english="If you want to keep exploring the game, select CONTINUE from the play menu." translation="لو شئت مواصلة استكشاف اللعبة، فعليك بخيار المواصلة من قائمة اللعب." explanation="" max="38*9" max_local="38*9"/>
<string english="You have unlocked a new Time Trial." translation="فتحت تحديا من جملة التحديات ضد الساعة." explanation="" max="38*7" max_local="38*7"/>
<string english="You have unlocked some new Time Trials." translation="فتحت تحديا جديدا من جملة التحديات ضد الساعة." explanation="" max="38*7" max_local="38*7"/>
<string english="You have unlocked No Death Mode." translation="فتحت تحدي بدون الموت." explanation="" max="38*7" max_local="38*7"/>
<string english="You have unlocked Flip Mode." translation="فتحت الوضع المعكوس." explanation="" max="38*7" max_local="38*7"/>
<string english="You have unlocked the intermission levels." translation="فتحت مستويات وصلات الفواصل." explanation="" max="38*7" max_local="38*7"/>
<string english="play a level" translation="لعب مرحلة" explanation="menu option"/>
<string english="level editor" translation="محرر المراحل" explanation="menu option"/>
<string english="open level folder" translation="فتح مجلد المراحل" explanation="menu option. Button that opens the folder with level files in a file explorer"/>
<string english="show level folder path" translation="إظهار مسار مجلد المراحل" explanation="menu option"/>
<string english="return" translation="عودة" explanation="menu option"/>
<string english="return to levels" translation="عودة للمراحل" explanation="menu option"/>
<string english="no, don&apos;t show me" translation="لا، لا ترني" explanation="menu option"/>
<string english="yes, reveal the path" translation="أجل، اكشف عن المسار" explanation="menu option. Path to the levels folder"/>
<string english="return to play menu" translation="عودة لقائمة اللعب" explanation="menu option" max="36" max_local="36"/>
<string english="try again" translation="محاولة جديدة" explanation="menu option, retry time trial" max="36" max_local="36"/>
<string english="ok" translation="موافقة" explanation="button, which is a menu option, so lowercase"/>
<string english="next page" translation="الصفحة التالية" explanation="menu option"/>
<string english="previous page" translation="الصفحة السابقة" explanation="menu option"/>
<string english="first page" translation="الصفحة الأولى" explanation="menu option"/>
<string english="last page" translation="الصفحة الأخيرة" explanation="menu option"/>
<string english="silence" translation="تجاهل" explanation="menu option, silence error message - do not show this message again"/>
<string english="continue from save" translation="المواصلة من التخزينة" explanation="menu option"/>
<string english="start from beginning" translation="البدء من البداية" explanation="menu option"/>
<string english="delete save" translation="حذف التخزينة" explanation="menu option"/>
<string english="back to levels" translation="العودة للمستويات" explanation="menu option"/>
<string english="The level editor is not currently supported on Steam Deck, as it requires a keyboard and mouse to use." translation="محرر المستويات يحتاج لوح مفاتيح وفأرة. لهذا السبب، ليس مدعوما على منصة Steam Deck." explanation="" max="38*5" max_local="38*5"/>
<string english="The level editor is not currently supported on this device, as it requires a keyboard and mouse to use." translation="محرر المستويات يحتاج لوح مفاتيح وفأرة. لهذا السبب، ليس مدعوما على جهازك." explanation="" max="38*5" max_local="38*5"/>
<string english="To install new player levels, copy the .vvvvvv files to the levels folder." translation="لتنصيب مراحل اللاعبين الجديدة، تنسخ ملفات .vvvvvv إلى مجلد levels." explanation="" max="38*5" max_local="38*5"/>
<string english="Are you sure you want to show the levels path? This may reveal sensitive information if you are streaming." translation="قبل إظهار مسار المستويات، يرجى التأكد لتجنب افتضاح أي معلومات حساسة أثناء البثوث. هل نظهره؟" explanation="" max="38*4" max_local="38*4"/>
<string english="The levels path is:" translation="مسار مجلد levels للمستويات:" explanation="" max="40" max_local="40"/>
<string english="[ Press {button} to Start ]" translation="[ نضغط {button} للبدء]" explanation="title screen. Expect `ACTION`" max="38*2" max_local="38*2"/>
<string english="ACTION = Space, Z, or V" translation="زر الفعل = المسافة أو Z أو V" explanation="title screen" max="38*3" max_local="38*3"/>
<string english="[Press {button} to return to editor]" translation="[نضغط {button} للعودة للمحرر]" explanation="`to editor` is sorta redundant" max="40" max_local="40"/>
<string english="- Press {button} to advance text -" translation="- نضغط {button} للتقدم في النص -" explanation="to dismiss a textbox. Expect `ACTION`" max="40" max_local="40"/>
<string english="Press {button} to continue" translation="نضغط {button} للمواصلة" explanation="Expect `ACTION`" max="34" max_local="34"/>
<string english="[Press {button} to unfreeze gameplay]" translation="[نضغط {button} لوقف تجميد اللعب العادي]" explanation="in level debugger: {button} makes everything start moving as normal. Limit is treacherous, expect TAB for {button}. Frozen is the initial state, so this is the first string of the two that users will see!" max="39" max_local="39"/>
<string english="[Press {button} to freeze gameplay]" translation="[نضغط {button} لتجميد اللعب العادي]" explanation="in level debugger: {button} makes everything stop moving. Limit is treacherous, expect TAB for {button}." max="39" max_local="39"/>
<string english="Current Time" translation="وقتك الحالي" explanation="super gravitron, stopwatch time" max="20" max_local="20"/>
<string english="Best Time" translation="أفضل وقت" explanation="super gravitron, best stopwatch time" max="20" max_local="20"/>
<string english="Next Trophy at 5 seconds" translation="الجائزة القادمة بعد 5 ثوان" explanation="" max="38*2" max_local="38*2"/>
<string english="Next Trophy at 10 seconds" translation="الجائزة القادمة بعد 10 ثوان" explanation="" max="38*2" max_local="38*2"/>
<string english="Next Trophy at 15 seconds" translation="الجائزة القادمة بعد 15 ثانية" explanation="" max="38*2" max_local="38*2"/>
<string english="Next Trophy at 20 seconds" translation="الجائزة القادمة بعد 20 ثانية" explanation="" max="38*2" max_local="38*2"/>
<string english="Next Trophy at 30 seconds" translation="الجائزة القادمة بعد 30 ثانية" explanation="" max="38*2" max_local="38*2"/>
<string english="Next Trophy at 1 minute" translation="الجائزة القادمة بعد دقيقة" explanation="" max="38*2" max_local="38*2"/>
<string english="All Trophies collected!" translation="كل الجوائز جمعت!" explanation="" max="38*2" max_local="38*2"/>
<string english="New Record!" translation="رقم قياسي جديد!" explanation="" max="20" max_local="20"/>
<string english="New Trophy!" translation="جائزة جديدة!" explanation="" max="20" max_local="20"/>
<string english="[Press {button} to stop]" translation="[نضغط {button} للتوقف]" explanation="stop super gravitron" max="40" max_local="40"/>
<string english="SUPER GRAVITRON" translation="أتون الجاذبية الخارق" explanation="" max="20" max_local="20"/>
<string english="SUPER GRAVITRON HIGHSCORE" translation="أفضل نتيجة لأتون الجاذبية الخارق" explanation="" max="38*4" max_local="38*4"/>
<string english="MAP" translation="خريطة" explanation="in-game menu" max="8" max_local="8"/>
<string english="GRAV" translation="جاذبية" explanation="in-game menu, Gravitron" max="8" max_local="8"/>
<string english="SHIP" translation="سفينة" explanation="in-game menu, spaceship" max="8" max_local="8"/>
<string english="CREW" translation="طاقم" explanation="in-game menu" max="8" max_local="8"/>
<string english="STATS" translation="إحصاء" explanation="in-game menu" max="8" max_local="8"/>
<string english="SAVE" translation="حفظ" explanation="in-game menu" max="8" max_local="8"/>
<string english="[ PAUSE ]" translation="[ راحة ]" explanation="in-game menu" max="40" max_local="40"/>
<string english="[ QUIT ]" translation="[ خروج ]" explanation="in-game menu" max="40" max_local="40"/>
<string english="[ GRAVITRON ]" translation="[ أتون جاذبية ]" explanation="in-game menu" max="40" max_local="40"/>
<string english="NO SIGNAL" translation="لا إشارة" explanation="map screen. So like a TV/computer monitor" max="29" max_local="29"/>
<string english="Press {button} to warp to the ship." translation="للتنقيل إلى السفينة، نضغط {button}." explanation="spaceship. Warp = teleport. Expect `ACTION`" max="38*7" max_local="38*7"/>
<string english="Missing..." translation="مفقود..." case="1" explanation="this male crew member is missing" max="15" max_local="15"/>
<string english="Missing..." translation="مفقودة..." case="2" explanation="this female crew member is missing" max="15" max_local="15"/>
<string english="Missing..." translation="أين أنت..." case="3" explanation="Viridian is missing (final level). You could even fill in something like `Uh-oh...` here if you really have to specify gender otherwise - everyone else is rescued, but the player is missing" max="15" max_local="15"/>
<string english="Rescued!" translation="أنقذته!" case="1" explanation="this male crew member is not missing anymore" max="15" max_local="15"/>
<string english="Rescued!" translation="أنقذتها!" case="2" explanation="this female crew member is not missing anymore" max="15" max_local="15"/>
<string english="(that&apos;s you!)" translation="(ها أنت!)" explanation="this crew member is you (Viridian)" max="15" max_local="15"/>
<string english="Cannot Save in Level Replay" translation="لا يحق الحفظ عند مشاهدة المستوى" explanation="in-game menu" max="38*7" max_local="38*7"/>
<string english="Cannot Save in No Death Mode" translation="لا يحق الحفظ في تحدي بدون الموت" explanation="in-game menu" max="38*7" max_local="38*7"/>
<string english="How&apos;d you get here?" translation="كيف وصلت هنا؟" explanation="in-game menu" max="38*7" max_local="38*7"/>
<string english="Cannot Save in Secret Lab" translation="لا يحق الحفظ في المخبر السري" explanation="in-game menu" max="38*7" max_local="38*7"/>
<string english="ERROR: Could not save game!" translation="خطأ: فشل حفظ تقدم اللعبة!" explanation="in-game menu" max="34*2" max_local="34*2"/>
<string english="ERROR: Could not save settings file!" translation="خطأ: فشل حفظ ملف الإعدادات!" explanation="" max="38*2" max_local="38*2"/>
<string english="Game saved ok!" translation="حفظت اللعبة على خير!" explanation="in-game menu" max="38*2" max_local="38*2"/>
<string english="[Press {button} to save your game]" translation="[نضغط {button} لحفظ لعبتك]" explanation="in-game menu. Expect `ACTION`" max="40" max_local="40"/>
<string english="(Note: The game is autosaved at every teleporter.)" translation="(اللعبة تحفظ تلقائيا عند كل آلة تنقيل.)" explanation="in-game menu" max="38*3" max_local="38*3"/>
<string english="Last Save:" translation="آخر تخزينة:" explanation="in-game menu" max="40" max_local="40"/>
<string english="Return to main menu?" translation="هل بودك العودة للقائمة الرئيسية؟" explanation="in-game menu" max="38*4" max_local="38*4"/>
<string english="Do you want to quit? You will lose any unsaved progress." translation="هل قررت المغادرة؟ قد يضيع
أي تقدم لم يحفظ بعد." explanation="in-game menu" max="38*4" max_local="38*4"/>
<string english="Do you want to return to the secret laboratory?" translation="هل بودك العودة للمخبر السري؟" explanation="in-game menu" max="38*4" max_local="38*4"/>
<string english="no, keep playing" translation="لا، سأواصل اللعب" explanation="in-game menu option" max="28" max_local="28"/>
<string english="[ NO, KEEP PLAYING ]" translation="[ لا، سأواصل اللعب ]" explanation="in-game menu option" max="32" max_local="32"/>
<string english="yes, quit to menu" translation="نعم، سأغادر" explanation="in-game menu option" max="24" max_local="24"/>
<string english="[ YES, QUIT TO MENU ]" translation="[ نعم، سأغادر ]" explanation="in-game menu option" max="28" max_local="28"/>
<string english="yes, return" translation="نعم، سأعود" explanation="in-game menu option" max="24" max_local="24"/>
<string english="[ YES, RETURN ]" translation="[ نعم، سأعود ]" explanation="in-game menu option" max="28" max_local="28"/>
<string english="no, return" translation="لا، سأعود" explanation="quit program menu option"/>
<string english="yes, quit" translation="نعم، سأخرج" explanation="quit program menu option"/>
<string english="return to game" translation="العودة للعبة" explanation="pause menu option" max="27" max_local="27"/>
<string english="quit to menu" translation="الخروج للقائمة" explanation="pause menu option" max="19" max_local="19"/>
<string english="Press Left/Right to choose a Teleporter" translation="نضغط يسار/يمين لاختيار آلة تنقيل" explanation="tight fit, so maybe `Left/Right: choose teleporter`, or use ←/→" max="40" max_local="40"/>
<string english="Press {button} to Teleport" translation="نضغط {button} للتنقل" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}. max is when displayed" max="40" max_local="40"/>
<string english="- Press {button} to Teleport -" translation="- نضغط {button} للتنقل -" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}" max="40" max_local="40"/>
<string english="Press {button} to explode" translation="نضغط {button} للتفجر" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}. max is when displayed. This was for testing, but people sometimes find it in custom levels" max="37" max_local="37"/>
<string english="Press {button} to talk to Violet" translation="نضغط {button} للحديث مع فايولت" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}. max is when displayed" max="37" max_local="37"/>
<string english="Press {button} to talk to Vitellary" translation="نضغط {button} للحديث مع فيتيلاري" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}. max is when displayed" max="37" max_local="37"/>
<string english="Press {button} to talk to Vermilion" translation="نضغط {button} للحديث مع فارميليون" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}. max is when displayed" max="37" max_local="37"/>
<string english="Press {button} to talk to Verdigris" translation="نضغط {button} للحديث مع فارديغري" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}. max is when displayed" max="37" max_local="37"/>
<string english="Press {button} to talk to Victoria" translation="نضغط {button} للحديث مع فيكتوريا" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}. max is when displayed" max="37" max_local="37"/>
<string english="Press {button} to activate terminal" translation="نضغط {button} لتفعيل الحاسب" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}. max is when displayed" max="37" max_local="37"/>
<string english="Press {button} to activate terminals" translation="نضغط {button} لتفعيل الحواسيب" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}. max is when displayed. there are 3 next to each other in the ship" max="37" max_local="37"/>
<string english="Press {button} to interact" translation="نضغط {button} للتفاعل" explanation="keyboard key (E or ENTER) or controller button glyph is filled in for {button}. max is when displayed" max="37" max_local="37"/>
<string english="- Press {button} to skip -" translation="- نضغط {button} للتجاوز -" explanation="keyboard key (ENTER) or controller button glyph is filled in for {button}. max is when displayed. This prompt is for skipping cutscenes" max="40" max_local="40"/>
<string english="Passion for Exploring" translation="Passion for Exploring" explanation="jukebox prompt. song name should probably not be translated" max="37*2" max_local="37*2"/>
<string english="Pushing Onwards" translation="Pushing Onwards" explanation="jukebox prompt. song name should probably not be translated" max="37*2" max_local="37*2"/>
<string english="Positive Force" translation="Positive Force" explanation="jukebox prompt. song name should probably not be translated" max="37*2" max_local="37*2"/>
<string english="Presenting VVVVVV" translation="Presenting VVVVVV" explanation="jukebox prompt. song name should probably not be translated" max="37*2" max_local="37*2"/>
<string english="Potential for Anything" translation="Potential for Anything" explanation="jukebox prompt. song name should probably not be translated" max="37*2" max_local="37*2"/>
<string english="Predestined Fate" translation="Predestined Fate" explanation="jukebox prompt. song name should probably not be translated" max="37*2" max_local="37*2"/>
<string english="Pipe Dream" translation="Pipe Dream" explanation="jukebox prompt. song name should probably not be translated" max="37*2" max_local="37*2"/>
<string english="Popular Potpourri" translation="Popular Potpourri" explanation="jukebox prompt. song name should probably not be translated" max="37*2" max_local="37*2"/>
<string english="Pressure Cooker" translation="Pressure Cooker" explanation="jukebox prompt. song name should probably not be translated" max="37*2" max_local="37*2"/>
<string english="ecroF evitisoP" translation="ecroF evitisoP" explanation="jukebox prompt. song name should probably not be translated" max="37*2" max_local="37*2"/>
<string english="Map Settings" translation="إعدادات الغرفة" explanation="title, editor, Level Settings, map is kinda inconsistent with everything else" max="20" max_local="20"/>
<string english="edit scripts" translation="تحرير السكربتات" explanation="level editor menu option"/>
<string english="change music" translation="تغيير الموسيقى" explanation="level editor menu option"/>
<string english="editor ghosts" translation="أشباح المحرر" explanation="level editor menu option. Toggles option to show a repetition of the player&apos;s path after playtesting"/>
<string english="Editor ghost trail is OFF" translation="مسار شبح المحرر معطل" explanation="level editor. Repetition of the player&apos;s path after playtesting is disabled" max="40" max_local="40"/>
<string english="Editor ghost trail is ON" translation="مسار شبح المحرر مفعل" explanation="level editor. Repetition of the player&apos;s path after playtesting is enabled" max="40" max_local="40"/>
<string english="load level" translation="فتح المستوى" explanation="level editor menu option"/>
<string english="save level" translation="حفظ المستوى" explanation="level editor menu option"/>
<string english="quit to main menu" translation="الخروج للقائمة الرئيسية" explanation="level editor menu option" max="22" max_local="22"/>
<string english="change name" translation="تبديل الاسم" explanation="level editor menu option"/>
<string english="change author" translation="تبديل المؤلف" explanation="level editor menu option"/>
<string english="change description" translation="تبديل الوصف" explanation="level editor menu option"/>
<string english="change website" translation="تبديل موقع النت" explanation="level editor menu option"/>
<string english="change font" translation="تبديل الخطوط" explanation="level editor menu option"/>
<string english="Level Font" translation="خط نص المستوى" explanation="title, editor, font that a custom level will show up in. You can select a language name here (like Chinese or Japanese which have different fonts, or `other`)" max="20" max_local="20"/>
<string english="Select the language in which the text in this level is written." translation="اختيار اللغة التي كتب بها نص هذا المستوى." explanation="" max="38*3" max_local="38*3"/>
<string english="Font: " translation="الخط: " explanation="level options, followed by name of font (mind the space)" max="15" max_local="15"/>
<string english="Map Music" translation="موسيقى الغرفة" explanation="title, editor, music that starts playing when a level is started. Can be changed with scripting later, so this is really just initial music" max="20" max_local="20"/>
<string english="Current map music:" translation="موسيقى الغرفة الحالية:" explanation="editor, followed by the number and name of a song, or `No background music`. Can be changed with scripting later, so this is really just initial music" max="38*2" max_local="38*2"/>
<string english="No background music" translation="لا موسيقى" explanation="editor, level starts with no song playing" max="38*2" max_local="38*2"/>
<string english="1: Pushing Onwards" translation="1: Pushing Onwards" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="2: Positive Force" translation="2: Positive Force" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="3: Potential for Anything" translation="3: Potential for Anything" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="4: Passion for Exploring" translation="4: Passion for Exploring" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="N/A: Pause" translation="N/A: Pause" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="5: Presenting VVVVVV" translation="5: Presenting VVVVVV" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="N/A: Plenary" translation="N/A: Plenary" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="6: Predestined Fate" translation="6: Predestined Fate" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="N/A: ecroF evitisoP" translation="N/A: ecroF evitisoP" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="7: Popular Potpourri" translation="7: Popular Potpourri" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="8: Pipe Dream" translation="8: Pipe Dream" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="9: Pressure Cooker" translation="9: Pressure Cooker" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="10: Paced Energy" translation="10: Paced Energy" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="11: Piercing the Sky" translation="11: Piercing the Sky" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="N/A: Predestined Fate Remix" translation="N/A: Predestined Fate Remix" explanation="editor, song name should probably not be translated" max="38*2" max_local="38*2"/>
<string english="?: something else" translation="?: شيء آخر" explanation="editor, song was not recognized" max="38*2" max_local="38*2"/>
<string english="next song" translation="اللحن التالي" explanation="level editor menu option"/>
<string english="previous song" translation="اللحن السابق" explanation="level editor menu option"/>
<string english="back" translation="عودة" explanation="level editor menu option"/>
<string english="Save before quitting?" translation="هل تود الحفظ قبل الخروج؟" explanation="level editor" max="38*4" max_local="38*4"/>
<string english="yes, save and quit" translation="أجل، حفظ ثم خروج" explanation="level editor menu option"/>
<string english="no, quit without saving" translation="لا، خروج بدون حفظ" explanation="level editor menu option"/>
<string english="return to editor" translation="عودة للمحرر" explanation="level editor menu option"/>
<string english="Untitled Level" translation="مستوى بلا عنوان" explanation="" max="20" max_local="20"/>
<string english="Unknown" translation="مجهول" explanation="by Unknown [author]"/>
<string english="Tile:" translation="خلية: " explanation="editor, selected " max="34" max_local="34"/>
<string english="SCRIPT BOX: Click on the first corner" translation="هامش السكربت: انقر الزاوية الأولى للصندوق" explanation="editor, a script box is an invisible box that runs a script when touched" max="39*3" max_local="39*3"/>
<string english="SCRIPT BOX: Click on the last corner" translation="هامش السكربت: انقر الزاوية المقابلة للصندوق" explanation="editor, a script box is an invisible box that runs a script when touched" max="39*3" max_local="39*3"/>
<string english="ENEMY BOUNDS: Click on the first corner" translation="هامش الأعداء: انقر الزاوية الأولى للصندوق" explanation="editor, invisible box which enemies always stay inside of" max="39*3" max_local="39*3"/>
<string english="ENEMY BOUNDS: Click on the last corner" translation="هامش الأعداء: انقر الزاوية المقابلة للصندوق" explanation="editor, invisible box which enemies always stay inside of" max="39*3" max_local="39*3"/>
<string english="PLATFORM BOUNDS: Click on the first corner" translation="هامش المنصات: انقر الزاوية الأولى للصندوق" explanation="editor, invisible box which moving platforms always stay inside of" max="39*3" max_local="39*3"/>
<string english="PLATFORM BOUNDS: Click on the last corner" translation="هامش المنصات: انقر الزاوية المقابلة للصندوق" explanation="editor, invisible box which moving platforms always stay inside of" max="39*3" max_local="39*3"/>
<string english="Click on the first corner" translation="انقر الزاوية الأولى" explanation="" max="39*3" max_local="39*3"/>
<string english="Click on the last corner" translation="انقر الزاوية الأخيرة" explanation="" max="39*3" max_local="39*3"/>
<string english="**** VVVVVV SCRIPT EDITOR ****" translation="**** محرر سكربتات VVVVVV ****" explanation="supposed to look like a Commodore 64 screen" max="36" max_local="36"/>
<string english="PRESS ESC TO RETURN TO MENU" translation="اضغط ESC للعودة للقائمة" explanation="Commodore 64-style script editor, TO MENU is redundant" max="36" max_local="36"/>
<string english="NO SCRIPT IDS FOUND" translation="لم نجد معرف ID للسكربتات" explanation="Commodore 64-style script editor, basically NO SCRIPTS FOUND" max="36" max_local="36"/>
<string english="CREATE A SCRIPT WITH EITHER THE TERMINAL OR SCRIPT BOX TOOLS" translation="أنشئ سكربتا إما بالواجهة النصية أو بأدوات السكربتات" explanation="Commodore 64-style script editor" max="36*5" max_local="36*5"/>
<string english="CURRENT SCRIPT: {name}" translation="السكربت الحالي: {name}" explanation="Commodore 64-style script editor. Char limit is soft, but the longer this is, the more often users&quot; script names run offscreen. Consider SCRIPT: instead" max="20" max_local="20"/>
<string english="Left click to place warp destination" translation="نقرة يسرى بالفأرة لوضع وجهة التنقيل" explanation="warp token: small teleporter with entrance and destination" max="39" max_local="39"/>
<string english="Right click to cancel" translation="نقرة يمنى بالفأرة للتراجع" explanation="" max="39" max_local="39"/>
<string english="{button1} and {button2} keys change tool" translation="الأزرار {button1} و {button2} لتغيير الأداة" explanation="These keys can be used to switch between tools" max="36" max_local="36"/>
<string english="1: Walls" translation="1: جدران" explanation="editor tool. Solid tiles" max="32" max_local="32"/>
<string english="2: Backing" translation="2: خلفيات" explanation="editor tool. Non-solid background tiles" max="32" max_local="32"/>
<string english="3: Spikes" translation="3: أشواك" explanation="editor tool" max="32" max_local="32"/>
<string english="4: Trinkets" translation="4: مقتنيات" explanation="editor tool. Shiny trinkets/collectibles" max="32" max_local="32"/>
<string english="5: Checkpoints" translation="5: نقاط حفظ" explanation="editor tool. You restart at the last checkpoint after death" max="32" max_local="32"/>
<string english="6: Disappearing Platforms" translation="6: منصات تتلاشى" explanation="editor tool. Platform that disappears when you step on it (disappearing platform, crumbling platform)" max="32" max_local="32"/>
<string english="7: Conveyors" translation="7: بساط متحرك" explanation="editor tool. Conveyor belt" max="32" max_local="32"/>
<string english="8: Moving Platforms" translation="8: منصات تتحرك" explanation="editor tool. Moving platform" max="32" max_local="32"/>
<string english="9: Enemies" translation="9: أعداء" explanation="editor tool" max="32" max_local="32"/>
<string english="0: Gravity Lines" translation="0: خطوط جاذبية" explanation="editor tool. Gravity line, which flips your gravity when touching it" max="32" max_local="32"/>
<string english="R: Roomtext" translation="R: نصوص حرة" explanation="editor tool. Freely typed text, consider just Text" max="32" max_local="32"/>
<string english="T: Terminals" translation="T: حواسيب" explanation="editor tool. Computer which can be activated by the player to run a script" max="32" max_local="32"/>
<string english="Y: Script Boxes" translation="Y: هامش سكربت" explanation="editor tool. Invisible box that runs a script when touched" max="32" max_local="32"/>
<string english="U: Warp Tokens" translation="U: نائب تنقيل" explanation="editor tool. Small teleporter with entrance and destination" max="32" max_local="32"/>
<string english="I: Warp Lines" translation="I: خط تنقيل" explanation="editor tool. Makes a room edge be connected to its opposite edge" max="32" max_local="32"/>
<string english="O: Crewmates" translation="O: عضو طاقم" explanation="editor tool. Crewmate that can be rescued" max="32" max_local="32"/>
<string english="P: Start Point" translation="P: نقطة بدء" explanation="editor tool" max="32" max_local="32"/>
<string english="START" translation="بدء" explanation="start point in level editor" max="10" max_local="10"/>
<string english="SPACE ^ SHIFT ^" translation="SPACE ^ SHIFT ^" explanation="editor, indicates both SPACE key and SHIFT key open up menus. ^ is rendered as up arrow" max="32" max_local="32"/>
<string english="F1: Change Tileset" translation="F1: تبديل مجموعة الخلايا" explanation="editor shortcut, switch to different tileset" max="25" max_local="25"/>
<string english="F2: Change Colour" translation="F2: تبديل الألوان" explanation="editor shortcut, switch to different tileset color/variant" max="25" max_local="25"/>
<string english="F3: Change Enemies" translation="F3: تبديل الأعداء" explanation="editor shortcut, change enemy appearance" max="25" max_local="25"/>
<string english="F4: Enemy Bounds" translation="F4: هامش الأعداء" explanation="editor shortcut, invisible box which enemies always stay inside of" max="25" max_local="25"/>
<string english="F5: Platform Bounds" translation="F5: هامش المنصة" explanation="editor shortcut, invisible box which platforms always stay inside of" max="25" max_local="25"/>
<string english="F9: Reload Resources" translation="F9: فتح ملفات الموارد" explanation="editor shortcut, reload tilesets, sprites, music, etc" max="25" max_local="25"/>
<string english="F10: Direct Mode" translation="F10: وضع الرسم المباشر" explanation="editor shortcut, direct mode is manual tile placing mode, where walls are not automatically made nice" max="25" max_local="25"/>
<string english="W: Change Warp Dir" translation="W: تبديل اتجاه التنقيل" explanation="editor shortcut, dir=direction, change which edges of the room wrap around. Can be horizontal, vertical or all sides" max="25" max_local="25"/>
<string english="E: Change Roomname" translation="E: تبديل اسم الغرفة" explanation="editor shortcut, the name of the room appears at the bottom" max="25" max_local="25"/>
<string english="S: Save Map" translation="S: حفظ الغرفة" explanation="level editor, save level, `Map` is basically redundant" max="12" max_local="12"/>
<string english="L: Load Map" translation="L: فتح الغرفة" explanation="level editor, load level, `Map` is basically redundant" max="12" max_local="12"/>
<string english="Enter map filename to save as:" translation="اكتب مسار ملف الغرفة لحفظه:" explanation="level editor text input, save level file as" max="39*3" max_local="39*3"/>
<string english="Enter map filename to load:" translation="اكتب مسار ملف الغرفة لفتحه:" explanation="level editor text input, load level file" max="39*3" max_local="39*3"/>
<string english="Enter new room name:" translation="اكتب اسم الغرفة الجديدة:" explanation="level editor text input, the name of the room appears at the bottom" max="39*3" max_local="39*3"/>
<string english="Enter room coordinates x,y:" translation="اكتب احداثيات الغرفة س,ص:" explanation="level editor text input, go to another room by its coordinates, for example, 9,15" max="39*3" max_local="39*3"/>
<string english="Enter script name:" translation="اكتب اسم السكربت:" explanation="level editor text input, enter name of newly created script, or edit the script name of a terminal/script box" max="39*3" max_local="39*3"/>
<string english="Enter roomtext:" translation="اكتب النص:" explanation="level editor text input, enter text to place in the room" max="39*3" max_local="39*3"/>
<string english="Space Station" translation="المحطة الفضائية " explanation="editor tileset name, Now using Space Station Tileset"/>
<string english="Outside" translation="الفضاء الخارجي" explanation="editor tileset name, Now using Outside Tileset"/>
<string english="Lab" translation="المخبر" explanation="editor tileset name, Now using Lab Tileset"/>
<string english="Warp Zone" translation="منطقة التنقيل" explanation="editor tileset name, Now using Warp Zone Tileset. The Warp Zone got its name because its rooms wrap around, but think Star Trek"/>
<string english="Ship" translation="السفينة" explanation="editor tileset name, Now using Ship Tileset. Spaceship"/>
<string english="Now using {area} Tileset" translation="سنغير لمجموعة الخلايا
من {area}" explanation="level editor, user changed the tileset of the room to {area} (like Ship, Lab, etc)" max="38*3" max_local="38*3"/>
<string english="Tileset Colour Changed" translation="تغير لون مجموعة الخلايا" explanation="level editor, user changed the tileset colour/variant of the room" max="38*3" max_local="38*3"/>
<string english="Enemy Type Changed" translation="تغير نوع الأعداء" explanation="level editor, user changed enemy appearance for the room" max="38*3" max_local="38*3"/>
<string english="Platform speed is now {speed}" translation="تغيرت سرعة المنصات إلى {speed}" explanation="level editor, user changed speed of platforms for the room" max="38*3" max_local="38*3"/>
<string english="Enemy speed is now {speed}" translation="تغيرت سرعة الأعداء إلى {speed}" explanation="level editor, user changed speed of enemies for the room" max="38*3" max_local="38*3"/>
<string english="Reloaded resources" translation="أعيد فتح ملفات الموارد" explanation="level editor, reloaded graphics assets/resources, music and sound effects" max="38*3" max_local="38*3"/>
<string english="ERROR: Invalid format" translation="خطأ: صيغة المكتوب غير مناسبة" explanation="user was supposed to enter something like `12,12`, but entered `as@df`" max="38*3" max_local="38*3"/>
<string english="Loaded map: {filename}.vvvvvv" translation="فتحت الغرفة: {filename}.vvvvvv" explanation="successfully loaded level file" max="38*3" max_local="38*3"/>
<string english="Saved map: {filename}.vvvvvv" translation="حفظت الغرفة: {filename}.vvvvvv" explanation="successfully saved level file" max="38*3" max_local="38*3"/>
<string english="ERROR: Could not load level" translation="خطأ: فشل فتح الغرفة" explanation="that level could not be loaded, maybe it does not exist" max="38*3" max_local="38*3"/>
<string english="ERROR: Could not save level!" translation="خطأ: فشل حفظ الغرفة!!" explanation="maybe the filename is too long? exclamation mark because you will lose your data if you quit" max="38*3" max_local="38*3"/>
<string english="Mapsize is now [{width},{height}]" translation="أبعاد الغرفة الآن [{width},{height}]" explanation="editor, the map is now {width} by {height}" max="38*3" max_local="38*3"/>
<string english="Direct Mode Disabled" translation="وضع الرسم المباشر معطل" explanation="editor, manual tile placing mode where walls are not automatically made nice, now changed to automatic mode" max="38*3" max_local="38*3"/>
<string english="Direct Mode Enabled" translation="وضع الرسم المباشر مفعل" explanation="editor, manual tile placing mode where walls are not automatically made nice, now changed to manual mode" max="38*3" max_local="38*3"/>
<string english="ERROR: Warp lines must be on edges" translation="خطأ: خطوط التنقيل ترسم على حواف الغرفة" explanation="level editor, warp lines make a room edge be connected to its opposite edge. So they must be placed on one of the edges, not in the middle" max="38*3" max_local="38*3"/>
<string english="Room warps in all directions" translation="الغرفة تلتف على نفسها من كل الاتجاهات" explanation="level editor, all edges of the room are connected with each other. If the player leaves the room they end up on the other side of the same room." max="38*3" max_local="38*3"/>
<string english="Room warps horizontally" translation="الغرفة تلتف على نفسها أفقيا" explanation="level editor, the left and right edges of the room are connected with each other. The player can still go to other rooms on the top and bottom" max="38*3" max_local="38*3"/>
<string english="Room warps vertically" translation="الغرفة تلتف على نفسها عموديا" explanation="level editor, the top and bottom edges of the room are connected with each other. The player can still go to other rooms on the left and right" max="38*3" max_local="38*3"/>
<string english="Room warping disabled" translation="الغرفة لا تلتف على نفسها" explanation="level editor, no edges are connected and the player can go to other rooms" max="38*3" max_local="38*3"/>
<string english="ERROR: No checkpoint to spawn at" translation="خطأ: لا نقطة حفظ يبعث اللاعب منها" explanation="we cannot playtest because there is no checkpoint in this room that the player could start (be spawned) at" max="38*3" max_local="38*3"/>
<string english="ERROR: Max number of trinkets is 100" translation="خطأ: أقصى عدد للمقتنيات 100" explanation="editor, user tried to place another trinket" max="38*3" max_local="38*3"/>
<string english="ERROR: Max number of crewmates is 100" translation="خطأ: أقصى عدد لأفراد الطاقم 100" explanation="editor, user tried to place another crewmate" max="38*3" max_local="38*3"/>
<string english="Level quits to menu" translation="يغادر المستوى إلى القائمة" explanation="editor message, user would have been forcefully returned to title screen but wasn&apos;t" max="38*3" max_local="38*3"/>
<string english="Level completed" translation="يختم المستوى" explanation="editor message, user would have been returned to levels list but wasn&apos;t" max="38*3" max_local="38*3"/>
<string english="Rolled credits" translation="تظهر شاشة فريق العمل" explanation="editor message, credits would have been shown but weren&apos;t" max="38*3" max_local="38*3"/>
<string english="Time trial completed" translation="تظهر شاشة ختم التحدي ضد الساعة" explanation="editor message, time trial complete screen would have been shown but wasn&apos;t" max="38*3" max_local="38*3"/>
<string english="{hrs}:{min|digits=2}:{sec|digits=2}" translation="{hrs}:{min|digits=2}:{sec|digits=2}" explanation="time format H:MM:SS"/>
<string english="{hrs}:{min|digits=2}:{sec|digits=2}.{cen|digits=2}" translation="{hrs}:{min|digits=2}:{sec|digits=2}.{cen|digits=2}" explanation="time format H:MM:SS.CC"/>
<string english="{min}:{sec|digits=2}" translation="{min}:{sec|digits=2}" explanation="time format M:SS"/>
<string english="{min}:{sec|digits=2}.{cen|digits=2}" translation="{min}:{sec|digits=2}.{cen|digits=2}" explanation="time format M:SS.CC"/>
<string english="{sec}.{cen|digits=2}" translation="{sec}.{cen|digits=2}" explanation="time format S.CC"/>
<string english=".99" translation=".99" explanation="appended to time format for 99/100 seconds (example: 1:15.99). Time trial results"/>
<string english="{area}, {time}" translation="{area} / {time}" explanation="saved game summary, e.g. `Space Station, 12:30:59`"/>
<string english="Level Complete!" translation="ختمت المستوى!" explanation="Might be tight, the exclamation mark may be removed" max="18" max_local="18"/>
<string english="Game Complete!" translation="ختمت اللعبة!" explanation="Might be tight, the exclamation mark may be removed" max="18" max_local="18"/>
<string english="You have rescued a crew member!" translation="أنقذت أحد أفراد طاقمك!" explanation="If you need to manually wordwrap: please ensure this has exactly two lines. Ignore the (font-adapted) maximum if it says 1 line." max="30*2" max_local="30*2"/>
<string english="All Crew Members Rescued!" translation="أنقذت جميع طاقمك!" explanation="" max="32" max_local="32"/>
<string english="All crewmates rescued!" translation="أنقذت جميع طاقمك!" explanation="" max="32" max_local="32"/>
<string english="Game Saved" translation="حفظت اللعبة" explanation="" max="30" max_local="30"/>
<string english="Press arrow keys or WASD to move" translation="تضغط الأسهم أو WASD للحركة" explanation="" max="32*2" max_local="32*2"/>
<string english="Press left/right to move" translation="نضغط يسار/يمين للحركة" explanation="" max="32*2" max_local="32*2"/>
<string english="Press {button} to flip" translation="نضغط {button} للعكس" explanation="expect `ACTION`" max="32*3" max_local="32*3"/>
<string english="Press {button} to view map and quicksave" translation="نضغط {button} لرؤية الخريطة والتخزين السريع" explanation="" max="32*3" max_local="32*3"/>
<string english="If you prefer, you can press UP or DOWN instead of ACTION to flip." translation="لو شئت، يمكنك للعكس ضغط فوق/تحت عوضا عن زر الفعل." explanation="" max="34*3" max_local="34*3"/>
<string english="Help! Can anyone hear this message?" translation="النجدة!! هل من سميع لهذه الرسالة؟" explanation="Violet speaking via Comms Relay" max="25*4" max_local="25*4"/>
<string english="Verdigris? Are you out there? Are you ok?" translation="فارديغري؟ أهذا أنت في الخارج؟ هل صحتك بخير؟" explanation="Violet speaking via Comms Relay" max="25*4" max_local="25*4"/>
<string english="Please help us! We&apos;ve crashed and need assistance!" translation="رجاء أنجدونا! سقطت سفينتنا ونحتاج مدد الدعم!" explanation="Violet speaking via Comms Relay" max="25*4" max_local="25*4"/>
<string english="Hello? Anyone out there?" translation="ألو، ألو؟ هل من أحد يسمعني؟" explanation="Violet speaking via Comms Relay" max="25*4" max_local="25*4"/>
<string english="This is Doctor Violet from the D.S.S. Souleye! Please respond!" translation="هنا الدكتورة فايولت، من سفينة D.S.S. سولاي! رجاء أجيبوني!" explanation="Violet speaking via Comms Relay" max="25*4" max_local="25*4"/>
<string english="Please... Anyone..." translation="ليت أحدا يرد... أترجاكم..." explanation="Violet speaking via Comms Relay" max="25*4" max_local="25*4"/>
<string english="Please be alright, everyone..." translation="أن تعودوا جميعا... سالمين..." explanation="Violet speaking via Comms Relay" max="25*4" max_local="25*4"/>
<string english="Congratulations!
You have found a shiny trinket!" translation="تهانينا!
وجدت تذكارا لماعا!" explanation="" max="34*4" max_local="34*4"/>
<string english="Congratulations!
You have found a lost crewmate!" translation="تهانينا!
وجدت أحد أفراد الطاقم التائهين!" explanation="" max="34*4" max_local="34*4"/>
<string english="Congratulations!
You have found the secret lab!" translation="تهانينا!
وجدت المخبر السري!" explanation="" max="34*4" max_local="34*4"/>
<string english="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." translation="المخبر السري جزء معزول عن بقية اللعبة. يمكنك العودة متى شئت بخيار مخصص له سيضاف في قائمة اللعب." explanation="" max="36*10" max_local="36*10"/>
<string english="Viridian" translation="فيريديان" explanation="crewmate name (player)" max="15" max_local="15"/>
<string english="Violet" translation="فايوليت" explanation="crewmate name" max="15" max_local="15"/>
<string english="Vitellary" translation="فيتيلاري" case="0" explanation="crewmate name" max="15" max_local="15"/>
<string english="Vermilion" translation="فارميليون" case="0" explanation="crewmate name" max="15" max_local="15"/>
<string english="Verdigris" translation="فارديغري" case="0" explanation="crewmate name" max="15" max_local="15"/>
<string english="Victoria" translation="فيكتوريا" case="0" explanation="crewmate name" max="15" max_local="15"/>
<string english="Vitellary" translation="فيتيلاري" case="1" explanation="crewmate name as menu option: Who do you want to play the level with?"/>
<string english="Vermilion" translation="فارميليون" case="1" explanation="crewmate name as menu option: Who do you want to play the level with?"/>
<string english="Verdigris" translation="فارديغري" case="1" explanation="crewmate name as menu option: Who do you want to play the level with?"/>
<string english="Victoria" translation="فيكتوريا" case="1" explanation="crewmate name as menu option: Who do you want to play the level with?"/>
<string english="Starring" translation="بطولة" explanation="credits roll. Starring the following 6 crew members" max="20" max_local="20"/>
<string english="Captain Viridian" translation="القبطان فيريديان" explanation="credits roll. Starring the following 6 crew members" max="27" max_local="27"/>
<string english="Doctor Violet" translation="الدكتورة فايوليت" explanation="credits roll. Starring the following 6 crew members" max="27" max_local="27"/>
<string english="Professor Vitellary" translation="الأستاذ فيتيلاري" explanation="credits roll. Starring the following 6 crew members" max="27" max_local="27"/>
<string english="Officer Vermilion" translation="النقيب فارميليون" explanation="credits roll. Starring the following 6 crew members" max="27" max_local="27"/>
<string english="Chief Verdigris" translation="القائد فارديغري" explanation="credits roll. Starring the following 6 crew members" max="27" max_local="27"/>
<string english="Doctor Victoria" translation="الدكتورة فيكتوريا" explanation="credits roll. Starring the following 6 crew members" max="27" max_local="27"/>
<string english="When you&apos;re standing on the floor, Vitellary will try to walk to you." translation="أثناء وقوفك على الأرضية، سيحاول فيتيلاري المشي نحوك." explanation="Intermission 1" max="34*4" max_local="34*4"/>
<string english="When you&apos;re standing on the floor, Vermilion will try to walk to you." translation="أثناء وقوفك على الأرضية، سيحاول فارميليون المشي نحوك." explanation="Intermission 1" max="34*4" max_local="34*4"/>
<string english="When you&apos;re standing on the floor, Verdigris will try to walk to you." translation="أثناء وقوفك على الأرضية، سيحاول فارديغري المشي نحوك." explanation="Intermission 1" max="34*4" max_local="34*4"/>
<string english="When you&apos;re standing on the floor, Victoria will try to walk to you." translation="أثناء وقوفك على الأرضية، ستحاول فيكتوريا المشي نحوك." explanation="Intermission 1" max="34*4" max_local="34*4"/>
<string english="When you&apos;re standing on the floor, your companion will try to walk to you." translation="أثناء وقوفك على الأرضية، سيحاول مرافقك أيا كان المشي نحوك." explanation="Intermission 1, unknown companion, normally impossible but reproducible in custom levels" max="34*4" max_local="34*4"/>
<string english="When you&apos;re standing on the ceiling, Vitellary will try to walk to you." translation="أثناء وقوفك على السقف، سيحاول فيتيلاري المشي نحوك." explanation="Intermission 1 in flip mode" max="34*4" max_local="34*4"/>
<string english="When you&apos;re standing on the ceiling, Vermilion will try to walk to you." translation="أثناء وقوفك على السقف، سيحاول فارميليون المشي نحوك." explanation="Intermission 1 in flip mode" max="34*4" max_local="34*4"/>
<string english="When you&apos;re standing on the ceiling, Verdigris will try to walk to you." translation="أثناء وقوفك على السقف، سيحاول فارديغري المشي نحوك." explanation="Intermission 1 in flip mode" max="34*4" max_local="34*4"/>
<string english="When you&apos;re standing on the ceiling, Victoria will try to walk to you." translation="أثناء وقوفك على السقف، ستحاول فيكتوريا المشي نحوك." explanation="Intermission 1 in flip mode" max="34*4" max_local="34*4"/>
<string english="When you&apos;re standing on the ceiling, your companion will try to walk to you." translation="أثناء وقوفك على السقف، سيحاول مرافقك أيا كان المشي نحوك." explanation="Intermission 1 in flip mode, unknown companion, normally impossible but reproducible in custom levels" max="34*4" max_local="34*4"/>
<string english="When you&apos;re NOT standing on the floor, Vitellary will stop and wait for you." translation="إن كفيت عن ملامسة الأرضية، سيتوقف فيتيلاري وينتظرك." explanation="Intermission 1" max="34*4" max_local="34*4"/>
<string english="When you&apos;re NOT standing on the floor, Vermilion will stop and wait for you." translation="إن كفيت عن ملامسة الأرضية، سيتوقف فارميليون وينتظرك." explanation="Intermission 1" max="34*4" max_local="34*4"/>
<string english="When you&apos;re NOT standing on the floor, Verdigris will stop and wait for you." translation="إن كفيت عن ملامسة الأرضية، سيتوقف فارديغري وينتظرك." explanation="Intermission 1" max="34*4" max_local="34*4"/>
<string english="When you&apos;re NOT standing on the floor, Victoria will stop and wait for you." translation="إن كفيت عن ملامسة الأرضية، ستتوقف فيكتوريا وتنتظرك." explanation="Intermission 1" max="34*4" max_local="34*4"/>
<string english="When you&apos;re NOT standing on the floor, your companion will stop and wait for you." translation="إن كفيت عن ملامسة الأرضية، سيتوقف مرافقك أيا كان وينتظرك." explanation="Intermission 1, unknown companion, normally impossible but reproducible in custom levels" max="34*4" max_local="34*4"/>
<string english="When you&apos;re NOT standing on the ceiling, Vitellary will stop and wait for you." translation="إن كفيت عن ملامسة السقف، سيتوقف فيتيلاري وينتظرك." explanation="Intermission 1 in flip mode" max="34*4" max_local="34*4"/>
<string english="When you&apos;re NOT standing on the ceiling, Vermilion will stop and wait for you." translation="إن كفيت عن ملامسة السقف، سيتوقف فارميليون وينتظرك." explanation="Intermission 1 in flip mode" max="34*4" max_local="34*4"/>
<string english="When you&apos;re NOT standing on the ceiling, Verdigris will stop and wait for you." translation="إن كفيت عن ملامسة السقف، سيتوقف فارديغري وينتظرك." explanation="Intermission 1 in flip mode" max="34*4" max_local="34*4"/>
<string english="When you&apos;re NOT standing on the ceiling, Victoria will stop and wait for you." translation="إن كفيت عن ملامسة السقف، ستتوقف فيكتوريا وتنتظرك." explanation="Intermission 1 in flip mode" max="34*4" max_local="34*4"/>
<string english="When you&apos;re NOT standing on the ceiling, your companion will stop and wait for you." translation="إن كفيت عن ملامسة السقف، سيتوقف مرافقك أيا كان وينتظرك." explanation="Intermission 1 in flip mode, unknown companion, normally impossible but reproducible in custom levels" max="34*4" max_local="34*4"/>
<string english="You can&apos;t continue to the next room until he is safely across." translation="لا يمكنك المواصلة للغرفة القادمة قبل أن يعبر سالما." explanation="Intermission 1" max="34*4" max_local="34*4"/>
<string english="You can&apos;t continue to the next room until she is safely across." translation="لا يمكنك المواصلة للغرفة القادمة قبل أن تعبر سالمة." explanation="Intermission 1" max="34*4" max_local="34*4"/>
<string english="You can&apos;t continue to the next room until they are safely across." translation="لا يمكنك المواصلة للغرفة القادمة قبل أن تعبرا سالمين." explanation="Intermission 1, unknown companion, normally impossible but reproducible in custom levels" max="34*4" max_local="34*4"/>
<string english="Survive for" translation="سأحاول الصمود" explanation="Gravitron. Line 1/2: Survive for 60 seconds!" max="20" max_local="20"/>
<string english="60 seconds!" translation="لمدة 60 ثانية!" explanation="Gravitron. Line 2/2: Survive for 60 seconds!" max="20" max_local="20"/>
<string english="Thanks for" translation="شكرا على" explanation="credits. Line 1/2: Thanks for playing!" max="20" max_local="20"/>
<string english="playing!" translation="اللعب!" explanation="credits. Line 2/2: Thanks for playing!" max="20" max_local="20"/>
<string english="SPACE STATION 1 MASTERED" translation="احترفت المحطة الفضائية 1" explanation="achievement/trophy title" max="38*2" max_local="38*2"/>
<string english="LABORATORY MASTERED" translation="احترفت المخبر" explanation="achievement/trophy title - V rank in time trial" max="38*2" max_local="38*2"/>
<string english="THE TOWER MASTERED" translation="احترفت البرج" explanation="achievement/trophy title - V rank in time trial" max="38*2" max_local="38*2"/>
<string english="SPACE STATION 2 MASTERED" translation="احترفت المحطة الفضائية 2" explanation="achievement/trophy title - V rank in time trial" max="38*2" max_local="38*2"/>
<string english="WARP ZONE MASTERED" translation="احترفت منطقة التنقيل" explanation="achievement/trophy title - V rank in time trial" max="38*2" max_local="38*2"/>
<string english="FINAL LEVEL MASTERED" translation="احترفت المستوى الأخير" explanation="achievement/trophy title - V rank in time trial" max="38*2" max_local="38*2"/>
<string english="Obtain a V Rank in this Time Trial" translation="حصلت على رتبة V في هذا التحدي ضد الساعة." explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="GAME COMPLETE" translation="ختم اللعبة" explanation="achievement/trophy title" max="38*2" max_local="38*2"/>
<string english="Complete the game" translation="ختمت اللعبة" case="1" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="FLIP MODE COMPLETE" translation="ختم الوضع المعكوس" explanation="achievement/trophy title" max="38*2" max_local="38*2"/>
<string english="Complete the game in flip mode" translation="ختمت اللعبة في الوضع المعكوس" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Win with less than 50 deaths" translation="ختمت اللعبة بأقل من 50 ميتة" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Win with less than 100 deaths" translation="ختمت اللعبة بأقل من 100 ميتة" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Win with less than 250 deaths" translation="ختمت اللعبة بأقل من 250 ميتة" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Win with less than 500 deaths" translation="ختمت اللعبة بأقل من 500 ميتة" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Last 5 seconds on the Super Gravitron" translation="صمدت 5 ثوان في أتون الجاذبية الخارق" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Last 10 seconds on the Super Gravitron" translation="صمدت 10 ثوان في أتون الجاذبية الخارق" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Last 15 seconds on the Super Gravitron" translation="صمدت 15 ثانية في أتون الجاذبية الخارق" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Last 20 seconds on the Super Gravitron" translation="صمدت 20 ثانية في أتون الجاذبية الخارق" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Last 30 seconds on the Super Gravitron" translation="صمدت 30 ثانية في أتون الجاذبية الخارق" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Last 1 minute on the Super Gravitron" translation="صمدت دقيقة في أتون الجاذبية الخارق" explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="MASTER OF THE UNIVERSE" translation="المحترف الكوني" explanation="achievement/trophy title - no death mode complete" max="38*2" max_local="38*2"/>
<string english="Complete the game in no death mode" translation="ختمت اللعبة في تحدي بدون الموت." explanation="achievement/trophy description" max="38*2" max_local="38*2"/>
<string english="Something went wrong, but we forgot the error message." translation="حصلت مشكلة، لكن نسينا كتابة رسالة خطأ مناسبة." explanation="the message that is printed in case the game detects an error but there&apos;s no error message set" max="38*6" max_local="38*6"/>
<string english="Could not mount {path}: real directory doesn&apos;t exist" translation="فشل ربط المسار {path}: لا وجود للمجلد الحقيقي" explanation="mount: link/attach a directory (folder) in the filesystem into the game&apos;s filesystem so we can access it" max="38*6" max_local="38*6"/>
<string english="Level {path} not found" translation="لا وجود للمرحلة {path}" explanation="" max="38*6" max_local="38*6"/>
<string english="Error parsing {path}: {error}" translation="فشل فتح {path}: {error}" explanation="we tried to parse the level file, but failed" max="38*6" max_local="38*6"/>
<string english="{filename} dimensions not exact multiples of {width} by {height}!" translation="الأبعاد في {filename} ليست مضاعفات صحيحة لقيم العرض {width} والارتفاع {height}!" explanation="filename is something like tiles.png, tiles2.png, etc. and width/height are something like 8, 32, etc.; this is used if the dimensions of a graphics file aren&apos;t an exact multiple of the given size (e.g. 8x8, 32x32, etc.)" max="38*6" max_local="38*6"/>
<string english="ERROR: Could not write to language folder! Make sure there is no &quot;lang&quot; folder next to the regular saves." translation="خطأ: فشلت الكتابة نحو مجلد اللغة! احرص أن لا مجلد &quot;lang&quot; بالقرب من التخزينات العادية" explanation="" max="38*5" max_local="38*5"/>
<string english="Localisation" translation="التوطين" explanation="" max="20" max_local="20"/>
<string english="Localisation Project Led by" translation="أدار مشروع التوطين" explanation="" max="40" max_local="40"/>
<string english="Translations by" translation="عمل على الترجمات" explanation=""/>
<string english="Translators" translation="المترجمون" explanation="" max="20" max_local="20"/>
<string english="Pan-European Font Design by" translation="تصميم الخط الأوروبي من" explanation="" max="40" max_local="40"/>
<string english="Fonts by" translation="الخطوط من" explanation=""/>
<string english="Other Fonts by" translation="الخطوط الأخرى من" explanation=""/>
<string english="Editing and LQA" translation="التحرير وضمان الجودة" explanation=""/>
<string english="Arabic" translation="العربية" explanation=""/>
<string english="Catalan" translation="الكاتالونية" explanation=""/>
<string english="Welsh" translation="الويلزية" explanation=""/>
<string english="German" translation="الألمانية" explanation=""/>
<string english="Esperanto" translation="الإسبرانتو" explanation=""/>
<string english="Spanish" translation="الإسبانية" explanation=""/>
<string english="French" translation="الفرنسية" explanation=""/>
<string english="Irish" translation="الإيرلندية" explanation=""/>
<string english="Italian" translation="الإيطالية" explanation=""/>
<string english="Japanese" translation="اليابانية" explanation=""/>
<string english="Korean" translation="الكورية" explanation=""/>
<string english="Dutch" translation="الهولندية" explanation=""/>
<string english="Polish" translation="البولونية" explanation=""/>
<string english="Brazilian Portuguese" translation="البرتغالية البرازيلية" explanation=""/>
<string english="European Portuguese" translation="البرتغالية الأوروبية" explanation=""/>
<string english="Russian" translation="الروسية" explanation=""/>
<string english="Silesian" translation="السيليسية" explanation=""/>
<string english="Turkish" translation="التركية" explanation=""/>
<string english="Ukrainian" translation="الأوكرانية" explanation=""/>
<string english="Chinese (Simplified)" translation="الصينية المبسطة" explanation=""/>
<string english="Chinese (Traditional)" translation="الصينية التقليدية" explanation=""/>
<string english="Spanish (ES)" translation="الإسبانية الأوروبية" explanation=""/>
<string english="Spanish (LATAM)" translation="الإسبانية الأمريكية" explanation=""/>
<string english="Spanish (ARG.)" translation="الإسبانية الأرجنتينية" explanation=""/>
<string english="Persian" translation="الفارسية" explanation=""/>
<string english="" translation="" explanation=""/>
<string english="" translation="" explanation=""/>
</strings>

View file

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Please read README.txt for information about the language files -->
<strings_plural max_local_for="8x10">
<string english_plural="You rescued {n_crew|wordy} crewmates" english_singular="You rescued {n_crew|wordy} crewmate" explanation="These two strings are displayed underneath each other (1/2)" max="40" var="n_crew" expect="6" max_local="40">
<translation form="0" translation="لم ننقذ أي فرد من الطاقم"/>
<translation form="1" translation="أنقذت فردا من الطاقم"/>
<translation form="2" translation="أنقذت فردين من الطاقم"/>
<translation form="3" translation="أنقذت {n_crew|wordy} أفراد من الطاقم"/>
<translation form="11" translation="أنقذت {n_crew|wordy} فرد من الطاقم"/>
</string>
<string english_plural="and found {n_trinkets|wordy} trinkets." english_singular="and found {n_trinkets|wordy} trinket." explanation="These two strings are displayed underneath each other (2/2)" max="38*2" var="n_trinkets" expect="20" max_local="38*2">
<translation form="0" translation="ولم نجد أي تذكار."/>
<translation form="1" translation="ووجدت تذكارا."/>
<translation form="2" translation="ووجدت تذكارين."/>
<translation form="3" translation="ووجدت {n_trinkets|wordy} تذكارات."/>
<translation form="11" translation="ووجدت {n_trinkets|wordy} تذكارا."/>
</string>
<string english_plural="And you found {n_trinkets|wordy} trinkets." english_singular="And you found {n_trinkets|wordy} trinket." explanation="You rescued all the crewmates! And you found XX trinket(s)." max="38*3" var="n_trinkets" expect="20" max_local="38*3">
<translation form="0" translation="ولم نجد أي تذكار."/>
<translation form="1" translation="ووجدت تذكارا."/>
<translation form="2" translation="ووجدت تذكارين."/>
<translation form="3" translation="ووجدت {n_trinkets|wordy} تذكارات."/>
<translation form="11" translation="ووجدت {n_trinkets|wordy} تذكارا."/>
</string>
<string english_plural="{n_crew|wordy} crewmates remain" english_singular="{n_crew|wordy} crewmate remains" explanation="Reminder: you can add |upper for an uppercase letter." max="38*2" var="n_crew" expect="100" max_local="38*2">
<translation form="0" translation="لم يبق أي فرد من الطاقم"/>
<translation form="1" translation="بقي فرد من الطاقم"/>
<translation form="2" translation="بقي فردان من الطاقم"/>
<translation form="3" translation="بقي {n_crew|wordy} أفراد من الطاقم"/>
<translation form="11" translation="بقي {n_crew|wordy} فردا من الطاقم"/>
</string>
<string english_plural="{n_crew|wordy} remain" english_singular="{n_crew|wordy} remains" explanation="You have rescued a crew member! XX remain" max="32*2" var="n_crew" expect="100" max_local="32*2">
<translation form="0" translation="لم يبق أي فرد من الطاقم"/>
<translation form="1" translation="بقي فرد من الطاقم"/>
<translation form="2" translation="بقي فردان من الطاقم"/>
<translation form="3" translation="بقي {n_crew|wordy} أفراد من الطاقم"/>
<translation form="11" translation="بقي {n_crew|wordy} فردا من الطاقم"/>
</string>
<string english_plural="Hardest Room (with {n_deaths} deaths)" english_singular="Hardest Room (with {n_deaths} death)" explanation="game complete screen" max="40" var="n_deaths" expect="1000" max_local="40">
<translation form="0" translation="أصعب غرفة (بدون ميتات)"/>
<translation form="1" translation="أصعب غرفة (ميتة واحدة)"/>
<translation form="2" translation="أصعب غرفة (ميتتان)"/>
<translation form="3" translation="أصعب غرفة ({n_deaths|wordy2} ميتات)"/>
<translation form="11" translation="أصعب غرفة ({n_deaths|wordy2} ميتة)"/>
</string>
<string english_plural="{n} normal room names untranslated" english_singular="{n} normal room name untranslated" explanation="per-area counts for room name translator mode" max="38*4" var="n" expect="48" max_local="38*4">
<translation form="0" translation="لم يبق أي اسم غرفة لم يترجم"/>
<translation form="1" translation="اسم غرفة عادي لم يترجم"/>
<translation form="2" translation="اسما غرفتين عادينين لم يترجما"/>
<translation form="3" translation="لم يترجم {n|wordy} أسماء لغرف عادية"/>
<translation form="11" translation="لم يترجم {n|wordy} اسم لغرف عادية"/>
</string>
</strings_plural>

View file

@ -0,0 +1,949 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Please read README.txt for information about the language files -->
<cutscenes>
<cutscene id="intro" explanation="">
<dialogue speaker="cyan" english="Uh oh..." translation="Ups..."/>
<dialogue speaker="purple" english="Is everything ok?" translation="Va tot bé?"/>
<dialogue speaker="cyan" english="No! We&apos;ve hit some kind of interference..." translation="No! Ha aparegut alguna mena dinterferència..."/>
<dialogue speaker="cyan" english="Something&apos;s wrong! We&apos;re going to crash!" translation="Alguna cosa no va bé! Ens estavellarem!"/>
<dialogue speaker="cyan" english="Evacuate!" translation="Evacuació!"/>
<dialogue speaker="blue" english="Oh no!" translation="Oh, no!"/>
<dialogue speaker="red" english="Everyone off the ship!" translation="Tothom fora de la nau!"/>
<dialogue speaker="yellow" english="This shouldn&apos;t be happening!" translation="Això no hauria de passar!"/>
<dialogue speaker="cyan" english="Phew! That was scary!" translation="Bufa! Quina por!"/>
<dialogue speaker="cyan" english="At least we all escaped, right guys?" translation="Almenys ens nhem pogut escapar tots, oi, nois?"/>
<dialogue speaker="cyan" english="...guys?" translation="Nois...?"/>
</cutscene>
<cutscene id="firststeps" explanation="">
<dialogue speaker="cyan" english="I wonder why the ship teleported me here alone?" translation="Per què la nau només mha teletransportat aquí a mi?"/>
<dialogue speaker="cyan" english="I hope everyone else got out ok..." translation="Espero que tothom nhagi|sortit il·lès..."/>
</cutscene>
<cutscene id="communicationstation" explanation="">
<dialogue speaker="cyan" english="Violet! Is that you?" translation="Violeta! Ets tu?"/>
<dialogue speaker="purple" english="Captain! You&apos;re ok!" translation="Cap! Estàs bé!"/>
<dialogue speaker="purple" english="Something has gone horribly wrong with the ship&apos;s teleporter!" translation="Ha passat alguna cosa terrible amb el teletransportador de la nau!"/>
<dialogue speaker="purple" english="I think everyone has been teleported away randomly! They could be anywhere!" translation="Em sembla que ha teletransportat tothom a llocs aleatoris! Podrien ser en qualsevol lloc!"/>
<dialogue speaker="cyan" english="Oh no!" translation="Ostres, no!"/>
<dialogue speaker="purple" english="I&apos;m on the ship - it&apos;s damaged badly, but it&apos;s still intact!" translation="Jo sóc a la nau. Està molt malmesa,|però encara està sencera!"/>
<dialogue speaker="purple" english="Where are you, Captain?" translation="Tu on ets, cap?"/>
<dialogue speaker="cyan" english="I&apos;m on some sort of space station... It seems pretty modern..." translation="En alguna mena destació espacial...|Sembla força moderna..."/>
<dialogue speaker="purple" english="There seems to be some sort of interference in this dimension..." translation="Sembla que en aquesta dimensió hi ha alguna mena dinterferència..."/>
<dialogue speaker="purple" english="I&apos;m broadcasting the coordinates of the ship to you now." translation="Ara tenviaré les coordenades de la nau."/>
<dialogue speaker="purple" english="I can&apos;t teleport you back, but..." translation="No et puc teletransportar fins aquí, però..."/>
<dialogue speaker="purple" english="If YOU can find a teleporter anywhere nearby, you should be able to teleport back to me!" translation="Si aconsegueixes trobar tu un teletransportador en algun lloc a prop don ets, hauries de poder-te teletransportar fins a la nau!"/>
<dialogue speaker="cyan" english="Ok! I&apos;ll try to find one!" translation="Entesos! Miraré de trobar-ne un!"/>
<dialogue speaker="purple" english="Good luck, Captain!" translation="Bona sort, cap!"/>
<dialogue speaker="purple" english="I&apos;ll keep trying to find the rest of the crew..." translation="Jo continuaré mirant de contactar amb la resta de la tripulació..."/>
</cutscene>
<cutscene id="trenchwarfare" explanation="player finds Trench Warfare trinket, if no trinkets found yet">
<dialogue speaker="cyan" english="Ohh! I wonder what that is?" translation="Oooh! Què deu ser, aquella cosa?"/>
<dialogue speaker="cyan" english="I probably don&apos;t really need it, but it might be nice to take it back to the ship to study..." translation="Suposo que no la necessito, però potser estaria bé dur-la a la nau per a estudiar-la..."/>
</cutscene>
<cutscene id="newtrenchwarfare" explanation="player finds Trench Warfare trinket, if other trinket already found">
<dialogue speaker="cyan" english="Oh! It&apos;s another one of those shiny things!" translation="Oh! Una altra cosa brillant daquelles!"/>
<dialogue speaker="cyan" english="I probably don&apos;t really need it, but it might be nice to take it back to the ship to study..." translation="Suposo que no la necessito, però potser estaria bé dur-la a la nau per a estudiar-la..."/>
</cutscene>
<cutscene id="teleporterback" explanation="">
<dialogue speaker="cyan" english="A teleporter!" translation="Un teletransportador!"/>
<dialogue speaker="cyan" english="I can get back to the ship with this!" translation="Amb això puc tornar a la nau!"/>
</cutscene>
<cutscene id="levelonecomplete_ending" explanation="">
<dialogue speaker="purple" english="Captain!" translation="Cap!"/>
</cutscene>
<cutscene id="bigopenworld" explanation="">
<dialogue speaker="player" english="So, Doctor - have you any idea what caused the crash?" translation="I doncs, doctora... Tens cap idea de què ha fet que ens estavelléssim?"/>
<dialogue speaker="purple" english="There&apos;s some sort of bizarre signal here that&apos;s interfering with our equipment..." translation="Hi ha alguna mena de senyal estrany|que provoca interferències als nostres sistemes..."/>
<dialogue speaker="purple" english="It caused the ship to lose its quantum position, collapsing us into this dimension!" translation="Ha fet que la nau perdés la posició quàntica i ens ha endinsat en aquesta dimensió!"/>
<dialogue speaker="player" english="Oh no!" translation="Ostres, no!"/>
<dialogue speaker="purple" english="But I think we should be able to fix the ship and get out of here..." translation="Però em sembla que hauríem de poder reparar la nau i sortir daquí..."/>
<dialogue speaker="purple" english="... as long as we can find the rest of the crew." translation="Sempre que trobem la resta de la tripulació, és clar..."/>
<dialogue speaker="purple" english="We really don&apos;t know anything about this place..." translation="No en sabem absolutament res, daquest lloc..."/>
<dialogue speaker="purple" english="Our friends could be anywhere - they could be lost, or in danger!" translation="Els nostres amics podrien ser en qualsevol racó. I podrien estar perduts o en perill!"/>
<dialogue speaker="player" english="Can they teleport back here?" translation="Es poden teletransportar fins aquí?"/>
<dialogue speaker="purple" english="Not unless they find some way to communicate with us!" translation="Si no troben cap manera de comunicar-se amb nosaltres, no!"/>
<dialogue speaker="purple" english="We can&apos;t pick up their signal and they can&apos;t teleport here unless they know where the ship is..." translation="No rebem cap senyal seu i no es poden teletransportar fins aquí si no saben on és la nau..."/>
<dialogue speaker="player" english="So what do we do?" translation="I què fem, doncs?"/>
<dialogue speaker="purple" english="We need to find them! Head out into the dimension and look for anywhere they might have ended up..." translation="Hem de trobar-los! Surt a la dimensió i mira de descobrir on han anat a parar..."/>
<dialogue speaker="player" english="Ok! Where do we start?" translation="Dacord! Per on comencem?"/>
<dialogue speaker="purple" english="Well, I&apos;ve been trying to find them with the ship&apos;s scanners!" translation="Bé, jo he mirat de localitzar-los amb els escàners de la nau!"/>
<dialogue speaker="purple" english="It&apos;s not working, but I did find something..." translation="No ha funcionat, però he descobert una cosa..."/>
<dialogue speaker="purple" english="These points show up on our scans as having high energy patterns!" translation="Aquests punts que apareixen a lescaneig tenen patrons dalta energia!"/>
<dialogue speaker="purple" english="There&apos;s a good chance they&apos;re teleporters - which means they&apos;re probably built near something important..." translation="És força probable que siguin teletransportadors, i això vol dir que estan construïts al costat de coses importants..."/>
<dialogue speaker="purple" english="They could be a very good place to start looking." translation="Poden ser un molt bon lloc on començar a investigar."/>
<dialogue speaker="player" english="Ok! I&apos;ll head out and see what I can find!" translation="Dacord! Doncs men vaig a mirar què hi trobo!"/>
<dialogue speaker="purple" english="I&apos;ll be right here if you need any help!" translation="Si necessites ajuda, seré aquí!"/>
</cutscene>
<cutscene id="bigopenworldskip" explanation="">
<dialogue speaker="purple" english="I&apos;ll be right here if you need any help!" translation="Si necessites ajuda, seré aquí!"/>
</cutscene>
<cutscene id="talkpurple_intro" explanation="">
<dialogue speaker="player" english="I&apos;m feeling a bit overwhelmed, Doctor." translation="Tot això maclapara una mica, doctora."/>
<dialogue speaker="player" english="Where do I begin?" translation="Per on començo?"/>
<dialogue speaker="purple" english="Remember that you can press {b_map} to check where you are on the map!" translation="Recorda que pots prémer {b_map} per a veure on ets del mapa!" buttons="1"/>
<dialogue speaker="purple" english="Look for areas where the rest of the crew might be..." translation="Cerca zones on pugui haver-hi altres tripulants..."/>
<dialogue speaker="purple" english="If you get lost, you can get back to the ship from any teleporter." translation="Si et perds, pots tornar a la nau a partir de qualsevol teletransportador."/>
<dialogue speaker="purple" english="And don&apos;t worry! We&apos;ll find everyone!" translation="I no pateixis! Segur que trobarem tothom!"/>
<dialogue speaker="purple" english="Everything will be ok!" translation="Tot anirà bé!"/>
</cutscene>
<cutscene id="talkpurple_3" explanation="only one player string is shown">
<dialogue speaker="purple" english="Are you doing ok, Captain?" translation="Estàs bé, cap?"/>
<dialogue speaker="player" english="I&apos;m worried about Victoria, Doctor!" translation="Em preocupa la Victòria, doctora!"/>
<dialogue speaker="player" english="I&apos;m worried about Vitellary, Doctor!" translation="Em preocupa en Vitel·lí, doctora!"/>
<dialogue speaker="player" english="I&apos;m worried about Verdigris, Doctor!" translation="Em preocupa en Verdet, doctora!"/>
<dialogue speaker="player" english="I&apos;m worried about Vermilion, Doctor!" translation="Em preocupa en Vermelló, doctora!"/>
<dialogue speaker="player" english="I&apos;m worried about you, Doctor!" translation="Em preocupes tu, doctora!"/>
<dialogue speaker="purple" english="Oh - well, don&apos;t worry, they&apos;ll show up!" translation="Ah, no pateixis, segur que apareixerà!"/>
<dialogue speaker="purple" english="Here! Have a lollipop!" translation="Té, una piruleta!"/>
</cutscene>
<cutscene id="trinketcollector" explanation="if no trinkets found yet">
<dialogue speaker="cyan" english="This seems like a good place to store anything I find out there..." translation="Això sembla un bon lloc on emmagatzemar les coses que trobi allà fora..."/>
<dialogue speaker="cyan" english="Victoria loves to study the interesting things we find on our adventures!" translation="La Victòria gaudeix estudiant les coses interessants que trobem durant les nostres aventures!"/>
</cutscene>
<cutscene id="newtrinketcollector" explanation="if at least one trinket found">
<dialogue speaker="cyan" english="This seems like a good place to store those shiny things." translation="Això sembla un bon lloc on emmagatzemar aquelles coses brillants..."/>
<dialogue speaker="cyan" english="Victoria loves to study the interesting things we find on our adventures!" translation="La Victòria gaudeix estudiant les coses interessants que trobem durant les nostres aventures!"/>
</cutscene>
<cutscene id="new2trinketcollector" explanation="">
<dialogue speaker="cyan" english="I hope she&apos;s ok..." translation="Espero que estigui bé..."/>
</cutscene>
<cutscene id="rescuegreen" explanation="nuance: `she&apos;s BACK on the ship` means more `she&apos;s home` and not `she has returned to the ship`. She has never left the ship">
<dialogue speaker="green" english="Captain! I&apos;ve been so worried!" translation="Cap! Estava molt preocupat!"/>
<dialogue speaker="player" english="Chief Verdigris! You&apos;re ok!" translation="Enginyer en cap Verdet! Estàs bé!"/>
<dialogue speaker="green" english="I&apos;ve been trying to get out, but I keep going around in circles..." translation="He mirat de sortir daquí, però no deixo de fer voltes..."/>
<dialogue speaker="player" english="I&apos;ve come from the ship. I&apos;m here to teleport you back to it." translation="Vinc de la nau. Sóc aquí per a teletransportar-thi."/>
<dialogue speaker="green" english="Is everyone else alright? Is Violet..." translation="La resta estan bé? I la Violeta..."/>
<dialogue speaker="player" english="She&apos;s fine - she&apos;s back on the ship!" translation="Està bé. És a la nau!"/>
<dialogue speaker="green" english="Oh! Great - Let&apos;s get going, then!" translation="Ah, fantàstic! Som-hi, doncs!"/>
</cutscene>
<cutscene id="rescueblue" explanation="">
<dialogue speaker="blue" english="Oh no! Captain! Are you stuck here too?" translation="Ostres, no! Cap! Tu tampoc no pots sortir daquí?"/>
<dialogue speaker="player" english="It&apos;s ok - I&apos;m here to rescue you!" translation="No pateixis, he vingut|a rescatar-te!"/>
<dialogue speaker="player" english="Let me explain everything..." translation="Deixam explicar-tho tot..."/>
<dialogue speaker="blue" english="What? I didn&apos;t understand any of that!" translation="Què? No he entès res de res!"/>
<dialogue speaker="player" english="Oh... well, don&apos;t worry." translation="Ah... Bé, doncs no pateixis."/>
<dialogue speaker="player" english="Follow me! Everything will be alright!" translation="Segueix-me! Tot anirà bé!"/>
<dialogue speaker="blue" english="Sniff... Really?" translation="Ai... Segur?"/>
<dialogue speaker="blue" english="Ok then!" translation="Dacord, doncs!"/>
</cutscene>
<cutscene id="rescuered" explanation="">
<dialogue speaker="red" english="Captain!" translation="Cap!"/>
<dialogue speaker="red" english="Am I ever glad to see you! I thought I was the only one to escape the ship..." translation="Malegro molt de veuret! Pensava que era lúnic que shavia escapat de la nau..."/>
<dialogue speaker="player" english="Vermilion! I knew you&apos;d be ok!" translation="Vermelló! Sabia que estaries bé!"/>
<dialogue speaker="red" english="So, what&apos;s the situation?" translation="I doncs, quina és la situació?"/>
<dialogue speaker="red" english="I see! Well, we&apos;d better get back then." translation="És clar! Dacord, doncs millor que tornem."/>
<dialogue speaker="red" english="There&apos;s a teleporter in the next room." translation="Hi ha un teletransportador a la sala del costat."/>
</cutscene>
<cutscene id="rescueyellow" explanation="">
<dialogue speaker="yellow" english="Ah, Viridian! You got off the ship alright too?" translation="Ah, Viridis! Tu també vas sortir de la nau sense fer-te mal, oi?"/>
<dialogue speaker="player" english="It&apos;s good to see you&apos;re alright, Professor!" translation="Malegro de veure que estàs bé, professor!"/>
<dialogue speaker="yellow" english="Is the ship ok?" translation="La nau està bé?"/>
<dialogue speaker="player" english="It&apos;s badly damaged, but Violet&apos;s been working on fixing it." translation="Està força malmesa, però la Violeta|treballa per a reparar-la."/>
<dialogue speaker="player" english="We could really use your help..." translation="La teva ajuda ens vindria molt bé..."/>
<dialogue speaker="yellow" english="Ah, of course!" translation="Ah, és clar!"/>
<dialogue speaker="yellow" english="The background interference in this dimension prevented the ship from finding a teleporter when we crashed!" translation="Les interferències del rerefons daquesta dimensió van impedir que la nau trobés un teletransportador abans que ens estavelléssim!"/>
<dialogue speaker="yellow" english="We&apos;ve all been teleported to different locations!" translation="Per això hem acabat teletransportats|a diferents llocs!"/>
<dialogue speaker="player" english="Er, that sounds about right!" translation="Eh... És clar, té sentit!"/>
<dialogue speaker="yellow" english="Let&apos;s get back to the ship, then!" translation="Tornem a la nau, doncs!"/>
<dialogue speaker="yellow" english="After you, Captain!" translation="Darrere teu, cap!"/>
</cutscene>
<cutscene id="int1blue_1" explanation="">
<dialogue speaker="blue" english="Waaaa!" translation="Buàààà!" case="1"/>
<dialogue speaker="blue" english="Captain! Are you ok?" translation="Cap! Estàs bé?"/>
<dialogue speaker="player" english="I&apos;m ok... this... this isn&apos;t the ship..." translation="Estic bé... Però això...|no és la nau..."/>
<dialogue speaker="player" english="Where are we?" translation="On som?"/>
<dialogue speaker="blue" english="Waaaa!" translation="Buàààà!" case="2"/>
<dialogue speaker="player" english="Something&apos;s gone wrong... We should look for a way back!" translation="Alguna cosa no ha anat bé... Hem de cercar un camí de tornada!"/>
</cutscene>
<cutscene id="int1blue_2" explanation="">
<dialogue speaker="player" english="Follow me! I&apos;ll help you!" translation="Segueix-me! Tajudaré!"/>
<dialogue speaker="blue" english="Promise you won&apos;t leave without me!" translation="Promet-me que no te naniràs sense mi!"/>
<dialogue speaker="player" english="I promise! Don&apos;t worry!" translation="Tho prometo! No pateixis!"/>
</cutscene>
<cutscene id="int1blue_3" explanation="">
<dialogue speaker="player" english="Are you ok down there, Doctor?" translation="Va tot bé allà baix, doctora?"/>
<dialogue speaker="blue" english="I wanna go home!" translation="Men vull anar a casa!"/>
<dialogue speaker="blue" english="Where are we? How did we even get here?" translation="On som? Com se suposa que hi hem arribat?"/>
<dialogue speaker="player" english="Well, Violet did say that the interference in the dimension we crashed in was causing problems with the teleporters..." translation="Doncs... la Violeta deia que les interferències de la dimensió on ens hem estavellat causaven problemes amb els teletransportadors..."/>
<dialogue speaker="player" english="I guess something went wrong..." translation="Suposo que alguna cosa ha anat malament..."/>
<dialogue speaker="player" english="But if we can find another teleporter, I think we can get back to the ship!" translation="Però si trobem un altre teletransportador, suposo que podrem tornar a la nau!"/>
<dialogue speaker="blue" english="Sniff..." translation="Aiii..."/>
</cutscene>
<cutscene id="int1blue_4" explanation="the split paths merge">
<dialogue speaker="blue" english="Captain! Captain! Wait for me!" translation="Cap! Cap! Esperam!"/>
<dialogue speaker="blue" english="Please don&apos;t leave me behind! I don&apos;t mean to be a burden!" translation="No em deixis enrere!|No vull ser cap càrrega!"/>
<dialogue speaker="blue" english="I&apos;m scared!" translation="Tinc por!"/>
<dialogue speaker="player" english="Oh... don&apos;t worry Victoria, I&apos;ll look after you!" translation="Ah... No pateixis, Victòria, tindré cura de tu!"/>
</cutscene>
<cutscene id="int1blue_5" explanation="">
<dialogue speaker="blue" english="We&apos;re never going to get out of here, are we?" translation="No sortirem mai daquí, oi que no?"/>
<dialogue speaker="player" english="I.. I don&apos;t know..." translation="N... no ho sé..."/>
<dialogue speaker="player" english="I don&apos;t know where we are or how we&apos;re going to get out..." translation="No sé on som ni com nhem de sortir..."/>
</cutscene>
<cutscene id="int1blue_6" explanation="">
<dialogue speaker="blue" english="We&apos;re going to be lost forever!" translation="Acabarem perduts per sempre més!"/>
<dialogue speaker="player" english="Ok, come on... Things aren&apos;t that bad." translation="Au, va... Les coses no estan tan malament."/>
<dialogue speaker="player" english="I have a feeling that we&apos;re nearly home!" translation="Tinc la sensació que som a prop de casa!"/>
<dialogue speaker="player" english="We can&apos;t be too far from another teleporter!" translation="No podem ser gaire lluny dun altre teletransportador!"/>
<dialogue speaker="blue" english="I hope you&apos;re right, captain..." translation="Espero que tinguis raó, cap..."/>
</cutscene>
<cutscene id="int1blue_7" explanation="">
<dialogue speaker="blue" english="Captain! You were right! It&apos;s a teleporter!" translation="Cap! Tenies raó! Hi ha un teletransportador!"/>
<dialogue speaker="player" english="Phew! You had me worried for a while there... I thought we were never going to find one." translation="Buf! Ja patia, la veritat... Pensava que no en trobaríem mai cap."/>
<dialogue speaker="blue" english="What? Really?" translation="Què? De debò?"/>
<dialogue speaker="player" english="Anyway, let&apos;s go back to the ship." translation="En fi, tornem a la nau."/>
</cutscene>
<cutscene id="int1green_1" explanation="">
<dialogue speaker="green" english="Huh? This isn&apos;t the ship..." translation="Eh? Això no és la nau..."/>
<dialogue speaker="green" english="Captain! What&apos;s going on?" translation="Cap! Què passa?"/>
<dialogue speaker="player" english="I... I don&apos;t know!" translation="N... no ho sé!"/>
<dialogue speaker="player" english="Where are we?" translation="On som?"/>
<dialogue speaker="green" english="Uh oh, this isn&apos;t good... Something must have gone wrong with the teleporter!" translation="Caram, això no va bé...|El teletransportador devia tenir algun problema!"/>
<dialogue speaker="player" english="Ok... no need to panic!" translation="Dacord... Mantinguem la calma!"/>
<dialogue speaker="player" english="Let&apos;s look for another teleporter!" translation="Cerquem un altre teletransportador!"/>
</cutscene>
<cutscene id="int1green_2" explanation="">
<dialogue speaker="player" english="Let&apos;s go this way!" translation="Anem cap allà!"/>
<dialogue speaker="green" english="After you, Captain!" translation="Després de tu, cap!"/>
</cutscene>
<cutscene id="int1green_3" explanation="just like in rescuegreen, `Violet is back on the ship` does not mean she was ever off the ship and has *returned* to it. Or maybe Verdigris thinks Violet was warped off the ship as well just like him and she *has* made her way back to the ship?">
<dialogue speaker="green" english="So Violet&apos;s back on the ship? She&apos;s really ok?" translation="Així, la Violeta és a la nau? De debò que està bé?"/>
<dialogue speaker="player" english="She&apos;s fine! She helped me find my way back!" translation="I tant! Mha ajudat a trobar|el camí de tornada!"/>
<dialogue speaker="green" english="Oh, phew! I was worried about her." translation="Ah, uf! Patia per ella."/>
<dialogue speaker="green" english="Captain, I have a secret..." translation="Cap, tinc un secret..."/>
<dialogue speaker="green" english="I really like Violet!" translation="Magrada molt la Violeta!"/>
<dialogue speaker="player" english="Is that so?" translation="De debò?"/>
<dialogue speaker="green" english="Please promise you won&apos;t tell her!" translation="Promet-me que no li ho diràs!"/>
</cutscene>
<cutscene id="int1green_4" explanation="the split paths merge">
<dialogue speaker="green" english="Hey again!" translation="Ep!"/>
<dialogue speaker="player" english="Hey!" translation="Ei!"/>
<dialogue speaker="player" english="Are you doing ok?" translation="Va tot bé?"/>
<dialogue speaker="green" english="I think so! I really hope we can find a way back to the ship..." translation="Diria que sí! Espero que trobem|un camí cap a la nau..."/>
</cutscene>
<cutscene id="int1green_5" explanation="">
<dialogue speaker="green" english="So, about Violet..." translation="Daixò... Pel que fa a la Violeta..."/>
<dialogue speaker="player" english="Um, yeah?" translation="Eh... Sí?"/>
<dialogue speaker="green" english="Do you have any advice?" translation="Tens cap consell?"/>
<dialogue speaker="player" english="Oh!" translation="Ah!"/>
<dialogue speaker="player" english="Hmm..." translation="Hum..."/>
<dialogue speaker="player" english="Um... you should... be yourself!" translation="Doncs... hauries de...|ser tu mateix!"/>
<dialogue speaker="green" english="Oh." translation="Ah."/>
<dialogue speaker="green" english="Thanks Captain!" translation="Gràcies, cap!"/>
</cutscene>
<cutscene id="int1green_6" explanation="">
<dialogue speaker="player" english="So, do you think you&apos;ll be able to fix the ship?" translation="I què, creus que podràs|reparar la nau?"/>
<dialogue speaker="green" english="Depends on how bad it is... I think so, though!" translation="Depèn de com estigui de malament...|Però penso que sí!"/>
<dialogue speaker="green" english="It&apos;s not very hard, really. The basic dimensional warping engine design is pretty simple, and if we can get that working we shouldn&apos;t have any trouble getting home." translation="No és tan difícil, en realitat. El disseny bàsic del motor de salt dimensional és força simple,|i si aconseguim que això funcioni, no hauríem de tenir problemes per a arribar a casa."/>
<dialogue speaker="player" english="Oh! Good!" translation="Ah! Genial!"/>
</cutscene>
<cutscene id="int1green_7" explanation="">
<dialogue speaker="green" english="Finally! A teleporter!" translation="Per fi! Un teletransportador!"/>
<dialogue speaker="green" english="I was getting worried we wouldn&apos;t find one..." translation="Ja començava a pensar que no en trobaríem cap..."/>
<dialogue speaker="player" english="Let&apos;s head back to the ship!" translation="Tornem a la nau!"/>
</cutscene>
<cutscene id="int1red_1" explanation="">
<dialogue speaker="red" english="Wow! Where are we?" translation="Bufa! On som?"/>
<dialogue speaker="player" english="This... isn&apos;t right... Something must have gone wrong with the teleporter!" translation="Això... no va bé... El teletransportador devia tenir algun problema!"/>
<dialogue speaker="red" english="Oh well... We can work it out when we get back to the ship!" translation="Què hi farem... Ja ho investigarem quan tornem a la nau!"/>
<dialogue speaker="red" english="Let&apos;s go exploring!" translation="Som-hi, a explorar!"/>
<dialogue speaker="player" english="Ok then!" translation="Molt bé!"/>
</cutscene>
<cutscene id="int1red_2" explanation="">
<dialogue speaker="player" english="Follow me!" translation="Segueix-me!"/>
<dialogue speaker="red" english="Aye aye, Captain!" translation="Rebut, cap!"/>
</cutscene>
<cutscene id="int1red_3" explanation="">
<dialogue speaker="red" english="Hey Viridian... how did the crash happen, exactly?" translation="Ei, Viridis... Com és que ens vam estavellar, exactament?"/>
<dialogue speaker="player" english="Oh, I don&apos;t really know - some sort of interference..." translation="Ah, doncs no ho sé del cert... Només sé que per les interferències..."/>
<dialogue speaker="player" english="...or something sciencey like that. It&apos;s not really my area." translation="O per alguna cosa científica així... No és el meu fort, aquest tema."/>
<dialogue speaker="red" english="Ah! Well, do you think we&apos;ll be able to fix the ship and go home?" translation="Ah! Bé, i creus que podrem reparar la nau i tornar a casa?"/>
<dialogue speaker="player" english="Of course! Everything will be ok!" translation="És clar! Tot anirà bé!"/>
</cutscene>
<cutscene id="int1red_4" explanation="the split paths merge">
<dialogue speaker="red" english="Hi again! You doing ok?" translation="Ep! Va tot bé?"/>
<dialogue speaker="player" english="I think so! But I really want to get back to the ship..." translation="Crec que sí! Però tinc moltes ganes de tornar a la nau..."/>
<dialogue speaker="red" english="We&apos;ll be ok! If we can find a teleporter somewhere we should be able to get back!" translation="Ens en sortirem! Si aconseguim trobar un teletransportador en algun lloc, hauríem de poder tornar-hi!"/>
</cutscene>
<cutscene id="int1red_5" explanation="">
<dialogue speaker="red" english="Are we there yet?" translation="Falta gaire?"/>
<dialogue speaker="player" english="We&apos;re getting closer, I think..." translation="Ens hi estem acostant, em sembla..."/>
<dialogue speaker="player" english="I hope..." translation="Espero..."/>
</cutscene>
<cutscene id="int1red_6" explanation="">
<dialogue speaker="player" english="I wonder where we are, anyway?" translation="On devem ser, a tot això?"/>
<dialogue speaker="player" english="This seems different from that dimension we crashed in, somehow..." translation="No sé per què, però aquest lloc sembla diferent de la dimensió on ens vam estavellar..."/>
<dialogue speaker="red" english="I dunno... But we must be close to a teleporter by now..." translation="No sé... Però ara ja devem ser a prop dun teletransportador..."/>
</cutscene>
<cutscene id="int1red_7" explanation="">
<dialogue speaker="player" english="We&apos;re there!" translation="Ja hi som!"/>
<dialogue speaker="red" english="See? I told you! Let&apos;s get back to the ship!" translation="Ho veus? Tho he dit! Vinga, tornem a la nau!"/>
</cutscene>
<cutscene id="int1yellow_1" explanation="">
<dialogue speaker="yellow" english="Oooh! This is interesting..." translation="Oooh! Que interessant..."/>
<dialogue speaker="yellow" english="Captain! Have you been here before?" translation="Cap! Has estat mai aquí?"/>
<dialogue speaker="player" english="What? Where are we?" translation="Què? On som?"/>
<dialogue speaker="yellow" english="I suspect something deflected our teleporter transmission! This is somewhere new..." translation="Em sembla que alguna cosa ha desviat la transmissió del teletransportador! Aquest lloc és nou..."/>
<dialogue speaker="player" english="Oh no!" translation="Ostres, no!"/>
<dialogue speaker="player" english="We should try to find a teleporter and get back to the ship..." translation="Hauríem de mirar de trobar un teletransportador i tornar a la nau..."/>
</cutscene>
<cutscene id="int1yellow_2" explanation="">
<dialogue speaker="player" english="Follow me!" translation="Segueix-me!"/>
<dialogue speaker="yellow" english="Right behind you, Captain!" translation="Darrere teu, cap!"/>
</cutscene>
<cutscene id="int1yellow_3" explanation="">
<dialogue speaker="player" english="What do you make of all this, Professor?" translation="Què en penses, de|tot això, professor?"/>
<dialogue speaker="yellow" english="I&apos;m guessing this dimension has something to do with the interference that caused us to crash!" translation="Suposo que aquesta dimensió té|alguna cosa a veure amb les interferències que han causat que ens estavelléssim!"/>
<dialogue speaker="yellow" english="Maybe we&apos;ll find the cause of it here?" translation="Potser en trobarem la causa aquí?"/>
<dialogue speaker="player" english="Oh wow! Really?" translation="Ah, ostres! De debò?"/>
<dialogue speaker="yellow" english="Well, it&apos;s just a guess. I&apos;ll need to get back to the ship before I can do any real tests..." translation="Bé, només és una suposició. Necessito tornar a la nau abans de poder fer cap experiment real..."/>
</cutscene>
<cutscene id="int1yellow_4" explanation="the split paths merge, and Vitellary sees a checkpoint">
<dialogue speaker="yellow" english="Ohh! What was that?" translation="Oooh! Què és, això?"/>
<dialogue speaker="player" english="What was what?" translation="Què és, això?"/>
<dialogue speaker="yellow" english="That big... C thing! I wonder what it does?" translation="Aquesta cosa grossa... amb una C! Què deu fer?"/>
<dialogue speaker="player" english="Em... I don&apos;t really know how to answer that question..." translation="Eh... No sé com respondre a la teva qüestió..."/>
<dialogue speaker="player" english="It&apos;s probably best not to acknowledge that it&apos;s there at all." translation="Segurament és millor que fem veure que no existeix."/>
<dialogue speaker="yellow" english="Maybe we should take it back to the ship to study it?" translation="Ens la podríem emportar a la nau i estudiar-la!"/>
<dialogue speaker="player" english="We really shouldn&apos;t think about it too much... Let&apos;s keep moving!" translation="No hi hauríem de pensar pas gaire... Vinga, som-hi!"/>
</cutscene>
<cutscene id="int1yellow_5" explanation="">
<dialogue speaker="yellow" english="You know, there&apos;s something really odd about this dimension..." translation="Saps? Hi ha una cosa molt estranya en aquesta dimensió..."/>
<dialogue speaker="player" english="Yeah?" translation="Què vols dir?"/>
<dialogue speaker="yellow" english="We shouldn&apos;t really be able to move between dimensions with a regular teleporter..." translation="No ens hauríem de poder moure entre dimensions amb un teletransportador normal..."/>
<dialogue speaker="yellow" english="Maybe this isn&apos;t a proper dimension at all?" translation="Potser no és una dimensió com a tal?"/>
<dialogue speaker="yellow" english="Maybe it&apos;s some kind of polar dimension? Something artificially created for some reason?" translation="Potser és alguna mena de dimensió polar? Alguna cosa creada artificialment per algun motiu..."/>
<dialogue speaker="yellow" english="I can&apos;t wait to get back to the ship. I have a lot of tests to run!" translation="Em moro de ganes de tornar a la nau. Hi he de fer un munt dexperiments!"/>
</cutscene>
<cutscene id="int1yellow_6" explanation="">
<dialogue speaker="yellow" english="I wonder if there&apos;s anything else in this dimension worth exploring?" translation="Hi deu haver alguna altra cosa daquesta dimensió que pagui la pena dexplorar?"/>
<dialogue speaker="player" english="Maybe... but we should probably just focus on finding the rest of the crew for now..." translation="Potser... Però segurament només ens hauríem de centrar a trobar la resta de la tripulació, ara mateix..."/>
</cutscene>
<cutscene id="int1yellow_7" explanation="">
<dialogue speaker="yellow" english="At last!" translation="Per fi!"/>
<dialogue speaker="yellow" english="Let&apos;s go back to the ship!" translation="Tornem a la nau!"/>
</cutscene>
<cutscene id="intermission_2" explanation="">
<dialogue speaker="player" english="Uh oh..." translation="Ups..."/>
<dialogue speaker="player" english="Not again!" translation="Una altra vegada no!"/>
</cutscene>
<cutscene id="int2intro_blue" explanation="">
<dialogue speaker="player" english="Victoria? Where are you?" translation="Victòria? On ets?"/>
<dialogue speaker="blue" english="Help!" translation="Ajuda!"/>
<dialogue speaker="player" english="Hang on! I&apos;ll save you!" translation="Resisteix! Et salvaré!"/>
</cutscene>
<cutscene id="int2intro_green" explanation="">
<dialogue speaker="player" english="Verdigris? Where are you?" translation="Verdet? On ets?"/>
<dialogue speaker="green" english="Aaagghh!" translation="Aaaahhh!"/>
<dialogue speaker="player" english="Hang on! I&apos;ll save you!" translation="Resisteix! Et salvaré!"/>
</cutscene>
<cutscene id="int2intro_red" explanation="">
<dialogue speaker="player" english="Vermilion? Where are you?" translation="Vermelló? On ets?"/>
<dialogue speaker="red" english="Wheeeee!" translation="Wiiiiiii!"/>
<dialogue speaker="player" english="Hang on! I&apos;ll save you!" translation="Resisteix! Et salvaré!"/>
</cutscene>
<cutscene id="int2intro_yellow" explanation="">
<dialogue speaker="player" english="Vitellary? Where are you?" translation="Vitel·lí? On ets?"/>
<dialogue speaker="yellow" english="Captain!" translation="Cap!"/>
<dialogue speaker="player" english="Hang on! I&apos;ll save you!" translation="Resisteix! Et salvaré!"/>
</cutscene>
<cutscene id="int2_blue" explanation="after gravitron">
<dialogue speaker="blue" english="I think I&apos;m going to be sick..." translation="Em sembla que em posaré malalta..."/>
<dialogue speaker="player" english="I feel dizzy..." translation="Quin mareig..."/>
</cutscene>
<cutscene id="int2_green" explanation="after gravitron">
<dialogue speaker="green" english="Phew! You&apos;re ok!" translation="Buf! Estàs bé!"/>
<dialogue speaker="player" english="I feel dizzy..." translation="Quin mareig..."/>
</cutscene>
<cutscene id="int2_red" explanation="after gravitron">
<dialogue speaker="red" english="Again! Let&apos;s go again!" translation="Una altra vegada! Tornem-hi!"/>
<dialogue speaker="player" english="I feel dizzy..." translation="Quin mareig..."/>
</cutscene>
<cutscene id="int2_yellow" explanation="after gravitron">
<dialogue speaker="yellow" english="That was interesting, wasn&apos;t it?" translation="Ha estat interessant, oi?"/>
<dialogue speaker="player" english="I feel dizzy..." translation="Quin mareig..."/>
</cutscene>
<cutscene id="talkpurple_1" explanation="">
<dialogue speaker="purple" english="... I hope Verdigris is alright." translation="Espero que en Verdet estigui bé..."/>
<dialogue speaker="purple" english="If you can find him, he&apos;d be a big help fixing the ship!" translation="Si aconseguissis trobar-lo, seria de gran ajuda a lhora|de reparar la nau!"/>
</cutscene>
<cutscene id="talkpurple_2" explanation="">
<dialogue speaker="purple" english="Chief Verdigris is so brave and ever so smart!" translation="Lenginyer en cap Verdet és valent i intel·ligent alhora!"/>
</cutscene>
<cutscene id="talkpurple_4" explanation="">
<dialogue speaker="purple" english="Welcome back, Captain!" translation="Tornes a ser aquí, cap!"/>
<dialogue speaker="purple" english="I think Victoria is quite happy to be back on the ship." translation="Em sembla que la Victòria està força contenta de tornar a ser a la nau."/>
<dialogue speaker="purple" english="She really doesn&apos;t like adventuring. She gets very homesick!" translation="No li agrada gens anar daventures. Senyora molt fàcilment!"/>
</cutscene>
<cutscene id="talkpurple_5" explanation="only one of the last 6 strings is shown">
<dialogue speaker="purple" english="Vermilion called in to say hello!" translation="En Vermelló tenvia salutacions!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find the rest of the crew!" translation="Té moltes ganes dajudar-te a trobar la resta de la tripulació!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find Victoria!" translation="Té moltes ganes dajudar-te a trobar la Victòria!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find Vitellary!" translation="Té moltes ganes dajudar-te a trobar en Vitel·lí!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find Verdigris!" translation="Té moltes ganes dajudar-te a trobar en Verdet!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find Vermilion!" translation="Té moltes ganes dajudar-te a trobar en Vermelló!"/>
<dialogue speaker="purple" english="He&apos;s really looking forward to helping you find you!" translation="Té moltes ganes dajudar-te|a trobar-te!"/>
</cutscene>
<cutscene id="talkpurple_6" explanation="">
<dialogue speaker="purple" english="Captain! You found Verdigris!" translation="Cap! Has trobat en Verdet!"/>
<dialogue speaker="purple" english="Thank you so much!" translation="Moltes gràcies!"/>
</cutscene>
<cutscene id="talkpurple_7" explanation="">
<dialogue speaker="purple" english="I&apos;m glad Professor Vitellary is ok!" translation="Malegro que el professor Vitel·lí estigui bé!"/>
<dialogue speaker="purple" english="He had lots of questions for me about this dimension." translation="Em volia fer un munt de qüestions sobre aquesta dimensió."/>
<dialogue speaker="purple" english="He&apos;s already gotten to work with his research!" translation="Ja sha posat a investigar!"/>
</cutscene>
<cutscene id="talkpurple_intermission1" explanation="">
<dialogue speaker="player" english="Doctor, something strange happened when we teleported back to the ship..." translation="Doctora, ha passat una cosa estranya quan ens hem teletransportat a la nau..."/>
<dialogue speaker="player" english="We got lost in another dimension!" translation="Ens hem perdut en una altra dimensió!"/>
<dialogue speaker="purple" english="Oh no!" translation="Ostres, no!"/>
<dialogue speaker="purple" english="Maybe that dimension has something to do with the interference that caused us to crash here?" translation="Potser aquella dimensió té alguna cosa a veure amb les interferències que han causat que ens estavelléssim aquí?"/>
<dialogue speaker="purple" english="I&apos;ll look into it..." translation="Ho investigaré..."/>
</cutscene>
<cutscene id="talkpurple_intermission2" explanation="">
<dialogue speaker="player" english="Doctor! Doctor! It happened again!" translation="Doctora, doctora! Ha tornat a passar!"/>
<dialogue speaker="player" english="The teleporter brought us to that weird dimension..." translation="El teletransportador ens ha portat a aquella dimensió estranya..."/>
<dialogue speaker="purple" english="Hmm, there&apos;s definitely something strange happening..." translation="Hum... Sens dubte, passa alguna cosa estranya..."/>
<dialogue speaker="purple" english="If only we could find the source of that interference!" translation="Tant de bo trobéssim lorigen de les interferències!"/>
</cutscene>
<cutscene id="talkpurple_intermission3" explanation="">
<dialogue speaker="player" english="Doctor, something strange has been happening when we teleport back to the ship..." translation="Doctora, quan ens teletransportem a la nau, passen coses estranyes..."/>
<dialogue speaker="player" english="We keep getting brought to another weird dimension!" translation="Sempre ens acaba portant a una altra dimensió estranya!"/>
<dialogue speaker="purple" english="Oh no!" translation="Ostres, no!"/>
<dialogue speaker="purple" english="Maybe that dimension has something to do with the interference that caused us to crash here?" translation="Potser aquella dimensió té alguna cosa a veure amb les interferències que han causat que ens estavelléssim aquí?"/>
<dialogue speaker="purple" english="Hmm, there&apos;s definitely something strange happening..." translation="Hum... Sens dubte, passa alguna cosa estranya..."/>
<dialogue speaker="purple" english="If only we could find the source of that interference!" translation="Tant de bo trobéssim lorigen de les interferències!"/>
</cutscene>
<cutscene id="talkpurple_8" explanation="">
<dialogue speaker="purple" english="Hey Captain! Now that you&apos;ve turned off the source of the interference, we can warp everyone back to the ship instantly, if we need to!" translation="Ei, cap! Ara que has desactivat lorigen de les interferències, podem teletransportar tothom a la nau instantàniament, si cal!"/>
<dialogue speaker="purple" english="Any time you want to come back to the ship, just select the new SHIP option in your menu!" translation="Si en algun moment vols tornar a la nau, selecciona la nova opció NAU al menú!"/>
</cutscene>
<cutscene id="talkgreen_1" explanation="">
<dialogue speaker="green" english="I&apos;m an engineer!" translation="Sóc enginyer!"/>
</cutscene>
<cutscene id="talkgreen_2" explanation="">
<dialogue speaker="green" english="I think I can get this ship moving again, but it&apos;s going to take a while..." translation="Em sembla que podré aconseguir que la nau es torni a moure, però em costarà una bona estona..."/>
</cutscene>
<cutscene id="talkgreen_3" explanation="">
<dialogue speaker="green" english="Victoria mentioned something about a lab? I wonder if she found anything down there?" translation="La Victòria parlava dun laboratori o una cosa així? Potser hi ha trobat alguna cosa, allà baix?"/>
</cutscene>
<cutscene id="talkgreen_4" explanation="">
<dialogue speaker="green" english="Vermilion&apos;s back! Yey!" translation="En Vermelló ha tornat! Visca!"/>
</cutscene>
<cutscene id="talkgreen_5" explanation="">
<dialogue speaker="green" english="The Professor had lots of questions about this dimension for me..." translation="El professor em volia fer un munt de qüestions sobre aquesta dimensió..."/>
<dialogue speaker="green" english="We still don&apos;t really know that much, though." translation="Però encara no en sabem gaire cosa."/>
<dialogue speaker="green" english="Until we work out what&apos;s causing that interference, we can&apos;t go anywhere." translation="Fins que no descobrim què causa les interferències, no podrem anar enlloc."/>
</cutscene>
<cutscene id="talkgreen_6" explanation="">
<dialogue speaker="green" english="I&apos;m so glad that Violet&apos;s alright!" translation="Malegro molt que la Violeta estigui bé!"/>
</cutscene>
<cutscene id="talkgreen_7" explanation="">
<dialogue speaker="green" english="That other dimension we ended up in must be related to this one, somehow..." translation="Laltra dimensió on havíem anat a parar deu estar relacionada amb aquesta dalguna manera..."/>
</cutscene>
<cutscene id="talkgreen_8" explanation="">
<dialogue speaker="green" english="The antenna&apos;s broken! This is going to be very hard to fix..." translation="Lantena està trencada! Això costarà molt de reparar..."/>
</cutscene>
<cutscene id="talkgreen_9" explanation="">
<dialogue speaker="green" english="It looks like we were warped into solid rock when we crashed!" translation="Sembla com si ens haguéssim teletransportat enmig de roca sòlida en estavellar-nos!"/>
<dialogue speaker="green" english="Hmm. It&apos;s going to be hard to separate from this..." translation="Hum... Costarà separar-nos-en..."/>
</cutscene>
<cutscene id="talkgreen_10" explanation="">
<dialogue speaker="green" english="The ship&apos;s all fixed up. We can leave at a moment&apos;s notice!" translation="La nau ja està tota reparada. Podem anar-nos-en en un tres i no res!"/>
</cutscene>
<cutscene id="talkred_1" explanation="">
<dialogue speaker="red" english="Don&apos;t worry, Sir!" translation="No pateixis!"/>
<dialogue speaker="red" english="We&apos;ll find a way out of here!" translation="Trobarem una manera de sortir daquí!"/>
</cutscene>
<cutscene id="talkred_2" explanation="">
<dialogue speaker="red" english="I hope Victoria is ok..." translation="Espero que la Victòria estigui bé..."/>
<dialogue speaker="red" english="She doesn&apos;t handle surprises very well..." translation="No li agraden gaire les sorpreses..."/>
</cutscene>
<cutscene id="talkred_3" explanation="">
<dialogue speaker="red" english="I don&apos;t know how we&apos;re going to get this ship working again!" translation="No sé pas com farem que la nau torni a funcionar!"/>
<dialogue speaker="red" english="Chief Verdigris would know what to do..." translation="Lenginyer en cap Verdet sabria què fer..."/>
</cutscene>
<cutscene id="talkred_4" explanation="">
<dialogue speaker="red" english="I wonder what caused the ship to crash here?" translation="Què devia fer que la nau sestavellés aquí?"/>
<dialogue speaker="red" english="It&apos;s the shame the Professor isn&apos;t here, huh? I&apos;m sure he could work it out!" translation="És una llàstima que el professor no hi sigui, eh? Segur que ell ho esbrinaria!"/>
</cutscene>
<cutscene id="talkred_5" explanation="">
<dialogue speaker="red" english="It&apos;s great to be back!" translation="És genial tornar a ser aquí!"/>
<dialogue speaker="red" english="I can&apos;t wait to help you find the rest of the crew!" translation="Em moro de ganes dajudar-te a trobar la resta de tripulants!"/>
<dialogue speaker="red" english="It&apos;ll be like old times, huh, Captain?" translation="Serà com als vells temps, oi, cap?"/>
</cutscene>
<cutscene id="talkred_6" explanation="">
<dialogue speaker="red" english="It&apos;s good to have Victoria back with us." translation="És genial que la Victòria torni a ser amb nosaltres."/>
<dialogue speaker="red" english="She really seems happy to get back to work in her lab!" translation="Sembla molt feliç de poder tornar a treballar al seu laboratori!"/>
</cutscene>
<cutscene id="talkred_7" explanation="">
<dialogue speaker="red" english="I think I saw Verdigris working on the outside of the ship!" translation="Em sembla que he vist en Verdet treballant a la part exterior de la nau!"/>
</cutscene>
<cutscene id="talkred_8" explanation="">
<dialogue speaker="red" english="You found Professor Vitellary! All right!" translation="Has trobat el professor Vitel·lí! Molt bé!"/>
<dialogue speaker="red" english="We&apos;ll have this interference thing worked out in no time now!" translation="Ara resoldre el tema de les interferències serà bufar i fer ampolles!"/>
</cutscene>
<cutscene id="talkred_9" explanation="">
<dialogue speaker="red" english="That other dimension was really strange, wasn&apos;t it?" translation="Aquella altra dimensió era ben estranya, eh?"/>
<dialogue speaker="red" english="I wonder what caused the teleporter to send us there?" translation="Per què el teletransportador ens va enviar allà?"/>
</cutscene>
<cutscene id="talkred_10" explanation="">
<dialogue speaker="red" english="Heya Captain!" translation="Iep, cap!"/>
<dialogue speaker="red" english="This way looks a little dangerous..." translation="Aquest camí sembla una mica perillós..."/>
</cutscene>
<cutscene id="talkred_11" explanation="">
<dialogue speaker="red" english="I&apos;m helping!" translation="Testic ajudant!"/>
</cutscene>
<cutscene id="talkred_12" explanation="">
<dialogue speaker="red" english="Hey Captain!" translation="Ei, cap!"/>
<dialogue speaker="red" english="I found something interesting around here - the same warp signature I saw when I landed!" translation="En aquesta zona he trobat una cosa interessant: les mateixes formes derivades dun teletransport que vaig veure quan vaig aterrar!"/>
<dialogue speaker="red" english="Someone from the ship must be nearby..." translation="Hi ha dhaver algú de la nau a prop..."/>
</cutscene>
<cutscene id="talkred_13" explanation="">
<dialogue speaker="red" english="This dimension is pretty exciting, isn&apos;t it?" translation="Aquesta dimensió és força emocionant, oi?"/>
<dialogue speaker="red" english="I wonder what we&apos;ll find?" translation="Què hi trobarem?"/>
</cutscene>
<cutscene id="talkblue_1" explanation="">
<dialogue speaker="blue" english="Any signs of Professor Vitellary?" translation="Hi ha cap senyal del professor Vitel·lí?"/>
<dialogue speaker="player" english="Sorry, not yet..." translation="Em sap greu, però de moment, no..."/>
<dialogue speaker="blue" english="I hope he&apos;s ok..." translation="Espero que estigui bé..."/>
</cutscene>
<cutscene id="talkblue_2" explanation="">
<dialogue speaker="blue" english="Thanks so much for saving me, Captain!" translation="Moltes gràcies per salvar-me, cap!"/>
</cutscene>
<cutscene id="talkblue_3" explanation="">
<dialogue speaker="blue" english="I&apos;m so glad to be back!" translation="Malegro molt de tornar a ser aquí!"/>
<dialogue speaker="blue" english="That lab was so dark and scary! I didn&apos;t like it at all..." translation="Aquell laboratori era fosc i feia por! No magradava gens..."/>
</cutscene>
<cutscene id="talkblue_4" explanation="">
<dialogue speaker="blue" english="Vitellary&apos;s back? I knew you&apos;d find him!" translation="En Vitel·lí torna a ser aquí? Sabia que el trobaries!"/>
<dialogue speaker="blue" english="I mean, I admit I was very worried that you wouldn&apos;t..." translation="Tot i que he dadmetre que tenia molta por que no el trobessis..."/>
<dialogue speaker="blue" english="or that something might have happened to him..." translation="O que li hagués passat|alguna cosa..."/>
<dialogue speaker="blue" english="sniff..." translation="Aix..."/>
<dialogue speaker="player" english="Doctor Victoria? He&apos;s ok!" translation="Doctora Victòria... Que està bé!"/>
<dialogue speaker="blue" english="Oh! Sorry! I was just thinking about what if he wasn&apos;t?" translation="Ah, perdona! És que pensava en què passaria si no ho estigués!"/>
<dialogue speaker="blue" english="Thank you, Captain!" translation="Gràcies, cap!"/>
</cutscene>
<cutscene id="talkblue_5" explanation="">
<dialogue speaker="blue" english="You found Vermilion! Great!" translation="Has trobat en Vermelló! Genial!"/>
<dialogue speaker="blue" english="I wish he wasn&apos;t so reckless!" translation="Tant de bo no fos tan imprudent!"/>
<dialogue speaker="blue" english="He&apos;ll get himself into trouble..." translation="Sempre es fica en problemes..."/>
</cutscene>
<cutscene id="talkblue_6" explanation="">
<dialogue speaker="blue" english="Verdigris is ok! Violet will be so happy!" translation="En Verdet està bé! La Violeta es posarà molt contenta!"/>
<dialogue speaker="blue" english="I&apos;m happy!" translation="Estic feliç!"/>
<dialogue speaker="blue" english="Though I was very worried..." translation="Tot i que patia molt..."/>
</cutscene>
<cutscene id="talkblue_7" explanation="">
<dialogue speaker="blue" english="Why did the teleporter send us to that scary dimension?" translation="Per què el teletransportador ens ha enviat a aquella dimensió tan espantosa?"/>
<dialogue speaker="blue" english="What happened?" translation="Què ha passat?"/>
<dialogue speaker="player" english="I don&apos;t know, Doctor..." translation="No ho sé, doctora..."/>
<dialogue speaker="blue" english="Why?" translation="Per què?"/>
</cutscene>
<cutscene id="talkblue_8" explanation="">
<dialogue speaker="blue" english="Heya Captain!" translation="Ei, cap!"/>
<dialogue speaker="blue" english="Are you going to try and find the rest of these shiny things?" translation="Miraràs de trobar la resta daquestes coses brillants?"/>
</cutscene>
<cutscene id="talkblue_trinket1" explanation="">
<dialogue speaker="blue" english="Hey Captain, I found this in that lab..." translation="Ei, cap, he trobat això en aquell laboratori..."/>
<dialogue speaker="blue" english="Any idea what it does?" translation="Tens cap idea de per a què serveix?"/>
<dialogue speaker="player" english="Sorry, I don&apos;t know!" translation="Em sap greu, però no ho sé!"/>
<dialogue speaker="player" english="They seem important, though..." translation="Semblen importants, però..."/>
<dialogue speaker="player" english="Maybe something will happen if we find them all?" translation="Potser passarà alguna cosa si els trobem tots?"/>
</cutscene>
<cutscene id="talkblue_trinket2" explanation="">
<dialogue speaker="blue" english="Captain! Come have a look at what I&apos;ve been working on!" translation="Cap! Mira en què he estat treballant!"/>
<dialogue speaker="blue" english="It looks like these shiny things are giving off a strange energy reading!" translation="Sembla que aquelles coses brillants tenen unes lectures denergia estranyes!"/>
<dialogue speaker="blue" english="So I analysed it..." translation="Així que ho he analitzat..."/>
</cutscene>
<cutscene id="talkblue_trinket3" explanation="">
<dialogue speaker="blue" english="Captain! Come have a look at what I&apos;ve been working on!" translation="Cap! Mira en què he estat treballant!"/>
<dialogue speaker="blue" english="I found this in that lab..." translation="He trobat això en aquell laboratori..."/>
<dialogue speaker="blue" english="It seemed to be giving off a weird energy reading..." translation="Tenia unes lectures denergia estranyes..."/>
<dialogue speaker="blue" english="So I analysed it..." translation="Així que ho he analitzat..."/>
</cutscene>
<cutscene id="talkblue_trinket4" explanation="">
<dialogue speaker="blue" english="...and I was able to find more of them with the ship&apos;s scanner!" translation="...i nhe descobert més amb lescàner de la nau!"/>
<dialogue speaker="blue" english="If you get a chance, it might be worth finding the rest of them!" translation="Si tés possible, potser estaria|bé trobar-ne la resta!"/>
<dialogue speaker="blue" english="Don&apos;t put yourself in any danger, though!" translation="Però no et posis en perill, eh?"/>
</cutscene>
<cutscene id="talkblue_trinket5" explanation="">
<dialogue speaker="blue" english="...but it looks like you&apos;ve already found all of them in this dimension!" translation="...però sembla que ja les has trobades totes en aquesta dimensió!"/>
<dialogue speaker="player" english="Oh? Really?" translation="Ah, sí? De debò?"/>
<dialogue speaker="blue" english="Yeah, well done! That can&apos;t have been easy!" translation="Sí, molt ben fet! Segur que no ha estat fàcil!"/>
</cutscene>
<cutscene id="talkblue_trinket6" explanation="">
<dialogue speaker="blue" english="...and they&apos;re related. They&apos;re all a part of something bigger!" translation="...i estan relacionades. Són part duna cosa més grossa!"/>
<dialogue speaker="player" english="Oh? Really?" translation="Ah, sí? De debò?"/>
<dialogue speaker="blue" english="Yeah! There seem to be twenty variations of the fundamental energy signature..." translation="Sí! Sembla que hi ha vint variacions de la signatura denergia fonamental..."/>
<dialogue speaker="blue" english="Wait..." translation="Un moment..."/>
<dialogue speaker="blue" english="Does that mean you&apos;ve found all of them?" translation="Això vol dir que les has trobades totes?"/>
</cutscene>
<cutscene id="talkyellow_1" explanation="">
<dialogue speaker="yellow" english="I&apos;m making some fascinating discoveries, captain!" translation="Estic fent uns descobriments fascinants, cap!"/>
</cutscene>
<cutscene id="talkyellow_2" explanation="">
<dialogue speaker="yellow" english="This isn&apos;t like any other dimension we&apos;ve been to, Captain." translation="Aquesta dimensió no és com les altres on hem estat, cap."/>
<dialogue speaker="yellow" english="There&apos;s something strange about this place..." translation="Aquest lloc és estrany, per algun motiu..."/>
</cutscene>
<cutscene id="talkyellow_3" explanation="">
<dialogue speaker="yellow" english="Captain, have you noticed that this dimension seems to wrap around?" translation="Cap, thas adonat que aquesta dimensió sembla que sigui cíclica? Quan en surts per un costat, apareixes per laltre."/>
<dialogue speaker="player" english="Yeah, it&apos;s strange..." translation="Sí, és estrany..."/>
<dialogue speaker="yellow" english="It looks like this dimension is having the same stability problems as our own!" translation="Sembla com si aquesta dimensió tingués els mateixos problemes destabilitat que nosaltres!"/>
<dialogue speaker="yellow" english="I hope we&apos;re not the ones causing it..." translation="Espero que no en siguem|els causants..."/>
<dialogue speaker="player" english="What? Do you think we might be?" translation="Què? Creus que podem ser-ho?"/>
<dialogue speaker="yellow" english="No no... that&apos;s very unlikely, really..." translation="No, no... És molt improbable, de fet..."/>
</cutscene>
<cutscene id="talkyellow_4" explanation="">
<dialogue speaker="yellow" english="My guess is that whoever used to live here was experimenting with ways to stop the dimension from collapsing." translation="Suposo que qui fos que vivia aquí experimentava amb maneres de fer que la dimensió no sesfondrés."/>
<dialogue speaker="yellow" english="It would explain why they&apos;ve wrapped the edges..." translation="Això explicaria per què les vores esdevenen cícliques..."/>
<dialogue speaker="yellow" english="Hey, maybe that&apos;s what&apos;s causing the interference?" translation="Escolta, potser és això, el que causa les interferències?"/>
</cutscene>
<cutscene id="talkyellow_5" explanation="">
<dialogue speaker="yellow" english="I wonder where the people who used to live here have gone?" translation="On deu haver anat, la gent que vivia aquí?"/>
</cutscene>
<cutscene id="talkyellow_6" explanation="">
<dialogue speaker="yellow" english="I think it&apos;s no coincidence that the teleporter was drawn to that dimension..." translation="Em penso que no és cap coincidència que el teletransportador anés a parar a aquella dimensió..."/>
<dialogue speaker="yellow" english="There&apos;s something there. I think it might be causing the interference that&apos;s stopping us from leaving..." translation="Allà hi ha alguna cosa. Potser és el que causa les interferències que no ens permeten anar-nos-en..."/>
</cutscene>
<cutscene id="talkyellow_7" explanation="">
<dialogue speaker="yellow" english="I&apos;m glad Verdigris is alright." translation="Malegro que en Verdet estigui bé."/>
<dialogue speaker="yellow" english="It&apos;ll be a lot easier to find some way out of here now that we can get the ship working again!" translation="Ara que podem fer que la nau torni a funcionar, serà molt més fàcil trobar una manera de sortir daquí!"/>
</cutscene>
<cutscene id="talkyellow_8" explanation="">
<dialogue speaker="yellow" english="Ah, you&apos;ve found Doctor Victoria? Excellent!" translation="Ah, has trobat la doctora Victòria? Excel·lent!"/>
<dialogue speaker="yellow" english="I have lots of questions for her!" translation="Li he de fer un munt de qüestions!"/>
</cutscene>
<cutscene id="talkyellow_9" explanation="">
<dialogue speaker="yellow" english="Vermilion says that he was trapped in some sort of tunnel?" translation="En Vermelló diu que estava atrapat en una mena de túnel?"/>
<dialogue speaker="player" english="Yeah, it just seemed to keep going and going..." translation="Sí, sembla que es repetia una vegada rere laltra..."/>
<dialogue speaker="yellow" english="Interesting... I wonder why it was built?" translation="Interessant... Per què el devien construir?"/>
</cutscene>
<cutscene id="talkyellow_10" explanation="">
<dialogue speaker="yellow" english="It&apos;s good to be back!" translation="Malegro de tornar a ser aquí!"/>
<dialogue speaker="yellow" english="I&apos;ve got so much work to catch up on..." translation="Tinc molta feina pendent..."/>
</cutscene>
<cutscene id="talkyellow_11" explanation="">
<dialogue speaker="yellow" english="I know it&apos;s probably a little dangerous to stay here now that this dimension is collapsing..." translation="Suposo que és una mica perillós estar-nos aquí ara que la dimensió sestà esfondrant..."/>
<dialogue speaker="yellow" english="...but it&apos;s so rare to find somewhere this interesting!" translation="Però és molt poc habitual trobar|un lloc tan interessant!"/>
<dialogue speaker="yellow" english="Maybe we&apos;ll find the answers to our own problems here?" translation="Potser hi trobarem les respostes als nostres propis problemes?"/>
</cutscene>
<cutscene id="talkyellow_trinket1" explanation="">
<dialogue speaker="yellow" english="Captain! I&apos;ve been meaning to give this to you..." translation="Cap! Feia temps que et volia donar això..."/>
<dialogue speaker="player" english="Professor! Where did you find this?" translation="Professor! On ho has trobat?"/>
<dialogue speaker="yellow" english="Oh, it was just lying around that space station." translation="Ah, era en aquella estació espacial."/>
<dialogue speaker="yellow" english="It&apos;s a pity Doctor Victoria isn&apos;t here, she loves studying that sort of thing..." translation="És una llàstima que no hi hagi la doctora Victòria. Li encanta estudiar aquesta mena de coses..."/>
<dialogue speaker="player" english="Any idea what it does?" translation="Tens cap idea de per a què serveixen?"/>
<dialogue speaker="yellow" english="Nope! But it is giving off a strange energy reading..." translation="No! Però tenen unes lectures denergia estranyes..."/>
</cutscene>
<cutscene id="talkyellow_trinket2" explanation="">
<dialogue speaker="yellow" english="...so I used the ship&apos;s scanner to find more of them!" translation="...així que he fet servir lescàner de la nau per a trobar-ne més!"/>
<dialogue speaker="yellow" english="...Please don&apos;t let them distract you from finding Victoria, though!" translation="Però... que no et distreguin de trobar la Victòria, dacord?"/>
<dialogue speaker="yellow" english="I hope she&apos;s ok..." translation="Espero que estigui bé..."/>
</cutscene>
<cutscene id="talkyellow_trinket3" explanation="">
<dialogue speaker="yellow" english="Can&apos;t seem to detect any more of them nearby, though." translation="Sembla que no en detecto|cap més a prop."/>
<dialogue speaker="yellow" english="Maybe you&apos;ve found them all?" translation="Potser ja les has trobades totes?"/>
</cutscene>
<cutscene id="finallevel_teleporter" explanation="">
<dialogue speaker="purple" english="Welcome back!" translation="Que bé que tornis a ser aquí!"/>
<dialogue speaker="purple" english="..." translation="..."/>
<dialogue speaker="purple" english="Um, where&apos;s Captain Viridian?" translation="Daixò... Cap Viridis, on ets?"/>
<dialogue speaker="player" english="... Hello?" translation="Hola...?"/>
<dialogue speaker="player" english="Is anyone there?" translation="Que hi ha algú?"/>
</cutscene>
<cutscene id="terminal_finallevel" explanation="">
<dialogue speaker="gray" english="* DIMENSIONAL STABILITY GENERATOR *
[ Currently Generating ]
Maximum Stability
[ Status ]
Online
READY _" translation="*** GENERADOR DESTABILITAT ***
DIMENSIONAL
[ Actualment sestà generant ]
Estabilitat màxima
[ Estat ]
Activat
A PUNT _" tt="1"/>
<dialogue speaker="cyan" english="Aha! This must be what&apos;s causing the interference!" translation="Ahà! Això deu ser el que causa les interferències!"/>
<dialogue speaker="cyan" english="I wonder if I can turn it off?" translation="Potser el podria apagar?"/>
<dialogue speaker="gray" english="WARNING: Disabling the Dimensional Stability Generator may lead to instability! Are you sure you want to do this?" translation="ADVERTIMENT: Si desactiveu el generador destabilitat dimensional, podeu generar inestabilitat!|Segur que voleu fer això?"/>
<dialogue speaker="cyan" english="Yes!" translation="Sí!" case="1"/>
<dialogue speaker="gray" english="Seriously! The whole dimension could collapse! Just think about this for a minute!
Are you really sure you want to do this?" translation="Ara de debò! La dimensió sencera es podria esfondrar! Penseu-hi durant uns minuts!
Segur que voleu fer això?"/>
<dialogue speaker="cyan" english="Yes!" translation="Sí!" case="2"/>
</cutscene>
<cutscene id="finalterminal_finish" explanation="">
<dialogue speaker="gray" english="-= WARNING =-
DIMENSIONAL STABILISER OFFLINE" translation="-= ADVERTIMENT =-
ESTABILITZADOR DIMENSIONAL DESCONNECTAT" centertext="1" pad="1"/>
<dialogue speaker="cyan" english="Uh oh..." translation="Ups..."/>
</cutscene>
<cutscene id="gamecomplete" explanation="">
<dialogue speaker="yellow" english="Any moment now..." translation="Hauria daparèixer en qualsevol moment..."/>
</cutscene>
<cutscene id="gamecomplete_ending" explanation="">
<dialogue speaker="player" english="Hello!" translation="Ep!"/>
<dialogue speaker="purple" english="Captain!" translation="Cap!" case="1" pad_right="8"/>
<dialogue speaker="yellow" english="Captain!" translation="Cap!" case="2" pad_right="6"/>
<dialogue speaker="red" english="Captain!" translation="Cap!" case="3" pad_right="4"/>
<dialogue speaker="green" english="Captain!" translation="Cap!" case="4" pad_right="2"/>
<dialogue speaker="blue" english="Captain!" translation="Cap!" case="5"/>
<dialogue speaker="blue" english="You&apos;re alright!" translation="Estàs bé!"/>
<dialogue speaker="blue" english="I knew you&apos;d be ok!" translation="Sabia que ten sortiries!"/>
<dialogue speaker="purple" english="We were very worried when you didn&apos;t come back..." translation="Patíem molt per si no tornaves..."/>
<dialogue speaker="green" english="...but when you turned off the source of the interference..." translation="...però quan has desactivat lorigen de les interferències..."/>
<dialogue speaker="yellow" english="...we were able to find you with the ship&apos;s scanners..." translation="...hem pogut localitzar-te amb els escàners de la nau..."/>
<dialogue speaker="red" english="...and teleport you back on board!" translation="...i teletransportar-te de nou a bord!"/>
<dialogue speaker="player" english="That was lucky!" translation="Quina sort!"/>
<dialogue speaker="player" english="Thanks guys!" translation="Gràcies, nois!"/>
<dialogue speaker="yellow" english="...it looks like this dimension is starting to destabilise, just like our own..." translation="...sembla que aquesta dimensió sestà començant a desestabilitzar, igual que la nostra..."/>
<dialogue speaker="red" english="...we can stay and explore for a little longer, but..." translation="Ens hi podem estar i explorar una mica més, però..."/>
<dialogue speaker="yellow" english="...eventually, it&apos;ll collapse completely." translation="...tard o dhora, sesfondrarà per complet."/>
<dialogue speaker="green" english="There&apos;s no telling exactly how long we have here. But the ship&apos;s fixed, so..." translation="No sabem exactament quant de temps tenim. Però la nau està reparada, així que..."/>
<dialogue speaker="blue" english="...as soon as we&apos;re ready, we can go home!" translation="...tan bon punt estiguem llestos,|podrem tornar a casa!"/>
<dialogue speaker="purple" english="What now, Captain?" translation="Què hi ha, cap?"/>
<dialogue speaker="player" english="Let&apos;s find a way to save this dimension!" translation="Trobem una manera de salvar aquesta dimensió!"/>
<dialogue speaker="player" english="And a way to save our home dimension too!" translation="I una manera de salvar la nostra, alhora!"/>
<dialogue speaker="player" english="The answer is out there, somewhere!" translation="Segur que la resposta és allà fora, en algun lloc!"/>
<dialogue speaker="player" english="Let&apos;s go!" translation="Som-hi!"/>
</cutscene>
<cutscene id="startepilogue" explanation="">
<dialogue speaker="blue" english="Wow! You found all of them!" translation="Bufa! Les has trobades totes!"/>
<dialogue speaker="player" english="Really? Great!" translation="De debò? Fantàstic!"/>
<dialogue speaker="blue" english="I&apos;ll run some tests and see if I can work out what they&apos;re for..." translation="Faré alguns experiments i miraré de descobrir per a què serveixen..."/>
<dialogue speaker="player" english="That... that didn&apos;t sound good..." translation="Això... no ha sonat gaire bé..."/>
<dialogue speaker="blue" english="Run!" translation="Correu!"/>
<dialogue speaker="player" english="Oh no!" translation="Oh, no!"/>
<dialogue speaker="red" english="Not again!" translation="Una altra vegada no!"/>
<dialogue speaker="player" english="Wait! It&apos;s stopped!" translation="Espereu! Sha aturat!"/>
<dialogue speaker="purple" english="This is where we were storing those shiny things? What happened?" translation="Aquí és on desàvem les coses brillants aquelles, oi? Què ha passat?"/>
<dialogue speaker="player" english="We were just playing with them, and..." translation="Hi estàvem jugant, i..."/>
<dialogue speaker="player" english="...they suddenly exploded!" translation="...han explotat de sobte!"/>
<dialogue speaker="blue" english="But look what they made! Is that a teleporter?" translation="Però mireu què han fet! És un teletransportador?"/>
<dialogue speaker="yellow" english="I think so, but..." translation="Això sembla, però..."/>
<dialogue speaker="yellow" english="I&apos;ve never seen a teleporter like that before..." translation="No he vist mai un teletransportador així..."/>
<dialogue speaker="red" english="We should investigate!" translation="Ho hauríem dinvestigar!"/>
<dialogue speaker="purple" english="What do you think, Captain?" translation="Què en penses, cap?"/>
<dialogue speaker="purple" english="Should we find out where it leads?" translation="Hauríem dinvestigar on porta?"/>
<dialogue speaker="player" english="Let&apos;s go!" translation="Som-hi!"/>
<dialogue speaker="blue" english="Oh no! We&apos;re trapped!" translation="Oh, no! Estem atrapats!"/>
<dialogue speaker="yellow" english="Oh dear..." translation="Ai, mare..."/>
<dialogue speaker="player" english="Hmm... how should we get out of this?" translation="Hum... Com ens en podem sortir?"/>
<dialogue speaker="player" english="COMBINE!" translation="FUSIÓ!" case="1"/>
<dialogue speaker="purple" english="COMBINE!" translation="FUSIÓ!" case="2"/>
<dialogue speaker="yellow" english="COMBINE!" translation="FUSIÓ!" case="3"/>
<dialogue speaker="red" english="COMBINE!" translation="FUSIÓ!" case="4"/>
<dialogue speaker="green" english="COMBINE!" translation="FUSIÓ!" case="5"/>
<dialogue speaker="blue" english="COMBINE!" translation="FUSIÓ!" case="6"/>
<dialogue speaker="purple" english="Or, you know... we could have just warped back to the ship..." translation="O bé... també hauríem pogut teletransportar-nos a la nau..."/>
<dialogue speaker="green" english="Wow! What is this?" translation="Ostres! Què és això?"/>
<dialogue speaker="yellow" english="It looks like another laboratory!" translation="Sembla un altre laboratori!"/>
<dialogue speaker="red" english="Let&apos;s have a look around!" translation="Donem-hi un cop dull!"/>
</cutscene>
<cutscene id="talkpurple_9" explanation="">
<dialogue speaker="purple" english="Look at all this research! This is going to be a big help back home!" translation="Mira quanta recerca! Això ens serà de gran ajuda quan tornem a casa!"/>
</cutscene>
<cutscene id="talkgreen_11" explanation="">
<dialogue speaker="green" english="I wonder why they abandoned this dimension? They were so close to working out how to fix it..." translation="Per què devien abandonar aquesta dimensió? Eren molt a prop de descobrir com reparar-la..."/>
<dialogue speaker="green" english="Maybe we can fix it for them? Maybe they&apos;ll come back?" translation="Potser la podem reparar en nom seu? Potser tornaran?"/>
</cutscene>
<cutscene id="talkblue_9" explanation="">
<dialogue speaker="blue" english="This lab is amazing! The scientists who worked here know a lot more about warp technology than we do!" translation="Aquest laboratori és increïble! Els científics que hi treballaven sabien moltes més coses de la tecnologia del teletransport que no pas nosaltres!"/>
</cutscene>
<cutscene id="talkyellow_12" explanation="">
<dialogue speaker="yellow" english="Captain! Have you seen this?" translation="Cap! Has vist això?"/>
<dialogue speaker="yellow" english="With their research and ours, we should be able to stabilise our own dimension!" translation="Si ajuntem les seves investigacions i les nostres, potser podríem estabilitzar la nostra dimensió!"/>
<dialogue speaker="yellow" english="We&apos;re saved!" translation="Estem salvats!"/>
</cutscene>
<cutscene id="talkred_14" explanation="super gravitron (difficult minigame)">
<dialogue speaker="red" english="Look what I found!" translation="Mira què he trobat!"/>
<dialogue speaker="red" english="It&apos;s pretty hard, I can only last for about 10 seconds..." translation="És força difícil, només hi he durat uns deu segons..."/>
</cutscene>
<cutscene id="terminal_jukebox" explanation="">
<dialogue speaker="gray" english="-= JUKEBOX =-
Songs will continue to play until you leave the ship.
Collect trinkets to unlock new songs!" translation="-= TOCADISCOS =-
Les cançons continuaran sonant fins que surtis de la nau.
Recull lluentons per a desblocar cançons noves!" centertext="1" padtowidth="264"/>
</cutscene>
<cutscene id="terminal_jukeunlock1" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
5 Trinkets
Pushing Onwards" translation="SEGÜENT PISTA A DESBLOCAR:
5 lluentons
Prement sempre endavant" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock2" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
8 Trinkets
Positive Force" translation="SEGÜENT PISTA A DESBLOCAR:
8 lluentons
Positivitat en la força" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock3" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
10 Trinkets
Presenting VVVVVV" translation="SEGÜENT PISTA A DESBLOCAR:
10 lluentons
Presentació de VVVVVV" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock4" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
12 Trinkets
Potential for Anything" translation="SEGÜENT PISTA A DESBLOCAR:
12 lluentons
Potencial per a qualsevol cosa" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock41" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
14 Trinkets
Pressure Cooker" translation="SEGÜENT PISTA A DESBLOCAR:
14 lluentons
Pressió dins lolla" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock5" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
16 Trinkets
Predestined Fate" translation="SEGÜENT PISTA A DESBLOCAR:
16 lluentons
Predestinació atzarosa" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock6" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
18 Trinkets
Popular Potpourri" translation="SEGÜENT PISTA A DESBLOCAR:
18 lluentons
Popurri popular" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_jukeunlock7" explanation="">
<dialogue speaker="gray" english="NEXT UNLOCK:
20 Trinkets
Pipe Dream" translation="SEGÜENT PISTA A DESBLOCAR:
20 lluentons
Pretensió impossible" tt="1" pad="1"/>
</cutscene>
<cutscene id="terminal_station_1" explanation="">
<dialogue speaker="gray" english="-= PERSONAL LOG =-" translation="-= DIARI PERSONAL =-" padtowidth="280"/>
<dialogue speaker="gray" english="Almost everyone has been evacuated from the space station now. The rest of us are leaving in a couple of days, once our research has been completed." translation="Ja han evacuat gairebé tothom de lestació espacial. La resta|ens nanirem daquí a un parell de dies, quan hàgim acabat la recerca." pad="1"/>
</cutscene>
<cutscene id="terminal_station_2" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= NOTES DE RECERCA =-" padtowidth="264"/>
<dialogue speaker="gray" english="...everything collapses, eventually. It&apos;s the way of the universe." translation="...tot sacaba esfondrant, tard o dhora. Lunivers és així." centertext="1" pad="1"/>
</cutscene>
<cutscene id="terminal_station_3" explanation="">
<dialogue speaker="gray" english="I wonder if the generator we set up in the polar dimension is what&apos;s affecting our teleporters?" translation="Potser el que afecta els nostres|teletransportadors és el generador|que vam instal·lar a la dimensió polar?"/>
<dialogue speaker="gray" english="No, it&apos;s probably just a glitch." translation="No, segurament només és un error."/>
</cutscene>
<cutscene id="terminal_station_4" explanation="a trinket that&apos;s difficult to get">
<dialogue speaker="gray" english="-= PERSONAL LOG =-" translation="-= DIARI PERSONAL =-" padtowidth="280"/>
<dialogue speaker="gray" english="Hah! Nobody will ever get this one." translation="Ha! Aquest no lagafarà mai ningú." pad="1"/>
</cutscene>
<cutscene id="terminal_warp_1" explanation="">
<dialogue speaker="gray" english="...The other day I was chased down a hallway by a giant cube with the word AVOID on it." translation="Laltre dia un cub gegant amb la paraula EVITA em va perseguir per una sala."/>
<dialogue speaker="gray" english="These security measures go too far!" translation="Aquestes mesures de seguretat són massa estrictes!"/>
</cutscene>
<cutscene id="terminal_warp_2" explanation="">
<dialogue speaker="gray" english="The only way into my private lab anymore is by teleporter." translation="Lúnica manera dentrar al meu laboratori privat és amb un teletransportador."/>
<dialogue speaker="gray" english="I&apos;ve made sure that it&apos;s difficult for unauthorised personnel to gain access." translation="Mhe assegurat que sigui difícil que personal no autoritzat hi obtingui accés."/>
</cutscene>
<cutscene id="terminal_outside_1" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= NOTES DE RECERCA =-" padtowidth="264"/>
<dialogue speaker="gray" english="... our first breakthrough was the creation of the inversion plane, which creates a mirrored dimension beyond a given event horizon ..." translation="...el nostre primer avenç va ser la creació del pla dinversió, que crea una dimensió mirall més enllà dun horitzó desdeveniments|concret..." pad="1"/>
</cutscene>
<cutscene id="terminal_outside_2" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= NOTES DE RECERCA =-" padtowidth="264"/>
<dialogue speaker="gray" english="...with just a small modification to the usual parameters, we were able to stabilise an infinite tunnel!" translation="...sols amb una petita modificació dels paràmetres normals, vam poder estabilitzar un túnel infinit!"/>
</cutscene>
<cutscene id="terminal_outside_3" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= NOTES DE RECERCA =-" padtowidth="264"/>
<dialogue speaker="gray" english="... the final step in creating the dimensional stabiliser was to create a feedback loop ..." translation="...lúltim pas a lhora de crear lestabilitzador dimensional va ser crear un bucle de retroalimentació..." pad="1"/>
</cutscene>
<cutscene id="terminal_outside_4" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= NOTES DE RECERCA =-" padtowidth="264"/>
<dialogue speaker="gray" english="...despite our best efforts, the dimensional stabiliser won&apos;t hold out forever. Its collapse is inevitable..." translation="...malgrat els nostres esforços, lestabilitzador dimensional no funcionarà eternament. En algun moment, sesfondrarà..." pad="1"/>
<dialogue speaker="cyan" english="Huh? These coordinates aren&apos;t even in this dimension!" translation="Eh? Aquestes coordenades no són ni tan sols en aquesta dimensió!"/>
</cutscene>
<cutscene id="terminal_outside_5" explanation="">
<dialogue speaker="gray" english="-= Personal Log =-" translation="-= DIARI PERSONAL =-" padtowidth="232"/>
<dialogue speaker="gray" english="... I&apos;ve had to seal off access to most of our research. Who knows what could happen if it fell into the wrong hands? ..." translation="...he hagut de segellar laccés a la major part de la recerca. Qui sap què podria passar si caigués en les mans equivocades..." centertext="1" pad="1"/>
</cutscene>
<cutscene id="terminal_outside_6" explanation="">
<dialogue speaker="gray" english="-= Research Notes =-" translation="-= NOTES DE RECERCA =-" padtowidth="264"/>
<dialogue speaker="gray" english="... access to the control center is still possible through the main atmospheric filters ..." translation="...laccés al centre de control encara és possible a través dels filtres atmosfèrics principals..."/>
</cutscene>
<cutscene id="terminal_lab_1" explanation="">
<dialogue speaker="gray" english="... it turns out the key to stabilising this dimension was to create a balancing force outside of it!" translation="...resulta que la clau per a estabilitzar aquesta dimensió era crear una força equilibradora fora de la dimensió!"/>
<dialogue speaker="gray" english="Though it looks like that&apos;s just a temporary solution, at best." translation="Tot i que això sembla només una solució temporal, com a molt."/>
<dialogue speaker="gray" english="I&apos;ve been working on something more permanent, but it seems it&apos;s going to be too late..." translation="He estat treballant en una cosa més permanent, però sembla que ja serà massa tard..."/>
</cutscene>
<cutscene id="terminal_lab_2" explanation="">
<dialogue speaker="gray" english="?SYNTAX ERROR" translation="?ERROR DE SINTAXI"/>
</cutscene>
<cutscene id="terminal_letsgo" explanation="">
<dialogue speaker="player" english="Now that the ship is fixed, we can leave anytime we want!" translation="Ara que la nau ja està reparada, ens en podem anar quan vulguem!"/>
<dialogue speaker="player" english="We&apos;ve all agreed to keep exploring this dimension, though." translation="Però tots hem estat dacord a continuar explorant aquesta dimensió."/>
<dialogue speaker="player" english="Who knows what we&apos;ll find?" translation="Qui sap què hi trobarem!"/>
</cutscene>
<cutscene id="terminal_radio" explanation="">
<dialogue speaker="gray" english="-= SHIP RADIO =-
[ Status ]
Broadcasting" translation="-= RÀDIO DE LA NAU =-
[ Estat ]
Sestà transmetent" tt="1" centertext="1" pad="2"/>
</cutscene>
<cutscene id="terminal_secretlab" explanation="">
<dialogue speaker="gray" english="-= WARNING =-
The Super-Gravitron is intended for entertainment purposes only." translation="-= ADVERTIMENT =-
El supergravitró només té finalitats dentreteniment." centertext="1" pad="1"/>
<dialogue speaker="gray" english="Anyone found using the Super Gravitron for educational purposes may be asked to stand in the naughty corner." translation="Si algú el fa servir per a finalitats educatives, és|possible que lenviem al racó de pensar."/>
</cutscene>
<cutscene id="terminal_shipcomputer" explanation="">
<dialogue speaker="gray" english="-= D.S.S. SOULEYE =-
Ship Navigation Controls" translation="-= DSS SOULEYE =-
Controls de navegació de la nau" centertext="1" pad="1"/>
<dialogue speaker="gray" english="Error! Error! Cannot isolate dimensional coordinates! Interference detected!" translation="Error! Error! No és possible aïllar les coordenades dimensionals! Shan detectat interferències!"/>
</cutscene>
<cutscene id="alreadyvisited" explanation="">
<dialogue speaker="cyan" english="...oh, I&apos;ve already found this." translation="Oh, això ja ho havia trobat..."/>
</cutscene>
<cutscene id="disableaccessibility" explanation="">
<dialogue speaker="gray" english="Please disable invincibility and/or slowdown before entering the Super Gravitron." translation="Desactiva la invencibilitat i/o lalentiment abans dentrar al supergravitró."/>
</cutscene>
</cutscenes>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<spritesmask sprite_w="32" sprite_h="32">
<sprite x="4" y="3" w="4"/> <!-- YES -->
<sprite x="3" y="4"/> <!-- OBEY -->
<sprite x="2" y="5" w="2"/> <!-- LIES receiver and LIES -->
<sprite x="4" y="5" w="2"/> <!-- TRUTH -->
</spritesmask>

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<langmeta>
<active>1</active>
<!-- should be lowercase because menu style, and should be in the language itself -->
<nativename>català</nativename>
<!-- English translation by X -->
<credit>Traducció al català|dEduard Ereza Martínez</credit>
<!-- On the language screen, hard limit 40 8x8 characters. Space/Z/V sets this as the language -->
<action_hint>Prem Espai, Z o V per a seleccionar</action_hint>
<!-- Same as above, but for a gamepad button (hard limit 40 8x8 characters) -->
<gamepad_hint>Prem {button} per a seleccionar</gamepad_hint>
<!-- Enable automatic word wrapping instead of having to manually insert newlines -->
<autowordwrap>1</autowordwrap>
<!-- Enable automatic full-caps display of selected menu options ([SELECTED] not selected) -->
<toupper>1</toupper>
<!-- When automatically uppercasing, map i to İ (for Turkish) -->
<toupper_i_dot>0</toupper_i_dot>
<!-- When automatically uppercasing, allow ~ to be used to stop the next letter from being uppercased (for Irish) -->
<toupper_lower_escape_char>0</toupper_lower_escape_char>
<!-- Enable for RTL languages like Arabic or Hebrew -->
<rtl>0</rtl>
<!-- The indication that a certain menu option or button is selected -->
<menu_select>[ {label} ]</menu_select>
<menu_select_tight>[{label}]</menu_select_tight>
<!-- The filename of the font to use. For example, "font_cn" means font_cn.png and font_cn.fontmeta. -->
<font>font</font>
</langmeta>

View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Please read README.txt for information about the language files -->
<numbers>
<number value="0" form="0" english="Zero" translation="zero"/>
<number value="1" form="1" english="One" translation="un"/>
<number value="2" form="0" english="Two" translation="dos"/>
<number value="3" form="0" english="Three" translation="tres"/>
<number value="4" form="0" english="Four" translation="quatre"/>
<number value="5" form="0" english="Five" translation="cinc"/>
<number value="6" form="0" english="Six" translation="sis"/>
<number value="7" form="0" english="Seven" translation="set"/>
<number value="8" form="0" english="Eight" translation="vuit"/>
<number value="9" form="0" english="Nine" translation="nou"/>
<number value="10" form="0" english="Ten" translation="deu"/>
<number value="11" form="0" english="Eleven" translation="onze"/>
<number value="12" form="0" english="Twelve" translation="dotze"/>
<number value="13" form="0" english="Thirteen" translation="tretze"/>
<number value="14" form="0" english="Fourteen" translation="catorze"/>
<number value="15" form="0" english="Fifteen" translation="quinze"/>
<number value="16" form="0" english="Sixteen" translation="setze"/>
<number value="17" form="0" english="Seventeen" translation="disset"/>
<number value="18" form="0" english="Eighteen" translation="divuit"/>
<number value="19" form="0" english="Nineteen" translation="dinou"/>
<number value="20" form="0" english="Twenty" translation="vint"/>
<number value="21" form="0" english="Twenty One" translation="vint-i-un"/>
<number value="22" form="0" english="Twenty Two" translation="vint-i-dos"/>
<number value="23" form="0" english="Twenty Three" translation="vint-i-tres"/>
<number value="24" form="0" english="Twenty Four" translation="vint-i-quatre"/>
<number value="25" form="0" english="Twenty Five" translation="vint-i-cinc"/>
<number value="26" form="0" english="Twenty Six" translation="vint-i-sis"/>
<number value="27" form="0" english="Twenty Seven" translation="vint-i-set"/>
<number value="28" form="0" english="Twenty Eight" translation="vint-i-vuit"/>
<number value="29" form="0" english="Twenty Nine" translation="vint-i-nou"/>
<number value="30" form="0" english="Thirty" translation="trenta"/>
<number value="31" form="0" english="Thirty One" translation="trenta-un"/>
<number value="32" form="0" english="Thirty Two" translation="trenta-dos"/>
<number value="33" form="0" english="Thirty Three" translation="trenta-tres"/>
<number value="34" form="0" english="Thirty Four" translation="trenta-quatre"/>
<number value="35" form="0" english="Thirty Five" translation="trenta-cinc"/>
<number value="36" form="0" english="Thirty Six" translation="trenta-sis"/>
<number value="37" form="0" english="Thirty Seven" translation="trenta-set"/>
<number value="38" form="0" english="Thirty Eight" translation="trenta-vuit"/>
<number value="39" form="0" english="Thirty Nine" translation="trenta-nou"/>
<number value="40" form="0" english="Forty" translation="quaranta"/>
<number value="41" form="0" english="Forty One" translation="quaranta-un"/>
<number value="42" form="0" english="Forty Two" translation="quaranta-dos"/>
<number value="43" form="0" english="Forty Three" translation="quaranta-tres"/>
<number value="44" form="0" english="Forty Four" translation="quaranta-quatre"/>
<number value="45" form="0" english="Forty Five" translation="quaranta-cinc"/>
<number value="46" form="0" english="Forty Six" translation="quaranta-sis"/>
<number value="47" form="0" english="Forty Seven" translation="quaranta-set"/>
<number value="48" form="0" english="Forty Eight" translation="quaranta-vuit"/>
<number value="49" form="0" english="Forty Nine" translation="quaranta-nou"/>
<number value="50" form="0" english="Fifty" translation="cinquanta"/>
<number value="51" form="0" english="Fifty One" translation="cinquanta-un"/>
<number value="52" form="0" english="Fifty Two" translation="cinquanta-dos"/>
<number value="53" form="0" english="Fifty Three" translation="cinquanta-tres"/>
<number value="54" form="0" english="Fifty Four" translation="cinquanta-quatre"/>
<number value="55" form="0" english="Fifty Five" translation="cinquanta-cinc"/>
<number value="56" form="0" english="Fifty Six" translation="cinquanta-sis"/>
<number value="57" form="0" english="Fifty Seven" translation="cinquanta-set"/>
<number value="58" form="0" english="Fifty Eight" translation="cinquanta-vuit"/>
<number value="59" form="0" english="Fifty Nine" translation="cinquanta-nou"/>
<number value="60" form="0" english="Sixty" translation="seixanta"/>
<number value="61" form="0" english="Sixty One" translation="seixanta-un"/>
<number value="62" form="0" english="Sixty Two" translation="seixanta-dos"/>
<number value="63" form="0" english="Sixty Three" translation="seixanta-tres"/>
<number value="64" form="0" english="Sixty Four" translation="seixanta-quatre"/>
<number value="65" form="0" english="Sixty Five" translation="seixanta-cinc"/>
<number value="66" form="0" english="Sixty Six" translation="seixanta-sis"/>
<number value="67" form="0" english="Sixty Seven" translation="seixanta-set"/>
<number value="68" form="0" english="Sixty Eight" translation="seixanta-vuit"/>
<number value="69" form="0" english="Sixty Nine" translation="seixanta-nou"/>
<number value="70" form="0" english="Seventy" translation="setanta"/>
<number value="71" form="0" english="Seventy One" translation="setanta-un"/>
<number value="72" form="0" english="Seventy Two" translation="setanta-dos"/>
<number value="73" form="0" english="Seventy Three" translation="setanta-tres"/>
<number value="74" form="0" english="Seventy Four" translation="setanta-quatre"/>
<number value="75" form="0" english="Seventy Five" translation="setanta-cinc"/>
<number value="76" form="0" english="Seventy Six" translation="setanta-sis"/>
<number value="77" form="0" english="Seventy Seven" translation="setanta-set"/>
<number value="78" form="0" english="Seventy Eight" translation="setanta-vuit"/>
<number value="79" form="0" english="Seventy Nine" translation="setanta-nou"/>
<number value="80" form="0" english="Eighty" translation="vuitanta"/>
<number value="81" form="0" english="Eighty One" translation="vuitanta-un"/>
<number value="82" form="0" english="Eighty Two" translation="vuitanta-dos"/>
<number value="83" form="0" english="Eighty Three" translation="vuitanta-tres"/>
<number value="84" form="0" english="Eighty Four" translation="vuitanta-quatre"/>
<number value="85" form="0" english="Eighty Five" translation="vuitanta-cinc"/>
<number value="86" form="0" english="Eighty Six" translation="vuitanta-sis"/>
<number value="87" form="0" english="Eighty Seven" translation="vuitanta-set"/>
<number value="88" form="0" english="Eighty Eight" translation="vuitanta-vuit"/>
<number value="89" form="0" english="Eighty Nine" translation="vuitanta-nou"/>
<number value="90" form="0" english="Ninety" translation="noranta"/>
<number value="91" form="0" english="Ninety One" translation="noranta-un"/>
<number value="92" form="0" english="Ninety Two" translation="noranta-dos"/>
<number value="93" form="0" english="Ninety Three" translation="noranta-tres"/>
<number value="94" form="0" english="Ninety Four" translation="noranta-quatre"/>
<number value="95" form="0" english="Ninety Five" translation="noranta-cinc"/>
<number value="96" form="0" english="Ninety Six" translation="noranta-sis"/>
<number value="97" form="0" english="Ninety Seven" translation="noranta-set"/>
<number value="98" form="0" english="Ninety Eight" translation="noranta-vuit"/>
<number value="99" form="0" english="Ninety Nine" translation="noranta-nou"/>
<number value="100" form="0" english="One Hundred" translation="cent"/>
<number value="101" form="0"/>
<number value="102" form="0"/>
<number value="103" form="0"/>
<number value="104" form="0"/>
<number value="105" form="0"/>
<number value="106" form="0"/>
<number value="107" form="0"/>
<number value="108" form="0"/>
<number value="109" form="0"/>
<number value="110" form="0"/>
<number value="111" form="0"/>
<number value="112" form="0"/>
<number value="113" form="0"/>
<number value="114" form="0"/>
<number value="115" form="0"/>
<number value="116" form="0"/>
<number value="117" form="0"/>
<number value="118" form="0"/>
<number value="119" form="0"/>
</numbers>

View file

@ -0,0 +1,193 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You can translate these in-game to get better context! See README.txt -->
<roomnames>
<roomname x="0" y="18" english="Single-slit Experiment" translation="Experiment de lúnica escletxa" explanation="(Many of the rooms in the Lab stage have science themed names.)"/>
<roomname x="0" y="19" english="Don&apos;t Flip Out" translation="Cap per avall i cap per amunt" explanation="Flip as in gravity flip, but also the expression in english, as in, keep your cool"/>
<roomname x="1" y="0" english="I&apos;m Sorry" translation="Em sap greu..." explanation="The room below this one is Please Forgive Me. There is also a secret path to the right which leads to the rooms &apos;Anomaly&apos; and &apos;Purest Unobtainium&apos;."/>
<roomname x="1" y="1" english="Please Forgive Me!" translation="Perdonam, si us plau!" explanation="The room above this one is I&apos;m Sorry"/>
<roomname x="1" y="17" english="Rascasse" translation="Escórpora" explanation="This is a type of fish with lots of thorny spikes on its back"/>
<roomname x="1" y="18" english="Keep Going" translation="Continua endavant" explanation="Literally just &apos;Keep Going&apos;, through the room"/>
<roomname x="1" y="19" english="Shuffled Hallway" translation="Corredor remogut" explanation="Just describes how the room looks. &apos;Shuffled&apos; as in offset."/>
<roomname x="2" y="0" english="Kids His Age Bounce" translation="Els nens daquesta edat són de goma" explanation="An old saying for when e.g. a small child falls out of a tree. A trampoline joke in this case."/>
<roomname x="2" y="1" english="Playing Foosball" translation="Hora de jugar al futbolí" explanation="This room resembles a Foosball table - https://en.wikipedia.org/wiki/Table_football"/>
<roomname x="2" y="4" english="Philadelphia Experiment" translation="Lexperiment Filadèlfia" explanation="There is a teleporter in this room. The Philadelphia Experiment is the name of 80&apos;s film about teleportation."/>
<roomname x="2" y="16" english="Get Ready To Bounce" translation="Preparat per a rebotar" explanation="The first room in the Lab zone. In the next room, you immediately come into contact with a gravity line, and flip back up."/>
<roomname x="2" y="17" english="It&apos;s Perfectly Safe" translation="No pateixis, és totalment segur" explanation="The second room in the lab zone. Don&apos;t worry, it&apos;s perfectly safe."/>
<roomname x="2" y="18" english="Young Man, It&apos;s Worth the Challenge" translation="Xiquets, el desafiament paga la pena" explanation="Not a reference to anything in particular - Bennett explains that this is just something that his high school chemistry teacher used to say to students. He thinks the teacher was probably misquoting George Bernard Shaw, who said &apos;Life is not meant to be easy, my child - but take courage: it can be delightful.&apos;."/>
<roomname x="2" y="19" english="Double-slit Experiment" translation="Experiment de la doble escletxa" explanation="(Many of the rooms in the Lab stage have science themed names.)"/>
<roomname x="3" y="0" english="Merge" translation="Estrenyiment" explanation="A wide section connecting to a narrower section - merge as in traffic"/>
<roomname x="3" y="1" english="A Difficult Chord" translation="Uns acords difícils" explanation="This room resembles a guitar chord"/>
<roomname x="3" y="4" english="Why So Blue?" translation="Una sala blava i trista" explanation="The crewmate Victoria is found here. Victoria is always sad! She&apos;s feeling blue."/>
<roomname x="3" y="16" english="Brought to you by the letter G" translation="Compte amb la gravetat" explanation="Reference to Seseme Street - also, this room resembles the letter G"/>
<roomname x="3" y="17" english="Thorny Exchange" translation="Passades entre punxes" explanation="Two people having a polite argument could be described as having a Thorny Exchange of words."/>
<roomname x="3" y="18" english="Square Root" translation="Arrel quadrada" explanation="This room resembles a Square Root symbol."/>
<roomname x="3" y="19" english="They Call Him Flipper" translation="Gira que giraràs" explanation="This is a line from an American TV show intro about a Dolphin - https://www.youtube.com/watch?v=azEOeTX1LqM"/>
<roomname x="4" y="0" english="Vibrating String Problem" translation="Problema de la corda vibrant" explanation="Another science themed room name, this one has two gravity lines that you bounce between."/>
<roomname x="4" y="1" english="The Living Dead End" translation="Latzucac més extrem" explanation="&apos;The living end&apos; is an idiom meaning &apos;the most extreme form of something&apos;, here it&apos;s an extreme dead end, i.e. a cul-de-sac"/>
<roomname x="4" y="2" english="AAAAAA" translation="AAAAAA" explanation="The player falls through this room without having time to stop - AAAAAA suggests a scream in English. Also, it&apos;s six As, like the title."/>
<roomname x="4" y="3" english="Diode" translation="Díode" explanation="This room can only be passed through in one direction. It resembles the electrical component."/>
<roomname x="4" y="4" english="I Smell Ozone" translation="Sento olor dozó" explanation="When you use a photocopier, it produces a distinctive smell (from the Ozone produced). This room has a background pattern that suggests a teleportation has recently happened here."/>
<roomname x="4" y="16" english="Free Your Mind" translation="Allibera la ment" explanation="Reference to the film The Matrix, where Morpheus jumps from the top of a Skyscaper - https://www.youtube.com/watch?v=ef_agVIvh0A"/>
<roomname x="4" y="17" english="I Changed My Mind, Thelma..." translation="He canviat didea, Thelma..." explanation="The room below Free Your Mind. Reference to the film Thelma and Louise, which ends with Thelma and Louise driving off a cliff, sorry, spoilers"/>
<roomname x="4" y="18" english="Hitting the Apex" translation="Derrapada ajustada" explanation="&apos;Hitting the Apex&apos; is the term used by race drivers for the optimal path around a corner"/>
<roomname x="4" y="19" english="Three&apos;s a Crowd" translation="Dos són companyia, tres són multitud" explanation="From the expression Two&apos;s Company, Three&apos;s a Crowd. This room has two challenges - the first has two gaps to cross, the second has three."/>
<roomname x="5" y="0" english="Spike Strip Deployed" translation="Tira de punxes desplegada" explanation="This room has some spikes on a gravity line. The name is a reference to the device that police might use to blow out the tyres of a speeding car."/>
<roomname x="5" y="1" english="Anomaly" translation="Anomalia" explanation="As in, a strange result in science. This room has lots of different colours, unlike other rooms in this stage."/>
<roomname x="5" y="16" english="In a Single Bound" translation="Dun sol salt" explanation="Superman is described as being able to leap tall buildings in a single bound."/>
<roomname x="5" y="17" english="Indirect Jump Vector" translation="Vector indirecte de salt" explanation="If you miss the gap in &apos;In a Single Bound&apos; above, you will end up back in this room - hence, your trajectory was off!"/>
<roomname x="6" y="0" english="Topsy Turvyism" translation="Sacsejat, no remenat" explanation="Topsy Turvy is Australian slang for upside down"/>
<roomname x="6" y="1" english="Purest Unobtainium" translation="Linobteni més pur" explanation="Unobtainium is a jokey made up term from science fiction for an impossible substance - https://en.wikipedia.org/wiki/Unobtainium"/>
<roomname x="6" y="16" english="Barani, Barani" translation="Un barani rere laltre" explanation="A Barani is a technical term for doing a flip on a trampoline"/>
<roomname x="6" y="17" english="Safety Dance" translation="Podré tornar enrere" explanation="Named after the 80s song by Men Without Hats."/>
<roomname x="7" y="0" english="Standing Wave" translation="Ona estacionària" explanation="Many of the rooms in the Lab stage have science themed names. This one is at the beginning of a section with gravity lines above and below you, before the section begins."/>
<roomname x="7" y="15" english="Entanglement Generator" translation="Generador dentrellaçament" explanation="This room contains a teleporter. Entanglement is an idea from quantum mechanics."/>
<roomname x="7" y="16" english="Heady Heights" translation="Altures embriagadores" explanation="Just below the highest point in the level."/>
<roomname x="7" y="17" english="Exhausted?" translation="Vols sortir del pou?" explanation="This room has an exit that sort of suggests an Exhaust Pipe in a car."/>
<roomname x="7" y="18" english="The Tantalizing Trinket" translation="El lluentó temptador" explanation="You see this Trinket just out of reach as you fall through the room."/>
<roomname x="7" y="19" english="The Bernoulli Principle" translation="El principi de Bernoulli" explanation="Many of the rooms in the Lab stage have science themed names - this one is just named after a formula relating to flight."/>
<roomname x="8" y="9" english="Teleporter Divot" translation="La marca del teletransportador" explanation="There is a pattern in the background of this room that indicates that a teleporter has sent someone to this room"/>
<roomname x="9" y="9" english="The Tower" translation="La Torre" explanation="This room is a single vertically scrolling stage, about 20 rooms high. The name is from the Tarot card."/>
<roomname x="10" y="4" english="Seeing Red" translation="Una visió roja" explanation="This is the room that you find the red crewmate in. (Seeing Red is an expression in English about being filled with rage, but that doesn&apos;t really apply here)"/>
<roomname x="10" y="5" english="Energize" translation="Endavant!" explanation="There is a teleporter in this Room. Energize is what they say on Star Trek when they use the teleporters."/>
<roomname x="10" y="6" english="Down Under" translation="Avall i a sota" explanation="Australia is sometimes called The Land Down Under because of its position on a globe. You complete this room by going down and under some moving platforms"/>
<roomname x="10" y="7" english="A Deception" translation="Enganyifa" explanation="This room appears trivial at first, but is connected to a difficult trinket challenge"/>
<roomname x="11" y="4" english="Building Apport" translation="Acord per al teletransport" explanation="An &apos;Apport&apos; is a kind of paranormal teleportation. Building Apport is a pun on the concept of &apos;building rapport&apos; (except that to &apos;apport&apos; is to teleport)."/>
<roomname x="11" y="5" english="Frown Upside Down" translation="Vinga, animat!" explanation="To &apos;Turn that Frown Upside Down&apos; is an expression in English, like &apos;cheer up&apos;, basically means to stop being unhappy"/>
<roomname x="11" y="6" english="Shenanigan" translation="Tripijoc" explanation="Shenanigan as in a prank, or practical joke. This room is connected to the &apos;Prize for the Reckless&apos; puzzle, and like the room &apos;A Deception&apos;, it appears trivial unless you know the secret"/>
<roomname x="11" y="7" english="Prize for the Reckless" translation="Un premi per la imprudència" explanation="This room contains a trinket that can only be collected by doing something difficult"/>
<roomname x="11" y="11" english="Conveying a New Idea" translation="Deixa que et transportin" explanation="This is the first room you encounter that has conveyor belts in it"/>
<roomname x="11" y="12" english="One Way Room" translation="Sala unidireccional" explanation="Can only be travelled through in one direction"/>
<roomname x="11" y="13" english="Boldly To Go" translation="Penetrant allà on no ha estat mai ningú" explanation="Star Trek reference, this rooms is near the entrance to the space station level"/>
<roomname x="11" y="14" english="The Filter" translation="El filtre" explanation="Like a filter from an air conditioning vent"/>
<roomname x="12" y="3" english="Security Sweep" translation="Escaneig de seguretat" explanation="Contains a single, fast moving enemy that moves up and down."/>
<roomname x="12" y="4" english="Gantry and Dolly" translation="Pòrtic i plataforma mòbil" explanation="Gantry and Dolly are the names for types of cranes that move crates around. This room has two different types of platforms."/>
<roomname x="12" y="5" english="The Yes Men" translation="Els homes del sí" explanation="Contains a number of enemies with briefcases and the word -YES- for a head. An expression for people who work at large companies and agree a lot with their bosses"/>
<roomname x="12" y="6" english="Stop and Reflect" translation="Aturat i reflexiona" explanation="Expression meaning to take a moment and think about what you&apos;re doing. In this room, a small puzzle where you need to use the underside of a moving platform to progress."/>
<roomname x="12" y="7" english="V Stitch" translation="Punt en V" explanation="A V Stitch is a type of crochet stitch."/>
<roomname x="12" y="11" english="Upstream Downstream" translation="Riu amunt i riu avall" explanation="As in swimming upstream or downstream, with or against a current in a river"/>
<roomname x="12" y="12" english="The High Road is Low" translation="El camí més elevat és el més baix" explanation="This room has two paths - a high path and a low path. The &apos;low&apos; path leads to a trinket, so the roomname is a sort of clue about which way to go."/>
<roomname x="12" y="13" english="Give Me A V" translation="Vèncer sescriu amb V" explanation="Room is in the shape of a big letter V. The roomname suggests the common american cheerleading chant - e.g. Give me an L! Give me an O! Give me a C! Give me an A! Give me an L! Give me an I! Give me an S! Give me an A! Give me a T! Give me an I! Give me an O! Give me an N! What does it spell? LOCALISATION!"/>
<roomname x="12" y="14" english="Outer Hull" translation="Coberta exterior" explanation="The entrance to the Space Station 2 level - the outer hull of a space station."/>
<roomname x="13" y="0" english="It&apos;s Not Easy Being Green" translation="No és fàcil ser verd" explanation="References a song by Kermit from the Muppets. This room is where you find the green crewmate."/>
<roomname x="13" y="3" english="Linear Collider" translation="Col·lisionador lineal" explanation="An early room, name is just meant to suggest something sciency. Room contains long, wave like enemies."/>
<roomname x="13" y="4" english="Comms Relay" translation="Repetidor de comunicacions" explanation="This room contains some communication equipment, like a radio."/>
<roomname x="13" y="5" english="Welcome Aboard" translation="Et donem la benvinguda a bord" explanation="The first room in the game"/>
<roomname x="13" y="6" english="Trench Warfare" translation="Guerra de trinxeres" explanation="Room contains a couple of pits with soldier-like enemies in them. Loosely references the 1983 videogame Hunchback."/>
<roomname x="13" y="7" english="B-B-B-Busted" translation="Això és un abús!" explanation="Room contains a large Bus. &apos;Bus&apos;ted as in &apos;Caught&apos;."/>
<roomname x="13" y="8" english="Level Complete!" translation="Nivell completat!" explanation="This room has a teleporter, which is normally found at the end of a level. However this room is midway through the stage."/>
<roomname x="13" y="9" english="Lighter Than Air" translation="Més volàtil que laire" explanation="This room has clouds that rise from the bottom of the screen to the top, which the player is faster than, implying that the player is lighter than air."/>
<roomname x="13" y="10" english="The Solution is Dilution" translation="La solució és la dilució" explanation="This room has a factory and pollution clouds in it. Apparently this phrase was once used by industrialists to advocate for not worrying too much about pollution."/>
<roomname x="13" y="11" english="The Cuckoo" translation="El cucut" explanation="This room contains a speaker that emits the word &apos;LIES&apos; over and over. A cuckoo&apos;s call decieves other birds!"/>
<roomname x="13" y="12" english="Backsliders" translation="Els tres bessons" explanation="A conveyor belt in this room pushes against you as you try to move, so you slide backwards."/>
<roomname x="13" y="13" english="Select Track" translation="Tria un camí" explanation="There are two paths you can pick between here"/>
<roomname x="14" y="0" english="Green Dudes Can&apos;t Flip" translation="Els verds no la saben invertir" explanation="You have a green crewmate with you in this room! A reference to the 90&apos;s film &apos;White Guy&apos;s Can&apos;t Jump&apos;."/>
<roomname x="14" y="1" english="This is how it is" translation="Així són les coses" explanation="literally as in, this is how the mechanic of this stage works - also an expression as in &apos;this is the way things are&apos;"/>
<roomname x="14" y="2" english="That&apos;s Why I Have To Kill You" translation="I per això thaig de matar" explanation="The follows the room named &apos;I love you&apos;. &apos;I love you, that&apos;s why I have to kill you&apos; is kind of a slasher horror trope."/>
<roomname x="14" y="3" english="Atmospheric Filtering Unit" translation="Unitat de filtratge atmosfèric" explanation="An early room, looks a bit like an air filter"/>
<roomname x="14" y="4" english="It&apos;s a Secret to Nobody" translation="No és cap secret per a ningú" explanation="A reference to the infamous Zelda quote &apos;It&apos;s a secret to everybody&apos;. This room contains the first trinket."/>
<roomname x="14" y="5" english="Conundrum" translation="Enigma" explanation="Conundrum as in puzzle, riddle, problem to be solved"/>
<roomname x="14" y="6" english="Boo! Think Fast!" translation="Ei, actua de pressa!" explanation="Contains a challenge that you need to react very quickly to. You might say &apos;Boo, think fast&apos; if you threw something at someone, expecting them to catch it."/>
<roomname x="14" y="7" english="The Sensible Room" translation="La sala sensata" explanation="Early corridor room containing no challenges. Sensible as in the opposite of Foolish - you might call someone sensible in english if they are excessively cautious."/>
<roomname x="14" y="8" english="The Hanged Man, Reversed" translation="El Penjat, del revés" explanation="Named after the Tarot Card, reversed as in Upside Down. The room contains a stationary enemy which resembles a Wheel of Fortune. The name is supposed to suggest a kind of out-of-place quality."/>
<roomname x="14" y="9" english="Green Grotto" translation="Cova verda" explanation="A peaceful green room."/>
<roomname x="14" y="10" english="Manic Mine" translation="Mina maníaca" explanation="A reference to the 8-bit game Manic Miner."/>
<roomname x="14" y="11" english="Clarion Call" translation="Crida a lacció" explanation="A &apos;Clarion Call&apos; is an idiom used when somebody makes a case for a course of action, for example in a politician&apos;s speech, or a call to battle. It sometimes has an association with dishonesty - in this room, the words &apos;LIES&apos; appear over and over."/>
<roomname x="14" y="12" english="Gordian Knot" translation="Nus gordià" explanation="As in the Gordian Knot from greek history. A complicated room that can be passed through twice."/>
<roomname x="14" y="13" english="You Chose... Poorly" translation="Has triat... malament" explanation="This room comes right after a choice between two paths. It&apos;s a quote from an Indiana Jones film."/>
<roomname x="15" y="0" english="Murdering Twinmaker" translation="Fabricador i assassinador de bessons" explanation="Room contains a teleporter. A &apos;Murdering Twinmaker&apos; is, uh, one way teleportation might work..."/>
<roomname x="15" y="1" english="A Bisected Spiral" translation="Una espiral biseccionada" explanation="Room is a spiral, cut down the middle"/>
<roomname x="15" y="2" english="Take the Red Pill" translation="Agafa la píndola vermella" explanation="This is a Matrix reference that hasn&apos;t aged well, lol"/>
<roomname x="15" y="3" english="Traffic Jam" translation="Embús de trànsit" explanation="The enemies in this room are Stop Signs"/>
<roomname x="15" y="4" english="Leap of Faith" translation="Salt de fe" explanation="To take a leap of faith means to do something without knowing how it&apos;s going to turn out."/>
<roomname x="15" y="5" english="Solitude" translation="Soledat" explanation="As in being alone, or in this case, lost by yourself"/>
<roomname x="15" y="6" english="Driller" translation="Perforació" explanation="Technically references the name of a C64 game, but that doesn&apos;t matter much"/>
<roomname x="15" y="7" english="Exhaust Chute" translation="Conducte devacuació de la brossa" explanation="Like a factory exhaust chute for disposing of rubbish"/>
<roomname x="15" y="8" english="Sorrow" translation="Pena" explanation="A difficult room that you might die in a lot"/>
<roomname x="15" y="9" english="doomS" translation="sosoxund suowqnS" explanation="The room is above &apos;Swoop&apos;, and is a copy of the room rotated 180 degrees! The room name &apos;doomS&apos; is the word &apos;Swoop&apos; rotated 180 degrees. When localising this room, don&apos;t worry too much about trying to keep the meaning of the words Dooms and Swoop, because they&apos;re not that important - instead, focus on picking words that have this 180 degree flip quality!"/>
<roomname x="15" y="10" english="Swoop" translation="Submons punxosos" explanation="See the note for room (15,9), doomS."/>
<roomname x="15" y="11" english="Chinese Rooms" translation="Lhabitació xinesa" explanation="This refers to a famous philosophical argument about artifical intelligence - https://en.wikipedia.org/wiki/Chinese_room."/>
<roomname x="15" y="12" english="You Just Keep Coming Back" translation="No fas res més que tornar aquí" explanation="You can pass through this room up to three times, depending on which route you take through the level."/>
<roomname x="15" y="13" english="Hyperspace Bypass 5" translation="Circumval·lació espacial 5" explanation="A conveyor belt will take you through this room without you needing to press any buttons, hench the bypass. The phrase Hyperspace Bypass is a reference to Hitchhiker&apos;s Guide to the Galaxy."/>
<roomname x="16" y="0" english="I Love You" translation="Testimo" explanation="The room contains a couple of heart shaped enemies"/>
<roomname x="16" y="1" english="As you like it" translation="Al vostre gust" explanation="This room can be approached in two different equivilent ways, whichever way you like it. &apos;As you like it&apos; is the name of a Shakespeare play."/>
<roomname x="16" y="2" english="Short Circuit" translation="Curtcircuit" explanation="Probably named after the 80s film Short Circuit. Also works because you&apos;ll hit a dead end if you keep walking forwards."/>
<roomname x="16" y="3" english="Twisty Little Passages" translation="Petits corredors recaragolats" explanation="A maze like room. Refers to the section from the 1976 text game Colossal Cave Adventure - you are in a maze of twisty little passages, all alike"/>
<roomname x="16" y="6" english="Quicksand" translation="Arenes movedisses" explanation="Contains lots of dissolving platforms."/>
<roomname x="16" y="7" english="The Tomb of Mad Carew" translation="La tomba de Mad Carew" explanation="A very obscure reference to the C64 game Dizzy"/>
<roomname x="16" y="8" english="Parabolica" translation="Parabòlica" explanation="This room contains a section of wall in the shape of a parabolic arch."/>
<roomname x="16" y="9" english="$eeing Dollar $ign$" translation="El dolor del dòlar" explanation="This is a green room that resembles a dollar sign shape"/>
<roomname x="16" y="10" english="What Lies Beneath?" translation="Què hi ha a sota?" explanation="The room below this contains enemies in the shape of the word &apos;Lies&apos;. So there&apos;s a double meaning here - as in, a question, &apos;what is below this room&apos;, and that the word LIES is literally beneath this room."/>
<roomname x="16" y="11" english="Spikes Do!" translation="Hi ha punxes!" explanation="This rooms is below the room named &apos;What lies Beneath?&apos;, and answers the question: Spikes do!"/>
<roomname x="16" y="12" english="Ha Ha Ha Not Really" translation="Ha, ha, ha! La veritat és que no!" explanation="This is a difficult room that follows one called &apos;Plain Sailing from here on&apos;. It&apos;s taunting the player. "/>
<roomname x="16" y="13" english="Plain Sailing from Here On" translation="A partir daquí tot seran flors i violes" explanation="This room is at the end of a long section, and promises &apos;Plain Sailing&apos; afterwards, as in, no further challenges. This is a lie"/>
<roomname x="17" y="0" english="As we go up, we go down" translation="El sostre és el terra i al revés" explanation="Named after the 1995 song by Guided by Voices"/>
<roomname x="17" y="1" english="Maze With No Entrance" translation="Un laberint sense entrada" explanation="This room is a maze which has no entrance, due to the nature of the warping mechanic."/>
<roomname x="17" y="2" english="The Brown Gate" translation="La porta marró" explanation="I think this is an Ultima 7 reference? A literal translation is fine here"/>
<roomname x="17" y="3" english="Edge Games" translation="El joc de les vores" explanation="Contains a trinket that you get by navigating around the edge of the screen. The use of the word EDGE is deliberate, refering to the trademark of a notoriously litigious individual who sued an indie developer around the time VVVVVV was made"/>
<roomname x="17" y="7" english="Brass Sent Us Under The Top" translation="Napoleó tenia cent soldats" explanation="The enemies in this room look like little army guys"/>
<roomname x="17" y="8" english="The Warning" translation="Agafat fort" explanation="This room has lots of checkpoints in it. It&apos;s beside the game&apos;s most difficult challenge, the Veni, Vidi, Vici section. The checkpoints don&apos;t really do anything, but they&apos;re a warning of the challenge ahead."/>
<roomname x="17" y="9" english="Just Pick Yourself Down" translation="...aixecat" explanation="A two part room name. From the expression &apos;If you fall down, just pick yourself up&apos;."/>
<roomname x="17" y="10" english="If You Fall Up" translation="Si caus..." explanation="A two part room name. From the expression &apos;If you fall down, just pick yourself up&apos;."/>
<roomname x="17" y="11" english="Chipper Cipher" translation="El receptor que sho empassa tot" explanation="Just some nice wordplay. Chipper means &apos;Jolly&apos; or &apos;Happy&apos;."/>
<roomname x="18" y="0" english="Time to get serious" translation="És lhora de posar-nos seriosos" explanation="Literal, this is the first room in the stage which is fairly difficult"/>
<roomname x="18" y="1" english="Wheeler&apos;s Wormhole" translation="Forat de cuc de Wheeler" explanation="Apparently a scientist named John Wheeler coined the phrase &apos;Wormhole&apos;! I just found that out. Anyway, this room has a teleporter in it."/>
<roomname x="18" y="2" english="Sweeney&apos;s Maze" translation="El laberint de Sweeney" explanation="Contains enemies that move strangly and resemble enemies from ZZT, an old game by Tim Sweeney"/>
<roomname x="18" y="3" english="Mind The Gap" translation="Vigileu amb la distància cotxe-andana" explanation="Refers to what train announcers say on the London Underground when you leave the train - mind the gap between the train and the station platform"/>
<roomname x="18" y="7" english="A Wrinkle in Time" translation="Un replec en el temps" explanation="The name of a 60&apos;s science fiction book. There is a teleporter in this room."/>
<roomname x="18" y="8" english="Getting Here is Half the Fun" translation="Arribar aquí és sols la meitat del camí" explanation="The top of the Veni Vidi Vici sequence. When you get to the top, you have to go all the way back down - hence, this room is the halfway point of the challenge."/>
<roomname x="18" y="9" english="Your Bitter Tears... Delicious" translation="Les teves llàgrimes... Delicioses!" explanation="Part of the Veni Vidi Vici sequence. Taunting the player."/>
<roomname x="18" y="10" english="Easy Mode Unlocked" translation="Mode fàcil desblocat" explanation="Part of the Veni Vidi Vici sequence. Taunting the player - the &apos;easy mode&apos; refers to the second passageway on the right side that is easier to take when going back through this room on the way down, but that passageway leads to death."/>
<roomname x="18" y="11" english="Vici!" translation="Vici!" explanation="The rooms Veni, Vidi, Vici! appear in sequence. Famous latin phrase attributed to Julius Caesar meaning I came, I saw, I conquered. The hardest challenge in the game."/>
<roomname x="18" y="12" english="Vidi" translation="Vidi" explanation="The rooms Veni, Vidi, Vici! appear in sequence. Famous latin phrase attributed to Julius Caesar meaning I came, I saw, I conquered. The hardest challenge in the game."/>
<roomname x="18" y="13" english="Veni" translation="Veni" explanation="The rooms Veni, Vidi, Vici! appear in sequence. Famous latin phrase attributed to Julius Caesar meaning I came, I saw, I conquered. The hardest challenge in the game."/>
<roomname x="18" y="14" english="Doing Things The Hard Way" translation="Les coses es fan de la manera difícil" explanation="The starting room from the Veni, Vidi, Vici! challenge."/>
<roomname x="19" y="0" english="To The Batcave!" translation="A la Batcova!" explanation="The Batcave, as in Batman&apos;s hideout."/>
<roomname x="19" y="1" english="Ascending and Descending" translation="Pujades i baixades" explanation="Just meant as in literally Ascending and Descending, going up and down. There is a room later in the game called Upstairs, Downstairs, which is a callback to this."/>
<roomname x="19" y="2" english="Shockwave Rider" translation="El genet de lona de xoc" explanation="Named after a 70&apos;s Science Fiction novel"/>
<roomname x="19" y="3" english="This will make you flip" translation="Acabaràs amb el cervell regirat" explanation="Flip is used here in the sense &apos;Flip out&apos;, as in, to lose your temper"/>
<roomname x="41" y="51" english="1950 Silverstone Grand V" translation="Gran Vremi de Silverstone de 1950" explanation="Refers to 1950 Silverstone Grand Prix (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="41" y="52" english="DIY V Repair" translation="Repara la V sense ajuda externa" explanation="DIY TV repair - this room has a television you can interact with, which changes the theme from black and white to colour"/>
<roomname x="41" y="56" english="Now Take My Lead" translation="Ara segueix-me" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="42" y="52" english="Party Time!" translation="És lhora de la festa!" explanation="The first room after the black and white section. Doesn&apos;t refer to any TV show."/>
<roomname x="42" y="56" english="What Are You Waiting For?" translation="Ara què esperes?" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="43" y="51" english="The Voon Show" translation="Mare Vostrum" explanation="Named after The Goon Show (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="43" y="52" english="Upstairs, Downstairs" translation="A dalt i a baix" explanation="Named after the 70s TV show - but also, refers to the earlier room Ascending and Descending, which this room is an updated version of. (Second part of the final stage has references to Colour TV shows)"/>
<roomname x="43" y="56" english="Don&apos;t Get Ahead of Yourself!" translation="No vulguis avançar tan de sobte!" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="44" y="51" english="Vertigo" translation="Vertigen" explanation="Named after the Hitchcock film (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="44" y="52" english="Timeslip" translation="Les tres bessones" explanation="Named after the 70s TV show - name also suggests a connection to the earlier room Backsliders, which this room is an updated version of (Second part of the final stage has references to Colour TV shows)"/>
<roomname x="44" y="56" english="Very Good" translation="Molt bé!" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="45" y="52" english="Three&apos;s Company" translation="Dos són companyia, tres són fantasia" explanation="Named after the 70s sitcom - but also, suggests a connection to the earlier room Two&apos;s Company, which this room is an updated version of (Second part of the final stage has references to Colour TV shows)"/>
<roomname x="45" y="56" english="Must I Do Everything For You?" translation="Què vols, que tho faci tot jo?" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="46" y="54" english="Temporary Fault..." translation="Error temporal..." explanation="Opening room of the final level."/>
<roomname x="46" y="56" english="Now Stay Close To Me..." translation="Ara no ten vagis gaire lluny..." explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="47" y="52" english="Cosmic Creepers" translation="Salem Saberhagen" explanation="Named after the cat from the 70s film Bedknobs and Broomsticks. Not sure why!"/>
<roomname x="47" y="54" english="Do Not Adjust the V-hold" translation="No ajusteu la sincronització vertical" explanation="I don&apos;t think V-hold is a real thing, it&apos;s supposed to just suggest &apos;Do not adjust your TV settings&apos;. (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="47" y="56" english="...But Not Too Close" translation="...però tampoc tan a prop!" explanation="In this room, if you go too quickly, your crewmate will walk into spikes."/>
<roomname x="48" y="52" english="The Villi People" translation="Les vellositats de lintestí" explanation="Bennett just thought this room looked &apos;intestinal&apos;. Villi as in part of the intestinal system. Also refers to the 80s band The Village People."/>
<roomname x="48" y="54" english="Regular Service Will Return Shortly" translation="El servei es recuperarà ben aviat" explanation="This is something you might hear on a TV station if they had lost reception. (The final stage has room names that suggest old black and white TV shows. A really, really good way to translate this level would be to use the names of black and white TV shows that are well known in your language, rather than trying to keep the exact meaning of the TV shows used here.)"/>
<roomname x="48" y="56" english="Don&apos;t Be Afraid" translation="No tinguis por" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="49" y="52" english="Panic Room" translation="La sala del pànic" explanation="A panic room is a safe room that you can hide in during an emergency, but I don&apos;t think that was Bennett&apos;s intention with this name. This room suddenly starts scrolling as soon as you enter, causing a panic."/>
<roomname x="49" y="54" english="Origami Room" translation="Sala dorigami" explanation="As in folded paper - this room is mirrored around the center point."/>
<roomname x="49" y="56" english="Do as I Say..." translation="Fes el que et dic..." explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="50" y="51" english="The V Stooges" translation="Els V guillats" explanation="Refers to the 3 Stooges (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="50" y="52" english="1954 World Cup Vinyl" translation="Vinil de la Copa del Món de 1954" explanation="Refers to the World Cup Final (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname x="50" y="56" english="...Not as I Do" translation="...no el que jo faig" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="51" y="53" english="The Final Challenge" translation="Lúltim repte" explanation="One of the last challenges in the game."/>
<roomname x="51" y="56" english="Mind Your Head" translation="Vés amb compte amb el cap" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="52" y="53" english="The Last Straw" translation="La gota que fa vessar el got" explanation="One more little challenge, just after the room called The Final Challenge"/>
<roomname x="52" y="56" english="Do Try To Keep Up" translation="Mira de seguir-me" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="53" y="48" english="Whee Sports" translation="Wiii, esports!" explanation="Refers to Wii Sports, the Nintendo Wii launch title. &apos;Whee&apos; as in what a child might say while going down a slide (this room has a long drop in it)"/>
<roomname x="53" y="49" english="Whizz Down The Shaft" translation="Afanyat i baixa pel pou" explanation="Whizz is Australian slang for doing something quickly, I think"/>
<roomname x="53" y="50" english="The Gravitron" translation="El gravitró" explanation="A special arcade section where you have to survive for 60 seconds. Bennett named this one so as to suggest a funfair ride (though not any one in particular)."/>
<roomname x="53" y="51" english="Tunnel of Terror" translation="El túnel del terror" explanation="Another name inspired by funfairs."/>
<roomname x="53" y="52" english="House of Mirrors" translation="La casa dels miralls" explanation="Another name inspired by funfairs."/>
<roomname x="53" y="53" english="W" translation="V doble" explanation="This room has platforms in a W shape."/>
<roomname x="53" y="56" english="You&apos;re Falling Behind" translation="Testàs endarrerint" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
<roomname x="54" y="48" english="VVVVVV" translation="VVVVVV" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 6/6"/>
<roomname x="54" y="49" english="VVVVV" translation="VVVVV" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 5/6"/>
<roomname x="54" y="50" english="VVVV" translation="VVVV" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 4/6"/>
<roomname x="54" y="51" english="VVV" translation="VVV" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 3/6"/>
<roomname x="54" y="52" english="VV" translation="VV" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 2/6"/>
<roomname x="54" y="53" english="V" translation="V" explanation="Final sequence of rooms that spell out V-V-V-V-V-V, 1/6"/>
<roomname x="54" y="56" english="Class Dismissed!" translation="Sha acabat la classe!" explanation="This entire intermission section has a tone of a strict schoolteacher leading a small child."/>
</roomnames>

View file

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Please read README.txt for information about the language files -->
<roomnames_special>
<roomname english="Outer Space" translation="Espai exterior" explanation="Literally Outer Space"/>
<roomname english="Dimension VVVVVV" translation="Dimensió VVVVVV" explanation="The overall area that the game takes place in."/>
<roomname english="The Ship" translation="La nau" explanation="The dimension exploring spaceship"/>
<roomname english="Secret Lab" translation="Laboratori secret" explanation="An endgame section containing achievement trophys"/>
<roomname english="Laboratory" translation="Laboratori" explanation="One of the stages"/>
<roomname english="The Tower" translation="La Torre" explanation="One of the stages"/>
<roomname english="Warp Zone" translation="Zona cíclica" explanation="One of the stages"/>
<roomname english="Space Station" translation="Estació espacial" explanation="One of the stages"/>
<roomname english="Outside Dimension VVVVVV" translation="Fora de la dimensió VVVVVV" explanation="For rare rooms outside the normal area"/>
<roomname english="???" translation="???" explanation=""/>
<roomname english="The Super Gravitron" translation="El supergravitró" explanation="An expanded version of the room at 53, 50"/>
<roomname english="I Can&apos;t Believe You Got This Far" translation="No em puc creure que hagis arribat aquí" explanation="If you&apos;re playing No Death Mode, the room Prize for the Reckless has this roomname instead (the room is altered to make it possible to do this section without dying)"/>
<roomname english="Imagine Spikes There, if You Like" translation="Si vols, imagina-thi punxes" explanation="If you&apos;re playing a time trial, the room Prize for the Reckless has this roomname instead (the room is altered to make it possible to do this section without dying)"/>
<!-- - -->
<roomname english="Rear Window" translation="La finestra indiscreta" explanation="Named after the Hitchcock film (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Rear Vindow" translation="La vinestra indiscreta" explanation=""/>
<!-- - -->
<roomname english="On the Waterfront" translation="La llei del silenci" explanation="Named after the 1954 film (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="On the Vaterfront" translation="La llei del vilenci" explanation=""/>
<!-- - -->
<roomname english="The Untouchables" translation="Els intocables" explanation="Before it was a film, the Untouchables was a TV series in 1959 (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="The Untouchavles" translation="Els intocavles" explanation=""/>
<!-- - -->
<roomname english="Television Newsveel" translation="Noticiaris i Docvmentals" explanation="Refers to Television Newsreel. (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Television Newsvel" translation="Notiviaris i Docvmentals" explanation=""/>
<roomname english="TelevisvonvNewsvel" translation="Notiviavisi Vocvumenvals" explanation=""/>
<roomname english="TvlvvvsvonvNevsvel" translation="Votivvavvsi Vovuvevvalv" explanation=""/>
<roomname english="vvvvvvsvovvNe svel" translation="Vvovvvavvvv Vovvvvavv" explanation=""/>
<roomname english="vhv vvv&apos;vvovv vevl" translation="Vvvevvvvvvev vvvpvvv" explanation=""/>
<roomname english="vhv V v&apos;Cvovv vewv" translation="Vvvevvtvvvev vevpvrv" explanation=""/>
<roomname english="vhe 9 v&apos;Cvovv vewv" translation="Vevevotvciev vesvvre" explanation=""/>
<roomname english="vhe 9 v&apos;Cvovv Newv" translation="Velevovíciev vesvre" explanation=""/>
<roomname english="The 9 O&apos;Cvovk Newv" translation="Televotíciev vesvre" explanation=""/>
<roomname english="The 9 O&apos;Clock News" translation="Telenotícies vespre" explanation="(Second part of the final stage has references to Colour TV shows)"/>
<!-- - -->
<roomname english="Vwitched" translation="M de monocrom" explanation="Reference to early black and white sitcom Bewitched (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Vwitvhed" translation="V de monovrom" explanation=""/>
<roomname english="vVwivcvedv" translation="V ve movovrov" explanation=""/>
<roomname english="vvvwMvcvMdvv" translation="V vv movvrvv" explanation=""/>
<roomname english="DvvvwMvfvvMdvvv" translation="V vv vovvrv" explanation=""/>
<roomname english="Dvav Mvfvr Mdvvvv" translation="V ve vovvt" explanation=""/>
<roomname english="Diav M for Mdrver" translation="V de vort" explanation=""/>
<roomname english="Dial M for Murder" translation="M de mort" explanation="Named after the Hitchcock film (Second part of the final stage has references to Colour TV shows)"/>
<!-- - -->
<roomname english="Gvnsmoke" translation="La llei del revòlver" explanation="Gunsmoke was a black and white Western (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Gvnsmove" translation="Va llei dev revòlver" explanation=""/>
<roomname english="Gvnvmovevv" translation="Va vvei dev revòvvervv" explanation=""/>
<roomname english="Gunvmove1vv6" translation="Va llei dev revòvver(1vv6v" explanation=""/>
<roomname english="Vunsmoke 19v6" translation="Va llei dev revòlver (19v6)" explanation=""/>
<roomname english="Gunsmoke 1966" translation="La llei del revòlver (1966)" explanation="Gunsmoke changed to colour in 1966 (Second part of the final stage has references to Colour TV shows)"/>
<!-- - -->
<roomname english="Please enjoy these repeats" translation="Us oferim les següents reposicions" explanation="This stage also has a number of rooms which are harder versions of easier challenges, like this one. (The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Please envoy theve repeats" translation="Vs oferim les següents revosicions" explanation=""/>
<roomname english="Plse envoy tse rvpvas" translation="Vsovriv ves vevüevtv revovivvns" explanation=""/>
<roomname english="Vl envoy te rvevs" translation="Vsvviv vevv evevtvevovvs" explanation=""/>
<roomname english="Vv evo tv vevs" translation="Vsvv vevv vvevvvoviv" explanation=""/>
<roomname english="Iv vhv Mvrvivs" translation="Divs velv mvrvvev" explanation=""/>
<roomname english="In the Margins" translation="Dins dels marges" explanation="Not sure if this has a TV show reference, might just be meant literally (Second part of the final stage has references to Colour TV shows)"/>
<!-- - -->
<roomname english="Try Jiggling the Antenna" translation="Prova de moure una mica lantena" explanation="(The final stage has room names that suggest old black and white TV shows.)"/>
<roomname english="Try Viggling the Antenna" translation="Prova de movre vna miva lantena" explanation=""/>
<roomname english="TryJivglvng theAvtevna" translation="Provavemovre unavivalvnteva" explanation=""/>
<roomname english="Tvvivglvng thAvtvvv" translation="Vrva vvmovve vvlvntva" explanation=""/>
<roomname english="Vvvgglvnv tvnvva" translation="Vv vmvve vvlvvva" explanation=""/>
<roomname english="Vvavvnvs vvtv" translation="Va vovva vev vev" explanation=""/>
<roomname english="Veavvn&apos;s Gvte" translation="Va povta dev vel" explanation="Named after the 1980&apos;s film (Second part of the final stage has references to Colour TV shows)"/>
<roomname english="Heaven&apos;s Gate" translation="La porta del cel" explanation="Named after the 1980&apos;s film (Second part of the final stage has references to Colour TV shows)"/>
</roomnames_special>

Some files were not shown because too many files have changed in this diff Show more