Преглед изворни кода

Longquan fix, implements sleepzone (WIP) (#156)

* longquan fix

* more WIP sleepzone beds

* rsi perm fix

* tp to safezone

* why doesnt it work aaaaa

* hm

* sleepzone stuff

* final fixes
Taislin пре 7 месеци
родитељ
комит
bc46a0844f

+ 4 - 0
.github/workflows/rsi-diff.yml

@@ -5,6 +5,10 @@ on:
     paths:
       - "**.rsi/**.png"
 
+permissions:
+  contents: read # Required for actions/checkout
+  pull-requests: write # Required for commenting on pull requests
+
 jobs:
   diff:
     name: Diff

+ 40 - 13
Content.Shared/Bed/Sleep/SleepingSystem.cs

@@ -24,6 +24,7 @@
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Timing;
+using Content.Shared.Civ14.SleepZone;
 
 namespace Content.Shared.Bed.Sleep;
 
@@ -36,10 +37,11 @@ public sealed partial class SleepingSystem : EntitySystem
     [Dependency] private readonly SharedAudioSystem _audio = default!;
     [Dependency] private readonly SharedEmitSoundSystem _emitSound = default!;
     [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
-
+    [Dependency] private readonly SleepZoneSystem _sleepzone = default!;
+    [Dependency] private readonly ILogManager _log = default!;
     public static readonly EntProtoId SleepActionId = "ActionSleep";
     public static readonly EntProtoId WakeActionId = "ActionWake";
-
+    private ISawmill _sawmill = default!;
     public override void Initialize()
     {
         base.Initialize();
@@ -67,6 +69,7 @@ public override void Initialize()
         SubscribeLocalEvent<SleepingComponent, EmoteAttemptEvent>(OnEmoteAttempt);
 
         SubscribeLocalEvent<SleepingComponent, BeforeForceSayEvent>(OnChangeForceSay, after: new[] { typeof(PainNumbnessSystem) });
+        _sawmill = _log.GetSawmill("sleeping");
     }
 
     private void OnUnbuckleAttempt(Entity<SleepingComponent> ent, ref UnbuckleAttemptEvent args)
@@ -74,7 +77,11 @@ private void OnUnbuckleAttempt(Entity<SleepingComponent> ent, ref UnbuckleAttemp
         // TODO is this necessary?
         // Shouldn't the interaction have already been blocked by a general interaction check?
         if (ent.Owner == args.User)
-            args.Cancelled = true;
+            //if mob has the sleepzone component, do not wake up when moved
+            if (!TryComp<SleepZoneComponent>(ent, out var sleepZone))
+            {
+                args.Cancelled = true;
+            }
     }
 
     private void OnBedSleepAction(Entity<ActionsContainerComponent> ent, ref SleepActionEvent args)
@@ -128,13 +135,6 @@ private void OnSleepStateChanged(Entity<MobStateComponent> ent, ref SleepStateCh
         RemComp<SpamEmitSoundComponent>(ent);
     }
 
-    private void OnMapInit(Entity<SleepingComponent> ent, ref MapInitEvent args)
-    {
-        var ev = new SleepStateChangedEvent(true);
-        RaiseLocalEvent(ent, ref ev);
-        _blindableSystem.UpdateIsBlind(ent.Owner);
-        _actionsSystem.AddAction(ent, ref ent.Comp.WakeAction, WakeActionId, ent);
-    }
 
     private void OnSpeakAttempt(Entity<SleepingComponent> ent, ref SpeakAttemptEvent args)
     {
@@ -261,18 +261,42 @@ private void Wake(Entity<SleepingComponent> ent)
     /// </summary>
     public bool TrySleeping(Entity<MobStateComponent?> ent)
     {
+        // Use Resolve to get the MobStateComponent if it exists, otherwise return false.
         if (!Resolve(ent, ref ent.Comp, logMissing: false))
             return false;
 
+        // Raise an event to see if anything wants to prevent sleeping.
         var tryingToSleepEvent = new TryingToSleepEvent(ent);
-        RaiseLocalEvent(ent, ref tryingToSleepEvent);
+        RaiseLocalEvent(ent.Owner, ref tryingToSleepEvent); // Use EntityUid (ent.Owner)
         if (tryingToSleepEvent.Cancelled)
             return false;
+        // Specific logic if the entity also has a SleepZoneComponent.
+        // Use TryComp with the original Entity<T> struct 'ent'.
+        if (TryComp<SleepZoneComponent>(ent, out var sleepZone))
+        {
+            _sleepzone.StartSleep(ent);
+        }
+        // Ensure the SleepingComponent exists and get a reference to it.
+        // Use ent.Owner (the EntityUid) with EnsureComp.
+        var sleepComp = EnsureComp<SleepingComponent>(ent.Owner);
+
+        // Raise the state changed event *after* the component is added. Use ent.Owner (the EntityUid).
+        var ev = new SleepStateChangedEvent(true);
+        RaiseLocalEvent(ent.Owner, ref ev);
+        _blindableSystem.UpdateIsBlind(ent.Owner);
+        _actionsSystem.AddAction(ent.Owner, ref sleepComp.WakeAction, WakeActionId, ent.Owner);
+
 
-        EnsureComp<SleepingComponent>(ent);
         return true;
     }
 
+    private void OnMapInit(Entity<SleepingComponent> ent, ref MapInitEvent args)
+    {
+        var ev = new SleepStateChangedEvent(true);
+        RaiseLocalEvent(ent, ref ev);
+        _blindableSystem.UpdateIsBlind(ent.Owner);
+        _actionsSystem.AddAction(ent, ref ent.Comp.WakeAction, WakeActionId, ent);
+    }
     /// <summary>
     /// Tries to wake up <paramref name="ent"/>, with a cooldown between attempts to prevent spam.
     /// </summary>
@@ -314,8 +338,11 @@ public bool TryWaking(Entity<SleepingComponent?> ent, bool force = false, Entity
             _audio.PlayPredicted(ent.Comp.WakeAttemptSound, ent, user);
             _popupSystem.PopupClient(Loc.GetString("wake-other-success", ("target", Identity.Entity(ent, EntityManager))), ent, user);
         }
-
         Wake((ent, ent.Comp));
+        if (TryComp<SleepZoneComponent>(ent, out var sleepZone))
+        {
+            _sleepzone.WakeUp(ent);
+        }
         return true;
     }
 

+ 21 - 0
Content.Shared/Civ14/SleepZone/SleepZoneComponent.cs

@@ -0,0 +1,21 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Map;
+
+namespace Content.Shared.Civ14.SleepZone;
+/// <summary>
+/// Enables an entity to go to sleep in the safezone.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class SleepZoneComponent : Component
+{
+    /// <summary>
+    /// The original coordinates the entity was teleported from.
+    /// </summary>
+    [DataField("origin"), NeverPushInheritance] // Prevent prototype system from copying this during creation
+    public EntityCoordinates? Origin; // Needs to be nullable
+    /// <summary>
+    /// Is the entity currently in the sleep zone?
+    /// </summary>
+    [DataField("isSleeping")]
+    public bool IsSleeping = false;
+}

+ 145 - 0
Content.Shared/Civ14/SleepZone/SleepZoneSystem.cs

@@ -0,0 +1,145 @@
+using Content.Shared.Coordinates;
+using Robust.Shared.Map;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Log; // Added for ILogManager and ISawmill
+using Robust.Shared.IoC; // Added for Dependency attribute
+using Robust.Shared.GameStates; // Needed for [RegisterComponent] if SleepZoneComponent wasn't partial
+using Robust.Shared.Serialization.Manager.Attributes; // Needed for [DataField]
+using System.Numerics;
+
+namespace Content.Shared.Civ14.SleepZone;
+public sealed partial class SleepZoneSystem : EntitySystem
+{
+    [Dependency] private readonly ILogManager _log = default!;
+    [Dependency] private readonly SharedTransformSystem _xform = default!;
+    [Dependency] private readonly IEntityManager _entities = default!;
+    private ISawmill _sawmill = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        _sawmill = _log.GetSawmill("sleepzone");
+    }
+
+    /// <summary>
+    /// Tries to find the first entity with the prototype "SleepZoneBed".
+    /// </summary>
+    /// <param name="bedId">The EntityUid of the found bed, or EntityUid.Invalid if none was found.</param>
+    /// <returns>True if a bed was found, false otherwise.</returns>
+    public bool TryFindSleepZoneBed(out EntityUid bedId)
+    {
+        const string bedPrototypeId = "SleepZoneBed";
+
+        // More efficient: directly query for the prototype we want
+        var query = _entities.EntityQueryEnumerator<MetaDataComponent, TransformComponent>();
+        while (query.MoveNext(out var uid, out var meta, out _))
+        {
+            if (meta.EntityPrototype?.ID == bedPrototypeId)
+            {
+                bedId = uid;
+                return true;
+            }
+        }
+        bedId = EntityUid.Invalid;
+        return false;
+    }
+
+
+    public void StartSleep(EntityUid entity)
+    {
+        // Use TryComp for cleaner component checking
+        if (!_entities.TryGetComponent<SleepZoneComponent>(entity, out var sleepZone))
+        {
+            _sawmill.Debug($"Entity {entity} does not have a SleepZoneComponent, cannot start sleep.");
+            return;
+        }
+
+        if (sleepZone.IsSleeping)
+        {
+            _sawmill.Debug($"Entity {entity} is already sleeping.");
+            return;
+        }
+
+        // Store the original absolute world position
+        sleepZone.Origin = Transform(entity).Coordinates;
+        _sawmill.Info($"Saved origin {sleepZone.Origin} for entity {entity}.");
+
+
+        if (TryTeleportToBed(entity))
+        {
+            sleepZone.IsSleeping = true;
+            _sawmill.Info($"Entity {entity} started sleeping successfully.");
+        }
+        else
+        {
+            _sawmill.Warning($"Entity {entity} failed to start sleeping because teleportation to bed failed.");
+            // Reset origin if teleport fails, as the entity hasn't moved.
+            sleepZone.Origin = EntityCoordinates.Invalid;
+        }
+    }
+
+    private bool TryTeleportToBed(EntityUid entityToTeleport)
+    {
+        if (TryFindSleepZoneBed(out var bedEntity))
+        {
+            // Use EntityExists for clarity
+            if (!_entities.EntityExists(bedEntity))
+            {
+                _sawmill.Warning($"Found bed {bedEntity} but it no longer exists.");
+                return false;
+            }
+
+            var targetCoords = Transform(bedEntity).Coordinates;
+
+            _sawmill.Info($"Found bed {bedEntity}, teleporting {entityToTeleport} into it at {targetCoords}.");
+
+            // Use _xform for SetCoordinates
+            _xform.SetCoordinates(entityToTeleport, targetCoords);
+            return true; // Teleport successful
+        }
+        else
+        {
+            _sawmill.Warning($"Could not find any entity with prototype 'SleepZoneBed' to teleport {entityToTeleport} to.");
+            return false;
+        }
+    }
+
+    public void WakeUp(EntityUid entity)
+    {
+        // Use TryComp for cleaner component checking
+        if (_entities.TryGetComponent<SleepZoneComponent>(entity, out var sleepZone))
+        {
+            if (!sleepZone.IsSleeping)
+            {
+                _sawmill.Debug($"Entity {entity} is not sleeping, cannot wake up.");
+                return;
+            }
+
+            // Check if the origin is valid before teleporting
+            if (!sleepZone.Origin.HasValue) // Use .HasValue for nullable types
+
+            {
+                // Use ToPrettyString for better entity logging if available, otherwise fallback
+                var entityString = _entities.ToPrettyString(entity);
+                _sawmill.Warning($"Entity {entityString} has no Origin coordinates stored, cannot teleport back.");
+                // Decide what to do here - maybe leave them in bed? Or teleport to a default spot?
+                // For now, just mark as not sleeping.
+                sleepZone.IsSleeping = false;
+                return;
+            }
+
+            _sawmill.Info($"Waking up entity {_entities.ToPrettyString(entity)}, returning to {sleepZone.Origin.Value}."); // Log the .Value
+
+            _xform.SetCoordinates(entity, sleepZone.Origin.Value);
+
+
+            sleepZone.IsSleeping = false;
+            // Clear the origin after use
+            sleepZone.Origin = null;
+        }
+        else
+        {
+            _sawmill.Debug($"Entity {entity} does not have a SleepZoneComponent, cannot wake up.");
+        }
+    }
+}

+ 7 - 0
Resources/Changelog/Changelog.yml

@@ -151,3 +151,10 @@ Entries:
     id: 13
     time: "2025-04-25T00:00:00.0000000+00:00"
     url: https://github.com/Civ13/Civ14/pull/158
+  - author: Taislin
+    changes:
+      - message: Adds the sleeping mechanic, which teleports you to the safezone.
+        type: Add
+    id: 14
+    time: "2025-04-26T00:00:00.0000000+00:00"
+    url: https://github.com/Civ13/Civ14/pull/156

+ 5 - 0
Resources/Prototypes/Civ14/Entities/Structures/Furniture/beds.yml

@@ -56,3 +56,8 @@
     - type: Construction
       graph: BedRollPrim14
       node: bedroll
+
+- type: entity
+  name: sleepzone bed
+  id: SleepZoneBed
+  parent: Bed

+ 1 - 0
Resources/Prototypes/Entities/Mobs/Species/human.yml

@@ -31,6 +31,7 @@
         - Snout
     - type: ShowAntagIcons
     - type: ShowSyndicateIcons
+    - type: SleepZone
     - type: Inventory
       femaleDisplacements:
         jumpsuit:

BIN
Resources/Textures/Civ14/Weapons/longquan.rsi/inhand-left.png