No copies or uploads of M.A.W.S. assetsExample code is free to useNo printed reproduction or modification of this documentationSuggestions: Discord to the authorLicense
Movement
Hop & thruster jump
One key, two jumps: a tap makes the legs push off, a double tap fires the thrusters. The server validates, every machine shows flames and sound, and only the physics owner changes the velocity.
In plain wordsPressing jump asks the server. The server checks fuel and cooldown, then counts up a number that every machine receives. Every machine shows flames and plays sound; only the machine that moves the walker pushes it into the air.
Flow
Driver key (tap / double tap)→PlayerController RPC→Server: seat, park, cooldown, fuel→RplProp counter + kind→Every machine: FX + sound→Owner: velocity
Telling tap and double tap apart
The first press sends a hop immediately. A second press within 0.45 s upgrades it to a thruster jump – the hop has already started its dip, so there is no input lag.
Example code – free to use
protected float m_fLastPress = -10;
protected void OnJumpKey(float value, EActionTrigger reason)
{
float now = GetGame().GetWorld().GetWorldTime() * 0.001;
int kind = KIND_HOP;
if (now - m_fLastPress < m_fDoubleTapTime)
kind = KIND_JET;
m_fLastPress = now;
SCR_PlayerController pc = SCR_PlayerController.Cast(GetGame().GetPlayerController());
if (pc)
pc.MyWalker_RequestJump(GetOwner().GetRootParent(), kind);
}
Server: validate, then count
Example code – free to use
[RplProp(onRplName: "OnJumpReplicated")]
protected int m_iJumpNr;
[RplProp(onRplName: "OnJumpReplicated")]
protected int m_iJumpKind;
[RplProp()]
protected float m_fFuel = 100;
void ServerJump(int kind)
{
if (IsParked() || OnCooldown())
return;
if (kind == KIND_JET && m_fFuel < m_fFuelPerJump)
kind = KIND_HOP; // not enough fuel: still hop
if (kind == KIND_JET)
m_fFuel -= m_fFuelPerJump; // refuels over time (CallLater)
m_iJumpKind = kind;
m_iJumpNr++;
Replication.BumpMe();
OnJumpReplicated();
}
Every machine: effects. Owner: physics.
Example code – free to use
protected void OnJumpReplicated()
{
if (m_iJumpNr == m_iJumpSeen)
return;
m_iJumpSeen = m_iJumpNr;
if (m_iJumpKind == KIND_JET)
{
m_fBurn = m_fBurnTime; // 1.6 s, the per-frame tick shows flames while > 0
return;
}
// hop: dip first, stretch and lift off a moment later
m_Rig.AddBodyImpulse(Vector(0, -m_fHopDip, 0));
GetGame().GetCallqueue().CallLater(HopStretch, m_fHopDipTime * 1000, false);
}
//! Per frame on the owner while burning.
protected void BurnTick(Physics phys, float dt)
{
vector v = phys.GetVelocity();
v = v + Vector(0, m_fJetUp * dt, 0) + m_vForward * (m_fJetForward * dt);
phys.SetVelocity(v);
}
Braking burn and landing
- Braking burn: below 14 m above ground and sinking faster than 7 m/s the nozzles fire again (20 m/s² up) until the sink rate is down to 3 m/s.
- Landing damper: close to the ground the sink rate is clamped to 3.5 m/s – the leg rig absorbs the rest visually. Hard landings otherwise pushed the hidden wheels through the terrain.
- Ground rescue (always active): if the root ends up more than 0.6 m below the terrain surface, it is lifted out.
Pitfall: soundSound sources are not unlimited. The hop sound and the thruster ignition 0.2 s later were both played through the same
SoundComponent on the pelvis – the ignition was swallowed. One-shots that overlap with other sounds are now played free via AudioSystem.PlayEvent(project, event, transform).Example code – free to use
AudioHandle PlayFree(ResourceName project, string eventName, vector pos)
{
vector m[4];
Math3D.MatrixIdentity4(m);
m[3] = pos;
return AudioSystem.PlayEvent(project, eventName, m);
}