From 8656222be375023f31149fd4d538ab6850028494 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Mon, 26 Jun 2023 21:25:24 -0700 Subject: [PATCH] split disp data into stats and visual substructures --- src/ChatCommands.cc | 32 +-- src/CommandFormats.hh | 34 +--- src/Episode3/PlayerState.hh | 2 +- src/Episode3/PlayerStateSubordinates.cc | 14 +- src/Episode3/PlayerStateSubordinates.hh | 4 +- src/Items.cc | 56 +++--- src/LevelTable.cc | 4 +- src/LevelTable.hh | 10 +- src/Player.cc | 255 ++++++------------------ src/Player.hh | 125 +++++------- src/ProxyCommands.cc | 36 ++-- src/ReceiveCommands.cc | 14 +- src/ReceiveSubcommands.cc | 52 ++--- src/SendCommands.cc | 26 +-- 14 files changed, 237 insertions(+), 427 deletions(-) diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 840689de..a3164fc2 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -735,51 +735,51 @@ static void server_command_edit(shared_ptr s, shared_ptr l, try { if (tokens.at(0) == "atp") { - c->game_data.player()->disp.stats.atp = stoul(tokens.at(1)); + c->game_data.player()->disp.stats.char_stats.atp = stoul(tokens.at(1)); } else if (tokens.at(0) == "mst") { - c->game_data.player()->disp.stats.mst = stoul(tokens.at(1)); + c->game_data.player()->disp.stats.char_stats.mst = stoul(tokens.at(1)); } else if (tokens.at(0) == "evp") { - c->game_data.player()->disp.stats.evp = stoul(tokens.at(1)); + c->game_data.player()->disp.stats.char_stats.evp = stoul(tokens.at(1)); } else if (tokens.at(0) == "hp") { - c->game_data.player()->disp.stats.hp = stoul(tokens.at(1)); + c->game_data.player()->disp.stats.char_stats.hp = stoul(tokens.at(1)); } else if (tokens.at(0) == "dfp") { - c->game_data.player()->disp.stats.dfp = stoul(tokens.at(1)); + c->game_data.player()->disp.stats.char_stats.dfp = stoul(tokens.at(1)); } else if (tokens.at(0) == "ata") { - c->game_data.player()->disp.stats.ata = stoul(tokens.at(1)); + c->game_data.player()->disp.stats.char_stats.ata = stoul(tokens.at(1)); } else if (tokens.at(0) == "lck") { - c->game_data.player()->disp.stats.lck = stoul(tokens.at(1)); + c->game_data.player()->disp.stats.char_stats.lck = stoul(tokens.at(1)); } else if (tokens.at(0) == "meseta") { - c->game_data.player()->disp.meseta = stoul(tokens.at(1)); + c->game_data.player()->disp.stats.meseta = stoul(tokens.at(1)); } else if (tokens.at(0) == "exp") { - c->game_data.player()->disp.experience = stoul(tokens.at(1)); + c->game_data.player()->disp.stats.experience = stoul(tokens.at(1)); } else if (tokens.at(0) == "level") { - c->game_data.player()->disp.level = stoul(tokens.at(1)) - 1; + c->game_data.player()->disp.stats.level = stoul(tokens.at(1)) - 1; } else if (tokens.at(0) == "namecolor") { uint32_t new_color; sscanf(tokens.at(1).c_str(), "%8X", &new_color); - c->game_data.player()->disp.name_color = new_color; + c->game_data.player()->disp.visual.name_color = new_color; } else if (tokens.at(0) == "secid") { uint8_t secid = section_id_for_name(decode_sjis(tokens.at(1))); if (secid == 0xFF) { send_text_message(c, u"$C6No such section ID"); return; } else { - c->game_data.player()->disp.section_id = secid; + c->game_data.player()->disp.visual.section_id = secid; } } else if (tokens.at(0) == "name") { c->game_data.player()->disp.name = add_language_marker(tokens.at(1), 'J'); } else if (tokens.at(0) == "npc") { if (tokens.at(1) == "none") { - c->game_data.player()->disp.extra_model = 0; - c->game_data.player()->disp.v2_flags &= 0xFD; + c->game_data.player()->disp.visual.extra_model = 0; + c->game_data.player()->disp.visual.v2_flags &= 0xFD; } else { uint8_t npc = npc_for_name(decode_sjis(tokens.at(1))); if (npc == 0xFF) { send_text_message(c, u"$C6No such NPC"); return; } - c->game_data.player()->disp.extra_model = npc; - c->game_data.player()->disp.v2_flags |= 0x02; + c->game_data.player()->disp.visual.extra_model = npc; + c->game_data.player()->disp.visual.v2_flags |= 0x02; } } else if (tokens.at(0) == "tech") { uint8_t level = stoul(tokens.at(2)) - 1; diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 7d1db738..08a9d912 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -4444,36 +4444,8 @@ struct G_SyncPlayerDispAndInventory_V3_6x70 { /* 00B8 */ le_uint32_t unknown_a10; /* 00BC */ le_uint32_t unknown_a11; /* 00C0 */ parray technique_levels; // Last byte is uninitialized - /* 00D4 */ struct { - parray name; - uint64_t unknown_a2; // Same as unknown_a2 in PlayerDispDataDCPCV3, presumably - le_uint32_t name_color; - uint8_t extra_model; - parray unused; - le_uint32_t name_color_checksum; - uint8_t section_id; - uint8_t char_class; - uint8_t v2_flags; - uint8_t version; - le_uint32_t v1_flags; - le_uint16_t costume; - le_uint16_t skin; - le_uint16_t face; - le_uint16_t head; - le_uint16_t hair; - le_uint16_t hair_r; - le_uint16_t hair_g; - le_uint16_t hair_b; - le_uint32_t proportion_x; - le_uint32_t proportion_y; - } __packed__ disp_part2; - /* 0124 */ struct { - PlayerStats stats; - parray unknown_a1; - le_uint32_t level; - le_uint32_t experience; - le_uint32_t meseta; - } __packed__ disp_part1; + /* 00D4 */ PlayerVisualConfig visual; + /* 0124 */ PlayerStats stats; /* 0148 */ struct { le_uint32_t num_items; // Entries >= num_items in this array contain uninitialized data (usually @@ -6110,7 +6082,7 @@ struct G_CardCountsRequest_GC_Ep3_6xB5x38 { struct G_UpdateAllPlayerStatistics_GC_Ep3_6xB4x39 { G_CardBattleCommandHeader header = {0xB4, sizeof(G_UpdateAllPlayerStatistics_GC_Ep3_6xB4x39) / 4, 0, 0x39, 0, 0, 0}; - parray stats; + parray stats; } __packed__; // 6xB3x3A / CAx3A: Unknown diff --git a/src/Episode3/PlayerState.hh b/src/Episode3/PlayerState.hh index bb37fac5..7d1a123f 100644 --- a/src/Episode3/PlayerState.hh +++ b/src/Episode3/PlayerState.hh @@ -182,7 +182,7 @@ public: uint32_t num_destroyed_fcs; uint8_t unknown_a16; uint8_t unknown_a17; - PlayerStats stats; + PlayerBattleStats stats; }; } // namespace Episode3 diff --git a/src/Episode3/PlayerStateSubordinates.cc b/src/Episode3/PlayerStateSubordinates.cc index 887db429..d872aa76 100644 --- a/src/Episode3/PlayerStateSubordinates.cc +++ b/src/Episode3/PlayerStateSubordinates.cc @@ -688,11 +688,11 @@ void HandAndEquipState::clear_FF() { this->unused2.clear(0xFF); } -PlayerStats::PlayerStats() { +PlayerBattleStats::PlayerBattleStats() { this->clear(); } -void PlayerStats::clear() { +void PlayerBattleStats::clear() { this->damage_given = 0; this->damage_taken = 0; this->num_opponent_cards_destroyed = 0; @@ -715,7 +715,7 @@ void PlayerStats::clear() { this->unused = 0; } -float PlayerStats::score(size_t num_rounds) const { +float PlayerBattleStats::score(size_t num_rounds) const { // Note: This formula doesn't match the formula on PSO-World, which is: // 35 // + (Attack Damage - Damage Taken) @@ -730,11 +730,11 @@ float PlayerStats::score(size_t num_rounds) const { return 38.0f + 0.8f * this->action_card_negated_damage - 2.3f * num_rounds - 1.8f * this->sc_damage_taken + 3.0f * this->max_attack_combo_size + (this->damage_given - this->damage_taken); } -uint8_t PlayerStats::rank(size_t num_rounds) const { +uint8_t PlayerBattleStats::rank(size_t num_rounds) const { return this->rank_for_score(this->score(num_rounds)); } -const char* PlayerStats::rank_name(size_t num_rounds) const { +const char* PlayerBattleStats::rank_name(size_t num_rounds) const { return this->name_for_rank(this->rank_for_score(this->score(num_rounds))); } @@ -744,7 +744,7 @@ static const float RANK_THRESHOLDS[RANK_THRESHOLD_COUNT] = { static const char* RANK_NAMES[RANK_THRESHOLD_COUNT + 1] = { "E", "D", "D+", "C", "C+", "B", "B+", "A", "A+", "S"}; -uint8_t PlayerStats::rank_for_score(float score) { +uint8_t PlayerBattleStats::rank_for_score(float score) { size_t rank = 0; while (rank < RANK_THRESHOLD_COUNT && RANK_THRESHOLDS[rank] <= score) { rank++; @@ -752,7 +752,7 @@ uint8_t PlayerStats::rank_for_score(float score) { return rank; } -const char* PlayerStats::name_for_rank(uint8_t rank) { +const char* PlayerBattleStats::name_for_rank(uint8_t rank) { if (rank >= RANK_THRESHOLD_COUNT + 1) { throw invalid_argument("invalid rank"); } diff --git a/src/Episode3/PlayerStateSubordinates.hh b/src/Episode3/PlayerStateSubordinates.hh index f5403743..f2bf8a5c 100644 --- a/src/Episode3/PlayerStateSubordinates.hh +++ b/src/Episode3/PlayerStateSubordinates.hh @@ -234,7 +234,7 @@ struct HandAndEquipState { std::string str() const; } __attribute__((packed)); -struct PlayerStats { +struct PlayerBattleStats { le_uint16_t damage_given; le_uint16_t damage_taken; le_uint16_t num_opponent_cards_destroyed; @@ -256,7 +256,7 @@ struct PlayerStats { le_uint16_t action_card_negated_damage; le_uint16_t unused; - PlayerStats(); + PlayerBattleStats(); void clear(); float score(size_t num_rounds) const; diff --git a/src/Items.cc b/src/Items.cc index 60e97b0a..fdfa8cb7 100644 --- a/src/Items.cc +++ b/src/Items.cc @@ -22,7 +22,7 @@ void player_use_item(shared_ptr s, shared_ptr c, size_t ite // Nothing to do (it should be deleted) } else if (item_identifier == 0x030200) { // Technique disk - uint8_t max_level = s->item_parameter_table->get_max_tech_level(player->disp.char_class, item.data.data1[4]); + uint8_t max_level = s->item_parameter_table->get_max_tech_level(player->disp.visual.char_class, item.data.data1[4]); if (item.data.data1[2] > max_level) { throw runtime_error("technique level too high"); } @@ -43,13 +43,13 @@ void player_use_item(shared_ptr s, shared_ptr c, size_t ite } else if ((item_identifier & 0xFFFF00) == 0x030B00) { // Material switch (item.data.data1[2]) { case 0: // Power Material - c->game_data.player()->disp.stats.atp += 2; + c->game_data.player()->disp.stats.char_stats.atp += 2; break; case 1: // Mind Material - c->game_data.player()->disp.stats.mst += 2; + c->game_data.player()->disp.stats.char_stats.mst += 2; break; case 2: // Evade Material - c->game_data.player()->disp.stats.evp += 2; + c->game_data.player()->disp.stats.char_stats.evp += 2; break; case 3: // HP Material c->game_data.player()->inventory.hp_materials_used += 2; @@ -58,10 +58,10 @@ void player_use_item(shared_ptr s, shared_ptr c, size_t ite c->game_data.player()->inventory.tp_materials_used += 2; break; case 5: // Def Material - c->game_data.player()->disp.stats.dfp += 2; + c->game_data.player()->disp.stats.char_stats.dfp += 2; break; case 6: // Luck Material - c->game_data.player()->disp.stats.lck += 2; + c->game_data.player()->disp.stats.char_stats.lck += 2; break; default: throw invalid_argument("unknown material used"); @@ -102,12 +102,12 @@ void player_use_item(shared_ptr s, shared_ptr c, size_t ite } else if (item_identifier == 0x030C00) { // Cell of MAG 502 auto& mag = player->inventory.items[player->inventory.find_equipped_mag()]; - mag.data.data1[1] = (player->disp.section_id & 1) ? 0x1D : 0x21; + mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x1D : 0x21; } else if (item_identifier == 0x030C01) { // Cell of MAG 213 auto& mag = player->inventory.items[player->inventory.find_equipped_mag()]; - mag.data.data1[1] = (player->disp.section_id & 1) ? 0x27 : 0x22; + mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x27 : 0x22; } else if (item_identifier == 0x030C02) { // Parts of RoboChao @@ -169,7 +169,7 @@ void player_use_item(shared_ptr s, shared_ptr c, size_t ite try { const auto& combo = s->item_parameter_table->get_item_combination( item.data, inv_item.data); - if (combo.char_class != 0xFF && combo.char_class != player->disp.char_class) { + if (combo.char_class != 0xFF && combo.char_class != player->disp.visual.char_class) { throw runtime_error("item combination requires specific char_class"); } if (combo.mag_level != 0xFF) { @@ -188,7 +188,7 @@ void player_use_item(shared_ptr s, shared_ptr c, size_t ite throw runtime_error("item combination applies with grind requirement, but equipped weapon grind is too low"); } } - if (combo.level != 0xFF && player->disp.level + 1 < combo.level) { + if (combo.level != 0xFF && player->disp.stats.level + 1 < combo.level) { throw runtime_error("item combination applies with level requirement, but player level is too low"); } // If we get here, then the combo applies @@ -275,7 +275,7 @@ void player_feed_mag(std::shared_ptr s, std::shared_ptr c, } else if (mag_level < 35) { // Level 10 evolution if (evolution_number < 1) { - switch (player->disp.char_class) { + switch (player->disp.visual.char_class) { case 0: // HUmar case 1: // HUnewearl case 2: // HUcast @@ -333,17 +333,17 @@ void player_feed_mag(std::shared_ptr s, std::shared_ptr c, if (evolution_number < 4) { if (mag_level >= 100) { - uint8_t section_id_group = player->disp.section_id % 3; + uint8_t section_id_group = player->disp.visual.section_id % 3; uint16_t def = mag_item.data.data1w[2] / 100; uint16_t pow = mag_item.data.data1w[3] / 100; uint16_t dex = mag_item.data.data1w[4] / 100; uint16_t mind = mag_item.data.data1w[5] / 100; - bool is_male = char_class_is_male(player->disp.char_class); + bool is_male = char_class_is_male(player->disp.visual.char_class); size_t table_index = (is_male ? 0 : 1) + section_id_group * 2; - bool is_hunter = char_class_is_hunter(player->disp.char_class); - bool is_ranger = char_class_is_ranger(player->disp.char_class); - bool is_force = char_class_is_force(player->disp.char_class); + bool is_hunter = char_class_is_hunter(player->disp.visual.char_class); + bool is_ranger = char_class_is_ranger(player->disp.visual.char_class); + bool is_force = char_class_is_force(player->disp.visual.char_class); if (is_force) { table_index += 12; } else if (is_ranger) { @@ -378,45 +378,45 @@ void player_feed_mag(std::shared_ptr s, std::shared_ptr c, uint16_t dex = mag_item.data.data1w[4] / 100; uint16_t mind = mag_item.data.data1w[5] / 100; - bool is_hunter = char_class_is_hunter(player->disp.char_class); - bool is_ranger = char_class_is_ranger(player->disp.char_class); - bool is_force = char_class_is_force(player->disp.char_class); + bool is_hunter = char_class_is_hunter(player->disp.visual.char_class); + bool is_ranger = char_class_is_ranger(player->disp.visual.char_class); + bool is_force = char_class_is_force(player->disp.visual.char_class); if (is_hunter + is_ranger + is_force != 1) { throw logic_error("char class is not exactly one of the top-level classes"); } if (is_hunter) { if (flags & 0x108) { - mag_item.data.data1[1] = (player->disp.section_id & 1) + mag_item.data.data1[1] = (player->disp.visual.section_id & 1) ? ((dex < mind) ? 0x08 : 0x06) : ((dex < mind) ? 0x0C : 0x05); } else if (flags & 0x010) { - mag_item.data.data1[1] = (player->disp.section_id & 1) + mag_item.data.data1[1] = (player->disp.visual.section_id & 1) ? ((mind < pow) ? 0x12 : 0x10) : ((mind < pow) ? 0x17 : 0x13); } else if (flags & 0x020) { - mag_item.data.data1[1] = (player->disp.section_id & 1) + mag_item.data.data1[1] = (player->disp.visual.section_id & 1) ? ((pow < dex) ? 0x16 : 0x24) : ((pow < dex) ? 0x07 : 0x1E); } } else if (is_ranger) { if (flags & 0x110) { - mag_item.data.data1[1] = (player->disp.section_id & 1) + mag_item.data.data1[1] = (player->disp.visual.section_id & 1) ? ((mind < pow) ? 0x0A : 0x05) : ((mind < pow) ? 0x0C : 0x06); } else if (flags & 0x008) { - mag_item.data.data1[1] = (player->disp.section_id & 1) + mag_item.data.data1[1] = (player->disp.visual.section_id & 1) ? ((dex < mind) ? 0x0A : 0x26) : ((dex < mind) ? 0x0C : 0x06); } else if (flags & 0x020) { - mag_item.data.data1[1] = (player->disp.section_id & 1) + mag_item.data.data1[1] = (player->disp.visual.section_id & 1) ? ((pow < dex) ? 0x18 : 0x1E) : ((pow < dex) ? 0x08 : 0x05); } } else if (is_force) { if (flags & 0x120) { if (def < 45) { - mag_item.data.data1[1] = (player->disp.section_id & 1) + mag_item.data.data1[1] = (player->disp.visual.section_id & 1) ? ((pow < dex) ? 0x17 : 0x09) : ((pow < dex) ? 0x1E : 0x1C); } else { @@ -424,7 +424,7 @@ void player_feed_mag(std::shared_ptr s, std::shared_ptr c, } } else if (flags & 0x008) { if (def < 45) { - mag_item.data.data1[1] = (player->disp.section_id & 1) + mag_item.data.data1[1] = (player->disp.visual.section_id & 1) ? ((dex < mind) ? 0x1C : 0x20) : ((dex < mind) ? 0x1F : 0x25); } else { @@ -432,7 +432,7 @@ void player_feed_mag(std::shared_ptr s, std::shared_ptr c, } } else if (flags & 0x010) { if (def < 45) { - mag_item.data.data1[1] = (player->disp.section_id & 1) + mag_item.data.data1[1] = (player->disp.visual.section_id & 1) ? ((mind < pow) ? 0x12 : 0x0C) : ((mind < pow) ? 0x15 : 0x11); } else { diff --git a/src/LevelTable.cc b/src/LevelTable.cc index 2399b886..b5ca1cb4 100644 --- a/src/LevelTable.cc +++ b/src/LevelTable.cc @@ -21,7 +21,7 @@ LevelTable::LevelTable(shared_ptr data, bool compressed) { this->table = reinterpret_cast(this->data->data()); } -const PlayerStats& LevelTable::base_stats_for_class(uint8_t char_class) const { +const CharacterStats& LevelTable::base_stats_for_class(uint8_t char_class) const { if (char_class >= 12) { throw out_of_range("invalid character class"); } @@ -39,7 +39,7 @@ const LevelTable::LevelStats& LevelTable::stats_for_level( return this->table->levels[char_class][level]; } -void LevelTable::LevelStats::apply(PlayerStats& ps) const { +void LevelTable::LevelStats::apply(CharacterStats& ps) const { ps.ata += this->ata; ps.atp += this->atp; ps.dfp += this->dfp; diff --git a/src/LevelTable.hh b/src/LevelTable.hh index 19862c9e..b8c265e8 100644 --- a/src/LevelTable.hh +++ b/src/LevelTable.hh @@ -6,7 +6,7 @@ #include #include -struct PlayerStats { +struct CharacterStats { le_uint16_t atp; le_uint16_t mst; le_uint16_t evp; @@ -15,7 +15,7 @@ struct PlayerStats { le_uint16_t ata; le_uint16_t lck; - PlayerStats() noexcept; + CharacterStats() noexcept; } __attribute__((packed)); class LevelTable { // from PlyLevelTbl.prs @@ -31,18 +31,18 @@ public: uint8_t tp; le_uint32_t experience; - void apply(PlayerStats& ps) const; + void apply(CharacterStats& ps) const; } __attribute__((packed)); struct Table { - PlayerStats base_stats[12]; + CharacterStats base_stats[12]; le_uint32_t unknown[12]; LevelStats levels[12][200]; } __attribute__((packed)); LevelTable(std::shared_ptr data, bool compressed); - const PlayerStats& base_stats_for_class(uint8_t char_class) const; + const CharacterStats& base_stats_for_class(uint8_t char_class) const; const LevelStats& stats_for_level(uint8_t char_class, uint8_t level) const; private: diff --git a/src/Player.cc b/src/Player.cc index 71095433..094961dd 100644 --- a/src/Player.cc +++ b/src/Player.cc @@ -27,7 +27,7 @@ static const string ACCOUNT_FILE_SIGNATURE = static FileContentsCache player_files_cache(300 * 1000 * 1000); -PlayerStats::PlayerStats() noexcept +CharacterStats::CharacterStats() noexcept : atp(0), mst(0), evp(0), @@ -36,11 +36,13 @@ PlayerStats::PlayerStats() noexcept ata(0), lck(0) {} -PlayerDispDataDCPCV3::PlayerDispDataDCPCV3() noexcept +PlayerStats::PlayerStats() noexcept : level(0), experience(0), - meseta(0), - unknown_a2(0), + meseta(0) {} + +PlayerVisualConfig::PlayerVisualConfig() noexcept + : unknown_a2(0), name_color(0), extra_model(0), name_color_checksum(0), @@ -62,121 +64,43 @@ PlayerDispDataDCPCV3::PlayerDispDataDCPCV3() noexcept void PlayerDispDataDCPCV3::enforce_v2_limits() { // V1/V2 have fewer classes, so we'll substitute some here - if (this->char_class == 11) { - this->char_class = 0; // FOmar -> HUmar - } else if (this->char_class == 10) { - this->char_class = 1; // RAmarl -> HUnewearl - } else if (this->char_class == 9) { - this->char_class = 5; // HUcaseal -> RAcaseal + if (this->visual.char_class == 11) { + this->visual.char_class = 0; // FOmar -> HUmar + } else if (this->visual.char_class == 10) { + this->visual.char_class = 1; // RAmarl -> HUnewearl + } else if (this->visual.char_class == 9) { + this->visual.char_class = 5; // HUcaseal -> RAcaseal } // If the player is somehow still not a valid class, make them appear as the // "ninja" NPC - if (this->char_class > 8) { - this->extra_model = 0; - this->v2_flags |= 2; + if (this->visual.char_class > 8) { + this->visual.extra_model = 0; + this->visual.v2_flags |= 2; } - this->version = 2; + this->visual.version = 2; } PlayerDispDataBB PlayerDispDataDCPCV3::to_bb() const { PlayerDispDataBB bb; - bb.stats.atp = this->stats.atp; - bb.stats.mst = this->stats.mst; - bb.stats.evp = this->stats.evp; - bb.stats.hp = this->stats.hp; - bb.stats.dfp = this->stats.dfp; - bb.stats.ata = this->stats.ata; - bb.stats.lck = this->stats.lck; - bb.unknown_a1 = this->unknown_a1; - bb.level = this->level; - bb.experience = this->experience; - bb.meseta = this->meseta; - bb.guild_card = " 0"; - bb.unknown_a2 = this->unknown_a2; - bb.name_color = this->name_color; - bb.extra_model = this->extra_model; - bb.unused = this->unused; - bb.name_color_checksum = this->name_color_checksum; - bb.section_id = this->section_id; - bb.char_class = this->char_class; - bb.v2_flags = this->v2_flags; - bb.version = this->version; - bb.v1_flags = this->v1_flags; - bb.costume = this->costume; - bb.skin = this->skin; - bb.face = this->face; - bb.head = this->head; - bb.hair = this->hair; - bb.hair_r = this->hair_r; - bb.hair_g = this->hair_g; - bb.hair_b = this->hair_b; - bb.proportion_x = this->proportion_x; - bb.proportion_y = this->proportion_y; - bb.name = add_language_marker(this->name, 'J'); + bb.stats = this->stats; + bb.visual = this->visual; + bb.visual.name = " 0"; + bb.name = add_language_marker(this->visual.name, 'J'); bb.config = this->config; bb.technique_levels = this->technique_levels; return bb; } PlayerDispDataBB::PlayerDispDataBB() noexcept - : level(0), - experience(0), - meseta(0), - unknown_a2(0), - name_color(0), - extra_model(0), - name_color_checksum(0), - section_id(0), - char_class(0), - v2_flags(0), - version(0), - v1_flags(0), - costume(0), - skin(0), - face(0), - head(0), - hair(0), - hair_r(0), - hair_g(0), - hair_b(0), - proportion_x(0), - proportion_y(0) {} + : play_time(0), + unknown_a3(0) {} PlayerDispDataDCPCV3 PlayerDispDataBB::to_dcpcv3() const { PlayerDispDataDCPCV3 ret; - ret.stats.atp = this->stats.atp; - ret.stats.mst = this->stats.mst; - ret.stats.evp = this->stats.evp; - ret.stats.hp = this->stats.hp; - ret.stats.dfp = this->stats.dfp; - ret.stats.ata = this->stats.ata; - ret.stats.lck = this->stats.lck; - ret.unknown_a1 = this->unknown_a1; - ret.level = this->level; - ret.experience = this->experience; - ret.meseta = this->meseta; - ret.unknown_a2 = this->unknown_a2; - ret.name_color = this->name_color; - ret.extra_model = this->extra_model; - ret.unused = this->unused; - ret.name_color_checksum = this->name_color_checksum; - ret.section_id = this->section_id; - ret.char_class = this->char_class; - ret.v2_flags = this->v2_flags; - ret.version = this->version; - ret.v1_flags = this->v1_flags; - ret.costume = this->costume; - ret.skin = this->skin; - ret.face = this->face; - ret.head = this->head; - ret.hair = this->hair; - ret.hair_r = this->hair_r; - ret.hair_g = this->hair_g; - ret.hair_b = this->hair_b; - ret.proportion_x = this->proportion_x; - ret.proportion_y = this->proportion_y; - ret.name = remove_language_marker(this->name); + ret.stats = this->stats; + ret.visual = this->visual; + ret.visual.name = remove_language_marker(this->name); ret.config = this->config; ret.technique_levels = this->technique_levels; return ret; @@ -184,105 +108,46 @@ PlayerDispDataDCPCV3 PlayerDispDataBB::to_dcpcv3() const { PlayerDispDataBBPreview PlayerDispDataBB::to_preview() const { PlayerDispDataBBPreview pre; - pre.level = this->level; - pre.experience = this->experience; - pre.guild_card = this->guild_card; - pre.unknown_a2 = this->unknown_a2; - pre.name_color = this->name_color; - pre.extra_model = this->extra_model; - pre.unused = this->unused; - pre.name_color_checksum = this->name_color_checksum; - pre.section_id = this->section_id; - pre.char_class = this->char_class; - pre.v2_flags = this->v2_flags; - pre.version = this->version; - pre.v1_flags = this->v1_flags; - pre.costume = this->costume; - pre.skin = this->skin; - pre.face = this->face; - pre.head = this->head; - pre.hair = this->hair; - pre.hair_r = this->hair_r; - pre.hair_g = this->hair_g; - pre.hair_b = this->hair_b; - pre.proportion_x = this->proportion_x; - pre.proportion_y = this->proportion_y; + pre.level = this->stats.level; + pre.experience = this->stats.experience; + pre.visual = this->visual; pre.name = this->name; pre.play_time = this->play_time; return pre; } void PlayerDispDataBB::apply_preview(const PlayerDispDataBBPreview& pre) { - this->level = pre.level; - this->experience = pre.experience; - this->guild_card = pre.guild_card; - this->unknown_a2 = pre.unknown_a2; - this->name_color = pre.name_color; - this->extra_model = pre.extra_model; - this->unused = pre.unused; - this->name_color_checksum = pre.name_color_checksum; - this->section_id = pre.section_id; - this->char_class = pre.char_class; - this->v2_flags = pre.v2_flags; - this->version = pre.version; - this->v1_flags = pre.v1_flags; - this->costume = pre.costume; - this->skin = pre.skin; - this->face = pre.face; - this->head = pre.head; - this->hair = pre.hair; - this->hair_r = pre.hair_r; - this->hair_g = pre.hair_g; - this->hair_b = pre.hair_b; - this->proportion_x = pre.proportion_x; - this->proportion_y = pre.proportion_y; + this->stats.level = pre.level; + this->stats.experience = pre.experience; + this->visual = pre.visual; this->name = pre.name; } void PlayerDispDataBB::apply_dressing_room(const PlayerDispDataBBPreview& pre) { - this->name_color = pre.name_color; - this->extra_model = pre.extra_model; - this->name_color_checksum = pre.name_color_checksum; - this->section_id = pre.section_id; - this->char_class = pre.char_class; - this->v2_flags = pre.v2_flags; - this->version = pre.version; - this->v1_flags = pre.v1_flags; - this->costume = pre.costume; - this->skin = pre.skin; - this->face = pre.face; - this->head = pre.head; - this->hair = pre.hair; - this->hair_r = pre.hair_r; - this->hair_g = pre.hair_g; - this->hair_b = pre.hair_b; - this->proportion_x = pre.proportion_x; - this->proportion_y = pre.proportion_y; + this->visual.name_color = pre.visual.name_color; + this->visual.extra_model = pre.visual.extra_model; + this->visual.name_color_checksum = pre.visual.name_color_checksum; + this->visual.section_id = pre.visual.section_id; + this->visual.char_class = pre.visual.char_class; + this->visual.v2_flags = pre.visual.v2_flags; + this->visual.version = pre.visual.version; + this->visual.v1_flags = pre.visual.v1_flags; + this->visual.costume = pre.visual.costume; + this->visual.skin = pre.visual.skin; + this->visual.face = pre.visual.face; + this->visual.head = pre.visual.head; + this->visual.hair = pre.visual.hair; + this->visual.hair_r = pre.visual.hair_r; + this->visual.hair_g = pre.visual.hair_g; + this->visual.hair_b = pre.visual.hair_b; + this->visual.proportion_x = pre.visual.proportion_x; + this->visual.proportion_y = pre.visual.proportion_y; this->name = pre.name; } PlayerDispDataBBPreview::PlayerDispDataBBPreview() noexcept : experience(0), level(0), - unknown_a2(0), - name_color(0), - extra_model(0), - name_color_checksum(0), - section_id(0), - char_class(0), - v2_flags(0), - version(0), - v1_flags(0), - costume(0), - skin(0), - face(0), - head(0), - hair(0), - hair_r(0), - hair_g(0), - hair_b(0), - proportion_x(0), - proportion_y(0), play_time(0) {} GuildCardV3::GuildCardV3() noexcept @@ -416,14 +281,14 @@ void ClientGameData::create_player( const PlayerDispDataBBPreview& preview, shared_ptr level_table) { shared_ptr data(new SavedPlayerDataBB( - load_object_file(player_template_filename(preview.char_class)))); + load_object_file(player_template_filename(preview.visual.char_class)))); if (data->signature != PLAYER_FILE_SIGNATURE) { throw runtime_error("player data header is incorrect"); } try { data->disp.apply_preview(preview); - data->disp.stats = level_table->base_stats_for_class(data->disp.char_class); + data->disp.stats.char_stats = level_table->base_stats_for_class(data->disp.visual.char_class); } catch (const exception& e) { throw runtime_error(string_printf("template application failed: %s", e.what())); } @@ -572,8 +437,8 @@ PlayerBB ClientGameData::export_player_bb() { ret.guild_card_description = player->guild_card_description; ret.reserved1 = 0; ret.reserved2 = 0; - ret.section_id = player->disp.section_id; - ret.char_class = player->disp.char_class; + ret.section_id = player->disp.visual.section_id; + ret.char_class = player->disp.visual.char_class; ret.unknown3 = 0; ret.symbol_chats = account->symbol_chats; ret.shortcuts = account->shortcuts; @@ -678,9 +543,9 @@ void SavedPlayerDataBB::add_item(const PlayerInventoryItem& item) { // Annoyingly, meseta is in the disp data, not in the inventory struct. If the // item is meseta, we have to modify disp instead. if (pid == MESETA_IDENTIFIER) { - this->disp.meseta += item.data.data2d; - if (this->disp.meseta > 999999) { - this->disp.meseta = 999999; + this->disp.stats.meseta += item.data.data2d; + if (this->disp.stats.meseta > 999999) { + this->disp.stats.meseta = 999999; } return; } @@ -762,10 +627,10 @@ PlayerInventoryItem SavedPlayerDataBB::remove_item( // If we're removing meseta (signaled by an invalid item ID), then create a // meseta item. if (item_id == 0xFFFFFFFF) { - if (amount <= this->disp.meseta) { - this->disp.meseta -= amount; + if (amount <= this->disp.stats.meseta) { + this->disp.stats.meseta -= amount; } else if (allow_meseta_overdraft) { - this->disp.meseta = 0; + this->disp.stats.meseta = 0; } else { throw out_of_range("player does not have enough meseta"); } @@ -919,7 +784,7 @@ size_t PlayerBank::find_item(uint32_t item_id) { } void SavedPlayerDataBB::print_inventory(FILE* stream) const { - fprintf(stream, "[PlayerInventory] Meseta: %" PRIu32 "\n", this->disp.meseta.load()); + fprintf(stream, "[PlayerInventory] Meseta: %" PRIu32 "\n", this->disp.stats.meseta.load()); fprintf(stream, "[PlayerInventory] %hhu items\n", this->inventory.num_items); for (size_t x = 0; x < this->inventory.num_items; x++) { const auto& item = this->inventory.items[x]; diff --git a/src/Player.hh b/src/Player.hh index 45614ffa..0b18816c 100644 --- a/src/Player.hh +++ b/src/Player.hh @@ -83,41 +83,56 @@ struct PendingCardTrade { struct PlayerDispDataBB; -struct PlayerDispDataDCPCV3 { // 0xD0 bytes - PlayerStats stats; - parray unknown_a1; - le_uint32_t level; - le_uint32_t experience; - le_uint32_t meseta; - ptext name; - uint64_t unknown_a2; - le_uint32_t name_color; - uint8_t extra_model; - parray unused; - le_uint32_t name_color_checksum; - uint8_t section_id; - uint8_t char_class; - uint8_t v2_flags; - uint8_t version; - le_uint32_t v1_flags; - le_uint16_t costume; - le_uint16_t skin; - le_uint16_t face; - le_uint16_t head; - le_uint16_t hair; - le_uint16_t hair_r; - le_uint16_t hair_g; - le_uint16_t hair_b; - le_float proportion_x; - le_float proportion_y; - parray config; - parray technique_levels; +struct PlayerStats { + /* 00 */ CharacterStats char_stats; + /* 0E */ parray unknown_a1; + /* 18 */ le_uint32_t level; + /* 1C */ le_uint32_t experience; + /* 20 */ le_uint32_t meseta; + /* 24 */ + + PlayerStats() noexcept; +} __attribute__((packed)); + +struct PlayerVisualConfig { + /* 00 */ ptext name; + /* 10 */ uint64_t unknown_a2; + /* 18 */ le_uint32_t name_color; + /* 1C */ uint8_t extra_model; + /* 1D */ parray unused; + /* 2C */ le_uint32_t name_color_checksum; + /* 30 */ uint8_t section_id; + /* 31 */ uint8_t char_class; + /* 32 */ uint8_t v2_flags; + /* 33 */ uint8_t version; + /* 34 */ le_uint32_t v1_flags; + /* 38 */ le_uint16_t costume; + /* 3A */ le_uint16_t skin; + /* 3C */ le_uint16_t face; + /* 3E */ le_uint16_t head; + /* 40 */ le_uint16_t hair; + /* 42 */ le_uint16_t hair_r; + /* 44 */ le_uint16_t hair_g; + /* 46 */ le_uint16_t hair_b; + /* 48 */ le_float proportion_x; + /* 4C */ le_float proportion_y; + /* 50 */ + + PlayerVisualConfig() noexcept; +} __attribute__((packed)); + +struct PlayerDispDataDCPCV3 { + /* 00 */ PlayerStats stats; + /* 24 */ PlayerVisualConfig visual; + /* 74 */ parray config; + /* BC */ parray technique_levels; + /* D0 */ // Note: This struct has a default constructor because it's used in a command // that has a fixed-size array. If we didn't define this constructor, the // trivial fields in that array's members would be uninitialized, and we could // send uninitialized memory to the client. - PlayerDispDataDCPCV3() noexcept; + PlayerDispDataDCPCV3() noexcept = default; void enforce_v2_limits(); PlayerDispDataBB to_bb() const; @@ -127,27 +142,9 @@ struct PlayerDispDataDCPCV3 { // 0xD0 bytes struct PlayerDispDataBBPreview { le_uint32_t experience; le_uint32_t level; - ptext guild_card; - uint64_t unknown_a2; - le_uint32_t name_color; - uint8_t extra_model; - parray unused; - le_uint32_t name_color_checksum; - uint8_t section_id; - uint8_t char_class; - uint8_t v2_flags; - uint8_t version; - le_uint32_t v1_flags; - le_uint16_t costume; - le_uint16_t skin; - le_uint16_t face; - le_uint16_t head; - le_uint16_t hair; - le_uint16_t hair_r; - le_uint16_t hair_g; - le_uint16_t hair_b; - le_float proportion_x; - le_float proportion_y; + // The name field in this structure is used for the player's Guild Card + // number, apparently (possibly because it's a char array and this is BB) + PlayerVisualConfig visual; ptext name; uint32_t play_time; @@ -157,31 +154,7 @@ struct PlayerDispDataBBPreview { // BB player appearance and stats data struct PlayerDispDataBB { PlayerStats stats; - parray unknown_a1; - le_uint32_t level; - le_uint32_t experience; - le_uint32_t meseta; - ptext guild_card; - uint64_t unknown_a2; - le_uint32_t name_color; // ARGB8888 - uint8_t extra_model; - parray unused; - le_uint32_t name_color_checksum; - uint8_t section_id; - uint8_t char_class; - uint8_t v2_flags; - uint8_t version; - le_uint32_t v1_flags; - le_uint16_t costume; - le_uint16_t skin; - le_uint16_t face; - le_uint16_t head; - le_uint16_t hair; - le_uint16_t hair_r; - le_uint16_t hair_g; - le_uint16_t hair_b; - le_float proportion_x; - le_float proportion_y; + PlayerVisualConfig visual; ptext name; le_uint32_t play_time; uint32_t unknown_a3; diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index 79c9c719..76d9a938 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -1025,11 +1025,11 @@ static HandlerResult C_GXB_61(shared_ptr, pd.disp.name = " "; modified = true; } - if (session.options.red_name && pd.disp.name_color != 0xFFFF0000) { - pd.disp.name_color = 0xFFFF0000; + if (session.options.red_name && pd.disp.visual.name_color != 0xFFFF0000) { + pd.disp.visual.name_color = 0xFFFF0000; modified = true; - } else if (session.options.blank_name && pd.disp.name_color != 0x00000000) { - pd.disp.name_color = 0x00000000; + } else if (session.options.blank_name && pd.disp.visual.name_color != 0x00000000) { + pd.disp.visual.name_color = 0x00000000; modified = true; } @@ -1054,14 +1054,14 @@ static HandlerResult C_GXB_61(shared_ptr, add_color_inplace(pd->info_board.data(), pd->info_board.size()); } if (session.options.blank_name) { - pd->disp.name = " "; + pd->disp.visual.name = " "; modified = true; } - if (session.options.red_name && pd->disp.name_color != 0xFFFF0000) { - pd->disp.name_color = 0xFFFF0000; + if (session.options.red_name && pd->disp.visual.name_color != 0xFFFF0000) { + pd->disp.visual.name_color = 0xFFFF0000; modified = true; - } else if (session.options.blank_name && pd->disp.name_color != 0x00000000) { - pd->disp.name_color = 0x00000000; + } else if (session.options.blank_name && pd->disp.visual.name_color != 0x00000000) { + pd->disp.visual.name_color = 0x00000000; modified = true; } } @@ -1340,7 +1340,7 @@ static HandlerResult S_65_67_68_EB(shared_ptr, if (index >= session.lobby_players.size()) { session.log.warning("Ignoring invalid player index %zu at position %zu", index, x); } else { - string name = encode_sjis(cmd.entries[x].disp.name); + string name = encode_sjis(cmd.entries[x].disp.visual.name); if (session.license && (cmd.entries[x].lobby_data.guild_card == session.remote_guild_card_number)) { cmd.entries[x].lobby_data.guild_card = session.license->serial_number; @@ -1353,8 +1353,8 @@ static HandlerResult S_65_67_68_EB(shared_ptr, auto& p = session.lobby_players[index]; p.guild_card_number = cmd.entries[x].lobby_data.guild_card; p.name = name; - p.section_id = cmd.entries[x].disp.section_id; - p.char_class = cmd.entries[x].disp.char_class; + p.section_id = cmd.entries[x].disp.visual.section_id; + p.char_class = cmd.entries[x].disp.visual.char_class; session.log.info("Added lobby player: (%zu) %" PRIu32 " %s", index, p.guild_card_number, p.name.c_str()); } @@ -1409,10 +1409,10 @@ static HandlerResult S_64(shared_ptr, auto& p = session.lobby_players[x]; p.guild_card_number = cmd->lobby_data[x].guild_card; if (cmd_ep3) { - ptext name = cmd_ep3->players_ep3[x].disp.name; + ptext name = cmd_ep3->players_ep3[x].disp.visual.name; p.name = name; - p.section_id = cmd_ep3->players_ep3[x].disp.section_id; - p.char_class = cmd_ep3->players_ep3[x].disp.char_class; + p.section_id = cmd_ep3->players_ep3[x].disp.visual.section_id; + p.char_class = cmd_ep3->players_ep3[x].disp.visual.char_class; } else { p.name.clear(); } @@ -1470,10 +1470,10 @@ static HandlerResult S_E8(shared_ptr, auto& p = session.lobby_players[x]; p.guild_card_number = player_entry.lobby_data.guild_card; - ptext name = player_entry.disp.name; + ptext name = player_entry.disp.visual.name; p.name = name; - p.section_id = player_entry.disp.section_id; - p.char_class = player_entry.disp.char_class; + p.section_id = player_entry.disp.visual.section_id; + p.char_class = player_entry.disp.visual.char_class; session.log.info("Added lobby player: (%zu) %" PRIu32 " %s", x, p.guild_card_number, p.name.c_str()); } diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 5d65d729..308f87ac 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -1478,12 +1478,12 @@ static void on_09(shared_ptr s, shared_ptr c, auto name = encode_sjis(player->disp.name); if (game->is_ep3()) { info += string_printf("%zu: $C6%s$C7 L%" PRIu32 "\n", - x + 1, name.c_str(), player->disp.level + 1); + x + 1, name.c_str(), player->disp.stats.level + 1); } else { info += string_printf("%zu: $C6%s$C7 %s L%" PRIu32 "\n", x + 1, name.c_str(), - abbreviation_for_char_class(player->disp.char_class), - player->disp.level + 1); + abbreviation_for_char_class(player->disp.visual.char_class), + player->disp.stats.level + 1); } } } @@ -1893,11 +1893,11 @@ static void on_10(shared_ptr s, shared_ptr c, send_lobby_message_box(c, u"$C6Incorrect password."); break; } - if (c->game_data.player()->disp.level < game->min_level) { + if (c->game_data.player()->disp.stats.level < game->min_level) { send_lobby_message_box(c, u"$C6Your level is too\nlow to join this\ngame."); break; } - if (c->game_data.player()->disp.level > game->max_level) { + if (c->game_data.player()->disp.stats.level > game->max_level) { send_lobby_message_box(c, u"$C6Your level is too\nhigh to join this\ngame."); break; } @@ -3125,7 +3125,7 @@ shared_ptr create_game_generic( } if (!(c->license->privileges & Privilege::FREE_JOIN_GAMES) && - (min_level > c->game_data.player()->disp.level)) { + (min_level > c->game_data.player()->disp.stats.level)) { // Note: We don't throw here because this is a situation players might // actually encounter while playing the game normally send_lobby_message_box(c, u"Your level is too\nlow for this\ndifficulty"); @@ -3156,7 +3156,7 @@ shared_ptr create_game_generic( game->version = c->version(); game->section_id = c->options.override_section_id >= 0 ? c->options.override_section_id - : c->game_data.player()->disp.section_id; + : c->game_data.player()->disp.visual.section_id; game->episode = episode; game->mode = mode; game->difficulty = difficulty; diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 8cbf7760..c04d3ba9 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -1115,7 +1115,7 @@ static void on_open_shop_bb_or_ep3_battle_subs(shared_ptr s, throw logic_error("item creator missing from BB game"); } - size_t level = c->game_data.player()->disp.level + 1; + size_t level = c->game_data.player()->disp.stats.level + 1; switch (cmd.shop_type) { case 0: c->game_data.shop_contents[0] = l->item_creator->generate_tool_shop_contents(level); @@ -1163,14 +1163,14 @@ static void on_bank_action_bb(shared_ptr, if (cmd.action == 0) { // deposit if (cmd.item_id == 0xFFFFFFFF) { // meseta - if (cmd.meseta_amount > c->game_data.player()->disp.meseta) { + if (cmd.meseta_amount > c->game_data.player()->disp.stats.meseta) { return; } if ((c->game_data.player()->bank.meseta + cmd.meseta_amount) > 999999) { return; } c->game_data.player()->bank.meseta += cmd.meseta_amount; - c->game_data.player()->disp.meseta -= cmd.meseta_amount; + c->game_data.player()->disp.stats.meseta -= cmd.meseta_amount; } else { // item auto item = c->game_data.player()->remove_item( cmd.item_id, cmd.item_amount, c->version() != GameVersion::BB); @@ -1182,11 +1182,11 @@ static void on_bank_action_bb(shared_ptr, if (cmd.meseta_amount > c->game_data.player()->bank.meseta) { return; } - if ((c->game_data.player()->disp.meseta + cmd.meseta_amount) > 999999) { + if ((c->game_data.player()->disp.stats.meseta + cmd.meseta_amount) > 999999) { return; } c->game_data.player()->bank.meseta -= cmd.meseta_amount; - c->game_data.player()->disp.meseta += cmd.meseta_amount; + c->game_data.player()->disp.stats.meseta += cmd.meseta_amount; } else { // item auto bank_item = c->game_data.player()->bank.remove_item(cmd.item_id, cmd.item_amount); PlayerInventoryItem item = bank_item; @@ -1406,29 +1406,29 @@ static void on_charge_attack_bb(shared_ptr, const auto& cmd = check_size_t(data, size); auto& disp = c->game_data.player()->disp; - if (cmd.meseta_amount > disp.meseta) { - disp.meseta = 0; + if (cmd.meseta_amount > disp.stats.meseta) { + disp.stats.meseta = 0; } else { - disp.meseta -= cmd.meseta_amount; + disp.stats.meseta -= cmd.meseta_amount; } } static void add_player_exp(shared_ptr s, shared_ptr l, shared_ptr c, uint32_t exp) { - c->game_data.player()->disp.experience += exp; + c->game_data.player()->disp.stats.experience += exp; send_give_experience(l, c, exp); bool leveled_up = false; do { const auto& level = s->level_table->stats_for_level( - c->game_data.player()->disp.char_class, c->game_data.player()->disp.level + 1); - if (c->game_data.player()->disp.experience >= level.experience) { + c->game_data.player()->disp.visual.char_class, c->game_data.player()->disp.stats.level + 1); + if (c->game_data.player()->disp.stats.experience >= level.experience) { leveled_up = true; - level.apply(c->game_data.player()->disp.stats); - c->game_data.player()->disp.level++; + level.apply(c->game_data.player()->disp.stats.char_stats); + c->game_data.player()->disp.stats.level++; } else { break; } - } while (c->game_data.player()->disp.level < 199); + } while (c->game_data.player()->disp.stats.level < 199); if (leveled_up) { send_level_up(l, c); } @@ -1460,7 +1460,7 @@ static void on_steal_exp_bb(shared_ptr s, if (special >= 0x09 && special <= 0x0B) { // Master's = 8, Lord's = 10, King's = 12 - uint32_t percent = 8 + ((special - 9) << 1) + (char_class_is_android(c->game_data.player()->disp.char_class) ? 30 : 0); + uint32_t percent = 8 + ((special - 9) << 1) + (char_class_is_android(c->game_data.player()->disp.visual.char_class) ? 30 : 0); uint32_t enemy_exp = s->battle_params->get(l->mode == GameMode::SOLO, l->episode, l->difficulty, enemy.type).experience; uint32_t stolen_exp = min((enemy_exp * percent) / 100, 80); if (c->options.debug) { @@ -1524,7 +1524,7 @@ static void on_enemy_killed_bb(shared_ptr s, if (!other_c) { continue; // No player } - if (other_c->game_data.player()->disp.level >= 199) { + if (other_c->game_data.player()->disp.stats.level >= 199) { continue; // Player is level 200 or higher } @@ -1558,10 +1558,10 @@ void on_meseta_reward_request_bb(shared_ptr, auto p = c->game_data.player(); if (cmd.amount < 0) { - if (-cmd.amount > static_cast(p->disp.meseta.load())) { - p->disp.meseta = 0; + if (-cmd.amount > static_cast(p->disp.stats.meseta.load())) { + p->disp.stats.meseta = 0; } else { - p->disp.meseta += cmd.amount; + p->disp.stats.meseta += cmd.amount; } } else if (cmd.amount > 0) { PlayerInventoryItem item; @@ -1649,7 +1649,7 @@ static void on_identify_item_bb(shared_ptr, return; // only weapons can be identified } - c->game_data.player()->disp.meseta -= 100; + c->game_data.player()->disp.stats.meseta -= 100; c->game_data.identify_result = c->game_data.player()->inventory.items[x]; c->game_data.identify_result.data.data1[4] &= 0x7F; send_item_identify_result(l, c); @@ -1698,8 +1698,8 @@ static void on_sell_item_at_shop_bb(shared_ptr s, auto item = c->game_data.player()->remove_item( cmd.item_id, cmd.amount, c->version() != GameVersion::BB); size_t price = (s->item_parameter_table->price_for_item(item.data) >> 3) * cmd.amount; - c->game_data.player()->disp.meseta = min( - c->game_data.player()->disp.meseta + price, 999999); + c->game_data.player()->disp.stats.meseta = min( + c->game_data.player()->disp.stats.meseta + price, 999999); auto name = item.data.name(false); l->log.info("Inventory item %hu:%08" PRIX32 " destroyed via sale (%s)", @@ -1733,10 +1733,10 @@ static void on_buy_shop_item_bb(shared_ptr, size_t price = item.data.data2d * cmd.amount; item.data.data2d = 0; - if (c->game_data.player()->disp.meseta < price) { + if (c->game_data.player()->disp.stats.meseta < price) { throw runtime_error("player does not have enough money"); } - c->game_data.player()->disp.meseta -= price; + c->game_data.player()->disp.stats.meseta -= price; item.data.id = cmd.inventory_item_id; c->game_data.player()->add_item(item); @@ -1758,10 +1758,10 @@ static void on_medical_center_bb(shared_ptr, shared_ptr l, shared_ptr c, uint8_t, uint8_t, const void*, size_t) { if (l->version == GameVersion::BB) { - if (c->game_data.player()->disp.meseta < 10) { + if (c->game_data.player()->disp.stats.meseta < 10) { throw runtime_error("insufficient funds"); } - c->game_data.player()->disp.meseta -= 10; + c->game_data.player()->disp.stats.meseta -= 10; } } diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 6d05d47f..0bd41603 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -1044,8 +1044,8 @@ void send_guild_card(shared_ptr c, shared_ptr source) { uint32_t guild_card_number = source->license->serial_number; u16string name = source->game_data.player()->disp.name; u16string description = source->game_data.player()->guild_card_description; - uint8_t section_id = source->game_data.player()->disp.section_id; - uint8_t char_class = source->game_data.player()->disp.char_class; + uint8_t section_id = source->game_data.player()->disp.visual.section_id; + uint8_t char_class = source->game_data.player()->disp.visual.char_class; send_guild_card( c->channel, guild_card_number, name, u"", description, section_id, char_class); @@ -1387,7 +1387,7 @@ static void send_join_spectator_team(shared_ptr c, shared_ptr l) p.inventory.items[y].data.bswap_data2_if_mag(); } p.disp = watched_lobby->clients[z]->game_data.player()->disp.to_dcpcv3(); - remove_language_marker_inplace(p.disp.name); + remove_language_marker_inplace(p.disp.visual.name); auto& e = cmd.entries[z]; e.player_tag = 0x00010000; @@ -1395,7 +1395,7 @@ static void send_join_spectator_team(shared_ptr c, shared_ptr l) e.name = watched_lobby->clients[z]->game_data.player()->disp.name; remove_language_marker_inplace(e.name); e.present = 1; - e.level = watched_lobby->clients[z]->game_data.player()->disp.level.load(); + e.level = watched_lobby->clients[z]->game_data.player()->disp.stats.level.load(); player_count++; } @@ -1421,13 +1421,13 @@ static void send_join_spectator_team(shared_ptr c, shared_ptr l) cmd.players[client_id].inventory.items[z].data.bswap_data2_if_mag(); } cmd.players[client_id].disp = entry.disp; - remove_language_marker_inplace(cmd.players[client_id].disp.name); + remove_language_marker_inplace(cmd.players[client_id].disp.visual.name); cmd.entries[client_id].player_tag = 0x00010000; cmd.entries[client_id].guild_card_number = entry.lobby_data.guild_card; - cmd.entries[client_id].name = entry.disp.name; + cmd.entries[client_id].name = entry.disp.visual.name; remove_language_marker_inplace(cmd.entries[client_id].name); cmd.entries[client_id].present = 1; - cmd.entries[client_id].level = entry.disp.level.load(); + cmd.entries[client_id].level = entry.disp.stats.level.load(); player_count++; } @@ -1444,13 +1444,13 @@ static void send_join_spectator_team(shared_ptr c, shared_ptr l) remove_language_marker_inplace(cmd.spectator_players[z - 4].lobby_data.name); cmd.spectator_players[z - 4].inventory = l->clients[z]->game_data.player()->inventory; cmd.spectator_players[z - 4].disp = l->clients[z]->game_data.player()->disp.to_dcpcv3(); - remove_language_marker_inplace(cmd.spectator_players[z - 4].disp.name); + remove_language_marker_inplace(cmd.spectator_players[z - 4].disp.visual.name); cmd.entries[z].player_tag = 0x00010000; cmd.entries[z].guild_card_number = l->clients[z]->license->serial_number; cmd.entries[z].name = l->clients[z]->game_data.player()->disp.name; remove_language_marker_inplace(cmd.entries[z].name); cmd.entries[z].present = 1; - cmd.entries[z].level = l->clients[z]->game_data.player()->disp.level.load(); + cmd.entries[z].level = l->clients[z]->game_data.player()->disp.stats.level.load(); player_count++; } } @@ -2052,7 +2052,7 @@ void send_shop(shared_ptr c, uint8_t shop_type) { // notifies players about a level up void send_level_up(shared_ptr l, shared_ptr c) { - PlayerStats stats = c->game_data.player()->disp.stats; + CharacterStats stats = c->game_data.player()->disp.stats.char_stats; for (size_t x = 0; x < c->game_data.player()->inventory.num_items; x++) { if ((c->game_data.player()->inventory.items[x].flags & 0x08) && @@ -2072,7 +2072,7 @@ void send_level_up(shared_ptr l, shared_ptr c) { stats.hp, stats.dfp, stats.ata, - c->game_data.player()->disp.level.load(), + c->game_data.player()->disp.stats.level.load(), 0}; send_command_t(l, 0x60, 0x00, cmd); } @@ -2304,8 +2304,8 @@ string ep3_description_for_client(shared_ptr c) { auto player = c->game_data.player(); return string_printf( "%s CLv%" PRIu32 " %c", - name_for_char_class(player->disp.char_class), - player->disp.level + 1, + name_for_char_class(player->disp.visual.char_class), + player->disp.stats.level + 1, char_for_language_code(player->inventory.language)); }