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  for (auto reg : all_regions)
48  if (!strcmp(reg->name, region_name))
49  return reg;
50 
51  for (auto reg : all_regions) {
52  if (reg->fallback) {
53  LOG(llevDebug, "region called %s requested, but not found, fallback used.\n", region_name);
54  return reg;
55  }
56  }
57  LOG(llevInfo, "Got no region or fallback for region %s.\n", region_name);
58  return NULL;
59 }
60 
74 }
75 
90 const char *get_name_of_region_for_map(const mapstruct *m) {
91  if (m->region != NULL)
92  return m->region->name;
93  for (auto reg : all_regions) {
94  if (reg->fallback)
95  return reg->name;
96  }
97  LOG(llevInfo, "map %s had no region and I couldn't find a fallback to use.\n", m->name);
98  return "unknown";
99 }
100 
118  region *reg;
119  char *substr;
120 
121  if (all_regions.empty()) {
122  return NULL;
123  }
124 
125  if (*name == '\0') {
126  for (reg = all_regions.front(); reg->parent != NULL; reg = reg->parent)
127  ;
128  return reg;
129  }
130 
131  for (auto reg : all_regions)
132  if (!strcasecmp(reg->name, name))
133  return reg;
134 
135  for (auto reg : all_regions)
136  if (reg->longname != NULL) {
137  if (!strcasecmp(reg->longname, name))
138  return reg;
139  }
140 
141  substr = NULL;
142  for (auto reg : all_regions)
143  if (reg->longname != NULL) {
144  substr = strstr(reg->longname, name);
145  if (substr != NULL)
146  return reg;
147  }
148  for (auto reg : all_regions)
149  if (reg->longname != NULL) {
150  /*
151  * This is not a bug, we want the region that is most identifiably a discrete
152  * area in the game, eg if we have 'scor', we want to return 'scorn' and not
153  * 'scornarena', regardless of their order on the list so we only look at those
154  * regions with a longname set.
155  */
156  substr = strstr(reg->name, name);
157  if (substr != NULL)
158  return reg;
159  }
160  for (auto reg : all_regions) {
161  substr = strstr(reg->name, name);
162  if (substr != NULL)
163  return reg;
164  }
165  /* if we are still here, we are going to have to give up, and give the top level region */
166  for (reg = all_regions.front(); reg->parent != NULL; reg = reg->parent)
167  ;
168  return reg;
169 }
170 
183 int region_is_child_of_region(const region *child, const region *r) {
184  if (r == NULL)
185  return -1;
186  if (child == NULL)
187  return 0;
188  if (!strcmp(child->name, r->name))
189  return 1;
190  else if (child->parent != NULL)
191  return region_is_child_of_region(child->parent, r);
192  else
193  return 0;
194 }
195 
210 const char *get_region_longname(const region *r) {
211  if (r->longname != NULL)
212  return r->longname;
213  else if (r->parent != NULL)
214  return get_region_longname(r->parent);
215  else {
216  LOG(llevDebug, "NOTICE region %s has no parent and no longname.\n", r->name);
217  return "no name can be found for the current region";
218  }
219 }
220 
231 const char *get_region_msg(const region *r) {
232  if (r->msg != NULL)
233  return r->msg;
234  else if (r->parent != NULL)
235  return get_region_msg(r->parent);
236  else {
237  LOG(llevDebug, "NOTICE region %s has no parent and no msg.\n", r->name);
238  return "no description can be found for the current region";
239  }
240 }
241 
253 object *get_jail_exit(object *op) {
254  region *reg, *orig;
255  object *exit;
256 
257  if (op->type != PLAYER) {
258  LOG(llevError, "region.c: get_jail_exit called against non-player object.\n");
259  return NULL;
260  }
261 
262  reg = get_region_by_map(op->map);
263  orig = reg;
264  while (reg != NULL) {
265  if (reg->jailmap) {
266  exit = object_new();
267  EXIT_PATH(exit) = add_string(reg->jailmap);
268  /* damned exits reset savebed and remove teleports, so the prisoner can't escape */
269  SET_FLAG(exit, FLAG_DAMNED);
270  EXIT_X(exit) = reg->jailx;
271  EXIT_Y(exit) = reg->jaily;
272  return exit;
273  } else
274  reg = reg->parent;
275  }
276  LOG(llevDebug, "No suitable jailmap for region %s was found.\n", orig ? orig->name : "(unknown region?)");
277  return NULL;
278 }
279 
295  region *add = (region *)calloc(1, sizeof(region));
296  if (add == NULL)
298 
299  return add;
300 }
301 
311 void init_regions(BufferReader *reader, const char *filename) {
312  region *add;
313  char *buf;
314 
315  char msgbuf[HUGE_BUF], *value;
316  int msgpos = 0;
317 
319  if (!all_regions.empty()) /* Only do this once */
320  return;
321 
322  std::map<region *, std::string> parents;
323 
324  add = NULL;
325  while ((buf = bufferreader_next_line(reader)) != NULL) {
326  while (isspace(*buf))
327  buf++;
328  if (*buf == 0)
329  continue; /* empty line */
330  value = strchr(buf, ' ');
331  if (value) {
332  *value = 0;
333  value++;
334  while (isspace(*value))
335  value++;
336  }
337 
338  /*
339  * This is a bizzare mutated form of the map and archetype parser
340  * rolled into one. Key is the field name, value is what it should
341  * be set to.
342  * We've already done the work to null terminate key,
343  * and strip off any leading spaces for both of these.
344  * We have not touched the newline at the end of the line -
345  * these might be needed for some values. the end pointer
346  * points to the first of the newlines.
347  * value could be NULL! It would be easy enough to just point
348  * this to "" to prevent cores, but that would let more errors slide
349  * through.
350  */
351  if (!strcmp(buf, "region")) {
352  add = get_region_struct();
353  add->name = strdup_local(value);
354  } else if (!strcmp(buf, "parent")) {
355  /*
356  * Note that this is in the initialisation code, so we don't actually
357  * assign the pointer to the parent yet, because it might not have been
358  * parsed.
359  */
360 
361  if (!add) {
362  LOG(llevError, "region.c: malformated regions file: \"parent\" before \"region\".\n");
364  }
365  if (!value) {
366  LOG(llevError, "region.c: malformated regions file: No value given for \"parent\" key.\n");
368  }
369  parents[add] = value;
370  } else if (!strcmp(buf, "longname")) {
371  if (!add) {
372  LOG(llevError, "region.c: malformated regions file: \"longname\" before \"region\".\n");
374  }
375  if (!value) {
376  LOG(llevError, "region.c: malformated regions file: No value given for \"longname\" key.\n");
378  }
379  add->longname = strdup_local(value);
380  } else if (!strcmp(buf, "jail")) {
381  /* jail entries are of the form: /path/to/map x y */
382  char path[MAX_BUF];
383  int x, y;
384 
385  if (!add) {
386  LOG(llevError, "region.c: malformated regions file: \"jail\" before \"region\".\n");
388  }
389  if (!value) {
390  LOG(llevError, "region.c: malformated regions file: No value given for \"jail\" key.\n");
392  }
393 
394  if (sscanf(value, "%[^ ] %d %d\n", path, &x, &y) != 3) {
395  LOG(llevError, "region.c: malformated regions entry: jail %s\n", value);
396  continue;
397  }
398  add->jailmap = strdup_local(path);
399  add->jailx = x;
400  add->jaily = y;
401  } else if (!strcmp(buf, "msg")) {
402  if (!add) {
403  LOG(llevError, "region.c: malformated regions file: \"msg\" before \"region\".\n");
405  }
406  char *other;
407  while ((other = bufferreader_next_line(reader)) != NULL) {
408  while (isspace(*other))
409  other++;
410  if (strcmp(other, "endmsg") == 0)
411  break;
412  else {
413  strcpy(msgbuf+msgpos, other);
414  msgpos += strlen(other);
415  strcpy(msgbuf+msgpos, "\n");
416  ++msgpos;
417  }
418  }
419  /*
420  * There may be regions with empty messages (eg, msg/endmsg
421  * with nothing between). When maps are loaded, this is done
422  * so better do it here too...
423  */
424  if (msgpos != 0)
425  add->msg = strdup_local(msgbuf);
426 
427  /* we have to reset msgpos, or the next region will store both msg blocks.*/
428  msgpos = 0;
429  } else if (!strcmp(buf, "fallback")) {
430  if (!add) {
431  LOG(llevError, "region.c: malformated regions file %s: \"fallback\" before \"region\".\n", filename);
433  }
434  if (!value) {
435  LOG(llevError, "region.c: malformated regions file %s: No value given for \"fallback\" key.\n", filename);
437  }
438  add->fallback = atoi(value);
439  } else if (!strcmp(buf, "end")) {
440  if (!add) {
441  LOG(llevError, "region.c: Ignoring spurious \"end\" between regions.\n");
442  continue;
443  }
444  all_regions.push_back(add);
445  add = NULL;
446  } else if (!strcmp(buf, "nomore")) {
447  if (add) {
448  LOG(llevError, "region.c: Last region not properly closed.\n");
449  free(add);
450  }
451  /* we have reached the end of the region specs....*/
452  break;
453  } else {
454  /* we should never get here, if we have, then something is wrong */
455  LOG(llevError, "Got unknown value in region file %s: %s %s\n", filename, buf, value);
456  }
457  }
458  if (!buf || strcmp(buf, "nomore")) {
459  LOG(llevError, "Got premature eof on regions file %s!\n", filename);
460  free(add);
461  }
462 
463  for (auto p : parents) {
464  p.first->parent = get_region_by_name(p.second.c_str());
465  if (!p.first->parent) {
466  LOG(llevError, "Couldn't find parent %s for region %s\n", p.second.c_str(), p.first->name);
467  }
468  }
469 }
470 
472  if (all_regions.empty()) {
473  return nullptr;
474  }
475  if (!reg) {
476  return all_regions.front();
477  }
478 
479  auto r = std::find(all_regions.begin(), all_regions.end(), reg);
480  if (r == all_regions.end() || (++r) == all_regions.end()) {
481  return nullptr;
482  }
483  return *r;
484 }
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:90
global.h
llevError
@ llevError
Definition: logger.h:11
get_region_struct
region * get_region_struct(void)
Definition: region.cpp:294
region::fallback
int8_t fallback
Definition: map.h:286
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:311
region_is_child_of_region
int region_is_child_of_region(const region *child, const region *r)
Definition: region.cpp:183
EXIT_PATH
#define EXIT_PATH(xyz)
Definition: define.h:439
msgbuf
static char msgbuf[HUGE_BUF]
Definition: loader.c:2079
SEE_LAST_ERROR
@ SEE_LAST_ERROR
Definition: define.h:52
region::name
char * name
Definition: map.h:276
npc_dialog.filename
filename
Definition: npc_dialog.py:99
buf
StringBuffer * buf
Definition: readable.cpp:1551
get_region_longname
const char * get_region_longname(const region *r)
Definition: region.cpp:210
HUGE_BUF
#define HUGE_BUF
Definition: define.h:37
region::longname
char * longname
Definition: map.h:282
m
static event_registration m
Definition: citylife.cpp:425
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
region::parent
region * parent
Definition: map.h:277
get_region_msg
const char * get_region_msg(const region *r)
Definition: region.cpp:231
region::msg
char * msg
Definition: map.h:284
python_init.path
path
Definition: python_init.py:8
region::jailmap
char * jailmap
Definition: map.h:288
region_get_next
region * region_get_next(region *reg)
Definition: region.cpp:471
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
fatal
void fatal(enum fatal_error err)
Definition: utils.cpp:570
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:1273
region
Definition: map.h:275
llevInfo
@ llevInfo
Definition: logger.h:12
all_regions
std::vector< region * > all_regions
Definition: init.cpp:108
get_jail_exit
object * get_jail_exit(object *op)
Definition: region.cpp:253
mapstruct
Definition: map.h:316
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:117
strcasecmp
int strcasecmp(const char *s1, const char *s2)
get_region_by_map
region * get_region_by_map(mapstruct *m)
Definition: region.cpp:72
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
BufferReader
Definition: bufferreader.cpp:21
ring_occidental_mages.r
r
Definition: ring_occidental_mages.py:6
llevDebug
@ llevDebug
Definition: logger.h:13
region::jaily
int16_t jaily
Definition: map.h:289
region::jailx
int16_t jailx
Definition: map.h:289
give.name
name
Definition: give.py:27
bufferreader_next_line
char * bufferreader_next_line(BufferReader *br)
Definition: bufferreader.cpp:102