Mercurial > hg > batmud > maputils
view src/mkcitymap.c @ 2833:d0e186348cb2 default tip
Add mention of soft level limitation to 'Eightleg woods'.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 26 May 2024 20:33:53 +0300 |
parents | 7677cfceded0 |
children |
line wrap: on
line source
/* * Create interactive XHTML+CSS+JS map from input * Programmed by Matti 'ccr' Hämäläinen <ccr@tnsp.org> * (C) Copyright 2006-2024 Tecnic Software productions (TNSP) */ #include "libmaputils.h" #include "liblocfile.h" #include "th_args.h" #include "th_string.h" char *optDestFilename = NULL, *optMapFilename = NULL, *optLocFilename = NULL, *optHeadFilename = NULL, *optMapTitle = NULL; bool optUseOldFormat = false; /* Arguments */ static const th_optarg optList[] = { { 0, '?', "help", "Show this help", OPT_NONE }, { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, { 2, 'o', "output", "Output filename", OPT_ARGREQ }, { 3, 't', "title", "Map title", OPT_ARGREQ }, { 4, 'h', "header-file", "Specify extra <head> data in file", OPT_ARGREQ }, { 5, 'O', "old-format", "Input using old symbols/colors", OPT_NONE }, }; const int optListN = sizeof(optList) / sizeof(optList[0]); void argShowHelp(void) { th_print_banner(stdout, th_prog_name, "[options] <mapfile> <locfile>"); th_args_help(stdout, optList, optListN, 0, 80 - 2); } bool argHandleOpt(const int optN, char *optArg, char *currArg) { switch (optN) { case 0: argShowHelp(); exit(0); break; case 1: th_verbosity++; break; case 2: optDestFilename = optArg; THMSG(2, "Output file set to '%s'.\n", optDestFilename); break; case 3: optMapTitle = optArg; THMSG(2, "Map title string set to '%s'.\n", optMapTitle); break; case 4: optHeadFilename = optArg; THMSG(2, "Head filename set to '%s'.\n", optHeadFilename); break; case 5: optUseOldFormat = true; THMSG(2, "Input is using old map symbols/colors.\n"); break; default: THERR("Unknown option '%s'.\n", currArg); return false; } return true; } bool argHandleFile(char *currArg) { if (!optMapFilename) optMapFilename = currArg; else if (!optLocFilename) optLocFilename = currArg; else { THERR("Too many filenames specified!\n"); return false; } return true; } void outputHTMLHeader(FILE *outFile, const MapLocations *locs) { (void) locs; muPrintHTMLhead(outFile, optMapTitle, true); if (optHeadFilename != NULL && muCopyFileToStream(outFile, optHeadFilename) < 0) { THERR("Error copying urchin file '%s' to output.\n", optHeadFilename); } fprintf(outFile, " <style type=\"text/css\">\n"); muPrintHTMLcolors(outFile, "span", NULL, NULL); fprintf(outFile, " </style>\n"); fprintf(outFile, "</head>\n" "<body onload=\"mapOnLoad();\">\n" ); if (optMapTitle) { fprintf(outFile, "<h1>"); fputse(optMapTitle, outFile); fprintf(outFile, "</h1>\n"); } fprintf(outFile, "<div id=\"smap\" class=\"%s\">" "<pre class=\"map\">", optUseOldFormat ? "old" : "new" ); } const char *getCityLocationType(const LocMarker *marker) { switch (marker->flags & LOCF_M_MASK) { // case LOCF_M_CITY: return "special"; case LOCF_M_PCITY: return "gov"; default: switch (marker->flags & LOCF_T_MASK) { case LOCF_T_SHRINE: return "shop"; case LOCF_T_GUILD: return "guild"; /* case LOCF_T_SS: return "ss"; case LOCF_T_MONSTER: return "monster"; case LOCF_T_TRAINER: return "trainer"; case LOCF_T_FORT: return "fort"; */ } break; } return "default"; } static char *getExtraNameData(const LocMarker *marker) { return th_strdup_printf( "%s", (marker->flags & LOCF_CLOSED) ? " [CLOSED]" : "" ); } static int compareLocation(const void *pp1, const void *pp2) { const LocMarker *vp1 = *(const LocMarker **) pp1, *vp2 = *(const LocMarker **) pp2; if (vp1->flags == vp2->flags) return th_strcasecmp(vp1->names[0].name, vp2->names[0].name); else return (vp1->flags & LOCF_MASK) - (vp2->flags & LOCF_MASK); } void outputHTMLFooter(FILE *outFile, MapLocations *locs) { char *tmps; fprintf(outFile, "</pre>" "</div>\n" "<div id=\"loclist\">\n" ); qsort(locs->locations, locs->nlocations, sizeof(LocMarker *), compareLocation); for (int n = 0; n < locs->nlocations; n++) { const LocMarker *marker = locs->locations[n]; if (marker->flags & LOCF_INVIS) continue; if ((tmps = getExtraNameData(marker)) == NULL) return; fprintf(outFile, "<a class=\"item %s%s lt%s lt%d\" id=\"listloc%d_%d\" href=\"#%d_%d\" " "data-id=\"%d_%d\" data-info=\"%s\" data-tt-title=\"%s%s\" ", "ltprimary", marker->nnames > 1 ? " ltsubitems" : "", getCityLocationType(marker), marker->align, marker->xc, marker->yc, marker->xc, marker->yc, marker->xc, marker->yc, marker->freeform ? "true" : "false", marker->names[0].name, tmps); if (marker->nnames > 1) { fprintf(outFile, "data-tt-names=\""); for (int i = 1; i < marker->nnames; i++) { fputse(marker->names[i].name, outFile); fprintf(outFile, "%s", i + 1 < marker->nnames ? " ; " : ""); } fprintf(outFile, "\" "); } if (marker->freeform) { fprintf(outFile, "data-tt-freeform=\""); fputse(marker->freeform, outFile); fprintf(outFile, "\""); } fprintf(outFile, ">"); fputse(marker->names[0].name, outFile); fputse(tmps, outFile); fprintf(outFile, "</a>\n"); for (int i = 1; i < marker->nnames; i++) { fprintf(outFile, " <a class=\"item %s lt%s lt%d\" href=\"#%d_%d\" " "data-id=\"%d_%d\">", "ltsecondary", getCityLocationType(marker), marker->align, marker->xc, marker->yc, marker->xc, marker->yc); fputse(marker->names[i].name, outFile); fprintf(outFile, "</a>\n"); } th_free(tmps); } fprintf(outFile, "</div>\n" "</body>\n" "</html>\n" ); } void outputMapHTML(FILE *outFile, const MapBlock *map, const MapLocations *locs) { int n, p = -1; bool span = false; for (int yc = 0; yc < map->height; yc++) { const unsigned char *dp = map->data + (map->scansize * yc); for (int xc = 0; xc < map->width; xc++) { if ((n = locFindByCoords(locs, xc, yc, true)) >= 0) { LocMarker *marker = locs->locations[n]; unsigned char chr; int color; if (span) fprintf(outFile, "</span>"); if (marker->uri != NULL) { // If URI/URL is set, parse piece color from it char *endptr = NULL; color = strtol(marker->uri, &endptr, 10); if (color < 0 || color >= nmapColors) color = 0; // If there is extra character after color number, // use it as the map patch if (endptr != NULL && *endptr != 0) chr = *endptr; else chr = *dp; } else { // If old format, use map piece color if (optUseOldFormat) color = muGetMapPieceColor(*dp, optUseOldFormat, true); else // Otherwise use "link" color color = col_light_red; chr = *dp; } if (marker->flags & LOCF_INVIS) { fprintf(outFile, "<span class=\"%c\">%c</span>", 'a' + color, chr); } else { fprintf(outFile, "<span class=\"%c\">" "<a class=\"loc\" id=\"maploc%d_%d\" data-id=\"%d_%d\" " "href=\"#%d_%d\">%c</a></span>", 'a' + color, marker->xc, marker->yc, marker->xc, marker->yc, marker->xc, marker->yc, chr); } span = false; p = -1; } else { if (p != *dp) { int color = muGetMapPieceColor(*dp, optUseOldFormat, true); if (span) fprintf(outFile, "</span>"); fprintf(outFile, "<span class=\"%c\">", 'a' + color); span = true; } fputc(*dp, outFile); p = *dp; } dp++; } if (span) fprintf(outFile, "</span>\n"); p = -1; span = false; } } int main(int argc, char *argv[]) { int res = 0; MapBlock *map = NULL; FILE *outFile = NULL, *inFile = NULL; LocFileInfo info; MapLocations locations; memset(&info, 0, sizeof(info)); memset(&locations, 0, sizeof(locations)); th_init("mkcitymap", "ASCII citymap converter", "0.5", NULL, NULL); th_verbosity = 0; // Parse arguments if (!th_args_process(argc, argv, optList, optListN, argHandleOpt, argHandleFile, OPTH_BAILOUT)) { res = 1; goto out; } if (optMapFilename == NULL || optLocFilename == NULL) { argShowHelp(); THERR("You need to specify at least map and loc filenames.\n"); res = 1; goto out; } // Parse location file locSetFileInfo(&info, optLocFilename, NULL); if ((inFile = fopen(info.filename, "rb")) == NULL) { THERR("Could not open location file '%s' for reading.\n", info.filename); res = -3; goto out; } if (!locParseLocStream(inFile, &info, &locations, info.xoffs, info.yoffs)) { res = -4; goto out; } // Open mapfile if ((map = mapBlockParseFile(optMapFilename, false)) == NULL) { THERR("Error parsing/opening mapfile '%s'.\n", optMapFilename); res = -5; goto out; } if (optDestFilename == NULL) outFile = stdout; else if ((outFile = fopen(optDestFilename, "wb")) == NULL) { THERR("Error opening output file '%s'!\n", optDestFilename); res = -6; goto out; } // Output data outputHTMLHeader(outFile, &locations); outputMapHTML(outFile, map, &locations); outputHTMLFooter(outFile, &locations); out: // Close input and output files if (outFile != NULL && outFile != stdout) fclose(outFile); if (inFile != NULL) fclose(inFile); mapBlockFree(map); locFreeMapLocations(&locations); locFreeFileInfo(&info); return res; }