feat(combat): Phase L.1c add outbound combat actions

This commit is contained in:
Erik 2026-04-28 10:57:12 +02:00
parent 29afc94b94
commit 25b9616703
5 changed files with 176 additions and 5 deletions

View file

@ -1,6 +1,7 @@
using System.Buffers.Binary;
using System.Net;
using System.Threading.Channels;
using AcDream.Core.Combat;
using AcDream.Core.Net.Cryptography;
using AcDream.Core.Net.Messages;
using AcDream.Core.Net.Packets;
@ -909,6 +910,48 @@ public sealed class WorldSession : IDisposable
SendGameAction(body);
}
/// <summary>Send retail ChangeCombatMode (0x0053).</summary>
public void SendChangeCombatMode(CombatMode mode)
{
uint seq = NextGameActionSequence();
byte[] body = CharacterActions.BuildChangeCombatMode(
seq,
(CharacterActions.CombatMode)(uint)mode);
SendGameAction(body);
}
/// <summary>Send retail TargetedMeleeAttack (0x0008).</summary>
public void SendMeleeAttack(uint targetGuid, AttackHeight attackHeight, float powerLevel)
{
uint seq = NextGameActionSequence();
byte[] body = AttackTargetRequest.BuildMelee(
seq,
targetGuid,
(uint)attackHeight,
powerLevel);
SendGameAction(body);
}
/// <summary>Send retail TargetedMissileAttack (0x000A).</summary>
public void SendMissileAttack(uint targetGuid, AttackHeight attackHeight, float accuracyLevel)
{
uint seq = NextGameActionSequence();
byte[] body = AttackTargetRequest.BuildMissile(
seq,
targetGuid,
(uint)attackHeight,
accuracyLevel);
SendGameAction(body);
}
/// <summary>Send retail CancelAttack (0x01B7).</summary>
public void SendCancelAttack()
{
uint seq = NextGameActionSequence();
byte[] body = AttackTargetRequest.BuildCancel(seq);
SendGameAction(body);
}
/// <summary>
/// Phase I.6: send a TurbineChat <c>RequestSendToRoomById</c> to a
/// global community room (General / Trade / LFG / Roleplay /

View file

@ -7,14 +7,17 @@ namespace AcDream.Core.Combat;
// Full research: docs/research/deepdives/r02-combat-system.md
// ─────────────────────────────────────────────────────────────────────
[Flags]
public enum CombatMode
{
Undef = 0,
NonCombat = 1,
Melee = 2,
Missile = 3,
Magic = 4,
Peaceful = 5,
NonCombat = 0x01,
Melee = 0x02,
Missile = 0x04,
Magic = 0x08,
ValidCombat = NonCombat | Melee | Missile | Magic,
CombatCombat = Melee | Missile | Magic,
}
public enum AttackHeight

View file

@ -39,6 +39,8 @@ public sealed class CombatState
{
private readonly ConcurrentDictionary<uint, float> _healthByGuid = new();
public CombatMode CurrentMode { get; private set; } = CombatMode.NonCombat;
/// <summary>Fires when a target's health percent changes (from UpdateHealth).</summary>
public event Action<uint /*guid*/, float /*percent*/>? HealthChanged;
@ -60,6 +62,9 @@ public sealed class CombatState
/// <summary>The server accepted the attack and the power bar/animation can begin.</summary>
public event Action? AttackCommenced;
/// <summary>The locally requested or server-confirmed combat mode changed.</summary>
public event Action<CombatMode>? CombatModeChanged;
/// <summary>
/// Fires when the server confirms the player landed a killing blow
/// (GameEvent <c>KillerNotification (0x01AD)</c>). Event payload is
@ -97,6 +102,15 @@ public sealed class CombatState
HealthChanged?.Invoke(targetGuid, healthPercent);
}
public void SetCombatMode(CombatMode mode)
{
if (CurrentMode == mode)
return;
CurrentMode = mode;
CombatModeChanged?.Invoke(mode);
}
public void OnVictimNotification(
string attackerName, uint attackerGuid, uint damageType, uint damage,
uint hitQuadrant, uint critical, uint attackType)