Crossfire Server, Branch 1.12  R12190
mapper.c
Go to the documentation of this file.
00001 /*
00002  * Crossfire map browser generator.
00003  *
00004  * Author: Nicolas Weeger <nicolas.weeger@laposte.net>, (C) 2006, 2007, 2008.
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019  */
00020 
00213 #include <time.h>
00214 #include <stdio.h>
00215 /* For strcasecmp(). */
00216 #include <strings.h>
00217 
00218 #include <global.h>
00219 #include <sproto.h>
00220 #include <image.h>
00221 
00222 #include <gd.h>
00223 #include <gdfonts.h>
00224 #include <gdfontl.h>
00225 #include <gdfontg.h>
00226 
00227 static gdImagePtr *gdfaces;
00228 
00229 extern int nrofpixmaps; /* Found in common/image.c */
00230 
00232 typedef struct struct_npc_info {
00233     const char *name;       
00234     const char *message;    
00235     int x, y;               
00236 } struct_npc_info;
00237 
00239 typedef struct struct_npc_list {
00240     struct_npc_info **npc;
00241     int count;
00242     int allocated;
00243 } struct_npc_list;
00244 
00246 typedef struct struct_race_list {
00247     struct struct_race **races; 
00248     int count;                  
00249     int allocated;              
00250 } struct_race_list;
00251 
00253 typedef struct {
00254     struct struct_map_in_quest **list;
00255     int count;
00256     int allocated;
00257 } struct_map_in_quest_list;
00258 
00260 typedef struct {
00261     struct struct_map_info **maps;
00262     int count;
00263     int allocated;
00264 } struct_map_list;
00265 
00267 typedef struct struct_map_info {
00268     char *path;
00269     char *name;
00270     char *filename;
00271     char *lore;
00272     region *cfregion;
00273     int level, pic_was_done, max_monster, min_monster;
00274     struct_map_list exits_from;
00275     struct_map_list exits_to;
00276     struct_map_in_quest_list quests;
00277 
00278     struct_map_list tiled_maps;
00279 
00280     struct_race_list monsters;
00281 
00282     struct_npc_list npcs;
00283 
00284     struct struct_map_info *tiled_group;
00285     int height, width;
00286     int tiled_x_from, tiled_y_from, processed;
00287     struct struct_map_info *tiles[4];
00288 } struct_map_info;
00289 
00291 static struct_map_list maps_list;
00292 
00294 static struct_map_list tiled_map_list;
00295 
00297 typedef struct struct_equipment {
00298     char *name;             
00299     int power;              
00300     int calc_power;         
00301     char *diff;             
00302     struct_map_list origin; 
00303 } struct_equipment;
00304 
00305 static struct_equipment **special_equipment = NULL;    
00307 static int equipment_count = 0;                        
00309 static int equipment_allocated = 0;                    
00313 typedef struct struct_race {
00314     char *name;             
00315     int count;              
00316     struct_map_list origin; 
00317 } struct_race;
00318 
00319 static struct_race_list races;     
00326 static void init_race_list(struct_race_list *list) {
00327     list->races = NULL;
00328     list->count = 0;
00329     list->allocated = 0;
00330 }
00331 
00342 static void add_race_to_list(struct_race *race, struct_race_list *list, int check) {
00343     if (check) {
00344         int test;
00345 
00346         for (test = 0; test < list->count; test++) {
00347             if (list->races[test] == race)
00348                 return;
00349         }
00350     }
00351 
00352     if (list->allocated == list->count) {
00353         list->allocated += 50;
00354         list->races = realloc(list->races, sizeof(struct_race *)*list->allocated);
00355     }
00356     list->races[list->count] = race;
00357     list->count++;
00358 }
00359 
00361 static char root[500];
00362 
00364 static int pics_allocated;
00365 
00366 /* Options */
00367 static int generate_pics = 1;     
00368 static int force_pics = 0;        
00369 static int generate_index = 1;    
00370 static int size_small = 16;       
00371 static int map_limit = -1;        
00372 static int show_maps = 0;         
00373 static int world_map = 1;         
00374 static int world_exit_info = 1;   
00375 static int tileset = 0;           
00377 static char *world_template;               
00378 static char *world_row_template;           
00379 static char *world_map_template;           
00381 static char *map_template;                 
00382 static char *map_no_exit_template;         
00383 static char *map_with_exit_template;       
00384 static char *map_exit_template;            
00385 static char *map_no_exit_to_template;      
00386 static char *map_with_exit_to_template;    
00387 static char *map_exit_to_template;         
00388 static char *map_lore_template;            
00389 static char *map_no_lore_template;         
00390 static char *map_no_monster_template;      
00391 static char *map_monster_before_template;  
00392 static char *map_monster_between_template; 
00393 static char *map_monster_one_template;     
00394 static char *map_monster_after_template;   
00396 static char *index_template;           
00397 static char *index_letter;             
00398 static char *index_map;                
00400 static char *region_template;          
00401 static char *region_letter_template;   
00402 static char *region_map_template;      
00404 static char *index_region_template;        
00405 static char *index_region_region_template; 
00407 static char *level_template;
00408 static char *level_value_template;
00409 static char *level_map_template;
00410 
00411 static char *index_quest_template;
00412 static char *quest_template;
00413 static char *quest_map_template;
00414 
00415 static char *map_no_quest_template;
00416 static char *map_with_quests_template;
00417 static char *map_one_quest_template;
00418 
00420 static int created_pics = 0; 
00421 static int cached_pics = 0;  
00424 enum output_format_type {
00425     OF_PNG = 0,     
00426     OF_JPG = 1      
00427 };
00428 
00430 static const char *output_extensions[] = {
00431     ".png",
00432     ".jpg"
00433 };
00434 
00436 static enum output_format_type output_format = OF_PNG;
00437 
00439 static int jpeg_quality = -1;
00440 
00442 static int rawmaps = 0;
00443 
00445 static int warn_no_path = 0;
00446 
00448 typedef struct struct_region_info {
00449     region *reg;                
00450     struct_map_list maps_list;  
00451     int sum_x, sum_y, sum;      
00452     int is_world;               
00453 } struct_region_info;
00454 
00455 static struct struct_region_info **regions = NULL; 
00456 static int region_count = 0;                
00457 static int region_allocated = 0;            
00459 static int list_unused_maps = 0;       
00460 static char **found_maps = NULL;       
00461 static int found_maps_count = 0;       
00462 static int found_maps_allocated = 0;   
00464 /* Path/exit info */
00465 static gdImagePtr infomap;         
00466 static int color_unlinked_exit;    
00467 static int color_linked_exit;      
00468 static int color_road;             
00469 static int color_blocking;         
00470 static int color_slowing;          
00472 static int **elevation_info;       
00473 static int elevation_min;          
00474 static int elevation_max;          
00476 /* Links between regions */
00477 static int do_regions_link = 0;
00478 static char **regions_link;
00479 static int regions_link_count = 0;
00480 static int regions_link_allocated = 0;
00481 
00483 #define S_DOOR      0
00484 #define S_KEY       1
00485 #define S_CONTAINER 2
00486 #define S_DETECTOR  3
00487 #define S_CONNECT   4
00488 #define S_MAX       5
00489 
00491 typedef struct {
00492     char *slaying;          
00493     struct_map_list maps[S_MAX];
00494 } struct_slaying_info;
00495 
00496 static struct_slaying_info **slaying_info = NULL;  
00497 static int slaying_count = 0;                      
00498 static int slaying_allocated = 0;                  
00505 static void init_map_list(struct_map_list *list) {
00506     list->maps = NULL;
00507     list->count = 0;
00508     list->allocated = 0;
00509 }
00510 
00511 static void add_map(struct_map_info *info, struct_map_list *list);
00512 
00513 static int is_special_equipment(object *item) {
00514     if (item->name == item->arch->clone.name)
00515         return 0;
00516     if (QUERY_FLAG(item, FLAG_NO_PICK))
00517         return 0;
00518     if (item->move_block == MOVE_ALL)
00519         return 0;
00520 
00521     if (IS_SHIELD(item) || IS_WEAPON(item) || IS_ARMOR(item) || IS_ARROW(item) || (item->type == ROD) || (item->type == WAND))
00522         return 1;
00523 
00524     return 0;
00525 }
00526 
00532 static struct_equipment *get_equipment(void) {
00533     struct_equipment *add = calloc(1, sizeof(struct_equipment));
00534 
00535     init_map_list(&add->origin);
00536     return add;
00537 }
00538 
00545 static void free_equipment(struct_equipment *equip) {
00546     free(equip->diff);
00547     free(equip->name);
00548     free(equip);
00549 }
00550 
00559 static struct_equipment *ensure_unique(struct_equipment *item) {
00560     int check;
00561     struct_equipment *comp;
00562 
00563     for (check = 0; check < equipment_count; check++) {
00564         comp = special_equipment[check];
00565 
00566         if (strcmp(comp->name, item->name))
00567             continue;
00568         if (comp->power != item->power)
00569             continue;
00570         if (comp->calc_power != item->calc_power)
00571             continue;
00572         if (strcmp(comp->diff, item->diff))
00573             continue;
00574 
00575         free_equipment(item);
00576         return comp;
00577     }
00578 
00579     if (equipment_count == equipment_allocated) {
00580         equipment_allocated += 50;
00581         special_equipment = realloc(special_equipment, sizeof(struct_equipment *)*equipment_allocated);
00582     }
00583     special_equipment[equipment_count] = item;
00584     equipment_count++;
00585 
00586     return item;
00587 }
00588 
00598 static void add_one_item(object *item, struct_map_info *map) {
00599     struct_equipment *add = get_equipment();
00600     StringBuffer *bf = stringbuffer_new();
00601     int x, y;
00602     sstring name, namepl;
00603 
00604     x = item->x;
00605     y = item->y;
00606     name = item->name;
00607     namepl = item->name_pl;
00608 
00609     item->x = item->arch->clone.x;
00610     item->y = item->arch->clone.y;
00611     item->name = item->arch->clone.name;
00612     item->name_pl = item->arch->clone.name_pl;
00613     get_ob_diff(bf, item, &item->arch->clone);
00614     add->diff = stringbuffer_finish(bf);
00615 
00616     item->x = x;
00617     item->y = y;
00618     item->name = name;
00619     item->name_pl = namepl;
00620 
00621     if (add->diff == NULL || strcmp(add->diff, "") == 0) {
00622         free_equipment(add);
00623         return;
00624     }
00625 
00626     add->name = strdup(item->name);
00627     add->power = item->item_power;
00628     add->calc_power = calc_item_power(item, 0);
00629 
00630     add = ensure_unique(add);
00631     add_map(map, &add->origin);
00632 }
00633 
00642 static void check_equipment(object *item, struct_map_info *map) {
00643     object *inv;
00644 
00645     if (is_special_equipment(item))
00646         add_one_item(item, map);
00647 
00648     for (inv = item->inv; inv; inv = inv->below) {
00649         check_equipment(inv, map);
00650     }
00651 }
00652 
00661 static int sort_equipment(const void *a, const void *b) {
00662     const struct_equipment *l = *(const struct_equipment **)a;
00663     const struct_equipment *r = *(const struct_equipment **)b;
00664     int c = l->power-r->power;
00665 
00666     if (c)
00667         return c;
00668     return strcasecmp(l->name, r->name);
00669 }
00670 
00679 static struct_race *get_race(const char *name) {
00680     int test;
00681     struct_race *item;
00682 
00683     for (test = 0; test < races.count; test++) {
00684         if (strcmp(races.races[test]->name, name) == 0) {
00685             races.races[test]->count++;
00686             return races.races[test];
00687         }
00688     }
00689 
00690     item = calloc(1, sizeof(struct_race));
00691     item->name = strdup(name);
00692     item->count = 1;
00693     init_map_list(&item->origin);
00694 
00695     add_race_to_list(item, &races, 0);
00696 
00697     return item;
00698 }
00699 
00708 static void add_monster(object *monster, struct_map_info *map) {
00709     struct_race *race;
00710 
00711     if (monster->head && monster != monster->head)
00712         return;
00713 
00714     map->min_monster = MIN(monster->level, map->min_monster);
00715     map->max_monster = MAX(monster->level, map->max_monster);
00716 
00717     race = get_race(monster->name);
00718     add_map(map, &race->origin);
00719     add_race_to_list(race, &map->monsters, 1);
00720 }
00721 
00730 static int sort_race(const void *a, const void *b) {
00731     const struct_race *l = *(const struct_race **)a;
00732     const struct_race *r = *(const struct_race **)b;
00733     return strcasecmp(l->name, r->name);
00734 }
00735 
00743 static int is_road(object *item) {
00744     int test;
00745     /* Archetypes used as roads. */
00746     const char *roads[] = {
00747         "cobblestones",
00748         "flagstone",
00749         "ice_stone",
00750         "snow",
00751         NULL };
00752     const char *partial[] = {
00753         "dirtroad_",
00754         NULL };
00755 
00756     for (test = 0; partial[test] != NULL; test++) {
00757         if (strstr(item->arch->name, partial[test]) != NULL)
00758             return 1;
00759     }
00760 
00761     if (!QUERY_FLAG(item, FLAG_IS_FLOOR))
00762         return 0;
00763 
00764     for (test = 0; roads[test] != NULL; test++) {
00765         if (strcmp(item->arch->name, roads[test]) == 0)
00766             return 1;
00767     }
00768 
00769     return 0;
00770 }
00771 
00779 static int is_blocking(object *item) {
00780     return item->move_block == MOVE_ALL ? 1 : 0;
00781 }
00782 
00793 static int get_elevation_color(int elevation, gdImagePtr elevationmap) {
00794     if (elevation > 0)
00795         return gdImageColorResolve(elevationmap, 200*elevation/elevation_max, 0, 0);
00796     else
00797         return gdImageColorResolve(elevationmap, 0, 0, 200*elevation/elevation_min);
00798 }
00799 
00808 static void do_exit_map(mapstruct *map) {
00809     int tx, ty, x, y;
00810     object *item, *test;
00811 
00812     if (sscanf(map->path, "/world/world_%d_%d", &x, &y) != 2)
00813         return;
00814 
00815     x -= 100;
00816     y -= 100;
00817 
00818     for (tx = 0; tx < MAP_WIDTH(map); tx++) {
00819         for (ty = 0; ty < MAP_HEIGHT(map); ty++) {
00820             item = GET_MAP_OB(map, tx, ty);
00821             while (item) {
00822                 test = item->head ? item->head : item;
00823 
00824                 if (test->type == EXIT || test->type == TELEPORTER) {
00825                     if (!test->slaying)
00826                         gdImageSetPixel(infomap, x*50+tx, y*50+ty, color_unlinked_exit);
00827                     else
00828                         gdImageSetPixel(infomap, x*50+tx, y*50+ty, color_linked_exit);
00829                 } else if (is_road(test))
00830                     gdImageSetPixel(infomap, x*50+tx, y*50+ty, color_road);
00831                 else if (is_blocking(test)) {
00832                     gdImageSetPixel(infomap, x*50+tx, y*50+ty, color_blocking);
00833                     /* can't get on the spot, so no need to go on. */
00834                     break;
00835                 } else if (test->move_slow != 0)
00836                     gdImageSetPixel(infomap, x*50+tx, y*50+ty, color_slowing);
00837 
00838                 if (item->elevation) {
00839                     elevation_min = MIN(elevation_min, item->elevation);
00840                     elevation_max = MAX(elevation_max, item->elevation);
00841                     elevation_info[x*50+tx][y*50+ty] = item->elevation;
00842                 }
00843 
00844                 item = item->above;
00845             }
00846         }
00847     }
00848 }
00849 
00850 void do_auto_apply(mapstruct *m);
00851 
00862 static int sortbyname(const void *a, const void *b) {
00863     return (strcmp(*(const char **)a, *(const char **)b));
00864 }
00865 
00876 static char *cat_template(char *source, char *add) {
00877     if (!source)
00878         return add;
00879     source = realloc(source, strlen(source)+strlen(add)+1);
00880     strcat(source, add);
00881     free(add);
00882     return source;
00883 }
00884 
00895 static void read_template(const char *name, char **buffer) {
00896     FILE *file;
00897     struct stat info;
00898 
00899     if (stat(name, &info)) {
00900         printf("Couldn't stat template %s!\n", name);
00901         exit(1);
00902     }
00903 
00904     (*buffer) = calloc(1, info.st_size+1);
00905     if (!(*buffer)) {
00906         printf("Template %s calloc failed!\n", name);
00907         exit(1);
00908     }
00909 
00910     if (info.st_size == 0) {
00911         (*buffer)[0] = '\0';
00912         return;
00913     }
00914 
00915     file = fopen(name, "rb");
00916     if (!file) {
00917         printf("Couldn't open template %s!\n", name);
00918         free(*buffer);
00919         exit(1);
00920     }
00921     if (fread(*buffer, info.st_size, 1, file) != 1) {
00922         printf("Couldn't read template %s!\n", name);
00923         free(*buffer);
00924         fclose(file);
00925         exit(1);
00926     }
00927     fclose(file);
00928 }
00929 
00947 static char *do_template(const char *template, const char **vars, const char **values) {
00948     int count = 0;
00949     const char *sharp = template;
00950     int maxlen = 0;
00951     int var = 0;
00952     char *result;
00953     char *current_result;
00954     const char *end;
00955 
00956     while ((sharp = strchr(sharp, '#')) != NULL) {
00957         sharp++;
00958         count++;
00959     }
00960     if (!count)
00961         return strdup(template);
00962     if (count%2) {
00963         printf("Malformed template, mismatched #!\n");
00964         return strdup(template);
00965     }
00966 
00967     while (vars[var] != NULL) {
00968         if (strlen(values[var]) > maxlen)
00969             maxlen = strlen(values[var]);
00970         var++;
00971     }
00972     result = calloc(1, strlen(template)+maxlen*(count/2)+1);
00973     if (!result)
00974         return NULL;
00975     current_result = result;
00976 
00977     sharp = template;
00978     while ((sharp = strchr(sharp, '#')) != NULL) {
00979         end = strchr(sharp+1, '#');
00980         strncpy(current_result, template, sharp-template);
00981         if (end == sharp+1) {
00982             strcat(current_result, "#");
00983         }
00984         else {
00985         current_result = current_result+strlen(current_result);
00986         var = 0;
00987         while (vars[var] != NULL && (strncmp(vars[var], sharp+1, end-sharp-1) || (strlen(vars[var]) != end-sharp-1)))
00988             /* tag must be the same length, else can take a wrong tag */
00989             var++;
00990         if (vars[var] == NULL)
00991             printf("Wrong tag: %s\n", sharp);
00992         else
00993             strcpy(current_result, values[var]);
00994         }
00995         current_result = current_result+strlen(current_result);
00996         sharp = end+1;
00997         template = sharp;
00998     }
00999     strcat(current_result, template);
01000     return result;
01001 }
01002 
01015 static void relative_path(const char *from, const char *to, char *result) {
01016     const char *fslash;
01017     const char *rslash;
01018 
01019     result[0] = '\0';
01020 
01021     fslash = strchr(from+1, '/');
01022     if (!fslash) {
01023         strcpy(result, to+1);
01024         return;
01025     }
01026 
01027     rslash = strchr(to+1, '/');
01028     while (fslash && rslash && (fslash-from == rslash-to) && strncmp(from, to, fslash-from+1) == 0) {
01029         from = fslash+1;
01030         to = rslash+1;
01031         fslash = strchr(fslash+1, '/');
01032         rslash = strchr(rslash+1, '/');
01033     }
01034 
01035     while (fslash) {
01036         strcat(result, "../");
01037         fslash = strchr(fslash+1, '/');
01038     }
01039     if (strlen(result) && result[strlen(result)-1] == '/' && *to == '/')
01040         result[strlen(result)-1] = '\0';
01041     strcat(result, to);
01042 }
01043 
01054 static int sort_mapname(const void *left, const void *right) {
01055     const char *l = *(const char **)left;
01056     const char *r = *(const char **)right;
01057     const char *sl = strrchr(l, '/');
01058     const char *sr = strrchr(r, '/');
01059     int c;
01060 
01061     if (!sl)
01062         sl = l;
01063     if (!sr)
01064         sr = r;
01065     c = strcasecmp(sl, sr);
01066     if (c)
01067         return c;
01068 
01069     return strcasecmp(l, r);
01070 }
01071 
01082 static int compare_map_info(const struct_map_info *left, const struct_map_info *right) {
01083     int c;
01084 
01085     if (left->tiled_group)
01086         left = left->tiled_group;
01087     if (right->tiled_group)
01088         right = right->tiled_group;
01089 
01090     c = strcasecmp(left->name, right->name);
01091     if (c)
01092         return c;
01093 
01094     return strcasecmp(left->path, right->path);
01095 }
01096 
01107 static int sort_map_info(const void *left, const void *right) {
01108     const struct_map_info *l = *(const struct_map_info **)left;
01109     const struct_map_info *r = *(const struct_map_info **)right;
01110     return compare_map_info(l, r);
01111 }
01112 
01123 static int sort_map_info_by_level(const void *left, const void *right) {
01124     const struct_map_info *l = *(const struct_map_info **)left;
01125     const struct_map_info *r = *(const struct_map_info **)right;
01126     int c = l->level-r->level;
01127     if (c)
01128         return c;
01129     return compare_map_info(l, r);
01130 }
01131 
01142 static int sort_region(const void *left, const void *right) {
01143     return strcmp((*((struct_region_info **)left))->reg->name, (*((struct_region_info **)right))->reg->name);
01144 }
01145 
01146 /************************************
01147  Start of quest-related definitions.
01148 ************************************/
01149 
01151 typedef struct struct_map_in_quest {
01152     struct_map_info *map;   
01153     char *description;      
01154     struct struct_quest *quest;    
01155 } struct_map_in_quest;
01156 
01158 typedef struct struct_quest {
01159     char *name;                     
01160     char *description;              
01161     int number;                     
01162     struct_map_info *mainmap;       
01163     struct_map_in_quest_list maps;  
01164 } struct_quest;
01165 
01166 static struct_quest **quests = NULL;   
01168 static int quests_count = 0;           
01170 static int quests_allocated = 0;       
01172 static void init_struct_map_in_quest_list(struct_map_in_quest_list *list) {
01173     list->list = NULL;
01174     list->count = 0;
01175     list->allocated = 0;
01176 }
01177 
01178 static void add_to_struct_map_in_quest_list(struct_map_in_quest_list *list, struct_map_in_quest *item) {
01179     if (list->count == list->allocated) {
01180         list->allocated += 10;
01181         list->list = realloc(list->list, sizeof(struct_map_in_quest *)*list->allocated);
01182     }
01183     list->list[list->count++] = item;
01184 }
01185 
01194 static struct_quest *get_quest_info(const char *name) {
01195     int test;
01196     struct_quest *add;
01197 
01198     for (test = 0; test < quests_count; test++) {
01199         if (strcmp(quests[test]->name, name) == 0)
01200             return quests[test];
01201     }
01202 
01203     if (quests_count == quests_allocated) {
01204         quests_allocated += 10;
01205         quests = realloc(quests, sizeof(struct_quest *)*quests_allocated);
01206     }
01207     add = calloc(1, sizeof(struct_quest));
01208     add->name = strdup(name);
01209     add->number = quests_count;
01210     init_struct_map_in_quest_list(&add->maps);
01211     quests[quests_count] = add;
01212     quests_count++;
01213     return add;
01214 }
01215 
01226 static void add_map_to_quest(struct_map_info *map, const char *name, const char *description) {
01227     struct_map_in_quest *add;
01228     struct_quest *quest = get_quest_info(name);
01229 
01230     add = calloc(1, sizeof(struct_map_in_quest));
01231     add->map = map;
01232     add->quest = quest;
01233     add->description = strdup(description);
01234     while (strlen(add->description) && add->description[strlen(add->description)-1] == '\n')
01235         add->description[strlen(add->description)-1] = '\0';
01236     add_to_struct_map_in_quest_list(&quest->maps, add);
01237     add_to_struct_map_in_quest_list(&map->quests, add);
01238 }
01239 
01248 static int sort_struct_map_in_quest(const void *left, const void *right) {
01249     int c;
01250 
01251     const struct_map_in_quest *l = *(const struct_map_in_quest **)left;
01252     const struct_map_in_quest *r = *(const struct_map_in_quest **)right;
01253     const struct_map_info *ml = l->map;
01254     const struct_map_info *mr = r->map;
01255 
01256     if (ml->tiled_group)
01257         ml = ml->tiled_group;
01258     if (mr->tiled_group)
01259         mr = mr->tiled_group;
01260 
01261     c = strcasecmp(ml->name, mr->name);
01262     if (c)
01263         return c;
01264 
01265     return strcasecmp(ml->path, mr->path);
01266 }
01267 
01278 static void define_quest(const char *name, struct_map_info *mainmap, const char *description) {
01279     struct_quest *quest = get_quest_info(name);
01280 
01281     if (quest->description || quest->mainmap) {
01282         printf("warning, multiple quest definition for %s, found in %s and %s.\n", quest->name, quest->mainmap ? quest->mainmap->path : "(unknown map)", mainmap->path);
01283         return;
01284     }
01285     quest->description = strdup(description);
01286     while (strlen(quest->description) && quest->description[strlen(quest->description)-1] == '\n')
01287         quest->description[strlen(quest->description)-1] = '\0';
01288     quest->mainmap = mainmap;
01289 }
01290 
01297 static void process_map_lore(struct_map_info *map) {
01298     char *start, *end, *next;
01299     char name[500];
01300     char description[500];
01301 
01302     start = strstr(map->lore, "@def");
01303     while (start) {
01304         description[0] = '\0';
01305         /* find name */
01306         end = strstr(start, "\n");
01307         if (end) {
01308             strncpy(name, start+5, end-start-5);
01309             name[end-start-5] = '\0';
01310             next = end+1;
01311             end = strstr(next, "@end");
01312             if (end) {
01313                 strncpy(description, next, end-next);
01314                 description[end-next] = '\0';
01315                 /* need to erase the text. */
01316                 memcpy(start, end+4, strlen(map->lore)-(end-start+3));
01317                 end = start;
01318             }
01319             else {
01320                 strcpy(description, next);
01321                 *start = '\0';
01322                 end = NULL;
01323             }
01324         } else {
01325             strcpy(name, start);
01326             *start = '\0';
01327             end = NULL;
01328         }
01329 
01330         define_quest(name, map, description);
01331         start = end ? strstr(end, "@def") : NULL;
01332     }
01333 
01334     start = strstr(map->lore, "@quest");
01335     while (start) {
01336         description[0] = '\0';
01337         /* find name */
01338         end = strstr(start, "\n");
01339         if (end) {
01340             strncpy(name, start+7, end-start-7);
01341             name[end-start-7] = '\0';
01342             next = end+1;
01343             end = strstr(next, "@end");
01344             if (end) {
01345                 strncpy(description, next, end-next);
01346                 description[end-next] = '\0';
01347                 /* need to erase the text. */
01348                 memcpy(start, end+4, strlen(map->lore)-(end-start+3));
01349                 end = start;
01350             }
01351             else {
01352                 strcpy(description, next);
01353                 *start = '\0';
01354                 end = NULL;
01355             }
01356         } else {
01357             strcpy(name, start);
01358             *start = '\0';
01359             end = NULL;
01360         }
01361 
01362         add_map_to_quest(map, name, description);
01363         start = end ? strstr(end, "@quest") : NULL;
01364     }
01365 }
01366 
01370 static void write_quests_page(void) {
01371     int quest, map;
01372     FILE *out;
01373     char path[500];
01374     char mappath[500];
01375     char mainmappath[500];
01376     char questid[500];
01377     const char *map_vars[] = { "MAPPATH", "MAPNAME", "MAPTEXT", NULL };
01378     const char *map_vals[] = { mappath, NULL, NULL, NULL };
01379     const char *quest_vars[] = { "QUESTNAME", "QUESTTEXT", "QUESTMAPS", "QUESTID", "MAINMAPPATH", "MAINMAPNAME", NULL };
01380     const char *quest_vals[] = { NULL, NULL, NULL, questid, mainmappath, NULL, NULL };
01381     const char *idx_vars[] = { "QUESTS", NULL };
01382     const char *idx_vals[] = { NULL, NULL };
01383     char *text_map = NULL;
01384     char *text_quest = NULL;
01385     char *text_idx = NULL;
01386 
01387     printf("Writing quest index...");
01388 
01389     for (quest = 0; quest < quests_count; quest++) {
01390         qsort(quests[quest]->maps.list, quests[quest]->maps.count, sizeof(struct_map_in_quest *), sort_struct_map_in_quest);
01391         for (map = 0; map < quests[quest]->maps.count; map++) {
01392             snprintf(mappath, sizeof(mappath), "%s.html", quests[quest]->maps.list[map]->map->path+1);
01393             map_vals[1] = quests[quest]->maps.list[map]->map->name;
01394             map_vals[2] = quests[quest]->maps.list[map]->description ? quests[quest]->maps.list[map]->description : "(no description)";
01395             text_map = cat_template(text_map, do_template(quest_map_template, map_vars, map_vals));
01396         }
01397         if (!text_map)
01398             text_map = strdup("");
01399 
01400         quest_vals[0] = quests[quest]->name;
01401         quest_vals[1] = quests[quest]->description ? quests[quest]->description : "(main map not processed)";
01402         quest_vals[2] = text_map;
01403         snprintf(questid, sizeof(questid), "quest_%d", quests[quest]->number);
01404         if (quests[quest]->mainmap) {
01405             snprintf(mainmappath, sizeof(mainmappath), "%s.html", quests[quest]->mainmap->path+1);
01406             quest_vals[5] = quests[quest]->mainmap->name;
01407         } else {
01408             snprintf(mainmappath, sizeof(mainmappath), "#");
01409             quest_vals[5] = "";
01410         }
01411         text_quest = cat_template(text_quest, do_template(quest_template, quest_vars, quest_vals));
01412         free(text_map);
01413         text_map = NULL;
01414     }
01415 
01416     if (!text_quest)
01417         text_quest = strdup("No quest.");
01418 
01419     idx_vals[0] = text_quest;
01420     text_idx = do_template(index_quest_template, idx_vars, idx_vals);
01421     free(text_quest);
01422 
01423     snprintf(path, sizeof(path), "%s/quests.html", root);
01424     out = fopen(path, "w+");
01425     fprintf(out, text_idx);
01426     fclose(out);
01427     free(text_idx);
01428 
01429     printf(" done.\n");
01430 }
01431 
01432 /************************************
01433  End of quest-related definitions.
01434 ************************************/
01435 
01436 /*********
01437 NPC-related stuff
01438 ********/
01439 
01445 static void init_npc_list(struct_npc_list *list) {
01446     list->allocated = 0;
01447     list->count = 0;
01448     list->npc = NULL;
01449 }
01450 
01458 static struct_npc_info *create_npc_info(const object *npc) {
01459     struct_npc_info *info = calloc(1, sizeof(struct_npc_info));
01460 
01461     info->name = strdup(npc->name);
01462     info->message = strdup(npc->msg);
01463     info->x = npc->x;
01464     info->y = npc->y;
01465 
01466     return info;
01467 }
01468 
01476 static void add_npc_to_map(struct_map_info *map, const object *npc) {
01477     if (map->npcs.count == map->npcs.allocated) {
01478         map->npcs.allocated += 50;
01479         map->npcs.npc = realloc(map->npcs.npc, map->npcs.allocated*sizeof(struct_npc_info *));
01480     }
01481 
01482     map->npcs.npc[map->npcs.count] = create_npc_info(npc);
01483     map->npcs.count++;
01484 }
01485 /* end of NPC stuff */
01486 
01498 static void add_map(struct_map_info *info, struct_map_list *list) {
01499     int map;
01500 
01501     for (map = 0; map < list->count; map++)
01502         if (list->maps[map] == info)
01503             return;
01504 
01505     if (list->count == list->allocated) {
01506         list->allocated += 50;
01507         list->maps = realloc(list->maps, list->allocated*sizeof(struct_map_info *));
01508     }
01509     list->maps[list->count] = info;
01510     list->count++;
01511 }
01512 
01523 static void replace_map(struct_map_info *find, struct_map_info *replace_by, struct_map_list *list) {
01524     int map;
01525 
01526     for (map = 0; map < list->count; map++) {
01527         if (list->maps[map] == find) {
01528             list->maps[map] = replace_by;
01529             return;
01530         }
01531     }
01532     printf("replace_map: couldn't find map %s.\n", find->path);
01533 }
01534 
01541 static struct_map_info *create_map_info(void) {
01542     struct_map_info *add = calloc(1, sizeof(struct_map_info));
01543 
01544     add->min_monster = 2000;
01545     init_map_list(&add->exits_to);
01546     init_map_list(&add->exits_from);
01547     init_map_list(&add->tiled_maps);
01548     init_struct_map_in_quest_list(&add->quests);
01549     init_race_list(&add->monsters);
01550     init_npc_list(&add->npcs);
01551     add->tiled_group = NULL;
01552 
01553     return add;
01554 }
01555 
01562 static struct_map_info *create_tiled_map(void) {
01563     struct_map_info *add = create_map_info();
01564 
01565     add_map(add, &tiled_map_list);
01566     return add;
01567 }
01568 
01580 static void merge_tiled_maps(struct_map_info *map, int tile, struct_map_info *tiled_map) {
01581     int g;
01582     struct_map_info *group = tiled_map->tiled_group;
01583     struct_map_info *change;
01584 
01585     while (group->tiled_maps.count > 0) {
01586         change = group->tiled_maps.maps[group->tiled_maps.count-1];
01587         change->tiled_group = map->tiled_group;
01588         add_map(change, &map->tiled_group->tiled_maps);
01589         group->tiled_maps.count--;
01590     }
01591 
01592     for (g = 0; g < tiled_map_list.count; g++) {
01593         if (tiled_map_list.maps[g] == group) {
01594             if (g < tiled_map_list.count-1)
01595                 tiled_map_list.maps[g] = tiled_map_list.maps[tiled_map_list.count-1];
01596             tiled_map_list.count--;
01597             free(group);
01598             return;
01599         }
01600     }
01601     printf("tiled_map not in tiled_map_list!");
01602     abort();
01603 
01604 }
01605 
01614 static struct_map_info *get_map_info(const char *path) {
01615     int map;
01616     struct_map_info *add;
01617     char *tmp;
01618 
01619     for (map = 0; map < maps_list.count; map++) {
01620         if (strcmp(maps_list.maps[map]->path, path) == 0)
01621             return maps_list.maps[map];
01622     }
01623 
01624     add = create_map_info();
01625     add->path = strdup(path);
01626     tmp = strrchr(path, '/');
01627     if (tmp)
01628         add->filename = strdup(tmp+1);
01629     else
01630         add->filename = strdup(path);
01631 
01632     add_map(add, &maps_list);
01633     return add;
01634 }
01635 
01642 static void list_map(const char *path) {
01643     int index;
01644 
01645     for (index = 0; index < found_maps_count; index++) {
01646         if (found_maps[index] && strcmp(path, found_maps[index]) == 0) {
01647             free(found_maps[index]);
01648             found_maps[index] = NULL;
01649             return;
01650         }
01651     }
01652     printf("Map processed but not found in directory reading? %s\n", path);
01653 }
01654 
01665 static void add_map_to_region(struct_map_info *map, region *reg) {
01666     int test;
01667     int x, y;
01668 
01669     for (test = 0; test < region_count; test++) {
01670         if (regions[test]->reg == reg)
01671             break;
01672     }
01673     if (test == region_count) {
01674         if (test == region_allocated) {
01675             region_allocated++;
01676             regions = realloc(regions, sizeof(struct_region_info *)*region_allocated);
01677             regions[test] = calloc(1, sizeof(struct_region_info));
01678         }
01679         region_count++;
01680         regions[test]->reg = reg;
01681     }
01682     add_map(map, &regions[test]->maps_list);
01683     if (sscanf(map->path, "/world/world_%d_%d", &x, &y) == 2) {
01684         regions[test]->sum_x += (x-100);
01685         regions[test]->sum_y += (y-100);
01686         regions[test]->sum++;
01687         regions[test]->is_world = 1;
01688     }
01689 }
01690 
01699 static void save_picture(FILE *file, gdImagePtr pic) {
01700     if (output_format == OF_PNG)
01701         gdImagePng(pic, file);
01702     else
01703         gdImageJpeg(pic, file, jpeg_quality);
01704 }
01705 
01715 static void add_region_link(mapstruct *source, mapstruct *dest, const char *linkname) {
01716     int search = 0;
01717     char entry[500];
01718     region *s, *d;
01719 
01720     s = get_region_by_map(source);
01721     d = get_region_by_map(dest);
01722     if (s == d)
01723         return;
01724 
01725     if (linkname && 0)
01726         snprintf(entry, sizeof(entry), "%s -> %s [ label = \"%s\" ]\n", s->name, d->name, linkname);
01727     else
01728         snprintf(entry, sizeof(entry), "%s -> %s\n", s->name, d->name);
01729 
01730     for (search = 0; search < regions_link_count; search++) {
01731         if (strcmp(regions_link[search], entry) == 0)
01732             return;
01733     }
01734 
01735     if (regions_link_count == regions_link_allocated) {
01736         regions_link_allocated += 10;
01737         regions_link = realloc(regions_link, sizeof(const char *)*regions_link_allocated);
01738     }
01739     regions_link[regions_link_count] = strdup(entry);
01740     regions_link_count++;
01741 }
01742 
01751 static int is_slaying(object *item) {
01752     return (item->type == LOCKED_DOOR || item->type == SPECIAL_KEY || item->type == CONTAINER || item->type == CHECK_INV);
01753 }
01754 
01755 
01764 static struct_slaying_info *get_slaying_struct(const char *slaying) {
01765     struct_slaying_info *add;
01766     int l;
01767 
01768     for (l = 0; l < slaying_count; l++) {
01769         if (!strcmp(slaying_info[l]->slaying, slaying))
01770             return slaying_info[l];
01771     }
01772     if (slaying_count == slaying_allocated) {
01773         slaying_allocated += 10;
01774         slaying_info = (struct_slaying_info **)realloc(slaying_info, sizeof(struct_slaying_info *)*slaying_allocated);
01775     }
01776 
01777     add = (struct_slaying_info *)calloc(1, sizeof(struct_slaying_info));
01778     add->slaying = strdup(slaying);
01779     for (l = 0; l < S_MAX; l++)
01780         init_map_list(&add->maps[l]);
01781 
01782     slaying_info[slaying_count] = add;
01783     slaying_count++;
01784 
01785     return add;
01786 }
01787 
01798 static void add_map_to_slaying(struct_slaying_info *info, int item, struct_map_info *map) {
01799     add_map(map, &info->maps[item]);
01800 }
01801 
01810 static void add_slaying(struct_map_info *map, object *item) {
01811     struct_slaying_info *info;
01812 
01813     if (!item->slaying)
01814         /* can be undefined */
01815         return;
01816 
01817     info = get_slaying_struct(item->slaying);
01818     if (item->type == LOCKED_DOOR)
01819         add_map_to_slaying(info, S_DOOR, map);
01820     else if (item->type == SPECIAL_KEY)
01821         add_map_to_slaying(info, S_KEY, map);
01822     else if (item->type == CONTAINER)
01823         add_map_to_slaying(info, S_CONTAINER, map);
01824     else
01825         add_map_to_slaying(info, S_CONNECT, map);
01826 }
01827 
01836 static void check_slaying_inventory(struct_map_info *map, object *item) {
01837     object *inv;
01838 
01839     for (inv = item->inv; inv; inv = inv->below) {
01840         if (is_slaying(inv))
01841             add_slaying(map, inv);
01842         check_slaying_inventory(map, inv);
01843     }
01844 }
01845 
01854 static void process_map(struct_map_info *info) {
01855     mapstruct *m;
01856     int x, y, isworld;
01857     object *item;
01858     FILE *out;
01859     gdImagePtr pic;
01860     gdImagePtr small;
01861     struct stat stats;
01862     struct stat statspic;
01863     char exit_path[500];
01864     char tmppath[MAX_BUF];
01865     char picpath[MAX_BUF], smallpicpath[MAX_BUF];
01866     int needpic = 0;
01867     struct_map_info *link;
01868 
01869     if (list_unused_maps)
01870         list_map(info->path);
01871 
01872     if (show_maps)
01873         printf(" processing map %s\n", info->path);
01874 
01875     m = ready_map_name(info->path, 0);
01876     if (!m) {
01877         printf("couldn't load map %s\n", info->path);
01878         return;
01879     }
01880 
01881     do_exit_map(m);
01882 
01883     if (!rawmaps)
01884         do_auto_apply(m);
01885 
01886     info->level = m->difficulty;
01887     if (m->maplore) {
01888         info->lore = strdup(m->maplore);
01889         process_map_lore(info);
01890     }
01891 
01892     isworld = (sscanf(info->path, "/world/world_%d_%d", &x, &y) == 2);
01893 
01894     if (m->name)
01895         info->name = strdup(m->name);
01896     else
01897         info->name = strdup(info->filename);
01898 
01899     info->cfregion = get_region_by_map(m);
01900     add_map_to_region(info, info->cfregion);
01901 
01902     snprintf(picpath, sizeof(picpath), "%s%s%s", root, info->path, output_extensions[output_format]);
01903     snprintf(smallpicpath, sizeof(smallpicpath), "%s%s.small%s", root, info->path, output_extensions[output_format]);
01904 
01905     if (force_pics)
01906         needpic = 1;
01907     else if (generate_pics) {
01908         create_pathname(info->path, tmppath, MAX_BUF);
01909         stat(tmppath, &stats);
01910         if (stat(picpath, &statspic) || (statspic.st_mtime < stats.st_mtime))
01911             needpic = 1;
01912         else if (stat(smallpicpath, &statspic) || (statspic.st_mtime < stats.st_mtime))
01913             needpic = 1;
01914     }
01915     else
01916         needpic = 0;
01917 
01918     if (needpic) {
01919         pic = gdImageCreateTrueColor(MAP_WIDTH(m)*32, MAP_HEIGHT(m)*32);
01920         created_pics++;
01921     }
01922     else
01923         cached_pics++;
01924 
01925     for (x = 0; x < 4; x++)
01926         if (m->tile_path[x] != NULL) {
01927             path_combine_and_normalize(m->path, m->tile_path[x], exit_path, sizeof(exit_path));
01928             create_pathname(exit_path, tmppath, MAX_BUF);
01929             if (stat(tmppath, &stats)) {
01930                 printf("  map %s doesn't exist in map %s, for tile %d.\n", exit_path, info->path, x);
01931             }
01932 
01933             if (isworld) {
01934                 link = get_map_info(exit_path);
01935                 add_map(link, &info->exits_from);
01936                 add_map(info, &link->exits_to);
01937 
01938                 if (do_regions_link) {
01939                     mapstruct *link = ready_map_name(exit_path, 0);
01940 
01941                     if (link && link != m) {
01942                         /* no need to link a map with itself. Also, if the exit points to the same map, we don't
01943                         * want to reset it. */
01944                         add_region_link(m, link, NULL);
01945                         link->reset_time = 1;
01946                         link->in_memory = MAP_IN_MEMORY;
01947                         delete_map(link);
01948                     }
01949                 }
01950             } else {
01951                 link = get_map_info(exit_path);
01952                 info->tiles[x] = link;
01953                 if (link->tiled_group) {
01954                     if (info->tiled_group && link->tiled_group != info->tiled_group) {
01955                         merge_tiled_maps(info, x, link);
01956                         continue;
01957                     }
01958                     if (link->tiled_group == info->tiled_group) {
01959                         continue;
01960                     }
01961                     if (!info->tiled_group) {
01962                         add_map(info, &link->tiled_group->tiled_maps);
01963                         continue;
01964                     }
01965                 }
01966 
01967                 if (!info->tiled_group) {
01968                     info->tiled_group = create_tiled_map();
01969                     add_map(info, &info->tiled_group->tiled_maps);
01970                 }
01971                 link->tiled_group = info->tiled_group;
01972                 add_map(link, &info->tiled_group->tiled_maps);
01973             }
01974         }
01975 
01976     info->width = MAP_WIDTH(m);
01977     info->height = MAP_HEIGHT(m);
01978 
01979     for (x = MAP_WIDTH(m)-1; x >= 0; x--)
01980         for (y = MAP_HEIGHT(m)-1; y >= 0; y--) {
01981             for (item = GET_MAP_OB(m, x, y); item; item = item->above) {
01982                 if (item->type == EXIT || item->type == TELEPORTER || item->type == PLAYER_CHANGER) {
01983                     char ep[500];
01984                     const char *start;
01985 
01986                     if (!item->slaying) {
01987                         ep[0] = '\0';
01988                         if (warn_no_path)
01989                             printf(" exit without any path at %d, %d on %s\n", item->x, item->y, info->path);
01990                     } else {
01991                         memset(ep, 0, 500);
01992                         if (strcmp(item->slaying, "/!"))
01993                             strcpy(ep, EXIT_PATH(item));
01994                         else {
01995                             if (!item->msg) {
01996                                 printf("  random map without message in %s at %d, %d\n", info->path, item->x, item->y);
01997                             } else {
01998                                 /* Some maps have a 'exit_on_final_map' flag, ignore it. */
01999                                 start = strstr(item->msg, "\nfinal_map ");
02000                                 if (!start && strncmp(item->msg, "final_map", strlen("final_map")) == 0)
02001                                     /* Message start is final_map, nice */
02002                                     start = item->msg;
02003                                 if (start) {
02004                                     char *end = strchr(start+1, '\n');
02005 
02006                                     start += strlen("final_map")+2;
02007                                     strncpy(ep, start, end-start);
02008                                 }
02009                             }
02010                         }
02011 
02012                         if (strlen(ep)) {
02013                             path_combine_and_normalize(m->path, ep, exit_path, 500);
02014                             create_pathname(exit_path, tmppath, MAX_BUF);
02015                             if (stat(tmppath, &stats)) {
02016                                 printf("  map %s doesn't exist in map %s, at %d, %d.\n", ep, info->path, item->x, item->y);
02017                             } else {
02018                                 link = get_map_info(exit_path);
02019                                 add_map(link, &info->exits_from);
02020                                 add_map(info, &link->exits_to);
02021 
02022                                 if (do_regions_link) {
02023                                     mapstruct *link = ready_map_name(exit_path, 0);
02024 
02025                                     if (link && link != m) {
02026                                         /* no need to link a map with itself. Also, if the exit points to the same map, we don't
02027                                          * want to reset it. */
02028                                         add_region_link(m, link, item->arch->clone.name);
02029                                         link->reset_time = 1;
02030                                         link->in_memory = MAP_IN_MEMORY;
02031                                         delete_map(link);
02032                                     }
02033                                 }
02034                             }
02035                         }
02036                     }
02037                 } else if (is_slaying(item))
02038                     add_slaying(info, item);
02039 
02040                 check_equipment(item, info);
02041 
02042                 check_slaying_inventory(info, item);
02043 
02044                 if (QUERY_FLAG(item, FLAG_MONSTER)) {
02045                     /* need to get the "real" archetype, as the item's archetype can certainly be a temporary one. */
02046                     archetype *arch = find_archetype(item->arch->name);
02047 
02048                     add_monster(item, info);
02049                     if ((QUERY_FLAG(item, FLAG_UNAGGRESSIVE) || QUERY_FLAG(item, FLAG_FRIENDLY)) && (item->msg != arch->clone.msg) && (item->msg != NULL))
02050                         add_npc_to_map(info, item);
02051                 }
02052 
02053                 if (item->invisible)
02054                     continue;
02055 
02056                 if (needpic) {
02057                     int sx, sy, hx, hy;
02058 
02059                     if (gdfaces[item->face->number] == NULL) {
02060                         int set = get_face_fallback(tileset, item->face->number);
02061 
02062                         gdfaces[item->face->number] = gdImageCreateFromPngPtr(facesets[set].faces[item->face->number].datalen, facesets[set].faces[item->face->number].data);
02063                         pics_allocated++;
02064                     }
02065                     if (item->head || item->more) {
02066                         get_multi_size(item, &sx, &sy, &hx, &hy);
02067                     } else {
02068                         hx = 0;
02069                         hy = 0;
02070                     }
02071                     if (gdfaces[item->face->number] != NULL && ((!item->head && !item->more) || (item->arch->clone.x+hx == 0 && item->arch->clone.y+hy == 0))) {
02072                         gdImageCopy(pic, gdfaces[item->face->number], x*32, y*32, 0, 0, gdfaces[item->face->number]->sx, gdfaces[item->face->number]->sy);
02073                     }
02074                 }
02075             }
02076         }
02077 
02078     if (needpic) {
02079         make_path_to_file(picpath);
02080         out = fopen(picpath, "wb+");
02081         save_picture(out, pic);
02082         fclose(out);
02083 
02084         small = gdImageCreateTrueColor(MAP_WIDTH(m)*size_small, MAP_HEIGHT(m)*size_small);
02085         gdImageCopyResampled(small, pic, 0, 0, 0, 0, MAP_WIDTH(m)*size_small, MAP_HEIGHT(m)*size_small, MAP_WIDTH(m)*32, MAP_HEIGHT(m)*32);
02086 
02087         out = fopen(smallpicpath, "wb+");
02088         save_picture(out, small);
02089         fclose(out);
02090         gdImageDestroy(small);
02091 
02092         gdImageDestroy(pic);
02093 
02094         info->pic_was_done = 1;
02095     }
02096 
02097     m->reset_time = 1;
02098     m->in_memory = MAP_IN_MEMORY;
02099     delete_map(m);
02100 }
02101 
02122 static char *do_map_index(const char *dest, struct_map_list *maps_list,
02123                           const char *template_page, const char *template_letter,
02124                           const char *template_map, const char **vars,
02125                           const char **values) {
02126 #define VARSADD 6
02127     int map;
02128     char *string;
02129     char mappath[500];
02130     char maphtml[500];
02131     char count[50];
02132     char lettercount[50];
02133     char *tmp;
02134     const char **idx_vars;
02135     const char **idx_values;
02136     char str_letter[2];
02137     char last_letter;
02138     char index_path[500];
02139     char *mapstext = NULL;
02140     int byletter;
02141     int basevalues, realcount = 0;
02142     struct_map_info *last_group = NULL;
02143 
02144     if (!generate_index)
02145         return strdup("");
02146 
02147     if (vars)
02148         for (basevalues = 0; vars[basevalues] != NULL; basevalues++)
02149             ;
02150     else
02151         basevalues = 0;
02152 
02153     idx_vars = malloc(sizeof(char *)*(basevalues+VARSADD));
02154     idx_vars[0] = "MAPCOUNT";
02155     memcpy(&idx_vars[1], vars, sizeof(char *)*basevalues);
02156     idx_vars[basevalues+VARSADD-1] = NULL;
02157 
02158     idx_values = malloc(sizeof(char *)*(basevalues+VARSADD-1));
02159     memcpy(&idx_values[1], values, sizeof(char *)*basevalues);
02160 
02161     string = NULL;
02162 
02163     idx_values[0] = count;
02164     /* wrong value, but in case the template needs to display something... */
02165     snprintf(count, sizeof(count), "%d", maps_list->count);
02166 
02167     idx_vars[basevalues+1] = "MAPNAME";
02168     idx_vars[basevalues+2] = "MAPPATH";
02169     idx_vars[basevalues+3] = "MAPHTML";
02170     idx_vars[basevalues+4] = NULL;
02171 
02172     qsort(maps_list->maps, maps_list->count, sizeof(const char *), sort_map_info);
02173 
02174     last_letter = '\0';
02175     str_letter[0] = '\0';
02176     str_letter[1] = '\0';
02177 
02178     strcpy(index_path, "/");
02179     strcat(index_path, dest);
02180 
02181     string = NULL;
02182     for (map = 0; map < maps_list->count; map++) {
02183         if (tolower(maps_list->maps[map]->name[0]) != last_letter) {
02184             if (mapstext != NULL) {
02185                 idx_vars[basevalues+1] = "MAPS";
02186                 idx_vars[basevalues+2] = "LETTER";
02187                 idx_vars[basevalues+3] = "LETTERCOUNT";
02188                 idx_vars[basevalues+4] = NULL;
02189                 idx_values[basevalues+1] = mapstext;
02190                 idx_values[basevalues+2] = str_letter;
02191                 snprintf(lettercount, sizeof(lettercount), "%d", byletter);
02192                 idx_values[basevalues+3] = lettercount;
02193                 string = cat_template(string, do_template(template_letter, idx_vars, idx_values));
02194                 free(mapstext);
02195                 mapstext = NULL;
02196                 idx_values[basevalues+2] = NULL;
02197             }
02198             last_letter = tolower(maps_list->maps[map]->name[0]);
02199             str_letter[0] = last_letter;
02200             byletter = 0;
02201             last_group = NULL;
02202         }
02203 
02204         if (last_group && last_group == maps_list->maps[map]->tiled_group)
02205             continue;
02206         else
02207             last_group = maps_list->maps[map]->tiled_group;
02208 
02209         realcount++;
02210         idx_vars[basevalues+1] = "MAPNAME";
02211         idx_vars[basevalues+2] = "MAPPATH";
02212         idx_vars[basevalues+3] = "MAPHTML";
02213         idx_values[basevalues+1] = last_group ? last_group->name : (maps_list->maps[map]->name ? maps_list->maps[map]->name : maps_list->maps[map]->path);
02214         relative_path(index_path, last_group ? last_group->path : maps_list->maps[map]->path, mappath);
02215         strcpy(maphtml, mappath);
02216         strcat(maphtml, ".html");
02217         idx_values[basevalues+2] = mappath;
02218         idx_values[basevalues+3] = maphtml;
02219         mapstext = cat_template(mapstext, do_template(template_map, idx_vars, idx_values));
02220         byletter++;
02221     }
02222     if (last_letter != '\0') {
02223         idx_vars[basevalues+1] = "MAPS";
02224         idx_vars[basevalues+2] = "LETTER";
02225         idx_vars[basevalues+3] = "LETTERCOUNT";
02226         idx_vars[basevalues+4] = NULL;
02227         idx_values[basevalues+1] = mapstext;
02228         idx_values[basevalues+2] = str_letter;
02229         snprintf(lettercount, sizeof(lettercount), "%d", byletter);
02230         idx_values[basevalues+3] = lettercount;
02231         string = cat_template(string, do_template(template_letter, idx_vars, idx_values));
02232         free(mapstext);
02233         mapstext = NULL;
02234         idx_values[basevalues+2] = NULL;
02235     }
02236 
02237     snprintf(count, sizeof(count), "%d", realcount);
02238     idx_values[basevalues+1] = string;
02239     idx_vars[basevalues+1] = "LETTERS";
02240     idx_vars[basevalues+2] = NULL;
02241     tmp = do_template(template_page, idx_vars, idx_values);
02242     free(string);
02243     free(idx_vars);
02244     free(idx_values);
02245     return tmp;
02246 }
02247 
02257 static void write_region_page(struct_region_info *reg) {
02258     char *string;
02259     FILE *index;
02260     char html[500];
02261     const char *vars[] = { "REGIONNAME", "REGIONHTML", "REGIONLONGNAME", "REGIONDESC", NULL };
02262     const char *values[] = { reg->reg->name, html, NULL, NULL };
02263 
02264     printf("Generating map index for region %s...", reg->reg->name);
02265 
02266     values[2] = get_region_longname(reg->reg);
02267     values[3] = get_region_msg(reg->reg);
02268 
02269     strcpy(html, reg->reg->name);
02270     strcat(html, ".html");
02271 
02272     string = do_map_index(html, &reg->maps_list, region_template, region_letter_template, region_map_template, vars, values);
02273 
02274     strcpy(html, root);
02275     strcat(html, "/");
02276     strcat(html, reg->reg->name);
02277     strcat(html, ".html");
02278     index = fopen(html, "w+");
02279     fprintf(index, string);
02280     fclose(index);
02281     free(string);
02282 
02283     printf(" done.\n");
02284 
02285 }
02286 
02290 static void write_all_regions(void) {
02291     int reg;
02292 
02293     qsort(regions, region_count, sizeof(struct_region_info *), sort_region);
02294 
02295     for (reg = 0; reg < region_count; reg++)
02296         write_region_page(regions[reg]);
02297 }
02298 
02302 static void write_maps_index(void) {
02303     char index_path[500];
02304     char *tmp;
02305     FILE *index;
02306 
02307     printf("Generating global map index in maps.html...");
02308 
02309     tmp = do_map_index("maps.html", &maps_list, index_template, index_letter, index_map, NULL, NULL);
02310 
02311     strcpy(index_path, root);
02312     strcat(index_path, "/maps.html");
02313     index = fopen(index_path, "w+");
02314     fprintf(index, tmp);
02315     fclose(index);
02316     free(tmp);
02317 
02318     printf(" done.\n");
02319 }
02320 
02324 static void write_region_index(void) {
02325     char *txt;
02326     char *final;
02327     char count[10];
02328     struct_region_info *region;
02329     int reg;
02330     char file[500];
02331     const char *vars[] = { "REGIONCOUNT", "REGIONFILE", "REGIONNAME", NULL };
02332     const char *values[] = { count, file, NULL };
02333     FILE *out;
02334 
02335     printf("Generating regions index in regions.html...");
02336 
02337     snprintf(count, sizeof(count), "%d", region_count);
02338     txt = NULL;
02339 
02340     for (reg = 0; reg < region_count; reg++) {
02341         region = regions[reg];
02342         snprintf(file, sizeof(file), "%s.html", region->reg->name);
02343         values[2] = get_region_longname(region->reg);
02344         txt = cat_template(txt, do_template(index_region_region_template, vars, values));
02345     }
02346     vars[1] = "REGIONS";
02347     values[1] = txt;
02348     vars[2] = NULL;
02349     final = do_template(index_region_template, vars, values);
02350     free(txt);
02351 
02352     strcpy(file, root);
02353     strcat(file, "/regions.html");
02354     out = fopen(file, "w+");
02355     fprintf(out, final);
02356     fclose(out);
02357     free(final);
02358 
02359     printf(" done.\n");
02360 }
02361 
02365 static void write_world_map(void) {
02366 #define SIZE 50
02367     int x, y;
02368     FILE *out;
02369     int wx, wy;
02370     char file[500];
02371     char *map = NULL;
02372     char *total;
02373     char *row = NULL;
02374     char mapleft[10], maptop[10], mapright[10], mapbottom[10];
02375     const char *vars[] = { NULL, NULL, "MAPLEFT", "MAPTOP", "MAPRIGHT", "MAPBOTTOM", NULL };
02376     const char *values[] = { NULL, NULL, mapleft, maptop, mapright, mapbottom, NULL };
02377     char name[100];
02378     char mappath[500], mapraw[500], mapregion[500];
02379     gdImagePtr pic;
02380     gdImagePtr small;
02381     gdFontPtr font;
02382     int region, color;
02383 
02384     if (!world_map)
02385         return;
02386 
02387     printf("Generating world map in world.html...");
02388     fflush(stdout);
02389 
02390     pic = gdImageCreateTrueColor(SIZE*30, SIZE*30);
02391 
02392     strcpy(file, root);
02393     strcat(file, "/world.html");
02394 
02395     wx = 100;
02396     wy = 100;
02397 
02398     for (y = 0; y < 30; y++) {
02399         for (x = 0; x < 30; x++) {
02400             values[0] = name;
02401             vars[0] = "MAPNAME";
02402             vars[1] = "MAPPATH";
02403             values[1] = mappath,
02404             snprintf(name, sizeof(name), "world_%d_%d", wx, wy);
02405             snprintf(mappath, sizeof(mappath), "world/%s.html", name);
02406             snprintf(mapleft, sizeof(mapleft), "%d", SIZE*x);
02407             snprintf(maptop, sizeof(maptop), "%d", SIZE*y);
02408             snprintf(mapright, sizeof(mapright), "%d", SIZE*(x+1)-1);
02409             snprintf(mapbottom, sizeof(mapbottom), "%d", SIZE*(y+1)-1);
02410 
02411             map = cat_template(map, do_template(world_map_template, vars, values));
02412 
02413             snprintf(mappath, sizeof(mappath), "%s/world/%s%s", root, name, output_extensions[output_format]);
02414 
02415             out = fopen(mappath, "rb");
02416             if (!out) {
02417                 printf("\n  warning: large pic not found for world_%d_%d", wx, wy);
02418                 wx++;
02419                 continue;
02420             }
02421             if (output_format == OF_PNG)
02422                 small = gdImageCreateFromPng(out);
02423             else
02424                 small = gdImageCreateFromJpeg(out);
02425             fclose(out);
02426             if (!small) {
02427                 printf("\n  warning: pic not found for world_%d_%d", wx, wy);
02428                 wx++;
02429                 continue;
02430             }
02431             gdImageCopyResized(pic, small, SIZE*x, SIZE*y, 0, 0, SIZE, SIZE, small->sx, small->sy);
02432             gdImageDestroy(small);
02433 
02434             wx++;
02435         }
02436         wy++;
02437         wx = 100;
02438         values[0] = map;
02439         vars[0] = "MAPS";
02440         vars[1] = NULL;
02441         row = cat_template(row, do_template(world_row_template, vars, values));
02442         free(map);
02443         map = NULL;
02444     }
02445     snprintf(mappath, sizeof(mappath), "world%s", output_extensions[output_format]);
02446     snprintf(mapraw, sizeof(mapraw), "world_raw%s", output_extensions[output_format]);
02447     snprintf(mapregion, sizeof(mapregion), "world_regions%s", output_extensions[output_format]);
02448 
02449     values[0] = row;
02450     vars[0] = "MAPS";
02451     values[1] = mappath;
02452     vars[1] = "WORLDMAP";
02453     values[2] = mapraw;
02454     vars[2] = "WORLDRAW";
02455     values[3] = mapregion;
02456     vars[3] = "WORLDREGIONS";
02457     vars[4] = NULL;
02458     total = do_template(world_template, vars, values);
02459     free(row);
02460     out = fopen(file, "w+");
02461     fprintf(out, total);
02462     free(total);
02463     fclose(out);
02464 
02465     snprintf(mappath, sizeof(mappath), "%s/world_raw%s", root, output_extensions[output_format]);
02466     out = fopen(mappath, "wb+");
02467     save_picture(out, pic);
02468     fclose(out);
02469 
02470     /* Write region names. */
02471     small = gdImageCreateTrueColor(SIZE*30, SIZE*30);
02472     font = gdFontGetGiant();
02473     color = gdImageColorAllocateAlpha(pic, 255, 0, 0, 20);
02474     for (region = 0; region < region_allocated; region++) {
02475         if (!regions[region]->is_world || regions[region]->sum == 0)
02476             continue;
02477 
02478         x = regions[region]->sum_x*SIZE/regions[region]->sum+SIZE/2-strlen(regions[region]->reg->name)*font->w/2;
02479         y = regions[region]->sum_y*SIZE/regions[region]->sum+SIZE/2-font->h/2;
02480         gdImageString(small, font, x, y, regions[region]->reg->name, color);
02481         gdImageString(pic, font, x, y, regions[region]->reg->name, color);
02482 
02483         /* For exit/road map, size isn't the same. */
02484         x = regions[region]->sum_x*50/regions[region]->sum+50/2-strlen(regions[region]->reg->name)*font->w/2;
02485         y = regions[region]->sum_y*50/regions[region]->sum+50/2-font->h/2;
02486         gdImageString(infomap, font, x, y, regions[region]->reg->name, color);
02487     }
02488 
02489     snprintf(mappath, sizeof(mappath), "%s/world_regions%s", root, output_extensions[output_format]);
02490     out = fopen(mappath, "wb+");
02491     save_picture(out, small);
02492     fclose(out);
02493     gdImageDestroy(small);
02494 
02495     snprintf(mappath, sizeof(mappath), "%s/world%s", root, output_extensions[output_format]);
02496     out = fopen(mappath, "wb+");
02497     save_picture(out, pic);
02498     fclose(out);
02499     gdImageDestroy(pic);
02500 
02501     printf(" done.\n");
02502 #undef SIZE
02503 }
02504 
02511 static void write_map_page(struct_map_info *map) {
02512     char *exits_text;
02513     char *exits_to;
02514     char *maplore;
02515     char *tmp;
02516     char *quests, *quest;
02517     char *monsters;
02518 
02519     char htmlpath[500];         /* Map file path. */
02520     char mappic[500];           /* Name of map's full size picture. */
02521     char mapsmallpic[500];      /* Name of map's small size picture. */
02522     char indexpath[500];        /* Relative path of full index. */
02523     char regionpath[500];       /* Path to region's filename. */
02524     char regionname[500];       /* Name of map's region. */
02525     char regionindexpath[500];  /* Path to region index file. */
02526     char worldmappath[500];     /* Path to world map. */
02527     char exit_path[500];
02528     char maplevel[5], minmonster[5], maxmonster[5];
02529     FILE *out;
02530     char questpath[500], questtemp[500];
02531     const char *quest_vars[] = { "NAME", "PATH", "TEXT", NULL };
02532     const char *quest_vals[] = { NULL, questpath, NULL, NULL };
02533     const char *q_vars[] = { "QUESTS", NULL };
02534     const char *q_vals[] = { NULL, NULL };
02535     const char *m_vars[] = { "NAME", NULL };
02536     const char *m_vals[] = { NULL, NULL };
02537     const char *vars[] = { "NAME", "MAPPATH", "MAPNAME", "MAPPIC", "MAPSMALLPIC", "MAPEXITFROM", "INDEXPATH", "REGIONPATH", "REGIONNAME", "REGIONINDEXPATH", "WORLDMAPPATH", "MAPLORE", "MAPEXITTO", "MAPLEVEL", "QUESTS", "MONSTERS", "MINMONSTER", "MAXMONSTER", NULL, NULL, NULL };
02538     const char *values[] = { map->path, htmlpath, map->name, mappic, mapsmallpic, "", indexpath, regionpath, regionname, regionindexpath, worldmappath, "", "", maplevel, NULL, "", minmonster, maxmonster, NULL, NULL, NULL };
02539     int vars_count = 0;
02540 
02541     while (vars[vars_count])
02542         vars_count++;
02543 
02544     snprintf(minmonster, sizeof(minmonster), "%d", map->min_monster);
02545     snprintf(maxmonster, sizeof(maxmonster), "%d", map->max_monster);
02546 
02547     relative_path(map->path, "/maps.html", indexpath);
02548     relative_path(map->path, "/world.html", worldmappath);
02549     relative_path(map->path, "/regions.html", regionindexpath);
02550 
02551     if (map->cfregion) {
02552         strcpy(regionname, get_region_longname(map->cfregion));
02553         strcpy(exit_path, "/");
02554         strcat(exit_path, map->cfregion->name);
02555         strcat(exit_path, ".html");
02556         relative_path(map->path, exit_path, regionpath);
02557     } else {
02558         regionpath[0]='\0';
02559         snprintf(regionname, sizeof(regionname), "(map was not processed)");
02560     }
02561 
02562     snprintf(mappic, sizeof(mappic), "%s%s", map->filename, output_extensions[output_format]);
02563     snprintf(mapsmallpic, sizeof(mapsmallpic), "%s.small%s", map->filename, output_extensions[output_format]);
02564 
02565     snprintf(htmlpath, sizeof(htmlpath), "%s%s.html", root, map->path);
02566     make_path_to_file(htmlpath);
02567 
02568     values[14] = "";
02569 
02570     snprintf(maplevel, sizeof(maplevel), "%d", map->level);
02571     if (map->lore && map->lore[0] != '\0') {
02572         values[11] = map->lore;
02573         maplore = do_template(map_lore_template, vars, values);
02574     } else {
02575         maplore = do_template(map_no_lore_template, vars, values);
02576     }
02577     values[11] = maplore;
02578 
02579     if (map->exits_from.count) {
02580         char *one_exit = NULL;
02581         int exit;
02582         char relative[500];
02583 
02584         vars[vars_count] = "EXITNAME";
02585         vars[vars_count+1] = "EXITFILE";
02586 
02587         qsort(map->exits_from.maps, map->exits_from.count, sizeof(const char *), sort_map_info);
02588         for (exit = 0; exit < map->exits_from.count; exit++) {
02589             relative_path(map->path, map->exits_from.maps[exit]->path, relative);
02590             values[vars_count] = map->exits_from.maps[exit]->name;
02591             strcat(relative, ".html");
02592             values[vars_count+1] = relative;
02593             one_exit = cat_template(one_exit, do_template(map_exit_template, vars, values));
02594         }
02595         vars[vars_count] = "EXIT";
02596         vars[vars_count+1] = NULL;
02597         values[vars_count] = one_exit;
02598         exits_text = do_template(map_with_exit_template, vars, values);
02599         free(one_exit);
02600     }
02601     else
02602         exits_text = do_template(map_no_exit_template, vars, values);
02603 
02604     values[5] = exits_text;
02605 
02606     if (map->exits_to.count) {
02607         char *one_exit = NULL;
02608         int exit;
02609         char relative[500];
02610 
02611         vars[vars_count] = "EXITNAME";
02612         vars[vars_count+1] = "EXITFILE";
02613 
02614         qsort(map->exits_to.maps, map->exits_to.count, sizeof(struct_map_info *), sort_map_info);
02615         for (exit = 0; exit < map->exits_to.count; exit++) {
02616             relative_path(map->path, map->exits_to.maps[exit]->path, relative);
02617             values[vars_count] = map->exits_to.maps[exit]->name;
02618             strcat(relative, ".html");
02619             values[vars_count+1] = relative;
02620             one_exit = cat_template(one_exit, do_template(map_exit_to_template, vars, values));
02621         }
02622         vars[vars_count] = "EXIT";
02623         vars[vars_count+1] = NULL;
02624         values[vars_count] = one_exit;
02625         exits_to = do_template(map_with_exit_to_template, vars, values);
02626         free(one_exit);
02627     } else
02628         exits_to = do_template(map_no_exit_to_template, vars, values);
02629 
02630     values[12] = exits_to;
02631 
02632     if (map->quests.count) {
02633         int q;
02634 
02635         quest = NULL;
02636         for (q = 0; q < map->quests.count; q++) {
02637             quest_vals[0] = map->quests.list[q]->quest->name;
02638             relative_path(map->path, "/quests.html", questtemp);
02639             snprintf(questpath, sizeof(questpath), "%s#quest_%d", questtemp, map->quests.list[q]->quest->number);
02640             quest_vals[2] = map->quests.list[q]->description;
02641             quest = cat_template(quest, do_template(map_one_quest_template, quest_vars, quest_vals));
02642         }
02643 
02644         q_vals[0] = quest;
02645         quests = do_template(map_with_quests_template, q_vars, q_vals);
02646         free(quest);
02647         quest = NULL;
02648     } else
02649         quests = strdup(map_no_quest_template);
02650     values[14] = quests;
02651 
02652     if (map->monsters.count) {
02653         int m;
02654 
02655         qsort(map->monsters.races, map->monsters.count, sizeof(struct_race *), sort_race);
02656 
02657         monsters = do_template(map_monster_before_template, vars, values);
02658         for (m = 0; m < map->monsters.count; m++) {
02659             m_vals[0] = map->monsters.races[m]->name;
02660             monsters = cat_template(monsters, do_template(map_monster_one_template, m_vars, m_vals));
02661             if (m != map->monsters.count-1)
02662                 monsters = cat_template(monsters, do_template(map_monster_between_template, vars, values));
02663         }
02664         monsters = cat_template(monsters, do_template(map_monster_after_template, vars, values));
02665     } else
02666         monsters = do_template(map_no_monster_template, vars, values);
02667     values[15] = monsters;
02668 
02669     vars[vars_count] = NULL;
02670     out = fopen(htmlpath, "w+");
02671     tmp = do_template(map_template, vars, values);
02672     fprintf(out, tmp);
02673     fclose(out);
02674     free(tmp);
02675     free(exits_text);
02676     free(exits_to);
02677     free(maplore);
02678     free(quests);
02679     free(monsters);
02680 }
02681 
02683 static void fix_map_names(void) {
02684     int map;
02685 
02686     for (map = 0; map < maps_list.count; map++) {
02687         if (maps_list.maps[map]->name)
02688             continue;
02689         if (!maps_list.maps[map]->filename) {
02690             printf("map without path!\n");
02691             abort();
02692         }
02693         maps_list.maps[map]->name = strdup(maps_list.maps[map]->filename);
02694     }
02695 }
02696 
02703 static void fix_tiled_map(void) {
02704     int map, tile;
02705     char name[500];
02706     char *slash, *test;
02707     region *cfregion;
02708 
02709     for (map = 0; map < tiled_map_list.count; map++) {
02710         if (tiled_map_list.maps[map]->tiled_maps.count == 0) {
02711             printf("empty tiled map group!");
02712             abort();
02713         }
02714 
02715         snprintf(name, sizeof(name), "tiled_map_group_%d", map);
02716         tiled_map_list.maps[map]->filename = strdup(name);
02717 
02718         cfregion = NULL;
02719         test = NULL;
02720 
02721         for (tile = 0; tile < tiled_map_list.maps[map]->tiled_maps.count; tile++) {
02722             if (tiled_map_list.maps[map]->tiled_maps.maps[tile]->cfregion == NULL)
02723                 /* map not processed, ignore it. */
02724                 continue;
02725 
02726             if (!cfregion)
02727                 cfregion = tiled_map_list.maps[map]->tiled_maps.maps[tile]->cfregion;
02728             else if (cfregion != tiled_map_list.maps[map]->tiled_maps.maps[tile]->cfregion) {
02729                 printf("*** warning: tiled maps %s and %s not in same region (%s and %s).\n",
02730                     tiled_map_list.maps[map]->tiled_maps.maps[0]->path, tiled_map_list.maps[map]->tiled_maps.maps[tile]->path,
02731                     tiled_map_list.maps[map]->tiled_maps.maps[0]->cfregion->name, tiled_map_list.maps[map]->tiled_maps.maps[tile]->cfregion->name);
02732                 cfregion = NULL;
02733             }
02734 
02735             if (strcmp(tiled_map_list.maps[map]->tiled_maps.maps[tile]->name, tiled_map_list.maps[map]->tiled_maps.maps[tile]->filename)) {
02736                 /* map has a custom name, use it */
02737                 if (!test)
02738                     test = tiled_map_list.maps[map]->tiled_maps.maps[tile]->name;
02739             }
02740         }
02741 
02742         if (!test) {
02743             /* this can happen of course if only partial maps were processed, but well... */
02744             printf("*** warning: tiled map without any name. First map path %s\n", tiled_map_list.maps[map]->tiled_maps.maps[0]->path);
02745             test = name;
02746         }
02747 
02748         tiled_map_list.maps[map]->name = strdup(test);
02749         tiled_map_list.maps[map]->cfregion = cfregion;
02750 
02751         strncpy(name, tiled_map_list.maps[map]->tiled_maps.maps[0]->path, sizeof(name));
02752         slash = strrchr(name, '/');
02753         if (!slash)
02754             snprintf(name, sizeof(name), "/");
02755         else
02756             *(slash+1) = '\0';
02757         strncat(name, tiled_map_list.maps[map]->filename, sizeof(name));
02758         tiled_map_list.maps[map]->path = strdup(name);
02759     }
02760 }
02761 
02772 static void fix_exits_for_map(struct_map_info *current, struct_map_list *from, int is_from) {
02773     int map, max;
02774     struct_map_info *group;
02775 
02776     max = from->count-1;
02777     for (map = max; map >= 0; map--) {
02778         if (from->maps[map]->tiled_group) {
02779             group = from->maps[map]->tiled_group;
02780             if (map != max)
02781                 from->maps[map] = from->maps[max];
02782             from->count--;
02783             max--;
02784             add_map(group, from);
02785             add_map(current->tiled_group ? current->tiled_group : current, is_from ? &group->exits_to : &group->exits_from);
02786         }
02787     }
02788 }
02789 
02791 static void fix_exits_to_tiled_maps(void) {
02792     int map, region, max;
02793     struct_map_info *group;
02794 
02795     for (map = 0; map < maps_list.count; map++) {
02796         fix_exits_for_map(maps_list.maps[map], &maps_list.maps[map]->exits_from, 1);
02797         fix_exits_for_map(maps_list.maps[map], &maps_list.maps[map]->exits_to, 0);
02798     }
02799 
02800     for (region = 0; region < region_count; region++) {
02801         max = regions[region]->maps_list.count-1;
02802         for (map = max; map >= 0; map--) {
02803             if (regions[region]->maps_list.maps[map]->tiled_group) {
02804                 group = regions[region]->maps_list.maps[map]->tiled_group;
02805                 if (map != max)
02806                     regions[region]->maps_list.maps[map] = regions[region]->maps_list.maps[max];
02807                 regions[region]->maps_list.count--;
02808                 max--;
02809                 add_map(group, &regions[region]->maps_list);
02810             }
02811         }
02812     }
02813 }
02814 
02819 static void fix_tiled_map_monsters(void) {
02820     int map, race, max;
02821     struct_map_info *group;
02822 
02823     for (race = 0; race < races.count; race++) {
02824         max = races.races[race]->origin.count-1;
02825         for (map = max; map >= 0; map--) {
02826             if (races.races[race]->origin.maps[map]->tiled_group) {
02827                 group = races.races[race]->origin.maps[map]->tiled_group;
02828                 if (map != max)
02829                     races.races[race]->origin.maps[map] = races.races[race]->origin.maps[max];
02830                 races.races[race]->origin.count--;
02831                 max--;
02832                 add_map(group, &races.races[race]->origin);
02833             }
02834         }
02835     }
02836 
02837     for (map = 0; map < maps_list.count; map++) {
02838         if (maps_list.maps[map]->tiled_group) {
02839             for (race = 0; race < maps_list.maps[map]->monsters.count; race++) {
02840                 add_race_to_list(maps_list.maps[map]->monsters.races[race], &maps_list.maps[map]->tiled_group->monsters, 1);
02841             }
02842         }
02843     }
02844 }
02845 
02847 static void write_all_maps(void) {
02848     int map;
02849 
02850     printf("Writing map pages...");
02851 
02852     for (map = 0; map < maps_list.count; map++)
02853         if (!maps_list.maps[map]->tiled_group)
02854             write_map_page(maps_list.maps[map]);
02855 
02856     printf(" done.\n");
02857 }
02858 
02859 static int tiled_map_need_pic(struct_map_info *map) {
02860     int test;
02861     char picpath[500];
02862     struct stat stats;
02863 
02864     snprintf(picpath, sizeof(picpath), "%s%s%s", root, map->path, output_extensions[output_format]);
02865     if (stat(picpath, &stats))
02866         return 1;
02867 
02868     snprintf(picpath, sizeof(picpath), "%s%s.small%s", root, map->path, output_extensions[output_format]);
02869     if (stat(picpath, &stats))
02870         return 1;
02871 
02872     for (test = 0; test < map->tiled_maps.count; test++) {
02873         if (map->tiled_maps.maps[test]->pic_was_done)
02874             return 1;
02875     }
02876 
02877     return 0;
02878 }
02879 
02891 static void do_tiled_map_picture(struct_map_info *map) {
02892     int xmin = 0, xmax = 0, ymin = 0, ymax = 0, tiled, count, last;
02893     char picpath[500];
02894     gdImagePtr small, large, load;
02895     FILE *out;
02896     struct_map_info *current;
02897 
02898     if (!generate_pics)
02899         return;
02900 
02901     printf(" Generating composite map for %s...", map->name);
02902     fflush(stdout);
02903 
02904     if (!tiled_map_need_pic(map)) {
02905         printf(" already uptodate.\n");
02906         return;
02907     }
02908 
02909     count = map->tiled_maps.count;
02910     if (count == 0) {
02911         printf("Tiled map without tiled maps?\n");
02912         abort();
02913     }
02914     map->tiled_maps.maps[0]->processed = 1;
02915     map->tiled_maps.maps[0]->tiled_x_from = 0;
02916     map->tiled_maps.maps[0]->tiled_y_from = 0;
02917 
02918     while (count > 0) {
02919         last = count;
02920 
02921         for (tiled = 0; tiled < map->tiled_maps.count; tiled++) {
02922             current = map->tiled_maps.maps[tiled];
02923             if (current->processed != 1)
02924                 continue;
02925 
02926             count--;
02927 
02928             if ((current->tiles[0]) && (current->tiles[0]->processed == 0)) {
02929                 current->tiles[0]->processed = 1;
02930                 current->tiles[0]->tiled_x_from = current->tiled_x_from;
02931                 current->tiles[0]->tiled_y_from = current->tiled_y_from-current->tiles[0]->height;
02932             }
02933             if ((current->tiles[1]) && (current->tiles[1]->processed == 0)) {
02934                 current->tiles[1]->processed = 1;
02935                 current->tiles[1]->tiled_x_from = current->tiled_x_from+current->width;
02936                 current->tiles[1]->tiled_y_from = current->tiled_y_from;
02937             }
02938             if ((current->tiles[2]) && (current->tiles[2]->processed == 0)) {
02939                 current->tiles[2]->processed = 1;
02940                 current->tiles[2]->tiled_x_from = current->tiled_x_from;
02941                 current->tiles[2]->tiled_y_from = current->tiled_y_from+current->height;
02942             }
02943             if ((current->tiles[3]) && (current->tiles[3]->processed == 0)) {
02944                 current->tiles[3]->processed = 1;
02945                 current->tiles[3]->tiled_x_from = current->tiled_x_from-current->tiles[3]->width;
02946                 current->tiles[3]->tiled_y_from = current->tiled_y_from;
02947             }
02948         }
02949 
02950         if (last == count) {
02951             printf("do_tiled_map_picture: didn't process any map in %s (%d left)??\n", map->path, last);
02952             abort();
02953         }
02954     }
02955 
02956     for (tiled = 0; tiled < map->tiled_maps.count; tiled++) {
02957         if (map->tiled_maps.maps[tiled]->tiled_x_from < xmin)
02958             xmin = map->tiled_maps.maps[tiled]->tiled_x_from;
02959         if (map->tiled_maps.maps[tiled]->tiled_y_from < ymin)
02960             ymin = map->tiled_maps.maps[tiled]->tiled_y_from;
02961         if (map->tiled_maps.maps[tiled]->tiled_x_from+map->tiled_maps.maps[tiled]->width > xmax)
02962             xmax = map->tiled_maps.maps[tiled]->tiled_x_from+map->tiled_maps.maps[tiled]->width;
02963         if (map->tiled_maps.maps[tiled]->tiled_y_from+map->tiled_maps.maps[tiled]->height > ymax)
02964             ymax = map->tiled_maps.maps[tiled]->tiled_y_from+map->tiled_maps.maps[tiled]->height;
02965     }
02966 
02967     large = gdImageCreateTrueColor(32*(xmax-xmin), 32*(ymax-ymin));
02968     small = gdImageCreateTrueColor(size_small*(xmax-xmin), size_small*(ymax-ymin));
02969 
02970     for (tiled = 0; tiled < map->tiled_maps.count; tiled++) {
02971         snprintf(picpath, sizeof(picpath), "%s%s%s", root, map->tiled_maps.maps[tiled]->path, output_extensions[output_format]);
02972 
02973         out = fopen(picpath, "rb");
02974         if (!out) {
02975             printf("\n  do_tiled_map_picture: warning: pic file not found for %s (errno=%d)\n", map->tiled_maps.maps[tiled]->path, errno);
02976             continue;
02977         }
02978         if (output_format == OF_PNG)
02979             load = gdImageCreateFromPng(out);
02980         else
02981             load = gdImageCreateFromJpeg(out);
02982         fclose(out);
02983         if (!load) {
02984             printf("\n  do_tiled_map_picture: warning: pic not found for %s\n", map->tiled_maps.maps[tiled]->path);
02985             continue;
02986         }
02987         gdImageCopy(large, load, 32*(map->tiled_maps.maps[tiled]->tiled_x_from-xmin), 32*(map->tiled_maps.maps[tiled]->tiled_y_from-ymin), 0, 0, load->sx, load->sy);
02988         gdImageDestroy(load);
02989 
02990         snprintf(picpath, sizeof(picpath), "%s%s.small%s", root, map->tiled_maps.maps[tiled]->path, output_extensions[output_format]);
02991         out = fopen(picpath, "rb");
02992         if (output_format == OF_PNG)
02993             load = gdImageCreateFromPng(out);
02994         else
02995             load = gdImageCreateFromJpeg(out);
02996         fclose(out);
02997         if (!load) {
02998             printf("\n  do_tiled_map_picture: warning: small pic not found for %s\n", map->tiled_maps.maps[tiled]->path);
02999             continue;
03000         }
03001         gdImageCopy(small, load, size_small*(map->tiled_maps.maps[tiled]->tiled_x_from-xmin), size_small*(map->tiled_maps.maps[tiled]->tiled_y_from-ymin), 0, 0, load->sx, load->sy);
03002         gdImageDestroy(load);
03003     }
03004 
03005     snprintf(picpath, sizeof(picpath), "%s%s%s", root, map->path, output_extensions[output_format]);
03006     out = fopen(picpath, "wb+");
03007     save_picture(out, large);
03008     fclose(out);
03009 
03010     snprintf(picpath, sizeof(picpath), "%s%s.small%s", root, map->path, output_extensions[output_format]);
03011     out = fopen(picpath, "wb+");
03012     save_picture(out, small);
03013     fclose(out);
03014 
03015     gdImageDestroy(small);
03016     gdImageDestroy(large);
03017 
03018     printf(" done.\n");
03019 }
03020 
03022 static void write_tiled_map_page(struct_map_info *map) {
03023 
03024     do_tiled_map_picture(map);
03025 
03028     write_map_page(map);
03029 }
03030 
03032 static void write_tiled_maps(void) {
03033     int map;
03034 
03035     printf("Writing tiled map information...\n");
03036 
03037     for (map = 0; map < tiled_map_list.count; map++)
03038         write_tiled_map_page(tiled_map_list.maps[map]);
03039 
03040         printf(" done.\n");
03041 }
03042 
03044 static void write_maps_by_level(void) {
03045     int map;
03046     FILE *out;
03047     char name[500];
03048     char mappath[500];
03049     char *letters = NULL;
03050     char *maps = NULL;
03051     char *level = NULL;
03052     int lastlevel = -1;
03053     char strlevel[10];
03054     char strcount[10];
03055     const char *val_vars[] = { "LEVEL", "MAPS", NULL };
03056     const char *val_values[] = { strlevel, NULL, NULL };
03057     const char *map_vars[] = { "MAPNAME", "MAPPATH", NULL };
03058     const char *map_values[] = { NULL, mappath, NULL };
03059     const char *idx_vars[] = { "COUNT", "LEVELS", NULL };
03060     const char *idx_values[] = { strcount, NULL, NULL };
03061     int levelcount = 0;
03062     struct_map_info *last_tiled = NULL;
03063     struct_map_info *process;
03064 
03065     printf("Writing map index by level...");
03066 
03067     snprintf(name, sizeof(name), "%s/index_by_level.html", root);
03068 
03069     qsort(maps_list.maps, maps_list.count, sizeof(struct_map_info *), sort_map_info_by_level);
03070 
03071     for (map = 0; map < maps_list.count; map++) {
03072         process = maps_list.maps[map];
03073         if (maps_list.maps[map]->level != lastlevel) {
03074             if (maps) {
03075                 snprintf(strlevel, sizeof(strlevel), "%d", lastlevel);
03076                 val_values[1] = maps;
03077                 letters = cat_template(letters, do_template(level_value_template, val_vars, val_values));
03078                 free(maps);
03079                 maps = NULL;
03080             }
03081             lastlevel = process->level;
03082             levelcount++;
03083             last_tiled = NULL;
03084         } else
03085             if (last_tiled && last_tiled == process->tiled_group)
03086                 /* Group maps of same tiled group and level, but make them appear in different levels if applicable. */
03087                 continue;
03088 
03089         if (process->tiled_group) {
03090             process = process->tiled_group;
03091             last_tiled = process;
03092         } else
03093             last_tiled = process->tiled_group;
03094 
03095         map_values[0] = process->name;
03096         snprintf(mappath, sizeof(mappath), "%s.html", process->path+1); /* don't want the leading / */
03097         maps = cat_template(maps, do_template(level_map_template, map_vars, map_values));
03098     }
03099 
03100     snprintf(strlevel, sizeof(strlevel), "%d", lastlevel);
03101     val_values[1] = maps;
03102     letters = cat_template(letters, do_template(level_value_template, val_vars, val_values));
03103     free(maps);
03104     maps = NULL;
03105 
03106     snprintf(strcount, sizeof(strcount), "%d", levelcount);
03107     idx_values[1] = letters;
03108     level = do_template(level_template, idx_vars, idx_values);
03109     free(letters);
03110 
03111     out = fopen(name, "w+");
03112     fprintf(out, level);
03113     fclose(out);
03114     free(level);
03115 
03116     printf(" done.\n");
03117 }
03118 
03122 static void write_equipment_index(void) {
03123     int item, map;
03124     FILE *out;
03125     char name[500];
03126 
03127     printf("Generating special equipment list..");
03128     fflush(stdout);
03129 
03130     qsort(special_equipment, equipment_count, sizeof(struct_equipment *), sort_equipment);
03131 
03132     snprintf(name, sizeof(name), "%s/items.html", root);
03133     out = fopen(name, "w+");
03134 
03135     fprintf(out, "<html><head><title>Item list</title></head></body><h1>Special items found in maps</h1>\n");
03136     fprintf(out, "<table border=\"1\"><tr><th>Name</th><th>Map(s)</th><th>Item power</th><th>Calc item power</th><th>Description</th></tr>\n");
03137 
03138     for (item = 0; item < equipment_count; item++) {
03139         fprintf(out, "<tr><td>%s</td><td><ul>", special_equipment[item]->name);
03140 
03141         for (map = 0; map < special_equipment[item]->origin.count; map++)
03142             fprintf(out, "<li>%s</li>\n", special_equipment[item]->origin.maps[map]->path);
03143 
03144         fprintf(out, "</ul></td><td>%d</td><td>%d</td><td><pre>%s</pre></td></tr>\n", special_equipment[item]->power, special_equipment[item]->calc_power, special_equipment[item]->diff);
03145     }
03146     fprintf(out, "</body></html>\n");
03147     fclose(out);
03148 
03149     printf(" done.\n");
03150 }
03151 
03155 static void write_race_index(void) {
03156     int item, map;
03157     FILE *out;
03158     char name[500];
03159 
03160     printf("Generating monster list...");
03161     fflush(stdout);
03162 
03163     qsort(races.races, races.count, sizeof(struct_race *), sort_race);
03164 
03165     snprintf(name, sizeof(name), "%s/monsters.html", root);
03166     out = fopen(name, "w+");
03167 
03168     fprintf(out, "<html><head><title>Monster list</title></head></body><h1>Monsters found in maps</h1>\n");
03169     fprintf(out, "<table border=\"1\"><tr><th>Name</th><th>Count</th><th>Map(s)</th></tr>\n");
03170 
03171     for (item = 0; item < races.count; item++) {
03172         fprintf(out, "<tr><td>%s</td><td>%d</td><td>Found on %d maps:<ul>", races.races[item]->name, races.races[item]->count, races.races[item]->origin.count);
03173 
03174         qsort(races.races[item]->origin.maps, races.races[item]->origin.count, sizeof(struct_map_info *), sort_map_info);
03175 
03176         for (map = 0; map < races.races[item]->origin.count; map++)
03177             fprintf(out, "<li>%s</li>\n", races.races[item]->origin.maps[map]->path);
03178 
03179         fprintf(out, "</ul></td></tr>\n");
03180     }
03181     fprintf(out, "</body></html>\n");
03182     fclose(out);
03183 
03184     printf(" done.\n");
03185 }
03186 
03188 static const char *ignore_path[] = {
03189     "/Info",
03190     "/editor",
03191     "/python",
03192     "/styles",
03193     "/templates",
03194     "/test",
03195     "/unlinked",
03196     NULL };
03197 
03199 static const char *ignore_name[] = {
03200     ".",
03201     "..",
03202     ".svn",
03203     "README",
03204     NULL };
03205 
03212 static void find_maps(const char *from) {
03213     struct dirent *file;
03214     struct stat statbuf;
03215     int status, ignore;
03216     char path[1024], full[1024];
03217     DIR *dir;
03218 
03219     for (ignore = 0; ignore_path[ignore] != NULL; ignore++) {
03220         if (strcmp(from, ignore_path[ignore]) == 0)
03221             return;
03222     }
03223 
03224     snprintf(path, sizeof(path), "%s/%s%s", settings.datadir, settings.mapdir, from);
03225     dir = opendir(path);
03226 
03227     if (dir) {
03228         for (file = readdir(dir); file; file = readdir(dir)) {
03229 
03230             for (ignore = 0; ignore_name[ignore] != NULL; ignore++) {
03231                 if (strcmp(file->d_name, ignore_name[ignore]) == 0)
03232                     break;
03233             }
03234             if (ignore_name[ignore] != NULL)
03235                 continue;
03236 
03237             snprintf(full, sizeof(full), "%s/%s", path, file->d_name);
03238 
03239             status = stat(full, &statbuf);
03240             if ((status != -1) && (S_ISDIR(statbuf.st_mode))) {
03241                 snprintf(full, sizeof(full), "%s/%s", from, file->d_name);
03242                 find_maps(full);
03243                 continue;
03244             }
03245             if (found_maps_count == found_maps_allocated) {
03246                 found_maps_allocated += 50;
03247                 found_maps = realloc(found_maps, found_maps_allocated*sizeof(char *));
03248             }
03249             snprintf(full, sizeof(full), "%s/%s", from, file->d_name);
03250             found_maps[found_maps_count++] = strdup(full);
03251         }
03252         closedir(dir);
03253     }
03254 }
03255 
03257 static void dump_unused_maps(void) {
03258     FILE *dump;
03259     char path[1024];
03260     int index, found = 0;
03261 
03262     snprintf(path, sizeof(path), "%s/%s", root, "maps.unused");
03263     dump = fopen(path, "w+");
03264     if (dump == NULL) {
03265         printf("Unable to open file maps.unused!\n");
03266         return;
03267     }
03268     for (index = 0; index < found_maps_count; index++) {
03269         if (found_maps[index] != NULL) {
03270             fprintf(dump, "%s\n", found_maps[index]);
03271             free(found_maps[index]);
03272             found++;
03273         }
03274     }
03275     fclose(dump);
03276     printf("%d unused maps.\n", found);
03277 }
03278 
03280 static void write_world_info(void) {
03281     FILE *file;
03282     char path[MAX_BUF];
03283     int x, y;
03284     gdImagePtr elevationmap;
03285 
03286     if (!world_exit_info)
03287         return;
03288 
03289     printf("Saving exit/blocking/road information...");
03290     snprintf(path, sizeof(path), "%s/%s%s", root, "world_info", output_extensions[output_format]);
03291     file = fopen(path, "wb+");
03292     save_picture(file, infomap);
03293     fclose(file);
03294     printf("done.\n");
03295     gdImageDestroy(infomap);
03296     infomap = NULL;
03297 
03298    if (elevation_min == 0 || elevation_max == 0) {
03299        puts("Error: Could not save elevation world map due to not finding any minimum or maximum elevation.");
03300        return;
03301    }
03302 
03303     elevationmap = gdImageCreateTrueColor(30*50, 30*50);;
03304 
03305     for (x = 0; x < 30*50; x++) {
03306         for (y = 0; y < 30*50; y++) {
03307             gdImageSetPixel(elevationmap, x, y, get_elevation_color(elevation_info[x][y], elevationmap));
03308         }
03309     }
03310 
03311     printf("Saving elevation world map...");
03312     snprintf(path, sizeof(path), "%s/%s%s", root, "world_elevation", output_extensions[output_format]);
03313     file = fopen(path, "wb+");
03314     save_picture(file, elevationmap);
03315     fclose(file);
03316     printf("done.\n");
03317     gdImageDestroy(elevationmap);
03318     elevationmap = NULL;
03319 }
03320 
03322 static void write_regions_link(void) {
03323     FILE *file;
03324     char path[MAX_BUF];
03325     int link;
03326 
03327     if (!do_regions_link)
03328         return;
03329 
03330     printf("Writing regions link file...");
03331     snprintf(path, sizeof(path), "%s/%s", root, "region_links.dot");
03332     file = fopen(path, "wb+");
03333     fprintf(file, "digraph {\n");
03334     for (link = 0; link < regions_link_count; link++)
03335         fprintf(file, regions_link[link]);
03336     fprintf(file, "}\n");
03337     fclose(file);
03338     printf("done.\n");
03339 }
03340 
03349 static void write_slaying_map_name(FILE *file, struct_map_info *map) {
03350     fprintf(file, "<a href=\"%s.html\">%s</a> (full map path: %s)", map->tiled_group ? map->tiled_group->path+1 : map->path+1, map->name, map->path);
03351 }
03352 
03367 static void write_one_slaying_info(FILE *file, struct_slaying_info *info, int item, const char *with, const char *without) {
03368     int map;
03369 
03370     if (info->maps[item].count == 0) {
03371         if (without)
03372             fprintf(file, without);
03373         return;
03374     }
03375 
03376     qsort(info->maps[item].maps, info->maps[item].count, sizeof(const char *), sort_mapname);
03377 
03378     fprintf(file, with);
03379     fprintf(file, "<ul>\n");
03380     for (map = 0; map < info->maps[item].count; map++) {
03381         fprintf(file, "\t<li>");
03382         write_slaying_map_name(file, info->maps[item].maps[map]);
03383         fprintf(file, "</li>\n");
03384     }
03385     fprintf(file, "</ul>\n");
03386 }
03387 
03398 static int sort_slaying(const void *left, const void *right) {
03399     struct_slaying_info *l = *(struct_slaying_info **)left;
03400     struct_slaying_info *r = *(struct_slaying_info **)right;
03401 
03402     return strcasecmp(l->slaying, r->slaying);
03403 }
03404 
03408 static void write_slaying_info(void) {
03409     FILE *file;
03410     char path[MAX_BUF];
03411     int lock;
03412     struct_slaying_info *info;
03413 
03414     printf("Writing slaying info file...");
03415 
03416     qsort(slaying_info, slaying_count, sizeof(struct_slaying_info *), sort_slaying);
03417 
03418     snprintf(path, sizeof(path), "%s/%s", root, "slaying_info.html");
03419     file = fopen(path, "wb+");
03420 
03421     fprintf(file, "<html>\n<head>\n<title>Slaying information</title>\n</head>\n<body>\n");
03422     fprintf(file, "<p>This is a list of various slaying fields on keys, containers, doors, detectors.</p>");
03423 
03424     for (lock = 0; lock < slaying_count; lock++) {
03425         info = slaying_info[lock];
03426         fprintf(file, "<h1>%s</h1>\n", info->slaying);
03427 
03428         if (info->maps[S_DOOR].count == 0 && info->maps[S_CONTAINER].count == 0 && info->maps[S_CONNECT].count == 0) {
03429             fprintf(file, "No door, container or detector matching this slaying.<br />\n");
03430         } else {
03431             write_one_slaying_info(file, info, S_DOOR, "Connected doors:\n", NULL);
03432             write_one_slaying_info(file, info, S_CONTAINER, "Matching containers:\n", NULL);
03433             write_one_slaying_info(file, info, S_CONNECT, "Detectors and such:\n", NULL);
03434         }
03435         write_one_slaying_info(file, info, S_KEY, "Matching keys:\n", "No key with this slaying.<br />\n");
03436     }
03437 
03438     fprintf(file, "</body>\n</html>\n");
03439 
03440     fclose(file);
03441     printf("done.\n");
03442 }
03443 
03447 static void write_npc_list(void) {
03448     FILE *file;
03449     char path[MAX_BUF];
03450     int map, npc;
03451 
03452     printf("Writing NPC info file...");
03453 
03454     qsort(slaying_info, slaying_count, sizeof(struct_slaying_info *), sort_slaying);
03455 
03456     snprintf(path, sizeof(path), "%s/%s", root, "npc_info.html");
03457     file = fopen(path, "wb+");
03458 
03459     fprintf(file, "<html>\n<head>\n<title>NPCs who have a special message</title>\n</head>\n<body>\n");
03460     fprintf(file, "<p>This is a list of NPCs having a special message.</p>");
03461     fprintf(file, "<ul>\n");
03462 
03463     for (map = 0; map < maps_list.count; map++) {
03464         if (maps_list.maps[map]->npcs.count == 0)
03465             continue;
03466         fprintf(file, "<li>%s</li>\n<ul>", maps_list.maps[map]->path);
03467         for (npc = 0; npc < maps_list.maps[map]->npcs.count; npc++) {
03468             fprintf(file, "<li>%s (%d,%d): <br /><pre>%s</pre></li>\n", maps_list.maps[map]->npcs.npc[npc]->name, maps_list.maps[map]->npcs.npc[npc]->x, maps_list.maps[map]->npcs.npc[npc]->y, maps_list.maps[map]->npcs.npc[npc]->message);
03469         }
03470         fprintf(file, "</ul>\n</li>\n");
03471     }
03472 
03473     fprintf(file, "</ul>\n");
03474     fprintf(file, "</body>\n</html>\n");
03475 
03476     fclose(file);
03477     printf("done.\n");
03478 }
03479 
03486 static void do_help(const char *program) {
03487     printf("Crossfire Mapper will generate pictures of maps, and create indexes for all maps and regions.\n\n");
03488     printf("Syntax: %s\n\n", program);
03489     printf("Optional arguments:\n");
03490     printf("  -nopics             don't generate pictures.\n");
03491     printf("  -noindex            don't generate global map index.\n");
03492     printf("  -root=<path>        destination path. Default 'html'.\n");
03493     printf("  -limit=<number>     stop processing after this number of maps, -1 to do all maps (default).\n");
03494     printf("  -showmaps           outputs the name of maps as they are processed.\n");
03495     printf("  -jpg[=quality]      generate jpg pictures, instead of default png. Quality should be 0-95, -1 for automatic.\n");
03496     printf("  -forcepics          force to regenerate pics, even if pics's date is after map's.\n");
03497     printf("  -addmap=<map>       adds a map to process. Path is relative to map's directory root.\n");
03498     printf("  -rawmaps            generates maps pics without items on random (shop, treasure) tiles.\n");
03499     printf("  -warnnopath         inform when an exit has no path set.\n");
03500     printf("  -listunusedmaps     finds all unused maps in the maps directory.\n");
03501     printf("  -noworldmap         don't write the world map in world.png.\n");
03502     printf("  -noregionslink      don't generate regions relation file.\n");
03503     printf("  -regionslink        generate regions relation file.\n");
03504     printf("  -noexitmap          don't generate map of exits.\n");
03505     printf("  -exitmap            generate map of exits.\n");
03506     printf("  -tileset=<number>   use specified tileset to generate the pictures. Default 0 (standard).\n");
03507     printf("\n\n");
03508     exit(0);
03509 }
03510 
03519 static void do_parameters(int argc, char **argv) {
03520     int arg = 1;
03521     char path[500];
03522 
03523     root[0] = '\0';
03524 
03525     while (arg < argc) {
03526         if (strcmp(argv[arg], "-nopics") == 0)
03527             generate_pics = 0;
03528         else if (strcmp(argv[arg], "-noindex") == 0)
03529             generate_index = 0;
03530         else if (strncmp(argv[arg], "-root=", 6) == 0)
03531             strncpy(root, argv[arg]+6, 500);
03532         else if (strncmp(argv[arg], "-limit=", 7) == 0)
03533             map_limit = atoi(argv[arg]+7);
03534         else if (strcmp(argv[arg], "-showmaps") == 0)
03535             show_maps = 1;
03536         else if (strcmp(argv[arg], "-jpg") == 0) {
03537             output_format = OF_JPG;
03538             if (argv[arg][4] == '=') {
03539                 jpeg_quality = atoi(argv[arg]+5);
03540                 if (jpeg_quality < 0)
03541                     jpeg_quality = -1;
03542             }
03543         }
03544         else if (strcmp(argv[arg], "-forcepics") == 0)
03545             force_pics = 1;
03546         else if (strncmp(argv[arg], "-addmap=", 8) == 0) {
03547             if (*(argv[arg]+8) == '/')
03548                 strncpy(path, argv[arg]+8, 500);
03549             else
03550                 snprintf(path, 500, "/%s", argv[arg]+8);
03551             add_map(get_map_info(path), &maps_list);
03552         }
03553         else if (strcmp(argv[arg], "-rawmaps") == 0)
03554             rawmaps = 1;
03555         else if (strcmp(argv[arg], "-warnnopath") == 0)
03556             warn_no_path = 1;
03557         else if (strcmp(argv[arg], "-listunusedmaps") == 0)
03558             list_unused_maps = 1;
03559         else if (strcmp(argv[arg], "-noworldmap") == 0)
03560             world_map = 0;
03561         else if (strcmp(argv[arg], "-noregionslink") == 0)
03562             do_regions_link = 0;
03563         else if (strcmp(argv[arg], "-regionslink") == 0)
03564             do_regions_link = 1;
03565         else if (strcmp(argv[arg], "-noexitmap") == 0)
03566             world_exit_info = 0;
03567         else if (strcmp(argv[arg], "-exitmap") == 0)
03568             world_exit_info = 1;
03569         else if (strncmp(argv[arg], "-tileset=", 9) == 0) {
03570             tileset = atoi(argv[arg]+9);
03571             /* check of validity is done in main() as we need to actually have the sets loaded. */
03572         } else
03573             do_help(argv[0]);
03574         arg++;
03575     }
03576     if (!strlen(root))
03577         strcpy(root, "html");
03578     if (root[strlen(root)-1] == '/')
03579         root[strlen(root)-1] = '\0';
03580     if (map_limit < -1)
03581         map_limit = -1;
03582 }
03583 
03587 static void create_destination(void) {
03588     char dummy[502];
03589 
03590     strcpy(dummy, root);
03591     strcat(dummy, "/a");
03592     make_path_to_file(dummy);
03593 }
03594 
03603 static const char *yesno(int value) {
03604     return (value ? "yes" : "no");
03605 }
03606 
03607 int main(int argc, char **argv) {
03608     int current_map = 0, i;
03609     char max[50];
03610     region *dummy;
03611 
03612     init_map_list(&maps_list);
03613     init_map_list(&tiled_map_list);
03614     init_race_list(&races);
03615     pics_allocated = 0;
03616 
03617     do_parameters(argc, argv);
03618 
03619     printf("Initializing Crossfire data...\n");
03620 
03621     settings.debug = 0;
03622 
03623     init_globals();
03624     init_library();
03625     init_archetypes();
03626     init_artifacts();
03627     init_formulae();
03628     init_readable();
03629     init_regions();
03630 
03631     init_gods();
03632     read_client_images();
03633 
03634     /* Add a dummy region so unlinked maps can be identified. */
03635     dummy = get_region_struct();
03636     dummy->fallback = 1;
03637     dummy->name = add_string("unlinked");
03638     dummy->longname = add_string("This dummy region contains all maps without a region set.");
03639     dummy->longname = add_string("This dummy region contains all maps without a region set.");
03640     dummy->next = first_region;
03641     first_region = dummy;
03642 
03643     printf("\n\n done.\n\n");
03644 
03645     if (!is_valid_faceset(tileset)) {
03646         printf("Erreor: invalid tileset %d!\n", tileset);
03647         exit(1);
03648     }
03649 
03650     create_destination();
03651     gdfaces = calloc(1, sizeof(gdImagePtr)*nrofpixmaps);
03652 
03653     read_template("templates/map.template", &map_template);
03654     read_template("templates/map_no_exit.template", &map_no_exit_template);
03655     read_template("templates/map_with_exit.template", &map_with_exit_template);
03656     read_template("templates/map_exit.template", &map_exit_template);
03657     read_template("templates/map_no_exit_to.template", &map_no_exit_to_template);
03658     read_template("templates/map_with_exit_to.template", &map_with_exit_to_template);
03659     read_template("templates/map_exit_to.template", &map_exit_to_template);
03660     read_template("templates/map_lore.template", &map_lore_template);
03661     read_template("templates/map_no_lore.template", &map_no_lore_template);
03662     read_template("templates/map_no_monster.template", &map_no_monster_template);
03663     read_template("templates/map_monster_before.template", &map_monster_before_template);
03664     read_template("templates/map_monster_between.template", &map_monster_between_template);
03665     read_template("templates/map_monster_one.template", &map_monster_one_template);
03666     read_template("templates/map_monster_after.template", &map_monster_after_template);
03667 
03668     read_template("templates/index.template", &index_template);
03669     read_template("templates/index_letter.template", &index_letter);
03670     read_template("templates/index_map.template", &index_map);
03671 
03672     read_template("templates/region.template", &region_template);
03673     read_template("templates/region_letter.template", &region_letter_template);
03674     read_template("templates/region_map.template", &region_map_template);
03675 
03676     read_template("templates/index_region.template", &index_region_template);
03677     read_template("templates/index_region_region.template", &index_region_region_template);
03678 
03679     read_template("templates/world.template", &world_template);
03680     read_template("templates/world_row.template", &world_row_template);
03681     read_template("templates/world_map.template", &world_map_template);
03682 
03683     read_template("templates/level.template", &level_template);
03684     read_template("templates/level_value.template", &level_value_template);
03685     read_template("templates/level_map.template", &level_map_template);
03686 
03687     read_template("templates/quests.template", &index_quest_template);
03688     read_template("templates/quests_quest.template", &quest_template);
03689     read_template("templates/quests_map.template", &quest_map_template);
03690 
03691     read_template("templates/map_with_quests.template", &map_with_quests_template);
03692     read_template("templates/map_one_quest.template", &map_one_quest_template);
03693     read_template("templates/map_no_quest.template", &map_no_quest_template);
03694 
03695     if (map_limit != -1)
03696         snprintf(max, sizeof(max), "%d", map_limit);
03697     else
03698         strcpy(max, "(none)");
03699     printf("Crossfire map browser generator\n");
03700     printf("-------------------------------\n\n");
03701     printf("Parameters:\n");
03702     printf("  path to write files:                 %s\n", root);
03703     printf("  maximum number of maps to process:   %s\n", max);
03704     printf("  will generate map picture:           %s\n", yesno(generate_pics));
03705     printf("  will always generate map picture:    %s\n", yesno(force_pics));
03706     printf("  picture output format:               %s\n", output_extensions[output_format]);
03707     if (output_format == OF_JPG)
03708         printf("  JPEG quality:                        %d\n", jpeg_quality);
03709     printf("  will generate map index:             %s\n", yesno(generate_index));
03710     printf("  show map being processed:            %s\n", yesno(show_maps));
03711     printf("  generate raw maps:                   %s\n", yesno(rawmaps));
03712     printf("  warn of exit without path:           %s\n", yesno(warn_no_path));
03713     printf("  list unused maps:                    %s\n", yesno(list_unused_maps));
03714     printf("  generate world map:                  %s\n", yesno(world_map));
03715     printf("  generate exit map:                   %s\n", yesno(world_exit_info));
03716     printf("  generate regions link file:          %s\n", yesno(do_regions_link));
03717     printf("  tileset:                             %s\n", facesets[tileset].fullname);
03718     printf("\n");
03719 
03720     if (list_unused_maps) {
03721         printf("listing all maps...");
03722         find_maps("");
03723         printf("done, %d maps found.\n", found_maps_count);
03724         qsort(found_maps, found_maps_count, sizeof(char *), sortbyname);
03725     }
03726 
03727     /* exit/blocking information. */
03728     infomap = gdImageCreateTrueColor(30*50, 30*50);
03729     color_unlinked_exit = gdImageColorResolve(infomap, 255, 0, 0);
03730     color_linked_exit = gdImageColorResolve(infomap, 255, 255, 255);
03731     color_road = gdImageColorResolve(infomap, 0, 255, 0);
03732     color_blocking = gdImageColorResolve(infomap, 0, 0, 255);
03733     color_slowing = gdImageColorResolve(infomap, 0, 0, 127);
03734     elevation_info = calloc(50*30, sizeof(int *));
03735     for (i = 0; i < 50*30; i++)
03736         elevation_info[i] = calloc(50*30, sizeof(int));
03737     elevation_min = 0;
03738     elevation_max = 0;
03739 
03740     printf("browsing maps...\n");
03741 
03742     get_map_info(first_map_path);
03743 
03744     while (current_map < maps_list.count) {
03745         process_map(maps_list.maps[current_map++]);
03746         if (current_map%100 == 0) {
03747             printf(" %d maps processed, %d map pictures created, %d map pictures were uptodate. %d faces used.\n", current_map, created_pics, cached_pics, pics_allocated);
03748         }
03749         if ((map_limit != -1) && (current_map == map_limit)) {
03750             printf(" --- map limit reached, stopping ---\n");
03751             break;
03752         }
03753     }
03754 
03755     printf(" finished map parsing, %d maps processed, %d map pictures created, %d map pictures were uptodate. Total %d faces used.\n", current_map, created_pics, cached_pics, pics_allocated);
03756 
03757     if (list_unused_maps)
03758         dump_unused_maps();
03759 
03760     fix_exits_to_tiled_maps();
03761     fix_map_names();
03762     fix_tiled_map();
03763     fix_tiled_map_monsters();
03764 
03765     write_all_maps();
03766     write_maps_index();
03767     write_maps_by_level();
03768     write_tiled_maps();
03769 
03770     write_all_regions();
03771     write_region_index();
03772 
03773     write_world_map();
03774     write_world_info();
03775 
03776     write_regions_link();
03777     write_slaying_info();
03778 
03779     write_quests_page();
03780 
03781     write_equipment_index();
03782     write_race_index();
03783     write_npc_list();
03784 
03785     return 0;
03786 }
03787 
03788 void do_auto_apply(mapstruct *m) {
03789     object *tmp, *above = NULL;
03790     int x, y;
03791 
03792     if (m == NULL)
03793         return;
03794 
03795     for (x = 0; x < MAP_WIDTH(m); x++)
03796         for (y = 0; y < MAP_HEIGHT(m); y++)
03797             for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = above) {
03798                 above = tmp->above;
03799 
03800                 if (tmp->inv) {
03801                     object *invtmp, *invnext;
03802 
03803                     for (invtmp = tmp->inv; invtmp != NULL; invtmp = invnext) {
03804                         invnext = invtmp->below;
03805 
03806                         if (QUERY_FLAG(invtmp, FLAG_AUTO_APPLY))
03807                             auto_apply(invtmp);
03808                         else if (invtmp->type == TREASURE && HAS_RANDOM_ITEMS(invtmp)) {
03809                             while ((invtmp->stats.hp--) > 0)
03810                                 create_treasure(invtmp->randomitems, invtmp, 0, m->difficulty, 0);
03811                             invtmp->randomitems = NULL;
03812                         } else if (invtmp
03813                         && invtmp->arch
03814                         && invtmp->type != TREASURE
03815                         && invtmp->type != SPELL
03816                         && invtmp->type != CLASS
03817                         && HAS_RANDOM_ITEMS(invtmp)) {
03818                             create_treasure(invtmp->randomitems, invtmp, 0, m->difficulty, 0);
03819                             /* Need to clear this so that we never try to create
03820                              * treasure again for this object
03821                              */
03822                             invtmp->randomitems = NULL;
03823                         }
03824                     }
03825                     /* This is really temporary - the code at the bottom will
03826                      * also set randomitems to null.  The problem is there are bunches
03827                      * of maps/players already out there with items that have spells
03828                      * which haven't had the randomitems set to null yet.
03829                      * MSW 2004-05-13
03830                      *
03831                      * And if it's a spellbook, it's better to set randomitems to NULL too,
03832                      * else you get two spells in the book ^_-
03833                      * Ryo 2004-08-16
03834                      */
03835                     if (tmp->type == WAND
03836                     || tmp->type == ROD
03837                     || tmp->type == SCROLL
03838                     || tmp->type == HORN
03839                     || tmp->type == FIREWALL
03840                     || tmp->type == POTION
03841                     || tmp->type == ALTAR
03842                     || tmp->type == SPELLBOOK)
03843                         tmp->randomitems = NULL;
03844                 }
03845 
03846                 if (QUERY_FLAG(tmp, FLAG_AUTO_APPLY))
03847                     auto_apply(tmp);
03848                 else if ((tmp->type == TREASURE || (tmp->type == CONTAINER)) && HAS_RANDOM_ITEMS(tmp)) {
03849                     while ((tmp->stats.hp--) > 0)
03850                         create_treasure(tmp->randomitems, tmp, 0, m->difficulty, 0);
03851                     tmp->randomitems = NULL;
03852                 } else if (tmp->type == TIMED_GATE) {
03853                     object *head = tmp->head != NULL ? tmp->head : tmp;
03854 
03855                     if (QUERY_FLAG(head, FLAG_IS_LINKED)) {
03856                         tmp->speed = 0;
03857                         update_ob_speed(tmp);
03858                     }
03859                     /* This function can be called everytime a map is loaded, even when
03860                      * swapping back in.  As such, we don't want to create the treasure
03861                      * over and ove again, so after we generate the treasure, blank out
03862                      * randomitems so if it is swapped in again, it won't make anything.
03863                      * This is a problem for the above objects, because they have counters
03864                      * which say how many times to make the treasure.
03865                      */
03866                 } else if (tmp
03867                 && tmp->arch
03868                 && tmp->type != PLAYER
03869                 && tmp->type != TREASURE
03870                 && tmp->type != SPELL
03871                 && tmp->type != PLAYER_CHANGER
03872                 && tmp->type != CLASS
03873                 && HAS_RANDOM_ITEMS(tmp)) {
03874                     create_treasure(tmp->randomitems, tmp, GT_APPLY, m->difficulty, 0);
03875                     tmp->randomitems = NULL;
03876                 }
03877             }
03878 
03879     for (x = 0; x < MAP_WIDTH(m); x++)
03880         for (y = 0; y < MAP_HEIGHT(m); y++)
03881             for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above)
03882                 if (tmp->above
03883                 && (tmp->type == TRIGGER_BUTTON || tmp->type == TRIGGER_PEDESTAL))
03884                     check_trigger(tmp, tmp->above);
03885 }
03886 
03887 #ifndef DOXYGEN_SHOULD_SKIP_THIS
03888 
03893 void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *txt, const char *txt2) {
03894     fprintf(logfile, "%s\n", txt);
03895 }
03896 
03897 void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format, ...) {
03898     va_list ap;
03899 
03900     va_start(ap, old_format);
03901     vfprintf(logfile, old_format, ap);
03902     va_end(ap);
03903 }
03904 
03905 void ext_info_map(int color, const mapstruct *map, uint8 type, uint8 subtype, const char *str1, const char *str2) {
03906     fprintf(logfile, "ext_info_map: %s\n", str2);
03907 }
03908 
03909 void move_firewall(object *ob) {
03910 }
03911 
03912 void emergency_save(int x) {
03913 }
03914 
03915 void clean_tmp_files(void) {
03916 }
03917 
03918 void esrv_send_item(object *ob, object *obx) {
03919 }
03920 
03921 void dragon_ability_gain(object *ob, int x, int y) {
03922 }
03923 
03924 void set_darkness_map(mapstruct *m) {
03925 }
03926 
03927 object *find_skill_by_number(object *who, int skillno) {
03928     return NULL;
03929 }
03930 
03931 void esrv_del_item(player *pl, int tag) {
03932 }
03933 
03934 void esrv_update_item(int flags, object *pl, object *op) {
03935 }
03936 
03937 void esrv_update_spells(player *pl) {
03938 }
03939 
03940 void monster_check_apply(object *ob, object *obt) {
03941 }
03942 
03943 void trap_adjust(object *ob, int x) {
03944 }
03945 
03946 int execute_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix) {
03947     return 0;
03948 }
03949 
03950 int execute_global_event(int eventcode, ...) {
03951     return 0;
03952 }
03953 
03954 int auto_apply(object *op) {
03955     object *tmp = NULL, *tmp2;
03956     int i;
03957 
03958     switch (op->type) {
03959     case SHOP_FLOOR:
03960         if (!HAS_RANDOM_ITEMS(op))
03961             return 0;
03962         do {
03963             i = 10; /* let's give it 10 tries */
03964             while ((tmp = generate_treasure(op->randomitems, op->stats.exp ? (int)op->stats.exp : MAX(op->map->difficulty, 5))) == NULL && --i)
03965                 ;
03966             if (tmp == NULL)
03967                 return 0;
03968             if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
03969                 free_object(tmp);
03970                 tmp = NULL;
03971             }
03972         } while (!tmp);
03973         tmp->x = op->x;
03974         tmp->y = op->y;
03975         SET_FLAG(tmp, FLAG_UNPAID);
03976         insert_ob_in_map(tmp, op->map, NULL, 0);
03977         CLEAR_FLAG(op, FLAG_AUTO_APPLY);
03978         identify(tmp);
03979         break;
03980 
03981     case TREASURE:
03982         if (QUERY_FLAG(op, FLAG_IS_A_TEMPLATE))
03983             return 0;
03984 
03985         while ((op->stats.hp--) > 0)
03986             create_treasure(op->randomitems, op, op->map ? GT_ENVIRONMENT : 0, op->stats.exp ? (int)op->stats.exp : op->map == NULL ? 14 : op->map->difficulty, 0);
03987 
03988         /* If we generated an object and put it in this object inventory,
03989          * move it to the parent object as the current object is about
03990          * to disappear.  An example of this item is the random_ *stuff
03991          * that is put inside other objects.
03992          */
03993         for (tmp = op->inv; tmp; tmp = tmp2) {
03994             tmp2 = tmp->below;
03995             remove_ob(tmp);
03996             if (op->env)
03997                 insert_ob_in_ob(tmp, op->env);
03998             else
03999                 free_object(tmp);
04000             }
04001         remove_ob(op);
04002         free_object(op);
04003         break;
04004     }
04005     return tmp ? 1 : 0;
04006 }
04007 
04008 void fix_auto_apply(mapstruct *m) {
04009 }
04010 
04011 #endif /* dummy DOXYGEN_SHOULD_SKIP_THIS */