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

# User Rev Content
1 elmex 1.1 /*
2 pippijn 1.39 * 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 elmex 1.1
25     /*
26     * General convenience functions for crossfire.
27     */
28    
29 root 1.37 #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 elmex 1.1 #include <global.h>
37     #include <funcpoint.h>
38     #include <material.h>
39    
40 root 1.4 #include <glib.h>
41    
42 root 1.40 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 elmex 1.1 /*
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 root 1.9 int
82 root 1.41 random_roll (int r_min, int r_max, const object *op, int goodbad)
83 root 1.9 {
84 root 1.41 int base = r_max - r_min > 1 ? 20 : 50; /* d2 and d3 are corner cases */
85 elmex 1.1
86 root 1.41 if (r_max < 1 || r_max < r_min)
87 root 1.9 {
88 root 1.41 LOG (llevError, "Calling random_roll with min=%d max=%d\n", r_min, r_max);
89     return r_min;
90 elmex 1.1 }
91    
92 root 1.41 if (op->type == PLAYER)
93 root 1.9 {
94 root 1.41 int luck = op->stats.luck;
95 elmex 1.1
96 root 1.41 if (rndm (base) < min (10, abs (luck)))
97     {
98     //TODO: take luck into account
99     }
100 elmex 1.1 }
101 root 1.41
102     return rndm (r_min, r_max);
103 elmex 1.1 }
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 root 1.9 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 elmex 1.1
120 root 1.9 if (max < 1 || diff < 1)
121     {
122 root 1.20 LOG (llevError, "Calling random_roll with min=%" PRId64 " max=%" PRId64 "\n", min, max);
123 root 1.9 return (min); /* avoids a float exception */
124 elmex 1.1 }
125    
126 root 1.9 /* 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 elmex 1.1
146 root 1.9 return (MAX (omin, MIN (max, (ran % diff) + min)));
147 elmex 1.1 }
148 root 1.9 return ((ran % diff) + min);
149 elmex 1.1 }
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 root 1.9 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 elmex 1.1
174 root 1.9 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 root 1.2 }
195 elmex 1.1 }
196 root 1.9 return (total);
197 elmex 1.1 }
198    
199 root 1.33 /* decay and destroy perishable items in a map */
200 root 1.9 void
201 root 1.32 maptile::decay_objects ()
202 elmex 1.1 {
203 root 1.35 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 root 1.32
213 root 1.35 // 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 root 1.32
217 root 1.35 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 elmex 1.1 }
266    
267     /* convert materialname to materialtype_t */
268    
269 root 1.9 materialtype_t *
270     name_to_material (const char *name)
271 elmex 1.1 {
272 root 1.9 materialtype_t *mt, *nmt;
273 elmex 1.1
274 root 1.9 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 root 1.2 }
282 elmex 1.1 }
283 root 1.9 return mt;
284 elmex 1.1 }
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 root 1.9 void
291     transmute_materialname (object *op, const object *change)
292 elmex 1.1 {
293 root 1.9 materialtype_t *mt;
294     int j;
295 elmex 1.1
296 root 1.9 if (op->materialname == NULL)
297     return;
298 elmex 1.1
299 root 1.9 if (change->materialname != NULL && strcmp (op->materialname, change->materialname))
300     return;
301    
302 root 1.26 if (!op->is_armor ())
303 root 1.9 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 elmex 1.1 }
322    
323     /* set the materialname and type for an item */
324 root 1.9 void
325     set_materialname (object *op, int difficulty, materialtype_t *nmt)
326 elmex 1.1 {
327 root 1.9 materialtype_t *mt, *lmt;
328    
329 pippijn 1.5 #ifdef NEW_MATERIAL_CODE
330 root 1.9 int j;
331 pippijn 1.5 #endif
332 elmex 1.1
333 root 1.9 if (op->materialname != NULL)
334     return;
335 elmex 1.1
336    
337    
338 root 1.9 if (nmt == NULL)
339     {
340     lmt = NULL;
341 elmex 1.1 #ifndef NEW_MATERIAL_CODE
342 root 1.9 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 root 1.2 }
349     }
350 elmex 1.1
351     #else
352 root 1.9 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 root 1.26 if (!(op->is_weapon () || op->is_armor ()))
359 root 1.9 break;
360 root 1.2 }
361     }
362 elmex 1.1 #endif
363 root 1.9 }
364     else
365     {
366     lmt = nmt;
367 elmex 1.1 }
368    
369 root 1.9 if (lmt != NULL)
370     {
371 elmex 1.1 #ifndef NEW_MATERIAL_CODE
372 root 1.9 op->materialname = lmt->name;
373     return;
374 elmex 1.1 #else
375    
376 root 1.26 if (op->stats.dam && op->is_weapon ())
377 root 1.9 {
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 root 1.26 if (op->stats.wc && op->is_weapon ())
385 root 1.9 op->stats.wc += lmt->wc;
386 root 1.26 if (op->is_armor ())
387 root 1.9 {
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 root 1.26 if (op->is_weapon () || op->is_armor ())
403 root 1.9 {
404     op->weight = (op->weight * lmt->weight) / 100;
405     op->value = (op->value * lmt->value) / 100;
406 root 1.2 }
407 elmex 1.1 #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 root 1.9 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 elmex 1.1 }
452 root 1.9 return lastneedle;
453    
454 elmex 1.1 }
455 root 1.9
456 elmex 1.1 #define EOL_SIZE (sizeof("\n")-1)
457 root 1.9 void
458     strip_endline (char *buf)
459     {
460     if (strlen (buf) < sizeof ("\n"))
461     {
462     return;
463 elmex 1.1 }
464 root 1.9 if (!strcmp (buf + strlen (buf) - EOL_SIZE, "\n"))
465     buf[strlen (buf) - EOL_SIZE] = '\0';
466 elmex 1.1 }
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 root 1.9 void
474     replace (const char *src, const char *key, const char *replacement, char *result, size_t resultsize)
475 elmex 1.1 {
476 root 1.9 size_t resultlen;
477     size_t keylen;
478 elmex 1.1
479 root 1.9 /* 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 root 1.2 {
493 root 1.9 snprintf (result + resultlen, resultsize - resultlen, "%s", replacement);
494     resultlen += strlen (result + resultlen);
495     src += keylen;
496 root 1.2 }
497 root 1.9 else
498 root 1.2 {
499 root 1.9 result[resultlen++] = *src++;
500 root 1.2 }
501 root 1.9 }
502     result[resultlen] = '\0';
503 elmex 1.1 }
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 root 1.9 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 elmex 1.1 return;
521 root 1.9 /* 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 root 1.11
528 root 1.9 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 root 1.11
542 root 1.9 return;
543 elmex 1.1 }
544 root 1.3
545 root 1.14 /////////////////////////////////////////////////////////////////////////////
546    
547 root 1.37 void
548     fork_abort (const char *msg)
549     {
550     if (!fork ())
551     {
552     signal (SIGABRT, SIG_DFL);
553     abort ();
554     }
555    
556 root 1.38 LOG (llevError, "fork abort: %s\n", msg);
557 root 1.37 }
558 root 1.38
559 root 1.25 void *salloc_ (int n) throw (std::bad_alloc)
560 root 1.10 {
561 root 1.25 void *ptr = g_slice_alloc (n);
562 root 1.13
563 root 1.23 if (!ptr)
564 root 1.13 throw std::bad_alloc ();
565 root 1.4
566 root 1.23 return ptr;
567     }
568    
569 root 1.25 void *salloc_ (int n, void *src) throw (std::bad_alloc)
570 root 1.23 {
571 root 1.25 void *ptr = salloc_ (n);
572 root 1.23
573 root 1.24 if (src)
574 root 1.25 memcpy (ptr, src, n);
575 root 1.24 else
576 root 1.25 memset (ptr, 0, n);
577 root 1.23
578     return ptr;
579 root 1.3 }
580 root 1.11
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 root 1.23 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 root 1.17
613 root 1.32 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