fix(combat): escape occupied login positions
Port retail's radius-aware placement ring so a relogging player is seated beside creatures occupying the saved location, and register the local body in the shared resolved-shadow pipeline. Route new forward movement and jump through AbortAutomaticAttack so repeat combat cancels immediately on movement. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
e671b8a5c4
commit
00fe993f6f
11 changed files with 643 additions and 16 deletions
|
|
@ -15,7 +15,8 @@ namespace AcDream.App.Combat;
|
|||
/// (0x0056ADE0), <c>StartAttackRequest</c> (0x0056C040),
|
||||
/// <c>EndAttackRequest</c> (0x0056C0E0), <c>UseTime</c> (0x0056C1F0),
|
||||
/// <c>HandleAttackDoneEvent</c> (0x0056C500), and
|
||||
/// <c>SetRequestedAttackHeight</c> (0x0056C8F0).
|
||||
/// <c>SetRequestedAttackHeight</c> (0x0056C8F0), and
|
||||
/// <c>AbortAutomaticAttack</c> (0x0056AE90).
|
||||
/// </remarks>
|
||||
public sealed class CombatAttackController : IDisposable
|
||||
{
|
||||
|
|
@ -27,6 +28,7 @@ public sealed class CombatAttackController : IDisposable
|
|||
private readonly CombatState _combat;
|
||||
private readonly Func<bool> _canStartAttack;
|
||||
private readonly Func<AttackHeight, float, bool> _sendAttack;
|
||||
private readonly Action _sendCancelAttack;
|
||||
private readonly Func<bool> _isDualWield;
|
||||
private readonly Func<bool> _playerReadyForAttack;
|
||||
private readonly Func<bool> _autoRepeatAttack;
|
||||
|
|
@ -48,6 +50,7 @@ public sealed class CombatAttackController : IDisposable
|
|||
CombatState combat,
|
||||
Func<bool> canStartAttack,
|
||||
Func<AttackHeight, float, bool> sendAttack,
|
||||
Action? sendCancelAttack = null,
|
||||
Func<bool>? isDualWield = null,
|
||||
Func<bool>? playerReadyForAttack = null,
|
||||
Func<bool>? autoRepeatAttack = null,
|
||||
|
|
@ -56,6 +59,7 @@ public sealed class CombatAttackController : IDisposable
|
|||
_combat = combat ?? throw new ArgumentNullException(nameof(combat));
|
||||
_canStartAttack = canStartAttack ?? throw new ArgumentNullException(nameof(canStartAttack));
|
||||
_sendAttack = sendAttack ?? throw new ArgumentNullException(nameof(sendAttack));
|
||||
_sendCancelAttack = sendCancelAttack ?? (() => { });
|
||||
_isDualWield = isDualWield ?? (() => false);
|
||||
_playerReadyForAttack = playerReadyForAttack ?? (() => true);
|
||||
_autoRepeatAttack = autoRepeatAttack ?? (() => false);
|
||||
|
|
@ -113,6 +117,26 @@ public sealed class CombatAttackController : IDisposable
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Routes the semantic inputs that retail turns into a new forward-motion
|
||||
/// command. Turns and sidesteps live in separate command lists and do not
|
||||
/// call <c>HandleNewForwardMovement</c>. Jump has its own equivalent cancel
|
||||
/// in <c>ClientCombatSystem::CommenceJump</c> (0x0056AF90).
|
||||
/// </summary>
|
||||
public void HandleMovementInput(InputAction action, ActivationType activation)
|
||||
{
|
||||
if (activation != ActivationType.Press)
|
||||
return;
|
||||
|
||||
if (action is InputAction.MovementForward
|
||||
or InputAction.MovementBackup
|
||||
or InputAction.MovementRunLock
|
||||
or InputAction.MovementJump)
|
||||
{
|
||||
AbortAutomaticAttack();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDesiredPower(float power)
|
||||
{
|
||||
float value = Math.Clamp(power, 0f, 1f);
|
||||
|
|
@ -153,6 +177,30 @@ public sealed class CombatAttackController : IDisposable
|
|||
StateChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>ClientCombatSystem::AbortAutomaticAttack</c> (0x0056AE90).
|
||||
/// A new forward-movement command enters here through
|
||||
/// <c>ACCmdInterp::HandleNewForwardMovement</c> (0x0058B1F0): notify the
|
||||
/// server, end repeat mode, and hide an in-progress combat power build.
|
||||
/// Request/pending flags are deliberately left for the normal server
|
||||
/// response, matching retail.
|
||||
/// </summary>
|
||||
public void AbortAutomaticAttack()
|
||||
{
|
||||
if (!_attackServerResponsePending
|
||||
&& !_attackRequestInProgress
|
||||
&& !_repeatAttacking)
|
||||
return;
|
||||
|
||||
_sendCancelAttack();
|
||||
_repeatAttacking = false;
|
||||
|
||||
if (_buildInProgress)
|
||||
ResetPowerBar();
|
||||
|
||||
StateChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retail <c>UseTime</c>: publishes the live build and commits a released
|
||||
/// request once the ticking level reaches the power captured on release.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue