split Episode3::DataIndex into multiple structures

This commit is contained in:
Martin Michelsen
2023-08-13 23:12:27 -07:00
parent 7e55719983
commit 87118049ab
36 changed files with 416 additions and 328 deletions
+3 -3
View File
@@ -5,7 +5,7 @@
#include <stdexcept>
#include <string>
#include "Episode3/DataIndex.hh"
#include "Episode3/DataIndexes.hh"
#include "Episode3/DeckState.hh"
#include "Episode3/MapState.hh"
#include "Episode3/PlayerStateSubordinates.hh"
@@ -5178,8 +5178,8 @@ struct G_MapList_GC_Ep3_6xB6x40 {
le_uint16_t compressed_data_size;
le_uint16_t unused;
// PRS-compressed map list data follows here. newserv generates this from the
// map index at startup time; see the MapList struct in Episode3/DataIndex.hh
// and Episode3::DataIndex::get_compressed_map_list for details on the format.
// map index at startup; see the MapList struct in Episode3/DataIndexes.hh
// and Episode3::MapIndex::get_compressed_map_list for details on the format.
} __packed__;
struct G_MapData_GC_Ep3_6xB6x41 {
+1 -1
View File
@@ -133,7 +133,7 @@ uint16_t AssistServer::card_id_for_card_ref(uint16_t card_ref) const {
return this->server()->card_id_for_card_ref(card_ref);
}
shared_ptr<const DataIndex::CardEntry> AssistServer::definition_for_card_id(
shared_ptr<const CardIndex::CardEntry> AssistServer::definition_for_card_id(
uint16_t card_id) const {
return this->server()->definition_for_card_id(card_id);
}
+4 -4
View File
@@ -5,7 +5,7 @@
#include <memory>
#include <vector>
#include "DataIndex.hh"
#include "DataIndexes.hh"
#include "DeckState.hh"
#include "PlayerState.hh"
@@ -24,7 +24,7 @@ public:
std::shared_ptr<const Server> server() const;
uint16_t card_id_for_card_ref(uint16_t card_ref) const;
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
uint32_t compute_num_assist_effects_for_client(uint16_t client_id);
uint32_t compute_num_assist_effects_for_team(uint32_t team_id);
@@ -40,11 +40,11 @@ private:
public:
parray<AssistEffect, 4> assist_effects;
std::shared_ptr<const DataIndex::CardEntry> assist_card_defs[4];
std::shared_ptr<const CardIndex::CardEntry> assist_card_defs[4];
uint32_t num_assist_cards_set;
parray<uint8_t, 4> client_ids_with_assists;
parray<AssistEffect, 4> active_assist_effects;
std::shared_ptr<const DataIndex::CardEntry> active_assist_card_defs[4];
std::shared_ptr<const CardIndex::CardEntry> active_assist_card_defs[4];
uint32_t num_active_assists;
std::shared_ptr<HandAndEquipState> hand_and_equip_states[4];
std::shared_ptr<parray<CardShortStatus, 0x10>> card_short_statuses[4];
+1 -1
View File
@@ -507,7 +507,7 @@ bool Card::get_attack_condition_value(
cond_type, card_ref, def_effect_index, value, out_value);
}
shared_ptr<const DataIndex::CardEntry> Card::get_definition() const {
shared_ptr<const CardIndex::CardEntry> Card::get_definition() const {
return this->def_entry;
}
+4 -4
View File
@@ -6,7 +6,7 @@
#include "../CommandFormats.hh"
#include "../Text.hh"
#include "DataIndex.hh"
#include "DataIndexes.hh"
namespace Episode3 {
@@ -58,7 +58,7 @@ public:
uint8_t def_effect_index,
uint16_t value,
uint16_t* out_value) const;
std::shared_ptr<const DataIndex::CardEntry> get_definition() const;
std::shared_ptr<const CardIndex::CardEntry> get_definition() const;
uint16_t get_card_ref() const;
uint8_t get_client_id() const;
uint8_t get_current_hp() const;
@@ -99,12 +99,12 @@ private:
public:
int16_t max_hp;
int16_t current_hp;
std::shared_ptr<const DataIndex::CardEntry> def_entry;
std::shared_ptr<const CardIndex::CardEntry> def_entry;
uint8_t client_id;
uint16_t card_id;
uint16_t card_ref;
uint16_t sc_card_ref;
std::shared_ptr<const DataIndex::CardEntry> sc_def_entry;
std::shared_ptr<const CardIndex::CardEntry> sc_def_entry;
CardType sc_card_type;
uint8_t team_id;
uint32_t card_flags;
+14 -14
View File
@@ -816,7 +816,7 @@ shared_ptr<Card> CardSpecial::compute_replaced_target_based_on_conditions(
// the Gifoie card's ID (00D9) for compute_effective_range.
// TODO: We should fix this so it doesn't rely on a fixed card definition.
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, 0x00D9, target_card_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, 0x00D9, target_card_loc, this->server()->base()->map_and_rules1);
auto card_refs_in_parry_range = target_ps->get_all_cards_within_range(
range, target_card_loc, 0xFF);
@@ -2526,7 +2526,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
if (ce && ps) {
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, ce->def.card_id, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
add_card_refs(ps->get_card_refs_within_range_from_all_players(range, card1_loc, CardType::ITEM));
}
}
@@ -2566,7 +2566,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
if (ce && ps) {
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, ce->def.card_id, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
add_card_refs(ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id()));
}
}
@@ -2631,7 +2631,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
// should fix this eventually.
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id());
for (uint16_t result_card_ref : result_card_refs) {
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
@@ -2655,7 +2655,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
// TODO: Again with the Gifoie hardcoding...
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
for (uint16_t result_card_ref : result_card_refs) {
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
@@ -2721,7 +2721,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
// TODO: Again with the Gifoie hardcoding...
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
for (uint16_t result_card_ref : result_card_refs) {
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
@@ -2775,7 +2775,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
// TODO: Yet another Gifoie hardcode location :(
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id());
for (uint16_t result_card_ref : result_card_refs) {
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
@@ -2802,7 +2802,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
// TODO: Sigh. Gifoie again.
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
for (uint16_t result_card_ref : result_card_refs) {
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
@@ -2898,7 +2898,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
// Slay instead of Gifoie
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x009C, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
for (uint16_t result_card_ref : result_card_refs) {
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
@@ -2927,7 +2927,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
// TODO: Sigh. Gifoie. Sigh.
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, 0xFF);
for (uint16_t result_card_ref : result_card_refs) {
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
@@ -2965,7 +2965,7 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
// TODO: One more Gifoie here.
uint16_t range_card_id = this->get_card_id_with_effective_range(card1, 0x00D9, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, range_card_id, card1_loc, this->server()->base()->map_and_rules1);
auto result_card_refs = ps->get_all_cards_within_range(range, card1_loc, card1->get_team_id());
for (uint16_t result_card_ref : result_card_refs) {
auto result_card = this->server()->card_for_set_card_ref(result_card_ref);
@@ -3383,7 +3383,7 @@ void CardSpecial::check_for_defense_interference(
shared_ptr<Card> target_card,
int16_t* inout_unknown_p4) {
// Note: This check is not part of the original implementation.
if (this->server()->base()->data_index->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
if (this->server()->base()->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
return;
}
@@ -4126,7 +4126,7 @@ vector<shared_ptr<const Card>> CardSpecial::filter_cards_by_range(
// TODO: Remove hardcoded card ID here (Earthquake)
uint16_t card_id = this->get_card_id_with_effective_range(card1, 0x00ED, card2);
parray<uint8_t, 9 * 9> range;
compute_effective_range(range, this->server()->base()->data_index, card_id, card1_loc, this->server()->base()->map_and_rules1);
compute_effective_range(range, this->server()->base()->card_index, card_id, card1_loc, this->server()->base()->map_and_rules1);
auto card_refs_in_range = ps->get_card_refs_within_range_from_all_players(range, card1_loc, CardType::ITEM);
for (auto card : cards) {
@@ -4342,7 +4342,7 @@ void CardSpecial::unknown_8024A9D8(const ActionState& pa, uint16_t action_card_r
void CardSpecial::check_for_attack_interference(shared_ptr<Card> unknown_p2) {
// Note: This check is not part of the original implementation.
if (this->server()->base()->data_index->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
if (this->server()->base()->behavior_flags & BehaviorFlag::DISABLE_INTERFERENCE) {
return;
}
+1 -1
View File
@@ -5,7 +5,7 @@
#include <memory>
#include "../Text.hh"
#include "DataIndex.hh"
#include "DataIndexes.hh"
namespace Episode3 {
@@ -1,4 +1,4 @@
#include "DataIndex.hh"
#include "DataIndexes.hh"
#include <stdint.h>
@@ -1056,6 +1056,33 @@ Card: %04" PRIX32 " \"%s\"\n\
}
}
void PlayerConfig::decrypt() {
if (!this->is_encrypted) {
return;
}
decrypt_trivial_gci_data(
&this->card_counts,
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
this->basis);
this->is_encrypted = 0;
this->basis = 0;
}
void PlayerConfig::encrypt(uint8_t basis) {
if (this->is_encrypted) {
if (this->basis == basis) {
return;
}
this->decrypt();
}
decrypt_trivial_gci_data(
&this->card_counts,
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
basis);
this->is_encrypted = 1;
this->basis = basis;
}
HPType hp_type_for_name(const char* name) {
if (!strcmp(name, "DEFEAT_PLAYER")) {
return HPType::DEFEAT_PLAYER;
@@ -1351,7 +1378,7 @@ void StateFlags::clear_FF() {
this->client_sc_card_types.clear(CardType::INVALID_FF);
}
string MapDefinition::str(const DataIndex* data_index) const {
string MapDefinition::str(const CardIndex* card_index) const {
deque<string> lines;
auto add_map = [&](const parray<parray<uint8_t, 0x10>, 0x10>& tiles) {
for (size_t y = 0; y < 0x10; y++) {
@@ -1456,10 +1483,10 @@ string MapDefinition::str(const DataIndex* data_index) const {
lines.emplace_back(" name: " + string(this->npc_decks[z].name));
for (size_t w = 0; w < 0x20; w++) {
uint16_t card_id = this->npc_decks[z].card_ids[w];
shared_ptr<const DataIndex::CardEntry> entry;
if (data_index) {
shared_ptr<const CardIndex::CardEntry> entry;
if (card_index) {
try {
entry = data_index->definition_for_card_id(card_id);
entry = card_index->definition_for_id(card_id);
} catch (const out_of_range&) {
}
}
@@ -1496,10 +1523,10 @@ string MapDefinition::str(const DataIndex* data_index) const {
}
for (size_t z = 0; z < 0x10; z++) {
uint16_t card_id = this->reward_card_ids[z];
shared_ptr<const DataIndex::CardEntry> entry;
if (data_index) {
shared_ptr<const CardIndex::CardEntry> entry;
if (card_index) {
try {
entry = data_index->definition_for_card_id(card_id);
entry = card_index->definition_for_id(card_id);
} catch (const out_of_range&) {
}
}
@@ -1671,14 +1698,12 @@ bool Rules::check_and_reset_invalid_fields() {
return ret;
}
DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
: behavior_flags(behavior_flags) {
CardIndex::CardIndex(const string& filename, const string& decompressed_filename, const string& text_filename) {
unordered_map<uint32_t, vector<string>> card_tags;
unordered_map<uint32_t, string> card_text;
if (this->behavior_flags & BehaviorFlag::LOAD_CARD_TEXT) {
if (!text_filename.empty()) {
try {
string data = prs_decompress(load_file(directory + "/card-text.mnr"));
string data = prs_decompress(load_file(text_filename));
StringReader r(data);
while (!r.eof()) {
@@ -1769,20 +1794,20 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
try {
string decompressed_data;
if (isfile(directory + "/card-definitions.mnrd")) {
this->mtime_for_card_definitions = stat(directory + "/card-definitions.mnrd").st_mtime;
decompressed_data = load_file(directory + "/card-definitions.mnrd");
this->mtime_for_card_definitions = stat(filename).st_mtime;
try {
decompressed_data = load_file(decompressed_filename);
this->compressed_card_definitions.clear();
} else {
this->mtime_for_card_definitions = stat(directory + "/card-definitions.mnr").st_mtime;
this->compressed_card_definitions = load_file(directory + "/card-definitions.mnr");
} catch (const cannot_open_file&) {
this->compressed_card_definitions = load_file(filename);
decompressed_data = prs_decompress(this->compressed_card_definitions);
}
if (decompressed_data.size() > 0x36EC0) {
throw runtime_error("decompressed card list data is too long");
}
// There's a footer after the card definitions, but we ignore it
// There's a footer after the card definitions (it's a standard-format REL
// file), but we ignore it
if (decompressed_data.size() % sizeof(CardDefinition) != sizeof(CardDefinitionsFooter)) {
throw runtime_error(string_printf(
"decompressed card update file size %zX is not aligned with card definition size %zX (%zX extra bytes)",
@@ -1792,10 +1817,9 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
size_t max_cards = decompressed_data.size() / sizeof(CardDefinition);
for (size_t x = 0; x < max_cards; x++) {
// The last card entry has the build date and some other metadata (and
// isn't a real card, obviously), so skip it. Seems like the card ID is
// always a large number that won't fit in a uint16_t, so we use that to
// determine if the entry is a real card or not.
if (defs[x].card_id & 0xFFFF0000) {
// isn't a real card, obviously), so skip it. The game detects this by
// checking for a negative value in type, which we also do here.
if (static_cast<int8_t>(defs[x].type) < 0) {
continue;
}
@@ -1816,7 +1840,7 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
entry->def.mv.decode_code();
entry->def.decode_range();
if (this->behavior_flags & BehaviorFlag::LOAD_CARD_TEXT) {
if (!text_filename.empty()) {
try {
entry->text = std::move(card_text.at(defs[x].card_id));
} catch (const out_of_range&) {
@@ -1836,6 +1860,25 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
"Compressed card definitions (%zu bytes -> %zu bytes) in %" PRIu64 "ms",
decompressed_data.size(), this->compressed_card_definitions.size(), diff);
}
if (this->compressed_card_definitions.size() > 0x7BF8) {
// Try to reduce the compressed size by clearing out text
static_game_data_log.info("Compressed card list data is too long; removing text");
for (size_t x = 0; x < max_cards; x++) {
if (static_cast<int8_t>(defs[x].type) < 0) {
continue;
}
defs[x].jp_name.clear();
}
uint64_t start = now();
this->compressed_card_definitions = prs_compress(decompressed_data);
uint64_t diff = now() - start;
static_game_data_log.info(
"Compressed card definitions (%zu bytes -> %zu bytes) in %" PRIu64 "ms",
decompressed_data.size(), this->compressed_card_definitions.size(), diff);
print_data(stderr, this->compressed_card_definitions);
}
if (this->compressed_card_definitions.size() > 0x7BF8) {
throw runtime_error("compressed card list data is too long");
}
@@ -1844,7 +1887,36 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
} catch (const exception& e) {
static_game_data_log.warning("Failed to load Episode 3 card update: %s", e.what());
}
}
const string& CardIndex::get_compressed_definitions() const {
if (this->compressed_card_definitions.empty()) {
throw runtime_error("card definitions are not available");
}
return this->compressed_card_definitions;
}
shared_ptr<const CardIndex::CardEntry> CardIndex::definition_for_id(uint32_t id) const {
return this->card_definitions.at(id);
}
shared_ptr<const CardIndex::CardEntry> CardIndex::definition_for_name(const string& name) const {
return this->card_definitions_by_name.at(name);
}
set<uint32_t> CardIndex::all_ids() const {
set<uint32_t> ret;
for (const auto& it : this->card_definitions) {
ret.emplace(it.first);
}
return ret;
}
uint64_t CardIndex::definitions_mtime() const {
return this->mtime_for_card_definitions;
}
MapIndex::MapIndex(const string& directory) {
auto add_maps_from_dir = [&](const string& dir, bool is_quest) -> void {
for (const auto& filename : list_directory(dir)) {
try {
@@ -1879,32 +1951,13 @@ DataIndex::DataIndex(const string& directory, uint32_t behavior_flags)
};
add_maps_from_dir(directory + "/maps-free", false);
add_maps_from_dir(directory + "/maps-quest", true);
try {
auto json = JSONObject::parse(load_file(directory + "/com-decks.json"));
for (const auto& def_json : json->as_list()) {
auto& def = this->com_decks.emplace_back(new COMDeckDefinition());
def->index = this->com_decks.size() - 1;
def->player_name = def_json->at(0)->as_string();
def->deck_name = def_json->at(1)->as_string();
auto card_ids_json = def_json->at(2)->as_list();
for (size_t z = 0; z < 0x1F; z++) {
def->card_ids[z] = card_ids_json.at(z)->as_int();
}
if (!this->com_decks_by_name.emplace(def->deck_name, def).second) {
throw runtime_error("duplicate COM deck name: " + def->deck_name);
}
}
} catch (const exception& e) {
static_game_data_log.warning("Failed to load Episode 3 COM decks: %s", e.what());
}
}
DataIndex::MapEntry::MapEntry(const MapDefinition& map, bool is_quest)
MapIndex::MapEntry::MapEntry(const MapDefinition& map, bool is_quest)
: map(map),
is_quest(is_quest) {}
DataIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
MapIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
: is_quest(is_quest),
compressed_data(compressed) {
string decompressed = prs_decompress(this->compressed_data);
@@ -1916,43 +1969,14 @@ DataIndex::MapEntry::MapEntry(const string& compressed, bool is_quest)
this->map = *reinterpret_cast<const MapDefinition*>(decompressed.data());
}
string DataIndex::MapEntry::compressed() const {
string MapIndex::MapEntry::compressed() const {
if (this->compressed_data.empty()) {
this->compressed_data = prs_compress(&this->map, sizeof(this->map));
}
return this->compressed_data;
}
const string& DataIndex::get_compressed_card_definitions() const {
if (this->compressed_card_definitions.empty()) {
throw runtime_error("card definitions are not available");
}
return this->compressed_card_definitions;
}
shared_ptr<const DataIndex::CardEntry> DataIndex::definition_for_card_id(
uint32_t id) const {
return this->card_definitions.at(id);
}
shared_ptr<const DataIndex::CardEntry> DataIndex::definition_for_card_name(
const string& name) const {
return this->card_definitions_by_name.at(name);
}
set<uint32_t> DataIndex::all_card_ids() const {
set<uint32_t> ret;
for (const auto& it : this->card_definitions) {
ret.emplace(it.first);
}
return ret;
}
uint64_t DataIndex::card_definitions_mtime() const {
return this->mtime_for_card_definitions;
}
const string& DataIndex::get_compressed_map_list() const {
const string& MapIndex::get_compressed_list() const {
if (this->compressed_map_list.empty()) {
StringWriter entries_w;
StringWriter strings_w;
@@ -2012,16 +2036,15 @@ const string& DataIndex::get_compressed_map_list() const {
return this->compressed_map_list;
}
shared_ptr<const DataIndex::MapEntry> DataIndex::definition_for_map_number(uint32_t id) const {
shared_ptr<const MapIndex::MapEntry> MapIndex::definition_for_number(uint32_t id) const {
return this->maps.at(id);
}
shared_ptr<const DataIndex::MapEntry> DataIndex::definition_for_map_name(
const string& name) const {
shared_ptr<const MapIndex::MapEntry> MapIndex::definition_for_name(const string& name) const {
return this->maps_by_name.at(name);
}
set<uint32_t> DataIndex::all_map_ids() const {
set<uint32_t> MapIndex::all_numbers() const {
set<uint32_t> ret;
for (const auto& it : this->maps) {
ret.emplace(it.first);
@@ -2029,47 +2052,41 @@ set<uint32_t> DataIndex::all_map_ids() const {
return ret;
}
size_t DataIndex::num_com_decks() const {
return this->com_decks.size();
}
shared_ptr<const COMDeckDefinition> DataIndex::com_deck(size_t which) const {
return this->com_decks.at(which);
}
shared_ptr<const COMDeckDefinition> DataIndex::com_deck(const string& which) const {
return this->com_decks_by_name.at(which);
}
shared_ptr<const COMDeckDefinition> DataIndex::random_com_deck() const {
return this->com_decks[random_object<size_t>() % this->com_decks.size()];
}
void PlayerConfig::decrypt() {
if (!this->is_encrypted) {
return;
}
decrypt_trivial_gci_data(
&this->card_counts,
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
this->basis);
this->is_encrypted = 0;
this->basis = 0;
}
void PlayerConfig::encrypt(uint8_t basis) {
if (this->is_encrypted) {
if (this->basis == basis) {
return;
COMDeckIndex::COMDeckIndex(const string& filename) {
try {
auto json = JSONObject::parse(load_file(filename));
for (const auto& def_json : json->as_list()) {
auto& def = this->decks.emplace_back(new COMDeckDefinition());
def->index = this->decks.size() - 1;
def->player_name = def_json->at(0)->as_string();
def->deck_name = def_json->at(1)->as_string();
auto card_ids_json = def_json->at(2)->as_list();
for (size_t z = 0; z < 0x1F; z++) {
def->card_ids[z] = card_ids_json.at(z)->as_int();
}
if (!this->decks_by_name.emplace(def->deck_name, def).second) {
throw runtime_error("duplicate COM deck name: " + def->deck_name);
}
}
this->decrypt();
} catch (const exception& e) {
static_game_data_log.warning("Failed to load Episode 3 COM decks: %s", e.what());
}
decrypt_trivial_gci_data(
&this->card_counts,
offsetof(PlayerConfig, decks) - offsetof(PlayerConfig, card_counts),
basis);
this->is_encrypted = 1;
this->basis = basis;
}
size_t COMDeckIndex::num_decks() const {
return this->decks.size();
}
shared_ptr<const COMDeckDefinition> COMDeckIndex::deck_for_index(size_t which) const {
return this->decks.at(which);
}
shared_ptr<const COMDeckDefinition> COMDeckIndex::deck_for_name(const string& which) const {
return this->decks_by_name.at(which);
}
shared_ptr<const COMDeckDefinition> COMDeckIndex::random_deck() const {
return this->decks[random_object<size_t>() % this->decks.size()];
}
} // namespace Episode3
@@ -15,21 +15,22 @@
namespace Episode3 {
// The comment in Server.hh does not apply to this file (and DataIndex.cc).
// The comment in Server.hh does not apply to this file (and DataIndexes.cc).
// Except for the Location structure, these structures and functions are not
// based on Sega's original implementation.
class DataIndex;
class CardIndex;
class MapIndex;
class COMDeckIndex;
const char* name_for_link_color(uint8_t color);
enum BehaviorFlag {
enum BehaviorFlag : uint32_t {
SKIP_DECK_VERIFY = 0x00000001,
IGNORE_CARD_COUNTS = 0x00000002,
SKIP_D1_D2_REPLACE = 0x00000004,
DISABLE_TIME_LIMITS = 0x00000008,
ENABLE_STATUS_MESSAGES = 0x00000010,
LOAD_CARD_TEXT = 0x00000020,
ENABLE_RECORDING = 0x00000040,
DISABLE_MASKING = 0x00000080,
DISABLE_INTERFERENCE = 0x00000100,
@@ -952,10 +953,9 @@ struct MapDefinition { // .mnmd format; also the format of (decompressed) quests
uint8_t deck_type;
} __attribute__((packed));
/* 5A10 */ parray<EntryState, 4> entry_states;
/* 5A18 */
std::string str(const DataIndex* data_index = nullptr) const;
std::string str(const CardIndex* card_index = nullptr) const;
} __attribute__((packed));
struct COMDeckDefinition {
@@ -965,9 +965,9 @@ struct COMDeckDefinition {
parray<le_uint16_t, 0x1F> card_ids;
};
class DataIndex {
class CardIndex {
public:
DataIndex(const std::string& directory, uint32_t behavior_flags);
CardIndex(const std::string& filename, const std::string& decompressed_filename, const std::string& text_filename = "");
struct CardEntry {
CardDefinition def;
@@ -975,6 +975,23 @@ public:
std::vector<std::string> debug_tags; // Empty unless debug == true
};
const std::string& get_compressed_definitions() const;
std::shared_ptr<const CardEntry> definition_for_id(uint32_t id) const;
std::shared_ptr<const CardEntry> definition_for_name(const std::string& name) const;
std::set<uint32_t> all_ids() const;
uint64_t definitions_mtime() const;
private:
std::string compressed_card_definitions;
std::unordered_map<uint32_t, std::shared_ptr<CardEntry>> card_definitions;
std::unordered_map<std::string, std::shared_ptr<CardEntry>> card_definitions_by_name;
uint64_t mtime_for_card_definitions;
};
class MapIndex {
public:
MapIndex(const std::string& directory);
class MapEntry {
public:
MapDefinition map;
@@ -989,42 +1006,33 @@ public:
mutable std::string compressed_data;
};
const std::string& get_compressed_card_definitions() const;
std::shared_ptr<const CardEntry> definition_for_card_id(uint32_t id) const;
std::shared_ptr<const CardEntry> definition_for_card_name(
const std::string& name) const;
std::set<uint32_t> all_card_ids() const;
uint64_t card_definitions_mtime() const;
const std::string& get_compressed_map_list() const;
std::shared_ptr<const MapEntry> definition_for_map_number(uint32_t id) const;
std::shared_ptr<const MapEntry> definition_for_map_name(
const std::string& name) const;
std::set<uint32_t> all_map_ids() const;
size_t num_com_decks() const;
std::shared_ptr<const COMDeckDefinition> com_deck(size_t which) const;
std::shared_ptr<const COMDeckDefinition> com_deck(const std::string& name) const;
std::shared_ptr<const COMDeckDefinition> random_com_deck() const;
const uint32_t behavior_flags;
const std::string& get_compressed_list() const;
std::shared_ptr<const MapEntry> definition_for_number(uint32_t id) const;
std::shared_ptr<const MapEntry> definition_for_name(const std::string& name) const;
std::set<uint32_t> all_numbers() const;
private:
std::string compressed_card_definitions;
std::unordered_map<uint32_t, std::shared_ptr<CardEntry>> card_definitions;
std::unordered_map<std::string, std::shared_ptr<CardEntry>> card_definitions_by_name;
uint64_t mtime_for_card_definitions;
// The compressed map list is generated on demand from the maps map below.
// It's marked mutable because the logical consistency of the DataIndex object
// It's marked mutable because the logical consistency of the MapIndex object
// is not violated from the caller's perspective even if we don't generate the
// compressed map list at load time.
mutable std::string compressed_map_list;
std::map<uint32_t, std::shared_ptr<MapEntry>> maps;
std::unordered_map<std::string, std::shared_ptr<MapEntry>> maps_by_name;
};
std::vector<std::shared_ptr<COMDeckDefinition>> com_decks;
std::unordered_map<std::string, std::shared_ptr<COMDeckDefinition>> com_decks_by_name;
class COMDeckIndex {
public:
COMDeckIndex(const std::string& filename);
size_t num_decks() const;
std::shared_ptr<const COMDeckDefinition> deck_for_index(size_t which) const;
std::shared_ptr<const COMDeckDefinition> deck_for_name(const std::string& name) const;
std::shared_ptr<const COMDeckDefinition> random_deck() const;
private:
std::vector<std::shared_ptr<COMDeckDefinition>> decks;
std::unordered_map<std::string, std::shared_ptr<COMDeckDefinition>> decks_by_name;
};
} // namespace Episode3
+1 -1
View File
@@ -5,7 +5,7 @@
#include <memory>
#include "../Text.hh"
#include "DataIndex.hh"
#include "DataIndexes.hh"
namespace Episode3 {
+1 -1
View File
@@ -6,7 +6,7 @@
#include "../Text.hh"
#include "Card.hh"
#include "DataIndex.hh"
#include "DataIndexes.hh"
#include "DeckState.hh"
#include "PlayerStateSubordinates.hh"
+1 -1
View File
@@ -5,7 +5,7 @@
#include <memory>
#include "../Text.hh"
#include "DataIndex.hh"
#include "DataIndexes.hh"
namespace Episode3 {
+8 -9
View File
@@ -8,7 +8,7 @@ namespace Episode3 {
void compute_effective_range(
parray<uint8_t, 9 * 9>& ret,
shared_ptr<const DataIndex> data_index,
shared_ptr<const CardIndex> card_index,
uint16_t card_id,
const Location& loc,
shared_ptr<const MapAndRulesState> map_and_rules) {
@@ -19,9 +19,9 @@ void compute_effective_range(
// Heavy Fog: one tile directly in front
range_def[3] = 0x00000100;
} else {
shared_ptr<const DataIndex::CardEntry> ce;
shared_ptr<const CardIndex::CardEntry> ce;
try {
ce = data_index->definition_for_card_id(card_id);
ce = card_index->definition_for_id(card_id);
} catch (const out_of_range&) {
return;
}
@@ -126,9 +126,9 @@ void compute_effective_range(
}
bool card_linkage_is_valid(
shared_ptr<const DataIndex::CardEntry> right_ce,
shared_ptr<const DataIndex::CardEntry> left_ce,
shared_ptr<const DataIndex::CardEntry> sc_ce,
shared_ptr<const CardIndex::CardEntry> right_ce,
shared_ptr<const CardIndex::CardEntry> left_ce,
shared_ptr<const CardIndex::CardEntry> sc_ce,
bool has_permission_effect) {
if (!right_ce) {
return false;
@@ -1613,7 +1613,7 @@ bool RulerServer::defense_card_matches_any_attack_card_top_color(
return false;
}
shared_ptr<const DataIndex::CardEntry> RulerServer::definition_for_card_ref(uint16_t card_ref) const {
shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_ref(uint16_t card_ref) const {
uint16_t card_id = this->card_id_for_card_ref(card_ref);
if (card_id == 0xFFFF) {
return nullptr;
@@ -1972,8 +1972,7 @@ uint16_t RulerServer::get_ally_sc_card_ref(uint16_t card_ref) const {
return 0xFFFF;
}
shared_ptr<const DataIndex::CardEntry> RulerServer::definition_for_card_id(
uint32_t card_id) const {
shared_ptr<const CardIndex::CardEntry> RulerServer::definition_for_card_id(uint32_t card_id) const {
return this->server()->definition_for_card_id(card_id);
}
+7 -7
View File
@@ -5,7 +5,7 @@
#include <memory>
#include "AssistServer.hh"
#include "DataIndex.hh"
#include "DataIndexes.hh"
#include "DeckState.hh"
#include "PlayerState.hh"
@@ -15,15 +15,15 @@ class Server;
void compute_effective_range(
parray<uint8_t, 9 * 9>& ret,
std::shared_ptr<const DataIndex> data_index,
std::shared_ptr<const CardIndex> card_index,
uint16_t card_id,
const Location& loc,
std::shared_ptr<const MapAndRulesState> map_and_rules);
bool card_linkage_is_valid(
std::shared_ptr<const DataIndex::CardEntry> right_def,
std::shared_ptr<const DataIndex::CardEntry> left_def,
std::shared_ptr<const DataIndex::CardEntry> sc_def,
std::shared_ptr<const CardIndex::CardEntry> right_def,
std::shared_ptr<const CardIndex::CardEntry> left_def,
std::shared_ptr<const CardIndex::CardEntry> sc_def,
bool has_permission_effect);
class RulerServer {
@@ -130,7 +130,7 @@ public:
uint16_t attacker_sc_card_ref) const;
bool defense_card_matches_any_attack_card_top_color(
const ActionState& pa) const;
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
int32_t error_code_for_client_setting_card(
uint8_t client_id,
uint16_t card_ref,
@@ -156,7 +156,7 @@ public:
size_t num_occupied_tiles,
size_t num_vacant_tiles) const;
uint16_t get_ally_sc_card_ref(uint16_t card_ref) const;
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_id(
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_id(
uint32_t card_id) const;
uint32_t get_card_id_with_effective_range(
uint16_t card_ref, uint16_t card_id_override, TargetMode* out_target_mode) const;
+22 -20
View File
@@ -26,11 +26,15 @@ void ServerBase::PresenceEntry::clear() {
ServerBase::ServerBase(
shared_ptr<Lobby> lobby,
shared_ptr<const DataIndex> data_index,
shared_ptr<const CardIndex> card_index,
shared_ptr<const MapIndex> map_index,
uint32_t behavior_flags,
shared_ptr<PSOLFGEncryption> random_crypt,
shared_ptr<const DataIndex::MapEntry> map_if_tournament)
shared_ptr<const MapIndex::MapEntry> map_if_tournament)
: lobby(lobby),
data_index(data_index),
card_index(card_index),
map_index(map_index),
behavior_flags(behavior_flags),
log(lobby->log.prefix + "[Ep3::Server] "),
random_crypt(random_crypt),
is_tournament(!!map_if_tournament),
@@ -182,7 +186,7 @@ void Server::send(const void* data, size_t size) const {
}
string masked_data;
if (!(this->base()->data_index->behavior_flags & BehaviorFlag::DISABLE_MASKING)) {
if (!(this->base()->behavior_flags & BehaviorFlag::DISABLE_MASKING)) {
if (size >= 8) {
masked_data.assign(reinterpret_cast<const char*>(data), size);
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
@@ -216,13 +220,12 @@ void Server::send_6xB4x46() const {
G_ServerVersionStrings_GC_Ep3_6xB4x46 cmd46;
cmd46.version_signature = VERSION_SIGNATURE;
cmd46.date_str1 = format_time(this->base()->data_index->card_definitions_mtime() * 1000000);
cmd46.date_str1 = format_time(this->base()->card_index->definitions_mtime() * 1000000);
cmd46.date_str2 = string_printf("Lobby/%08" PRIX32, l->lobby_id);
this->send(cmd46);
}
string Server::prepare_6xB6x41_map_definition(
shared_ptr<const DataIndex::MapEntry> map) {
string Server::prepare_6xB6x41_map_definition(shared_ptr<const MapIndex::MapEntry> map) {
const auto& compressed = map->compressed();
StringWriter w;
@@ -260,7 +263,7 @@ void Server::send_commands_for_joining_spectator(Channel& c) const {
__attribute__((format(printf, 2, 3))) void Server::log_debug(const char* fmt, ...) const {
auto l = this->base()->lobby.lock();
if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
if (l && (this->base()->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
va_list va;
va_start(va, fmt);
this->base()->log.info_v(fmt, va);
@@ -270,7 +273,7 @@ __attribute__((format(printf, 2, 3))) void Server::log_debug(const char* fmt, ..
__attribute__((format(printf, 2, 3))) void Server::send_debug_message_printf(const char* fmt, ...) const {
auto l = this->base()->lobby.lock();
if (l && (this->base()->data_index->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
if (l && (this->base()->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES)) {
va_list va;
va_start(va, fmt);
std::string buf = string_vprintf(fmt, va);
@@ -367,10 +370,9 @@ void Server::draw_phase_before() {
}
}
shared_ptr<const DataIndex::CardEntry> Server::definition_for_card_ref(uint16_t card_ref) const {
shared_ptr<const CardIndex::CardEntry> Server::definition_for_card_ref(uint16_t card_ref) const {
try {
return this->base()->data_index->definition_for_card_id(
this->card_id_for_card_ref(card_ref));
return this->base()->card_index->definition_for_id(this->card_id_for_card_ref(card_ref));
} catch (const out_of_range&) {
return nullptr;
}
@@ -581,9 +583,9 @@ void Server::copy_player_states_to_prev_states() {
}
}
shared_ptr<const DataIndex::CardEntry> Server::definition_for_card_id(uint16_t card_id) const {
shared_ptr<const CardIndex::CardEntry> Server::definition_for_card_id(uint16_t card_id) const {
try {
return this->base()->data_index->definition_for_card_id(card_id);
return this->base()->card_index->definition_for_id(card_id);
} catch (const out_of_range&) {
return nullptr;
}
@@ -1839,7 +1841,7 @@ void Server::handle_6xB3x13_update_map_during_setup(const string& data) {
*b->map_and_rules1 = in_cmd.map_and_rules_state;
*b->map_and_rules2 = in_cmd.map_and_rules_state;
b->overlay_state = in_cmd.overlay_state;
if (b->data_index->behavior_flags & BehaviorFlag::DISABLE_TIME_LIMITS) {
if (b->behavior_flags & BehaviorFlag::DISABLE_TIME_LIMITS) {
b->map_and_rules1->rules.overall_time_limit = 0;
b->map_and_rules1->rules.phase_time_limit = 0;
b->map_and_rules2->rules.overall_time_limit = 0;
@@ -1869,9 +1871,9 @@ void Server::handle_6xB3x14_update_deck_during_setup(const string& data) {
}
DeckEntry entry = in_cmd.entry;
int32_t verify_error = 0;
if (!(this->base()->data_index->behavior_flags & BehaviorFlag::SKIP_DECK_VERIFY)) {
if (!(this->base()->behavior_flags & BehaviorFlag::SKIP_DECK_VERIFY)) {
// Note: Sega's original implementation doesn't use the card counts here
if (this->base()->data_index->behavior_flags & BehaviorFlag::IGNORE_CARD_COUNTS) {
if (this->base()->behavior_flags & BehaviorFlag::IGNORE_CARD_COUNTS) {
verify_error = this->ruler_server->verify_deck(entry.card_ids);
} else {
verify_error = this->ruler_server->verify_deck(entry.card_ids,
@@ -1881,7 +1883,7 @@ void Server::handle_6xB3x14_update_deck_during_setup(const string& data) {
if (verify_error) {
throw runtime_error(string_printf("invalid deck: -0x%" PRIX32, verify_error));
}
if (!(this->base()->data_index->behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
if (!(this->base()->behavior_flags & BehaviorFlag::SKIP_D1_D2_REPLACE)) {
this->ruler_server->replace_D1_D2_rarity_cards_with_Attack(entry.card_ids);
}
*this->base()->deck_entries[in_cmd.client_id] = in_cmd.entry;
@@ -2135,7 +2137,7 @@ void Server::handle_6xB3x40_map_list_request(const string& data) {
throw runtime_error("lobby is deleted");
}
const auto& list_data = this->base()->data_index->get_compressed_map_list();
const auto& list_data = this->base()->map_index->get_compressed_list();
StringWriter w;
uint32_t subcommand_size = (list_data.size() + sizeof(G_MapList_GC_Ep3_6xB6x40) + 3) & (~3);
@@ -2164,7 +2166,7 @@ void Server::handle_6xB3x41_map_request(const string& data) {
throw runtime_error("lobby is deleted");
}
base->last_chosen_map = base->data_index->definition_for_map_number(cmd.map_number);
base->last_chosen_map = base->map_index->definition_for_number(cmd.map_number);
auto out_cmd = this->prepare_6xB6x41_map_definition(base->last_chosen_map);
send_command(l, 0x6C, 0x00, out_cmd);
for (auto watcher_l : l->watcher_lobbies) {
+13 -9
View File
@@ -19,7 +19,7 @@ namespace Episode3 {
/**
* This implementation of Episode 3 battles (contained in all files in the
* src/Episode3 directory, except for DataIndex.hh/cc) is derived from Sega's
* src/Episode3 directory, except for DataIndexes.hh/cc) is derived from Sega's
* original server implementation, reverse-engineered from the Episode 3 client
* executable. The control flow, function breakdown, and structure definitions
* in these files map very closely to how their server implementation was
@@ -49,7 +49,7 @@ namespace Episode3 {
// - - - - - - - DeckState
// - - - - - - - HandAndEquipState
// - - - - - - - MapAndRulesState / OverlayState
// - - - - - - - - Everything within DataIndex
// - - - - - - - - Everything within DataIndexes
class Server;
@@ -57,9 +57,11 @@ class ServerBase : public std::enable_shared_from_this<ServerBase> {
public:
ServerBase(
std::shared_ptr<Lobby> lobby,
std::shared_ptr<const DataIndex> data_index,
std::shared_ptr<const CardIndex> card_index,
std::shared_ptr<const MapIndex> map_index,
uint32_t behavior_flags,
std::shared_ptr<PSOLFGEncryption> random_crypt,
std::shared_ptr<const DataIndex::MapEntry> map_if_tournament);
std::shared_ptr<const MapIndex::MapEntry> map_if_tournament);
void init();
void reset();
void recreate_server();
@@ -73,11 +75,13 @@ public:
} __attribute__((packed));
std::weak_ptr<Lobby> lobby;
std::shared_ptr<const DataIndex> data_index;
std::shared_ptr<const CardIndex> card_index;
std::shared_ptr<const MapIndex> map_index;
uint32_t behavior_flags;
PrefixedLogger log;
std::shared_ptr<PSOLFGEncryption> random_crypt;
bool is_tournament;
std::shared_ptr<const DataIndex::MapEntry> last_chosen_map;
std::shared_ptr<const MapIndex::MapEntry> last_chosen_map;
std::shared_ptr<MapAndRulesState> map_and_rules1;
std::shared_ptr<MapAndRulesState> map_and_rules2;
@@ -135,7 +139,7 @@ public:
bool advance_battle_phase();
void action_phase_after();
void draw_phase_before();
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_ref(uint16_t card_ref) const;
std::shared_ptr<Card> card_for_set_card_ref(uint16_t card_ref);
std::shared_ptr<const Card> card_for_set_card_ref(uint16_t card_ref) const;
uint16_t card_id_for_card_ref(uint16_t card_ref) const;
@@ -147,7 +151,7 @@ public:
void compute_all_map_occupied_bits();
void compute_team_dice_boost(uint8_t team_id);
void copy_player_states_to_prev_states();
std::shared_ptr<const DataIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
std::shared_ptr<const CardIndex::CardEntry> definition_for_card_id(uint16_t card_id) const;
void destroy_cards_with_zero_hp();
void determine_first_team_turn();
void dice_phase_after();
@@ -229,7 +233,7 @@ public:
G_UpdateDecks_GC_Ep3_6xB4x07 prepare_6xB4x07_decks_update() const;
G_SetPlayerNames_GC_Ep3_6xB4x1C prepare_6xB4x1C_names_update() const;
static std::string prepare_6xB6x41_map_definition(
std::shared_ptr<const DataIndex::MapEntry> map);
std::shared_ptr<const MapIndex::MapEntry> map);
G_SetTrapTileLocations_GC_Ep3_6xB4x50 prepare_6xB4x50_trap_tile_locations() const;
std::vector<std::shared_ptr<Card>> const_cast_set_cards_v(
+21 -20
View File
@@ -280,15 +280,17 @@ shared_ptr<Tournament::Team> Tournament::Match::opponent_team_for_team(
}
Tournament::Tournament(
shared_ptr<const DataIndex> data_index,
shared_ptr<const MapIndex> map_index,
shared_ptr<const COMDeckIndex> com_deck_index,
uint8_t number,
const string& name,
shared_ptr<const DataIndex::MapEntry> map,
shared_ptr<const MapIndex::MapEntry> map,
const Rules& rules,
size_t num_teams,
bool is_2v2)
: log(string_printf("[Tournament/%02hhX] ", number)),
data_index(data_index),
map_index(map_index),
com_deck_index(com_deck_index),
number(number),
name(name),
map(map),
@@ -308,11 +310,13 @@ Tournament::Tournament(
}
Tournament::Tournament(
std::shared_ptr<const DataIndex> data_index,
shared_ptr<const MapIndex> map_index,
shared_ptr<const COMDeckIndex> com_deck_index,
uint8_t number,
std::shared_ptr<const JSONObject> json)
: log(string_printf("[Tournament/%02hhX] ", number)),
data_index(data_index),
map_index(map_index),
com_deck_index(com_deck_index),
source_json(json),
number(number),
current_state(State::REGISTRATION) {}
@@ -324,7 +328,7 @@ void Tournament::init() {
if (this->source_json) {
auto& dict = this->source_json->as_dict();
this->name = dict.at("name")->as_string();
this->map = this->data_index->definition_for_map_number(dict.at("map_number")->as_int());
this->map = this->map_index->definition_for_number(dict.at("map_number")->as_int());
this->rules = Rules(dict.at("rules"));
this->is_2v2 = dict.at("is_2v2")->as_bool();
is_registration_complete = dict.at("is_registration_complete")->as_bool();
@@ -344,8 +348,7 @@ void Tournament::init() {
team->players.emplace_back(serial_number);
this->all_player_serial_numbers.emplace(serial_number);
} else {
team->players.emplace_back(this->data_index->com_deck(
player_json->as_string()));
team->players.emplace_back(this->com_deck_index->deck_for_name(player_json->as_string()));
}
}
}
@@ -486,10 +489,6 @@ std::shared_ptr<JSONObject> Tournament::json() const {
return shared_ptr<JSONObject>(new JSONObject(std::move(dict)));
}
std::shared_ptr<const DataIndex> Tournament::get_data_index() const {
return this->data_index;
}
uint8_t Tournament::get_number() const {
return this->number;
}
@@ -498,7 +497,7 @@ const string& Tournament::get_name() const {
return this->name;
}
shared_ptr<const DataIndex::MapEntry> Tournament::get_map() const {
shared_ptr<const MapIndex::MapEntry> Tournament::get_map() const {
return this->map;
}
@@ -593,13 +592,13 @@ void Tournament::start() {
throw logic_error("non-human player on team before tournament start");
}
}
if (this->data_index->num_com_decks() < t->max_players - t->players.size()) {
if (this->com_deck_index->num_decks() < t->max_players - t->players.size()) {
throw runtime_error("not enough COM decks to complete team");
}
// TODO: Don't allow duplicate COM decks, nor duplicate COM SCs on the same
// team
while (t->players.size() < t->max_players) {
t->players.emplace_back(this->data_index->random_com_deck());
t->players.emplace_back(this->com_deck_index->random_deck());
}
}
@@ -654,10 +653,12 @@ void Tournament::print_bracket(FILE* stream) const {
}
TournamentIndex::TournamentIndex(
shared_ptr<const DataIndex> data_index,
shared_ptr<const MapIndex> map_index,
shared_ptr<const COMDeckIndex> com_deck_index,
const string& state_filename,
bool skip_load_state)
: data_index(data_index),
: map_index(map_index),
com_deck_index(com_deck_index),
state_filename(state_filename) {
if (this->state_filename.empty() || skip_load_state) {
return;
@@ -671,7 +672,7 @@ TournamentIndex::TournamentIndex(
}
for (size_t z = 0; z < 0x20; z++) {
if (!list.at(z)->is_null()) {
this->tournaments[z].reset(new Tournament(this->data_index, z, list[z]));
this->tournaments[z].reset(new Tournament(this->map_index, this->com_deck_index, z, list[z]));
this->tournaments[z]->init();
}
}
@@ -706,7 +707,7 @@ vector<shared_ptr<Tournament>> TournamentIndex::all_tournaments() const {
shared_ptr<Tournament> TournamentIndex::create_tournament(
const string& name,
shared_ptr<const DataIndex::MapEntry> map,
shared_ptr<const MapIndex::MapEntry> map,
const Rules& rules,
size_t num_teams,
bool is_2v2) {
@@ -722,7 +723,7 @@ shared_ptr<Tournament> TournamentIndex::create_tournament(
}
auto t = make_shared<Tournament>(
this->data_index, number, name, map, rules, num_teams, is_2v2);
this->map_index, this->com_deck_index, number, name, map, rules, num_teams, is_2v2);
t->init();
this->tournaments[number] = t;
return t;
+14 -10
View File
@@ -92,15 +92,17 @@ public:
};
Tournament(
std::shared_ptr<const DataIndex> data_index,
std::shared_ptr<const MapIndex> map_index,
std::shared_ptr<const COMDeckIndex> com_deck_index,
uint8_t number,
const std::string& name,
std::shared_ptr<const DataIndex::MapEntry> map,
std::shared_ptr<const MapIndex::MapEntry> map,
const Rules& rules,
size_t num_teams,
bool is_2v2);
Tournament(
std::shared_ptr<const DataIndex> data_index,
std::shared_ptr<const MapIndex> map_index,
std::shared_ptr<const COMDeckIndex> com_deck_index,
uint8_t number,
std::shared_ptr<const JSONObject> json);
~Tournament() = default;
@@ -108,10 +110,9 @@ public:
std::shared_ptr<JSONObject> json() const;
std::shared_ptr<const DataIndex> get_data_index() const;
uint8_t get_number() const;
const std::string& get_name() const;
std::shared_ptr<const DataIndex::MapEntry> get_map() const;
std::shared_ptr<const MapIndex::MapEntry> get_map() const;
const Rules& get_rules() const;
bool get_is_2v2() const;
State get_state() const;
@@ -131,11 +132,12 @@ public:
private:
PrefixedLogger log;
std::shared_ptr<const DataIndex> data_index;
std::shared_ptr<const MapIndex> map_index;
std::shared_ptr<const COMDeckIndex> com_deck_index;
std::shared_ptr<const JSONObject> source_json;
uint8_t number;
std::string name;
std::shared_ptr<const DataIndex::MapEntry> map;
std::shared_ptr<const MapIndex::MapEntry> map;
Rules rules;
size_t num_teams;
bool is_2v2;
@@ -160,7 +162,8 @@ private:
class TournamentIndex {
public:
explicit TournamentIndex(
std::shared_ptr<const DataIndex> data_index,
std::shared_ptr<const MapIndex> map_index,
std::shared_ptr<const COMDeckIndex> com_deck_index,
const std::string& state_filename,
bool skip_load_state = false);
~TournamentIndex() = default;
@@ -171,7 +174,7 @@ public:
std::shared_ptr<Tournament> create_tournament(
const std::string& name,
std::shared_ptr<const DataIndex::MapEntry> map,
std::shared_ptr<const MapIndex::MapEntry> map,
const Rules& rules,
size_t num_teams,
bool is_2v2);
@@ -183,7 +186,8 @@ public:
uint32_t serial_number) const;
private:
std::shared_ptr<const DataIndex> data_index;
std::shared_ptr<const MapIndex> map_index;
std::shared_ptr<const COMDeckIndex> com_deck_index;
std::string state_filename;
std::shared_ptr<Tournament> tournaments[0x20];
};
+13 -1
View File
@@ -195,7 +195,7 @@ FunctionCodeIndex::FunctionCodeIndex(const string& directory) {
isdigit(filename[filename.size() - 12]) &&
(filename[filename.size() - 11] == 'O' || filename[filename.size() - 11] == 'S') &&
(filename[filename.size() - 10] == 'E' || filename[filename.size() - 10] == 'J' || filename[filename.size() - 10] == 'P') &&
isdigit(filename[filename.size() - 9])) {
(isdigit(filename[filename.size() - 9]) || filename[filename.size() - 9] == 'T')) {
specific_version = 0x33000000 | (filename[filename.size() - 11] << 16) | (filename[filename.size() - 10] << 8) | filename[filename.size() - 9];
patch_name = filename.substr(0, filename.size() - 13);
}
@@ -375,6 +375,18 @@ uint32_t specific_version_for_gc_header_checksum(uint32_t header_checksum) {
}
}
}
{
// Generate entries for Trial Editions
data.region_code = 'J';
data.system_code = 'D';
data.version_code = 0;
uint32_t checksum = crc32(&data, sizeof(data));
uint32_t specific_version = 0x33004A54 | (*game_code2 << 16);
if (!checksum_to_specific_version.emplace(checksum, specific_version).second) {
throw logic_error("multiple specific_versions have same header checksum");
}
data.system_code = 'G';
}
}
}
return checksum_to_specific_version.at(header_checksum);
+1
View File
@@ -37,6 +37,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
SPECTATORS_FORBIDDEN = 0x00004000,
START_BATTLE_PLAYER_IMMEDIATELY = 0x00008000,
DROPS_ENABLED = 0x00010000, // Does not affect BB
IS_EP3_TRIAL = 0x00020000,
// Flags used only for lobbies
PUBLIC = 0x01000000,
+23 -22
View File
@@ -1478,32 +1478,33 @@ int main(int argc, char** argv) {
break;
}
case Behavior::SHOW_EP3_MAPS:
case Behavior::SHOW_EP3_CARDS: {
config_log.info("Collecting Episode 3 data");
Episode3::DataIndex index("system/ep3", Episode3::BehaviorFlag::LOAD_CARD_TEXT);
Episode3::CardIndex card_index("system/ep3/card-definitions.mnr", "system/ep3/card-definitions.mnrd", "system/ep3/card-text.mnr");
if (behavior == Behavior::SHOW_EP3_MAPS) {
auto map_ids = index.all_map_ids();
log_info("%zu maps", map_ids.size());
for (uint32_t map_id : map_ids) {
auto map = index.definition_for_map_number(map_id);
string s = map->map.str(&index);
fprintf(stdout, "%s\n", s.c_str());
}
} else {
auto card_ids = index.all_card_ids();
log_info("%zu card definitions", card_ids.size());
for (uint32_t card_id : card_ids) {
auto entry = index.definition_for_card_id(card_id);
string s = entry->def.str(false);
string tags = entry->debug_tags.empty() ? "(none)" : join(entry->debug_tags, ", ");
string text = entry->text.empty() ? "(No text available)" : str_replace_all(entry->text, "\n", "\n ");
fprintf(stdout, "%s\n Tags: %s\n Text:\n %s\n\n", s.c_str(), tags.c_str(), text.c_str());
}
auto card_ids = card_index.all_ids();
log_info("%zu card definitions", card_ids.size());
for (uint32_t card_id : card_ids) {
auto entry = card_index.definition_for_id(card_id);
string s = entry->def.str(false);
string tags = entry->debug_tags.empty() ? "(none)" : join(entry->debug_tags, ", ");
string text = entry->text.empty() ? "(No text available)" : str_replace_all(entry->text, "\n", "\n ");
fprintf(stdout, "%s\n Tags: %s\n Text:\n %s\n\n", s.c_str(), tags.c_str(), text.c_str());
}
break;
}
case Behavior::SHOW_EP3_MAPS: {
config_log.info("Collecting Episode 3 data");
Episode3::MapIndex map_index("system/ep3");
Episode3::CardIndex card_index("system/ep3/card-definitions.mnr", "system/ep3/card-definitions.mnrd", "system/ep3/card-text.mnr");
auto map_ids = map_index.all_numbers();
log_info("%zu maps", map_ids.size());
for (uint32_t map_id : map_ids) {
auto map = map_index.definition_for_number(map_id);
string s = map->map.str(&card_index);
fprintf(stdout, "%s\n", s.c_str());
}
break;
}
+1 -1
View File
@@ -9,7 +9,7 @@
#include <utility>
#include <vector>
#include "Episode3/DataIndex.hh"
#include "Episode3/DataIndexes.hh"
#include "ItemData.hh"
#include "LevelTable.hh"
#include "Text.hh"
+24 -10
View File
@@ -303,7 +303,7 @@ static void set_console_client_flags(
c->log.info("Game version changed to DC");
} else if (c->version() == GameVersion::GC) {
c->flags |= Client::Flag::IS_GC_TRIAL_EDITION;
c->log.info("Trial edition flag set");
c->log.info("GC Trial Edition flag set");
}
}
c->flags |= flags_for_version(c->version(), sub_version);
@@ -930,7 +930,7 @@ static bool add_next_game_client(
state_cmd.state.first_team_turn = 0xFF;
state_cmd.state.tournament_flag = 0x01;
state_cmd.state.client_sc_card_types.clear(Episode3::CardType::INVALID_FF);
if (!(s->ep3_data_index->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
set_mask_for_ep3_game_command(&state_cmd, sizeof(state_cmd), mask_key);
}
@@ -958,7 +958,7 @@ static bool add_next_game_client(
ex_cmd.lose_entries[z].threshold = lose_entries[z].first;
ex_cmd.lose_entries[z].value = lose_entries[z].second;
}
if (!(s->ep3_data_index->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
set_mask_for_ep3_game_command(&ex_cmd, sizeof(ex_cmd), mask_key);
}
@@ -1239,7 +1239,7 @@ static void on_DC_Ep3(shared_ptr<ServerState> s, shared_ptr<Client> c,
if (l->tournament_match) {
auto tourn = l->tournament_match->tournament.lock();
if (tourn) {
send_ep3_set_tournament_player_decks(l, c, l->tournament_match);
send_ep3_set_tournament_player_decks(s, l, c, l->tournament_match);
string data = Episode3::Server::prepare_6xB6x41_map_definition(
tourn->get_map());
c->channel.send(0x6C, 0x00, data);
@@ -1312,8 +1312,14 @@ static void on_CA_Ep3(shared_ptr<ServerState> s, shared_ptr<Client> c,
l->log.info("Recreating Episode 3 server state");
}
auto tourn = l->tournament_match ? l->tournament_match->tournament.lock() : nullptr;
bool is_trial = (l->flags & Lobby::Flag::IS_EP3_TRIAL);
l->ep3_server_base = make_shared<Episode3::ServerBase>(
l, s->ep3_data_index, l->random_crypt, tourn ? tourn->get_map() : nullptr);
l,
is_trial ? s->ep3_card_index_trial : s->ep3_card_index,
s->ep3_map_index,
s->ep3_behavior_flags,
l->random_crypt,
tourn ? tourn->get_map() : nullptr);
l->ep3_server_base->init();
if (s->ep3_behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES) {
@@ -1366,7 +1372,7 @@ static void on_CA_Ep3(shared_ptr<ServerState> s, shared_ptr<Client> c,
} else {
throw logic_error("invalid winner team id");
}
send_ep3_tournament_match_result(l, l->tournament_match);
send_ep3_tournament_match_result(s, l, l->tournament_match);
on_tournament_bracket_updated(s, tourn);
l->ep3_server_base->server->tournament_match_result_sent = true;
@@ -1866,6 +1872,7 @@ static void on_10(shared_ptr<ServerState> s, shared_ptr<Client> c,
}
if ((game->version != c->version()) ||
(!game->is_ep3() != !(c->flags & Client::Flag::IS_EPISODE_3)) ||
(!(game->flags & Lobby::Flag::IS_EP3_TRIAL) != !(c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)) ||
((game->flags & Lobby::Flag::NON_V1_ONLY) && (c->flags & Client::Flag::IS_DC_V1))) {
send_lobby_message_box(c, u"$C6You cannot join this\ngame because it is\nfor a different\nversion of PSO.");
break;
@@ -2141,6 +2148,11 @@ static void on_84(shared_ptr<ServerState> s, shared_ptr<Client> c,
return;
}
if (new_lobby->is_game()) {
send_lobby_message_box(c, u"$C6Can't change lobby\n\n$C7The specified lobby\nis a game.");
return;
}
if (new_lobby->is_ep3() && !(c->flags & Client::Flag::IS_EPISODE_3)) {
send_lobby_message_box(c, u"$C6Can't change lobby\n\n$C7The lobby is for\nEpisode 3 only.");
return;
@@ -3203,10 +3215,11 @@ shared_ptr<Lobby> create_game_generic(
(c->version() == GameVersion::BB) ||
(s->item_tracking_enabled && (mode == GameMode::NORMAL || mode == GameMode::SOLO));
// only disable drops if the config flag is set and are playing regualr multi-mode.
// drops are still enabled for battle and challenge modes.
bool drops_enabled =
(s->drops_enabled || (mode != GameMode::NORMAL));
// Only disable drops if the config flag is set and are playing regular
// multi-mode. Drops are still enabled for battle and challenge modes.
bool drops_enabled = (s->drops_enabled || (mode != GameMode::NORMAL));
bool is_ep3_trial = (c->version() == GameVersion::GC) && (c->flags & Client::Flag::IS_EPISODE_3) && (c->flags & Client::Flag::IS_EP3_TRIAL_EDITION);
shared_ptr<Lobby> game = s->create_lobby();
game->name = name;
@@ -3214,6 +3227,7 @@ shared_ptr<Lobby> create_game_generic(
Lobby::Flag::GAME |
(item_tracking_enabled ? Lobby::Flag::ITEM_TRACKING_ENABLED : 0) |
(drops_enabled ? Lobby::Flag::DROPS_ENABLED : 0) |
(is_ep3_trial ? Lobby::Flag::IS_EP3_TRIAL : 0) |
((s->cheat_mode_behavior == ServerState::CheatModeBehavior::ON_BY_DEFAULT) ? Lobby::Flag::CHEATS_ENABLED : 0);
game->password = password;
game->version = c->version();
+1 -1
View File
@@ -380,7 +380,7 @@ static void on_ep3_battle_subs(shared_ptr<ServerState> s,
}
}
if (!(s->ep3_data_index->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
uint8_t mask_key = 0;
while (!mask_key) {
mask_key = random_object<uint8_t>();
+13 -6
View File
@@ -2180,7 +2180,9 @@ void send_rare_enemy_index_list(shared_ptr<Client> c, const vector<size_t>& inde
void send_ep3_card_list_update(shared_ptr<ServerState> s, shared_ptr<Client> c) {
if (!(c->flags & Client::Flag::HAS_EP3_CARD_DEFS)) {
const auto& data = s->ep3_data_index->get_compressed_card_definitions();
const auto& data = (c->flags & Client::Flag::IS_EP3_TRIAL_EDITION)
? s->ep3_card_index_trial->get_compressed_definitions()
: s->ep3_card_index->get_compressed_definitions();
StringWriter w;
w.put_u32l(data.size());
@@ -2486,6 +2488,7 @@ void send_ep3_game_details(shared_ptr<Client> c, shared_ptr<Lobby> l) {
}
void send_ep3_set_tournament_player_decks(
shared_ptr<ServerState> s,
shared_ptr<Lobby> l,
shared_ptr<Client> c,
shared_ptr<const Episode3::Tournament::Match> match) {
@@ -2532,7 +2535,7 @@ void send_ep3_set_tournament_player_decks(
add_entries_for_team(match->preceding_a->winner_team, 0);
add_entries_for_team(match->preceding_b->winner_team, 2);
if (!(tourn->get_data_index()->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
set_mask_for_ep3_game_command(&cmd, sizeof(cmd), mask_key);
}
@@ -2543,7 +2546,7 @@ void send_ep3_set_tournament_player_decks(
}
void send_ep3_tournament_match_result(
shared_ptr<Lobby> l, shared_ptr<const Episode3::Tournament::Match> match) {
shared_ptr<ServerState> s, shared_ptr<Lobby> l, shared_ptr<const Episode3::Tournament::Match> match) {
auto tourn = match->tournament.lock();
if (!tourn) {
return;
@@ -2585,13 +2588,13 @@ void send_ep3_tournament_match_result(
// the player 1000000 and never charge for anything.
cmd.meseta_amount = 100;
cmd.meseta_reward_text = "You got %s meseta!";
if (!(tourn->get_data_index()->behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
if (!(s->ep3_behavior_flags & Episode3::BehaviorFlag::DISABLE_MASKING)) {
uint8_t mask_key = (random_object<uint32_t>() % 0xFF) + 1;
set_mask_for_ep3_game_command(&cmd, sizeof(cmd), mask_key);
}
send_command_t(l, 0xC9, 0x00, cmd);
if (tourn->get_data_index()->behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES) {
if (s->ep3_behavior_flags & Episode3::BehaviorFlag::ENABLE_STATUS_MESSAGES) {
send_text_message_printf(l, "$C5TOURN/%02hhX/%zu WIN %c",
tourn->get_number(), match->round_num,
match->winner_team == match->preceding_a->winner_team ? 'A' : 'B');
@@ -2806,6 +2809,10 @@ void send_card_auction_if_all_clients_ready(
distribution_size += it.second.first;
}
auto card_index = (l->flags & Lobby::Flag::IS_EP3_TRIAL)
? s->ep3_card_index_trial
: s->ep3_card_index;
S_StartCardAuction_GC_Ep3_EF cmd;
cmd.points_available = s->ep3_card_auction_points;
for (size_t z = 0; z < num_cards; z++) {
@@ -2814,7 +2821,7 @@ void send_card_auction_if_all_clients_ready(
if (v >= it.second.first) {
v -= it.second.first;
} else {
cmd.entries[z].card_id = s->ep3_data_index->definition_for_card_name(it.first)->def.card_id.load();
cmd.entries[z].card_id = card_index->definition_for_name(it.first)->def.card_id.load();
cmd.entries[z].min_price = it.second.second;
break;
}
+2
View File
@@ -352,10 +352,12 @@ void send_ep3_tournament_info(
std::shared_ptr<Client> c,
std::shared_ptr<const Episode3::Tournament> t);
void send_ep3_set_tournament_player_decks(
std::shared_ptr<ServerState> s,
std::shared_ptr<Lobby> l,
std::shared_ptr<Client> c,
std::shared_ptr<const Episode3::Tournament::Match> match);
void send_ep3_tournament_match_result(
std::shared_ptr<ServerState> s,
std::shared_ptr<Lobby> l,
std::shared_ptr<const Episode3::Tournament::Match> match);
+1 -1
View File
@@ -451,7 +451,7 @@ Proxy session commands:\n\
} else if (command_name == "create-tournament") {
string name = get_quoted_string(command_args);
string map_name = get_quoted_string(command_args);
auto map = this->state->ep3_data_index->definition_for_map_name(map_name);
auto map = this->state->ep3_map_index->definition_for_name(map_name);
uint32_t num_teams = stoul(get_quoted_string(command_args), nullptr, 0);
Episode3::Rules rules;
rules.set_defaults();
+10 -5
View File
@@ -888,19 +888,24 @@ void ServerState::load_item_tables() {
}
void ServerState::load_ep3_data() {
config_log.info("Collecting Episode 3 data");
this->ep3_data_index.reset(new Episode3::DataIndex(
"system/ep3", this->ep3_behavior_flags));
config_log.info("Collecting Episode 3 maps");
this->ep3_map_index.reset(new Episode3::MapIndex("system/ep3"));
config_log.info("Loading Episode 3 card definitions");
this->ep3_card_index.reset(new Episode3::CardIndex("system/ep3/card-definitions.mnr", "system/ep3/card-definitions.mnrd", "system/ep3/card-text.mnr"));
config_log.info("Loading Episode 3 trial card definitions");
this->ep3_card_index_trial.reset(new Episode3::CardIndex("system/ep3/card-definitions-trial.mnr", "system/ep3/card-definitions-trial.mnrd", "system/ep3/card-text-trial.mnr"));
config_log.info("Loading Episode 3 COM decks");
this->ep3_com_deck_index.reset(new Episode3::COMDeckIndex("system/ep3/com-decks.json"));
const string& tournament_state_filename = "system/ep3/tournament-state.json";
try {
this->ep3_tournament_index.reset(new Episode3::TournamentIndex(
this->ep3_data_index, tournament_state_filename));
this->ep3_map_index, this->ep3_com_deck_index, tournament_state_filename));
config_log.info("Loaded Episode 3 tournament state");
} catch (const exception& e) {
config_log.warning("Cannot load Episode 3 tournament state: %s", e.what());
this->ep3_tournament_index.reset(new Episode3::TournamentIndex(
this->ep3_data_index, tournament_state_filename, true));
this->ep3_map_index, this->ep3_com_deck_index, tournament_state_filename, true));
}
}
+5 -2
View File
@@ -11,7 +11,7 @@
#include "Client.hh"
#include "CommonItemSet.hh"
#include "Episode3/DataIndex.hh"
#include "Episode3/DataIndexes.hh"
#include "Episode3/Tournament.hh"
#include "FunctionCompiler.hh"
#include "GSLArchive.hh"
@@ -70,7 +70,10 @@ struct ServerState {
std::shared_ptr<const PatchFileIndex> pc_patch_file_index;
std::shared_ptr<const PatchFileIndex> bb_patch_file_index;
std::shared_ptr<const DOLFileIndex> dol_file_index;
std::shared_ptr<const Episode3::DataIndex> ep3_data_index;
std::shared_ptr<const Episode3::CardIndex> ep3_card_index;
std::shared_ptr<const Episode3::CardIndex> ep3_card_index_trial;
std::shared_ptr<const Episode3::MapIndex> ep3_map_index;
std::shared_ptr<const Episode3::COMDeckIndex> ep3_com_deck_index;
std::shared_ptr<const QuestCategoryIndex> quest_category_index;
std::shared_ptr<const QuestIndex> quest_index;
std::shared_ptr<const LevelTable> level_table;
-1
View File
@@ -87,7 +87,6 @@ uint32_t flags_for_version(GameVersion version, int64_t sub_version) {
return Client::Flag::NO_D6_AFTER_LOBBY |
Client::Flag::IS_EPISODE_3 |
Client::Flag::IS_EP3_TRIAL_EDITION |
Client::Flag::ENCRYPTED_SEND_FUNCTION_CALL |
Client::Flag::SEND_FUNCTION_CALL_NO_CACHE_PATCH;
case 0x42: // GC Ep3 JP
return Client::Flag::NO_D6_AFTER_LOBBY |