1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-11-18 00:49:42 +01:00
Commit graph

2373 commits

Author SHA1 Message Date
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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