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>
This commit is contained in:
deltanedas
2023-04-14 01:17:25 +00:00
committed by GitHub
parent 40537ddfeb
commit 457af3ee30
7 changed files with 142 additions and 1 deletions

View File

@@ -0,0 +1,23 @@
using Content.Server.Friends.Systems;
namespace Content.Server.Friends.Components;
/// <summary>
/// Pet something to become friends with it (use in hand, press Z)
/// Uses FactionExceptionComponent behind the scenes
/// </summary>
[RegisterComponent, Access(typeof(PettableFriendSystem))]
public sealed class PettableFriendComponent : Component
{
/// <summary>
/// Localized popup sent when petting for the first time
/// </summary>
[DataField("successString", required: true), ViewVariables(VVAccess.ReadWrite)]
public string SuccessString = string.Empty;
/// <summary>
/// Localized popup sent when petting multiple times
/// </summary>
[DataField("failureString", required: true), ViewVariables(VVAccess.ReadWrite)]
public string FailureString = string.Empty;
}

View File

@@ -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<PettableFriendComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<PettableFriendComponent, GotRehydratedEvent>(OnRehydrated);
}
private void OnUseInHand(EntityUid uid, PettableFriendComponent comp, UseInHandEvent args)
{
var user = args.User;
if (args.Handled || !TryComp<FactionExceptionComponent>(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<FactionExceptionComponent>(uid, out var comp))
return;
var targetComp = AddComp<FactionExceptionComponent>(args.Target);
_factionException.IgnoreEntities(targetComp, comp.Ignored);
}
}

View File

@@ -0,0 +1,17 @@
using Content.Server.NPC.Systems;
namespace Content.Server.NPC.Components;
/// <summary>
/// Prevents an NPC from attacking ignored entities from enemy factions.
/// Can be added to if pettable, see PettableFriendComponent.
/// </summary>
[RegisterComponent, Access(typeof(FactionExceptionSystem))]
public sealed class FactionExceptionComponent : Component
{
/// <summary>
/// List of entities that this NPC will refuse to attack
/// </summary>
[DataField("ignored")]
public HashSet<EntityUid> Ignored = new();
}

View File

@@ -1,6 +1,7 @@
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.Interaction; using Content.Server.Interaction;
using Content.Server.NPC.Components;
using Content.Server.NPC.Pathfinding; using Content.Server.NPC.Pathfinding;
using Content.Server.NPC.Systems; using Content.Server.NPC.Systems;
using Content.Shared.Examine; using Content.Shared.Examine;
@@ -8,6 +9,7 @@ using Content.Shared.Interaction;
using Content.Shared.Mobs; using Content.Shared.Mobs;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Robust.Shared.Map; using Robust.Shared.Map;
//using Robust.Shared.Prototypes;
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators; namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators;
@@ -15,6 +17,7 @@ public abstract class NPCCombatOperator : HTNOperator
{ {
[Dependency] protected readonly IEntityManager EntManager = default!; [Dependency] protected readonly IEntityManager EntManager = default!;
private FactionSystem _factions = default!; private FactionSystem _factions = default!;
private FactionExceptionSystem _factionException = default!;
protected InteractionSystem Interaction = default!; protected InteractionSystem Interaction = default!;
private PathfindingSystem _pathfinding = default!; private PathfindingSystem _pathfinding = default!;
@@ -38,6 +41,7 @@ public abstract class NPCCombatOperator : HTNOperator
base.Initialize(sysManager); base.Initialize(sysManager);
sysManager.GetEntitySystem<ExamineSystemShared>(); sysManager.GetEntitySystem<ExamineSystemShared>();
_factions = sysManager.GetEntitySystem<FactionSystem>(); _factions = sysManager.GetEntitySystem<FactionSystem>();
_factionException = sysManager.GetEntitySystem<FactionExceptionSystem>();
Interaction = sysManager.GetEntitySystem<InteractionSystem>(); Interaction = sysManager.GetEntitySystem<InteractionSystem>();
_pathfinding = sysManager.GetEntitySystem<PathfindingSystem>(); _pathfinding = sysManager.GetEntitySystem<PathfindingSystem>();
} }
@@ -85,6 +89,8 @@ public abstract class NPCCombatOperator : HTNOperator
paths.Add(UpdateTarget(owner, existingTarget, existingTarget, ownerCoordinates, blackboard, radius, canMove, xformQuery, targets)); paths.Add(UpdateTarget(owner, existingTarget, existingTarget, ownerCoordinates, blackboard, radius, canMove, xformQuery, targets));
} }
EntManager.TryGetComponent<FactionExceptionComponent>(owner, out var factionException);
// TODO: Need a perception system instead // TODO: Need a perception system instead
// TODO: This will be expensive so will be good to optimise and cut corners. // TODO: This will be expensive so will be good to optimise and cut corners.
foreach (var target in _factions foreach (var target in _factions
@@ -93,7 +99,8 @@ public abstract class NPCCombatOperator : HTNOperator
if (mobQuery.TryGetComponent(target, out var mobState) && if (mobQuery.TryGetComponent(target, out var mobState) &&
mobState.CurrentState > MobState.Alive || mobState.CurrentState > MobState.Alive ||
target == existingTarget || target == existingTarget ||
target == owner) target == owner ||
(factionException != null && _factionException.IsIgnored(factionException, target)))
{ {
continue; continue;
} }

View File

@@ -0,0 +1,33 @@
using Content.Server.NPC.Components;
namespace Content.Server.NPC.Systems;
/// <summary>
/// Prevents an NPC from attacking some entities from an enemy faction.
/// </summary>
public sealed class FactionExceptionSystem : EntitySystem
{
/// <summary>
/// Returns whether the entity from an enemy faction won't be attacked
/// </summary>
public bool IsIgnored(FactionExceptionComponent comp, EntityUid target)
{
return comp.Ignored.Contains(target);
}
/// <summary>
/// Prevents an entity from an enemy faction from being attacked
/// </summary>
public void IgnoreEntity(FactionExceptionComponent comp, EntityUid target)
{
comp.Ignored.Add(target);
}
/// <summary>
/// Prevents a list of entities from an enemy faction from being attacked
/// </summary>
public void IgnoreEntities(FactionExceptionComponent comp, IEnumerable<EntityUid> ignored)
{
comp.Ignored.UnionWith(ignored);
}
}

View File

@@ -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-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-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-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-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-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. 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-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-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-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-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-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! petting-failure-possum = You reach out to pet {THE($target)}, but are met with hisses and snarls!

View File

@@ -88,3 +88,12 @@
hard: false hard: false
layer: layer:
- LowImpassable - 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