Crossfire Server, Branch 1.12
R12190
|
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, ®ions[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, ®->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, ®ions[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", ®ion_template); 03673 read_template("templates/region_letter.template", ®ion_letter_template); 03674 read_template("templates/region_map.template", ®ion_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 */