Crossfire Server, Branch 1.12
R12190
|
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 }