Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e2d76f77be | |||
| 0b80af3f41 | |||
| f65acda803 | |||
| 53f485b8f2 | |||
| 69f40f9157 | |||
| 84bb946e05 | |||
| eb132f38d2 | |||
| 0f1fbb1069 | |||
| c9f7ca2259 | |||
| 8594e5af3c | |||
| 6b5e657630 | |||
| a7845e4b0e |
@@ -385,7 +385,7 @@ There are many options available when starting a proxy session. All options are
|
||||
* **Block pings**: blocks automatic pings sent by the client, and responds to ping commands from the server automatically. This works around a bug in Sylverant's login server.
|
||||
* **Infinite HP**: automatically heals you whenever you get hit. An attack that kills you in one hit will still kill you, however.
|
||||
* **Infinite TP**: automatically restores your TP whenever you use any technique.
|
||||
* **Switch assist**: attempts to unlock doors that require two players in a one-player game.
|
||||
* **Switch assist**: attempts to unlock doors that require two or four players in a one-player game.
|
||||
* **Infinite Meseta** (Episode 3 only): gives you 1,000,000 Meseta, regardless of the value sent by the remote server.
|
||||
* **Block events**: disables holiday events sent by the remote server.
|
||||
* **Block patches**: prevents any B2 (patch) commands from reaching the client.
|
||||
@@ -449,7 +449,7 @@ Some commands only work on the game server and not on the proxy server. The chat
|
||||
* `$secid <section-id>`: Sets your override section ID. After running this command, any games you create will use your override section ID for rare drops instead of your character's actual section ID. If you're in a game and you are the leader of the game, this also immediately changes the item tables used by the server when creating items. To revert to your actual section id, run `$secid` with no name after it. On the proxy server, this will not work if the remote server controls item drops (e.g. on BB, or on Schtserv with server drops enabled). If the server does not allow cheat mode anywhere (that is, "CheatModeBehavior" is "Off" in config.json), this command does nothing.
|
||||
* `$rand <seed>`: Sets your override random seed (specified as a 32-bit hex value). This will make any games you create use the given seed for rare enemies. This also makes item drops deterministic in Blue Burst games hosted by newserv. On the proxy server, this command can cause desyncs with other players in the same game, since they will not see the overridden random seed. To remove the override, run `$rand` with no arguments. If the server does not allow cheat mode anywhere (that is, "CheatModeBehavior" is "Off" in config.json), this command does nothing.
|
||||
* `$ln [name-or-type]`: Sets the lobby number. Visible only to you. This command exists because some non-lobby maps can be loaded as lobbies with invalid lobby numbers. See the "GC lobby types" and "Ep3 lobby types" entries in the information menu for acceptable values here. Note that non-lobby maps do not have a lobby counter, so there's no way to exit the lobby without using either `$ln` again or `$exit`. On the game server, `$ln` reloads the lobby immediately; on the proxy server, it doesn't take effect until you load another lobby yourself (which means you'll like have to use `$exit` to escape). Run this command with no argument to return to the default lobby.
|
||||
* `$swa`: Enables or disables switch assist. When enabled, the server will attempt to automatically unlock two-player doors in non-quest games if you step on both switches sequentially.
|
||||
* `$swa`: Enables or disables switch assist. When enabled, the server will attempt to automatically unlock two-player and four-player doors in non-quest games if you step on all the required switches sequentially.
|
||||
* `$exit`: If you're in a lobby, sends you to the main menu (which ends your proxy session, if you're in one). If you're in a game or spectator team, sends you to the lobby (but does not end your proxy session if you're in one). Does nothing if you're in a non-Episode 3 game and no quest is in progress.
|
||||
* `$patch <name>`: Run a patch on your client. `<name>` must exactly match the name of a patch on the server.
|
||||
|
||||
@@ -468,7 +468,7 @@ Some commands only work on the game server and not on the proxy server. The chat
|
||||
* `$minlevel <level>`: Sets the minimum level for players to join the current game.
|
||||
* `$password <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 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 states of enemies, objects, and switches will be saved, and items left on the floor will not be deleted (except items only visible to the leaving player). If the game is empty for too long (15 minutes by default), it is then deleted. There is an edge case with persistence: if the player defeats a boss, leaves the room, joins again, and returns to the boss arena, neither the boss nor the exit warp will spawn, so they will be stuck there and have to use $warp or Quit Game to get out. For this reason, `$warp 0` is allowed in boss arenas once the boss is defeated, even if cheat mode is disabled.
|
||||
|
||||
* 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.
|
||||
|
||||
@@ -1231,3 +1231,22 @@ F95D --------:???????? --------:???????? 006B93FC:???????? none
|
||||
F95E --------:???????? --------:???????? 006B941C:???????? LLL
|
||||
F95F --------:???????? --------:???????? 006B9104:???????? LLLLL
|
||||
F960 --------:???????? --------:???????? 006B915C:???????? L
|
||||
|
||||
Event action stream opcodes
|
||||
DC-NTE-------------- DCv1---------------- GC12US11------------ BB------------------
|
||||
00 => 8C14A304-?? 8C14A41C 8C165BA8-?? 8C165CC0 8020784C-() 80207760 0080CC8C-?? 0061CDB0 nop
|
||||
01 => 8C14A304-?? 8C14A420 8C165BA8-?? 8C165CC4 8020784C-() 80207754 0080CC8C-?? 0080CD40 stop
|
||||
02 => ----------- -------- ----------- -------- ----------- -------- ----------- --------
|
||||
03 => ----------- -------- ----------- -------- ----------- -------- ----------- --------
|
||||
04 => ----------- -------- ----------- -------- ----------- -------- ----------- --------
|
||||
05 => ----------- -------- ----------- -------- ----------- -------- ----------- --------
|
||||
06 => ----------- -------- ----------- -------- ----------- -------- ----------- --------
|
||||
07 => ----------- -------- ----------- -------- ----------- -------- ----------- --------
|
||||
08 => 8C14A364-?? 8C14A42C 8C165C08-?? 8C165CD0 802077AC-WW 80207734 0080CCB4-?? 0080CD4C construct_object section, group
|
||||
09 => 8C14A364-?? 8C14A438 8C165C08-?? 8C165CDC 802077AC-WW 80207714 0080CCB4-?? 0080CD64 construct_enemy section, wave_number
|
||||
0A => 8C14A318-?? 8C14A444 8C165BBC-?? 8C165CE8 80207804-W 80207694 0080CC9C-?? 0080CD7C send_room_unlock? id // sends 6x05 with flags=1 or flags=3
|
||||
0B => 8C14A318-?? 8C14A4A0 8C165BBC-?? 8C165D44 80207804-W 80207634 0080CC9C-?? 0080CDDC send_room_unlock? id // sends 6x05 with flags=0
|
||||
0C => 8C14A3D4-?? 8C14A4E8 8C165C78-?? 8C165D8C 80207764-L 802075C8 0080CD00-?? 0080CE24 trigger_event event_id // sends 6x67
|
||||
0D => 8C14A364-?? 8C14A53C 8C165C08-?? 8C165DE0 802077AC-WW 802075A0 0080CCB4-?? 0080CE74 construct_enemy_stop section, wave_number // equivalent to construct_enemy then stop
|
||||
0E => ----------- -------- ----------- -------- ----------- -------- ----------- --------
|
||||
0F => ----------- -------- ----------- -------- ----------- -------- ----------- --------
|
||||
|
||||
+23
-8
@@ -597,8 +597,7 @@ static void server_command_patch(shared_ptr<Client> c, const std::string& args)
|
||||
auto s = c->require_server_state();
|
||||
// Note: We can't look this up outside of the closure because
|
||||
// c->specific_version can change during prepare_client_for_patches
|
||||
auto fn = s->function_code_index->name_and_specific_version_to_patch_function.at(
|
||||
string_printf("%s-%08" PRIX32, args.c_str(), c->config.specific_version));
|
||||
auto fn = s->function_code_index->get_patch(args, c->config.specific_version);
|
||||
send_function_call(c, fn);
|
||||
c->function_call_response_queue.emplace_back(empty_function_call_response_handler);
|
||||
} catch (const out_of_range&) {
|
||||
@@ -617,8 +616,7 @@ static void proxy_command_patch(shared_ptr<ProxyServer::LinkedSession> ses, cons
|
||||
ses->log.info("Version detected as %08" PRIX32, ses->config.specific_version);
|
||||
}
|
||||
auto s = ses->require_server_state();
|
||||
auto fn = s->function_code_index->name_and_specific_version_to_patch_function.at(
|
||||
string_printf("%s-%08" PRIX32, args.c_str(), ses->config.specific_version));
|
||||
auto fn = s->function_code_index->get_patch(args, ses->config.specific_version);
|
||||
send_function_call(ses->client_channel, ses->config, fn);
|
||||
// Don't forward the patch response to the server
|
||||
ses->function_call_return_handler_queue.emplace_back(empty_patch_return_handler);
|
||||
@@ -684,6 +682,7 @@ static void server_command_exit(shared_ptr<Client> c, const std::string&) {
|
||||
G_UnusedHeader cmd = {0x73, 0x01, 0x0000};
|
||||
c->channel.send(0x60, 0x00, cmd);
|
||||
c->floor = 0;
|
||||
c->recent_switch_flags.clear();
|
||||
} else if (is_ep3(c->version())) {
|
||||
c->channel.send(0xED, 0x00);
|
||||
} else {
|
||||
@@ -1473,18 +1472,34 @@ static void server_command_warp(shared_ptr<Client> c, const std::string& args, b
|
||||
auto s = c->require_server_state();
|
||||
auto l = c->require_lobby();
|
||||
check_is_game(l, true);
|
||||
check_cheats_enabled(l, c);
|
||||
|
||||
uint32_t floor = stoul(args, nullptr, 0);
|
||||
if (c->floor == floor) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case: $warp[me] 0 is allowed in boss arenas if the boss is already
|
||||
// defeated, even if cheats are disabled. This is because if a player returns
|
||||
// to a boss arena after a persistence gap in the game, the exit warp won't
|
||||
// exist, so they need a way to get out.
|
||||
bool should_check_cheats = is_warpall || (floor != 0) || !floor_is_boss_arena(l->episode, c->floor);
|
||||
if (!should_check_cheats) {
|
||||
for (const auto* event : l->map->get_events(c->floor)) {
|
||||
if (!(event->flags & 0x18)) {
|
||||
should_check_cheats = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (should_check_cheats) {
|
||||
check_cheats_enabled(l, c);
|
||||
}
|
||||
|
||||
size_t limit = floor_limit_for_episode(l->episode);
|
||||
if (limit == 0) {
|
||||
return;
|
||||
} else if (floor > limit) {
|
||||
send_text_message_printf(c, "$C6Area numbers must\nbe %zu or less.", limit);
|
||||
send_text_message_printf(c, "$C6Area numbers must\nbe %zu or less", limit);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1638,7 +1653,7 @@ static void server_command_infinite_hp(shared_ptr<Client> c, const std::string&)
|
||||
c->config.toggle_flag(Client::Flag::INFINITE_HP_ENABLED);
|
||||
bool enabled = c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED);
|
||||
send_text_message_printf(c, "$C6Infinite HP %s", enabled ? "enabled" : "disabled");
|
||||
if (enabled && l->is_game() && is_v1_or_v2(c->version())) {
|
||||
if (enabled && l->is_game()) {
|
||||
send_remove_conditions(c);
|
||||
}
|
||||
}
|
||||
@@ -1649,7 +1664,7 @@ static void proxy_command_infinite_hp(shared_ptr<ProxyServer::LinkedSession> ses
|
||||
ses->config.toggle_flag(Client::Flag::INFINITE_HP_ENABLED);
|
||||
bool enabled = ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED);
|
||||
send_text_message_printf(ses->client_channel, "$C6Infinite HP %s", enabled ? "enabled" : "disabled");
|
||||
if (enabled && ses->is_in_game && is_v1_or_v2(ses->version())) {
|
||||
if (enabled && ses->is_in_game) {
|
||||
send_remove_conditions(ses->client_channel, ses->lobby_client_id);
|
||||
send_remove_conditions(ses->server_channel, ses->lobby_client_id);
|
||||
}
|
||||
|
||||
@@ -215,7 +215,6 @@ Client::Client(
|
||||
}
|
||||
this->config.specific_version = default_specific_version_for_version(version, -1);
|
||||
|
||||
this->last_switch_enabled_command.header.subcommand = 0;
|
||||
memset(&this->next_connection_addr, 0, sizeof(this->next_connection_addr));
|
||||
|
||||
this->reschedule_save_game_data_event();
|
||||
|
||||
+1
-1
@@ -250,7 +250,7 @@ public:
|
||||
|
||||
// Miscellaneous (used by chat commands)
|
||||
uint32_t next_exp_value; // next EXP value to give
|
||||
G_SwitchStateChanged_6x05 last_switch_enabled_command;
|
||||
RecentSwitchFlags recent_switch_flags; // used for switch assist
|
||||
bool can_chat;
|
||||
struct PendingCharacterExport {
|
||||
std::shared_ptr<const License> license;
|
||||
|
||||
+30
-23
@@ -3828,21 +3828,28 @@ struct G_ExtendedHeader {
|
||||
struct G_Unknown_6x04 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t unknown_a1 = 0;
|
||||
le_uint16_t unused = 0;
|
||||
le_uint16_t unknown_a2 = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x05: Switch state changed
|
||||
// Some things that don't look like switches are implemented as switches using
|
||||
// this subcommand. For example, when all enemies in a room are defeated, this
|
||||
// subcommand is used to unlock the doors.
|
||||
// Note: In the client, this is a subclass of 6x04, similar to how 6xA2 is a
|
||||
// subclass of 6x60.
|
||||
|
||||
struct G_SwitchStateChanged_6x05 {
|
||||
// Note: header.object_id is 0xFFFF for room clear when all enemies defeated
|
||||
G_ObjectIDHeader header;
|
||||
parray<uint8_t, 2> unknown_a1;
|
||||
// TODO: Some of these might be big-endian on GC; it only byteswaps
|
||||
// unknown_a3. Are the others actually uint16, or are they uint8[2]?
|
||||
le_uint16_t unknown_a1 = 0;
|
||||
le_uint16_t unknown_a2 = 0;
|
||||
parray<uint8_t, 2> unknown_a3;
|
||||
uint8_t floor = 0;
|
||||
le_uint16_t switch_flag_num = 0;
|
||||
uint8_t switch_flag_floor = 0;
|
||||
// Only two bits in flags have meanings:
|
||||
// 01 - set unlock flag (if not set, the flag is cleared instead)
|
||||
// 02 - play room unlock sound if floor matches client's floor
|
||||
uint8_t flags = 0; // Bit field, with 2 lowest bits having meaning
|
||||
} __packed__;
|
||||
|
||||
@@ -4422,15 +4429,15 @@ struct G_PlayerDied_6x4D {
|
||||
le_uint32_t unknown_a1 = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x4E: Player died (protected on V3/V4)
|
||||
// 6x4E: Player is dead can be revived (protected on V3/V4)
|
||||
|
||||
struct G_PlayerDied_6x4E {
|
||||
struct G_PlayerRevivable_6x4E {
|
||||
G_ClientIDHeader header;
|
||||
} __packed__;
|
||||
|
||||
// 6x4F: Player resurrected (via Scape Doll) (protected on V3/V4)
|
||||
// 6x4F: Player revived (protected on V3/V4)
|
||||
|
||||
struct G_PlayerUsedScapeDoll_6x4F {
|
||||
struct G_PlayerRevived_6x4F {
|
||||
G_ClientIDHeader header;
|
||||
} __packed__;
|
||||
|
||||
@@ -4635,7 +4642,7 @@ struct G_TriggerSetEvent_6x67 {
|
||||
G_UnusedHeader header;
|
||||
le_uint32_t floor = 0;
|
||||
le_uint32_t event_id = 0; // NOT event index
|
||||
le_uint32_t unused = 0;
|
||||
le_uint32_t client_id = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x68: Create telepipe / cast Ryuker
|
||||
@@ -4754,11 +4761,11 @@ struct G_SyncSetFlagState_6x6E_Decompressed {
|
||||
le_uint16_t total_size = 0; // == sum of the following 3 fields
|
||||
le_uint16_t entity_set_flags_size = 0;
|
||||
le_uint16_t event_set_flags_size = 0;
|
||||
le_uint16_t unused_size = 0;
|
||||
le_uint16_t switch_flags_size = 0;
|
||||
// Variable-length fields follow here:
|
||||
// EntitySetFlags entity_set_flags; // Total size is set_flags_size
|
||||
// le_uint16_t event_set_flags[event_set_flags_size / 2]; // Same order as in map files (NOT sorted by event_id)
|
||||
// uint8_t unused[is_v1 ? 0x200 : 0x240]; // Possibly an early implementation of 6x6F; unused even in DC NTE
|
||||
// SwitchFlags switch_flags; // 0x200 bytes on v1 abd earlier; 0x240 bytes on v2 and later
|
||||
|
||||
struct EntitySetFlags {
|
||||
le_uint32_t object_set_flags_offset = 0;
|
||||
@@ -4993,11 +5000,11 @@ struct G_UpdateQuestFlag_V3_BB_6x75 : G_UpdateQuestFlag_DC_PC_6x75 {
|
||||
le_uint16_t unused = 0;
|
||||
} __packed__;
|
||||
|
||||
// 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.
|
||||
// 6x76: Set entity set flags
|
||||
// This command can only be used to set set flags, since the game performs a
|
||||
// bitwise OR operation instead of a simple assignment.
|
||||
|
||||
struct G_SetEntityFlags_6x76 {
|
||||
struct G_SetEntitySetFlags_6x76 {
|
||||
G_EnemyIDHeader header; // 1000-3FFF = enemy, 4000-FFFF = object
|
||||
le_uint16_t floor = 0;
|
||||
le_uint16_t flags = 0;
|
||||
@@ -5235,8 +5242,9 @@ struct G_Unknown_6x91 {
|
||||
le_uint32_t unknown_a2 = 0;
|
||||
le_uint16_t unknown_a3 = 0;
|
||||
le_uint16_t unknown_a4 = 0;
|
||||
le_uint16_t unknown_a5 = 0;
|
||||
parray<uint8_t, 2> unknown_a6;
|
||||
le_uint16_t switch_flag_num = 0;
|
||||
uint8_t should_set = 0; // The switch flag is only set if this is equal to 1; otherwise it's cleared
|
||||
uint8_t switch_flag_floor = 0;
|
||||
} __packed__;
|
||||
|
||||
// 6x92: Unknown (not valid on Episode 3)
|
||||
@@ -5251,9 +5259,9 @@ struct G_Unknown_6x92 {
|
||||
|
||||
struct G_ActivateTimedSwitch_6x93 {
|
||||
G_UnusedHeader header;
|
||||
le_uint16_t floor = 0;
|
||||
le_uint16_t switch_id = 0;
|
||||
uint8_t unknown_a1 = 0; // Logic is different if this is 1 vs. any other value
|
||||
le_uint16_t switch_flag_floor = 0;
|
||||
le_uint16_t switch_flag_num = 0;
|
||||
uint8_t should_set = 0; // The switch flag is only set if this is equal to 1; otherwise it's cleared
|
||||
parray<uint8_t, 3> unused;
|
||||
} __packed__;
|
||||
|
||||
@@ -5362,10 +5370,9 @@ struct G_GalGryphonBossActions_6xA0 {
|
||||
parray<le_uint32_t, 4> unknown_a4;
|
||||
} __packed__;
|
||||
|
||||
// 6xA1: Unknown (not valid on pre-V3) (protected on V3/V4)
|
||||
// Part of revive process. Occurs right after revive command; function unclear.
|
||||
// 6xA1: Revive player (not valid on pre-V3) (protected on V3/V4)
|
||||
|
||||
struct G_Unknown_6xA1 {
|
||||
struct G_RevivePlayer_V3_BB_6xA1 {
|
||||
G_ClientIDHeader header;
|
||||
} __packed__;
|
||||
|
||||
|
||||
+119
-108
@@ -38,6 +38,8 @@ const char* name_for_enum<EnemyType>(EnemyType type) {
|
||||
return "BOOTA";
|
||||
case EnemyType::BULCLAW:
|
||||
return "BULCLAW";
|
||||
case EnemyType::BULK:
|
||||
return "BULK";
|
||||
case EnemyType::CANADINE:
|
||||
return "CANADINE";
|
||||
case EnemyType::CANADINE_GROUP:
|
||||
@@ -287,6 +289,7 @@ EnemyType enum_for_name<EnemyType>(const char* name) {
|
||||
{"BOOMA", EnemyType::BOOMA},
|
||||
{"BOOTA", EnemyType::BOOTA},
|
||||
{"BULCLAW", EnemyType::BULCLAW},
|
||||
{"BULK", EnemyType::BULK},
|
||||
{"CANADINE", EnemyType::CANADINE},
|
||||
{"CANADINE_GROUP", EnemyType::CANADINE_GROUP},
|
||||
{"CANANE", EnemyType::CANANE},
|
||||
@@ -409,150 +412,152 @@ bool enemy_type_valid_for_episode(Episode episode, EnemyType enemy_type) {
|
||||
switch (episode) {
|
||||
case Episode::EP1:
|
||||
switch (enemy_type) {
|
||||
case EnemyType::MOTHMANT:
|
||||
case EnemyType::MONEST:
|
||||
case EnemyType::SAVAGE_WOLF:
|
||||
case EnemyType::BARBAROUS_WOLF:
|
||||
case EnemyType::POISON_LILY:
|
||||
case EnemyType::NAR_LILY:
|
||||
case EnemyType::SINOW_BEAT:
|
||||
case EnemyType::CANADINE:
|
||||
case EnemyType::CANADINE_GROUP:
|
||||
case EnemyType::CANANE:
|
||||
case EnemyType::CHAOS_SORCERER:
|
||||
case EnemyType::CHAOS_BRINGER:
|
||||
case EnemyType::DARK_BELRA:
|
||||
case EnemyType::DE_ROL_LE:
|
||||
case EnemyType::DRAGON:
|
||||
case EnemyType::SINOW_GOLD:
|
||||
case EnemyType::RAG_RAPPY:
|
||||
case EnemyType::AL_RAPPY:
|
||||
case EnemyType::NANO_DRAGON:
|
||||
case EnemyType::DUBCHIC:
|
||||
case EnemyType::GILLCHIC:
|
||||
case EnemyType::GARANZ:
|
||||
case EnemyType::DARK_GUNNER:
|
||||
case EnemyType::BARBAROUS_WOLF:
|
||||
case EnemyType::BOOMA:
|
||||
case EnemyType::BULCLAW:
|
||||
case EnemyType::BULK:
|
||||
case EnemyType::CANADINE_GROUP:
|
||||
case EnemyType::CANADINE:
|
||||
case EnemyType::CANANE:
|
||||
case EnemyType::CHAOS_BRINGER:
|
||||
case EnemyType::CHAOS_SORCERER:
|
||||
case EnemyType::CLAW:
|
||||
case EnemyType::VOL_OPT_2:
|
||||
case EnemyType::POUILLY_SLIME:
|
||||
case EnemyType::POFUILLY_SLIME:
|
||||
case EnemyType::PAN_ARMS:
|
||||
case EnemyType::HIDOOM:
|
||||
case EnemyType::MIGIUM:
|
||||
case EnemyType::DARVANT:
|
||||
case EnemyType::DARVANT_ULTIMATE:
|
||||
case EnemyType::DARK_BELRA:
|
||||
case EnemyType::DARK_FALZ_1:
|
||||
case EnemyType::DARK_FALZ_2:
|
||||
case EnemyType::DARK_FALZ_3:
|
||||
case EnemyType::HILDEBEAR:
|
||||
case EnemyType::HILDEBLUE:
|
||||
case EnemyType::BOOMA:
|
||||
case EnemyType::GOBOOMA:
|
||||
case EnemyType::GIGOBOOMA:
|
||||
case EnemyType::GRASS_ASSASSIN:
|
||||
case EnemyType::EVIL_SHARK:
|
||||
case EnemyType::PAL_SHARK:
|
||||
case EnemyType::GUIL_SHARK:
|
||||
case EnemyType::DARK_GUNNER:
|
||||
case EnemyType::DARVANT_ULTIMATE:
|
||||
case EnemyType::DARVANT:
|
||||
case EnemyType::DE_ROL_LE:
|
||||
case EnemyType::DEATH_GUNNER:
|
||||
case EnemyType::DELSABER:
|
||||
case EnemyType::DIMENIAN:
|
||||
case EnemyType::DRAGON:
|
||||
case EnemyType::DUBCHIC:
|
||||
case EnemyType::EVIL_SHARK:
|
||||
case EnemyType::GARANZ:
|
||||
case EnemyType::GIGOBOOMA:
|
||||
case EnemyType::GILLCHIC:
|
||||
case EnemyType::GOBOOMA:
|
||||
case EnemyType::GRASS_ASSASSIN:
|
||||
case EnemyType::GUIL_SHARK:
|
||||
case EnemyType::HIDOOM:
|
||||
case EnemyType::HILDEBEAR:
|
||||
case EnemyType::HILDEBLUE:
|
||||
case EnemyType::LA_DIMENIAN:
|
||||
case EnemyType::MIGIUM:
|
||||
case EnemyType::MONEST:
|
||||
case EnemyType::MOTHMANT:
|
||||
case EnemyType::NANO_DRAGON:
|
||||
case EnemyType::NAR_LILY:
|
||||
case EnemyType::PAL_SHARK:
|
||||
case EnemyType::PAN_ARMS:
|
||||
case EnemyType::POFUILLY_SLIME:
|
||||
case EnemyType::POISON_LILY:
|
||||
case EnemyType::POUILLY_SLIME:
|
||||
case EnemyType::RAG_RAPPY:
|
||||
case EnemyType::SAVAGE_WOLF:
|
||||
case EnemyType::SINOW_BEAT:
|
||||
case EnemyType::SINOW_GOLD:
|
||||
case EnemyType::SO_DIMENIAN:
|
||||
case EnemyType::VOL_OPT_2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case Episode::EP2:
|
||||
switch (enemy_type) {
|
||||
case EnemyType::MOTHMANT:
|
||||
case EnemyType::MONEST:
|
||||
case EnemyType::SAVAGE_WOLF:
|
||||
case EnemyType::BARBAROUS_WOLF:
|
||||
case EnemyType::POISON_LILY:
|
||||
case EnemyType::NAR_LILY:
|
||||
case EnemyType::SINOW_BERILL:
|
||||
case EnemyType::GEE:
|
||||
case EnemyType::CHAOS_SORCERER:
|
||||
case EnemyType::DELBITER:
|
||||
case EnemyType::DARK_BELRA:
|
||||
case EnemyType::BARBA_RAY:
|
||||
case EnemyType::GOL_DRAGON:
|
||||
case EnemyType::SINOW_SPIGELL:
|
||||
case EnemyType::RAG_RAPPY:
|
||||
case EnemyType::LOVE_RAPPY:
|
||||
case EnemyType::SAINT_RAPPY:
|
||||
case EnemyType::EGG_RAPPY:
|
||||
case EnemyType::HALLO_RAPPY:
|
||||
case EnemyType::GI_GUE:
|
||||
case EnemyType::DUBCHIC:
|
||||
case EnemyType::GILLCHIC:
|
||||
case EnemyType::GARANZ:
|
||||
case EnemyType::GAL_GRYPHON:
|
||||
case EnemyType::EPSILON:
|
||||
case EnemyType::BARBAROUS_WOLF:
|
||||
case EnemyType::CHAOS_SORCERER:
|
||||
case EnemyType::DARK_BELRA:
|
||||
case EnemyType::DEL_LILY:
|
||||
case EnemyType::ILL_GILL:
|
||||
case EnemyType::OLGA_FLOW_1:
|
||||
case EnemyType::OLGA_FLOW_2:
|
||||
case EnemyType::GAEL:
|
||||
case EnemyType::DELBITER:
|
||||
case EnemyType::DELDEPTH:
|
||||
case EnemyType::PAN_ARMS:
|
||||
case EnemyType::HIDOOM:
|
||||
case EnemyType::MIGIUM:
|
||||
case EnemyType::MERICAROL:
|
||||
case EnemyType::UL_GIBBON:
|
||||
case EnemyType::ZOL_GIBBON:
|
||||
case EnemyType::GIBBLES:
|
||||
case EnemyType::MORFOS:
|
||||
case EnemyType::RECOBOX:
|
||||
case EnemyType::RECON:
|
||||
case EnemyType::SINOW_ZOA:
|
||||
case EnemyType::SINOW_ZELE:
|
||||
case EnemyType::MERIKLE:
|
||||
case EnemyType::MERICUS:
|
||||
case EnemyType::HILDEBEAR:
|
||||
case EnemyType::HILDEBLUE:
|
||||
case EnemyType::MERILLIA:
|
||||
case EnemyType::MERILTAS:
|
||||
case EnemyType::GRASS_ASSASSIN:
|
||||
case EnemyType::DOLMOLM:
|
||||
case EnemyType::DOLMDARL:
|
||||
case EnemyType::DELSABER:
|
||||
case EnemyType::DIMENIAN:
|
||||
case EnemyType::DOLMDARL:
|
||||
case EnemyType::DOLMOLM:
|
||||
case EnemyType::DUBCHIC:
|
||||
case EnemyType::EGG_RAPPY:
|
||||
case EnemyType::EPSILON:
|
||||
case EnemyType::GAEL:
|
||||
case EnemyType::GAL_GRYPHON:
|
||||
case EnemyType::GARANZ:
|
||||
case EnemyType::GEE:
|
||||
case EnemyType::GI_GUE:
|
||||
case EnemyType::GIBBLES:
|
||||
case EnemyType::GILLCHIC:
|
||||
case EnemyType::GOL_DRAGON:
|
||||
case EnemyType::GRASS_ASSASSIN:
|
||||
case EnemyType::HALLO_RAPPY:
|
||||
case EnemyType::HIDOOM:
|
||||
case EnemyType::HILDEBEAR:
|
||||
case EnemyType::HILDEBLUE:
|
||||
case EnemyType::ILL_GILL:
|
||||
case EnemyType::LA_DIMENIAN:
|
||||
case EnemyType::LOVE_RAPPY:
|
||||
case EnemyType::MERICAROL:
|
||||
case EnemyType::MERICUS:
|
||||
case EnemyType::MERIKLE:
|
||||
case EnemyType::MERILLIA:
|
||||
case EnemyType::MERILTAS:
|
||||
case EnemyType::MIGIUM:
|
||||
case EnemyType::MONEST:
|
||||
case EnemyType::MORFOS:
|
||||
case EnemyType::MOTHMANT:
|
||||
case EnemyType::NAR_LILY:
|
||||
case EnemyType::OLGA_FLOW_1:
|
||||
case EnemyType::OLGA_FLOW_2:
|
||||
case EnemyType::PAN_ARMS:
|
||||
case EnemyType::POISON_LILY:
|
||||
case EnemyType::RAG_RAPPY:
|
||||
case EnemyType::RECOBOX:
|
||||
case EnemyType::RECON:
|
||||
case EnemyType::SAINT_RAPPY:
|
||||
case EnemyType::SAVAGE_WOLF:
|
||||
case EnemyType::SINOW_BERILL:
|
||||
case EnemyType::SINOW_SPIGELL:
|
||||
case EnemyType::SINOW_ZELE:
|
||||
case EnemyType::SINOW_ZOA:
|
||||
case EnemyType::SO_DIMENIAN:
|
||||
case EnemyType::UL_GIBBON:
|
||||
case EnemyType::ZOL_GIBBON:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case Episode::EP4:
|
||||
switch (enemy_type) {
|
||||
case EnemyType::BOOTA:
|
||||
case EnemyType::ZE_BOOTA:
|
||||
case EnemyType::BA_BOOTA:
|
||||
case EnemyType::SAND_RAPPY:
|
||||
case EnemyType::DEL_RAPPY:
|
||||
case EnemyType::ZU:
|
||||
case EnemyType::PAZUZU:
|
||||
case EnemyType::ASTARK:
|
||||
case EnemyType::SATELLITE_LIZARD:
|
||||
case EnemyType::YOWIE:
|
||||
case EnemyType::DORPHON:
|
||||
case EnemyType::DORPHON_ECLAIR:
|
||||
case EnemyType::GORAN:
|
||||
case EnemyType::PYRO_GORAN:
|
||||
case EnemyType::GORAN_DETONATOR:
|
||||
case EnemyType::SAND_RAPPY_ALT:
|
||||
case EnemyType::BA_BOOTA:
|
||||
case EnemyType::BOOTA:
|
||||
case EnemyType::DEL_RAPPY_ALT:
|
||||
case EnemyType::DEL_RAPPY:
|
||||
case EnemyType::DORPHON_ECLAIR:
|
||||
case EnemyType::DORPHON:
|
||||
case EnemyType::GIRTABLULU:
|
||||
case EnemyType::GORAN_DETONATOR:
|
||||
case EnemyType::GORAN:
|
||||
case EnemyType::KONDRIEU:
|
||||
case EnemyType::MERISSA_A:
|
||||
case EnemyType::MERISSA_AA:
|
||||
case EnemyType::ZU_ALT:
|
||||
case EnemyType::PAZUZU_ALT:
|
||||
case EnemyType::SATELLITE_LIZARD_ALT:
|
||||
case EnemyType::YOWIE_ALT:
|
||||
case EnemyType::GIRTABLULU:
|
||||
case EnemyType::PAZUZU:
|
||||
case EnemyType::PYRO_GORAN:
|
||||
case EnemyType::SAINT_MILLION:
|
||||
case EnemyType::SAND_RAPPY_ALT:
|
||||
case EnemyType::SAND_RAPPY:
|
||||
case EnemyType::SATELLITE_LIZARD_ALT:
|
||||
case EnemyType::SATELLITE_LIZARD:
|
||||
case EnemyType::SHAMBERTIN:
|
||||
case EnemyType::KONDRIEU:
|
||||
case EnemyType::YOWIE_ALT:
|
||||
case EnemyType::YOWIE:
|
||||
case EnemyType::ZE_BOOTA:
|
||||
case EnemyType::ZU_ALT:
|
||||
case EnemyType::ZU:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -611,8 +616,10 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
|
||||
case EnemyType::GARANZ:
|
||||
return 0x1D;
|
||||
case EnemyType::DARK_GUNNER:
|
||||
case EnemyType::DEATH_GUNNER:
|
||||
return 0x1E;
|
||||
case EnemyType::BULCLAW:
|
||||
case EnemyType::BULK:
|
||||
return 0x1F;
|
||||
case EnemyType::CLAW:
|
||||
return 0x20;
|
||||
@@ -865,6 +872,8 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
|
||||
return 0x09;
|
||||
case EnemyType::BOOTA:
|
||||
return 0x4D;
|
||||
case EnemyType::BULK:
|
||||
return 0x27;
|
||||
case EnemyType::BULCLAW:
|
||||
return 0x28;
|
||||
case EnemyType::CANADINE:
|
||||
@@ -886,6 +895,8 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
|
||||
return 0x2F;
|
||||
case EnemyType::DARK_GUNNER:
|
||||
return 0x22;
|
||||
case EnemyType::DEATH_GUNNER:
|
||||
return 0x23;
|
||||
case EnemyType::DE_ROL_LE:
|
||||
return 0x2D;
|
||||
case EnemyType::DEL_LILY:
|
||||
|
||||
@@ -20,6 +20,7 @@ enum class EnemyType {
|
||||
BOOMA,
|
||||
BOOTA,
|
||||
BULCLAW,
|
||||
BULK,
|
||||
CANADINE,
|
||||
CANADINE_GROUP,
|
||||
CANANE,
|
||||
|
||||
@@ -3150,20 +3150,35 @@ vector<shared_ptr<const Card>> CardSpecial::get_targeted_cards_for_condition(
|
||||
}
|
||||
break;
|
||||
case 0x24: { // p36
|
||||
auto log36 = log.sub("(p36) ");
|
||||
// On NTE, this includes SCs and items; on other versions, it's SCs only
|
||||
static const auto should_include = +[](shared_ptr<const CardIndex::CardEntry> ce, bool is_nte) -> bool {
|
||||
return (ce && (ce->def.is_sc() || (is_nte ? (ce->def.type == CardType::ITEM) : false)));
|
||||
};
|
||||
bool is_nte = s->options.is_nte();
|
||||
if (as.original_attacker_card_ref == 0xFFFF) {
|
||||
log36.debug("original_attacker_card_ref missing");
|
||||
// debug_str_for_card_ref
|
||||
for (size_t z = 0; (z < 4 * 9) && (as.target_card_refs[z] != 0xFFFF); z++) {
|
||||
string debug_ref_str = s->debug_str_for_card_ref(as.target_card_refs[z]);
|
||||
log36.debug("examining %s", debug_ref_str.c_str());
|
||||
auto result_card = s->card_for_set_card_ref(as.target_card_refs[z]);
|
||||
if (result_card && should_include(result_card->get_definition(), is_nte)) {
|
||||
log36.debug("adding %s", debug_ref_str.c_str());
|
||||
ret.emplace_back(result_card);
|
||||
} else {
|
||||
log36.debug("skipping %s", debug_ref_str.c_str());
|
||||
}
|
||||
}
|
||||
} else if (card2 && should_include(card2->get_definition(), is_nte)) {
|
||||
string debug_ref_str = s->debug_str_for_card_ref(card2->get_card_ref());
|
||||
log36.debug("original_attacker_card_ref present; adding card2 = %s", debug_ref_str.c_str());
|
||||
ret.emplace_back(card2);
|
||||
} else if (card2) {
|
||||
string debug_ref_str = s->debug_str_for_card_ref(card2->get_card_ref());
|
||||
log36.debug("original_attacker_card_ref present and card2 (%s) not eligible", debug_ref_str.c_str());
|
||||
} else {
|
||||
log36.debug("original_attacker_card_ref present and card2 missing");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1723,7 +1723,7 @@ string MapDefinition::str(const CardIndex* card_index, uint8_t language) const {
|
||||
auto add_map = [&](const parray<parray<uint8_t, 0x10>, 0x10>& tiles) {
|
||||
for (size_t y = 0; y < this->height; y++) {
|
||||
string line = " ";
|
||||
for (size_t x = 0; x < this->height; x++) {
|
||||
for (size_t x = 0; x < this->width; x++) {
|
||||
line += string_printf(" %02hhX", tiles[y][x]);
|
||||
}
|
||||
lines.emplace_back(std::move(line));
|
||||
|
||||
@@ -345,6 +345,12 @@ bool FunctionCodeIndex::patch_menu_empty(uint32_t specific_version) const {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<const CompiledFunctionCode> FunctionCodeIndex::get_patch(
|
||||
const std::string& name, uint32_t specific_version) const {
|
||||
return this->name_and_specific_version_to_patch_function.at(
|
||||
string_printf("%s-%08" PRIX32, name.c_str(), specific_version));
|
||||
}
|
||||
|
||||
DOLFileIndex::DOLFileIndex(const string& directory) {
|
||||
if (!function_compiler_available()) {
|
||||
function_compiler_log.info("Function compiler is not available");
|
||||
|
||||
@@ -70,6 +70,8 @@ struct FunctionCodeIndex {
|
||||
|
||||
std::shared_ptr<const Menu> patch_menu(uint32_t specific_version) const;
|
||||
bool patch_menu_empty(uint32_t specific_version) const;
|
||||
|
||||
std::shared_ptr<const CompiledFunctionCode> get_patch(const std::string& name, uint32_t specific_version) const;
|
||||
};
|
||||
|
||||
struct DOLFileIndex {
|
||||
|
||||
@@ -101,6 +101,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
|
||||
parray<le_uint32_t, 0x20> variations;
|
||||
std::unique_ptr<QuestFlags> quest_flags_known; // If null, ALL quest flags are known
|
||||
std::unique_ptr<QuestFlags> quest_flag_values;
|
||||
std::unique_ptr<SwitchFlags> switch_flags;
|
||||
|
||||
// Game config
|
||||
Version base_version;
|
||||
|
||||
+38
-1
@@ -1115,6 +1115,43 @@ Action a_disassemble_quest_map(
|
||||
data = prs_decompress(data);
|
||||
}
|
||||
string result = Map::disassemble_quest_data(data.data(), data.size());
|
||||
write_output_data(args, result.data(), result.size(), "txt");
|
||||
});
|
||||
Action a_disassemble_free_map(
|
||||
"disassemble-free-map", "\
|
||||
disassemble-free-map INPUT-FILENAME [OUTPUT-FILENAME]\n\
|
||||
Disassemble the input free-roam map (.dat or .evt file) into a text\n\
|
||||
representation of the data it contains. Unlike othe disassembly actions,\n\
|
||||
this action expects its input to be already decompressed. If the input is\n\
|
||||
compressed, use the --compressed option. Also unlike other options, the\n\
|
||||
input must be from a file (that is, INPUT-FILENAME is required and cannot\n\
|
||||
be \"-\").\n",
|
||||
+[](Arguments& args) {
|
||||
const string& input_filename = args.get<string>(1, true);
|
||||
bool is_events = ends_with(input_filename, ".evt");
|
||||
bool is_enemies = ends_with(input_filename, "e.dat") || ends_with(input_filename, "e_s.dat") || ends_with(input_filename, "e_c1.dat") || ends_with(input_filename, "e_d.dat");
|
||||
bool is_objects = ends_with(input_filename, "o.dat") || ends_with(input_filename, "o_s.dat") || ends_with(input_filename, "o_c1.dat") || ends_with(input_filename, "o_d.dat");
|
||||
if (!is_objects && !is_enemies && !is_events) {
|
||||
throw runtime_error("cannot determine input file type");
|
||||
}
|
||||
|
||||
string data = read_input_data(args);
|
||||
if (args.get<bool>("compressed")) {
|
||||
data = prs_decompress(data);
|
||||
}
|
||||
|
||||
string result;
|
||||
if (is_objects) {
|
||||
result = Map::disassemble_objects_data(data.data(), data.size());
|
||||
} else if (is_enemies) {
|
||||
result = Map::disassemble_enemies_data(data.data(), data.size());
|
||||
} else if (is_events) {
|
||||
result = Map::disassemble_wave_events_data(data.data(), data.size());
|
||||
} else {
|
||||
throw logic_error("unhandled input type");
|
||||
}
|
||||
result.push_back('\n');
|
||||
|
||||
write_output_data(args, result.data(), result.size(), "txt");
|
||||
});
|
||||
Action a_disassemble_set_data_table(
|
||||
@@ -1439,7 +1476,7 @@ Action a_convert_rare_item_set(
|
||||
+[](Arguments& args) {
|
||||
auto version = get_cli_version(args);
|
||||
|
||||
double rate_factor = args.get<double>("multiply");
|
||||
double rate_factor = args.get<double>("multiply", 1.0);
|
||||
auto s = make_shared<ServerState>("system/config.json");
|
||||
s->load_config_early();
|
||||
s->load_patch_indexes(false);
|
||||
|
||||
+188
-37
@@ -14,6 +14,10 @@ using namespace std;
|
||||
|
||||
static constexpr float UINT32_MAX_AS_FLOAT = 4294967296.0f;
|
||||
|
||||
static uint64_t section_index_key(uint8_t floor, uint16_t section, uint16_t wave_number) {
|
||||
return (static_cast<uint64_t>(floor) << 32) | (static_cast<uint64_t>(section) << 16) | static_cast<uint64_t>(wave_number);
|
||||
}
|
||||
|
||||
const char* Map::name_for_object_type(uint16_t type) {
|
||||
switch (type) {
|
||||
case 0x0000:
|
||||
@@ -655,23 +659,34 @@ string Map::EnemyEntry::str() const {
|
||||
this->unused.load());
|
||||
}
|
||||
|
||||
Map::Enemy::Enemy(uint16_t enemy_id, size_t source_index, size_t set_index, uint8_t floor, EnemyType type)
|
||||
Map::Enemy::Enemy(
|
||||
uint16_t enemy_id,
|
||||
size_t source_index,
|
||||
size_t set_index,
|
||||
uint8_t floor,
|
||||
uint16_t section,
|
||||
uint16_t wave_number,
|
||||
EnemyType type)
|
||||
: source_index(source_index),
|
||||
set_index(set_index),
|
||||
enemy_id(enemy_id),
|
||||
total_damage(0),
|
||||
game_flags(0),
|
||||
section(section),
|
||||
wave_number(wave_number),
|
||||
type(type),
|
||||
floor(floor),
|
||||
state_flags(0) {}
|
||||
|
||||
string Map::Enemy::str() const {
|
||||
return string_printf("[Map::Enemy E-%hX source %zX %s%s floor=%02hhX flags=%02hhX]",
|
||||
return string_printf("[Map::Enemy E-%hX source %zX %s%s floor=%02hhX section=%04hX wave_number=%04hX flags=%02hhX]",
|
||||
this->enemy_id,
|
||||
this->source_index,
|
||||
name_for_enum(this->type),
|
||||
enemy_type_is_rare(this->type) ? " RARE" : "",
|
||||
this->floor,
|
||||
this->section,
|
||||
this->wave_number,
|
||||
this->state_flags);
|
||||
}
|
||||
|
||||
@@ -721,10 +736,11 @@ void Map::add_objects_from_map_data(uint8_t floor, const void* data, size_t size
|
||||
uint16_t object_id = this->objects.size();
|
||||
this->objects.emplace_back(Object{
|
||||
.source_index = z,
|
||||
.object_id = object_id,
|
||||
.floor = floor,
|
||||
.object_id = object_id,
|
||||
.base_type = objects[z].base_type,
|
||||
.section = objects[z].section,
|
||||
.group = objects[z].group,
|
||||
.param1 = objects[z].param1,
|
||||
.param3 = objects[z].param3,
|
||||
.param4 = objects[z].param4,
|
||||
@@ -734,6 +750,8 @@ void Map::add_objects_from_map_data(uint8_t floor, const void* data, size_t size
|
||||
.set_flags = 0,
|
||||
.item_drop_checked = false,
|
||||
});
|
||||
uint64_t k = section_index_key(floor, objects[z].section, objects[z].group);
|
||||
this->floor_section_and_group_to_object_index.emplace(k, object_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -782,7 +800,9 @@ void Map::add_enemy(
|
||||
|
||||
auto add = [&](EnemyType type) -> void {
|
||||
uint16_t enemy_id = this->enemies.size();
|
||||
this->enemies.emplace_back(enemy_id, source_index, set_index, floor, type);
|
||||
this->enemies.emplace_back(enemy_id, source_index, set_index, floor, e.section, e.wave_number, type);
|
||||
uint64_t k = section_index_key(floor, e.section, e.wave_number);
|
||||
this->floor_section_and_wave_number_to_enemy_index.emplace(k, enemy_id);
|
||||
};
|
||||
|
||||
EnemyType child_type = EnemyType::UNKNOWN;
|
||||
@@ -1434,7 +1454,7 @@ void Map::add_random_enemies_from_map_data(
|
||||
}
|
||||
if (remaining_waves) {
|
||||
/* 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->add_event(wave_next_event_id, entry.flags, floor, entry.section, wave_number, 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<const char*>(&wave_next_event_id), sizeof(wave_next_event_id));
|
||||
@@ -1444,15 +1464,17 @@ void Map::add_random_enemies_from_map_data(
|
||||
}
|
||||
|
||||
/* 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);
|
||||
this->add_event(wave_next_event_id, entry.flags, floor, entry.section, wave_number, 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) {
|
||||
void Map::add_event(uint32_t event_id, uint16_t flags, uint8_t floor, uint16_t section, uint16_t wave_number, uint32_t action_stream_offset) {
|
||||
size_t index = this->events.size();
|
||||
auto& ev = this->events.emplace_back();
|
||||
ev.event_id = event_id;
|
||||
ev.section = section;
|
||||
ev.wave_number = wave_number;
|
||||
ev.flags = flags;
|
||||
ev.floor = floor;
|
||||
ev.action_stream_offset = action_stream_offset;
|
||||
@@ -1460,6 +1482,9 @@ void Map::add_event(uint32_t event_id, uint16_t flags, uint8_t floor, uint32_t a
|
||||
if (!this->floor_and_event_id_to_index.emplace(k, index).second) {
|
||||
this->log.warning("Duplicate event ID: W-%02hhX-%" PRIX32, floor, event_id);
|
||||
}
|
||||
|
||||
k = section_index_key(floor, section, wave_number);
|
||||
this->floor_section_and_wave_number_to_event_index.emplace(k, index);
|
||||
}
|
||||
|
||||
Map::Event& Map::get_event(uint8_t floor, uint32_t event_id) {
|
||||
@@ -1486,7 +1511,7 @@ void Map::add_events_from_map_data(uint8_t floor, const void* data, size_t size)
|
||||
auto events_r = r.sub(header.entries_offset, sizeof(Event1Entry) * header.entry_count);
|
||||
while (!events_r.eof()) {
|
||||
const auto& entry = events_r.get<Event1Entry>();
|
||||
this->add_event(entry.event_id, entry.flags, floor, entry.action_stream_offset + action_stream_base_offset);
|
||||
this->add_event(entry.event_id, entry.flags, floor, entry.section, entry.wave_number, entry.action_stream_offset + action_stream_base_offset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1638,6 +1663,154 @@ Map::Enemy& Map::find_enemy(uint8_t floor, EnemyType type) {
|
||||
throw out_of_range("enemy not found");
|
||||
}
|
||||
|
||||
std::vector<Map::Object*> Map::get_objects(uint8_t floor, uint16_t section, uint16_t group) {
|
||||
uint64_t k = section_index_key(floor, section, group);
|
||||
vector<Object*> ret;
|
||||
for (auto its = this->floor_section_and_group_to_object_index.equal_range(k); its.first != its.second; its.first++) {
|
||||
ret.emplace_back(&this->objects.at(its.first->second));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Map::Enemy*> Map::get_enemies(uint8_t floor, uint16_t section, uint16_t wave_number) {
|
||||
uint64_t k = section_index_key(floor, section, wave_number);
|
||||
vector<Enemy*> ret;
|
||||
for (auto its = this->floor_section_and_wave_number_to_enemy_index.equal_range(k); its.first != its.second; its.first++) {
|
||||
ret.emplace_back(&this->enemies.at(its.first->second));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Map::Event*> Map::get_events(uint8_t floor, uint16_t section, uint16_t wave_number) {
|
||||
uint64_t k = section_index_key(floor, section, wave_number);
|
||||
vector<Event*> ret;
|
||||
for (auto its = this->floor_section_and_wave_number_to_event_index.equal_range(k); its.first != its.second; its.first++) {
|
||||
ret.emplace_back(&this->events.at(its.first->second));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Map::Event*> Map::get_events(uint8_t floor) {
|
||||
uint64_t k_start = (static_cast<uint64_t>(floor) << 32);
|
||||
uint64_t k_end = (static_cast<uint64_t>(floor + 1) << 32);
|
||||
vector<Event*> ret;
|
||||
for (auto it = this->floor_and_event_id_to_index.lower_bound(k_start);
|
||||
(it != this->floor_and_event_id_to_index.end()) && (it->first < k_end);
|
||||
it++) {
|
||||
ret.emplace_back(&this->events.at(it->second));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename EntryT>
|
||||
static string disassemble_vector_file_t(const void* data, size_t size, size_t* entry_number, char type_ch) {
|
||||
deque<string> ret;
|
||||
StringReader r(data, size);
|
||||
|
||||
size_t local_entry_number = 0;
|
||||
if (!entry_number) {
|
||||
entry_number = &local_entry_number;
|
||||
}
|
||||
|
||||
while (r.remaining() >= sizeof(EntryT)) {
|
||||
string o_str = r.get<EntryT>().str();
|
||||
ret.emplace_back(string_printf("/* %c-%zX */ %s", type_ch, (*entry_number)++, o_str.c_str()));
|
||||
}
|
||||
if (r.remaining()) {
|
||||
ret.emplace_back("// Warning: section size is not a multiple of entry size");
|
||||
size_t size = r.remaining();
|
||||
ret.emplace_back(format_data(r.getv(size), size));
|
||||
}
|
||||
return join(ret, "\n");
|
||||
}
|
||||
|
||||
string Map::disassemble_objects_data(const void* data, size_t size, size_t* object_number) {
|
||||
return disassemble_vector_file_t<ObjectEntry>(data, size, object_number, 'K');
|
||||
}
|
||||
|
||||
string Map::disassemble_enemies_data(const void* data, size_t size, size_t* enemy_number) {
|
||||
return disassemble_vector_file_t<EnemyEntry>(data, size, enemy_number, 'S');
|
||||
}
|
||||
|
||||
string Map::disassemble_wave_events_data(const void* data, size_t size, uint8_t floor) {
|
||||
deque<string> ret;
|
||||
StringReader r(data, size);
|
||||
|
||||
const auto& evt_header = r.get<EventsSectionHeader>();
|
||||
if (evt_header.format == 0x65767432) { // 'evt2'
|
||||
ret.emplace_back(".evt2_format"); // TODO
|
||||
size_t size = r.remaining();
|
||||
ret.emplace_back(format_data(r.getv(size), size));
|
||||
} else {
|
||||
auto action_stream_r = r.sub(evt_header.action_stream_offset);
|
||||
for (size_t z = 0; z < evt_header.entry_count; z++) {
|
||||
const auto& entry = r.get<Event1Entry>();
|
||||
ret.emplace_back(string_printf("/* W-%02hhX-%" PRIX32 " */ [Event1Entry flags=%04hX type=%04hX section=%04hX wave_number=%04hX delay=%" PRIu32 "]",
|
||||
floor,
|
||||
entry.event_id.load(),
|
||||
entry.flags.load(),
|
||||
entry.event_type.load(),
|
||||
entry.section.load(),
|
||||
entry.wave_number.load(),
|
||||
entry.delay.load()));
|
||||
auto ev_actions_r = action_stream_r.sub(entry.action_stream_offset);
|
||||
bool should_continue = true;
|
||||
while (!ev_actions_r.eof() && should_continue) {
|
||||
uint8_t opcode = ev_actions_r.get_u8();
|
||||
switch (opcode) {
|
||||
case 0x00:
|
||||
ret.emplace_back(string_printf(" 00 nop"));
|
||||
break;
|
||||
case 0x01:
|
||||
ret.emplace_back(string_printf(" 01 stop"));
|
||||
should_continue = false;
|
||||
break;
|
||||
case 0x08: {
|
||||
uint16_t section = ev_actions_r.get_u16l();
|
||||
uint16_t group = ev_actions_r.get_u16l();
|
||||
ret.emplace_back(string_printf(" 08 %04hX %04hX construct_objects section=%04hX group=%04hX",
|
||||
section, group, section, group));
|
||||
break;
|
||||
}
|
||||
case 0x09: {
|
||||
uint16_t section = ev_actions_r.get_u16l();
|
||||
uint16_t wave_number = ev_actions_r.get_u16l();
|
||||
ret.emplace_back(string_printf(" 09 %04hX %04hX construct_enemies section=%04hX wave_number=%04hX",
|
||||
section, wave_number, section, wave_number));
|
||||
break;
|
||||
}
|
||||
case 0x0A: {
|
||||
uint16_t id = ev_actions_r.get_u16l();
|
||||
ret.emplace_back(string_printf(" 0A %04hX enable_switch_flag id=%04hX", id, id));
|
||||
break;
|
||||
}
|
||||
case 0x0B: {
|
||||
uint16_t id = ev_actions_r.get_u16l();
|
||||
ret.emplace_back(string_printf(" 0B %04hX disable_switch_flag id=%04hX", id, id));
|
||||
break;
|
||||
}
|
||||
case 0x0C: {
|
||||
uint32_t event_id = ev_actions_r.get_u32l();
|
||||
ret.emplace_back(string_printf(" 0C %08" PRIX32 " trigger_event event_id=%08" PRIX32, event_id, event_id));
|
||||
break;
|
||||
}
|
||||
case 0x0D: {
|
||||
uint16_t section = ev_actions_r.get_u16l();
|
||||
uint16_t wave_number = ev_actions_r.get_u16l();
|
||||
ret.emplace_back(string_printf(" 0D %04hX %04hX construct_enemies_stop section=%04hX wave_number=%04hX",
|
||||
section, wave_number, section, wave_number));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret.emplace_back(string_printf(" %02hhX .invalid", opcode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return join(ret, "\n");
|
||||
}
|
||||
|
||||
string Map::disassemble_quest_data(const void* data, size_t size) {
|
||||
auto all_floor_sections = Map::collect_quest_map_data_sections(data, size);
|
||||
|
||||
@@ -1651,56 +1824,34 @@ string Map::disassemble_quest_data(const void* data, size_t size) {
|
||||
if (floor_sections.objects != 0xFFFFFFFF) {
|
||||
ret.emplace_back(string_printf(".objects %zu", floor));
|
||||
const auto& header = r.pget<SectionHeader>(floor_sections.objects);
|
||||
auto sub_r = r.sub(floor_sections.objects + sizeof(SectionHeader), header.data_size);
|
||||
while (sub_r.remaining() >= sizeof(ObjectEntry)) {
|
||||
string o_str = sub_r.get<ObjectEntry>().str();
|
||||
ret.emplace_back(string_printf("/* K-%zX */ %s", object_number++, o_str.c_str()));
|
||||
}
|
||||
if (sub_r.remaining()) {
|
||||
ret.emplace_back("// Warning: object section size is not a multiple of object entry size");
|
||||
size_t offset = floor_sections.objects + sizeof(SectionHeader) + r.where();
|
||||
size_t bytes = r.remaining();
|
||||
ret.emplace_back(format_data(r.getv(r.remaining()), bytes, offset));
|
||||
}
|
||||
size_t offset = floor_sections.objects + sizeof(SectionHeader);
|
||||
ret.emplace_back(Map::disassemble_objects_data(r.pgetv(offset, header.data_size), header.data_size, &object_number));
|
||||
}
|
||||
|
||||
if (floor_sections.enemies != 0xFFFFFFFF) {
|
||||
ret.emplace_back(string_printf(".enemies %zu", floor));
|
||||
const auto& header = r.pget<SectionHeader>(floor_sections.enemies);
|
||||
auto sub_r = r.sub(floor_sections.enemies + sizeof(SectionHeader), header.data_size);
|
||||
while (sub_r.remaining() >= sizeof(EnemyEntry)) {
|
||||
string e_str = sub_r.get<EnemyEntry>().str();
|
||||
ret.emplace_back(string_printf("/* entry %zX */ %s", enemy_number++, e_str.c_str()));
|
||||
}
|
||||
if (sub_r.remaining()) {
|
||||
ret.emplace_back("// Warning: enemy section size is not a multiple of enemy entry size");
|
||||
size_t offset = floor_sections.objects + sizeof(SectionHeader) + r.where();
|
||||
size_t bytes = r.remaining();
|
||||
ret.emplace_back(format_data(r.getv(r.remaining()), bytes, offset));
|
||||
}
|
||||
size_t offset = floor_sections.enemies + sizeof(SectionHeader);
|
||||
ret.emplace_back(Map::disassemble_enemies_data(r.pgetv(offset, header.data_size), header.data_size, &enemy_number));
|
||||
}
|
||||
|
||||
// TODO: Add disassembly for these section types
|
||||
if (floor_sections.wave_events != 0xFFFFFFFF) {
|
||||
ret.emplace_back(string_printf(".wave_events %zu", floor));
|
||||
const auto& header = r.pget<SectionHeader>(floor_sections.wave_events);
|
||||
size_t offset = floor_sections.wave_events + sizeof(SectionHeader);
|
||||
auto sub_r = r.sub(offset, header.data_size);
|
||||
ret.emplace_back(format_data(r.getv(r.remaining()), header.data_size, offset));
|
||||
ret.emplace_back(Map::disassemble_wave_events_data(r.pgetv(offset, header.data_size), header.data_size, floor));
|
||||
}
|
||||
if (floor_sections.random_enemy_locations != 0xFFFFFFFF) {
|
||||
ret.emplace_back(string_printf(".random_enemy_locations %zu", floor));
|
||||
const auto& header = r.pget<SectionHeader>(floor_sections.random_enemy_locations);
|
||||
size_t offset = floor_sections.random_enemy_locations + sizeof(SectionHeader);
|
||||
auto sub_r = r.sub(offset, header.data_size);
|
||||
ret.emplace_back(format_data(r.getv(r.remaining()), header.data_size, offset));
|
||||
ret.emplace_back(format_data(sub_r.getv(sub_r.remaining()), header.data_size, offset));
|
||||
}
|
||||
if (floor_sections.random_enemy_definitions != 0xFFFFFFFF) {
|
||||
ret.emplace_back(string_printf(".random_enemy_definitions %zu", floor));
|
||||
const auto& header = r.pget<SectionHeader>(floor_sections.random_enemy_definitions);
|
||||
size_t offset = floor_sections.random_enemy_definitions + sizeof(SectionHeader);
|
||||
auto sub_r = r.sub(offset, header.data_size);
|
||||
ret.emplace_back(format_data(r.getv(r.remaining()), header.data_size, offset));
|
||||
ret.emplace_back(format_data(sub_r.getv(sub_r.remaining()), header.data_size, offset));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+39
-6
@@ -103,6 +103,10 @@ struct Map {
|
||||
|
||||
struct Event1Entry { // Section type 3 (WAVE_EVENTS) if format == 0
|
||||
/* 00 */ le_uint32_t event_id;
|
||||
// Bits in flags:
|
||||
// 0004 = is active
|
||||
// 0008 = post-wave actions have been run
|
||||
// 0010 = all enemies killed
|
||||
/* 04 */ le_uint16_t flags;
|
||||
/* 06 */ le_uint16_t event_type;
|
||||
/* 08 */ le_uint16_t section;
|
||||
@@ -208,10 +212,11 @@ struct Map {
|
||||
// TODO: Add more fields in here if we ever care about them. Currently we
|
||||
// only care about boxes with fixed item drops.
|
||||
size_t source_index;
|
||||
uint16_t object_id;
|
||||
uint8_t floor;
|
||||
uint16_t object_id;
|
||||
uint16_t base_type;
|
||||
uint16_t section;
|
||||
uint16_t group;
|
||||
float param1; // If <= 0, this is a specialized box, and the specialization is in param4/5/6
|
||||
float param3; // If == 0, the item should be varied by difficulty and area
|
||||
uint32_t param4;
|
||||
@@ -239,23 +244,35 @@ struct Map {
|
||||
uint16_t enemy_id;
|
||||
uint16_t total_damage;
|
||||
uint32_t game_flags; // From 6x0A
|
||||
uint16_t section;
|
||||
uint16_t wave_number;
|
||||
EnemyType type;
|
||||
uint8_t floor;
|
||||
uint8_t state_flags;
|
||||
|
||||
Enemy(uint16_t enemy_id, size_t source_index, size_t set_index, uint8_t floor, EnemyType type);
|
||||
Enemy(
|
||||
uint16_t enemy_id,
|
||||
size_t source_index,
|
||||
size_t set_index,
|
||||
uint8_t floor,
|
||||
uint16_t section,
|
||||
uint16_t wave_number,
|
||||
EnemyType type);
|
||||
|
||||
std::string str() const;
|
||||
} __attribute__((packed));
|
||||
};
|
||||
|
||||
struct Event {
|
||||
uint32_t event_id;
|
||||
uint16_t flags;
|
||||
uint16_t section;
|
||||
uint16_t wave_number;
|
||||
uint8_t floor;
|
||||
uint32_t action_stream_offset;
|
||||
std::vector<size_t> enemy_indexes;
|
||||
|
||||
std::string str() const;
|
||||
} __attribute__((packed));
|
||||
};
|
||||
|
||||
struct DATParserRandomState {
|
||||
PSOV2Encryption random;
|
||||
@@ -306,7 +323,13 @@ struct Map {
|
||||
std::shared_ptr<DATParserRandomState> random_state,
|
||||
std::shared_ptr<const RareEnemyRates> rare_rates = DEFAULT_RARE_ENEMIES);
|
||||
|
||||
void add_event(uint32_t event_id, uint16_t flags, uint8_t floor, uint32_t action_stream_offset);
|
||||
void add_event(
|
||||
uint32_t event_id,
|
||||
uint16_t flags,
|
||||
uint8_t floor,
|
||||
uint16_t section,
|
||||
uint16_t wave_number,
|
||||
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);
|
||||
@@ -330,7 +353,14 @@ struct Map {
|
||||
|
||||
const Enemy& find_enemy(uint8_t floor, EnemyType type) const;
|
||||
Enemy& find_enemy(uint8_t floor, EnemyType type);
|
||||
std::vector<Object*> get_objects(uint8_t floor, uint16_t section, uint16_t wave_number);
|
||||
std::vector<Enemy*> get_enemies(uint8_t floor, uint16_t section, uint16_t wave_number);
|
||||
std::vector<Event*> get_events(uint8_t floor, uint16_t section, uint16_t wave_number);
|
||||
std::vector<Event*> get_events(uint8_t floor);
|
||||
|
||||
static std::string disassemble_objects_data(const void* data, size_t size, size_t* object_number = nullptr);
|
||||
static std::string disassemble_enemies_data(const void* data, size_t size, size_t* enemy_number = nullptr);
|
||||
static std::string disassemble_wave_events_data(const void* data, size_t size, uint8_t floor = 0xFF);
|
||||
static std::string disassemble_quest_data(const void* data, size_t size);
|
||||
|
||||
PrefixedLogger log;
|
||||
@@ -343,7 +373,10 @@ struct Map {
|
||||
std::vector<size_t> rare_enemy_indexes;
|
||||
std::vector<Event> events;
|
||||
std::string event_action_stream;
|
||||
std::unordered_map<uint64_t, size_t> floor_and_event_id_to_index;
|
||||
std::map<uint64_t, size_t> floor_and_event_id_to_index;
|
||||
std::unordered_multimap<uint64_t, size_t> floor_section_and_group_to_object_index;
|
||||
std::unordered_multimap<uint64_t, size_t> floor_section_and_wave_number_to_enemy_index;
|
||||
std::unordered_multimap<uint64_t, size_t> floor_section_and_wave_number_to_event_index;
|
||||
};
|
||||
|
||||
class SetDataTableBase {
|
||||
|
||||
@@ -1091,3 +1091,26 @@ SymbolChat::SymbolChat()
|
||||
: spec(0),
|
||||
corner_objects(0x00FF),
|
||||
face_parts() {}
|
||||
|
||||
void RecentSwitchFlags::add(uint16_t flag_num) {
|
||||
if ((flag_num != ((this->flag_nums >> 48) & 0xFFFF)) &&
|
||||
(flag_num != ((this->flag_nums >> 32) & 0xFFFF)) &&
|
||||
(flag_num != ((this->flag_nums >> 16) & 0xFFFF)) &&
|
||||
(flag_num != (this->flag_nums & 0xFFFF))) {
|
||||
this->flag_nums = this->flag_nums << 16 | flag_num;
|
||||
}
|
||||
}
|
||||
|
||||
string RecentSwitchFlags::enable_commands(uint8_t floor) const {
|
||||
StringWriter w;
|
||||
uint64_t flag_nums = this->flag_nums;
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
uint16_t flag_num = flag_nums;
|
||||
if (flag_num == 0xFFFF) {
|
||||
continue;
|
||||
}
|
||||
w.put(G_SwitchStateChanged_6x05{{0x05, 0x03, 0xFFFF}, 0, 0, flag_num, static_cast<uint8_t>(floor), 0x01});
|
||||
flag_nums >>= 16;
|
||||
}
|
||||
return std::move(w.str());
|
||||
}
|
||||
|
||||
@@ -565,6 +565,20 @@ struct QuestFlagsV1 {
|
||||
operator QuestFlags() const;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct SwitchFlags {
|
||||
parray<parray<uint8_t, 0x20>, 0x12> data;
|
||||
|
||||
inline bool get(uint8_t floor, uint16_t flag_num) const {
|
||||
return this->data[floor][flag_num >> 3] & (0x80 >> (flag_num & 7));
|
||||
}
|
||||
inline void set(uint8_t floor, uint16_t flag_num) {
|
||||
this->data[floor][flag_num >> 3] |= (0x80 >> (flag_num & 7));
|
||||
}
|
||||
inline void clear(uint8_t floor, uint16_t flag_num) {
|
||||
this->data[floor][flag_num >> 3] &= ~(0x80 >> (flag_num & 7));
|
||||
}
|
||||
} __attribute__((packed));
|
||||
|
||||
struct BattleRules {
|
||||
enum class TechDiskMode : uint8_t {
|
||||
ALLOW = 0,
|
||||
@@ -719,3 +733,15 @@ struct SymbolChat {
|
||||
|
||||
SymbolChat();
|
||||
} __attribute__((packed));
|
||||
|
||||
struct RecentSwitchFlags {
|
||||
uint64_t flag_nums = 0xFFFFFFFFFFFFFFFF;
|
||||
|
||||
inline void clear() {
|
||||
this->flag_nums = 0xFFFFFFFFFFFFFFFF;
|
||||
}
|
||||
|
||||
void add(uint16_t flag_num);
|
||||
|
||||
std::string enable_commands(uint8_t floor) const;
|
||||
};
|
||||
|
||||
@@ -1946,15 +1946,13 @@ HandlerResult C_6x<void>(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, u
|
||||
if (!data.empty()) {
|
||||
if ((data[0] == 0x05) && ses->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED)) {
|
||||
auto& cmd = check_size_t<G_SwitchStateChanged_6x05>(data);
|
||||
if (cmd.flags && cmd.header.object_id != 0xFFFF) {
|
||||
if (ses->last_switch_enabled_command.header.subcommand == 0x05) {
|
||||
ses->log.info("Switch assist: replaying previous enable command");
|
||||
ses->server_channel.send(0x60, 0x00, &ses->last_switch_enabled_command,
|
||||
sizeof(ses->last_switch_enabled_command));
|
||||
ses->client_channel.send(0x60, 0x00, &ses->last_switch_enabled_command,
|
||||
sizeof(ses->last_switch_enabled_command));
|
||||
if ((cmd.flags & 1) && (cmd.header.object_id != 0xFFFF)) {
|
||||
ses->recent_switch_flags.add(cmd.switch_flag_num);
|
||||
string commands = ses->recent_switch_flags.enable_commands(ses->floor);
|
||||
if (!commands.empty()) {
|
||||
ses->server_channel.send(0x60, 0x00, commands);
|
||||
ses->client_channel.send(0x60, 0x00, commands);
|
||||
}
|
||||
ses->last_switch_enabled_command = cmd;
|
||||
}
|
||||
|
||||
} else if (data[0] == 0x21) {
|
||||
@@ -1962,7 +1960,7 @@ HandlerResult C_6x<void>(shared_ptr<ProxyServer::LinkedSession> ses, uint16_t, u
|
||||
ses->floor = cmd.floor;
|
||||
|
||||
} else if (data[0] == 0x0C) {
|
||||
if (is_v1_or_v2(ses->version()) && ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
if (ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
send_remove_conditions(ses->client_channel, ses->lobby_client_id);
|
||||
send_remove_conditions(ses->server_channel, ses->lobby_client_id);
|
||||
}
|
||||
|
||||
@@ -537,7 +537,6 @@ ProxyServer::LinkedSession::LinkedSession(
|
||||
lobby_mode(GameMode::NORMAL),
|
||||
lobby_episode(Episode::EP1),
|
||||
lobby_random_seed(0) {
|
||||
this->last_switch_enabled_command.header.subcommand = 0;
|
||||
memset(this->prev_server_command_bytes, 0, sizeof(this->prev_server_command_bytes));
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ public:
|
||||
Client::Config config;
|
||||
// A null handler in here means to forward the response to the remote server
|
||||
std::deque<std::function<void(uint32_t return_value, uint32_t checksum)>> function_call_return_handler_queue;
|
||||
G_SwitchStateChanged_6x05 last_switch_enabled_command;
|
||||
RecentSwitchFlags recent_switch_flags; // used for switch assist
|
||||
ItemData next_drop_item;
|
||||
uint32_t next_item_id;
|
||||
|
||||
|
||||
+2
-11
@@ -2515,17 +2515,7 @@ static void on_10(shared_ptr<Client> c, uint16_t, uint32_t, string& data) {
|
||||
// leader)
|
||||
if (game->count_clients() == 1) {
|
||||
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_ENEMY_AND_SET_STATE);
|
||||
c->config.set_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_OBJECT_STATE);
|
||||
c->config.set_flag(Client::Flag::SHOULD_SEND_ARTIFICIAL_FLAG_STATE);
|
||||
}
|
||||
@@ -4329,6 +4319,7 @@ shared_ptr<Lobby> create_game_generic(
|
||||
game->quest_flag_values = make_unique<QuestFlags>();
|
||||
game->quest_flags_known = make_unique<QuestFlags>();
|
||||
}
|
||||
game->switch_flags = make_unique<SwitchFlags>();
|
||||
|
||||
return game;
|
||||
}
|
||||
|
||||
+286
-56
@@ -485,11 +485,10 @@ static void on_sync_joining_player_compressed_state(shared_ptr<Client> c, uint8_
|
||||
send_game_item_state(target);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x6E: {
|
||||
StringReader r(decompressed);
|
||||
const auto& dec_header = r.get<G_SyncSetFlagState_6x6E_Decompressed>();
|
||||
if (dec_header.total_size != dec_header.entity_set_flags_size + dec_header.event_set_flags_size + dec_header.unused_size) {
|
||||
if (dec_header.total_size != dec_header.entity_set_flags_size + dec_header.event_set_flags_size + dec_header.switch_flags_size) {
|
||||
throw runtime_error("incorrect size fields in 6x6E header");
|
||||
}
|
||||
|
||||
@@ -518,8 +517,9 @@ static void on_sync_joining_player_compressed_state(shared_ptr<Client> c, uint8_
|
||||
for (size_t z = 0; z < min<size_t>(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)",
|
||||
l->log.warning("(K-%zX) Set flags from client (%04hX) do not match set flags from map (%04hX)",
|
||||
z, flags, l->map->objects[z].set_flags);
|
||||
l->map->objects[z].set_flags = flags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,8 +533,9 @@ static void on_sync_joining_player_compressed_state(shared_ptr<Client> c, uint8_
|
||||
for (size_t z = 0; z < min<size_t>(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)",
|
||||
l->log.warning("(S-%zX) Set flags from client (%04hX) do not match set flags from map (%04hX)",
|
||||
z, flags, l->map->enemy_set_flags[z]);
|
||||
l->map->enemy_set_flags[z] = flags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,32 +553,60 @@ static void on_sync_joining_player_compressed_state(shared_ptr<Client> c, uint8_
|
||||
}
|
||||
for (size_t z = 0; z < min<size_t>(num_event_flags, l->map->events.size()); z++) {
|
||||
uint16_t flags = event_set_flags_r.get_u16l();
|
||||
const auto& event = l->map->events[z];
|
||||
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);
|
||||
event.flags = 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);
|
||||
size_t expected_switch_flag_num_floors = is_v1(c->version()) ? 0x10 : 0x12;
|
||||
size_t expected_switch_flags_size = expected_switch_flag_num_floors * 0x20;
|
||||
if (dec_header.switch_flags_size != expected_switch_flags_size) {
|
||||
l->log.warning("Switch flags size (0x%" PRIX32 ") does not match expected size (0x%zX)",
|
||||
dec_header.switch_flags_size.load(), expected_switch_flags_size);
|
||||
} else {
|
||||
l->log.info("Switch flags size matches expected size (0x%zX)", expected_switch_flags_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");
|
||||
if (l->switch_flags) {
|
||||
StringReader switch_flags_r = r.sub(r.where() + dec_header.entity_set_flags_size + dec_header.event_set_flags_size);
|
||||
for (size_t floor = 0; floor < expected_switch_flag_num_floors; floor++) {
|
||||
// There is a bug in most (perhaps all) versions of the game, which
|
||||
// causes this array to be too small. It looks like Sega forgot to
|
||||
// account for the header (G_SyncSetFlagState_6x6E_Decompressed)
|
||||
// before compressing the buffer, so the game cuts off the last 8
|
||||
// bytes of the switch flags. Since this only affects the last floor,
|
||||
// which rarely has any switches on it (or is even accessible by the
|
||||
// player), it's not surprising that no one noticed this. But it does
|
||||
// mean we have to check switch_flags_r.eof() here.
|
||||
for (size_t z = 0; (z < 0x20) && !switch_flags_r.eof(); z++) {
|
||||
uint8_t& l_flags = l->switch_flags->data[floor][z];
|
||||
uint8_t r_flags = switch_flags_r.get_u8();
|
||||
if (l_flags != r_flags) {
|
||||
l->log.warning("Switch flags do not match at %02zX[%02zX] (expected %02hhX, received %02hhX)",
|
||||
floor, z, l_flags, r_flags);
|
||||
l_flags = r_flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
decompressed.resize(decompressed.size() - dec_header.unused_size.load() + target_unused_size, '\0');
|
||||
auto* wdec_header = reinterpret_cast<G_SyncSetFlagState_6x6E_Decompressed*>(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;
|
||||
}
|
||||
|
||||
// size_t target_switch_flag_num_floors = is_v1(target->version()) ? 0x10 : 0x12;
|
||||
// size_t target_switch_flags_size = target_switch_flag_num_floors * 0x20;
|
||||
// if (dec_header.switch_flags_size != target_switch_flags_size) {
|
||||
// l->log.info("Resizing switch flags from 0x%" PRIX32 " bytes to 0x%zX bytes",
|
||||
// dec_header.switch_flags_size.load(), target_switch_flags_size);
|
||||
// if (dec_header.switch_flags_size >= decompressed.size()) {
|
||||
// throw runtime_error("switch flags size is too large");
|
||||
// }
|
||||
// decompressed.resize(decompressed.size() - dec_header.switch_flags_size.load() + target_switch_flags_size, '\0');
|
||||
// auto* wdec_header = reinterpret_cast<G_SyncSetFlagState_6x6E_Decompressed*>(decompressed.data());
|
||||
// wdec_header->switch_flags_size = target_switch_flags_size;
|
||||
// wdec_header->total_size = wdec_header->entity_set_flags_size + wdec_header->event_set_flags_size + wdec_header->switch_flags_size;
|
||||
// }
|
||||
|
||||
send_game_join_sync_command_compressed(
|
||||
target,
|
||||
compressed_data,
|
||||
@@ -1007,7 +1036,7 @@ static void on_sync_joining_player_disp_and_inventory(
|
||||
parsed->visual.enforce_lobby_join_limits_for_version(target_v);
|
||||
if (s->version_name_colors) {
|
||||
parsed->visual.name_color = s->name_color_for_version(c_v);
|
||||
if (is_v1_or_v2(c_v)) {
|
||||
if (is_v1_or_v2(target_v)) {
|
||||
parsed->visual.compute_name_color_checksum();
|
||||
}
|
||||
}
|
||||
@@ -1335,8 +1364,9 @@ static void on_change_floor_6x1F(shared_ptr<Client> c, uint8_t command, uint8_t
|
||||
|
||||
} else {
|
||||
const auto& cmd = check_size_t<G_SetPlayerFloor_6x1F>(data, size);
|
||||
if (cmd.floor >= 0) {
|
||||
if (cmd.floor >= 0 && c->floor != static_cast<uint32_t>(cmd.floor)) {
|
||||
c->floor = cmd.floor;
|
||||
c->recent_switch_flags.clear();
|
||||
}
|
||||
}
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
@@ -1344,21 +1374,22 @@ static void on_change_floor_6x1F(shared_ptr<Client> c, uint8_t command, uint8_t
|
||||
|
||||
static void on_change_floor_6x21(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_InterLevelWarp_6x21>(data, size);
|
||||
if (cmd.floor >= 0) {
|
||||
if (cmd.floor >= 0 && c->floor != static_cast<uint32_t>(cmd.floor)) {
|
||||
c->floor = cmd.floor;
|
||||
c->recent_switch_flags.clear();
|
||||
}
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
}
|
||||
|
||||
// When a player dies, decrease their mag's synchro
|
||||
static void on_player_died(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_ClientIDHeader>(data, size, 0xFFFF);
|
||||
const auto& cmd = check_size_t<G_PlayerDied_6x4D>(data, size, 0xFFFF);
|
||||
|
||||
auto l = c->require_lobby();
|
||||
if (!l->is_game() || (cmd.client_id != c->lobby_client_id)) {
|
||||
if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Decrease MAG's synchro
|
||||
try {
|
||||
auto& inventory = c->character()->inventory;
|
||||
size_t mag_index = inventory.find_equipped_item(EquipSlot::MAG);
|
||||
@@ -1370,13 +1401,58 @@ static void on_player_died(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
}
|
||||
|
||||
static void on_player_revivable(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_PlayerRevivable_6x4E>(data, size, 0xFFFF);
|
||||
|
||||
auto l = c->require_lobby();
|
||||
if (!l->is_game() || (cmd.header.client_id != c->lobby_client_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
|
||||
// Revive if infinite HP is enabled
|
||||
bool player_cheats_enabled = l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->check_flag(License::Flag::CHEAT_ANYWHERE));
|
||||
if (player_cheats_enabled && c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
G_UseMedicalCenter_6x31 v2_cmd = {0x31, 0x01, c->lobby_client_id};
|
||||
G_RevivePlayer_V3_BB_6xA1 v3_cmd = {0xA1, 0x01, c->lobby_client_id};
|
||||
for (auto lc : l->clients) {
|
||||
if (!lc) {
|
||||
continue;
|
||||
}
|
||||
bool use_v3 = !is_v1_or_v2(lc->version()) || (lc->version() == Version::GC_NTE);
|
||||
const void* data = use_v3 ? static_cast<const void*>(&v3_cmd) : static_cast<const void*>(&v2_cmd);
|
||||
size_t size = use_v3 ? sizeof(v3_cmd) : sizeof(v2_cmd);
|
||||
if (lc == c) {
|
||||
send_protected_command(lc, data, size, false);
|
||||
} else {
|
||||
send_command(lc, 0x60, 0x00, data, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_player_revived(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
||||
check_size_t<G_PlayerRevived_6x4F>(data, size, 0xFFFF);
|
||||
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game()) {
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
bool player_cheats_enabled = !is_v1(c->version()) &&
|
||||
(l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->check_flag(License::Flag::CHEAT_ANYWHERE)));
|
||||
if (player_cheats_enabled && c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
send_player_stats_change(c, PlayerStatsChange::ADD_HP, 2550);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void on_received_condition(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_ClientIDHeader>(data, size, 0xFFFF);
|
||||
|
||||
auto l = c->require_lobby();
|
||||
if (l->is_game()) {
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
if (is_v1_or_v2(c->version()) && (cmd.client_id == c->lobby_client_id)) {
|
||||
if (cmd.client_id == c->lobby_client_id) {
|
||||
bool player_cheats_enabled = l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->license->check_flag(License::Flag::CHEAT_ANYWHERE));
|
||||
if (player_cheats_enabled && c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
|
||||
send_remove_conditions(c);
|
||||
@@ -1469,18 +1545,25 @@ static void on_switch_state_changed(shared_ptr<Client> c, uint8_t command, uint8
|
||||
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
|
||||
if (cmd.flags && cmd.header.object_id != 0xFFFF) {
|
||||
if (!l->quest &&
|
||||
c->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED) &&
|
||||
(c->last_switch_enabled_command.header.subcommand == 0x05)) {
|
||||
c->log.info("[Switch assist] Replaying previous enable command");
|
||||
if (l->switch_flags) {
|
||||
if (cmd.flags & 1) {
|
||||
l->switch_flags->set(cmd.switch_flag_floor, cmd.switch_flag_num);
|
||||
} else {
|
||||
l->switch_flags->clear(cmd.switch_flag_floor, cmd.switch_flag_num);
|
||||
}
|
||||
}
|
||||
|
||||
if ((cmd.flags & 1) && cmd.header.object_id != 0xFFFF) {
|
||||
c->recent_switch_flags.add(cmd.switch_flag_num);
|
||||
if (!l->quest && c->config.check_flag(Client::Flag::SWITCH_ASSIST_ENABLED)) {
|
||||
if (c->config.check_flag(Client::Flag::DEBUG_ENABLED)) {
|
||||
send_text_message(c, "$C5Switch assist");
|
||||
}
|
||||
forward_subcommand(c, command, flag, &c->last_switch_enabled_command, sizeof(c->last_switch_enabled_command));
|
||||
send_command_t(c, command, flag, c->last_switch_enabled_command);
|
||||
string commands = c->recent_switch_flags.enable_commands(c->floor);
|
||||
if (!commands.empty()) {
|
||||
send_command(c, 0x60, 0x00, commands);
|
||||
}
|
||||
}
|
||||
c->last_switch_enabled_command = cmd;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1505,8 +1588,9 @@ void on_movement_with_floor(shared_ptr<Client> c, uint8_t command, uint8_t flag,
|
||||
}
|
||||
c->x = cmd.x;
|
||||
c->z = cmd.z;
|
||||
if (cmd.floor >= 0) {
|
||||
if (cmd.floor >= 0 && c->floor != static_cast<uint32_t>(cmd.floor)) {
|
||||
c->floor = cmd.floor;
|
||||
c->recent_switch_flags.clear();
|
||||
}
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
}
|
||||
@@ -2408,10 +2492,19 @@ DropReconcileResult reconcile_drop_request_with_map(
|
||||
// rt_indexes in Episode 4 don't match those sent in the command; we just
|
||||
// ignore what the client sends.
|
||||
if ((episode != Episode::EP4) && (cmd.rt_index != res.effective_rt_index)) {
|
||||
log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32,
|
||||
cmd.rt_index, res.effective_rt_index);
|
||||
if (!is_v4(version)) {
|
||||
res.effective_rt_index = cmd.rt_index;
|
||||
// Special cases: BULCLAW => BULK and DARK_GUNNER => DEATH_GUNNER
|
||||
if (cmd.rt_index == 0x27 && map_enemy->type == EnemyType::BULCLAW) {
|
||||
log.info("E-%hX killed as BULK instead of BULCLAW", map_enemy->enemy_id);
|
||||
res.effective_rt_index = 0x27;
|
||||
} else if (cmd.rt_index == 0x23 && map_enemy->type == EnemyType::DARK_GUNNER) {
|
||||
log.info("E-%hX killed as DEATH_GUNNER instead of DARK_GUNNER", map_enemy->enemy_id);
|
||||
res.effective_rt_index = 0x23;
|
||||
} else {
|
||||
log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32,
|
||||
cmd.rt_index, res.effective_rt_index);
|
||||
if (!is_v4(version)) {
|
||||
res.effective_rt_index = cmd.rt_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cmd.floor != map_enemy->floor) {
|
||||
@@ -2664,25 +2757,130 @@ static void on_set_quest_flag(shared_ptr<Client> c, uint8_t command, uint8_t fla
|
||||
}
|
||||
}
|
||||
|
||||
static void on_set_entity_flag(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
||||
static void on_set_entity_set_flag(shared_ptr<Client> 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<G_SetEntityFlags_6x76>(data, size);
|
||||
const auto& cmd = check_size_t<G_SetEntitySetFlags_6x76>(data, size);
|
||||
if (l->map) {
|
||||
if (cmd.header.enemy_id >= 0x1000 && cmd.header.enemy_id < 0x4000) {
|
||||
if (cmd.header.enemy_id >= 0x4000) {
|
||||
uint16_t object_index = cmd.header.enemy_id - 0x4000;
|
||||
try {
|
||||
l->map->enemies.at(cmd.header.enemy_id - 0x1000).game_flags |= cmd.flags;
|
||||
uint16_t& set_flags = l->map->objects.at(object_index).set_flags;
|
||||
set_flags |= cmd.flags;
|
||||
l->log.info("Client set set flags %04hX on K-%hX (flags are now %04hX)",
|
||||
cmd.flags.load(), object_index, cmd.flags.load());
|
||||
} catch (const out_of_range&) {
|
||||
l->log.warning("Flag update refers to missing object");
|
||||
}
|
||||
|
||||
} else if (cmd.header.enemy_id >= 0x1000) {
|
||||
int32_t section = -1;
|
||||
int32_t wave_number = -1;
|
||||
uint16_t enemy_index = cmd.header.enemy_id - 0x1000;
|
||||
try {
|
||||
const auto& enemy = l->map->enemies.at(enemy_index);
|
||||
uint16_t& set_flags = l->map->enemy_set_flags.at(enemy.set_index);
|
||||
set_flags |= cmd.flags;
|
||||
section = enemy.section;
|
||||
wave_number = enemy.wave_number;
|
||||
l->log.info("Client set set flags %04hX on E-%hX (flags are now %04hX)",
|
||||
cmd.flags.load(), enemy_index, cmd.flags.load());
|
||||
} 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");
|
||||
|
||||
if ((section >= 0) && (wave_number >= 0)) {
|
||||
// When all enemies in a wave event have (set_flags & 8), which means
|
||||
// they are defeated, set event_flags = (event_flags | 0x18) & (~4),
|
||||
// which means it is done and should not trigger
|
||||
bool all_enemies_defeated = true;
|
||||
l->log.info("Checking for defeated enemies with section=%04" PRIX32 " wave_number=%04" PRIX32,
|
||||
section, wave_number);
|
||||
for (const Map::Enemy* enemy : l->map->get_enemies(cmd.floor, section, wave_number)) {
|
||||
if (!(l->map->enemy_set_flags.at(enemy->set_index) & 8)) {
|
||||
l->log.info("E-%hX is not defeated; cannot advance event to finished state", enemy->enemy_id);
|
||||
all_enemies_defeated = false;
|
||||
break;
|
||||
} else {
|
||||
l->log.info("E-%hX is defeated", enemy->enemy_id);
|
||||
}
|
||||
}
|
||||
if (all_enemies_defeated) {
|
||||
l->log.info("All enemies defeated; setting events with section=%04" PRIX32 " wave_number=%04" PRIX32 " to finished state",
|
||||
section, wave_number);
|
||||
for (Map::Event* event : l->map->get_events(cmd.floor, section, wave_number)) {
|
||||
event->flags = (event->flags | 0x18) & (~4);
|
||||
l->log.info("Set flags on W-%02hhX-%" PRIX32 " to %04hX", event->floor, event->event_id, event->flags);
|
||||
|
||||
StringReader actions_r(l->map->event_action_stream);
|
||||
actions_r.go(event->action_stream_offset);
|
||||
while (!actions_r.eof()) {
|
||||
uint8_t opcode = actions_r.get_u8();
|
||||
switch (opcode) {
|
||||
case 0x00: // nop
|
||||
l->log.info("(W-%02hhX-%" PRIX32 " script) nop", event->floor, event->event_id);
|
||||
break;
|
||||
case 0x01: // stop
|
||||
l->log.info("(W-%02hhX-%" PRIX32 " script) stop", event->floor, event->event_id);
|
||||
actions_r.go(actions_r.size());
|
||||
break;
|
||||
case 0x08: { // construct_objects
|
||||
uint16_t section = actions_r.get_u16l();
|
||||
uint16_t group = actions_r.get_u16l();
|
||||
l->log.info("(W-%02hhX-%" PRIX32 " script) construct_objects %04hX %04hX", event->floor, event->event_id, section, group);
|
||||
for (auto* obj : l->map->get_objects(event->floor, section, group)) {
|
||||
if (!(obj->set_flags & 0x0A)) {
|
||||
l->log.info("(W-%02hhX-%" PRIX32 " script) Setting flags 0012 on object K-%hX", event->floor, event->event_id, obj->object_id);
|
||||
obj->set_flags |= 0x12;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x09: // construct_enemies
|
||||
case 0x0D: { // construct_enemies_stop
|
||||
uint16_t section = actions_r.get_u16l();
|
||||
uint16_t wave_number = actions_r.get_u16l();
|
||||
l->log.info("(W-%02hhX-%" PRIX32 " script) construct_enemies %04hX %04hX", event->floor, event->event_id, section, wave_number);
|
||||
for (auto* enemy : l->map->get_enemies(event->floor, section, wave_number)) {
|
||||
uint16_t& set_flags = l->map->enemy_set_flags.at(enemy->set_index);
|
||||
if (!(set_flags & 0x0A)) {
|
||||
l->log.info("(W-%02hhX-%" PRIX32 " script) Setting flags 0002 on enemy set S-%zX (from E-%hX)", event->floor, event->event_id, enemy->set_index, enemy->enemy_id);
|
||||
set_flags |= 0x02;
|
||||
}
|
||||
}
|
||||
if (opcode == 0x0D) {
|
||||
actions_r.go(actions_r.size());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x0A: // enable_switch_flag
|
||||
case 0x0B: { // disable_switch_flag
|
||||
// These opcodes cause the client to send 6x05 commands, so
|
||||
// we don't have to do anything here.
|
||||
uint16_t switch_flag_num = actions_r.get_u16l();
|
||||
l->log.info("(W-%02hhX-%" PRIX32 " script) %sable_switch_flag %04hX",
|
||||
event->floor, event->event_id, (opcode & 1) ? "dis" : "en", switch_flag_num);
|
||||
break;
|
||||
}
|
||||
case 0x0C: { // trigger_event
|
||||
// This opcode causes the client to send a 6x67 command, so
|
||||
// we don't have to do anything here.
|
||||
uint32_t event_id = actions_r.get_u32l();
|
||||
l->log.info("(W-%02hhX-%" PRIX32 " script) trigger_event W-%02hhX-%" PRIX32,
|
||||
event->floor, event->event_id, event->floor, event_id);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
l->log.warning("(W-%02hhX-%" PRIX32 ") Invalid opcode %02hhX at offset %zX in event action stream",
|
||||
event->floor, event->event_id, opcode, actions_r.where() - 1);
|
||||
actions_r.go(actions_r.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2698,18 +2896,50 @@ static void on_trigger_set_event(shared_ptr<Client> c, uint8_t command, uint8_t
|
||||
|
||||
const auto& cmd = check_size_t<G_TriggerSetEvent_6x67>(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;
|
||||
l->log.info("Client triggered set event W-%02" PRIX32 "-%" PRIX32, cmd.floor.load(), cmd.event_id.load());
|
||||
} catch (const out_of_range&) {
|
||||
l->log.warning("Client triggered missing event W-%02" PRIX32 "-%" PRIX32, cmd.floor.load(), cmd.event_id.load());
|
||||
l->log.warning("Client triggered missing set event W-%02" PRIX32 "-%" PRIX32, cmd.floor.load(), cmd.event_id.load());
|
||||
}
|
||||
}
|
||||
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
}
|
||||
|
||||
static void on_unknown_6x91(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_Unknown_6x91>(data, size);
|
||||
auto l = c->require_lobby();
|
||||
if (!l->is_game()) {
|
||||
return;
|
||||
}
|
||||
if (l->switch_flags &&
|
||||
(cmd.should_set == 1) &&
|
||||
(cmd.switch_flag_num < 0x100) &&
|
||||
(cmd.switch_flag_floor < 0x12) &&
|
||||
(cmd.header.object_id >= 0x4000) &&
|
||||
(cmd.header.object_id != 0xFFFF)) {
|
||||
l->switch_flags->set(cmd.switch_flag_floor, cmd.switch_flag_num);
|
||||
}
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
}
|
||||
|
||||
static void on_activate_timed_switch(shared_ptr<Client> c, uint8_t command, uint8_t flag, void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_ActivateTimedSwitch_6x93>(data, size);
|
||||
auto l = c->require_lobby();
|
||||
if (!l->is_game()) {
|
||||
return;
|
||||
}
|
||||
if (l->switch_flags) {
|
||||
if (cmd.should_set == 1) {
|
||||
l->switch_flags->set(cmd.switch_flag_floor, cmd.switch_flag_num);
|
||||
} else {
|
||||
l->switch_flags->clear(cmd.switch_flag_floor, cmd.switch_flag_num);
|
||||
}
|
||||
}
|
||||
forward_subcommand(c, command, flag, data, size);
|
||||
}
|
||||
|
||||
static void on_battle_scores(shared_ptr<Client> c, uint8_t command, uint8_t, void* data, size_t size) {
|
||||
const auto& cmd = check_size_t<G_BattleScores_6x7F<false>>(data, size);
|
||||
|
||||
@@ -4126,8 +4356,8 @@ const SubcommandDefinition subcommand_definitions[0x100] = {
|
||||
/* 6x4B */ {0x40, 0x46, 0x4B, on_change_hp<G_ClientIDHeader>},
|
||||
/* 6x4C */ {0x41, 0x47, 0x4C, on_change_hp<G_ClientIDHeader>},
|
||||
/* 6x4D */ {0x42, 0x48, 0x4D, on_player_died},
|
||||
/* 6x4E */ {0x00, 0x00, 0x4E, on_forward_check_game_client},
|
||||
/* 6x4F */ {0x43, 0x49, 0x4F, on_forward_check_game_client},
|
||||
/* 6x4E */ {0x00, 0x00, 0x4E, on_player_revivable},
|
||||
/* 6x4F */ {0x43, 0x49, 0x4F, on_player_revived},
|
||||
/* 6x50 */ {0x44, 0x4A, 0x50, on_forward_check_game_client},
|
||||
/* 6x51 */ {0x00, 0x00, 0x51, on_invalid},
|
||||
/* 6x52 */ {0x46, 0x4C, 0x52, on_set_animation_state},
|
||||
@@ -4166,7 +4396,7 @@ const SubcommandDefinition subcommand_definitions[0x100] = {
|
||||
/* 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_set_entity_flag},
|
||||
/* 6x76 */ {0x00, 0x00, 0x76, on_set_entity_set_flag},
|
||||
/* 6x77 */ {0x00, 0x00, 0x77, on_forward_check_game},
|
||||
/* 6x78 */ {0x00, 0x00, 0x78, forward_subcommand_m},
|
||||
/* 6x79 */ {0x00, 0x00, 0x79, on_forward_check_lobby},
|
||||
@@ -4193,9 +4423,9 @@ const SubcommandDefinition subcommand_definitions[0x100] = {
|
||||
/* 6x8E */ {0x00, 0x00, 0x8E, on_forward_check_game},
|
||||
/* 6x8F */ {0x00, 0x00, 0x8F, on_forward_check_game},
|
||||
/* 6x90 */ {0x00, 0x00, 0x90, on_forward_check_game},
|
||||
/* 6x91 */ {0x00, 0x00, 0x91, on_forward_check_game},
|
||||
/* 6x91 */ {0x00, 0x00, 0x91, on_unknown_6x91},
|
||||
/* 6x92 */ {0x00, 0x00, 0x92, on_forward_check_game},
|
||||
/* 6x93 */ {0x00, 0x00, 0x93, on_forward_check_game},
|
||||
/* 6x93 */ {0x00, 0x00, 0x93, on_activate_timed_switch},
|
||||
/* 6x94 */ {0x00, 0x00, 0x94, on_warp},
|
||||
/* 6x95 */ {0x00, 0x00, 0x95, on_forward_check_game},
|
||||
/* 6x96 */ {0x00, 0x00, 0x96, on_forward_check_game},
|
||||
|
||||
+76
-12
@@ -477,6 +477,62 @@ void send_function_call(
|
||||
ch.send(0xB2, code ? code->index : 0x00, data);
|
||||
}
|
||||
|
||||
bool send_protected_command(std::shared_ptr<Client> c, const void* data, size_t size, bool echo_to_lobby) {
|
||||
switch (c->version()) {
|
||||
case Version::DC_NTE:
|
||||
case Version::DC_V1_11_2000_PROTOTYPE:
|
||||
case Version::DC_V1:
|
||||
case Version::DC_V2:
|
||||
case Version::PC_NTE:
|
||||
case Version::PC_V2:
|
||||
case Version::GC_NTE:
|
||||
if (echo_to_lobby) {
|
||||
send_command(c->require_lobby(), 0x60, 0x00, data, size);
|
||||
} else {
|
||||
send_command(c, 0x60, 0x00, data, size);
|
||||
}
|
||||
return true;
|
||||
|
||||
case Version::GC_V3:
|
||||
case Version::XB_V3:
|
||||
case Version::GC_EP3_NTE:
|
||||
case Version::GC_EP3: {
|
||||
auto s = c->require_server_state();
|
||||
if (!s->enable_v3_v4_protected_subcommands ||
|
||||
c->config.check_flag(Client::Flag::NO_SEND_FUNCTION_CALL) ||
|
||||
c->config.check_flag(Client::Flag::SEND_FUNCTION_CALL_CHECKSUM_ONLY)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prepare_client_for_patches(c, [wc = weak_ptr<Client>(c), data = string(reinterpret_cast<const char*>(data), size), echo_to_lobby]() {
|
||||
auto c = wc.lock();
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
auto s = c->require_server_state();
|
||||
auto fn = s->function_code_index->get_patch("CallProtectedHandler", c->config.specific_version);
|
||||
uint32_t size_label_value = is_big_endian(c->version()) ? data.size() : bswap32(data.size());
|
||||
send_function_call(c, fn, {{"size", size_label_value}}, data);
|
||||
c->function_call_response_queue.emplace_back(empty_function_call_response_handler);
|
||||
if (echo_to_lobby) {
|
||||
auto l = c->lobby.lock();
|
||||
if (l) {
|
||||
send_command_excluding_client(l, c, 0x60, 0x00, data.data(), data.size());
|
||||
}
|
||||
}
|
||||
} catch (const exception& e) {
|
||||
c->log.warning("Failed to send protected command: %s", e.what());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void send_reconnect(shared_ptr<Client> c, uint32_t address, uint16_t port) {
|
||||
S_Reconnect_19 cmd = {{address, port, 0}};
|
||||
send_command_t(c, is_patch(c->version()) ? 0x14 : 0x19, 0x00, cmd);
|
||||
@@ -2354,12 +2410,14 @@ void send_player_stats_change(Channel& ch, uint16_t client_id, PlayerStatsChange
|
||||
}
|
||||
|
||||
void send_remove_conditions(shared_ptr<Client> c) {
|
||||
auto l = c->require_lobby();
|
||||
for (auto& lc : l->clients) {
|
||||
if (lc) {
|
||||
send_remove_conditions(lc->channel, c->lobby_client_id);
|
||||
}
|
||||
parray<G_AddOrRemoveCondition_6x0C_6x0D, 4> cmds;
|
||||
for (size_t z = 0; z < 4; z++) {
|
||||
auto& cmd = cmds[z];
|
||||
cmd.header = {0x0D, sizeof(G_AddOrRemoveCondition_6x0C_6x0D) >> 2, c->lobby_client_id};
|
||||
cmd.unknown_a1 = z;
|
||||
cmd.unknown_a2 = 0;
|
||||
}
|
||||
send_protected_command(c, &cmds, sizeof(cmds), true);
|
||||
}
|
||||
|
||||
void send_remove_conditions(Channel& ch, uint16_t client_id) {
|
||||
@@ -2381,6 +2439,7 @@ void send_warp(Channel& ch, uint8_t client_id, uint32_t floor, bool is_private)
|
||||
void send_warp(shared_ptr<Client> c, uint32_t floor, bool is_private) {
|
||||
send_warp(c->channel, c->lobby_client_id, floor, is_private);
|
||||
c->floor = floor;
|
||||
c->recent_switch_flags.clear();
|
||||
}
|
||||
|
||||
void send_warp(shared_ptr<Lobby> l, uint32_t floor, bool is_private) {
|
||||
@@ -2557,8 +2616,8 @@ void send_game_set_state(shared_ptr<Client> c) {
|
||||
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;
|
||||
header.switch_flags_size = is_v1(c->version()) ? 0x200 : 0x240;
|
||||
header.total_size = header.entity_set_flags_size + header.event_set_flags_size + header.switch_flags_size;
|
||||
|
||||
StringWriter w;
|
||||
w.put(header);
|
||||
@@ -2572,7 +2631,12 @@ void send_game_set_state(shared_ptr<Client> c) {
|
||||
for (const auto& event : l->map->events) {
|
||||
w.put_u16l(event.flags);
|
||||
}
|
||||
w.extend_by(header.unused_size, 0x00);
|
||||
if (l->switch_flags) {
|
||||
static_assert(sizeof(SwitchFlags) == 0x240, "switch_flags size is incorrect");
|
||||
w.write(l->switch_flags->data.data(), header.switch_flags_size);
|
||||
} else {
|
||||
w.extend_by(header.switch_flags_size, 0x00);
|
||||
}
|
||||
|
||||
send_game_join_sync_command(c, w.str(), 0x5F, 0x66, 0x6E);
|
||||
}
|
||||
@@ -2632,10 +2696,10 @@ void send_game_flag_state_t(shared_ptr<Client> c) {
|
||||
|
||||
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<const char*>(&cmd), sizeof(cmd));
|
||||
auto& queue_cmd = c->game_join_command_queue->emplace_back();
|
||||
queue_cmd.command = 0x0062;
|
||||
queue_cmd.flag = c->lobby_client_id;
|
||||
queue_cmd.data.assign(reinterpret_cast<const char*>(&cmd), sizeof(cmd));
|
||||
} else {
|
||||
send_command_t(c, 0x62, c->lobby_client_id, cmd);
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ void send_function_call(
|
||||
uint32_t checksum_addr = 0,
|
||||
uint32_t checksum_size = 0,
|
||||
uint32_t override_relocations_offset = 0);
|
||||
bool send_protected_command(std::shared_ptr<Client> c, const void* data, size_t size, bool echo_to_lobby);
|
||||
|
||||
void send_reconnect(std::shared_ptr<Client> c, uint32_t address, uint16_t port);
|
||||
void send_pc_console_split_reconnect(
|
||||
|
||||
+5
-3
@@ -734,6 +734,7 @@ void ServerState::load_config_early() {
|
||||
this->default_rare_notifs_enabled_v1_v2 = this->config_json->get_bool("RareNotificationsEnabledByDefaultV1V2", this->default_rare_notifs_enabled_v1_v2);
|
||||
this->default_rare_notifs_enabled_v3_v4 = this->config_json->get_bool("RareNotificationsEnabledByDefaultV3V4", this->default_rare_notifs_enabled_v3_v4);
|
||||
this->ep3_send_function_call_enabled = this->config_json->get_bool("EnableEpisode3SendFunctionCall", false);
|
||||
this->enable_v3_v4_protected_subcommands = this->config_json->get_bool("EnableV3V4ProtectedSubcommands", false);
|
||||
this->catch_handler_exceptions = this->config_json->get_bool("CatchHandlerExceptions", true);
|
||||
|
||||
auto parse_int_list = +[](const JSON& json) -> vector<uint32_t> {
|
||||
@@ -1139,14 +1140,15 @@ void ServerState::load_config_late() {
|
||||
for (size_t trap_type = 0; trap_type < 5; trap_type++) {
|
||||
auto& trap_card_ids = this->ep3_trap_card_ids[trap_type];
|
||||
for (const auto& card_it : ep3_trap_cards_json.at(trap_type)->as_list()) {
|
||||
const string& card_name = card_it->as_string();
|
||||
try {
|
||||
const auto& card = this->ep3_card_index->definition_for_name_normalized(card_it->as_string());
|
||||
const auto& card = this->ep3_card_index->definition_for_name_normalized(card_name);
|
||||
if (card->def.type != Episode3::CardType::ASSIST) {
|
||||
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list is not an assist card", name.c_str()));
|
||||
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list is not an assist card", card_name.c_str()));
|
||||
}
|
||||
trap_card_ids.emplace_back(card->def.card_id);
|
||||
} catch (const out_of_range&) {
|
||||
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list does not exist", name.c_str()));
|
||||
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list does not exist", card_name.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
QuestFlagsForDifficulty quest_flag_persist_mask;
|
||||
uint64_t persistent_game_idle_timeout_usecs = 0;
|
||||
bool ep3_send_function_call_enabled = false;
|
||||
bool enable_v3_v4_protected_subcommands = false;
|
||||
bool catch_handler_exceptions = true;
|
||||
bool ep3_infinite_meseta = false;
|
||||
std::vector<uint32_t> ep3_defeat_player_meseta_rewards = {400, 500, 600, 700, 800};
|
||||
|
||||
@@ -775,6 +775,19 @@ const char* name_for_floor(Episode episode, uint8_t floor) {
|
||||
}
|
||||
}
|
||||
|
||||
bool floor_is_boss_arena(Episode episode, uint8_t floor) {
|
||||
switch (episode) {
|
||||
case Episode::EP1:
|
||||
return (floor >= 0x0B) && (floor <= 0x0E);
|
||||
case Episode::EP2:
|
||||
return (floor >= 0x0C) && (floor <= 0x0F);
|
||||
case Episode::EP4:
|
||||
return (floor == 0x09);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t class_flags_for_class(uint8_t char_class) {
|
||||
static constexpr uint8_t flags[12] = {
|
||||
0x25, 0x2A, 0x31, 0x45, 0x51, 0x52, 0x86, 0x89, 0x8A, 0x32, 0x85, 0x46};
|
||||
|
||||
@@ -76,6 +76,7 @@ extern const std::unordered_map<std::string, uint8_t> mag_color_for_name;
|
||||
size_t floor_limit_for_episode(Episode ep);
|
||||
uint8_t floor_for_name(const std::string& name);
|
||||
const char* name_for_floor(Episode episode, uint8_t floor);
|
||||
bool floor_is_boss_arena(Episode episode, uint8_t floor);
|
||||
|
||||
uint32_t class_flags_for_class(uint8_t char_class);
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ TextTranscoder::Result TextTranscoder::operator()(
|
||||
if (!truncate_oversize_result) {
|
||||
throw runtime_error("string does not fit in buffer");
|
||||
} else {
|
||||
src_bytes = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerGC
|
||||
.data 0x805C5650
|
||||
.data 0x801E3F9C
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerGC
|
||||
.data 0x805CC630
|
||||
.data 0x801E3F9C
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerGC
|
||||
.data 0x805D5E50
|
||||
.data 0x801E405C
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerGC
|
||||
.data 0x805C4D58
|
||||
.data 0x801E3B38
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerGC
|
||||
.data 0x805CF320
|
||||
.data 0x801E40BC
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerGC
|
||||
.data 0x805D67A0
|
||||
.data 0x801E4290
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerGC
|
||||
.data 0x805D6540
|
||||
.data 0x801E4008
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerGC
|
||||
.data 0x805D2090
|
||||
.data 0x801E4698
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerXB
|
||||
.data 0x00723F68
|
||||
.data 0x002DDB00
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerXB
|
||||
.data 0x007237E8
|
||||
.data 0x002DE000
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerXB
|
||||
.data 0x0071E8C8
|
||||
.data 0x002DBBA0
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerXB
|
||||
.data 0x0071EF28
|
||||
.data 0x002DC720
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerXB
|
||||
.data 0x00726A68
|
||||
.data 0x002DDFE0
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerXB
|
||||
.data 0x00723F68
|
||||
.data 0x002DDB30
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,14 @@
|
||||
.meta hide_from_patches_menu
|
||||
.meta name="CallProtectedHandler"
|
||||
.meta description=""
|
||||
|
||||
entry_ptr:
|
||||
reloc0:
|
||||
.offsetof start
|
||||
start:
|
||||
.include CallProtectedHandlerXB
|
||||
.data 0x007242E8
|
||||
.data 0x002DE030
|
||||
size:
|
||||
.data 0x00000000
|
||||
data:
|
||||
@@ -0,0 +1,32 @@
|
||||
stwu [r1 - 0x10], r1
|
||||
mflr r0
|
||||
stw [r1 + 0x14], r0
|
||||
stw [r1 + 0x08], r31
|
||||
stw [r1 + 0x0C], r30
|
||||
|
||||
b get_data_addr
|
||||
resume:
|
||||
mflr r31
|
||||
|
||||
lwz r30, [r31]
|
||||
li r0, 1
|
||||
stw [r30], r0
|
||||
|
||||
addi r3, r31, 0x0C
|
||||
lwz r4, [r31 + 8]
|
||||
lwz r0, [r31 + 4]
|
||||
mtctr r0
|
||||
bctrl
|
||||
|
||||
li r0, 0
|
||||
stw [r30], r0
|
||||
|
||||
lwz r30, [r1 + 0x0C]
|
||||
lwz r31, [r1 + 0x08]
|
||||
lwz r0, [r1 + 0x14]
|
||||
mtlr r0
|
||||
addi r1, r1, 0x10
|
||||
blr
|
||||
|
||||
get_data_addr:
|
||||
bl resume
|
||||
@@ -0,0 +1,20 @@
|
||||
jmp get_data_addr
|
||||
resume:
|
||||
xchg ebx, [esp]
|
||||
|
||||
mov edx, [ebx]
|
||||
mov dword [edx], 1
|
||||
|
||||
mov edx, [ebx + 4]
|
||||
lea ecx, [ebx + 0x0C]
|
||||
mov eax, [ebx + 8]
|
||||
call edx
|
||||
|
||||
mov edx, [ebx]
|
||||
mov dword [edx], 0
|
||||
|
||||
pop ebx
|
||||
ret
|
||||
|
||||
get_data_addr:
|
||||
call resume
|
||||
@@ -966,7 +966,13 @@
|
||||
// exploiting a bug in Episode 3, and while it seems to work reliably on
|
||||
// Dolphin, it hasn't been tested on a real GameCube. So, newserv doesn't
|
||||
// enable Episode 3 USA patches by default; it only does if this option is on.
|
||||
// "EnableEpisode3SendFunctionCall": true,
|
||||
"EnableEpisode3SendFunctionCall": false,
|
||||
|
||||
// Whether to enable protected subcommands on GC and Xbox. This enables the
|
||||
// infinite HP cheat to also automatically revive players and clear conditions
|
||||
// like poison and freeze. (On v1 and v2, those functions are always enabled
|
||||
// in infinite HP mode.)
|
||||
"EnableV3V4ProtectedSubcommands": false,
|
||||
|
||||
// Whether to allow cross-play for various game versions. DCv1 and DCv2 are
|
||||
// always allowed to join each other's games (though DCv2 can deny permission
|
||||
|
||||
@@ -968,6 +968,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001005, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000406, "VJAYA"]],
|
||||
"Box-Forest2": [["1/256", 0x000405, "BRIONAC"]],
|
||||
@@ -981,6 +982,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001101, "SOUL BANISH"]],
|
||||
@@ -1018,6 +1020,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/2097152", 0x030C03, "Heart of Opa Opa"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["9/65536", 0x000E02, "TWIN BRAND"]],
|
||||
"Box-Caves2": [["9/16384", 0x001003, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000706, "WALS-MK2"]],
|
||||
"Box-Forest2": [["1/256", 0x000705, "VISK-235W"]],
|
||||
@@ -1031,6 +1034,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021C, "RANGER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001500, "FLAME VISIT"]],
|
||||
@@ -1068,6 +1072,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/512", 0x001005, "AGITO"]],
|
||||
"Box-Caves2": [["9/16384", 0x001004, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000306, "BLOODY ART"]],
|
||||
"Box-Forest2": [["1/256", 0x000305, "BLADE DANCE"]],
|
||||
@@ -1081,6 +1086,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["7/33554432", 0x001001, "AGITO"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x000E02, "TWIN BRAND"]],
|
||||
@@ -1118,6 +1124,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001002, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000C05, "ICE STAFF:DAGON"]],
|
||||
"Box-Forest2": [["1/256", 0x000C04, "FIRE SCEPTER:AGNI"]],
|
||||
@@ -1131,6 +1138,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021C, "RANGER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002400, "MAGICAL PIECE"]],
|
||||
@@ -1168,6 +1176,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001006, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000806, "H&S25 JUSTICE"]],
|
||||
"Box-Forest2": [["1/256", 0x000805, "M&A60 VISE"]],
|
||||
@@ -1176,11 +1185,12 @@
|
||||
"CANADINE": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANADINE_GROUP": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANANE": [["7/16384", 0x010123, "SENSE PLATE"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsabre's Left Arm"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsaber's Left Arm"]],
|
||||
"CHAOS_SORCERER": [["9/2097152", 0x030D00, "Sorcerer's Right Arm"]],
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021B, "FORCE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001400, "INFERNO BAZOOKA"]],
|
||||
@@ -1218,6 +1228,7 @@
|
||||
"BARBAROUS_WOLF": [["1/32768", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/4096", 0x010121, "PARASITE WEAR:Nelgal"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001003, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x010119, "RANGER FIELD"]],
|
||||
"Box-Forest2": [["1/256", 0x010118, "HUNTER FIELD"]],
|
||||
@@ -1231,6 +1242,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["1/32768", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010218, "S-PARTS ver2.01"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x010122, "PARASITE WEAR:Vajulla"]],
|
||||
@@ -1268,6 +1280,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["13/536870912", 0x001001, "AGITO"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/512", 0x001005, "AGITO"]],
|
||||
"Box-Caves2": [["9/16384", 0x001004, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000206, "LAST SURVIVOR"]],
|
||||
"Box-Forest2": [["1/256", 0x000205, "FLOWEN'S SWORD"]],
|
||||
@@ -1281,6 +1294,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["7/33554432", 0x001001, "AGITO"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002100, "CHAIN SAWD"]],
|
||||
@@ -1318,6 +1332,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x000905, "CRUSH BULLET"]],
|
||||
"Box-Caves2": [["9/16384", 0x001002, "AGITO"]],
|
||||
"Box-Caves3": [["7/2048", 0x000906, "METEOR SMASH"]],
|
||||
"Box-Forest2": [["3/1024", 0x000905, "CRUSH BULLET"]],
|
||||
@@ -1331,6 +1346,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021B, "FORCE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001200, "SPREAD NEEDLE"]],
|
||||
@@ -1368,6 +1384,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/2097152", 0x030C03, "Heart of Opa Opa"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["9/65536", 0x002100, "CHAIN SAWD"]],
|
||||
"Box-Caves2": [["9/16384", 0x001006, "AGITO"]],
|
||||
"Box-Caves3": [["7/2048", 0x000506, "DISKA OF LIBERATOR"]],
|
||||
"Box-Forest2": [["3/1024", 0x000505, "SLICER OF ASSASSIN"]],
|
||||
@@ -1381,6 +1398,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x000D01, "SILENCE CLAW"]],
|
||||
@@ -1418,6 +1436,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001005, "AGITO"]],
|
||||
"Box-Caves3": [["3/512", 0x000605, "VARISTA"]],
|
||||
"Box-Forest2": [["5/1024", 0x000106, "KALADBOLG"]],
|
||||
@@ -1426,11 +1445,12 @@
|
||||
"CANADINE": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANADINE_GROUP": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANANE": [["7/16384", 0x010124, "GRAVITON PLATE"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsabre's Left Arm"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsaber's Left Arm"]],
|
||||
"CHAOS_SORCERER": [["9/2097152", 0x030D00, "Sorcerer's Right Arm"]],
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002300, "STING TIP"]],
|
||||
@@ -2443,6 +2463,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001005, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000406, "VJAYA"]],
|
||||
"Box-Forest2": [["1/256", 0x000405, "BRIONAC"]],
|
||||
@@ -2456,6 +2477,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001101, "SOUL BANISH"]],
|
||||
@@ -2493,6 +2515,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/2097152", 0x030C03, "Heart of Opa Opa"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["9/65536", 0x000E02, "TWIN BRAND"]],
|
||||
"Box-Caves2": [["9/16384", 0x001003, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000706, "WALS-MK2"]],
|
||||
"Box-Forest2": [["1/256", 0x000705, "VISK-235W"]],
|
||||
@@ -2506,6 +2529,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021C, "RANGER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001500, "FLAME VISIT"]],
|
||||
@@ -2543,6 +2567,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/512", 0x001005, "AGITO"]],
|
||||
"Box-Caves2": [["9/16384", 0x001004, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000306, "BLOODY ART"]],
|
||||
"Box-Forest2": [["1/256", 0x000305, "BLADE DANCE"]],
|
||||
@@ -2556,6 +2581,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["7/33554432", 0x001001, "AGITO"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x000E02, "TWIN BRAND"]],
|
||||
@@ -2593,6 +2619,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001002, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000C05, "ICE STAFF:DAGON"]],
|
||||
"Box-Forest2": [["1/256", 0x000C04, "FIRE SCEPTER:AGNI"]],
|
||||
@@ -2606,6 +2633,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021C, "RANGER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002400, "MAGICAL PIECE"]],
|
||||
@@ -2643,6 +2671,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001006, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000806, "H&S25 JUSTICE"]],
|
||||
"Box-Forest2": [["1/256", 0x000805, "M&A60 VISE"]],
|
||||
@@ -2651,11 +2680,12 @@
|
||||
"CANADINE": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANADINE_GROUP": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANANE": [["7/16384", 0x010123, "SENSE PLATE"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsabre's Left Arm"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsaber's Left Arm"]],
|
||||
"CHAOS_SORCERER": [["9/2097152", 0x030D00, "Sorcerer's Right Arm"]],
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021B, "FORCE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001400, "INFERNO BAZOOKA"]],
|
||||
@@ -2693,6 +2723,7 @@
|
||||
"BARBAROUS_WOLF": [["1/32768", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/4096", 0x010121, "PARASITE WEAR:Nelgal"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001003, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x010119, "RANGER FIELD"]],
|
||||
"Box-Forest2": [["1/256", 0x010118, "HUNTER FIELD"]],
|
||||
@@ -2706,6 +2737,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["1/32768", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010218, "S-PARTS ver2.01"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x010122, "PARASITE WEAR:Vajulla"]],
|
||||
@@ -2743,6 +2775,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["13/536870912", 0x001001, "AGITO"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/512", 0x001005, "AGITO"]],
|
||||
"Box-Caves2": [["9/16384", 0x001004, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000206, "LAST SURVIVOR"]],
|
||||
"Box-Forest2": [["1/256", 0x000205, "FLOWEN'S SWORD"]],
|
||||
@@ -2756,6 +2789,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["7/33554432", 0x001001, "AGITO"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002100, "CHAIN SAWD"]],
|
||||
@@ -2793,6 +2827,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x000905, "CRUSH BULLET"]],
|
||||
"Box-Caves2": [["9/16384", 0x001002, "AGITO"]],
|
||||
"Box-Caves3": [["7/2048", 0x000906, "METEOR SMASH"]],
|
||||
"Box-Forest2": [["3/1024", 0x000905, "CRUSH BULLET"]],
|
||||
@@ -2806,6 +2841,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021B, "FORCE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001200, "SPREAD NEEDLE"]],
|
||||
@@ -2843,6 +2879,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/2097152", 0x030C03, "Heart of Opa Opa"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["9/65536", 0x002100, "CHAIN SAWD"]],
|
||||
"Box-Caves2": [["9/16384", 0x001006, "AGITO"]],
|
||||
"Box-Caves3": [["7/2048", 0x000506, "DISKA OF LIBERATOR"]],
|
||||
"Box-Forest2": [["3/1024", 0x000505, "SLICER OF ASSASSIN"]],
|
||||
@@ -2856,6 +2893,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x000D01, "SILENCE CLAW"]],
|
||||
@@ -2893,6 +2931,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001005, "AGITO"]],
|
||||
"Box-Caves3": [["3/512", 0x000605, "VARISTA"]],
|
||||
"Box-Forest2": [["5/1024", 0x000106, "KALADBOLG"]],
|
||||
@@ -2901,11 +2940,12 @@
|
||||
"CANADINE": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANADINE_GROUP": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANANE": [["7/16384", 0x010124, "GRAVITON PLATE"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsabre's Left Arm"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsaber's Left Arm"]],
|
||||
"CHAOS_SORCERER": [["9/2097152", 0x030D00, "Sorcerer's Right Arm"]],
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002300, "STING TIP"]],
|
||||
@@ -3918,6 +3958,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001005, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000406, "VJAYA"]],
|
||||
"Box-Forest2": [["1/256", 0x000405, "BRIONAC"]],
|
||||
@@ -3931,6 +3972,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001101, "SOUL BANISH"]],
|
||||
@@ -3968,6 +4010,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/2097152", 0x030C03, "Heart of Opa Opa"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["9/65536", 0x000E02, "TWIN BRAND"]],
|
||||
"Box-Caves2": [["9/16384", 0x001003, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000706, "WALS-MK2"]],
|
||||
"Box-Forest2": [["1/256", 0x000705, "VISK-235W"]],
|
||||
@@ -3981,6 +4024,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021C, "RANGER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001500, "FLAME VISIT"]],
|
||||
@@ -4018,6 +4062,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/512", 0x001005, "AGITO"]],
|
||||
"Box-Caves2": [["9/16384", 0x001004, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000306, "BLOODY ART"]],
|
||||
"Box-Forest2": [["1/256", 0x000305, "BLADE DANCE"]],
|
||||
@@ -4031,6 +4076,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["7/33554432", 0x001001, "AGITO"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x000E02, "TWIN BRAND"]],
|
||||
@@ -4068,6 +4114,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001002, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000C05, "ICE STAFF:DAGON"]],
|
||||
"Box-Forest2": [["1/256", 0x000C04, "FIRE SCEPTER:AGNI"]],
|
||||
@@ -4081,6 +4128,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021C, "RANGER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002400, "MAGICAL PIECE"]],
|
||||
@@ -4118,6 +4166,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001006, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000806, "H&S25 JUSTICE"]],
|
||||
"Box-Forest2": [["1/256", 0x000805, "M&A60 VISE"]],
|
||||
@@ -4126,11 +4175,12 @@
|
||||
"CANADINE": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANADINE_GROUP": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANANE": [["7/16384", 0x010123, "SENSE PLATE"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsabre's Left Arm"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsaber's Left Arm"]],
|
||||
"CHAOS_SORCERER": [["9/2097152", 0x030D00, "Sorcerer's Right Arm"]],
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021B, "FORCE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001400, "INFERNO BAZOOKA"]],
|
||||
@@ -4168,6 +4218,7 @@
|
||||
"BARBAROUS_WOLF": [["1/32768", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/4096", 0x010121, "PARASITE WEAR:Nelgal"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001003, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x010119, "RANGER FIELD"]],
|
||||
"Box-Forest2": [["1/256", 0x010118, "HUNTER FIELD"]],
|
||||
@@ -4181,6 +4232,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["1/32768", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010218, "S-PARTS ver2.01"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x010122, "PARASITE WEAR:Vajulla"]],
|
||||
@@ -4218,6 +4270,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["13/536870912", 0x001001, "AGITO"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/512", 0x001005, "AGITO"]],
|
||||
"Box-Caves2": [["9/16384", 0x001004, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000206, "LAST SURVIVOR"]],
|
||||
"Box-Forest2": [["1/256", 0x000205, "FLOWEN'S SWORD"]],
|
||||
@@ -4231,6 +4284,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["7/33554432", 0x001001, "AGITO"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002100, "CHAIN SAWD"]],
|
||||
@@ -4268,6 +4322,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x000905, "CRUSH BULLET"]],
|
||||
"Box-Caves2": [["9/16384", 0x001002, "AGITO"]],
|
||||
"Box-Caves3": [["7/2048", 0x000906, "METEOR SMASH"]],
|
||||
"Box-Forest2": [["3/1024", 0x000905, "CRUSH BULLET"]],
|
||||
@@ -4281,6 +4336,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021B, "FORCE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001200, "SPREAD NEEDLE"]],
|
||||
@@ -4318,6 +4374,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/2097152", 0x030C03, "Heart of Opa Opa"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["9/65536", 0x002100, "CHAIN SAWD"]],
|
||||
"Box-Caves2": [["9/16384", 0x001006, "AGITO"]],
|
||||
"Box-Caves3": [["7/2048", 0x000506, "DISKA OF LIBERATOR"]],
|
||||
"Box-Forest2": [["3/1024", 0x000505, "SLICER OF ASSASSIN"]],
|
||||
@@ -4331,6 +4388,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x000D01, "SILENCE CLAW"]],
|
||||
@@ -4368,6 +4426,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001005, "AGITO"]],
|
||||
"Box-Caves3": [["3/512", 0x000605, "VARISTA"]],
|
||||
"Box-Forest2": [["5/1024", 0x000106, "KALADBOLG"]],
|
||||
@@ -4376,11 +4435,12 @@
|
||||
"CANADINE": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANADINE_GROUP": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANANE": [["7/16384", 0x010124, "GRAVITON PLATE"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsabre's Left Arm"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsaber's Left Arm"]],
|
||||
"CHAOS_SORCERER": [["9/2097152", 0x030D00, "Sorcerer's Right Arm"]],
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002300, "STING TIP"]],
|
||||
@@ -5393,6 +5453,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001005, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000406, "VJAYA"]],
|
||||
"Box-Forest2": [["1/256", 0x000405, "BRIONAC"]],
|
||||
@@ -5406,6 +5467,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001101, "SOUL BANISH"]],
|
||||
@@ -5443,6 +5505,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/2097152", 0x030C03, "Heart of Opa Opa"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["9/65536", 0x000E02, "TWIN BRAND"]],
|
||||
"Box-Caves2": [["9/16384", 0x001003, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000706, "WALS-MK2"]],
|
||||
"Box-Forest2": [["1/256", 0x000705, "VISK-235W"]],
|
||||
@@ -5456,6 +5519,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021C, "RANGER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001500, "FLAME VISIT"]],
|
||||
@@ -5493,6 +5557,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/512", 0x001005, "AGITO"]],
|
||||
"Box-Caves2": [["9/16384", 0x001004, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000306, "BLOODY ART"]],
|
||||
"Box-Forest2": [["1/256", 0x000305, "BLADE DANCE"]],
|
||||
@@ -5506,6 +5571,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["7/33554432", 0x001001, "AGITO"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x000E02, "TWIN BRAND"]],
|
||||
@@ -5543,6 +5609,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001002, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000C05, "ICE STAFF:DAGON"]],
|
||||
"Box-Forest2": [["1/256", 0x000C04, "FIRE SCEPTER:AGNI"]],
|
||||
@@ -5556,6 +5623,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021C, "RANGER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002400, "MAGICAL PIECE"]],
|
||||
@@ -5593,6 +5661,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001006, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000806, "H&S25 JUSTICE"]],
|
||||
"Box-Forest2": [["1/256", 0x000805, "M&A60 VISE"]],
|
||||
@@ -5601,11 +5670,12 @@
|
||||
"CANADINE": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANADINE_GROUP": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANANE": [["7/16384", 0x010123, "SENSE PLATE"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsabre's Left Arm"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsaber's Left Arm"]],
|
||||
"CHAOS_SORCERER": [["9/2097152", 0x030D00, "Sorcerer's Right Arm"]],
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021B, "FORCE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001400, "INFERNO BAZOOKA"]],
|
||||
@@ -5643,6 +5713,7 @@
|
||||
"BARBAROUS_WOLF": [["1/32768", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/4096", 0x010121, "PARASITE WEAR:Nelgal"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001003, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x010119, "RANGER FIELD"]],
|
||||
"Box-Forest2": [["1/256", 0x010118, "HUNTER FIELD"]],
|
||||
@@ -5656,6 +5727,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["1/32768", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021E, "ATTRIBUTE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010218, "S-PARTS ver2.01"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x010122, "PARASITE WEAR:Vajulla"]],
|
||||
@@ -5693,6 +5765,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["13/536870912", 0x001001, "AGITO"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/512", 0x001005, "AGITO"]],
|
||||
"Box-Caves2": [["9/16384", 0x001004, "AGITO"]],
|
||||
"Box-Caves3": [["5/1024", 0x000206, "LAST SURVIVOR"]],
|
||||
"Box-Forest2": [["1/256", 0x000205, "FLOWEN'S SWORD"]],
|
||||
@@ -5706,6 +5779,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["7/33554432", 0x001001, "AGITO"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002100, "CHAIN SAWD"]],
|
||||
@@ -5743,6 +5817,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x000905, "CRUSH BULLET"]],
|
||||
"Box-Caves2": [["9/16384", 0x001002, "AGITO"]],
|
||||
"Box-Caves3": [["7/2048", 0x000906, "METEOR SMASH"]],
|
||||
"Box-Forest2": [["3/1024", 0x000905, "CRUSH BULLET"]],
|
||||
@@ -5756,6 +5831,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021B, "FORCE WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["5/512", 0x030D03, "Delsaber's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x001200, "SPREAD NEEDLE"]],
|
||||
@@ -5793,6 +5869,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["7/2097152", 0x030C03, "Heart of Opa Opa"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["9/65536", 0x002100, "CHAIN SAWD"]],
|
||||
"Box-Caves2": [["9/16384", 0x001006, "AGITO"]],
|
||||
"Box-Caves3": [["7/2048", 0x000506, "DISKA OF LIBERATOR"]],
|
||||
"Box-Forest2": [["3/1024", 0x000505, "SLICER OF ASSASSIN"]],
|
||||
@@ -5806,6 +5883,7 @@
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x000D01, "SILENCE CLAW"]],
|
||||
@@ -5843,6 +5921,7 @@
|
||||
"BARBAROUS_WOLF": [["9/2097152", 0x010214, "Celestial Shield"]],
|
||||
"BOOMA": [["11/1024", 0x000F00, "BRAVE KNUCKLE"]],
|
||||
"BULCLAW": [["3/262144", 0x000F02, "GOD HAND"]],
|
||||
"BULK": [["5/2048", 0x002600, "SUPPRESSED GUN"]],
|
||||
"Box-Caves2": [["9/16384", 0x001005, "AGITO"]],
|
||||
"Box-Caves3": [["3/512", 0x000605, "VARISTA"]],
|
||||
"Box-Forest2": [["5/1024", 0x000106, "KALADBOLG"]],
|
||||
@@ -5851,11 +5930,12 @@
|
||||
"CANADINE": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANADINE_GROUP": [["13/524288", 0x010222, "REGENERATE GEAR"]],
|
||||
"CANANE": [["7/16384", 0x010124, "GRAVITON PLATE"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsabre's Left Arm"]],
|
||||
"CHAOS_BRINGER": [["1/2097152", 0x030D05, "Delsaber's Left Arm"]],
|
||||
"CHAOS_SORCERER": [["9/2097152", 0x030D00, "Sorcerer's Right Arm"]],
|
||||
"CLAW": [["1/32768", 0x001200, "SPREAD NEEDLE"]],
|
||||
"DARK_BELRA": [["9/2097152", 0x010117, "Celestial Armor"]],
|
||||
"DARK_GUNNER": [["9/65536", 0x01021D, "HUNTER WALL"]],
|
||||
"DEATH_GUNNER": [["7/16384", 0x010217, "S-PARTS ver1.16"]],
|
||||
"DELSABER": [["3/4096", 0x030D04, "C-bringer's Right Arm"]],
|
||||
"DE_ROL_LE": [["3/8", 0x010120, "PARASITE WEAR:De Rol"]],
|
||||
"DIMENIAN": [["13/16384", 0x002300, "STING TIP"]],
|
||||
|
||||
+8851
-1953
File diff suppressed because it is too large
Load Diff
@@ -6244,6 +6244,8 @@ I 56327 2024-03-03 23:40:27 - [Commands] Received from C-2 (Jess) (version=GC_V3
|
||||
0010 | 00 00 00 00 |
|
||||
I 56327 2024-03-03 23:40:27 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 05 03 5A 42 00 00 00 00 65 00 03 01 | ` ZB e
|
||||
I 56327 2024-03-03 23:40:27 - [Commands] Sending to C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 05 03 FF FF 00 00 00 00 65 00 03 01 | ` e
|
||||
I 56327 2024-03-03 23:40:27 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 42 03 00 00 89 8E 65 C4 32 06 7B 43 | ` B e 2 {C
|
||||
I 56327 2024-03-03 23:40:27 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
@@ -6260,7 +6262,8 @@ I 56327 2024-03-03 23:40:27 - [Commands] Received from C-2 (Jess) (version=GC_V3
|
||||
0000 | 60 00 10 00 05 03 59 42 00 00 00 00 64 00 03 01 | ` YB d
|
||||
I 56327 2024-03-03 23:40:27 - [C-2] [Switch assist] Replaying previous enable command
|
||||
I 56327 2024-03-03 23:40:27 - [Commands] Sending to C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 05 03 5A 42 00 00 00 00 65 00 03 01 | ` ZB e
|
||||
0000 | 60 00 1C 00 05 03 FF FF 00 00 00 00 64 00 03 01 | ` d
|
||||
0010 | 05 03 FF FF 00 00 00 00 65 00 03 01 | e
|
||||
I 56327 2024-03-03 23:40:28 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 0B 03 58 42 01 00 00 00 58 02 00 00 | ` XB X
|
||||
I 56327 2024-03-03 23:40:28 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
@@ -11671,7 +11674,9 @@ I 56327 2024-03-03 23:45:37 - [Commands] Received from C-2 (Jess) (version=GC_V3
|
||||
0000 | 60 00 10 00 05 03 08 42 00 00 00 00 6C 00 03 01 | ` B l
|
||||
I 56327 2024-03-03 23:45:37 - [C-2] [Switch assist] Replaying previous enable command
|
||||
I 56327 2024-03-03 23:45:37 - [Commands] Sending to C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 05 03 59 42 00 00 00 00 64 00 03 01 | ` YB d
|
||||
0000 | 60 00 28 00 05 03 FF FF 00 00 00 00 6C 00 03 01 | ` ( l
|
||||
0010 | 05 03 FF FF 00 00 00 00 64 00 03 01 05 03 FF FF | d
|
||||
0020 | 00 00 00 00 65 00 03 01 | e
|
||||
I 56327 2024-03-03 23:45:37 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 05 03 59 42 00 00 00 00 64 00 03 00 | ` YB d
|
||||
I 56327 2024-03-03 23:45:38 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
@@ -15242,7 +15247,7 @@ I 56327 2024-03-03 23:49:06 - [Commands] Received from C-2 (Jess) (version=GC_V3
|
||||
0000 | 60 00 10 00 05 03 66 43 00 00 00 00 68 00 04 01 | ` fC h
|
||||
I 56327 2024-03-03 23:49:06 - [C-2] [Switch assist] Replaying previous enable command
|
||||
I 56327 2024-03-03 23:49:06 - [Commands] Sending to C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 05 03 08 42 00 00 00 00 6C 00 03 01 | ` B l
|
||||
0000 | 60 00 10 00 05 03 FF FF 00 00 00 00 68 00 04 01 | ` h
|
||||
I 56327 2024-03-03 23:49:06 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 42 03 00 00 AB 88 9B C3 D3 0F 10 44 | ` B D
|
||||
I 56327 2024-03-03 23:49:06 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
@@ -15267,7 +15272,8 @@ I 56327 2024-03-03 23:49:08 - [Commands] Received from C-2 (Jess) (version=GC_V3
|
||||
0000 | 60 00 10 00 05 03 65 43 00 00 00 00 67 00 04 01 | ` eC g
|
||||
I 56327 2024-03-03 23:49:08 - [C-2] [Switch assist] Replaying previous enable command
|
||||
I 56327 2024-03-03 23:49:08 - [Commands] Sending to C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 05 03 66 43 00 00 00 00 68 00 04 01 | ` fC h
|
||||
0000 | 60 00 1C 00 05 03 FF FF 00 00 00 00 67 00 04 01 | ` g
|
||||
0010 | 05 03 FF FF 00 00 00 00 68 00 04 01 | h
|
||||
I 56327 2024-03-03 23:49:08 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 0B 03 3B 43 01 00 00 00 3B 03 00 00 | ` ;C ;
|
||||
I 56327 2024-03-03 23:49:08 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
@@ -16093,7 +16099,9 @@ I 56327 2024-03-03 23:50:04 - [Commands] Received from C-2 (Jess) (version=GC_V3
|
||||
0000 | 60 00 10 00 05 03 41 43 00 00 00 00 66 00 04 01 | ` AC f
|
||||
I 56327 2024-03-03 23:50:04 - [C-2] [Switch assist] Replaying previous enable command
|
||||
I 56327 2024-03-03 23:50:04 - [Commands] Sending to C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 05 03 65 43 00 00 00 00 67 00 04 01 | ` eC g
|
||||
0000 | 60 00 28 00 05 03 FF FF 00 00 00 00 66 00 04 01 | ` ( f
|
||||
0010 | 05 03 FF FF 00 00 00 00 67 00 04 01 05 03 FF FF | g
|
||||
0020 | 00 00 00 00 68 00 04 01 | h
|
||||
I 56327 2024-03-03 23:50:04 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
0000 | 60 00 10 00 05 03 65 43 00 00 00 00 67 00 04 00 | ` eC g
|
||||
I 56327 2024-03-03 23:50:04 - [Commands] Received from C-2 (Jess) (version=GC_V3 command=60 flag=00)
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
"PPPStackListen": [],
|
||||
"HTTPListen": [],
|
||||
"Episode3BehaviorFlags": 0xFA,
|
||||
"EnableEpisode3SendFunctionCall": false,
|
||||
"EnableV3V4ProtectedSubcommands": false,
|
||||
|
||||
"Episode3InfiniteMeseta": false,
|
||||
"Episode3DefeatPlayerMeseta": [400, 500, 600, 700, 800],
|
||||
|
||||
Reference in New Issue
Block a user