ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/region.C
Revision: 1.20
Committed: Mon Jan 29 15:04:21 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.19: +94 -145 lines
Log Message:
rewrote region parser, changed region fileformat a bit

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 root 1.20 * Reads/parses the region file, and copies into a linked list
221     * of region structs.
222     */
223     static bool
224     parse_regions (object_thawer &fp)
225     {
226     region *newreg;
227     region *reg;
228    
229     newreg = NULL;
230     for (;;)
231     {
232     keyword kw = fp.get_kv ();
233    
234     switch (kw)
235     {
236     case KW_EOF:
237     if (newreg)
238     {
239     LOG (llevError, "%s: end of file while reading regions.\n", fp.name);
240     return false;
241     }
242     else
243     return true;
244    
245     case KW_end:
246     /* Place this new region last on the list, if the list is empty put it first */
247     for (reg = first_region; reg && reg->next; reg = reg->next)
248     ;
249    
250     if (!reg)
251     first_region = newreg;
252     else
253     reg->next = newreg;
254    
255     newreg = 0;
256     break;
257    
258     default:
259     case KW_ERROR:
260     LOG (llevError, "%s: skipping errornous line (%s) while reading regions.\n", fp.name, fp.last_keyword);
261     break;
262    
263     case KW_region:
264     newreg = new region;
265     fp.get (newreg->name);
266     break;
267    
268     case KW_parent:
269     /*
270     * Note that this is in the initialisation code, so we don't actually
271     * assign the pointer to the parent yet, because it might not have been
272     * parsed.
273     */
274     fp.get (newreg->parent_name);
275     break;
276    
277     case KW_longname:
278     newreg->longname = strdup (fp.get_str ());
279     break;
280    
281     case KW_jail_map:
282     fp.get (newreg->jailmap);
283     break;
284    
285     case KW_jail_x:
286     fp.get (newreg->jailx);
287     break;
288    
289     case KW_jail_y:
290     fp.get (newreg->jaily);
291     break;
292    
293     case KW_msg:
294     fp.get_ml (KW_endmsg, newreg->msg);
295     break;
296    
297     case KW_fallback:
298     fp.get (newreg->fallback);
299     break;
300    
301     case KW_nomore:
302     /* we have reached the end of the region specs.... */
303     return true;
304     }
305     }
306     }
307    
308     /*
309 elmex 1.1 * First initialises the archtype hash-table (init_archetable()).
310     * Reads and parses the archetype file (with the first and second-pass
311     * functions).
312     * Then initialises treasures by calling load_treasures().
313     */
314 root 1.4 void
315     init_regions (void)
316     {
317     char filename[MAX_BUF];
318     int comp;
319    
320     if (first_region != NULL) /* Only do this once */
321 elmex 1.1 return;
322    
323 root 1.19 // make sure one region is always available
324     first_region = new region;
325     first_region->name = "<builtin>";
326     first_region->longname = strdup ("Built-in Region");
327    
328 root 1.4 sprintf (filename, "%s/%s/%s", settings.datadir, settings.mapdir, settings.regions);
329     LOG (llevDebug, "Reading regions from %s...\n", filename);
330 root 1.20
331     object_thawer fp (filename);
332    
333     if (!fp)
334 root 1.4 {
335     LOG (llevError, " Can't open regions file %s in init_regions.\n", filename);
336     return;
337     }
338 root 1.19
339 root 1.4 parse_regions (fp);
340 root 1.20
341 root 1.4 assign_region_parents ();
342     LOG (llevDebug, " done\n");
343 elmex 1.1 }