Crossfire Server, Branch 1.12  R12190
region.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_map_c =
00003  *   "$Id: region.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2001-2003 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The authors can be reached via e-mail at crossfire-devel@real-time.com
00027 */
00028 
00036 #include <global.h>
00037 
00038 #ifndef WIN32 /* ---win32 exclude header */
00039 #include <unistd.h>
00040 #endif /* win32 */
00041 
00042 static void parse_regions(FILE *fp);
00043 static void assign_region_parents(void);
00044 
00057 region *get_region_by_name(const char *region_name) {
00058     region *reg;
00059 
00060     for (reg = first_region; reg != NULL; reg = reg->next)
00061         if (!strcmp(reg->name, region_name))
00062             return reg;
00063 
00064     for (reg = first_region; reg != NULL; reg = reg->next) {
00065         if (reg->fallback) {
00066             LOG(llevDebug, "region called %s requested, but not found, fallback used.\n", region_name);
00067             return reg;
00068         }
00069     }
00070     LOG(llevInfo, "Got no region or fallback for region %s.\n", region_name);
00071     return NULL;
00072 }
00073 
00085 region *get_region_by_map(mapstruct *m) {
00086     return get_region_by_name(get_name_of_region_for_map(m));
00087 }
00088 
00103 const char *get_name_of_region_for_map(const mapstruct  *m) {
00104     region *reg;
00105 
00106     if (m->region != NULL)
00107         return m->region->name;
00108     for (reg = first_region; reg != NULL; reg = reg->next) {
00109         if (reg->fallback)
00110             return reg->name;
00111     }
00112     LOG(llevInfo, "map %s had no region and I couldn't find a fallback to use.\n", m->name);
00113     return "unknown";
00114 }
00115 
00132 region *get_region_from_string(const char *name) {
00133     region *reg;
00134     char *substr;
00135     char *p;
00136 
00137     if (first_region == NULL) {
00138         return NULL;
00139     }
00140 
00141     if (name == NULL) {
00142         for (reg = first_region; reg->parent != NULL; reg = reg->parent)
00143             ;
00144         return reg;
00145     }
00146     p = strchr(name, '\n');
00147     if (p)
00148         *p = '\0';
00149     for (reg = first_region; reg != NULL; reg = reg->next)
00150         if (!strcasecmp(reg->name, name))
00151             return reg;
00152 
00153     for (reg = first_region; reg != NULL; reg = reg->next)
00154         if (reg->longname != NULL) {
00155             if (!strcasecmp(reg->longname, name))
00156                 return reg;
00157         }
00158 
00159     substr = NULL;
00160     for (reg = first_region; reg != NULL; reg = reg->next)
00161         if (reg->longname != NULL) {
00162             substr = strstr(reg->longname, name);
00163             if (substr != NULL)
00164                 return reg;
00165         }
00166     for (reg = first_region; reg != NULL; reg = reg->next)
00167         if (reg->longname != NULL) {
00168             /*
00169              * This is not a bug, we want the region that is  most identifiably a discrete
00170              * area in the game, eg if we have 'scor', we want to return 'scorn' and not
00171              * 'scornarena', regardless of their order on the list so we only look at those
00172              * regions with a longname set.
00173              */
00174             substr = strstr(reg->name, name);
00175             if (substr != NULL)
00176                 return reg;
00177         }
00178     for (reg = first_region; reg != NULL; reg = reg->next) {
00179         substr = strstr(reg->name, name);
00180         if (substr != NULL)
00181             return reg;
00182     }
00183     /* if we are still here, we are going to have to give up, and give the top level region */
00184     for (reg = first_region; reg->parent != NULL; reg = reg->parent)
00185         ;
00186     return reg;
00187 }
00188 
00201 int region_is_child_of_region(const region *child, const region *r) {
00202 
00203     if (r == NULL)
00204         return -1;
00205     if (child == NULL)
00206         return 0;
00207     if (!strcmp(child->name, r->name))
00208         return 1;
00209     else if (child->parent != NULL)
00210         return region_is_child_of_region(child->parent, r);
00211     else
00212         return 0;
00213 }
00214 
00229 const char *get_region_longname(const region *r) {
00230     if (r->longname != NULL)
00231         return r->longname;
00232     else if (r->parent != NULL)
00233         return get_region_longname(r->parent);
00234     else {
00235         LOG(llevDebug, "NOTICE region %s has no parent and no longname.\n", r->name);
00236         return "no name can be found for the current region";
00237     }
00238 }
00239 
00250 const char *get_region_msg(const region *r) {
00251     if (r->msg != NULL)
00252         return r->msg;
00253     else if (r->parent != NULL)
00254         return get_region_msg(r->parent);
00255     else {
00256         LOG(llevDebug, "NOTICE region %s has no parent and no msg.\n", r->name);
00257         return "no description can be found for the current region";
00258     }
00259 }
00260 
00272 object *get_jail_exit(object *op) {
00273     region *reg;
00274     object *exit;
00275 
00276     if (op->type != PLAYER) {
00277         LOG(llevError, "region.c: get_jail_exit called against non-player object.\n");
00278         return NULL;
00279     }
00280 
00281     reg = get_region_by_map(op->map);
00282     while (reg != NULL) {
00283         if (reg->jailmap) {
00284             exit = get_object();
00285             EXIT_PATH(exit) = add_string(reg->jailmap);
00286             /* damned exits reset savebed and remove teleports, so the prisoner can't escape */
00287             SET_FLAG(exit, FLAG_DAMNED);
00288             EXIT_X(exit) = reg->jailx;
00289             EXIT_Y(exit) = reg->jaily;
00290             return exit;
00291         } else
00292             reg = reg->parent;
00293     }
00294     LOG(llevDebug, "No suitable jailmap for region %s was found.\n", reg->name);
00295     return NULL;
00296 }
00297 
00301 void init_regions(void) {
00302     FILE *fp;
00303     char filename[MAX_BUF];
00304     int comp;
00305 
00306     if (first_region != NULL) /* Only do this once */
00307         return;
00308 
00309     snprintf(filename, sizeof(filename), "%s/%s/%s", settings.datadir, settings.mapdir, settings.regions);
00310     LOG(llevDebug, "Reading regions from %s...\n", filename);
00311     if ((fp = open_and_uncompress(filename, 0, &comp)) == NULL) {
00312         LOG(llevError, " Can't open regions file %s in init_regions.\n", filename);
00313         return;
00314     }
00315     parse_regions(fp);
00316     assign_region_parents();
00317     LOG(llevDebug, " done\n");
00318 
00319     close_and_delete(fp, comp);
00320 }
00321 
00336 region *get_region_struct(void) {
00337     region *new;
00338 
00339     new = (region *)CALLOC(1, sizeof(region));
00340     if (new == NULL)
00341         fatal(OUT_OF_MEMORY);
00342 
00343     memset(new, '\0', sizeof(region));
00344 
00345     return new;
00346 }
00347 
00355 static void parse_regions(FILE *fp) {
00356     region *new;
00357     region *reg;
00358 
00359     char buf[HUGE_BUF], msgbuf[HUGE_BUF], *key = NULL, *value, *end;
00360     int msgpos = 0;
00361 
00362     new = NULL;
00363     while (fgets(buf, HUGE_BUF-1, fp) != NULL) {
00364         buf[HUGE_BUF-1] = 0;
00365         key = buf;
00366         while (isspace(*key))
00367             key++;
00368         if (*key == 0)
00369             continue;    /* empty line */
00370         value = strchr(key, ' ');
00371         if (!value) {
00372             end = strchr(key, '\n');
00373             *end = 0;
00374         } else {
00375             *value = 0;
00376             value++;
00377             while (isspace(*value))
00378                 value++;
00379             end = strchr(value, '\n');
00380         }
00381 
00382         /*
00383          * This is a bizzare mutated form of the map and archetype parser
00384          * rolled into one. Key is the field name, value is what it should
00385          * be set to.
00386          * We've already done the work to null terminate key,
00387          * and strip off any leading spaces for both of these.
00388          * We have not touched the newline at the end of the line -
00389          * these might be needed for some values. the end pointer
00390          * points to the first of the newlines.
00391          * value could be NULL!  It would be easy enough to just point
00392          * this to "" to prevent cores, but that would let more errors slide
00393          * through.
00394          */
00395         if (!strcmp(key, "region")) {
00396             *end = 0;
00397             new = get_region_struct();
00398             new->name = strdup_local(value);
00399         } else if (!strcmp(key, "parent")) {
00400             /*
00401              * Note that this is in the initialisation code, so we don't actually
00402              * assign the pointer to the parent yet, because it might not have been
00403              * parsed.
00404              */
00405             *end = 0;
00406             new->parent_name = strdup_local(value);
00407         } else if (!strcmp(key, "longname")) {
00408             *end = 0;
00409             new->longname = strdup_local(value);
00410         } else if (!strcmp(key, "jail")) {
00411             /* jail entries are of the form: /path/to/map x y */
00412             char path[MAX_BUF];
00413             int x, y;
00414 
00415             if (sscanf(value, "%[^ ] %d %d\n", path, &x, &y) != 3) {
00416                 LOG(llevError, "region.c: malformated regions entry: jail %s\n", value);
00417                 continue;
00418             }
00419             new->jailmap = strdup_local(path);
00420             new->jailx = x;
00421             new->jaily = y;
00422         } else if (!strcmp(key, "msg")) {
00423             while (fgets(buf, HUGE_BUF-1, fp) != NULL) {
00424                 if (!strcmp(buf, "endmsg\n"))
00425                     break;
00426                 else {
00427                     strcpy(msgbuf+msgpos, buf);
00428                     msgpos += strlen(buf);
00429                 }
00430             }
00431             /*
00432              * There may be regions with empty messages (eg, msg/endmsg
00433              * with nothing between). When maps are loaded, this is done
00434              * so better do it here too...
00435              */
00436             if (msgpos != 0)
00437                 new->msg = strdup_local(msgbuf);
00438 
00439             /* we have to reset msgpos, or the next region will store both msg blocks.*/
00440             msgpos = 0;
00441         } else if (!strcmp(key, "fallback")) {
00442             *end = 0;
00443             new->fallback = atoi(value);
00444         } else if (!strcmp(key, "end")) {
00445             /* Place this new region last on the list, if the list is empty put it first */
00446             for (reg = first_region; reg != NULL && reg->next != NULL; reg = reg->next)
00447                 ;
00448 
00449             if (reg == NULL)
00450                 first_region = new;
00451             else
00452                 reg->next = new;
00453             new = NULL;
00454         } else if (!strcmp(key, "nomore")) {
00455             /* we have reached the end of the region specs....*/
00456             break;
00457         } else {
00458             /* we should never get here, if we have, then something is wrong */
00459             LOG(llevError, "Got unknown value in region file: %s %s\n", key, value);
00460         }
00461     }
00462     if (!key || strcmp(key, "nomore"))
00463         LOG(llevError, "Got premature eof on regions file!\n");
00464 }
00465 
00469 static void assign_region_parents(void) {
00470     region *reg;
00471     uint32 parent_count = 0;
00472     uint32 region_count = 0;
00473 
00474     for (reg = first_region; reg != NULL && reg->next != NULL; reg = reg->next) {
00475         if (reg->parent_name != NULL) {
00476             reg->parent = get_region_by_name(reg->parent_name);
00477             parent_count++;
00478         }
00479         region_count++;
00480     }
00481     LOG(llevDebug, "Assigned %u regions with %u parents.\n", region_count, parent_count);
00482 }