rename area -> floor in most places
This commit is contained in:
@@ -315,9 +315,9 @@ Some commands only work on the game server and not on the proxy server. The chat
|
||||
* Cheat mode commands
|
||||
* `$cheat`: Enables or disables cheat mode for the current game. All other cheat mode commands do nothing if cheat mode is disabled. By default, cheat mode is off in new games but can be enabled; there is an option in config.json that allows you to disable cheat mode entirely, or set it to on by default in new games.
|
||||
* `$infhp` / `$inftp`: Enables or disables infinite HP or TP mode. Applies to only you. In infinite HP mode, one-hit KO attacks will still kill you.
|
||||
* `$warpme <area-id>`: Warps yourself to the given area.
|
||||
* `$warpall <area-id>`: Warps everyone in the game to the given area. You must be the leader to use this command, unless you're on the proxy server.
|
||||
* `$next`: Warps yourself to the next area.
|
||||
* `$warpme <floor-id>`: Warps yourself to the given floor.
|
||||
* `$warpall <floor-id>`: Warps everyone in the game to the given floor. You must be the leader to use this command, unless you're on the proxy server.
|
||||
* `$next`: Warps yourself to the next floor.
|
||||
* `$swa`: Enables or disables switch assist. When enabled, the server will attempt to automatically unlock two-player doors in solo games if you step on both switches sequentially.
|
||||
* `$item <desc>` (or `$i <desc>`): Create an item. `desc` may be a description of the item (e.g. "Hell Saber +5 0/10/25/0/10") or a string of hex data specifying the item code. Item codes are 16 hex bytes; at least 2 bytes must be specified, and all unspecified bytes are zeroes. If you are on the proxy server, you must not be using Blue Burst for this command to work. On the game server, this command works for all versions.
|
||||
* `$unset <index>`: In an Episode 3 battle, removes one of your set cards from the field. `<index>` is the index of the set card as it appears on your screen - 1 is the card next to your SC's icon, 2 is the card to the right of 1, etc. This does not cause a Hunters-side SC to lose HP, as they normally do when their items are destroyed.
|
||||
|
||||
+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