Sci-Fi Expansion Pack
Gravity field
A back module that lifts everything around the walker 10–30 m into the air, holds it there for 15 seconds and then slams it down at four times gravity.
Phases
| Phase | Duration | What happens |
|---|---|---|
| CHARGE | 5 s | boundary effect, warning sound |
| LIFT | 15 s | every object with mass in the circle floats up to its hover height and drifts slowly |
| SLAM | 2 s | extra 3 g downwards (= 4 g total); characters are guided down and hit the ground |
| IDLE | 90 s cooldown | shield recharges, which was off during the field |
Only the server switches phases. Phase, a phase counter and the remaining cooldown are replicated through the
module manager (via modded class).
Every machine moves what it simulates
Physics of a player-driven car runs on that player's client, AI characters run on the server, dropped items may run anywhere. Applying forces only on the server would miss most of them. Instead every machine applies the field to the bodies it simulates.
//! Per frame on every machine while LIFT or SLAM (callqueue: vehicle frames do not tick on foreign clients).
protected void FieldTick()
{
float dt = GetGame().GetWorld().GetTimeSlice();
foreach (IEntity e : m_aInField)
{
if (!e)
continue;
RplComponent rpl = RplComponent.Cast(e.FindComponent(RplComponent));
if (rpl && !rpl.IsOwner())
continue; // someone else simulates it
Physics p = e.GetPhysics();
if (!p || !p.IsDynamic())
continue;
if (m_iPhase == PHASE_LIFT)
Hover(e, p, dt);
else
p.ApplyImpulse(Vector(0, -3.0 * 9.81 * p.GetMass() * dt, 0)); // +3 g
}
}
Hover as a spring
A plain upward force would launch light objects into orbit and barely move heavy ones. A damped spring toward a per-object hover height (start height + 8..12 m, with a slow pendulum) works for crates and cars alike.
protected void Hover(IEntity e, Physics p, float dt)
{
MyGravObj o = m_mObjects.Get(e);
o.m_fPhase += dt * Math.PI2 / o.m_fPeriod;
float target = o.m_fHover + 0.6 * Math.Sin(o.m_fPhase);
vector v = p.GetVelocity();
float err = target - e.GetOrigin()[1];
float vy = Math.Clamp(err * 1.5, -6, 6); // m/s toward the hover height
v[1] = v[1] + (vy - v[1]) * Math.Clamp(dt * 3, 0, 1);
v[0] = v[0] + (o.m_vDrift[0] - v[0]) * Math.Clamp(dt, 0, 1);
v[2] = v[2] + (o.m_vDrift[2] - v[2]) * Math.Clamp(dt, 0, 1);
p.SetVelocity(v);
p.SetAngularVelocity(o.m_vSpin);
}
Characters
Characters do not respond to forces while animated, and their ragdoll bones cannot be reached from script. What
works: the server makes every living character in the field unconscious – a vanilla state that puts it into
ragdoll and replicates cleanly. The flight path is simulated in script and written as a position with
Teleport; the ragdoll follows and tumbles on its own. Survivors wake up three seconds after the field.
// server, once per character that enters the field
SCR_CharacterControllerComponent cc = SCR_CharacterControllerComponent.Cast(ch.GetCharacterController());
if (cc && cc.GetLifeState() == ECharacterLifeState.ALIVE)
cc.SetUnconscious(true); // ragdoll; woken again after the field
// every frame: AI characters on EVERY machine (same random seed), player characters only on their own client
vector m[4];
ch.GetWorldTransform(m); // keep the body's own orientation
m[3] = o.m_vSimPos; // set only the position
ch.Teleport(m);
PhysicsRagdoll.GetRagdoll() returns null for characters – the bones cannot be driven. Work with the character root only.The slam: guided, not dropped
Both obvious solutions failed in the test. Teleporting ragdolls downward at 35 m/s flung one 24 km away and crashed the client ("Entity out of world bounds"). A free fall from 30 m reaches over 20 m/s, and thin ragdoll limbs tunnel through the terrain. The working version keeps the 4 g but caps the speed and slows down near the ground. The impact damage is still that of a 4 g fall from the hover height, credited to whoever triggered the field.
sv[1] = sv[1] - 4 * 9.81 * dt;
float maxFall = Math.Clamp((sp[1] - ground) * 2.5, 3, 12); // at most 12 m/s, slower near the ground
if (sv[1] < -maxFall)
sv[1] = -maxFall;
sp = sp + sv * dt;
if (sp[1] <= ground + 0.2)
Land(ch); // stop teleporting; damage after 0.3 s
// damage: what a 4 g impact from the hover height would do
float impact = Math.Sqrt(2 * 4 * 9.81 * hoverHeight);
float damage = Math.Max(0, impact - 6) * 12;
MyDamage.Apply(ch, EDamageType.KINETIC, damage, Instigator.CreateInstigator(triggeredBy));