Mercurial > hg > batmud > maputils
view www/common.inc.php @ 2763:78ad0e51b7b5
Improve wizards.txt parser, add functionality for specifying alternative /
additional names as some wizards have used more than one.
Also other improvements in wizard data handling.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 12 Mar 2024 15:47:58 +0200 |
parents | 06ce4399639c |
children |
line wrap: on
line source
<?php // Globals and definitions $errorSet = FALSE; $errorMsgs = []; $statusSet = 0; $statusMsg = ""; // Location flags (translated to PHP from liblocfile.h) define("LOCF_NONE" , 0x000000); // Marker types define("LOCF_M_SCENIC1" , 0x000001); // '?' Scenic marker define("LOCF_M_SCENIC2" , 0x000002); // '%' Shrine marker/etc define("LOCF_M_PCITY" , 0x000004); // 'C' Player city define("LOCF_M_CITY" , 0x000008); // 'c' City define("LOCF_M_MASK" , 0x00000f); // Location types define("LOCF_T_SHRINE" , 0x000010); // 'S' Raceshrine define("LOCF_T_GUILD" , 0x000020); // 'G' Guild define("LOCF_T_SS" , 0x000040); // 'P' Player guild/Secret Society define("LOCF_T_MONSTER" , 0x000080); // 'M' Special monster define("LOCF_T_TRAINER" , 0x000100); // 'T' Guild trainer define("LOCF_T_FORT" , 0x000200); // 'F' Regions fort define("LOCF_T_MASK" , 0x00fff0); define("LOCF_MASK" , (LOCF_M_PCITY | LOCF_M_CITY | LOCF_T_MASK)); // Extra flags define("LOCF_INVIS" , 0x010000); // '-' Invisible marker / Don't show label define("LOCF_CLOSED" , 0x020000); // '!' Area is CLOSED define("LOCF_INSTANCED" , 0x040000); // 'I' Location is "instanced" for each player define("LOCF_INVALID" , 0x400000); // Possibly invalid location define("LOCF_NOMARKER" , 0x800000); // Location has no marker in mapdata or explicitly defined define("LOCF_Q_MASK" , 0xff0000); // Creator roles define("AUTHOR_ORIG" , 0x000001); // '@' Original area creator define("AUTHOR_RECODER" , 0x000002); // '!' Converter or recoder of area define("AUTHOR_MAINTAINER" , 0x000004); // '%' Maintainer define("AUTHOR_EXPANDER" , 0x000008); // '&' Expander, adding new things // Area name flags define("NAME_ORIG" , 0x000001); // '@' Original area name // Timestamp accuracy define("TS_ACC_DEFAULT" , 0); define("TS_ACC_KNOWN" , '!'); define("TS_ACC_GUESSTIMATE" , '?'); define("TS_ACC_APPROXIMATE" , '#'); // Location types table define("LTI_PAGE_DESC" , 0); define("LTI_MENU_TITLE" , 1); define("LTI_INVERT_FLAGS" , 2); define("LTI_FLAGS" , 3); $locationTypes = [ //ID => Page description , Menu title , InvFlags, Mask flags "" => ["ALL" , "EVERYTHING" , TRUE , 0 ], "A" => ["Areas" , "Areas" , TRUE , LOCF_MASK ], "S" => ["Race and other shrines" , "Shrines" , FALSE , LOCF_T_SHRINE ], "G" => ["Guilds" , "Guilds" , FALSE , LOCF_T_GUILD ], "P" => ["Outworld secret societies" , "SS" , FALSE , LOCF_T_SS ], "M" => ["Special outworld monsters" , "Monsters" , FALSE , LOCF_T_MONSTER ], "c" => ["Major cities" , "Cities" , FALSE , LOCF_M_CITY ], "T" => ["Guild trainers" , "Trainers" , FALSE , LOCF_T_TRAINER ], "C" => ["Player cities" , "PCities" , FALSE , LOCF_M_PCITY ], //"F" => ["Regional Forts" , "Forts" , FALSE , LOCF_T_FORT ], ]; function mpGetLocationTypePrefix($flags) { switch ($flags & LOCF_M_MASK) { case LOCF_M_PCITY: return "PCITY "; case LOCF_M_CITY: return "CITY "; default: switch ($flags & LOCF_T_MASK) { case LOCF_T_SHRINE: return "SHRINE "; 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 "; default: return ""; } } } function mpGetLocationTypeStr($flags) { switch ($flags & LOCF_M_MASK) { case LOCF_M_PCITY: return "Player City"; case LOCF_M_CITY: return "Major City"; default: switch ($flags & LOCF_T_MASK) { case LOCF_T_SHRINE: return "Race Shrine"; 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"; default: return "Area"; } } } function mpError($msg) { global $errorSet, $errorMsgs; $errorSet = TRUE; $errorMsgs[] = $msg; return FALSE; } function mpTrimIfString($val) { if (is_string($val)) return trim($val); else return $val; } function mpGetRequestItem($name, $default = "", $allowGet = FALSE) { if ($allowGet) return isset($_REQUEST[$name]) ? mpTrimIfString($_REQUEST[$name]) : $default; else return isset($_POST[$name]) ? mpTrimIfString($_POST[$name]) : $default; } function chentities($str) { return htmlentities($str, ENT_NOQUOTES, "UTF-8"); } function mpLocFormatTime($loc) { switch ($loc["added_accuracy"]) { case TS_ACC_GUESSTIMATE: return strftime("%b %Y", $loc["added"]); case TS_ACC_APPROXIMATE: return strftime("%b %Y", $loc["added"]); case TS_ACC_DEFAULT: return strftime("%d %b %Y*", $loc["added"]); default: return strftime("%d %b %Y", $loc["added"]); } } function mpPrintPageHeader($title, $extra = "", $bodyExtra = "", $useMenu = TRUE) { global $pageTitle, $pageMapURL, $pageCSS, $pageCharset, $pageLang; echo "<!DOCTYPE html>\n". "<html".(isset($pageLang) ? " lang=\"".$pageLang."\"" : "").">\n". "<head>\n". " <meta charset=\"".$pageCharset."\">\n". " <title>".strip_tags($pageTitle)."</title>\n". " <meta name=\"keywords\" content=\"batmud,map,laenor,lucentium,rothikgen,desolathya,furnachia,mud,mush,mmorpg,mmo,massively,multiplayer,online,network,game,ggr,pupunen\"/>\n". " <link rel=\"shortcut icon\" href=\"/maps/favicon.ico\" type=\"image/x-icon\" />\n". $extra. " <link rel=\"stylesheet\" href=\"".$pageCSS."\" type=\"text/css\" />\n". "</head>\n". "<body".$bodyExtra.">\n"; if ($useMenu) require "menu.inc.php"; echo "<div id=\"contents\">\n"; /* // CETA banner expires on 27.8.2017 if (time() < mktime(0, 0, 0, 8, 27, 2017)) { echo "<div style=\"text-align: center;\"><a href=\"https://suomiceta.wordpress.com/\"><img src=\"/cropped-suomiceta_nettisivubanneri.png\" alt=\"Suomi-CETA\" /></a></div>\n"; } */ } function mpPrintPageFooter($useContents = TRUE) { if ($useContents) echo "</div>\n"; echo "</body>\n</html>\n"; } function mpGetMTimeStr($mtime) { // return date("D d.m.Y H:i:s T", $mtime); return date("d.m.Y H:i", $mtime); } function mpFingerURL($name) { return "https://www.bat.org/char/".$name; } function burl($name) { // return "<a href=\"".mpFingerURL($name)."\">".$name."</a>"; return "<b>".$name."</b>"; } function mpGetMapURLLink($data, $link, $name, $gmap) { global $pageGMapURL, $continentList; if ($link) { $c = $continentList[$data["continent"]]; $str = "<a class=\"mapLink\" href=\"". $data["continent"].".html#loc".$data["x"]."_".$data["y"]. "\">".chentities($name)."</a>"; if (isset($pageGMapURL) && $gmap && $c[CTI_HAS_MAP] == TRUE && $c[CTI_REG_CONT] == TRUE) // hasmap & read regular cont must be true { $str .= " <a class=\"gmapLink\" title=\"GMap link\" ". "href=\"".$pageGMapURL."?x=".$data["globalx"]. "&y=".$data["globaly"]."&zoom=10\">★</a>"; } return $str; } else return "<b>".chentities($name)."</b>"; } function mpGetMapLink($data, $link, $gmap, $ltype) { return mpGetMapURLLink($data, $link, ($ltype ? mpGetLocationTypePrefix($data["flags"]) : "").$data["name"], $gmap); } function mpChConv($str) { global $pageCharset; return iconv("UTF-8", $pageCharset, $str); } function mpAddLocNames(&$list, $input) { $list = []; foreach ($input as $name) { $nflags = 0; $npos = 0; switch (substr($name, 0, 1)) { case "@": $npos++; $nflags = AUTHOR_ORIG; break; case "!": $npos++; $nflags = AUTHOR_RECODER; break; case "%": $npos++; $nflags = AUTHOR_MAINTAINER; break; case "&": $npos++; $nflags = AUTHOR_EXPANDER; break; } $list[] = [ "name" => substr($name, $npos), "flags" => $nflags ]; } } function mpGetGlobalCoords($continent, $xc, $yc, &$gx, &$gy) { global $continentList, $worldMap; if (isset($continentList[$continent])) { $gx = $worldMap["ox"] + $continentList[$continent][CTI_XOFFS] + $xc - 1; $gy = $worldMap["oy"] + $continentList[$continent][CTI_YOFFS] + $yc - 1; return TRUE; } else return FALSE; } function mpParseLocFile($filename, &$locations, $applyFilter, $invertFilter, $filterMask) { global $locationTypes; $file = @fopen(basename($filename.".loc"), "r"); if (!$file) return FALSE; while (!feof($file)) { $inLine = mpChConv(trim(fgets($file))); // Ignore comments and empty lines if (strlen($inLine) == 0 || $inLine[0] == "#") continue; if (preg_match("/^(\d+)\s*;\s*(\d+)\s*;\s*([0-3][A-Za-z%!\?\-]*)\s*;\s*([^;]+)\s*;\s*(.*?)\s*;\s*(.*?)\s*;\s*(.*?)\s*;\s*(.*?)\s*$/", $inLine, $record)) { // Get location names mpAddLocNames($names, preg_split("/\s*\|\s*/", $record[4])); // Parse the flags, first skip the label orientation $rawflags = $record[3]; for ($fi = 0; $fi < strlen($rawflags) && (ctype_digit($rawflags[$fi]) || $rawflags[$fi] == ' '); $fi++); $rawflags = substr($rawflags, $fi); $id = $rawflags.$names[0]["name"]; $flags = LOCF_NONE; for ($fi = 0; $fi < strlen($rawflags); $fi++) { switch ($rawflags[$fi]) { case "?": $flags |= LOCF_M_SCENIC1; break; case "%": $flags |= LOCF_M_SCENIC2; break; case "C": $flags |= LOCF_M_PCITY; break; case "c": $flags |= LOCF_M_CITY; break; case "S": $flags |= LOCF_T_SHRINE; break; case "G": $flags |= LOCF_T_GUILD; break; case "P": $flags |= LOCF_T_SS; break; case "M": $flags |= LOCF_T_MONSTER; break; case "T": $flags |= LOCF_T_TRAINER; break; case "F": $flags |= LOCF_T_FORT; break; case "-": $flags |= LOCF_INVIS; break; case "!": $flags |= LOCF_CLOSED; break; case "I": $flags |= LOCF_INVALID; break; default: mpError("Location '<b>".$id."</b>' has invalid flag '<b>".$rawflags[$fi]."</b>' <=> ".$filename.")"); } } // Is the location visible? Does it match the filter, if any? if (($flags & LOCF_INVIS) == 0 && (!$applyFilter || (($flags & $filterMask) ? TRUE : FALSE) != $invertFilter)) { // Check for duplicate locations if (isset($locations[$id])) { mpError("Location '<b>".$id."</b>' already defined (".$locations[$id]["continent"]." <=> ".$filename.")"); continue; } // Create primary location name $name = $names[0]["name"]; if ($flags & LOCF_CLOSED) { $name .= " (CLOSED)"; $ffreeform = $record[8]." This location is closed."; } else { $ffreeform = $record[8]; } mpGetGlobalCoords($filename, $record[1], $record[2], $glx, $gly); // Split author names if (strlen($record[5]) > 0) mpAddLocNames($authors, preg_split("/\s*,\s*/", $record[5])); else $authors = []; // Get timestamp $stamp = $record[6]; $added = -1; $added_accuracy = TS_ACC_DEFAULT; if (strlen($stamp) > 0) { switch ($stamp[0]) { case TS_ACC_KNOWN: case TS_ACC_GUESSTIMATE: case TS_ACC_APPROXIMATE: $added_accuracy = $stamp[0]; $stamp = substr($stamp, 1); break; } if (sscanf($stamp, "%02d.%02d.%04d", $aday, $amonth, $ayear) == 3) { if (($added = mktime(0, 0, 0, $amonth, $aday, $ayear)) === FALSE) $added = -1; } else { mpError("Location <b>".$record[4]."</b> has invalid timestamp '".$record[6]."'"); } } // Add location to array of locations $locations[$id] = [ "name" => $name, // NOTE! Name field should be the first one in this list due to how we are abusing asort() etc "names" => $names, "continent" => $filename, "x" => $record[1], "y" => $record[2], "globalx" => $glx, "globaly" => $gly, "flags" => $flags, "authors" => $authors, "added" => $added, "added_accuracy" => $added_accuracy, "url" => strlen($record[7]) ? $record[7] : NULL, "freeform" => strlen($ffreeform) ? $ffreeform : NULL, ]; } } else { mpError($inLine); //return FALSE; } } fclose($file); return TRUE; } function mpParseLocFiles($applyFilter, $invertFilter = TRUE, $filterMask = LOCF_M_PCITY | LOCF_T_FORT) { global $continentList; $locations = []; foreach ($continentList as $id => $data) { if ($data[CTI_REG_CONT]) mpParseLocFile($id, $locations, $applyFilter, $invertFilter, $filterMask); } return $locations; } function mpStoreWizInfoField(&$wizInfo, $field, $data) { $str = trim($data); if (strlen($str) > 0) $wizInfo[$field] = $str; } function mpStoreWizInfoRecordInfo($nline, &$wizInfo, $record) { // Parse and check names $allnames = preg_split("/\s*\|\s*/", trim($record[1])); foreach ($allnames as $name) { if ($name == "" || $name != strtoupper(substr($name, 0, 1)).strtolower(substr($name, 1))) { mpError("Invalid name in wizard record (line #".$nline."): '".$name."'."); } } // Use first name as primary "key" $keyname = $allnames[0]; if (isset($wizInfo[$keyname])) { mpError("Wizard record '".$keyname."' is already set on line #". $wizInfo[$keyname]["nline"].", vs line #".$nline); return FALSE; } else { $data = [ "name" => $keyname, "names" => array_slice($allnames, 1), "areas" => 0, "nline" => $nline, ]; $countries = array_map( function ($item) { return strtolower($item); }, array_filter(preg_split("/\s*\|\s*/", $record[2]), function ($item) { return $item != ""; })); if (count($countries) > 0) $data["countries"] = $countries; mpStoreWizInfoField($data, "homeURL", $record[3]); mpStoreWizInfoField($data, "imageURL", $record[4]); mpStoreWizInfoField($data, "desc", $record[5]); $wizInfo[$keyname] = $data; return TRUE; } } function mpParseWizInfoFile($filename, &$wizInfo) { $startLine = $nline = 0; if (($file = @fopen($filename, "r")) === false) return FALSE; $contMode = FALSE; while (!feof($file)) { $line = mpChConv(trim(fgets($file))); $nline++; if (strlen($line) == 0 || $line[0] == "#") continue; if ($contMode) { if (substr($line, -1, 1) == '$') { $record[5] .= " ".trim(substr($line, 0, -1)); $contMode = FALSE; mpStoreWizInfoRecordInfo($startLine, $wizInfo, $record); } else { $record[5] .= " ".trim($line); } } else if (preg_match("/^([A-Za-z\|]+);([a-z\|]+)?;(https?:\/\/[^;]+|bat)?;([^;]+)?;([^\$]*)\\\$$/", $line, $record)) mpStoreWizInfoRecordInfo($nline, $wizInfo, $record); else if (preg_match("/^([A-Za-z\|]+);([a-z\|]+)?;(https?:\/\/[^;]+|bat)?;([^;]+)?;(.*)$/", $line, $record)) { $startLine = $nline; $contMode = TRUE; } else mpError("Wizard record line #".$nline.": ".$line); } fclose($file); return TRUE; } function mpMakeWizInfoAliases($wizInfo) { $aliases = []; foreach ($wizInfo as $name => $data) { foreach ($data["names"] as $nname) $aliases[$nname] = $name; } return $aliases; } function mpReadWizInfoFiles() { $wizInfo = []; mpParseWizInfoFile("wizards.txt", $wizInfo); return $wizInfo; } function mpPrintExtraBoxAlphaList($prefix, $table, $class = FALSE) { echo "<div id=\"extraBox\"".($class !== FALSE ? " class=\"".$class."\"" : "").">\n"; foreach ($table as $alpha => $locs) { $letter = strtoupper($alpha); echo " <a href=\"#".$prefix.$letter."\">".$letter."</a> \n"; } echo "</div>\n"; } function mpSpecialDate($where, $index) { global $specialDate; $str = ""; switch ($specialDate) { case "aprilli": for ($ni = 0; $ni < 4; $ni++) { $str .= "<img src=\"img/". (($ni % 2 == 0 && $where == "index") ? "skull.gif" : "uc.gif"). "\" alt=\"ZOMG\" />"; } break; } return $str; } ?>