Files
psopeeps-newserv/src/StaticGameData.cc
T
2023-10-18 11:57:13 -07:00

796 lines
17 KiB
C++

#include "StaticGameData.hh"
#include <array>
#include "Text.hh"
using namespace std;
bool episode_has_arpg_semantics(Episode ep) {
return (ep == Episode::EP1) || (ep == Episode::EP2) || (ep == Episode::EP4);
}
const char* name_for_episode(Episode ep) {
switch (ep) {
case Episode::NONE:
return "No episode";
case Episode::EP1:
return "Episode 1";
case Episode::EP2:
return "Episode 2";
case Episode::EP3:
return "Episode 3";
case Episode::EP4:
return "Episode 4";
default:
return "Unknown episode";
}
}
const char* token_name_for_episode(Episode ep) {
switch (ep) {
case Episode::EP1:
return "Episode1";
case Episode::EP2:
return "Episode2";
case Episode::EP3:
return "Episode3";
case Episode::EP4:
return "Episode4";
default:
throw logic_error("invalid episode");
}
}
const char* abbreviation_for_episode(Episode ep) {
switch (ep) {
case Episode::NONE:
return "None";
case Episode::EP1:
return "Ep1";
case Episode::EP2:
return "Ep2";
case Episode::EP3:
return "Ep3";
case Episode::EP4:
return "Ep4";
default:
return "UnkEp";
}
}
const char* name_for_mode(GameMode mode) {
switch (mode) {
case GameMode::NORMAL:
return "Normal";
case GameMode::BATTLE:
return "Battle";
case GameMode::CHALLENGE:
return "Challenge";
case GameMode::SOLO:
return "Solo";
default:
return "Unknown mode";
}
}
const char* abbreviation_for_mode(GameMode mode) {
switch (mode) {
case GameMode::NORMAL:
return "Nml";
case GameMode::BATTLE:
return "Btl";
case GameMode::CHALLENGE:
return "Chl";
case GameMode::SOLO:
return "Solo";
default:
return "UnkMd";
}
}
const vector<string> section_id_to_name = {
"Viridia", "Greennill", "Skyly", "Bluefull", "Purplenum",
"Pinkal", "Redria", "Oran", "Yellowboze", "Whitill"};
const unordered_map<string, uint8_t> name_to_section_id({
{"viridia", 0},
{"greennill", 1},
{"skyly", 2},
{"bluefull", 3},
{"purplenum", 4},
{"pinkal", 5},
{"redria", 6},
{"oran", 7},
{"yellowboze", 8},
{"whitill", 9},
// Shortcuts for chat commands
{"b", 3},
{"g", 1},
{"o", 7},
{"pi", 5},
{"pu", 4},
{"r", 6},
{"s", 2},
{"v", 0},
{"w", 9},
{"y", 8},
});
const vector<string> lobby_event_to_name = {
"none", "xmas", "none", "val", "easter", "hallo", "sonic", "newyear",
"summer", "white", "wedding", "fall", "s-spring", "s-summer", "spring"};
const unordered_map<string, uint8_t> name_to_lobby_event({
{"none", 0},
{"xmas", 1},
{"val", 3},
{"easter", 4},
{"hallo", 5},
{"sonic", 6},
{"newyear", 7},
{"summer", 8},
{"white", 9},
{"wedding", 10},
{"fall", 11},
{"s-spring", 12},
{"s-summer", 13},
{"spring", 14},
});
const unordered_map<uint8_t, string> lobby_type_to_name({
{0x00, "normal"},
{0x0F, "inormal"},
{0x10, "ipc"},
{0x11, "iball"},
{0x67, "cave2u"},
{0xD4, "cave1"},
{0xE9, "planet"},
{0xEA, "clouds"},
{0xED, "cave"},
{0xEE, "jungle"},
{0xEF, "forest2-2"},
{0xF0, "forest2-1"},
{0xF1, "windpower"},
{0xF2, "overview"},
{0xF3, "seaside"},
{0xF4, "fons"},
{0xF5, "dmorgue"},
{0xF6, "caelum"},
{0xF8, "cyber"},
{0xF9, "boss1"},
{0xFA, "boss2"},
{0xFB, "dolor"},
{0xFC, "dragon"},
{0xFD, "derolle"},
{0xFE, "volopt"},
{0xFF, "darkfalz"},
});
const unordered_map<string, uint8_t> name_to_lobby_type({
{"normal", 0x00},
{"inormal", 0x0F},
{"ipc", 0x10},
{"iball", 0x11},
{"cave1", 0xD4},
{"cave2u", 0x67},
{"dragon", 0xFC},
{"derolle", 0xFD},
{"volopt", 0xFE},
{"darkfalz", 0xFF},
{"planet", 0xE9},
{"clouds", 0xEA},
{"cave", 0xED},
{"jungle", 0xEE},
{"forest2-2", 0xEF},
{"forest2-1", 0xF0},
{"windpower", 0xF1},
{"overview", 0xF2},
{"seaside", 0xF3},
{"fons", 0xF4},
{"dmorgue", 0xF5},
{"caelum", 0xF6},
{"cyber", 0xF8},
{"boss1", 0xF9},
{"boss2", 0xFA},
{"dolor", 0xFB},
{"ravum", 0xFC},
{"sky", 0xFE},
{"morgue", 0xFF},
});
const vector<string> npc_id_to_name({"ninja", "rico", "sonic", "knuckles", "tails", "flowen", "elly"});
const unordered_map<string, uint8_t> name_to_npc_id = {
{"ninja", 0}, {"rico", 1}, {"sonic", 2}, {"knuckles", 3}, {"tails", 4}, {"flowen", 5}, {"elly", 6}};
const string& name_for_section_id(uint8_t section_id) {
if (section_id < section_id_to_name.size()) {
return section_id_to_name[section_id];
} else {
static const string ret = "<Unknown section id>";
return ret;
}
}
u16string u16name_for_section_id(uint8_t section_id) {
return decode_sjis(name_for_section_id(section_id));
}
uint8_t section_id_for_name(const string& name) {
string lower_name = tolower(name);
try {
return name_to_section_id.at(lower_name);
} catch (const out_of_range&) {
}
try {
uint64_t x = stoul(name);
if (x < section_id_to_name.size()) {
return x;
}
} catch (const invalid_argument&) {
} catch (const out_of_range&) {
}
return 0xFF;
}
uint8_t section_id_for_name(const u16string& name) {
return section_id_for_name(encode_sjis(name));
}
const string& name_for_event(uint8_t event) {
if (event < lobby_event_to_name.size()) {
return lobby_event_to_name[event];
} else {
static const string ret = "<Unknown lobby event>";
return ret;
}
}
u16string u16name_for_event(uint8_t event) {
return decode_sjis(name_for_event(event));
}
uint8_t event_for_name(const string& name) {
try {
return name_to_lobby_event.at(name);
} catch (const out_of_range&) {
}
try {
uint64_t x = stoul(name);
if (x < lobby_event_to_name.size()) {
return x;
}
} catch (const invalid_argument&) {
} catch (const out_of_range&) {
}
return 0xFF;
}
uint8_t event_for_name(const u16string& name) {
return event_for_name(encode_sjis(name));
}
const string& name_for_lobby_type(uint8_t type) {
try {
return lobby_type_to_name.at(type);
} catch (const out_of_range&) {
static const string ret = "<Unknown lobby type>";
return ret;
}
}
u16string u16name_for_lobby_type(uint8_t type) {
return decode_sjis(name_for_lobby_type(type));
}
uint8_t lobby_type_for_name(const string& name) {
try {
return name_to_lobby_type.at(name);
} catch (const out_of_range&) {
}
try {
uint64_t x = stoul(name, nullptr, 0);
if (lobby_type_to_name.count(x)) {
return x;
}
} catch (const invalid_argument&) {
} catch (const out_of_range&) {
}
return 0x80;
}
uint8_t lobby_type_for_name(const u16string& name) {
return lobby_type_for_name(encode_sjis(name));
}
const string& name_for_npc(uint8_t npc) {
try {
return npc_id_to_name.at(npc);
} catch (const out_of_range&) {
static const string ret = "<Unknown NPC>";
return ret;
}
}
u16string u16name_for_npc(uint8_t npc) {
return decode_sjis(name_for_npc(npc));
}
uint8_t npc_for_name(const string& name) {
try {
return name_to_npc_id.at(name);
} catch (const out_of_range&) {
}
try {
uint64_t x = stoul(name);
if (x < npc_id_to_name.size()) {
return x;
}
} catch (const invalid_argument&) {
} catch (const out_of_range&) {
}
return 0xFF;
}
uint8_t npc_for_name(const u16string& name) {
return npc_for_name(encode_sjis(name));
}
const char* name_for_char_class(uint8_t cls) {
static const array<const char*, 12> names = {
"HUmar",
"HUnewearl",
"HUcast",
"RAmar",
"RAcast",
"RAcaseal",
"FOmarl",
"FOnewm",
"FOnewearl",
"HUcaseal",
"FOmar",
"RAmarl",
};
try {
return names.at(cls);
} catch (const out_of_range&) {
return "Unknown";
}
}
const char* abbreviation_for_char_class(uint8_t cls) {
static const array<const char*, 12> names = {
"HUmr",
"HUnl",
"HUcs",
"RAmr",
"RAcs",
"RAcl",
"FOml",
"FOnm",
"FOnl",
"HUcl",
"FOmr",
"RAml",
};
try {
return names.at(cls);
} catch (const out_of_range&) {
return "???";
}
}
enum ClassFlag {
MALE = 0x01,
HUMAN = 0x02,
NEWMAN = 0x04,
ANDROID = 0x08,
HUNTER = 0x10,
RANGER = 0x20,
FORCE = 0x40,
};
static array<uint8_t, 12> class_flags = {
ClassFlag::HUNTER | ClassFlag::HUMAN | ClassFlag::MALE, // HUmar
ClassFlag::HUNTER | ClassFlag::NEWMAN, // HUnewearl
ClassFlag::HUNTER | ClassFlag::ANDROID | ClassFlag::MALE, // HUcast
ClassFlag::RANGER | ClassFlag::HUMAN | ClassFlag::MALE, // RAmar
ClassFlag::RANGER | ClassFlag::ANDROID | ClassFlag::MALE, // RAcast
ClassFlag::RANGER | ClassFlag::ANDROID, // RAcaseal
ClassFlag::FORCE | ClassFlag::HUMAN, // FOmarl
ClassFlag::FORCE | ClassFlag::NEWMAN | ClassFlag::MALE, // FOnewm
ClassFlag::FORCE | ClassFlag::NEWMAN, // FOnewearl
ClassFlag::HUNTER | ClassFlag::ANDROID, // HUcaseal
ClassFlag::FORCE | ClassFlag::HUMAN | ClassFlag::MALE, // FOmar
ClassFlag::RANGER | ClassFlag::HUMAN, // RAmarl
};
bool char_class_is_male(uint8_t cls) {
return class_flags.at(cls) & ClassFlag::MALE;
}
bool char_class_is_human(uint8_t cls) {
return class_flags.at(cls) & ClassFlag::HUMAN;
}
bool char_class_is_newman(uint8_t cls) {
return class_flags.at(cls) & ClassFlag::NEWMAN;
}
bool char_class_is_android(uint8_t cls) {
return class_flags.at(cls) & ClassFlag::ANDROID;
}
bool char_class_is_hunter(uint8_t cls) {
return class_flags.at(cls) & ClassFlag::HUNTER;
}
bool char_class_is_ranger(uint8_t cls) {
return class_flags.at(cls) & ClassFlag::RANGER;
}
bool char_class_is_force(uint8_t cls) {
return class_flags.at(cls) & ClassFlag::FORCE;
}
const char* name_for_difficulty(uint8_t difficulty) {
static const array<const char*, 4> names = {
"Normal", "Hard", "Very Hard", "Ultimate"};
try {
return names.at(difficulty);
} catch (const out_of_range&) {
return "Unknown";
}
}
const char* token_name_for_difficulty(uint8_t difficulty) {
static const array<const char*, 4> names = {
"Normal", "Hard", "VeryHard", "Ultimate"};
try {
return names.at(difficulty);
} catch (const out_of_range&) {
return "Unknown";
}
}
char abbreviation_for_difficulty(uint8_t difficulty) {
static const array<char, 4> names = {'N', 'H', 'V', 'U'};
try {
return names.at(difficulty);
} catch (const out_of_range&) {
return '?';
}
}
char char_for_language_code(uint8_t language_code) {
switch (language_code) {
case 0:
return 'J';
case 1:
return 'E';
case 2:
return 'G';
case 3:
return 'F';
case 4:
return 'S';
default:
return '?';
}
}
uint8_t language_code_for_char(char language_char) {
switch (language_char) {
case 'J':
case 'j':
return 0;
case 'E':
case 'e':
return 1;
case 'G':
case 'g':
return 2;
case 'F':
case 'f':
return 3;
case 'S':
case 's':
return 4;
default:
throw runtime_error("unknown language");
}
}
size_t max_stack_size_for_item(uint8_t data0, uint8_t data1) {
if (data0 == 4) {
return 999999;
}
if (data0 == 3) {
if ((data1 < 9) && (data1 != 2)) {
return 10;
} else if (data1 == 0x10) {
return 99;
}
}
return 1;
}
const vector<string> tech_id_to_name = {
"foie", "gifoie", "rafoie",
"barta", "gibarta", "rabarta",
"zonde", "gizonde", "razonde",
"grants", "deband", "jellen", "zalure", "shifta",
"ryuker", "resta", "anti", "reverser", "megid"};
const unordered_map<string, uint8_t> name_to_tech_id({
{"foie", 0},
{"gifoie", 1},
{"rafoie", 2},
{"barta", 3},
{"gibarta", 4},
{"rabarta", 5},
{"zonde", 6},
{"gizonde", 7},
{"razonde", 8},
{"grants", 9},
{"deband", 10},
{"jellen", 11},
{"zalure", 12},
{"shifta", 13},
{"ryuker", 14},
{"resta", 15},
{"anti", 16},
{"reverser", 17},
{"megid", 18},
});
const string& name_for_technique(uint8_t tech) {
try {
return tech_id_to_name.at(tech);
} catch (const out_of_range&) {
static const string ret = "<Unknown technique>";
return ret;
}
}
u16string u16name_for_technique(uint8_t tech) {
return decode_sjis(name_for_technique(tech));
}
uint8_t technique_for_name(const string& name) {
try {
return name_to_tech_id.at(name);
} catch (const out_of_range&) {
}
try {
uint64_t x = stoul(name);
if (x < tech_id_to_name.size()) {
return x;
}
} catch (const invalid_argument&) {
} catch (const out_of_range&) {
}
return 0xFF;
}
uint8_t technique_for_name(const u16string& name) {
return technique_for_name(encode_sjis(name));
}
const vector<const char*> name_for_mag_color({
/* 00 */ "red",
/* 01 */ "blue",
/* 02 */ "yellow",
/* 03 */ "green",
/* 04 */ "purple",
/* 05 */ "black",
/* 06 */ "white",
/* 07 */ "cyan",
/* 08 */ "brown",
/* 09 */ "orange",
/* 0A */ "light-blue",
/* 0B */ "olive",
/* 0C */ "light-cyan",
/* 0D */ "dark-purple",
/* 0E */ "grey",
/* 0F */ "light-grey",
/* 10 */ "pink",
/* 11 */ "dark-cyan",
/* 12 */ "costume",
});
const unordered_map<string, uint8_t> mag_color_for_name({
{"red", 0x00},
{"blue", 0x01},
{"yellow", 0x02},
{"green", 0x03},
{"purple", 0x04},
{"black", 0x05},
{"white", 0x06},
{"cyan", 0x07},
{"brown", 0x08},
{"orange", 0x09},
{"light-blue", 0x0A},
{"olive", 0x0B},
{"light-cyan", 0x0C},
{"dark-purple", 0x0D},
{"grey", 0x0E},
{"light-grey", 0x0F},
{"pink", 0x10},
{"dark-cyan", 0x11},
{"costume-color", 0x12},
});
uint8_t area_for_name(const std::string& name) {
static const unordered_map<string, uint8_t> areas({
{"pioneer2", 0x00},
{"p2", 0x00},
{"forest1", 0x01},
{"f1", 0x01},
{"forest2", 0x02},
{"f2", 0x02},
{"caves1", 0x03},
{"cave1", 0x03},
{"c1", 0x03},
{"caves2", 0x04},
{"cave2", 0x04},
{"c2", 0x04},
{"caves3", 0x05},
{"cave3", 0x05},
{"c3", 0x05},
{"mines1", 0x06},
{"mine1", 0x06},
{"m1", 0x06},
{"mines2", 0x07},
{"mine2", 0x07},
{"m2", 0x07},
{"ruins1", 0x08},
{"ruin1", 0x08},
{"r1", 0x08},
{"ruins2", 0x09},
{"ruin2", 0x09},
{"r2", 0x09},
{"ruins3", 0x0A},
{"ruin3", 0x0A},
{"r3", 0x0A},
{"dragon", 0x0B},
{"derolle", 0x0C},
{"volopt", 0x0D},
{"darkfalz", 0x0E},
{"lobby", 0x0F},
{"battle1", 0x10},
{"battle2", 0x11},
{"pioneer2", 0x00},
{"p2", 0x00},
{"vrtemplealpha", 0x01},
{"templealpha", 0x01},
{"vrtemplebeta", 0x02},
{"templebeta", 0x02},
{"vrspaceshipalpha", 0x03},
{"spaceshipalpha", 0x03},
{"vrspaceshipbeta", 0x04},
{"spaceshipbeta", 0x04},
{"centralcontrolarea", 0x05},
{"cca", 0x05},
{"junglenorth", 0x06},
{"jungleeast", 0x07},
{"mountain", 0x08},
{"seaside", 0x09},
{"seabedupper", 0x0A},
{"seabedlower", 0x0B},
{"galgryphon", 0x0C},
{"olgaflow", 0x0D},
{"barbaray", 0x0E},
{"goldragon", 0x0F},
{"seasidenight", 0x10},
{"tower", 0x11},
{"pioneer2", 0x00},
{"p2", 0x00},
{"cratereast", 0x01},
{"ce", 0x01},
{"craterwest", 0x02},
{"cw", 0x02},
{"cratersouth", 0x03},
{"cs", 0x03},
{"craternorth", 0x04},
{"cn", 0x04},
{"craterinterior", 0x05},
{"ci", 0x05},
{"desert1", 0x06},
{"d1", 0x06},
{"desert2", 0x07},
{"d2", 0x07},
{"desert3", 0x08},
{"d3", 0x08},
{"saintmillion", 0x09},
{"purgatory", 0x0A},
});
return areas.at(tolower(name));
}
static const array<const char*, 0x12> ep1_area_names = {
"Pioneer2",
"Forest1",
"Forest2",
"Caves1",
"Caves2",
"Caves3",
"Mines1",
"Mines2",
"Ruins1",
"Ruins2",
"Ruins3",
"Dragon",
"DeRolLe",
"VolOpt",
"DarkFalz",
"Lobby",
"Battle1",
"Battle2",
};
static const array<const char*, 0x12> ep2_area_names = {
"Pioneer2",
"VRTempleAlpha",
"VRTempleBeta",
"VRSpaceshipAlpha",
"VRSpaceshipBeta",
"CentralControlArea",
"JungleNorth",
"JungleEast",
"Mountain",
"Seaside",
"SeabedUpper",
"SeabedLower",
"GalGryphon",
"OlgaFlow",
"BarbaRay",
"GolDragon",
"SeasideNight",
"Tower",
};
static const array<const char*, 0x0B> ep4_area_names = {
"Pioneer2",
"CraterEast",
"CraterWest",
"CraterSouth",
"CraterNorth",
"CraterInterior",
"Desert1",
"Desert2",
"Desert3",
"SaintMillion",
"Purgatory",
};
size_t area_limit_for_episode(Episode ep) {
switch (ep) {
case Episode::EP1:
return ep1_area_names.size() - 1;
case Episode::EP2:
return ep2_area_names.size() - 1;
case Episode::EP4:
return ep4_area_names.size() - 1;
default:
return 0;
}
}
const char* name_for_area(Episode episode, uint8_t area) {
switch (episode) {
case Episode::EP1:
return ep1_area_names.at(area);
case Episode::EP2:
return ep2_area_names.at(area);
case Episode::EP4:
return ep4_area_names.at(area);
default:
throw logic_error("invalid episode for drop area");
}
}