Loot 1 (simulation): Zone owns floating items, rolls death drops on SimulationDice, expires them and commits pickups
Zone.Release is the one adder, StepFloatingItems and TryPickUp the only removers; DropLoot runs from the one death subscription before the entity leaves the zone. Three C# default settings. Unity's drop path is untouched (loot-1-present deletes it). Not yet built or run at this commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d50f54bf53
commit
fa4467e9a3
4 changed files with 468 additions and 1 deletions
|
|
@ -40,3 +40,14 @@ public struct KinematicBody
|
|||
Position += (Velocity + Drift) * dt;
|
||||
}
|
||||
}
|
||||
|
||||
// An item adrift in a zone: a death drop or jettisoned cargo. Zone.Release is its only adder; Zone's floating-item
|
||||
// step (expiry) and TryPickUp are its only removers. Runtime state, never saved.
|
||||
public sealed class FloatingItem
|
||||
{
|
||||
public FloatingBodyId Id;
|
||||
public ItemInstance Item;
|
||||
public KinematicBody Body;
|
||||
public double ReleasedAt;
|
||||
public float Lifetime;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ public class GameplaySettings
|
|||
public float FloatingBodyDrag = .05f;
|
||||
public float FloatingBodyLaunchDrag = .2f;
|
||||
public float FloatingBodyGravity = 1f;
|
||||
// Loot: the chance each equipped item (the hull excepted) drops when its carrier dies, the launch speed of a
|
||||
// dropped item, and how many seconds a floating item lasts.
|
||||
public float LootDropProbability = .25f;
|
||||
public float LootDropVelocity = 25f;
|
||||
public float FloatingItemLifetime = 30f;
|
||||
public float SevereHeatstrokeRiskThreshold = .25f;
|
||||
public float WormholeDepth = 1000;
|
||||
public float WormholeExitVelocity = 20;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ public class Zone
|
|||
// Vented vapour clouds, in vent order. Zone owns their existence: Vent is the one adder, StepClouds the one
|
||||
// remover, and Obscuration the one reader that detection uses. Runtime state, never saved.
|
||||
public ReactiveCollection<VapourCloud> Clouds = new ReactiveCollection<VapourCloud>();
|
||||
|
||||
// Floating items (death drops, jettisoned cargo), in release order. Zone owns their existence: Release is the one
|
||||
// adder; StepFloatingItems (expiry) and TryPickUp are the only removers. Runtime state, never saved.
|
||||
public ReactiveCollection<FloatingItem> FloatingItems = new ReactiveCollection<FloatingItem>();
|
||||
public Dictionary<CultRecordKey, BodyData> Planets = new Dictionary<CultRecordKey, BodyData>();
|
||||
public Dictionary<CultRecordKey, Planet> PlanetInstances = new Dictionary<CultRecordKey, Planet>();
|
||||
|
||||
|
|
@ -93,6 +97,9 @@ public class Zone
|
|||
private uint _lastBodyId;
|
||||
public FloatingBodyId NextBodyId() => new FloatingBodyId(++_lastBodyId);
|
||||
|
||||
// The death ordinal of the loot dice: the nth death in this zone rolls on SimulationDice.For(CombatSeed, LootStream, n).
|
||||
private uint _deaths;
|
||||
|
||||
public Zone(ItemManager itemManager, PlanetSettings settings, ZonePack pack, GalaxyZone galaxyZone, Galaxy galaxy)
|
||||
{
|
||||
_time = pack.Time;
|
||||
|
|
@ -108,7 +115,7 @@ public class Zone
|
|||
// in the simulation, not only in Unity's own loot-drop subscription. One subscription point covers
|
||||
// every join, whichever caller admits the entity (deserialization below, a jump, a spawned turret) --
|
||||
// ObserveAdd fires for all of them. Forbidden writer: no presentation may remove an entity from Zone.
|
||||
Entities.ObserveAdd().Subscribe(add => add.Value.Death.Subscribe(_ => { Entities.Remove(add.Value); add.Value.Deactivate(); }));
|
||||
Entities.ObserveAdd().Subscribe(add => add.Value.Death.Subscribe(_ => { DropLoot(add.Value); Entities.Remove(add.Value); add.Value.Deactivate(); }));
|
||||
|
||||
Targets.Add(new EntityTargets());
|
||||
|
||||
|
|
@ -232,6 +239,7 @@ public class Zone
|
|||
|
||||
StepMines(deltaTime);
|
||||
StepClouds(deltaTime);
|
||||
StepFloatingItems(deltaTime);
|
||||
|
||||
// Cut 3: after every entity has had its chance to fire this tick, age and resolve the shots that
|
||||
// firing queued. A shot fired this tick with a flight time shorter than CommitHorizon commits and
|
||||
|
|
@ -252,6 +260,61 @@ public class Zone
|
|||
Clouds.Add(cloud);
|
||||
}
|
||||
|
||||
// The one adder of floating items: death drops and jettison both come here. The item drifts as a KinematicBody
|
||||
// launched at `velocity` and expires after FloatingItemLifetime.
|
||||
public FloatingItem Release(ItemInstance item, float2 position, float2 velocity)
|
||||
{
|
||||
var floating = new FloatingItem
|
||||
{
|
||||
Id = NextBodyId(), Item = item, Body = new KinematicBody { Position = position, Velocity = velocity },
|
||||
ReleasedAt = _time, Lifetime = _itemManager.GameplaySettings.FloatingItemLifetime
|
||||
};
|
||||
FloatingItems.Add(floating);
|
||||
return floating;
|
||||
}
|
||||
|
||||
// The one commit of a pickup: the first of the taker's cargo bays that accepts the item stores it and the item
|
||||
// stops floating. Otherwise nothing changes.
|
||||
public bool TryPickUp(Entity taker, FloatingBodyId id)
|
||||
{
|
||||
var floating = FloatingItems.FirstOrDefault(f => f.Id.Equals(id));
|
||||
if (floating == null || !taker.CargoBays.Any(bay => bay.TryStore(floating.Item))) return false;
|
||||
FloatingItems.Remove(floating);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Called from the death subscription, before the entity leaves the zone: one generator per death. Each equipped
|
||||
// item but the hull drops on a LootDropProbability draw; every cargo stack drops whole. Each drop leaves from the
|
||||
// dead entity's position at a random heading. Draw order: Equipment order (the probability draw, then the heading
|
||||
// of a dropped item), then each cargo bay's stacks in CargoBays order (a heading each).
|
||||
private void DropLoot(Entity dead)
|
||||
{
|
||||
var settings = _itemManager.GameplaySettings;
|
||||
var random = SimulationDice.For(CombatSeed, SimulationDice.LootStream, ++_deaths);
|
||||
var at = new float2(dead.Position.x, dead.Position.z);
|
||||
float2 Heading() { var angle = random.NextFloat() * 2f * PI; return float2(cos(angle), sin(angle)); }
|
||||
foreach (var equipped in dead.Equipment.ToArray())
|
||||
{
|
||||
if (equipped == dead.EquippedHull || !(random.NextFloat() < settings.LootDropProbability)) continue;
|
||||
Release(equipped.EquippableItem, at, Heading() * settings.LootDropVelocity);
|
||||
}
|
||||
foreach (var bay in dead.CargoBays.ToArray())
|
||||
foreach (var stack in bay.Cargo.Keys.ToArray())
|
||||
Release(stack, at, Heading() * settings.LootDropVelocity);
|
||||
}
|
||||
|
||||
// After StepClouds and before FireControl.Step: each floating item drifts as a KinematicBody on the zone's force
|
||||
// and is removed once its lifetime has run out.
|
||||
private void StepFloatingItems(float deltaTime)
|
||||
{
|
||||
var settings = _itemManager.GameplaySettings;
|
||||
foreach (var floating in FloatingItems.ToArray())
|
||||
{
|
||||
floating.Body.Step(deltaTime, GetForce(floating.Body.Position), settings);
|
||||
if (floating.ReleasedAt + floating.Lifetime <= _time) FloatingItems.Remove(floating);
|
||||
}
|
||||
}
|
||||
|
||||
// The fraction of a sight line's signal that survives the clouds between `a` and `b`: the product of
|
||||
// (1 - opacity now) over every cloud whose disc the segment touches, so 1f when none does. Derived from the
|
||||
// live clouds at each call and stored nowhere; Sensor.Gain is where it enters detection.
|
||||
|
|
|
|||
Loading…
Reference in a new issue