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);
|
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",
|
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++) {
|
for (size_t z = 0; z < 0x60; z++) {
|
||||||
phosg::fwrite_fmt(stream, " {:02X} ", 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);
|
fputc('\n', stream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,12 +70,25 @@ public:
|
|||||||
} __packed_ws__(MovementData, 0x30);
|
} __packed_ws__(MovementData, 0x30);
|
||||||
|
|
||||||
struct Table {
|
struct Table {
|
||||||
/* 0000 */ parray<parray<PlayerStats, 0x60>, 4> stats;
|
/* 0000 */ parray<parray<PlayerStats, 0x60>, 4> stats; // [difficulty][bp_index]
|
||||||
/* 3600 */ parray<parray<AttackData, 0x60>, 4> attack_data;
|
/* 3600 */ parray<parray<AttackData, 0x60>, 4> attack_data; // [difficulty][bp_index]
|
||||||
/* 7E00 */ parray<parray<ResistData, 0x60>, 4> resist_data;
|
/* 7E00 */ parray<parray<ResistData, 0x60>, 4> resist_data; // [difficulty][bp_index]
|
||||||
/* AE00 */ parray<parray<MovementData, 0x60>, 4> movement_data;
|
/* AE00 */ parray<parray<MovementData, 0x60>, 4> movement_data; // [difficulty][bp_index]
|
||||||
/* F600 */
|
/* 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;
|
void print(FILE* stream, Episode episode) const;
|
||||||
} __packed_ws__(Table, 0xF600);
|
} __packed_ws__(Table, 0xF600);
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -8,6 +8,7 @@
|
|||||||
#include <phosg/Time.hh>
|
#include <phosg/Time.hh>
|
||||||
|
|
||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
|
#include "StaticGameData.hh"
|
||||||
#include "Version.hh"
|
#include "Version.hh"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@@ -16,7 +17,7 @@ extern bool use_terminal_colors;
|
|||||||
|
|
||||||
Channel::Channel(
|
Channel::Channel(
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const string& name,
|
const string& name,
|
||||||
phosg::TerminalFormat terminal_send_color,
|
phosg::TerminalFormat terminal_send_color,
|
||||||
phosg::TerminalFormat terminal_recv_color)
|
phosg::TerminalFormat terminal_recv_color)
|
||||||
@@ -249,7 +250,7 @@ shared_ptr<SocketChannel> SocketChannel::create(
|
|||||||
std::shared_ptr<asio::io_context> io_context,
|
std::shared_ptr<asio::io_context> io_context,
|
||||||
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const string& name,
|
const string& name,
|
||||||
phosg::TerminalFormat terminal_send_color,
|
phosg::TerminalFormat terminal_send_color,
|
||||||
phosg::TerminalFormat terminal_recv_color) {
|
phosg::TerminalFormat terminal_recv_color) {
|
||||||
@@ -263,7 +264,7 @@ SocketChannel::SocketChannel(
|
|||||||
std::shared_ptr<asio::io_context> io_context,
|
std::shared_ptr<asio::io_context> io_context,
|
||||||
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const string& name,
|
const string& name,
|
||||||
phosg::TerminalFormat terminal_send_color,
|
phosg::TerminalFormat terminal_send_color,
|
||||||
phosg::TerminalFormat terminal_recv_color)
|
phosg::TerminalFormat terminal_recv_color)
|
||||||
@@ -331,7 +332,7 @@ asio::awaitable<void> SocketChannel::send_task() {
|
|||||||
PeerChannel::PeerChannel(
|
PeerChannel::PeerChannel(
|
||||||
std::shared_ptr<asio::io_context> io_context,
|
std::shared_ptr<asio::io_context> io_context,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
phosg::TerminalFormat terminal_send_color,
|
phosg::TerminalFormat terminal_send_color,
|
||||||
phosg::TerminalFormat terminal_recv_color)
|
phosg::TerminalFormat terminal_recv_color)
|
||||||
|
|||||||
+5
-5
@@ -12,7 +12,7 @@
|
|||||||
class Channel {
|
class Channel {
|
||||||
public:
|
public:
|
||||||
Version version;
|
Version version;
|
||||||
uint8_t language;
|
Language language;
|
||||||
std::shared_ptr<PSOEncryption> crypt_in;
|
std::shared_ptr<PSOEncryption> crypt_in;
|
||||||
std::shared_ptr<PSOEncryption> crypt_out;
|
std::shared_ptr<PSOEncryption> crypt_out;
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
Channel(
|
Channel(
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||||
phosg::TerminalFormat terminal_recv_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,
|
static std::shared_ptr<SocketChannel> create(std::shared_ptr<asio::io_context> io_context,
|
||||||
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const std::string& name = "",
|
const std::string& name = "",
|
||||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||||
phosg::TerminalFormat terminal_recv_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::shared_ptr<asio::io_context> io_context,
|
||||||
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
std::unique_ptr<asio::ip::tcp::socket>&& sock,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
phosg::TerminalFormat terminal_send_color,
|
phosg::TerminalFormat terminal_send_color,
|
||||||
phosg::TerminalFormat terminal_recv_color);
|
phosg::TerminalFormat terminal_recv_color);
|
||||||
@@ -158,7 +158,7 @@ public:
|
|||||||
PeerChannel(
|
PeerChannel(
|
||||||
std::shared_ptr<asio::io_context> io_context,
|
std::shared_ptr<asio::io_context> io_context,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const std::string& name = "",
|
const std::string& name = "",
|
||||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||||
phosg::TerminalFormat terminal_recv_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) {
|
if (tokens.at(1).size() != 1) {
|
||||||
throw runtime_error("invalid language");
|
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;
|
a.c->channel->language = new_language;
|
||||||
p->inventory.language = new_language;
|
p->inventory.language = new_language;
|
||||||
p->guild_card.language = new_language;
|
p->guild_card.language = new_language;
|
||||||
@@ -1829,7 +1829,7 @@ ChatCommandDefinition cc_playrec(
|
|||||||
auto record = make_shared<Episode3::BattleRecord>(data);
|
auto record = make_shared<Episode3::BattleRecord>(data);
|
||||||
auto battle_player = make_shared<Episode3::BattleRecordPlayer>(s->io_context, record);
|
auto battle_player = make_shared<Episode3::BattleRecordPlayer>(s->io_context, record);
|
||||||
auto game = create_game_generic(
|
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 (game) {
|
||||||
if (start_battle_player_immediately) {
|
if (start_battle_player_immediately) {
|
||||||
game->set_flag(Lobby::Flag::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));
|
a.c->proxy_session->server_channel->send(0x60, 0x00, &cmd, sizeof(cmd));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint8_t difficulty = a.c->proxy_session ? a.c->proxy_session->lobby_difficulty : a.c->require_lobby()->difficulty;
|
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}, difficulty, 0x0000};
|
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));
|
a.c->channel->send(0x60, 0x00, &cmd, sizeof(cmd));
|
||||||
if (a.c->proxy_session) {
|
if (a.c->proxy_session) {
|
||||||
a.c->proxy_session->server_channel->send(0x60, 0x00, &cmd, sizeof(cmd));
|
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;
|
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);
|
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}",
|
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);
|
type_name, nearest_worldspace_pos.x, nearest_worldspace_pos.z);
|
||||||
auto set_str = set_entry->str(a.c->version(), area);
|
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}",
|
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 IntegralExpression> expr,
|
||||||
shared_ptr<const Lobby> game,
|
shared_ptr<const Lobby> game,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
size_t num_players,
|
size_t num_players,
|
||||||
bool v1_present) const {
|
bool v1_present) const {
|
||||||
if (this->login && this->login->account->check_flag(Account::Flag::DISABLE_QUEST_REQUIREMENTS)) {
|
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();
|
auto p = this->character_file();
|
||||||
IntegralExpression::Env env = {
|
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,
|
.challenge_records = &p->challenge_records,
|
||||||
.team = this->team(),
|
.team = this->team(),
|
||||||
.num_players = num_players,
|
.num_players = num_players,
|
||||||
@@ -404,7 +404,7 @@ bool Client::can_see_quest(
|
|||||||
shared_ptr<const Quest> q,
|
shared_ptr<const Quest> q,
|
||||||
shared_ptr<const Lobby> game,
|
shared_ptr<const Lobby> game,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
size_t num_players,
|
size_t num_players,
|
||||||
bool v1_present) const {
|
bool v1_present) const {
|
||||||
if (!q->has_version_any_language(this->version())) {
|
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 Quest> q,
|
||||||
shared_ptr<const Lobby> game,
|
shared_ptr<const Lobby> game,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
size_t num_players,
|
size_t num_players,
|
||||||
bool v1_present) const {
|
bool v1_present) const {
|
||||||
if (!q->has_version_any_language(this->version())) {
|
if (!q->has_version_any_language(this->version())) {
|
||||||
@@ -618,7 +618,7 @@ void Client::save_character_file() {
|
|||||||
|
|
||||||
void Client::create_character_file(
|
void Client::create_character_file(
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const PlayerDispDataBBPreview& preview,
|
const PlayerDispDataBBPreview& preview,
|
||||||
shared_ptr<const LevelTable> level_table) {
|
shared_ptr<const LevelTable> level_table) {
|
||||||
this->character_data = PSOBBCharacterFile::create_from_preview(guild_card_number, language, preview, 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) {
|
void Client::update_character_data_after_load(shared_ptr<PSOBBCharacterFile> charfile) {
|
||||||
charfile->import_tethealla_material_usage(this->require_server_state()->level_table(this->version()));
|
charfile->import_tethealla_material_usage(this->require_server_state()->level_table(this->version()));
|
||||||
|
|
||||||
uint8_t lang = this->language();
|
Language lang = this->language();
|
||||||
this->log.info_f("Overriding language fields in save files with {:02X} ({})", lang, char_for_language_code(lang));
|
this->log.info_f("Overriding language fields in save files with {}", name_for_language(lang));
|
||||||
charfile->inventory.language = lang;
|
charfile->inventory.language = lang;
|
||||||
charfile->guild_card.language = lang;
|
charfile->guild_card.language = lang;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -228,7 +228,7 @@ public:
|
|||||||
inline Version version() const {
|
inline Version version() const {
|
||||||
return this->channel->version;
|
return this->channel->version;
|
||||||
}
|
}
|
||||||
inline uint8_t language() const {
|
inline Language language() const {
|
||||||
return this->channel->language;
|
return this->channel->language;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,21 +267,21 @@ public:
|
|||||||
std::shared_ptr<const IntegralExpression> expr,
|
std::shared_ptr<const IntegralExpression> expr,
|
||||||
std::shared_ptr<const Lobby> game,
|
std::shared_ptr<const Lobby> game,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
size_t num_players,
|
size_t num_players,
|
||||||
bool v1_present) const;
|
bool v1_present) const;
|
||||||
bool can_see_quest(
|
bool can_see_quest(
|
||||||
std::shared_ptr<const Quest> q,
|
std::shared_ptr<const Quest> q,
|
||||||
std::shared_ptr<const Lobby> game,
|
std::shared_ptr<const Lobby> game,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
size_t num_players,
|
size_t num_players,
|
||||||
bool v1_present) const;
|
bool v1_present) const;
|
||||||
bool can_play_quest(
|
bool can_play_quest(
|
||||||
std::shared_ptr<const Quest> q,
|
std::shared_ptr<const Quest> q,
|
||||||
std::shared_ptr<const Lobby> game,
|
std::shared_ptr<const Lobby> game,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
size_t num_players,
|
size_t num_players,
|
||||||
bool v1_present) const;
|
bool v1_present) const;
|
||||||
|
|
||||||
@@ -316,7 +316,7 @@ public:
|
|||||||
void save_character_file();
|
void save_character_file();
|
||||||
void create_character_file(
|
void create_character_file(
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const PlayerDispDataBBPreview& preview,
|
const PlayerDispDataBBPreview& preview,
|
||||||
std::shared_ptr<const LevelTable> level_table);
|
std::shared_ptr<const LevelTable> level_table);
|
||||||
void create_battle_overlay(std::shared_ptr<const BattleRules> rules, 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;
|
/* 00 */ be_uint64_t hardware_id;
|
||||||
/* 08 */ le_uint32_t sub_version = 0;
|
/* 08 */ le_uint32_t sub_version = 0;
|
||||||
/* 0C */ uint8_t is_extended = 0;
|
/* 0C */ uint8_t is_extended = 0;
|
||||||
/* 0D */ uint8_t language = 0;
|
/* 0D */ Language language = Language::JAPANESE;
|
||||||
/* 0E */ le_uint16_t unused = 0;
|
/* 0E */ le_uint16_t unused = 0;
|
||||||
// Note: These are suffixed with 2 since they come from the same source data
|
// 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
|
// 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;
|
/* 00 */ be_uint64_t hardware_id;
|
||||||
/* 08 */ le_uint32_t sub_version = 0;
|
/* 08 */ le_uint32_t sub_version = 0;
|
||||||
/* 0C */ uint8_t is_extended = 0;
|
/* 0C */ uint8_t is_extended = 0;
|
||||||
/* 0D */ uint8_t language = 0;
|
/* 0D */ Language language = Language::JAPANESE;
|
||||||
/* 0E */ le_uint16_t unused = 0;
|
/* 0E */ le_uint16_t unused = 0;
|
||||||
/* 10 */ pstring<TextEncoding::ASCII, 0x10> serial_number;
|
/* 10 */ pstring<TextEncoding::ASCII, 0x10> serial_number;
|
||||||
/* 20 */ pstring<TextEncoding::ASCII, 0x10> access_key;
|
/* 20 */ pstring<TextEncoding::ASCII, 0x10> access_key;
|
||||||
@@ -414,7 +414,7 @@ struct C_LegacyLogin_PC_V3_04 {
|
|||||||
struct C_LegacyLogin_BB_04 {
|
struct C_LegacyLogin_BB_04 {
|
||||||
/* 00 */ le_uint32_t sub_version = 0;
|
/* 00 */ le_uint32_t sub_version = 0;
|
||||||
/* 04 */ uint8_t is_extended = 0;
|
/* 04 */ uint8_t is_extended = 0;
|
||||||
/* 05 */ uint8_t language = 0;
|
/* 05 */ Language language = Language::JAPANESE;
|
||||||
/* 06 */ le_uint16_t unused = 0;
|
/* 06 */ le_uint16_t unused = 0;
|
||||||
/* 08 */ pstring<TextEncoding::ASCII, 0x10> username;
|
/* 08 */ pstring<TextEncoding::ASCII, 0x10> username;
|
||||||
/* 18 */ pstring<TextEncoding::ASCII, 0x10> password;
|
/* 18 */ pstring<TextEncoding::ASCII, 0x10> password;
|
||||||
@@ -1286,7 +1286,7 @@ struct S_JoinGameT_DC_PC {
|
|||||||
/* 0104 */ uint8_t client_id = 0;
|
/* 0104 */ uint8_t client_id = 0;
|
||||||
/* 0105 */ uint8_t leader_id = 0;
|
/* 0105 */ uint8_t leader_id = 0;
|
||||||
/* 0106 */ uint8_t disable_udp = 1;
|
/* 0106 */ uint8_t disable_udp = 1;
|
||||||
/* 0107 */ uint8_t difficulty = 0;
|
/* 0107 */ Difficulty difficulty = Difficulty::NORMAL;
|
||||||
/* 0108 */ uint8_t battle_mode = 0;
|
/* 0108 */ uint8_t battle_mode = 0;
|
||||||
/* 0109 */ uint8_t event = 0;
|
/* 0109 */ uint8_t event = 0;
|
||||||
/* 010A */ uint8_t section_id = 0;
|
/* 010A */ uint8_t section_id = 0;
|
||||||
@@ -1687,7 +1687,7 @@ struct C_Login_DCNTE_8B {
|
|||||||
be_uint64_t hardware_id;
|
be_uint64_t hardware_id;
|
||||||
le_uint32_t sub_version = 0x20;
|
le_uint32_t sub_version = 0x20;
|
||||||
uint8_t is_extended = 0;
|
uint8_t is_extended = 0;
|
||||||
uint8_t language = 0;
|
Language language = Language::JAPANESE;
|
||||||
parray<uint8_t, 2> unused1;
|
parray<uint8_t, 2> unused1;
|
||||||
pstring<TextEncoding::ASCII, 0x11> serial_number;
|
pstring<TextEncoding::ASCII, 0x11> serial_number;
|
||||||
pstring<TextEncoding::ASCII, 0x11> access_key;
|
pstring<TextEncoding::ASCII, 0x11> access_key;
|
||||||
@@ -1748,7 +1748,7 @@ struct C_RegisterV1_DC_92 {
|
|||||||
be_uint64_t hardware_id;
|
be_uint64_t hardware_id;
|
||||||
le_uint32_t sub_version;
|
le_uint32_t sub_version;
|
||||||
uint8_t unused1 = 0;
|
uint8_t unused1 = 0;
|
||||||
uint8_t language = 0;
|
Language language = Language::JAPANESE;
|
||||||
parray<uint8_t, 2> unused2;
|
parray<uint8_t, 2> unused2;
|
||||||
pstring<TextEncoding::ASCII, 0x30> serial_number2;
|
pstring<TextEncoding::ASCII, 0x30> serial_number2;
|
||||||
pstring<TextEncoding::ASCII, 0x30> access_key2;
|
pstring<TextEncoding::ASCII, 0x30> access_key2;
|
||||||
@@ -1767,7 +1767,7 @@ struct C_LoginV1_DC_93 {
|
|||||||
/* 08 */ be_uint64_t hardware_id;
|
/* 08 */ be_uint64_t hardware_id;
|
||||||
/* 10 */ le_uint32_t sub_version = 0;
|
/* 10 */ le_uint32_t sub_version = 0;
|
||||||
/* 14 */ uint8_t is_extended = 0;
|
/* 14 */ uint8_t is_extended = 0;
|
||||||
/* 15 */ uint8_t language = 0;
|
/* 15 */ Language language = Language::JAPANESE;
|
||||||
/* 16 */ parray<uint8_t, 2> unused1;
|
/* 16 */ parray<uint8_t, 2> unused1;
|
||||||
/* 18 */ pstring<TextEncoding::ASCII, 0x11> serial_number;
|
/* 18 */ pstring<TextEncoding::ASCII, 0x11> serial_number;
|
||||||
/* 29 */ pstring<TextEncoding::ASCII, 0x11> access_key;
|
/* 29 */ pstring<TextEncoding::ASCII, 0x11> access_key;
|
||||||
@@ -1788,7 +1788,7 @@ struct C_LoginBase_BB_93 {
|
|||||||
/* 00 */ le_uint32_t player_tag = 0x00010000;
|
/* 00 */ le_uint32_t player_tag = 0x00010000;
|
||||||
/* 04 */ le_uint32_t guild_card_number = 0;
|
/* 04 */ le_uint32_t guild_card_number = 0;
|
||||||
/* 08 */ le_uint32_t sub_version = 0;
|
/* 08 */ le_uint32_t sub_version = 0;
|
||||||
/* 0C */ uint8_t language = 0;
|
/* 0C */ Language language = Language::JAPANESE;
|
||||||
/* 0D */ int8_t character_slot = 0;
|
/* 0D */ int8_t character_slot = 0;
|
||||||
// Values for connection_phase:
|
// Values for connection_phase:
|
||||||
// 00 - initial connection (client will request system file, characters, etc.)
|
// 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;
|
/* 00 */ be_uint64_t hardware_id;
|
||||||
/* 08 */ le_uint32_t sub_version = 0;
|
/* 08 */ le_uint32_t sub_version = 0;
|
||||||
/* 0C */ uint8_t unused1 = 0;
|
/* 0C */ uint8_t unused1 = 0;
|
||||||
/* 0D */ uint8_t language = 0;
|
/* 0D */ Language language = Language::JAPANESE;
|
||||||
/* 0E */ parray<uint8_t, 2> unused2;
|
/* 0E */ parray<uint8_t, 2> unused2;
|
||||||
/* 10 */ pstring<TextEncoding::ASCII, 0x30> serial_number; // On XB, this is the XBL gamertag
|
/* 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
|
/* 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 {
|
struct C_Register_BB_9C {
|
||||||
le_uint32_t sub_version = 0;
|
le_uint32_t sub_version = 0;
|
||||||
uint8_t unused1 = 0;
|
uint8_t unused1 = 0;
|
||||||
uint8_t language = 0;
|
Language language = Language::JAPANESE;
|
||||||
parray<uint8_t, 2> unused2;
|
parray<uint8_t, 2> unused2;
|
||||||
pstring<TextEncoding::ASCII, 0x30> username;
|
pstring<TextEncoding::ASCII, 0x30> username;
|
||||||
pstring<TextEncoding::ASCII, 0x30> password;
|
pstring<TextEncoding::ASCII, 0x30> password;
|
||||||
@@ -2002,7 +2002,7 @@ struct C_Login_DC_PC_GC_9D {
|
|||||||
/* 08 */ be_uint64_t hardware_id;
|
/* 08 */ be_uint64_t hardware_id;
|
||||||
/* 10 */ le_uint32_t sub_version = 0;
|
/* 10 */ le_uint32_t sub_version = 0;
|
||||||
/* 14 */ uint8_t is_extended = 0; // If 1, structure has extended format
|
/* 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
|
/* 16 */ parray<uint8_t, 0x2> unused3; // Always zeroes
|
||||||
/* 18 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
|
/* 18 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
|
||||||
/* 28 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
|
/* 28 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
|
||||||
@@ -2062,7 +2062,7 @@ struct C_LoginExtended_BB_9E {
|
|||||||
/* 0000 */ le_uint32_t player_tag = 0x00010000;
|
/* 0000 */ le_uint32_t player_tag = 0x00010000;
|
||||||
/* 0004 */ le_uint32_t guild_card_number = 0; // == account_id when on newserv
|
/* 0004 */ le_uint32_t guild_card_number = 0; // == account_id when on newserv
|
||||||
/* 0008 */ le_uint32_t sub_version = 0;
|
/* 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;
|
/* 0010 */ le_uint32_t unknown_a2 = 0;
|
||||||
/* 0014 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number; // Always blank?
|
/* 0014 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number; // Always blank?
|
||||||
/* 0024 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key; // == "?"
|
/* 0024 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key; // == "?"
|
||||||
@@ -2570,7 +2570,7 @@ check_struct_size(C_CreateGame_DCNTE, 0x28);
|
|||||||
|
|
||||||
template <TextEncoding Encoding>
|
template <TextEncoding Encoding>
|
||||||
struct C_CreateGameT : C_CreateGameBaseT<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)
|
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.
|
// Note: Episode 3 uses the challenge mode flag for view battle permissions.
|
||||||
// 0 = view battle allowed; 1 = not allowed
|
// 0 = view battle allowed; 1 = not allowed
|
||||||
@@ -2915,7 +2915,7 @@ struct C_SetChallengeModeCharacterTemplate_BB_02DF {
|
|||||||
|
|
||||||
struct C_SetChallengeModeDifficulty_BB_03DF {
|
struct C_SetChallengeModeDifficulty_BB_03DF {
|
||||||
// No existing challenge mode quest sets this to a value other than zero.
|
// 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);
|
} __packed_ws__(C_SetChallengeModeDifficulty_BB_03DF, 4);
|
||||||
|
|
||||||
struct C_SetChallengeModeEXPMultiplier_BB_04DF {
|
struct C_SetChallengeModeEXPMultiplier_BB_04DF {
|
||||||
@@ -3324,7 +3324,7 @@ struct S_JoinSpectatorTeam_Ep3_E8 {
|
|||||||
/* 1170 */ uint8_t client_id = 0;
|
/* 1170 */ uint8_t client_id = 0;
|
||||||
/* 1171 */ uint8_t leader_id = 0;
|
/* 1171 */ uint8_t leader_id = 0;
|
||||||
/* 1172 */ uint8_t disable_udp = 1;
|
/* 1172 */ uint8_t disable_udp = 1;
|
||||||
/* 1173 */ uint8_t difficulty = 0;
|
/* 1173 */ Difficulty difficulty = Difficulty::NORMAL;
|
||||||
/* 1174 */ uint8_t battle_mode = 0;
|
/* 1174 */ uint8_t battle_mode = 0;
|
||||||
/* 1175 */ uint8_t event = 0;
|
/* 1175 */ uint8_t event = 0;
|
||||||
/* 1176 */ uint8_t section_id = 0;
|
/* 1176 */ uint8_t section_id = 0;
|
||||||
@@ -5145,7 +5145,7 @@ struct G_6x70_Base_V1 {
|
|||||||
/* 0040 */ StatusEffectState attack_status_effect;
|
/* 0040 */ StatusEffectState attack_status_effect;
|
||||||
/* 004C */ StatusEffectState defense_status_effect;
|
/* 004C */ StatusEffectState defense_status_effect;
|
||||||
/* 0058 */ StatusEffectState unused_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;
|
/* 0068 */ le_uint32_t player_tag = 0;
|
||||||
/* 006C */ le_uint32_t guild_card_number = 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)
|
/* 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);
|
} __packed_ws__(G_UpdateQuestFlag_DC_PC_6x75, 8);
|
||||||
|
|
||||||
struct G_UpdateQuestFlag_V3_BB_6x75 : G_UpdateQuestFlag_DC_PC_6x75 {
|
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;
|
le_uint16_t unused = 0;
|
||||||
} __packed_ws__(G_UpdateQuestFlag_V3_BB_6x75, 0x0C);
|
} __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_type_drop_probs_json = json.at("EnemyTypeDropProbs").as_dict();
|
||||||
const auto& enemy_item_classes_json = json.at("EnemyItemClasses").as_dict();
|
const auto& enemy_item_classes_json = json.at("EnemyItemClasses").as_dict();
|
||||||
for (size_t z = 0; z < 0x64; z++) {
|
for (size_t z = 0; z < 0x64; z++) {
|
||||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (Episode episode : ALL_EPISODES_V4) {
|
||||||
for (Episode episode : episodes) {
|
|
||||||
auto types = enemy_types_for_rare_table_index(episode, z);
|
auto types = enemy_types_for_rare_table_index(episode, z);
|
||||||
vector<string> names;
|
vector<string> names;
|
||||||
if (types.empty()) {
|
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_type_drop_probs_json = phosg::JSON::dict();
|
||||||
phosg::JSON enemy_item_classes_json = phosg::JSON::dict();
|
phosg::JSON enemy_item_classes_json = phosg::JSON::dict();
|
||||||
for (size_t z = 0; z < 0x64; z++) {
|
for (size_t z = 0; z < 0x64; z++) {
|
||||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (Episode episode : ALL_EPISODES_V4) {
|
||||||
for (Episode episode : episodes) {
|
|
||||||
auto types = enemy_types_for_rare_table_index(episode, z);
|
auto types = enemy_types_for_rare_table_index(episode, z);
|
||||||
vector<string> names;
|
vector<string> names;
|
||||||
if (types.empty()) {
|
if (types.empty()) {
|
||||||
@@ -506,13 +504,11 @@ phosg::JSON CommonItemSet::Table::json() const {
|
|||||||
|
|
||||||
phosg::JSON CommonItemSet::json() const {
|
phosg::JSON CommonItemSet::json() const {
|
||||||
auto modes_dict = phosg::JSON::dict();
|
auto modes_dict = phosg::JSON::dict();
|
||||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
for (const auto& mode : ALL_GAME_MODES_V4) {
|
||||||
for (const auto& mode : modes) {
|
|
||||||
auto episodes_dict = phosg::JSON::dict();
|
auto episodes_dict = phosg::JSON::dict();
|
||||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (const auto& episode : ALL_EPISODES_V4) {
|
||||||
for (const auto& episode : episodes) {
|
|
||||||
auto difficulty_dict = phosg::JSON::dict();
|
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();
|
auto section_id_dict = phosg::JSON::dict();
|
||||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||||
try {
|
try {
|
||||||
@@ -532,11 +528,9 @@ phosg::JSON CommonItemSet::json() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CommonItemSet::print(FILE* stream) 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 : ALL_GAME_MODES_V4) {
|
||||||
for (const auto& mode : modes) {
|
for (const auto& episode : ALL_EPISODES_V4) {
|
||||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
for (const auto& episode : episodes) {
|
|
||||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
|
||||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||||
try {
|
try {
|
||||||
auto table = this->get_table(episode, mode, difficulty, section_id);
|
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 {
|
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 : ALL_GAME_MODES_V4) {
|
||||||
for (const auto& mode : modes) {
|
for (const auto& episode : ALL_EPISODES_V4) {
|
||||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (const auto& difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
for (const auto& episode : episodes) {
|
|
||||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
|
||||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||||
shared_ptr<const Table> this_table;
|
shared_ptr<const Table> this_table;
|
||||||
shared_ptr<const Table> other_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);
|
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
|
// Bits: -----EEEMMDDSSSS
|
||||||
return (((static_cast<uint16_t>(episode) << 8) & 0x0700) |
|
return (((static_cast<uint16_t>(episode) << 8) & 0x0700) |
|
||||||
((static_cast<uint16_t>(mode) << 6) & 0x00C0) |
|
((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(
|
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 {
|
try {
|
||||||
return this->tables.at(this->key_for_table(episode, mode, difficulty, secid));
|
return this->tables.at(this->key_for_table(episode, mode, difficulty, secid));
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
throw runtime_error(std::format("common item table not available for episode={}, mode={}, difficulty={}, secid={}",
|
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.
|
// Hard, etc.
|
||||||
{
|
{
|
||||||
AFSArchive pt_afs(pt_afs_data);
|
AFSArchive pt_afs(pt_afs_data);
|
||||||
size_t max_difficulty;
|
bool include_ultimate;
|
||||||
if (pt_afs.num_entries() >= 40) {
|
if (pt_afs.num_entries() >= 40) {
|
||||||
max_difficulty = 4;
|
include_ultimate = true;
|
||||||
} else if (pt_afs.num_entries() >= 30) {
|
} else if (pt_afs.num_entries() >= 30) {
|
||||||
max_difficulty = 3;
|
include_ultimate = false;
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(std::format("PT AFS file has unexpected entry count ({})", pt_afs.num_entries()));
|
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++) {
|
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);
|
phosg::StringReader r(entry.first, entry.second);
|
||||||
auto table = make_shared<Table>(r, false, false, Episode::EP1);
|
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);
|
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)
|
// 30th are used (section_id is ignored)
|
||||||
if (ct_afs_data) {
|
if (ct_afs_data) {
|
||||||
AFSArchive ct_afs(ct_afs_data);
|
AFSArchive ct_afs(ct_afs_data);
|
||||||
size_t max_difficulty;
|
bool include_ultimate;
|
||||||
if (ct_afs.num_entries() >= 40) {
|
if (ct_afs.num_entries() >= 40) {
|
||||||
max_difficulty = 4;
|
include_ultimate = true;
|
||||||
} else if (ct_afs.num_entries() >= 30) {
|
} else if (ct_afs.num_entries() >= 30) {
|
||||||
max_difficulty = 3;
|
include_ultimate = false;
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error(std::format("CT AFS file has unexpected entry count ({})", ct_afs.num_entries()));
|
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++) {
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
auto r = ct_afs.get_reader(difficulty * 10);
|
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);
|
auto table = make_shared<Table>(r, false, false, Episode::EP1);
|
||||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
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);
|
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) {
|
GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gsl_data, bool is_big_endian) {
|
||||||
GSLArchive gsl(gsl_data, 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 = "";
|
const char* episode_token = "";
|
||||||
switch (episode) {
|
switch (episode) {
|
||||||
case Episode::EP1:
|
case Episode::EP1:
|
||||||
@@ -746,9 +744,8 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
|
|||||||
section_id);
|
section_id);
|
||||||
};
|
};
|
||||||
|
|
||||||
vector<Episode> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (Episode episode : ALL_EPISODES_V4) {
|
||||||
for (Episode episode : episodes) {
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
|
|
||||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
for (size_t section_id = 0; section_id < 10; section_id++) {
|
||||||
phosg::StringReader r;
|
phosg::StringReader r;
|
||||||
try {
|
try {
|
||||||
@@ -773,7 +770,7 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (episode != Episode::EP4) {
|
if (episode != Episode::EP4) {
|
||||||
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
try {
|
try {
|
||||||
auto r = gsl.get_reader(filename_for_table(episode, difficulty, 0, true));
|
auto r = gsl.get_reader(filename_for_table(episode, difficulty, 0, true));
|
||||||
auto table = make_shared<Table>(r, is_big_endian, true, episode);
|
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);
|
Episode episode = episode_keys.at(episode_it.first);
|
||||||
|
|
||||||
for (const auto& difficulty_it : episode_it.second->as_dict()) {
|
for (const auto& difficulty_it : episode_it.second->as_dict()) {
|
||||||
static const unordered_map<string, uint8_t> difficulty_keys(
|
static const unordered_map<string, Difficulty> difficulty_keys(
|
||||||
{{"Normal", 0}, {"Hard", 1}, {"VeryHard", 2}, {"Ultimate", 3}});
|
{{"Normal", Difficulty::NORMAL}, {"Hard", Difficulty::HARD}, {"VeryHard", Difficulty::VERY_HARD}, {"Ultimate", Difficulty::ULTIMATE}});
|
||||||
uint8_t difficulty = difficulty_keys.at(difficulty_it.first);
|
Difficulty difficulty = difficulty_keys.at(difficulty_it.first);
|
||||||
|
|
||||||
for (const auto& section_id_it : difficulty_it.second->as_dict()) {
|
for (const auto& section_id_it : difficulty_it.second->as_dict()) {
|
||||||
uint8_t section_id = section_id_for_name(section_id_it.first);
|
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;
|
||||||
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;
|
phosg::JSON json() const;
|
||||||
void print(FILE* stream) const;
|
void print(FILE* stream) const;
|
||||||
void print_diff(FILE* stream, const CommonItemSet& other) const;
|
void print_diff(FILE* stream, const CommonItemSet& other) const;
|
||||||
@@ -279,7 +279,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
CommonItemSet() = default;
|
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;
|
std::unordered_map<uint16_t, std::shared_ptr<Table>> tables;
|
||||||
};
|
};
|
||||||
|
|||||||
+10
-10
@@ -40,7 +40,7 @@ DownloadSession::DownloadSession(
|
|||||||
uint16_t remote_port,
|
uint16_t remote_port,
|
||||||
const std::string& output_dir,
|
const std::string& output_dir,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file,
|
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file,
|
||||||
uint32_t serial_number2,
|
uint32_t serial_number2,
|
||||||
uint32_t serial_number,
|
uint32_t serial_number,
|
||||||
@@ -222,13 +222,13 @@ void DownloadSession::send_61_98(bool is_98) {
|
|||||||
if (is_v1(this->version)) {
|
if (is_v1(this->version)) {
|
||||||
C_CharacterData_DCv1_61_98 ret;
|
C_CharacterData_DCv1_61_98 ret;
|
||||||
ret.inventory = this->character->inventory;
|
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);
|
this->channel->send(command, 0x01, ret);
|
||||||
|
|
||||||
} else if (this->version == Version::DC_V2) {
|
} else if (this->version == Version::DC_V2) {
|
||||||
C_CharacterData_DCv2_61_98 ret;
|
C_CharacterData_DCv2_61_98 ret;
|
||||||
ret.inventory = this->character->inventory;
|
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.challenge = this->character->challenge_records;
|
||||||
ret.records.battle = this->character->battle_records;
|
ret.records.battle = this->character->battle_records;
|
||||||
ret.choice_search_config = this->character->choice_search_config;
|
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) {
|
} else if (this->version == Version::PC_V2) {
|
||||||
C_CharacterData_PC_61_98 ret;
|
C_CharacterData_PC_61_98 ret;
|
||||||
ret.inventory = this->character->inventory;
|
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.challenge = this->character->challenge_records;
|
||||||
ret.records.battle = this->character->battle_records;
|
ret.records.battle = this->character->battle_records;
|
||||||
ret.choice_search_config = this->character->choice_search_config;
|
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)) {
|
} else if (is_v3(this->version)) {
|
||||||
C_CharacterData_V3_61_98 ret;
|
C_CharacterData_V3_61_98 ret;
|
||||||
ret.inventory = this->character->inventory;
|
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.challenge = this->character->challenge_records;
|
||||||
ret.records.battle = this->character->battle_records;
|
ret.records.battle = this->character->battle_records;
|
||||||
ret.choice_search_config = this->character->choice_search_config;
|
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;
|
C_CreateGame_PC_C1 ret;
|
||||||
ret.name.encode(random_name());
|
ret.name.encode(random_name());
|
||||||
ret.password.encode(random_name());
|
ret.password.encode(random_name());
|
||||||
ret.difficulty = 0;
|
ret.difficulty = Difficulty::NORMAL;
|
||||||
ret.battle_mode = (game_config.mode == GameMode::BATTLE);
|
ret.battle_mode = (game_config.mode == GameMode::BATTLE);
|
||||||
ret.challenge_mode = (game_config.mode == GameMode::CHALLENGE);
|
ret.challenge_mode = (game_config.mode == GameMode::CHALLENGE);
|
||||||
ret.episode = 1;
|
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;
|
C_CreateGame_DC_V3_0C_C1_Ep3_EC ret;
|
||||||
ret.name.encode(random_name());
|
ret.name.encode(random_name());
|
||||||
ret.password.encode(random_name());
|
ret.password.encode(random_name());
|
||||||
ret.difficulty = 0;
|
ret.difficulty = Difficulty::NORMAL;
|
||||||
ret.battle_mode = (game_config.mode == GameMode::BATTLE);
|
ret.battle_mode = (game_config.mode == GameMode::BATTLE);
|
||||||
ret.challenge_mode = (game_config.mode == GameMode::CHALLENGE);
|
ret.challenge_mode = (game_config.mode == GameMode::CHALLENGE);
|
||||||
if (is_v1(this->version)) {
|
if (is_v1(this->version)) {
|
||||||
@@ -582,7 +582,7 @@ asio::awaitable<void> DownloadSession::on_message(Channel::Message& msg) {
|
|||||||
C_CreateGame_BB_C1 ret;
|
C_CreateGame_BB_C1 ret;
|
||||||
ret.name.encode(random_name());
|
ret.name.encode(random_name());
|
||||||
ret.password.encode(random_name());
|
ret.password.encode(random_name());
|
||||||
ret.difficulty = 0;
|
ret.difficulty = Difficulty::NORMAL;
|
||||||
ret.battle_mode = (game_config.mode == GameMode::BATTLE);
|
ret.battle_mode = (game_config.mode == GameMode::BATTLE);
|
||||||
ret.challenge_mode = (game_config.mode == GameMode::CHALLENGE);
|
ret.challenge_mode = (game_config.mode == GameMode::CHALLENGE);
|
||||||
if (game_config.episode == Episode::EP1) {
|
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 : '_');
|
filtered_name.push_back((isalnum(ch) || (ch == '-') || (ch == '.') || (ch == '_')) ? ch : '_');
|
||||||
}
|
}
|
||||||
string local_filename = std::format(
|
string local_filename = std::format(
|
||||||
"{}/{:016X}_{}_{}_{}_{}",
|
"{}/{:016X}_{}_{}_{:c}_{}",
|
||||||
this->output_dir,
|
this->output_dir,
|
||||||
this->current_request,
|
this->current_request,
|
||||||
phosg::now(),
|
phosg::now(),
|
||||||
phosg::name_for_enum(this->version),
|
phosg::name_for_enum(this->version),
|
||||||
char_for_language_code(this->language),
|
char_for_language(this->language),
|
||||||
filtered_name);
|
filtered_name);
|
||||||
this->open_files.emplace(internal_name, OpenFile{.request = this->current_request, .filename = local_filename, .total_size = cmd.file_size, .data = ""});
|
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,
|
uint16_t remote_port,
|
||||||
const std::string& output_dir,
|
const std::string& output_dir,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file,
|
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file,
|
||||||
uint32_t serial_number2,
|
uint32_t serial_number2,
|
||||||
uint32_t serial_number,
|
uint32_t serial_number,
|
||||||
@@ -50,7 +50,7 @@ protected:
|
|||||||
uint16_t remote_port;
|
uint16_t remote_port;
|
||||||
std::string output_dir;
|
std::string output_dir;
|
||||||
Version version;
|
Version version;
|
||||||
uint8_t language;
|
Language language;
|
||||||
bool show_command_data;
|
bool show_command_data;
|
||||||
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file;
|
std::shared_ptr<const PSOBBEncryption::KeyFile> bb_key_file;
|
||||||
uint32_t serial_number;
|
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();
|
phosg::JSON card_ids_json = phosg::JSON::list();
|
||||||
for (size_t z = 0; z < this->card_ids.size(); z++) {
|
for (size_t z = 0; z < this->card_ids.size(); z++) {
|
||||||
if (this->card_ids[z] != 0xFFFF) {
|
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();
|
phosg::JSON params_json = phosg::JSON::list();
|
||||||
for (size_t z = 0; z < this->params.size(); z++) {
|
for (size_t z = 0; z < this->params.size(); z++) {
|
||||||
params_json.emplace_back(this->params[z].load());
|
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();
|
phosg::JSON strings_json = phosg::JSON::list();
|
||||||
for (size_t z = 0; z < this->strings.size(); z++) {
|
for (size_t z = 0; z < this->strings.size(); z++) {
|
||||||
strings_json.emplace_back(this->strings[z].decode(language));
|
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]);
|
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;
|
deque<string> lines;
|
||||||
auto add_map = [&](const parray<parray<uint8_t, 0x10>, 0x10>& tiles) {
|
auto add_map = [&](const parray<parray<uint8_t, 0x10>, 0x10>& tiles) {
|
||||||
for (size_t y = 0; y < this->height; y++) {
|
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
|
// Some cards intentionally have the same name, so we just leave them
|
||||||
// unindexed (they can still be looked up by ID, of course)
|
// 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.emplace(name, entry);
|
||||||
this->card_definitions_by_name_normalized.emplace(this->normalize_card_name(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;
|
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),
|
: map(map),
|
||||||
language(language) {}
|
language(language) {}
|
||||||
|
|
||||||
MapIndex::VersionedMap::VersionedMap(std::string&& compressed_data, uint8_t language)
|
MapIndex::VersionedMap::VersionedMap(std::string&& compressed_data, Language language)
|
||||||
: language(language),
|
: language(language),
|
||||||
compressed_data(make_shared<string>(std::move(compressed_data))) {
|
compressed_data(make_shared<string>(std::move(compressed_data))) {
|
||||||
string decompressed = prs_decompress(*this->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)
|
MapIndex::Map::Map(shared_ptr<const VersionedMap> initial_version)
|
||||||
: map_number(initial_version->map->map_number),
|
: map_number(initial_version->map->map_number),
|
||||||
initial_version(initial_version) {
|
initial_version(initial_version) {
|
||||||
this->versions.resize(this->initial_version->language + 1);
|
size_t lang_index = static_cast<size_t>(this->initial_version->language);
|
||||||
this->versions[this->initial_version->language] = initial_version;
|
this->versions.resize(lang_index + 1);
|
||||||
|
this->versions[lang_index] = initial_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MapIndex::Map::add_version(std::shared_ptr<const VersionedMap> vm) {
|
void MapIndex::Map::add_version(std::shared_ptr<const VersionedMap> vm) {
|
||||||
if (this->versions.size() <= vm->language) {
|
size_t lang_index = static_cast<size_t>(vm->language);
|
||||||
this->versions.resize(vm->language + 1);
|
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");
|
throw runtime_error("map version already exists");
|
||||||
}
|
}
|
||||||
this->initial_version->map->assert_semantically_equivalent(*vm->map);
|
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 {
|
bool MapIndex::Map::has_version(Language language) const {
|
||||||
return (this->versions.size() > language) && !!this->versions[language];
|
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 the requested language exists, return it
|
||||||
if ((language < this->versions.size()) && this->versions[language]) {
|
if ((lang_index < this->versions.size()) && this->versions[lang_index]) {
|
||||||
return this->versions[language];
|
return this->versions[lang_index];
|
||||||
}
|
}
|
||||||
// If English exists, return it
|
// If English exists, return it
|
||||||
if ((1 < this->versions.size()) && this->versions[1]) {
|
constexpr size_t english_lang_index = static_cast<size_t>(Language::ENGLISH);
|
||||||
return this->versions[1];
|
if ((english_lang_index < this->versions.size()) && this->versions[english_lang_index]) {
|
||||||
|
return this->versions[english_lang_index];
|
||||||
}
|
}
|
||||||
// Return the first version that exists
|
// Return the first version that exists
|
||||||
for (const auto& vm : this->versions) {
|
for (const auto& vm : this->versions) {
|
||||||
@@ -2754,7 +2760,7 @@ MapIndex::MapIndex(const string& directory) {
|
|||||||
if (base_filename[base_filename.size() - 2] != '-') {
|
if (base_filename[base_filename.size() - 2] != '-') {
|
||||||
throw runtime_error("language code not present");
|
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;
|
shared_ptr<VersionedMap> vm;
|
||||||
if (decompressed_data) {
|
if (decompressed_data) {
|
||||||
@@ -2773,7 +2779,7 @@ MapIndex::MapIndex(const string& directory) {
|
|||||||
static_game_data_log.debug_f("({}) Created Episode 3 map {:08X} {} ({}; {})",
|
static_game_data_log.debug_f("({}) Created Episode 3 map {:08X} {} ({}; {})",
|
||||||
filename,
|
filename,
|
||||||
vm->map->map_number,
|
vm->map->map_number,
|
||||||
char_for_language_code(vm->language),
|
char_for_language(vm->language),
|
||||||
vm->map->is_quest() ? "quest" : "free",
|
vm->map->is_quest() ? "quest" : "free",
|
||||||
name);
|
name);
|
||||||
} else {
|
} else {
|
||||||
@@ -2781,7 +2787,7 @@ MapIndex::MapIndex(const string& directory) {
|
|||||||
static_game_data_log.debug_f("({}) Added Episode 3 map version {:08X} {} ({}; {})",
|
static_game_data_log.debug_f("({}) Added Episode 3 map version {:08X} {} ({}; {})",
|
||||||
filename,
|
filename,
|
||||||
vm->map->map_number,
|
vm->map->map_number,
|
||||||
char_for_language_code(vm->language),
|
char_for_language(vm->language),
|
||||||
vm->map->is_quest() ? "quest" : "free",
|
vm->map->is_quest() ? "quest" : "free",
|
||||||
name);
|
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) {
|
if (num_players == 0) {
|
||||||
throw runtime_error("cannot generate map list for no players");
|
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");
|
throw logic_error("player count is too high in map list generation");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (language >= this->compressed_map_lists.size()) {
|
size_t lang_index = static_cast<size_t>(language);
|
||||||
this->compressed_map_lists.resize(language + 1);
|
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()) {
|
if (compressed_map_list.empty()) {
|
||||||
phosg::StringWriter entries_w;
|
phosg::StringWriter entries_w;
|
||||||
phosg::StringWriter strings_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;
|
/* 00 */ pstring<TextEncoding::MARKED, 0x18> deck_name;
|
||||||
/* 18 */ parray<be_uint16_t, 0x20> card_ids; // Last one appears to always be FFFF
|
/* 18 */ parray<be_uint16_t, 0x20> card_ids; // Last one appears to always be FFFF
|
||||||
/* 58 */
|
/* 58 */
|
||||||
phosg::JSON json(uint8_t language) const;
|
phosg::JSON json(Language language) const;
|
||||||
} __packed_ws__(NPCDeck, 0x58);
|
} __packed_ws__(NPCDeck, 0x58);
|
||||||
/* 1FE8 */ parray<NPCDeck, 3> npc_decks; // Unused if name[0] == 0
|
/* 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.
|
// TODO: Figure out exactly how these are used and document here.
|
||||||
/* 0018 */ parray<be_uint16_t, 0x7E> params;
|
/* 0018 */ parray<be_uint16_t, 0x7E> params;
|
||||||
/* 0114 */
|
/* 0114 */
|
||||||
phosg::JSON json(uint8_t language) const;
|
phosg::JSON json(Language language) const;
|
||||||
} __packed_ws__(AIParams, 0x114);
|
} __packed_ws__(AIParams, 0x114);
|
||||||
/* 20F0 */ parray<AIParams, 3> npc_ai_params; // Unused if name[0] == 0
|
/* 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 '^'.
|
// strings, excluding any that are empty or begin with the character '^'.
|
||||||
/* 0004 */ parray<pstring<TextEncoding::MARKED, 0x40>, 4> strings;
|
/* 0004 */ parray<pstring<TextEncoding::MARKED, 0x40>, 4> strings;
|
||||||
/* 0104 */
|
/* 0104 */
|
||||||
phosg::JSON json(uint8_t language) const;
|
phosg::JSON json(Language language) const;
|
||||||
} __packed_ws__(DialogueSet, 0x104);
|
} __packed_ws__(DialogueSet, 0x104);
|
||||||
|
|
||||||
// There are up to 0x10 of these per valid NPC, but only the first 13 of them
|
// 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.
|
// text may differ.
|
||||||
void assert_semantically_equivalent(const MapDefinition& other) const;
|
void assert_semantically_equivalent(const MapDefinition& other) const;
|
||||||
|
|
||||||
std::string str(const CardIndex* card_index, uint8_t language) const;
|
std::string str(const CardIndex* card_index, Language language) const;
|
||||||
phosg::JSON json(uint8_t language) const;
|
phosg::JSON json(Language language) const;
|
||||||
} __packed_ws__(MapDefinition, 0x5A18);
|
} __packed_ws__(MapDefinition, 0x5A18);
|
||||||
|
|
||||||
struct MapDefinitionTrial {
|
struct MapDefinitionTrial {
|
||||||
@@ -1592,10 +1592,10 @@ public:
|
|||||||
class VersionedMap {
|
class VersionedMap {
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<const MapDefinition> map;
|
std::shared_ptr<const MapDefinition> map;
|
||||||
uint8_t language;
|
Language language;
|
||||||
|
|
||||||
VersionedMap(std::shared_ptr<const MapDefinition> map, uint8_t language);
|
VersionedMap(std::shared_ptr<const MapDefinition> map, Language language);
|
||||||
VersionedMap(std::string&& compressed_data, uint8_t language);
|
VersionedMap(std::string&& compressed_data, Language language);
|
||||||
|
|
||||||
std::shared_ptr<const MapDefinitionTrial> trial() const;
|
std::shared_ptr<const MapDefinitionTrial> trial() const;
|
||||||
std::shared_ptr<const std::string> compressed(bool 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);
|
explicit Map(std::shared_ptr<const VersionedMap> initial_version);
|
||||||
|
|
||||||
void add_version(std::shared_ptr<const VersionedMap> vm);
|
void add_version(std::shared_ptr<const VersionedMap> vm);
|
||||||
bool has_version(uint8_t language) const;
|
bool has_version(Language language) const;
|
||||||
std::shared_ptr<const VersionedMap> version(uint8_t language) const;
|
std::shared_ptr<const VersionedMap> version(Language language) const;
|
||||||
inline const std::vector<std::shared_ptr<const VersionedMap>>& all_versions() const {
|
inline const std::vector<std::shared_ptr<const VersionedMap>>& all_versions() const {
|
||||||
return this->versions;
|
return this->versions;
|
||||||
}
|
}
|
||||||
@@ -1626,7 +1626,7 @@ public:
|
|||||||
std::vector<std::shared_ptr<const VersionedMap>> versions;
|
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 {
|
inline std::shared_ptr<const Map> get(uint32_t id) const {
|
||||||
return this->maps.at(id);
|
return this->maps.at(id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ void DeckState::print(FILE* stream, std::shared_ptr<const CardIndex> card_index)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ce) {
|
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",
|
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));
|
z, e.deck_index, this->card_refs[z], e.card_id, name, name_for_card_state(e.state));
|
||||||
} else {
|
} 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
|
// NTE doesn't have the date_str2 field, but we send it anyway to make
|
||||||
// debugging easier.
|
// debugging easier.
|
||||||
G_ServerVersionStrings_Ep3_6xB4x46 cmd;
|
G_ServerVersionStrings_Ep3_6xB4x46 cmd;
|
||||||
cmd.version_signature.encode(this->options.is_nte() ? VERSION_SIGNATURE_NTE : VERSION_SIGNATURE, 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()), 1);
|
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);
|
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);
|
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);
|
auto vm = map->version(language);
|
||||||
|
|
||||||
const auto& compressed = vm->compressed(is_nte);
|
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) {
|
if (this->last_chosen_map) {
|
||||||
string data = this->prepare_6xB6x41_map_definition(this->last_chosen_map, ch->language, this->options.is_nte());
|
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);
|
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
|
// 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
|
// smaller). So, use the values from the last chosen map if applicable, or
|
||||||
// the values from the $dicerange command if available.
|
// 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;
|
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;
|
auto& server_rules = this->map_and_rules->rules;
|
||||||
// NTE can specify the DEF dice value range in its Rules struct, so we use
|
// 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;
|
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);
|
const auto& list_data = this->options.map_index->get_compressed_list(num_players, language);
|
||||||
|
|
||||||
phosg::StringWriter w;
|
phosg::StringWriter w;
|
||||||
@@ -2553,15 +2553,16 @@ void Server::send_6xB6x41_to_all_clients() const {
|
|||||||
if (!c) {
|
if (!c) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (map_commands_by_language.size() <= c->language()) {
|
size_t lang_index = static_cast<size_t>(c->language());
|
||||||
map_commands_by_language.resize(c->language() + 1);
|
if (map_commands_by_language.size() <= lang_index) {
|
||||||
|
map_commands_by_language.resize(lang_index + 1);
|
||||||
}
|
}
|
||||||
if (map_commands_by_language[c->language()].empty()) {
|
if (map_commands_by_language[lang_index].empty()) {
|
||||||
map_commands_by_language[c->language()] = this->prepare_6xB6x41_map_definition(
|
map_commands_by_language[lang_index] = this->prepare_6xB6x41_map_definition(
|
||||||
this->last_chosen_map, c->language(), this->options.is_nte());
|
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);
|
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[c->language()]);
|
send_command(c, 0x6C, 0x00, map_commands_by_language[lang_index]);
|
||||||
};
|
};
|
||||||
for (const auto& c : l->clients) {
|
for (const auto& c : l->clients) {
|
||||||
send_to_client(c);
|
send_to_client(c);
|
||||||
@@ -2586,7 +2587,7 @@ void Server::send_6xB6x41_to_all_clients() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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);
|
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_UpdateDecks_Ep3_6xB4x07 prepare_6xB4x07_decks_update() const;
|
||||||
G_SetPlayerNames_Ep3_6xB4x1C prepare_6xB4x1C_names_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;
|
void send_6xB6x41_to_all_clients() const;
|
||||||
G_SetTrapTileLocations_Ep3_6xB4x50 prepare_6xB4x50_trap_tile_locations() 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) {
|
if (en_vm) {
|
||||||
string map_name = en_vm->map->name.decode(en_vm->language);
|
string map_name = en_vm->map->name.decode(en_vm->language);
|
||||||
ret += std::format(" Map: {:08X} ({})\n", this->map->map_number, map_name);
|
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,
|
this->io_context,
|
||||||
make_unique<asio::ip::tcp::socket>(std::move(client_sock)),
|
make_unique<asio::ip::tcp::socket>(std::move(client_sock)),
|
||||||
listen_sock->version,
|
listen_sock->version,
|
||||||
1,
|
Language::ENGLISH,
|
||||||
"",
|
"",
|
||||||
phosg::TerminalFormat::FG_YELLOW,
|
phosg::TerminalFormat::FG_YELLOW,
|
||||||
phosg::TerminalFormat::FG_GREEN);
|
phosg::TerminalFormat::FG_GREEN);
|
||||||
|
|||||||
+6
-6
@@ -132,7 +132,7 @@ std::shared_ptr<phosg::JSON> HTTPServer::generate_client_json(
|
|||||||
{"RemoteAddress", c->channel->default_name()},
|
{"RemoteAddress", c->channel->default_name()},
|
||||||
{"Version", phosg::name_for_enum(c->version())},
|
{"Version", phosg::name_for_enum(c->version())},
|
||||||
{"SubVersion", c->sub_version},
|
{"SubVersion", c->sub_version},
|
||||||
{"Language", name_for_language_code(c->language())},
|
{"Language", name_for_language(c->language())},
|
||||||
{"LocationX", c->pos.x.load()},
|
{"LocationX", c->pos.x.load()},
|
||||||
{"LocationZ", c->pos.z.load()},
|
{"LocationZ", c->pos.z.load()},
|
||||||
{"LocationFloor", c->floor},
|
{"LocationFloor", c->floor},
|
||||||
@@ -173,7 +173,7 @@ std::shared_ptr<phosg::JSON> HTTPServer::generate_client_json(
|
|||||||
if (p) {
|
if (p) {
|
||||||
if (!is_ep3(c->version())) {
|
if (!is_ep3(c->version())) {
|
||||||
if (c->version() != Version::DC_NTE) {
|
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("NumHPMaterialsUsed", p->get_material_usage(PSOBBCharacterFile::MaterialType::HP));
|
||||||
ret->emplace("NumTPMaterialsUsed", p->get_material_usage(PSOBBCharacterFile::MaterialType::TP));
|
ret->emplace("NumTPMaterialsUsed", p->get_material_usage(PSOBBCharacterFile::MaterialType::TP));
|
||||||
if (!is_v1_or_v2(c->version())) {
|
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({
|
lobby_players_json.emplace_back(phosg::JSON::dict({
|
||||||
{"GuildCardNumber", p.guild_card_number},
|
{"GuildCardNumber", p.guild_card_number},
|
||||||
{"Name", p.name},
|
{"Name", p.name},
|
||||||
{"Language", name_for_language_code(p.language)},
|
{"Language", name_for_language(p.language)},
|
||||||
{"SectionID", name_for_section_id(p.section_id)},
|
{"SectionID", name_for_section_id(p.section_id)},
|
||||||
{"CharClass", name_for_char_class(p.char_class)},
|
{"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({
|
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()},
|
{"TeamID", deck_entry->team_id.load()},
|
||||||
{"Cards", std::move(cards_json)},
|
{"Cards", std::move(cards_json)},
|
||||||
{"GodWhimFlag", deck_entry->god_whim_flag},
|
{"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({
|
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},
|
{"ClientID", ep3s->name_entries[z].client_id},
|
||||||
{"IsCOM", !!ep3s->name_entries[z].is_cpu_player},
|
{"IsCOM", !!ep3s->name_entries[z].is_cpu_player},
|
||||||
{"Deck", std::move(deck_json)},
|
{"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)},
|
{"AccountID", c->login ? c->login->account->account_id : phosg::JSON(nullptr)},
|
||||||
{"Name", p ? p->disp.name.decode(c->language()) : phosg::JSON(nullptr)},
|
{"Name", p ? p->disp.name.decode(c->language()) : phosg::JSON(nullptr)},
|
||||||
{"Version", phosg::name_for_enum(c->version())},
|
{"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)},
|
{"Level", p ? p->disp.stats.level + 1 : phosg::JSON(nullptr)},
|
||||||
{"Class", p ? name_for_char_class(p->disp.visual.char_class) : 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)},
|
{"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> ipss_client,
|
||||||
std::weak_ptr<IPSSClient::TCPConnection> tcp_conn,
|
std::weak_ptr<IPSSClient::TCPConnection> tcp_conn,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
phosg::TerminalFormat terminal_send_color,
|
phosg::TerminalFormat terminal_send_color,
|
||||||
phosg::TerminalFormat terminal_recv_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;
|
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()) {
|
if (!this->state->game_server.get()) {
|
||||||
this->log.error_f("No server available for TCP connection {}", conn_str);
|
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> ipss_client,
|
||||||
std::weak_ptr<IPSSClient::TCPConnection> tcp_conn,
|
std::weak_ptr<IPSSClient::TCPConnection> tcp_conn,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const std::string& name = "",
|
const std::string& name = "",
|
||||||
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
phosg::TerminalFormat terminal_send_color = phosg::TerminalFormat::END,
|
||||||
phosg::TerminalFormat terminal_recv_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});
|
w.put<GVMFileHeader>({.signature = 0x47564D48, .header_size = 0x48, .flags = 0x000F, .num_files = 1});
|
||||||
GVMFileEntry file_entry;
|
GVMFileEntry file_entry;
|
||||||
file_entry.file_num = 0;
|
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.data_format = data_format;
|
||||||
file_entry.format_flags = 0;
|
file_entry.format_flags = 0;
|
||||||
file_entry.dimensions = (dimensions_field << 4) | dimensions_field;
|
file_entry.dimensions = (dimensions_field << 4) | dimensions_field;
|
||||||
|
|||||||
+4
-4
@@ -32,7 +32,7 @@ ItemCreator::ItemCreator(
|
|||||||
std::shared_ptr<const ItemData::StackLimits> stack_limits,
|
std::shared_ptr<const ItemData::StackLimits> stack_limits,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
std::shared_ptr<RandomGenerator> rand_crypt,
|
std::shared_ptr<RandomGenerator> rand_crypt,
|
||||||
shared_ptr<const BattleRules> restrictions)
|
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[1] = 1;
|
||||||
item.data1[2] = pt.pop();
|
item.data1[2] = pt.pop();
|
||||||
|
|
||||||
if ((this->difficulty == 3) && (player_level > 99)) {
|
if ((this->difficulty == Difficulty::ULTIMATE) && (player_level > 99)) {
|
||||||
if (player_level > 150) {
|
if (player_level > 150) {
|
||||||
item.data1[2] += 3;
|
item.data1[2] += 3;
|
||||||
} else if (player_level >= 100) {
|
} 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[1] = 2;
|
||||||
item.data1[2] = pt.pop();
|
item.data1[2] = pt.pop();
|
||||||
|
|
||||||
if ((this->difficulty == 3) && (player_level > 99)) {
|
if ((this->difficulty == Difficulty::ULTIMATE) && (player_level > 99)) {
|
||||||
if (player_level > 150) {
|
if (player_level > 150) {
|
||||||
item.data1[2] += 3;
|
item.data1[2] += 3;
|
||||||
} else if (player_level >= 100) {
|
} else if (player_level >= 100) {
|
||||||
@@ -1360,7 +1360,7 @@ vector<ItemData> ItemCreator::generate_weapon_shop_contents(size_t player_level)
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t table_index;
|
size_t table_index;
|
||||||
if (this->difficulty == 3) {
|
if (this->difficulty == Difficulty::ULTIMATE) {
|
||||||
if (player_level < 11) {
|
if (player_level < 11) {
|
||||||
table_index = 0;
|
table_index = 0;
|
||||||
} else if (player_level < 26) {
|
} else if (player_level < 26) {
|
||||||
|
|||||||
+2
-2
@@ -22,7 +22,7 @@ public:
|
|||||||
std::shared_ptr<const ItemData::StackLimits> stack_limits,
|
std::shared_ptr<const ItemData::StackLimits> stack_limits,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
std::shared_ptr<RandomGenerator> rand_crypt,
|
std::shared_ptr<RandomGenerator> rand_crypt,
|
||||||
std::shared_ptr<const BattleRules> restrictions = nullptr);
|
std::shared_ptr<const BattleRules> restrictions = nullptr);
|
||||||
@@ -61,7 +61,7 @@ private:
|
|||||||
std::shared_ptr<const ItemData::StackLimits> stack_limits;
|
std::shared_ptr<const ItemData::StackLimits> stack_limits;
|
||||||
Episode episode;
|
Episode episode;
|
||||||
GameMode mode;
|
GameMode mode;
|
||||||
uint8_t difficulty;
|
Difficulty difficulty;
|
||||||
uint8_t section_id;
|
uint8_t section_id;
|
||||||
std::shared_ptr<const RareItemSet> rare_item_set;
|
std::shared_ptr<const RareItemSet> rare_item_set;
|
||||||
std::shared_ptr<const ArmorRandomSet> armor_random_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->rare_item_set(logic_version, this->quest),
|
||||||
s->armor_random_set,
|
s->armor_random_set,
|
||||||
s->tool_random_set,
|
s->tool_random_set,
|
||||||
s->weapon_random_sets.at(this->difficulty),
|
s->weapon_random_set(this->difficulty),
|
||||||
s->tekker_adjustment_set,
|
s->tekker_adjustment_set,
|
||||||
s->item_parameter_table(logic_version),
|
s->item_parameter_table(logic_version),
|
||||||
s->item_stack_limits(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;
|
uint8_t override_section_id = 0xFF;
|
||||||
Episode episode = Episode::NONE;
|
Episode episode = Episode::NONE;
|
||||||
GameMode mode = GameMode::NORMAL;
|
GameMode mode = GameMode::NORMAL;
|
||||||
uint8_t difficulty = 0; // 0-3
|
Difficulty difficulty = Difficulty::NORMAL;
|
||||||
float base_exp_multiplier = 1.0f;
|
float base_exp_multiplier = 1.0f;
|
||||||
float exp_share_multiplier = 0.5f;
|
float exp_share_multiplier = 0.5f;
|
||||||
float challenge_exp_multiplier = 1.0f;
|
float challenge_exp_multiplier = 1.0f;
|
||||||
|
|||||||
+17
-21
@@ -1610,7 +1610,7 @@ Action a_disassemble_quest_script(
|
|||||||
if (!args.get<bool>("decompressed")) {
|
if (!args.get<bool>("decompressed")) {
|
||||||
data = prs_decompress(data);
|
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 reassembly_mode = args.get<bool>("reassembly");
|
||||||
bool use_qedit_names = args.get<bool>("qedit");
|
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);
|
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,
|
remote_port,
|
||||||
args.get<string>("output-dir", true),
|
args.get<string>("output-dir", true),
|
||||||
version,
|
version,
|
||||||
args.get<uint8_t>("language"),
|
static_cast<Language>(args.get<uint8_t>("language")),
|
||||||
key,
|
key,
|
||||||
phosg::random_object<uint32_t>(),
|
phosg::random_object<uint32_t>(),
|
||||||
serial_number,
|
serial_number,
|
||||||
@@ -2173,12 +2173,10 @@ Action a_convert_rare_item_set(
|
|||||||
} else if (output_filename_lower.ends_with(".html")) {
|
} else if (output_filename_lower.ends_with(".html")) {
|
||||||
Version cli_version = get_cli_version(args, Version::BB_V4);
|
Version cli_version = get_cli_version(args, Version::BB_V4);
|
||||||
bool is_v1 = ::is_v1(cli_version);
|
bool is_v1 = ::is_v1(cli_version);
|
||||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
for (GameMode mode : ALL_GAME_MODES_V4) {
|
||||||
for (GameMode mode : modes) {
|
for (Episode episode : ALL_EPISODES_V4) {
|
||||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
for (Episode episode : episodes) {
|
if ((is_v1 && (difficulty == Difficulty::ULTIMATE)) || (!rs->has_entries_for_game_config(mode, episode, difficulty))) {
|
||||||
for (size_t difficulty = 0; difficulty < (is_v1 ? 3 : 4); difficulty++) {
|
|
||||||
if (!rs->has_entries_for_game_config(mode, episode, difficulty)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto item_name_index = s->item_name_index(cli_version);
|
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;
|
shared_ptr<const TextSet> text_english;
|
||||||
try {
|
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&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2833,12 +2831,13 @@ Action a_show_ep3_maps(
|
|||||||
phosg::log_info_f("{} maps", map_ids.size());
|
phosg::log_info_f("{} maps", map_ids.size());
|
||||||
for (const auto& [map_number, map] : map_ids) {
|
for (const auto& [map_number, map] : map_ids) {
|
||||||
const auto& vms = map->all_versions();
|
const auto& vms = map->all_versions();
|
||||||
for (size_t language = 0; language < vms.size(); language++) {
|
for (size_t lang_index = 0; lang_index < vms.size(); lang_index++) {
|
||||||
if (!vms[language]) {
|
if (!vms[lang_index]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
string map_s = vms[language]->map->str(s->ep3_card_index.get(), language);
|
Language language = static_cast<Language>(lang_index);
|
||||||
phosg::fwrite_fmt(stdout, "({}) {}\n", char_for_language_code(language), map_s);
|
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) {
|
for (const auto& it : s->supermap_for_free_play_key) {
|
||||||
auto episode = static_cast<Episode>((it.first >> 28) & 7);
|
auto episode = static_cast<Episode>((it.first >> 28) & 7);
|
||||||
auto mode = static_cast<GameMode>((it.first >> 26) & 3);
|
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 floor = (it.first >> 16) & 0xFF;
|
||||||
uint8_t layout = (it.first >> 8) & 0xFF;
|
uint8_t layout = (it.first >> 8) & 0xFF;
|
||||||
uint8_t entities = (it.first >> 0) & 0xFF;
|
uint8_t entities = (it.first >> 0) & 0xFF;
|
||||||
@@ -2924,12 +2923,9 @@ Action a_check_supermaps(
|
|||||||
|
|
||||||
// Generate MapStates for a few random variations
|
// Generate MapStates for a few random variations
|
||||||
for (size_t z = 0; z < 0x20; z++) {
|
for (size_t z = 0; z < 0x20; z++) {
|
||||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
Episode episode = ALL_EPISODES_V4[phosg::random_object<uint32_t>() % ALL_EPISODES_V4.size()];
|
||||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
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);
|
||||||
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;
|
|
||||||
uint8_t event = phosg::random_object<uint32_t>() % 8;
|
uint8_t event = phosg::random_object<uint32_t>() % 8;
|
||||||
uint32_t random_seed = phosg::random_object<uint32_t>();
|
uint32_t random_seed = phosg::random_object<uint32_t>();
|
||||||
phosg::fwrite_fmt(stderr, "FREE MAP STATE TEST: {} {} {}\n",
|
phosg::fwrite_fmt(stderr, "FREE MAP STATE TEST: {} {} {}\n",
|
||||||
@@ -3014,7 +3010,7 @@ Action a_check_supermaps(
|
|||||||
|
|
||||||
auto map_state = make_shared<MapState>(
|
auto map_state = make_shared<MapState>(
|
||||||
0,
|
0,
|
||||||
phosg::random_object<uint8_t>() & 3,
|
static_cast<Difficulty>(phosg::random_object<uint8_t>() & 3),
|
||||||
0,
|
0,
|
||||||
phosg::random_object<uint32_t>(),
|
phosg::random_object<uint32_t>(),
|
||||||
MapState::DEFAULT_RARE_ENEMIES,
|
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 MapFile::ObjectSetEntry::str(Version version, uint8_t area) const {
|
||||||
string name_str = MapFile::name_for_object_type(this->base_type, version, area);
|
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,
|
this->base_type,
|
||||||
name_str,
|
name_str,
|
||||||
this->floor,
|
this->floor,
|
||||||
@@ -3416,8 +3416,7 @@ string MapFile::ObjectSetEntry::str(Version version, uint8_t area) const {
|
|||||||
this->param3,
|
this->param3,
|
||||||
this->param4,
|
this->param4,
|
||||||
this->param5,
|
this->param5,
|
||||||
this->param6,
|
this->param6);
|
||||||
this->unused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t MapFile::ObjectSetEntry::semantic_hash(uint8_t floor) const {
|
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 {
|
string MapFile::EnemySetEntry::str(Version version, uint8_t area) const {
|
||||||
auto type_name = MapFile::name_for_enemy_type(this->base_type, version, area);
|
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,
|
this->base_type,
|
||||||
type_name,
|
type_name,
|
||||||
this->num_children,
|
this->num_children,
|
||||||
@@ -3459,8 +3458,7 @@ string MapFile::EnemySetEntry::str(Version version, uint8_t area) const {
|
|||||||
this->param4,
|
this->param4,
|
||||||
this->param5,
|
this->param5,
|
||||||
this->param6,
|
this->param6,
|
||||||
this->param7,
|
this->param7);
|
||||||
this->unused);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t MapFile::EnemySetEntry::semantic_hash(uint8_t floor) const {
|
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(
|
MapState::MapState(
|
||||||
uint64_t lobby_or_session_id,
|
uint64_t lobby_or_session_id,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint32_t random_seed,
|
uint32_t random_seed,
|
||||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
||||||
@@ -6001,7 +5999,7 @@ MapState::MapState(
|
|||||||
|
|
||||||
MapState::MapState(
|
MapState::MapState(
|
||||||
uint64_t lobby_or_session_id,
|
uint64_t lobby_or_session_id,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint32_t random_seed,
|
uint32_t random_seed,
|
||||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
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()) {
|
for (const auto& ene : fc.super_map->all_enemies()) {
|
||||||
auto& ene_st = this->enemy_states.emplace_back(make_shared<EnemyState>());
|
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) {
|
if (ene->child_index == 0) {
|
||||||
this->enemy_set_states.emplace_back(ene_st);
|
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;
|
EnemyType type;
|
||||||
switch (ene->type) {
|
switch (ene->type) {
|
||||||
case EnemyType::DARK_FALZ_3:
|
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_2
|
||||||
: EnemyType::DARK_FALZ_3;
|
: EnemyType::DARK_FALZ_3;
|
||||||
break;
|
break;
|
||||||
case EnemyType::DARVANT:
|
case EnemyType::DARVANT:
|
||||||
type = (this->difficulty == 3) ? EnemyType::DARVANT_ULTIMATE : EnemyType::DARVANT;
|
type = (this->difficulty == Difficulty::ULTIMATE) ? EnemyType::DARVANT_ULTIMATE : EnemyType::DARVANT;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
type = ene->type;
|
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, "BB rare rates: {}\n", rare_rates_str);
|
||||||
|
|
||||||
phosg::fwrite_fmt(stream, "Base indexes:\n");
|
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");
|
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++) {
|
for (size_t floor = 0; floor < this->floor_config_entries.size(); floor++) {
|
||||||
auto fc = this->floor_config_entries[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, "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) {
|
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);
|
const auto& fc = this->floor_config(ene_st->super_ene->floor);
|
||||||
for (Version v : ALL_NON_PATCH_VERSIONS) {
|
for (Version v : ALL_NON_PATCH_VERSIONS) {
|
||||||
const auto& ene_v = ene_st->super_ene->version(v);
|
const auto& ene_v = ene_st->super_ene->version(v);
|
||||||
|
|||||||
+11
-6
@@ -187,7 +187,7 @@ public:
|
|||||||
/* 34 */ le_int32_t param4 = 0;
|
/* 34 */ le_int32_t param4 = 0;
|
||||||
/* 38 */ le_int32_t param5 = 0;
|
/* 38 */ le_int32_t param5 = 0;
|
||||||
/* 3C */ le_int32_t param6 = 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 */
|
/* 44 */
|
||||||
|
|
||||||
uint64_t semantic_hash(uint8_t floor) const;
|
uint64_t semantic_hash(uint8_t floor) const;
|
||||||
@@ -215,7 +215,7 @@ public:
|
|||||||
/* 3C */ le_float param5 = 0.0f;
|
/* 3C */ le_float param5 = 0.0f;
|
||||||
/* 40 */ le_int16_t param6 = 0;
|
/* 40 */ le_int16_t param6 = 0;
|
||||||
/* 42 */ le_int16_t param7 = 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 */
|
/* 48 */
|
||||||
|
|
||||||
uint64_t semantic_hash(uint8_t floor) const;
|
uint64_t semantic_hash(uint8_t floor) const;
|
||||||
@@ -710,6 +710,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct EnemyState {
|
struct EnemyState {
|
||||||
|
std::shared_ptr<EnemyState> alias_ene_st; // Null for most enemies
|
||||||
std::shared_ptr<const SuperMap::Enemy> super_ene;
|
std::shared_ptr<const SuperMap::Enemy> super_ene;
|
||||||
enum Flag {
|
enum Flag {
|
||||||
LAST_HIT_MASK = 0x0003,
|
LAST_HIT_MASK = 0x0003,
|
||||||
@@ -745,7 +746,7 @@ public:
|
|||||||
inline void set_mericarand_variant_flag(Version version) {
|
inline void set_mericarand_variant_flag(Version version) {
|
||||||
this->mericarand_variant_flags |= (1 << static_cast<size_t>(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->super_ene->type == EnemyType::MERICARAND) {
|
||||||
if (this->is_rare(version)) {
|
if (this->is_rare(version)) {
|
||||||
return ((this->mericarand_variant_flags >> static_cast<size_t>(version)) & 1)
|
return ((this->mericarand_variant_flags >> static_cast<size_t>(version)) & 1)
|
||||||
@@ -754,6 +755,10 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
return EnemyType::MERICAROL;
|
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 {
|
} else {
|
||||||
return this->is_rare(version)
|
return this->is_rare(version)
|
||||||
? type_definition_for_enemy(this->super_ene->type).rare_type(episode, event, this->super_ene->floor)
|
? type_definition_for_enemy(this->super_ene->type).rare_type(episode, event, this->super_ene->floor)
|
||||||
@@ -874,7 +879,7 @@ public:
|
|||||||
|
|
||||||
phosg::PrefixedLogger log;
|
phosg::PrefixedLogger log;
|
||||||
std::vector<FloorConfig> floor_config_entries;
|
std::vector<FloorConfig> floor_config_entries;
|
||||||
uint8_t difficulty = 0;
|
Difficulty difficulty = Difficulty::NORMAL;
|
||||||
uint8_t event = 0;
|
uint8_t event = 0;
|
||||||
uint32_t random_seed = 0;
|
uint32_t random_seed = 0;
|
||||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates;
|
std::shared_ptr<const RareEnemyRates> bb_rare_rates;
|
||||||
@@ -889,7 +894,7 @@ public:
|
|||||||
// Constructor for free play
|
// Constructor for free play
|
||||||
MapState(
|
MapState(
|
||||||
uint64_t lobby_or_session_id,
|
uint64_t lobby_or_session_id,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint32_t random_seed, // For client-matched rare enemies (non-BB)
|
uint32_t random_seed, // For client-matched rare enemies (non-BB)
|
||||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
||||||
@@ -898,7 +903,7 @@ public:
|
|||||||
// Constructor for quests
|
// Constructor for quests
|
||||||
MapState(
|
MapState(
|
||||||
uint64_t lobby_or_session_id,
|
uint64_t lobby_or_session_id,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
uint32_t random_seed, // For client-matched rare enemies (non-BB)
|
uint32_t random_seed, // For client-matched rare enemies (non-BB)
|
||||||
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
std::shared_ptr<const RareEnemyRates> bb_rare_rates,
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ asio::awaitable<void> PatchDownloadSession::run() {
|
|||||||
this->io_context,
|
this->io_context,
|
||||||
std::move(sock),
|
std::move(sock),
|
||||||
this->version,
|
this->version,
|
||||||
1,
|
Language::ENGLISH,
|
||||||
netloc_str,
|
netloc_str,
|
||||||
this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END,
|
this->show_command_data ? phosg::TerminalFormat::FG_GREEN : phosg::TerminalFormat::END,
|
||||||
this->show_command_data ? phosg::TerminalFormat::FG_YELLOW : 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;
|
/* 0000 */ uint8_t num_items = 0;
|
||||||
/* 0001 */ uint8_t hp_from_materials = 0;
|
/* 0001 */ uint8_t hp_from_materials = 0;
|
||||||
/* 0002 */ uint8_t tp_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;
|
/* 0004 */ parray<PlayerInventoryItemT<BE>, 30> items;
|
||||||
/* 034C */
|
/* 034C */
|
||||||
|
|
||||||
@@ -265,14 +265,14 @@ struct PlayerInventoryT {
|
|||||||
// issue - its inventory format matches the rest of the versions.
|
// issue - its inventory format matches the rest of the versions.
|
||||||
this->hp_from_materials = 0;
|
this->hp_from_materials = 0;
|
||||||
this->tp_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)) {
|
} else if ((v != Version::PC_NTE) && (v != Version::PC_V2)) {
|
||||||
if (this->language > 4) {
|
if (static_cast<size_t>(this->language) > 4) {
|
||||||
this->language = 0;
|
this->language = Language::JAPANESE;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this->language > 7) {
|
if (static_cast<size_t>(this->language) > 7) {
|
||||||
this->language = 0;
|
this->language = Language::JAPANESE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ void GuildCardBB::clear() {
|
|||||||
this->team_name.clear();
|
this->team_name.clear();
|
||||||
this->description.clear();
|
this->description.clear();
|
||||||
this->present = 0;
|
this->present = 0;
|
||||||
this->language = 0;
|
this->language = Language::JAPANESE;
|
||||||
this->section_id = 0;
|
this->section_id = 0;
|
||||||
this->char_class = 0;
|
this->char_class = 0;
|
||||||
}
|
}
|
||||||
@@ -218,11 +218,11 @@ PlayerRecordsChallengeBB::PlayerRecordsChallengeBB(const PlayerRecordsChallengeD
|
|||||||
grave_x(rec.grave_x),
|
grave_x(rec.grave_x),
|
||||||
grave_y(rec.grave_y),
|
grave_y(rec.grave_y),
|
||||||
grave_z(rec.grave_z),
|
grave_z(rec.grave_z),
|
||||||
grave_team(rec.grave_team.decode(), 1),
|
grave_team(rec.grave_team.decode(), Language::ENGLISH),
|
||||||
grave_message(rec.grave_message.decode(), 1),
|
grave_message(rec.grave_message.decode(), Language::ENGLISH),
|
||||||
unknown_m5(0),
|
unknown_m5(0),
|
||||||
unknown_t6(0),
|
unknown_t6(0),
|
||||||
rank_title(rec.rank_title.decode(), 1),
|
rank_title(rec.rank_title.decode(), Language::ENGLISH),
|
||||||
unknown_l7(0) {}
|
unknown_l7(0) {}
|
||||||
|
|
||||||
PlayerRecordsChallengeBB::PlayerRecordsChallengeBB(const PlayerRecordsChallengePC& rec)
|
PlayerRecordsChallengeBB::PlayerRecordsChallengeBB(const PlayerRecordsChallengePC& rec)
|
||||||
@@ -242,11 +242,11 @@ PlayerRecordsChallengeBB::PlayerRecordsChallengeBB(const PlayerRecordsChallengeP
|
|||||||
grave_x(rec.grave_x),
|
grave_x(rec.grave_x),
|
||||||
grave_y(rec.grave_y),
|
grave_y(rec.grave_y),
|
||||||
grave_z(rec.grave_z),
|
grave_z(rec.grave_z),
|
||||||
grave_team(rec.grave_team.decode(), 1),
|
grave_team(rec.grave_team.decode(), Language::ENGLISH),
|
||||||
grave_message(rec.grave_message.decode(), 1),
|
grave_message(rec.grave_message.decode(), Language::ENGLISH),
|
||||||
unknown_m5(0),
|
unknown_m5(0),
|
||||||
unknown_t6(0),
|
unknown_t6(0),
|
||||||
rank_title(rec.rank_title.decode(), 1),
|
rank_title(rec.rank_title.decode(), Language::ENGLISH),
|
||||||
unknown_l7(0) {}
|
unknown_l7(0) {}
|
||||||
|
|
||||||
PlayerRecordsChallengeBB::operator PlayerRecordsChallengeDC() const {
|
PlayerRecordsChallengeBB::operator PlayerRecordsChallengeDC() const {
|
||||||
|
|||||||
+37
-30
@@ -328,7 +328,7 @@ struct PlayerDispDataDCPCV3T {
|
|||||||
this->visual.enforce_lobby_join_limits_for_version(v);
|
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));
|
} __attribute__((packed));
|
||||||
using PlayerDispDataDCPCV3 = PlayerDispDataDCPCV3T<false>;
|
using PlayerDispDataDCPCV3 = PlayerDispDataDCPCV3T<false>;
|
||||||
using PlayerDispDataDCPCV3BE = PlayerDispDataDCPCV3T<true>;
|
using PlayerDispDataDCPCV3BE = PlayerDispDataDCPCV3T<true>;
|
||||||
@@ -361,7 +361,7 @@ struct PlayerDispDataBB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <bool BE>
|
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;
|
PlayerDispDataDCPCV3T<BE> ret;
|
||||||
ret.stats = this->stats;
|
ret.stats = this->stats;
|
||||||
ret.visual = this->visual;
|
ret.visual = this->visual;
|
||||||
@@ -377,7 +377,7 @@ struct PlayerDispDataBB {
|
|||||||
} __packed_ws__(PlayerDispDataBB, 0x190);
|
} __packed_ws__(PlayerDispDataBB, 0x190);
|
||||||
|
|
||||||
template <bool BE>
|
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;
|
PlayerDispDataBB bb;
|
||||||
bb.stats = this->stats;
|
bb.stats = this->stats;
|
||||||
bb.visual = this->visual;
|
bb.visual = this->visual;
|
||||||
@@ -398,7 +398,7 @@ struct GuildCardDCNTE {
|
|||||||
/* 20 */ pstring<TextEncoding::MARKED, 0x48> description;
|
/* 20 */ pstring<TextEncoding::MARKED, 0x48> description;
|
||||||
/* 68 */ parray<uint8_t, 0x0F> unused2;
|
/* 68 */ parray<uint8_t, 0x0F> unused2;
|
||||||
/* 77 */ uint8_t present = 0;
|
/* 77 */ uint8_t present = 0;
|
||||||
/* 78 */ uint8_t language = 0;
|
/* 78 */ Language language = Language::JAPANESE;
|
||||||
/* 79 */ uint8_t section_id = 0;
|
/* 79 */ uint8_t section_id = 0;
|
||||||
/* 7A */ uint8_t char_class = 0;
|
/* 7A */ uint8_t char_class = 0;
|
||||||
/* 7B */
|
/* 7B */
|
||||||
@@ -413,7 +413,7 @@ struct GuildCardDC {
|
|||||||
/* 20 */ pstring<TextEncoding::MARKED, 0x48> description;
|
/* 20 */ pstring<TextEncoding::MARKED, 0x48> description;
|
||||||
/* 68 */ parray<uint8_t, 0x11> unused2;
|
/* 68 */ parray<uint8_t, 0x11> unused2;
|
||||||
/* 79 */ uint8_t present = 0;
|
/* 79 */ uint8_t present = 0;
|
||||||
/* 7A */ uint8_t language = 0;
|
/* 7A */ Language language = Language::JAPANESE;
|
||||||
/* 7B */ uint8_t section_id = 0;
|
/* 7B */ uint8_t section_id = 0;
|
||||||
/* 7C */ uint8_t char_class = 0;
|
/* 7C */ uint8_t char_class = 0;
|
||||||
/* 7D */
|
/* 7D */
|
||||||
@@ -428,7 +428,7 @@ struct GuildCardPC {
|
|||||||
/* 08 */ pstring<TextEncoding::UTF16, 0x18> name;
|
/* 08 */ pstring<TextEncoding::UTF16, 0x18> name;
|
||||||
/* 38 */ pstring<TextEncoding::UTF16, 0x5A> description;
|
/* 38 */ pstring<TextEncoding::UTF16, 0x5A> description;
|
||||||
/* EC */ uint8_t present = 0;
|
/* EC */ uint8_t present = 0;
|
||||||
/* ED */ uint8_t language = 0;
|
/* ED */ Language language = Language::JAPANESE;
|
||||||
/* EE */ uint8_t section_id = 0;
|
/* EE */ uint8_t section_id = 0;
|
||||||
/* EF */ uint8_t char_class = 0;
|
/* EF */ uint8_t char_class = 0;
|
||||||
/* F0 */
|
/* F0 */
|
||||||
@@ -456,7 +456,7 @@ struct GuildCardGCT {
|
|||||||
/* 08:08 */ pstring<TextEncoding::ASCII, 0x18> name;
|
/* 08:08 */ pstring<TextEncoding::ASCII, 0x18> name;
|
||||||
/* 20:20 */ pstring<TextEncoding::MARKED, DescriptionLength> description;
|
/* 20:20 */ pstring<TextEncoding::MARKED, DescriptionLength> description;
|
||||||
/* A0:8C */ uint8_t present = 0;
|
/* 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;
|
/* A2:8E */ uint8_t section_id = 0;
|
||||||
/* A3:8F */ uint8_t char_class = 0;
|
/* A3:8F */ uint8_t char_class = 0;
|
||||||
/* A4:90 */
|
/* A4:90 */
|
||||||
@@ -480,7 +480,7 @@ struct GuildCardXB {
|
|||||||
/* 0010 */ pstring<TextEncoding::ASCII, 0x18> name;
|
/* 0010 */ pstring<TextEncoding::ASCII, 0x18> name;
|
||||||
/* 0028 */ pstring<TextEncoding::MARKED, 0x200> description;
|
/* 0028 */ pstring<TextEncoding::MARKED, 0x200> description;
|
||||||
/* 0228 */ uint8_t present = 0;
|
/* 0228 */ uint8_t present = 0;
|
||||||
/* 0229 */ uint8_t language = 0;
|
/* 0229 */ Language language = Language::JAPANESE;
|
||||||
/* 022A */ uint8_t section_id = 0;
|
/* 022A */ uint8_t section_id = 0;
|
||||||
/* 022B */ uint8_t char_class = 0;
|
/* 022B */ uint8_t char_class = 0;
|
||||||
/* 022C */
|
/* 022C */
|
||||||
@@ -494,7 +494,7 @@ struct GuildCardBB {
|
|||||||
/* 0034 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> team_name;
|
/* 0034 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> team_name;
|
||||||
/* 0054 */ pstring<TextEncoding::UTF16, 0x58> description;
|
/* 0054 */ pstring<TextEncoding::UTF16, 0x58> description;
|
||||||
/* 0104 */ uint8_t present = 0;
|
/* 0104 */ uint8_t present = 0;
|
||||||
/* 0105 */ uint8_t language = 0;
|
/* 0105 */ Language language = Language::JAPANESE;
|
||||||
/* 0106 */ uint8_t section_id = 0;
|
/* 0106 */ uint8_t section_id = 0;
|
||||||
/* 0107 */ uint8_t char_class = 0;
|
/* 0107 */ uint8_t char_class = 0;
|
||||||
/* 0108 */
|
/* 0108 */
|
||||||
@@ -776,13 +776,13 @@ struct PlayerRecordsChallengeBB {
|
|||||||
grave_x(rec.grave_x),
|
grave_x(rec.grave_x),
|
||||||
grave_y(rec.grave_y),
|
grave_y(rec.grave_y),
|
||||||
grave_z(rec.grave_z),
|
grave_z(rec.grave_z),
|
||||||
grave_team(rec.grave_team.decode(), 1),
|
grave_team(rec.grave_team.decode(), Language::ENGLISH),
|
||||||
grave_message(rec.grave_message.decode(), 1),
|
grave_message(rec.grave_message.decode(), Language::ENGLISH),
|
||||||
unknown_m5(rec.unknown_m5),
|
unknown_m5(rec.unknown_m5),
|
||||||
ep1_online_award_state(rec.ep1_online_award_state),
|
ep1_online_award_state(rec.ep1_online_award_state),
|
||||||
ep2_online_award_state(rec.ep2_online_award_state),
|
ep2_online_award_state(rec.ep2_online_award_state),
|
||||||
ep1_offline_award_state(rec.ep1_offline_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) {
|
unknown_l7(rec.unknown_l7) {
|
||||||
for (size_t z = 0; z < std::min<size_t>(this->unknown_t6.size(), rec.unknown_t6.size()); z++) {
|
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];
|
this->unknown_t6[z] = rec.unknown_t6[z];
|
||||||
@@ -810,8 +810,8 @@ struct PlayerRecordsChallengeBB {
|
|||||||
ret.grave_x = this->grave_x;
|
ret.grave_x = this->grave_x;
|
||||||
ret.grave_y = this->grave_y;
|
ret.grave_y = this->grave_y;
|
||||||
ret.grave_z = this->grave_z;
|
ret.grave_z = this->grave_z;
|
||||||
ret.grave_team.encode(this->grave_team.decode(), 1);
|
ret.grave_team.encode(this->grave_team.decode(), Language::ENGLISH);
|
||||||
ret.grave_message.encode(this->grave_message.decode(), 1);
|
ret.grave_message.encode(this->grave_message.decode(), Language::ENGLISH);
|
||||||
ret.unknown_m5 = this->unknown_m5;
|
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++) {
|
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];
|
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.ep1_online_award_state = this->ep1_online_award_state;
|
||||||
ret.ep2_online_award_state = this->ep2_online_award_state;
|
ret.ep2_online_award_state = this->ep2_online_award_state;
|
||||||
ret.ep1_offline_award_state = this->ep1_offline_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;
|
ret.unknown_l7 = this->unknown_l7;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -856,31 +856,31 @@ check_struct_size(PlayerRecordsBattle, 0x18);
|
|||||||
check_struct_size(PlayerRecordsBattleBE, 0x18);
|
check_struct_size(PlayerRecordsBattleBE, 0x18);
|
||||||
|
|
||||||
template <typename DestT, typename SrcT = DestT>
|
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,
|
static_assert(phosg::always_false<DestT, SrcT>::v,
|
||||||
"unspecialized convert_player_disp_data should never be called");
|
"unspecialized convert_player_disp_data should never be called");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
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;
|
return src;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline PlayerDispDataDCPCV3 convert_player_disp_data<PlayerDispDataDCPCV3, PlayerDispDataBB>(
|
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);
|
return src.to_dcpcv3<false>(to_language, from_language);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB, PlayerDispDataDCPCV3>(
|
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);
|
return src.to_bb(to_language, from_language);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB>(
|
inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB>(
|
||||||
const PlayerDispDataBB& src, uint8_t, uint8_t) {
|
const PlayerDispDataBB& src, Language, Language) {
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -916,21 +916,28 @@ struct QuestFlagsForDifficulty {
|
|||||||
struct QuestFlags {
|
struct QuestFlags {
|
||||||
parray<QuestFlagsForDifficulty, 4> data;
|
parray<QuestFlagsForDifficulty, 4> data;
|
||||||
|
|
||||||
inline bool get(uint8_t difficulty, uint16_t flag_index) const {
|
inline QuestFlagsForDifficulty& for_difficulty(Difficulty difficulty) {
|
||||||
return this->data[difficulty].get(flag_index);
|
return this->data[static_cast<size_t>(difficulty)];
|
||||||
}
|
}
|
||||||
inline void set(uint8_t difficulty, uint16_t flag_index) {
|
inline const QuestFlagsForDifficulty& for_difficulty(Difficulty difficulty) const {
|
||||||
this->data[difficulty].set(flag_index);
|
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) {
|
inline void set(Difficulty difficulty, uint16_t flag_index) {
|
||||||
this->data[difficulty].update_all(set);
|
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) {
|
inline void update_all(bool set) {
|
||||||
for (size_t z = 0; z < 4; z++) {
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
this->update_all(z, set);
|
this->update_all(difficulty, set);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} __packed_ws__(QuestFlags, 0x200);
|
} __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());
|
G_SpecializableItemDropRequest_6xA2 cmd = normalize_drop_request(msg.data.data(), msg.data.size());
|
||||||
auto rec = reconcile_drop_request_with_map(
|
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;
|
ItemCreator::DropResult res;
|
||||||
if (rec.obj_st) {
|
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_game = false;
|
||||||
c->proxy_session->is_in_quest = false;
|
c->proxy_session->is_in_quest = false;
|
||||||
c->floor = 0x0F;
|
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_section_id = 0;
|
||||||
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
||||||
c->proxy_session->lobby_episode = Episode::EP1;
|
c->proxy_session->lobby_episode = Episode::EP1;
|
||||||
@@ -1682,7 +1688,7 @@ static asio::awaitable<HandlerResult> S_64(shared_ptr<Client> c, Channel::Messag
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
c->proxy_session->lobby_event = 0;
|
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_section_id = c->character_file()->disp.visual.section_id;
|
||||||
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
||||||
c->proxy_session->lobby_random_seed = phosg::random_object<uint32_t>();
|
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_game = true;
|
||||||
c->proxy_session->is_in_quest = false;
|
c->proxy_session->is_in_quest = false;
|
||||||
c->proxy_session->lobby_event = cmd.event;
|
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_section_id = cmd.section_id;
|
||||||
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
||||||
c->proxy_session->lobby_random_seed = 0;
|
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_game = false;
|
||||||
c->proxy_session->is_in_quest = false;
|
c->proxy_session->is_in_quest = false;
|
||||||
c->proxy_session->lobby_event = 0;
|
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_section_id = 0;
|
||||||
c->proxy_session->lobby_episode = Episode::EP1;
|
c->proxy_session->lobby_episode = Episode::EP1;
|
||||||
c->proxy_session->lobby_mode = GameMode::NORMAL;
|
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->rare_item_set(version, nullptr),
|
||||||
s->armor_random_set,
|
s->armor_random_set,
|
||||||
s->tool_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->tekker_adjustment_set,
|
||||||
s->item_parameter_table(version),
|
s->item_parameter_table(version),
|
||||||
s->item_stack_limits(version),
|
s->item_stack_limits(version),
|
||||||
|
|||||||
+2
-2
@@ -28,7 +28,7 @@ struct ProxySession {
|
|||||||
uint32_t guild_card_number = 0;
|
uint32_t guild_card_number = 0;
|
||||||
uint64_t xb_user_id = 0;
|
uint64_t xb_user_id = 0;
|
||||||
std::string name;
|
std::string name;
|
||||||
uint8_t language = 0;
|
Language language = Language::JAPANESE;
|
||||||
uint8_t section_id = 0;
|
uint8_t section_id = 0;
|
||||||
uint8_t char_class = 0;
|
uint8_t char_class = 0;
|
||||||
};
|
};
|
||||||
@@ -38,7 +38,7 @@ struct ProxySession {
|
|||||||
bool is_in_quest = false;
|
bool is_in_quest = false;
|
||||||
uint8_t leader_client_id = 0;
|
uint8_t leader_client_id = 0;
|
||||||
uint8_t lobby_event = 0;
|
uint8_t lobby_event = 0;
|
||||||
uint8_t lobby_difficulty = 0;
|
Difficulty lobby_difficulty = Difficulty::NORMAL;
|
||||||
uint8_t lobby_section_id = 0;
|
uint8_t lobby_section_id = 0;
|
||||||
GameMode lobby_mode = GameMode::NORMAL;
|
GameMode lobby_mode = GameMode::NORMAL;
|
||||||
Episode lobby_episode = Episode::EP1;
|
Episode lobby_episode = Episode::EP1;
|
||||||
|
|||||||
+33
-32
@@ -203,7 +203,7 @@ void VersionedQuest::assert_valid() const {
|
|||||||
if (this->version == Version::UNKNOWN) {
|
if (this->version == Version::UNKNOWN) {
|
||||||
throw runtime_error("version is not set");
|
throw runtime_error("version is not set");
|
||||||
}
|
}
|
||||||
if (this->language == 0xFF) {
|
if (this->language == Language::UNKNOWN) {
|
||||||
throw runtime_error("language is not set");
|
throw runtime_error("language is not set");
|
||||||
}
|
}
|
||||||
switch (this->meta.episode) {
|
switch (this->meta.episode) {
|
||||||
@@ -290,7 +290,7 @@ string VersionedQuest::pvr_filename() const {
|
|||||||
|
|
||||||
string VersionedQuest::xb_filename() const {
|
string VersionedQuest::xb_filename() const {
|
||||||
return std::format("quest{}_{}.dat",
|
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 {
|
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);
|
files.emplace(std::format("quest{}.pvr", this->meta.quest_number), this->pvr_contents);
|
||||||
}
|
}
|
||||||
string xb_filename = std::format("quest{}_{}.dat",
|
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);
|
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) {
|
for (const auto& [_, vq] : this->versions) {
|
||||||
versions_json.emplace_back(phosg::JSON::dict({
|
versions_json.emplace_back(phosg::JSON::dict({
|
||||||
{"Version", phosg::name_for_enum(vq->version)},
|
{"Version", phosg::name_for_enum(vq->version)},
|
||||||
{"Language", name_for_language_code(vq->language)},
|
{"Language", name_for_language(vq->language)},
|
||||||
{"Name", vq->meta.name},
|
{"Name", vq->meta.name},
|
||||||
{"ShortDescription", vq->meta.short_description},
|
{"ShortDescription", vq->meta.short_description},
|
||||||
{"LongDescription", vq->meta.long_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) {
|
uint32_t Quest::versions_key(Version v, Language language) {
|
||||||
return (static_cast<uint32_t>(v) << 8) | language;
|
return (static_cast<uint32_t>(v) << 8) | static_cast<uint8_t>(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
const string& Quest::name_for_language(uint8_t language) const {
|
const string& Quest::name_for_language(Language language) const {
|
||||||
if (!this->names_by_language.at(language).empty()) {
|
size_t lang_index = static_cast<size_t>(language);
|
||||||
return this->names_by_language[language];
|
if (!this->names_by_language.at(lang_index).empty()) {
|
||||||
|
return this->names_by_language[lang_index];
|
||||||
}
|
}
|
||||||
if (!this->names_by_language[1].empty()) {
|
constexpr size_t english_lang_index = static_cast<size_t>(Language::ENGLISH);
|
||||||
return this->names_by_language[1];
|
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) {
|
for (const string& name : this->names_by_language) {
|
||||||
if (!name.empty()) {
|
if (!name.empty()) {
|
||||||
@@ -354,7 +356,8 @@ void Quest::add_version(shared_ptr<const VersionedQuest> vq) {
|
|||||||
this->meta.assert_compatible(vq->meta);
|
this->meta.assert_compatible(vq->meta);
|
||||||
this->versions.emplace(this->versions_key(vq->version, vq->language), vq);
|
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()) {
|
if (name_by_language.empty()) {
|
||||||
name_by_language = vq->meta.name;
|
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;
|
bool any_map_file_present = false;
|
||||||
array<shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
|
array<shared_ptr<const MapFile>, NUM_VERSIONS> map_files;
|
||||||
for (Version v : ALL_NON_PATCH_VERSIONS) {
|
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) {
|
if (vq && vq->map_file) {
|
||||||
auto map_file = vq->map_file;
|
auto map_file = vq->map_file;
|
||||||
if (map_file->has_random_sections()) {
|
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;
|
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));
|
return this->versions.count(this->versions_key(v, language));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Quest::has_version_any_language(Version v) const {
|
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);
|
auto it = this->versions.lower_bound(k);
|
||||||
return ((it != this->versions.end()) && ((it->first & 0xFF00) == 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
|
// Return the requested version, if it exists
|
||||||
try {
|
try {
|
||||||
return this->versions.at(this->versions_key(v, language));
|
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
|
// Return the English version, if it exists
|
||||||
try {
|
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&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the first language, if it exists
|
// Return the first language, if it exists
|
||||||
auto it = this->versions.lower_bound(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, 0))) {
|
if ((it == this->versions.end()) || ((it->first & 0xFF00) != this->versions_key(v, Language::JAPANESE))) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return it->second;
|
return it->second;
|
||||||
@@ -646,7 +649,7 @@ QuestIndex::QuestIndex(
|
|||||||
if (language_token.size() != 1) {
|
if (language_token.size() != 1) {
|
||||||
throw runtime_error("language token is not a single character");
|
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);
|
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);
|
auto q_it = this->quests_by_number.find(vq->meta.quest_number);
|
||||||
if (q_it != this->quests_by_number.end()) {
|
if (q_it != this->quests_by_number.end()) {
|
||||||
q_it->second->add_version(vq);
|
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,
|
filenames_str,
|
||||||
phosg::name_for_enum(vq->version),
|
phosg::name_for_enum(vq->version),
|
||||||
char_for_language_code(vq->language),
|
char_for_language(vq->language),
|
||||||
vq->meta.quest_number,
|
vq->meta.quest_number,
|
||||||
vq->meta.name,
|
vq->meta.name);
|
||||||
phosg::format_data_string(vq->meta.area_for_floor.data(), 0x12));
|
|
||||||
} else {
|
} else {
|
||||||
auto q = make_shared<Quest>(vq);
|
auto q = make_shared<Quest>(vq);
|
||||||
this->quests_by_number.emplace(vq->meta.quest_number, q);
|
this->quests_by_number.emplace(vq->meta.quest_number, q);
|
||||||
this->quests_by_name.emplace(vq->meta.name, 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);
|
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,
|
filenames_str,
|
||||||
phosg::name_for_enum(vq->version),
|
phosg::name_for_enum(vq->version),
|
||||||
char_for_language_code(vq->language),
|
char_for_language(vq->language),
|
||||||
vq->meta.quest_number,
|
vq->meta.quest_number,
|
||||||
vq->meta.name,
|
vq->meta.name,
|
||||||
name_for_episode(vq->meta.episode),
|
name_for_episode(vq->meta.episode),
|
||||||
category_name,
|
category_name,
|
||||||
vq->meta.category_id,
|
vq->meta.category_id,
|
||||||
vq->meta.joinable ? "joinable" : "not joinable",
|
vq->meta.joinable ? "joinable" : "not joinable");
|
||||||
phosg::format_data_string(vq->meta.area_for_floor.data(), 0x12));
|
|
||||||
}
|
}
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
static_game_data_log.warning_f("({}) Failed to index quest file: {}", basename, e.what());
|
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;
|
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
|
// 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
|
// 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
|
// 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)) {
|
if (decompressed_bin.size() < sizeof(PSOQuestHeaderDC)) {
|
||||||
throw runtime_error("bin file is too small for header");
|
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;
|
reinterpret_cast<PSOQuestHeaderDC*>(data_ptr)->language = override_language;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -967,7 +968,7 @@ shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(uint8_t overrid
|
|||||||
if (decompressed_bin.size() < sizeof(PSOQuestHeaderPC)) {
|
if (decompressed_bin.size() < sizeof(PSOQuestHeaderPC)) {
|
||||||
throw runtime_error("bin file is too small for header");
|
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;
|
reinterpret_cast<PSOQuestHeaderPC*>(data_ptr)->language = override_language;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -977,7 +978,7 @@ shared_ptr<VersionedQuest> VersionedQuest::create_download_quest(uint8_t overrid
|
|||||||
if (decompressed_bin.size() < sizeof(PSOQuestHeaderGC)) {
|
if (decompressed_bin.size() < sizeof(PSOQuestHeaderGC)) {
|
||||||
throw runtime_error("bin file is too small for header");
|
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;
|
reinterpret_cast<PSOQuestHeaderGC*>(data_ptr)->language = override_language;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
+6
-6
@@ -72,7 +72,7 @@ struct VersionedQuest {
|
|||||||
// Most of these default values are intentionally invalid; we use these
|
// Most of these default values are intentionally invalid; we use these
|
||||||
// values to check if each field was parsed during quest indexing.
|
// values to check if each field was parsed during quest indexing.
|
||||||
Version version = Version::UNKNOWN;
|
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> bin_contents;
|
||||||
std::shared_ptr<const std::string> dat_contents;
|
std::shared_ptr<const std::string> dat_contents;
|
||||||
std::shared_ptr<const MapFile> map_file;
|
std::shared_ptr<const MapFile> map_file;
|
||||||
@@ -86,7 +86,7 @@ struct VersionedQuest {
|
|||||||
std::string pvr_filename() const;
|
std::string pvr_filename() const;
|
||||||
std::string xb_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;
|
std::string encode_qst() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -107,14 +107,14 @@ struct Quest {
|
|||||||
|
|
||||||
std::shared_ptr<const SuperMap> get_supermap(int64_t random_seed) const;
|
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);
|
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;
|
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 {
|
struct QuestIndex {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ void QuestMetadata::assert_compatible(const QuestMetadata& other) const {
|
|||||||
if (this->challenge_difficulty != other.challenge_difficulty) {
|
if (this->challenge_difficulty != other.challenge_difficulty) {
|
||||||
throw runtime_error(std::format(
|
throw runtime_error(std::format(
|
||||||
"quest version has different challenge difficulty (existing: {}, new: {})",
|
"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++) {
|
for (size_t z = 0; z < this->area_for_floor.size(); z++) {
|
||||||
const auto& this_fa = this->area_for_floor[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)},
|
{"BattleRules", this->battle_rules ? this->battle_rules->json() : phosg::JSON(nullptr)},
|
||||||
{"ChallengeTemplateIndex", (this->challenge_template_index >= 0) ? this->challenge_template_index : 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)},
|
{"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},
|
{"DescriptionFlag", this->description_flag},
|
||||||
{"AvailableExpression", this->available_expression ? this->available_expression->str() : phosg::JSON(nullptr)},
|
{"AvailableExpression", this->available_expression ? this->available_expression->str() : phosg::JSON(nullptr)},
|
||||||
{"EnabledExpression", 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;
|
std::shared_ptr<const BattleRules> battle_rules;
|
||||||
ssize_t challenge_template_index = -1;
|
ssize_t challenge_template_index = -1;
|
||||||
float challenge_exp_multiplier = -1.0f;
|
float challenge_exp_multiplier = -1.0f;
|
||||||
int8_t challenge_difficulty = -1;
|
Difficulty challenge_difficulty = Difficulty::UNKNOWN;
|
||||||
uint8_t description_flag = 0x00;
|
uint8_t description_flag = 0x00;
|
||||||
std::shared_ptr<const IntegralExpression> available_expression;
|
std::shared_ptr<const IntegralExpression> available_expression;
|
||||||
std::shared_ptr<const IntegralExpression> enabled_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) {
|
static TextEncoding encoding_for_language(Language language) {
|
||||||
return (language ? TextEncoding::ISO8859 : TextEncoding::SJIS);
|
return ((language == Language::JAPANESE) ? TextEncoding::SJIS : TextEncoding::ISO8859);
|
||||||
}
|
}
|
||||||
|
|
||||||
static string escape_string(const string& data, TextEncoding encoding = TextEncoding::UTF8) {
|
static string escape_string(const string& data, TextEncoding encoding = TextEncoding::UTF8) {
|
||||||
@@ -2970,7 +2970,7 @@ std::string disassemble_quest_script(
|
|||||||
const void* data,
|
const void* data,
|
||||||
size_t size,
|
size_t size,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t override_language,
|
Language override_language,
|
||||||
bool reassembly_mode,
|
bool reassembly_mode,
|
||||||
bool use_qedit_names) {
|
bool use_qedit_names) {
|
||||||
phosg::StringReader r(data, size);
|
phosg::StringReader r(data, size);
|
||||||
@@ -2980,14 +2980,14 @@ std::string disassemble_quest_script(
|
|||||||
bool use_wstrs = false;
|
bool use_wstrs = false;
|
||||||
size_t code_offset = 0;
|
size_t code_offset = 0;
|
||||||
size_t function_table_offset = 0;
|
size_t function_table_offset = 0;
|
||||||
uint8_t language;
|
Language language;
|
||||||
switch (version) {
|
switch (version) {
|
||||||
case Version::DC_NTE: {
|
case Version::DC_NTE: {
|
||||||
const auto& header = r.get<PSOQuestHeaderDCNTE>();
|
const auto& header = r.get<PSOQuestHeaderDCNTE>();
|
||||||
code_offset = header.code_offset;
|
code_offset = header.code_offset;
|
||||||
function_table_offset = header.function_table_offset;
|
function_table_offset = header.function_table_offset;
|
||||||
language = 0;
|
language = Language::JAPANESE;
|
||||||
lines.emplace_back(".name " + escape_string(header.name.decode(0)));
|
lines.emplace_back(".name " + escape_string(header.name.decode(Language::JAPANESE)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Version::DC_11_2000:
|
case Version::DC_11_2000:
|
||||||
@@ -2996,15 +2996,15 @@ std::string disassemble_quest_script(
|
|||||||
const auto& header = r.get<PSOQuestHeaderDC>();
|
const auto& header = r.get<PSOQuestHeaderDC>();
|
||||||
code_offset = header.code_offset;
|
code_offset = header.code_offset;
|
||||||
function_table_offset = header.function_table_offset;
|
function_table_offset = header.function_table_offset;
|
||||||
if (override_language != 0xFF) {
|
if (override_language != Language::UNKNOWN) {
|
||||||
language = override_language;
|
language = override_language;
|
||||||
} else if (header.language < 5) {
|
} else if (static_cast<size_t>(header.language) < 5) {
|
||||||
language = header.language;
|
language = header.language;
|
||||||
} else {
|
} else {
|
||||||
language = 1;
|
language = Language::ENGLISH;
|
||||||
}
|
}
|
||||||
lines.emplace_back(std::format(".quest_num {}", header.quest_number));
|
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(".name " + escape_string(header.name.decode(language)));
|
||||||
lines.emplace_back(".short_desc " + escape_string(header.short_description.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)));
|
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>();
|
const auto& header = r.get<PSOQuestHeaderPC>();
|
||||||
code_offset = header.code_offset;
|
code_offset = header.code_offset;
|
||||||
function_table_offset = header.function_table_offset;
|
function_table_offset = header.function_table_offset;
|
||||||
if (override_language != 0xFF) {
|
if (override_language != Language::UNKNOWN) {
|
||||||
language = override_language;
|
language = override_language;
|
||||||
} else if (header.language < 8) {
|
} else if (static_cast<size_t>(header.language) < 8) {
|
||||||
language = header.language;
|
language = header.language;
|
||||||
} else {
|
} else {
|
||||||
language = 1;
|
language = Language::ENGLISH;
|
||||||
}
|
}
|
||||||
lines.emplace_back(std::format(".quest_num {}", header.quest_number));
|
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(".name " + escape_string(header.name.decode(language)));
|
||||||
lines.emplace_back(".short_desc " + escape_string(header.short_description.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)));
|
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>();
|
const auto& header = r.get<PSOQuestHeaderGC>();
|
||||||
code_offset = header.code_offset;
|
code_offset = header.code_offset;
|
||||||
function_table_offset = header.function_table_offset;
|
function_table_offset = header.function_table_offset;
|
||||||
if (override_language != 0xFF) {
|
if (override_language != Language::UNKNOWN) {
|
||||||
language = override_language;
|
language = override_language;
|
||||||
} else if (header.language < 5) {
|
} else if (static_cast<size_t>(header.language) < 5) {
|
||||||
language = header.language;
|
language = header.language;
|
||||||
} else {
|
} else {
|
||||||
language = 1;
|
language = Language::ENGLISH;
|
||||||
}
|
}
|
||||||
lines.emplace_back(std::format(".quest_num {}", header.quest_number));
|
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(".name " + escape_string(header.name.decode(language)));
|
||||||
lines.emplace_back(".short_desc " + escape_string(header.short_description.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)));
|
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>();
|
const auto& header = r.get<PSOQuestHeaderBB>();
|
||||||
code_offset = header.code_offset;
|
code_offset = header.code_offset;
|
||||||
function_table_offset = header.function_table_offset;
|
function_table_offset = header.function_table_offset;
|
||||||
if (override_language != 0xFF) {
|
if (override_language != Language::UNKNOWN) {
|
||||||
language = override_language;
|
language = override_language;
|
||||||
} else {
|
} else {
|
||||||
language = 1;
|
language = Language::ENGLISH;
|
||||||
}
|
}
|
||||||
lines.emplace_back(std::format(".quest_num {}", header.quest_number));
|
lines.emplace_back(std::format(".quest_num {}", header.quest_number));
|
||||||
lines.emplace_back(std::format(".episode {}", name_for_header_episode_number(header.episode)));
|
lines.emplace_back(std::format(".episode {}", name_for_header_episode_number(header.episode)));
|
||||||
@@ -3335,7 +3335,7 @@ std::string disassemble_quest_script(
|
|||||||
} else {
|
} else {
|
||||||
string s = cmd_r.get_cstr();
|
string s = cmd_r.get_cstr();
|
||||||
if (def->flags & F_PUSH_ARG) {
|
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));
|
dasm_arg = escape_string(s, encoding_for_language(language));
|
||||||
}
|
}
|
||||||
@@ -4008,7 +4008,7 @@ AssembledQuestScript assemble_quest_script(
|
|||||||
string quest_short_desc;
|
string quest_short_desc;
|
||||||
string quest_long_desc;
|
string quest_long_desc;
|
||||||
int64_t quest_num = -1;
|
int64_t quest_num = -1;
|
||||||
uint8_t quest_language = 1;
|
Language quest_language = Language::ENGLISH;
|
||||||
Episode quest_episode = Episode::EP1;
|
Episode quest_episode = Episode::EP1;
|
||||||
uint8_t quest_max_players = 4;
|
uint8_t quest_max_players = 4;
|
||||||
bool quest_joinable = false;
|
bool quest_joinable = false;
|
||||||
@@ -4033,7 +4033,11 @@ AssembledQuestScript assemble_quest_script(
|
|||||||
} else if (line.text.starts_with(".quest_num ")) {
|
} else if (line.text.starts_with(".quest_num ")) {
|
||||||
quest_num = stoul(line.text.substr(11), nullptr, 0);
|
quest_num = stoul(line.text.substr(11), nullptr, 0);
|
||||||
} else if (line.text.starts_with(".language ")) {
|
} 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 ")) {
|
} else if (line.text.starts_with(".episode ")) {
|
||||||
quest_episode = episode_for_token_name(line.text.substr(9));
|
quest_episode = episode_for_token_name(line.text.substr(9));
|
||||||
} else if (line.text.starts_with(".max_players ")) {
|
} 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_NTE:
|
||||||
case Version::GC_EP3:
|
case Version::GC_EP3:
|
||||||
case Version::XB_V3:
|
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);
|
code_w.put_u8(0);
|
||||||
break;
|
break;
|
||||||
case Version::PC_NTE:
|
case Version::PC_NTE:
|
||||||
@@ -4571,7 +4575,7 @@ AssembledQuestScript assemble_quest_script(
|
|||||||
header.code_offset = sizeof(header);
|
header.code_offset = sizeof(header);
|
||||||
header.function_table_offset = sizeof(header) + code_w.size();
|
header.function_table_offset = sizeof(header) + code_w.size();
|
||||||
header.size = header.function_table_offset + function_table.size() * sizeof(function_table[0]);
|
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);
|
w.put(header);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4662,7 +4666,7 @@ AssembledQuestScript assemble_quest_script(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void populate_quest_metadata_from_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);
|
phosg::StringReader r(data, size);
|
||||||
uint32_t code_offset = r.size();
|
uint32_t code_offset = r.size();
|
||||||
uint32_t function_table_offset = r.size();
|
uint32_t function_table_offset = r.size();
|
||||||
@@ -5192,7 +5196,10 @@ void populate_quest_metadata_from_script(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xF824: // set_cmode_difficulty
|
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);
|
// phosg::fwrite_fmt(stderr, ">>> Trace: meta.challenge_difficulty = {}\n", meta.challenge_difficulty);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -38,7 +38,7 @@ struct PSOQuestHeaderDC { // Same format for DC v1 and v2
|
|||||||
/* 0008 */ le_uint32_t size = 0;
|
/* 0008 */ le_uint32_t size = 0;
|
||||||
/* 000C */ le_uint16_t unknown_a1 = 0;
|
/* 000C */ le_uint16_t unknown_a1 = 0;
|
||||||
/* 000E */ le_uint16_t unknown_a2 = 0;
|
/* 000E */ le_uint16_t unknown_a2 = 0;
|
||||||
/* 0010 */ uint8_t language = 0;
|
/* 0010 */ Language language = Language::JAPANESE;
|
||||||
/* 0011 */ uint8_t unknown_a3 = 0;
|
/* 0011 */ uint8_t unknown_a3 = 0;
|
||||||
/* 0012 */ le_uint16_t quest_number = 0; // 0xFFFF for challenge quests
|
/* 0012 */ le_uint16_t quest_number = 0; // 0xFFFF for challenge quests
|
||||||
/* 0014 */ pstring<TextEncoding::MARKED, 0x20> name;
|
/* 0014 */ pstring<TextEncoding::MARKED, 0x20> name;
|
||||||
@@ -53,7 +53,7 @@ struct PSOQuestHeaderPC {
|
|||||||
/* 0008 */ le_uint32_t size = 0;
|
/* 0008 */ le_uint32_t size = 0;
|
||||||
/* 000C */ le_uint16_t unknown_a1 = 0;
|
/* 000C */ le_uint16_t unknown_a1 = 0;
|
||||||
/* 000E */ le_uint16_t unknown_a2 = 0;
|
/* 000E */ le_uint16_t unknown_a2 = 0;
|
||||||
/* 0010 */ uint8_t language = 0;
|
/* 0010 */ Language language = Language::JAPANESE;
|
||||||
/* 0011 */ uint8_t unknown_a3 = 0;
|
/* 0011 */ uint8_t unknown_a3 = 0;
|
||||||
/* 0012 */ le_uint16_t quest_number = 0; // 0xFFFF for challenge quests
|
/* 0012 */ le_uint16_t quest_number = 0; // 0xFFFF for challenge quests
|
||||||
/* 0014 */ pstring<TextEncoding::UTF16, 0x20> name;
|
/* 0014 */ pstring<TextEncoding::UTF16, 0x20> name;
|
||||||
@@ -70,7 +70,7 @@ struct PSOQuestHeaderGC {
|
|||||||
/* 0008 */ le_uint32_t size = 0;
|
/* 0008 */ le_uint32_t size = 0;
|
||||||
/* 000C */ le_uint16_t unknown_a1 = 0;
|
/* 000C */ le_uint16_t unknown_a1 = 0;
|
||||||
/* 000E */ le_uint16_t unknown_a2 = 0;
|
/* 000E */ le_uint16_t unknown_a2 = 0;
|
||||||
/* 0010 */ uint8_t language = 0;
|
/* 0010 */ Language language = Language::JAPANESE;
|
||||||
/* 0011 */ uint8_t unknown_a3 = 0;
|
/* 0011 */ uint8_t unknown_a3 = 0;
|
||||||
// Note: The GC client byteswaps this field, then loads it as a byte, so
|
// 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
|
// 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,
|
const void* data,
|
||||||
size_t size,
|
size_t size,
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t override_language = 0xFF,
|
Language override_language = Language::UNKNOWN,
|
||||||
bool reassembly_mode = false,
|
bool reassembly_mode = false,
|
||||||
bool use_qedit_names = false);
|
bool use_qedit_names = false);
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ struct AssembledQuestScript {
|
|||||||
std::string data;
|
std::string data;
|
||||||
int64_t quest_number = -1;
|
int64_t quest_number = -1;
|
||||||
Version version = Version::UNKNOWN;
|
Version version = Version::UNKNOWN;
|
||||||
uint8_t language = 0xFF;
|
Language language = Language::UNKNOWN;
|
||||||
Episode episode = Episode::NONE;
|
Episode episode = Episode::NONE;
|
||||||
bool joinable = false;
|
bool joinable = false;
|
||||||
uint8_t max_players = 0x00;
|
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>& script_include_directories,
|
||||||
const std::vector<std::string>& native_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) {
|
RareItemSet::RareItemSet(const AFSArchive& afs, bool is_v1) {
|
||||||
const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
for (GameMode mode : ALL_GAME_MODES_V4) {
|
||||||
for (GameMode mode : modes) {
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
|
|
||||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
for (size_t section_id = 0; section_id < 10; section_id++) {
|
||||||
try {
|
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);
|
ParsedRELData rel(afs.get_reader(index), false, is_v1);
|
||||||
this->collections.emplace(
|
this->collections.emplace(
|
||||||
this->key_for_params(mode, Episode::EP1, difficulty, section_id),
|
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",
|
return std::format("ItemRT{}{}{}{}.rel",
|
||||||
((mode == GameMode::CHALLENGE) ? "c" : ""),
|
((mode == GameMode::CHALLENGE) ? "c" : ""),
|
||||||
((episode == Episode::EP2) ? "l" : ""),
|
((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) {
|
RareItemSet::RareItemSet(const GSLArchive& gsl, bool is_big_endian) {
|
||||||
const array<Episode, 2> episodes = {Episode::EP1, Episode::EP2};
|
for (GameMode mode : ALL_GAME_MODES_V23) {
|
||||||
const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
for (Episode episode : ALL_EPISODES_V3) {
|
||||||
for (GameMode mode : modes) {
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
for (Episode episode : episodes) {
|
|
||||||
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
|
|
||||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
for (size_t section_id = 0; section_id < 10; section_id++) {
|
||||||
try {
|
try {
|
||||||
string filename = this->gsl_entry_name_for_table(mode, episode, difficulty, section_id);
|
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) {
|
RareItemSet::RareItemSet(const string& rel_data, bool is_big_endian) {
|
||||||
// Tables are 0x280 bytes in size in this format, laid out sequentially
|
// Tables are 0x280 bytes in size in this format, laid out sequentially
|
||||||
phosg::StringReader r(rel_data);
|
phosg::StringReader r(rel_data);
|
||||||
array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (Episode episode : ALL_EPISODES_V4) {
|
||||||
for (size_t ep_index = 0; ep_index < episodes.size(); ep_index++) {
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
|
|
||||||
for (size_t section_id = 0; section_id < 10; section_id++) {
|
for (size_t section_id = 0; section_id < 10; section_id++) {
|
||||||
try {
|
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);
|
ParsedRELData rel(r.sub(0x280 * index, 0x280), is_big_endian, false);
|
||||||
this->collections.emplace(
|
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());
|
rel.as_collection());
|
||||||
} catch (const out_of_range&) {
|
} 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);
|
Episode episode = episode_keys.at(episode_it.first);
|
||||||
|
|
||||||
for (const auto& difficulty_it : episode_it.second->as_dict()) {
|
for (const auto& difficulty_it : episode_it.second->as_dict()) {
|
||||||
static const unordered_map<string, uint8_t> difficulty_keys(
|
static const unordered_map<string, Difficulty> difficulty_keys(
|
||||||
{{"Normal", 0}, {"Hard", 1}, {"VeryHard", 2}, {"Ultimate", 3}});
|
{{"Normal", Difficulty::NORMAL}, {"Hard", Difficulty::HARD}, {"VeryHard", Difficulty::VERY_HARD}, {"Ultimate", Difficulty::ULTIMATE}});
|
||||||
uint8_t difficulty = difficulty_keys.at(difficulty_it.first);
|
Difficulty difficulty = difficulty_keys.at(difficulty_it.first);
|
||||||
|
|
||||||
for (const auto& section_id_it : difficulty_it.second->as_dict()) {
|
for (const auto& section_id_it : difficulty_it.second->as_dict()) {
|
||||||
uint8_t section_id = section_id_for_name(section_id_it.first);
|
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 {
|
std::string RareItemSet::serialize_afs(bool is_v1) const {
|
||||||
vector<string> files;
|
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++) {
|
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||||
ParsedRELData rel(this->get_collection(GameMode::NORMAL, Episode::EP1, difficulty, section_id));
|
ParsedRELData rel(this->get_collection(GameMode::NORMAL, Episode::EP1, difficulty, section_id));
|
||||||
files.emplace_back(rel.serialize(false, is_v1));
|
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 {
|
std::string RareItemSet::serialize_gsl(bool big_endian) const {
|
||||||
unordered_map<string, string> files;
|
unordered_map<string, string> files;
|
||||||
|
|
||||||
static const std::array<Episode, 2> episodes = {Episode::EP1, Episode::EP2};
|
for (Episode episode : ALL_EPISODES_V3) {
|
||||||
for (Episode episode : episodes) {
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
|
||||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||||
try {
|
try {
|
||||||
string filename = this->gsl_entry_name_for_table(GameMode::NORMAL, episode, difficulty, section_id);
|
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++) {
|
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||||
try {
|
try {
|
||||||
string filename = this->gsl_entry_name_for_table(GameMode::CHALLENGE, Episode::EP1, difficulty, section_id);
|
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(
|
string RareItemSet::serialize_html(
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
shared_ptr<const ItemNameIndex> name_index,
|
shared_ptr<const ItemNameIndex> name_index,
|
||||||
shared_ptr<const CommonItemSet> common_item_set) const {
|
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);
|
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 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);
|
add_specs_row(&type_def, name, false, specs_lists);
|
||||||
}
|
}
|
||||||
for (uint8_t floor : zone_type.floors) {
|
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 {
|
phosg::JSON RareItemSet::json(shared_ptr<const ItemNameIndex> name_index) const {
|
||||||
auto modes_dict = phosg::JSON::dict();
|
auto modes_dict = phosg::JSON::dict();
|
||||||
static const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
for (const auto& mode : ALL_GAME_MODES_V4) {
|
||||||
for (const auto& mode : modes) {
|
|
||||||
auto episodes_dict = phosg::JSON::dict();
|
auto episodes_dict = phosg::JSON::dict();
|
||||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (const auto& episode : ALL_EPISODES_V4) {
|
||||||
for (const auto& episode : episodes) {
|
|
||||||
auto difficulty_dict = phosg::JSON::dict();
|
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();
|
auto section_id_dict = phosg::JSON::dict();
|
||||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||||
auto collection_dict = phosg::JSON::dict();
|
auto collection_dict = phosg::JSON::dict();
|
||||||
@@ -884,7 +881,7 @@ void RareItemSet::print_collection(
|
|||||||
FILE* stream,
|
FILE* stream,
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
shared_ptr<const ItemNameIndex> name_index) const {
|
shared_ptr<const ItemNameIndex> name_index) const {
|
||||||
const SpecCollection* collection;
|
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 {
|
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};
|
for (GameMode mode : ALL_GAME_MODES_V4) {
|
||||||
static const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (Episode episode : ALL_EPISODES_V4) {
|
||||||
for (GameMode mode : modes) {
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
for (Episode episode : episodes) {
|
|
||||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
|
||||||
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||||
try {
|
try {
|
||||||
this->print_collection(stream, mode, episode, difficulty, section_id, name_index);
|
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(
|
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 {
|
try {
|
||||||
return this->get_collection(mode, episode, difficulty, secid).rt_index_to_specs.at(rt_index);
|
return this->get_collection(mode, episode, difficulty, secid).rt_index_to_specs.at(rt_index);
|
||||||
} catch (const out_of_range&) {
|
} 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(
|
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 {
|
try {
|
||||||
return this->get_collection(mode, episode, difficulty, secid).box_area_norm_to_specs.at(area_norm);
|
return this->get_collection(mode, episode, difficulty, secid).box_area_norm_to_specs.at(area_norm);
|
||||||
} catch (const out_of_range&) {
|
} 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++) {
|
for (uint8_t section_id = 0; section_id < 10; section_id++) {
|
||||||
if (this->collections.count(this->key_for_params(mode, episode, difficulty, section_id))) {
|
if (this->collections.count(this->key_for_params(mode, episode, difficulty, section_id))) {
|
||||||
return true;
|
return true;
|
||||||
@@ -974,19 +969,19 @@ bool RareItemSet::has_entries_for_game_config(GameMode mode, Episode episode, ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
const RareItemSet::SpecCollection& RareItemSet::get_collection(
|
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));
|
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) {
|
uint16_t RareItemSet::key_for_params(GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid) {
|
||||||
if (difficulty > 3) {
|
if (static_cast<size_t>(difficulty) > 3) {
|
||||||
throw logic_error("incorrect difficulty");
|
throw logic_error("incorrect difficulty");
|
||||||
}
|
}
|
||||||
if (secid > 10) {
|
if (secid > 10) {
|
||||||
throw logic_error("incorrect section id");
|
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) {
|
switch (mode) {
|
||||||
case GameMode::NORMAL:
|
case GameMode::NORMAL:
|
||||||
break;
|
break;
|
||||||
|
|||||||
+8
-8
@@ -34,18 +34,18 @@ public:
|
|||||||
~RareItemSet() = default;
|
~RareItemSet() = default;
|
||||||
|
|
||||||
std::vector<ExpandedDrop> get_enemy_specs(
|
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(
|
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_afs(bool is_v1) const;
|
||||||
std::string serialize_gsl(bool big_endian) const;
|
std::string serialize_gsl(bool big_endian) const;
|
||||||
std::string serialize_html(
|
std::string serialize_html(
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
std::shared_ptr<const ItemNameIndex> name_index = nullptr,
|
std::shared_ptr<const ItemNameIndex> name_index = nullptr,
|
||||||
std::shared_ptr<const CommonItemSet> common_item_set = nullptr) const;
|
std::shared_ptr<const CommonItemSet> common_item_set = nullptr) const;
|
||||||
phosg::JSON json(std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
phosg::JSON json(std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
||||||
@@ -56,7 +56,7 @@ public:
|
|||||||
FILE* stream,
|
FILE* stream,
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
|
||||||
void print_all_collections(FILE* stream, 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;
|
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 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, uint8_t difficulty, uint8_t secid);
|
static uint16_t key_for_params(GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid);
|
||||||
|
|
||||||
static uint32_t expand_rate(uint8_t pc);
|
static uint32_t expand_rate(uint8_t pc);
|
||||||
static uint8_t compress_rate(uint32_t probability);
|
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) {
|
if (!q) {
|
||||||
c->log.info_f("There is no quest to enable server function calls for specific version {:08X}", c->specific_version);
|
c->log.info_f("There is no quest to enable server function calls for specific version {:08X}", c->specific_version);
|
||||||
} else if (q) {
|
} else if (q) {
|
||||||
auto vq = q->version(c->version(), 1);
|
auto vq = q->version(c->version(), Language::ENGLISH);
|
||||||
if (vq) {
|
if (vq) {
|
||||||
c->set_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
|
c->set_flag(Client::Flag::HAS_SEND_FUNCTION_CALL);
|
||||||
c->set_flag(Client::Flag::SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE);
|
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;
|
lobby_data.guild_card_number = c->login->account->account_id;
|
||||||
send_command_t(c, 0x64, 0x01, cmd);
|
send_command_t(c, 0x64, 0x01, cmd);
|
||||||
} else {
|
} 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 bin_filename = vq->bin_filename();
|
||||||
string dat_filename = vq->dat_filename();
|
string dat_filename = vq->dat_filename();
|
||||||
string xb_filename = vq->xb_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()) {
|
for (const auto& file : index->all_files()) {
|
||||||
send_patch_change_to_directory(c, path_directories, file->path_directories);
|
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->channel->send(0x0C, 0x00, req);
|
||||||
c->patch_file_checksum_requests.emplace_back(file);
|
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()) {
|
if (req.needs_update()) {
|
||||||
send_patch_change_to_directory(c, path_directories, req.file->path_directories);
|
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);
|
c->channel->send(0x06, 0x00, open_cmd);
|
||||||
|
|
||||||
for (size_t x = 0; x < req.file->chunk_crcs.size(); x++) {
|
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->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) {
|
if (base_cmd.menu_id == MenuID::LOBBY) {
|
||||||
c->preferred_lobby_id = base_cmd.preferred_lobby_id;
|
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,
|
version_token,
|
||||||
name_for_char_class(player->disp.visual.char_class),
|
name_for_char_class(player->disp.visual.char_class),
|
||||||
player->disp.stats.level + 1,
|
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->allowed_drop_modes = l->quest->meta.allowed_drop_modes;
|
||||||
l->drop_mode = l->quest->meta.default_drop_mode;
|
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->difficulty = l->quest->meta.challenge_difficulty;
|
||||||
}
|
}
|
||||||
l->create_item_creator();
|
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();
|
lc->channel->disconnect();
|
||||||
break;
|
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 bin_filename = vq->bin_filename();
|
||||||
string dat_filename = vq->dat_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 map = s->ep3_download_map_index->get(item_id);
|
||||||
auto vm = map->version(c->language());
|
auto vm = map->version(c->language());
|
||||||
auto name = vm->map->name.decode(vm->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);
|
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);
|
send_open_quest_file(c, name, filename, "", map->map_number, QuestFileType::EPISODE_3, data);
|
||||||
co_return;
|
co_return;
|
||||||
@@ -4136,14 +4136,15 @@ static asio::awaitable<void> on_DF_BB(shared_ptr<Client> c, Channel::Message& ms
|
|||||||
if (!l->quest) {
|
if (!l->quest) {
|
||||||
throw runtime_error("challenge mode difficulty config command sent in non-challenge game");
|
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");
|
throw runtime_error("incorrect difficulty level");
|
||||||
}
|
}
|
||||||
if (l->difficulty != cmd.difficulty) {
|
if (l->difficulty != cmd_difficulty) {
|
||||||
l->difficulty = cmd.difficulty;
|
l->difficulty = cmd_difficulty;
|
||||||
l->create_item_creator();
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4491,7 +4492,7 @@ shared_ptr<Lobby> create_game_generic(
|
|||||||
const std::string& password,
|
const std::string& password,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
bool allow_v1,
|
bool allow_v1,
|
||||||
shared_ptr<Lobby> watched_lobby,
|
shared_ptr<Lobby> watched_lobby,
|
||||||
shared_ptr<Episode3::BattleRecordPlayer> battle_player) {
|
shared_ptr<Episode3::BattleRecordPlayer> battle_player) {
|
||||||
@@ -4503,7 +4504,7 @@ shared_ptr<Lobby> create_game_generic(
|
|||||||
throw invalid_argument("incorrect episode number");
|
throw invalid_argument("incorrect episode number");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (difficulty > 3) {
|
if (static_cast<size_t>(difficulty) > 3) {
|
||||||
throw invalid_argument("incorrect difficulty level");
|
throw invalid_argument("incorrect difficulty level");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4527,7 +4528,7 @@ shared_ptr<Lobby> create_game_generic(
|
|||||||
game->difficulty = difficulty;
|
game->difficulty = difficulty;
|
||||||
game->allowed_versions = s->compatibility_groups.at(static_cast<size_t>(creator_c->version()));
|
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");
|
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_NTE);
|
||||||
game->forbid_version(Version::DC_11_2000);
|
game->forbid_version(Version::DC_11_2000);
|
||||||
game->forbid_version(Version::DC_V1);
|
game->forbid_version(Version::DC_V1);
|
||||||
@@ -4701,7 +4702,7 @@ shared_ptr<Lobby> create_game_generic(
|
|||||||
if (game->mode == GameMode::CHALLENGE) {
|
if (game->mode == GameMode::CHALLENGE) {
|
||||||
game->rare_enemy_rates = s->rare_enemy_rates_challenge;
|
game->rare_enemy_rates = s->rare_enemy_rates_challenge;
|
||||||
} else {
|
} 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) {
|
if (game->episode != Episode::EP3) {
|
||||||
@@ -4736,7 +4737,7 @@ shared_ptr<Lobby> create_game_generic(
|
|||||||
|
|
||||||
if (quest_flag_rewrites && !quest_flag_rewrites->empty()) {
|
if (quest_flag_rewrites && !quest_flag_rewrites->empty()) {
|
||||||
IntegralExpression::Env env = {
|
IntegralExpression::Env env = {
|
||||||
.flags = &p->quest_flags.data.at(difficulty),
|
.flags = &p->quest_flags.for_difficulty(difficulty),
|
||||||
.challenge_records = &p->challenge_records,
|
.challenge_records = &p->challenge_records,
|
||||||
.team = creator_c->team(),
|
.team = creator_c->team(),
|
||||||
.num_players = 1,
|
.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;
|
shared_ptr<Lobby> game;
|
||||||
if (is_pre_v1(c->version())) {
|
if (is_pre_v1(c->version())) {
|
||||||
const auto& cmd = check_size_t<C_CreateGame_DCNTE>(msg.data);
|
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 {
|
} else {
|
||||||
const auto& cmd = check_size_t<C_CreateGame_DC_V3_0C_C1_Ep3_EC>(msg.data);
|
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&) {
|
} catch (const out_of_range&) {
|
||||||
throw std::logic_error("cannot find patch enable quest after it was previously found during login");
|
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) {
|
if (!vq) {
|
||||||
throw std::logic_error("cannot find patch enable quest version after it was previously found during login");
|
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 bin_filename = vq->bin_filename();
|
||||||
string dat_filename = vq->dat_filename();
|
string dat_filename = vq->dat_filename();
|
||||||
string xb_filename = vq->xb_filename();
|
string xb_filename = vq->xb_filename();
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ std::shared_ptr<Lobby> create_game_generic(
|
|||||||
const std::string& password = "",
|
const std::string& password = "",
|
||||||
Episode episode = Episode::EP1,
|
Episode episode = Episode::EP1,
|
||||||
GameMode mode = GameMode::NORMAL,
|
GameMode mode = GameMode::NORMAL,
|
||||||
uint8_t difficulty = 0,
|
Difficulty difficulty = Difficulty::NORMAL,
|
||||||
bool allow_v1 = false,
|
bool allow_v1 = false,
|
||||||
std::shared_ptr<Lobby> watched_lobby = nullptr,
|
std::shared_ptr<Lobby> watched_lobby = nullptr,
|
||||||
std::shared_ptr<Episode3::BattleRecordPlayer> battle_player = nullptr);
|
std::shared_ptr<Episode3::BattleRecordPlayer> battle_player = nullptr);
|
||||||
|
|||||||
+46
-24
@@ -805,7 +805,7 @@ Parsed6x70Data::Parsed6x70Data(
|
|||||||
unknown_a6_nte(cmd.unknown_a6),
|
unknown_a6_nte(cmd.unknown_a6),
|
||||||
bonus_hp_from_materials(0),
|
bonus_hp_from_materials(0),
|
||||||
bonus_tp_from_materials(0),
|
bonus_tp_from_materials(0),
|
||||||
language(0),
|
language(Language::JAPANESE),
|
||||||
player_tag(0x00010000),
|
player_tag(0x00010000),
|
||||||
guild_card_number(guild_card_number),
|
guild_card_number(guild_card_number),
|
||||||
unknown_a6(0),
|
unknown_a6(0),
|
||||||
@@ -829,7 +829,7 @@ Parsed6x70Data::Parsed6x70Data(
|
|||||||
Parsed6x70Data::Parsed6x70Data(
|
Parsed6x70Data::Parsed6x70Data(
|
||||||
const G_SyncPlayerDispAndInventory_DC112000_6x70& cmd,
|
const G_SyncPlayerDispAndInventory_DC112000_6x70& cmd,
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint8_t language,
|
Language language,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
bool from_client_customization)
|
bool from_client_customization)
|
||||||
: from_version(from_version),
|
: from_version(from_version),
|
||||||
@@ -922,8 +922,8 @@ Parsed6x70Data::Parsed6x70Data(
|
|||||||
this->floor = cmd.floor;
|
this->floor = cmd.floor;
|
||||||
this->xb_user_id = this->default_xb_user_id();
|
this->xb_user_id = this->default_xb_user_id();
|
||||||
this->xb_unknown_a16 = cmd.unknown_a16;
|
this->xb_unknown_a16 = cmd.unknown_a16;
|
||||||
this->name = cmd.name.decode(cmd.base.language);
|
this->name = cmd.name.decode(this->language);
|
||||||
this->visual.name.encode(this->name, cmd.base.language);
|
this->visual.name.encode(this->name, this->language);
|
||||||
}
|
}
|
||||||
|
|
||||||
G_SyncPlayerDispAndInventory_DCNTE_6x70 Parsed6x70Data::as_dc_nte(shared_ptr<ServerState> s) const {
|
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;
|
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;
|
G_SyncPlayerDispAndInventory_BB_6x70 ret;
|
||||||
ret.base = this->base_v1(true);
|
ret.base = this->base_v1(true);
|
||||||
ret.name.encode(this->name, language);
|
ret.name.encode(this->name, language);
|
||||||
@@ -1116,7 +1116,7 @@ Parsed6x70Data::Parsed6x70Data(
|
|||||||
attack_status_effect(base.attack_status_effect),
|
attack_status_effect(base.attack_status_effect),
|
||||||
defense_status_effect(base.defense_status_effect),
|
defense_status_effect(base.defense_status_effect),
|
||||||
unused_status_effect(base.unused_status_effect),
|
unused_status_effect(base.unused_status_effect),
|
||||||
language(base.language),
|
language(static_cast<Language>(base.language32.load())),
|
||||||
player_tag(base.player_tag),
|
player_tag(base.player_tag),
|
||||||
guild_card_number(guild_card_number), // Ignore the client's GC#
|
guild_card_number(guild_card_number), // Ignore the client's GC#
|
||||||
unknown_a6(base.unknown_a6),
|
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.attack_status_effect = this->attack_status_effect;
|
||||||
ret.defense_status_effect = this->defense_status_effect;
|
ret.defense_status_effect = this->defense_status_effect;
|
||||||
ret.unused_status_effect = this->unused_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.player_tag = this->player_tag;
|
||||||
ret.guild_card_number = this->guild_card_number;
|
ret.guild_card_number = this->guild_card_number;
|
||||||
ret.unknown_a6 = this->unknown_a6;
|
ret.unknown_a6 = this->unknown_a6;
|
||||||
@@ -2805,6 +2805,7 @@ DropReconcileResult reconcile_drop_request_with_map(
|
|||||||
shared_ptr<Client> c,
|
shared_ptr<Client> c,
|
||||||
G_SpecializableItemDropRequest_6xA2& cmd,
|
G_SpecializableItemDropRequest_6xA2& cmd,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
|
Difficulty difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
shared_ptr<MapState> map,
|
shared_ptr<MapState> map,
|
||||||
bool mark_drop) {
|
bool mark_drop) {
|
||||||
@@ -2857,7 +2858,7 @@ DropReconcileResult reconcile_drop_request_with_map(
|
|||||||
} else {
|
} else {
|
||||||
if (map) {
|
if (map) {
|
||||||
res.ene_st = map->enemy_state_for_index(version, cmd.floor, cmd.entity_index);
|
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));
|
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;
|
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
|
// 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
|
// mode, so that we can correctly mark enemies and objects as having dropped
|
||||||
// their items in persistent games.
|
// their items in persistent games.
|
||||||
G_SpecializableItemDropRequest_6xA2 cmd = normalize_drop_request(msg.data, msg.size);
|
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;
|
ServerDropMode drop_mode = l->drop_mode;
|
||||||
switch (drop_mode) {
|
switch (drop_mode) {
|
||||||
@@ -3058,7 +3059,8 @@ static asio::awaitable<void> on_set_quest_flag(shared_ptr<Client> c, SubcommandM
|
|||||||
co_return;
|
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)) {
|
if (is_v1_or_v2(c->version()) && (c->version() != Version::GC_NTE)) {
|
||||||
const auto& cmd = msg.check_size_t<G_UpdateQuestFlag_DC_PC_6x75>();
|
const auto& cmd = msg.check_size_t<G_UpdateQuestFlag_DC_PC_6x75>();
|
||||||
flag_num = cmd.flag;
|
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>();
|
const auto& cmd = msg.check_size_t<G_UpdateQuestFlag_V3_BB_6x75>();
|
||||||
flag_num = cmd.flag;
|
flag_num = cmd.flag;
|
||||||
action = cmd.action;
|
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
|
// The client explicitly checks action for both 0 and 1 - any other value
|
||||||
// means no operation is performed.
|
// 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;
|
co_return;
|
||||||
}
|
}
|
||||||
bool should_set = (action == 0);
|
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
|
// 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
|
// request after the end of the second phase. On all other difficulty
|
||||||
// levels, send it after the third phase.
|
// 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;
|
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;
|
boss_enemy_type = EnemyType::DARK_FALZ_3;
|
||||||
}
|
}
|
||||||
} else if (is_ep2 && (flag_num == 0x0057) && (area == 0x0D)) {
|
} 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);
|
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();
|
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);
|
src_flags = (src_flags & 0xFFFFFFC0) | (ene_st->game_flags & 0x0000003F);
|
||||||
}
|
}
|
||||||
ene_st->game_flags = src_flags;
|
ene_st->game_flags = src_flags;
|
||||||
ene_st->total_damage = cmd.total_damage;
|
ene_st->total_damage = cmd.total_damage;
|
||||||
ene_st->set_last_hit_by_client_id(c->lobby_client_id);
|
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);
|
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) {
|
for (auto lc : l->clients) {
|
||||||
if (lc && (lc != c)) {
|
if (lc && (lc != c)) {
|
||||||
@@ -3485,7 +3494,7 @@ static asio::awaitable<void> on_set_enemy_low_game_flags_ultimate(shared_ptr<Cli
|
|||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
auto l = c->require_lobby();
|
auto l = c->require_lobby();
|
||||||
if (!l->is_game() || (l->difficulty != 3)) {
|
if (!l->is_game() || (l->difficulty != Difficulty::ULTIMATE)) {
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3888,7 +3897,7 @@ static uint32_t base_exp_for_enemy_type(
|
|||||||
shared_ptr<const BattleParamsIndex> bp_index,
|
shared_ptr<const BattleParamsIndex> bp_index,
|
||||||
EnemyType enemy_type,
|
EnemyType enemy_type,
|
||||||
Episode current_episode,
|
Episode current_episode,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
uint8_t area,
|
uint8_t area,
|
||||||
bool is_solo) {
|
bool is_solo) {
|
||||||
// Always try the current episode first. If the current episode is Ep4, try
|
// 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 {
|
try {
|
||||||
const auto& bp_table = bp_index->get_table(is_solo, episode);
|
const auto& bp_table = bp_index->get_table(is_solo, episode);
|
||||||
uint32_t bp_index = type_definition_for_enemy(enemy_type).bp_index;
|
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&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3971,16 +3980,16 @@ static asio::awaitable<void> on_steal_exp_bb(shared_ptr<Client> c, SubcommandMes
|
|||||||
co_return;
|
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(
|
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);
|
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
|
// 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
|
// applying the android bonus if so. We don't do anything for those special
|
||||||
// types, so we don't check for that here.
|
// 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;
|
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)) {
|
if (c->check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||||
c->log.info_f("Stolen EXP from E-{:03X} with enemy_exp={} percent={:g} stolen_exp={}",
|
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);
|
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).
|
// relevant players EXP then, if they deserve it).
|
||||||
if (!ene_st->ever_hit_by_client_id(c->lobby_client_id) ||
|
if (!ene_st->ever_hit_by_client_id(c->lobby_client_id) ||
|
||||||
(ene_st->server_flags & MapState::EnemyState::Flag::EXP_GIVEN)) {
|
(ene_st->server_flags & MapState::EnemyState::Flag::EXP_GIVEN)) {
|
||||||
|
l->log.info_f("EXP already given for this enemy; ignoring request");
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
ene_st->server_flags |= MapState::EnemyState::Flag::EXP_GIVEN;
|
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(
|
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);
|
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++) {
|
for (size_t client_id = 0; client_id < 4; client_id++) {
|
||||||
auto lc = l->clients[client_id];
|
auto lc = l->clients[client_id];
|
||||||
if (!lc) {
|
if (!lc) {
|
||||||
|
l->log.info_f("No client in slot {}", client_id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4038,14 +4050,19 @@ static asio::awaitable<void> on_enemy_exp_request_bb(shared_ptr<Client> c, Subco
|
|||||||
double rate_factor;
|
double rate_factor;
|
||||||
if (lc->character_file()->disp.stats.level >= 199) {
|
if (lc->character_file()->disp.stats.level >= 199) {
|
||||||
rate_factor = 0.0;
|
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)) {
|
} else if (ene_st->last_hit_by_client_id(client_id)) {
|
||||||
rate_factor = max<double>(1.0, exp_share_multiplier);
|
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)) {
|
} else if (ene_st->ever_hit_by_client_id(client_id)) {
|
||||||
rate_factor = max<double>(0.8, exp_share_multiplier);
|
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) {
|
} else if (lc->floor == ene_st->super_ene->floor) {
|
||||||
rate_factor = max<double>(0.0, exp_share_multiplier);
|
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 {
|
} else {
|
||||||
rate_factor = 0.0;
|
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) {
|
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->base_exp_multiplier *
|
||||||
l->challenge_exp_multiplier *
|
l->challenge_exp_multiplier *
|
||||||
(is_ep2 ? 1.3 : 1.0);
|
(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)) {
|
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));
|
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>();
|
const auto& cmd = msg.check_size_t<G_RequestItemDropFromQuest_BB_6xE0>();
|
||||||
auto s = c->require_server_state();
|
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++) {
|
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()) {
|
if (results.empty()) {
|
||||||
throw runtime_error("invalid result type");
|
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;
|
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,
|
std::shared_ptr<Client> c,
|
||||||
G_SpecializableItemDropRequest_6xA2& cmd,
|
G_SpecializableItemDropRequest_6xA2& cmd,
|
||||||
Episode episode,
|
Episode episode,
|
||||||
|
Difficulty difficulty,
|
||||||
uint8_t event,
|
uint8_t event,
|
||||||
std::shared_ptr<MapState> map,
|
std::shared_ptr<MapState> map,
|
||||||
bool mark_drop);
|
bool mark_drop);
|
||||||
@@ -50,7 +51,7 @@ public:
|
|||||||
StatusEffectState attack_status_effect;
|
StatusEffectState attack_status_effect;
|
||||||
StatusEffectState defense_status_effect;
|
StatusEffectState defense_status_effect;
|
||||||
StatusEffectState unused_status_effect;
|
StatusEffectState unused_status_effect;
|
||||||
uint32_t language = 0;
|
Language language = Language::JAPANESE;
|
||||||
uint32_t player_tag = 0;
|
uint32_t player_tag = 0;
|
||||||
uint32_t guild_card_number = 0;
|
uint32_t guild_card_number = 0;
|
||||||
uint32_t unknown_a6 = 0;
|
uint32_t unknown_a6 = 0;
|
||||||
@@ -79,7 +80,7 @@ public:
|
|||||||
Parsed6x70Data(
|
Parsed6x70Data(
|
||||||
const G_SyncPlayerDispAndInventory_DC112000_6x70& cmd,
|
const G_SyncPlayerDispAndInventory_DC112000_6x70& cmd,
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint8_t language,
|
Language language,
|
||||||
Version from_version,
|
Version from_version,
|
||||||
bool from_client_customization);
|
bool from_client_customization);
|
||||||
Parsed6x70Data(
|
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_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_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_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;
|
uint64_t default_xb_user_id() const;
|
||||||
void clear_v1_unused_item_fields();
|
void clear_v1_unused_item_fields();
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ ReplaySession::Client::Client(shared_ptr<asio::io_context> io_context, uint64_t
|
|||||||
: id(id),
|
: id(id),
|
||||||
port(port),
|
port(port),
|
||||||
version(version),
|
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 {
|
string ReplaySession::Client::str() const {
|
||||||
return std::format("Client[{}, T-{}, {}]", this->id, this->port, phosg::name_for_enum(this->version));
|
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<uint16_t, 4> corner_objects;
|
||||||
array<SymbolChatFacePart, 12> face_parts;
|
array<SymbolChatFacePart, 12> face_parts;
|
||||||
|
|
||||||
SaveFileSymbolChatEntryBB to_entry(uint8_t language) const {
|
SaveFileSymbolChatEntryBB to_entry(Language language) const {
|
||||||
SaveFileSymbolChatEntryBB ret;
|
SaveFileSymbolChatEntryBB ret;
|
||||||
ret.present = 1;
|
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;
|
ret.spec.spec = this->spec;
|
||||||
for (size_t z = 0; z < 4; z++) {
|
for (size_t z = 0; z < 4; z++) {
|
||||||
ret.spec.corner_objects[z] = this->corner_objects[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(
|
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const PlayerVisualConfig& visual,
|
const PlayerVisualConfig& visual,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
shared_ptr<const LevelTable> level_table) {
|
shared_ptr<const LevelTable> level_table) {
|
||||||
@@ -540,7 +540,7 @@ shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_config(
|
|||||||
|
|
||||||
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_preview(
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const PlayerDispDataBBPreview& preview,
|
const PlayerDispDataBBPreview& preview,
|
||||||
shared_ptr<const LevelTable> level_table) {
|
shared_ptr<const LevelTable> level_table) {
|
||||||
return PSOBBCharacterFile::create_from_config(
|
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) {
|
shared_ptr<PSOBBCharacterFile> PSOBBCharacterFile::create_from_file(const PSODCNTECharacterFile::Character& src) {
|
||||||
auto ret = PSOBBCharacterFile::create_from_config(
|
auto ret = PSOBBCharacterFile::create_from_config(
|
||||||
src.guild_card.guild_card_number,
|
src.guild_card.guild_card_number,
|
||||||
0,
|
Language::JAPANESE,
|
||||||
src.disp.visual,
|
src.disp.visual,
|
||||||
src.disp.visual.name.decode(0),
|
src.disp.visual.name.decode(Language::JAPANESE),
|
||||||
nullptr);
|
nullptr);
|
||||||
ret->inventory = src.inventory;
|
ret->inventory = src.inventory;
|
||||||
ret->inventory.decode_from_client(Version::DC_V1);
|
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->disp = src.disp.to_bb(language, language);
|
||||||
ret->validation_flags = 0;
|
ret->validation_flags = 0;
|
||||||
ret->creation_timestamp = src.creation_timestamp;
|
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.guild_card.guild_card_number,
|
||||||
src.inventory.language,
|
src.inventory.language,
|
||||||
src.disp.visual,
|
src.disp.visual,
|
||||||
src.disp.visual.name.decode(0),
|
src.disp.visual.name.decode(Language::JAPANESE),
|
||||||
nullptr);
|
nullptr);
|
||||||
ret->inventory = src.inventory;
|
ret->inventory = src.inventory;
|
||||||
ret->inventory.decode_from_client(Version::DC_V1);
|
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->disp = src.disp.to_bb(language, language);
|
||||||
ret->validation_flags = 0;
|
ret->validation_flags = 0;
|
||||||
ret->creation_timestamp = src.creation_timestamp;
|
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.guild_card.guild_card_number,
|
||||||
src.inventory.language,
|
src.inventory.language,
|
||||||
src.disp.visual,
|
src.disp.visual,
|
||||||
src.disp.visual.name.decode(0),
|
src.disp.visual.name.decode(Language::JAPANESE),
|
||||||
nullptr);
|
nullptr);
|
||||||
ret->inventory = src.inventory;
|
ret->inventory = src.inventory;
|
||||||
ret->inventory.decode_from_client(Version::DC_V1);
|
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->disp = src.disp.to_bb(language, language);
|
||||||
ret->validation_flags = src.validation_flags;
|
ret->validation_flags = src.validation_flags;
|
||||||
ret->creation_timestamp = src.creation_timestamp;
|
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.guild_card.guild_card_number,
|
||||||
src.inventory.language,
|
src.inventory.language,
|
||||||
src.disp.visual,
|
src.disp.visual,
|
||||||
src.disp.visual.name.decode(0),
|
src.disp.visual.name.decode(Language::JAPANESE),
|
||||||
nullptr);
|
nullptr);
|
||||||
ret->inventory = src.inventory;
|
ret->inventory = src.inventory;
|
||||||
ret->inventory.decode_from_client(Version::DC_V2);
|
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->disp = src.disp.to_bb(language, language);
|
||||||
ret->validation_flags = src.validation_flags;
|
ret->validation_flags = src.validation_flags;
|
||||||
ret->creation_timestamp = src.creation_timestamp;
|
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.guild_card.guild_card_number,
|
||||||
src.inventory.language,
|
src.inventory.language,
|
||||||
src.disp.visual,
|
src.disp.visual,
|
||||||
src.disp.visual.name.decode(0),
|
src.disp.visual.name.decode(Language::JAPANESE),
|
||||||
nullptr);
|
nullptr);
|
||||||
ret->inventory = src.inventory;
|
ret->inventory = src.inventory;
|
||||||
// Note: We intentionally do not call ret->inventory.decode_from_client here.
|
// 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
|
// 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
|
// 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.
|
// 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->disp = src.disp.to_bb(language, language);
|
||||||
ret->validation_flags = src.validation_flags;
|
ret->validation_flags = src.validation_flags;
|
||||||
ret->creation_timestamp = src.creation_timestamp;
|
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.guild_card.guild_card_number,
|
||||||
src.inventory.language,
|
src.inventory.language,
|
||||||
src.disp.visual,
|
src.disp.visual,
|
||||||
src.disp.visual.name.decode(0),
|
src.disp.visual.name.decode(Language::JAPANESE),
|
||||||
nullptr);
|
nullptr);
|
||||||
ret->inventory = src.inventory;
|
ret->inventory = src.inventory;
|
||||||
// Note: We intentionally do not call ret->inventory.decode_from_client here.
|
// 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
|
// 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
|
// 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.
|
// 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->disp = src.disp.to_bb(language, language);
|
||||||
ret->validation_flags = src.validation_flags;
|
ret->validation_flags = src.validation_flags;
|
||||||
ret->creation_timestamp = src.creation_timestamp;
|
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.guild_card.guild_card_number,
|
||||||
src.inventory.language,
|
src.inventory.language,
|
||||||
src.disp.visual,
|
src.disp.visual,
|
||||||
src.disp.visual.name.decode(0),
|
src.disp.visual.name.decode(Language::JAPANESE),
|
||||||
nullptr);
|
nullptr);
|
||||||
ret->inventory = src.inventory;
|
ret->inventory = src.inventory;
|
||||||
uint8_t language = ret->inventory.language;
|
Language language = ret->inventory.language;
|
||||||
ret->disp = src.disp.to_bb(language, language);
|
ret->disp = src.disp.to_bb(language, language);
|
||||||
ret->validation_flags = src.validation_flags;
|
ret->validation_flags = src.validation_flags;
|
||||||
ret->creation_timestamp = src.creation_timestamp;
|
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.guild_card.guild_card_number,
|
||||||
src.inventory.language,
|
src.inventory.language,
|
||||||
src.disp.visual,
|
src.disp.visual,
|
||||||
src.disp.visual.name.decode(0),
|
src.disp.visual.name.decode(Language::JAPANESE),
|
||||||
nullptr);
|
nullptr);
|
||||||
ret->inventory = src.inventory;
|
ret->inventory = src.inventory;
|
||||||
ret->inventory.decode_from_client(Version::XB_V3);
|
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->disp = src.disp.to_bb(language, language);
|
||||||
ret->validation_flags = src.validation_flags;
|
ret->validation_flags = src.validation_flags;
|
||||||
ret->creation_timestamp = src.creation_timestamp;
|
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 {
|
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;
|
PSODCNTECharacterFile::Character ret;
|
||||||
ret.inventory = this->inventory;
|
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 {
|
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;
|
PSODC112000CharacterFile::Character ret;
|
||||||
ret.inventory = this->inventory;
|
ret.inventory = this->inventory;
|
||||||
@@ -949,7 +949,7 @@ PSODC112000CharacterFile::Character PSOBBCharacterFile::as_11_2000(uint64_t hard
|
|||||||
}
|
}
|
||||||
|
|
||||||
PSOBBCharacterFile::operator PSODCV1CharacterFile::Character() const {
|
PSOBBCharacterFile::operator PSODCV1CharacterFile::Character() const {
|
||||||
uint8_t language = this->inventory.language;
|
Language language = this->inventory.language;
|
||||||
|
|
||||||
PSODCV1CharacterFile::Character ret;
|
PSODCV1CharacterFile::Character ret;
|
||||||
ret.inventory = this->inventory;
|
ret.inventory = this->inventory;
|
||||||
@@ -981,7 +981,7 @@ PSOBBCharacterFile::operator PSODCV1CharacterFile::Character() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PSOBBCharacterFile::operator PSODCV2CharacterFile::Character() const {
|
PSOBBCharacterFile::operator PSODCV2CharacterFile::Character() const {
|
||||||
uint8_t language = this->inventory.language;
|
Language language = this->inventory.language;
|
||||||
|
|
||||||
PSODCV2CharacterFile::Character ret;
|
PSODCV2CharacterFile::Character ret;
|
||||||
ret.inventory = this->inventory;
|
ret.inventory = this->inventory;
|
||||||
@@ -1022,7 +1022,7 @@ PSOBBCharacterFile::operator PSODCV2CharacterFile::Character() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PSOBBCharacterFile::operator PSOGCNTECharacterFileCharacter() const {
|
PSOBBCharacterFile::operator PSOGCNTECharacterFileCharacter() const {
|
||||||
uint8_t language = this->inventory.language;
|
Language language = this->inventory.language;
|
||||||
|
|
||||||
PSOGCNTECharacterFileCharacter ret;
|
PSOGCNTECharacterFileCharacter ret;
|
||||||
ret.inventory = this->inventory;
|
ret.inventory = this->inventory;
|
||||||
@@ -1060,7 +1060,7 @@ PSOBBCharacterFile::operator PSOGCNTECharacterFileCharacter() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PSOBBCharacterFile::operator PSOGCCharacterFile::Character() const {
|
PSOBBCharacterFile::operator PSOGCCharacterFile::Character() const {
|
||||||
uint8_t language = this->inventory.language;
|
Language language = this->inventory.language;
|
||||||
|
|
||||||
PSOGCCharacterFile::Character ret;
|
PSOGCCharacterFile::Character ret;
|
||||||
ret.inventory = this->inventory;
|
ret.inventory = this->inventory;
|
||||||
@@ -1108,7 +1108,7 @@ PSOBBCharacterFile::operator PSOGCCharacterFile::Character() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PSOBBCharacterFile::operator PSOXBCharacterFile::Character() const {
|
PSOBBCharacterFile::operator PSOXBCharacterFile::Character() const {
|
||||||
uint8_t language = this->inventory.language;
|
Language language = this->inventory.language;
|
||||||
|
|
||||||
PSOXBCharacterFile::Character ret;
|
PSOXBCharacterFile::Character ret;
|
||||||
ret.inventory = this->inventory;
|
ret.inventory = this->inventory;
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ struct SaveFileChatShortcutEntryT {
|
|||||||
/* 40:54:A4 */
|
/* 40:54:A4 */
|
||||||
|
|
||||||
template <bool RetBE, TextEncoding RetEncoding, size_t RetMaxSize>
|
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;
|
SaveFileChatShortcutEntryT<RetBE, RetEncoding, RetMaxSize> ret;
|
||||||
ret.type = this->type;
|
ret.type = this->type;
|
||||||
switch (ret.type) {
|
switch (ret.type) {
|
||||||
@@ -268,7 +268,7 @@ struct PSOPCSystemFile { // PSO______COM
|
|||||||
// assumption that Sega didn't change much between versions.
|
// assumption that Sega didn't change much between versions.
|
||||||
/* 0004 */ le_int16_t music_volume = 0;
|
/* 0004 */ le_int16_t music_volume = 0;
|
||||||
/* 0006 */ int8_t sound_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;
|
/* 0008 */ le_int32_t server_time_delta_frames = 1728000;
|
||||||
/* 000C */ parray<le_uint16_t, 0x10> unknown_a4; // Last one is always 0x1234?
|
/* 000C */ parray<le_uint16_t, 0x10> unknown_a4; // Last one is always 0x1234?
|
||||||
/* 002C */ parray<uint8_t, 0x100> event_flags;
|
/* 002C */ parray<uint8_t, 0x100> event_flags;
|
||||||
@@ -281,7 +281,7 @@ struct PSOGCSystemFile {
|
|||||||
/* 0000 */ be_uint32_t checksum = 0;
|
/* 0000 */ be_uint32_t checksum = 0;
|
||||||
/* 0004 */ be_int16_t music_volume = 0; // 0 = full volume; -250 = min volume
|
/* 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
|
/* 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
|
// This field stores the effective time zone offset between the server and
|
||||||
// client, in frames. The default value is 1728000, which corresponds to 16
|
// client, in frames. The default value is 1728000, which corresponds to 16
|
||||||
// hours. This is recomputed when the client receives a B1 command.
|
// hours. This is recomputed when the client receives a B1 command.
|
||||||
@@ -309,7 +309,7 @@ struct PSOXBSystemFile {
|
|||||||
/* 0000 */ le_uint32_t checksum = 0;
|
/* 0000 */ le_uint32_t checksum = 0;
|
||||||
/* 0004 */ le_int16_t music_volume = -50;
|
/* 0004 */ le_int16_t music_volume = -50;
|
||||||
/* 0006 */ int8_t sound_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 = 200;
|
/* 0008 */ be_int32_t server_time_delta_frames = 200;
|
||||||
/* 000C */ be_uint16_t udp_behavior = 0; // 0 = auto, 1 = on, 2 = off
|
/* 000C */ be_uint16_t udp_behavior = 0; // 0 = auto, 1 = on, 2 = off
|
||||||
/* 000E */ be_uint16_t surround_sound_enabled = 0;
|
/* 000E */ be_uint16_t surround_sound_enabled = 0;
|
||||||
@@ -338,7 +338,7 @@ struct PSOBBMinimalSystemFile {
|
|||||||
/* 0000 */ be_uint32_t checksum = 0;
|
/* 0000 */ be_uint32_t checksum = 0;
|
||||||
/* 0004 */ be_int16_t music_volume = 0;
|
/* 0004 */ be_int16_t music_volume = 0;
|
||||||
/* 0006 */ int8_t sound_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;
|
/* 0008 */ be_int32_t server_time_delta_frames = 1728000;
|
||||||
/* 000C */ be_uint16_t udp_behavior = 0; // 0 = auto, 1 = on, 2 = off
|
/* 000C */ be_uint16_t udp_behavior = 0; // 0 = auto, 1 = on, 2 = off
|
||||||
/* 000E */ be_uint16_t surround_sound_enabled = 0;
|
/* 000E */ be_uint16_t surround_sound_enabled = 0;
|
||||||
@@ -871,13 +871,13 @@ struct PSOBBCharacterFile {
|
|||||||
|
|
||||||
static std::shared_ptr<PSOBBCharacterFile> create_from_config(
|
static std::shared_ptr<PSOBBCharacterFile> create_from_config(
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const PlayerVisualConfig& visual,
|
const PlayerVisualConfig& visual,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
std::shared_ptr<const LevelTable> level_table);
|
std::shared_ptr<const LevelTable> level_table);
|
||||||
static std::shared_ptr<PSOBBCharacterFile> create_from_preview(
|
static std::shared_ptr<PSOBBCharacterFile> create_from_preview(
|
||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
uint8_t language,
|
Language language,
|
||||||
const PlayerDispDataBBPreview& preview,
|
const PlayerDispDataBBPreview& preview,
|
||||||
std::shared_ptr<const LevelTable> level_table);
|
std::shared_ptr<const LevelTable> level_table);
|
||||||
static std::shared_ptr<PSOBBCharacterFile> create_from_file(const PSODCNTECharacterFile::Character& src);
|
static std::shared_ptr<PSOBBCharacterFile> create_from_file(const PSODCNTECharacterFile::Character& src);
|
||||||
@@ -978,7 +978,7 @@ struct PSODCV1V2GuildCardFile {
|
|||||||
/* 0004 */ parray<PSODCGuildCardFileEntry, 100> entries;
|
/* 0004 */ parray<PSODCGuildCardFileEntry, 100> entries;
|
||||||
/* 3204 */ le_int16_t music_volume = 0;
|
/* 3204 */ le_int16_t music_volume = 0;
|
||||||
/* 3206 */ int8_t sound_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
|
/* 3208 */ le_int32_t server_time_delta_frames = 540000; // 648000 on DCv1
|
||||||
/* 320C */ le_uint32_t creation_timestamp = 0;
|
/* 320C */ le_uint32_t creation_timestamp = 0;
|
||||||
/* 3210 */ le_uint32_t round2_seed = 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) {
|
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);
|
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 sys = c->system_file(true);
|
||||||
auto team = c->team();
|
auto team = c->team();
|
||||||
if (c->check_flag(Client::Flag::FORCE_ENGLISH_LANGUAGE_BB)) {
|
if (c->check_flag(Client::Flag::FORCE_ENGLISH_LANGUAGE_BB)) {
|
||||||
p->inventory.language = 1;
|
p->inventory.language = Language::ENGLISH;
|
||||||
p->guild_card.language = 1;
|
p->guild_card.language = Language::ENGLISH;
|
||||||
sys->language = 1;
|
sys->language = Language::ENGLISH;
|
||||||
}
|
}
|
||||||
|
|
||||||
SC_SyncSaveFiles_BB_E7 cmd;
|
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(
|
string prepare_chat_data(
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
uint8_t from_client_id,
|
uint8_t from_client_id,
|
||||||
const string& from_name,
|
const string& from_name,
|
||||||
const string& text,
|
const string& text,
|
||||||
@@ -1024,7 +1024,7 @@ string prepare_chat_data(
|
|||||||
string data;
|
string data;
|
||||||
|
|
||||||
if (version == Version::BB_V4) {
|
if (version == Version::BB_V4) {
|
||||||
data.append(language ? "\tE" : "\tJ");
|
data.append((language == Language::JAPANESE) ? "\tJ" : "\tE");
|
||||||
}
|
}
|
||||||
data.append(from_name);
|
data.append(from_name);
|
||||||
if (version == Version::DC_NTE) {
|
if (version == Version::DC_NTE) {
|
||||||
@@ -1037,7 +1037,7 @@ string prepare_chat_data(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (uses_utf16(version)) {
|
if (uses_utf16(version)) {
|
||||||
data.append(language ? "\tE" : "\tJ");
|
data.append((language == Language::JAPANESE) ? "\tJ" : "\tE");
|
||||||
data.append(text);
|
data.append(text);
|
||||||
return tt_utf8_to_utf16(data);
|
return tt_utf8_to_utf16(data);
|
||||||
} else if (version == Version::DC_NTE) {
|
} else if (version == Version::DC_NTE) {
|
||||||
@@ -1291,7 +1291,7 @@ void send_guild_card_dc_pc_gc_t(
|
|||||||
uint32_t guild_card_number,
|
uint32_t guild_card_number,
|
||||||
const string& name,
|
const string& name,
|
||||||
const string& description,
|
const string& description,
|
||||||
uint8_t language,
|
Language language,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
uint8_t char_class) {
|
uint8_t char_class) {
|
||||||
CmdT cmd;
|
CmdT cmd;
|
||||||
@@ -1315,7 +1315,7 @@ void send_guild_card_xb(
|
|||||||
uint64_t xb_user_id,
|
uint64_t xb_user_id,
|
||||||
const string& name,
|
const string& name,
|
||||||
const string& description,
|
const string& description,
|
||||||
uint8_t language,
|
Language language,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
uint8_t char_class) {
|
uint8_t char_class) {
|
||||||
G_SendGuildCard_XB_6x06 cmd;
|
G_SendGuildCard_XB_6x06 cmd;
|
||||||
@@ -1341,7 +1341,7 @@ static void send_guild_card_bb(
|
|||||||
const string& name,
|
const string& name,
|
||||||
const string& team_name,
|
const string& team_name,
|
||||||
const string& description,
|
const string& description,
|
||||||
uint8_t language,
|
Language language,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
uint8_t char_class) {
|
uint8_t char_class) {
|
||||||
G_SendGuildCard_BB_6x06 cmd;
|
G_SendGuildCard_BB_6x06 cmd;
|
||||||
@@ -1366,7 +1366,7 @@ void send_guild_card(
|
|||||||
const string& name,
|
const string& name,
|
||||||
const string& team_name,
|
const string& team_name,
|
||||||
const string& description,
|
const string& description,
|
||||||
uint8_t language,
|
Language language,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
uint8_t char_class) {
|
uint8_t char_class) {
|
||||||
switch (ch->version) {
|
switch (ch->version) {
|
||||||
@@ -1573,7 +1573,7 @@ void send_game_menu_t(
|
|||||||
auto& e = entries.emplace_back();
|
auto& e = entries.emplace_back();
|
||||||
e.menu_id = MenuID::GAME;
|
e.menu_id = MenuID::GAME;
|
||||||
e.item_id = l->lobby_id;
|
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();
|
e.num_players = l->count_clients();
|
||||||
if (is_dc(c->version())) {
|
if (is_dc(c->version())) {
|
||||||
e.episode = l->version_is_allowed(Version::DC_V1) ? 1 : 0;
|
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
|
if (l->quest_flags_known) { // Not all flags known; send multiple 6x75s
|
||||||
phosg::StringWriter w;
|
phosg::StringWriter w;
|
||||||
bool use_v3_cmd = !is_v1_or_v2(c->version()) || (c->version() == Version::GC_NTE);
|
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) {
|
if ((difficulty != l->difficulty) && !use_v3_cmd) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto& diff_flags = l->quest_flag_values->data.at(difficulty);
|
const auto& diff_flags = l->quest_flag_values->for_difficulty(difficulty);
|
||||||
const auto& diff_known_flags = l->quest_flags_known->data.at(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++) {
|
for (uint8_t z = 0; z < diff_known_flags.data.size(); z++) {
|
||||||
uint8_t known_flags = diff_known_flags.data[z];
|
uint8_t known_flags = diff_known_flags.data[z];
|
||||||
if (!known_flags) {
|
if (!known_flags) {
|
||||||
@@ -2969,7 +2969,7 @@ void send_game_flag_state_t(shared_ptr<Client> c) {
|
|||||||
uint16_t flag_num = ((z << 3) | sh);
|
uint16_t flag_num = ((z << 3) | sh);
|
||||||
if (use_v3_cmd) {
|
if (use_v3_cmd) {
|
||||||
w.put(G_UpdateQuestFlag_V3_BB_6x75{
|
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 {
|
} else {
|
||||||
w.put(G_UpdateQuestFlag_DC_PC_6x75{
|
w.put(G_UpdateQuestFlag_DC_PC_6x75{
|
||||||
{0x75, 0x02, 0x0000}, flag_num, (((flag_values << sh) & 0x80) ? 0 : 1)});
|
{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{} {}",
|
"{} CLv{} {}",
|
||||||
name_for_char_class(p->disp.visual.char_class),
|
name_for_char_class(p->disp.visual.char_class),
|
||||||
p->disp.stats.level + 1,
|
p->disp.stats.level + 1,
|
||||||
char_for_language_code(p->inventory.language));
|
char_for_language(p->inventory.language));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename RulesT>
|
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.num_players_per_team = match->preceding_a->winner_team->max_players;
|
||||||
cmd.winner_team_id = (match->preceding_b->winner_team == match->winner_team);
|
cmd.winner_team_id = (match->preceding_b->winner_team == match->winner_team);
|
||||||
cmd.meseta_amount = meseta_reward;
|
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) &&
|
if ((lc->version() != Version::GC_EP3_NTE) &&
|
||||||
!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
||||||
uint8_t mask_key = (phosg::random_object<uint32_t>() % 0xFF) + 1;
|
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.total_spectators = total_spectators;
|
||||||
cmd.text_size = text.size();
|
cmd.text_size = text.size();
|
||||||
cmd.text.encode(text, 1);
|
cmd.text.encode(text, Language::ENGLISH);
|
||||||
for (auto c : watcher_l->clients) {
|
for (auto c : watcher_l->clients) {
|
||||||
if (c) {
|
if (c) {
|
||||||
if ((c->version() == Version::GC_EP3) && !(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
|
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(
|
std::string prepare_chat_data(
|
||||||
Version version,
|
Version version,
|
||||||
uint8_t language,
|
Language language,
|
||||||
uint8_t from_client_id,
|
uint8_t from_client_id,
|
||||||
const std::string& from_name,
|
const std::string& from_name,
|
||||||
const std::string& text,
|
const std::string& text,
|
||||||
@@ -298,7 +298,7 @@ void send_guild_card(
|
|||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::string& team_name,
|
const std::string& team_name,
|
||||||
const std::string& description,
|
const std::string& description,
|
||||||
uint8_t language,
|
Language language,
|
||||||
uint8_t section_id,
|
uint8_t section_id,
|
||||||
uint8_t char_class);
|
uint8_t char_class);
|
||||||
void send_guild_card(std::shared_ptr<Client> c, std::shared_ptr<Client> source);
|
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;
|
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)
|
const auto& min_levels = is_v4(version)
|
||||||
? this->min_levels_v4
|
? this->min_levels_v4
|
||||||
: is_v3(version)
|
: is_v3(version)
|
||||||
@@ -402,21 +402,21 @@ size_t ServerState::default_min_level_for_game(Version version, Episode episode,
|
|||||||
: this->min_levels_v1_v2;
|
: this->min_levels_v1_v2;
|
||||||
switch (episode) {
|
switch (episode) {
|
||||||
case Episode::EP1:
|
case Episode::EP1:
|
||||||
return min_levels[0].at(difficulty);
|
return min_levels[0].at(static_cast<size_t>(difficulty));
|
||||||
case Episode::EP2:
|
case Episode::EP2:
|
||||||
return min_levels[1].at(difficulty);
|
return min_levels[1].at(static_cast<size_t>(difficulty));
|
||||||
case Episode::EP3:
|
case Episode::EP3:
|
||||||
return 0;
|
return 0;
|
||||||
case Episode::EP4:
|
case Episode::EP4:
|
||||||
return min_levels[2].at(difficulty);
|
return min_levels[2].at(static_cast<size_t>(difficulty));
|
||||||
default:
|
default:
|
||||||
throw runtime_error("invalid episode");
|
throw runtime_error("invalid episode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<const SetDataTableBase> ServerState::set_data_table(
|
shared_ptr<const SetDataTableBase> ServerState::set_data_table(
|
||||||
Version version, Episode episode, GameMode mode, uint8_t difficulty) const {
|
Version version, Episode episode, GameMode mode, Difficulty difficulty) const {
|
||||||
bool use_ult_tables = ((episode == Episode::EP1) && (difficulty == 3) && !is_v1(version) && (version != Version::PC_NTE));
|
bool use_ult_tables = ((episode == Episode::EP1) && (difficulty == Difficulty::ULTIMATE) && !is_v1(version) && (version != Version::PC_NTE));
|
||||||
if (mode == GameMode::SOLO && is_v4(version)) {
|
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;
|
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&) {
|
} 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;
|
shared_ptr<const MapState::RareEnemyRates> prev = MapState::DEFAULT_RARE_ENEMIES;
|
||||||
try {
|
try {
|
||||||
string key = "RareEnemyRates-";
|
string key = "RareEnemyRates-";
|
||||||
key += token_name_for_difficulty(z);
|
key += token_name_for_difficulty(difficulty);
|
||||||
this->rare_enemy_rates_by_difficulty[z] = make_shared<MapState::RareEnemyRates>(this->config_json->at(key));
|
this->rare_enemy_rates_by_difficulty[diff_index] = make_shared<MapState::RareEnemyRates>(this->config_json->at(key));
|
||||||
prev = this->rare_enemy_rates_by_difficulty[z];
|
prev = this->rare_enemy_rates_by_difficulty[diff_index];
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
this->rare_enemy_rates_by_difficulty[z] = prev;
|
this->rare_enemy_rates_by_difficulty[diff_index] = prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
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 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");
|
auto enemies_data = this->load_map_file(Version::GC_EP3, "map_city_on_battle_e.dat");
|
||||||
if (objects_data || enemies_data) {
|
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);
|
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_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;
|
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");
|
config_log.info_f("Loading free play map files");
|
||||||
for (Version v : ALL_ARPG_SEMANTIC_VERSIONS) {
|
for (Version v : ALL_ARPG_SEMANTIC_VERSIONS) {
|
||||||
const array<Episode, 3> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
|
for (Episode episode : ALL_EPISODES_V4) {
|
||||||
for (Episode episode : episodes) {
|
|
||||||
if ((episode == Episode::EP2 && is_v1_or_v2(v) && (v != Version::GC_NTE)) ||
|
if ((episode == Episode::EP2 && is_v1_or_v2(v) && (v != Version::GC_NTE)) ||
|
||||||
(episode == Episode::EP4 && !is_v4(v))) {
|
(episode == Episode::EP4 && !is_v4(v))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const array<GameMode, 4> modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO};
|
for (GameMode mode : ALL_GAME_MODES_V4) {
|
||||||
for (GameMode mode : modes) {
|
|
||||||
if ((mode == GameMode::BATTLE) && is_pre_v1(v)) {
|
if ((mode == GameMode::BATTLE) && is_pre_v1(v)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1629,8 +1628,8 @@ void ServerState::load_maps() {
|
|||||||
if ((mode == GameMode::SOLO && !is_v4(v))) {
|
if ((mode == GameMode::SOLO && !is_v4(v))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (uint8_t difficulty = 0; difficulty < 4; difficulty++) {
|
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
||||||
if ((difficulty == 3) && is_v1(v)) {
|
if ((difficulty == Difficulty::ULTIMATE) && is_v1(v)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto sdt = this->set_data_table(v, episode, mode, difficulty);
|
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(
|
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);
|
uint32_t free_play_key = this->free_play_key(episode, mode, difficulty, floor, layout, entities);
|
||||||
try {
|
try {
|
||||||
return this->supermap_for_free_play_key.at(free_play_key);
|
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(
|
vector<shared_ptr<const SuperMap>> ServerState::supermaps_for_variations(
|
||||||
Episode episode,
|
Episode episode, GameMode mode, Difficulty difficulty, const Variations& variations) {
|
||||||
GameMode mode,
|
|
||||||
uint8_t difficulty,
|
|
||||||
const Variations& variations) {
|
|
||||||
vector<shared_ptr<const SuperMap>> ret;
|
vector<shared_ptr<const SuperMap>> ret;
|
||||||
for (size_t floor = 0; floor < 0x12; floor++) {
|
for (size_t floor = 0; floor < 0x12; floor++) {
|
||||||
Variations::Entry e;
|
Variations::Entry e;
|
||||||
@@ -1881,7 +1877,7 @@ void ServerState::load_word_select_table() {
|
|||||||
unique_ptr<UnicodeTextSet> pc_unitxt_data;
|
unique_ptr<UnicodeTextSet> pc_unitxt_data;
|
||||||
if (this->text_index) {
|
if (this->text_index) {
|
||||||
config_log.debug_f("(Word select) Using PC_V2 unitxt_e.prs from 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 {
|
} else {
|
||||||
config_log.debug_f("(Word select) Loading PC_V2 unitxt_e.prs");
|
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"));
|
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 {
|
shared_ptr<const TextIndex> text_index) const {
|
||||||
switch (limits->version) {
|
switch (limits->version) {
|
||||||
case Version::DC_NTE:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
default:
|
||||||
return nullptr;
|
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::unordered_map<std::string, std::shared_ptr<const RareItemSet>> rare_item_sets;
|
||||||
std::shared_ptr<const ArmorRandomSet> armor_random_set;
|
std::shared_ptr<const ArmorRandomSet> armor_random_set;
|
||||||
std::shared_ptr<const ToolRandomSet> tool_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::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set;
|
||||||
std::array<std::shared_ptr<const ItemParameterTable>, NUM_VERSIONS> item_parameter_tables;
|
std::array<std::shared_ptr<const ItemParameterTable>, NUM_VERSIONS> item_parameter_tables;
|
||||||
std::shared_ptr<const ItemTranslationTable> item_translation_table;
|
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;
|
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;
|
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 LevelTable> level_table(Version version) const;
|
||||||
std::shared_ptr<const ItemParameterTable> item_parameter_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;
|
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);
|
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;
|
std::vector<PortConfiguration> parse_port_configuration(const phosg::JSON& json) const;
|
||||||
|
|
||||||
static constexpr uint32_t free_play_key(
|
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) |
|
return (static_cast<uint32_t>(episode) << 28) |
|
||||||
(static_cast<uint32_t>(mode) << 26) |
|
(static_cast<uint32_t>(mode) << 26) |
|
||||||
(static_cast<uint32_t>(difficulty) << 24) |
|
(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);
|
(static_cast<uint32_t>(entities) << 0);
|
||||||
}
|
}
|
||||||
std::shared_ptr<const SuperMap> get_free_play_supermap(
|
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(
|
std::vector<std::shared_ptr<const SuperMap>> supermaps_for_variations(
|
||||||
Episode episode,
|
Episode episode,
|
||||||
GameMode mode,
|
GameMode mode,
|
||||||
uint8_t difficulty,
|
Difficulty difficulty,
|
||||||
const Variations& variations);
|
const Variations& variations);
|
||||||
|
|
||||||
void create_default_lobbies();
|
void create_default_lobbies();
|
||||||
|
|||||||
@@ -940,7 +940,7 @@ ShellCommand c_show_slots(
|
|||||||
if (player.guild_card_number) {
|
if (player.guild_card_number) {
|
||||||
ret.emplace_back(format(" {}: {} => {} ({}, {}, {})",
|
ret.emplace_back(format(" {}: {} => {} ({}, {}, {})",
|
||||||
z, player.guild_card_number, player.name,
|
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_char_class(player.char_class),
|
||||||
name_for_section_id(player.section_id)));
|
name_for_section_id(player.section_id)));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+30
-19
@@ -476,36 +476,36 @@ bool char_class_is_force(uint8_t cls) {
|
|||||||
return class_flags.at(cls) & ClassFlag::FORCE;
|
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 = {
|
static const array<const char*, 4> names = {
|
||||||
"Normal", "Hard", "Very Hard", "Ultimate"};
|
"Normal", "Hard", "Very Hard", "Ultimate"};
|
||||||
try {
|
try {
|
||||||
return names.at(difficulty);
|
return names.at(static_cast<size_t>(difficulty));
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
return "Unknown";
|
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 = {
|
static const array<const char*, 4> names = {
|
||||||
"Normal", "Hard", "VeryHard", "Ultimate"};
|
"Normal", "Hard", "VeryHard", "Ultimate"};
|
||||||
try {
|
try {
|
||||||
return names.at(difficulty);
|
return names.at(static_cast<size_t>(difficulty));
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
return "Unknown";
|
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'};
|
static const array<char, 4> names = {'N', 'H', 'V', 'U'};
|
||||||
try {
|
try {
|
||||||
return names.at(difficulty);
|
return names.at(static_cast<size_t>(difficulty));
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* name_for_language_code(uint8_t language_code) {
|
const char* name_for_language(Language language) {
|
||||||
array<const char*, 8> names = {{"Japanese",
|
array<const char*, 8> names = {{"Japanese",
|
||||||
"English",
|
"English",
|
||||||
"German",
|
"German",
|
||||||
@@ -514,39 +514,41 @@ const char* name_for_language_code(uint8_t language_code) {
|
|||||||
"Simplified Chinese",
|
"Simplified Chinese",
|
||||||
"Traditional Chinese",
|
"Traditional Chinese",
|
||||||
"Korean"}};
|
"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) {
|
char char_for_language(Language language) {
|
||||||
return (language_code < 8) ? "JEGFSBTK"[language_code] : '?';
|
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) {
|
switch (language_char) {
|
||||||
case 'J':
|
case 'J':
|
||||||
case 'j':
|
case 'j':
|
||||||
return 0;
|
return Language::JAPANESE;
|
||||||
case 'E':
|
case 'E':
|
||||||
case 'e':
|
case 'e':
|
||||||
return 1;
|
return Language::ENGLISH;
|
||||||
case 'G':
|
case 'G':
|
||||||
case 'g':
|
case 'g':
|
||||||
return 2;
|
return Language::GERMAN;
|
||||||
case 'F':
|
case 'F':
|
||||||
case 'f':
|
case 'f':
|
||||||
return 3;
|
return Language::FRENCH;
|
||||||
case 'S':
|
case 'S':
|
||||||
case 's':
|
case 's':
|
||||||
return 4;
|
return Language::SPANISH;
|
||||||
case 'B':
|
case 'B':
|
||||||
case 'b':
|
case 'b':
|
||||||
return 5;
|
return Language::SIMPLIFIED_CHINESE;
|
||||||
case 'T':
|
case 'T':
|
||||||
case 't':
|
case 't':
|
||||||
return 6;
|
return Language::TRADITIONAL_CHINESE;
|
||||||
case 'K':
|
case 'K':
|
||||||
case 'k':
|
case 'k':
|
||||||
return 7;
|
return Language::KOREAN;
|
||||||
default:
|
default:
|
||||||
throw runtime_error("unknown language");
|
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_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_EP2({0, 29, 49, 89});
|
||||||
const array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP4({0, 39, 79, 109});
|
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,
|
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);
|
bool episode_has_arpg_semantics(Episode ep);
|
||||||
const char* name_for_episode(Episode ep);
|
const char* name_for_episode(Episode ep);
|
||||||
const char* token_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_ranger(uint8_t cls);
|
||||||
bool char_class_is_force(uint8_t cls);
|
bool char_class_is_force(uint8_t cls);
|
||||||
|
|
||||||
const char* name_for_difficulty(uint8_t difficulty);
|
const char* name_for_difficulty(Difficulty difficulty);
|
||||||
const char* token_name_for_difficulty(uint8_t difficulty);
|
const char* token_name_for_difficulty(Difficulty difficulty);
|
||||||
char abbreviation_for_difficulty(uint8_t difficulty);
|
char abbreviation_for_difficulty(Difficulty difficulty);
|
||||||
|
|
||||||
const char* name_for_language_code(uint8_t language_code);
|
const char* name_for_language(Language language);
|
||||||
char char_for_language_code(uint8_t language_code);
|
char char_for_language(Language language);
|
||||||
uint8_t language_code_for_char(char language_char);
|
Language language_for_char(char language_char);
|
||||||
|
|
||||||
extern const std::vector<const char*> name_for_mag_color;
|
extern const std::vector<const char*> name_for_mag_color;
|
||||||
extern const std::unordered_map<std::string, uint8_t> mag_color_for_name;
|
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_EP1;
|
||||||
extern const std::array<size_t, 4> DEFAULT_MIN_LEVELS_V4_EP2;
|
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<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_ascii_to_utf8("UTF-8", "ASCII");
|
||||||
TextTranscoder tt_utf8_to_ascii("ASCII", "UTF-8");
|
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) {
|
if (is_utf16) {
|
||||||
return tt_utf8_to_utf16(utf8);
|
return tt_utf8_to_utf16(utf8);
|
||||||
} else {
|
} else {
|
||||||
if (default_language) {
|
if (default_language == Language::JAPANESE) {
|
||||||
try {
|
|
||||||
return tt_utf8_to_8859(utf8);
|
|
||||||
} catch (const exception& e) {
|
|
||||||
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
try {
|
||||||
return tt_utf8_to_sega_sjis(utf8);
|
return tt_utf8_to_sega_sjis(utf8);
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
return "\tE" + tt_utf8_to_8859(utf8);
|
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) {
|
if (is_utf16) {
|
||||||
string to_encode = "\t";
|
string to_encode = "\t";
|
||||||
to_encode += marker_for_language_code(default_language);
|
to_encode += marker_for_language(default_language);
|
||||||
to_encode += utf8;
|
to_encode += utf8;
|
||||||
return tt_utf8_to_utf16(to_encode);
|
return tt_utf8_to_utf16(to_encode);
|
||||||
} else {
|
} else {
|
||||||
if (default_language) {
|
if (default_language == Language::JAPANESE) {
|
||||||
try {
|
try {
|
||||||
return "\tE" + tt_utf8_to_8859(utf8);
|
|
||||||
} catch (const exception& e) {
|
|
||||||
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
|
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
|
||||||
|
} catch (const exception& e) {
|
||||||
|
return "\tE" + tt_utf8_to_8859(utf8);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return "\tJ" + tt_utf8_to_sega_sjis(utf8);
|
|
||||||
} catch (const exception& e) {
|
|
||||||
return "\tE" + tt_utf8_to_8859(utf8);
|
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) {
|
if (is_utf16) {
|
||||||
string ret = tt_utf16_to_utf8(data);
|
string ret = tt_utf16_to_utf8(data);
|
||||||
if (ret.size() >= 2 && ret[0] == '\t' && is_language_marker_utf16(ret[1])) {
|
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 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) {
|
char marker_for_language(Language language) {
|
||||||
switch (language_code) {
|
switch (language) {
|
||||||
case 0:
|
case Language::JAPANESE:
|
||||||
return 'J';
|
return 'J';
|
||||||
case 1:
|
case Language::ENGLISH:
|
||||||
case 2:
|
case Language::GERMAN:
|
||||||
case 3:
|
case Language::FRENCH:
|
||||||
case 4:
|
case Language::SPANISH:
|
||||||
return 'E';
|
return 'E';
|
||||||
case 5:
|
case Language::SIMPLIFIED_CHINESE:
|
||||||
return 'B';
|
return 'B';
|
||||||
case 6:
|
case Language::TRADITIONAL_CHINESE:
|
||||||
return 'T';
|
return 'T';
|
||||||
case 7:
|
case Language::KOREAN:
|
||||||
return 'K';
|
return 'K';
|
||||||
default:
|
default:
|
||||||
return 'E';
|
return 'E';
|
||||||
|
|||||||
+16
-15
@@ -11,6 +11,7 @@
|
|||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include "StaticGameData.hh"
|
||||||
#include "Types.hh"
|
#include "Types.hh"
|
||||||
|
|
||||||
#define check_struct_size(StructT, Size) \
|
#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_ascii_to_utf8;
|
||||||
extern TextTranscoder tt_utf8_to_ascii;
|
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_optional(const std::string& utf8, Language default_language, bool is_utf16);
|
||||||
std::string tt_encode_marked(const std::string& utf8, uint8_t 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, uint8_t 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_sjis_8859(char marker);
|
||||||
bool is_language_marker_utf16(char marker);
|
bool is_language_marker_utf16(char marker);
|
||||||
|
|
||||||
@@ -467,7 +468,7 @@ struct pstring {
|
|||||||
pstring(const pstring<Encoding, Chars, BytesPerChar>& other) {
|
pstring(const pstring<Encoding, Chars, BytesPerChar>& other) {
|
||||||
memcpy(this->data, other.data, Bytes);
|
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);
|
this->encode(s, language);
|
||||||
}
|
}
|
||||||
pstring(pstring<Encoding, Chars, BytesPerChar>&& other) = delete;
|
pstring(pstring<Encoding, Chars, BytesPerChar>&& other) = delete;
|
||||||
@@ -485,7 +486,7 @@ struct pstring {
|
|||||||
}
|
}
|
||||||
pstring<Encoding, Chars, BytesPerChar>& operator=(pstring<Encoding, Chars, BytesPerChar>&& s) = delete;
|
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 {
|
try {
|
||||||
switch (Encoding) {
|
switch (Encoding) {
|
||||||
case TextEncoding::CHALLENGE8:
|
case TextEncoding::CHALLENGE8:
|
||||||
@@ -513,7 +514,7 @@ struct pstring {
|
|||||||
break;
|
break;
|
||||||
} else if (s.size() <= 2 || s[0] != '\t' || s[1] == 'C') {
|
} else if (s.size() <= 2 || s[0] != '\t' || s[1] == 'C') {
|
||||||
std::string to_encode = "\t";
|
std::string to_encode = "\t";
|
||||||
to_encode += marker_for_language_code(client_language);
|
to_encode += marker_for_language(client_language);
|
||||||
to_encode += s;
|
to_encode += s;
|
||||||
auto ret = tt_utf8_to_utf16(this->data, Bytes, to_encode.data(), to_encode.size(), true);
|
auto ret = tt_utf8_to_utf16(this->data, Bytes, to_encode.data(), to_encode.size(), true);
|
||||||
this->clear_after_bytes(ret.bytes_written);
|
this->clear_after_bytes(ret.bytes_written);
|
||||||
@@ -536,7 +537,7 @@ struct pstring {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TextEncoding::MARKED: {
|
case TextEncoding::MARKED: {
|
||||||
if (client_language == 0) {
|
if (client_language == Language::JAPANESE) {
|
||||||
try {
|
try {
|
||||||
auto ret = tt_utf8_to_sega_sjis(this->data, Bytes, s.data(), s.size(), true);
|
auto ret = tt_utf8_to_sega_sjis(this->data, Bytes, s.data(), s.size(), true);
|
||||||
this->clear_after_bytes(ret.bytes_written);
|
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 {
|
try {
|
||||||
switch (Encoding) {
|
switch (Encoding) {
|
||||||
case TextEncoding::CHALLENGE8: {
|
case TextEncoding::CHALLENGE8: {
|
||||||
@@ -623,16 +624,16 @@ struct pstring {
|
|||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
if (this->data[0] == '\t') {
|
if (this->data[0] == '\t') {
|
||||||
if (this->data[1] == 'J') {
|
if (this->data[1] == 'J') {
|
||||||
client_language = 0;
|
client_language = Language::JAPANESE;
|
||||||
offset = 2;
|
offset = 2;
|
||||||
} else if (this->data[1] != 'C') {
|
} else if (this->data[1] != 'C') {
|
||||||
client_language = 1;
|
client_language = Language::ENGLISH;
|
||||||
offset = 2;
|
offset = 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return client_language
|
return (client_language == Language::JAPANESE)
|
||||||
? 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)
|
||||||
: 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:
|
default:
|
||||||
throw std::logic_error("unknown text encoding");
|
throw std::logic_error("unknown text encoding");
|
||||||
@@ -650,7 +651,7 @@ struct pstring {
|
|||||||
return (memcmp(this->data, other.data, Bytes) != 0);
|
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;
|
return this->decode(language) == other;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-40
@@ -454,54 +454,54 @@ TextIndex::TextIndex(
|
|||||||
: log("[TextIndex] ", static_game_data_log.min_level) {
|
: log("[TextIndex] ", static_game_data_log.min_level) {
|
||||||
if (!directory.empty()) {
|
if (!directory.empty()) {
|
||||||
auto add_version = [&](Version version, const string& subdirectory, function<shared_ptr<TextSet>(const string&, bool)> make_set) -> void {
|
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({
|
static const map<string, Language> bintext_filenames({
|
||||||
{"TextJapanese.pr2", 0x00},
|
{"TextJapanese.pr2", Language::JAPANESE},
|
||||||
{"TextEnglish.pr2", 0x01},
|
{"TextEnglish.pr2", Language::ENGLISH},
|
||||||
{"TextGerman.pr2", 0x02},
|
{"TextGerman.pr2", Language::GERMAN},
|
||||||
{"TextFrench.pr2", 0x03},
|
{"TextFrench.pr2", Language::FRENCH},
|
||||||
{"TextSpanish.pr2", 0x04},
|
{"TextSpanish.pr2", Language::SPANISH},
|
||||||
});
|
});
|
||||||
static const map<string, uint8_t> unitext_filenames({
|
static const map<string, Language> unitext_filenames({
|
||||||
{"unitxt_j.prs", 0x00}, // PC/BB Japanese
|
{"unitxt_j.prs", Language::JAPANESE}, // PC/BB Japanese
|
||||||
{"unitxt_e.prs", 0x01}, // PC/BB English
|
{"unitxt_e.prs", Language::ENGLISH}, // PC/BB English
|
||||||
{"unitxt_g.prs", 0x02}, // PC/BB German
|
{"unitxt_g.prs", Language::GERMAN}, // PC/BB German
|
||||||
{"unitxt_f.prs", 0x03}, // PC/BB French
|
{"unitxt_f.prs", Language::FRENCH}, // PC/BB French
|
||||||
{"unitxt_s.prs", 0x04}, // PC/BB Spanish
|
{"unitxt_s.prs", Language::SPANISH}, // PC/BB Spanish
|
||||||
{"unitxt_b.prs", 0x05}, // PC Simplified Chinese
|
{"unitxt_b.prs", Language::SIMPLIFIED_CHINESE}, // PC Simplified Chinese
|
||||||
{"unitxt_cs.prs", 0x05}, // BB Simplified Chinese
|
{"unitxt_cs.prs", Language::SIMPLIFIED_CHINESE}, // BB Simplified Chinese
|
||||||
{"unitxt_t.prs", 0x06}, // PC Traditional Chinese
|
{"unitxt_t.prs", Language::TRADITIONAL_CHINESE}, // PC Traditional Chinese
|
||||||
{"unitxt_ct.prs", 0x06}, // BB Traditional Chinese
|
{"unitxt_ct.prs", Language::TRADITIONAL_CHINESE}, // BB Traditional Chinese
|
||||||
{"unitxt_k.prs", 0x07}, // PC Korean
|
{"unitxt_k.prs", Language::KOREAN}, // PC Korean
|
||||||
{"unitxt_h.prs", 0x07}, // BB Korean
|
{"unitxt_h.prs", Language::KOREAN}, // BB Korean
|
||||||
});
|
});
|
||||||
if (!uses_utf16(version)) {
|
if (!uses_utf16(version)) {
|
||||||
for (const auto& it : bintext_filenames) {
|
for (const auto& [base_filename, language] : bintext_filenames) {
|
||||||
string file_path = directory + "/" + subdirectory + "/" + it.first;
|
string file_path = directory + "/" + subdirectory + "/" + base_filename;
|
||||||
string json_path = file_path + ".json";
|
string json_path = file_path + ".json";
|
||||||
if (std::filesystem::is_regular_file(json_path)) {
|
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->log.debug_f("Loading {} {} JSON text set from {}", phosg::name_for_enum(version), name_for_language(language), json_path);
|
||||||
this->add_set(version, it.second, make_shared<BinaryTextSet>(phosg::JSON::parse(phosg::load_file(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)) {
|
} 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->log.debug_f("Loading {} {} binary text set from {}", phosg::name_for_enum(version), name_for_language(language), file_path);
|
||||||
this->add_set(version, it.second, make_set(phosg::load_file(file_path), it.second == 0));
|
this->add_set(version, language, make_set(phosg::load_file(file_path), language == Language::JAPANESE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const auto& it : unitext_filenames) {
|
for (const auto& [base_filename, language] : unitext_filenames) {
|
||||||
string file_path = directory + "/" + subdirectory + "/" + it.first;
|
string file_path = directory + "/" + subdirectory + "/" + base_filename;
|
||||||
string json_path = file_path + ".json";
|
string json_path = file_path + ".json";
|
||||||
if (std::filesystem::is_regular_file(json_path)) {
|
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->log.debug_f("Loading {} {} JSON text set from {}", phosg::name_for_enum(version), name_for_language(language), json_path);
|
||||||
this->add_set(version, it.second, make_shared<UnicodeTextSet>(phosg::JSON::parse(phosg::load_file(json_path))));
|
this->add_set(version, language, make_shared<UnicodeTextSet>(phosg::JSON::parse(phosg::load_file(json_path))));
|
||||||
} else {
|
} 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) {
|
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->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, it.second, make_set(*patch_file, it.second == 0));
|
this->add_set(version, language, make_set(*patch_file, language == Language::JAPANESE));
|
||||||
} else {
|
} else {
|
||||||
if (std::filesystem::is_regular_file(file_path)) {
|
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->log.debug_f("Loading {} {} Unicode text set from {}", phosg::name_for_enum(version), name_for_language(language), file_path);
|
||||||
this->add_set(version, it.second, make_set(phosg::load_file(file_path), it.second == 0));
|
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;
|
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));
|
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);
|
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);
|
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));
|
return this->sets.at(this->key_for_set(version, language));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t TextIndex::key_for_set(Version version, uint8_t language) {
|
uint32_t TextIndex::key_for_set(Version version, Language language) {
|
||||||
return (static_cast<uint32_t>(version) << 8) | 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);
|
std::function<std::shared_ptr<const std::string>(Version, const std::string&)> get_patch_file = nullptr);
|
||||||
~TextIndex() = default;
|
~TextIndex() = default;
|
||||||
|
|
||||||
void add_set(Version version, uint8_t language, std::shared_ptr<const TextSet> ts);
|
void add_set(Version version, Language language, std::shared_ptr<const TextSet> ts);
|
||||||
void delete_set(Version version, uint8_t language);
|
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::string& get(Version version, Language 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;
|
const std::vector<std::string>& get(Version version, Language language, size_t collection_index) const;
|
||||||
std::shared_ptr<const TextSet> get(Version version, uint8_t language) const;
|
std::shared_ptr<const TextSet> get(Version version, Language language) const;
|
||||||
|
|
||||||
protected:
|
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;
|
phosg::PrefixedLogger log;
|
||||||
std::unordered_map<uint32_t, std::shared_ptr<const TextSet>> sets;
|
std::unordered_map<uint32_t, std::shared_ptr<const TextSet>> sets;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.version DC_V1
|
.version DC_V1
|
||||||
.quest_num 50
|
.quest_num 50
|
||||||
.language 1
|
.language E
|
||||||
.name "Soul of a Blacksmith"
|
.name "Soul of a Blacksmith"
|
||||||
.short_desc "A blacksmith\nwants to make\na weapon using\nRagol\'s unknown\nmaterials."
|
.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"
|
.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
|
.version DC_V1
|
||||||
.quest_num 52
|
.quest_num 52
|
||||||
.language 1
|
.language E
|
||||||
.name "The Retired Hunter"
|
.name "The Retired Hunter"
|
||||||
.short_desc "I will kill\n10000 monsters\nbefore I die!"
|
.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"
|
.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
|
.version GC_V3
|
||||||
.quest_num 88530
|
.quest_num 88530
|
||||||
.language 1
|
.language E
|
||||||
.episode Episode1
|
.episode Episode1
|
||||||
.name "GC v1.2 USA patch enabler"
|
.name "GC v1.2 USA patch enabler"
|
||||||
.short_desc ""
|
.short_desc ""
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.version GC_V3
|
.version GC_V3
|
||||||
.quest_num 88531
|
.quest_num 88531
|
||||||
.language 0
|
.language J
|
||||||
.episode Episode1
|
.episode Episode1
|
||||||
.name "GC v1.5 JP patch enabler"
|
.name "GC v1.5 JP patch enabler"
|
||||||
.short_desc ""
|
.short_desc ""
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.version GC_EP3
|
.version GC_EP3
|
||||||
.quest_num 88532
|
.quest_num 88532
|
||||||
.language 1
|
.language E
|
||||||
.episode Episode1
|
.episode Episode1
|
||||||
.name "GC Ep3 USA patch enabler"
|
.name "GC Ep3 USA patch enabler"
|
||||||
.short_desc ""
|
.short_desc ""
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.version GC_EP3
|
.version GC_EP3
|
||||||
.quest_num 88533
|
.quest_num 88533
|
||||||
.language 1
|
.language E
|
||||||
.episode Episode1
|
.episode Episode1
|
||||||
.name "GC Ep3 EU patch enabler"
|
.name "GC Ep3 EU patch enabler"
|
||||||
.short_desc ""
|
.short_desc ""
|
||||||
|
|||||||
@@ -27,15 +27,15 @@
|
|||||||
// versions (DC, GC, and XB), this affects how strings are encoded - Japanese
|
// 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
|
// 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:
|
// used for strings in all languages.) The language values are:
|
||||||
// 0 = Japanese
|
// J = Japanese
|
||||||
// 1 = English
|
// E = English
|
||||||
// 2 = German
|
// G = German
|
||||||
// 3 = French
|
// F = French
|
||||||
// 4 = Spanish
|
// S = Spanish
|
||||||
// 5 = Chinese (simplified)
|
// B = Chinese (simplified)
|
||||||
// 6 = Chinese (traditional)
|
// T = Chinese (traditional)
|
||||||
// 7 = Korean
|
// K = Korean
|
||||||
.language 1
|
.language E
|
||||||
|
|
||||||
// The .episode directive specifies the quest's episode. The server ignores this
|
// 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
|
// if a set_episode or set_episode2 opcode is present in the code following the
|
||||||
|
|||||||
Reference in New Issue
Block a user