use object flags for switch assist; closes #571

This commit is contained in:
Martin Michelsen
2024-10-16 23:23:19 -07:00
parent 47bc37e806
commit a7d436a894
12 changed files with 87 additions and 82 deletions
-1
View File
@@ -864,7 +864,6 @@ static void server_command_exit(shared_ptr<Client> c, const std::string&) {
G_UnusedHeader cmd = {0x73, 0x01, 0x0000};
c->channel.send(0x60, 0x00, cmd);
c->floor = 0;
c->recent_switch_flags.clear();
} else if (is_ep3(c->version())) {
c->channel.send(0xED, 0x00);
} else {
-1
View File
@@ -258,7 +258,6 @@ public:
// Miscellaneous (used by chat commands)
uint32_t next_exp_value; // next EXP value to give
RecentSwitchFlags recent_switch_flags; // used for switch assist
bool can_chat;
struct PendingCharacterExport {
std::shared_ptr<const Account> dest_account;
+40 -1
View File
@@ -746,7 +746,7 @@ void Map::add_objects_from_owned_map_data(uint8_t floor, const void* data, size_
const auto* objects = reinterpret_cast<const ObjectEntry*>(data);
for (size_t z = 0; z < entry_count; z++) {
uint16_t object_id = this->objects.size();
this->objects.emplace_back(Object{
const auto& object = this->objects.emplace_back(Object{
.args = &objects[z],
.source_index = z,
.floor = floor,
@@ -755,8 +755,37 @@ void Map::add_objects_from_owned_map_data(uint8_t floor, const void* data, size_
.set_flags = 0,
.item_drop_checked = false,
});
uint64_t k = section_index_key(floor, objects[z].section, objects[z].group);
this->floor_section_and_group_to_object_index.emplace(k, object_id);
uint32_t base_switch_flag = 0;
uint32_t num_switch_flags = 0;
switch (object.args->base_type) {
case 0x00C1: // TODoorCave01
base_switch_flag = object.args->param4;
num_switch_flags = (4 - clamp<size_t>(object.args->param5, 0, 4));
break;
case 0x14A: // TODoorAncient08
case 0x14B: // TODoorAncient09
base_switch_flag = object.args->param4;
num_switch_flags = (object.args->base_type == 0x14A) ? 4 : 2;
break;
case 0x1AB: // TODoorFourLightRuins
case 0x1C0: // TODoorFourLightSpace
case 0x221: // TODoorFourLightSeabed
case 0x222: // TODoorFourLightSeabedU
base_switch_flag = object.args->param4;
num_switch_flags = object.args->param5;
break;
}
if ((num_switch_flags > 1) && !(base_switch_flag & 0xFFFFFF00)) {
for (size_t z = 0; z < num_switch_flags; z++) {
this->floor_and_switch_flag_to_door_index.emplace((floor << 8) | (base_switch_flag + z), object_id);
}
}
}
}
@@ -1748,6 +1777,16 @@ std::vector<Map::Event*> Map::get_events(uint8_t floor) {
return ret;
}
vector<Map::Object*> Map::doors_for_switch_flag(uint8_t floor, uint8_t switch_flag) {
vector<Map::Object*> ret;
for (auto its = this->floor_and_switch_flag_to_door_index.equal_range((floor << 8) | switch_flag);
its.first != its.second;
its.first++) {
ret.emplace_back(&this->objects[its.first->second]);
}
return ret;
}
template <typename EntryT>
static string disassemble_vector_file_t(const void* data, size_t size, size_t* entry_number, char type_ch) {
deque<string> ret;
+3
View File
@@ -369,6 +369,8 @@ struct Map {
std::vector<Event*> get_events(uint8_t floor, uint16_t section, uint16_t wave_number);
std::vector<Event*> get_events(uint8_t floor);
std::vector<Object*> doors_for_switch_flag(uint8_t floor, uint8_t switch_flag);
static std::string disassemble_objects_data(const void* data, size_t size, size_t* object_number = nullptr);
static std::string disassemble_enemies_data(const void* data, size_t size, size_t* enemy_number = nullptr);
static std::string disassemble_wave_events_data(const void* data, size_t size, uint8_t floor = 0xFF);
@@ -389,6 +391,7 @@ struct Map {
std::unordered_multimap<uint64_t, size_t> floor_section_and_group_to_object_index;
std::unordered_multimap<uint64_t, size_t> floor_section_and_wave_number_to_enemy_index;
std::unordered_multimap<uint64_t, size_t> floor_section_and_wave_number_to_event_index;
std::unordered_multimap<uint16_t, size_t> floor_and_switch_flag_to_door_index;
};
class SetDataTableBase {
-23
View File
@@ -699,29 +699,6 @@ const ChallengeTemplateDefinition& get_challenge_template_definition(Version ver
}
}
void RecentSwitchFlags::add(uint16_t flag_num) {
if ((flag_num != ((this->flag_nums >> 48) & 0xFFFF)) &&
(flag_num != ((this->flag_nums >> 32) & 0xFFFF)) &&
(flag_num != ((this->flag_nums >> 16) & 0xFFFF)) &&
(flag_num != (this->flag_nums & 0xFFFF))) {
this->flag_nums = this->flag_nums << 16 | flag_num;
}
}
string RecentSwitchFlags::enable_commands(uint8_t floor) const {
phosg::StringWriter w;
uint64_t flag_nums = this->flag_nums;
for (size_t z = 0; z < 4; z++) {
uint16_t flag_num = flag_nums;
if (flag_num == 0xFFFF) {
continue;
}
w.put(G_SwitchStateChanged_6x05{{0x05, 0x03, 0xFFFF}, 0, 0, flag_num, static_cast<uint8_t>(floor), 0x01});
flag_nums >>= 16;
}
return std::move(w.str());
}
const QuestFlagsForDifficulty bb_quest_flag_apply_mask{{
// clang-format off
/* 0000 */ 0x00, 0x3F, 0xFF, 0xE3, 0xE0, 0xFF, 0xFF, 0x00,
-12
View File
@@ -994,16 +994,4 @@ using SymbolChatBE = SymbolChatT<true>;
check_struct_size(SymbolChat, 0x3C);
check_struct_size(SymbolChatBE, 0x3C);
struct RecentSwitchFlags {
uint64_t flag_nums = 0xFFFFFFFFFFFFFFFF;
inline void clear() {
this->flag_nums = 0xFFFFFFFFFFFFFFFF;
}
void add(uint16_t flag_num);
std::string enable_commands(uint8_t floor) const;
};
extern const QuestFlagsForDifficulty bb_quest_flag_apply_mask;
+15 -6
View File
@@ -2006,12 +2006,21 @@ HandlerResult C_6x<void>(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, u
if (!data.empty()) {
if ((data[0] == 0x05) && ses->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED)) {
auto& cmd = check_size_t<G_SwitchStateChanged_6x05>(data);
if ((cmd.flags & 1) && (cmd.header.object_id != 0xFFFF)) {
ses->recent_switch_flags.add(cmd.switch_flag_num);
string commands = ses->recent_switch_flags.enable_commands(ses->floor);
if (!commands.empty()) {
ses->server_channel.send(0x60, 0x00, commands);
ses->client_channel.send(0x60, 0x00, commands);
if (ses->map && (cmd.flags & 1) && (cmd.header.object_id != 0xFFFF)) {
for (auto* door : ses->map->doors_for_switch_flag(cmd.switch_flag_floor, cmd.switch_flag_num)) {
if (door->game_flags & 0x0001) {
continue;
}
door->game_flags |= 1;
G_UpdateObjectState_6x0B cmd0B;
cmd0B.header.subcommand = 0x0B;
cmd0B.header.size = sizeof(cmd0B) / 4;
cmd0B.header.client_id = door->object_id | 0x4000;
cmd0B.flags = door->game_flags;
cmd0B.object_index = door->object_id;
ses->client_channel.send(0x60, 0x00, &cmd0B, sizeof(cmd0B));
ses->server_channel.send(0x60, 0x00, &cmd0B, sizeof(cmd0B));
}
}
-1
View File
@@ -70,7 +70,6 @@ public:
Client::Config config;
// A null handler in here means to forward the response to the remote server
std::deque<std::function<void(uint32_t return_value, uint32_t checksum)>> function_call_return_handler_queue;
RecentSwitchFlags recent_switch_flags; // used for switch assist
ItemData next_drop_item;
uint32_t next_item_id;
+25 -13
View File
@@ -1410,7 +1410,6 @@ static void on_change_floor_6x1F(shared_ptr<Client> c, uint8_t command, uint8_t
const auto& cmd = check_size_t<G_SetPlayerFloor_6x1F>(data, size);
if (cmd.floor >= 0 && c->floor != static_cast<uint32_t>(cmd.floor)) {
c->floor = cmd.floor;
c->recent_switch_flags.clear();
}
}
forward_subcommand(c, command, flag, data, size);
@@ -1420,7 +1419,6 @@ static void on_change_floor_6x21(shared_ptr<Client> c, uint8_t command, uint8_t
const auto& cmd = check_size_t<G_InterLevelWarp_6x21>(data, size);
if (cmd.floor >= 0 && c->floor != static_cast<uint32_t>(cmd.floor)) {
c->floor = cmd.floor;
c->recent_switch_flags.clear();
}
forward_subcommand(c, command, flag, data, size);
}
@@ -1581,7 +1579,30 @@ static void on_switch_state_changed(shared_ptr<Client> c, uint8_t command, uint8
return;
}
forward_subcommand(c, command, flag, data, size);
if (!l->quest &&
(cmd.flags & 1) &&
(cmd.header.object_id != 0xFFFF) &&
(cmd.switch_flag_num < 0x100) &&
c->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED)) {
for (auto* door : l->map->doors_for_switch_flag(cmd.switch_flag_floor, cmd.switch_flag_num)) {
if (door->game_flags & 0x0001) {
continue;
}
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
send_text_message_printf(c, "$C5SWA K-%hX %02hhX %02hX",
door->object_id, cmd.switch_flag_floor, cmd.switch_flag_num.load());
}
door->game_flags |= 1;
G_UpdateObjectState_6x0B cmd0B;
cmd0B.header.subcommand = 0x0B;
cmd0B.header.size = sizeof(cmd0B) / 4;
cmd0B.header.client_id = door->object_id | 0x4000;
cmd0B.flags = door->game_flags;
cmd0B.object_index = door->object_id;
send_command_t(l, 0x60, 0x00, cmd0B);
}
}
if (l->switch_flags) {
if (cmd.flags & 1) {
@@ -1597,15 +1618,7 @@ static void on_switch_state_changed(shared_ptr<Client> c, uint8_t command, uint8
}
}
if ((cmd.flags & 1) && cmd.header.object_id != 0xFFFF) {
c->recent_switch_flags.add(cmd.switch_flag_num);
if (!l->quest && c->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED)) {
string commands = c->recent_switch_flags.enable_commands(c->floor);
if (!commands.empty()) {
send_command(c, 0x60, 0x00, commands);
}
}
}
forward_subcommand(c, command, flag, data, size);
}
static void on_play_sound_from_player(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
@@ -1640,7 +1653,6 @@ void on_movement_with_floor(shared_ptr<Client> c, uint8_t command, uint8_t flag,
c->z = cmd.z;
if (cmd.floor >= 0 && c->floor != static_cast<uint32_t>(cmd.floor)) {
c->floor = cmd.floor;
c->recent_switch_flags.clear();
}
forward_subcommand(c, command, flag, data, size);
}
-1
View File
@@ -2567,7 +2567,6 @@ void send_warp(Channel& ch, uint8_t client_id, uint32_t floor, bool is_private)
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;
c->recent_switch_flags.clear();
}
void send_warp(shared_ptr<Lobby> l, uint32_t floor, bool is_private) {