ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/utils.C
Revision: 1.10
Committed: Mon Sep 11 01:16:20 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.9: +12 -9 lines
Log Message:
fix perl class for archetype, never free once-allocated objects, or destruct
them, there are too many long-lived references (and refcount doesn't help,
likely because it isn't correctly being incremented/decremented).

File Contents

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