ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/utils.C
Revision: 1.36
Committed: Sat Jan 6 14:42:29 2007 UTC (17 years, 4 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.35: +1 -0 lines
Log Message:
added some copyrights

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