4012 lines
129 KiB
C
4012 lines
129 KiB
C
/*
|
|
* Crossfire map browser generator.
|
|
*
|
|
* Author: Nicolas Weeger <nicolas.weeger@laposte.net>, (C) 2006, 2007, 2008.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/**
|
|
* @file mapper.c
|
|
* This program generates map browsing web pages.
|
|
*
|
|
* Quick run: without arguments, will make sensible choices.
|
|
*
|
|
* For help, try the -help option.
|
|
*
|
|
* The following information is generated:
|
|
* - a page per map
|
|
* - a page per region
|
|
* - a global map index
|
|
* - the world map, including regions information
|
|
* - the world map, with exits and blocking zones
|
|
* - the world map, with elevation information.
|
|
*
|
|
* Since this program browses maps from the first map, only maps linked from there will be processed.
|
|
*
|
|
* Maps are generated as the server sees them, that is with weather effects, treasures instead of markers,
|
|
* and things like that.
|
|
*
|
|
* For maps, 2 pictures are generated, one real size and one small-size.
|
|
*
|
|
* Three maps are generated for the world: raw world map, world map with region information, region information only.
|
|
*
|
|
* Maps are always sorted, by the map name (part after the last /).
|
|
*
|
|
* Files are generated based on templates, where tags are replaced by their runtime value.
|
|
*
|
|
* Templates are recursively included. Here is the list:
|
|
* - map.template: main map template.
|
|
* - map_no_exit.template: template to use when no exit on the map.
|
|
* - map_with_exit.template: template to use when there are exits on the map.
|
|
* - map_exit.template: template to use for each exit on the map.
|
|
* - map_lore: template to use to insert the map's lore.
|
|
* - map_no_lore: template when no lore for the map.
|
|
* - map_no_quest.template: when the map is linked to no quest.
|
|
* - map_with_quests.template: the map is linked to at least one quest.
|
|
* - map_one_quest.template: one quest link.
|
|
* - map_no_monster: used when no monster on map.
|
|
* - map_monster_before: applied before the monster list.
|
|
* - map_monster_one: one monster on the map.
|
|
* - map_monster_between: added after each monster except the last.
|
|
* - map_monster_after: added after the last monster in the list.
|
|
* - region.template: region page template.
|
|
* - region_letter: template for one letter
|
|
* - region_map.template: one map in a region
|
|
* - world.template: world map template
|
|
* - world_row.template: one row of world maps.
|
|
* - world_map.template: one map in the template.
|
|
* - index.template: global map index.
|
|
* - index_letter.template: one letter in the index
|
|
* - index_map.template: one map in the whole index
|
|
* - index_region.template: region index template.
|
|
* - index_region_region.template: one region in the index.
|
|
* - level.template: index of maps by level.
|
|
* - level_value.template: one level.
|
|
* - level_map.template: one map in the level.
|
|
* - quests.template: quest index.
|
|
* - quests_quest.template: one quest.
|
|
* - quests_map.template: one map in a quest.
|
|
*
|
|
*
|
|
* Tags must be in the form <code>\#TAG#</code>. To have a # in the code, please put ##. Valid tags are:
|
|
*
|
|
* - map:
|
|
* - NAME: map relative path
|
|
* - MAPPATH: full path to currently generated file.
|
|
* - MAPNAME: name of the map (part of the path after last /).
|
|
* - MAPPIC: name of full-size pic of the map.
|
|
* - MAPSMALLPIC: name of reduced-size pic of the map.
|
|
* - MAPEXIT: text generated by map_with_exit or map_no_exit.
|
|
* - INDEXPATH: path to index.html file.
|
|
* - REGIONPATH: path to region's file.
|
|
* - REGIONNAME: name of map's region.
|
|
* - REGIONINDEXPATH: path to region index file.
|
|
* - WORLDMAPPATH: path to world map file.
|
|
* - MAPLORE: map's lore.
|
|
* - MAPLEVEL: level as defined in the map.
|
|
* - MINMONSTER and MAXMONSTER: minimum and maximum level of monsters on the map.
|
|
* - map_no_exit:
|
|
* - tags for map, except MAPEXIT.
|
|
* - map_with_exit:
|
|
* - tags for map, except MAPEXIT.
|
|
* - EXIT: text generated by the map exits.
|
|
* - map_exit:
|
|
* - map's tags.
|
|
* - EXITNAME: name of exit (part of the path after /).
|
|
* - EXITPATH: relative path of exit's page.
|
|
* - map_lore:
|
|
* - tags for map, except MAPEXIT.
|
|
* - MAPLORE: map's lore.
|
|
* - map_no_lore:
|
|
* - tags for map, except MAPEXIT.
|
|
* - map_with_quests:
|
|
* - tags for map
|
|
* - QUESTS: result of map_one_quest processing.
|
|
* - map_one_quest:
|
|
* - NAME: map's name.
|
|
* - PATH: path to the quest page from the index.
|
|
* - TEXT: text associated to the map about the quest.
|
|
* - map_no_quest: not processed.
|
|
* - map_no_monster:
|
|
* - map's tags
|
|
* - map_monster_before:
|
|
* - map's tags
|
|
* - map_monster_one:
|
|
* - NAME: monster's name.
|
|
* - map_monster_between:
|
|
* - map's tags
|
|
* - map_monster_after:
|
|
* - map's tags
|
|
* - region:
|
|
* - MAPCOUNT: count of maps in region.
|
|
* - LETTERS: text generated by region_letter processing.
|
|
* - region_letter:
|
|
* - region's tags, except LETTERS.
|
|
* - MAPNAME: name of the map (part of the path after last /).
|
|
* - MAPPATH: relative path of the map from the map's root.
|
|
* - MAPHTML: relative path of HTML file for map.
|
|
* - region_map:
|
|
* - tags of region_letter.
|
|
* - MAPNAME: name of the map (part of the path after last /).
|
|
* - MAPPATH: relative path of the map from the map's root.
|
|
* - MAPHTML: relative path of HTML file for map.
|
|
* - world:
|
|
* - MAPS: text generated by world_row processing.
|
|
* - WORLDMAP: path to world map picture, with regions information.
|
|
* - WORLDRAW: path to raw world map, without regions information.
|
|
* - WORLDREGIONS: path to world map picture containing only regions information.
|
|
* - world_row:
|
|
* - MAPS: text generated by world_map processing.
|
|
* - world_map:
|
|
* - MAPNAME: name of the map (part of the path after last /).
|
|
* - MAPPATH: relative path of the map's generated page.
|
|
* - MAPLEFT, MAPTOP, MAPRIGHT, MAPBOTTOM: coordinates (in pixels) of current map in full world map.
|
|
* - index:
|
|
* - MAPCOUNT: count of maps.
|
|
* - LETTERS: text generated by index_letter processing.
|
|
* - index_letter:
|
|
* - tags of index, except LETTERS.
|
|
* - MAPS: text generated by index_letter processing.
|
|
* - LETTER: letter being processed, uppercase.
|
|
* - LETTERCOUNT: number of maps for current letter.
|
|
* - index_map:
|
|
* - tags of index_letter.
|
|
* - MAPNAME: name of the map (part of the path after last /).
|
|
* - MAPPATH: relative path of the map from the map's root.
|
|
* - MAPHTML: relative path of HTML file for map.
|
|
* - index_region:
|
|
* - REGIONCOUNT: total number of regions.
|
|
* - REGIONS: text generated by index_region_region processing.
|
|
* - index_region_region:
|
|
* - REGIONFILE: relative path to region page.
|
|
* - REGIONNAME: region name.
|
|
* - level:
|
|
* - COUNT: count of different levels.
|
|
* - LEVELS: result of the sub-level templates.
|
|
* - level_value:
|
|
* - LEVEL: current level.
|
|
* - MAPS: result of the level maps templates.
|
|
* - level_map:
|
|
* - MAPNAME: name of the map.
|
|
* - MAPPATH: relative path of the map from the index.
|
|
* - quests:
|
|
* - QUESTS: processing of quest.
|
|
* - quests_quest:
|
|
* - QUESTNAME: quest name.
|
|
* - QUESTTEXT: quest description.
|
|
* - QUESTMAPS: processing of quests_map.
|
|
* - QUESTID: unique quest identifier, anchor to index page.
|
|
* - MAINMAPPATH: path to the map defining the quest.
|
|
* - MAINMAPNAME: name of the map defining the quest.
|
|
* - quests_map:
|
|
* - MAPPATH: path to the map.
|
|
* - MAPNAME: map name.
|
|
* - MAPTEXT: description associated.
|
|
*
|
|
* To build this program, from the utils directory:
|
|
* <pre>gcc mapper.c -I../include ../common/libcross.a -o mapper -lm -lgd</pre>
|
|
*
|
|
* @todo
|
|
* - split this file in multiple ones for easier maintenance
|
|
* - add missing documentation on variables / functions
|
|
* - add command line argument for large / small picture size
|
|
* - add maximum width/height for small picture
|
|
* - add slaying information to maps themselves
|
|
* - make the equipment page use templates
|
|
* - shop catalog
|
|
* - treasure list use
|
|
*/
|
|
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
/* For strcasecmp(). */
|
|
#include <strings.h>
|
|
|
|
#include <global.h>
|
|
#include <sproto.h>
|
|
#include <image.h>
|
|
|
|
#include <gd.h>
|
|
#include <gdfonts.h>
|
|
#include <gdfontl.h>
|
|
#include <gdfontg.h>
|
|
|
|
static gdImagePtr *gdfaces;
|
|
|
|
extern int nrofpixmaps; /* Found in common/image.c */
|
|
|
|
/** Information about a NPC with a custom message. */
|
|
typedef struct struct_npc_info {
|
|
const char *name; /**< NPC's name. */
|
|
const char *message; /**< NPC's message. */
|
|
int x, y; /**< Coordinates in the map. */
|
|
} struct_npc_info;
|
|
|
|
/** List of NPCs with a custom message. */
|
|
typedef struct struct_npc_list {
|
|
struct_npc_info **npc;
|
|
int count;
|
|
int allocated;
|
|
} struct_npc_list;
|
|
|
|
/** Collection of races. */
|
|
typedef struct struct_race_list {
|
|
struct struct_race **races; /**< Races on the list. */
|
|
int count; /**< Number of races. */
|
|
int allocated; /**< Allocated space. */
|
|
} struct_race_list;
|
|
|
|
/** Utility structure to group map-quest link structure. */
|
|
typedef struct {
|
|
struct struct_map_in_quest **list;
|
|
int count;
|
|
int allocated;
|
|
} struct_map_in_quest_list;
|
|
|
|
/** List of maps. */
|
|
typedef struct {
|
|
struct struct_map_info **maps;
|
|
int count;
|
|
int allocated;
|
|
} struct_map_list;
|
|
|
|
/** Map information. */
|
|
typedef struct struct_map_info {
|
|
char *path;
|
|
char *name;
|
|
char *filename;
|
|
char *lore;
|
|
region *cfregion;
|
|
int level, pic_was_done, max_monster, min_monster;
|
|
struct_map_list exits_from;
|
|
struct_map_list exits_to;
|
|
struct_map_in_quest_list quests;
|
|
|
|
struct_map_list tiled_maps;
|
|
|
|
struct_race_list monsters;
|
|
|
|
struct_npc_list npcs;
|
|
|
|
struct struct_map_info *tiled_group;
|
|
int height, width;
|
|
int tiled_x_from, tiled_y_from, processed;
|
|
struct struct_map_info *tiles[4];
|
|
} struct_map_info;
|
|
|
|
/** Maps to process or found. */
|
|
static struct_map_list maps_list;
|
|
|
|
/** Pseudo-maps grouping other maps. */
|
|
static struct_map_list tiled_map_list;
|
|
|
|
/** One special item (weapon, shield, ...). */
|
|
typedef struct struct_equipment {
|
|
char *name; /**< Item's name. */
|
|
int power; /**< Item power as declared by the item itself. */
|
|
int calc_power; /**< Item power calculated via calc_item_power(). */
|
|
char *diff; /**< Result of get_ob_diff() with the item's clone. */
|
|
struct_map_list origin; /**< Map(s) this item is found in. */
|
|
} struct_equipment;
|
|
|
|
static struct_equipment **special_equipment = NULL; /**< Special equipment list. */
|
|
|
|
static int equipment_count = 0; /**< Number of items in special_equipment. */
|
|
|
|
static int equipment_allocated = 0; /**< Allocated items in special_equipment. */
|
|
|
|
|
|
/** One monster race in the maps. */
|
|
typedef struct struct_race {
|
|
char *name; /**< Monster's name. */
|
|
int count; /**< Number found on map. */
|
|
struct_map_list origin; /**< Maps to find said monster. */
|
|
} struct_race;
|
|
|
|
static struct_race_list races; /**< Monsters found in maps. */
|
|
|
|
/**
|
|
* Blanks a struct_race_list.
|
|
* @param list
|
|
* list to blank.
|
|
*/
|
|
static void init_race_list(struct_race_list *list) {
|
|
list->races = NULL;
|
|
list->count = 0;
|
|
list->allocated = 0;
|
|
}
|
|
|
|
/**
|
|
* Appends a race to a race list.
|
|
*
|
|
* @param race
|
|
* race to add.
|
|
* @param list
|
|
* list to add to.
|
|
* @param check
|
|
* if 0, don't check if race is already on the list ; else don't make duplicated entries.
|
|
*/
|
|
static void add_race_to_list(struct_race *race, struct_race_list *list, int check) {
|
|
if (check) {
|
|
int test;
|
|
|
|
for (test = 0; test < list->count; test++) {
|
|
if (list->races[test] == race)
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (list->allocated == list->count) {
|
|
list->allocated += 50;
|
|
list->races = realloc(list->races, sizeof(struct_race *)*list->allocated);
|
|
}
|
|
list->races[list->count] = race;
|
|
list->count++;
|
|
}
|
|
|
|
/** Path to store generated files. Relative or absolute, shouldn't end with a / */
|
|
static char root[500];
|
|
|
|
/** Number of created pictures for GD. */
|
|
static int pics_allocated;
|
|
|
|
/* Options */
|
|
static int generate_pics = 1; /**< Whether to generate the picture or not. */
|
|
static int force_pics = 0; /**< To force picture regeneration even if map didn't change. */
|
|
static int generate_index = 1; /**< Whether to do the map index or not. */
|
|
static int size_small = 16; /**< Tile size for small map */
|
|
static int map_limit = -1; /**< Maximum number of maps to browse, -1 for all. */
|
|
static int show_maps = 0; /**< If set, will generate much information on map loaded. */
|
|
static int world_map = 1; /**< If set, will generate a world map. */
|
|
static int world_exit_info = 1; /**< If set, will generate a world map of exits. */
|
|
static int tileset = 0; /**< Tileset to use to generate pics. */
|
|
|
|
static char *world_template; /**< World map template. */
|
|
static char *world_row_template; /**< One row in the world map. */
|
|
static char *world_map_template; /**< One map in a row. */
|
|
|
|
static char *map_template; /**< Map template. */
|
|
static char *map_no_exit_template; /**< World map template: no exit. */
|
|
static char *map_with_exit_template; /**< Map template: exit template. */
|
|
static char *map_exit_template; /**< Map template: one exit. */
|
|
static char *map_no_exit_to_template; /**< World map template: no exit leading to this map. */
|
|
static char *map_with_exit_to_template; /**< Map template: exits leading to this map. */
|
|
static char *map_exit_to_template; /**< Map template: one exit leading to this map. */
|
|
static char *map_lore_template; /**< Map template: lore. */
|
|
static char *map_no_lore_template; /**< Map template: no lore. */
|
|
static char *map_no_monster_template; /**< Map template: no monster. */
|
|
static char *map_monster_before_template; /**< Map template: before the monster list. */
|
|
static char *map_monster_between_template; /**< Map template: between each monster. */
|
|
static char *map_monster_one_template; /**< Map template: one monster. */
|
|
static char *map_monster_after_template; /**< Map template: after the monster list. */
|
|
|
|
static char *index_template; /**< Index page template. */
|
|
static char *index_letter; /**< Index page template: one letter, including the maps it contains. */
|
|
static char *index_map; /**< Index page template: one map. */
|
|
|
|
static char *region_template; /**< Region page template. */
|
|
static char *region_letter_template; /**< One letter for the region. */
|
|
static char *region_map_template; /**< Region page template: one map. */
|
|
|
|
static char *index_region_template; /**< Region index template. */
|
|
static char *index_region_region_template; /**< One region in the region index template. */
|
|
|
|
static char *level_template;
|
|
static char *level_value_template;
|
|
static char *level_map_template;
|
|
|
|
static char *index_quest_template;
|
|
static char *quest_template;
|
|
static char *quest_map_template;
|
|
|
|
static char *map_no_quest_template;
|
|
static char *map_with_quests_template;
|
|
static char *map_one_quest_template;
|
|
|
|
/** Picture statistics. */
|
|
static int created_pics = 0; /**< Total created pics. */
|
|
static int cached_pics = 0; /**< Non recreated pics. */
|
|
|
|
/** Map output formats. */
|
|
enum output_format_type {
|
|
OF_PNG = 0, /**< PNG, default value. */
|
|
OF_JPG = 1 /**< JPG. */
|
|
};
|
|
|
|
/** Extensions depending on output format. */
|
|
static const char *output_extensions[] = {
|
|
".png",
|
|
".jpg"
|
|
};
|
|
|
|
/** Selected output format. */
|
|
static enum output_format_type output_format = OF_PNG;
|
|
|
|
/** Quality for jpg pictures. */
|
|
static int jpeg_quality = -1;
|
|
|
|
/** Whether to generate raw pics or instancied ones. */
|
|
static int rawmaps = 0;
|
|
|
|
/** Whether to warn of exits without a path */
|
|
static int warn_no_path = 0;
|
|
|
|
/** Region information. */
|
|
typedef struct struct_region_info {
|
|
region *reg; /**< Region. */
|
|
struct_map_list maps_list; /**< Maps in the region. */
|
|
int sum_x, sum_y, sum; /**< Sum of locations, to compute name position. */
|
|
int is_world; /**< If set, this region has at least one map part of the world, thus region name should be written. */
|
|
} struct_region_info;
|
|
|
|
static struct struct_region_info **regions = NULL; /**< Found regions. */
|
|
static int region_count = 0; /**< Count of regions. */
|
|
static int region_allocated = 0; /**< Allocated size of regions. */
|
|
|
|
static int list_unused_maps = 0; /**< If set, program will list maps found in directory but not linked from the first maps. */
|
|
static char **found_maps = NULL; /**< Maps found in directories. */
|
|
static int found_maps_count = 0; /**< Number of items in found_maps. */
|
|
static int found_maps_allocated = 0; /**< Allocated size of found_maps. */
|
|
|
|
/* Path/exit info */
|
|
static gdImagePtr infomap; /**< World map with exits / roads / blocking / ... */
|
|
static int color_unlinked_exit; /**< Color for exits without a path set. */
|
|
static int color_linked_exit; /**< Exit leading to another map. */
|
|
static int color_road; /**< Road or equivalent. */
|
|
static int color_blocking; /**< Block all movement. */
|
|
static int color_slowing; /**< Slows movement. */
|
|
|
|
static int **elevation_info; /**< All elevation spots in the "world_" maps. */
|
|
static int elevation_min; /**< Maximal elevation found. */
|
|
static int elevation_max; /**< Lowest elevation found. */
|
|
|
|
/* Links between regions */
|
|
static int do_regions_link = 0;
|
|
static char **regions_link;
|
|
static int regions_link_count = 0;
|
|
static int regions_link_allocated = 0;
|
|
|
|
/** Connection/slaying information. */
|
|
#define S_DOOR 0
|
|
#define S_KEY 1
|
|
#define S_CONTAINER 2
|
|
#define S_DETECTOR 3
|
|
#define S_CONNECT 4
|
|
#define S_MAX 5
|
|
|
|
/** slaying information. */
|
|
typedef struct {
|
|
char *slaying; /**< Slaying value. */
|
|
struct_map_list maps[S_MAX];
|
|
} struct_slaying_info;
|
|
|
|
static struct_slaying_info **slaying_info = NULL; /**< Found slaying fields. */
|
|
static int slaying_count = 0; /**< Count of items in slaying_info. */
|
|
static int slaying_allocated = 0; /**< Allocated size of slaying_info. */
|
|
|
|
/**
|
|
* Initialises a list structure.
|
|
* @param list
|
|
* list to blank.
|
|
*/
|
|
static void init_map_list(struct_map_list *list) {
|
|
list->maps = NULL;
|
|
list->count = 0;
|
|
list->allocated = 0;
|
|
}
|
|
|
|
static void add_map(struct_map_info *info, struct_map_list *list);
|
|
|
|
static int is_special_equipment(object *item) {
|
|
if (item->name == item->arch->clone.name)
|
|
return 0;
|
|
if (QUERY_FLAG(item, FLAG_NO_PICK))
|
|
return 0;
|
|
if (item->move_block == MOVE_ALL)
|
|
return 0;
|
|
|
|
if (IS_SHIELD(item) || IS_WEAPON(item) || IS_ARMOR(item) || IS_ARROW(item) || (item->type == ROD) || (item->type == WAND))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Gets an empty struct_equipment.
|
|
* @return
|
|
* new item.
|
|
*/
|
|
static struct_equipment *get_equipment(void) {
|
|
struct_equipment *add = calloc(1, sizeof(struct_equipment));
|
|
|
|
init_map_list(&add->origin);
|
|
return add;
|
|
}
|
|
|
|
/**
|
|
* Frees a struct_equipment.
|
|
*
|
|
* @param equip
|
|
* item to free.
|
|
*/
|
|
static void free_equipment(struct_equipment *equip) {
|
|
free(equip->diff);
|
|
free(equip->name);
|
|
free(equip);
|
|
}
|
|
|
|
/**
|
|
* Searches the item list for an identical item, except maps.
|
|
*
|
|
* @param item
|
|
* item to search. The variable may be freed, so must not be used after calling this function.
|
|
* @return
|
|
* item guaranteed to be unique in the item list.
|
|
*/
|
|
static struct_equipment *ensure_unique(struct_equipment *item) {
|
|
int check;
|
|
struct_equipment *comp;
|
|
|
|
for (check = 0; check < equipment_count; check++) {
|
|
comp = special_equipment[check];
|
|
|
|
if (strcmp(comp->name, item->name))
|
|
continue;
|
|
if (comp->power != item->power)
|
|
continue;
|
|
if (comp->calc_power != item->calc_power)
|
|
continue;
|
|
if (strcmp(comp->diff, item->diff))
|
|
continue;
|
|
|
|
free_equipment(item);
|
|
return comp;
|
|
}
|
|
|
|
if (equipment_count == equipment_allocated) {
|
|
equipment_allocated += 50;
|
|
special_equipment = realloc(special_equipment, sizeof(struct_equipment *)*equipment_allocated);
|
|
}
|
|
special_equipment[equipment_count] = item;
|
|
equipment_count++;
|
|
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* Adds an item to the list of special items.
|
|
*
|
|
* @param item
|
|
* item to add.
|
|
* @param map
|
|
* map it is on.
|
|
* @todo merge items with the same properties.
|
|
*/
|
|
static void add_one_item(object *item, struct_map_info *map) {
|
|
struct_equipment *add = get_equipment();
|
|
StringBuffer *bf = stringbuffer_new();
|
|
int x, y;
|
|
sstring name, namepl;
|
|
|
|
x = item->x;
|
|
y = item->y;
|
|
name = item->name;
|
|
namepl = item->name_pl;
|
|
|
|
item->x = item->arch->clone.x;
|
|
item->y = item->arch->clone.y;
|
|
item->name = item->arch->clone.name;
|
|
item->name_pl = item->arch->clone.name_pl;
|
|
get_ob_diff(bf, item, &item->arch->clone);
|
|
add->diff = stringbuffer_finish(bf);
|
|
|
|
item->x = x;
|
|
item->y = y;
|
|
item->name = name;
|
|
item->name_pl = namepl;
|
|
|
|
if (add->diff == NULL || strcmp(add->diff, "") == 0) {
|
|
free_equipment(add);
|
|
return;
|
|
}
|
|
|
|
add->name = strdup(item->name);
|
|
add->power = item->item_power;
|
|
add->calc_power = calc_item_power(item, 0);
|
|
|
|
add = ensure_unique(add);
|
|
add_map(map, &add->origin);
|
|
}
|
|
|
|
/**
|
|
* Checks if item and its inventory are worthy to be listed.
|
|
*
|
|
* @param item
|
|
* item to check.
|
|
* @param map
|
|
* map the item is on.
|
|
*/
|
|
static void check_equipment(object *item, struct_map_info *map) {
|
|
object *inv;
|
|
|
|
if (is_special_equipment(item))
|
|
add_one_item(item, map);
|
|
|
|
for (inv = item->inv; inv; inv = inv->below) {
|
|
check_equipment(inv, map);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sort 2 struct_equipment, first on item power then name.
|
|
* @param a
|
|
* @param b
|
|
* items to compare.
|
|
* @return
|
|
* -1, 0 or 1.
|
|
*/
|
|
static int sort_equipment(const void *a, const void *b) {
|
|
const struct_equipment *l = *(const struct_equipment **)a;
|
|
const struct_equipment *r = *(const struct_equipment **)b;
|
|
int c = l->power-r->power;
|
|
|
|
if (c)
|
|
return c;
|
|
return strcasecmp(l->name, r->name);
|
|
}
|
|
|
|
/**
|
|
* Returns the race for specified name.
|
|
*
|
|
* @param name
|
|
* monster's name.
|
|
* @return
|
|
* race structure.
|
|
*/
|
|
static struct_race *get_race(const char *name) {
|
|
int test;
|
|
struct_race *item;
|
|
|
|
for (test = 0; test < races.count; test++) {
|
|
if (strcmp(races.races[test]->name, name) == 0) {
|
|
races.races[test]->count++;
|
|
return races.races[test];
|
|
}
|
|
}
|
|
|
|
item = calloc(1, sizeof(struct_race));
|
|
item->name = strdup(name);
|
|
item->count = 1;
|
|
init_map_list(&item->origin);
|
|
|
|
add_race_to_list(item, &races, 0);
|
|
|
|
return item;
|
|
}
|
|
|
|
/**
|
|
* Adds a monster to the monster list.
|
|
*
|
|
* @param monster
|
|
* monster to add. Can be any part.
|
|
* @param map
|
|
* map to add the monster to.
|
|
*/
|
|
static void add_monster(object *monster, struct_map_info *map) {
|
|
struct_race *race;
|
|
|
|
if (monster->head && monster != monster->head)
|
|
return;
|
|
|
|
map->min_monster = MIN(monster->level, map->min_monster);
|
|
map->max_monster = MAX(monster->level, map->max_monster);
|
|
|
|
race = get_race(monster->name);
|
|
add_map(map, &race->origin);
|
|
add_race_to_list(race, &map->monsters, 1);
|
|
}
|
|
|
|
/**
|
|
* Sort 2 struct_race.
|
|
* @param a
|
|
* @param b
|
|
* items to compare.
|
|
* @return
|
|
* -1, 0 or 1.
|
|
*/
|
|
static int sort_race(const void *a, const void *b) {
|
|
const struct_race *l = *(const struct_race **)a;
|
|
const struct_race *r = *(const struct_race **)b;
|
|
return strcasecmp(l->name, r->name);
|
|
}
|
|
|
|
/**
|
|
* Checks if ::object is considered a road or not.
|
|
* @param item
|
|
* ::object to check.
|
|
* @return
|
|
* 1 if object is a road, 0 else.
|
|
*/
|
|
static int is_road(object *item) {
|
|
int test;
|
|
/* Archetypes used as roads. */
|
|
const char *roads[] = {
|
|
"cobblestones",
|
|
"flagstone",
|
|
"ice_stone",
|
|
"snow",
|
|
NULL };
|
|
const char *partial[] = {
|
|
"dirtroad_",
|
|
NULL };
|
|
|
|
for (test = 0; partial[test] != NULL; test++) {
|
|
if (strstr(item->arch->name, partial[test]) != NULL)
|
|
return 1;
|
|
}
|
|
|
|
if (!QUERY_FLAG(item, FLAG_IS_FLOOR))
|
|
return 0;
|
|
|
|
for (test = 0; roads[test] != NULL; test++) {
|
|
if (strcmp(item->arch->name, roads[test]) == 0)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Checks if item blocks movement or not.
|
|
* @param item
|
|
* ::object to test.
|
|
* @return
|
|
* 1 if item blocks all movement, 0 else.
|
|
*/
|
|
static int is_blocking(object *item) {
|
|
return item->move_block == MOVE_ALL ? 1 : 0;
|
|
}
|
|
|
|
/**
|
|
* Gets the color for an elevation.
|
|
*
|
|
* @param elevation
|
|
* elevation to get color for.
|
|
* @param elevationmap
|
|
* picture that will get the color.
|
|
* @return
|
|
* color.
|
|
*/
|
|
static int get_elevation_color(int elevation, gdImagePtr elevationmap) {
|
|
if (elevation > 0)
|
|
return gdImageColorResolve(elevationmap, 200*elevation/elevation_max, 0, 0);
|
|
else
|
|
return gdImageColorResolve(elevationmap, 0, 0, 200*elevation/elevation_min);
|
|
}
|
|
|
|
/**
|
|
* Proceses exit / road / blocking information for specified map into the global infomap map.
|
|
*
|
|
* If map isn't a world map, won't do anything.
|
|
*
|
|
* @param map
|
|
* map to write info for.
|
|
*/
|
|
static void do_exit_map(mapstruct *map) {
|
|
int tx, ty, x, y;
|
|
object *item, *test;
|
|
|
|
if (sscanf(map->path, "/world/world_%d_%d", &x, &y) != 2)
|
|
return;
|
|
|
|
x -= 100;
|
|
y -= 100;
|
|
|
|
for (tx = 0; tx < MAP_WIDTH(map); tx++) {
|
|
for (ty = 0; ty < MAP_HEIGHT(map); ty++) {
|
|
item = GET_MAP_OB(map, tx, ty);
|
|
while (item) {
|
|
test = item->head ? item->head : item;
|
|
|
|
if (test->type == EXIT || test->type == TELEPORTER) {
|
|
if (!test->slaying)
|
|
gdImageSetPixel(infomap, x*50+tx, y*50+ty, color_unlinked_exit);
|
|
else
|
|
gdImageSetPixel(infomap, x*50+tx, y*50+ty, color_linked_exit);
|
|
} else if (is_road(test))
|
|
gdImageSetPixel(infomap, x*50+tx, y*50+ty, color_road);
|
|
else if (is_blocking(test)) {
|
|
gdImageSetPixel(infomap, x*50+tx, y*50+ty, color_blocking);
|
|
/* can't get on the spot, so no need to go on. */
|
|
break;
|
|
} else if (test->move_slow != 0)
|
|
gdImageSetPixel(infomap, x*50+tx, y*50+ty, color_slowing);
|
|
|
|
if (item->elevation) {
|
|
elevation_min = MIN(elevation_min, item->elevation);
|
|
elevation_max = MAX(elevation_max, item->elevation);
|
|
elevation_info[x*50+tx][y*50+ty] = item->elevation;
|
|
}
|
|
|
|
item = item->above;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void do_auto_apply(mapstruct *m);
|
|
|
|
/**
|
|
* Sort values alphabetically
|
|
* Used by qsort to sort values alphabetically.
|
|
* @param a
|
|
* First value
|
|
* @param b
|
|
* Second value
|
|
* @return
|
|
* -1 if a is less than b, 0 if a equals b, 1 else.
|
|
*/
|
|
static int sortbyname(const void *a, const void *b) {
|
|
return (strcmp(*(const char **)a, *(const char **)b));
|
|
}
|
|
|
|
/**
|
|
* Concatenates a string, and free concatenated string.
|
|
*
|
|
* @param source
|
|
* string to append to. Can be NULL.
|
|
* @param add
|
|
* string that is appened. Will be free()d after. Must not be NULL.
|
|
* @return
|
|
* new string that should be free()d by caller.
|
|
*/
|
|
static char *cat_template(char *source, char *add) {
|
|
if (!source)
|
|
return add;
|
|
source = realloc(source, strlen(source)+strlen(add)+1);
|
|
strcat(source, add);
|
|
free(add);
|
|
return source;
|
|
}
|
|
|
|
/**
|
|
* Reads a file in memory.
|
|
*
|
|
* @param name
|
|
* file path to read.
|
|
* @param buffer
|
|
* where to store. Can be left uninitialized in case of errors.
|
|
* @note
|
|
* will exit() with code 1 if any error occurs or if the file doesn't exist.
|
|
*/
|
|
static void read_template(const char *name, char **buffer) {
|
|
FILE *file;
|
|
struct stat info;
|
|
|
|
if (stat(name, &info)) {
|
|
printf("Couldn't stat template %s!\n", name);
|
|
exit(1);
|
|
}
|
|
|
|
(*buffer) = calloc(1, info.st_size+1);
|
|
if (!(*buffer)) {
|
|
printf("Template %s calloc failed!\n", name);
|
|
exit(1);
|
|
}
|
|
|
|
if (info.st_size == 0) {
|
|
(*buffer)[0] = '\0';
|
|
return;
|
|
}
|
|
|
|
file = fopen(name, "rb");
|
|
if (!file) {
|
|
printf("Couldn't open template %s!\n", name);
|
|
free(*buffer);
|
|
exit(1);
|
|
}
|
|
if (fread(*buffer, info.st_size, 1, file) != 1) {
|
|
printf("Couldn't read template %s!\n", name);
|
|
free(*buffer);
|
|
fclose(file);
|
|
exit(1);
|
|
}
|
|
fclose(file);
|
|
}
|
|
|
|
/**
|
|
* Processes a template.
|
|
*
|
|
* Variables in the form <code>\#VARIABLE#</code> will be substituted for specified values.
|
|
*
|
|
* @param template
|
|
* template to process.
|
|
* @param vars
|
|
* variables to replace. Array must be NULL-terminated.
|
|
* @param values
|
|
* variables to replace by. Must be the same size as vars, no NULL element allowed.
|
|
* @return
|
|
* filled-in template, that must be free()d be caller. NULL if memory allocation error.
|
|
*
|
|
* @note
|
|
* returned string will be a memory block larger than required, for performance reasons.
|
|
*/
|
|
static char *do_template(const char *template, const char **vars, const char **values) {
|
|
int count = 0;
|
|
const char *sharp = template;
|
|
int maxlen = 0;
|
|
int var = 0;
|
|
char *result;
|
|
char *current_result;
|
|
const char *end;
|
|
|
|
while ((sharp = strchr(sharp, '#')) != NULL) {
|
|
sharp++;
|
|
count++;
|
|
}
|
|
if (!count)
|
|
return strdup(template);
|
|
if (count%2) {
|
|
printf("Malformed template, mismatched #!\n");
|
|
return strdup(template);
|
|
}
|
|
|
|
while (vars[var] != NULL) {
|
|
if (strlen(values[var]) > maxlen)
|
|
maxlen = strlen(values[var]);
|
|
var++;
|
|
}
|
|
result = calloc(1, strlen(template)+maxlen*(count/2)+1);
|
|
if (!result)
|
|
return NULL;
|
|
current_result = result;
|
|
|
|
sharp = template;
|
|
while ((sharp = strchr(sharp, '#')) != NULL) {
|
|
end = strchr(sharp+1, '#');
|
|
strncpy(current_result, template, sharp-template);
|
|
if (end == sharp+1) {
|
|
strcat(current_result, "#");
|
|
}
|
|
else {
|
|
current_result = current_result+strlen(current_result);
|
|
var = 0;
|
|
while (vars[var] != NULL && (strncmp(vars[var], sharp+1, end-sharp-1) || (strlen(vars[var]) != end-sharp-1)))
|
|
/* tag must be the same length, else can take a wrong tag */
|
|
var++;
|
|
if (vars[var] == NULL)
|
|
printf("Wrong tag: %s\n", sharp);
|
|
else
|
|
strcpy(current_result, values[var]);
|
|
}
|
|
current_result = current_result+strlen(current_result);
|
|
sharp = end+1;
|
|
template = sharp;
|
|
}
|
|
strcat(current_result, template);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Computes the shortest path from one file to another.
|
|
*
|
|
* @param from
|
|
* origin.
|
|
* @param to
|
|
* destination.
|
|
* @param result
|
|
* string that will contain the calculated path. Must be large enough, no test done.
|
|
* @warning
|
|
* from and to must be absolute paths (starting with /).
|
|
*/
|
|
static void relative_path(const char *from, const char *to, char *result) {
|
|
const char *fslash;
|
|
const char *rslash;
|
|
|
|
result[0] = '\0';
|
|
|
|
fslash = strchr(from+1, '/');
|
|
if (!fslash) {
|
|
strcpy(result, to+1);
|
|
return;
|
|
}
|
|
|
|
rslash = strchr(to+1, '/');
|
|
while (fslash && rslash && (fslash-from == rslash-to) && strncmp(from, to, fslash-from+1) == 0) {
|
|
from = fslash+1;
|
|
to = rslash+1;
|
|
fslash = strchr(fslash+1, '/');
|
|
rslash = strchr(rslash+1, '/');
|
|
}
|
|
|
|
while (fslash) {
|
|
strcat(result, "../");
|
|
fslash = strchr(fslash+1, '/');
|
|
}
|
|
if (strlen(result) && result[strlen(result)-1] == '/' && *to == '/')
|
|
result[strlen(result)-1] = '\0';
|
|
strcat(result, to);
|
|
}
|
|
|
|
/**
|
|
* Sorts the strings according to the last part of the filename (after the last /).
|
|
*
|
|
* @param left
|
|
* first string.
|
|
* @param right
|
|
* second string.
|
|
* @return
|
|
* comparison on last element, and if equal then on whole string.
|
|
*/
|
|
static int sort_mapname(const void *left, const void *right) {
|
|
const char *l = *(const char **)left;
|
|
const char *r = *(const char **)right;
|
|
const char *sl = strrchr(l, '/');
|
|
const char *sr = strrchr(r, '/');
|
|
int c;
|
|
|
|
if (!sl)
|
|
sl = l;
|
|
if (!sr)
|
|
sr = r;
|
|
c = strcasecmp(sl, sr);
|
|
if (c)
|
|
return c;
|
|
|
|
return strcasecmp(l, r);
|
|
}
|
|
|
|
/**
|
|
* Compares struct_map_info according to the map name or the path if equal.
|
|
*
|
|
* @param left
|
|
* first item.
|
|
* @param right
|
|
* second item.
|
|
* @return
|
|
* comparison on name, and if equal then on whole path.
|
|
*/
|
|
static int compare_map_info(const struct_map_info *left, const struct_map_info *right) {
|
|
int c;
|
|
|
|
if (left->tiled_group)
|
|
left = left->tiled_group;
|
|
if (right->tiled_group)
|
|
right = right->tiled_group;
|
|
|
|
c = strcasecmp(left->name, right->name);
|
|
if (c)
|
|
return c;
|
|
|
|
return strcasecmp(left->path, right->path);
|
|
}
|
|
|
|
/**
|
|
* Sorts the struct_map_info according to the map name or the path if equal.
|
|
*
|
|
* @param left
|
|
* first item.
|
|
* @param right
|
|
* second item.
|
|
* @return
|
|
* comparison on name, and if equal then on whole path.
|
|
*/
|
|
static int sort_map_info(const void *left, const void *right) {
|
|
const struct_map_info *l = *(const struct_map_info **)left;
|
|
const struct_map_info *r = *(const struct_map_info **)right;
|
|
return compare_map_info(l, r);
|
|
}
|
|
|
|
/**
|
|
* Sorts the struct_map_info according to the map's level, and if equal the name or the path.
|
|
*
|
|
* @param left
|
|
* first item.
|
|
* @param right
|
|
* second item.
|
|
* @return
|
|
* comparison on name, and if equal then on whole path.
|
|
*/
|
|
static int sort_map_info_by_level(const void *left, const void *right) {
|
|
const struct_map_info *l = *(const struct_map_info **)left;
|
|
const struct_map_info *r = *(const struct_map_info **)right;
|
|
int c = l->level-r->level;
|
|
if (c)
|
|
return c;
|
|
return compare_map_info(l, r);
|
|
}
|
|
|
|
/**
|
|
* Sorts an array of struct_region_info by region name.
|
|
*
|
|
* @param left
|
|
* first region.
|
|
* @param right
|
|
* second region.
|
|
* @return
|
|
* result of strcmp() for names.
|
|
*/
|
|
static int sort_region(const void *left, const void *right) {
|
|
return strcmp((*((struct_region_info **)left))->reg->name, (*((struct_region_info **)right))->reg->name);
|
|
}
|
|
|
|
/************************************
|
|
Start of quest-related definitions.
|
|
************************************/
|
|
|
|
/** Link between a quest and a map. */
|
|
typedef struct struct_map_in_quest {
|
|
struct_map_info *map; /**< Linked map. */
|
|
char *description; /**< Description associated with the map for the quest. */
|
|
struct struct_quest *quest; /**< Point back to the quest. */
|
|
} struct_map_in_quest;
|
|
|
|
/** One quest. */
|
|
typedef struct struct_quest {
|
|
char *name; /**< Quest's name. */
|
|
char *description; /**< Description, from the main map's lore. */
|
|
int number; /**< Unique quest identifier. */
|
|
struct_map_info *mainmap; /**< Map defining the quest. Can be NULL if quest has no definition or map not processed. */
|
|
struct_map_in_quest_list maps; /**< Maps part of this quest. */
|
|
} struct_quest;
|
|
|
|
static struct_quest **quests = NULL; /**< All quests in the game. */
|
|
|
|
static int quests_count = 0; /**< Count of quests. */
|
|
|
|
static int quests_allocated = 0; /**< Allocated items in quests. */
|
|
|
|
static void init_struct_map_in_quest_list(struct_map_in_quest_list *list) {
|
|
list->list = NULL;
|
|
list->count = 0;
|
|
list->allocated = 0;
|
|
}
|
|
|
|
static void add_to_struct_map_in_quest_list(struct_map_in_quest_list *list, struct_map_in_quest *item) {
|
|
if (list->count == list->allocated) {
|
|
list->allocated += 10;
|
|
list->list = realloc(list->list, sizeof(struct_map_in_quest *)*list->allocated);
|
|
}
|
|
list->list[list->count++] = item;
|
|
}
|
|
|
|
/**
|
|
* Gets the information for a quest, create the field if needed.
|
|
*
|
|
* @param name
|
|
* quest's name.
|
|
* @return
|
|
* information, never NULL.
|
|
*/
|
|
static struct_quest *get_quest_info(const char *name) {
|
|
int test;
|
|
struct_quest *add;
|
|
|
|
for (test = 0; test < quests_count; test++) {
|
|
if (strcmp(quests[test]->name, name) == 0)
|
|
return quests[test];
|
|
}
|
|
|
|
if (quests_count == quests_allocated) {
|
|
quests_allocated += 10;
|
|
quests = realloc(quests, sizeof(struct_quest *)*quests_allocated);
|
|
}
|
|
add = calloc(1, sizeof(struct_quest));
|
|
add->name = strdup(name);
|
|
add->number = quests_count;
|
|
init_struct_map_in_quest_list(&add->maps);
|
|
quests[quests_count] = add;
|
|
quests_count++;
|
|
return add;
|
|
}
|
|
|
|
/**
|
|
* Links a map to a quest.
|
|
*
|
|
* @param map
|
|
* map to link.
|
|
* @param name
|
|
* quest name.
|
|
* @param description
|
|
* associated link description. Must not be NULL.
|
|
*/
|
|
static void add_map_to_quest(struct_map_info *map, const char *name, const char *description) {
|
|
struct_map_in_quest *add;
|
|
struct_quest *quest = get_quest_info(name);
|
|
|
|
add = calloc(1, sizeof(struct_map_in_quest));
|
|
add->map = map;
|
|
add->quest = quest;
|
|
add->description = strdup(description);
|
|
while (strlen(add->description) && add->description[strlen(add->description)-1] == '\n')
|
|
add->description[strlen(add->description)-1] = '\0';
|
|
add_to_struct_map_in_quest_list(&quest->maps, add);
|
|
add_to_struct_map_in_quest_list(&map->quests, add);
|
|
}
|
|
|
|
/**
|
|
* Sorts 2 struct_map_in_quest, on the map's name or path.
|
|
* @param left
|
|
* @param right
|
|
* items to compare.
|
|
* @return
|
|
* -1, 0 or 1.
|
|
*/
|
|
static int sort_struct_map_in_quest(const void *left, const void *right) {
|
|
int c;
|
|
|
|
const struct_map_in_quest *l = *(const struct_map_in_quest **)left;
|
|
const struct_map_in_quest *r = *(const struct_map_in_quest **)right;
|
|
const struct_map_info *ml = l->map;
|
|
const struct_map_info *mr = r->map;
|
|
|
|
if (ml->tiled_group)
|
|
ml = ml->tiled_group;
|
|
if (mr->tiled_group)
|
|
mr = mr->tiled_group;
|
|
|
|
c = strcasecmp(ml->name, mr->name);
|
|
if (c)
|
|
return c;
|
|
|
|
return strcasecmp(ml->path, mr->path);
|
|
}
|
|
|
|
/**
|
|
* Sets the main map for a quest.
|
|
*
|
|
* @param name
|
|
* quest name.
|
|
* @param mainmap
|
|
* main map to associate.
|
|
* @param description
|
|
* quest description. Must not be NULL.
|
|
*/
|
|
static void define_quest(const char *name, struct_map_info *mainmap, const char *description) {
|
|
struct_quest *quest = get_quest_info(name);
|
|
|
|
if (quest->description || quest->mainmap) {
|
|
printf("warning, multiple quest definition for %s, found in %s and %s.\n", quest->name, quest->mainmap ? quest->mainmap->path : "(unknown map)", mainmap->path);
|
|
return;
|
|
}
|
|
quest->description = strdup(description);
|
|
while (strlen(quest->description) && quest->description[strlen(quest->description)-1] == '\n')
|
|
quest->description[strlen(quest->description)-1] = '\0';
|
|
quest->mainmap = mainmap;
|
|
}
|
|
|
|
/**
|
|
* Extracts from the map's lore quest information if found. May modify map->lore.
|
|
*
|
|
* @param map
|
|
* map to process.
|
|
*/
|
|
static void process_map_lore(struct_map_info *map) {
|
|
char *start, *end, *next;
|
|
char name[500];
|
|
char description[500];
|
|
|
|
start = strstr(map->lore, "@def");
|
|
while (start) {
|
|
description[0] = '\0';
|
|
/* find name */
|
|
end = strstr(start, "\n");
|
|
if (end) {
|
|
strncpy(name, start+5, end-start-5);
|
|
name[end-start-5] = '\0';
|
|
next = end+1;
|
|
end = strstr(next, "@end");
|
|
if (end) {
|
|
strncpy(description, next, end-next);
|
|
description[end-next] = '\0';
|
|
/* need to erase the text. */
|
|
memcpy(start, end+4, strlen(map->lore)-(end-start+3));
|
|
end = start;
|
|
}
|
|
else {
|
|
strcpy(description, next);
|
|
*start = '\0';
|
|
end = NULL;
|
|
}
|
|
} else {
|
|
strcpy(name, start);
|
|
*start = '\0';
|
|
end = NULL;
|
|
}
|
|
|
|
define_quest(name, map, description);
|
|
start = end ? strstr(end, "@def") : NULL;
|
|
}
|
|
|
|
start = strstr(map->lore, "@quest");
|
|
while (start) {
|
|
description[0] = '\0';
|
|
/* find name */
|
|
end = strstr(start, "\n");
|
|
if (end) {
|
|
strncpy(name, start+7, end-start-7);
|
|
name[end-start-7] = '\0';
|
|
next = end+1;
|
|
end = strstr(next, "@end");
|
|
if (end) {
|
|
strncpy(description, next, end-next);
|
|
description[end-next] = '\0';
|
|
/* need to erase the text. */
|
|
memcpy(start, end+4, strlen(map->lore)-(end-start+3));
|
|
end = start;
|
|
}
|
|
else {
|
|
strcpy(description, next);
|
|
*start = '\0';
|
|
end = NULL;
|
|
}
|
|
} else {
|
|
strcpy(name, start);
|
|
*start = '\0';
|
|
end = NULL;
|
|
}
|
|
|
|
add_map_to_quest(map, name, description);
|
|
start = end ? strstr(end, "@quest") : NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Writes the global quests page.
|
|
*/
|
|
static void write_quests_page(void) {
|
|
int quest, map;
|
|
FILE *out;
|
|
char path[500];
|
|
char mappath[500];
|
|
char mainmappath[500];
|
|
char questid[500];
|
|
const char *map_vars[] = { "MAPPATH", "MAPNAME", "MAPTEXT", NULL };
|
|
const char *map_vals[] = { mappath, NULL, NULL, NULL };
|
|
const char *quest_vars[] = { "QUESTNAME", "QUESTTEXT", "QUESTMAPS", "QUESTID", "MAINMAPPATH", "MAINMAPNAME", NULL };
|
|
const char *quest_vals[] = { NULL, NULL, NULL, questid, mainmappath, NULL, NULL };
|
|
const char *idx_vars[] = { "QUESTS", NULL };
|
|
const char *idx_vals[] = { NULL, NULL };
|
|
char *text_map = NULL;
|
|
char *text_quest = NULL;
|
|
char *text_idx = NULL;
|
|
|
|
printf("Writing quest index...");
|
|
|
|
for (quest = 0; quest < quests_count; quest++) {
|
|
qsort(quests[quest]->maps.list, quests[quest]->maps.count, sizeof(struct_map_in_quest *), sort_struct_map_in_quest);
|
|
for (map = 0; map < quests[quest]->maps.count; map++) {
|
|
snprintf(mappath, sizeof(mappath), "%s.html", quests[quest]->maps.list[map]->map->path+1);
|
|
map_vals[1] = quests[quest]->maps.list[map]->map->name;
|
|
map_vals[2] = quests[quest]->maps.list[map]->description ? quests[quest]->maps.list[map]->description : "(no description)";
|
|
text_map = cat_template(text_map, do_template(quest_map_template, map_vars, map_vals));
|
|
}
|
|
if (!text_map)
|
|
text_map = strdup("");
|
|
|
|
quest_vals[0] = quests[quest]->name;
|
|
quest_vals[1] = quests[quest]->description ? quests[quest]->description : "(main map not processed)";
|
|
quest_vals[2] = text_map;
|
|
snprintf(questid, sizeof(questid), "quest_%d", quests[quest]->number);
|
|
if (quests[quest]->mainmap) {
|
|
snprintf(mainmappath, sizeof(mainmappath), "%s.html", quests[quest]->mainmap->path+1);
|
|
quest_vals[5] = quests[quest]->mainmap->name;
|
|
} else {
|
|
snprintf(mainmappath, sizeof(mainmappath), "#");
|
|
quest_vals[5] = "";
|
|
}
|
|
text_quest = cat_template(text_quest, do_template(quest_template, quest_vars, quest_vals));
|
|
free(text_map);
|
|
text_map = NULL;
|
|
}
|
|
|
|
if (!text_quest)
|
|
text_quest = strdup("No quest.");
|
|
|
|
idx_vals[0] = text_quest;
|
|
text_idx = do_template(index_quest_template, idx_vars, idx_vals);
|
|
free(text_quest);
|
|
|
|
snprintf(path, sizeof(path), "%s/quests.html", root);
|
|
out = fopen(path, "w+");
|
|
fprintf(out, text_idx);
|
|
fclose(out);
|
|
free(text_idx);
|
|
|
|
printf(" done.\n");
|
|
}
|
|
|
|
/************************************
|
|
End of quest-related definitions.
|
|
************************************/
|
|
|
|
/*********
|
|
NPC-related stuff
|
|
********/
|
|
|
|
/**
|
|
* Initialise a list of NPCs.
|
|
* @param list
|
|
* list to initialise.
|
|
*/
|
|
static void init_npc_list(struct_npc_list *list) {
|
|
list->allocated = 0;
|
|
list->count = 0;
|
|
list->npc = NULL;
|
|
}
|
|
|
|
/**
|
|
* Create the struct_npc_info from the specified NPC. It must have a name and message.
|
|
* @param npc
|
|
* NPC to gather info for.
|
|
* @return
|
|
* structure with info.
|
|
*/
|
|
static struct_npc_info *create_npc_info(const object *npc) {
|
|
struct_npc_info *info = calloc(1, sizeof(struct_npc_info));
|
|
|
|
info->name = strdup(npc->name);
|
|
info->message = strdup(npc->msg);
|
|
info->x = npc->x;
|
|
info->y = npc->y;
|
|
|
|
return info;
|
|
}
|
|
|
|
/**
|
|
* Link the specified NPC to the specified map.
|
|
* @param map
|
|
* map the NPC is in.
|
|
* @param npc
|
|
* NPC to link. Must have a name and message.
|
|
*/
|
|
static void add_npc_to_map(struct_map_info *map, const object *npc) {
|
|
if (map->npcs.count == map->npcs.allocated) {
|
|
map->npcs.allocated += 50;
|
|
map->npcs.npc = realloc(map->npcs.npc, map->npcs.allocated*sizeof(struct_npc_info *));
|
|
}
|
|
|
|
map->npcs.npc[map->npcs.count] = create_npc_info(npc);
|
|
map->npcs.count++;
|
|
}
|
|
/* end of NPC stuff */
|
|
|
|
/**
|
|
* Adds a map to specified array, if it isn't already.
|
|
*
|
|
* @param info
|
|
* map to add.
|
|
* @param list
|
|
* list to add to.
|
|
*
|
|
* @note
|
|
* will allocate memory and update variables when required.
|
|
*/
|
|
static void add_map(struct_map_info *info, struct_map_list *list) {
|
|
int map;
|
|
|
|
for (map = 0; map < list->count; map++)
|
|
if (list->maps[map] == info)
|
|
return;
|
|
|
|
if (list->count == list->allocated) {
|
|
list->allocated += 50;
|
|
list->maps = realloc(list->maps, list->allocated*sizeof(struct_map_info *));
|
|
}
|
|
list->maps[list->count] = info;
|
|
list->count++;
|
|
}
|
|
|
|
/**
|
|
* Replaces a map in a map list. Will emit a warning if map can't be found.
|
|
*
|
|
* @param find
|
|
* map to replace.
|
|
* @param replace_by
|
|
* replacement map.
|
|
* @param list
|
|
* where to search.
|
|
*/
|
|
static void replace_map(struct_map_info *find, struct_map_info *replace_by, struct_map_list *list) {
|
|
int map;
|
|
|
|
for (map = 0; map < list->count; map++) {
|
|
if (list->maps[map] == find) {
|
|
list->maps[map] = replace_by;
|
|
return;
|
|
}
|
|
}
|
|
printf("replace_map: couldn't find map %s.\n", find->path);
|
|
}
|
|
|
|
/**
|
|
* Returns an initialised struct_map_info.
|
|
*
|
|
* @return
|
|
* new struct_map_info.
|
|
*/
|
|
static struct_map_info *create_map_info(void) {
|
|
struct_map_info *add = calloc(1, sizeof(struct_map_info));
|
|
|
|
add->min_monster = 2000;
|
|
init_map_list(&add->exits_to);
|
|
init_map_list(&add->exits_from);
|
|
init_map_list(&add->tiled_maps);
|
|
init_struct_map_in_quest_list(&add->quests);
|
|
init_race_list(&add->monsters);
|
|
init_npc_list(&add->npcs);
|
|
add->tiled_group = NULL;
|
|
|
|
return add;
|
|
}
|
|
|
|
/**
|
|
* Create a new tiled map and link it to the tiled map list.
|
|
*
|
|
* @return
|
|
* new tiled map.
|
|
*/
|
|
static struct_map_info *create_tiled_map(void) {
|
|
struct_map_info *add = create_map_info();
|
|
|
|
add_map(add, &tiled_map_list);
|
|
return add;
|
|
}
|
|
|
|
/**
|
|
* Merge two tiled maps groups. This can happen if based on processing we do one map with tiled maps,
|
|
* another with tiled maps, and later figure out the tiles are actually linked.
|
|
*
|
|
* @param map
|
|
* the map that being processed has a tiling to a map in another group. Its group will be the final merging group.
|
|
* @param tile
|
|
* the tile index causing the merge
|
|
* @param tiled_map
|
|
* the map tiled to another group. Its group will disappear.
|
|
*/
|
|
static void merge_tiled_maps(struct_map_info *map, int tile, struct_map_info *tiled_map) {
|
|
int g;
|
|
struct_map_info *group = tiled_map->tiled_group;
|
|
struct_map_info *change;
|
|
|
|
while (group->tiled_maps.count > 0) {
|
|
change = group->tiled_maps.maps[group->tiled_maps.count-1];
|
|
change->tiled_group = map->tiled_group;
|
|
add_map(change, &map->tiled_group->tiled_maps);
|
|
group->tiled_maps.count--;
|
|
}
|
|
|
|
for (g = 0; g < tiled_map_list.count; g++) {
|
|
if (tiled_map_list.maps[g] == group) {
|
|
if (g < tiled_map_list.count-1)
|
|
tiled_map_list.maps[g] = tiled_map_list.maps[tiled_map_list.count-1];
|
|
tiled_map_list.count--;
|
|
free(group);
|
|
return;
|
|
}
|
|
}
|
|
printf("tiled_map not in tiled_map_list!");
|
|
abort();
|
|
|
|
}
|
|
|
|
/**
|
|
* Gets or creates if required the info structure for a map.
|
|
*
|
|
* @param path
|
|
* map to consider.
|
|
* @return
|
|
* associated structure.
|
|
*/
|
|
static struct_map_info *get_map_info(const char *path) {
|
|
int map;
|
|
struct_map_info *add;
|
|
char *tmp;
|
|
|
|
for (map = 0; map < maps_list.count; map++) {
|
|
if (strcmp(maps_list.maps[map]->path, path) == 0)
|
|
return maps_list.maps[map];
|
|
}
|
|
|
|
add = create_map_info();
|
|
add->path = strdup(path);
|
|
tmp = strrchr(path, '/');
|
|
if (tmp)
|
|
add->filename = strdup(tmp+1);
|
|
else
|
|
add->filename = strdup(path);
|
|
|
|
add_map(add, &maps_list);
|
|
return add;
|
|
}
|
|
|
|
/**
|
|
* Marks specified path as processed.
|
|
*
|
|
* @param path
|
|
* map to remove.
|
|
*/
|
|
static void list_map(const char *path) {
|
|
int index;
|
|
|
|
for (index = 0; index < found_maps_count; index++) {
|
|
if (found_maps[index] && strcmp(path, found_maps[index]) == 0) {
|
|
free(found_maps[index]);
|
|
found_maps[index] = NULL;
|
|
return;
|
|
}
|
|
}
|
|
printf("Map processed but not found in directory reading? %s\n", path);
|
|
}
|
|
|
|
/**
|
|
* Links a map to a region.
|
|
*
|
|
* Will not readd the map if already linked.
|
|
*
|
|
* @param map
|
|
* map name.
|
|
* @param reg
|
|
* region to link the map to.
|
|
*/
|
|
static void add_map_to_region(struct_map_info *map, region *reg) {
|
|
int test;
|
|
int x, y;
|
|
|
|
for (test = 0; test < region_count; test++) {
|
|
if (regions[test]->reg == reg)
|
|
break;
|
|
}
|
|
if (test == region_count) {
|
|
if (test == region_allocated) {
|
|
region_allocated++;
|
|
regions = realloc(regions, sizeof(struct_region_info *)*region_allocated);
|
|
regions[test] = calloc(1, sizeof(struct_region_info));
|
|
}
|
|
region_count++;
|
|
regions[test]->reg = reg;
|
|
}
|
|
add_map(map, ®ions[test]->maps_list);
|
|
if (sscanf(map->path, "/world/world_%d_%d", &x, &y) == 2) {
|
|
regions[test]->sum_x += (x-100);
|
|
regions[test]->sum_y += (y-100);
|
|
regions[test]->sum++;
|
|
regions[test]->is_world = 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Saves a map to a file, based on jpg/png settings.
|
|
*
|
|
* @param file
|
|
* opened file to which to save.
|
|
* @param pic
|
|
* picture to save.
|
|
*/
|
|
static void save_picture(FILE *file, gdImagePtr pic) {
|
|
if (output_format == OF_PNG)
|
|
gdImagePng(pic, file);
|
|
else
|
|
gdImageJpeg(pic, file, jpeg_quality);
|
|
}
|
|
|
|
/**
|
|
* Creates a link between two maps if they are on different regions.
|
|
* @param source
|
|
* map from.
|
|
* @param dest
|
|
* map to.
|
|
* @param linkname
|
|
* name of the link as it should appear. Unused.
|
|
*/
|
|
static void add_region_link(mapstruct *source, mapstruct *dest, const char *linkname) {
|
|
int search = 0;
|
|
char entry[500];
|
|
region *s, *d;
|
|
|
|
s = get_region_by_map(source);
|
|
d = get_region_by_map(dest);
|
|
if (s == d)
|
|
return;
|
|
|
|
if (linkname && 0)
|
|
snprintf(entry, sizeof(entry), "%s -> %s [ label = \"%s\" ]\n", s->name, d->name, linkname);
|
|
else
|
|
snprintf(entry, sizeof(entry), "%s -> %s\n", s->name, d->name);
|
|
|
|
for (search = 0; search < regions_link_count; search++) {
|
|
if (strcmp(regions_link[search], entry) == 0)
|
|
return;
|
|
}
|
|
|
|
if (regions_link_count == regions_link_allocated) {
|
|
regions_link_allocated += 10;
|
|
regions_link = realloc(regions_link, sizeof(const char *)*regions_link_allocated);
|
|
}
|
|
regions_link[regions_link_count] = strdup(entry);
|
|
regions_link_count++;
|
|
}
|
|
|
|
/**
|
|
* Is the slaying field relevant for this item?
|
|
*
|
|
* @param item
|
|
* item to check.
|
|
* @return
|
|
* 1 if relevant, 0 else.
|
|
*/
|
|
static int is_slaying(object *item) {
|
|
return (item->type == LOCKED_DOOR || item->type == SPECIAL_KEY || item->type == CONTAINER || item->type == CHECK_INV);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a struct_slaying_info for specified slaying. Creates a new one if required.
|
|
*
|
|
* @param slaying
|
|
* value to get the structure of.
|
|
* @return
|
|
* structure for slaying. Never NULL.
|
|
*/
|
|
static struct_slaying_info *get_slaying_struct(const char *slaying) {
|
|
struct_slaying_info *add;
|
|
int l;
|
|
|
|
for (l = 0; l < slaying_count; l++) {
|
|
if (!strcmp(slaying_info[l]->slaying, slaying))
|
|
return slaying_info[l];
|
|
}
|
|
if (slaying_count == slaying_allocated) {
|
|
slaying_allocated += 10;
|
|
slaying_info = (struct_slaying_info **)realloc(slaying_info, sizeof(struct_slaying_info *)*slaying_allocated);
|
|
}
|
|
|
|
add = (struct_slaying_info *)calloc(1, sizeof(struct_slaying_info));
|
|
add->slaying = strdup(slaying);
|
|
for (l = 0; l < S_MAX; l++)
|
|
init_map_list(&add->maps[l]);
|
|
|
|
slaying_info[slaying_count] = add;
|
|
slaying_count++;
|
|
|
|
return add;
|
|
}
|
|
|
|
/**
|
|
* Adds the specified map to the slaying information if not already present.
|
|
*
|
|
* @param info
|
|
* structure to add to.
|
|
* @param item
|
|
* one of the S_xxx values specifying what type of slaying this is.
|
|
* @param map
|
|
* map to add.
|
|
*/
|
|
static void add_map_to_slaying(struct_slaying_info *info, int item, struct_map_info *map) {
|
|
add_map(map, &info->maps[item]);
|
|
}
|
|
|
|
/**
|
|
* Adds the item's information to the map.
|
|
*
|
|
* @param map
|
|
* map containing the item.
|
|
* @param item
|
|
* item which slaying field we're considering.
|
|
*/
|
|
static void add_slaying(struct_map_info *map, object *item) {
|
|
struct_slaying_info *info;
|
|
|
|
if (!item->slaying)
|
|
/* can be undefined */
|
|
return;
|
|
|
|
info = get_slaying_struct(item->slaying);
|
|
if (item->type == LOCKED_DOOR)
|
|
add_map_to_slaying(info, S_DOOR, map);
|
|
else if (item->type == SPECIAL_KEY)
|
|
add_map_to_slaying(info, S_KEY, map);
|
|
else if (item->type == CONTAINER)
|
|
add_map_to_slaying(info, S_CONTAINER, map);
|
|
else
|
|
add_map_to_slaying(info, S_CONNECT, map);
|
|
}
|
|
|
|
/**
|
|
* Recursively checks if the object should be considered for slaying information.
|
|
*
|
|
* @param map
|
|
* map containing the items.
|
|
* @param item
|
|
* item to consider. Must not be NULL.
|
|
*/
|
|
static void check_slaying_inventory(struct_map_info *map, object *item) {
|
|
object *inv;
|
|
|
|
for (inv = item->inv; inv; inv = inv->below) {
|
|
if (is_slaying(inv))
|
|
add_slaying(map, inv);
|
|
check_slaying_inventory(map, inv);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes a map.
|
|
*
|
|
* Generates the map pictures (big and small), and exit information.
|
|
*
|
|
* @param info
|
|
* map to process.
|
|
*/
|
|
static void process_map(struct_map_info *info) {
|
|
mapstruct *m;
|
|
int x, y, isworld;
|
|
object *item;
|
|
FILE *out;
|
|
gdImagePtr pic;
|
|
gdImagePtr small;
|
|
struct stat stats;
|
|
struct stat statspic;
|
|
char exit_path[500];
|
|
char tmppath[MAX_BUF];
|
|
char picpath[MAX_BUF], smallpicpath[MAX_BUF];
|
|
int needpic = 0;
|
|
struct_map_info *link;
|
|
|
|
if (list_unused_maps)
|
|
list_map(info->path);
|
|
|
|
if (show_maps)
|
|
printf(" processing map %s\n", info->path);
|
|
|
|
m = ready_map_name(info->path, 0);
|
|
if (!m) {
|
|
printf("couldn't load map %s\n", info->path);
|
|
return;
|
|
}
|
|
|
|
do_exit_map(m);
|
|
|
|
if (!rawmaps)
|
|
do_auto_apply(m);
|
|
|
|
info->level = m->difficulty;
|
|
if (m->maplore) {
|
|
info->lore = strdup(m->maplore);
|
|
process_map_lore(info);
|
|
}
|
|
|
|
isworld = (sscanf(info->path, "/world/world_%d_%d", &x, &y) == 2);
|
|
|
|
if (m->name)
|
|
info->name = strdup(m->name);
|
|
else
|
|
info->name = strdup(info->filename);
|
|
|
|
info->cfregion = get_region_by_map(m);
|
|
add_map_to_region(info, info->cfregion);
|
|
|
|
snprintf(picpath, sizeof(picpath), "%s%s%s", root, info->path, output_extensions[output_format]);
|
|
snprintf(smallpicpath, sizeof(smallpicpath), "%s%s.small%s", root, info->path, output_extensions[output_format]);
|
|
|
|
if (force_pics)
|
|
needpic = 1;
|
|
else if (generate_pics) {
|
|
create_pathname(info->path, tmppath, MAX_BUF);
|
|
stat(tmppath, &stats);
|
|
if (stat(picpath, &statspic) || (statspic.st_mtime < stats.st_mtime))
|
|
needpic = 1;
|
|
else if (stat(smallpicpath, &statspic) || (statspic.st_mtime < stats.st_mtime))
|
|
needpic = 1;
|
|
}
|
|
else
|
|
needpic = 0;
|
|
|
|
if (needpic) {
|
|
pic = gdImageCreateTrueColor(MAP_WIDTH(m)*32, MAP_HEIGHT(m)*32);
|
|
created_pics++;
|
|
}
|
|
else
|
|
cached_pics++;
|
|
|
|
for (x = 0; x < 4; x++)
|
|
if (m->tile_path[x] != NULL) {
|
|
path_combine_and_normalize(m->path, m->tile_path[x], exit_path, sizeof(exit_path));
|
|
create_pathname(exit_path, tmppath, MAX_BUF);
|
|
if (stat(tmppath, &stats)) {
|
|
printf(" map %s doesn't exist in map %s, for tile %d.\n", exit_path, info->path, x);
|
|
}
|
|
|
|
if (isworld) {
|
|
link = get_map_info(exit_path);
|
|
add_map(link, &info->exits_from);
|
|
add_map(info, &link->exits_to);
|
|
|
|
if (do_regions_link) {
|
|
mapstruct *link = ready_map_name(exit_path, 0);
|
|
|
|
if (link && link != m) {
|
|
/* no need to link a map with itself. Also, if the exit points to the same map, we don't
|
|
* want to reset it. */
|
|
add_region_link(m, link, NULL);
|
|
link->reset_time = 1;
|
|
link->in_memory = MAP_IN_MEMORY;
|
|
delete_map(link);
|
|
}
|
|
}
|
|
} else {
|
|
link = get_map_info(exit_path);
|
|
info->tiles[x] = link;
|
|
if (link->tiled_group) {
|
|
if (info->tiled_group && link->tiled_group != info->tiled_group) {
|
|
merge_tiled_maps(info, x, link);
|
|
continue;
|
|
}
|
|
if (link->tiled_group == info->tiled_group) {
|
|
continue;
|
|
}
|
|
if (!info->tiled_group) {
|
|
add_map(info, &link->tiled_group->tiled_maps);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!info->tiled_group) {
|
|
info->tiled_group = create_tiled_map();
|
|
add_map(info, &info->tiled_group->tiled_maps);
|
|
}
|
|
link->tiled_group = info->tiled_group;
|
|
add_map(link, &info->tiled_group->tiled_maps);
|
|
}
|
|
}
|
|
|
|
info->width = MAP_WIDTH(m);
|
|
info->height = MAP_HEIGHT(m);
|
|
|
|
for (x = MAP_WIDTH(m)-1; x >= 0; x--)
|
|
for (y = MAP_HEIGHT(m)-1; y >= 0; y--) {
|
|
for (item = GET_MAP_OB(m, x, y); item; item = item->above) {
|
|
if (item->type == EXIT || item->type == TELEPORTER || item->type == PLAYER_CHANGER) {
|
|
char ep[500];
|
|
const char *start;
|
|
|
|
if (!item->slaying) {
|
|
ep[0] = '\0';
|
|
if (warn_no_path)
|
|
printf(" exit without any path at %d, %d on %s\n", item->x, item->y, info->path);
|
|
} else {
|
|
memset(ep, 0, 500);
|
|
if (strcmp(item->slaying, "/!"))
|
|
strcpy(ep, EXIT_PATH(item));
|
|
else {
|
|
if (!item->msg) {
|
|
printf(" random map without message in %s at %d, %d\n", info->path, item->x, item->y);
|
|
} else {
|
|
/* Some maps have a 'exit_on_final_map' flag, ignore it. */
|
|
start = strstr(item->msg, "\nfinal_map ");
|
|
if (!start && strncmp(item->msg, "final_map", strlen("final_map")) == 0)
|
|
/* Message start is final_map, nice */
|
|
start = item->msg;
|
|
if (start) {
|
|
char *end = strchr(start+1, '\n');
|
|
|
|
start += strlen("final_map")+2;
|
|
strncpy(ep, start, end-start);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (strlen(ep)) {
|
|
path_combine_and_normalize(m->path, ep, exit_path, 500);
|
|
create_pathname(exit_path, tmppath, MAX_BUF);
|
|
if (stat(tmppath, &stats)) {
|
|
printf(" map %s doesn't exist in map %s, at %d, %d.\n", ep, info->path, item->x, item->y);
|
|
} else {
|
|
link = get_map_info(exit_path);
|
|
add_map(link, &info->exits_from);
|
|
add_map(info, &link->exits_to);
|
|
|
|
if (do_regions_link) {
|
|
mapstruct *link = ready_map_name(exit_path, 0);
|
|
|
|
if (link && link != m) {
|
|
/* no need to link a map with itself. Also, if the exit points to the same map, we don't
|
|
* want to reset it. */
|
|
add_region_link(m, link, item->arch->clone.name);
|
|
link->reset_time = 1;
|
|
link->in_memory = MAP_IN_MEMORY;
|
|
delete_map(link);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (is_slaying(item))
|
|
add_slaying(info, item);
|
|
|
|
check_equipment(item, info);
|
|
|
|
check_slaying_inventory(info, item);
|
|
|
|
if (QUERY_FLAG(item, FLAG_MONSTER)) {
|
|
/* need to get the "real" archetype, as the item's archetype can certainly be a temporary one. */
|
|
archetype *arch = find_archetype(item->arch->name);
|
|
|
|
add_monster(item, info);
|
|
if ((QUERY_FLAG(item, FLAG_UNAGGRESSIVE) || QUERY_FLAG(item, FLAG_FRIENDLY)) && (item->msg != arch->clone.msg) && (item->msg != NULL))
|
|
add_npc_to_map(info, item);
|
|
}
|
|
|
|
if (item->invisible)
|
|
continue;
|
|
|
|
if (needpic) {
|
|
int sx, sy, hx, hy;
|
|
|
|
if (gdfaces[item->face->number] == NULL) {
|
|
int set = get_face_fallback(tileset, item->face->number);
|
|
|
|
gdfaces[item->face->number] = gdImageCreateFromPngPtr(facesets[set].faces[item->face->number].datalen, facesets[set].faces[item->face->number].data);
|
|
pics_allocated++;
|
|
}
|
|
if (item->head || item->more) {
|
|
get_multi_size(item, &sx, &sy, &hx, &hy);
|
|
} else {
|
|
hx = 0;
|
|
hy = 0;
|
|
}
|
|
if (gdfaces[item->face->number] != NULL && ((!item->head && !item->more) || (item->arch->clone.x+hx == 0 && item->arch->clone.y+hy == 0))) {
|
|
gdImageCopy(pic, gdfaces[item->face->number], x*32, y*32, 0, 0, gdfaces[item->face->number]->sx, gdfaces[item->face->number]->sy);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (needpic) {
|
|
make_path_to_file(picpath);
|
|
out = fopen(picpath, "wb+");
|
|
save_picture(out, pic);
|
|
fclose(out);
|
|
|
|
small = gdImageCreateTrueColor(MAP_WIDTH(m)*size_small, MAP_HEIGHT(m)*size_small);
|
|
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);
|
|
|
|
out = fopen(smallpicpath, "wb+");
|
|
save_picture(out, small);
|
|
fclose(out);
|
|
gdImageDestroy(small);
|
|
|
|
gdImageDestroy(pic);
|
|
|
|
info->pic_was_done = 1;
|
|
}
|
|
|
|
m->reset_time = 1;
|
|
m->in_memory = MAP_IN_MEMORY;
|
|
delete_map(m);
|
|
}
|
|
|
|
/**
|
|
* Creates the page for a map index.
|
|
*
|
|
* @param dest
|
|
* path relative to root where the index will be located, without leading /. Used to compute the map's path relative to the index.
|
|
* @param maps_list
|
|
* maps in the index.
|
|
* @param template_page
|
|
* global page template.
|
|
* @param template_letter
|
|
* template for one letter of the index.
|
|
* @param template_map
|
|
* template for one map.
|
|
* @param vars
|
|
* template variables to give access to.
|
|
* @param values
|
|
* associated values.
|
|
* @return
|
|
* processed template. Should be free() by the caller.
|
|
*/
|
|
static char *do_map_index(const char *dest, struct_map_list *maps_list,
|
|
const char *template_page, const char *template_letter,
|
|
const char *template_map, const char **vars,
|
|
const char **values) {
|
|
#define VARSADD 6
|
|
int map;
|
|
char *string;
|
|
char mappath[500];
|
|
char maphtml[500];
|
|
char count[50];
|
|
char lettercount[50];
|
|
char *tmp;
|
|
const char **idx_vars;
|
|
const char **idx_values;
|
|
char str_letter[2];
|
|
char last_letter;
|
|
char index_path[500];
|
|
char *mapstext = NULL;
|
|
int byletter;
|
|
int basevalues, realcount = 0;
|
|
struct_map_info *last_group = NULL;
|
|
|
|
if (!generate_index)
|
|
return strdup("");
|
|
|
|
if (vars)
|
|
for (basevalues = 0; vars[basevalues] != NULL; basevalues++)
|
|
;
|
|
else
|
|
basevalues = 0;
|
|
|
|
idx_vars = malloc(sizeof(char *)*(basevalues+VARSADD));
|
|
idx_vars[0] = "MAPCOUNT";
|
|
memcpy(&idx_vars[1], vars, sizeof(char *)*basevalues);
|
|
idx_vars[basevalues+VARSADD-1] = NULL;
|
|
|
|
idx_values = malloc(sizeof(char *)*(basevalues+VARSADD-1));
|
|
memcpy(&idx_values[1], values, sizeof(char *)*basevalues);
|
|
|
|
string = NULL;
|
|
|
|
idx_values[0] = count;
|
|
/* wrong value, but in case the template needs to display something... */
|
|
snprintf(count, sizeof(count), "%d", maps_list->count);
|
|
|
|
idx_vars[basevalues+1] = "MAPNAME";
|
|
idx_vars[basevalues+2] = "MAPPATH";
|
|
idx_vars[basevalues+3] = "MAPHTML";
|
|
idx_vars[basevalues+4] = NULL;
|
|
|
|
qsort(maps_list->maps, maps_list->count, sizeof(const char *), sort_map_info);
|
|
|
|
last_letter = '\0';
|
|
str_letter[0] = '\0';
|
|
str_letter[1] = '\0';
|
|
|
|
strcpy(index_path, "/");
|
|
strcat(index_path, dest);
|
|
|
|
string = NULL;
|
|
for (map = 0; map < maps_list->count; map++) {
|
|
if (tolower(maps_list->maps[map]->name[0]) != last_letter) {
|
|
if (mapstext != NULL) {
|
|
idx_vars[basevalues+1] = "MAPS";
|
|
idx_vars[basevalues+2] = "LETTER";
|
|
idx_vars[basevalues+3] = "LETTERCOUNT";
|
|
idx_vars[basevalues+4] = NULL;
|
|
idx_values[basevalues+1] = mapstext;
|
|
idx_values[basevalues+2] = str_letter;
|
|
snprintf(lettercount, sizeof(lettercount), "%d", byletter);
|
|
idx_values[basevalues+3] = lettercount;
|
|
string = cat_template(string, do_template(template_letter, idx_vars, idx_values));
|
|
free(mapstext);
|
|
mapstext = NULL;
|
|
idx_values[basevalues+2] = NULL;
|
|
}
|
|
last_letter = tolower(maps_list->maps[map]->name[0]);
|
|
str_letter[0] = last_letter;
|
|
byletter = 0;
|
|
last_group = NULL;
|
|
}
|
|
|
|
if (last_group && last_group == maps_list->maps[map]->tiled_group)
|
|
continue;
|
|
else
|
|
last_group = maps_list->maps[map]->tiled_group;
|
|
|
|
realcount++;
|
|
idx_vars[basevalues+1] = "MAPNAME";
|
|
idx_vars[basevalues+2] = "MAPPATH";
|
|
idx_vars[basevalues+3] = "MAPHTML";
|
|
idx_values[basevalues+1] = last_group ? last_group->name : (maps_list->maps[map]->name ? maps_list->maps[map]->name : maps_list->maps[map]->path);
|
|
relative_path(index_path, last_group ? last_group->path : maps_list->maps[map]->path, mappath);
|
|
strcpy(maphtml, mappath);
|
|
strcat(maphtml, ".html");
|
|
idx_values[basevalues+2] = mappath;
|
|
idx_values[basevalues+3] = maphtml;
|
|
mapstext = cat_template(mapstext, do_template(template_map, idx_vars, idx_values));
|
|
byletter++;
|
|
}
|
|
if (last_letter != '\0') {
|
|
idx_vars[basevalues+1] = "MAPS";
|
|
idx_vars[basevalues+2] = "LETTER";
|
|
idx_vars[basevalues+3] = "LETTERCOUNT";
|
|
idx_vars[basevalues+4] = NULL;
|
|
idx_values[basevalues+1] = mapstext;
|
|
idx_values[basevalues+2] = str_letter;
|
|
snprintf(lettercount, sizeof(lettercount), "%d", byletter);
|
|
idx_values[basevalues+3] = lettercount;
|
|
string = cat_template(string, do_template(template_letter, idx_vars, idx_values));
|
|
free(mapstext);
|
|
mapstext = NULL;
|
|
idx_values[basevalues+2] = NULL;
|
|
}
|
|
|
|
snprintf(count, sizeof(count), "%d", realcount);
|
|
idx_values[basevalues+1] = string;
|
|
idx_vars[basevalues+1] = "LETTERS";
|
|
idx_vars[basevalues+2] = NULL;
|
|
tmp = do_template(template_page, idx_vars, idx_values);
|
|
free(string);
|
|
free(idx_vars);
|
|
free(idx_values);
|
|
return tmp;
|
|
}
|
|
|
|
/**
|
|
* Generates the web page for a region.
|
|
*
|
|
* @param reg
|
|
* region/maps for which to generate.
|
|
*
|
|
* @note
|
|
* will sort the maps.
|
|
*/
|
|
static void write_region_page(struct_region_info *reg) {
|
|
char *string;
|
|
FILE *index;
|
|
char html[500];
|
|
const char *vars[] = { "REGIONNAME", "REGIONHTML", "REGIONLONGNAME", "REGIONDESC", NULL };
|
|
const char *values[] = { reg->reg->name, html, NULL, NULL };
|
|
|
|
printf("Generating map index for region %s...", reg->reg->name);
|
|
|
|
values[2] = get_region_longname(reg->reg);
|
|
values[3] = get_region_msg(reg->reg);
|
|
|
|
strcpy(html, reg->reg->name);
|
|
strcat(html, ".html");
|
|
|
|
string = do_map_index(html, ®->maps_list, region_template, region_letter_template, region_map_template, vars, values);
|
|
|
|
strcpy(html, root);
|
|
strcat(html, "/");
|
|
strcat(html, reg->reg->name);
|
|
strcat(html, ".html");
|
|
index = fopen(html, "w+");
|
|
fprintf(index, string);
|
|
fclose(index);
|
|
free(string);
|
|
|
|
printf(" done.\n");
|
|
|
|
}
|
|
|
|
/**
|
|
* Generates all map indexes for a region.
|
|
*/
|
|
static void write_all_regions(void) {
|
|
int reg;
|
|
|
|
qsort(regions, region_count, sizeof(struct_region_info *), sort_region);
|
|
|
|
for (reg = 0; reg < region_count; reg++)
|
|
write_region_page(regions[reg]);
|
|
}
|
|
|
|
/**
|
|
* Generates global map index, file maps.html.
|
|
*/
|
|
static void write_maps_index(void) {
|
|
char index_path[500];
|
|
char *tmp;
|
|
FILE *index;
|
|
|
|
printf("Generating global map index in maps.html...");
|
|
|
|
tmp = do_map_index("maps.html", &maps_list, index_template, index_letter, index_map, NULL, NULL);
|
|
|
|
strcpy(index_path, root);
|
|
strcat(index_path, "/maps.html");
|
|
index = fopen(index_path, "w+");
|
|
fprintf(index, tmp);
|
|
fclose(index);
|
|
free(tmp);
|
|
|
|
printf(" done.\n");
|
|
}
|
|
|
|
/**
|
|
* Generates region index.
|
|
*/
|
|
static void write_region_index(void) {
|
|
char *txt;
|
|
char *final;
|
|
char count[10];
|
|
struct_region_info *region;
|
|
int reg;
|
|
char file[500];
|
|
const char *vars[] = { "REGIONCOUNT", "REGIONFILE", "REGIONNAME", NULL };
|
|
const char *values[] = { count, file, NULL };
|
|
FILE *out;
|
|
|
|
printf("Generating regions index in regions.html...");
|
|
|
|
snprintf(count, sizeof(count), "%d", region_count);
|
|
txt = NULL;
|
|
|
|
for (reg = 0; reg < region_count; reg++) {
|
|
region = regions[reg];
|
|
snprintf(file, sizeof(file), "%s.html", region->reg->name);
|
|
values[2] = get_region_longname(region->reg);
|
|
txt = cat_template(txt, do_template(index_region_region_template, vars, values));
|
|
}
|
|
vars[1] = "REGIONS";
|
|
values[1] = txt;
|
|
vars[2] = NULL;
|
|
final = do_template(index_region_template, vars, values);
|
|
free(txt);
|
|
|
|
strcpy(file, root);
|
|
strcat(file, "/regions.html");
|
|
out = fopen(file, "w+");
|
|
fprintf(out, final);
|
|
fclose(out);
|
|
free(final);
|
|
|
|
printf(" done.\n");
|
|
}
|
|
|
|
/**
|
|
* Generates a big world map.
|
|
*/
|
|
static void write_world_map(void) {
|
|
#define SIZE 50
|
|
int x, y;
|
|
FILE *out;
|
|
int wx, wy;
|
|
char file[500];
|
|
char *map = NULL;
|
|
char *total;
|
|
char *row = NULL;
|
|
char mapleft[10], maptop[10], mapright[10], mapbottom[10];
|
|
const char *vars[] = { NULL, NULL, "MAPLEFT", "MAPTOP", "MAPRIGHT", "MAPBOTTOM", NULL };
|
|
const char *values[] = { NULL, NULL, mapleft, maptop, mapright, mapbottom, NULL };
|
|
char name[100];
|
|
char mappath[500], mapraw[500], mapregion[500];
|
|
gdImagePtr pic;
|
|
gdImagePtr small;
|
|
gdFontPtr font;
|
|
int region, color;
|
|
|
|
if (!world_map)
|
|
return;
|
|
|
|
printf("Generating world map in world.html...");
|
|
fflush(stdout);
|
|
|
|
pic = gdImageCreateTrueColor(SIZE*30, SIZE*30);
|
|
|
|
strcpy(file, root);
|
|
strcat(file, "/world.html");
|
|
|
|
wx = 100;
|
|
wy = 100;
|
|
|
|
for (y = 0; y < 30; y++) {
|
|
for (x = 0; x < 30; x++) {
|
|
values[0] = name;
|
|
vars[0] = "MAPNAME";
|
|
vars[1] = "MAPPATH";
|
|
values[1] = mappath,
|
|
snprintf(name, sizeof(name), "world_%d_%d", wx, wy);
|
|
snprintf(mappath, sizeof(mappath), "world/%s.html", name);
|
|
snprintf(mapleft, sizeof(mapleft), "%d", SIZE*x);
|
|
snprintf(maptop, sizeof(maptop), "%d", SIZE*y);
|
|
snprintf(mapright, sizeof(mapright), "%d", SIZE*(x+1)-1);
|
|
snprintf(mapbottom, sizeof(mapbottom), "%d", SIZE*(y+1)-1);
|
|
|
|
map = cat_template(map, do_template(world_map_template, vars, values));
|
|
|
|
snprintf(mappath, sizeof(mappath), "%s/world/%s%s", root, name, output_extensions[output_format]);
|
|
|
|
out = fopen(mappath, "rb");
|
|
if (!out) {
|
|
printf("\n warning: large pic not found for world_%d_%d", wx, wy);
|
|
wx++;
|
|
continue;
|
|
}
|
|
if (output_format == OF_PNG)
|
|
small = gdImageCreateFromPng(out);
|
|
else
|
|
small = gdImageCreateFromJpeg(out);
|
|
fclose(out);
|
|
if (!small) {
|
|
printf("\n warning: pic not found for world_%d_%d", wx, wy);
|
|
wx++;
|
|
continue;
|
|
}
|
|
gdImageCopyResized(pic, small, SIZE*x, SIZE*y, 0, 0, SIZE, SIZE, small->sx, small->sy);
|
|
gdImageDestroy(small);
|
|
|
|
wx++;
|
|
}
|
|
wy++;
|
|
wx = 100;
|
|
values[0] = map;
|
|
vars[0] = "MAPS";
|
|
vars[1] = NULL;
|
|
row = cat_template(row, do_template(world_row_template, vars, values));
|
|
free(map);
|
|
map = NULL;
|
|
}
|
|
snprintf(mappath, sizeof(mappath), "world%s", output_extensions[output_format]);
|
|
snprintf(mapraw, sizeof(mapraw), "world_raw%s", output_extensions[output_format]);
|
|
snprintf(mapregion, sizeof(mapregion), "world_regions%s", output_extensions[output_format]);
|
|
|
|
values[0] = row;
|
|
vars[0] = "MAPS";
|
|
values[1] = mappath;
|
|
vars[1] = "WORLDMAP";
|
|
values[2] = mapraw;
|
|
vars[2] = "WORLDRAW";
|
|
values[3] = mapregion;
|
|
vars[3] = "WORLDREGIONS";
|
|
vars[4] = NULL;
|
|
total = do_template(world_template, vars, values);
|
|
free(row);
|
|
out = fopen(file, "w+");
|
|
fprintf(out, total);
|
|
free(total);
|
|
fclose(out);
|
|
|
|
snprintf(mappath, sizeof(mappath), "%s/world_raw%s", root, output_extensions[output_format]);
|
|
out = fopen(mappath, "wb+");
|
|
save_picture(out, pic);
|
|
fclose(out);
|
|
|
|
/* Write region names. */
|
|
small = gdImageCreateTrueColor(SIZE*30, SIZE*30);
|
|
font = gdFontGetGiant();
|
|
color = gdImageColorAllocateAlpha(pic, 255, 0, 0, 20);
|
|
for (region = 0; region < region_allocated; region++) {
|
|
if (!regions[region]->is_world || regions[region]->sum == 0)
|
|
continue;
|
|
|
|
x = regions[region]->sum_x*SIZE/regions[region]->sum+SIZE/2-strlen(regions[region]->reg->name)*font->w/2;
|
|
y = regions[region]->sum_y*SIZE/regions[region]->sum+SIZE/2-font->h/2;
|
|
gdImageString(small, font, x, y, regions[region]->reg->name, color);
|
|
gdImageString(pic, font, x, y, regions[region]->reg->name, color);
|
|
|
|
/* For exit/road map, size isn't the same. */
|
|
x = regions[region]->sum_x*50/regions[region]->sum+50/2-strlen(regions[region]->reg->name)*font->w/2;
|
|
y = regions[region]->sum_y*50/regions[region]->sum+50/2-font->h/2;
|
|
gdImageString(infomap, font, x, y, regions[region]->reg->name, color);
|
|
}
|
|
|
|
snprintf(mappath, sizeof(mappath), "%s/world_regions%s", root, output_extensions[output_format]);
|
|
out = fopen(mappath, "wb+");
|
|
save_picture(out, small);
|
|
fclose(out);
|
|
gdImageDestroy(small);
|
|
|
|
snprintf(mappath, sizeof(mappath), "%s/world%s", root, output_extensions[output_format]);
|
|
out = fopen(mappath, "wb+");
|
|
save_picture(out, pic);
|
|
fclose(out);
|
|
gdImageDestroy(pic);
|
|
|
|
printf(" done.\n");
|
|
#undef SIZE
|
|
}
|
|
|
|
/**
|
|
* Write a map page.
|
|
*
|
|
* @param map
|
|
* map to write page of.
|
|
*/
|
|
static void write_map_page(struct_map_info *map) {
|
|
char *exits_text;
|
|
char *exits_to;
|
|
char *maplore;
|
|
char *tmp;
|
|
char *quests, *quest;
|
|
char *monsters;
|
|
|
|
char htmlpath[500]; /* Map file path. */
|
|
char mappic[500]; /* Name of map's full size picture. */
|
|
char mapsmallpic[500]; /* Name of map's small size picture. */
|
|
char indexpath[500]; /* Relative path of full index. */
|
|
char regionpath[500]; /* Path to region's filename. */
|
|
char regionname[500]; /* Name of map's region. */
|
|
char regionindexpath[500]; /* Path to region index file. */
|
|
char worldmappath[500]; /* Path to world map. */
|
|
char exit_path[500];
|
|
char maplevel[5], minmonster[5], maxmonster[5];
|
|
FILE *out;
|
|
char questpath[500], questtemp[500];
|
|
const char *quest_vars[] = { "NAME", "PATH", "TEXT", NULL };
|
|
const char *quest_vals[] = { NULL, questpath, NULL, NULL };
|
|
const char *q_vars[] = { "QUESTS", NULL };
|
|
const char *q_vals[] = { NULL, NULL };
|
|
const char *m_vars[] = { "NAME", NULL };
|
|
const char *m_vals[] = { NULL, NULL };
|
|
const char *vars[] = { "NAME", "MAPPATH", "MAPNAME", "MAPPIC", "MAPSMALLPIC", "MAPEXITFROM", "INDEXPATH", "REGIONPATH", "REGIONNAME", "REGIONINDEXPATH", "WORLDMAPPATH", "MAPLORE", "MAPEXITTO", "MAPLEVEL", "QUESTS", "MONSTERS", "MINMONSTER", "MAXMONSTER", NULL, NULL, NULL };
|
|
const char *values[] = { map->path, htmlpath, map->name, mappic, mapsmallpic, "", indexpath, regionpath, regionname, regionindexpath, worldmappath, "", "", maplevel, NULL, "", minmonster, maxmonster, NULL, NULL, NULL };
|
|
int vars_count = 0;
|
|
|
|
while (vars[vars_count])
|
|
vars_count++;
|
|
|
|
snprintf(minmonster, sizeof(minmonster), "%d", map->min_monster);
|
|
snprintf(maxmonster, sizeof(maxmonster), "%d", map->max_monster);
|
|
|
|
relative_path(map->path, "/maps.html", indexpath);
|
|
relative_path(map->path, "/world.html", worldmappath);
|
|
relative_path(map->path, "/regions.html", regionindexpath);
|
|
|
|
if (map->cfregion) {
|
|
strcpy(regionname, get_region_longname(map->cfregion));
|
|
strcpy(exit_path, "/");
|
|
strcat(exit_path, map->cfregion->name);
|
|
strcat(exit_path, ".html");
|
|
relative_path(map->path, exit_path, regionpath);
|
|
} else {
|
|
regionpath[0]='\0';
|
|
snprintf(regionname, sizeof(regionname), "(map was not processed)");
|
|
}
|
|
|
|
snprintf(mappic, sizeof(mappic), "%s%s", map->filename, output_extensions[output_format]);
|
|
snprintf(mapsmallpic, sizeof(mapsmallpic), "%s.small%s", map->filename, output_extensions[output_format]);
|
|
|
|
snprintf(htmlpath, sizeof(htmlpath), "%s%s.html", root, map->path);
|
|
make_path_to_file(htmlpath);
|
|
|
|
values[14] = "";
|
|
|
|
snprintf(maplevel, sizeof(maplevel), "%d", map->level);
|
|
if (map->lore && map->lore[0] != '\0') {
|
|
values[11] = map->lore;
|
|
maplore = do_template(map_lore_template, vars, values);
|
|
} else {
|
|
maplore = do_template(map_no_lore_template, vars, values);
|
|
}
|
|
values[11] = maplore;
|
|
|
|
if (map->exits_from.count) {
|
|
char *one_exit = NULL;
|
|
int exit;
|
|
char relative[500];
|
|
|
|
vars[vars_count] = "EXITNAME";
|
|
vars[vars_count+1] = "EXITFILE";
|
|
|
|
qsort(map->exits_from.maps, map->exits_from.count, sizeof(const char *), sort_map_info);
|
|
for (exit = 0; exit < map->exits_from.count; exit++) {
|
|
relative_path(map->path, map->exits_from.maps[exit]->path, relative);
|
|
values[vars_count] = map->exits_from.maps[exit]->name;
|
|
strcat(relative, ".html");
|
|
values[vars_count+1] = relative;
|
|
one_exit = cat_template(one_exit, do_template(map_exit_template, vars, values));
|
|
}
|
|
vars[vars_count] = "EXIT";
|
|
vars[vars_count+1] = NULL;
|
|
values[vars_count] = one_exit;
|
|
exits_text = do_template(map_with_exit_template, vars, values);
|
|
free(one_exit);
|
|
}
|
|
else
|
|
exits_text = do_template(map_no_exit_template, vars, values);
|
|
|
|
values[5] = exits_text;
|
|
|
|
if (map->exits_to.count) {
|
|
char *one_exit = NULL;
|
|
int exit;
|
|
char relative[500];
|
|
|
|
vars[vars_count] = "EXITNAME";
|
|
vars[vars_count+1] = "EXITFILE";
|
|
|
|
qsort(map->exits_to.maps, map->exits_to.count, sizeof(struct_map_info *), sort_map_info);
|
|
for (exit = 0; exit < map->exits_to.count; exit++) {
|
|
relative_path(map->path, map->exits_to.maps[exit]->path, relative);
|
|
values[vars_count] = map->exits_to.maps[exit]->name;
|
|
strcat(relative, ".html");
|
|
values[vars_count+1] = relative;
|
|
one_exit = cat_template(one_exit, do_template(map_exit_to_template, vars, values));
|
|
}
|
|
vars[vars_count] = "EXIT";
|
|
vars[vars_count+1] = NULL;
|
|
values[vars_count] = one_exit;
|
|
exits_to = do_template(map_with_exit_to_template, vars, values);
|
|
free(one_exit);
|
|
} else
|
|
exits_to = do_template(map_no_exit_to_template, vars, values);
|
|
|
|
values[12] = exits_to;
|
|
|
|
if (map->quests.count) {
|
|
int q;
|
|
|
|
quest = NULL;
|
|
for (q = 0; q < map->quests.count; q++) {
|
|
quest_vals[0] = map->quests.list[q]->quest->name;
|
|
relative_path(map->path, "/quests.html", questtemp);
|
|
snprintf(questpath, sizeof(questpath), "%s#quest_%d", questtemp, map->quests.list[q]->quest->number);
|
|
quest_vals[2] = map->quests.list[q]->description;
|
|
quest = cat_template(quest, do_template(map_one_quest_template, quest_vars, quest_vals));
|
|
}
|
|
|
|
q_vals[0] = quest;
|
|
quests = do_template(map_with_quests_template, q_vars, q_vals);
|
|
free(quest);
|
|
quest = NULL;
|
|
} else
|
|
quests = strdup(map_no_quest_template);
|
|
values[14] = quests;
|
|
|
|
if (map->monsters.count) {
|
|
int m;
|
|
|
|
qsort(map->monsters.races, map->monsters.count, sizeof(struct_race *), sort_race);
|
|
|
|
monsters = do_template(map_monster_before_template, vars, values);
|
|
for (m = 0; m < map->monsters.count; m++) {
|
|
m_vals[0] = map->monsters.races[m]->name;
|
|
monsters = cat_template(monsters, do_template(map_monster_one_template, m_vars, m_vals));
|
|
if (m != map->monsters.count-1)
|
|
monsters = cat_template(monsters, do_template(map_monster_between_template, vars, values));
|
|
}
|
|
monsters = cat_template(monsters, do_template(map_monster_after_template, vars, values));
|
|
} else
|
|
monsters = do_template(map_no_monster_template, vars, values);
|
|
values[15] = monsters;
|
|
|
|
vars[vars_count] = NULL;
|
|
out = fopen(htmlpath, "w+");
|
|
tmp = do_template(map_template, vars, values);
|
|
fprintf(out, tmp);
|
|
fclose(out);
|
|
free(tmp);
|
|
free(exits_text);
|
|
free(exits_to);
|
|
free(maplore);
|
|
free(quests);
|
|
free(monsters);
|
|
}
|
|
|
|
/** Ensures all maps have a name (if there was a limit to map processing, some maps will have a NULL name which causes issues). */
|
|
static void fix_map_names(void) {
|
|
int map;
|
|
|
|
for (map = 0; map < maps_list.count; map++) {
|
|
if (maps_list.maps[map]->name)
|
|
continue;
|
|
if (!maps_list.maps[map]->filename) {
|
|
printf("map without path!\n");
|
|
abort();
|
|
}
|
|
maps_list.maps[map]->name = strdup(maps_list.maps[map]->filename);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensures all tiled maps have a name, a region, a filename and a path.
|
|
* Will try to find a suitable name and region from the maps in the group.
|
|
* @todo
|
|
* use a better filename, try to get the start of the map filenames.
|
|
*/
|
|
static void fix_tiled_map(void) {
|
|
int map, tile;
|
|
char name[500];
|
|
char *slash, *test;
|
|
region *cfregion;
|
|
|
|
for (map = 0; map < tiled_map_list.count; map++) {
|
|
if (tiled_map_list.maps[map]->tiled_maps.count == 0) {
|
|
printf("empty tiled map group!");
|
|
abort();
|
|
}
|
|
|
|
snprintf(name, sizeof(name), "tiled_map_group_%d", map);
|
|
tiled_map_list.maps[map]->filename = strdup(name);
|
|
|
|
cfregion = NULL;
|
|
test = NULL;
|
|
|
|
for (tile = 0; tile < tiled_map_list.maps[map]->tiled_maps.count; tile++) {
|
|
if (tiled_map_list.maps[map]->tiled_maps.maps[tile]->cfregion == NULL)
|
|
/* map not processed, ignore it. */
|
|
continue;
|
|
|
|
if (!cfregion)
|
|
cfregion = tiled_map_list.maps[map]->tiled_maps.maps[tile]->cfregion;
|
|
else if (cfregion != tiled_map_list.maps[map]->tiled_maps.maps[tile]->cfregion) {
|
|
printf("*** warning: tiled maps %s and %s not in same region (%s and %s).\n",
|
|
tiled_map_list.maps[map]->tiled_maps.maps[0]->path, tiled_map_list.maps[map]->tiled_maps.maps[tile]->path,
|
|
tiled_map_list.maps[map]->tiled_maps.maps[0]->cfregion->name, tiled_map_list.maps[map]->tiled_maps.maps[tile]->cfregion->name);
|
|
cfregion = NULL;
|
|
}
|
|
|
|
if (strcmp(tiled_map_list.maps[map]->tiled_maps.maps[tile]->name, tiled_map_list.maps[map]->tiled_maps.maps[tile]->filename)) {
|
|
/* map has a custom name, use it */
|
|
if (!test)
|
|
test = tiled_map_list.maps[map]->tiled_maps.maps[tile]->name;
|
|
}
|
|
}
|
|
|
|
if (!test) {
|
|
/* this can happen of course if only partial maps were processed, but well... */
|
|
printf("*** warning: tiled map without any name. First map path %s\n", tiled_map_list.maps[map]->tiled_maps.maps[0]->path);
|
|
test = name;
|
|
}
|
|
|
|
tiled_map_list.maps[map]->name = strdup(test);
|
|
tiled_map_list.maps[map]->cfregion = cfregion;
|
|
|
|
strncpy(name, tiled_map_list.maps[map]->tiled_maps.maps[0]->path, sizeof(name));
|
|
slash = strrchr(name, '/');
|
|
if (!slash)
|
|
snprintf(name, sizeof(name), "/");
|
|
else
|
|
*(slash+1) = '\0';
|
|
strncat(name, tiled_map_list.maps[map]->filename, sizeof(name));
|
|
tiled_map_list.maps[map]->path = strdup(name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Changes for the list all maps to the tiled map they are part of, if applicable.
|
|
*
|
|
* @param current
|
|
* map currently being processed.
|
|
* @param from
|
|
* list that contains the exits to/from map to be fixed.
|
|
* @param is_from
|
|
* if non zero, <code>from</code> is exit_from field, else it is an exit_to.
|
|
*/
|
|
static void fix_exits_for_map(struct_map_info *current, struct_map_list *from, int is_from) {
|
|
int map, max;
|
|
struct_map_info *group;
|
|
|
|
max = from->count-1;
|
|
for (map = max; map >= 0; map--) {
|
|
if (from->maps[map]->tiled_group) {
|
|
group = from->maps[map]->tiled_group;
|
|
if (map != max)
|
|
from->maps[map] = from->maps[max];
|
|
from->count--;
|
|
max--;
|
|
add_map(group, from);
|
|
add_map(current->tiled_group ? current->tiled_group : current, is_from ? &group->exits_to : &group->exits_from);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Changes all exits to maps in a tiled map to point directly to the tiled map. Same for region lists. */
|
|
static void fix_exits_to_tiled_maps(void) {
|
|
int map, region, max;
|
|
struct_map_info *group;
|
|
|
|
for (map = 0; map < maps_list.count; map++) {
|
|
fix_exits_for_map(maps_list.maps[map], &maps_list.maps[map]->exits_from, 1);
|
|
fix_exits_for_map(maps_list.maps[map], &maps_list.maps[map]->exits_to, 0);
|
|
}
|
|
|
|
for (region = 0; region < region_count; region++) {
|
|
max = regions[region]->maps_list.count-1;
|
|
for (map = max; map >= 0; map--) {
|
|
if (regions[region]->maps_list.maps[map]->tiled_group) {
|
|
group = regions[region]->maps_list.maps[map]->tiled_group;
|
|
if (map != max)
|
|
regions[region]->maps_list.maps[map] = regions[region]->maps_list.maps[max];
|
|
regions[region]->maps_list.count--;
|
|
max--;
|
|
add_map(group, ®ions[region]->maps_list);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Makes all monsters point to tiled maps instead of map when appliable, and merge
|
|
* map monster to tiled map.
|
|
*/
|
|
static void fix_tiled_map_monsters(void) {
|
|
int map, race, max;
|
|
struct_map_info *group;
|
|
|
|
for (race = 0; race < races.count; race++) {
|
|
max = races.races[race]->origin.count-1;
|
|
for (map = max; map >= 0; map--) {
|
|
if (races.races[race]->origin.maps[map]->tiled_group) {
|
|
group = races.races[race]->origin.maps[map]->tiled_group;
|
|
if (map != max)
|
|
races.races[race]->origin.maps[map] = races.races[race]->origin.maps[max];
|
|
races.races[race]->origin.count--;
|
|
max--;
|
|
add_map(group, &races.races[race]->origin);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (map = 0; map < maps_list.count; map++) {
|
|
if (maps_list.maps[map]->tiled_group) {
|
|
for (race = 0; race < maps_list.maps[map]->monsters.count; race++) {
|
|
add_race_to_list(maps_list.maps[map]->monsters.races[race], &maps_list.maps[map]->tiled_group->monsters, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Ensures all maps have a name, and writes all map pages. */
|
|
static void write_all_maps(void) {
|
|
int map;
|
|
|
|
printf("Writing map pages...");
|
|
|
|
for (map = 0; map < maps_list.count; map++)
|
|
if (!maps_list.maps[map]->tiled_group)
|
|
write_map_page(maps_list.maps[map]);
|
|
|
|
printf(" done.\n");
|
|
}
|
|
|
|
static int tiled_map_need_pic(struct_map_info *map) {
|
|
int test;
|
|
char picpath[500];
|
|
struct stat stats;
|
|
|
|
snprintf(picpath, sizeof(picpath), "%s%s%s", root, map->path, output_extensions[output_format]);
|
|
if (stat(picpath, &stats))
|
|
return 1;
|
|
|
|
snprintf(picpath, sizeof(picpath), "%s%s.small%s", root, map->path, output_extensions[output_format]);
|
|
if (stat(picpath, &stats))
|
|
return 1;
|
|
|
|
for (test = 0; test < map->tiled_maps.count; test++) {
|
|
if (map->tiled_maps.maps[test]->pic_was_done)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Generates the large and small pictures for a tiled map.
|
|
* This uses the large/small pictures made during process_map(), so having a map limit could lead
|
|
* to maps not found and invalid results.
|
|
*
|
|
* @param map
|
|
* tiled map to make the picture of.
|
|
* @todo
|
|
* add a field to struct_map_info to remember if pic was updated or not, and update the tiled map
|
|
* only if one map has changed / the pic doesn't exist.
|
|
*/
|
|
static void do_tiled_map_picture(struct_map_info *map) {
|
|
int xmin = 0, xmax = 0, ymin = 0, ymax = 0, tiled, count, last;
|
|
char picpath[500];
|
|
gdImagePtr small, large, load;
|
|
FILE *out;
|
|
struct_map_info *current;
|
|
|
|
if (!generate_pics)
|
|
return;
|
|
|
|
printf(" Generating composite map for %s...", map->name);
|
|
fflush(stdout);
|
|
|
|
if (!tiled_map_need_pic(map)) {
|
|
printf(" already uptodate.\n");
|
|
return;
|
|
}
|
|
|
|
count = map->tiled_maps.count;
|
|
if (count == 0) {
|
|
printf("Tiled map without tiled maps?\n");
|
|
abort();
|
|
}
|
|
map->tiled_maps.maps[0]->processed = 1;
|
|
map->tiled_maps.maps[0]->tiled_x_from = 0;
|
|
map->tiled_maps.maps[0]->tiled_y_from = 0;
|
|
|
|
while (count > 0) {
|
|
last = count;
|
|
|
|
for (tiled = 0; tiled < map->tiled_maps.count; tiled++) {
|
|
current = map->tiled_maps.maps[tiled];
|
|
if (current->processed != 1)
|
|
continue;
|
|
|
|
count--;
|
|
|
|
if ((current->tiles[0]) && (current->tiles[0]->processed == 0)) {
|
|
current->tiles[0]->processed = 1;
|
|
current->tiles[0]->tiled_x_from = current->tiled_x_from;
|
|
current->tiles[0]->tiled_y_from = current->tiled_y_from-current->tiles[0]->height;
|
|
}
|
|
if ((current->tiles[1]) && (current->tiles[1]->processed == 0)) {
|
|
current->tiles[1]->processed = 1;
|
|
current->tiles[1]->tiled_x_from = current->tiled_x_from+current->width;
|
|
current->tiles[1]->tiled_y_from = current->tiled_y_from;
|
|
}
|
|
if ((current->tiles[2]) && (current->tiles[2]->processed == 0)) {
|
|
current->tiles[2]->processed = 1;
|
|
current->tiles[2]->tiled_x_from = current->tiled_x_from;
|
|
current->tiles[2]->tiled_y_from = current->tiled_y_from+current->height;
|
|
}
|
|
if ((current->tiles[3]) && (current->tiles[3]->processed == 0)) {
|
|
current->tiles[3]->processed = 1;
|
|
current->tiles[3]->tiled_x_from = current->tiled_x_from-current->tiles[3]->width;
|
|
current->tiles[3]->tiled_y_from = current->tiled_y_from;
|
|
}
|
|
}
|
|
|
|
if (last == count) {
|
|
printf("do_tiled_map_picture: didn't process any map in %s (%d left)??\n", map->path, last);
|
|
abort();
|
|
}
|
|
}
|
|
|
|
for (tiled = 0; tiled < map->tiled_maps.count; tiled++) {
|
|
if (map->tiled_maps.maps[tiled]->tiled_x_from < xmin)
|
|
xmin = map->tiled_maps.maps[tiled]->tiled_x_from;
|
|
if (map->tiled_maps.maps[tiled]->tiled_y_from < ymin)
|
|
ymin = map->tiled_maps.maps[tiled]->tiled_y_from;
|
|
if (map->tiled_maps.maps[tiled]->tiled_x_from+map->tiled_maps.maps[tiled]->width > xmax)
|
|
xmax = map->tiled_maps.maps[tiled]->tiled_x_from+map->tiled_maps.maps[tiled]->width;
|
|
if (map->tiled_maps.maps[tiled]->tiled_y_from+map->tiled_maps.maps[tiled]->height > ymax)
|
|
ymax = map->tiled_maps.maps[tiled]->tiled_y_from+map->tiled_maps.maps[tiled]->height;
|
|
}
|
|
|
|
large = gdImageCreateTrueColor(32*(xmax-xmin), 32*(ymax-ymin));
|
|
small = gdImageCreateTrueColor(size_small*(xmax-xmin), size_small*(ymax-ymin));
|
|
|
|
for (tiled = 0; tiled < map->tiled_maps.count; tiled++) {
|
|
snprintf(picpath, sizeof(picpath), "%s%s%s", root, map->tiled_maps.maps[tiled]->path, output_extensions[output_format]);
|
|
|
|
out = fopen(picpath, "rb");
|
|
if (!out) {
|
|
printf("\n do_tiled_map_picture: warning: pic file not found for %s (errno=%d)\n", map->tiled_maps.maps[tiled]->path, errno);
|
|
continue;
|
|
}
|
|
if (output_format == OF_PNG)
|
|
load = gdImageCreateFromPng(out);
|
|
else
|
|
load = gdImageCreateFromJpeg(out);
|
|
fclose(out);
|
|
if (!load) {
|
|
printf("\n do_tiled_map_picture: warning: pic not found for %s\n", map->tiled_maps.maps[tiled]->path);
|
|
continue;
|
|
}
|
|
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);
|
|
gdImageDestroy(load);
|
|
|
|
snprintf(picpath, sizeof(picpath), "%s%s.small%s", root, map->tiled_maps.maps[tiled]->path, output_extensions[output_format]);
|
|
out = fopen(picpath, "rb");
|
|
if (output_format == OF_PNG)
|
|
load = gdImageCreateFromPng(out);
|
|
else
|
|
load = gdImageCreateFromJpeg(out);
|
|
fclose(out);
|
|
if (!load) {
|
|
printf("\n do_tiled_map_picture: warning: small pic not found for %s\n", map->tiled_maps.maps[tiled]->path);
|
|
continue;
|
|
}
|
|
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);
|
|
gdImageDestroy(load);
|
|
}
|
|
|
|
snprintf(picpath, sizeof(picpath), "%s%s%s", root, map->path, output_extensions[output_format]);
|
|
out = fopen(picpath, "wb+");
|
|
save_picture(out, large);
|
|
fclose(out);
|
|
|
|
snprintf(picpath, sizeof(picpath), "%s%s.small%s", root, map->path, output_extensions[output_format]);
|
|
out = fopen(picpath, "wb+");
|
|
save_picture(out, small);
|
|
fclose(out);
|
|
|
|
gdImageDestroy(small);
|
|
gdImageDestroy(large);
|
|
|
|
printf(" done.\n");
|
|
}
|
|
|
|
/** Writes the page for a tiled map group. */
|
|
static void write_tiled_map_page(struct_map_info *map) {
|
|
|
|
do_tiled_map_picture(map);
|
|
|
|
/** @todo: do a real page, with the various levels, maps and such. */
|
|
|
|
write_map_page(map);
|
|
}
|
|
|
|
/** Outputs all tiled map pages. */
|
|
static void write_tiled_maps(void) {
|
|
int map;
|
|
|
|
printf("Writing tiled map information...\n");
|
|
|
|
for (map = 0; map < tiled_map_list.count; map++)
|
|
write_tiled_map_page(tiled_map_list.maps[map]);
|
|
|
|
printf(" done.\n");
|
|
}
|
|
|
|
/** Outputs the list of maps sorted by level. */
|
|
static void write_maps_by_level(void) {
|
|
int map;
|
|
FILE *out;
|
|
char name[500];
|
|
char mappath[500];
|
|
char *letters = NULL;
|
|
char *maps = NULL;
|
|
char *level = NULL;
|
|
int lastlevel = -1;
|
|
char strlevel[10];
|
|
char strcount[10];
|
|
const char *val_vars[] = { "LEVEL", "MAPS", NULL };
|
|
const char *val_values[] = { strlevel, NULL, NULL };
|
|
const char *map_vars[] = { "MAPNAME", "MAPPATH", NULL };
|
|
const char *map_values[] = { NULL, mappath, NULL };
|
|
const char *idx_vars[] = { "COUNT", "LEVELS", NULL };
|
|
const char *idx_values[] = { strcount, NULL, NULL };
|
|
int levelcount = 0;
|
|
struct_map_info *last_tiled = NULL;
|
|
struct_map_info *process;
|
|
|
|
printf("Writing map index by level...");
|
|
|
|
snprintf(name, sizeof(name), "%s/index_by_level.html", root);
|
|
|
|
qsort(maps_list.maps, maps_list.count, sizeof(struct_map_info *), sort_map_info_by_level);
|
|
|
|
for (map = 0; map < maps_list.count; map++) {
|
|
process = maps_list.maps[map];
|
|
if (maps_list.maps[map]->level != lastlevel) {
|
|
if (maps) {
|
|
snprintf(strlevel, sizeof(strlevel), "%d", lastlevel);
|
|
val_values[1] = maps;
|
|
letters = cat_template(letters, do_template(level_value_template, val_vars, val_values));
|
|
free(maps);
|
|
maps = NULL;
|
|
}
|
|
lastlevel = process->level;
|
|
levelcount++;
|
|
last_tiled = NULL;
|
|
} else
|
|
if (last_tiled && last_tiled == process->tiled_group)
|
|
/* Group maps of same tiled group and level, but make them appear in different levels if applicable. */
|
|
continue;
|
|
|
|
if (process->tiled_group) {
|
|
process = process->tiled_group;
|
|
last_tiled = process;
|
|
} else
|
|
last_tiled = process->tiled_group;
|
|
|
|
map_values[0] = process->name;
|
|
snprintf(mappath, sizeof(mappath), "%s.html", process->path+1); /* don't want the leading / */
|
|
maps = cat_template(maps, do_template(level_map_template, map_vars, map_values));
|
|
}
|
|
|
|
snprintf(strlevel, sizeof(strlevel), "%d", lastlevel);
|
|
val_values[1] = maps;
|
|
letters = cat_template(letters, do_template(level_value_template, val_vars, val_values));
|
|
free(maps);
|
|
maps = NULL;
|
|
|
|
snprintf(strcount, sizeof(strcount), "%d", levelcount);
|
|
idx_values[1] = letters;
|
|
level = do_template(level_template, idx_vars, idx_values);
|
|
free(letters);
|
|
|
|
out = fopen(name, "w+");
|
|
fprintf(out, level);
|
|
fclose(out);
|
|
free(level);
|
|
|
|
printf(" done.\n");
|
|
}
|
|
|
|
/**
|
|
* Writes the item page.
|
|
*/
|
|
static void write_equipment_index(void) {
|
|
int item, map;
|
|
FILE *out;
|
|
char name[500];
|
|
|
|
printf("Generating special equipment list..");
|
|
fflush(stdout);
|
|
|
|
qsort(special_equipment, equipment_count, sizeof(struct_equipment *), sort_equipment);
|
|
|
|
snprintf(name, sizeof(name), "%s/items.html", root);
|
|
out = fopen(name, "w+");
|
|
|
|
fprintf(out, "<html><head><title>Item list</title></head></body><h1>Special items found in maps</h1>\n");
|
|
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");
|
|
|
|
for (item = 0; item < equipment_count; item++) {
|
|
fprintf(out, "<tr><td>%s</td><td><ul>", special_equipment[item]->name);
|
|
|
|
for (map = 0; map < special_equipment[item]->origin.count; map++)
|
|
fprintf(out, "<li>%s</li>\n", special_equipment[item]->origin.maps[map]->path);
|
|
|
|
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);
|
|
}
|
|
fprintf(out, "</body></html>\n");
|
|
fclose(out);
|
|
|
|
printf(" done.\n");
|
|
}
|
|
|
|
/**
|
|
* Writes the monster information page.
|
|
*/
|
|
static void write_race_index(void) {
|
|
int item, map;
|
|
FILE *out;
|
|
char name[500];
|
|
|
|
printf("Generating monster list...");
|
|
fflush(stdout);
|
|
|
|
qsort(races.races, races.count, sizeof(struct_race *), sort_race);
|
|
|
|
snprintf(name, sizeof(name), "%s/monsters.html", root);
|
|
out = fopen(name, "w+");
|
|
|
|
fprintf(out, "<html><head><title>Monster list</title></head></body><h1>Monsters found in maps</h1>\n");
|
|
fprintf(out, "<table border=\"1\"><tr><th>Name</th><th>Count</th><th>Map(s)</th></tr>\n");
|
|
|
|
for (item = 0; item < races.count; item++) {
|
|
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);
|
|
|
|
qsort(races.races[item]->origin.maps, races.races[item]->origin.count, sizeof(struct_map_info *), sort_map_info);
|
|
|
|
for (map = 0; map < races.races[item]->origin.count; map++)
|
|
fprintf(out, "<li>%s</li>\n", races.races[item]->origin.maps[map]->path);
|
|
|
|
fprintf(out, "</ul></td></tr>\n");
|
|
}
|
|
fprintf(out, "</body></html>\n");
|
|
fclose(out);
|
|
|
|
printf(" done.\n");
|
|
}
|
|
|
|
/** Directories to ignore for map search. */
|
|
static const char *ignore_path[] = {
|
|
"/Info",
|
|
"/editor",
|
|
"/python",
|
|
"/styles",
|
|
"/templates",
|
|
"/test",
|
|
"/unlinked",
|
|
NULL };
|
|
|
|
/** File names to ignore for map search. */
|
|
static const char *ignore_name[] = {
|
|
".",
|
|
"..",
|
|
".svn",
|
|
"README",
|
|
NULL };
|
|
|
|
/**
|
|
* Recursively find all all maps in a directory.
|
|
*
|
|
* @param from
|
|
* path to search from, without trailing /.
|
|
*/
|
|
static void find_maps(const char *from) {
|
|
struct dirent *file;
|
|
struct stat statbuf;
|
|
int status, ignore;
|
|
char path[1024], full[1024];
|
|
DIR *dir;
|
|
|
|
for (ignore = 0; ignore_path[ignore] != NULL; ignore++) {
|
|
if (strcmp(from, ignore_path[ignore]) == 0)
|
|
return;
|
|
}
|
|
|
|
snprintf(path, sizeof(path), "%s/%s%s", settings.datadir, settings.mapdir, from);
|
|
dir = opendir(path);
|
|
|
|
if (dir) {
|
|
for (file = readdir(dir); file; file = readdir(dir)) {
|
|
|
|
for (ignore = 0; ignore_name[ignore] != NULL; ignore++) {
|
|
if (strcmp(file->d_name, ignore_name[ignore]) == 0)
|
|
break;
|
|
}
|
|
if (ignore_name[ignore] != NULL)
|
|
continue;
|
|
|
|
snprintf(full, sizeof(full), "%s/%s", path, file->d_name);
|
|
|
|
status = stat(full, &statbuf);
|
|
if ((status != -1) && (S_ISDIR(statbuf.st_mode))) {
|
|
snprintf(full, sizeof(full), "%s/%s", from, file->d_name);
|
|
find_maps(full);
|
|
continue;
|
|
}
|
|
if (found_maps_count == found_maps_allocated) {
|
|
found_maps_allocated += 50;
|
|
found_maps = realloc(found_maps, found_maps_allocated*sizeof(char *));
|
|
}
|
|
snprintf(full, sizeof(full), "%s/%s", from, file->d_name);
|
|
found_maps[found_maps_count++] = strdup(full);
|
|
}
|
|
closedir(dir);
|
|
}
|
|
}
|
|
|
|
/** Writes the list of unused maps, maps found in the directories but not linked from the other maps. */
|
|
static void dump_unused_maps(void) {
|
|
FILE *dump;
|
|
char path[1024];
|
|
int index, found = 0;
|
|
|
|
snprintf(path, sizeof(path), "%s/%s", root, "maps.unused");
|
|
dump = fopen(path, "w+");
|
|
if (dump == NULL) {
|
|
printf("Unable to open file maps.unused!\n");
|
|
return;
|
|
}
|
|
for (index = 0; index < found_maps_count; index++) {
|
|
if (found_maps[index] != NULL) {
|
|
fprintf(dump, "%s\n", found_maps[index]);
|
|
free(found_maps[index]);
|
|
found++;
|
|
}
|
|
}
|
|
fclose(dump);
|
|
printf("%d unused maps.\n", found);
|
|
}
|
|
|
|
/** Writes the exit information world map. */
|
|
static void write_world_info(void) {
|
|
FILE *file;
|
|
char path[MAX_BUF];
|
|
int x, y;
|
|
gdImagePtr elevationmap;
|
|
|
|
if (!world_exit_info)
|
|
return;
|
|
|
|
printf("Saving exit/blocking/road information...");
|
|
snprintf(path, sizeof(path), "%s/%s%s", root, "world_info", output_extensions[output_format]);
|
|
file = fopen(path, "wb+");
|
|
save_picture(file, infomap);
|
|
fclose(file);
|
|
printf("done.\n");
|
|
gdImageDestroy(infomap);
|
|
infomap = NULL;
|
|
|
|
if (elevation_min == 0 || elevation_max == 0) {
|
|
puts("Error: Could not save elevation world map due to not finding any minimum or maximum elevation.");
|
|
return;
|
|
}
|
|
|
|
elevationmap = gdImageCreateTrueColor(30*50, 30*50);;
|
|
|
|
for (x = 0; x < 30*50; x++) {
|
|
for (y = 0; y < 30*50; y++) {
|
|
gdImageSetPixel(elevationmap, x, y, get_elevation_color(elevation_info[x][y], elevationmap));
|
|
}
|
|
}
|
|
|
|
printf("Saving elevation world map...");
|
|
snprintf(path, sizeof(path), "%s/%s%s", root, "world_elevation", output_extensions[output_format]);
|
|
file = fopen(path, "wb+");
|
|
save_picture(file, elevationmap);
|
|
fclose(file);
|
|
printf("done.\n");
|
|
gdImageDestroy(elevationmap);
|
|
elevationmap = NULL;
|
|
}
|
|
|
|
/** Write the .dot file representing links between regions. */
|
|
static void write_regions_link(void) {
|
|
FILE *file;
|
|
char path[MAX_BUF];
|
|
int link;
|
|
|
|
if (!do_regions_link)
|
|
return;
|
|
|
|
printf("Writing regions link file...");
|
|
snprintf(path, sizeof(path), "%s/%s", root, "region_links.dot");
|
|
file = fopen(path, "wb+");
|
|
fprintf(file, "digraph {\n");
|
|
for (link = 0; link < regions_link_count; link++)
|
|
fprintf(file, regions_link[link]);
|
|
fprintf(file, "}\n");
|
|
fclose(file);
|
|
printf("done.\n");
|
|
}
|
|
|
|
/**
|
|
* Helper function to write a map to a file with its link and full path.
|
|
*
|
|
* @param file
|
|
* where to write.
|
|
* @param map
|
|
* map info to write.
|
|
*/
|
|
static void write_slaying_map_name(FILE *file, struct_map_info *map) {
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Writes all maps of the specified slaying information.
|
|
*
|
|
* @param file
|
|
* file to write to.
|
|
* @param info
|
|
* slaying information to write.
|
|
* @param item
|
|
* which of the S_xxx to write.
|
|
* @param with
|
|
* text to write when there are maps to write. Mustn't be NULL.
|
|
* @param without
|
|
* text to write when there are no maps. Can be NULL.
|
|
*/
|
|
static void write_one_slaying_info(FILE *file, struct_slaying_info *info, int item, const char *with, const char *without) {
|
|
int map;
|
|
|
|
if (info->maps[item].count == 0) {
|
|
if (without)
|
|
fprintf(file, without);
|
|
return;
|
|
}
|
|
|
|
qsort(info->maps[item].maps, info->maps[item].count, sizeof(const char *), sort_mapname);
|
|
|
|
fprintf(file, with);
|
|
fprintf(file, "<ul>\n");
|
|
for (map = 0; map < info->maps[item].count; map++) {
|
|
fprintf(file, "\t<li>");
|
|
write_slaying_map_name(file, info->maps[item].maps[map]);
|
|
fprintf(file, "</li>\n");
|
|
}
|
|
fprintf(file, "</ul>\n");
|
|
}
|
|
|
|
/**
|
|
* Helper function to sort an array of struct_slaying_info.
|
|
*
|
|
* @param left
|
|
* first item.
|
|
* @param right
|
|
* second item.
|
|
* @return
|
|
* sort order.
|
|
*/
|
|
static int sort_slaying(const void *left, const void *right) {
|
|
struct_slaying_info *l = *(struct_slaying_info **)left;
|
|
struct_slaying_info *r = *(struct_slaying_info **)right;
|
|
|
|
return strcasecmp(l->slaying, r->slaying);
|
|
}
|
|
|
|
/**
|
|
* Writes all slaying info to file.
|
|
*/
|
|
static void write_slaying_info(void) {
|
|
FILE *file;
|
|
char path[MAX_BUF];
|
|
int lock;
|
|
struct_slaying_info *info;
|
|
|
|
printf("Writing slaying info file...");
|
|
|
|
qsort(slaying_info, slaying_count, sizeof(struct_slaying_info *), sort_slaying);
|
|
|
|
snprintf(path, sizeof(path), "%s/%s", root, "slaying_info.html");
|
|
file = fopen(path, "wb+");
|
|
|
|
fprintf(file, "<html>\n<head>\n<title>Slaying information</title>\n</head>\n<body>\n");
|
|
fprintf(file, "<p>This is a list of various slaying fields on keys, containers, doors, detectors.</p>");
|
|
|
|
for (lock = 0; lock < slaying_count; lock++) {
|
|
info = slaying_info[lock];
|
|
fprintf(file, "<h1>%s</h1>\n", info->slaying);
|
|
|
|
if (info->maps[S_DOOR].count == 0 && info->maps[S_CONTAINER].count == 0 && info->maps[S_CONNECT].count == 0) {
|
|
fprintf(file, "No door, container or detector matching this slaying.<br />\n");
|
|
} else {
|
|
write_one_slaying_info(file, info, S_DOOR, "Connected doors:\n", NULL);
|
|
write_one_slaying_info(file, info, S_CONTAINER, "Matching containers:\n", NULL);
|
|
write_one_slaying_info(file, info, S_CONNECT, "Detectors and such:\n", NULL);
|
|
}
|
|
write_one_slaying_info(file, info, S_KEY, "Matching keys:\n", "No key with this slaying.<br />\n");
|
|
}
|
|
|
|
fprintf(file, "</body>\n</html>\n");
|
|
|
|
fclose(file);
|
|
printf("done.\n");
|
|
}
|
|
|
|
/**
|
|
* Write the list of all found NPCs in maps.
|
|
*/
|
|
static void write_npc_list(void) {
|
|
FILE *file;
|
|
char path[MAX_BUF];
|
|
int map, npc;
|
|
|
|
printf("Writing NPC info file...");
|
|
|
|
qsort(slaying_info, slaying_count, sizeof(struct_slaying_info *), sort_slaying);
|
|
|
|
snprintf(path, sizeof(path), "%s/%s", root, "npc_info.html");
|
|
file = fopen(path, "wb+");
|
|
|
|
fprintf(file, "<html>\n<head>\n<title>NPCs who have a special message</title>\n</head>\n<body>\n");
|
|
fprintf(file, "<p>This is a list of NPCs having a special message.</p>");
|
|
fprintf(file, "<ul>\n");
|
|
|
|
for (map = 0; map < maps_list.count; map++) {
|
|
if (maps_list.maps[map]->npcs.count == 0)
|
|
continue;
|
|
fprintf(file, "<li>%s</li>\n<ul>", maps_list.maps[map]->path);
|
|
for (npc = 0; npc < maps_list.maps[map]->npcs.count; npc++) {
|
|
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);
|
|
}
|
|
fprintf(file, "</ul>\n</li>\n");
|
|
}
|
|
|
|
fprintf(file, "</ul>\n");
|
|
fprintf(file, "</body>\n</html>\n");
|
|
|
|
fclose(file);
|
|
printf("done.\n");
|
|
}
|
|
|
|
/**
|
|
* Prints usage information, and exit.
|
|
*
|
|
* @param program
|
|
* program path.
|
|
*/
|
|
static void do_help(const char *program) {
|
|
printf("Crossfire Mapper will generate pictures of maps, and create indexes for all maps and regions.\n\n");
|
|
printf("Syntax: %s\n\n", program);
|
|
printf("Optional arguments:\n");
|
|
printf(" -nopics don't generate pictures.\n");
|
|
printf(" -noindex don't generate global map index.\n");
|
|
printf(" -root=<path> destination path. Default 'html'.\n");
|
|
printf(" -limit=<number> stop processing after this number of maps, -1 to do all maps (default).\n");
|
|
printf(" -showmaps outputs the name of maps as they are processed.\n");
|
|
printf(" -jpg[=quality] generate jpg pictures, instead of default png. Quality should be 0-95, -1 for automatic.\n");
|
|
printf(" -forcepics force to regenerate pics, even if pics's date is after map's.\n");
|
|
printf(" -addmap=<map> adds a map to process. Path is relative to map's directory root.\n");
|
|
printf(" -rawmaps generates maps pics without items on random (shop, treasure) tiles.\n");
|
|
printf(" -warnnopath inform when an exit has no path set.\n");
|
|
printf(" -listunusedmaps finds all unused maps in the maps directory.\n");
|
|
printf(" -noworldmap don't write the world map in world.png.\n");
|
|
printf(" -noregionslink don't generate regions relation file.\n");
|
|
printf(" -regionslink generate regions relation file.\n");
|
|
printf(" -noexitmap don't generate map of exits.\n");
|
|
printf(" -exitmap generate map of exits.\n");
|
|
printf(" -tileset=<number> use specified tileset to generate the pictures. Default 0 (standard).\n");
|
|
printf("\n\n");
|
|
exit(0);
|
|
}
|
|
|
|
/**
|
|
* Handles command-line parameters.
|
|
*
|
|
* @param argc
|
|
* number of parameters, including program name.
|
|
* @param argv
|
|
* arguments, including program name.
|
|
*/
|
|
static void do_parameters(int argc, char **argv) {
|
|
int arg = 1;
|
|
char path[500];
|
|
|
|
root[0] = '\0';
|
|
|
|
while (arg < argc) {
|
|
if (strcmp(argv[arg], "-nopics") == 0)
|
|
generate_pics = 0;
|
|
else if (strcmp(argv[arg], "-noindex") == 0)
|
|
generate_index = 0;
|
|
else if (strncmp(argv[arg], "-root=", 6) == 0)
|
|
strncpy(root, argv[arg]+6, 500);
|
|
else if (strncmp(argv[arg], "-limit=", 7) == 0)
|
|
map_limit = atoi(argv[arg]+7);
|
|
else if (strcmp(argv[arg], "-showmaps") == 0)
|
|
show_maps = 1;
|
|
else if (strcmp(argv[arg], "-jpg") == 0) {
|
|
output_format = OF_JPG;
|
|
if (argv[arg][4] == '=') {
|
|
jpeg_quality = atoi(argv[arg]+5);
|
|
if (jpeg_quality < 0)
|
|
jpeg_quality = -1;
|
|
}
|
|
}
|
|
else if (strcmp(argv[arg], "-forcepics") == 0)
|
|
force_pics = 1;
|
|
else if (strncmp(argv[arg], "-addmap=", 8) == 0) {
|
|
if (*(argv[arg]+8) == '/')
|
|
strncpy(path, argv[arg]+8, 500);
|
|
else
|
|
snprintf(path, 500, "/%s", argv[arg]+8);
|
|
add_map(get_map_info(path), &maps_list);
|
|
}
|
|
else if (strcmp(argv[arg], "-rawmaps") == 0)
|
|
rawmaps = 1;
|
|
else if (strcmp(argv[arg], "-warnnopath") == 0)
|
|
warn_no_path = 1;
|
|
else if (strcmp(argv[arg], "-listunusedmaps") == 0)
|
|
list_unused_maps = 1;
|
|
else if (strcmp(argv[arg], "-noworldmap") == 0)
|
|
world_map = 0;
|
|
else if (strcmp(argv[arg], "-noregionslink") == 0)
|
|
do_regions_link = 0;
|
|
else if (strcmp(argv[arg], "-regionslink") == 0)
|
|
do_regions_link = 1;
|
|
else if (strcmp(argv[arg], "-noexitmap") == 0)
|
|
world_exit_info = 0;
|
|
else if (strcmp(argv[arg], "-exitmap") == 0)
|
|
world_exit_info = 1;
|
|
else if (strncmp(argv[arg], "-tileset=", 9) == 0) {
|
|
tileset = atoi(argv[arg]+9);
|
|
/* check of validity is done in main() as we need to actually have the sets loaded. */
|
|
} else
|
|
do_help(argv[0]);
|
|
arg++;
|
|
}
|
|
if (!strlen(root))
|
|
strcpy(root, "html");
|
|
if (root[strlen(root)-1] == '/')
|
|
root[strlen(root)-1] = '\0';
|
|
if (map_limit < -1)
|
|
map_limit = -1;
|
|
}
|
|
|
|
/**
|
|
* Ensures destination directory exists.
|
|
*/
|
|
static void create_destination(void) {
|
|
char dummy[502];
|
|
|
|
strcpy(dummy, root);
|
|
strcat(dummy, "/a");
|
|
make_path_to_file(dummy);
|
|
}
|
|
|
|
/**
|
|
* Helper to write yes/no.
|
|
*
|
|
* @param value
|
|
* value to print.
|
|
* @return
|
|
* "no" if value == 0, "yes" else.
|
|
*/
|
|
static const char *yesno(int value) {
|
|
return (value ? "yes" : "no");
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
int current_map = 0, i;
|
|
char max[50];
|
|
region *dummy;
|
|
|
|
init_map_list(&maps_list);
|
|
init_map_list(&tiled_map_list);
|
|
init_race_list(&races);
|
|
pics_allocated = 0;
|
|
|
|
do_parameters(argc, argv);
|
|
|
|
printf("Initializing Crossfire data...\n");
|
|
|
|
settings.debug = 0;
|
|
|
|
init_globals();
|
|
init_library();
|
|
init_archetypes();
|
|
init_artifacts();
|
|
init_formulae();
|
|
init_readable();
|
|
init_regions();
|
|
|
|
init_gods();
|
|
read_client_images();
|
|
|
|
/* Add a dummy region so unlinked maps can be identified. */
|
|
dummy = get_region_struct();
|
|
dummy->fallback = 1;
|
|
dummy->name = add_string("unlinked");
|
|
dummy->longname = add_string("This dummy region contains all maps without a region set.");
|
|
dummy->longname = add_string("This dummy region contains all maps without a region set.");
|
|
dummy->next = first_region;
|
|
first_region = dummy;
|
|
|
|
printf("\n\n done.\n\n");
|
|
|
|
if (!is_valid_faceset(tileset)) {
|
|
printf("Erreor: invalid tileset %d!\n", tileset);
|
|
exit(1);
|
|
}
|
|
|
|
create_destination();
|
|
gdfaces = calloc(1, sizeof(gdImagePtr)*nrofpixmaps);
|
|
|
|
read_template("templates/map.template", &map_template);
|
|
read_template("templates/map_no_exit.template", &map_no_exit_template);
|
|
read_template("templates/map_with_exit.template", &map_with_exit_template);
|
|
read_template("templates/map_exit.template", &map_exit_template);
|
|
read_template("templates/map_no_exit_to.template", &map_no_exit_to_template);
|
|
read_template("templates/map_with_exit_to.template", &map_with_exit_to_template);
|
|
read_template("templates/map_exit_to.template", &map_exit_to_template);
|
|
read_template("templates/map_lore.template", &map_lore_template);
|
|
read_template("templates/map_no_lore.template", &map_no_lore_template);
|
|
read_template("templates/map_no_monster.template", &map_no_monster_template);
|
|
read_template("templates/map_monster_before.template", &map_monster_before_template);
|
|
read_template("templates/map_monster_between.template", &map_monster_between_template);
|
|
read_template("templates/map_monster_one.template", &map_monster_one_template);
|
|
read_template("templates/map_monster_after.template", &map_monster_after_template);
|
|
|
|
read_template("templates/index.template", &index_template);
|
|
read_template("templates/index_letter.template", &index_letter);
|
|
read_template("templates/index_map.template", &index_map);
|
|
|
|
read_template("templates/region.template", ®ion_template);
|
|
read_template("templates/region_letter.template", ®ion_letter_template);
|
|
read_template("templates/region_map.template", ®ion_map_template);
|
|
|
|
read_template("templates/index_region.template", &index_region_template);
|
|
read_template("templates/index_region_region.template", &index_region_region_template);
|
|
|
|
read_template("templates/world.template", &world_template);
|
|
read_template("templates/world_row.template", &world_row_template);
|
|
read_template("templates/world_map.template", &world_map_template);
|
|
|
|
read_template("templates/level.template", &level_template);
|
|
read_template("templates/level_value.template", &level_value_template);
|
|
read_template("templates/level_map.template", &level_map_template);
|
|
|
|
read_template("templates/quests.template", &index_quest_template);
|
|
read_template("templates/quests_quest.template", &quest_template);
|
|
read_template("templates/quests_map.template", &quest_map_template);
|
|
|
|
read_template("templates/map_with_quests.template", &map_with_quests_template);
|
|
read_template("templates/map_one_quest.template", &map_one_quest_template);
|
|
read_template("templates/map_no_quest.template", &map_no_quest_template);
|
|
|
|
if (map_limit != -1)
|
|
snprintf(max, sizeof(max), "%d", map_limit);
|
|
else
|
|
strcpy(max, "(none)");
|
|
printf("Crossfire map browser generator\n");
|
|
printf("-------------------------------\n\n");
|
|
printf("Parameters:\n");
|
|
printf(" path to write files: %s\n", root);
|
|
printf(" maximum number of maps to process: %s\n", max);
|
|
printf(" will generate map picture: %s\n", yesno(generate_pics));
|
|
printf(" will always generate map picture: %s\n", yesno(force_pics));
|
|
printf(" picture output format: %s\n", output_extensions[output_format]);
|
|
if (output_format == OF_JPG)
|
|
printf(" JPEG quality: %d\n", jpeg_quality);
|
|
printf(" will generate map index: %s\n", yesno(generate_index));
|
|
printf(" show map being processed: %s\n", yesno(show_maps));
|
|
printf(" generate raw maps: %s\n", yesno(rawmaps));
|
|
printf(" warn of exit without path: %s\n", yesno(warn_no_path));
|
|
printf(" list unused maps: %s\n", yesno(list_unused_maps));
|
|
printf(" generate world map: %s\n", yesno(world_map));
|
|
printf(" generate exit map: %s\n", yesno(world_exit_info));
|
|
printf(" generate regions link file: %s\n", yesno(do_regions_link));
|
|
printf(" tileset: %s\n", facesets[tileset].fullname);
|
|
printf("\n");
|
|
|
|
if (list_unused_maps) {
|
|
printf("listing all maps...");
|
|
find_maps("");
|
|
printf("done, %d maps found.\n", found_maps_count);
|
|
qsort(found_maps, found_maps_count, sizeof(char *), sortbyname);
|
|
}
|
|
|
|
/* exit/blocking information. */
|
|
infomap = gdImageCreateTrueColor(30*50, 30*50);
|
|
color_unlinked_exit = gdImageColorResolve(infomap, 255, 0, 0);
|
|
color_linked_exit = gdImageColorResolve(infomap, 255, 255, 255);
|
|
color_road = gdImageColorResolve(infomap, 0, 255, 0);
|
|
color_blocking = gdImageColorResolve(infomap, 0, 0, 255);
|
|
color_slowing = gdImageColorResolve(infomap, 0, 0, 127);
|
|
elevation_info = calloc(50*30, sizeof(int *));
|
|
for (i = 0; i < 50*30; i++)
|
|
elevation_info[i] = calloc(50*30, sizeof(int));
|
|
elevation_min = 0;
|
|
elevation_max = 0;
|
|
|
|
printf("browsing maps...\n");
|
|
|
|
get_map_info(first_map_path);
|
|
|
|
while (current_map < maps_list.count) {
|
|
process_map(maps_list.maps[current_map++]);
|
|
if (current_map%100 == 0) {
|
|
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);
|
|
}
|
|
if ((map_limit != -1) && (current_map == map_limit)) {
|
|
printf(" --- map limit reached, stopping ---\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
if (list_unused_maps)
|
|
dump_unused_maps();
|
|
|
|
fix_exits_to_tiled_maps();
|
|
fix_map_names();
|
|
fix_tiled_map();
|
|
fix_tiled_map_monsters();
|
|
|
|
write_all_maps();
|
|
write_maps_index();
|
|
write_maps_by_level();
|
|
write_tiled_maps();
|
|
|
|
write_all_regions();
|
|
write_region_index();
|
|
|
|
write_world_map();
|
|
write_world_info();
|
|
|
|
write_regions_link();
|
|
write_slaying_info();
|
|
|
|
write_quests_page();
|
|
|
|
write_equipment_index();
|
|
write_race_index();
|
|
write_npc_list();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void do_auto_apply(mapstruct *m) {
|
|
object *tmp, *above = NULL;
|
|
int x, y;
|
|
|
|
if (m == NULL)
|
|
return;
|
|
|
|
for (x = 0; x < MAP_WIDTH(m); x++)
|
|
for (y = 0; y < MAP_HEIGHT(m); y++)
|
|
for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = above) {
|
|
above = tmp->above;
|
|
|
|
if (tmp->inv) {
|
|
object *invtmp, *invnext;
|
|
|
|
for (invtmp = tmp->inv; invtmp != NULL; invtmp = invnext) {
|
|
invnext = invtmp->below;
|
|
|
|
if (QUERY_FLAG(invtmp, FLAG_AUTO_APPLY))
|
|
auto_apply(invtmp);
|
|
else if (invtmp->type == TREASURE && HAS_RANDOM_ITEMS(invtmp)) {
|
|
while ((invtmp->stats.hp--) > 0)
|
|
create_treasure(invtmp->randomitems, invtmp, 0, m->difficulty, 0);
|
|
invtmp->randomitems = NULL;
|
|
} else if (invtmp
|
|
&& invtmp->arch
|
|
&& invtmp->type != TREASURE
|
|
&& invtmp->type != SPELL
|
|
&& invtmp->type != CLASS
|
|
&& HAS_RANDOM_ITEMS(invtmp)) {
|
|
create_treasure(invtmp->randomitems, invtmp, 0, m->difficulty, 0);
|
|
/* Need to clear this so that we never try to create
|
|
* treasure again for this object
|
|
*/
|
|
invtmp->randomitems = NULL;
|
|
}
|
|
}
|
|
/* This is really temporary - the code at the bottom will
|
|
* also set randomitems to null. The problem is there are bunches
|
|
* of maps/players already out there with items that have spells
|
|
* which haven't had the randomitems set to null yet.
|
|
* MSW 2004-05-13
|
|
*
|
|
* And if it's a spellbook, it's better to set randomitems to NULL too,
|
|
* else you get two spells in the book ^_-
|
|
* Ryo 2004-08-16
|
|
*/
|
|
if (tmp->type == WAND
|
|
|| tmp->type == ROD
|
|
|| tmp->type == SCROLL
|
|
|| tmp->type == HORN
|
|
|| tmp->type == FIREWALL
|
|
|| tmp->type == POTION
|
|
|| tmp->type == ALTAR
|
|
|| tmp->type == SPELLBOOK)
|
|
tmp->randomitems = NULL;
|
|
}
|
|
|
|
if (QUERY_FLAG(tmp, FLAG_AUTO_APPLY))
|
|
auto_apply(tmp);
|
|
else if ((tmp->type == TREASURE || (tmp->type == CONTAINER)) && HAS_RANDOM_ITEMS(tmp)) {
|
|
while ((tmp->stats.hp--) > 0)
|
|
create_treasure(tmp->randomitems, tmp, 0, m->difficulty, 0);
|
|
tmp->randomitems = NULL;
|
|
} else if (tmp->type == TIMED_GATE) {
|
|
object *head = tmp->head != NULL ? tmp->head : tmp;
|
|
|
|
if (QUERY_FLAG(head, FLAG_IS_LINKED)) {
|
|
tmp->speed = 0;
|
|
update_ob_speed(tmp);
|
|
}
|
|
/* This function can be called everytime a map is loaded, even when
|
|
* swapping back in. As such, we don't want to create the treasure
|
|
* over and ove again, so after we generate the treasure, blank out
|
|
* randomitems so if it is swapped in again, it won't make anything.
|
|
* This is a problem for the above objects, because they have counters
|
|
* which say how many times to make the treasure.
|
|
*/
|
|
} else if (tmp
|
|
&& tmp->arch
|
|
&& tmp->type != PLAYER
|
|
&& tmp->type != TREASURE
|
|
&& tmp->type != SPELL
|
|
&& tmp->type != PLAYER_CHANGER
|
|
&& tmp->type != CLASS
|
|
&& HAS_RANDOM_ITEMS(tmp)) {
|
|
create_treasure(tmp->randomitems, tmp, GT_APPLY, m->difficulty, 0);
|
|
tmp->randomitems = NULL;
|
|
}
|
|
}
|
|
|
|
for (x = 0; x < MAP_WIDTH(m); x++)
|
|
for (y = 0; y < MAP_HEIGHT(m); y++)
|
|
for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above)
|
|
if (tmp->above
|
|
&& (tmp->type == TRIGGER_BUTTON || tmp->type == TRIGGER_PEDESTAL))
|
|
check_trigger(tmp, tmp->above);
|
|
}
|
|
|
|
#ifndef DOXYGEN_SHOULD_SKIP_THIS
|
|
|
|
/**
|
|
* Dummy functions to link the library.
|
|
*/
|
|
|
|
void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *txt, const char *txt2) {
|
|
fprintf(logfile, "%s\n", txt);
|
|
}
|
|
|
|
void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format, ...) {
|
|
va_list ap;
|
|
|
|
va_start(ap, old_format);
|
|
vfprintf(logfile, old_format, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void ext_info_map(int color, const mapstruct *map, uint8 type, uint8 subtype, const char *str1, const char *str2) {
|
|
fprintf(logfile, "ext_info_map: %s\n", str2);
|
|
}
|
|
|
|
void move_firewall(object *ob) {
|
|
}
|
|
|
|
void emergency_save(int x) {
|
|
}
|
|
|
|
void clean_tmp_files(void) {
|
|
}
|
|
|
|
void esrv_send_item(object *ob, object *obx) {
|
|
}
|
|
|
|
void dragon_ability_gain(object *ob, int x, int y) {
|
|
}
|
|
|
|
void set_darkness_map(mapstruct *m) {
|
|
}
|
|
|
|
object *find_skill_by_number(object *who, int skillno) {
|
|
return NULL;
|
|
}
|
|
|
|
void esrv_del_item(player *pl, int tag) {
|
|
}
|
|
|
|
void esrv_update_item(int flags, object *pl, object *op) {
|
|
}
|
|
|
|
void esrv_update_spells(player *pl) {
|
|
}
|
|
|
|
void monster_check_apply(object *ob, object *obt) {
|
|
}
|
|
|
|
void trap_adjust(object *ob, int x) {
|
|
}
|
|
|
|
int execute_event(object *op, int eventcode, object *activator, object *third, const char *message, int fix) {
|
|
return 0;
|
|
}
|
|
|
|
int execute_global_event(int eventcode, ...) {
|
|
return 0;
|
|
}
|
|
|
|
int auto_apply(object *op) {
|
|
object *tmp = NULL, *tmp2;
|
|
int i;
|
|
|
|
switch (op->type) {
|
|
case SHOP_FLOOR:
|
|
if (!HAS_RANDOM_ITEMS(op))
|
|
return 0;
|
|
do {
|
|
i = 10; /* let's give it 10 tries */
|
|
while ((tmp = generate_treasure(op->randomitems, op->stats.exp ? (int)op->stats.exp : MAX(op->map->difficulty, 5))) == NULL && --i)
|
|
;
|
|
if (tmp == NULL)
|
|
return 0;
|
|
if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) {
|
|
free_object(tmp);
|
|
tmp = NULL;
|
|
}
|
|
} while (!tmp);
|
|
tmp->x = op->x;
|
|
tmp->y = op->y;
|
|
SET_FLAG(tmp, FLAG_UNPAID);
|
|
insert_ob_in_map(tmp, op->map, NULL, 0);
|
|
CLEAR_FLAG(op, FLAG_AUTO_APPLY);
|
|
identify(tmp);
|
|
break;
|
|
|
|
case TREASURE:
|
|
if (QUERY_FLAG(op, FLAG_IS_A_TEMPLATE))
|
|
return 0;
|
|
|
|
while ((op->stats.hp--) > 0)
|
|
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);
|
|
|
|
/* If we generated an object and put it in this object inventory,
|
|
* move it to the parent object as the current object is about
|
|
* to disappear. An example of this item is the random_ *stuff
|
|
* that is put inside other objects.
|
|
*/
|
|
for (tmp = op->inv; tmp; tmp = tmp2) {
|
|
tmp2 = tmp->below;
|
|
remove_ob(tmp);
|
|
if (op->env)
|
|
insert_ob_in_ob(tmp, op->env);
|
|
else
|
|
free_object(tmp);
|
|
}
|
|
remove_ob(op);
|
|
free_object(op);
|
|
break;
|
|
}
|
|
return tmp ? 1 : 0;
|
|
}
|
|
|
|
void fix_auto_apply(mapstruct *m) {
|
|
}
|
|
|
|
#endif /* dummy DOXYGEN_SHOULD_SKIP_THIS */
|