ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/utils.C
Revision: 1.41
Committed: Thu Jan 18 22:19:59 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.40: +13 -24 lines
Log Message:
RANDOM was broken due to signedness issues, also rewrote random_roll, no longer uses luck, but should

File Contents

# Content
1 /*
2 * CrossFire, A Multiplayer game for X-windows
3 *
4 * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5 * Copyright (C) 2002 Mark Wedel & Crossfire Development Team
6 * Copyright (C) 1992 Frank Tore Johansen
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * The authors can be reached via e-mail at <crossfire@schmorp.de>
23 */
24
25 /*
26 * General convenience functions for crossfire.
27 */
28
29 #include <cstdlib>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <sys/time.h>
33 #include <time.h>
34 #include <signal.h>
35
36 #include <global.h>
37 #include <funcpoint.h>
38 #include <material.h>
39
40 #include <glib.h>
41
42 rand_gen rndm (time (0));
43
44 tausworthe_random_generator::tausworthe_random_generator (uint32_t seed)
45 {
46 state [0] = max ( 2, seed * 69069U);
47 state [1] = max ( 8, state [0] * 69069U);
48 state [2] = max ( 16, state [1] * 69069U);
49 state [3] = max (128, state [2] * 69069U);
50
51 for (int i = 11; --i; )
52 operator ()();
53 }
54
55 uint32_t
56 tausworthe_random_generator::next ()
57 {
58 state [0] = ((state [0] & 0xFFFFFFFEU) << 18U) ^ (((state [0] << 6U) ^ state [0]) >> 13U);
59 state [1] = ((state [1] & 0xFFFFFFF8U) << 2U) ^ (((state [1] << 2U) ^ state [1]) >> 27U);
60 state [2] = ((state [2] & 0xFFFFFFF0U) << 7U) ^ (((state [2] << 13U) ^ state [2]) >> 21U);
61 state [3] = ((state [3] & 0xFFFFFF80U) << 13U) ^ (((state [3] << 3U) ^ state [3]) >> 12U);
62
63 return state [0] ^ state [1] ^ state [2] ^ state [3];
64 }
65
66 /*
67 * The random functions here take luck into account when rolling random
68 * dice or numbers. This function has less of an impact the larger the
69 * difference becomes in the random numbers. IE, the effect is lessened
70 * on a 1-1000 roll, vs a 1-6 roll. This can be used by crafty programmers,
71 * to specifically disable luck in certain rolls, simply by making the
72 * numbers larger (ie, 1d1000 > 500 vs 1d6 > 3)
73 */
74
75 /*
76 * Roll a random number between min and max. Uses op to determine luck,
77 * and if goodbad is non-zero, luck increases the roll, if zero, it decreases.
78 * Generally, op should be the player/caster/hitter requesting the roll,
79 * not the recipient (ie, the poor slob getting hit). [garbled 20010916]
80 */
81 int
82 random_roll (int r_min, int r_max, const object *op, int goodbad)
83 {
84 int base = r_max - r_min > 1 ? 20 : 50; /* d2 and d3 are corner cases */
85
86 if (r_max < 1 || r_max < r_min)
87 {
88 LOG (llevError, "Calling random_roll with min=%d max=%d\n", r_min, r_max);
89 return r_min;
90 }
91
92 if (op->type == PLAYER)
93 {
94 int luck = op->stats.luck;
95
96 if (rndm (base) < min (10, abs (luck)))
97 {
98 //TODO: take luck into account
99 }
100 }
101
102 return rndm (r_min, r_max);
103 }
104
105 /*
106 * This is a 64 bit version of random_roll above. This is needed
107 * for exp loss calculations for players changing religions.
108 */
109
110 sint64
111 random_roll64 (sint64 min, sint64 max, const object *op, int goodbad)
112 {
113 sint64 omin, diff, luck, ran;
114 int base;
115
116 omin = min;
117 diff = max - min + 1;
118 ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
119
120 if (max < 1 || diff < 1)
121 {
122 LOG (llevError, "Calling random_roll with min=%" PRId64 " max=%" PRId64 "\n", min, max);
123 return (min); /* avoids a float exception */
124 }
125
126 /* Don't know of a portable call to get 64 bit random values.
127 * So make a call to get two 32 bit random numbers, and just to
128 * a little byteshifting. Do make sure the first one is only
129 * 32 bit, so we don't get skewed results
130 */
131 ran = (RANDOM () & 0xffffffff) | ((sint64) RANDOM () << 32);
132
133 if (op->type != PLAYER)
134 return ((ran % diff) + min);
135
136 luck = op->stats.luck;
137 if (RANDOM () % base < MIN (10, abs (luck)))
138 {
139 /* we have a winner */
140 ((luck > 0) ? (luck = 1) : (luck = -1));
141 diff -= luck;
142 if (diff < 1)
143 return (omin); /*check again */
144 ((goodbad) ? (min += luck) : (diff));
145
146 return (MAX (omin, MIN (max, (ran % diff) + min)));
147 }
148 return ((ran % diff) + min);
149 }
150
151 /*
152 * Roll a number of dice (2d3, 4d6). Uses op to determine luck,
153 * If goodbad is non-zero, luck increases the roll, if zero, it decreases.
154 * Generally, op should be the player/caster/hitter requesting the roll,
155 * not the recipient (ie, the poor slob getting hit).
156 * The args are num D size (ie 4d6) [garbled 20010916]
157 */
158
159 int
160 die_roll (int num, int size, const object *op, int goodbad)
161 {
162 int min, diff, luck, total, i, gotlucky, base, ran;
163
164 diff = size;
165 min = 1;
166 luck = total = gotlucky = 0;
167 ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
168 if (size < 2 || diff < 1)
169 {
170 LOG (llevError, "Calling die_roll with num=%d size=%d\n", num, size);
171 return (num); /* avoids a float exception */
172 }
173
174 if (op->type == PLAYER)
175 luck = op->stats.luck;
176
177 for (i = 0; i < num; i++)
178 {
179 if (RANDOM () % base < MIN (10, abs (luck)) && !gotlucky)
180 {
181 /* we have a winner */
182 gotlucky++;
183 ((luck > 0) ? (luck = 1) : (luck = -1));
184 diff -= luck;
185 if (diff < 1)
186 return (num); /*check again */
187 ((goodbad) ? (min += luck) : (diff));
188 ran = RANDOM ();
189 total += MAX (1, MIN (size, (ran % diff) + min));
190 }
191 else
192 {
193 total += RANDOM () % size + 1;
194 }
195 }
196 return (total);
197 }
198
199 /* decay and destroy perishable items in a map */
200 void
201 maptile::decay_objects ()
202 {
203 if (!spaces)
204 return;
205
206 for (mapspace *ms = spaces + size (); ms-- > spaces; )
207 for (object *above, *op = ms->bot; op; op = above)
208 {
209 above = op->above;
210
211 bool destroy = 0;
212
213 // do not decay anything above unique floor tiles (yet :)
214 if (QUERY_FLAG (op, FLAG_IS_FLOOR) && QUERY_FLAG (op, FLAG_UNIQUE))
215 break;
216
217 if (QUERY_FLAG (op, FLAG_IS_FLOOR)
218 || QUERY_FLAG (op, FLAG_OBJ_ORIGINAL)
219 || QUERY_FLAG (op, FLAG_OBJ_SAVE_ON_OVL)
220 || QUERY_FLAG (op, FLAG_UNIQUE)
221 || QUERY_FLAG (op, FLAG_OVERLAY_FLOOR)
222 || QUERY_FLAG (op, FLAG_UNPAID)
223 || op->is_alive ())
224 ; // do not decay
225 else if (op->is_weapon ())
226 {
227 op->stats.dam--;
228 if (op->stats.dam < 0)
229 destroy = 1;
230 }
231 else if (op->is_armor ())
232 {
233 op->stats.ac--;
234 if (op->stats.ac < 0)
235 destroy = 1;
236 }
237 else if (op->type == FOOD)
238 {
239 op->stats.food -= rndm (5, 20);
240 if (op->stats.food < 0)
241 destroy = 1;
242 }
243 else
244 {
245 int mat = op->material;
246
247 if (mat & M_PAPER
248 || mat & M_LEATHER
249 || mat & M_WOOD
250 || mat & M_ORGANIC
251 || mat & M_CLOTH
252 || mat & M_LIQUID
253 || (mat & M_IRON && rndm (1, 5) == 1)
254 || (mat & M_GLASS && rndm (1, 2) == 1)
255 || ((mat & M_STONE || mat & M_ADAMANT) && rndm (1, 10) == 1)
256 || ((mat & M_SOFT_METAL || mat & M_BONE) && rndm (1, 3) == 1)
257 || (mat & M_ICE && temp > 32))
258 destroy = 1;
259 }
260
261 /* adjust overall chance below */
262 if (destroy && rndm (0, 1))
263 op->destroy ();
264 }
265 }
266
267 /* convert materialname to materialtype_t */
268
269 materialtype_t *
270 name_to_material (const char *name)
271 {
272 materialtype_t *mt, *nmt;
273
274 mt = NULL;
275 for (nmt = materialt; nmt != NULL && nmt->next != NULL; nmt = nmt->next)
276 {
277 if (strcmp (name, nmt->name) == 0)
278 {
279 mt = nmt;
280 break;
281 }
282 }
283 return mt;
284 }
285
286 /* when doing transmutation of objects, we have to recheck the resistances,
287 * as some that did not apply previously, may apply now.
288 */
289
290 void
291 transmute_materialname (object *op, const object *change)
292 {
293 materialtype_t *mt;
294 int j;
295
296 if (op->materialname == NULL)
297 return;
298
299 if (change->materialname != NULL && strcmp (op->materialname, change->materialname))
300 return;
301
302 if (!op->is_armor ())
303 return;
304
305 mt = name_to_material (op->materialname);
306 if (!mt)
307 {
308 LOG (llevError, "archetype '%s>%s' uses nonexistent material '%s'\n", &op->arch->name, &op->name, &op->materialname);
309 return;
310 }
311
312 for (j = 0; j < NROFATTACKS; j++)
313 if (op->resist[j] == 0 && change->resist[j] != 0)
314 {
315 op->resist[j] += mt->mod[j];
316 if (op->resist[j] > 100)
317 op->resist[j] = 100;
318 if (op->resist[j] < -100)
319 op->resist[j] = -100;
320 }
321 }
322
323 /* set the materialname and type for an item */
324 void
325 set_materialname (object *op, int difficulty, materialtype_t *nmt)
326 {
327 materialtype_t *mt, *lmt;
328
329 #ifdef NEW_MATERIAL_CODE
330 int j;
331 #endif
332
333 if (op->materialname != NULL)
334 return;
335
336
337
338 if (nmt == NULL)
339 {
340 lmt = NULL;
341 #ifndef NEW_MATERIAL_CODE
342 for (mt = materialt; mt != NULL && mt->next != NULL; mt = mt->next)
343 {
344 if (op->material & mt->material)
345 {
346 lmt = mt;
347 break;
348 }
349 }
350
351 #else
352 for (mt = materialt; mt != NULL && mt->next != NULL; mt = mt->next)
353 {
354 if (op->material & mt->material && rndm (1, 100) <= mt->chance &&
355 difficulty >= mt->difficulty && (op->magic >= mt->magic || mt->magic == 0))
356 {
357 lmt = mt;
358 if (!(op->is_weapon () || op->is_armor ()))
359 break;
360 }
361 }
362 #endif
363 }
364 else
365 {
366 lmt = nmt;
367 }
368
369 if (lmt != NULL)
370 {
371 #ifndef NEW_MATERIAL_CODE
372 op->materialname = lmt->name;
373 return;
374 #else
375
376 if (op->stats.dam && op->is_weapon ())
377 {
378 op->stats.dam += lmt->damage;
379 if (op->stats.dam < 1)
380 op->stats.dam = 1;
381 }
382 if (op->stats.sp && op->type == BOW)
383 op->stats.sp += lmt->sp;
384 if (op->stats.wc && op->is_weapon ())
385 op->stats.wc += lmt->wc;
386 if (op->is_armor ())
387 {
388 if (op->stats.ac)
389 op->stats.ac += lmt->ac;
390 for (j = 0; j < NROFATTACKS; j++)
391 if (op->resist[j] != 0)
392 {
393 op->resist[j] += lmt->mod[j];
394 if (op->resist[j] > 100)
395 op->resist[j] = 100;
396 if (op->resist[j] < -100)
397 op->resist[j] = -100;
398 }
399 }
400 op->materialname = add_string (lmt->name);
401 /* dont make it unstackable if it doesn't need to be */
402 if (op->is_weapon () || op->is_armor ())
403 {
404 op->weight = (op->weight * lmt->weight) / 100;
405 op->value = (op->value * lmt->value) / 100;
406 }
407 #endif
408 }
409 }
410
411 /*
412 * Strip out the media tags from a String.
413 * Warning the input string will contain the result string
414 */
415 void
416 strip_media_tag (char *message)
417 {
418 int in_tag = 0;
419 char *dest;
420 char *src;
421
422 src = dest = message;
423 while (*src != '\0')
424 {
425 if (*src == '[')
426 {
427 in_tag = 1;
428 }
429 else if (in_tag && (*src == ']'))
430 in_tag = 0;
431 else if (!in_tag)
432 {
433 *dest = *src;
434 dest++;
435 }
436 src++;
437 }
438 *dest = '\0';
439 }
440
441 const char *
442 strrstr (const char *haystack, const char *needle)
443 {
444 const char *lastneedle;
445
446 lastneedle = NULL;
447 while ((haystack = strstr (haystack, needle)) != NULL)
448 {
449 lastneedle = haystack;
450 haystack++;
451 }
452 return lastneedle;
453
454 }
455
456 #define EOL_SIZE (sizeof("\n")-1)
457 void
458 strip_endline (char *buf)
459 {
460 if (strlen (buf) < sizeof ("\n"))
461 {
462 return;
463 }
464 if (!strcmp (buf + strlen (buf) - EOL_SIZE, "\n"))
465 buf[strlen (buf) - EOL_SIZE] = '\0';
466 }
467
468 /**
469 * Replace in string src all occurrences of key by replacement. The resulting
470 * string is put into result; at most resultsize characters (including the
471 * terminating null character) will be written to result.
472 */
473 void
474 replace (const char *src, const char *key, const char *replacement, char *result, size_t resultsize)
475 {
476 size_t resultlen;
477 size_t keylen;
478
479 /* special case to prevent infinite loop if key==replacement=="" */
480 if (strcmp (key, replacement) == 0)
481 {
482 snprintf (result, resultsize, "%s", src);
483 return;
484 }
485
486 keylen = strlen (key);
487
488 resultlen = 0;
489 while (*src != '\0' && resultlen + 1 < resultsize)
490 {
491 if (strncmp (src, key, keylen) == 0)
492 {
493 snprintf (result + resultlen, resultsize - resultlen, "%s", replacement);
494 resultlen += strlen (result + resultlen);
495 src += keylen;
496 }
497 else
498 {
499 result[resultlen++] = *src++;
500 }
501 }
502 result[resultlen] = '\0';
503 }
504
505 /**
506 * Taking a string as an argument, mutate it into a string that looks like a list.
507 * a 'list' for the purposes here, is a string of items, seperated by commas, except
508 * for the last entry, which has an 'and' before it, and a full stop (period) after it.
509 * This function will also strip all trailing non alphanumeric characters.
510 * It does not insert an oxford comma.
511 */
512
513 void
514 make_list_like (char *input)
515 {
516 char *p, tmp[MAX_BUF];
517 int i;
518
519 if (!input || strlen (input) > MAX_BUF - 5)
520 return;
521 /* bad stuff would happen if we continued here, the -5 is to make space for ' and ' */
522
523 strncpy (tmp, input, MAX_BUF - 5);
524 /*trim all trailing commas, spaces etc. */
525 for (i = strlen (tmp); !isalnum (tmp[i]) && i >= 0; i--)
526 tmp[i] = '\0';
527
528 strcat (tmp, ".");
529
530 p = strrchr (tmp, ',');
531 if (p)
532 {
533 *p = '\0';
534 strcpy (input, tmp);
535 p++;
536 strcat (input, " and");
537 strcat (input, p);
538 }
539 else
540 strcpy (input, tmp);
541
542 return;
543 }
544
545 /////////////////////////////////////////////////////////////////////////////
546
547 void
548 fork_abort (const char *msg)
549 {
550 if (!fork ())
551 {
552 signal (SIGABRT, SIG_DFL);
553 abort ();
554 }
555
556 LOG (llevError, "fork abort: %s\n", msg);
557 }
558
559 void *salloc_ (int n) throw (std::bad_alloc)
560 {
561 void *ptr = g_slice_alloc (n);
562
563 if (!ptr)
564 throw std::bad_alloc ();
565
566 return ptr;
567 }
568
569 void *salloc_ (int n, void *src) throw (std::bad_alloc)
570 {
571 void *ptr = salloc_ (n);
572
573 if (src)
574 memcpy (ptr, src, n);
575 else
576 memset (ptr, 0, n);
577
578 return ptr;
579 }
580
581 void assign (char *dst, const char *src, int maxlen)
582 {
583 if (!src)
584 src = "";
585
586 int len = strlen (src);
587
588 if (len >= maxlen - 1)
589 {
590 if (maxlen <= 4)
591 {
592 memset (dst, '.', maxlen - 1);
593 dst [maxlen - 1] = 0;
594 }
595 else
596 {
597 memcpy (dst, src, maxlen - 4);
598 memcpy (dst + maxlen - 4, "...", 4);
599 }
600 }
601 else
602 memcpy (dst, src, len + 1);
603 }
604
605 tstamp now ()
606 {
607 struct timeval tv;
608
609 gettimeofday (&tv, 0);
610 return tstamp (tv.tv_sec) + tstamp (tv.tv_usec) * tstamp (1e-6);
611 }
612
613 int
614 similar_direction (int a, int b)
615 {
616 if (!a || !b)
617 return 0;
618
619 int diff = (b - a) & 7;
620 return diff <= 1 || diff >= 7;
621 }
622