ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/region.C
Revision: 1.19
Committed: Mon Jan 29 14:46:01 2007 UTC (17 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.18: +65 -126 lines
Log Message:
partial region cleanup

File Contents

# User Rev Content
1 elmex 1.1 /*
2 pippijn 1.16 * CrossFire, A Multiplayer game for X-windows
3     *
4     * Copyright (C) 2005, 2006, 2007 Marc Lehmann & Crossfire+ Development Team
5     * Copyright (C) 2001-2003 Mark Wedel & Crossfire Development Team
6     * Copyright (C) 1992 Frank Tore Johansen
7     *
8     * This program is free software; you can redistribute it and/or modify
9     * it under the terms of the GNU General Public License as published by
10     * the Free Software Foundation; either version 2 of the License, or
11     * (at your option) any later version.
12     *
13     * This program is distributed in the hope that it will be useful,
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     * GNU General Public License for more details.
17     *
18     * You should have received a copy of the GNU General Public License
19     * along with this program; if not, write to the Free Software
20     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21     *
22     * The authors can be reached via e-mail at <crossfire@schmorp.de>
23     */
24 elmex 1.1
25     #include <global.h>
26 pippijn 1.8 #include <unistd.h>
27 root 1.4
28 root 1.18 region *
29     region::default_region ()
30     {
31     for (region *reg = first_region; reg; reg = reg->next)
32     if (reg->fallback)
33     return reg;
34    
35     return first_region;
36     }
37    
38 elmex 1.1 /*
39     * Pass a char array, returns a pointer to the region of the same name.
40     * if it can't find a region of the same name it returns the first region
41     * with the 'fallback' property set.
42     * if it can't find a matching name /or/ a fallback region it logs an info message
43     * message and returns NULL
44     * used by the map parsing code.
45     */
46 root 1.4 region *
47 root 1.18 region::find (const char *name)
48 root 1.4 {
49 root 1.18 for (region *reg = first_region; reg; reg = reg->next)
50     if (!strcmp (reg->name, name))
51     return reg;
52 root 1.4
53 root 1.18 LOG (llevError, "region called %s requested, but not found, using fallback.\n", name);
54 root 1.4
55 root 1.18 return default_region ();
56 elmex 1.1 }
57    
58     /*
59     * Tries to find a region that 'name' corresponds to.
60     * It looks, in order, for:
61     * an exact match to region name (case insensitive)
62     * an exact match to longname (case insensitive)
63     * a substring that matches to the longname (eg Kingdom)
64     * a substring that matches to the region name (eg nav)
65     * if it can find none of these it returns the first parentless region
66     * (there should be only one of these - the top level one)
67     * If we got a NULL, then just return the top level region
68     *
69     */
70 root 1.4 region *
71 root 1.19 region::find_fuzzy (const char *name)
72 root 1.4 {
73     region *reg;
74     char *substr;
75     char *p;
76    
77     if (name == NULL)
78     {
79 root 1.19 for (reg = first_region; reg->parent; reg = reg->parent)
80     ;
81    
82 root 1.4 return reg;
83     }
84 root 1.19
85 root 1.4 p = strchr (name, '\n');
86     if (p)
87     *p = '\0';
88 root 1.19
89     for (reg = first_region; reg; reg = reg->next)
90 root 1.4 if (!strcasecmp (reg->name, name))
91     return reg;
92    
93 root 1.19 for (reg = first_region; reg; reg = reg->next)
94     if (reg->longname)
95     if (!strcasecmp (reg->longname, name))
96     return reg;
97 root 1.4
98     substr = NULL;
99 root 1.19 for (reg = first_region; reg; reg = reg->next)
100     if (reg->longname)
101 root 1.4 {
102     substr = strstr (reg->longname, name);
103 root 1.19 if (substr)
104 root 1.4 return reg;
105     }
106 root 1.19
107     for (reg = first_region; reg; reg = reg->next)
108     if (reg->longname)
109 root 1.4 {
110 root 1.2 /*
111 root 1.19 * This is not a bug, we want the region that is most identifiably a discrete
112 root 1.2 * area in the game, eg if we have 'scor', we want to return 'scorn' and not
113     * 'scornarena', regardless of their order on the list so we only look at those
114     * regions with a longname set.
115     */
116 root 1.4 substr = strstr (reg->name, name);
117 root 1.19 if (substr)
118 root 1.4 return reg;
119     }
120 root 1.19
121     for (reg = first_region; reg; reg = reg->next)
122 root 1.4 {
123     substr = strstr (reg->name, name);
124 root 1.19 if (substr)
125 root 1.4 return reg;
126     }
127 root 1.19
128 root 1.4 /* if we are still here, we are going to have to give up, and give the top level region */
129 root 1.19 for (reg = first_region; reg->parent; reg = reg->parent)
130     ;
131    
132 root 1.4 return reg;
133 elmex 1.1 }
134    
135     /*
136     * returns 1 if the player is in the region reg, or a child region thereof
137     * otherwise returns 0
138     * if passed a NULL region returns -1
139     */
140    
141 root 1.19 static int
142 root 1.4 region_is_child_of_region (const region * child, const region * r)
143     {
144    
145     if (r == NULL)
146     return -1;
147 root 1.19
148 root 1.4 if (child == NULL)
149     return 0;
150 root 1.19
151 root 1.4 if (!strcmp (child->name, r->name))
152     return 1;
153 root 1.19
154 root 1.4 else if (child->parent != NULL)
155     return region_is_child_of_region (child->parent, r);
156     else
157     return 0;
158 elmex 1.1 }
159    
160     /** Returns an object which is an exit through which the player represented by op should be
161     * sent in order to be imprisoned. If there is no suitable place to which an exit can be
162     * constructed, then NULL will be returned. The caller is responsible for freeing the object
163     * created by this function.
164     */
165 root 1.4 object *
166     get_jail_exit (object *op)
167     {
168     region *reg;
169     object *exit;
170    
171     if (op->type != PLAYER)
172     {
173     LOG (llevError, "region.c: get_jail_exit called against non-player object.\n");
174     return NULL;
175     }
176 root 1.19
177     reg = op->region ();
178     while (reg)
179 root 1.4 {
180     if (reg->jailmap)
181     {
182 root 1.9 exit = object::create ();
183 root 1.4 EXIT_PATH (exit) = reg->jailmap;
184     /* damned exits reset savebed and remove teleports, so the prisoner can't escape */
185     SET_FLAG (exit, FLAG_DAMNED);
186     EXIT_X (exit) = reg->jailx;
187     EXIT_Y (exit) = reg->jaily;
188     return exit;
189 root 1.2 }
190 root 1.4 else
191     reg = reg->parent;
192 elmex 1.1 }
193 root 1.19
194     LOG (llevDebug, "No suitable jailmap for region %s was found.\n", &reg->name);
195 root 1.4 return NULL;
196 elmex 1.1 }
197    
198 root 1.19 static void
199     assign_region_parents (void)
200     {
201     region *reg;
202     uint32 parent_count = 0;
203     uint32 region_count = 0;
204    
205     for (reg = first_region; reg && reg->next; reg = reg->next)
206     {
207     if (reg->parent_name)
208     {
209     reg->parent = region::find (reg->parent_name);
210     parent_count++;
211     }
212    
213     region_count++;
214     }
215    
216     LOG (llevDebug, "Assigned %u regions with %u parents.\n", region_count, parent_count);
217     }
218    
219 elmex 1.1 /*
220     * First initialises the archtype hash-table (init_archetable()).
221     * Reads and parses the archetype file (with the first and second-pass
222     * functions).
223     * Then initialises treasures by calling load_treasures().
224     */
225 root 1.4 void
226     init_regions (void)
227     {
228     FILE *fp;
229     char filename[MAX_BUF];
230     int comp;
231    
232     if (first_region != NULL) /* Only do this once */
233 elmex 1.1 return;
234    
235 root 1.19 // make sure one region is always available
236     first_region = new region;
237     first_region->name = "<builtin>";
238     first_region->longname = strdup ("Built-in Region");
239    
240 root 1.4 sprintf (filename, "%s/%s/%s", settings.datadir, settings.mapdir, settings.regions);
241     LOG (llevDebug, "Reading regions from %s...\n", filename);
242     if ((fp = open_and_uncompress (filename, 0, &comp)) == NULL)
243     {
244     LOG (llevError, " Can't open regions file %s in init_regions.\n", filename);
245     return;
246     }
247 root 1.19
248 root 1.4 parse_regions (fp);
249     assign_region_parents ();
250     LOG (llevDebug, " done\n");
251    
252     close_and_delete (fp, comp);
253 elmex 1.1 }
254    
255     /*
256     * Reads/parses the region file, and copies into a linked list
257     * of region structs.
258     */
259 root 1.4 void
260     parse_regions (FILE * fp)
261     {
262     region *newreg;
263     region *reg;
264    
265     char buf[HUGE_BUF], msgbuf[HUGE_BUF], *key = NULL, *value, *end;
266     int msgpos = 0;
267    
268     newreg = NULL;
269     while (fgets (buf, HUGE_BUF - 1, fp) != NULL)
270     {
271     buf[HUGE_BUF - 1] = 0;
272     key = buf;
273     while (isspace (*key))
274     key++;
275     if (*key == 0)
276     continue; /* empty line */
277     value = strchr (key, ' ');
278     if (!value)
279     {
280     end = strchr (key, '\n');
281     *end = 0;
282     }
283     else
284     {
285     *value = 0;
286     value++;
287     while (isspace (*value))
288 root 1.2 value++;
289 root 1.4 end = strchr (value, '\n');
290 root 1.2 }
291 root 1.4
292     /*
293     * This is a bizzare mutated form of the map and archetype parser
294     * rolled into one. Key is the field name, value is what it should
295     * be set to.
296     * We've already done the work to null terminate key,
297     * and strip off any leading spaces for both of these.
298     * We have not touched the newline at the end of the line -
299     * these might be needed for some values. the end pointer
300     * points to the first of the newlines.
301     * value could be NULL! It would be easy enough to just point
302     * this to "" to prevent cores, but that would let more errors slide
303     * through.
304     */
305     if (!strcmp (key, "region"))
306     {
307     *end = 0;
308 root 1.19 newreg = new region;
309     newreg->name = value;
310 root 1.4 }
311     else if (!strcmp (key, "parent"))
312     {
313     /*
314     * Note that this is in the initialisation code, so we don't actually
315     * assign the pointer to the parent yet, because it might not have been
316     * parsed.
317     */
318     *end = 0;
319 root 1.19 newreg->parent_name = value;
320 root 1.4 }
321     else if (!strcmp (key, "longname"))
322     {
323     *end = 0;
324 root 1.11 newreg->longname = strdup (value);
325 root 1.4 }
326     else if (!strcmp (key, "jail"))
327     {
328     /* jail entries are of the form: /path/to/map x y */
329     char path[MAX_BUF];
330     int x, y;
331    
332     if (sscanf (value, "%[^ ] %d %d\n", path, &x, &y) != 3)
333     {
334     LOG (llevError, "region.c: malformated regions entry: jail %s\n", value);
335     continue;
336 root 1.2 }
337 root 1.11 newreg->jailmap = strdup (path);
338 root 1.4 newreg->jailx = x;
339     newreg->jaily = y;
340     }
341     else if (!strcmp (key, "msg"))
342     {
343     while (fgets (buf, HUGE_BUF - 1, fp) != NULL)
344     {
345     if (!strcmp (buf, "endmsg\n"))
346     break;
347     else
348     {
349     strcpy (msgbuf + msgpos, buf);
350     msgpos += strlen (buf);
351 root 1.2 }
352     }
353 root 1.4 /*
354     * There may be regions with empty messages (eg, msg/endmsg
355     * with nothing between). When maps are loaded, this is done
356     * so better do it here too...
357     */
358     if (msgpos != 0)
359 root 1.11 newreg->msg = strdup (msgbuf);
360 root 1.4
361     /* we have to reset msgpos, or the next region will store both msg blocks. */
362     msgpos = 0;
363     }
364     else if (!strcmp (key, "fallback"))
365     {
366     *end = 0;
367     newreg->fallback = atoi (value);
368     }
369     else if (!strcmp (key, "end"))
370     {
371     /* Place this new region last on the list, if the list is empty put it first */
372     for (reg = first_region; reg != NULL && reg->next != NULL; reg = reg->next);
373    
374     if (reg == NULL)
375     first_region = newreg;
376     else
377     reg->next = newreg;
378     newreg = NULL;
379     }
380     else if (!strcmp (key, "nomore"))
381     {
382     /* we have reached the end of the region specs.... */
383     break;
384     }
385     else
386     {
387     /* we should never get here, if we have, then something is wrong */
388     LOG (llevError, "Got unknown value in region file: %s %s\n", key, value);
389     }
390     }
391     if (!key || strcmp (key, "nomore"))
392     LOG (llevError, "Got premature eof on regions file!\n");
393     }
394