ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/utils.C
Revision: 1.47
Committed: Wed Feb 7 02:04:46 2007 UTC (17 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.46: +10 -17 lines
Log Message:
rework material code in preparation for nuking object->materials, also add some useful object methods in preparation of the mapspace limiting

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.42 rand_gen rndm;
43 root 1.40
44 root 1.42 void
45     tausworthe_random_generator::seed (uint32_t seed)
46 root 1.40 {
47 root 1.43 state [0] = max ( 2U, seed * 69069U);
48     state [1] = max ( 8U, state [0] * 69069U);
49     state [2] = max ( 16U, state [1] * 69069U);
50     state [3] = max (128U, state [2] * 69069U);
51 root 1.40
52     for (int i = 11; --i; )
53     operator ()();
54     }
55    
56     uint32_t
57     tausworthe_random_generator::next ()
58     {
59     state [0] = ((state [0] & 0xFFFFFFFEU) << 18U) ^ (((state [0] << 6U) ^ state [0]) >> 13U);
60     state [1] = ((state [1] & 0xFFFFFFF8U) << 2U) ^ (((state [1] << 2U) ^ state [1]) >> 27U);
61     state [2] = ((state [2] & 0xFFFFFFF0U) << 7U) ^ (((state [2] << 13U) ^ state [2]) >> 21U);
62     state [3] = ((state [3] & 0xFFFFFF80U) << 13U) ^ (((state [3] << 3U) ^ state [3]) >> 12U);
63    
64     return state [0] ^ state [1] ^ state [2] ^ state [3];
65     }
66    
67 root 1.42 uint32_t
68     tausworthe_random_generator::get_range (uint32_t r_max)
69     {
70     return next () % r_max;
71     }
72    
73     // return a number within (min .. max)
74     int
75     tausworthe_random_generator::get_range (int r_min, int r_max)
76     {
77     return r_min + (*this) (max (r_max - r_min + 1, 1));
78     }
79    
80 elmex 1.1 /*
81     * The random functions here take luck into account when rolling random
82     * dice or numbers. This function has less of an impact the larger the
83     * difference becomes in the random numbers. IE, the effect is lessened
84     * on a 1-1000 roll, vs a 1-6 roll. This can be used by crafty programmers,
85     * to specifically disable luck in certain rolls, simply by making the
86     * numbers larger (ie, 1d1000 > 500 vs 1d6 > 3)
87     */
88    
89     /*
90     * Roll a random number between min and max. Uses op to determine luck,
91     * and if goodbad is non-zero, luck increases the roll, if zero, it decreases.
92     * Generally, op should be the player/caster/hitter requesting the roll,
93     * not the recipient (ie, the poor slob getting hit). [garbled 20010916]
94     */
95 root 1.9 int
96 root 1.41 random_roll (int r_min, int r_max, const object *op, int goodbad)
97 root 1.9 {
98 root 1.41 int base = r_max - r_min > 1 ? 20 : 50; /* d2 and d3 are corner cases */
99 elmex 1.1
100 root 1.41 if (r_max < 1 || r_max < r_min)
101 root 1.9 {
102 root 1.41 LOG (llevError, "Calling random_roll with min=%d max=%d\n", r_min, r_max);
103     return r_min;
104 elmex 1.1 }
105    
106 root 1.41 if (op->type == PLAYER)
107 root 1.9 {
108 root 1.41 int luck = op->stats.luck;
109 elmex 1.1
110 root 1.41 if (rndm (base) < min (10, abs (luck)))
111     {
112     //TODO: take luck into account
113     }
114 elmex 1.1 }
115 root 1.41
116     return rndm (r_min, r_max);
117 elmex 1.1 }
118    
119     /*
120     * This is a 64 bit version of random_roll above. This is needed
121     * for exp loss calculations for players changing religions.
122     */
123 root 1.9 sint64
124     random_roll64 (sint64 min, sint64 max, const object *op, int goodbad)
125     {
126     sint64 omin, diff, luck, ran;
127     int base;
128    
129     omin = min;
130     diff = max - min + 1;
131     ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
132 elmex 1.1
133 root 1.9 if (max < 1 || diff < 1)
134     {
135 root 1.20 LOG (llevError, "Calling random_roll with min=%" PRId64 " max=%" PRId64 "\n", min, max);
136 root 1.9 return (min); /* avoids a float exception */
137 elmex 1.1 }
138    
139 root 1.45 /*
140     * Make a call to get two 32 bit unsigned random numbers, and just to
141     * a little bitshifting.
142 root 1.9 */
143 root 1.45 ran = (sint64) rndm.next () ^ ((sint64) rndm.next () << 31);
144 root 1.9
145     if (op->type != PLAYER)
146     return ((ran % diff) + min);
147    
148     luck = op->stats.luck;
149 root 1.45 if (rndm (base) < MIN (10, abs (luck)))
150 root 1.9 {
151     /* we have a winner */
152     ((luck > 0) ? (luck = 1) : (luck = -1));
153     diff -= luck;
154     if (diff < 1)
155     return (omin); /*check again */
156     ((goodbad) ? (min += luck) : (diff));
157 elmex 1.1
158 root 1.9 return (MAX (omin, MIN (max, (ran % diff) + min)));
159 elmex 1.1 }
160 root 1.45
161 root 1.9 return ((ran % diff) + min);
162 elmex 1.1 }
163    
164     /*
165     * Roll a number of dice (2d3, 4d6). Uses op to determine luck,
166     * If goodbad is non-zero, luck increases the roll, if zero, it decreases.
167     * Generally, op should be the player/caster/hitter requesting the roll,
168     * not the recipient (ie, the poor slob getting hit).
169     * The args are num D size (ie 4d6) [garbled 20010916]
170     */
171    
172 root 1.9 int
173     die_roll (int num, int size, const object *op, int goodbad)
174     {
175 root 1.45 int min, diff, luck, total, i, gotlucky, base;
176 root 1.9
177     diff = size;
178     min = 1;
179     luck = total = gotlucky = 0;
180     ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
181     if (size < 2 || diff < 1)
182     {
183     LOG (llevError, "Calling die_roll with num=%d size=%d\n", num, size);
184     return (num); /* avoids a float exception */
185     }
186 elmex 1.1
187 root 1.9 if (op->type == PLAYER)
188     luck = op->stats.luck;
189    
190     for (i = 0; i < num; i++)
191     {
192 root 1.45 if (rndm (base) < MIN (10, abs (luck)) && !gotlucky)
193 root 1.9 {
194     /* we have a winner */
195     gotlucky++;
196     ((luck > 0) ? (luck = 1) : (luck = -1));
197     diff -= luck;
198     if (diff < 1)
199     return (num); /*check again */
200     ((goodbad) ? (min += luck) : (diff));
201 root 1.45 total += MAX (1, MIN (size, rndm (diff) + min));
202 root 1.9 }
203     else
204 root 1.45 total += rndm (size) + 1;
205 elmex 1.1 }
206 root 1.45
207     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 root 1.47 int mat = op->materials;
257 root 1.35
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 root 1.47 name_to_material (const shstr &name)
282 elmex 1.1 {
283 root 1.47 for (materialtype_t *mt = materialt; mt && mt->next; mt = mt->next)
284     if (name == mt->name)
285     return mt;
286 elmex 1.1
287 root 1.47 return materialt;
288 elmex 1.1 }
289    
290     /* when doing transmutation of objects, we have to recheck the resistances,
291     * as some that did not apply previously, may apply now.
292     */
293    
294 root 1.9 void
295     transmute_materialname (object *op, const object *change)
296 elmex 1.1 {
297 root 1.9 materialtype_t *mt;
298     int j;
299 elmex 1.1
300 root 1.9 if (op->materialname == NULL)
301     return;
302 elmex 1.1
303 root 1.9 if (change->materialname != NULL && strcmp (op->materialname, change->materialname))
304     return;
305    
306 root 1.26 if (!op->is_armor ())
307 root 1.9 return;
308    
309     mt = name_to_material (op->materialname);
310     if (!mt)
311     {
312     LOG (llevError, "archetype '%s>%s' uses nonexistent material '%s'\n", &op->arch->name, &op->name, &op->materialname);
313     return;
314     }
315    
316     for (j = 0; j < NROFATTACKS; j++)
317     if (op->resist[j] == 0 && change->resist[j] != 0)
318     {
319     op->resist[j] += mt->mod[j];
320     if (op->resist[j] > 100)
321     op->resist[j] = 100;
322     if (op->resist[j] < -100)
323     op->resist[j] = -100;
324     }
325 elmex 1.1 }
326    
327     /* set the materialname and type for an item */
328 root 1.9 void
329     set_materialname (object *op, int difficulty, materialtype_t *nmt)
330 elmex 1.1 {
331 root 1.9 materialtype_t *mt, *lmt;
332    
333 pippijn 1.5 #ifdef NEW_MATERIAL_CODE
334 root 1.9 int j;
335 pippijn 1.5 #endif
336 elmex 1.1
337 root 1.9 if (op->materialname != NULL)
338     return;
339 elmex 1.1
340    
341    
342 root 1.9 if (nmt == NULL)
343     {
344     lmt = NULL;
345 elmex 1.1 #ifndef NEW_MATERIAL_CODE
346 root 1.47 for (mt = materialt; mt && mt->next; mt = mt->next)
347 root 1.9 {
348 root 1.47 if (op->materials & mt->material)
349 root 1.9 {
350     lmt = mt;
351     break;
352 root 1.2 }
353     }
354 elmex 1.1
355     #else
356 root 1.47 for (mt = materialt; mt && mt->next; mt = mt->next)
357 root 1.9 {
358 root 1.47 if (op->materials & mt->material && rndm (1, 100) <= mt->chance &&
359 root 1.9 difficulty >= mt->difficulty && (op->magic >= mt->magic || mt->magic == 0))
360     {
361     lmt = mt;
362 root 1.26 if (!(op->is_weapon () || op->is_armor ()))
363 root 1.9 break;
364 root 1.2 }
365     }
366 elmex 1.1 #endif
367 root 1.9 }
368     else
369     {
370     lmt = nmt;
371 elmex 1.1 }
372    
373 root 1.9 if (lmt != NULL)
374     {
375 elmex 1.1 #ifndef NEW_MATERIAL_CODE
376 root 1.9 op->materialname = lmt->name;
377     return;
378 elmex 1.1 #else
379    
380 root 1.26 if (op->stats.dam && op->is_weapon ())
381 root 1.9 {
382     op->stats.dam += lmt->damage;
383     if (op->stats.dam < 1)
384     op->stats.dam = 1;
385     }
386 root 1.46
387 root 1.9 if (op->stats.sp && op->type == BOW)
388     op->stats.sp += lmt->sp;
389 root 1.26 if (op->stats.wc && op->is_weapon ())
390 root 1.9 op->stats.wc += lmt->wc;
391 root 1.26 if (op->is_armor ())
392 root 1.9 {
393     if (op->stats.ac)
394     op->stats.ac += lmt->ac;
395     for (j = 0; j < NROFATTACKS; j++)
396     if (op->resist[j] != 0)
397     {
398     op->resist[j] += lmt->mod[j];
399     if (op->resist[j] > 100)
400     op->resist[j] = 100;
401     if (op->resist[j] < -100)
402     op->resist[j] = -100;
403     }
404     }
405 root 1.46 op->materialname = lmt->name;
406 root 1.9 /* dont make it unstackable if it doesn't need to be */
407 root 1.26 if (op->is_weapon () || op->is_armor ())
408 root 1.9 {
409     op->weight = (op->weight * lmt->weight) / 100;
410     op->value = (op->value * lmt->value) / 100;
411 root 1.2 }
412 elmex 1.1 #endif
413     }
414     }
415    
416     /*
417     * Strip out the media tags from a String.
418     * Warning the input string will contain the result string
419     */
420 root 1.9 void
421     strip_media_tag (char *message)
422     {
423     int in_tag = 0;
424     char *dest;
425     char *src;
426    
427     src = dest = message;
428     while (*src != '\0')
429     {
430     if (*src == '[')
431     {
432     in_tag = 1;
433     }
434     else if (in_tag && (*src == ']'))
435     in_tag = 0;
436     else if (!in_tag)
437     {
438     *dest = *src;
439     dest++;
440     }
441     src++;
442     }
443     *dest = '\0';
444     }
445    
446     const char *
447     strrstr (const char *haystack, const char *needle)
448     {
449     const char *lastneedle;
450    
451     lastneedle = NULL;
452     while ((haystack = strstr (haystack, needle)) != NULL)
453     {
454     lastneedle = haystack;
455     haystack++;
456 elmex 1.1 }
457 root 1.9 return lastneedle;
458    
459 elmex 1.1 }
460 root 1.9
461 elmex 1.1 #define EOL_SIZE (sizeof("\n")-1)
462 root 1.9 void
463     strip_endline (char *buf)
464     {
465     if (strlen (buf) < sizeof ("\n"))
466     {
467     return;
468 elmex 1.1 }
469 root 1.9 if (!strcmp (buf + strlen (buf) - EOL_SIZE, "\n"))
470     buf[strlen (buf) - EOL_SIZE] = '\0';
471 elmex 1.1 }
472    
473     /**
474     * Replace in string src all occurrences of key by replacement. The resulting
475     * string is put into result; at most resultsize characters (including the
476     * terminating null character) will be written to result.
477     */
478 root 1.9 void
479     replace (const char *src, const char *key, const char *replacement, char *result, size_t resultsize)
480 elmex 1.1 {
481 root 1.9 size_t resultlen;
482     size_t keylen;
483 elmex 1.1
484 root 1.9 /* special case to prevent infinite loop if key==replacement=="" */
485     if (strcmp (key, replacement) == 0)
486     {
487     snprintf (result, resultsize, "%s", src);
488     return;
489     }
490    
491     keylen = strlen (key);
492    
493     resultlen = 0;
494     while (*src != '\0' && resultlen + 1 < resultsize)
495     {
496     if (strncmp (src, key, keylen) == 0)
497 root 1.2 {
498 root 1.9 snprintf (result + resultlen, resultsize - resultlen, "%s", replacement);
499     resultlen += strlen (result + resultlen);
500     src += keylen;
501 root 1.2 }
502 root 1.9 else
503 root 1.2 {
504 root 1.9 result[resultlen++] = *src++;
505 root 1.2 }
506 root 1.9 }
507     result[resultlen] = '\0';
508 elmex 1.1 }
509    
510     /**
511     * Taking a string as an argument, mutate it into a string that looks like a list.
512     * a 'list' for the purposes here, is a string of items, seperated by commas, except
513     * for the last entry, which has an 'and' before it, and a full stop (period) after it.
514     * This function will also strip all trailing non alphanumeric characters.
515     * It does not insert an oxford comma.
516     */
517    
518 root 1.9 void
519     make_list_like (char *input)
520     {
521     char *p, tmp[MAX_BUF];
522     int i;
523    
524     if (!input || strlen (input) > MAX_BUF - 5)
525 elmex 1.1 return;
526 root 1.9 /* bad stuff would happen if we continued here, the -5 is to make space for ' and ' */
527    
528     strncpy (tmp, input, MAX_BUF - 5);
529     /*trim all trailing commas, spaces etc. */
530     for (i = strlen (tmp); !isalnum (tmp[i]) && i >= 0; i--)
531     tmp[i] = '\0';
532 root 1.11
533 root 1.9 strcat (tmp, ".");
534    
535     p = strrchr (tmp, ',');
536     if (p)
537     {
538     *p = '\0';
539     strcpy (input, tmp);
540     p++;
541     strcat (input, " and");
542     strcat (input, p);
543     }
544     else
545     strcpy (input, tmp);
546 root 1.11
547 root 1.9 return;
548 elmex 1.1 }
549 root 1.3
550 root 1.14 /////////////////////////////////////////////////////////////////////////////
551    
552 root 1.37 void
553     fork_abort (const char *msg)
554     {
555     if (!fork ())
556     {
557     signal (SIGABRT, SIG_DFL);
558     abort ();
559     }
560    
561 root 1.38 LOG (llevError, "fork abort: %s\n", msg);
562 root 1.37 }
563 root 1.38
564 root 1.25 void *salloc_ (int n) throw (std::bad_alloc)
565 root 1.10 {
566 root 1.44 #ifdef PREFER_MALLOC
567     void *ptr = malloc (n);
568     #else
569 root 1.25 void *ptr = g_slice_alloc (n);
570 root 1.44 #endif
571 root 1.13
572 root 1.23 if (!ptr)
573 root 1.13 throw std::bad_alloc ();
574 root 1.4
575 root 1.23 return ptr;
576     }
577    
578 root 1.25 void *salloc_ (int n, void *src) throw (std::bad_alloc)
579 root 1.23 {
580 root 1.25 void *ptr = salloc_ (n);
581 root 1.23
582 root 1.24 if (src)
583 root 1.25 memcpy (ptr, src, n);
584 root 1.24 else
585 root 1.25 memset (ptr, 0, n);
586 root 1.23
587     return ptr;
588 root 1.3 }
589 root 1.11
590     void assign (char *dst, const char *src, int maxlen)
591     {
592     if (!src)
593     src = "";
594    
595     int len = strlen (src);
596    
597     if (len >= maxlen - 1)
598     {
599     if (maxlen <= 4)
600     {
601     memset (dst, '.', maxlen - 1);
602     dst [maxlen - 1] = 0;
603     }
604     else
605     {
606     memcpy (dst, src, maxlen - 4);
607     memcpy (dst + maxlen - 4, "...", 4);
608     }
609     }
610     else
611     memcpy (dst, src, len + 1);
612     }
613    
614 root 1.23 tstamp now ()
615     {
616     struct timeval tv;
617    
618     gettimeofday (&tv, 0);
619     return tstamp (tv.tv_sec) + tstamp (tv.tv_usec) * tstamp (1e-6);
620     }
621 root 1.17
622 root 1.32 int
623     similar_direction (int a, int b)
624     {
625     if (!a || !b)
626     return 0;
627    
628     int diff = (b - a) & 7;
629     return diff <= 1 || diff >= 7;
630     }
631