implement BB challenge mode random enemy generation
This commit is contained in:
@@ -38,7 +38,7 @@
|
||||
|
||||
## PSOBB
|
||||
|
||||
- Find any remaining mismatches in enemy IDs / experience
|
||||
- Find any remaining mismatches in enemy indexes / experience
|
||||
- Support EXP multipliers
|
||||
- Sale prices for non-rare weapons with specials are computed incorrectly when buying/selling at shops
|
||||
- Implement trade window
|
||||
|
||||
+14
-17
@@ -2724,7 +2724,7 @@ struct C_GuildCardDataRequest_BB_03DC {
|
||||
|
||||
struct S_RareMonsterList_BB_DE {
|
||||
// Unused entries are set to FFFF
|
||||
parray<le_uint16_t, 0x10> enemy_ids;
|
||||
parray<le_uint16_t, 0x10> enemy_indexes;
|
||||
} __packed__;
|
||||
|
||||
// DF (C->S): Set Challenge Mode parameters (BB)
|
||||
@@ -3646,7 +3646,7 @@ struct G_ClientIDHeader {
|
||||
struct G_EnemyIDHeader {
|
||||
uint8_t subcommand = 0;
|
||||
uint8_t size = 0;
|
||||
le_uint16_t enemy_id = 0; // In range [0x1000, 0x4000)
|
||||
le_uint16_t enemy_id = 0; // In [0x1000, 0x4000); not the same as enemy_index!
|
||||
} __packed__;
|
||||
struct G_ObjectIDHeader {
|
||||
uint8_t subcommand = 0;
|
||||
@@ -3775,8 +3775,7 @@ struct G_Unknown_6x09 {
|
||||
template <bool IsBigEndian>
|
||||
struct G_EnemyHitByPlayer_6x0A {
|
||||
G_EnemyIDHeader header;
|
||||
// Note: enemy_id (in header) is in the range [0x1000, 0x4000)
|
||||
le_uint16_t enemy_id = 0;
|
||||
le_uint16_t enemy_index = 0; // [0, 0xB50)
|
||||
le_uint16_t remaining_hp = 0;
|
||||
typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type flags = 0;
|
||||
} __packed__;
|
||||
@@ -3790,8 +3789,8 @@ struct G_EnemyHitByPlayer_DC_PC_XB_BB_6x0A : G_EnemyHitByPlayer_6x0A<false> {
|
||||
|
||||
struct G_BoxDestroyed_6x0B {
|
||||
G_ClientIDHeader header;
|
||||
le_uint32_t unknown_a2 = 0;
|
||||
le_uint32_t unknown_a3 = 0;
|
||||
le_uint32_t flags = 0;
|
||||
le_uint32_t object_index = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x0C: Add condition (poison/slow/etc.)
|
||||
@@ -4528,15 +4527,13 @@ struct G_SyncGameStateHeader_6x6B_6x6C_6x6D_6x6E {
|
||||
// Decompressed format is a list of these
|
||||
struct G_SyncEnemyState_6x6B_Entry_Decompressed {
|
||||
// TODO: Verify this format on DC and PC. It appears correct for GC and BB.
|
||||
le_uint32_t unknown_a1 = 0; // Possibly some kind of flags
|
||||
// enemy_index is not the same as enemy_id, unfortunately - the enemy_id sent
|
||||
// in the 6x76 command when an enemy is killed does not match enemy_index
|
||||
le_uint16_t enemy_index = 0; // FFFF = enemy is dead
|
||||
le_uint16_t damage_taken = 0;
|
||||
uint8_t unknown_a4 = 0;
|
||||
uint8_t unknown_a5 = 0;
|
||||
uint8_t unknown_a6 = 0;
|
||||
uint8_t unknown_a7 = 0;
|
||||
le_uint32_t flags = 0;
|
||||
le_uint16_t last_attacker = 0;
|
||||
le_uint16_t remaining_hp = 0;
|
||||
uint8_t red_buff_type = 0;
|
||||
uint8_t red_buff_level = 0;
|
||||
uint8_t blue_buff_type = 0;
|
||||
uint8_t blue_buff_level = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x6C: Sync object state (used while loading into game; same header format as 6E)
|
||||
@@ -5498,7 +5495,7 @@ struct G_MedicalCenterUsed_BB_6xC5 {
|
||||
|
||||
struct G_StealEXP_BB_6xC6 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t enemy_id = 0;
|
||||
le_uint16_t enemy_index = 0;
|
||||
le_uint16_t unknown_a1 = 0;
|
||||
} __packed__;
|
||||
|
||||
@@ -5515,7 +5512,7 @@ struct G_ChargeAttack_BB_6xC7 {
|
||||
|
||||
struct G_EnemyKilled_BB_6xC8 {
|
||||
G_EnemyIDHeader header;
|
||||
le_uint16_t enemy_id = 0;
|
||||
le_uint16_t enemy_index = 0;
|
||||
le_uint16_t killer_client_id = 0;
|
||||
uint8_t unknown_a1 = 0;
|
||||
parray<uint8_t, 3> unused;
|
||||
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
// V2/V3: -> parray<uint8_t, 0x05>
|
||||
/* 14 */ U32T armor_slot_count_prob_table_offset;
|
||||
|
||||
// This array (indexed by enemy_id) specifies the range of meseta values
|
||||
// This array (indexed by enemy_type) specifies the range of meseta values
|
||||
// that each enemy can drop.
|
||||
// V2/V3: -> parray<Range<U16T>, 0x64>
|
||||
/* 18 */ U32T enemy_meseta_ranges_offset;
|
||||
|
||||
+690
-448
File diff suppressed because it is too large
Load Diff
+185
-2
@@ -14,6 +14,170 @@
|
||||
#include "Text.hh"
|
||||
|
||||
struct Map {
|
||||
struct SectionHeader {
|
||||
enum class Type {
|
||||
END = 0,
|
||||
OBJECTS = 1,
|
||||
ENEMIES = 2,
|
||||
WAVE_EVENTS = 3,
|
||||
RANDOM_ENEMY_LOCATIONS = 4,
|
||||
RANDOM_ENEMY_DEFINITIONS = 5,
|
||||
};
|
||||
le_uint32_t le_type;
|
||||
le_uint32_t section_size; // Includes this header
|
||||
le_uint32_t floor;
|
||||
le_uint32_t data_size;
|
||||
|
||||
inline Type type() const {
|
||||
return static_cast<Type>(this->le_type.load());
|
||||
}
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ObjectEntry { // Section type 1 (OBJECTS)
|
||||
/* 00 */ le_uint16_t base_type;
|
||||
/* 02 */ le_uint16_t flags;
|
||||
/* 04 */ le_uint16_t index;
|
||||
/* 06 */ le_uint16_t unknown_a2;
|
||||
/* 08 */ le_uint16_t entity_id; // == index + 0x4000
|
||||
/* 0A */ le_uint16_t group;
|
||||
/* 0C */ le_uint16_t section;
|
||||
/* 0E */ le_uint16_t unknown_a3;
|
||||
/* 10 */ le_float x;
|
||||
/* 14 */ le_float y;
|
||||
/* 18 */ le_float z;
|
||||
/* 1C */ le_uint32_t x_angle;
|
||||
/* 20 */ le_uint32_t y_angle;
|
||||
/* 24 */ le_uint32_t z_angle;
|
||||
/* 28 */ le_float param1;
|
||||
/* 2C */ le_float param2;
|
||||
/* 30 */ le_float param3;
|
||||
/* 34 */ le_uint32_t param4;
|
||||
/* 38 */ le_uint32_t param5;
|
||||
/* 3C */ le_uint32_t param6;
|
||||
/* 40 */ le_uint32_t unused; // Reserved for pointer in client's memory; unused by server
|
||||
/* 44 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct EnemyEntry { // Section type 2 (ENEMIES)
|
||||
/* 00 */ le_uint16_t base_type;
|
||||
/* 02 */ le_uint16_t flags;
|
||||
/* 04 */ le_uint16_t index;
|
||||
/* 06 */ le_uint16_t num_children;
|
||||
/* 08 */ le_uint16_t floor;
|
||||
/* 0A */ le_uint16_t entity_id; // == index + 0x1000
|
||||
/* 0C */ le_uint16_t section;
|
||||
/* 0E */ le_uint16_t wave_number;
|
||||
/* 10 */ le_uint16_t wave_number2;
|
||||
/* 12 */ le_uint16_t unknown_a1;
|
||||
/* 14 */ le_float x;
|
||||
/* 18 */ le_float y;
|
||||
/* 1C */ le_float z;
|
||||
/* 20 */ le_uint32_t x_angle;
|
||||
/* 24 */ le_uint32_t y_angle;
|
||||
/* 28 */ le_uint32_t z_angle;
|
||||
/* 2C */ le_float fparam1;
|
||||
/* 30 */ le_float fparam2;
|
||||
/* 34 */ le_float fparam3;
|
||||
/* 38 */ le_float fparam4;
|
||||
/* 3C */ le_float fparam5;
|
||||
/* 40 */ le_uint16_t uparam1;
|
||||
/* 42 */ le_uint16_t uparam2;
|
||||
/* 44 */ le_uint32_t unused; // Reserved for pointer in client's memory; unused by server
|
||||
/* 48 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct EventsSectionHeader { // Section type 3 (WAVE_EVENTS)
|
||||
/* 00 */ le_uint32_t footer_offset;
|
||||
/* 04 */ le_uint32_t entries_offset;
|
||||
/* 08 */ le_uint32_t entry_count;
|
||||
/* 0C */ be_uint32_t format; // 0 or 'evt2'
|
||||
/* 10 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct Event1Entry { // Section type 3 (WAVE_EVENTS) if format == 0
|
||||
/* 00 */ le_uint32_t event_id;
|
||||
/* 04 */ le_uint16_t flags;
|
||||
/* 06 */ le_uint16_t unknown_a2;
|
||||
/* 08 */ le_uint16_t section;
|
||||
/* 0A */ le_uint16_t wave_number;
|
||||
/* 0C */ le_uint32_t delay;
|
||||
/* 10 */ le_uint32_t clear_events_index;
|
||||
/* 14 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct Event2Entry { // Section type 3 (WAVE_EVENTS) if format == 'evt2'
|
||||
/* 00 */ le_uint32_t event_id;
|
||||
/* 04 */ le_uint16_t flags;
|
||||
/* 06 */ le_uint16_t unknown_a2;
|
||||
/* 08 */ le_uint16_t section;
|
||||
/* 0A */ le_uint16_t wave_number;
|
||||
/* 0C */ le_uint16_t min_delay;
|
||||
/* 0E */ le_uint16_t max_delay;
|
||||
/* 10 */ uint8_t min_enemies;
|
||||
/* 11 */ uint8_t max_enemies;
|
||||
/* 12 */ le_uint16_t max_waves;
|
||||
/* 14 */ le_uint32_t clear_events_index;
|
||||
/* 18 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct RandomEnemyLocationsHeader { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
||||
/* 00 */ le_uint32_t section_table_offset; // Offset to RandomEnemyLocationSegment structs, from start of this struct
|
||||
/* 04 */ le_uint32_t entries_offset; // Offset to RandomEnemyLocationEntry structs, from start of this struct
|
||||
/* 08 */ le_uint32_t num_sections;
|
||||
/* 0C */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct RandomEnemyLocationSection { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
||||
/* 00 */ le_uint16_t section;
|
||||
/* 02 */ le_uint16_t count;
|
||||
/* 04 */ le_uint32_t offset;
|
||||
/* 08 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct RandomEnemyLocationEntry { // Section type 4 (RANDOM_ENEMY_LOCATIONS)
|
||||
/* 00 */ le_float x;
|
||||
/* 04 */ le_float y;
|
||||
/* 08 */ le_float z;
|
||||
/* 0C */ le_uint32_t x_angle;
|
||||
/* 10 */ le_uint32_t y_angle;
|
||||
/* 14 */ le_uint32_t z_angle;
|
||||
/* 18 */ uint16_t unknown_a9;
|
||||
/* 1A */ uint16_t unknown_a10;
|
||||
/* 1C */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct RandomEnemyDefinitionsHeader { // Section type 5 (RANDOM_ENEMY_DEFINITIONS)
|
||||
/* 00 */ le_uint32_t entries_offset; // Offset to RandomEnemyDefinition structs, from start of this struct
|
||||
/* 04 */ le_uint32_t weight_entries_offset; // Offset to RandomEnemyDefinitionWeights structs, from start of this struct
|
||||
/* 08 */ le_uint32_t entry_count;
|
||||
/* 0C */ le_uint32_t weight_entry_count;
|
||||
/* 10 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct RandomEnemyDefinition { // Section type 5 (RANDOM_ENEMY_DEFINITIONS)
|
||||
// All fields through entry_num map to the corresponding fields in
|
||||
// EnemyEntry. Note that the order of the uparam fields is switched!
|
||||
/* 00 */ le_float fparam1;
|
||||
/* 04 */ le_float fparam2;
|
||||
/* 08 */ le_float fparam3;
|
||||
/* 0C */ le_float fparam4;
|
||||
/* 10 */ le_float fparam5;
|
||||
/* 14 */ le_uint16_t uparam2;
|
||||
/* 16 */ le_uint16_t uparam1;
|
||||
/* 18 */ le_uint32_t entry_num;
|
||||
/* 1C */ le_uint16_t min_children;
|
||||
/* 1E */ le_uint16_t max_children;
|
||||
/* 20 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct RandomEnemyWeight { // Section type 5 (RANDOM_ENEMY_DEFINITIONS)
|
||||
/* 00 */ uint8_t base_type_index;
|
||||
/* 01 */ uint8_t definition_entry_num;
|
||||
/* 02 */ uint8_t weight;
|
||||
/* 03 */ uint8_t unknown_a4;
|
||||
/* 04 */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct RareEnemyRates {
|
||||
uint32_t hildeblue; // HILDEBEAR -> HILDEBLUE
|
||||
uint32_t rappy; // RAG_RAPPY -> {AL_RAPPY or seasonal rappies}; SAND_RAPPY -> DEL_RAPPY
|
||||
@@ -51,6 +215,15 @@ struct Map {
|
||||
|
||||
void clear();
|
||||
void add_objects_from_map_data(const void* data, size_t size);
|
||||
bool check_and_log_rare_enemy(bool default_is_rare, uint32_t rare_rate);
|
||||
void add_enemy(EnemyType type);
|
||||
void add_enemy(
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
uint8_t event,
|
||||
size_t index,
|
||||
const EnemyEntry& e,
|
||||
const RareEnemyRates& rare_rates = Map::DEFAULT_RARE_ENEMIES);
|
||||
void add_enemies_from_map_data(
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
@@ -58,12 +231,22 @@ struct Map {
|
||||
const void* data,
|
||||
size_t size,
|
||||
const RareEnemyRates& rare_rates = Map::DEFAULT_RARE_ENEMIES);
|
||||
void add_random_enemies_from_map_data(
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
uint8_t event,
|
||||
StringReader wave_events_r,
|
||||
StringReader random_enemy_locations_r,
|
||||
StringReader random_enemy_definitions_r,
|
||||
uint32_t rare_seed,
|
||||
const RareEnemyRates& rare_rates = Map::DEFAULT_RARE_ENEMIES);
|
||||
void add_enemies_and_objects_from_quest_data(
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
uint8_t event,
|
||||
const void* data,
|
||||
size_t size,
|
||||
uint32_t rare_seed,
|
||||
const RareEnemyRates& rare_rates = Map::DEFAULT_RARE_ENEMIES);
|
||||
};
|
||||
|
||||
@@ -74,9 +257,9 @@ struct Map {
|
||||
class SetDataTable {
|
||||
public:
|
||||
struct SetEntry {
|
||||
std::string name1;
|
||||
std::string object_list_basename;
|
||||
std::string enemy_list_basename;
|
||||
std::string name3;
|
||||
std::string event_list_basename;
|
||||
};
|
||||
|
||||
SetDataTable(std::shared_ptr<const std::string> data, bool big_endian);
|
||||
|
||||
@@ -2620,8 +2620,9 @@ static void on_AC_V3_BB(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
|
||||
l->event,
|
||||
dat_contents.data(),
|
||||
dat_contents.size(),
|
||||
l->random_seed,
|
||||
(l->mode == GameMode::CHALLENGE) ? Map::NO_RARE_ENEMIES : Map::DEFAULT_RARE_ENEMIES);
|
||||
c->log.info("Replaced enemies list with quest layout (%zu entries)",
|
||||
l->log.info("Replaced enemies list with quest layout (%zu entries)",
|
||||
l->map->enemies.size());
|
||||
for (size_t z = 0; z < l->map->enemies.size(); z++) {
|
||||
string e_str = l->map->enemies[z].str();
|
||||
|
||||
+15
-16
@@ -1621,11 +1621,11 @@ static void on_enemy_hit(shared_ptr<Client> c, uint8_t command, uint8_t, const v
|
||||
if (!l->map) {
|
||||
throw runtime_error("game does not have a map loaded");
|
||||
}
|
||||
if (cmd.enemy_id >= l->map->enemies.size()) {
|
||||
if (cmd.enemy_index >= l->map->enemies.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& enemy = l->map->enemies[cmd.enemy_id];
|
||||
auto& enemy = l->map->enemies[cmd.enemy_index];
|
||||
if (enemy.flags & Map::Enemy::Flag::DEFEATED) {
|
||||
return;
|
||||
}
|
||||
@@ -1633,7 +1633,7 @@ static void on_enemy_hit(shared_ptr<Client> c, uint8_t command, uint8_t, const v
|
||||
enemy.last_hit_by_client_id = c->lobby_client_id;
|
||||
}
|
||||
|
||||
G_EnemyHitByPlayer_GC_6x0A sw_cmd = {{{cmd.header.subcommand, cmd.header.size, cmd.header.enemy_id}, cmd.enemy_id, cmd.remaining_hp, cmd.flags.load()}};
|
||||
G_EnemyHitByPlayer_GC_6x0A sw_cmd = {{{cmd.header.subcommand, cmd.header.size, cmd.header.enemy_id}, cmd.enemy_index, cmd.remaining_hp, cmd.flags.load()}};
|
||||
bool sender_is_gc = (c->version() == GameVersion::GC);
|
||||
for (auto lc : l->clients) {
|
||||
if (lc && (lc != c)) {
|
||||
@@ -1721,7 +1721,7 @@ static void on_steal_exp_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void*
|
||||
const auto& cmd = check_size_t<G_StealEXP_BB_6xC6>(data, size);
|
||||
|
||||
auto p = c->game_data.player();
|
||||
const auto& enemy = l->map->enemies.at(cmd.enemy_id);
|
||||
const auto& enemy = l->map->enemies.at(cmd.enemy_index);
|
||||
const auto& inventory = p->inventory;
|
||||
const auto& weapon = inventory.items[inventory.find_equipped_weapon()];
|
||||
|
||||
@@ -1751,7 +1751,7 @@ static void on_steal_exp_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void*
|
||||
uint32_t stolen_exp = min<uint32_t>((enemy_exp * percent) / 100, (l->difficulty + 1) * 20);
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5+%" PRIu32 " E-%hX %s",
|
||||
stolen_exp, cmd.enemy_id.load(), name_for_enum(enemy.type));
|
||||
stolen_exp, cmd.enemy_index.load(), name_for_enum(enemy.type));
|
||||
}
|
||||
add_player_exp(c, stolen_exp);
|
||||
}
|
||||
@@ -1774,17 +1774,17 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t command, uint8_t fl
|
||||
if (!l->map) {
|
||||
throw runtime_error("game does not have a map loaded");
|
||||
}
|
||||
if (cmd.enemy_id >= l->map->enemies.size()) {
|
||||
if (cmd.enemy_index >= l->map->enemies.size()) {
|
||||
send_text_message(c, "$C6Missing enemy killed");
|
||||
return;
|
||||
}
|
||||
|
||||
auto& e = l->map->enemies[cmd.enemy_id];
|
||||
auto& e = l->map->enemies[cmd.enemy_index];
|
||||
string e_str = e.str();
|
||||
c->log.info("Enemy killed: E-%hX => %s", cmd.enemy_id.load(), e_str.c_str());
|
||||
c->log.info("Enemy killed: E-%hX => %s", cmd.enemy_index.load(), e_str.c_str());
|
||||
if (e.flags & Map::Enemy::Flag::DEFEATED) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5E-%hX __DEFEATED__", cmd.enemy_id.load());
|
||||
send_text_message_printf(c, "$C5E-%hX __DEFEATED__", cmd.enemy_index.load());
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1796,7 +1796,7 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t command, uint8_t fl
|
||||
experience = bp_table.stats[l->difficulty][bp_index].experience * l->exp_multiplier;
|
||||
} catch (const exception& e) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5E-%hX __MISSING__\n%s", cmd.enemy_id.load(), e.what());
|
||||
send_text_message_printf(c, "$C5E-%hX __MISSING__\n%s", cmd.enemy_index.load(), e.what());
|
||||
} else {
|
||||
send_text_message_printf(c, "$C4Unknown enemy type killed:\n%s", e.what());
|
||||
}
|
||||
@@ -1812,19 +1812,18 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t command, uint8_t fl
|
||||
if (!other_c) {
|
||||
continue; // No player
|
||||
}
|
||||
if (other_c->game_data.player()->disp.stats.level >= 199) {
|
||||
continue; // Player is level 200 or higher
|
||||
}
|
||||
|
||||
if (experience != 0xFFFFFFFF) {
|
||||
// Killer gets full experience, others get 77%
|
||||
bool is_killer = (e.last_hit_by_client_id == other_c->lobby_client_id);
|
||||
uint32_t player_exp = is_killer ? experience : ((experience * 77) / 100);
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
if (other_c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message_printf(c, "$C5+%" PRIu32 " E-%hX %s",
|
||||
player_exp, cmd.enemy_id.load(), name_for_enum(e.type));
|
||||
player_exp, cmd.enemy_index.load(), name_for_enum(e.type));
|
||||
}
|
||||
if (other_c->game_data.player()->disp.stats.level < 199) {
|
||||
add_player_exp(other_c, player_exp);
|
||||
}
|
||||
add_player_exp(c, player_exp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -2327,13 +2327,13 @@ void send_set_exp_multiplier(std::shared_ptr<Lobby> l) {
|
||||
|
||||
void send_rare_enemy_index_list(shared_ptr<Client> c, const vector<size_t>& indexes) {
|
||||
S_RareMonsterList_BB_DE cmd;
|
||||
if (indexes.size() > cmd.enemy_ids.size()) {
|
||||
if (indexes.size() > cmd.enemy_indexes.size()) {
|
||||
throw runtime_error("too many rare enemies");
|
||||
}
|
||||
for (size_t z = 0; z < indexes.size(); z++) {
|
||||
cmd.enemy_ids[z] = indexes[z];
|
||||
cmd.enemy_indexes[z] = indexes[z];
|
||||
}
|
||||
cmd.enemy_ids.clear_after(indexes.size(), 0xFFFF);
|
||||
cmd.enemy_indexes.clear_after(indexes.size(), 0xFFFF);
|
||||
send_command_t(c, 0xDE, 0x00, cmd);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user