MovementParameters gains the verbatim selection family: GetCommand 0x0052aa00 (the walk-vs-run cascade INCLUDING the CanCharge 0x10 fast-path ACE dropped - retail's default can_charge=false + the fast-path present, the A13+A15 canceling-pair trap avoided; inclusive threshold edge per the raw), TowardsAndAway, GetDesiredHeading per the live Ghidra decompile (fwd-towards 0 / fwd-away 180 / back-towards 180 / back-away 0), FromWire/FromWireTurnTo (UnPackNet semantics, all 18 A4 masks round-tripped). New MoveToMath: HeadingDiff per the live Ghidra decompile of 0x00528fb0 (the 360-diff NOT-TurnRight mirror + F_EPSILON 0.000199999995f - the BN "arg unused" artifact corrected), HeadingGreater (the visible TurnRight idiom), PositionHeading/Get/SetHeading reusing the codebase's single yaw-heading convention (P5), CylinderDistance (PDB arg order; planar-minus-radii shape documented as the interpretation - the raw's x87 body is garbled; seam noted). MovementType gains Invalid + retail 6/7/8/9; MovementStruct widened (ObjectId/TopLevelId/Pos/Radius/Height/Params, additive); WeenieError += 0x0B/0x36/0x37/0x38/0x3D with retail-meaning doc comments. 148 new conformance tests. Full suite: 3,860 passed. Implemented by a dedicated agent against the V0-pinned spec; scope + suite independently verified. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
472 lines
20 KiB
C#
472 lines
20 KiB
C#
using AcDream.Core.Physics;
|
|
|
|
namespace AcDream.Core.Physics.Motion;
|
|
|
|
/// <summary>
|
|
/// R3-W1 — verbatim port of retail's <c>MovementParameters</c>
|
|
/// (<c>acclient.h:31453</c>, struct #3460; bitfield struct
|
|
/// <c>acclient.h:31423-31443</c>):
|
|
/// <code>
|
|
/// struct __cppobj MovementParameters : PackObj
|
|
/// {
|
|
/// union { unsigned int bitfield; ... } ___u1;
|
|
/// float distance_to_object;
|
|
/// float min_distance;
|
|
/// float desired_heading;
|
|
/// float speed;
|
|
/// float fail_distance;
|
|
/// float walk_run_threshhold;
|
|
/// unsigned int context_id;
|
|
/// HoldKey hold_key_to_apply;
|
|
/// unsigned int action_stamp;
|
|
/// };
|
|
/// </code>
|
|
///
|
|
/// <para>
|
|
/// The bitfield's absolute mask table is pinned in
|
|
/// <c>docs/research/2026-07-02-r3-motioninterp/W0-pins.md</c> §A4 (bit-for-bit
|
|
/// identical to ACE's <c>MovementParamFlags</c>):
|
|
/// </para>
|
|
/// <list type="table">
|
|
/// <item><term>0x1</term><description>CanWalk</description></item>
|
|
/// <item><term>0x2</term><description>CanRun</description></item>
|
|
/// <item><term>0x4</term><description>CanSidestep</description></item>
|
|
/// <item><term>0x8</term><description>CanWalkBackwards</description></item>
|
|
/// <item><term>0x10</term><description>CanCharge</description></item>
|
|
/// <item><term>0x20</term><description>FailWalk</description></item>
|
|
/// <item><term>0x40</term><description>UseFinalHeading</description></item>
|
|
/// <item><term>0x80</term><description>Sticky</description></item>
|
|
/// <item><term>0x100</term><description>MoveAway</description></item>
|
|
/// <item><term>0x200</term><description>MoveTowards</description></item>
|
|
/// <item><term>0x400</term><description>UseSpheres</description></item>
|
|
/// <item><term>0x800</term><description>SetHoldKey</description></item>
|
|
/// <item><term>0x1000</term><description>Autonomous</description></item>
|
|
/// <item><term>0x2000</term><description>ModifyRawState</description></item>
|
|
/// <item><term>0x4000</term><description>ModifyInterpretedState</description></item>
|
|
/// <item><term>0x8000</term><description>CancelMoveTo</description></item>
|
|
/// <item><term>0x10000</term><description>StopCompletely</description></item>
|
|
/// <item><term>0x20000</term><description>DisableJumpDuringLink</description></item>
|
|
/// </list>
|
|
///
|
|
/// <para>
|
|
/// Ctor default (raw 300510-300534, <c>0x00524380</c>):
|
|
/// <c>(bitfield & 0xfffdee0f) | 0x1ee0f</c> → <c>0x1EE0F</c> sets
|
|
/// {CanWalk, CanRun, CanSidestep, CanWalkBackwards, MoveTowards, UseSpheres,
|
|
/// SetHoldKey, ModifyRawState, ModifyInterpretedState, CancelMoveTo,
|
|
/// StopCompletely}; clears {CanCharge, FailWalk, UseFinalHeading, Sticky,
|
|
/// MoveAway, Autonomous, DisableJumpDuringLink}.
|
|
/// Scalars: <c>min_distance=0</c>, <c>distance_to_object=0.6</c>,
|
|
/// <c>fail_distance=FLT_MAX</c>, <c>desired_heading=0</c>, <c>speed=1</c>,
|
|
/// <c>walk_run_threshhold=15</c> (NOT ACE's 1.0 — W0-pins A4 divergence trap),
|
|
/// <c>context_id=0</c>, <c>hold_key_to_apply=HoldKey.Invalid</c>,
|
|
/// <c>action_stamp=0</c>.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// <b>ACE-divergence traps (W0-pins A4, do not copy):</b> ACE's
|
|
/// <c>MovementParameters</c> ctor sets <c>CanCharge = true</c>
|
|
/// (MovementParameters.cs:58) — retail's default has bit <c>0x10</c> CLEAR;
|
|
/// this port defaults <c>CanCharge = false</c>. ACE also changed
|
|
/// <c>Default_WalkRunThreshold</c> to 1.0 (L50) vs retail's literal 15.0
|
|
/// (@300519) — this port defaults <c>WalkRunThreshhold = 15f</c>.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// Named bool properties per plan (no <c>ToBitfield()</c>/<c>FromBitfield()</c>
|
|
/// pair — the wire never carries this struct raw; <c>RawMotionState::Pack</c>
|
|
/// serializes the STATE, not this transient parameter block. If a future
|
|
/// slice needs the packed form, add the pair then with a cited call site).
|
|
/// </para>
|
|
/// </summary>
|
|
public sealed class MovementParameters
|
|
{
|
|
// ── bitfield flags (retail 0x1EE0F default; W0-pins A4) ───────────────
|
|
|
|
/// <summary>Mask 0x1 — default true.</summary>
|
|
public bool CanWalk { get; set; } = true;
|
|
|
|
/// <summary>Mask 0x2 — default true.</summary>
|
|
public bool CanRun { get; set; } = true;
|
|
|
|
/// <summary>Mask 0x4 — default true.</summary>
|
|
public bool CanSidestep { get; set; } = true;
|
|
|
|
/// <summary>Mask 0x8 — default true.</summary>
|
|
public bool CanWalkBackwards { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Mask 0x10 — default FALSE. ACE-divergence trap (W0-pins A4): ACE's
|
|
/// ctor sets this true (MovementParameters.cs:58); retail's 0x1EE0F
|
|
/// default has bit 0x10 CLEAR. Do not "fix" this to true.
|
|
/// </summary>
|
|
public bool CanCharge { get; set; }
|
|
|
|
/// <summary>Mask 0x20 — default false.</summary>
|
|
public bool FailWalk { get; set; }
|
|
|
|
/// <summary>Mask 0x40 — default false.</summary>
|
|
public bool UseFinalHeading { get; set; }
|
|
|
|
/// <summary>Mask 0x80 — default false.</summary>
|
|
public bool Sticky { get; set; }
|
|
|
|
/// <summary>Mask 0x100 — default false.</summary>
|
|
public bool MoveAway { get; set; }
|
|
|
|
/// <summary>Mask 0x200 — default true.</summary>
|
|
public bool MoveTowards { get; set; } = true;
|
|
|
|
/// <summary>Mask 0x400 — default true.</summary>
|
|
public bool UseSpheres { get; set; } = true;
|
|
|
|
/// <summary>Mask 0x800 — default true. DoMotion @306188: byte1&8
|
|
/// requests a <c>SetHoldKey</c> call before <c>adjust_motion</c>.</summary>
|
|
public bool SetHoldKey { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Mask 0x1000 — default FALSE. Not the same virtual as
|
|
/// <c>IWeenieObject.IsThePlayer</c> (W0-pins A3) — this is the
|
|
/// per-call "was this an autonomous (locally-predicted) action?" flag.
|
|
/// </summary>
|
|
public bool Autonomous { get; set; }
|
|
|
|
/// <summary>Mask 0x2000 — default true. DoMotion @306213: byte1&0x20
|
|
/// mirrors the applied motion into <c>RawMotionState</c> via
|
|
/// <c>ApplyMotion</c>/<c>RemoveMotion</c>.</summary>
|
|
public bool ModifyRawState { get; set; } = true;
|
|
|
|
/// <summary>Mask 0x4000 — default true. Mirrors into
|
|
/// <c>InterpretedMotionState</c>.</summary>
|
|
public bool ModifyInterpretedState { get; set; } = true;
|
|
|
|
/// <summary>Mask 0x8000 — default true. Bitfield high-byte sign bit;
|
|
/// DoMotion/StopMotion @306183/@305684: triggers
|
|
/// <c>interrupt_current_movement</c> before the rest of the call.</summary>
|
|
public bool CancelMoveTo { get; set; } = true;
|
|
|
|
/// <summary>Mask 0x10000 — default true.</summary>
|
|
public bool StopCompletelyFlag { get; set; } = true;
|
|
|
|
/// <summary>
|
|
/// Mask 0x20000 — default FALSE. DoInterpretedMotion @305597: when set,
|
|
/// forces the computed <c>jump_error_code</c> to <c>0x48</c> (A1: jump
|
|
/// BLOCKED by motion/position) regardless of what
|
|
/// <c>motion_allows_jump</c> would have said.
|
|
/// </summary>
|
|
public bool DisableJumpDuringLink { get; set; }
|
|
|
|
// ── scalar fields (retail ctor 0x00524380 defaults) ───────────────────
|
|
|
|
/// <summary>Retail default 0.6.</summary>
|
|
public float DistanceToObject { get; set; } = 0.6f;
|
|
|
|
/// <summary>Retail default 0.</summary>
|
|
public float MinDistance { get; set; }
|
|
|
|
/// <summary>Retail default 0.</summary>
|
|
public float DesiredHeading { get; set; }
|
|
|
|
/// <summary>Retail default 1.</summary>
|
|
public float Speed { get; set; } = 1f;
|
|
|
|
/// <summary>Retail default FLT_MAX.</summary>
|
|
public float FailDistance { get; set; } = float.MaxValue;
|
|
|
|
/// <summary>
|
|
/// Retail default 15.0 (@300519). ACE-divergence trap (W0-pins A4): ACE
|
|
/// changed <c>Default_WalkRunThreshold</c> to 1.0 — do not copy.
|
|
/// </summary>
|
|
public float WalkRunThreshhold { get; set; } = 15f;
|
|
|
|
/// <summary>Retail default 0.</summary>
|
|
public uint ContextId { get; set; }
|
|
|
|
/// <summary>Retail default <see cref="Physics.HoldKey.Invalid"/>.</summary>
|
|
public HoldKey HoldKeyToApply { get; set; } = HoldKey.Invalid;
|
|
|
|
/// <summary>Retail default 0.</summary>
|
|
public uint ActionStamp { get; set; }
|
|
|
|
// ── R4-V1: command-selection family (closes M2-mechanics) ─────────────
|
|
|
|
/// <summary>
|
|
/// Retail <c>MovementParameters::get_command</c> (<c>0x0052aa00</c>,
|
|
/// raw 307946-308012), VERBATIM per
|
|
/// docs/research/2026-07-03-r4-moveto/r4-moveto-decomp.md §5c. Picks the
|
|
/// motion command + moving-away flag from the towards/away bitfield
|
|
/// combination, THEN the walk-vs-run <see cref="HoldKey"/> cascade.
|
|
///
|
|
/// <para>
|
|
/// <b>Command pick</b> (mirrors <c>towards_and_away</c>'s bands but is
|
|
/// NOT identical — see the asymmetry note on <see cref="TowardsAndAway"/>):
|
|
/// <list type="bullet">
|
|
/// <item><description><c>MoveTowards && MoveAway</c> → delegate
|
|
/// to <see cref="TowardsAndAway"/> (the three-band form).</description></item>
|
|
/// <item><description><c>MoveTowards</c> only (or neither flag set —
|
|
/// retail's <c>else if</c> falls through to the SAME branch): plain
|
|
/// towards — <c>dist > DistanceToObject</c> → WalkForward,
|
|
/// !movingAway; else idle (cmd 0).</description></item>
|
|
/// <item><description><c>MoveAway</c> only: pure away —
|
|
/// <c>dist < MinDistance</c> → WalkForward, movingAway=true (the
|
|
/// heading flips +180 via <see cref="GetDesiredHeading"/> — turn-around,
|
|
/// NOT WalkBackwards, unlike <see cref="TowardsAndAway"/>'s min-band);
|
|
/// else idle.</description></item>
|
|
/// </list>
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// <b>THE walk-vs-run rule</b> (confirms
|
|
/// <c>feedback_autowalk_cancharge_bit</c> — port RETAIL's version of
|
|
/// BOTH the fast-path ACE dropped and the threshold-close-walk pair):
|
|
/// <c>HoldKey.Run</c> ⇐ <c>CanCharge</c> set (the fast-path — wins
|
|
/// regardless of CanRun/CanWalk/distance), OR (<c>CanRun</c> set AND
|
|
/// (<c>CanWalk</c> clear OR <c>dist - DistanceToObject >
|
|
/// WalkRunThreshhold</c>)). <c>HoldKey.None</c> (walk) ⇐ no CanRun, or
|
|
/// walk-capable within the threshold (INCLUSIVE ≤ — the raw's
|
|
/// <c>test ah,0x41</c> after the fcom is the not-greater-than reading,
|
|
/// §5c @308003).
|
|
/// </para>
|
|
/// </summary>
|
|
/// <param name="dist">Current distance-to-target (retail's
|
|
/// <c>GetCurrentDistance</c> result — center or cylinder distance per
|
|
/// <see cref="MoveToMath.CylinderDistance"/>).</param>
|
|
/// <param name="headingDiff">Heading-to-target minus current heading,
|
|
/// normalized [0,360) — UNUSED by <c>get_command</c> itself (the raw
|
|
/// signature carries it for parity with the caller's local; retail's
|
|
/// body never reads <c>arg3</c> in this build). Kept as a parameter for
|
|
/// call-site symmetry with <c>BeginMoveForward</c> (§4c), which computes
|
|
/// it immediately before calling <c>get_command</c>.</param>
|
|
/// <param name="motion">Chosen motion command id, or 0 if no movement
|
|
/// is needed (already in range).</param>
|
|
/// <param name="holdKey">Chosen hold key (walk vs run).</param>
|
|
/// <param name="movingAway">True if the chosen motion moves the mover
|
|
/// AWAY from the target (feeds <see cref="GetDesiredHeading"/> and the
|
|
/// arrival predicate's polarity).</param>
|
|
public void GetCommand(float dist, float headingDiff, out uint motion, out HoldKey holdKey, out bool movingAway)
|
|
{
|
|
_ = headingDiff; // retail's arg3 — unread in this build's body (§5c)
|
|
|
|
// ── command + moving_away pick ──────────────────────────────────
|
|
if (MoveTowards && MoveAway)
|
|
{
|
|
TowardsAndAway(dist, out motion, out movingAway);
|
|
}
|
|
else if (MoveAway && !MoveTowards)
|
|
{
|
|
// pure AWAY: dist < min_distance → WalkForward, moving away
|
|
// (turn-around; heading flips +180 via GetDesiredHeading).
|
|
if (dist < MinDistance)
|
|
{
|
|
motion = MotionCommand.WalkForward;
|
|
movingAway = true;
|
|
}
|
|
else
|
|
{
|
|
motion = 0u;
|
|
movingAway = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// plain TOWARDS (MoveTowards set, or neither flag set — retail's
|
|
// `else if ((flags & 0x100) == 0)` falls to the same label).
|
|
if (dist > DistanceToObject)
|
|
{
|
|
motion = MotionCommand.WalkForward;
|
|
movingAway = false;
|
|
}
|
|
else
|
|
{
|
|
motion = 0u;
|
|
movingAway = false;
|
|
}
|
|
}
|
|
|
|
// ── walk-vs-run HoldKey cascade ─────────────────────────────────
|
|
if (CanCharge)
|
|
{
|
|
// THE fast-path ACE dropped: can_charge short-circuits straight
|
|
// to Run regardless of CanRun/CanWalk/distance.
|
|
holdKey = HoldKey.Run;
|
|
return;
|
|
}
|
|
if (!CanRun)
|
|
{
|
|
holdKey = HoldKey.None;
|
|
return;
|
|
}
|
|
if (CanWalk && (dist - DistanceToObject) <= WalkRunThreshhold)
|
|
{
|
|
holdKey = HoldKey.None;
|
|
return;
|
|
}
|
|
holdKey = HoldKey.Run;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retail <c>MovementParameters::towards_and_away</c>
|
|
/// (<c>0x0052a9a0</c>, raw 307917-307942), VERBATIM per
|
|
/// r4-moveto-decomp.md §5d. Three bands:
|
|
/// <list type="bullet">
|
|
/// <item><description><c>dist > DistanceToObject</c> → WalkForward,
|
|
/// towards (not moving away).</description></item>
|
|
/// <item><description><c>dist - MinDistance < F_EPSILON</c> (inside
|
|
/// the min-distance band) → WalkBackward, moving away. NOTE the
|
|
/// asymmetry vs <see cref="GetCommand"/>'s pure-away branch: this backs
|
|
/// up with WalkBackwards (no turn-around), not WalkForward+heading-flip
|
|
/// (r4-moveto-decomp.md :656).</description></item>
|
|
/// <item><description>otherwise (strictly inside [MinDistance,
|
|
/// DistanceToObject]) → idle (cmd 0).</description></item>
|
|
/// </list>
|
|
/// </summary>
|
|
/// <param name="dist">Current distance-to-target.</param>
|
|
/// <param name="cmd">Chosen motion command, or 0 if already in the dead
|
|
/// band.</param>
|
|
/// <param name="movingAway">True only for the WalkBackward (min-band)
|
|
/// case.</param>
|
|
public void TowardsAndAway(float dist, out uint cmd, out bool movingAway)
|
|
{
|
|
const float epsilon = 0.000199999995f;
|
|
|
|
if (dist > DistanceToObject)
|
|
{
|
|
cmd = MotionCommand.WalkForward;
|
|
movingAway = false;
|
|
return;
|
|
}
|
|
if (dist - MinDistance < epsilon)
|
|
{
|
|
cmd = MotionCommand.WalkBackward;
|
|
movingAway = true;
|
|
return;
|
|
}
|
|
cmd = 0u;
|
|
movingAway = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retail <c>MovementParameters::get_desired_heading</c>
|
|
/// (<c>0x0052aad0</c>), PINNED by direct Ghidra decompile of
|
|
/// <c>patchmem.gpr</c> (fetched live during V0 — see
|
|
/// docs/research/2026-07-03-r4-moveto/ghidra-confirmations.md §P2,
|
|
/// ACE-shaped constants CONFIRMED exact, superseding the earlier
|
|
/// BN-garble-based "high confidence" pin):
|
|
/// <code>
|
|
/// __thiscall get_desired_heading(command, movingAway)
|
|
/// {
|
|
/// if (command == RunForward || command == WalkForward) {
|
|
/// if (!movingAway) return 0.0f;
|
|
/// } else {
|
|
/// if (command != WalkBackward) return 0.0f;
|
|
/// if (movingAway) return 0.0f;
|
|
/// }
|
|
/// return 180.0f;
|
|
/// }
|
|
/// </code>
|
|
/// Truth table: forward/run + towards → 0°; forward/run + away → 180°;
|
|
/// backward + towards → 180°; backward + away → 0°; any other command
|
|
/// → 0°. This is the heading OFFSET added to the raw heading-to-target
|
|
/// so an "away" walk faces away and an "away" backstep faces the
|
|
/// target.
|
|
/// </summary>
|
|
public float GetDesiredHeading(uint command, bool movingAway)
|
|
{
|
|
if (command == MotionCommand.RunForward || command == MotionCommand.WalkForward)
|
|
{
|
|
if (!movingAway) return 0f;
|
|
}
|
|
else
|
|
{
|
|
if (command != MotionCommand.WalkBackward) return 0f;
|
|
if (movingAway) return 0f;
|
|
}
|
|
return 180f;
|
|
}
|
|
|
|
// ── R4-V1: wire factory (closes M15/wire-exposure groundwork) ─────────
|
|
|
|
/// <summary>
|
|
/// Factory for the retail <c>MovementParameters::UnPackNet</c> 7-dword
|
|
/// MoveTo wire form (<c>0x0052ac50</c>, 0x1c bytes, raw 308118-308205 —
|
|
/// r4-moveto-decomp.md §2g): <c>bitfield, distance_to_object,
|
|
/// min_distance, fail_distance, speed, walk_run_threshhold,
|
|
/// desired_heading</c>. Used by MoveToObject (type 6) and
|
|
/// MoveToPosition (type 7) wire payloads — the SAME field order
|
|
/// <c>UpdateMotion.TryParseMoveToPayload</c> already reads
|
|
/// (UpdateMotion.cs:328-341). The bitfield fully OVERWRITES every named
|
|
/// flag (UnPackNet does not merge with ctor defaults — every bit not
|
|
/// present in <paramref name="bitfield"/> resolves to false); the wire
|
|
/// bitfield carries <c>can_charge</c> (0x10), the walk-vs-run answer
|
|
/// consumed by <see cref="GetCommand"/> (cross-ref
|
|
/// <c>feedback_autowalk_cancharge_bit</c>).
|
|
/// </summary>
|
|
public static MovementParameters FromWire(
|
|
uint bitfield,
|
|
float distanceToObject,
|
|
float minDistance,
|
|
float failDistance,
|
|
float speed,
|
|
float walkRunThreshhold,
|
|
float desiredHeading)
|
|
{
|
|
var p = new MovementParameters();
|
|
ApplyBitfield(p, bitfield);
|
|
p.DistanceToObject = distanceToObject;
|
|
p.MinDistance = minDistance;
|
|
p.FailDistance = failDistance;
|
|
p.Speed = speed;
|
|
p.WalkRunThreshhold = walkRunThreshhold;
|
|
p.DesiredHeading = desiredHeading;
|
|
return p;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Factory for the retail <c>MovementParameters::UnPackNet</c> 3-dword
|
|
/// TurnTo wire form (0xc bytes, r4-moveto-decomp.md §2g): <c>bitfield,
|
|
/// speed, desired_heading</c>. Used by TurnToObject (type 8) and
|
|
/// TurnToHeading (type 9) wire payloads. Distance-related scalars
|
|
/// (<c>DistanceToObject</c>/<c>MinDistance</c>/<c>FailDistance</c>/
|
|
/// <c>WalkRunThreshhold</c>) are NOT on this wire form and keep the
|
|
/// <see cref="MovementParameters"/> ctor defaults — retail's UnPackNet
|
|
/// for this form only ever writes the three fields named here.
|
|
/// </summary>
|
|
public static MovementParameters FromWireTurnTo(
|
|
uint bitfield,
|
|
float speed,
|
|
float desiredHeading)
|
|
{
|
|
var p = new MovementParameters();
|
|
ApplyBitfield(p, bitfield);
|
|
p.Speed = speed;
|
|
p.DesiredHeading = desiredHeading;
|
|
return p;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decode the A4-pinned bitfield masks onto the named bool properties.
|
|
/// Shared by <see cref="FromWire"/>/<see cref="FromWireTurnTo"/> — the
|
|
/// wire bitfield always fully overwrites (retail's UnPackNet assigns
|
|
/// the raw dword straight into <c>this->bitfield</c>, no merge).
|
|
/// </summary>
|
|
private static void ApplyBitfield(MovementParameters p, uint bitfield)
|
|
{
|
|
p.CanWalk = (bitfield & 0x1u) != 0;
|
|
p.CanRun = (bitfield & 0x2u) != 0;
|
|
p.CanSidestep = (bitfield & 0x4u) != 0;
|
|
p.CanWalkBackwards = (bitfield & 0x8u) != 0;
|
|
p.CanCharge = (bitfield & 0x10u) != 0;
|
|
p.FailWalk = (bitfield & 0x20u) != 0;
|
|
p.UseFinalHeading = (bitfield & 0x40u) != 0;
|
|
p.Sticky = (bitfield & 0x80u) != 0;
|
|
p.MoveAway = (bitfield & 0x100u) != 0;
|
|
p.MoveTowards = (bitfield & 0x200u) != 0;
|
|
p.UseSpheres = (bitfield & 0x400u) != 0;
|
|
p.SetHoldKey = (bitfield & 0x800u) != 0;
|
|
p.Autonomous = (bitfield & 0x1000u) != 0;
|
|
p.ModifyRawState = (bitfield & 0x2000u) != 0;
|
|
p.ModifyInterpretedState = (bitfield & 0x4000u) != 0;
|
|
p.CancelMoveTo = (bitfield & 0x8000u) != 0;
|
|
p.StopCompletelyFlag = (bitfield & 0x10000u) != 0;
|
|
p.DisableJumpDuringLink = (bitfield & 0x20000u) != 0;
|
|
}
|
|
}
|