rename area -> floor in most places
This commit is contained in:
+20
-20
@@ -396,7 +396,7 @@ static void server_command_exit(shared_ptr<Client> c, const std::string&) {
|
||||
} else if (l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS) || l->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) {
|
||||
G_UnusedHeader cmd = {0x73, 0x01, 0x0000};
|
||||
c->channel.send(0x60, 0x00, cmd);
|
||||
c->area = 0;
|
||||
c->floor = 0;
|
||||
} else {
|
||||
send_text_message(c, "$C6You must return to\nthe lobby first");
|
||||
}
|
||||
@@ -1081,23 +1081,23 @@ static void server_command_warp(shared_ptr<Client> c, const std::string& args, b
|
||||
check_is_game(l, true);
|
||||
check_cheats_enabled(l);
|
||||
|
||||
uint32_t area = stoul(args, nullptr, 0);
|
||||
if (c->area == area) {
|
||||
uint32_t floor = stoul(args, nullptr, 0);
|
||||
if (c->floor == floor) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t limit = area_limit_for_episode(l->episode);
|
||||
size_t limit = floor_limit_for_episode(l->episode);
|
||||
if (limit == 0) {
|
||||
return;
|
||||
} else if (area > limit) {
|
||||
} else if (floor > limit) {
|
||||
send_text_message_printf(c, "$C6Area numbers must\nbe %zu or less.", limit);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_warpall) {
|
||||
send_warp(l, area, false);
|
||||
send_warp(l, floor, false);
|
||||
} else {
|
||||
send_warp(c, area, true);
|
||||
send_warp(c, floor, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1116,12 +1116,12 @@ static void proxy_command_warp(shared_ptr<ProxyServer::LinkedSession> ses, const
|
||||
send_text_message(ses->client_channel, "$C6You must be in a\ngame to use this\ncommand");
|
||||
return;
|
||||
}
|
||||
uint32_t area = stoul(args, nullptr, 0);
|
||||
send_warp(ses->client_channel, ses->lobby_client_id, area, !is_warpall);
|
||||
uint32_t floor = stoul(args, nullptr, 0);
|
||||
send_warp(ses->client_channel, ses->lobby_client_id, floor, !is_warpall);
|
||||
if (is_warpall) {
|
||||
send_warp(ses->server_channel, ses->lobby_client_id, area, false);
|
||||
send_warp(ses->server_channel, ses->lobby_client_id, floor, false);
|
||||
}
|
||||
ses->area = area;
|
||||
ses->floor = floor;
|
||||
}
|
||||
|
||||
static void proxy_command_warpme(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
|
||||
@@ -1138,11 +1138,11 @@ static void server_command_next(shared_ptr<Client> c, const std::string&) {
|
||||
check_is_game(l, true);
|
||||
check_cheats_enabled(l);
|
||||
|
||||
size_t limit = area_limit_for_episode(l->episode);
|
||||
size_t limit = floor_limit_for_episode(l->episode);
|
||||
if (limit == 0) {
|
||||
return;
|
||||
}
|
||||
send_warp(c, (c->area + 1) % limit, true);
|
||||
send_warp(c, (c->floor + 1) % limit, true);
|
||||
}
|
||||
|
||||
static void proxy_command_next(shared_ptr<ProxyServer::LinkedSession> ses, const std::string&) {
|
||||
@@ -1153,8 +1153,8 @@ static void proxy_command_next(shared_ptr<ProxyServer::LinkedSession> ses, const
|
||||
return;
|
||||
}
|
||||
|
||||
ses->area++;
|
||||
send_warp(ses->client_channel, ses->lobby_client_id, ses->area, true);
|
||||
ses->floor++;
|
||||
send_warp(ses->client_channel, ses->lobby_client_id, ses->floor, true);
|
||||
}
|
||||
|
||||
static void server_command_what(shared_ptr<Client> c, const std::string&) {
|
||||
@@ -1170,7 +1170,7 @@ static void server_command_what(shared_ptr<Client> c, const std::string&) {
|
||||
float min_dist2 = 0.0f;
|
||||
uint32_t nearest_item_id = 0xFFFFFFFF;
|
||||
for (const auto& it : l->item_id_to_floor_item) {
|
||||
if (it.second.area != c->area) {
|
||||
if (it.second.floor != c->floor) {
|
||||
continue;
|
||||
}
|
||||
float dx = it.second.x - c->x;
|
||||
@@ -1305,8 +1305,8 @@ static void server_command_item(shared_ptr<Client> c, const std::string& args) {
|
||||
ItemData item = s->item_name_index->parse_item_description(c->version(), args);
|
||||
item.id = l->generate_item_id(c->lobby_client_id);
|
||||
|
||||
l->add_item(item, c->area, c->x, c->z);
|
||||
send_drop_stacked_item(l, item, c->area, c->x, c->z);
|
||||
l->add_item(item, c->floor, c->x, c->z);
|
||||
send_drop_stacked_item(l, item, c->floor, c->x, c->z);
|
||||
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message(c, "$C7Item created:\n" + name);
|
||||
@@ -1340,8 +1340,8 @@ static void proxy_command_item(shared_ptr<ProxyServer::LinkedSession> ses, const
|
||||
send_text_message(ses->client_channel, "$C7Next drop:\n" + name);
|
||||
|
||||
} else {
|
||||
send_drop_stacked_item(s, ses->client_channel, item, ses->area, ses->x, ses->z);
|
||||
send_drop_stacked_item(s, ses->server_channel, item, ses->area, ses->x, ses->z);
|
||||
send_drop_stacked_item(s, ses->client_channel, item, ses->floor, ses->x, ses->z);
|
||||
send_drop_stacked_item(s, ses->server_channel, item, ses->floor, ses->x, ses->z);
|
||||
|
||||
string name = s->describe_item(ses->version(), item, true);
|
||||
send_text_message(ses->client_channel, "$C7Item created:\n" + name);
|
||||
|
||||
+1
-1
@@ -146,7 +146,7 @@ Client::Client(
|
||||
sub_version(-1),
|
||||
x(0.0f),
|
||||
z(0.0f),
|
||||
area(0),
|
||||
floor(0),
|
||||
lobby_client_id(0),
|
||||
lobby_arrow_color(0),
|
||||
preferred_lobby_id(-1),
|
||||
|
||||
+1
-1
@@ -176,7 +176,7 @@ struct Client : public std::enable_shared_from_this<Client> {
|
||||
int32_t sub_version;
|
||||
float x;
|
||||
float z;
|
||||
uint32_t area;
|
||||
uint32_t floor;
|
||||
std::weak_ptr<Lobby> lobby;
|
||||
uint8_t lobby_client_id;
|
||||
uint8_t lobby_arrow_color;
|
||||
|
||||
+29
-29
@@ -3679,7 +3679,7 @@ struct G_SwitchStateChanged_6x05 {
|
||||
parray<uint8_t, 2> unknown_a1;
|
||||
le_uint16_t unknown_a2 = 0;
|
||||
parray<uint8_t, 2> unknown_a3;
|
||||
uint8_t area = 0;
|
||||
uint8_t floor = 0;
|
||||
uint8_t flags = 0; // Bit field, with 2 lowest bits having meaning
|
||||
} __packed__;
|
||||
|
||||
@@ -3870,11 +3870,11 @@ struct G_DestroyNPC_6x1C {
|
||||
// 6x1D: Invalid subcommand
|
||||
// 6x1E: Invalid subcommand
|
||||
|
||||
// 6x1F: Set player area
|
||||
// 6x1F: Set player floor
|
||||
|
||||
struct G_SetPlayerArea_6x1F {
|
||||
G_ClientIDHeader header;
|
||||
le_uint32_t area = 0;
|
||||
le_uint32_t floor = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x20: Set position
|
||||
@@ -3883,7 +3883,7 @@ struct G_SetPlayerArea_6x1F {
|
||||
|
||||
struct G_SetPosition_6x20 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint32_t area = 0;
|
||||
le_uint32_t floor = 0;
|
||||
le_float x = 0.0f;
|
||||
le_float y = 0.0f;
|
||||
le_float z = 0.0f;
|
||||
@@ -3894,12 +3894,12 @@ struct G_SetPosition_6x20 {
|
||||
|
||||
struct G_InterLevelWarp_6x21 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint32_t area = 0;
|
||||
le_uint32_t floor = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x22: Set player invisible
|
||||
// 6x23: Set player visible
|
||||
// These are generally used while a player is in the process of changing areas.
|
||||
// These are generally used while a player is in the process of changing floors.
|
||||
|
||||
struct G_SetPlayerVisibility_6x22_6x23 {
|
||||
G_ClientIDHeader header;
|
||||
@@ -3954,7 +3954,7 @@ struct G_DeleteInventoryItem_6x29 {
|
||||
struct G_DropItem_6x2A {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t unknown_a1 = 0; // Should be 1... maybe amount?
|
||||
le_uint16_t area = 0;
|
||||
le_uint16_t floor = 0;
|
||||
le_uint32_t item_id = 0;
|
||||
le_float x = 0.0f;
|
||||
le_float y = 0.0f;
|
||||
@@ -4093,7 +4093,7 @@ struct G_StopAtPosition_6x3E {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t unknown_a1 = 0;
|
||||
le_uint16_t unknown_a2 = 0;
|
||||
le_uint16_t area = 0;
|
||||
le_uint16_t floor = 0;
|
||||
le_uint16_t unknown_a3 = 0;
|
||||
le_float x = 0.0f;
|
||||
le_float y = 0.0f;
|
||||
@@ -4106,7 +4106,7 @@ struct G_SetPosition_6x3F {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t unknown_a1 = 0;
|
||||
le_uint16_t angle = 0;
|
||||
le_uint16_t area = 0;
|
||||
le_uint16_t floor = 0;
|
||||
le_uint16_t room = 0;
|
||||
le_float x = 0.0f;
|
||||
le_float y = 0.0f;
|
||||
@@ -4309,7 +4309,7 @@ struct G_LobbyAnimation_6x58 {
|
||||
struct G_PickUpItem_6x59 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t client_id2 = 0;
|
||||
le_uint16_t area = 0;
|
||||
le_uint16_t floor = 0;
|
||||
le_uint32_t item_id = 0;
|
||||
} __packed__;
|
||||
|
||||
@@ -4318,7 +4318,7 @@ struct G_PickUpItem_6x59 {
|
||||
struct G_PickUpItemRequest_6x5A {
|
||||
G_ClientIDHeader header;
|
||||
le_uint32_t item_id = 0;
|
||||
le_uint16_t area = 0;
|
||||
le_uint16_t floor = 0;
|
||||
le_uint16_t unused = 0;
|
||||
} __packed__;
|
||||
|
||||
@@ -4336,7 +4336,7 @@ struct G_Unknown_6x5C {
|
||||
|
||||
struct G_DropStackedItem_DC_6x5D {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t area = 0;
|
||||
le_uint16_t floor = 0;
|
||||
le_uint16_t unknown_a2 = 0; // Corresponds to FloorItem::unknown_a2
|
||||
le_float x = 0.0f;
|
||||
le_float z = 0.0f;
|
||||
@@ -4357,15 +4357,15 @@ struct G_BuyShopItem_6x5E {
|
||||
// 6x5F: Drop item from box/enemy
|
||||
|
||||
struct FloorItem {
|
||||
uint8_t area = 0;
|
||||
uint8_t floor = 0;
|
||||
uint8_t from_enemy = 0;
|
||||
le_uint16_t entity_id = 0; // < 0x0B50 if from_enemy != 0; otherwise < 0x0BA0
|
||||
le_float x = 0.0f;
|
||||
le_float z = 0.0f;
|
||||
le_uint16_t unknown_a2 = 0;
|
||||
// The drop number is scoped to the area and increments by 1 each time an
|
||||
// item is dropped. The last item dropped in each area has drop_number equal
|
||||
// to total_items_dropped_per_area[area - 1] - 1.
|
||||
// The drop number is scoped to the floor and increments by 1 each time an
|
||||
// item is dropped. The last item dropped in each floor has drop_number equal
|
||||
// to total_items_dropped_per_floor[floor - 1] - 1.
|
||||
le_uint16_t drop_number = 0;
|
||||
ItemData item;
|
||||
} __packed__;
|
||||
@@ -4383,7 +4383,7 @@ struct G_DropItem_PC_V3_BB_6x5F : G_DropItem_DC_6x5F {
|
||||
|
||||
struct G_StandardDropItemRequest_DC_6x60 {
|
||||
G_UnusedHeader header;
|
||||
uint8_t area = 0;
|
||||
uint8_t floor = 0;
|
||||
uint8_t rt_index = 0;
|
||||
le_uint16_t entity_id = 0;
|
||||
le_float x = 0.0f;
|
||||
@@ -4413,7 +4413,7 @@ struct G_ActivateMagEffect_6x61 {
|
||||
struct G_DestroyGroundItem_6x63 {
|
||||
G_UnusedHeader header;
|
||||
le_uint32_t item_id = 0;
|
||||
le_uint32_t area = 0;
|
||||
le_uint32_t floor = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x64: Unknown (not valid on Episode 3)
|
||||
@@ -4433,7 +4433,7 @@ struct G_UseStarAtomizer_6x66 {
|
||||
|
||||
struct G_CreateEnemySet_6x67 {
|
||||
G_UnusedHeader header;
|
||||
// unused1 could be area; the client checks this againset a global but the
|
||||
// unused1 could be floor; the client checks this against a global but the
|
||||
// logic is the same in both branches
|
||||
le_uint32_t unused1 = 0;
|
||||
le_uint32_t unknown_a1 = 0;
|
||||
@@ -4510,16 +4510,16 @@ struct G_SyncItemState_6x6D_Decompressed {
|
||||
// TODO: Verify this format on DC and PC. It appears correct for GC and BB.
|
||||
// Note: 16 vs. 15 is not a bug here - there really is an extra field in the
|
||||
// total drop count vs. the floor item count. Despite this, Pioneer 2 or Lab
|
||||
// (area 0) isn't included in total_items_dropped_per_area (so Forest 1 is [0]
|
||||
// in that array) but it is included in floor_item_count_per_area (so Forest 1
|
||||
// is [1] there).
|
||||
parray<le_uint16_t, 16> total_items_dropped_per_area;
|
||||
// (floor 0) isn't included in total_items_dropped_per_floor (so Forest 1 is
|
||||
// [0] in that array) but it is included in floor_item_count_per_floor (so
|
||||
// Forest 1 is [1] there).
|
||||
parray<le_uint16_t, 16> total_items_dropped_per_floor;
|
||||
// Only [0]-[3] in this array are ever actually used in normal gameplay, but
|
||||
// the client fills in all 12 of these with reasonable values.
|
||||
parray<le_uint32_t, 12> next_item_id_per_player;
|
||||
parray<le_uint32_t, 15> floor_item_count_per_area;
|
||||
parray<le_uint32_t, 15> floor_item_count_per_floor;
|
||||
// Variable-length field:
|
||||
// FloorItem items[sum(floor_item_count_per_area)];
|
||||
// FloorItem items[sum(floor_item_count_per_floor)];
|
||||
} __packed__;
|
||||
|
||||
// 6x6E: Sync flag state (used while loading into game)
|
||||
@@ -4886,7 +4886,7 @@ struct G_Unknown_6x92 {
|
||||
|
||||
struct G_ActivateTimedSwitch_6x93 {
|
||||
G_UnusedHeader header;
|
||||
le_uint16_t area = 0;
|
||||
le_uint16_t floor = 0;
|
||||
le_uint16_t switch_id = 0;
|
||||
uint8_t unknown_a1 = 0; // Logic is different if this is 1 vs. any other value
|
||||
parray<uint8_t, 3> unused;
|
||||
@@ -4896,7 +4896,7 @@ struct G_ActivateTimedSwitch_6x93 {
|
||||
|
||||
struct G_InterLevelWarp_6x94 {
|
||||
G_UnusedHeader header;
|
||||
le_uint16_t area = 0;
|
||||
le_uint16_t floor = 0;
|
||||
parray<uint8_t, 2> unused;
|
||||
} __packed__;
|
||||
|
||||
@@ -5141,7 +5141,7 @@ struct G_MoveLobbyChair_6xB0 {
|
||||
|
||||
struct G_Unknown_6xB2 {
|
||||
G_UnusedHeader header;
|
||||
uint8_t area = 0;
|
||||
uint8_t floor = 0;
|
||||
uint8_t unused = 0;
|
||||
le_uint16_t client_id = 0;
|
||||
le_uint32_t unknown_a3 = 0; // PSO GC puts 0x00051720 (333600) here
|
||||
@@ -5427,7 +5427,7 @@ struct G_TeamInvitationAction_BB_6xC1_6xC2_6xCD_6xCE {
|
||||
|
||||
struct G_SplitStackedItem_BB_6xC3 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t area = 0;
|
||||
le_uint16_t floor = 0;
|
||||
le_uint16_t unused2 = 0;
|
||||
le_float x = 0.0f;
|
||||
le_float z = 0.0f;
|
||||
|
||||
+4
-8
@@ -78,8 +78,7 @@ private:
|
||||
bool are_rare_drops_allowed() const;
|
||||
uint8_t normalize_area_number(uint8_t area) const;
|
||||
|
||||
ItemData on_monster_item_drop_with_norm_area(
|
||||
uint32_t enemy_type, uint8_t norm_area);
|
||||
ItemData on_monster_item_drop_with_norm_area(uint32_t enemy_type, uint8_t norm_area);
|
||||
ItemData on_box_item_drop_with_norm_area(uint8_t area_norm);
|
||||
|
||||
uint32_t rand_int(uint64_t max);
|
||||
@@ -105,22 +104,19 @@ private:
|
||||
void generate_common_item_variances(uint32_t norm_area, ItemData& item);
|
||||
void generate_common_armor_slots_and_bonuses(ItemData& item);
|
||||
void generate_common_armor_slot_count(ItemData& item);
|
||||
void generate_common_armor_or_shield_type_and_variances(
|
||||
char area_norm, ItemData& item);
|
||||
void generate_common_armor_or_shield_type_and_variances(char area_norm, ItemData& item);
|
||||
void generate_common_tool_variances(uint32_t area_norm, ItemData& item);
|
||||
uint8_t generate_tech_disk_level(uint32_t tech_num, uint32_t area_norm);
|
||||
void generate_common_tool_type(uint8_t tool_class, ItemData& item) const;
|
||||
void generate_common_mag_variances(ItemData& item) const;
|
||||
void generate_common_weapon_variances(uint8_t area_norm, ItemData& item);
|
||||
void generate_common_weapon_grind(ItemData& item,
|
||||
uint8_t offset_within_subtype_range);
|
||||
void generate_common_weapon_grind(ItemData& item, uint8_t offset_within_subtype_range);
|
||||
void generate_common_weapon_bonuses(ItemData& item, uint8_t area_norm);
|
||||
void generate_common_weapon_special(ItemData& item, uint8_t area_norm);
|
||||
uint8_t choose_weapon_special(uint8_t det);
|
||||
void generate_unit_weights_tables();
|
||||
void generate_common_unit_variances(uint8_t det, ItemData& item);
|
||||
void choose_tech_disk_level_for_tool_shop(
|
||||
ItemData& item, size_t player_level, uint8_t tech_num_index);
|
||||
void choose_tech_disk_level_for_tool_shop(ItemData& item, size_t player_level, uint8_t tech_num_index);
|
||||
static void clear_tool_item_if_invalid(ItemData& item);
|
||||
void clear_item_if_restricted(ItemData& item) const;
|
||||
|
||||
|
||||
+2
-2
@@ -336,10 +336,10 @@ const Lobby::FloorItem& Lobby::find_item(uint32_t item_id) const {
|
||||
return this->item_id_to_floor_item.at(item_id);
|
||||
}
|
||||
|
||||
void Lobby::add_item(const ItemData& data, uint8_t area, float x, float z) {
|
||||
void Lobby::add_item(const ItemData& data, uint8_t floor, float x, float z) {
|
||||
auto& fi = this->item_id_to_floor_item[data.id];
|
||||
fi.data = data;
|
||||
fi.area = area;
|
||||
fi.floor = floor;
|
||||
fi.x = x;
|
||||
fi.z = z;
|
||||
}
|
||||
|
||||
+2
-2
@@ -64,7 +64,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
ItemData data;
|
||||
float x;
|
||||
float z;
|
||||
uint8_t area;
|
||||
uint8_t floor;
|
||||
};
|
||||
std::shared_ptr<Map> map;
|
||||
std::array<uint32_t, 12> next_item_id;
|
||||
@@ -188,7 +188,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
|
||||
bool item_exists(uint32_t item_id) const;
|
||||
const FloorItem& find_item(uint32_t item_id) const;
|
||||
void add_item(const ItemData& item, uint8_t area, float x, float z);
|
||||
void add_item(const ItemData& item, uint8_t floor, float x, float z);
|
||||
ItemData remove_item(uint32_t item_id);
|
||||
uint32_t generate_item_id(uint8_t client_id);
|
||||
void on_item_id_generated_externally(uint32_t item_id);
|
||||
|
||||
+105
-104
@@ -13,8 +13,9 @@ using namespace std;
|
||||
|
||||
static constexpr float UINT32_MAX_AS_FLOAT = 4294967296.0f;
|
||||
|
||||
Map::Enemy::Enemy(EnemyType type)
|
||||
Map::Enemy::Enemy(uint8_t floor, EnemyType type)
|
||||
: type(type),
|
||||
floor(floor),
|
||||
flags(0),
|
||||
last_hit_by_client_id(0) {
|
||||
}
|
||||
@@ -52,9 +53,9 @@ bool Map::check_and_log_rare_enemy(bool default_is_rare, uint32_t rare_rate) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void Map::add_enemy(EnemyType type) {
|
||||
static_game_data_log.info("Adding enemy E-%zX => %s", this->enemies.size(), name_for_enum(type));
|
||||
this->enemies.emplace_back(type);
|
||||
void Map::add_enemy(uint8_t floor, EnemyType type) {
|
||||
static_game_data_log.info("Adding enemy %02hhX:E-%zX => %s", floor, this->enemies.size(), name_for_enum(type));
|
||||
this->enemies.emplace_back(floor, type);
|
||||
}
|
||||
|
||||
void Map::add_enemy(
|
||||
@@ -67,39 +68,39 @@ void Map::add_enemy(
|
||||
switch (e.base_type) {
|
||||
case 0x40: {
|
||||
bool is_rare = this->check_and_log_rare_enemy(e.uparam1 & 0x01, rare_rates.hildeblue);
|
||||
this->add_enemy(is_rare ? EnemyType::HILDEBLUE : EnemyType::HILDEBEAR);
|
||||
this->add_enemy(e.floor, is_rare ? EnemyType::HILDEBLUE : EnemyType::HILDEBEAR);
|
||||
break;
|
||||
}
|
||||
case 0x41: {
|
||||
bool is_rare = this->check_and_log_rare_enemy(e.uparam1 & 0x01, rare_rates.rappy);
|
||||
switch (episode) {
|
||||
case Episode::EP1:
|
||||
this->add_enemy(is_rare ? EnemyType::AL_RAPPY : EnemyType::RAG_RAPPY);
|
||||
this->add_enemy(e.floor, is_rare ? EnemyType::AL_RAPPY : EnemyType::RAG_RAPPY);
|
||||
break;
|
||||
case Episode::EP2:
|
||||
if (is_rare) {
|
||||
switch (event) {
|
||||
case 0x01:
|
||||
this->add_enemy(EnemyType::SAINT_RAPPY);
|
||||
this->add_enemy(e.floor, EnemyType::SAINT_RAPPY);
|
||||
break;
|
||||
case 0x04:
|
||||
this->add_enemy(EnemyType::EGG_RAPPY);
|
||||
this->add_enemy(e.floor, EnemyType::EGG_RAPPY);
|
||||
break;
|
||||
case 0x05:
|
||||
this->add_enemy(EnemyType::HALLO_RAPPY);
|
||||
this->add_enemy(e.floor, EnemyType::HALLO_RAPPY);
|
||||
break;
|
||||
default:
|
||||
this->add_enemy(EnemyType::LOVE_RAPPY);
|
||||
this->add_enemy(e.floor, EnemyType::LOVE_RAPPY);
|
||||
}
|
||||
} else {
|
||||
this->add_enemy(EnemyType::RAG_RAPPY);
|
||||
this->add_enemy(e.floor, EnemyType::RAG_RAPPY);
|
||||
}
|
||||
break;
|
||||
case Episode::EP4:
|
||||
if (e.floor > 0x05) {
|
||||
this->add_enemy(is_rare ? EnemyType::DEL_RAPPY_ALT : EnemyType::SAND_RAPPY_ALT);
|
||||
this->add_enemy(e.floor, is_rare ? EnemyType::DEL_RAPPY_ALT : EnemyType::SAND_RAPPY_ALT);
|
||||
} else {
|
||||
this->add_enemy(is_rare ? EnemyType::DEL_RAPPY : EnemyType::SAND_RAPPY);
|
||||
this->add_enemy(e.floor, is_rare ? EnemyType::DEL_RAPPY : EnemyType::SAND_RAPPY);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -108,292 +109,292 @@ void Map::add_enemy(
|
||||
break;
|
||||
}
|
||||
case 0x42: {
|
||||
this->add_enemy(EnemyType::MONEST);
|
||||
this->add_enemy(e.floor, EnemyType::MONEST);
|
||||
for (size_t x = 0; x < 30; x++) {
|
||||
this->add_enemy(EnemyType::MOTHMANT);
|
||||
this->add_enemy(e.floor, EnemyType::MOTHMANT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x43: {
|
||||
this->add_enemy(e.fparam2 ? EnemyType::BARBAROUS_WOLF : EnemyType::SAVAGE_WOLF);
|
||||
this->add_enemy(e.floor, e.fparam2 ? EnemyType::BARBAROUS_WOLF : EnemyType::SAVAGE_WOLF);
|
||||
break;
|
||||
}
|
||||
case 0x44:
|
||||
static const EnemyType types[3] = {EnemyType::BOOMA, EnemyType::GOBOOMA, EnemyType::GIGOBOOMA};
|
||||
this->add_enemy(types[e.uparam1 % 3]);
|
||||
this->add_enemy(e.floor, types[e.uparam1 % 3]);
|
||||
break;
|
||||
case 0x60:
|
||||
this->add_enemy(EnemyType::GRASS_ASSASSIN);
|
||||
this->add_enemy(e.floor, EnemyType::GRASS_ASSASSIN);
|
||||
break;
|
||||
case 0x61:
|
||||
if ((episode == Episode::EP2) && (e.floor > 0x0F)) {
|
||||
this->add_enemy(EnemyType::DEL_LILY);
|
||||
this->add_enemy(e.floor, EnemyType::DEL_LILY);
|
||||
} else {
|
||||
bool is_rare = this->check_and_log_rare_enemy(e.uparam1 & 0x01, rare_rates.nar_lily);
|
||||
this->add_enemy(is_rare ? EnemyType::NAR_LILY : EnemyType::POISON_LILY);
|
||||
this->add_enemy(e.floor, is_rare ? EnemyType::NAR_LILY : EnemyType::POISON_LILY);
|
||||
}
|
||||
break;
|
||||
case 0x62:
|
||||
this->add_enemy(EnemyType::NANO_DRAGON);
|
||||
this->add_enemy(e.floor, EnemyType::NANO_DRAGON);
|
||||
break;
|
||||
case 0x63: {
|
||||
static const EnemyType types[3] = {EnemyType::EVIL_SHARK, EnemyType::PAL_SHARK, EnemyType::GUIL_SHARK};
|
||||
this->add_enemy(types[e.uparam1 % 3]);
|
||||
this->add_enemy(e.floor, types[e.uparam1 % 3]);
|
||||
break;
|
||||
}
|
||||
case 0x64: {
|
||||
bool is_rare = this->check_and_log_rare_enemy(e.uparam1 & 0x01, rare_rates.pouilly_slime);
|
||||
for (size_t x = 0; x < 5; x++) { // Main slime + 4 clones
|
||||
this->add_enemy(is_rare ? EnemyType::POFUILLY_SLIME : EnemyType::POUILLY_SLIME);
|
||||
this->add_enemy(e.floor, is_rare ? EnemyType::POFUILLY_SLIME : EnemyType::POUILLY_SLIME);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x65:
|
||||
this->add_enemy(EnemyType::PAN_ARMS);
|
||||
this->add_enemy(EnemyType::HIDOOM);
|
||||
this->add_enemy(EnemyType::MIGIUM);
|
||||
this->add_enemy(e.floor, EnemyType::PAN_ARMS);
|
||||
this->add_enemy(e.floor, EnemyType::HIDOOM);
|
||||
this->add_enemy(e.floor, EnemyType::MIGIUM);
|
||||
break;
|
||||
case 0x80:
|
||||
this->add_enemy((e.uparam1 & 0x01) ? EnemyType::GILLCHIC : EnemyType::DUBCHIC);
|
||||
this->add_enemy(e.floor, (e.uparam1 & 0x01) ? EnemyType::GILLCHIC : EnemyType::DUBCHIC);
|
||||
break;
|
||||
case 0x81:
|
||||
this->add_enemy(EnemyType::GARANZ);
|
||||
this->add_enemy(e.floor, EnemyType::GARANZ);
|
||||
break;
|
||||
case 0x82: {
|
||||
EnemyType type = e.fparam2 ? EnemyType::SINOW_GOLD : EnemyType::SINOW_BEAT;
|
||||
size_t count = (e.num_children == 0) ? 5 : (e.num_children + 1);
|
||||
for (size_t z = 0; z < count; z++) {
|
||||
this->add_enemy(type);
|
||||
this->add_enemy(e.floor, type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x83:
|
||||
this->add_enemy(EnemyType::CANADINE);
|
||||
this->add_enemy(e.floor, EnemyType::CANADINE);
|
||||
break;
|
||||
case 0x84:
|
||||
this->add_enemy(EnemyType::CANANE);
|
||||
this->add_enemy(e.floor, EnemyType::CANANE);
|
||||
for (size_t x = 0; x < 8; x++) {
|
||||
this->add_enemy(EnemyType::CANADINE_GROUP);
|
||||
this->add_enemy(e.floor, EnemyType::CANADINE_GROUP);
|
||||
}
|
||||
break;
|
||||
case 0x85:
|
||||
this->add_enemy(EnemyType::DUBWITCH);
|
||||
this->add_enemy(e.floor, EnemyType::DUBWITCH);
|
||||
break;
|
||||
case 0xA0:
|
||||
this->add_enemy(EnemyType::DELSABER);
|
||||
this->add_enemy(e.floor, EnemyType::DELSABER);
|
||||
break;
|
||||
case 0xA1:
|
||||
this->add_enemy(EnemyType::CHAOS_SORCERER);
|
||||
this->add_enemy(EnemyType::BEE_R);
|
||||
this->add_enemy(EnemyType::BEE_L);
|
||||
this->add_enemy(e.floor, EnemyType::CHAOS_SORCERER);
|
||||
this->add_enemy(e.floor, EnemyType::BEE_R);
|
||||
this->add_enemy(e.floor, EnemyType::BEE_L);
|
||||
break;
|
||||
case 0xA2:
|
||||
this->add_enemy(EnemyType::DARK_GUNNER);
|
||||
this->add_enemy(e.floor, EnemyType::DARK_GUNNER);
|
||||
break;
|
||||
case 0xA3:
|
||||
this->add_enemy(EnemyType::DEATH_GUNNER);
|
||||
this->add_enemy(e.floor, EnemyType::DEATH_GUNNER);
|
||||
break;
|
||||
case 0xA4:
|
||||
this->add_enemy(EnemyType::CHAOS_BRINGER);
|
||||
this->add_enemy(e.floor, EnemyType::CHAOS_BRINGER);
|
||||
break;
|
||||
case 0xA5:
|
||||
this->add_enemy(EnemyType::DARK_BELRA);
|
||||
this->add_enemy(e.floor, EnemyType::DARK_BELRA);
|
||||
break;
|
||||
case 0xA6: {
|
||||
static const EnemyType types[3] = {EnemyType::DIMENIAN, EnemyType::LA_DIMENIAN, EnemyType::SO_DIMENIAN};
|
||||
this->add_enemy(types[e.uparam1 % 3]);
|
||||
this->add_enemy(e.floor, types[e.uparam1 % 3]);
|
||||
break;
|
||||
}
|
||||
case 0xA7:
|
||||
this->add_enemy(EnemyType::BULCLAW);
|
||||
this->add_enemy(e.floor, EnemyType::BULCLAW);
|
||||
for (size_t x = 0; x < 4; x++) {
|
||||
this->add_enemy(EnemyType::CLAW);
|
||||
this->add_enemy(e.floor, EnemyType::CLAW);
|
||||
}
|
||||
break;
|
||||
case 0xA8:
|
||||
this->add_enemy(EnemyType::CLAW);
|
||||
this->add_enemy(e.floor, EnemyType::CLAW);
|
||||
break;
|
||||
case 0xC0:
|
||||
if (episode == Episode::EP1) {
|
||||
this->add_enemy(EnemyType::DRAGON);
|
||||
this->add_enemy(e.floor, EnemyType::DRAGON);
|
||||
} else if (episode == Episode::EP2) {
|
||||
this->add_enemy(EnemyType::GAL_GRYPHON);
|
||||
this->add_enemy(e.floor, EnemyType::GAL_GRYPHON);
|
||||
} else {
|
||||
throw runtime_error("DRAGON-type enemy placed outside of Episodes 1 or 2");
|
||||
}
|
||||
break;
|
||||
case 0xC1:
|
||||
this->add_enemy(EnemyType::DE_ROL_LE);
|
||||
this->add_enemy(e.floor, EnemyType::DE_ROL_LE);
|
||||
for (size_t z = 0; z < 0x0A; z++) {
|
||||
this->add_enemy(EnemyType::DE_ROL_LE_BODY);
|
||||
this->add_enemy(e.floor, EnemyType::DE_ROL_LE_BODY);
|
||||
}
|
||||
for (size_t z = 0; z < 0x09; z++) {
|
||||
this->add_enemy(EnemyType::DE_ROL_LE_MINE);
|
||||
this->add_enemy(e.floor, EnemyType::DE_ROL_LE_MINE);
|
||||
}
|
||||
break;
|
||||
case 0xC2:
|
||||
this->add_enemy(EnemyType::VOL_OPT_1);
|
||||
this->add_enemy(e.floor, EnemyType::VOL_OPT_1);
|
||||
for (size_t z = 0; z < 6; z++) {
|
||||
this->add_enemy(EnemyType::VOL_OPT_PILLAR);
|
||||
this->add_enemy(e.floor, EnemyType::VOL_OPT_PILLAR);
|
||||
}
|
||||
for (size_t z = 0; z < 24; z++) {
|
||||
this->add_enemy(EnemyType::VOL_OPT_MONITOR);
|
||||
this->add_enemy(e.floor, EnemyType::VOL_OPT_MONITOR);
|
||||
}
|
||||
for (size_t z = 0; z < 2; z++) {
|
||||
this->add_enemy(EnemyType::NONE);
|
||||
this->add_enemy(e.floor, EnemyType::NONE);
|
||||
}
|
||||
this->add_enemy(EnemyType::VOL_OPT_AMP);
|
||||
this->add_enemy(EnemyType::VOL_OPT_CORE);
|
||||
this->add_enemy(EnemyType::NONE);
|
||||
this->add_enemy(e.floor, EnemyType::VOL_OPT_AMP);
|
||||
this->add_enemy(e.floor, EnemyType::VOL_OPT_CORE);
|
||||
this->add_enemy(e.floor, EnemyType::NONE);
|
||||
break;
|
||||
case 0xC5:
|
||||
this->add_enemy(EnemyType::VOL_OPT_2);
|
||||
this->add_enemy(e.floor, EnemyType::VOL_OPT_2);
|
||||
break;
|
||||
case 0xC8:
|
||||
if (difficulty) {
|
||||
this->add_enemy(EnemyType::DARK_FALZ_3);
|
||||
this->add_enemy(e.floor, EnemyType::DARK_FALZ_3);
|
||||
} else {
|
||||
this->add_enemy(EnemyType::DARK_FALZ_2);
|
||||
this->add_enemy(e.floor, EnemyType::DARK_FALZ_2);
|
||||
}
|
||||
for (size_t x = 0; x < 0x1FD; x++) {
|
||||
this->add_enemy(difficulty == 3 ? EnemyType::DARVANT_ULTIMATE : EnemyType::DARVANT);
|
||||
this->add_enemy(e.floor, difficulty == 3 ? EnemyType::DARVANT_ULTIMATE : EnemyType::DARVANT);
|
||||
}
|
||||
this->add_enemy(EnemyType::DARK_FALZ_3);
|
||||
this->add_enemy(EnemyType::DARK_FALZ_2);
|
||||
this->add_enemy(EnemyType::DARK_FALZ_1);
|
||||
this->add_enemy(e.floor, EnemyType::DARK_FALZ_3);
|
||||
this->add_enemy(e.floor, EnemyType::DARK_FALZ_2);
|
||||
this->add_enemy(e.floor, EnemyType::DARK_FALZ_1);
|
||||
break;
|
||||
case 0xCA:
|
||||
for (size_t z = 0; z < 0x201; z++) {
|
||||
this->add_enemy(EnemyType::OLGA_FLOW_2);
|
||||
this->add_enemy(e.floor, EnemyType::OLGA_FLOW_2);
|
||||
}
|
||||
break;
|
||||
case 0xCB:
|
||||
this->add_enemy(EnemyType::BARBA_RAY);
|
||||
this->add_enemy(e.floor, EnemyType::BARBA_RAY);
|
||||
for (size_t z = 0; z < 0x2F; z++) {
|
||||
this->add_enemy(EnemyType::PIG_RAY);
|
||||
this->add_enemy(e.floor, EnemyType::PIG_RAY);
|
||||
}
|
||||
break;
|
||||
case 0xCC:
|
||||
for (size_t z = 0; z < 6; z++) {
|
||||
this->add_enemy(EnemyType::GOL_DRAGON);
|
||||
this->add_enemy(e.floor, EnemyType::GOL_DRAGON);
|
||||
}
|
||||
break;
|
||||
case 0xD4: {
|
||||
EnemyType type = (e.uparam1 & 1) ? EnemyType::SINOW_SPIGELL : EnemyType::SINOW_BERILL;
|
||||
for (size_t z = 0; z < 5; z++) {
|
||||
this->add_enemy(type);
|
||||
this->add_enemy(e.floor, type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xD5:
|
||||
this->add_enemy((e.uparam1 & 0x01) ? EnemyType::MERILTAS : EnemyType::MERILLIA);
|
||||
this->add_enemy(e.floor, (e.uparam1 & 0x01) ? EnemyType::MERILTAS : EnemyType::MERILLIA);
|
||||
break;
|
||||
case 0xD6:
|
||||
if (e.uparam1 == 0) {
|
||||
this->add_enemy(EnemyType::MERICAROL);
|
||||
this->add_enemy(e.floor, EnemyType::MERICAROL);
|
||||
} else {
|
||||
this->add_enemy(((e.uparam1 % 3) == 2) ? EnemyType::MERICUS : EnemyType::MERIKLE);
|
||||
this->add_enemy(e.floor, ((e.uparam1 % 3) == 2) ? EnemyType::MERICUS : EnemyType::MERIKLE);
|
||||
}
|
||||
break;
|
||||
case 0xD7:
|
||||
this->add_enemy((e.uparam1 & 0x01) ? EnemyType::ZOL_GIBBON : EnemyType::UL_GIBBON);
|
||||
this->add_enemy(e.floor, (e.uparam1 & 0x01) ? EnemyType::ZOL_GIBBON : EnemyType::UL_GIBBON);
|
||||
break;
|
||||
case 0xD8:
|
||||
this->add_enemy(EnemyType::GIBBLES);
|
||||
this->add_enemy(e.floor, EnemyType::GIBBLES);
|
||||
break;
|
||||
case 0xD9:
|
||||
this->add_enemy(EnemyType::GEE);
|
||||
this->add_enemy(e.floor, EnemyType::GEE);
|
||||
break;
|
||||
case 0xDA:
|
||||
this->add_enemy(EnemyType::GI_GUE);
|
||||
this->add_enemy(e.floor, EnemyType::GI_GUE);
|
||||
break;
|
||||
case 0xDB:
|
||||
this->add_enemy(EnemyType::DELDEPTH);
|
||||
this->add_enemy(e.floor, EnemyType::DELDEPTH);
|
||||
break;
|
||||
case 0xDC:
|
||||
this->add_enemy(EnemyType::DELBITER);
|
||||
this->add_enemy(e.floor, EnemyType::DELBITER);
|
||||
break;
|
||||
case 0xDD:
|
||||
this->add_enemy((e.uparam1 & 0x01) ? EnemyType::DOLMDARL : EnemyType::DOLMOLM);
|
||||
this->add_enemy(e.floor, (e.uparam1 & 0x01) ? EnemyType::DOLMDARL : EnemyType::DOLMOLM);
|
||||
break;
|
||||
case 0xDE:
|
||||
this->add_enemy(EnemyType::MORFOS);
|
||||
this->add_enemy(e.floor, EnemyType::MORFOS);
|
||||
break;
|
||||
case 0xDF:
|
||||
this->add_enemy(EnemyType::RECOBOX);
|
||||
this->add_enemy(e.floor, EnemyType::RECOBOX);
|
||||
for (size_t x = 0; x < e.num_children; x++) {
|
||||
this->add_enemy(EnemyType::RECON);
|
||||
this->add_enemy(e.floor, EnemyType::RECON);
|
||||
}
|
||||
break;
|
||||
case 0xE0:
|
||||
if ((episode == Episode::EP2) && (e.floor > 0x0F)) {
|
||||
this->add_enemy(EnemyType::EPSILON);
|
||||
this->add_enemy(e.floor, EnemyType::EPSILON);
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
this->add_enemy(EnemyType::EPSIGUARD);
|
||||
this->add_enemy(e.floor, EnemyType::EPSIGUARD);
|
||||
}
|
||||
} else {
|
||||
this->add_enemy((e.uparam1 & 0x01) ? EnemyType::SINOW_ZELE : EnemyType::SINOW_ZOA);
|
||||
this->add_enemy(e.floor, (e.uparam1 & 0x01) ? EnemyType::SINOW_ZELE : EnemyType::SINOW_ZOA);
|
||||
}
|
||||
break;
|
||||
case 0xE1:
|
||||
this->add_enemy(EnemyType::ILL_GILL);
|
||||
this->add_enemy(e.floor, EnemyType::ILL_GILL);
|
||||
break;
|
||||
case 0x0110:
|
||||
this->add_enemy(EnemyType::ASTARK);
|
||||
this->add_enemy(e.floor, EnemyType::ASTARK);
|
||||
break;
|
||||
case 0x0111:
|
||||
if (e.floor > 0x05) {
|
||||
this->add_enemy(e.fparam2 ? EnemyType::YOWIE_ALT : EnemyType::SATELLITE_LIZARD_ALT);
|
||||
this->add_enemy(e.floor, e.fparam2 ? EnemyType::YOWIE_ALT : EnemyType::SATELLITE_LIZARD_ALT);
|
||||
} else {
|
||||
this->add_enemy(e.fparam2 ? EnemyType::YOWIE : EnemyType::SATELLITE_LIZARD);
|
||||
this->add_enemy(e.floor, e.fparam2 ? EnemyType::YOWIE : EnemyType::SATELLITE_LIZARD);
|
||||
}
|
||||
break;
|
||||
case 0x0112: {
|
||||
bool is_rare = this->check_and_log_rare_enemy(e.uparam1 & 0x01, rare_rates.merissa_aa);
|
||||
this->add_enemy(is_rare ? EnemyType::MERISSA_AA : EnemyType::MERISSA_A);
|
||||
this->add_enemy(e.floor, is_rare ? EnemyType::MERISSA_AA : EnemyType::MERISSA_A);
|
||||
break;
|
||||
}
|
||||
case 0x0113:
|
||||
this->add_enemy(EnemyType::GIRTABLULU);
|
||||
this->add_enemy(e.floor, EnemyType::GIRTABLULU);
|
||||
break;
|
||||
case 0x0114: {
|
||||
bool is_rare = this->check_and_log_rare_enemy(e.uparam1 & 0x01, rare_rates.pazuzu);
|
||||
if (e.floor > 0x05) {
|
||||
this->add_enemy(is_rare ? EnemyType::PAZUZU_ALT : EnemyType::ZU_ALT);
|
||||
this->add_enemy(e.floor, is_rare ? EnemyType::PAZUZU_ALT : EnemyType::ZU_ALT);
|
||||
} else {
|
||||
this->add_enemy(is_rare ? EnemyType::PAZUZU : EnemyType::ZU);
|
||||
this->add_enemy(e.floor, is_rare ? EnemyType::PAZUZU : EnemyType::ZU);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x0115:
|
||||
if (e.uparam1 & 2) {
|
||||
this->add_enemy(EnemyType::BA_BOOTA);
|
||||
this->add_enemy(e.floor, EnemyType::BA_BOOTA);
|
||||
} else {
|
||||
this->add_enemy((e.uparam1 & 1) ? EnemyType::ZE_BOOTA : EnemyType::BOOTA);
|
||||
this->add_enemy(e.floor, (e.uparam1 & 1) ? EnemyType::ZE_BOOTA : EnemyType::BOOTA);
|
||||
}
|
||||
break;
|
||||
case 0x0116: {
|
||||
bool is_rare = this->check_and_log_rare_enemy(e.uparam1 & 0x01, rare_rates.dorphon_eclair);
|
||||
this->add_enemy(is_rare ? EnemyType::DORPHON_ECLAIR : EnemyType::DORPHON);
|
||||
this->add_enemy(e.floor, is_rare ? EnemyType::DORPHON_ECLAIR : EnemyType::DORPHON);
|
||||
break;
|
||||
}
|
||||
case 0x0117: {
|
||||
static const EnemyType types[3] = {EnemyType::GORAN, EnemyType::PYRO_GORAN, EnemyType::GORAN_DETONATOR};
|
||||
this->add_enemy(types[e.uparam1 % 3]);
|
||||
this->add_enemy(e.floor, types[e.uparam1 % 3]);
|
||||
break;
|
||||
}
|
||||
case 0x0119: {
|
||||
bool is_rare = this->check_and_log_rare_enemy((e.fparam2 != 0.0f), rare_rates.kondrieu);
|
||||
if (is_rare) {
|
||||
this->add_enemy(EnemyType::KONDRIEU);
|
||||
this->add_enemy(e.floor, EnemyType::KONDRIEU);
|
||||
} else {
|
||||
this->add_enemy((e.uparam1 & 1) ? EnemyType::SHAMBERTIN : EnemyType::SAINT_MILLION);
|
||||
this->add_enemy(e.floor, (e.uparam1 & 1) ? EnemyType::SHAMBERTIN : EnemyType::SAINT_MILLION);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
for (size_t z = 0; z < static_cast<size_t>(e.num_children + 1); z++) {
|
||||
this->add_enemy(EnemyType::UNKNOWN);
|
||||
this->add_enemy(e.floor, EnemyType::UNKNOWN);
|
||||
}
|
||||
static_game_data_log.warning(
|
||||
"(Entry %zu, offset %zX in file) Unknown enemy type %04hX",
|
||||
@@ -844,7 +845,7 @@ struct AreaMapFileIndex {
|
||||
variation2_values(variation2_values) {}
|
||||
};
|
||||
|
||||
// These are indexed as [episode][is_solo][area], where episode is 0-2
|
||||
// These are indexed as [episode][is_solo][floor], where episode is 0-2
|
||||
static const vector<vector<vector<AreaMapFileIndex>>> map_file_info = {
|
||||
{
|
||||
// Episode 1
|
||||
@@ -1009,7 +1010,7 @@ void generate_variations(
|
||||
}
|
||||
|
||||
vector<string> map_filenames_for_variation(
|
||||
Episode episode, bool is_solo, uint8_t area, uint32_t var1, uint32_t var2, bool is_enemies) {
|
||||
Episode episode, bool is_solo, uint8_t floor, uint32_t var1, uint32_t var2, bool is_enemies) {
|
||||
// Map filenames are like map_<name_token>[_VV][_VV][_off]<e|o>[_s].dat
|
||||
// name_token comes from AreaMapFileIndex
|
||||
// _VV are the values from the variation<1|2>_values vector (in contrast to
|
||||
@@ -1020,10 +1021,10 @@ vector<string> map_filenames_for_variation(
|
||||
const auto& ep_index = map_file_info_for_episode(episode);
|
||||
const AreaMapFileIndex* a = nullptr;
|
||||
if (is_solo) {
|
||||
a = &ep_index.at(true).at(area);
|
||||
a = &ep_index.at(true).at(floor);
|
||||
}
|
||||
if (!a || !a->name_token) {
|
||||
a = &ep_index.at(false).at(area);
|
||||
a = &ep_index.at(false).at(floor);
|
||||
}
|
||||
if (!a->name_token) {
|
||||
return vector<string>();
|
||||
|
||||
+6
-5
@@ -202,10 +202,11 @@ struct Map {
|
||||
ITEM_DROPPED = 0x20,
|
||||
};
|
||||
EnemyType type;
|
||||
uint8_t floor;
|
||||
uint8_t flags;
|
||||
uint8_t last_hit_by_client_id;
|
||||
|
||||
explicit Enemy(EnemyType type);
|
||||
Enemy(uint8_t floor, EnemyType type);
|
||||
|
||||
std::string str() const;
|
||||
} __attribute__((packed));
|
||||
@@ -216,7 +217,7 @@ 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(uint8_t floor, EnemyType type);
|
||||
void add_enemy(
|
||||
Episode episode,
|
||||
uint8_t difficulty,
|
||||
@@ -274,8 +275,8 @@ private:
|
||||
template <bool IsBigEndian>
|
||||
void load_table_t(std::shared_ptr<const std::string> data);
|
||||
|
||||
// Indexes are [area_id][variation1][variation2]
|
||||
// area_id is cumulative per episode, so Ep2 starts at area_id=18.
|
||||
// Indexes are [floor][variation1][variation2]
|
||||
// floor is cumulative per episode, so Ep2 starts at floor=18.
|
||||
std::vector<std::vector<std::vector<SetEntry>>> entries;
|
||||
};
|
||||
|
||||
@@ -285,5 +286,5 @@ void generate_variations(
|
||||
Episode episode,
|
||||
bool is_solo);
|
||||
std::vector<std::string> map_filenames_for_variation(
|
||||
Episode episode, bool is_solo, uint8_t area, uint32_t var1, uint32_t var2, bool is_enemies);
|
||||
Episode episode, bool is_solo, uint8_t floor, uint32_t var1, uint32_t var2, bool is_enemies);
|
||||
void load_map_files();
|
||||
|
||||
@@ -996,8 +996,8 @@ static HandlerResult S_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
const auto& cmd = check_size_t<G_StandardDropItemRequest_DC_6x60>(
|
||||
data, sizeof(G_StandardDropItemRequest_PC_V3_BB_6x60));
|
||||
ses->next_drop_item.id = ses->next_item_id++;
|
||||
send_drop_item(s, ses->server_channel, ses->next_drop_item, true, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item(s, ses->client_channel, ses->next_drop_item, true, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item(s, ses->server_channel, ses->next_drop_item, true, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item(s, ses->client_channel, ses->next_drop_item, true, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
ses->next_drop_item.clear();
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
|
||||
@@ -1008,8 +1008,8 @@ static HandlerResult S_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
} else if ((static_cast<uint8_t>(data[0]) == 0xA2) && ses->next_drop_item.data1d[0] && (ses->version() != GameVersion::BB)) {
|
||||
const auto& cmd = check_size_t<G_SpecializableItemDropRequest_6xA2>(data);
|
||||
ses->next_drop_item.id = ses->next_item_id++;
|
||||
send_drop_item(s, ses->server_channel, ses->next_drop_item, false, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item(s, ses->client_channel, ses->next_drop_item, false, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item(s, ses->server_channel, ses->next_drop_item, false, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item(s, ses->client_channel, ses->next_drop_item, false, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
ses->next_drop_item.clear();
|
||||
return HandlerResult::Type::SUPPRESS;
|
||||
|
||||
@@ -1344,7 +1344,7 @@ static HandlerResult S_65_67_68_EB(shared_ptr<ProxyServer::LinkedSession> ses, u
|
||||
ses->clear_lobby_players(12);
|
||||
ses->is_in_game = false;
|
||||
ses->is_in_quest = false;
|
||||
ses->area = 0x0F;
|
||||
ses->floor = 0x0F;
|
||||
|
||||
// This command can cause the client to no longer send D6 responses when
|
||||
// 1A/D5 large message boxes are closed. newserv keeps track of this
|
||||
@@ -1423,7 +1423,7 @@ static HandlerResult S_64(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
}
|
||||
|
||||
ses->clear_lobby_players(4);
|
||||
ses->area = 0;
|
||||
ses->floor = 0;
|
||||
ses->is_in_game = true;
|
||||
ses->is_in_quest = false;
|
||||
|
||||
@@ -1477,7 +1477,7 @@ static HandlerResult S_E8(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t,
|
||||
auto& cmd = check_size_t<S_JoinSpectatorTeam_GC_Ep3_E8>(data);
|
||||
|
||||
ses->clear_lobby_players(12);
|
||||
ses->area = 0;
|
||||
ses->floor = 0;
|
||||
ses->is_in_game = true;
|
||||
ses->is_in_quest = false;
|
||||
|
||||
@@ -1555,7 +1555,7 @@ static HandlerResult S_66_69_E9(shared_ptr<ProxyServer::LinkedSession> ses, uint
|
||||
}
|
||||
|
||||
static HandlerResult C_98(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t command, uint32_t flag, string& data) {
|
||||
ses->area = 0x0F;
|
||||
ses->floor = 0x0F;
|
||||
ses->is_in_game = false;
|
||||
ses->is_in_quest = false;
|
||||
if (ses->version() == GameVersion::GC ||
|
||||
@@ -1681,7 +1681,7 @@ static HandlerResult C_6x(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t c
|
||||
if (!data.empty()) {
|
||||
if (data[0] == 0x21) {
|
||||
const auto& cmd = check_size_t<G_InterLevelWarp_6x21>(data);
|
||||
ses->area = cmd.area;
|
||||
ses->floor = cmd.floor;
|
||||
|
||||
} else if (data[0] == 0x2F || data[0] == 0x4B || data[0] == 0x4C) {
|
||||
if (ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
|
||||
+1
-1
@@ -489,7 +489,7 @@ ProxyServer::LinkedSession::LinkedSession(
|
||||
lobby_players(12),
|
||||
lobby_client_id(0),
|
||||
leader_client_id(0),
|
||||
area(0),
|
||||
floor(0),
|
||||
x(0.0),
|
||||
z(0.0),
|
||||
is_in_game(false),
|
||||
|
||||
+1
-1
@@ -86,7 +86,7 @@ public:
|
||||
std::vector<LobbyPlayer> lobby_players;
|
||||
size_t lobby_client_id;
|
||||
size_t leader_client_id;
|
||||
uint16_t area;
|
||||
uint16_t floor;
|
||||
float x;
|
||||
float z;
|
||||
bool is_in_game;
|
||||
|
||||
+2
-2
@@ -319,7 +319,7 @@ RareItemSet::RareItemSet(const JSON& json, GameVersion version, shared_ptr<const
|
||||
for (const auto& item_it : section_id_it.second->as_dict()) {
|
||||
vector<ExpandedDrop>* target;
|
||||
if (starts_with(item_it.first, "Box-")) {
|
||||
uint8_t area = area_for_name(item_it.first.substr(4));
|
||||
uint8_t area = floor_for_name(item_it.first.substr(4));
|
||||
if (collection.box_area_to_specs.size() <= area) {
|
||||
collection.box_area_to_specs.resize(area + 1);
|
||||
}
|
||||
@@ -484,7 +484,7 @@ std::string RareItemSet::serialize_json(GameVersion version, shared_ptr<const It
|
||||
|
||||
if (!area_list.empty()) {
|
||||
collection_dict.emplace(
|
||||
string_printf("Box-%s", name_for_area(episode, area)),
|
||||
string_printf("Box-%s", name_for_floor(episode, area)),
|
||||
std::move(area_list));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3720,19 +3720,19 @@ shared_ptr<Lobby> create_game_generic(
|
||||
|
||||
if (game->base_version == GameVersion::BB) {
|
||||
game->map.reset(new Map());
|
||||
for (size_t area = 0; area < 0x10; area++) {
|
||||
for (size_t floor = 0; floor < 0x10; floor++) {
|
||||
c->log.info("[Map/%zu] Using variations %" PRIX32 ", %" PRIX32,
|
||||
area, game->variations[area * 2].load(), game->variations[area * 2 + 1].load());
|
||||
floor, game->variations[floor * 2].load(), game->variations[floor * 2 + 1].load());
|
||||
|
||||
auto enemy_filenames = map_filenames_for_variation(
|
||||
game->episode,
|
||||
is_solo,
|
||||
area,
|
||||
game->variations[area * 2],
|
||||
game->variations[area * 2 + 1],
|
||||
floor,
|
||||
game->variations[floor * 2],
|
||||
game->variations[floor * 2 + 1],
|
||||
true);
|
||||
if (enemy_filenames.empty()) {
|
||||
c->log.info("[Map/%zu:e] No file to load", area);
|
||||
c->log.info("[Map/%zu:e] No file to load", floor);
|
||||
} else {
|
||||
bool any_map_loaded = false;
|
||||
for (const string& filename : enemy_filenames) {
|
||||
@@ -3743,7 +3743,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
game->episode, game->difficulty, game->event, map_data->data(), map_data->size());
|
||||
size_t entries_loaded = game->map->enemies.size() - start_offset;
|
||||
c->log.info("[Map/%zu:e] Loaded %s (%zu entries)",
|
||||
area, filename.c_str(), entries_loaded);
|
||||
floor, filename.c_str(), entries_loaded);
|
||||
for (size_t z = start_offset; z < game->map->enemies.size(); z++) {
|
||||
string e_str = game->map->enemies[z].str();
|
||||
static_game_data_log.info("(Entry %zX) %s", z, e_str.c_str());
|
||||
@@ -3751,11 +3751,11 @@ shared_ptr<Lobby> create_game_generic(
|
||||
any_map_loaded = true;
|
||||
break;
|
||||
} catch (const exception& e) {
|
||||
c->log.info("[Map/%zu:e] Failed to load %s: %s", area, filename.c_str(), e.what());
|
||||
c->log.info("[Map/%zu:e] Failed to load %s: %s", floor, filename.c_str(), e.what());
|
||||
}
|
||||
}
|
||||
if (!any_map_loaded) {
|
||||
throw runtime_error(string_printf("no enemy maps loaded for area %zu", area));
|
||||
throw runtime_error(string_printf("no enemy maps loaded for floor %zu", floor));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+38
-34
@@ -206,8 +206,8 @@ static void on_sync_joining_player_item_state(shared_ptr<Client> c, uint8_t comm
|
||||
auto* decompressed_cmd = reinterpret_cast<G_SyncItemState_6x6D_Decompressed*>(decompressed.data());
|
||||
|
||||
size_t num_floor_items = 0;
|
||||
for (size_t z = 0; z < decompressed_cmd->floor_item_count_per_area.size(); z++) {
|
||||
num_floor_items += decompressed_cmd->floor_item_count_per_area[z];
|
||||
for (size_t z = 0; z < decompressed_cmd->floor_item_count_per_floor.size(); z++) {
|
||||
num_floor_items += decompressed_cmd->floor_item_count_per_floor[z];
|
||||
}
|
||||
|
||||
size_t required_size = sizeof(G_SyncItemState_6x6D_Decompressed) + num_floor_items * sizeof(FloorItem);
|
||||
@@ -559,9 +559,9 @@ static void on_set_player_visibility(shared_ptr<Client> c, uint8_t command, uint
|
||||
// Game commands used by cheat mechanisms
|
||||
|
||||
template <typename CmdT>
|
||||
static void on_change_area(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
static void on_change_floor(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<CmdT>(data, size);
|
||||
c->area = cmd.area;
|
||||
c->floor = cmd.floor;
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
}
|
||||
|
||||
@@ -684,7 +684,7 @@ void on_movement(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void
|
||||
}
|
||||
|
||||
template <typename CmdT>
|
||||
void on_movement_with_area(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
void on_movement_with_floor(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<CmdT>(data, size);
|
||||
if (cmd.header.client_id != c->lobby_client_id) {
|
||||
return;
|
||||
@@ -692,7 +692,7 @@ void on_movement_with_area(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
|
||||
c->x = cmd.x;
|
||||
c->z = cmd.z;
|
||||
c->area = cmd.area;
|
||||
c->floor = cmd.floor;
|
||||
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
}
|
||||
@@ -711,12 +711,12 @@ static void on_player_drop_item(shared_ptr<Client> c, uint8_t command, uint8_t f
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
auto p = c->game_data.character();
|
||||
auto item = p->remove_item(cmd.item_id, 0, c->version() != GameVersion::BB);
|
||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||
l->add_item(item, cmd.floor, cmd.x, cmd.z);
|
||||
|
||||
auto s = c->require_server_state();
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hu dropped item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(), cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(), cmd.floor.load(), cmd.x.load(), cmd.z.load());
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
auto name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s",
|
||||
@@ -816,13 +816,13 @@ static void on_drop_partial_stack_t(shared_ptr<Client> c, uint8_t command, uint8
|
||||
ItemData item = cmd.item_data;
|
||||
item.decode_for_version(c->version());
|
||||
l->on_item_id_generated_externally(item.id);
|
||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||
l->add_item(item, cmd.floor, cmd.x, cmd.z);
|
||||
|
||||
auto s = c->require_server_state();
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hu split stack to create floor item %08" PRIX32 " (%s) at %hu:(%g, %g)",
|
||||
cmd.header.client_id.load(), item.id.load(), name.c_str(),
|
||||
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||
cmd.floor.load(), cmd.x.load(), cmd.z.load());
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5SPLIT %08" PRIX32 "\n%s", item.id.load(), name.c_str());
|
||||
@@ -871,13 +871,13 @@ static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint
|
||||
// removed again by the 6x29 handler)
|
||||
p->add_item(item);
|
||||
|
||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||
l->add_item(item, cmd.floor, cmd.x, cmd.z);
|
||||
|
||||
auto s = c->require_server_state();
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hu split stack %08" PRIX32 " (removed: %s) at %hu:(%g, %g)",
|
||||
cmd.header.client_id.load(), cmd.item_id.load(), name.c_str(),
|
||||
cmd.area.load(), cmd.x.load(), cmd.z.load());
|
||||
cmd.floor.load(), cmd.x.load(), cmd.z.load());
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
auto name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5SPLIT/BB %08" PRIX32 "\n%s",
|
||||
@@ -885,7 +885,7 @@ static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint
|
||||
}
|
||||
p->print_inventory(stderr, c->version(), s->item_name_index);
|
||||
|
||||
send_drop_stacked_item(l, item, cmd.area, cmd.x, cmd.z);
|
||||
send_drop_stacked_item(l, item, cmd.floor, cmd.x, cmd.z);
|
||||
|
||||
} else {
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
@@ -950,12 +950,12 @@ static void on_box_or_enemy_item_drop_t(shared_ptr<Client> c, uint8_t command, u
|
||||
ItemData item = cmd.item.item;
|
||||
item.decode_for_version(c->version());
|
||||
l->on_item_id_generated_externally(item.id);
|
||||
l->add_item(item, cmd.item.area, cmd.item.x, cmd.item.z);
|
||||
l->add_item(item, cmd.item.floor, cmd.item.x, cmd.item.z);
|
||||
|
||||
auto s = c->require_server_state();
|
||||
auto name = s->describe_item(c->version(), item, false);
|
||||
l->log.info("Player %hhu (leader) created floor item %08" PRIX32 " (%s) at %hhu:(%g, %g)",
|
||||
l->leader_id, item.id.load(), name.c_str(), cmd.item.area, cmd.item.x.load(), cmd.item.z.load());
|
||||
l->leader_id, item.id.load(), name.c_str(), cmd.item.floor, cmd.item.x.load(), cmd.item.z.load());
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
string name = s->describe_item(c->version(), item, true);
|
||||
send_text_message_printf(c, "$C5DROP %08" PRIX32 "\n%s", item.id.load(), name.c_str());
|
||||
@@ -1087,7 +1087,7 @@ static void on_pick_up_item_request(shared_ptr<Client> c, uint8_t command, uint8
|
||||
}
|
||||
p->print_inventory(stderr, c->version(), s->item_name_index);
|
||||
|
||||
send_pick_up_item(c, cmd.item_id, cmd.area);
|
||||
send_pick_up_item(c, cmd.item_id, cmd.floor);
|
||||
|
||||
} else if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED) && !l->item_exists(cmd.item_id)) {
|
||||
l->log.warning("Player %hu requests to pick up %08" PRIX32 ", but the item does not exist; dropping command",
|
||||
@@ -1414,7 +1414,7 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
||||
throw runtime_error("item drop request has incorrect subcommand");
|
||||
}
|
||||
cmd.entity_id = in_cmd.entity_id;
|
||||
cmd.area = in_cmd.area;
|
||||
cmd.floor = in_cmd.floor;
|
||||
cmd.rt_index = in_cmd.rt_index;
|
||||
cmd.x = in_cmd.x;
|
||||
cmd.z = in_cmd.z;
|
||||
@@ -1426,12 +1426,12 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
||||
throw runtime_error("item drop request has incorrect subcommand");
|
||||
}
|
||||
cmd.entity_id = in_cmd.entity_id;
|
||||
cmd.area = in_cmd.area;
|
||||
cmd.floor = in_cmd.floor;
|
||||
cmd.rt_index = in_cmd.rt_index;
|
||||
cmd.x = in_cmd.x;
|
||||
cmd.z = in_cmd.z;
|
||||
cmd.ignore_def = true;
|
||||
cmd.effective_area = in_cmd.area;
|
||||
cmd.effective_area = in_cmd.floor;
|
||||
}
|
||||
|
||||
ItemData item;
|
||||
@@ -1453,6 +1453,10 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
||||
c->log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32,
|
||||
cmd.rt_index, expected_rt_index);
|
||||
}
|
||||
if (cmd.floor != enemy.floor) {
|
||||
c->log.warning("Floor %02hhX from command does not match entity\'s expected floor %02hhX",
|
||||
cmd.floor, enemy.floor);
|
||||
}
|
||||
}
|
||||
item = l->item_creator->on_monster_item_drop(cmd.entity_id, cmd.rt_index, cmd.effective_area);
|
||||
}
|
||||
@@ -1463,11 +1467,11 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
|
||||
item.id = l->generate_item_id(0xFF);
|
||||
string name = s->item_name_index->describe_item(l->base_version, item);
|
||||
l->log.info("Entity %04hX (area %02hX) created item %s", cmd.entity_id.load(), cmd.effective_area, name.c_str());
|
||||
l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g", item.id.load(), cmd.area, cmd.x.load(), cmd.z.load());
|
||||
l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g", item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load());
|
||||
if (l->check_flag(Lobby::Flag::ITEM_TRACKING_ENABLED)) {
|
||||
l->add_item(item, cmd.area, cmd.x, cmd.z);
|
||||
l->add_item(item, cmd.floor, cmd.x, cmd.z);
|
||||
}
|
||||
send_drop_item(l, item, cmd.rt_index != 0x30, cmd.area, cmd.x, cmd.z, cmd.entity_id);
|
||||
send_drop_item(l, item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1512,7 +1516,7 @@ static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t fla
|
||||
if (c->version() == GameVersion::GC) {
|
||||
bool should_send_boss_drop_req = false;
|
||||
bool is_ep2 = (l->episode == Episode::EP2);
|
||||
if ((l->episode == Episode::EP1) && (c->area == 0x0E)) {
|
||||
if ((l->episode == Episode::EP1) && (c->floor == 0x0E)) {
|
||||
// On Normal, Dark Falz does not have a third phase, so send the drop
|
||||
// request after the end of the second phase. On all other difficulty
|
||||
// levels, send it after the third phase.
|
||||
@@ -1520,7 +1524,7 @@ static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t fla
|
||||
((difficulty != 0) && (flag_index == 0x0037))) {
|
||||
should_send_boss_drop_req = true;
|
||||
}
|
||||
} else if (is_ep2 && (flag_index == 0x0057) && (c->area == 0x0D)) {
|
||||
} else if (is_ep2 && (flag_index == 0x0057) && (c->floor == 0x0D)) {
|
||||
should_send_boss_drop_req = true;
|
||||
}
|
||||
|
||||
@@ -1530,7 +1534,7 @@ static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t fla
|
||||
G_StandardDropItemRequest_PC_V3_BB_6x60 req = {
|
||||
{
|
||||
{0x60, 0x06, 0x0000},
|
||||
static_cast<uint8_t>(c->area),
|
||||
static_cast<uint8_t>(c->floor),
|
||||
static_cast<uint8_t>(is_ep2 ? 0x4E : 0x2F),
|
||||
0x0B4F,
|
||||
is_ep2 ? -9999.0f : 10160.58984375f,
|
||||
@@ -1755,7 +1759,7 @@ static void on_steal_exp_bb(shared_ptr<Client> c, uint8_t, uint8_t, const void*
|
||||
add_player_exp(c, stolen_exp);
|
||||
}
|
||||
|
||||
static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t command, uint8_t flag, const void* data, size_t size) {
|
||||
static void on_enemy_killed_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();
|
||||
|
||||
@@ -1763,8 +1767,6 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t command, uint8_t fl
|
||||
throw runtime_error("BB-only command sent in non-BB game");
|
||||
}
|
||||
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
|
||||
const auto& cmd = check_size_t<G_EnemyKilled_BB_6xC8>(data, size);
|
||||
|
||||
if (!l->is_game()) {
|
||||
@@ -1806,11 +1808,13 @@ static void on_enemy_killed_bb(shared_ptr<Client> c, uint8_t command, uint8_t fl
|
||||
if (!((e.flags >> x) & 1)) {
|
||||
continue; // Player did not hit this enemy
|
||||
}
|
||||
|
||||
auto other_c = l->clients[x];
|
||||
if (!other_c) {
|
||||
continue; // No player
|
||||
}
|
||||
if (other_c->floor != e.floor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (experience != 0xFFFFFFFF) {
|
||||
// Killer gets full experience, others get 77%
|
||||
@@ -2307,9 +2311,9 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
||||
/* 6x1C */ on_forward_check_size_game,
|
||||
/* 6x1D */ nullptr,
|
||||
/* 6x1E */ nullptr,
|
||||
/* 6x1F */ on_change_area<G_SetPlayerArea_6x1F>,
|
||||
/* 6x20 */ on_movement_with_area<G_SetPosition_6x20>,
|
||||
/* 6x21 */ on_change_area<G_InterLevelWarp_6x21>,
|
||||
/* 6x1F */ on_change_floor<G_SetPlayerArea_6x1F>,
|
||||
/* 6x20 */ on_movement_with_floor<G_SetPosition_6x20>,
|
||||
/* 6x21 */ on_change_floor<G_InterLevelWarp_6x21>,
|
||||
/* 6x22 */ on_forward_check_size_client,
|
||||
/* 6x23 */ on_set_player_visibility,
|
||||
/* 6x24 */ on_forward_check_size_game,
|
||||
@@ -2338,8 +2342,8 @@ subcommand_handler_t subcommand_handlers[0x100] = {
|
||||
/* 6x3B */ on_forward_check_size,
|
||||
/* 6x3C */ nullptr,
|
||||
/* 6x3D */ nullptr,
|
||||
/* 6x3E */ on_movement_with_area<G_StopAtPosition_6x3E>,
|
||||
/* 6x3F */ on_movement_with_area<G_SetPosition_6x3F>,
|
||||
/* 6x3E */ on_movement_with_floor<G_StopAtPosition_6x3E>,
|
||||
/* 6x3F */ on_movement_with_floor<G_SetPosition_6x3F>,
|
||||
/* 6x40 */ on_movement<G_WalkToPosition_6x40>,
|
||||
/* 6x41 */ nullptr,
|
||||
/* 6x42 */ on_movement<G_RunToPosition_6x42>,
|
||||
|
||||
+17
-17
@@ -2100,20 +2100,20 @@ void send_player_stats_change(Channel& ch, uint16_t client_id, PlayerStatsChange
|
||||
send_command_vt(ch, (subs.size() > 0x400 / sizeof(G_UpdatePlayerStat_6x9A)) ? 0x6C : 0x60, 0x00, subs);
|
||||
}
|
||||
|
||||
void send_warp(Channel& ch, uint8_t client_id, uint32_t area, bool is_private) {
|
||||
G_InterLevelWarp_6x94 cmd = {{0x94, 0x02, 0}, area, {}};
|
||||
void send_warp(Channel& ch, uint8_t client_id, uint32_t floor, bool is_private) {
|
||||
G_InterLevelWarp_6x94 cmd = {{0x94, 0x02, 0}, floor, {}};
|
||||
ch.send(is_private ? 0x62 : 0x60, client_id, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void send_warp(shared_ptr<Client> c, uint32_t area, bool is_private) {
|
||||
send_warp(c->channel, c->lobby_client_id, area, is_private);
|
||||
c->area = area;
|
||||
void send_warp(shared_ptr<Client> c, uint32_t floor, bool is_private) {
|
||||
send_warp(c->channel, c->lobby_client_id, floor, is_private);
|
||||
c->floor = floor;
|
||||
}
|
||||
|
||||
void send_warp(shared_ptr<Lobby> l, uint32_t area, bool is_private) {
|
||||
void send_warp(shared_ptr<Lobby> l, uint32_t floor, bool is_private) {
|
||||
for (const auto& c : l->clients) {
|
||||
if (c) {
|
||||
send_warp(c, area, is_private);
|
||||
send_warp(c, floor, is_private);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2135,44 +2135,44 @@ void send_set_player_visibility(shared_ptr<Lobby> l, shared_ptr<Client> c,
|
||||
// BB game commands
|
||||
|
||||
void send_drop_item(shared_ptr<ServerState> s, Channel& ch, const ItemData& item,
|
||||
bool from_enemy, uint8_t area, float x, float z, uint16_t entity_id) {
|
||||
bool from_enemy, uint8_t floor, float x, float z, uint16_t entity_id) {
|
||||
G_DropItem_PC_V3_BB_6x5F cmd = {
|
||||
{{0x5F, 0x0B, 0x0000}, {area, from_enemy, entity_id, x, z, 0, 0, item}}, 0};
|
||||
{{0x5F, 0x0B, 0x0000}, {floor, from_enemy, entity_id, x, z, 0, 0, item}}, 0};
|
||||
cmd.item.item.encode_for_version(ch.version, s->item_parameter_table_for_version(ch.version));
|
||||
ch.send(0x60, 0x00, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void send_drop_item(shared_ptr<Lobby> l, const ItemData& item,
|
||||
bool from_enemy, uint8_t area, float x, float z, uint16_t entity_id) {
|
||||
bool from_enemy, uint8_t floor, float x, float z, uint16_t entity_id) {
|
||||
auto s = l->require_server_state();
|
||||
for (auto& c : l->clients) {
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
send_drop_item(s, c->channel, item, from_enemy, area, x, z, entity_id);
|
||||
send_drop_item(s, c->channel, item, from_enemy, floor, x, z, entity_id);
|
||||
}
|
||||
}
|
||||
|
||||
void send_drop_stacked_item(shared_ptr<ServerState> s, Channel& ch, const ItemData& item, uint8_t area, float x, float z) {
|
||||
G_DropStackedItem_PC_V3_BB_6x5D cmd = {{{0x5D, 0x0A, 0x0000}, area, 0, x, z, item}, 0};
|
||||
void send_drop_stacked_item(shared_ptr<ServerState> s, Channel& ch, const ItemData& item, uint8_t floor, float x, float z) {
|
||||
G_DropStackedItem_PC_V3_BB_6x5D cmd = {{{0x5D, 0x0A, 0x0000}, floor, 0, x, z, item}, 0};
|
||||
cmd.item_data.encode_for_version(ch.version, s->item_parameter_table_for_version(ch.version));
|
||||
ch.send(0x60, 0x00, &cmd, sizeof(cmd));
|
||||
}
|
||||
|
||||
void send_drop_stacked_item(shared_ptr<Lobby> l, const ItemData& item, uint8_t area, float x, float z) {
|
||||
void send_drop_stacked_item(shared_ptr<Lobby> l, const ItemData& item, uint8_t floor, float x, float z) {
|
||||
auto s = l->require_server_state();
|
||||
for (auto& c : l->clients) {
|
||||
if (!c) {
|
||||
continue;
|
||||
}
|
||||
send_drop_stacked_item(s, c->channel, item, area, x, z);
|
||||
send_drop_stacked_item(s, c->channel, item, floor, x, z);
|
||||
}
|
||||
}
|
||||
|
||||
void send_pick_up_item(shared_ptr<Client> c, uint32_t item_id, uint8_t area) {
|
||||
void send_pick_up_item(shared_ptr<Client> c, uint32_t item_id, uint8_t floor) {
|
||||
auto l = c->require_lobby();
|
||||
uint16_t client_id = c->lobby_client_id;
|
||||
G_PickUpItem_6x59 cmd = {{0x59, 0x03, client_id}, client_id, area, item_id};
|
||||
G_PickUpItem_6x59 cmd = {{0x59, 0x03, client_id}, client_id, floor, item_id};
|
||||
send_command_t(l, 0x60, 0x00, cmd);
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -284,23 +284,23 @@ enum PlayerStatsChange {
|
||||
void send_player_stats_change(std::shared_ptr<Client> c, PlayerStatsChange stat, uint32_t amount);
|
||||
void send_player_stats_change(
|
||||
Channel& ch, uint16_t client_id, PlayerStatsChange stat, uint32_t amount);
|
||||
void send_warp(Channel& ch, uint8_t client_id, uint32_t area, bool is_private);
|
||||
void send_warp(std::shared_ptr<Client> c, uint32_t area, bool is_private);
|
||||
void send_warp(std::shared_ptr<Lobby> l, uint32_t area, bool is_private);
|
||||
void send_warp(Channel& ch, uint8_t client_id, uint32_t floor, bool is_private);
|
||||
void send_warp(std::shared_ptr<Client> c, uint32_t floor, bool is_private);
|
||||
void send_warp(std::shared_ptr<Lobby> l, uint32_t floor, bool is_private);
|
||||
|
||||
void send_ep3_change_music(Channel& ch, uint32_t song);
|
||||
void send_set_player_visibility(std::shared_ptr<Client> c, bool visible);
|
||||
void send_revive_player(std::shared_ptr<Client> c);
|
||||
|
||||
void send_drop_item(std::shared_ptr<ServerState> s, Channel& ch, const ItemData& item,
|
||||
bool from_enemy, uint8_t area, float x, float z, uint16_t request_id);
|
||||
bool from_enemy, uint8_t floor, float x, float z, uint16_t request_id);
|
||||
void send_drop_item(std::shared_ptr<Lobby> l, const ItemData& item,
|
||||
bool from_enemy, uint8_t area, float x, float z, uint16_t request_id);
|
||||
bool from_enemy, uint8_t floor, float x, float z, uint16_t request_id);
|
||||
void send_drop_stacked_item(std::shared_ptr<ServerState> s, Channel& ch, const ItemData& item,
|
||||
uint8_t area, float x, float z);
|
||||
uint8_t floor, float x, float z);
|
||||
void send_drop_stacked_item(std::shared_ptr<Lobby> l, const ItemData& item,
|
||||
uint8_t area, float x, float z);
|
||||
void send_pick_up_item(std::shared_ptr<Client> c, uint32_t id, uint8_t area);
|
||||
uint8_t floor, float x, float z);
|
||||
void send_pick_up_item(std::shared_ptr<Client> c, uint32_t id, uint8_t floor);
|
||||
void send_create_inventory_item(std::shared_ptr<Client> c, const ItemData& item);
|
||||
void send_destroy_item(std::shared_ptr<Client> c, uint32_t item_id, uint32_t amount);
|
||||
void send_item_identify_result(std::shared_ptr<Client> c);
|
||||
|
||||
+9
-9
@@ -230,9 +230,9 @@ Proxy session commands:\n\
|
||||
Change your lobby marker color.\n\
|
||||
warp AREA-ID\n\
|
||||
warpme AREA-ID\n\
|
||||
Send yourself to a specific area.\n\
|
||||
Send yourself to a specific floor.\n\
|
||||
warpall AREA-ID\n\
|
||||
Send everyone to a specific area.\n\
|
||||
Send everyone to a specific floor.\n\
|
||||
set-override-section-id [SECTION-ID]\n\
|
||||
Override the section ID for games you create or join. This affects the\n\
|
||||
active drop chart if you are the leader of the game and the server doesn't\n\
|
||||
@@ -706,15 +706,15 @@ Proxy session commands:\n\
|
||||
} else if ((command_name == "warp") || (command_name == "warpme")) {
|
||||
auto ses = this->get_proxy_session(session_name);
|
||||
|
||||
uint8_t area = stoul(command_args);
|
||||
send_warp(ses->client_channel, ses->lobby_client_id, area, true);
|
||||
uint8_t floor = stoul(command_args);
|
||||
send_warp(ses->client_channel, ses->lobby_client_id, floor, true);
|
||||
|
||||
} else if (command_name == "warpall") {
|
||||
auto ses = this->get_proxy_session(session_name);
|
||||
|
||||
uint8_t area = stoul(command_args);
|
||||
send_warp(ses->client_channel, ses->lobby_client_id, area, false);
|
||||
send_warp(ses->server_channel, ses->lobby_client_id, area, false);
|
||||
uint8_t floor = stoul(command_args);
|
||||
send_warp(ses->client_channel, ses->lobby_client_id, floor, false);
|
||||
send_warp(ses->server_channel, ses->lobby_client_id, floor, false);
|
||||
|
||||
} else if ((command_name == "info-board") || (command_name == "info-board-data")) {
|
||||
auto ses = this->get_proxy_session(session_name);
|
||||
@@ -815,8 +815,8 @@ Proxy session commands:\n\
|
||||
send_text_message(ses->client_channel, "$C7Next drop:\n" + name);
|
||||
|
||||
} else {
|
||||
send_drop_stacked_item(s, ses->client_channel, item, ses->area, ses->x, ses->z);
|
||||
send_drop_stacked_item(s, ses->server_channel, item, ses->area, ses->x, ses->z);
|
||||
send_drop_stacked_item(s, ses->client_channel, item, ses->floor, ses->x, ses->z);
|
||||
send_drop_stacked_item(s, ses->server_channel, item, ses->floor, ses->x, ses->z);
|
||||
|
||||
string name = s->describe_item(ses->version(), ses->next_drop_item, true);
|
||||
send_text_message(ses->client_channel, "$C7Item created:\n" + name);
|
||||
|
||||
+15
-15
@@ -583,8 +583,8 @@ const unordered_map<string, uint8_t> mag_color_for_name({
|
||||
{"costume-color", 0x12},
|
||||
});
|
||||
|
||||
uint8_t area_for_name(const std::string& name) {
|
||||
static const unordered_map<string, uint8_t> areas({
|
||||
uint8_t floor_for_name(const std::string& name) {
|
||||
static const unordered_map<string, uint8_t> floors({
|
||||
{"pioneer2", 0x00},
|
||||
{"p2", 0x00},
|
||||
{"forest1", 0x01},
|
||||
@@ -669,10 +669,10 @@ uint8_t area_for_name(const std::string& name) {
|
||||
{"saintmillion", 0x09},
|
||||
{"purgatory", 0x0A},
|
||||
});
|
||||
return areas.at(tolower(name));
|
||||
return floors.at(tolower(name));
|
||||
}
|
||||
|
||||
static const array<const char*, 0x12> ep1_area_names = {
|
||||
static const array<const char*, 0x12> ep1_floor_names = {
|
||||
"Pioneer2",
|
||||
"Forest1",
|
||||
"Forest2",
|
||||
@@ -693,7 +693,7 @@ static const array<const char*, 0x12> ep1_area_names = {
|
||||
"Battle2",
|
||||
};
|
||||
|
||||
static const array<const char*, 0x12> ep2_area_names = {
|
||||
static const array<const char*, 0x12> ep2_floor_names = {
|
||||
"Pioneer2",
|
||||
"VRTempleAlpha",
|
||||
"VRTempleBeta",
|
||||
@@ -714,7 +714,7 @@ static const array<const char*, 0x12> ep2_area_names = {
|
||||
"Tower",
|
||||
};
|
||||
|
||||
static const array<const char*, 0x0B> ep4_area_names = {
|
||||
static const array<const char*, 0x0B> ep4_floor_names = {
|
||||
"Pioneer2",
|
||||
"CraterEast",
|
||||
"CraterWest",
|
||||
@@ -728,29 +728,29 @@ static const array<const char*, 0x0B> ep4_area_names = {
|
||||
"Purgatory",
|
||||
};
|
||||
|
||||
size_t area_limit_for_episode(Episode ep) {
|
||||
size_t floor_limit_for_episode(Episode ep) {
|
||||
switch (ep) {
|
||||
case Episode::EP1:
|
||||
return ep1_area_names.size() - 1;
|
||||
return ep1_floor_names.size() - 1;
|
||||
case Episode::EP2:
|
||||
return ep2_area_names.size() - 1;
|
||||
return ep2_floor_names.size() - 1;
|
||||
case Episode::EP4:
|
||||
return ep4_area_names.size() - 1;
|
||||
return ep4_floor_names.size() - 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char* name_for_area(Episode episode, uint8_t area) {
|
||||
const char* name_for_floor(Episode episode, uint8_t floor) {
|
||||
switch (episode) {
|
||||
case Episode::EP1:
|
||||
return ep1_area_names.at(area);
|
||||
return ep1_floor_names.at(floor);
|
||||
case Episode::EP2:
|
||||
return ep2_area_names.at(area);
|
||||
return ep2_floor_names.at(floor);
|
||||
case Episode::EP4:
|
||||
return ep4_area_names.at(area);
|
||||
return ep4_floor_names.at(floor);
|
||||
default:
|
||||
throw logic_error("invalid episode for drop area");
|
||||
throw logic_error("invalid episode for drop floor");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,9 +71,9 @@ uint8_t language_code_for_char(char language_char);
|
||||
extern const std::vector<const char*> name_for_mag_color;
|
||||
extern const std::unordered_map<std::string, uint8_t> mag_color_for_name;
|
||||
|
||||
size_t area_limit_for_episode(Episode ep);
|
||||
uint8_t area_for_name(const std::string& name);
|
||||
const char* name_for_area(Episode episode, uint8_t area);
|
||||
size_t floor_limit_for_episode(Episode ep);
|
||||
uint8_t floor_for_name(const std::string& name);
|
||||
const char* name_for_floor(Episode episode, uint8_t floor);
|
||||
|
||||
uint32_t class_flags_for_class(uint8_t char_class);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user