describe entire battle rules structure
This commit is contained in:
@@ -5486,8 +5486,7 @@ struct G_ExchangeItemForTeamPoints_BB_6xCC {
|
||||
|
||||
struct G_RestartBattle_BB_6xCF {
|
||||
G_UnusedHeader header;
|
||||
parray<le_uint32_t, 11> unknown_a1;
|
||||
le_uint32_t unknown_a2;
|
||||
BattleRules rules;
|
||||
} __packed__;
|
||||
|
||||
// 6xD0: Battle mode level up (BB; handled by server)
|
||||
|
||||
+5
-5
@@ -470,7 +470,7 @@ void ItemCreator::clear_item_if_restricted(ItemData& item) const {
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (this->restrictions->forbid_mags) {
|
||||
if (this->restrictions->mag_mode == BattleRules::MagMode::FORBID_ALL) {
|
||||
this->log.info("Restricted: mags not allowed");
|
||||
item.clear();
|
||||
}
|
||||
@@ -489,11 +489,11 @@ void ItemCreator::clear_item_if_restricted(ItemData& item) const {
|
||||
break;
|
||||
case BattleRules::TechDiskMode::LIMIT_LEVEL:
|
||||
this->log.info("Restricted: tech disk level limited to %hhu",
|
||||
static_cast<uint8_t>(this->restrictions->max_tech_disk_level + 1));
|
||||
if (this->restrictions->max_tech_disk_level == 0) {
|
||||
static_cast<uint8_t>(this->restrictions->max_tech_level + 1));
|
||||
if (this->restrictions->max_tech_level == 0) {
|
||||
item.data1[2] = 0;
|
||||
} else {
|
||||
item.data1[2] %= this->restrictions->max_tech_disk_level;
|
||||
item.data1[2] %= this->restrictions->max_tech_level;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -505,7 +505,7 @@ void ItemCreator::clear_item_if_restricted(ItemData& item) const {
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (this->restrictions->meseta_drop_mode == BattleRules::MesetaDropMode::FORBID_ALL) {
|
||||
if (this->restrictions->meseta_mode == BattleRules::MesetaMode::FORBID_ALL) {
|
||||
this->log.info("Restricted: meseta not allowed");
|
||||
item.clear();
|
||||
}
|
||||
|
||||
+4
-4
@@ -49,7 +49,7 @@ void ClientGameData::create_battle_overlay(shared_ptr<const BattleRules> rules,
|
||||
this->overlay_player_data->inventory.remove_all_items_of_type(0);
|
||||
this->overlay_player_data->inventory.remove_all_items_of_type(1);
|
||||
}
|
||||
if (rules->forbid_mags) {
|
||||
if (rules->mag_mode == BattleRules::MagMode::FORBID_ALL) {
|
||||
this->overlay_player_data->inventory.remove_all_items_of_type(2);
|
||||
}
|
||||
if (rules->tool_mode != BattleRules::ToolMode::ALLOW) {
|
||||
@@ -93,8 +93,8 @@ void ClientGameData::create_battle_overlay(shared_ptr<const BattleRules> rules,
|
||||
// TODO: Verify this is what the game actually does.
|
||||
for (uint8_t tech_num = 0; tech_num < 0x13; tech_num++) {
|
||||
uint8_t existing_level = this->overlay_player_data->get_technique_level(tech_num);
|
||||
if ((existing_level != 0xFF) && (existing_level > rules->max_tech_disk_level)) {
|
||||
this->overlay_player_data->set_technique_level(tech_num, rules->max_tech_disk_level);
|
||||
if ((existing_level != 0xFF) && (existing_level > rules->max_tech_level)) {
|
||||
this->overlay_player_data->set_technique_level(tech_num, rules->max_tech_level);
|
||||
}
|
||||
}
|
||||
} else if (rules->tech_disk_mode == BattleRules::TechDiskMode::FORBID_ALL) {
|
||||
@@ -102,7 +102,7 @@ void ClientGameData::create_battle_overlay(shared_ptr<const BattleRules> rules,
|
||||
this->overlay_player_data->set_technique_level(tech_num, 0xFF);
|
||||
}
|
||||
}
|
||||
if (rules->meseta_drop_mode != BattleRules::MesetaDropMode::ALLOW) {
|
||||
if (rules->meseta_mode != BattleRules::MesetaMode::ALLOW) {
|
||||
this->overlay_player_data->disp.stats.meseta = 0;
|
||||
}
|
||||
if (rules->forbid_scape_dolls) {
|
||||
|
||||
+94
-19
@@ -472,15 +472,33 @@ size_t PlayerBank::find_item(uint32_t item_id) {
|
||||
}
|
||||
|
||||
BattleRules::BattleRules(const JSON& json) {
|
||||
static const JSON empty_list = JSON::list();
|
||||
|
||||
this->tech_disk_mode = json.get_enum("tech_disk_mode", this->tech_disk_mode);
|
||||
this->weapon_and_armor_mode = json.get_enum("weapon_and_armor_mode", this->weapon_and_armor_mode);
|
||||
this->forbid_mags = json.get_bool("forbid_mags", this->forbid_mags);
|
||||
this->mag_mode = json.get_enum("mag_mode", this->mag_mode);
|
||||
this->tool_mode = json.get_enum("tool_mode", this->tool_mode);
|
||||
this->meseta_drop_mode = json.get_enum("meseta_drop_mode", this->meseta_drop_mode);
|
||||
this->forbid_scape_dolls = json.get_bool("forbid_scape_dolls", this->forbid_scape_dolls);
|
||||
this->max_tech_disk_level = json.get_int("max_tech_disk_level", this->max_tech_disk_level);
|
||||
this->replace_char = json.get_bool("replace_char", this->replace_char);
|
||||
this->trap_mode = json.get_enum("trap_mode", this->trap_mode);
|
||||
this->unused_F817 = json.get_int("unused_F817", this->unused_F817);
|
||||
this->respawn_mode = json.get_int("respawn_mode", this->respawn_mode);
|
||||
this->replace_char = json.get_int("replace_char", this->replace_char);
|
||||
this->drop_weapon = json.get_int("drop_weapon", this->drop_weapon);
|
||||
this->is_teams = json.get_int("is_teams", this->is_teams);
|
||||
this->hide_target_reticle = json.get_int("hide_target_reticle", this->hide_target_reticle);
|
||||
this->meseta_mode = json.get_enum("meseta_mode", this->meseta_mode);
|
||||
this->death_level_up = json.get_int("death_level_up", this->death_level_up);
|
||||
const JSON& trap_counts_json = json.get("trap_counts", empty_list);
|
||||
for (size_t z = 0; z < trap_counts_json.size(); z++) {
|
||||
this->trap_counts[z] = trap_counts_json.at(z).as_int();
|
||||
}
|
||||
this->enable_sonar = json.get_int("enable_sonar", this->enable_sonar);
|
||||
this->sonar_count = json.get_int("sonar_count", this->sonar_count);
|
||||
this->forbid_scape_dolls = json.get_int("forbid_scape_dolls", this->forbid_scape_dolls);
|
||||
this->lives = json.get_int("lives", this->lives);
|
||||
this->max_tech_level = json.get_int("max_tech_level", this->max_tech_level);
|
||||
this->char_level = json.get_int("char_level", this->char_level);
|
||||
this->time_limit = json.get_int("time_limit", this->time_limit);
|
||||
this->death_tech_level_up = json.get_int("death_tech_level_up", this->death_tech_level_up);
|
||||
this->box_drop_area = json.get_int("box_drop_area", this->box_drop_area);
|
||||
}
|
||||
|
||||
@@ -488,14 +506,27 @@ JSON BattleRules::json() const {
|
||||
return JSON::dict({
|
||||
{"tech_disk_mode", this->tech_disk_mode},
|
||||
{"weapon_and_armor_mode", this->weapon_and_armor_mode},
|
||||
{"forbid_mags", this->forbid_mags},
|
||||
{"mag_mode", this->mag_mode},
|
||||
{"tool_mode", this->tool_mode},
|
||||
{"meseta_drop_mode", this->meseta_drop_mode},
|
||||
{"forbid_scape_dolls", this->forbid_scape_dolls},
|
||||
{"max_tech_disk_level", this->max_tech_disk_level},
|
||||
{"trap_mode", this->trap_mode},
|
||||
{"unused_F817", this->unused_F817},
|
||||
{"respawn_mode", this->respawn_mode},
|
||||
{"replace_char", this->replace_char},
|
||||
{"char_level", this->char_level},
|
||||
{"box_drop_area", this->box_drop_area},
|
||||
{"drop_weapon", this->drop_weapon},
|
||||
{"is_teams", this->is_teams},
|
||||
{"hide_target_reticle", this->hide_target_reticle},
|
||||
{"meseta_mode", this->meseta_mode},
|
||||
{"death_level_up", this->death_level_up},
|
||||
{"trap_counts", JSON::list({this->trap_counts[0], this->trap_counts[1], this->trap_counts[2], this->trap_counts[3]})},
|
||||
{"enable_sonar", this->enable_sonar},
|
||||
{"sonar_count", this->sonar_count},
|
||||
{"forbid_scape_dolls", this->forbid_scape_dolls},
|
||||
{"lives", this->lives.load()},
|
||||
{"max_tech_level", this->max_tech_level.load()},
|
||||
{"char_level", this->char_level.load()},
|
||||
{"time_limit", this->time_limit.load()},
|
||||
{"death_tech_level_up", this->death_tech_level_up.load()},
|
||||
{"box_drop_area", this->box_drop_area.load()},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -555,6 +586,28 @@ BattleRules::WeaponAndArmorMode enum_for_name<BattleRules::WeaponAndArmorMode>(c
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
const char* name_for_enum<BattleRules::MagMode>(BattleRules::MagMode v) {
|
||||
switch (v) {
|
||||
case BattleRules::MagMode::ALLOW:
|
||||
return "ALLOW";
|
||||
case BattleRules::MagMode::FORBID_ALL:
|
||||
return "FORBID_ALL";
|
||||
default:
|
||||
throw invalid_argument("invalid BattleRules::MagMode value");
|
||||
}
|
||||
}
|
||||
template <>
|
||||
BattleRules::MagMode enum_for_name<BattleRules::MagMode>(const char* name) {
|
||||
if (!strcmp(name, "ALLOW")) {
|
||||
return BattleRules::MagMode::ALLOW;
|
||||
} else if (!strcmp(name, "FORBID_ALL")) {
|
||||
return BattleRules::MagMode::FORBID_ALL;
|
||||
} else {
|
||||
throw invalid_argument("invalid BattleRules::MagMode name");
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
const char* name_for_enum<BattleRules::ToolMode>(BattleRules::ToolMode v) {
|
||||
switch (v) {
|
||||
@@ -582,26 +635,48 @@ BattleRules::ToolMode enum_for_name<BattleRules::ToolMode>(const char* name) {
|
||||
}
|
||||
|
||||
template <>
|
||||
const char* name_for_enum<BattleRules::MesetaDropMode>(BattleRules::MesetaDropMode v) {
|
||||
const char* name_for_enum<BattleRules::TrapMode>(BattleRules::TrapMode v) {
|
||||
switch (v) {
|
||||
case BattleRules::MesetaDropMode::ALLOW:
|
||||
case BattleRules::TrapMode::DEFAULT:
|
||||
return "DEFAULT";
|
||||
case BattleRules::TrapMode::ALL_PLAYERS:
|
||||
return "ALL_PLAYERS";
|
||||
default:
|
||||
throw invalid_argument("invalid BattleRules::TrapMode value");
|
||||
}
|
||||
}
|
||||
template <>
|
||||
BattleRules::TrapMode enum_for_name<BattleRules::TrapMode>(const char* name) {
|
||||
if (!strcmp(name, "DEFAULT")) {
|
||||
return BattleRules::TrapMode::DEFAULT;
|
||||
} else if (!strcmp(name, "ALL_PLAYERS")) {
|
||||
return BattleRules::TrapMode::ALL_PLAYERS;
|
||||
} else {
|
||||
throw invalid_argument("invalid BattleRules::TrapMode name");
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
const char* name_for_enum<BattleRules::MesetaMode>(BattleRules::MesetaMode v) {
|
||||
switch (v) {
|
||||
case BattleRules::MesetaMode::ALLOW:
|
||||
return "ALLOW";
|
||||
case BattleRules::MesetaDropMode::FORBID_ALL:
|
||||
case BattleRules::MesetaMode::FORBID_ALL:
|
||||
return "FORBID_ALL";
|
||||
case BattleRules::MesetaDropMode::CLEAR_AND_ALLOW:
|
||||
case BattleRules::MesetaMode::CLEAR_AND_ALLOW:
|
||||
return "CLEAR_AND_ALLOW";
|
||||
default:
|
||||
throw invalid_argument("invalid BattleRules::MesetaDropMode value");
|
||||
}
|
||||
}
|
||||
template <>
|
||||
BattleRules::MesetaDropMode enum_for_name<BattleRules::MesetaDropMode>(const char* name) {
|
||||
BattleRules::MesetaMode enum_for_name<BattleRules::MesetaMode>(const char* name) {
|
||||
if (!strcmp(name, "ALLOW")) {
|
||||
return BattleRules::MesetaDropMode::ALLOW;
|
||||
return BattleRules::MesetaMode::ALLOW;
|
||||
} else if (!strcmp(name, "FORBID_ALL")) {
|
||||
return BattleRules::MesetaDropMode::FORBID_ALL;
|
||||
return BattleRules::MesetaMode::FORBID_ALL;
|
||||
} else if (!strcmp(name, "CLEAR_AND_ALLOW")) {
|
||||
return BattleRules::MesetaDropMode::CLEAR_AND_ALLOW;
|
||||
return BattleRules::MesetaMode::CLEAR_AND_ALLOW;
|
||||
} else {
|
||||
throw invalid_argument("invalid BattleRules::MesetaDropMode name");
|
||||
}
|
||||
|
||||
+88
-16
@@ -447,39 +447,111 @@ inline PlayerDispDataBB convert_player_disp_data<PlayerDispDataBB>(
|
||||
}
|
||||
|
||||
struct BattleRules {
|
||||
enum class TechDiskMode {
|
||||
enum class TechDiskMode : uint8_t {
|
||||
ALLOW = 0,
|
||||
FORBID_ALL = 1,
|
||||
LIMIT_LEVEL = 2,
|
||||
};
|
||||
enum class WeaponAndArmorMode {
|
||||
enum class WeaponAndArmorMode : uint8_t {
|
||||
ALLOW = 0,
|
||||
CLEAR_AND_ALLOW = 1,
|
||||
FORBID_ALL = 2,
|
||||
FORBID_RARES = 3,
|
||||
};
|
||||
enum class ToolMode {
|
||||
enum class MagMode : uint8_t {
|
||||
ALLOW = 0,
|
||||
FORBID_ALL = 1,
|
||||
};
|
||||
enum class ToolMode : uint8_t {
|
||||
ALLOW = 0,
|
||||
CLEAR_AND_ALLOW = 1,
|
||||
FORBID_ALL = 2,
|
||||
};
|
||||
enum class MesetaDropMode {
|
||||
enum class TrapMode : uint8_t {
|
||||
DEFAULT = 0,
|
||||
ALL_PLAYERS = 1,
|
||||
};
|
||||
enum class MesetaMode : uint8_t {
|
||||
ALLOW = 0,
|
||||
FORBID_ALL = 1,
|
||||
CLEAR_AND_ALLOW = 2,
|
||||
};
|
||||
TechDiskMode tech_disk_mode = TechDiskMode::ALLOW;
|
||||
WeaponAndArmorMode weapon_and_armor_mode = WeaponAndArmorMode::ALLOW;
|
||||
bool forbid_mags = false;
|
||||
ToolMode tool_mode = ToolMode::ALLOW;
|
||||
MesetaDropMode meseta_drop_mode = MesetaDropMode::ALLOW;
|
||||
bool forbid_scape_dolls = false;
|
||||
uint8_t max_tech_disk_level = 0xFF; // 0xFF = no maximum
|
||||
|
||||
bool replace_char = false; // char_type in quest opcodes
|
||||
uint16_t char_level = 0; // Only used if replace_char is true
|
||||
|
||||
uint8_t box_drop_area = 0;
|
||||
// Set by quest opcode F812, but values are remapped.
|
||||
// F812 00 => FORBID_ALL
|
||||
// F812 01 => ALLOW
|
||||
// F812 02 => LIMIT_LEVEL
|
||||
/* 00 */ TechDiskMode tech_disk_mode = TechDiskMode::ALLOW;
|
||||
// Set by quest opcode F813, but values are remapped.
|
||||
// F813 00 => FORBID_ALL
|
||||
// F813 01 => ALLOW
|
||||
// F813 02 => CLEAR_AND_ALLOW
|
||||
// F813 03 => FORBID_RARES
|
||||
/* 01 */ WeaponAndArmorMode weapon_and_armor_mode = WeaponAndArmorMode::ALLOW;
|
||||
// Set by quest opcode F814, but values are remapped.
|
||||
// F814 00 => FORBID_ALL
|
||||
// F814 01 => ALLOW
|
||||
/* 02 */ MagMode mag_mode = MagMode::ALLOW;
|
||||
// Set by quest opcode F815, but values are remapped.
|
||||
// F815 00 => FORBID_ALL
|
||||
// F815 01 => ALLOW
|
||||
// F815 02 => CLEAR_AND_ALLOW
|
||||
/* 03 */ ToolMode tool_mode = ToolMode::ALLOW;
|
||||
// Set by quest opcode F816. Values are not remapped.
|
||||
// F816 00 => DEFAULT
|
||||
// F816 01 => ALL_PLAYERS
|
||||
/* 04 */ TrapMode trap_mode = TrapMode::DEFAULT;
|
||||
// Set by quest opcode F817. Value appears to be unused in all PSO versions.
|
||||
/* 05 */ uint8_t unused_F817 = 0;
|
||||
// Set by quest opcode F818, but values are remapped.
|
||||
// F818 00 => 01
|
||||
// F818 01 => 00
|
||||
// F818 02 => 02
|
||||
// TODO: Define an enum class for this field.
|
||||
/* 06 */ uint8_t respawn_mode = 0;
|
||||
// Set by quest opcode F819.
|
||||
/* 07 */ uint8_t replace_char = 0;
|
||||
// Set by quest opcode F81A, but value is inverted.
|
||||
/* 08 */ uint8_t drop_weapon = 0;
|
||||
// Set by quest opcode F81B.
|
||||
/* 09 */ uint8_t is_teams = 0;
|
||||
// Set by quest opcode F852.
|
||||
/* 0A */ uint8_t hide_target_reticle = 0;
|
||||
// Set by quest opcode F81E. Values are not remapped.
|
||||
// F81E 00 => ALLOW
|
||||
// F81E 01 => FORBID_ALL
|
||||
// F81E 02 => CLEAR_AND_ALLOW
|
||||
/* 0B */ MesetaMode meseta_mode = MesetaMode::ALLOW;
|
||||
// Set by quest opcode F81D.
|
||||
/* 0C */ uint8_t death_level_up = 0;
|
||||
// Set by quest opcode F851. The trap type is remapped:
|
||||
// F851 00 XX => set count to XX for trap type 00
|
||||
// F851 01 XX => set count to XX for trap type 02
|
||||
// F851 02 XX => set count to XX for trap type 03
|
||||
// F851 03 XX => set count to XX for trap type 01
|
||||
/* 0D */ parray<uint8_t, 4> trap_counts;
|
||||
// Set by quest opcode F85E.
|
||||
/* 11 */ uint8_t enable_sonar = 0;
|
||||
// Set by quest opcode F85F.
|
||||
/* 12 */ uint8_t sonar_count = 0;
|
||||
// Set by quest opcode F89E.
|
||||
/* 13 */ uint8_t forbid_scape_dolls = 0;
|
||||
// This value does not appear to be set by any quest opcode.
|
||||
/* 14 */ le_uint32_t unknown_a1 = 0;
|
||||
// Set by quest opcode F86F.
|
||||
/* 18 */ le_uint32_t lives = 0;
|
||||
// Set by quest opcode F870.
|
||||
/* 1C */ le_uint32_t max_tech_level = 0;
|
||||
// Set by quest opcode F871.
|
||||
/* 20 */ le_uint32_t char_level = 0;
|
||||
// Set by quest opcode F872.
|
||||
/* 24 */ le_uint32_t time_limit = 0;
|
||||
// Set by quest opcode F8A8.
|
||||
/* 28 */ le_uint16_t death_tech_level_up = 0;
|
||||
/* 2A */ parray<uint8_t, 2> unused;
|
||||
// Set by quest opcode F86B.
|
||||
/* 2C */ le_uint32_t box_drop_area = 0;
|
||||
/* 30 */
|
||||
|
||||
BattleRules() = default;
|
||||
explicit BattleRules(const JSON& json);
|
||||
@@ -487,7 +559,7 @@ struct BattleRules {
|
||||
|
||||
bool operator==(const BattleRules& other) const = default;
|
||||
bool operator!=(const BattleRules& other) const = default;
|
||||
};
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ChallengeTemplateDefinition {
|
||||
uint32_t level;
|
||||
|
||||
@@ -1739,6 +1739,46 @@ static void on_medical_center_bb(shared_ptr<Client> c, uint8_t, uint8_t, const v
|
||||
}
|
||||
}
|
||||
|
||||
static void on_battle_restart_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void* data, size_t size) {
|
||||
auto s = c->require_server_state();
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() &&
|
||||
(l->mode == GameMode::BATTLE) &&
|
||||
(l->flags & Lobby::Flag::QUEST_IN_PROGRESS) &&
|
||||
(l->base_version == GameVersion::BB)) {
|
||||
const auto& cmd = check_size_t<G_RestartBattle_BB_6xCF>(data, size);
|
||||
|
||||
shared_ptr<BattleRules> new_rules(new BattleRules(cmd.rules));
|
||||
if (l->item_creator) {
|
||||
l->item_creator->set_restrictions(new_rules);
|
||||
}
|
||||
|
||||
for (auto& lc : l->clients) {
|
||||
if (lc) {
|
||||
lc->game_data.delete_overlay();
|
||||
lc->game_data.create_battle_overlay(new_rules, s->level_table);
|
||||
}
|
||||
}
|
||||
l->map->clear();
|
||||
}
|
||||
}
|
||||
|
||||
static void on_battle_level_up_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void*, size_t) {
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game() &&
|
||||
(l->mode == GameMode::BATTLE) &&
|
||||
(l->flags & Lobby::Flag::QUEST_IN_PROGRESS) &&
|
||||
(l->base_version == GameVersion::BB)) {
|
||||
// Requests the client to be leveled up by num_levels levels. The server should
|
||||
// respond with a 6x30 command.
|
||||
|
||||
struct G_BattleModeLevelUp_BB_6xD0 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint32_t num_levels;
|
||||
} __packed__;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef void (*subcommand_handler_t)(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size);
|
||||
@@ -1951,8 +1991,8 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
||||
/* 6xCC */ nullptr,
|
||||
/* 6xCD */ nullptr,
|
||||
/* 6xCE */ nullptr,
|
||||
/* 6xCF */ on_forward_check_size_game,
|
||||
/* 6xD0 */ nullptr,
|
||||
/* 6xCF */ on_battle_restart_bb,
|
||||
/* 6xD0 */ on_battle_level_up_bb,
|
||||
/* 6xD1 */ nullptr,
|
||||
/* 6xD2 */ nullptr,
|
||||
/* 6xD3 */ nullptr,
|
||||
|
||||
+8
-13
@@ -2151,30 +2151,25 @@ void send_shop(shared_ptr<Client> c, uint8_t shop_type) {
|
||||
send_command(c, 0x60, 0x00, &cmd, sizeof(cmd) - sizeof(cmd.item_datas[0]) * (20 - contents.size()));
|
||||
}
|
||||
|
||||
// notifies players about a level up
|
||||
void send_level_up(shared_ptr<Client> c) {
|
||||
auto l = c->require_lobby();
|
||||
auto p = c->game_data.player();
|
||||
CharacterStats stats = p->disp.stats.char_stats;
|
||||
|
||||
for (size_t x = 0; x < p->inventory.num_items; x++) {
|
||||
if ((p->inventory.items[x].flags & 0x08) &&
|
||||
(p->inventory.items[x].data.data1[0] == 0x02)) {
|
||||
stats.dfp += (p->inventory.items[x].data.data1w[2] / 100);
|
||||
stats.atp += (p->inventory.items[x].data.data1w[3] / 50);
|
||||
stats.ata += (p->inventory.items[x].data.data1w[4] / 200);
|
||||
stats.mst += (p->inventory.items[x].data.data1w[5] / 50);
|
||||
}
|
||||
const ItemData* mag = nullptr;
|
||||
try {
|
||||
mag = &p->inventory.items[p->inventory.find_equipped_mag()].data;
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
|
||||
G_LevelUp_6x30 cmd = {
|
||||
{0x30, sizeof(G_LevelUp_6x30) / 4, c->lobby_client_id},
|
||||
stats.atp,
|
||||
stats.mst,
|
||||
stats.atp + (mag ? (mag->data1w[3] / 50) : 0),
|
||||
stats.mst + (mag ? (mag->data1w[5] / 50) : 0),
|
||||
stats.evp,
|
||||
stats.hp,
|
||||
stats.dfp,
|
||||
stats.ata,
|
||||
stats.dfp + (mag ? (mag->data1w[2] / 100) : 0),
|
||||
stats.ata + (mag ? (mag->data1w[4] / 20) : 0),
|
||||
p->disp.stats.level.load(),
|
||||
0};
|
||||
send_command_t(l, 0x60, 0x00, cmd);
|
||||
|
||||
Reference in New Issue
Block a user