From 83291d550172fa2d0bb64b80720e54d8fb7dcf11 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Thu, 19 Mar 2026 23:01:30 -0700 Subject: [PATCH] more details on player_flags --- src/CommandFormats.hh | 28 +++----- src/Map.cc | 45 +++++++++++- src/QuestScript.cc | 2 +- src/ReceiveSubcommands.cc | 70 ++++++++++--------- src/SendCommands.cc | 8 +-- .../Debug/PlayerFlags.3OE1.patch.s | 13 ++-- 6 files changed, 99 insertions(+), 67 deletions(-) diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 47c27560..a5c81fb9 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -3782,13 +3782,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) @@ -3803,16 +3804,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. @@ -5452,9 +5445,10 @@ 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); diff --git a/src/Map.cc b/src/Map.cc index 9fde4c2c..f2503723 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -3446,9 +3446,48 @@ void MapFile::RandomState::generate_shuffled_location_table( } const array 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 */ + 0x00E0, /* TObjEneMe3SinowZoaReal/TObjEneEpsilonBody (depending on area) */ + 0x00E0, /* TObjEneMe3SinowZoaReal/TObjEneEpsilonBody (depending on area) */ + 0x00E1, /* TObjEneIllGill */ +}; MapFile::MapFile(std::shared_ptr data) { for (uint8_t z = 0; z < this->sections_for_floor.size(); z++) { diff --git a/src/QuestScript.cc b/src/QuestScript.cc index 505f6e5f..ddfb1161 100644 --- a/src/QuestScript.cc +++ b/src/QuestScript.cc @@ -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. diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 136f1bba..e95723d1 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -1147,40 +1147,42 @@ uint32_t Parsed6x70Data::convert_player_flags(uint32_t player_flags, bool to_v3) // 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 = unknown (TODO) - // ? 00000004 ? 00000004 = loading? (allows 6x3E to update room ID and near-enemy flag; TODO: what does this do?) - // ? 00000008 ? 00000008 = unknown (TODO) - // ? 00000010 00000010 = should send position update? (6x3E, 6x40, or 6x42) - // ? 00000020 ? 00000020 = unknown (TODO) - // ? 00000040 ? 00000040 = unknown (TODO) - // ? 00000080 ? 00000080 = seems to affect some particle effects? (TODO) - // ? 00000100 -------- = unknown (TODO) - // ? 00000200 00000100 = chat or pause menu is open (suppresses action palette) - // ? 00000400 ? 00000200 = unknown (TODO) - // ? 00000800 -------- = unknown (TODO) - // ? 00001000 -------- = unknown (TODO) - // ? 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) - // ? 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) - // ? 00100000 00010000 = has permanent trap vision (e.g. is android) - // ? 00200000 ? 00020000 = related to items; seems always set for the local player in P2 but not other players? - // ? 00400000 ? 00040000 = warping? (TODO: set by 6x21 and 6x22) - // ? 00800000 ? 00080000 = unknown (TODO) - // ? 01000000 ? 00100000 = unknown (TODO) - // ? 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, but items are visible (TODO: is this used for Stealth Suit on BB?) - // ? 10000000 01000000 = if set, player does not drop weapon on death - // -------- ? 02000000 = used by TObjRoomId when param6 == 0x00010000 (TODO: figure out what this does) - // -------- 04000000 = is sitting in lobby chair - // -------- ? 08000000 = related to lobby chairs (TODO: see handle_6xAE) - // -------- 10000000 = using alternate lobby chair pose (X+B instead of X+A on GC, for example) + // V1/V2 V3/V4 + // 00000001 00000001 = player hold is set (see notes on 6x2C and 6x2D in CommandFormats.hh) + // 00000002 00000002 ? = unknown (TODO: related to player holds; client sends 6x2D when this is set) + // 00000004 00000004 = is on a different floor from the local player (or is loading into game) + // 00000008 00000008 ? = unknown (TODO) + // 00000010 00000010 = player has sent any update command (position, attack, etc.) this frame + // 00000020 00000020 ? = unknown (TODO) + // 00000040 00000040 ? = unknown (TODO) + // 00000080 00000080 = is in windup animation for technique or PB + // 00000100 -------- ? = unknown (TODO) + // 00000200 00000100 = chat, pause, or quick menu is open (suppresses action palette) + // 00000400 00000200 = is in PB cutscene + // 00000800 -------- ? = unknown (TODO) + // 00001000 -------- ? = unknown (TODO) + // 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) + // 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) + // 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 free-play 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 ? = 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 ? = is doing lobby animation? (TODO: verify this) + // -------- 40000000 ? = unknown (TODO: used in offline multi-player Challenge mode; see tree from 3OE1:801BE594) if (to_v3) { return (player_flags & 0x000000FF) | diff --git a/src/SendCommands.cc b/src/SendCommands.cc index 40fe86c8..c850c239 100644 --- a/src/SendCommands.cc +++ b/src/SendCommands.cc @@ -2663,16 +2663,16 @@ asio::awaitable send_change_player_hp( } asio::awaitable send_remove_negative_conditions(shared_ptr 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 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)); diff --git a/system/client-functions/Debug/PlayerFlags.3OE1.patch.s b/system/client-functions/Debug/PlayerFlags.3OE1.patch.s index 77f9a408..7d0273a3 100644 --- a/system/client-functions/Debug/PlayerFlags.3OE1.patch.s +++ b/system/client-functions/Debug/PlayerFlags.3OE1.patch.s @@ -34,10 +34,10 @@ hook_again: mr r3, r30 bl TObjPlayer_for_client_id cmplwi r3, 0 - beq hook_player_missing + beq hook_skip_player lwz r6, [r3 + 0x0334] # player_flags - lwz r3, [r13 - 0x5280] # local_client_id - cmp r3, r30 + lwz r4, [r13 - 0x5280] # local_client_id + cmp r4, r30 bne hook_not_local_player lis r3, 0xFFFF ori r3, r3, 0x00FF @@ -45,15 +45,11 @@ hook_again: hook_not_local_player: lis r3, 0xFFFF ori r3, r3, 0xFFFF - b hook_player_flags_ok -hook_player_missing: - lis r3, 0xFF00 - ori r3, r3, 0x00FF hook_player_flags_ok: bl set_debug_text_color lis r3, 0x0002 - ori r3, r3, 0x0018 + ori r3, r3, 0x000B add r3, r3, r30 bl hook_get_fmt_string .binary "Player %2d: %08X"00000000 @@ -62,6 +58,7 @@ hook_get_fmt_string: mr r5, r30 bl render_debug_printf +hook_skip_player: addi r30, r30, 1 cmplwi r30, 0x0C blt hook_again