Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rodentia Species #208

Merged
merged 15 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Content.Server.Nutrition;
using Content.Server.Speech;
using Content.Server.Speech.EntitySystems;
using Content.Shared.DeltaV.Storage.Components;
using Content.Shared.DeltaV.Storage.EntitySystems;
using Content.Shared.Storage;

namespace Content.Server.DeltaV.Storage.EntitySystems;

public sealed class MouthStorageSystem : SharedMouthStorageSystem
{
[Dependency] private readonly ReplacementAccentSystem _replacement = default!;
public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<MouthStorageComponent, AccentGetEvent>(OnAccent);
SubscribeLocalEvent<MouthStorageComponent, IngestionAttemptEvent>(OnIngestAttempt);
}

// Force you to mumble if you have items in your mouth
private void OnAccent(EntityUid uid, MouthStorageComponent component, AccentGetEvent args)
{
if (IsMouthBlocked(component))
args.Message = _replacement.ApplyReplacements(args.Message, "mumble");
}

// Attempting to eat or drink anything with items in your mouth won't work
private void OnIngestAttempt(EntityUid uid, MouthStorageComponent component, IngestionAttemptEvent args)
{
if (!IsMouthBlocked(component))
return;

if (!TryComp<StorageComponent>(component.MouthId, out var storage))
return;

var firstItem = storage.Container.ContainedEntities[0];
args.Blocker = firstItem;
args.Cancel();
}
}
5 changes: 5 additions & 0 deletions Content.Server/Mousetrap/MousetrapSystem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Content.Server.Damage.Systems;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Abilities;
using Content.Shared.Interaction.Events;
using Content.Shared.Mousetrap;
using Content.Shared.StepTrigger.Systems;
Expand Down Expand Up @@ -35,6 +36,10 @@ private void OnUseInHand(EntityUid uid, MousetrapComponent component, UseInHandE

private void OnStepTriggerAttempt(EntityUid uid, MousetrapComponent component, ref StepTriggerAttemptEvent args)
{
// DeltaV: Entities with this component always trigger mouse traps, even if wearing shoes
if (HasComp<AlwaysTriggerMousetrapComponent>(args.Tripper))
args.Cancelled = false;

args.Continue |= component.IsActive;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Abilities;

[RegisterComponent, NetworkedComponent]
public sealed partial class AlwaysTriggerMousetrapComponent : Component;
5 changes: 5 additions & 0 deletions Content.Shared/DeltaV/Abilities/RummagerComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Abilities;

[RegisterComponent, NetworkedComponent]
public sealed partial class RummagerComponent : Component;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Content.Shared.DeltaV.Storage.EntitySystems;
using Content.Shared.FixedPoint;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared.DeltaV.Storage.Components;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(SharedMouthStorageSystem))]
public sealed partial class MouthStorageComponent : Component
{
public const string MouthContainerId = "mouth";

[DataField, AutoNetworkedField]
public EntProtoId? OpenStorageAction;

[DataField, AutoNetworkedField]
public EntityUid? Action;

[DataField]
public EntProtoId MouthProto = "ActionOpenMouthStorage";

[ViewVariables]
public Container Mouth = default!;

[DataField]
public EntityUid? MouthId;

// Mimimum inflicted damage on hit to spit out items
[DataField]
public FixedPoint2 SpitDamageThreshold = FixedPoint2.New(2);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using Content.Shared.Actions;
using Content.Shared.CombatMode;
using Content.Shared.Damage;
using Content.Shared.DeltaV.Storage.Components;
using Content.Shared.Examine;
using Content.Shared.IdentityManagement;
using Content.Shared.Standing;
using Content.Shared.Storage;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.Containers;
using Robust.Shared.Map;

namespace Content.Shared.DeltaV.Storage.EntitySystems;

public abstract class SharedMouthStorageSystem : EntitySystem
{
[Dependency] private readonly DumpableSystem _dumpableSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<MouthStorageComponent, MapInitEvent>(OnMouthStorageInit);
SubscribeLocalEvent<MouthStorageComponent, DownedEvent>(DropAllContents);
SubscribeLocalEvent<MouthStorageComponent, DisarmedEvent>(DropAllContents);
SubscribeLocalEvent<MouthStorageComponent, DamageChangedEvent>(OnDamageModified);
SubscribeLocalEvent<MouthStorageComponent, ExaminedEvent>(OnExamined);
}

protected bool IsMouthBlocked(MouthStorageComponent component)
{
if (!TryComp<StorageComponent>(component.MouthId, out var storage))
return false;

return storage.Container.ContainedEntities.Count > 0;
}

private void OnMouthStorageInit(EntityUid uid, MouthStorageComponent component, MapInitEvent args)
{
if (string.IsNullOrWhiteSpace(component.MouthProto))
return;

component.Mouth = _containerSystem.EnsureContainer<Container>(uid, MouthStorageComponent.MouthContainerId);
component.Mouth.ShowContents = false;
component.Mouth.OccludesLight = false;

var mouth = Spawn(component.MouthProto, new EntityCoordinates(uid, 0, 0));
_containerSystem.Insert(mouth, component.Mouth);
component.MouthId = mouth;

if (!string.IsNullOrWhiteSpace(component.OpenStorageAction) && component.Action == null)
_actionsSystem.AddAction(uid, ref component.Action, component.OpenStorageAction, mouth);
}

private void DropAllContents(EntityUid uid, MouthStorageComponent component, EntityEventArgs args)
{
if (component.MouthId == null)
return;

_dumpableSystem.DumpContents(component.MouthId.Value, uid, uid);
}

private void OnDamageModified(EntityUid uid, MouthStorageComponent component, DamageChangedEvent args)
{
if (args.DamageDelta == null
|| !args.DamageIncreased
|| args.DamageDelta.GetTotal() < component.SpitDamageThreshold)
return;

DropAllContents(uid, component, args);
}

// Other people can see if this person has items in their mouth.
private void OnExamined(EntityUid uid, MouthStorageComponent component, ExaminedEvent args)
{
if (IsMouthBlocked(component))
{
var subject = Identity.Entity(uid, EntityManager);
args.PushMarkup(Loc.GetString("mouth-storage-examine-condition-occupied", ("entity", subject)));
}
}
}
3 changes: 3 additions & 0 deletions Content.Shared/Humanoid/NamingSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ public string GetName(string species, Gender? gender = null)
case SpeciesNaming.FirstDashFirst:
return Loc.GetString("namepreset-firstdashfirst",
("first1", GetFirstName(speciesProto, gender)), ("first2", GetFirstName(speciesProto, gender)));
case SpeciesNaming.LastFirst: // DeltaV: Rodentia name scheme
return Loc.GetString("namepreset-lastfirst",
("last", GetLastName(speciesProto)), ("first", GetFirstName(speciesProto, gender)));
case SpeciesNaming.FirstDashLast:
return Loc.GetString("namepreset-firstdashlast",
("first", GetFirstName(speciesProto, gender)), ("last", GetLastName(speciesProto)));
Expand Down
1 change: 1 addition & 0 deletions Content.Shared/Humanoid/Prototypes/SpeciesPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,5 @@ public enum SpeciesNaming : byte
//End of Nyano - Summary: for Oni naming
TheFirstofLast,
FirstDashLast,
LastFirst, // DeltaV
}
14 changes: 13 additions & 1 deletion Content.Shared/RatKing/RatKingRummageableComponent.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Content.Shared.Random;
using Content.Shared.Random;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
Expand All @@ -20,6 +20,18 @@ public sealed partial class RatKingRummageableComponent : Component
[AutoNetworkedField]
public bool Looted;

