ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/region.C
Revision: 1.21
Committed: Thu Feb 1 19:15:38 2007 UTC (17 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.20: +87 -133 lines
Log Message:
- improve error messages from object_thawer to incldue line numbers
- provide geenric parse error handler
- finish basic design of generic object loader
- implement generic regions loader
- use it to load regions:
  loader_region loader;
  if (!loader.load (filename))
    error;
- regions should now be the very first filetype that could be reloaded at runtime

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.21 #include "loader.h"
29    
30     regionvec regions;
31    
32 root 1.18 region *
33     region::default_region ()
34     {
35 root 1.21 for_all_regions (rgn)
36     if (rgn->fallback)
37     return rgn;
38 root 1.18
39 root 1.21 return regions [0];
40 root 1.18 }
41    
42 elmex 1.1 /*
43     * Pass a char array, returns a pointer to the region of the same name.
44     * if it can't find a region of the same name it returns the first region
45     * with the 'fallback' property set.
46     * if it can't find a matching name /or/ a fallback region it logs an info message
47     * message and returns NULL
48     * used by the map parsing code.
49     */
50 root 1.4 region *
51 root 1.18 region::find (const char *name)
52 root 1.4 {
53 root 1.21 for_all_regions (rgn)
54     if (!strcmp (rgn->name, name))
55     return rgn;
56 root 1.4
57 root 1.18 LOG (llevError, "region called %s requested, but not found, using fallback.\n", name);
58 root 1.4
59 root 1.18 return default_region ();
60 elmex 1.1 }
61    
62     /*
63     * Tries to find a region that 'name' corresponds to.
64     * It looks, in order, for:
65     * an exact match to region name (case insensitive)
66     * an exact match to longname (case insensitive)
67     * a substring that matches to the longname (eg Kingdom)
68     * a substring that matches to the region name (eg nav)
69     * if it can find none of these it returns the first parentless region
70     * (there should be only one of these - the top level one)
71     * If we got a NULL, then just return the top level region
72     *
73     */
74 root 1.4 region *
75 root 1.19 region::find_fuzzy (const char *name)
76 root 1.4 {
77 root 1.21 if (!name)
78     return default_region ();
79 root 1.19
80 root 1.21 char *p = strchr (name, '\n');
81 root 1.4 if (p)
82     *p = '\0';
83 root 1.19
84 root 1.21 for_all_regions (rgn)
85     if (!strcasecmp (rgn->name, name))
86     return rgn;
87    
88     for_all_regions (rgn)
89     if (rgn->longname)
90     if (!strcasecmp (rgn->longname, name))
91     return rgn;
92    
93     for_all_regions (rgn)
94     if (rgn->longname)
95 root 1.4 {
96 root 1.21 if (strstr (rgn->longname, name))
97     return rgn;
98 root 1.4 }
99 root 1.19
100 root 1.21 for_all_regions (rgn)
101     if (rgn->longname)
102 root 1.4 {
103 root 1.2 /*
104 root 1.19 * This is not a bug, we want the region that is most identifiably a discrete
105 root 1.2 * area in the game, eg if we have 'scor', we want to return 'scorn' and not
106     * 'scornarena', regardless of their order on the list so we only look at those
107     * regions with a longname set.
108     */
109 root 1.21 if (strstr (rgn->name, name))
110     return rgn;
111 root 1.4 }
112 root 1.19
113 root 1.21 for_all_regions (rgn)
114 root 1.4 {
115 root 1.21 if (strstr (rgn->name, name))
116     return rgn;
117 root 1.4 }
118 root 1.19
119 root 1.21 return default_region ();
120 elmex 1.1 }
121    
122     /*
123     * returns 1 if the player is in the region reg, or a child region thereof
124     * otherwise returns 0
125     * if passed a NULL region returns -1
126     */
127    
128 root 1.19 static int
129 root 1.4 region_is_child_of_region (const region * child, const region * r)
130     {
131    
132     if (r == NULL)
133     return -1;
134 root 1.19
135 root 1.4 if (child == NULL)
136     return 0;
137 root 1.19
138 root 1.4 if (!strcmp (child->name, r->name))
139     return 1;
140 root 1.19
141 root 1.4 else if (child->parent != NULL)
142     return region_is_child_of_region (child->parent, r);
143     else
144     return 0;
145 elmex 1.1 }
146    
147     /** Returns an object which is an exit through which the player represented by op should be
148     * sent in order to be imprisoned. If there is no suitable place to which an exit can be
149     * constructed, then NULL will be returned. The caller is responsible for freeing the object
150     * created by this function.
151     */
152 root 1.4 object *
153     get_jail_exit (object *op)
154     {
155     region *reg;
156     object *exit;
157    
158     if (op->type != PLAYER)
159     {
160     LOG (llevError, "region.c: get_jail_exit called against non-player object.\n");
161     return NULL;
162     }
163 root 1.19
164     reg = op->region ();
165     while (reg)
166 root 1.4 {
167     if (reg->jailmap)
168     {
169 root 1.9 exit = object::create ();
170 root 1.4 EXIT_PATH (exit) = reg->jailmap;
171     /* damned exits reset savebed and remove teleports, so the prisoner can't escape */
172     SET_FLAG (exit, FLAG_DAMNED);
173     EXIT_X (exit) = reg->jailx;
174     EXIT_Y (exit) = reg->jaily;
175     return exit;
176 root 1.2 }
177 root 1.4 else
178     reg = reg->parent;
179 elmex 1.1 }
180 root 1.19
181 root 1.21 LOG (llevError, "No suitable jailmap for region %s was found.\n", &reg->name);
182    
183     return 0;
184 elmex 1.1 }
185    
186 root 1.21 region *loader_region::get_region (const char *name)
187 root 1.19 {
188 root 1.21 region *rgn = new region;
189     rgn->name = name;
190     return rgn;
191     }
192 root 1.19
193 root 1.21 void loader_region::put_region (region *rgn)
194     {
195     for_all_regions (old)
196     if (old->name == rgn->name)
197     {
198     // replace, copy new values (ugly)
199     rgn->index = old->index;
200     *old = *rgn;
201     delete rgn;
202 root 1.19
203 root 1.21 return;
204     }
205 root 1.19
206 root 1.21 // just append
207     regions.push_back (rgn);
208 root 1.19 }
209    
210 root 1.21 bool
211     loader_base::parse_region (object_thawer &thawer, region *rgn)
212 root 1.20 {
213     for (;;)
214     {
215 root 1.21 keyword kw = thawer.get_kv ();
216 root 1.20
217     switch (kw)
218     {
219     case KW_parent:
220 root 1.21 rgn->parent = region::find (thawer.get_str ());
221 root 1.20 break;
222    
223     case KW_longname:
224 root 1.21 thawer.get (rgn->longname);
225 root 1.20 break;
226    
227     case KW_jail_map:
228 root 1.21 thawer.get (rgn->jailmap);
229 root 1.20 break;
230    
231     case KW_jail_x:
232 root 1.21 thawer.get (rgn->jailx);
233 root 1.20 break;
234    
235     case KW_jail_y:
236 root 1.21 thawer.get (rgn->jaily);
237 root 1.20 break;
238    
239     case KW_msg:
240 root 1.21 thawer.get_ml (KW_endmsg, rgn->msg);
241 root 1.20 break;
242    
243     case KW_fallback:
244 root 1.21 thawer.get (rgn->fallback);
245 root 1.20 break;
246    
247 root 1.21 case KW_end:
248 root 1.20 return true;
249 root 1.21
250     default:
251     if (!thawer.parse_error (kw, "region", rgn->name))
252     return false;
253     break;
254 root 1.20 }
255     }
256     }
257    
258     /*
259 elmex 1.1 * First initialises the archtype hash-table (init_archetable()).
260     * Reads and parses the archetype file (with the first and second-pass
261     * functions).
262     * Then initialises treasures by calling load_treasures().
263     */
264 root 1.4 void
265     init_regions (void)
266     {
267     char filename[MAX_BUF];
268     int comp;
269    
270 root 1.21 if (!regions.size ())
271     {
272     // make sure one region is always available
273     region *rgn = new region;
274     rgn->name = "<builtin>";
275     rgn->longname = "Built-in Region";
276     regions.push_back (rgn);
277     }
278 root 1.19
279 root 1.4 sprintf (filename, "%s/%s/%s", settings.datadir, settings.mapdir, settings.regions);
280     LOG (llevDebug, "Reading regions from %s...\n", filename);
281 root 1.20
282 root 1.21 loader_region loader;
283 root 1.20
284 root 1.21 if (!loader.load (filename))
285 root 1.4 {
286     LOG (llevError, " Can't open regions file %s in init_regions.\n", filename);
287     return;
288     }
289 root 1.19
290 root 1.21 if (!loader.load (filename))
291     {
292     LOG (llevError, " Can't open regions file %s in init_regions.\n", filename);
293     return;
294     }
295 root 1.20
296 root 1.4 LOG (llevDebug, " done\n");
297 elmex 1.1 }