Compare commits

...

34 Commits

Author SHA1 Message Date
incentive f05e68492d PSO Peeps Start
Docker / Build (push) Has been cancelled
2026-05-01 23:14:17 -04:00
Martin Michelsen 7f68d41bac fix port name in game server lookup 2026-04-25 22:21:46 -07:00
Martin Michelsen 75e7232096 handle BB not sending C6 after 08E8 2026-04-25 12:06:43 -07:00
Martin Michelsen 7a29b39771 allow 6xCB in free-play 2026-04-24 20:36:45 -07:00
Martin Michelsen cfcb56b13f update command notes 2026-04-24 20:17:48 -07:00
Martin Michelsen 9e6740b778 update 6x30 notes 2026-04-20 20:00:11 -07:00
Martin Michelsen 590f937959 add release script 2026-04-19 13:32:26 -07:00
Martin Michelsen 31abc24e81 don't allow players to pick up items if they are too far away 2026-04-19 09:35:20 -07:00
Martin Michelsen 507fbf0451 add another useless AR code 2026-04-19 08:57:49 -07:00
Martin Michelsen 1fa660129d add last-hit tracking for target subcommands 2026-04-11 08:28:40 -07:00
Martin Michelsen 67082f7b6b update static 2026-04-11 08:14:12 -07:00
Martin Michelsen b34c9a7c88 improve error message for missing quest common/rare item sets 2026-04-05 21:50:53 -07:00
Martin Michelsen 87e85932a4 switch rare drops to stacked space logic 2026-04-03 19:52:42 -07:00
Martin Michelsen b704d827ed add support for direct Xbox connections 2026-04-01 21:47:21 -07:00
Martin Michelsen 598ecf88e3 revise death_flags notes 2026-04-01 08:48:19 -07:00
Martin Michelsen a05971017d explain a few of the unknown player_flags bits 2026-03-30 19:38:15 -07:00
Martin Michelsen b7819413b0 handle missing DAR entries in HTML generator 2026-03-27 07:55:44 -07:00
Martin Michelsen 80e4b0e6fe clean up formatting on Ep3 drop rates comment 2026-03-23 20:39:44 -07:00
Martin Michelsen daee47b722 use new phosg parallel functions 2026-03-22 21:37:52 -07:00
Martin Michelsen 5724fb9a12 add $allrare debug command; closes #739 2026-03-22 21:37:43 -07:00
Martin Michelsen 983753f840 add FogDebug patch 2026-03-21 09:30:59 -07:00
Martin Michelsen 53d2318873 more details on player_flags 2026-03-20 22:21:21 -07:00
Martin Michelsen 83291d5501 more details on player_flags 2026-03-19 23:01:30 -07:00
Martin Michelsen 55be92a56f add game duration to info window 2026-03-19 10:42:01 -07:00
Martin Michelsen 6a23e5da0a make some sense out of game_flags and player_flags 2026-03-18 22:44:02 -07:00
Martin Michelsen 4571cf7fdc fix attribute count check in 6xDA 2026-03-11 07:19:53 -07:00
Martin Michelsen 4e3549ba6b use EnemyType in ItemCreator; fix incorrect drop tables 2026-03-08 20:40:15 -07:00
Martin Michelsen 3cbf64dda2 update quest collision opcode docs 2026-03-08 12:55:57 -07:00
Martin Michelsen 382bc6b7ce don't allow error cases for bb_exchange_pd_percent to destroy items 2026-03-04 21:37:54 -08:00
Martin Michelsen e05991ffb3 add test for trade window sequence 2026-03-03 20:43:46 -08:00
Martin Michelsen ffda97222d document --language option in disassemble-quest-script 2026-03-02 08:46:38 -08:00
Martin Michelsen 8f21604367 clean up CMakeLists a bit 2026-03-02 08:46:25 -08:00
Martin Michelsen 4045504b61 also send combatant info board entries in spectator teams 2026-02-28 20:49:36 -08:00
Martin Michelsen 4aad1514c2 port a few more bug fix patches to Xbox 2026-02-28 20:49:19 -08:00
99 changed files with 7731 additions and 1232 deletions
+12 -1
View File
@@ -3,6 +3,7 @@
# Build products
newserv
newserv.exe
src/Revision.cc
# CMake files
@@ -37,12 +38,22 @@ system/teams/*.json
# Files fuzziqersoftware uses that don't make sense to be committed to the main
# repository
*.dec
*.WIP-s
files
make_release.py
notes-private
old-khyller
old-newserv
release
release.zip
all-quests
system/dol
system/patch-bb/data
system/client-functions/Debug-Private
system/config.2.json
system/ep3/banners
system/ep3/cardtex
system/ep3/cardtex-trial
system/players
system/quests/includes
system/quests/private
.vscode
+5 -4
View File
@@ -134,11 +134,12 @@ set(SOURCES
add_executable(newserv ${SOURCES})
target_include_directories(newserv PUBLIC ${ASIO_INCLUDE_DIR} ${Iconv_INCLUDE_DIRS})
target_link_libraries(newserv phosg::phosg ${Iconv_LIBRARIES} pthread resource_file::resource_file)
target_link_libraries(newserv phosg::phosg ${Iconv_LIBRARIES} resource_file::resource_file)
if (WIN32)
target_compile_definitions(newserv PUBLIC -DWINVER=0x0A00 -D_WIN32_WINNT=0x0A00)
target_link_libraries(newserv ws2_32 mswsock bcrypt iphlpapi -static -static-libgcc -static-libstdc++)
target_compile_options(newserv PRIVATE -Wa,-mbig-obj)
target_compile_definitions(newserv PUBLIC WINVER=0x0A00 _WIN32_WINNT=0x0A00)
target_compile_options(newserv PRIVATE -Wa,-mbig-obj -Wno-mismatched-new-delete)
target_link_options(newserv PRIVATE -static -static-libgcc -static-libstdc++)
target_link_libraries(newserv ws2_32 mswsock bcrypt iphlpapi)
endif()
add_dependencies(newserv newserv-Revision-cc)
+2
View File
@@ -603,8 +603,10 @@ Some commands only work for clients not in proxy sessions. The chat commands are
* `$qsyncall <reg-num> <value>`: Set a quest register's value for everyone in the game. `<reg-num>` should be either rXX (e.g. r60) or fXX (e.g. f60); if the latter, `<value>` is parsed as a floating-point value instead of as an integer.
* `$swset [floor] <flag-num>` and `$swclear [floor] <flag-num>`: Set or clear a switch flag. If floor is not given, sets or clears the flag on your current floor.
* `$swsetall`: Set all switch flags on your current floor. This unlocks all doors, disables all laser fences, triggers all light/poison switches, etc.
* `$allrare`: Make all enemies and boxes drop their rare items every time.
* `$gc` (non-proxy only): Send your own Guild Card to yourself.
* `$sc <data>`: Send a command to yourself.
* `$scp <data>`: Send a protected command to yourself.
* `$ss <data>`: Send a command to the remote server (if in a proxy session) or to the game server.
* `$sb <data>`: Send a command to yourself, and to the remote server or game server.
* `$auction` (Episode 3 only): Bring up the CARD Auction menu, even if there are fewer than 4 players are in the game or you don't have a VIP card.
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env python3
import os
import shutil
import subprocess
import sys
from typing import Callable
def filter_directory(dir: str, predicate: Callable[[str], bool]):
for filename in os.listdir(dir):
if not predicate(filename):
path = os.path.join(dir, filename)
if os.path.isfile(path):
os.remove(path)
else:
shutil.rmtree(path)
def main():
print("Deleting existing release directory")
if os.path.exists("release"):
shutil.rmtree("release")
if os.path.exists("release.zip"):
os.remove("release.zip")
os.mkdir("release")
print("Adding executables")
shutil.copy("newserv", "release/newserv-macos")
shutil.copy("newserv.exe", "release/newserv-windows.exe")
shutil.copy("README.md", "release/README.md")
print("Adding system directory")
shutil.copytree("system", "release/system")
print("Removing instance-based and temporary files")
filter_directory(
"release/system",
lambda filename: (not filename.endswith(".json"))
or filename == "config.example.json",
)
filter_directory(
"release/system/ep3", lambda filename: not filename.startswith("cardtex")
)
filter_directory(
"release/system/client-functions",
lambda filename: filename not in ("Debug-Private", "FastLoading", "notes.txt"),
)
filter_directory("release/system/dol", lambda filename: False)
filter_directory("release/system/ep3/banners", lambda filename: False)
filter_directory("release/system/ep3/battle-records", lambda filename: False)
filter_directory("release/system/licenses", lambda filename: False)
filter_directory("release/system/players", lambda filename: False)
filter_directory(
"release/system/quests",
lambda filename: filename not in ("private", "includes"),
)
filter_directory("release/system/teams", lambda filename: filename == "base.json")
subprocess.check_call(["find", "release", "-name", ".DS_Store", "-delete"])
subprocess.check_call(["find", "release", "-name", "*.WIP-s", "-delete"])
print("Setting up configuration")
os.rename("release/system/config.example.json", "release/system/config.json")
if __name__ == "__main__":
sys.exit(main())
+34
View File
@@ -1074,3 +1074,37 @@ Enable quest board menu in free play (for use with the above code)
3OJ3 => 04262E44 38600001
3OJ4 => 04263EB8 38600001
3OP0 => 0426374C 38600001
All classes' footsteps sound like RAcast's
(Change the 2 in 38600002 to 0 for human/Newman, 1 for lighter androids, or 3 if you want to be annoyed)
3OE0 => 041B3ED0 38600002
041B3ED4 4E800020
3OE1 => 041B3ED0 38600002
041B3ED4 4E800020
3OE2 => 041B4068 38600002
041B406C 4E800020
3OJ2 => 041B3AE4 38600002
041B3AE8 4E800020
3OJ3 => 041B3F38 38600002
041B3F3C 4E800020
3OJ4 => 041B552C 38600002
041B5530 4E800020
3OJ5 => 041B4004 38600002
041B4008 4E800020
3OJT => 0420A120 38600002
0420A124 4E800020
3OP0 => 041B4524 38600002
041B4528 4E800020
3SE0 => 040D0378 38600002
040D037C 4E800020
3SJ0 => 040D0394 38600002
040D0398 4E800020
3SJT => 040D431C 38600002
040D4320 4E800020
3SP0 => 040D07BC 38600002
040D07C0 4E800020
Rappy size modifier
3OE1 => 040C1E24 48000020 // Disable flag check in render
045D0718 40800000 // X/Z scale as float (here, 4.0)
045D071C 40800000 // Y scale as float (here, 4.0)
+2 -2
View File
@@ -538,8 +538,8 @@ fparam4 = TODO: 3OE1:802D7490; NNF: Jump cooldown time (Higher value = less wait
iparam1 = TODO: 3OE1:802D7484
MOVEMENT DATA 40 (MORFOS)
fparam1 = TODO: 3OE1:80335A98, 3OE1:80335DBC; NNF: Laser speed (also affects hitbox property - needs retest with others for opinion).
fparam2 = TODO: 3OE1:80335ADC; NNF: Laser damage.
fparam1 = laser speed; hitbox radius is fparam1 * 1.5
fparam2 = laser damage
iparam1 = TODO: 3OE1:80332298, 3OE1:803321C4; NNF: Firing rate of regular laser attack. Laser attack when aggressive (charging) is unaffected.
iparam2 = TODO: 3OE1:8033161C, 3OE1:8033192C, 3OE1:80331B4C, 3OE1:80331D00, 3OE1:80331FA0; NNF: Speed at which Morphos spins after firing laser.
iparam3 = TODO: 3OE1:80331F04; NNF: Interval in frames of attacks
+24 -5
View File
@@ -2227,6 +2227,15 @@ ChatCommandDefinition cc_fastkill(
}
co_return;
});
ChatCommandDefinition cc_allrare(
{"$allrare"},
+[](const Args& a) -> asio::awaitable<void> {
a.check_debug_enabled();
a.c->toggle_flag(Client::Flag::ALL_RARES_ENABLED);
send_text_message_fmt(
a.c, "$C6All-rares {}", a.c->check_flag(Client::Flag::ALL_RARES_ENABLED) ? "enabled" : "disabled");
co_return;
});
ChatCommandDefinition cc_rand(
{"$rand"},
@@ -2349,14 +2358,18 @@ ChatCommandDefinition cc_saverec(
co_return;
});
static asio::awaitable<void> command_send_command(const Args& a, bool to_client, bool to_server) {
static asio::awaitable<void> command_send_command(const Args& a, bool to_client, bool to_server, bool send_protected) {
if (!a.c->proxy_session) {
a.check_debug_enabled();
}
string data = phosg::parse_data_string(a.text);
data.resize((data.size() + 3) & (~3));
if (to_client) {
a.c->channel->send(data);
if (send_protected) {
co_await send_protected_command(a.c, data.data(), data.size(), false);
} else {
a.c->channel->send(data);
}
}
if (to_server) {
if (a.c->proxy_session) {
@@ -2371,13 +2384,19 @@ static asio::awaitable<void> command_send_command(const Args& a, bool to_client,
ChatCommandDefinition cc_sb(
{"$sb"},
+[](const Args& a) -> asio::awaitable<void> {
return command_send_command(a, true, true);
return command_send_command(a, true, true, false);
});
ChatCommandDefinition cc_sc(
{"$sc"},
+[](const Args& a) -> asio::awaitable<void> {
return command_send_command(a, true, false);
return command_send_command(a, true, false, false);
});
ChatCommandDefinition cc_scp(
{"$scp"},
+[](const Args& a) -> asio::awaitable<void> {
return command_send_command(a, true, false, true);
});
ChatCommandDefinition cc_secid(
@@ -2571,7 +2590,7 @@ ChatCommandDefinition cc_spec(
ChatCommandDefinition cc_ss(
{"$ss"},
+[](const Args& a) -> asio::awaitable<void> {
return command_send_command(a, false, true);
return command_send_command(a, false, true, false);
});
ChatCommandDefinition cc_stat(
+3 -3
View File
@@ -1008,9 +1008,9 @@ void Client::load_all_files() {
auto stack_limits = s->item_stack_limits(this->version());
this->blocked_senders.clear();
for (size_t z = 0; z < this->guild_card_data->blocked.size(); z++) {
if (this->guild_card_data->blocked[z].present) {
this->blocked_senders.emplace(this->guild_card_data->blocked[z].guild_card_number);
for (size_t z = 0; z < this->guild_card_data->blocked_senders.size(); z++) {
if (this->guild_card_data->blocked_senders[z].present) {
this->blocked_senders.emplace(this->guild_card_data->blocked_senders[z].guild_card_number);
}
}
+3
View File
@@ -78,6 +78,7 @@ public:
INFINITE_HP_ENABLED = 0x0000000040000000,
INFINITE_TP_ENABLED = 0x0000000080000000,
FAST_KILLS_ENABLED = 0x0000000100000000,
ALL_RARES_ENABLED = 0x0000100000000000,
DEBUG_ENABLED = 0x0000000200000000,
ITEM_DROP_NOTIFICATIONS_1 = 0x0000000400000000,
ITEM_DROP_NOTIFICATIONS_2 = 0x0000000800000000,
@@ -139,6 +140,7 @@ public:
std::shared_ptr<Channel> channel;
std::shared_ptr<PSOBBMultiKeyDetectorEncryption> bb_detector_crypt;
ServerBehavior server_behavior;
uint16_t listener_port = 0;
std::unordered_map<std::string, std::function<void()>> disconnect_hooks;
uint64_t ping_start_time = 0;
@@ -149,6 +151,7 @@ public:
uint8_t override_lobby_event = 0xFF; // FF = no override
uint8_t override_lobby_number = 0x80; // 80 = no override
int64_t override_random_seed = -1;
int8_t selected_blueballz_tier = -1; // -1 = normal lobby/game; 0..10 = requested Blueballz tier
std::unique_ptr<Variations> override_variations;
VectorXYZF pos;
uint32_t floor = 0x0F;
+243 -191
View File
@@ -144,7 +144,7 @@ struct S_ServerInit_Patch_02 {
} __packed_ws__(S_ServerInit_Patch_02, 0x48);
// 02 (C->S): Encryption started
// No arguments
// No arguments.
// 03: Invalid command
@@ -163,7 +163,7 @@ struct C_Login_Patch_04 {
} __packed_ws__(C_Login_Patch_04, 0x6C);
// 05 (S->C): Disconnect
// No arguments
// No arguments.
// This command is not used in the normal flow (described above). Generally the server should disconnect after sending
// a 12 or 15 command instead of an 05.
@@ -203,10 +203,10 @@ struct S_EnterDirectory_Patch_09 {
} __packed_ws__(S_EnterDirectory_Patch_09, 0x40);
// 0A (S->C): Exit directory
// No arguments
// No arguments.
// 0B (S->C): Start patch session and go to patch root directory
// No arguments
// No arguments.
// 0C (S->C): File checksum request
@@ -216,7 +216,7 @@ struct S_FileChecksumRequest_Patch_0C {
} __packed_ws__(S_FileChecksumRequest_Patch_0C, 0x24);
// 0D (S->C): End of file checksum requests
// No arguments
// No arguments.
// 0E: Invalid command
@@ -229,7 +229,7 @@ struct C_FileInformation_Patch_0F {
} __packed_ws__(C_FileInformation_Patch_0F, 0x0C);
// 10 (C->S): End of file information command list
// No arguments
// No arguments.
// 11 (S->C): Start file downloads
@@ -239,7 +239,7 @@ struct S_StartFileDownloads_Patch_11 {
} __packed_ws__(S_StartFileDownloads_Patch_11, 0x08);
// 12 (S->C): End patch session successfully
// No arguments
// No arguments.
// 13 (S->C): Message box
// Same format and usage as commands 1A/D5 on the game server (described below). On PSOBB, the message box appears in
@@ -293,10 +293,10 @@ struct SC_TextHeader_01_06_11_B0_EE {
// Command 17 should be used instead for the first connection.
// All commands after this command will be encrypted with PSO V2 encryption on DC, PC, and GC Episodes 1&2 Trial
// Edition, or PSO V3 encryption on other V3 versions. The (encrypted) response depends on the client's version:
// - DC NTE clients will respond with 8B.
// - DC 11/2000 and DCv1 clients will respond with 93.
// - DCv2, PCv2, and GC NTE clients will respond with an 9A, 9D, or 9E.
// - V3 clients will respond with 9A or 9E command.
// - DC NTE clients will respond with an 8B command.
// - DC 11/2000 and DCv1 clients will respond with a 93 command.
// - DCv2, PCv2, and GC NTE clients will respond with a 9A, 9D, or 9E command.
// - V3 clients will respond with a 9A or 9E command.
// The copyright field in the below structure must contain "DreamCast Lobby Server. Copyright SEGA Enterprises. 1999".
// (The above text is required on all versions that use this command, including those versions that don't run on the
// DreamCast.)
@@ -494,16 +494,16 @@ struct S_MenuItemT {
// - On BB, 0x40/0x41 mean Episodes 1/2 as on GC, and 0x43 means Episode 4.
uint8_t episode = 0;
// Flags (01 and 02 are used for all menus; the rest are only used for the game menu):
// 01 = Send name? (client sends the name field in the 10 command if this item is chosen, but it's blank)
// 02 = Locked (lock icon appears in the menu; player is prompted for a password if they choose this game)
// 04 = In battle (Episode 3 only; a sword icon appears in the menu)
// 04 = Disabled (BB only; used for solo games)
// 10 = Is battle mode
// 20 = Is challenge mode
// 40 = Is v2 only (DCv2/PC); game name renders in orange
// 40 = Is Episode 1 (V3/BB)
// 80 = Is Episode 2 (V3/BB)
// C0 = Is Episode 4 (BB)
// 01 = Send name? (client sends the name field in the 10 command if this item is chosen, but it's blank)
// 02 = Locked (lock icon appears in the menu; player is prompted for a password if they choose this game)
// 04 = In battle (Episode 3 only; a sword icon appears in the menu)
// 04 = Disabled (BB only; used for solo games)
// 10 = Is battle mode
// 20 = Is challenge mode
// 40 = Is v2 only (DCv2/PC); game name renders in orange
// 40 = Is Episode 1 (V3/BB)
// 80 = Is Episode 2 (V3/BB)
// C0 = Is Episode 4 (BB)
uint8_t flags = 0;
} __attribute__((packed));
using S_MenuItem_PC_BB_08 = S_MenuItemT<TextEncoding::UTF16>;
@@ -513,7 +513,7 @@ check_struct_size(S_MenuItem_DC_V3_08_Ep3_E6, 0x1C);
// 08 (C->S): Request game list
// Internal name: SndGameList
// No arguments
// No arguments.
// 08 (S->C): Game list
// Internal name: RcvGameList
@@ -530,6 +530,7 @@ struct C_MenuItemInfoRequest_09 {
le_uint32_t item_id = 0;
} __packed_ws__(C_MenuItemInfoRequest_09, 8);
// 0A: Invalid command
// 0B: Invalid command
// 0C (C->S): Create game (DCv1)
@@ -709,10 +710,10 @@ struct C_WriteFileConfirmation_V3_BB_13_A7 {
// Internal name: RcvPsoRegistConnectV2
// Same format as 02 command, but a different copyright: "DreamCast Port Map. Copyright SEGA Enterprises. 1999"
// The response depends on the client's version:
// - DC NTE will respond with 8B.
// - DC 11/2000 and DCv1 will respond with 90.
// - DCv2, PCv2, and GC NTE clients will respond with 9A or 9D.
// - V3 (GC/Xbox) clients will respond with a DB command when they receive a 17 command in any online
// - DC NTE will respond with an 8B command.
// - DC 11/2000 and DCv1 will respond with a 90 command.
// - DCv2, PCv2, and GC NTE clients will respond with a 9A or 9D command.
// - V3 (GC/Xbox) clients will respond with a DB command (but see the notes on DB for Xbox clients).
// 18 (S->C): Account verification result (PC/V3)
// Behaves exactly the same as 9A (S->C). No arguments except header.flag.
@@ -861,7 +862,7 @@ struct S_GallonPlanResult_BB_25 {
// 2D: Invalid command
// 2E: Invalid command
// 2F: Invalid command
// 30: Invalid command
// 30: Invalid command (but used as a newserv extension; see end of this file)
// 31: Invalid command
// 32: Invalid command
// 33: Invalid command
@@ -1234,12 +1235,12 @@ struct S_JoinGame_BB_64 : S_JoinGameT_DC_PC<PlayerLobbyDataBB> {
// Similarly to 64, the client will ignore 64 and 65 commands while loading, and will buffer all other commands except
// 1D until loading is done.
struct LobbyFlags_DCNTE {
struct LobbyFlagsDCNTE {
uint8_t client_id = 0;
uint8_t leader_id = 0;
uint8_t disable_udp = 1;
uint8_t unused = 0;
} __packed_ws__(LobbyFlags_DCNTE, 4);
} __packed_ws__(LobbyFlagsDCNTE, 4);
struct LobbyFlags {
uint8_t client_id = 0;
@@ -1273,7 +1274,7 @@ struct S_JoinLobbyT {
return offsetof(S_JoinLobbyT, entries) + used_entries * sizeof(Entry);
}
} __attribute__((packed));
using S_JoinLobby_DCNTE_65_67_68 = S_JoinLobbyT<LobbyFlags_DCNTE, PlayerLobbyDataDCGC, PlayerDispDataDCPCV3>;
using S_JoinLobby_DCNTE_65_67_68 = S_JoinLobbyT<LobbyFlagsDCNTE, PlayerLobbyDataDCGC, PlayerDispDataDCPCV3>;
using S_JoinLobby_PC_65_67_68 = S_JoinLobbyT<LobbyFlags, PlayerLobbyDataPC, PlayerDispDataDCPCV3>;
using S_JoinLobby_DC_GC_65_67_68_Ep3_EB = S_JoinLobbyT<LobbyFlags, PlayerLobbyDataDCGC, PlayerDispDataDCPCV3>;
using S_JoinLobby_BB_65_67_68 = S_JoinLobbyT<LobbyFlags, PlayerLobbyDataBB, PlayerDispDataBB>;
@@ -1523,7 +1524,7 @@ struct C_ConnectionInfo_DCNTE_8A {
// client responds with an 8B command.
// 8A (C->S): Request lobby/game name (except DC NTE)
// No arguments
// No arguments.
// 8A (S->C): Lobby/game name (except DC NTE)
// Contents is a string containing the lobby or game name. All versions after DCv1 send an 8A command to request the
@@ -1576,7 +1577,7 @@ struct C_LoginV1_DC_PC_V3_90 {
// despite its size not being a multiple of 4. This is fixed in later versions, so we have to handle both cases.
} __packed_ws__(C_LoginV1_DC_PC_V3_90, 0x22);
// 90 (S->C): Account verification result (V3)
// 90 (S->C): Account verification result (DC/PC/V3)
// Behaves exactly the same as 9A (S->C). No arguments except header.flag.
// 91 (S->C): Start encryption at login server (legacy; non-BB only)
@@ -1604,7 +1605,7 @@ struct C_RegisterV1_DC_92 {
// Internal name: RcvPsoRegist
// Same format and usage as 9C (S->C) command.
// 93 (C->S): Log in (DCv1)
// 93 (C->S): Log in (DCv1, BB)
struct C_LoginV1_DC_93 {
/* 00 */ le_uint32_t player_tag = 0x00010000;
@@ -1627,8 +1628,6 @@ struct C_LoginExtendedV1_DC_93 : C_LoginV1_DC_93 {
SC_MeetUserExtension_DC_V3 extension;
} __packed_ws__(C_LoginExtendedV1_DC_93, 0x110);
// 93 (C->S): Log in (BB)
struct C_LoginBase_BB_93 {
/* 00 */ le_uint32_t player_tag = 0x00010000;
/* 04 */ le_uint32_t guild_card_number = 0;
@@ -1674,7 +1673,7 @@ struct C_LoginWithHardwareInfo_BB_93 : C_LoginBase_BB_93 {
// 95 (S->C): Request player data
// Internal name: RcvRecognition
// No arguments
// No arguments.
// For some reason, some servers send high values in the header.flag field here. The header.flag field is completely
// unused by the client, however - sending zero works just fine. The original Sega servers had some uninitialized
// memory bugs, of which that may have been one, and other private servers may have just duplicated Sega's behavior.
@@ -1698,7 +1697,7 @@ struct C_CharSaveInfo_DCv2_PC_V3_BB_96 {
// 97 (S->C): Save to memory card
// Internal name: RcvSaveCountCheck
// No arguments
// No arguments.
// Internally, this command is called RcvSaveCountCheck, even though the counter in the 96 command (to which 97 is a
// reply) counts more events than saves. Sending this command with header.flag == 0 will show a message saying that
// "character data was improperly saved", and will delete the character's items and challenge mode records. newserv
@@ -1718,7 +1717,7 @@ struct C_CharSaveInfo_DCv2_PC_V3_BB_96 {
// 99 (C->S): Server time accepted
// Internal name: SndPsoDirList
// No arguments
// No arguments.
// This command's internal name suggests that it's actually a request for the ship select menu, but it's only sent as
// the response to a B1 command (server time) and the client doesn't set any state to indicate it's waiting for a ship
// select menu, so we just treat it as confirmation of a received B1 command instead.
@@ -1763,14 +1762,13 @@ struct C_Login_DC_PC_V3_9A {
// 13 = servers under maintenance (118)
// Seems like most (all?) of the rest of the codes are "network error" (119).
// 9B (S->C): Secondary server init (non-BB, non-DCv1)
// Behaves exactly the same as 17 (S->C).
// 9B (S->C): Secondary server init (BB)
// Format is the same as 03 (and the client uses the same encryption afterward). The only differences:
// 9B (S->C): Secondary server init (DCv2 and later)
// On versions before BB, this command behaves exactly the same as 17 (S->C).
// On BB, the format of this command is the same as 03 (and the client uses the same encryption afterward). The only
// differences are:
// - 9B does not work during the data-server phase (before the client has reached the ship select menu), but 03 does.
// - For command 9B, the copyright string must be "PSO NEW PM Server. Copyright 1999-2002 SONICTEAM.".
// - The client will respond to 9B with DB instead of 93.
// - The client will respond to this command with a DB command instead of a 93 command.
// 9C (C->S): Register
// Internal name: SndPsoRegist
@@ -1826,13 +1824,13 @@ struct C_Login_DC_PC_GC_9D {
/* 08 */ be_uint64_t hardware_id;
/* 10 */ le_uint32_t sub_version = 0;
/* 14 */ uint8_t is_extended = 0; // If 1, structure has extended format
/* 15 */ Language language = Language::JAPANESE; // 0 = JP, 1 = EN, 2 = DE, 3 = FR, 4 = ES
/* 15 */ Language language = Language::JAPANESE;
/* 16 */ parray<uint8_t, 0x2> unused3; // Always zeroes
/* 18 */ pstring<TextEncoding::ASCII, 0x10> v1_serial_number;
/* 28 */ pstring<TextEncoding::ASCII, 0x10> v1_access_key;
/* 38 */ pstring<TextEncoding::ASCII, 0x10> serial_number; // On XB, this is the XBL gamertag
/* 48 */ pstring<TextEncoding::ASCII, 0x10> access_key; // On XB, this is the XBL user ID
/* 58 */ pstring<TextEncoding::ASCII, 0x30> serial_number2; // On DCv2, this is the hardware ID; on XB, this is the XBL gamertag
/* 58 */ pstring<TextEncoding::ASCII, 0x30> serial_number2; // DCv2: hardware ID; XB: XBL gamertag
/* 88 */ pstring<TextEncoding::ASCII, 0x30> access_key2; // On XB, this is the XBL user ID
/* B8 */ pstring<TextEncoding::ASCII, 0x10> login_character_name;
/* C8 */
@@ -1936,7 +1934,7 @@ struct C_ChangeShipOrBlock_A0_A1 {
// Same as 07 command.
// A2 (C->S): Request quest menu
// No arguments
// No arguments.
// A2 (S->C): Quest menu
// Client will respond with an 09, 10, or A9 command. For 09, the server should send the category or quest description
@@ -2014,7 +2012,7 @@ struct S_QuestMenuEntry_BB_A2_A4 {
// A9 (C->S): Quest menu closed (canceled)
// Internal name: SndQuestEnd
// No arguments
// No arguments.
// This command is sent when the in-game quest menu (A2) is closed. This is used by the server to unlock the game if
// the players don't select a quest, since players are forbidden from joining while the quest menu is open. When the
// download quest menu is closed, either by downloading a quest or canceling, the client sends A0 instead.
@@ -2148,7 +2146,7 @@ struct S_RankUpdate_Ep3_B7 {
} __packed_ws__(S_RankUpdate_Ep3_B7, 0x1C);
// B7 (C->S): Confirm rank update (Episode 3)
// No arguments
// No arguments.
// The client sends this after it receives a B7 from the server.
// B8 (S->C): Update card definitions (Episode 3)
@@ -2159,7 +2157,7 @@ struct S_RankUpdate_Ep3_B7 {
// Note: PSO BB accepts this command as well, but ignores it.
// B8 (C->S): Confirm updated card definitions (Episode 3)
// No arguments
// No arguments.
// The client sends this after it receives a B8 from the server.
// B8 (C->S): Valid but ignored (BB)
@@ -2457,21 +2455,33 @@ struct S_ConfirmTournamentEntry_Ep3_CC {
// CF: Invalid command
// D0 (C->S): Start trade sequence (V3/BB)
// The trade window sequence is a bit complicated. On pre-BB versions, the normal flow is:
// The trade window sequence is a bit complicated. On pre-BB versions, the sequence is:
// - Clients sync trade state with 6xA6 commands
// - When both have confirmed, one client (the initiator) sends D0
// - The server sends D1 to the other client (the responder)
// - The responder sends D0
// - When both have confirmed, one client (the initiator) sends D0 with the items it intends to give to the other
// player (the responder)
// - The server sends D1 to the responder
// - The responder sends D0 with its own item list
// - The server sends D1 to both clients
// - Both clients delete the sent items from their inventories and send the appropriate subcommand (6x29)
// - Both clients delete the sent items from their inventories and send the appropriate subcommands (6x29)
// - Both clients send D2; similarly to how AC works, the server doesn't proceed until both D2 commands are received
// - The server sends D3 to both clients with each other's data from their D0 commands, followed immediately by D4 01
// to both clients, which completes the trade
// - Both clients send the appropriate subcommand to create inventory items
// On BB, the flow is similar, except after both D2 commands are received, the server instead handles the rest of the
// process - it sends 6x29 commands to delete the inventory items and 6xBE to create the traded items.
// At any point if an error occurs, either client may send a D4 00, which cancels the entire sequence. The server
// should then send D4 00 to both clients.
// - Both clients send the appropriate subcommands to create inventory items (6x5E)
// On BB, the sequence is:
// - Clients sync trade state with 6xA6 commands
// - When both have confirmed, one client (the initiator) sends D0 with the items it intends to give to the other
// player (the responder)
// - The server sends D1 to the responder
// - The responder sends D0 with its own item list
// - The server sends D1 to both clients
// - Both clients send D2; similarly to how AC works, the server doesn't proceed until both D2 commands are received
// - The server executes the trade by sending 6x29 for each item given away from each player, followed by 6xBE to
// create each item in the other player's inventory (note that all 6x29 commands must be sent before any 6xBE
// command, since the client's item state may desync if one player's inventory is full and they receive a 6xBE)
// - The server sends D3 followed immediately by D4 01 to both clients, which completes the trade; unlike in the pre-BB
// case, the D3 commands are empty (no item list is sent)
// At any point if an error occurs, either client may send D4 00, which cancels the entire sequence. The server should
// then send D4 00 to both clients.
struct SC_TradeItems_D0_D3 { // D0 when sent by client, D3 when sent by server
le_uint16_t target_client_id = 0;
@@ -2492,19 +2502,20 @@ struct SC_TradeItems_D0_D3 { // D0 when sent by client, D3 when sent by server
// On BB, this command has no arguments (and the server generates the appropriate delete and create inventory item
// commands), but the D3 command must still must be sent before the D4 command to advance the trade state.
// D4 (C->S): Trade failed (V3/BB)
// D4 (C->S): Cancel/fail trade sequence (V3/BB)
// No arguments. See the description of D0 for usage information.
// D4 (S->C): Trade complete (V3/BB)
// header.flag must be 0 (trade failed) or 1 (trade complete). See the description of D0 for usage information.
// D5: Large message box (V3/BB)
// D5 (S->C): Large message box (V3/BB)
// Same as 1A command, except the maximum length of the message is 0x1000 bytes. On BB, this command is not valid
// during the data server phase (whereas 1A is valid there). The BB client ignores all D5 commands after the first one
// sent in each connection; this logic does not apply to 1A.
// sent in each connection; this logic does not apply to 1A. This logic was added for some reason in BB; V3 does not
// have this restriction.
// D6 (C->S): Large message box closed (V3)
// No arguments
// No arguments.
// DC, PC, and BB do not send this command at all. GC US v1.0 and v1.1 will send this command when any large message
// box (1A/D5) is closed; GC Plus and Episode 3 will send D6 only for large message boxes that occur before the client
// has joined a lobby. (After joining a lobby, large message boxes will still be displayed if sent by the server, but
@@ -2523,7 +2534,7 @@ struct C_GBAGameRequest_V3_D7 {
} __packed_ws__(C_GBAGameRequest_V3_D7, 0x10);
// D7 (S->C): GBA file not found (V3/BB)
// No arguments
// No arguments.
// This command is not valid on PSO GC Episodes 1&2 Trial Edition. PSO BB accepts but completely ignores this command.
// This command tells the client that the file it requested via a D7 command does not exist. This causes the F8C1
// (get_dl_status) quest opcode to return 0 (file not found), rather than 1 (download in progress) or 2 (complete).
@@ -2554,7 +2565,10 @@ check_struct_size(S_InfoBoardEntry_BB_D8, 0x178);
// This command is not valid on PSO GC Episodes 1&2 Trial Edition.
// DB (C->S): Verify license (V3/BB)
// Server should respond with a 9A command.
// Server should respond with a 9A command. But Insignia's proxy never sends this command to the remote server, so
// newserv will only see this command from an Xbox client if it's connected through a different Xbox Live
// implementation. For this reason, newserv does not check the credentials when it receives DB from an Xbox client; it
// instead sends 9A and checks the credentials when the client subsequently sends 9E.
struct C_VerifyAccount_V3_DB {
pstring<TextEncoding::ASCII, 0x10> v1_serial_number; // Unused
@@ -3161,7 +3175,7 @@ struct SC_TeamChat_BB_07EA {
} __packed_ws__(SC_TeamChat_BB_07EA, 0x20);
// 08EA (C->S): Get team member list
// No arguments
// No arguments.
// 09EA (S->C): Team member list
@@ -3188,7 +3202,7 @@ struct S_Unknown_BB_0CEA {
} __packed_ws__(S_Unknown_BB_0CEA, 0x20);
// 0DEA (C->S): Get team name
// No arguments
// No arguments.
// 0EEA (S->C): Team name
@@ -3251,7 +3265,7 @@ struct S_TeamInfoForPlayer_BB_13EA_15EA_Entry {
// No arguments except header.flag, which is zero if the transfer failed or nonzero if it succeeded.
// 18EA: Intra-team ranking information
// No arguments (C->S)
// No arguments (C->S).
struct S_IntraTeamRanking_BB_18EA {
/* 0000 */ le_uint32_t ranking_points = 0;
@@ -3271,7 +3285,7 @@ struct S_IntraTeamRanking_BB_18EA {
} __packed_ws__(S_IntraTeamRanking_BB_18EA, 0x10);
// 19EA: Team reward list
// No arguments (C->S)
// No arguments (C->S).
struct S_TeamRewardList_BB_19EA_1AEA {
le_uint32_t num_entries;
@@ -3434,7 +3448,7 @@ struct S_AdvanceCardTradeState_Ep3_EE_FlagD1 {
} __packed_ws__(S_AdvanceCardTradeState_Ep3_EE_FlagD1, 4);
// EE D2 (C->S): Trade can proceed
// No arguments
// No arguments.
// EE D3 (S->C): Execute trade
// Same format as EE D0
@@ -3710,6 +3724,49 @@ struct G_UpdateEnemyStateT_6x0A {
G_EntityIDHeader header;
le_uint16_t enemy_index = 0; // [0, 0xB50)
le_uint16_t total_damage = 0;
// The bits in game_flags mean (all addresses in TODOs are for 3OE1):
// 00000001 = is poisoned
// 00000002 = is paralyzed
// 00000004 = is shocked
// 00000008 = is slow
// 00000010 = is confused
// 00000020 = is frozen
// 00000040 = cures and prevents all negative status effects
// 00000080 = appears to be unused (TODO: look for any usage of this flag)
// 00000100 = missed by attack (often set immediately before showing red "MISS" text)
// 00000200 = hit by attack (causes flinch for most enemies)
// 00000400 = last hit did damage greater than 25% of enemy's max HP (some enemies don't clear this)
// 00000800 = is dead (when set for most enemies, plays the death animation and then destroys the enemy)
// 00001000 = unknown (TODO: see TObjEnemyV8048ee80_v1C, TObjEnemyV8048ee80_v3B, TObjEneDolmOlm_v3B)
// 00002000 = unknown (TODO: see TObjGrass_v1E, 8011EA08, TObjEneIllGill_v1E, TObjEneIllGill_init)
// 00004000 = unknown (TODO: has status effect in slot 5; De Rol Le uses this; Vol Opt uses it too at
// TBoss3Volopt_update, TBoss3VoloptCore_update, TBoss3VoloptP01_update, TBoss3VolOptP02_update,
// TObjectV8047c128_v39, TObjectV8047c128_v38; related to paralysis somehow? see
// TObjectV8047c128_v24_update_paralysis_effect)
// 00008000 = immune to freeze (TODO: see TBoss3VolOptP02_init_inner; maybe other things use it too)
// 00010000 = unknown (TODO: see 801BA1F8)
// 00020000 = can't attack, cast techs, or use items (e.g. Vol Opt cage and Ruins falling traps set this)
// 00040000 = untargetable (e.g. TObjEneBeast sets this at construction time; it's cleared when it roars)
// 00080000 = appears to be unused (TODO: look for any usage of this flag)
// 00100000 = for players, is near enemy; for some enemies, is activated (not set if in idle animations, e.g. for
// TObjEneBeast and related classes) (unverified on v2)
// 00200000 = is attacking? (TODO: also set when TObjEneMoja is jumping though; also see TObjEnemyV8048ee80_v3C,
// TObjEneMerillLia_v3C, TObjEneSaver_v3A)
// 00400000 = unknown (TODO: see TOSensor_vF, 801CC358, 801CD224)
// 00800000 = affected by gravity? can be aerial? (wolves don't have this, perhaps their jumps are hardcoded; see
// TObjEnemyV8048ee80_v5B) (unverified on v2)
// 01000000 = immune to shock and freeze and paralysis (for Canadine/Mothmant/etc, only set when they're higher
// than player level; TODO: maybe other things too; also used by TObjPlayer, see 801B84B4; also has a
// meaning for weapons, see TItemWeapon_v10) (unverified on v2)
// 02000000 = invisible (also suppresses particles, some sounds, and hit/miss text)
// 04000000 = temporarily invincible (e.g. Dragon while it roars to advance to phase 2) (unverified on v2)
// 08000000 = unknown (TODO; see 80113384, TItemMag_v1B, TItemMag_v1A, 801182B0, TObjPlayer_render; probably
// graphical effects only)
// 10000000 = entity is player
// 20000000 = entity is enemy
// 40000000 = entity is object (some entities have both this and 20000000 set; this appears to make TWindowLockOn
// not show anything but the entity is still attackable, see TWindowLockOn_should_show_for_entity)
// 80000000 = entity is item
typename std::conditional_t<BE, be_uint32_t, le_uint32_t> game_flags = 0;
} __attribute__((packed));
using G_UpdateEnemyState_GC_6x0A = G_UpdateEnemyStateT_6x0A<true>;
@@ -3726,13 +3783,14 @@ struct G_UpdateObjectState_6x0B {
} __packed_ws__(G_UpdateObjectState_6x0B, 0x0C);
// 6x0C: Add status effect (poison/slow/etc.) (protected on V3/V4)
// 6x0D: Remove status effect (protected on V3/V4)
struct G_AddStatusEffect_6x0C {
struct G_AddOrRemoveStatusEffect_6x0C_6x0D {
G_ClientIDHeader header;
// Each status effect has an assigned slot; there are 5 slots and each slot may only hold one effect at a time. (The
// last slot, slot 4, is unused.) If a new status effect is added to a slot that already contains one, the existing
// status effect is replaced. Non-technique status effects have fixed or indefinite durations; technique-based
// effects have durations based on the technique's level.
// last slot, slot 4, may be used by certain boss effects; this is unconfirmed.) If a new status effect is added to a
// slot that already contains one, the existing status effect is replaced. Non-technique status effects have fixed or
// indefinite durations; technique-based effects have durations based on the technique's level.
// Values for effect_type:
// 02 = Freeze (slot 1; 5 seconds)
// 03 = Shock (slot 1; 10 seconds)
@@ -3747,16 +3805,8 @@ struct G_AddStatusEffect_6x0C {
// 12 = Confuse (slot 1; 10 seconds)
// Anything else = command is ignored
le_uint32_t effect_type = 0;
le_float amount = 0; // Only used for Shifta/Deband/Jellen/Zalure
} __packed_ws__(G_AddStatusEffect_6x0C, 0x0C);
// 6x0D: Clear status effect slot (protected on V3/V4)
struct G_RemoveStatusEffect_6x0D {
G_ClientIDHeader header;
le_uint32_t slot = 0; // See 6x0C description for slot values
le_uint32_t unused = 0;
} __packed_ws__(G_RemoveStatusEffect_6x0D, 0x0C);
le_float amount = 0; // Only used in 6x0C for Shifta/Deband/Jellen/Zalure; unused in 6x0D
} __packed_ws__(G_AddOrRemoveStatusEffect_6x0C_6x0D, 0x0C);
// 6x0E: Clear all negative status effects (protected on V3/V4)
// It seems that the client never sends this command.
@@ -3873,7 +3923,7 @@ struct G_DisablePKModeForPlayer_6x1C {
// 6x1D: Request partial player data (pre-v1 only)
// The subcommand number 6x1D is not used in any final version of PSO; this number is assigned based on what the
// command number would be if it were. On DC NTE, this is subcommand 6x19; on 11/2000, it's 6x1B. This command does not
// appear to ever be sent by the client; however, it will respond with 6x1E if it receives this command.
// appear to ever be sent by the client; however, it will respond with a 6x1E command if it receives this command.
struct G_RequestPartialPlayerData_DCProtos_6x1D {
G_UnusedHeader header;
@@ -4045,8 +4095,8 @@ struct G_ChangePlayerHP_6x2F {
} __packed_ws__(G_ChangePlayerHP_6x2F, 0x0C);
// 6x30: Change player level (protected on GC NTE/V3 but not V4)
// On DC NTE, the updated stats aren't sent, and the client may only gain a single level at once. On other versions,
// this is not the case.
// On DC NTE and 11/2000, the updated stats aren't sent, and the client may only gain a single level at once. On other
// versions, this is not the case.
struct G_ChangePlayerLevel_DCNTE_6x30 {
G_ClientIDHeader header;
@@ -4112,7 +4162,7 @@ struct G_Unknown_6x36 {
struct G_PhotonBlast_6x37 {
G_ClientIDHeader header;
le_uint16_t unknown_a1 = 0;
le_uint16_t amount = 0; // Amount of PB energy to expend (ignored by client upon receipt)
le_uint16_t unused = 0;
} __packed_ws__(G_PhotonBlast_6x37, 8);
@@ -4199,7 +4249,9 @@ struct G_SetPosition_6x3F {
struct G_WalkToPosition_6x40 {
G_ClientIDHeader header;
VectorXZF pos;
le_uint32_t action = 0;
// In the flags field, bit 00000008 is set if game_flag 00100000 is set (same as is_near_enemy in 6x3E and 6x3F). The
// meanings of the other bits are unknown.
le_uint32_t flags = 0;
} __packed_ws__(G_WalkToPosition_6x40, 0x10);
// 6x41: Move to position (v1)
@@ -4277,8 +4329,8 @@ struct G_ShieldAttack_6x4A {
G_ClientIDHeader header;
} __packed_ws__(G_ShieldAttack_6x4A, 4);
// 6x4B: Hit by enemy (protected on GC NTE/V3/V4)
// 6x4C: Hit by enemy (protected on GC NTE/V3/V4)
// 6x4B: Minor hit by enemy (<= 25% of max HP; protected on GC NTE/V3/V4)
// 6x4C: Major hit by enemy (> 25% of max HP; protected on GC NTE/V3/V4)
struct G_HitByEnemy_6x4B_6x4C {
G_ClientIDHeader header;
@@ -4380,7 +4432,7 @@ struct G_Unknown_6x57 {
struct G_LobbyAnimation_6x58 {
G_ClientIDHeader header;
le_uint16_t animation_number = 0;
le_uint16_t unused = 0;
le_uint16_t flags = 0; // 0 = normal, 1 = use opposite gender's animation
} __packed_ws__(G_LobbyAnimation_6x58, 8);
// 6x59: Pick up item
@@ -4675,7 +4727,7 @@ struct G_6x70_Sub_Telepipe {
struct G_6x70_Base_DCNTE {
/* 0000 */ le_uint16_t client_id = 0;
/* 0002 */ le_uint16_t room_id = 0;
/* 0004 */ le_uint32_t flags1 = 0;
/* 0004 */ le_uint32_t game_flags = 0;
/* 0008 */ VectorXYZF pos;
/* 0014 */ VectorXYZI angle;
/* 0020 */ le_uint16_t phase = 0;
@@ -4683,41 +4735,39 @@ struct G_6x70_Base_DCNTE {
} __packed_ws__(G_6x70_Base_DCNTE, 0x24);
struct G_SyncPlayerDispAndInventory_DCNTE_6x70 {
// Offsets in this struct are relative to the overall command header
/* 0004 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x60, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DCNTE_6x70)};
/* 000C */ G_6x70_Base_DCNTE base;
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x60, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DCNTE_6x70)};
/* 0008 */ G_6x70_Base_DCNTE base;
// The following two fields appear to contain uninitialized data
/* 0030 */ le_uint32_t unknown_a5 = 0;
/* 0034 */ le_uint32_t unknown_a6 = 0;
/* 0038 */ G_6x70_Sub_Telepipe telepipe;
/* 0054 */ le_uint32_t death_flags = 0;
/* 0058 */ PlayerHoldState_DCProtos hold_state;
/* 0068 */ le_uint32_t area = 0;
/* 006C */ le_uint32_t game_flags = 0;
/* 0070 */ PlayerVisualConfig visual;
/* 00C0 */ PlayerStats stats;
/* 00E4 */ le_uint32_t num_items = 0;
/* 00E8 */ parray<PlayerInventoryItem, 0x1E> items;
/* 0430 */
/* 002C */ le_uint32_t unknown_a5 = 0;
/* 0030 */ le_uint32_t unknown_a6 = 0;
/* 0034 */ G_6x70_Sub_Telepipe telepipe;
/* 0050 */ le_uint32_t death_flags = 0;
/* 0054 */ PlayerHoldState_DCProtos hold_state;
/* 0064 */ le_uint32_t area = 0;
/* 0068 */ le_uint32_t player_flags = 0;
/* 006C */ PlayerVisualConfig visual;
/* 00BC */ PlayerStats stats;
/* 00E0 */ le_uint32_t num_items = 0;
/* 00E4 */ parray<PlayerInventoryItem, 0x1E> items;
/* 042C */
} __packed_ws__(G_SyncPlayerDispAndInventory_DCNTE_6x70, 0x42C);
struct G_SyncPlayerDispAndInventory_DC112000_6x70 {
// Offsets in this struct are relative to the overall command header
/* 0004 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x67, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC112000_6x70)};
/* 000C */ G_6x70_Base_DCNTE base;
/* 0030 */ le_uint16_t bonus_hp_from_materials = 0;
/* 0032 */ le_uint16_t bonus_tp_from_materials = 0;
/* 0034 */ parray<uint8_t, 0x10> unknown_a5;
/* 0044 */ G_6x70_Sub_Telepipe telepipe;
/* 0060 */ le_uint32_t death_flags = 0;
/* 0064 */ PlayerHoldState_DCProtos hold_state;
/* 0074 */ le_uint32_t area = 0;
/* 0078 */ le_uint32_t game_flags = 0;
/* 007C */ PlayerVisualConfig visual;
/* 00CC */ PlayerStats stats;
/* 00F0 */ le_uint32_t num_items = 0;
/* 00F4 */ parray<PlayerInventoryItem, 0x1E> items;
/* 043C */
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x67, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC112000_6x70)};
/* 0008 */ G_6x70_Base_DCNTE base;
/* 002C */ le_uint16_t bonus_hp_from_materials = 0;
/* 002E */ le_uint16_t bonus_tp_from_materials = 0;
/* 0030 */ parray<uint8_t, 0x10> unknown_a5;
/* 0040 */ G_6x70_Sub_Telepipe telepipe;
/* 005C */ le_uint32_t death_flags = 0;
/* 0060 */ PlayerHoldState_DCProtos hold_state;
/* 0070 */ le_uint32_t area = 0;
/* 0074 */ le_uint32_t player_flags = 0;
/* 0078 */ PlayerVisualConfig visual;
/* 00C8 */ PlayerStats stats;
/* 00EC */ le_uint32_t num_items = 0;
/* 00F0 */ parray<PlayerInventoryItem, 0x1E> items;
/* 0438 */
} __packed_ws__(G_SyncPlayerDispAndInventory_DC112000_6x70, 0x438);
struct G_6x70_Base_V1 {
@@ -4728,71 +4778,71 @@ struct G_6x70_Base_V1 {
/* 0034 */ StatusEffectState temporary_status_effect;
/* 0040 */ StatusEffectState attack_status_effect;
/* 004C */ StatusEffectState defense_status_effect;
/* 0058 */ StatusEffectState unused_status_effect;
/* 0058 */ StatusEffectState unknown_a1_status_effect;
/* 0064 */ le_uint32_t language32 = 0;
/* 0068 */ le_uint32_t player_tag = 0;
/* 006C */ le_uint32_t guild_card_number = 0;
/* 0070 */ le_uint32_t unknown_a6 = 0; // Probably battle-related (assigned together with battle_team_number)
/* 0074 */ le_uint32_t battle_team_number = 0;
/* 0078 */ G_6x70_Sub_Telepipe telepipe;
/* 0094 */ le_uint32_t death_flags = 0; // Only a few bits are used. 4 = player is dead
// Only a few bits appear to be used in death_flags. Known values:
// 00000001 = should drop weapon/item on death
// 00000002 = does not have any automatic revival item (Scape Doll or Ragol Ring)
// 00000004 = was revived by automatic revival item
/* 0094 */ le_uint32_t death_flags = 0;
/* 0098 */ PlayerHoldState hold_state;
/* 00AC */ le_uint32_t area = 0;
/* 00B0 */ le_uint32_t game_flags = 0;
/* 00B0 */ le_uint32_t player_flags = 0;
/* 00B4 */ parray<uint8_t, 0x14> technique_levels_v1 = 0xFF; // Last byte is uninitialized
/* 00C8 */ PlayerVisualConfig visual;
/* 0118 */
} __packed_ws__(G_6x70_Base_V1, 0x118);
struct G_SyncPlayerDispAndInventory_DC_PC_6x70 {
// Offsets in this struct are relative to the overall command header
/* 0004 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC_PC_6x70)};
/* 000C */ G_6x70_Base_V1 base;
/* 0124 */ PlayerStats stats;
/* 0148 */ le_uint32_t num_items = 0;
/* 014C */ parray<PlayerInventoryItem, 0x1E> items;
/* 0494 */
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC_PC_6x70)};
/* 0008 */ G_6x70_Base_V1 base;
/* 0120 */ PlayerStats stats;
/* 0144 */ le_uint32_t num_items = 0;
/* 0148 */ parray<PlayerInventoryItem, 0x1E> items;
/* 0490 */
} __packed_ws__(G_SyncPlayerDispAndInventory_DC_PC_6x70, 0x490);
// GC NTE also uses this format.
// GC NTE also uses this format
struct G_SyncPlayerDispAndInventory_GC_6x70 {
// Offsets in this struct are relative to the overall command header
/* 0004 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_GC_6x70)};
/* 000C */ G_6x70_Base_V1 base;
/* 0124 */ PlayerStats stats;
/* 0148 */ le_uint32_t num_items = 0;
/* 014C */ parray<PlayerInventoryItem, 0x1E> items;
/* 0494 */ le_uint32_t floor = 0;
/* 0498 */
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_GC_6x70)};
/* 0008 */ G_6x70_Base_V1 base;
/* 0120 */ PlayerStats stats;
/* 0144 */ le_uint32_t num_items = 0;
/* 0148 */ parray<PlayerInventoryItem, 0x1E> items;
/* 0490 */ le_uint32_t floor = 0;
/* 0494 */
} __packed_ws__(G_SyncPlayerDispAndInventory_GC_6x70, 0x494);
struct G_SyncPlayerDispAndInventory_XB_6x70 {
// Offsets in this struct are relative to the overall command header
/* 0004 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_XB_6x70)};
/* 000C */ G_6x70_Base_V1 base;
/* 0124 */ PlayerStats stats;
/* 0148 */ le_uint32_t num_items = 0;
/* 014C */ parray<PlayerInventoryItem, 0x1E> items;
/* 0494 */ le_uint32_t floor = 0;
/* 0498 */ le_uint32_t xb_user_id_high = 0;
/* 049C */ le_uint32_t xb_user_id_low = 0;
/* 04A0 */ le_uint32_t unknown_a16 = 0;
/* 04A4 */
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_XB_6x70)};
/* 0008 */ G_6x70_Base_V1 base;
/* 0120 */ PlayerStats stats;
/* 0144 */ le_uint32_t num_items = 0;
/* 0148 */ parray<PlayerInventoryItem, 0x1E> items;
/* 0490 */ le_uint32_t floor = 0;
/* 0494 */ le_uint32_t xb_user_id_high = 0;
/* 0498 */ le_uint32_t xb_user_id_low = 0;
/* 049C */ le_uint32_t unknown_a16 = 0;
/* 04A0 */
} __packed_ws__(G_SyncPlayerDispAndInventory_XB_6x70, 0x4A0);
struct G_SyncPlayerDispAndInventory_BB_6x70 {
// Offsets in this struct are relative to the overall command header
/* 0008 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_BB_6x70)};
/* 0010 */ G_6x70_Base_V1 base;
/* 0128 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> name;
/* 0148 */ PlayerStats stats;
/* 016C */ le_uint32_t num_items = 0;
/* 0170 */ parray<PlayerInventoryItem, 0x1E> items;
/* 04B8 */ le_uint32_t floor = 0;
/* 04BC */ le_uint32_t xb_user_id_high = 0;
/* 04C0 */ le_uint32_t xb_user_id_low = 0;
/* 04C4 */ le_uint32_t unknown_a16 = 0;
/* 04C8 */
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_BB_6x70)};
/* 0008 */ G_6x70_Base_V1 base;
/* 0120 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> name;
/* 0140 */ PlayerStats stats;
/* 0164 */ le_uint32_t num_items = 0;
/* 0168 */ parray<PlayerInventoryItem, 0x1E> items;
/* 04B0 */ le_uint32_t floor = 0;
/* 04B4 */ le_uint32_t xb_user_id_high = 0;
/* 04B8 */ le_uint32_t xb_user_id_low = 0;
/* 04BC */ le_uint32_t unknown_a16 = 0;
/* 04C0 */
} __packed_ws__(G_SyncPlayerDispAndInventory_BB_6x70, 0x4C0);
// 6x71: Unblock game join (used while loading into game)
@@ -4994,6 +5044,8 @@ struct G_EnableDropWeaponOnDeath_6x82 {
struct G_PlaceTrap_6x83 {
G_ClientIDHeader header;
le_uint16_t trap_type = 0;
// trap_index is actually the number of traps remaining for this client after setting this one (so it counts backward
// from their total trap count)
le_uint16_t trap_index = 0;
} __packed_ws__(G_PlaceTrap_6x83, 8);
@@ -5192,13 +5244,18 @@ struct G_LevelUpAllTechniques_6x9B {
parray<uint8_t, 3> unused;
} __packed_ws__(G_LevelUpAllTechniques_6x9B, 8);
// 6x9C: Set enemy low game flags (not valid on Episode 3)
// This command only has an effect in Ultimate mode; it sets the low 6 bits of game_flags (those that match 0x3F).
// 6x9C: Set enemy status effect flags (not valid on Episode 3)
// This command only has an effect in Ultimate mode; it sets the low 6 bits of game_flags (those that match 0x3F). This
// essentially separates status effects from the 6x0A command. It's not clear why Sega did this only in Ultimate mode.
struct G_SetEnemyLowGameFlagsUltimate_6x9C {
G_EntityIDHeader header;
// A virtual function is called on the enemy if low_game_flags is equal to any of 0x02, 0x04, 0x10, or 0x20.
le_uint32_t low_game_flags = 0;
// This field is expected to have one of these values (it should not actually be treated as a bit field):
// 0x00000002 = add paralysis status
// 0x00000004 = add shock status
// 0x00000010 = add confuse status
// 0x00000020 = add freeze status
le_uint32_t status_effect_flags = 0;
} __packed_ws__(G_SetEnemyLowGameFlagsUltimate_6x9C, 8);
// 6x9D: Set dead flag (Challenge mode; not valid on Episode 3)
@@ -5389,25 +5446,20 @@ struct G_SetAnimationState_6xAE {
G_ClientIDHeader header;
le_uint16_t animation_number = 0;
le_uint16_t unused = 0;
// This field contains the flags field on the sender's TObjPlayer object. If the bit 04000000 is set in this field,
// then (flags & 1C000000) is or'ed into the TObjPlayer's flags field. All other bits are ignored.
le_uint32_t flags = 0;
// This field contains the player_flags field on the sender's TObjPlayer object. If the bit 04000000 is set in this
// field, then (flags & 1C000000) is or'ed into the TObjPlayer's player_flags field on the receiver's end; all other
// bits are ignored. (For the meanings of these bits, see Parsed6x70Data::convert_player_flags.)
le_uint32_t player_flags = 0;
le_float animation_timer = 0;
} __packed_ws__(G_SetAnimationState_6xAE, 0x10);
// 6xAF: Turn lobby chair (not valid on pre-V3 or GC Trial Edition) (protected on V3/V4)
struct G_TurnLobbyChair_6xAF {
G_ClientIDHeader header;
le_uint32_t angle = 0; // In range [0x0000, 0xFFFF]
} __packed_ws__(G_TurnLobbyChair_6xAF, 8);
// 6xB0: Move lobby chair (not valid on pre-V3 or GC Trial Edition) (protected on V3/V4)
struct G_MoveLobbyChair_6xB0 {
struct G_TurnOrMoveLobbyChair_6xAF_6xB0 {
G_ClientIDHeader header;
le_uint32_t unknown_a1 = 0;
} __packed_ws__(G_MoveLobbyChair_6xB0, 8);
le_uint32_t angle = 0; // In range [0x0000, 0xFFFF]
} __packed_ws__(G_TurnOrMoveLobbyChair_6xAF_6xB0, 8);
// 6xB1: Unknown (not valid on pre-V3 or GC Trial Edition)
// This subcommand is completely ignored.
@@ -5673,7 +5725,7 @@ struct G_BankAction_BB_6xBD {
G_UnusedHeader header;
le_uint32_t item_id = 0;
le_uint32_t meseta_amount = 0;
uint8_t action = 0; // 0 = deposit, 1 = take, 3 = done (close bank window)
uint8_t action = 0; // 0 = deposit, 1 = take, 2 = unknown (compact contents?), 3 = commit (close bank window)
uint8_t item_amount = 0;
le_uint16_t item_index = 0; // 0xFFFF = meseta
} __packed_ws__(G_BankAction_BB_6xBD, 0x10);
@@ -6059,7 +6111,7 @@ struct G_SetMesetaSlotPrizeResult_BB_6xE3 {
ItemData item;
} __packed_ws__(G_SetMesetaSlotPrizeResult_BB_6xE3, 0x18);
// 6xE4: Invalid subcommand (but used as an extension; see end of this file)
// 6xE4: Invalid subcommand (but used as a newserv extension; see end of this file)
// 6xE5: Invalid subcommand
// 6xE6: Invalid subcommand
// 6xE7: Invalid subcommand
@@ -6325,7 +6377,7 @@ struct G_SetPlayerDeck_Ep3_CAx14 {
struct G_HardResetServerState_Ep3_CAx15 {
G_CardServerDataCommandHeader header = {0xB3, sizeof(G_HardResetServerState_Ep3_CAx15) / 4, 0, 0x15, 0, 0, 0, 0, 0};
// No arguments
// No arguments.
} __packed_ws__(G_HardResetServerState_Ep3_CAx15, 0x10);
// 6xB5x17: Unknown
@@ -6333,7 +6385,7 @@ struct G_HardResetServerState_Ep3_CAx15 {
struct G_Unknown_Ep3_6xB5x17 {
G_CardBattleCommandHeader header = {0xB5, sizeof(G_Unknown_Ep3_6xB5x17) / 4, 0, 0x17, 0, 0, 0};
// No arguments
// No arguments.
} __packed_ws__(G_Unknown_Ep3_6xB5x17, 8);
// 6xB5x1A: Force disconnect
@@ -6343,7 +6395,7 @@ struct G_Unknown_Ep3_6xB5x17 {
struct G_ForceDisconnect_Ep3_6xB5x1A {
G_CardBattleCommandHeader header = {0xB5, sizeof(G_ForceDisconnect_Ep3_6xB5x1A) / 4, 0, 0x1A, 0, 0, 0};
// No arguments
// No arguments.
} __packed_ws__(G_ForceDisconnect_Ep3_6xB5x1A, 8);
// 6xB3x1B / CAx1B: Set player name
@@ -6416,7 +6468,7 @@ struct G_EndBattle_Ep3_CAx21 {
struct G_Unknown_Ep3_6xB4x22 {
G_CardBattleCommandHeader header = {0xB4, sizeof(G_Unknown_Ep3_6xB4x22) / 4, 0, 0x22, 0, 0, 0};
// No arguments
// No arguments.
} __packed_ws__(G_Unknown_Ep3_6xB4x22, 8);
// 6xB4x23: Unknown
@@ -6559,7 +6611,7 @@ struct G_SetDeckInBattleSetupMenu_Ep3_6xB5x2F {
struct G_Unused_Ep3_6xB5x30 {
G_CardBattleCommandHeader header = {0xB5, sizeof(G_Unused_Ep3_6xB5x30) / 4, 0, 0x30, 0, 0, 0};
// No arguments
// No arguments.
} __packed_ws__(G_Unused_Ep3_6xB5x30, 8);
// 6xB5x31: Confirm deck selection
@@ -7064,7 +7116,7 @@ struct G_RejectBattleStartRequest_Ep3_6xB4x53 {
// 30 (C->S): Extended player info
// Requested with the GetExtendedPlayerInfo patch. Format depends on version:
// DC v2: PSODCV2CharacterFile
// GC v3: PSOGCCharacterFile::Character
// GC v3: PSOGCCharacterFile::Character (including big-endian fields, unlike 61)
// XB v3: PSOXBCharacterFile::Character
// 6xE4: Increment enemy damage
+8
View File
@@ -37,6 +37,14 @@ struct VectorXZF {
inline double norm2() const {
return ((this->x * this->x) + (this->z * this->z));
}
inline double dist(const VectorXZF& other) const {
return sqrt(this->dist2(other));
}
inline double dist2(const VectorXZF& other) const {
double x = this->x - other.x;
double z = this->z - other.z;
return ((x * x) + (z * z));
}
inline VectorXZF rotate_y(double angle) const {
double s = sin(angle);
+150 -130
View File
@@ -134,67 +134,57 @@ CommonItemSet::Table::Table(std::shared_ptr<const Table> prev_table, const phosg
parse_field("UnitMaxStarsTable", this->unit_max_stars_table, prev_table ? &prev_table->unit_max_stars_table : nullptr);
parse_field("BoxItemClassProbTable", this->box_item_class_prob_table, prev_table ? &prev_table->box_item_class_prob_table : nullptr);
const auto* enemy_meseta_ranges_json = json.count("EnemyMesetaRanges") ? &json.at("EnemyMesetaRanges").as_dict() : nullptr;
const auto* enemy_type_drop_probs_json = json.count("EnemyTypeDropProbs") ? &json.at("EnemyTypeDropProbs").as_dict() : nullptr;
const auto* enemy_item_classes_json = json.count("EnemyItemClasses") ? &json.at("EnemyItemClasses").as_dict() : nullptr;
if (enemy_item_classes_json) {
// Unspecified is 0xFF, not 0, unlike the other enemy-indexed arrays (except for [0], apparently... sigh)
this->enemy_item_classes[0] = 0;
this->enemy_item_classes.clear_after(1, 0xFF);
if (json.count("EnemyMesetaRanges")) {
const auto& dict = json.at("EnemyMesetaRanges").as_dict();
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
from_json_into(*dict.at(phosg::name_for_enum(enemy_type)), this->enemy_type_meseta_ranges[enemy_type]);
} catch (const out_of_range&) {
}
}
} else {
this->enemy_type_meseta_ranges = prev_table->enemy_type_meseta_ranges;
}
for (size_t z = 0; z < NUM_RT_INDEXES_V4; z++) {
auto types = enemy_types_for_rare_table_index(this->episode, z);
vector<string> names;
if (types.empty()) {
names.emplace_back(std::format("!{:02X}", z));
} else {
for (auto type : types) {
names.emplace_back(phosg::name_for_enum(type));
if (json.count("EnemyTypeDropProbs")) {
const auto& dict = json.at("EnemyTypeDropProbs").as_dict();
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
this->enemy_type_drop_probs[enemy_type] = dict.at(phosg::name_for_enum(enemy_type))->as_int();
} catch (const out_of_range&) {
}
}
for (const auto& name : names) {
if (enemy_meseta_ranges_json) {
try {
from_json_into(*enemy_meseta_ranges_json->at(name), this->enemy_meseta_ranges[z]);
} catch (const out_of_range&) {
}
} else if (prev_table) {
this->enemy_meseta_ranges = prev_table->enemy_meseta_ranges;
}
if (enemy_type_drop_probs_json) {
try {
this->enemy_type_drop_probs[z] = enemy_type_drop_probs_json->at(name)->as_int();
} catch (const out_of_range&) {
}
} else if (prev_table) {
this->enemy_type_drop_probs = prev_table->enemy_type_drop_probs;
}
if (enemy_item_classes_json) {
try {
this->enemy_item_classes[z] = enemy_item_classes_json->at(name)->as_int();
} catch (const out_of_range&) {
}
} else if (prev_table) {
this->enemy_item_classes = prev_table->enemy_item_classes;
} else {
this->enemy_type_drop_probs = prev_table->enemy_type_drop_probs;
}
if (json.count("EnemyItemClasses")) {
const auto& dict = json.at("EnemyItemClasses").as_dict();
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
this->enemy_type_item_classes[enemy_type] = dict.at(phosg::name_for_enum(enemy_type))->as_int();
} catch (const out_of_range&) {
}
}
} else {
this->enemy_type_item_classes = prev_table->enemy_type_item_classes;
}
}
static const char* name_for_common_item_class(uint8_t item_class) {
switch (item_class) {
case 0x00:
return "WEAPON ";
return "WEAPON";
case 0x01:
return "ARMOR ";
return "ARMOR";
case 0x02:
return "SHIELD ";
return "SHIELD";
case 0x03:
return "UNIT ";
return "UNIT";
case 0x04:
return "TOOL ";
return "TOOL";
case 0x05:
return "MESETA ";
return "MESETA";
case 0x06:
return "NOTHING";
default:
@@ -203,42 +193,29 @@ static const char* name_for_common_item_class(uint8_t item_class) {
}
void CommonItemSet::Table::print(FILE* stream) const {
const auto& meseta_ranges = this->enemy_meseta_ranges;
const auto& meseta_ranges = this->enemy_type_meseta_ranges;
const auto& drop_probs = this->enemy_type_drop_probs;
const auto& item_classes = this->enemy_item_classes;
const auto& item_classes = this->enemy_type_item_classes;
phosg::fwrite_fmt(stream, "Enemy tables:\n");
phosg::fwrite_fmt(stream, " ## $LOW $HIGH DAR% ITEM ENEMIES\n");
for (size_t z = 0; z < NUM_RT_INDEXES_V4; z++) {
string enemies_str;
for (EnemyType enemy_type : enemy_types_for_rare_table_index(this->episode, z)) {
if (!enemies_str.empty()) {
enemies_str += ", ";
}
enemies_str += phosg::name_for_enum(enemy_type);
}
if (drop_probs[z] || !enemies_str.empty()) {
phosg::fwrite_fmt(stream, " {:02X} {:5} {:5} {:3}% {:02X}:{} {}\n",
z, meseta_ranges[z].min, meseta_ranges[z].max, drop_probs[z], item_classes[z],
name_for_common_item_class(item_classes[z]), enemies_str);
} else {
phosg::fwrite_fmt(stream, " {:02X} ----- ----- 0% --\n", z);
phosg::fwrite_fmt(stream, " ##:ENEMY $LOW $HIGH DAR% ITEM\n");
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
const auto& def = type_definition_for_enemy(enemy_type);
try {
const auto& meseta_range = meseta_ranges.at(enemy_type);
const auto& drop_prob = drop_probs.at(enemy_type);
const auto& item_class = item_classes.at(enemy_type);
phosg::fwrite_fmt(stream, " {:02X}:{:<23} {:5} {:5} {:3}% {:02X}:{:<7}\n",
def.rt_index, phosg::name_for_enum(enemy_type),
meseta_range.min, meseta_range.max, drop_prob, item_class,
name_for_common_item_class(item_class));
} catch (const out_of_range&) {
phosg::fwrite_fmt(stream, " {:02X}:{:<23} ----- ----- ---- --:-------\n",
def.rt_index, phosg::name_for_enum(enemy_type));
}
}
static const array<const char*, 12> base_weapon_type_names = {
"SABER ",
"SWORD ",
"DAGGER ",
"PARTISAN",
"SLICER ",
"HANDGUN ",
"RIFLE ",
"MECHGUN ",
"SHOT ",
"CANE ",
"ROD ",
"WAND ",
};
"SABER", "SWORD", "DAGGER", "PARTISAN", "SLICER", "HANDGUN", "RIFLE", "MECHGUN", "SHOT", "CANE", "ROD", "WAND"};
phosg::fwrite_fmt(stream, "Base weapon config:\n");
phosg::fwrite_fmt(stream, " TYPE PROB [SB AL] FLOORS\n");
for (size_t z = 0; z < 12; z++) {
@@ -256,7 +233,7 @@ void CommonItemSet::Table::print(FILE* stream) const {
floor_to_class[x] = this->subtype_base_table[z] + (x / this->subtype_area_length_table[z]);
}
}
phosg::fwrite_fmt(stream, " {:02X}:{} {:3}% [{:02X} {:02X}] {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}\n",
phosg::fwrite_fmt(stream, " {:02X}:{:<8} {:3}% [{:02X} {:02X}] {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}\n",
z, base_weapon_type_names[z], this->base_weapon_type_prob_table[z],
this->subtype_base_table[z], this->subtype_area_length_table[z],
floor_to_class[0], floor_to_class[1], floor_to_class[2], floor_to_class[3], floor_to_class[4],
@@ -413,20 +390,50 @@ void CommonItemSet::Table::print_diff(FILE* stream, const Table& other) const {
phosg::format_data_string(&this->armor_slot_count_prob_table, sizeof(this->armor_slot_count_prob_table)),
phosg::format_data_string(&other.armor_slot_count_prob_table, sizeof(other.armor_slot_count_prob_table)));
}
if (this->enemy_meseta_ranges != other.enemy_meseta_ranges) {
phosg::fwrite_fmt(stream, "> enemy_meseta_ranges: {} -> {}\n",
phosg::format_data_string(&this->enemy_meseta_ranges, sizeof(this->enemy_meseta_ranges)),
phosg::format_data_string(&other.enemy_meseta_ranges, sizeof(other.enemy_meseta_ranges)));
auto format_enemy_range_table = [&](const std::unordered_map<EnemyType, Range<uint16_t>>& table) -> std::string {
string ret = "";
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
const auto& range = table.at(enemy_type);
if (!ret.empty()) {
ret += ",";
}
ret += std::format("{}=[{},{}]", phosg::name_for_enum(enemy_type), range.min, range.max);
} catch (const out_of_range&) {
}
}
return ret;
};
auto format_enemy_u8_table = [&](const std::unordered_map<EnemyType, uint8_t>& table) -> std::string {
string ret = "";
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
uint8_t value = table.at(enemy_type);
if (!ret.empty()) {
ret += ",";
}
ret += std::format("{}={}", phosg::name_for_enum(enemy_type), value);
} catch (const out_of_range&) {
}
}
return ret;
};
if (this->enemy_type_meseta_ranges != other.enemy_type_meseta_ranges) {
phosg::fwrite_fmt(stream, "> enemy_type_meseta_ranges: {} -> {}\n",
format_enemy_range_table(this->enemy_type_meseta_ranges),
format_enemy_range_table(other.enemy_type_meseta_ranges));
}
if (this->enemy_type_drop_probs != other.enemy_type_drop_probs) {
phosg::fwrite_fmt(stream, "> enemy_type_drop_probs: {} -> {}\n",
phosg::format_data_string(&this->enemy_type_drop_probs, sizeof(this->enemy_type_drop_probs)),
phosg::format_data_string(&other.enemy_type_drop_probs, sizeof(other.enemy_type_drop_probs)));
format_enemy_u8_table(this->enemy_type_drop_probs),
format_enemy_u8_table(other.enemy_type_drop_probs));
}
if (this->enemy_item_classes != other.enemy_item_classes) {
phosg::fwrite_fmt(stream, "> enemy_item_classes: {} -> {}\n",
phosg::format_data_string(&this->enemy_item_classes, sizeof(this->enemy_item_classes)),
phosg::format_data_string(&other.enemy_item_classes, sizeof(other.enemy_item_classes)));
if (this->enemy_type_item_classes != other.enemy_type_item_classes) {
phosg::fwrite_fmt(stream, "> enemy_type_item_classes: {} -> {}\n",
format_enemy_u8_table(this->enemy_type_item_classes),
format_enemy_u8_table(other.enemy_type_item_classes));
}
if (this->box_meseta_ranges != other.box_meseta_ranges) {
phosg::fwrite_fmt(stream, "> box_meseta_ranges: {} -> {}\n",
@@ -517,44 +524,45 @@ phosg::JSON CommonItemSet::Table::json(std::shared_ptr<const Table> prev_table)
ret.emplace("ArmorSlotCountProbTable", to_json(this->armor_slot_count_prob_table));
}
bool needs_enemy_meseta_ranges = (!prev_table || (this->enemy_meseta_ranges != prev_table->enemy_meseta_ranges));
bool needs_enemy_type_drop_probs = (!prev_table || (this->enemy_type_drop_probs != prev_table->enemy_type_drop_probs));
bool needs_enemy_item_classes = (!prev_table || (this->enemy_item_classes != prev_table->enemy_item_classes));
if (needs_enemy_meseta_ranges || needs_enemy_type_drop_probs || needs_enemy_item_classes) {
phosg::JSON enemy_meseta_ranges_json = phosg::JSON::dict();
bool needs_enemy_type_meseta_ranges = (!prev_table ||
(this->enemy_type_meseta_ranges != prev_table->enemy_type_meseta_ranges));
bool needs_enemy_type_drop_probs = (!prev_table ||
(this->enemy_type_drop_probs != prev_table->enemy_type_drop_probs));
bool needs_enemy_type_item_classes = (!prev_table ||
(this->enemy_type_item_classes != prev_table->enemy_type_item_classes));
if (needs_enemy_type_meseta_ranges || needs_enemy_type_drop_probs || needs_enemy_type_item_classes) {
phosg::JSON enemy_type_meseta_ranges_json = phosg::JSON::dict();
phosg::JSON enemy_type_drop_probs_json = phosg::JSON::dict();
phosg::JSON enemy_item_classes_json = phosg::JSON::dict();
for (size_t z = 0; z < NUM_RT_INDEXES_V4; z++) {
auto types = enemy_types_for_rare_table_index(this->episode, z);
vector<string> names;
if (types.empty()) {
names.emplace_back(std::format("!{:02X}", z));
} else {
for (auto type : types) {
names.emplace_back(phosg::name_for_enum(type));
phosg::JSON enemy_type_item_classes_json = phosg::JSON::dict();
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
auto name = phosg::name_for_enum(enemy_type);
if (needs_enemy_type_meseta_ranges) {
try {
enemy_type_meseta_ranges_json.emplace(name, to_json(this->enemy_type_meseta_ranges.at(enemy_type)));
} catch (const std::out_of_range&) {
}
}
for (const auto& name : names) {
if (needs_enemy_meseta_ranges && (!types.empty() || !this->enemy_meseta_ranges[z].empty())) {
enemy_meseta_ranges_json.emplace(name, to_json(this->enemy_meseta_ranges[z]));
if (needs_enemy_type_drop_probs) {
try {
enemy_type_drop_probs_json.emplace(name, this->enemy_type_drop_probs.at(enemy_type));
} catch (const std::out_of_range&) {
}
if (needs_enemy_type_drop_probs && (!types.empty() || this->enemy_type_drop_probs[z])) {
enemy_type_drop_probs_json.emplace(name, this->enemy_type_drop_probs[z]);
}
if (needs_enemy_item_classes && (!types.empty() || (this->enemy_item_classes[z] != ((z == 0) ? 0x00 : 0xFF)))) {
enemy_item_classes_json.emplace(name, this->enemy_item_classes[z]);
}
if (needs_enemy_type_item_classes) {
try {
enemy_type_item_classes_json.emplace(name, this->enemy_type_item_classes.at(enemy_type));
} catch (const std::out_of_range&) {
}
}
}
if (needs_enemy_meseta_ranges) {
ret.emplace("EnemyMesetaRanges", std::move(enemy_meseta_ranges_json));
if (needs_enemy_type_meseta_ranges) {
ret.emplace("EnemyMesetaRanges", std::move(enemy_type_meseta_ranges_json));
}
if (needs_enemy_type_drop_probs) {
ret.emplace("EnemyTypeDropProbs", std::move(enemy_type_drop_probs_json));
}
if (needs_enemy_item_classes) {
ret.emplace("EnemyItemClasses", std::move(enemy_item_classes_json));
if (needs_enemy_type_item_classes) {
ret.emplace("EnemyItemClasses", std::move(enemy_type_item_classes_json));
}
}
@@ -705,13 +713,27 @@ void CommonItemSet::Table::parse_itempt_t(const phosg::StringReader& r, bool is_
this->grind_prob_table = r.pget<parray<parray<uint8_t, 4>, 9>>(offsets.grind_prob_table_offset);
this->armor_shield_type_index_prob_table = r.pget<parray<uint8_t, 0x05>>(offsets.armor_shield_type_index_prob_table_offset);
this->armor_slot_count_prob_table = r.pget<parray<uint8_t, 0x05>>(offsets.armor_slot_count_prob_table_offset);
const auto& data = r.pget<parray<Range<U16T<BE>>, NUM_RT_INDEXES_V3>>(offsets.enemy_meseta_ranges_offset);
for (size_t z = 0; z < data.size(); z++) {
this->enemy_meseta_ranges[z] = Range<uint16_t>{data[z].min, data[z].max};
const auto& enemy_rt_index_meseta_ranges = r.pget<parray<Range<U16T<BE>>, NUM_RT_INDEXES_V3>>(
offsets.enemy_rt_index_meseta_ranges_offset);
const auto& enemy_rt_index_drop_probs = r.pget<parray<uint8_t, NUM_RT_INDEXES_V3>>(
offsets.enemy_rt_index_drop_probs_offset);
const auto& enemy_rt_index_item_classes = r.pget<parray<uint8_t, NUM_RT_INDEXES_V3>>(
offsets.enemy_rt_index_item_classes_offset);
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
const auto& def = type_definition_for_enemy(enemy_type);
if (def.valid_in_episode(this->episode) && (def.rt_index < enemy_rt_index_meseta_ranges.size())) {
const auto& meseta_range = enemy_rt_index_meseta_ranges[def.rt_index];
if (meseta_range.max > 0) {
this->enemy_type_meseta_ranges.emplace(enemy_type, Range<uint16_t>{meseta_range.min, meseta_range.max});
}
if (enemy_rt_index_drop_probs[def.rt_index] > 0) {
this->enemy_type_drop_probs.emplace(enemy_type, enemy_rt_index_drop_probs[def.rt_index]);
}
if (enemy_rt_index_item_classes[def.rt_index] != 0xFF) {
this->enemy_type_item_classes.emplace(enemy_type, enemy_rt_index_item_classes[def.rt_index]);
}
}
}
this->enemy_type_drop_probs = r.pget<parray<uint8_t, NUM_RT_INDEXES_V3>>(offsets.enemy_type_drop_probs_offset);
this->enemy_item_classes = r.pget<parray<uint8_t, NUM_RT_INDEXES_V3>>(offsets.enemy_item_classes_offset);
this->enemy_item_classes.clear_after(NUM_RT_INDEXES_V3, 0xFF);
{
const auto& data = r.pget<parray<Range<U16T<BE>>, 0x0A>>(offsets.box_meseta_ranges_offset);
for (size_t z = 0; z < data.size(); z++) {
@@ -873,7 +895,7 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
section_id);
};
for (Episode episode : ALL_EPISODES_V4) {
for (Episode episode : ALL_EPISODES_V3) {
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
for (size_t section_id = 0; section_id < 10; section_id++) {
phosg::StringReader r;
@@ -898,17 +920,15 @@ GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gs
}
}
if (episode != Episode::EP4) {
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
try {
auto r = gsl.get_reader(filename_for_table(episode, difficulty, 0, true));
auto table = make_shared<Table>(r, is_big_endian, true, episode);
for (size_t section_id = 0; section_id < 10; section_id++) {
this->tables.emplace(this->key_for_table(episode, GameMode::CHALLENGE, difficulty, section_id), table);
}
} catch (const out_of_range&) {
// GC NTE doesn't have Ep2 challenge; just skip adding the table
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
try {
auto r = gsl.get_reader(filename_for_table(episode, difficulty, 0, true));
auto table = make_shared<Table>(r, is_big_endian, true, episode);
for (size_t section_id = 0; section_id < 10; section_id++) {
this->tables.emplace(this->key_for_table(episode, GameMode::CHALLENGE, difficulty, section_id), table);
}
} catch (const out_of_range&) {
// GC NTE doesn't have Ep2 challenge; just skip adding the table
}
}
}
+11 -10
View File
@@ -42,9 +42,10 @@ public:
parray<parray<uint8_t, 4>, 9> grind_prob_table;
parray<uint8_t, 0x05> armor_shield_type_index_prob_table;
parray<uint8_t, 0x05> armor_slot_count_prob_table;
parray<Range<uint16_t>, NUM_RT_INDEXES_V4> enemy_meseta_ranges;
parray<uint8_t, NUM_RT_INDEXES_V4> enemy_type_drop_probs;
parray<uint8_t, NUM_RT_INDEXES_V4> enemy_item_classes;
// Note: PSO originally uses arrays indexed by rt_index here, but we index enemies by the EnemyType enum instead
std::unordered_map<EnemyType, Range<uint16_t>> enemy_type_meseta_ranges;
std::unordered_map<EnemyType, uint8_t> enemy_type_drop_probs;
std::unordered_map<EnemyType, uint8_t> enemy_type_item_classes;
parray<Range<uint16_t>, 0x0A> box_meseta_ranges;
bool has_rare_bonus_value_prob_table;
parray<parray<uint16_t, 6>, 0x17> bonus_value_prob_table;
@@ -126,17 +127,17 @@ public:
// V2/V3: -> parray<uint8_t, 0x05>
/* 14 */ U32T<BE> armor_slot_count_prob_table_offset;
// This array (indexed by enemy_type) specifies the range of meseta values that each enemy can drop.
// This array (indexed by rt_index) specifies the range of meseta values that each enemy can drop.
// V2/V3: -> parray<Range<U16T>, NUM_RT_INDEXES_V3>
/* 18 */ U32T<BE> enemy_meseta_ranges_offset;
/* 18 */ U32T<BE> enemy_rt_index_meseta_ranges_offset;
// Each byte in this table (indexed by enemy_type) represents the percent chance that the enemy drops anything at
// Each byte in this table (indexed by rt_index) represents the percent chance that the enemy drops anything at
// all. (This check is done before the rare drop check, so the chance of getting a rare item from an enemy is
// essentially this probability multiplied by the rare drop rate.)
// V2/V3: -> parray<uint8_t, NUM_RT_INDEXES_V3>
/* 1C */ U32T<BE> enemy_type_drop_probs_offset;
/* 1C */ U32T<BE> enemy_rt_index_drop_probs_offset;
// Each byte in this table (indexed by enemy_type) represents the class of item that can drop. The values are:
// Each byte in this table (indexed by rt_index) represents the class of item that can drop. The values are:
// 00 = weapon
// 01 = armor
// 02 = shield
@@ -145,7 +146,7 @@ public:
// 05 = meseta
// Anything else = no item
// V2/V3: -> parray<uint8_t, NUM_RT_INDEXES_V3>
/* 20 */ U32T<BE> enemy_item_classes_offset;
/* 20 */ U32T<BE> enemy_rt_index_item_classes_offset;
// This table (indexed by area - 1) specifies the ranges of meseta values that can drop from boxes.
// V2/V3: -> parray<Range<U16T>, 0x0A>
@@ -229,7 +230,7 @@ public:
// This index probability table determines which type of items drop from boxes. The table is indexed as
// [item_class][area - 1], with item_class as the result value (that is, in the example below, the game looks at
// a single column and sums the values going down, then the chosen item class is one of the row indexes based on
// the weight values in the column.) The resulting value has the same meaning as in enemy_item_classes above.
// the weight values in the column.) The resulting value has the same meaning as in enemy_rt_index_item_classes.
// For example, this array might look like the following:
// [07 07 08 08 06 07 08 09 09 0A] // Chances per area of a weapon drop
// [02 02 02 02 03 02 02 02 03 03] // Chances per area of an armor drop
+2 -1
View File
@@ -13,6 +13,7 @@ static constexpr size_t NUM_RT_INDEXES_V3 = 0x64;
static constexpr size_t NUM_RT_INDEXES_V4 = 0x70;
enum class EnemyType : uint8_t {
MIN_VALUE = 0,
UNKNOWN = 0,
NONE,
NON_ENEMY_NPC,
@@ -146,7 +147,7 @@ enum class EnemyType : uint8_t {
ZOL_GIBBON,
ZU_CRATER,
ZU_DESERT,
MAX_ENEMY_TYPE,
MAX_VALUE,
};
struct EnemyTypeDefinition {
+24 -22
View File
@@ -675,8 +675,8 @@ struct CardDefinition {
// - (rate / 10) % 100 (that is, the tens and hundreds decimal places) specify the environment number + 1. For
// example, if this field contains 5, then this drop only applies if the battle took place at Molae Venti
// (environment number 4). If this field is zero, the drop applies regardless of where the battle took place.
// - rate / 1000 (the thousands decimal place) specifies the rarity class. This can be any number in the range [0,
// 9], and affects how likely the card is to appear based on the player's level. See below for details.
// - (rate / 1000) % 10 (the thousands decimal place) specifies the rarity class. This can be any number in the range
// [0, 9], and affects how likely the card is to appear based on the player's level. See below for details.
// - rate / 10000 (the ten-thousands decimal place) specifies if the drop rate applies only if the player used a
// Hunters deck (1), only if they used an Arkz deck (2), or if they used any deck (0).
//
@@ -684,16 +684,16 @@ struct CardDefinition {
// that applies, the game adds the card ID into an appropriate bucket based on the rarity class. (If both drop rates
// for a card apply, the card ID is added twice.) The player's level class is then computed according to the
// following table:
// 1 2 3 4 5 6 7 8 9 10
// CLvOff 1-2 3-4 5-9 10-14 15-19 20-25 26-29 30-39 40-49 50+
// CLvOn 1-2 3-4 5-10 11-16 17-23 24-32 33-39 40-49 50-99 100+
// Level class 1 2 3 4 5 6 7 8 9 10
// CLvOff 1-2 3-4 5-9 10-14 15-19 20-25 26-29 30-39 40-49 50+
// CLvOn 1-2 3-4 5-10 11-16 17-23 24-32 33-39 40-49 50-99 100+
// For the purposes of this computation, the player's level is used by default (CLvOn or CLvOff), but the map may
// override it - see win_level_override and loss_level_override in MapDefinition. This specifies which row in the
// following tables will be used.
//
// Cards are then chosen from the buckets with a weighted distribution according to these tables (row is player's
// level class, column is card's rarity class):
// Offline:
// Offline ---------------------------------------------------------------
// LC | RC = 0 1 2 3 4 5 6 7 8 9
// 1 | 8000 2000 50
// 2 | 6000 3500 500 20
@@ -705,7 +705,7 @@ struct CardDefinition {
// 8 | 1789 2100 2100 2100 1100 800 10 1
// 9 | 14620 20000 21000 22000 13000 9000 300 80
// 10 | 133997 190000 200000 200000 150000 120000 5000 1000 2 1
// Online:
// Online --------------------------------------------------------------------
// LC | RC = 0 1 2 3 4 5 6 7 8 9
// 1 | 8000 2000 50
// 2 | 6000 3500 500 50
@@ -729,31 +729,33 @@ struct CardDefinition {
// - In the blue pack, the restricted cards must be creature cards.
// - In the red pack, the restricted cards must be item cards.
// - In the green pack, the restricted cards must be action cards.
// - In the black pack, the restricted cards may be any card type.
// For example, if you get a B+ rank after winning a battle and pick the green pack, you will always get at least two
// action cards.
// action cards. The remaining cards can be any type, so this restriction essentially biases the blue, red, and green
// packs toward specific card types.
//
// The game then samples N card IDs from the appropriate buckets (where N is the number chosen above), but for the
// first 1 or 2 cards, it applies the restriction described above and re-draws if the card is the wrong type. After
// sampling the N card IDs, it sorts them and presents them to the player.
// The game then samples N card IDs from the appropriate buckets (where N is the number chosen above based on the
// post-battle rank), but for the first 1 or 2 cards, it applies the restriction described above and re-draws if the
// card is the wrong type. After sampling the N card IDs, it sorts them and presents them to the player.
//
// There is one more effect to consider after cards are chosen: cards may randomly transform into VIP cards or into
// stronger (and rarer) cards. The chance of each of these occurring is based on the rarity of that card that may
// stronger (and rarer) cards. The chance of each of these occurring is based on the rarity of the card that may
// transform, and the number of copies of that card which the player already has. In the below table, P(activate) is
// the probability that any transformation is attempted at all; if this check passes, the player sees the glow effect
// and "The change will occur..." text. P(vip) is the probability that the card becomes a VIP card, after the glow
// effect plays. P(rare) is the probability of the card becoming a rarer card after the glow effect. Therefore, the
// final probability that a card will transform into a VIP card is P(activate) * P(vip), and the final probability of
// transforming into a rarer card is P(activate) * P(rare).
// ======== Card rank N4-N1 ======== ======== Card rank R4-R1 ========
// Count P(activate) P(rare) P(vip) P(activate) P(rare) P(vip)
// 0-4 0% 0% 0% 0% 0% 0%
// 5-10 1.923077% 55% 0.5% 2.0408163% 55% 0.5%
// 11-16 2.1276595% 60% 0.45454544% 2.2727273% 60% 0.4761905%
// 17-24 2.3809524% 70% 0.4347826% 2.5641026% 70% 0.45454544%
// 25-32 2.7027028% 70% 0.4% 2.9411765% 70% 0.5%
// 33-40 3.125% 80% 0.38461538% 3.448276% 70% 0.5%
// 41-52 3.7037037% 80% 0.35714286% 4.1666668% 80% 0.45454544%
// 53-99 5% 90% 0.33333334% 5.263158% 90% 0.4347826%
// ========= Card rank N4-N1 ========= ========= Card rank R4-R1 =========
// Count P(activate) P(rare) P(vip) P(activate) P(rare) P(vip)
// 0-4 0% 0% 0% 0% 0% 0%
// 5-10 1.923077% 55% 0.5% 2.0408163% 55% 0.5%
// 11-16 2.1276595% 60% 0.45454544% 2.2727273% 60% 0.4761905%
// 17-24 2.3809524% 70% 0.4347826% 2.5641026% 70% 0.45454544%
// 25-32 2.7027028% 70% 0.4% 2.9411765% 70% 0.5%
// 33-40 3.125% 80% 0.38461538% 3.448276% 70% 0.5%
// 41-52 3.7037037% 80% 0.35714286% 4.1666668% 80% 0.45454544%
// 53-99 5% 90% 0.33333334% 5.263158% 90% 0.4347826%
//
// If a transformation occurs, the card transforms to a card of a different rank. First, the game consults the
// following table to determine the rank of the resulting card (original card's rank on the left, new card's rank
+2
View File
@@ -44,6 +44,7 @@ void GameServer::listen(
shared_ptr<Client> GameServer::connect_channel(shared_ptr<Channel> ch, uint16_t port, ServerBehavior initial_state) {
auto c = make_shared<Client>(this->shared_from_this(), ch, initial_state);
c->listener_port = port;
this->log.info_f("Client connected: C-{:X} via TSI-{}-{}-{}",
c->id, port, phosg::name_for_enum(ch->version), phosg::name_for_enum(initial_state));
@@ -133,6 +134,7 @@ shared_ptr<Client> GameServer::create_client(
phosg::TerminalFormat::FG_YELLOW,
phosg::TerminalFormat::FG_GREEN);
auto c = make_shared<Client>(this->shared_from_this(), channel, listen_sock->behavior);
c->listener_port = listen_sock->endpoint.port();
this->log.info_f("Client connected: C-{:X} via {}", c->id, listen_sock->name);
return c;
+151
View File
@@ -5,6 +5,7 @@
#include <phosg/Network.hh>
#include <string>
#include <map>
#include <vector>
#include "GameServer.hh"
@@ -54,6 +55,156 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> state)
co_return nullptr;
});
this->router.add(HTTPRequest::Method::GET, "/metrics", [this](ArgsT&&) -> RetT {
auto version_label = +[](Version v) -> const char* {
if (is_patch(v)) {
return "patch";
} else if (is_v4(v)) {
return "v4";
} else if (is_v3(v)) {
return "v3";
} else if (is_v1_or_v2(v)) {
return "v2";
} else {
return "other";
}
};
auto escape_label = +[](const string& in) -> string {
string out;
for (char ch : in) {
if (ch == '\\') {
out += "\\\\";
} else if (ch == '"') {
out += "\\\"";
} else if (ch == '\n') {
out += "\\n";
} else {
out += ch;
}
}
return out;
};
auto add_metric = [](string& out, const string& name, uint64_t value) -> void {
out += name;
out += " ";
out += std::to_string(value);
out += "\n";
};
auto add_metric_1label = [](string& out, const string& name, const string& label_name, const string& label_value, uint64_t value) -> void {
out += name;
out += "{";
out += label_name;
out += "=\"";
out += label_value;
out += "\"} ";
out += std::to_string(value);
out += "\n";
};
map<string, uint64_t> connected_by_version;
map<string, uint64_t> lobby_players_by_version;
map<string, uint64_t> game_players_by_version;
uint64_t connected_total = 0;
uint64_t lobbies_total = 0;
uint64_t games_total = 0;
uint64_t players_in_lobbies_total = 0;
uint64_t players_in_games_total = 0;
for (const auto& c : this->state->game_server->all_clients()) {
connected_total++;
connected_by_version[version_label(c->version())]++;
}
for (const auto& [_, l] : this->state->id_to_lobby) {
if (l->is_game()) {
games_total++;
} else {
lobbies_total++;
}
for (size_t z = 0; z < l->max_clients; z++) {
auto lc = l->clients[z];
if (!lc) {
continue;
}
const char* v = version_label(lc->version());
if (l->is_game()) {
players_in_games_total++;
game_players_by_version[v]++;
} else {
players_in_lobbies_total++;
lobby_players_by_version[v]++;
}
}
}
string server_name = escape_label(this->state->name);
string revision = escape_label(GIT_REVISION_HASH);
string out;
out += "# HELP pso_newserv_up Whether this newserv HTTP metrics endpoint is reachable\n";
out += "# TYPE pso_newserv_up gauge\n";
add_metric(out, "pso_newserv_up", 1);
out += "# HELP pso_newserv_build_info Build and server identity info\n";
out += "# TYPE pso_newserv_build_info gauge\n";
out += "pso_newserv_build_info{server_name=\"";
out += server_name;
out += "\",revision=\"";
out += revision;
out += "\",build_time=\"";
out += std::to_string(BUILD_TIMESTAMP);
out += "\"} 1\n";
out += "# HELP pso_newserv_clients_connected_total Connected clients, including patch/proxy/menu states\n";
out += "# TYPE pso_newserv_clients_connected_total gauge\n";
add_metric(out, "pso_newserv_clients_connected_total", connected_total);
out += "# HELP pso_newserv_clients_connected Connected clients by coarse version family\n";
out += "# TYPE pso_newserv_clients_connected gauge\n";
for (const auto& [version, count] : connected_by_version) {
add_metric_1label(out, "pso_newserv_clients_connected", "version", version, count);
}
out += "# HELP pso_newserv_lobbies_total Non-game lobby count\n";
out += "# TYPE pso_newserv_lobbies_total gauge\n";
add_metric(out, "pso_newserv_lobbies_total", lobbies_total);
out += "# HELP pso_newserv_games_total Game room count\n";
out += "# TYPE pso_newserv_games_total gauge\n";
add_metric(out, "pso_newserv_games_total", games_total);
out += "# HELP pso_newserv_players_in_lobbies_total Players currently in non-game lobbies\n";
out += "# TYPE pso_newserv_players_in_lobbies_total gauge\n";
add_metric(out, "pso_newserv_players_in_lobbies_total", players_in_lobbies_total);
out += "# HELP pso_newserv_players_in_games_total Players currently in game rooms\n";
out += "# TYPE pso_newserv_players_in_games_total gauge\n";
add_metric(out, "pso_newserv_players_in_games_total", players_in_games_total);
out += "# HELP pso_newserv_players_in_lobbies Players currently in non-game lobbies by coarse version family\n";
out += "# TYPE pso_newserv_players_in_lobbies gauge\n";
for (const auto& [version, count] : lobby_players_by_version) {
add_metric_1label(out, "pso_newserv_players_in_lobbies", "version", version, count);
}
out += "# HELP pso_newserv_players_in_games Players currently in game rooms by coarse version family\n";
out += "# TYPE pso_newserv_players_in_games gauge\n";
for (const auto& [version, count] : game_players_by_version) {
add_metric_1label(out, "pso_newserv_players_in_games", "version", version, count);
}
co_return RouterRetT(RawResponse{
.content_type = "text/plain; version=0.0.4; charset=utf-8",
.data = std::move(out),
});
});
this->router.add(HTTPRequest::Method::GET, "/y/clients", [this](ArgsT&&) -> RetT {
auto res = make_shared<phosg::JSON>(phosg::JSON::list());
for (const auto& c : this->state->game_server->all_clients()) {
+110 -92
View File
@@ -38,6 +38,7 @@ ItemCreator::ItemCreator(
shared_ptr<const BattleRules> restrictions)
: log(std::format("[ItemCreator:{}/{}/{}/{}] ", phosg::name_for_enum(stack_limits->version), abbreviation_for_mode(mode), abbreviation_for_difficulty(difficulty), section_id), lobby_log.min_level),
logic_version(stack_limits->version),
is_legacy_replay(false),
stack_limits(stack_limits),
mode(mode),
difficulty(difficulty),
@@ -136,13 +137,13 @@ uint8_t ItemCreator::table_index_for_area(uint8_t area) const {
return data[area];
}
ItemCreator::DropResult ItemCreator::on_box_item_drop(uint8_t area) {
ItemCreator::DropResult ItemCreator::on_box_item_drop(uint8_t area, bool force_rare) {
try {
uint8_t table_index = this->table_index_for_area(area);
this->log.info_f("Box drop checks for area {:02X} (table index {:02X})", area, table_index);
DropResult res;
res.item = this->check_rare_specs_and_create_rare_box_item(area);
res.item = this->check_rare_specs_and_create_rare_box_item(area, force_rare);
if (!res.item.empty()) {
res.is_from_rare_table = true;
} else {
@@ -188,34 +189,40 @@ ItemCreator::DropResult ItemCreator::on_box_item_drop(uint8_t area) {
}
}
ItemCreator::DropResult ItemCreator::on_monster_item_drop(uint32_t enemy_type, uint8_t area) {
ItemCreator::DropResult ItemCreator::on_monster_item_drop(EnemyType enemy_type, uint8_t area, bool force_rare) {
try {
// Note: The original GC implementation uses (enemy_type > 0x58) here; we extend it to the full array size for BB
if (enemy_type >= NUM_RT_INDEXES_V4) {
this->log.warning_f("Invalid enemy type: {:X}", enemy_type);
return DropResult();
}
this->log.info_f("Enemy type: {:X}", enemy_type);
// Note: The original implementation has a bounds check for enemy_type here, because it uses rt_index instead
// if (enemy_type >= NUM_RT_INDEXES_V4) {
// this->log.warning_f("Invalid enemy type: {:X}", enemy_type);
// return DropResult();
// }
this->log.info_f("Enemy type: {}", phosg::name_for_enum(enemy_type));
auto pt = this->pt(area);
uint8_t type_drop_prob = pt->enemy_type_drop_probs.at(enemy_type);
uint8_t drop_sample = this->rand_int(100);
if (drop_sample >= type_drop_prob) {
this->log.info_f("Drop not chosen ({} >= {})", drop_sample, type_drop_prob);
uint8_t type_drop_prob = 0;
try {
type_drop_prob = pt->enemy_type_drop_probs.at(enemy_type);
} catch (const std::out_of_range&) {
this->log.info_f("No drop probability is set for this enemy type");
return DropResult();
} else {
this->log.info_f("Drop chosen ({} < {})", drop_sample, type_drop_prob);
}
if (!force_rare) {
uint8_t drop_sample = this->rand_int(100);
if (drop_sample >= type_drop_prob) {
this->log.info_f("Drop not chosen ({} >= {})", drop_sample, type_drop_prob);
return DropResult();
} else {
this->log.info_f("Drop chosen ({} < {})", drop_sample, type_drop_prob);
}
}
DropResult res;
res.item = this->check_rare_spec_and_create_rare_enemy_item(enemy_type, area);
res.item = this->check_rare_spec_and_create_rare_enemy_item(enemy_type, area, force_rare);
if (!res.item.empty()) {
res.is_from_rare_table = true;
} else {
uint32_t item_class_determinant =
this->should_allow_meseta_drops() ? this->rand_int(3) : (this->rand_int(2) + 1);
uint32_t item_class;
uint8_t item_class_determinant = this->should_allow_meseta_drops() ? this->rand_int(3) : (this->rand_int(2) + 1);
uint8_t item_class;
switch (item_class_determinant) {
case 0:
item_class = 5;
@@ -224,7 +231,12 @@ ItemCreator::DropResult ItemCreator::on_monster_item_drop(uint32_t enemy_type, u
item_class = 4;
break;
case 2:
item_class = pt->enemy_item_classes.at(enemy_type);
try {
item_class = pt->enemy_type_item_classes.at(enemy_type);
} catch (const out_of_range&) {
this->log.info_f("Item class is not set for this enemy type");
item_class = 0xFF;
}
break;
default:
throw logic_error("invalid item class determinant");
@@ -251,7 +263,12 @@ ItemCreator::DropResult ItemCreator::on_monster_item_drop(uint32_t enemy_type, u
break;
case 5: // Meseta
res.item.data1[0] = 0x04;
res.item.data2d = this->choose_meseta_amount(pt->enemy_meseta_ranges, enemy_type) & 0xFFFF;
try {
res.item.data2d = this->choose_meseta_amount(pt->enemy_type_meseta_ranges.at(enemy_type)) & 0xFFFF;
} catch (const out_of_range&) {
this->log.info_f("Meseta range is not set for this enemy type");
return DropResult();
}
break;
default:
return res;
@@ -270,30 +287,56 @@ ItemCreator::DropResult ItemCreator::on_monster_item_drop(uint32_t enemy_type, u
}
}
ItemData ItemCreator::check_rare_specs_and_create_rare_box_item(uint8_t area) {
ItemData item;
ItemData ItemCreator::check_rare_specs_and_create_rare_item(
const std::vector<RareItemSet::ExpandedDrop>& specs, uint8_t area, bool force_rare) {
if (specs.empty()) {
return ItemData();
}
// This logic differs from the original client logic. This logic "stacks" all rare rates into a single probability
// space, whereas the original client logic chooses a new random number for each rare spec that it checks. The
// stacking logic makes the order of specs irrelevant, whereas the original client logic means that later specs are
// actually more rare than they should be. In the original client, this only matters for boxes, because enemies could
// not have multiple specs. Also, the original code uses 0xFFFFFFFF as the maximum here; we use 0x100000000 instead,
// which makes all rare items SLIGHTLY more rare.
int64_t det = force_rare ? 0 : this->rand_int(0x100000000);
if (this->is_legacy_replay) {
// For some old tests, we waste a few replay values because they used the old (non-stacked) logic. New tests should
// not use this codepath.
for (size_t z = 1; z < specs.size(); z++) {
this->rand_int(0x100000000);
}
}
this->log.info_f("{} specs to check with det={:08X} rare_mult={:g}", specs.size(), det, this->rare_drop_rate_multiplier);
for (const auto& spec : specs) {
uint64_t effective_probability = spec.probability;
if (this->rare_drop_rate_multiplier != 1.0) {
double multiplied_probability = static_cast<double>(spec.probability) * this->rare_drop_rate_multiplier;
effective_probability = (multiplied_probability >= 4294967296.0)
? 0x100000000ULL
: static_cast<uint64_t>(multiplied_probability);
}
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
this->log.info_f("Checking spec {:08X} => effective {:08X} => {} with det={:08X}",
spec.probability, effective_probability, spec.data.hex(), det);
}
det -= effective_probability;
if (det < 0) {
return this->create_rare_item(spec.data, area);
}
}
return ItemData();
}
ItemData ItemCreator::check_rare_specs_and_create_rare_box_item(uint8_t area, bool force_rare) {
if (!this->are_rare_drops_allowed()) {
return item;
return ItemData();
}
uint8_t table_index = this->table_index_for_area(area);
Episode episode = episode_for_area(area);
auto rare_specs = this->rare_item_set->get_box_specs(this->mode, episode, this->difficulty, this->section_id, table_index);
for (const auto& spec : rare_specs) {
item = this->check_rate_and_create_rare_item(spec, area);
if (!item.empty()) {
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
auto hex = spec.data.hex();
this->log.info_f("Box spec {:08X} produced item {}", spec.probability, hex);
}
break;
}
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
auto hex = spec.data.hex();
this->log.info_f("Box spec {:08X} did not produce item {}", spec.probability, hex);
}
}
return item;
auto specs = this->rare_item_set->get_box_specs(this->mode, episode, this->difficulty, this->section_id, table_index);
return this->check_rare_specs_and_create_rare_item(specs, area, force_rare);
}
uint32_t ItemCreator::rand_int(uint64_t max) {
@@ -305,24 +348,18 @@ float ItemCreator::rand_float_0_1_from_crypt() {
return (static_cast<double>(this->rand_crypt->next() >> 16) / 65536.0);
}
template <size_t NumRanges>
uint32_t ItemCreator::choose_meseta_amount(
const parray<CommonItemSet::Table::Range<uint16_t>, NumRanges> ranges,
size_t table_index) {
uint16_t min = ranges[table_index].min;
uint16_t max = ranges[table_index].max;
uint32_t ItemCreator::choose_meseta_amount(const CommonItemSet::Table::Range<uint16_t>& range) {
// Note: The original code returns 0xFF here if either limit is equal to 0xFF (despite them being 16-bit integers!)
uint16_t ret;
if (min == max) {
ret = min;
} else if (max < min) {
ret = this->rand_int((min - max) + 1) + max;
if (range.min == range.max) {
ret = range.min;
} else if (range.max < range.min) {
ret = this->rand_int((range.min - range.max) + 1) + range.max;
} else {
ret = this->rand_int((max - min) + 1) + min;
ret = this->rand_int((range.max - range.min) + 1) + range.min;
}
this->log.info_f("Chose {} Meseta from range [{}, {}]", ret, min, max);
this->log.info_f("Chose {} Meseta from range [{}, {}]", ret, range.min, range.max);
return ret;
}
@@ -330,45 +367,24 @@ bool ItemCreator::should_allow_meseta_drops() const {
return (this->mode != GameMode::CHALLENGE);
}
ItemData ItemCreator::check_rare_spec_and_create_rare_enemy_item(uint32_t enemy_type, uint8_t area) {
ItemData item;
if (this->are_rare_drops_allowed() && (enemy_type > 0) && (enemy_type < NUM_RT_INDEXES_V4)) {
// Note: In the original implementation, enemies can only have one possible rare drop. In our implementation, they
// can have multiple rare drops if JSONRareItemSet is used (the other RareItemSet implementations never return
// multiple drops for an enemy type).
Episode episode = episode_for_area(area);
auto rare_specs = this->rare_item_set->get_enemy_specs(
this->mode, episode, this->difficulty, this->section_id, enemy_type);
for (const auto& spec : rare_specs) {
item = this->check_rate_and_create_rare_item(spec, area);
if (!item.empty()) {
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
auto hex = spec.data.hex();
this->log.info_f("Enemy spec {:08X} produced item {}", spec.probability, hex);
}
break;
}
if (this->log.should_log(phosg::LogLevel::L_INFO)) {
auto hex = spec.data.hex();
this->log.info_f("Enemy spec {:08X} did not produce item {}", spec.probability, hex);
}
}
ItemData ItemCreator::check_rare_spec_and_create_rare_enemy_item(EnemyType enemy_type, uint8_t area, bool force_rare) {
// Note: The original implementation has a bounds check for enemy_type here, since it uses rt_index instead.
// if ((enemy_type <= 0) || (enemy_type >= NUM_RT_INDEXES_V4)) return ItemData{};
if (!this->are_rare_drops_allowed()) {
return ItemData{};
}
return item;
// Note: In the original implementation, enemies can only have one possible rare drop. In our implementation, they
// can have multiple rare drops if JSONRareItemSet is used (the other RareItemSet implementations never return
// multiple drops for an enemy type).
Episode episode = episode_for_area(area);
auto specs = this->rare_item_set->get_enemy_specs(
this->mode, episode, this->difficulty, this->section_id, enemy_type);
return this->check_rare_specs_and_create_rare_item(specs, area, force_rare);
}
ItemData ItemCreator::check_rate_and_create_rare_item(const RareItemSet::ExpandedDrop& drop, uint8_t area) {
if (drop.probability == 0) {
return ItemData();
}
// Note: The original code uses 0xFFFFFFFF as the maximum here. We use 0x100000000 instead, which makes all rare
// items SLIGHTLY more rare.
if (this->rand_int(0x100000000) >= drop.probability) {
return ItemData();
}
ItemData item = drop.data;
ItemData ItemCreator::create_rare_item(const ItemData& drop_item, uint8_t area) {
ItemData item = drop_item;
if (item.can_be_encoded_in_rel_rare_table()) {
switch (item.data1[0]) {
case 0:
@@ -617,9 +633,11 @@ void ItemCreator::generate_common_item_variances(ItemData& item, uint8_t area) {
case 3:
this->generate_common_tool_variances(item, area);
break;
case 4:
item.data2d = this->choose_meseta_amount(this->pt(area)->box_meseta_ranges, this->table_index_for_area(area)) & 0xFFFF;
case 4: {
const auto& range = this->pt(area)->box_meseta_ranges.at(this->table_index_for_area(area));
item.data2d = this->choose_meseta_amount(range) & 0xFFFF;
break;
}
default:
// Note: The original code does the following here:
// item.clear();
+19 -8
View File
@@ -37,10 +37,18 @@ public:
bool is_from_rare_table = false;
};
DropResult on_monster_item_drop(uint32_t enemy_type, uint8_t area);
DropResult on_box_item_drop(uint8_t area);
inline void set_legacy_replay() {
this->is_legacy_replay = true;
}
inline void set_rare_drop_rate_multiplier(double multiplier) {
this->rare_drop_rate_multiplier = multiplier;
}
DropResult on_monster_item_drop(EnemyType enemy_type, uint8_t area, bool force_rare);
DropResult on_box_item_drop(uint8_t area, bool force_rare);
// Note: param3-6 refer to the corresponding fields of the object definition
DropResult on_specialized_box_item_drop(uint8_t area, float param3, uint32_t param4, uint32_t param5, uint32_t param6);
DropResult on_specialized_box_item_drop(
uint8_t area, float param3, uint32_t param4, uint32_t param5, uint32_t param6);
ItemData base_item_for_specialized_box(uint32_t param4, uint32_t param5, uint32_t param6) const;
std::vector<ItemData> generate_armor_shop_contents(Episode episode, size_t player_level);
@@ -69,6 +77,8 @@ private:
phosg::PrefixedLogger log;
Version logic_version;
bool is_legacy_replay;
double rare_drop_rate_multiplier = 1.0;
std::shared_ptr<const ItemData::StackLimits> stack_limits;
GameMode mode;
Difficulty difficulty;
@@ -116,14 +126,15 @@ private:
uint32_t rand_int(uint64_t max);
float rand_float_0_1_from_crypt();
template <size_t NumRanges>
uint32_t choose_meseta_amount(const parray<CommonItemSet::Table::Range<uint16_t>, NumRanges> ranges, size_t table_index);
uint32_t choose_meseta_amount(const CommonItemSet::Table::Range<uint16_t>& range);
bool should_allow_meseta_drops() const;
ItemData check_rare_spec_and_create_rare_enemy_item(uint32_t enemy_type, uint8_t area);
ItemData check_rare_specs_and_create_rare_box_item(uint8_t area);
ItemData check_rate_and_create_rare_item(const RareItemSet::ExpandedDrop& drop, uint8_t area);
ItemData check_rare_spec_and_create_rare_enemy_item(EnemyType enemy_type, uint8_t area, bool force_rare);
ItemData check_rare_specs_and_create_rare_box_item(uint8_t area, bool force_rare);
ItemData check_rare_specs_and_create_rare_item(
const std::vector<RareItemSet::ExpandedDrop>& specs, uint8_t area, bool force_rare);
ItemData create_rare_item(const ItemData& drop_item, uint8_t area);
void generate_rare_weapon_bonuses(ItemData& item, Episode episode, uint32_t random_sample);
void deduplicate_weapon_bonuses(ItemData& item) const;
+9
View File
@@ -145,6 +145,7 @@ uint32_t Lobby::FloorItemManager::reassign_all_item_ids(uint32_t next_item_id) {
Lobby::Lobby(shared_ptr<ServerState> s, uint32_t id, bool is_game)
: server_state(s),
log(std::format("[{}:{:X}] ", is_game ? "Game" : "Lobby", id), lobby_log.min_level),
creation_time(phosg::now()),
lobby_id(id),
random_seed(phosg::random_object<uint32_t>()),
rand_crypt(make_shared<DisabledRandomGenerator>()),
@@ -229,6 +230,14 @@ void Lobby::create_item_creator(Version logic_version) {
effective_section_id,
rand_crypt,
this->quest ? this->quest->meta.battle_rules : nullptr);
if (this->blueballz_tier >= 0) {
double rare_mult = 1.25 + (static_cast<double>(this->blueballz_tier) * 0.25);
this->item_creator->set_rare_drop_rate_multiplier(rare_mult);
this->log.info_f("Blueballz +{} rare drop rate multiplier set to {:g}x", this->blueballz_tier, rare_mult);
}
if (s->use_legacy_item_random_behavior) {
this->item_creator->set_legacy_replay();
}
}
uint8_t Lobby::effective_section_id() const {
+4
View File
@@ -81,6 +81,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
START_BATTLE_PLAYER_IMMEDIATELY = 0x00010000,
CANNOT_CHANGE_CHEAT_MODE = 0x00020000,
USE_CREATOR_SECTION_ID = 0x00040000,
BLUEBALLZ_PLUS0 = 0x00080000,
// Flags used only for lobbies
PUBLIC = 0x01000000,
DEFAULT = 0x02000000,
@@ -91,6 +92,8 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
std::weak_ptr<ServerState> server_state;
phosg::PrefixedLogger log;
uint64_t creation_time;
uint32_t lobby_id;
uint32_t min_level = 0;
@@ -116,6 +119,7 @@ struct Lobby : public std::enable_shared_from_this<Lobby> {
Episode episode = Episode::NONE;
GameMode mode = GameMode::NORMAL;
Difficulty difficulty = Difficulty::NORMAL;
int8_t blueballz_tier = -1; // -1 = disabled; 0..10 = Blueballz +0..+10
float base_exp_multiplier = 1.0f;
float exp_share_multiplier = 0.5f;
float challenge_exp_multiplier = 1.0f;
+99 -69
View File
@@ -464,7 +464,7 @@ Action a_psov2_encrypt_single_test(
current_value, num_mismatches, phosg::format_duration(crypt_time), phosg::format_duration(single_time),
static_cast<float>(crypt_time) / single_time);
};
phosg::parallel_range_blocks<uint64_t>(thread_fn, 0, 0x100000000, 0x1000, num_threads, progress_fn);
phosg::parallel_blocks<uint64_t>(thread_fn, 0, 0x100000000, 0x1000, num_threads, progress_fn);
progress_fn(0, 0, 0xFFFFFFFF, 0);
});
@@ -813,7 +813,7 @@ static void a_encrypt_decrypt_vms_save_fn(phosg::Arguments& args) {
}
} else {
uint64_t seed = phosg::parallel_range_blocks<uint64_t>([&](uint64_t serial_number, size_t) -> bool {
uint64_t seed = phosg::parallel_blocks<uint64_t>([&](uint64_t serial_number, size_t) -> bool {
try {
auto decrypted = decrypt_fixed_size_data_section_t<StructT, false, ChecksumLength>(
data_section, sizeof(StructT), serial_number, skip_checksum, override_round2_seed);
@@ -1136,9 +1136,8 @@ Action a_decrypt_dcv2_executable(
executable (DP_ADDRESS.JPN), INDEXES should be the path to the index fixup\n\
table (KATSUO.SEA), and VALUES should be the path to the value fixup table\n\
(IWASHI.SEA). The output is written to EXEC.dec.\n\
If --simple is given, uses the simpler encryption method used in some\n\
community modifications of the game (Enhancement Pack, for example). In\n\
this case, --seed is not required; if not given, finds the seed\n\
If --simple is given, uses the encryption method used in Ives\' Enhancement\n\
Pack. In this case, --seed is not required; if not given, finds the seed\n\
automatically, and prints it to stderr so you will be able to use it when\n\
re-encrypting.\n",
+[](phosg::Arguments& args) {
@@ -1166,8 +1165,8 @@ Action a_encrypt_dcv2_executable(
executable (DP_ADDRESS.JPN) and INDEXES should be the path to the index\n\
fixup table (KATSUO.SEA). The output is written to EXEC.enc and\n\
INDEXES.enc.\n\
If --simple is given, uses the simpler encryption method used in some\n\
community modifications of the game. In this case, --seed is required.\n",
If --simple is given, uses the simpler encryption method used in Ives\'\n\
Enhancement Pack. In this case, --seed is required.\n",
+[](phosg::Arguments& args) {
string executable_filename = args.get<string>("executable", true);
string executable_data = phosg::load_file(executable_filename);
@@ -1188,7 +1187,7 @@ Action a_encrypt_dcv2_executable(
Action a_decode_gci_snapshot(
"decode-gci-snapshot", "\
decode-gci-snapshot [INPUT-FILENAME [OUTPUT-FILENAME]]\n\
Decode a PSO GC snapshot file into a Windows BMP image.\n",
Decode a PSO GC snapshot file (in GCI format) into a Windows BMP image.\n",
+[](phosg::Arguments& args) {
auto data = read_input_data(args);
phosg::StringReader r(data);
@@ -1366,7 +1365,7 @@ Action a_salvage_gci(
};
if (!round2) {
phosg::parallel_range_blocks<uint64_t>(
phosg::parallel_blocks<uint64_t>(
[&](uint64_t seed, size_t thread_num) -> bool {
auto decrypted = decrypt_fixed_size_data_section_t<StructT, true>(
data_section, header.data_size, seed, true);
@@ -1386,7 +1385,7 @@ Action a_salvage_gci(
// high half, which is much faster than trying all possible seeds. Unfortunately, this relies on the
// distribution of values in the plaintext, so it only works for the round-2 seed - the decrypted data after
// round 1 is still essentially random.
phosg::parallel_range_blocks<uint64_t>(try_round2_seed, 0, 0x100000, 0x1000, num_threads);
phosg::parallel_blocks<uint64_t>(try_round2_seed, 0, 0x100000, 0x1000, num_threads);
auto intermediate_top_seeds = merge_top_seeds(top_seeds_by_thread);
if (intermediate_top_seeds.empty()) {
throw logic_error("no intermediate seeds were found");
@@ -1397,7 +1396,7 @@ Action a_salvage_gci(
for (auto& top_seeds : top_seeds_by_thread) {
top_seeds.clear();
}
phosg::parallel_range_blocks<uint64_t>(
phosg::parallel_blocks<uint64_t>(
[&](uint64_t seed, size_t thread_num) -> bool {
return try_round2_seed((seed << 16) | round2_lower_half, thread_num);
},
@@ -1405,7 +1404,7 @@ Action a_salvage_gci(
} else {
// The user requested not to take any shortcuts, so burn a lot of CPU power
phosg::parallel_range_blocks<uint64_t>(try_round2_seed, 0, 0x100000000, 0x1000, num_threads);
phosg::parallel_blocks<uint64_t>(try_round2_seed, 0, 0x100000000, 0x1000, num_threads);
}
print_top_seeds(merge_top_seeds(top_seeds_by_thread));
@@ -1474,7 +1473,7 @@ Action a_find_decryption_seed(
return true;
};
uint64_t seed = phosg::parallel_range_blocks<uint64_t>([&](uint64_t seed, size_t) -> bool {
uint64_t seed = phosg::parallel_blocks<uint64_t>([&](uint64_t seed, size_t) -> bool {
string be_decrypt_buf = ciphertext.substr(0, max_plaintext_size);
string le_decrypt_buf = ciphertext.substr(0, max_plaintext_size);
if (uses_v3_encryption(version)) {
@@ -1622,13 +1621,17 @@ Action a_disassemble_quest_script(
disassemble-quest-script [OPTIONS] [INPUT-FILENAME [OUTPUT-FILENAME]]\n\
Disassemble the input quest script (.bin file) into a text representation\n\
of the commands and metadata it contains. Specify the quest\'s game version\n\
with one of the --dc-nte, --dc-v1, --dc-v2, --pc, --gc-nte, --gc, --gc-ep3,\n\
--xb, or --bb options. newserv uses more descriptive opcode mnemonics by\n\
default; the --qedit option will result in names matching those used by\n\
QEdit. If you intend to reassemble the script, after editing it, use the\n\
--reassembly option to add explicit label numbers and remove offsets and\n\
data in code sections. To include script references from the map, use the\n\
--map-file=FILENAME option.\n",
with one of the --dc-nte, --dc-v1, --dc-v2, --pc, --gc-nte, --gc,\n\
--gc-ep3, --xb, or --bb options. Other options:\n\
--qedit: newserv uses more descriptive opcode mnemonics by default; this\n\
option will result in names matching those used by QEdit.\n\
--reassembly: If you intend to reassemble the script after editing it,\n\
use this option to add explicit label numbers and remove offsets and\n\
data in code sections.\n\
--map-file=FILENAME: Include references to script labels from this map.\n\
--language=L: Decode strings using this language. L may be J, E, G, F,\n\
S, B, T, or K, for Japanese, English, German, French, Spanish,\n\
Simplified Chinese, Traditional Chinese, or Korean respectively.\n",
+[](phosg::Arguments& args) {
string data = read_input_data(args);
auto version = get_cli_version(args);
@@ -1641,7 +1644,8 @@ Action a_disassemble_quest_script(
auto map_data = make_shared<string>(prs_decompress(phosg::load_file(map_filename)));
map_file = make_shared<MapFile>(map_data);
}
Language language = static_cast<Language>(args.get<uint8_t>("language", 0xFF));
const auto& language_str = args.get<string>("language");
Language language = language_str.empty() ? Language::ENGLISH : language_for_name(language_str);
bool reassembly_mode = args.get<bool>("reassembly");
bool use_qedit_names = args.get<bool>("qedit");
string result = disassemble_quest_script(
@@ -1917,8 +1921,7 @@ Action a_extract_ppk("extract-ppk", "\
PC/BB format. For PPK archives, the --password= option is required.\n",
a_extract_archive_fn);
Action a_encode_sjis(
"transcode-text", nullptr, +[](phosg::Arguments& args) {
Action a_transcode_text("transcode-text", nullptr, +[](phosg::Arguments& args) {
TextTranscoder* tt_from = nullptr;
{
std::string from_name = args.get<std::string>("from");
@@ -1958,8 +1961,7 @@ Action a_encode_sjis(
if (tt_to) {
data = (*tt_to)(data);
}
write_output_data(args, data.data(), data.size(), "txt");
});
write_output_data(args, data.data(), data.size(), "txt"); });
Action a_decode_text_archive(
"decode-text-archive", "\
@@ -2199,6 +2201,25 @@ Action a_download_files(
io_context->run();
});
std::shared_ptr<RareItemSet> load_rare_item_set(
const std::string& filename, bool is_v1, std::shared_ptr<const ItemNameIndex> v4_item_name_index) {
string filename_lower = phosg::tolower(filename);
auto data = make_shared<string>(phosg::load_file(filename));
if (filename_lower.ends_with(".json")) {
return make_shared<RareItemSet>(phosg::JSON::parse(*data), v4_item_name_index);
} else if (filename_lower.ends_with(".gsl")) {
return make_shared<RareItemSet>(GSLArchive(data, false), false);
} else if (filename_lower.ends_with(".gslb")) {
return make_shared<RareItemSet>(GSLArchive(data, true), true);
} else if (filename_lower.ends_with(".afs")) {
return make_shared<RareItemSet>(AFSArchive(data), is_v1);
} else if (filename_lower.ends_with(".rel")) {
return make_shared<RareItemSet>(*data, true);
} else {
throw runtime_error("cannot determine input format; use a filename ending with .json, .gsl, .gslb, .afs, or .rel");
}
}
Action a_convert_rare_item_set(
"convert-rare-item-set", "\
convert-rare-item-set INPUT-FILENAME [OUTPUT-FILENAME] [OPTIONS]\n\
@@ -2228,24 +2249,8 @@ Action a_convert_rare_item_set(
if (input_filename.empty() || (input_filename == "-")) {
throw runtime_error("input filename must be given");
}
string input_filename_lower = phosg::tolower(input_filename);
auto data = make_shared<string>(read_input_data(args));
shared_ptr<RareItemSet> rs;
if (input_filename_lower.ends_with(".json")) {
rs = make_shared<RareItemSet>(phosg::JSON::parse(*data), s->item_name_index_opt(get_cli_version(args, Version::BB_V4)));
} else if (input_filename_lower.ends_with(".gsl")) {
rs = make_shared<RareItemSet>(GSLArchive(data, false), false);
} else if (input_filename_lower.ends_with(".gslb")) {
rs = make_shared<RareItemSet>(GSLArchive(data, true), true);
} else if (input_filename_lower.ends_with(".afs")) {
rs = make_shared<RareItemSet>(AFSArchive(data), is_v1(get_cli_version(args, Version::DC_V2)));
} else if (input_filename_lower.ends_with(".rel")) {
rs = make_shared<RareItemSet>(*data, true);
} else {
throw runtime_error("cannot determine input format; use a filename ending with .json, .gsl, .gslb, .afs, or .rel");
}
auto rs = load_rare_item_set(
input_filename, is_v1(get_cli_version(args, Version::BB_V4)), s->item_name_index(Version::BB_V4));
if (rate_factor != 1.0) {
rs->multiply_all_rates(rate_factor);
}
@@ -2289,6 +2294,32 @@ Action a_convert_rare_item_set(
throw runtime_error("cannot determine output format; use a filename ending with .json, .gsl, .gslb, or .afs");
}
});
Action a_compare_rare_item_set(
"compare-rare-item-set", nullptr,
+[](phosg::Arguments& args) {
string input_filename1 = args.get<string>(1, false);
if (input_filename1.empty() || (input_filename1 == "-")) {
throw runtime_error("two input filenames must be given");
}
string input_filename2 = args.get<string>(2, false);
if (input_filename2.empty() || (input_filename2 == "-")) {
throw runtime_error("two input filenames must be given");
}
auto s = make_shared<ServerState>(get_config_filename(args));
s->load_config_early();
s->load_patch_indexes();
s->load_text_index();
s->load_item_definitions();
s->load_item_name_indexes();
s->load_drop_tables();
bool is_v1 = ::is_v1(get_cli_version(args, Version::BB_V4));
auto rs1 = load_rare_item_set(input_filename1, is_v1, s->item_name_index(Version::BB_V4));
auto rs2 = load_rare_item_set(input_filename2, is_v1, s->item_name_index(Version::BB_V4));
rs1->print_diff(stdout, *rs2);
});
static shared_ptr<CommonItemSet> load_common_item_set(
const std::string& filename, const std::string& ct_filename, bool big_endian) {
@@ -2759,26 +2790,26 @@ Action a_generate_ep3_cards_html(
}
}
phosg::parallel_range<uint32_t>([&](uint32_t index, size_t) -> bool {
auto& info = this->card_infos[index];
if (!info.large_filename.empty()) {
auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(info.large_filename));
img.resize(512, 399);
info.large_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL);
}
if (!info.medium_filename.empty()) {
auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(info.medium_filename));
img.resize(184, 144);
info.medium_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL);
}
if (!info.small_filename.empty()) {
auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(info.small_filename));
img.resize(58, 43);
info.small_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL);
}
return false;
},
0, this->card_infos.size(), num_threads);
phosg::parallel_range(
this->card_infos, [&](CardInfo& info, size_t) -> bool {
if (!info.large_filename.empty()) {
auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(info.large_filename));
img.resize(512, 399);
info.large_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL);
}
if (!info.medium_filename.empty()) {
auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(info.medium_filename));
img.resize(184, 144);
info.medium_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL);
}
if (!info.small_filename.empty()) {
auto img = phosg::ImageRGBA8888N::from_file_data(phosg::load_file(info.small_filename));
img.resize(58, 43);
info.small_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL);
}
return false;
},
num_threads);
}
this->num_output_columns = 1 + (!no_disassembly) + this->show_small_column + this->show_medium_column + this->show_large_column;
@@ -3093,8 +3124,7 @@ Action a_check_supermaps(
auto f = phosg::fopen_unique(filename, "wt");
phosg::fwrite_fmt(f.get(), "QUEST {} ({})\n", it.first, it.second->meta.name);
phosg::fwrite_fmt(f.get(), "ENEMY--------------- DCNTE 11/2K DC-V1 DC-V2 PCNTE PC-V2 GCNTE GC-V3 XB-V3 BB-V4\n");
for (size_t type_ss = 0; type_ss < static_cast<size_t>(EnemyType::MAX_ENEMY_TYPE); type_ss++) {
EnemyType type = static_cast<EnemyType>(type_ss);
for (auto type : phosg::EnumRange<EnemyType>()) {
bool any_count_nonzero = false;
array<size_t, NUM_VERSIONS> counts;
for (Version v : ALL_NON_PATCH_VERSIONS) {
@@ -3319,7 +3349,7 @@ Action a_optimize_materialized_map(
return false;
};
phosg::parallel_range_blocks<uint64_t>(thread_fn, 0, 0x100000000, 0x100, num_threads);
phosg::parallel_blocks<uint64_t>(thread_fn, 0, 0x100000000, 0x100, num_threads);
});
Action a_print_free_supermap(
@@ -3605,7 +3635,7 @@ Action a_generate_all_dc_serial_numbers(
found_counts[7].load(), serial_numbers[7].size(),
found_counts[8].load(), serial_numbers[8].size());
};
phosg::parallel_range_blocks<uint64_t>(thread_fn, 0, 0x100000000, 0x1000, num_threads, progress_fn);
phosg::parallel_blocks<uint64_t>(thread_fn, 0, 0x100000000, 0x1000, num_threads, progress_fn);
if (num_mismatches > 0) {
throw logic_error("mismatches occurred during test");
@@ -3749,7 +3779,7 @@ Action a_replay_ep3_battle_commands(
run_replay(base_seed, 0);
} else {
size_t num_threads = args.get<size_t>("threads", 0);
phosg::parallel_range_blocks<int64_t>(run_replay, 0, 0x100000000, 0x1000, num_threads);
phosg::parallel_blocks<int64_t>(run_replay, 0, 0x100000000, 0x1000, num_threads);
}
});
@@ -3905,7 +3935,7 @@ Action a_run_server_replay_log(
use_terminal_colors = true;
}
auto state = make_shared<ServerState>(get_config_filename(args));
auto state = make_shared<ServerState>(get_config_filename(args), !replay_log_filename.empty());
if (args.get<bool>("debug")) {
state->is_debug = true;
}
+54 -12
View File
@@ -866,9 +866,10 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// param5; if the dot product is positive, param2 is used)
// param3 = room ID to use if the dot product described above is zero or negative
// param5 = angle (see param2)
// param6 = if equal to 0x00010000, only the player's room_id field is set, and game flag 0x02000000 is set on
// the player; if not equal to 0x00010000, then both room_id and room_id2 are set and no game flag is set
// (TODO: What are the visible behavior differences due to this parameter?)
// param6 = on v2 and before, this is ignored; on v3 and later, if param6 is equal to 0x00010000, only the
// player's room_id field is set, and player flag 0x02000000 is set on the player; if not equal to 0x00010000,
// then both room_id and room_id_in_custom_area (if in a non-rearrangeable area, like Pioneer 2 or Forest) are
// set and no game flag is set (TODO: What are the visible behavior differences due to this parameter?)
{0x000E, F_V0_V4, 0x00005FFFFFF83FFE, "TObjRoomId"},
// Sensor of some kind (TODO). Params:
@@ -1372,10 +1373,10 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// Item box. Params:
// param1 = if positive, box is specialized to drop a specific item or type of item; if zero or negative, box
// drops any common item or none at all (and param3-6 are all ignored)
// param3 = if zero, then only data1[0-1] are used and the rest of the ItemData is cleared, then bonuses, grinds,
// etc. are applied to the item; if nonzero, the item is not randomized at all and drops exactly as specified
// in param4-6
// drops any item (including box rares for the current floor), or nothing at all, and param3-6 are all ignored
// param3 = if zero, then only data1[0-1] (the high 2 bytes of param4) are used and the rest of the ItemData is
// cleared, then bonuses, grinds, etc. are applied to the item; if nonzero, the item is not randomized at all
// and drops exactly as specified in param4-6
// param4-6 = item definition (see below)
// Not all fields in ItemData can be specified in the item definition here. The field order here does not match the
// field order in ItemData! The item definition is encoded here as follows:
@@ -2822,7 +2823,8 @@ static const vector<DATEntityDefinition> dat_enemy_definitions({
// param2 = if less than 1, this is a Savage Wolf; otherwise it's a Barbarous Wolf
{0x0043, F_V0_V4, 0x0000000000600006, "TObjEneBm5Wolf"},
// Booma, Gobooma, or Gigobooma. Params:
// Booma, Gobooma, or Gigobooma. The activation radius is fixed and cannot be changed: 50 for Hunters, 100 for
// Rangers and Forces; the deactivation radius is 100 for Hunters and 150 for Rangers and Forces. Params:
// param1 = TODO (fraction of max HP; see TObjEnemyV8048ee80_v5A)
// param2 = idle walk radius (when there's no target, it will walk around its spawn location within this radius;
// if this is zero, it stands still instead)
@@ -3445,9 +3447,49 @@ void MapFile::RandomState::generate_shuffled_location_table(
}
const array<uint32_t, 41> MapFile::RAND_ENEMY_BASE_TYPES = {
0x44, 0x43, 0x41, 0x42, 0x40, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0xA0, 0xA1,
0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE0, 0xE1};
0x0044, /* TObjEneBeast */
0x0043, /* TObjEneBm5Wolf */
0x0041, /* TObjEneLappy */
0x0042, /* TObjEneBm3FlyNest */
0x0040, /* TObjEneMoja */
0x0060, /* TObjGrass */
0x0061, /* TObjEneRe2Flower */
0x0062, /* TObjEneNanoDrago */
0x0063, /* TObjEneShark */
0x0064, /* TObjEneSlime */
0x0065, /* TObjEnePanarms */
0x0080, /* TObjEneDubchik */
0x0081, /* TObjEneGyaranzo */
0x0082, /* TObjEneMe3ShinowaReal */
0x0083, /* TObjEneMe1Canadin */
0x0084, /* TObjEneMe1CanadinLeader */
0x0085, /* TOCtrlDubchik */
0x00A0, /* TObjEneSaver */
0x00A1, /* TObjEneRe4Sorcerer */
0x00A2, /* TObjEneDarkGunner */
0x00A3, /* TObjEneDarkGunCenter */
0x00A4, /* TObjEneDf2Bringer */
0x00A5, /* TObjEneRe7Berura */
0x00A6, /* TObjEneDimedian */
0x00A7, /* TObjEneBalClawBody */
0x00A8, /* TObjEneBalClawClaw */
0x00D4, /* TObjEneMe3StelthReal */
0x00D5, /* TObjEneMerillLia */
0x00D6, /* TObjEneBm9Mericarol */
0x00D7, /* TObjEneBm5GibonU */
0x00D8, /* TObjEneGibbles */
0x00D9, /* TObjEneMe1Gee */
0x00DA, /* TObjEneMe1GiGue */
0x00DB, /* TObjEneDelDepth */
0x00DC, /* TObjEneDellBiter */
0x00DD, /* TObjEneDolmOlm */
0x00DE, /* TObjEneMorfos */
0x00DF, /* TObjEneRecobox */
// This is not a bug; 0x00E0 really does appear twice in this list on the client.
0x00E0, /* TObjEneMe3SinowZoaReal/TObjEneEpsilonBody (depending on area) */
0x00E0, /* TObjEneMe3SinowZoaReal/TObjEneEpsilonBody (depending on area) */
0x00E1, /* TObjEneIllGill */
};
MapFile::MapFile(std::shared_ptr<const std::string> data) {
for (uint8_t z = 0; z < this->sections_for_floor.size(); z++) {
@@ -6566,7 +6608,7 @@ void MapState::import_enemy_states_from_sync(Version from_version, const SyncEne
// Only set the state if it's not an alias
if (ene_st->super_ene == ene) {
if (ene_st->game_flags != entry.flags) {
this->log.warning_f("({:04X} => E-{:03X}) Flags from client ({:08X}) do not match game flags from map ({:08X})",
this->log.warning_f("({:04X} => E-{:03X}) Game flags from client ({:08X}) do not match game flags from map ({:08X})",
enemy_index, ene_st->e_id, entry.flags, ene_st->game_flags);
ene_st->game_flags = entry.flags;
}
+6
View File
@@ -42,6 +42,12 @@ constexpr uint32_t PATCH_SWITCHES = 0x11666611;
constexpr uint32_t PROGRAMS = 0x11777711;
constexpr uint32_t DISCONNECT = 0x11888811;
constexpr uint32_t CLEAR_LICENSE = 0x11999911;
constexpr uint32_t BB_LIVE_SHIP = 0x11AAAA11;
constexpr uint32_t BB_TEST_SHIP = 0x11BBBB11;
constexpr uint32_t BB_VANILLA_SHIP = 0x11CCCC11;
constexpr uint32_t BB_HARDCORE_SHIP = 0x11F00D11;
constexpr uint32_t BB_DEV_SHIP = 0x11EEEE11;
constexpr uint32_t BLUEBALLZ_PLUS0 = 0x11DDDD11;
} // namespace MainMenuItemID
namespace ClearLicenseConfirmationMenuItemID {
+2 -2
View File
@@ -721,7 +721,7 @@ const ChallengeTemplateDefinition& get_challenge_template_definition(Version ver
}
PlayerHoldState::PlayerHoldState(const PlayerHoldState_DCProtos& proto)
: unknown_a1(proto.unknown_a1),
: expiration_frames(proto.expiration_frames),
unknown_a2(proto.unknown_a2),
unknown_a3(0),
trigger_radius2(proto.trigger_radius2),
@@ -730,7 +730,7 @@ PlayerHoldState::PlayerHoldState(const PlayerHoldState_DCProtos& proto)
PlayerHoldState::operator PlayerHoldState_DCProtos() const {
PlayerHoldState_DCProtos ret;
ret.unknown_a1 = this->unknown_a1;
ret.expiration_frames = this->expiration_frames;
ret.unknown_a2 = this->unknown_a2;
ret.trigger_radius2 = this->trigger_radius2;
ret.x = this->x;
+8 -2
View File
@@ -64,6 +64,12 @@ struct PlayerVisualConfigT {
// F = force, R = ranger, H = hunter
// A = android, N = newman, M = human
// f = female, m = male
// Enemies also have a class_flags field, though it isn't part of PlayerVisualConfig. The bits for enemies are:
// -------- -------- -------- ----DMAN
// D = Dark attribute
// M = Machine attribute
// A = Altered Beast attribute
// N = Native attribute
/* 34 */ U32T<BE> class_flags = 0;
/* 38 */ U16T<BE> costume = 0;
/* 3A */ U16T<BE> skin = 0;
@@ -1123,7 +1129,7 @@ struct TelepipeState {
struct PlayerHoldState_DCProtos {
// This is used in all versions of this command except DCNTE and 11/2000.
/* 00 */ le_uint16_t unknown_a1 = 0;
/* 00 */ le_uint16_t expiration_frames = 0;
/* 02 */ le_uint16_t unknown_a2 = 0;
// unknown_a3 is missing in this format, unlike the v1+ format below
/* 04 */ le_float trigger_radius2 = 0.0f;
@@ -1134,7 +1140,7 @@ struct PlayerHoldState_DCProtos {
struct PlayerHoldState {
// This is used in all versions of this command except DCNTE and 11/2000.
/* 00 */ le_uint16_t unknown_a1 = 0;
/* 00 */ le_uint16_t expiration_frames = 0;
/* 02 */ le_uint16_t unknown_a2 = 0;
/* 04 */ le_uint32_t unknown_a3 = 0;
/* 08 */ le_float trigger_radius2 = 0.0f;
+20 -6
View File
@@ -960,7 +960,8 @@ static asio::awaitable<HandlerResult> SC_6x60_6xA2(shared_ptr<Client> c, Channel
if (rec.obj_st) {
if (rec.ignore_def) {
c->log.info_f("Creating item from box {:04X} (area {:02X})", cmd.entity_index, cmd.effective_area);
res = c->proxy_session->item_creator->on_box_item_drop(cmd.effective_area);
res = c->proxy_session->item_creator->on_box_item_drop(
cmd.effective_area, c->check_flag(Client::Flag::ALL_RARES_ENABLED));
} else {
c->log.info_f("Creating item from box {:04X} (area {:02X}; specialized with {:g} {:08X} {:08X} {:08X})",
cmd.entity_index, cmd.effective_area,
@@ -970,7 +971,8 @@ static asio::awaitable<HandlerResult> SC_6x60_6xA2(shared_ptr<Client> c, Channel
}
} else {
c->log.info_f("Creating item from enemy {:04X} (area {:02X})", cmd.entity_index, cmd.effective_area);
res = c->proxy_session->item_creator->on_monster_item_drop(rec.effective_rt_index, cmd.effective_area);
res = c->proxy_session->item_creator->on_monster_item_drop(
rec.effective_enemy_type, cmd.effective_area, c->check_flag(Client::Flag::ALL_RARES_ENABLED));
}
if (res.item.empty()) {
@@ -1022,8 +1024,15 @@ static asio::awaitable<HandlerResult> S_6x(shared_ptr<Client> c, Channel::Messag
case 0x17: {
const auto& cmd = msg.check_size_t<G_SetEntityPositionAndAngle_6x17>();
if (cmd.header.entity_id == c->lobby_client_id) {
c->log.warning_f("Blocking subcommand 6x17 targeting local client");
co_return HandlerResult::SUPPRESS;
// Vol Opt phase 1 -> phase 2 uses 6x17 targeting the local client to move
// players into the second arena phase. Allow this only while the proxy-side
// client is already on the Vol Opt floor.
if (c->floor == 0x0D) {
c->log.info_f("Allowing subcommand 6x17 targeting local client on Vol Opt floor");
} else {
c->log.warning_f("Blocking subcommand 6x17 targeting local client outside Vol Opt floor");
co_return HandlerResult::SUPPRESS;
}
}
break;
}
@@ -1643,7 +1652,7 @@ static asio::awaitable<HandlerResult> S_65_67_68_EB(shared_ptr<Client> c, Channe
c->log.warning_f("Proxied player appears multiple times in lobby");
}
if constexpr (sizeof(cmd.lobby_flags) > sizeof(LobbyFlags_DCNTE)) {
if constexpr (sizeof(cmd.lobby_flags) > sizeof(LobbyFlagsDCNTE)) {
c->proxy_session->lobby_event = cmd.lobby_flags.event;
if (c->override_lobby_event != 0xFF) {
cmd.lobby_flags.event = c->override_lobby_event;
@@ -2828,7 +2837,12 @@ asio::awaitable<void> handle_proxy_server_commands(
if (ec == asio::error::eof || ec == asio::error::connection_reset) {
error_str = "Server channel\ndisconnected";
} else if (ec == asio::error::operation_aborted) {
// This happens when the player chooses Change Ship/Change Block, so we don't show an error message
// If this is the currently-active backend channel, treat the abort as a backend disconnect.
// Normal Change Ship/Change Block reconnects replace ses->server_channel first; the old
// aborted channel will not match here, so those expected aborts stay silent.
if ((c->proxy_session == ses) && (ses->server_channel == channel)) {
error_str = "Server channel\ndisconnected";
}
} else {
error_str = e.what();
}
+1 -1
View File
@@ -154,7 +154,7 @@ string find_seed_and_decrypt_download_quest_data_section(
const void* data_section, size_t size, bool skip_checksum, bool is_ep3_trial, size_t num_threads) {
mutex result_lock;
string result;
uint64_t result_seed = phosg::parallel_range_blocks<uint64_t>([&](uint64_t seed, size_t) {
uint64_t result_seed = phosg::parallel_blocks<uint64_t>([&](uint64_t seed, size_t) {
try {
string ret = decrypt_download_quest_data_section<BE>(data_section, size, seed, skip_checksum, is_ep3_trial);
lock_guard<mutex> g(result_lock);
+8 -6
View File
@@ -676,7 +676,7 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
// Sets a player's starting position.
// valueA = client ID
// regsB[0-2] = position (x, y, z as integers)
// r egsB[3] = angle
// regsB[3] = angle
{0x76, "set_player_start_position", "p_setpos", {CLIENT_ID, {R_REG_SET_FIXED, 4}}, F_V0_V4 | F_ARGS},
// Returns players to the Hunter's Guild counter.
@@ -780,6 +780,8 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
// regsA[0-2] = location (x, y, z as integers)
// regsA[3] = radius
// regsA[4] = label index where thread should start
// After executing this opcode, regsA[0] is replaced with the object's token, which can be used in del_obj_param,
// move_coords_object, etc.
{0x8C, "at_coords_call", nullptr, {{R_REG_SET_FIXED, 5}}, F_V0_V4},
// Like at_coords_call, but the thread is not started automatically. Instead, the player's primary action button
@@ -890,7 +892,7 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
{0xB1, "thread_stg", nullptr, {SCRIPT16}, F_V0_V4},
// Deletes an interactable object previously created by set_obj_param. valueA is the object's token, as returned by
// regB from set_obj_param.
// regB from set_obj_param, or regsA[0] from e.g. at_coords_call.
{0xB2, "del_obj_param", nullptr, {R_REG}, F_V0_V4},
// Creates an item in the player's inventory. If the item is successfully created, this opcode sends 6x2B on all
@@ -2004,7 +2006,7 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
{0xF8CB, "clear_slot_invincible", "set_slot_targetable?", {R_REG}, F_V3_V4},
// These opcodes inflict various status conditions on a player. In the case of Shifta/Deband/Jellen/Zalure, the
// effective technicuqe level is 21.
// effective technique level is 21.
// regA = client ID
{0xF8CC, "set_slot_poison", nullptr, {R_REG}, F_V3_V4},
{0xF8CD, "set_slot_paralyze", nullptr, {R_REG}, F_V3_V4},
@@ -2089,9 +2091,9 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
// regsB[0-2] = location (x, y, z as integers)
{0xF8E6, "move_coords_object", nullptr, {R_REG, {R_REG_SET_FIXED, 3}}, F_V3_V4},
// These are the same as their counterparts without _ex, but these return an object token in regB which can be used
// with del_obj_param, move_coords_object, etc. set_obj_param_ex is the same as set_obj_param, since set_obj_param
// already returns an object token.
// These are the same as their counterparts without _ex, but these also return the object ID (which is distinct
// from the object token) in regB. There is a bug in set_obj_param_ex - the object token is not returned anywhere,
// and only the object ID is returned in regB.
{0xF8E7, "at_coords_call_ex", nullptr, {{R_REG_SET_FIXED, 5}, W_REG}, F_V3_V4},
{0xF8E8, "at_coords_talk_ex", nullptr, {{R_REG_SET_FIXED, 5}, W_REG}, F_V3_V4},
{0xF8E9, "npc_coords_call_ex", "walk_to_coord_call_ex", {{R_REG_SET_FIXED, 5}, W_REG}, F_V3_V4},
+172 -89
View File
@@ -177,23 +177,31 @@ RareItemSet::ParsedRELData::ParsedRELData(phosg::StringReader r, bool big_endian
}
RareItemSet::ParsedRELData::ParsedRELData(const SpecCollection& collection) {
for (const auto& specs : collection.rt_index_to_specs) {
ExpandedDrop effective_spec;
this->monster_rares.resize(NUM_RT_INDEXES_V4);
for (const auto& [enemy_type, specs] : collection.enemy_specs) {
const auto& def = type_definition_for_enemy(enemy_type);
if (def.rt_index == 0xFF) {
throw runtime_error(std::format(
"monster spec for {} has no rt_index and cannot be converted to ItemRT format", def.enum_name));
}
auto& dest_spec = this->monster_rares.at(def.rt_index);
for (const auto& spec : specs) {
if (effective_spec.data.empty()) {
effective_spec = spec;
} else if ((effective_spec.probability != spec.probability) || (effective_spec.data != spec.data)) {
throw runtime_error("monster spec cannot be converted to ItemRT format");
if (dest_spec.data.empty()) {
dest_spec = spec;
} else if ((dest_spec.probability != spec.probability) || (dest_spec.data != spec.data)) {
throw runtime_error(std::format(
"monster spec for {} contains multiple drops and cannot be converted to ItemRT format", def.enum_name));
}
}
this->monster_rares.emplace_back(specs.empty() ? ExpandedDrop() : specs[0]);
}
if (collection.box_area_norm_to_specs.size() > 0xFF) {
if (collection.box_specs.size() > 0xFF) {
throw runtime_error("area_norm value too high");
}
for (uint8_t area_norm = 0; area_norm < collection.box_area_norm_to_specs.size(); area_norm++) {
for (const auto& spec : collection.box_area_norm_to_specs[area_norm]) {
for (uint8_t area_norm = 0; area_norm < collection.box_specs.size(); area_norm++) {
for (const auto& spec : collection.box_specs[area_norm]) {
uint8_t area_norm_plus_1 = area_norm + 1;
this->box_rares.emplace_back(BoxRare{.area_norm_plus_1 = area_norm_plus_1, .drop = spec});
}
@@ -208,27 +216,26 @@ std::string RareItemSet::ParsedRELData::serialize(bool big_endian, bool is_v1) c
}
}
RareItemSet::SpecCollection RareItemSet::ParsedRELData::as_collection() const {
RareItemSet::SpecCollection RareItemSet::ParsedRELData::as_collection(Episode episode) const {
SpecCollection ret;
for (size_t z = 0; z < this->monster_rares.size(); z++) {
const auto& drop = this->monster_rares[z];
for (size_t rt_index = 0; rt_index < this->monster_rares.size(); rt_index++) {
const auto& drop = this->monster_rares[rt_index];
if (drop.data.empty()) {
continue;
}
if (z >= ret.rt_index_to_specs.size()) {
ret.rt_index_to_specs.resize(z + 1);
for (auto enemy_type : enemy_types_for_rare_table_index(episode, rt_index)) {
ret.enemy_specs[enemy_type].emplace_back(drop);
}
ret.rt_index_to_specs[z].emplace_back(drop);
}
for (const auto& drop : this->box_rares) {
if ((drop.area_norm_plus_1 == 0) || drop.drop.data.empty()) {
continue;
}
uint8_t area_norm = drop.area_norm_plus_1 - 1;
if (area_norm >= ret.box_area_norm_to_specs.size()) {
ret.box_area_norm_to_specs.resize(area_norm + 1);
if (area_norm >= ret.box_specs.size()) {
ret.box_specs.resize(area_norm + 1);
}
ret.box_area_norm_to_specs[area_norm].emplace_back(drop.drop);
ret.box_specs[area_norm].emplace_back(drop.drop);
}
return ret;
}
@@ -241,8 +248,7 @@ RareItemSet::RareItemSet(const AFSArchive& afs, bool is_v1) {
size_t index = static_cast<size_t>(difficulty) * 10 + section_id;
ParsedRELData rel(afs.get_reader(index), false, is_v1);
this->collections.emplace(
this->key_for_params(mode, Episode::EP1, difficulty, section_id),
rel.as_collection());
this->key_for_params(mode, Episode::EP1, difficulty, section_id), rel.as_collection(Episode::EP1));
} catch (const out_of_range&) {
}
}
@@ -250,7 +256,8 @@ RareItemSet::RareItemSet(const AFSArchive& afs, bool is_v1) {
}
}
string RareItemSet::gsl_entry_name_for_table(GameMode mode, Episode episode, Difficulty difficulty, uint8_t section_id) {
string RareItemSet::gsl_entry_name_for_table(
GameMode mode, Episode episode, Difficulty difficulty, uint8_t section_id) {
return std::format("ItemRT{}{}{}{}.rel",
((mode == GameMode::CHALLENGE) ? "c" : ""),
((episode == Episode::EP2) ? "l" : ""),
@@ -259,7 +266,7 @@ string RareItemSet::gsl_entry_name_for_table(GameMode mode, Episode episode, Dif
}
RareItemSet::RareItemSet(const GSLArchive& gsl, bool is_big_endian) {
for (GameMode mode : ALL_GAME_MODES_V23) {
for (GameMode mode : {GameMode::NORMAL, GameMode::CHALLENGE}) {
for (Episode episode : ALL_EPISODES_V3) {
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
for (size_t section_id = 0; section_id < 10; section_id++) {
@@ -267,7 +274,7 @@ RareItemSet::RareItemSet(const GSLArchive& gsl, bool is_big_endian) {
string filename = this->gsl_entry_name_for_table(mode, episode, difficulty, section_id);
ParsedRELData rel(gsl.get_reader(filename), is_big_endian, false);
this->collections.emplace(
this->key_for_params(mode, episode, difficulty, section_id), rel.as_collection());
this->key_for_params(mode, episode, difficulty, section_id), rel.as_collection(episode));
} catch (const out_of_range&) {
}
}
@@ -287,7 +294,7 @@ RareItemSet::RareItemSet(const string& rel_data, bool is_big_endian) {
size_t index = (ep_index * 40) + static_cast<size_t>(difficulty) * 10 + section_id;
ParsedRELData rel(r.sub(0x280 * index, 0x280), is_big_endian, false);
this->collections.emplace(
this->key_for_params(GameMode::NORMAL, episode, difficulty, section_id), rel.as_collection());
this->key_for_params(GameMode::NORMAL, episode, difficulty, section_id), rel.as_collection(episode));
} catch (const out_of_range&) {
}
}
@@ -315,26 +322,19 @@ RareItemSet::RareItemSet(const phosg::JSON& json, shared_ptr<const ItemNameIndex
uint8_t section_id = section_id_for_name(section_id_it.first);
auto& collection = this->collections[this->key_for_params(mode, episode, difficulty, section_id)];
for (const auto& item_it : section_id_it.second->as_dict()) {
for (const auto& [enemy_type_name, specs_json] : section_id_it.second->as_dict()) {
vector<ExpandedDrop>* target;
if (item_it.first.starts_with("Box-")) {
uint8_t area_norm = FloorDefinition::get(episode, item_it.first.substr(4)).drop_area_norm;
if (collection.box_area_norm_to_specs.size() <= area_norm) {
collection.box_area_norm_to_specs.resize(area_norm + 1);
if (enemy_type_name.starts_with("Box-")) {
uint8_t area_norm = FloorDefinition::get(episode, enemy_type_name.substr(4)).drop_area_norm;
if (collection.box_specs.size() <= area_norm) {
collection.box_specs.resize(area_norm + 1);
}
target = &collection.box_area_norm_to_specs[area_norm];
target = &collection.box_specs[area_norm];
} else {
size_t rt_index = type_definition_for_enemy(phosg::enum_for_name<EnemyType>(item_it.first)).rt_index;
if (rt_index == 0xFF) {
throw runtime_error("enemy type " + item_it.first + " does not have an rt_index");
}
if (collection.rt_index_to_specs.size() <= rt_index) {
collection.rt_index_to_specs.resize(rt_index + 1);
}
target = &collection.rt_index_to_specs[rt_index];
target = &collection.enemy_specs[phosg::enum_for_name<EnemyType>(enemy_type_name)];
}
for (const auto& spec_json : item_it.second->as_list()) {
for (const auto& spec_json : specs_json->as_list()) {
auto& d = target->emplace_back();
auto prob_desc = spec_json->at(0);
@@ -702,7 +702,11 @@ string RareItemSet::serialize_html(
std::string exact_token = std::format("Exact rate: {} / {}", frac.first, frac.second);
if (common_item_set && type_def && type_def->rt_index != 0xFF) {
auto table = common_item_set->get_table(episode, mode, difficulty, section_id);
uint8_t dar = table->enemy_type_drop_probs.at(type_def->rt_index);
uint8_t dar = 0;
try {
dar = table->enemy_type_drop_probs.at(type_def->type);
} catch (const out_of_range&) {
}
exact_token += std::format(" (DAR: {}%)", dar);
frac.first *= dar;
frac.second *= 100;
@@ -747,13 +751,9 @@ string RareItemSet::serialize_html(
for (const auto& zone_type : zone_types) {
add_location_header(zone_type.name);
for (EnemyType type : zone_type.types) {
uint8_t rt_index = type_definition_for_enemy(type).rt_index;
if (rt_index == 0xFF) {
continue;
}
array<vector<ExpandedDrop>, 10> specs_lists;
for (uint8_t section_id = 0; section_id < 10; section_id++) {
specs_lists[section_id] = this->get_enemy_specs(mode, episode, difficulty, section_id, rt_index);
specs_lists[section_id] = this->get_enemy_specs(mode, episode, difficulty, section_id, type);
}
const auto& type_def = type_definition_for_enemy(type);
const char* name = (difficulty == Difficulty::ULTIMATE && type_def.ultimate_name) ? type_def.ultimate_name : type_def.in_game_name;
@@ -787,13 +787,9 @@ phosg::JSON RareItemSet::json(shared_ptr<const ItemNameIndex> name_index) const
auto section_id_dict = phosg::JSON::dict();
for (uint8_t section_id = 0; section_id < 10; section_id++) {
auto collection_dict = phosg::JSON::dict();
for (size_t rt_index = 0; rt_index < 0x80; rt_index++) {
const auto& enemy_types = enemy_types_for_rare_table_index(episode, rt_index);
if (enemy_types.empty()) {
continue;
}
for (const auto& spec : this->get_enemy_specs(GameMode::NORMAL, episode, difficulty, section_id, rt_index)) {
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
const auto& specs = this->get_enemy_specs(GameMode::NORMAL, episode, difficulty, section_id, enemy_type);
for (const auto& spec : specs) {
if (spec.data.empty()) {
continue;
}
@@ -807,12 +803,8 @@ phosg::JSON RareItemSet::json(shared_ptr<const ItemNameIndex> name_index) const
if (name_index) {
spec_json.emplace_back(name_index->describe_item(spec.data));
}
for (const auto& enemy_type : enemy_types) {
if (type_definition_for_enemy(enemy_type).valid_in_episode(episode)) {
phosg::JSON this_spec_json = spec_json;
collection_dict.emplace(phosg::name_for_enum(enemy_type), phosg::JSON::list()).first->second->emplace_back(std::move(this_spec_json));
}
}
auto list_emplace_ret = collection_dict.emplace(phosg::name_for_enum(enemy_type), phosg::JSON::list());
list_emplace_ret.first->second->emplace_back(std::move(spec_json));
}
}
@@ -858,17 +850,17 @@ phosg::JSON RareItemSet::json(shared_ptr<const ItemNameIndex> name_index) const
}
void RareItemSet::multiply_all_rates(double factor) {
auto multiply_rates_vec = +[](vector<vector<ExpandedDrop>>& vec, double factor) -> void {
for (auto& vec_it : vec) {
for (auto& z_it : vec_it) {
uint64_t new_probability = z_it.probability * factor;
z_it.probability = min<uint64_t>(new_probability, 0xFFFFFFFF);
for (auto& [_, collection] : this->collections) {
for (auto& [_, specs] : collection.enemy_specs) {
for (auto& spec : specs) {
spec.probability = min<uint64_t>(spec.probability * factor, 0xFFFFFFFF);
}
}
for (auto& specs : collection.box_specs) {
for (auto& spec : specs) {
spec.probability = min<uint64_t>(spec.probability * factor, 0xFFFFFFFF);
}
}
};
for (auto& coll_it : this->collections) {
multiply_rates_vec(coll_it.second.rt_index_to_specs, factor);
multiply_rates_vec(coll_it.second.box_area_norm_to_specs, factor);
}
}
@@ -893,28 +885,22 @@ void RareItemSet::print_collection(
name_for_section_id(section_id));
phosg::fwrite_fmt(stream, " Monster rares:\n");
for (size_t z = 0; z < collection->rt_index_to_specs.size(); z++) {
string enemy_types_str;
const auto& enemy_types = enemy_types_for_rare_table_index(episode, z);
for (EnemyType enemy_type : enemy_types) {
enemy_types_str += phosg::name_for_enum(enemy_type);
enemy_types_str.push_back(',');
}
if (!enemy_types_str.empty()) {
enemy_types_str.resize(enemy_types_str.size() - 1);
}
for (const auto& spec : collection->rt_index_to_specs[z]) {
string s = name_index ? spec.str(name_index) : spec.str();
phosg::fwrite_fmt(stream, " {:02X}: {} ({})\n", z, s, enemy_types_str);
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
try {
const auto& def = type_definition_for_enemy(enemy_type);
for (const auto& spec : collection->enemy_specs.at(enemy_type)) {
string s = name_index ? spec.str(name_index) : spec.str();
phosg::fwrite_fmt(stream, " {:<23} {}\n", def.enum_name, s);
}
} catch (const out_of_range&) {
}
}
phosg::fwrite_fmt(stream, " Box rares:\n");
for (size_t area_norm = 0; area_norm < collection->box_area_norm_to_specs.size(); area_norm++) {
for (const auto& spec : collection->box_area_norm_to_specs[area_norm]) {
for (size_t area_norm = 0; area_norm < collection->box_specs.size(); area_norm++) {
for (const auto& spec : collection->box_specs[area_norm]) {
string s = name_index ? spec.str(name_index) : spec.str();
phosg::fwrite_fmt(stream, " (area-norm {:02X}) {}\n", area_norm, s);
phosg::fwrite_fmt(stream, " (area-norm {:02X}) {}\n", area_norm, s);
}
}
}
@@ -934,10 +920,100 @@ void RareItemSet::print_all_collections(FILE* stream, std::shared_ptr<const Item
}
}
void RareItemSet::SpecCollection::print_diff(FILE* stream, const SpecCollection& other) const {
auto format_specs = [](const std::vector<ExpandedDrop>& specs) -> std::string {
std::string ret;
for (const auto& spec : specs) {
if (!ret.empty()) {
ret += ",";
}
ret += std::format("{:08X}:{}", spec.probability, spec.data.short_hex());
}
return ret;
};
const std::vector<ExpandedDrop> empty_specs{};
for (auto enemy_type : phosg::EnumRange<EnemyType>()) {
const std::vector<ExpandedDrop>* this_specs = &empty_specs;
const std::vector<ExpandedDrop>* other_specs = &empty_specs;
try {
this_specs = &this->enemy_specs.at(enemy_type);
} catch (const out_of_range&) {
}
try {
other_specs = &other.enemy_specs.at(enemy_type);
} catch (const out_of_range&) {
}
if (*this_specs != *other_specs) {
phosg::fwrite_fmt(stream, " {}: {} -> {}\n",
phosg::name_for_enum(enemy_type), format_specs(*this_specs), format_specs(*other_specs));
}
}
for (size_t area_norm = 0; area_norm < 10; area_norm++) {
const auto& this_specs = (area_norm < this->box_specs.size()) ? this->box_specs[area_norm] : empty_specs;
const auto& other_specs = (area_norm < other.box_specs.size()) ? other.box_specs[area_norm] : empty_specs;
if (this_specs != other_specs) {
phosg::fwrite_fmt(stream, " Box (area_norm {}): {} -> {}\n",
area_norm, format_specs(this_specs), format_specs(other_specs));
}
}
}
void RareItemSet::print_diff(FILE* stream, const RareItemSet& other) const {
bool any_difference_found = false;
for (const auto& episode : ALL_EPISODES_V4) {
for (const auto& mode : ALL_GAME_MODES_V4) {
for (const auto& difficulty : ALL_DIFFICULTIES_V234) {
for (uint8_t section_id = 0; section_id < 10; section_id++) {
const SpecCollection* this_coll = nullptr;
const SpecCollection* other_coll = nullptr;
try {
this_coll = &this->get_collection(mode, episode, difficulty, section_id);
} catch (const out_of_range&) {
}
try {
other_coll = &other.get_collection(mode, episode, difficulty, section_id);
} catch (const out_of_range&) {
}
if (!this_coll && !other_coll) {
continue;
} else if (!this_coll) {
any_difference_found = true;
phosg::fwrite_fmt(stream, "> Collection present in other but not this: {} {} {} {}\n",
name_for_episode(episode),
name_for_mode(mode),
name_for_difficulty(difficulty),
name_for_section_id(section_id));
} else if (!other_coll) {
any_difference_found = true;
phosg::fwrite_fmt(stream, "> Collection present in this but not other: {} {} {} {}\n",
name_for_episode(episode),
name_for_mode(mode),
name_for_difficulty(difficulty),
name_for_section_id(section_id));
} else if (*this_coll != *other_coll) {
any_difference_found = true;
phosg::fwrite_fmt(stream, "> Collections do not match: {} {} {} {}\n",
name_for_episode(episode),
name_for_mode(mode),
name_for_difficulty(difficulty),
name_for_section_id(section_id));
this_coll->print_diff(stream, *other_coll);
}
}
}
}
}
if (!any_difference_found) {
phosg::fwrite_fmt(stream, "> These rare item sets are identical\n");
}
}
std::vector<RareItemSet::ExpandedDrop> RareItemSet::get_enemy_specs(
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, uint8_t rt_index) const {
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, EnemyType enemy_type) const {
try {
return this->get_collection(mode, episode, difficulty, secid).rt_index_to_specs.at(rt_index);
return this->get_collection(mode, episode, difficulty, secid).enemy_specs.at(enemy_type);
} catch (const out_of_range&) {
static const std::vector<ExpandedDrop> empty_vector;
return empty_vector;
@@ -947,7 +1023,7 @@ std::vector<RareItemSet::ExpandedDrop> RareItemSet::get_enemy_specs(
std::vector<RareItemSet::ExpandedDrop> RareItemSet::get_box_specs(
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, uint8_t area_norm) const {
try {
return this->get_collection(mode, episode, difficulty, secid).box_area_norm_to_specs.at(area_norm);
return this->get_collection(mode, episode, difficulty, secid).box_specs.at(area_norm);
} catch (const out_of_range&) {
static const std::vector<ExpandedDrop> empty_vector;
return empty_vector;
@@ -965,7 +1041,14 @@ bool RareItemSet::has_entries_for_game_config(GameMode mode, Episode episode, Di
const RareItemSet::SpecCollection& RareItemSet::get_collection(
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid) const {
return this->collections.at(this->key_for_params(mode, episode, difficulty, secid));
try {
return this->collections.at(this->key_for_params(mode, episode, difficulty, secid));
} catch (const out_of_range&) {
if (mode == GameMode::BATTLE || mode == GameMode::SOLO) {
return this->collections.at(this->key_for_params(GameMode::NORMAL, episode, difficulty, secid));
}
throw;
}
}
uint16_t RareItemSet::key_for_params(GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid) {
+15 -6
View File
@@ -22,6 +22,9 @@ public:
uint32_t probability = 0;
ItemData data;
bool operator==(const ExpandedDrop& other) const = default;
bool operator!=(const ExpandedDrop& other) const = default;
std::string str() const;
std::string str(std::shared_ptr<const ItemNameIndex> name_index) const;
};
@@ -34,7 +37,7 @@ public:
~RareItemSet() = default;
std::vector<ExpandedDrop> get_enemy_specs(
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, uint8_t rt_index) const;
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, EnemyType enemy_type) const;
std::vector<ExpandedDrop> get_box_specs(
GameMode mode, Episode episode, Difficulty difficulty, uint8_t secid, uint8_t area_norm) const;
@@ -60,11 +63,17 @@ public:
uint8_t section_id,
std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
void print_all_collections(FILE* stream, std::shared_ptr<const ItemNameIndex> name_index = nullptr) const;
void print_diff(FILE* stream, const RareItemSet& other) const;
protected:
struct SpecCollection {
std::vector<std::vector<ExpandedDrop>> rt_index_to_specs;
std::vector<std::vector<ExpandedDrop>> box_area_norm_to_specs;
std::unordered_map<EnemyType, std::vector<ExpandedDrop>> enemy_specs;
std::vector<std::vector<ExpandedDrop>> box_specs; // Indexed by area_norm
bool operator==(const SpecCollection& other) const = default;
bool operator!=(const SpecCollection& other) const = default;
void print_diff(FILE* stream, const SpecCollection& other) const;
};
struct ParsedRELData {
@@ -95,8 +104,8 @@ protected:
ExpandedDrop drop;
};
std::vector<ExpandedDrop> monster_rares;
std::vector<BoxRare> box_rares;
std::vector<ExpandedDrop> monster_rares; // Indexed by rt_index
std::vector<BoxRare> box_rares; // Not indexed (area_norm + 1 is in the struct)
ParsedRELData() = default;
ParsedRELData(phosg::StringReader r, bool big_endian, bool is_v1);
@@ -108,7 +117,7 @@ protected:
template <bool BE>
std::string serialize_t(bool is_v1) const;
SpecCollection as_collection() const;
SpecCollection as_collection(Episode episode) const;
};
std::unordered_map<uint16_t, SpecCollection> collections;
+612 -70
View File
File diff suppressed because it is too large Load Diff
+370 -88
View File
@@ -2,7 +2,9 @@
#include <math.h>
#include <string.h>
#include <time.h>
#include <fstream>
#include <memory>
#include <phosg/Random.hh>
#include <phosg/Strings.hh>
@@ -76,6 +78,184 @@ using SDF = SubcommandDefinition::Flag;
extern const vector<SubcommandDefinition> subcommand_definitions;
static string json_escape_for_hardcore_ledger(const string& text) {
string ret;
ret.reserve(text.size() + 16);
for (char ch : text) {
switch (ch) {
case '\\':
ret += "\\\\";
break;
case '"':
ret += "\\\"";
break;
case '\n':
ret += "\\n";
break;
case '\r':
ret += "\\r";
break;
case '\t':
ret += "\\t";
break;
default:
ret += ch;
}
}
return ret;
}
static string hardcore_death_character_name_for_ledger(shared_ptr<Client> c) {
try {
auto p = c->character_file(false);
if (p) {
string name = p->disp.name.decode(c->language());
if (!name.empty()) {
return name;
}
}
} catch (const exception&) {
}
return "";
}
static string hardcore_death_ledger_filename(shared_ptr<Client> c) {
string char_filename = c->character_filename();
size_t slash_offset = char_filename.find_last_of('/');
if (slash_offset == string::npos) {
return "hardcore-deaths.jsonl";
}
return char_filename.substr(0, slash_offset + 1) + "hardcore-deaths.jsonl";
}
static string hardcore_zone_name(shared_ptr<Client> c) {
// Basic Episode 1 floor names. Unknown Episode 2/4 floors will fall back safely for now.
switch (c->floor) {
case 0:
return "Pioneer 2";
case 1:
return "Forest 1";
case 2:
return "Forest 2";
case 3:
return "Caves 1";
case 4:
return "Caves 2";
case 5:
return "Caves 3";
case 6:
return "Mines 1";
case 7:
return "Mines 2";
case 8:
return "Ruins 1";
case 9:
return "Ruins 2";
case 10:
return "Ruins 3";
case 11:
return "Dragon";
case 12:
return "De Rol Le";
case 13:
return "Vol Opt";
case 14:
return "Dark Falz";
default:
return std::format("unknown zone {}", c->floor);
}
}
static bool append_hardcore_death_ledger(shared_ptr<Client> c, const char* reason) {
string filename = hardcore_death_ledger_filename(c);
ofstream f(filename, ios::out | ios::app);
if (!f.good()) {
return false;
}
uint32_t account_id = 0;
string username;
if (c->login) {
if (c->login->account) {
account_id = c->login->account->account_id;
}
if (c->login->bb_license) {
username = c->login->bb_license->username;
}
}
string character_file = c->character_filename();
string character_name = hardcore_death_character_name_for_ledger(c);
string zone = hardcore_zone_name(c);
f << "{"
<< "\"time_epoch\":" << static_cast<long long>(time(nullptr)) << ","
<< "\"account_id\":" << account_id << ","
<< "\"username\":\"" << json_escape_for_hardcore_ledger(username) << "\","
<< "\"character_file\":\"" << json_escape_for_hardcore_ledger(character_file) << "\","
<< "\"character_name\":\"" << json_escape_for_hardcore_ledger(character_name) << "\","
<< "\"zone\":\"" << json_escape_for_hardcore_ledger(zone) << "\","
<< "\"reason\":\"" << json_escape_for_hardcore_ledger(reason ? reason : "") << "\""
<< "}\n";
return f.good();
}
static string bb_hardcore_dead_filename_for_subcommands(shared_ptr<Client> c) {
return c->character_filename() + ".hardcore-dead";
}
static bool current_ship_is_hardcore_bb(shared_ptr<Client> c) {
try {
auto s = c->require_server_state();
return s->enable_hardcore_mode && (c->version() == Version::BB_V4);
} catch (const exception&) {
return false;
}
}
static bool mark_bb_character_hardcore_dead_for_subcommands(shared_ptr<Client> c, const char* reason) {
string filename = bb_hardcore_dead_filename_for_subcommands(c);
bool already_dead = false;
{
ifstream existing_f(filename);
already_dead = existing_f.good();
}
ofstream f(filename, ios::out | ios::trunc);
if (!f.good()) {
return false;
}
uint32_t account_id = 0;
if (c->login && c->login->account) {
account_id = c->login->account->account_id;
}
f << "status=hardcore-dead\n";
f << "reason=" << reason << "\n";
f << "account_id=" << account_id << "\n";
f << "character_file=" << c->character_filename() << "\n";
bool ok = f.good();
f.close();
if (ok && !already_dead) {
if (append_hardcore_death_ledger(c, reason)) {
c->log.info_f("Hardcore permadeath: appended death ledger entry for {}", c->character_filename());
} else {
c->log.warning_f("Hardcore permadeath: FAILED to append death ledger entry for {}", c->character_filename());
}
}
return ok;
}
const SubcommandDefinition* def_for_subcommand(Version version, uint8_t subcommand) {
static bool populated = false;
static std::array<const SubcommandDefinition*, 0x100> nte_defs;
@@ -399,7 +579,7 @@ asio::awaitable<void> forward_subcommand_with_entity_id_transcode_t(shared_ptr<C
template <typename HeaderT>
asio::awaitable<void> forward_subcommand_with_entity_targets_transcode_and_track_hits_t(
shared_ptr<Client> c, SubcommandMessage& msg) {
// I'm lazy and this should never happen for item commands (since all players need to stay in sync)
// I'm lazy and this should never happen for private commands
if (command_is_private(msg.command)) {
throw runtime_error("entity subcommand sent via private command");
}
@@ -430,16 +610,12 @@ asio::awaitable<void> forward_subcommand_with_entity_targets_transcode_and_track
if ((res.entity_id >= 0x1000) && (res.entity_id < 0x4000)) {
auto ene_st = l->map_state->enemy_state_for_index(c->version(), res.entity_id - 0x1000);
res.ene_st = ene_st;
// Track hits for all resolved enemies
c->log.info_f("Claiming last hit on E-{:03X}", ene_st->e_id);
ene_st->set_last_hit_by_client_id(c->lobby_client_id);
l->log.info_f("E-{:03X} last hit claimed", ene_st->e_id);
if (ene_st->alias_target_ene_st) {
c->log.info_f("Claiming last hit on E-{:03X} (alias of E-{:03X})",
ene_st->alias_target_ene_st->e_id, ene_st->e_id);
ene_st->alias_target_ene_st->set_last_hit_by_client_id(c->lobby_client_id);
l->log.info_f("Alias target E-{:03X} last hit claimed", ene_st->alias_target_ene_st->e_id);
}
} else if ((res.entity_id >= 0x4000) && (res.entity_id < 0xFFFF)) {
res.obj_st = l->map_state->object_state_for_index(c->version(), res.entity_id - 0x4000);
}
@@ -803,8 +979,8 @@ Parsed6x70Data::Parsed6x70Data(
death_flags(cmd.death_flags),
hold_state(cmd.hold_state),
area(cmd.area),
game_flags(cmd.game_flags),
game_flags_is_v3(false),
player_flags(cmd.player_flags),
player_flags_is_v3(false),
visual(cmd.visual),
stats(cmd.stats),
num_items(cmd.num_items),
@@ -839,8 +1015,8 @@ Parsed6x70Data::Parsed6x70Data(
death_flags(cmd.death_flags),
hold_state(cmd.hold_state),
area(cmd.area),
game_flags(cmd.game_flags),
game_flags_is_v3(false),
player_flags(cmd.player_flags),
player_flags_is_v3(false),
visual(cmd.visual),
stats(cmd.stats),
num_items(cmd.num_items),
@@ -872,7 +1048,7 @@ Parsed6x70Data::Parsed6x70Data(
Version from_version,
bool from_client_customization)
: Parsed6x70Data(cmd.base, guild_card_number, from_version, from_client_customization) {
this->game_flags_is_v3 = true;
this->player_flags_is_v3 = true;
this->stats = cmd.stats;
this->num_items = cmd.num_items;
this->items = cmd.items;
@@ -888,7 +1064,7 @@ Parsed6x70Data::Parsed6x70Data(
Version from_version,
bool from_client_customization)
: Parsed6x70Data(cmd.base, guild_card_number, from_version, from_client_customization) {
this->game_flags_is_v3 = true;
this->player_flags_is_v3 = true;
this->stats = cmd.stats;
this->num_items = cmd.num_items;
this->items = cmd.items;
@@ -904,7 +1080,7 @@ Parsed6x70Data::Parsed6x70Data(
Version from_version,
bool from_client_customization)
: Parsed6x70Data(cmd.base, guild_card_number, from_version, from_client_customization) {
this->game_flags_is_v3 = true;
this->player_flags_is_v3 = true;
this->stats = cmd.stats;
this->num_items = cmd.num_items;
this->items = cmd.items;
@@ -924,7 +1100,7 @@ G_SyncPlayerDispAndInventory_DCNTE_6x70 Parsed6x70Data::as_dc_nte(shared_ptr<Ser
ret.death_flags = this->death_flags;
ret.hold_state = this->hold_state;
ret.area = this->area;
ret.game_flags = this->get_game_flags(false);
ret.player_flags = this->get_player_flags(false);
ret.visual = this->visual;
ret.stats = this->stats;
ret.num_items = this->num_items;
@@ -956,7 +1132,7 @@ G_SyncPlayerDispAndInventory_DC112000_6x70 Parsed6x70Data::as_dc_112000(shared_p
ret.death_flags = this->death_flags;
ret.hold_state = this->hold_state;
ret.area = this->area;
ret.game_flags = this->get_game_flags(false);
ret.player_flags = this->get_player_flags(false);
ret.visual = this->visual;
ret.stats = this->stats;
ret.num_items = this->num_items;
@@ -1112,7 +1288,7 @@ Parsed6x70Data::Parsed6x70Data(
temporary_status_effect(base.temporary_status_effect),
attack_status_effect(base.attack_status_effect),
defense_status_effect(base.defense_status_effect),
unused_status_effect(base.unused_status_effect),
unknown_a1_status_effect(base.unknown_a1_status_effect),
language(static_cast<Language>(base.language32.load())),
player_tag(base.player_tag),
guild_card_number(guild_card_number), // Ignore the client's GC#
@@ -1122,8 +1298,8 @@ Parsed6x70Data::Parsed6x70Data(
death_flags(base.death_flags),
hold_state(base.hold_state),
area(base.area),
game_flags(base.game_flags),
game_flags_is_v3(!is_v1_or_v2(from_version)),
player_flags(base.player_flags),
player_flags_is_v3(!is_v1_or_v2(from_version)),
technique_levels_v1(base.technique_levels_v1),
visual(base.visual) {}
@@ -1136,7 +1312,7 @@ G_6x70_Base_V1 Parsed6x70Data::base_v1(bool is_v3) const {
ret.temporary_status_effect = this->temporary_status_effect;
ret.attack_status_effect = this->attack_status_effect;
ret.defense_status_effect = this->defense_status_effect;
ret.unused_status_effect = this->unused_status_effect;
ret.unknown_a1_status_effect = this->unknown_a1_status_effect;
ret.language32 = static_cast<size_t>(this->language);
ret.player_tag = this->player_tag;
ret.guild_card_number = this->guild_card_number;
@@ -1146,46 +1322,73 @@ G_6x70_Base_V1 Parsed6x70Data::base_v1(bool is_v3) const {
ret.death_flags = this->death_flags;
ret.hold_state = this->hold_state;
ret.area = this->area;
ret.game_flags = this->get_game_flags(is_v3);
ret.player_flags = this->get_player_flags(is_v3);
ret.technique_levels_v1 = this->technique_levels_v1;
ret.visual = this->visual;
return ret;
}
uint32_t Parsed6x70Data::convert_game_flags(uint32_t game_flags, bool to_v3) {
// The format of game_flags for players was changed significantly between v2 and v3, and not accounting for this
// results in odd effects like other characters not appearing when joining a game. Unfortunately, some bits were
// deleted on v3 and other bits were added, so it doesn't suffice to simply store the most complete format of this
// field - we have to be able to convert between the two.
// Bits on v2: JIHCBAzy xwvutsrq ponmlkji hgfedcba
// Bits on v3: JIHGFEDC BAzyxwvu srqponkj hgfedcba
// The bits ilmt were removed in v3 and the bits to their left were shifted right. The bits DEFG were added in v3 and
// do not exist on v2. Known meanings for these bits so far:
// o = is dead
// n = should play hit animation
// y = is near enemy
// H = is enemy?
// I = is object? (some entities have both H and I set though)
// J = is item
uint32_t Parsed6x70Data::convert_player_flags(uint32_t player_flags, bool to_v3) {
// The format of player_flags was changed significantly between v2 and v3, and not accounting for this results in odd
// effects like other characters not appearing when joining a game. Unfortunately, some bits were deleted on v3 and
// other bits were added, so it doesn't suffice to simply store the most complete format of this field - we have to
// be able to convert between the two. What's known about these bits (? indicates meaning/behavior is unverified):
// V1/V2 V3/V4
// 00000001 00000001 = player hold is set (see notes on 6x2C and 6x2D in CommandFormats.hh)
// 00000002 00000002 = player hold is set for the purpose of dropping an item
// 00000004 00000004 = is on a different floor from the local player (or is loading into game)
// 00000008 00000008 = unknown (TODO: appears to be entirely unused, at least in v2 and later)
// 00000010 00000010 = player has sent any update command (position, attack, etc.) this frame
// 00000020 00000020 = unknown (TODO: appears to be entirely unused, at least in v2 and later)
// 00000040 00000040 = unknown (TODO: appears to be entirely unused, at least in v2 and later)
// 00000080 00000080 = is in windup animation for technique or PB
// 00000100 -------- = unknown (TODO: appears to be entirely unused, at least in v2)
// 00000200 00000100 = chat, pause, or quick menu is open (suppresses action palette)
// 00000400 00000200 = is in PB cutscene
// 00000800 -------- = player is standing in water (steps create splash particles)
// 00001000 -------- = player is standing in grass (steps create grass particles)
// 00002000 00000400 = action palette is disabled by p_action_disable or one of the TObjQuestColA* objects
// 00004000 00000800 = is about to return to Pioneer 2 after a death (TODO: also set by p_return_guild)
// 00008000 00001000 = cannot use telepipe / Ryuker (e.g. boss warps set this flag when the player is nearby)
// 00010000 00002000 = unknown (TODO: appears to be entirely unused, at least in v3)
// 00020000 00004000 = is teleporting as a result of 6x24 (set only briefly after appearing at destination)
// 00040000 00008000 = is dead NPC (set by e.g. npc_crptalk_id when regsA[4] == 1)
// 00080000 -------- = unknown (TODO: appears to be entirely unused, at least in v2)
// 00100000 00010000 = has permanent trap vision (e.g. is android)
// 00200000 00020000 = equipped items are invisible / intangible (e.g. in Pioneer 2)
// 00400000 00040000 = is loading / changing floors (set by 6x22 and at game join, cleared by 6x23)
// 00800000 00080000 = player data is in the process of being exported to save file format
// 01000000 00100000 = initial cutscene with the Principal has started in offline free-play single-mode
// 02000000 00200000 = is visible (set shortly after warping into a floor; remains set until next warp)
// 04000000 00400000 = position is valid (therefore player can be rendered)
// 08000000 00800000 = player is invisible if not local, but items are still visible (TODO: what about on BB?)
// 10000000 01000000 = if set, player does not drop weapon on death
// -------- 02000000 = player's room_id_in_custom_area does not match player's room_id (used by TObjRoomId when
// param6 == 0x00010000; TODO: figure out what this does)
// -------- 04000000 = is sitting in lobby chair
// -------- 08000000 = using male animations / poses in lobby chair
// -------- 10000000 = using alternate lobby chair pose (X+B instead of X+A on GC, for example)
// -------- 20000000 = appears to be unused (there is no codepath that sets this flag); if set, the player will
// freeze when attempting any lobby animation
// -------- 40000000 = unknown (TODO: used in offline multi-player Challenge mode; see tree from 3OE1:801BE594)
if (to_v3) {
return (game_flags & 0xE00000FF) |
((game_flags & 0x00000600) >> 1) |
((game_flags & 0x0007E000) >> 3) |
((game_flags & 0x1FF00000) >> 4);
return (player_flags & 0x000000FF) |
((player_flags & 0x00000600) >> 1) |
((player_flags & 0x0007E000) >> 3) |
((player_flags & 0x1FF00000) >> 4);
} else {
return (game_flags & 0xE00000FF) |
((game_flags << 1) & 0x00000600) |
((game_flags << 3) & 0x0007E000) |
((game_flags << 4) & 0x1FF00000);
return (player_flags & 0x000000FF) |
((player_flags << 1) & 0x00000600) |
((player_flags << 3) & 0x0007E000) |
((player_flags << 4) & 0x1FF00000);
}
}
uint32_t Parsed6x70Data::get_game_flags(bool is_v3) const {
return (this->game_flags_is_v3 == is_v3)
? this->game_flags
: Parsed6x70Data::convert_game_flags(this->game_flags, is_v3);
uint32_t Parsed6x70Data::get_player_flags(bool is_v3) const {
return (this->player_flags_is_v3 == is_v3)
? this->player_flags
: Parsed6x70Data::convert_player_flags(this->player_flags, is_v3);
}
static asio::awaitable<void> on_sync_joining_player_disp_and_inventory(
@@ -1636,7 +1839,21 @@ static asio::awaitable<void> on_player_died(shared_ptr<Client> c, SubcommandMess
} catch (const out_of_range&) {
}
bool hardcore_death = current_ship_is_hardcore_bb(c);
if (hardcore_death) {
if (mark_bb_character_hardcore_dead_for_subcommands(c, "player-death-subcommand")) {
c->log.warning_f("Hardcore permadeath: marked BB character dead: {}", c->character_filename());
} else {
c->log.error_f("Hardcore permadeath: FAILED to mark BB character dead: {}", c->character_filename());
}
}
forward_subcommand(c, msg);
if (hardcore_death) {
send_message_box(c, "DADDY FALZ WINS");
c->channel->disconnect();
}
}
static asio::awaitable<void> on_player_revivable(shared_ptr<Client> c, SubcommandMessage& msg) {
@@ -1647,8 +1864,23 @@ static asio::awaitable<void> on_player_revivable(shared_ptr<Client> c, Subcomman
co_return;
}
bool hardcore_death = current_ship_is_hardcore_bb(c);
if (hardcore_death) {
if (mark_bb_character_hardcore_dead_for_subcommands(c, "player-revivable-subcommand")) {
c->log.warning_f("Hardcore permadeath: marked BB character dead from revivable state: {}", c->character_filename());
} else {
c->log.error_f("Hardcore permadeath: FAILED to mark BB character dead from revivable state: {}", c->character_filename());
}
}
forward_subcommand(c, msg);
if (hardcore_death) {
send_message_box(c, "DADDY FALZ WINS");
c->channel->disconnect();
co_return;
}
// Revive if infinite HP is enabled
bool player_cheats_enabled = l->check_flag(Lobby::Flag::CHEATS_ENABLED) ||
(c->login->account->check_flag(Account::Flag::CHEAT_ANYWHERE));
@@ -1671,6 +1903,17 @@ static asio::awaitable<void> on_player_revived(shared_ptr<Client> c, SubcommandM
auto l = c->require_lobby();
if (l->is_game()) {
if (current_ship_is_hardcore_bb(c)) {
if (mark_bb_character_hardcore_dead_for_subcommands(c, "player-revived-subcommand")) {
c->log.warning_f("Hardcore permadeath: blocked revive and marked BB character dead: {}", c->character_filename());
} else {
c->log.error_f("Hardcore permadeath: FAILED to mark BB character dead while blocking revive: {}", c->character_filename());
}
send_message_box(c, "DADDY FALZ WINS");
c->channel->disconnect();
co_return;
}
forward_subcommand(c, msg);
if ((l->check_flag(Lobby::Flag::CHEATS_ENABLED) || (c->login->account->check_flag(Account::Flag::CHEAT_ANYWHERE))) &&
c->check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
@@ -2241,6 +2484,15 @@ static asio::awaitable<void> on_pick_up_item_generic(
co_return;
}
// TODO: Figure out what the actual max range is; 30 is an overestimate
double dist2 = fi->pos.dist2(c->pos);
if (dist2 > 900.0) {
l->log.warning_f("Player {} requests to pick up {:08X}, but it is too far away (dist2={})",
client_id, item_id, dist2);
l->add_item(floor, fi);
co_return;
}
try {
p->add_item(fi->data, *s->item_stack_limits(c->version()));
} catch (const out_of_range&) {
@@ -2810,7 +3062,7 @@ DropReconcileResult reconcile_drop_request_with_map(
bool is_box = (cmd.rt_index == 0x30);
DropReconcileResult res;
res.effective_rt_index = 0xFF;
res.effective_enemy_type = EnemyType::UNKNOWN;
res.should_drop = true;
res.ignore_def = (cmd.ignore_def != 0);
if (!map) {
@@ -2856,22 +3108,22 @@ DropReconcileResult reconcile_drop_request_with_map(
res.ref_ene_st = map->enemy_state_for_index(version, cmd.floor, cmd.entity_index);
res.target_ene_st = res.ref_ene_st->alias_target_ene_st ? res.ref_ene_st->alias_target_ene_st : res.ref_ene_st;
uint8_t area = map->floor_to_area.at(res.target_ene_st->super_ene->floor);
EnemyType type = res.target_ene_st->type(version, area, difficulty, event);
res.effective_enemy_type = res.target_ene_st->type(version, area, difficulty, event);
c->log.info_f("Drop check for E-{:03X} (target E-{:03X}, type {})",
res.ref_ene_st->e_id, res.target_ene_st->e_id, phosg::name_for_enum(type));
res.effective_rt_index = type_definition_for_enemy(type).rt_index;
res.ref_ene_st->e_id, res.target_ene_st->e_id, phosg::name_for_enum(res.effective_enemy_type));
uint8_t expected_rt_index = type_definition_for_enemy(res.effective_enemy_type).rt_index;
bool mismatched_rt_index = false;
if (cmd.rt_index != res.effective_rt_index) {
if (cmd.rt_index != expected_rt_index) {
// Special cases: BULCLAW => BULK and DARK_GUNNER => DEATH_GUNNER
if (cmd.rt_index == 0x27 && type == EnemyType::BULCLAW) {
if ((cmd.rt_index == 0x27) && (res.effective_enemy_type == EnemyType::BULCLAW)) {
c->log.info_f("E-{:03X} killed as BULK instead of BULCLAW", res.target_ene_st->e_id);
res.effective_rt_index = 0x27;
} else if (cmd.rt_index == 0x23 && type == EnemyType::DARK_GUNNER) {
res.effective_enemy_type = EnemyType::BULK;
} else if ((cmd.rt_index == 0x23) && (res.effective_enemy_type == EnemyType::DARK_GUNNER)) {
c->log.info_f("E-{:03X} killed as DEATH_GUNNER instead of DARK_GUNNER", res.target_ene_st->e_id);
res.effective_rt_index = 0x23;
res.effective_enemy_type = EnemyType::DEATH_GUNNER;
} else {
c->log.warning_f("rt_index {:02X} from command does not match entity\'s expected index {:02X}",
cmd.rt_index, res.effective_rt_index);
cmd.rt_index, expected_rt_index);
mismatched_rt_index = true;
}
}
@@ -2881,9 +3133,10 @@ DropReconcileResult reconcile_drop_request_with_map(
}
if (c->check_flag(Client::Flag::DEBUG_ENABLED)) {
std::string rt_index_str = mismatched_rt_index
? std::format(" $C4{:02X}->{:02X}$C5", cmd.rt_index, res.effective_rt_index)
: std::format(" {:02X}", res.effective_rt_index);
send_text_message_fmt(c, "$C5E-{:03X}{} {}", res.target_ene_st->e_id, rt_index_str, phosg::name_for_enum(type));
? std::format(" $C4{:02X}->{:02X}$C5", cmd.rt_index, expected_rt_index)
: std::format(" {:02X}", expected_rt_index);
send_text_message_fmt(c, "$C5E-{:03X}{} {}",
res.target_ene_st->e_id, rt_index_str, phosg::name_for_enum(res.effective_enemy_type));
}
}
@@ -2947,12 +3200,13 @@ static asio::awaitable<void> on_entity_drop_item_request(shared_ptr<Client> c, S
}
if (rec.should_drop) {
auto generate_item = [&]() -> ItemCreator::DropResult {
auto generate_item_for_client = [&](std::shared_ptr<Client> c) -> ItemCreator::DropResult {
bool force_rare = c->check_flag(Client::Flag::ALL_RARES_ENABLED);
if (rec.obj_st) {
if (rec.ignore_def) {
l->log.info_f("Creating item from box {:04X} => K-{:03X} (area {:02X})",
cmd.entity_index, rec.obj_st->k_id, cmd.effective_area);
return l->item_creator->on_box_item_drop(cmd.effective_area);
return l->item_creator->on_box_item_drop(cmd.effective_area, force_rare);
} else {
l->log.info_f(
"Creating item from box {:04X} => K-{:03X} (area {:02X}; specialized with {:g} {:08X} {:08X} {:08X})",
@@ -2963,7 +3217,7 @@ static asio::awaitable<void> on_entity_drop_item_request(shared_ptr<Client> c, S
} else if (rec.target_ene_st) {
l->log.info_f("Creating item from enemy {:04X} => E-{:03X} (area {:02X})",
cmd.entity_index, rec.target_ene_st->e_id, cmd.effective_area);
return l->item_creator->on_monster_item_drop(rec.effective_rt_index, cmd.effective_area);
return l->item_creator->on_monster_item_drop(rec.effective_enemy_type, cmd.effective_area, force_rare);
} else {
throw runtime_error("neither object nor enemy were present");
}
@@ -2985,7 +3239,7 @@ static asio::awaitable<void> on_entity_drop_item_request(shared_ptr<Client> c, S
throw logic_error("unhandled simple drop mode");
case ServerDropMode::SERVER_SHARED:
case ServerDropMode::SERVER_DUPLICATE: {
auto res = generate_item();
auto res = generate_item_for_client(c);
if (res.item.empty()) {
l->log.info_f("No item was created");
} else {
@@ -3023,7 +3277,7 @@ static asio::awaitable<void> on_entity_drop_item_request(shared_ptr<Client> c, S
case ServerDropMode::SERVER_PRIVATE: {
for (const auto& lc : l->clients) {
if (lc && (rec.obj_st || (lc->floor == cmd.floor))) {
auto res = generate_item();
auto res = generate_item_for_client(lc);
if (res.item.empty()) {
l->log.info_f("No item was created for {}", lc->channel->name);
} else {
@@ -3434,17 +3688,29 @@ static asio::awaitable<void> on_update_enemy_state(shared_ptr<Client> c, Subcomm
}
auto ene_st = l->map_state->enemy_state_for_index(c->version(), cmd.enemy_index);
uint32_t src_flags = is_big_endian(c->version()) ? bswap32(cmd.game_flags) : cmd.game_flags.load();
if (src_flags & 0xD0000000) { // Don't allow player, object, or item flags to be set
throw std::runtime_error("incorrect entity type flags in 6x0A command");
}
if (l->difficulty == Difficulty::ULTIMATE) {
src_flags = (src_flags & 0xFFFFFFC0) | (ene_st->game_flags & 0x0000003F);
}
bool should_track_hit = (src_flags & 0x00000200); // "Enemy was hit" flag (see 6x0A comments in CommandFormats.hh)
if (should_track_hit) {
ene_st->set_last_hit_by_client_id(c->lobby_client_id);
}
ene_st->game_flags = src_flags;
ene_st->total_damage = cmd.total_damage;
l->log.info_f("E-{:03X} updated to damage={} game_flags={:08X}", ene_st->e_id, ene_st->total_damage, ene_st->game_flags);
l->log.info_f("E-{:03X} updated to damage={} game_flags={:08X}; last hit {}",
ene_st->e_id, ene_st->total_damage, ene_st->game_flags, should_track_hit ? "claimed" : "not claimed");
if (ene_st->alias_target_ene_st) {
if (should_track_hit) {
ene_st->alias_target_ene_st->set_last_hit_by_client_id(c->lobby_client_id);
}
ene_st->alias_target_ene_st->game_flags = src_flags;
ene_st->alias_target_ene_st->total_damage = cmd.total_damage;
l->log.info_f("Alias target E-{:03X} updated to damage={} game_flags={:08X}",
ene_st->alias_target_ene_st->e_id, ene_st->alias_target_ene_st->total_damage, ene_st->alias_target_ene_st->game_flags);
l->log.info_f("Alias target E-{:03X} updated to damage={} game_flags={:08X}; last hit {}",
ene_st->alias_target_ene_st->e_id, ene_st->alias_target_ene_st->total_damage, ene_st->alias_target_ene_st->game_flags, should_track_hit ? "claimed" : "not claimed");
}
// TODO: It'd be nice if this worked on bosses too, but it seems we have to use each boss' specific state-syncing
@@ -3492,8 +3758,18 @@ static asio::awaitable<void> on_incr_enemy_damage(shared_ptr<Client> c, Subcomma
cmd.total_damage_before_hit.load(),
cmd.current_hp_before_hit.load(),
cmd.max_hp.load());
if (cmd.hit_amount > 0) {
c->log.info_f("Claiming last hit on E-{:03X}", ene_st->e_id);
ene_st->set_last_hit_by_client_id(c->lobby_client_id);
}
ene_st->total_damage = std::min<uint32_t>(ene_st->total_damage + cmd.hit_amount, cmd.max_hp);
if (ene_st->alias_target_ene_st) {
if (cmd.hit_amount > 0) {
c->log.info_f("Claiming last hit on E-{:03X} (alias of E-{:03X})",
ene_st->alias_target_ene_st->e_id, ene_st->e_id);
ene_st->alias_target_ene_st->set_last_hit_by_client_id(c->lobby_client_id);
}
ene_st->alias_target_ene_st->total_damage = std::min<uint32_t>(
ene_st->alias_target_ene_st->total_damage + cmd.hit_amount, cmd.max_hp);
}
@@ -3501,13 +3777,13 @@ static asio::awaitable<void> on_incr_enemy_damage(shared_ptr<Client> c, Subcomma
co_await forward_subcommand_with_entity_id_transcode_t<G_IncrementEnemyDamage_Extension_6xE4>(c, msg);
}
static asio::awaitable<void> on_set_enemy_low_game_flags_ultimate(shared_ptr<Client> c, SubcommandMessage& msg) {
static asio::awaitable<void> on_set_enemy_status_effect_flags_ultimate(shared_ptr<Client> c, SubcommandMessage& msg) {
auto& cmd = msg.check_size_t<G_SetEnemyLowGameFlagsUltimate_6x9C>();
if (command_is_private(msg.command) ||
(cmd.header.entity_id < 0x1000) ||
(cmd.header.entity_id >= 0x4000) ||
(cmd.low_game_flags & 0xFFFFFFC0) ||
(cmd.status_effect_flags & 0xFFFFFFC0) ||
(c->lobby_client_id > 3)) {
co_return;
}
@@ -3517,12 +3793,12 @@ static asio::awaitable<void> on_set_enemy_low_game_flags_ultimate(shared_ptr<Cli
}
auto ene_st = l->map_state->enemy_state_for_index(c->version(), cmd.header.entity_id - 0x1000);
if (!(ene_st->game_flags & cmd.low_game_flags)) {
ene_st->game_flags |= cmd.low_game_flags;
if (!(ene_st->game_flags & cmd.status_effect_flags)) {
ene_st->game_flags |= cmd.status_effect_flags;
l->log.info_f("E-{:03X} updated to game_flags={:08X}", ene_st->e_id, ene_st->game_flags);
}
if (ene_st->alias_target_ene_st && !(ene_st->alias_target_ene_st->game_flags & cmd.low_game_flags)) {
ene_st->alias_target_ene_st->game_flags |= cmd.low_game_flags;
if (ene_st->alias_target_ene_st && !(ene_st->alias_target_ene_st->game_flags & cmd.status_effect_flags)) {
ene_st->alias_target_ene_st->game_flags |= cmd.status_effect_flags;
l->log.info_f("Alias E-{:03X} updated to game_flags={:08X}",
ene_st->alias_target_ene_st->e_id, ene_st->alias_target_ene_st->game_flags);
}
@@ -4061,8 +4337,11 @@ static asio::awaitable<void> on_enemy_exp_request_bb(shared_ptr<Client> c, Subco
// If the requesting player never hit this enemy, they are probably cheating; ignore the command. Also, each player
// sends a 6xC8 if they ever hit the enemy; we only react to the first 6xC8 for each enemy (and give all relevant
// players EXP then, if they deserve it).
if (!ene_st->ever_hit_by_client_id(c->lobby_client_id) ||
(ene_st->server_flags & MapState::EnemyState::Flag::EXP_GIVEN)) {
if (!ene_st->ever_hit_by_client_id(c->lobby_client_id)) {
l->log.warning_f("The requesting player did not hit this enemy; ignoring request");
co_return;
}
if (ene_st->server_flags & MapState::EnemyState::Flag::EXP_GIVEN) {
l->log.info_f("EXP already given for this enemy; ignoring request");
co_return;
}
@@ -4248,9 +4527,6 @@ asio::awaitable<void> on_transfer_item_via_mail_message_bb(shared_ptr<Client> c,
if (!l->is_game()) {
throw runtime_error("6xCB command sent in non-game lobby");
}
if (!l->check_flag(Lobby::Flag::QUEST_IN_PROGRESS) && !l->check_flag(Lobby::Flag::JOINABLE_QUEST_IN_PROGRESS)) {
throw runtime_error("6xCB command sent during free play");
}
if (cmd.header.client_id != c->lobby_client_id) {
throw runtime_error("6xCB command sent by incorrect client");
}
@@ -4439,7 +4715,7 @@ static asio::awaitable<void> on_destroy_floor_item(shared_ptr<Client> c, Subcomm
static asio::awaitable<void> on_identify_item_bb(shared_ptr<Client> c, SubcommandMessage& msg) {
auto l = c->require_lobby();
if (!l->is_game()) {
throw runtime_error("6xDA command sent in non-game lobby");
throw runtime_error("6xB8 command sent in non-game lobby");
}
if (c->version() == Version::BB_V4) {
@@ -5377,10 +5653,8 @@ static asio::awaitable<void> on_upgrade_weapon_attribute_bb(shared_ptr<Client> c
if (payment_item.stack_size(*s->item_stack_limits(c->version())) < cmd.payment_count) {
throw runtime_error("not enough payment items present");
}
p->remove_item(payment_item.id, cmd.payment_count, *s->item_stack_limits(c->version()));
send_destroy_item_to_lobby(c, payment_item.id, cmd.payment_count);
uint8_t attribute_amount = 0;
int8_t attribute_amount = 0;
if (cmd.payment_type == 1 && cmd.payment_count == 1) {
attribute_amount = 30;
} else if (cmd.payment_type == 0 && cmd.payment_count == 4) {
@@ -5392,7 +5666,7 @@ static asio::awaitable<void> on_upgrade_weapon_attribute_bb(shared_ptr<Client> c
}
size_t attribute_index = 0;
for (size_t z = 6; z <= (item.has_kill_count() ? 10 : 8); z += 2) {
for (size_t z = 6; z <= (item.has_kill_count() ? 8 : 10); z += 2) {
if ((item.data1[z] == 0) || (!(item.data1[z] & 0x80) && (item.data1[z] == cmd.attribute))) {
attribute_index = z;
break;
@@ -5401,8 +5675,16 @@ static asio::awaitable<void> on_upgrade_weapon_attribute_bb(shared_ptr<Client> c
if (attribute_index == 0) {
throw runtime_error("no available attribute slots");
}
int8_t new_attr_value = static_cast<int8_t>(item.data1[attribute_index + 1]) + attribute_amount;
if (new_attr_value > 100) {
throw runtime_error("bonus value exceeds 100");
}
p->remove_item(payment_item.id, cmd.payment_count, *s->item_stack_limits(c->version()));
send_destroy_item_to_lobby(c, payment_item.id, cmd.payment_count);
item.data1[attribute_index] = cmd.attribute;
item.data1[attribute_index + 1] += attribute_amount;
item.data1[attribute_index + 1] += new_attr_value;
send_destroy_item_to_lobby(c, item.id, 1);
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
@@ -5585,7 +5867,7 @@ const vector<SubcommandDefinition> subcommand_definitions{
/* 6x99 */ {NONE, NONE, 0x99, on_forward_check_game},
/* 6x9A */ {NONE, NONE, 0x9A, on_forward_check_game_client},
/* 6x9B */ {NONE, NONE, 0x9B, on_forward_check_game},
/* 6x9C */ {NONE, NONE, 0x9C, on_set_enemy_low_game_flags_ultimate},
/* 6x9C */ {NONE, NONE, 0x9C, on_set_enemy_status_effect_flags_ultimate},
/* 6x9D */ {NONE, NONE, 0x9D, on_forward_check_game},
/* 6x9E */ {NONE, NONE, 0x9E, forward_subcommand_m},
/* 6x9F */ {NONE, NONE, 0x9F, forward_subcommand_with_entity_id_transcode_t<G_GalGryphonBossActions_6x9F>},
+6 -6
View File
@@ -23,7 +23,7 @@ struct DropReconcileResult {
// for drop computation (which may be the result of following an alias from the ref ene_st)
std::shared_ptr<MapState::EnemyState> ref_ene_st;
std::shared_ptr<MapState::EnemyState> target_ene_st;
uint8_t effective_rt_index;
EnemyType effective_enemy_type;
bool should_drop;
bool ignore_def;
};
@@ -52,7 +52,7 @@ public:
StatusEffectState temporary_status_effect;
StatusEffectState attack_status_effect;
StatusEffectState defense_status_effect;
StatusEffectState unused_status_effect;
StatusEffectState unknown_a1_status_effect; // De Rol Le uses this?
Language language = Language::JAPANESE;
uint32_t player_tag = 0;
uint32_t guild_card_number = 0;
@@ -62,8 +62,8 @@ public:
uint32_t death_flags = 0;
PlayerHoldState hold_state;
uint32_t area = 0;
uint32_t game_flags = 0;
bool game_flags_is_v3 = false;
uint32_t player_flags = 0;
bool player_flags_is_v3 = false;
parray<uint8_t, 0x14> technique_levels_v1 = 0xFF;
PlayerVisualConfig visual;
std::string name;
@@ -121,8 +121,8 @@ protected:
Parsed6x70Data(
const G_6x70_Base_V1& base, uint32_t guild_card_number, Version from_version, bool from_client_customization);
G_6x70_Base_V1 base_v1(bool is_v3) const;
static uint32_t convert_game_flags(uint32_t game_flags, bool to_v3);
uint32_t get_game_flags(bool is_v3) const;
static uint32_t convert_player_flags(uint32_t player_flags, bool to_v3);
uint32_t get_player_flags(bool is_v3) const;
};
bool validate_6xBB(G_SyncCardTradeServerState_Ep3_6xBB& cmd);
+3
View File
@@ -365,6 +365,9 @@ ReplaySession::ReplaySession(shared_ptr<ServerState> state, FILE* input_log, boo
if (line == "### use psov2 crypt") {
this->state->use_psov2_rand_crypt = true;
}
if (line == "### use legacy item random behavior") {
this->state->use_legacy_item_random_behavior = true;
}
if (line.starts_with("### cc ")) {
// ### cc $<chat command>
if (this->clients.size() != 1) {
+6 -6
View File
@@ -324,17 +324,17 @@ void PSOBBGuildCardFile::delete_duplicates() {
{
unordered_set<uint32_t> seen;
size_t read_index = 0, write_index = 0;
for (read_index = 0; read_index < this->blocked.size(); read_index++) {
const auto& read_blocked = this->blocked[read_index];
if (seen.emplace(read_blocked.guild_card_number).second) {
for (read_index = 0; read_index < this->blocked_senders.size(); read_index++) {
const auto& read_blocked_senders = this->blocked_senders[read_index];
if (seen.emplace(read_blocked_senders.guild_card_number).second) {
if (write_index != read_index) {
this->blocked[write_index] = read_blocked;
this->blocked_senders[write_index] = read_blocked_senders;
}
write_index++;
}
}
for (; write_index < this->blocked.size(); write_index++) {
this->blocked[write_index].clear();
for (; write_index < this->blocked_senders.size(); write_index++) {
this->blocked_senders[write_index].clear();
}
}
+1 -1
View File
@@ -994,7 +994,7 @@ struct PSOBBGuildCardFile {
} __packed_ws__(Entry, 0x1BC);
/* 0000 */ PSOBBMinimalSystemFile system_file;
/* 0114 */ parray<GuildCardBB, 0x1C> blocked;
/* 0114 */ parray<GuildCardBB, 0x1C> blocked_senders;
/* 1DF4 */ parray<uint8_t, 0x180> unknown_a2;
/* 1F74 */ parray<Entry, 0x69> entries;
/* D590 */
+103 -43
View File
@@ -1,4 +1,5 @@
#include "SendCommands.hh"
#include "BattleParamsIndex.hh"
#include <inttypes.h>
#include <stdarg.h>
@@ -690,6 +691,68 @@ void send_guild_card_chunk_bb(shared_ptr<Client> c, size_t chunk_index) {
send_command(c, 0x02DC, 0x00000000, &cmd, sizeof(cmd) - sizeof(cmd.data) + data_size);
}
static bool is_battle_param_stream_file_for_blueballz(const string& filename) {
return (filename == "BattleParamEntry.dat") ||
(filename == "BattleParamEntry_on.dat") ||
(filename == "BattleParamEntry_lab.dat") ||
(filename == "BattleParamEntry_lab_on.dat") ||
(filename == "BattleParamEntry_ep4.dat") ||
(filename == "BattleParamEntry_ep4_on.dat");
}
static shared_ptr<const string> bb_stream_file_data_for_client(shared_ptr<Client> c, const string& filename) {
auto s = c->require_server_state();
auto raw_data = s->bb_stream_files_cache->get_or_load("system/blueburst/" + filename).file->data;
int64_t effective_blueballz_hp_scale_tier = (c->selected_blueballz_tier >= 0)
? c->selected_blueballz_tier
: s->blueballz_enemy_hp_scale_tier;
if (!is_battle_param_stream_file_for_blueballz(filename) || (effective_blueballz_hp_scale_tier < 0)) {
return raw_data;
}
effective_blueballz_hp_scale_tier = std::min<int64_t>(
s->blueballz_max_tier,
effective_blueballz_hp_scale_tier);
string scaled_data = *raw_data;
if (scaled_data.size() < sizeof(BattleParamsIndex::Table)) {
c->log.warning_f("Blueballz enemy HP scaling skipped for {}; file is too small", filename);
return raw_data;
}
double mult = 1.0 + (static_cast<double>(effective_blueballz_hp_scale_tier) * 0.25);
auto* table = reinterpret_cast<BattleParamsIndex::Table*>(scaled_data.data());
size_t ultimate_index = static_cast<size_t>(Difficulty::ULTIMATE);
auto scale_u16 = [mult](uint32_t v) -> uint16_t {
if (v == 0) {
return 0;
}
uint32_t scaled = static_cast<uint32_t>(static_cast<double>(v) * mult);
if (scaled < 1) {
scaled = 1;
}
if (scaled > 0xFFFF) {
scaled = 0xFFFF;
}
return static_cast<uint16_t>(scaled);
};
for (size_t z = 0; z < 0x60; z++) {
auto& stats = table->stats[ultimate_index][z];
stats.char_stats.hp = scale_u16(stats.char_stats.hp);
}
c->log.info_f("Blueballz enemy HP scaling: serving {} with tier {} ({:g}x Ultimate HP)",
filename, effective_blueballz_hp_scale_tier, mult);
return make_shared<string>(std::move(scaled_data));
}
static const vector<string> stream_file_entries = {
"ItemMagEdit.prs",
"ItemPMT.prs",
@@ -705,24 +768,15 @@ static const vector<string> stream_file_entries = {
void send_stream_file_index_bb(shared_ptr<Client> c) {
auto s = c->require_server_state();
c->log.info_f("PSO Peeps BBZ stream debug: send_stream_file_index_bb called");
vector<S_StreamFileIndexEntry_BB_01EB> entries;
size_t offset = 0;
for (const string& filename : stream_file_entries) {
string key = "system/blueburst/" + filename;
auto cache_res = s->bb_stream_files_cache->get_or_load(key);
auto file_data = bb_stream_file_data_for_client(c, filename);
auto& e = entries.emplace_back();
e.size = cache_res.file->data->size();
// Computing the checksum can be slow, so we cache it along with the file data. If the cache result was just
// populated, then it may be different, so we always recompute the checksum in that case.
if (cache_res.generate_called) {
e.checksum = crc32(cache_res.file->data->data(), e.size);
s->bb_stream_files_cache->replace_obj<uint32_t>(key + ".crc32", e.checksum);
} else {
auto compute_checksum = [&](const string&) -> uint32_t {
return crc32(cache_res.file->data->data(), e.size);
};
e.checksum = s->bb_stream_files_cache->get_obj<uint32_t>(key + ".crc32", compute_checksum).obj;
}
e.size = file_data->size();
e.checksum = crc32(file_data->data(), e.size);
e.offset = offset;
e.filename.encode(filename);
offset += e.size;
@@ -733,30 +787,27 @@ void send_stream_file_index_bb(shared_ptr<Client> c) {
void send_stream_file_chunk_bb(shared_ptr<Client> c, uint32_t chunk_index) {
auto s = c->require_server_state();
auto cache_result = s->bb_stream_files_cache->get(
"<BB stream file>", [&](const string&) -> string {
size_t bytes = 0;
for (const auto& name : stream_file_entries) {
bytes += s->bb_stream_files_cache->get_or_load("system/blueburst/" + name).file->data->size();
}
c->log.info_f("PSO Peeps BBZ stream debug: send_stream_file_chunk_bb called for chunk {}", chunk_index);
string ret;
ret.reserve(bytes);
for (const auto& name : stream_file_entries) {
ret += *s->bb_stream_files_cache->get_or_load("system/blueburst/" + name).file->data;
}
return ret;
});
const auto& contents = cache_result.file->data;
size_t total_bytes = 0;
for (const auto& name : stream_file_entries) {
total_bytes += bb_stream_file_data_for_client(c, name)->size();
}
string contents;
contents.reserve(total_bytes);
for (const auto& name : stream_file_entries) {
contents += *bb_stream_file_data_for_client(c, name);
}
S_StreamFileChunk_BB_02EB chunk_cmd;
chunk_cmd.chunk_index = chunk_index;
size_t offset = sizeof(chunk_cmd.data) * chunk_index;
if (offset > contents->size()) {
if (offset > contents.size()) {
throw runtime_error("client requested chunk beyond end of stream file");
}
size_t bytes = min<size_t>(contents->size() - offset, sizeof(chunk_cmd.data));
chunk_cmd.data.assign_range(reinterpret_cast<const uint8_t*>(contents->data() + offset), bytes, 0);
size_t bytes = min<size_t>(contents.size() - offset, sizeof(chunk_cmd.data));
chunk_cmd.data.assign_range(reinterpret_cast<const uint8_t*>(contents.data() + offset), bytes, 0);
size_t cmd_size = offsetof(S_StreamFileChunk_BB_02EB, data) + bytes;
cmd_size = (cmd_size + 3) & ~3;
@@ -1139,16 +1190,25 @@ void send_simple_mail(shared_ptr<ServerState> s, uint32_t from_guild_card_number
template <TextEncoding NameEncoding, TextEncoding MessageEncoding>
void send_info_board_t(shared_ptr<Client> c) {
vector<S_InfoBoardEntryT_D8<NameEncoding, MessageEncoding>> entries;
auto l = c->require_lobby();
for (const auto& other_c : l->clients) {
if (!other_c.get()) {
continue;
auto add_clients_from_lobby = [&](std::shared_ptr<Lobby> l) -> void {
for (const auto& lc : l->clients) {
if (lc) {
auto lp = lc->character_file(true, false);
auto& e = entries.emplace_back();
e.name.encode(lp->disp.name.decode(lp->inventory.language), c->language());
e.message.encode(add_color(lp->info_board.decode(lp->inventory.language)), c->language());
}
}
auto other_p = other_c->character_file(true, false);
auto& e = entries.emplace_back();
e.name.encode(other_p->disp.name.decode(other_p->inventory.language), c->language());
e.message.encode(add_color(other_p->info_board.decode(other_p->inventory.language)), c->language());
};
auto l = c->require_lobby();
auto watched_l = l->watched_lobby.lock();
if (watched_l) {
add_clients_from_lobby(watched_l);
}
add_clients_from_lobby(l);
send_command_vt(c, 0xD8, entries.size(), entries);
}
@@ -2654,16 +2714,16 @@ asio::awaitable<void> send_change_player_hp(
}
asio::awaitable<void> send_remove_negative_conditions(shared_ptr<Client> c) {
G_AddStatusEffect_6x0C cmd;
cmd.header = {0x0C, sizeof(G_AddStatusEffect_6x0C) >> 2, c->lobby_client_id};
G_AddOrRemoveStatusEffect_6x0C_6x0D cmd;
cmd.header = {0x0C, sizeof(G_AddOrRemoveStatusEffect_6x0C_6x0D) >> 2, c->lobby_client_id};
cmd.effect_type = 7; // Healing ring
cmd.amount = 0;
co_await send_protected_command(c, &cmd, sizeof(cmd), true);
}
void send_remove_negative_conditions(std::shared_ptr<Channel> ch, uint16_t client_id) {
G_AddStatusEffect_6x0C cmd;
cmd.header = {0x0C, sizeof(G_AddStatusEffect_6x0C) >> 2, client_id};
G_AddOrRemoveStatusEffect_6x0C_6x0D cmd;
cmd.header = {0x0C, sizeof(G_AddOrRemoveStatusEffect_6x0C_6x0D) >> 2, client_id};
cmd.effect_type = 7; // Healing ring
cmd.amount = 0;
ch->send(0x60, 0x00, &cmd, sizeof(cmd));
+32 -5
View File
@@ -73,10 +73,11 @@ ServerState::QuestF960Result::QuestF960Result(
}
}
ServerState::ServerState(const string& config_filename)
ServerState::ServerState(const string& config_filename, bool is_replay)
: creation_time(phosg::now()),
io_context(make_shared<asio::io_context>(1)),
config_filename(config_filename),
is_replay(is_replay),
thread_pool(make_unique<asio::thread_pool>()),
bb_stream_files_cache(new FileContentsCache(3600000000ULL)),
bb_system_cache(new FileContentsCache(3600000000ULL)),
@@ -259,7 +260,7 @@ shared_ptr<Client> ServerState::find_client(const string* identifier, uint64_t a
for (auto& other_l : this->all_lobbies()) {
if (l == other_l) {
continue; // don't bother looking again
continue; // Don't bother looking again
}
try {
return other_l->find_client(identifier, account_id);
@@ -319,7 +320,7 @@ uint16_t ServerState::game_server_port_for_version(Version v) const {
case Version::XB_V3:
return this->name_to_port_config.at("xb")->port;
case Version::BB_V4:
return this->name_to_port_config.at("xb")->port;
return this->name_to_port_config.at("bb-data1")->port;
default:
throw runtime_error("unknown version");
}
@@ -505,7 +506,12 @@ ItemData ServerState::parse_item_description(Version version, const string& desc
shared_ptr<const CommonItemSet> ServerState::common_item_set(Version logic_version, shared_ptr<const Quest> q) const {
if (q && !q->meta.common_item_set_name.empty()) {
return this->common_item_sets.at(q->meta.common_item_set_name);
try {
return this->common_item_sets.at(q->meta.common_item_set_name);
} catch (const std::out_of_range&) {
throw runtime_error(std::format("common item set {} for quest {} does not exist",
q->meta.common_item_set_name, q->meta.name));
}
} else if (is_v1_or_v2(logic_version) && (logic_version != Version::GC_NTE)) {
// TODO: We should probably have a v1 common item set at some point too
return this->common_item_sets.at("common-table-v1-v2");
@@ -519,7 +525,12 @@ shared_ptr<const CommonItemSet> ServerState::common_item_set(Version logic_versi
shared_ptr<const RareItemSet> ServerState::rare_item_set(Version logic_version, shared_ptr<const Quest> q) const {
if (q && !q->meta.rare_item_set_name.empty()) {
return this->rare_item_sets.at(q->meta.rare_item_set_name);
try {
return this->rare_item_sets.at(q->meta.rare_item_set_name);
} catch (const std::out_of_range&) {
throw runtime_error(std::format("rare item set {} for quest {} does not exist",
q->meta.rare_item_set_name, q->meta.name));
}
} else if (is_v1(logic_version)) {
return this->rare_item_sets.at("rare-table-v1");
} else if (is_v2(logic_version) && (logic_version != Version::GC_NTE)) {
@@ -904,6 +915,22 @@ void ServerState::load_config_early() {
this->cheat_mode_behavior = parse_behavior_switch("CheatModeBehavior", BehaviorSwitch::OFF_BY_DEFAULT);
this->default_switch_assist_enabled = this->config_json->get_bool("EnableSwitchAssistByDefault", false);
this->use_game_creator_section_id = this->config_json->get_bool("UseGameCreatorSectionID", false);
this->enable_bb_ship_selection_menu = this->config_json->get_bool("EnableBBShipSelectionMenu", false);
this->enable_blueballz = this->config_json->get_bool("EnableBlueballz", false);
this->enable_hardcore_mode = this->config_json->get_bool("EnableHardcoreMode", false);
this->blueballz_max_tier = std::min<int64_t>(10, std::max<int64_t>(0, this->config_json->get_int("BlueballzMaxTier", 10)));
this->blueballz_unlocked_tier_v2 = std::min<int64_t>(
this->blueballz_max_tier,
std::max<int64_t>(-1, this->config_json->get_int("BlueballzUnlockedTierV2", -1)));
this->blueballz_unlocked_tier_v3 = std::min<int64_t>(
this->blueballz_max_tier,
std::max<int64_t>(-1, this->config_json->get_int("BlueballzUnlockedTierV3", -1)));
this->blueballz_unlocked_tier_v4 = std::min<int64_t>(
this->blueballz_max_tier,
std::max<int64_t>(-1, this->config_json->get_int("BlueballzUnlockedTierV4", -1)));
this->blueballz_enemy_hp_scale_tier = std::min<int64_t>(
this->blueballz_max_tier,
std::max<int64_t>(-1, this->config_json->get_int("BlueballzEnemyHPScaleTier", -1)));
this->rare_notifs_enabled_for_client_drops = this->config_json->get_bool("RareNotificationsEnabledForClientDrops", false);
this->default_rare_notifs_enabled_v1_v2 = this->config_json->get_bool("RareNotificationsEnabledByDefault", false);
this->default_rare_notifs_enabled_v3_v4 = this->default_rare_notifs_enabled_v1_v2;
+11 -1
View File
@@ -91,6 +91,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<const phosg::JSON> config_json;
bool one_time_config_loaded = false;
bool default_lobbies_created = false;
bool is_replay = false;
size_t num_worker_threads = 0;
std::unique_ptr<asio::thread_pool> thread_pool;
@@ -155,7 +156,16 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
BehaviorSwitch cheat_mode_behavior = BehaviorSwitch::OFF_BY_DEFAULT;
bool default_switch_assist_enabled = false;
bool use_game_creator_section_id = false;
bool enable_bb_ship_selection_menu = false;
bool use_psov2_rand_crypt = false; // Used in some tests
bool enable_blueballz = false;
int64_t blueballz_enemy_hp_scale_tier = -1; // -1 = disabled; 0..10 = scale BB enemy HP in stream files
bool enable_hardcore_mode = false;
int8_t blueballz_max_tier = 10;
int8_t blueballz_unlocked_tier_v2 = 0;
int8_t blueballz_unlocked_tier_v3 = 0;
int8_t blueballz_unlocked_tier_v4 = 0;
bool use_legacy_item_random_behavior = false; // Used in some tests
bool rare_notifs_enabled_for_client_drops = false;
bool default_rare_notifs_enabled_v1_v2 = false;
bool default_rare_notifs_enabled_v3_v4 = false;
@@ -308,7 +318,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::unordered_map<uint32_t, ProxySession::PersistentConfig> proxy_persistent_configs;
explicit ServerState(const std::string& config_filename = "");
explicit ServerState(const std::string& config_filename = "", bool is_replay = false);
ServerState(const ServerState&) = delete;
ServerState(ServerState&&) = delete;
ServerState& operator=(const ServerState&) = delete;
+12 -2
View File
@@ -293,8 +293,18 @@ uint8_t npc_for_name(const string& name, Version version) {
const char* name_for_char_class(uint8_t cls) {
static const array<const char*, 12> names = {
"HUmar", "HUnewearl", "HUcast", "RAmar", "RAcast", "RAcaseal", "FOmarl", "FOnewm", "FOnewearl", "HUcaseal",
"FOmar", "RAmarl"};
/* 00 */ "HUmar", // 0
/* 01 */ "HUnewearl", // 0
/* 02 */ "HUcast", // 1
/* 03 */ "RAmar", // 0
/* 04 */ "RAcast", // 2
/* 05 */ "RAcaseal", // 1
/* 06 */ "FOmarl", // 0
/* 07 */ "FOnewm", // 0
/* 08 */ "FOnewearl", // 0
/* 09 */ "HUcaseal", // 1
/* 0A */ "FOmar", // 0
/* 0B */ "RAmarl"}; // 0
try {
return names.at(cls);
} catch (const out_of_range&) {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 560 B

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -18,6 +18,7 @@ start:
# Tiny Grass Assassins Bug Fix
.data <VERS 0x0016227A 0x0016238A 0x0016232A 0x0016240A 0x0016229A 0x0016242A 0x0016225A>
.data 0x00000002
.binary EB0E
@@ -25,6 +26,7 @@ start:
# Shield DFP/EVP Bug Fix (allows shields to reach true max DFP/EVP values)
.data <VERS 0x00185D8E 0x00185F4E 0x0018600E 0x00185F0E 0x00185F6E 0x00185F2E 0x00185F2E>
.data 0x00000001
.binary 16
@@ -35,6 +37,7 @@ start:
# VR Spaceship Item Drop Bug Fix (allows items to drop from enemies above a certain Y position)
.data <VERS 0x00175D75 0x00175E55 0x00175F35 0x00175EC5 0x00175ED5 0x00175EE5 0x00175E95>
.data 0x00000002
.data 0x435C0000
@@ -42,6 +45,7 @@ start:
# Gol Dragon Camera Bug Fix (makes the camera after Gol Dragon display "normally")
.data <VERS 0x000A8AE1 0x000A8C51 0x000A8BD1 0x000A89C1 0x000A8961 0x000A89E1 0x000A8921>
.data 0x00000002
.binary 01
@@ -49,6 +53,7 @@ start:
# Rain Drops Color Bug Fix
.data <VERS 0x0054D670 0x0054DD00 0x005557E8 0x00552C68 0x00552508 0x00552C68 0x00553008>
.data 0x00000008
.binary 7080808060707070
@@ -56,6 +61,7 @@ start:
# TP Bar Color Bug Fix
.data <VERS 0x002779CE 0x00277C7E 0x0027808E 0x00277DAE 0x00277ECE 0x00277DCE 0x00277F9E>
.data 0x00000004
.data 0xFF00AAFA
@@ -71,13 +77,116 @@ start:
# Olga Flow Barta Bug Fix
.label g1_hook_call, <VERS 0x000970E0 0x000973F0 0x00097460 0x00097140 0x000970E0 0x00097160 0x00096FE0>
.label g1_hook_loc, <VERS 0x00097124 0x00097434 0x000974A4 0x00097184 0x00097124 0x000971A4 0x00097024>
.data g1_hook_call
.data 6
.address g1_hook_call
mov eax, esi
cmp al, 19
jmp g1_hook_loc
g1_hook_call_end:
.data g1_hook_loc
.deltaof g1_hook_start, g1_hook_end
.address g1_hook_loc
g1_hook_start:
jne g1_hook_skip_replace_value
mov al, 2
g1_hook_skip_replace_value:
cmp eax, [ebx + 0x440] // Original opcode
jmp g1_hook_call_end
g1_hook_end:
# Morfos Frozen Player Bug Fix
.label g2_hook_call, <VERS 0x0012E257 0x0012E387 0x0012E4E7 0x0012E537 0x0012E567 0x0012E557 0x0012E5A7>
.label g2_hook_loc1, <VERS 0x0012E5F4 0x0012E724 0x0012E884 0x0012E8D4 0x0012E904 0x0012E8F4 0x0012E944>
.label g2_hook_loc2, <VERS 0x0012E622 0x0012E752 0x0012E8B2 0x0012E902 0x0012E932 0x0012E922 0x0012E972>
.data g2_hook_call
.data 6
.address g2_hook_call
call g2_hook_loc1
nop
.data g2_hook_loc1
.deltaof g2_hook_start1, g2_hook_end1
.address g2_hook_loc1
g2_hook_start1:
fld1 st0 // st = [1.0, speed]
fld1 st0 // st = [1.0, 1.0, speed]
fadd st0, st1 // st = [2.0, 1.0, speed]
fdivp st1, st0 // st = [0.5, speed]
jmp g2_hook_loc2
g2_hook_end1:
.data g2_hook_loc2
.deltaof g2_hook_start2, g2_hook_end2
.address g2_hook_loc2
g2_hook_start2:
test byte [esi + 0x30], 0x20 // If not set, use 1.5; if set, use 0.5
jnz g2_hook_entity_is_frozen
fld1 st0 // st = [1, 0.5, speed]
faddp st1, st0 // st = [1.5, speed]
g2_hook_entity_is_frozen:
fmulp st1, st0 // st = [((game_flags & 0x20) ? 0.5 : 1.5) * speed]
ret
g2_hook_end2:
# Dropped Mag Color Bug Fix (only needed on beta version)
.only_versions 4OJB
.data 0x001759E6
.data 1
.binary 12
.data 0x00180898
.data 1
.binary 12
.all_versions
# Box/Fence Fadeout Bug Fix
.data <VERS 0x001D229B 0x001D244B 0x001D295B 0x001D241B 0x001D26AB 0x001D243B 0x001D26DB>
.data 2
nop
nop
.data <VERS 0x001DF7C4 0x001DF924 0x001DFD94 0x001DF964 0x001DFB04 0x001DF984 0x001DFA74>
.data 6
nop
nop
nop
nop
nop
nop
# TODO: Port the rest of the patches in the GC version of BugFixes:
# Olga Flow Barta Bug Fix
# Morfos Frozen Player Bug Fix
# Bulclaw HP Bug Fix
# Control Tower: Delbiter Death SFX Bug Fix
# Weapon Attributes Patch
# Invalid Items Bug Fix
# Item Removal Maxed Stats Bug Fix
# Unit Present Bug Fix
# Bank Item Stacking Bug Fix
# Meseta Drop System Bug Fix
# Offline Quests Drop Table Bug Fix
# Mag Revival Priority Bug Fix
# Mag Revival Challenge & Quest Mode Bug Fix
# Reverser Target Lock Bug Fix
# Deband/Shifta/Resta Target Bug Fix
# Tech Auto Targeting Bug Fix
# Enable Trap Animations
# Tsumikiri J-Sword special attack + rapid weapon switch bug fix
# Control Tower: Delbiter Death SFX Bug Fix
# Ruins Laser Fence SFX Bug Fix
# SFX Cancellation Distance Bug Fix
# Foie SFX Pitch Bug Fix
@@ -92,28 +201,12 @@ start:
# Grants SFX Pitch Bug Fix
# Megid SFX Pitch Bug Fix
# Anti SFX Pitch Bug Fix
# Invalid Items Bug Fix
# Item Removal Maxed Stats Bug Fix
# Unit Present Bug Fix
# Bank Item Stacking Bug Fix
# Dropped Mag Color Bug Fix
# Meseta Drop System Bug Fix
# Present Color Bug Fix
# Offline Quests Drop Table Bug Fix
# Mag Revival Priority Bug Fix
# Mag Revival Challenge & Quest Mode Bug Fix
# Chat Bubble Window TAB Bug Fix
# Chat Log Window LF/Tab Bug Fix
# Dark/Hell Special GFX Bug Fix
# Box/Fence Fadeout Bug Fix
# Devil's and Demon's Special Damage Display Bug Fix
# Christmas Trees Bug Fix
# Reverser Target Lock Bug Fix
# Deband/Shifta/Resta Target Bug Fix
# Tech Auto Targeting Bug Fix
# Enable Trap Animations
# Tsumikiri J-Sword special attack + rapid weapon switch bug fix
.data 0x00000000
@@ -0,0 +1,106 @@
.meta hide_from_patches_menu
.meta name="Player flags"
.meta description=""
entry_ptr:
reloc0:
.offsetof start
start:
.include WriteCodeBlocksGC
.label check_controller_button, 0x801A6C68 # [std](ControllerState* st, uint32_t flags) -> bool
.label TFogCtrl_change_fog, 0x800FB10C # [std](TFogCtrl* this, uint32_t fog_num, uint32_t instant_transition) -> void
.label render_debug_printf, 0x803D4E3C # [std](uint32_t coords, const char* fmt, ...) -> void
.label set_debug_text_color, 0x803D4990 # [](uint32_t color_argb) -> void
.label hook_call, 0x80228A38
.label hook_loc, 0x8000A000
# Disable D-pad up + down chat shortcuts
.data 0x80246314
.data 4
li r4, 0
.data 0x80246330
.data 4
li r4, 0
.data hook_call
.data 4
.address hook_call
b hook_loc
.data hook_loc
.deltaof hook_start, hook_end
.address hook_loc
hook_start:
mflr r0
stwu [r1 - 0x20], r1
stw [r1 + 0x24], r0
stw [r1 + 0x08], r30
stw [r1 + 0x0C], r31
lis r30, 0x804F
ori r30, r30, 0xC7A8
lwz r4, [r13 - 0x5280] # local_client_id
rlwinm r4, r4, 2, 0, 29
lwzx r30, [r30 + r4] # r30 = TFogCtrl_for_client_id[local_client_id]
cmpwi r30, 0
beq hook_skip_all
lwz r31, [r30 + 0x0184] # Active slot number
rlwinm r31, r31, 2, 0, 29
addi r31, r31, 0x174
lwzx r31, [r30 + r31] # Active slot pointer
lwz r31, [r31 + 0x40] # Active fog number
# Check for button presses to change fog
lis r3, 0x8050
ori r3, r3, 0x9848
li r4, 0x0010 # D-pad up
bl check_controller_button
cmplwi r3, 0
beq hook_skip_incr_fog
mr r3, r30
addi r4, r31, 1
andi. r4, r4, 0x007F
li r5, 1
bl TFogCtrl_change_fog
hook_skip_incr_fog:
lis r3, 0x8050
ori r3, r3, 0x9848
li r4, 0x0020 # D-pad down
bl check_controller_button
cmplwi r3, 0
beq hook_skip_decr_fog
mr r3, r30
subi r4, r31, 1
andi. r4, r4, 0x007F
li r5, 1
bl TFogCtrl_change_fog
hook_skip_decr_fog:
# Show the current fog number
lis r3, 0xFFFF
ori r3, r3, 0x00FF
bl set_debug_text_color
lis r3, 0x0002
ori r3, r3, 0x000B
bl hook_get_fmt_string
.binary "Fog: %02X"000000
hook_get_fmt_string:
mflr r4
mr r5, r31
bl render_debug_printf
hook_skip_all:
lwz r31, [r1 + 0x0C]
lwz r30, [r1 + 0x08]
lwz r0, [r1 + 0x24]
addi r1, r1, 0x20
mtlr r0
blr
hook_end:
.data 0
.data 0
@@ -0,0 +1,74 @@
.meta hide_from_patches_menu
.meta name="Player flags"
.meta description=""
entry_ptr:
reloc0:
.offsetof start
start:
.include WriteCodeBlocksGC
.label TObjPlayer_for_client_id, 0x801BA59C # [std](uint32_t client_id)
.label render_debug_printf, 0x803D4E3C # [std](uint32_t coords, const char* fmt, ...);
.label set_debug_text_color, 0x803D4990 # [](uint32_t color_argb);
.label hook_call, 0x80228A38
.label hook_loc, 0x8000A000
.data hook_call
.data 4
.address hook_call
b hook_loc
.data hook_loc
.deltaof hook_start, hook_end
.address hook_loc
hook_start:
mflr r0
stwu [r1 - 0x20], r1
stw [r1 + 0x24], r0
stw [r1 + 8], r30
li r30, 0
hook_again:
li r6, 0
mr r3, r30
bl TObjPlayer_for_client_id
cmplwi r3, 0
beq hook_skip_player
lwz r6, [r3 + 0x0334] # player_flags
lwz r4, [r13 - 0x5280] # local_client_id
cmp r4, r30
bne hook_not_local_player
lis r3, 0xFFFF
ori r3, r3, 0x00FF
b hook_player_flags_ok
hook_not_local_player:
lis r3, 0xFFFF
ori r3, r3, 0xFFFF
hook_player_flags_ok:
bl set_debug_text_color
lis r3, 0x0002
ori r3, r3, 0x000B
add r3, r3, r30
bl hook_get_fmt_string
.binary "Player %2d: %08X"00000000
hook_get_fmt_string:
mflr r4
mr r5, r30
bl render_debug_printf
hook_skip_player:
addi r30, r30, 1
cmplwi r30, 0x0C
blt hook_again
lwz r30, [r1 + 8]
lwz r0, [r1 + 0x24]
addi r1, r1, 0x20
mtlr r0
blr
hook_end:
.data 0
.data 0
-1
View File
@@ -1 +0,0 @@
ItemCT-pc-v2.afs
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
ItemPT-pc-v2.afs
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
ItemPT-pc-v2.afs
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
ItemRT-pc-v2.afs
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
ItemCT-pc-v2.afs
+30 -158
View File
@@ -122,8 +122,6 @@
"CHAOS_SORCERER": [145, 200],
"CLAW": [95, 145],
"DARK_BELRA": [50, 80],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [100, 122],
"DEATH_GUNNER": [100, 122],
"DELSABER": [150, 165],
@@ -143,7 +141,6 @@
"HILDEBLUE": [1000, 1500],
"LA_DIMENIAN": [115, 165],
"MIGIUM": [50, 85],
"MONEST": 0,
"MOTHMANT": [80, 90],
"NANO_DRAGON": [50, 170],
"NAR_LILY": [1000, 2000],
@@ -159,7 +156,7 @@
"SO_DIMENIAN": [135, 180],
"VOL_OPT_2": 2000
},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 28, "BULCLAW": 40, "BULK": 30, "CANADINE": 31, "CANADINE_GROUP": 31, "CANANE": 31, "CHAOS_BRINGER": 85, "CHAOS_SORCERER": 40, "CLAW": 28, "DARK_BELRA": 40, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 30, "DEATH_GUNNER": 30, "DELSABER": 35, "DE_ROL_LE": 100, "DIMENIAN": 28, "DRAGON": 100, "DUBCHIC": 28, "EVIL_SHARK": 28, "GARANZ": 80, "GIGOBOOMA": 35, "GILLCHIC": 28, "GOBOOMA": 32, "GRASS_ASSASSIN": 50, "GUIL_SHARK": 15, "HIDOOM": 40, "HILDEBEAR": 80, "HILDEBLUE": 100, "LA_DIMENIAN": 31, "MIGIUM": 40, "MONEST": 0, "MOTHMANT": 25, "NANO_DRAGON": 50, "NAR_LILY": 100, "PAL_SHARK": 32, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 50, "SINOW_GOLD": 50, "SO_DIMENIAN": 33, "VOL_OPT_2": 100},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 28, "BULCLAW": 40, "BULK": 30, "CANADINE": 31, "CANADINE_GROUP": 31, "CANANE": 31, "CHAOS_BRINGER": 85, "CHAOS_SORCERER": 40, "CLAW": 28, "DARK_BELRA": 40, "DARK_GUNNER": 30, "DEATH_GUNNER": 30, "DELSABER": 35, "DE_ROL_LE": 100, "DIMENIAN": 28, "DRAGON": 100, "DUBCHIC": 28, "EVIL_SHARK": 28, "GARANZ": 80, "GIGOBOOMA": 35, "GILLCHIC": 28, "GOBOOMA": 32, "GRASS_ASSASSIN": 50, "GUIL_SHARK": 15, "HIDOOM": 40, "HILDEBEAR": 80, "HILDEBLUE": 100, "LA_DIMENIAN": 31, "MIGIUM": 40, "MOTHMANT": 25, "NANO_DRAGON": 50, "NAR_LILY": 100, "PAL_SHARK": 32, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 50, "SINOW_GOLD": 50, "SO_DIMENIAN": 33, "VOL_OPT_2": 100},
"GrindProbTable": [
[100, 30, 25, 20],
[0, 30, 23, 15],
@@ -485,8 +482,6 @@
"CHAOS_SORCERER": [145, 200],
"CLAW": [95, 145],
"DARK_BELRA": [50, 80],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [100, 122],
"DEATH_GUNNER": [100, 122],
"DELSABER": [150, 165],
@@ -506,7 +501,6 @@
"HILDEBLUE": [1000, 1500],
"LA_DIMENIAN": [115, 165],
"MIGIUM": [50, 85],
"MONEST": 0,
"MOTHMANT": [80, 90],
"NANO_DRAGON": [50, 170],
"NAR_LILY": [1000, 2000],
@@ -579,8 +573,6 @@
"CHAOS_SORCERER": [145, 210],
"CLAW": [95, 155],
"DARK_BELRA": [50, 90],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [100, 132],
"DEATH_GUNNER": [100, 132],
"DELSABER": [150, 175],
@@ -600,7 +592,6 @@
"HILDEBLUE": [1000, 1500],
"LA_DIMENIAN": [115, 175],
"MIGIUM": [50, 95],
"MONEST": 0,
"MOTHMANT": [80, 100],
"NANO_DRAGON": [50, 180],
"NAR_LILY": [1000, 2000],
@@ -748,8 +739,6 @@
"CHAOS_SORCERER": [115, 135],
"CLAW": [45, 75],
"DARK_BELRA": 10,
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [110, 125],
"DEATH_GUNNER": [110, 125],
"DELSABER": [95, 100],
@@ -769,7 +758,6 @@
"HILDEBLUE": [500, 1000],
"LA_DIMENIAN": [100, 120],
"MIGIUM": [15, 20],
"MONEST": 0,
"MOTHMANT": [3, 6],
"NANO_DRAGON": [60, 70],
"NAR_LILY": [500, 1200],
@@ -847,8 +835,6 @@
"CHAOS_SORCERER": [120, 135],
"CLAW": [50, 75],
"DARK_BELRA": 10,
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [105, 130],
"DEATH_GUNNER": [105, 130],
"DELSABER": [95, 100],
@@ -868,7 +854,6 @@
"HILDEBLUE": [500, 1000],
"LA_DIMENIAN": [105, 120],
"MIGIUM": [17, 20],
"MONEST": 0,
"MOTHMANT": [5, 6],
"NANO_DRAGON": [65, 70],
"NAR_LILY": [500, 1200],
@@ -922,8 +907,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -943,7 +926,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -1357,8 +1339,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -1378,7 +1358,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -1441,8 +1420,6 @@
"CHAOS_SORCERER": [200, 425],
"CLAW": [155, 335],
"DARK_BELRA": [100, 160],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 285],
"DEATH_GUNNER": [150, 275],
"DELSABER": [150, 330],
@@ -1462,7 +1439,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 165],
"MONEST": 0,
"MOTHMANT": [120, 170],
"NANO_DRAGON": [80, 340],
"NAR_LILY": [2000, 2500],
@@ -1499,8 +1475,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -1520,7 +1494,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -1602,7 +1575,7 @@
[56, 140],
[58, 145]
],
"EnemyItemClasses": {"AL_RAPPY": 2, "BARBAROUS_WOLF": 2, "BOOMA": 0, "BULCLAW": 3, "BULK": 0, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 1, "CHAOS_BRINGER": 3, "CHAOS_SORCERER": 3, "CLAW": 0, "DARK_BELRA": 1, "DARK_FALZ_2": 255, "DARK_FALZ_3": 255, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 1, "DE_ROL_LE": 255, "DIMENIAN": 0, "DRAGON": 255, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 2, "GIGOBOOMA": 3, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 3, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 3, "MONEST": 255, "MOTHMANT": 0, "NANO_DRAGON": 2, "NAR_LILY": 3, "PAL_SHARK": 0, "PAN_ARMS": 2, "POFUILLY_SLIME": 1, "POISON_LILY": 1, "POUILLY_SLIME": 3, "RAG_RAPPY": 2, "SAVAGE_WOLF": 1, "SINOW_BEAT": 0, "SINOW_GOLD": 3, "SO_DIMENIAN": 1, "VOL_OPT_2": 255},
"EnemyItemClasses": {"AL_RAPPY": 2, "BARBAROUS_WOLF": 2, "BOOMA": 0, "BULCLAW": 3, "BULK": 0, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 1, "CHAOS_BRINGER": 3, "CHAOS_SORCERER": 3, "CLAW": 0, "DARK_BELRA": 1, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 1, "DIMENIAN": 0, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 2, "GIGOBOOMA": 3, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 3, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 3, "MOTHMANT": 0, "NANO_DRAGON": 2, "NAR_LILY": 3, "PAL_SHARK": 0, "PAN_ARMS": 2, "POFUILLY_SLIME": 1, "POISON_LILY": 1, "POUILLY_SLIME": 3, "RAG_RAPPY": 2, "SAVAGE_WOLF": 1, "SINOW_BEAT": 0, "SINOW_GOLD": 3, "SO_DIMENIAN": 1},
"EnemyMesetaRanges": {
"AL_RAPPY": [1500, 2000],
"BARBAROUS_WOLF": [120, 164],
@@ -1616,8 +1589,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -1637,7 +1608,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -1653,7 +1623,7 @@
"SO_DIMENIAN": [185, 295],
"VOL_OPT_2": 3500
},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 30, "BULCLAW": 45, "BULK": 35, "CANADINE": 35, "CANADINE_GROUP": 35, "CANANE": 35, "CHAOS_BRINGER": 88, "CHAOS_SORCERER": 35, "CLAW": 30, "DARK_BELRA": 45, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 45, "DEATH_GUNNER": 45, "DELSABER": 45, "DE_ROL_LE": 100, "DIMENIAN": 30, "DRAGON": 100, "DUBCHIC": 30, "EVIL_SHARK": 30, "GARANZ": 85, "GIGOBOOMA": 35, "GILLCHIC": 30, "GOBOOMA": 38, "GRASS_ASSASSIN": 55, "GUIL_SHARK": 15, "HIDOOM": 45, "HILDEBEAR": 85, "HILDEBLUE": 100, "LA_DIMENIAN": 35, "MIGIUM": 45, "MONEST": 0, "MOTHMANT": 25, "NANO_DRAGON": 55, "NAR_LILY": 100, "PAL_SHARK": 35, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 60, "SINOW_GOLD": 55, "SO_DIMENIAN": 35, "VOL_OPT_2": 100},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 30, "BULCLAW": 45, "BULK": 35, "CANADINE": 35, "CANADINE_GROUP": 35, "CANANE": 35, "CHAOS_BRINGER": 88, "CHAOS_SORCERER": 35, "CLAW": 30, "DARK_BELRA": 45, "DARK_GUNNER": 45, "DEATH_GUNNER": 45, "DELSABER": 45, "DE_ROL_LE": 100, "DIMENIAN": 30, "DRAGON": 100, "DUBCHIC": 30, "EVIL_SHARK": 30, "GARANZ": 85, "GIGOBOOMA": 35, "GILLCHIC": 30, "GOBOOMA": 38, "GRASS_ASSASSIN": 55, "GUIL_SHARK": 15, "HIDOOM": 45, "HILDEBEAR": 85, "HILDEBLUE": 100, "LA_DIMENIAN": 35, "MIGIUM": 45, "MOTHMANT": 25, "NANO_DRAGON": 55, "NAR_LILY": 100, "PAL_SHARK": 35, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 60, "SINOW_GOLD": 55, "SO_DIMENIAN": 35, "VOL_OPT_2": 100},
"GrindProbTable": [
[100, 30, 25, 20],
[0, 35, 23, 14],
@@ -1979,8 +1949,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -2000,7 +1968,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -2063,8 +2030,6 @@
"CHAOS_SORCERER": [200, 425],
"CLAW": [155, 335],
"DARK_BELRA": [100, 160],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 285],
"DEATH_GUNNER": [150, 275],
"DELSABER": [150, 330],
@@ -2084,7 +2049,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 165],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -2135,7 +2099,7 @@
[210, 275],
[220, 285]
],
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 2, "BOOMA": 7, "BULCLAW": 4, "BULK": 7, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 2, "CHAOS_BRINGER": 4, "CHAOS_SORCERER": 4, "CLAW": 7, "DARK_BELRA": 2, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 2, "DE_ROL_LE": 0, "DIMENIAN": 7, "DRAGON": 0, "DUBCHIC": 7, "EVIL_SHARK": 7, "GARANZ": 4, "GIGOBOOMA": 2, "GILLCHIC": 7, "GOBOOMA": 7, "GRASS_ASSASSIN": 10, "GUIL_SHARK": 7, "HIDOOM": 2, "HILDEBEAR": 10, "HILDEBLUE": 100, "LA_DIMENIAN": 7, "MIGIUM": 4, "MONEST": 0, "MOTHMANT": 7, "NANO_DRAGON": 2, "NAR_LILY": 100, "PAL_SHARK": 7, "PAN_ARMS": 2, "POFUILLY_SLIME": 2, "POISON_LILY": 2, "POUILLY_SLIME": 100, "RAG_RAPPY": 2, "SAVAGE_WOLF": 2, "SINOW_BEAT": 7, "SINOW_GOLD": 4, "SO_DIMENIAN": 2, "VOL_OPT_2": 0},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 2, "BOOMA": 7, "BULCLAW": 4, "BULK": 7, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 2, "CHAOS_BRINGER": 4, "CHAOS_SORCERER": 4, "CLAW": 7, "DARK_BELRA": 2, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 2, "DIMENIAN": 7, "DUBCHIC": 7, "EVIL_SHARK": 7, "GARANZ": 4, "GIGOBOOMA": 2, "GILLCHIC": 7, "GOBOOMA": 7, "GRASS_ASSASSIN": 10, "GUIL_SHARK": 7, "HIDOOM": 2, "HILDEBEAR": 10, "HILDEBLUE": 100, "LA_DIMENIAN": 7, "MIGIUM": 4, "MOTHMANT": 7, "NANO_DRAGON": 2, "NAR_LILY": 100, "PAL_SHARK": 7, "PAN_ARMS": 2, "POFUILLY_SLIME": 2, "POISON_LILY": 2, "POUILLY_SLIME": 100, "RAG_RAPPY": 2, "SAVAGE_WOLF": 2, "SINOW_BEAT": 7, "SINOW_GOLD": 4, "SO_DIMENIAN": 2},
"GrindProbTable": [
[100, 30, 25, 20],
[0, 30, 23, 15],
@@ -2483,8 +2447,8 @@
[130, 165],
[135, 175]
],
"EnemyMesetaRanges": {"AL_RAPPY": 0, "BARBAROUS_WOLF": 0, "BOOMA": 0, "BULCLAW": 0, "BULK": 0, "CANADINE": 0, "CANADINE_GROUP": 0, "CANANE": 0, "CHAOS_BRINGER": 0, "CHAOS_SORCERER": 0, "CLAW": 0, "DARK_BELRA": 0, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 0, "DEATH_GUNNER": 0, "DELSABER": 0, "DE_ROL_LE": 0, "DIMENIAN": 0, "DRAGON": 0, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 0, "GIGOBOOMA": 0, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 0, "HIDOOM": 0, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 0, "MONEST": 0, "MOTHMANT": 0, "NANO_DRAGON": 0, "NAR_LILY": 0, "PAL_SHARK": 0, "PAN_ARMS": 0, "POFUILLY_SLIME": 0, "POISON_LILY": 0, "POUILLY_SLIME": 0, "RAG_RAPPY": 0, "SAVAGE_WOLF": 0, "SINOW_BEAT": 0, "SINOW_GOLD": 0, "SO_DIMENIAN": 0, "VOL_OPT_2": 0},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 12, "BOOMA": 20, "BULCLAW": 20, "BULK": 20, "CANADINE": 12, "CANADINE_GROUP": 12, "CANANE": 20, "CHAOS_BRINGER": 40, "CHAOS_SORCERER": 30, "CLAW": 20, "DARK_BELRA": 30, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 12, "DEATH_GUNNER": 12, "DELSABER": 25, "DE_ROL_LE": 0, "DIMENIAN": 21, "DRAGON": 0, "DUBCHIC": 20, "EVIL_SHARK": 20, "GARANZ": 25, "GIGOBOOMA": 12, "GILLCHIC": 20, "GOBOOMA": 20, "GRASS_ASSASSIN": 20, "GUIL_SHARK": 12, "HIDOOM": 30, "HILDEBEAR": 30, "HILDEBLUE": 100, "LA_DIMENIAN": 23, "MIGIUM": 30, "MONEST": 0, "MOTHMANT": 20, "NANO_DRAGON": 20, "NAR_LILY": 100, "PAL_SHARK": 20, "PAN_ARMS": 30, "POFUILLY_SLIME": 12, "POISON_LILY": 12, "POUILLY_SLIME": 100, "RAG_RAPPY": 12, "SAVAGE_WOLF": 12, "SINOW_BEAT": 12, "SINOW_GOLD": 20, "SO_DIMENIAN": 25, "VOL_OPT_2": 0},
"EnemyMesetaRanges": {},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 12, "BOOMA": 20, "BULCLAW": 20, "BULK": 20, "CANADINE": 12, "CANADINE_GROUP": 12, "CANANE": 20, "CHAOS_BRINGER": 40, "CHAOS_SORCERER": 30, "CLAW": 20, "DARK_BELRA": 30, "DARK_GUNNER": 12, "DEATH_GUNNER": 12, "DELSABER": 25, "DIMENIAN": 21, "DUBCHIC": 20, "EVIL_SHARK": 20, "GARANZ": 25, "GIGOBOOMA": 12, "GILLCHIC": 20, "GOBOOMA": 20, "GRASS_ASSASSIN": 20, "GUIL_SHARK": 12, "HIDOOM": 30, "HILDEBEAR": 30, "HILDEBLUE": 100, "LA_DIMENIAN": 23, "MIGIUM": 30, "MOTHMANT": 20, "NANO_DRAGON": 20, "NAR_LILY": 100, "PAL_SHARK": 20, "PAN_ARMS": 30, "POFUILLY_SLIME": 12, "POISON_LILY": 12, "POUILLY_SLIME": 100, "RAG_RAPPY": 12, "SAVAGE_WOLF": 12, "SINOW_BEAT": 12, "SINOW_GOLD": 20, "SO_DIMENIAN": 25},
"NonRareBonusProbSpec": [
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3],
[255, 255, 0, 0, 0, 1, 1, 1, 2, 2],
@@ -2773,8 +2737,8 @@
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[44, 43, 42, 42, 41, 41, 41, 39, 38, 38]
],
"EnemyMesetaRanges": {"AL_RAPPY": 0, "BARBAROUS_WOLF": 0, "BOOMA": 0, "BULCLAW": 0, "BULK": 0, "CANADINE": 0, "CANADINE_GROUP": 0, "CANANE": 0, "CHAOS_BRINGER": 0, "CHAOS_SORCERER": 0, "CLAW": 0, "DARK_BELRA": 0, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 0, "DEATH_GUNNER": 0, "DELSABER": 0, "DE_ROL_LE": 0, "DIMENIAN": 0, "DRAGON": 0, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 0, "GIGOBOOMA": 0, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 0, "HIDOOM": 0, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 0, "MONEST": 0, "MOTHMANT": 0, "NANO_DRAGON": 0, "NAR_LILY": 0, "PAL_SHARK": 0, "PAN_ARMS": 0, "POFUILLY_SLIME": 0, "POISON_LILY": 0, "POUILLY_SLIME": 0, "RAG_RAPPY": 0, "SAVAGE_WOLF": 0, "SINOW_BEAT": 0, "SINOW_GOLD": 0, "SO_DIMENIAN": 0, "VOL_OPT_2": 0},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 2, "BOOMA": 7, "BULCLAW": 4, "BULK": 7, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 2, "CHAOS_BRINGER": 4, "CHAOS_SORCERER": 4, "CLAW": 7, "DARK_BELRA": 2, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 2, "DE_ROL_LE": 0, "DIMENIAN": 7, "DRAGON": 0, "DUBCHIC": 7, "EVIL_SHARK": 7, "GARANZ": 4, "GIGOBOOMA": 2, "GILLCHIC": 7, "GOBOOMA": 7, "GRASS_ASSASSIN": 10, "GUIL_SHARK": 7, "HIDOOM": 2, "HILDEBEAR": 10, "HILDEBLUE": 100, "LA_DIMENIAN": 7, "MIGIUM": 4, "MONEST": 0, "MOTHMANT": 7, "NANO_DRAGON": 2, "NAR_LILY": 100, "PAL_SHARK": 7, "PAN_ARMS": 2, "POFUILLY_SLIME": 2, "POISON_LILY": 2, "POUILLY_SLIME": 100, "RAG_RAPPY": 2, "SAVAGE_WOLF": 2, "SINOW_BEAT": 7, "SINOW_GOLD": 4, "SO_DIMENIAN": 2, "VOL_OPT_2": 0},
"EnemyMesetaRanges": {},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 2, "BOOMA": 7, "BULCLAW": 4, "BULK": 7, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 2, "CHAOS_BRINGER": 4, "CHAOS_SORCERER": 4, "CLAW": 7, "DARK_BELRA": 2, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 2, "DIMENIAN": 7, "DUBCHIC": 7, "EVIL_SHARK": 7, "GARANZ": 4, "GIGOBOOMA": 2, "GILLCHIC": 7, "GOBOOMA": 7, "GRASS_ASSASSIN": 10, "GUIL_SHARK": 7, "HIDOOM": 2, "HILDEBEAR": 10, "HILDEBLUE": 100, "LA_DIMENIAN": 7, "MIGIUM": 4, "MOTHMANT": 7, "NANO_DRAGON": 2, "NAR_LILY": 100, "PAL_SHARK": 7, "PAN_ARMS": 2, "POFUILLY_SLIME": 2, "POISON_LILY": 2, "POUILLY_SLIME": 100, "RAG_RAPPY": 2, "SAVAGE_WOLF": 2, "SINOW_BEAT": 7, "SINOW_GOLD": 4, "SO_DIMENIAN": 2},
"TechniqueIndexProbTable": [
[108, 108, 108, 108, 109, 108, 110, 109, 120, 125],
[80, 80, 80, 80, 84, 85, 84, 100, 104, 112],
@@ -3039,7 +3003,7 @@
[380, 430],
[400, 440]
],
"EnemyItemClasses": {"AL_RAPPY": 2, "BARBAROUS_WOLF": 2, "BOOMA": 0, "BULCLAW": 3, "BULK": 0, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 1, "CHAOS_BRINGER": 3, "CHAOS_SORCERER": 3, "CLAW": 0, "DARK_BELRA": 1, "DARK_FALZ_2": 255, "DARK_FALZ_3": 255, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 1, "DE_ROL_LE": 255, "DIMENIAN": 0, "DRAGON": 255, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 2, "GIGOBOOMA": 3, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 3, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 3, "MONEST": 255, "MOTHMANT": 0, "NANO_DRAGON": 2, "NAR_LILY": 3, "PAL_SHARK": 0, "PAN_ARMS": 2, "POFUILLY_SLIME": 1, "POISON_LILY": 1, "POUILLY_SLIME": 3, "RAG_RAPPY": 2, "SAVAGE_WOLF": 1, "SINOW_BEAT": 0, "SINOW_GOLD": 3, "SO_DIMENIAN": 1, "VOL_OPT_2": 255},
"EnemyItemClasses": {"AL_RAPPY": 2, "BARBAROUS_WOLF": 2, "BOOMA": 0, "BULCLAW": 3, "BULK": 0, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 1, "CHAOS_BRINGER": 3, "CHAOS_SORCERER": 3, "CLAW": 0, "DARK_BELRA": 1, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 1, "DIMENIAN": 0, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 2, "GIGOBOOMA": 3, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 3, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 3, "MOTHMANT": 0, "NANO_DRAGON": 2, "NAR_LILY": 3, "PAL_SHARK": 0, "PAN_ARMS": 2, "POFUILLY_SLIME": 1, "POISON_LILY": 1, "POUILLY_SLIME": 3, "RAG_RAPPY": 2, "SAVAGE_WOLF": 1, "SINOW_BEAT": 0, "SINOW_GOLD": 3, "SO_DIMENIAN": 1},
"EnemyMesetaRanges": {
"AL_RAPPY": [1500, 2000],
"BARBAROUS_WOLF": [120, 164],
@@ -3053,8 +3017,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -3074,7 +3036,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -3090,7 +3051,7 @@
"SO_DIMENIAN": [185, 295],
"VOL_OPT_2": 3500
},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 30, "BULCLAW": 45, "BULK": 35, "CANADINE": 35, "CANADINE_GROUP": 35, "CANANE": 35, "CHAOS_BRINGER": 88, "CHAOS_SORCERER": 35, "CLAW": 30, "DARK_BELRA": 45, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 45, "DEATH_GUNNER": 45, "DELSABER": 45, "DE_ROL_LE": 100, "DIMENIAN": 30, "DRAGON": 100, "DUBCHIC": 30, "EVIL_SHARK": 30, "GARANZ": 85, "GIGOBOOMA": 35, "GILLCHIC": 30, "GOBOOMA": 38, "GRASS_ASSASSIN": 55, "GUIL_SHARK": 15, "HIDOOM": 45, "HILDEBEAR": 85, "HILDEBLUE": 100, "LA_DIMENIAN": 35, "MIGIUM": 45, "MONEST": 0, "MOTHMANT": 25, "NANO_DRAGON": 55, "NAR_LILY": 100, "PAL_SHARK": 35, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 60, "SINOW_GOLD": 55, "SO_DIMENIAN": 35, "VOL_OPT_2": 100},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 30, "BULCLAW": 45, "BULK": 35, "CANADINE": 35, "CANADINE_GROUP": 35, "CANANE": 35, "CHAOS_BRINGER": 88, "CHAOS_SORCERER": 35, "CLAW": 30, "DARK_BELRA": 45, "DARK_GUNNER": 45, "DEATH_GUNNER": 45, "DELSABER": 45, "DE_ROL_LE": 100, "DIMENIAN": 30, "DRAGON": 100, "DUBCHIC": 30, "EVIL_SHARK": 30, "GARANZ": 85, "GIGOBOOMA": 35, "GILLCHIC": 30, "GOBOOMA": 38, "GRASS_ASSASSIN": 55, "GUIL_SHARK": 15, "HIDOOM": 45, "HILDEBEAR": 85, "HILDEBLUE": 100, "LA_DIMENIAN": 35, "MIGIUM": 45, "MOTHMANT": 25, "NANO_DRAGON": 55, "NAR_LILY": 100, "PAL_SHARK": 35, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 60, "SINOW_GOLD": 55, "SO_DIMENIAN": 35, "VOL_OPT_2": 100},
"GrindProbTable": [
[100, 30, 25, 20],
[0, 35, 23, 14],
@@ -3480,8 +3441,6 @@
"CHAOS_SORCERER": [145, 200],
"CLAW": [95, 145],
"DARK_BELRA": [50, 80],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [100, 122],
"DEATH_GUNNER": [100, 122],
"DELSABER": [150, 165],
@@ -3501,7 +3460,6 @@
"HILDEBLUE": [1000, 1500],
"LA_DIMENIAN": [115, 165],
"MIGIUM": [50, 85],
"MONEST": 0,
"MOTHMANT": [80, 90],
"NANO_DRAGON": [50, 170],
"NAR_LILY": [1000, 2000],
@@ -3517,7 +3475,7 @@
"SO_DIMENIAN": [135, 180],
"VOL_OPT_2": 2000
},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 28, "BULCLAW": 40, "BULK": 30, "CANADINE": 31, "CANADINE_GROUP": 31, "CANANE": 31, "CHAOS_BRINGER": 85, "CHAOS_SORCERER": 40, "CLAW": 28, "DARK_BELRA": 40, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 30, "DEATH_GUNNER": 30, "DELSABER": 35, "DE_ROL_LE": 100, "DIMENIAN": 28, "DRAGON": 100, "DUBCHIC": 28, "EVIL_SHARK": 28, "GARANZ": 80, "GIGOBOOMA": 35, "GILLCHIC": 28, "GOBOOMA": 32, "GRASS_ASSASSIN": 50, "GUIL_SHARK": 15, "HIDOOM": 40, "HILDEBEAR": 80, "HILDEBLUE": 100, "LA_DIMENIAN": 31, "MIGIUM": 40, "MONEST": 0, "MOTHMANT": 25, "NANO_DRAGON": 50, "NAR_LILY": 100, "PAL_SHARK": 32, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 50, "SINOW_GOLD": 50, "SO_DIMENIAN": 33, "VOL_OPT_2": 100},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 28, "BULCLAW": 40, "BULK": 30, "CANADINE": 31, "CANADINE_GROUP": 31, "CANANE": 31, "CHAOS_BRINGER": 85, "CHAOS_SORCERER": 40, "CLAW": 28, "DARK_BELRA": 40, "DARK_GUNNER": 30, "DEATH_GUNNER": 30, "DELSABER": 35, "DE_ROL_LE": 100, "DIMENIAN": 28, "DRAGON": 100, "DUBCHIC": 28, "EVIL_SHARK": 28, "GARANZ": 80, "GIGOBOOMA": 35, "GILLCHIC": 28, "GOBOOMA": 32, "GRASS_ASSASSIN": 50, "GUIL_SHARK": 15, "HIDOOM": 40, "HILDEBEAR": 80, "HILDEBLUE": 100, "LA_DIMENIAN": 31, "MIGIUM": 40, "MOTHMANT": 25, "NANO_DRAGON": 50, "NAR_LILY": 100, "PAL_SHARK": 32, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 50, "SINOW_GOLD": 50, "SO_DIMENIAN": 33, "VOL_OPT_2": 100},
"GrindProbTable": [
[100, 30, 25, 20],
[0, 30, 23, 15],
@@ -3843,8 +3801,6 @@
"CHAOS_SORCERER": [145, 200],
"CLAW": [95, 145],
"DARK_BELRA": [50, 80],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [100, 122],
"DEATH_GUNNER": [100, 122],
"DELSABER": [150, 165],
@@ -3864,7 +3820,6 @@
"HILDEBLUE": [1000, 1500],
"LA_DIMENIAN": [115, 165],
"MIGIUM": [50, 85],
"MONEST": 0,
"MOTHMANT": [80, 90],
"NANO_DRAGON": [50, 170],
"NAR_LILY": [1000, 2000],
@@ -3937,8 +3892,6 @@
"CHAOS_SORCERER": [145, 210],
"CLAW": [95, 155],
"DARK_BELRA": [50, 90],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [100, 132],
"DEATH_GUNNER": [100, 132],
"DELSABER": [150, 175],
@@ -3958,7 +3911,6 @@
"HILDEBLUE": [1000, 1500],
"LA_DIMENIAN": [115, 175],
"MIGIUM": [50, 95],
"MONEST": 0,
"MOTHMANT": [80, 100],
"NANO_DRAGON": [50, 180],
"NAR_LILY": [1000, 2000],
@@ -4120,7 +4072,7 @@
[19, 50],
[21, 55]
],
"EnemyItemClasses": {"AL_RAPPY": 2, "BARBAROUS_WOLF": 0, "BOOMA": 0, "BULCLAW": 3, "BULK": 0, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 1, "CHAOS_BRINGER": 3, "CHAOS_SORCERER": 3, "CLAW": 0, "DARK_BELRA": 1, "DARK_FALZ_2": 255, "DARK_FALZ_3": 255, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 1, "DE_ROL_LE": 255, "DIMENIAN": 0, "DRAGON": 255, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 2, "GIGOBOOMA": 2, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 3, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 3, "MONEST": 255, "MOTHMANT": 0, "NANO_DRAGON": 2, "NAR_LILY": 3, "PAL_SHARK": 0, "PAN_ARMS": 2, "POFUILLY_SLIME": 1, "POISON_LILY": 1, "POUILLY_SLIME": 3, "RAG_RAPPY": 1, "SAVAGE_WOLF": 1, "SINOW_BEAT": 0, "SINOW_GOLD": 3, "SO_DIMENIAN": 1, "VOL_OPT_2": 255},
"EnemyItemClasses": {"AL_RAPPY": 2, "BARBAROUS_WOLF": 0, "BOOMA": 0, "BULCLAW": 3, "BULK": 0, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 1, "CHAOS_BRINGER": 3, "CHAOS_SORCERER": 3, "CLAW": 0, "DARK_BELRA": 1, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 1, "DIMENIAN": 0, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 2, "GIGOBOOMA": 2, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 3, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 3, "MOTHMANT": 0, "NANO_DRAGON": 2, "NAR_LILY": 3, "PAL_SHARK": 0, "PAN_ARMS": 2, "POFUILLY_SLIME": 1, "POISON_LILY": 1, "POUILLY_SLIME": 3, "RAG_RAPPY": 1, "SAVAGE_WOLF": 1, "SINOW_BEAT": 0, "SINOW_GOLD": 3, "SO_DIMENIAN": 1},
"EnemyMesetaRanges": {
"AL_RAPPY": [500, 1000],
"BARBAROUS_WOLF": [15, 30],
@@ -4134,8 +4086,6 @@
"CHAOS_SORCERER": [115, 135],
"CLAW": [45, 75],
"DARK_BELRA": 10,
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [110, 125],
"DEATH_GUNNER": [110, 125],
"DELSABER": [95, 100],
@@ -4155,7 +4105,6 @@
"HILDEBLUE": [500, 1000],
"LA_DIMENIAN": [100, 120],
"MIGIUM": [15, 20],
"MONEST": 0,
"MOTHMANT": [3, 6],
"NANO_DRAGON": [60, 70],
"NAR_LILY": [500, 1200],
@@ -4171,7 +4120,7 @@
"SO_DIMENIAN": [120, 145],
"VOL_OPT_2": 900
},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 28, "BULCLAW": 40, "BULK": 30, "CANADINE": 33, "CANADINE_GROUP": 33, "CANANE": 33, "CHAOS_BRINGER": 85, "CHAOS_SORCERER": 30, "CLAW": 28, "DARK_BELRA": 40, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 40, "DEATH_GUNNER": 40, "DELSABER": 40, "DE_ROL_LE": 100, "DIMENIAN": 28, "DRAGON": 100, "DUBCHIC": 28, "EVIL_SHARK": 28, "GARANZ": 80, "GIGOBOOMA": 35, "GILLCHIC": 28, "GOBOOMA": 32, "GRASS_ASSASSIN": 50, "GUIL_SHARK": 15, "HIDOOM": 40, "HILDEBEAR": 80, "HILDEBLUE": 100, "LA_DIMENIAN": 31, "MIGIUM": 40, "MONEST": 0, "MOTHMANT": 25, "NANO_DRAGON": 50, "NAR_LILY": 100, "PAL_SHARK": 32, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 55, "SINOW_GOLD": 50, "SO_DIMENIAN": 33, "VOL_OPT_2": 100},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 28, "BULCLAW": 40, "BULK": 30, "CANADINE": 33, "CANADINE_GROUP": 33, "CANANE": 33, "CHAOS_BRINGER": 85, "CHAOS_SORCERER": 30, "CLAW": 28, "DARK_BELRA": 40, "DARK_GUNNER": 40, "DEATH_GUNNER": 40, "DELSABER": 40, "DE_ROL_LE": 100, "DIMENIAN": 28, "DRAGON": 100, "DUBCHIC": 28, "EVIL_SHARK": 28, "GARANZ": 80, "GIGOBOOMA": 35, "GILLCHIC": 28, "GOBOOMA": 32, "GRASS_ASSASSIN": 50, "GUIL_SHARK": 15, "HIDOOM": 40, "HILDEBEAR": 80, "HILDEBLUE": 100, "LA_DIMENIAN": 31, "MIGIUM": 40, "MOTHMANT": 25, "NANO_DRAGON": 50, "NAR_LILY": 100, "PAL_SHARK": 32, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 55, "SINOW_GOLD": 50, "SO_DIMENIAN": 33, "VOL_OPT_2": 100},
"GrindProbTable": [
[100, 35, 30, 25],
[0, 25, 20, 15],
@@ -4498,8 +4447,6 @@
"CHAOS_SORCERER": [115, 135],
"CLAW": [45, 75],
"DARK_BELRA": 10,
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [110, 125],
"DEATH_GUNNER": [110, 125],
"DELSABER": [95, 100],
@@ -4519,7 +4466,6 @@
"HILDEBLUE": [500, 1000],
"LA_DIMENIAN": [100, 120],
"MIGIUM": [15, 20],
"MONEST": 0,
"MOTHMANT": [3, 6],
"NANO_DRAGON": [60, 70],
"NAR_LILY": [500, 1200],
@@ -4597,8 +4543,6 @@
"CHAOS_SORCERER": [120, 135],
"CLAW": [50, 75],
"DARK_BELRA": 10,
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [105, 130],
"DEATH_GUNNER": [105, 130],
"DELSABER": [95, 100],
@@ -4618,7 +4562,6 @@
"HILDEBLUE": [500, 1000],
"LA_DIMENIAN": [105, 120],
"MIGIUM": [17, 20],
"MONEST": 0,
"MOTHMANT": [5, 6],
"NANO_DRAGON": [65, 70],
"NAR_LILY": [500, 1200],
@@ -4672,8 +4615,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -4693,7 +4634,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -5107,8 +5047,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -5128,7 +5066,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -5191,8 +5128,6 @@
"CHAOS_SORCERER": [200, 425],
"CLAW": [155, 335],
"DARK_BELRA": [100, 160],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 285],
"DEATH_GUNNER": [150, 275],
"DELSABER": [150, 330],
@@ -5212,7 +5147,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 165],
"MONEST": 0,
"MOTHMANT": [120, 170],
"NANO_DRAGON": [80, 340],
"NAR_LILY": [2000, 2500],
@@ -5249,8 +5183,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -5270,7 +5202,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -5352,7 +5283,7 @@
[56, 140],
[58, 145]
],
"EnemyItemClasses": {"AL_RAPPY": 2, "BARBAROUS_WOLF": 2, "BOOMA": 0, "BULCLAW": 3, "BULK": 0, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 1, "CHAOS_BRINGER": 3, "CHAOS_SORCERER": 3, "CLAW": 0, "DARK_BELRA": 1, "DARK_FALZ_2": 255, "DARK_FALZ_3": 255, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 1, "DE_ROL_LE": 255, "DIMENIAN": 0, "DRAGON": 255, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 2, "GIGOBOOMA": 3, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 3, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 3, "MONEST": 255, "MOTHMANT": 0, "NANO_DRAGON": 2, "NAR_LILY": 3, "PAL_SHARK": 0, "PAN_ARMS": 2, "POFUILLY_SLIME": 1, "POISON_LILY": 1, "POUILLY_SLIME": 3, "RAG_RAPPY": 2, "SAVAGE_WOLF": 1, "SINOW_BEAT": 0, "SINOW_GOLD": 3, "SO_DIMENIAN": 1, "VOL_OPT_2": 255},
"EnemyItemClasses": {"AL_RAPPY": 2, "BARBAROUS_WOLF": 2, "BOOMA": 0, "BULCLAW": 3, "BULK": 0, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 1, "CHAOS_BRINGER": 3, "CHAOS_SORCERER": 3, "CLAW": 0, "DARK_BELRA": 1, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 1, "DIMENIAN": 0, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 2, "GIGOBOOMA": 3, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 3, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 3, "MOTHMANT": 0, "NANO_DRAGON": 2, "NAR_LILY": 3, "PAL_SHARK": 0, "PAN_ARMS": 2, "POFUILLY_SLIME": 1, "POISON_LILY": 1, "POUILLY_SLIME": 3, "RAG_RAPPY": 2, "SAVAGE_WOLF": 1, "SINOW_BEAT": 0, "SINOW_GOLD": 3, "SO_DIMENIAN": 1},
"EnemyMesetaRanges": {
"AL_RAPPY": [1500, 2000],
"BARBAROUS_WOLF": [120, 164],
@@ -5366,8 +5297,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -5387,7 +5316,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -5403,7 +5331,7 @@
"SO_DIMENIAN": [185, 295],
"VOL_OPT_2": 3500
},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 30, "BULCLAW": 45, "BULK": 35, "CANADINE": 35, "CANADINE_GROUP": 35, "CANANE": 35, "CHAOS_BRINGER": 88, "CHAOS_SORCERER": 35, "CLAW": 30, "DARK_BELRA": 45, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 45, "DEATH_GUNNER": 45, "DELSABER": 45, "DE_ROL_LE": 100, "DIMENIAN": 30, "DRAGON": 100, "DUBCHIC": 30, "EVIL_SHARK": 30, "GARANZ": 85, "GIGOBOOMA": 35, "GILLCHIC": 30, "GOBOOMA": 38, "GRASS_ASSASSIN": 55, "GUIL_SHARK": 15, "HIDOOM": 45, "HILDEBEAR": 85, "HILDEBLUE": 100, "LA_DIMENIAN": 35, "MIGIUM": 45, "MONEST": 0, "MOTHMANT": 25, "NANO_DRAGON": 55, "NAR_LILY": 100, "PAL_SHARK": 35, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 60, "SINOW_GOLD": 55, "SO_DIMENIAN": 35, "VOL_OPT_2": 100},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 30, "BULCLAW": 45, "BULK": 35, "CANADINE": 35, "CANADINE_GROUP": 35, "CANANE": 35, "CHAOS_BRINGER": 88, "CHAOS_SORCERER": 35, "CLAW": 30, "DARK_BELRA": 45, "DARK_GUNNER": 45, "DEATH_GUNNER": 45, "DELSABER": 45, "DE_ROL_LE": 100, "DIMENIAN": 30, "DRAGON": 100, "DUBCHIC": 30, "EVIL_SHARK": 30, "GARANZ": 85, "GIGOBOOMA": 35, "GILLCHIC": 30, "GOBOOMA": 38, "GRASS_ASSASSIN": 55, "GUIL_SHARK": 15, "HIDOOM": 45, "HILDEBEAR": 85, "HILDEBLUE": 100, "LA_DIMENIAN": 35, "MIGIUM": 45, "MOTHMANT": 25, "NANO_DRAGON": 55, "NAR_LILY": 100, "PAL_SHARK": 35, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 60, "SINOW_GOLD": 55, "SO_DIMENIAN": 35, "VOL_OPT_2": 100},
"GrindProbTable": [
[100, 30, 25, 20],
[0, 35, 23, 14],
@@ -5729,8 +5657,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -5750,7 +5676,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -5813,8 +5738,6 @@
"CHAOS_SORCERER": [200, 425],
"CLAW": [155, 335],
"DARK_BELRA": [100, 160],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 285],
"DEATH_GUNNER": [150, 275],
"DELSABER": [150, 330],
@@ -5834,7 +5757,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 165],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -5958,8 +5880,6 @@
"CHAOS_SORCERER": [145, 200],
"CLAW": [95, 145],
"DARK_BELRA": [50, 80],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [100, 122],
"DEATH_GUNNER": [100, 122],
"DELSABER": [150, 165],
@@ -5979,7 +5899,6 @@
"HILDEBLUE": [1000, 1500],
"LA_DIMENIAN": [115, 165],
"MIGIUM": [50, 85],
"MONEST": 0,
"MOTHMANT": [80, 90],
"NANO_DRAGON": [50, 170],
"NAR_LILY": [1000, 2000],
@@ -5995,7 +5914,7 @@
"SO_DIMENIAN": [135, 180],
"VOL_OPT_2": 2000
},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 28, "BULCLAW": 40, "BULK": 30, "CANADINE": 31, "CANADINE_GROUP": 31, "CANANE": 31, "CHAOS_BRINGER": 85, "CHAOS_SORCERER": 40, "CLAW": 28, "DARK_BELRA": 40, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 30, "DEATH_GUNNER": 30, "DELSABER": 35, "DE_ROL_LE": 100, "DIMENIAN": 28, "DRAGON": 100, "DUBCHIC": 28, "EVIL_SHARK": 28, "GARANZ": 80, "GIGOBOOMA": 35, "GILLCHIC": 28, "GOBOOMA": 32, "GRASS_ASSASSIN": 50, "GUIL_SHARK": 15, "HIDOOM": 40, "HILDEBEAR": 80, "HILDEBLUE": 100, "LA_DIMENIAN": 31, "MIGIUM": 40, "MONEST": 0, "MOTHMANT": 25, "NANO_DRAGON": 50, "NAR_LILY": 100, "PAL_SHARK": 32, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 50, "SINOW_GOLD": 50, "SO_DIMENIAN": 33, "VOL_OPT_2": 100},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 28, "BULCLAW": 40, "BULK": 30, "CANADINE": 31, "CANADINE_GROUP": 31, "CANANE": 31, "CHAOS_BRINGER": 85, "CHAOS_SORCERER": 40, "CLAW": 28, "DARK_BELRA": 40, "DARK_GUNNER": 30, "DEATH_GUNNER": 30, "DELSABER": 35, "DE_ROL_LE": 100, "DIMENIAN": 28, "DRAGON": 100, "DUBCHIC": 28, "EVIL_SHARK": 28, "GARANZ": 80, "GIGOBOOMA": 35, "GILLCHIC": 28, "GOBOOMA": 32, "GRASS_ASSASSIN": 50, "GUIL_SHARK": 15, "HIDOOM": 40, "HILDEBEAR": 80, "HILDEBLUE": 100, "LA_DIMENIAN": 31, "MIGIUM": 40, "MOTHMANT": 25, "NANO_DRAGON": 50, "NAR_LILY": 100, "PAL_SHARK": 32, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 50, "SINOW_GOLD": 50, "SO_DIMENIAN": 33, "VOL_OPT_2": 100},
"GrindProbTable": [
[100, 30, 25, 20],
[0, 30, 23, 15],
@@ -6321,8 +6240,6 @@
"CHAOS_SORCERER": [145, 200],
"CLAW": [95, 145],
"DARK_BELRA": [50, 80],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [100, 122],
"DEATH_GUNNER": [100, 122],
"DELSABER": [150, 165],
@@ -6342,7 +6259,6 @@
"HILDEBLUE": [1000, 1500],
"LA_DIMENIAN": [115, 165],
"MIGIUM": [50, 85],
"MONEST": 0,
"MOTHMANT": [80, 90],
"NANO_DRAGON": [50, 170],
"NAR_LILY": [1000, 2000],
@@ -6415,8 +6331,6 @@
"CHAOS_SORCERER": [145, 210],
"CLAW": [95, 155],
"DARK_BELRA": [50, 90],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [100, 132],
"DEATH_GUNNER": [100, 132],
"DELSABER": [150, 175],
@@ -6436,7 +6350,6 @@
"HILDEBLUE": [1000, 1500],
"LA_DIMENIAN": [115, 175],
"MIGIUM": [50, 95],
"MONEST": 0,
"MOTHMANT": [80, 100],
"NANO_DRAGON": [50, 180],
"NAR_LILY": [1000, 2000],
@@ -6584,8 +6497,6 @@
"CHAOS_SORCERER": [115, 135],
"CLAW": [45, 75],
"DARK_BELRA": 10,
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [110, 125],
"DEATH_GUNNER": [110, 125],
"DELSABER": [95, 100],
@@ -6605,7 +6516,6 @@
"HILDEBLUE": [500, 1000],
"LA_DIMENIAN": [100, 120],
"MIGIUM": [15, 20],
"MONEST": 0,
"MOTHMANT": [3, 6],
"NANO_DRAGON": [60, 70],
"NAR_LILY": [500, 1200],
@@ -6683,8 +6593,6 @@
"CHAOS_SORCERER": [120, 135],
"CLAW": [50, 75],
"DARK_BELRA": 10,
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [105, 130],
"DEATH_GUNNER": [105, 130],
"DELSABER": [95, 100],
@@ -6704,7 +6612,6 @@
"HILDEBLUE": [500, 1000],
"LA_DIMENIAN": [105, 120],
"MIGIUM": [17, 20],
"MONEST": 0,
"MOTHMANT": [5, 6],
"NANO_DRAGON": [65, 70],
"NAR_LILY": [500, 1200],
@@ -6758,8 +6665,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -6779,7 +6684,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -7193,8 +7097,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -7214,7 +7116,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -7277,8 +7178,6 @@
"CHAOS_SORCERER": [200, 425],
"CLAW": [155, 335],
"DARK_BELRA": [100, 160],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 285],
"DEATH_GUNNER": [150, 275],
"DELSABER": [150, 330],
@@ -7298,7 +7197,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 165],
"MONEST": 0,
"MOTHMANT": [120, 170],
"NANO_DRAGON": [80, 340],
"NAR_LILY": [2000, 2500],
@@ -7335,8 +7233,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -7356,7 +7252,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -7438,7 +7333,7 @@
[56, 140],
[58, 145]
],
"EnemyItemClasses": {"AL_RAPPY": 2, "BARBAROUS_WOLF": 2, "BOOMA": 0, "BULCLAW": 3, "BULK": 0, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 1, "CHAOS_BRINGER": 3, "CHAOS_SORCERER": 3, "CLAW": 0, "DARK_BELRA": 1, "DARK_FALZ_2": 255, "DARK_FALZ_3": 255, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 1, "DE_ROL_LE": 255, "DIMENIAN": 0, "DRAGON": 255, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 2, "GIGOBOOMA": 3, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 3, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 3, "MONEST": 255, "MOTHMANT": 0, "NANO_DRAGON": 2, "NAR_LILY": 3, "PAL_SHARK": 0, "PAN_ARMS": 2, "POFUILLY_SLIME": 1, "POISON_LILY": 1, "POUILLY_SLIME": 3, "RAG_RAPPY": 2, "SAVAGE_WOLF": 1, "SINOW_BEAT": 0, "SINOW_GOLD": 3, "SO_DIMENIAN": 1, "VOL_OPT_2": 255},
"EnemyItemClasses": {"AL_RAPPY": 2, "BARBAROUS_WOLF": 2, "BOOMA": 0, "BULCLAW": 3, "BULK": 0, "CANADINE": 2, "CANADINE_GROUP": 2, "CANANE": 1, "CHAOS_BRINGER": 3, "CHAOS_SORCERER": 3, "CLAW": 0, "DARK_BELRA": 1, "DARK_GUNNER": 2, "DEATH_GUNNER": 2, "DELSABER": 1, "DIMENIAN": 0, "DUBCHIC": 0, "EVIL_SHARK": 0, "GARANZ": 2, "GIGOBOOMA": 3, "GILLCHIC": 0, "GOBOOMA": 0, "GRASS_ASSASSIN": 0, "GUIL_SHARK": 3, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "LA_DIMENIAN": 0, "MIGIUM": 3, "MOTHMANT": 0, "NANO_DRAGON": 2, "NAR_LILY": 3, "PAL_SHARK": 0, "PAN_ARMS": 2, "POFUILLY_SLIME": 1, "POISON_LILY": 1, "POUILLY_SLIME": 3, "RAG_RAPPY": 2, "SAVAGE_WOLF": 1, "SINOW_BEAT": 0, "SINOW_GOLD": 3, "SO_DIMENIAN": 1},
"EnemyMesetaRanges": {
"AL_RAPPY": [1500, 2000],
"BARBAROUS_WOLF": [120, 164],
@@ -7452,8 +7347,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -7473,7 +7366,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -7489,7 +7381,7 @@
"SO_DIMENIAN": [185, 295],
"VOL_OPT_2": 3500
},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 30, "BULCLAW": 45, "BULK": 35, "CANADINE": 35, "CANADINE_GROUP": 35, "CANANE": 35, "CHAOS_BRINGER": 88, "CHAOS_SORCERER": 35, "CLAW": 30, "DARK_BELRA": 45, "DARK_FALZ_2": 0, "DARK_FALZ_3": 0, "DARK_GUNNER": 45, "DEATH_GUNNER": 45, "DELSABER": 45, "DE_ROL_LE": 100, "DIMENIAN": 30, "DRAGON": 100, "DUBCHIC": 30, "EVIL_SHARK": 30, "GARANZ": 85, "GIGOBOOMA": 35, "GILLCHIC": 30, "GOBOOMA": 38, "GRASS_ASSASSIN": 55, "GUIL_SHARK": 15, "HIDOOM": 45, "HILDEBEAR": 85, "HILDEBLUE": 100, "LA_DIMENIAN": 35, "MIGIUM": 45, "MONEST": 0, "MOTHMANT": 25, "NANO_DRAGON": 55, "NAR_LILY": 100, "PAL_SHARK": 35, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 60, "SINOW_GOLD": 55, "SO_DIMENIAN": 35, "VOL_OPT_2": 100},
"EnemyTypeDropProbs": {"AL_RAPPY": 100, "BARBAROUS_WOLF": 45, "BOOMA": 30, "BULCLAW": 45, "BULK": 35, "CANADINE": 35, "CANADINE_GROUP": 35, "CANANE": 35, "CHAOS_BRINGER": 88, "CHAOS_SORCERER": 35, "CLAW": 30, "DARK_BELRA": 45, "DARK_GUNNER": 45, "DEATH_GUNNER": 45, "DELSABER": 45, "DE_ROL_LE": 100, "DIMENIAN": 30, "DRAGON": 100, "DUBCHIC": 30, "EVIL_SHARK": 30, "GARANZ": 85, "GIGOBOOMA": 35, "GILLCHIC": 30, "GOBOOMA": 38, "GRASS_ASSASSIN": 55, "GUIL_SHARK": 15, "HIDOOM": 45, "HILDEBEAR": 85, "HILDEBLUE": 100, "LA_DIMENIAN": 35, "MIGIUM": 45, "MOTHMANT": 25, "NANO_DRAGON": 55, "NAR_LILY": 100, "PAL_SHARK": 35, "PAN_ARMS": 90, "POFUILLY_SLIME": 27, "POISON_LILY": 25, "POUILLY_SLIME": 100, "RAG_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BEAT": 60, "SINOW_GOLD": 55, "SO_DIMENIAN": 35, "VOL_OPT_2": 100},
"GrindProbTable": [
[100, 30, 25, 20],
[0, 35, 23, 14],
@@ -7815,8 +7707,6 @@
"CHAOS_SORCERER": [200, 400],
"CLAW": [135, 245],
"DARK_BELRA": [100, 130],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 250],
"DEATH_GUNNER": [150, 250],
"DELSABER": [150, 300],
@@ -7836,7 +7726,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 135],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -7899,8 +7788,6 @@
"CHAOS_SORCERER": [200, 425],
"CLAW": [155, 335],
"DARK_BELRA": [100, 160],
"DARK_FALZ_2": 0,
"DARK_FALZ_3": 0,
"DARK_GUNNER": [150, 285],
"DEATH_GUNNER": [150, 275],
"DELSABER": [150, 330],
@@ -7920,7 +7807,6 @@
"HILDEBLUE": [1500, 2000],
"LA_DIMENIAN": [165, 275],
"MIGIUM": [55, 165],
"MONEST": 0,
"MOTHMANT": [120, 140],
"NANO_DRAGON": [80, 310],
"NAR_LILY": [2000, 2500],
@@ -8029,7 +7915,7 @@
[44, 110],
[46, 115]
],
"EnemyItemClasses": {"BARBAROUS_WOLF": 0, "BARBA_RAY": 255, "BARBA_RAY_JOINT": 255, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 2, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EGG_RAPPY": 255, "EPSILON": 3, "GAL_GRYPHON": 255, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GOL_DRAGON": 255, "GRASS_ASSASSIN": 0, "HALLO_RAPPY": 255, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MONEST": 255, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "OLGA_FLOW_2": 255, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 1, "RECOBOX": 3, "RECON": 0, "SAINT_RAPPY": 255, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyItemClasses": {"BARBAROUS_WOLF": 0, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 2, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EPSILON": 3, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GRASS_ASSASSIN": 0, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 1, "RECOBOX": 3, "RECON": 0, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyMesetaRanges": {
"BARBAROUS_WOLF": [100, 180],
"BARBA_RAY": 1300,
@@ -8068,11 +7954,9 @@
"MERILLIA": [60, 65],
"MERILTAS": [70, 85],
"MIGIUM": [120, 185],
"MONEST": 0,
"MORFOS": [50, 80],
"MOTHMANT": [80, 90],
"NAR_LILY": [1000, 2000],
"OLGA_FLOW_2": 0,
"PAN_ARMS": [50, 85],
"POISON_LILY": [60, 65],
"RAG_RAPPY": [85, 100],
@@ -9127,7 +9011,7 @@
[64, 160],
[66, 165]
],
"EnemyItemClasses": {"BARBAROUS_WOLF": 2, "BARBA_RAY": 255, "BARBA_RAY_JOINT": 255, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 1, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EGG_RAPPY": 255, "EPSILON": 3, "GAL_GRYPHON": 255, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GOL_DRAGON": 255, "GRASS_ASSASSIN": 0, "HALLO_RAPPY": 255, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MONEST": 255, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "OLGA_FLOW_2": 255, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 2, "RECOBOX": 3, "RECON": 0, "SAINT_RAPPY": 255, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyItemClasses": {"BARBAROUS_WOLF": 2, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 1, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EPSILON": 3, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GRASS_ASSASSIN": 0, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 2, "RECOBOX": 3, "RECON": 0, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyMesetaRanges": {
"BARBAROUS_WOLF": [150, 260],
"BARBA_RAY": 2800,
@@ -9166,11 +9050,9 @@
"MERILLIA": [90, 110],
"MERILTAS": [100, 135],
"MIGIUM": [55, 135],
"MONEST": 0,
"MORFOS": [151, 171],
"MOTHMANT": [120, 140],
"NAR_LILY": [2000, 2500],
"OLGA_FLOW_2": 0,
"PAN_ARMS": [165, 265],
"POISON_LILY": [90, 110],
"RAG_RAPPY": [85, 150],
@@ -9567,8 +9449,8 @@
[210, 275],
[220, 285]
],
"EnemyItemClasses": {"BARBAROUS_WOLF": 0, "BARBA_RAY": 255, "BARBA_RAY_JOINT": 255, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 2, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EGG_RAPPY": 255, "EPSILON": 3, "GAL_GRYPHON": 255, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GOL_DRAGON": 255, "GRASS_ASSASSIN": 0, "HALLO_RAPPY": 255, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MONEST": 255, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "OLGA_FLOW_2": 255, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 1, "RECOBOX": 3, "RECON": 0, "SAINT_RAPPY": 255, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyTypeDropProbs": {"BARBAROUS_WOLF": 2, "BARBA_RAY": 100, "BARBA_RAY_JOINT": 100, "CHAOS_SORCERER": 7, "DARK_BELRA": 7, "DELBITER": 7, "DELDEPTH": 4, "DELSABER": 4, "DEL_LILY": 7, "DIMENIAN": 2, "DOLMDARL": 4, "DOLMOLM": 2, "DUBCHIC": 7, "EGG_RAPPY": 100, "EPSILON": 7, "GAL_GRYPHON": 100, "GARANZ": 7, "GEE": 2, "GIBBLES": 10, "GILLCHIC": 2, "GI_GUE": 10, "GOL_DRAGON": 100, "GRASS_ASSASSIN": 4, "HALLO_RAPPY": 100, "HIDOOM": 10, "HILDEBEAR": 10, "HILDEBLUE": 100, "ILL_GILL": 7, "LA_DIMENIAN": 4, "LOVE_RAPPY": 100, "MERICARAND": 10, "MERICAROL": 10, "MERICUS": 10, "MERIKLE": 10, "MERILLIA": 2, "MERILTAS": 4, "MIGIUM": 10, "MONEST": 0, "MORFOS": 4, "MOTHMANT": 1, "NAR_LILY": 100, "OLGA_FLOW_2": 0, "PAN_ARMS": 10, "POISON_LILY": 2, "RAG_RAPPY": 2, "RECOBOX": 2, "RECON": 1, "SAINT_RAPPY": 100, "SAVAGE_WOLF": 2, "SINOW_BERILL": 2, "SINOW_SPIGELL": 4, "SINOW_ZELE": 4, "SINOW_ZOA": 2, "SO_DIMENIAN": 4, "UL_GIBBON": 2, "ZOL_GIBBON": 4},
"EnemyItemClasses": {"BARBAROUS_WOLF": 0, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 2, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EPSILON": 3, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GRASS_ASSASSIN": 0, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 1, "RECOBOX": 3, "RECON": 0, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyTypeDropProbs": {"BARBAROUS_WOLF": 2, "BARBA_RAY": 100, "BARBA_RAY_JOINT": 100, "CHAOS_SORCERER": 7, "DARK_BELRA": 7, "DELBITER": 7, "DELDEPTH": 4, "DELSABER": 4, "DEL_LILY": 7, "DIMENIAN": 2, "DOLMDARL": 4, "DOLMOLM": 2, "DUBCHIC": 7, "EGG_RAPPY": 100, "EPSILON": 7, "GAL_GRYPHON": 100, "GARANZ": 7, "GEE": 2, "GIBBLES": 10, "GILLCHIC": 2, "GI_GUE": 10, "GOL_DRAGON": 100, "GRASS_ASSASSIN": 4, "HALLO_RAPPY": 100, "HIDOOM": 10, "HILDEBEAR": 10, "HILDEBLUE": 100, "ILL_GILL": 7, "LA_DIMENIAN": 4, "LOVE_RAPPY": 100, "MERICARAND": 10, "MERICAROL": 10, "MERICUS": 10, "MERIKLE": 10, "MERILLIA": 2, "MERILTAS": 4, "MIGIUM": 10, "MORFOS": 4, "MOTHMANT": 1, "NAR_LILY": 100, "PAN_ARMS": 10, "POISON_LILY": 2, "RAG_RAPPY": 2, "RECOBOX": 2, "RECON": 1, "SAINT_RAPPY": 100, "SAVAGE_WOLF": 2, "SINOW_BERILL": 2, "SINOW_SPIGELL": 4, "SINOW_ZELE": 4, "SINOW_ZOA": 2, "SO_DIMENIAN": 4, "UL_GIBBON": 2, "ZOL_GIBBON": 4},
"GrindProbTable": [
[100, 30, 25, 20],
[0, 30, 23, 15],
@@ -9916,8 +9798,8 @@
[130, 165],
[135, 175]
],
"EnemyMesetaRanges": {"BARBAROUS_WOLF": 0, "BARBA_RAY": 0, "BARBA_RAY_JOINT": 0, "CHAOS_SORCERER": 0, "DARK_BELRA": 0, "DELBITER": 0, "DELDEPTH": 0, "DELSABER": 0, "DEL_LILY": 0, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EGG_RAPPY": 0, "EPSILON": 0, "GAL_GRYPHON": 0, "GARANZ": 0, "GEE": 0, "GIBBLES": 0, "GILLCHIC": 0, "GI_GUE": 0, "GOL_DRAGON": 0, "GRASS_ASSASSIN": 0, "HALLO_RAPPY": 0, "HIDOOM": 0, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 0, "MERICARAND": 0, "MERICAROL": 0, "MERICUS": 0, "MERIKLE": 0, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 0, "MONEST": 0, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 0, "OLGA_FLOW_2": 0, "PAN_ARMS": 0, "POISON_LILY": 0, "RAG_RAPPY": 0, "RECOBOX": 0, "RECON": 0, "SAINT_RAPPY": 0, "SAVAGE_WOLF": 0, "SINOW_BERILL": 0, "SINOW_SPIGELL": 0, "SINOW_ZELE": 0, "SINOW_ZOA": 0, "SO_DIMENIAN": 0, "UL_GIBBON": 0, "ZOL_GIBBON": 0},
"EnemyTypeDropProbs": {"BARBAROUS_WOLF": 20, "BARBA_RAY": 100, "BARBA_RAY_JOINT": 100, "CHAOS_SORCERER": 30, "DARK_BELRA": 30, "DELBITER": 30, "DELDEPTH": 25, "DELSABER": 20, "DEL_LILY": 20, "DIMENIAN": 12, "DOLMDARL": 20, "DOLMOLM": 12, "DUBCHIC": 20, "EGG_RAPPY": 100, "EPSILON": 20, "GAL_GRYPHON": 100, "GARANZ": 30, "GEE": 12, "GIBBLES": 40, "GILLCHIC": 12, "GI_GUE": 40, "GOL_DRAGON": 100, "GRASS_ASSASSIN": 20, "HALLO_RAPPY": 100, "HIDOOM": 40, "HILDEBEAR": 40, "HILDEBLUE": 100, "ILL_GILL": 20, "LA_DIMENIAN": 20, "LOVE_RAPPY": 100, "MERICARAND": 40, "MERICAROL": 40, "MERICUS": 40, "MERIKLE": 40, "MERILLIA": 12, "MERILTAS": 20, "MIGIUM": 40, "MONEST": 0, "MORFOS": 30, "MOTHMANT": 5, "NAR_LILY": 100, "OLGA_FLOW_2": 0, "PAN_ARMS": 40, "POISON_LILY": 12, "RAG_RAPPY": 12, "RECOBOX": 12, "RECON": 5, "SAINT_RAPPY": 100, "SAVAGE_WOLF": 12, "SINOW_BERILL": 20, "SINOW_SPIGELL": 25, "SINOW_ZELE": 20, "SINOW_ZOA": 15, "SO_DIMENIAN": 25, "UL_GIBBON": 12, "ZOL_GIBBON": 20},
"EnemyMesetaRanges": {},
"EnemyTypeDropProbs": {"BARBAROUS_WOLF": 20, "BARBA_RAY": 100, "BARBA_RAY_JOINT": 100, "CHAOS_SORCERER": 30, "DARK_BELRA": 30, "DELBITER": 30, "DELDEPTH": 25, "DELSABER": 20, "DEL_LILY": 20, "DIMENIAN": 12, "DOLMDARL": 20, "DOLMOLM": 12, "DUBCHIC": 20, "EGG_RAPPY": 100, "EPSILON": 20, "GAL_GRYPHON": 100, "GARANZ": 30, "GEE": 12, "GIBBLES": 40, "GILLCHIC": 12, "GI_GUE": 40, "GOL_DRAGON": 100, "GRASS_ASSASSIN": 20, "HALLO_RAPPY": 100, "HIDOOM": 40, "HILDEBEAR": 40, "HILDEBLUE": 100, "ILL_GILL": 20, "LA_DIMENIAN": 20, "LOVE_RAPPY": 100, "MERICARAND": 40, "MERICAROL": 40, "MERICUS": 40, "MERIKLE": 40, "MERILLIA": 12, "MERILTAS": 20, "MIGIUM": 40, "MORFOS": 30, "MOTHMANT": 5, "NAR_LILY": 100, "PAN_ARMS": 40, "POISON_LILY": 12, "RAG_RAPPY": 12, "RECOBOX": 12, "RECON": 5, "SAINT_RAPPY": 100, "SAVAGE_WOLF": 12, "SINOW_BERILL": 20, "SINOW_SPIGELL": 25, "SINOW_ZELE": 20, "SINOW_ZOA": 15, "SO_DIMENIAN": 25, "UL_GIBBON": 12, "ZOL_GIBBON": 20},
"NonRareBonusProbSpec": [
[0, 0, 0, 1, 1, 1, 2, 2, 2, 3],
[255, 255, 0, 0, 0, 1, 1, 1, 2, 2],
@@ -10845,7 +10727,7 @@
[44, 110],
[46, 115]
],
"EnemyItemClasses": {"BARBAROUS_WOLF": 0, "BARBA_RAY": 255, "BARBA_RAY_JOINT": 255, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 2, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EGG_RAPPY": 255, "EPSILON": 3, "GAL_GRYPHON": 255, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GOL_DRAGON": 255, "GRASS_ASSASSIN": 0, "HALLO_RAPPY": 255, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MONEST": 255, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "OLGA_FLOW_2": 255, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 1, "RECOBOX": 3, "RECON": 0, "SAINT_RAPPY": 255, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyItemClasses": {"BARBAROUS_WOLF": 0, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 2, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EPSILON": 3, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GRASS_ASSASSIN": 0, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 1, "RECOBOX": 3, "RECON": 0, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyMesetaRanges": {
"BARBAROUS_WOLF": [100, 180],
"BARBA_RAY": 1300,
@@ -10884,11 +10766,9 @@
"MERILLIA": [60, 65],
"MERILTAS": [70, 85],
"MIGIUM": [120, 185],
"MONEST": 0,
"MORFOS": [50, 80],
"MOTHMANT": [80, 90],
"NAR_LILY": [1000, 2000],
"OLGA_FLOW_2": 0,
"PAN_ARMS": [50, 85],
"POISON_LILY": [60, 65],
"RAG_RAPPY": [85, 100],
@@ -11416,7 +11296,7 @@
[24, 60],
[26, 65]
],
"EnemyItemClasses": {"BARBAROUS_WOLF": 0, "BARBA_RAY": 255, "BARBA_RAY_JOINT": 255, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 1, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EGG_RAPPY": 255, "EPSILON": 3, "GAL_GRYPHON": 255, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GOL_DRAGON": 255, "GRASS_ASSASSIN": 0, "HALLO_RAPPY": 255, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MONEST": 255, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "OLGA_FLOW_2": 255, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 1, "RECOBOX": 3, "RECON": 0, "SAINT_RAPPY": 255, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 2, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyItemClasses": {"BARBAROUS_WOLF": 0, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 1, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EPSILON": 3, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GRASS_ASSASSIN": 0, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 1, "RECOBOX": 3, "RECON": 0, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 2, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyMesetaRanges": {
"BARBAROUS_WOLF": [30, 45],
"BARBA_RAY": 400,
@@ -11455,11 +11335,9 @@
"MERILLIA": [40, 50],
"MERILTAS": [45, 60],
"MIGIUM": [45, 65],
"MONEST": 0,
"MORFOS": [100, 130],
"MOTHMANT": [3, 6],
"NAR_LILY": [500, 1200],
"OLGA_FLOW_2": 0,
"PAN_ARMS": [24, 44],
"POISON_LILY": [4, 10],
"RAG_RAPPY": [18, 20],
@@ -11475,7 +11353,7 @@
"UL_GIBBON": [37, 55],
"ZOL_GIBBON": [45, 60]
},
"EnemyTypeDropProbs": {"BARBAROUS_WOLF": 30, "BARBA_RAY": 100, "BARBA_RAY_JOINT": 100, "CHAOS_SORCERER": 25, "DARK_BELRA": 35, "DELBITER": 80, "DELDEPTH": 40, "DELSABER": 25, "DEL_LILY": 35, "DIMENIAN": 30, "DOLMDARL": 35, "DOLMOLM": 30, "DUBCHIC": 30, "EGG_RAPPY": 100, "EPSILON": 30, "GAL_GRYPHON": 100, "GARANZ": 50, "GEE": 30, "GIBBLES": 80, "GILLCHIC": 30, "GI_GUE": 80, "GOL_DRAGON": 100, "GRASS_ASSASSIN": 40, "HALLO_RAPPY": 100, "HIDOOM": 45, "HILDEBEAR": 80, "HILDEBLUE": 100, "ILL_GILL": 40, "LA_DIMENIAN": 31, "LOVE_RAPPY": 100, "MERICARAND": 80, "MERICAROL": 80, "MERICUS": 80, "MERIKLE": 80, "MERILLIA": 30, "MERILTAS": 35, "MIGIUM": 45, "MONEST": 0, "MORFOS": 40, "MOTHMANT": 20, "NAR_LILY": 100, "OLGA_FLOW_2": 0, "PAN_ARMS": 50, "POISON_LILY": 30, "RAG_RAPPY": 100, "RECOBOX": 35, "RECON": 10, "SAINT_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BERILL": 40, "SINOW_SPIGELL": 45, "SINOW_ZELE": 35, "SINOW_ZOA": 30, "SO_DIMENIAN": 30, "UL_GIBBON": 30, "ZOL_GIBBON": 35},
"EnemyTypeDropProbs": {"BARBAROUS_WOLF": 30, "BARBA_RAY": 100, "BARBA_RAY_JOINT": 100, "CHAOS_SORCERER": 25, "DARK_BELRA": 35, "DELBITER": 80, "DELDEPTH": 40, "DELSABER": 25, "DEL_LILY": 35, "DIMENIAN": 30, "DOLMDARL": 35, "DOLMOLM": 30, "DUBCHIC": 30, "EGG_RAPPY": 100, "EPSILON": 30, "GAL_GRYPHON": 100, "GARANZ": 50, "GEE": 30, "GIBBLES": 80, "GILLCHIC": 30, "GI_GUE": 80, "GOL_DRAGON": 100, "GRASS_ASSASSIN": 40, "HALLO_RAPPY": 100, "HIDOOM": 45, "HILDEBEAR": 80, "HILDEBLUE": 100, "ILL_GILL": 40, "LA_DIMENIAN": 31, "LOVE_RAPPY": 100, "MERICARAND": 80, "MERICAROL": 80, "MERICUS": 80, "MERIKLE": 80, "MERILLIA": 30, "MERILTAS": 35, "MIGIUM": 45, "MORFOS": 40, "MOTHMANT": 20, "NAR_LILY": 100, "PAN_ARMS": 50, "POISON_LILY": 30, "RAG_RAPPY": 100, "RECOBOX": 35, "RECON": 10, "SAINT_RAPPY": 100, "SAVAGE_WOLF": 30, "SINOW_BERILL": 40, "SINOW_SPIGELL": 45, "SINOW_ZELE": 35, "SINOW_ZOA": 30, "SO_DIMENIAN": 30, "UL_GIBBON": 30, "ZOL_GIBBON": 35},
"GrindProbTable": [
[100, 35, 30, 25],
[0, 25, 20, 15],
@@ -12343,7 +12221,7 @@
[64, 160],
[66, 165]
],
"EnemyItemClasses": {"BARBAROUS_WOLF": 2, "BARBA_RAY": 255, "BARBA_RAY_JOINT": 255, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 1, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EGG_RAPPY": 255, "EPSILON": 3, "GAL_GRYPHON": 255, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GOL_DRAGON": 255, "GRASS_ASSASSIN": 0, "HALLO_RAPPY": 255, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MONEST": 255, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "OLGA_FLOW_2": 255, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 2, "RECOBOX": 3, "RECON": 0, "SAINT_RAPPY": 255, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyItemClasses": {"BARBAROUS_WOLF": 2, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 1, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EPSILON": 3, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GRASS_ASSASSIN": 0, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 2, "RECOBOX": 3, "RECON": 0, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyMesetaRanges": {
"BARBAROUS_WOLF": [150, 260],
"BARBA_RAY": 2800,
@@ -12382,11 +12260,9 @@
"MERILLIA": [90, 110],
"MERILTAS": [100, 135],
"MIGIUM": [55, 135],
"MONEST": 0,
"MORFOS": [151, 171],
"MOTHMANT": [120, 140],
"NAR_LILY": [2000, 2500],
"OLGA_FLOW_2": 0,
"PAN_ARMS": [165, 265],
"POISON_LILY": [90, 110],
"RAG_RAPPY": [85, 150],
@@ -12841,7 +12717,7 @@
[44, 110],
[46, 115]
],
"EnemyItemClasses": {"BARBAROUS_WOLF": 0, "BARBA_RAY": 255, "BARBA_RAY_JOINT": 255, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 2, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EGG_RAPPY": 255, "EPSILON": 3, "GAL_GRYPHON": 255, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GOL_DRAGON": 255, "GRASS_ASSASSIN": 0, "HALLO_RAPPY": 255, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MONEST": 255, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "OLGA_FLOW_2": 255, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 1, "RECOBOX": 3, "RECON": 0, "SAINT_RAPPY": 255, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyItemClasses": {"BARBAROUS_WOLF": 0, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 2, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EPSILON": 3, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GRASS_ASSASSIN": 0, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 1, "RECOBOX": 3, "RECON": 0, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyMesetaRanges": {
"BARBAROUS_WOLF": [100, 180],
"BARBA_RAY": 1300,
@@ -12880,11 +12756,9 @@
"MERILLIA": [60, 65],
"MERILTAS": [70, 85],
"MIGIUM": [120, 185],
"MONEST": 0,
"MORFOS": [50, 80],
"MOTHMANT": [80, 90],
"NAR_LILY": [1000, 2000],
"OLGA_FLOW_2": 0,
"PAN_ARMS": [50, 85],
"POISON_LILY": [60, 65],
"RAG_RAPPY": [85, 100],
@@ -13939,7 +13813,7 @@
[64, 160],
[66, 165]
],
"EnemyItemClasses": {"BARBAROUS_WOLF": 2, "BARBA_RAY": 255, "BARBA_RAY_JOINT": 255, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 1, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EGG_RAPPY": 255, "EPSILON": 3, "GAL_GRYPHON": 255, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GOL_DRAGON": 255, "GRASS_ASSASSIN": 0, "HALLO_RAPPY": 255, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MONEST": 255, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "OLGA_FLOW_2": 255, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 2, "RECOBOX": 3, "RECON": 0, "SAINT_RAPPY": 255, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyItemClasses": {"BARBAROUS_WOLF": 2, "CHAOS_SORCERER": 3, "DARK_BELRA": 1, "DELBITER": 0, "DELDEPTH": 2, "DELSABER": 1, "DEL_LILY": 1, "DIMENIAN": 0, "DOLMDARL": 0, "DOLMOLM": 0, "DUBCHIC": 0, "EPSILON": 3, "GARANZ": 2, "GEE": 0, "GIBBLES": 3, "GILLCHIC": 0, "GI_GUE": 2, "GRASS_ASSASSIN": 0, "HIDOOM": 1, "HILDEBEAR": 0, "HILDEBLUE": 0, "ILL_GILL": 0, "LA_DIMENIAN": 0, "LOVE_RAPPY": 2, "MERICARAND": 3, "MERICAROL": 3, "MERICUS": 2, "MERIKLE": 1, "MERILLIA": 0, "MERILTAS": 0, "MIGIUM": 3, "MORFOS": 0, "MOTHMANT": 0, "NAR_LILY": 3, "PAN_ARMS": 2, "POISON_LILY": 1, "RAG_RAPPY": 2, "RECOBOX": 3, "RECON": 0, "SAVAGE_WOLF": 1, "SINOW_BERILL": 0, "SINOW_SPIGELL": 1, "SINOW_ZELE": 1, "SINOW_ZOA": 0, "SO_DIMENIAN": 1, "UL_GIBBON": 0, "ZOL_GIBBON": 2},
"EnemyMesetaRanges": {
"BARBAROUS_WOLF": [150, 260],
"BARBA_RAY": 2800,
@@ -13978,11 +13852,9 @@
"MERILLIA": [90, 110],
"MERILTAS": [100, 135],
"MIGIUM": [55, 135],
"MONEST": 0,
"MORFOS": [151, 171],
"MOTHMANT": [120, 140],
"NAR_LILY": [2000, 2500],
"OLGA_FLOW_2": 0,
"PAN_ARMS": [165, 265],
"POISON_LILY": [90, 110],
"RAG_RAPPY": [85, 150],
+138 -83
View File
@@ -49,7 +49,7 @@
["13/4096", 0x000B03]
],
"CANADINE": [["1/128", 0x000A04]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000A04]],
"CANANE": [["7/256", 0x000B04]],
"CHAOS_BRINGER": [["1/64", 0x002C00]],
"CHAOS_SORCERER": [["7/256", 0x000B04]],
@@ -125,7 +125,7 @@
["7/8192", 0x030E18]
],
"CANADINE": [["1/128", 0x000605]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000605]],
"CANANE": [["7/256", 0x000705]],
"CHAOS_BRINGER": [["1/64", 0x002C00]],
"CHAOS_SORCERER": [["7/256", 0x000705]],
@@ -212,7 +212,7 @@
["7/8192", 0x030E18]
],
"CANADINE": [["1/128", 0x000A04]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000A04]],
"CANANE": [["7/256", 0x000305]],
"CHAOS_BRINGER": [["1/64", 0x002C00]],
"CHAOS_SORCERER": [["7/256", 0x000305]],
@@ -304,7 +304,7 @@
["3/2048", 0x030E24]
],
"CANADINE": [["1/128", 0x000A04]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000A04]],
"CANANE": [["7/256", 0x000C04]],
"CHAOS_BRINGER": [["1/64", 0x002C00]],
"CHAOS_SORCERER": [["7/256", 0x000C04]],
@@ -385,7 +385,7 @@
["7/8192", 0x030E18]
],
"CANADINE": [["1/128", 0x000A04]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000A04]],
"CANANE": [["7/256", 0x000805]],
"CHAOS_BRINGER": [["1/64", 0x002C00]],
"CHAOS_SORCERER": [["7/256", 0x000305]],
@@ -471,7 +471,7 @@
["7/8192", 0x01024B]
],
"CANADINE": [["1/128", 0x000605]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000605]],
"CANANE": [["7/256", 0x000505]],
"CHAOS_BRINGER": [["1/64", 0x000506]],
"CHAOS_SORCERER": [["7/256", 0x000905]],
@@ -551,7 +551,7 @@
["7/8192", 0x01024B]
],
"CANADINE": [["1/128", 0x000105]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000105]],
"CANANE": [["7/256", 0x000705]],
"CHAOS_BRINGER": [["1/64", 0x000F00]],
"CHAOS_SORCERER": [["7/256", 0x000205]],
@@ -594,43 +594,18 @@
"BARBAROUS_WOLF": [["1/128", 0x000605]],
"BOOMA": [["1/128", 0x030D0F]],
"BULCLAW": [["1/128", 0x000905]],
"Box-Cave1": [
["9/536870912", 0x030E1C],
["13/134217728", 0x030E16],
["5/134217728", 0x01024A]
],
"Box-Cave3": [
["7/1073741824", 0x020000],
["7/1073741824", 0x000404],
["3/536870912", 0x000404],
["13/2147483648", 0x030E1B],
["13/2147483648", 0x030E1D],
["13/2147483648", 0x030E1F],
["7/1073741824", 0x030E17],
["7/1073741824", 0x01024B]
],
"Box-DarkFalz": [["1/268435456", 0x01024C]],
"Box-Lobby": [["1/268435456", 0x01024D]],
"Box-Mine1": [["3/536870912", 0x020000]],
"Box-Pioneer2": [
["7/2147483648", 0x000904],
["7/2147483648", 0x000904]
],
"Box-Ruins1": [
["3/536870912", 0x030E1E],
["13/2147483648", 0x030E18],
["13/2147483648", 0x030E19]
],
"Box-VolOpt": [
["5/1073741824", 0x000905],
["7/2147483648", 0x000405],
["7/1073741824", 0x01024C],
["7/2147483648", 0x01024C],
["1/268435456", 0x030F00],
["13/2147483648", 0x030E1A]
],
"Box-Forest1": [[0x00380000, 0x030E1D]],
"Box-Forest2": [[0x00380000, 0x030E1A], [0x00380000, 0x030E18]],
"Box-Cave1": [[0x00380000, 0x030E16]],
"Box-Cave2": [[0x00380000, 0x030F00], [0x00380000, 0x01024D]],
"Box-Cave3": [[0x00900000, 0x000904], [0x00900000, 0x000404], [0x00380000, 0x030E1B]],
"Box-Mine1": [[0x02400000, 0x020000], [0x00240000, 0x01024C], [0x00380000, 0x030E1E]],
"Box-Mine2": [[0x01000000, 0x020000], [0x00B00000, 0x000904], [0x00B00000, 0x000404], [0x00240000, 0x01024C], [0x00380000, 0x030E1F], [0x00380000, 0x01024A]],
"Box-Ruins1": [[0x00380000, 0x030E1C], [0x00380000, 0x030E19]],
"Box-Ruins2": [[0x00380000, 0x01024C], [0x00380000, 0x030E17]],
"Box-Ruins3": [[0x00D00000, 0x000905], [0x00D00000, 0x000405], [0x00380000, 0x01024B]],
"CANADINE": [["1/128", 0x000605]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000605]],
"CANANE": [["7/256", 0x000405]],
"CHAOS_BRINGER": [["1/64", 0x001003]],
"CHAOS_SORCERER": [["7/256", 0x000905]],
@@ -715,7 +690,7 @@
["7/8192", 0x01024B]
],
"CANADINE": [["1/128", 0x000605]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000605]],
"CANANE": [["7/256", 0x030A00]],
"CHAOS_BRINGER": [["1/64", 0x000506]],
"CHAOS_SORCERER": [["7/256", 0x000505]],
@@ -793,7 +768,7 @@
],
"Box-Ruins3": [["7/8192", 0x030E24]],
"CANADINE": [["1/128", 0x000A04]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000A04]],
"CANANE": [["7/256", 0x000C04]],
"CHAOS_BRINGER": [["1/64", 0x006800]],
"CHAOS_SORCERER": [["7/256", 0x000905]],
@@ -860,7 +835,7 @@
["13/16384", 0x000B02]
],
"CANADINE": [["9/16384", 0x01024E]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["9/16384", 0x01024E]],
"CANANE": [["9/16", 0x030002]],
"CHAOS_BRINGER": [["9/16", 0x030002]],
"CHAOS_SORCERER": [["9/16", 0x030300]],
@@ -922,7 +897,7 @@
["7/8192", 0x030E24]
],
"CANADINE": [["9/16384", 0x01024D]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["9/16384", 0x01024D]],
"CANANE": [["11/8192", 0x030A00]],
"CHAOS_BRINGER": [["9/16", 0x030002]],
"CHAOS_SORCERER": [["7/8", 0x030B02]],
@@ -990,7 +965,7 @@
["7/8192", 0x030E24]
],
"CANADINE": [["9/16384", 0x01024E]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["9/16384", 0x01024E]],
"CANANE": [["9/16", 0x030300]],
"CHAOS_BRINGER": [["9/16", 0x030002]],
"CHAOS_SORCERER": [["9/16", 0x030002]],
@@ -1064,7 +1039,7 @@
["7/8192", 0x030E24]
],
"CANADINE": [["9/16384", 0x01024E]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["9/16384", 0x01024E]],
"CANANE": [["9/16", 0x030300]],
"CHAOS_BRINGER": [["9/16", 0x030002]],
"CHAOS_SORCERER": [["9/16", 0x030002]],
@@ -1129,7 +1104,7 @@
["13/16384", 0x000303]
],
"CANADINE": [["9/16384", 0x01024E]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["9/16384", 0x01024E]],
"CANANE": [["9/16", 0x030300]],
"CHAOS_BRINGER": [["9/16", 0x030300]],
"CHAOS_SORCERER": [["9/16", 0x030002]],
@@ -1193,7 +1168,7 @@
["7/8192", 0x030E24]
],
"CANADINE": [["9/16384", 0x01024E]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["9/16384", 0x01024E]],
"CANANE": [["9/16", 0x030300]],
"CHAOS_BRINGER": [["9/16", 0x030002]],
"CHAOS_SORCERER": [["9/16", 0x030002]],
@@ -1316,7 +1291,7 @@
["13/16384", 0x000403]
],
"CANADINE": [["9/16384", 0x01024C]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["9/16384", 0x01024C]],
"CANANE": [["9/16", 0x030002]],
"CHAOS_BRINGER": [["9/16", 0x030002]],
"CHAOS_SORCERER": [["9/16", 0x030B01]],
@@ -1429,7 +1404,7 @@
"Box-Ruins2": [["7/8192", 0x030E25]],
"Box-Ruins3": [["7/8192", 0x030E24]],
"CANADINE": [["9/16384", 0x01024E]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["9/16384", 0x01024E]],
"CANANE": [["9/16", 0x030300]],
"CHAOS_BRINGER": [["9/16", 0x030002]],
"CHAOS_SORCERER": [["9/16", 0x030002]],
@@ -1515,14 +1490,14 @@
["11/4096", 0x000A06]
],
"CANADINE": [["1/64", 0x000B06]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/64", 0x000B06]],
"CANANE": [["1/1024", 0x010216]],
"CHAOS_BRINGER": [["1/1024", 0x010134]],
"CHAOS_SORCERER": [["13/4096", 0x030D00]],
"CLAW": [["1/64", 0x000407]],
"DARK_BELRA": [["13/131072", 0x030E0A]],
"DARK_FALZ_2": [["1/64", 0x010232]],
"DARK_FALZ_3": [],
"DARK_FALZ_3": [["1/64", 0x010232]],
"DARK_GUNNER": [["13/4096", 0x004100]],
"DELSABER": [["13/4096", 0x009400]],
"DE_ROL_LE": [["13/64", 0x000B06]],
@@ -1606,14 +1581,14 @@
["7/8192", 0x030E24]
],
"CANADINE": [["1/64", 0x000707]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/64", 0x000707]],
"CANANE": [["1/1024", 0x01022B]],
"CHAOS_BRINGER": [["13/4096", 0x030D04]],
"CHAOS_SORCERER": [["5/524288", 0x001D00]],
"CLAW": [["1/64", 0x000307]],
"DARK_BELRA": [["13/4096", 0x030D0B]],
"DARK_FALZ_2": [["1/64", 0x010231]],
"DARK_FALZ_3": [],
"DARK_FALZ_3": [["1/64", 0x010231]],
"DARK_GUNNER": [["13/4096", 0x004C00]],
"DELSABER": [["13/131072", 0x001001]],
"DE_ROL_LE": [["7/256", 0x001300]],
@@ -1697,14 +1672,14 @@
["11/4096", 0x000A06]
],
"CANADINE": [["1/64", 0x000207]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/64", 0x000207]],
"CANANE": [["13/4096", 0x008902]],
"CHAOS_BRINGER": [["13/131072", 0x005A00]],
"CHAOS_SORCERER": [["13/4096", 0x030D00]],
"CLAW": [["1/64", 0x000D01]],
"DARK_BELRA": [["13/4096", 0x009200]],
"DARK_FALZ_2": [["1/64", 0x010231]],
"DARK_FALZ_3": [],
"DARK_FALZ_3": [["1/64", 0x010231]],
"DARK_GUNNER": [["13/4096", 0x009400]],
"DELSABER": [["5/1024", 0x030D05]],
"DE_ROL_LE": [["13/64", 0x000207]],
@@ -1794,14 +1769,14 @@
["11/4096", 0x000A06]
],
"CANADINE": [["1/64", 0x000C06]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/64", 0x000C06]],
"CANANE": [["1/1024", 0x010134]],
"CHAOS_BRINGER": [["13/4096", 0x030D04]],
"CHAOS_SORCERER": [["13/4096", 0x030D00]],
"CLAW": [["13/4096", 0x000D01]],
"DARK_BELRA": [["13/4096", 0x009200]],
"DARK_FALZ_2": [["1/64", 0x010231]],
"DARK_FALZ_3": [],
"DARK_FALZ_3": [["1/64", 0x010231]],
"DARK_GUNNER": [["13/4096", 0x009400]],
"DELSABER": [["5/1024", 0x030D05]],
"DE_ROL_LE": [["7/256", 0x001300]],
@@ -1883,14 +1858,14 @@
["11/4096", 0x000A06]
],
"CANADINE": [["1/1024", 0x01022B]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/1024", 0x01022B]],
"CANANE": [["13/4096", 0x006C00]],
"CHAOS_BRINGER": [["13/4096", 0x030D04]],
"CHAOS_SORCERER": [["13/131072", 0x030E0A]],
"CLAW": [["1/64", 0x008C01]],
"DARK_BELRA": [["13/4096", 0x030D0B]],
"DARK_FALZ_2": [["1/64", 0x010232]],
"DARK_FALZ_3": [],
"DARK_FALZ_3": [["1/64", 0x010232]],
"DARK_GUNNER": [["1/1024", 0x010216]],
"DELSABER": [["5/1024", 0x030D05]],
"DE_ROL_LE": [["13/64", 0x000307]],
@@ -1978,14 +1953,14 @@
["7/8192", 0x030E24]
],
"CANADINE": [["1/64", 0x009005]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/64", 0x009005]],
"CANANE": [["13/4096", 0x030E08]],
"CHAOS_BRINGER": [["13/4096", 0x001200]],
"CHAOS_SORCERER": [["5/524288", 0x001D00]],
"CLAW": [["13/4096", 0x010117]],
"DARK_BELRA": [["13/4096", 0x002E00]],
"DARK_FALZ_2": [["1/64", 0x010227]],
"DARK_FALZ_3": [],
"DARK_FALZ_3": [["1/64", 0x010227]],
"DARK_GUNNER": [["1/512", 0x010134]],
"DELSABER": [["5/1024", 0x030D05]],
"DE_ROL_LE": [["13/64", 0x000507]],
@@ -2069,14 +2044,14 @@
["11/4096", 0x000A06]
],
"CANADINE": [["1/64", 0x000207]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/64", 0x000207]],
"CANANE": [["13/4096", 0x008902]],
"CHAOS_BRINGER": [["13/4096", 0x002E00]],
"CHAOS_SORCERER": [["5/524288", 0x001D00]],
"CLAW": [["1/64", 0x000D01]],
"DARK_BELRA": [["1/1024", 0x010133]],
"DARK_FALZ_2": [["1/64", 0x010227]],
"DARK_FALZ_3": [],
"DARK_FALZ_3": [["1/64", 0x010227]],
"DARK_GUNNER": [["13/131072", 0x009302]],
"DELSABER": [["5/1024", 0x030D05]],
"DE_ROL_LE": [["13/64", 0x000207]],
@@ -2158,14 +2133,14 @@
["11/4096", 0x000A06]
],
"CANADINE": [["13/131072", 0x009300]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["13/131072", 0x009300]],
"CANANE": [["5/1024", 0x010226]],
"CHAOS_BRINGER": [["13/4096", 0x030D04]],
"CHAOS_SORCERER": [["13/131072", 0x001F00]],
"CLAW": [["1/64", 0x000D01]],
"DARK_BELRA": [["13/4096", 0x030D0B]],
"DARK_FALZ_2": [["1/64", 0x010227]],
"DARK_FALZ_3": [],
"DARK_FALZ_3": [["1/64", 0x010227]],
"DARK_GUNNER": [["1/1024", 0x01012B]],
"DELSABER": [["5/1024", 0x030D05]],
"DE_ROL_LE": [["13/64", 0x000407]],
@@ -2245,14 +2220,14 @@
["11/4096", 0x000A06]
],
"CANADINE": [["13/131072", 0x009309]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["13/131072", 0x009309]],
"CANANE": [["13/4096", 0x008902]],
"CHAOS_BRINGER": [["1/512", 0x010232]],
"CHAOS_SORCERER": [["13/131072", 0x004200]],
"CLAW": [["1/512", 0x010332]],
"DARK_BELRA": [["13/4096", 0x030D0B]],
"DARK_FALZ_2": [["7/256", 0x010227]],
"DARK_FALZ_3": [],
"DARK_FALZ_3": [["7/256", 0x010227]],
"DARK_GUNNER": [["13/4096", 0x004100]],
"DELSABER": [["13/4096", 0x009400]],
"DE_ROL_LE": [["13/64", 0x000507]],
@@ -2335,14 +2310,14 @@
["7/8192", 0x030E24]
],
"CANADINE": [["13/131072", 0x008D00]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["13/131072", 0x008D00]],
"CANANE": [["13/4096", 0x030E08]],
"CHAOS_BRINGER": [["13/4096", 0x030D04]],
"CHAOS_SORCERER": [["13/4096", 0x030D00]],
"CLAW": [["1/64", 0x009007]],
"DARK_BELRA": [["13/4096", 0x030D0B]],
"DARK_FALZ_2": [["1/64", 0x010232]],
"DARK_FALZ_3": [],
"DARK_FALZ_3": [["1/64", 0x010232]],
"DARK_GUNNER": [["13/4096", 0x009200]],
"DELSABER": [["1/64", 0x030D03]],
"DE_ROL_LE": [["7/256", 0x001300]],
@@ -2428,7 +2403,7 @@
["7/8192", 0x01024B]
],
"CANADINE": [["1/128", 0x000B05]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000B05]],
"CANANE": [["1/128", 0x010223]],
"CHAOS_BRINGER": [["7/256", 0x000407]],
"CHAOS_SORCERER": [["7/256", 0x008C01]],
@@ -2514,7 +2489,7 @@
["7/8192", 0x030F00]
],
"CANADINE": [["1/128", 0x000706]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000706]],
"CANANE": [["1/128", 0x010223]],
"CHAOS_BRINGER": [["7/256", 0x030500]],
"CHAOS_SORCERER": [["7/256", 0x008C00]],
@@ -2602,7 +2577,7 @@
["13/4096", 0x000A05]
],
"CANADINE": [["1/128", 0x000306]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000306]],
"CANANE": [["1/128", 0x010223]],
"CHAOS_BRINGER": [["7/256", 0x000307]],
"CHAOS_SORCERER": [["7/256", 0x008C01]],
@@ -2688,7 +2663,7 @@
["13/4096", 0x000606]
],
"CANADINE": [["1/128", 0x000C05]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000C05]],
"CANANE": [["1/128", 0x010223]],
"CHAOS_BRINGER": [["7/256", 0x000C06]],
"CHAOS_SORCERER": [["7/256", 0x008C01]],
@@ -2774,7 +2749,7 @@
["13/4096", 0x000A05]
],
"CANADINE": [["1/128", 0x000806]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000806]],
"CANANE": [["1/128", 0x010223]],
"CHAOS_BRINGER": [["7/256", 0x000807]],
"CHAOS_SORCERER": [["7/256", 0x008C00]],
@@ -2860,7 +2835,7 @@
["7/8192", 0x030E25]
],
"CANADINE": [["1/128", 0x000506]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000506]],
"CANANE": [["1/128", 0x010223]],
"CHAOS_BRINGER": [["7/256", 0x000506]],
"CHAOS_SORCERER": [["7/256", 0x008C00]],
@@ -2944,7 +2919,7 @@
["7/8192", 0x030F00]
],
"CANADINE": [["1/128", 0x000705]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000705]],
"CANANE": [["1/128", 0x010226]],
"CHAOS_BRINGER": [["7/256", 0x000207]],
"CHAOS_SORCERER": [["7/256", 0x008C00]],
@@ -3030,7 +3005,7 @@
["13/4096", 0x000A05]
],
"CANADINE": [["1/128", 0x000405]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000405]],
"CANANE": [["1/1024", 0x010134]],
"CHAOS_BRINGER": [["7/256", 0x000907]],
"CHAOS_SORCERER": [["7/256", 0x008C00]],
@@ -3116,7 +3091,7 @@
["7/8192", 0x01024B]
],
"CANADINE": [["1/128", 0x000506]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000506]],
"CANANE": [["1/128", 0x010221]],
"CHAOS_BRINGER": [["7/256", 0x000506]],
"CHAOS_SORCERER": [["7/256", 0x008C00]],
@@ -3208,7 +3183,7 @@
["13/4096", 0x000505]
],
"CANADINE": [["1/128", 0x000806]],
"CANADINE_GROUP": [],
"CANADINE_GROUP": [["1/128", 0x000806]],
"CANANE": [["1/128", 0x010223]],
"CHAOS_BRINGER": [["7/256", 0x000B05]],
"CHAOS_SORCERER": [["7/256", 0x008C00]],
@@ -3253,6 +3228,7 @@
"Bluefull": {
"BARBAROUS_WOLF": [["1/128", 0x000B04]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000404],
["9/4096", 0x000B02],
@@ -3310,6 +3286,7 @@
"LA_DIMENIAN": [["1/128", 0x000105]],
"LOVE_RAPPY": [["7/8", 0x002600]],
"MERICAROL": [["7/256", 0x000406]],
"MERICARAND": [["7/256", 0x000406]],
"MERICUS": [["7/256", 0x000406]],
"MERIKLE": [["7/256", 0x000406]],
"MERILLIA": [["1/128", 0x000105]],
@@ -3333,6 +3310,7 @@
"Greennill": {
"BARBAROUS_WOLF": [["1/128", 0x000305]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000704],
["9/4096", 0x000304]
@@ -3387,6 +3365,7 @@
"LA_DIMENIAN": [["1/128", 0x000605]],
"LOVE_RAPPY": [["7/8", 0x002300]],
"MERICAROL": [["7/256", 0x000706]],
"MERICARAND": [["7/256", 0x000706]],
"MERICUS": [["7/256", 0x000706]],
"MERIKLE": [["7/256", 0x000706]],
"MERILLIA": [["1/128", 0x000606]],
@@ -3410,6 +3389,7 @@
"Oran": {
"BARBAROUS_WOLF": [["1/128", 0x000205]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000304],
["9/4096", 0x000204],
@@ -3479,6 +3459,7 @@
"LA_DIMENIAN": [["1/128", 0x000105]],
"LOVE_RAPPY": [["7/8", 0x002300]],
"MERICAROL": [["7/256", 0x000306]],
"MERICARAND": [["7/256", 0x000306]],
"MERICUS": [["7/256", 0x000306]],
"MERIKLE": [["7/256", 0x000306]],
"MERILLIA": [["1/128", 0x000105]],
@@ -3502,6 +3483,7 @@
"Pinkal": {
"BARBAROUS_WOLF": [["1/128", 0x000405]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000C03],
["9/4096", 0x000404],
@@ -3572,6 +3554,7 @@
"LA_DIMENIAN": [["1/128", 0x000A04]],
"LOVE_RAPPY": [["7/8", 0x002300]],
"MERICAROL": [["7/256", 0x000C05]],
"MERICARAND": [["7/256", 0x000C05]],
"MERICUS": [["7/256", 0x000C05]],
"MERIKLE": [["7/256", 0x000C05]],
"MERILLIA": [["1/128", 0x000A04]],
@@ -3595,6 +3578,7 @@
"Purplenum": {
"BARBAROUS_WOLF": [["1/128", 0x000305]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000804],
["9/4096", 0x000304],
@@ -3656,6 +3640,7 @@
"LA_DIMENIAN": [["1/128", 0x000605]],
"LOVE_RAPPY": [["7/8", 0x004900]],
"MERICAROL": [["7/256", 0x000806]],
"MERICARAND": [["7/256", 0x000806]],
"MERICUS": [["7/256", 0x000806]],
"MERIKLE": [["7/256", 0x000806]],
"MERILLIA": [["1/128", 0x000605]],
@@ -3679,6 +3664,7 @@
"Redria": {
"BARBAROUS_WOLF": [["1/128", 0x000905]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000504],
["9/4096", 0x000904],
@@ -3751,6 +3737,7 @@
"LA_DIMENIAN": [["1/128", 0x000A04]],
"LOVE_RAPPY": [["7/8", 0x002600]],
"MERICAROL": [["7/256", 0x000506]],
"MERICARAND": [["7/256", 0x000506]],
"MERICUS": [["7/256", 0x000506]],
"MERIKLE": [["7/256", 0x000506]],
"MERILLIA": [["1/128", 0x000105]],
@@ -3774,6 +3761,7 @@
"Skyly": {
"BARBAROUS_WOLF": [["1/128", 0x000705]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000204],
["9/4096", 0x000704],
@@ -3830,6 +3818,7 @@
"LA_DIMENIAN": [["1/128", 0x000105]],
"LOVE_RAPPY": [["7/8", 0x002300]],
"MERICAROL": [["7/256", 0x000206]],
"MERICARAND": [["7/256", 0x000206]],
"MERICUS": [["7/256", 0x000206]],
"MERIKLE": [["7/256", 0x000206]],
"MERILLIA": [["1/128", 0x000105]],
@@ -3853,6 +3842,7 @@
"Viridia": {
"BARBAROUS_WOLF": [["1/128", 0x000405]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000904],
["9/4096", 0x000404],
@@ -3914,6 +3904,7 @@
"LA_DIMENIAN": [["1/128", 0x000105]],
"LOVE_RAPPY": [["7/8", 0x002000]],
"MERICAROL": [["7/256", 0x000906]],
"MERICARAND": [["7/256", 0x000906]],
"MERICUS": [["7/256", 0x000906]],
"MERIKLE": [["7/256", 0x000906]],
"MERILLIA": [["1/128", 0x000105]],
@@ -3937,6 +3928,7 @@
"Whitill": {
"BARBAROUS_WOLF": [["1/128", 0x000805]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000504],
["9/4096", 0x000804],
@@ -4002,6 +3994,7 @@
"LA_DIMENIAN": [["1/128", 0x000605]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/256", 0x000506]],
"MERICARAND": [["7/256", 0x000506]],
"MERICUS": [["7/256", 0x000506]],
"MERIKLE": [["7/256", 0x000506]],
"MERILLIA": [["1/128", 0x000605]],
@@ -4025,6 +4018,7 @@
"Yellowboze": {
"BARBAROUS_WOLF": [["1/128", 0x000205]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [["7/8192", 0x01024C]],
"Box-Mountain": [["1/256", 0x020000]],
"Box-JungleEast": [
@@ -4073,6 +4067,7 @@
"LA_DIMENIAN": [["1/128", 0x000605]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/256", 0x000506]],
"MERICARAND": [["7/256", 0x000506]],
"MERICUS": [["7/256", 0x000306]],
"MERIKLE": [["7/256", 0x000406]],
"MERILLIA": [["1/128", 0x000105]],
@@ -4098,6 +4093,7 @@
"Bluefull": {
"BARBAROUS_WOLF": [["9/16384", 0x030A00]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000402],
["9/16384", 0x000B01],
@@ -4150,6 +4146,7 @@
"LA_DIMENIAN": [["9/16384", 0x030A00]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["9/16384", 0x030A00]],
@@ -4169,6 +4166,7 @@
"Greennill": {
"BARBAROUS_WOLF": [["3/8192", 0x030F00]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000702],
["9/16384", 0x000302]
@@ -4219,6 +4217,7 @@
"LA_DIMENIAN": [["9/16384", 0x030B06]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILTAS": [["9/16384", 0x030A00]],
@@ -4238,6 +4237,7 @@
"Oran": {
"BARBAROUS_WOLF": [["9/16384", 0x030A00]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000302],
["9/16384", 0x000202],
@@ -4288,6 +4288,7 @@
"LA_DIMENIAN": [["11/8192", 0x030B05]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["11/8192", 0x030B00]],
@@ -4309,6 +4310,7 @@
"Pinkal": {
"BARBAROUS_WOLF": [["11/8192", 0x030B01]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000C01],
["9/16384", 0x000402],
@@ -4372,6 +4374,7 @@
"LA_DIMENIAN": [["11/8192", 0x030B01]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["11/8192", 0x030B00]],
@@ -4393,6 +4396,7 @@
},
"Purplenum": {
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000802],
["9/16384", 0x000302],
@@ -4447,6 +4451,7 @@
"LA_DIMENIAN": [["9/16384", 0x030A00]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["11/8192", 0x030B03]],
@@ -4466,6 +4471,7 @@
"Redria": {
"BARBAROUS_WOLF": [["11/8192", 0x030B01]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000502],
["9/16384", 0x000902],
@@ -4523,6 +4529,7 @@
"LA_DIMENIAN": [["11/8192", 0x030B01]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILTAS": [["11/8192", 0x030B03]],
@@ -4538,6 +4545,7 @@
"Skyly": {
"BARBAROUS_WOLF": [["9/16384", 0x030B06]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000202],
["9/16384", 0x000702],
@@ -4591,6 +4599,7 @@
"LA_DIMENIAN": [["9/16384", 0x030A00]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILTAS": [["9/16384", 0x030A00]],
@@ -4612,6 +4621,7 @@
"Viridia": {
"BARBAROUS_WOLF": [["11/8192", 0x030B00]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000902],
["9/16384", 0x000402],
@@ -4667,6 +4677,7 @@
"LA_DIMENIAN": [["11/8192", 0x030B01]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["9/16384", 0x030A00]],
@@ -4687,6 +4698,7 @@
},
"Whitill": {
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000502],
["9/16384", 0x000802],
@@ -4739,6 +4751,7 @@
"ILL_GILL": [["7/32768", 0x010309]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["11/8192", 0x030B03]],
@@ -4760,6 +4773,7 @@
"Yellowboze": {
"BARBAROUS_WOLF": [["11/8192", 0x030300]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["7/8192", 0x030F00],
["7/8192", 0x01024C]
@@ -4807,6 +4821,7 @@
"ILL_GILL": [["7/32768", 0x01030D]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["11/8192", 0x030B00]],
@@ -4830,6 +4845,7 @@
"Bluefull": {
"BARBAROUS_WOLF": [["13/4096", 0x002600]],
"BARBA_RAY": [["13/64", 0x000407]],
"BARBA_RAY_JOINT": [["13/64", 0x000407]],
"Box-JungleNorth": [
["13/8192", 0x000407],
["7/4096", 0x000B05],
@@ -4902,6 +4918,7 @@
"LA_DIMENIAN": [["1/512", 0x010317]],
"LOVE_RAPPY": [["1/2", 0x005500]],
"MERICAROL": [["13/4096", 0x005B00]],
"MERICARAND": [["13/4096", 0x005B00]],
"MERICUS": [["13/4096", 0x003D00]],
"MERIKLE": [["13/4096", 0x009500]],
"MERILLIA": [["1/64", 0x000407]],
@@ -4928,6 +4945,7 @@
"Greennill": {
"BARBAROUS_WOLF": [["1/512", 0x010335]],
"BARBA_RAY": [["13/64", 0x000E01]],
"BARBA_RAY_JOINT": [["13/64", 0x000E01]],
"Box-JungleNorth": [
["13/8192", 0x000707],
["7/4096", 0x000306],
@@ -4998,6 +5016,7 @@
"LA_DIMENIAN": [["1/1024", 0x010229]],
"LOVE_RAPPY": [["7/8", 0x01031D]],
"MERICAROL": [["13/4096", 0x005600]],
"MERICARAND": [["13/4096", 0x005600]],
"MERICUS": [["13/4096", 0x008B02]],
"MERIKLE": [["13/4096", 0x003D00]],
"MERILLIA": [["1/64", 0x000707]],
@@ -5024,6 +5043,7 @@
"Oran": {
"BARBAROUS_WOLF": [["13/4096", 0x002700]],
"BARBA_RAY": [["13/64", 0x008B02]],
"BARBA_RAY_JOINT": [["13/64", 0x008B02]],
"Box-JungleNorth": [
["13/8192", 0x000307],
["7/4096", 0x000206],
@@ -5096,6 +5116,7 @@
"LA_DIMENIAN": [["1/512", 0x010313]],
"LOVE_RAPPY": [["1/2", 0x005B00]],
"MERICAROL": [["13/4096", 0x008902]],
"MERICARAND": [["13/4096", 0x008902]],
"MERICUS": [["13/4096", 0x008B02]],
"MERIKLE": [["13/4096", 0x010346]],
"MERILLIA": [["1/64", 0x000307]],
@@ -5121,6 +5142,7 @@
"Pinkal": {
"BARBAROUS_WOLF": [["1/512", 0x010320]],
"BARBA_RAY": [["13/64", 0x008902]],
"BARBA_RAY_JOINT": [["13/64", 0x008902]],
"Box-JungleNorth": [
["13/8192", 0x000C06],
["7/4096", 0x000406],
@@ -5199,6 +5221,7 @@
"LA_DIMENIAN": [["1/64", 0x000C06]],
"LOVE_RAPPY": [["1/2", 0x005500]],
"MERICAROL": [["13/4096", 0x008902]],
"MERICARAND": [["13/4096", 0x008902]],
"MERICUS": [["13/4096", 0x008B02]],
"MERIKLE": [["13/4096", 0x005B00]],
"MERILLIA": [["13/131072", 0x009305]],
@@ -5225,6 +5248,7 @@
"Purplenum": {
"BARBAROUS_WOLF": [["1/512", 0x01030F]],
"BARBA_RAY": [["13/64", 0x000807]],
"BARBA_RAY_JOINT": [["13/64", 0x000807]],
"Box-JungleNorth": [
["13/8192", 0x000807],
["7/4096", 0x000306],
@@ -5295,6 +5319,7 @@
"LA_DIMENIAN": [["13/4096", 0x004600]],
"LOVE_RAPPY": [["1/2", 0x01031D]],
"MERICAROL": [["13/131072", 0x001E00]],
"MERICARAND": [["13/131072", 0x001E00]],
"MERICUS": [["1/1024", 0x001001]],
"MERIKLE": [["13/4096", 0x009500]],
"MERILLIA": [["1/64", 0x000307]],
@@ -5321,6 +5346,7 @@
"Redria": {
"BARBAROUS_WOLF": [["13/4096", 0x001006]],
"BARBA_RAY": [["7/256", 0x000E02]],
"BARBA_RAY_JOINT": [["7/256", 0x000E02]],
"Box-JungleNorth": [
["13/8192", 0x000507],
["7/4096", 0x000107],
@@ -5393,6 +5419,7 @@
"LA_DIMENIAN": [["13/4096", 0x01030F]],
"LOVE_RAPPY": [["7/8", 0x01031D]],
"MERICAROL": [["13/4096", 0x008902]],
"MERICARAND": [["13/4096", 0x008902]],
"MERICUS": [["13/4096", 0x008B02]],
"MERIKLE": [["13/4096", 0x005E00]],
"MERILLIA": [["13/4096", 0x001004]],
@@ -5418,6 +5445,7 @@
"Skyly": {
"BARBAROUS_WOLF": [["13/4096", 0x002000]],
"BARBA_RAY": [["13/64", 0x000E02]],
"BARBA_RAY_JOINT": [["13/64", 0x000E02]],
"Box-JungleNorth": [
["13/8192", 0x000207],
["7/4096", 0x000706],
@@ -5490,6 +5518,7 @@
"LA_DIMENIAN": [["13/4096", 0x010224]],
"LOVE_RAPPY": [["1/2", 0x006900]],
"MERICAROL": [["13/4096", 0x002F00]],
"MERICARAND": [["13/4096", 0x002F00]],
"MERICUS": [["13/4096", 0x006B00]],
"MERIKLE": [["13/4096", 0x003D00]],
"MERILLIA": [["1/64", 0x000E01]],
@@ -5516,6 +5545,7 @@
"Viridia": {
"BARBAROUS_WOLF": [["13/4096", 0x010224]],
"BARBA_RAY": [["13/64", 0x000907]],
"BARBA_RAY_JOINT": [["13/64", 0x000907]],
"Box-JungleNorth": [
["13/8192", 0x000907],
["7/4096", 0x000406],
@@ -5594,6 +5624,7 @@
"LA_DIMENIAN": [["13/4096", 0x002000]],
"LOVE_RAPPY": [["1/2", 0x001006]],
"MERICAROL": [["13/4096", 0x003B00]],
"MERICARAND": [["13/4096", 0x003B00]],
"MERICUS": [["13/4096", 0x004600]],
"MERIKLE": [["13/4096", 0x001101]],
"MERILLIA": [["1/512", 0x01011D]],
@@ -5620,6 +5651,7 @@
"Whitill": {
"BARBAROUS_WOLF": [["1/64", 0x000807]],
"BARBA_RAY": [["13/64", 0x000507]],
"BARBA_RAY_JOINT": [["13/64", 0x000507]],
"Box-JungleNorth": [
["13/8192", 0x000507],
["7/4096", 0x000806],
@@ -5690,6 +5722,7 @@
"LA_DIMENIAN": [["1/64", 0x000D01]],
"LOVE_RAPPY": [["1/2", 0x010132]],
"MERICAROL": [["1/64", 0x008902]],
"MERICARAND": [["1/64", 0x008902]],
"MERICUS": [["1/64", 0x008B02]],
"MERIKLE": [["13/4096", 0x002F00]],
"MERILLIA": [["1/64", 0x000807]],
@@ -5716,6 +5749,7 @@
"Yellowboze": {
"BARBAROUS_WOLF": [["1/512", 0x01030F]],
"BARBA_RAY": [["7/256", 0x000F02]],
"BARBA_RAY_JOINT": [["7/256", 0x000F02]],
"Box-JungleNorth": [
["1/1024", 0x000107],
["1/1024", 0x000607],
@@ -5785,6 +5819,7 @@
"LA_DIMENIAN": [["13/4096", 0x010225]],
"LOVE_RAPPY": [["1/2", 0x005B00]],
"MERICAROL": [["13/4096", 0x008902]],
"MERICARAND": [["13/4096", 0x008902]],
"MERICUS": [["13/4096", 0x002F00]],
"MERIKLE": [["13/4096", 0x005E00]],
"MERILLIA": [["1/64", 0x000C06]],
@@ -5813,6 +5848,7 @@
"Bluefull": {
"BARBAROUS_WOLF": [["1/128", 0x000406]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000405],
["9/4096", 0x000B03],
@@ -5881,6 +5917,7 @@
"LA_DIMENIAN": [["1/128", 0x000B05]],
"LOVE_RAPPY": [["7/8", 0x000407]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x010125]],
"MERILLIA": [["1/128", 0x000406]],
@@ -5904,6 +5941,7 @@
"Greennill": {
"BARBAROUS_WOLF": [["1/128", 0x000706]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000705],
["9/4096", 0x000304],
@@ -5972,6 +6010,7 @@
"LA_DIMENIAN": [["1/128", 0x000706]],
"LOVE_RAPPY": [["7/8", 0x000707]],
"MERICAROL": [["7/256", 0x005600]],
"MERICARAND": [["7/256", 0x005600]],
"MERICUS": [["7/256", 0x008C01]],
"MERIKLE": [["7/256", 0x000707]],
"MERILLIA": [["1/128", 0x000706]],
@@ -5995,6 +6034,7 @@
"Oran": {
"BARBAROUS_WOLF": [["1/128", 0x000306]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000305],
["9/4096", 0x000204],
@@ -6065,6 +6105,7 @@
"LA_DIMENIAN": [["1/128", 0x000306]],
"LOVE_RAPPY": [["7/8", 0x010219]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x000307]],
"MERILLIA": [["1/128", 0x000306]],
@@ -6088,6 +6129,7 @@
"Pinkal": {
"BARBAROUS_WOLF": [["1/128", 0x000C05]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000C04],
["9/4096", 0x000404],
@@ -6162,6 +6204,7 @@
"LA_DIMENIAN": [["1/128", 0x000C05]],
"LOVE_RAPPY": [["7/8", 0x002300]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x005600]],
"MERILLIA": [["1/128", 0x000406]],
@@ -6185,6 +6228,7 @@
"Purplenum": {
"BARBAROUS_WOLF": [["1/128", 0x000806]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000805],
["9/4096", 0x000304],
@@ -6255,6 +6299,7 @@
"LA_DIMENIAN": [["1/128", 0x000E00]],
"LOVE_RAPPY": [["7/8", 0x000807]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x000307]],
"MERILLIA": [["1/128", 0x000306]],
@@ -6278,6 +6323,7 @@
"Redria": {
"BARBAROUS_WOLF": [["1/128", 0x000906]],
"BARBA_RAY": [["3/32", 0x000E01]],
"BARBA_RAY_JOINT": [["3/32", 0x000E01]],
"Box-JungleNorth": [
["9/4096", 0x000504],
["9/4096", 0x000904],
@@ -6346,6 +6392,7 @@
"LA_DIMENIAN": [["1/128", 0x000906]],
"LOVE_RAPPY": [["7/8", 0x01031D]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x000F01]],
"MERIKLE": [["7/256", 0x001004]],
"MERILLIA": [["1/128", 0x000506]],
@@ -6369,6 +6416,7 @@
"Skyly": {
"BARBAROUS_WOLF": [["1/128", 0x000206]],
"BARBA_RAY": [["3/32", 0x000E01]],
"BARBA_RAY_JOINT": [["3/32", 0x000E01]],
"Box-JungleNorth": [
["9/4096", 0x000205],
["9/4096", 0x000704],
@@ -6439,6 +6487,7 @@
"LA_DIMENIAN": [["1/128", 0x000206]],
"LOVE_RAPPY": [["7/8", 0x000207]],
"MERICAROL": [["7/256", 0x005601]],
"MERICARAND": [["7/256", 0x005601]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x000207]],
"MERILLIA": [["1/128", 0x000205]],
@@ -6462,6 +6511,7 @@
"Viridia": {
"BARBAROUS_WOLF": [["1/128", 0x010219]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000905],
["9/4096", 0x000404],
@@ -6532,6 +6582,7 @@
"LA_DIMENIAN": [["1/64", 0x000406]],
"LOVE_RAPPY": [["7/8", 0x000907]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x000407]],
"MERILLIA": [["1/128", 0x000906]],
@@ -6555,6 +6606,7 @@
"Whitill": {
"BARBAROUS_WOLF": [["1/128", 0x000506]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000505],
["9/4096", 0x000804],
@@ -6627,6 +6679,7 @@
"LA_DIMENIAN": [["1/128", 0x000806]],
"LOVE_RAPPY": [["7/8", 0x010222]],
"MERICAROL": [["7/256", 0x000506]],
"MERICARAND": [["7/256", 0x000506]],
"MERICUS": [["7/256", 0x005601]],
"MERIKLE": [["7/256", 0x008901]],
"MERILLIA": [["1/128", 0x000506]],
@@ -6650,6 +6703,7 @@
"Yellowboze": {
"BARBAROUS_WOLF": [["1/128", 0x000806]],
"BARBA_RAY": [["3/32", 0x000F01]],
"BARBA_RAY_JOINT": [["3/32", 0x000F01]],
"Box-JungleNorth": [
["9/4096", 0x000105],
["9/4096", 0x000605],
@@ -6722,6 +6776,7 @@
"LA_DIMENIAN": [["1/128", 0x010225]],
"LOVE_RAPPY": [["7/8", 0x000906]],
"MERICAROL": [["7/256", 0x003B00]],
"MERICARAND": [["7/256", 0x003B00]],
"MERICUS": [["7/256", 0x010125]],
"MERIKLE": [["7/256", 0x008B01]],
"MERILLIA": [["1/128", 0x000106]],
+90 -35
View File
@@ -610,41 +610,16 @@
"BARBAROUS_WOLF": [["1/128", 0x000605]],
"BOOMA": [["1/128", 0x030D0F]],
"BULCLAW": [["1/128", 0x000905]],
"Box-Cave1": [
["9/536870912", 0x030E1C],
["13/134217728", 0x030E16],
["5/134217728", 0x01024A]
],
"Box-Cave3": [
["7/1073741824", 0x020000],
["7/1073741824", 0x000404],
["3/536870912", 0x000404],
["13/2147483648", 0x030E1B],
["13/2147483648", 0x030E1D],
["13/2147483648", 0x030E1F],
["7/1073741824", 0x030E17],
["7/1073741824", 0x01024B]
],
"Box-DarkFalz": [["1/268435456", 0x01024C]],
"Box-Lobby": [["1/268435456", 0x01024D]],
"Box-Mine1": [["3/536870912", 0x020000]],
"Box-Pioneer2": [
["7/2147483648", 0x000904],
["7/2147483648", 0x000904]
],
"Box-Ruins1": [
["3/536870912", 0x030E1E],
["13/2147483648", 0x030E18],
["13/2147483648", 0x030E19]
],
"Box-VolOpt": [
["5/1073741824", 0x000905],
["7/2147483648", 0x000405],
["7/1073741824", 0x01024C],
["7/2147483648", 0x01024C],
["1/268435456", 0x030F00],
["13/2147483648", 0x030E1A]
],
"Box-Forest1": [[0x00380000, 0x030E1D]],
"Box-Forest2": [[0x00380000, 0x030E1A], [0x00380000, 0x030E18]],
"Box-Cave1": [[0x00380000, 0x030E16]],
"Box-Cave2": [[0x00380000, 0x030F00], [0x00380000, 0x01024D]],
"Box-Cave3": [[0x00900000, 0x000904], [0x00900000, 0x000404], [0x00380000, 0x030E1B]],
"Box-Mine1": [[0x02400000, 0x020000], [0x00240000, 0x01024C], [0x00380000, 0x030E1E]],
"Box-Mine2": [[0x01000000, 0x020000], [0x00B00000, 0x000904], [0x00B00000, 0x000404], [0x00240000, 0x01024C], [0x00380000, 0x030E1F], [0x00380000, 0x01024A]],
"Box-Ruins1": [[0x00380000, 0x030E1C], [0x00380000, 0x030E19]],
"Box-Ruins2": [[0x00380000, 0x01024C], [0x00380000, 0x030E17]],
"Box-Ruins3": [[0x00D00000, 0x000905], [0x00D00000, 0x000405], [0x00380000, 0x01024B]],
"CANADINE": [["1/128", 0x000605]],
"CANADINE_GROUP": [["1/128", 0x000605]],
"CANANE": [["7/256", 0x000405]],
@@ -3269,6 +3244,7 @@
"Bluefull": {
"BARBAROUS_WOLF": [["1/128", 0x000B04]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000404],
["9/4096", 0x000B02],
@@ -3326,6 +3302,7 @@
"LA_DIMENIAN": [["1/128", 0x000105]],
"LOVE_RAPPY": [["7/8", 0x002600]],
"MERICAROL": [["7/256", 0x000406]],
"MERICARAND": [["7/256", 0x000406]],
"MERICUS": [["7/256", 0x000406]],
"MERIKLE": [["7/256", 0x000406]],
"MERILLIA": [["1/128", 0x000105]],
@@ -3349,6 +3326,7 @@
"Greennill": {
"BARBAROUS_WOLF": [["1/128", 0x000305]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000704],
["9/4096", 0x000304]
@@ -3403,6 +3381,7 @@
"LA_DIMENIAN": [["1/128", 0x000605]],
"LOVE_RAPPY": [["7/8", 0x002300]],
"MERICAROL": [["7/256", 0x000706]],
"MERICARAND": [["7/256", 0x000706]],
"MERICUS": [["7/256", 0x000706]],
"MERIKLE": [["7/256", 0x000706]],
"MERILLIA": [["1/128", 0x000606]],
@@ -3426,6 +3405,7 @@
"Oran": {
"BARBAROUS_WOLF": [["1/128", 0x000205]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000304],
["9/4096", 0x000204],
@@ -3495,6 +3475,7 @@
"LA_DIMENIAN": [["1/128", 0x000105]],
"LOVE_RAPPY": [["7/8", 0x002300]],
"MERICAROL": [["7/256", 0x000306]],
"MERICARAND": [["7/256", 0x000306]],
"MERICUS": [["7/256", 0x000306]],
"MERIKLE": [["7/256", 0x000306]],
"MERILLIA": [["1/128", 0x000105]],
@@ -3518,6 +3499,7 @@
"Pinkal": {
"BARBAROUS_WOLF": [["1/128", 0x000405]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000C03],
["9/4096", 0x000404],
@@ -3588,6 +3570,7 @@
"LA_DIMENIAN": [["1/128", 0x000A04]],
"LOVE_RAPPY": [["7/8", 0x002300]],
"MERICAROL": [["7/256", 0x000C05]],
"MERICARAND": [["7/256", 0x000C05]],
"MERICUS": [["7/256", 0x000C05]],
"MERIKLE": [["7/256", 0x000C05]],
"MERILLIA": [["1/128", 0x000A04]],
@@ -3611,6 +3594,7 @@
"Purplenum": {
"BARBAROUS_WOLF": [["1/128", 0x000305]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000804],
["9/4096", 0x000304],
@@ -3672,6 +3656,7 @@
"LA_DIMENIAN": [["1/128", 0x000605]],
"LOVE_RAPPY": [["7/8", 0x004900]],
"MERICAROL": [["7/256", 0x000806]],
"MERICARAND": [["7/256", 0x000806]],
"MERICUS": [["7/256", 0x000806]],
"MERIKLE": [["7/256", 0x000806]],
"MERILLIA": [["1/128", 0x000605]],
@@ -3695,6 +3680,7 @@
"Redria": {
"BARBAROUS_WOLF": [["1/128", 0x000905]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000504],
["9/4096", 0x000904],
@@ -3767,6 +3753,7 @@
"LA_DIMENIAN": [["1/128", 0x000A04]],
"LOVE_RAPPY": [["7/8", 0x002600]],
"MERICAROL": [["7/256", 0x000506]],
"MERICARAND": [["7/256", 0x000506]],
"MERICUS": [["7/256", 0x000506]],
"MERIKLE": [["7/256", 0x000506]],
"MERILLIA": [["1/128", 0x000105]],
@@ -3790,6 +3777,7 @@
"Skyly": {
"BARBAROUS_WOLF": [["1/128", 0x000705]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000204],
["9/4096", 0x000704],
@@ -3846,6 +3834,7 @@
"LA_DIMENIAN": [["1/128", 0x000105]],
"LOVE_RAPPY": [["7/8", 0x002300]],
"MERICAROL": [["7/256", 0x000206]],
"MERICARAND": [["7/256", 0x000206]],
"MERICUS": [["7/256", 0x000206]],
"MERIKLE": [["7/256", 0x000206]],
"MERILLIA": [["1/128", 0x000105]],
@@ -3869,6 +3858,7 @@
"Viridia": {
"BARBAROUS_WOLF": [["1/128", 0x000405]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000904],
["9/4096", 0x000404],
@@ -3930,6 +3920,7 @@
"LA_DIMENIAN": [["1/128", 0x000105]],
"LOVE_RAPPY": [["7/8", 0x002000]],
"MERICAROL": [["7/256", 0x000906]],
"MERICARAND": [["7/256", 0x000906]],
"MERICUS": [["7/256", 0x000906]],
"MERIKLE": [["7/256", 0x000906]],
"MERILLIA": [["1/128", 0x000105]],
@@ -3953,6 +3944,7 @@
"Whitill": {
"BARBAROUS_WOLF": [["1/128", 0x000805]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [
["9/4096", 0x000504],
["9/4096", 0x000804],
@@ -4018,6 +4010,7 @@
"LA_DIMENIAN": [["1/128", 0x000605]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/256", 0x000506]],
"MERICARAND": [["7/256", 0x000506]],
"MERICUS": [["7/256", 0x000506]],
"MERIKLE": [["7/256", 0x000506]],
"MERILLIA": [["1/128", 0x000605]],
@@ -4041,6 +4034,7 @@
"Yellowboze": {
"BARBAROUS_WOLF": [["1/128", 0x000205]],
"BARBA_RAY": [["1/16", 0x000E00]],
"BARBA_RAY_JOINT": [["1/16", 0x000E00]],
"Box-JungleNorth": [["7/8192", 0x01024C]],
"Box-Mountain": [["1/256", 0x020000]],
"Box-JungleEast": [
@@ -4089,6 +4083,7 @@
"LA_DIMENIAN": [["1/128", 0x000605]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/256", 0x000506]],
"MERICARAND": [["7/256", 0x000506]],
"MERICUS": [["7/256", 0x000306]],
"MERIKLE": [["7/256", 0x000406]],
"MERILLIA": [["1/128", 0x000105]],
@@ -4114,6 +4109,7 @@
"Bluefull": {
"BARBAROUS_WOLF": [["9/16384", 0x030A00]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000402],
["9/16384", 0x000B01],
@@ -4166,6 +4162,7 @@
"LA_DIMENIAN": [["9/16384", 0x030A00]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["9/16384", 0x030A00]],
@@ -4185,6 +4182,7 @@
"Greennill": {
"BARBAROUS_WOLF": [["3/8192", 0x030F00]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000702],
["9/16384", 0x000302]
@@ -4235,6 +4233,7 @@
"LA_DIMENIAN": [["9/16384", 0x030B06]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILTAS": [["9/16384", 0x030A00]],
@@ -4254,6 +4253,7 @@
"Oran": {
"BARBAROUS_WOLF": [["9/16384", 0x030A00]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000302],
["9/16384", 0x000202],
@@ -4304,6 +4304,7 @@
"LA_DIMENIAN": [["11/8192", 0x030B05]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["11/8192", 0x030B00]],
@@ -4325,6 +4326,7 @@
"Pinkal": {
"BARBAROUS_WOLF": [["11/8192", 0x030B01]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000C01],
["9/16384", 0x000402],
@@ -4388,6 +4390,7 @@
"LA_DIMENIAN": [["11/8192", 0x030B01]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["11/8192", 0x030B00]],
@@ -4409,6 +4412,7 @@
},
"Purplenum": {
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000802],
["9/16384", 0x000302],
@@ -4463,6 +4467,7 @@
"LA_DIMENIAN": [["9/16384", 0x030A00]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["11/8192", 0x030B03]],
@@ -4482,6 +4487,7 @@
"Redria": {
"BARBAROUS_WOLF": [["11/8192", 0x030B01]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000502],
["9/16384", 0x000902],
@@ -4539,6 +4545,7 @@
"LA_DIMENIAN": [["11/8192", 0x030B01]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILTAS": [["11/8192", 0x030B03]],
@@ -4554,6 +4561,7 @@
"Skyly": {
"BARBAROUS_WOLF": [["9/16384", 0x030B06]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000202],
["9/16384", 0x000702],
@@ -4607,6 +4615,7 @@
"LA_DIMENIAN": [["9/16384", 0x030A00]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILTAS": [["9/16384", 0x030A00]],
@@ -4628,6 +4637,7 @@
"Viridia": {
"BARBAROUS_WOLF": [["11/8192", 0x030B00]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000902],
["9/16384", 0x000402],
@@ -4683,6 +4693,7 @@
"LA_DIMENIAN": [["11/8192", 0x030B01]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["9/16384", 0x030A00]],
@@ -4703,6 +4714,7 @@
},
"Whitill": {
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["9/16384", 0x000502],
["9/16384", 0x000802],
@@ -4755,6 +4767,7 @@
"ILL_GILL": [["7/32768", 0x010309]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["11/8192", 0x030B03]],
@@ -4776,6 +4789,7 @@
"Yellowboze": {
"BARBAROUS_WOLF": [["11/8192", 0x030300]],
"BARBA_RAY": [["7/8", 0x030500]],
"BARBA_RAY_JOINT": [["7/8", 0x030500]],
"Box-JungleNorth": [
["7/8192", 0x030F00],
["7/8192", 0x01024C]
@@ -4823,6 +4837,7 @@
"ILL_GILL": [["7/32768", 0x01030D]],
"LOVE_RAPPY": [["7/8", 0x01031C]],
"MERICAROL": [["7/8", 0x030500]],
"MERICARAND": [["7/8", 0x030500]],
"MERICUS": [["7/8", 0x030500]],
"MERIKLE": [["7/8", 0x030500]],
"MERILLIA": [["11/8192", 0x030B00]],
@@ -4846,6 +4861,7 @@
"Bluefull": {
"BARBAROUS_WOLF": [["13/4096", 0x002600]],
"BARBA_RAY": [["7/256", 0x030D15]],
"BARBA_RAY_JOINT": [["7/256", 0x030D15]],
"Box-JungleNorth": [
["13/8192", 0x000407],
["7/4096", 0x000B05],
@@ -4918,6 +4934,7 @@
"LA_DIMENIAN": [["1/512", 0x010317]],
"LOVE_RAPPY": [["1/2", 0x005500]],
"MERICAROL": [["13/4096", 0x005B00]],
"MERICARAND": [["13/4096", 0x005B00]],
"MERICUS": [["13/4096", 0x003D00]],
"MERIKLE": [["13/4096", 0x009500]],
"MERILLIA": [["1/64", 0x000407]],
@@ -4944,6 +4961,7 @@
"Greennill": {
"BARBAROUS_WOLF": [["1/512", 0x010335]],
"BARBA_RAY": [["7/256", 0x01035F]],
"BARBA_RAY_JOINT": [["7/256", 0x01035F]],
"Box-JungleNorth": [
["13/8192", 0x000707],
["7/4096", 0x000306],
@@ -5014,6 +5032,7 @@
"LA_DIMENIAN": [["1/1024", 0x010229]],
"LOVE_RAPPY": [["7/8", 0x01031D]],
"MERICAROL": [["13/4096", 0x005600]],
"MERICARAND": [["13/4096", 0x005600]],
"MERICUS": [["13/4096", 0x008B02]],
"MERIKLE": [["13/4096", 0x003D00]],
"MERILLIA": [["1/64", 0x000707]],
@@ -5040,6 +5059,7 @@
"Oran": {
"BARBAROUS_WOLF": [["13/4096", 0x002700]],
"BARBA_RAY": [["7/256", 0x01035E]],
"BARBA_RAY_JOINT": [["7/256", 0x01035E]],
"Box-JungleNorth": [
["13/8192", 0x000307],
["7/4096", 0x000206],
@@ -5112,6 +5132,7 @@
"LA_DIMENIAN": [["1/512", 0x010313]],
"LOVE_RAPPY": [["1/2", 0x005B00]],
"MERICAROL": [["13/4096", 0x008902]],
"MERICARAND": [["13/4096", 0x008902]],
"MERICUS": [["13/4096", 0x008B02]],
"MERIKLE": [["13/4096", 0x010346]],
"MERILLIA": [["1/64", 0x000307]],
@@ -5137,6 +5158,7 @@
"Pinkal": {
"BARBAROUS_WOLF": [["1/512", 0x010320]],
"BARBA_RAY": [["7/256", 0x030D15]],
"BARBA_RAY_JOINT": [["7/256", 0x030D15]],
"Box-JungleNorth": [
["13/8192", 0x000C06],
["7/4096", 0x000406],
@@ -5215,6 +5237,7 @@
"LA_DIMENIAN": [["1/64", 0x000C06]],
"LOVE_RAPPY": [["1/2", 0x005500]],
"MERICAROL": [["13/4096", 0x008902]],
"MERICARAND": [["13/4096", 0x008902]],
"MERICUS": [["13/4096", 0x008B02]],
"MERIKLE": [["13/4096", 0x005B00]],
"MERILLIA": [["13/131072", 0x009305]],
@@ -5241,6 +5264,7 @@
"Purplenum": {
"BARBAROUS_WOLF": [["1/512", 0x01030F]],
"BARBA_RAY": [["7/256", 0x01035E]],
"BARBA_RAY_JOINT": [["7/256", 0x01035E]],
"Box-JungleNorth": [
["13/8192", 0x000807],
["7/4096", 0x000306],
@@ -5311,6 +5335,7 @@
"LA_DIMENIAN": [["13/4096", 0x004600]],
"LOVE_RAPPY": [["1/2", 0x01031D]],
"MERICAROL": [["13/131072", 0x001E00]],
"MERICARAND": [["13/131072", 0x001E00]],
"MERICUS": [["1/1024", 0x001001]],
"MERIKLE": [["13/4096", 0x009500]],
"MERILLIA": [["1/64", 0x000307]],
@@ -5337,6 +5362,7 @@
"Redria": {
"BARBAROUS_WOLF": [["13/4096", 0x001006]],
"BARBA_RAY": [["7/256", 0x000E02]],
"BARBA_RAY_JOINT": [["7/256", 0x000E02]],
"Box-JungleNorth": [
["13/8192", 0x000507],
["7/4096", 0x000107],
@@ -5409,6 +5435,7 @@
"LA_DIMENIAN": [["13/4096", 0x01030F]],
"LOVE_RAPPY": [["7/8", 0x01031D]],
"MERICAROL": [["13/4096", 0x008902]],
"MERICARAND": [["13/4096", 0x008902]],
"MERICUS": [["13/4096", 0x008B02]],
"MERIKLE": [["13/4096", 0x005E00]],
"MERILLIA": [["13/4096", 0x001004]],
@@ -5434,6 +5461,7 @@
"Skyly": {
"BARBAROUS_WOLF": [["13/4096", 0x002000]],
"BARBA_RAY": [["11/256", 0x000E02]],
"BARBA_RAY_JOINT": [["11/256", 0x000E02]],
"Box-JungleNorth": [
["13/8192", 0x000207],
["7/4096", 0x000706],
@@ -5506,6 +5534,7 @@
"LA_DIMENIAN": [["13/4096", 0x010224]],
"LOVE_RAPPY": [["1/2", 0x006900]],
"MERICAROL": [["13/4096", 0x002F00]],
"MERICARAND": [["13/4096", 0x002F00]],
"MERICUS": [["13/4096", 0x006B00]],
"MERIKLE": [["13/4096", 0x003D00]],
"MERILLIA": [["1/64", 0x000E01]],
@@ -5532,6 +5561,7 @@
"Viridia": {
"BARBAROUS_WOLF": [["13/4096", 0x010224]],
"BARBA_RAY": [["7/256", 0x001402]],
"BARBA_RAY_JOINT": [["7/256", 0x001402]],
"Box-JungleNorth": [
["13/8192", 0x000907],
["7/4096", 0x000406],
@@ -5610,6 +5640,7 @@
"LA_DIMENIAN": [["13/4096", 0x002000]],
"LOVE_RAPPY": [["1/2", 0x001006]],
"MERICAROL": [["13/4096", 0x003B00]],
"MERICARAND": [["13/4096", 0x003B00]],
"MERICUS": [["13/4096", 0x004600]],
"MERIKLE": [["13/4096", 0x001101]],
"MERILLIA": [["1/512", 0x01011D]],
@@ -5636,6 +5667,7 @@
"Whitill": {
"BARBAROUS_WOLF": [["1/64", 0x000807]],
"BARBA_RAY": [["13/512", 0x01035E]], // Schtserv's original table has ["7/256", 0x01035D]] (Heavenly/HP) here
"BARBA_RAY_JOINT": [["13/512", 0x01035E]], // Schtserv's original table has ["7/256", 0x01035D]] (Heavenly/HP) here
"Box-JungleNorth": [
["13/8192", 0x000507],
["7/4096", 0x000806],
@@ -5706,6 +5738,7 @@
"LA_DIMENIAN": [["1/64", 0x000D01]],
"LOVE_RAPPY": [["1/2", 0x010132]],
"MERICAROL": [["1/64", 0x008902]],
"MERICARAND": [["1/64", 0x008902]],
"MERICUS": [["1/64", 0x008B02]],
"MERIKLE": [["13/4096", 0x002F00]],
"MERILLIA": [["1/64", 0x000807]],
@@ -5732,6 +5765,7 @@
"Yellowboze": {
"BARBAROUS_WOLF": [["1/512", 0x01030F]],
"BARBA_RAY": [["7/256", 0x000F02]],
"BARBA_RAY_JOINT": [["7/256", 0x000F02]],
"Box-JungleNorth": [
["1/1024", 0x000107],
["1/1024", 0x000607],
@@ -5801,6 +5835,7 @@
"LA_DIMENIAN": [["13/4096", 0x010225]],
"LOVE_RAPPY": [["1/2", 0x005B00]],
"MERICAROL": [["13/4096", 0x008902]],
"MERICARAND": [["13/4096", 0x008902]],
"MERICUS": [["13/4096", 0x002F00]],
"MERIKLE": [["13/4096", 0x005E00]],
"MERILLIA": [["1/64", 0x000C06]],
@@ -5829,6 +5864,7 @@
"Bluefull": {
"BARBAROUS_WOLF": [["1/128", 0x000406]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000405],
["9/4096", 0x000B03],
@@ -5897,6 +5933,7 @@
"LA_DIMENIAN": [["1/128", 0x000B05]],
"LOVE_RAPPY": [["7/8", 0x000407]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x010125]],
"MERILLIA": [["1/128", 0x000406]],
@@ -5920,6 +5957,7 @@
"Greennill": {
"BARBAROUS_WOLF": [["1/128", 0x000706]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000705],
["9/4096", 0x000304],
@@ -5988,6 +6026,7 @@
"LA_DIMENIAN": [["1/128", 0x000706]],
"LOVE_RAPPY": [["7/8", 0x000707]],
"MERICAROL": [["7/256", 0x005600]],
"MERICARAND": [["7/256", 0x005600]],
"MERICUS": [["7/256", 0x008C01]],
"MERIKLE": [["7/256", 0x000707]],
"MERILLIA": [["1/128", 0x000706]],
@@ -6011,6 +6050,7 @@
"Oran": {
"BARBAROUS_WOLF": [["1/128", 0x000306]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000305],
["9/4096", 0x000204],
@@ -6081,6 +6121,7 @@
"LA_DIMENIAN": [["1/128", 0x000306]],
"LOVE_RAPPY": [["7/8", 0x010219]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x000307]],
"MERILLIA": [["1/128", 0x000306]],
@@ -6104,6 +6145,7 @@
"Pinkal": {
"BARBAROUS_WOLF": [["1/128", 0x000C05]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000C04],
["9/4096", 0x000404],
@@ -6178,6 +6220,7 @@
"LA_DIMENIAN": [["1/128", 0x000C05]],
"LOVE_RAPPY": [["7/8", 0x002300]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x005600]],
"MERILLIA": [["1/128", 0x000406]],
@@ -6201,6 +6244,7 @@
"Purplenum": {
"BARBAROUS_WOLF": [["1/128", 0x000806]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000805],
["9/4096", 0x000304],
@@ -6271,6 +6315,7 @@
"LA_DIMENIAN": [["1/128", 0x000E00]],
"LOVE_RAPPY": [["7/8", 0x000807]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x000307]],
"MERILLIA": [["1/128", 0x000306]],
@@ -6294,6 +6339,7 @@
"Redria": {
"BARBAROUS_WOLF": [["1/128", 0x000906]],
"BARBA_RAY": [["3/32", 0x000E01]],
"BARBA_RAY_JOINT": [["3/32", 0x000E01]],
"Box-JungleNorth": [
["9/4096", 0x000504],
["9/4096", 0x000904],
@@ -6362,6 +6408,7 @@
"LA_DIMENIAN": [["1/128", 0x000906]],
"LOVE_RAPPY": [["7/8", 0x01031D]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x000F01]],
"MERIKLE": [["7/256", 0x001004]],
"MERILLIA": [["1/128", 0x000506]],
@@ -6385,6 +6432,7 @@
"Skyly": {
"BARBAROUS_WOLF": [["1/128", 0x000206]],
"BARBA_RAY": [["3/32", 0x000E01]],
"BARBA_RAY_JOINT": [["3/32", 0x000E01]],
"Box-JungleNorth": [
["9/4096", 0x000205],
["9/4096", 0x000704],
@@ -6455,6 +6503,7 @@
"LA_DIMENIAN": [["1/128", 0x000206]],
"LOVE_RAPPY": [["7/8", 0x000207]],
"MERICAROL": [["7/256", 0x005601]],
"MERICARAND": [["7/256", 0x005601]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x000207]],
"MERILLIA": [["1/128", 0x000205]],
@@ -6478,6 +6527,7 @@
"Viridia": {
"BARBAROUS_WOLF": [["1/128", 0x010219]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000905],
["9/4096", 0x000404],
@@ -6548,6 +6598,7 @@
"LA_DIMENIAN": [["1/64", 0x000406]],
"LOVE_RAPPY": [["7/8", 0x000907]],
"MERICAROL": [["7/256", 0x008901]],
"MERICARAND": [["7/256", 0x008901]],
"MERICUS": [["7/256", 0x008B01]],
"MERIKLE": [["7/256", 0x000407]],
"MERILLIA": [["1/128", 0x000906]],
@@ -6571,6 +6622,7 @@
"Whitill": {
"BARBAROUS_WOLF": [["1/128", 0x000506]],
"BARBA_RAY": [["3/32", 0x008901]],
"BARBA_RAY_JOINT": [["3/32", 0x008901]],
"Box-JungleNorth": [
["9/4096", 0x000505],
["9/4096", 0x000804],
@@ -6643,6 +6695,7 @@
"LA_DIMENIAN": [["1/128", 0x000806]],
"LOVE_RAPPY": [["7/8", 0x010222]],
"MERICAROL": [["7/256", 0x000506]],
"MERICARAND": [["7/256", 0x000506]],
"MERICUS": [["7/256", 0x005601]],
"MERIKLE": [["7/256", 0x008901]],
"MERILLIA": [["1/128", 0x000506]],
@@ -6666,6 +6719,7 @@
"Yellowboze": {
"BARBAROUS_WOLF": [["1/128", 0x000806]],
"BARBA_RAY": [["3/32", 0x000F01]],
"BARBA_RAY_JOINT": [["3/32", 0x000F01]],
"Box-JungleNorth": [
["9/4096", 0x000105],
["9/4096", 0x000605],
@@ -6738,6 +6792,7 @@
"LA_DIMENIAN": [["1/128", 0x010225]],
"LOVE_RAPPY": [["7/8", 0x000906]],
"MERICAROL": [["7/256", 0x003B00]],
"MERICARAND": [["7/256", 0x003B00]],
"MERICUS": [["7/256", 0x010125]],
"MERIKLE": [["7/256", 0x008B01]],
"MERILLIA": [["1/128", 0x000106]],
Binary file not shown.
Binary file not shown.
-1
View File
@@ -1,3 +1,2 @@
{
"EnabledIf": "!F_0065 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-1
View File
@@ -1,3 +1,2 @@
{
"EnabledIf": "!F_0067 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_01F9",
"EnabledIf": "!F_0069 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-1
View File
@@ -1,3 +1,2 @@
{
"EnabledIf": "!F_006B || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B",
"EnabledIf": "!F_006D || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B",
"EnabledIf": "!F_006F || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B",
"EnabledIf": "!F_0071 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0071",
"EnabledIf": "!F_0073 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B",
"EnabledIf": "!F_0075 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_01F9",
"EnabledIf": "!F_0077 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_01F9",
"EnabledIf": "!F_0079 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0077 && F_0079 && F_007F && F_0085",
"EnabledIf": "!F_007B || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_007B",
"EnabledIf": "!F_007D || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_01F9",
"EnabledIf": "!F_007F || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0077 && F_0079 && F_007F && F_0085",
"EnabledIf": "!F_0081 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_0201",
"EnabledIf": "!F_0083 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_01F9",
"EnabledIf": "!F_0085 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_0201",
"EnabledIf": "!F_0087 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_0209",
"EnabledIf": "!F_0089 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_0201",
"EnabledIf": "!F_008B || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_008B",
"EnabledIf": "!F_008D || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_008D && F_0209",
"EnabledIf": "!F_008F || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_006F && F_0209",
"EnabledIf": "!F_0091 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_0209",
"EnabledIf": "!F_0093 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-2
View File
@@ -1,4 +1,2 @@
{
"AvailableIf": "F_0065 && F_0067 && F_006B && F_008D && F_0209",
"EnabledIf": "!F_0095 || (F_0065 && F_0067 && F_0069 && F_006B && F_006D && F_006F && F_0071 && F_0073 && F_0075 && F_0077 && F_0079 && F_007B && F_007D && F_007F && F_0081 && F_0083 && F_0085 && F_0087 && F_0089 && F_008B && F_008D && F_008F && F_0091 && F_0093 && F_0095)",
}
-1
View File
@@ -1,3 +1,2 @@
{
"AvailableIf": "F_0073 && (V_NumPlayers == 1)",
}
@@ -1,4 +1,5 @@
### use psov2 crypt
### use legacy item random behavior
I 35932 2025-05-22 21:56:44 - [C-1] Channel name updated: C-1 @ ip:10.37.129.2:49620
I 35932 2025-05-22 21:56:44 - [C-1] Created
I 35932 2025-05-22 21:56:44 - [GameServer] Client connected: C-1 via TG-9300-PC_V2-pc-game_server
+1
View File
@@ -1,4 +1,5 @@
### use psov2 crypt
### use legacy item random behavior
I 39471 2025-05-21 23:39:20 - [IPStackSimulator] Virtual network N-2 connected via T-IPS-5059
I 39471 2025-05-21 23:39:20 - [IPStackSimulator] Client opened TCP connection 23232323238C062D (10.0.1.535:1581 -> 35.35.35.35:9100) (acked_server_seq=BD656BE7, next_client_seq=4F6748DF)
I 39471 2025-05-21 23:39:20 - [C-9] Channel name updated: C-9 @ ipss:N-2:127.0.0.1:57072
File diff suppressed because it is too large Load Diff