use object flags for switch assist; closes #571
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user