/// <summary>
/// DeltaV: Last time the object was looted, used to check if cooldown has expired
/// </summary>
[ViewVariables]
public TimeSpan? LastLooted;

/// <summary>
/// DeltaV: Minimum time between rummage attempts
/// </summary>
[DataField, AutoNetworkedField]
public TimeSpan RummageCooldown = TimeSpan.FromMinutes(5);

/// <summary>
/// How long it takes to rummage through a rummageable container.
/// </summary>
Expand Down
20 changes: 16 additions & 4 deletions Content.Shared/RatKing/SharedRatKingSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Shared.Actions;
using Content.Shared.Actions;
using Content.Shared.Abilities; // DeltavV
using Content.Shared.DoAfter;
using Content.Shared.Random;
using Content.Shared.Random.Helpers;
Expand All @@ -9,11 +10,13 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;

namespace Content.Shared.RatKing;

public abstract class SharedRatKingSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!; // DeltaV - Used for rummage cooldown
[Dependency] private readonly INetManager _net = default!;
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
[Dependency] protected readonly IRobustRandom Random = default!;
Expand Down Expand Up @@ -105,7 +108,10 @@ private void UpdateActions(EntityUid uid, RatKingComponent? component = null)

private void OnGetVerb(EntityUid uid, RatKingRummageableComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!HasComp<RatKingComponent>(args.User) || component.Looted)
// DeltaV - Use RummagerComponent instead of RatKingComponent
// (This is so we can give Rodentia rummage abilities)
// Additionally, adds a cooldown check
if (!HasComp<RummagerComponent>(args.User) || component.Looted || _gameTiming.CurTime < component.LastLooted + component.RummageCooldown)
return;

