Crossfire Server, Branches 1.12  R18729
region.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_map_c =
3  * "$Id: region.c 11578 2009-02-23 22:02:27Z lalo $";
4  */
5 
6 /*
7  CrossFire, A Multiplayer game for X-windows
8 
9  Copyright (C) 2001-2003 Mark Wedel & Crossfire Development Team
10  Copyright (C) 1992 Frank Tore Johansen
11 
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, write to the Free Software
24  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 
26  The authors can be reached via e-mail at crossfire-devel@real-time.com
27 */
28 
36 #include <global.h>
37 
38 #ifndef WIN32 /* ---win32 exclude header */
39 #include <unistd.h>
40 #endif /* win32 */
41 
42 static void parse_regions(FILE *fp);
43 static void assign_region_parents(void);
44 
57 region *get_region_by_name(const char *region_name) {
58  region *reg;
59 
60  for (reg = first_region; reg != NULL; reg = reg->next)
61  if (!strcmp(reg->name, region_name))
62  return reg;
63 
64  for (reg = first_region; reg != NULL; reg = reg->next) {
65  if (reg->fallback) {
66  LOG(llevDebug, "region called %s requested, but not found, fallback used.\n", region_name);
67  return reg;
68  }
69  }
70  LOG(llevInfo, "Got no region or fallback for region %s.\n", region_name);
71  return NULL;
72 }
73 
87 }
88 
103 const char *get_name_of_region_for_map(const mapstruct *m) {
104  region *reg;
105 
106  if (m->region != NULL)
107  return m->region->name;
108  for (reg = first_region; reg != NULL; reg = reg->next) {
109  if (reg->fallback)
110  return reg->name;
111  }
112  LOG(llevInfo, "map %s had no region and I couldn't find a fallback to use.\n", m->name);
113  return "unknown";
114 }
115 
132 region *get_region_from_string(const char *name) {
133  region *reg;
134  char *substr;
135  char *p;
136 
137  if (first_region == NULL) {
138  return NULL;
139  }
140 
141  if (name == NULL) {
142  for (reg = first_region; reg->parent != NULL; reg = reg->parent)
143  ;
144  return reg;
145  }
146  p = strchr(name, '\n');
147  if (p)
148  *p = '\0';
149  for (reg = first_region; reg != NULL; reg = reg->next)
150  if (!strcasecmp(reg->name, name))
151  return reg;
152 
153  for (reg = first_region; reg != NULL; reg = reg->next)
154  if (reg->longname != NULL) {
155  if (!strcasecmp(reg->longname, name))
156  return reg;
157  }
158 
159  substr = NULL;
160  for (reg = first_region; reg != NULL; reg = reg->next)
161  if (reg->longname != NULL) {
162  substr = strstr(reg->longname, name);
163  if (substr != NULL)
164  return reg;
165  }
166  for (reg = first_region; reg != NULL; reg = reg->next)
167  if (reg->longname != NULL) {
168  /*
169  * This is not a bug, we want the region that is most identifiably a discrete
170  * area in the game, eg if we have 'scor', we want to return 'scorn' and not
171  * 'scornarena', regardless of their order on the list so we only look at those
172  * regions with a longname set.
173  */
174  substr = strstr(reg->name, name);
175  if (substr != NULL)
176  return reg;
177  }
178  for (reg = first_region; reg != NULL; reg = reg->next) {
179  substr = strstr(reg->name, name);
180  if (substr != NULL)
181  return reg;
182  }
183  /* if we are still here, we are going to have to give up, and give the top level region */
184  for (reg = first_region; reg->parent != NULL; reg = reg->parent)
185  ;
186  return reg;
187 }
188 
201 int region_is_child_of_region(const region *child, const region *r) {
202 
203  if (r == NULL)
204  return -1;
205  if (child == NULL)
206  return 0;
207  if (!strcmp(child->name, r->name))
208  return 1;
209  else if (child->parent != NULL)
210  return region_is_child_of_region(child->parent, r);
211  else
212  return 0;
213 }
214 
229 const char *get_region_longname(const region *r) {
230  if (r->longname != NULL)
231  return r->longname;
232  else if (r->parent != NULL)
233  return get_region_longname(r->parent);
234  else {
235  LOG(llevDebug, "NOTICE region %s has no parent and no longname.\n", r->name);
236  return "no name can be found for the current region";
237  }
238 }
239 
250 const char *get_region_msg(const region *r) {
251  if (r->msg != NULL)
252  return r->msg;
253  else if (r->parent != NULL)
254  return get_region_msg(r->parent);
255  else {
256  LOG(llevDebug, "NOTICE region %s has no parent and no msg.\n", r->name);
257  return "no description can be found for the current region";
258  }
259 }
260 
272 object *get_jail_exit(object *op) {
273  region *reg;
274  object *exit;
275 
276  if (op->type != PLAYER) {
277  LOG(llevError, "region.c: get_jail_exit called against non-player object.\n");
278  return NULL;
279  }
280 
281  reg = get_region_by_map(op->map);
282  while (reg != NULL) {
283  if (reg->jailmap) {
284  exit = get_object();
285  EXIT_PATH(exit) = add_string(reg->jailmap);
286  /* damned exits reset savebed and remove teleports, so the prisoner can't escape */
287  SET_FLAG(exit, FLAG_DAMNED);
288  EXIT_X(exit) = reg->jailx;
289  EXIT_Y(exit) = reg->jaily;
290  return exit;
291  } else
292  reg = reg->parent;
293  }
294  LOG(llevDebug, "No suitable jailmap for region %s was found.\n", reg->name);
295  return NULL;
296 }
297 
301 void init_regions(void) {
302  FILE *fp;
303  char filename[MAX_BUF];
304  int comp;
305 
306  if (first_region != NULL) /* Only do this once */
307  return;
308 
309  snprintf(filename, sizeof(filename), "%s/%s/%s", settings.datadir, settings.mapdir, settings.regions);
310  LOG(llevDebug, "Reading regions from %s...\n", filename);
311  if ((fp = open_and_uncompress(filename, 0, &comp)) == NULL) {
312  LOG(llevError, " Can't open regions file %s in init_regions.\n", filename);
313  return;
314  }
315  parse_regions(fp);
317  LOG(llevDebug, " done\n");
318 
319  close_and_delete(fp, comp);
320 }
321 
337  region *new;
338 
339  new = (region *)CALLOC(1, sizeof(region));
340  if (new == NULL)
342 
343  memset(new, '\0', sizeof(region));
344 
345  return new;
346 }
347 
355 static void parse_regions(FILE *fp) {
356  region *new;
357  region *reg;
358 
359  char buf[HUGE_BUF], msgbuf[HUGE_BUF], *key = NULL, *value, *end;
360  int msgpos = 0;
361 
362  new = NULL;
363  while (fgets(buf, HUGE_BUF-1, fp) != NULL) {
364  buf[HUGE_BUF-1] = 0;
365  key = buf;
366  while (isspace(*key))
367  key++;
368  if (*key == 0)
369  continue; /* empty line */
370  value = strchr(key, ' ');
371  if (!value) {
372  end = strchr(key, '\n');
373  *end = 0;
374  } else {
375  *value = 0;
376  value++;
377  while (isspace(*value))
378  value++;
379  end = strchr(value, '\n');
380  }
381 
382  /*
383  * This is a bizzare mutated form of the map and archetype parser
384  * rolled into one. Key is the field name, value is what it should
385  * be set to.
386  * We've already done the work to null terminate key,
387  * and strip off any leading spaces for both of these.
388  * We have not touched the newline at the end of the line -
389  * these might be needed for some values. the end pointer
390  * points to the first of the newlines.
391  * value could be NULL! It would be easy enough to just point
392  * this to "" to prevent cores, but that would let more errors slide
393  * through.
394  */
395  if (!strcmp(key, "region")) {
396  *end = 0;
397  new = get_region_struct();
398  new->name = strdup_local(value);
399  } else if (!strcmp(key, "parent")) {
400  /*
401  * Note that this is in the initialisation code, so we don't actually
402  * assign the pointer to the parent yet, because it might not have been
403  * parsed.
404  */
405  *end = 0;
406  new->parent_name = strdup_local(value);
407  } else if (!strcmp(key, "longname")) {
408  *end = 0;
409  new->longname = strdup_local(value);
410  } else if (!strcmp(key, "jail")) {
411  /* jail entries are of the form: /path/to/map x y */
412  char path[MAX_BUF];
413  int x, y;
414 
415  if (sscanf(value, "%[^ ] %d %d\n", path, &x, &y) != 3) {
416  LOG(llevError, "region.c: malformated regions entry: jail %s\n", value);
417  continue;
418  }
419  new->jailmap = strdup_local(path);
420  new->jailx = x;
421  new->jaily = y;
422  } else if (!strcmp(key, "msg")) {
423  while (fgets(buf, HUGE_BUF-1, fp) != NULL) {
424  if (!strcmp(buf, "endmsg\n"))
425  break;
426  else {
427  strcpy(msgbuf+msgpos, buf);
428  msgpos += strlen(buf);
429  }
430  }
431  /*
432  * There may be regions with empty messages (eg, msg/endmsg
433  * with nothing between). When maps are loaded, this is done
434  * so better do it here too...
435  */
436  if (msgpos != 0)
437  new->msg = strdup_local(msgbuf);
438 
439  /* we have to reset msgpos, or the next region will store both msg blocks.*/
440  msgpos = 0;
441  } else if (!strcmp(key, "fallback")) {
442  *end = 0;
443  new->fallback = atoi(value);
444  } else if (!strcmp(key, "end")) {
445  /* Place this new region last on the list, if the list is empty put it first */
446  for (reg = first_region; reg != NULL && reg->next != NULL; reg = reg->next)
447  ;
448 
449  if (reg == NULL)
450  first_region = new;
451  else
452  reg->next = new;
453  new = NULL;
454  } else if (!strcmp(key, "nomore")) {
455  /* we have reached the end of the region specs....*/
456  break;
457  } else {
458  /* we should never get here, if we have, then something is wrong */
459  LOG(llevError, "Got unknown value in region file: %s %s\n", key, value);
460  }
461  }
462  if (!key || strcmp(key, "nomore"))
463  LOG(llevError, "Got premature eof on regions file!\n");
464 }
465 
469 static void assign_region_parents(void) {
470  region *reg;
471  uint32 parent_count = 0;
472  uint32 region_count = 0;
473 
474  for (reg = first_region; reg != NULL && reg->next != NULL; reg = reg->next) {
475  if (reg->parent_name != NULL) {
477  parent_count++;
478  }
479  region_count++;
480  }
481  LOG(llevDebug, "Assigned %u regions with %u parents.\n", region_count, parent_count);
482 }
#define FLAG_DAMNED
Definition: define.h:614
sint16 jailx
Definition: map.h:319
#define OUT_OF_MEMORY
Definition: define.h:94
#define CALLOC(x, y)
Definition: global.h:295
#define SET_FLAG(xyz, p)
Definition: define.h:510
region * get_region_by_name(const char *region_name)
Definition: region.c:57
int region_is_child_of_region(const region *child, const region *r)
Definition: region.c:201
#define HUGE_BUF
Definition: define.h:83
const char * regions
Definition: global.h:339
region * get_region_by_map(mapstruct *m)
Definition: region.c:85
sint8 fallback
Definition: map.h:316
void close_and_delete(FILE *fp, int compressed)
Definition: porting.c:748
const char * parent_name
Definition: map.h:300
object * get_jail_exit(object *op)
Definition: region.c:272
#define PLAYER
Definition: define.h:113
region * get_region_from_string(const char *name)
Definition: region.c:132
const char * get_region_longname(const region *r)
Definition: region.c:229
const char * name
Definition: map.h:299
char * name
Definition: map.h:349
static int region_count
Definition: mapper.c:456
struct mapdef * map
Definition: object.h:155
struct regiondef * next
Definition: map.h:298
Definition: map.h:297
region * get_region_struct(void)
Definition: region.c:336
#define EXIT_PATH(xyz)
Definition: define.h:748
void fatal(int err)
Definition: glue.c:60
#define EXIT_X(xyz)
Definition: define.h:750
struct regiondef * parent
Definition: map.h:307
const char * longname
Definition: map.h:312
char * strdup_local(const char *str)
Definition: porting.c:310
#define EXIT_Y(xyz)
Definition: define.h:751
#define MAX_BUF
Definition: define.h:81
object * get_object(void)
Definition: object.c:921
const char * datadir
Definition: global.h:334
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
char * jailmap
Definition: map.h:318
const char * mapdir
Definition: global.h:337
struct Settings settings
Definition: init.c:48
void init_regions(void)
Definition: region.c:301
const char * msg
Definition: map.h:314
sstring add_string(const char *str)
Definition: shstr.c:116
const char * get_name_of_region_for_map(const mapstruct *m)
Definition: region.c:103
int strcasecmp(const char *s1, const char *s2)
Definition: porting.c:434
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
unsigned int uint32
Definition: global.h:58
EXTERN region * first_region
Definition: global.h:192
static void assign_region_parents(void)
Definition: region.c:469
Definition: map.h:346
sint16 jaily
Definition: map.h:319
static void parse_regions(FILE *fp)
Definition: region.c:355
struct regiondef * region
Definition: map.h:350
FILE * open_and_uncompress(const char *name, int flag, int *compressed)
Definition: porting.c:724
uint8 type
Definition: object.h:189
const char * get_region_msg(const region *r)
Definition: region.c:250