11// SPDX-FileCopyrightText: 2025 Evaisa <mail@evaisa.dev>
2- //
2+ // SPDX-FileCopyrightText: 2025 RichardBlonski <48651647+RichardBlonski@users.noreply.github.com>
33// SPDX-License-Identifier: AGPL-3.0-or-later
44
55using Content . Shared . _DV . Abilities ;
66using Content . Shared . _Starlight . VentCrawling ;
7- using Content . Shared . Actions ;
87using Content . Shared . Climbing . Components ;
98using Content . Shared . Climbing . Events ;
109using Content . Shared . Interaction . Events ;
2221using Robust . Shared . Physics . Systems ;
2322using Robust . Shared . Player ;
2423using Robust . Shared . Random ;
24+ using Content . Shared . Actions ;
25+ using Content . Shared . Mobs . Components ;
26+ using Content . Shared . Doors . Components ;
27+ using Content . Shared . Conveyor ;
28+ using Robust . Shared . Physics . Events ;
29+ using Robust . Shared . Physics ;
30+ using Robust . Shared . Physics . Components ;
31+ using Robust . Shared . Physics . Systems ;
2532
2633namespace Content . Goobstation . Shared . FloorGoblin ;
2734
@@ -40,9 +47,6 @@ public abstract class SharedCrawlUnderFloorSystem : EntitySystem
4047 [ Dependency ] private readonly TileSystem _tile = default ! ;
4148 [ Dependency ] private readonly SharedStealthSystem _stealth = default ! ;
4249
43- private const int HiddenMask = ( int ) ( CollisionGroup . HighImpassable | CollisionGroup . MidImpassable | CollisionGroup . LowImpassable | CollisionGroup . InteractImpassable ) ;
44- private const int HiddenLayer = ( int ) ( CollisionGroup . HighImpassable | CollisionGroup . MidImpassable | CollisionGroup . LowImpassable | CollisionGroup . MobLayer ) ;
45-
4650 public override void Initialize ( )
4751 {
4852 base . Initialize ( ) ;
@@ -51,6 +55,7 @@ public override void Initialize()
5155 SubscribeLocalEvent < CrawlUnderFloorComponent , AttemptClimbEvent > ( OnAttemptClimb ) ;
5256 SubscribeLocalEvent < MapGridComponent , TileChangedEvent > ( OnTileChanged ) ;
5357 SubscribeLocalEvent < CrawlUnderFloorComponent , MoveEvent > ( OnMove ) ;
58+ SubscribeLocalEvent < CrawlUnderFloorComponent , PreventCollideEvent > ( OnPreventCollision ) ;
5459 SubscribeLocalEvent < CrawlUnderFloorComponent , AttackAttemptEvent > ( OnAttemptAttack ) ;
5560 SubscribeLocalEvent < AttackAttemptEvent > ( OnAnyAttackAttempt ) ;
5661 }
@@ -130,21 +135,47 @@ private void OnTileChanged(EntityUid gridUid, MapGridComponent grid, ref TileCha
130135
131136 private void OnMove ( EntityUid uid , CrawlUnderFloorComponent comp , ref MoveEvent args )
132137 {
133- ProcessCrawlStateChange ( uid , comp , false ) ;
138+ // Just update the crawl state based on whether we're enabled
139+ ProcessCrawlStateChange ( uid , comp , comp . Enabled ) ;
134140 }
135141
142+
136143 private void OnAttemptAttack ( EntityUid uid , CrawlUnderFloorComponent comp , AttackAttemptEvent args )
137144 {
138145 if ( IsHidden ( uid , comp ) )
139146 args . Cancel ( ) ;
140147 }
141148
142- private void OnAnyAttackAttempt ( AttackAttemptEvent args )
149+ private void OnAnyAttackAttempt ( AttackAttemptEvent ev )
143150 {
144- if ( args . Target is not { } target )
151+ if ( HasComp < CrawlUnderFloorComponent > ( ev . Target ) )
152+ ev . Cancel ( ) ;
153+ }
154+
155+ private void OnPreventCollision ( EntityUid uid , CrawlUnderFloorComponent component , ref PreventCollideEvent args )
156+ {
157+ var otherUid = args . OtherEntity ;
158+
159+ // Always prevent collision with mobs
160+ if ( HasComp < MobStateComponent > ( otherUid ) )
161+ {
162+ args . Cancelled = true ;
145163 return ;
146- if ( TryComp ( target , out CrawlUnderFloorComponent ? goblinComp ) && IsHidden ( target , goblinComp ) )
147- args . Cancel ( ) ;
164+ }
165+
166+ // Handle airlocks - allow phasing in stealth mode
167+ if ( HasComp < AirlockComponent > ( otherUid ) & & component . Enabled )
168+ {
169+ args . Cancelled = true ;
170+ return ;
171+ }
172+
173+ // Handle conveyor belts - allow phasing in stealth mode
174+ if ( HasComp < ConveyorComponent > ( otherUid ) & & component . Enabled )
175+ {
176+ args . Cancelled = true ;
177+ return ;
178+ }
148179 }
149180
150181 protected void PlayDuendeSound ( EntityUid uid , float probability = 0.3f )
@@ -163,7 +194,6 @@ protected bool EnableSneakMode(EntityUid uid, CrawlUnderFloorComponent component
163194 return false ;
164195 component . Enabled = true ;
165196 Dirty ( uid , component ) ;
166- UpdateSneakCollision ( uid , component ) ;
167197 return true ;
168198 }
169199
@@ -189,33 +219,21 @@ protected bool DisableSneakMode(EntityUid uid, CrawlUnderFloorComponent componen
189219 return true ;
190220 }
191221
192- protected void UpdateSneakCollision ( EntityUid uid , CrawlUnderFloorComponent comp )
193- {
194- if ( ! TryComp ( uid , out FixturesComponent ? fixtures ) )
195- return ;
196-
197- var hidden = IsHidden ( uid , comp ) ;
198-
199- foreach ( var ( key , fixture ) in fixtures . Fixtures )
200- {
201- var baseMask = GetOrCacheBase ( comp . ChangedFixtures , key , fixture . CollisionMask ) ;
202- var desiredMask = hidden ? GetHiddenMask ( baseMask ) : baseMask ;
203- if ( fixture . CollisionMask != desiredMask )
204- _physics . SetCollisionMask ( uid , key , fixture , desiredMask , manager : fixtures ) ;
205-
206- var baseLayer = GetOrCacheBase ( comp . ChangedFixtureLayers , key , fixture . CollisionLayer ) ;
207- var desiredLayer = hidden ? GetHiddenLayer ( baseLayer ) : baseLayer ;
208- if ( fixture . CollisionLayer != desiredLayer )
209- _physics . SetCollisionLayer ( uid , key , fixture , desiredLayer , manager : fixtures ) ;
210- }
211- }
212222
213223 public bool IsOnCollidingTile ( EntityUid uid )
214224 {
215- if ( ! TryGetCurrentTile ( uid , out var tileRef , out _ ) )
225+ // If we're under the floor, don't consider any tiles as colliding
226+ if ( TryComp < CrawlUnderFloorComponent > ( uid , out var crawlComp ) &&
227+ crawlComp . Enabled &&
228+ ! IsOnSubfloor ( uid ) )
229+ {
216230 return false ;
217- if ( tileRef . Tile . IsEmpty )
231+ }
232+
233+ // Standard collision check for tiles
234+ if ( ! TryGetCurrentTile ( uid , out var tileRef , out _ ) || tileRef . Tile . IsEmpty )
218235 return false ;
236+
219237 return _turf . IsTileBlocked ( tileRef , CollisionGroup . MobMask ) ;
220238 }
221239
@@ -237,7 +255,7 @@ private bool IsInSpace(EntityUid uid)
237255 }
238256
239257 public bool IsHidden ( EntityUid uid , CrawlUnderFloorComponent comp )
240- => comp . Enabled && ! IsOnSubfloor ( uid ) ;
258+ => comp . Enabled ; // No longer check for subfloor, just check if crawling is enabled
241259
242260 private void HandleCrawlTransition ( EntityUid uid , bool wasOnSubfloor , bool isOnSubfloor , CrawlUnderFloorComponent comp , bool causedByTileChange )
243261 {
@@ -260,15 +278,6 @@ private void HandleCrawlTransition(EntityUid uid, bool wasOnSubfloor, bool isOnS
260278 PlayDuendeSound ( uid , causedByTileChange ? 1f : 0.3f ) ;
261279 }
262280
263-
264- private static int GetHiddenMask ( int baseMask )
265- => baseMask
266- & ~ HiddenMask ;
267-
268- private static int GetHiddenLayer ( int baseLayer )
269- => baseLayer
270- & ~ HiddenLayer ;
271-
272281 private static int GetOrCacheBase < TKey > ( List < ( TKey , int ) > list , TKey key , int current )
273282 {
274283 var idx = list . FindIndex ( t => EqualityComparer < TKey > . Default . Equals ( t . Item1 , key ) ) ;
@@ -294,8 +303,39 @@ private void PryTileIfUnder(EntityUid uid, CrawlUnderFloorComponent comp)
294303 _tile . PryTile ( snapPos , gridUid ) ;
295304 }
296305
306+ private void UpdateCollisionMask ( EntityUid uid , bool stealthMode )
307+ {
308+ if ( ! TryComp < FixturesComponent > ( uid , out var fixtures ) )
309+ return ;
310+
311+ if ( stealthMode )
312+ {
313+ // In stealth mode, set to SmallMob collision to maintain some physics
314+ // while still allowing phasing through most objects
315+ foreach ( var ( id , fixture ) in fixtures . Fixtures )
316+ {
317+ _physics . SetCollisionMask ( uid , id , fixture , ( int ) CollisionGroup . SmallMobMask , fixtures ) ;
318+ _physics . SetCollisionLayer ( uid , id , fixture , ( int ) CollisionGroup . SmallMobLayer , fixtures ) ;
319+ }
320+ }
321+ else
322+ {
323+ // In normal mode, use standard mob collision
324+ foreach ( var ( id , fixture ) in fixtures . Fixtures )
325+ {
326+ _physics . SetCollisionMask ( uid , id , fixture , ( int ) CollisionGroup . MobMask , fixtures ) ;
327+ _physics . SetCollisionLayer ( uid , id , fixture , ( int ) CollisionGroup . MobLayer , fixtures ) ;
328+ }
329+ }
330+
331+ Dirty ( uid , fixtures ) ;
332+ }
333+
297334 private void SetStealth ( EntityUid uid , bool enabled )
298335 {
336+ // Update collision mask based on stealth state
337+ UpdateCollisionMask ( uid , enabled ) ;
338+
299339 // Evil hud overlay hiding shitcode that hijacks StealthComponent
300340 if ( enabled )
301341 {
@@ -324,9 +364,6 @@ private void RefreshCrawlSubfloorState(EntityUid uid, CrawlUnderFloorComponent c
324364 var old = comp . WasOnSubfloor ;
325365 comp . WasOnSubfloor = now ;
326366
327- if ( comp . Enabled && now != old )
328- UpdateSneakCollision ( uid , comp ) ;
329-
330367 HandleCrawlTransition ( uid , old , now , comp , causedByTileChange ) ;
331368 }
332369
0 commit comments