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
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.
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.
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.)
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).
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.
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.
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.
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.
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.
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.
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.
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).
"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.
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.
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.
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.
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.
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.
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.
- 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.
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.
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.
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)
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.
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.)
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.
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.
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.
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.
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.
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)
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.
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.
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!
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.
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.
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).
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.
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.
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).
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.
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).
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.
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.
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.
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.
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`.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
`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.
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.
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.
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.
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.
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.
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.
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.
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
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
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
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