2021-02-21 00:40:11 +01:00
|
|
|
#define CL_DEFINITION
|
2021-02-20 08:19:09 +01:00
|
|
|
#include "CustomLevels.h"
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
#include <physfs.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string>
|
|
|
|
#include <tinyxml2.h>
|
|
|
|
|
2022-12-01 07:30:16 +01:00
|
|
|
#include "Alloc.h"
|
2021-09-25 01:37:27 +02:00
|
|
|
#include "Constants.h"
|
2021-02-21 00:40:11 +01:00
|
|
|
#include "Editor.h"
|
2021-02-20 05:51:25 +01:00
|
|
|
#include "Enums.h"
|
|
|
|
#include "FileSystemUtils.h"
|
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-01-02 05:14:53 +01:00
|
|
|
#include "Font.h"
|
2021-02-20 05:51:25 +01:00
|
|
|
#include "Game.h"
|
|
|
|
#include "Graphics.h"
|
|
|
|
#include "GraphicsUtil.h"
|
|
|
|
#include "KeyPoll.h"
|
2022-12-30 22:57:24 +01:00
|
|
|
#include "Localization.h"
|
|
|
|
#include "LocalizationStorage.h"
|
2021-02-20 05:51:25 +01:00
|
|
|
#include "Map.h"
|
2023-01-07 19:28:07 +01:00
|
|
|
#include "Screen.h"
|
2021-02-20 05:51:25 +01:00
|
|
|
#include "Script.h"
|
2023-02-23 04:11:36 +01:00
|
|
|
#include "UTF8.h"
|
2021-02-20 05:51:25 +01:00
|
|
|
#include "UtilityClass.h"
|
|
|
|
#include "Vlogging.h"
|
|
|
|
#include "XMLUtils.h"
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#define SCNx32 "x"
|
|
|
|
#define SCNu32 "u"
|
|
|
|
#else
|
|
|
|
#ifndef __STDC_FORMAT_MACROS
|
|
|
|
#define __STDC_FORMAT_MACROS
|
|
|
|
#endif
|
|
|
|
#ifndef _POSIX_SOURCE
|
|
|
|
#define _POSIX_SOURCE
|
|
|
|
#endif
|
|
|
|
#include <inttypes.h>
|
|
|
|
#endif
|
|
|
|
|
2021-09-25 01:37:27 +02:00
|
|
|
#define VMULT(y) (y * SCREEN_WIDTH_TILES * maxwidth)
|
|
|
|
#define Y_INBOUNDS(y) (y >= 0 && y < SCREEN_HEIGHT_TILES * maxheight)
|
|
|
|
|
2021-02-21 00:45:48 +01:00
|
|
|
RoomProperty::RoomProperty(void)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
tileset=0;
|
|
|
|
tilecol=0;
|
|
|
|
warpdir=0;
|
|
|
|
platx1=0;
|
|
|
|
platy1=0;
|
|
|
|
platx2=320;
|
|
|
|
platy2=240;
|
|
|
|
platv=4;
|
|
|
|
enemyx1=0;
|
|
|
|
enemyy1=0;
|
|
|
|
enemyx2=320;
|
|
|
|
enemyy2=240;
|
|
|
|
enemytype=0;
|
|
|
|
directmode=0;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
customlevelclass::customlevelclass(void)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
// comparison, not case sensitive.
|
|
|
|
static bool compare_nocase (std::string first, std::string second)
|
|
|
|
{
|
|
|
|
unsigned int i=0;
|
|
|
|
while ( (i<first.length()) && (i<second.length()) )
|
|
|
|
{
|
|
|
|
if (SDL_tolower(first[i])<SDL_tolower(second[i]))
|
|
|
|
return true;
|
|
|
|
else if (SDL_tolower(first[i])>SDL_tolower(second[i]))
|
|
|
|
return false;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
if (first.length()<second.length())
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-12-30 22:57:24 +01:00
|
|
|
/* translate_title and translate_creator are used to display default title/author
|
|
|
|
* as being translated, while they're actually stored in English in the level file.
|
|
|
|
* This way we translate "Untitled Level" and "Unknown" without
|
|
|
|
* spreading around translations in level files posted online! */
|
2024-02-03 00:58:46 +01:00
|
|
|
bool translate_title(const std::string& title)
|
2022-12-30 22:57:24 +01:00
|
|
|
{
|
2024-02-03 00:58:46 +01:00
|
|
|
return title == "Untitled Level";
|
2022-12-30 22:57:24 +01:00
|
|
|
}
|
|
|
|
|
2024-02-03 00:58:46 +01:00
|
|
|
bool translate_creator(const std::string& creator)
|
2022-12-30 22:57:24 +01:00
|
|
|
{
|
2024-02-03 00:58:46 +01:00
|
|
|
return creator == "Unknown";
|
2022-12-30 22:57:24 +01:00
|
|
|
}
|
|
|
|
|
2021-02-20 05:51:25 +01:00
|
|
|
static void levelZipCallback(const char* filename)
|
|
|
|
{
|
|
|
|
if (!FILESYSTEM_isFile(filename))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (endsWith(filename, ".zip"))
|
|
|
|
{
|
|
|
|
FILESYSTEM_loadZip(filename);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
void customlevelclass::loadZips(void)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
FILESYSTEM_enumerateLevelDirFileNames(levelZipCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void replace_all(std::string& str, const std::string& from, const std::string& to)
|
|
|
|
{
|
|
|
|
if (from.empty())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t start_pos = 0;
|
|
|
|
|
|
|
|
while ((start_pos = str.find(from, start_pos)) != std::string::npos)
|
|
|
|
{
|
|
|
|
str.replace(start_pos, from.length(), to);
|
|
|
|
start_pos += to.length(); //In case `to` contains `from`, like replacing 'x' with 'yx'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string find_tag(const std::string& buf, const std::string& start, const std::string& end)
|
|
|
|
{
|
|
|
|
size_t tag = buf.find(start);
|
|
|
|
|
|
|
|
if (tag == std::string::npos)
|
|
|
|
{
|
|
|
|
//No start tag
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t tag_start = tag + start.size();
|
|
|
|
size_t tag_close = buf.find(end, tag_start);
|
|
|
|
|
|
|
|
if (tag_close == std::string::npos)
|
|
|
|
{
|
|
|
|
//No close tag
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t tag_len = tag_close - tag_start;
|
|
|
|
std::string value(buf.substr(tag_start, tag_len));
|
|
|
|
|
|
|
|
//Encode special XML entities
|
|
|
|
replace_all(value, """, "\"");
|
|
|
|
replace_all(value, "&", "&");
|
|
|
|
replace_all(value, "'", "'");
|
|
|
|
replace_all(value, "<", "<");
|
|
|
|
replace_all(value, ">", ">");
|
|
|
|
|
|
|
|
//Encode general XML entities
|
|
|
|
size_t start_pos = 0;
|
|
|
|
while ((start_pos = value.find("&#", start_pos)) != std::string::npos)
|
|
|
|
{
|
|
|
|
if (start_pos + 2 >= value.length())
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hex = value[start_pos + 2] == 'x';
|
|
|
|
size_t end = value.find(';', start_pos);
|
|
|
|
|
|
|
|
if (end == std::string::npos)
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t real_start = start_pos + 2 + ((int) hex);
|
|
|
|
std::string number(value.substr(real_start, end - real_start));
|
|
|
|
|
|
|
|
if (!is_positive_num(number.c_str(), hex))
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t character = 0;
|
|
|
|
if (hex)
|
|
|
|
{
|
|
|
|
SDL_sscanf(number.c_str(), "%" SCNx32, &character);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
SDL_sscanf(number.c_str(), "%" SCNu32, &character);
|
|
|
|
}
|
2023-02-23 04:11:36 +01:00
|
|
|
value.replace(start_pos, end - start_pos + 1, UTF8_encode(character).bytes);
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define TAG_FINDER(NAME, TAG) \
|
|
|
|
static std::string NAME(const std::string& buf) \
|
|
|
|
{ \
|
|
|
|
return find_tag(buf, "<" TAG ">", "</" TAG ">"); \
|
|
|
|
}
|
|
|
|
|
|
|
|
TAG_FINDER(find_metadata, "MetaData") //only for checking that it exists
|
|
|
|
|
|
|
|
TAG_FINDER(find_creator, "Creator")
|
|
|
|
TAG_FINDER(find_title, "Title")
|
|
|
|
TAG_FINDER(find_desc1, "Desc1")
|
|
|
|
TAG_FINDER(find_desc2, "Desc2")
|
|
|
|
TAG_FINDER(find_desc3, "Desc3")
|
|
|
|
TAG_FINDER(find_website, "website")
|
2023-01-20 03:56:17 +01:00
|
|
|
TAG_FINDER(find_font, "font")
|
2024-01-03 20:09:23 +01:00
|
|
|
TAG_FINDER(find_rtl, "rtl")
|
2021-02-20 05:51:25 +01:00
|
|
|
|
2022-06-19 22:41:48 +02:00
|
|
|
/* For CliPlaytestArgs */
|
|
|
|
TAG_FINDER(find_playtest, "Playtest")
|
|
|
|
TAG_FINDER(find_playx, "playx")
|
|
|
|
TAG_FINDER(find_playy, "playy")
|
|
|
|
TAG_FINDER(find_playrx, "playrx")
|
|
|
|
TAG_FINDER(find_playry, "playry")
|
|
|
|
TAG_FINDER(find_playgc, "playgc")
|
|
|
|
TAG_FINDER(find_playmusic, "playmusic")
|
|
|
|
|
2021-02-20 05:51:25 +01:00
|
|
|
#undef TAG_FINDER
|
|
|
|
|
|
|
|
static void levelMetaDataCallback(const char* filename)
|
|
|
|
{
|
2021-02-21 00:40:11 +01:00
|
|
|
extern customlevelclass cl;
|
2021-02-20 05:51:25 +01:00
|
|
|
LevelMetaData temp;
|
|
|
|
std::string filename_ = filename;
|
|
|
|
|
|
|
|
if (!endsWith(filename, ".vvvvvv")
|
|
|
|
|| !FILESYSTEM_isFile(filename)
|
|
|
|
|| FILESYSTEM_isMounted(filename))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
if (cl.getLevelMetaData(filename_, temp))
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2021-02-21 00:40:11 +01:00
|
|
|
cl.ListOfMetaData.push_back(temp);
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
2023-05-16 03:12:28 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
vlog_warn("Level %s not found :(", filename_.c_str());
|
|
|
|
}
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
void customlevelclass::getDirectoryData(void)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
ListOfMetaData.clear();
|
|
|
|
|
|
|
|
FILESYSTEM_clearLevelDirError();
|
|
|
|
|
|
|
|
loadZips();
|
|
|
|
|
|
|
|
FILESYSTEM_enumerateLevelDirFileNames(levelMetaDataCallback);
|
|
|
|
|
|
|
|
for(size_t i = 0; i < ListOfMetaData.size(); i++)
|
|
|
|
{
|
|
|
|
for(size_t k = 0; k < ListOfMetaData.size(); k++)
|
|
|
|
{
|
|
|
|
if(compare_nocase(ListOfMetaData[i].title, ListOfMetaData[k].title ))
|
|
|
|
{
|
|
|
|
std::swap(ListOfMetaData[i] , ListOfMetaData[k]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2022-06-19 22:41:48 +02:00
|
|
|
bool customlevelclass::getLevelMetaDataAndPlaytestArgs(const std::string& _path, LevelMetaData& _data, CliPlaytestArgs* pt_args)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
unsigned char *uMem;
|
2023-03-18 23:12:24 +01:00
|
|
|
FILESYSTEM_loadFileToMemory(_path.c_str(), &uMem, NULL);
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
if (uMem == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string buf((char*) uMem);
|
|
|
|
|
2022-12-01 07:30:16 +01:00
|
|
|
VVV_free(uMem);
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
if (find_metadata(buf) == "")
|
|
|
|
{
|
|
|
|
vlog_warn("Couldn't load metadata for %s", _path.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-02-03 00:58:46 +01:00
|
|
|
_data.creator = find_creator(buf);
|
|
|
|
_data.creator_is_gettext = translate_creator(_data.creator);
|
|
|
|
_data.title = find_title(buf);
|
|
|
|
_data.title_is_gettext = translate_title(_data.title);
|
2021-02-20 05:51:25 +01:00
|
|
|
_data.Desc1 = find_desc1(buf);
|
|
|
|
_data.Desc2 = find_desc2(buf);
|
|
|
|
_data.Desc3 = find_desc3(buf);
|
|
|
|
_data.website = find_website(buf);
|
2023-01-20 03:56:17 +01:00
|
|
|
if (!font::find_main_font_by_name(find_font(buf).c_str(), &_data.level_main_font_idx))
|
|
|
|
{
|
|
|
|
_data.level_main_font_idx = font::get_font_idx_8x8();
|
|
|
|
}
|
2024-01-03 20:09:23 +01:00
|
|
|
_data.rtl = help.Int(find_rtl(buf).c_str());
|
2021-02-20 05:51:25 +01:00
|
|
|
|
2023-01-19 20:15:17 +01:00
|
|
|
|
2022-06-19 22:41:48 +02:00
|
|
|
if (pt_args != NULL)
|
|
|
|
{
|
|
|
|
const std::string playtest = find_playtest(buf);
|
|
|
|
if (playtest == "")
|
|
|
|
{
|
|
|
|
pt_args->valid = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pt_args->valid = true;
|
|
|
|
pt_args->x = help.Int(find_playx(playtest).c_str());
|
|
|
|
pt_args->y = help.Int(find_playy(playtest).c_str());
|
|
|
|
pt_args->rx = help.Int(find_playrx(playtest).c_str());
|
|
|
|
pt_args->ry = help.Int(find_playry(playtest).c_str());
|
|
|
|
pt_args->gc = help.Int(find_playgc(playtest).c_str());
|
|
|
|
pt_args->music = help.Int(find_playmusic(playtest).c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-20 05:51:25 +01:00
|
|
|
_data.filename = _path;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-06-19 22:41:48 +02:00
|
|
|
bool customlevelclass::getLevelMetaData(const std::string& _path, LevelMetaData& _data)
|
|
|
|
{
|
|
|
|
return getLevelMetaDataAndPlaytestArgs(_path, _data, NULL);
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
void customlevelclass::reset(void)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
version=2; //New smaller format change is 2
|
|
|
|
|
|
|
|
mapwidth=5;
|
|
|
|
mapheight=5;
|
|
|
|
|
2022-12-30 22:57:24 +01:00
|
|
|
title="Untitled Level"; // Already translatable
|
2021-02-21 01:04:50 +01:00
|
|
|
creator="Unknown";
|
2021-02-20 05:51:25 +01:00
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
levmusic=0;
|
2021-02-20 05:51:25 +01:00
|
|
|
|
2021-02-21 01:01:39 +01:00
|
|
|
customentities.clear();
|
2021-02-20 05:51:25 +01:00
|
|
|
levmusic=0;
|
|
|
|
|
|
|
|
for (int j = 0; j < maxheight; j++)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < maxwidth; i++)
|
|
|
|
{
|
2021-02-21 00:54:24 +01:00
|
|
|
roomproperties[i+(j*maxwidth)].tileset=0;
|
|
|
|
roomproperties[i+(j*maxwidth)].tilecol=(i+j)%32;
|
|
|
|
roomproperties[i+(j*maxwidth)].roomname="";
|
|
|
|
roomproperties[i+(j*maxwidth)].warpdir=0;
|
|
|
|
roomproperties[i+(j*maxwidth)].platx1=0;
|
|
|
|
roomproperties[i+(j*maxwidth)].platy1=0;
|
|
|
|
roomproperties[i+(j*maxwidth)].platx2=320;
|
|
|
|
roomproperties[i+(j*maxwidth)].platy2=240;
|
|
|
|
roomproperties[i+(j*maxwidth)].platv=4;
|
|
|
|
roomproperties[i+(j*maxwidth)].enemyx1=0;
|
|
|
|
roomproperties[i+(j*maxwidth)].enemyy1=0;
|
|
|
|
roomproperties[i+(j*maxwidth)].enemyx2=320;
|
|
|
|
roomproperties[i+(j*maxwidth)].enemyy2=240;
|
|
|
|
roomproperties[i+(j*maxwidth)].enemytype=0;
|
|
|
|
roomproperties[i+(j*maxwidth)].directmode=0;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SDL_zeroa(contents);
|
|
|
|
|
|
|
|
script.clearcustom();
|
|
|
|
|
|
|
|
onewaycol_override = false;
|
2022-11-21 20:02:38 +01:00
|
|
|
|
2023-03-18 22:48:20 +01:00
|
|
|
script.textbox_colours.clear();
|
|
|
|
script.add_default_colours();
|
2022-12-12 00:05:20 +01:00
|
|
|
map.specialroomnames.clear();
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
const int* customlevelclass::loadlevel( int rxi, int ryi )
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
//Set up our buffer array to be picked up by mapclass
|
|
|
|
rxi -= 100;
|
|
|
|
ryi -= 100;
|
|
|
|
if(rxi<0)rxi+=mapwidth;
|
|
|
|
if(ryi<0)ryi+=mapheight;
|
|
|
|
if(rxi>=mapwidth)rxi-=mapwidth;
|
|
|
|
if(ryi>=mapheight)ryi-=mapheight;
|
|
|
|
|
|
|
|
static int result[1200];
|
|
|
|
|
|
|
|
for (int j = 0; j < 30; j++)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 40; i++)
|
|
|
|
{
|
|
|
|
result[i + j*40] = gettile(rxi, ryi, i, j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::getlevelcol(const int tileset, const int tilecol)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
if(tileset==0) //Space Station
|
|
|
|
{
|
|
|
|
return tilecol;
|
|
|
|
}
|
|
|
|
else if(tileset==1) //Outside
|
|
|
|
{
|
|
|
|
return 32+tilecol;
|
|
|
|
}
|
|
|
|
else if(tileset==2) //Lab
|
|
|
|
{
|
|
|
|
return 40+tilecol;
|
|
|
|
}
|
|
|
|
else if(tileset==3) //Warp Zone
|
|
|
|
{
|
|
|
|
return 46+tilecol;
|
|
|
|
}
|
|
|
|
else if(tileset==4) //Ship
|
|
|
|
{
|
|
|
|
return 52+tilecol;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::getenemycol(int t)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
switch(t)
|
|
|
|
{
|
|
|
|
//RED
|
|
|
|
case 3:
|
|
|
|
case 7:
|
|
|
|
case 12:
|
|
|
|
case 23:
|
|
|
|
case 28:
|
|
|
|
case 34:
|
|
|
|
case 42:
|
|
|
|
case 48:
|
|
|
|
case 58:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
//GREEN
|
|
|
|
case 5:
|
|
|
|
case 9:
|
|
|
|
case 22:
|
|
|
|
case 25:
|
|
|
|
case 29:
|
|
|
|
case 31:
|
|
|
|
case 38:
|
|
|
|
case 46:
|
|
|
|
case 52:
|
|
|
|
case 53:
|
|
|
|
return 7;
|
|
|
|
break;
|
|
|
|
//BLUE
|
|
|
|
case 1:
|
|
|
|
case 6:
|
|
|
|
case 14:
|
|
|
|
case 27:
|
|
|
|
case 33:
|
|
|
|
case 44:
|
|
|
|
case 50:
|
|
|
|
case 57:
|
|
|
|
return 12;
|
|
|
|
break;
|
|
|
|
//YELLOW
|
|
|
|
case 4:
|
|
|
|
case 17:
|
|
|
|
case 24:
|
|
|
|
case 30:
|
|
|
|
case 37:
|
|
|
|
case 45:
|
|
|
|
case 51:
|
|
|
|
case 55:
|
|
|
|
return 9;
|
|
|
|
break;
|
|
|
|
//PURPLE
|
|
|
|
case 2:
|
|
|
|
case 11:
|
|
|
|
case 15:
|
|
|
|
case 19:
|
|
|
|
case 32:
|
|
|
|
case 36:
|
|
|
|
case 49:
|
|
|
|
return 20;
|
|
|
|
break;
|
|
|
|
//CYAN
|
|
|
|
case 8:
|
|
|
|
case 10:
|
|
|
|
case 13:
|
|
|
|
case 18:
|
|
|
|
case 26:
|
|
|
|
case 35:
|
|
|
|
case 41:
|
|
|
|
case 47:
|
|
|
|
case 54:
|
|
|
|
return 11;
|
|
|
|
break;
|
|
|
|
//PINK
|
|
|
|
case 16:
|
|
|
|
case 20:
|
|
|
|
case 39:
|
|
|
|
case 43:
|
|
|
|
case 56:
|
|
|
|
return 8;
|
|
|
|
break;
|
|
|
|
//ORANGE
|
|
|
|
case 21:
|
|
|
|
case 40:
|
|
|
|
return 17;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::getwarpbackground(int rx, int ry)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2021-02-21 00:45:48 +01:00
|
|
|
const RoomProperty* const room = getroomprop(rx, ry);
|
2021-02-20 05:51:25 +01:00
|
|
|
switch(room->tileset)
|
|
|
|
{
|
|
|
|
case 0: //Space Station
|
|
|
|
switch(room->tilecol)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return 3;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
return 2;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
return 4;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
return 3;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
return 2;
|
|
|
|
break;
|
|
|
|
case 11:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
return 3;
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
return 2;
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
return 4;
|
|
|
|
break;
|
|
|
|
case 17:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 18:
|
|
|
|
return 3;
|
|
|
|
break;
|
|
|
|
case 19:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 20:
|
|
|
|
return 4;
|
|
|
|
break;
|
|
|
|
case 21:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 22:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 23:
|
|
|
|
return 4;
|
|
|
|
break;
|
|
|
|
case 24:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 25:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 26:
|
|
|
|
return 3;
|
|
|
|
break;
|
|
|
|
case 27:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 28:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 29:
|
|
|
|
return 4;
|
|
|
|
break;
|
|
|
|
case 30:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 31:
|
|
|
|
return 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1: //Outside
|
|
|
|
switch(room->tilecol)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return 3;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
return 2;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
return 4;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
return 2;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
return 4;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2: //Lab
|
|
|
|
switch(room->tilecol)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
return 2;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
return 3;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
return 4;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3: //Warp Zone
|
|
|
|
switch(room->tilecol)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
return 2;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
return 3;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
return 4;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 4: //Ship
|
|
|
|
switch(room->tilecol)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return 5;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
return 4;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
return 2;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
return 3;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
return 1;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 5: //Tower
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::gettileidx(
|
2021-02-20 05:51:25 +01:00
|
|
|
const int rx,
|
|
|
|
const int ry,
|
|
|
|
const int x,
|
|
|
|
const int y
|
|
|
|
) {
|
|
|
|
const int yoff = y + ry*30;
|
|
|
|
int mult;
|
|
|
|
int idx;
|
|
|
|
|
2021-09-25 01:37:27 +02:00
|
|
|
if (Y_INBOUNDS(yoff))
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
mult = VMULT(yoff);
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
mult = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = x + rx*40 + mult;
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
void customlevelclass::settile(
|
2021-02-20 05:51:25 +01:00
|
|
|
const int rx,
|
|
|
|
const int ry,
|
|
|
|
const int x,
|
|
|
|
const int y,
|
|
|
|
const int t
|
|
|
|
) {
|
|
|
|
const int idx = gettileidx(rx, ry, x, y);
|
|
|
|
|
|
|
|
if (!INBOUNDS_ARR(idx, contents))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
contents[idx] = t;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::gettile(
|
2021-02-20 05:51:25 +01:00
|
|
|
const int rx,
|
|
|
|
const int ry,
|
|
|
|
const int x,
|
|
|
|
const int y
|
|
|
|
) {
|
|
|
|
const int idx = gettileidx(rx, ry, x, y);
|
|
|
|
|
|
|
|
if (!INBOUNDS_ARR(idx, contents))
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return contents[idx];
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::getabstile(const int x, const int y)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
int yoff;
|
|
|
|
|
2021-09-25 01:37:27 +02:00
|
|
|
if (Y_INBOUNDS(y))
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2021-09-25 01:37:27 +02:00
|
|
|
yoff = VMULT(y);
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
yoff = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = x + yoff;
|
|
|
|
|
|
|
|
if (!INBOUNDS_ARR(idx, contents))
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return contents[idx];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::getroompropidx(const int rx, const int ry)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
return rx + ry*maxwidth;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:45:48 +01:00
|
|
|
const RoomProperty* customlevelclass::getroomprop(const int rx, const int ry)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
const int idx = getroompropidx(rx, ry);
|
|
|
|
|
2021-02-21 00:54:24 +01:00
|
|
|
if (INBOUNDS_ARR(idx, roomproperties))
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2021-02-21 00:54:24 +01:00
|
|
|
return &roomproperties[idx];
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-21 00:45:48 +01:00
|
|
|
static RoomProperty blank;
|
2021-02-20 05:51:25 +01:00
|
|
|
blank.tileset = 1;
|
|
|
|
blank.directmode = 1;
|
|
|
|
blank.roomname.clear();
|
|
|
|
|
|
|
|
return ␣
|
|
|
|
}
|
|
|
|
|
|
|
|
#define FOREACH_PROP(NAME, TYPE) \
|
2021-02-21 00:40:11 +01:00
|
|
|
void customlevelclass::setroom##NAME(const int rx, const int ry, const TYPE NAME) \
|
2021-02-20 05:51:25 +01:00
|
|
|
{ \
|
|
|
|
const int idx = getroompropidx(rx, ry); \
|
|
|
|
\
|
2021-02-21 00:54:24 +01:00
|
|
|
if (!INBOUNDS_ARR(idx, roomproperties)) \
|
2021-02-20 05:51:25 +01:00
|
|
|
{ \
|
|
|
|
return; \
|
|
|
|
} \
|
|
|
|
\
|
2021-02-21 00:54:24 +01:00
|
|
|
roomproperties[idx].NAME = NAME; \
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
ROOM_PROPERTIES
|
|
|
|
|
|
|
|
#undef FOREACH_PROP
|
|
|
|
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::absfree( int x, int y )
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
//Returns 0 if tile is not a block, 1 otherwise, abs on grid
|
|
|
|
if(x>=0 && y>=0 && x<mapwidth*40 && y<mapheight*30)
|
|
|
|
{
|
|
|
|
if(getabstile(x, y)==0)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(getabstile(x, y)>=2 && getabstile(x, y)<80)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if(getabstile(x, y)>=680)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
void customlevelclass::findstartpoint(void)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
//Ok! Scan the room for the closest checkpoint
|
|
|
|
int testeditor=-1;
|
|
|
|
//First up; is there a start point on this screen?
|
2021-02-21 01:01:39 +01:00
|
|
|
for(size_t i=0; i<customentities.size(); i++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
//if() on screen
|
2021-02-21 01:01:39 +01:00
|
|
|
if(customentities[i].t==16 && testeditor==-1)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
testeditor=i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(testeditor==-1)
|
|
|
|
{
|
|
|
|
game.edsavex = 160;
|
|
|
|
game.edsavey = 120;
|
|
|
|
game.edsaverx = 100;
|
|
|
|
game.edsavery = 100;
|
|
|
|
game.edsavegc = 0;
|
|
|
|
game.edsavey--;
|
|
|
|
game.edsavedir=1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
//Start point spawn
|
2023-03-29 09:18:42 +02:00
|
|
|
game.edsavex = (customentities[testeditor].x * 8) - 4;
|
|
|
|
game.edsavey = customentities[testeditor].y * 8;
|
|
|
|
game.edsaverx = 100 + customentities[testeditor].rx;
|
|
|
|
game.edsavery = 100 + customentities[testeditor].ry;
|
2021-02-20 05:51:25 +01:00
|
|
|
game.edsavegc = 0;
|
|
|
|
game.edsavey++;
|
2021-02-21 01:01:39 +01:00
|
|
|
game.edsavedir=1-customentities[testeditor].p1;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::findtrinket(int t)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
int ttrinket=0;
|
2021-02-21 01:01:39 +01:00
|
|
|
for(int i=0; i<(int)customentities.size(); i++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
if(i==t) return ttrinket;
|
2021-02-21 01:01:39 +01:00
|
|
|
if(customentities[i].t==9) ttrinket++;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::findcrewmate(int t)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
int ttrinket=0;
|
2021-02-21 01:01:39 +01:00
|
|
|
for(int i=0; i<(int)customentities.size(); i++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
if(i==t) return ttrinket;
|
2021-02-21 01:01:39 +01:00
|
|
|
if(customentities[i].t==15) ttrinket++;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::findwarptoken(int t)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
int ttrinket=0;
|
2021-02-21 01:01:39 +01:00
|
|
|
for(int i=0; i<(int)customentities.size(); i++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
if(i==t) return ttrinket;
|
2021-02-21 01:01:39 +01:00
|
|
|
if(customentities[i].t==13) ttrinket++;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-03-17 05:09:41 +01:00
|
|
|
bool customlevelclass::load(std::string _path)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
|
|
tinyxml2::XMLHandle hDoc(&doc);
|
|
|
|
tinyxml2::XMLElement* pElem;
|
|
|
|
|
|
|
|
reset();
|
2021-02-21 00:40:11 +01:00
|
|
|
ed.reset();
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
static const char *levelDir = "levels/";
|
|
|
|
if (_path.compare(0, SDL_strlen(levelDir), levelDir) != 0)
|
|
|
|
{
|
|
|
|
_path = levelDir + _path;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILESYSTEM_unmountAssets();
|
|
|
|
if (game.cliplaytest && game.playassets != "")
|
|
|
|
{
|
|
|
|
MAYBE_FAIL(FILESYSTEM_mountAssets(game.playassets.c_str()));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MAYBE_FAIL(FILESYSTEM_mountAssets(_path.c_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!FILESYSTEM_loadTiXml2Document(_path.c_str(), doc))
|
|
|
|
{
|
2023-05-17 19:09:59 +02:00
|
|
|
FILESYSTEM_setLevelDirError(
|
|
|
|
loc::gettext("Level {path} not found"),
|
|
|
|
"path:str",
|
|
|
|
_path.c_str()
|
|
|
|
);
|
2021-02-20 05:51:25 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (doc.Error())
|
|
|
|
{
|
2023-05-17 19:09:59 +02:00
|
|
|
FILESYSTEM_setLevelDirError(
|
|
|
|
loc::gettext("Error parsing {path}: {error}"),
|
|
|
|
"path:str, error:str",
|
|
|
|
_path.c_str(),
|
|
|
|
doc.ErrorStr()
|
|
|
|
);
|
2021-02-20 05:51:25 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
ed.loaded_filepath = _path;
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
version = 0;
|
2023-01-20 03:56:17 +01:00
|
|
|
level_font_name = "font";
|
2024-01-03 20:09:23 +01:00
|
|
|
rtl = false;
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
for (pElem = hDoc
|
|
|
|
.FirstChildElement()
|
|
|
|
.FirstChildElement("Data")
|
|
|
|
.FirstChildElement()
|
|
|
|
.ToElement();
|
|
|
|
pElem != NULL;
|
|
|
|
pElem = pElem->NextSiblingElement())
|
|
|
|
{
|
|
|
|
const char* pKey = pElem->Value();
|
|
|
|
const char* pText = pElem->GetText();
|
|
|
|
if(pText == NULL)
|
|
|
|
{
|
|
|
|
pText = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_strcmp(pKey, "MetaData") == 0)
|
|
|
|
{
|
|
|
|
|
|
|
|
for( tinyxml2::XMLElement* subElem = pElem->FirstChildElement(); subElem; subElem= subElem->NextSiblingElement())
|
|
|
|
{
|
|
|
|
const char* pKey_ = subElem->Value();
|
|
|
|
const char* pText_ = subElem->GetText() ;
|
|
|
|
if(pText_ == NULL)
|
|
|
|
{
|
|
|
|
pText_ = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if(SDL_strcmp(pKey_, "Creator") == 0)
|
|
|
|
{
|
2021-02-21 01:04:50 +01:00
|
|
|
creator = pText_;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(SDL_strcmp(pKey_, "Title") == 0)
|
|
|
|
{
|
2021-02-21 01:04:50 +01:00
|
|
|
title = pText_;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(SDL_strcmp(pKey_, "Desc1") == 0)
|
|
|
|
{
|
|
|
|
Desc1 = pText_;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(SDL_strcmp(pKey_, "Desc2") == 0)
|
|
|
|
{
|
|
|
|
Desc2 = pText_;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(SDL_strcmp(pKey_, "Desc3") == 0)
|
|
|
|
{
|
|
|
|
Desc3 = pText_;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(SDL_strcmp(pKey_, "website") == 0)
|
|
|
|
{
|
|
|
|
website = pText_;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(SDL_strcmp(pKey_, "onewaycol_override") == 0)
|
|
|
|
{
|
|
|
|
onewaycol_override = help.Int(pText_);
|
|
|
|
}
|
2023-01-20 03:56:17 +01:00
|
|
|
|
|
|
|
if(SDL_strcmp(pKey_, "font") == 0)
|
|
|
|
{
|
|
|
|
level_font_name = pText_;
|
|
|
|
}
|
2024-01-03 20:09:23 +01:00
|
|
|
|
|
|
|
if(SDL_strcmp(pKey_, "rtl") == 0)
|
|
|
|
{
|
|
|
|
rtl = help.Int(pText_);
|
|
|
|
}
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_strcmp(pKey, "mapwidth") == 0)
|
|
|
|
{
|
|
|
|
mapwidth = help.Int(pText);
|
|
|
|
}
|
|
|
|
if (SDL_strcmp(pKey, "mapheight") == 0)
|
|
|
|
{
|
|
|
|
mapheight = help.Int(pText);
|
|
|
|
}
|
|
|
|
if (SDL_strcmp(pKey, "levmusic") == 0)
|
|
|
|
{
|
|
|
|
levmusic = help.Int(pText);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (SDL_strcmp(pKey, "contents") == 0 && pText[0] != '\0')
|
|
|
|
{
|
|
|
|
int x = 0;
|
|
|
|
int y = 0;
|
|
|
|
|
|
|
|
char buffer[16];
|
|
|
|
size_t start = 0;
|
|
|
|
|
|
|
|
while (next_split_s(buffer, sizeof(buffer), &start, pText, ','))
|
|
|
|
{
|
|
|
|
const int idx = x + maxwidth*40*y;
|
|
|
|
|
|
|
|
if (INBOUNDS_ARR(idx, contents))
|
|
|
|
{
|
|
|
|
contents[idx] = help.Int(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
++x;
|
|
|
|
|
|
|
|
if (x == mapwidth*40)
|
|
|
|
{
|
|
|
|
x = 0;
|
|
|
|
++y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (SDL_strcmp(pKey, "edEntities") == 0)
|
|
|
|
{
|
|
|
|
for( tinyxml2::XMLElement* edEntityEl = pElem->FirstChildElement(); edEntityEl; edEntityEl=edEntityEl->NextSiblingElement())
|
|
|
|
{
|
2021-02-21 00:56:59 +01:00
|
|
|
CustomEntity entity = CustomEntity();
|
2021-02-20 05:51:25 +01:00
|
|
|
const char* text = edEntityEl->GetText();
|
2023-03-29 09:18:42 +02:00
|
|
|
int global_x = 0;
|
|
|
|
int global_y = 0;
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
if (text != NULL)
|
|
|
|
{
|
|
|
|
size_t len = SDL_strlen(text);
|
|
|
|
|
|
|
|
// And now we come to the part where we have to deal with
|
|
|
|
// the terrible decisions of the past.
|
|
|
|
//
|
|
|
|
// For some reason, the closing tag of edentities generated
|
|
|
|
// by 2.2 and below has not only been put on a separate
|
|
|
|
// line, but also indented to match with the opening tag as
|
|
|
|
// well. Like this:
|
|
|
|
//
|
|
|
|
// <edentity ...>contents
|
|
|
|
// </edentity>
|
|
|
|
//
|
|
|
|
// Instead of doing <edentity ...>contents</edentity>.
|
|
|
|
//
|
|
|
|
// This is COMPLETELY terrible. This requires the XML to be
|
|
|
|
// parsed in an extremely specific and quirky way, which
|
|
|
|
// TinyXML-1 just happened to do.
|
|
|
|
//
|
|
|
|
// TinyXML-2 by default interprets the newline and the next
|
|
|
|
// indentation of whitespace literally, so you end up with
|
|
|
|
// tag contents that has a linefeed plus a bunch of extra
|
|
|
|
// spaces. You can't fix this by setting the whitespace
|
|
|
|
// mode to COLLAPSE_WHITESPACE, that does way more than
|
|
|
|
// TinyXML-1 ever did - it removes the leading whitespace
|
|
|
|
// from things like <edentity ...> this</edentity>, and
|
|
|
|
// collapses XML-encoded whitespace like <edentity ...>
|
|
|
|
//    this</edentity>, which TinyXML-1 never did.
|
|
|
|
//
|
|
|
|
// Best solution here is to specifically hardcode removing
|
|
|
|
// the linefeed + the extremely specific amount of
|
|
|
|
// whitespace at the end of the contents.
|
|
|
|
|
|
|
|
if (endsWith(text, "\n ")) // linefeed + exactly 12 spaces
|
|
|
|
{
|
|
|
|
// 12 spaces + 1 linefeed = 13 chars
|
|
|
|
len -= 13;
|
|
|
|
}
|
|
|
|
|
|
|
|
entity.scriptname = std::string(text, len);
|
|
|
|
}
|
2023-03-29 09:18:42 +02:00
|
|
|
edEntityEl->QueryIntAttribute("x", &global_x);
|
|
|
|
edEntityEl->QueryIntAttribute("y", &global_y);
|
|
|
|
entity.rx = global_x / SCREEN_WIDTH_TILES;
|
|
|
|
entity.x = global_x % SCREEN_WIDTH_TILES;
|
|
|
|
entity.ry = global_y / SCREEN_HEIGHT_TILES;
|
|
|
|
entity.y = global_y % SCREEN_HEIGHT_TILES;
|
2021-02-20 05:51:25 +01:00
|
|
|
edEntityEl->QueryIntAttribute("t", &entity.t);
|
|
|
|
|
|
|
|
edEntityEl->QueryIntAttribute("p1", &entity.p1);
|
|
|
|
edEntityEl->QueryIntAttribute("p2", &entity.p2);
|
|
|
|
edEntityEl->QueryIntAttribute("p3", &entity.p3);
|
|
|
|
edEntityEl->QueryIntAttribute("p4", &entity.p4);
|
|
|
|
edEntityEl->QueryIntAttribute("p5", &entity.p5);
|
|
|
|
edEntityEl->QueryIntAttribute("p6", &entity.p6);
|
|
|
|
|
2021-02-21 01:01:39 +01:00
|
|
|
customentities.push_back(entity);
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_strcmp(pKey, "levelMetaData") == 0)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
for( tinyxml2::XMLElement* edLevelClassElement = pElem->FirstChildElement(); edLevelClassElement; edLevelClassElement=edLevelClassElement->NextSiblingElement())
|
|
|
|
{
|
2021-02-21 00:54:24 +01:00
|
|
|
if (!INBOUNDS_ARR(i, roomproperties))
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(edLevelClassElement->GetText() != NULL)
|
|
|
|
{
|
2021-02-21 00:54:24 +01:00
|
|
|
roomproperties[i].roomname = std::string(edLevelClassElement->GetText()) ;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-21 00:54:24 +01:00
|
|
|
edLevelClassElement->QueryIntAttribute("tileset", &roomproperties[i].tileset);
|
|
|
|
edLevelClassElement->QueryIntAttribute("tilecol", &roomproperties[i].tilecol);
|
|
|
|
edLevelClassElement->QueryIntAttribute("platx1", &roomproperties[i].platx1);
|
|
|
|
edLevelClassElement->QueryIntAttribute("platy1", &roomproperties[i].platy1);
|
|
|
|
edLevelClassElement->QueryIntAttribute("platx2", &roomproperties[i].platx2);
|
|
|
|
edLevelClassElement->QueryIntAttribute("platy2", &roomproperties[i].platy2);
|
|
|
|
edLevelClassElement->QueryIntAttribute("platv", &roomproperties[i].platv);
|
|
|
|
edLevelClassElement->QueryIntAttribute("enemyx1", &roomproperties[i].enemyx1);
|
|
|
|
edLevelClassElement->QueryIntAttribute("enemyy1", &roomproperties[i].enemyy1);
|
|
|
|
edLevelClassElement->QueryIntAttribute("enemyx2", &roomproperties[i].enemyx2);
|
|
|
|
edLevelClassElement->QueryIntAttribute("enemyy2", &roomproperties[i].enemyy2);
|
|
|
|
edLevelClassElement->QueryIntAttribute("enemytype", &roomproperties[i].enemytype);
|
|
|
|
edLevelClassElement->QueryIntAttribute("directmode", &roomproperties[i].directmode);
|
|
|
|
|
|
|
|
edLevelClassElement->QueryIntAttribute("warpdir", &roomproperties[i].warpdir);
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
i++;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SDL_strcmp(pKey, "script") == 0 && pText[0] != '\0')
|
|
|
|
{
|
|
|
|
Script script_;
|
|
|
|
bool headerfound = false;
|
|
|
|
|
|
|
|
size_t start = 0;
|
|
|
|
size_t len = 0;
|
|
|
|
size_t prev_start = 0;
|
|
|
|
|
|
|
|
while (next_split(&start, &len, &pText[start], '|'))
|
|
|
|
{
|
|
|
|
if (len > 0 && pText[prev_start + len - 1] == ':')
|
|
|
|
{
|
|
|
|
if (headerfound)
|
|
|
|
{
|
|
|
|
script.customscripts.push_back(script_);
|
|
|
|
}
|
|
|
|
|
|
|
|
script_.name = std::string(&pText[prev_start], len - 1);
|
|
|
|
script_.contents.clear();
|
|
|
|
headerfound = true;
|
|
|
|
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (headerfound)
|
|
|
|
{
|
|
|
|
script_.contents.push_back(std::string(&pText[prev_start], len));
|
|
|
|
}
|
|
|
|
|
|
|
|
next:
|
|
|
|
prev_start = start;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add the last script */
|
|
|
|
if (headerfound)
|
|
|
|
{
|
|
|
|
script.customscripts.push_back(script_);
|
|
|
|
}
|
|
|
|
}
|
2022-11-21 20:02:38 +01:00
|
|
|
|
|
|
|
if (SDL_strcmp(pKey, "TextboxColours") == 0)
|
|
|
|
{
|
|
|
|
for (tinyxml2::XMLElement* textColourElement = pElem->FirstChildElement(); textColourElement; textColourElement = textColourElement->NextSiblingElement())
|
|
|
|
{
|
|
|
|
if (SDL_strcmp(textColourElement->Value(), "colour") == 0)
|
|
|
|
{
|
|
|
|
int r = 255;
|
|
|
|
int g = 255;
|
|
|
|
int b = 255;
|
|
|
|
|
|
|
|
textColourElement->QueryIntAttribute("r", &r);
|
|
|
|
textColourElement->QueryIntAttribute("g", &g);
|
|
|
|
textColourElement->QueryIntAttribute("b", &b);
|
|
|
|
|
|
|
|
const char* name = textColourElement->Attribute("name");
|
|
|
|
|
|
|
|
if (name != NULL)
|
|
|
|
{
|
|
|
|
SDL_Colour colour;
|
|
|
|
colour.r = r;
|
|
|
|
colour.g = g;
|
|
|
|
colour.b = b;
|
|
|
|
|
2023-03-18 22:48:20 +01:00
|
|
|
script.textbox_colours[name] = colour;
|
2022-11-21 20:02:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-12-12 00:05:20 +01:00
|
|
|
|
|
|
|
if (SDL_strcmp(pKey, "SpecialRoomnames") == 0)
|
|
|
|
{
|
|
|
|
for (tinyxml2::XMLElement* roomnameElement = pElem->FirstChildElement(); roomnameElement; roomnameElement = roomnameElement->NextSiblingElement())
|
|
|
|
{
|
|
|
|
const char* roomnameType = roomnameElement->Value();
|
|
|
|
Roomname name;
|
|
|
|
name.x = 0;
|
|
|
|
name.y = 0;
|
|
|
|
name.flag = -1;
|
|
|
|
name.loop = false;
|
2023-02-18 00:52:57 +01:00
|
|
|
name.type = RoomnameType_STATIC;
|
2022-12-12 00:05:20 +01:00
|
|
|
name.progress = 0;
|
|
|
|
name.delay = 0;
|
|
|
|
if (SDL_strcmp(roomnameType, "transform") == 0)
|
|
|
|
{
|
2023-02-18 00:52:57 +01:00
|
|
|
name.type = RoomnameType_TRANSFORM;
|
2022-12-12 00:05:20 +01:00
|
|
|
name.delay = 2;
|
|
|
|
}
|
|
|
|
else if (SDL_strcmp(roomnameType, "glitch") == 0)
|
|
|
|
{
|
2023-02-18 00:52:57 +01:00
|
|
|
name.type = RoomnameType_GLITCH;
|
2022-12-12 00:05:20 +01:00
|
|
|
name.progress = 1;
|
|
|
|
name.delay = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
name.text.clear();
|
|
|
|
|
|
|
|
roomnameElement->QueryIntAttribute("x", &name.x);
|
|
|
|
roomnameElement->QueryIntAttribute("y", &name.y);
|
|
|
|
roomnameElement->QueryIntAttribute("flag", &name.flag);
|
|
|
|
|
|
|
|
roomnameElement->QueryBoolAttribute("loop", &name.loop);
|
|
|
|
|
2023-02-18 00:52:57 +01:00
|
|
|
// Rooms start at (100, 100) instead of (0, 0), so offset the coordinates
|
|
|
|
name.x += 100;
|
|
|
|
name.y += 100;
|
|
|
|
|
|
|
|
if (name.type == RoomnameType_STATIC)
|
2022-12-12 00:05:20 +01:00
|
|
|
{
|
|
|
|
const char* text = roomnameElement->GetText();
|
|
|
|
if (text != NULL)
|
|
|
|
{
|
|
|
|
name.text.push_back(std::string(text));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Does it have children?
|
|
|
|
if (roomnameElement->FirstChildElement() == NULL)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (tinyxml2::XMLElement* textElement = roomnameElement->FirstChildElement(); textElement; textElement = textElement->NextSiblingElement())
|
|
|
|
{
|
|
|
|
if (SDL_strcmp(textElement->Value(), "text") == 0)
|
|
|
|
{
|
|
|
|
const char* text = textElement->GetText();
|
|
|
|
if (text != NULL)
|
|
|
|
{
|
|
|
|
name.text.push_back(std::string(text));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
map.specialroomnames.push_back(name);
|
|
|
|
}
|
|
|
|
}
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mapwidth < maxwidth)
|
|
|
|
{
|
|
|
|
/* Unscramble platv, since it was stored incorrectly
|
|
|
|
* in 2.2 and previous... */
|
|
|
|
size_t i;
|
|
|
|
int x = 0;
|
|
|
|
int y = 0;
|
|
|
|
int temp_platv[numrooms];
|
|
|
|
|
|
|
|
for (i = 0; i < numrooms; ++i)
|
|
|
|
{
|
2021-02-21 00:54:24 +01:00
|
|
|
temp_platv[i] = roomproperties[i].platv;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < numrooms; ++i)
|
|
|
|
{
|
|
|
|
if (x < mapwidth)
|
|
|
|
{
|
|
|
|
const int platv_idx = x + y * mapwidth;
|
|
|
|
if (INBOUNDS_ARR(platv_idx, temp_platv))
|
|
|
|
{
|
2021-02-21 00:54:24 +01:00
|
|
|
roomproperties[i].platv = temp_platv[platv_idx];
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-02-21 00:54:24 +01:00
|
|
|
roomproperties[i].platv = 4; /* default */
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
++x;
|
|
|
|
|
|
|
|
if (x >= maxwidth)
|
|
|
|
{
|
|
|
|
x = 0;
|
|
|
|
++y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-30 22:57:24 +01:00
|
|
|
loc::loadtext_custom(_path.c_str());
|
2023-01-20 03:56:17 +01:00
|
|
|
font::load_custom(level_font_name.c_str());
|
2022-12-30 22:57:24 +01:00
|
|
|
|
2021-02-20 05:51:25 +01:00
|
|
|
version=2;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-09-07 00:41:49 +02:00
|
|
|
bool customlevelclass::save(const std::string& _path)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
tinyxml2::XMLDocument doc;
|
|
|
|
|
|
|
|
std::string newpath("levels/" + _path);
|
|
|
|
|
|
|
|
// Try to preserve the XML of the currently-loaded one
|
2021-02-21 00:40:11 +01:00
|
|
|
bool already_exists = !ed.loaded_filepath.empty()
|
|
|
|
&& FILESYSTEM_loadTiXml2Document(ed.loaded_filepath.c_str(), doc);
|
|
|
|
if (!already_exists && !ed.loaded_filepath.empty())
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2021-02-21 00:40:11 +01:00
|
|
|
vlog_error("Currently-loaded %s not found", ed.loaded_filepath.c_str());
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
ed.loaded_filepath = newpath;
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
tinyxml2::XMLElement* msg;
|
|
|
|
|
|
|
|
xml::update_declaration(doc);
|
|
|
|
|
|
|
|
tinyxml2::XMLElement * root = xml::update_element(doc, "MapData");
|
|
|
|
root->SetAttribute("version",version);
|
|
|
|
|
|
|
|
xml::update_comment(root, " Save file ");
|
|
|
|
|
|
|
|
tinyxml2::XMLElement * data = xml::update_element(root, "Data");
|
|
|
|
|
|
|
|
msg = xml::update_element(data, "MetaData");
|
|
|
|
|
|
|
|
//getUser
|
2021-02-21 01:04:50 +01:00
|
|
|
xml::update_tag(msg, "Creator", creator.c_str());
|
2021-02-20 05:51:25 +01:00
|
|
|
|
2021-02-21 01:04:50 +01:00
|
|
|
xml::update_tag(msg, "Title", title.c_str());
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
xml::update_tag(msg, "Created", version);
|
|
|
|
|
2021-02-21 01:04:50 +01:00
|
|
|
xml::update_tag(msg, "Modified", modifier.c_str());
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
xml::update_tag(msg, "Modifiers", version);
|
|
|
|
|
|
|
|
xml::update_tag(msg, "Desc1", Desc1.c_str());
|
|
|
|
|
|
|
|
xml::update_tag(msg, "Desc2", Desc2.c_str());
|
|
|
|
|
|
|
|
xml::update_tag(msg, "Desc3", Desc3.c_str());
|
|
|
|
|
|
|
|
xml::update_tag(msg, "website", website.c_str());
|
|
|
|
|
|
|
|
if (onewaycol_override)
|
|
|
|
{
|
|
|
|
xml::update_tag(msg, "onewaycol_override", onewaycol_override);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Delete the element. I could just delete one, but just to be sure,
|
|
|
|
// I will delete all of them if there are more than one
|
|
|
|
tinyxml2::XMLElement* element;
|
|
|
|
while ((element = msg->FirstChildElement("onewaycol_override"))
|
|
|
|
!= NULL)
|
|
|
|
{
|
|
|
|
doc.DeleteNode(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-20 03:56:17 +01:00
|
|
|
if (level_font_name != "" && level_font_name != "font")
|
|
|
|
{
|
|
|
|
xml::update_tag(msg, "font", level_font_name.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Get rid of it completely, same as <onewaycol_override>
|
|
|
|
tinyxml2::XMLElement* element;
|
|
|
|
while ((element = msg->FirstChildElement("font")) != NULL)
|
|
|
|
{
|
|
|
|
doc.DeleteNode(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-03 20:09:23 +01:00
|
|
|
if (rtl)
|
|
|
|
{
|
|
|
|
xml::update_tag(msg, "rtl", rtl);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Also get rid of this one...
|
|
|
|
tinyxml2::XMLElement* element;
|
|
|
|
while ((element = msg->FirstChildElement("rtl")) != NULL)
|
|
|
|
{
|
|
|
|
doc.DeleteNode(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-20 05:51:25 +01:00
|
|
|
xml::update_tag(data, "mapwidth", mapwidth);
|
|
|
|
|
|
|
|
xml::update_tag(data, "mapheight", mapheight);
|
|
|
|
|
|
|
|
xml::update_tag(data, "levmusic", levmusic);
|
|
|
|
|
|
|
|
//New save format
|
|
|
|
std::string contentsString="";
|
|
|
|
for(int y = 0; y < mapheight*30; y++ )
|
|
|
|
{
|
|
|
|
for(int x = 0; x < mapwidth*40; x++ )
|
|
|
|
{
|
|
|
|
contentsString += help.String(getabstile(x, y)) + ",";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xml::update_tag(data, "contents", contentsString.c_str());
|
|
|
|
|
|
|
|
|
|
|
|
msg = xml::update_element_delete_contents(data, "edEntities");
|
2021-02-21 01:01:39 +01:00
|
|
|
for(size_t i = 0; i < customentities.size(); i++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
tinyxml2::XMLElement *edentityElement = doc.NewElement( "edentity" );
|
2023-03-29 09:18:42 +02:00
|
|
|
const int global_x = customentities[i].rx * SCREEN_WIDTH_TILES + customentities[i].x;
|
|
|
|
const int global_y = customentities[i].ry * SCREEN_HEIGHT_TILES + customentities[i].y;
|
|
|
|
edentityElement->SetAttribute("x", global_x);
|
|
|
|
edentityElement->SetAttribute("y", global_y);
|
2021-02-21 01:01:39 +01:00
|
|
|
edentityElement->SetAttribute( "t", customentities[i].t);
|
|
|
|
edentityElement->SetAttribute( "p1", customentities[i].p1);
|
|
|
|
edentityElement->SetAttribute( "p2", customentities[i].p2);
|
|
|
|
edentityElement->SetAttribute( "p3", customentities[i].p3);
|
|
|
|
edentityElement->SetAttribute( "p4", customentities[i].p4);
|
|
|
|
edentityElement->SetAttribute( "p5", customentities[i].p5);
|
|
|
|
edentityElement->SetAttribute( "p6", customentities[i].p6);
|
|
|
|
edentityElement->LinkEndChild( doc.NewText( customentities[i].scriptname.c_str() )) ;
|
2021-02-20 05:51:25 +01:00
|
|
|
msg->LinkEndChild( edentityElement );
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = xml::update_element_delete_contents(data, "levelMetaData");
|
|
|
|
|
|
|
|
int temp_platv[numrooms];
|
2021-09-11 01:55:27 +02:00
|
|
|
for (size_t i = 0; i < SDL_arraysize(temp_platv); ++i)
|
|
|
|
{
|
|
|
|
temp_platv[i] = 4; /* default */
|
|
|
|
}
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
if (mapwidth < maxwidth)
|
|
|
|
{
|
|
|
|
/* Re-scramble platv, since it was stored incorrectly
|
|
|
|
* in 2.2 and previous... */
|
|
|
|
size_t i;
|
|
|
|
int x = 0;
|
|
|
|
int y = 0;
|
|
|
|
for (i = 0; i < numrooms; ++i)
|
|
|
|
{
|
|
|
|
if (x < mapwidth)
|
|
|
|
{
|
|
|
|
const int platv_idx = x + y * mapwidth;
|
|
|
|
if (INBOUNDS_ARR(platv_idx, temp_platv))
|
|
|
|
{
|
2021-02-21 00:54:24 +01:00
|
|
|
temp_platv[platv_idx] = roomproperties[i].platv;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
++x;
|
|
|
|
|
|
|
|
if (x >= mapwidth)
|
|
|
|
{
|
|
|
|
/* Skip to next actual row. */
|
|
|
|
i += maxwidth - mapwidth;
|
|
|
|
x = 0;
|
|
|
|
++y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:54:24 +01:00
|
|
|
for(size_t i = 0; i < SDL_arraysize(roomproperties); i++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2021-02-21 00:45:48 +01:00
|
|
|
tinyxml2::XMLElement *roompropertyElement = doc.NewElement( "edLevelClass" );
|
2021-02-21 00:54:24 +01:00
|
|
|
roompropertyElement->SetAttribute( "tileset", roomproperties[i].tileset);
|
|
|
|
roompropertyElement->SetAttribute( "tilecol", roomproperties[i].tilecol);
|
|
|
|
roompropertyElement->SetAttribute( "platx1", roomproperties[i].platx1);
|
|
|
|
roompropertyElement->SetAttribute( "platy1", roomproperties[i].platy1);
|
|
|
|
roompropertyElement->SetAttribute( "platx2", roomproperties[i].platx2);
|
|
|
|
roompropertyElement->SetAttribute( "platy2", roomproperties[i].platy2);
|
2021-02-21 00:45:48 +01:00
|
|
|
roompropertyElement->SetAttribute( "platv", temp_platv[i]);
|
2021-02-21 00:54:24 +01:00
|
|
|
roompropertyElement->SetAttribute( "enemyx1", roomproperties[i].enemyx1);
|
|
|
|
roompropertyElement->SetAttribute( "enemyy1", roomproperties[i].enemyy1);
|
|
|
|
roompropertyElement->SetAttribute( "enemyx2", roomproperties[i].enemyx2);
|
|
|
|
roompropertyElement->SetAttribute( "enemyy2", roomproperties[i].enemyy2);
|
|
|
|
roompropertyElement->SetAttribute( "enemytype", roomproperties[i].enemytype);
|
|
|
|
roompropertyElement->SetAttribute( "directmode", roomproperties[i].directmode);
|
|
|
|
roompropertyElement->SetAttribute( "warpdir", roomproperties[i].warpdir);
|
|
|
|
|
|
|
|
roompropertyElement->LinkEndChild( doc.NewText( roomproperties[i].roomname.c_str() )) ;
|
2021-02-21 00:45:48 +01:00
|
|
|
msg->LinkEndChild( roompropertyElement );
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::string scriptString;
|
|
|
|
for(size_t i = 0; i < script.customscripts.size(); i++)
|
|
|
|
{
|
|
|
|
Script& script_ = script.customscripts[i];
|
|
|
|
|
|
|
|
scriptString += script_.name + ":|";
|
|
|
|
for (size_t ii = 0; ii < script_.contents.size(); ++ii)
|
|
|
|
{
|
|
|
|
scriptString += script_.contents[ii];
|
|
|
|
|
|
|
|
// Inserts a space if the line ends with a :
|
|
|
|
if (script_.contents[ii].length() && *script_.contents[ii].rbegin() == ':')
|
|
|
|
{
|
|
|
|
scriptString += " ";
|
|
|
|
}
|
|
|
|
|
|
|
|
scriptString += "|";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xml::update_tag(data, "script", scriptString.c_str());
|
|
|
|
|
|
|
|
return FILESYSTEM_saveTiXml2Document(newpath.c_str(), doc);
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
void customlevelclass::generatecustomminimap(void)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2022-11-30 22:25:28 +01:00
|
|
|
map.customzoom = 1;
|
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 22:35:14 +01:00
|
|
|
if (mapwidth <= 10 && mapheight <= 10)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2022-11-30 22:25:28 +01:00
|
|
|
map.customzoom = 2;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
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 22:35:14 +01:00
|
|
|
if (mapwidth <= 5 && mapheight <= 5)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2022-11-30 22:25:28 +01:00
|
|
|
map.customzoom = 4;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
2022-11-30 22:25:28 +01:00
|
|
|
// Set minimap offsets
|
|
|
|
switch (map.customzoom)
|
|
|
|
{
|
|
|
|
case 4:
|
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 22:35:14 +01:00
|
|
|
map.custommmxoff = 24 * (5 - mapwidth);
|
|
|
|
map.custommmyoff = 18 * (5 - mapheight);
|
2022-11-30 22:25:28 +01:00
|
|
|
break;
|
|
|
|
case 2:
|
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 22:35:14 +01:00
|
|
|
map.custommmxoff = 12 * (10 - mapwidth);
|
|
|
|
map.custommmyoff = 9 * (10 - mapheight);
|
2022-11-30 22:25:28 +01:00
|
|
|
break;
|
|
|
|
default:
|
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 22:35:14 +01:00
|
|
|
map.custommmxoff = 6 * (20 - mapwidth);
|
|
|
|
map.custommmyoff = int(4.5 * (20 - mapheight));
|
2022-11-30 22:25:28 +01:00
|
|
|
break;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
2022-11-30 22:25:28 +01:00
|
|
|
map.custommmxsize = 240 - (map.custommmxoff * 2);
|
|
|
|
map.custommmysize = 180 - (map.custommmyoff * 2);
|
|
|
|
|
2023-01-07 19:28:07 +01:00
|
|
|
// Start drawing the minimap
|
|
|
|
|
|
|
|
SDL_Texture* target = SDL_GetRenderTarget(gameScreen.m_renderer);
|
|
|
|
graphics.set_render_target(graphics.images[IMAGE_CUSTOMMINIMAP]);
|
|
|
|
graphics.clear();
|
2021-02-20 05:51:25 +01:00
|
|
|
|
2022-11-30 22:25:28 +01:00
|
|
|
// Scan over the map size
|
|
|
|
for (int j2 = 0; j2 < mapheight; j2++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2022-11-30 22:25:28 +01:00
|
|
|
for (int i2 = 0; i2 < mapwidth; i2++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2023-05-22 19:24:36 +02:00
|
|
|
std::vector<SDL_Point> dark_points;
|
|
|
|
std::vector<SDL_Point> light_points;
|
|
|
|
|
|
|
|
bool dark = getroomprop(i2, j2)->tileset == 1;
|
2021-02-20 05:51:25 +01:00
|
|
|
|
2022-11-30 22:25:28 +01:00
|
|
|
// Ok, now scan over each square
|
|
|
|
for (int j = 0; j < 9 * map.customzoom; j++)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 12 * map.customzoom; i++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2022-11-30 22:25:28 +01:00
|
|
|
int tile;
|
|
|
|
switch (map.customzoom)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2022-11-30 22:25:28 +01:00
|
|
|
case 4:
|
|
|
|
tile = absfree(
|
|
|
|
int(i * 0.83) + (i2 * 40),
|
|
|
|
int(j * 0.83) + (j2 * 30)
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
tile = absfree(
|
|
|
|
int(i * 1.6) + (i2 * 40),
|
|
|
|
int(j * 1.6) + (j2 * 30)
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
tile = absfree(
|
|
|
|
3 + (i * 3) + (i2 * 40),
|
|
|
|
(j * 3) + (j2 * 30)
|
|
|
|
);
|
|
|
|
break;
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
2022-11-30 22:25:28 +01:00
|
|
|
if (tile >= 1)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2023-05-22 19:24:36 +02:00
|
|
|
// Add this pixel
|
|
|
|
SDL_Point point = { (i2 * 12 * map.customzoom) + i, (j2 * 9 * map.customzoom) + j };
|
|
|
|
if (dark)
|
|
|
|
{
|
|
|
|
dark_points.push_back(point);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
light_points.push_back(point);
|
|
|
|
}
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-05-22 19:24:36 +02:00
|
|
|
// Draw them all at once
|
|
|
|
if (!dark_points.empty())
|
|
|
|
{
|
|
|
|
graphics.draw_points(dark_points.data(), dark_points.size(), 96, 96, 96);
|
|
|
|
}
|
|
|
|
if (!light_points.empty())
|
|
|
|
{
|
|
|
|
graphics.draw_points(light_points.data(), light_points.size(), 196, 196, 196);
|
|
|
|
}
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
}
|
2023-01-07 19:28:07 +01:00
|
|
|
|
|
|
|
graphics.set_render_target(target);
|
2021-02-20 05:51:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return a graphics-ready color based off of the given tileset and tilecol
|
|
|
|
// Much kudos to Dav999 for saving me a lot of work, because I stole these colors from const.lua in Ved! -Info Teddy
|
2023-01-02 01:36:43 +01:00
|
|
|
SDL_Color customlevelclass::getonewaycol(const int rx, const int ry)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2021-02-21 00:45:48 +01:00
|
|
|
const RoomProperty* const room = getroomprop(rx, ry);
|
2021-02-20 05:51:25 +01:00
|
|
|
switch (room->tileset) {
|
|
|
|
|
|
|
|
case 0: // Space Station
|
|
|
|
switch (room->tilecol) {
|
|
|
|
case -1:
|
|
|
|
return graphics.getRGB(109, 109, 109);
|
|
|
|
case 0:
|
|
|
|
return graphics.getRGB(131, 141, 235);
|
|
|
|
case 1:
|
|
|
|
return graphics.getRGB(227, 140, 227);
|
|
|
|
case 2:
|
|
|
|
return graphics.getRGB(242, 126, 151);
|
|
|
|
case 3:
|
|
|
|
return graphics.getRGB(229, 235, 133);
|
|
|
|
case 4:
|
|
|
|
return graphics.getRGB(148, 238, 130);
|
|
|
|
case 5:
|
|
|
|
return graphics.getRGB(140, 165, 227);
|
|
|
|
case 6:
|
|
|
|
return graphics.getRGB(227, 140, 148);
|
|
|
|
case 7:
|
|
|
|
return graphics.getRGB(140, 173, 228);
|
|
|
|
case 8:
|
|
|
|
return graphics.getRGB(142, 235, 137);
|
|
|
|
case 9:
|
|
|
|
return graphics.getRGB(137, 235, 206);
|
|
|
|
case 10:
|
|
|
|
return graphics.getRGB(235, 139, 223);
|
|
|
|
case 11:
|
|
|
|
return graphics.getRGB(238, 130, 138);
|
|
|
|
case 12:
|
|
|
|
return graphics.getRGB(137, 235, 178);
|
|
|
|
case 13:
|
|
|
|
return graphics.getRGB(125, 205, 247);
|
|
|
|
case 14:
|
|
|
|
return graphics.getRGB(190, 137, 235);
|
|
|
|
case 15:
|
|
|
|
return graphics.getRGB(235, 137, 206);
|
|
|
|
case 16:
|
|
|
|
return graphics.getRGB(229, 247, 127);
|
|
|
|
case 17:
|
|
|
|
return graphics.getRGB(127, 200, 247);
|
|
|
|
case 18:
|
|
|
|
return graphics.getRGB(197, 137, 235);
|
|
|
|
case 19:
|
|
|
|
return graphics.getRGB(235, 131, 175);
|
|
|
|
case 20:
|
|
|
|
return graphics.getRGB(242, 210, 123);
|
|
|
|
case 21:
|
|
|
|
return graphics.getRGB(131, 235, 158);
|
|
|
|
case 22:
|
|
|
|
return graphics.getRGB(242, 126, 151);
|
|
|
|
case 23:
|
|
|
|
return graphics.getRGB(219, 243, 123);
|
|
|
|
case 24:
|
|
|
|
return graphics.getRGB(131, 234, 145);
|
|
|
|
case 25:
|
|
|
|
return graphics.getRGB(131, 199, 234);
|
|
|
|
case 26:
|
|
|
|
return graphics.getRGB(141, 131, 234);
|
|
|
|
case 27:
|
|
|
|
return graphics.getRGB(226, 140, 144);
|
|
|
|
case 28:
|
|
|
|
return graphics.getRGB(129, 236, 144);
|
|
|
|
case 29:
|
|
|
|
return graphics.getRGB(235, 231, 131);
|
|
|
|
case 30:
|
|
|
|
return graphics.getRGB(153, 235, 131);
|
|
|
|
case 31:
|
|
|
|
return graphics.getRGB(207, 131, 235);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1: // Outside
|
|
|
|
switch (room->tilecol) {
|
|
|
|
case 0:
|
|
|
|
return graphics.getRGB(57, 86, 140);
|
|
|
|
case 1:
|
|
|
|
return graphics.getRGB(156, 42, 42);
|
|
|
|
case 2:
|
|
|
|
return graphics.getRGB(42, 156, 155);
|
|
|
|
case 3:
|
|
|
|
return graphics.getRGB(125, 36, 162);
|
|
|
|
case 4:
|
|
|
|
return graphics.getRGB(191, 198, 0);
|
|
|
|
case 5:
|
|
|
|
return graphics.getRGB(0, 198, 126);
|
|
|
|
case 6:
|
|
|
|
return graphics.getRGB(224, 110, 177);
|
|
|
|
case 7:
|
|
|
|
return graphics.getRGB(255, 142, 87);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2: // Lab
|
|
|
|
switch (room->tilecol) {
|
|
|
|
case 0:
|
|
|
|
return graphics.getRGB(0, 165, 206);
|
|
|
|
case 1:
|
|
|
|
return graphics.getRGB(206, 5, 0);
|
|
|
|
case 2:
|
|
|
|
return graphics.getRGB(222, 0, 173);
|
|
|
|
case 3:
|
|
|
|
return graphics.getRGB(27, 67, 255);
|
|
|
|
case 4:
|
|
|
|
return graphics.getRGB(194, 206, 0);
|
|
|
|
case 5:
|
|
|
|
return graphics.getRGB(0, 206, 39);
|
|
|
|
case 6:
|
|
|
|
return graphics.getRGB(0, 165, 206);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3: // Warp Zone
|
|
|
|
switch (room->tilecol) {
|
|
|
|
case 0:
|
|
|
|
return graphics.getRGB(113, 178, 197);
|
|
|
|
case 1:
|
|
|
|
return graphics.getRGB(197, 113, 119);
|
|
|
|
case 2:
|
|
|
|
return graphics.getRGB(196, 113, 197);
|
|
|
|
case 3:
|
|
|
|
return graphics.getRGB(149, 113, 197);
|
|
|
|
case 4:
|
|
|
|
return graphics.getRGB(197, 182, 113);
|
|
|
|
case 5:
|
|
|
|
return graphics.getRGB(141, 197, 113);
|
|
|
|
case 6:
|
|
|
|
return graphics.getRGB(109, 109, 109);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4: // Ship
|
|
|
|
switch (room->tilecol) {
|
|
|
|
case 0:
|
|
|
|
return graphics.getRGB(0, 206, 39);
|
|
|
|
case 1:
|
|
|
|
return graphics.getRGB(0, 165, 206);
|
|
|
|
case 2:
|
|
|
|
return graphics.getRGB(194, 206, 0);
|
|
|
|
case 3:
|
|
|
|
return graphics.getRGB(206, 0, 160);
|
|
|
|
case 4:
|
|
|
|
return graphics.getRGB(27, 67, 255);
|
|
|
|
case 5:
|
|
|
|
return graphics.getRGB(206, 5, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Uh, I guess return solid white
|
|
|
|
return graphics.getRGB(255, 255, 255);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This version detects the room automatically
|
2023-01-02 01:36:43 +01:00
|
|
|
SDL_Color customlevelclass::getonewaycol(void)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
if (game.gamestate == EDITORMODE)
|
2021-02-21 00:40:11 +01:00
|
|
|
{
|
|
|
|
return getonewaycol(ed.levx, ed.levy);
|
|
|
|
}
|
2023-08-23 19:51:11 +02:00
|
|
|
else if (map.custommode)
|
2021-02-21 00:40:11 +01:00
|
|
|
{
|
2021-02-20 05:51:25 +01:00
|
|
|
return getonewaycol(game.roomx - 100, game.roomy - 100);
|
2021-02-21 00:40:11 +01:00
|
|
|
}
|
2021-02-20 05:51:25 +01:00
|
|
|
|
|
|
|
// Uh, I guess return solid white
|
|
|
|
return graphics.getRGB(255, 255, 255);
|
|
|
|
}
|
|
|
|
|
2021-10-28 01:49:57 +02:00
|
|
|
static SDL_INLINE bool inbounds(const CustomEntity* entity)
|
|
|
|
{
|
|
|
|
extern customlevelclass cl;
|
|
|
|
return entity->x >= 0
|
|
|
|
&& entity->y >= 0
|
|
|
|
&& entity->x < cl.mapwidth * SCREEN_WIDTH_TILES
|
|
|
|
&& entity->y < cl.mapheight * SCREEN_HEIGHT_TILES;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::numtrinkets(void)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
int temp = 0;
|
2021-02-21 01:01:39 +01:00
|
|
|
for (size_t i = 0; i < customentities.size(); i++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2021-10-28 01:49:57 +02:00
|
|
|
if (customentities[i].t == 9 && inbounds(&customentities[i]))
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
temp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return temp;
|
|
|
|
}
|
|
|
|
|
2021-02-21 00:40:11 +01:00
|
|
|
int customlevelclass::numcrewmates(void)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
int temp = 0;
|
2021-02-21 01:01:39 +01:00
|
|
|
for (size_t i = 0; i < customentities.size(); i++)
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
2021-10-28 01:49:57 +02:00
|
|
|
if (customentities[i].t == 15 && inbounds(&customentities[i]))
|
2021-02-20 05:51:25 +01:00
|
|
|
{
|
|
|
|
temp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return temp;
|
|
|
|
}
|