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