public HyposprayComponentState(FixedPoint2 curVolume, FixedPoint2 maxVolume) { CurVolume = curVolume; MaxVolume = maxVolume; }
/// <summary> /// Raised directed at a victim when someone has force fed them a drink. /// </summary> private void OnDrink(EntityUid uid, SharedBodyComponent body, DrinkEvent args) { if (args.Drink.Deleted) { return; } args.Drink.CancelToken = null; var transferAmount = FixedPoint2.Min(args.Drink.TransferAmount, args.DrinkSolution.DrainAvailable); var drained = _solutionContainerSystem.Drain(args.Drink.Owner, args.DrinkSolution, transferAmount); var forceDrink = uid != args.User; if (!_bodySystem.TryGetComponentsOnMechanisms <StomachComponent>(uid, out var stomachs, body)) { _popupSystem.PopupEntity( forceDrink ? Loc.GetString("drink-component-try-use-drink-cannot-drink-other") : Loc.GetString("drink-component-try-use-drink-had-enough"), uid, Filter.Entities(args.User)); if (EntityManager.HasComponent <RefillableSolutionComponent>(uid)) { _spillableSystem.SpillAt(args.User, drained, "PuddleSmear"); return; } _solutionContainerSystem.Refill(uid, args.DrinkSolution, drained); return; } var firstStomach = stomachs.FirstOrNull( stomach => _stomachSystem.CanTransferSolution((stomach.Comp).Owner, drained)); // All stomach are full or can't handle whatever solution we have. if (firstStomach == null) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough-other"), uid, Filter.Entities(args.User)); _spillableSystem.SpillAt(uid, drained, "PuddleSmear"); return; } if (forceDrink) { EntityManager.TryGetComponent(uid, out MetaDataComponent? targetMeta); var targetName = targetMeta?.EntityName ?? string.Empty; EntityManager.TryGetComponent(args.User, out MetaDataComponent? userMeta); var userName = userMeta?.EntityName ?? string.Empty; _popupSystem.PopupEntity( Loc.GetString("drink-component-force-feed-success", ("user", userName)), uid, Filter.Entities(uid)); _popupSystem.PopupEntity( Loc.GetString("drink-component-force-feed-success-user", ("target", targetName)), args.User, Filter.Entities(args.User)); } else { _popupSystem.PopupEntity( Loc.GetString("drink-component-try-use-drink-success-slurp"), args.User, Filter.Pvs(args.User)); } SoundSystem.Play(Filter.Pvs(uid), args.Drink.UseSound.GetSound(), uid, AudioParams.Default.WithVolume(-2f)); drained.DoEntityReaction(uid, ReactionMethod.Ingestion); _stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, drained, firstStomach.Value.Comp); }
private void TryMetabolize(EntityUid uid, MetabolizerComponent?meta = null, MechanismComponent?mech = null) { if (!Resolve(uid, ref meta)) { return; } Resolve(uid, ref mech, false); // First step is get the solution we actually care about Solution? solution = null; EntityUid?solutionEntityUid = null; SolutionContainerManagerComponent?manager = null; if (meta.SolutionOnBody) { if (mech != null) { var body = mech.Body; if (body != null) { if (!Resolve((body).Owner, ref manager, false)) { return; } _solutionContainerSystem.TryGetSolution((body).Owner, meta.SolutionName, out solution, manager); solutionEntityUid = body.Owner; } } } else { if (!Resolve(uid, ref manager, false)) { return; } _solutionContainerSystem.TryGetSolution(uid, meta.SolutionName, out solution, manager); solutionEntityUid = uid; } if (solutionEntityUid == null || solution == null) { return; } // randomize the reagent list so we don't have any weird quirks // like alphabetical order or insertion order mattering for processing var list = solution.Contents.ToArray(); _random.Shuffle(list); int reagents = 0; foreach (var reagent in list) { if (!_prototypeManager.TryIndex <ReagentPrototype>(reagent.ReagentId, out var proto)) { continue; } FixedPoint2 mostToRemove = FixedPoint2.Zero; if (proto.Metabolisms == null) { if (meta.RemoveEmpty) { _solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId, FixedPoint2.New(1)); } continue; } // we're done here entirely if this is true if (reagents >= meta.MaxReagentsProcessable) { return; } reagents += 1; // loop over all our groups and see which ones apply if (meta.MetabolismGroups == null) { continue; } foreach (var group in meta.MetabolismGroups) { if (!proto.Metabolisms.Keys.Contains(group.Id)) { continue; } var entry = proto.Metabolisms[group.Id]; // we don't remove reagent for every group, just whichever had the biggest rate if (entry.MetabolismRate > mostToRemove) { mostToRemove = entry.MetabolismRate; } // if it's possible for them to be dead, and they are, // then we shouldn't process any effects, but should probably // still remove reagents if (EntityManager.TryGetComponent <MobStateComponent>(solutionEntityUid.Value, out var state)) { if (state.IsDead()) { continue; } } var args = new ReagentEffectArgs(solutionEntityUid.Value, (meta).Owner, solution, proto, entry.MetabolismRate, EntityManager, null); // do all effects, if conditions apply foreach (var effect in entry.Effects) { if (!effect.ShouldApply(args, _random)) { continue; } if (effect.ShouldLog) { var entity = args.SolutionEntity; _logSystem.Add(LogType.ReagentEffect, effect.LogImpact, $"Metabolism effect {effect.GetType().Name} of reagent {args.Reagent.Name:reagent} applied on entity {entity} at {EntityManager.GetComponent<TransformComponent>(entity).Coordinates}"); } effect.Effect(args); } } // remove a certain amount of reagent if (mostToRemove > FixedPoint2.Zero) { _solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId, mostToRemove); } } }
private void OnForceFeed(EntityUid uid, SharedBodyComponent body, ForceFeedEvent args) { if (args.Food.Deleted) { return; } args.Food.CancelToken = null; if (!_bodySystem.TryGetComponentsOnMechanisms <StomachComponent>(uid, out var stomachs, body)) { return; } var transferAmount = args.Food.TransferAmount != null ? FixedPoint2.Min((FixedPoint2)args.Food.TransferAmount, args.FoodSolution.CurrentVolume) : args.FoodSolution.CurrentVolume; var split = _solutionContainerSystem.SplitSolution((args.Food).Owner, args.FoodSolution, transferAmount); var firstStomach = stomachs.FirstOrNull( stomach => _stomachSystem.CanTransferSolution((stomach.Comp).Owner, split)); if (firstStomach == null) { _solutionContainerSystem.TryAddSolution(uid, args.FoodSolution, split); _popupSystem.PopupEntity(Loc.GetString("food-system-you-cannot-eat-any-more-other"), uid, Filter.Entities(args.User)); return; } split.DoEntityReaction(uid, ReactionMethod.Ingestion); _stomachSystem.TryTransferSolution((firstStomach.Value.Comp).Owner, split, firstStomach.Value.Comp); EntityManager.TryGetComponent(uid, out MetaDataComponent? targetMeta); var targetName = targetMeta?.EntityName ?? string.Empty; EntityManager.TryGetComponent(args.User, out MetaDataComponent? userMeta); var userName = userMeta?.EntityName ?? string.Empty; _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed-success", ("user", userName)), uid, Filter.Entities(uid)); _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed-success-user", ("target", targetName)), args.User, Filter.Entities(args.User)); SoundSystem.Play(Filter.Pvs(uid), args.Food.UseSound.GetSound(), uid, AudioParams.Default.WithVolume(-1f)); // Try to break all used utensils foreach (var utensil in args.Utensils) { _utensilSystem.TryBreak((utensil).Owner, args.User); } if (args.Food.UsesRemaining > 0) { return; } if (string.IsNullOrEmpty(args.Food.TrashPrototype)) { EntityManager.QueueDeleteEntity((args.Food).Owner); } else { DeleteAndSpawnTrash(args.Food, args.User); } }
public PuddleComponent?SpillAt(TileRef tileRef, Solution solution, string prototype, bool overflow = true, bool sound = true, bool noTileReact = false, bool combine = true) { if (solution.TotalVolume <= 0) { return(null); } // If space return early, let that spill go out into the void if (tileRef.Tile.IsEmpty) { return(null); } var gridId = tileRef.GridIndex; if (!_mapManager.TryGetGrid(gridId, out var mapGrid)) { return(null); // Let's not spill to invalid grids. } if (!noTileReact) { // First, do all tile reactions foreach (var(reagentId, quantity) in solution.Contents) { var proto = _prototypeManager.Index <ReagentPrototype>(reagentId); proto.ReactionTile(tileRef, quantity); } } // Tile reactions used up everything. if (solution.CurrentVolume == FixedPoint2.Zero) { return(null); } // Get normalized co-ordinate for spill location and spill it in the centre // TODO: Does SnapGrid or something else already do this? var spillGridCoords = mapGrid.GridTileToWorld(tileRef.GridIndices); var spillEntities = _entityLookup.GetEntitiesIntersecting(mapGrid.ParentMapId, spillGridCoords.Position).ToArray(); foreach (var spillEntity in spillEntities) { if (_solutionContainerSystem.TryGetRefillableSolution(spillEntity, out var solutionContainerComponent)) { _solutionContainerSystem.Refill(spillEntity, solutionContainerComponent, solution.SplitSolution(FixedPoint2.Min( solutionContainerComponent.AvailableVolume, solutionContainerComponent.MaxSpillRefill)) ); } } if (combine) { foreach (var spillEntity in spillEntities) { if (!EntityManager.TryGetComponent(spillEntity, out PuddleComponent? puddleComponent)) { continue; } if (!overflow && _puddleSystem.WouldOverflow(puddleComponent.Owner, solution, puddleComponent)) { return(null); } if (!_puddleSystem.TryAddSolution(puddleComponent.Owner, solution, sound)) { continue; } return(puddleComponent); } } var puddleEnt = EntityManager.SpawnEntity(prototype, spillGridCoords); var newPuddleComponent = EntityManager.GetComponent <PuddleComponent>(puddleEnt); _puddleSystem.TryAddSolution(newPuddleComponent.Owner, solution, sound); return(newPuddleComponent); }
public async Task Test() { var server = StartServer(new ServerContentIntegrationOption { ExtraPrototypes = Prototypes }); await server.WaitIdleAsync(); var sEntityManager = server.ResolveDependency <IEntityManager>(); var sMapManager = server.ResolveDependency <IMapManager>(); var sPrototypeManager = server.ResolveDependency <IPrototypeManager>(); var sEntitySystemManager = server.ResolveDependency <IEntitySystemManager>(); EntityUid sDestructibleEntity = default; DamageableComponent sDamageableComponent = null; DestructibleComponent sDestructibleComponent = null; TestDestructibleListenerSystem sTestThresholdListenerSystem = null; DamageableSystem sDamageableSystem = null; await server.WaitPost(() => { var gridId = GetMainGrid(sMapManager).GridEntityId; var coordinates = new EntityCoordinates(gridId, 0, 0); sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleEntityId, coordinates); sDamageableComponent = IoCManager.Resolve <IEntityManager>().GetComponent <DamageableComponent>(sDestructibleEntity); sDestructibleComponent = IoCManager.Resolve <IEntityManager>().GetComponent <DestructibleComponent>(sDestructibleEntity); sTestThresholdListenerSystem = sEntitySystemManager.GetEntitySystem <TestDestructibleListenerSystem>(); sTestThresholdListenerSystem.ThresholdsReached.Clear(); sDamageableSystem = sEntitySystemManager.GetEntitySystem <DamageableSystem>(); }); await server.WaitRunTicks(5); await server.WaitAssertion(() => { Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); }); await server.WaitAssertion(() => { var bluntDamage = new DamageSpecifier(sPrototypeManager.Index <DamageTypePrototype>("TestBlunt"), 10); sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true); // No thresholds reached yet, the earliest one is at 20 damage Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true); // Only one threshold reached, 20 Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold 20 var msg = sTestThresholdListenerSystem.ThresholdsReached[0]; var threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Is.Empty); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); sTestThresholdListenerSystem.ThresholdsReached.Clear(); sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 3, true); // One threshold reached, 50, since 20 already triggered before and it has not been healed below that amount Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold 50 msg = sTestThresholdListenerSystem.ThresholdsReached[0]; threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Has.Count.EqualTo(3)); var soundThreshold = (PlaySoundBehavior)threshold.Behaviors[0]; var spawnThreshold = (SpawnEntitiesBehavior)threshold.Behaviors[1]; var actsThreshold = (DoActsBehavior)threshold.Behaviors[2]; Assert.That(actsThreshold.Acts, Is.EqualTo(ThresholdActs.Breakage)); Assert.That(soundThreshold.Sound.GetSound(), Is.EqualTo("/Audio/Effects/woodhit.ogg")); Assert.That(spawnThreshold.Spawn, Is.Not.Null); Assert.That(spawnThreshold.Spawn.Count, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Key, Is.EqualTo(SpawnedEntityId)); Assert.That(spawnThreshold.Spawn.Single().Value.Min, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Value.Max, Is.EqualTo(1)); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Damage for 50 again, up to 100 now sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 5, true); // No thresholds reached as they weren't healed below the trigger amount Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Set damage to 0 sDamageableSystem.SetAllDamage(sDamageableComponent, 0); // Damage for 100, up to 100 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 10, true); // Two thresholds reached as damage increased past the previous, 20 and 50 Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(2)); sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Heal the entity for 40 damage, down to 60 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * -4, true); // Thresholds don't work backwards Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Damage for 10, up to 70 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true); // Not enough healing to de-trigger a threshold Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Heal by 30, down to 40 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * -3, true); // Thresholds don't work backwards Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Damage up to 50 again sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage, true); // The 50 threshold should have triggered again, after being healed Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); msg = sTestThresholdListenerSystem.ThresholdsReached[0]; threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Has.Count.EqualTo(3)); soundThreshold = (PlaySoundBehavior)threshold.Behaviors[0]; spawnThreshold = (SpawnEntitiesBehavior)threshold.Behaviors[1]; actsThreshold = (DoActsBehavior)threshold.Behaviors[2]; // Check that it matches the YAML prototype Assert.That(actsThreshold.Acts, Is.EqualTo(ThresholdActs.Breakage)); Assert.That(soundThreshold.Sound.GetSound(), Is.EqualTo("/Audio/Effects/woodhit.ogg")); Assert.That(spawnThreshold.Spawn, Is.Not.Null); Assert.That(spawnThreshold.Spawn.Count, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Key, Is.EqualTo(SpawnedEntityId)); Assert.That(spawnThreshold.Spawn.Single().Value.Min, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Value.Max, Is.EqualTo(1)); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); // Reset thresholds reached sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Heal all damage sDamageableSystem.SetAllDamage(sDamageableComponent, 0); // Damage up to 50 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 5, true); // Check that the total damage matches Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(50))); // Both thresholds should have triggered Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Has.Exactly(2).Items); // Verify the first one, should be the lowest one (20) msg = sTestThresholdListenerSystem.ThresholdsReached[0]; var trigger = (DamageTrigger)msg.Threshold.Trigger; Assert.NotNull(trigger); Assert.That(trigger.Damage, Is.EqualTo(20)); threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Is.Empty); // Verify the second one, should be the highest one (50) msg = sTestThresholdListenerSystem.ThresholdsReached[1]; trigger = (DamageTrigger)msg.Threshold.Trigger; Assert.NotNull(trigger); Assert.That(trigger.Damage, Is.EqualTo(50)); threshold = msg.Threshold; Assert.That(threshold.Behaviors, Has.Count.EqualTo(3)); soundThreshold = (PlaySoundBehavior)threshold.Behaviors[0]; spawnThreshold = (SpawnEntitiesBehavior)threshold.Behaviors[1]; actsThreshold = (DoActsBehavior)threshold.Behaviors[2]; // Check that it matches the YAML prototype Assert.That(actsThreshold.Acts, Is.EqualTo(ThresholdActs.Breakage)); Assert.That(soundThreshold.Sound.GetSound(), Is.EqualTo("/Audio/Effects/woodhit.ogg")); Assert.That(spawnThreshold.Spawn, Is.Not.Null); Assert.That(spawnThreshold.Spawn.Count, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Key, Is.EqualTo(SpawnedEntityId)); Assert.That(spawnThreshold.Spawn.Single().Value.Min, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Value.Max, Is.EqualTo(1)); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); // Reset thresholds reached sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Heal the entity completely sDamageableSystem.SetAllDamage(sDamageableComponent, 0); // Check that the entity has 0 damage Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero)); // Set both thresholds to only trigger once foreach (var destructibleThreshold in sDestructibleComponent.Thresholds) { Assert.NotNull(destructibleThreshold.Trigger); destructibleThreshold.TriggersOnce = true; } // Damage the entity up to 50 damage again sDamageableSystem.TryChangeDamage(sDestructibleEntity, bluntDamage * 5, true); // Check that the total damage matches Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(50))); // No thresholds should have triggered as they were already triggered before, and they are set to only trigger once Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Set both thresholds to trigger multiple times foreach (var destructibleThreshold in sDestructibleComponent.Thresholds) { Assert.NotNull(destructibleThreshold.Trigger); destructibleThreshold.TriggersOnce = false; } // Check that the total damage matches Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(50))); // They shouldn't have been triggered by changing TriggersOnce Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); }); }
/// <summary> /// Tries to eat some food /// </summary> /// <param name="uid">Food entity.</param> /// <param name="user">Feeding initiator.</param> /// <returns>True if an interaction occurred (i.e., food was consumed, or a pop-up message was created)</returns> public bool TryUseFood(EntityUid uid, EntityUid user, FoodComponent?food = null) { if (!Resolve(uid, ref food)) { return(false); } // if currently being used to force-feed, cancel that action. if (food.CancelToken != null) { food.CancelToken.Cancel(); food.CancelToken = null; return(true); } if (uid == user || //Suppresses self-eating EntityManager.TryGetComponent <MobStateComponent>(uid, out var mobState) && mobState.IsAlive()) // Suppresses eating alive mobs { return(false); } if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var solution)) { return(false); } if (food.UsesRemaining <= 0) { _popupSystem.PopupEntity(Loc.GetString("food-system-try-use-food-is-empty", ("entity", uid)), user, Filter.Entities(user)); DeleteAndSpawnTrash(food, user); return(true); } if (!EntityManager.TryGetComponent(user, out SharedBodyComponent ? body) || !_bodySystem.TryGetComponentsOnMechanisms <StomachComponent>(user, out var stomachs, body)) { return(false); } if (IsMouthBlocked(user, user)) { return(true); } var usedUtensils = new List <UtensilComponent>(); if (!TryGetRequiredUtensils(user, food, out var utensils)) { return(true); } if (!user.InRangeUnobstructed(uid, popup: true)) { return(true); } var transferAmount = food.TransferAmount != null?FixedPoint2.Min((FixedPoint2)food.TransferAmount, solution.CurrentVolume) : solution.CurrentVolume; var split = _solutionContainerSystem.SplitSolution(uid, solution, transferAmount); var firstStomach = stomachs.FirstOrNull( stomach => _stomachSystem.CanTransferSolution((stomach.Comp).Owner, split)); if (firstStomach == null) { _solutionContainerSystem.TryAddSolution(uid, solution, split); _popupSystem.PopupEntity(Loc.GetString("food-system-you-cannot-eat-any-more"), user, Filter.Entities(user)); return(true); } // TODO: Account for partial transfer. split.DoEntityReaction(user, ReactionMethod.Ingestion); _stomachSystem.TryTransferSolution((firstStomach.Value.Comp).Owner, split, firstStomach.Value.Comp); SoundSystem.Play(Filter.Pvs(user), food.UseSound.GetSound(), user, AudioParams.Default.WithVolume(-1f)); _popupSystem.PopupEntity(Loc.GetString(food.EatMessage, ("food", food.Owner)), user, Filter.Entities(user)); // Try to break all used utensils foreach (var utensil in usedUtensils) { _utensilSystem.TryBreak((utensil).Owner, user); } if (food.UsesRemaining > 0) { return(true); } if (string.IsNullOrEmpty(food.TrashPrototype)) { EntityManager.QueueDeleteEntity((food).Owner); } else { DeleteAndSpawnTrash(food, user); } return(true); }
public async Task AndTest() { var server = StartServer(new ServerContentIntegrationOption { ExtraPrototypes = Prototypes }); await server.WaitIdleAsync(); var sEntityManager = server.ResolveDependency <IEntityManager>(); var sMapManager = server.ResolveDependency <IMapManager>(); var sPrototypeManager = server.ResolveDependency <IPrototypeManager>(); var sEntitySystemManager = server.ResolveDependency <IEntitySystemManager>(); EntityUid sDestructibleEntity = default; DamageableComponent sDamageableComponent = null; TestDestructibleListenerSystem sTestThresholdListenerSystem = null; DamageableSystem sDamageableSystem = null; await server.WaitPost(() => { var gridId = GetMainGrid(sMapManager).GridEntityId; var coordinates = new EntityCoordinates(gridId, 0, 0); sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageGroupEntityId, coordinates); sDamageableComponent = IoCManager.Resolve <IEntityManager>().GetComponent <DamageableComponent>(sDestructibleEntity); sTestThresholdListenerSystem = sEntitySystemManager.GetEntitySystem <TestDestructibleListenerSystem>(); sTestThresholdListenerSystem.ThresholdsReached.Clear(); sDamageableSystem = sEntitySystemManager.GetEntitySystem <DamageableSystem>(); }); await server.WaitRunTicks(5); await server.WaitAssertion(() => { Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); }); await server.WaitAssertion(() => { var bruteDamageGroup = sPrototypeManager.Index <DamageGroupPrototype>("TestBrute"); var burnDamageGroup = sPrototypeManager.Index <DamageGroupPrototype>("TestBurn"); DamageSpecifier bruteDamage = new(bruteDamageGroup, FixedPoint2.New(5)); DamageSpecifier burnDamage = new(burnDamageGroup, FixedPoint2.New(5)); // Raise brute damage to 5 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bruteDamage, true); // No thresholds reached yet, the earliest one is at 10 damage Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise brute damage to 10 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bruteDamage, true); // No threshold reached, burn needs to be 10 as well Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise burn damage to 10 sDamageableSystem.TryChangeDamage(sDestructibleEntity, burnDamage * 2, true); // One threshold reached, brute 10 + burn 10 Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold brute 10 + burn 10 var msg = sTestThresholdListenerSystem.ThresholdsReached[0]; var threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Is.Empty); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); Assert.IsInstanceOf <AndTrigger>(threshold.Trigger); var trigger = (AndTrigger)threshold.Trigger; Assert.IsInstanceOf <DamageGroupTrigger>(trigger.Triggers[0]); Assert.IsInstanceOf <DamageGroupTrigger>(trigger.Triggers[1]); sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Raise brute damage to 20 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bruteDamage * 2, true); // No new thresholds reached Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise burn damage to 20 sDamageableSystem.TryChangeDamage(sDestructibleEntity, burnDamage * 2, true); // No new thresholds reached Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Lower brute damage to 0 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bruteDamage * -10); Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.New(20))); // No new thresholds reached, healing should not trigger it Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise brute damage back up to 10 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bruteDamage * 2, true); // 10 brute + 10 burn threshold reached, brute was healed and brought back to its threshold amount and slash stayed the same Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Heal both classes of damage to 0 sDamageableSystem.SetAllDamage(sDamageableComponent, 0); // No new thresholds reached, healing should not trigger it Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise brute damage to 10 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bruteDamage * 2, true); // No new thresholds reached Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise burn damage to 10 sDamageableSystem.TryChangeDamage(sDestructibleEntity, burnDamage * 2, true); // Both classes of damage were healed and then raised again, the threshold should have been reached as triggers once is default false Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold brute 10 + burn 10 msg = sTestThresholdListenerSystem.ThresholdsReached[0]; threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Is.Empty); Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); Assert.IsInstanceOf <AndTrigger>(threshold.Trigger); trigger = (AndTrigger)threshold.Trigger; Assert.IsInstanceOf <DamageGroupTrigger>(trigger.Triggers[0]); Assert.IsInstanceOf <DamageGroupTrigger>(trigger.Triggers[1]); sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Change triggers once to true threshold.TriggersOnce = true; // Heal brute and burn back to 0 sDamageableSystem.SetAllDamage(sDamageableComponent, 0); // No new thresholds reached from healing Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise brute damage to 10 sDamageableSystem.TryChangeDamage(sDestructibleEntity, bruteDamage * 2, true); // No new thresholds reached Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise burn damage to 10 sDamageableSystem.TryChangeDamage(sDestructibleEntity, burnDamage * 2, true); // No new thresholds reached as triggers once is set to true and it already triggered before Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); }); }
public async Task PuddlePauseTest() { await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true }); var server = pairTracker.Pair.Server; var sMapManager = server.ResolveDependency <IMapManager>(); var sTileDefinitionManager = server.ResolveDependency <ITileDefinitionManager>(); var sGameTiming = server.ResolveDependency <IGameTiming>(); var entityManager = server.ResolveDependency <IEntityManager>(); MapId sMapId = default; IMapGrid sGrid; EntityUid sGridId = default; EntityCoordinates sCoordinates = default; // Spawn a paused map with one tile to spawn puddles on await server.WaitPost(() => { sMapId = sMapManager.CreateMap(); sMapManager.SetMapPaused(sMapId, true); sGrid = sMapManager.CreateGrid(sMapId); sGridId = sGrid.GridEntityId; entityManager.GetComponent <MetaDataComponent>(sGridId).EntityPaused = true; // See https://github.com/space-wizards/RobustToolbox/issues/1444 var tileDefinition = sTileDefinitionManager["underplating"]; var tile = new Tile(tileDefinition.TileId); sCoordinates = sGrid.ToCoordinates(); sGrid.SetTile(sCoordinates, tile); }); // Check that the map and grid are paused await server.WaitAssertion(() => { Assert.True(sMapManager.IsGridPaused(sGridId)); Assert.True(sMapManager.IsMapPaused(sMapId)); }); float evaporateTime = default; PuddleComponent puddle = null; MetaDataComponent meta = null; EvaporationComponent evaporation; var amount = 2; var entitySystemManager = server.ResolveDependency <IEntitySystemManager>(); var spillSystem = entitySystemManager.GetEntitySystem <SpillableSystem>(); // Spawn a puddle await server.WaitAssertion(() => { var solution = new Solution("Water", FixedPoint2.New(amount)); puddle = spillSystem.SpillAt(solution, sCoordinates, "PuddleSmear"); meta = entityManager.GetComponent <MetaDataComponent>(puddle.Owner); // Check that the puddle was created Assert.NotNull(puddle); evaporation = entityManager.GetComponent <EvaporationComponent>(puddle.Owner); meta.EntityPaused = true; // See https://github.com/space-wizards/RobustToolbox/issues/1445 Assert.True(meta.EntityPaused); // Check that the puddle is going to evaporate Assert.Positive(evaporation.EvaporateTime); // Should have a timer component added to it for evaporation Assert.That(evaporation.Accumulator, Is.EqualTo(0f)); evaporateTime = evaporation.EvaporateTime; }); // Wait enough time for it to evaporate if it was unpaused var sTimeToWait = 5 + (int)Math.Ceiling(amount * evaporateTime * sGameTiming.TickRate); await PoolManager.RunTicksSync(pairTracker.Pair, sTimeToWait); // No evaporation due to being paused await server.WaitAssertion(() => { Assert.True(meta.EntityPaused); // Check that the puddle still exists Assert.False(meta.EntityDeleted); }); // Unpause the map await server.WaitPost(() => { sMapManager.SetMapPaused(sMapId, false); }); // Check that the map, grid and puddle are unpaused await server.WaitAssertion(() => { Assert.False(sMapManager.IsMapPaused(sMapId)); Assert.False(sMapManager.IsGridPaused(sGridId)); Assert.False(meta.EntityPaused); // Check that the puddle still exists Assert.False(meta.EntityDeleted); }); // Wait enough time for it to evaporate await PoolManager.RunTicksSync(pairTracker.Pair, sTimeToWait); // Puddle evaporation should have ticked await server.WaitAssertion(() => { // Check that puddle has been deleted Assert.True(puddle.Deleted); }); await pairTracker.CleanReturnAsync(); }
public ReagentDelta(string reagentId, FixedPoint2 quantity) { ReagentId = reagentId; Quantity = quantity; Lifetime = 0.0f; }
public override void Effect(ReagentEffectArgs args) { var scale = ScaleByQuantity ? args.Quantity : FixedPoint2.New(1); EntitySystem.Get <DamageableSystem>().TryChangeDamage(args.SolutionEntity, Damage * scale, IgnoreResistances); }
private void DrawFromBlood(EntityUid user, EntityUid target, InjectorComponent component, Solution injectorSolution, BloodstreamComponent stream, FixedPoint2 transferAmount) { var drawAmount = (float)transferAmount; var bloodAmount = drawAmount; var chemAmount = 0f; if (stream.ChemicalSolution.CurrentVolume > 0f) // If they have stuff in their chem stream, we'll draw some of that { bloodAmount = drawAmount * 0.85f; chemAmount = drawAmount * 0.15f; } var bloodTemp = stream.BloodSolution.SplitSolution(bloodAmount); var chemTemp = stream.ChemicalSolution.SplitSolution(chemAmount); _solutions.TryAddSolution(component.Owner, injectorSolution, bloodTemp); _solutions.TryAddSolution(component.Owner, injectorSolution, chemTemp); _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message", ("amount", transferAmount), ("target", Identity.Entity(target, EntityManager))), component.Owner, Filter.Entities(user)); Dirty(component); AfterDraw(component); }