args.Verbs.Add(new AlternativeVerb
Expand All @@ -128,10 +134,16 @@ private void OnGetVerb(EntityUid uid, RatKingRummageableComponent component, Get

private void OnDoAfterComplete(EntityUid uid, RatKingRummageableComponent component, RatKingRummageDoAfterEvent args)
{
if (args.Cancelled || component.Looted)
// DeltaV - Rummaging an object updates the looting cooldown rather than a "previously looted" check.
// Note that the "Looted" boolean can still be checked (by mappers/admins)
// to disable rummaging on the object indefinitely, but rummaging will no
// longer permanently prevent future rummaging.
var time = _gameTiming.CurTime;
if (args.Cancelled || component.Looted || time < component.LastLooted + component.RummageCooldown)
return;

component.Looted = true;
component.LastLooted = time;
// End DeltaV change
Dirty(uid, component);
_audio.PlayPredicted(component.Sound, uid, args.User);

Expand Down
26 changes: 20 additions & 6 deletions Content.Shared/Storage/EntitySystems/DumpableSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Content.Shared.Placeable;
using Content.Shared.Storage.Components;
using Content.Shared.Verbs;
using JetBrains.Annotations;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
Expand Down Expand Up @@ -140,27 +141,40 @@ private void StartDoAfter(EntityUid storageUid, EntityUid targetUid, EntityUid u

private void OnDoAfter(EntityUid uid, DumpableComponent component, DumpableDoAfterEvent args)
{
if (args.Handled || args.Cancelled || !TryComp<StorageComponent>(uid, out var storage) || storage.Container.ContainedEntities.Count == 0)
if (args.Handled || args.Cancelled)
return;

DumpContents(uid, args.Args.Target, args.Args.User, component);
}

// DeltaV: Refactor to allow dumping that doesn't require a verb
[PublicAPI]
public void DumpContents(EntityUid uid, EntityUid? target, EntityUid user, DumpableComponent? component = null)
{
if (!TryComp<StorageComponent>(uid, out var storage) || !Resolve(uid, ref component))
return;

if (storage.Container.ContainedEntities.Count == 0)
return;

var dumpQueue = new Queue<EntityUid>(storage.Container.ContainedEntities);

var dumped = false;

if (_disposalUnitSystem.HasDisposals(args.Args.Target))
if (_disposalUnitSystem.HasDisposals(target))
{
dumped = true;

foreach (var entity in dumpQueue)
{
_disposalUnitSystem.DoInsertDisposalUnit(args.Args.Target.Value, entity, args.Args.User);
_disposalUnitSystem.DoInsertDisposalUnit(target.Value, entity, user);
}
}
else if (HasComp<PlaceableSurfaceComponent>(args.Args.Target))
else if (HasComp<PlaceableSurfaceComponent>(target))
{
dumped = true;

var (targetPos, targetRot) = _transformSystem.GetWorldPositionRotation(args.Args.Target.Value);
var (targetPos, targetRot) = _transformSystem.GetWorldPositionRotation(target.Value);

foreach (var entity in dumpQueue)
{
Expand All @@ -180,7 +194,7 @@ private void OnDoAfter(EntityUid uid, DumpableComponent component, DumpableDoAft

if (dumped)
{
_audio.PlayPredicted(component.DumpSound, uid, args.User);
_audio.PlayPredicted(component.DumpSound, uid, user);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
crawl-under-objects-toggle-on = Now sneaking
crawl-under-objects-toggle-off = Now standing
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ chat-speech-verb-harpy-1 = chirps
chat-speech-verb-harpy-2 = tweets
chat-speech-verb-harpy-3 = caws
chat-speech-verb-harpy-4 = trills

chat-speech-verb-name-rodentia = Rodentia
chat-speech-verb-rodentia-1 = squeaks
chat-speech-verb-rodentia-2 = pieps
chat-speech-verb-rodentia-3 = chatters
chat-speech-verb-rodentia-4 = squeals
Loading
Loading