ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/utils.C
Revision: 1.40
Committed: Thu Jan 18 19:32:37 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.39: +24 -20 lines
Log Message:
just experimenting

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     random_roll (int min, int max, const object *op, int goodbad)
83     {
84     int omin, diff, luck, base, ran;
85 elmex 1.1
86 root 1.9 omin = min;
87     diff = max - min + 1;
88     ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
89 elmex 1.1
90 root 1.9 if (max < 1 || diff < 1)
91     {
92     LOG (llevError, "Calling random_roll with min=%d max=%d\n", min, max);
93     return (min); /* avoids a float exception */
94 elmex 1.1 }
95    
96 root 1.9 ran = RANDOM ();
97 elmex 1.1
98 root 1.9 if (op->type != PLAYER)
99     return ((ran % diff) + min);
100 elmex 1.1
101 root 1.9 luck = op->stats.luck;
102     if (RANDOM () % base < MIN (10, abs (luck)))
103     {
104     /* we have a winner */
105     ((luck > 0) ? (luck = 1) : (luck = -1));
106     diff -= luck;
107     if (diff < 1)
108     return (omin); /*check again */
109     ((goodbad) ? (min += luck) : (diff));
110 elmex 1.1
111 root 1.9 return (MAX (omin, MIN (max, (ran % diff) + min)));
112 elmex 1.1 }
113 root 1.9 return ((ran % diff) + min);
114 elmex 1.1 }
115    
116     /*
117     * This is a 64 bit version of random_roll above. This is needed
118     * for exp loss calculations for players changing religions.
119     */
120    
121 root 1.9 sint64
122     random_roll64 (sint64 min, sint64 max, const object *op, int goodbad)
123     {
124     sint64 omin, diff, luck, ran;
125     int base;
126    
127     omin = min;
128     diff = max - min + 1;
129     ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
130 elmex 1.1
131 root 1.9 if (max < 1 || diff < 1)
132     {
133 root 1.20 LOG (llevError, "Calling random_roll with min=%" PRId64 " max=%" PRId64 "\n", min, max);
134 root 1.9 return (min); /* avoids a float exception */
135 elmex 1.1 }
136    
137 root 1.9 /* Don't know of a portable call to get 64 bit random values.
138     * So make a call to get two 32 bit random numbers, and just to
139     * a little byteshifting. Do make sure the first one is only
140     * 32 bit, so we don't get skewed results
141     */
142     ran = (RANDOM () & 0xffffffff) | ((sint64) RANDOM () << 32);
143    
144     if (op->type != PLAYER)
145     return ((ran % diff) + min);
146    
147     luck = op->stats.luck;
148     if (RANDOM () % base < MIN (10, abs (luck)))
149     {
150     /* we have a winner */
151     ((luck > 0) ? (luck = 1) : (luck = -1));
152     diff -= luck;
153     if (diff < 1)
154     return (omin); /*check again */
155     ((goodbad) ? (min += luck) : (diff));
156 elmex 1.1
157 root 1.9 return (MAX (omin, MIN (max, (ran % diff) + min)));
158 elmex 1.1 }
159 root 1.9 return ((ran % diff) + min);
160 elmex 1.1 }
161    
162     /*
163     * Roll a number of dice (2d3, 4d6). Uses op to determine luck,
164     * If goodbad is non-zero, luck increases the roll, if zero, it decreases.
165     * Generally, op should be the player/caster/hitter requesting the roll,
166     * not the recipient (ie, the poor slob getting hit).
167     * The args are num D size (ie 4d6) [garbled 20010916]
168     */
169    
170 root 1.9 int
171     die_roll (int num, int size, const object *op, int goodbad)
172     {
173     int min, diff, luck, total, i, gotlucky, base, ran;
174    
175     diff = size;
176     min = 1;
177     luck = total = gotlucky = 0;
178     ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
179     if (size < 2 || diff < 1)
180     {
181     LOG (llevError, "Calling die_roll with num=%d size=%d\n", num, size);
182     return (num); /* avoids a float exception */
183     }
184 elmex 1.1
185 root 1.9 if (op->type == PLAYER)
186     luck = op->stats.luck;
187    
188     for (i = 0; i < num; i++)
189     {
190     if (RANDOM () % base < MIN (10, abs (luck)) && !gotlucky)
191     {
192     /* we have a winner */
193     gotlucky++;
194     ((luck > 0) ? (luck = 1) : (luck = -1));
195     diff -= luck;
196     if (diff < 1)
197     return (num); /*check again */
198     ((goodbad) ? (min += luck) : (diff));
199     ran = RANDOM ();
200     total += MAX (1, MIN (size, (ran % diff) + min));
201     }
202     else
203     {
204     total += RANDOM () % size + 1;
205 root 1.2 }
206 elmex 1.1 }
207 root 1.9 return (total);
208 elmex 1.1 }
209    
210 root 1.33 /* decay and destroy perishable items in a map */
211 root 1.9 void
212 root 1.32 maptile::decay_objects ()
213 elmex 1.1 {
214 root 1.35 if (!spaces)
215     return;
216    
217     for (mapspace *ms = spaces + size (); ms-- > spaces; )
218     for (object *above, *op = ms->bot; op; op = above)
219     {
220     above = op->above;
221    
222     bool destroy = 0;
223 root 1.32
224 root 1.35 // do not decay anything above unique floor tiles (yet :)
225     if (QUERY_FLAG (op, FLAG_IS_FLOOR) && QUERY_FLAG (op, FLAG_UNIQUE))
226     break;
227 root 1.32
228 root 1.35 if (QUERY_FLAG (op, FLAG_IS_FLOOR)
229     || QUERY_FLAG (op, FLAG_OBJ_ORIGINAL)
230     || QUERY_FLAG (op, FLAG_OBJ_SAVE_ON_OVL)
231     || QUERY_FLAG (op, FLAG_UNIQUE)
232     || QUERY_FLAG (op, FLAG_OVERLAY_FLOOR)
233     || QUERY_FLAG (op, FLAG_UNPAID)
234     || op->is_alive ())
235     ; // do not decay
236     else if (op->is_weapon ())
237     {
238     op->stats.dam--;
239     if (op->stats.dam < 0)
240     destroy = 1;
241     }
242     else if (op->is_armor ())
243     {
244     op->stats.ac--;
245     if (op->stats.ac < 0)
246     destroy = 1;
247     }
248     else if (op->type == FOOD)
249     {
250     op->stats.food -= rndm (5, 20);
251     if (op->stats.food < 0)
252     destroy = 1;
253     }
254     else
255     {
256     int mat = op->material;
257    
258     if (mat & M_PAPER
259     || mat & M_LEATHER
260     || mat & M_WOOD
261     || mat & M_ORGANIC
262     || mat & M_CLOTH
263     || mat & M_LIQUID
264     || (mat & M_IRON && rndm (1, 5) == 1)
265     || (mat & M_GLASS && rndm (1, 2) == 1)
266     || ((mat & M_STONE || mat & M_ADAMANT) && rndm (1, 10) == 1)
267     || ((mat & M_SOFT_METAL || mat & M_BONE) && rndm (1, 3) == 1)
268     || (mat & M_ICE && temp > 32))
269     destroy = 1;
270     }
271    
272     /* adjust overall chance below */
273     if (destroy && rndm (0, 1))
274     op->destroy ();
275     }
276 elmex 1.1 }
277    
278     /* convert materialname to materialtype_t */
279    
280 root 1.9 materialtype_t *
281     name_to_material (const char *name)
282 elmex 1.1 {
283 root 1.9 materialtype_t *mt, *nmt;
284 elmex 1.1
285 root 1.9 mt = NULL;
286     for (nmt = materialt; nmt != NULL && nmt->next != NULL; nmt = nmt->next)
287     {
288     if (strcmp (name, nmt->name) == 0)
289     {
290     mt = nmt;
291     break;
292 root 1.2 }
293 elmex 1.1 }
294 root 1.9 return mt;
295 elmex 1.1 }
296    
297     /* when doing transmutation of objects, we have to recheck the resistances,
298     * as some that did not apply previously, may apply now.
299     */
300    
301 root 1.9 void
302     transmute_materialname (object *op, const object *change)
303 elmex 1.1 {
304 root 1.9 materialtype_t *mt;
305     int j;
306 elmex 1.1
307 root 1.9 if (op->materialname == NULL)
308     return;
309 elmex 1.1
310 root 1.9 if (change->materialname != NULL && strcmp (op->materialname, change->materialname))
311     return;
312    
313 root 1.26 if (!op->is_armor ())
314 root 1.9 return;
315    
316     mt = name_to_material (op->materialname);
317     if (!mt)
318     {
319     LOG (llevError, "archetype '%s>%s' uses nonexistent material '%s'\n", &op->arch->name, &op->name, &op->materialname);
320     return;
321     }
322    
323     for (j = 0; j < NROFATTACKS; j++)
324     if (op->resist[j] == 0 && change->resist[j] != 0)
325     {
326     op->resist[j] += mt->mod[j];
327     if (op->resist[j] > 100)
328     op->resist[j] = 100;
329     if (op->resist[j] < -100)
330     op->resist[j] = -100;
331     }
332 elmex 1.1 }
333    
334     /* set the materialname and type for an item */
335 root 1.9 void
336     set_materialname (object *op, int difficulty, materialtype_t *nmt)
337 elmex 1.1 {
338 root 1.9 materialtype_t *mt, *lmt;
339    
340 pippijn 1.5 #ifdef NEW_MATERIAL_CODE
341 root 1.9 int j;
342 pippijn 1.5 #endif
343 elmex 1.1
344 root 1.9 if (op->materialname != NULL)
345     return;
346 elmex 1.1
347    
348    
349 root 1.9 if (nmt == NULL)
350     {
351     lmt = NULL;
352 elmex 1.1 #ifndef NEW_MATERIAL_CODE
353 root 1.9 for (mt = materialt; mt != NULL && mt->next != NULL; mt = mt->next)
354     {
355     if (op->material & mt->material)
356     {
357     lmt = mt;
358     break;
359 root 1.2 }
360     }
361 elmex 1.1
362     #else
363 root 1.9 for (mt = materialt; mt != NULL && mt->next != NULL; mt = mt->next)
364     {
365     if (op->material & mt->material && rndm (1, 100) <= mt->chance &&
366     difficulty >= mt->difficulty && (op->magic >= mt->magic || mt->magic == 0))
367     {
368     lmt = mt;
369 root 1.26 if (!(op->is_weapon () || op->is_armor ()))
370 root 1.9 break;
371 root 1.2 }
372     }
373 elmex 1.1 #endif
374 root 1.9 }
375     else
376     {
377     lmt = nmt;
378 elmex 1.1 }
379    
380 root 1.9 if (lmt != NULL)
381     {
382 elmex 1.1 #ifndef NEW_MATERIAL_CODE
383 root 1.9 op->materialname = lmt->name;
384     return;
385 elmex 1.1 #else
386    
387 root 1.26 if (op->stats.dam && op->is_weapon ())
388 root 1.9 {
389     op->stats.dam += lmt->damage;
390     if (op->stats.dam < 1)
391     op->stats.dam = 1;
392     }
393     if (op->stats.sp && op->type == BOW)
394     op->stats.sp += lmt->sp;
395 root 1.26 if (op->stats.wc && op->is_weapon ())
396 root 1.9 op->stats.wc += lmt->wc;
397 root 1.26 if (op->is_armor ())
398 root 1.9 {
399     if (op->stats.ac)
400     op->stats.ac += lmt->ac;
401     for (j = 0; j < NROFATTACKS; j++)
402     if (op->resist[j] != 0)
403     {
404     op->resist[j] += lmt->mod[j];
405     if (op->resist[j] > 100)
406     op->resist[j] = 100;
407     if (op->resist[j] < -100)
408     op->resist[j] = -100;
409     }
410     }
411     op->materialname = add_string (lmt->name);
412     /* dont make it unstackable if it doesn't need to be */
413 root 1.26 if (op->is_weapon () || op->is_armor ())
414 root 1.9 {
415     op->weight = (op->weight * lmt->weight) / 100;
416     op->value = (op->value * lmt->value) / 100;
417 root 1.2 }
418 elmex 1.1 #endif
419     }
420     }
421    
422     /*
423     * Strip out the media tags from a String.
424     * Warning the input string will contain the result string
425     */
426 root 1.9 void
427     strip_media_tag (char *message)
428     {
429     int in_tag = 0;
430     char *dest;
431     char *src;
432    
433     src = dest = message;
434     while (*src != '\0')
435     {
436     if (*src == '[')
437     {
438     in_tag = 1;
439     }
440     else if (in_tag && (*src == ']'))
441     in_tag = 0;
442     else if (!in_tag)
443     {
444     *dest = *src;
445     dest++;
446     }
447     src++;
448     }
449     *dest = '\0';
450     }
451    
452     const char *
453     strrstr (const char *haystack, const char *needle)
454     {
455     const char *lastneedle;
456    
457     lastneedle = NULL;
458     while ((haystack = strstr (haystack, needle)) != NULL)
459     {
460     lastneedle = haystack;
461     haystack++;
462 elmex 1.1 }
463 root 1.9 return lastneedle;
464    
465 elmex 1.1 }
466 root 1.9
467 elmex 1.1 #define EOL_SIZE (sizeof("\n")-1)
468 root 1.9 void
469     strip_endline (char *buf)
470     {
471     if (strlen (buf) < sizeof ("\n"))
472     {
473     return;
474 elmex 1.1 }
475 root 1.9 if (!strcmp (buf + strlen (buf) - EOL_SIZE, "\n"))
476     buf[strlen (buf) - EOL_SIZE] = '\0';
477 elmex 1.1 }
478    
479     /**
480     * Replace in string src all occurrences of key by replacement. The resulting
481     * string is put into result; at most resultsize characters (including the
482     * terminating null character) will be written to result.
483     */
484 root 1.9 void
485     replace (const char *src, const char *key, const char *replacement, char *result, size_t resultsize)
486 elmex 1.1 {
487 root 1.9 size_t resultlen;
488     size_t keylen;
489 elmex 1.1
490 root 1.9 /* special case to prevent infinite loop if key==replacement=="" */
491     if (strcmp (key, replacement) == 0)
492     {
493     snprintf (result, resultsize, "%s", src);
494     return;
495     }
496    
497     keylen = strlen (key);
498    
499     resultlen = 0;
500     while (*src != '\0' && resultlen + 1 < resultsize)
501     {
502     if (strncmp (src, key, keylen) == 0)
503 root 1.2 {
504 root 1.9 snprintf (result + resultlen, resultsize - resultlen, "%s", replacement);
505     resultlen += strlen (result + resultlen);
506     src += keylen;
507 root 1.2 }
508 root 1.9 else
509 root 1.2 {
510 root 1.9 result[resultlen++] = *src++;
511 root 1.2 }
512 root 1.9 }
513     result[resultlen] = '\0';
514 elmex 1.1 }
515    
516     /**
517     * Taking a string as an argument, mutate it into a string that looks like a list.
518     * a 'list' for the purposes here, is a string of items, seperated by commas, except
519     * for the last entry, which has an 'and' before it, and a full stop (period) after it.
520     * This function will also strip all trailing non alphanumeric characters.
521     * It does not insert an oxford comma.
522     */
523    
524 root 1.9 void
525     make_list_like (char *input)
526     {
527     char *p, tmp[MAX_BUF];
528     int i;
529    
530     if (!input || strlen (input) > MAX_BUF - 5)
531 elmex 1.1 return;
532 root 1.9 /* bad stuff would happen if we continued here, the -5 is to make space for ' and ' */
533    
534     strncpy (tmp, input, MAX_BUF - 5);
535     /*trim all trailing commas, spaces etc. */
536     for (i = strlen (tmp); !isalnum (tmp[i]) && i >= 0; i--)
537     tmp[i] = '\0';
538 root 1.11
539 root 1.9 strcat (tmp, ".");
540    
541     p = strrchr (tmp, ',');
542     if (p)
543     {
544     *p = '\0';
545     strcpy (input, tmp);
546     p++;
547     strcat (input, " and");
548     strcat (input, p);
549     }
550     else
551     strcpy (input, tmp);
552 root 1.11
553 root 1.9 return;
554 elmex 1.1 }
555 root 1.3
556 root 1.14 /////////////////////////////////////////////////////////////////////////////
557    
558 root 1.37 void
559     fork_abort (const char *msg)
560     {
561     if (!fork ())
562     {
563     signal (SIGABRT, SIG_DFL);
564     abort ();
565     }
566    
567 root 1.38 LOG (llevError, "fork abort: %s\n", msg);
568 root 1.37 }
569 root 1.38
570 root 1.25 void *salloc_ (int n) throw (std::bad_alloc)
571 root 1.10 {
572 root 1.25 void *ptr = g_slice_alloc (n);
573 root 1.13
574 root 1.23 if (!ptr)
575 root 1.13 throw std::bad_alloc ();
576 root 1.4
577 root 1.23 return ptr;
578     }
579    
580 root 1.25 void *salloc_ (int n, void *src) throw (std::bad_alloc)
581 root 1.23 {
582 root 1.25 void *ptr = salloc_ (n);
583 root 1.23
584 root 1.24 if (src)
585 root 1.25 memcpy (ptr, src, n);
586 root 1.24 else
587 root 1.25 memset (ptr, 0, n);
588 root 1.23
589     return ptr;
590 root 1.3 }
591 root 1.11
592     void assign (char *dst, const char *src, int maxlen)
593     {
594     if (!src)
595     src = "";
596    
597     int len = strlen (src);
598    
599     if (len >= maxlen - 1)
600     {
601     if (maxlen <= 4)
602     {
603     memset (dst, '.', maxlen - 1);
604     dst [maxlen - 1] = 0;
605     }
606     else
607     {
608     memcpy (dst, src, maxlen - 4);
609     memcpy (dst + maxlen - 4, "...", 4);
610     }
611     }
612     else
613     memcpy (dst, src, len + 1);
614     }
615    
616 root 1.23 tstamp now ()
617     {
618     struct timeval tv;
619    
620     gettimeofday (&tv, 0);
621     return tstamp (tv.tv_sec) + tstamp (tv.tv_usec) * tstamp (1e-6);
622     }
623 root 1.17
624 root 1.32 int
625     similar_direction (int a, int b)
626     {
627     if (!a || !b)
628     return 0;
629    
630     int diff = (b - a) & 7;
631     return diff <= 1 || diff >= 7;
632     }
633