From 457af3ee307cefe5c4cbb8960c10823f39ea39eb Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Fri, 14 Apr 2023 01:17:25 +0000 Subject: [PATCH] pet dehydrated fish to make him nice to you (#14709) * petting fish to make him nice to you * fix fishe, refactor a bit * fishe * pro * feedback, for now * refactor * pro --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Components/PettableFriendComponent.cs | 23 +++++++++ .../Friends/Systems/PettableFriendSystem.cs | 50 +++++++++++++++++++ .../Components/FactionExceptionComponent.cs | 17 +++++++ .../Operators/NPCCombatOperator.cs | 9 +++- .../NPC/Systems/FactionExceptionSystem.cs | 33 ++++++++++++ .../interaction-popup-component.ftl | 2 + .../Objects/Specific/rehydrateable.yml | 9 ++++ 7 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 Content.Server/Friends/Components/PettableFriendComponent.cs create mode 100644 Content.Server/Friends/Systems/PettableFriendSystem.cs create mode 100644 Content.Server/NPC/Components/FactionExceptionComponent.cs create mode 100644 Content.Server/NPC/Systems/FactionExceptionSystem.cs diff --git a/Content.Server/Friends/Components/PettableFriendComponent.cs b/Content.Server/Friends/Components/PettableFriendComponent.cs new file mode 100644 index 0000000000..18404931ee --- /dev/null +++ b/Content.Server/Friends/Components/PettableFriendComponent.cs @@ -0,0 +1,23 @@ +using Content.Server.Friends.Systems; + +namespace Content.Server.Friends.Components; + +/// +/// Pet something to become friends with it (use in hand, press Z) +/// Uses FactionExceptionComponent behind the scenes +/// +[RegisterComponent, Access(typeof(PettableFriendSystem))] +public sealed class PettableFriendComponent : Component +{ + /// + /// Localized popup sent when petting for the first time + /// + [DataField("successString", required: true), ViewVariables(VVAccess.ReadWrite)] + public string SuccessString = string.Empty; + + /// + /// Localized popup sent when petting multiple times + /// + [DataField("failureString", required: true), ViewVariables(VVAccess.ReadWrite)] + public string FailureString = string.Empty; +} diff --git a/Content.Server/Friends/Systems/PettableFriendSystem.cs b/Content.Server/Friends/Systems/PettableFriendSystem.cs new file mode 100644 index 0000000000..6ef9724877 --- /dev/null +++ b/Content.Server/Friends/Systems/PettableFriendSystem.cs @@ -0,0 +1,50 @@ +using Content.Server.Chemistry.Components; +using Content.Server.Friends.Components; +using Content.Server.NPC.Components; +using Content.Server.NPC.Systems; +using Content.Shared.Interaction.Events; +using Content.Shared.Popups; + +namespace Content.Server.Friends.Systems; + +public sealed class PettableFriendSystem : EntitySystem +{ + [Dependency] private readonly FactionExceptionSystem _factionException = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(OnRehydrated); + } + + private void OnUseInHand(EntityUid uid, PettableFriendComponent comp, UseInHandEvent args) + { + var user = args.User; + if (args.Handled || !TryComp(uid, out var factionException)) + return; + + if (_factionException.IsIgnored(factionException, user)) + { + _popup.PopupEntity(Loc.GetString(comp.FailureString, ("target", uid)), user, user); + return; + } + + // you have made a new friend :) + _popup.PopupEntity(Loc.GetString(comp.SuccessString, ("target", uid)), user, user); + _factionException.IgnoreEntity(factionException, user); + args.Handled = true; + } + + private void OnRehydrated(EntityUid uid, PettableFriendComponent _, ref GotRehydratedEvent args) + { + // can only pet before hydrating, after that the fish cannot be negotiated with + if (!TryComp(uid, out var comp)) + return; + + var targetComp = AddComp(args.Target); + _factionException.IgnoreEntities(targetComp, comp.Ignored); + } +} diff --git a/Content.Server/NPC/Components/FactionExceptionComponent.cs b/Content.Server/NPC/Components/FactionExceptionComponent.cs new file mode 100644 index 0000000000..661156668c --- /dev/null +++ b/Content.Server/NPC/Components/FactionExceptionComponent.cs @@ -0,0 +1,17 @@ +using Content.Server.NPC.Systems; + +namespace Content.Server.NPC.Components; + +/// +/// Prevents an NPC from attacking ignored entities from enemy factions. +/// Can be added to if pettable, see PettableFriendComponent. +/// +[RegisterComponent, Access(typeof(FactionExceptionSystem))] +public sealed class FactionExceptionComponent : Component +{ + /// + /// List of entities that this NPC will refuse to attack + /// + [DataField("ignored")] + public HashSet Ignored = new(); +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/NPCCombatOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/NPCCombatOperator.cs index b0b6b8d785..9b22f70da9 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/NPCCombatOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/NPCCombatOperator.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using Content.Server.Interaction; +using Content.Server.NPC.Components; using Content.Server.NPC.Pathfinding; using Content.Server.NPC.Systems; using Content.Shared.Examine; @@ -8,6 +9,7 @@ using Content.Shared.Interaction; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Robust.Shared.Map; +//using Robust.Shared.Prototypes; namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators; @@ -15,6 +17,7 @@ public abstract class NPCCombatOperator : HTNOperator { [Dependency] protected readonly IEntityManager EntManager = default!; private FactionSystem _factions = default!; + private FactionExceptionSystem _factionException = default!; protected InteractionSystem Interaction = default!; private PathfindingSystem _pathfinding = default!; @@ -38,6 +41,7 @@ public abstract class NPCCombatOperator : HTNOperator base.Initialize(sysManager); sysManager.GetEntitySystem(); _factions = sysManager.GetEntitySystem(); + _factionException = sysManager.GetEntitySystem(); Interaction = sysManager.GetEntitySystem(); _pathfinding = sysManager.GetEntitySystem(); } @@ -85,6 +89,8 @@ public abstract class NPCCombatOperator : HTNOperator paths.Add(UpdateTarget(owner, existingTarget, existingTarget, ownerCoordinates, blackboard, radius, canMove, xformQuery, targets)); } + EntManager.TryGetComponent(owner, out var factionException); + // TODO: Need a perception system instead // TODO: This will be expensive so will be good to optimise and cut corners. foreach (var target in _factions @@ -93,7 +99,8 @@ public abstract class NPCCombatOperator : HTNOperator if (mobQuery.TryGetComponent(target, out var mobState) && mobState.CurrentState > MobState.Alive || target == existingTarget || - target == owner) + target == owner || + (factionException != null && _factionException.IsIgnored(factionException, target))) { continue; } diff --git a/Content.Server/NPC/Systems/FactionExceptionSystem.cs b/Content.Server/NPC/Systems/FactionExceptionSystem.cs new file mode 100644 index 0000000000..909fe3904d --- /dev/null +++ b/Content.Server/NPC/Systems/FactionExceptionSystem.cs @@ -0,0 +1,33 @@ +using Content.Server.NPC.Components; + +namespace Content.Server.NPC.Systems; + +/// +/// Prevents an NPC from attacking some entities from an enemy faction. +/// +public sealed class FactionExceptionSystem : EntitySystem +{ + /// + /// Returns whether the entity from an enemy faction won't be attacked + /// + public bool IsIgnored(FactionExceptionComponent comp, EntityUid target) + { + return comp.Ignored.Contains(target); + } + + /// + /// Prevents an entity from an enemy faction from being attacked + /// + public void IgnoreEntity(FactionExceptionComponent comp, EntityUid target) + { + comp.Ignored.Add(target); + } + + /// + /// Prevents a list of entities from an enemy faction from being attacked + /// + public void IgnoreEntities(FactionExceptionComponent comp, IEnumerable ignored) + { + comp.Ignored.UnionWith(ignored); + } +} diff --git a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl index 759717ac40..40ace46de2 100644 --- a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl +++ b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl @@ -9,6 +9,7 @@ petting-success-bird = You pet {THE($target)} on {POSS-ADJ($target)} cute feathe petting-success-cat = You pet {THE($target)} on {POSS-ADJ($target)} fuzzy little head. petting-success-corrupted-corgi = In an act of hubris, you pet {THE($target)} on {POSS-ADJ($target)} cursed little head. petting-success-crab = You pet {THE($target)} on {POSS-ADJ($target)} smooth little head. +petting-success-dehydrated-carp = You pet {THE($target)} on {POSS-ADJ($target)} dry little head. {CAPITALIZE(OBJECT($target))} seems to like you now! petting-success-dog = You pet {THE($target)} on {POSS-ADJ($target)} soft floofy head. petting-success-frog = You pet {THE($target)} on {POSS-ADJ($target)} slippery little head. petting-success-goat = You pet {THE($target)} on {POSS-ADJ($target)} horned floofy head. @@ -29,6 +30,7 @@ petting-failure-generic = You reach out to pet {THE($target)}, but {SUBJECT($tar petting-failure-bat = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} too hard to catch! petting-failure-corrupted-corgi = You reach out to pet {THE($target)}, but think better of it. petting-failure-crab = You reach out to pet {THE($target)}, but {SUBJECT($target)} snaps {POSS-ADJ($target)} claws in your general direction! +petting-failure-dehydrated-carp = You pet {THE($target)} on {POSS-ADJ($target)} dry little head. petting-failure-goat = You reach out to pet {THE($target)}, but {SUBJECT($target)} stubbornly refuses! petting-failure-goose = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} too horrible! petting-failure-possum = You reach out to pet {THE($target)}, but are met with hisses and snarls! diff --git a/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml b/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml index 0970251207..15d4911367 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/rehydrateable.yml @@ -88,3 +88,12 @@ hard: false layer: - LowImpassable + # pet fish before rehydrating and he will be nice to you + - type: FactionException + - type: PettableFriend + successString: petting-success-dehydrated-carp + failureString: petting-failure-dehydrated-carp + - type: EmitSoundOnUse + handle: false + sound: + path: /Audio/Effects/bite.ogg