add $makeobj; update some object notes

This commit is contained in:
Martin Michelsen
2025-11-02 17:13:20 -08:00
parent 4e2f62bc73
commit 155ed6bcf9
10 changed files with 346 additions and 77 deletions
+1
View File
@@ -606,6 +606,7 @@ Some commands only work for clients not in proxy sessions. The chat commands are
* `$ss <data>`: Send a command to the remote server (if in a proxy session) or to the game server. * `$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. * `$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. * `$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 <type> [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 * Personal state commands
* `$arrow <color-id>`: 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). * `$arrow <color-id>`: 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).
+96
View File
@@ -1,5 +1,6 @@
#include "ChatCommands.hh" #include "ChatCommands.hh"
#include <ctype.h>
#include <string.h> #include <string.h>
#include <filesystem> #include <filesystem>
@@ -1622,6 +1623,101 @@ ChatCommandDefinition cc_loadchar(
co_return; co_return;
}); });
ChatCommandDefinition cc_makeobj(
{"$makeobj"},
+[](const Args& a) -> asio::awaitable<void> {
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<uint32_t*>(&z);
};
unordered_map<string, uint32_t> 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( ChatCommandDefinition cc_matcount(
{"$matcount"}, {"$matcount"},
+[](const Args& a) -> asio::awaitable<void> { +[](const Args& a) -> asio::awaitable<void> {
+1 -1
View File
@@ -149,7 +149,7 @@ public:
uint8_t override_lobby_number = 0x80; // 80 = no override uint8_t override_lobby_number = 0x80; // 80 = no override
int64_t override_random_seed = -1; int64_t override_random_seed = -1;
std::unique_ptr<Variations> override_variations; std::unique_ptr<Variations> override_variations;
VectorXZF pos; VectorXYZF pos;
uint32_t floor = 0x0F; uint32_t floor = 0x0F;
std::weak_ptr<Lobby> lobby; std::weak_ptr<Lobby> lobby;
uint8_t lobby_client_id = 0; uint8_t lobby_client_id = 0;
+2 -3
View File
@@ -4210,8 +4210,7 @@ struct G_VolOptBossActions_6x16 {
// 6x17: Set entity position and angle (not valid on Episode 3) // 6x17: Set entity position and angle (not valid on Episode 3)
// This command sets an entity's position and angle without performing any // 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 // validity checks, even on v3 and later.
// affects a player other than the sender.
struct G_SetEntityPositionAndAngle_6x17 { struct G_SetEntityPositionAndAngle_6x17 {
G_EntityIDHeader header; G_EntityIDHeader header;
@@ -4760,7 +4759,7 @@ struct G_IntraMapWarp_6x55 {
G_ClientIDHeader header; G_ClientIDHeader header;
le_uint32_t angle_y = 0; le_uint32_t angle_y = 0;
VectorXYZF from_pos; VectorXYZF from_pos;
VectorXYZF to_pos; VectorXYZF pos;
} __packed_ws__(G_IntraMapWarp_6x55, 0x20); } __packed_ws__(G_IntraMapWarp_6x55, 0x20);
// 6x56: Set player position and angle (protected on GC NTE/V3/V4) // 6x56: Set player position and angle (protected on GC NTE/V3/V4)
+85 -51
View File
@@ -618,7 +618,8 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// param4 = source type: // param4 = source type:
// 0 = use this set when advancing from a lower floor // 0 = use this set when advancing from a lower floor
// 1 = use this set when returning from a higher 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_V0_V4, 0x00007FFFFFFFFFFF, "TObjPlayerSet"},
{0x0000, F_EP3, 0x0000000000008001, "TObjPlayerSet"}, {0x0000, F_EP3, 0x0000000000008001, "TObjPlayerSet"},
@@ -938,7 +939,7 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// Radar collision. Params: // Radar collision. Params:
// param1 = radius // param1 = radius
// param4 = TODO // param4 = minimap segment ID
{0x0017, F_V0_V4, 0x00004FFFFFF8FFFE, "TObjRaderCol"}, {0x0017, F_V0_V4, 0x00004FFFFFF8FFFE, "TObjRaderCol"},
{0x0017, F_EP3, 0x0000000000008000, "TObjRaderCol"}, {0x0017, F_EP3, 0x0000000000008000, "TObjRaderCol"},
@@ -990,7 +991,7 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// that the object is not destroyed immediately if it's blue and the game // that the object is not destroyed immediately if it's blue and the game
// is Challenge mode. Params: // is Challenge mode. Params:
// param1 = player set ID (TODO: what exactly does this do? Looks like it // 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 // param4 = destination floor
// param6 = color (0 = blue, 1 = red) // param6 = color (0 = blue, 1 = red)
{0x001B, F_V1_V4, 0x00005000000078FE, "TObjAreaWarpQuest"}, {0x001B, F_V1_V4, 0x00005000000078FE, "TObjAreaWarpQuest"},
@@ -1005,10 +1006,10 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// Params: // Params:
// param1 = TODO // param1 = TODO
// param2 = TODO // param2 = TODO
// param3 = TODO // param3 = TODO (1 byte only)
// param4 = TODO // param4 = TODO (1 byte only)
// param5 = TODO // param5 = TODO (1 byte only)
// param6 = TODO // param6 = TODO (1 byte only)
{0x001D, F_V2_V4, 0x0000400000000002, "TEffStarLight2D_Base"}, {0x001D, F_V2_V4, 0x0000400000000002, "TEffStarLight2D_Base"},
// VR Temple Beta / Barba Ray lens flare effect. // VR Temple Beta / Barba Ray lens flare effect.
@@ -1017,7 +1018,6 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// Hides the area map when the player is near this object. Params: // Hides the area map when the player is near this object. Params:
// param1 = radius // param1 = radius
// TODO: Test this.
{0x001F, F_V2_V4, 0x00004FFC3FFB07FE, "TObjRaderHideCol"}, {0x001F, F_V2_V4, 0x00004FFC3FFB07FE, "TObjRaderHideCol"},
// Item-detecting floor switch. Params: // Item-detecting floor switch. Params:
@@ -1114,7 +1114,30 @@ static const vector<DATEntityDefinition> dat_object_definitions({
{0x0023, F_V2_V4, 0x00004FFC3FFB07FE, "TOAttackableCol"}, {0x0023, F_V2_V4, 0x00004FFC3FFB07FE, "TOAttackableCol"},
// Damage effect. Params: // 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 // param1 = damage radius
// param2 = damage value, scaled by difficulty: // param2 = damage value, scaled by difficulty:
// Normal: param2 * 2 // Normal: param2 * 2
@@ -1132,9 +1155,9 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// Switch flag timer. This object watches for a switch flag to be // Switch flag timer. This object watches for a switch flag to be
// activated, then once it is, waits for a specified time, then disables // 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 switch flag or activates up to three other switch flags. Note that
// that this object just provides the timer functionality; the trigger // this object just provides the timer functionality; the trigger switch
// switch flag must be activated by some other object. Params: // flag must be activated by some other object. Params:
// angle.x = trigger mode: // angle.x = trigger mode:
// 0 = disable watched switch flag when timer expires // 0 = disable watched switch flag when timer expires
// any positive number = enable up to 3 other switch flags when timer // any positive number = enable up to 3 other switch flags when timer
@@ -1341,18 +1364,16 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// be no parameters. // be no parameters.
{0x0054, F_V0_V4, 0x0000600000000001, "TObjCityDoor_Lobby"}, {0x0054, F_V0_V4, 0x0000600000000001, "TObjCityDoor_Lobby"},
// Version of the main warp for Challenge mode? This object seems to // Version of the main warp for Challenge mode. This object looks like the
// behave similarly to boss teleporters; it shows the player a Yes/No // main Ragol warp on Pioneer 2, but behaves similarly to boss teleporters:
// confirmation menu and sends 6x6A to synchronize state. There is a // it shows the player a Yes/No confirmation menu and sends 6x6A to
// global named last_set_mainwarp_value which is set to param4 when this // synchronize state. There is a global named last_set_mainwarp_value which
// object is constructed, but may be changed by a set_mainwarp quest // is set to param4 when this object is constructed, but may be changed by
// opcode after that. If that happens, this object replaces its // a set_mainwarp quest opcode after that. If that happens, this object
// dest_floor with the floor specified in the last set_mainwarp quest // replaces its dest_floor with the floor specified in the last
// opcode. Params: // set_mainwarp quest opcode. Params:
// param4 = destination floor // param4 = destination floor
// param5 = switch flag number // 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"}, {0x0055, F_V2_V4, 0x0000600000040001, "TObjCityMainWarpChallenge"},
// Episode 2 Lab door. Params: // Episode 2 Lab door. Params:
@@ -1378,8 +1399,10 @@ static const vector<DATEntityDefinition> dat_object_definitions({
{0x0057, F_V3_V4, 0x0000600000040001, "TObjTradeCollision"}, {0x0057, F_V3_V4, 0x0000600000040001, "TObjTradeCollision"},
{0x0057, F_EP3, 0x0000000000000001, "TObjTradeCollision"}, {0x0057, F_EP3, 0x0000000000000001, "TObjTradeCollision"},
// TODO: Describe this object. Presumably similar to TObjTradeCollision // This object appears to be unused in both Ep3 NTE and the final release.
// but enables the deck edit counter? Params: // 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 // param1 = radius
{0x0058, F_EP3, 0x0000000000000001, "TObjDeckCollision"}, {0x0058, F_EP3, 0x0000000000000001, "TObjDeckCollision"},
@@ -1398,9 +1421,13 @@ static const vector<DATEntityDefinition> dat_object_definitions({
{0x0081, F_V0_V4, 0x00004FF00078003E, "TObjDoorKey"}, {0x0081, F_V0_V4, 0x00004FF00078003E, "TObjDoorKey"},
// Laser fence and square laser fence. Params: // 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 // param4 = switch flag number
// param6 = model (TODO) // param6 = model (even value = short, odd value = long)
{0x0082, F_V0_V4, 0x00004FF0000300FE, "TObjLazerFenceNorm"}, {0x0082, F_V0_V4, 0x00004FF0000300FE, "TObjLazerFenceNorm"},
{0x0083, F_V0_V4, 0x00004FF03FFB00FE, "TObjLazerFence4"}, {0x0083, F_V0_V4, 0x00004FF03FFB00FE, "TObjLazerFence4"},
@@ -1419,8 +1446,8 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// param1-3 = TODO // param1-3 = TODO
{0x0086, F_V0_V4, 0x00004E0000000006, "TButterfly"}, {0x0086, F_V0_V4, 0x00004E0000000006, "TButterfly"},
// TODO: Describe this object. Params: // Small vehicle. Params:
// param1 = model number // param1 = model number (even value = crashed, odd value = intact)
{0x0087, F_V0_V4, 0x0000400000000006, "TMotorcycle"}, {0x0087, F_V0_V4, 0x0000400000000006, "TMotorcycle"},
// Item box. Params: // Item box. Params:
@@ -1439,8 +1466,9 @@ static const vector<DATEntityDefinition> dat_object_definitions({
{0x0088, F_V0_V4, 0x00004FF0B00000FE, "TObjContainerBase2"}, {0x0088, F_V0_V4, 0x00004FF0B00000FE, "TObjContainerBase2"},
{0x0088, F_EP3, 0x0000000000000002, "TObjContainerBase2"}, {0x0088, F_EP3, 0x0000000000000002, "TObjContainerBase2"},
// Elevated cylindrical tank. Params: // Elevated cylindrical tank. There appear to be no parameters; param1-3
// param1-3 = TODO // are copied into the object instance as a Vector3F, but it appears that
// that vector is never used.
{0x0089, F_V0_V4, 0x0000400000000006, "TObjTank"}, {0x0089, F_V0_V4, 0x0000400000000006, "TObjTank"},
// TODO: Describe this object. Params: // TODO: Describe this object. Params:
@@ -1463,7 +1491,7 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// param4 = base switch flag (the actual switch flags used are param4, // param4 = base switch flag (the actual switch flags used are param4,
// param4 + 1, param4 + 2, etc.) // param4 + 1, param4 + 2, etc.)
// param5 = number of switch flags (clamped to [1, 4]) // 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_V0_V1, 0x0000000000000006, "TObjContainerIdo"},
{0x008C, F_V2_V4, 0x000040000000000E, "TObjContainerIdo"}, {0x008C, F_V2_V4, 0x000040000000000E, "TObjContainerIdo"},
@@ -1473,8 +1501,9 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// negative = enabled when player is within 70 units // negative = enabled when player is within 70 units
// range [0x00, 0xFF] = enabled by corresponding switch flag // range [0x00, 0xFF] = enabled by corresponding switch flag
// 0x100 and above = never enabled // 0x100 and above = never enabled
// param5 = message number (used with message quest opcode; TODO: has // param5 = message number ("character ID" in qedit; if this is outside
// the same [100, 999] check as some other objects) // 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 // param6 = quest label to call when activated
{0x008D, F_V0_V4, 0x00004000000027FE, "TOCapsuleAncient01"}, {0x008D, F_V0_V4, 0x00004000000027FE, "TOCapsuleAncient01"},
@@ -1492,7 +1521,7 @@ static const vector<DATEntityDefinition> dat_object_definitions({
{0x008F, F_V0_V4, 0x0000400000000006, "TObjHashi"}, {0x008F, F_V0_V4, 0x0000400000000006, "TObjHashi"},
// Generic switch. Visually, this is the type usually used for objects // 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: // bridge. Params:
// param1 = activation mode: // param1 = activation mode:
// negative = temporary (TODO: test this) // negative = temporary (TODO: test this)
@@ -1558,7 +1587,8 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// Params for TOHangceilingCave01Normal (param6 = 0): // Params for TOHangceilingCave01Normal (param6 = 0):
// param1 = TODO (radius delta? value is param1 + 29) // param1 = TODO (radius delta? value is param1 + 29)
// param2 = TODO (value is 1 - param2) // 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): // Params for TOHangceilingCave01Key (param6 = 1):
// param1-3 = same as for TOHangceilingCave01Normal // param1-3 = same as for TOHangceilingCave01Normal
// param4 = switch flag number (drops when switch flag is activated; // param4 = switch flag number (drops when switch flag is activated;
@@ -1698,10 +1728,13 @@ static const vector<DATEntityDefinition> dat_object_definitions({
// appears that some may have different scale factors or offsets (TODO). // appears that some may have different scale factors or offsets (TODO).
{0x0106, F_V0_V4, 0x00004000000000C0, "TODragonflyMachine01"}, {0x0106, F_V0_V4, 0x00004000000000C0, "TODragonflyMachine01"},
// Floating blue light. Params: // Floating rotating blue light (often appears next to doors). The light
// param4 = TODO // rotates about its vertical axis and floats up and down sinusoidally.
// param5 = TODO // Params:
// param6 = TODO // 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"}, {0x0107, F_V0_V4, 0x00004000000000C0, "TOLightMachine01"},
// Self-destructing objects. Params: // Self-destructing objects. Params:
@@ -1711,22 +1744,23 @@ static const vector<DATEntityDefinition> dat_object_definitions({
{0x010A, F_V0_V4, 0x00004000000000C0, "TOExplosiveMachine03"}, {0x010A, F_V0_V4, 0x00004000000000C0, "TOExplosiveMachine03"},
// Spark machine. This looks like it's intended to appear in the bridge // 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 // room in Mine 1, to create an effect of the columnar machines sparking.
// sparking. This is implemented as four columns that randomly change their // This is implemented as four columns that randomly change their
// visibility. Each column has an accumulator value, which is initially // visibility, intended to be in the same position as the map geometry.
// zero. Every frame, the value (param1 - 0.98) is added to each column's // Each column has an accumulator value, which is initially zero. Every
// accumulator and a random number between 0 and 1 is chosen; if the random // frame, the value (param1 - 0.98) is added to each column's accumulator
// number is less than the column's accumulator, its visibility state is // and a random number between 0 and 1 is chosen; if the random number is
// changed and its accumulator is reset to zero. In this manner, param1 can // less than the column's accumulator, its visibility state is changed and
// be thought of as the frequency of state changes - 0.98 would mean they // its accumulator is reset to zero. In this manner, param1 can be thought
// never change state, 1.98 would mean they change every frame. Params: // 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) // 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 // visible; if > 0, all columns flicker
{0x010B, F_V0_V4, 0x00004000000000C0, "TOSparkMachine01"}, {0x010B, F_V0_V4, 0x00004000000000C0, "TOSparkMachine01"},
// Large flashing box. Params: // Open stall with a spinning red light on either side. Params:
// param2 = TODO (seems it only matters if this is < 0 or not) // param2 = if > 0, a gray box is present in the left half of the stall
{0x010C, F_V0_V4, 0x00004000000000C0, "TOHangerMachine01"}, {0x010C, F_V0_V4, 0x00004000000000C0, "TOHangerMachine01"},
// Ruins entrance door (after Vol Opt). This object reads quest flags // Ruins entrance door (after Vol Opt). This object reads quest flags
+10 -3
View File
@@ -2036,13 +2036,20 @@ asio::awaitable<HandlerResult> C_6x(shared_ptr<Client> c, Channel::Message& msg)
c->pos = msg.check_size_t<G_SetPosition_6x3F>().pos; c->pos = msg.check_size_t<G_SetPosition_6x3F>().pos;
break; break;
case 0x40: case 0x40: {
c->pos = msg.check_size_t<G_WalkToPosition_6x40>().pos; const auto& cmd = msg.check_size_t<G_WalkToPosition_6x40>();
c->pos.x = cmd.pos.x;
c->pos.z = cmd.pos.z;
break; break;
}
case 0x41: case 0x41:
c->pos = msg.check_size_t<G_MoveToPosition_6x41_6x42>().pos; case 0x42: {
const auto& cmd = msg.check_size_t<G_MoveToPosition_6x41_6x42>();
c->pos.x = cmd.pos.x;
c->pos.z = cmd.pos.z;
break; break;
}
case 0x48: case 0x48:
if (!is_v1(c->version()) && c->check_flag(Client::Flag::INFINITE_TP_ENABLED)) { if (!is_v1(c->version()) && c->check_flag(Client::Flag::INFINITE_TP_ENABLED)) {
+3 -1
View File
@@ -630,7 +630,9 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
// <rXX> => value of rXX as %d (signed integer) // <rXX> => value of rXX as %d (signed integer)
// <fXX> => value of rXX as %f (floating-point) (v3 and later) // <fXX> => value of rXX as %f (floating-point) (v3 and later)
// <color X> => changes text color like $CX would (supported on 11/2000 and // <color X> => changes text color like $CX would (supported on 11/2000 and
// later); X must be numeric, so <color G> does not work // later); X must be numeric and in the range 0-7, so <color 8>, <color
// 9>, and <color G> do not work (though \tC8, \tC9, and \tCG can be used
// directly in the text, and do work)
// <cr> => newline // <cr> => newline
// <hero name> or <name hero> => character's name // <hero name> or <name hero> => character's name
// <hero job> or <name job> => character's class // <hero job> or <name job> => character's class
+44 -18
View File
@@ -1263,6 +1263,7 @@ static asio::awaitable<void> on_sync_joining_player_disp_and_inventory(
throw logic_error("6x70 command from unknown game version"); throw logic_error("6x70 command from unknown game version");
} }
c->pos = c->last_reported_6x70->base.pos;
send_game_player_state(target, c, false); send_game_player_state(target, c, false);
} }
@@ -1845,7 +1846,18 @@ static asio::awaitable<void> on_play_sound_from_player(shared_ptr<Client> c, Sub
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
template <typename CmdT> template <typename CmdT>
static asio::awaitable<void> on_movement(shared_ptr<Client> c, SubcommandMessage& msg) { static asio::awaitable<void> on_movement_xz(shared_ptr<Client> c, SubcommandMessage& msg) {
const auto& cmd = msg.check_size_t<CmdT>();
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 <typename CmdT>
static asio::awaitable<void> on_movement_xyz(shared_ptr<Client> c, SubcommandMessage& msg) {
const auto& cmd = msg.check_size_t<CmdT>(); const auto& cmd = msg.check_size_t<CmdT>();
if (cmd.header.client_id != c->lobby_client_id) { if (cmd.header.client_id != c->lobby_client_id) {
co_return; co_return;
@@ -1855,7 +1867,21 @@ static asio::awaitable<void> on_movement(shared_ptr<Client> c, SubcommandMessage
} }
template <typename CmdT> template <typename CmdT>
static asio::awaitable<void> on_movement_with_floor(shared_ptr<Client> c, SubcommandMessage& msg) { static asio::awaitable<void> on_movement_xz_with_floor(shared_ptr<Client> c, SubcommandMessage& msg) {
const auto& cmd = msg.check_size_t<CmdT>();
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<uint32_t>(cmd.floor)) {
c->floor = cmd.floor;
}
forward_subcommand(c, msg);
}
template <typename CmdT>
static asio::awaitable<void> on_movement_xyz_with_floor(shared_ptr<Client> c, SubcommandMessage& msg) {
const auto& cmd = msg.check_size_t<CmdT>(); const auto& cmd = msg.check_size_t<CmdT>();
if (cmd.header.client_id != c->lobby_client_id) { if (cmd.header.client_id != c->lobby_client_id) {
co_return; co_return;
@@ -3735,14 +3761,14 @@ static asio::awaitable<void> on_set_entity_pos_and_angle_6x17(shared_ptr<Client>
if (l->area_for_floor(c->version(), c->floor) != 0x0D) { if (l->area_for_floor(c->version(), c->floor) != 0x0D) {
throw runtime_error("client sent 6x17 command in area other than Vol Opt"); 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 // 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 // command instead of raising; this could have been due to a data race
auto target = l->clients.at(cmd.header.entity_id); auto target = l->clients.at(cmd.header.entity_id);
if (!target || target->floor != c->floor) { if (!target || target->floor != c->floor) {
co_return; co_return;
}
} }
target->pos = cmd.pos;
co_await forward_subcommand_with_entity_id_transcode_t<G_SetEntityPositionAndAngle_6x17>(c, msg); co_await forward_subcommand_with_entity_id_transcode_t<G_SetEntityPositionAndAngle_6x17>(c, msg);
} }
@@ -5358,11 +5384,11 @@ const vector<SubcommandDefinition> subcommand_definitions{
/* 6x1D */ {0x19, 0x1B, 0x1D, on_invalid}, /* 6x1D */ {0x19, 0x1B, 0x1D, on_invalid},
/* 6x1E */ {0x1A, 0x1C, 0x1E, on_invalid}, /* 6x1E */ {0x1A, 0x1C, 0x1E, on_invalid},
/* 6x1F */ {0x1B, 0x1D, 0x1F, on_change_floor_6x1F}, /* 6x1F */ {0x1B, 0x1D, 0x1F, on_change_floor_6x1F},
/* 6x20 */ {0x1C, 0x1E, 0x20, on_movement_with_floor<G_SetPosition_6x20>}, /* 6x20 */ {0x1C, 0x1E, 0x20, on_movement_xyz_with_floor<G_SetPosition_6x20>},
/* 6x21 */ {0x1D, 0x1F, 0x21, on_change_floor_6x21}, /* 6x21 */ {0x1D, 0x1F, 0x21, on_change_floor_6x21},
/* 6x22 */ {0x1E, 0x20, 0x22, on_forward_check_client}, /* 6x22 */ {0x1E, 0x20, 0x22, on_forward_check_client},
/* 6x23 */ {0x1F, 0x21, 0x23, on_set_player_visible}, /* 6x23 */ {0x1F, 0x21, 0x23, on_set_player_visible},
/* 6x24 */ {0x20, 0x22, 0x24, on_forward_check_game}, /* 6x24 */ {0x20, 0x22, 0x24, on_movement_xyz<G_TeleportPlayer_6x24>},
/* 6x25 */ {0x21, 0x23, 0x25, on_equip_item}, /* 6x25 */ {0x21, 0x23, 0x25, on_equip_item},
/* 6x26 */ {0x22, 0x24, 0x26, on_unequip_item}, // TODO: Why does BB allow this in the lobby? /* 6x26 */ {0x22, 0x24, 0x26, on_unequip_item}, // TODO: Why does BB allow this in the lobby?
/* 6x27 */ {0x23, 0x25, 0x27, on_use_item}, /* 6x27 */ {0x23, 0x25, 0x27, on_use_item},
@@ -5389,11 +5415,11 @@ const vector<SubcommandDefinition> subcommand_definitions{
/* 6x3B */ {NONE, 0x38, 0x3B, forward_subcommand_m}, /* 6x3B */ {NONE, 0x38, 0x3B, forward_subcommand_m},
/* 6x3C */ {0x34, 0x39, 0x3C, forward_subcommand_m}, /* 6x3C */ {0x34, 0x39, 0x3C, forward_subcommand_m},
/* 6x3D */ {0x35, 0x3A, 0x3D, on_invalid}, /* 6x3D */ {0x35, 0x3A, 0x3D, on_invalid},
/* 6x3E */ {NONE, NONE, 0x3E, on_movement_with_floor<G_StopAtPosition_6x3E>}, /* 6x3E */ {NONE, NONE, 0x3E, on_movement_xyz_with_floor<G_StopAtPosition_6x3E>},
/* 6x3F */ {0x36, 0x3B, 0x3F, on_movement_with_floor<G_SetPosition_6x3F>}, /* 6x3F */ {0x36, 0x3B, 0x3F, on_movement_xyz_with_floor<G_SetPosition_6x3F>},
/* 6x40 */ {0x37, 0x3C, 0x40, on_movement<G_WalkToPosition_6x40>}, /* 6x40 */ {0x37, 0x3C, 0x40, on_movement_xz<G_WalkToPosition_6x40>},
/* 6x41 */ {0x38, 0x3D, 0x41, on_movement<G_MoveToPosition_6x41_6x42>}, /* 6x41 */ {0x38, 0x3D, 0x41, on_movement_xz<G_MoveToPosition_6x41_6x42>},
/* 6x42 */ {0x39, 0x3E, 0x42, on_movement<G_MoveToPosition_6x41_6x42>}, /* 6x42 */ {0x39, 0x3E, 0x42, on_movement_xz<G_MoveToPosition_6x41_6x42>},
/* 6x43 */ {0x3A, 0x3F, 0x43, on_forward_check_game_client}, /* 6x43 */ {0x3A, 0x3F, 0x43, on_forward_check_game_client},
/* 6x44 */ {0x3B, 0x40, 0x44, on_forward_check_game_client}, /* 6x44 */ {0x3B, 0x40, 0x44, on_forward_check_game_client},
/* 6x45 */ {0x3C, 0x41, 0x45, on_forward_check_game_client}, /* 6x45 */ {0x3C, 0x41, 0x45, on_forward_check_game_client},
@@ -5412,8 +5438,8 @@ const vector<SubcommandDefinition> subcommand_definitions{
/* 6x52 */ {0x46, 0x4C, 0x52, on_set_animation_state}, /* 6x52 */ {0x46, 0x4C, 0x52, on_set_animation_state},
/* 6x53 */ {0x47, 0x4D, 0x53, on_forward_check_game}, /* 6x53 */ {0x47, 0x4D, 0x53, on_forward_check_game},
/* 6x54 */ {0x48, 0x4E, 0x54, forward_subcommand_m}, /* 6x54 */ {0x48, 0x4E, 0x54, forward_subcommand_m},
/* 6x55 */ {0x49, 0x4F, 0x55, on_forward_check_game_client}, /* 6x55 */ {0x49, 0x4F, 0x55, on_movement_xyz<G_IntraMapWarp_6x55>},
/* 6x56 */ {0x4A, 0x50, 0x56, on_movement<G_SetPlayerPositionAndAngle_6x56>}, /* 6x56 */ {0x4A, 0x50, 0x56, on_movement_xyz<G_SetPlayerPositionAndAngle_6x56>},
/* 6x57 */ {NONE, 0x51, 0x57, on_forward_check_client}, /* 6x57 */ {NONE, 0x51, 0x57, on_forward_check_client},
/* 6x58 */ {NONE, NONE, 0x58, on_forward_check_client}, /* 6x58 */ {NONE, NONE, 0x58, on_forward_check_client},
/* 6x59 */ {0x4B, 0x52, 0x59, on_pick_up_item}, /* 6x59 */ {0x4B, 0x52, 0x59, on_pick_up_item},
@@ -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
@@ -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