Crossfire Server, Trunk
region.cpp
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 #include <map>
32 #include <string>
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 
125  if (first_region == NULL) {
126  return NULL;
127  }
128 
129  if (*name == '\0') {
130  for (reg = first_region; reg->parent != NULL; reg = reg->parent)
131  ;
132  return reg;
133  }
134 
135  for (reg = first_region; reg != NULL; reg = reg->next)
136  if (!strcasecmp(reg->name, name))
137  return reg;
138 
139  for (reg = first_region; reg != NULL; reg = reg->next)
140  if (reg->longname != NULL) {
141  if (!strcasecmp(reg->longname, name))
142  return reg;
143  }
144 
145  substr = NULL;
146  for (reg = first_region; reg != NULL; reg = reg->next)
147  if (reg->longname != NULL) {
148  substr = strstr(reg->longname, name);
149  if (substr != NULL)
150  return reg;
151  }
152  for (reg = first_region; reg != NULL; reg = reg->next)
153  if (reg->longname != NULL) {
154  /*
155  * This is not a bug, we want the region that is most identifiably a discrete
156  * area in the game, eg if we have 'scor', we want to return 'scorn' and not
157  * 'scornarena', regardless of their order on the list so we only look at those
158  * regions with a longname set.
159  */
160  substr = strstr(reg->name, name);
161  if (substr != NULL)
162  return reg;
163  }
164  for (reg = first_region; reg != NULL; reg = reg->next) {
165  substr = strstr(reg->name, name);
166  if (substr != NULL)
167  return reg;
168  }
169  /* if we are still here, we are going to have to give up, and give the top level region */
170  for (reg = first_region; reg->parent != NULL; reg = reg->parent)
171  ;
172  return reg;
173 }
174 
187 int region_is_child_of_region(const region *child, const region *r) {
188  if (r == NULL)
189  return -1;
190  if (child == NULL)
191  return 0;
192  if (!strcmp(child->name, r->name))
193  return 1;
194  else if (child->parent != NULL)
195  return region_is_child_of_region(child->parent, r);
196  else
197  return 0;
198 }
199 
214 const char *get_region_longname(const region *r) {
215  if (r->longname != NULL)
216  return r->longname;
217  else if (r->parent != NULL)
218  return get_region_longname(r->parent);
219  else {
220  LOG(llevDebug, "NOTICE region %s has no parent and no longname.\n", r->name);
221  return "no name can be found for the current region";
222  }
223 }
224 
235 const char *get_region_msg(const region *r) {
236  if (r->msg != NULL)
237  return r->msg;
238  else if (r->parent != NULL)
239  return get_region_msg(r->parent);
240  else {
241  LOG(llevDebug, "NOTICE region %s has no parent and no msg.\n", r->name);
242  return "no description can be found for the current region";
243  }
244 }
245 
257 object *get_jail_exit(object *op) {
258  region *reg, *orig;
259  object *exit;
260 
261  if (op->type != PLAYER) {
262  LOG(llevError, "region.c: get_jail_exit called against non-player object.\n");
263  return NULL;
264  }
265 
266  reg = get_region_by_map(op->map);
267  orig = reg;
268  while (reg != NULL) {
269  if (reg->jailmap) {
270  exit = object_new();
271  EXIT_PATH(exit) = add_string(reg->jailmap);
272  /* damned exits reset savebed and remove teleports, so the prisoner can't escape */
273  SET_FLAG(exit, FLAG_DAMNED);
274  EXIT_X(exit) = reg->jailx;
275  EXIT_Y(exit) = reg->jaily;
276  return exit;
277  } else
278  reg = reg->parent;
279  }
280  LOG(llevDebug, "No suitable jailmap for region %s was found.\n", orig ? orig->name : "(unknown region?)");
281  return NULL;
282 }
283 
299  region *add = (region *)calloc(1, sizeof(region));
300  if (add == NULL)
302 
303  return add;
304 }
305 
315 void init_regions(BufferReader *reader, const char *filename) {
316  region *add;
317  region *reg;
318  char *buf;
319 
320  char msgbuf[HUGE_BUF], *value;
321  int msgpos = 0;
322 
324  if (first_region != NULL) /* Only do this once */
325  return;
326 
327  std::map<region *, std::string> parents;
328 
329  add = NULL;
330  while ((buf = bufferreader_next_line(reader)) != NULL) {
331  while (isspace(*buf))
332  buf++;
333  if (*buf == 0)
334  continue; /* empty line */
335  value = strchr(buf, ' ');
336  if (value) {
337  *value = 0;
338  value++;
339  while (isspace(*value))
340  value++;
341  }
342 
343  /*
344  * This is a bizzare mutated form of the map and archetype parser
345  * rolled into one. Key is the field name, value is what it should
346  * be set to.
347  * We've already done the work to null terminate key,
348  * and strip off any leading spaces for both of these.
349  * We have not touched the newline at the end of the line -
350  * these might be needed for some values. the end pointer
351  * points to the first of the newlines.
352  * value could be NULL! It would be easy enough to just point
353  * this to "" to prevent cores, but that would let more errors slide
354  * through.
355  */
356  if (!strcmp(buf, "region")) {
357  add = get_region_struct();
358  add->name = strdup_local(value);
359  } else if (!strcmp(buf, "parent")) {
360  /*
361  * Note that this is in the initialisation code, so we don't actually
362  * assign the pointer to the parent yet, because it might not have been
363  * parsed.
364  */
365 
366  if (!add) {
367  LOG(llevError, "region.c: malformated regions file: \"parent\" before \"region\".\n");
369  }
370  if (!value) {
371  LOG(llevError, "region.c: malformated regions file: No value given for \"parent\" key.\n");
373  }
374  parents[add] = value;
375  } else if (!strcmp(buf, "longname")) {
376  if (!add) {
377  LOG(llevError, "region.c: malformated regions file: \"longname\" before \"region\".\n");
379  }
380  if (!value) {
381  LOG(llevError, "region.c: malformated regions file: No value given for \"longname\" key.\n");
383  }
384  add->longname = strdup_local(value);
385  } else if (!strcmp(buf, "jail")) {
386  /* jail entries are of the form: /path/to/map x y */
387  char path[MAX_BUF];
388  int x, y;
389 
390  if (!add) {
391  LOG(llevError, "region.c: malformated regions file: \"jail\" before \"region\".\n");
393  }
394  if (!value) {
395  LOG(llevError, "region.c: malformated regions file: No value given for \"jail\" key.\n");
397  }
398 
399  if (sscanf(value, "%[^ ] %d %d\n", path, &x, &y) != 3) {
400  LOG(llevError, "region.c: malformated regions entry: jail %s\n", value);
401  continue;
402  }
403  add->jailmap = strdup_local(path);
404  add->jailx = x;
405  add->jaily = y;
406  } else if (!strcmp(buf, "msg")) {
407  if (!add) {
408  LOG(llevError, "region.c: malformated regions file: \"msg\" before \"region\".\n");
410  }
411  char *other;
412  while ((other = bufferreader_next_line(reader)) != NULL) {
413  while (isspace(*other))
414  other++;
415  if (strcmp(other, "endmsg") == 0)
416  break;
417  else {
418  strcpy(msgbuf+msgpos, other);
419  msgpos += strlen(other);
420  strcpy(msgbuf+msgpos, "\n");
421  ++msgpos;
422  }
423  }
424  /*
425  * There may be regions with empty messages (eg, msg/endmsg
426  * with nothing between). When maps are loaded, this is done
427  * so better do it here too...
428  */
429  if (msgpos != 0)
430  add->msg = strdup_local(msgbuf);
431 
432  /* we have to reset msgpos, or the next region will store both msg blocks.*/
433  msgpos = 0;
434  } else if (!strcmp(buf, "fallback")) {
435  if (!add) {
436  LOG(llevError, "region.c: malformated regions file %s: \"fallback\" before \"region\".\n", filename);
438  }
439  if (!value) {
440  LOG(llevError, "region.c: malformated regions file %s: No value given for \"fallback\" key.\n", filename);
442  }
443  add->fallback = atoi(value);
444  } else if (!strcmp(buf, "end")) {
445  if (!add) {
446  LOG(llevError, "region.c: Ignoring spurious \"end\" between regions.\n");
447  continue;
448  }
449  /* Place this add region last on the list, if the list is empty put it first */
450  for (reg = first_region; reg != NULL && reg->next != NULL; reg = reg->next)
451  ;
452 
453  if (reg == NULL)
454  first_region = add;
455  else
456  reg->next = add;
457  add = NULL;
458  } else if (!strcmp(buf, "nomore")) {
459  if (add) {
460  LOG(llevError, "region.c: Last region not properly closed.\n");
461  free(add);
462  }
463  /* we have reached the end of the region specs....*/
464  break;
465  } else {
466  /* we should never get here, if we have, then something is wrong */
467  LOG(llevError, "Got unknown value in region file %s: %s %s\n", filename, buf, value);
468  }
469  }
470  if (!buf || strcmp(buf, "nomore")) {
471  LOG(llevError, "Got premature eof on regions file %s!\n", filename);
472  free(add);
473  }
474 
475  for (auto p : parents) {
476  p.first->parent = get_region_by_name(p.second.c_str());
477  if (!p.first->parent) {
478  LOG(llevError, "Couldn't find parent %s for region %s\n", p.second.c_str(), p.first->name);
479  }
480  }
481 }
PLAYER
@ PLAYER
Definition: object.h:110
get_name_of_region_for_map
const char * get_name_of_region_for_map(const mapstruct *m)
Definition: region.cpp:92
global.h
first_region
region * first_region
Definition: init.cpp:108
llevError
@ llevError
Definition: logger.h:11
get_region_struct
region * get_region_struct(void)
Definition: region.cpp:298
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:51
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
get_region_by_name
region * get_region_by_name(const char *region_name)
Definition: region.cpp:46
strdup_local
#define strdup_local
Definition: compat.h:29
diamondslots.x
x
Definition: diamondslots.py:15
init_regions
void init_regions(BufferReader *reader, const char *filename)
Definition: region.cpp:315
region_is_child_of_region
int region_is_child_of_region(const region *child, const region *r)
Definition: region.cpp:187
EXIT_PATH
#define EXIT_PATH(xyz)
Definition: define.h:439
regiondef::jailx
int16_t jailx
Definition: map.h:290
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
buf
StringBuffer * buf
Definition: readable.cpp:1611
get_region_longname
const char * get_region_longname(const region *r)
Definition: region.cpp:214
HUGE_BUF
#define HUGE_BUF
Definition: define.h:37
regiondef::next
struct regiondef * next
Definition: map.h:276
m
static event_registration m
Definition: citylife.cpp:425
regiondef::jaily
int16_t jaily
Definition: map.h:290
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
get_region_msg
const char * get_region_msg(const region *r)
Definition: region.cpp:235
python_init.path
path
Definition: python_init.py:8
regiondef::name
char * name
Definition: map.h:277
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
mapdef
Definition: map.h:317
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:580
EXIT_X
#define EXIT_X(xyz)
Definition: define.h:441
MAX_BUF
#define MAX_BUF
Definition: define.h:35
object_new
object * object_new(void)
Definition: object.cpp:1249
llevInfo
@ llevInfo
Definition: logger.h:12
get_jail_exit
object * get_jail_exit(object *op)
Definition: region.cpp:257
regiondef::parent
struct regiondef * parent
Definition: map.h:278
regiondef::jailmap
char * jailmap
Definition: map.h:289
give.op
op
Definition: give.py:33
autojail.value
value
Definition: autojail.py:6
EXIT_Y
#define EXIT_Y(xyz)
Definition: define.h:442
diamondslots.y
y
Definition: diamondslots.py:16
get_region_from_string
region * get_region_from_string(const char *name)
Definition: region.cpp:121
strcasecmp
int strcasecmp(const char *s1, const char *s2)
get_region_by_map
region * get_region_by_map(mapstruct *m)
Definition: region.cpp:74
regiondef::fallback
int8_t fallback
Definition: map.h:287
regiondef::msg
char * msg
Definition: map.h:285
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
regiondef::longname
char * longname
Definition: map.h:283
BufferReader
Definition: bufferreader.cpp:21
ring_occidental_mages.r
r
Definition: ring_occidental_mages.py:6
llevDebug
@ llevDebug
Definition: logger.h:13
give.name
name
Definition: give.py:27
bufferreader_next_line
char * bufferreader_next_line(BufferReader *br)
Definition: bufferreader.cpp:102
regiondef
Definition: map.h:275