ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/region.C
Revision: 1.5
Committed: Wed Sep 13 23:39:27 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.4: +0 -6 lines
Log Message:
*** empty log message ***

File Contents

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