ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/region.C
Revision: 1.3
Committed: Sun Sep 3 00:18:40 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.2: +2 -2 lines
Log Message:
THIS CODE WILL NOT COMPILE
use the STABLE tag instead.

- major changes in object lifetime and memory management
- replaced manual refcounting by shstr class
- removed quest system
- many optimisations
- major changes

File Contents

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