Crossfire Server, Trunk  R20513
utils.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
26 #include "global.h"
27 
28 #include <ctype.h>
29 #include <math.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "sproto.h"
34 
42 int random_roll(int min, int max, const object *op, int goodbad) {
43  int omin, diff, luck, base, ran;
44 
45  omin = min;
46  diff = max-min+1;
47  ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
48 
49  if (max < 1 || diff < 1) {
50  LOG(llevError, "Calling random_roll with min=%d max=%d\n", min, max);
51  return(min); /* avoids a float exception */
52  }
53 
54  ran = RANDOM();
55 
56  if (op->type != PLAYER)
57  return((ran%diff)+min);
58 
59  luck = op->stats.luck;
60  if (RANDOM()%base < (unsigned int)MIN(10, abs(luck))) {
61  /* we have a winner */
62  ((luck > 0) ? (luck = 1) : (luck = -1));
63  diff -= luck;
64  if (diff < 1)
65  return(omin); /*check again*/
66  ((goodbad) ? (min += luck) : (diff));
67 
68  return(MAX(omin, MIN(max, (ran%diff)+min)));
69  }
70  return((ran%diff)+min);
71 }
72 
77 int64_t random_roll64(int64_t min, int64_t max, const object *op, int goodbad) {
78  int64_t omin, diff, luck, ran;
79  int base;
80 
81  omin = min;
82  diff = max-min+1;
83  ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
84 
85  if (max < 1 || diff < 1) {
86  LOG(llevError, "Calling random_roll with min=%"FMT64" max=%"FMT64"\n", min, max);
87  return(min); /* avoids a float exception */
88  }
89 
90  /* Don't know of a portable call to get 64 bit random values.
91  * So make a call to get two 32 bit random numbers, and just to
92  * a little byteshifting. Do make sure the first one is only
93  * 32 bit, so we don't get skewed results
94  */
95  ran = (RANDOM()&0xffffffff)|((int64_t)RANDOM()<<32);
96 
97  if (op->type != PLAYER)
98  return((ran%diff)+min);
99 
100  luck = op->stats.luck;
101  if (RANDOM()%base < (unsigned int)MIN(10, abs(luck))) {
102  /* we have a winner */
103  ((luck > 0) ? (luck = 1) : (luck = -1));
104  diff -= luck;
105  if (diff < 1)
106  return (omin); /*check again*/
107  ((goodbad) ? (min += luck) : (diff));
108 
109  return (MAX(omin, MIN(max, (ran%diff)+min)));
110  }
111  return ((ran%diff)+min);
112 }
113 
121 int die_roll(int num, int size, const object *op, int goodbad) {
122  int min, diff, luck, total, i, gotlucky, base, ran;
123 
124  diff = size;
125  min = 1;
126  luck = total = gotlucky = 0;
127  ((diff > 2) ? (base = 20) : (base = 50)); /* d2 and d3 are corner cases */
128  if (size < 2 || diff < 1) {
129  LOG(llevError, "Calling die_roll with num=%d size=%d\n", num, size);
130  return(num); /* avoids a float exception */
131  }
132 
133  if (op->type == PLAYER)
134  luck = op->stats.luck;
135 
136  for (i = 0; i < num; i++) {
137  if (RANDOM()%base < (unsigned int)MIN(10, abs(luck)) && !gotlucky) {
138  /* we have a winner */
139  gotlucky++;
140  ((luck > 0) ? (luck = 1) : (luck = -1));
141  diff -= luck;
142  if (diff < 1)
143  return(num); /*check again*/
144  ((goodbad) ? (min += luck) : (diff));
145  ran = RANDOM();
146  total += MAX(1, MIN(size, (ran%diff)+min));
147  } else {
148  total += RANDOM()%size+1;
149  }
150  }
151  return(total);
152 }
153 
161 int rndm(int min, int max) {
162  int diff;
163 
164  diff = max-min+1;
165  if (max < 1 || diff < 1)
166  return (min);
167 
168  return (RANDOM()%diff+min);
169 }
170 
175  int x, y, destroy;
176 
177  if (m->unique)
178  return;
179 
180  for (x = 0; x < MAP_WIDTH(m); x++)
181  for (y = 0; y < MAP_HEIGHT(m); y++)
182  FOR_MAP_PREPARE(m, x, y, op) {
183  destroy = 0;
185  break;
186  if (QUERY_FLAG(op, FLAG_IS_FLOOR)
188  || QUERY_FLAG(op, FLAG_UNIQUE)
190  || QUERY_FLAG(op, FLAG_UNPAID)
191  || IS_LIVE(op))
192  continue;
193  if (op->head)
194  /* Don't try to remove a non head part of a multipart object, object_remove() would abort(). */
195  continue;
196  /* otherwise, we decay and destroy */
197  if (IS_WEAPON(op)) {
198  op->stats.dam--;
199  if (op->stats.dam < 0)
200  destroy = 1;
201  } else if (IS_ARMOR(op)
202  || IS_SHIELD(op)
203  || op->type == GIRDLE
204  || op->type == GLOVES
205  || op->type == CLOAK) {
206  op->stats.ac--;
207  if (op->stats.ac < 0)
208  destroy = 1;
209  } else if (op->type == FOOD) {
210  op->stats.food -= rndm(5, 20);
211  if (op->stats.food < 0)
212  destroy = 1;
213  } else {
214  if (op->material&M_PAPER
215  || op->material&M_LEATHER
216  || op->material&M_WOOD
217  || op->material&M_ORGANIC
218  || op->material&M_CLOTH
219  || op->material&M_LIQUID)
220  destroy = 1;
221  if (op->material&M_IRON && rndm(1, 5) == 1)
222  destroy = 1;
223  if (op->material&M_GLASS && rndm(1, 2) == 1)
224  destroy = 1;
225  if ((op->material&M_STONE || op->material&M_ADAMANT) && rndm(1, 10) == 1)
226  destroy = 1;
227  if ((op->material&M_SOFT_METAL || op->material&M_BONE)
228  && rndm(1, 3) == 1)
229  destroy = 1;
230  if (op->material&M_ICE && rndm(0, 100) > 70)
231  destroy = 1;
232  }
233  /* adjust overall chance below */
234  if (destroy && rndm(0, 1)) {
235  object_remove(op);
237  }
238  } FOR_MAP_FINISH();
239 }
240 
247 materialtype_t *name_to_material(const char *name) {
248  materialtype_t *mt, *nmt;
249 
250  mt = NULL;
251  for (nmt = materialt; nmt != NULL && nmt->next != NULL; nmt = nmt->next) {
252  if (strcmp(name, nmt->name) == 0) {
253  mt = nmt;
254  break;
255  }
256  }
257  return mt;
258 }
259 
266 void transmute_materialname(object *op, const object *change) {
267  materialtype_t *mt;
268  int j;
269 
270  if (op->materialname == NULL)
271  return;
272 
273  if (change->materialname != NULL
274  && strcmp(op->materialname, change->materialname))
275  return;
276 
277  if (!(IS_ARMOR(op) || IS_SHIELD(op) || op->type == GIRDLE || op->type == GLOVES || op->type == CLOAK))
278  return;
279 
280  mt = name_to_material(op->materialname);
281  if (!mt) {
282  LOG(llevError, "archetype '%s>%s' uses nonexistent material '%s'\n", op->arch->name, op->name, op->materialname);
283  return;
284  }
285 
286  for (j = 0; j < NROFATTACKS; j++)
287  if (op->resist[j] == 0 && change->resist[j] != 0) {
288  op->resist[j] += mt->mod[j];
289  if (op->resist[j] > 100)
290  op->resist[j] = 100;
291  if (op->resist[j] < -100)
292  op->resist[j] = -100;
293  }
294 }
295 
300 void set_materialname(object *op) {
301  materialtype_t *mt;
302 
303  if (op->materialname != NULL)
304  return;
305 
306  for (mt = materialt; mt != NULL; mt = mt->next) {
307  if (op->material&mt->material) {
308  break;
309  }
310  }
311 
312  if (mt != NULL) {
313  op->materialname = add_string(mt->name);
314  return;
315  }
316 }
317 
324 const char *strrstr(const char *haystack, const char *needle) {
325  const char *lastneedle;
326 
327  lastneedle = NULL;
328  while ((haystack = strstr(haystack, needle)) != NULL) {
329  lastneedle = haystack;
330  haystack++;
331  }
332  return lastneedle;
333 }
334 
335 #define EOL_SIZE (sizeof("\n")-1)
336 
340 void strip_endline(char *buf) {
341  if (strlen(buf) < sizeof("\n")) {
342  return;
343  }
344  if (!strcmp(buf+strlen(buf)-EOL_SIZE, "\n"))
345  buf[strlen(buf)-EOL_SIZE] = '\0';
346 }
347 
353 void replace(const char *src, const char *key, const char *replacement, char *result, size_t resultsize) {
354  size_t resultlen;
355  size_t keylen;
356 
357  /* special case to prevent infinite loop if key==replacement=="" */
358  if (strcmp(key, replacement) == 0) {
359  snprintf(result, resultsize, "%s", src);
360  return;
361  }
362 
363  keylen = strlen(key);
364 
365  resultlen = 0;
366  while (*src != '\0' && resultlen+1 < resultsize) {
367  if (strncmp(src, key, keylen) == 0) {
368  snprintf(result+resultlen, resultsize-resultlen, "%s", replacement);
369  resultlen += strlen(result+resultlen);
370  src += keylen;
371  } else {
372  result[resultlen++] = *src++;
373  }
374  }
375  result[resultlen] = '\0';
376 }
377 
394 void make_list_like(char *input) {
395  char *p, tmp[MAX_BUF];
396  int i;
397  if (!input || strlen(input) > MAX_BUF-5)
398  return;
399  /* bad stuff would happen if we continued here, the -5 is to make space for ' and ' */
400 
401  strncpy(tmp, input, MAX_BUF-5);
402  /*trim all trailing commas, spaces etc.*/
403  for (i = strlen(tmp); i >= 0 && !isalnum(tmp[i]); i--) {
404  tmp[i] = '\0';
405  }
406  strcat(tmp, ".");
407 
408  p = strrchr(tmp, ',');
409  if (p) {
410  *p = '\0';
411  strcpy(input, tmp);
412  p++;
413  strcat(input, " and");
414  strcat(input, p);
415  } else
416  strcpy(input, tmp);
417  return;
418 }
419 
426 int get_random_dir(void) {
427  return rndm(1, 8);
428 }
429 
438 int get_randomized_dir(int dir) {
439  return absdir(dir+RANDOM()%3+RANDOM()%3-2);
440 }
441 
452 int adjust_dir(int dir, int destination_dir) {
453  int diff;
454 
455  diff = (destination_dir-dir)&7;
456  if (1 <= diff && diff <= 3)
457  dir++;
458  else if (5 <= diff && diff <= 7)
459  dir--;
460  else if (rndm(0, 1) == 0)
461  dir++;
462  else
463  dir--;
464  return absdir(dir);
465 }
466 
473 void replace_unprintable_chars(char *buf) {
474  char *p;
475 
476  for (p = buf; *p != '\0'; p++) {
477  if (*p < ' ') {
478  *p = ' ';
479  }
480  }
481 }
482 
499 size_t split_string(char *str, char *array[], size_t array_size, char sep) {
500  char *p;
501  size_t pos;
502 
503  if (array_size <= 0)
504  return 0;
505 
506  if (*str == '\0') {
507  array[0] = str;
508  return 1;
509  }
510 
511  pos = 0;
512  p = str;
513  while (pos < array_size) {
514  array[pos++] = p;
515  while (*p != '\0' && *p != sep)
516  p++;
517  if (pos >= array_size)
518  break;
519  if (*p != sep)
520  break;
521  *p++ = '\0';
522  }
523  return pos;
524 }
525 
533 StringBuffer *describe_spellpath_attenuation(const char *attenuation, int value, StringBuffer *buf) {
534  if (buf == NULL)
535  buf = stringbuffer_new();
536 
537  if (value) {
538  int i, j = 0;
539  stringbuffer_append_printf(buf, "(%s: ", attenuation);
540  for (i = 0; i < NRSPELLPATHS; i++)
541  if (value&(1<<i)) {
542  if (j)
543  stringbuffer_append_string(buf, ", ");
544  else
545  j = 1;
547  }
548  stringbuffer_append_string(buf, ")");
549  }
550 
551  return buf;
552 }
553 
561 StringBuffer *describe_attacktype(const char *attack, int value, StringBuffer *buf) {
562  if (buf == NULL)
563  buf = stringbuffer_new();
564 
565  if (value) {
566  int i, j = 0;
567  stringbuffer_append_printf(buf, "(%s: ", attack);
568  for (i = 0; i < NROFATTACKS; i++)
569  if (value&(1<<i)) {
570  if (j)
571  stringbuffer_append_string(buf, ", ");
572  else
573  j = 1;
575  }
576  stringbuffer_append_string(buf, ")");
577  }
578 
579  return buf;
580 }
581 
585 int isqrt(int n) {
586  return (int)sqrt(n);
587 }
588 
596 void fatal(enum fatal_error err) {
597  const char *fatalmsgs[] = {
598  "Failed to allocate memory",
599  "Failed repeatedly to load maps",
600  "Hashtable for archetypes is too small",
601  "Fatal issue in archetype file",
602  "See last error",
603  };
604 
605  fprintf(logfile, "Fatal error: %s\n", fatalmsgs[err]);
606  emergency_save(0);
607  clean_tmp_files();
608  fprintf(logfile, "Exiting...\n");
609  exit(err);
610 }
int64_t random_roll64(int64_t min, int64_t max, const object *op, int goodbad)
This is a 64 bit version of random_roll() above.
Definition: utils.c:77
#define M_BONE
Bone.
Definition: material.h:25
EXTERN FILE * logfile
Used by server/daemon.c.
Definition: global.h:144
Error, serious thing.
Definition: logger.h:11
void clean_tmp_files(void)
Remove temporary map files.
Definition: main.c:336
#define M_ICE
Ice.
Definition: material.h:26
#define M_STONE
Stone.
Definition: material.h:20
#define FLAG_IS_FLOOR
Can&#39;t see what&#39;s underneath this object.
Definition: define.h:303
#define FLAG_UNPAID
Object hasn&#39;t been paid for yet.
Definition: define.h:236
int8_t mod[NROFATTACKS]
Modification to resistances.
Definition: material.h:38
const char * strrstr(const char *haystack, const char *needle)
Finds a string in a string.
Definition: utils.c:324
materialtype_t * name_to_material(const char *name)
Convert materialname to materialtype_t.
Definition: utils.c:247
const char * name
Name of the material.
Definition: material.h:34
EXTERN materialtype_t * materialt
Material types.
Definition: material.h:43
uint16_t material
What materials this object consist of.
Definition: object.h:347
See Cloak.
Definition: object.h:204
See Food.
Definition: object.h:112
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.c:596
StringBuffer * stringbuffer_new(void)
Create a new string buffer.
Definition: stringbuffer.c:57
#define MAP_HEIGHT(m)
Map height.
Definition: map.h:80
int isqrt(int n)
Compute the square root.
Definition: utils.c:585
#define IS_WEAPON(op)
Definition: define.h:162
#define M_ADAMANT
Adamant.
Definition: material.h:22
void decay_objects(mapstruct *m)
Decay and destroy persihable items in a map.
Definition: utils.c:174
#define M_CLOTH
Cloth.
Definition: material.h:21
int material
What basic type(s) it is linked to.
Definition: material.h:36
See Girdle.
Definition: object.h:223
#define MAX(x, y)
Definition: compat.h:20
void strip_endline(char *buf)
Removes endline from buffer (modified in place).
Definition: utils.c:340
Global type definitions and header inclusions.
int absdir(int d)
Computes an absolute direction.
Definition: object.c:3637
void transmute_materialname(object *op, const object *change)
When doing transmutation of objects, we have to recheck the resistances, as some that did not apply p...
Definition: utils.c:266
const char *const spellpathnames[NRSPELLPATHS]
Perhaps not the best place for this, but needs to be in some file in the common area so that standalo...
Definition: init.c:120
#define MIN(x, y)
Definition: compat.h:17
int adjust_dir(int dir, int destination_dir)
Adjusts a given direction by +/-1 towards a destination direction.
Definition: utils.c:452
#define M_SOFT_METAL
Soft metal.
Definition: material.h:24
int rndm(int min, int max)
Returns a number between min and max.
Definition: utils.c:161
void make_list_like(char *input)
Taking a string as an argument, mutate it into a string that looks like a list.
Definition: utils.c:394
const char * destination_dir
Root destination dir.
void object_free_drop_inventory(object *ob)
Frees everything allocated by an object, removes it from the list of used objects, and puts it on the list of free objects.
Definition: object.c:1368
#define EOL_SIZE
Definition: utils.c:335
int get_randomized_dir(int dir)
Returns a random direction (1..8) similar to a given direction.
Definition: utils.c:438
#define FLAG_OBJ_ORIGINAL
NEVER SET THIS.
Definition: define.h:365
void stringbuffer_append_string(StringBuffer *sb, const char *str)
Append a string to a string buffer instance.
Definition: stringbuffer.c:95
#define M_LIQUID
Liquid.
Definition: material.h:23
const char * materialname
Specific material name.
Definition: object.h:346
uint32_t unique
If set, this is a per player unique map.
Definition: map.h:337
#define snprintf
Definition: win32.h:46
#define FMT64
Definition: compat.h:12
int die_roll(int num, int size, const object *op, int goodbad)
Roll a number of dice (2d3, 4d6).
Definition: utils.c:121
const char * name
The name of the object, obviously...
Definition: object.h:311
#define M_GLASS
Glass.
Definition: material.h:16
One material type.
Definition: material.h:33
#define FLAG_OVERLAY_FLOOR
Object is an overlay floor.
Definition: define.h:255
int get_random_dir(void)
Returns a random direction (1..8).
Definition: utils.c:426
#define M_ORGANIC
General organic.
Definition: material.h:19
int8_t luck
Affects thaco and ac from time to time.
Definition: living.h:38
#define IS_LIVE(op)
Definition: define.h:172
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
void emergency_save(int flag)
Save all players.
Definition: main.c:333
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
#define NRSPELLPATHS
Number of spell paths.
Definition: spells.h:40
#define IS_SHIELD(op)
Definition: define.h:169
StringBuffer * describe_attacktype(const char *attack, int value, StringBuffer *buf)
Describe the specified attack type.
Definition: utils.c:561
void replace_unprintable_chars(char *buf)
Replaces any unprintable character in the given buffer with a space.
Definition: utils.c:473
#define M_IRON
Iron.
Definition: material.h:15
signed __int64 int64_t
Definition: win32.h:168
#define FOR_MAP_FINISH()
Finishes FOR_MAP_PREPARE().
Definition: define.h:765
int16_t resist[NROFATTACKS]
Resistance adjustments for attacks.
Definition: object.h:341
See Player.
Definition: object.h:107
void replace(const char *src, const char *key, const char *replacement, char *result, size_t resultsize)
Replace in string src all occurrences of key by replacement.
Definition: utils.c:353
#define RANDOM()
Definition: define.h:679
living stats
Str, Con, Dex, etc.
Definition: object.h:368
struct archt * arch
Pointer to archetype.
Definition: object.h:412
#define MAP_WIDTH(m)
Map width.
Definition: map.h:78
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:338
#define NROFATTACKS
Definition: attack.h:17
sstring add_string(const char *str)
This will add &#39;str&#39; to the hash table.
Definition: shstr.c:124
void stringbuffer_append_printf(StringBuffer *sb, const char *format,...)
Append a formatted string to a string buffer instance.
Definition: stringbuffer.c:104
StringBuffer * describe_spellpath_attenuation(const char *attenuation, int value, StringBuffer *buf)
Describe the specified path attenuation.
Definition: utils.c:533
#define M_PAPER
Paper.
Definition: material.h:14
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Splits a string delimited by passed in sep value into characters into an array of strings...
Definition: utils.c:499
#define M_LEATHER
Leather.
Definition: material.h:17
See Gloves.
Definition: object.h:213
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
struct _materialtype * next
Next item on the list.
Definition: material.h:39
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Constructs a loop iterating over all objects of a map tile.
Definition: define.h:758
#define IS_ARMOR(op)
Definition: define.h:165
void set_materialname(object *op)
Set the material name and type for an item, if not set.
Definition: utils.c:300
A buffer that will be expanded as content is added to it.
Definition: stringbuffer.c:25
This is a game-map.
Definition: map.h:325
int random_roll(int min, int max, const object *op, int goodbad)
Roll a random number between min and max.
Definition: utils.c:42
#define FLAG_UNIQUE
Item is really unique (UNIQUE_ITEMS)
Definition: define.h:288
#define M_WOOD
Wood.
Definition: material.h:18
fatal_error
Fatal variables; used as arguments to fatal().
Definition: define.h:47
const char * name
More definite name, like "generate_kobold".
Definition: object.h:466
void object_remove(object *op)
This function removes the object op from the linked list of objects which it is currently tied to...
Definition: object.c:1654
const char *const attacks[NROFATTACKS]
Attack type names.
Definition: living.c:129