ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/region.c
Revision: 1.1
Committed: Fri Feb 3 07:11:40 2006 UTC (18 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Branch point for: UPSTREAM
Log Message:
Initial revision

File Contents

# User Rev Content
1 root 1.1 /*
2     * static char *rcsid_map_c =
3     * "$Id: region.c,v 1.8 2005/12/05 23:34:03 akirschbaum Exp $";
4     */
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     if (!strcmp(reg->name, region_name)) return reg;
49    
50     for (reg=first_region;reg!=NULL;reg=reg->next) {
51     if (reg->fallback) {
52     LOG(llevDebug,"region called %s requested, but not found, fallback used.\n", region_name);
53     return reg;
54     }
55     }
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     return get_region_by_name(get_name_of_region_for_map(m));
63     }
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(mapstruct *m) {
72     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     LOG(llevInfo,"map %s had no region and I couldn't find a fallback to use.\n", m->name);
78     return "unknown";
79     }
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     if (!strcasecmp(reg->name, name)) return reg;
106    
107     for (reg=first_region;reg!=NULL;reg=reg->next)
108     if (reg->longname != NULL) {
109     if (!strcasecmp(reg->longname, name)) return reg;
110     }
111    
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     if (substr != NULL) return reg;
117     }
118     for (reg=first_region;reg!=NULL;reg=reg->next)
119     if (reg->longname != NULL) {
120     /*
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     substr=strstr(reg->name, name);
127     if (substr != NULL) return reg;
128     }
129     for (reg=first_region;reg!=NULL;reg=reg->next) {
130     substr=strstr(reg->name, name);
131     if (substr != NULL) return reg;
132     }
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(region *child, region *r) {
145    
146     if (r==NULL)
147     return -1;
148     if (child == NULL)
149     return 0;
150     if (!strcmp(child->name, r->name))
151     return 1;
152     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(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(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     /*
189     * First initialises the archtype hash-table (init_archetable()).
190     * Reads and parses the archetype file (with the first and second-pass
191     * functions).
192     * Then initialises treasures by calling load_treasures().
193     */
194     void init_regions(void) {
195     FILE *fp;
196     char filename[MAX_BUF];
197     int comp;
198    
199     if(first_region!=NULL) /* Only do this once */
200     return;
201    
202     sprintf(filename,"%s/%s",settings.datadir,settings.regions);
203     LOG(llevDebug,"Reading regions from %s...\n",filename);
204     if((fp=open_and_uncompress(filename,0,&comp))==NULL) {
205     LOG(llevError," Can't open regions file %s in init_regions.\n", filename);
206     return;
207     }
208     parse_regions(fp);
209     assign_region_parents();
210     LOG(llevDebug," done\n");
211    
212     close_and_delete(fp, comp);
213     }
214    
215     /*
216     * Allocates and zeros a region struct, this isn't free()'d anywhere, so might
217     * be a memory leak, but it shouldn't matter too much since it isn't called that
218     * often....
219     */
220    
221     region *get_region_struct(void) {
222    
223     region *new;
224    
225     new=(region *)CALLOC(1,sizeof(region));
226     if(new==NULL)
227     fatal(OUT_OF_MEMORY);
228    
229     memset(new, sizeof(region), '\0');
230    
231     return new;
232     }
233    
234     /*
235     * Reads/parses the region file, and copies into a linked list
236     * of region structs.
237     */
238     void parse_regions(FILE *fp) {
239     region *new;
240     region *reg;
241    
242     char buf[HUGE_BUF], msgbuf[HUGE_BUF], *key=NULL, *value, *end;
243     int msgpos=0;
244    
245     new = NULL;
246     while (fgets(buf, HUGE_BUF-1, fp)!=NULL) {
247     buf[HUGE_BUF-1] = 0;
248     key = buf;
249     while (isspace(*key)) key++;
250     if (*key == 0) continue; /* empty line */
251     value = strchr(key, ' ');
252     if (!value) {
253     end = strchr(key, '\n');
254     *end=0;
255     } else {
256     *value = 0;
257     value++;
258     while (isspace(*value)) value++;
259     end = strchr(value, '\n');
260     }
261    
262     /*
263     * This is a bizzare mutated form of the map and archetype parser
264     * rolled into one. Key is the field name, value is what it should
265     * be set to.
266     * We've already done the work to null terminate key,
267     * and strip off any leading spaces for both of these.
268     * We have not touched the newline at the end of the line -
269     * these might be needed for some values. the end pointer
270     * points to the first of the newlines.
271     * value could be NULL! It would be easy enough to just point
272     * this to "" to prevent cores, but that would let more errors slide
273     * through.
274     */
275     if (!strcmp(key,"region")) {
276     *end=0;
277     new=get_region_struct();
278     new->name = strdup_local(value);
279     }
280     else if (!strcmp(key,"parent")) {
281     /*
282     * Note that this is in the initialisation code, so we don't actually
283     * assign the pointer to the parent yet, because it might not have been
284     * parsed.
285     */
286     *end=0;
287     new->parent_name = strdup_local(value);
288     }
289     else if (!strcmp(key,"longname")) {
290     *end=0;
291     new->longname = strdup_local(value);
292     }
293     else if (!strcmp(key,"msg")) {
294     while (fgets(buf, HUGE_BUF-1, fp)!=NULL) {
295     if (!strcmp(buf,"endmsg\n")) break;
296     else {
297     strcpy(msgbuf+msgpos, buf);
298     msgpos += strlen(buf);
299     }
300     }
301     /*
302     * There may be regions with empty messages (eg, msg/endmsg
303     * with nothing between). When maps are loaded, this is done
304     * so better do it here too...
305     */
306     if (msgpos != 0)
307     new->msg = strdup_local(msgbuf);
308    
309     /* we have to reset msgpos, or the next region will store both msg blocks.*/
310     msgpos=0;
311     }
312     else if (!strcmp(key,"fallback")) {
313     *end=0;
314     new->fallback = atoi(value);
315     }
316     else if (!strcmp(key,"end")) {
317     /* Place this new region last on the list, if the list is empty put it first */
318     for (reg=first_region;reg!=NULL&&reg->next!=NULL;reg=reg->next);
319    
320     if (reg==NULL) first_region=new;
321     else reg->next=new;
322     new = NULL;
323     }
324     else if (!strcmp(key,"nomore")) {
325     /* we have reached the end of the region specs....*/
326     break;
327     }
328     else {
329     /* we should never get here, if we have, then something is wrong */
330     LOG(llevError, "Got unknown value in region file: %s %s\n", key, value);
331     }
332     }
333     if (!key || strcmp(key,"nomore"))
334     LOG(llevError, "Got premature eof on regions file!\n");
335     }
336    
337     void assign_region_parents(void) {
338     region *reg;
339     uint32 parent_count=0;
340     uint32 region_count=0;
341     for (reg=first_region;reg!=NULL&&reg->next!=NULL;reg=reg->next) {
342     if (reg->parent_name!=NULL) {
343     reg->parent=get_region_by_name(reg->parent_name);
344     parent_count++;
345     }
346     region_count++;
347     }
348     LOG(llevDebug, "Assigned %u regions with %u parents.", region_count, parent_count);
349     }