1
0
Fork 0
mirror of https://github.com/TerryCavanagh/VVVVVV.git synced 2024-12-22 17:49:43 +01:00
VVVVVV/desktop_version/src/UtilityClass.cpp

323 lines
6.5 KiB
C++
Raw Normal View History

#define HELP_DEFINITION
2020-01-01 21:29:24 +01:00
#include "UtilityClass.h"
#include <SDL.h>
2020-01-01 21:29:24 +01:00
#include <sstream>
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 17:13:46 +01:00
#include "Constants.h"
#include "Localization.h"
Refactor loading arrays from XML to not use the STL The current way "arrays" from XML files are loaded (before this commit is applied) goes something like this: 1. Read the buffer of the contents of the tag using TinyXML-2. 2. Allocate a buffer on the heap of the same size, and copy the existing buffer to it. (This is what the statement `std::string TextString = pText;` does.) 3. For each delimiter in the heap-allocated buffer... a. Allocate another buffer on the heap, and copy the characters from the previous delimiter to the delimiter you just hit. b. Then allocate the buffer AGAIN, to copy it into an std::vector. 4. Then re-allocate every single buffer YET AGAIN, because you need to make a copy of the std::vector in split() to return it to the caller. As you can see, the existing way uses a lot of memory allocations and data marshalling, just to split some text. The problem here is mostly making a temporary std::vector of split text, before doing any actual useful work (most likely, putting it into an array or ANOTHER std::vector - if the latter, then that's yet another memory allocation on top of the memory allocation you already did; this memory allocation is unavoidable, unlike the ones mentioned earlier, which should be removed). So I noticed that since we're iterating over the entire string once (just to shove its contents into a temporary std::vector), and then basically iterating over it again - why can't the whole thing just be more immediate, and just be iterated over once? So that's what I've done here. I've axed the split() function (both of them, actually), and made next_split() and next_split_s(). next_split() will take an existing string and a starting index, and it will find the next occurrence of the given delimiter in the string. Once it does so, it will return the length from the previous starting index, and modify your starting index as well. The price for immediateness is that you're supposed to handle keeping the index of the previous starting index around in order to be able to use the function; updating it after each iteration is also your responsibility. (By the way, next_split() doesn't use SDL_strchr(), because we can't get the length of the substring for the last substring. We could handle this special case specifically, but it'd be uglier; it also introduces iterating over the last substring twice, when we only need to do it once.) next_split_s() does the same thing as next_split(), except it will copy the resulting substring into a buffer that you provide (along with its size). Useful if you don't particularly care about the length of the substring. All callers have been updated accordingly. This new system does not make ANY heap allocations at all; at worst, it allocates a temporary buffer on the stack, but that's only if you use next_split_s(); plus, it'd be a fixed-size buffer, and stack allocations are negligible anyway. This improves performance when loading any sort of XML file, especially loading custom levels - which, on my system at least, I can noticeably tell (there's less of a freeze when I load in to a custom level with lots of scripts). It also decreases memory usage, because the heap isn't being used just to iterate over some delimiters when XML files are loaded.
2021-02-13 01:37:29 +01:00
#include "Maths.h"
#include "VFormat.h"
Refactor loading arrays from XML to not use the STL The current way "arrays" from XML files are loaded (before this commit is applied) goes something like this: 1. Read the buffer of the contents of the tag using TinyXML-2. 2. Allocate a buffer on the heap of the same size, and copy the existing buffer to it. (This is what the statement `std::string TextString = pText;` does.) 3. For each delimiter in the heap-allocated buffer... a. Allocate another buffer on the heap, and copy the characters from the previous delimiter to the delimiter you just hit. b. Then allocate the buffer AGAIN, to copy it into an std::vector. 4. Then re-allocate every single buffer YET AGAIN, because you need to make a copy of the std::vector in split() to return it to the caller. As you can see, the existing way uses a lot of memory allocations and data marshalling, just to split some text. The problem here is mostly making a temporary std::vector of split text, before doing any actual useful work (most likely, putting it into an array or ANOTHER std::vector - if the latter, then that's yet another memory allocation on top of the memory allocation you already did; this memory allocation is unavoidable, unlike the ones mentioned earlier, which should be removed). So I noticed that since we're iterating over the entire string once (just to shove its contents into a temporary std::vector), and then basically iterating over it again - why can't the whole thing just be more immediate, and just be iterated over once? So that's what I've done here. I've axed the split() function (both of them, actually), and made next_split() and next_split_s(). next_split() will take an existing string and a starting index, and it will find the next occurrence of the given delimiter in the string. Once it does so, it will return the length from the previous starting index, and modify your starting index as well. The price for immediateness is that you're supposed to handle keeping the index of the previous starting index around in order to be able to use the function; updating it after each iteration is also your responsibility. (By the way, next_split() doesn't use SDL_strchr(), because we can't get the length of the substring for the last substring. We could handle this special case specifically, but it'd be uglier; it also introduces iterating over the last substring twice, when we only need to do it once.) next_split_s() does the same thing as next_split(), except it will copy the resulting substring into a buffer that you provide (along with its size). Useful if you don't particularly care about the length of the substring. All callers have been updated accordingly. This new system does not make ANY heap allocations at all; at worst, it allocates a temporary buffer on the stack, but that's only if you use next_split_s(); plus, it'd be a fixed-size buffer, and stack allocations are negligible anyway. This improves performance when loading any sort of XML file, especially loading custom levels - which, on my system at least, I can noticeably tell (there's less of a freeze when I load in to a custom level with lots of scripts). It also decreases memory usage, because the heap isn't being used just to iterate over some delimiters when XML files are loaded.
2021-02-13 01:37:29 +01:00
int ss_toi(const std::string& str)
2020-01-01 21:29:24 +01:00
{
int retval = 0;
bool negative = false;
static const int radix = 10;
for (size_t i = 0; i < str.size(); ++i)
{
const char chr = str[i];
if (i == 0 && chr == '-')
{
negative = true;
continue;
}
if (SDL_isdigit(chr))
{
retval *= radix;
retval += chr - '0';
}
else
{
break;
}
}
if (negative)
{
return -retval;
}
return retval;
2020-01-01 21:29:24 +01:00
}
Refactor loading arrays from XML to not use the STL The current way "arrays" from XML files are loaded (before this commit is applied) goes something like this: 1. Read the buffer of the contents of the tag using TinyXML-2. 2. Allocate a buffer on the heap of the same size, and copy the existing buffer to it. (This is what the statement `std::string TextString = pText;` does.) 3. For each delimiter in the heap-allocated buffer... a. Allocate another buffer on the heap, and copy the characters from the previous delimiter to the delimiter you just hit. b. Then allocate the buffer AGAIN, to copy it into an std::vector. 4. Then re-allocate every single buffer YET AGAIN, because you need to make a copy of the std::vector in split() to return it to the caller. As you can see, the existing way uses a lot of memory allocations and data marshalling, just to split some text. The problem here is mostly making a temporary std::vector of split text, before doing any actual useful work (most likely, putting it into an array or ANOTHER std::vector - if the latter, then that's yet another memory allocation on top of the memory allocation you already did; this memory allocation is unavoidable, unlike the ones mentioned earlier, which should be removed). So I noticed that since we're iterating over the entire string once (just to shove its contents into a temporary std::vector), and then basically iterating over it again - why can't the whole thing just be more immediate, and just be iterated over once? So that's what I've done here. I've axed the split() function (both of them, actually), and made next_split() and next_split_s(). next_split() will take an existing string and a starting index, and it will find the next occurrence of the given delimiter in the string. Once it does so, it will return the length from the previous starting index, and modify your starting index as well. The price for immediateness is that you're supposed to handle keeping the index of the previous starting index around in order to be able to use the function; updating it after each iteration is also your responsibility. (By the way, next_split() doesn't use SDL_strchr(), because we can't get the length of the substring for the last substring. We could handle this special case specifically, but it'd be uglier; it also introduces iterating over the last substring twice, when we only need to do it once.) next_split_s() does the same thing as next_split(), except it will copy the resulting substring into a buffer that you provide (along with its size). Useful if you don't particularly care about the length of the substring. All callers have been updated accordingly. This new system does not make ANY heap allocations at all; at worst, it allocates a temporary buffer on the stack, but that's only if you use next_split_s(); plus, it'd be a fixed-size buffer, and stack allocations are negligible anyway. This improves performance when loading any sort of XML file, especially loading custom levels - which, on my system at least, I can noticeably tell (there's less of a freeze when I load in to a custom level with lots of scripts). It also decreases memory usage, because the heap isn't being used just to iterate over some delimiters when XML files are loaded.
2021-02-13 01:37:29 +01:00
bool next_split(
size_t* start,
size_t* len,
const char* str,
const char delim
Refactor loading arrays from XML to not use the STL The current way "arrays" from XML files are loaded (before this commit is applied) goes something like this: 1. Read the buffer of the contents of the tag using TinyXML-2. 2. Allocate a buffer on the heap of the same size, and copy the existing buffer to it. (This is what the statement `std::string TextString = pText;` does.) 3. For each delimiter in the heap-allocated buffer... a. Allocate another buffer on the heap, and copy the characters from the previous delimiter to the delimiter you just hit. b. Then allocate the buffer AGAIN, to copy it into an std::vector. 4. Then re-allocate every single buffer YET AGAIN, because you need to make a copy of the std::vector in split() to return it to the caller. As you can see, the existing way uses a lot of memory allocations and data marshalling, just to split some text. The problem here is mostly making a temporary std::vector of split text, before doing any actual useful work (most likely, putting it into an array or ANOTHER std::vector - if the latter, then that's yet another memory allocation on top of the memory allocation you already did; this memory allocation is unavoidable, unlike the ones mentioned earlier, which should be removed). So I noticed that since we're iterating over the entire string once (just to shove its contents into a temporary std::vector), and then basically iterating over it again - why can't the whole thing just be more immediate, and just be iterated over once? So that's what I've done here. I've axed the split() function (both of them, actually), and made next_split() and next_split_s(). next_split() will take an existing string and a starting index, and it will find the next occurrence of the given delimiter in the string. Once it does so, it will return the length from the previous starting index, and modify your starting index as well. The price for immediateness is that you're supposed to handle keeping the index of the previous starting index around in order to be able to use the function; updating it after each iteration is also your responsibility. (By the way, next_split() doesn't use SDL_strchr(), because we can't get the length of the substring for the last substring. We could handle this special case specifically, but it'd be uglier; it also introduces iterating over the last substring twice, when we only need to do it once.) next_split_s() does the same thing as next_split(), except it will copy the resulting substring into a buffer that you provide (along with its size). Useful if you don't particularly care about the length of the substring. All callers have been updated accordingly. This new system does not make ANY heap allocations at all; at worst, it allocates a temporary buffer on the stack, but that's only if you use next_split_s(); plus, it'd be a fixed-size buffer, and stack allocations are negligible anyway. This improves performance when loading any sort of XML file, especially loading custom levels - which, on my system at least, I can noticeably tell (there's less of a freeze when I load in to a custom level with lots of scripts). It also decreases memory usage, because the heap isn't being used just to iterate over some delimiters when XML files are loaded.
2021-02-13 01:37:29 +01:00
) {
size_t idx = 0;
*len = 0;
if (str[idx] == '\0')
{
return false;
}
while (true)
{
if (str[idx] == delim)
{
*start += 1;
return true;
}
else if (str[idx] == '\0')
{
return true;
}
idx += 1;
*start += 1;
*len += 1;
}
2020-01-01 21:29:24 +01:00
}
Refactor loading arrays from XML to not use the STL The current way "arrays" from XML files are loaded (before this commit is applied) goes something like this: 1. Read the buffer of the contents of the tag using TinyXML-2. 2. Allocate a buffer on the heap of the same size, and copy the existing buffer to it. (This is what the statement `std::string TextString = pText;` does.) 3. For each delimiter in the heap-allocated buffer... a. Allocate another buffer on the heap, and copy the characters from the previous delimiter to the delimiter you just hit. b. Then allocate the buffer AGAIN, to copy it into an std::vector. 4. Then re-allocate every single buffer YET AGAIN, because you need to make a copy of the std::vector in split() to return it to the caller. As you can see, the existing way uses a lot of memory allocations and data marshalling, just to split some text. The problem here is mostly making a temporary std::vector of split text, before doing any actual useful work (most likely, putting it into an array or ANOTHER std::vector - if the latter, then that's yet another memory allocation on top of the memory allocation you already did; this memory allocation is unavoidable, unlike the ones mentioned earlier, which should be removed). So I noticed that since we're iterating over the entire string once (just to shove its contents into a temporary std::vector), and then basically iterating over it again - why can't the whole thing just be more immediate, and just be iterated over once? So that's what I've done here. I've axed the split() function (both of them, actually), and made next_split() and next_split_s(). next_split() will take an existing string and a starting index, and it will find the next occurrence of the given delimiter in the string. Once it does so, it will return the length from the previous starting index, and modify your starting index as well. The price for immediateness is that you're supposed to handle keeping the index of the previous starting index around in order to be able to use the function; updating it after each iteration is also your responsibility. (By the way, next_split() doesn't use SDL_strchr(), because we can't get the length of the substring for the last substring. We could handle this special case specifically, but it'd be uglier; it also introduces iterating over the last substring twice, when we only need to do it once.) next_split_s() does the same thing as next_split(), except it will copy the resulting substring into a buffer that you provide (along with its size). Useful if you don't particularly care about the length of the substring. All callers have been updated accordingly. This new system does not make ANY heap allocations at all; at worst, it allocates a temporary buffer on the stack, but that's only if you use next_split_s(); plus, it'd be a fixed-size buffer, and stack allocations are negligible anyway. This improves performance when loading any sort of XML file, especially loading custom levels - which, on my system at least, I can noticeably tell (there's less of a freeze when I load in to a custom level with lots of scripts). It also decreases memory usage, because the heap isn't being used just to iterate over some delimiters when XML files are loaded.
2021-02-13 01:37:29 +01:00
bool next_split_s(
char buffer[],
const size_t buffer_size,
size_t* start,
const char* str,
const char delim
Refactor loading arrays from XML to not use the STL The current way "arrays" from XML files are loaded (before this commit is applied) goes something like this: 1. Read the buffer of the contents of the tag using TinyXML-2. 2. Allocate a buffer on the heap of the same size, and copy the existing buffer to it. (This is what the statement `std::string TextString = pText;` does.) 3. For each delimiter in the heap-allocated buffer... a. Allocate another buffer on the heap, and copy the characters from the previous delimiter to the delimiter you just hit. b. Then allocate the buffer AGAIN, to copy it into an std::vector. 4. Then re-allocate every single buffer YET AGAIN, because you need to make a copy of the std::vector in split() to return it to the caller. As you can see, the existing way uses a lot of memory allocations and data marshalling, just to split some text. The problem here is mostly making a temporary std::vector of split text, before doing any actual useful work (most likely, putting it into an array or ANOTHER std::vector - if the latter, then that's yet another memory allocation on top of the memory allocation you already did; this memory allocation is unavoidable, unlike the ones mentioned earlier, which should be removed). So I noticed that since we're iterating over the entire string once (just to shove its contents into a temporary std::vector), and then basically iterating over it again - why can't the whole thing just be more immediate, and just be iterated over once? So that's what I've done here. I've axed the split() function (both of them, actually), and made next_split() and next_split_s(). next_split() will take an existing string and a starting index, and it will find the next occurrence of the given delimiter in the string. Once it does so, it will return the length from the previous starting index, and modify your starting index as well. The price for immediateness is that you're supposed to handle keeping the index of the previous starting index around in order to be able to use the function; updating it after each iteration is also your responsibility. (By the way, next_split() doesn't use SDL_strchr(), because we can't get the length of the substring for the last substring. We could handle this special case specifically, but it'd be uglier; it also introduces iterating over the last substring twice, when we only need to do it once.) next_split_s() does the same thing as next_split(), except it will copy the resulting substring into a buffer that you provide (along with its size). Useful if you don't particularly care about the length of the substring. All callers have been updated accordingly. This new system does not make ANY heap allocations at all; at worst, it allocates a temporary buffer on the stack, but that's only if you use next_split_s(); plus, it'd be a fixed-size buffer, and stack allocations are negligible anyway. This improves performance when loading any sort of XML file, especially loading custom levels - which, on my system at least, I can noticeably tell (there's less of a freeze when I load in to a custom level with lots of scripts). It also decreases memory usage, because the heap isn't being used just to iterate over some delimiters when XML files are loaded.
2021-02-13 01:37:29 +01:00
) {
size_t len = 0;
const size_t prev_start = *start;
const bool retval = next_split(start, &len, &str[*start], delim);
if (retval)
{
/* Using SDL_strlcpy() here results in calling SDL_strlen() */
/* on the whole string, which results in a visible freeze */
/* if it's a very large string */
const size_t length = SDL_min(buffer_size - 1, len);
SDL_memcpy(buffer, &str[prev_start], length);
buffer[length] = '\0';
}
return retval;
2020-01-01 21:29:24 +01:00
}
UtilityClass::UtilityClass(void) :
2020-01-01 21:29:24 +01:00
glow(0),
glowdir(0)
2020-01-01 21:29:24 +01:00
{
slowsine = 0;
2020-01-01 21:29:24 +01:00
}
std::string UtilityClass::String( int _v )
{
std::ostringstream os;
os << _v;
return(os.str());
2020-01-01 21:29:24 +01:00
}
int UtilityClass::Int(const char* str, int fallback /*= 0*/)
{
if (!is_number(str))
{
return fallback;
}
return (int) SDL_strtol(str, NULL, 0);
}
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 17:13:46 +01:00
int UtilityClass::hms_to_seconds(int h, int m, int s)
2020-01-01 21:29:24 +01:00
{
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 17:13:46 +01:00
return h*3600 + m*60 + s;
}
void UtilityClass::format_time(char* buffer, const size_t buffer_size, int seconds, int frames, bool always_minutes)
{
int s = seconds % 60;
int m = (seconds / 60) % 60;
int h = seconds / 3600;
if (h > 0)
{
/* H:MM:SS / H:MM:SS.CC */
vformat_buf(buffer, buffer_size,
loc::gettext(frames == -1 ? "{hrs}:{min|digits=2}:{sec|digits=2}" : "{hrs}:{min|digits=2}:{sec|digits=2}.{cen|digits=2}"),
"hrs:int, min:int, sec:int, cen:int",
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 17:13:46 +01:00
h, m, s, frames * 100 / 30
);
}
else if (m > 0 || always_minutes || frames == -1)
{
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 17:13:46 +01:00
/* M:SS / M:SS.CC */
vformat_buf(buffer, buffer_size,
loc::gettext(frames == -1 ? "{min}:{sec|digits=2}" : "{min}:{sec|digits=2}.{cen|digits=2}"),
"min:int, sec:int, cen:int",
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 17:13:46 +01:00
m, s, frames * 100 / 30
);
}
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 17:13:46 +01:00
else
{
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 17:13:46 +01:00
/* S.CC */
vformat_buf(buffer, buffer_size,
loc::gettext("{sec}.{cen|digits=2}"),
"sec:int, cen:int",
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 17:13:46 +01:00
s, frames * 100 / 30
);
}
2020-01-01 21:29:24 +01:00
}
std::string UtilityClass::timestring( int t )
{
//given a time t in frames, return a time in seconds
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 17:13:46 +01:00
char output[SCREEN_WIDTH_CHARS + 1];
format_time(output, sizeof(output), t / 30, t % 30, false);
return output;
2020-01-01 21:29:24 +01:00
}
Add a system for selecting between wordy/wordy2 Some languages have different spellings of wordy numbers based on the gender of the things they're counting (uno crewmate versus una trinket) or what a number's role is in the sentence (e.g. twenta out of twentu). We've always had the idea we couldn't support such complex differences though, because the game can't be adapted to know what gender each object will have and what word classes might exist in other languages, so translators would in those cases just have to forgo the wordy numbers and just let the game use "20 out of 20". A solution we came up semi-recently though (after all translations were finished except for Arabic), was to allow the translator to define however many classes of wordy numbers they need, and fill them all out. This would not need the game to be *adapted* for every language's specific grammar and word genders/classes. Instead, the translator would just choose their correct self-defined class at the time they use `wordy` in the VFormat placeholder. Something like {n|wordy|class=feminine}, or {n|wordy_feminine}. So this would benefit several languages, but we came up with the solution a little late for all languages to benefit from it. The Arabic translators asked for two separate classes of wordy numbers though, so my plan is to first just have a second list of wordy numbers (translation2 in numbers.xml), which can be accessed by passing the `wordy2` flag to VFormat, instead of `wordy`. Once 2.4 is released, we can take our time to do it properly. This would involve the ability for translators to define however many classes they need, to name them what they want, and this name would then be useable in VFormat placeholders. We can convert all existing translations to have one class defined by default, such as "wordy", or "translation" depending on implementation, but there's not so much concern for maintaining backwards compatibility here, so we can do a mass-switchover for all language files. That said, it wouldn't be too hard to add a special case for "translation" being "wordy" either. We can then ask translators if they would like to change anything with the new system in place. For now, we can use this system for Arabic, maybe Spanish since there were complaints about uno/una, and *maybe* Dutch (it has a thing where the number "one" is often capitalized differently, but it's not mandatory per se)
2024-01-06 04:15:06 +01:00
std::string UtilityClass::number_words(int _t, const char* number_class)
2020-01-01 21:29:24 +01:00
{
if (loc::lang != "en")
{
Add a system for selecting between wordy/wordy2 Some languages have different spellings of wordy numbers based on the gender of the things they're counting (uno crewmate versus una trinket) or what a number's role is in the sentence (e.g. twenta out of twentu). We've always had the idea we couldn't support such complex differences though, because the game can't be adapted to know what gender each object will have and what word classes might exist in other languages, so translators would in those cases just have to forgo the wordy numbers and just let the game use "20 out of 20". A solution we came up semi-recently though (after all translations were finished except for Arabic), was to allow the translator to define however many classes of wordy numbers they need, and fill them all out. This would not need the game to be *adapted* for every language's specific grammar and word genders/classes. Instead, the translator would just choose their correct self-defined class at the time they use `wordy` in the VFormat placeholder. Something like {n|wordy|class=feminine}, or {n|wordy_feminine}. So this would benefit several languages, but we came up with the solution a little late for all languages to benefit from it. The Arabic translators asked for two separate classes of wordy numbers though, so my plan is to first just have a second list of wordy numbers (translation2 in numbers.xml), which can be accessed by passing the `wordy2` flag to VFormat, instead of `wordy`. Once 2.4 is released, we can take our time to do it properly. This would involve the ability for translators to define however many classes they need, to name them what they want, and this name would then be useable in VFormat placeholders. We can convert all existing translations to have one class defined by default, such as "wordy", or "translation" depending on implementation, but there's not so much concern for maintaining backwards compatibility here, so we can do a mass-switchover for all language files. That said, it wouldn't be too hard to add a special case for "translation" being "wordy" either. We can then ask translators if they would like to change anything with the new system in place. For now, we can use this system for Arabic, maybe Spanish since there were complaints about uno/una, and *maybe* Dutch (it has a thing where the number "one" is often capitalized differently, but it's not mandatory per se)
2024-01-06 04:15:06 +01:00
return loc::getnumber(_t, number_class);
}
static const std::string ones_place[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"};
static const std::string tens_place[] = {"Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"};
static const std::string teens[] = {"Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
if (_t < 0 || _t > 100)
{
return String(_t);
}
else if (_t == 0)
{
return "Zero";
}
else if (_t == 100)
{
return "One Hundred";
}
else if (_t >= 1 && _t <= 9)
{
return ones_place[_t-1];
}
else if (_t >= 11 && _t <= 19)
{
return teens[_t-11];
}
else if (_t % 10 == 0)
{
return tens_place[(_t/10)-1];
}
else
{
return tens_place[(_t/10)-1] + " " + ones_place[(_t%10)-1];
}
2020-01-01 21:29:24 +01:00
}
bool UtilityClass::intersects( SDL_Rect A, SDL_Rect B )
{
return (SDL_HasIntersection(&A, &B) == SDL_TRUE);
2020-01-01 21:29:24 +01:00
}
void UtilityClass::updateglow(void)
2020-01-01 21:29:24 +01:00
{
slowsine++;
if (slowsine >= 64) slowsine = 0;
if (glowdir == 0) {
glow+=2;
if (glow >= 62) glowdir = 1;
}else {
glow-=2;
if (glow < 2) glowdir = 0;
}
2020-01-01 21:29:24 +01:00
}
bool is_number(const char* str)
{
if (!SDL_isdigit(str[0]) && str[0] != '-')
{
return false;
}
if (str[0] == '-' && str[1] == '\0')
{
return false;
}
for (size_t i = 1; str[i] != '\0'; ++i)
{
if (!SDL_isdigit(str[i]))
{
return false;
}
}
return true;
}
bool is_positive_num(const char* str, const bool hex)
{
if (str[0] == '\0')
{
return false;
}
for (size_t i = 0; str[i] != '\0'; ++i)
{
if (hex)
{
if (!SDL_isxdigit(str[i]))
{
return false;
}
}
else
{
if (!SDL_isdigit(str[i]))
{
return false;
}
}
}
return true;
}
bool endsWith(const char* str, const char* suffix)
{
const size_t str_size = SDL_strlen(str);
const size_t suffix_size = SDL_strlen(suffix);
if (str_size < suffix_size)
{
return false;
}
return SDL_strcmp(&str[str_size - suffix_size], suffix) == 0;
}
void VVV_fillstring(
char* buffer,
const size_t buffer_size,
const char fillchar
) {
SDL_memset(buffer, fillchar, buffer_size - 1);
buffer[buffer_size - 1] = '\0';
}
void _VVV_between(
const char* original,
const size_t left_length,
char* middle,
const size_t right_length,
const size_t middle_size
) {
size_t middle_length = SDL_strlen(original);
middle_length -= left_length + right_length;
SDL_strlcpy(
middle,
&original[left_length],
SDL_min(middle_length + 1, middle_size)
);
}