Crossfire Server, Trunk
region.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 
21 #include "global.h"
22 
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #ifndef WIN32 /* ---win32 exclude header */
28 #include <unistd.h>
29 #endif /* win32 */
30 
31 static void parse_regions(FILE *fp);
32 static void assign_region_parents(void);
33 
46 region *get_region_by_name(const char *region_name) {
47  region *reg;
48 
49  for (reg = first_region; reg != NULL; reg = reg->next)
50  if (!strcmp(reg->name, region_name))
51  return reg;
52 
53  for (reg = first_region; reg != NULL; reg = reg->next) {
54  if (reg->fallback) {
55  LOG(llevDebug, "region called %s requested, but not found, fallback used.\n", region_name);
56  return reg;
57  }
58  }
59  LOG(llevInfo, "Got no region or fallback for region %s.\n", region_name);
60  return NULL;
61 }
62 
76 }
77 
92 const char *get_name_of_region_for_map(const mapstruct *m) {
93  region *reg;
94 
95  if (m->region != NULL)
96  return m->region->name;
97  for (reg = first_region; reg != NULL; reg = reg->next) {
98  if (reg->fallback)
99  return reg->name;
100  }
101  LOG(llevInfo, "map %s had no region and I couldn't find a fallback to use.\n", m->name);
102  return "unknown";
103 }
104 
122  region *reg;
123  char *substr;
124  char *p;
125 
126  if (first_region == NULL) {
127  return NULL;
128  }
129 
130  if (*name == '\0') {
131  for (reg = first_region; reg->parent != NULL; reg = reg->parent)
132  ;
133  return reg;
134  }
135  p = strchr(name, '\n');
136  if (p)
137  *p = '\0';
138  for (reg = first_region; reg != NULL; reg = reg->next)
139  if (!strcasecmp(reg->name, name))
140  return reg;
141 
142  for (reg = first_region; reg != NULL; reg = reg->next)
143  if (reg->longname != NULL) {
144  if (!strcasecmp(reg->longname, name))
145  return reg;
146  }
147 
148  substr = NULL;
149  for (reg = first_region; reg != NULL; reg = reg->next)
150  if (reg->longname != NULL) {
151  substr = strstr(reg->longname, name);
152  if (substr != NULL)
153  return reg;
154  }
155  for (reg = first_region; reg != NULL; reg = reg->next)
156  if (reg->longname != NULL) {
157  /*
158  * This is not a bug, we want the region that is most identifiably a discrete
159  * area in the game, eg if we have 'scor', we want to return 'scorn' and not
160  * 'scornarena', regardless of their order on the list so we only look at those
161  * regions with a longname set.
162  */
163  substr = strstr(reg->name, name);
164  if (substr != NULL)
165  return reg;
166  }
167  for (reg = first_region; reg != NULL; reg = reg->next) {
168  substr = strstr(reg->name, name);
169  if (substr != NULL)
170  return reg;
171  }
172  /* if we are still here, we are going to have to give up, and give the top level region */
173  for (reg = first_region; reg->parent != NULL; reg = reg->parent)
174  ;
175  return reg;
176 }
177 
190 int region_is_child_of_region(const region *child, const region *r) {
191  if (r == NULL)
192  return -1;
193  if (child == NULL)
194  return 0;
195  if (!strcmp(child->name, r->name))
196  return 1;
197  else if (child->parent != NULL)
198  return region_is_child_of_region(child->parent, r);
199  else
200  return 0;
201 }
202 
217 const char *get_region_longname(const region *r) {
218  if (r->longname != NULL)
219  return r->longname;
220  else if (r->parent != NULL)
221  return get_region_longname(r->parent);
222  else {
223  LOG(llevDebug, "NOTICE region %s has no parent and no longname.\n", r->name);
224  return "no name can be found for the current region";
225  }
226 }
227 
238 const char *get_region_msg(const region *r) {
239  if (r->msg != NULL)
240  return r->msg;
241  else if (r->parent != NULL)
242  return get_region_msg(r->parent);
243  else {
244  LOG(llevDebug, "NOTICE region %s has no parent and no msg.\n", r->name);
245  return "no description can be found for the current region";
246  }
247 }
248 
260 object *get_jail_exit(object *op) {
261  region *reg, *orig;
262  object *exit;
263 
264  if (op->type != PLAYER) {
265  LOG(llevError, "region.c: get_jail_exit called against non-player object.\n");
266  return NULL;
267  }
268 
269  reg = get_region_by_map(op->map);
270  orig = reg;
271  while (reg != NULL) {
272  if (reg->jailmap) {
273  exit = object_new();
274  EXIT_PATH(exit) = add_string(reg->jailmap);
275  /* damned exits reset savebed and remove teleports, so the prisoner can't escape */
276  SET_FLAG(exit, FLAG_DAMNED);
277  EXIT_X(exit) = reg->jailx;
278  EXIT_Y(exit) = reg->jaily;
279  return exit;
280  } else
281  reg = reg->parent;
282  }
283  LOG(llevDebug, "No suitable jailmap for region %s was found.\n", orig ? orig->name : "(unknown region?)");
284  return NULL;
285 }
286 
293 int init_regions(void) {
294  FILE *fp;
295  char filename[MAX_BUF];
296 
297  if (first_region != NULL) /* Only do this once */
298  return 0;
299 
300  snprintf(filename, sizeof(filename), "%s/%s/%s", settings.datadir, settings.mapdir, settings.regions);
301  if ((fp = fopen(filename, "r")) == NULL) {
302  LOG(llevError, "Couldn't read regions file from \"%s\".\n", filename);
303  return 1;
304  }
305  parse_regions(fp);
307 
308  fclose(fp);
309  return 0;
310 }
311 
327  region *new = (region *)calloc(1, sizeof(region));
328  if (new == NULL)
330 
331  return new;
332 }
333 
341 static void parse_regions(FILE *fp) {
342  region *new;
343  region *reg;
344 
345  char buf[HUGE_BUF], msgbuf[HUGE_BUF], *key = NULL, *value, *end;
346  int msgpos = 0;
347 
348  new = NULL;
349  while (fgets(buf, HUGE_BUF-1, fp) != NULL) {
350  buf[HUGE_BUF-1] = 0;
351  key = buf;
352  while (isspace(*key))
353  key++;
354  if (*key == 0)
355  continue; /* empty line */
356  value = strchr(key, ' ');
357  if (!value) {
358  end = strchr(key, '\n');
359  *end = 0;
360  } else {
361  *value = 0;
362  value++;
363  /* isspace() includes newline. To avoid crash on empty line further
364  * down we must check for it here.
365  */
366  while (isspace(*value) && *value != '\n')
367  value++;
368  end = strchr(value, '\n');
369  }
370 
371  /*
372  * This is a bizzare mutated form of the map and archetype parser
373  * rolled into one. Key is the field name, value is what it should
374  * be set to.
375  * We've already done the work to null terminate key,
376  * and strip off any leading spaces for both of these.
377  * We have not touched the newline at the end of the line -
378  * these might be needed for some values. the end pointer
379  * points to the first of the newlines.
380  * value could be NULL! It would be easy enough to just point
381  * this to "" to prevent cores, but that would let more errors slide
382  * through.
383  */
384  if (!strcmp(key, "region")) {
385  *end = 0;
386  new = get_region_struct();
387  new->name = strdup_local(value);
388  } else if (!strcmp(key, "parent")) {
389  /*
390  * Note that this is in the initialisation code, so we don't actually
391  * assign the pointer to the parent yet, because it might not have been
392  * parsed.
393  */
394  *end = 0;
395 
396  if (!new) {
397  LOG(llevError, "region.c: malformated regions file: \"parent\" before \"region\".\n");
399  }
400  if (!value) {
401  LOG(llevError, "region.c: malformated regions file: No value given for \"parent\" key.\n");
403  }
404  new->parent_name = strdup_local(value);
405  } else if (!strcmp(key, "longname")) {
406  *end = 0;
407  if (!new) {
408  LOG(llevError, "region.c: malformated regions file: \"longname\" before \"region\".\n");
410  }
411  if (!value) {
412  LOG(llevError, "region.c: malformated regions file: No value given for \"longname\" key.\n");
414  }
415  new->longname = strdup_local(value);
416  } else if (!strcmp(key, "jail")) {
417  /* jail entries are of the form: /path/to/map x y */
418  char path[MAX_BUF];
419  int x, y;
420 
421  if (!new) {
422  LOG(llevError, "region.c: malformated regions file: \"jail\" before \"region\".\n");
424  }
425  if (!value) {
426  LOG(llevError, "region.c: malformated regions file: No value given for \"jail\" key.\n");
428  }
429 
430  if (sscanf(value, "%[^ ] %d %d\n", path, &x, &y) != 3) {
431  LOG(llevError, "region.c: malformated regions entry: jail %s\n", value);
432  continue;
433  }
434  new->jailmap = strdup_local(path);
435  new->jailx = x;
436  new->jaily = y;
437  } else if (!strcmp(key, "msg")) {
438  if (!new) {
439  LOG(llevError, "region.c: malformated regions file: \"msg\" before \"region\".\n");
441  }
442  while (fgets(buf, HUGE_BUF-1, fp) != NULL) {
443  key = buf;
444  while (isspace(*key))
445  key++;
446  if (strcmp(key, "endmsg\n") == 0)
447  break;
448  else {
449  strcpy(msgbuf+msgpos, key);
450  msgpos += strlen(key);
451  }
452  }
453  /*
454  * There may be regions with empty messages (eg, msg/endmsg
455  * with nothing between). When maps are loaded, this is done
456  * so better do it here too...
457  */
458  if (msgpos != 0)
459  new->msg = strdup_local(msgbuf);
460 
461  /* we have to reset msgpos, or the next region will store both msg blocks.*/
462  msgpos = 0;
463  } else if (!strcmp(key, "fallback")) {
464  *end = 0;
465  if (!new) {
466  LOG(llevError, "region.c: malformated regions file: \"fallback\" before \"region\".\n");
468  }
469  if (!value) {
470  LOG(llevError, "region.c: malformated regions file: No value given for \"fallback\" key.\n");
472  }
473  new->fallback = atoi(value);
474  } else if (!strcmp(key, "end")) {
475  if (!new) {
476  LOG(llevError, "region.c: Ignoring spurious \"end\" between regions.\n");
477  continue;
478  }
479  /* Place this new region last on the list, if the list is empty put it first */
480  for (reg = first_region; reg != NULL && reg->next != NULL; reg = reg->next)
481  ;
482 
483  if (reg == NULL)
484  first_region = new;
485  else
486  reg->next = new;
487  new = NULL;
488  } else if (!strcmp(key, "nomore")) {
489  if (new) {
490  LOG(llevError, "region.c: Last region not properly closed.\n");
491  free(new);
492  }
493  /* we have reached the end of the region specs....*/
494  break;
495  } else {
496  /* we should never get here, if we have, then something is wrong */
497  LOG(llevError, "Got unknown value in region file: %s %s\n", key, value);
498  }
499  }
500  if (!key || strcmp(key, "nomore")) {
501  LOG(llevError, "Got premature eof on regions file!\n");
502  free(new);
503  }
504 }
505 
509 static void assign_region_parents(void) {
510  region *reg;
511  uint32_t parent_count = 0;
512  uint32_t region_count = 0;
513 
514  for (reg = first_region; reg != NULL; reg = reg->next) {
515  if (reg->parent_name != NULL) {
517  if (reg->parent == NULL) {
518  LOG(llevError, "Couldn't find parent %s for region %s\n", reg->name, reg->parent_name);
519  }
520  parent_count++;
521  }
522  region_count++;
523  }
524  LOG(llevDebug, "regions: loaded %u with %u parents\n",
525  region_count, parent_count);
526 }
PLAYER
@ PLAYER
Definition: object.h:107
Settings::mapdir
const char * mapdir
Definition: global.h:248
global.h
add_string
sstring add_string(const char *str)
Definition: shstr.c:124
llevError
@ llevError
Definition: logger.h:11
get_region_from_string
region * get_region_from_string(const char *name)
Definition: region.c:121
Settings::regions
const char * regions
Definition: global.h:249
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
strdup_local
#define strdup_local
Definition: compat.h:29
diamondslots.x
x
Definition: diamondslots.py:15
object_new
object * object_new(void)
Definition: object.c:1255
Settings::datadir
const char * datadir
Definition: global.h:245
EXIT_PATH
#define EXIT_PATH(xyz)
Definition: define.h:439
get_name_of_region_for_map
const char * get_name_of_region_for_map(const mapstruct *m)
Definition: region.c:92
python_event.path
path
Definition: python_event.py:11
regiondef::jailx
int16_t jailx
Definition: map.h:297
get_region_longname
const char * get_region_longname(const region *r)
Definition: region.c:217
init_regions
int init_regions(void)
Definition: region.c:293
msgbuf
static char msgbuf[HUGE_BUF]
Definition: loader.c:2079
SEE_LAST_ERROR
@ SEE_LAST_ERROR
Definition: define.h:52
npc_dialog.filename
filename
Definition: npc_dialog.py:99
parse_regions
static void parse_regions(FILE *fp)
Definition: region.c:341
regiondef::parent_name
char * parent_name
Definition: map.h:278
HUGE_BUF
#define HUGE_BUF
Definition: define.h:37
settings
struct Settings settings
Definition: init.c:39
regiondef::next
struct regiondef * next
Definition: map.h:276
m
static event_registration m
Definition: citylife.cpp:427
regiondef::jaily
int16_t jaily
Definition: map.h:297
fatal
void fatal(enum fatal_error err)
Definition: utils.c:580
get_region_by_name
region * get_region_by_name(const char *region_name)
Definition: region.c:46
get_jail_exit
object * get_jail_exit(object *op)
Definition: region.c:260
regiondef::name
char * name
Definition: map.h:277
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
mapdef
Definition: map.h:324
EXIT_X
#define EXIT_X(xyz)
Definition: define.h:441
MAX_BUF
#define MAX_BUF
Definition: define.h:35
llevInfo
@ llevInfo
Definition: logger.h:12
get_region_by_map
region * get_region_by_map(mapstruct *m)
Definition: region.c:74
regiondef::parent
struct regiondef * parent
Definition: map.h:285
regiondef::jailmap
char * jailmap
Definition: map.h:296
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
give.op
op
Definition: give.py:33
autojail.value
value
Definition: autojail.py:6
get_region_struct
region * get_region_struct(void)
Definition: region.c:326
assign_region_parents
static void assign_region_parents(void)
Definition: region.c:509
EXIT_Y
#define EXIT_Y(xyz)
Definition: define.h:442
diamondslots.y
y
Definition: diamondslots.py:16
region_count
static size_t region_count
Definition: mapper.cpp:363
buf
StringBuffer * buf
Definition: readable.c:1610
strcasecmp
int strcasecmp(const char *s1, const char *s2)
castle_read.key
key
Definition: castle_read.py:64
get_region_msg
const char * get_region_msg(const region *r)
Definition: region.c:238
regiondef::fallback
int8_t fallback
Definition: map.h:294
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
first_region
EXTERN region * first_region
Definition: global.h:117
regiondef::longname
char * longname
Definition: map.h:290
ring_occidental_mages.r
r
Definition: ring_occidental_mages.py:6
region_is_child_of_region
int region_is_child_of_region(const region *child, const region *r)
Definition: region.c:190
llevDebug
@ llevDebug
Definition: logger.h:13
give.name
name
Definition: give.py:27
regiondef
Definition: map.h:275