Crossfire Server, Branch 1.12  R12190
utils.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_utils_c =
00003  *   "$Id: utils.c 11869 2009-06-14 14:32:45Z akirschbaum $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The authors can be reached via e-mail at crossfire-devel@real-time.com
00027 */
00028 
00041 #include <stdlib.h>
00042 #include <global.h>
00043 
00051 int random_roll(int min, int max, const object *op, int goodbad) {
00052     int omin, diff, luck, base, ran;
00053 
00054     omin = min;
00055     diff = max-min+1;
00056     ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
00057 
00058     if (max < 1 || diff < 1) {
00059         LOG(llevError, "Calling random_roll with min=%d max=%d\n", min, max);
00060         return(min); /* avoids a float exception */
00061     }
00062 
00063     ran = RANDOM();
00064 
00065     if (op->type != PLAYER)
00066         return((ran%diff)+min);
00067 
00068     luck = op->stats.luck;
00069     if (RANDOM()%base < MIN(10, abs(luck))) {
00070         /* we have a winner */
00071         ((luck > 0) ? (luck = 1) : (luck = -1));
00072         diff -= luck;
00073         if (diff < 1)
00074             return(omin); /*check again*/
00075         ((goodbad) ? (min += luck) : (diff));
00076 
00077         return(MAX(omin, MIN(max, (ran%diff)+min)));
00078     }
00079     return((ran%diff)+min);
00080 }
00081 
00086 sint64 random_roll64(sint64 min, sint64 max, const object *op, int goodbad) {
00087     sint64 omin, diff, luck, ran;
00088     int base;
00089 
00090     omin = min;
00091     diff = max-min+1;
00092     ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
00093 
00094     if (max < 1 || diff < 1) {
00095 #ifndef WIN32
00096         LOG(llevError, "Calling random_roll with min=%lld max=%lld\n", min, max);
00097 #else
00098         LOG(llevError, "Calling random_roll with min=%I64d max=%I64d\n", min, max);
00099 #endif
00100         return(min); /* avoids a float exception */
00101     }
00102 
00103     /* Don't know of a portable call to get 64 bit random values.
00104      * So make a call to get two 32 bit random numbers, and just to
00105      * a little byteshifting.  Do make sure the first one is only
00106      * 32 bit, so we don't get skewed results
00107      */
00108     ran = (RANDOM()&0xffffffff)|((sint64)RANDOM()<<32);
00109 
00110     if (op->type != PLAYER)
00111         return((ran%diff)+min);
00112 
00113     luck = op->stats.luck;
00114     if (RANDOM()%base < MIN(10, abs(luck))) {
00115         /* we have a winner */
00116         ((luck > 0) ? (luck = 1) : (luck = -1));
00117         diff -= luck;
00118         if (diff < 1)
00119             return (omin); /*check again*/
00120         ((goodbad) ? (min += luck) : (diff));
00121 
00122         return (MAX(omin, MIN(max, (ran%diff)+min)));
00123     }
00124     return ((ran%diff)+min);
00125 }
00126 
00134 int die_roll(int num, int size, const object *op, int goodbad) {
00135     int min, diff, luck, total, i, gotlucky, base, ran;
00136 
00137     diff = size;
00138     min = 1;
00139     luck = total = gotlucky = 0;
00140     ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
00141     if (size < 2 || diff < 1) {
00142         LOG(llevError, "Calling die_roll with num=%d size=%d\n", num, size);
00143         return(num); /* avoids a float exception */
00144     }
00145 
00146     if (op->type == PLAYER)
00147         luck = op->stats.luck;
00148 
00149     for (i = 0; i < num; i++) {
00150         if (RANDOM()%base < MIN(10, abs(luck)) && !gotlucky) {
00151             /* we have a winner */
00152             gotlucky++;
00153             ((luck > 0) ? (luck = 1) : (luck = -1));
00154             diff -= luck;
00155             if (diff < 1)
00156                 return(num); /*check again*/
00157             ((goodbad) ? (min += luck) : (diff));
00158             ran = RANDOM();
00159             total += MAX(1, MIN(size, (ran%diff)+min));
00160         } else {
00161             total += RANDOM()%size+1;
00162         }
00163     }
00164     return(total);
00165 }
00166 
00174 int rndm(int min, int max) {
00175     int diff;
00176 
00177     diff = max-min+1;
00178     if (max < 1 || diff < 1)
00179         return (min);
00180 
00181     return (RANDOM()%diff+min);
00182 }
00183 
00187 void decay_objects(mapstruct *m) {
00188     int x, y, destroy;
00189     object *op, *otmp;
00190 
00191     if (m->unique)
00192         return;
00193 
00194     for (x = 0; x < MAP_WIDTH(m); x++)
00195         for (y = 0; y < MAP_HEIGHT(m); y++)
00196             for (op = GET_MAP_OB(m, x, y); op; op = otmp) {
00197                 destroy = 0;
00198                 otmp = op->above;
00199                 if (QUERY_FLAG(op, FLAG_IS_FLOOR) && QUERY_FLAG(op, FLAG_UNIQUE))
00200                     break;
00201                 if (QUERY_FLAG(op, FLAG_IS_FLOOR)
00202                 || QUERY_FLAG(op, FLAG_OBJ_ORIGINAL)
00203                 || QUERY_FLAG(op, FLAG_UNIQUE)
00204                 || QUERY_FLAG(op, FLAG_OVERLAY_FLOOR)
00205                 || QUERY_FLAG(op, FLAG_UNPAID)
00206                 || IS_LIVE(op))
00207                     continue;
00208                 if (op->head)
00209                     /* Don't try to remove a non head part of a multipart object, remove_ob() would abort(). */
00210                     continue;
00211                 /* otherwise, we decay and destroy */
00212                 if (IS_WEAPON(op)) {
00213                     op->stats.dam--;
00214                     if (op->stats.dam < 0)
00215                         destroy = 1;
00216                 } else if (IS_ARMOR(op)
00217                 || IS_SHIELD(op)
00218                 || op->type == GIRDLE
00219                 || op->type == GLOVES
00220                 || op->type == CLOAK) {
00221                     op->stats.ac--;
00222                     if (op->stats.ac < 0)
00223                         destroy = 1;
00224                 } else if (op->type == FOOD) {
00225                     op->stats.food -= rndm(5, 20);
00226                     if (op->stats.food < 0)
00227                         destroy = 1;
00228                 } else {
00229                     if (op->material&M_PAPER
00230                     || op->material&M_LEATHER
00231                     || op->material&M_WOOD
00232                     || op->material&M_ORGANIC
00233                     || op->material&M_CLOTH
00234                     || op->material&M_LIQUID)
00235                         destroy = 1;
00236                     if (op->material&M_IRON && rndm(1, 5) == 1)
00237                         destroy = 1;
00238                     if (op->material&M_GLASS && rndm(1, 2) == 1)
00239                         destroy = 1;
00240                     if ((op->material&M_STONE || op->material&M_ADAMANT) && rndm(1, 10) == 1)
00241                         destroy = 1;
00242                     if ((op->material&M_SOFT_METAL || op->material&M_BONE)
00243                     && rndm(1, 3) == 1)
00244                         destroy = 1;
00245                     if (op->material&M_ICE && rndm(0, 100) > 70)
00246                         destroy = 1;
00247                 }
00248                 /* adjust overall chance below */
00249                 if (destroy && rndm(0, 1)) {
00250                     remove_ob(op);
00251                     free_object(op);
00252                 }
00253             }
00254 }
00255 
00262 materialtype_t *name_to_material(const char *name) {
00263     materialtype_t *mt, *nmt;
00264 
00265     mt = NULL;
00266     for (nmt = materialt; nmt != NULL && nmt->next != NULL; nmt = nmt->next) {
00267         if (strcmp(name, nmt->name) == 0) {
00268             mt = nmt;
00269             break;
00270         }
00271     }
00272     return mt;
00273 }
00274 
00281 void transmute_materialname(object *op, const object *change) {
00282     materialtype_t *mt;
00283     int j;
00284 
00285     if (op->materialname == NULL)
00286         return;
00287 
00288     if (change->materialname != NULL
00289     && strcmp(op->materialname, change->materialname))
00290         return;
00291 
00292     if (!(IS_ARMOR(op) || IS_SHIELD(op) || op->type == GIRDLE || op->type == GLOVES || op->type == CLOAK))
00293         return;
00294 
00295     mt = name_to_material(op->materialname);
00296     if (!mt) {
00297         LOG(llevError, "archetype '%s>%s' uses nonexistent material '%s'\n", op->arch->name, op->name, op->materialname);
00298         return;
00299     }
00300 
00301     for (j = 0; j < NROFATTACKS; j++)
00302         if (op->resist[j] == 0 && change->resist[j] != 0) {
00303             op->resist[j] += mt->mod[j];
00304             if (op->resist[j] > 100)
00305                 op->resist[j] = 100;
00306             if (op->resist[j] < -100)
00307                 op->resist[j] = -100;
00308         }
00309 }
00310 
00314 void set_materialname(object *op, int difficulty, materialtype_t *nmt) {
00315     materialtype_t *mt, *lmt;
00316 
00317     if (op->materialname != NULL)
00318         return;
00319 
00320     if (nmt == NULL) {
00321         lmt = NULL;
00322 #ifndef NEW_MATERIAL_CODE
00323         for (mt = materialt; mt != NULL && mt->next != NULL; mt = mt->next) {
00324             if (op->material&mt->material) {
00325                 lmt = mt;
00326                 break;
00327             }
00328         }
00329 #else
00330         for (mt = materialt; mt != NULL && mt->next != NULL; mt = mt->next) {
00331             if (op->material&mt->material
00332             && rndm(1, 100) <= mt->chance
00333             && difficulty >= mt->difficulty
00334             && (op->magic >= mt->magic || mt->magic == 0)) {
00335                 lmt = mt;
00336                 if (!(IS_WEAPON(op) || IS_ARMOR(op) || IS_SHIELD(op) || op->type == GIRDLE || op->type == GLOVES || op->type == CLOAK))
00337                     break;
00338             }
00339         }
00340 #endif
00341     } else {
00342         lmt = nmt;
00343     }
00344 
00345     if (lmt != NULL) {
00346 #ifndef NEW_MATERIAL_CODE
00347         op->materialname = add_string(lmt->name);
00348         return;
00349 #else
00350 
00351         if (op->stats.dam && IS_WEAPON(op)) {
00352             op->stats.dam += lmt->damage;
00353             if (op->stats.dam < 1)
00354                 op->stats.dam = 1;
00355         }
00356         if (op->stats.sp && op->type == BOW)
00357             op->stats.sp += lmt->sp;
00358         if (op->stats.wc && IS_WEAPON(op))
00359             op->stats.wc += lmt->wc;
00360         if (IS_ARMOR(op) || IS_SHIELD(op) || op->type == GIRDLE || op->type == GLOVES || op->type == CLOAK) {
00361             int j;
00362 
00363             if (op->stats.ac)
00364                 op->stats.ac += lmt->ac;
00365             for (j = 0; j < NROFATTACKS; j++)
00366                 if (op->resist[j] != 0) {
00367                     op->resist[j] += lmt->mod[j];
00368                     if (op->resist[j] > 100)
00369                         op->resist[j] = 100;
00370                     if (op->resist[j] < -100)
00371                         op->resist[j] = -100;
00372                 }
00373         }
00374         op->materialname = add_string(lmt->name);
00375         /* dont make it unstackable if it doesn't need to be */
00376         if (IS_WEAPON(op) || IS_ARMOR(op) || IS_SHIELD(op) || op->type == GIRDLE || op->type == GLOVES || op->type == CLOAK) {
00377             op->weight = (op->weight*lmt->weight)/100;
00378             op->value = (op->value*lmt->value)/100;
00379         }
00380 #endif
00381     }
00382 }
00383 
00388 void strip_media_tag(char *message) {
00389     int in_tag = 0;
00390     char *dest;
00391     char *src;
00392     src = dest = message;
00393 
00394     while (*src != '\0') {
00395         if (*src == '[') {
00396             in_tag = 1;
00397         } else if (in_tag && (*src == ']'))
00398             in_tag = 0;
00399         else if (!in_tag) {
00400             *dest = *src;
00401             dest++;
00402         }
00403         src++;
00404     }
00405     *dest = '\0';
00406 }
00407 
00414 const char *strrstr(const char *haystack, const char *needle) {
00415     const char *lastneedle;
00416 
00417     lastneedle = NULL;
00418     while ((haystack = strstr(haystack, needle)) != NULL) {
00419         lastneedle = haystack;
00420         haystack++;
00421     }
00422     return lastneedle;
00423 
00424 }
00425 
00426 #define EOL_SIZE (sizeof("\n")-1)
00427 
00431 void strip_endline(char *buf) {
00432     if (strlen(buf) < sizeof("\n")) {
00433         return;
00434     }
00435     if (!strcmp(buf+strlen(buf)-EOL_SIZE, "\n"))
00436         buf[strlen(buf)-EOL_SIZE] = '\0';
00437 }
00438 
00444 void replace(const char *src, const char *key, const char *replacement, char *result, size_t resultsize) {
00445     size_t resultlen;
00446     size_t keylen;
00447 
00448     /* special case to prevent infinite loop if key==replacement=="" */
00449     if (strcmp(key, replacement) == 0) {
00450         snprintf(result, resultsize, "%s", src);
00451         return;
00452     }
00453 
00454     keylen = strlen(key);
00455 
00456     resultlen = 0;
00457     while (*src != '\0' && resultlen+1 < resultsize) {
00458         if (strncmp(src, key, keylen) == 0) {
00459             snprintf(result+resultlen, resultsize-resultlen, "%s", replacement);
00460             resultlen += strlen(result+resultlen);
00461             src += keylen;
00462         } else {
00463             result[resultlen++] = *src++;
00464         }
00465     }
00466     result[resultlen] = '\0';
00467 }
00468 
00485 void make_list_like(char *input) {
00486     char *p, tmp[MAX_BUF];
00487     int i;
00488     if (!input || strlen(input) > MAX_BUF-5)
00489         return;
00490     /* bad stuff would happen if we continued here, the -5 is to make space for ' and ' */
00491 
00492     strncpy(tmp, input, MAX_BUF-5);
00493     /*trim all trailing commas, spaces etc.*/
00494     for (i = strlen(tmp); !isalnum(tmp[i]) && i >= 0; i--)
00495         tmp[i] = '\0';
00496     strcat(tmp, ".");
00497 
00498     p = strrchr(tmp, ',');
00499     if (p) {
00500         *p = '\0';
00501         strcpy(input, tmp);
00502         p++;
00503         strcat(input, " and");
00504         strcat(input, p);
00505     } else
00506         strcpy(input, tmp);
00507     return;
00508 }
00509 
00516 void replace_unprintable_chars(char *buf) {
00517     char *p;
00518 
00519     for (p = buf; *p != '\0'; p++) {
00520         if (*p < ' ') {
00521             *p = ' ';
00522         }
00523     }
00524 }