make some sense out of game_flags and player_flags
This commit is contained in:
+15
-5
@@ -2349,14 +2349,18 @@ ChatCommandDefinition cc_saverec(
|
||||
co_return;
|
||||
});
|
||||
|
||||
static asio::awaitable<void> command_send_command(const Args& a, bool to_client, bool to_server) {
|
||||
static asio::awaitable<void> command_send_command(const Args& a, bool to_client, bool to_server, bool send_protected) {
|
||||
if (!a.c->proxy_session) {
|
||||
a.check_debug_enabled();
|
||||
}
|
||||
string data = phosg::parse_data_string(a.text);
|
||||
data.resize((data.size() + 3) & (~3));
|
||||
if (to_client) {
|
||||
a.c->channel->send(data);
|
||||
if (send_protected) {
|
||||
co_await send_protected_command(a.c, data.data(), data.size(), false);
|
||||
} else {
|
||||
a.c->channel->send(data);
|
||||
}
|
||||
}
|
||||
if (to_server) {
|
||||
if (a.c->proxy_session) {
|
||||
@@ -2371,13 +2375,19 @@ static asio::awaitable<void> command_send_command(const Args& a, bool to_client,
|
||||
ChatCommandDefinition cc_sb(
|
||||
{"$sb"},
|
||||
+[](const Args& a) -> asio::awaitable<void> {
|
||||
return command_send_command(a, true, true);
|
||||
return command_send_command(a, true, true, false);
|
||||
});
|
||||
|
||||
ChatCommandDefinition cc_sc(
|
||||
{"$sc"},
|
||||
+[](const Args& a) -> asio::awaitable<void> {
|
||||
return command_send_command(a, true, false);
|
||||
return command_send_command(a, true, false, false);
|
||||
});
|
||||
|
||||
ChatCommandDefinition cc_scp(
|
||||
{"$scp"},
|
||||
+[](const Args& a) -> asio::awaitable<void> {
|
||||
return command_send_command(a, true, false, true);
|
||||
});
|
||||
|
||||
ChatCommandDefinition cc_secid(
|
||||
@@ -2571,7 +2581,7 @@ ChatCommandDefinition cc_spec(
|
||||
ChatCommandDefinition cc_ss(
|
||||
{"$ss"},
|
||||
+[](const Args& a) -> asio::awaitable<void> {
|
||||
return command_send_command(a, false, true);
|
||||
return command_send_command(a, false, true, false);
|
||||
});
|
||||
|
||||
ChatCommandDefinition cc_stat(
|
||||
|
||||
+137
-93
@@ -3723,6 +3723,49 @@ struct G_UpdateEnemyStateT_6x0A {
|
||||
G_EntityIDHeader header;
|
||||
le_uint16_t enemy_index = 0; // [0, 0xB50)
|
||||
le_uint16_t total_damage = 0;
|
||||
// The bits in game_flags mean (all addresses in TODOs are for 3OE1):
|
||||
// 00000001 = is poisoned
|
||||
// 00000002 = is paralyzed
|
||||
// 00000004 = is shocked
|
||||
// 00000008 = is slow
|
||||
// 00000010 = is confused
|
||||
// 00000020 = is frozen
|
||||
// 00000040 = cures and prevents all negative status effects
|
||||
// 00000080 = appears to be unused (TODO: look for any usage of this flag)
|
||||
// 00000100 = missed by attack (often set immediately before showing red "MISS" text)
|
||||
// 00000200 = hit by attack (causes flinch for most enemies)
|
||||
// 00000400 = last hit did damage greater than 25% of enemy's max HP (some enemies don't clear this)
|
||||
// 00000800 = is dead (when set for most enemies, plays the death animation and then destroys the enemy)
|
||||
// 00001000 = unknown (TODO: see TObjEnemyV8048ee80_v1C, TObjEnemyV8048ee80_v3B, TObjEneDolmOlm_v3B)
|
||||
// 00002000 = unknown (TODO: see TObjGrass_v1E, 8011EA08, TObjEneIllGill_v1E, TObjEneIllGill_init)
|
||||
// 00004000 = unknown (TODO: has status effect in slot 5; see TObjectV8047c128_v24_update_paralysis_effect; De Rol
|
||||
// Le uses this; Vol Opt uses it too at TBoss3Volopt_update, TBoss3VoloptCore_update,
|
||||
// TBoss3VoloptP01_update, TBoss3VolOptP02_update, TObjectV8047c128_v39, TObjectV8047c128_v38; related
|
||||
// to paralysis somehow? see TObjectV8047c128_v24_update_paralysis_effect)
|
||||
// 00008000 = immune to freeze (TODO: see TBoss3VolOptP02_init_inner; maybe other things use it too)
|
||||
// 00010000 = unknown (TODO: see 801BA1F8)
|
||||
// 00020000 = can't attack, cast techs, or use items (e.g. Vol Opt cage and Ruins falling traps set this)
|
||||
// 00040000 = untargetable (e.g. TObjEneBeast sets this at construction time; it's cleared when it roars)
|
||||
// 00080000 = appears to be unused (TODO: look for any usage of this flag)
|
||||
// 00100000 = for players, is near enemy; for some enemies, is activated (not set if in idle animations, e.g. for
|
||||
// TObjEneBeast and related classes) (unverified on v2)
|
||||
// 00200000 = is attacking? (TODO: also set when TObjEneMoja is jumping though; also see TObjEnemyV8048ee80_v3C,
|
||||
// TObjEneMerillLia_v3C, TObjEneSaver_v3A)
|
||||
// 00400000 = unknown (TODO: see TOSensor_vF, 801CC358, 801CD224)
|
||||
// 00800000 = affected by gravity? can be aerial? (wolves don't have this, perhaps their jumps are hardcoded; see
|
||||
// TObjEnemyV8048ee80_v5B) (unverified on v2)
|
||||
// 01000000 = immune to shock and freeze and paralysis (for Canadine/Mothmant/etc, only set when they're higher
|
||||
// than player level; TODO: maybe other things too; also used by TObjPlayer, see 801B84B4; also has a
|
||||
// meaning for weapons, see TItemWeapon_v10) (unverified on v2)
|
||||
// 02000000 = invisible (also suppresses particles, some sounds, and hit/miss text)
|
||||
// 04000000 = temporarily invincible (e.g. Dragon while it roars to advance to phase 2) (unverified on v2)
|
||||
// 08000000 = unknown (TODO; see 80113384, TItemMag_v1B, TItemMag_v1A, 801182B0, TObjPlayer_render; probably
|
||||
// graphical effects only)
|
||||
// 10000000 = entity is player
|
||||
// 20000000 = entity is enemy
|
||||
// 40000000 = entity is object (some entities have both this and 20000000 set; this appears to make TWindowLockOn
|
||||
// not show anything but the entity is still attackable, see TWindowLockOn_should_show_for_entity)
|
||||
// 80000000 = entity is item
|
||||
typename std::conditional_t<BE, be_uint32_t, le_uint32_t> game_flags = 0;
|
||||
} __attribute__((packed));
|
||||
using G_UpdateEnemyState_GC_6x0A = G_UpdateEnemyStateT_6x0A<true>;
|
||||
@@ -4125,7 +4168,7 @@ struct G_Unknown_6x36 {
|
||||
|
||||
struct G_PhotonBlast_6x37 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t unknown_a1 = 0;
|
||||
le_uint16_t amount = 0; // Amount of PB energy to expend (ignored by client upon receipt)
|
||||
le_uint16_t unused = 0;
|
||||
} __packed_ws__(G_PhotonBlast_6x37, 8);
|
||||
|
||||
@@ -4212,7 +4255,9 @@ struct G_SetPosition_6x3F {
|
||||
struct G_WalkToPosition_6x40 {
|
||||
G_ClientIDHeader header;
|
||||
VectorXZF pos;
|
||||
le_uint32_t action = 0;
|
||||
// In the flags field, bit 00000008 is set if game_flag 00100000 is set (same as is_near_enemy in 6x3E and 6x3F). The
|
||||
// meanings of the other bits are unknown.
|
||||
le_uint32_t flags = 0;
|
||||
} __packed_ws__(G_WalkToPosition_6x40, 0x10);
|
||||
|
||||
// 6x41: Move to position (v1)
|
||||
@@ -4290,8 +4335,8 @@ struct G_ShieldAttack_6x4A {
|
||||
G_ClientIDHeader header;
|
||||
} __packed_ws__(G_ShieldAttack_6x4A, 4);
|
||||
|
||||
// 6x4B: Hit by enemy (protected on GC NTE/V3/V4)
|
||||
// 6x4C: Hit by enemy (protected on GC NTE/V3/V4)
|
||||
// 6x4B: Minor hit by enemy (<= 25% of max HP; protected on GC NTE/V3/V4)
|
||||
// 6x4C: Major hit by enemy (> 25% of max HP; protected on GC NTE/V3/V4)
|
||||
|
||||
struct G_HitByEnemy_6x4B_6x4C {
|
||||
G_ClientIDHeader header;
|
||||
@@ -4393,7 +4438,7 @@ struct G_Unknown_6x57 {
|
||||
struct G_LobbyAnimation_6x58 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t animation_number = 0;
|
||||
le_uint16_t unused = 0;
|
||||
le_uint16_t flags = 0; // Only lowest bit appears to be used
|
||||
} __packed_ws__(G_LobbyAnimation_6x58, 8);
|
||||
|
||||
// 6x59: Pick up item
|
||||
@@ -4688,7 +4733,7 @@ struct G_6x70_Sub_Telepipe {
|
||||
struct G_6x70_Base_DCNTE {
|
||||
/* 0000 */ le_uint16_t client_id = 0;
|
||||
/* 0002 */ le_uint16_t room_id = 0;
|
||||
/* 0004 */ le_uint32_t flags1 = 0;
|
||||
/* 0004 */ le_uint32_t game_flags = 0;
|
||||
/* 0008 */ VectorXYZF pos;
|
||||
/* 0014 */ VectorXYZI angle;
|
||||
/* 0020 */ le_uint16_t phase = 0;
|
||||
@@ -4696,41 +4741,39 @@ struct G_6x70_Base_DCNTE {
|
||||
} __packed_ws__(G_6x70_Base_DCNTE, 0x24);
|
||||
|
||||
struct G_SyncPlayerDispAndInventory_DCNTE_6x70 {
|
||||
// Offsets in this struct are relative to the overall command header
|
||||
/* 0004 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x60, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DCNTE_6x70)};
|
||||
/* 000C */ G_6x70_Base_DCNTE base;
|
||||
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x60, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DCNTE_6x70)};
|
||||
/* 0008 */ G_6x70_Base_DCNTE base;
|
||||
// The following two fields appear to contain uninitialized data
|
||||
/* 0030 */ le_uint32_t unknown_a5 = 0;
|
||||
/* 0034 */ le_uint32_t unknown_a6 = 0;
|
||||
/* 0038 */ G_6x70_Sub_Telepipe telepipe;
|
||||
/* 0054 */ le_uint32_t death_flags = 0;
|
||||
/* 0058 */ PlayerHoldState_DCProtos hold_state;
|
||||
/* 0068 */ le_uint32_t area = 0;
|
||||
/* 006C */ le_uint32_t game_flags = 0;
|
||||
/* 0070 */ PlayerVisualConfig visual;
|
||||
/* 00C0 */ PlayerStats stats;
|
||||
/* 00E4 */ le_uint32_t num_items = 0;
|
||||
/* 00E8 */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 0430 */
|
||||
/* 002C */ le_uint32_t unknown_a5 = 0;
|
||||
/* 0030 */ le_uint32_t unknown_a6 = 0;
|
||||
/* 0034 */ G_6x70_Sub_Telepipe telepipe;
|
||||
/* 0050 */ le_uint32_t death_flags = 0;
|
||||
/* 0054 */ PlayerHoldState_DCProtos hold_state;
|
||||
/* 0064 */ le_uint32_t area = 0;
|
||||
/* 0068 */ le_uint32_t player_flags = 0;
|
||||
/* 006C */ PlayerVisualConfig visual;
|
||||
/* 00BC */ PlayerStats stats;
|
||||
/* 00E0 */ le_uint32_t num_items = 0;
|
||||
/* 00E4 */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 042C */
|
||||
} __packed_ws__(G_SyncPlayerDispAndInventory_DCNTE_6x70, 0x42C);
|
||||
|
||||
struct G_SyncPlayerDispAndInventory_DC112000_6x70 {
|
||||
// Offsets in this struct are relative to the overall command header
|
||||
/* 0004 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x67, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC112000_6x70)};
|
||||
/* 000C */ G_6x70_Base_DCNTE base;
|
||||
/* 0030 */ le_uint16_t bonus_hp_from_materials = 0;
|
||||
/* 0032 */ le_uint16_t bonus_tp_from_materials = 0;
|
||||
/* 0034 */ parray<uint8_t, 0x10> unknown_a5;
|
||||
/* 0044 */ G_6x70_Sub_Telepipe telepipe;
|
||||
/* 0060 */ le_uint32_t death_flags = 0;
|
||||
/* 0064 */ PlayerHoldState_DCProtos hold_state;
|
||||
/* 0074 */ le_uint32_t area = 0;
|
||||
/* 0078 */ le_uint32_t game_flags = 0;
|
||||
/* 007C */ PlayerVisualConfig visual;
|
||||
/* 00CC */ PlayerStats stats;
|
||||
/* 00F0 */ le_uint32_t num_items = 0;
|
||||
/* 00F4 */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 043C */
|
||||
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x67, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC112000_6x70)};
|
||||
/* 0008 */ G_6x70_Base_DCNTE base;
|
||||
/* 002C */ le_uint16_t bonus_hp_from_materials = 0;
|
||||
/* 002E */ le_uint16_t bonus_tp_from_materials = 0;
|
||||
/* 0030 */ parray<uint8_t, 0x10> unknown_a5;
|
||||
/* 0040 */ G_6x70_Sub_Telepipe telepipe;
|
||||
/* 005C */ le_uint32_t death_flags = 0;
|
||||
/* 0060 */ PlayerHoldState_DCProtos hold_state;
|
||||
/* 0070 */ le_uint32_t area = 0;
|
||||
/* 0074 */ le_uint32_t player_flags = 0;
|
||||
/* 0078 */ PlayerVisualConfig visual;
|
||||
/* 00C8 */ PlayerStats stats;
|
||||
/* 00EC */ le_uint32_t num_items = 0;
|
||||
/* 00F0 */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 0438 */
|
||||
} __packed_ws__(G_SyncPlayerDispAndInventory_DC112000_6x70, 0x438);
|
||||
|
||||
struct G_6x70_Base_V1 {
|
||||
@@ -4741,71 +4784,71 @@ struct G_6x70_Base_V1 {
|
||||
/* 0034 */ StatusEffectState temporary_status_effect;
|
||||
/* 0040 */ StatusEffectState attack_status_effect;
|
||||
/* 004C */ StatusEffectState defense_status_effect;
|
||||
/* 0058 */ StatusEffectState unused_status_effect;
|
||||
/* 0058 */ StatusEffectState unknown_a1_status_effect;
|
||||
/* 0064 */ le_uint32_t language32 = 0;
|
||||
/* 0068 */ le_uint32_t player_tag = 0;
|
||||
/* 006C */ le_uint32_t guild_card_number = 0;
|
||||
/* 0070 */ le_uint32_t unknown_a6 = 0; // Probably battle-related (assigned together with battle_team_number)
|
||||
/* 0074 */ le_uint32_t battle_team_number = 0;
|
||||
/* 0078 */ G_6x70_Sub_Telepipe telepipe;
|
||||
/* 0094 */ le_uint32_t death_flags = 0; // Only a few bits are used. 4 = player is dead
|
||||
// Only a few bits appear to be used in death_flags. Known values:
|
||||
// 00000001 = should drop weapon/item on death
|
||||
// 00000002 = has automatic revival item (Scape Doll or Ragol Ring)
|
||||
// 00000004 = unknown (TODO; causes client to send 6x31 instead of 6xA1 when revived)
|
||||
/* 0094 */ le_uint32_t death_flags = 0;
|
||||
/* 0098 */ PlayerHoldState hold_state;
|
||||
/* 00AC */ le_uint32_t area = 0;
|
||||
/* 00B0 */ le_uint32_t game_flags = 0;
|
||||
/* 00B0 */ le_uint32_t player_flags = 0;
|
||||
/* 00B4 */ parray<uint8_t, 0x14> technique_levels_v1 = 0xFF; // Last byte is uninitialized
|
||||
/* 00C8 */ PlayerVisualConfig visual;
|
||||
/* 0118 */
|
||||
} __packed_ws__(G_6x70_Base_V1, 0x118);
|
||||
|
||||
struct G_SyncPlayerDispAndInventory_DC_PC_6x70 {
|
||||
// Offsets in this struct are relative to the overall command header
|
||||
/* 0004 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC_PC_6x70)};
|
||||
/* 000C */ G_6x70_Base_V1 base;
|
||||
/* 0124 */ PlayerStats stats;
|
||||
/* 0148 */ le_uint32_t num_items = 0;
|
||||
/* 014C */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 0494 */
|
||||
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_DC_PC_6x70)};
|
||||
/* 0008 */ G_6x70_Base_V1 base;
|
||||
/* 0120 */ PlayerStats stats;
|
||||
/* 0144 */ le_uint32_t num_items = 0;
|
||||
/* 0148 */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 0490 */
|
||||
} __packed_ws__(G_SyncPlayerDispAndInventory_DC_PC_6x70, 0x490);
|
||||
|
||||
// GC NTE also uses this format.
|
||||
// GC NTE also uses this format
|
||||
struct G_SyncPlayerDispAndInventory_GC_6x70 {
|
||||
// Offsets in this struct are relative to the overall command header
|
||||
/* 0004 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_GC_6x70)};
|
||||
/* 000C */ G_6x70_Base_V1 base;
|
||||
/* 0124 */ PlayerStats stats;
|
||||
/* 0148 */ le_uint32_t num_items = 0;
|
||||
/* 014C */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 0494 */ le_uint32_t floor = 0;
|
||||
/* 0498 */
|
||||
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_GC_6x70)};
|
||||
/* 0008 */ G_6x70_Base_V1 base;
|
||||
/* 0120 */ PlayerStats stats;
|
||||
/* 0144 */ le_uint32_t num_items = 0;
|
||||
/* 0148 */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 0490 */ le_uint32_t floor = 0;
|
||||
/* 0494 */
|
||||
} __packed_ws__(G_SyncPlayerDispAndInventory_GC_6x70, 0x494);
|
||||
|
||||
struct G_SyncPlayerDispAndInventory_XB_6x70 {
|
||||
// Offsets in this struct are relative to the overall command header
|
||||
/* 0004 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_XB_6x70)};
|
||||
/* 000C */ G_6x70_Base_V1 base;
|
||||
/* 0124 */ PlayerStats stats;
|
||||
/* 0148 */ le_uint32_t num_items = 0;
|
||||
/* 014C */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 0494 */ le_uint32_t floor = 0;
|
||||
/* 0498 */ le_uint32_t xb_user_id_high = 0;
|
||||
/* 049C */ le_uint32_t xb_user_id_low = 0;
|
||||
/* 04A0 */ le_uint32_t unknown_a16 = 0;
|
||||
/* 04A4 */
|
||||
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_XB_6x70)};
|
||||
/* 0008 */ G_6x70_Base_V1 base;
|
||||
/* 0120 */ PlayerStats stats;
|
||||
/* 0144 */ le_uint32_t num_items = 0;
|
||||
/* 0148 */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 0490 */ le_uint32_t floor = 0;
|
||||
/* 0494 */ le_uint32_t xb_user_id_high = 0;
|
||||
/* 0498 */ le_uint32_t xb_user_id_low = 0;
|
||||
/* 049C */ le_uint32_t unknown_a16 = 0;
|
||||
/* 04A0 */
|
||||
} __packed_ws__(G_SyncPlayerDispAndInventory_XB_6x70, 0x4A0);
|
||||
|
||||
struct G_SyncPlayerDispAndInventory_BB_6x70 {
|
||||
// Offsets in this struct are relative to the overall command header
|
||||
/* 0008 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_BB_6x70)};
|
||||
/* 0010 */ G_6x70_Base_V1 base;
|
||||
/* 0128 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> name;
|
||||
/* 0148 */ PlayerStats stats;
|
||||
/* 016C */ le_uint32_t num_items = 0;
|
||||
/* 0170 */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 04B8 */ le_uint32_t floor = 0;
|
||||
/* 04BC */ le_uint32_t xb_user_id_high = 0;
|
||||
/* 04C0 */ le_uint32_t xb_user_id_low = 0;
|
||||
/* 04C4 */ le_uint32_t unknown_a16 = 0;
|
||||
/* 04C8 */
|
||||
/* 0000 */ G_ExtendedHeaderT<G_ClientIDHeader> header = {{0x70, 0x00, 0x0000}, sizeof(G_SyncPlayerDispAndInventory_BB_6x70)};
|
||||
/* 0008 */ G_6x70_Base_V1 base;
|
||||
/* 0120 */ pstring<TextEncoding::UTF16_ALWAYS_MARKED, 0x10> name;
|
||||
/* 0140 */ PlayerStats stats;
|
||||
/* 0164 */ le_uint32_t num_items = 0;
|
||||
/* 0168 */ parray<PlayerInventoryItem, 0x1E> items;
|
||||
/* 04B0 */ le_uint32_t floor = 0;
|
||||
/* 04B4 */ le_uint32_t xb_user_id_high = 0;
|
||||
/* 04B8 */ le_uint32_t xb_user_id_low = 0;
|
||||
/* 04BC */ le_uint32_t unknown_a16 = 0;
|
||||
/* 04C0 */
|
||||
} __packed_ws__(G_SyncPlayerDispAndInventory_BB_6x70, 0x4C0);
|
||||
|
||||
// 6x71: Unblock game join (used while loading into game)
|
||||
@@ -5007,6 +5050,8 @@ struct G_EnableDropWeaponOnDeath_6x82 {
|
||||
struct G_PlaceTrap_6x83 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint16_t trap_type = 0;
|
||||
// trap_index is actually the number of traps remaining for this client after setting this one (so it counts backward
|
||||
// from their total trap count)
|
||||
le_uint16_t trap_index = 0;
|
||||
} __packed_ws__(G_PlaceTrap_6x83, 8);
|
||||
|
||||
@@ -5205,13 +5250,18 @@ struct G_LevelUpAllTechniques_6x9B {
|
||||
parray<uint8_t, 3> unused;
|
||||
} __packed_ws__(G_LevelUpAllTechniques_6x9B, 8);
|
||||
|
||||
// 6x9C: Set enemy low game flags (not valid on Episode 3)
|
||||
// This command only has an effect in Ultimate mode; it sets the low 6 bits of game_flags (those that match 0x3F).
|
||||
// 6x9C: Set enemy status effect flags (not valid on Episode 3)
|
||||
// This command only has an effect in Ultimate mode; it sets the low 6 bits of game_flags (those that match 0x3F). This
|
||||
// essentially separates status effects from the 6x0A command. It's not clear why Sega did this only in Ultimate mode.
|
||||
|
||||
struct G_SetEnemyLowGameFlagsUltimate_6x9C {
|
||||
G_EntityIDHeader header;
|
||||
// A virtual function is called on the enemy if low_game_flags is equal to any of 0x02, 0x04, 0x10, or 0x20.
|
||||
le_uint32_t low_game_flags = 0;
|
||||
// This field is expected to have one of these values (it should not actually be treated as a bit field):
|
||||
// 0x00000002 = add paralysis status
|
||||
// 0x00000004 = add shock status
|
||||
// 0x00000010 = add confuse status
|
||||
// 0x00000020 = add freeze status
|
||||
le_uint32_t status_effect_flags = 0;
|
||||
} __packed_ws__(G_SetEnemyLowGameFlagsUltimate_6x9C, 8);
|
||||
|
||||
// 6x9D: Set dead flag (Challenge mode; not valid on Episode 3)
|
||||
@@ -5409,18 +5459,12 @@ struct G_SetAnimationState_6xAE {
|
||||
} __packed_ws__(G_SetAnimationState_6xAE, 0x10);
|
||||
|
||||
// 6xAF: Turn lobby chair (not valid on pre-V3 or GC Trial Edition) (protected on V3/V4)
|
||||
|
||||
struct G_TurnLobbyChair_6xAF {
|
||||
G_ClientIDHeader header;
|
||||
le_uint32_t angle = 0; // In range [0x0000, 0xFFFF]
|
||||
} __packed_ws__(G_TurnLobbyChair_6xAF, 8);
|
||||
|
||||
// 6xB0: Move lobby chair (not valid on pre-V3 or GC Trial Edition) (protected on V3/V4)
|
||||
|
||||
struct G_MoveLobbyChair_6xB0 {
|
||||
struct G_TurnOrMoveLobbyChair_6xAF_6xB0 {
|
||||
G_ClientIDHeader header;
|
||||
le_uint32_t unknown_a1 = 0;
|
||||
} __packed_ws__(G_MoveLobbyChair_6xB0, 8);
|
||||
le_uint32_t angle = 0; // In range [0x0000, 0xFFFF]
|
||||
} __packed_ws__(G_TurnOrMoveLobbyChair_6xAF_6xB0, 8);
|
||||
|
||||
// 6xB1: Unknown (not valid on pre-V3 or GC Trial Edition)
|
||||
// This subcommand is completely ignored.
|
||||
@@ -5686,7 +5730,7 @@ struct G_BankAction_BB_6xBD {
|
||||
G_UnusedHeader header;
|
||||
le_uint32_t item_id = 0;
|
||||
le_uint32_t meseta_amount = 0;
|
||||
uint8_t action = 0; // 0 = deposit, 1 = take, 3 = done (close bank window)
|
||||
uint8_t action = 0; // 0 = deposit, 1 = take, 2 = unknown (compact contents?), 3 = commit (close bank window)
|
||||
uint8_t item_amount = 0;
|
||||
le_uint16_t item_index = 0; // 0xFFFF = meseta
|
||||
} __packed_ws__(G_BankAction_BB_6xBD, 0x10);
|
||||
|
||||
+6
-5
@@ -1372,10 +1372,10 @@ static const vector<DATEntityDefinition> dat_object_definitions({
|
||||
|
||||
// Item box. Params:
|
||||
// param1 = if positive, box is specialized to drop a specific item or type of item; if zero or negative, box
|
||||
// drops any common item or none at all (and param3-6 are all ignored)
|
||||
// param3 = if zero, then only data1[0-1] are used and the rest of the ItemData is cleared, then bonuses, grinds,
|
||||
// etc. are applied to the item; if nonzero, the item is not randomized at all and drops exactly as specified
|
||||
// in param4-6
|
||||
// drops any item (including box rares for the current floor), or nothing at all, and param3-6 are all ignored
|
||||
// param3 = if zero, then only data1[0-1] (the high 2 bytes of param4) are used and the rest of the ItemData is
|
||||
// cleared, then bonuses, grinds, etc. are applied to the item; if nonzero, the item is not randomized at all
|
||||
// and drops exactly as specified in param4-6
|
||||
// param4-6 = item definition (see below)
|
||||
// Not all fields in ItemData can be specified in the item definition here. The field order here does not match the
|
||||
// field order in ItemData! The item definition is encoded here as follows:
|
||||
@@ -2822,7 +2822,8 @@ static const vector<DATEntityDefinition> dat_enemy_definitions({
|
||||
// param2 = if less than 1, this is a Savage Wolf; otherwise it's a Barbarous Wolf
|
||||
{0x0043, F_V0_V4, 0x0000000000600006, "TObjEneBm5Wolf"},
|
||||
|
||||
// Booma, Gobooma, or Gigobooma. Params:
|
||||
// Booma, Gobooma, or Gigobooma. The activation radius is fixed and cannot be changed: 50 for Hunters, 100 for
|
||||
// Rangers and Forces; the deactivation radius is 100 for Hunters and 150 for Rangers and Forces. Params:
|
||||
// param1 = TODO (fraction of max HP; see TObjEnemyV8048ee80_v5A)
|
||||
// param2 = idle walk radius (when there's no target, it will walk around its spawn location within this radius;
|
||||
// if this is zero, it stands still instead)
|
||||
|
||||
@@ -64,6 +64,12 @@ struct PlayerVisualConfigT {
|
||||
// F = force, R = ranger, H = hunter
|
||||
// A = android, N = newman, M = human
|
||||
// f = female, m = male
|
||||
// Enemies also have a class_flags field, though it isn't part of PlayerVisualConfig. The bits for enemies are:
|
||||
// -------- -------- -------- ----DMAN
|
||||
// D = Dark attribute
|
||||
// M = Machine attribute
|
||||
// A = Altered Beast attribute
|
||||
// N = Native attribute
|
||||
/* 34 */ U32T<BE> class_flags = 0;
|
||||
/* 38 */ U16T<BE> costume = 0;
|
||||
/* 3A */ U16T<BE> skin = 0;
|
||||
|
||||
+106
-68
@@ -397,7 +397,7 @@ asio::awaitable<void> forward_subcommand_with_entity_id_transcode_t(shared_ptr<C
|
||||
}
|
||||
|
||||
template <typename HeaderT>
|
||||
asio::awaitable<void> forward_subcommand_with_entity_targets_transcode_and_track_hits_t(
|
||||
asio::awaitable<void> forward_subcommand_with_entity_targets_transcode_t(
|
||||
shared_ptr<Client> c, SubcommandMessage& msg) {
|
||||
// I'm lazy and this should never happen for item commands (since all players need to stay in sync)
|
||||
if (command_is_private(msg.command)) {
|
||||
@@ -430,16 +430,6 @@ asio::awaitable<void> forward_subcommand_with_entity_targets_transcode_and_track
|
||||
if ((res.entity_id >= 0x1000) && (res.entity_id < 0x4000)) {
|
||||
auto ene_st = l->map_state->enemy_state_for_index(c->version(), res.entity_id - 0x1000);
|
||||
res.ene_st = ene_st;
|
||||
|
||||
// Track hits for all resolved enemies
|
||||
c->log.info_f("Claiming last hit on E-{:03X}", ene_st->e_id);
|
||||
ene_st->set_last_hit_by_client_id(c->lobby_client_id);
|
||||
if (ene_st->alias_target_ene_st) {
|
||||
c->log.info_f("Claiming last hit on E-{:03X} (alias of E-{:03X})",
|
||||
ene_st->alias_target_ene_st->e_id, ene_st->e_id);
|
||||
ene_st->alias_target_ene_st->set_last_hit_by_client_id(c->lobby_client_id);
|
||||
}
|
||||
|
||||
} else if ((res.entity_id >= 0x4000) && (res.entity_id < 0xFFFF)) {
|
||||
res.obj_st = l->map_state->object_state_for_index(c->version(), res.entity_id - 0x4000);
|
||||
}
|
||||
@@ -803,8 +793,8 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
death_flags(cmd.death_flags),
|
||||
hold_state(cmd.hold_state),
|
||||
area(cmd.area),
|
||||
game_flags(cmd.game_flags),
|
||||
game_flags_is_v3(false),
|
||||
player_flags(cmd.player_flags),
|
||||
player_flags_is_v3(false),
|
||||
visual(cmd.visual),
|
||||
stats(cmd.stats),
|
||||
num_items(cmd.num_items),
|
||||
@@ -839,8 +829,8 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
death_flags(cmd.death_flags),
|
||||
hold_state(cmd.hold_state),
|
||||
area(cmd.area),
|
||||
game_flags(cmd.game_flags),
|
||||
game_flags_is_v3(false),
|
||||
player_flags(cmd.player_flags),
|
||||
player_flags_is_v3(false),
|
||||
visual(cmd.visual),
|
||||
stats(cmd.stats),
|
||||
num_items(cmd.num_items),
|
||||
@@ -872,7 +862,7 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
Version from_version,
|
||||
bool from_client_customization)
|
||||
: Parsed6x70Data(cmd.base, guild_card_number, from_version, from_client_customization) {
|
||||
this->game_flags_is_v3 = true;
|
||||
this->player_flags_is_v3 = true;
|
||||
this->stats = cmd.stats;
|
||||
this->num_items = cmd.num_items;
|
||||
this->items = cmd.items;
|
||||
@@ -888,7 +878,7 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
Version from_version,
|
||||
bool from_client_customization)
|
||||
: Parsed6x70Data(cmd.base, guild_card_number, from_version, from_client_customization) {
|
||||
this->game_flags_is_v3 = true;
|
||||
this->player_flags_is_v3 = true;
|
||||
this->stats = cmd.stats;
|
||||
this->num_items = cmd.num_items;
|
||||
this->items = cmd.items;
|
||||
@@ -904,7 +894,7 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
Version from_version,
|
||||
bool from_client_customization)
|
||||
: Parsed6x70Data(cmd.base, guild_card_number, from_version, from_client_customization) {
|
||||
this->game_flags_is_v3 = true;
|
||||
this->player_flags_is_v3 = true;
|
||||
this->stats = cmd.stats;
|
||||
this->num_items = cmd.num_items;
|
||||
this->items = cmd.items;
|
||||
@@ -924,7 +914,7 @@ G_SyncPlayerDispAndInventory_DCNTE_6x70 Parsed6x70Data::as_dc_nte(shared_ptr<Ser
|
||||
ret.death_flags = this->death_flags;
|
||||
ret.hold_state = this->hold_state;
|
||||
ret.area = this->area;
|
||||
ret.game_flags = this->get_game_flags(false);
|
||||
ret.player_flags = this->get_player_flags(false);
|
||||
ret.visual = this->visual;
|
||||
ret.stats = this->stats;
|
||||
ret.num_items = this->num_items;
|
||||
@@ -956,7 +946,7 @@ G_SyncPlayerDispAndInventory_DC112000_6x70 Parsed6x70Data::as_dc_112000(shared_p
|
||||
ret.death_flags = this->death_flags;
|
||||
ret.hold_state = this->hold_state;
|
||||
ret.area = this->area;
|
||||
ret.game_flags = this->get_game_flags(false);
|
||||
ret.player_flags = this->get_player_flags(false);
|
||||
ret.visual = this->visual;
|
||||
ret.stats = this->stats;
|
||||
ret.num_items = this->num_items;
|
||||
@@ -1112,7 +1102,7 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
temporary_status_effect(base.temporary_status_effect),
|
||||
attack_status_effect(base.attack_status_effect),
|
||||
defense_status_effect(base.defense_status_effect),
|
||||
unused_status_effect(base.unused_status_effect),
|
||||
unknown_a1_status_effect(base.unknown_a1_status_effect),
|
||||
language(static_cast<Language>(base.language32.load())),
|
||||
player_tag(base.player_tag),
|
||||
guild_card_number(guild_card_number), // Ignore the client's GC#
|
||||
@@ -1122,8 +1112,8 @@ Parsed6x70Data::Parsed6x70Data(
|
||||
death_flags(base.death_flags),
|
||||
hold_state(base.hold_state),
|
||||
area(base.area),
|
||||
game_flags(base.game_flags),
|
||||
game_flags_is_v3(!is_v1_or_v2(from_version)),
|
||||
player_flags(base.player_flags),
|
||||
player_flags_is_v3(!is_v1_or_v2(from_version)),
|
||||
technique_levels_v1(base.technique_levels_v1),
|
||||
visual(base.visual) {}
|
||||
|
||||
@@ -1136,7 +1126,7 @@ G_6x70_Base_V1 Parsed6x70Data::base_v1(bool is_v3) const {
|
||||
ret.temporary_status_effect = this->temporary_status_effect;
|
||||
ret.attack_status_effect = this->attack_status_effect;
|
||||
ret.defense_status_effect = this->defense_status_effect;
|
||||
ret.unused_status_effect = this->unused_status_effect;
|
||||
ret.unknown_a1_status_effect = this->unknown_a1_status_effect;
|
||||
ret.language32 = static_cast<size_t>(this->language);
|
||||
ret.player_tag = this->player_tag;
|
||||
ret.guild_card_number = this->guild_card_number;
|
||||
@@ -1146,46 +1136,69 @@ G_6x70_Base_V1 Parsed6x70Data::base_v1(bool is_v3) const {
|
||||
ret.death_flags = this->death_flags;
|
||||
ret.hold_state = this->hold_state;
|
||||
ret.area = this->area;
|
||||
ret.game_flags = this->get_game_flags(is_v3);
|
||||
ret.player_flags = this->get_player_flags(is_v3);
|
||||
ret.technique_levels_v1 = this->technique_levels_v1;
|
||||
ret.visual = this->visual;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t Parsed6x70Data::convert_game_flags(uint32_t game_flags, bool to_v3) {
|
||||
// The format of game_flags for players was changed significantly between v2 and v3, and not accounting for this
|
||||
// results in odd effects like other characters not appearing when joining a game. Unfortunately, some bits were
|
||||
// deleted on v3 and other bits were added, so it doesn't suffice to simply store the most complete format of this
|
||||
// field - we have to be able to convert between the two.
|
||||
|
||||
// Bits on v2: JIHCBAzy xwvutsrq ponmlkji hgfedcba
|
||||
// Bits on v3: JIHGFEDC BAzyxwvu srqponkj hgfedcba
|
||||
// The bits ilmt were removed in v3 and the bits to their left were shifted right. The bits DEFG were added in v3 and
|
||||
// do not exist on v2. Known meanings for these bits so far:
|
||||
// o = is dead
|
||||
// n = should play hit animation
|
||||
// y = is near enemy
|
||||
// H = is enemy?
|
||||
// I = is object? (some entities have both H and I set though)
|
||||
// J = is item
|
||||
uint32_t Parsed6x70Data::convert_player_flags(uint32_t player_flags, bool to_v3) {
|
||||
// The format of player_flags was changed significantly between v2 and v3, and not accounting for this results in odd
|
||||
// effects like other characters not appearing when joining a game. Unfortunately, some bits were deleted on v3 and
|
||||
// other bits were added, so it doesn't suffice to simply store the most complete format of this field - we have to
|
||||
// be able to convert between the two. What's known about these bits (? indicates meaning/behavior is unverified):
|
||||
// * V1/V2 * V3/V4
|
||||
// ? 00000001 00000001 = player hold is set (see notes on 6x2C and 6x2D in CommandFormats.hh)
|
||||
// ? 00000002 ? 00000002 = 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)
|
||||
|
||||
if (to_v3) {
|
||||
return (game_flags & 0xE00000FF) |
|
||||
((game_flags & 0x00000600) >> 1) |
|
||||
((game_flags & 0x0007E000) >> 3) |
|
||||
((game_flags & 0x1FF00000) >> 4);
|
||||
return (player_flags & 0x000000FF) |
|
||||
((player_flags & 0x00000600) >> 1) |
|
||||
((player_flags & 0x0007E000) >> 3) |
|
||||
((player_flags & 0x1FF00000) >> 4);
|
||||
} else {
|
||||
return (game_flags & 0xE00000FF) |
|
||||
((game_flags << 1) & 0x00000600) |
|
||||
((game_flags << 3) & 0x0007E000) |
|
||||
((game_flags << 4) & 0x1FF00000);
|
||||
return (player_flags & 0x000000FF) |
|
||||
((player_flags << 1) & 0x00000600) |
|
||||
((player_flags << 3) & 0x0007E000) |
|
||||
((player_flags << 4) & 0x1FF00000);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Parsed6x70Data::get_game_flags(bool is_v3) const {
|
||||
return (this->game_flags_is_v3 == is_v3)
|
||||
? this->game_flags
|
||||
: Parsed6x70Data::convert_game_flags(this->game_flags, is_v3);
|
||||
uint32_t Parsed6x70Data::get_player_flags(bool is_v3) const {
|
||||
return (this->player_flags_is_v3 == is_v3)
|
||||
? this->player_flags
|
||||
: Parsed6x70Data::convert_player_flags(this->player_flags, is_v3);
|
||||
}
|
||||
|
||||
static asio::awaitable<void> on_sync_joining_player_disp_and_inventory(
|
||||
@@ -3435,17 +3448,29 @@ static asio::awaitable<void> on_update_enemy_state(shared_ptr<Client> c, Subcomm
|
||||
}
|
||||
auto ene_st = l->map_state->enemy_state_for_index(c->version(), cmd.enemy_index);
|
||||
uint32_t src_flags = is_big_endian(c->version()) ? bswap32(cmd.game_flags) : cmd.game_flags.load();
|
||||
if (src_flags & 0xD0000000) { // Don't allow player, object, or item flags to be set
|
||||
throw std::runtime_error("incorrect entity type flags in 6x0A command");
|
||||
}
|
||||
if (l->difficulty == Difficulty::ULTIMATE) {
|
||||
src_flags = (src_flags & 0xFFFFFFC0) | (ene_st->game_flags & 0x0000003F);
|
||||
}
|
||||
|
||||
bool should_track_hit = (src_flags & 0x00000200); // "Enemy was hit" flag (see 6x0A comments in CommandFormats.hh)
|
||||
if (should_track_hit) {
|
||||
ene_st->set_last_hit_by_client_id(c->lobby_client_id);
|
||||
}
|
||||
ene_st->game_flags = src_flags;
|
||||
ene_st->total_damage = cmd.total_damage;
|
||||
l->log.info_f("E-{:03X} updated to damage={} game_flags={:08X}", ene_st->e_id, ene_st->total_damage, ene_st->game_flags);
|
||||
l->log.info_f("E-{:03X} updated to damage={} game_flags={:08X}; last hit {}",
|
||||
ene_st->e_id, ene_st->total_damage, ene_st->game_flags, should_track_hit ? "claimed" : "not claimed");
|
||||
if (ene_st->alias_target_ene_st) {
|
||||
if (should_track_hit) {
|
||||
ene_st->alias_target_ene_st->set_last_hit_by_client_id(c->lobby_client_id);
|
||||
}
|
||||
ene_st->alias_target_ene_st->game_flags = src_flags;
|
||||
ene_st->alias_target_ene_st->total_damage = cmd.total_damage;
|
||||
l->log.info_f("Alias target E-{:03X} updated to damage={} game_flags={:08X}",
|
||||
ene_st->alias_target_ene_st->e_id, ene_st->alias_target_ene_st->total_damage, ene_st->alias_target_ene_st->game_flags);
|
||||
l->log.info_f("Alias target E-{:03X} updated to damage={} game_flags={:08X}; last hit {}",
|
||||
ene_st->alias_target_ene_st->e_id, ene_st->alias_target_ene_st->total_damage, ene_st->alias_target_ene_st->game_flags, should_track_hit ? "claimed" : "not claimed");
|
||||
}
|
||||
|
||||
// TODO: It'd be nice if this worked on bosses too, but it seems we have to use each boss' specific state-syncing
|
||||
@@ -3493,8 +3518,18 @@ static asio::awaitable<void> on_incr_enemy_damage(shared_ptr<Client> c, Subcomma
|
||||
cmd.total_damage_before_hit.load(),
|
||||
cmd.current_hp_before_hit.load(),
|
||||
cmd.max_hp.load());
|
||||
|
||||
if (cmd.hit_amount > 0) {
|
||||
c->log.info_f("Claiming last hit on E-{:03X}", ene_st->e_id);
|
||||
ene_st->set_last_hit_by_client_id(c->lobby_client_id);
|
||||
}
|
||||
ene_st->total_damage = std::min<uint32_t>(ene_st->total_damage + cmd.hit_amount, cmd.max_hp);
|
||||
if (ene_st->alias_target_ene_st) {
|
||||
if (cmd.hit_amount > 0) {
|
||||
c->log.info_f("Claiming last hit on E-{:03X} (alias of E-{:03X})",
|
||||
ene_st->alias_target_ene_st->e_id, ene_st->e_id);
|
||||
ene_st->alias_target_ene_st->set_last_hit_by_client_id(c->lobby_client_id);
|
||||
}
|
||||
ene_st->alias_target_ene_st->total_damage = std::min<uint32_t>(
|
||||
ene_st->alias_target_ene_st->total_damage + cmd.hit_amount, cmd.max_hp);
|
||||
}
|
||||
@@ -3502,13 +3537,13 @@ static asio::awaitable<void> on_incr_enemy_damage(shared_ptr<Client> c, Subcomma
|
||||
co_await forward_subcommand_with_entity_id_transcode_t<G_IncrementEnemyDamage_Extension_6xE4>(c, msg);
|
||||
}
|
||||
|
||||
static asio::awaitable<void> on_set_enemy_low_game_flags_ultimate(shared_ptr<Client> c, SubcommandMessage& msg) {
|
||||
static asio::awaitable<void> on_set_enemy_status_effect_flags_ultimate(shared_ptr<Client> c, SubcommandMessage& msg) {
|
||||
auto& cmd = msg.check_size_t<G_SetEnemyLowGameFlagsUltimate_6x9C>();
|
||||
|
||||
if (command_is_private(msg.command) ||
|
||||
(cmd.header.entity_id < 0x1000) ||
|
||||
(cmd.header.entity_id >= 0x4000) ||
|
||||
(cmd.low_game_flags & 0xFFFFFFC0) ||
|
||||
(cmd.status_effect_flags & 0xFFFFFFC0) ||
|
||||
(c->lobby_client_id > 3)) {
|
||||
co_return;
|
||||
}
|
||||
@@ -3518,12 +3553,12 @@ static asio::awaitable<void> on_set_enemy_low_game_flags_ultimate(shared_ptr<Cli
|
||||
}
|
||||
|
||||
auto ene_st = l->map_state->enemy_state_for_index(c->version(), cmd.header.entity_id - 0x1000);
|
||||
if (!(ene_st->game_flags & cmd.low_game_flags)) {
|
||||
ene_st->game_flags |= cmd.low_game_flags;
|
||||
if (!(ene_st->game_flags & cmd.status_effect_flags)) {
|
||||
ene_st->game_flags |= cmd.status_effect_flags;
|
||||
l->log.info_f("E-{:03X} updated to game_flags={:08X}", ene_st->e_id, ene_st->game_flags);
|
||||
}
|
||||
if (ene_st->alias_target_ene_st && !(ene_st->alias_target_ene_st->game_flags & cmd.low_game_flags)) {
|
||||
ene_st->alias_target_ene_st->game_flags |= cmd.low_game_flags;
|
||||
if (ene_st->alias_target_ene_st && !(ene_st->alias_target_ene_st->game_flags & cmd.status_effect_flags)) {
|
||||
ene_st->alias_target_ene_st->game_flags |= cmd.status_effect_flags;
|
||||
l->log.info_f("Alias E-{:03X} updated to game_flags={:08X}",
|
||||
ene_st->alias_target_ene_st->e_id, ene_st->alias_target_ene_st->game_flags);
|
||||
}
|
||||
@@ -4062,8 +4097,11 @@ static asio::awaitable<void> on_enemy_exp_request_bb(shared_ptr<Client> c, Subco
|
||||
// If the requesting player never hit this enemy, they are probably cheating; ignore the command. Also, each player
|
||||
// sends a 6xC8 if they ever hit the enemy; we only react to the first 6xC8 for each enemy (and give all relevant
|
||||
// players EXP then, if they deserve it).
|
||||
if (!ene_st->ever_hit_by_client_id(c->lobby_client_id) ||
|
||||
(ene_st->server_flags & MapState::EnemyState::Flag::EXP_GIVEN)) {
|
||||
if (!ene_st->ever_hit_by_client_id(c->lobby_client_id)) {
|
||||
l->log.warning_f("The requesting player did not hit this enemy; ignoring request");
|
||||
co_return;
|
||||
}
|
||||
if (ene_st->server_flags & MapState::EnemyState::Flag::EXP_GIVEN) {
|
||||
l->log.info_f("EXP already given for this enemy; ignoring request");
|
||||
co_return;
|
||||
}
|
||||
@@ -5506,10 +5544,10 @@ const vector<SubcommandDefinition> subcommand_definitions{
|
||||
/* 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},
|
||||
/* 6x46 */ {NONE, 0x42, 0x46, forward_subcommand_with_entity_targets_transcode_and_track_hits_t<G_AttackFinished_Header_6x46>},
|
||||
/* 6x47 */ {0x3D, 0x43, 0x47, forward_subcommand_with_entity_targets_transcode_and_track_hits_t<G_CastTechnique_Header_6x47>},
|
||||
/* 6x46 */ {NONE, 0x42, 0x46, forward_subcommand_with_entity_targets_transcode_t<G_AttackFinished_Header_6x46>},
|
||||
/* 6x47 */ {0x3D, 0x43, 0x47, forward_subcommand_with_entity_targets_transcode_t<G_CastTechnique_Header_6x47>},
|
||||
/* 6x48 */ {NONE, NONE, 0x48, on_cast_technique_finished},
|
||||
/* 6x49 */ {0x3E, 0x44, 0x49, forward_subcommand_with_entity_targets_transcode_and_track_hits_t<G_ExecutePhotonBlast_Header_6x49>},
|
||||
/* 6x49 */ {0x3E, 0x44, 0x49, forward_subcommand_with_entity_targets_transcode_t<G_ExecutePhotonBlast_Header_6x49>},
|
||||
/* 6x4A */ {0x3F, 0x45, 0x4A, on_change_hp<G_ClientIDHeader>},
|
||||
/* 6x4B */ {0x40, 0x46, 0x4B, on_change_hp<G_ClientIDHeader>},
|
||||
/* 6x4C */ {0x41, 0x47, 0x4C, on_change_hp<G_ClientIDHeader>},
|
||||
@@ -5592,7 +5630,7 @@ const vector<SubcommandDefinition> subcommand_definitions{
|
||||
/* 6x99 */ {NONE, NONE, 0x99, on_forward_check_game},
|
||||
/* 6x9A */ {NONE, NONE, 0x9A, on_forward_check_game_client},
|
||||
/* 6x9B */ {NONE, NONE, 0x9B, on_forward_check_game},
|
||||
/* 6x9C */ {NONE, NONE, 0x9C, on_set_enemy_low_game_flags_ultimate},
|
||||
/* 6x9C */ {NONE, NONE, 0x9C, on_set_enemy_status_effect_flags_ultimate},
|
||||
/* 6x9D */ {NONE, NONE, 0x9D, on_forward_check_game},
|
||||
/* 6x9E */ {NONE, NONE, 0x9E, forward_subcommand_m},
|
||||
/* 6x9F */ {NONE, NONE, 0x9F, forward_subcommand_with_entity_id_transcode_t<G_GalGryphonBossActions_6x9F>},
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
StatusEffectState temporary_status_effect;
|
||||
StatusEffectState attack_status_effect;
|
||||
StatusEffectState defense_status_effect;
|
||||
StatusEffectState unused_status_effect;
|
||||
StatusEffectState unknown_a1_status_effect; // De Rol Le uses this?
|
||||
Language language = Language::JAPANESE;
|
||||
uint32_t player_tag = 0;
|
||||
uint32_t guild_card_number = 0;
|
||||
@@ -62,8 +62,8 @@ public:
|
||||
uint32_t death_flags = 0;
|
||||
PlayerHoldState hold_state;
|
||||
uint32_t area = 0;
|
||||
uint32_t game_flags = 0;
|
||||
bool game_flags_is_v3 = false;
|
||||
uint32_t player_flags = 0;
|
||||
bool player_flags_is_v3 = false;
|
||||
parray<uint8_t, 0x14> technique_levels_v1 = 0xFF;
|
||||
PlayerVisualConfig visual;
|
||||
std::string name;
|
||||
@@ -121,8 +121,8 @@ protected:
|
||||
Parsed6x70Data(
|
||||
const G_6x70_Base_V1& base, uint32_t guild_card_number, Version from_version, bool from_client_customization);
|
||||
G_6x70_Base_V1 base_v1(bool is_v3) const;
|
||||
static uint32_t convert_game_flags(uint32_t game_flags, bool to_v3);
|
||||
uint32_t get_game_flags(bool is_v3) const;
|
||||
static uint32_t convert_player_flags(uint32_t player_flags, bool to_v3);
|
||||
uint32_t get_player_flags(bool is_v3) const;
|
||||
};
|
||||
|
||||
bool validate_6xBB(G_SyncCardTradeServerState_Ep3_6xBB& cmd);
|
||||
|
||||
+12
-2
@@ -293,8 +293,18 @@ uint8_t npc_for_name(const string& name, Version version) {
|
||||
|
||||
const char* name_for_char_class(uint8_t cls) {
|
||||
static const array<const char*, 12> names = {
|
||||
"HUmar", "HUnewearl", "HUcast", "RAmar", "RAcast", "RAcaseal", "FOmarl", "FOnewm", "FOnewearl", "HUcaseal",
|
||||
"FOmar", "RAmarl"};
|
||||
/* 00 */ "HUmar", // 0
|
||||
/* 01 */ "HUnewearl", // 0
|
||||
/* 02 */ "HUcast", // 1
|
||||
/* 03 */ "RAmar", // 0
|
||||
/* 04 */ "RAcast", // 2
|
||||
/* 05 */ "RAcaseal", // 1
|
||||
/* 06 */ "FOmarl", // 0
|
||||
/* 07 */ "FOnewm", // 0
|
||||
/* 08 */ "FOnewearl", // 0
|
||||
/* 09 */ "HUcaseal", // 1
|
||||
/* 0A */ "FOmar", // 0
|
||||
/* 0B */ "RAmarl"}; // 0
|
||||
try {
|
||||
return names.at(cls);
|
||||
} catch (const out_of_range&) {
|
||||
|
||||
Reference in New Issue
Block a user