From 34bac4c5b5a33cd271e14f61a9c9fe8df4ec4af8 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Wed, 28 Feb 2024 19:42:45 -0800 Subject: [PATCH] add enemy, object, and event tracking for persistence --- README.md | 6 +- src/ChatCommands.cc | 40 +- src/Client.cc | 3 +- src/Client.hh | 90 ++-- src/CommandFormats.hh | 23 +- src/Lobby.cc | 27 +- src/Lobby.hh | 7 +- src/Main.cc | 297 +++++------- src/Map.cc | 214 +++++++-- src/Map.hh | 43 +- src/PlayerSubordinates.cc | 15 + src/PlayerSubordinates.hh | 7 + src/ReceiveCommands.cc | 49 +- src/ReceiveSubcommands.cc | 458 ++++++++++++++----- src/SendCommands.cc | 148 +++++- src/SendCommands.hh | 13 + src/Text.cc | 8 +- system/maps/pc-nte/map_ancient01_00_00.evt | Bin 488 -> 872 bytes system/maps/pc-nte/map_ancient01_00_01.evt | Bin 614 -> 1136 bytes system/maps/pc-nte/map_ancient01_01_00.evt | Bin 578 -> 1068 bytes system/maps/pc-nte/map_ancient01_01_01.evt | Bin 613 -> 1180 bytes system/maps/pc-nte/map_ancient01_02_00.evt | Bin 578 -> 1080 bytes system/maps/pc-nte/map_ancient01_02_01.evt | Bin 639 -> 1212 bytes system/maps/pc-nte/map_ancient02_00_00.evt | Bin 576 -> 1008 bytes system/maps/pc-nte/map_ancient02_00_01.evt | Bin 595 -> 1064 bytes system/maps/pc-nte/map_ancient02_01_00.evt | Bin 618 -> 1088 bytes system/maps/pc-nte/map_ancient02_01_01.evt | Bin 684 -> 1224 bytes system/maps/pc-nte/map_ancient02_02_00.evt | Bin 601 -> 1052 bytes system/maps/pc-nte/map_ancient02_02_01.evt | Bin 641 -> 1132 bytes system/maps/pc-nte/map_ancient03_00_00.evt | Bin 586 -> 1088 bytes system/maps/pc-nte/map_ancient03_00_01.evt | Bin 724 -> 1384 bytes system/maps/pc-nte/map_ancient03_01_00.evt | Bin 662 -> 1252 bytes system/maps/pc-nte/map_ancient03_01_01.evt | Bin 667 -> 1260 bytes system/maps/pc-nte/map_ancient03_02_00.evt | Bin 717 -> 1372 bytes system/maps/pc-nte/map_ancient03_02_01.evt | Bin 729 -> 1392 bytes system/maps/pc-nte/map_boss01.evt | Bin 27 -> 44 bytes system/maps/pc-nte/map_boss01gj.evt | Bin 24 -> 40 bytes system/maps/pc-nte/map_boss02.evt | Bin 27 -> 44 bytes system/maps/pc-nte/map_boss04.evt | Bin 32 -> 52 bytes system/maps/pc-nte/map_cave01_00_00.evt | Bin 493 -> 964 bytes system/maps/pc-nte/map_cave01_00_01.evt | Bin 505 -> 976 bytes system/maps/pc-nte/map_cave01_01_00.evt | Bin 474 -> 900 bytes system/maps/pc-nte/map_cave01_01_01.evt | Bin 455 -> 856 bytes system/maps/pc-nte/map_cave01_02_00.evt | Bin 639 -> 1264 bytes system/maps/pc-nte/map_cave01_02_01.evt | Bin 589 -> 1156 bytes system/maps/pc-nte/map_cave02_00_00.evt | Bin 458 -> 864 bytes system/maps/pc-nte/map_cave02_00_01.evt | Bin 486 -> 932 bytes system/maps/pc-nte/map_cave02_01_00.evt | Bin 469 -> 908 bytes system/maps/pc-nte/map_cave02_01_01.evt | Bin 489 -> 940 bytes system/maps/pc-nte/map_cave02_02_00.evt | Bin 458 -> 876 bytes system/maps/pc-nte/map_cave02_02_01.evt | Bin 448 -> 848 bytes system/maps/pc-nte/map_cave03_00_00.evt | Bin 609 -> 1164 bytes system/maps/pc-nte/map_cave03_00_01.evt | Bin 650 -> 1252 bytes system/maps/pc-nte/map_cave03_01_00.evt | Bin 567 -> 1080 bytes system/maps/pc-nte/map_cave03_01_01.evt | Bin 567 -> 1104 bytes system/maps/pc-nte/map_cave03_02_00.evt | Bin 525 -> 1020 bytes system/maps/pc-nte/map_cave03_02_01.evt | Bin 482 -> 924 bytes system/maps/pc-nte/map_forest01_00.evt | Bin 254 -> 500 bytes system/maps/pc-nte/map_forest01_01.evt | Bin 273 -> 544 bytes system/maps/pc-nte/map_forest01_02.evt | Bin 282 -> 564 bytes system/maps/pc-nte/map_forest01_03.evt | Bin 257 -> 524 bytes system/maps/pc-nte/map_forest01_04.evt | Bin 264 -> 524 bytes system/maps/pc-nte/map_forest01_05.evt | Bin 68 -> 116 bytes system/maps/pc-nte/map_forest01_gj.evt | Bin 80 -> 144 bytes system/maps/pc-nte/map_forest02_00.evt | Bin 389 -> 744 bytes system/maps/pc-nte/map_forest02_01.evt | Bin 401 -> 776 bytes system/maps/pc-nte/map_forest02_02.evt | Bin 436 -> 844 bytes system/maps/pc-nte/map_forest02_03.evt | Bin 389 -> 768 bytes system/maps/pc-nte/map_forest02_04.evt | Bin 420 -> 816 bytes system/maps/pc-nte/map_machine01_00_00.evt | Bin 485 -> 892 bytes system/maps/pc-nte/map_machine01_00_01.evt | Bin 435 -> 796 bytes system/maps/pc-nte/map_machine01_01_00.evt | Bin 599 -> 1136 bytes system/maps/pc-nte/map_machine01_01_01.evt | Bin 600 -> 1152 bytes system/maps/pc-nte/map_machine01_02_00.evt | Bin 503 -> 928 bytes system/maps/pc-nte/map_machine01_02_01.evt | Bin 452 -> 876 bytes system/maps/pc-nte/map_machine02_00_00.evt | Bin 402 -> 748 bytes system/maps/pc-nte/map_machine02_00_01.evt | Bin 408 -> 748 bytes system/maps/pc-nte/map_machine02_01_00.evt | Bin 569 -> 1080 bytes system/maps/pc-nte/map_machine02_01_01.evt | Bin 530 -> 980 bytes system/maps/pc-nte/map_machine02_02_00.evt | Bin 430 -> 808 bytes system/maps/pc-nte/map_machine02_02_01.evt | Bin 426 -> 784 bytes tests/GC-Episode2PrivateDrops2P.test.txt | 4 +- tests/GC-ForestGame.test.txt | 5 + tests/GC-PoisonRoom.test.txt | 2 +- tests/PC-DCv1-CrossplayPrivateDrops.test.txt | 26 +- 85 files changed, 1004 insertions(+), 481 deletions(-) diff --git a/README.md b/README.md index 59984059..8575c32a 100644 --- a/README.md +++ b/README.md @@ -431,8 +431,8 @@ Some commands only work on the game server and not on the proxy server. The chat * The rest of the commands in this section are enabled on the game server. (They are always enabled on the proxy server.) * `$quest ` (game server only): Load a quest by quest number. Can be used to load battle or challenge quests with only one player present. * `$qcall `: Call a quest function on your client. - * `$qcheck ` (game server only): Show the value of a quest flag. This command can be used without debug mode enabled. - * `$qset ` or `$qclear `: Set or clear a quest flag for everyone in the game. + * `$qcheck ` (game server only): Show the value of a quest flag. This command can be used without debug mode enabled. If you're in a game, show the value of the flag in that game; if you're in the lobby, show the saved value of that quest flag for your character (BB only). + * `$qset ` or `$qclear `: Set or clear a quest flag for everyone in the game. If you're in the lobby and on BB, set or clear the saved value of a quest flag in your character file. * `$qgread ` (game server only): Get the value of a quest counter ("global flag"). This command can be used without debug mode enabled. * `$qgwrite ` (game server only): Set the value of a quest counter ("global flag") for yourself. * `$qsync `: Set a quest register's value for yourself only. `` should be either rXX (e.g. r60) or fXX (e.g. f60); if the latter, `` is parsed as a floating-point value instead of as an integer. @@ -468,7 +468,7 @@ Some commands only work on the game server and not on the proxy server. The chat * `$minlevel `: Sets the minimum level for players to join the current game. * `$password `: Sets the game's join password. To unlock the game, run `$password` with nothing after it. * `$dropmode [mode]`: Changes the way item drops behave in the current game. `mode` can be `none`, `client`, `shared`, `private`, or `duplicate`. If `mode` is not given, tells you the current drop mode without changing it. See the "Item tables and drop modes" section for more information. - * `$persist`: Enable or disable persistence for the current game. When persistence is on, the game will not be deleted when the last player leaves. The state of enemies and objects on the map will be reset when the last player leaves, but dropped items will not be deleted. If the game is empty for too long (15 minutes by default), it is then deleted. + * `$persist`: Enable or disable persistence for the current game. When persistence is on, the game will not be deleted when the last player leaves. The state of enemies on the map will be reset when the last player leaves, but dropped items will not be deleted. If the game is empty for too long (15 minutes by default), it is then deleted. * Episode 3 commands (game server only) * `$spec`: Toggles the allow spectators flag for Episode 3 games. If any players are spectating when this flag is disabled, they will be sent back to the lobby. diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index 1e5a76d9..7832353f 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -311,10 +311,22 @@ static void server_command_qcheck(shared_ptr c, const std::string& args) auto l = c->require_lobby(); uint16_t flag_num = stoul(args, nullptr, 0); - send_text_message_printf(c, "$C7Quest flag 0x%hX (%hu)\nis %s on %s", - flag_num, flag_num, - c->character()->quest_flags.get(l->difficulty, flag_num) ? "set" : "not set", - name_for_difficulty(l->difficulty)); + if (l->is_game()) { + if (!l->quest_flags_known || l->quest_flags_known->get(l->difficulty, flag_num)) { + send_text_message_printf(c, "$C7Game: flag 0x%hX (%hu)\nis %s on %s", + flag_num, flag_num, + c->character()->quest_flags.get(l->difficulty, flag_num) ? "set" : "not set", + name_for_difficulty(l->difficulty)); + } else { + send_text_message_printf(c, "$C7Game: flag 0x%hX (%hu)\nis unknown on %s", + flag_num, flag_num, name_for_difficulty(l->difficulty)); + } + } else if (c->version() == Version::BB_V4) { + send_text_message_printf(c, "$C7Player: flag 0x%hX (%hu)\nis %s on %s", + flag_num, flag_num, + c->character()->quest_flags.get(l->difficulty, flag_num) ? "set" : "not set", + name_for_difficulty(l->difficulty)); + } } static void server_command_qset_qclear(shared_ptr c, const std::string& args, bool should_set) { @@ -330,10 +342,22 @@ static void server_command_qset_qclear(shared_ptr c, const std::string& uint16_t flag_num = stoul(args, nullptr, 0); - if (should_set) { - c->character()->quest_flags.set(l->difficulty, flag_num); - } else { - c->character()->quest_flags.clear(l->difficulty, flag_num); + if (l->is_game()) { + if (l->quest_flags_known) { + l->quest_flags_known->set(l->difficulty, flag_num); + } + if (should_set) { + l->quest_flag_values->set(l->difficulty, flag_num); + } else { + l->quest_flag_values->clear(l->difficulty, flag_num); + } + } else if (c->version() == Version::BB_V4) { + auto p = c->character(); + if (should_set) { + p->quest_flags.set(l->difficulty, flag_num); + } else { + p->quest_flags.clear(l->difficulty, flag_num); + } } if (is_v1_or_v2(c->version())) { diff --git a/src/Client.cc b/src/Client.cc index 205912f7..636db5e9 100644 --- a/src/Client.cc +++ b/src/Client.cc @@ -361,9 +361,10 @@ bool Client::evaluate_quest_availability_expression( if (!expr) { return true; } + auto l = this->lobby.lock(); auto p = this->character(); QuestAvailabilityExpression::Env env = { - .flags = &p->quest_flags.data.at(difficulty), + .flags = (l && !l->quest_flags_known) ? &l->quest_flag_values->data.at(difficulty) : &p->quest_flags.data.at(difficulty), .challenge_records = &p->challenge_records, .team = this->team(), .num_players = num_players, diff --git a/src/Client.hh b/src/Client.hh index ff5142e2..6be8deb5 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -34,60 +34,62 @@ public: // TODO: It'd be nice to use a pattern here (e.g. all server-side flags are // in the high bits) but that would require re-recording or manually // rewriting all the tests - CLIENT_SIDE_MASK = 0xFFFFFFFFFC0FFFFB, + CLIENT_SIDE_MASK = 0xFF3CFFFF7C0FFFFB, // Version-related flags - CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002, - LICENSE_WAS_CREATED = 0x0000000000000004, // Server-side only - NO_D6_AFTER_LOBBY = 0x0000000000000100, - NO_D6 = 0x0000000000000200, - FORCE_ENGLISH_LANGUAGE_BB = 0x0000000000000400, + CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002, + LICENSE_WAS_CREATED = 0x0000000000000004, // Server-side only + NO_D6_AFTER_LOBBY = 0x0000000000000100, + NO_D6 = 0x0000000000000200, + FORCE_ENGLISH_LANGUAGE_BB = 0x0000000000000400, // Flags describing the behavior for send_function_call - NO_SEND_FUNCTION_CALL = 0x0000000000001000, - ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000, - SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000, - SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000, - USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x0000000000010000, + NO_SEND_FUNCTION_CALL = 0x0000000000001000, + ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000, + SEND_FUNCTION_CALL_CHECKSUM_ONLY = 0x0000000000004000, + SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000, + USE_OVERFLOW_FOR_SEND_FUNCTION_CALL = 0x0000000000010000, // State flags - LOADING = 0x0000000000100000, // Server-side only - LOADING_QUEST = 0x0000000000200000, // Server-side only - LOADING_RUNNING_JOINABLE_QUEST = 0x0000000000400000, // Server-side only - LOADING_TOURNAMENT = 0x0000000000800000, // Server-side only - IN_INFORMATION_MENU = 0x0000000001000000, // Server-side only - AT_WELCOME_MESSAGE = 0x0000000002000000, // Server-side only - SAVE_ENABLED = 0x0000000004000000, - HAS_EP3_CARD_DEFS = 0x0000000008000000, - HAS_EP3_MEDIA_UPDATES = 0x0000000010000000, - USE_OVERRIDE_RANDOM_SEED = 0x0000000020000000, - HAS_GUILD_CARD_NUMBER = 0x0000000040000000, - AT_BANK_COUNTER = 0x0000000080000000, // Server-side only - SHOULD_SEND_ARTIFICIAL_ITEM_STATE = 0x0001000000000000, // Server-side only - SHOULD_SEND_ARTIFICIAL_FLAG_STATE = 0x0002000000000000, // Server-side only - SHOULD_SEND_ENABLE_SAVE = 0x0004000000000000, - SWITCH_ASSIST_ENABLED = 0x0000000100000000, + LOADING = 0x0000000000100000, // Server-side only + LOADING_QUEST = 0x0000000000200000, // Server-side only + LOADING_RUNNING_JOINABLE_QUEST = 0x0000000000400000, // Server-side only + LOADING_TOURNAMENT = 0x0000000000800000, // Server-side only + IN_INFORMATION_MENU = 0x0000000001000000, // Server-side only + AT_WELCOME_MESSAGE = 0x0000000002000000, // Server-side only + SAVE_ENABLED = 0x0000000004000000, + HAS_EP3_CARD_DEFS = 0x0000000008000000, + HAS_EP3_MEDIA_UPDATES = 0x0000000010000000, + USE_OVERRIDE_RANDOM_SEED = 0x0000000020000000, + HAS_GUILD_CARD_NUMBER = 0x0000000040000000, + AT_BANK_COUNTER = 0x0000000080000000, // Server-side only + SHOULD_SEND_ARTIFICIAL_ITEM_STATE = 0x0001000000000000, // Server-side only + SHOULD_SEND_ARTIFICIAL_ENEMY_AND_SET_STATE = 0x0040000000000000, // Server-side only + SHOULD_SEND_ARTIFICIAL_OBJECT_STATE = 0x0080000000000000, // Server-side only + SHOULD_SEND_ARTIFICIAL_FLAG_STATE = 0x0002000000000000, // Server-side only + SHOULD_SEND_ENABLE_SAVE = 0x0004000000000000, + SWITCH_ASSIST_ENABLED = 0x0000000100000000, // Cheat mode and option flags - INFINITE_HP_ENABLED = 0x0000000200000000, - INFINITE_TP_ENABLED = 0x0000000400000000, - DEBUG_ENABLED = 0x0000000800000000, - ITEM_DROP_NOTIFICATIONS_1 = 0x0010000000000000, - ITEM_DROP_NOTIFICATIONS_2 = 0x0020000000000000, + INFINITE_HP_ENABLED = 0x0000000200000000, + INFINITE_TP_ENABLED = 0x0000000400000000, + DEBUG_ENABLED = 0x0000000800000000, + ITEM_DROP_NOTIFICATIONS_1 = 0x0010000000000000, + ITEM_DROP_NOTIFICATIONS_2 = 0x0020000000000000, // Proxy option flags - PROXY_SAVE_FILES = 0x0000001000000000, - PROXY_CHAT_COMMANDS_ENABLED = 0x0000002000000000, - PROXY_PLAYER_NOTIFICATIONS_ENABLED = 0x0000008000000000, - PROXY_SUPPRESS_CLIENT_PINGS = 0x0000010000000000, - PROXY_SUPPRESS_REMOTE_LOGIN = 0x0000020000000000, - PROXY_ZERO_REMOTE_GUILD_CARD = 0x0000040000000000, - PROXY_EP3_INFINITE_MESETA_ENABLED = 0x0000080000000000, - PROXY_EP3_INFINITE_TIME_ENABLED = 0x0000100000000000, - PROXY_RED_NAME_ENABLED = 0x0000200000000000, - PROXY_BLANK_NAME_ENABLED = 0x0000400000000000, - PROXY_BLOCK_FUNCTION_CALLS = 0x0000800000000000, - PROXY_EP3_UNMASK_WHISPERS = 0x0008000000000000, + PROXY_SAVE_FILES = 0x0000001000000000, + PROXY_CHAT_COMMANDS_ENABLED = 0x0000002000000000, + PROXY_PLAYER_NOTIFICATIONS_ENABLED = 0x0000008000000000, + PROXY_SUPPRESS_CLIENT_PINGS = 0x0000010000000000, + PROXY_SUPPRESS_REMOTE_LOGIN = 0x0000020000000000, + PROXY_ZERO_REMOTE_GUILD_CARD = 0x0000040000000000, + PROXY_EP3_INFINITE_MESETA_ENABLED = 0x0000080000000000, + PROXY_EP3_INFINITE_TIME_ENABLED = 0x0000100000000000, + PROXY_RED_NAME_ENABLED = 0x0000200000000000, + PROXY_BLANK_NAME_ENABLED = 0x0000400000000000, + PROXY_BLOCK_FUNCTION_CALLS = 0x0000800000000000, + PROXY_EP3_UNMASK_WHISPERS = 0x0008000000000000, // clang-format on }; enum class ItemDropNotificationMode { diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 47e7bce5..656b6998 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -4629,9 +4629,9 @@ struct G_UseStarAtomizer_6x66 { parray target_client_ids; } __packed__; -// 6x67: Trigger wave event +// 6x67: Trigger set event -struct G_TriggerWaveEvent_6x67 { +struct G_TriggerSetEvent_6x67 { G_UnusedHeader header; le_uint32_t floor = 0; le_uint32_t event_id = 0; // NOT event index @@ -4773,7 +4773,12 @@ struct G_SyncSetFlagState_6x6E_Decompressed { // 6x6F: Set quest flags (used while loading into game) -struct G_SetQuestFlags_6x6F { +struct G_SetQuestFlagsV1_6x6F { + G_UnusedHeader header; + QuestFlagsV1 quest_flags; +} __packed__; + +struct G_SetQuestFlagsV2V3V4_6x6F { G_UnusedHeader header; QuestFlags quest_flags; } __packed__; @@ -4988,12 +4993,14 @@ struct G_UpdateQuestFlag_V3_BB_6x75 : G_UpdateQuestFlag_DC_PC_6x75 { le_uint16_t unused = 0; } __packed__; -// 6x76: Enemy killed +// 6x76: Set entity flags +// This command can only be used to set flags, since the game performs a bitwise +// OR operation instead of a simple assignment. -struct G_EnemyKilled_6x76 { - G_EnemyIDHeader header; - le_uint16_t unknown_a1 = 0; - le_uint16_t unknown_a2 = 0; // Flags of some sort +struct G_SetEntityFlags_6x76 { + G_EnemyIDHeader header; // 1000-3FFF = enemy, 4000-FFFF = object + le_uint16_t floor = 0; + le_uint16_t flags = 0; } __packed__; // 6x77: Sync quest data diff --git a/src/Lobby.cc b/src/Lobby.cc index b4c79e82..b07bc8c4 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -325,11 +325,13 @@ shared_ptr Lobby::load_maps( shared_ptr opt_rand_crypt, const parray& variations, const PrefixedLogger* log) { - auto enemy_filenames = sdt->map_filenames_for_variations(variations, episode, mode, true); - auto object_filenames = sdt->map_filenames_for_variations(variations, episode, mode, false); + auto enemy_filenames = sdt->map_filenames_for_variations(variations, episode, mode, SetDataTable::FilenameType::ENEMIES); + auto object_filenames = sdt->map_filenames_for_variations(variations, episode, mode, SetDataTable::FilenameType::OBJECTS); + auto event_filenames = sdt->map_filenames_for_variations(variations, episode, mode, SetDataTable::FilenameType::EVENTS); return Lobby::load_maps( enemy_filenames, object_filenames, + event_filenames, version, episode, mode, @@ -346,6 +348,7 @@ shared_ptr Lobby::load_maps( shared_ptr Lobby::load_maps( const vector& enemy_filenames, const vector& object_filenames, + const vector& event_filenames, Version version, Episode episode, GameMode mode, @@ -402,6 +405,21 @@ shared_ptr Lobby::load_maps( } else if (log) { log->info("No objects to load for floor %02zX", floor); } + + const auto& floor_event_filename = event_filenames.at(floor); + if (!floor_event_filename.empty()) { + auto map_data = get_file_data(version, floor_event_filename); + if (map_data) { + map->add_events_from_map_data(floor, map_data->data(), map_data->size()); + if (log) { + log->info("Loaded events map %s for floor %02zX", floor_event_filename.c_str(), floor); + } + } else if (log) { + log->info("Events map %s for floor %02zX cannot be used; skipping", floor_event_filename.c_str(), floor); + } + } else if (log) { + log->info("No events to load for floor %02zX", floor); + } } return map; @@ -464,6 +482,11 @@ void Lobby::load_maps() { string e_str = this->map->enemies[z].str(); this->log.info("(E-%zX) %s", z, e_str.c_str()); } + this->log.info("Generated events list (%zu entries):", this->map->events.size()); + for (size_t z = 0; z < this->map->events.size(); z++) { + string e_str = this->map->events[z].str(); + this->log.info("%s", e_str.c_str()); + } this->log.info("Loaded maps contain %zu object entries and %zu enemy entries overall (%zu as rares)", this->map->objects.size(), this->map->enemies.size(), this->map->rare_enemy_indexes.size()); } diff --git a/src/Lobby.hh b/src/Lobby.hh index 0142e4b3..0e557117 100644 --- a/src/Lobby.hh +++ b/src/Lobby.hh @@ -92,15 +92,15 @@ struct Lobby : public std::enable_shared_from_this { uint32_t min_level; uint32_t max_level; - // Item state + // Game state std::array next_item_id_for_client; uint32_t next_game_item_id; std::vector floor_item_managers; - - // Map state std::shared_ptr rare_enemy_rates; std::shared_ptr map; parray variations; + std::unique_ptr quest_flags_known; // If null, ALL quest flags are known + std::unique_ptr quest_flag_values; // Game config Version base_version; @@ -226,6 +226,7 @@ struct Lobby : public std::enable_shared_from_this { static std::shared_ptr load_maps( const std::vector& enemy_filenames, const std::vector& object_filenames, + const std::vector& event_filenames, Version version, Episode episode, GameMode mode, diff --git a/src/Main.cc b/src/Main.cc index 0a3c9554..4ffc493b 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -1127,187 +1127,6 @@ Action a_disassemble_set_data_table( string str = sdt.str(); write_output_data(args, str.data(), str.size(), "txt"); }); -Action a_check_set_data_table( - "check-set-data-tables", nullptr, +[](Arguments&) { - auto s = make_shared(); - s->load_patch_indexes(false); - s->load_set_data_tables(false); - static_game_data_log.min_level = LogLevel::DISABLED; - - auto get_file_data = [&](Version version, const string& filename) -> shared_ptr { - try { - return s->load_map_file(version, filename); - } catch (const cannot_open_file&) { - return nullptr; - } - }; - - auto check_filenames = [&](Version version, const string& sdt_filename, const vector& ns_filenames) -> string { - for (size_t z = 0; z < ns_filenames.size(); z++) { - const auto& ns_filename = ns_filenames[z]; - auto data = get_file_data(version, ns_filename); - if (data) { - if (sdt_filename != ns_filename) { - string ns_filenames_str = join(ns_filenames, ", "); - return string_printf("SDT => %s, NS => [%s]", sdt_filename.c_str(), ns_filenames_str.c_str()); - } - return "OK"; - } - if (!data && (sdt_filename == ns_filename)) { - string ns_filenames_str = join(ns_filenames, ", "); - return string_printf("SDT => %s (missing)", sdt_filename.c_str()); - } - } - if (ns_filenames.empty() && sdt_filename.empty()) { - return "OK (no files)"; - } else if (ns_filenames.empty()) { - auto data = get_file_data(version, sdt_filename); - if (data) { - return string_printf("NS blank, SDT => %s", sdt_filename.c_str()); - } else { - return string_printf("NS blank, SDT => %s (missing)", sdt_filename.c_str()); - } - } else if (sdt_filename.empty()) { - string ns_filenames_str = join(ns_filenames, ", "); - return string_printf("SDT blank, NS => [%s] (all missing)", ns_filenames_str.c_str()); - } else { - string ns_filenames_str = join(ns_filenames, ", "); - return string_printf("SDT => %s (missing), NS => [%s] (all missing)", sdt_filename.c_str(), ns_filenames_str.c_str()); - } - }; - - size_t num_checks = 0; - size_t num_errors = 0; - auto check_table = [&](Version version) { - vector episodes({Episode::EP1}); - if (!is_v1_or_v2(version) || (version == Version::GC_NTE)) { - episodes.emplace_back(Episode::EP2); - if (is_v4(version)) { - episodes.emplace_back(Episode::EP4); - } - } - - vector modes({GameMode::NORMAL}); - if (!is_v1(version)) { - modes.emplace_back(GameMode::BATTLE); - modes.emplace_back(GameMode::CHALLENGE); - } - if (is_v4(version)) { - modes.emplace_back(GameMode::SOLO); - } - - uint8_t max_difficulty = is_v1(version) ? 2 : 3; - - for (Episode episode : episodes) { - for (GameMode mode : modes) { - for (uint8_t difficulty = 0; difficulty <= max_difficulty; difficulty++) { - auto sdt = s->set_data_table(version, episode, mode, difficulty); - auto ns_var_maxes = variation_maxes_deprecated(version, episode, (mode == GameMode::SOLO)); - size_t num_floors; - if (episode == Episode::EP4) { - num_floors = 0x0B; - } else if (episode == Episode::EP2) { - num_floors = 0x10; - } else { - num_floors = 0x0F; - } - for (size_t floor = 0; floor < num_floors; floor++) { - auto sdt_var_avail = sdt->num_available_variations_for_floor(episode, floor); - auto sdt_var_maxes = sdt->num_free_roam_variations_for_floor(episode, mode == GameMode::SOLO, floor); - size_t sdt_var1_max_avail = sdt_var_avail.first - 1; - size_t sdt_var2_max_avail = sdt_var_avail.second - 1; - size_t sdt_var1_max = sdt_var_maxes.first - 1; - size_t sdt_var2_max = sdt_var_maxes.second - 1; - size_t ns_var1_max = ns_var_maxes[floor * 2]; - size_t ns_var2_max = ns_var_maxes[floor * 2 + 1]; - num_checks += 4; - if (sdt_var1_max > sdt_var1_max_avail) { - fprintf(stdout, "## %-8s %-10s %-10s %-10s %02zX VAR1:[SDT:%02zX SDTA:%02zX]\n", - name_for_enum(version), - name_for_episode(episode), - name_for_mode(mode), - name_for_difficulty(difficulty), - floor, - sdt_var1_max, - sdt_var1_max_avail); - num_errors++; - } - if (sdt_var2_max > sdt_var2_max_avail) { - fprintf(stdout, "## %-8s %-10s %-10s %-10s %02zX VAR2:[SDT:%02zX SDTA:%02zX]\n", - name_for_enum(version), - name_for_episode(episode), - name_for_mode(mode), - name_for_difficulty(difficulty), - floor, - sdt_var2_max, - sdt_var2_max_avail); - num_errors++; - } - if (sdt_var1_max < ns_var1_max) { - fprintf(stdout, "## %-8s %-10s %-10s %-10s %02zX VAR1:[SDT:%02zX NS:%02zX]\n", - name_for_enum(version), - name_for_episode(episode), - name_for_mode(mode), - name_for_difficulty(difficulty), - floor, - sdt_var1_max, - ns_var1_max); - num_errors++; - } - if (sdt_var2_max < ns_var2_max) { - fprintf(stdout, "## %-8s %-10s %-10s %-10s %02zX VAR2:[SDT:%02zX NS:%02zX]\n", - name_for_enum(version), - name_for_episode(episode), - name_for_mode(mode), - name_for_difficulty(difficulty), - floor, - sdt_var2_max, - ns_var2_max); - num_errors++; - } - for (size_t var1 = 0; var1 <= ns_var1_max; var1++) { - for (size_t var2 = 0; var2 <= ns_var2_max; var2++) { - auto sdt_enemy_filename = sdt->map_filename_for_variation(floor, var1, var2, episode, mode, true); - auto sdt_object_filename = sdt->map_filename_for_variation(floor, var1, var2, episode, mode, false); - auto ns_enemy_filenames = map_filenames_for_variation_deprecated(floor, var1, var2, version, episode, mode, true); - auto ns_object_filenames = map_filenames_for_variation_deprecated(floor, var1, var2, version, episode, mode, false); - string enemies_error = check_filenames(version, sdt_enemy_filename, ns_enemy_filenames); - string objects_error = check_filenames(version, sdt_object_filename, ns_object_filenames); - num_checks += 2; - num_errors += (enemies_error != "OK") + (objects_error != "OK"); - fprintf(stdout, "%s %-8s %-10s %-10s %-10s %02zX %02zX %02zX E:[%s] O:[%s] E:%-30s O:%-30s\n", - ((enemies_error != "OK") || (objects_error != "OK")) ? "##" : " ", - name_for_enum(version), - name_for_episode(episode), - name_for_mode(mode), - name_for_difficulty(difficulty), - floor, - var1, - var2, - enemies_error.c_str(), - objects_error.c_str(), - sdt_enemy_filename.c_str(), - sdt_object_filename.c_str()); - } - } - } - } - } - } - }; - - check_table(Version::DC_NTE); - check_table(Version::DC_V1_11_2000_PROTOTYPE); - check_table(Version::DC_V1); - check_table(Version::DC_V2); - check_table(Version::PC_NTE); - check_table(Version::PC_V2); - check_table(Version::GC_NTE); - check_table(Version::GC_V3); - check_table(Version::XB_V3); - check_table(Version::BB_V4); - fprintf(stdout, "%zu/%zu errors\n", num_errors, num_checks); - }); Action a_assemble_quest_script( "assemble-quest-script", "\ @@ -2189,6 +2008,122 @@ Action a_find_rare_enemy_seeds( parallel_range(thread_fn, 0, 0x100000000, num_threads, nullptr); }); +Action a_load_maps_test( + "load-maps-test", nullptr, +[](Arguments&) { + using SDT = SetDataTable; + auto s = make_shared("system/config.json"); + s->load_config_early(); + s->clear_map_file_caches(); + s->load_patch_indexes(false); + s->load_set_data_tables(false); + s->load_quest_index(false); + for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) { + Version v = static_cast(v_s); + if (is_ep3(v)) { + continue; + } + const array episodes = {Episode::EP1, Episode::EP2, Episode::EP4}; + for (Episode episode : episodes) { + if (episode == Episode::EP4 && !is_v4(v)) { + continue; + } + if (episode == Episode::EP2 && is_v1_or_v2(v) && (v != Version::GC_NTE)) { + continue; + } + const array modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO}; + for (GameMode mode : modes) { + if ((mode == GameMode::BATTLE || mode == GameMode::CHALLENGE) && is_v1(v)) { + continue; + } + if (mode == GameMode::SOLO && !is_v4(v)) { + continue; + } + for (uint8_t difficulty = 0; difficulty < 4; difficulty++) { + if (difficulty == 3 && is_v1(v)) { + continue; + } + auto sdt = s->set_data_table(v, episode, mode, difficulty); + for (uint8_t floor = 0; floor < 0x12; floor++) { + auto variation_maxes = sdt->num_free_roam_variations_for_floor(episode, mode == GameMode::SOLO, floor); + for (size_t var1 = 0; var1 < variation_maxes.first; var1++) { + for (size_t var2 = 0; var2 < variation_maxes.second; var2++) { + auto enemies_filename = sdt->map_filename_for_variation( + floor, var1, var2, episode, mode, SDT::FilenameType::ENEMIES); + auto objects_filename = sdt->map_filename_for_variation( + floor, var1, var2, episode, mode, SDT::FilenameType::OBJECTS); + auto events_filename = sdt->map_filename_for_variation( + floor, var1, var2, episode, mode, SDT::FilenameType::EVENTS); + + fprintf(stderr, "... %s %s %s %s %02hhX %zX %zX", + name_for_enum(v), name_for_episode(episode), name_for_mode(mode), name_for_difficulty(difficulty), floor, var1, var2); + auto map = make_shared(v, 0, 0, nullptr); + if (!enemies_filename.empty()) { + fprintf(stderr, " [%s => ", enemies_filename.c_str()); + auto map_data = s->load_map_file(v, enemies_filename); + if (map_data) { + map->add_enemies_from_map_data( + episode, difficulty, 0, 0, map_data->data(), map_data->size(), Map::DEFAULT_RARE_ENEMIES); + fprintf(stderr, "%zu enemies, %zu sets]", map->enemies.size(), map->enemy_set_flags.size()); + } else { + fprintf(stderr, "__MISSING__]"); + } + } + if (!objects_filename.empty()) { + fprintf(stderr, " [%s => ", objects_filename.c_str()); + auto map_data = s->load_map_file(v, objects_filename); + if (map_data) { + map->add_objects_from_map_data(floor, map_data->data(), map_data->size()); + fprintf(stderr, "%zu objects]", map->objects.size()); + } else { + fprintf(stderr, "__MISSING__]"); + } + } + if (!events_filename.empty()) { + fprintf(stderr, " [%s => ", events_filename.c_str()); + auto map_data = s->load_map_file(v, events_filename); + if (map_data) { + map->add_events_from_map_data(floor, map_data->data(), map_data->size()); + fprintf(stderr, "%zu events, %zu action bytes]", map->events.size(), map->event_action_stream.size()); + } else { + fprintf(stderr, "__MISSING__]"); + } + } + fputc('\n', stderr); + } + } + } + } + } + } + } + + for (const auto& q_it : s->default_quest_index->quests_by_number) { + for (const auto& vq_it : q_it.second->versions) { + auto vq = vq_it.second; + shared_ptr map = Lobby::load_maps( + vq->version, + vq->episode, + 0, + 0, + 0, + Map::DEFAULT_RARE_ENEMIES, + 0, + nullptr, + vq->dat_contents_decompressed); + fprintf(stderr, "... %" PRIu32 " (%s) %s %s %s => %zu enemies (%zu sets), %zu objects, %zu events\n", + vq->quest_number, + vq->name.c_str(), + name_for_episode(vq->episode), + name_for_enum(vq->version), + name_for_language_code(vq->language), + map->enemies.size(), + map->enemy_set_flags.size(), + map->objects.size(), + map->events.size()); + } + } + }); + Action a_parse_object_graph( "parse-object-graph", nullptr, +[](Arguments& args) { uint32_t root_object_address = args.get("root", Arguments::IntFormat::HEX); diff --git a/src/Map.cc b/src/Map.cc index b6210e5c..6e4a5e24 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -675,6 +675,15 @@ string Map::Enemy::str() const { this->state_flags); } +string Map::Event::str() const { + return string_printf("[Map::Event W-%02hhX-%" PRIX32 " flags=%04hX floor=%02hhX action_stream_offset=%" PRIX32 "]", + this->floor, + this->event_id, + this->flags, + this->floor, + this->action_stream_offset); +} + string Map::Object::str() const { return string_printf("[Map::Object source %zX %04hX(%s) @%04hX p1=%g p456=[%08" PRIX32 " %08" PRIX32 " %08" PRIX32 "] floor=%02hhX item_drop_checked=%s]", this->source_index, @@ -1189,9 +1198,6 @@ void Map::add_enemy( case 0x00C7: // TBoss3VoloptHiraisin case 0x0118: add(EnemyType::UNKNOWN); - this->log.warning( - "(Entry %zu, offset %zX in file) Unknown enemy type %04hX", - source_index, source_index * sizeof(EnemyEntry), e.base_type.load()); break; default: @@ -1320,6 +1326,10 @@ void Map::add_random_enemies_from_map_data( } wave_events_segment_r.go(wave_events_header.entries_offset); + size_t action_stream_base_offset = this->event_action_stream.size(); + this->event_action_stream += wave_events_segment_r.pread( + wave_events_header.action_stream_offset, wave_events_segment_r.size() - wave_events_header.action_stream_offset); + const auto& locations_header = locations_segment_r.get(); const auto& definitions_header = definitions_segment_r.get(); auto definitions_r = definitions_segment_r.sub( @@ -1336,6 +1346,7 @@ void Map::add_random_enemies_from_map_data( size_t remaining_waves = random_state->rand_int_biased(1, entry.max_waves); // Trace: at 0080E125 EAX is wave count + le_uint32_t wave_next_event_id = entry.event_id; uint32_t wave_number = entry.wave_number; while (remaining_waves) { remaining_waves--; @@ -1422,17 +1433,60 @@ void Map::add_random_enemies_from_map_data( } } if (remaining_waves) { - // We don't generate the event stream here, but the client does, and in - // doing so, it uses one value from random to determine the delay - // parameter of the event. To keep our state in sync with what the - // client would do, we skip a random value here. - random_state->random.next(); + /* ev.delay = */ random_state->rand_int_biased(entry.min_delay, entry.max_delay); + this->add_event(wave_next_event_id, entry.flags, floor, this->event_action_stream.size()); + this->event_action_stream.push_back(0x0C); + wave_next_event_id = entry.event_id + wave_number + 10000; + this->event_action_stream.append(reinterpret_cast(&wave_next_event_id), sizeof(wave_next_event_id)); + this->event_action_stream.push_back(0x01); wave_number++; } } - // For the same reason as above, we need to skip another random value here. - random_state->random.next(); + /* ev.delay = */ random_state->rand_int_biased(entry.min_delay, entry.max_delay); + this->add_event(wave_next_event_id, entry.flags, floor, action_stream_base_offset + entry.action_stream_offset); + wave_number++; + } +} + +void Map::add_event(uint32_t event_id, uint16_t flags, uint8_t floor, uint32_t action_stream_offset) { + size_t index = this->events.size(); + auto& ev = this->events.emplace_back(); + ev.event_id = event_id; + ev.flags = flags; + ev.floor = floor; + ev.action_stream_offset = action_stream_offset; + uint64_t k = (static_cast(floor) << 32) | event_id; + if (!this->floor_and_event_id_to_index.emplace(k, index).second) { + this->log.warning("Duplicate event ID: W-%02hhX-%" PRIX32, floor, event_id); + } +} + +Map::Event& Map::get_event(uint8_t floor, uint32_t event_id) { + uint64_t k = (static_cast(floor) << 32) | event_id; + return this->events.at(this->floor_and_event_id_to_index.at(k)); +} + +const Map::Event& Map::get_event(uint8_t floor, uint32_t event_id) const { + uint64_t k = (static_cast(floor) << 32) | event_id; + return this->events.at(this->floor_and_event_id_to_index.at(k)); +} + +void Map::add_events_from_map_data(uint8_t floor, const void* data, size_t size) { + StringReader r(data, size); + const auto& header = r.get(); + if (header.format != 0) { + throw runtime_error("events section format is not zero"); + } + + size_t action_stream_base_offset = this->event_action_stream.size(); + this->event_action_stream += r.pread(header.action_stream_offset, r.size() - header.action_stream_offset); + + this->events.reserve(this->events.size() + header.entry_count); + auto events_r = r.sub(header.entries_offset, sizeof(Event1Entry) * header.entry_count); + while (!events_r.eof()) { + const auto& entry = events_r.get(); + this->add_event(entry.event_id, entry.flags, floor, entry.action_stream_offset + action_stream_base_offset); } } @@ -1519,23 +1573,10 @@ void Map::add_entities_from_quest_data( this->add_objects_from_map_data(floor, r.pgetv(floor_sections.objects + sizeof(header), header.data_size), header.data_size); } - if (floor_sections.enemies != 0xFFFFFFFF) { - const auto& header = r.pget(floor_sections.enemies); - if (header.data_size % sizeof(EnemyEntry)) { - throw runtime_error("quest layout enemy section size is not a multiple of enemy entry size"); - } - this->add_enemies_from_map_data( - episode, - difficulty, - event, - floor, - r.pgetv(floor_sections.enemies + sizeof(header), header.data_size), - header.data_size, - rare_rates); - - } else if ((floor_sections.wave_events != 0xFFFFFFFF) && + if ((floor_sections.wave_events != 0xFFFFFFFF) && (floor_sections.random_enemy_locations != 0xFFFFFFFF) && (floor_sections.random_enemy_definitions != 0xFFFFFFFF)) { + // Challenge Mode random enemy waves const auto& wave_events_header = r.pget(floor_sections.wave_events); const auto& random_enemy_locations_header = r.pget(floor_sections.random_enemy_locations); const auto& random_enemy_definitions_header = r.pget(floor_sections.random_enemy_definitions); @@ -1552,6 +1593,29 @@ void Map::add_entities_from_quest_data( r.sub(floor_sections.random_enemy_definitions + sizeof(SectionHeader), random_enemy_definitions_header.data_size), random_state, rare_rates); + + } else { + // Non-Challenge (standard) enemies + if (floor_sections.enemies != 0xFFFFFFFF) { + const auto& header = r.pget(floor_sections.enemies); + if (header.data_size % sizeof(EnemyEntry)) { + throw runtime_error("quest layout enemy section size is not a multiple of enemy entry size"); + } + this->add_enemies_from_map_data( + episode, + difficulty, + event, + floor, + r.pgetv(floor_sections.enemies + sizeof(header), header.data_size), + header.data_size, + rare_rates); + } + + if (floor_sections.wave_events != 0xFFFFFFFF) { + const auto& wave_events_header = r.pget(floor_sections.wave_events); + const void* data = r.pgetv(floor_sections.wave_events + sizeof(SectionHeader), wave_events_header.data_size); + this->add_events_from_map_data(floor, data, wave_events_header.data_size); + } } } } @@ -1657,14 +1721,14 @@ parray SetDataTableBase::generate_variations( } vector SetDataTableBase::map_filenames_for_variations( - const parray& variations, Episode episode, GameMode mode, bool is_enemies) const { + const parray& variations, Episode episode, GameMode mode, FilenameType type) const { vector ret; for (uint8_t floor = 0; floor < 0x10; floor++) { ret.emplace_back(this->map_filename_for_variation( - floor, variations[floor * 2], variations[floor * 2 + 1], episode, mode, is_enemies)); + floor, variations[floor * 2], variations[floor * 2 + 1], episode, mode, type)); } for (uint8_t floor = 0x10; floor < 0x12; floor++) { - ret.emplace_back(this->map_filename_for_variation(floor, 0, 0, episode, mode, is_enemies)); + ret.emplace_back(this->map_filename_for_variation(floor, 0, 0, episode, mode, type)); } return ret; } @@ -1738,8 +1802,8 @@ void SetDataTable::load_table_t(const string& data) { while (!var2_r.eof()) { auto& entry = var2_v.emplace_back(); entry.object_list_basename = r.pget_cstr(var2_r.get()); - entry.enemy_list_basename = r.pget_cstr(var2_r.get()); - entry.event_list_basename = r.pget_cstr(var2_r.get()); + entry.enemy_and_event_list_basename = r.pget_cstr(var2_r.get()); + entry.area_setup_filename = r.pget_cstr(var2_r.get()); } } } @@ -1750,7 +1814,10 @@ pair SetDataTable::num_available_variations_for_floor(Episod if (area == 0xFF) { return make_pair(1, 1); } else { - const auto& e = this->entries.at(area); + if (area >= this->entries.size()) { + return make_pair(1, 1); + } + const auto& e = this->entries[area]; return make_pair(e.size(), e.at(0).size()); } } @@ -1789,7 +1856,7 @@ pair SetDataTable::num_free_roam_variations_for_floor(Episod } string SetDataTable::map_filename_for_variation( - uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, bool is_enemies) const { + uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const { uint8_t area = this->default_area_for_floor(episode, floor); if (area == 0xFF) { return ""; @@ -1800,22 +1867,35 @@ string SetDataTable::map_filename_for_variation( } const auto& entry = this->entries.at(area).at(var1).at(var2); - string filename = is_enemies ? entry.enemy_list_basename : entry.object_list_basename; - filename += (is_enemies ? "e" : "o"); + string filename; + switch (type) { + case FilenameType::OBJECTS: + filename = entry.object_list_basename + "o"; + break; + case FilenameType::ENEMIES: + filename = entry.enemy_and_event_list_basename + "e"; + break; + case FilenameType::EVENTS: + filename = entry.enemy_and_event_list_basename; + break; + default: + throw logic_error("invalid map filename type"); + } + bool is_events = (type == FilenameType::EVENTS); switch ((floor != 0) ? GameMode::NORMAL : mode) { case GameMode::NORMAL: - filename += ".dat"; + filename += is_events ? ".evt" : ".dat"; break; case GameMode::SOLO: - filename += "_s.dat"; + filename += is_events ? "_s.evt" : "_s.dat"; break; case GameMode::CHALLENGE: - filename += "_c1.dat"; + filename += is_events ? "_c1.evt" : "_c1.dat"; break; case GameMode::BATTLE: - filename += "_d.dat"; + filename += is_events ? "_d.evt" : "_d.dat"; break; default: throw logic_error("invalid game mode"); @@ -1826,14 +1906,15 @@ string SetDataTable::map_filename_for_variation( string SetDataTable::str() const { vector lines; - lines.emplace_back(string_printf("FL/V1/V2 => ----------------------OBJECT -----------------------ENEMY -----------------------EVENT\n")); + lines.emplace_back(string_printf("FL/V1/V2 => ----------------------OBJECT -----------------ENEMY+EVENT -----------------------SETUP\n")); for (size_t a = 0; a < this->entries.size(); a++) { const auto& v1_v = this->entries[a]; for (size_t v1 = 0; v1 < v1_v.size(); v1++) { const auto& v2_v = v1_v[v1]; for (size_t v2 = 0; v2 < v2_v.size(); v2++) { const auto& e = v2_v[v2]; - lines.emplace_back(string_printf("%02zX/%02zX/%02zX => %28s %28s %28s\n", a, v1, v2, e.object_list_basename.c_str(), e.enemy_list_basename.c_str(), e.event_list_basename.c_str())); + lines.emplace_back(string_printf("%02zX/%02zX/%02zX => %28s %28s %28s\n", + a, v1, v2, e.object_list_basename.c_str(), e.enemy_and_event_list_basename.c_str(), e.area_setup_filename.c_str())); } } } @@ -1854,7 +1935,7 @@ struct AreaMapFileInfo { variation2_values(variation2_values) {} }; -const array>, 0x10> SetDataTableDCNTE::NAMES = {{ +const array>, 0x12> SetDataTableDCNTE::NAMES = {{ /* 00 */ {{"map_city00_00"}}, /* 01 */ {{"map_forest01_00", "map_forest01_01"}}, /* 02 */ {{"map_forest02_00", "map_forest02_03"}}, @@ -1871,12 +1952,15 @@ const array>, 0x10> SetDataTableDCNTE::NAMES = {{ /* 0D */ {{"map_boss03"}}, /* 0E */ {{"map_boss04"}}, /* 0F */ {{"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}}, + /* 10 */ {}, + /* 11 */ {}, }}; SetDataTableDCNTE::SetDataTableDCNTE() : SetDataTableBase(Version::DC_NTE) {} pair SetDataTableDCNTE::num_available_variations_for_floor(Episode, uint8_t floor) const { - return make_pair(this->NAMES[floor].size(), this->NAMES[floor][0].size()); + const auto& floor_names = this->NAMES.at(floor); + return make_pair(floor_names.size(), floor_names.empty() ? 0 : this->NAMES.at(floor)[0].size()); } pair SetDataTableDCNTE::num_free_roam_variations_for_floor(Episode episode, bool, uint8_t floor) const { @@ -1884,14 +1968,29 @@ pair SetDataTableDCNTE::num_free_roam_variations_for_floor(E } string SetDataTableDCNTE::map_filename_for_variation( - uint8_t floor, uint32_t var1, uint32_t var2, Episode, GameMode, bool is_enemies) const { - if (floor >= this->NAMES.size()) { + uint8_t floor, uint32_t var1, uint32_t var2, Episode, GameMode, FilenameType type) const { + try { + string basename = this->NAMES.at(floor).at(var1).at(var2); + switch (type) { + case FilenameType::ENEMIES: + basename += "e.dat"; + break; + case FilenameType::OBJECTS: + basename += "o.dat"; + break; + case FilenameType::EVENTS: + basename += ".evt"; + break; + default: + throw logic_error("invalid map filename type"); + } + return basename; + } catch (const out_of_range&) { return ""; } - return this->NAMES.at(floor).at(var1).at(var2) + (is_enemies ? "e.dat" : "o.dat"); } -const array>, 0x10> SetDataTableDC112000::NAMES = {{ +const array>, 0x12> SetDataTableDC112000::NAMES = {{ /* 00 */ {{"map_city00_00"}}, /* 01 */ {{"map_forest01_00", "map_forest01_01", "map_forest01_02", "map_forest01_03", "map_forest01_04"}}, /* 02 */ {{"map_forest02_00", "map_forest02_01", "map_forest02_02", "map_forest02_03", "map_forest02_04"}}, @@ -1908,12 +2007,15 @@ const array>, 0x10> SetDataTableDC112000::NAMES = {{ /* 0D */ {{"map_boss03"}}, /* 0E */ {{"map_boss04"}}, /* 0F */ {{"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}, {"map_visuallobby"}}, + /* 10 */ {}, + /* 11 */ {}, }}; SetDataTableDC112000::SetDataTableDC112000() : SetDataTableBase(Version::DC_V1_11_2000_PROTOTYPE) {} pair SetDataTableDC112000::num_available_variations_for_floor(Episode, uint8_t floor) const { - return make_pair(this->NAMES[floor].size(), this->NAMES[floor][0].size()); + const auto& floor_names = this->NAMES.at(floor); + return make_pair(floor_names.size(), floor_names.empty() ? 0 : this->NAMES.at(floor)[0].size()); } pair SetDataTableDC112000::num_free_roam_variations_for_floor(Episode episode, bool, uint8_t floor) const { @@ -1921,11 +2023,25 @@ pair SetDataTableDC112000::num_free_roam_variations_for_floo } string SetDataTableDC112000::map_filename_for_variation( - uint8_t floor, uint32_t var1, uint32_t var2, Episode, GameMode, bool is_enemies) const { + uint8_t floor, uint32_t var1, uint32_t var2, Episode, GameMode, FilenameType type) const { if (floor >= this->NAMES.size()) { return ""; } - return this->NAMES.at(floor).at(var1).at(var2) + (is_enemies ? "e.dat" : "o.dat"); + string basename = this->NAMES.at(floor).at(var1).at(var2); + switch (type) { + case FilenameType::ENEMIES: + basename += "e.dat"; + break; + case FilenameType::OBJECTS: + basename += "o.dat"; + break; + case FilenameType::EVENTS: + basename += ".evt"; + break; + default: + throw logic_error("invalid map filename type"); + } + return basename; } static const vector map_file_info_dc_nte = { diff --git a/src/Map.hh b/src/Map.hh index 1d2e812c..9e9c761c 100644 --- a/src/Map.hh +++ b/src/Map.hh @@ -94,7 +94,7 @@ struct Map { } __attribute__((packed)); struct EventsSectionHeader { // Section type 3 (WAVE_EVENTS) - /* 00 */ le_uint32_t footer_offset; + /* 00 */ le_uint32_t action_stream_offset; /* 04 */ le_uint32_t entries_offset; /* 08 */ le_uint32_t entry_count; /* 0C */ be_uint32_t format; // 0 or 'evt2' @@ -248,6 +248,15 @@ struct Map { std::string str() const; } __attribute__((packed)); + struct Event { + uint32_t event_id; + uint16_t flags; + uint8_t floor; + uint32_t action_stream_offset; + + std::string str() const; + } __attribute__((packed)); + struct DATParserRandomState { PSOV2Encryption random; PSOV2Encryption location_table_random; @@ -297,6 +306,11 @@ struct Map { std::shared_ptr random_state, std::shared_ptr rare_rates = DEFAULT_RARE_ENEMIES); + void add_event(uint32_t event_id, uint16_t flags, uint8_t floor, uint32_t action_stream_offset); + Event& get_event(uint8_t floor, uint32_t event_id); + const Event& get_event(uint8_t floor, uint32_t event_id) const; + void add_events_from_map_data(uint8_t floor, const void* data, size_t size); + struct DATSectionsForFloor { uint32_t objects = 0xFFFFFFFF; uint32_t enemies = 0xFFFFFFFF; @@ -327,6 +341,9 @@ struct Map { std::vector enemies; std::vector enemy_set_flags; std::vector rare_enemy_indexes; + std::vector events; + std::string event_action_stream; + std::unordered_map floor_and_event_id_to_index; }; class SetDataTableBase { @@ -340,10 +357,16 @@ public: virtual std::pair num_available_variations_for_floor(Episode episode, uint8_t floor) const = 0; virtual std::pair num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const = 0; + enum class FilenameType { + OBJECTS = 0, + ENEMIES, + EVENTS, + }; + virtual std::string map_filename_for_variation( - uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, bool is_enemies) const = 0; + uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const = 0; std::vector map_filenames_for_variations( - const parray& variations, Episode episode, GameMode mode, bool is_enemies) const; + const parray& variations, Episode episode, GameMode mode, FilenameType type) const; uint8_t default_area_for_floor(Episode episode, uint8_t floor) const; @@ -357,8 +380,8 @@ class SetDataTable : public SetDataTableBase { public: struct SetEntry { std::string object_list_basename; - std::string enemy_list_basename; - std::string event_list_basename; + std::string enemy_and_event_list_basename; + std::string area_setup_filename; }; SetDataTable(Version version, const std::string& data); @@ -367,7 +390,7 @@ public: virtual std::pair num_available_variations_for_floor(Episode episode, uint8_t floor) const; virtual std::pair num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const; virtual std::string map_filename_for_variation( - uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, bool is_enemies) const; + uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const; std::string str() const; @@ -388,10 +411,10 @@ public: virtual std::pair num_available_variations_for_floor(Episode episode, uint8_t floor) const; virtual std::pair num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const; virtual std::string map_filename_for_variation( - uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, bool is_enemies) const; + uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const; private: - static const std::array>, 0x10> NAMES; + static const std::array>, 0x12> NAMES; }; class SetDataTableDC112000 : public SetDataTableBase { @@ -402,10 +425,10 @@ public: virtual std::pair num_available_variations_for_floor(Episode episode, uint8_t floor) const; virtual std::pair num_free_roam_variations_for_floor(Episode episode, bool is_solo, uint8_t floor) const; virtual std::string map_filename_for_variation( - uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, bool is_enemies) const; + uint8_t floor, uint32_t var1, uint32_t var2, Episode episode, GameMode mode, FilenameType type) const; private: - static const std::array>, 0x10> NAMES; + static const std::array>, 0x12> NAMES; }; void generate_variations_deprecated( diff --git a/src/PlayerSubordinates.cc b/src/PlayerSubordinates.cc index 7e6e9ef0..e0cc481c 100644 --- a/src/PlayerSubordinates.cc +++ b/src/PlayerSubordinates.cc @@ -697,6 +697,21 @@ void PlayerBank::assign_ids(uint32_t base_id) { } } +QuestFlagsV1& QuestFlagsV1::operator=(const QuestFlags& other) { + this->data[0] = other.data[0]; + this->data[1] = other.data[1]; + this->data[2] = other.data[2]; + return *this; +} + +QuestFlagsV1::operator QuestFlags() const { + QuestFlags ret; + ret.data[0] = this->data[0]; + ret.data[1] = this->data[1]; + ret.data[2] = this->data[2]; + return ret; +} + BattleRules::BattleRules(const JSON& json) { static const JSON empty_list = JSON::list(); diff --git a/src/PlayerSubordinates.hh b/src/PlayerSubordinates.hh index b34c921f..33a88e09 100644 --- a/src/PlayerSubordinates.hh +++ b/src/PlayerSubordinates.hh @@ -558,6 +558,13 @@ struct QuestFlags { } } __attribute__((packed)); +struct QuestFlagsV1 { + parray data; + + QuestFlagsV1& operator=(const QuestFlags& other); + operator QuestFlags() const; +} __attribute__((packed)); + struct BattleRules { enum class TechDiskMode : uint8_t { ALLOW = 0, diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 6095acd8..981e9782 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -365,15 +365,24 @@ static void on_1D(shared_ptr c, uint16_t, uint32_t, string&) { c->game_join_command_queue.reset(); } - if (c->config.check_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ITEM_STATE)) { - c->config.clear_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ITEM_STATE); - if (!is_ep3(c->version())) { - send_game_item_state(c); + if (!is_ep3(c->version())) { + if (c->config.check_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ITEM_STATE)) { + c->config.clear_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ITEM_STATE); + send_game_item_state(c); // 6x6D + } + if (c->config.check_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_OBJECT_STATE)) { + c->config.clear_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_OBJECT_STATE); + send_game_object_state(c); // 6x6C + } + if (c->config.check_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ENEMY_AND_SET_STATE)) { + c->config.clear_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ENEMY_AND_SET_STATE); + send_game_enemy_state(c); // 6x6B + send_game_set_state(c); // 6x6E } } if (c->config.check_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_FLAG_STATE)) { c->config.clear_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_FLAG_STATE); - send_game_flag_state(c); + send_game_flag_state(c); // 6x6F } } @@ -2501,13 +2510,24 @@ static void on_10(shared_ptr c, uint16_t, uint32_t, string& data) { } if (game->is_game()) { c->config.set_flag(Client::Flag::LOADING); - // If no one was in the game before, then there's no leader to send the - // item state - send it to the joining player (who is now the leader) + // If no one was in the game before, then there's no leader to send + // the game state - send it to the joining player (who is now the + // leader) if (game->count_clients() == 1) { - // No one was in the game before, so the object and enemy state is lost; - // regenerate it as if the game was just created - game->load_maps(); c->config.set_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ITEM_STATE); + // TODO: Eventually, we want to send the enemy and set states too, + // but currently this doesn't work well. Instead, we reset their + // flags so it's as if they were never defeated. + // c->config.set_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_ENEMY_AND_SET_STATE); + if (game->map) { + for (auto& enemy : game->map->enemies) { + enemy.game_flags = 0; + enemy.total_damage = 0; + enemy.state_flags = 0; + } + } + c->config.set_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_OBJECT_STATE); + c->config.set_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_FLAG_STATE); } } break; @@ -4301,6 +4321,15 @@ shared_ptr create_game_generic( game->map = make_shared(game->base_version, game->lobby_id, game->random_seed, game->opt_rand_crypt); } + // The game's quest flags are inherited from the creator, if known + if (c->version() == Version::BB_V4) { + game->quest_flag_values = make_unique(p->quest_flags); + game->quest_flags_known = nullptr; + } else { + game->quest_flag_values = make_unique(); + game->quest_flags_known = make_unique(); + } + return game; } diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index e7164591..65159bac 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -328,19 +328,19 @@ static shared_ptr get_sync_target(shared_ptr sender_c, uint8_t c return nullptr; } -static void on_forward_sync_joining_player_state(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { +static void on_sync_joining_player_compressed_state(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { auto target = get_sync_target(c, command, flag, false); if (!target) { return; } - uint8_t subcommand; + uint8_t orig_subcommand_number; size_t decompressed_size; size_t compressed_size; const void* compressed_data; if (is_pre_v1(c->version())) { const auto& cmd = check_size_t(data, size, 0xFFFF); - subcommand = cmd.header.basic_header.subcommand; + orig_subcommand_number = cmd.header.basic_header.subcommand; decompressed_size = cmd.decompressed_size; compressed_size = size - sizeof(cmd); compressed_data = reinterpret_cast(data) + sizeof(cmd); @@ -349,117 +349,276 @@ static void on_forward_sync_joining_player_state(shared_ptr c, uint8_t c if (cmd.compressed_size > size - sizeof(cmd)) { throw runtime_error("compressed end offset is beyond end of command"); } - subcommand = cmd.header.basic_header.subcommand; + orig_subcommand_number = cmd.header.basic_header.subcommand; decompressed_size = cmd.decompressed_size; compressed_size = cmd.compressed_size; compressed_data = reinterpret_cast(data) + sizeof(cmd); } + const auto* subcommand_def = def_for_subcommand(c->version(), orig_subcommand_number); + if (!subcommand_def) { + throw runtime_error("unknown sync subcommand"); + } + + string decompressed = bc0_decompress(compressed_data, compressed_size); if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { - string decompressed = bc0_decompress(compressed_data, compressed_size); c->log.info("Decompressed sync data (%zX -> %zX bytes; expected %zX):", compressed_size, decompressed.size(), decompressed_size); print_data(stderr, decompressed); } - if (is_pre_v1(c->version()) == is_pre_v1(target->version())) { - on_forward_check_game_loading(c, command, flag, data, size); + switch (subcommand_def->final_subcommand) { + case 0x6B: { + auto l = c->require_lobby(); + if (l->map) { + l->log.info("Checking client enemy state against server state"); + StringReader r(decompressed); + size_t count = r.size() / sizeof(G_SyncEnemyState_6x6B_Entry_Decompressed); + if (count != l->map->enemies.size()) { + l->log.warning("Enemy count from client (%zu) does not match enemy count from map (%zu)", + count, l->map->enemies.size()); + } else { + l->log.info("Enemy count from client matches enemy count from map (%zu)", l->map->enemies.size()); + } - } else if (is_pre_v1(target->version())) { - StringWriter w; - uint32_t cmd_size = ((compressed_size + sizeof(G_SyncGameStateHeader_DCNTE_6x6B_6x6C_6x6D_6x6E)) + 3) & (~3); - w.put(G_SyncGameStateHeader_DCNTE_6x6B_6x6C_6x6D_6x6E{ - .header = {{subcommand, 0x00, 0x0000}, cmd_size}, - .decompressed_size = decompressed_size, - }); - w.write(compressed_data, compressed_size); - while (w.size() & 3) { - w.put_u8(0); - } - const string& data_to_send = w.str(); - forward_subcommand(c, command, flag, data_to_send.data(), data_to_send.size()); + // TODO: We should UPDATE our view of the flags here, not just check them + for (size_t z = 0; z < min(count, l->map->enemies.size()); z++) { + const auto& entry = r.get(); + if (l->map->enemies[z].game_flags != entry.flags) { + l->log.warning("(E-%zX) Flags from client (%08" PRIX32 ") do not match game flags from map (%08" PRIX32 ")", + z, entry.flags.load(), l->map->enemies[z].game_flags); + } + if (l->map->enemies[z].total_damage != entry.total_damage) { + l->log.warning("(E-%zX) Total damage from client (%hu) does not match total damage from map (%hu)", + z, entry.total_damage.load(), l->map->enemies[z].total_damage); + } + } + } - } else { - StringWriter w; - uint32_t cmd_size = ((compressed_size + sizeof(G_SyncGameStateHeader_6x6B_6x6C_6x6D_6x6E)) + 3) & (~3); - w.put(G_SyncGameStateHeader_6x6B_6x6C_6x6D_6x6E{ - .header = {{subcommand, 0x00, 0x0000}, cmd_size}, - .decompressed_size = decompressed_size, - .compressed_size = compressed_size, - }); - w.write(compressed_data, compressed_size); - while (w.size() & 3) { - w.put_u8(0); + send_game_join_sync_command_compressed( + target, + compressed_data, + compressed_size, + decompressed_size, + subcommand_def->nte_subcommand, + subcommand_def->proto_subcommand, + subcommand_def->final_subcommand); + break; } - const string& data_to_send = w.str(); - forward_subcommand(c, command, flag, data_to_send.data(), data_to_send.size()); + + case 0x6C: { + auto l = c->require_lobby(); + if (l->map) { + l->log.info("Checking client object state against server state"); + StringReader r(decompressed); + size_t count = r.size() / sizeof(G_SyncObjectState_6x6C_Entry_Decompressed); + if (count > l->map->objects.size()) { + l->log.warning("Object count from client (%zu) exceeds object count from map (%zu)", + count, l->map->objects.size()); + } else if (count < l->map->objects.size()) { + // This is normal because we load objects for inaccessible maps (e.g. lobby) + l->log.info("Object count from client (%zu) is less than object count from map (%zu) (this is normal)", + count, l->map->objects.size()); + } else { + l->log.info("Object count from client matches object count from map (%zu)", l->map->objects.size()); + } + + // TODO: We should UPDATE our view of the flags here, not just check them + for (size_t z = 0; z < min(count, l->map->enemies.size()); z++) { + const auto& entry = r.get(); + if (l->map->objects[z].game_flags != entry.flags) { + l->log.warning("(K-%zX) Flags from client (%04hX) do not match game flags from map (%04hX)", + z, entry.flags.load(), l->map->objects[z].game_flags); + } + } + } + + send_game_join_sync_command_compressed( + target, + compressed_data, + compressed_size, + decompressed_size, + subcommand_def->nte_subcommand, + subcommand_def->proto_subcommand, + subcommand_def->final_subcommand); + break; + } + + case 0x6D: { + if (decompressed.size() < sizeof(G_SyncItemState_6x6D_Decompressed)) { + throw runtime_error(string_printf( + "decompressed 6x6D data (0x%zX bytes) is too short for header (0x%zX bytes)", + decompressed.size(), sizeof(G_SyncItemState_6x6D_Decompressed))); + } + auto* decompressed_cmd = reinterpret_cast(decompressed.data()); + + size_t num_floor_items = 0; + 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); + if (decompressed.size() < required_size) { + throw runtime_error(string_printf( + "decompressed 6x6D data (0x%zX bytes) is too short for all floor items (0x%zX bytes)", + decompressed.size(), required_size)); + } + + auto l = c->require_lobby(); + size_t target_num_items = target->character()->inventory.num_items; + for (size_t z = 0; z < 12; z++) { + uint32_t client_next_id = decompressed_cmd->next_item_id_per_player[z]; + uint32_t server_next_id = l->next_item_id_for_client[z]; + if (client_next_id == server_next_id) { + l->log.info("Next item ID for player %zu (%08" PRIX32 ") matches expected value", z, l->next_item_id_for_client[z]); + } else if ((z == target->lobby_client_id) && (client_next_id == server_next_id - target_num_items)) { + l->log.info("Next item ID for player %zu (%08" PRIX32 ") matches expected value before inventory item ID assignment (%08" PRIX32 ")", z, l->next_item_id_for_client[z], static_cast(server_next_id - target_num_items)); + } else { + l->log.warning("Next item ID for player %zu (%08" PRIX32 ") does not match expected value (%08" PRIX32 ")", + z, decompressed_cmd->next_item_id_per_player[z].load(), l->next_item_id_for_client[z]); + } + } + + // The leader's item state is never forwarded since the leader may be able + // to see items that the joining player should not see. We always generate + // a new item state for the joining player instead. + send_game_item_state(target); + break; + } + + case 0x6E: { + StringReader r(decompressed); + const auto& dec_header = r.get(); + if (dec_header.total_size != dec_header.entity_set_flags_size + dec_header.event_set_flags_size + dec_header.unused_size) { + throw runtime_error("incorrect size fields in 6x6E header"); + } + + auto l = c->require_lobby(); + if (l->map) { + l->log.info("Checking client set flag state against server state"); + + StringReader set_flags_r = r.sub(r.where(), dec_header.entity_set_flags_size); + const auto& set_flags_header = set_flags_r.get(); + + if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { + c->log.info("Set flags data:"); + print_data(stderr, set_flags_r.all()); + } + + if (set_flags_header.num_object_sets > l->map->objects.size()) { + l->log.warning("Object set count from client (%hu) exceeds object count from map (%zu)", + set_flags_header.num_object_sets.load(), l->map->objects.size()); + } else if (set_flags_header.num_object_sets < l->map->objects.size()) { + // This is normal because we load objects for inaccessible maps (e.g. lobby) + l->log.info("Object set count from client (%hu) is less than object count from map (%zu) (this is normal)", + set_flags_header.num_object_sets.load(), l->map->objects.size()); + } else { + l->log.info("Object set count from client matches object count from map (%zu)", l->map->objects.size()); + } + for (size_t z = 0; z < min(set_flags_header.num_object_sets, l->map->objects.size()); z++) { + uint16_t flags = set_flags_r.get_u16l(); + if (flags != l->map->objects[z].set_flags) { + l->log.warning("(K-%zX) Set flags from client (%04hX) do not match flags from map (%04hX)", + z, flags, l->map->objects[z].set_flags); + } + } + + set_flags_r.go(sizeof(set_flags_header) + set_flags_header.num_object_sets * sizeof(le_uint16_t)); + if (set_flags_header.num_enemy_sets != l->map->enemy_set_flags.size()) { + l->log.warning("Enemy set count from client (%hu) does not match count from map (%zu)", + set_flags_header.num_enemy_sets.load(), l->map->enemy_set_flags.size()); + } else { + l->log.info("Enemy set count from client matches count from map (%zu)", l->map->enemy_set_flags.size()); + } + for (size_t z = 0; z < min(set_flags_header.num_enemy_sets, l->map->enemy_set_flags.size()); z++) { + uint16_t flags = set_flags_r.get_u16l(); + if (flags != l->map->enemy_set_flags[z]) { + l->log.warning("(S-%zX) Set flags from client (%04hX) do not match flags from map (%04hX)", + z, flags, l->map->enemy_set_flags[z]); + } + } + + StringReader event_set_flags_r = r.sub(r.where() + dec_header.entity_set_flags_size, dec_header.event_set_flags_size); + if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { + c->log.info("Event flags data:"); + print_data(stderr, event_set_flags_r.all()); + } + size_t num_event_flags = event_set_flags_r.size() / sizeof(le_uint16_t); + if (num_event_flags != l->map->events.size()) { + l->log.warning("Event count from client (%zu) does not match count from map (%zu)", + num_event_flags, l->map->events.size()); + } else { + l->log.info("Event count from client matches count from map (%zu)", l->map->events.size()); + } + for (size_t z = 0; z < min(num_event_flags, l->map->events.size()); z++) { + uint16_t flags = event_set_flags_r.get_u16l(); + const auto& event = l->map->events[z]; + if (flags != event.flags) { + l->log.warning("(W-%02hhX-%" PRIX32 ") Event flags from client (%04hX) do not match flags from map (%04hX)", + event.floor, event.event_id, flags, event.flags); + } + } + } + + size_t expected_unused_size = is_v1(c->version()) ? 0x200 : 0x240; + size_t target_unused_size = is_v1(target->version()) ? 0x200 : 0x240; + if (dec_header.unused_size != expected_unused_size) { + l->log.warning("Unused data size (0x%" PRIX32 ") does not match expected size (0x%zX)", + dec_header.unused_size.load(), expected_unused_size); + } + if (dec_header.unused_size != target_unused_size) { + l->log.info("Resizing unused data from 0x%" PRIX32 " bytes to 0x%zX bytes", + dec_header.unused_size.load(), target_unused_size); + if (dec_header.unused_size >= decompressed.size()) { + throw runtime_error("unused size is too large"); + } + decompressed.resize(decompressed.size() - dec_header.unused_size.load() + target_unused_size, '\0'); + auto* wdec_header = reinterpret_cast(decompressed.data()); + wdec_header->unused_size = target_unused_size; + wdec_header->total_size = wdec_header->entity_set_flags_size + wdec_header->event_set_flags_size + wdec_header->unused_size; + } + + send_game_join_sync_command_compressed( + target, + compressed_data, + compressed_size, + decompressed_size, + subcommand_def->nte_subcommand, + subcommand_def->proto_subcommand, + subcommand_def->final_subcommand); + break; + } + + default: + throw logic_error("invalid compressed sync state subcommand"); } } -static void on_sync_joining_player_item_state(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { - auto target = get_sync_target(c, command, flag, false); - if (!target) { +template +static void on_sync_joining_player_quest_flags_t(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { + const auto& cmd = check_size_t(data, size); + + if (!command_is_private(command)) { return; } - const auto& l = c->require_lobby(); - string decompressed; - size_t compressed_size; - size_t decompressed_size; - if (is_pre_v1(c->version())) { - const auto& cmd = check_size_t(data, size, 0xFFFF); - compressed_size = size - sizeof(cmd); - decompressed_size = cmd.decompressed_size; - decompressed = bc0_decompress(reinterpret_cast(data) + sizeof(cmd), compressed_size); + auto l = c->require_lobby(); + if (l->is_game() && l->any_client_loading() && (l->leader_id == c->lobby_client_id)) { + l->quest_flags_known = nullptr; // All quest flags are now known + l->quest_flag_values = make_unique(cmd.quest_flags); + auto target = l->clients.at(flag); + if (target) { + send_game_flag_state(target); + } + } +} + +static void on_sync_joining_player_quest_flags(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { + if (is_v1(c->version())) { + on_sync_joining_player_quest_flags_t(c, command, flag, data, size); } else { - const auto& cmd = check_size_t(data, size, 0xFFFF); - compressed_size = cmd.compressed_size; - decompressed_size = cmd.decompressed_size; - if (compressed_size > size - sizeof(cmd)) { - throw runtime_error("compressed end offset is beyond end of command"); - } - decompressed = bc0_decompress(reinterpret_cast(data) + sizeof(cmd), cmd.compressed_size); + on_sync_joining_player_quest_flags_t(c, command, flag, data, size); } - if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) { - c->log.info("Decompressed item sync data (%zX -> %zX bytes; expected %zX):", - compressed_size, decompressed.size(), decompressed_size); - print_data(stderr, decompressed); - } - - if (decompressed.size() < sizeof(G_SyncItemState_6x6D_Decompressed)) { - throw runtime_error(string_printf( - "decompressed 6x6D data (0x%zX bytes) is too short for header (0x%zX bytes)", - decompressed.size(), sizeof(G_SyncItemState_6x6D_Decompressed))); - } - auto* decompressed_cmd = reinterpret_cast(decompressed.data()); - - size_t num_floor_items = 0; - 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); - if (decompressed.size() < required_size) { - throw runtime_error(string_printf( - "decompressed 6x6D data (0x%zX bytes) is too short for all floor items (0x%zX bytes)", - decompressed.size(), required_size)); - } - - size_t target_num_items = target->character()->inventory.num_items; - for (size_t z = 0; z < 12; z++) { - uint32_t client_next_id = decompressed_cmd->next_item_id_per_player[z]; - uint32_t server_next_id = l->next_item_id_for_client[z]; - if (client_next_id == server_next_id) { - l->log.info("Next item ID for player %zu (%08" PRIX32 ") matches expected value", z, l->next_item_id_for_client[z]); - } else if ((z == target->lobby_client_id) && (client_next_id == server_next_id - target_num_items)) { - l->log.info("Next item ID for player %zu (%08" PRIX32 ") matches expected value before inventory item ID assignment (%08" PRIX32 ")", z, l->next_item_id_for_client[z], static_cast(server_next_id - target_num_items)); - } else { - l->log.warning("Next item ID for player %zu (%08" PRIX32 ") does not match expected value (%08" PRIX32 ")", - z, decompressed_cmd->next_item_id_per_player[z].load(), l->next_item_id_for_client[z]); - } - } - - send_game_item_state(target); } class Parsed6x70Data { @@ -2404,39 +2563,50 @@ static void on_set_quest_flag(shared_ptr c, uint8_t command, uint8_t fla return; } - uint16_t flag_index, difficulty, action; + uint16_t flag_num, difficulty, action; if (is_v1_or_v2(c->version()) && (c->version() != Version::GC_NTE)) { const auto& cmd = check_size_t(data, size); - flag_index = cmd.flag; + flag_num = cmd.flag; action = cmd.action; difficulty = l->difficulty; } else { const auto& cmd = check_size_t(data, size); - flag_index = cmd.flag; + flag_num = cmd.flag; action = cmd.action; difficulty = cmd.difficulty; } - if ((flag_index >= 0x400) || (difficulty > 3) || (action > 1)) { + // The client explicitly checks action for both 0 and 1 - any other value + // means no operation is performed. + if ((flag_num >= 0x400) || (difficulty > 3) || (action > 1)) { return; } + bool should_set = (action == 0); - // TODO: Should we allow overlays here? - auto p = c->character(true, false); - - auto s = c->require_server_state(); - if (s->quest_flag_persist_mask.get(flag_index)) { - // The client explicitly checks for both 0 and 1 - any other value means no - // operation is performed. - if (action == 0) { - c->log.info("Setting quest flag %s:%03hX", name_for_difficulty(difficulty), flag_index); - p->quest_flags.set(difficulty, flag_index); - } else if (action == 1) { - c->log.info("Clearing quest flag %s:%03hX", name_for_difficulty(difficulty), flag_index); - p->quest_flags.clear(difficulty, flag_index); - } + if (l->quest_flags_known) { + l->quest_flags_known->set(difficulty, flag_num); + } + if (should_set) { + l->quest_flag_values->set(difficulty, flag_num); } else { - c->log.info("Quest flag %s:%03hX cannot be modified", name_for_difficulty(difficulty), flag_index); + l->quest_flag_values->clear(difficulty, flag_num); + } + + if (c->version() == Version::BB_V4) { + auto s = c->require_server_state(); + // TODO: Should we allow overlays here? + auto p = c->character(true, false); + if (s->quest_flag_persist_mask.get(flag_num)) { + if (should_set) { + c->log.info("Setting quest flag %s:%03hX", name_for_difficulty(difficulty), flag_num); + p->quest_flags.set(difficulty, flag_num); + } else { + c->log.info("Clearing quest flag %s:%03hX", name_for_difficulty(difficulty), flag_num); + p->quest_flags.clear(difficulty, flag_num); + } + } else { + c->log.info("Quest flag %s:%03hX cannot be modified", name_for_difficulty(difficulty), flag_num); + } } forward_subcommand(c, command, flag, data, size); @@ -2448,12 +2618,12 @@ static void on_set_quest_flag(shared_ptr c, uint8_t command, uint8_t fla // 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. - if ((difficulty == 0) && (flag_index == 0x0035)) { + if ((difficulty == 0) && (flag_num == 0x0035)) { boss_enemy_type = EnemyType::DARK_FALZ_2; - } else if ((difficulty != 0) && (flag_index == 0x0037)) { + } else if ((difficulty != 0) && (flag_num == 0x0037)) { boss_enemy_type = EnemyType::DARK_FALZ_3; } - } else if (is_ep2 && (flag_index == 0x0057) && (c->floor == 0x0D)) { + } else if (is_ep2 && (flag_num == 0x0057) && (c->floor == 0x0D)) { boss_enemy_type = EnemyType::OLGA_FLOW_2; } @@ -2494,6 +2664,52 @@ static void on_set_quest_flag(shared_ptr c, uint8_t command, uint8_t fla } } +static void on_set_entity_flag(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { + auto l = c->require_lobby(); + if (!l->is_game()) { + return; + } + + const auto& cmd = check_size_t(data, size); + if (l->map) { + if (cmd.header.enemy_id >= 0x1000 && cmd.header.enemy_id < 0x4000) { + try { + l->map->enemies.at(cmd.header.enemy_id - 0x1000).game_flags |= cmd.flags; + } catch (const out_of_range&) { + l->log.warning("Flag update refers to missing enemy"); + } + } else if (cmd.header.enemy_id >= 0x4000) { + try { + l->map->objects.at(cmd.header.enemy_id - 0x4000).game_flags |= cmd.flags; + } catch (const out_of_range&) { + l->log.warning("Flag update refers to missing object"); + } + } + } + + forward_subcommand(c, command, flag, data, size); +} + +static void on_trigger_set_event(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { + auto l = c->require_lobby(); + if (!l->is_game()) { + return; + } + + const auto& cmd = check_size_t(data, size); + if (l->map) { + // TODO: The game's logic is significantly more complex than this. Do we + // need to do anything fancy here? + try { + l->map->get_event(cmd.floor, cmd.event_id).flags |= 0x04; + } catch (const out_of_range&) { + l->log.warning("Client triggered missing event W-%02" PRIX32 "-%" PRIX32, cmd.floor.load(), cmd.event_id.load()); + } + } + + forward_subcommand(c, command, flag, data, size); +} + static void on_battle_scores(shared_ptr c, uint8_t command, uint8_t, void* data, size_t size) { const auto& cmd = check_size_t>(data, size); @@ -3935,22 +4151,22 @@ const SubcommandDefinition subcommand_definitions[0x100] = { /* 6x64 */ {0x56, 0x5D, 0x64, on_forward_check_game}, /* 6x65 */ {0x57, 0x5E, 0x65, on_forward_check_game}, /* 6x66 */ {0x00, 0x00, 0x66, on_forward_check_game}, - /* 6x67 */ {0x58, 0x5F, 0x67, on_forward_check_game}, + /* 6x67 */ {0x58, 0x5F, 0x67, on_trigger_set_event}, /* 6x68 */ {0x59, 0x60, 0x68, on_forward_check_game}, /* 6x69 */ {0x5A, 0x61, 0x69, on_npc_control}, /* 6x6A */ {0x5B, 0x62, 0x6A, on_forward_check_game}, - /* 6x6B */ {0x5C, 0x63, 0x6B, on_forward_sync_joining_player_state, SDF::USE_JOIN_COMMAND_QUEUE}, - /* 6x6C */ {0x5D, 0x64, 0x6C, on_forward_sync_joining_player_state, SDF::USE_JOIN_COMMAND_QUEUE}, - /* 6x6D */ {0x5E, 0x65, 0x6D, on_sync_joining_player_item_state, SDF::USE_JOIN_COMMAND_QUEUE}, - /* 6x6E */ {0x5F, 0x66, 0x6E, on_forward_sync_joining_player_state, SDF::USE_JOIN_COMMAND_QUEUE}, - /* 6x6F */ {0x00, 0x00, 0x6F, on_forward_check_game_loading, SDF::USE_JOIN_COMMAND_QUEUE}, + /* 6x6B */ {0x5C, 0x63, 0x6B, on_sync_joining_player_compressed_state, SDF::USE_JOIN_COMMAND_QUEUE}, + /* 6x6C */ {0x5D, 0x64, 0x6C, on_sync_joining_player_compressed_state, SDF::USE_JOIN_COMMAND_QUEUE}, + /* 6x6D */ {0x5E, 0x65, 0x6D, on_sync_joining_player_compressed_state, SDF::USE_JOIN_COMMAND_QUEUE}, + /* 6x6E */ {0x5F, 0x66, 0x6E, on_sync_joining_player_compressed_state, SDF::USE_JOIN_COMMAND_QUEUE}, + /* 6x6F */ {0x00, 0x00, 0x6F, on_sync_joining_player_quest_flags, SDF::USE_JOIN_COMMAND_QUEUE}, /* 6x70 */ {0x60, 0x67, 0x70, on_sync_joining_player_disp_and_inventory, SDF::USE_JOIN_COMMAND_QUEUE}, /* 6x71 */ {0x00, 0x00, 0x71, on_forward_check_game_loading, SDF::USE_JOIN_COMMAND_QUEUE}, /* 6x72 */ {0x61, 0x68, 0x72, on_forward_check_game_loading, SDF::USE_JOIN_COMMAND_QUEUE}, /* 6x73 */ {0x00, 0x00, 0x73, on_forward_check_game_quest}, /* 6x74 */ {0x62, 0x69, 0x74, on_word_select, SDF::ALWAYS_FORWARD_TO_WATCHERS}, /* 6x75 */ {0x00, 0x00, 0x75, on_set_quest_flag}, - /* 6x76 */ {0x00, 0x00, 0x76, on_forward_check_game}, + /* 6x76 */ {0x00, 0x00, 0x76, on_set_entity_flag}, /* 6x77 */ {0x00, 0x00, 0x77, on_forward_check_game}, /* 6x78 */ {0x00, 0x00, 0x78, forward_subcommand_m}, /* 6x79 */ {0x00, 0x00, 0x79, on_forward_check_lobby}, diff --git a/src/SendCommands.cc b/src/SendCommands.cc index a8b0c73e..5f3f8319 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -2396,30 +2396,44 @@ void send_ep3_change_music(Channel& ch, uint32_t song) { ch.send(0x60, 0x00, cmd); } -static void send_game_join_sync_command( +void send_game_join_sync_command( shared_ptr c, const void* data, size_t size, uint8_t dc_nte_sc, uint8_t dc_11_2000_sc, uint8_t sc) { string compressed_data = bc0_compress(data, size); + send_game_join_sync_command_compressed(c, compressed_data.data(), compressed_data.size(), size, dc_nte_sc, dc_11_2000_sc, sc); +} +void send_game_join_sync_command(shared_ptr c, const string& data, uint8_t dc_nte_sc, uint8_t dc_11_2000_sc, uint8_t sc) { + send_game_join_sync_command(c, data.data(), data.size(), dc_nte_sc, dc_11_2000_sc, sc); +} + +void send_game_join_sync_command_compressed( + shared_ptr c, + const void* data, + size_t size, + size_t decompressed_size, + uint8_t dc_nte_sc, + uint8_t dc_11_2000_sc, + uint8_t sc) { StringWriter w; if (is_pre_v1(c->version())) { G_SyncGameStateHeader_DCNTE_6x6B_6x6C_6x6D_6x6E compressed_header; compressed_header.header.basic_header.subcommand = (c->version() == Version::DC_NTE) ? dc_nte_sc : dc_11_2000_sc; compressed_header.header.basic_header.size = 0x00; compressed_header.header.basic_header.unused = 0x0000; - compressed_header.header.size = (compressed_data.size() + sizeof(G_SyncGameStateHeader_DCNTE_6x6B_6x6C_6x6D_6x6E) + 3) & (~3); - compressed_header.decompressed_size = size; + compressed_header.header.size = (size + sizeof(G_SyncGameStateHeader_DCNTE_6x6B_6x6C_6x6D_6x6E) + 3) & (~3); + compressed_header.decompressed_size = decompressed_size; w.put(compressed_header); } else { G_SyncGameStateHeader_6x6B_6x6C_6x6D_6x6E compressed_header; compressed_header.header.basic_header.subcommand = sc; compressed_header.header.basic_header.size = 0x00; compressed_header.header.basic_header.unused = 0x0000; - compressed_header.header.size = (compressed_data.size() + sizeof(G_SyncGameStateHeader_6x6B_6x6C_6x6D_6x6E) + 3) & (~3); - compressed_header.decompressed_size = size; - compressed_header.compressed_size = compressed_data.size(); + compressed_header.header.size = (size + sizeof(G_SyncGameStateHeader_6x6B_6x6C_6x6D_6x6E) + 3) & (~3); + compressed_header.decompressed_size = decompressed_size; + compressed_header.compressed_size = size; w.put(compressed_header); } - w.write(compressed_data); + w.write(data, size); while (w.size() & 3) { w.put_u8(0x00); } @@ -2525,31 +2539,119 @@ void send_game_object_state(shared_ptr c) { send_game_join_sync_command(c, entries.data(), entries.size() * sizeof(entries[0]), 0x5D, 0x64, 0x6C); } -void send_game_flag_state(shared_ptr c) { +void send_game_set_state(shared_ptr c) { + auto l = c->require_lobby(); + if (!l->map) { + return; + } + + size_t num_object_sets = l->map->objects.size(); + size_t num_enemy_sets = l->map->enemy_set_flags.size(); + + G_SyncSetFlagState_6x6E_Decompressed::EntitySetFlags entity_set_flags_header; + entity_set_flags_header.object_set_flags_offset = sizeof(entity_set_flags_header); + entity_set_flags_header.num_object_sets = num_object_sets; + entity_set_flags_header.enemy_set_flags_offset = sizeof(entity_set_flags_header) + num_object_sets * sizeof(le_uint16_t); + entity_set_flags_header.num_enemy_sets = num_enemy_sets; + + G_SyncSetFlagState_6x6E_Decompressed header; + header.entity_set_flags_size = sizeof(entity_set_flags_header) + (num_object_sets + num_enemy_sets) * sizeof(le_uint16_t); + header.event_set_flags_size = sizeof(le_uint16_t) * l->map->events.size(); + header.unused_size = is_v1(c->version()) ? 0x200 : 0x240; + header.total_size = header.entity_set_flags_size + header.event_set_flags_size + header.unused_size; + + StringWriter w; + w.put(header); + w.put(entity_set_flags_header); + for (const auto& obj : l->map->objects) { + w.put_u16l(obj.set_flags); + } + for (uint16_t enemy_set_flags : l->map->enemy_set_flags) { + w.put_u16l(enemy_set_flags); + } + for (const auto& event : l->map->events) { + w.put_u16l(event.flags); + } + w.extend_by(header.unused_size, 0x00); + + send_game_join_sync_command(c, w.str(), 0x5F, 0x66, 0x6E); +} + +template +void send_game_flag_state_t(shared_ptr c) { auto l = c->require_lobby(); - G_SetQuestFlags_6x6F cmd; - cmd.header.subcommand = 0x6F; - cmd.header.size = sizeof(G_SetQuestFlags_6x6F) >> 2; - cmd.header.unused = 0x0000; - cmd.quest_flags = c->character()->quest_flags; - - for (const auto& lc : l->clients) { - if (!lc) { - continue; + if (l->quest_flags_known) { // Not all flags known; send multiple 6x75s + StringWriter w; + bool use_v3_cmd = !is_v1_or_v2(c->version()) || (c->version() == Version::GC_NTE); + for (uint8_t difficulty = 0; difficulty < 4; difficulty++) { + if ((difficulty != l->difficulty) && !use_v3_cmd) { + continue; + } + const auto& diff_flags = l->quest_flag_values->data.at(difficulty); + const auto& diff_known_flags = l->quest_flags_known->data.at(difficulty); + for (uint8_t z = 0; z < diff_known_flags.data.size(); z++) { + uint8_t known_flags = diff_known_flags.data[z]; + if (!known_flags) { + continue; + } + uint8_t flag_values = diff_flags.data[z]; + for (uint8_t sh = 0; sh < 8; sh++) { + if ((known_flags << sh) & 0x80) { + uint16_t flag_num = ((z << 3) | sh); + if (use_v3_cmd) { + w.put(G_UpdateQuestFlag_V3_BB_6x75{ + {{0x75, 0x03, 0x0000}, flag_num, (((flag_values << sh) & 0x80) ? 0 : 1)}, difficulty, 0}); + } else { + w.put(G_UpdateQuestFlag_DC_PC_6x75{ + {0x75, 0x02, 0x0000}, flag_num, (((flag_values << sh) & 0x80) ? 0 : 1)}); + } + } + } + } } - if (lc->game_join_command_queue) { - lc->log.info("Client not ready to receive join commands; adding to queue"); - auto& cmd = lc->game_join_command_queue->emplace_back(); - cmd.command = 0x0060; - cmd.flag = 0x00000000; + + if (w.size() > 0) { + if (c->game_join_command_queue) { + c->log.info("Client not ready to receive join commands; adding to queue"); + auto& cmd = c->game_join_command_queue->emplace_back(); + cmd.command = 0x006D; + cmd.flag = c->lobby_client_id; + cmd.data = std::move(w.str()); + } else { + send_command(c, 0x6D, c->lobby_client_id, w.str()); + } + } + + } else { // All flags known; send 6x6F + CmdT cmd; + cmd.header.subcommand = 0x6F; + cmd.header.size = sizeof(CmdT) >> 2; + cmd.header.unused = 0x0000; + cmd.quest_flags = (l && !l->quest_flags_known) ? *l->quest_flag_values : c->character()->quest_flags; + + if (c->game_join_command_queue) { + c->log.info("Client not ready to receive join commands; adding to queue"); + auto& cmd = c->game_join_command_queue->emplace_back(); + cmd.command = 0x0062; + cmd.flag = c->lobby_client_id; cmd.data.assign(reinterpret_cast(&cmd), sizeof(cmd)); } else { - send_command_t(lc, 0x60, 0x00, cmd); + send_command_t(c, 0x62, c->lobby_client_id, cmd); } } } +void send_game_flag_state(shared_ptr c) { + // DC NTE and 11/2000 don't have this command at all; v1 has it but it doesn't + // include flags for Ultimate. + if (!is_v1(c->version())) { + send_game_flag_state_t(c); + } else if (!is_pre_v1(c->version())) { + send_game_flag_state_t(c); + } +} + void send_drop_item_to_channel(shared_ptr s, Channel& ch, const ItemData& item, bool from_enemy, uint8_t floor, float x, float z, uint16_t entity_id) { uint8_t subcommand = get_pre_v1_subcommand(ch.version, 0x51, 0x58, 0x5F); diff --git a/src/SendCommands.hh b/src/SendCommands.hh index 8d8d51d0..e402fe14 100644 --- a/src/SendCommands.hh +++ b/src/SendCommands.hh @@ -309,9 +309,22 @@ void send_warp(std::shared_ptr l, uint32_t floor, bool is_private); void send_ep3_change_music(Channel& ch, uint32_t song); void send_revive_player(std::shared_ptr c); +void send_game_join_sync_command( + std::shared_ptr c, const void* data, size_t size, uint8_t dc_nte_sc, uint8_t dc_11_2000_sc, uint8_t sc); +void send_game_join_sync_command( + std::shared_ptr c, const std::string& data, uint8_t dc_nte_sc, uint8_t dc_11_2000_sc, uint8_t sc); +void send_game_join_sync_command_compressed( + std::shared_ptr c, + const void* data, + size_t size, + size_t decompressed_size, + uint8_t dc_nte_sc, + uint8_t dc_11_2000_sc, + uint8_t sc); void send_game_item_state(std::shared_ptr c); void send_game_enemy_state(std::shared_ptr c); void send_game_object_state(std::shared_ptr c); +void send_game_set_state(std::shared_ptr c); void send_game_flag_state(std::shared_ptr c); void send_drop_item_to_channel(std::shared_ptr s, Channel& ch, const ItemData& item, bool from_enemy, uint8_t floor, float x, float z, uint16_t request_id); diff --git a/src/Text.cc b/src/Text.cc index 27174926..f37260bc 100644 --- a/src/Text.cc +++ b/src/Text.cc @@ -35,7 +35,12 @@ TextTranscoder::Result TextTranscoder::operator()( const void* orig_src = src; while (src_bytes > 0) { size_t src_bytes_before = src_bytes; - size_t ret = iconv(this->ic, reinterpret_cast(const_cast(&src)), &src_bytes, reinterpret_cast(&dest), &dest_bytes); + size_t ret = iconv( + this->ic, + reinterpret_cast(const_cast(&src)), + &src_bytes, + reinterpret_cast(&dest), + &dest_bytes); size_t bytes_read = reinterpret_cast(src) - reinterpret_cast(orig_src); if (ret == this->FAILURE_RESULT) { @@ -64,7 +69,6 @@ TextTranscoder::Result TextTranscoder::operator()( default: throw runtime_error("transcoding failed: " + string_for_error(errno)); } - } else if (src_bytes_before == src_bytes) { throw runtime_error("could not transcode any characters"); } diff --git a/system/maps/pc-nte/map_ancient01_00_00.evt b/system/maps/pc-nte/map_ancient01_00_00.evt index 265c49f01d8697d1a416c752bcb4a06c40859a19..3229e7e9d7949ec3c27f76492d7a8fa61f349fdf 100755 GIT binary patch literal 872 zcmX|KXJ@Mkp|G;#@8A!x z*22U{g|V;@3XPTPxjT2~OD4PL_nmX^%$@Ja5HJZ~PRpN7X~6IRL!kfF^RzJQvoW!c z{Wpf1yzU#<`dW49EN5#{hmq}uuPwGUn|%9?s6s#G(J+Zg3=VsJ1Zj)UjG1QHyDjzbMu=dGxO h58jfGT5Wxj#xvmA^4^DL$;V{L2V<;)Bd}ht_a72BIMx6F literal 488 zcmWlTJxmjE7{>1^wa4F5&kFbfSWkOWi>MSW+Gt`sIOxg(o67Y}6ecEJjJd=ZCC0(Y z#b6j9#&`~8k;E9TeoQ73ZDKHzKpAY)5Bhn1-~a1%`z62UdGZ9tU4kIbpfDf+O&UcK z9|@O)I*V#Fp_BMI*9Bj_+2UNcMm`w&?MYIQUun=vpUSUVeDD|C>PylpAI`v(PP_VZ zqsuTRPEkPRi?NU1B&D4=gqZHsK76O=Di5VquECF}X7^~6{0VD1891GRb&Yz{7N3FF z=cV@IYznGd5V5#A+Jo6Urbhba5 zf8eg8f_xLUqbfh3@~sqjtTvec;JUjtr&zoJ(}jhW1N;pXb=m~8<4Z}Wiyb-U%ds(3 zF5o!>N3-H(@3n%+B1U-xhh;pSz%zI@l=prrNOq*Sp$z3uu^m>j6ZcB_V-NC6ObOy~ z0|zlAqnf}I7?9DdCjOpmg#E9=JFaqnSy*n)hS*|_4cu-XNpWv{zGC~U()OXeau|CI z?8R;w_a|^4`X!W6*^6BU`f!hcYd2Vgb%sY+v+j}=X(d*%=a(vv-&f^#?r?UuhbC*1 T{jFxZV)YHf&2EZuB0=~MXM7Bz diff --git a/system/maps/pc-nte/map_ancient01_00_01.evt b/system/maps/pc-nte/map_ancient01_00_01.evt index 8cf0a2ea34dac9baacf2ef09fb64087640e32591..f584d6d6c22b81620c74a2168fcba753b69ac4e0 100755 GIT binary patch literal 1136 zcmYk4zjI7+6vfZYu4Hra4Iz?EEOytAZ2TrcFl#27ZK8`A6BUz$0;5p+6Hrj8G@??e zRQ>=RH5nzN!O+LK?|b)+oq0R&eBL?t-S4}%RsqHU%;@spgW}&X0zoCCj~Z#6Eab+1*j^lGx{=KFeoY6#G)LT(2j#S+ZR3s@O{O*NuA1Vvk~n zy3yZ?Sn?$|R=_GO`6e5OL4qS-0!P8^;0|yM90F@FOTM$lZQ!6HzL>_H;4W|+RR5NI z;Vk)zN%#g?@}05dTVk95C&4;61?~kK;54`g+z;-P$ydaZFNGyv1LF*MSi)y*$){`V zfV1Ec@F;i)JP5YI19~5uV5?AKMMWLg~u{&>)coZdi0y+C*7t zOf*4QNld^5S8fdDl@MQ1kxC3fQmnUyc39x_|L>Jy^_}E=$vGAwljEe@$W?Q|?p)9e zTJKm@>F$Z)8R1+xmj_PAVwM}l}1trAwbQO3&hN22BKPDQFJ=N^tZU(7Bf zZ>?5VGizeWvP4bdF$s_JUni5At1_hxr-bwsrT4QB2v9dDS(C`O93$?JY zxCuF_rx{|OPbfku4FCU ztmUEDa-Q4GS{{k@3rkv_imepZsPsJ(TP>_vvFBos6`|SljBRC43QOO+qkJoC>F2s# zvA2^^Xy|7xZ`!5-_pjK7BIYvp{!_MDSn4(P?Pu1>bv3b_c0fZXe_LB@x3Dy0LhMyx>7HlAUKf_` zc}~p3T?Mc*8v0U>^H79s&=8)1dxZJOXyWqu{X^6$>?w6`BBN!8x!C_P|AO z2|Np)15blzz-3To@K~gItj>4>JgN8a6u1D+gU2H~4_=73$CaALC7Q>zna4#LSHM-+ HU@-U(HC07~ literal 578 zcmW-dT}V@59LCS)wA1TRmzvFOwU0JG*7_i@kE_sFiQUyjBP6PA=R_nCL6OkSnxvFL zLP6~=w=OcGNf#m1)wB<~=qksSGHHIK@3uCYy7&Cw^LFOd1J93#A3PSmisSS-%C&L8 z&S-Q*j@jUSfSK}w$h0=XYDD&h@Y4nq&CWvn&h6JdCy4u4}0eAFKCvr_?b_AU5KBJ-1& z7yJ^N@so=XFiL9SPr=csY>CP@6VUDf%S=?h105=g$oIm~x#hhNt}XA#b-25@A@~;I z84Qj}ECb^(@FXgCOe>Lom}n50FRr|XH!6zA?_l7@5Uq78iGDb*6WN4AnYV99%%Q|G zQ1Za&VW+YNUF($w|AZ}4PF_(7aw^0NqQra-Me}9O6ksjWigp%{^Oak8FOH}v1NAtn zisJv8ao4L#8gFAExuQW=KO;>+=$`yT?5lb*wyGoAwFScuRAZ$jBz!`LXzbu+p%Gh2 z5bM(0vEekTb<>fe;L7skMV9oS+(I3{Tu*HcRFATgITEzz$ynY>w0}dX9Se3;sfF6C)ES~}bf2EOd+1*3;AuN;d7c-h3L)Yu xe&4On?s@P5**9$*9XN-~#r?gT2VMrRl}NH=C4K8WnOaZVI@2y)nK2E={RbGBMTP(X diff --git a/system/maps/pc-nte/map_ancient01_01_01.evt b/system/maps/pc-nte/map_ancient01_01_01.evt index 06edc4c0f36d85c1783164eaa20b12c9aa040058..8fca51b79e9d2dcf8044adb2f9310d20e2afa151 100755 GIT binary patch literal 1180 zcmX|>KX6P@6voflgv7Tu5_aQHLUz}`eGx>r$tcjIkxpUM=zD7^9;y%&2JMJMWx5Z|1(Y-*3-(=iYO_`_@K8rbJ|c!>&v9T?JypucK;DmAPPJ zRIi#t)~MqqC|@TVsacEcSmut_Y>8|!*`}H;lYPwG%{4no_Lalx?ufdlsP2ysnb%#R ze2~oRu997~Fy=KQ_bk=9(INBP^OUz^E>*;>kv*LBxJo?k8s&9Ej42Uk`dPfMMBELs zLAx)JukMj8<@>x>kI0sjd9NOmoygp=x_?i|o;P@7 z-IbWjQ>qKIL*{FHNqI4u&*cr-i_G=8yeGSyaXyz1WLJ{;Tt1QYBX)MS1=>kJS}|&g zurit$iDe}lSP%$N6K!oIJ`x{6AS$A5K?W!t7}A;h|G$H)@7$bol6#7Ubc!;yA*!4L z(tJeGQS=x9*{Ac6Kv6T%!=u+(97%JF*$}$QlXTH6z7~Fmu&Sq#sLodz+K3ZH$n9!cr02-4Y$Gz76;XL1K^El9Q*`rJShvR z-%N!JuBwH}3Fyk(81S=Yc0Gb!5EGz=Nm<Fvxnqu&xb1lGYLnQFNjV5y$tTe&2 zo+It0c#RqlL~tl&7QGF%U}JEukXVj_O{-hPgw}F_Rjh2(UXa-{iBtstfv(voU5@si z5RE0Vgwh++(Tr)a%!ziRQUhX<&W>(Qa(C79QGTh=HuH?HNbLOek+zx8 zwwoviZMl!KUETGIshU$u%B!W$$!m1}WZd|0*`?5l8C%XuY_lPD{72H~ODf5vRqR>l qJSmM!Pozuoifrjv?^~+qMF&1lty-1(wSpPPYMNgvkz*v2P}Dz4vS!Bs diff --git a/system/maps/pc-nte/map_ancient01_02_00.evt b/system/maps/pc-nte/map_ancient01_02_00.evt index 1f5582e7484b9d8994a5e888ba1e6b49c3cd9c3b..213eb25f04012f9bf2ef600895a9b3a2daafac53 100755 GIT binary patch literal 1080 zcmX|=OOH%p5Qg9CxqADygSn&st zNQgvY$41;%Ru)7^tnk+Ps=w~kIbF~B-l{rP)xT=M41hJ6e_mHt!#qsi{P{5DcV{(F zzb0iL3mdCg61${We(r2&=j7dJj99ITw=DN^IX70Zb+Ko`hM^sZU0KQ6q4Ihyr)67( zW$hWUK~<=s8QODVFY3C;Z-#a#?`2_Gdr_<#c{f9QRjgN7&U;O)Us%>&7kex791Ak< z%eL&Dql)ET-IRAB>g&Y0TVmh6Ve&gs-)(t63d=d}iTw`kMra?3?Z{l4f<2e_WPHSO z-dA#;7M5r9Mr0L4Ev|eBj0fa1mSr8{l4W2HXwq(Stc~9-IaDfm5Jf zUVWO7&WCNu$7?Qp_3w0QE5D+Lx8SGF{P{AM}F~&BiKJi(g1h}K6EiIk?_g<%~@8q21eCD{0VT7}okr<%U zhg6m+I0vkY&gF=1I>SvSvf+DJrdf!845xIRG*URS~PP6u?zj6mX>-@5B~ChRwEX$xEXI5SjtZo2slrNw_(Q#E^GLC!tnZbq(kM_So@oa?2n4=;hI_* z71Y7#oj>i|^j`z-iK*hZn6gQ)aJXxodyjZqUtEpG3q0!jebtj-_4Fd#8pt{B@4Xt& J?Ove_^B+F5LMH$K diff --git a/system/maps/pc-nte/map_ancient01_02_01.evt b/system/maps/pc-nte/map_ancient01_02_01.evt index b873749087e377d5efa8d554fa3a670124cd3d07..81b3170784280cd33fc19d15c726c9144df0aed9 100755 GIT binary patch literal 1212 zcmX|=%WsTf6vm%3Grf4`E!7)U=7MUqD(O@*YzV=Il})>0({^obZGn(j@dvmhR{jC8 z5V!Czh{VcHNU94v&-u~CT9iY2k#!WtEu5c?wY+^Jk|Uf#En0gHOea=$vN;-X$p-rHKRt2uW~?&jQp)kJ}F zvaQ1E6}up|U09=HSHxDT0~uPm-gU9}O+DndbT)r|L-wJtsJ9{Zv9PFjOYBo&LsjmU z*lzC8&pqyn-J8}!en0nkAn$%*(c`h$wVc~ktk3zG?6(hAemCd7lJ}>u$bBt#$HNqt za_&2MZyEy@bN(#%wj;$fbKDPk>p8BO_xn?-P%sP_NcI^cUkO`EOMjXy8I_4F3xdN>}g?fj!m)Tm&TG`6H9(Ej4R-2a24!< z%it-!@S9*<1JA%Lxwb92nk~70jVHk+IWAI5E=x-;L`yC`;{vz{&VwhwE_f6?1|A37 zU`Ii&J4>!KV+%Y2&Vh%)L*T4lxU?*}n2gil47d**2M>S;^}^L-oCK%9{on++2V{Be zqA~6T#}wuxOT~pRj RM?hT(-O%Lox8xHy`wsw^OC$gQ literal 639 zcmWlUT}V@L7{>Rha~^Z4EuH2%>YU9ioSD{zv9$$#5Y>%KtBW>wF0#S`p{}Gh`jj9@ zqN}n7MG@vjMbU+{Ir6R`vsTb@Zm9pcxw92Jd*Ac#c=dbW<@xcvMwe11)15<^MFw=B zN07_3!yq4^yt2_t6%eFFkd1RxNtijvaa2`DDkAX7k5zs&y@g1zmrnbz6=n=!(TCPI z@Im3B`%L0c2e@l5N(QoP5Ij=S=n#A?9raQl9)r#xC($(v zp*5bGgK3VcPY}g2bL#a(nG5hl|+FqT7eP>3gGLF_=F|I>$H~%)5sP(-)UBQ!{a4cS62b z+cv*VgYC##vDJZF6YOLn(V^mZbMU`-OQD7BI@hy(Hg}41!5)Lfugm=q6!9t>D zU9`oHCT!Lqi);fn`mhO&BdAj|b|SEo1*@52f#IfB}9%J!{9*E#qy-eye+v;D5e~8`I{qfp< z^!uso$@GBbdHhmZ)H1sjIz6!49O4XTZbYEVu+}NX9m}pd!C8mi&qs_k#%>2M>S+xahJS@QMUJb7K#bS;+@$ z+y@SWV<3fmhL(J4#u``$=fET2QBYUblF!7FPr=v#o8SaE2_6Kez!rE&uibo0Znq^j z*tiAu!ENxekb7xd2QPpd;3jwxyrdUyo+Y=-k{e{~g3HLqKi>cRv~l$Ay|(oP!!F%lDFWMi-;NJxxJ z<3H^EFPXex;s$~wF)0CQ2283lFiGl(Fqrn_;`I%=z;-t#VncJ%QLQr!Kv^g0lX1{ zHigSUg}dQTO#tO}=$wQn3#`QPuje+r$-I+L%-`*XXA7+KM*tr}rYL#2A0Ksr=;J^C zKJJE52P<>0!!V556<)6Jmtk0Ou<`)Dx(Xp5cl&V+X6z1j+`-p9`N#!U2SqLn&jb^7ueg~wt~dpd6uePlza)?mabm% z@qr-S&Q_`Hz+-XMHuu{?(}=!vjvmD#qN^6GruO&JJUl3()Q82mx<%Bf3OWV8-tpht}zq&f2dil8i)UY+gL_Ni-r{d6aO0~YVnAyHXBJDS+ zo7x3DgwDg*xgq}Q(38!N`Zr;YCem4$E#N+sb1?Tj?*HzT;^tsnG|jm6b7RDoN}KUY zi#EJ1Xw|x0gSCFF!+H@LQX-OJItLBYG=y+zq_K<)nY6->C-9_*r(9Txrv*HNFRH?v FkpF`cI-39h diff --git a/system/maps/pc-nte/map_ancient02_00_01.evt b/system/maps/pc-nte/map_ancient02_00_01.evt index f4cb181242f626366c5be04fccf5f6b596aeb09f..5f739a9968e1768fdf0202ef27b1546a82d0cf68 100755 GIT binary patch literal 1064 zcmX|=J5v-<5QR_g!Y&*x?jkHJqC8Y|mu2xq20qBFLM;Lf#K_1$V5Et;ks2CnB9?)r z7O^I#8c1Yj85n3;CPsLArnxiKcjkQ4eIMODe=9%}z=F)LPsM1Mfaxx;FTq+OTY9g{ zz6F~MzX|Cd!P>!E()D47hBhi#)7!LcD6?w8=EeHOVuqQB?TCFC(I7pe*uLJDWgjyu zV^_r1BX%xgSH&JB4bpQFyDs%O{5s)xMeJpx^y|paKVO$^XI3rP4Y4OVcA#K4#olJW zL2OFjmc7fYR-EmQ*!#@t1-mQuEavORUhauCyb#je*z}$-|i}St_+s&+8V@GT!VwWQJv)JmG2I-~P->%dx^^dc)@cS);T096SNDE>9z3ScUK-;nxCUMTFA8}TjAy}fdYlJW^cVvVg5#imZ%dv-OP)1L Yo+o1qoCMq8A#e&j44wk@yX@`#2iLtuWdHyG literal 595 zcmWlTO-K}B7=UNjS$F(ebMkjxO&xRc-?A(S&C=}(W683tAK;HP^^l|3(*Q_Wi!E?fM*^=PBH=G7R5^8Ib|{wb8UN znd4yb(eFN5n*|-yF*__|`)GEM41c4mKH8e&qgg-H6q!D2aK`e6fKzCaqzO>{a4=pl zEs`?$qVALFRJAi!(+N`wEg^M&Xc9@$vPkOTvBM=NP}4m02T56woS%SKX)--!)h?cd z(S$3cU50^3DPgrMVR#=RHi>DAUGu}ZLX)3d_rS~th1#_-1%_1IS3VNI3!Q1aOuc5$ zDDVmm?2X-r5s~y)zzuB>Ui2w+YG)K~z46h?6?pB3pDM1D)V+5QZdK@S_z2@7sS?R- z^X`cQGG*i8F1Tv-(uUV%L=mfGvRY!AFKf~*1ZMtjSZ0}v49N6GZ>n3Yo};oSYeTZLwzW z)+*;g-ENeJ^ErGF@Av#c<%Up!D+9O+SM#{086Uw%(_eOPv*yzFoKr%m2;WAI9+ZlO z{a84Mix-6oeX)L0&qt?u+^9xp^=K`xA9EYWjkgb~l#c zpk`&oS3@}~;l5`5ieHArh85-=Q2a5ZY{XejStnmOXstq57waaL<=(E?g0qI}X2m9> z2IURC%aUgEip8WWwtL@-Rjpvk+wxjH z-YEtXE4&}YUbuJKy`RMHxNh3_{VaAjvBLXJ?3H_G-1|f9yX$6r-=AVnG7Zx7T(_a@ z_UIPN(9!g-;=SuGx$d9Xhr}}6QeM-ZP4%O#cST{}HYB#%*kT!OXj&J$oLKSBEwNVe zZo`~(<6>(`*OzO@mf0Ty=YAyxF4(|1opu5N}Hex&H!d$A6N!=fRrLyBL?<^ z17H;#G}mEp0-OX@>1dr5qV-bXDeyFS23!Kqg6F_xa7B-;g#u54^R}inMc{U@0`3BL zgL}ZeU=17shrtnW6x;_kz%g@JD}-pp4?FSi^~O7p0J*hLYV7(p*P+r`j`L<+hP+d_)b z%Vph!5kU=NP!drI;W`yqLPb(yX_UR3)8?kjt^fad8eM<*efbp`^c=?zAXml#d(#}# zaalv4lUa-5qMaph8za7Cc&ku;;?%K7Dl8VrEJvhi2VqHOxiTA`W;MoJe?1#zRtQsd z4wj($>Yzbpo+3XrLrS^P&Soq-B33IjDpaqiZP03!S+Pjl$DvDRg3NsTAmB!i;KlhwDBK4R=It0%Bsdi?Io$*4CLQARdEL@JqD@5u7=bdyrYlrh*xT4U>sP8^p zi&LdY$HAG-+nFo;$P2uK#aB<^W;|FW(ihNw(od^8;I$V9|I<6T8>ecK&O-ltKdtV7 zg;}_eU}LS%{4@Z^-FbHQr6xDB@(~{U>25!TUU=eQ6@P4|z`&xQx3Rs#jR4E8RlADp zEOVxBxbYU~L@VLu8|5KaadMRvmdHVvPHC$-0jjY2A1FlCQ6SG{HjX6B#N|=ZF z!`MtlEI(e|4iv}ogNe^Ks3|AD8biUDbaY888P!U|Qd1~wF@)<;*o<3EDdFP7 t(P~$86V~upD`6d)4cLI4jn}J|`PkLvr102EOvs2#RX+ICsWHZJ{{S?_PV)c& diff --git a/system/maps/pc-nte/map_ancient02_01_01.evt b/system/maps/pc-nte/map_ancient02_01_01.evt index 91b4e645fd954fa33257f22d64ca57b3cec6a384..ee5627224395173bea1421cb9b01b95b79542321 100755 GIT binary patch literal 1224 zcmX|=y>m@b6vfxc`$)2!d?1k*AulgJ@{%V(d_)NG%!nc>43feOl^F#pjlY1xDA3WQ zL4je$C~5o&%t*yh(3mksrFz!Bcjvq}``){LckOfTJ!jqP1)vu|LuJ2@*)gCWLI3+M#xcdoO%w@>nqAvr4U8I zvj+$mc3)lW!v-Cshvi$))s$++eO320#m1rz(p7)vGg3|Wjkxck*rKzVv#Vmiy|<=b z^W0D^C13u&x5bv7je74LvGcYN(xd(!_oOZ)R=}$CL)A*^Ey@i%Qe8^EQnm)GVwV#u zXTGOm_r167^F9-M-lKzb-QV}6)QiLl*}lCIdysmI*&J(PZ<8;d_lwwL_l^4;U&Wp{ zn{f6+?52fBdcxoLm((lwHQe`C?6dnCzNY`g)>3c&d@Zrpskew}X$^sIskfA|ve@^; z%D5$46uaA&H#B|TL9utv7MzWU)s%S~_^isZHY>JRU1#~8&x*v(#YR(a{(K8! z%gI;D@?u$8Nvw?b8v2}Av=1q82;7DcqJ0{HP4E;#wEPFwK!qt<$O8k|1{S~~*a=cM zEY*SSU>9+t4^ zg=k^ZealjamY%>8SOzzPePBPh1snh?;2^jatb!x5T7W{}c2J*cv`7T*0C!5*zK3W# zt$}SLL$s|6+z(EJ2f%|8U0}CP4R8-Q2~L4~!F}KictkC>J0aSBXcSxS5N&}2$3dmB H)oT3*2^m!= literal 684 zcmWlX-Ahwp9LLY*)b(`Clld~6rN>DWoij z)GkUYk<47u3ql3DGwVVxO7b95OYkK<^X=GhXTRrpT7SUj`~7_38-F>7VYmj&q%lCh z=PD{D>N=<_G*5fiO4Uq;*AW5}qlEihTjLFbdA)_k7|6Cs2;E|(NhDH)HG$|{0?|XA zS|E~BJnrQmLB`XY{@ns;OooxIyp@VYE^j8}WD6u-Ao=}ZWUaISzMfhTNJ4-;2gTVI z%3G){8dePCilm;d?}L(2V&8P!f^rLOOEi!cs4EMzQm3yi9nRXQ)A_{vK!ge-(Zo4B zyWspN3GQ`HT(D77i1^|iv>QlrAvE^D%kVZUwQs(Hu|69Ok{p9@-$+cF$KnroJ4y=R zrwCmFNpa1>FL)l%NVU+pez?m0ha3{T7D!u~KxC+&6iAxoukV{ZQ0b?YFkmDLRat$M z`8{VO!BU7Q&Ddy24RgcGPD^*xwKU&V2(kW;fy^QcO=$4*LACVsGbk~Akc#82)TQy~ zNhNauDY39pN;HxhjlW1L9}Em}b}u(q*l5)!oww94u|u;eq-RD@oKMB7Q|H&&GGz8O zv+4zzuaY}FtZHgOHjkrgNP7=GtVS~kMb4N0QO}3U?0XcBc@A%P;s}p{^X+ZIMqIrT zU>7|mPClx}$1q!nPGCM4=Oy8jE%`HxO6ihz>acVsTTYcYrBe!acY737#Ik!h6Xl^D z9F8>OC>*WDF__okSPkBZFX~apEL*L3zMx$L>%BAVr4>%z(=8{-=G6q{%_`~{T2sua z>+S>rI~%{I75pwF~1M*x8odqBzY`CZdID(p{Cm#in76tr?Bq! JB^vl5^A8m$co_fy diff --git a/system/maps/pc-nte/map_ancient02_02_00.evt b/system/maps/pc-nte/map_ancient02_02_00.evt index bdb7ee14b287b3ab01e30c38e2f876e1e8cf90ba..8a7f296176d9ab396c655fdf3ee8dea2b769bdfd 100755 GIT binary patch literal 1052 zcmX| zb|uA<`|9Ghol?w2EmC#&Ri*mwYq+l=w&lKte0Dsm7`U(LzGbm>XDw$p#coY#k#2eJ z9jRyTn|I$mvF%FkoA-ZtAo)D`veDf(#9lgUd+w%K#R?+bR<70Hsp3`gWuWwH#am|! zp8H1Zq313r_pW&qj}pr;klt3jOSy&7H{OdqNxmZ1q(3P(5-Y*(^;xl**f<_be^Gq! z_jUYVzKQL)ujBXF5&M{Y8SYE}Q2ch*^W2|eFRY=`J+I#{sXxh=*N@VF6U*;Wmfq4# zS#ICo7sZ-c&ep_g+E&E|_5UodRbA{xV)-{_#9Ar0p#Ralh^;4Iaa89;vD=B2Mm6b) zMH}8gHLU;^!4g8WISrfxH89bJG4K$07(5Qvz&5CkvJ0LBPl2bwGhh!q4_;7)jbPwe z@SKFrS>QOh3)~G7EQ1l80QVq-XtNYp0Vlz|;688)+z%cA58AoSONcfh5;g!KTFXPU zCI>Eo7r|xl5_lP0(Sda}@CtYpTm`Rzeek-aTh9XPU;}J|E${?54bFhGIuERZN5G@t aF}=sCAEMP;saDPqt$=|Ya1miR9R33alSl^u literal 601 zcmWO1Jxmi}9Dwm_IiS9+Qi~#A(jK<7SWrMMR`G*6;y^GMH6%vbYZC(lkwFGO(i}=e z6O~PgCPZ9Rv<}K-BHVFKCdL>L7;2(4DMu+idcgJlzpqTs@>?Ez+{iH26fjN((|QA| z=VDd3g{S6ebC~{Ytk(*BxLG3h4xBCS!fs2L21QbaTg?)2NMy5}4ol?Qx>1SM;5Lao z;w0k7y`4O*=4nS4zOZxKJIN*dq(UMdiCmqAbywpYycVMea=E zB5RoXjdOz~cvL3cBGO*;;BlTF47$ime8b=(icP$-Sc3Z_)F!rteE7ASu=40ze5*<( z`AqwW_OUBM{RN)Mf$bJDHy>(WaPRv;@Q&wx^x#>Ec!Sc`EdIdBq(3yHC2`ir(L zp1>o;VajT;HLS^GhSgAdqdY>zE%WE}urWki2cgfB?Nf z=*(a2QmO-PGTIc2rj$;Z`18fOPs+szEeLMJniNZU(riGNGa`q6p}Hl5Wh%+8xK;B& z%JOVk<@TwiEbNEU5URC6-x@;a&>=W%f(9@&!oBPKGG3l176TW)*I%2EA8o_Rh-5AhC zx}jcQzN11p-4@0sdF@@4S% z*i^j?HWR&X#qLM%O!U4JdyrU$Md>ZotJGU6_INM$IQhzYZ{VZqNn(}a>_3UUiQZ=H z`&sNu_?p`HmTT45)SDH(-^4akZ+GACVn0%E87tDiRX-D}6yN(t>{nvFcqIK-^)H^+ zig(GRH#`i|tvLHWv9+EqYlm-G?5+nyx*caVDz+8AtKpjvt1F}KTTyvl=EXX-ZPxwX z1+nXib?3Vz)=0gjB4^8DtI1a`&gzQT&BQA3SzHs#J@`fgd%z4V!E(>Iu?{xC3Ggs@ z1U#w*54CY0xE~w=4}b^3QE(bmJ~#&+1CN9A;0dq=o(31x=Yck!1W!qLYK>*E0``JC zz&?;*KbXTT_hcHY;2^jQ+zk$ad%(Tmu&;aiEcd)gc$_Tv02vp-GZLN>%RLub_pw{< zgSOm9Yg`5|fEU3_U>m%w1)rty5I6?bz;PiTn{fc#slT(}Iq*EVq!k~EI-cQ+b LKUZTDoQ3@dPQXr8 literal 641 zcmWlUTS!xJ9LLYx%=zESlb4mwoJU=ow^FNVrsbxImmZ`rM0ybBHqb$YMN-s*tQv`s z_|SvCSQ$h??ZqTgD0f;3KIDtTsHR5qa%Qum!=3$q|9|WK^Syj@*>Z+qw}3G)n3l>E zQp}TsWj3mvRoQ7mSs=1+5?As>$CDRn@6~qt$xAY^*~F7vo|I_tAWs;Q>;9)IKj6Auvh+n4I>9g3)4m{!`ZdFah^9YZ|`9eJz z#-l0hf8PHXkM;S8y#I|8PuOUYRZl#)PpcyfQLv!p{vv;q%YnJB>b) zc*RYb<~?yK_5;n#XHRD2$P#fghN5t`hc;STJt!>Gmky(!B;8t@MlUN#VSd0XU{ z39rOcK6V4I<~V2)({GK0@vinBR7}L=i?NY3J1y8ro)QKVy6KRaCz@FA)y%j>B-<%W z^Q3W=C;2f>uIZ+2tD7sdOlZ|**{N&K)GJ(YP{nqQQ^<=ywayB)LVH{+(Od5nF2^UW zPsL_I{h)eHh9Za^Lm#Kmj7)7=SqpJWF%P(WC~$!ujv9lV>#pD-@h}6%dXxoPC1k^g zLXiDITxs-}3ywn>3*}vK1D$e%>9gh5`tk|4cv4KbxFNcF&~3yefzk!ZfEeiK(6~$) zsMrj!-_ZR%bP!TmNV7mXWHf;$Fpw%p1AflrD8TvqKGogKPtys-2eap diff --git a/system/maps/pc-nte/map_ancient03_00_00.evt b/system/maps/pc-nte/map_ancient03_00_00.evt index bb26c03ceacf2f4a60654b6694503f6eb0faa3c0..f8a7484dd274af9eb97bd1ec7e84a8a610a405ce 100755 GIT binary patch literal 1088 zcmX|=J8V-y5Jm4K!3j4Dc77z`V>=%MjuZYN1wn`oI$9{HsAzzSiUKK8A|VQ@R1{SF zYN(JvNYoS%g@THX3L2_#=k0i(^wyr^xpUv!z5Y7@GyrsT`}Nd(HJsE<|ElX{-B)J= z6|1RwRoGy~lGy9QhAK8L_NK6X6`K)T36ExYw8TD+YEj(`4}YFdg=LSP*cWe8-(+ZS ztJ)pzv;2H_RQ@O|YwwC}hi^Bu55(&J@~XSx`%qP*u&jL~wien}Xg9>(2I~cTBK9q` zy_ok*?0aE(-nQ6IVR^5fi@k_>OEK@g*uCgmig_Qz?iZHleHOc^bM*IE5A9b~w+hSq z@>A^bw3ezbYk#S_8ouq&{ubNx16AD)-#@Ch3d`DmV(#O8Q=zSC)|HVy%f3nMU3e@< z--PN*W4UiRJZ8n#3(FoYvCDcG z&cQ{WwVSsn>~jb@GRb{Q5G>E$`UAe!HmFvZehD6kUfxy9lJKx0vy`3%7u}(-f{8b$ zgbcDK(zj;81GB6oas9-0FP~wRA=;8@sM+bsEQUg5Uv4))A4FG(I%caRl(?p8N>6j9P-QZH0d9Y8#ZduH?I&vwh35-ayeFn@hg!eEU{uK_EeFwGraIF zJ|prj+)bvh#SQ;r{QdEj_JZUjl?;b|SzjSSAYDh=ZM1VuGCr*Z4EK7pQi|?$;~uOM z(2n`l5m+9(zitdHFj8~}h@6%kSaTX{m*mBS?!1s3Nr{tx&A1J>uIPIO+=pd_%i{co xU$0LN(tL3|X|&o>hCd}l8=`U}9t_|iJY11D;!YpMV*&2P+M5XWcQ+TBd2c57SJqTN!f?QT(TJ=nxcLhCK*NkTX|l|v7dgE)vd;ozVb zP7;X(amJAY7Z(wdo+J|ggG+tqnddi~%r85i-+bqp*=J_|bQ9HxXclIlXKYWVDcFF0 z#iwckiFGAbf>#UHotT1swXmdfZ5`fPxnQmxhC7qb7|mxiz&`bQ+$ecm6Yl3=enuPY zN!Ctg?G>=sRo6~t?NzvMg1L4H>}{}~RP!>}T+TO^^W6e_msu;b+h9L)zpZ@6U9exl z{EYixFSB+!YgfT;UV&Zg!UMTFIp0TkmxB3k^#$y~P!Z?( zzQH}7YiU3$@PEJ_+C{-{pcZ3mut&kV6Z;MJI9N|&f5A>fz72`}1A7{ArNoe$R)duj zD}jASterauv$-mR9jz41`{)BZ7R>vogMEORwhgnIN8q3AFPQhy06P`T`)GpA2lGDK zVDDj`&->`WKN4}?hxOVC=6x)H&6_mUQb~QB1{=o?8C%TtodG*LRxqz`5$s$rukQlb z`Cwk(Ww43J=k+avEk&Hyw*q!8nAdj)?0T?D`mOGPsVy+8tt<;dD{yQtS&+X3$Ci)P z){Oc9ArJ#z}UN>?D~$lnpsciv*LPHp#3u zyR0^?>=JSLZW8=OsEsG94J7KZd1JK+W0@ksimHtfs|^mTjS9;Q$sUruB>PBaL2WQt qZ4?+O>{?mvB3bR~SoV_~AUQ~Kh~zNI9A4O6vD)ph+I=vuum1(TY!8&}0uTPYrN=+tUNhosuW;99$Pes#&oWhYWDZ%;wr~ z6s(EKCf0^uRG{w-q#J?%J24IS1*s^7xVqtiRAeMG(7FAIrvXYjFzg|r@VRS53@6;C zuwZ5pB-}~9Yu6Hn?fwXIOvg!u{wN1IZkA@8lMI$Zo`jOuOLZ|ploYke7q?&REITx(D zU?Arui6T%zG?GLSXe7G25m6Cnc98nu!7V4bVrE0Y;Z9U1;x2wH)=e!IH}$&H z-+lY1h?INvmlx>x0!NaFIvIB>FcoVn`MPC6U*@5Mo?6=M6nPbgDX7+BR{M(DpP|Ie z9@I=t{ZX#u=J^KJgg1*E%~+z2$4l}CF-qK1g^RxgJ-mQC$Q6Ri8h} z@6I|Z)}`u2=vtwxi{14`s#~F(Q1!CtYL$0d>}jy6@XmU0C*B7kga1oz+=xioGkkZ56vM_Enen8;luuRGsVIV);L~uW}>$ z&4zAG>`8yt&Bj^QRsJoye3tiOYaLm)6y6UimqOPF_Stpu-#4P)7qLf$<$mA9e(CZv z4~2I_)w4MBPhj;)M&8?H)xV3btJ>vNu~FsQb(do9 zHL+z6YsK;|Z-}iFmgn9QyI5GBdrxev^viP}h>aJP=ROom{?Zt0U?;d8>;`+l1nvL{ zOa87H4eWqf^7LEsyc>6dyTD$s58Msz0SCdMV)U?E@^~Bj!2uPXVKwoHZrS&O`@sF+ zu%3G+ExNS8DR2fn3QmJi}v^}zW1TA^!EFM&1cgd>`>Qgne zsB2zAabazREHL!=AmCJ_=Tfj5H4L?eT& zySy{=rZBz8Oev_GBk4jg-D(9Y=Er=ebIXXc=Xp2I7fLejJauSE-CiGuw5+VX^9sUJT08( zX*qD&ZK8%&`e2BsI-Xjm;ibYM&THr`cy)wYhvA8q#@Wanc+=^o@#NlDSYj7R#&77D zhHAM~-heJRH1=Vci#{BOr)g2^n;QO)<^ zA`D6jC$9M5mtDx@>7f^%OF?=p0;5NE#y40|4@#b9dxP5Fpp=k`lo(w!&qcN4!Ouxi z*Ug`EZK7=h%E{{-o)+=6$`|y+A4s9bO1u)%&@1Hoqc+hTvJk^WKbk|$#^9QpCb&rp zQMbpoir4GG)=V0d4$)?Y+r$zr!(C#y7M=H3NWQcL16EQXT{FW2lHA|4$*`Mj=^UHr zL^&qOkkqlqkkxG^k8IYgd02Tj6Y5Rf7J9cia@<5xP|<@SR(%3fotS}{Y&CthEtuGy zyUa4`u+8bgI3(K{)#2yv4shW9JY$^0wq4!wqSLC%q@0r+g$;)~##9jyM zMD0tld%l>8J5l>a(eucgj=XiTM&wOL-g~jZ!g7xfVnc=HnLmm>4ZU{g{S$<&mCiL7> zU*kJwLhp>&x5BdCIkE4BWxWexEqT=SRzmNp*peHmV%b;M#g+@pdN;)`7MAsHi(M*g zbLE4(VwVf6SM0vnN?}_n_CRdB%+g!2Ct?$Y^;OLGC;6eV=-QWPY72FH%(`!EnmfZTrQE&`QU_YpK1T{#XUc2KhxyLQJt2a_x;5ax1w!sdCZStMBn literal 667 zcmWlTT}V@59LBe~*>PKWw6fexcXY$KWtrwzQLtGTqLp^Fq6nPZ$;?c>kc422vxw44 z=x*L*Nl}t|kwhQRhf|Ee3QA){Sh4x>PTfq*cklnaoxvZT2OggP^UHB0$z<#j$Sg8K z%jHTOAITwV7xm|B-BjU3$;4d3(JT;0593T0y5mQAJWYup#j5snNUqKP z5R$9&?DIz0H4M?e+V(IT$+WNPUR?QqL=qU3Nf(`5{k2F;bkM}f#YI}BCiQ#XJ-vx9c`xt#>G#}o-o5AaQ(mMjBJ;fMdZPAJY4IMh zS6;75_B58u*Z|e*#PS&nWS`AOU&UjGsm>I8EXIye&U%gs_f3(#9g4mQ&oM*!F0q(n zfvlN}zK+MPQ9i8nSj@3b`N2@;iQjRB>T99LqVF2zW@0hNb+Tt(U)yusAuHJ?QMbLm z4XSctG4=u3S&yxI>{GJGwmH;wk9|({%@EnB``%D}Pb}8=p8BV=s{8D_3WEk!s=nSw zs*dNax$g^E)AQEcw@KDYEY|#kY$?U|WoNlX_QKh;ueVKh!@qah*ZW0wGqJedU$R?? z#r1Mr>t$N6Kg&^}-blWIjE(=t3K?sV-Ale=#+qcyyloE}USErP%fGGR_05rOCl>2l zBwIBsY%;AE z^WG-An0ztsU9wAw#eKO?ww_qrmq%p5hE;=&r=m}gr?44R^a}#!Z6_m|{=> z!7jmWK}k>+>=5h}1i=tZHeHIUU=M|jkOmtX#h75PU{t_)4>lhfY$6l~1qTF&1XF?$ z!7zWY5m3IFj+w><`-p0Sx?sOxLeLOQ3Yr4``Dm~%SF{Ce!CpiWT7-!9*!P-M{RIng8CRh|4C$(nKV8>6RovsEu iPYrg0iW7p4U`cRNa7u8RKiH9JutU;d$D-QpcK-o(C|a)o literal 717 zcmW-dSxggQ6ouQ;!Z21DS%o5Wv}F?zQ5H2uX=>t{s0l(q4W`f)k)0kt1&wiT=CaW;_-JWWWBv5ZAdE(=+N;Z9%)?w$Pzx;< zlbUI#R|g?Va5EddUJinWZ1gsiSnSklr;i8Wu6Y$b9fF@GHNM+V-@>mXkG7|`5~LcI z;HMQC5s_L}7ySlBmHQ#pPX}HsTmBDYb&%m<)yhOY6sJ?;1F{I`TWeTW0|x5hV#A*- zR|77?rTZS{Lx6(It7<6hc-e@i9ugP0KGQ?PiDjmWnInieL~eQ*mrT+}$>9bRO=`wa zVA1|p>^7;nB%s}7RCm;oqgsyYYzzGD(X{;#QMA_>)ni1=39Dwu!b$0+R5vq`Hgd$n zoN(}TlUCRXF0$!er#cz>jmI(bJ=1LTCdYnwwS} z6*F=oCnsTNJl+~NdkvCuK@udJL*9&b4%0C293R6=bCNV16X*bofXQd&J-n5}jVPv| z=)+BzZpFkg?o0pg@y^WP?7Z3gYF0^_6XYC+oQqoyVD_-LVqW<)Z&h{?-}u&S> aaHj)z;qF+eHfU5vf{J)3)XO-g)BOXjjgrX# diff --git a/system/maps/pc-nte/map_ancient03_02_01.evt b/system/maps/pc-nte/map_ancient03_02_01.evt index 80a07c0856e2f10e0231d125091fa9cdc96c9981..3d7ab61a465e3c5462bfb605355c9d1fe37fa3f4 100755 GIT binary patch literal 1392 zcmY+EJ#3Fr6vm&^?>@b>v|9Die_Pd$e_OOgTOwj^B@#g}m?TUFBnAv(QX(WKqp%PY z2CIq0WHQ(!7Kx9Esh;!R)0_Aw|2OaRd(U&vdG9&*{n`k00hrO>f1j$WVG|5J)je>y z&2OAFRIEknfn_!xaov*7JJ(o_>z8~{SYzd_i9IadrizVyIX2qt% zI}my2#NKu2B0UiGElRC4WOgLtmL=~Nmg_q$cCWBp-x;y@ZmoQI56((`C@gy~NI#BR zCL+g0u}4~W2l2lh=ImcD8BjFtk?{%@2q>J=mcyCF4aU|9oardR3L_c~X?vdCx z4^vEE#J!YiZC_)#=2wz!h2>h_ip@tYweWrv8;e?Mk;CU=ys+&3EVfX*xtHI?W=dS{ zH&J@Wn)yBvE}kG!j5R|?B{o8@~}ygBcH^o_`VliuC%Z26j&WRl@EU$N7 zY`CnK-{YFti4vFJ@CH zXvq(qS@JVwYzGN;f?Z$&JA~GO>%l&7v%-x1;1;kK)Ca?oProIfcViFOE#X6L+yHI_ zw}RV*e145Ja1h)M4uC`8FgOP4bCP^)E%~4tC&0Q>f2Ig^Ecs+w@>w)agS)^mTJjzm_k;7`0=Nhs01xVcH_ei_$&xq5Z1o=<16L0K literal 729 zcmW+ySxi$=6nrh)<5A@a2(q_GDXX$rHseCm{wO9UE+~9xSXvFn4K-eO^U^QHBkAtt2D~ z<#s@yfri$hLdZ*0k`x^j{emwMda8x8et0OYr>zwhQVHru2Jz47acDKr;6SwvzRs=1 z3@b?uj3yXpIyq;D4s#&OLeBR?B(JB#L*x?hqU3<eD>1bDT3YiT;q1B&vcr?sC$ diff --git a/system/maps/pc-nte/map_boss01.evt b/system/maps/pc-nte/map_boss01.evt index edf041a87c79eb188e7f6fc2de497c61f54906c2..5624f167d6430734a4de014272b708d17b14adb2 100755 GIT binary patch literal 44 gcmY#kU|$Z7ytkO diff --git a/system/maps/pc-nte/map_boss02.evt b/system/maps/pc-nte/map_boss02.evt index edf041a87c79eb188e7f6fc2de497c61f54906c2..5624f167d6430734a4de014272b708d17b14adb2 100755 GIT binary patch literal 44 gcmY#kU|1APxs3h+qN|i~tbv0Ez$r literal 32 ocmZ=(VfZg_=MN*ppZ|JHfr1(o3D{P#7!(SH zrj>oEDir+>Dw?){q2G7U>PDcB_c;`y6dNer5@ zRpS@>l?&Dhb(~qHU{$ae5!;CK>R<p(i5t zDb$EWv)4+bq?w zN*?#lhTl5a&QS83jhr_icQZ>lZ-c$dtWw;u1GdFboYfuY?LjS8+AQ6>54lNH_@(pS zL%ojvjYsSUuwT*V@yO)}>@NJ8@MEu9uzxYjCj7`wVAq+YUY&y7WR`k$26mg-gCh1E z>@$q{)gza$P+u}jb$o+<82y`$T)u-X$2_JZmmgp&nWbDV!QN(;a=8Li=ccN2H`P!} z8G;L_&XrRe6GN7r&|EEbE|Xe^7$)u97x*8xb278x|XcyE;3e&f3*kt=c2e FyZ@OVMJ@mU literal 493 zcmVu5bpf|Bmn&X^#KI>`>`_oC}sbJ_Gkao3JmuDo1GJ8{{v&}2mGFM zXa5B34Q%{DCH^4eh5Z!#iwx#w{{rk9*!+xV{{sx{_|X4VCH^ADCH^Y>Q#B1{|0Mn} z*8Egw|0Mo4xcn0WWdD&h{Ni*y{J#_$X8*+bLu>rNXaB_cNv!M$|D^V*{5AP!|D^V>HvIZ${{_eFwfxC81^X1G`^(n+%4h$i`^?z< z2?h2v#-;ns{987K^E9RV)7Jc4XaA)8*;xEtXa7^A``_{m!LsW5BI^MW3cx_o^ZM%$ z`fBU~?HlbzS_kk!D((83?Fi}y?G~}{407%S`r_{TAyV(>2n6Q~{Q$=B6%z1({|nOa z1o}ep`T?@>_tWL)3Lw#wES;V^s zd6qQ~((XRkpLW;{vgQYne+!GekHG#F7I}wY{|lR_YIzJc%p41uV+-s@_Fy4%JOx|J z9xSKb4%mJduS!SFyHE#(MJ;<^hlNEghhXiDx0LaYz((1trOf*R?0sPo?Suk-qP{nftNFjZw0jHoWIF76nfD#!N@0Gd{;M@B!v-|_tj^A=HN++a-_&YibznA4oo!N6+}H?p{<_+% F=sBs+N~8b) literal 505 zcmWlUKWq|V7{)&iXz7W1T59X5DKsTE4h~l@YSKhSTy$#GL~C6H?hQo8*Jc}iOqibd<2Me&4G%BTQDbBVre%|3ci^T>9gnr+ zaPV`K*E|XlR$#SPq6_4d3N(Vpb6Mjwbj5y&W{fj=*pg`wE?k3cnTCRJ3GiK+IbH_?H zc91(V#1IczTw)KDqI?<0%3h4i!CwRV!;)s(ZdSbbR@H^SS8*UvTjN}reZ;qy?01ko z1fk8zRPAfJ&S9akfL}I#>_Ua&dDcbxJe7-02TnDy?^x?M8{F(^`IvUA*S^BK>ghIS lgigs_XPpGC>)a4~R{b}nbNGvnDgzEZ)x}X~{g4+B`VafqDGC4p diff --git a/system/maps/pc-nte/map_cave01_01_00.evt b/system/maps/pc-nte/map_cave01_01_00.evt index 9b9252df1ca3b78d08c50a2f151a2a1e6b17f8d7..b856fba83c73c4b8f4d85bdbe73bc32090ba2cac 100755 GIT binary patch literal 900 zcmX|=yDvmh5XNVA*K788zu&u53X!1HP)Q^j?;t8Fi9|)vOC)Lv(NHN=Djk(dq4HNK zyeqzM&+NI$Il15O&Np-KoViySkp>YdV(}VQ?kas)6THRsnCDKGDX~hZeIl^nakWtQ zxiX7!&5(bdZ_ML*!M^zbdd%|;Lj5F`E!7wT`%NrYVk2OiSoE6bn}&MNmRYPj3;B^) z%r_7A>F+Y?-UYC)#G-ct?857gO7ybk3i`yl6YkxDI%qGm=-q}qOe}hL!HyD(-?0Za z>2(X9?-=YPV1q7rUrwQ>Qe4b;23F(mGVb1Uu+hhd1zyY~@l$>$*N-Y2l-#G>~ZY{v5y-TMOe<~=C7 z_Z2K~0M$$^CmJ|qY8kN#u}Wx8lLpR@22P9`rX$N|RwY&~RwGs`R)@zN6b&2=RjU_k z5NibBd8-BgY;kz?qs)CJPct%nWCWEy2Ff3*LI2f@_yfEa(du-pWoXx<>NT1N;sJV zc90jaliLFWW9-#TD8YQAMho1YTrtE_k+#CLDkRt}4d;RBq`yehonVKbc-3B*`=>F# zk=qX&8apBwhw?BM2c(nqacKD6%>+3KYEyIbOdi&=PM<<^XTj1~sh1AJ>*sNBoBZPo zzH4kiq3HzN+3BiYngaE%=H_@A?rCgxQ(AmTqfLfjQJ&5QiJ(U@FBn@G#P+Oxj_?5pC; zcy)QOcHO^>HU~(6@Ii8{A8q;l@k13KFnQ_w)|erYZ4on5L#Fs}4QKEgzEPKcwvGI% zpvZkvM}xaoaXqlQy&>-)?ZLTIO`It-ao@VM)S+8FmKBwL?2eI6($!YJY72PYUJ1(; Nv0bpb7bAX-`wx1G4ix|Z diff --git a/system/maps/pc-nte/map_cave01_01_01.evt b/system/maps/pc-nte/map_cave01_01_01.evt index 5ff78d62dab0810619c271719d4c39985e6e330b..55011814b45701b95f75724eaf2cd4155fd017b2 100755 GIT binary patch literal 856 zcmX|5L7oaEv3WouzP#E|XC{QRAibA4r{11xa zFyZx1XHvDBdEa)=o9XV09MA!v#K(T8*;`PApqj0Loq{cl+IQq})s_=9>4K=Z<^sG}(G$F?Wuvh3B=iEP32K)qYQ%#kdOPL1NKkg>2RL zD|_xX*^M2Xy6ks5pxSefs>dCYo%hGMs>dBsUL+PhPRK43D`a)CdVaa@g70@mR&iF5 zSm!IUNk2=;J+8^7oGl}I+>#yhu`{dhcSrS^ud`T#d&-)7EPC7n*|#-?deJ=|seTfR z9#3SyiN$+7ll>*uklpQtY{ost+~bYxwa6Fsn0vfa&8E2M@j(`Buq4U?mpKK?~3ZXd|>4s?b(`v$Aq8tC|F>lLV`b1gnK8dyt1VQQMtGi_pOM HTCMgE6h1sd literal 455 zcmV;&0XY62d;$Of5N`bd9{~LSZ$A0~01I~fJZ1k^1aoHp0_+BC{8(rI1CHzr{M%Il zWd9;1{u8+TQw?VS#w7k3{8UP3|0Mo|ZA&HoAtnAPto%%8|0Mo#F#HJu8)W}9#r`qZ z{96=e|HS?`So~aP|6}C*J^Vo%h5aBU{#MugiwtJ}B>r63{8{DvJjTWTUHp4B7iRy& z{$tksd}sgU{At+yeP{m!Z0v&d^_xZfGKKwi1YBkR_+w=Mfcz&LrTZV{{D#~7C}saO z<@}7;{AK0x3dV)~qWtqU17`mu{;0P6|K?k<^0RI{KE@o|HkC}(flr2$NuE}*kbPYI_&`rs_g*^ z9S}tu1@nUL`XTb}?Hq*dAN?8__8;{TLAa8AAOP z{T3AAYVifsa`O5?;_~`Z^9arm;4u;T4uts+{Rt@Pdh`hT>h$UmRSLp*3MUE%5d8=O x{R;hneB?^?D6~@c2>QbI`hqg|`l9zB%YpQa-wFrt1Pt&H`wAel(EkYk0060C1VI1* diff --git a/system/maps/pc-nte/map_cave01_02_00.evt b/system/maps/pc-nte/map_cave01_02_00.evt index 8d49130631fe937c9a268d96c0e3dd26f6117cd5..d79c8b27081ead452236b726169d018dd744cbb7 100755 GIT binary patch literal 1264 zcmX|>J!_Ov5QS%UlTCJZqS1}U7)^G!kQ5pr8?eyEI;s2sKVqXOSXo&`Ed({72#S@~ z{s#-OF`ZagX=P=vAd)DAc+S1UEbPmBo}HO<@7%fjz9}*(A}8_X=Vp1Tn5dj7?;^hu zBhV&|!dg(b%5P-N!X}`1GH#@>F4zlYpvpqjn}&Lstk#TwBdF>`CZHL%YaH(K_4 z9qe)gk5a$(?*`O5H(glW?`^1i$*RX4u=~lX$6c@o$*RXX*p>8X72k(ok1}qounn-s z$;JzN4)!EjyRcVa8_BG&*I--^*W4-V7I_18Dta&<>@C=Cuwk%wU|T$R=wbBq1Jri1 zy5CKP#6E^n_xp)iQ~ZIsb-!O2maOjgE7;la?S=0*u&0g(bT53rLp@7YeSd(p!*@Ay zx4^E3$8zNEfUPB~xlMRpOIFw123v`>^rK!E`e*p|quvbIuVl5}JlJaH)_TXF|7Kk6 z!EvyE$!ZUlz?>ITZCq?b)Oq_T)EfAXis1@7uaG(~jT$a%Q*2DE1YPt?PNdpCvHcJnJ9Q44I!8;L!=!3mu_>_wh&(7ZEp|w3 zR_ut_VZ?Ai)HxDrGh(=p&U07iNyC%}sm`NQRykS{kzVSk^Q_c)I_f+RwRy1~w|ID*l%b9<+YEznql!Z!BNwno|(yRVqW+2U-LpqC+y0N0hW-E1& zbrDK8BQK%|=0(T|Z`xcILP4aVMz+o6{Fc*c+dkj#d>yZzm*;uleSW(4GKNuIBg{bt zsPzPvFzjupx6-7no1;>cV*@TI$lNF&rbtl8LO0a#M51k{`<$e>69#$eAX*!ox6*P+ zKuBMpXY)Dg7w&(d=QKQRfa+P;FbFjU;dnEod1`sN*$4-;R=Ou3G)=K3dZkT$=(>yf!>>E$nRy5??>_Cb@X4nQtA}O84N=JZ- zlWMJWY#LH(jylrg9rmP%miCXX1~RqI^sCQng&eJLq`&?c*z%{L%V-9@8D?6cPsveB z1N?-3F=~J?bmf_$&LE_0a47Y*B8-zu?l(z;ckA-v+R?e{1eunT&*m~@JS(Zll)PnCZ6v~OV-j;lUldpUafP=3^Jtcq%42#w7?We^GB({@ zp5Nh`EJ)fvBtom1>i|qU^Da!Q5*%G2vkzF35nZtkexML-2p){^| z5K@{F*fW~w^R2ewsx`?c-|k?xHbA0A&l)aBs<1t2n$<=1(wT(Ow2nvD<*~AO(W80g O;|&MK8zWT8F#iD`R(hWR diff --git a/system/maps/pc-nte/map_cave01_02_01.evt b/system/maps/pc-nte/map_cave01_02_01.evt index 2a740129a95f78e6e36b2d91189ad236ab2d5642..029e21d94373282e3b8b1fdd113e3ae480ae33d3 100755 GIT binary patch literal 1156 zcmX|=J8x7$42A7YvP-<)5XeFZkL;2HB0(s!k!aF@s7MJxLIEU-ga(Npz(Z6NqzVsF zQPELR@drRqQBhFSQ9;2=qJeX+jjuGL>u-F<9*^hdn8>trkqudVyyBP-#6z> z)@oQA)?_Q#O8WI+ZuI}N@SA~orPx@boddJdjyG%wwpo4?4Lb(5Rjl2xlVIJ_b{e(> zb|!NkW-h0|wln8p=DY%Sw)`TOb6`))FLF5#b_;{&?dE=Kuui9TkZc3&cxne3<2u-4 zv50X4>~CsEsl5;Oo5g`WO6>!hdA5;+_94*aWUHy&2D?2=XWmcLc?Z^=VxfHocDGoZ z?K#-JVo~Q8U_Udz<&5zX>~ZF|oH1U3Jt-D3UW5HAZNzv7wv=o>_j?cari%&oeD3!F z*4tvS-$$^W)Gns>6WF6-ao*2h>&X_{_wtzaH2o^o(nn%M{pmysKc5b05t4kkkfZLs92zQRn+mJ0gZ+IUj*K z+g??jEv(iTJ0x~kY*y?5R@tzswnuEQ*o@ddfr~nOQJvkV;hC!N*e`Yv%WOKeIq01| zq|UBUXP>CEGt^vc8V0Xlofodw6Ppt2ika91wY*Dp-k#cSjJ8-uYz%+nV!N=!i%@6j X)mdyc6sHA)6;)>qu?R;3+1(i3Miwt7#?k$VxX-t!3BgBtQ;^XYNF-6w*X^wi9?XINF-f| z8l%Jw!p0b46D5I-1`FM2LNvi@;-Yi_%551s+1vl#YwPNK$(M5$$2eHTafUeIj&i`d z6)K$bG(wxmCbxAGQwC8nlq&80%(_JNv(Q&1F)#I0!WEgFE<&|-;LTL!LnaU-KB`8| zevo8V+<=Zvg4Rq+9OyW-*NfORlRMD|}^V=ya}g0G=1J>nq&iSCH04y8UoU zpvGm9^?})*r!ZV!1&ho=Ap3y{v~^ja!;rC6O00>F{@N9?tpa`Qhf5-B%d2s?YpX7( zC#!Hz&gY9>xGytDQ}#7HaLde_d;>RA>Rd}0-VVakJUGJe9-ie@2Rd?R5O z*&!G4&B7P0#GJ(Q4ZJdIc9I{~dVyL5`f~s#3iE+M=n>iB;1b<{^ZL5NH}L_uP*_WT z&sX{}?uCnwh@&&3*tzQVQ57mdwres|A^jW@U2%RUce@NFirf;L&Q+l^x`nWUH}-CM zqPmHQ0V~%>Vh3X{(`w!Cb*$*xc&Pdt-3-?x`1%ggK&r<`&D!EnG|2BW>{WD-O0u6L ze+-*bTkmiMQhcC2bTmm1kUC4?GV%s$KkG);V)$I#gcsu)+_D`!`H46134RAFhjwDR zGQNx`w4~X}tG(@0ySQR+ELaYW#Tz9YDR-t-S&b#UkkH^GNj&l@xjvsct4!QU8Sq-l NH6Kk!N=?9V{{W!fRUrTX diff --git a/system/maps/pc-nte/map_cave02_00_00.evt b/system/maps/pc-nte/map_cave02_00_00.evt index 28f63265aeb3aa6bf5747bc6af40086fa2de3bcf..88a3f7e7534d3c25608e4ad49c59bc1161132e52 100755 GIT binary patch literal 864 zcmYk4KTBLu5XH}|tE=PP_-}PJi5tU45CjFS0z&Mq6A1*gP71LKCR&+FFo*;MJ3oL` z2o_c;f{3MMk~$wl&$)MewdHwFa7icjk=lv&Ar zPxY4l6k7@32ddB7bk@bUO|@IGQsX1pUd8IU?jG3|L%D;oo@%bg{|!c84#;k0qh~yP zU#VX8Kd@5w8|7-nO5IbkGufCo9J%LIYdYl2O73^6zd~da;k%%^4%P~GN&UMaXlTX% z{iOO+u~PSn>_j&Dh9dWdDrwlvpTm;I$$SR>jE^aJ2mS;;3A3chFnz4QBtw#QrP%V2uG_{|z$y z&5~vRD6A0wGdKLT4`%-Y$Lu-$c{XMG4CVY)*8F;B|AhTkxctQgX8*YZ>|EmaHpTu8 zCH`R6{Df!!B>rl+{8<8I|BXDw{%ibedlzQ^#Qt%t{CsEs9(v|Hef8n*5u6p{N2U=jTXiJxBP9%W&adq|GQlL+XrUlrIAmuXegzOsf93=9h@WAqF3KtOl z5d!@Z{R|=sBmFRx3KJmxq6pn;@!e4!{8;}AK|=HTvhxu7BJ`l+sR}vh1q}N1^9#xD z4C?jzLR$9l27u#a3J_}wLgETC3II6gA=38e3IhDz5b_8R`W6822EhC(0sjI30A4Qr AHUIzs diff --git a/system/maps/pc-nte/map_cave02_00_01.evt b/system/maps/pc-nte/map_cave02_00_01.evt index bc2ce76a78ddbf87aba94d84b7c425c1032f6dbc..3490657a6b25b4d3fc687cc04b2fa1ef06f5046e 100755 GIT binary patch literal 932 zcmX|jry3{WyB=MlX-ZmJ9P5D`d|jb~0jDsdo$W{?@2BI4A4nVffamHVbp#Q?kR^ z%I7tnQNAe5@3BR8WH#oy6#1;?urSZJNBv$53T}pPpK8>unEPH*ZijCWzBgnCW}{BQ z-cgO~74z6*%G1KU=MQAJBVR9KKazc$^w?g^^b6(p!aR0FcBjPpeaB>PEtI*YBHt<1 zQKMqs%U8-_VV>^?*{i}l-*2+lg*EbS{3T1asG==s9om3igf^iQkYt-Fsyx~5NwRGd ztwBVE(w0S%t%oQVK@*gHiX{7*lI;75wxJ#9WoQ>V1LdIg3Ur#jWFJIy7J3yr$J0D? tk#RO_Nj6>4C7x`4l5Aq4J!l`g0KEpiZaHiilI;4D?81`lilR3l{{h;nIB@^~ literal 486 zcmVVA{{!qVZ2V^@{sfNfGyH9rW&aO_{WNU+ z(`Ek^V`cw1{68CI`W5B;K5P6yXaD5qqQ+gSo+|2)P1 zP+0t0XaD2G{!#pU7aC^&tuR#2RP+B>t;w{4OW{g!sE`{Eemi4qJu&yyEu-X8)^%{len+ zXa9u#vB>;|XaB~8{m%S`=4bx}?9|x&47%z84+;kW3fN%^=?V!5{RaK8xB(0@>k#J( z1@&c@B)ef`+&Xin(iR_3hy}Zx$g=LAj()2{|u7xg!%&V>kkm?5JLSD{S^rP z7X1#n^a>OCfKm@*CPA?08W7s%>V!Z diff --git a/system/maps/pc-nte/map_cave02_01_00.evt b/system/maps/pc-nte/map_cave02_01_00.evt index a060d694754aebd4f962e9e5f33859e46a7d763d..8912523609431b1ce9fd96fc770abfdf75057972 100755 GIT binary patch literal 908 zcmX|-j0O@WgM<(fqcBP; z#Lz#%U%_6_dGG0M(wq1Bz0W!4z2}^_7*URh#_-qQPjoUBiAkboqT94SD2qib1$7r# zJYo)PM{V$BELQWbB-+f!79sZ(!Q;#~4z(Yc_f0_`1mvTC8>}pYu`5u zwW%ob_>!RKR-!Crzd5zXTitYN;I<+}v?2+ZfZ z0(;EkfTu=2-!;^kBCsxty@5KhK6hCU?!ju7uWY^tuto+4blH5*P?N#uYrKOsgU^5S zC)m3As+R8y>@|x6x@!5FP)?VUovtBE3{SdfEJ%)Aog^8OBy@_TgCtFYePMRGDJ4VmRAd~i+r9e_7DUtM(3@FxCvh>2I h%>vL~undxnl8lg4NQOv;@m?>F_j)%Lbg$KF{R313IV1o8 literal 469 zcmV;`0V@6tG5N`bdA^`mV-9GvO02X%q3T6MJ2yr2t{8<8I|4lq4{$e;a7iRw?{%F?xd}se8{%*MZkpX1?jVLAlc>HUt4`%-) z{(h|dlE?le{;_}jwa5NB1nh;j{KLimErtEDmHfnK|Hg#cJ&By*@WctSZUmL~# zBZd9P*Zj8)X8(l!&ANi@TI&G}%IgCG3h4;;y6pCdqqG4D{Q{*5QiSjZ`3C^~+l2~& z?*9yevhM3a>;(*>@A?4}@bC*93l%WksaWW1K_|U L;1Tx!ApZaW4!R5w diff --git a/system/maps/pc-nte/map_cave02_01_01.evt b/system/maps/pc-nte/map_cave02_01_01.evt index c736434641d89da1bc018302ad902b9d01384cd5..d04cbccc4e52729f2fc4567fb7c2c27d5f18a242 100755 GIT binary patch literal 940 zcmX|=Ju^d55QcY?h*&3*5Q*=P(WFVGGGhjRLF1zkjRJ*H8C1qsVHBxUMu7&6N~MY! z4HYeZgg@Y2yZ7A8Il1q1_ub9ivw2Gq6^Up9ORom&&d`jHT~GOhrT$qKay7=K|Cxr= zosEF#Z5LLn6C2bBnYRq}5}A1iz?$x@xwj5>U%&=kbMGY7LuBTi0z20*#C5rM9_p|y zVODn$@+dO%u7I_?Zr!~Nu&a`J>t1&a@+HQZcO7gAz0`n`j41>u5gld4aInQ^*fRh$(yA8PrTJVHVefoQ=%tUVzO-W_2&Y4%LcW zdCzwT_2sPM>=F9fy%q1v6WC2;R^u7$HZrTx0^7pU8l9f+6>1@qF#FB#kh_Wy6Flw{ zYA-U2`-a|+%LT+Z@ zn)?R5D3bIo7*^>@glJ&&bU2rZb1690-xW}&yo zqtInC_Z?y??S};kE~!-Ohv&(ffm`ARehc}4OxtCeUWJz;Y5xNm30|o*z6BrQbwZ_E zumrd77}#sz>~$Do6&ly^7nlr~gfmG_gDJHUSWCfuCy-tTZgHpAg@Oz^(Oq|Or!9~D z)Uv{Y70~f99OiK-iMzJj3L$&Cpl|vM9Xvk1fd|l0#$WO!aogsn23;tk_kDkJz9Ut3 zZ@grOzm|%8EVqcfK8u?#?@B>#y29J#if)hZ?HuCsD1_J1jEIJi6yokydHYwDwFhh4 Z-JM+DzgTS_J$O=Y=IaONv1gWH{sR|f51;@5 diff --git a/system/maps/pc-nte/map_cave02_02_00.evt b/system/maps/pc-nte/map_cave02_02_00.evt index f024c5d85c619e6ac29a9d9e0780825c31e7e097..9bca2c5e5efce0fb048afc1d5c0cb24ab379477b 100755 GIT binary patch literal 876 zcmX|E7Wf%4GNlG?=@9FvsAA~_S@0#kw>vcVQL-wsmzOMJ>mhv*Ql)WQ6&Dk`MpJZniWv#;N+5SBFdlX*pfozyr zs`rOXE0AEzCBfE7l(+cwEMBD$V%ro6wi}|8&?#sQ8lVbg*7-97orS)Daw{ZQyG0w&CbSKGNwEN3gm$3w&{xno=rU_qZ$+0ZW37^4 X%@JLJu0q!g+Z#);cNATRj7FpX9lAJ2 literal 458 zcmV;*0X6;~kOBYz5N`bdAOQUTgFgBJ01dYM3T6MvW&e#7W&Z^Ho7)Fw{{pG({M={% znFH|JsBb0y3nl&?*!1Xa5H5OlrP-{CsEsB>rjK{KElc|1HKP{&4)n8fX6`{(am0jRpG-#r}TQg7XDt|HS@; z+QRoG{vO5tk=Xp_CH@!2W&fxA2Q>+1|0Mpc*8J$l{sjB6WWa^}DB~smviz$L8fO0_ z{0Sp@L zaQcev0SZLr&IF_K0Q1xk>9Xzw`XcY}2>S{gLkbK141oQ<3Klu`44Ur@`U>y?`GOMh z`hxLb)N$zyL2~l?;_`s-iV86h`4NQq68#e({Za1-<%;y|Ar$fz!2Q|^7#Q;$Lirv2 z9tiy!{UICu8ASacydfg>Maraw*9smu7{m$dRpc?{>XW+NZ4#rv{D^i_GV$<&HqFi@2 z<*Y`wky*;Elg&7*yU*_aW!N1xXR}nLjwVaF3zWx(=qbBzk?P66SN8gr$ky1G`R3fW zOtohPMV7v|M)hfkY`}e6RF~~dmV7&uSDCfO8SIjMnJwlPBHO3B$-cJ84#;jZD@Jxm z_QtT>s^5D=HSfNv_i;kDX}0J|`#7U|c2@CR+u3``prGQp7gQgarJ66vJ~K--Uz5Es zQf{jGj_N!6QqA{dKbe(c9}i^P49o5GS{|uZ3Qd;gXZ5aTmTGw=3-+)?C!u^XIsqMr z4)HV$9f63BLdT$WXfL!I+5_!__CvX=U~faT!Kgs1&>D1*Il*=;T81jL1KJMlga#(q zQYF|LMZ0*iMM<#LFv}({T4EFkZI%*jdJ=42qU?iZb2>#L8;t}T1~aW!3D%vcK>q## D`J6Sw literal 448 zcmV;x0YCm9d;$Of5N`bd9{~LSmp=Ld01tNj3T6N21#@Qq0_+Cd{E-1<|0u@A{tf(J zHf8@KW&an}{9tGQB>o=Q{4Hhw9#X~rgy$PY{1;{aJJB#>M_c z{KPhe{SL+cQf~ZN0%ZR@Gh5Z)A{%pAXqX=gI#>D<|{G{e* z{{!rJ*!V*MHR8VLCt`4IRVLiip19tiyr{UHzi5QO~_{S#pQq4EqNBJbWG!u)ay6%hRw zV*MD*3?bt1><&Qkn+h1+3J75RjtUGE&sy<>`pWU)4Jh`y@`(B>^U4A;^yvU9{D4{V qqV*v967~rCg7)f&I7vnk_Xh1jD);)D_aw#?Ao?PJ+9Cl3{{R4ed;Bc` diff --git a/system/maps/pc-nte/map_cave03_00_00.evt b/system/maps/pc-nte/map_cave03_00_00.evt index 4639d3510511f8a7d3eab884343466abd7196123..6ecdfcf175151781c919ad8cfe7ee16f42c6a13c 100755 GIT binary patch literal 1164 zcmX|=J&ep@5XNVAm))Hk?zp`@?hd;?HxkXALPdz&6<4W9xXMXHrP8TXq9H`3qY)C7 zN>s?XC^Zr^8dP`91=V_61m*MGU9TlVv&0R@=9Tmdl77t>fHic&U@)+z1v_LyfEl~*1H3>Sy=4vF4#J!VO}S5??ZiUR;(rid_G2e zD=c!KfDLoqh0J{p_9yRtA=iBc_P4OeeGT@nusFvXu+IqYWh(1^fI5@?n96#ez|Q8n zy`1+2>|9~dj~`(7^1MBXb$@|9$a#az{SEeF90T+q*Vur1Sy<$5fvsiVmNK`6T5H*l zrOe$4_OYO&p5rTPWP(QuWARy4nfeA>a?Lcji+{4 z><9$irA|L7OXD#+O`}emD6}0A>xzv7(GlwOfI7RbHZ8VCY%esstGUs(m_dw*x!4Z; u$@Zx_yQ9v2DC*fnReP7%Zv4RpsPoFzdC6*%VsNwbCe?X&YH-wWIQ$QH1z4m2 literal 609 zcmW-dT}V@57{||a)8p}!$E8a*OJ_SKLiAyQbx~MNFQf~*X|04}wj<69*F|?yOwUC{ zkrYDUl_ew@p_D`+LCu+qt|D5kBDFkI&pz_#rthBjecN{R@aOkDfA~Gsj(mm@Vwh=Q zfbP@jQTp3%0Q&w@v76?p69wRxsPH!7O7gJLCXz!UdE&Xh!%c0()B_JBntxZVg36ym zX4NE;rmygb&9dVTcr4LH8#xU{c`cpvFx={E1J8xBENmZ#i&ADzoP!RD9@VQIQ!p*# zmeI7g6I_-vZaRRwBW8*IN%eZ*ij;}hCVTBEtBk#U5a;h?7y98yLZbTg%_*3(r^-a) zy$9h!tDE}k1IZ!ykvX@K;V@7;G>T*_1SxU7F<^)1u$e6&}@gA84_~9Nr}4rWC3nl>84;^mSdS;eNbi<&Wd0QsW2^P>;vd1p*O!Puo}f~#h+3AdL-uf_$m z5Z9x#iTR2qu3`jNCXD}N-88arrJVL|KR3p0>ssX2bi$TV(<7g@7)oAmi5XEQg5ZjYU2*i#927dsCNKuLqB&q}kj-OkS5&fDFx$h3$oBKZ7Kjw)vnZNB1p zIKz>wRIqWV-OS1bbFjt8ZAWe&?9~K@LZ{pXsMnd5Tz|%QtldL zt(4d~%wxGt#MSzUrFoYjuVt3@cLi)Kv$0~&TVO9@y>+Z+&;!JF3j_2z{+H}JVvt!{ z?g0*hcMlAJr5^OvAwt{)=Q5|fM zSqW66f#G{I<*rD%U@fpWF>fPkbjYHY8?nDW*j{F7e+yvynI#V`fxXS_K(QB|@mH3` z`pu}Z3Ra2#Xhw}Suxe(h#w9T4g;VDhQ-ig}#g2*{6YCZ)EwSVH%$udoyQGFQfK|(4 zlR&&SYCW;j5WE}eybbE~zuF10lMpnyI&G~^BdeVf>qF43YVA~nmQ<$+3)B&t6+0+4 zEp|w33g2ivby`ggKkGD;I_;x2Cw2sa9#N}`Rm5sy21Glk(*O!hI#sNj3KXlWv+rsy mHX*9cPO2S78hfSA?x?dL>g<5pj2Nzy^O)6nuxhaCaQGjNNLLF0 literal 650 zcmW-dJxmi}7{}XEpd1AY6sWY)7C6BW20l$l{Gch2;Kbyh45qY~&<+rcgVCi@5 zqyTcHRO8IM7N~ZTXES9kqH>@-m>p$E4$>~I&{8!mjnMF|aLPsMq(<0Xa=*zzCmxVvhr; z08c z7>*yiA;b_n%z1#Y(CVP2H;^{^gjN=o+;GCkKP`LUA}yI^v;y_U+>Du)zPh0yi`G5x z(}~Mv^aHLK*;F|#{R%=C3l_h$8-PilXpwbMcvQuZ2qc1Vz=DC6@|WMc3tVKaPAWo< zj_%Z@?v(xm8f1v7HaSwBiB*oe)9e2x6p=skZ%s4 zO@`!pZtos)lm64n@fd2#|DRh(lQs4QkoImwxucCol+*=2PbXH{M3ZP4Pft_*=fW+l z4ll6!U^u-P@A*O%@z%b-ywxt87rJUVJk9B_Dr)0uxLUoi9aZDL7`A?jadmc%7M@SX zh3XJ^hxYLHo{2Zx#0v3@M%0Pc60w+ybRw*Jjs8@WX`k|Am`dy$bUSBrh24ap$xt6} zQ6&_m=O>b57$-SuK3?p61+btC+x9Kt5T!OLQ`V(}R4El*NMUcOfW3kRr-ZAKdsN{= SR=atKy`4w=V!V(Lh2kGu)ps-i diff --git a/system/maps/pc-nte/map_cave03_01_00.evt b/system/maps/pc-nte/map_cave03_01_00.evt index fea278f34d808c7abbc7824e4ccc6e374c4ee85d..3496716cb957b982496c8655d47797e75612e8e0 100755 GIT binary patch literal 1080 zcmX|~&&d_tiv>FDCJKFO3Jf~Hxk=ZoW zm%^&d64}jC+sv#>c2e@DGMgtmX2`FXJ%I(P?=utDzy`O=jNjq68F_1DFJwx+$$uGe zcb#!iSe4l=veT#?N9|p*zmYeN+IwW&@-d82*WQ=vO;|Jg?NNRcVqQP|9#I`d?Jk)< z_gHL^3)L=-#hx&}7FK1pPj*~bGqa~;OX0UoqS|MS)jk)h?eKd}bvw>BB-b6hWL#4N zsE2X3L#mC!>a)Ej8x>Zc?H$>N`0h^ly(fDR|F;uV4RBu`nR`>T0<=evQbNnNF6?ZU-+F9zh2AO&k-)NI{3ae}T zWQV1;%Cn}qO&W4b8f)_goLxzpU9+Uo6tkq^v!rn|pMp=r4L$>J!DrzPPw+W7&yzH3 z=4aq%;caTol$m$n=cqI#mNXZZGzsSC;av)~+q?(wtJ0F%YDtZ>q}G{VgfGJ9;S2Cf z@T+{GURhFiEU6#n%lS2vn5_)mex@t%0eqcE7i~#bYcizOW%v@2ZqbtN&1$5IA^Zw_ K6~30M?esr^?naUT literal 567 zcmWlSZ%7ki9LMk6X7jxAQkOaZY&UmK2Zbe0f?mW-6MGT8=uK~u)^2a~g$Y5g0@)de z22u1zU(JFHDvT1bqBPwK(!h+Qs~1@|ThH{|+{Vt|^E{pJzI^|Dz7DI2VT4W0)H6Vj zux5RCvKCGT>FB#+nHB_4Ax4{|f{!l{cPrc}GY~Q4g}ZhRLq5$>#;Jv~5imtEaLVM*KD%2?0r{KY9sI$p5 z8pgxW>XY)02fO#4fQL8G`N=V82+^oJAAv@D$S@wN@VXs36#Q}(0+!v5sDfwV%Eb_! z&&OQ3#~~WVi+)YPBP|O40lja68AJSe`+FOhg3%?b+>Uvn6qLY3m>`SCO(2xaew;2X5;S)bX~MQ`eN#oPENu2R5uKR%45@^~a=)=Sg6IFMGIfuCx{ z`VJ}GNr(cjjo|v^%ucuN&9HM%UsMaY#*$pl=x6;|wudf>4(tl!I_!Qm$5Mv*53*uC A9{>OV diff --git a/system/maps/pc-nte/map_cave03_01_01.evt b/system/maps/pc-nte/map_cave03_01_01.evt index 086d99456c564d8d12b9b691abd04c75f8fde044..be1aff89a7d96d5f8d1065096aead61921781da1 100755 GIT binary patch literal 1104 zcmX|=Jx^3o5Qg8`6&dtips)@1VV#}R#tZU z69i1uD7Ka=_$Q1~;X8MRxyd>A&U0qoGiT1+n{{9qz(o#w{)o+j%N!m1a?4ZnWG&XD~}=CQM6d&v$}dRNF8!bi}@K*g@p`fiA<9eXU$ zT4)?F&+it^FA=*Kes{>eCUd{LWZTI+mwRO2MmaHV5uVF^T4UiiA9@eSCfn{eA7^_+ zb1IqZJtmt@=6X-awk?!et&M^$tHJ8D#PKtk>pdg86m{-I>~pe@L!2tx&$dkK zQ?fc&fHjWUjP+bzl3fkf4ZT-n7wkf5cjIiYY0V__T;7m9jJjN-ugSdS*a&tz^xlze zCiDE>lWiq)y$!Mt$vnSJvSzTk(A%=u7AM+sk>6)pt@Lxf9kO;Z*V`rg#bL20L+>}O z8&QK^usyPsIBzfZ*e6>}=6ehY_)X z$Dqd{QmlVPhoK{Uoq#q8iLJe)SVKv%77}ei+fWv|SgS~}<`Av%%dTIFUAQ!?EtEGd z#V%2bU7P4>=qQ<;hO1pumgA|Z0 zg<7sfjzEWxyn5Q|CrYol4W8ZRh)QIJfv~v8%VHZ;OaksRiT4THoH^?C8= z=CaaHn#98=;p^#}qJAI+r$96AC+pA$BMU4(E8ugJu*i`pjtsyTj>JN>3$Wxg`AMjD z@dLCA*u|sKL9pxVt^m3MP6t=Vp*XM&ymmDxNV?xVx&_H*SH0hS`A!_ZH>kS_=oU&H zF&5v0d1p%_Flc}%N8U!4A5D~uoj6o`o`hpQV&>6{kI>2Z$zTAzwiP+z4b?($+1^(< z0m9Tfm~r{Y86Hi>VYZPxeF^63ngE)CPviBSwOJTx^${nJ=E9(B)QkwATfngZS_Qqa zVfmSaA8g6MqqPL|rYe@0k4W&q#aAsUAaQue)gNg}EHd&R1!AtJ6?8L2TQz9A8quij z8{P6Ay=0cs7WsHaBZo5y>yGqJHQH6y=9O7@UR&JFNP8q|?O)elI;WAxa@_mX3?9yXx}ur uC$`eo`{{YwdH}TtkQvz?qy3&$2QEFy0Pg)2E0(qLP{p0J=NwvzqW%M~gG=84 diff --git a/system/maps/pc-nte/map_cave03_02_00.evt b/system/maps/pc-nte/map_cave03_02_00.evt index 57639e49b07cb2ce70ec6eed687448da0adb5e19..cb0de832c108736bb6f63aaad7c708e84f7ccca2 100755 GIT binary patch literal 1020 zcmX|Dj>GrLXG_t*7zyF;7PlHXoif3XV{ z8=?2Quv*0u*%!&E>qP7=dS466vC8u*axBJq3uJ>}y@*{T`xiNSp<5>VUs%rDC*w9h zx1QCjyWEx68L~#j?$bRC-FoC$BYQNPb?cGi0o})i)u=RkNZT!}Ua?KGCxtaCwnMfW zb#x=gQ?dm$hkiG5Jfqi&yYxf%oNO!Z+z;IgvhBihy)Vg7VspKFWV=O|>)j`7hi)Zu zydgUZ-Ad$mN7gC2oZ~&&+o)qHVh_m1G${0!BK8BlA3|i4q5DYhUUSIun}4Ev962_q z)6w6=qJJAaPwYGGb78snKgCLH?)@)mi!S&6H`ysocbTGTc28)dts%>IIiuSwEZ6&o zENRF+X{??7Ma@t*Gz=q#rhy|3UfextC_QNm9g~KuhI#rLH*RX0JZXMBX<|HSMjTUy zS;IBMw4rUdVo2Q6TsW>9ZWuT(N$>ATFYcJ*N^i`>`_o9%cWVlM`nD0%Pn3{FHNN{{!p{%>2pH z(Cic?{vufX%4h$nB>tF^<@}8(W&bDqE&pZzGbR2&K(*^Mr;?AhIRfm_*8Iq4 z|0ML+r0ggDB=kw!)W`l~B=q6@=NqN_7p42+*ZcaZaBWdXhM-=}ITI&S*%IoeJ9|{(t{UPv*>?q@s?Fjk; z?)rjq?)u{H0N0jb3it{b2>luTFvbcJMEwlpAwuuu90c7m^=@AzE|Nj60yooMF diff --git a/system/maps/pc-nte/map_cave03_02_01.evt b/system/maps/pc-nte/map_cave03_02_01.evt index b31b88b41c705a2d9987635ee1b8ff41d80dfdaa..bb7e009d1970385417a7860f5c08cf4fb8b47772 100755 GIT binary patch literal 924 zcmX|=J5Q8B6o%ik7cMWOpu2#GT)ZSi6BA;Vn9vv-`~y@#VIq=HQCU&(8=xarR#p}^ zS`aWA6)F=GD+@b+fws$gX3owgGdu6|oy&aZobN*em;i7`%YWy2H*{f`&u8GD`N8lg zSWD`#GhmHuN6Dkgnwfdr|JApZ*Oay za`eLPhuCJs>xJDfv8~F=oqvn5`j$KYDa?`&p(P(XV-swFZEy%221j9*e5fq>7#XRP z4~lUNT-4tc@G3YCCh$7g(Q_XLOWuCtgaYuIq<7Df_sf!Z$ddQOcmtdPr@>ip3Y-MH v;0SnI7Tyg@UVlqoapO&$yr!1Cin^N@$#@H#1691_#jxbBZ`6lj{eJ%vy^ca( literal 482 zcmV<80UiDyxB>tG5N`bdA^`mV$v*l402Fro9%cXa0UKohGG+e+H~f}sg?C;kME>?8bbEoJ{6h5aO4{KE@o|6_#xD*VKaXa9u#F#K)P1^X2x{xn?t zKL}?3Vw z+x!#d{59qLow)qJ6lVX%p1#K>;Vdd(F)=U5I+jSPzqrR2>|Wpf?DnR%Iy%~3I+DM z?)Dh*3Lr!I4fzfT{SW;xQVJ3P{ksU@n(z8T3h?0&9KQ+?1@RK{`ay#7`ttG+`eO4! zloRF!Bl(1pO8L2t^8?qY5G3lJg+?0`v&_a`e(s8unuK5c<;fg=E_H=^PNy Y9W4DB_6(Z$5c&Z)>N)2D{|Ns8046XF$N&HU diff --git a/system/maps/pc-nte/map_forest01_00.evt b/system/maps/pc-nte/map_forest01_00.evt index b407b7304cadca86ae9a1ecf523e87d84b016dc5..13277350ab08a0b30a3bfd127eede74e81da7d81 100755 GIT binary patch literal 500 zcmX|-u}(rk5Jl&{_u%O|1;J2ISy`A+X=7n!qXiWWm{5^G>yP*ceh~EVC)ne?yTiU@ zUiO~s*`1vqn2pTro~7?Q|6z9qd(LaKWv7K$li84IRau*vlYLdzWi}?;vg9V#8&^}- zxxKO`vj@h%%G%5x$zHUL-XPo=(}n#OR@~fUb7jSSWt<4nYr@st$I6QP&U~t@xSz}) z!TNCL%=@^b=ZQLN!8%k{+!fgqOZRr+u9<$L=Puk0nSAZLiG4dJ*P!7VEnLGy8dw@K pXku{92&uP3$u$T#Pb30YdK_HOLE@ub4a3zUQp=T^K)N;H@(p&T{1qks0;whb{L^Ru#w7ki{L~s}|0MoM+x$mm{|6=hOxXO#<@^W6CH_+U z$Qoz=B>q_2{3#{=1SS4l*!-Ji`UI9G{$d6GTI>kL{$}F!7rOKT3@Y^;`kM6t3Pvpo z2J=C3_WI)XfbEhBSj!B$_aOQO0PXc4_9GzrF~IN(3j0!t7m)S*zE!C+7bcn}P410Hx1@K80=Hwl~c zX6oCn>7RXt*|wSOF!bElH*CvbNA+yB9CZ*YmCdQHI~ytsvX#=9=ZrI+7L4y<%}Ql^ zls}yfmF<%qD~-NcdJn07*;`}Hdm>rZta(o_&c@oy zlKLXql=t0Gzh&KtRW#cz;u-BpUL`oOgwGhR~Ma{1ZvF4TR=LXyW>f>k5s9&>-+)J{W5t)G&P0 G0Q>t&2@d9T5#w7kb z{1rAO{sJZbMb`Y&Xa6MrOIZBWXa8d){!#qZb7%hq>{o33M`iy9V}<=*{Kp$b{0D{o zWNZA$Xa9u#YHa)|CH@3sCH{2$n_I>C1SS4@CH!ja2*v(>tziEQ#QuLm^&0d$0St=t z0Sbis3J3~O2JlMtu==|70c`&v3VR9`v`wD=toIvnT5by~={Qm#|uc?n1 diff --git a/system/maps/pc-nte/map_forest01_02.evt b/system/maps/pc-nte/map_forest01_02.evt index 59439e44396d50829087bcb24a9b8eaadf2ab8ec..fc2b673cb842b77dd6d255aee37fcc10c8754ca3 100755 GIT binary patch literal 564 zcmX|;AyNb}5JkIpc4r{lViABspeO_gKv5VlpfCs)g+ib>5ExFwda!Vn;r-53GWApG zm!$K%XX9ooGuuNN=Q+DA%=zjVb0IcM)(g)xiFL_>=t3yjJel(DPE0qIW(R#YR`cx> z|1?(f9S~Pf&QmqtA?&xElS8v3^w(IQ?1cDsVguxg^(p$AJP%|Pliy8aU9wBq9a0^` z^zJp$+r*aeGuAigaW3e#B%he>tsCo--NQbS&aIH%U87?mVpS&ggx*z9vg+@d=%caf z?*;atW1QPU+I~f$pX8z6;sxxXE_yGecyAsG literal 282 zcmV+#0pNV5N`bd76APJGlu%>1=#!nW&i)p0cQUK?5PIv0%rd?1MCbo{4^*2 z1XJv&6&q#$0)_n%Yy8t^|AhS)to+nx|AhS|9cTOOV}$)7{M`v>|6XGMC;Z-XXa5H5 zEo}TpW&a0^{5^j!`NGJY8{6X0KZ)N`r82mEh_Cowu z1ZHOc0_42mk;8 diff --git a/system/maps/pc-nte/map_forest01_03.evt b/system/maps/pc-nte/map_forest01_03.evt index c961205231065f7e8789608c59874014c0f14f8d..dc64c5a8d0d489458d5621020d31d01b3040e957 100755 GIT binary patch literal 524 zcmX|-JyJqJ5QL|f_W;KY43tb0BO?MxBqD+;jWi+h0wf+tt@1XS>wTbmXKG*7Y;AvU z{#s<(u2{W-$7NKMZY()H6!|sZN`zR%6vGjsaghH;N$|qDg1Ha%+ZwIQLhN{QFA)wt*xfoB)htJIC=2LS%0jD!4fEhgi literal 257 zcmV+c0sj6UpaB2?5N`bd6af7HZ-)Bo3)uV~W&c;r1ZMvN?6C&?N5%e)2gUvh{42)- zX8*+gHK@pE{{zSD7W^qTCH@2@{vEFTn*nD3B>u4@{G4b1QY8M!opWdZ1ne(t{85Gd z2xEo)GyGDGXa9u#Is9!EW&Z-j{yMDu(`Wz0{;@#()Mx)zMEpiY`egr0MEpv{{OpYd zh5bwXE&m1k1%>_FWaak-h5c8g@@M~q{jpvAG>82;1?*~e{0tiO0SXJL_6!R3`T~0O z@C5q`v=H-B_5}LE_WB_*_vZ;x_5=YS`6VFwGf3$H?*wG}1akTWfaxIzxd;UP2K`(; H0sjC1kn)Lf diff --git a/system/maps/pc-nte/map_forest01_04.evt b/system/maps/pc-nte/map_forest01_04.evt index 1724cb381f55445dada9a0ab9721b96ab940d17d..92032617a00a2a7c88c2a58d8d288957c951ece5 100755 GIT binary patch literal 524 zcmX|;y-EZ@5QMw-=T6!VE>I&8BSA3IR3ig1Gx7(F1Px4lAPvNq(f85(teWndg9w^<+b13#yOK#>Rr|N^R_0mRI+F4;z-=J<6ZX z(z{RgrZ)Pv%X>(5H*HvYk0|duOYaHULuXT)<&^A8R^GL$`_8GJi+QmN>fcci=C#`; z)n8}%jcc+qwXttzY4sb{x-yu{d#je(ZMO725}~u4?}=Xa6Mr9NYYFCH@N~{vf#gR|IDN#w7kG{8$=i|0Mn_oBUa4 z{{&;~GW?<&h5ZnP{XEgW&b_6{PP25|HkC}L;Um_XaD5cY9vS2*~tT7N=@>-wTT_XQ69J}G>c-J c__cany;DA=FARyUo|*p+A2S;`95Vy_0sUkI(*OVf literal 68 zcmZ=xVE8X^=MM|Rpa1zazZe;~-~2I3{U6TqEbTwj8@3yN+>icam-u7z_#4-s<4kEF YL9Z1&hYvGy{buB0dH5~hHzNZB0G%Zu=Kufz diff --git a/system/maps/pc-nte/map_forest01_gj.evt b/system/maps/pc-nte/map_forest01_gj.evt index ff1bdda66a6839e1e9dc20479fd318e43dc82d30..66e136ebab659ca6329c96de91ecaa9a11686c2a 100755 GIT binary patch literal 144 zcmX|(u?>Jg3An4{{R3t7$)xk diff --git a/system/maps/pc-nte/map_forest02_00.evt b/system/maps/pc-nte/map_forest02_00.evt index 66b570d320435dd5bb387f226c5a0fae3338fc1e..d3f265db7ff0b24d55ce47b83619ee1e94bcac1c 100755 GIT binary patch literal 744 zcmX|n333I36=~Z5E2nFAn}kO0i=y!gAI5@D*_^7koXjS zfP6m!2-*FB;oRz`W^`NjY1`$pdpDR(o7n==Z!$mKntyg|wq+qx<7xJ{uwX;%F52h` zx&`al4AvAH3BfvO4Qmc93E@}h8`eB}Qqjyp&Q;BIA=m`c>qT@zzYPmE!1|6(3+v=w zmSJZ|ak0%^!MkYLBuw>J(OxC!+&kDMmU`STtf{vNJ1DHmxm&PbsL8GE1#25!2~jV# z=kDM=x4DTkbnXGVMopY1V|IjYh47}@++(~^{l$VEVxFQ0Az}t?+)upMHg3SAb%%5G zR#=_S{SA9BtefYjJ|0n1ubIIO-bab6a=kyWe}(ljyMz5NtZzT??~x~c9iH?(c&see z7TKXEE!pFUS6ZVdEzV=G$P7Gb6`nMFPa3z!uEm~3*5yev^vE_nX(S%|MCsC=bWKmX T7%$bgCw1ycy?Gp(kH_OriViFx literal 389 zcmV;00eb!)R00415N`bd9RU3Q3$FSB_5b|;Efr<|0%iXd{L^RuV*VcX2i*KW2xk8S zV*nKP4J`a0#r{C<{5*{I7yOMWW&Z^HF(dXEZv2}8X8!`F#H>9w#r_6G{30&=V*+OX z#P<*ODA@dDXa55n$S&LbWhMR!CH^&7{BLFd$Q%LKaW?!{1R-YsyLeLeTv&}~|AhTQ z{LNWs{{-x@Nc?bz{l^6W$o5j${CQ>n48}$LQ~bL&CH@Sh_E}c^ddL1U_lW;p{B46} z{|zPnUJC;D1!n)m30f4}{FlZ455@jySp4V3{>bbDJh5*4=x6^q1&DOE{OM=^2Ltw0 zc$kO%W90XH{PA$>8KE%}p diff --git a/system/maps/pc-nte/map_forest02_01.evt b/system/maps/pc-nte/map_forest02_01.evt index 2cbe40d63596ece7673c2d0cfa8ff417504f2cd5..bad69d35706164b83eb231a1dee2e0be27044407 100755 GIT binary patch literal 776 zcmX|%omAOU{>f!|1% z&!7%12|r=Z-CgaouD$nY#`9)oFFR%*%xr}8%%pF(jya$FJXhAWInF&aTUp1JIrq`u z%9<26z`s=1CN_=FwcNT?_=^987E4?M)Bk-&Pw55R8s6q3{tH?`Mof#&IL)JPLiiq1 z$^!a9Uzv4M-6h<9Ww{U4IZzl`q2#XOjtiSAYz@EF2)a%2E~=}VKZO!gCuZwtug2vW ze21N&9M{@!P8;Z4h^&Z|yNUjk+(-@0c^h4z9G7eC;I0cBl-ynXs1c3oOi|rs#ov cdel%nOpvX%J*rWUYR#jX@i0f2`*=Kl2X?b7^8f$< literal 401 zcmV;C0dD>ud;$Of5N`bd9{~LS6(0Hl0HEpo0cHO^(`Ww!03OEn2K*L6XaD{(jP?io z&E04J1lX|+{N88(xdrSKr~ClyvJm_{WA+;SDIsP5#sSB$8~mGv0cQWg6d3j)3;Z+1 z{<0AKcw_b@{5>0G{|06HCO7|AwsmnP>lG`mu-nnrHt)^6>wN z?EDNm?EwJ=3_|Yu7qadF3bFG+%km=dNb(=`f~xWQ8uF0u2}JLr^Z5a)^zK6Y3TX-m vM+#U43VHJBLDKcKTY`XvbZGXUf{Ald)_2>$>8XJX$e diff --git a/system/maps/pc-nte/map_forest02_02.evt b/system/maps/pc-nte/map_forest02_02.evt index 7d98bff30c1b7454df337d1682623ecfea6f172b..9e13c8f4a66258e9e2202677c2347cefdc2a6309 100755 GIT binary patch literal 844 zcmX|P5BQ2yyPvYocW|5w~ZNpAb&dt4i;Pkopx>WBIY_G6^oymiyg)JoCA?zHfUPK!2 zDf->}BK_%Zm*`Jnd6!qP3zT!UC9|*SZ}Dj(X5Y|lVV$jTzDCMd?jn-!I~uqDS!E-_ zALvF1#|WvHU+BK~jnWG=o57+UpnKH&^1xnrOdj=OJd73U0eIBPJ+PkI!XwUFt-FT_ zvDV^Ii}0w?lcdRd)GR5W|Ik=yEp!$J7T9UjtUPKu9(W5;_xGq9ldcPSSOBPH_xXJO E0GJUk^#A|> literal 436 zcmV;l0ZaZLqyhi{5N`bdAprdU6(0Hl0HEpoT4n!*(`Wzw9vJopd;At>{{sLrjP?io z&E04J1lX|+{2yiiklF!b*cALJOJ)BAW&ezun*nD30%P18{4-0%{spD`v;{q7|9pu2 zJoa%Q{9^)*X8#2HCj8B0Xa582u`K*PC;mAE#5OkkZO8rvQ`qQbW5xao{4EP*{AOqW z&ut2&{cmOe3#9zSLb&`_1ZMv^0!&CY{8(rI1IMsV{8<`j|D^U*Yy5DB{iOR_TcLdE{<0*-Wj{58C1|D^GQ zR{VM={?hV@|A+h)#mD|~^J4#v{Kg??|6nMV_LBwv+UyNQ{FEZ{1wv;3h!2eRn*42- z#r_Y)^qy?|=f(aHA@l41r1ty_`s)D!46Xw00Se*sne8C*a_%JZAN4`{@A_)+5bp`| zM&Swx@Bu3E`XLJY3WS0RI10fB{bBJ8f}-~N e68B)_q3;ZW0Q$xf4E`@f=?L$FP4E98{{R5<;__Gk diff --git a/system/maps/pc-nte/map_forest02_03.evt b/system/maps/pc-nte/map_forest02_03.evt index 620e83a08a51532c5b0bbcd8837664fa0f27b77a..b05fedde5d9477ad4e415ed5423c41f174082317 100755 GIT binary patch literal 768 zcmYL{Jud}O5Qb-V_uk97N3IB>;9jEe6^*EbT19kJ5}~-2k3@q?NK^`y!cXB}sA>HT z&wF+qyLmS=Pv%T!-m}BPY~0NHNKZF?-Slpx*eqsNKrLyiVIl{_E%cdLk@~6EbY^Ah zr(R!~Rn{Y(K;N0QQvVt9LBmRjW|>7R4XYY9NB*Xv$SW&sl4ud_2*KKDnP>@}vTM!i z`PRuUgsABvd9aBNt3Ouv+akRbN_C}e6YZjtTvu5j-$PfZuB-RhC%b7_{GkJ`wicJRNV>@@sW1R7@lg#9`T{BXzq zaxpyiM;82H$NpmX3ieH`{Bg(r#QCvK{CQ>ng$(2M|5(`kyCwb%#%1+h{JiF8{{wVn z*!+Vf{tdPz{%At?1!n(^0t5DL{LLU|{{!r?bo?P_|2YJ(de;1ehy4WmdzxbP$NmOm z#DV-O8)yFq!}f=p{3~bw2xIJx{Ffnx{m22w?2-KEmc{-L#q`G90SXul2J-F!`9b>c z`fBh%=_wHP^6{eeAM4`s0QNki3JLSN^C0sX^@OhqWeNax?G|G60bu_L1n;`_`XMUz j>Iw?>8u$8wiud~Z_ei23K>98~`aA&FYC!f{Amac4R5;ed diff --git a/system/maps/pc-nte/map_forest02_04.evt b/system/maps/pc-nte/map_forest02_04.evt index c38095cea87e1f51c2f32baa8f79df461c73729e..58e206edc151745637a38b3625d002c8c960f80f 100755 GIT binary patch literal 816 zcmX|ih8PTGhS8qCZ^r$`O?TVXD) zWv!vYDw#&iTJMGV=<7$`&8$MbVZcJ}5ImES=_t--Antg_PoW~w?0VN;1C9)4)6Cvx(YY&ih`B9Ojxt$<; zBm!$^-zl<>>}yLE=X(kESy<}971&o{_2|0+dxBEEblzKJ-^J%+-|xVF3TwzF@(1X* zux9N2GnfX<(0G$njTc3Omr#>{;i_@^8oF?BzG|GP2HZj8%+!wfRO1lT*mgBGT0>nz oLjne6Y^Vl|QH}l4&;(=$)Ocn!o)Ry42sM7T8b4G+NA&OSAJHl<&j0`b literal 420 zcmV;V0bBkbkOBYz5N`bdAOQUT3(ooh{w*G5{|smUWYZ7!X#W*u{{n{S{{`6m(`Wwz zOvJRk7H9th05YZh&E04J1lX|>{2yii0NVU98TK3&{3&Jsa@qmJ9bEjI0cQV*!W8zg zBm6zZ{*4Bu`zQPhV*+OX_mM63n*3yE{{v%)GyFawC;qNH#`ZV-ZDz;*1=wn!WySsq zW99rq{BI#;{}=(s07U#(g#>2*yLcG(N^AUBXa7X}P^|n}XaD5)u~z(fh5d~T<@{j$ zZM(((3}y9Zto(Y%{*v|(|7zR(gJu5>CH`yHV)X@P|3va}*!-8p{tw2*{&oE4LdE{< z1CH2x{59xj|K#_9HvH*l{|Cp|hy0ithy8^3h}-<}$NmIE^p4p4@@M}DV*pzAlx+O- zXa5OPBlMc`3T$W$vh4v7`MT~P@4E^V*9sZ&gzpmY?;j}M0`Um?a`EsM;p+@y@)G)P z`Uv^j^ZJ5H^!mE=0Nqca3Wy51@&ycf_3%N-@$~}t0QH0r_VV`#`V0vA8VTb&2>M9~ O`d7l&9{m6R0ssJ8x9z3? diff --git a/system/maps/pc-nte/map_machine01_00_00.evt b/system/maps/pc-nte/map_machine01_00_00.evt index 472e2cc2becdc821ab1faee0d66f418d7af010e7..db38bd07ef082c6e1844f44cf008c26b3917c3b8 100755 GIT binary patch literal 892 zcmX|z;3u?2~PjdYu#M?0vRKEQ3;iVU&IDJD z3E53zu?DAPw}}lgBXGv}>uYpLte)qL`^4hE^~wGumgRE?WLq^hR9&z0CDncwS&!V- z_{nHB{IbNn4PW&bwC{&%kYBNb-<#Qw2;{8`2RjXcHviu`MP7iRy& z{*Y|^W5xbGW5xcU{NJI){_Fz7^s%P=;Aj8FB=oHO;TmWE=|AhV5 z`~`-`{`Y|V|K~XT3L1p{8~q#r{iiJodF%|j>??EwlAqURFxa_#{0vat$t z3`*}H`nvCg?Fj7&0R7cN3Jm=X2>mAQAm+;O2>Lql;2{<47R3D*_977ZC9(PZ4C3-I z_8|bl>{SXRDD8^#813or1w#D>{RaU3rV#2H^aT2f^!h>i^!jS`2-qLtF(CO436fg( b^&t=L5E9`AK=Wn_FoP}p7-H~&{{a918sZZp diff --git a/system/maps/pc-nte/map_machine01_00_01.evt b/system/maps/pc-nte/map_machine01_00_01.evt index 88fc5341683d18e457d0d2307d1bb064109214fb..4e39eefbd6a644c78b46d35f2aac8c8c1474a23e 100755 GIT binary patch literal 796 zcmYL{J5L)?5QWcp&FgS^8UrF25{&3*>_8e@N)1hlNC;9=P|;EGkZ9l!P*L#{Ql&=X z^#`b^;2$JSpmfgcPL{Ri>YmT%p52|(LkZKy74TBKUabJkS7mcA@!t75%iZ4uvoQN6|4 zc05a0Y)J3rd7s0#A@yVGKP#U3n`A9~YvKDYb{)R8$lDQXgl|23dt%MH7U}hP=6$K{ z@O8p>Aa*oU_&SkyD0y76Og#inRVO8@<~=CH1HD6@9)DJIe~+PMm#^JTF->qbD)W!jgXsmVEq{eBj10a9qNt zYBaC{D*Jtxd}hW0a1a~4LQCE>Q#kv; wekjqB_s5cV#5f0j0O!F5sP8xV`I2?AvQG{yc(xcplbX8*>-{!siAHpTum#r{;b{J+KiHO2nPT3p5c zHm1e?XIvE;X8&aRZQJ}=#r{0S{&cwfdlzQ^#)SQP{Cq-Z|0Eua_JI7&eP{m!*s+HE zV}<>VJ%#;~{B7UG{yjzfnQZ*vXa56^#Gm|a?QH6!TK?AoJ7;Y|jb< zu>T7TA!73I2ZZwo{Rsg5%OLFv^8oEQ2=@B(_8}qZB82@T{Uj*uqV$CAAM+q6^Lq6N z`s(%if?D?HdF&wt{RRO2t_bY{_xeI|_vac2?Hm089Q~RI3IXa2008QSkqQj`4G{e% dWAhID5eW4X{Sa&l!iEZ#3Knqf0ssF2005!2@Yw(W diff --git a/system/maps/pc-nte/map_machine01_01_00.evt b/system/maps/pc-nte/map_machine01_01_00.evt index ad0c8f69f80f3c04808bb0ce0f321b48f19b61e3..f33c9dce1eb146160cb10858e05077006c877a73 100755 GIT binary patch literal 1136 zcmX|=y>Ami5XIkcXU7?DoCMnm1QG%bkx(dNxxyA50;K>7grGwRi2{WxQc$RF5ek~5LV|=p0P!sl1>wzlG-ti_z2CjryW5?+RsdrFj!<^2=TXpt@HaR% zoVA^m^v7!k1#RrgIi;J=1%erp9({v)UFSeQ!0#=Au)%$glKpbF;JGf@gC+<11%JL{biX54Om#BkDus>U;=DXkHqfrf?A#AK-csASfjh_xxVM#k!^Xd z&z$wuCbb=Guy60lzIg4T*M25DKh1%D(QALueU03p_9xk;$d&mXf62Z@uEH6Bf7JJ2 zRrWsnj8e8;vJXdel4tg5NV1PXlC{5R6Uy&S>rgJH|0U~W(IIFJIt*2aB&)3F`PVH5;qU5L6c(4?X+ zYB$}d=C-qa#_2uZ+qr%I{CIduoT-GEdQ@_N0DeBE z=t%w~=0Rr^&=6R;NT=A?RePFuUEt4yb%Zbcr66BB%VsWrvUAEw#v#|LN!)i zWmnGCz+VwlU(&W%O4xyV+7*oFJ20)-(vt;we%@t*dK zKP%@ZcWXDhyBwEznr$H253{2prjzr`I|K#gT*@GJcE5K#CUFBfIm|{O5EL=f<9z~! znk$go>}d!VC7$)^1#`pqu!u%F9)cetX4}YHHbG%Y#2j+y7JFR-KSj*F;0s<}XofnM z{C;L@huMLHvTp%weqQhAUo1m@euSp%O9ZbBX-CB8ov>-;XI89y11|2?!WJ%fLsa4x zo^V2k#PttC3Iw!uX&x_!xQK1(9jnuHIb7R9jYq>eq3ziui)!HuV5O8>iJxml)5Xb*>yL)#GPetzlztF@&Er0^NN@#4cvJufn(ZbpaKN1?n%0#fzSXfz^ z*jX79k%F2^8>uuF#!s+3|1NJDE*o zU5{+7!;SV-KFe8JyP+#E-+-U-dFV>F&r#RUy6GJ6JlVgjo6hkrkPR?~(M&U=%rlXX4%TTO3qz00~D^G?uS;_8O3zyj^tT$`aQz0S*I z!=bC(cZKXyW;6NB56SL~a`T)G_kBd`Zm_y_M=WKif;Pm0)d*v*xeJ7IVDWe7-kipR;Z@*WfMLcCfmp>txr%xAgvQk$nqY<#|7n z{m8nxoZ}PO#W8NQ=W>6yY0U>K{5igm{S4m{?|J60WLHC1=lD+cD|Agp1^#mV4%TwE zL-r?F+u1Ie7La5wlO%h6B-slix)-_+It1MhJpd7HLzTS#N;dH#$p%`q32i|K=xc*3 zItWeBJ+y5=DYN+#9f2N#9wD|lBepRTJq|r#mJNm^8wE+$d?M?uBz}AVOK3&g s`XtFZBiey>p~KKo=s{BJ1Kq96lB}>atZt%*p+{+njzRfv((m{G12aBaYXATM literal 600 zcmW-cO-K}B9LC@MaMQQ7wp=%JD@`JURu85lnnrAPDNkK8>yRI_Lqw8!$_f@~b0`b6 zPP!3B5R{dB$WEf{hc5|v5lVwlN>^ta+u70Gh4DY{+tlfK4iC?dUy0v>kfTdS9t7<3 zDb0kO9GG}kaVEQcXaE{A-ByU~?C~+Pj6Dz)nE&l`ll~br*ytO6M`%`rr_mM6C2ow^U(-G~5OKzSu@S0c)clH@FB>DvI77MINeRdu0{mWqw zwD#-O!STi92&V=JwGnX?9vCh=j=WyEDX?4=@_cw#mgNF^W6t)c30R8J#?@?S5|#xv z)0vJyCs$6bmef3_N(PlIEJD(-JHHCw-^0a&J_8b3h8BU5ezQ>{OMM|=3 z4r4UO?4!5Vf7^Q{BtL4yL7eB5>JwL$DQp+@eC4Ho*z>+UgWs+rAhIHZyx zxlFB3mTGU3qE@VI^p&Nwp;W16R;TCBVJp&YrAk=M)v7iVj)&Ox5f|!3DlR&nknD7J bOVsMqn_h3`Yq7X=Kt)+K9}oUu%#6@q5A|KG diff --git a/system/maps/pc-nte/map_machine01_02_00.evt b/system/maps/pc-nte/map_machine01_02_00.evt index f59b0028a17f549ade15a1ca563beaedceae0818..6fc4cd7af9f24813660341e1dbe6bdb2bb3a2dba 100755 GIT binary patch literal 928 zcmX|j?nSh2+5JIyn2_{SEtSqF2P(e(fqJm(s(g2~dFjQ1_ zR8(4P01J#Awxa$K#&S7xnapm^d++@A%)R?&?vD~M2w;k`&vm{E8W4W(KR3>r&dT(2 z`A5M7S%w2#3xfa=f&$T_bU}bz)t8JseCq6whXMZZQGUoWSL{&Za)Yp4KR(Cc}wn)E5eev3P zF1$*=PJInlWVT7xh}tri>9?rs$UVqC*(UoQtOqaYcc^C6_Tm-&E_D;R3ZB!yp?(Id z=Jno^4MnZuHT^s4SLFKmCcrUu8!Tn^p6qw9{@kk*vOmFU*rPwC{stS!wI9h=eBTa< z?Qur!5A#CT@p+unZFp|cbDzl$iu>HEvkTh8V5Pj?CD~E1BC{(p<#EzUNV0<{$&Q{R zJ8YtZ(1*N@5ZDP4EkRkB?3jooI|ZT%+7GQk`6~JdT8B2EP3SO_Hf%{YUgm9jl5Acy zHV#QP02=GBB!Bp;nk4I!=qPlI#+o783$5@r1RaM?Kp(SSvt`yTu}jP?lp zo5Krc{{my|4*bMpXa57N0B-!nXa59_up0brn}z);<@_OR{Pku3GUH|cCH#{U8fO3G z{3~1h|Hb|@#r}Zg|Mxb<{xik?I2G{%X8&SzwDvhF@)TzO#B?q8KiK>U#r`zLh5bzY z6UJr#HT*po_FN16za{?c5aU{7_G0|Nb7%hp>}V1UC;nobQTAzV!t}@f1!MPb?EK1S z{{{dtQ1*59{1qqu2LNSQ_I85vkH`K907mw)eEigB|2YY;g7*9tC;kcmc^LMCoBZ9! z{tIIyiTvJkXa5ZBk8J$kXa5ajqxO~j;4f$Y4gh#!@;3b8Xa5h!#G(9IHbwkA1@@?} z{CgK>|Ah0guKZ)g{*67w{&T4n#j*!+fP{{kHB!<+nvXa56Z?8N*Ehl%Y9 zAN&l0y6gHX>>&D@?1btN;}ID77zOY0?e>Va3dao$%I*RA0@Cm7gaQf^3J@d;2Q>;3 z`3U3+=mQEB{eVQ>n(!d{3h@fi67u>L%AEg-^D6q(oc{v#2>NpM`hw#1`cn2F`oi{v ts~NN!DD#^4g!2dU2pIVmLirp09*F%Qz!32ULiY*%3NZZv^Zy9{005@r7hM1V diff --git a/system/maps/pc-nte/map_machine01_02_01.evt b/system/maps/pc-nte/map_machine01_02_01.evt index d7271e78a6c435f8d4c517e3b1769ea34be625ac..690345c559343508720475515293d918b657b507 100755 GIT binary patch literal 876 zcmY+CyH0{Z5QWbG3JmKNyhkOmGSLRGF;G}p*x89CHa>$|SSXc=iB{TJSyTA}-YW|q zz^AarGy6|A@h7wF`F3_@cXqCGz$Ab=&A!h#3ThDkvhUm3jI#puW?U4kI2$K>u9%~- z91V;Cyd;*7dXelku|mvIC%gA}>m*(3P1>)shUaLJ-BdYHH<+W%**0zHeogn=F&Ey; z{F%l%HtYKdie&&Up(wcYQW z>|~Aub(`@lbf0#bSXQemva`gp^WBhjQ@jxj_!zfj7s+ol*6xn%GO=Q;)dN{Su@W{| zm`Adn$Lsj{24p|(*Wvl>EuLsc$uA${y^&qyIdEUs_r6nICYIIUgRGxeR(tES-8Bwz zZ+5pYD&mE6SaBs-IVD&DMRS%*OVm~<(OKvMbP>7)t#b=E Q+3iF}p+)daG8_*70OhJV6#xJL literal 452 zcmV;#0XzO5kOBYz5N`bdAOQUT$v*l402H?T0cHQwW&e#8W&Z^H87<=)`yOTg4j25x z3ugZStNZ}}yqf>SXa56Z@EZKZGiUz<#5IJQh5ace{92%F{Pku3GUMg^H2jkjLT3M* zJdE}_{B8fm{xik?L2B^=X8!_C>`U_IXa582Ql!~u|D^k|S^NoQ|BW<-{bT%VTNGyh zMEq%N{1e6gHIv2ud<bbCu(7uMWXJwF1F*)mhy!H*S|$ET$B1YDljQfvh-d%0B=o}UMQ0WXB^$_}s^@RHR u_16M4lnMw6@VfT!Ar|u)g#8-*8xZ{BIgDK@CW@EgaQ8q{{R4t|Mx}! diff --git a/system/maps/pc-nte/map_machine02_00_00.evt b/system/maps/pc-nte/map_machine02_00_00.evt index e264b502717fda63d2ae8b14791d2ac566752179..aa488768cb62ace16e1c5d24e0a5cd31b892aaf1 100755 GIT binary patch literal 748 zcmYk4t5O3&5Jhii!y}yu1QH0(RPhiIi3tRi*eC>r;vvxlg$jlrpb9W3G!BOY1pEOX z0DJ^G+@9@P*sY$OKGS`-r)QcupbTJ{X3s|)1#=L->C=i6LETxN_^oJb#&T$JrnL8r zC8*Lj-D}Tncy5jC$64E1o$R*4fqt8Mw!BFjdEA!AHOLCA*W$LFHR&DO&mv1}Y16BC zT*u>f$X-3J!+&(?n;m*w*W-4{hE^o~F5|4WecDCF60QITw4*FHAK8HH#97bv&d5GJ zx9591C;Q4+sy8J2&R9Oyc1iZ^dVSZsB74bL+N&F~(|CY_K9ZPwM>hD!tmb>#TEc-^ zd#?9DZ#`qF-XqyY#?tzp$aDr0?BFHXQHxGP%lrtGiNTIgbd1K!6YNk#l{0I*Fjr@x z6=)T4-I^@H+G;sE4xNBbLJQC$G(aR+-y~S4Bv@}mYtRMgB6Nw)Jh5$Cf^AfE1-c65 QB{hN4-=_qff{aF^U)|*|`2YX_ literal 402 zcmV;D0d4*tECK)k5N`bd8vy+O(?0qE02Ma;0cHO!L-+e0<7NK@{KE@EX8*q}jP?rr zeZ*(~0{|2N+eH15CH^QS{ukK%^=1Du0%iZuZ2yxJX8!_?>?Hgx|Hb|@#s0v&{|N#% zWdAf}|17TjTNGyhr27E8|7{b+{x!w^HZ1(V#r{C-<28Mv2Xm}CH{>)CHZyyHQ;Ceg#CLq{NZQ+ z1jl57{Jl1X{YHiTj@JCXXa9u#lDPbB8)pAG1H_m~aA5xm*m0ixAp;v^|4@bfsB8Qp zXa9u#udMtdXa6Mr4YuO&5%mQOI`H}rs_+2{5A6{U`C{M-#|$Cr@$&^*{2A^HTJwnZ z3ibj@^!5zI3KIPg-3nOp;M(+%`bzbN%nAnm2N3-T0{sd745SJS>LBAn_6Yj2_WA-M w_ty;j3K;z%8T}eY^A_m>3HpK;@Ej279i;u)>c0)b$o~oa3JwtY{{;U400aftQvd(} diff --git a/system/maps/pc-nte/map_machine02_00_01.evt b/system/maps/pc-nte/map_machine02_00_01.evt index b7acb2adb73e993d08368c65b9aec7875b1777ae..d6b99a3d1f44392fb3627aec0f55f89351f3de8c 100755 GIT binary patch literal 748 zcmZ9K&n`nz5XQf`x0LBArA1Oj5DSY&qPmc@Vb!&;Dhms|gT$Ye7Z3{z3%rA^KM8N( z0eA^4`R3lqS-3af$;|J}%)K+`Y$iYjK!dX9JzE765We}-%nZRS$u5z(uO-_vmY_qc zNWJ}IWwI~NU1hA9HR`I$h2E;?*6Dr6xHPLbOZMxmm3;@8Bm42(7ISTTi5g}PR&R}Z zptVfx*@rTg;E>h|RrR;d^4xne33d}C*x8Ggp(AwcfJF-s33gth zQ-pRRqDsqVF2SZOItr~o$DrfT8kE;gg3VBZO-+K$NmQT-bO1UC9fA&X!v-V4Mj={< ZHlQ<5-q2uOi&m*g785N(OV9xM{R0x{F=qe( literal 408 zcmV;J0cZXnKmq^&5N`bd902_P(?0qE02Ma;0cHO!L-+e0%=}vRu>ZpgX8#bsJY)6; z{KR8m{|*iO^&2JrGKKvV*Zh+cX8*+g8rb~y$NmEh?7Yzb|7HI(46OgLAN=9*8zKzHJB>tQH z+aUvF|4=3VpIH1NXa8eF{Gj|JO=tfk{-N>(46^U~4+VXLKffoxba?U;hJJk1J6S-$E$qs|1yzVQqAHj~~eSS@Lt*p`G zs@mr_)T22b=z81(t?`z69IS%VzoQ0GJL=nevfaqlu|ofWdLFC+_2MJ-B3Kiv^n281 zxnI9Li)Q(-=FxL<0Z-5N`bdDFFQc$v*l402H?T0cHQwW&e#8W&aBN87<=)`yOTg4)^@S z3ugZU06Ylxyiorx$Nq?ygp{!x^7Uo^jWQ+v9Q@7yW&bnO5dR}L{Pz=P{{nN^Tmm6x z|FeRo_A2re9%lap09s@AF#HJ{#r`zq{5{+JTgCn~1^Yu}G{^n}j@U~4&1J{_1lX}q z{AOqWIR)%iHvAJM{xwqkpj|Bdzs3GQ?Bi~X_GA2oz-RxQWLWlTBKILD{+v-(_G%*Z z$Nq?%M)t9A{K{wl#|8j+_IB9(6({}&fK>K1cjEHL{s_l7ef$<4$NmWbd1Ll|{M{SJ z{tCnNgxmbyXa5Uh@`~8}-)H{}9EgzH{NQK*4a4-7*!)@L{5;0R^q~BE%@=0>0-Uj? z{CsEsjROPjto$`vC;mk8t@iwd$NmKXEg1H*+x&)S{|2M;x!C-NXa5JP!ROpP{sah0_h@W|D^2ZvUFwtTE+fx=lnAxZD#+3{q5xK z3J5+5Plo{t8vG1G%IEeG2=5a40QeK53JmL_=}__vs_KC3ISMdJ3J65~3FZM(>+M?M z3JK*5D(tBGW@!Hla_u1c;_V8+>hAij%AEhx%>QcfAo|+z2>MF$`qH}cbP662{T~JJ zdh_~G>hsa^^bq=D^@RG;_16v%+7A-_5W(Z{%?cI(@AN|o82uRt{Tuy>9LB%|=Arlh HApZaWOJ71< diff --git a/system/maps/pc-nte/map_machine02_01_01.evt b/system/maps/pc-nte/map_machine02_01_01.evt index 97dcb480ce41664d6c4a8e64a8969738532951d8..70b3179562da3a6c50257f67eda893a9c20fc9fa 100755 GIT binary patch literal 980 zcmX|=Jxo+l5QWdYx4*NO-(^`8W1_@_1`61%+%2RqQlW)~P#LkcvJjzF6DkWUE2*fc zsEGws7HVQ`sOcrpt!FNid7E?Jo$sFevv=nGD}YG=4=8*7aR+See-j*`?22@wxM4BWJ5*L&l5LRhtdtRj&7jy=Ce;Scz@M zJ&MD8%vZrP_WBmtMwwOdn7xNo5v+!Hj31dJ=GO6=@fy_%UxO_hmfdF~Q9O{R0uTM$WBu#&7qMJJ$>eAS^1XcMX&u>zE2B}cPWlq9Pm238_`Sp`V4oJ+DS zOR~I*3bcTh&?>Y(?0qE02Q|U0cHQ)W&e#9W&a2K87<=)`yOTg2p9ar z3ugaft337&Z2ZJ${{xQf6#Q-VCH^uc{u^xk|7HI(jQl^M<_DTE|M#ugNAc+4@{LL0;{|gkcRs0uc|2YhZTlV}IXa5ZVS{U|W z+x!XT{4_=PbR^Wp{y^;GEsXYh{B0A({x!w^ifsJ9#r`#8CG?Q|SsTUvJjMQ$H~f1S zX8!`mOq%?B7H9thVe?}Dp!`~MC;kM)p=|tx$NmM5#HajihG+iDyWdBfw{mz^GB4_^s zW9-lTBXei}1MJWo{0bPt{TcEB41#*<`s(V30SXW75D58BLkbA}35fj)=7Rd``fBVb z({k;I`r_^L6${M&^6v!tV(|Jw((w91@qoIP3J`q?!j1}03I!1T28HLk@#hf`^AaHa zuL`vC3W?ActGTQOe+Y-DWJSQYHYSZu5gc2h(H9ou}%PRbdEb?m{f%U@JB$s2bzOlB=cLMfiW81dw5bQm$$ggn@wjai({eCaNK7ubpyU>@2 z&%m-imn*QZz{Whg1^W&xN2kyu#G|dzwfyeD`g(ZKUEA+H)Kl<9=plMQIK4zHGsI4> z2}^-w8lNnp4yb3%k|uEwdZ5fs&x@U&5KEDyL{cW1C8?0ip+k>^Wu9aKLhH^>E6p-N wGD$L}Q)v0vX~7`09_+LN>~zoUbeAkul0}jw5?p4d8)9L-KoXH;Ao9e&f0!OO-~a#s literal 430 zcmV;f0a5-RXaWEL5N`bd9svCR(?0qE02Oxp0cHO`2q9+w(=EpK2K+4yJ z|1`z^EV%qz6lVV>#QrjHbI1MzOfzi!Wyk&mj@UW;&1Pr+1?;gu{1YYqjWy-`Qv7Yd z#r`$r{8()KS;hW5Bs&gS{U|;oBZl${{~~QkNoR%Xa5K6 zmF)cNXa5KQ#du=!{Jl2i{6+=)sMSmxX8(lvuGsv($NmEx*t>iD4BG7h3KA&%49f02 z_7DIH5d!&oMEoD?0iy8sMnnn>^MiWv0r`URAo}w1K<42J82Abm0{s^KjAaTK=R#`p z=NX9e8uNle^!l>&5bX^@?GF7961@ucMEZL6^Ff05`ttW6`V1iY8i3F}3LzZ*9hm)@ Y=?tJR?E(7jVG2PC1q|~E{0092045IT`v3p{ diff --git a/system/maps/pc-nte/map_machine02_02_01.evt b/system/maps/pc-nte/map_machine02_02_01.evt index f6dbb16b023c57d0c9f0e4fc5dcae11cf5439d17..b532ae531162b99297051a06c4e91c2f5c11fea7 100755 GIT binary patch literal 784 zcmX||pVcMxhrHtXDw%IWPi@K$dqx5`l)eXyv04*oNZ9QktI3n znuXsr@}*eiY}+C`P@Y@NeRjq|WTl+#kR_3oF<`t$rIC&0Y@cl0_vrYUkH}8k*YO&h zk)1}Cr*SjUVn&ZH@$Mp_4 zyQj|G*LUBL?Av{P-}jMh5PhSXK9RjT+x6UMTd%=^@h)@MIeVd$?J7WY3OWr5b}b~> zMG&n*C;3;VcyoevQ-bwRBv^YSSVJUO3nW<4MH6TW9cOICm0;x*oq^V%8FY@wDkoZp n&RVSV&;?Sf6W6seh*qEzWcID1i_ivi3EDJ+eS>HbT7vuoU)3+> literal 426 zcmV;b0agAVR00415N`bd9RU3Q$v*l402H?T0cHQwW&e#8W&a2K87<=)`yOTg4j25x z3ugZ!t338Vy#K@_Xa58I#Qz%nkv1j%C?)lAJ{B2{!{yoM1 zn{52w#r{2$Mf{;$$Nt7-^{)KAHf8@tCH}7!up4IoV&f?GwJiJ!iSius0SXX8`4RaH zK}zrXy6*_>4(%}y`3GA3G42eK@i6)V@&O3>3iJAcdh`0~^C0?K^g!2t3K$p)2?g*P z_4*--_39Ep?a2xV^$J5E?K1X+?G^JDH1G--MEx1+QNs49%J+cdfeMig>>w2L8o={N U3LKFA9SGk%LhJ?d1`zoG07bLqi~s-t diff --git a/tests/GC-Episode2PrivateDrops2P.test.txt b/tests/GC-Episode2PrivateDrops2P.test.txt index 4330b23c..0b5f2832 100644 --- a/tests/GC-Episode2PrivateDrops2P.test.txt +++ b/tests/GC-Episode2PrivateDrops2P.test.txt @@ -3185,7 +3185,7 @@ I 94381 2023-12-29 15:36:50 - [Commands] Received from C-2 (Jess) (version=GC_V3 01F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0200 | 00 00 00 00 00 00 00 00 | I 94381 2023-12-29 15:36:50 - [Commands] Sending to C-5 (Jonah) (version=GC_V3 command=62 flag=01) -0000 | 62 01 08 02 6F 81 88 98 00 12 CB C0 00 08 00 00 | b o +0000 | 62 01 08 02 6F 81 00 00 00 12 CB C0 00 08 00 00 | b o 0010 | 00 20 00 10 0A 20 00 00 00 00 00 00 00 00 00 00 | 0020 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0030 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -12489,7 +12489,7 @@ I 94381 2023-12-29 15:42:19 - [Commands] Received from C-2 (Jess) (version=GC_V3 01F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0200 | 00 00 00 00 00 00 00 00 | I 94381 2023-12-29 15:42:19 - [Commands] Sending to C-5 (Jonah) (version=GC_V3 command=62 flag=01) -0000 | 62 01 08 02 6F 81 88 98 00 12 CB C0 00 08 00 00 | b o +0000 | 62 01 08 02 6F 81 00 00 00 12 CB C0 00 08 00 00 | b o 0010 | 00 30 00 10 0A 20 00 00 00 00 00 00 00 00 00 00 | 0 0020 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0030 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | diff --git a/tests/GC-ForestGame.test.txt b/tests/GC-ForestGame.test.txt index a87b22b9..de566705 100644 --- a/tests/GC-ForestGame.test.txt +++ b/tests/GC-ForestGame.test.txt @@ -614,6 +614,11 @@ I 49108 2023-05-26 16:18:25 - [Commands] Received from C-2 (Jess) (version=GC co 0010 | 00 00 00 00 | I 49108 2023-05-26 16:18:25 - [Commands] Received from C-2 (Jess) (version=GC command=60 flag=00) 0000 | 60 00 10 00 52 03 00 00 00 00 00 00 00 80 00 00 | ` R +I 49108 2023-05-26 16:18:25 - [Commands] Received from C-2 (Jess) (version=GC command=60 flag=00) +0000 | 06 00 20 00 00 00 00 00 00 00 00 00 09 45 24 76 | E$v +0010 | 61 72 69 61 74 69 6F 6E 73 20 30 30 30 31 30 34 | ariations 000104 +0020 | 32 31 30 31 32 30 32 31 31 30 32 30 32 31 32 31 | 2101202110202121 +0030 | 30 30 30 30 30 30 30 30 30 30 00 00 | 0000000000 I 49108 2023-05-26 16:18:32 - [Commands] Received from C-2 (Jess) (version=GC command=C1 flag=03) 0000 | C1 03 30 00 00 00 00 00 00 00 00 00 09 45 31 31 | 0 E11 0010 | 31 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 11 diff --git a/tests/GC-PoisonRoom.test.txt b/tests/GC-PoisonRoom.test.txt index 2a819480..3a604df9 100644 --- a/tests/GC-PoisonRoom.test.txt +++ b/tests/GC-PoisonRoom.test.txt @@ -25658,7 +25658,7 @@ I 56327 2024-03-03 23:56:43 - [Commands] Received from C-2 (Jess) (version=GC_V3 01F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0200 | 00 00 00 00 00 00 00 00 | I 56327 2024-03-03 23:56:43 - [Commands] Sending to C-5 (NO DATA ) (version=GC_V3 command=62 flag=01) -0000 | 62 01 08 02 6F 81 88 98 00 12 CB C0 00 08 00 00 | b o +0000 | 62 01 08 02 6F 81 00 00 00 12 CB C0 00 08 00 00 | b o 0010 | 00 32 00 10 0A 20 00 00 00 00 00 00 00 00 00 00 | 2 0020 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0030 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | diff --git a/tests/PC-DCv1-CrossplayPrivateDrops.test.txt b/tests/PC-DCv1-CrossplayPrivateDrops.test.txt index c457ffa4..e62732f5 100644 --- a/tests/PC-DCv1-CrossplayPrivateDrops.test.txt +++ b/tests/PC-DCv1-CrossplayPrivateDrops.test.txt @@ -2822,7 +2822,7 @@ I 97037 2023-12-29 15:57:06 - [Commands] Received from C-3 (Tali) (version=PC_V2 01F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0200 | 00 00 00 00 00 00 00 00 | I 97037 2023-12-29 15:57:06 - [Commands] Sending to C-5 (88888888) (version=DC_V1 command=62 flag=01) -0000 | 62 01 08 02 6F 81 00 00 00 00 00 00 00 00 00 00 | b o +0000 | 62 01 88 01 6F 61 00 00 00 00 00 00 00 00 00 00 | b o 0010 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0020 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0030 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -2846,15 +2846,7 @@ I 97037 2023-12-29 15:57:06 - [Commands] Sending to C-5 (88888888) (version=DC_V 0150 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0160 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0170 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | -0180 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | -0190 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | -01A0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | -01B0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | -01C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | -01D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | -01E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | -01F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | -0200 | 00 00 00 00 00 00 00 00 | +0180 | 00 00 00 00 00 00 00 00 | I 97037 2023-12-29 15:57:06 - [Commands] Received from C-3 (Tali) (version=PC_V2 command=62 flag=01) 0000 | 08 00 62 01 71 01 00 00 | b q I 97037 2023-12-29 15:57:06 - [Commands] Sending to C-5 (88888888) (version=DC_V1 command=62 flag=01) @@ -7036,7 +7028,7 @@ I 97037 2023-12-29 15:59:36 - [Commands] Sending to C-3 (Tali) (version=PC_V2 co 1910 | B0 FD B0 B1 FD B0 B2 FD B0 B3 FD B0 55 B4 FD B0 | U 1920 | B5 FD B0 B6 FD B0 B7 FD B0 55 B8 FD B0 B9 FD B0 | U 1930 | BA FD B0 BB FD B0 55 BC FD B0 BD FD B0 BE FD B0 | U -1940 | BF FD B0 03 C0 08 5F 73 | _s +1940 | BF FD B0 03 C0 08 00 00 | I 97037 2023-12-29 15:59:37 - [Commands] Received from C-5 (88888888) (version=DC_V1 command=6D flag=00) 0000 | 6D 00 B4 03 6E 00 00 00 B0 03 00 00 BA 1D 00 00 | m n 0010 | 9F 03 00 00 FF BA 1D 0A 19 B0 02 00 02 FD 10 EB | @@ -7186,7 +7178,7 @@ I 97037 2023-12-29 15:59:37 - [Commands] Received from C-5 (88888888) (version=D 0170 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0180 | 00 00 00 00 00 00 00 00 | I 97037 2023-12-29 15:59:37 - [Commands] Sending to C-3 (Tali) (version=PC_V2 command=62 flag=00) -0000 | 88 01 62 00 6F 61 00 00 00 00 08 00 00 00 00 00 | b oa +0000 | 08 02 62 00 6F 81 00 00 00 00 08 00 00 00 00 00 | b oa 0010 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0020 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0030 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | @@ -7210,7 +7202,15 @@ I 97037 2023-12-29 15:59:37 - [Commands] Sending to C-3 (Tali) (version=PC_V2 co 0150 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0160 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 0170 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | -0180 | 00 00 00 00 00 00 00 00 | +0180 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0190 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +01A0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +01B0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +01C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +01D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +01E0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +01F0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | +0200 | 00 00 00 00 00 00 00 00 | I 97037 2023-12-29 15:59:37 - [Commands] Received from C-5 (88888888) (version=DC_V1 command=62 flag=00) 0000 | 62 00 08 00 71 01 00 00 | b q I 97037 2023-12-29 15:59:37 - [Commands] Sending to C-3 (Tali) (version=PC_V2 command=62 flag=00)