diff --git a/Content.Server/Fluids/EntitySystems/SpraySystem.cs b/Content.Server/Fluids/EntitySystems/SpraySystem.cs index fe179be402..a1f195bf43 100644 --- a/Content.Server/Fluids/EntitySystems/SpraySystem.cs +++ b/Content.Server/Fluids/EntitySystems/SpraySystem.cs @@ -14,6 +14,7 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Physics.Components; using Robust.Shared.Prototypes; using System.Numerics; +using Robust.Shared.Map; namespace Content.Server.Fluids.EntitySystems; @@ -35,6 +36,19 @@ public sealed class SpraySystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnActivateInWorld); + } + + private void OnActivateInWorld(Entity entity, ref UserActivateInWorldEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + + var targetMapPos = _transform.GetMapCoordinates(GetEntityQuery().GetComponent(args.Target)); + + Spray(entity, args.User, targetMapPos); } private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) @@ -44,29 +58,36 @@ public sealed class SpraySystem : EntitySystem args.Handled = true; + var clickPos = _transform.ToMapCoordinates(args.ClickLocation); + + Spray(entity, args.User, clickPos); + } + + public void Spray(Entity entity, EntityUid user, MapCoordinates mapcoord) + { if (!_solutionContainer.TryGetSolution(entity.Owner, SprayComponent.SolutionName, out var soln, out var solution)) return; - var ev = new SprayAttemptEvent(args.User); + var ev = new SprayAttemptEvent(user); RaiseLocalEvent(entity, ref ev); if (ev.Cancelled) return; - if (!TryComp(entity, out var useDelay) - || _useDelay.IsDelayed((entity, useDelay))) + if (TryComp(entity, out var useDelay) + && _useDelay.IsDelayed((entity, useDelay))) return; if (solution.Volume <= 0) { - _popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), entity.Owner, args.User); + _popupSystem.PopupEntity(Loc.GetString("spray-component-is-empty-message"), entity.Owner, user); return; } var xformQuery = GetEntityQuery(); - var userXform = xformQuery.GetComponent(args.User); + var userXform = xformQuery.GetComponent(user); var userMapPos = _transform.GetMapCoordinates(userXform); - var clickMapPos = args.ClickLocation.ToMap(EntityManager, _transform); + var clickMapPos = mapcoord; var diffPos = clickMapPos.Position - userMapPos.Position; if (diffPos == Vector2.Zero || diffPos == Vector2Helpers.NaN) @@ -88,8 +109,6 @@ public sealed class SpraySystem : EntitySystem var amount = Math.Max(Math.Min((solution.Volume / entity.Comp.TransferAmount).Int(), entity.Comp.VaporAmount), 1); var spread = entity.Comp.VaporSpread / amount; - // TODO: Just use usedelay homie. - var cooldownTime = 0f; for (var i = 0; i < amount; i++) { @@ -131,20 +150,19 @@ public sealed class SpraySystem : EntitySystem // impulse direction is defined in world-coordinates, not local coordinates var impulseDirection = rotation.ToVec(); var time = diffLength / entity.Comp.SprayVelocity; - cooldownTime = MathF.Max(time, cooldownTime); - _vapor.Start(ent, vaporXform, impulseDirection * diffLength, entity.Comp.SprayVelocity, target, time, args.User); + _vapor.Start(ent, vaporXform, impulseDirection * diffLength, entity.Comp.SprayVelocity, target, time, user); - if (TryComp(args.User, out var body)) + if (TryComp(user, out var body)) { - if (_gravity.IsWeightless(args.User, body)) - _physics.ApplyLinearImpulse(args.User, -impulseDirection.Normalized() * entity.Comp.PushbackAmount, body: body); + if (_gravity.IsWeightless(user, body)) + _physics.ApplyLinearImpulse(user, -impulseDirection.Normalized() * entity.Comp.PushbackAmount, body: body); } } _audio.PlayPvs(entity.Comp.SpraySound, entity, entity.Comp.SpraySound.Params.WithVariation(0.125f)); - _useDelay.SetLength(entity.Owner, TimeSpan.FromSeconds(cooldownTime)); - _useDelay.TryResetDelay((entity, useDelay)); + if (useDelay != null) + _useDelay.TryResetDelay((entity, useDelay)); } } diff --git a/Content.Server/NPC/Queries/Considerations/TargetOnFireCon.cs b/Content.Server/NPC/Queries/Considerations/TargetOnFireCon.cs new file mode 100644 index 0000000000..d86dbc06ec --- /dev/null +++ b/Content.Server/NPC/Queries/Considerations/TargetOnFireCon.cs @@ -0,0 +1,9 @@ +namespace Content.Server.NPC.Queries.Considerations; + +/// +/// Returns 1f if the target is on fire or 0f if not. +/// +public sealed partial class TargetOnFireCon : UtilityConsideration +{ + +} diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index 8dff93648b..60bc5cdfd8 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -1,3 +1,4 @@ +using Content.Server.Atmos.Components; using Content.Server.Fluids.EntitySystems; using Content.Server.NPC.Queries; using Content.Server.NPC.Queries.Considerations; @@ -351,6 +352,12 @@ public sealed class NPCUtilitySystem : EntitySystem return 0f; } + case TargetOnFireCon: + { + if (TryComp(targetUid, out FlammableComponent? fire) && fire.OnFire) + return 1f; + return 0f; + } default: throw new NotImplementedException(); } diff --git a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl index 10773d6de8..a180b698fa 100644 --- a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl +++ b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl @@ -60,6 +60,7 @@ petting-success-honkbot = You pet {THE($target)} on {POSS-ADJ($target)} slippery petting-success-mimebot = You pet {THE($target)} on {POSS-ADJ($target)} cold metal head. petting-success-cleanbot = You pet {THE($target)} on {POSS-ADJ($target)} damp metal head. petting-success-medibot = You pet {THE($target)} on {POSS-ADJ($target)} sterile metal head. +petting-success-firebot = You pet {THE($target)} on {POSS-ADJ($target)} warm metal head. petting-success-generic-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} metal head. petting-success-salvage-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} dirty metal head. petting-success-engineer-cyborg = You pet {THE($target)} on {POSS-ADJ($target)} reflective metal head. @@ -73,6 +74,7 @@ petting-failure-honkbot = You reach out to pet {THE($target)}, but {SUBJECT($tar petting-failure-cleanbot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy mopping! petting-failure-mimebot = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy miming! petting-failure-medibot = You reach out to pet {THE($target)}, but {POSS-ADJ($target)} syringe nearly stabs your hand! +petting-failure-firebot = You reach out to pet {THE($target)}, but {SUBJECT($target)} sprays you in the face before you can get close! petting-failure-generic-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy stating laws! petting-failure-salvage-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy drilling! petting-failure-engineer-cyborg = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy repairing! diff --git a/Resources/Locale/en-US/npc/firebot.ftl b/Resources/Locale/en-US/npc/firebot.ftl new file mode 100644 index 0000000000..758874ceaa --- /dev/null +++ b/Resources/Locale/en-US/npc/firebot.ftl @@ -0,0 +1 @@ +firebot-fire-detected = Fire detected! diff --git a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml index 625ef7a056..11643c076d 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml @@ -249,6 +249,7 @@ - type: Tag tags: - WhitelistChameleon + - FireHelmet - type: HideLayerClothing slots: - Hair @@ -281,6 +282,7 @@ - type: Tag tags: - WhitelistChameleon + - FireHelmet - type: HideLayerClothing slots: - Hair diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index d5b4366e2b..7d988c6fe9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -108,6 +108,53 @@ - type: NoSlip - type: Insulated +- type: entity + parent: MobSiliconBase + id: MobFireBot + name: firebot + description: A little fire extinguishing bot. He looks rather anxious. + components: + - type: Sprite + sprite: Mobs/Silicon/Bots/firebot.rsi + state: firebot + - type: Construction + graph: FireBot + node: bot + - type: SentienceTarget + flavorKind: station-event-random-sentience-flavor-mechanical + - type: HTN + rootTask: + task: FirebotCompound + - type: SolutionContainerManager + solutions: + spray: + maxVol: 10 + reagents: + - ReagentId: Water + Quantity: 10 + - type: SolutionRegeneration + solution: spray + generated: + reagents: + - ReagentId: Water + Quantity: 10 + - type: Spray + transferAmount: 10 + pushbackAmount: 60 + spraySound: + path: /Audio/Effects/extinguish.ogg + sprayedPrototype: ExtinguisherSpray + vaporAmount: 1 + vaporSpread: 90 + sprayVelocity: 3.0 + - type: UseDelay + delay: 4 + - type: InteractionPopup + interactSuccessString: petting-success-firebot + interactFailureString: petting-failure-firebot + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg + - type: entity parent: MobSiliconBase id: MobHonkBot diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index 0389db27ea..b306da6442 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -62,6 +62,9 @@ - Rolling speedModifier: 0.5 # its very big, awkward to use - type: Appearance + - type: Tag + tags: + - FireExtinguisher - type: GenericVisualizer visuals: enum.ToggleVisuals.Toggled: diff --git a/Resources/Prototypes/NPCs/firebot.yml b/Resources/Prototypes/NPCs/firebot.yml new file mode 100644 index 0000000000..acea6498bd --- /dev/null +++ b/Resources/Prototypes/NPCs/firebot.yml @@ -0,0 +1,44 @@ +- type: htnCompound + id: FirebotCompound + branches: + - tasks: + - !type:HTNCompoundTask + task: DouseFireTargetCompound + - tasks: + - !type:HTNCompoundTask + task: IdleCompound + +- type: htnCompound + id: DouseFireTargetCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:UtilityOperator + proto: NearbyOnFire + + - !type:HTNPrimitiveTask + operator: !type:SpeakOperator + speech: firebot-fire-detected + hidden: true + + - !type:HTNPrimitiveTask + operator: !type:MoveToOperator + pathfindInPlanning: true + removeKeyOnFinish: false + targetKey: TargetCoordinates + pathfindKey: TargetPathfind + rangeKey: InteractRange + + - !type:HTNPrimitiveTask + preconditions: + - !type:TargetInRangePrecondition + targetKey: Target + rangeKey: InteractRange + operator: !type:InteractWithOperator + targetKey: Target + services: + - !type:UtilityService + id: FireService + proto: NearbyOnFire + key: Target + diff --git a/Resources/Prototypes/NPCs/utility_queries.yml b/Resources/Prototypes/NPCs/utility_queries.yml index 06bc0a9a9e..e10a0ed30c 100644 --- a/Resources/Prototypes/NPCs/utility_queries.yml +++ b/Resources/Prototypes/NPCs/utility_queries.yml @@ -144,6 +144,27 @@ - !type:TargetAccessibleCon curve: !type:BoolCurve +- type: utilityQuery + id: NearbyOnFire + query: + - !type:ComponentQuery + components: + - type: Flammable + # why does Flammable even have a required damage + damage: + types: + burn: 0 + considerations: + - !type:TargetDistanceCon + curve: !type:PresetCurve + preset: TargetDistance + - !type:TargetAccessibleCon + curve: !type:BoolCurve + - !type:TargetInLOSOrCurrentCon + curve: !type:BoolCurve + - !type:TargetOnFireCon + curve: !type:BoolCurve + - type: utilityQuery id: NearbyPuddles query: diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/bots/firebot.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/firebot.yml new file mode 100644 index 0000000000..977ffd4093 --- /dev/null +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/bots/firebot.yml @@ -0,0 +1,33 @@ +- type: constructionGraph + id: FireBot + start: start + graph: + - node: start + edges: + - to: bot + steps: + - tag: FireExtinguisher + icon: + sprite: Objects/Misc/fire_extinguisher.rsi + state: fire_extinguisher_open + name: fire extinguisher + - tag: FireHelmet + icon: + sprite: Clothing/Head/Helmets/firehelmet.rsi + state: icon + name: fire helmet + doAfter: 2 + - tag: ProximitySensor + icon: + sprite: Objects/Misc/proximity_sensor.rsi + state: icon + name: proximity sensor + doAfter: 2 + - tag: BorgArm + icon: + sprite: Mobs/Silicon/drone.rsi + state: l_hand + name: borg arm + doAfter: 2 + - node: bot + entity: MobFireBot diff --git a/Resources/Prototypes/Recipes/Crafting/bots.yml b/Resources/Prototypes/Recipes/Crafting/bots.yml index 3031f4a780..21b524a060 100644 --- a/Resources/Prototypes/Recipes/Crafting/bots.yml +++ b/Resources/Prototypes/Recipes/Crafting/bots.yml @@ -11,6 +11,19 @@ sprite: Mobs/Silicon/Bots/cleanbot.rsi state: cleanbot +- type: construction + name: firebot + id: firebot + graph: FireBot + startNode: start + targetNode: bot + category: construction-category-utilities + objectType: Item + description: This bot puts out fires wherever it goes. + icon: + sprite: Mobs/Silicon/Bots/firebot.rsi + state: firebot + - type: construction name: honkbot id: honkbot diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 2a07d061d3..86ade97d4e 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -617,6 +617,12 @@ - type: Tag id: FirelockElectronics +- type: Tag + id: FireExtinguisher + +- type: Tag + id: FireHelmet + - type: Tag id: Flare diff --git a/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/firebot.png b/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/firebot.png new file mode 100644 index 0000000000..70ee9313d2 Binary files /dev/null and b/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/firebot.png differ diff --git a/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/meta.json b/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/meta.json new file mode 100644 index 0000000000..e13b42deee --- /dev/null +++ b/Resources/Textures/Mobs/Silicon/Bots/firebot.rsi/meta.json @@ -0,0 +1,20 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation/commit/eba0d62005e7754dd8b1c88e45cd949c360774d5", + "states": [ + { + "name": "firebot", + "delays": [ + [ + 0.5, + 0.2 + ] + ] + } + ] +}