/*
* This file is part of Deliantra, the Roguelike Realtime MMORPG.
*
* Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
* Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
* Copyright (©) 1992,2007 Frank Tore Johansen
*
* Deliantra is free software: you can redistribute it and/or modify it under
* the terms of the Affero 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 Affero GNU General Public License
* and the GNU General Public License along with this program. If not, see
* .
*
* The authors can be reached via e-mail to
*/
/*
* compatibility functions for older (GPL) source code parts
*/
#include
#include
#include
#include
#include
#include
#include "define.h"
#include "path.h"
/* buf_overflow() - we don't want to exceed the buffer size of
* buf1 by adding on buf2! Returns true if overflow will occur.
*/
int
buf_overflow (const char *buf1, const char *buf2, int bufsize)
{
int len1 = 0, len2 = 0;
if (buf1)
len1 = strlen (buf1);
if (buf2)
len2 = strlen (buf2);
if ((len1 + len2) >= bufsize)
return 1;
return 0;
}
/////////////////////////////////////////////////////////////////////////////
static const char *const fatalmsgs[80] = {
"Failed to allocate memory",
"Failed repeatedly to load maps",
"Hashtable for archetypes is too small",
"Too many errors"
};
/*
* fatal() is meant to be called whenever a fatal signal is intercepted.
* It will call the emergency_save and the clean_tmp_files functions.
*/
void
fatal (int err)
{
LOG (llevError, "Fatal: %s\n", fatalmsgs [err]);
cleanup (fatalmsgs[err], 1);
}
/////////////////////////////////////////////////////////////////////////////
/*
* The random functions here take luck into account when rolling random
* dice or numbers. This function has less of an impact the larger the
* difference becomes in the random numbers. IE, the effect is lessened
* on a 1-1000 roll, vs a 1-6 roll. This can be used by crafty programmers,
* to specifically disable luck in certain rolls, simply by making the
* numbers larger (ie, 1d1000 > 500 vs 1d6 > 3)
*/
/*
* Roll a random number between min and max. Uses op to determine luck,
* and if goodbad is non-zero, luck increases the roll, if zero, it decreases.
* Generally, op should be the player/caster/hitter requesting the roll,
* not the recipient (ie, the poor slob getting hit). [garbled 20010916]
*/
int
random_roll (int r_min, int r_max, const object *op, int goodbad)
{
r_max = max (r_min, r_max);
int base = r_max - r_min > 1 ? 20 : 50; /* d2 and d3 are corner cases */
if (op->type == PLAYER)
{
int luck = op->stats.luck;
if (rndm (base) < min (10, abs (luck)))
{
//TODO: take luck into account
}
}
return rndm (r_min, r_max);
}
/*
* This is a 64 bit version of random_roll above. This is needed
* for exp loss calculations for players changing religions.
*/
sint64
random_roll64 (sint64 r_min, sint64 r_max, const object *op, int goodbad)
{
sint64 omin = r_min;
sint64 range = max (0, r_max - r_min + 1);
int base = range > 2 ? 20 : 50; /* d2 and d3 are corner cases */
/*
* Make a call to get two 32 bit unsigned random numbers, and just to
* a little bitshifting.
*/
sint64 ran = (sint64) rndm.next () ^ ((sint64) rndm.next () << 31);
if (op->type != PLAYER)
return ((ran % range) + r_min);
int luck = op->stats.luck;
if (rndm (base) < min (10, abs (luck)))
{
/* we have a winner */
((luck > 0) ? (luck = 1) : (luck = -1));
range -= luck;
if (range < 1)
return (omin); /*check again */
((goodbad) ? (r_min += luck) : (range));
return (max (omin, min (r_max, (ran % range) + r_min)));
}
return ran % range + r_min;
}
/*
* Roll a number of dice (2d3, 4d6). Uses op to determine luck,
* If goodbad is non-zero, luck increases the roll, if zero, it decreases.
* Generally, op should be the player/caster/hitter requesting the roll,
* not the recipient (ie, the poor slob getting hit).
* The args are num D size (ie 4d6) [garbled 20010916]
*/
int
die_roll (int num, int size, const object *op, int goodbad)
{
int min, luck, total, i, gotlucky;
int diff = size;
min = 1;
luck = total = gotlucky = 0;
int base = diff > 2 ? 20 : 50; /* d2 and d3 are corner cases */
if (size < 2 || diff < 1)
{
LOG (llevError, "Calling die_roll with num=%d size=%d\n", num, size);
return num; /* avoids a float exception */
}
if (op->type == PLAYER)
luck = op->stats.luck;
for (i = 0; i < num; i++)
{
if (rndm (base) < MIN (10, abs (luck)) && !gotlucky)
{
/* we have a winner */
gotlucky++;
((luck > 0) ? (luck = 1) : (luck = -1));
diff -= luck;
if (diff < 1)
return (num); /*check again */
((goodbad) ? (min += luck) : (diff));
total += MAX (1, MIN (size, rndm (diff) + min));
}
else
total += rndm (size) + 1;
}
return total;
}
/////////////////////////////////////////////////////////////////////////////
#ifndef PATH_MAX
# define PATH_MAX 8192
#endif
char *
path_combine (const char *src, const char *dst)
{
char *p;
static char path[PATH_MAX];
if (*dst == '/')
{
/* absolute destination path => ignore source path */
strcpy (path, dst);
}
else
{
/* relative destination path => add after last '/' of source */
strcpy (path, src);
p = strrchr (path, '/');
if (p)
p++;
else
{
p = path;
if (*src == '/')
*p++ = '/';
}
strcpy (p, dst);
}
return path;
}
void
path_normalize (char *path)
{
char *p; /* points to the beginning of the path not yet processed; this is
either a path component or a path separator character */
char *q; /* points to the end of the path component p points to */
char *w; /* points to the end of the already normalized path; w <= p is
maintained */
size_t len; /* length of current component (which p points to) */
p = path;
w = p;
while (*p != '\0')
{
if (*p == '/')
{
if ((w == path && *path == '/') || (w > path && w[-1] != '/'))
*w++ = '/';
p++;
continue;
}
q = strchr (p, '/');
if (q == NULL)
q = p + strlen (p);
len = q - p;
assert (len > 0);
if (len == 1 && *p == '.')
{
/* remove current component */
}
else if (len == 2 && memcmp (p, "..", 2) == 0)
{
if (w == path || (w == path + 3 && memcmp (path, "../", 3) == 0))
{
/* keep ".." at beginning of relative path ("../x" => "../x") */
memmove (w, p, len);
w += len;
}
else if (w == path + 1 && *path == '/')
{
/* remove ".." at beginning of absolute path ("/../x" => "/x") */
}
else
{
/* remove both current component ".." and preceding one */
if (w > path && w[-1] == '/')
w--;
while (w > path && w[-1] != '/')
w--;
}
}
else
{
/* normal component ==> add it */
memmove (w, p, len);
w += len;
}
p = q;
}
/* remove trailing slashes, but keep the one at the start of the path */
while (w > path + 1 && w[-1] == '/')
w--;
*w = '\0';
}
char *
path_combine_and_normalize (const char *src, const char *dst)
{
char *path;
path = path_combine (src, dst);
path_normalize (path);
return (path);
}
char *
strcasestr_local (const char *s, const char *find)
{
char c, sc;
size_t len;
if ((c = *find++) != 0)
{
c = tolower (c);
len = strlen (find);
do
{
do
{
if ((sc = *s++) == 0)
return NULL;
}
while (tolower (sc) != c);
}
while (strncasecmp (s, find, len) != 0);
s--;
}
return (char *) s;
}
/**
* open_and_uncompress() first searches for the original filename. If it exist,
* then it opens it and returns the file-pointer.
*/
FILE *
open_and_uncompress (const char *name, int flag, int *compressed)
{
*compressed = 0;
return fopen (name, "r");
}
/*
* See open_and_uncompress().
*/
void
close_and_delete (FILE * fp, int compressed)
{
fclose (fp);
}
/*
* Strip out the media tags from a String.
* Warning the input string will contain the result string
*/
void
strip_media_tag (char *message)
{
int in_tag = 0;
char *dest;
char *src;
src = dest = message;
while (*src != '\0')
{
if (*src == '[')
{
in_tag = 1;
}
else if (in_tag && (*src == ']'))
in_tag = 0;
else if (!in_tag)
{
*dest = *src;
dest++;
}
src++;
}
*dest = '\0';
}
const char *
strrstr (const char *haystack, const char *needle)
{
const char *lastneedle;
lastneedle = NULL;
while ((haystack = strstr (haystack, needle)) != NULL)
{
lastneedle = haystack;
haystack++;
}
return lastneedle;
}
#define EOL_SIZE (sizeof("\n")-1)
void
strip_endline (char *buf)
{
if (strlen (buf) < sizeof ("\n"))
{
return;
}
if (!strcmp (buf + strlen (buf) - EOL_SIZE, "\n"))
buf[strlen (buf) - EOL_SIZE] = '\0';
}
/**
* Replace in string src all occurrences of key by replacement. The resulting
* string is put into result; at most resultsize characters (including the
* terminating null character) will be written to result.
*/
void
replace (const char *src, const char *key, const char *replacement, char *result, size_t resultsize)
{
size_t resultlen;
size_t keylen;
/* special case to prevent infinite loop if key==replacement=="" */
if (strcmp (key, replacement) == 0)
{
snprintf (result, resultsize, "%s", src);
return;
}
keylen = strlen (key);
resultlen = 0;
while (*src != '\0' && resultlen + 1 < resultsize)
{
if (strncmp (src, key, keylen) == 0)
{
snprintf (result + resultlen, resultsize - resultlen, "%s", replacement);
resultlen += strlen (result + resultlen);
src += keylen;
}
else
{
result[resultlen++] = *src++;
}
}
result[resultlen] = '\0';
}