use enums for difficulty and language; fix enemy state aliases; closes #694
This commit is contained in:
@@ -33,12 +33,12 @@ void BattleParamsIndex::Table::print(FILE* stream, Episode episode) const {
|
||||
names_str);
|
||||
};
|
||||
|
||||
for (size_t diff = 0; diff < 4; diff++) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
phosg::fwrite_fmt(stream, "{} ZZ ATP PSV EVP HP DFP ATA LCK ESP EXP DIFF NAMES\n",
|
||||
abbreviation_for_difficulty(diff));
|
||||
abbreviation_for_difficulty(difficulty));
|
||||
for (size_t z = 0; z < 0x60; z++) {
|
||||
phosg::fwrite_fmt(stream, " {:02X} ", z);
|
||||
print_entry(this->stats[diff][z], z);
|
||||
print_entry(this->stats[static_cast<size_t>(difficulty)][z], z);
|
||||
fputc('\n', stream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,12 +70,25 @@ public:
|
||||
} __packed_ws__(MovementData, 0x30);
|
||||
|
||||
struct Table {
|
||||
/* 0000 */ parray<parray<PlayerStats, 0x60>, 4> stats;
|
||||
/* 3600 */ parray<parray<AttackData, 0x60>, 4> attack_data;
|
||||
/* 7E00 */ parray<parray<ResistData, 0x60>, 4> resist_data;
|
||||
/* AE00 */ parray<parray<MovementData, 0x60>, 4> movement_data;
|
||||
/* 0000 */ parray<parray<PlayerStats, 0x60>, 4> stats; // [difficulty][bp_index]
|
||||
/* 3600 */ parray<parray<AttackData, 0x60>, 4> attack_data; // [difficulty][bp_index]
|
||||
/* 7E00 */ parray<parray<ResistData, 0x60>, 4> resist_data; // [difficulty][bp_index]
|
||||
/* AE00 */ parray<parray<MovementData, 0x60>, 4> movement_data; // [difficulty][bp_index]
|
||||
/* F600 */
|
||||
|
||||
const PlayerStats& stats_for_index(Difficulty difficulty, uint8_t index) const {
|
||||
return this->stats.at(static_cast<size_t>(difficulty)).at(index);
|
||||
}
|
||||
const AttackData& attack_data_for_index(Difficulty difficulty, uint8_t index) const {
|
||||
return this->attack_data.at(static_cast<size_t>(difficulty)).at(index);
|
||||
}
|
||||
const ResistData& resist_data_for_index(Difficulty difficulty, uint8_t index) const {
|
||||
return this->resist_data.at(static_cast<size_t>(difficulty)).at(index);
|
||||
}
|
||||
const MovementData& movement_data_for_index(Difficulty difficulty, uint8_t index) const {
|
||||
return this->movement_data.at(static_cast<size_t>(difficulty)).at(index);
|
||||
}
|
||||
|
||||
void print(FILE* stream, Episode episode) const;
|
||||
} __packed_ws__(Table, 0xF600);
|
||||
|
||||
|
||||
+5
-4
@@ -8,6 +8,7 @@
|
||||
#include <phosg/Time.hh>
|
||||
|
||||
#include "Loggers.hh"
|
||||
#include "StaticGameData.hh"
|
||||
#include "Version.hh"
|
||||
|
||||
using namespace std;
|
||||
@@ -16,7 +17,7 @@ extern bool use_terminal_colors;
|
||||
|
||||
Channel::Channel(
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color)
|
||||
@@ -249,7 +250,7 @@ shared_ptr<SocketChannel> SocketChannel::create(
|
||||
std::shared_ptr<asio::io_context> io_context,
|
||||
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color) {
|
||||
@@ -263,7 +264,7 @@ SocketChannel::SocketChannel(
|
||||
std::shared_ptr<asio::io_context> io_context,
|
||||
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color)
|
||||
@@ -331,7 +332,7 @@ asio::awaitable<void> SocketChannel::send_task() {
|
||||
PeerChannel::PeerChannel(
|
||||
std::shared_ptr<asio::io_context> io_context,
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color)
|
||||
|
||||
+5
-5
@@ -12,7 +12,7 @@
|
||||
class Channel {
|
||||
public:
|
||||
Version version;
|
||||
uint8_t language;
|
||||
Language language;
|
||||
std::shared_ptr<PSOEncryption> crypt_in;
|
||||
std::shared_ptr<PSOEncryption> crypt_out;
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
protected:
|
||||
Channel(
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END);
|
||||
@@ -120,7 +120,7 @@ public:
|
||||
static std::shared_ptr<SocketChannel> create(std::shared_ptr<asio::io_context> io_context,
|
||||
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const std::string& name = "",
|
||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END);
|
||||
@@ -138,7 +138,7 @@ private:
|
||||
std::shared_ptr<asio::io_context> io_context,
|
||||
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color);
|
||||
@@ -158,7 +158,7 @@ public:
|
||||
PeerChannel(
|
||||
std::shared_ptr<asio::io_context> io_context,
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const std::string& name = "",
|
||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END);
|
||||
|
||||
+5
-5
@@ -966,7 +966,7 @@ ChatCommandDefinition cc_edit(
|
||||
if (tokens.at(1).size() != 1) {
|
||||
throw runtime_error("invalid language");
|
||||
}
|
||||
uint8_t new_language = language_code_for_char(tokens.at(1).at(0));
|
||||
Language new_language = language_for_char(tokens.at(1).at(0));
|
||||
a.c->channel->language = new_language;
|
||||
p->inventory.language = new_language;
|
||||
p->guild_card.language = new_language;
|
||||
@@ -1829,7 +1829,7 @@ ChatCommandDefinition cc_playrec(
|
||||
auto record = make_shared<Episode3::BattleRecord>(data);
|
||||
auto battle_player = make_shared<Episode3::BattleRecordPlayer>(s->io_context, record);
|
||||
auto game = create_game_generic(
|
||||
s, a.c, a.text, "", Episode::EP3, GameMode::NORMAL, 0, false, nullptr, battle_player);
|
||||
s, a.c, a.text, "", Episode::EP3, GameMode::NORMAL, Difficulty::NORMAL, false, nullptr, battle_player);
|
||||
if (game) {
|
||||
if (start_battle_player_immediately) {
|
||||
game->set_flag(Lobby::Flag::START_BATTLE_PLAYER_IMMEDIATELY);
|
||||
@@ -1920,8 +1920,8 @@ static void command_qset_qclear(const Args& a, bool should_set) {
|
||||
a.c->proxy_session->server_channel->send(0x60, 0x00, &cmd, sizeof(cmd));
|
||||
}
|
||||
} else {
|
||||
uint8_t difficulty = a.c->proxy_session ? a.c->proxy_session->lobby_difficulty : a.c->require_lobby()->difficulty;
|
||||
G_UpdateQuestFlag_V3_BB_6x75 cmd = {{{0x75, 0x03, 0x0000}, flag_num, should_set ? 0 : 1}, difficulty, 0x0000};
|
||||
Difficulty difficulty = a.c->proxy_session ? a.c->proxy_session->lobby_difficulty : a.c->require_lobby()->difficulty;
|
||||
G_UpdateQuestFlag_V3_BB_6x75 cmd = {{{0x75, 0x03, 0x0000}, flag_num, should_set ? 0 : 1}, static_cast<uint16_t>(difficulty), 0x0000};
|
||||
a.c->channel->send(0x60, 0x00, &cmd, sizeof(cmd));
|
||||
if (a.c->proxy_session) {
|
||||
a.c->proxy_session->server_channel->send(0x60, 0x00, &cmd, sizeof(cmd));
|
||||
@@ -2892,7 +2892,7 @@ static void whatobj_whatene_fn(const Args& a, bool include_objs, bool include_en
|
||||
const auto* set_entry = nearest_ene->super_ene->version(a.c->version()).set_entry;
|
||||
string type_name = MapFile::name_for_enemy_type(set_entry->base_type, a.c->version(), area);
|
||||
send_text_message_fmt(a.c, "$C5E-{:03X}\n$C6{}\n$C2{}\n$C7X:{:.2f} Z:{:.2f}",
|
||||
nearest_ene->e_id, phosg::name_for_enum(nearest_ene->type(a.c->version(), l->episode, l->event)),
|
||||
nearest_ene->e_id, phosg::name_for_enum(nearest_ene->type(a.c->version(), l->episode, l->difficulty, l->event)),
|
||||
type_name, nearest_worldspace_pos.x, nearest_worldspace_pos.z);
|
||||
auto set_str = set_entry->str(a.c->version(), area);
|
||||
a.c->log.info_f("Enemy found via $whatobj: E-{:03X} {} at x={:g} y={:g} z={:g}",
|
||||
|
||||
+7
-7
@@ -371,7 +371,7 @@ bool Client::evaluate_quest_availability_expression(
|
||||
shared_ptr<const IntegralExpression> expr,
|
||||
shared_ptr<const Lobby> game,
|
||||
uint8_t event,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
size_t num_players,
|
||||
bool v1_present) const {
|
||||
if (this->login && this->login->account->check_flag(Account::Flag::DISABLE_QUEST_REQUIREMENTS)) {
|
||||
@@ -385,7 +385,7 @@ bool Client::evaluate_quest_availability_expression(
|
||||
}
|
||||
auto p = this->character_file();
|
||||
IntegralExpression::Env env = {
|
||||
.flags = &p->quest_flags.data.at(difficulty),
|
||||
.flags = &p->quest_flags.data.at(static_cast<size_t>(difficulty)),
|
||||
.challenge_records = &p->challenge_records,
|
||||
.team = this->team(),
|
||||
.num_players = num_players,
|
||||
@@ -404,7 +404,7 @@ bool Client::can_see_quest(
|
||||
shared_ptr<const Quest> q,
|
||||
shared_ptr<const Lobby> game,
|
||||
uint8_t event,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
size_t num_players,
|
||||
bool v1_present) const {
|
||||
if (!q->has_version_any_language(this->version())) {
|
||||
@@ -418,7 +418,7 @@ bool Client::can_play_quest(
|
||||
shared_ptr<const Quest> q,
|
||||
shared_ptr<const Lobby> game,
|
||||
uint8_t event,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
size_t num_players,
|
||||
bool v1_present) const {
|
||||
if (!q->has_version_any_language(this->version())) {
|
||||
@@ -618,7 +618,7 @@ void Client::save_character_file() {
|
||||
|
||||
void Client::create_character_file(
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const PlayerDispDataBBPreview& preview,
|
||||
shared_ptr<const LevelTable> level_table) {
|
||||
this->character_data = PSOBBCharacterFile::create_from_preview(guild_card_number, language, preview, level_table);
|
||||
@@ -1027,8 +1027,8 @@ void Client::load_all_files() {
|
||||
void Client::update_character_data_after_load(shared_ptr<PSOBBCharacterFile> charfile) {
|
||||
charfile->import_tethealla_material_usage(this->require_server_state()->level_table(this->version()));
|
||||
|
||||
uint8_t lang = this->language();
|
||||
this->log.info_f("Overriding language fields in save files with {:02X} ({})", lang, char_for_language_code(lang));
|
||||
Language lang = this->language();
|
||||
this->log.info_f("Overriding language fields in save files with {}", name_for_language(lang));
|
||||
charfile->inventory.language = lang;
|
||||
charfile->guild_card.language = lang;
|
||||
}
|
||||
|
||||
+5
-5
@@ -228,7 +228,7 @@ public:
|
||||
inline Version version() const {
|
||||
return this->channel->version;
|
||||
}
|
||||
inline uint8_t language() const {
|
||||
inline Language language() const {
|
||||
return this->channel->language;
|
||||
}
|
||||
|
||||
@@ -267,21 +267,21 @@ public:
|
||||
std::shared_ptr<const IntegralExpression> expr,
|
||||
std::shared_ptr<const Lobby> game,
|
||||
uint8_t event,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
size_t num_players,
|
||||
bool v1_present) const;
|
||||
bool can_see_quest(
|
||||
std::shared_ptr<const Quest> q,
|
||||
std::shared_ptr<const Lobby> game,
|
||||
uint8_t event,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
size_t num_players,
|
||||
bool v1_present) const;
|
||||
bool can_play_quest(
|
||||
std::shared_ptr<const Quest> q,
|
||||
std::shared_ptr<const Lobby> game,
|
||||
uint8_t event,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
size_t num_players,
|
||||
bool v1_present) const;
|
||||
|
||||
@@ -316,7 +316,7 @@ public:
|
||||
void save_character_file();
|
||||
void create_character_file(
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const PlayerDispDataBBPreview& preview,
|
||||
std::shared_ptr<const LevelTable> level_table);
|
||||
void create_battle_overlay(std::shared_ptr<const BattleRules> rules, std::shared_ptr<const LevelTable> level_table);
|
||||
|
||||
+17
-17
@@ -349,7 +349,7 @@ struct C_LegacyLogin_PC_V3_03 {
|
||||
/* 00 */ be_uint64_t hardware_id;
|
||||
/* 08 */ le_uint32_t sub_version = 0;
|
||||
/* 0C */ uint8_t is_extended = 0;
|
||||
/* 0D */ uint8_t language = 0;
|
||||
/* 0D */ Language language = Language::JAPANESE;
|
||||
/* 0E */ le_uint16_t unused = 0;
|
||||
// Note: These are suffixed with 2 since they come from the same source data
|
||||
// as the corresponding fields in 9D/9E. (Even though serial_number and
|
||||
@@ -404,7 +404,7 @@ struct C_LegacyLogin_PC_V3_04 {
|
||||
/* 00 */ be_uint64_t hardware_id;
|
||||
/* 08 */ le_uint32_t sub_version = 0;
|
||||
/* 0C */ uint8_t is_extended = 0;
|
||||
/* 0D */ uint8_t language = 0;
|
||||
/* 0D */ Language language = Language::JAPANESE;
|
||||
/* 0E */ le_uint16_t unused = 0;
|
||||
/* 10 */ pstring<TextEncoding::ASCII, 0x10> serial_number;
|
||||
/* 20 */ pstring<TextEncoding::ASCII, 0x10> access_key;
|
||||
@@ -414,7 +414,7 @@ struct C_LegacyLogin_PC_V3_04 {
|
||||
struct C_LegacyLogin_BB_04 {
|
||||
/* 00 */ le_uint32_t sub_version = 0;
|
||||
/* 04 */ uint8_t is_extended = 0;
|
||||
/* 05 */ uint8_t language = 0;
|
||||
/* 05 */ Language language = Language::JAPANESE;
|
||||
/* 06 */ le_uint16_t unused = 0;
|
||||
/* 08 */ pstring<TextEncoding::ASCII, 0x10> username;
|
||||
/* 18 */ pstring<TextEncoding::ASCII, 0x10> password;
|
||||
@@ -1286,7 +1286,7 @@ struct S_JoinGameT_DC_PC {
|
||||
/* 0104 */ uint8_t client_id = 0;
|
||||
/* 0105 */ uint8_t leader_id = 0;
|
||||
/* 0106 */ uint8_t disable_udp = 1;
|
||||
/* 0107 */ uint8_t difficulty = 0;
|
||||
/* 0107 */ Difficulty difficulty = Difficulty::NORMAL;
|
||||
/* 0108 */ uint8_t battle_mode = 0;
|
||||
/* 0109 */ uint8_t event = 0;
|
||||
/* 010A */ uint8_t section_id = 0;
|
||||
@@ -1687,7 +1687,7 @@ struct C_Login_DCNTE_8B {
|
||||
be_uint64_t hardware_id;
|
||||
le_uint32_t sub_version = 0x20;
|
||||
uint8_t is_extended = 0;
|
||||
uint8_t language = 0;
|
||||
Language language = Language::JAPANESE;
|
||||
parray<uint8_t, 2> unused1;
|
||||
pstring<TextEncoding::ASCII, 0x11> serial_number;
|
||||
pstring<TextEncoding::ASCII, 0x11> access_key;
|
||||
@@ -1748,7 +1748,7 @@ struct C_RegisterV1_DC_92 {
|
||||
be_uint64_t hardware_id;
|
||||
le_uint32_t sub_version;
|
||||
uint8_t unused1 = 0;
|
||||
uint8_t language = 0;
|
||||
Language language = Language::JAPANESE;
|
||||
parray<uint8_t, 2> unused2;
|
||||
pstring<TextEncoding::ASCII, 0x30> serial_number2;
|
||||
pstring<TextEncoding::ASCII, 0x30> access_key2;
|
||||
@@ -1767,7 +1767,7 @@ struct C_LoginV1_DC_93 {
|
||||
/* 08 */ be_uint64_t hardware_id;
|
||||
/* 10 */ le_uint32_t sub_version = 0;
|
||||
/* 14 */ uint8_t is_extended = 0;
|
||||
/* 15 */ uint8_t language = 0;
|
||||
/* 15 */ Language language = Language::JAPANESE;
|
||||
/* 16 */ parray<uint8_t, 2> unused1;
|
||||
/* 18 */ pstring<TextEncoding::ASCII, 0x11> serial_number;
|
||||
/* 29 */ pstring<TextEncoding::ASCII, 0x11> access_key;
|
||||
@@ -1788,7 +1788,7 @@ struct C_LoginBase_BB_93 {
|
||||
/* 00 */ le_uint32_t player_tag = 0x00010000;
|
||||
/* 04 */ le_uint32_t guild_card_number = 0;
|
||||
/* 08 */ le_uint32_t sub_version = 0;
|
||||
/* 0C */ uint8_t language = 0;
|
||||
/* 0C */ Language language = Language::JAPANESE;
|
||||
/* 0D */ int8_t character_slot = 0;
|
||||
// Values for connection_phase:
|
||||
// 00 - initial connection (client will request system file, characters, etc.)
|
||||
@@ -1952,7 +1952,7 @@ struct C_Register_DC_PC_V3_9C {
|
||||
/* 00 */ be_uint64_t hardware_id;
|
||||
/* 08 */ le_uint32_t sub_version = 0;
|
||||
/* 0C */ uint8_t unused1 = 0;
|
||||
/* 0D */ uint8_t language = 0;
|
||||
/* 0D */ Language language = Language::JAPANESE;
|
||||
/* 0E */ parray<uint8_t, 2> unused2;
|
||||
/* 10 */ pstring<TextEncoding::ASCII, 0x30> serial_number; // On XB, this is the XBL gamertag
|
||||
/* 40 */ pstring<TextEncoding::ASCII, 0x30> access_key; // On XB, this is the XBL user ID
|
||||
@@ -1963,7 +1963,7 @@ struct C_Register_DC_PC_V3_9C {
|
||||
struct C_Register_BB_9C {
|
||||
le_uint32_t sub_version = 0;
|
||||
uint8_t unused1 = 0;
|
||||
uint8_t language = 0;
|
||||
Language language = Language::JAPANESE;
|
||||
parray<uint8_t, 2> unused2;
|
||||
pstring<TextEncoding::ASCII, 0x30> username;
|
||||
pstring<TextEncoding::ASCII, 0x30> password;
|
||||
@@ -2002,7 +2002,7 @@ struct C_Login_DC_PC_GC_9D {
|
||||
/* 08 */ be_uint64_t hardware_id;
|
||||
/* 10 */ le_uint32_t sub_version = 0;
|
||||
/* 14 */ uint8_t is_extended = 0; // If 1, structure has extended format
|
||||
/* 15 */ uint8_t language = 0; // 0 = JP, 1 = EN, 2 = DE, 3 = FR, 4 = ES
|
||||
/* 15 */ Language language = Language::JAPANESE; // 0 = JP, 1 = EN, 2 = DE, 3 = FR, 4 = ES
|
||||
/* 16 */ parray<uint8_t, 0x2> unused3; // Always zeroes
|
||||
/* 18 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
|
||||
/* 28 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
|
||||
@@ -2062,7 +2062,7 @@ struct C_LoginExtended_BB_9E {
|
||||
/* 0000 */ le_uint32_t player_tag = 0x00010000;
|
||||
/* 0004 */ le_uint32_t guild_card_number = 0; // == account_id when on newserv
|
||||
/* 0008 */ le_uint32_t sub_version = 0;
|
||||
/* 000C */ le_uint32_t language = 0;
|
||||
/* 000C */ le_uint32_t language32 = 0;
|
||||
/* 0010 */ le_uint32_t unknown_a2 = 0;
|
||||
/* 0014 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number; // Always blank?
|
||||
/* 0024 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key; // == "?"
|
||||
@@ -2570,7 +2570,7 @@ check_struct_size(C_CreateGame_DCNTE, 0x28);
|
||||
|
||||
template <TextEncoding Encoding>
|
||||
struct C_CreateGameT : C_CreateGameBaseT<Encoding> {
|
||||
uint8_t difficulty = 0; // 0-3 (always 0 on Episode 3)
|
||||
Difficulty difficulty = Difficulty::NORMAL; // Always NORMAL on Episode 3
|
||||
uint8_t battle_mode = 0; // 0 or 1 (always 0 on Episode 3)
|
||||
// Note: Episode 3 uses the challenge mode flag for view battle permissions.
|
||||
// 0 = view battle allowed; 1 = not allowed
|
||||
@@ -2915,7 +2915,7 @@ struct C_SetChallengeModeCharacterTemplate_BB_02DF {
|
||||
|
||||
struct C_SetChallengeModeDifficulty_BB_03DF {
|
||||
// No existing challenge mode quest sets this to a value other than zero.
|
||||
le_uint32_t difficulty = 0;
|
||||
le_uint32_t difficulty32 = 0;
|
||||
} __packed_ws__(C_SetChallengeModeDifficulty_BB_03DF, 4);
|
||||
|
||||
struct C_SetChallengeModeEXPMultiplier_BB_04DF {
|
||||
@@ -3324,7 +3324,7 @@ struct S_JoinSpectatorTeam_Ep3_E8 {
|
||||
/* 1170 */ uint8_t client_id = 0;
|
||||
/* 1171 */ uint8_t leader_id = 0;
|
||||
/* 1172 */ uint8_t disable_udp = 1;
|
||||
/* 1173 */ uint8_t difficulty = 0;
|
||||
/* 1173 */ Difficulty difficulty = Difficulty::NORMAL;
|
||||
/* 1174 */ uint8_t battle_mode = 0;
|
||||
/* 1175 */ uint8_t event = 0;
|
||||
/* 1176 */ uint8_t section_id = 0;
|
||||
@@ -5145,7 +5145,7 @@ struct G_6x70_Base_V1 {
|
||||
/* 0040 */ StatusEffectState attack_status_effect;
|
||||
/* 004C */ StatusEffectState defense_status_effect;
|
||||
/* 0058 */ StatusEffectState unused_status_effect;
|
||||
/* 0064 */ le_uint32_t language = 0;
|
||||
/* 0064 */ le_uint32_t language32 = 0;
|
||||
/* 0068 */ le_uint32_t player_tag = 0;
|
||||
/* 006C */ le_uint32_t guild_card_number = 0;
|
||||
/* 0070 */ le_uint32_t unknown_a6 = 0; // Probably battle-related (assigned together with battle_team_number)
|
||||
@@ -5256,7 +5256,7 @@ struct G_UpdateQuestFlag_DC_PC_6x75 {
|
||||
} __packed_ws__(G_UpdateQuestFlag_DC_PC_6x75, 8);
|
||||
|
||||
struct G_UpdateQuestFlag_V3_BB_6x75 : G_UpdateQuestFlag_DC_PC_6x75 {
|
||||
le_uint16_t difficulty = 0;
|
||||
le_uint16_t difficulty16 = 0;
|
||||
le_uint16_t unused = 0;
|
||||
} __packed_ws__(G_UpdateQuestFlag_V3_BB_6x75, 0x0C);
|
||||
|
||||
|
||||
+37
-40
@@ -122,8 +122,7 @@ CommonItemSet::Table::Table(const phosg::JSON& json, Episode episode)
|
||||
const auto& enemy_type_drop_probs_json = json.at("EnemyTypeDropProbs").as_dict();
|
||||
const auto& enemy_item_classes_json = json.at("EnemyItemClasses").as_dict();
|
||||
for (size_t z = 0; z < 0x64; z++) {
|
||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (Episode episode : episodes) {
|
||||
for (Episode episode : ALL_EPISODES_V4) {
|
||||
auto types = enemy_types_for_rare_table_index(episode, z);
|
||||
vector<string> names;
|
||||
if (types.empty()) {
|
||||
@@ -460,8 +459,7 @@ phosg::JSON CommonItemSet::Table::json() const {
|
||||
phosg::JSON enemy_type_drop_probs_json = phosg::JSON::dict();
|
||||
phosg::JSON enemy_item_classes_json = phosg::JSON::dict();
|
||||
for (size_t z = 0; z < 0x64; z++) {
|
||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (Episode episode : episodes) {
|
||||
for (Episode episode : ALL_EPISODES_V4) {
|
||||
auto types = enemy_types_for_rare_table_index(episode, z);
|
||||
vector<string> names;
|
||||
if (types.empty()) {
|
||||
@@ -506,13 +504,11 @@ phosg::JSON CommonItemSet::Table::json() const {
|
||||
|
||||
phosg::JSON CommonItemSet::json() const {
|
||||
auto modes_dict = phosg::JSON::dict();
|
||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
for (const auto& mode : modes) {
|
||||
for (const auto& mode : ALL_GAME_MODES_V4) {
|
||||
auto episodes_dict = phosg::JSON::dict();
|
||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (const auto& episode : episodes) {
|
||||
for (const auto& episode : ALL_EPISODES_V4) {
|
||||
auto difficulty_dict = phosg::JSON::dict();
|
||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (const auto& difficulty : ALL_DIFFICULTIES_V234) {
|
||||
auto section_id_dict = phosg::JSON::dict();
|
||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||
try {
|
||||
@@ -532,11 +528,9 @@ phosg::JSON CommonItemSet::json() const {
|
||||
}
|
||||
|
||||
void CommonItemSet::print(FILE* stream) const {
|
||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
for (const auto& mode : modes) {
|
||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (const auto& episode : episodes) {
|
||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (const auto& mode : ALL_GAME_MODES_V4) {
|
||||
for (const auto& episode : ALL_EPISODES_V4) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||
try {
|
||||
auto table = this->get_table(episode, mode, difficulty, section_id);
|
||||
@@ -552,11 +546,9 @@ void CommonItemSet::print(FILE* stream) const {
|
||||
}
|
||||
|
||||
void CommonItemSet::print_diff(FILE* stream, const CommonItemSet& other) const {
|
||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
for (const auto& mode : modes) {
|
||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (const auto& episode : episodes) {
|
||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (const auto& mode : ALL_GAME_MODES_V4) {
|
||||
for (const auto& episode : ALL_EPISODES_V4) {
|
||||
for (const auto& difficulty : ALL_DIFFICULTIES_V234) {
|
||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||
shared_ptr<const Table> this_table;
|
||||
shared_ptr<const Table> other_table;
|
||||
@@ -654,7 +646,7 @@ void CommonItemSet::Table::parse_itempt_t(const phosg::StringReader& r, bool is_
|
||||
this->box_item_class_prob_table = r.pget<parray<parray<uint8_t, 10>, 7>>(offsets.box_item_class_prob_table_offset);
|
||||
}
|
||||
|
||||
uint16_t CommonItemSet::key_for_table(Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) {
|
||||
uint16_t CommonItemSet::key_for_table(Episode episode, GameMode mode, Difficulty difficulty, uint8_t secid) {
|
||||
// Bits: -----EEEMMDDSSSS
|
||||
return (((static_cast<uint16_t>(episode) << 8) & 0x0700) |
|
||||
((static_cast<uint16_t>(mode) << 6) & 0x00C0) |
|
||||
@@ -663,12 +655,12 @@ uint16_t CommonItemSet::key_for_table(Episode episode, GameMode mode, uint8_t di
|
||||
}
|
||||
|
||||
shared_ptr<const CommonItemSet::Table> CommonItemSet::get_table(
|
||||
Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const {
|
||||
Episode episode, GameMode mode, Difficulty difficulty, uint8_t secid) const {
|
||||
try {
|
||||
return this->tables.at(this->key_for_table(episode, mode, difficulty, secid));
|
||||
} catch (const out_of_range&) {
|
||||
throw runtime_error(std::format("common item table not available for episode={}, mode={}, difficulty={}, secid={}",
|
||||
name_for_episode(episode), name_for_mode(mode), difficulty, secid));
|
||||
name_for_episode(episode), name_for_mode(mode), name_for_difficulty(difficulty), secid));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,17 +670,20 @@ AFSV2CommonItemSet::AFSV2CommonItemSet(
|
||||
// Hard, etc.
|
||||
{
|
||||
AFSArchive pt_afs(pt_afs_data);
|
||||
size_t max_difficulty;
|
||||
bool include_ultimate;
|
||||
if (pt_afs.num_entries() >= 40) {
|
||||
max_difficulty = 4;
|
||||
include_ultimate = true;
|
||||
} else if (pt_afs.num_entries() >= 30) {
|
||||
max_difficulty = 3;
|
||||
include_ultimate = false;
|
||||
} else {
|
||||
throw std::runtime_error(std::format("PT AFS file has unexpected entry count ({})", pt_afs.num_entries()));
|
||||
}
|
||||
for (size_t difficulty = 0; difficulty < max_difficulty; difficulty++) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
if ((difficulty == Difficulty::ULTIMATE) && !include_ultimate) {
|
||||
continue;
|
||||
}
|
||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
||||
auto entry = pt_afs.get(difficulty * 10 + section_id);
|
||||
auto entry = pt_afs.get(static_cast<size_t>(difficulty) * 10 + section_id);
|
||||
phosg::StringReader r(entry.first, entry.second);
|
||||
auto table = make_shared<Table>(r, false, false, Episode::EP1);
|
||||
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::NORMAL, difficulty, section_id), table);
|
||||
@@ -702,16 +697,19 @@ AFSV2CommonItemSet::AFSV2CommonItemSet(
|
||||
// 30th are used (section_id is ignored)
|
||||
if (ct_afs_data) {
|
||||
AFSArchive ct_afs(ct_afs_data);
|
||||
size_t max_difficulty;
|
||||
bool include_ultimate;
|
||||
if (ct_afs.num_entries() >= 40) {
|
||||
max_difficulty = 4;
|
||||
include_ultimate = true;
|
||||
} else if (ct_afs.num_entries() >= 30) {
|
||||
max_difficulty = 3;
|
||||
include_ultimate = false;
|
||||
} else {
|
||||
throw std::runtime_error(std::format("CT AFS file has unexpected entry count ({})", ct_afs.num_entries()));
|
||||
}
|
||||
for (size_t difficulty = 0; difficulty < max_difficulty; difficulty++) {
|
||||
auto r = ct_afs.get_reader(difficulty * 10);
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
if ((difficulty == Difficulty::ULTIMATE) && !include_ultimate) {
|
||||
continue;
|
||||
}
|
||||
auto r = ct_afs.get_reader(static_cast<size_t>(difficulty) * 10);
|
||||
auto table = make_shared<Table>(r, false, false, Episode::EP1);
|
||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
||||
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::CHALLENGE, difficulty, section_id), table);
|
||||
@@ -723,7 +721,7 @@ AFSV2CommonItemSet::AFSV2CommonItemSet(
|
||||
GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gsl_data, bool is_big_endian) {
|
||||
GSLArchive gsl(gsl_data, is_big_endian);
|
||||
|
||||
auto filename_for_table = +[](Episode episode, uint8_t difficulty, uint8_t section_id, bool is_challenge) -> string {
|
||||
auto filename_for_table = +[](Episode episode, Difficulty difficulty, uint8_t section_id, bool is_challenge) -> string {
|
||||
const char* episode_token = "";
|
||||
switch (episode) {
|
||||
case Episode::EP1:
|
||||
@@ -746,9 +744,8 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
|
||||
section_id);
|
||||
};
|
||||
|
||||
vector<Episode> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (Episode episode : episodes) {
|
||||
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (Episode episode : ALL_EPISODES_V4) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
||||
phosg::StringReader r;
|
||||
try {
|
||||
@@ -773,7 +770,7 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
|
||||
}
|
||||
|
||||
if (episode != Episode::EP4) {
|
||||
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
try {
|
||||
auto r = gsl.get_reader(filename_for_table(episode, difficulty, 0, true));
|
||||
auto table = make_shared<Table>(r, is_big_endian, true, episode);
|
||||
@@ -800,9 +797,9 @@ JSONCommonItemSet::JSONCommonItemSet(const phosg::JSON& json) {
|
||||
Episode episode = episode_keys.at(episode_it.first);
|
||||
|
||||
for (const auto& difficulty_it : episode_it.second->as_dict()) {
|
||||
static const unordered_map<string, uint8_t> difficulty_keys(
|
||||
{{"Normal", 0}, {"Hard", 1}, {"VeryHard", 2}, {"Ultimate", 3}});
|
||||
uint8_t difficulty = difficulty_keys.at(difficulty_it.first);
|
||||
static const unordered_map<string, Difficulty> difficulty_keys(
|
||||
{{"Normal", Difficulty::NORMAL}, {"Hard", Difficulty::HARD}, {"VeryHard", Difficulty::VERY_HARD}, {"Ultimate", Difficulty::ULTIMATE}});
|
||||
Difficulty difficulty = difficulty_keys.at(difficulty_it.first);
|
||||
|
||||
for (const auto& section_id_it : difficulty_it.second->as_dict()) {
|
||||
uint8_t section_id = section_id_for_name(section_id_it.first);
|
||||
|
||||
@@ -271,7 +271,7 @@ public:
|
||||
bool operator==(const CommonItemSet& other) const = default;
|
||||
bool operator!=(const CommonItemSet& other) const = default;
|
||||
|
||||
std::shared_ptr<const Table> get_table(Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const;
|
||||
std::shared_ptr<const Table> get_table(Episode episode, GameMode mode, Difficulty difficulty, uint8_t secid) const;
|
||||
phosg::JSON json() const;
|
||||
void print(FILE* stream) const;
|
||||
void print_diff(FILE* stream, const CommonItemSet& other) const;
|
||||
@@ -279,7 +279,7 @@ public:
|
||||
protected:
|
||||
CommonItemSet() = default;
|
||||
|
||||
static uint16_t key_for_table(Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid);
|
||||
static uint16_t key_for_table(Episode episode, GameMode mode, Difficulty difficulty, uint8_t secid);
|
||||
|
||||
std::unordered_map<uint16_t, std::shared_ptr<Table>> tables;
|
||||
};
|
||||
|
||||
+10
-10
@@ -40,7 +40,7 @@ DownloadSession::DownloadSession(
|
||||
uint16_t remote_port,
|
||||
const std::string& output_dir,
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file,
|
||||
uint32_t serial_number2,
|
||||
uint32_t serial_number,
|
||||
@@ -222,13 +222,13 @@ void DownloadSession::send_61_98(bool is_98) {
|
||||
if (is_v1(this->version)) {
|
||||
C_CharacterData_DCv1_61_98 ret;
|
||||
ret.inventory = this->character->inventory;
|
||||
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, 1, 1);
|
||||
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
|
||||
this->channel->send(command, 0x01, ret);
|
||||
|
||||
} else if (this->version == Version::DC_V2) {
|
||||
C_CharacterData_DCv2_61_98 ret;
|
||||
ret.inventory = this->character->inventory;
|
||||
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, 1, 1);
|
||||
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
|
||||
ret.records.challenge = this->character->challenge_records;
|
||||
ret.records.battle = this->character->battle_records;
|
||||
ret.choice_search_config = this->character->choice_search_config;
|
||||
@@ -237,7 +237,7 @@ void DownloadSession::send_61_98(bool is_98) {
|
||||
} else if (this->version == Version::PC_V2) {
|
||||
C_CharacterData_PC_61_98 ret;
|
||||
ret.inventory = this->character->inventory;
|
||||
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, 1, 1);
|
||||
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
|
||||
ret.records.challenge = this->character->challenge_records;
|
||||
ret.records.battle = this->character->battle_records;
|
||||
ret.choice_search_config = this->character->choice_search_config;
|
||||
@@ -246,7 +246,7 @@ void DownloadSession::send_61_98(bool is_98) {
|
||||
} else if (is_v3(this->version)) {
|
||||
C_CharacterData_V3_61_98 ret;
|
||||
ret.inventory = this->character->inventory;
|
||||
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, 1, 1);
|
||||
ret.disp = convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(this->character->disp, Language::ENGLISH, Language::ENGLISH);
|
||||
ret.records.challenge = this->character->challenge_records;
|
||||
ret.records.battle = this->character->battle_records;
|
||||
ret.choice_search_config = this->character->choice_search_config;
|
||||
@@ -552,7 +552,7 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
|
||||
C_CreateGame_PC_C1 ret;
|
||||
ret.name.encode(random_name());
|
||||
ret.password.encode(random_name());
|
||||
ret.difficulty = 0;
|
||||
ret.difficulty = Difficulty::NORMAL;
|
||||
ret.battle_mode = (game_config.mode == GameMode::BATTLE);
|
||||
ret.challenge_mode = (game_config.mode == GameMode::CHALLENGE);
|
||||
ret.episode = 1;
|
||||
@@ -562,7 +562,7 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
|
||||
C_CreateGame_DC_V3_0C_C1_Ep3_EC ret;
|
||||
ret.name.encode(random_name());
|
||||
ret.password.encode(random_name());
|
||||
ret.difficulty = 0;
|
||||
ret.difficulty = Difficulty::NORMAL;
|
||||
ret.battle_mode = (game_config.mode == GameMode::BATTLE);
|
||||
ret.challenge_mode = (game_config.mode == GameMode::CHALLENGE);
|
||||
if (is_v1(this->version)) {
|
||||
@@ -582,7 +582,7 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
|
||||
C_CreateGame_BB_C1 ret;
|
||||
ret.name.encode(random_name());
|
||||
ret.password.encode(random_name());
|
||||
ret.difficulty = 0;
|
||||
ret.difficulty = Difficulty::NORMAL;
|
||||
ret.battle_mode = (game_config.mode == GameMode::BATTLE);
|
||||
ret.challenge_mode = (game_config.mode == GameMode::CHALLENGE);
|
||||
if (game_config.episode == Episode::EP1) {
|
||||
@@ -651,12 +651,12 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
|
||||
filtered_name.push_back((isalnum(ch) || (ch == '-') || (ch == '.') || (ch == '_')) ? ch : '_');
|
||||
}
|
||||
string local_filename = std::format(
|
||||
"{}/{:016X}_{}_{}_{}_{}",
|
||||
"{}/{:016X}_{}_{}_{:c}_{}",
|
||||
this->output_dir,
|
||||
this->current_request,
|
||||
phosg::now(),
|
||||
phosg::name_for_enum(this->version),
|
||||
char_for_language_code(this->language),
|
||||
char_for_language(this->language),
|
||||
filtered_name);
|
||||
this->open_files.emplace(internal_name, OpenFile{.request = this->current_request, .filename = local_filename, .total_size = cmd.file_size, .data = ""});
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
uint16_t remote_port,
|
||||
const std::string& output_dir,
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file,
|
||||
uint32_t serial_number2,
|
||||
uint32_t serial_number,
|
||||
@@ -50,7 +50,7 @@ protected:
|
||||
uint16_t remote_port;
|
||||
std::string output_dir;
|
||||
Version version;
|
||||
uint8_t language;
|
||||
Language language;
|
||||
bool show_command_data;
|
||||
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file;
|
||||
uint32_t serial_number;
|
||||
|
||||
+34
-27
@@ -1720,7 +1720,7 @@ phosg::JSON MapDefinition::CameraSpec::json() const {
|
||||
});
|
||||
}
|
||||
|
||||
phosg::JSON MapDefinition::NPCDeck::json(uint8_t language) const {
|
||||
phosg::JSON MapDefinition::NPCDeck::json(Language language) const {
|
||||
phosg::JSON card_ids_json = phosg::JSON::list();
|
||||
for (size_t z = 0; z < this->card_ids.size(); z++) {
|
||||
if (this->card_ids[z] != 0xFFFF) {
|
||||
@@ -1733,7 +1733,7 @@ phosg::JSON MapDefinition::NPCDeck::json(uint8_t language) const {
|
||||
});
|
||||
}
|
||||
|
||||
phosg::JSON MapDefinition::AIParams::json(uint8_t language) const {
|
||||
phosg::JSON MapDefinition::AIParams::json(Language language) const {
|
||||
phosg::JSON params_json = phosg::JSON::list();
|
||||
for (size_t z = 0; z < this->params.size(); z++) {
|
||||
params_json.emplace_back(this->params[z].load());
|
||||
@@ -1745,7 +1745,7 @@ phosg::JSON MapDefinition::AIParams::json(uint8_t language) const {
|
||||
});
|
||||
}
|
||||
|
||||
phosg::JSON MapDefinition::DialogueSet::json(uint8_t language) const {
|
||||
phosg::JSON MapDefinition::DialogueSet::json(Language language) const {
|
||||
phosg::JSON strings_json = phosg::JSON::list();
|
||||
for (size_t z = 0; z < this->strings.size(); z++) {
|
||||
strings_json.emplace_back(this->strings[z].decode(language));
|
||||
@@ -1818,7 +1818,7 @@ string MapDefinition::CameraSpec::str() const {
|
||||
this->unknown_a2[1], this->unknown_a2[2]);
|
||||
}
|
||||
|
||||
string MapDefinition::str(const CardIndex* card_index, uint8_t language) const {
|
||||
string MapDefinition::str(const CardIndex* card_index, Language language) const {
|
||||
deque<string> lines;
|
||||
auto add_map = [&](const parray<parray<uint8_t, 0x10>, 0x10>& tiles) {
|
||||
for (size_t y = 0; y < this->height; y++) {
|
||||
@@ -2503,7 +2503,7 @@ CardIndex::CardIndex(
|
||||
|
||||
// Some cards intentionally have the same name, so we just leave them
|
||||
// unindexed (they can still be looked up by ID, of course)
|
||||
string name = entry->def.en_name.decode(1);
|
||||
string name = entry->def.en_name.decode(Language::ENGLISH);
|
||||
this->card_definitions_by_name.emplace(name, entry);
|
||||
this->card_definitions_by_name_normalized.emplace(this->normalize_card_name(name), entry);
|
||||
|
||||
@@ -2620,11 +2620,11 @@ string CardIndex::normalize_card_name(const string& name) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
MapIndex::VersionedMap::VersionedMap(shared_ptr<const MapDefinition> map, uint8_t language)
|
||||
MapIndex::VersionedMap::VersionedMap(shared_ptr<const MapDefinition> map, Language language)
|
||||
: map(map),
|
||||
language(language) {}
|
||||
|
||||
MapIndex::VersionedMap::VersionedMap(std::string&& compressed_data, uint8_t language)
|
||||
MapIndex::VersionedMap::VersionedMap(std::string&& compressed_data, Language language)
|
||||
: language(language),
|
||||
compressed_data(make_shared<string>(std::move(compressed_data))) {
|
||||
string decompressed = prs_decompress(*this->compressed_data);
|
||||
@@ -2673,33 +2673,39 @@ std::shared_ptr<const std::string> MapIndex::VersionedMap::trial_download() cons
|
||||
MapIndex::Map::Map(shared_ptr<const VersionedMap> initial_version)
|
||||
: map_number(initial_version->map->map_number),
|
||||
initial_version(initial_version) {
|
||||
this->versions.resize(this->initial_version->language + 1);
|
||||
this->versions[this->initial_version->language] = initial_version;
|
||||
size_t lang_index = static_cast<size_t>(this->initial_version->language);
|
||||
this->versions.resize(lang_index + 1);
|
||||
this->versions[lang_index] = initial_version;
|
||||
}
|
||||
|
||||
void MapIndex::Map::add_version(std::shared_ptr<const VersionedMap> vm) {
|
||||
if (this->versions.size() <= vm->language) {
|
||||
this->versions.resize(vm->language + 1);
|
||||
size_t lang_index = static_cast<size_t>(vm->language);
|
||||
if (this->versions.size() <= lang_index) {
|
||||
this->versions.resize(lang_index + 1);
|
||||
}
|
||||
if (this->versions[vm->language]) {
|
||||
if (this->versions[lang_index]) {
|
||||
throw runtime_error("map version already exists");
|
||||
}
|
||||
this->initial_version->map->assert_semantically_equivalent(*vm->map);
|
||||
this->versions[vm->language] = vm;
|
||||
this->versions[lang_index] = vm;
|
||||
}
|
||||
|
||||
bool MapIndex::Map::has_version(uint8_t language) const {
|
||||
return (this->versions.size() > language) && !!this->versions[language];
|
||||
bool MapIndex::Map::has_version(Language language) const {
|
||||
size_t lang_index = static_cast<size_t>(language);
|
||||
return (this->versions.size() > lang_index) && !!this->versions[lang_index];
|
||||
}
|
||||
|
||||
shared_ptr<const MapIndex::VersionedMap> MapIndex::Map::version(uint8_t language) const {
|
||||
shared_ptr<const MapIndex::VersionedMap> MapIndex::Map::version(Language language) const {
|
||||
size_t lang_index = static_cast<size_t>(language);
|
||||
|
||||
// If the requested language exists, return it
|
||||
if ((language < this->versions.size()) && this->versions[language]) {
|
||||
return this->versions[language];
|
||||
if ((lang_index < this->versions.size()) && this->versions[lang_index]) {
|
||||
return this->versions[lang_index];
|
||||
}
|
||||
// If English exists, return it
|
||||
if ((1 < this->versions.size()) && this->versions[1]) {
|
||||
return this->versions[1];
|
||||
constexpr size_t english_lang_index = static_cast<size_t>(Language::ENGLISH);
|
||||
if ((english_lang_index < this->versions.size()) && this->versions[english_lang_index]) {
|
||||
return this->versions[english_lang_index];
|
||||
}
|
||||
// Return the first version that exists
|
||||
for (const auto& vm : this->versions) {
|
||||
@@ -2754,7 +2760,7 @@ MapIndex::MapIndex(const string& directory) {
|
||||
if (base_filename[base_filename.size() - 2] != '-') {
|
||||
throw runtime_error("language code not present");
|
||||
}
|
||||
uint8_t language = language_code_for_char(base_filename[base_filename.size() - 1]);
|
||||
Language language = language_for_char(base_filename[base_filename.size() - 1]);
|
||||
|
||||
shared_ptr<VersionedMap> vm;
|
||||
if (decompressed_data) {
|
||||
@@ -2773,7 +2779,7 @@ MapIndex::MapIndex(const string& directory) {
|
||||
static_game_data_log.debug_f("({}) Created Episode 3 map {:08X} {} ({}; {})",
|
||||
filename,
|
||||
vm->map->map_number,
|
||||
char_for_language_code(vm->language),
|
||||
char_for_language(vm->language),
|
||||
vm->map->is_quest() ? "quest" : "free",
|
||||
name);
|
||||
} else {
|
||||
@@ -2781,7 +2787,7 @@ MapIndex::MapIndex(const string& directory) {
|
||||
static_game_data_log.debug_f("({}) Added Episode 3 map version {:08X} {} ({}; {})",
|
||||
filename,
|
||||
vm->map->map_number,
|
||||
char_for_language_code(vm->language),
|
||||
char_for_language(vm->language),
|
||||
vm->map->is_quest() ? "quest" : "free",
|
||||
name);
|
||||
}
|
||||
@@ -2794,7 +2800,7 @@ MapIndex::MapIndex(const string& directory) {
|
||||
}
|
||||
}
|
||||
|
||||
const string& MapIndex::get_compressed_list(size_t num_players, uint8_t language) const {
|
||||
const string& MapIndex::get_compressed_list(size_t num_players, Language language) const {
|
||||
if (num_players == 0) {
|
||||
throw runtime_error("cannot generate map list for no players");
|
||||
}
|
||||
@@ -2802,10 +2808,11 @@ const string& MapIndex::get_compressed_list(size_t num_players, uint8_t language
|
||||
throw logic_error("player count is too high in map list generation");
|
||||
}
|
||||
|
||||
if (language >= this->compressed_map_lists.size()) {
|
||||
this->compressed_map_lists.resize(language + 1);
|
||||
size_t lang_index = static_cast<size_t>(language);
|
||||
if (lang_index >= this->compressed_map_lists.size()) {
|
||||
this->compressed_map_lists.resize(lang_index + 1);
|
||||
}
|
||||
string& compressed_map_list = this->compressed_map_lists[language].at(num_players - 1);
|
||||
string& compressed_map_list = this->compressed_map_lists[lang_index].at(num_players - 1);
|
||||
if (compressed_map_list.empty()) {
|
||||
phosg::StringWriter entries_w;
|
||||
phosg::StringWriter strings_w;
|
||||
|
||||
+11
-11
@@ -1315,7 +1315,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
/* 00 */ pstring<TextEncoding::MARKED, 0x18> deck_name;
|
||||
/* 18 */ parray<be_uint16_t, 0x20> card_ids; // Last one appears to always be FFFF
|
||||
/* 58 */
|
||||
phosg::JSON json(uint8_t language) const;
|
||||
phosg::JSON json(Language language) const;
|
||||
} __packed_ws__(NPCDeck, 0x58);
|
||||
/* 1FE8 */ parray<NPCDeck, 3> npc_decks; // Unused if name[0] == 0
|
||||
|
||||
@@ -1331,7 +1331,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
// TODO: Figure out exactly how these are used and document here.
|
||||
/* 0018 */ parray<be_uint16_t, 0x7E> params;
|
||||
/* 0114 */
|
||||
phosg::JSON json(uint8_t language) const;
|
||||
phosg::JSON json(Language language) const;
|
||||
} __packed_ws__(AIParams, 0x114);
|
||||
/* 20F0 */ parray<AIParams, 3> npc_ai_params; // Unused if name[0] == 0
|
||||
|
||||
@@ -1384,7 +1384,7 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
// strings, excluding any that are empty or begin with the character '^'.
|
||||
/* 0004 */ parray<pstring<TextEncoding::MARKED, 0x40>, 4> strings;
|
||||
/* 0104 */
|
||||
phosg::JSON json(uint8_t language) const;
|
||||
phosg::JSON json(Language language) const;
|
||||
} __packed_ws__(DialogueSet, 0x104);
|
||||
|
||||
// There are up to 0x10 of these per valid NPC, but only the first 13 of them
|
||||
@@ -1490,8 +1490,8 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
|
||||
// text may differ.
|
||||
void assert_semantically_equivalent(const MapDefinition& other) const;
|
||||
|
||||
std::string str(const CardIndex* card_index, uint8_t language) const;
|
||||
phosg::JSON json(uint8_t language) const;
|
||||
std::string str(const CardIndex* card_index, Language language) const;
|
||||
phosg::JSON json(Language language) const;
|
||||
} __packed_ws__(MapDefinition, 0x5A18);
|
||||
|
||||
struct MapDefinitionTrial {
|
||||
@@ -1592,10 +1592,10 @@ public:
|
||||
class VersionedMap {
|
||||
public:
|
||||
std::shared_ptr<const MapDefinition> map;
|
||||
uint8_t language;
|
||||
Language language;
|
||||
|
||||
VersionedMap(std::shared_ptr<const MapDefinition> map, uint8_t language);
|
||||
VersionedMap(std::string&& compressed_data, uint8_t language);
|
||||
VersionedMap(std::shared_ptr<const MapDefinition> map, Language language);
|
||||
VersionedMap(std::string&& compressed_data, Language language);
|
||||
|
||||
std::shared_ptr<const MapDefinitionTrial> trial() const;
|
||||
std::shared_ptr<const std::string> compressed(bool trial) const;
|
||||
@@ -1616,8 +1616,8 @@ public:
|
||||
explicit Map(std::shared_ptr<const VersionedMap> initial_version);
|
||||
|
||||
void add_version(std::shared_ptr<const VersionedMap> vm);
|
||||
bool has_version(uint8_t language) const;
|
||||
std::shared_ptr<const VersionedMap> version(uint8_t language) const;
|
||||
bool has_version(Language language) const;
|
||||
std::shared_ptr<const VersionedMap> version(Language language) const;
|
||||
inline const std::vector<std::shared_ptr<const VersionedMap>>& all_versions() const {
|
||||
return this->versions;
|
||||
}
|
||||
@@ -1626,7 +1626,7 @@ public:
|
||||
std::vector<std::shared_ptr<const VersionedMap>> versions;
|
||||
};
|
||||
|
||||
const std::string& get_compressed_list(size_t num_players, uint8_t language) const;
|
||||
const std::string& get_compressed_list(size_t num_players, Language language) const;
|
||||
inline std::shared_ptr<const Map> get(uint32_t id) const {
|
||||
return this->maps.at(id);
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ void DeckState::print(FILE* stream, std::shared_ptr<const CardIndex> card_index)
|
||||
}
|
||||
}
|
||||
if (ce) {
|
||||
string name = ce->def.en_name.decode(1);
|
||||
string name = ce->def.en_name.decode(Language::ENGLISH);
|
||||
phosg::fwrite_fmt(stream, " ({:02}) index={:02X} ref=@{:04X} card_id=#{:04X} \"{}\" {}\n",
|
||||
z, e.deck_index, this->card_refs[z], e.card_id, name, name_for_card_state(e.state));
|
||||
} else {
|
||||
|
||||
+15
-14
@@ -274,14 +274,14 @@ void Server::send_6xB4x46() const {
|
||||
// NTE doesn't have the date_str2 field, but we send it anyway to make
|
||||
// debugging easier.
|
||||
G_ServerVersionStrings_Ep3_6xB4x46 cmd;
|
||||
cmd.version_signature.encode(this->options.is_nte() ? VERSION_SIGNATURE_NTE : VERSION_SIGNATURE, 1);
|
||||
cmd.date_str1.encode(std::format("Card definitions: {:016X}", this->options.card_index->definitions_hash()), 1);
|
||||
cmd.version_signature.encode(this->options.is_nte() ? VERSION_SIGNATURE_NTE : VERSION_SIGNATURE, Language::ENGLISH);
|
||||
cmd.date_str1.encode(std::format("Card definitions: {:016X}", this->options.card_index->definitions_hash()), Language::ENGLISH);
|
||||
string build_date = phosg::format_time(BUILD_TIMESTAMP);
|
||||
cmd.date_str2.encode(std::format("newserv {} compiled at {}", GIT_REVISION_HASH, build_date), 1);
|
||||
cmd.date_str2.encode(std::format("newserv {} compiled at {}", GIT_REVISION_HASH, build_date), Language::ENGLISH);
|
||||
this->send(cmd);
|
||||
}
|
||||
|
||||
string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::Map> map, uint8_t language, bool is_nte) {
|
||||
string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::Map> map, Language language, bool is_nte) {
|
||||
auto vm = map->version(language);
|
||||
|
||||
const auto& compressed = vm->compressed(is_nte);
|
||||
@@ -306,7 +306,7 @@ void Server::send_commands_for_joining_spectator(std::shared_ptr<Channel> ch) co
|
||||
|
||||
if (this->last_chosen_map) {
|
||||
string data = this->prepare_6xB6x41_map_definition(this->last_chosen_map, ch->language, this->options.is_nte());
|
||||
this->log().info_f("Sending {} version of map {:08X}", char_for_language_code(ch->language), this->last_chosen_map->map_number);
|
||||
this->log().info_f("Sending {} version of map {:08X}", name_for_language(ch->language), this->last_chosen_map->map_number);
|
||||
ch->send(0x6C, 0x00, data);
|
||||
}
|
||||
|
||||
@@ -2137,7 +2137,7 @@ void Server::handle_CAx13_update_map_during_setup_t(shared_ptr<Client> c, const
|
||||
// in the case of NTE, no values at all, since the Rules structure is
|
||||
// smaller). So, use the values from the last chosen map if applicable, or
|
||||
// the values from the $dicerange command if available.
|
||||
uint8_t language = c ? c->language() : 1;
|
||||
Language language = c ? c->language() : Language::ENGLISH;
|
||||
const Rules* map_rules = this->last_chosen_map ? &this->last_chosen_map->version(language)->map->default_rules : nullptr;
|
||||
auto& server_rules = this->map_and_rules->rules;
|
||||
// NTE can specify the DEF dice value range in its Rules struct, so we use
|
||||
@@ -2526,7 +2526,7 @@ void Server::handle_CAx40_map_list_request(shared_ptr<Client> sender_c, const st
|
||||
}
|
||||
|
||||
size_t num_players = l ? l->count_clients() : 1;
|
||||
uint8_t language = sender_c ? sender_c->language() : 1;
|
||||
Language language = sender_c ? sender_c->language() : Language::ENGLISH;
|
||||
const auto& list_data = this->options.map_index->get_compressed_list(num_players, language);
|
||||
|
||||
phosg::StringWriter w;
|
||||
@@ -2553,15 +2553,16 @@ void Server::send_6xB6x41_to_all_clients() const {
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
if (map_commands_by_language.size() <= c->language()) {
|
||||
map_commands_by_language.resize(c->language() + 1);
|
||||
size_t lang_index = static_cast<size_t>(c->language());
|
||||
if (map_commands_by_language.size() <= lang_index) {
|
||||
map_commands_by_language.resize(lang_index + 1);
|
||||
}
|
||||
if (map_commands_by_language[c->language()].empty()) {
|
||||
map_commands_by_language[c->language()] = this->prepare_6xB6x41_map_definition(
|
||||
if (map_commands_by_language[lang_index].empty()) {
|
||||
map_commands_by_language[lang_index] = this->prepare_6xB6x41_map_definition(
|
||||
this->last_chosen_map, c->language(), this->options.is_nte());
|
||||
}
|
||||
this->log().info_f("Sending {} version of map {:08X}", char_for_language_code(c->language()), this->last_chosen_map->map_number);
|
||||
send_command(c, 0x6C, 0x00, map_commands_by_language[c->language()]);
|
||||
this->log().info_f("Sending {} version of map {:08X}", name_for_language(c->language()), this->last_chosen_map->map_number);
|
||||
send_command(c, 0x6C, 0x00, map_commands_by_language[lang_index]);
|
||||
};
|
||||
for (const auto& c : l->clients) {
|
||||
send_to_client(c);
|
||||
@@ -2586,7 +2587,7 @@ void Server::send_6xB6x41_to_all_clients() const {
|
||||
}
|
||||
|
||||
} else {
|
||||
auto out_data = this->prepare_6xB6x41_map_definition(this->last_chosen_map, 1, false);
|
||||
auto out_data = this->prepare_6xB6x41_map_definition(this->last_chosen_map, Language::ENGLISH, false);
|
||||
this->send(out_data.data(), out_data.size(), 0x6C, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ public:
|
||||
|
||||
G_UpdateDecks_Ep3_6xB4x07 prepare_6xB4x07_decks_update() const;
|
||||
G_SetPlayerNames_Ep3_6xB4x1C prepare_6xB4x1C_names_update() const;
|
||||
static std::string prepare_6xB6x41_map_definition(std::shared_ptr<const MapIndex::Map> map, uint8_t language, bool is_nte);
|
||||
static std::string prepare_6xB6x41_map_definition(std::shared_ptr<const MapIndex::Map> map, Language language, bool is_nte);
|
||||
void send_6xB6x41_to_all_clients() const;
|
||||
G_SetTrapTileLocations_Ep3_6xB4x50 prepare_6xB4x50_trap_tile_locations() const;
|
||||
|
||||
|
||||
@@ -737,7 +737,7 @@ string Tournament::bracket_str() const {
|
||||
}
|
||||
};
|
||||
|
||||
auto en_vm = this->map->version(1);
|
||||
auto en_vm = this->map->version(Language::ENGLISH);
|
||||
if (en_vm) {
|
||||
string map_name = en_vm->map->name.decode(en_vm->language);
|
||||
ret += std::format(" Map: {:08X} ({})\n", this->map->map_number, map_name);
|
||||
|
||||
+1
-1
@@ -128,7 +128,7 @@ shared_ptr<Client> GameServer::create_client(shared_ptr<GameServerSocket> listen
|
||||
this->io_context,
|
||||
make_unique<asio::ip::tcp::socket>(std::move(client_sock)),
|
||||
listen_sock->version,
|
||||
1,
|
||||
Language::ENGLISH,
|
||||
"",
|
||||
phosg::TerminalFormat::FG_YELLOW,
|
||||
phosg::TerminalFormat::FG_GREEN);
|
||||
|
||||
+6
-6
@@ -132,7 +132,7 @@ std::shared_ptr<phosg::JSON> HTTPServer::generate_client_json(
|
||||
{"RemoteAddress", c->channel->default_name()},
|
||||
{"Version", phosg::name_for_enum(c->version())},
|
||||
{"SubVersion", c->sub_version},
|
||||
{"Language", name_for_language_code(c->language())},
|
||||
{"Language", name_for_language(c->language())},
|
||||
{"LocationX", c->pos.x.load()},
|
||||
{"LocationZ", c->pos.z.load()},
|
||||
{"LocationFloor", c->floor},
|
||||
@@ -173,7 +173,7 @@ std::shared_ptr<phosg::JSON> HTTPServer::generate_client_json(
|
||||
if (p) {
|
||||
if (!is_ep3(c->version())) {
|
||||
if (c->version() != Version::DC_NTE) {
|
||||
ret->emplace("InventoryLanguage", name_for_language_code(p->inventory.language));
|
||||
ret->emplace("InventoryLanguage", name_for_language(p->inventory.language));
|
||||
ret->emplace("NumHPMaterialsUsed", p->get_material_usage(PSOBBCharacterFile::MaterialType::HP));
|
||||
ret->emplace("NumTPMaterialsUsed", p->get_material_usage(PSOBBCharacterFile::MaterialType::TP));
|
||||
if (!is_v1_or_v2(c->version())) {
|
||||
@@ -302,7 +302,7 @@ std::shared_ptr<phosg::JSON> HTTPServer::generate_client_json(
|
||||
lobby_players_json.emplace_back(phosg::JSON::dict({
|
||||
{"GuildCardNumber", p.guild_card_number},
|
||||
{"Name", p.name},
|
||||
{"Language", name_for_language_code(p.language)},
|
||||
{"Language", name_for_language(p.language)},
|
||||
{"SectionID", name_for_section_id(p.section_id)},
|
||||
{"CharClass", name_for_char_class(p.char_class)},
|
||||
}));
|
||||
@@ -481,7 +481,7 @@ std::shared_ptr<phosg::JSON> HTTPServer::generate_lobby_json(
|
||||
}
|
||||
}
|
||||
deck_json = phosg::JSON::dict({
|
||||
{"Name", deck_entry->name.decode(lc ? lc->language() : 1)},
|
||||
{"Name", deck_entry->name.decode(lc ? lc->language() : Language::ENGLISH)},
|
||||
{"TeamID", deck_entry->team_id.load()},
|
||||
{"Cards", std::move(cards_json)},
|
||||
{"GodWhimFlag", deck_entry->god_whim_flag},
|
||||
@@ -490,7 +490,7 @@ std::shared_ptr<phosg::JSON> HTTPServer::generate_lobby_json(
|
||||
}
|
||||
|
||||
auto player_json = phosg::JSON::dict({
|
||||
{"PlayerName", ep3s->name_entries[z].name.decode(lc ? lc->language() : 1)},
|
||||
{"PlayerName", ep3s->name_entries[z].name.decode(lc ? lc->language() : Language::ENGLISH)},
|
||||
{"ClientID", ep3s->name_entries[z].client_id},
|
||||
{"IsCOM", !!ep3s->name_entries[z].is_cpu_player},
|
||||
{"Deck", std::move(deck_json)},
|
||||
@@ -608,7 +608,7 @@ std::shared_ptr<phosg::JSON> HTTPServer::generate_summary_json() const {
|
||||
{"AccountID", c->login ? c->login->account->account_id : phosg::JSON(nullptr)},
|
||||
{"Name", p ? p->disp.name.decode(c->language()) : phosg::JSON(nullptr)},
|
||||
{"Version", phosg::name_for_enum(c->version())},
|
||||
{"Language", name_for_language_code(c->language())},
|
||||
{"Language", name_for_language(c->language())},
|
||||
{"Level", p ? p->disp.stats.level + 1 : phosg::JSON(nullptr)},
|
||||
{"Class", p ? name_for_char_class(p->disp.visual.char_class) : phosg::JSON(nullptr)},
|
||||
{"SectionID", p ? name_for_section_id(p->disp.visual.section_id) : phosg::JSON(nullptr)},
|
||||
|
||||
@@ -166,7 +166,7 @@ IPSSChannel::IPSSChannel(
|
||||
std::weak_ptr<IPSSClient> ipss_client,
|
||||
std::weak_ptr<IPSSClient::TCPConnection> tcp_conn,
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const std::string& name,
|
||||
phosg::TerminalFormat terminal_send_color,
|
||||
phosg::TerminalFormat terminal_recv_color)
|
||||
@@ -1396,7 +1396,7 @@ asio::awaitable<void> IPStackSimulator::open_server_connection(
|
||||
}
|
||||
const auto& port_config = port_config_it->second;
|
||||
|
||||
conn->server_channel = make_shared<IPSSChannel>(this->shared_from_this(), c, conn, port_config->version, 1);
|
||||
conn->server_channel = make_shared<IPSSChannel>(this->shared_from_this(), c, conn, port_config->version, Language::ENGLISH);
|
||||
|
||||
if (!this->state->game_server.get()) {
|
||||
this->log.error_f("No server available for TCP connection {}", conn_str);
|
||||
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
std::weak_ptr<IPSSClient> ipss_client,
|
||||
std::weak_ptr<IPSSClient::TCPConnection> tcp_conn,
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const std::string& name = "",
|
||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||
phosg::TerminalFormat terminal_recv_color = phosg::TerminalFormat::END);
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ string encode_gvm(const phosg::ImageRGBA8888N& img, GVRDataFormat data_format, c
|
||||
w.put<GVMFileHeader>({.signature = 0x47564D48, .header_size = 0x48, .flags = 0x000F, .num_files = 1});
|
||||
GVMFileEntry file_entry;
|
||||
file_entry.file_num = 0;
|
||||
file_entry.name.encode(internal_name, 1);
|
||||
file_entry.name.encode(internal_name, Language::ENGLISH);
|
||||
file_entry.data_format = data_format;
|
||||
file_entry.format_flags = 0;
|
||||
file_entry.dimensions = (dimensions_field << 4) | dimensions_field;
|
||||
|
||||
+4
-4
@@ -32,7 +32,7 @@ ItemCreator::ItemCreator(
|
||||
std::shared_ptr<const ItemData::StackLimits> stack_limits,
|
||||
Episode episode,
|
||||
GameMode mode,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
uint8_t section_id,
|
||||
std::shared_ptr<RandomGenerator> rand_crypt,
|
||||
shared_ptr<const BattleRules> restrictions)
|
||||
@@ -1072,7 +1072,7 @@ void ItemCreator::generate_armor_shop_armors(vector<ItemData>& shop, size_t play
|
||||
item.data1[1] = 1;
|
||||
item.data1[2] = pt.pop();
|
||||
|
||||
if ((this->difficulty == 3) && (player_level > 99)) {
|
||||
if ((this->difficulty == Difficulty::ULTIMATE) && (player_level > 99)) {
|
||||
if (player_level > 150) {
|
||||
item.data1[2] += 3;
|
||||
} else if (player_level >= 100) {
|
||||
@@ -1116,7 +1116,7 @@ void ItemCreator::generate_armor_shop_shields(vector<ItemData>& shop, size_t pla
|
||||
item.data1[1] = 2;
|
||||
item.data1[2] = pt.pop();
|
||||
|
||||
if ((this->difficulty == 3) && (player_level > 99)) {
|
||||
if ((this->difficulty == Difficulty::ULTIMATE) && (player_level > 99)) {
|
||||
if (player_level > 150) {
|
||||
item.data1[2] += 3;
|
||||
} else if (player_level >= 100) {
|
||||
@@ -1360,7 +1360,7 @@ vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level)
|
||||
}
|
||||
|
||||
size_t table_index;
|
||||
if (this->difficulty == 3) {
|
||||
if (this->difficulty == Difficulty::ULTIMATE) {
|
||||
if (player_level < 11) {
|
||||
table_index = 0;
|
||||
} else if (player_level < 26) {
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@ public:
|
||||
std::shared_ptr<const ItemData::StackLimits> stack_limits,
|
||||
Episode episode,
|
||||
GameMode mode,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
uint8_t section_id,
|
||||
std::shared_ptr<RandomGenerator> rand_crypt,
|
||||
std::shared_ptr<const BattleRules> restrictions = nullptr);
|
||||
@@ -61,7 +61,7 @@ private:
|
||||
std::shared_ptr<const ItemData::StackLimits> stack_limits;
|
||||
Episode episode;
|
||||
GameMode mode;
|
||||
uint8_t difficulty;
|
||||
Difficulty difficulty;
|
||||
uint8_t section_id;
|
||||
std::shared_ptr<const RareItemSet> rare_item_set;
|
||||
std::shared_ptr<const ArmorRandomSet> armor_random_set;
|
||||
|
||||
+1
-1
@@ -220,7 +220,7 @@ void Lobby::create_item_creator(Version logic_version) {
|
||||
s->rare_item_set(logic_version, this->quest),
|
||||
s->armor_random_set,
|
||||
s->tool_random_set,
|
||||
s->weapon_random_sets.at(this->difficulty),
|
||||
s->weapon_random_set(this->difficulty),
|
||||
s->tekker_adjustment_set,
|
||||
s->item_parameter_table(logic_version),
|
||||
s->item_stack_limits(logic_version),
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
uint8_t override_section_id = 0xFF;
|
||||
Episode episode = Episode::NONE;
|
||||
GameMode mode = GameMode::NORMAL;
|
||||
uint8_t difficulty = 0; // 0-3
|
||||
Difficulty difficulty = Difficulty::NORMAL;
|
||||
float base_exp_multiplier = 1.0f;
|
||||
float exp_share_multiplier = 0.5f;
|
||||
float challenge_exp_multiplier = 1.0f;
|
||||
|
||||
+17
-21
@@ -1610,7 +1610,7 @@ Action a_disassemble_quest_script(
|
||||
if (!args.get<bool>("decompressed")) {
|
||||
data = prs_decompress(data);
|
||||
}
|
||||
uint8_t override_language = args.get<uint8_t>("language", 0xFF);
|
||||
Language override_language = static_cast<Language>(args.get<uint8_t>("language", 0xFF));
|
||||
bool reassembly_mode = args.get<bool>("reassembly");
|
||||
bool use_qedit_names = args.get<bool>("qedit");
|
||||
string result = disassemble_quest_script(data.data(), data.size(), version, override_language, reassembly_mode, use_qedit_names);
|
||||
@@ -2080,7 +2080,7 @@ Action a_download_files(
|
||||
remote_port,
|
||||
args.get<string>("output-dir", true),
|
||||
version,
|
||||
args.get<uint8_t>("language"),
|
||||
static_cast<Language>(args.get<uint8_t>("language")),
|
||||
key,
|
||||
phosg::random_object<uint32_t>(),
|
||||
serial_number,
|
||||
@@ -2173,12 +2173,10 @@ Action a_convert_rare_item_set(
|
||||
} else if (output_filename_lower.ends_with(".html")) {
|
||||
Version cli_version = get_cli_version(args, Version::BB_V4);
|
||||
bool is_v1 = ::is_v1(cli_version);
|
||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
for (GameMode mode : modes) {
|
||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (Episode episode : episodes) {
|
||||
for (size_t difficulty = 0; difficulty < (is_v1 ? 3 : 4); difficulty++) {
|
||||
if (!rs->has_entries_for_game_config(mode, episode, difficulty)) {
|
||||
for (GameMode mode : ALL_GAME_MODES_V4) {
|
||||
for (Episode episode : ALL_EPISODES_V4) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
if ((is_v1 && (difficulty == Difficulty::ULTIMATE)) || (!rs->has_entries_for_game_config(mode, episode, difficulty))) {
|
||||
continue;
|
||||
}
|
||||
auto item_name_index = s->item_name_index(cli_version);
|
||||
@@ -2594,7 +2592,7 @@ Action a_generate_ep3_cards_html(
|
||||
|
||||
shared_ptr<const TextSet> text_english;
|
||||
try {
|
||||
text_english = s->text_index->get(Version::GC_EP3, 1);
|
||||
text_english = s->text_index->get(Version::GC_EP3, Language::ENGLISH);
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
|
||||
@@ -2833,12 +2831,13 @@ Action a_show_ep3_maps(
|
||||
phosg::log_info_f("{} maps", map_ids.size());
|
||||
for (const auto& [map_number, map] : map_ids) {
|
||||
const auto& vms = map->all_versions();
|
||||
for (size_t language = 0; language < vms.size(); language++) {
|
||||
if (!vms[language]) {
|
||||
for (size_t lang_index = 0; lang_index < vms.size(); lang_index++) {
|
||||
if (!vms[lang_index]) {
|
||||
continue;
|
||||
}
|
||||
string map_s = vms[language]->map->str(s->ep3_card_index.get(), language);
|
||||
phosg::fwrite_fmt(stdout, "({}) {}\n", char_for_language_code(language), map_s);
|
||||
Language language = static_cast<Language>(lang_index);
|
||||
string map_s = vms[lang_index]->map->str(s->ep3_card_index.get(), language);
|
||||
phosg::fwrite_fmt(stdout, "({}) {}\n", char_for_language(language), map_s);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2891,7 +2890,7 @@ Action a_check_supermaps(
|
||||
for (const auto& it : s->supermap_for_free_play_key) {
|
||||
auto episode = static_cast<Episode>((it.first >> 28) & 7);
|
||||
auto mode = static_cast<GameMode>((it.first >> 26) & 3);
|
||||
uint8_t difficulty = (it.first >> 24) & 3;
|
||||
Difficulty difficulty = static_cast<Difficulty>((it.first >> 24) & 3);
|
||||
uint8_t floor = (it.first >> 16) & 0xFF;
|
||||
uint8_t layout = (it.first >> 8) & 0xFF;
|
||||
uint8_t entities = (it.first >> 0) & 0xFF;
|
||||
@@ -2924,12 +2923,9 @@ Action a_check_supermaps(
|
||||
|
||||
// Generate MapStates for a few random variations
|
||||
for (size_t z = 0; z < 0x20; z++) {
|
||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
|
||||
Episode episode = episodes[phosg::random_object<uint32_t>() % episodes.size()];
|
||||
GameMode mode = modes[phosg::random_object<uint32_t>() % modes.size()];
|
||||
uint8_t difficulty = phosg::random_object<uint32_t>() % 4;
|
||||
Episode episode = ALL_EPISODES_V4[phosg::random_object<uint32_t>() % ALL_EPISODES_V4.size()];
|
||||
GameMode mode = ALL_GAME_MODES_V4[phosg::random_object<uint32_t>() % ALL_GAME_MODES_V4.size()];
|
||||
Difficulty difficulty = static_cast<Difficulty>(phosg::random_object<uint32_t>() % 4);
|
||||
uint8_t event = phosg::random_object<uint32_t>() % 8;
|
||||
uint32_t random_seed = phosg::random_object<uint32_t>();
|
||||
phosg::fwrite_fmt(stderr, "FREE MAP STATE TEST: {} {} {}\n",
|
||||
@@ -3014,7 +3010,7 @@ Action a_check_supermaps(
|
||||
|
||||
auto map_state = make_shared<MapState>(
|
||||
0,
|
||||
phosg::random_object<uint8_t>() & 3,
|
||||
static_cast<Difficulty>(phosg::random_object<uint8_t>() & 3),
|
||||
0,
|
||||
phosg::random_object<uint32_t>(),
|
||||
MapState::DEFAULT_RARE_ENEMIES,
|
||||
|
||||
+20
-13
@@ -3398,7 +3398,7 @@ string MapFile::name_for_enemy_type(uint16_t type, Version version, uint8_t area
|
||||
|
||||
string MapFile::ObjectSetEntry::str(Version version, uint8_t area) const {
|
||||
string name_str = MapFile::name_for_object_type(this->base_type, version, area);
|
||||
return std::format("[ObjectSetEntry type={:04X} \"{}\" floor={:04X} group={:04X} room={:04X} a3={:04X} x={:g} y={:g} z={:g} x_angle={:08X} y_angle={:08X} z_angle={:08X} params=[{:g} {:g} {:g} {:08X} {:08X} {:08X}] unused={:08X}]",
|
||||
return std::format("[ObjectSetEntry type={:04X} \"{}\" floor={:04X} group={:04X} room={:04X} a3={:04X} x={:g} y={:g} z={:g} x_angle={:08X} y_angle={:08X} z_angle={:08X} params=[{:g} {:g} {:g} {:08X} {:08X} {:08X}]]",
|
||||
this->base_type,
|
||||
name_str,
|
||||
this->floor,
|
||||
@@ -3416,8 +3416,7 @@ string MapFile::ObjectSetEntry::str(Version version, uint8_t area) const {
|
||||
this->param3,
|
||||
this->param4,
|
||||
this->param5,
|
||||
this->param6,
|
||||
this->unused);
|
||||
this->param6);
|
||||
}
|
||||
|
||||
uint64_t MapFile::ObjectSetEntry::semantic_hash(uint8_t floor) const {
|
||||
@@ -3438,7 +3437,7 @@ uint64_t MapFile::ObjectSetEntry::semantic_hash(uint8_t floor) const {
|
||||
|
||||
string MapFile::EnemySetEntry::str(Version version, uint8_t area) const {
|
||||
auto type_name = MapFile::name_for_enemy_type(this->base_type, version, area);
|
||||
return std::format("[EnemySetEntry type={:04X} \"{}\" num_children={:04X} floor={:04X} room={:04X} wave_number={:04X} wave_number2={:04X} a1={:04X} x={:g} y={:g} z={:g} x_angle={:08X} y_angle={:08X} z_angle={:08X} params=[{:g} {:g} {:g} {:g} {:g} {:04X} {:04X}] unused={:08X}]",
|
||||
return std::format("[EnemySetEntry type={:04X} \"{}\" num_children={:04X} floor={:04X} room={:04X} wave_number={:04X} wave_number2={:04X} a1={:04X} x={:g} y={:g} z={:g} x_angle={:08X} y_angle={:08X} z_angle={:08X} params=[{:g} {:g} {:g} {:g} {:g} {:04X} {:04X}]]",
|
||||
this->base_type,
|
||||
type_name,
|
||||
this->num_children,
|
||||
@@ -3459,8 +3458,7 @@ string MapFile::EnemySetEntry::str(Version version, uint8_t area) const {
|
||||
this->param4,
|
||||
this->param5,
|
||||
this->param6,
|
||||
this->param7,
|
||||
this->unused);
|
||||
this->param7);
|
||||
}
|
||||
|
||||
uint64_t MapFile::EnemySetEntry::semantic_hash(uint8_t floor) const {
|
||||
@@ -5951,7 +5949,7 @@ size_t MapState::EventIterator::num_entities_on_current_floor() const {
|
||||
|
||||
MapState::MapState(
|
||||
uint64_t lobby_or_session_id,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
uint8_t event,
|
||||
uint32_t random_seed,
|
||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
||||
@@ -6001,7 +5999,7 @@ MapState::MapState(
|
||||
|
||||
MapState::MapState(
|
||||
uint64_t lobby_or_session_id,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
uint8_t event,
|
||||
uint32_t random_seed,
|
||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
||||
@@ -6048,6 +6046,12 @@ void MapState::index_super_map(const FloorConfig& fc, shared_ptr<RandomGenerator
|
||||
|
||||
for (const auto& ene : fc.super_map->all_enemies()) {
|
||||
auto& ene_st = this->enemy_states.emplace_back(make_shared<EnemyState>());
|
||||
if (ene->alias_enemy_index_delta) {
|
||||
ene_st->alias_ene_st = this->enemy_states.at((this->enemy_states.size() - 1) + ene->alias_enemy_index_delta);
|
||||
if (ene_st->alias_ene_st->alias_ene_st) {
|
||||
throw std::runtime_error("target for enemy state alias is itself an alias");
|
||||
}
|
||||
}
|
||||
if (ene->child_index == 0) {
|
||||
this->enemy_set_states.emplace_back(ene_st);
|
||||
}
|
||||
@@ -6059,12 +6063,12 @@ void MapState::index_super_map(const FloorConfig& fc, shared_ptr<RandomGenerator
|
||||
EnemyType type;
|
||||
switch (ene->type) {
|
||||
case EnemyType::DARK_FALZ_3:
|
||||
type = ((this->difficulty == 0) && (ene->alias_enemy_index_delta == 0))
|
||||
type = ((this->difficulty == Difficulty::NORMAL) && (ene->alias_enemy_index_delta == 0))
|
||||
? EnemyType::DARK_FALZ_2
|
||||
: EnemyType::DARK_FALZ_3;
|
||||
break;
|
||||
case EnemyType::DARVANT:
|
||||
type = (this->difficulty == 3) ? EnemyType::DARVANT_ULTIMATE : EnemyType::DARVANT;
|
||||
type = (this->difficulty == Difficulty::ULTIMATE) ? EnemyType::DARVANT_ULTIMATE : EnemyType::DARVANT;
|
||||
break;
|
||||
default:
|
||||
type = ene->type;
|
||||
@@ -6687,7 +6691,7 @@ void MapState::print(FILE* stream) const {
|
||||
phosg::fwrite_fmt(stream, "BB rare rates: {}\n", rare_rates_str);
|
||||
|
||||
phosg::fwrite_fmt(stream, "Base indexes:\n");
|
||||
phosg::fwrite_fmt(stream, " FL DCTE----------- DCPR----------- DCV1----------- DCV2----------- PCTE----------- PCV2----------- GCTE----------- GCV3----------- GCEP3TE-------- GCEP3---------- XBV3----------- BBV4-----------\n");
|
||||
phosg::fwrite_fmt(stream, " FL DC-NTE--------- DC-11-2000----- DC-V1---------- DC-V2---------- PC-NTE--------- PC-V2---------- GC-NTE--------- GC-V3---------- GC-EP3-NTE----- GC-EP3--------- XB-V3---------- BB-V4----------\n");
|
||||
phosg::fwrite_fmt(stream, " FL KST EST ESS EVT KST EST ESS EVT KST EST ESS EVT KST EST ESS EVT KST EST ESS EVT KST EST ESS EVT KST EST ESS EVT KST EST ESS EVT KST EST ESS EVT KST EST ESS EVT KST EST ESS EVT KST EST ESS EVT\n");
|
||||
for (size_t floor = 0; floor < this->floor_config_entries.size(); floor++) {
|
||||
auto fc = this->floor_config_entries[floor];
|
||||
@@ -6723,9 +6727,12 @@ void MapState::print(FILE* stream) const {
|
||||
}
|
||||
|
||||
phosg::fwrite_fmt(stream, "Enemies:\n");
|
||||
phosg::fwrite_fmt(stream, " FL ENEID DCTE----- DCPR----- DCV1----- DCV2----- PCTE----- PCV2----- GCTE----- GCV3----- EP3TE---- GCEP3---- XBV3----- BBV4----- ENEMY\n");
|
||||
phosg::fwrite_fmt(stream, " FL ENEID ALIAS DCTE----- DCPR----- DCV1----- DCV2----- PCTE----- PCV2----- GCTE----- GCV3----- EP3TE---- GCEP3---- XBV3----- BBV4----- ENEMY\n");
|
||||
for (const auto& ene_st : this->enemy_states) {
|
||||
phosg::fwrite_fmt(stream, " {:02X} E-{:03X}", ene_st->super_ene->floor, ene_st->e_id);
|
||||
std::string alias_str = ene_st->super_ene->alias_enemy_index_delta
|
||||
? std::format("E-{:03X}", ene_st->e_id + ene_st->super_ene->alias_enemy_index_delta)
|
||||
: "-----";
|
||||
phosg::fwrite_fmt(stream, " {:02X} E-{:03X} {}", ene_st->super_ene->floor, ene_st->e_id, alias_str);
|
||||
const auto& fc = this->floor_config(ene_st->super_ene->floor);
|
||||
for (Version v : ALL_NON_PATCH_VERSIONS) {
|
||||
const auto& ene_v = ene_st->super_ene->version(v);
|
||||
|
||||
+11
-6
@@ -187,7 +187,7 @@ public:
|
||||
/* 34 */ le_int32_t param4 = 0;
|
||||
/* 38 */ le_int32_t param5 = 0;
|
||||
/* 3C */ le_int32_t param6 = 0;
|
||||
/* 40 */ le_uint32_t unused = 0; // Reserved for pointer in client's memory; unused by server
|
||||
/* 40 */ le_uint32_t unused_obj_ptr = 0; // Reserved for pointer in client's memory; unused by server
|
||||
/* 44 */
|
||||
|
||||
uint64_t semantic_hash(uint8_t floor) const;
|
||||
@@ -215,7 +215,7 @@ public:
|
||||
/* 3C */ le_float param5 = 0.0f;
|
||||
/* 40 */ le_int16_t param6 = 0;
|
||||
/* 42 */ le_int16_t param7 = 0;
|
||||
/* 44 */ le_uint32_t unused = 0; // Reserved for pointer in client's memory; unused by server
|
||||
/* 44 */ le_uint32_t unused_obj_ptr = 0; // Reserved for pointer in client's memory; unused by server
|
||||
/* 48 */
|
||||
|
||||
uint64_t semantic_hash(uint8_t floor) const;
|
||||
@@ -710,6 +710,7 @@ public:
|
||||
};
|
||||
|
||||
struct EnemyState {
|
||||
std::shared_ptr<EnemyState> alias_ene_st; // Null for most enemies
|
||||
std::shared_ptr<const SuperMap::Enemy> super_ene;
|
||||
enum Flag {
|
||||
LAST_HIT_MASK = 0x0003,
|
||||
@@ -745,7 +746,7 @@ public:
|
||||
inline void set_mericarand_variant_flag(Version version) {
|
||||
this->mericarand_variant_flags |= (1 << static_cast<size_t>(version));
|
||||
}
|
||||
inline EnemyType type(Version version, Episode episode, uint8_t event) const {
|
||||
inline EnemyType type(Version version, Episode episode, Difficulty difficulty, uint8_t event) const {
|
||||
if (this->super_ene->type == EnemyType::MERICARAND) {
|
||||
if (this->is_rare(version)) {
|
||||
return ((this->mericarand_variant_flags >> static_cast<size_t>(version)) & 1)
|
||||
@@ -754,6 +755,10 @@ public:
|
||||
} else {
|
||||
return EnemyType::MERICAROL;
|
||||
}
|
||||
} else if (this->super_ene->type == EnemyType::DARK_FALZ_3) {
|
||||
return ((difficulty == Difficulty::NORMAL) && (this->super_ene->alias_enemy_index_delta == 0))
|
||||
? EnemyType::DARK_FALZ_2
|
||||
: EnemyType::DARK_FALZ_3;
|
||||
} else {
|
||||
return this->is_rare(version)
|
||||
? type_definition_for_enemy(this->super_ene->type).rare_type(episode, event, this->super_ene->floor)
|
||||
@@ -874,7 +879,7 @@ public:
|
||||
|
||||
phosg::PrefixedLogger log;
|
||||
std::vector<FloorConfig> floor_config_entries;
|
||||
uint8_t difficulty = 0;
|
||||
Difficulty difficulty = Difficulty::NORMAL;
|
||||
uint8_t event = 0;
|
||||
uint32_t random_seed = 0;
|
||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates;
|
||||
@@ -889,7 +894,7 @@ public:
|
||||
// Constructor for free play
|
||||
MapState(
|
||||
uint64_t lobby_or_session_id,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
uint8_t event,
|
||||
uint32_t random_seed, // For client-matched rare enemies (non-BB)
|
||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
||||
@@ -898,7 +903,7 @@ public:
|
||||
// Constructor for quests
|
||||
MapState(
|
||||
uint64_t lobby_or_session_id,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
uint8_t event,
|
||||
uint32_t random_seed, // For client-matched rare enemies (non-BB)
|
||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
||||
|
||||
@@ -61,7 +61,7 @@ asio::awaitable<void> PatchDownloadSession::run() {
|
||||
this->io_context,
|
||||
std::move(sock),
|
||||
this->version,
|
||||
1,
|
||||
Language::ENGLISH,
|
||||
netloc_str,
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END,
|
||||
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : phosg::TerminalFormat::END);
|
||||
|
||||
@@ -116,7 +116,7 @@ struct PlayerInventoryT {
|
||||
/* 0000 */ uint8_t num_items = 0;
|
||||
/* 0001 */ uint8_t hp_from_materials = 0;
|
||||
/* 0002 */ uint8_t tp_from_materials = 0;
|
||||
/* 0003 */ uint8_t language = 0;
|
||||
/* 0003 */ Language language = Language::JAPANESE;
|
||||
/* 0004 */ parray<PlayerInventoryItemT<BE>, 30> items;
|
||||
/* 034C */
|
||||
|
||||
@@ -265,14 +265,14 @@ struct PlayerInventoryT {
|
||||
// issue - its inventory format matches the rest of the versions.
|
||||
this->hp_from_materials = 0;
|
||||
this->tp_from_materials = 0;
|
||||
this->language = 0;
|
||||
this->language = Language::JAPANESE;
|
||||
} else if ((v != Version::PC_NTE) && (v != Version::PC_V2)) {
|
||||
if (this->language > 4) {
|
||||
this->language = 0;
|
||||
if (static_cast<size_t>(this->language) > 4) {
|
||||
this->language = Language::JAPANESE;
|
||||
}
|
||||
} else {
|
||||
if (this->language > 7) {
|
||||
this->language = 0;
|
||||
if (static_cast<size_t>(this->language) > 7) {
|
||||
this->language = Language::JAPANESE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ void GuildCardBB::clear() {
|
||||
this->team_name.clear();
|
||||
this->description.clear();
|
||||
this->present = 0;
|
||||
this->language = 0;
|
||||
this->language = Language::JAPANESE;
|
||||
this->section_id = 0;
|
||||
this->char_class = 0;
|
||||
}
|
||||
@@ -218,11 +218,11 @@ PlayerRecordsChallengeBB::PlayerRecordsChallengeBB(const PlayerRecordsChallengeD
|
||||
grave_x(rec.grave_x),
|
||||
grave_y(rec.grave_y),
|
||||
grave_z(rec.grave_z),
|
||||
grave_team(rec.grave_team.decode(), 1),
|
||||
grave_message(rec.grave_message.decode(), 1),
|
||||
grave_team(rec.grave_team.decode(), Language::ENGLISH),
|
||||
grave_message(rec.grave_message.decode(), Language::ENGLISH),
|
||||
unknown_m5(0),
|
||||
unknown_t6(0),
|
||||
rank_title(rec.rank_title.decode(), 1),
|
||||
rank_title(rec.rank_title.decode(), Language::ENGLISH),
|
||||
unknown_l7(0) {}
|
||||
|
||||
PlayerRecordsChallengeBB::PlayerRecordsChallengeBB(const PlayerRecordsChallengePC& rec)
|
||||
@@ -242,11 +242,11 @@ PlayerRecordsChallengeBB::PlayerRecordsChallengeBB(const PlayerRecordsChallengeP
|
||||
grave_x(rec.grave_x),
|
||||
grave_y(rec.grave_y),
|
||||
grave_z(rec.grave_z),
|
||||
grave_team(rec.grave_team.decode(), 1),
|
||||
grave_message(rec.grave_message.decode(), 1),
|
||||
grave_team(rec.grave_team.decode(), Language::ENGLISH),
|
||||
grave_message(rec.grave_message.decode(), Language::ENGLISH),
|
||||
unknown_m5(0),
|
||||
unknown_t6(0),
|
||||
rank_title(rec.rank_title.decode(), 1),
|
||||
rank_title(rec.rank_title.decode(), Language::ENGLISH),
|
||||
unknown_l7(0) {}
|
||||
|
||||
PlayerRecordsChallengeBB::operator PlayerRecordsChallengeDC() const {
|
||||
|
||||
+37
-30
@@ -328,7 +328,7 @@ struct PlayerDispDataDCPCV3T {
|
||||
this->visual.enforce_lobby_join_limits_for_version(v);
|
||||
}
|
||||
|
||||
PlayerDispDataBB to_bb(uint8_t to_language, uint8_t from_language) const;
|
||||
PlayerDispDataBB to_bb(Language to_language, Language from_language) const;
|
||||
} __attribute__((packed));
|
||||
using PlayerDispDataDCPCV3 = PlayerDispDataDCPCV3T<false>;
|
||||
using PlayerDispDataDCPCV3BE = PlayerDispDataDCPCV3T<true>;
|
||||
@@ -361,7 +361,7 @@ struct PlayerDispDataBB {
|
||||
}
|
||||
|
||||
template <bool BE>
|
||||
PlayerDispDataDCPCV3T<BE> to_dcpcv3(uint8_t to_language, uint8_t from_language) const {
|
||||
PlayerDispDataDCPCV3T<BE> to_dcpcv3(Language to_language, Language from_language) const {
|
||||
PlayerDispDataDCPCV3T<BE> ret;
|
||||
ret.stats = this->stats;
|
||||
ret.visual = this->visual;
|
||||
@@ -377,7 +377,7 @@ struct PlayerDispDataBB {
|
||||
} __packed_ws__(PlayerDispDataBB, 0x190);
|
||||
|
||||
template <bool BE>
|
||||
PlayerDispDataBB PlayerDispDataDCPCV3T<BE>::to_bb(uint8_t to_language, uint8_t from_language) const {
|
||||
PlayerDispDataBB PlayerDispDataDCPCV3T<BE>::to_bb(Language to_language, Language from_language) const {
|
||||
PlayerDispDataBB bb;
|
||||
bb.stats = this->stats;
|
||||
bb.visual = this->visual;
|
||||
@@ -398,7 +398,7 @@ struct GuildCardDCNTE {
|
||||
/* 20 */ pstring<TextEncoding::MARKED, 0x48> description;
|
||||
/* 68 */ parray<uint8_t, 0x0F> unused2;
|
||||
/* 77 */ uint8_t present = 0;
|
||||
/* 78 */ uint8_t language = 0;
|
||||
/* 78 */ Language language = Language::JAPANESE;
|
||||
/* 79 */ uint8_t section_id = 0;
|
||||
/* 7A */ uint8_t char_class = 0;
|
||||
/* 7B */
|
||||
@@ -413,7 +413,7 @@ struct GuildCardDC {
|
||||
/* 20 */ pstring<TextEncoding::MARKED, 0x48> description;
|
||||
/* 68 */ parray<uint8_t, 0x11> unused2;
|
||||
/* 79 */ uint8_t present = 0;
|
||||
/* 7A */ uint8_t language = 0;
|
||||
/* 7A */ Language language = Language::JAPANESE;
|
||||
/* 7B */ uint8_t section_id = 0;
|
||||
/* 7C */ uint8_t char_class = 0;
|
||||
/* 7D */
|
||||
@@ -428,7 +428,7 @@ struct GuildCardPC {
|
||||
/* 08 */ pstring<TextEncoding::UTF16, 0x18> name;
|
||||
/* 38 */ pstring<TextEncoding::UTF16, 0x5A> description;
|
||||
/* EC */ uint8_t present = 0;
|
||||
/* ED */ uint8_t language = 0;
|
||||
/* ED */ Language language = Language::JAPANESE;
|
||||
/* EE */ uint8_t section_id = 0;
|
||||
/* EF */ uint8_t char_class = 0;
|
||||
/* F0 */
|
||||
@@ -456,7 +456,7 @@ struct GuildCardGCT {
|
||||
/* 08:08 */ pstring<TextEncoding::ASCII, 0x18> name;
|
||||
/* 20:20 */ pstring<TextEncoding::MARKED, DescriptionLength> description;
|
||||
/* A0:8C */ uint8_t present = 0;
|
||||
/* A1:8D */ uint8_t language = 0;
|
||||
/* A1:8D */ Language language = Language::JAPANESE;
|
||||
/* A2:8E */ uint8_t section_id = 0;
|
||||
/* A3:8F */ uint8_t char_class = 0;
|
||||
/* A4:90 */
|
||||
@@ -480,7 +480,7 @@ struct GuildCardXB {
|
||||
/* 0010 */ pstring<TextEncoding::ASCII, 0x18> name;
|
||||
/* 0028 */ pstring<TextEncoding::MARKED, 0x200> description;
|
||||
/* 0228 */ uint8_t present = 0;
|
||||
/* 0229 */ uint8_t language = 0;
|
||||
/* 0229 */ Language language = Language::JAPANESE;
|
||||
/* 022A */ uint8_t section_id = 0;
|
||||
/* 022B */ uint8_t char_class = 0;
|
||||
/* 022C */
|
||||
@@ -494,7 +494,7 @@ struct GuildCardBB {
|
||||
/* 0034 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> team_name;
|
||||
/* 0054 */ pstring<TextEncoding::UTF16, 0x58> description;
|
||||
/* 0104 */ uint8_t present = 0;
|
||||
/* 0105 */ uint8_t language = 0;
|
||||
/* 0105 */ Language language = Language::JAPANESE;
|
||||
/* 0106 */ uint8_t section_id = 0;
|
||||
/* 0107 */ uint8_t char_class = 0;
|
||||
/* 0108 */
|
||||
@@ -776,13 +776,13 @@ struct PlayerRecordsChallengeBB {
|
||||
grave_x(rec.grave_x),
|
||||
grave_y(rec.grave_y),
|
||||
grave_z(rec.grave_z),
|
||||
grave_team(rec.grave_team.decode(), 1),
|
||||
grave_message(rec.grave_message.decode(), 1),
|
||||
grave_team(rec.grave_team.decode(), Language::ENGLISH),
|
||||
grave_message(rec.grave_message.decode(), Language::ENGLISH),
|
||||
unknown_m5(rec.unknown_m5),
|
||||
ep1_online_award_state(rec.ep1_online_award_state),
|
||||
ep2_online_award_state(rec.ep2_online_award_state),
|
||||
ep1_offline_award_state(rec.ep1_offline_award_state),
|
||||
rank_title(rec.rank_title.decode(), 1),
|
||||
rank_title(rec.rank_title.decode(), Language::ENGLISH),
|
||||
unknown_l7(rec.unknown_l7) {
|
||||
for (size_t z = 0; z < std::min<size_t>(this->unknown_t6.size(), rec.unknown_t6.size()); z++) {
|
||||
this->unknown_t6[z] = rec.unknown_t6[z];
|
||||
@@ -810,8 +810,8 @@ struct PlayerRecordsChallengeBB {
|
||||
ret.grave_x = this->grave_x;
|
||||
ret.grave_y = this->grave_y;
|
||||
ret.grave_z = this->grave_z;
|
||||
ret.grave_team.encode(this->grave_team.decode(), 1);
|
||||
ret.grave_message.encode(this->grave_message.decode(), 1);
|
||||
ret.grave_team.encode(this->grave_team.decode(), Language::ENGLISH);
|
||||
ret.grave_message.encode(this->grave_message.decode(), Language::ENGLISH);
|
||||
ret.unknown_m5 = this->unknown_m5;
|
||||
for (size_t z = 0; z < std::min<size_t>(ret.unknown_t6.size(), this->unknown_t6.size()); z++) {
|
||||
ret.unknown_t6[z] = this->unknown_t6[z];
|
||||
@@ -819,7 +819,7 @@ struct PlayerRecordsChallengeBB {
|
||||
ret.ep1_online_award_state = this->ep1_online_award_state;
|
||||
ret.ep2_online_award_state = this->ep2_online_award_state;
|
||||
ret.ep1_offline_award_state = this->ep1_offline_award_state;
|
||||
ret.rank_title.encode(this->rank_title.decode(), 1);
|
||||
ret.rank_title.encode(this->rank_title.decode(), Language::ENGLISH);
|
||||
ret.unknown_l7 = this->unknown_l7;
|
||||
return ret;
|
||||
}
|
||||
@@ -856,31 +856,31 @@ check_struct_size(PlayerRecordsBattle, 0x18);
|
||||
check_struct_size(PlayerRecordsBattleBE, 0x18);
|
||||
|
||||
template <typename DestT, typename SrcT = DestT>
|
||||
DestT convert_player_disp_data(const SrcT&, uint8_t, uint8_t) {
|
||||
DestT convert_player_disp_data(const SrcT&, Language, Language) {
|
||||
static_assert(phosg::always_false<DestT, SrcT>::v,
|
||||
"unspecialized convert_player_disp_data should never be called");
|
||||
}
|
||||
|
||||
template <>
|
||||
inline PlayerDispDataDCPCV3 convert_player_disp_data<PlayerDispDataDCPCV3>(const PlayerDispDataDCPCV3& src, uint8_t, uint8_t) {
|
||||
inline PlayerDispDataDCPCV3 convert_player_disp_data<PlayerDispDataDCPCV3>(const PlayerDispDataDCPCV3& src, Language, Language) {
|
||||
return src;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline PlayerDispDataDCPCV3 convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(
|
||||
const PlayerDispDataBB& src, uint8_t to_language, uint8_t from_language) {
|
||||
const PlayerDispDataBB& src, Language to_language, Language from_language) {
|
||||
return src.to_dcpcv3<false>(to_language, from_language);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB, PlayerDispDataDCPCV3>(
|
||||
const PlayerDispDataDCPCV3& src, uint8_t to_language, uint8_t from_language) {
|
||||
const PlayerDispDataDCPCV3& src, Language to_language, Language from_language) {
|
||||
return src.to_bb(to_language, from_language);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB>(
|
||||
const PlayerDispDataBB& src, uint8_t, uint8_t) {
|
||||
const PlayerDispDataBB& src, Language, Language) {
|
||||
return src;
|
||||
}
|
||||
|
||||
@@ -916,21 +916,28 @@ struct QuestFlagsForDifficulty {
|
||||
struct QuestFlags {
|
||||
parray<QuestFlagsForDifficulty, 4> data;
|
||||
|
||||
inline bool get(uint8_t difficulty, uint16_t flag_index) const {
|
||||
return this->data[difficulty].get(flag_index);
|
||||
inline QuestFlagsForDifficulty& for_difficulty(Difficulty difficulty) {
|
||||
return this->data[static_cast<size_t>(difficulty)];
|
||||
}
|
||||
inline void set(uint8_t difficulty, uint16_t flag_index) {
|
||||
this->data[difficulty].set(flag_index);
|
||||
inline const QuestFlagsForDifficulty& for_difficulty(Difficulty difficulty) const {
|
||||
return this->data[static_cast<size_t>(difficulty)];
|
||||
}
|
||||
inline void clear(uint8_t difficulty, uint16_t flag_index) {
|
||||
this->data[difficulty].clear(flag_index);
|
||||
|
||||
inline bool get(Difficulty difficulty, uint16_t flag_index) const {
|
||||
return this->for_difficulty(difficulty).get(flag_index);
|
||||
}
|
||||
inline void update_all(uint8_t difficulty, bool set) {
|
||||
this->data[difficulty].update_all(set);
|
||||
inline void set(Difficulty difficulty, uint16_t flag_index) {
|
||||
this->for_difficulty(difficulty).set(flag_index);
|
||||
}
|
||||
inline void clear(Difficulty difficulty, uint16_t flag_index) {
|
||||
this->for_difficulty(difficulty).clear(flag_index);
|
||||
}
|
||||
inline void update_all(Difficulty difficulty, bool set) {
|
||||
this->for_difficulty(difficulty).update_all(set);
|
||||
}
|
||||
inline void update_all(bool set) {
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
this->update_all(z, set);
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
this->update_all(difficulty, set);
|
||||
}
|
||||
}
|
||||
} __packed_ws__(QuestFlags, 0x200);
|
||||
|
||||
+11
-5
@@ -872,7 +872,13 @@ static asio::awaitable<HandlerResult> SC_6x60_6xA2(shared_ptr<Client> c, Channel
|
||||
|
||||
G_SpecializableItemDropRequest_6xA2 cmd = normalize_drop_request(msg.data.data(), msg.data.size());
|
||||
auto rec = reconcile_drop_request_with_map(
|
||||
c, cmd, c->proxy_session->lobby_episode, c->proxy_session->lobby_event, c->proxy_session->map_state, false);
|
||||
c,
|
||||
cmd,
|
||||
c->proxy_session->lobby_episode,
|
||||
c->proxy_session->lobby_difficulty,
|
||||
c->proxy_session->lobby_event,
|
||||
c->proxy_session->map_state,
|
||||
false);
|
||||
|
||||
ItemCreator::DropResult res;
|
||||
if (rec.obj_st) {
|
||||
@@ -1509,7 +1515,7 @@ static asio::awaitable<HandlerResult> S_65_67_68_EB(shared_ptr<Client> c, Channe
|
||||
c->proxy_session->is_in_game = false;
|
||||
c->proxy_session->is_in_quest = false;
|
||||
c->floor = 0x0F;
|
||||
c->proxy_session->lobby_difficulty = 0;
|
||||
c->proxy_session->lobby_difficulty = Difficulty::NORMAL;
|
||||
c->proxy_session->lobby_section_id = 0;
|
||||
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
||||
c->proxy_session->lobby_episode = Episode::EP1;
|
||||
@@ -1682,7 +1688,7 @@ static asio::awaitable<HandlerResult> S_64(shared_ptr<Client> c, Channel::Messag
|
||||
|
||||
} else {
|
||||
c->proxy_session->lobby_event = 0;
|
||||
c->proxy_session->lobby_difficulty = 0;
|
||||
c->proxy_session->lobby_difficulty = Difficulty::NORMAL;
|
||||
c->proxy_session->lobby_section_id = c->character_file()->disp.visual.section_id;
|
||||
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
||||
c->proxy_session->lobby_random_seed = phosg::random_object<uint32_t>();
|
||||
@@ -1758,7 +1764,7 @@ static asio::awaitable<HandlerResult> S_E8(shared_ptr<Client> c, Channel::Messag
|
||||
c->proxy_session->is_in_game = true;
|
||||
c->proxy_session->is_in_quest = false;
|
||||
c->proxy_session->lobby_event = cmd.event;
|
||||
c->proxy_session->lobby_difficulty = 0;
|
||||
c->proxy_session->lobby_difficulty = Difficulty::NORMAL;
|
||||
c->proxy_session->lobby_section_id = cmd.section_id;
|
||||
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
||||
c->proxy_session->lobby_random_seed = 0;
|
||||
@@ -1848,7 +1854,7 @@ static asio::awaitable<HandlerResult> C_98(shared_ptr<Client> c, Channel::Messag
|
||||
c->proxy_session->is_in_game = false;
|
||||
c->proxy_session->is_in_quest = false;
|
||||
c->proxy_session->lobby_event = 0;
|
||||
c->proxy_session->lobby_difficulty = 0;
|
||||
c->proxy_session->lobby_difficulty = Difficulty::NORMAL;
|
||||
c->proxy_session->lobby_section_id = 0;
|
||||
c->proxy_session->lobby_episode = Episode::EP1;
|
||||
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ void ProxySession::set_drop_mode(
|
||||
s->rare_item_set(version, nullptr),
|
||||
s->armor_random_set,
|
||||
s->tool_random_set,
|
||||
s->weapon_random_sets.at(this->lobby_difficulty),
|
||||
s->weapon_random_set(this->lobby_difficulty),
|
||||
s->tekker_adjustment_set,
|
||||
s->item_parameter_table(version),
|
||||
s->item_stack_limits(version),
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@ struct ProxySession {
|
||||
uint32_t guild_card_number = 0;
|
||||
uint64_t xb_user_id = 0;
|
||||
std::string name;
|
||||
uint8_t language = 0;
|
||||
Language language = Language::JAPANESE;
|
||||
uint8_t section_id = 0;
|
||||
uint8_t char_class = 0;
|
||||
};
|
||||
@@ -38,7 +38,7 @@ struct ProxySession {
|
||||
bool is_in_quest = false;
|
||||
uint8_t leader_client_id = 0;
|
||||
uint8_t lobby_event = 0;
|
||||
uint8_t lobby_difficulty = 0;
|
||||
Difficulty lobby_difficulty = Difficulty::NORMAL;
|
||||
uint8_t lobby_section_id = 0;
|
||||
GameMode lobby_mode = GameMode::NORMAL;
|
||||
Episode lobby_episode = Episode::EP1;
|
||||
|
||||
+33
-32
@@ -203,7 +203,7 @@ void VersionedQuest::assert_valid() const {
|
||||
if (this->version == Version::UNKNOWN) {
|
||||
throw runtime_error("version is not set");
|
||||
}
|
||||
if (this->language == 0xFF) {
|
||||
if (this->language == Language::UNKNOWN) {
|
||||
throw runtime_error("language is not set");
|
||||
}
|
||||
switch (this->meta.episode) {
|
||||
@@ -290,7 +290,7 @@ string VersionedQuest::pvr_filename() const {
|
||||
|
||||
string VersionedQuest::xb_filename() const {
|
||||
return std::format("quest{}_{}.dat",
|
||||
this->meta.quest_number, static_cast<char>(tolower(char_for_language_code(this->language))));
|
||||
this->meta.quest_number, static_cast<char>(tolower(char_for_language(this->language))));
|
||||
}
|
||||
|
||||
string VersionedQuest::encode_qst() const {
|
||||
@@ -301,7 +301,7 @@ string VersionedQuest::encode_qst() const {
|
||||
files.emplace(std::format("quest{}.pvr", this->meta.quest_number), this->pvr_contents);
|
||||
}
|
||||
string xb_filename = std::format("quest{}_{}.dat",
|
||||
this->meta.quest_number, static_cast<char>(tolower(char_for_language_code(language))));
|
||||
this->meta.quest_number, static_cast<char>(tolower(char_for_language(language))));
|
||||
return encode_qst_file(files, this->meta.name, this->meta.quest_number, xb_filename, this->version, this->is_dlq_encoded);
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ phosg::JSON Quest::json() const {
|
||||
for (const auto& [_, vq] : this->versions) {
|
||||
versions_json.emplace_back(phosg::JSON::dict({
|
||||
{"Version", phosg::name_for_enum(vq->version)},
|
||||
{"Language", name_for_language_code(vq->language)},
|
||||
{"Language", name_for_language(vq->language)},
|
||||
{"Name", vq->meta.name},
|
||||
{"ShortDescription", vq->meta.short_description},
|
||||
{"LongDescription", vq->meta.long_description},
|
||||
@@ -331,16 +331,18 @@ phosg::JSON Quest::json() const {
|
||||
});
|
||||
}
|
||||
|
||||
uint32_t Quest::versions_key(Version v, uint8_t language) {
|
||||
return (static_cast<uint32_t>(v) << 8) | language;
|
||||
uint32_t Quest::versions_key(Version v, Language language) {
|
||||
return (static_cast<uint32_t>(v) << 8) | static_cast<uint8_t>(language);
|
||||
}
|
||||
|
||||
const string& Quest::name_for_language(uint8_t language) const {
|
||||
if (!this->names_by_language.at(language).empty()) {
|
||||
return this->names_by_language[language];
|
||||
const string& Quest::name_for_language(Language language) const {
|
||||
size_t lang_index = static_cast<size_t>(language);
|
||||
if (!this->names_by_language.at(lang_index).empty()) {
|
||||
return this->names_by_language[lang_index];
|
||||
}
|
||||
if (!this->names_by_language[1].empty()) {
|
||||
return this->names_by_language[1];
|
||||
constexpr size_t english_lang_index = static_cast<size_t>(Language::ENGLISH);
|
||||
if (!this->names_by_language[english_lang_index].empty()) {
|
||||
return this->names_by_language[english_lang_index];
|
||||
}
|
||||
for (const string& name : this->names_by_language) {
|
||||
if (!name.empty()) {
|
||||
@@ -354,7 +356,8 @@ void Quest::add_version(shared_ptr<const VersionedQuest> vq) {
|
||||
this->meta.assert_compatible(vq->meta);
|
||||
this->versions.emplace(this->versions_key(vq->version, vq->language), vq);
|
||||
|
||||
auto& name_by_language = this->names_by_language.at(vq->language);
|
||||
size_t lang_index = static_cast<size_t>(vq->language);
|
||||
auto& name_by_language = this->names_by_language.at(lang_index);
|
||||
if (name_by_language.empty()) {
|
||||
name_by_language = vq->meta.name;
|
||||
}
|
||||
@@ -369,7 +372,7 @@ std::shared_ptr<const SuperMap> Quest::get_supermap(int64_t random_seed) const {
|
||||
bool any_map_file_present = false;
|
||||
array<shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
|
||||
for (Version v : ALL_NON_PATCH_VERSIONS) {
|
||||
auto vq = this->version(v, 1);
|
||||
auto vq = this->version(v, Language::ENGLISH);
|
||||
if (vq && vq->map_file) {
|
||||
auto map_file = vq->map_file;
|
||||
if (map_file->has_random_sections()) {
|
||||
@@ -398,17 +401,17 @@ std::shared_ptr<const SuperMap> Quest::get_supermap(int64_t random_seed) const {
|
||||
return supermap;
|
||||
}
|
||||
|
||||
bool Quest::has_version(Version v, uint8_t language) const {
|
||||
bool Quest::has_version(Version v, Language language) const {
|
||||
return this->versions.count(this->versions_key(v, language));
|
||||
}
|
||||
|
||||
bool Quest::has_version_any_language(Version v) const {
|
||||
uint32_t k = this->versions_key(v, 0);
|
||||
uint32_t k = this->versions_key(v, Language::JAPANESE);
|
||||
auto it = this->versions.lower_bound(k);
|
||||
return ((it != this->versions.end()) && ((it->first & 0xFF00) == k));
|
||||
}
|
||||
|
||||
shared_ptr<const VersionedQuest> Quest::version(Version v, uint8_t language) const {
|
||||
shared_ptr<const VersionedQuest> Quest::version(Version v, Language language) const {
|
||||
// Return the requested version, if it exists
|
||||
try {
|
||||
return this->versions.at(this->versions_key(v, language));
|
||||
@@ -417,13 +420,13 @@ shared_ptr<const VersionedQuest> Quest::version(Version v, uint8_t language) con
|
||||
|
||||
// Return the English version, if it exists
|
||||
try {
|
||||
return this->versions.at(this->versions_key(v, 1));
|
||||
return this->versions.at(this->versions_key(v, Language::ENGLISH));
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
|
||||
// Return the first language, if it exists
|
||||
auto it = this->versions.lower_bound(this->versions_key(v, 0));
|
||||
if ((it == this->versions.end()) || ((it->first & 0xFF00) != this->versions_key(v, 0))) {
|
||||
auto it = this->versions.lower_bound(this->versions_key(v, Language::JAPANESE));
|
||||
if ((it == this->versions.end()) || ((it->first & 0xFF00) != this->versions_key(v, Language::JAPANESE))) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second;
|
||||
@@ -646,7 +649,7 @@ QuestIndex::QuestIndex(
|
||||
if (language_token.size() != 1) {
|
||||
throw runtime_error("language token is not a single character");
|
||||
}
|
||||
vq->language = language_code_for_char(language_token[0]);
|
||||
vq->language = language_for_char(language_token[0]);
|
||||
}
|
||||
|
||||
auto bin_decompressed = prs_decompress(*entry.data);
|
||||
@@ -777,29 +780,27 @@ QuestIndex::QuestIndex(
|
||||
auto q_it = this->quests_by_number.find(vq->meta.quest_number);
|
||||
if (q_it != this->quests_by_number.end()) {
|
||||
q_it->second->add_version(vq);
|
||||
static_game_data_log.debug_f("({}) Added {} {} version of quest {} ({}) with floors {}",
|
||||
static_game_data_log.debug_f("({}) Added {} {} version of quest {} ({})",
|
||||
filenames_str,
|
||||
phosg::name_for_enum(vq->version),
|
||||
char_for_language_code(vq->language),
|
||||
char_for_language(vq->language),
|
||||
vq->meta.quest_number,
|
||||
vq->meta.name,
|
||||
phosg::format_data_string(vq->meta.area_for_floor.data(), 0x12));
|
||||
vq->meta.name);
|
||||
} else {
|
||||
auto q = make_shared<Quest>(vq);
|
||||
this->quests_by_number.emplace(vq->meta.quest_number, q);
|
||||
this->quests_by_name.emplace(vq->meta.name, q);
|
||||
this->quests_by_category_id_and_number[q->meta.category_id].emplace(vq->meta.quest_number, q);
|
||||
static_game_data_log.debug_f("({}) Created {} {} quest {} ({}) ({}, {} ({}), {}) with floors {}",
|
||||
static_game_data_log.debug_f("({}) Created {} {} quest {} ({}) ({}, {} ({}), {})",
|
||||
filenames_str,
|
||||
phosg::name_for_enum(vq->version),
|
||||
char_for_language_code(vq->language),
|
||||
char_for_language(vq->language),
|
||||
vq->meta.quest_number,
|
||||
vq->meta.name,
|
||||
name_for_episode(vq->meta.episode),
|
||||
category_name,
|
||||
vq->meta.category_id,
|
||||
vq->meta.joinable ? "joinable" : "not joinable",
|
||||
phosg::format_data_string(vq->meta.area_for_floor.data(), 0x12));
|
||||
vq->meta.joinable ? "joinable" : "not joinable");
|
||||
}
|
||||
} catch (const exception& e) {
|
||||
static_game_data_log.warning_f("({}) Failed to index quest file: {}", basename, e.what());
|
||||
@@ -935,7 +936,7 @@ string encode_download_quest_data(const string& compressed_data, size_t decompre
|
||||
return data;
|
||||
}
|
||||
|
||||
shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(uint8_t override_language) const {
|
||||
shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(Language override_language) const {
|
||||
// The download flag needs to be set in the bin header, or else the client
|
||||
// will ignore it when scanning for download quests in an offline game. To set
|
||||
// this flag, we need to decompress the quest's .bin file, set the flag, then
|
||||
@@ -958,7 +959,7 @@ shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(uint8_t overrid
|
||||
if (decompressed_bin.size() < sizeof(PSOQuestHeaderDC)) {
|
||||
throw runtime_error("bin file is too small for header");
|
||||
}
|
||||
if (override_language != 0xFF) {
|
||||
if (override_language != Language::UNKNOWN) {
|
||||
reinterpret_cast<PSOQuestHeaderDC*>(data_ptr)->language = override_language;
|
||||
}
|
||||
break;
|
||||
@@ -967,7 +968,7 @@ shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(uint8_t overrid
|
||||
if (decompressed_bin.size() < sizeof(PSOQuestHeaderPC)) {
|
||||
throw runtime_error("bin file is too small for header");
|
||||
}
|
||||
if (override_language != 0xFF) {
|
||||
if (override_language != Language::UNKNOWN) {
|
||||
reinterpret_cast<PSOQuestHeaderPC*>(data_ptr)->language = override_language;
|
||||
}
|
||||
break;
|
||||
@@ -977,7 +978,7 @@ shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(uint8_t overrid
|
||||
if (decompressed_bin.size() < sizeof(PSOQuestHeaderGC)) {
|
||||
throw runtime_error("bin file is too small for header");
|
||||
}
|
||||
if (override_language != 0xFF) {
|
||||
if (override_language != Language::UNKNOWN) {
|
||||
reinterpret_cast<PSOQuestHeaderGC*>(data_ptr)->language = override_language;
|
||||
}
|
||||
break;
|
||||
|
||||
+6
-6
@@ -72,7 +72,7 @@ struct VersionedQuest {
|
||||
// Most of these default values are intentionally invalid; we use these
|
||||
// values to check if each field was parsed during quest indexing.
|
||||
Version version = Version::UNKNOWN;
|
||||
uint8_t language = 0xFF;
|
||||
Language language = Language::UNKNOWN;
|
||||
std::shared_ptr<const std::string> bin_contents;
|
||||
std::shared_ptr<const std::string> dat_contents;
|
||||
std::shared_ptr<const MapFile> map_file;
|
||||
@@ -86,7 +86,7 @@ struct VersionedQuest {
|
||||
std::string pvr_filename() const;
|
||||
std::string xb_filename() const;
|
||||
|
||||
std::shared_ptr<VersionedQuest> create_download_quest(uint8_t override_language = 0xFF) const;
|
||||
std::shared_ptr<VersionedQuest> create_download_quest(Language override_language = Language::UNKNOWN) const;
|
||||
std::string encode_qst() const;
|
||||
};
|
||||
|
||||
@@ -107,14 +107,14 @@ struct Quest {
|
||||
|
||||
std::shared_ptr<const SuperMap> get_supermap(int64_t random_seed) const;
|
||||
|
||||
const std::string& name_for_language(uint8_t language) const;
|
||||
const std::string& name_for_language(Language language) const;
|
||||
|
||||
void add_version(std::shared_ptr<const VersionedQuest> vq);
|
||||
bool has_version(Version v, uint8_t language) const;
|
||||
bool has_version(Version v, Language language) const;
|
||||
bool has_version_any_language(Version v) const;
|
||||
std::shared_ptr<const VersionedQuest> version(Version v, uint8_t language) const;
|
||||
std::shared_ptr<const VersionedQuest> version(Version v, Language language) const;
|
||||
|
||||
static uint32_t versions_key(Version v, uint8_t language);
|
||||
static uint32_t versions_key(Version v, Language language);
|
||||
};
|
||||
|
||||
struct QuestIndex {
|
||||
|
||||
@@ -69,7 +69,7 @@ void QuestMetadata::assert_compatible(const QuestMetadata& other) const {
|
||||
if (this->challenge_difficulty != other.challenge_difficulty) {
|
||||
throw runtime_error(std::format(
|
||||
"quest version has different challenge difficulty (existing: {}, new: {})",
|
||||
this->challenge_difficulty, other.challenge_difficulty));
|
||||
name_for_difficulty(this->challenge_difficulty), name_for_difficulty(other.challenge_difficulty)));
|
||||
}
|
||||
for (size_t z = 0; z < this->area_for_floor.size(); z++) {
|
||||
const auto& this_fa = this->area_for_floor[z];
|
||||
@@ -150,7 +150,7 @@ phosg::JSON QuestMetadata::json() const {
|
||||
{"BattleRules", this->battle_rules ? this->battle_rules->json() : phosg::JSON(nullptr)},
|
||||
{"ChallengeTemplateIndex", (this->challenge_template_index >= 0) ? this->challenge_template_index : phosg::JSON(nullptr)},
|
||||
{"ChallengeEXPMultiplier", (this->challenge_exp_multiplier >= 0) ? this->challenge_exp_multiplier : phosg::JSON(nullptr)},
|
||||
{"ChallengeDifficulty", (this->challenge_difficulty >= 0) ? this->challenge_difficulty : phosg::JSON(nullptr)},
|
||||
{"ChallengeDifficulty", (this->challenge_difficulty != Difficulty::UNKNOWN) ? name_for_difficulty(this->challenge_difficulty) : phosg::JSON(nullptr)},
|
||||
{"DescriptionFlag", this->description_flag},
|
||||
{"AvailableExpression", this->available_expression ? this->available_expression->str() : phosg::JSON(nullptr)},
|
||||
{"EnabledExpression", this->available_expression ? this->available_expression->str() : phosg::JSON(nullptr)},
|
||||
|
||||
@@ -28,7 +28,7 @@ struct QuestMetadata {
|
||||
std::shared_ptr<const BattleRules> battle_rules;
|
||||
ssize_t challenge_template_index = -1;
|
||||
float challenge_exp_multiplier = -1.0f;
|
||||
int8_t challenge_difficulty = -1;
|
||||
Difficulty challenge_difficulty = Difficulty::UNKNOWN;
|
||||
uint8_t description_flag = 0x00;
|
||||
std::shared_ptr<const IntegralExpression> available_expression;
|
||||
std::shared_ptr<const IntegralExpression> enabled_expression;
|
||||
|
||||
+34
-27
@@ -82,8 +82,8 @@ static const char* name_for_header_episode_number(uint8_t episode) {
|
||||
}
|
||||
}
|
||||
|
||||
static TextEncoding encoding_for_language(uint8_t language) {
|
||||
return (language ? TextEncoding::ISO8859 : TextEncoding::SJIS);
|
||||
static TextEncoding encoding_for_language(Language language) {
|
||||
return ((language == Language::JAPANESE) ? TextEncoding::SJIS : TextEncoding::ISO8859);
|
||||
}
|
||||
|
||||
static string escape_string(const string& data, TextEncoding encoding = TextEncoding::UTF8) {
|
||||
@@ -2970,7 +2970,7 @@ std::string disassemble_quest_script(
|
||||
const void* data,
|
||||
size_t size,
|
||||
Version version,
|
||||
uint8_t override_language,
|
||||
Language override_language,
|
||||
bool reassembly_mode,
|
||||
bool use_qedit_names) {
|
||||
phosg::StringReader r(data, size);
|
||||
@@ -2980,14 +2980,14 @@ std::string disassemble_quest_script(
|
||||
bool use_wstrs = false;
|
||||
size_t code_offset = 0;
|
||||
size_t function_table_offset = 0;
|
||||
uint8_t language;
|
||||
Language language;
|
||||
switch (version) {
|
||||
case Version::DC_NTE: {
|
||||
const auto& header = r.get<PSOQuestHeaderDCNTE>();
|
||||
code_offset = header.code_offset;
|
||||
function_table_offset = header.function_table_offset;
|
||||
language = 0;
|
||||
lines.emplace_back(".name " + escape_string(header.name.decode(0)));
|
||||
language = Language::JAPANESE;
|
||||
lines.emplace_back(".name " + escape_string(header.name.decode(Language::JAPANESE)));
|
||||
break;
|
||||
}
|
||||
case Version::DC_11_2000:
|
||||
@@ -2996,15 +2996,15 @@ std::string disassemble_quest_script(
|
||||
const auto& header = r.get<PSOQuestHeaderDC>();
|
||||
code_offset = header.code_offset;
|
||||
function_table_offset = header.function_table_offset;
|
||||
if (override_language != 0xFF) {
|
||||
if (override_language != Language::UNKNOWN) {
|
||||
language = override_language;
|
||||
} else if (header.language < 5) {
|
||||
} else if (static_cast<size_t>(header.language) < 5) {
|
||||
language = header.language;
|
||||
} else {
|
||||
language = 1;
|
||||
language = Language::ENGLISH;
|
||||
}
|
||||
lines.emplace_back(std::format(".quest_num {}", header.quest_number));
|
||||
lines.emplace_back(std::format(".language {}", header.language));
|
||||
lines.emplace_back(std::format(".language {}", char_for_language(header.language)));
|
||||
lines.emplace_back(".name " + escape_string(header.name.decode(language)));
|
||||
lines.emplace_back(".short_desc " + escape_string(header.short_description.decode(language)));
|
||||
lines.emplace_back(".long_desc " + escape_string(header.long_description.decode(language)));
|
||||
@@ -3016,15 +3016,15 @@ std::string disassemble_quest_script(
|
||||
const auto& header = r.get<PSOQuestHeaderPC>();
|
||||
code_offset = header.code_offset;
|
||||
function_table_offset = header.function_table_offset;
|
||||
if (override_language != 0xFF) {
|
||||
if (override_language != Language::UNKNOWN) {
|
||||
language = override_language;
|
||||
} else if (header.language < 8) {
|
||||
} else if (static_cast<size_t>(header.language) < 8) {
|
||||
language = header.language;
|
||||
} else {
|
||||
language = 1;
|
||||
language = Language::ENGLISH;
|
||||
}
|
||||
lines.emplace_back(std::format(".quest_num {}", header.quest_number));
|
||||
lines.emplace_back(std::format(".language {}", header.language));
|
||||
lines.emplace_back(std::format(".language {}", char_for_language(header.language)));
|
||||
lines.emplace_back(".name " + escape_string(header.name.decode(language)));
|
||||
lines.emplace_back(".short_desc " + escape_string(header.short_description.decode(language)));
|
||||
lines.emplace_back(".long_desc " + escape_string(header.long_description.decode(language)));
|
||||
@@ -3038,15 +3038,15 @@ std::string disassemble_quest_script(
|
||||
const auto& header = r.get<PSOQuestHeaderGC>();
|
||||
code_offset = header.code_offset;
|
||||
function_table_offset = header.function_table_offset;
|
||||
if (override_language != 0xFF) {
|
||||
if (override_language != Language::UNKNOWN) {
|
||||
language = override_language;
|
||||
} else if (header.language < 5) {
|
||||
} else if (static_cast<size_t>(header.language) < 5) {
|
||||
language = header.language;
|
||||
} else {
|
||||
language = 1;
|
||||
language = Language::ENGLISH;
|
||||
}
|
||||
lines.emplace_back(std::format(".quest_num {}", header.quest_number));
|
||||
lines.emplace_back(std::format(".language {}", header.language));
|
||||
lines.emplace_back(std::format(".language {}", char_for_language(header.language)));
|
||||
lines.emplace_back(".name " + escape_string(header.name.decode(language)));
|
||||
lines.emplace_back(".short_desc " + escape_string(header.short_description.decode(language)));
|
||||
lines.emplace_back(".long_desc " + escape_string(header.long_description.decode(language)));
|
||||
@@ -3057,10 +3057,10 @@ std::string disassemble_quest_script(
|
||||
const auto& header = r.get<PSOQuestHeaderBB>();
|
||||
code_offset = header.code_offset;
|
||||
function_table_offset = header.function_table_offset;
|
||||
if (override_language != 0xFF) {
|
||||
if (override_language != Language::UNKNOWN) {
|
||||
language = override_language;
|
||||
} else {
|
||||
language = 1;
|
||||
language = Language::ENGLISH;
|
||||
}
|
||||
lines.emplace_back(std::format(".quest_num {}", header.quest_number));
|
||||
lines.emplace_back(std::format(".episode {}", name_for_header_episode_number(header.episode)));
|
||||
@@ -3335,7 +3335,7 @@ std::string disassemble_quest_script(
|
||||
} else {
|
||||
string s = cmd_r.get_cstr();
|
||||
if (def->flags & F_PUSH_ARG) {
|
||||
arg_stack_values.emplace_back(language ? tt_8859_to_utf8(s) : tt_sega_sjis_to_utf8(s));
|
||||
arg_stack_values.emplace_back((language == Language::JAPANESE) ? tt_sega_sjis_to_utf8(s) : tt_8859_to_utf8(s));
|
||||
}
|
||||
dasm_arg = escape_string(s, encoding_for_language(language));
|
||||
}
|
||||
@@ -4008,7 +4008,7 @@ AssembledQuestScript assemble_quest_script(
|
||||
string quest_short_desc;
|
||||
string quest_long_desc;
|
||||
int64_t quest_num = -1;
|
||||
uint8_t quest_language = 1;
|
||||
Language quest_language = Language::ENGLISH;
|
||||
Episode quest_episode = Episode::EP1;
|
||||
uint8_t quest_max_players = 4;
|
||||
bool quest_joinable = false;
|
||||
@@ -4033,7 +4033,11 @@ AssembledQuestScript assemble_quest_script(
|
||||
} else if (line.text.starts_with(".quest_num ")) {
|
||||
quest_num = stoul(line.text.substr(11), nullptr, 0);
|
||||
} else if (line.text.starts_with(".language ")) {
|
||||
quest_language = stoul(line.text.substr(10), nullptr, 0);
|
||||
auto code = line.text.substr(10);
|
||||
if (code.size() != 1) {
|
||||
throw runtime_error(".language directive argument is invalid");
|
||||
}
|
||||
quest_language = language_for_char(code[0]);
|
||||
} else if (line.text.starts_with(".episode ")) {
|
||||
quest_episode = episode_for_token_name(line.text.substr(9));
|
||||
} else if (line.text.starts_with(".max_players ")) {
|
||||
@@ -4322,7 +4326,7 @@ AssembledQuestScript assemble_quest_script(
|
||||
case Version::GC_EP3_NTE:
|
||||
case Version::GC_EP3:
|
||||
case Version::XB_V3:
|
||||
code_w.write(bin ? text : (quest_language ? tt_utf8_to_8859(text) : tt_utf8_to_sega_sjis(text)));
|
||||
code_w.write(bin ? text : ((quest_language == Language::JAPANESE) ? tt_utf8_to_sega_sjis(text) : tt_utf8_to_8859(text)));
|
||||
code_w.put_u8(0);
|
||||
break;
|
||||
case Version::PC_NTE:
|
||||
@@ -4571,7 +4575,7 @@ AssembledQuestScript assemble_quest_script(
|
||||
header.code_offset = sizeof(header);
|
||||
header.function_table_offset = sizeof(header) + code_w.size();
|
||||
header.size = header.function_table_offset + function_table.size() * sizeof(function_table[0]);
|
||||
header.name.encode(quest_name, 0);
|
||||
header.name.encode(quest_name, Language::JAPANESE);
|
||||
w.put(header);
|
||||
break;
|
||||
}
|
||||
@@ -4662,7 +4666,7 @@ AssembledQuestScript assemble_quest_script(
|
||||
}
|
||||
|
||||
void populate_quest_metadata_from_script(
|
||||
QuestMetadata& meta, const void* data, size_t size, Version version, uint8_t language) {
|
||||
QuestMetadata& meta, const void* data, size_t size, Version version, Language language) {
|
||||
phosg::StringReader r(data, size);
|
||||
uint32_t code_offset = r.size();
|
||||
uint32_t function_table_offset = r.size();
|
||||
@@ -5192,7 +5196,10 @@ void populate_quest_metadata_from_script(
|
||||
break;
|
||||
|
||||
case 0xF824: // set_cmode_difficulty
|
||||
meta.challenge_difficulty = get_single_int32_arg();
|
||||
meta.challenge_difficulty = static_cast<Difficulty>(get_single_int32_arg());
|
||||
if (static_cast<size_t>(meta.challenge_difficulty) > 3) {
|
||||
throw std::runtime_error("invalid challenge mode difficulty");
|
||||
}
|
||||
// phosg::fwrite_fmt(stderr, ">>> Trace: meta.challenge_difficulty = {}\n", meta.challenge_difficulty);
|
||||
break;
|
||||
|
||||
|
||||
+6
-6
@@ -38,7 +38,7 @@ struct PSOQuestHeaderDC { // Same format for DC v1 and v2
|
||||
/* 0008 */ le_uint32_t size = 0;
|
||||
/* 000C */ le_uint16_t unknown_a1 = 0;
|
||||
/* 000E */ le_uint16_t unknown_a2 = 0;
|
||||
/* 0010 */ uint8_t language = 0;
|
||||
/* 0010 */ Language language = Language::JAPANESE;
|
||||
/* 0011 */ uint8_t unknown_a3 = 0;
|
||||
/* 0012 */ le_uint16_t quest_number = 0; // 0xFFFF for challenge quests
|
||||
/* 0014 */ pstring<TextEncoding::MARKED, 0x20> name;
|
||||
@@ -53,7 +53,7 @@ struct PSOQuestHeaderPC {
|
||||
/* 0008 */ le_uint32_t size = 0;
|
||||
/* 000C */ le_uint16_t unknown_a1 = 0;
|
||||
/* 000E */ le_uint16_t unknown_a2 = 0;
|
||||
/* 0010 */ uint8_t language = 0;
|
||||
/* 0010 */ Language language = Language::JAPANESE;
|
||||
/* 0011 */ uint8_t unknown_a3 = 0;
|
||||
/* 0012 */ le_uint16_t quest_number = 0; // 0xFFFF for challenge quests
|
||||
/* 0014 */ pstring<TextEncoding::UTF16, 0x20> name;
|
||||
@@ -70,7 +70,7 @@ struct PSOQuestHeaderGC {
|
||||
/* 0008 */ le_uint32_t size = 0;
|
||||
/* 000C */ le_uint16_t unknown_a1 = 0;
|
||||
/* 000E */ le_uint16_t unknown_a2 = 0;
|
||||
/* 0010 */ uint8_t language = 0;
|
||||
/* 0010 */ Language language = Language::JAPANESE;
|
||||
/* 0011 */ uint8_t unknown_a3 = 0;
|
||||
// Note: The GC client byteswaps this field, then loads it as a byte, so
|
||||
// technically the high byte of this is what the client uses as the quest
|
||||
@@ -109,7 +109,7 @@ std::string disassemble_quest_script(
|
||||
const void* data,
|
||||
size_t size,
|
||||
Version version,
|
||||
uint8_t override_language = 0xFF,
|
||||
Language override_language = Language::UNKNOWN,
|
||||
bool reassembly_mode = false,
|
||||
bool use_qedit_names = false);
|
||||
|
||||
@@ -117,7 +117,7 @@ struct AssembledQuestScript {
|
||||
std::string data;
|
||||
int64_t quest_number = -1;
|
||||
Version version = Version::UNKNOWN;
|
||||
uint8_t language = 0xFF;
|
||||
Language language = Language::UNKNOWN;
|
||||
Episode episode = Episode::NONE;
|
||||
bool joinable = false;
|
||||
uint8_t max_players = 0x00;
|
||||
@@ -130,4 +130,4 @@ AssembledQuestScript assemble_quest_script(
|
||||
const std::vector<std::string>& script_include_directories,
|
||||
const std::vector<std::string>& native_include_directories);
|
||||
|
||||
void populate_quest_metadata_from_script(QuestMetadata& meta, const void* data, size_t size, Version version, uint8_t language);
|
||||
void populate_quest_metadata_from_script(QuestMetadata& meta, const void* data, size_t size, Version version, Language language);
|
||||
|
||||
+38
-43
@@ -236,12 +236,11 @@ RareItemSet::SpecCollection RareItemSet::ParsedRELData::as_collection() const {
|
||||
}
|
||||
|
||||
RareItemSet::RareItemSet(const AFSArchive& afs, bool is_v1) {
|
||||
const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
for (GameMode mode : modes) {
|
||||
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (GameMode mode : ALL_GAME_MODES_V4) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
||||
try {
|
||||
size_t index = difficulty * 10 + section_id;
|
||||
size_t index = static_cast<size_t>(difficulty) * 10 + section_id;
|
||||
ParsedRELData rel(afs.get_reader(index), false, is_v1);
|
||||
this->collections.emplace(
|
||||
this->key_for_params(mode, Episode::EP1, difficulty, section_id),
|
||||
@@ -253,7 +252,7 @@ RareItemSet::RareItemSet(const AFSArchive& afs, bool is_v1) {
|
||||
}
|
||||
}
|
||||
|
||||
string RareItemSet::gsl_entry_name_for_table(GameMode mode, Episode episode, uint8_t difficulty, uint8_t section_id) {
|
||||
string RareItemSet::gsl_entry_name_for_table(GameMode mode, Episode episode, Difficulty difficulty, uint8_t section_id) {
|
||||
return std::format("ItemRT{}{}{}{}.rel",
|
||||
((mode == GameMode::CHALLENGE) ? "c" : ""),
|
||||
((episode == Episode::EP2) ? "l" : ""),
|
||||
@@ -262,11 +261,9 @@ string RareItemSet::gsl_entry_name_for_table(GameMode mode, Episode episode, uin
|
||||
}
|
||||
|
||||
RareItemSet::RareItemSet(const GSLArchive& gsl, bool is_big_endian) {
|
||||
const array<Episode, 2> episodes = {Episode::EP1, Episode::EP2};
|
||||
const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
for (GameMode mode : modes) {
|
||||
for (Episode episode : episodes) {
|
||||
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (GameMode mode : ALL_GAME_MODES_V23) {
|
||||
for (Episode episode : ALL_EPISODES_V3) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
||||
try {
|
||||
string filename = this->gsl_entry_name_for_table(mode, episode, difficulty, section_id);
|
||||
@@ -285,15 +282,15 @@ RareItemSet::RareItemSet(const GSLArchive& gsl, bool is_big_endian) {
|
||||
RareItemSet::RareItemSet(const string& rel_data, bool is_big_endian) {
|
||||
// Tables are 0x280 bytes in size in this format, laid out sequentially
|
||||
phosg::StringReader r(rel_data);
|
||||
array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (size_t ep_index = 0; ep_index < episodes.size(); ep_index++) {
|
||||
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (Episode episode : ALL_EPISODES_V4) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
||||
try {
|
||||
size_t index = (ep_index * 40) + difficulty * 10 + section_id;
|
||||
size_t ep_index = (episode == Episode::EP1) ? 0 : ((episode == Episode::EP2) ? 1 : 2);
|
||||
size_t index = (ep_index * 40) + static_cast<size_t>(difficulty) * 10 + section_id;
|
||||
ParsedRELData rel(r.sub(0x280 * index, 0x280), is_big_endian, false);
|
||||
this->collections.emplace(
|
||||
this->key_for_params(GameMode::NORMAL, episodes[ep_index], difficulty, section_id),
|
||||
this->key_for_params(GameMode::NORMAL, episode, difficulty, section_id),
|
||||
rel.as_collection());
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
@@ -314,9 +311,9 @@ RareItemSet::RareItemSet(const phosg::JSON& json, shared_ptr<const ItemNameIndex
|
||||
Episode episode = episode_keys.at(episode_it.first);
|
||||
|
||||
for (const auto& difficulty_it : episode_it.second->as_dict()) {
|
||||
static const unordered_map<string, uint8_t> difficulty_keys(
|
||||
{{"Normal", 0}, {"Hard", 1}, {"VeryHard", 2}, {"Ultimate", 3}});
|
||||
uint8_t difficulty = difficulty_keys.at(difficulty_it.first);
|
||||
static const unordered_map<string, Difficulty> difficulty_keys(
|
||||
{{"Normal", Difficulty::NORMAL}, {"Hard", Difficulty::HARD}, {"VeryHard", Difficulty::VERY_HARD}, {"Ultimate", Difficulty::ULTIMATE}});
|
||||
Difficulty difficulty = difficulty_keys.at(difficulty_it.first);
|
||||
|
||||
for (const auto& section_id_it : difficulty_it.second->as_dict()) {
|
||||
uint8_t section_id = section_id_for_name(section_id_it.first);
|
||||
@@ -385,7 +382,10 @@ RareItemSet::RareItemSet(const phosg::JSON& json, shared_ptr<const ItemNameIndex
|
||||
|
||||
std::string RareItemSet::serialize_afs(bool is_v1) const {
|
||||
vector<string> files;
|
||||
for (uint8_t difficulty = 0; difficulty < (is_v1 ? 3 : 4); difficulty++) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
if (is_v1 && (difficulty == Difficulty::ULTIMATE)) {
|
||||
continue;
|
||||
}
|
||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||
ParsedRELData rel(this->get_collection(GameMode::NORMAL, Episode::EP1, difficulty, section_id));
|
||||
files.emplace_back(rel.serialize(false, is_v1));
|
||||
@@ -397,9 +397,8 @@ std::string RareItemSet::serialize_afs(bool is_v1) const {
|
||||
std::string RareItemSet::serialize_gsl(bool big_endian) const {
|
||||
unordered_map<string, string> files;
|
||||
|
||||
static const std::array<Episode, 2> episodes = {Episode::EP1, Episode::EP2};
|
||||
for (Episode episode : episodes) {
|
||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (Episode episode : ALL_EPISODES_V3) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||
try {
|
||||
string filename = this->gsl_entry_name_for_table(GameMode::NORMAL, episode, difficulty, section_id);
|
||||
@@ -412,7 +411,7 @@ std::string RareItemSet::serialize_gsl(bool big_endian) const {
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||
try {
|
||||
string filename = this->gsl_entry_name_for_table(GameMode::CHALLENGE, Episode::EP1, difficulty, section_id);
|
||||
@@ -429,7 +428,7 @@ std::string RareItemSet::serialize_gsl(bool big_endian) const {
|
||||
string RareItemSet::serialize_html(
|
||||
GameMode mode,
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
shared_ptr<const ItemNameIndex> name_index,
|
||||
shared_ptr<const CommonItemSet> common_item_set) const {
|
||||
|
||||
@@ -762,7 +761,7 @@ string RareItemSet::serialize_html(
|
||||
specs_lists[section_id] = this->get_enemy_specs(mode, episode, difficulty, section_id, rt_index);
|
||||
}
|
||||
const auto& type_def = type_definition_for_enemy(type);
|
||||
const char* name = (difficulty == 3 && type_def.ultimate_name) ? type_def.ultimate_name : type_def.in_game_name;
|
||||
const char* name = (difficulty == Difficulty::ULTIMATE && type_def.ultimate_name) ? type_def.ultimate_name : type_def.in_game_name;
|
||||
add_specs_row(&type_def, name, false, specs_lists);
|
||||
}
|
||||
for (uint8_t floor : zone_type.floors) {
|
||||
@@ -785,13 +784,11 @@ string RareItemSet::serialize_html(
|
||||
|
||||
phosg::JSON RareItemSet::json(shared_ptr<const ItemNameIndex> name_index) const {
|
||||
auto modes_dict = phosg::JSON::dict();
|
||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
for (const auto& mode : modes) {
|
||||
for (const auto& mode : ALL_GAME_MODES_V4) {
|
||||
auto episodes_dict = phosg::JSON::dict();
|
||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (const auto& episode : episodes) {
|
||||
for (const auto& episode : ALL_EPISODES_V4) {
|
||||
auto difficulty_dict = phosg::JSON::dict();
|
||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (const auto& difficulty : ALL_DIFFICULTIES_V234) {
|
||||
auto section_id_dict = phosg::JSON::dict();
|
||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||
auto collection_dict = phosg::JSON::dict();
|
||||
@@ -884,7 +881,7 @@ void RareItemSet::print_collection(
|
||||
FILE* stream,
|
||||
GameMode mode,
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
uint8_t section_id,
|
||||
shared_ptr<const ItemNameIndex> name_index) const {
|
||||
const SpecCollection* collection;
|
||||
@@ -928,11 +925,9 @@ void RareItemSet::print_collection(
|
||||
}
|
||||
|
||||
void RareItemSet::print_all_collections(FILE* stream, std::shared_ptr<const ItemNameIndex> name_index) const {
|
||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (GameMode mode : modes) {
|
||||
for (Episode episode : episodes) {
|
||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (GameMode mode : ALL_GAME_MODES_V4) {
|
||||
for (Episode episode : ALL_EPISODES_V4) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||
try {
|
||||
this->print_collection(stream, mode, episode, difficulty, section_id, name_index);
|
||||
@@ -945,7 +940,7 @@ void RareItemSet::print_all_collections(FILE* stream, std::shared_ptr<const Item
|
||||
}
|
||||
|
||||
std::vector<RareItemSet::ExpandedDrop> RareItemSet::get_enemy_specs(
|
||||
GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid, uint8_t rt_index) const {
|
||||
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, uint8_t rt_index) const {
|
||||
try {
|
||||
return this->get_collection(mode, episode, difficulty, secid).rt_index_to_specs.at(rt_index);
|
||||
} catch (const out_of_range&) {
|
||||
@@ -955,7 +950,7 @@ std::vector<RareItemSet::ExpandedDrop> RareItemSet::get_enemy_specs(
|
||||
}
|
||||
|
||||
std::vector<RareItemSet::ExpandedDrop> RareItemSet::get_box_specs(
|
||||
GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid, uint8_t area_norm) const {
|
||||
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, uint8_t area_norm) const {
|
||||
try {
|
||||
return this->get_collection(mode, episode, difficulty, secid).box_area_norm_to_specs.at(area_norm);
|
||||
} catch (const out_of_range&) {
|
||||
@@ -964,7 +959,7 @@ std::vector<RareItemSet::ExpandedDrop> RareItemSet::get_box_specs(
|
||||
}
|
||||
}
|
||||
|
||||
bool RareItemSet::has_entries_for_game_config(GameMode mode, Episode episode, uint8_t difficulty) const {
|
||||
bool RareItemSet::has_entries_for_game_config(GameMode mode, Episode episode, Difficulty difficulty) const {
|
||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||
if (this->collections.count(this->key_for_params(mode, episode, difficulty, section_id))) {
|
||||
return true;
|
||||
@@ -974,19 +969,19 @@ bool RareItemSet::has_entries_for_game_config(GameMode mode, Episode episode, ui
|
||||
}
|
||||
|
||||
const RareItemSet::SpecCollection& RareItemSet::get_collection(
|
||||
GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid) const {
|
||||
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid) const {
|
||||
return this->collections.at(this->key_for_params(mode, episode, difficulty, secid));
|
||||
}
|
||||
|
||||
uint16_t RareItemSet::key_for_params(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid) {
|
||||
if (difficulty > 3) {
|
||||
uint16_t RareItemSet::key_for_params(GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid) {
|
||||
if (static_cast<size_t>(difficulty) > 3) {
|
||||
throw logic_error("incorrect difficulty");
|
||||
}
|
||||
if (secid > 10) {
|
||||
throw logic_error("incorrect section id");
|
||||
}
|
||||
|
||||
uint16_t key = ((difficulty & 3) << 4) | (secid & 0x0F);
|
||||
uint16_t key = ((static_cast<size_t>(difficulty) & 3) << 4) | (secid & 0x0F);
|
||||
switch (mode) {
|
||||
case GameMode::NORMAL:
|
||||
break;
|
||||
|
||||
+8
-8
@@ -34,18 +34,18 @@ public:
|
||||
~RareItemSet() = default;
|
||||
|
||||
std::vector<ExpandedDrop> get_enemy_specs(
|
||||
GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid, uint8_t rt_index) const;
|
||||
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, uint8_t rt_index) const;
|
||||
std::vector<ExpandedDrop> get_box_specs(
|
||||
GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid, uint8_t area_norm) const;
|
||||
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, uint8_t area_norm) const;
|
||||
|
||||
bool has_entries_for_game_config(GameMode mode, Episode episode, uint8_t difficulty) const;
|
||||
bool has_entries_for_game_config(GameMode mode, Episode episode, Difficulty difficulty) const;
|
||||
|
||||
std::string serialize_afs(bool is_v1) const;
|
||||
std::string serialize_gsl(bool big_endian) const;
|
||||
std::string serialize_html(
|
||||
GameMode mode,
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
std::shared_ptr<const ItemNameIndex> name_index = nullptr,
|
||||
std::shared_ptr<const CommonItemSet> common_item_set = nullptr) const;
|
||||
phosg::JSON json(std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
FILE* stream,
|
||||
GameMode mode,
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
uint8_t section_id,
|
||||
std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
||||
void print_all_collections(FILE* stream, std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
||||
@@ -113,10 +113,10 @@ protected:
|
||||
|
||||
std::unordered_map<uint16_t, SpecCollection> collections;
|
||||
|
||||
const SpecCollection& get_collection(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid) const;
|
||||
const SpecCollection& get_collection(GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid) const;
|
||||
|
||||
static std::string gsl_entry_name_for_table(GameMode mode, Episode episode, uint8_t difficulty, uint8_t section_id);
|
||||
static uint16_t key_for_params(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid);
|
||||
static std::string gsl_entry_name_for_table(GameMode mode, Episode episode, Difficulty difficulty, uint8_t section_id);
|
||||
static uint16_t key_for_params(GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid);
|
||||
|
||||
static uint32_t expand_rate(uint8_t pc);
|
||||
static uint8_t compress_rate(uint32_t probability);
|
||||
|
||||
+30
-21
@@ -347,7 +347,7 @@ static asio::awaitable<void> on_login_complete(shared_ptr<Client> c) {
|
||||
if (!q) {
|
||||
c->log.info_f("There is no quest to enable server function calls for specific version {:08X}", c->specific_version);
|
||||
} else if (q) {
|
||||
auto vq = q->version(c->version(), 1);
|
||||
auto vq = q->version(c->version(), Language::ENGLISH);
|
||||
if (vq) {
|
||||
c->set_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
|
||||
c->set_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
|
||||
@@ -363,7 +363,7 @@ static asio::awaitable<void> on_login_complete(shared_ptr<Client> c) {
|
||||
lobby_data.guild_card_number = c->login->account->account_id;
|
||||
send_command_t(c, 0x64, 0x01, cmd);
|
||||
} else {
|
||||
c->log.info_f("Sending {} version of quest \"{}\"", char_for_language_code(vq->language), vq->meta.name);
|
||||
c->log.info_f("Sending {} version of quest \"{}\"", name_for_language(vq->language), vq->meta.name);
|
||||
string bin_filename = vq->bin_filename();
|
||||
string dat_filename = vq->dat_filename();
|
||||
string xb_filename = vq->xb_filename();
|
||||
@@ -613,7 +613,7 @@ static asio::awaitable<void> on_04_U(shared_ptr<Client> c, Channel::Message& msg
|
||||
for (const auto& file : index->all_files()) {
|
||||
send_patch_change_to_directory(c, path_directories, file->path_directories);
|
||||
|
||||
S_FileChecksumRequest_Patch_0C req = {c->patch_file_checksum_requests.size(), {file->name, 1}};
|
||||
S_FileChecksumRequest_Patch_0C req = {c->patch_file_checksum_requests.size(), {file->name, Language::ENGLISH}};
|
||||
c->channel->send(0x0C, 0x00, req);
|
||||
c->patch_file_checksum_requests.emplace_back(file);
|
||||
}
|
||||
@@ -667,7 +667,7 @@ asio::awaitable<void> on_10_U(shared_ptr<Client> c, Channel::Message&) {
|
||||
if (req.needs_update()) {
|
||||
send_patch_change_to_directory(c, path_directories, req.file->path_directories);
|
||||
|
||||
S_OpenFile_Patch_06 open_cmd = {0, req.file->size, {req.file->name, 1}};
|
||||
S_OpenFile_Patch_06 open_cmd = {0, req.file->size, {req.file->name, Language::ENGLISH}};
|
||||
c->channel->send(0x06, 0x00, open_cmd);
|
||||
|
||||
for (size_t x = 0; x < req.file->chunk_crcs.size(); x++) {
|
||||
@@ -1490,7 +1490,7 @@ static asio::awaitable<void> on_93_BB(shared_ptr<Client> c, Channel::Message& ms
|
||||
c->set_flag(Client::Flag::FORCE_ENGLISH_LANGUAGE_BB);
|
||||
}
|
||||
}
|
||||
c->channel->language = c->check_flag(Client::Flag::FORCE_ENGLISH_LANGUAGE_BB) ? 1 : base_cmd.language;
|
||||
c->channel->language = c->check_flag(Client::Flag::FORCE_ENGLISH_LANGUAGE_BB) ? Language::ENGLISH : base_cmd.language;
|
||||
|
||||
if (base_cmd.menu_id == MenuID::LOBBY) {
|
||||
c->preferred_lobby_id = base_cmd.preferred_lobby_id;
|
||||
@@ -2237,7 +2237,7 @@ static asio::awaitable<void> on_09(shared_ptr<Client> c, Channel::Message& msg)
|
||||
version_token,
|
||||
name_for_char_class(player->disp.visual.char_class),
|
||||
player->disp.stats.level + 1,
|
||||
char_for_language_code(game_c->language()));
|
||||
char_for_language(game_c->language()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2471,7 +2471,7 @@ void set_lobby_quest(shared_ptr<Lobby> l, shared_ptr<const Quest> q, bool substi
|
||||
l->allowed_drop_modes = l->quest->meta.allowed_drop_modes;
|
||||
l->drop_mode = l->quest->meta.default_drop_mode;
|
||||
}
|
||||
if (l->quest->meta.challenge_difficulty >= 0) {
|
||||
if (l->quest->meta.challenge_difficulty != Difficulty::UNKNOWN) {
|
||||
l->difficulty = l->quest->meta.challenge_difficulty;
|
||||
}
|
||||
l->create_item_creator();
|
||||
@@ -2491,7 +2491,7 @@ void set_lobby_quest(shared_ptr<Lobby> l, shared_ptr<const Quest> q, bool substi
|
||||
lc->channel->disconnect();
|
||||
break;
|
||||
}
|
||||
lc->log.info_f("Sending {} version of quest \"{}\"", char_for_language_code(vq->language), vq->meta.name);
|
||||
lc->log.info_f("Sending {} version of quest \"{}\"", name_for_language(vq->language), vq->meta.name);
|
||||
|
||||
string bin_filename = vq->bin_filename();
|
||||
string dat_filename = vq->dat_filename();
|
||||
@@ -2901,7 +2901,7 @@ static asio::awaitable<void> on_10_ep3_download_quest_menu(shared_ptr<Client> c,
|
||||
auto map = s->ep3_download_map_index->get(item_id);
|
||||
auto vm = map->version(c->language());
|
||||
auto name = vm->map->name.decode(vm->language);
|
||||
string filename = std::format("m{:06}p_{:c}.bin", map->map_number, tolower(char_for_language_code(vm->language)));
|
||||
string filename = std::format("m{:06}p_{:c}.bin", map->map_number, tolower(char_for_language(vm->language)));
|
||||
auto data = (c->version() == Version::GC_EP3_NTE) ? vm->trial_download() : vm->compressed(false);
|
||||
send_open_quest_file(c, name, filename, "", map->map_number, QuestFileType::EPISODE_3, data);
|
||||
co_return;
|
||||
@@ -4136,14 +4136,15 @@ static asio::awaitable<void> on_DF_BB(shared_ptr<Client> c, Channel::Message& ms
|
||||
if (!l->quest) {
|
||||
throw runtime_error("challenge mode difficulty config command sent in non-challenge game");
|
||||
}
|
||||
if (static_cast<uint32_t>(l->quest->meta.challenge_difficulty) != cmd.difficulty) {
|
||||
Difficulty cmd_difficulty = static_cast<Difficulty>(cmd.difficulty32.load());
|
||||
if (l->quest->meta.challenge_difficulty != cmd_difficulty) {
|
||||
throw runtime_error("incorrect difficulty level");
|
||||
}
|
||||
if (l->difficulty != cmd.difficulty) {
|
||||
l->difficulty = cmd.difficulty;
|
||||
if (l->difficulty != cmd_difficulty) {
|
||||
l->difficulty = cmd_difficulty;
|
||||
l->create_item_creator();
|
||||
}
|
||||
l->log.info_f("(Challenge mode) Difficulty set to {:02X}", l->difficulty);
|
||||
l->log.info_f("(Challenge mode) Difficulty set to {}", name_for_difficulty(l->difficulty));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4491,7 +4492,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
const std::string& password,
|
||||
Episode episode,
|
||||
GameMode mode,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
bool allow_v1,
|
||||
shared_ptr<Lobby> watched_lobby,
|
||||
shared_ptr<Episode3::BattleRecordPlayer> battle_player) {
|
||||
@@ -4503,7 +4504,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
throw invalid_argument("incorrect episode number");
|
||||
}
|
||||
|
||||
if (difficulty > 3) {
|
||||
if (static_cast<size_t>(difficulty) > 3) {
|
||||
throw invalid_argument("incorrect difficulty level");
|
||||
}
|
||||
|
||||
@@ -4527,7 +4528,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
game->difficulty = difficulty;
|
||||
game->allowed_versions = s->compatibility_groups.at(static_cast<size_t>(creator_c->version()));
|
||||
static_assert(NUM_VERSIONS == 14, "Don't forget to update the group compatibility restrictions");
|
||||
if (!allow_v1 || (difficulty > 2) || (mode == GameMode::CHALLENGE) || (mode == GameMode::SOLO)) {
|
||||
if (!allow_v1 || (difficulty == Difficulty::ULTIMATE) || (mode == GameMode::CHALLENGE) || (mode == GameMode::SOLO)) {
|
||||
game->forbid_version(Version::DC_NTE);
|
||||
game->forbid_version(Version::DC_11_2000);
|
||||
game->forbid_version(Version::DC_V1);
|
||||
@@ -4701,7 +4702,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
if (game->mode == GameMode::CHALLENGE) {
|
||||
game->rare_enemy_rates = s->rare_enemy_rates_challenge;
|
||||
} else {
|
||||
game->rare_enemy_rates = s->rare_enemy_rates_by_difficulty.at(game->difficulty);
|
||||
game->rare_enemy_rates = s->rare_enemy_rates(game->difficulty);
|
||||
}
|
||||
|
||||
if (game->episode != Episode::EP3) {
|
||||
@@ -4736,7 +4737,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
|
||||
if (quest_flag_rewrites && !quest_flag_rewrites->empty()) {
|
||||
IntegralExpression::Env env = {
|
||||
.flags = &p->quest_flags.data.at(difficulty),
|
||||
.flags = &p->quest_flags.for_difficulty(difficulty),
|
||||
.challenge_records = &p->challenge_records,
|
||||
.team = creator_c->team(),
|
||||
.num_players = 1,
|
||||
@@ -4788,7 +4789,15 @@ static asio::awaitable<void> on_0C_C1_E7_EC(shared_ptr<Client> c, Channel::Messa
|
||||
shared_ptr<Lobby> game;
|
||||
if (is_pre_v1(c->version())) {
|
||||
const auto& cmd = check_size_t<C_CreateGame_DCNTE>(msg.data);
|
||||
game = create_game_generic(s, c, cmd.name.decode(c->language()), cmd.password.decode(c->language()), Episode::EP1, GameMode::NORMAL, 0, true);
|
||||
game = create_game_generic(
|
||||
s,
|
||||
c,
|
||||
cmd.name.decode(c->language()),
|
||||
cmd.password.decode(c->language()),
|
||||
Episode::EP1,
|
||||
GameMode::NORMAL,
|
||||
Difficulty::NORMAL,
|
||||
true);
|
||||
|
||||
} else {
|
||||
const auto& cmd = check_size_t<C_CreateGame_DC_V3_0C_C1_Ep3_EC>(msg.data);
|
||||
@@ -4952,11 +4961,11 @@ static asio::awaitable<void> on_6F(shared_ptr<Client> c, Channel::Message& msg)
|
||||
} catch (const out_of_range&) {
|
||||
throw std::logic_error("cannot find patch enable quest after it was previously found during login");
|
||||
}
|
||||
auto vq = q->version(is_ep3(c->version()) ? Version::GC_V3 : c->version(), 1);
|
||||
auto vq = q->version(is_ep3(c->version()) ? Version::GC_V3 : c->version(), Language::ENGLISH);
|
||||
if (!vq) {
|
||||
throw std::logic_error("cannot find patch enable quest version after it was previously found during login");
|
||||
}
|
||||
c->log.info_f("Sending {} version of quest \"{}\"", char_for_language_code(vq->language), vq->meta.name);
|
||||
c->log.info_f("Sending {} version of quest \"{}\"", name_for_language(vq->language), vq->meta.name);
|
||||
string bin_filename = vq->bin_filename();
|
||||
string dat_filename = vq->dat_filename();
|
||||
string xb_filename = vq->xb_filename();
|
||||
|
||||
@@ -13,7 +13,7 @@ std::shared_ptr<Lobby> create_game_generic(
|
||||
const std::string& password = "",
|
||||
Episode episode = Episode::EP1,
|
||||
GameMode mode = GameMode::NORMAL,
|
||||
uint8_t difficulty = 0,
|
||||
Difficulty difficulty = Difficulty::NORMAL,
|
||||
bool allow_v1 = false,
|
||||
std::shared_ptr<Lobby> watched_lobby = nullptr,
|
||||
std::shared_ptr<Episode3::BattleRecordPlayer> battle_player = nullptr);
|
||||
|
||||
+46
-24
@@ -805,7 +805,7 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
unknown_a6_nte(cmd.unknown_a6),
|
||||
bonus_hp_from_materials(0),
|
||||
bonus_tp_from_materials(0),
|
||||
language(0),
|
||||
language(Language::JAPANESE),
|
||||
player_tag(0x00010000),
|
||||
guild_card_number(guild_card_number),
|
||||
unknown_a6(0),
|
||||
@@ -829,7 +829,7 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
Parsed6x70Data::Parsed6x70Data(
|
||||
const G_SyncPlayerDispAndInventory_DC112000_6x70& cmd,
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
Version from_version,
|
||||
bool from_client_customization)
|
||||
: from_version(from_version),
|
||||
@@ -922,8 +922,8 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
this->floor = cmd.floor;
|
||||
this->xb_user_id = this->default_xb_user_id();
|
||||
this->xb_unknown_a16 = cmd.unknown_a16;
|
||||
this->name = cmd.name.decode(cmd.base.language);
|
||||
this->visual.name.encode(this->name, cmd.base.language);
|
||||
this->name = cmd.name.decode(this->language);
|
||||
this->visual.name.encode(this->name, this->language);
|
||||
}
|
||||
|
||||
G_SyncPlayerDispAndInventory_DCNTE_6x70 Parsed6x70Data::as_dc_nte(shared_ptr<ServerState> s) const {
|
||||
@@ -1051,7 +1051,7 @@ G_SyncPlayerDispAndInventory_XB_6x70 Parsed6x70Data::as_xb(shared_ptr<ServerStat
|
||||
return ret;
|
||||
}
|
||||
|
||||
G_SyncPlayerDispAndInventory_BB_6x70 Parsed6x70Data::as_bb(shared_ptr<ServerState> s, uint8_t language) const {
|
||||
G_SyncPlayerDispAndInventory_BB_6x70 Parsed6x70Data::as_bb(shared_ptr<ServerState> s, Language language) const {
|
||||
G_SyncPlayerDispAndInventory_BB_6x70 ret;
|
||||
ret.base = this->base_v1(true);
|
||||
ret.name.encode(this->name, language);
|
||||
@@ -1116,7 +1116,7 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
attack_status_effect(base.attack_status_effect),
|
||||
defense_status_effect(base.defense_status_effect),
|
||||
unused_status_effect(base.unused_status_effect),
|
||||
language(base.language),
|
||||
language(static_cast<Language>(base.language32.load())),
|
||||
player_tag(base.player_tag),
|
||||
guild_card_number(guild_card_number), // Ignore the client's GC#
|
||||
unknown_a6(base.unknown_a6),
|
||||
@@ -1140,7 +1140,7 @@ G_6x70_Base_V1 Parsed6x70Data::base_v1(bool is_v3) const {
|
||||
ret.attack_status_effect = this->attack_status_effect;
|
||||
ret.defense_status_effect = this->defense_status_effect;
|
||||
ret.unused_status_effect = this->unused_status_effect;
|
||||
ret.language = this->language;
|
||||
ret.language32 = static_cast<size_t>(this->language);
|
||||
ret.player_tag = this->player_tag;
|
||||
ret.guild_card_number = this->guild_card_number;
|
||||
ret.unknown_a6 = this->unknown_a6;
|
||||
@@ -2805,6 +2805,7 @@ DropReconcileResult reconcile_drop_request_with_map(
|
||||
shared_ptr<Client> c,
|
||||
G_SpecializableItemDropRequest_6xA2& cmd,
|
||||
Episode episode,
|
||||
Difficulty difficulty,
|
||||
uint8_t event,
|
||||
shared_ptr<MapState> map,
|
||||
bool mark_drop) {
|
||||
@@ -2857,7 +2858,7 @@ DropReconcileResult reconcile_drop_request_with_map(
|
||||
} else {
|
||||
if (map) {
|
||||
res.ene_st = map->enemy_state_for_index(version, cmd.floor, cmd.entity_index);
|
||||
EnemyType type = res.ene_st->type(version, episode, event);
|
||||
EnemyType type = res.ene_st->type(version, episode, difficulty, event);
|
||||
c->log.info_f("Drop check for E-{:03X} {}", res.ene_st->e_id, phosg::name_for_enum(type));
|
||||
res.effective_rt_index = type_definition_for_enemy(type).rt_index;
|
||||
// rt_indexes in Episode 4 don't match those sent in the command; we just
|
||||
@@ -2921,7 +2922,7 @@ static asio::awaitable<void> on_entity_drop_item_request(shared_ptr<Client> c, S
|
||||
// mode, so that we can correctly mark enemies and objects as having dropped
|
||||
// their items in persistent games.
|
||||
G_SpecializableItemDropRequest_6xA2 cmd = normalize_drop_request(msg.data, msg.size);
|
||||
auto rec = reconcile_drop_request_with_map(c, cmd, l->episode, l->event, l->map_state, true);
|
||||
auto rec = reconcile_drop_request_with_map(c, cmd, l->episode, l->difficulty, l->event, l->map_state, true);
|
||||
|
||||
ServerDropMode drop_mode = l->drop_mode;
|
||||
switch (drop_mode) {
|
||||
@@ -3058,7 +3059,8 @@ static asio::awaitable<void> on_set_quest_flag(shared_ptr<Client> c, SubcommandM
|
||||
co_return;
|
||||
}
|
||||
|
||||
uint16_t flag_num, difficulty, action;
|
||||
uint16_t flag_num, action;
|
||||
Difficulty difficulty;
|
||||
if (is_v1_or_v2(c->version()) && (c->version() != Version::GC_NTE)) {
|
||||
const auto& cmd = msg.check_size_t<G_UpdateQuestFlag_DC_PC_6x75>();
|
||||
flag_num = cmd.flag;
|
||||
@@ -3068,12 +3070,12 @@ static asio::awaitable<void> on_set_quest_flag(shared_ptr<Client> c, SubcommandM
|
||||
const auto& cmd = msg.check_size_t<G_UpdateQuestFlag_V3_BB_6x75>();
|
||||
flag_num = cmd.flag;
|
||||
action = cmd.action;
|
||||
difficulty = cmd.difficulty;
|
||||
difficulty = static_cast<Difficulty>(cmd.difficulty16.load());
|
||||
}
|
||||
|
||||
// The client explicitly checks action for both 0 and 1 - any other value
|
||||
// means no operation is performed.
|
||||
if ((flag_num >= 0x400) || (difficulty > 3) || (action > 1)) {
|
||||
if ((flag_num >= 0x400) || (static_cast<size_t>(difficulty) > 3) || (action > 1)) {
|
||||
co_return;
|
||||
}
|
||||
bool should_set = (action == 0);
|
||||
@@ -3110,9 +3112,9 @@ static asio::awaitable<void> on_set_quest_flag(shared_ptr<Client> c, SubcommandM
|
||||
// On Normal, Dark Falz does not have a third phase, so send the drop
|
||||
// request after the end of the second phase. On all other difficulty
|
||||
// levels, send it after the third phase.
|
||||
if ((difficulty == 0) && (flag_num == 0x0035)) {
|
||||
if ((difficulty == Difficulty::NORMAL) && (flag_num == 0x0035)) {
|
||||
boss_enemy_type = EnemyType::DARK_FALZ_2;
|
||||
} else if ((difficulty != 0) && (flag_num == 0x0037)) {
|
||||
} else if ((difficulty != Difficulty::NORMAL) && (flag_num == 0x0037)) {
|
||||
boss_enemy_type = EnemyType::DARK_FALZ_3;
|
||||
}
|
||||
} else if (is_ep2 && (flag_num == 0x0057) && (area == 0x0D)) {
|
||||
@@ -3426,13 +3428,20 @@ static asio::awaitable<void> on_update_enemy_state(shared_ptr<Client> c, Subcomm
|
||||
}
|
||||
auto ene_st = l->map_state->enemy_state_for_index(c->version(), c->floor, cmd.enemy_index);
|
||||
uint32_t src_flags = is_big_endian(c->version()) ? bswap32(cmd.game_flags) : cmd.game_flags.load();
|
||||
if (l->difficulty == 3) {
|
||||
if (l->difficulty == Difficulty::ULTIMATE) {
|
||||
src_flags = (src_flags & 0xFFFFFFC0) | (ene_st->game_flags & 0x0000003F);
|
||||
}
|
||||
ene_st->game_flags = src_flags;
|
||||
ene_st->total_damage = cmd.total_damage;
|
||||
ene_st->set_last_hit_by_client_id(c->lobby_client_id);
|
||||
l->log.info_f("E-{:03X} updated to damage={} game_flags={:08X}", ene_st->e_id, ene_st->total_damage, ene_st->game_flags);
|
||||
auto& alias_ene_st = ene_st->alias_ene_st;
|
||||
if (alias_ene_st) {
|
||||
alias_ene_st->game_flags = ene_st->game_flags;
|
||||
alias_ene_st->total_damage = ene_st->total_damage;
|
||||
alias_ene_st->server_flags = ene_st->server_flags;
|
||||
l->log.info_f("E-{:03X} updated via alias from E-{:03X}", alias_ene_st->e_id, ene_st->e_id);
|
||||
}
|
||||
|
||||
for (auto lc : l->clients) {
|
||||
if (lc && (lc != c)) {
|
||||
@@ -3485,7 +3494,7 @@ static asio::awaitable<void> on_set_enemy_low_game_flags_ultimate(shared_ptr<Cli
|
||||
co_return;
|
||||
}
|
||||
auto l = c->require_lobby();
|
||||
if (!l->is_game() || (l->difficulty != 3)) {
|
||||
if (!l->is_game() || (l->difficulty != Difficulty::ULTIMATE)) {
|
||||
co_return;
|
||||
}
|
||||
|
||||
@@ -3888,7 +3897,7 @@ static uint32_t base_exp_for_enemy_type(
|
||||
shared_ptr<const BattleParamsIndex> bp_index,
|
||||
EnemyType enemy_type,
|
||||
Episode current_episode,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
uint8_t area,
|
||||
bool is_solo) {
|
||||
// Always try the current episode first. If the current episode is Ep4, try
|
||||
@@ -3918,7 +3927,7 @@ static uint32_t base_exp_for_enemy_type(
|
||||
try {
|
||||
const auto& bp_table = bp_index->get_table(is_solo, episode);
|
||||
uint32_t bp_index = type_definition_for_enemy(enemy_type).bp_index;
|
||||
return bp_table.stats[difficulty][bp_index].experience;
|
||||
return bp_table.stats_for_index(difficulty, bp_index).experience;
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
}
|
||||
@@ -3971,16 +3980,16 @@ static asio::awaitable<void> on_steal_exp_bb(shared_ptr<Client> c, SubcommandMes
|
||||
co_return;
|
||||
}
|
||||
|
||||
auto type = ene_st->type(c->version(), l->episode, l->event);
|
||||
auto type = ene_st->type(c->version(), l->episode, l->difficulty, l->event);
|
||||
uint32_t enemy_exp = base_exp_for_enemy_type(
|
||||
s->battle_params, type, l->episode, l->difficulty, ene_st->super_ene->floor, l->mode == GameMode::SOLO);
|
||||
|
||||
// Note: The original code checks if special.type is 9, 10, or 11, and skips
|
||||
// applying the android bonus if so. We don't do anything for those special
|
||||
// types, so we don't check for that here.
|
||||
float percent = special.amount + ((l->difficulty == 3) && char_class_is_android(p->disp.visual.char_class) ? 30 : 0);
|
||||
float percent = special.amount + ((l->difficulty == Difficulty::ULTIMATE) && char_class_is_android(p->disp.visual.char_class) ? 30 : 0);
|
||||
float ep2_factor = (l->episode == Episode::EP2) ? 1.3 : 1.0;
|
||||
uint32_t stolen_exp = max<uint32_t>(min<uint32_t>((enemy_exp * percent * ep2_factor) / 100.0f, (l->difficulty + 1) * 20), 1);
|
||||
uint32_t stolen_exp = max<uint32_t>(min<uint32_t>((enemy_exp * percent * ep2_factor) / 100.0f, (static_cast<size_t>(l->difficulty) + 1) * 20), 1);
|
||||
if (c->check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
c->log.info_f("Stolen EXP from E-{:03X} with enemy_exp={} percent={:g} stolen_exp={}",
|
||||
ene_st->e_id, enemy_exp, percent, stolen_exp);
|
||||
@@ -4012,17 +4021,20 @@ static asio::awaitable<void> on_enemy_exp_request_bb(shared_ptr<Client> c, Subco
|
||||
// relevant players EXP then, if they deserve it).
|
||||
if (!ene_st->ever_hit_by_client_id(c->lobby_client_id) ||
|
||||
(ene_st->server_flags & MapState::EnemyState::Flag::EXP_GIVEN)) {
|
||||
l->log.info_f("EXP already given for this enemy; ignoring request");
|
||||
co_return;
|
||||
}
|
||||
ene_st->server_flags |= MapState::EnemyState::Flag::EXP_GIVEN;
|
||||
|
||||
auto type = ene_st->type(c->version(), l->episode, l->event);
|
||||
auto type = ene_st->type(c->version(), l->episode, l->difficulty, l->event);
|
||||
double base_exp = base_exp_for_enemy_type(
|
||||
s->battle_params, type, l->episode, l->difficulty, ene_st->super_ene->floor, l->mode == GameMode::SOLO);
|
||||
l->log.info_f("Base EXP for this enemy ({}) is {:g}", phosg::name_for_enum(type), base_exp);
|
||||
|
||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||
auto lc = l->clients[client_id];
|
||||
if (!lc) {
|
||||
l->log.info_f("No client in slot {}", client_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -4038,14 +4050,19 @@ static asio::awaitable<void> on_enemy_exp_request_bb(shared_ptr<Client> c, Subco
|
||||
double rate_factor;
|
||||
if (lc->character_file()->disp.stats.level >= 199) {
|
||||
rate_factor = 0.0;
|
||||
l->log.info_f("Client in slot {} is level 200 and cannot receive EXP", client_id);
|
||||
} else if (ene_st->last_hit_by_client_id(client_id)) {
|
||||
rate_factor = max<double>(1.0, exp_share_multiplier);
|
||||
l->log.info_f("Client in slot {} killed this enemy; EXP rate is {:g}", client_id, rate_factor);
|
||||
} else if (ene_st->ever_hit_by_client_id(client_id)) {
|
||||
rate_factor = max<double>(0.8, exp_share_multiplier);
|
||||
l->log.info_f("Client in slot {} tagged this enemy; EXP rate is {:g}", client_id, rate_factor);
|
||||
} else if (lc->floor == ene_st->super_ene->floor) {
|
||||
rate_factor = max<double>(0.0, exp_share_multiplier);
|
||||
l->log.info_f("Client in slot {} shared this enemy; EXP rate is {:g}", client_id, rate_factor);
|
||||
} else {
|
||||
rate_factor = 0.0;
|
||||
l->log.info_f("Client in slot {} is not near this enemy; EXP rate is {:g}", client_id, rate_factor);
|
||||
}
|
||||
|
||||
if (rate_factor > 0.0) {
|
||||
@@ -4061,6 +4078,7 @@ static asio::awaitable<void> on_enemy_exp_request_bb(shared_ptr<Client> c, Subco
|
||||
l->base_exp_multiplier *
|
||||
l->challenge_exp_multiplier *
|
||||
(is_ep2 ? 1.3 : 1.0);
|
||||
l->log.info_f("Client in slot {} receives {} EXP", client_id, player_exp);
|
||||
if (lc->check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_fmt(lc, "$C5+{} E-{:03X} {}", player_exp, ene_st->e_id, phosg::name_for_enum(type));
|
||||
}
|
||||
@@ -5025,9 +5043,9 @@ static asio::awaitable<void> on_quest_F95E_result_bb(shared_ptr<Client> c, Subco
|
||||
const auto& cmd = msg.check_size_t<G_RequestItemDropFromQuest_BB_6xE0>();
|
||||
auto s = c->require_server_state();
|
||||
|
||||
size_t count = (cmd.type > 0x03) ? 1 : (l->difficulty + 1);
|
||||
size_t count = (cmd.type > 0x03) ? 1 : (static_cast<size_t>(l->difficulty) + 1);
|
||||
for (size_t z = 0; z < count; z++) {
|
||||
const auto& results = s->quest_F95E_results.at(cmd.type).at(l->difficulty);
|
||||
const auto& results = s->quest_F95E_results.at(cmd.type).at(static_cast<size_t>(l->difficulty));
|
||||
if (results.empty()) {
|
||||
throw runtime_error("invalid result type");
|
||||
}
|
||||
@@ -5606,3 +5624,7 @@ asio::awaitable<void> on_subcommand_multi(shared_ptr<Client> c, Channel::Message
|
||||
offset += cmd_size;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO; // Dark Falz EXP doesn't work
|
||||
// TODO; // Guild Card Search doesn't work
|
||||
// TODO; // Team Search doesn't work
|
||||
|
||||
@@ -29,6 +29,7 @@ DropReconcileResult reconcile_drop_request_with_map(
|
||||
std::shared_ptr<Client> c,
|
||||
G_SpecializableItemDropRequest_6xA2& cmd,
|
||||
Episode episode,
|
||||
Difficulty difficulty,
|
||||
uint8_t event,
|
||||
std::shared_ptr<MapState> map,
|
||||
bool mark_drop);
|
||||
@@ -50,7 +51,7 @@ public:
|
||||
StatusEffectState attack_status_effect;
|
||||
StatusEffectState defense_status_effect;
|
||||
StatusEffectState unused_status_effect;
|
||||
uint32_t language = 0;
|
||||
Language language = Language::JAPANESE;
|
||||
uint32_t player_tag = 0;
|
||||
uint32_t guild_card_number = 0;
|
||||
uint32_t unknown_a6 = 0;
|
||||
@@ -79,7 +80,7 @@ public:
|
||||
Parsed6x70Data(
|
||||
const G_SyncPlayerDispAndInventory_DC112000_6x70& cmd,
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
Version from_version,
|
||||
bool from_client_customization);
|
||||
Parsed6x70Data(
|
||||
@@ -108,7 +109,7 @@ public:
|
||||
G_SyncPlayerDispAndInventory_DC_PC_6x70 as_dc_pc(std::shared_ptr<ServerState> s, Version to_version) const;
|
||||
G_SyncPlayerDispAndInventory_GC_6x70 as_gc_gcnte(std::shared_ptr<ServerState> s, Version to_version) const;
|
||||
G_SyncPlayerDispAndInventory_XB_6x70 as_xb(std::shared_ptr<ServerState> s) const;
|
||||
G_SyncPlayerDispAndInventory_BB_6x70 as_bb(std::shared_ptr<ServerState> s, uint8_t language) const;
|
||||
G_SyncPlayerDispAndInventory_BB_6x70 as_bb(std::shared_ptr<ServerState> s, Language language) const;
|
||||
|
||||
uint64_t default_xb_user_id() const;
|
||||
void clear_v1_unused_item_fields();
|
||||
|
||||
@@ -50,7 +50,7 @@ ReplaySession::Client::Client(shared_ptr<asio::io_context> io_context, uint64_t
|
||||
: id(id),
|
||||
port(port),
|
||||
version(version),
|
||||
channel(make_shared<PeerChannel>(io_context, this->version, 1, std::format("R-{:X}", this->id))) {}
|
||||
channel(make_shared<PeerChannel>(io_context, this->version, Language::ENGLISH, std::format("R-{:X}", this->id))) {}
|
||||
|
||||
string ReplaySession::Client::str() const {
|
||||
return std::format("Client[{}, T-{}, {}]", this->id, this->port, phosg::name_for_enum(this->version));
|
||||
|
||||
+28
-28
@@ -15,10 +15,10 @@ struct DefaultSymbolChatEntry {
|
||||
array<uint16_t, 4> corner_objects;
|
||||
array<SymbolChatFacePart, 12> face_parts;
|
||||
|
||||
SaveFileSymbolChatEntryBB to_entry(uint8_t language) const {
|
||||
SaveFileSymbolChatEntryBB to_entry(Language language) const {
|
||||
SaveFileSymbolChatEntryBB ret;
|
||||
ret.present = 1;
|
||||
ret.name.encode(this->language_to_name.at(language), language);
|
||||
ret.name.encode(this->language_to_name.at(static_cast<size_t>(language)), language);
|
||||
ret.spec.spec = this->spec;
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
ret.spec.corner_objects[z] = this->corner_objects[z];
|
||||
@@ -350,7 +350,7 @@ PlayerDispDataBBPreview PSOBBCharacterFile::to_preview() const {
|
||||
|
||||
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const PlayerVisualConfig& visual,
|
||||
const std::string& name,
|
||||
shared_ptr<const LevelTable> level_table) {
|
||||
@@ -540,7 +540,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
|
||||
|
||||
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const PlayerDispDataBBPreview& preview,
|
||||
shared_ptr<const LevelTable> level_table) {
|
||||
return PSOBBCharacterFile::create_from_config(
|
||||
@@ -550,13 +550,13 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCNTECharacterFile::Character& src) {
|
||||
auto ret = PSOBBCharacterFile::create_from_config(
|
||||
src.guild_card.guild_card_number,
|
||||
0,
|
||||
Language::JAPANESE,
|
||||
src.disp.visual,
|
||||
src.disp.visual.name.decode(0),
|
||||
src.disp.visual.name.decode(Language::JAPANESE),
|
||||
nullptr);
|
||||
ret->inventory = src.inventory;
|
||||
ret->inventory.decode_from_client(Version::DC_V1);
|
||||
uint8_t language = ret->inventory.language;
|
||||
Language language = ret->inventory.language;
|
||||
ret->disp = src.disp.to_bb(language, language);
|
||||
ret->validation_flags = 0;
|
||||
ret->creation_timestamp = src.creation_timestamp;
|
||||
@@ -585,11 +585,11 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODC1
|
||||
src.guild_card.guild_card_number,
|
||||
src.inventory.language,
|
||||
src.disp.visual,
|
||||
src.disp.visual.name.decode(0),
|
||||
src.disp.visual.name.decode(Language::JAPANESE),
|
||||
nullptr);
|
||||
ret->inventory = src.inventory;
|
||||
ret->inventory.decode_from_client(Version::DC_V1);
|
||||
uint8_t language = ret->inventory.language;
|
||||
Language language = ret->inventory.language;
|
||||
ret->disp = src.disp.to_bb(language, language);
|
||||
ret->validation_flags = 0;
|
||||
ret->creation_timestamp = src.creation_timestamp;
|
||||
@@ -628,11 +628,11 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCV
|
||||
src.guild_card.guild_card_number,
|
||||
src.inventory.language,
|
||||
src.disp.visual,
|
||||
src.disp.visual.name.decode(0),
|
||||
src.disp.visual.name.decode(Language::JAPANESE),
|
||||
nullptr);
|
||||
ret->inventory = src.inventory;
|
||||
ret->inventory.decode_from_client(Version::DC_V1);
|
||||
uint8_t language = ret->inventory.language;
|
||||
Language language = ret->inventory.language;
|
||||
ret->disp = src.disp.to_bb(language, language);
|
||||
ret->validation_flags = src.validation_flags;
|
||||
ret->creation_timestamp = src.creation_timestamp;
|
||||
@@ -661,11 +661,11 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCV
|
||||
src.guild_card.guild_card_number,
|
||||
src.inventory.language,
|
||||
src.disp.visual,
|
||||
src.disp.visual.name.decode(0),
|
||||
src.disp.visual.name.decode(Language::JAPANESE),
|
||||
nullptr);
|
||||
ret->inventory = src.inventory;
|
||||
ret->inventory.decode_from_client(Version::DC_V2);
|
||||
uint8_t language = ret->inventory.language;
|
||||
Language language = ret->inventory.language;
|
||||
ret->disp = src.disp.to_bb(language, language);
|
||||
ret->validation_flags = src.validation_flags;
|
||||
ret->creation_timestamp = src.creation_timestamp;
|
||||
@@ -701,14 +701,14 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCN
|
||||
src.guild_card.guild_card_number,
|
||||
src.inventory.language,
|
||||
src.disp.visual,
|
||||
src.disp.visual.name.decode(0),
|
||||
src.disp.visual.name.decode(Language::JAPANESE),
|
||||
nullptr);
|
||||
ret->inventory = src.inventory;
|
||||
// Note: We intentionally do not call ret->inventory.decode_from_client here.
|
||||
// This is because the GC client byteswaps data2 in each item before sending
|
||||
// it to the server in the 61 and 98 commands, but GetExtendedPlayerInfo does
|
||||
// not do this, so the data2 fields are already in the correct order here.
|
||||
uint8_t language = ret->inventory.language;
|
||||
Language language = ret->inventory.language;
|
||||
ret->disp = src.disp.to_bb(language, language);
|
||||
ret->validation_flags = src.validation_flags;
|
||||
ret->creation_timestamp = src.creation_timestamp;
|
||||
@@ -742,14 +742,14 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCC
|
||||
src.guild_card.guild_card_number,
|
||||
src.inventory.language,
|
||||
src.disp.visual,
|
||||
src.disp.visual.name.decode(0),
|
||||
src.disp.visual.name.decode(Language::JAPANESE),
|
||||
nullptr);
|
||||
ret->inventory = src.inventory;
|
||||
// Note: We intentionally do not call ret->inventory.decode_from_client here.
|
||||
// This is because the GC client byteswaps data2 in each item before sending
|
||||
// it to the server in the 61 and 98 commands, but GetExtendedPlayerInfo does
|
||||
// not do this, so the data2 fields are already in the correct order here.
|
||||
uint8_t language = ret->inventory.language;
|
||||
Language language = ret->inventory.language;
|
||||
ret->disp = src.disp.to_bb(language, language);
|
||||
ret->validation_flags = src.validation_flags;
|
||||
ret->creation_timestamp = src.creation_timestamp;
|
||||
@@ -793,10 +793,10 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOGCE
|
||||
src.guild_card.guild_card_number,
|
||||
src.inventory.language,
|
||||
src.disp.visual,
|
||||
src.disp.visual.name.decode(0),
|
||||
src.disp.visual.name.decode(Language::JAPANESE),
|
||||
nullptr);
|
||||
ret->inventory = src.inventory;
|
||||
uint8_t language = ret->inventory.language;
|
||||
Language language = ret->inventory.language;
|
||||
ret->disp = src.disp.to_bb(language, language);
|
||||
ret->validation_flags = src.validation_flags;
|
||||
ret->creation_timestamp = src.creation_timestamp;
|
||||
@@ -841,11 +841,11 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOXBC
|
||||
src.guild_card.guild_card_number,
|
||||
src.inventory.language,
|
||||
src.disp.visual,
|
||||
src.disp.visual.name.decode(0),
|
||||
src.disp.visual.name.decode(Language::JAPANESE),
|
||||
nullptr);
|
||||
ret->inventory = src.inventory;
|
||||
ret->inventory.decode_from_client(Version::XB_V3);
|
||||
uint8_t language = ret->inventory.language;
|
||||
Language language = ret->inventory.language;
|
||||
ret->disp = src.disp.to_bb(language, language);
|
||||
ret->validation_flags = src.validation_flags;
|
||||
ret->creation_timestamp = src.creation_timestamp;
|
||||
@@ -886,7 +886,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSOXBC
|
||||
}
|
||||
|
||||
PSODCNTECharacterFile::Character PSOBBCharacterFile::as_dc_nte(uint64_t hardware_id) const {
|
||||
uint8_t language = this->inventory.language;
|
||||
Language language = this->inventory.language;
|
||||
|
||||
PSODCNTECharacterFile::Character ret;
|
||||
ret.inventory = this->inventory;
|
||||
@@ -912,7 +912,7 @@ PSODCNTECharacterFile::Character PSOBBCharacterFile::as_dc_nte(uint64_t hardware
|
||||
}
|
||||
|
||||
PSODC112000CharacterFile::Character PSOBBCharacterFile::as_11_2000(uint64_t hardware_id) const {
|
||||
uint8_t language = this->inventory.language;
|
||||
Language language = this->inventory.language;
|
||||
|
||||
PSODC112000CharacterFile::Character ret;
|
||||
ret.inventory = this->inventory;
|
||||
@@ -949,7 +949,7 @@ PSODC112000CharacterFile::Character PSOBBCharacterFile::as_11_2000(uint64_t hard
|
||||
}
|
||||
|
||||
PSOBBCharacterFile::operator PSODCV1CharacterFile::Character() const {
|
||||
uint8_t language = this->inventory.language;
|
||||
Language language = this->inventory.language;
|
||||
|
||||
PSODCV1CharacterFile::Character ret;
|
||||
ret.inventory = this->inventory;
|
||||
@@ -981,7 +981,7 @@ PSOBBCharacterFile::operator PSODCV1CharacterFile::Character() const {
|
||||
}
|
||||
|
||||
PSOBBCharacterFile::operator PSODCV2CharacterFile::Character() const {
|
||||
uint8_t language = this->inventory.language;
|
||||
Language language = this->inventory.language;
|
||||
|
||||
PSODCV2CharacterFile::Character ret;
|
||||
ret.inventory = this->inventory;
|
||||
@@ -1022,7 +1022,7 @@ PSOBBCharacterFile::operator PSODCV2CharacterFile::Character() const {
|
||||
}
|
||||
|
||||
PSOBBCharacterFile::operator PSOGCNTECharacterFileCharacter() const {
|
||||
uint8_t language = this->inventory.language;
|
||||
Language language = this->inventory.language;
|
||||
|
||||
PSOGCNTECharacterFileCharacter ret;
|
||||
ret.inventory = this->inventory;
|
||||
@@ -1060,7 +1060,7 @@ PSOBBCharacterFile::operator PSOGCNTECharacterFileCharacter() const {
|
||||
}
|
||||
|
||||
PSOBBCharacterFile::operator PSOGCCharacterFile::Character() const {
|
||||
uint8_t language = this->inventory.language;
|
||||
Language language = this->inventory.language;
|
||||
|
||||
PSOGCCharacterFile::Character ret;
|
||||
ret.inventory = this->inventory;
|
||||
@@ -1108,7 +1108,7 @@ PSOBBCharacterFile::operator PSOGCCharacterFile::Character() const {
|
||||
}
|
||||
|
||||
PSOBBCharacterFile::operator PSOXBCharacterFile::Character() const {
|
||||
uint8_t language = this->inventory.language;
|
||||
Language language = this->inventory.language;
|
||||
|
||||
PSOXBCharacterFile::Character ret;
|
||||
ret.inventory = this->inventory;
|
||||
|
||||
@@ -193,7 +193,7 @@ struct SaveFileChatShortcutEntryT {
|
||||
/* 40:54:A4 */
|
||||
|
||||
template <bool RetBE, TextEncoding RetEncoding, size_t RetMaxSize>
|
||||
SaveFileChatShortcutEntryT<RetBE, RetEncoding, RetMaxSize> convert(uint8_t language) const {
|
||||
SaveFileChatShortcutEntryT<RetBE, RetEncoding, RetMaxSize> convert(Language language) const {
|
||||
SaveFileChatShortcutEntryT<RetBE, RetEncoding, RetMaxSize> ret;
|
||||
ret.type = this->type;
|
||||
switch (ret.type) {
|
||||
@@ -268,7 +268,7 @@ struct PSOPCSystemFile { // PSO______COM
|
||||
// assumption that Sega didn't change much between versions.
|
||||
/* 0004 */ le_int16_t music_volume = 0;
|
||||
/* 0006 */ int8_t sound_volume = 0;
|
||||
/* 0007 */ uint8_t language = 1;
|
||||
/* 0007 */ Language language = Language::ENGLISH;
|
||||
/* 0008 */ le_int32_t server_time_delta_frames = 1728000;
|
||||
/* 000C */ parray<le_uint16_t, 0x10> unknown_a4; // Last one is always 0x1234?
|
||||
/* 002C */ parray<uint8_t, 0x100> event_flags;
|
||||
@@ -281,7 +281,7 @@ struct PSOGCSystemFile {
|
||||
/* 0000 */ be_uint32_t checksum = 0;
|
||||
/* 0004 */ be_int16_t music_volume = 0; // 0 = full volume; -250 = min volume
|
||||
/* 0006 */ int8_t sound_volume = 0; // 0 = full volume; -100 = min volume
|
||||
/* 0007 */ uint8_t language = 1;
|
||||
/* 0007 */ Language language = Language::ENGLISH;
|
||||
// This field stores the effective time zone offset between the server and
|
||||
// client, in frames. The default value is 1728000, which corresponds to 16
|
||||
// hours. This is recomputed when the client receives a B1 command.
|
||||
@@ -309,7 +309,7 @@ struct PSOXBSystemFile {
|
||||
/* 0000 */ le_uint32_t checksum = 0;
|
||||
/* 0004 */ le_int16_t music_volume = -50;
|
||||
/* 0006 */ int8_t sound_volume = 0;
|
||||
/* 0007 */ uint8_t language = 0;
|
||||
/* 0007 */ Language language = Language::JAPANESE;
|
||||
/* 0008 */ be_int32_t server_time_delta_frames = 200;
|
||||
/* 000C */ be_uint16_t udp_behavior = 0; // 0 = auto, 1 = on, 2 = off
|
||||
/* 000E */ be_uint16_t surround_sound_enabled = 0;
|
||||
@@ -338,7 +338,7 @@ struct PSOBBMinimalSystemFile {
|
||||
/* 0000 */ be_uint32_t checksum = 0;
|
||||
/* 0004 */ be_int16_t music_volume = 0;
|
||||
/* 0006 */ int8_t sound_volume = 0;
|
||||
/* 0007 */ uint8_t language = 0;
|
||||
/* 0007 */ Language language = Language::JAPANESE;
|
||||
/* 0008 */ be_int32_t server_time_delta_frames = 1728000;
|
||||
/* 000C */ be_uint16_t udp_behavior = 0; // 0 = auto, 1 = on, 2 = off
|
||||
/* 000E */ be_uint16_t surround_sound_enabled = 0;
|
||||
@@ -871,13 +871,13 @@ struct PSOBBCharacterFile {
|
||||
|
||||
static std::shared_ptr<PSOBBCharacterFile> create_from_config(
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const PlayerVisualConfig& visual,
|
||||
const std::string& name,
|
||||
std::shared_ptr<const LevelTable> level_table);
|
||||
static std::shared_ptr<PSOBBCharacterFile> create_from_preview(
|
||||
uint32_t guild_card_number,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
const PlayerDispDataBBPreview& preview,
|
||||
std::shared_ptr<const LevelTable> level_table);
|
||||
static std::shared_ptr<PSOBBCharacterFile> create_from_file(const PSODCNTECharacterFile::Character& src);
|
||||
@@ -978,7 +978,7 @@ struct PSODCV1V2GuildCardFile {
|
||||
/* 0004 */ parray<PSODCGuildCardFileEntry, 100> entries;
|
||||
/* 3204 */ le_int16_t music_volume = 0;
|
||||
/* 3206 */ int8_t sound_volume = 0;
|
||||
/* 3207 */ uint8_t language = 1;
|
||||
/* 3207 */ Language language = Language::ENGLISH;
|
||||
/* 3208 */ le_int32_t server_time_delta_frames = 540000; // 648000 on DCv1
|
||||
/* 320C */ le_uint32_t creation_timestamp = 0;
|
||||
/* 3210 */ le_uint32_t round2_seed = 0;
|
||||
|
||||
+19
-19
@@ -306,7 +306,7 @@ void send_set_guild_card_number(shared_ptr<Client> c) {
|
||||
}
|
||||
|
||||
void send_patch_enter_directory(shared_ptr<Client> c, const string& dir) {
|
||||
S_EnterDirectory_Patch_09 cmd = {{dir, 1}};
|
||||
S_EnterDirectory_Patch_09 cmd = {{dir, Language::ENGLISH}};
|
||||
c->channel->send(0x09, 0x00, cmd);
|
||||
}
|
||||
|
||||
@@ -793,9 +793,9 @@ void send_complete_player_bb(shared_ptr<Client> c) {
|
||||
auto sys = c->system_file(true);
|
||||
auto team = c->team();
|
||||
if (c->check_flag(Client::Flag::FORCE_ENGLISH_LANGUAGE_BB)) {
|
||||
p->inventory.language = 1;
|
||||
p->guild_card.language = 1;
|
||||
sys->language = 1;
|
||||
p->inventory.language = Language::ENGLISH;
|
||||
p->guild_card.language = Language::ENGLISH;
|
||||
sys->language = Language::ENGLISH;
|
||||
}
|
||||
|
||||
SC_SyncSaveFiles_BB_E7 cmd;
|
||||
@@ -1016,7 +1016,7 @@ void send_text_or_scrolling_message(shared_ptr<ServerState> s, const std::string
|
||||
|
||||
string prepare_chat_data(
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
uint8_t from_client_id,
|
||||
const string& from_name,
|
||||
const string& text,
|
||||
@@ -1024,7 +1024,7 @@ string prepare_chat_data(
|
||||
string data;
|
||||
|
||||
if (version == Version::BB_V4) {
|
||||
data.append(language ? "\tE" : "\tJ");
|
||||
data.append((language == Language::JAPANESE) ? "\tJ" : "\tE");
|
||||
}
|
||||
data.append(from_name);
|
||||
if (version == Version::DC_NTE) {
|
||||
@@ -1037,7 +1037,7 @@ string prepare_chat_data(
|
||||
}
|
||||
|
||||
if (uses_utf16(version)) {
|
||||
data.append(language ? "\tE" : "\tJ");
|
||||
data.append((language == Language::JAPANESE) ? "\tJ" : "\tE");
|
||||
data.append(text);
|
||||
return tt_utf8_to_utf16(data);
|
||||
} else if (version == Version::DC_NTE) {
|
||||
@@ -1291,7 +1291,7 @@ void send_guild_card_dc_pc_gc_t(
|
||||
uint32_t guild_card_number,
|
||||
const string& name,
|
||||
const string& description,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
uint8_t section_id,
|
||||
uint8_t char_class) {
|
||||
CmdT cmd;
|
||||
@@ -1315,7 +1315,7 @@ void send_guild_card_xb(
|
||||
uint64_t xb_user_id,
|
||||
const string& name,
|
||||
const string& description,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
uint8_t section_id,
|
||||
uint8_t char_class) {
|
||||
G_SendGuildCard_XB_6x06 cmd;
|
||||
@@ -1341,7 +1341,7 @@ static void send_guild_card_bb(
|
||||
const string& name,
|
||||
const string& team_name,
|
||||
const string& description,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
uint8_t section_id,
|
||||
uint8_t char_class) {
|
||||
G_SendGuildCard_BB_6x06 cmd;
|
||||
@@ -1366,7 +1366,7 @@ void send_guild_card(
|
||||
const string& name,
|
||||
const string& team_name,
|
||||
const string& description,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
uint8_t section_id,
|
||||
uint8_t char_class) {
|
||||
switch (ch->version) {
|
||||
@@ -1573,7 +1573,7 @@ void send_game_menu_t(
|
||||
auto& e = entries.emplace_back();
|
||||
e.menu_id = MenuID::GAME;
|
||||
e.item_id = l->lobby_id;
|
||||
e.difficulty_tag = (is_ep3(c->version()) ? 0x0A : (l->difficulty + 0x22));
|
||||
e.difficulty_tag = (is_ep3(c->version()) ? 0x0A : (static_cast<size_t>(l->difficulty) + 0x22));
|
||||
e.num_players = l->count_clients();
|
||||
if (is_dc(c->version())) {
|
||||
e.episode = l->version_is_allowed(Version::DC_V1) ? 1 : 0;
|
||||
@@ -2952,12 +2952,12 @@ void send_game_flag_state_t(shared_ptr<Client> c) {
|
||||
if (l->quest_flags_known) { // Not all flags known; send multiple 6x75s
|
||||
phosg::StringWriter w;
|
||||
bool use_v3_cmd = !is_v1_or_v2(c->version()) || (c->version() == Version::GC_NTE);
|
||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
if ((difficulty != l->difficulty) && !use_v3_cmd) {
|
||||
continue;
|
||||
}
|
||||
const auto& diff_flags = l->quest_flag_values->data.at(difficulty);
|
||||
const auto& diff_known_flags = l->quest_flags_known->data.at(difficulty);
|
||||
const auto& diff_flags = l->quest_flag_values->for_difficulty(difficulty);
|
||||
const auto& diff_known_flags = l->quest_flags_known->for_difficulty(difficulty);
|
||||
for (uint8_t z = 0; z < diff_known_flags.data.size(); z++) {
|
||||
uint8_t known_flags = diff_known_flags.data[z];
|
||||
if (!known_flags) {
|
||||
@@ -2969,7 +2969,7 @@ void send_game_flag_state_t(shared_ptr<Client> c) {
|
||||
uint16_t flag_num = ((z << 3) | sh);
|
||||
if (use_v3_cmd) {
|
||||
w.put(G_UpdateQuestFlag_V3_BB_6x75{
|
||||
{{0x75, 0x03, 0x0000}, flag_num, (((flag_values << sh) & 0x80) ? 0 : 1)}, difficulty, 0});
|
||||
{{0x75, 0x03, 0x0000}, flag_num, (((flag_values << sh) & 0x80) ? 0 : 1)}, static_cast<uint16_t>(difficulty), 0});
|
||||
} else {
|
||||
w.put(G_UpdateQuestFlag_DC_PC_6x75{
|
||||
{0x75, 0x02, 0x0000}, flag_num, (((flag_values << sh) & 0x80) ? 0 : 1)});
|
||||
@@ -3541,7 +3541,7 @@ string ep3_description_for_client(shared_ptr<Client> c) {
|
||||
"{} CLv{} {}",
|
||||
name_for_char_class(p->disp.visual.char_class),
|
||||
p->disp.stats.level + 1,
|
||||
char_for_language_code(p->inventory.language));
|
||||
char_for_language(p->inventory.language));
|
||||
}
|
||||
|
||||
template <typename RulesT>
|
||||
@@ -3783,7 +3783,7 @@ void send_ep3_tournament_match_result(shared_ptr<Lobby> l, uint32_t meseta_rewar
|
||||
cmd.num_players_per_team = match->preceding_a->winner_team->max_players;
|
||||
cmd.winner_team_id = (match->preceding_b->winner_team == match->winner_team);
|
||||
cmd.meseta_amount = meseta_reward;
|
||||
cmd.meseta_reward_text.encode("You got %s meseta!", 1);
|
||||
cmd.meseta_reward_text.encode("You got %s meseta!", Language::ENGLISH);
|
||||
if ((lc->version() != Version::GC_EP3_NTE) &&
|
||||
!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
uint8_t mask_key = (phosg::random_object<uint32_t>() % 0xFF) + 1;
|
||||
@@ -3854,7 +3854,7 @@ void send_ep3_update_game_metadata(shared_ptr<Lobby> l) {
|
||||
}
|
||||
cmd.total_spectators = total_spectators;
|
||||
cmd.text_size = text.size();
|
||||
cmd.text.encode(text, 1);
|
||||
cmd.text.encode(text, Language::ENGLISH);
|
||||
for (auto c : watcher_l->clients) {
|
||||
if (c) {
|
||||
if ((c->version() == Version::GC_EP3) && !(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||
|
||||
+2
-2
@@ -231,7 +231,7 @@ void send_text_or_scrolling_message(std::shared_ptr<ServerState> s, const std::s
|
||||
|
||||
std::string prepare_chat_data(
|
||||
Version version,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
uint8_t from_client_id,
|
||||
const std::string& from_name,
|
||||
const std::string& text,
|
||||
@@ -298,7 +298,7 @@ void send_guild_card(
|
||||
const std::string& name,
|
||||
const std::string& team_name,
|
||||
const std::string& description,
|
||||
uint8_t language,
|
||||
Language language,
|
||||
uint8_t section_id,
|
||||
uint8_t char_class);
|
||||
void send_guild_card(std::shared_ptr<Client> c, std::shared_ptr<Client> source);
|
||||
|
||||
+30
-34
@@ -394,7 +394,7 @@ shared_ptr<const vector<string>> ServerState::information_contents_for_client(sh
|
||||
return is_v1_or_v2(c->version()) ? this->information_contents_v2 : this->information_contents_v3;
|
||||
}
|
||||
|
||||
size_t ServerState::default_min_level_for_game(Version version, Episode episode, uint8_t difficulty) const {
|
||||
size_t ServerState::default_min_level_for_game(Version version, Episode episode, Difficulty difficulty) const {
|
||||
const auto& min_levels = is_v4(version)
|
||||
? this->min_levels_v4
|
||||
: is_v3(version)
|
||||
@@ -402,21 +402,21 @@ size_t ServerState::default_min_level_for_game(Version version, Episode episode,
|
||||
: this->min_levels_v1_v2;
|
||||
switch (episode) {
|
||||
case Episode::EP1:
|
||||
return min_levels[0].at(difficulty);
|
||||
return min_levels[0].at(static_cast<size_t>(difficulty));
|
||||
case Episode::EP2:
|
||||
return min_levels[1].at(difficulty);
|
||||
return min_levels[1].at(static_cast<size_t>(difficulty));
|
||||
case Episode::EP3:
|
||||
return 0;
|
||||
case Episode::EP4:
|
||||
return min_levels[2].at(difficulty);
|
||||
return min_levels[2].at(static_cast<size_t>(difficulty));
|
||||
default:
|
||||
throw runtime_error("invalid episode");
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const SetDataTableBase> ServerState::set_data_table(
|
||||
Version version, Episode episode, GameMode mode, uint8_t difficulty) const {
|
||||
bool use_ult_tables = ((episode == Episode::EP1) && (difficulty == 3) && !is_v1(version) && (version != Version::PC_NTE));
|
||||
Version version, Episode episode, GameMode mode, Difficulty difficulty) const {
|
||||
bool use_ult_tables = ((episode == Episode::EP1) && (difficulty == Difficulty::ULTIMATE) && !is_v1(version) && (version != Version::PC_NTE));
|
||||
if (mode == GameMode::SOLO && is_v4(version)) {
|
||||
return use_ult_tables ? this->bb_solo_set_data_table_ep1_ult : this->bb_solo_set_data_table;
|
||||
}
|
||||
@@ -1280,15 +1280,16 @@ void ServerState::load_config_early() {
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
size_t diff_index = static_cast<size_t>(difficulty);
|
||||
shared_ptr<const MapState::RareEnemyRates> prev = MapState::DEFAULT_RARE_ENEMIES;
|
||||
try {
|
||||
string key = "RareEnemyRates-";
|
||||
key += token_name_for_difficulty(z);
|
||||
this->rare_enemy_rates_by_difficulty[z] = make_shared<MapState::RareEnemyRates>(this->config_json->at(key));
|
||||
prev = this->rare_enemy_rates_by_difficulty[z];
|
||||
key += token_name_for_difficulty(difficulty);
|
||||
this->rare_enemy_rates_by_difficulty[diff_index] = make_shared<MapState::RareEnemyRates>(this->config_json->at(key));
|
||||
prev = this->rare_enemy_rates_by_difficulty[diff_index];
|
||||
} catch (const out_of_range&) {
|
||||
this->rare_enemy_rates_by_difficulty[z] = prev;
|
||||
this->rare_enemy_rates_by_difficulty[diff_index] = prev;
|
||||
}
|
||||
}
|
||||
try {
|
||||
@@ -1599,7 +1600,7 @@ void ServerState::load_maps() {
|
||||
auto objects_data = this->load_map_file(Version::GC_EP3, "map_city_on_battle_o.dat");
|
||||
auto enemies_data = this->load_map_file(Version::GC_EP3, "map_city_on_battle_e.dat");
|
||||
if (objects_data || enemies_data) {
|
||||
uint32_t free_play_key = this->free_play_key(Episode::EP3, GameMode::NORMAL, 0, 0, 0, 0);
|
||||
uint32_t free_play_key = this->free_play_key(Episode::EP3, GameMode::NORMAL, Difficulty::NORMAL, 0, 0, 0);
|
||||
auto map_file = make_shared<MapFile>(0, objects_data, enemies_data, nullptr);
|
||||
new_map_file_for_source_hash.emplace(map_file->source_hash(), map_file);
|
||||
new_map_files_for_free_play_key[free_play_key].at(static_cast<size_t>(Version::GC_EP3)) = map_file;
|
||||
@@ -1611,15 +1612,13 @@ void ServerState::load_maps() {
|
||||
|
||||
config_log.info_f("Loading free play map files");
|
||||
for (Version v : ALL_ARPG_SEMANTIC_VERSIONS) {
|
||||
const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
for (Episode episode : episodes) {
|
||||
for (Episode episode : ALL_EPISODES_V4) {
|
||||
if ((episode == Episode::EP2 && is_v1_or_v2(v) && (v != Version::GC_NTE)) ||
|
||||
(episode == Episode::EP4 && !is_v4(v))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
for (GameMode mode : modes) {
|
||||
for (GameMode mode : ALL_GAME_MODES_V4) {
|
||||
if ((mode == GameMode::BATTLE) && is_pre_v1(v)) {
|
||||
continue;
|
||||
}
|
||||
@@ -1629,8 +1628,8 @@ void ServerState::load_maps() {
|
||||
if ((mode == GameMode::SOLO && !is_v4(v))) {
|
||||
continue;
|
||||
}
|
||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
||||
if ((difficulty == 3) && is_v1(v)) {
|
||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||
if ((difficulty == Difficulty::ULTIMATE) && is_v1(v)) {
|
||||
continue;
|
||||
}
|
||||
auto sdt = this->set_data_table(v, episode, mode, difficulty);
|
||||
@@ -1705,7 +1704,7 @@ void ServerState::load_maps() {
|
||||
}
|
||||
|
||||
shared_ptr<const SuperMap> ServerState::get_free_play_supermap(
|
||||
Episode episode, GameMode mode, uint8_t difficulty, uint8_t floor, uint32_t layout, uint32_t entities) {
|
||||
Episode episode, GameMode mode, Difficulty difficulty, uint8_t floor, uint32_t layout, uint32_t entities) {
|
||||
uint32_t free_play_key = this->free_play_key(episode, mode, difficulty, floor, layout, entities);
|
||||
try {
|
||||
return this->supermap_for_free_play_key.at(free_play_key);
|
||||
@@ -1759,10 +1758,7 @@ shared_ptr<const SuperMap> ServerState::get_free_play_supermap(
|
||||
}
|
||||
|
||||
vector<shared_ptr<const SuperMap>> ServerState::supermaps_for_variations(
|
||||
Episode episode,
|
||||
GameMode mode,
|
||||
uint8_t difficulty,
|
||||
const Variations& variations) {
|
||||
Episode episode, GameMode mode, Difficulty difficulty, const Variations& variations) {
|
||||
vector<shared_ptr<const SuperMap>> ret;
|
||||
for (size_t floor = 0; floor < 0x12; floor++) {
|
||||
Variations::Entry e;
|
||||
@@ -1881,7 +1877,7 @@ void ServerState::load_word_select_table() {
|
||||
unique_ptr<UnicodeTextSet> pc_unitxt_data;
|
||||
if (this->text_index) {
|
||||
config_log.debug_f("(Word select) Using PC_V2 unitxt_e.prs from text index");
|
||||
pc_unitxt_collection = &this->text_index->get(Version::PC_V2, 1, 35);
|
||||
pc_unitxt_collection = &this->text_index->get(Version::PC_V2, Language::ENGLISH, 35);
|
||||
} else {
|
||||
config_log.debug_f("(Word select) Loading PC_V2 unitxt_e.prs");
|
||||
pc_unitxt_data = make_unique<UnicodeTextSet>(phosg::load_file("system/text-sets/pc-v2/unitxt_e.prs"));
|
||||
@@ -1930,25 +1926,25 @@ shared_ptr<ItemNameIndex> ServerState::create_item_name_index_for_version(
|
||||
shared_ptr<const TextIndex> text_index) const {
|
||||
switch (limits->version) {
|
||||
case Version::DC_NTE:
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::DC_NTE, 0, 2));
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::DC_NTE, Language::JAPANESE, 2));
|
||||
case Version::DC_11_2000:
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::DC_11_2000, 1, 2));
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::DC_11_2000, Language::ENGLISH, 2));
|
||||
case Version::DC_V1:
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::DC_V1, 1, 2));
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::DC_V1, Language::ENGLISH, 2));
|
||||
case Version::DC_V2:
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::DC_V2, 1, 3));
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::DC_V2, Language::ENGLISH, 3));
|
||||
case Version::PC_NTE:
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::PC_NTE, 1, 3));
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::PC_NTE, Language::ENGLISH, 3));
|
||||
case Version::PC_V2:
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::PC_V2, 1, 3));
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::PC_V2, Language::ENGLISH, 3));
|
||||
case Version::GC_NTE:
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::GC_NTE, 1, 0));
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::GC_NTE, Language::ENGLISH, 0));
|
||||
case Version::GC_V3:
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::GC_V3, 1, 0));
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::GC_V3, Language::ENGLISH, 0));
|
||||
case Version::XB_V3:
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::XB_V3, 1, 0));
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::XB_V3, Language::ENGLISH, 0));
|
||||
case Version::BB_V4:
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::BB_V4, 1, 1));
|
||||
return make_shared<ItemNameIndex>(pmt, limits, text_index->get(Version::BB_V4, Language::ENGLISH, 1));
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+13
-6
@@ -199,7 +199,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
std::unordered_map<std::string, std::shared_ptr<const RareItemSet>> rare_item_sets;
|
||||
std::shared_ptr<const ArmorRandomSet> armor_random_set;
|
||||
std::shared_ptr<const ToolRandomSet> tool_random_set;
|
||||
std::array<std::shared_ptr<const WeaponRandomSet>, 4> weapon_random_sets;
|
||||
std::array<std::shared_ptr<const WeaponRandomSet>, 4> weapon_random_sets; // Keyed oin difficulty
|
||||
std::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set;
|
||||
std::array<std::shared_ptr<const ItemParameterTable>, NUM_VERSIONS> item_parameter_tables;
|
||||
std::shared_ptr<const ItemTranslationTable> item_translation_table;
|
||||
@@ -344,7 +344,14 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
std::shared_ptr<const Menu> proxy_destinations_menu(Version version) const;
|
||||
const std::vector<std::pair<std::string, uint16_t>>& proxy_destinations(Version version) const;
|
||||
|
||||
std::shared_ptr<const SetDataTableBase> set_data_table(Version version, Episode episode, GameMode mode, uint8_t difficulty) const;
|
||||
std::shared_ptr<const SetDataTableBase> set_data_table(Version version, Episode episode, GameMode mode, Difficulty difficulty) const;
|
||||
|
||||
inline std::shared_ptr<const WeaponRandomSet> weapon_random_set(Difficulty difficulty) const {
|
||||
return this->weapon_random_sets.at(static_cast<size_t>(difficulty));
|
||||
}
|
||||
inline std::shared_ptr<const MapState::RareEnemyRates> rare_enemy_rates(Difficulty difficulty) const {
|
||||
return this->rare_enemy_rates_by_difficulty.at(static_cast<size_t>(difficulty));
|
||||
}
|
||||
|
||||
std::shared_ptr<const LevelTable> level_table(Version version) const;
|
||||
std::shared_ptr<const ItemParameterTable> item_parameter_table(Version version) const;
|
||||
@@ -376,7 +383,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
|
||||
std::shared_ptr<const std::vector<std::string>> information_contents_for_client(std::shared_ptr<const Client> c) const;
|
||||
|
||||
size_t default_min_level_for_game(Version version, Episode episode, uint8_t difficulty) const;
|
||||
size_t default_min_level_for_game(Version version, Episode episode, Difficulty difficulty) const;
|
||||
|
||||
void set_port_configuration(const std::vector<PortConfiguration>& port_configs);
|
||||
|
||||
@@ -391,7 +398,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
std::vector<PortConfiguration> parse_port_configuration(const phosg::JSON& json) const;
|
||||
|
||||
static constexpr uint32_t free_play_key(
|
||||
Episode episode, GameMode mode, uint8_t difficulty, uint8_t floor, uint32_t layout, uint32_t entities) {
|
||||
Episode episode, GameMode mode, Difficulty difficulty, uint8_t floor, uint32_t layout, uint32_t entities) {
|
||||
return (static_cast<uint32_t>(episode) << 28) |
|
||||
(static_cast<uint32_t>(mode) << 26) |
|
||||
(static_cast<uint32_t>(difficulty) << 24) |
|
||||
@@ -400,11 +407,11 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
(static_cast<uint32_t>(entities) << 0);
|
||||
}
|
||||
std::shared_ptr<const SuperMap> get_free_play_supermap(
|
||||
Episode episode, GameMode mode, uint8_t difficulty, uint8_t floor, uint32_t layout, uint32_t entities);
|
||||
Episode episode, GameMode mode, Difficulty difficulty, uint8_t floor, uint32_t layout, uint32_t entities);
|
||||
std::vector<std::shared_ptr<const SuperMap>> supermaps_for_variations(
|
||||
Episode episode,
|
||||
GameMode mode,
|
||||
uint8_t difficulty,
|
||||
Difficulty difficulty,
|
||||
const Variations& variations);
|
||||
|
||||
void create_default_lobbies();
|
||||
|
||||
@@ -940,7 +940,7 @@ ShellCommand c_show_slots(
|
||||
if (player.guild_card_number) {
|
||||
ret.emplace_back(format(" {}: {} => {} ({}, {}, {})",
|
||||
z, player.guild_card_number, player.name,
|
||||
char_for_language_code(player.language),
|
||||
char_for_language(player.language),
|
||||
name_for_char_class(player.char_class),
|
||||
name_for_section_id(player.section_id)));
|
||||
} else {
|
||||
|
||||
+30
-19
@@ -476,36 +476,36 @@ bool char_class_is_force(uint8_t cls) {
|
||||
return class_flags.at(cls) & ClassFlag::FORCE;
|
||||
}
|
||||
|
||||
const char* name_for_difficulty(uint8_t difficulty) {
|
||||
const char* name_for_difficulty(Difficulty difficulty) {
|
||||
static const array<const char*, 4> names = {
|
||||
"Normal", "Hard", "Very Hard", "Ultimate"};
|
||||
try {
|
||||
return names.at(difficulty);
|
||||
return names.at(static_cast<size_t>(difficulty));
|
||||
} catch (const out_of_range&) {
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
const char* token_name_for_difficulty(uint8_t difficulty) {
|
||||
const char* token_name_for_difficulty(Difficulty difficulty) {
|
||||
static const array<const char*, 4> names = {
|
||||
"Normal", "Hard", "VeryHard", "Ultimate"};
|
||||
try {
|
||||
return names.at(difficulty);
|
||||
return names.at(static_cast<size_t>(difficulty));
|
||||
} catch (const out_of_range&) {
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
char abbreviation_for_difficulty(uint8_t difficulty) {
|
||||
char abbreviation_for_difficulty(Difficulty difficulty) {
|
||||
static const array<char, 4> names = {'N', 'H', 'V', 'U'};
|
||||
try {
|
||||
return names.at(difficulty);
|
||||
return names.at(static_cast<size_t>(difficulty));
|
||||
} catch (const out_of_range&) {
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
const char* name_for_language_code(uint8_t language_code) {
|
||||
const char* name_for_language(Language language) {
|
||||
array<const char*, 8> names = {{"Japanese",
|
||||
"English",
|
||||
"German",
|
||||
@@ -514,39 +514,41 @@ const char* name_for_language_code(uint8_t language_code) {
|
||||
"Simplified Chinese",
|
||||
"Traditional Chinese",
|
||||
"Korean"}};
|
||||
return (language_code < 8) ? names[language_code] : "Unknown";
|
||||
size_t lang_index = static_cast<size_t>(language);
|
||||
return (lang_index < 8) ? names[lang_index] : "Unknown";
|
||||
}
|
||||
|
||||
char char_for_language_code(uint8_t language_code) {
|
||||
return (language_code < 8) ? "JEGFSBTK"[language_code] : '?';
|
||||
char char_for_language(Language language) {
|
||||
size_t lang_index = static_cast<size_t>(language);
|
||||
return (lang_index < 8) ? "JEGFSBTK"[lang_index] : '?';
|
||||
}
|
||||
|
||||
uint8_t language_code_for_char(char language_char) {
|
||||
Language language_for_char(char language_char) {
|
||||
switch (language_char) {
|
||||
case 'J':
|
||||
case 'j':
|
||||
return 0;
|
||||
return Language::JAPANESE;
|
||||
case 'E':
|
||||
case 'e':
|
||||
return 1;
|
||||
return Language::ENGLISH;
|
||||
case 'G':
|
||||
case 'g':
|
||||
return 2;
|
||||
return Language::GERMAN;
|
||||
case 'F':
|
||||
case 'f':
|
||||
return 3;
|
||||
return Language::FRENCH;
|
||||
case 'S':
|
||||
case 's':
|
||||
return 4;
|
||||
return Language::SPANISH;
|
||||
case 'B':
|
||||
case 'b':
|
||||
return 5;
|
||||
return Language::SIMPLIFIED_CHINESE;
|
||||
case 'T':
|
||||
case 't':
|
||||
return 6;
|
||||
return Language::TRADITIONAL_CHINESE;
|
||||
case 'K':
|
||||
case 'k':
|
||||
return 7;
|
||||
return Language::KOREAN;
|
||||
default:
|
||||
throw runtime_error("unknown language");
|
||||
}
|
||||
@@ -809,3 +811,12 @@ const array<size_t, 4> DEFAULT_MIN_LEVELS_V123({0, 19, 39, 79});
|
||||
const array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP1({0, 19, 39, 79});
|
||||
const array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP2({0, 29, 49, 89});
|
||||
const array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP4({0, 39, 79, 109});
|
||||
|
||||
const array<GameMode, 2> ALL_GAME_MODES_V1 = {GameMode::NORMAL, GameMode::BATTLE};
|
||||
const array<GameMode, 3> ALL_GAME_MODES_V23 = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE};
|
||||
const array<GameMode, 4> ALL_GAME_MODES_V4 = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
||||
const array<Episode, 1> ALL_EPISODES_V12 = {Episode::EP1};
|
||||
const array<Episode, 2> ALL_EPISODES_V3 = {Episode::EP1, Episode::EP2};
|
||||
const array<Episode, 3> ALL_EPISODES_V4 = {Episode::EP1, Episode::EP2, Episode::EP4};
|
||||
const array<Difficulty, 3> ALL_DIFFICULTIES_V1 = {Difficulty::NORMAL, Difficulty::HARD, Difficulty::VERY_HARD};
|
||||
const array<Difficulty, 4> ALL_DIFFICULTIES_V234 = {Difficulty::NORMAL, Difficulty::HARD, Difficulty::VERY_HARD, Difficulty::ULTIMATE};
|
||||
|
||||
+35
-6
@@ -17,6 +17,26 @@ enum class Episode {
|
||||
EP4 = 4,
|
||||
};
|
||||
|
||||
enum class Difficulty : uint8_t {
|
||||
NORMAL = 0,
|
||||
HARD = 1,
|
||||
VERY_HARD = 2,
|
||||
ULTIMATE = 3,
|
||||
UNKNOWN = 0xFF,
|
||||
};
|
||||
|
||||
enum class Language : uint8_t {
|
||||
JAPANESE = 0,
|
||||
ENGLISH = 1,
|
||||
GERMAN = 2,
|
||||
FRENCH = 3,
|
||||
SPANISH = 4,
|
||||
SIMPLIFIED_CHINESE = 5,
|
||||
TRADITIONAL_CHINESE = 6,
|
||||
KOREAN = 7,
|
||||
UNKNOWN = 0xFF,
|
||||
};
|
||||
|
||||
bool episode_has_arpg_semantics(Episode ep);
|
||||
const char* name_for_episode(Episode ep);
|
||||
const char* token_name_for_episode(Episode ep);
|
||||
@@ -63,13 +83,13 @@ bool char_class_is_hunter(uint8_t cls);
|
||||
bool char_class_is_ranger(uint8_t cls);
|
||||
bool char_class_is_force(uint8_t cls);
|
||||
|
||||
const char* name_for_difficulty(uint8_t difficulty);
|
||||
const char* token_name_for_difficulty(uint8_t difficulty);
|
||||
char abbreviation_for_difficulty(uint8_t difficulty);
|
||||
const char* name_for_difficulty(Difficulty difficulty);
|
||||
const char* token_name_for_difficulty(Difficulty difficulty);
|
||||
char abbreviation_for_difficulty(Difficulty difficulty);
|
||||
|
||||
const char* name_for_language_code(uint8_t language_code);
|
||||
char char_for_language_code(uint8_t language_code);
|
||||
uint8_t language_code_for_char(char language_char);
|
||||
const char* name_for_language(Language language);
|
||||
char char_for_language(Language language);
|
||||
Language language_for_char(char language_char);
|
||||
|
||||
extern const std::vector<const char*> name_for_mag_color;
|
||||
extern const std::unordered_map<std::string, uint8_t> mag_color_for_name;
|
||||
@@ -110,3 +130,12 @@ extern const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V123;
|
||||
extern const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP1;
|
||||
extern const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP2;
|
||||
extern const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP4;
|
||||
|
||||
extern const std::array<GameMode, 2> ALL_GAME_MODES_V1;
|
||||
extern const std::array<GameMode, 3> ALL_GAME_MODES_V23;
|
||||
extern const std::array<GameMode, 4> ALL_GAME_MODES_V4;
|
||||
extern const std::array<Episode, 1> ALL_EPISODES_V12;
|
||||
extern const std::array<Episode, 2> ALL_EPISODES_V3;
|
||||
extern const std::array<Episode, 3> ALL_EPISODES_V4;
|
||||
extern const std::array<Difficulty, 3> ALL_DIFFICULTIES_V1;
|
||||
extern const std::array<Difficulty, 4> ALL_DIFFICULTIES_V234;
|
||||
|
||||
+27
-27
@@ -275,50 +275,50 @@ TextTranscoder tt_utf8_to_utf16("UTF-16LE", "UTF-8");
|
||||
TextTranscoder tt_ascii_to_utf8("UTF-8", "ASCII");
|
||||
TextTranscoder tt_utf8_to_ascii("ASCII", "UTF-8");
|
||||
|
||||
string tt_encode_marked_optional(const string& utf8, uint8_t default_language, bool is_utf16) {
|
||||
string tt_encode_marked_optional(const string& utf8, Language default_language, bool is_utf16) {
|
||||
if (is_utf16) {
|
||||
return tt_utf8_to_utf16(utf8);
|
||||
} else {
|
||||
if (default_language) {
|
||||
try {
|
||||
return tt_utf8_to_8859(utf8);
|
||||
} catch (const exception& e) {
|
||||
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
|
||||
}
|
||||
} else {
|
||||
if (default_language == Language::JAPANESE) {
|
||||
try {
|
||||
return tt_utf8_to_sega_sjis(utf8);
|
||||
} catch (const exception& e) {
|
||||
return "\tE" + tt_utf8_to_8859(utf8);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return tt_utf8_to_8859(utf8);
|
||||
} catch (const exception& e) {
|
||||
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string tt_encode_marked(const string& utf8, uint8_t default_language, bool is_utf16) {
|
||||
string tt_encode_marked(const string& utf8, Language default_language, bool is_utf16) {
|
||||
if (is_utf16) {
|
||||
string to_encode = "\t";
|
||||
to_encode += marker_for_language_code(default_language);
|
||||
to_encode += marker_for_language(default_language);
|
||||
to_encode += utf8;
|
||||
return tt_utf8_to_utf16(to_encode);
|
||||
} else {
|
||||
if (default_language) {
|
||||
if (default_language == Language::JAPANESE) {
|
||||
try {
|
||||
return "\tE" + tt_utf8_to_8859(utf8);
|
||||
} catch (const exception& e) {
|
||||
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
|
||||
} catch (const exception& e) {
|
||||
return "\tE" + tt_utf8_to_8859(utf8);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
|
||||
} catch (const exception& e) {
|
||||
return "\tE" + tt_utf8_to_8859(utf8);
|
||||
} catch (const exception& e) {
|
||||
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string tt_decode_marked(const string& data, uint8_t default_language, bool is_utf16) {
|
||||
string tt_decode_marked(const string& data, Language default_language, bool is_utf16) {
|
||||
if (is_utf16) {
|
||||
string ret = tt_utf16_to_utf8(data);
|
||||
if (ret.size() >= 2 && ret[0] == '\t' && is_language_marker_utf16(ret[1])) {
|
||||
@@ -333,7 +333,7 @@ string tt_decode_marked(const string& data, uint8_t default_language, bool is_ut
|
||||
return tt_8859_to_utf8(data.substr(2));
|
||||
}
|
||||
}
|
||||
return default_language ? tt_8859_to_utf8(data) : tt_sega_sjis_to_utf8(data);
|
||||
return (default_language == Language::JAPANESE) ? tt_sega_sjis_to_utf8(data) : tt_8859_to_utf8(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,20 +486,20 @@ string escape_player_name(const string& name) {
|
||||
}
|
||||
}
|
||||
|
||||
char marker_for_language_code(uint8_t language_code) {
|
||||
switch (language_code) {
|
||||
case 0:
|
||||
char marker_for_language(Language language) {
|
||||
switch (language) {
|
||||
case Language::JAPANESE:
|
||||
return 'J';
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case Language::ENGLISH:
|
||||
case Language::GERMAN:
|
||||
case Language::FRENCH:
|
||||
case Language::SPANISH:
|
||||
return 'E';
|
||||
case 5:
|
||||
case Language::SIMPLIFIED_CHINESE:
|
||||
return 'B';
|
||||
case 6:
|
||||
case Language::TRADITIONAL_CHINESE:
|
||||
return 'T';
|
||||
case 7:
|
||||
case Language::KOREAN:
|
||||
return 'K';
|
||||
default:
|
||||
return 'E';
|
||||
|
||||
+16
-15
@@ -11,6 +11,7 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "StaticGameData.hh"
|
||||
#include "Types.hh"
|
||||
|
||||
#define check_struct_size(StructT, Size) \
|
||||
@@ -81,11 +82,11 @@ extern TextTranscoder tt_utf8_to_utf16;
|
||||
extern TextTranscoder tt_ascii_to_utf8;
|
||||
extern TextTranscoder tt_utf8_to_ascii;
|
||||
|
||||
std::string tt_encode_marked_optional(const std::string& utf8, uint8_t default_language, bool is_utf16);
|
||||
std::string tt_encode_marked(const std::string& utf8, uint8_t default_language, bool is_utf16);
|
||||
std::string tt_decode_marked(const std::string& data, uint8_t default_language, bool is_utf16);
|
||||
std::string tt_encode_marked_optional(const std::string& utf8, Language default_language, bool is_utf16);
|
||||
std::string tt_encode_marked(const std::string& utf8, Language default_language, bool is_utf16);
|
||||
std::string tt_decode_marked(const std::string& data, Language default_language, bool is_utf16);
|
||||
|
||||
char marker_for_language_code(uint8_t language_code);
|
||||
char marker_for_language(Language language);
|
||||
bool is_language_marker_sjis_8859(char marker);
|
||||
bool is_language_marker_utf16(char marker);
|
||||
|
||||
@@ -467,7 +468,7 @@ struct pstring {
|
||||
pstring(const pstring<Encoding, Chars, BytesPerChar>& other) {
|
||||
memcpy(this->data, other.data, Bytes);
|
||||
}
|
||||
pstring(const std::string& s, uint8_t language) {
|
||||
pstring(const std::string& s, Language language) {
|
||||
this->encode(s, language);
|
||||
}
|
||||
pstring(pstring<Encoding, Chars, BytesPerChar>&& other) = delete;
|
||||
@@ -485,7 +486,7 @@ struct pstring {
|
||||
}
|
||||
pstring<Encoding, Chars, BytesPerChar>& operator=(pstring<Encoding, Chars, BytesPerChar>&& s) = delete;
|
||||
|
||||
void encode(const std::string& s, uint8_t client_language = 1) {
|
||||
void encode(const std::string& s, Language client_language = Language::ENGLISH) {
|
||||
try {
|
||||
switch (Encoding) {
|
||||
case TextEncoding::CHALLENGE8:
|
||||
@@ -513,7 +514,7 @@ struct pstring {
|
||||
break;
|
||||
} else if (s.size() <= 2 || s[0] != '\t' || s[1] == 'C') {
|
||||
std::string to_encode = "\t";
|
||||
to_encode += marker_for_language_code(client_language);
|
||||
to_encode += marker_for_language(client_language);
|
||||
to_encode += s;
|
||||
auto ret = tt_utf8_to_utf16(this->data, Bytes, to_encode.data(), to_encode.size(), true);
|
||||
this->clear_after_bytes(ret.bytes_written);
|
||||
@@ -536,7 +537,7 @@ struct pstring {
|
||||
break;
|
||||
}
|
||||
case TextEncoding::MARKED: {
|
||||
if (client_language == 0) {
|
||||
if (client_language == Language::JAPANESE) {
|
||||
try {
|
||||
auto ret = tt_utf8_to_sega_sjis(this->data, Bytes, s.data(), s.size(), true);
|
||||
this->clear_after_bytes(ret.bytes_written);
|
||||
@@ -592,7 +593,7 @@ struct pstring {
|
||||
}
|
||||
}
|
||||
|
||||
std::string decode(uint8_t client_language = 1) const {
|
||||
std::string decode(Language client_language = Language::ENGLISH) const {
|
||||
try {
|
||||
switch (Encoding) {
|
||||
case TextEncoding::CHALLENGE8: {
|
||||
@@ -623,16 +624,16 @@ struct pstring {
|
||||
size_t offset = 0;
|
||||
if (this->data[0] == '\t') {
|
||||
if (this->data[1] == 'J') {
|
||||
client_language = 0;
|
||||
client_language = Language::JAPANESE;
|
||||
offset = 2;
|
||||
} else if (this->data[1] != 'C') {
|
||||
client_language = 1;
|
||||
client_language = Language::ENGLISH;
|
||||
offset = 2;
|
||||
}
|
||||
}
|
||||
return client_language
|
||||
? tt_8859_to_utf8(&this->data[offset], this->used_chars_8() - offset)
|
||||
: tt_sega_sjis_to_utf8(&this->data[offset], this->used_chars_8() - offset);
|
||||
return (client_language == Language::JAPANESE)
|
||||
? tt_sega_sjis_to_utf8(&this->data[offset], this->used_chars_8() - offset)
|
||||
: tt_8859_to_utf8(&this->data[offset], this->used_chars_8() - offset);
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("unknown text encoding");
|
||||
@@ -650,7 +651,7 @@ struct pstring {
|
||||
return (memcmp(this->data, other.data, Bytes) != 0);
|
||||
}
|
||||
|
||||
bool eq(const std::string& other, uint8_t language = 1) const {
|
||||
bool eq(const std::string& other, Language language = Language::ENGLISH) const {
|
||||
return this->decode(language) == other;
|
||||
}
|
||||
|
||||
|
||||
+40
-40
@@ -454,54 +454,54 @@ TextIndex::TextIndex(
|
||||
: log("[TextIndex] ", static_game_data_log.min_level) {
|
||||
if (!directory.empty()) {
|
||||
auto add_version = [&](Version version, const string& subdirectory, function<shared_ptr<TextSet>(const string&, bool)> make_set) -> void {
|
||||
static const map<string, uint8_t> bintext_filenames({
|
||||
{"TextJapanese.pr2", 0x00},
|
||||
{"TextEnglish.pr2", 0x01},
|
||||
{"TextGerman.pr2", 0x02},
|
||||
{"TextFrench.pr2", 0x03},
|
||||
{"TextSpanish.pr2", 0x04},
|
||||
static const map<string, Language> bintext_filenames({
|
||||
{"TextJapanese.pr2", Language::JAPANESE},
|
||||
{"TextEnglish.pr2", Language::ENGLISH},
|
||||
{"TextGerman.pr2", Language::GERMAN},
|
||||
{"TextFrench.pr2", Language::FRENCH},
|
||||
{"TextSpanish.pr2", Language::SPANISH},
|
||||
});
|
||||
static const map<string, uint8_t> unitext_filenames({
|
||||
{"unitxt_j.prs", 0x00}, // PC/BB Japanese
|
||||
{"unitxt_e.prs", 0x01}, // PC/BB English
|
||||
{"unitxt_g.prs", 0x02}, // PC/BB German
|
||||
{"unitxt_f.prs", 0x03}, // PC/BB French
|
||||
{"unitxt_s.prs", 0x04}, // PC/BB Spanish
|
||||
{"unitxt_b.prs", 0x05}, // PC Simplified Chinese
|
||||
{"unitxt_cs.prs", 0x05}, // BB Simplified Chinese
|
||||
{"unitxt_t.prs", 0x06}, // PC Traditional Chinese
|
||||
{"unitxt_ct.prs", 0x06}, // BB Traditional Chinese
|
||||
{"unitxt_k.prs", 0x07}, // PC Korean
|
||||
{"unitxt_h.prs", 0x07}, // BB Korean
|
||||
static const map<string, Language> unitext_filenames({
|
||||
{"unitxt_j.prs", Language::JAPANESE}, // PC/BB Japanese
|
||||
{"unitxt_e.prs", Language::ENGLISH}, // PC/BB English
|
||||
{"unitxt_g.prs", Language::GERMAN}, // PC/BB German
|
||||
{"unitxt_f.prs", Language::FRENCH}, // PC/BB French
|
||||
{"unitxt_s.prs", Language::SPANISH}, // PC/BB Spanish
|
||||
{"unitxt_b.prs", Language::SIMPLIFIED_CHINESE}, // PC Simplified Chinese
|
||||
{"unitxt_cs.prs", Language::SIMPLIFIED_CHINESE}, // BB Simplified Chinese
|
||||
{"unitxt_t.prs", Language::TRADITIONAL_CHINESE}, // PC Traditional Chinese
|
||||
{"unitxt_ct.prs", Language::TRADITIONAL_CHINESE}, // BB Traditional Chinese
|
||||
{"unitxt_k.prs", Language::KOREAN}, // PC Korean
|
||||
{"unitxt_h.prs", Language::KOREAN}, // BB Korean
|
||||
});
|
||||
if (!uses_utf16(version)) {
|
||||
for (const auto& it : bintext_filenames) {
|
||||
string file_path = directory + "/" + subdirectory + "/" + it.first;
|
||||
for (const auto& [base_filename, language] : bintext_filenames) {
|
||||
string file_path = directory + "/" + subdirectory + "/" + base_filename;
|
||||
string json_path = file_path + ".json";
|
||||
if (std::filesystem::is_regular_file(json_path)) {
|
||||
this->log.debug_f("Loading {} {} JSON text set from {}", phosg::name_for_enum(version), char_for_language_code(it.second), json_path);
|
||||
this->add_set(version, it.second, make_shared<BinaryTextSet>(phosg::JSON::parse(phosg::load_file(json_path))));
|
||||
this->log.debug_f("Loading {} {} JSON text set from {}", phosg::name_for_enum(version), name_for_language(language), json_path);
|
||||
this->add_set(version, language, make_shared<BinaryTextSet>(phosg::JSON::parse(phosg::load_file(json_path))));
|
||||
} else if (std::filesystem::is_regular_file(file_path)) {
|
||||
this->log.debug_f("Loading {} {} binary text set from {}", phosg::name_for_enum(version), char_for_language_code(it.second), file_path);
|
||||
this->add_set(version, it.second, make_set(phosg::load_file(file_path), it.second == 0));
|
||||
this->log.debug_f("Loading {} {} binary text set from {}", phosg::name_for_enum(version), name_for_language(language), file_path);
|
||||
this->add_set(version, language, make_set(phosg::load_file(file_path), language == Language::JAPANESE));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto& it : unitext_filenames) {
|
||||
string file_path = directory + "/" + subdirectory + "/" + it.first;
|
||||
for (const auto& [base_filename, language] : unitext_filenames) {
|
||||
string file_path = directory + "/" + subdirectory + "/" + base_filename;
|
||||
string json_path = file_path + ".json";
|
||||
if (std::filesystem::is_regular_file(json_path)) {
|
||||
this->log.debug_f("Loading {} {} JSON text set from {}", phosg::name_for_enum(version), char_for_language_code(it.second), json_path);
|
||||
this->add_set(version, it.second, make_shared<UnicodeTextSet>(phosg::JSON::parse(phosg::load_file(json_path))));
|
||||
this->log.debug_f("Loading {} {} JSON text set from {}", phosg::name_for_enum(version), name_for_language(language), json_path);
|
||||
this->add_set(version, language, make_shared<UnicodeTextSet>(phosg::JSON::parse(phosg::load_file(json_path))));
|
||||
} else {
|
||||
auto patch_file = get_patch_file ? get_patch_file(version, it.first) : nullptr;
|
||||
auto patch_file = get_patch_file ? get_patch_file(version, base_filename) : nullptr;
|
||||
if (patch_file) {
|
||||
this->log.debug_f("Loading {} {} Unicode text set from {} in patch tree", phosg::name_for_enum(version), char_for_language_code(it.second), it.first);
|
||||
this->add_set(version, it.second, make_set(*patch_file, it.second == 0));
|
||||
this->log.debug_f("Loading {} {} Unicode text set from {} in patch tree", phosg::name_for_enum(version), name_for_language(language), base_filename);
|
||||
this->add_set(version, language, make_set(*patch_file, language == Language::JAPANESE));
|
||||
} else {
|
||||
if (std::filesystem::is_regular_file(file_path)) {
|
||||
this->log.debug_f("Loading {} {} Unicode text set from {}", phosg::name_for_enum(version), char_for_language_code(it.second), file_path);
|
||||
this->add_set(version, it.second, make_set(phosg::load_file(file_path), it.second == 0));
|
||||
this->log.debug_f("Loading {} {} Unicode text set from {}", phosg::name_for_enum(version), name_for_language(language), file_path);
|
||||
this->add_set(version, language, make_set(phosg::load_file(file_path), language == Language::JAPANESE));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -531,26 +531,26 @@ TextIndex::TextIndex(
|
||||
}
|
||||
}
|
||||
|
||||
void TextIndex::add_set(Version version, uint8_t language, std::shared_ptr<const TextSet> ts) {
|
||||
void TextIndex::add_set(Version version, Language language, std::shared_ptr<const TextSet> ts) {
|
||||
this->sets[this->key_for_set(version, language)] = ts;
|
||||
}
|
||||
|
||||
void TextIndex::delete_set(Version version, uint8_t language) {
|
||||
void TextIndex::delete_set(Version version, Language language) {
|
||||
this->sets.erase(this->key_for_set(version, language));
|
||||
}
|
||||
|
||||
const std::string& TextIndex::get(Version version, uint8_t language, size_t collection_index, size_t string_index) const {
|
||||
const std::string& TextIndex::get(Version version, Language language, size_t collection_index, size_t string_index) const {
|
||||
return this->get(version, language)->get(collection_index, string_index);
|
||||
}
|
||||
|
||||
const std::vector<std::string>& TextIndex::get(Version version, uint8_t language, size_t collection_index) const {
|
||||
const std::vector<std::string>& TextIndex::get(Version version, Language language, size_t collection_index) const {
|
||||
return this->get(version, language)->get(collection_index);
|
||||
}
|
||||
|
||||
std::shared_ptr<const TextSet> TextIndex::get(Version version, uint8_t language) const {
|
||||
std::shared_ptr<const TextSet> TextIndex::get(Version version, Language language) const {
|
||||
return this->sets.at(this->key_for_set(version, language));
|
||||
}
|
||||
|
||||
uint32_t TextIndex::key_for_set(Version version, uint8_t language) {
|
||||
return (static_cast<uint32_t>(version) << 8) | language;
|
||||
uint32_t TextIndex::key_for_set(Version version, Language language) {
|
||||
return (static_cast<uint32_t>(version) << 8) | static_cast<size_t>(language);
|
||||
}
|
||||
|
||||
+6
-6
@@ -96,15 +96,15 @@ public:
|
||||
std::function<std::shared_ptr<const std::string>(Version, const std::string&)> get_patch_file = nullptr);
|
||||
~TextIndex() = default;
|
||||
|
||||
void add_set(Version version, uint8_t language, std::shared_ptr<const TextSet> ts);
|
||||
void delete_set(Version version, uint8_t language);
|
||||
void add_set(Version version, Language language, std::shared_ptr<const TextSet> ts);
|
||||
void delete_set(Version version, Language language);
|
||||
|
||||
const std::string& get(Version version, uint8_t language, size_t collection_index, size_t string_index) const;
|
||||
const std::vector<std::string>& get(Version version, uint8_t language, size_t collection_index) const;
|
||||
std::shared_ptr<const TextSet> get(Version version, uint8_t language) const;
|
||||
const std::string& get(Version version, Language language, size_t collection_index, size_t string_index) const;
|
||||
const std::vector<std::string>& get(Version version, Language language, size_t collection_index) const;
|
||||
std::shared_ptr<const TextSet> get(Version version, Language language) const;
|
||||
|
||||
protected:
|
||||
static uint32_t key_for_set(Version version, uint8_t language);
|
||||
static uint32_t key_for_set(Version version, Language language);
|
||||
|
||||
phosg::PrefixedLogger log;
|
||||
std::unordered_map<uint32_t, std::shared_ptr<const TextSet>> sets;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.version DC_V1
|
||||
.quest_num 50
|
||||
.language 1
|
||||
.language E
|
||||
.name "Soul of a Blacksmith"
|
||||
.short_desc "A blacksmith\nwants to make\na weapon using\nRagol\'s unknown\nmaterials."
|
||||
.long_desc "Client: Ozwald\nQuest:\nI want to make a new\nweapon using unknown\nmaterials on Ragol.\nReward: 2500 Meseta"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.version DC_V1
|
||||
.quest_num 52
|
||||
.language 1
|
||||
.language E
|
||||
.name "The Retired Hunter"
|
||||
.short_desc "I will kill\n10000 monsters\nbefore I die!"
|
||||
.long_desc "Client: Donoph\nQuest:\n An old hunter, Donoph,\n is about to die.\n Defeat 99 monsters to\n fulfill his dream.\nReward: 8000 Meseta\n"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.version GC_V3
|
||||
.quest_num 88530
|
||||
.language 1
|
||||
.language E
|
||||
.episode Episode1
|
||||
.name "GC v1.2 USA patch enabler"
|
||||
.short_desc ""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.version GC_V3
|
||||
.quest_num 88531
|
||||
.language 0
|
||||
.language J
|
||||
.episode Episode1
|
||||
.name "GC v1.5 JP patch enabler"
|
||||
.short_desc ""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.version GC_EP3
|
||||
.quest_num 88532
|
||||
.language 1
|
||||
.language E
|
||||
.episode Episode1
|
||||
.name "GC Ep3 USA patch enabler"
|
||||
.short_desc ""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.version GC_EP3
|
||||
.quest_num 88533
|
||||
.language 1
|
||||
.language E
|
||||
.episode Episode1
|
||||
.name "GC Ep3 EU patch enabler"
|
||||
.short_desc ""
|
||||
|
||||
@@ -27,15 +27,15 @@
|
||||
// versions (DC, GC, and XB), this affects how strings are encoded - Japanese
|
||||
// uses Shift-JIS and other languages use ISO8859. (On PC V2 and BB, UTF-16 is
|
||||
// used for strings in all languages.) The language values are:
|
||||
// 0 = Japanese
|
||||
// 1 = English
|
||||
// 2 = German
|
||||
// 3 = French
|
||||
// 4 = Spanish
|
||||
// 5 = Chinese (simplified)
|
||||
// 6 = Chinese (traditional)
|
||||
// 7 = Korean
|
||||
.language 1
|
||||
// J = Japanese
|
||||
// E = English
|
||||
// G = German
|
||||
// F = French
|
||||
// S = Spanish
|
||||
// B = Chinese (simplified)
|
||||
// T = Chinese (traditional)
|
||||
// K = Korean
|
||||
.language E
|
||||
|
||||
// The .episode directive specifies the quest's episode. The server ignores this
|
||||
// if a set_episode or set_episode2 opcode is present in the code following the
|
||||
|
||||
Reference in New Issue
Block a user