Esempio n. 1
0
 public HyposprayComponentState(FixedPoint2 curVolume, FixedPoint2 maxVolume)
 {
     CurVolume = curVolume;
     MaxVolume = maxVolume;
 }
Esempio n. 2
0
        /// <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);
        }
Esempio n. 3
0
        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);
        }
Esempio n. 8
0
        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);
            });
        }
Esempio n. 9
0
        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();
        }
Esempio n. 10
0
 public ReagentDelta(string reagentId, FixedPoint2 quantity)
 {
     ReagentId = reagentId;
     Quantity  = quantity;
     Lifetime  = 0.0f;
 }
Esempio n. 11
0
        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);
    }