private void AddSpillVerb(EntityUid uid, SpillableComponent component, GetOtherVerbsEvent args) { if (!args.CanAccess || !args.CanInteract) { return; } if (!_solutionContainerSystem.TryGetDrainableSolution(args.Target.Uid, out var solution)) { return; } if (solution.DrainAvailable == ReagentUnit.Zero) { return; } Verb verb = new(); verb.Text = Loc.GetString("spill-target-verb-get-data-text"); // TODO VERB ICONS spill icon? pouring out a glass/beaker? verb.Act = () => _solutionContainerSystem.SplitSolution(args.Target.Uid, solution, solution.DrainAvailable).SpillAt(args.Target.Transform.Coordinates, "PuddleSmear"); args.Verbs.Add(verb); }
private void AddSpillVerb(EntityUid uid, SpillableComponent component, GetVerbsEvent <Verb> args) { if (!args.CanAccess || !args.CanInteract) { return; } if (!_solutionContainerSystem.TryGetDrainableSolution(args.Target, out var solution)) { return; } if (solution.DrainAvailable == FixedPoint2.Zero) { return; } Verb verb = new(); verb.Text = Loc.GetString("spill-target-verb-get-data-text"); // TODO VERB ICONS spill icon? pouring out a glass/beaker? verb.Act = () => { var puddleSolution = _solutionContainerSystem.SplitSolution(args.Target, solution, solution.DrainAvailable); SpillAt(puddleSolution, Transform(args.Target).Coordinates, "PuddleSmear"); }; verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately. args.Verbs.Add(verb); }
private void HandleChemicalPayloadTrigger(EntityUid uid, ChemicalPayloadComponent component, TriggerEvent args) { if (component.BeakerSlotA.Item is not EntityUid beakerA || component.BeakerSlotB.Item is not EntityUid beakerB || !TryComp(beakerA, out FitsInDispenserComponent? compA) || !TryComp(beakerB, out FitsInDispenserComponent? compB) || !_solutionSystem.TryGetSolution(beakerA, compA.Solution, out var solutionA) || !_solutionSystem.TryGetSolution(beakerB, compB.Solution, out var solutionB) || solutionA.TotalVolume == 0 || solutionB.TotalVolume == 0) { return; } var solStringA = SolutionContainerSystem.ToPrettyString(solutionA); var solStringB = SolutionContainerSystem.ToPrettyString(solutionB); _logSystem.Add(LogType.ChemicalReaction, $"Chemical bomb payload {ToPrettyString(uid):payload} at {Transform(uid).MapPosition:location} is combining two solutions: {solStringA:solutionA} and {solStringB:solutionB}"); solutionA.MaxVolume += solutionB.MaxVolume; _solutionSystem.TryAddSolution(beakerA, solutionA, solutionB); solutionB.RemoveAllSolution(); // The grenade might be a dud. Redistribute solution: var tmpSol = _solutionSystem.SplitSolution(beakerA, solutionA, solutionA.CurrentVolume * solutionB.MaxVolume / solutionA.MaxVolume); _solutionSystem.TryAddSolution(beakerB, solutionB, tmpSol); solutionA.MaxVolume -= solutionB.MaxVolume; _solutionSystem.UpdateChemicals(beakerA, solutionA, false); }
private void OnFire(EntityUid uid, ChemicalAmmoComponent component, AmmoShotEvent args) { if (!_solutionSystem.TryGetSolution(uid, component.SolutionName, out var ammoSolution)) { return; } var projectiles = args.FiredProjectiles; var projectileSolutionContainers = new List <(EntityUid, Solution)>(); foreach (var projectile in projectiles) { if (_solutionSystem .TryGetSolution(projectile, component.SolutionName, out var projectileSolutionContainer)) { projectileSolutionContainers.Add((uid, projectileSolutionContainer)); } } if (!projectileSolutionContainers.Any()) { return; } var solutionPerProjectile = ammoSolution.CurrentVolume * (1 / projectileSolutionContainers.Count); foreach (var(projectileUid, projectileSolution) in projectileSolutionContainers) { var solutionToTransfer = _solutionSystem.SplitSolution(uid, ammoSolution, solutionPerProjectile); _solutionSystem.TryAddSolution(projectileUid, projectileSolution, solutionToTransfer); } _solutionSystem.RemoveAllSolution(uid, ammoSolution); }
private bool TrySliceFood(EntityUid uid, EntityUid user, EntityUid usedItem, SliceableFoodComponent?component = null, FoodComponent?food = null, TransformComponent?transform = null) { if (!Resolve(uid, ref component, ref food, ref transform) || string.IsNullOrEmpty(component.Slice)) { return(false); } if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var solution)) { return(false); } if (!EntityManager.TryGetComponent(usedItem, out UtensilComponent ? utensil) || (utensil.Types & UtensilType.Knife) == 0) { return(false); } var sliceUid = EntityManager.SpawnEntity(component.Slice, transform.Coordinates); var lostSolution = _solutionContainerSystem.SplitSolution(uid, solution, solution.CurrentVolume / FixedPoint2.New(component.Count)); // Fill new slice FillSlice(sliceUid, lostSolution); if (EntityManager.TryGetComponent(user, out HandsComponent? handsComponent)) { if (ContainerHelpers.IsInContainer(component.Owner)) { handsComponent.PutInHandOrDrop(EntityManager.GetComponent <ItemComponent>(sliceUid)); } } SoundSystem.Play(Filter.Pvs(uid), component.Sound.GetSound(), transform.Coordinates, AudioParams.Default.WithVolume(-2)); component.Count--; // If someone makes food proto with 1 slice... if (component.Count < 1) { EntityManager.DeleteEntity(uid); return(true); } // Split last slice if (component.Count == 1) { var lastSlice = EntityManager.SpawnEntity(component.Slice, transform.Coordinates); // Fill last slice with the rest of the solution FillSlice(lastSlice, solution); EntityManager.DeleteEntity(uid); } return(true); }
/// <summary> /// Will overflow this entity to neighboring entities if required /// </summary> private void CheckOverflow(PuddleComponent puddleComponent) { if (puddleComponent.CurrentVolume <= puddleComponent.OverflowVolume || puddleComponent.Overflown) { return; } var nextPuddles = new List <PuddleComponent>() { puddleComponent }; var overflownPuddles = new List <PuddleComponent>(); while (puddleComponent.OverflowLeft > FixedPoint2.Zero && nextPuddles.Count > 0) { foreach (var next in nextPuddles.ToArray()) { nextPuddles.Remove(next); next.Overflown = true; overflownPuddles.Add(next); var adjacentPuddles = GetAllAdjacentOverflow(next).ToArray(); if (puddleComponent.OverflowLeft <= FixedPoint2.Epsilon * adjacentPuddles.Length) { break; } if (adjacentPuddles.Length == 0) { continue; } var numberOfAdjacent = FixedPoint2.New(adjacentPuddles.Length); var overflowSplit = puddleComponent.OverflowLeft / numberOfAdjacent; foreach (var adjacent in adjacentPuddles) { var adjacentPuddle = adjacent(); var quantity = FixedPoint2.Min(overflowSplit, adjacentPuddle.OverflowVolume); var puddleSolution = _solutionContainerSystem.EnsureSolution(puddleComponent.Owner, puddleComponent.SolutionName); var spillAmount = _solutionContainerSystem.SplitSolution(puddleComponent.Owner, puddleSolution, quantity); TryAddSolution(adjacentPuddle.Owner, spillAmount, false, false); nextPuddles.Add(adjacentPuddle); } } } foreach (var puddle in overflownPuddles) { puddle.Overflown = false; } }
private void ReleaseToFloor(EntityCoordinates clickLocation, AbsorbentComponent absorbent, Solution?absorbedSolution) { if ((_mapManager.TryGetGrid(clickLocation.GetGridUid(EntityManager), out var mapGrid)) && // needs valid grid absorbedSolution is not null) // needs a solution to place on the tile { TileRef tile = mapGrid.GetTileRef(clickLocation); // Drop some of the absorbed liquid onto the ground var releaseAmount = FixedPoint2.Min(absorbent.ResidueAmount, absorbedSolution.CurrentVolume); // The release amount specified on the absorbent component, or the amount currently absorbed (whichever is less). var releasedSolution = _solutionSystem.SplitSolution(absorbent.Owner, absorbedSolution, releaseAmount); // Remove releaseAmount of solution from the absorbent component _spillableSystem.SpillAt(tile, releasedSolution, puddlePrototypeId); // And spill it onto the tile. } }
public override void Update(float frameTime) { base.Update(frameTime); var queueDelete = new RemQueue <EvaporationComponent>(); foreach (var evaporationComponent in EntityManager.EntityQuery <EvaporationComponent>()) { var uid = evaporationComponent.Owner; evaporationComponent.Accumulator += frameTime; if (!_solutionContainerSystem.TryGetSolution(uid, evaporationComponent.SolutionName, out var solution)) { // If no solution, delete the entity EntityManager.QueueDeleteEntity(uid); continue; } if (evaporationComponent.Accumulator < evaporationComponent.EvaporateTime) { continue; } evaporationComponent.Accumulator -= evaporationComponent.EvaporateTime; if (evaporationComponent.EvaporationToggle == true) { _solutionContainerSystem.SplitSolution(uid, solution, FixedPoint2.Min(FixedPoint2.New(1), solution.CurrentVolume)); // removes 1 unit, or solution current volume, whichever is lower. } if (solution.CurrentVolume <= 0) { EntityManager.QueueDeleteEntity(uid); } else if (solution.CurrentVolume <= evaporationComponent.LowerLimit || // if puddle is too big or too small to evaporate. solution.CurrentVolume >= evaporationComponent.UpperLimit) { evaporationComponent.EvaporationToggle = false; // pause evaporation } else { evaporationComponent.EvaporationToggle = true; // unpause evaporation, e.g. if a puddle previously above evaporation UpperLimit was brought down below evaporation UpperLimit via mopping. } } foreach (var evaporationComponent in queueDelete) { EntityManager.RemoveComponent(evaporationComponent.Owner, evaporationComponent); } }
public override void Update(float frameTime) { base.Update(frameTime); var queueDelete = new RemQueue <EvaporationComponent>(); foreach (var evaporationComponent in EntityManager.EntityQuery <EvaporationComponent>()) { var uid = evaporationComponent.Owner; evaporationComponent.Accumulator += frameTime; if (!_solutionContainerSystem.TryGetSolution(uid, evaporationComponent.SolutionName, out var solution)) { // If no solution, delete the entity queueDelete.Add(evaporationComponent); continue; } if (evaporationComponent.Accumulator < evaporationComponent.EvaporateTime) { continue; } evaporationComponent.Accumulator -= evaporationComponent.EvaporateTime; _solutionContainerSystem.SplitSolution(uid, solution, FixedPoint2.Min(FixedPoint2.New(1), solution.CurrentVolume)); if (solution.CurrentVolume == 0) { EntityManager.QueueDeleteEntity(uid); } else if (solution.CurrentVolume <= evaporationComponent.LowerLimit || solution.CurrentVolume >= evaporationComponent.UpperLimit) { queueDelete.Add(evaporationComponent); } } foreach (var evaporationComponent in queueDelete) { EntityManager.RemoveComponent(evaporationComponent.Owner, evaporationComponent); } }
private void OnFeed(EntityUid uid, SharedBodyComponent body, FeedEvent 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)); var forceFeed = uid != args.User; // No stomach so just popup a message that they can't eat. if (firstStomach == null) { _solutionContainerSystem.TryAddSolution(uid, args.FoodSolution, split); _popupSystem.PopupEntity( forceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other") : Loc.GetString("food-system-you-cannot-eat-any-more") , uid, Filter.Entities(args.User)); return; } split.DoEntityReaction(uid, ReactionMethod.Ingestion); _stomachSystem.TryTransferSolution(firstStomach.Value.Comp.Owner, split, firstStomach.Value.Comp); if (forceFeed) { 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)); } else { _popupSystem.PopupEntity(Loc.GetString(args.Food.EatMessage, ("food", args.Food.Owner)), 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); } }
private void OnAfterInteract(EntityUid uid, SprayComponent component, AfterInteractEvent args) { if (args.Handled) { return; } args.Handled = true; if (!_solutionContainerSystem.TryGetSolution(uid, SprayComponent.SolutionName, out var solution)) { return; } var ev = new SprayAttemptEvent(args.User); RaiseLocalEvent(uid, ev, false); if (ev.Cancelled) { return; } var curTime = _gameTiming.CurTime; if (TryComp <ItemCooldownComponent>(uid, out var cooldown) && curTime < cooldown.CooldownEnd) { return; } if (solution.CurrentVolume <= 0) { _popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), uid, Filter.Entities(args.User)); return; } var playerPos = Transform(args.User).Coordinates; if (args.ClickLocation.GetGridUid(EntityManager) != playerPos.GetGridUid(EntityManager)) { return; } var direction = (args.ClickLocation.Position - playerPos.Position).Normalized; var threeQuarters = direction * 0.75f; var quarter = direction * 0.25f; var amount = Math.Max(Math.Min((solution.CurrentVolume / component.TransferAmount).Int(), component.VaporAmount), 1); var spread = component.VaporSpread / amount; for (var i = 0; i < amount; i++) { var rotation = new Angle(direction.ToAngle() + Angle.FromDegrees(spread * i) - Angle.FromDegrees(spread * (amount - 1) / 2)); var(_, diffPos) = args.ClickLocation - playerPos; var diffNorm = diffPos.Normalized; var diffLength = diffPos.Length; var target = Transform(args.User).Coordinates .Offset((diffNorm + rotation.ToVec()).Normalized * diffLength + quarter); if (target.TryDistance(EntityManager, playerPos, out var distance) && distance > component.SprayDistance) { target = Transform(args.User).Coordinates .Offset(diffNorm * component.SprayDistance); } var newSolution = _solutionContainerSystem.SplitSolution(uid, solution, component.TransferAmount); if (newSolution.TotalVolume <= FixedPoint2.Zero) { break; } var vapor = Spawn(component.SprayedPrototype, playerPos.Offset(distance < 1 ? quarter : threeQuarters)); Transform(vapor).LocalRotation = rotation; if (TryComp(vapor, out AppearanceComponent? appearance)) { appearance.SetData(VaporVisuals.Color, solution.Color.WithAlpha(1f)); appearance.SetData(VaporVisuals.State, true); } // Add the solution to the vapor and actually send the thing var vaporComponent = Comp <VaporComponent>(vapor); _vaporSystem.TryAddSolution(vaporComponent, newSolution); // impulse direction is defined in world-coordinates, not local coordinates var impulseDirection = Transform(vapor).WorldRotation.ToVec(); _vaporSystem.Start(vaporComponent, impulseDirection, component.SprayVelocity, target, component.SprayAliveTime); if (component.Impulse > 0f && TryComp(args.User, out PhysicsComponent? body)) { body.ApplyLinearImpulse(-impulseDirection * component.Impulse); } } SoundSystem.Play(component.SpraySound.GetSound(), Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.125f)); RaiseLocalEvent(uid, new RefreshItemCooldownEvent(curTime, curTime + TimeSpan.FromSeconds(component.CooldownTime)), true); }
private void OnInteractUsing(EntityUid uid, PlantHolderComponent component, InteractUsingEvent args) { if (TryComp(args.Used, out SeedComponent? seeds)) { if (component.Seed == null) { if (!_botanySystem.TryGetSeed(seeds, out var seed)) { return; } _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-plant-success-message", ("seedName", seed.Name), ("seedNoun", seed.Noun)), Filter.Entities(args.User), PopupType.Medium); component.Seed = seed; component.Dead = false; component.Age = 1; component.Health = component.Seed.Endurance; component.LastCycle = _gameTiming.CurTime; EntityManager.QueueDeleteEntity(args.Used); component.CheckLevelSanity(); component.UpdateSprite(); return; } _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-already-seeded-message", ("name", Comp <MetaDataComponent>(uid).EntityName)), Filter.Entities(args.User), PopupType.Medium); return; } if (_tagSystem.HasTag(args.Used, "Hoe")) { if (component.WeedLevel > 0) { _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-remove-weeds-message", ("name", Comp <MetaDataComponent>(uid).EntityName)), Filter.Entities(args.User), PopupType.Medium); _popupSystem.PopupEntity(Loc.GetString("plant-holder-component-remove-weeds-others-message", ("otherName", Comp <MetaDataComponent>(args.User).EntityName)), uid, Filter.PvsExcept(args.User)); component.WeedLevel = 0; component.UpdateSprite(); } else { _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-no-weeds-message"), Filter.Entities(args.User)); } return; } if (_tagSystem.HasTag(args.Used, "Shovel")) { if (component.Seed != null) { _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-remove-plant-message", ("name", Comp <MetaDataComponent>(uid).EntityName)), Filter.Entities(args.User), PopupType.Medium); _popupSystem.PopupEntity(Loc.GetString("plant-holder-component-remove-plant-others-message", ("name", Comp <MetaDataComponent>(args.User).EntityName)), uid, Filter.PvsExcept(args.User)); component.RemovePlant(); } else { _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-no-plant-message", ("name", Comp <MetaDataComponent>(uid).EntityName)), Filter.Entities(args.User)); } return; } if (_solutionSystem.TryGetDrainableSolution(args.Used, out var solution) && _solutionSystem.TryGetSolution(uid, component.SoilSolutionName, out var targetSolution) && TryComp(args.Used, out SprayComponent? spray)) { var amount = FixedPoint2.New(1); var targetEntity = uid; var solutionEntity = args.Used; SoundSystem.Play(spray.SpraySound.GetSound(), Filter.Pvs(args.Used), args.Used, AudioHelpers.WithVariation(0.125f)); var split = _solutionSystem.Drain(solutionEntity, solution, amount); if (split.TotalVolume == 0) { _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-no-plant-message", ("owner", args.Used)), Filter.Entities(args.User)); return; } _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-spray-message", ("owner", uid), ("amount", split.TotalVolume)), Filter.Entities(args.User), PopupType.Medium); _solutionSystem.TryAddSolution(targetEntity, targetSolution, split); component.ForceUpdateByExternalCause(); return; } if (_tagSystem.HasTag(args.Used, "PlantSampleTaker")) { if (component.Seed == null) { _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-nothing-to-sample-message"), Filter.Entities(args.User)); return; } if (component.Sampled) { _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-already-sampled-message"), Filter.Entities(args.User)); return; } if (component.Dead) { _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-dead-plant-message"), Filter.Entities(args.User)); return; } component.Seed.Unique = false; var seed = _botanySystem.SpawnSeedPacket(component.Seed, Transform(args.User).Coordinates); seed.RandomOffset(0.25f); _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-take-sample-message", ("seedName", component.Seed.DisplayName)), Filter.Entities(args.User)); component.Health -= (_random.Next(3, 5) * 10); if (_random.Prob(0.3f)) { component.Sampled = true; } // Just in case. component.CheckLevelSanity(); component.ForceUpdateByExternalCause(); return; } if (HasComp <SharpComponent>(args.Used)) { component.DoHarvest(args.User); } if (TryComp <ProduceComponent?>(args.Used, out var produce)) { _popupSystem.PopupCursor(Loc.GetString("plant-holder-component-compost-message", ("owner", uid), ("usingItem", args.Used)), Filter.Entities(args.User), PopupType.Medium); _popupSystem.PopupEntity(Loc.GetString("plant-holder-component-compost-others-message", ("user", Identity.Entity(args.User, EntityManager)), ("usingItem", args.Used), ("owner", uid)), uid, Filter.PvsExcept(args.User)); if (_solutionSystem.TryGetSolution(args.Used, produce.SolutionName, out var solution2)) { // This deliberately discards overfill. _solutionSystem.TryAddSolution(args.Used, solution2, _solutionSystem.SplitSolution(args.Used, solution2, solution2.TotalVolume)); component.ForceUpdateByExternalCause(); } EntityManager.QueueDeleteEntity(args.Used); } }
/// <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); }
private void OnAfterInteract(EntityUid uid, SprayComponent component, AfterInteractEvent args) { if (args.Handled) { return; } args.Handled = true; if (!_solutionContainerSystem.TryGetSolution(uid, SprayComponent.SolutionName, out var solution)) { return; } var ev = new SprayAttemptEvent(args.User); RaiseLocalEvent(uid, ev, false); if (ev.Cancelled) { return; } var curTime = _gameTiming.CurTime; if (TryComp <ItemCooldownComponent>(uid, out var cooldown) && curTime < cooldown.CooldownEnd) { return; } if (solution.CurrentVolume <= 0) { _popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), uid, Filter.Entities(args.User)); return; } var xformQuery = GetEntityQuery <TransformComponent>(); var userXform = xformQuery.GetComponent(args.User); var userMapPos = userXform.MapPosition; var clickMapPos = args.ClickLocation.ToMap(EntityManager); var diffPos = clickMapPos.Position - userMapPos.Position; if (diffPos == Vector2.Zero || diffPos == Vector2.NaN) { return; } var diffLength = diffPos.Length; var diffNorm = diffPos.Normalized; var diffAngle = diffNorm.ToAngle(); // Vectors to determine the spawn offset of the vapor clouds. var threeQuarters = diffNorm * 0.75f; var quarter = diffNorm * 0.25f; var amount = Math.Max(Math.Min((solution.CurrentVolume / component.TransferAmount).Int(), component.VaporAmount), 1); var spread = component.VaporSpread / amount; for (var i = 0; i < amount; i++) { var rotation = new Angle(diffAngle + Angle.FromDegrees(spread * i) - Angle.FromDegrees(spread * (amount - 1) / 2)); // Calculate the destination for the vapor cloud. Limit to the maximum spray distance. var target = userMapPos .Offset((diffNorm + rotation.ToVec()).Normalized * diffLength + quarter); var distance = target.Position.Length; if (distance > component.SprayDistance) { target = userMapPos.Offset(diffNorm * component.SprayDistance); } var newSolution = _solutionContainerSystem.SplitSolution(uid, solution, component.TransferAmount); if (newSolution.TotalVolume <= FixedPoint2.Zero) { break; } // Spawn the vapor cloud onto the grid/map the user is present on. Offset the start position based on how far the target destination is. var vaporPos = userMapPos.Offset(distance < 1 ? quarter : threeQuarters); var vapor = Spawn(component.SprayedPrototype, vaporPos); var vaporXform = xformQuery.GetComponent(vapor); vaporXform.WorldRotation = rotation; if (TryComp(vapor, out AppearanceComponent? appearance)) { appearance.SetData(VaporVisuals.Color, solution.Color.WithAlpha(1f)); appearance.SetData(VaporVisuals.State, true); } // Add the solution to the vapor and actually send the thing var vaporComponent = Comp <VaporComponent>(vapor); _vaporSystem.TryAddSolution(vaporComponent, newSolution); // impulse direction is defined in world-coordinates, not local coordinates var impulseDirection = rotation.ToVec(); _vaporSystem.Start(vaporComponent, vaporXform, impulseDirection, component.SprayVelocity, target, component.SprayAliveTime, args.User); } SoundSystem.Play(component.SpraySound.GetSound(), Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.125f)); RaiseLocalEvent(uid, new RefreshItemCooldownEvent(curTime, curTime + TimeSpan.FromSeconds(component.CooldownTime)), true); }
public override void Update(float frameTime) { base.Update(frameTime); foreach (var drain in EntityQuery <DrainComponent>()) { drain.Accumulator += frameTime; if (drain.Accumulator < drain.DrainFrequency) { continue; } drain.Accumulator -= drain.DrainFrequency; /// Best to do this one every second rather than once every tick... _solutionSystem.TryGetSolution(drain.Owner, DrainComponent.SolutionName, out var drainSolution); if (drainSolution is null) { return; } /// Remove a bit from the buffer _solutionSystem.SplitSolution(drain.Owner, drainSolution, (drain.UnitsDestroyedPerSecond * drain.DrainFrequency)); /// This will ensure that UnitsPerSecond is per second... var amount = drain.UnitsPerSecond * drain.DrainFrequency; var xform = Transform(drain.Owner); List <PuddleComponent> puddles = new(); foreach (var entity in _lookup.GetEntitiesInRange(xform.MapPosition, drain.Range)) { /// No InRangeUnobstructed because there's no collision group that fits right now /// and these are placed by mappers and not buildable/movable so shouldnt really be a problem... if (TryComp <PuddleComponent>(entity, out var puddleComp)) { puddles.Add(puddleComp); } } if (puddles.Count == 0) { _ambientSoundSystem.SetAmbience(drain.Owner, false); continue; } _ambientSoundSystem.SetAmbience(drain.Owner, true); amount /= puddles.Count; foreach (var puddle in puddles) { /// Queue the solution deletion if it's empty. EvaporationSystem might also do this /// but queuedelete should be pretty safe. if (!_solutionSystem.TryGetSolution(puddle.Owner, puddle.SolutionName, out var puddleSolution)) { EntityManager.QueueDeleteEntity(puddle.Owner); continue; } /// Removes the lowest of: /// the drain component's units per second adjusted for # of puddles /// the puddle's remaining volume (making it cleanly zero) /// the drain's remaining volume in its buffer. var transferSolution = _solutionSystem.SplitSolution(puddle.Owner, puddleSolution, FixedPoint2.Min(FixedPoint2.New(amount), puddleSolution.CurrentVolume, drainSolution.AvailableVolume)); _solutionSystem.TryAddSolution(drain.Owner, drainSolution, transferSolution); if (puddleSolution.CurrentVolume <= 0) { EntityManager.QueueDeleteEntity(puddle.Owner); } } } }