727 lines
16 KiB
C++
727 lines
16 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "ultima/shared/std/containers.h"
|
|
#include "ultima/nuvie/core/nuvie_defs.h"
|
|
#include "ultima/nuvie/misc/u6_misc.h"
|
|
#include "ultima/nuvie/conf/configuration.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/file.h"
|
|
#include "common/fs.h"
|
|
#include "common/str.h"
|
|
|
|
namespace Ultima {
|
|
namespace Nuvie {
|
|
|
|
using namespace Std;
|
|
|
|
void Tokenise(const Std::string &str, Std::vector<Std::string> &tokens, char delimiter = ' ') {
|
|
Std::string delimiters(delimiter);
|
|
|
|
// Skip delimiters at beginning.
|
|
string::size_type lastPos = str.findFirstNotOf(delimiters, 0);
|
|
|
|
for (string::size_type pos = str.findFirstOf(delimiters, lastPos) ;
|
|
string::npos != pos || string::npos != lastPos ;
|
|
pos = str.findFirstOf(delimiters, lastPos)) {
|
|
// Found a token, add it to the vector.
|
|
tokens.push_back(str.substr(lastPos, pos - lastPos));
|
|
// Skip delimiters. Note the "not_of"
|
|
lastPos = str.findFirstNotOf(delimiters, pos);
|
|
}
|
|
}
|
|
|
|
Std::string config_get_game_key(const Configuration *config) {
|
|
Std::string game_key, game_name;
|
|
|
|
config->value("config/GameName", game_name);
|
|
|
|
game_key.assign("config/");
|
|
game_key.append(game_name);
|
|
|
|
return game_key;
|
|
}
|
|
|
|
const char *get_game_tag(int game_type) {
|
|
switch (game_type) {
|
|
case NUVIE_GAME_U6 :
|
|
return "U6";
|
|
case NUVIE_GAME_MD :
|
|
return "MD";
|
|
case NUVIE_GAME_SE :
|
|
return "SE";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
void config_get_path(const Configuration *config, const Std::string &filename, Common::Path &path) {
|
|
Std::string key, game_name;
|
|
Common::Path game_dir, tmp_path;
|
|
|
|
config->value("config/GameName", game_name);
|
|
|
|
key.assign("config/");
|
|
key.append(game_name);
|
|
key.append("/gamedir");
|
|
|
|
config->pathFromValue(key, "", game_dir);
|
|
|
|
tmp_path = game_dir.appendComponent(filename);
|
|
|
|
path = tmp_path;
|
|
}
|
|
|
|
int mkdir_recursive(const Common::Path &path, int mode) {
|
|
#ifdef TODO
|
|
vector<string> directories;
|
|
string tmp_path;
|
|
|
|
Tokenise(path, directories, U6PATH_DELIMITER);
|
|
|
|
if (path.find(U6PATH_DELIMITER) == 0)
|
|
tmp_path += U6PATH_DELIMITER;
|
|
|
|
for (const auto &dir : directories) {
|
|
debug("%s, ", dir.c_str());
|
|
|
|
tmp_path += dir;
|
|
tmp_path += U6PATH_DELIMITER;
|
|
if (!directory_exists(tmp_path.c_str())) {
|
|
#if defined(WIN32)
|
|
int ret = mkdir(tmp_path.c_str());
|
|
#else
|
|
int ret = mkdir(tmp_path.c_str(), mode);
|
|
#endif
|
|
if (ret != 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
#else
|
|
error("TODO");
|
|
#endif
|
|
}
|
|
|
|
//return the uint8 game_type from a char string
|
|
uint8 get_game_type(const char *string) {
|
|
if (string != nullptr && strlen(string) >= 2) {
|
|
if (strcmp("md", string) == 0 || strcmp("martian", string) == 0)
|
|
return NUVIE_GAME_MD;
|
|
if (strcmp("se", string) == 0 || strcmp("savage", string) == 0)
|
|
return NUVIE_GAME_SE;
|
|
if (strcmp("u6", string) == 0 || strcmp("ultima6", string) == 0)
|
|
return NUVIE_GAME_U6;
|
|
}
|
|
|
|
return NUVIE_GAME_NONE;
|
|
}
|
|
|
|
nuvie_game_t get_game_type(const Configuration *config) {
|
|
int game_type;
|
|
config->value("config/GameType", game_type);
|
|
|
|
return (nuvie_game_t)game_type;
|
|
}
|
|
|
|
void build_path(const Common::Path &path, const Std::string &filename, Common::Path &full_path) {
|
|
full_path = path.appendComponent(filename);
|
|
}
|
|
|
|
bool has_fmtowns_support(const Configuration *config) {
|
|
Std::string townsdir;
|
|
config->value("config/townsdir", townsdir, "");
|
|
if (townsdir != "" && directory_exists(townsdir.c_str()))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool directory_exists(const Common::Path &directory) {
|
|
return Common::FSNode(directory).exists() ||
|
|
Common::FSNode(ConfMan.getPath("path").joinInPlace(directory)).exists();
|
|
}
|
|
|
|
bool file_exists(const Common::Path &path) {
|
|
return Common::File::exists(path);
|
|
}
|
|
|
|
void print_b(DebugLevelType level, uint8 num) {
|
|
sint8 i;
|
|
|
|
for (i = 7; i >= 0; i--) {
|
|
if (num & (1 << i))
|
|
DEBUG(1, level, "1");
|
|
else
|
|
DEBUG(1, level, "0");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void print_b16(DebugLevelType level, uint16 num) {
|
|
sint8 i;
|
|
|
|
for (i = 15; i >= 0; i--) {
|
|
if (num & (1 << i))
|
|
DEBUG(1, level, "1");
|
|
else
|
|
DEBUG(1, level, "0");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void print_indent(DebugLevelType level, uint8 indent) {
|
|
uint16 i;
|
|
|
|
for (i = 0; i < indent; i++)
|
|
DEBUG(1, level, " ");
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void print_bool(DebugLevelType level, bool state, const char *yes, const char *no) {
|
|
DEBUG(1, level, "%s", state ? yes : no);
|
|
}
|
|
|
|
|
|
void print_flags(DebugLevelType level, uint8 num, const char *f[8]) {
|
|
Std::string complete_flags = "";
|
|
print_b(level, num);
|
|
if (num != 0)
|
|
complete_flags += "(";
|
|
for (uint32 i = 0; i < 8; i++)
|
|
if ((num & (1 << i)) && f[i])
|
|
complete_flags += f[i];
|
|
if (num != 0)
|
|
complete_flags += ")";
|
|
DEBUG(1, level, "%s", complete_flags.c_str());
|
|
}
|
|
|
|
|
|
/* Where rect1 and rect2 merge, subtract and copy that rect to sub_rect.
|
|
* Returns false if the rectangles don't intersect.
|
|
*/
|
|
bool subtract_rect(const Common::Rect *rect1, const Common::Rect *rect2, Common::Rect *sub_rect) {
|
|
uint16 rect1_x2 = rect1->right, rect1_y2 = rect1->bottom;
|
|
uint16 rect2_x2 = rect2->right, rect2_y2 = rect2->bottom;
|
|
uint16 x1, x2, y1, y2;
|
|
|
|
if (line_in_rect(rect1->left, rect1->top, rect1_x2, rect1->top, rect2)
|
|
|| line_in_rect(rect1_x2, rect1->top, rect1_x2, rect1_y2, rect2)
|
|
|| line_in_rect(rect1->left, rect1->top, rect1->left, rect1_y2, rect2)
|
|
|| line_in_rect(rect1->left, rect1_y2, rect1_x2, rect1_y2, rect2)) {
|
|
x1 = rect2->left >= rect1->left ? rect2->left : rect1->left;
|
|
y1 = rect2->top >= rect1->top ? rect2->top : rect1->top;
|
|
x2 = rect2_x2 <= rect1_x2 ? rect2_x2 : rect1_x2;
|
|
y2 = rect2_y2 <= rect1_y2 ? rect2_y2 : rect1_y2;
|
|
} else
|
|
return false;
|
|
if (sub_rect) { // you can perform test without returning a subtraction
|
|
sub_rect->left = x1;
|
|
sub_rect->top = y1;
|
|
sub_rect->setWidth(x2 - x1);
|
|
sub_rect->setHeight(y2 - y1);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const char *get_direction_name(NuvieDir dir) {
|
|
switch (dir) {
|
|
case NUVIE_DIR_N:
|
|
return "north";
|
|
case NUVIE_DIR_NE:
|
|
return "Northeast";
|
|
case NUVIE_DIR_E:
|
|
return "East";
|
|
case NUVIE_DIR_SE:
|
|
return "Southeast";
|
|
case NUVIE_DIR_S:
|
|
return "South";
|
|
case NUVIE_DIR_SW:
|
|
return "Southwest";
|
|
case NUVIE_DIR_W:
|
|
return "West";
|
|
case NUVIE_DIR_NW:
|
|
return "Northwest";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "nowhere";
|
|
}
|
|
|
|
/* Returns name of relative direction. 0,0 prints "nowhere".
|
|
*/
|
|
const char *get_direction_name(sint16 rel_x, sint16 rel_y) {
|
|
return get_direction_name(get_direction_code(rel_x, rel_y));
|
|
}
|
|
|
|
/* Gets the nuvie direction code from the original u6 direction code. */
|
|
NuvieDir get_nuvie_dir_code(uint8 original_dir_code) {
|
|
NuvieDir dir = NUVIE_DIR_NONE;
|
|
//convert original direction into nuvie direction.
|
|
//original
|
|
// 701
|
|
// 6 2
|
|
// 543
|
|
//
|
|
// nuvie
|
|
// 704
|
|
// 3 1
|
|
// 625
|
|
switch (original_dir_code) {
|
|
case 0:
|
|
dir = NUVIE_DIR_N;
|
|
break;
|
|
case 1:
|
|
dir = NUVIE_DIR_NE;
|
|
break;
|
|
case 2:
|
|
dir = NUVIE_DIR_E;
|
|
break;
|
|
case 3:
|
|
dir = NUVIE_DIR_SE;
|
|
break;
|
|
case 4:
|
|
dir = NUVIE_DIR_S;
|
|
break;
|
|
case 5:
|
|
dir = NUVIE_DIR_SW;
|
|
break;
|
|
case 6:
|
|
dir = NUVIE_DIR_W;
|
|
break;
|
|
case 7:
|
|
dir = NUVIE_DIR_NW;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return dir;
|
|
}
|
|
|
|
sint8 get_original_dir_code(NuvieDir nuvie_dir_code) {
|
|
sint8 dir = -1;
|
|
//convert nuvie direction into original direction.
|
|
switch (nuvie_dir_code) {
|
|
case NUVIE_DIR_N:
|
|
dir = 0;
|
|
break;
|
|
case NUVIE_DIR_NE:
|
|
dir = 1;
|
|
break;
|
|
case NUVIE_DIR_E:
|
|
dir = 2;
|
|
break;
|
|
case NUVIE_DIR_SE:
|
|
dir = 3;
|
|
break;
|
|
case NUVIE_DIR_S:
|
|
dir = 4;
|
|
break;
|
|
case NUVIE_DIR_SW:
|
|
dir = 5;
|
|
break;
|
|
case NUVIE_DIR_W:
|
|
dir = 6;
|
|
break;
|
|
case NUVIE_DIR_NW:
|
|
dir = 7;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return dir;
|
|
}
|
|
/* Returns direction code of relative direction.
|
|
*/
|
|
NuvieDir get_direction_code(sint16 rel_x, sint16 rel_y) {
|
|
if (rel_x == 0 && rel_y < 0)
|
|
return NUVIE_DIR_N;
|
|
else if (rel_x > 0 && rel_y < 0)
|
|
return NUVIE_DIR_NE;
|
|
else if (rel_x > 0 && rel_y == 0)
|
|
return NUVIE_DIR_E;
|
|
else if (rel_x > 0 && rel_y > 0)
|
|
return NUVIE_DIR_SE;
|
|
else if (rel_x == 0 && rel_y > 0)
|
|
return NUVIE_DIR_S;
|
|
else if (rel_x < 0 && rel_y > 0)
|
|
return NUVIE_DIR_SW;
|
|
else if (rel_x < 0 && rel_y == 0)
|
|
return NUVIE_DIR_W;
|
|
else if (rel_x < 0 && rel_y < 0)
|
|
return NUVIE_DIR_NW;
|
|
|
|
return NUVIE_DIR_NONE;
|
|
}
|
|
|
|
NuvieDir get_reverse_direction(NuvieDir dir) {
|
|
switch (dir) {
|
|
case NUVIE_DIR_N :
|
|
return NUVIE_DIR_S;
|
|
case NUVIE_DIR_E :
|
|
return NUVIE_DIR_W;
|
|
case NUVIE_DIR_S :
|
|
return NUVIE_DIR_N;
|
|
case NUVIE_DIR_W :
|
|
return NUVIE_DIR_E;
|
|
|
|
case NUVIE_DIR_NE :
|
|
return NUVIE_DIR_SW;
|
|
case NUVIE_DIR_SE :
|
|
return NUVIE_DIR_NW;
|
|
case NUVIE_DIR_SW :
|
|
return NUVIE_DIR_NE;
|
|
case NUVIE_DIR_NW :
|
|
return NUVIE_DIR_SE;
|
|
|
|
case NUVIE_DIR_NONE :
|
|
default :
|
|
break;
|
|
}
|
|
|
|
return NUVIE_DIR_NONE;
|
|
}
|
|
|
|
void get_relative_dir(NuvieDir dir, sint16 *rel_x, sint16 *rel_y) {
|
|
switch (dir) {
|
|
case NUVIE_DIR_N :
|
|
*rel_x = 0;
|
|
*rel_y = -1;
|
|
break;
|
|
case NUVIE_DIR_E :
|
|
*rel_x = 1;
|
|
*rel_y = 0;
|
|
break;
|
|
case NUVIE_DIR_S :
|
|
*rel_x = 0;
|
|
*rel_y = 1;
|
|
break;
|
|
case NUVIE_DIR_W :
|
|
*rel_x = -1;
|
|
*rel_y = 0;
|
|
break;
|
|
|
|
case NUVIE_DIR_NE :
|
|
*rel_x = 1;
|
|
*rel_y = -1;
|
|
break;
|
|
case NUVIE_DIR_SE :
|
|
*rel_x = 1;
|
|
*rel_y = 1;
|
|
break;
|
|
case NUVIE_DIR_SW :
|
|
*rel_x = -1;
|
|
*rel_y = 1;
|
|
break;
|
|
case NUVIE_DIR_NW :
|
|
*rel_x = -1;
|
|
*rel_y = -1;
|
|
break;
|
|
|
|
case NUVIE_DIR_NONE :
|
|
default :
|
|
*rel_x = 0;
|
|
*rel_y = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int str_bsearch(const char *const str[], int max, const char *value) {
|
|
int position;
|
|
int begin = 0;
|
|
int end = max - 1;
|
|
int cond = 0;
|
|
|
|
while (begin <= end) {
|
|
position = (begin + end) / 2;
|
|
if ((cond = strcmp(str[position], value)) == 0)
|
|
return position;
|
|
else if (cond < 0)
|
|
begin = position + 1;
|
|
else
|
|
end = position - 1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
#define LINE_FRACTION 65536L
|
|
|
|
void draw_line_8bit(int sx, int sy, int ex, int ey, uint8 col, uint8 *pixels, uint16 w, uint16 h) {
|
|
uint16 pitch = w;
|
|
int xinc = 1;
|
|
int yinc = 1;
|
|
|
|
if (sx == ex) {
|
|
sx --;
|
|
if (sy > ey) {
|
|
yinc = -1;
|
|
sy--;
|
|
}
|
|
} else {
|
|
if (sx > ex) {
|
|
sx--;
|
|
xinc = -1;
|
|
} else {
|
|
ex--;
|
|
}
|
|
|
|
if (sy > ey) {
|
|
yinc = -1;
|
|
sy--;
|
|
ey--;
|
|
}
|
|
}
|
|
|
|
uint8 *pixptr = (uint8 *)(pixels + pitch * sy + sx);
|
|
uint8 *pixend = (uint8 *)(pixels + pitch * ey + ex);
|
|
pitch = pitch * yinc;
|
|
|
|
int cury = sy;
|
|
int curx = sx;
|
|
int width = w;
|
|
int height = h;
|
|
bool no_clip = true;
|
|
|
|
if (sx >= width && ex >= width) return;
|
|
if (sy >= height && ey >= height) return;
|
|
if (sx < 0 && ex < 0) return;
|
|
if (sy < 0 && ey < 0) return;
|
|
|
|
if (sy < 0 || sy >= height || sx < 0 || sx >= width) no_clip = false;
|
|
if (ey < 0 || ey >= height || ex < 0 || ex >= width) no_clip = false;
|
|
|
|
// vertical
|
|
if (sx == ex) {
|
|
//Std::cout << "Vertical" << Std::endl;
|
|
// start is below end
|
|
while (pixptr != pixend) {
|
|
if (no_clip || (cury >= 0 && cury < height)) *pixptr = col;
|
|
pixptr += pitch;
|
|
cury += yinc;
|
|
}
|
|
}
|
|
// Horizontal
|
|
else if (sy == ey) {
|
|
//Std::cout << "Horizontal" << Std::endl;
|
|
while (pixptr != pixend) {
|
|
if (no_clip || (curx >= 0 && curx < width)) *pixptr = col;
|
|
pixptr += xinc;
|
|
curx += xinc;
|
|
}
|
|
}
|
|
// Diagonal xdiff >= ydiff
|
|
else if (ABS(sx - ex) >= ABS(sy - ey)) {
|
|
//Std::cout << "Diagonal 1" << Std::endl;
|
|
uint32 fraction = ABS((LINE_FRACTION * (sy - ey)) / (sx - ex));
|
|
uint32 ycounter = 0;
|
|
|
|
for (; ;) {
|
|
if ((no_clip || (cury >= 0 && cury < height && curx >= 0 && curx < width)))
|
|
*pixptr = col;
|
|
pixptr += xinc;
|
|
if (curx == ex) break;
|
|
curx += xinc;
|
|
ycounter += fraction;
|
|
|
|
// Need to work out if we need to change line
|
|
if (ycounter > LINE_FRACTION) {
|
|
ycounter -= LINE_FRACTION;
|
|
pixptr += pitch;
|
|
cury += yinc;
|
|
}
|
|
}
|
|
}
|
|
// Diagonal ydiff > xdiff
|
|
else {
|
|
//Std::cout << "Diagonal 2" << Std::endl;
|
|
uint32 fraction = ABS((LINE_FRACTION * (sx - ex)) / (sy - ey));
|
|
uint32 xcounter = 0;
|
|
|
|
for (; ;) {
|
|
if ((no_clip || (cury >= 0 && cury < height && curx >= 0 && curx < width)))
|
|
*pixptr = col;
|
|
pixptr += pitch;
|
|
if (cury == ey) break;
|
|
cury += yinc;
|
|
xcounter += fraction;
|
|
|
|
// Need to work out if we need to change line
|
|
if (xcounter > LINE_FRACTION) {
|
|
xcounter -= LINE_FRACTION;
|
|
pixptr += xinc;
|
|
curx += xinc;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
bool string_i_compare(const Std::string &s1, const Std::string &s2) {
|
|
return scumm_stricmp(s1.c_str(), s2.c_str()) == 0;
|
|
}
|
|
|
|
void *nuvie_realloc(void *ptr, size_t size) {
|
|
void *new_ptr = realloc(ptr, size);
|
|
if (!new_ptr)
|
|
free(ptr);
|
|
return new_ptr;
|
|
}
|
|
|
|
|
|
uint32 sdl_getpixel(const Graphics::ManagedSurface *surface, int x, int y) {
|
|
int bpp = surface->format.bytesPerPixel;
|
|
/* Here p is the address to the pixel we want to retrieve */
|
|
const byte *p = (const byte *)surface->getBasePtr(x, y);
|
|
|
|
switch (bpp) {
|
|
case 1:
|
|
return *p;
|
|
break;
|
|
|
|
case 2:
|
|
return *(const uint16 *)p;
|
|
break;
|
|
|
|
case 3:
|
|
error("TODO: RGB24 unsupported");
|
|
break;
|
|
|
|
case 4:
|
|
return *(const uint32 *)p;
|
|
break;
|
|
|
|
default:
|
|
return 0; /* shouldn't happen, but avoids warnings */
|
|
}
|
|
}
|
|
|
|
|
|
static void scaleLine8Bit(unsigned char *Target, const unsigned char *Source, int SrcWidth, int TgtWidth) {
|
|
int NumPixels = TgtWidth;
|
|
int IntPart = SrcWidth / TgtWidth;
|
|
int FractPart = SrcWidth % TgtWidth;
|
|
int E = 0;
|
|
|
|
while (NumPixels-- > 0) {
|
|
*Target++ = *Source;
|
|
Source += IntPart;
|
|
E += FractPart;
|
|
if (E >= TgtWidth) {
|
|
E -= TgtWidth;
|
|
Source++;
|
|
} /* if */
|
|
} /* while */
|
|
}
|
|
|
|
// Coarse 2D scaling from https://web.archive.org/web/20111011173251/http://www.compuphase.com/graphic/scale.htm
|
|
void scale_rect_8bit(const unsigned char *Source, unsigned char *Target, int SrcWidth, int SrcHeight,
|
|
int TgtWidth, int TgtHeight) {
|
|
int NumPixels = TgtHeight;
|
|
int IntPart = (SrcHeight / TgtHeight) * SrcWidth;
|
|
int FractPart = SrcHeight % TgtHeight;
|
|
int E = 0;
|
|
const unsigned char *PrevSource = nullptr;
|
|
|
|
while (NumPixels-- > 0) {
|
|
if (Source == PrevSource) {
|
|
memcpy(Target, Target - TgtWidth, TgtWidth * sizeof(*Target));
|
|
} else {
|
|
scaleLine8Bit(Target, Source, SrcWidth, TgtWidth);
|
|
PrevSource = Source;
|
|
} /* if */
|
|
Target += TgtWidth;
|
|
Source += IntPart;
|
|
E += FractPart;
|
|
if (E >= TgtHeight) {
|
|
E -= TgtHeight;
|
|
Source += SrcWidth;
|
|
} /* if */
|
|
} /* while */
|
|
}
|
|
|
|
bool has_file_extension(const char *filename, const char *extension) {
|
|
if (strlen(filename) > strlen(extension) &&
|
|
scumm_stricmp((const char *)&filename[strlen(filename) - 4], extension) == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
uint16 wrap_signed_coord(sint16 coord, uint8 level) {
|
|
uint16 width = MAP_SIDE_LENGTH(level);
|
|
if (coord < 0) {
|
|
return (uint16)(width + coord);
|
|
}
|
|
|
|
return (uint16)coord % width;
|
|
}
|
|
|
|
sint8 get_wrapped_rel_dir(sint16 p1, sint16 p2, uint8 level) {
|
|
uint16 stride = MAP_SIDE_LENGTH(level);
|
|
|
|
sint16 ret = clamp(p1 - p2, -1, 1);
|
|
|
|
if (abs(p1 - p2) > stride / 2) {
|
|
return -ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
Std::string encode_xml_entity(const Std::string &s) {
|
|
string ret;
|
|
|
|
for (const auto &c : s) {
|
|
switch (c) {
|
|
case '<':
|
|
ret += "<";
|
|
break;
|
|
case '>':
|
|
ret += ">";
|
|
break;
|
|
case '"':
|
|
ret += """;
|
|
break;
|
|
case '\'':
|
|
ret += "'";
|
|
break;
|
|
case '&':
|
|
ret += "&";
|
|
break;
|
|
default:
|
|
ret += c;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
} // End of namespace Nuvie
|
|
} // End of namespace Ultima
|