Zone.Entities read-only outside Zone: Admit and Remove are the only membership writers
Entities is a MemberView with no mutating member; Admit installs the death watch with the membership and refuses a member already present, Remove takes it away with the membership. The Reset and duplicate-Add bypass paths of the death watch cannot be spelled any more. Tests move to Admit/Remove; Admit also activates, so the explicit Activate calls that followed a raw Add are dropped. Unity call sites route through Zone.Remove. Builds: unverified at this commit (verified in the job at the head). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
38c2fcf6e4
commit
fa911f2d85
50 changed files with 206 additions and 320 deletions
|
|
@ -64,10 +64,8 @@ public static class ImpactPresenterSmoke
|
|||
var hullRef = items.ItemData.RefOf<ItemData>(items.ItemData.GetByName<HullData>("Smoke Hull"));
|
||||
var ship = new Ship(items, zone, new EquippableItem { Data = hullRef, Durability = 1000, Lot = 1 }, new EntitySettings());
|
||||
var other = new Ship(items, zone, new EquippableItem { Data = hullRef, Durability = 1000, Lot = 2 }, new EntitySettings());
|
||||
zone.Entities.Add(ship);
|
||||
zone.Entities.Add(other);
|
||||
ship.Activate();
|
||||
other.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
zone.Admit(other, piloted: false);
|
||||
|
||||
// The scene's side: the tuning object with a pooled effect, and the game manager that names the player's ship.
|
||||
var feedbackObject = new GameObject("Smoke Impact Feedback");
|
||||
|
|
|
|||
|
|
@ -739,7 +739,7 @@ public class ActionGameManager : MonoBehaviour
|
|||
if (CurrentEntity != null)
|
||||
{
|
||||
CurrentEntity.Deactivate();
|
||||
CurrentEntity.Zone.Entities.Remove(CurrentEntity);
|
||||
CurrentEntity.Zone.Remove(CurrentEntity);
|
||||
CurrentEntity.Zone = Zone;
|
||||
Zone.Admit(CurrentEntity, piloted: false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +0,0 @@
|
|||
using System;
|
||||
using CultMath;
|
||||
using CultMath.UnityBridge;
|
||||
using UnityEngine;
|
||||
using static CultMath.math;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
public class GridObject : MonoBehaviour
|
||||
{
|
||||
public float RotationSpeed;
|
||||
public float GridOffset;
|
||||
public float GridAttraction;
|
||||
public float Gravity;
|
||||
public float Drag = .1f;
|
||||
public float LaunchDrag;
|
||||
|
||||
private float2 _selfVelocity;
|
||||
private float _timeOffset;
|
||||
|
||||
public Zone Zone { get; set; }
|
||||
public Vector3 Velocity { get; set; }
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_timeOffset = Random.value * 100;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
var t = transform;
|
||||
t.localRotation = Quaternion.Euler(sin(Time.time - _timeOffset * RotationSpeed) * 90, 0, cos(Time.time - _timeOffset * RotationSpeed) * 90);
|
||||
|
||||
if (Zone == null) return;
|
||||
|
||||
var position = t.position;
|
||||
var gridHeight = Zone.GetHeight(position.Flatland().ToCultMath()) + GridOffset;
|
||||
Velocity += Vector3.up * (sign(gridHeight - position.y) * GridAttraction * Time.deltaTime);
|
||||
Velocity *= max(0, 1 - LaunchDrag * Time.deltaTime);
|
||||
var normal = Zone.GetNormal(position.Flatland().ToCultMath());
|
||||
var force = new float2(normal.x, normal.z);
|
||||
var forceMagnitude = lengthsq(force);
|
||||
if (forceMagnitude > .001f)
|
||||
{
|
||||
var fa = 1 / (1 - forceMagnitude) - 1;
|
||||
_selfVelocity += normalize(force) * Zone.Settings.GravityStrength * fa * Gravity;
|
||||
}
|
||||
|
||||
_selfVelocity *= max(0, 1 - Drag * Time.deltaTime);
|
||||
t.position = position + (Velocity + new Vector3(_selfVelocity.x, 0, _selfVelocity.y)) * Time.deltaTime;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 737f47c158214f0995ba2835f040ed96
|
||||
timeCreated: 1618441439
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
public class TimedDestroy : MonoBehaviour
|
||||
{
|
||||
public float Duration;
|
||||
|
||||
private float _startTime;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_startTime = Time.time;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if(Time.time - _startTime > Duration) Destroy(gameObject);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 5d5539a23b814254b038a16f32f1d8a9
|
||||
timeCreated: 1618453298
|
||||
|
|
@ -1507,7 +1507,7 @@ public abstract class Entity
|
|||
{
|
||||
bay.DockedShip = ship;
|
||||
ship.SetParent(this);
|
||||
Zone.Entities.Remove(ship);
|
||||
Zone.Remove(ship);
|
||||
ship.Deactivate();
|
||||
ship.Docked.OnNext(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,29 @@ using float2 = CultMath.float2;
|
|||
public class Zone
|
||||
{
|
||||
public Action<string> Log;
|
||||
public ReactiveCollection<Entity> Entities = new ReactiveCollection<Entity>();
|
||||
|
||||
// Membership, in admission order. Admit and Remove are the only writers; everyone else reads this view, which has
|
||||
// no mutating member, so the death watch below follows membership by construction.
|
||||
private readonly ReactiveCollection<Entity> _entities = new ReactiveCollection<Entity>();
|
||||
public MemberView Entities { get; }
|
||||
|
||||
public sealed class MemberView : IReadOnlyReactiveCollection<Entity>
|
||||
{
|
||||
private readonly ReactiveCollection<Entity> _members;
|
||||
internal MemberView(ReactiveCollection<Entity> members) => _members = members;
|
||||
public int Count => _members.Count;
|
||||
public Entity this[int index] => _members[index];
|
||||
public int IndexOf(Entity entity) => _members.IndexOf(entity);
|
||||
public bool Contains(Entity entity) => _members.Contains(entity);
|
||||
public IEnumerator<Entity> GetEnumerator() => _members.GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public UniRx.IObservable<CollectionAddEvent<Entity>> ObserveAdd() => _members.ObserveAdd();
|
||||
public UniRx.IObservable<int> ObserveCountChanged(bool notifyCurrentCount = false) => _members.ObserveCountChanged(notifyCurrentCount);
|
||||
public UniRx.IObservable<CollectionMoveEvent<Entity>> ObserveMove() => _members.ObserveMove();
|
||||
public UniRx.IObservable<CollectionRemoveEvent<Entity>> ObserveRemove() => _members.ObserveRemove();
|
||||
public UniRx.IObservable<CollectionReplaceEvent<Entity>> ObserveReplace() => _members.ObserveReplace();
|
||||
public UniRx.IObservable<Unit> ObserveReset() => _members.ObserveReset();
|
||||
}
|
||||
|
||||
// Laid mines, in lay order. Zone owns their existence: Lay is the one adder, the mine step the one remover,
|
||||
// and every removal is a blast. Runtime state, never saved.
|
||||
|
|
@ -105,30 +127,13 @@ public class Zone
|
|||
// The death ordinal of the loot dice: the nth death in this zone rolls on SimulationDice.For(CombatSeed, LootStream, n).
|
||||
private uint _deaths;
|
||||
|
||||
// One death subscription per member entity, owned here: a membership record and its subscription are created
|
||||
// together, and only when the entity is not already a member. Leaving Entities (Remove) or resetting it (Clear)
|
||||
// disposes it; nothing else touches it.
|
||||
// One death subscription per member entity: Admit creates it together with the membership, Remove disposes it
|
||||
// together with the membership, and nothing else touches either.
|
||||
private readonly Dictionary<Entity, IDisposable> _deathWatches = new Dictionary<Entity, IDisposable>();
|
||||
|
||||
private void WatchDeath(Entity entity)
|
||||
{
|
||||
if (_deathWatches.ContainsKey(entity)) return;
|
||||
_deathWatches.Add(entity, entity.Death.Subscribe(_ => { DropLoot(entity); Entities.Remove(entity); entity.Deactivate(); }));
|
||||
}
|
||||
|
||||
private void UnwatchDeath(Entity entity)
|
||||
{
|
||||
if (_deathWatches.Remove(entity, out var watch)) watch.Dispose();
|
||||
}
|
||||
|
||||
private void UnwatchAll()
|
||||
{
|
||||
foreach (var watch in _deathWatches.Values) watch.Dispose();
|
||||
_deathWatches.Clear();
|
||||
}
|
||||
|
||||
public Zone(ItemManager itemManager, PlanetSettings settings, ZonePack pack, GalaxyZone galaxyZone, Galaxy galaxy)
|
||||
{
|
||||
Entities = new MemberView(_entities);
|
||||
_time = pack.Time;
|
||||
GalaxyZone = galaxyZone;
|
||||
Galaxy = galaxy;
|
||||
|
|
@ -144,11 +149,8 @@ public class Zone
|
|||
// ObserveAdd fires for all of them. Forbidden writer: no presentation may remove an entity from Zone.
|
||||
// The death watch lives exactly as long as the entity's membership in this zone: installed on admission,
|
||||
// disposed on removal (death, dock, jump), so an entity admitted again never holds a second one.
|
||||
Entities.ObserveAdd().Subscribe(add => WatchDeath(add.Value));
|
||||
Entities.ObserveRemove().Subscribe(remove => UnwatchDeath(remove.Value));
|
||||
Entities.ObserveReset().Subscribe(_ => UnwatchAll());
|
||||
// The one record of a removal for observers (ruling reveal-ends-on-perception): read by the next Sense pass.
|
||||
Entities.ObserveRemove().Subscribe(remove => _departures.Add(new Departure(remove.Value, remove.Value.Position.xz,
|
||||
_entities.ObserveRemove().Subscribe(remove => _departures.Add(new Departure(remove.Value, remove.Value.Position.xz,
|
||||
remove.Value.Visibility - remove.Value.SignatureOn(SignatureChannel.Emission))));
|
||||
|
||||
Targets.Add(new EntityTargets());
|
||||
|
|
@ -205,17 +207,30 @@ public class Zone
|
|||
// TODO: Associate planets with stored entities for planetary colonies
|
||||
}
|
||||
|
||||
// The one admission into this zone (docs/scenarios-cut.md, 2.2): the only writer of Entities.Add and the only
|
||||
// The one admission into this zone (docs/scenarios-cut.md, 2.2): the only writer of membership and the only
|
||||
// creator of agents. Construction, new-run staging (RunStart), warp arrival, undock and the console's spawned
|
||||
// turret all join through here. A piloted entity gets the zone's Minion; an unpiloted one gets no agent at all.
|
||||
// Only a ship can be piloted. The Death subscription above covers every join, whichever caller admits it.
|
||||
// Only a ship can be piloted. The death watch is installed here, for every join, whichever caller admits it.
|
||||
// An entity that is already a member is refused: it would hold a second watch and a second agent.
|
||||
public void Admit(Entity entity, bool piloted)
|
||||
{
|
||||
Entities.Add(entity);
|
||||
if (_entities.Contains(entity)) throw new InvalidOperationException("entity: already a member of this zone");
|
||||
_entities.Add(entity);
|
||||
_deathWatches.Add(entity, entity.Death.Subscribe(_ => { DropLoot(entity); Remove(entity); entity.Deactivate(); }));
|
||||
entity.Activate();
|
||||
if (piloted) Agents.Add(CreateAgent((Ship) entity));
|
||||
}
|
||||
|
||||
// The one removal from this zone (death, dock, jump): membership and its death watch leave together. False, and
|
||||
// nothing changes, when the entity is not a member. The caller deactivates; Remove does not.
|
||||
public bool Remove(Entity entity)
|
||||
{
|
||||
if (!_entities.Remove(entity)) return false;
|
||||
_deathWatches.Remove(entity, out var watch);
|
||||
watch.Dispose();
|
||||
return true;
|
||||
}
|
||||
|
||||
private Agent CreateAgent(Ship ship)
|
||||
{
|
||||
var agent = new Minion(ship);
|
||||
|
|
@ -231,7 +246,7 @@ public class Zone
|
|||
{
|
||||
Radius = Pack.Radius,
|
||||
Mass = Pack.Mass,
|
||||
Entities = Entities.Select(EntitySerializer.Pack).ToList(),
|
||||
Entities = _entities.Select(EntitySerializer.Pack).ToList(),
|
||||
Orbits = Orbits.Keys.Select(key => new CultRecordRef<OrbitData>(key)).ToList(),
|
||||
Planets = Planets.Keys.Select(key => new CultRecordRef<BodyData>(key)).ToList(),
|
||||
Time = _time,
|
||||
|
|
|
|||
|
|
@ -113,11 +113,10 @@ public sealed class BrownoutTests : IDisposable
|
|||
// not flight, and a lone off-centre thruster could not fire without turning.
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName<GearData>("Consumer")), new int2(2, 1)));
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
zone.Admit(ship, piloted: false);
|
||||
// Ship.Turn stays 0, so Ship.Update commands no rotation: no incidental torque thrust from the
|
||||
// ship trying to turn. Aim steers nothing, so this one is only a plausible aim.
|
||||
ship.Aim = float3(0, 0, 1);
|
||||
ship.Activate();
|
||||
return ship;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,9 +96,8 @@ public sealed class ConditionRatioTests : IDisposable
|
|||
// not flight, and a lone off-centre thruster could not fire without turning.
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName<GearData>("Consumer")), new int2(2, 1)));
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
zone.Admit(ship, piloted: false);
|
||||
ship.Aim = float3(0, 0, 1); // Turn stays 0, as in BrownoutTests
|
||||
ship.Activate();
|
||||
consumer = ship.Equipment.Single(e => e.Data.Name == "Consumer");
|
||||
Assert.True(ship.GetBehavior<Thruster>().Torque == 0f, "the lone thruster turns nothing");
|
||||
|
||||
|
|
|
|||
|
|
@ -83,8 +83,7 @@ public sealed class ConsumableTests : IDisposable
|
|||
var ship = new Ship(items, zone, Gear(cache.Get(hullRef)), new EntitySettings());
|
||||
Assert.True(ship.TryEquip(Gear(cache.Get(engineRef)), EngineCell));
|
||||
Assert.True(ship.TryEquip(Gear(cache.Get(holdRef)), HoldCell));
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
var data = cache.Get(consumableRef);
|
||||
return new Rig
|
||||
{
|
||||
|
|
|
|||
|
|
@ -187,8 +187,7 @@ public sealed class ContactRevealTests : IDisposable
|
|||
Assert.True(ship.TryFindSpace(item, out var at));
|
||||
Assert.True(ship.TryEquip(item, at));
|
||||
}
|
||||
_zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
_zone.Admit(ship, piloted: false);
|
||||
ship.Position = position;
|
||||
ship.Aim = float3(0, 0, 1);
|
||||
_zone.Update(0f); // brings the gear online
|
||||
|
|
@ -514,8 +513,8 @@ public sealed class ContactRevealTests : IDisposable
|
|||
{
|
||||
switch (how)
|
||||
{
|
||||
case "remove": zone.Entities.Remove(e); break;
|
||||
case "warp": e.Deactivate(); zone.Entities.Remove(e); break;
|
||||
case "remove": zone.Remove(e); break;
|
||||
case "warp": e.Deactivate(); zone.Remove(e); break;
|
||||
case "death": e.Hull.Durability = 0f; e.HullDamage.OnNext(1f); break;
|
||||
default: throw new Exception("fixture: " + how);
|
||||
}
|
||||
|
|
@ -554,7 +553,7 @@ public sealed class ContactRevealTests : IDisposable
|
|||
Assert.True(e.Visibility * o.PassiveInfo(e.Position.xz, 1f) <= _items.GameplaySettings.TargetDetectionInfoThreshold, "fixture: its last light is below o's gate");
|
||||
Assert.Equal(2, times.Count);
|
||||
|
||||
_zone.Entities.Remove(e);
|
||||
_zone.Remove(e);
|
||||
Assert.Equal(2, o.RevealsOf(e).Count());
|
||||
|
||||
var lapsed = false;
|
||||
|
|
@ -640,7 +639,7 @@ public sealed class ContactRevealTests : IDisposable
|
|||
var gate = _items.GameplaySettings.TargetDetectionInfoThreshold / o.PassiveInfo(e.Position.xz, 1f);
|
||||
ShowOnly(e, gate * against, Guns(e)[0]);
|
||||
|
||||
_zone.Entities.Remove(e);
|
||||
_zone.Remove(e);
|
||||
Tick(.1f);
|
||||
|
||||
Assert.Equal(ended ? 0 : 1, o.RevealsOf(e).Count());
|
||||
|
|
@ -662,7 +661,7 @@ public sealed class ContactRevealTests : IDisposable
|
|||
ShowOnly(e, threshold * 8f, Guns(e)[0]);
|
||||
Assert.True(e.Visibility * o.PassiveInfo(e.Position.xz, 1f) == threshold, "fixture: the ship's light at removal is exactly the gate");
|
||||
|
||||
_zone.Entities.Remove(e);
|
||||
_zone.Remove(e);
|
||||
Tick(.1f);
|
||||
|
||||
Assert.Single(o.RevealsOf(e));
|
||||
|
|
@ -679,7 +678,7 @@ public sealed class ContactRevealTests : IDisposable
|
|||
ShowOnly(e, 0f, Guns(e)[0]);
|
||||
e.SetSignature(new object(), SignatureChannel.Emission, 1000f, Guns(e)[0]);
|
||||
|
||||
_zone.Entities.Remove(e);
|
||||
_zone.Remove(e);
|
||||
Tick(.1f);
|
||||
|
||||
Assert.Single(o.RevealsOf(e));
|
||||
|
|
@ -696,8 +695,9 @@ public sealed class ContactRevealTests : IDisposable
|
|||
Tick(1f); // the gun cools down
|
||||
ShowOnly(e, 1000f, gun); // bright enough for any gate
|
||||
|
||||
_zone.Entities.Remove(e);
|
||||
_zone.Entities.Add(e);
|
||||
_zone.Remove(e);
|
||||
e.Deactivate();
|
||||
_zone.Admit(e, piloted: false);
|
||||
Tick(.1f);
|
||||
|
||||
Assert.Equal(at, Assert.Single(o.RevealsOf(e)).Time);
|
||||
|
|
@ -813,7 +813,7 @@ public sealed class ContactRevealTests : IDisposable
|
|||
var first = Assert.Single(o.RevealsOf(locker)); // fixture: the lock paints every tick, and the first one is recorded
|
||||
Assert.True(locker.Visibility * o.PassiveInfo(locker.Position.xz, 1f) <= _items.GameplaySettings.TargetDetectionInfoThreshold, "fixture: its departure is below o's gate");
|
||||
|
||||
_zone.Entities.Remove(locker); // dies, warps or docks while its latest paint is still in o's inbox
|
||||
_zone.Remove(locker); // dies, warps or docks while its latest paint is still in o's inbox
|
||||
var inboxed = ((System.Collections.IList) typeof(Entity).GetField("_paintInbox", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(o)).Count;
|
||||
Assert.True(inboxed > 0 || o.IncomingPaints.Count > 0, "fixture: a paint is in flight");
|
||||
Tick(.1f);
|
||||
|
|
@ -865,7 +865,7 @@ public sealed class ContactRevealTests : IDisposable
|
|||
for (var tick = 0; tick < 100 && inbox.Count == 0; tick++) Tick(.1f);
|
||||
Assert.NotEmpty(inbox); // fixture: the ping crossed o and its paint waits to be published
|
||||
|
||||
_zone.Entities.Remove(p);
|
||||
_zone.Remove(p);
|
||||
Tick(.1f);
|
||||
|
||||
Assert.Single(o.IncomingPaints);
|
||||
|
|
@ -1039,7 +1039,7 @@ public sealed class ContactRevealTests : IDisposable
|
|||
ShowOnly(e, 1f, gun);
|
||||
var threshold = _items.GameplaySettings.TargetDetectionInfoThreshold;
|
||||
Assert.True(e.Visibility * o.PassiveInfo(e.Position.xz, 1f) <= threshold, "fixture: from where o is, the departure is below the gate");
|
||||
_zone.Entities.Remove(e);
|
||||
_zone.Remove(e);
|
||||
Tick(.1f);
|
||||
Assert.Single(o.RevealsOf(e));
|
||||
|
||||
|
|
@ -1069,8 +1069,8 @@ public sealed class ContactRevealTests : IDisposable
|
|||
var threshold = _items.GameplaySettings.TargetDetectionInfoThreshold;
|
||||
Assert.True(a.Visibility * o.PassiveInfo(a.Position.xz, 1f) > threshold && b.Visibility * o.PassiveInfo(b.Position.xz, 1f) > threshold, "fixture: both departures pass the gate");
|
||||
|
||||
_zone.Entities.Remove(firstThenSecond ? a : b);
|
||||
_zone.Entities.Remove(firstThenSecond ? b : a);
|
||||
_zone.Remove(firstThenSecond ? a : b);
|
||||
_zone.Remove(firstThenSecond ? b : a);
|
||||
Tick(.1f);
|
||||
|
||||
Assert.Empty(o.RevealsOf(a));
|
||||
|
|
@ -1096,7 +1096,7 @@ public sealed class ContactRevealTests : IDisposable
|
|||
Tick(.01f);
|
||||
Assert.Same(guns[1], Assert.Single(o.RevealsOf(e)).Item); // fixture: one gun ended as destroyed, one waiting
|
||||
e.Deactivate(); // the warp path: the ship's own subscriptions to the observer go with it
|
||||
_zone.Entities.Remove(e);
|
||||
_zone.Remove(e);
|
||||
for (var i = 0; i < 40; i++) Tick(.1f); // past BearingMemory (2 s)
|
||||
Assert.Equal(0, RevealBookSubjects(o)); // fixture: pruned
|
||||
|
||||
|
|
@ -1179,7 +1179,7 @@ public sealed class ContactRevealTests : IDisposable
|
|||
Assert.Contains(identified.VisibilitySources.Values, v => v.Item == gunItems[0] && v.Strength > 0f);
|
||||
var departed = _zone.Entities.Single(x => x.Equipment.Contains(gunItems[18]));
|
||||
foreach (var source in departed.VisibilitySources.Keys.ToArray()) departed.SetSignature(source, SignatureChannel.Reflection, 0f, gunItems[18]);
|
||||
_zone.Entities.Remove(departed);
|
||||
_zone.Remove(departed);
|
||||
Assert.Single((System.Collections.IList) typeof(Zone).GetProperty("Departures", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(_zone));
|
||||
sense();
|
||||
Assert.Single(o.RevealsOf(departed)); // fixture: awaiting its prune
|
||||
|
|
|
|||
|
|
@ -64,8 +64,7 @@ public sealed class ContactTests : IDisposable
|
|||
{
|
||||
var hullRef = Items.ItemData.RefOf<ItemData>(Items.ItemData.GetByName<HullData>(hull));
|
||||
var ship = new Ship(Items, Zone, new EquippableItem { Data = hullRef, Durability = 1000000, Lot = Lot++ }, new EntitySettings());
|
||||
Zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
Zone.Admit(ship, piloted: false);
|
||||
ship.Position = at;
|
||||
ship.Velocity = velocity;
|
||||
return ship;
|
||||
|
|
|
|||
|
|
@ -176,10 +176,8 @@ public sealed class FireAuthorityTests : IDisposable
|
|||
// extra gear a test needs (a second aimable item, a shield, a reactor) must go on before Activate().
|
||||
beforeActivate?.Invoke(items, shooter, target);
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = float3(0, 0, targetRange);
|
||||
|
|
@ -327,7 +325,7 @@ public sealed class FireAuthorityTests : IDisposable
|
|||
FireControl.Fire(e.Weapon, e.WeaponItem, e.Shooter);
|
||||
var before = e.Target.Hull.Durability;
|
||||
|
||||
e.Zone.Entities.Remove(e.Target);
|
||||
e.Zone.Remove(e.Target);
|
||||
e.Zone.Update(10f); // well past arrival
|
||||
|
||||
Assert.Equal(before, e.Target.Hull.Durability);
|
||||
|
|
|
|||
|
|
@ -177,10 +177,8 @@ public sealed class FireControlCut11Tests : IDisposable
|
|||
Assert.True(target.TryEquip(shield, spos));
|
||||
}
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3(37, 0, -11);
|
||||
target.Position = float3(37, 0, 89); // 100 units straight ahead of the shooter
|
||||
|
|
@ -280,7 +278,7 @@ public sealed class FireControlCut11Tests : IDisposable
|
|||
// What the HUD's arrival countdown reads: time left in flight at commit, 5 s - 4.7 s.
|
||||
Assert.Equal(.3f, committed.ArrivalIn, 3);
|
||||
|
||||
e.Zone.Entities.Remove(e.Target); // the target is gone before the shot arrives
|
||||
e.Zone.Remove(e.Target); // the target is gone before the shot arrives
|
||||
e.Zone.Update(1f);
|
||||
|
||||
Assert.NotNull(resolved);
|
||||
|
|
@ -307,7 +305,7 @@ public sealed class FireControlCut11Tests : IDisposable
|
|||
e.Zone.Update(1f);
|
||||
SafeAssert.Empty(committed);
|
||||
|
||||
e.Zone.Entities.Remove(e.Target);
|
||||
e.Zone.Remove(e.Target);
|
||||
e.Zone.Update(1f);
|
||||
|
||||
foreach (var outcome in new[] { SafeAssert.Only(committed), SafeAssert.Only(resolved) })
|
||||
|
|
|
|||
|
|
@ -224,10 +224,8 @@ public sealed class FireControlCut123Tests : IDisposable
|
|||
cockpit = target.Equipment.Single(x => x.EquippableItem == ci);
|
||||
}
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
// Cut 11.3: the shooter sits off the origin.
|
||||
shooter.Position = float3(37, 0, -11);
|
||||
|
|
|
|||
|
|
@ -242,10 +242,8 @@ public sealed partial class FireControlCut124Tests : IDisposable
|
|||
Assert.True(target.TryEquip(shield, spos));
|
||||
}
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
// Off-origin (the fixture rule): the shooter sits away from the world origin, and the target sits
|
||||
// `targetRange` ahead of it along the shooter's own +z, so a stationary target's TravelDirection is
|
||||
|
|
@ -515,8 +513,7 @@ public sealed partial class FireControlCut124Tests : IDisposable
|
|||
Data = e.Items.ItemData.RefOf<ItemData>(e.Items.ItemData.GetByName<GearData>("Marker")), Durability = 1000000, Lot = lot + 1
|
||||
};
|
||||
Assert.True(ship.TryEquip(marker, new int2(1, 1)));
|
||||
e.Zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
e.Zone.Admit(ship, piloted: false);
|
||||
ship.Position = position;
|
||||
return ship;
|
||||
}
|
||||
|
|
@ -1465,7 +1462,7 @@ public sealed partial class FireControlCut124Tests : IDisposable
|
|||
FireControl.Fire(e.Weapon, e.WeaponItem, e.Shooter);
|
||||
Assert.True(SafeAssert.OnlyShot(e.Zone).Target == e.Target, "fixture: the pushed-out round keeps its target");
|
||||
e.Zone.Update(.01f);
|
||||
e.Zone.Entities.Remove(e.Target);
|
||||
e.Zone.Remove(e.Target);
|
||||
e.Zone.Update(3f);
|
||||
|
||||
SafeAssert.NoShots(e.Zone);
|
||||
|
|
@ -1510,7 +1507,7 @@ public sealed partial class FireControlCut124Tests : IDisposable
|
|||
var e = Build(TestSettings(), SolidShape(5, 4), velocity: 20f, fuse: WeaponFuse.Proximity, blastRadius: 4f, damage: 100f);
|
||||
FireControl.Fire(e.Weapon, e.WeaponItem, e.Shooter);
|
||||
using var c = e.Zone.ShotCommitted.Subscribe(_ => throw new InvalidOperationException("observer"));
|
||||
e.Zone.Entities.Remove(e.Target);
|
||||
e.Zone.Remove(e.Target);
|
||||
|
||||
Assert.ThrowsAny<Exception>(() => e.Zone.Update(.01f));
|
||||
var second = Record.Exception(() => e.Zone.Update(.01f));
|
||||
|
|
@ -2004,7 +2001,7 @@ public sealed partial class FireControlCut124Tests : IDisposable
|
|||
|
||||
FireControl.Fire(e.Weapon, e.WeaponItem, e.Shooter);
|
||||
for (var i = 0; i < updatesBeforeLeaving; i++) e.Zone.Update(.01f);
|
||||
e.Zone.Entities.Remove(e.Shooter);
|
||||
e.Zone.Remove(e.Shooter);
|
||||
e.Zone.Update(4f);
|
||||
|
||||
SafeAssert.NoShots(e.Zone);
|
||||
|
|
@ -2022,7 +2019,7 @@ public sealed partial class FireControlCut124Tests : IDisposable
|
|||
|
||||
FireControl.Fire(e.Weapon, e.WeaponItem, e.Shooter);
|
||||
e.Zone.Update(.01f);
|
||||
e.Zone.Entities.Remove(e.Shooter);
|
||||
e.Zone.Remove(e.Shooter);
|
||||
e.Zone.Update(6f);
|
||||
|
||||
SafeAssert.NoShots(e.Zone);
|
||||
|
|
@ -2044,7 +2041,7 @@ public sealed partial class FireControlCut124Tests : IDisposable
|
|||
|
||||
for (var i = 0; i < 12; i++) FireControl.Fire(e.Weapon, e.WeaponItem, e.Shooter);
|
||||
e.Zone.Update(.01f);
|
||||
e.Zone.Entities.Remove(e.Shooter);
|
||||
e.Zone.Remove(e.Shooter);
|
||||
e.Zone.Update(6f);
|
||||
|
||||
SafeAssert.NoShots(e.Zone);
|
||||
|
|
@ -2274,7 +2271,7 @@ public sealed partial class FireControlCut124Tests : IDisposable
|
|||
FireControl.Fire(e.Weapon, e.WeaponItem, e.Shooter);
|
||||
e.Zone.Update(committedFirst ? 4.6f : .01f);
|
||||
Assert.Equal(committedFirst, e.Zone.PendingShots[0].Committed);
|
||||
e.Zone.Entities.Remove(e.Target);
|
||||
e.Zone.Remove(e.Target);
|
||||
e.Zone.Update(6f);
|
||||
|
||||
foreach (var s in subscriptions) s.Dispose();
|
||||
|
|
|
|||
|
|
@ -269,10 +269,8 @@ public sealed class FireControlCut12Tests : IDisposable
|
|||
Assert.True(target.TryEquip(shield, spos));
|
||||
}
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
// Cut 11.3: the shooter sits off the origin -- a shooter at float3.zero makes `target - source` and
|
||||
// `target + source` the same vector, hiding a whole class of sign mutants.
|
||||
|
|
|
|||
|
|
@ -154,10 +154,8 @@ public sealed class FireControlCut4Tests : IDisposable
|
|||
var targetGun = new EquippableItem { Data = targetGunRef, Durability = 1, Lot = 9 };
|
||||
Assert.True(target.TryEquip(targetGun, new int2(0, 0)));
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = float3(0, 0, targetRange);
|
||||
|
|
@ -233,8 +231,7 @@ public sealed class FireControlCut4Tests : IDisposable
|
|||
Assert.True(shooter.TryEquip(gun, new int2(0, 0)));
|
||||
var weapon = (InstantWeapon) shooter.Equipment.Single(e => e.EquippableItem == gun).Behaviors.Single(b => b is Weapon);
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
shooter.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Update(0f); // warm up resolved stats
|
||||
|
||||
// No target, no OnFire subscriber -- exactly a headless shooter with no target and no presentation.
|
||||
|
|
@ -276,7 +273,7 @@ public sealed class FireControlCut4Tests : IDisposable
|
|||
var ship = new Ship(items, zone, hull, new EntitySettings());
|
||||
ship.Position = position;
|
||||
ship.Direction = direction;
|
||||
zone.Entities.Add(ship);
|
||||
zone.Admit(ship, piloted: false);
|
||||
return ship;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -184,10 +184,8 @@ public sealed class FireControlCut5Tests : IDisposable
|
|||
|
||||
beforeActivate?.Invoke(items, shooter, target);
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = float3(0, 0, targetRange);
|
||||
|
|
@ -339,10 +337,8 @@ public sealed class FireControlCut5Tests : IDisposable
|
|||
var targetGun = new EquippableItem { Data = targetGunRef, Durability = 1, Lot = 5 };
|
||||
Assert.True(target.TryEquip(targetGun, new int2(0, 0)));
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = targetPosition;
|
||||
|
|
@ -427,10 +423,8 @@ public sealed class FireControlCut5Tests : IDisposable
|
|||
var targetGun = new EquippableItem { Data = gunRef, Durability = 1, Lot = 4 };
|
||||
Assert.True(target.TryEquip(targetGun, new int2(0, 0)));
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
return (items, shooter, target, weaponItem);
|
||||
}
|
||||
|
|
@ -576,8 +570,7 @@ public sealed class FireControlCut5Tests : IDisposable
|
|||
Assert.True(target.TryEquip(medium, mediumPos));
|
||||
var mediumItem = target.Equipment.Single(e => e.EquippableItem == medium);
|
||||
|
||||
zone.Entities.Add(target);
|
||||
target.Activate();
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
return (items, target, bigItem, mediumItem);
|
||||
}
|
||||
|
|
@ -592,8 +585,7 @@ public sealed class FireControlCut5Tests : IDisposable
|
|||
var (items, target, big, medium) = BuildRevealTarget(settings);
|
||||
var observerHullRef = items.ItemData.RefOf<ItemData>(items.ItemData.GetByName<HullData>("Hull"));
|
||||
var observer = new Ship(items, target.Zone, new EquippableItem { Data = observerHullRef, Durability = 1000, Lot = 1 }, new EntitySettings());
|
||||
target.Zone.Entities.Add(observer);
|
||||
observer.Activate();
|
||||
target.Zone.Admit(observer, piloted: false);
|
||||
observer.SetTarget(target);
|
||||
|
||||
// With both items alive: Big (index 0) reveals at .2, Medium (index 1) at .8.
|
||||
|
|
|
|||
|
|
@ -156,10 +156,8 @@ public sealed class FireControlCut6Tests : IDisposable
|
|||
var targetGun = new EquippableItem { Data = targetGunRef, Durability = 1, Lot = 5 };
|
||||
Assert.True(target.TryEquip(targetGun, new int2(0, 0)));
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = float3(0, 0, targetRange);
|
||||
|
|
|
|||
|
|
@ -138,10 +138,8 @@ public sealed class FireControlCut6cTests : IDisposable
|
|||
var targetGun = new EquippableItem { Data = targetGunRef, Durability = 1, Lot = 9 };
|
||||
Assert.True(target.TryEquip(targetGun, new int2(0, 0)));
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = float3(0, 0, targetRange);
|
||||
|
|
@ -267,8 +265,7 @@ public sealed class FireControlCut6cTests : IDisposable
|
|||
|
||||
var zone = new Zone(items, new PlanetSettings(), new ZonePack(), new GalaxyZone { Name = "Test", Owner = null }, null);
|
||||
var entity = EntitySerializer.Unpack(items, zone, pack);
|
||||
zone.Entities.Add(entity);
|
||||
entity.Activate();
|
||||
zone.Admit(entity, piloted: false);
|
||||
zone.Update(0f);
|
||||
|
||||
Assert.Null(entity.GetBehavior<TargetingSystem>());
|
||||
|
|
|
|||
|
|
@ -156,10 +156,8 @@ public sealed class FireControlCut6dTests : IDisposable
|
|||
Assert.True(target.TryEquip(marker, markerCell.Value));
|
||||
}
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = float3(0, 0, targetRange);
|
||||
|
|
|
|||
|
|
@ -133,10 +133,8 @@ public sealed class FireControlCut7Tests : IDisposable
|
|||
|
||||
beforeActivate?.Invoke(items, shooter, target);
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = float3(0, 0, 100);
|
||||
|
|
|
|||
|
|
@ -129,10 +129,8 @@ public sealed class FireControlCut9Tests : IDisposable
|
|||
Assert.True(target.TryFindSpace(targetMarker, out var targetMarkerPos));
|
||||
Assert.True(target.TryEquip(targetMarker, targetMarkerPos));
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = float3(0, 0, 100);
|
||||
|
|
@ -340,10 +338,8 @@ public sealed class FireControlCut9Tests : IDisposable
|
|||
var targetGun = new EquippableItem { Data = targetGunRef, Durability = 1, Lot = 5 };
|
||||
Assert.True(target.TryEquip(targetGun, new int2(0, 0)));
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = float3(0, 0, 100);
|
||||
|
|
@ -567,10 +563,8 @@ public sealed class FireControlCut9Tests : IDisposable
|
|||
var bystanderGun = new EquippableItem { Data = bystanderGunRef, Durability = 1, Lot = 4 };
|
||||
Assert.True(bystander.TryEquip(bystanderGun, new int2(0, 0))); // _orderedEquipment must not be empty/null for Update to run
|
||||
|
||||
zone.Entities.Add(locker);
|
||||
zone.Entities.Add(bystander);
|
||||
locker.Activate();
|
||||
bystander.Activate();
|
||||
zone.Admit(locker, piloted: false);
|
||||
zone.Admit(bystander, piloted: false);
|
||||
|
||||
locker.Position = float3.zero;
|
||||
bystander.Position = float3(0, 0, 100);
|
||||
|
|
@ -603,7 +597,7 @@ public sealed class FireControlCut9Tests : IDisposable
|
|||
// the target dies or leaves while the locker is inactive -- its own Zone.Entities.ObserveRemove
|
||||
// subscription (the only thing that would have nulled Target) was disposed by Deactivate above, so
|
||||
// nothing reacts to this removal yet.
|
||||
s.Zone.Entities.Remove(s.Bystander);
|
||||
s.Zone.Remove(s.Bystander);
|
||||
|
||||
s.Locker.Activate(); // undock
|
||||
Assert.True(s.Locker.Active);
|
||||
|
|
|
|||
|
|
@ -238,10 +238,8 @@ public sealed class FireControlPerWeaponTests : IDisposable
|
|||
var target = new Ship(items, zone, new EquippableItem { Data = hullRef, Durability = 1000000, Lot = lot++ }, new EntitySettings());
|
||||
Assert.True(target.TryEquip(Make("Gun", lot++, 1), new int2(0, 0)));
|
||||
|
||||
zone.Entities.Add(shooter);
|
||||
zone.Entities.Add(target);
|
||||
shooter.Activate();
|
||||
target.Activate();
|
||||
zone.Admit(shooter, piloted: false);
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3(-53, 0, -101);
|
||||
target.Position = shooter.Position + float3(0, 0, targetRange);
|
||||
|
|
|
|||
|
|
@ -95,8 +95,7 @@ public sealed class FireControlTests : IDisposable
|
|||
weapons[i] = ship.Equipment.Single(e => e.EquippableItem == gun);
|
||||
}
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
|
||||
return (items, ship, weapons);
|
||||
}
|
||||
|
|
@ -195,8 +194,7 @@ public sealed class FireControlTests : IDisposable
|
|||
// gun so the target is a real, ordinarily-constructed ship rather than a workaround-shaped one.
|
||||
var targetGunRef = items.ItemData.RefOf<ItemData>(items.ItemData.GetByName<GearData>("Gun"));
|
||||
Assert.True(target.TryEquip(new EquippableItem { Data = targetGunRef, Durability = 1, Lot = 1 }, new int2(0, 0)));
|
||||
zone.Entities.Add(target);
|
||||
target.Activate();
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
shooter.Position = float3.zero;
|
||||
target.Position = targetPosition;
|
||||
|
|
|
|||
|
|
@ -239,9 +239,8 @@ public sealed class FloatingBodyTests : IDisposable
|
|||
Lot = items.Lots.Add(new Lot { Design = cache.RefOf<ItemData>(design), Origin = new Attributed(), Quality = .5f, Roles = new List<RoleFill>() })
|
||||
};
|
||||
var ship = new Ship(items, zone, hull, new EntitySettings());
|
||||
zone.Entities.Add(ship);
|
||||
zone.Admit(ship, piloted: false);
|
||||
ship.Aim = float3(0, 0, 1);
|
||||
ship.Activate();
|
||||
ship.Position = float3(x, 0, 0);
|
||||
return ship;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,8 +82,7 @@ public sealed class IffAndCombatTests : IDisposable
|
|||
Assert.True(ship.TryEquip(gun, HardpointCell));
|
||||
}
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
return ship;
|
||||
}
|
||||
|
||||
|
|
@ -132,8 +131,9 @@ public sealed class IffAndCombatTests : IDisposable
|
|||
a.SetIff(b, true);
|
||||
Assert.True(a.EntityHostility[b]);
|
||||
|
||||
zone.Entities.Remove(b); // b leaves the zone: a's override on b must be cleared
|
||||
zone.Entities.Add(b); // b returns as a "new" contact from a's perspective
|
||||
zone.Remove(b); // b leaves the zone: a's override on b must be cleared
|
||||
b.Deactivate();
|
||||
zone.Admit(b, piloted: false); // b returns as a "new" contact from a's perspective
|
||||
|
||||
Assert.False(a.EntityHostility[b]); // recomputed fresh from the derived (non-hostile) rule
|
||||
Assert.False(a.IsHostileTo(b));
|
||||
|
|
|
|||
|
|
@ -33,8 +33,7 @@ public sealed partial class FireControlCut124Tests
|
|||
{
|
||||
var hullRef = e.Items.ItemData.RefOf<ItemData>(e.Items.ItemData.GetByName<HullData>("Hull"));
|
||||
var ship = new Ship(e.Items, e.Zone, new EquippableItem { Data = hullRef, Durability = 100000, Lot = lot }, new EntitySettings());
|
||||
e.Zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
e.Zone.Admit(ship, piloted: false);
|
||||
ship.Position = position;
|
||||
return ship;
|
||||
}
|
||||
|
|
@ -135,7 +134,7 @@ public sealed partial class FireControlCut124Tests
|
|||
var gone = Build(TestSettings(), SolidShape(5, 4), velocity: 50f, damage: 100f);
|
||||
var goneAbsorbs = new[] { gone.Target, gone.Shooter }.SelectMany(x => Collect(x, subs)).ToList();
|
||||
FireControl.Fire(gone.Weapon, gone.WeaponItem, gone.Shooter);
|
||||
gone.Zone.Entities.Remove(gone.Target);
|
||||
gone.Zone.Remove(gone.Target);
|
||||
for (var i = 0; i < 40; i++) gone.Zone.Update(.1f);
|
||||
foreach (var s in subs) s.Dispose();
|
||||
|
||||
|
|
|
|||
|
|
@ -97,8 +97,7 @@ public sealed class InputCapacitorTests : IDisposable
|
|||
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, reactorData)));
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
|
||||
return (ship, items, ship.GetBehavior<Reactor>());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,8 +105,7 @@ public sealed class LockWarningTests : IDisposable
|
|||
var gearRef = _items.ItemData.RefOf<ItemData>(_items.ItemData.GetByName<GearData>(launcher));
|
||||
Assert.True(ship.TryEquip(new EquippableItem { Data = gearRef, Durability = 1, Lot = _nextLot++ }, HardpointCell));
|
||||
}
|
||||
_zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
_zone.Admit(ship, piloted: false);
|
||||
ship.Position = position;
|
||||
_zone.Update(0f); // brings the gear online
|
||||
return ship;
|
||||
|
|
|
|||
|
|
@ -133,8 +133,7 @@ public sealed class LootTests : IDisposable
|
|||
Assert.True(ship.CargoBays.Single().TryStore(lab.Ore(7)));
|
||||
Assert.True(ship.CargoBays.Single().TryStore(lab.Make("Spare")));
|
||||
}
|
||||
lab.Zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
lab.Zone.Admit(ship, piloted: false);
|
||||
ship.Position = at;
|
||||
return ship;
|
||||
}
|
||||
|
|
@ -153,8 +152,7 @@ public sealed class LootTests : IDisposable
|
|||
Assert.True(station.TryEquip(spare, pos));
|
||||
Assert.True(station.CargoBays.Single().TryStore(lab.Ore(3)));
|
||||
}
|
||||
lab.Zone.Entities.Add(station);
|
||||
station.Activate();
|
||||
lab.Zone.Admit(station, piloted: false);
|
||||
station.Position = at;
|
||||
return station;
|
||||
}
|
||||
|
|
@ -393,10 +391,10 @@ public sealed class LootTests : IDisposable
|
|||
var lab = Build(probability: 1f);
|
||||
var ship = Spawn(lab, float3(3, 0, 4));
|
||||
// What Entity.TryDock then Entity.TryUndock do to the zone: Entities.Remove + Deactivate, then Zone.Admit.
|
||||
lab.Zone.Entities.Remove(ship);
|
||||
lab.Zone.Remove(ship);
|
||||
ship.Deactivate();
|
||||
lab.Zone.Admit(ship, piloted: false);
|
||||
lab.Zone.Entities.Remove(ship);
|
||||
lab.Zone.Remove(ship);
|
||||
lab.Zone.Admit(ship, piloted: false);
|
||||
Kill(ship);
|
||||
var dropped = lab.Zone.FloatingItems.Select(f => f.Item).ToList();
|
||||
|
|
@ -412,7 +410,7 @@ public sealed class LootTests : IDisposable
|
|||
var ship = Spawn(lab, float3(3, 0, 4));
|
||||
// What ActionGameManager does on a jump.
|
||||
ship.Deactivate();
|
||||
lab.Zone.Entities.Remove(ship);
|
||||
lab.Zone.Remove(ship);
|
||||
ship.Zone = other;
|
||||
other.Admit(ship, piloted: false);
|
||||
Kill(ship);
|
||||
|
|
@ -509,9 +507,10 @@ public sealed class LootTests : IDisposable
|
|||
Assert.True(lab.Zone.FloatingItems.Select(f => Math.Round(Math.Atan2(f.Body.Velocity.y, f.Body.Velocity.x), 3)).Distinct().Count() > 1);
|
||||
}
|
||||
|
||||
// A member added twice holds one death subscription, so it drops each item once, as a single death would.
|
||||
// Membership has one owner (aetheria-release zone-entities-encapsulation): a second admission of a member is
|
||||
// refused, so it never holds a second death watch and the zone never lists it twice.
|
||||
[Fact]
|
||||
public void AMemberAddedTwiceStillDropsEachItemOnce()
|
||||
public void AMemberAdmittedTwiceIsRefusedAndStillDropsEachItemOnce()
|
||||
{
|
||||
var once = Build("Once", probability: 1f);
|
||||
Kill(Spawn(once, float3(3, 0, 4)));
|
||||
|
|
@ -520,20 +519,45 @@ public sealed class LootTests : IDisposable
|
|||
|
||||
var lab = Build("Twice", probability: 1f);
|
||||
var ship = Spawn(lab, float3(3, 0, 4));
|
||||
lab.Zone.Entities.Add(ship);
|
||||
Assert.Throws<InvalidOperationException>(() => lab.Zone.Admit(ship, piloted: false));
|
||||
Assert.Equal(1, lab.Zone.Entities.Count(e => e == ship));
|
||||
Kill(ship);
|
||||
Assert.Equal(baseline, lab.Zone.FloatingItems.Count);
|
||||
}
|
||||
|
||||
// Clearing Entities removes every member, so a death afterwards drops nothing in this zone.
|
||||
// Remove is the one removal: a non-member is refused with nothing changed, and a member's removal takes its death
|
||||
// watch with it, so a death signal afterwards drops nothing and a re-admission watches exactly once.
|
||||
[Fact]
|
||||
public void AClearedZoneDropsNothingWhenAFormerMemberDies()
|
||||
public void RemovingANonMemberChangesNothingAndARemovedMembersWatchGoesWithIt()
|
||||
{
|
||||
var lab = Build("Cleared", probability: 1f);
|
||||
var lab = Build(probability: 1f);
|
||||
var ship = Spawn(lab, float3(3, 0, 4));
|
||||
lab.Zone.Entities.Clear();
|
||||
var stranger = Spawn(Build("Elsewhere", probability: 1f), float3(1, 0, 1));
|
||||
var members = lab.Zone.Entities.Count;
|
||||
Assert.False(lab.Zone.Remove(stranger));
|
||||
Assert.Equal(members, lab.Zone.Entities.Count);
|
||||
|
||||
Assert.True(lab.Zone.Remove(ship));
|
||||
Assert.False(lab.Zone.Remove(ship));
|
||||
Kill(ship);
|
||||
Assert.Empty(lab.Zone.FloatingItems);
|
||||
|
||||
ship.Deactivate();
|
||||
lab.Zone.Admit(ship, piloted: false);
|
||||
Kill(ship);
|
||||
var dropped = lab.Zone.FloatingItems.Select(f => f.Item).ToList();
|
||||
Assert.NotEmpty(dropped);
|
||||
Assert.Equal(dropped.Count, dropped.Distinct().Count());
|
||||
}
|
||||
|
||||
// The membership view has no mutating member: a caller can read it and subscribe to it, and nothing else.
|
||||
[Fact]
|
||||
public void TheMembershipViewOffersNoWayToMutateMembership()
|
||||
{
|
||||
var lab = Build();
|
||||
Spawn(lab, float3(3, 0, 4));
|
||||
Assert.IsNotAssignableFrom<System.Collections.Generic.ICollection<Entity>>(lab.Zone.Entities);
|
||||
Assert.IsNotAssignableFrom<System.Collections.IList>(lab.Zone.Entities);
|
||||
}
|
||||
|
||||
// The pickup is whole or nothing at the arithmetic boundary: two stacks of 90 hold 20 more in all and no free
|
||||
|
|
|
|||
|
|
@ -130,8 +130,7 @@ public sealed class MineTests : IDisposable
|
|||
var targeting = lab.Make("Targeting", 1);
|
||||
Assert.True(layer.TryFindSpace(targeting, out var tpos));
|
||||
Assert.True(layer.TryEquip(targeting, tpos));
|
||||
zone.Entities.Add(layer);
|
||||
layer.Activate();
|
||||
zone.Admit(layer, piloted: false);
|
||||
layer.Position = layerAt ?? float3(-53, 0, -101);
|
||||
lab.Layer = layer;
|
||||
zone.Update(1f);
|
||||
|
|
@ -152,8 +151,7 @@ public sealed class MineTests : IDisposable
|
|||
Assert.True(ship.TryFindSpace(gear, out var spos));
|
||||
Assert.True(ship.TryEquip(gear, spos));
|
||||
}
|
||||
lab.Zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
lab.Zone.Admit(ship, piloted: false);
|
||||
ship.Position = at;
|
||||
if (shield != null) ship.Shield.Item.Enabled.Value = true;
|
||||
return ship;
|
||||
|
|
|
|||
|
|
@ -222,10 +222,9 @@ public sealed partial class MiningCut3Tests : IDisposable
|
|||
s.Cache.Upsert(gun);
|
||||
Assert.True(ship.TryEquip(Mint(s, gun)));
|
||||
}
|
||||
s.Zone.Entities.Add(ship);
|
||||
s.Zone.Admit(ship, piloted: false);
|
||||
Hold(ship, at);
|
||||
ship.Aim = float3(0, 0, 1);
|
||||
ship.Activate();
|
||||
return ship;
|
||||
}
|
||||
|
||||
|
|
@ -867,10 +866,9 @@ public sealed partial class MiningCut3Tests : IDisposable
|
|||
var ship = new Ship(s.Items, s.Zone, Mint(s, s.Hull), new EntitySettings());
|
||||
Assert.True(ship.TryEquip(Mint(s, s.Eye)));
|
||||
foreach (var weapon in weapons) Assert.True(ship.TryEquip(Mint(s, weapon)));
|
||||
s.Zone.Entities.Add(ship);
|
||||
s.Zone.Admit(ship, piloted: false);
|
||||
Hold(ship, eyeAt);
|
||||
ship.Aim = float3(0, 0, 1);
|
||||
ship.Activate();
|
||||
Tick((ship, eyeAt)); // weapons read their ranges when they run
|
||||
return ship;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,10 +54,9 @@ public sealed partial class MiningCut3Tests
|
|||
var ship = new Ship(s.Items, s.Zone, Mint(s, s.Hull), new EntitySettings());
|
||||
Assert.True(ship.TryEquip(Mint(s, eye)));
|
||||
Assert.True(ship.TryEquip(Mint(s, gun)));
|
||||
s.Zone.Entities.Add(ship);
|
||||
s.Zone.Admit(ship, piloted: false);
|
||||
Hold(ship, at);
|
||||
ship.Aim = float3(0, 0, 1);
|
||||
ship.Activate();
|
||||
Tick((ship, at)); // equipment comes online and weapons read their ranges on the first tick
|
||||
return ship;
|
||||
}
|
||||
|
|
@ -177,7 +176,7 @@ public sealed partial class MiningCut3Tests
|
|||
Assert.True(expected.ToHashSet().SetEquals(offered), $"sensitivity {sensitivity}: {expected.Count} visible, {offered.Count} offered");
|
||||
visibleSeen += expected.Count;
|
||||
if (examined < inReach.Count) pruned++;
|
||||
s.Zone.Entities.Remove(observer);
|
||||
s.Zone.Remove(observer);
|
||||
}
|
||||
Assert.True(visibleSeen > 50 && pruned > 4, $"degenerate: {visibleSeen} visible, {pruned} queries pruned");
|
||||
}
|
||||
|
|
@ -422,7 +421,7 @@ public sealed partial class MiningCut3Tests
|
|||
var brightest = all.Max(c => s.Zone.ChunkVisibility(c));
|
||||
var unit = Observer(s, float2(5000, 0), 350f, 1f, Falloff());
|
||||
var sensitivity = 120f / (brightest * unit.DetectionReachPerVisibility());
|
||||
s.Zone.Entities.Remove(unit);
|
||||
s.Zone.Remove(unit);
|
||||
|
||||
var offered = new List<ChunkId>();
|
||||
long examined = 0, inReach = 0;
|
||||
|
|
@ -437,7 +436,7 @@ public sealed partial class MiningCut3Tests
|
|||
observer.VisibleChunksInReach(offered);
|
||||
examined += s.Zone.Targets.Examined - before;
|
||||
inReach += all.Count(c => length(At(s, c) - at) <= 350f);
|
||||
s.Zone.Entities.Remove(observer);
|
||||
s.Zone.Remove(observer);
|
||||
}
|
||||
Assert.True(examined < .265 * inReach, $"examined {examined} of {inReach} rocks in reach: the visibility bound has loosened");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public sealed partial class MiningCut3Tests
|
|||
{
|
||||
var observer = Observer(s, at, reach, sensitivity, Falloff());
|
||||
if (inReach.Count(observer.ChunkVisible) >= share * inReach.Count) return (s, observer);
|
||||
s.Zone.Entities.Remove(observer);
|
||||
s.Zone.Remove(observer);
|
||||
}
|
||||
throw new InvalidOperationException("no sensitivity sees the rocks");
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ public sealed partial class MiningCut3Tests
|
|||
private static Ship Sight(Scene s, Ship observer, float2 at, bool enemy = false)
|
||||
{
|
||||
var ship = new Ship(s.Items, s.Zone, Mint(s, s.Hull), new EntitySettings());
|
||||
s.Zone.Entities.Add(ship);
|
||||
s.Zone.Admit(ship, piloted: false);
|
||||
Hold(ship, at);
|
||||
observer.VisibleEntities.Add(ship);
|
||||
if (enemy) observer.VisibleEnemies.Add(ship);
|
||||
|
|
@ -265,7 +265,7 @@ public sealed partial class MiningCut3Tests
|
|||
observer.Aim = float3(cos(heading), 0, sin(heading));
|
||||
Check($"the reticle at {heading}", Cost(observer.TargetUnderReticle, TargetRef.None), reticle);
|
||||
}
|
||||
s.Zone.Entities.Remove(observer);
|
||||
s.Zone.Remove(observer);
|
||||
}
|
||||
}
|
||||
Assert.True(failures.Count == 0, string.Join("; ", failures));
|
||||
|
|
@ -336,10 +336,9 @@ public sealed partial class MiningCut3Tests
|
|||
var ship = new Ship(s.Items, s.Zone, Mint(s, s.Hull), new EntitySettings());
|
||||
Assert.True(ship.TryEquip(Mint(s, s.Eye)));
|
||||
Assert.True(ship.TryEquip(Mint(s, launcher)));
|
||||
s.Zone.Entities.Add(ship);
|
||||
s.Zone.Admit(ship, piloted: false);
|
||||
Hold(ship, at);
|
||||
ship.Aim = float3(0, 0, 1);
|
||||
ship.Activate();
|
||||
Tick((ship, at));
|
||||
Assert.Contains(ship.Weapons, w => w.Range == 400f);
|
||||
Assert.True(AllChunks(s).Any(c => length(At(s, c) - at) <= 400f && ship.ChunkVisible(c)), "there are rocks within the launcher's range");
|
||||
|
|
@ -375,7 +374,7 @@ public sealed partial class MiningCut3Tests
|
|||
private static Ship Unseen(Scene s, float2 at)
|
||||
{
|
||||
var ship = new Ship(s.Items, s.Zone, Mint(s, s.Hull), new EntitySettings());
|
||||
s.Zone.Entities.Add(ship);
|
||||
s.Zone.Admit(ship, piloted: false);
|
||||
Hold(ship, at);
|
||||
return ship;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -266,8 +266,7 @@ public sealed class MunitionPartsTests : IDisposable
|
|||
Assert.True(ship.TryEquip(new EquippableItem { Data = cache.RefOf<ItemData>(gear), Durability = gear.Durability, Lot = 1 }, new int2(x, y)), name);
|
||||
}
|
||||
Assert.True(ship.TryEquip(new EquippableItem { Data = cache.RefOf<ItemData>(gt), Durability = gt.Durability, Lot = 2 }, hardpoint.Position));
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
// Settle at the item's optimal temperature, as ConditionRatioTests does, so heat and wear read their identity and only
|
||||
// the role's quality moves a stat.
|
||||
var item = ship.Equipment.Single(e => e.Data.Name == "GT 3K");
|
||||
|
|
|
|||
|
|
@ -125,8 +125,7 @@ public sealed class PaintModeTests : IDisposable
|
|||
var gearRef2 = _items.ItemData.RefOf<ItemData>(_items.ItemData.GetByName<GearData>(sensor2));
|
||||
Assert.True(ship.TryEquip(new EquippableItem { Data = gearRef2, Durability = 1, Lot = _nextLot++ }, new int2(1, 0)));
|
||||
}
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
ship.Position = position;
|
||||
ship.SetSignature(new object(), SignatureChannel.Reflection, 5f, null);
|
||||
zone.Update(0f); // brings the gear online
|
||||
|
|
@ -334,7 +333,7 @@ public sealed class PaintModeTests : IDisposable
|
|||
if (tick == 2 && illuminatorDies)
|
||||
{
|
||||
Assert.True(SensorOf(a).PingRadius < 100f, "fixture: A dies before its wavefront reaches T");
|
||||
_zone.Entities.Remove(a);
|
||||
_zone.Remove(a);
|
||||
}
|
||||
track.Add(Track(c, t));
|
||||
}
|
||||
|
|
@ -425,7 +424,7 @@ public sealed class PaintModeTests : IDisposable
|
|||
for (var tick = 1; tick <= 24; tick++)
|
||||
{
|
||||
Tick(.1f);
|
||||
if (tick == removePingerAfterTick) _zone.Entities.Remove(a);
|
||||
if (tick == removePingerAfterTick) _zone.Remove(a);
|
||||
Near(tick <= 3 ? 0f : .21213f, Track(c, t));
|
||||
Assert.Equal(tick == 4 ? 1 : 0, t.IncomingPaints.Count);
|
||||
}
|
||||
|
|
@ -686,7 +685,7 @@ public sealed class PaintModeTests : IDisposable
|
|||
SensorOf(a).Ping();
|
||||
for (var tick = 1; tick <= 3; tick++) Tick(.1f);
|
||||
Assert.True(SensorOf(a).PingRadius > 50f); // the front had passed 50 m by the move
|
||||
a.Deactivate(); _zone.Entities.Remove(a); a.Zone = z2; z2.Admit(a, false);
|
||||
a.Deactivate(); _zone.Remove(a); a.Zone = z2; z2.Admit(a, false);
|
||||
var paints = 0;
|
||||
for (var tick = 1; tick <= 24; tick++) { z2.Update(.1f); paints += near.IncomingPaints.Count + far.IncomingPaints.Count; }
|
||||
Assert.Equal(0, paints);
|
||||
|
|
@ -706,8 +705,8 @@ public sealed class PaintModeTests : IDisposable
|
|||
var paints = 0;
|
||||
for (var tick = 1; tick <= 30; tick++)
|
||||
{
|
||||
if (tick == 5) { _zone.Entities.Remove(t); t.Deactivate(); }
|
||||
if (tick == 8) { _zone.Entities.Add(t); t.Activate(); }
|
||||
if (tick == 5) { _zone.Remove(t); t.Deactivate(); }
|
||||
if (tick == 8) { _zone.Admit(t, piloted: false); }
|
||||
Tick(.1f);
|
||||
paints += t.IncomingPaints.Count;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,8 +101,7 @@ public sealed class PowerBusTests : IDisposable
|
|||
|
||||
if (reactorFirst) { EquipReactor(); EquipCapacitor(); } else { EquipCapacitor(); EquipReactor(); }
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
|
||||
var reactor = ship.GetBehavior<Reactor>();
|
||||
var capacitor = ship.GetBehavior<Capacitor>();
|
||||
|
|
|
|||
|
|
@ -103,8 +103,7 @@ public sealed class PowerCurveTests : IDisposable
|
|||
Assert.True(ship.TryEquip(Mint(cache, items, reactorData)));
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName<GearData>("Engine"))));
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
return ship;
|
||||
}
|
||||
|
||||
|
|
@ -408,9 +407,8 @@ public sealed class PowerCurveTests : IDisposable
|
|||
var ship = new Ship(items, zone, hullItem, new EntitySettings());
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName<GearData>("Drain")), HardpointCell));
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName<GearData>("Booster")), SecondHardpointCell));
|
||||
zone.Entities.Add(ship);
|
||||
|
||||
var error = Assert.Throws<InvalidOperationException>(() => ship.Activate());
|
||||
var error = Assert.Throws<InvalidOperationException>(() => zone.Admit(ship, piloted: false));
|
||||
Assert.Contains("Drain", error.Message); // the item whose request would be corrupted
|
||||
Assert.Contains("Booster", error.Message); // the item whose modifier would corrupt it
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,8 +72,7 @@ public sealed class PowerTierTests : IDisposable
|
|||
((ReactorData) reactorData.Behaviors[0]).Charge = Constant(reactorCharge);
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, reactorData)));
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
|
||||
var reactor = ship.GetBehavior<Reactor>();
|
||||
ship.Update(0f);
|
||||
|
|
|
|||
|
|
@ -314,9 +314,8 @@ public sealed class RestoredHullsTests
|
|||
Assert.True(ship.TryEquip(fillerItem, filled.Position));
|
||||
}
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
zone.Admit(ship, piloted: false);
|
||||
ship.Position = float3.zero;
|
||||
ship.Activate();
|
||||
zone.Update(0f); // warm-up: resolves equipment stats before the driven ticks read them
|
||||
return ship;
|
||||
}
|
||||
|
|
@ -618,9 +617,8 @@ public sealed class RestoredHullsTests
|
|||
var zone = new Zone(items, new PlanetSettings(), new ZonePack(), new GalaxyZone { Name = "Test", Owner = null }, null);
|
||||
var ship = (Ship) EntitySerializer.Unpack(items, zone, pack);
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
zone.Admit(ship, piloted: false);
|
||||
ship.Position = float3.zero;
|
||||
ship.Activate();
|
||||
zone.Update(0f);
|
||||
|
||||
ship.MovementDirection = float2(0, 1); // full forward
|
||||
|
|
|
|||
|
|
@ -95,8 +95,7 @@ public sealed class ShieldReserveTests : IDisposable
|
|||
var shieldData = cache.GetByName<GearData>("Shield");
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, shieldData)));
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
|
||||
return (ship, ship.GetBehavior<Shield>());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,8 +159,7 @@ public sealed class SignatureTests : IDisposable
|
|||
Assert.True(ship.TryEquip(item, at));
|
||||
}
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
ship.Aim = float3(0, 0, 1);
|
||||
zone.Update(1f);
|
||||
return rig;
|
||||
|
|
|
|||
|
|
@ -189,8 +189,7 @@ public sealed partial class FireControlCut124Tests
|
|||
var e = Build(TestSettings(), SolidShape(5, 4), hardpointArc: SolutionArc, weaponRange: 200f);
|
||||
var hull = e.Items.ItemData.RefOf<ItemData>(e.HullData);
|
||||
var decoy = new Ship(e.Items, e.Zone, new EquippableItem { Data = hull, Durability = 1000000, Lot = 400 }, new EntitySettings());
|
||||
e.Zone.Entities.Add(decoy);
|
||||
decoy.Activate();
|
||||
e.Zone.Admit(decoy, piloted: false);
|
||||
var at = radians(40f);
|
||||
decoy.Position = e.Shooter.Position + float3(sin(at), 0, cos(at)) * 100f;
|
||||
e.Shooter.VisibleEntities.Add(decoy);
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ public sealed class StatResolverTests : IDisposable
|
|||
Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName<GearData>("Battery")), HardpointCell));
|
||||
if (withBooster)
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName<GearData>("Booster")), SecondHardpointCell));
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
return ship;
|
||||
}
|
||||
|
||||
|
|
@ -287,9 +286,8 @@ public sealed class StatResolverTests : IDisposable
|
|||
var hullItem = Mint(cache, items, cache.GetByName<HullData>("Skiff"));
|
||||
var ship = new Ship(items, zone, hullItem, new EntitySettings());
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName<GearData>("Loop")), HardpointCell));
|
||||
zone.Entities.Add(ship);
|
||||
|
||||
var error = Assert.Throws<InvalidOperationException>(() => ship.Activate());
|
||||
var error = Assert.Throws<InvalidOperationException>(() => zone.Admit(ship, piloted: false));
|
||||
Assert.Contains(ship.Name, error.Message); // the entity, by name ("Skiff", HullData.Name)
|
||||
Assert.Contains(nameof(CapacitorData), error.Message);
|
||||
Assert.Contains(nameof(CapacitorData.Capacity), error.Message);
|
||||
|
|
@ -513,8 +511,7 @@ public sealed class StatResolverTests : IDisposable
|
|||
Assert.True(ship.TryEquip(boost, cells[i + 1]));
|
||||
}
|
||||
ship.OverrideShutdown = true;
|
||||
zone.Entities.Add(ship);
|
||||
ship.Activate();
|
||||
zone.Admit(ship, piloted: false);
|
||||
|
||||
Func<ConsumableItem> mint = null;
|
||||
if (consumable != null)
|
||||
|
|
|
|||
|
|
@ -147,8 +147,7 @@ public sealed class TargetingSystemTests : IDisposable
|
|||
targetingItem = target.Equipment.Single(e => e.EquippableItem == targeting);
|
||||
}
|
||||
|
||||
zone.Entities.Add(target);
|
||||
target.Activate();
|
||||
zone.Admit(target, piloted: false);
|
||||
|
||||
return (items, target, weapon, wideItem, narrowItem, targetingItem);
|
||||
}
|
||||
|
|
@ -160,8 +159,7 @@ public sealed class TargetingSystemTests : IDisposable
|
|||
var hullRef = items.ItemData.RefOf<ItemData>(items.ItemData.GetByName<HullData>("TestHull"));
|
||||
var hull = new EquippableItem { Data = hullRef, Durability = 1, Lot = 1 };
|
||||
var observer = new Ship(items, zone, hull, new EntitySettings());
|
||||
zone.Entities.Add(observer);
|
||||
observer.Activate();
|
||||
zone.Admit(observer, piloted: false);
|
||||
return observer;
|
||||
}
|
||||
|
||||
|
|
@ -351,9 +349,8 @@ public sealed class TargetingSystemTests : IDisposable
|
|||
targeting.Lot = items.Lots.Add(new Lot { Design = cache.RefOf<ItemData>(targetingData), Origin = new Attributed(), Quality = .5f, Roles = new System.Collections.Generic.List<RoleFill>() });
|
||||
Assert.True(ship.TryEquip(targeting));
|
||||
|
||||
zone.Entities.Add(ship);
|
||||
zone.Admit(ship, piloted: false);
|
||||
ship.Aim = float3(0, 0, 1);
|
||||
ship.Activate();
|
||||
return ship;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -105,9 +105,8 @@ public sealed class ThrottleLockTests : IDisposable
|
|||
}
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, gear, ItemRotation.CounterClockwise), new int2(1, 3)));
|
||||
Assert.True(ship.TryEquip(Mint(cache, items, gear, ItemRotation.Clockwise), new int2(3, 3)));
|
||||
zone.Entities.Add(ship);
|
||||
zone.Admit(ship, piloted: false);
|
||||
ship.Aim = float3(0, 0, 1);
|
||||
ship.Activate();
|
||||
// A thruster is live from its first performance update; the allocator reads that every tick.
|
||||
foreach (var item in ship.Equipment) item.UpdatePerformance();
|
||||
|
||||
|
|
|
|||
|
|
@ -130,10 +130,8 @@ public sealed class VapourCloudTests : IDisposable
|
|||
// Held off until a test pulls the trigger: the dump vents on its first run.
|
||||
lab.Dump.Enabled.Value = false;
|
||||
|
||||
zone.Entities.Add(lab.Observer);
|
||||
zone.Entities.Add(lab.Target);
|
||||
lab.Observer.Activate();
|
||||
lab.Target.Activate();
|
||||
zone.Admit(lab.Observer, piloted: false);
|
||||
zone.Admit(lab.Target, piloted: false);
|
||||
lab.Observer.Position = float3(0, 0, 0);
|
||||
lab.Observer.Aim = float3(0, 0, 1);
|
||||
lab.Target.Position = float3(0, 0, distance);
|
||||
|
|
|
|||
Loading…
Reference in a new issue