/* * 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'; }