diff --git a/README.md b/README.md index fdd312ea..d4a85681 100644 --- a/README.md +++ b/README.md @@ -606,6 +606,7 @@ Some commands only work for clients not in proxy sessions. The chat commands are * `$ss `: Send a command to the remote server (if in a proxy session) or to the game server. * `$sb `: 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. + * `$makeobj [coords...] [angles...] [params...]`: Create a map object. This is only implemented for a few specific client versions. The type is an integer like `273` or `0x0107`. Coordinates are specified as e.g. `x:30 y:0 z:-25.5`; if coordinates are not specified, the object is created at the player's coordinates. Angles are specified as e.g. `r:0 p:0x1000 w:-0x400` (for roll, pitch, and yaw, respectively). Parameters are specified as e.g. `1:2.0 2:0.0 5:0x4000`; any unspecified parameters are set to zero. The object is only created for the calling player and is not added to the server's map state; if the object ever sends update commands (e.g. 6x0B), it will likely result in a disconnection. * Personal state commands * `$arrow `: Change your lobby arrow color. The color may be specified by number (0-12) or by name (red, blue, green, yellow, purple, cyan, orange, pink, white, white2, white3, or black). diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index f7673585..cf5d8c9c 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -1,5 +1,6 @@ #include "ChatCommands.hh" +#include #include #include @@ -1622,6 +1623,101 @@ ChatCommandDefinition cc_loadchar( co_return; }); +ChatCommandDefinition cc_makeobj( + {"$makeobj"}, + +[](const Args& a) -> asio::awaitable { + a.check_debug_enabled(); + + auto tokens = phosg::split(a.text, ' '); + if (tokens.size() < 1) { + throw runtime_error("not enough arguments"); + } + + uint32_t base_type_high = stoul(tokens[0], nullptr, 0) << 16; + VectorXYZF pos = a.c->pos; + VectorXYZI angle{0, 0, 0}; + VectorXYZF param123{0, 0, 0}; + VectorXYZI param456{0, 0, 0}; + for (size_t z = 1; z < tokens.size(); z++) { + auto subtokens = phosg::split(tokens[z], ':'); + if (subtokens.size() != 2 || subtokens[0].size() != 1) { + throw runtime_error("invalid argument: " + tokens[z]); + } + switch (tolower(subtokens[0].front())) { + case 'X': + case 'x': + pos.x = stof(subtokens[1]); + break; + case 'Y': + case 'y': + pos.y = stof(subtokens[1]); + break; + case 'Z': + case 'z': + pos.z = stof(subtokens[1]); + break; + case 'R': + case 'r': + angle.x = stol(subtokens[1], nullptr, 0); + break; + case 'P': + case 'p': + angle.y = stol(subtokens[1], nullptr, 0); + break; + case 'W': + case 'w': + angle.z = stol(subtokens[1], nullptr, 0); + break; + case '1': + param123.x = stof(subtokens[1]); + break; + case '2': + param123.y = stof(subtokens[1]); + break; + case '3': + param123.z = stof(subtokens[1]); + break; + case '4': + param456.x = stol(subtokens[1], nullptr, 0); + break; + case '5': + param456.y = stol(subtokens[1], nullptr, 0); + break; + case '6': + param456.z = stol(subtokens[1], nullptr, 0); + break; + default: + throw runtime_error("invalid argument: " + tokens[z]); + } + } + + auto encode_float = [](float z) -> uint32_t { + return *reinterpret_cast(&z); + }; + + unordered_map label_writes{ + {"base_type_high", base_type_high}, + {"floor_low", a.c->floor}, + {"pos_x", encode_float(pos.x)}, + {"pos_y", encode_float(pos.y)}, + {"pos_z", encode_float(pos.z)}, + {"angle_x", angle.x}, + {"angle_y", angle.y}, + {"angle_z", angle.z}, + {"param1", encode_float(param123.x)}, + {"param2", encode_float(param123.y)}, + {"param3", encode_float(param123.z)}, + {"param4", param456.x}, + {"param5", param456.y}, + {"param6", param456.z}, + }; + + co_await prepare_client_for_patches(a.c); + auto s = a.c->require_server_state(); + auto fn = s->function_code_index->get_patch("CreateObject", a.c->specific_version); + co_await send_function_call(a.c, fn, label_writes); + }); + ChatCommandDefinition cc_matcount( {"$matcount"}, +[](const Args& a) -> asio::awaitable { diff --git a/src/Client.hh b/src/Client.hh index c1f29297..c18ecd05 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -149,7 +149,7 @@ public: uint8_t override_lobby_number = 0x80; // 80 = no override int64_t override_random_seed = -1; std::unique_ptr override_variations; - VectorXZF pos; + VectorXYZF pos; uint32_t floor = 0x0F; std::weak_ptr lobby; uint8_t lobby_client_id = 0; diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index e3c4747d..99903892 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -4210,8 +4210,7 @@ struct G_VolOptBossActions_6x16 { // 6x17: Set entity position and angle (not valid on Episode 3) // This command sets an entity's position and angle without performing any -// validity checks, even on v3 and later. We unconditionally block this if it -// affects a player other than the sender. +// validity checks, even on v3 and later. struct G_SetEntityPositionAndAngle_6x17 { G_EntityIDHeader header; @@ -4760,7 +4759,7 @@ struct G_IntraMapWarp_6x55 { G_ClientIDHeader header; le_uint32_t angle_y = 0; VectorXYZF from_pos; - VectorXYZF to_pos; + VectorXYZF pos; } __packed_ws__(G_IntraMapWarp_6x55, 0x20); // 6x56: Set player position and angle (protected on GC NTE/V3/V4) diff --git a/src/Map.cc b/src/Map.cc index 80391683..6673de14 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -618,7 +618,8 @@ static const vector dat_object_definitions({ // param4 = source type: // 0 = use this set when advancing from a lower floor // 1 = use this set when returning from a higher floor - // anything else = set is unused + // anything else = set is unused (TODO: but maybe used by + // TObjAreaWarpQuest?) {0x0000, F_V0_V4, 0x00007FFFFFFFFFFF, "TObjPlayerSet"}, {0x0000, F_EP3, 0x0000000000008001, "TObjPlayerSet"}, @@ -938,7 +939,7 @@ static const vector dat_object_definitions({ // Radar collision. Params: // param1 = radius - // param4 = TODO + // param4 = minimap segment ID {0x0017, F_V0_V4, 0x00004FFFFFF8FFFE, "TObjRaderCol"}, {0x0017, F_EP3, 0x0000000000008000, "TObjRaderCol"}, @@ -990,7 +991,7 @@ static const vector dat_object_definitions({ // that the object is not destroyed immediately if it's blue and the game // is Challenge mode. Params: // param1 = player set ID (TODO: what exactly does this do? Looks like it - // does nothing unless it's >= 2) + // does nothing unless it's >= 2; see TObjPlayerSet) // param4 = destination floor // param6 = color (0 = blue, 1 = red) {0x001B, F_V1_V4, 0x00005000000078FE, "TObjAreaWarpQuest"}, @@ -1005,10 +1006,10 @@ static const vector dat_object_definitions({ // Params: // param1 = TODO // param2 = TODO - // param3 = TODO - // param4 = TODO - // param5 = TODO - // param6 = TODO + // param3 = TODO (1 byte only) + // param4 = TODO (1 byte only) + // param5 = TODO (1 byte only) + // param6 = TODO (1 byte only) {0x001D, F_V2_V4, 0x0000400000000002, "TEffStarLight2D_Base"}, // VR Temple Beta / Barba Ray lens flare effect. @@ -1017,7 +1018,6 @@ static const vector dat_object_definitions({ // Hides the area map when the player is near this object. Params: // param1 = radius - // TODO: Test this. {0x001F, F_V2_V4, 0x00004FFC3FFB07FE, "TObjRaderHideCol"}, // Item-detecting floor switch. Params: @@ -1114,7 +1114,30 @@ static const vector dat_object_definitions({ {0x0023, F_V2_V4, 0x00004FFC3FFB07FE, "TOAttackableCol"}, // Damage effect. Params: - // angle.x = effect type (in range [0x00, 0x14]; TODO: list these) + // angle.x = effect type (in range [0x00, 0x14]; the following were + // tested in Forest 1 and may have different effects in different + // areas): + // 00 = fiery explosion with vertical camera shake + // 01 = vertical camera shake only + // 02 = bluish energy ball + // 03 = expanding white flash + // 04 = contracting white flash + // 05 = nothing + // 06 = rising fireball + // 07 = blue flash + // 08 = purple flash + // 09 = teal flash + // 0A = nothing + // 0B = big orange flash + // 0C = nothing + // 0D = big white flash + // 0E = black smoky flash + // 0F = nothing + // 10 = nothing + // 11 = smaller black smoke + // 12 = bluish-purple flash + // 13 = quick blue flash + // 14 = lavender-gray flash // param1 = damage radius // param2 = damage value, scaled by difficulty: // Normal: param2 * 2 @@ -1132,9 +1155,9 @@ static const vector dat_object_definitions({ // Switch flag timer. This object watches for a switch flag to be // activated, then once it is, waits for a specified time, then disables - // that switch flag and activates up to three other switch flags. Note - // that this object just provides the timer functionality; the trigger - // switch flag must be activated by some other object. Params: + // that switch flag or activates up to three other switch flags. Note that + // this object just provides the timer functionality; the trigger switch + // flag must be activated by some other object. Params: // angle.x = trigger mode: // 0 = disable watched switch flag when timer expires // any positive number = enable up to 3 other switch flags when timer @@ -1341,18 +1364,16 @@ static const vector dat_object_definitions({ // be no parameters. {0x0054, F_V0_V4, 0x0000600000000001, "TObjCityDoor_Lobby"}, - // Version of the main warp for Challenge mode? This object seems to - // behave similarly to boss teleporters; it shows the player a Yes/No - // confirmation menu and sends 6x6A to synchronize state. There is a - // global named last_set_mainwarp_value which is set to param4 when this - // object is constructed, but may be changed by a set_mainwarp quest - // opcode after that. If that happens, this object replaces its - // dest_floor with the floor specified in the last set_mainwarp quest - // opcode. Params: + // Version of the main warp for Challenge mode. This object looks like the + // main Ragol warp on Pioneer 2, but behaves similarly to boss teleporters: + // it shows the player a Yes/No confirmation menu and sends 6x6A to + // synchronize state. There is a global named last_set_mainwarp_value which + // is set to param4 when this object is constructed, but may be changed by + // a set_mainwarp quest opcode after that. If that happens, this object + // replaces its dest_floor with the floor specified in the last + // set_mainwarp quest opcode. Params: // param4 = destination floor // param5 = switch flag number - // TODO: This thing has a lot of code; figure out if there are any other - // parameters {0x0055, F_V2_V4, 0x0000600000040001, "TObjCityMainWarpChallenge"}, // Episode 2 Lab door. Params: @@ -1378,8 +1399,10 @@ static const vector dat_object_definitions({ {0x0057, F_V3_V4, 0x0000600000040001, "TObjTradeCollision"}, {0x0057, F_EP3, 0x0000000000000001, "TObjTradeCollision"}, - // TODO: Describe this object. Presumably similar to TObjTradeCollision - // but enables the deck edit counter? Params: + // This object appears to be unused in both Ep3 NTE and the final release. + // It may have been an early version of Deck Edit or Entry Counter + // sequence, perhaps responsible for setting the players' statuses when + // they're near either of those counters. Params: // param1 = radius {0x0058, F_EP3, 0x0000000000000001, "TObjDeckCollision"}, @@ -1398,9 +1421,13 @@ static const vector dat_object_definitions({ {0x0081, F_V0_V4, 0x00004FF00078003E, "TObjDoorKey"}, // Laser fence and square laser fence. Params: - // param1 = color (range [0, 3]) + // param1 = color: + // <=0 = red + // 1 = cyan + // 2 = green + // >=3 = magenta // param4 = switch flag number - // param6 = model (TODO) + // param6 = model (even value = short, odd value = long) {0x0082, F_V0_V4, 0x00004FF0000300FE, "TObjLazerFenceNorm"}, {0x0083, F_V0_V4, 0x00004FF03FFB00FE, "TObjLazerFence4"}, @@ -1419,8 +1446,8 @@ static const vector dat_object_definitions({ // param1-3 = TODO {0x0086, F_V0_V4, 0x00004E0000000006, "TButterfly"}, - // TODO: Describe this object. Params: - // param1 = model number + // Small vehicle. Params: + // param1 = model number (even value = crashed, odd value = intact) {0x0087, F_V0_V4, 0x0000400000000006, "TMotorcycle"}, // Item box. Params: @@ -1439,8 +1466,9 @@ static const vector dat_object_definitions({ {0x0088, F_V0_V4, 0x00004FF0B00000FE, "TObjContainerBase2"}, {0x0088, F_EP3, 0x0000000000000002, "TObjContainerBase2"}, - // Elevated cylindrical tank. Params: - // param1-3 = TODO + // Elevated cylindrical tank. There appear to be no parameters; param1-3 + // are copied into the object instance as a Vector3F, but it appears that + // that vector is never used. {0x0089, F_V0_V4, 0x0000400000000006, "TObjTank"}, // TODO: Describe this object. Params: @@ -1463,7 +1491,7 @@ static const vector dat_object_definitions({ // param4 = base switch flag (the actual switch flags used are param4, // param4 + 1, param4 + 2, etc.) // param5 = number of switch flags (clamped to [1, 4]) - // param6 = TODO (only matters if this is zero or nonzero) + // param6 = movement effect (0 = sparks + thud, 1 = grinding sound) {0x008C, F_V0_V1, 0x0000000000000006, "TObjContainerIdo"}, {0x008C, F_V2_V4, 0x000040000000000E, "TObjContainerIdo"}, @@ -1473,8 +1501,9 @@ static const vector dat_object_definitions({ // negative = enabled when player is within 70 units // range [0x00, 0xFF] = enabled by corresponding switch flag // 0x100 and above = never enabled - // param5 = message number (used with message quest opcode; TODO: has - // the same [100, 999] check as some other objects) + // param5 = message number ("character ID" in qedit; if this is outside + // the range [100, 999], the quest label in param6 is called in the + // free play script instead of the quest script) // param6 = quest label to call when activated {0x008D, F_V0_V4, 0x00004000000027FE, "TOCapsuleAncient01"}, @@ -1492,7 +1521,7 @@ static const vector dat_object_definitions({ {0x008F, F_V0_V4, 0x0000400000000006, "TObjHashi"}, // Generic switch. Visually, this is the type usually used for objects - // other than doors, such as lights, poison rooms, and the Forest 1 + // other than doors, such as lights, poison rooms, and the Forest 2 // bridge. Params: // param1 = activation mode: // negative = temporary (TODO: test this) @@ -1558,7 +1587,8 @@ static const vector dat_object_definitions({ // Params for TOHangceilingCave01Normal (param6 = 0): // param1 = TODO (radius delta? value is param1 + 29) // param2 = TODO (value is 1 - param2) - // param3 = TODO (value is param3 + 100) + // param3 = base damage (value is param3 + 100; scaled by difficulty: + // Normal = 1x, Hard = 2x, Very Hard = 3x, Ultimate = 6x) // Params for TOHangceilingCave01Key (param6 = 1): // param1-3 = same as for TOHangceilingCave01Normal // param4 = switch flag number (drops when switch flag is activated; @@ -1698,10 +1728,13 @@ static const vector dat_object_definitions({ // appears that some may have different scale factors or offsets (TODO). {0x0106, F_V0_V4, 0x00004000000000C0, "TODragonflyMachine01"}, - // Floating blue light. Params: - // param4 = TODO - // param5 = TODO - // param6 = TODO + // Floating rotating blue light (often appears next to doors). The light + // rotates about its vertical axis and floats up and down sinusoidally. + // Params: + // param4 = float cycles per second (value is param4 * 0.1 + 1.0) + // param5 = max float distance (value is param5 * 0.1 + 0.5) + // param6 = rotation speed in angle units per frame (0x10000 = 1 complete + // rotation) {0x0107, F_V0_V4, 0x00004000000000C0, "TOLightMachine01"}, // Self-destructing objects. Params: @@ -1711,22 +1744,23 @@ static const vector dat_object_definitions({ {0x010A, F_V0_V4, 0x00004000000000C0, "TOExplosiveMachine03"}, // Spark machine. This looks like it's intended to appear in the bridge - // rooms in the Mines, to create an effect of the columnar machines - // sparking. This is implemented as four columns that randomly change their - // visibility. Each column has an accumulator value, which is initially - // zero. Every frame, the value (param1 - 0.98) is added to each column's - // accumulator and a random number between 0 and 1 is chosen; if the random - // number is less than the column's accumulator, its visibility state is - // changed and its accumulator is reset to zero. In this manner, param1 can - // be thought of as the frequency of state changes - 0.98 would mean they - // never change state, 1.98 would mean they change every frame. Params: + // room in Mine 1, to create an effect of the columnar machines sparking. + // This is implemented as four columns that randomly change their + // visibility, intended to be in the same position as the map geometry. + // Each column has an accumulator value, which is initially zero. Every + // frame, the value (param1 - 0.98) is added to each column's accumulator + // and a random number between 0 and 1 is chosen; if the random number is + // less than the column's accumulator, its visibility state is changed and + // its accumulator is reset to zero. In this manner, param1 can be thought + // of as the frequency of state changes - 0.98 would mean they never change + // state, 1.98 would mean they change every frame. Params: // param1 = state change accumulation per frame (value is param1 - 0.98) - // param2 = if <= 0, only one column flickers and the other are always + // param2 = if <= 0, only one column flickers and the others are always // visible; if > 0, all columns flicker {0x010B, F_V0_V4, 0x00004000000000C0, "TOSparkMachine01"}, - // Large flashing box. Params: - // param2 = TODO (seems it only matters if this is < 0 or not) + // Open stall with a spinning red light on either side. Params: + // param2 = if > 0, a gray box is present in the left half of the stall {0x010C, F_V0_V4, 0x00004000000000C0, "TOHangerMachine01"}, // Ruins entrance door (after Vol Opt). This object reads quest flags diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index af756ac1..73667b95 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -2036,13 +2036,20 @@ asio::awaitable C_6x(shared_ptr c, Channel::Message& msg) c->pos = msg.check_size_t().pos; break; - case 0x40: - c->pos = msg.check_size_t().pos; + case 0x40: { + const auto& cmd = msg.check_size_t(); + c->pos.x = cmd.pos.x; + c->pos.z = cmd.pos.z; break; + } case 0x41: - c->pos = msg.check_size_t().pos; + case 0x42: { + const auto& cmd = msg.check_size_t(); + c->pos.x = cmd.pos.x; + c->pos.z = cmd.pos.z; break; + } case 0x48: if (!is_v1(c->version()) && c->check_flag(Client::Flag::INFINITE_TP_ENABLED)) { diff --git a/src/QuestScript.cc b/src/QuestScript.cc index c922edc7..a8d92c22 100644 --- a/src/QuestScript.cc +++ b/src/QuestScript.cc @@ -630,7 +630,9 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = { // => value of rXX as %d (signed integer) // => value of rXX as %f (floating-point) (v3 and later) // => changes text color like $CX would (supported on 11/2000 and - // later); X must be numeric, so does not work + // later); X must be numeric and in the range 0-7, so , , and do not work (though \tC8, \tC9, and \tCG can be used + // directly in the text, and do work) // => newline // or => character's name // or => character's class diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index ba790fee..53223ace 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -1263,6 +1263,7 @@ static asio::awaitable on_sync_joining_player_disp_and_inventory( throw logic_error("6x70 command from unknown game version"); } + c->pos = c->last_reported_6x70->base.pos; send_game_player_state(target, c, false); } @@ -1845,7 +1846,18 @@ static asio::awaitable on_play_sound_from_player(shared_ptr c, Sub //////////////////////////////////////////////////////////////////////////////// template -static asio::awaitable on_movement(shared_ptr c, SubcommandMessage& msg) { +static asio::awaitable on_movement_xz(shared_ptr c, SubcommandMessage& msg) { + const auto& cmd = msg.check_size_t(); + if (cmd.header.client_id != c->lobby_client_id) { + co_return; + } + c->pos.x = cmd.pos.x; + c->pos.z = cmd.pos.z; + forward_subcommand(c, msg); +} + +template +static asio::awaitable on_movement_xyz(shared_ptr c, SubcommandMessage& msg) { const auto& cmd = msg.check_size_t(); if (cmd.header.client_id != c->lobby_client_id) { co_return; @@ -1855,7 +1867,21 @@ static asio::awaitable on_movement(shared_ptr c, SubcommandMessage } template -static asio::awaitable on_movement_with_floor(shared_ptr c, SubcommandMessage& msg) { +static asio::awaitable on_movement_xz_with_floor(shared_ptr c, SubcommandMessage& msg) { + const auto& cmd = msg.check_size_t(); + if (cmd.header.client_id != c->lobby_client_id) { + co_return; + } + c->pos.x = cmd.pos.x; + c->pos.z = cmd.pos.z; + if (cmd.floor >= 0 && c->floor != static_cast(cmd.floor)) { + c->floor = cmd.floor; + } + forward_subcommand(c, msg); +} + +template +static asio::awaitable on_movement_xyz_with_floor(shared_ptr c, SubcommandMessage& msg) { const auto& cmd = msg.check_size_t(); if (cmd.header.client_id != c->lobby_client_id) { co_return; @@ -3735,14 +3761,14 @@ static asio::awaitable on_set_entity_pos_and_angle_6x17(shared_ptr if (l->area_for_floor(c->version(), c->floor) != 0x0D) { throw runtime_error("client sent 6x17 command in area other than Vol Opt"); } - if (cmd.header.entity_id != c->lobby_client_id) { - // If the target is on a different floor or does not exist, just drop the - // command instead of raising; this could have been due to a data race - auto target = l->clients.at(cmd.header.entity_id); - if (!target || target->floor != c->floor) { - co_return; - } + + // If the target is on a different floor or does not exist, just drop the + // command instead of raising; this could have been due to a data race + auto target = l->clients.at(cmd.header.entity_id); + if (!target || target->floor != c->floor) { + co_return; } + target->pos = cmd.pos; co_await forward_subcommand_with_entity_id_transcode_t(c, msg); } @@ -5358,11 +5384,11 @@ const vector subcommand_definitions{ /* 6x1D */ {0x19, 0x1B, 0x1D, on_invalid}, /* 6x1E */ {0x1A, 0x1C, 0x1E, on_invalid}, /* 6x1F */ {0x1B, 0x1D, 0x1F, on_change_floor_6x1F}, - /* 6x20 */ {0x1C, 0x1E, 0x20, on_movement_with_floor}, + /* 6x20 */ {0x1C, 0x1E, 0x20, on_movement_xyz_with_floor}, /* 6x21 */ {0x1D, 0x1F, 0x21, on_change_floor_6x21}, /* 6x22 */ {0x1E, 0x20, 0x22, on_forward_check_client}, /* 6x23 */ {0x1F, 0x21, 0x23, on_set_player_visible}, - /* 6x24 */ {0x20, 0x22, 0x24, on_forward_check_game}, + /* 6x24 */ {0x20, 0x22, 0x24, on_movement_xyz}, /* 6x25 */ {0x21, 0x23, 0x25, on_equip_item}, /* 6x26 */ {0x22, 0x24, 0x26, on_unequip_item}, // TODO: Why does BB allow this in the lobby? /* 6x27 */ {0x23, 0x25, 0x27, on_use_item}, @@ -5389,11 +5415,11 @@ const vector subcommand_definitions{ /* 6x3B */ {NONE, 0x38, 0x3B, forward_subcommand_m}, /* 6x3C */ {0x34, 0x39, 0x3C, forward_subcommand_m}, /* 6x3D */ {0x35, 0x3A, 0x3D, on_invalid}, - /* 6x3E */ {NONE, NONE, 0x3E, on_movement_with_floor}, - /* 6x3F */ {0x36, 0x3B, 0x3F, on_movement_with_floor}, - /* 6x40 */ {0x37, 0x3C, 0x40, on_movement}, - /* 6x41 */ {0x38, 0x3D, 0x41, on_movement}, - /* 6x42 */ {0x39, 0x3E, 0x42, on_movement}, + /* 6x3E */ {NONE, NONE, 0x3E, on_movement_xyz_with_floor}, + /* 6x3F */ {0x36, 0x3B, 0x3F, on_movement_xyz_with_floor}, + /* 6x40 */ {0x37, 0x3C, 0x40, on_movement_xz}, + /* 6x41 */ {0x38, 0x3D, 0x41, on_movement_xz}, + /* 6x42 */ {0x39, 0x3E, 0x42, on_movement_xz}, /* 6x43 */ {0x3A, 0x3F, 0x43, on_forward_check_game_client}, /* 6x44 */ {0x3B, 0x40, 0x44, on_forward_check_game_client}, /* 6x45 */ {0x3C, 0x41, 0x45, on_forward_check_game_client}, @@ -5412,8 +5438,8 @@ const vector subcommand_definitions{ /* 6x52 */ {0x46, 0x4C, 0x52, on_set_animation_state}, /* 6x53 */ {0x47, 0x4D, 0x53, on_forward_check_game}, /* 6x54 */ {0x48, 0x4E, 0x54, forward_subcommand_m}, - /* 6x55 */ {0x49, 0x4F, 0x55, on_forward_check_game_client}, - /* 6x56 */ {0x4A, 0x50, 0x56, on_movement}, + /* 6x55 */ {0x49, 0x4F, 0x55, on_movement_xyz}, + /* 6x56 */ {0x4A, 0x50, 0x56, on_movement_xyz}, /* 6x57 */ {NONE, 0x51, 0x57, on_forward_check_client}, /* 6x58 */ {NONE, NONE, 0x58, on_forward_check_client}, /* 6x59 */ {0x4B, 0x52, 0x59, on_pick_up_item}, diff --git a/system/client-functions/Debug/CreateObject.3OE1.patch.s b/system/client-functions/Debug/CreateObject.3OE1.patch.s new file mode 100644 index 00000000..430b9190 --- /dev/null +++ b/system/client-functions/Debug/CreateObject.3OE1.patch.s @@ -0,0 +1,52 @@ +.meta hide_from_patches_menu +.meta name="CreateObject" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + mflr r0 + b get_data +get_data_ret: + mflr r3 + mtlr r0 + lwz r0, [r3] + mtctr r0 + addi r3, r3, 4 + bctr + +get_data: + bl get_data_ret + .data 0x8020C158 # construct_dat_object_from_args +base_type_high: + .data 0xFFFF0000 # base_type, set_flags +floor_low: + .data 0x0000FFFF # index, floor + .data 0x00000000 # entity_id, group + .data 0x00000000 # room, unknown_a3 +pos_x: + .float 0.0 # pos.x +pos_y: + .float 0.0 # pos.y +pos_z: + .float 0.0 # pos.z +angle_x: + .data 0x00000000 # angle.x +angle_y: + .data 0x00000000 # angle.y +angle_z: + .data 0x00000000 # angle.z +param1: + .float 0.0 # param1 +param2: + .float 0.0 # param2 +param3: + .float 0.0 # param3 +param4: + .data 0 # param4 +param5: + .data 0 # param5 +param6: + .data 0 # param6 + .data 0 # unused_obj_ptr diff --git a/system/client-functions/Debug/CreateObject.3SE0.patch.s b/system/client-functions/Debug/CreateObject.3SE0.patch.s new file mode 100644 index 00000000..1aba194c --- /dev/null +++ b/system/client-functions/Debug/CreateObject.3SE0.patch.s @@ -0,0 +1,52 @@ +.meta hide_from_patches_menu +.meta name="CreateObject" +.meta description="" + +entry_ptr: +reloc0: + .offsetof start +start: + mflr r0 + b get_data +get_data_ret: + mflr r4 + mtlr r0 + lwz r0, [r4] + mtctr r0 + addi r4, r4, 4 + bctr + +get_data: + bl get_data_ret + .data 0x80056D6C # construct_dat_object_from_args +base_type_high: + .data 0xFFFF0000 # base_type, set_flags +floor_low: + .data 0x0000FFFF # index, floor + .data 0x00000000 # entity_id, group + .data 0x00000000 # room, unknown_a3 +pos_x: + .float 0.0 # pos.x +pos_y: + .float 0.0 # pos.y +pos_z: + .float 0.0 # pos.z +angle_x: + .data 0x00000000 # angle.x +angle_y: + .data 0x00000000 # angle.y +angle_z: + .data 0x00000000 # angle.z +param1: + .float 0.0 # param1 +param2: + .float 0.0 # param2 +param3: + .float 0.0 # param3 +param4: + .data 0 # param4 +param5: + .data 0 # param5 +param6: + .data 0 # param6 + .data 0 # unused_obj_ptr