add $makeobj; update some object notes
This commit is contained in:
@@ -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.
|
||||
* `$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.
|
||||
* `$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
|
||||
* `$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).
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "ChatCommands.hh"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <filesystem>
|
||||
@@ -1622,6 +1623,101 @@ ChatCommandDefinition cc_loadchar(
|
||||
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(
|
||||
{"$matcount"},
|
||||
+[](const Args& a) -> asio::awaitable<void> {
|
||||
|
||||
+1
-1
@@ -149,7 +149,7 @@ public:
|
||||
uint8_t override_lobby_number = 0x80; // 80 = no override
|
||||
int64_t override_random_seed = -1;
|
||||
std::unique_ptr<Variations> override_variations;
|
||||
VectorXZF pos;
|
||||
VectorXYZF pos;
|
||||
uint32_t floor = 0x0F;
|
||||
std::weak_ptr<Lobby> lobby;
|
||||
uint8_t lobby_client_id = 0;
|
||||
|
||||
@@ -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)
|
||||
|
||||
+85
-51
@@ -618,7 +618,8 @@ static const vector<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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<DATEntityDefinition> 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
|
||||
|
||||
+10
-3
@@ -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;
|
||||
break;
|
||||
|
||||
case 0x40:
|
||||
c->pos = msg.check_size_t<G_WalkToPosition_6x40>().pos;
|
||||
case 0x40: {
|
||||
const auto& cmd = msg.check_size_t<G_WalkToPosition_6x40>();
|
||||
c->pos.x = cmd.pos.x;
|
||||
c->pos.z = cmd.pos.z;
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
case 0x48:
|
||||
if (!is_v1(c->version()) && c->check_flag(Client::Flag::INFINITE_TP_ENABLED)) {
|
||||
|
||||
+3
-1
@@ -630,7 +630,9 @@ static const QuestScriptOpcodeDefinition opcode_defs[] = {
|
||||
// <rXX> => value of rXX as %d (signed integer)
|
||||
// <fXX> => value of rXX as %f (floating-point) (v3 and later)
|
||||
// <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
|
||||
// <hero name> or <name hero> => character's name
|
||||
// <hero job> or <name job> => character's class
|
||||
|
||||
+44
-18
@@ -1263,6 +1263,7 @@ static asio::awaitable<void> 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<void> on_play_sound_from_player(shared_ptr<Client> c, Sub
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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>();
|
||||
if (cmd.header.client_id != c->lobby_client_id) {
|
||||
co_return;
|
||||
@@ -1855,7 +1867,21 @@ static asio::awaitable<void> on_movement(shared_ptr<Client> c, SubcommandMessage
|
||||
}
|
||||
|
||||
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>();
|
||||
if (cmd.header.client_id != c->lobby_client_id) {
|
||||
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) {
|
||||
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<G_SetEntityPositionAndAngle_6x17>(c, msg);
|
||||
}
|
||||
@@ -5358,11 +5384,11 @@ const vector<SubcommandDefinition> 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<G_SetPosition_6x20>},
|
||||
/* 6x20 */ {0x1C, 0x1E, 0x20, on_movement_xyz_with_floor<G_SetPosition_6x20>},
|
||||
/* 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<G_TeleportPlayer_6x24>},
|
||||
/* 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<SubcommandDefinition> 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<G_StopAtPosition_6x3E>},
|
||||
/* 6x3F */ {0x36, 0x3B, 0x3F, on_movement_with_floor<G_SetPosition_6x3F>},
|
||||
/* 6x40 */ {0x37, 0x3C, 0x40, on_movement<G_WalkToPosition_6x40>},
|
||||
/* 6x41 */ {0x38, 0x3D, 0x41, on_movement<G_MoveToPosition_6x41_6x42>},
|
||||
/* 6x42 */ {0x39, 0x3E, 0x42, on_movement<G_MoveToPosition_6x41_6x42>},
|
||||
/* 6x3E */ {NONE, NONE, 0x3E, on_movement_xyz_with_floor<G_StopAtPosition_6x3E>},
|
||||
/* 6x3F */ {0x36, 0x3B, 0x3F, on_movement_xyz_with_floor<G_SetPosition_6x3F>},
|
||||
/* 6x40 */ {0x37, 0x3C, 0x40, on_movement_xz<G_WalkToPosition_6x40>},
|
||||
/* 6x41 */ {0x38, 0x3D, 0x41, on_movement_xz<G_MoveToPosition_6x41_6x42>},
|
||||
/* 6x42 */ {0x39, 0x3E, 0x42, on_movement_xz<G_MoveToPosition_6x41_6x42>},
|
||||
/* 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<SubcommandDefinition> 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<G_SetPlayerPositionAndAngle_6x56>},
|
||||
/* 6x55 */ {0x49, 0x4F, 0x55, on_movement_xyz<G_IntraMapWarp_6x55>},
|
||||
/* 6x56 */ {0x4A, 0x50, 0x56, on_movement_xyz<G_SetPlayerPositionAndAngle_6x56>},
|
||||
/* 6x57 */ {NONE, 0x51, 0x57, on_forward_check_client},
|
||||
/* 6x58 */ {NONE, NONE, 0x58, on_forward_check_client},
|
||||
/* 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
|
||||
Reference in New Issue
Block a user