{"TotalCount":154,"Files":[{"Ident":"rue.house","Path":"Game/MansionGame.Music.cs","FileName":"MansionGame.Music.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"using Sandbox;\n\nnamespace BrickJam;\n\npublic sealed partial class MansionGame\n{\n\t/// \u003Csummary\u003EHow fast the music fades in/out (per second). Legacy faded on the server tick.\u003C/summary\u003E\n\tpublic float MusicVolumeChangeRate =\u003E 0.5f;\n\n\t/// \u003Csummary\u003ETarget music volume - background level, the tracks are mastered loud.\u003C/summary\u003E\n\tpublic float MusicVolume =\u003E 0.15f;\n\n\tprivate SoundHandle musicHandle;\n\tprivate LevelType musicLevel = LevelType.None;\n\tprivate float musicVolume;\n\n\t/// \u003Csummary\u003E\n\t/// Client-side music orchestration (every client, host included - music is local audio). Scene-System\n\t/// port of the legacy host-side \u003Cc\u003EProcessMusic\u003C/c\u003E: drive the track from the replicated\n\t/// \u003Csee cref=\u0022CurrentLevelType\u0022/\u003E and crossfade when the level changes.\n\t/// \u003C/summary\u003E\n\tprotected override void OnUpdate()\n\t{\n\t\tvar track = Level.GetMusic( CurrentLevelType );\n\n\t\tif ( CurrentLevelType != musicLevel )\n\t\t{\n\t\t\t// Level changed: fade the old track out, then swap once it\u0027s silent.\n\t\t\tmusicVolume = musicVolume.LerpTo( 0f, MusicVolumeChangeRate * Time.Delta );\n\n\t\t\tif ( musicVolume \u003C= 0.01f )\n\t\t\t{\n\t\t\t\tmusicHandle?.Stop();\n\t\t\t\tmusicHandle = null;\n\t\t\t\tmusicLevel = CurrentLevelType;\n\t\t\t\tmusicVolume = 0f;\n\t\t\t}\n\n\t\t\tApplyMusicVolume();\n\t\t\treturn;\n\t\t}\n\n\t\t// Same level: keep the track playing (restart if the asset isn\u0027t looped) and fade toward target.\n\t\tif ( !string.IsNullOrEmpty( track ) )\n\t\t{\n\t\t\tif ( musicHandle is null || musicHandle.IsStopped )\n\t\t\t{\n\t\t\t\tmusicHandle = Sound.Play( track );\n\t\t\t\tif ( musicHandle is not null )\n\t\t\t\t\tmusicHandle.Volume = musicVolume; // start at the current (faded) level, not full blast\n\t\t\t}\n\n\t\t\tmusicVolume = musicVolume.LerpTo( MusicVolume, MusicVolumeChangeRate * Time.Delta );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmusicVolume = musicVolume.LerpTo( 0f, MusicVolumeChangeRate * Time.Delta );\n\t\t\tif ( musicVolume \u003C= 0.01f \u0026\u0026 musicHandle is not null )\n\t\t\t{\n\t\t\t\tmusicHandle.Stop();\n\t\t\t\tmusicHandle = null;\n\t\t\t}\n\t\t}\n\n\t\tApplyMusicVolume();\n\t}\n\n\tprivate void ApplyMusicVolume()\n\t{\n\t\tif ( musicHandle is not null \u0026\u0026 !musicHandle.IsStopped )\n\t\t\tmusicHandle.Volume = musicVolume;\n\t}\n}\n"},{"Ident":"rue.house","Path":"Grid/AStarPathBuilder.cs","FileName":"AStarPathBuilder.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing Sandbox;\n\nnamespace GridAStar;\n\npublic struct AStarPathBuilder\n{\n\tpublic Grid Grid { get; private set; } = null;\n\tpublic List\u003Cstring\u003E TagsToExclude { get; private set; } = new() { \u0022occupied\u0022 };\n\tpublic bool HasTagsToExlude =\u003E TagsToExclude.Count() \u003E 0;\n\tpublic bool HasOccupiedTagToExclude =\u003E HasTagsToExlude ? TagsToExclude.Contains( \u0022occupied\u0022 ) : false;\n\tpublic List\u003Cstring\u003E TagsToInclude { get; private set; } = new();\n\tpublic bool HasTagsToInclude =\u003E TagsToInclude.Count() \u003E 0;\n\tpublic Dictionary\u003Cstring, float\u003E TagsToAvoid { get; private set; } = new();\n\tpublic bool HasTagsToAvoid =\u003E TagsToAvoid.Count() \u003E 0;\n\tpublic bool AcceptsPartial { get; private set; } = false;\n\tpublic float MaxCheckDistance { get; private set; } = float.PositiveInfinity;\n\tpublic float MaxDropHeight { get; private set; } = GridSettings.DEFAULT_DROP_HEIGHT;\n\tpublic Component PathCreator { get; private set; } = null;\n\tpublic bool HasPathCreator =\u003E PathCreator != null;\n\n\tpublic AStarPathBuilder() { }\n\tpublic AStarPathBuilder( Grid grid ) : this()\n\t{\n\t\tGrid = grid;\n\t}\n\n\tpublic static AStarPathBuilder From( Grid grid ) =\u003E new AStarPathBuilder( grid );\n\n\tpublic AStarPathBuilder WithTags( params string[] tags )\n\t{\n\t\tforeach ( var tag in tags )\n\t\t{\n\t\t\tif ( !TagsToInclude.Contains( tag ) )\n\t\t\t\tTagsToInclude.Add( tag );\n\t\t\tif ( TagsToExclude.Contains( tag ) )\n\t\t\t\tTagsToExclude.Remove( tag );\n\t\t}\n\t\treturn this;\n\t}\n\n\tpublic AStarPathBuilder WithoutTags( params string[] tags )\n\t{\n\t\tforeach ( var tag in tags )\n\t\t{\n\t\t\tif ( !TagsToExclude.Contains( tag ) )\n\t\t\t\tTagsToExclude.Add( tag );\n\t\t\tif ( TagsToInclude.Contains( tag ) )\n\t\t\t\tTagsToInclude.Remove( tag );\n\t\t}\n\t\treturn this;\n\t}\n\n\t/// \u003Csummary\u003E\n\t/// Which tags to avoid, when found it will add the malus to its total cost.\n\t/// \u003C/summary\u003E\n\tpublic AStarPathBuilder AvoidTag( string tag, float malus )\n\t{\n\t\tmalus = Math.Abs( malus );\n\n\t\tif ( !TagsToAvoid.ContainsKey( tag ) )\n\t\t\tTagsToAvoid.Add( tag, malus );\n\t\telse\n\t\t\tTagsToAvoid[tag] = malus;\n\n\t\treturn this;\n\t}\n\n\tpublic AStarPathBuilder WithMaxDistance( float maxDistance )\n\t{\n\t\tMaxCheckDistance = Math.Max( 0f, maxDistance );\n\t\treturn this;\n\t}\n\n\tpublic AStarPathBuilder WithMaxDropHeight( float maxDropHeight )\n\t{\n\t\tMaxDropHeight = Math.Min( Grid.MaxDropHeight, maxDropHeight );\n\t\treturn this;\n\t}\n\n\tpublic AStarPathBuilder WithPartialEnabled()\n\t{\n\t\tAcceptsPartial = true;\n\t\treturn this;\n\t}\n\n\tpublic AStarPathBuilder WithPathCreator( Component pathCreator )\n\t{\n\t\tPathCreator = pathCreator;\n\t\treturn this;\n\t}\n\n\tpublic AStarPath Run( Cell startingCell, Cell targetCell, bool reversed = false, bool withCellConnections = true )\n\t{\n\t\tif ( startingCell is null || targetCell is null || startingCell == targetCell ) return AStarPath.Empty();\n\n\t\treturn AStarPath.From( this, Grid.ComputePathInternal( this, startingCell, targetCell, CancellationToken.None, reversed, withCellConnections ) );\n\t}\n\tpublic AStarPath Run( Vector3 startingPosition, Cell targetCell, bool reversed = false, bool withCellConnections = true ) =\u003E Run( Grid.GetCell( startingPosition ), targetCell, reversed, withCellConnections );\n\tpublic AStarPath Run( Cell startingCell, Vector3 targetPosition, bool reversed = false, bool withCellConnections = true ) =\u003E Run( startingCell, Grid.GetCell( targetPosition ), reversed, withCellConnections );\n\tpublic AStarPath Run( Vector3 startingPosition, Vector3 targetPosition, bool reversed = false, bool withCellConnections = true ) =\u003E Run( Grid.GetCell( startingPosition ), Grid.GetCell( targetPosition ), reversed, withCellConnections );\n\n\tinternal AStarPath Run( Cell startingCell, Cell targetCell, CancellationToken token, bool reversed = false, bool withCellConnections = true )\n\t{\n\t\tif ( startingCell is null || targetCell is null || startingCell == targetCell ) return AStarPath.Empty();\n\n\t\treturn AStarPath.From( this, Grid.ComputePathInternal( this, startingCell, targetCell, token, reversed, withCellConnections ) );\n\t}\n\n\tpublic async Task\u003CAStarPath\u003E RunAsync( Cell startingCell, Cell targetCell, CancellationToken token, bool reversed = false, bool withCellConnections = true )\n\t{\n\t\tvar builder = this;\n\n\t\treturn await GameTask.RunInThreadAsync( () =\u003E builder.Run( startingCell, targetCell, token, reversed, withCellConnections ) );\n\t}\n\tpublic async Task\u003CAStarPath\u003E RunAsync( Vector3 startingPosition, Cell targetCell, CancellationToken token, bool reversed = false, bool withCellConnections = true ) =\u003E await RunAsync( Grid.GetCell( startingPosition ), targetCell, token, reversed, withCellConnections );\n\tpublic async Task\u003CAStarPath\u003E RunAsync( Cell startingCell, Vector3 targetPosition, CancellationToken token, bool reversed = false, bool withCellConnections = true ) =\u003E await RunAsync( startingCell, Grid.GetCell( targetPosition ), token, reversed, withCellConnections );\n\tpublic async Task\u003CAStarPath\u003E RunAsync( Vector3 startingPosition, Vector3 targetPosition, CancellationToken token, bool reversed = false, bool withCellConnections = true ) =\u003E await RunAsync( Grid.GetCell( startingPosition ), Grid.GetCell( targetPosition ), token, reversed, withCellConnections );\n\n\tpublic async Task\u003CAStarPath\u003E RunInParallel( Cell startingCell, Cell targetCell, CancellationTokenSource tokenSource, bool withCellConnections = true )\n\t{\n\t\tif ( startingCell is null || targetCell is null || startingCell == targetCell ) return AStarPath.Empty();\n\n\t\tvar fromTo = RunAsync( startingCell, targetCell, tokenSource.Token, false, withCellConnections );\n\t\tvar toFrom = RunAsync( targetCell, startingCell, tokenSource.Token, true, false ); // You can\u0027t reverse some cell connections, like dropping down\n\n\t\tvar pathResult = await GameTask.WhenAny( fromTo, toFrom ).Result;\n\n\t\t// Cancel the other task that hasn\u0027t finished yet.\n\t\ttokenSource.Cancel();\n\n\t\treturn pathResult;\n\t}\n\tpublic async Task\u003CAStarPath\u003E RunInParallel( Vector3 startingPosition, Cell targetCell, CancellationTokenSource tokenSource, bool withCellConnections = true ) =\u003E await RunInParallel( Grid.GetCell( startingPosition ), targetCell, tokenSource, withCellConnections );\n\tpublic async Task\u003CAStarPath\u003E RunInParallel( Cell startingCell, Vector3 targetPosition, CancellationTokenSource tokenSource, bool withCellConnections = true ) =\u003E await RunInParallel( startingCell, Grid.GetCell( targetPosition ), tokenSource, withCellConnections );\n\tpublic async Task\u003CAStarPath\u003E RunInParallel( Vector3 startingPosition, Vector3 targetPosition, CancellationTokenSource tokenSource, bool withCellConnections = true ) =\u003E await RunInParallel( Grid.GetCell( startingPosition ), Grid.GetCell( targetPosition ), tokenSource, withCellConnections );\n}\n"},{"Ident":"rue.house","Path":"Grid/IntVector2.cs","FileName":"IntVector2.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"using System;\nusing Sandbox;\n\nnamespace GridAStar;\n\n/// \u003Csummary\u003E\n/// Like a Vector2, but with integers instead.\n/// \u003C/summary\u003E\npublic struct IntVector2 : IEquatable\u003CIntVector2\u003E\n{\n\tpublic int x { get; set; }\n\tpublic int y { get; set; }\n\n\tpublic int this[int index]\n\t{\n\t\tget\n\t\t{\n\t\t\tint result = index switch\n\t\t\t{\n\t\t\t\t0 =\u003E x,\n\t\t\t\t1 =\u003E y,\n\t\t\t\t_ =\u003E throw new IndexOutOfRangeException(),\n\t\t\t};\n\n\t\t\treturn result;\n\t\t}\n\t\tset\n\t\t{\n\t\t\tswitch ( index )\n\t\t\t{\n\t\t\t\tcase 0:\n\t\t\t\t\tx = value;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\ty = value;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic IntVector2( int x, int y )\n\t{\n\t\tthis.x = x;\n\t\tthis.y = y;\n\t}\n\n\tpublic IntVector2 WithX( int x ) =\u003E new IntVector2( x, this.y );\n\tpublic IntVector2 WithY( int y ) =\u003E new IntVector2( this.x, y );\n\tpublic Vector2 ToVector2() =\u003E new Vector2( x, y );\n\tpublic float DistanceSquared( IntVector2 other ) =\u003E ToVector2().DistanceSquared( other.ToVector2() );\n\tpublic override string ToString() =\u003E $\u0022{x},{y}\u0022;\n\tpublic override bool Equals( object obj ) =\u003E obj is IntVector2 other \u0026\u0026 Equals( other );\n\tpublic bool Equals( IntVector2 other ) =\u003E x == other.x \u0026\u0026 y == other.y;\n\tpublic override int GetHashCode() =\u003E HashCode.Combine( x, y );\n\n\tpublic static bool operator ==( IntVector2 left, IntVector2 right ) =\u003E left.Equals( right );\n\tpublic static bool operator !=( IntVector2 left, IntVector2 right ) =\u003E !(left == right);\n\tpublic static IntVector2 operator \u002B( IntVector2 a, IntVector2 b ) =\u003E new IntVector2( a.x \u002B b.x, a.y \u002B b.y );\n\tpublic static IntVector2 operator -( IntVector2 a, IntVector2 b ) =\u003E new IntVector2( a.x - b.x, a.y - b.y );\n}\n"},{"Ident":"rue.house","Path":"Levels/Level.cs","FileName":"Level.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"using System;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing System.Collections.Generic;\nusing Sandbox;\n\nnamespace BrickJam;\n\n/// \u003Csummary\u003E\n/// Level orchestration. The legacy \u003Cc\u003ELevel\u003C/c\u003E was an \u003Cc\u003EEntity\u003C/c\u003E (for replication); in the Scene\n/// System it is a plain server-side object owned by \u003Csee cref=\u0022MansionGame\u0022/\u003E. The single replicated\n/// fact clients need \u2014 the current level type \u2014 lives on the manager as\n/// \u003Csee cref=\u0022MansionGame.CurrentLevelType\u0022/\u003E.\n///\n/// DEFERRED: grid generation (\u003Cc\u003EGenerateGrid\u003C/c\u003E, needs the grid package), music\n/// (\u003Cc\u003EProcessMusic\u003C/c\u003E), and the black-screen transition \u002B event-log messages (UI system).\n/// \u003C/summary\u003E\npublic abstract partial class Level\n{\n\tpublic abstract LevelType Type { get; }\n\tpublic string Music =\u003E GetMusic( Type );\n\tpublic virtual BBox WorldBox =\u003E new( new Vector3( -100000f ), new Vector3( 100000f ) );\n\n\t/// \u003Csummary\u003E\n\t/// Music track per level. Single source of truth so the client-side music player\n\t/// (\u003Csee cref=\u0022MansionGame\u0022/\u003E) can resolve a track from the replicated \u003Csee cref=\u0022LevelType\u0022/\u003E\n\t/// without a host-side \u003Csee cref=\u0022Level\u0022/\u003E instance.\n\t/// \u003C/summary\u003E\n\tpublic static string GetMusic( LevelType type ) =\u003E type switch\n\t{\n\t\tLevelType.Shop =\u003E \u0022sounds/music/scary_quest_at_midnight.sound\u0022,\n\t\tLevelType.Mansion =\u003E \u0022sounds/music/looming_trees_in_eerie_woods.sound\u0022,\n\t\tLevelType.Dungeon =\u003E \u0022sounds/music/malevolent_sightings_in_the_room.sound\u0022,\n\t\tLevelType.Bathrooms =\u003E \u0022sounds/music/depths_and_terror.sound\u0022,\n\t\t_ =\u003E null,\n\t};\n\n\tpublic LegacyUsableComponent Exit { get; set; }\n\tpublic List\u003CNPC\u003E Monsters { get; } = new();\n\tpublic TimeSince SinceStarted { get; set; }\n\n\tpublic static bool GameIsEnding { get; set; }\n\n\tprotected Scene Scene =\u003E MansionGame.Instance.Scene;\n\n\tpublic virtual void Compute()\n\t{\n\t\tvar players = Scene.GetAllComponents\u003CPlayer\u003E().ToList();\n\t\tif ( players.Count \u003E 0 \u0026\u0026 players.All( x =\u003E !x.IsAlive ) \u0026\u0026 !GameIsEnding )\n\t\t{\n\t\t\tMansionGame.RestartGame();\n\t\t\tGameIsEnding = true;\n\t\t\t// TODO (UI): Eventlog \u0022Looks like everyone died, better luck next time!\u0022\n\t\t}\n\t}\n\n\tprotected void RespawnAll() =\u003E MansionGame.Instance.RespawnAll();\n\n\tpublic virtual async Task Start()\n\t{\n\t\tawait GameTask.Yield();\n\n\t\tRespawnAll();\n\n\t\t// Companion spawning for players who bought the upgrade.\n\t\tforeach ( var player in Scene.GetAllComponents\u003CPlayer\u003E().ToList() )\n\t\t{\n\t\t\tif ( player.HasUpgrade( \u0022Cartoony Sidekick\u0022 ) )\n\t\t\t{\n\t\t\t\tvar doob = NPC.Create\u003CDoob\u003E( player.WorldPosition, player.WorldRotation );\n\t\t\t\tdoob.Owner = player;\n\t\t\t\tplayer.Doob = doob;\n\n\t\t\t\t// Who\u0027s a Good Boy?: Doob summoned to protect this player.\n\t\t\t\tplayer.TrackAchievement( GameStats.AchGoodBoy );\n\t\t\t}\n\t\t}\n\n\t\tExit?.GameObject.Destroy();\n\n\t\tif ( Type == LevelType.Bathrooms )\n\t\t{\n\t\t\tvar finalDoors = Scene.GetAllComponents\u003CValidFinalDoorPosition\u003E().ToList();\n\t\t\tvar spot = MansionGame.Random.FromList( finalDoors, null );\n\t\t\tif ( spot is not null )\n\t\t\t\tExit = FinalDoor.Create( spot.WorldPosition, spot.WorldRotation );\n\t\t}\n\t\telse\n\t\t{\n\t\t\tvar trapdoors = Scene.GetAllComponents\u003CValidTrapdoorPosition\u003E().Where( x =\u003E x.LevelType == Type ).ToList();\n\t\t\tvar spot = MansionGame.Random.FromList( trapdoors, null );\n\t\t\tif ( spot is not null )\n\t\t\t\tExit = Trapdoor.Create( spot.WorldPosition, spot.WorldRotation );\n\t\t}\n\n\t\tforeach ( var spawner in Scene.GetAllComponents\u003CLootSpawner\u003E().Where( x =\u003E x.LevelType == Type ).ToList() )\n\t\t\tspawner.SpawnLoot();\n\n\t\tforeach ( var door in Scene.GetAllComponents\u003CDoor\u003E().Where( x =\u003E x.LevelType == Type ).ToList() )\n\t\t\tdoor.Close();\n\n\t\tawait GameTask.DelayRealtimeSeconds( 1f );\n\n\t\tawait GenerateGrid();\n\n\t\tGameIsEnding = false;\n\t\tSinceStarted = 0f; // reset the level clock (used by the Slipped on a Soap achievement)\n\t\tMansionGame.Instance.TimerStart();\n\t}\n\n\tpublic virtual async Task End()\n\t{\n\t\tawait GameTask.Yield();\n\n\t\tMansionGame.Instance?.ShowBlackScreen( 2f, 1f, 1f );\n\n\t\tExit?.GameObject.Destroy();\n\t\tExit = null;\n\n\t\tforeach ( var monster in Monsters.ToList() )\n\t\t\tRemoveMonster( monster );\n\n\t\tforeach ( var spawner in Scene.GetAllComponents\u003CLootSpawner\u003E().ToList() )\n\t\t\tspawner.DeleteLoot();\n\n\t\tforeach ( var door in Scene.GetAllComponents\u003CDoor\u003E().Where( x =\u003E WorldBox.Contains( x.WorldPosition ) ).ToList() )\n\t\t\tdoor.Close();\n\n\t\tforeach ( var loot in Scene.GetAllComponents\u003CLoot\u003E().ToList() )\n\t\t\tloot.GameObject.Destroy();\n\n\t\tMansionGame.Instance.TimerStop();\n\n\t\tforeach ( var doob in Scene.GetAllComponents\u003CDoob\u003E().ToList() )\n\t\t{\n\t\t\tif ( doob.Owner.IsValid() )\n\t\t\t\tdoob.Owner.Doob = null;\n\n\t\t\tdoob.GameObject.Destroy();\n\t\t}\n\t}\n\n\tpublic virtual void RegisterMonster( NPC monster ) =\u003E Monsters.Add( monster );\n\n\tpublic virtual void RemoveMonster( NPC monster )\n\t{\n\t\tMonsters.Remove( monster );\n\t\tif ( monster.IsValid() )\n\t\t\tmonster.GameObject.Destroy();\n\t}\n\n\tpublic static Type GetClrType( LevelType type ) =\u003E type switch\n\t{\n\t\tLevelType.Shop =\u003E typeof( ShopLevel ),\n\t\tLevelType.Mansion =\u003E typeof( MansionLevel ),\n\t\tLevelType.Dungeon =\u003E typeof( DungeonLevel ),\n\t\tLevelType.Bathrooms =\u003E typeof( BathroomsLevel ),\n\t\t_ =\u003E null,\n\t};\n}\n"},{"Ident":"rue.house","Path":"Levels/ShopLevel.cs","FileName":"ShopLevel.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"using System.Linq;\nusing System.Threading.Tasks;\nusing Sandbox;\n\nnamespace BrickJam;\n\npublic sealed class ShopLevel : Level\n{\n\tpublic override LevelType Type =\u003E LevelType.Shop;\n\n\tpublic override async Task Start()\n\t{\n\t\t// Shop is a safe hub - no base.Start() (no exit/monsters/loot/grid).\n\t\tawait GameTask.Yield();\n\n\t\tRespawnAll();\n\t\tMansionGame.Instance.TimerStop();\n\t}\n}\n"},{"Ident":"rue.house","Path":"NPC/Specter.cs","FileName":"Specter.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"using System;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Sandbox;\nusing GridAStar;\n\nnamespace BrickJam;\n\n/// \u003Csummary\u003E\n/// Specter monster - sinks into the floor and rises elsewhere. Scene-System port of legacy \u003Cc\u003ESpecter\u003C/c\u003E,\n/// now on grid A*: it chases directly, and on a long idle teleports to a random grid cell (sink \u2192 reposition\n/// \u2192 rise). The lamp light flicker (legacy CapsuleLightEntity) is deferred to the effects system.\n/// \u003C/summary\u003E\n[Title( \u0022Specter\u0022 )]\n[Category( \u0022NPC\u0022 )]\npublic sealed partial class Specter : NPC\n{\n\tpublic override string ModelPath { get; set; } = \u0022models/specter/specter.vmdl\u0022;\n\tpublic override float WalkSpeed { get; set; } = 120f;\n\tpublic override float RunSpeed { get; set; } = 320f;\n\tpublic override float MaxVisionAngle { get; set; } = 240f;\n\tpublic override float MaxVisionRange { get; set; } = 1000f;\n\tpublic override float MaxVisionRangeWhenChasing { get; set; } = 1000f;\n\tpublic override float MaxVisionAngleWhenChasing { get; set; } = 180f;\n\tpublic override float MaxRememberTime { get; set; } = 3f;\n\tpublic override string IdleSound =\u003E \u0022sounds/specter/spectermoan.sound\u0022;\n\tpublic override float IdleVolume =\u003E 1.5f;\n\tpublic override string AttackSound =\u003E \u0022sounds/specter/spectermoan.sound\u0022;\n\tpublic override float AttackVolume =\u003E 2f;\n\n\tpublic float TimeToTeleport =\u003E 2f;\n\tpublic bool IsLowering =\u003E LastTeleport \u003C= TimeToTeleport / 2f \u002B 0.5f;\n\tpublic bool IsRising =\u003E LastTeleport \u003C= TimeToTeleport \u002B 1f \u0026\u0026 LastTeleport \u003E TimeToTeleport / 2f \u002B 0.5f;\n\tpublic bool IsTeleporting =\u003E IsLowering || IsRising;\n\n\t[Sync] public TimeSince LastTeleport { get; set; } = 999f;\n\n\tprivate static readonly Color LampColor = new( 1f, 0.55f, 0.2f );\n\tprivate PointLight lamp;\n\n\tprotected override void OnStart()\n\t{\n\t\tbase.OnStart();\n\n\t\t// Atmospheric lamp light (legacy CapsuleLightEntity). Purely visual, so each client builds its OWN\n\t\t// non-networked light and flickers it locally in OnUpdate - NPC logic (Think) is host-only and the\n\t\t// flicker shouldn\u0027t depend on replicated state.\n\t\tvar lampGo = new GameObject( true, \u0022Lamp\u0022 ) { Parent = GameObject };\n\t\tlampGo.LocalPosition = Vector3.Up * 40f;\n\t\tlamp = lampGo.Components.Create\u003CPointLight\u003E();\n\t\tlamp.LightColor = LampColor;\n\t\tlamp.Radius = 350f;\n\t\tlamp.Shadows = false; // atmospheric glow - skip the (6-face) shadow pass for this point light\n\t}\n\n\tprotected override void OnUpdate()\n\t{\n\t\tif ( !lamp.IsValid() )\n\t\t\treturn;\n\n\t\t// Eerie flicker; fade the lamp out while the specter is sunk into the floor (teleporting).\n\t\tvar t = Time.Now;\n\t\tvar flicker = 0.7f \u002B 0.18f * MathF.Sin( t * 27f ) \u002B 0.12f * MathF.Sin( t * 11.3f );\n\t\tvar visible = IsTeleporting ? 0f : 1f;\n\n\t\tlamp.LightColor = LampColor * (flicker * visible * 4f);\n\t}\n\n\tpublic override void ComputeIdleAndSeek()\n\t{\n\t\t// While sinking/rising we don\u0027t make new decisions; ComputeMotion drives the vertical move.\n\t\tif ( IsTeleporting )\n\t\t\treturn;\n\n\t\tif ( InVision.Count \u003E 0 )\n\t\t{\n\t\t\tTarget = InVision.OrderBy( x =\u003E x.Key.WorldPosition.Distance( WorldPosition ) ).FirstOrDefault().Key;\n\n\t\t\tif ( Target is Player player \u0026\u0026 player.Doob.IsValid() )\n\t\t\t\tTarget = player.Doob;\n\n\t\t\tLastTarget = Target;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tTarget = null;\n\n\t\t\tif ( !IsFollowingPath \u0026\u0026 nextIdle \u0026\u0026 CurrentGrid is not null )\n\t\t\t{\n\t\t\t\tvar isLongIdle = MansionGame.Random.NextSingle() \u003C= 0.2f;\n\t\t\t\tvar allCells = CurrentGrid.AllCells.ToList();\n\n\t\t\t\tCell chosen = null;\n\t\t\t\tfor ( var tried = 0; tried \u003C 20 \u0026\u0026 allCells.Count \u003E 0; tried\u002B\u002B )\n\t\t\t\t{\n\t\t\t\t\tvar candidate = MansionGame.Random.FromList( allCells, null );\n\t\t\t\t\tif ( candidate is null )\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tvar dist = candidate.Position.Distance( WorldPosition );\n\t\t\t\t\tif ( isLongIdle ? dist \u003E= 1000f : (dist \u003E= 400f \u0026\u0026 dist \u003C= 1000f) )\n\t\t\t\t\t{\n\t\t\t\t\t\tchosen = candidate;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif ( chosen != null )\n\t\t\t\t{\n\t\t\t\t\tif ( isLongIdle )\n\t\t\t\t\t\tTeleport( chosen.Position );\n\t\t\t\t\telse\n\t\t\t\t\t\tNavigateTo( chosen );\n\t\t\t\t}\n\n\t\t\t\tnextIdle = MansionGame.Random.NextSingle() * 1f \u002B 1f;\n\t\t\t\tLastTarget = null;\n\t\t\t}\n\t\t}\n\n\t\tif ( Target.IsValid() \u0026\u0026 Target.WorldPosition.Distance( WorldPosition ) \u003C= KillRange )\n\t\t{\n\t\t\tif ( Target is Player p )\n\t\t\t\t_ = CatchPlayer( p );\n\t\t\telse if ( Target is Doob d )\n\t\t\t\t_ = CatchDoob( d );\n\t\t}\n\n\t\tif ( nextIdleSound \u0026\u0026 !string.IsNullOrEmpty( IdleSound ) )\n\t\t{\n\t\t\tSoundExtensions.BroadcastPlay( IdleSound, WorldPosition, IdleVolume );\n\t\t\tnextIdleSound = MansionGame.Random.NextSingle() * 4f \u002B 4f;\n\t\t}\n\t}\n\n\tpublic override void ComputeMotion()\n\t{\n\t\tif ( !IsTeleporting )\n\t\t{\n\t\t\tbase.ComputeMotion();\n\t\t\treturn;\n\t\t}\n\n\t\t// Sink into / rise out of the floor under manual control (off the grid).\n\t\tif ( IsLowering )\n\t\t\tWorldPosition \u002B= Vector3.Down * CollisionHeight * Time.Delta / (TimeToTeleport / 2f);\n\t\telse if ( IsRising )\n\t\t\tWorldPosition \u002B= Vector3.Up * CollisionHeight * Time.Delta / (TimeToTeleport / 2f);\n\t}\n\n\tpublic async void Teleport( Vector3 position )\n\t{\n\t\tLastTeleport = 0;\n\t\tMansionGame.Instance?.PlayEffect( \u0022prefabs/particles/specter_teleport.prefab\u0022, WorldPosition, Rotation.Identity );\n\n\t\tawait Task.DelayRealtimeSeconds( TimeToTeleport * 0.5f \u002B 0.5f );\n\n\t\tWorldPosition = position \u002B Vector3.Down * CollisionHeight;\n\t}\n}\n"},{"Ident":"rue.house","Path":"UI/GroundLootPanel.razor","FileName":"GroundLootPanel.razor","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"@using System\n@using Sandbox\n@using Sandbox.UI\n@namespace BrickJam.UI\n@inherits PanelComponent\n\n\u003Croot\u003E\n\t@if ( Loot.IsValid() )\n\t{\n\t\t\u003Cdiv class=\u0022container\u0022\u003E\n\t\t\t\u003Cspan\u003E@Loot.FullName\u003C/span\u003E\n\t\t\t\u003Cdiv class=\u0022row\u0022\u003E\n\t\t\t\t\u003Cspan class=\u0022currency\u0022\u003E$\u003C/span\u003E\u003Cspan\u003E@($\u0022{Loot.MonetaryValue:n0}\u0022)\u003C/span\u003E\n\t\t\t\u003C/div\u003E\n\t\t\u003C/div\u003E\n\t}\n\u003C/root\u003E\n\n@code {\n\tprivate Loot loot;\n\tprivate Loot Loot =\u003E loot ??= GetComponentInParent\u003CLoot\u003E();\n\n\tprotected override int BuildHash() =\u003E HashCode.Combine( Loot?.FullName, Loot?.MonetaryValue ?? 0 );\n}\n\n\u003Cstyle\u003E\n\tGroundLootPanel {\n\t\ttransition: transform 0.5s ease-in-out;\n\t\tjustify-content: center;\n\t\talign-items: center;\n\t\twidth: 100%;\n\t\theight: 100%;\n\n\t\t.container {\n\t\t\tpadding: 10px;\n\t\t\tpadding-right: 30px;\n\t\t\tpadding-left: 30px;\n\t\t\tfont-size: 32px;\n\t\t\talign-items: center;\n\t\t\tfont-family: \u0022alagard\u0022;\n\t\t\tflex-direction: column;\n\t\t\tcolor: white;\n\t\t\ttext-shadow: 3px 3px 0px black;\n\t\t\tbackground: linear-gradient(to left, rgba(black, 0) 0%, rgba(black, 0.5) 20%, rgba(black, 0.5) 80%, rgba(black, 0) 100%);\n\n\t\t\t.currency {\n\t\t\t\tpadding-right: 4px;\n\t\t\t\tfont-size: 22px;\n\t\t\t\ttop: 5px;\n\t\t\t\tcolor: rgba(50, 205, 50, 1);\n\t\t\t}\n\t\t}\n\n\t\t\u0026:outro {\n\t\t\ttransform: scale(0);\n\t\t}\n\n\t\t\u0026:intro {\n\t\t\ttransform: scale(0);\n\t\t}\n\t}\n\u003C/style\u003E\n"},{"Ident":"rue.house","Path":"UI/Hud.razor","FileName":"Hud.razor","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"@using System\n@using Sandbox\n@using Sandbox.UI\n@namespace BrickJam.UI\n@inherits PanelComponent\n\n\u003Croot\u003E\n\t@{\n\t\tvar player = Player.Local;\n\n\t\tvar isSpectator = player?.Spectating ?? false;\n\t\tvar isPlayer = player?.IsValid() ?? false;\n\t}\n\n\t\u003CMoneyCounter /\u003E\n\t\u003CEventlog /\u003E\n\t\u003CPlayerCounter /\u003E\n\t\u003CStunIndicator /\u003E\n\t\u003CQuickPing /\u003E\n\t\u003CSubtitlesList /\u003E\n\t\u003CLockpicker /\u003E\n\t\u003CBlackScreen /\u003E\n\t\n\t\u003Cdiv class=\u0022input-hints\u0022\u003E\n\t\t@if ( isSpectator )\n\t\t{\n\t\t\t\u003Cdiv class=\u0022hint\u0022\u003E\u003Cinputglyph action=\u0022StopFollowing\u0022/\u003EStop following\u003C/div\u003E\n\t\t\t\u003Cdiv class=\u0022hint\u0022\u003E\u003Cinputglyph action=\u0022FollowNext\u0022/\u003ENext player\u003C/div\u003E\n\t\t\t\u003Cdiv class=\u0022hint\u0022\u003E\u003Cinputglyph action=\u0022FollowPrevious\u0022/\u003EPrevious player\u003C/div\u003E\n\t\t}\n\t\telse\n\t\t{\n\t\t\t\u003Cdiv class=\u0022hint\u0022\u003E\u003Cinputglyph action=\u0022inventory\u0022/\u003EInventory\u003C/div\u003E\n\t\t\t\u003Cdiv class=\u0022hint\u0022\u003E\u003Cinputglyph action=\u0022chat\u0022/\u003EChat\u003C/div\u003E\n\t\t\t\u003Cdiv class=\u0022hint\u0022\u003E\u003Cinputglyph action=\u0022ping\u0022/\u003EQuick Ping\u003C/div\u003E\n\t\t}\n\t\u003C/div\u003E\n\n\t@if ( player.IsValid() )\n\t{\n\t\t\u003CInventory /\u003E\n\t\t\u003CShop /\u003E\n\t\t\u003CTextInput Ghost=\u0022Say something...\u0022 class=\u0022chat-input\u0022 @ref=\u0022ChatInput\u0022 onsubmit=@OnChatSubmit /\u003E\n\n\t\t@if ( !player.SeenTips )\n\t\t{\n\t\t\t\u003CTutorialTips /\u003E\n\t\t}\n\t}\n\n\t@if ( player.IsValid() \u0026\u0026 player.IsAlive \u0026\u0026 !LockpickerBus.IsOpen )\n\t{\n\t\t\u003Cdiv class=\u0022crosshair\u0022\u003E\n\t\t\t\u003Cdiv class=\u0022mark\u0022\u003E\u002B\u003C/div\u003E\n\t\t\t\u003CRadialProgress /\u003E\n\t\t\t\u003CInteractionTip /\u003E\n\t\t\u003C/div\u003E\n\t}\n\n\t@if ( player.IsValid() \u0026\u0026 !player.IsAlive )\n\t{\n\t\t\u003Cdiv class=\u0022death\u0022\u003E\n\t\t\t\u003Cspan class=\u0022title\u0022\u003EYOU\u0027RE DEAD!\u003C/span\u003E\n\t\t\u003C/div\u003E\n\t}\n\u003C/root\u003E\n\n@code {\n\tprivate TextInput ChatInput { get; set; }\n\n\tprotected override void OnUpdate()\n\t{\n\t\tif ( ChatInput is null )\n\t\t\treturn;\n\n\t\tif ( Input.Pressed( \u0022chat\u0022 ) )\n\t\t{\n\t\t\tChatInput.Focus();\n\t\t\tChatInput.AddClass( \u0022visible\u0022 );\n\t\t}\n\t}\n\n\tprivate void OnChatSubmit()\n\t{\n\t\tif ( ChatInput is null )\n\t\t\treturn;\n\n\t\tvar text = ChatInput.Text;\n\t\tif ( !string.IsNullOrWhiteSpace( text ) )\n\t\t\tPlayer.Local?.Say( text );\n\n\t\tChatInput.Text = \u0022\u0022;\n\t\tChatInput.RemoveClass( \u0022visible\u0022 );\n\t}\n\n\tprotected override int BuildHash() =\u003E HashCode.Combine(\n\t\tPlayer.Local,\n\t\tPlayer.Local?.IsAlive ?? false,\n\t\tLockpickerBus.IsOpen );\n}\n\n\u003Cstyle\u003E\n\tHud {\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tfont-family: \u0022alagard\u0022;\n\t\tjustify-content: flex-start;\n\n\t\t.crosshair {\n\t\t\tposition: absolute;\n\t\t\ttop: 0; left: 0;\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\n\t\t\t.mark {\n\t\t\t\tcolor: white;\n\t\t\t\tfont-size: 28px;\n\t\t\t\ttext-shadow: 2px 2px 0px black;\n\t\t\t}\n\t\t}\n\n\t\t.input-hints {\n\t\t\tposition: absolute;\n\t\t\tbottom: 30px;\n\t\t\tright: 0px;\n\t\t\talign-items: flex-end;\n\t\t\tflex-direction: column;\n\t\t\tz-index: 3;\n\n\t\t\t.hint {\n\t\t\t\tcolor: white;\n\t\t\t\theight: 32px;\n\t\t\t\tjustify-content: center;\n\t\t\t\tfont-size: 32px;\n\t\t\t\tpadding-left: 50px;\n\t\t\t\tpadding-top: 10px;\n\t\t\t\tpadding-bottom: 40px;\n\t\t\t\tpadding-right: 10px;\n\t\t\t\tbackground: linear-gradient(to left, rgba(black, 0.5) 0%, rgba(black, 0.2) 75%, rgba(black, 0) 100%);\n\t\t\t\tmargin-top: 5px;\n\t\t\t\ttext-shadow: 3px 3px 0px black;\n\n\t\t\t\tInputGlyph {\n\t\t\t\t\twidth: 32px;\n\t\t\t\t\taspect-ratio: 1;\n\t\t\t\t\tmargin-right: 10px;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t.chat-input {\n\t\t\tposition: absolute;\n\t\t\twidth: 450px;\n\t\t\tmin-height: 20px;\n\t\t\tfont-size: 24px;\n\t\t\topacity: 0;\n\t\t\tpointer-events: all;\n\t\t\tbackground-color: rgba(37, 64, 98, 1);\n\t\t\tbox-shadow: 3px 3px 0px 0px rgba(17, 44, 78, 1);\n\t\t\ttransition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;\n\t\t\ttransform: translate(-50% 200px);\n\t\t\ttext-shadow: 3px 3px 0px black;\n\t\t\tleft: 50%;\n\t\t\ttop: 50%;\n\t\t\tmargin-top: 40px;\n\n\t\t\t\u0026.visible {\n\t\t\t\topacity: 1;\n\t\t\t\ttransform: translate(-50% 0px);\n\t\t\t}\n\t\t}\n\n\t\t.death {\n\t\t\tposition: absolute;\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t\tleft: 0px;\n\t\t\ttop: 0px;\n\t\t\tcolor: white;\n\t\t\tfont-size: 32px;\n\t\t\tz-index: 2;\n\t\t\tbackdrop-filter: grayscale(100%);\n\t\t\tjustify-content: center;\n\t\t\ttransition: opacity 1s ease-in-out;\n\t\t\topacity: 0;\n\t\t\ttext-shadow: 4px 4px 0px black;\n\n\t\t\t\u0026.visible {\n\t\t\t\topacity: 1;\n\t\t\t}\n\n\t\t\t.container {\n\t\t\t\tflex-direction: column;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\tspan {\n\t\t\t\tbackground: linear-gradient(to left, rgba(black, 0) 0%, rgba(black, 0.4) 20%, rgba(black, 0.4) 80%, rgba(black, 0) 100%);\n\t\t\t\tpadding: 10px;\n\t\t\t\tpadding-left: 40px;\n\t\t\t\tpadding-right: 40px;\n\t\t\t}\n\n\t\t\t.title {\n\t\t\t\tcolor: red;\n\t\t\t\tfont-size: 64px;\n\t\t\t\tmargin-top: 150px;\n\t\t\t\tmargin-bottom: 20px;\n\t\t\t}\n\t\t}\n\t}\n\u003C/style\u003E\n"},{"Ident":"rue.house","Path":"UI/Nametag.razor","FileName":"Nametag.razor","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"@using System\n@using Sandbox\n@using Sandbox.UI\n@using WorldPanel = Sandbox.WorldPanel\n@namespace BrickJam.UI\n@inherits PanelComponent\n\n\u003Croot\u003E\n\t@if ( !IsLocal \u0026\u0026 Player.IsValid() \u0026\u0026 Player.IsAlive )\n\t{\n\t\t\u003Cdiv class=\u0022container\u0022\u003E\n\t\t\t\u003Cspan\u003E@Name\u003C/span\u003E\n\t\t\u003C/div\u003E\n\t}\n\u003C/root\u003E\n\n@code {\n\tprivate Player player;\n\tprivate Player Player =\u003E player ??= GetComponentInParent\u003CPlayer\u003E();\n\n\t// Don\u0027t draw your own nametag in first person.\n\tprivate bool IsLocal =\u003E Player == Player.Local;\n\n\tprivate string Name =\u003E Player.IsValid() \u0026\u0026 Player.Network.Owner is { } owner ? owner.DisplayName : \u0022player\u0022;\n\n\tprotected override int BuildHash() =\u003E HashCode.Combine( IsLocal, Name, Player?.IsAlive ?? false );\n}\n\n\u003Cstyle\u003E\n\tNametag {\n\t\ttransition: transform 0.5s ease-in-out;\n\t\tjustify-content: center;\n\t\talign-items: center;\n\t\twidth: 100%;\n\t\theight: 100%;\n\n\t\t.container {\n\t\t\tpadding: 10px;\n\t\t\tpadding-right: 30px;\n\t\t\tpadding-left: 30px;\n\t\t\tfont-size: 32px;\n\t\t\talign-items: center;\n\t\t\tfont-family: \u0022alagard\u0022;\n\t\t\tflex-direction: column;\n\t\t\tcolor: white;\n\t\t\ttext-shadow: 3px 3px 0px black;\n\t\t\tbackground: linear-gradient(to left, rgba(black, 0) 0%, rgba(black, 0.5) 20%, rgba(black, 0.5) 80%, rgba(black, 0) 100%);\n\t\t}\n\n\t\t\u0026:outro {\n\t\t\ttransform: scale(0);\n\t\t}\n\n\t\t\u0026:intro {\n\t\t\ttransform: scale(0);\n\t\t}\n\t}\n\u003C/style\u003E\n"},{"Ident":"rue.house","Path":"UI/PingBus.cs","FileName":"PingBus.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"using System;\nusing Sandbox;\n\nnamespace BrickJam.UI;\n\n/// \u003Csummary\u003EDecouples gameplay from the Razor \u003Cc\u003EQuickPing\u003C/c\u003E panel (see \u003Csee cref=\u0022EventlogBus\u0022/\u003E).\u003C/summary\u003E\npublic static class PingBus\n{\n\tpublic enum PingType { Enemy, Exit, Loot, Other }\n\n\tpublic static event Action\u003CVector3, PingType\u003E OnPing;\n\n\tpublic static void Post( Vector3 worldPosition, PingType type ) =\u003E OnPing?.Invoke( worldPosition, type );\n}\n"},{"Ident":"rue.house","Path":"UI/SubtitlesList.razor","FileName":"SubtitlesList.razor","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"@using System\n@using System.Linq\n@using System.Collections.Generic\n@using Sandbox\n@using Sandbox.UI\n@namespace BrickJam.UI\n@inherits Panel\n\n\u003Croot\u003E\n\t@foreach ( var line in lines )\n\t{\n\t\t\u003Cdiv class=\u0022subtitle\u0022\u003E\n\t\t\t@if ( !string.IsNullOrEmpty( line.Speaker ) )\n\t\t\t{\n\t\t\t\t\u003Cspan class=\u0022speaker\u0022\u003E@line.Speaker:\u003C/span\u003E\n\t\t\t}\n\t\t\t\u003Cspan class=\u0022text\u0022\u003E@line.Text\u003C/span\u003E\n\t\t\u003C/div\u003E\n\t}\n\u003C/root\u003E\n\n@code {\n\tprivate readonly List\u003C(string Speaker, string Text, TimeUntil Expire)\u003E lines = new();\n\n\tpublic SubtitlesList()\n\t{\n\t\tSubtitleBus.OnSubtitle \u002B= Add;\n\t}\n\n\tpublic override void OnDeleted()\n\t{\n\t\tbase.OnDeleted();\n\t\tSubtitleBus.OnSubtitle -= Add;\n\t}\n\n\tprivate void Add( string speaker, string text, float duration )\n\t{\n\t\tlines.Add( (speaker, text, duration) );\n\t\tif ( lines.Count \u003E 3 )\n\t\t\tlines.RemoveAt( 0 );\n\t}\n\n\tpublic override void Tick()\n\t{\n\t\tlines.RemoveAll( l =\u003E l.Expire );\n\t}\n\n\tprotected override int BuildHash() =\u003E HashCode.Combine( lines.Count, lines.LastOrDefault().Text );\n}\n\n\u003Cstyle\u003E\n\tSubtitlesList {\n\t\tposition: absolute;\n\t\tleft: 0; right: 0;\n\t\tbottom: 60px;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\n\t\t.subtitle {\n\t\t\tflex-direction: row;\n\t\t\tpadding: 6px 16px;\n\t\t\tmargin-top: 4px;\n\t\t\tbackground-color: rgba(0,0,0,0.6);\n\t\t\tfont-size: 26px;\n\t\t\ttext-shadow: 2px 2px 0px black;\n\n\t\t\t.speaker { color: rgba(255,220,80,1); margin-right: 8px; }\n\t\t\t.text { color: white; }\n\t\t}\n\t}\n\u003C/style\u003E\n"},{"Ident":"rue.house","Path":"ui/controls/switchcontrol.razor.scss","FileName":"switchcontrol.razor.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"\r\n.switchcontrol\r\n{\r\n    flex-direction: row;\r\n    width: 100px;\r\n    min-height: 24px;\r\n    align-items: center;\r\n    cursor: pointer;\r\n\r\n    .switch-frame\r\n    {\r\n        flex-grow: 0;\r\n        flex-shrink: 1;\r\n        width: 48px;\r\n        height: 16px;\r\n        background-color: #fff1;\r\n        margin: 0px 5px;\r\n        align-items: center;\r\n        border-radius: 100px;\r\n        transition: all 0.4s linear;\r\n\r\n        .switch-inner\r\n        {\r\n            position: relative;\r\n            flex-grow: 0;\r\n            flex-shrink: 1;\r\n            background-color: #999;\r\n            width: 25px;\r\n            height: 25px;\r\n            border-radius: 100px;\r\n            left: 20%;\r\n            transform: translateX( -50% );\r\n            transition: all 0.3s ease-out;\r\n        }\r\n    }\r\n\r\n    \u0026.active\r\n    {\r\n        .switch-frame\r\n        {\r\n            background-color: #fffa;\r\n        }\r\n\r\n        .switch-inner\r\n        {\r\n            left: 80%;\r\n            background-color: #fff;\r\n        }\r\n    }\r\n}\r\n"},{"Ident":"rue.house","Path":"styles/form.scss","FileName":"form.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"\r\n$form-control-height: 28px !default;\r\n\r\n@import \u0022form/_checkbox.scss\u0022;\r\n@import \u0022form/_switch.scss\u0022;\r\n@import \u0022form/_dropdown.scss\u0022;\r\n@import \u0022form/_coloreditor.scss\u0022;\r\n@import \u0022form/_colorproperty.scss\u0022;\r\n\r\n.form\r\n{\r\n\tflex-direction: column;\r\n\talign-items: stretch;\r\n\tjustify-content: flex-start;\r\n\toverflow: scroll;\r\n}\r\n\r\n.field-group\r\n{\r\n\tflex-direction: column;\r\n\tflex-shrink: 0;\r\n}\r\n\r\n.field-header\r\n{\r\n\tflex-shrink: 0;\r\n}\r\n\r\n.field\r\n{\r\n\tcolor: white;\r\n\tfont-size: 14px;\r\n\tflex-shrink: 0;\r\n\tflex-grow: 0;\r\n\r\n\t\u003E .label\r\n\t{\r\n\t\tflex-grow: 0;\r\n\t\tflex-shrink: 0;\r\n\t\tfont-weight: 600;\r\n\t\topacity: 0.4;\r\n\t\twidth: 20%;\r\n\t\tfont-size: 13px;\r\n\t}\r\n\r\n\t\u003E .control\r\n\t{\r\n\t\tflex-shrink: 0;\r\n\t\tflex-grow: 1;\r\n\t\tflex-direction: column;\r\n\t}\r\n}\r\n\r\n.is-vertical \u003E .field, .field.is-vertical\r\n{\r\n\tflex-direction: column;\r\n\r\n\t\u003E .label\r\n\t{\r\n\t\twidth: auto;\r\n\t\theight: auto;\r\n\t}\r\n}"},{"Ident":"rue.house","Path":"ui/controls/color/colorpickercontrol.cs.scss","FileName":"colorpickercontrol.cs.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"ColorPickerControl\r\n{\r\n\tflex-direction: column;\r\n\tflex-shrink: 0;\r\n\tgap: 0.5rem;\r\n\tmargin: 1rem;\r\n}"},{"Ident":"rue.house","Path":"ui/controls/enumcontrol.cs.scss","FileName":"enumcontrol.cs.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"EnumControl\r\n{\r\n\tgap: 2px;\r\n\tflex-grow: 1;\r\n}\r\n\r\nEnumControl DropDown,\r\nEnumControl ButtonGroup\r\n{\r\n\tborder-radius: 8px;\r\n\tbackground-color: #000a;\r\n\tflex-grow: 1;\r\n}\r\n\r\nEnumControl DropDown\r\n{\r\n\tflex-grow: 1;\r\n\tmin-height: 32px;\r\n}\r\n\r\nEnumControl ButtonGroup\r\n{\r\n\tborder-radius: 12px;\r\n\toverflow: hidden;\r\n\tmin-height: 32px;\r\n\r\n\tButton\r\n\t{\r\n\t\tflex-grow: 1;\r\n\t\tjustify-content: center;\r\n\t\talign-items: center;\r\n\t\tgap: 4px;\r\n\t\tcolor: #aaa;\r\n\t\tfont-size: 1rem;\r\n\t\tcursor: pointer;\r\n\r\n\t\t.icon\r\n\t\t{\r\n\t\t\tcolor: #08f;\r\n\t\t}\r\n\r\n\t\t\u0026:hover\r\n\t\t{\r\n\t\t\tcolor: #ddd;\r\n\r\n\t\t\t.icon\r\n\t\t\t{\r\n\t\t\t\tcolor: #3af;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\u0026:active\r\n\t\t{\r\n\t\t\tbackground-color: #04a;\r\n\t\t\tcolor: white;\r\n\t\t\ttransform: translateX( 1px ) translateY( 1px );\r\n\r\n\t\t\t.icon\r\n\t\t\t{\r\n\t\t\t\tcolor: #fff;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t\u0026.active\r\n\t\t{\r\n\t\t\tbackground-color: #08f;\r\n\t\t\tcolor: white;\r\n\t\t\tpointer-events: none;\r\n\r\n\t\t\t.icon\r\n\t\t\t{\r\n\t\t\t\tcolor: #fff;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}"},{"Ident":"rue.house","Path":"ui/controls/color/coloralphacontrol.cs.scss","FileName":"coloralphacontrol.cs.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"ColorAlphaControl\r\n{\r\n\tgap: 0.5rem;\r\n\tflex-grow: 1;\r\n\tpointer-events: all;\r\n\tbackground: linear-gradient( to right, black, white );\r\n\tborder-radius: 4px;\r\n\tpadding: 2px;\r\n\theight: 12px;\r\n\tposition: relative;\r\n\tcursor: pointer;\r\n\tborder: 1px solid #333;\r\n\r\n\t\u0026:hover\r\n\t{\r\n\t\tborder: 1px solid #08f;\r\n\t}\r\n\r\n\t\u0026:active\r\n\t{\r\n\t\tborder: 1px solid #fff;\r\n\t}\r\n\r\n\t.handle\r\n\t{\r\n\t\ttop: -5px;\r\n\t\tbottom: -5px;\r\n\t\taspect-ratio: 1;\r\n\t\tborder-radius: 100px;\r\n\t\tborder: 2px solid #444;\r\n\t\tposition: absolute;\r\n\t\tbackground-color: white;\r\n\t\tbox-shadow: 2px 2px 16px #000a;\r\n\t\ttransform: translateX( -50% );\r\n\t\tpointer-events: none;\r\n\t}\r\n}\r\n"},{"Ident":"rue.house","Path":"ui/controls/color/colorsaturationvaluecontrol.cs.scss","FileName":"colorsaturationvaluecontrol.cs.scss","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"ColorSaturationValueControl\r\n{\r\n\twidth: 240px;\r\n\theight: 240px;\r\n\tbackground-color: red;\r\n\tposition: relative;\r\n\tborder-radius: 4px;\r\n\tcursor: pointer;\r\n\tborder: 1px solid #333;\r\n\r\n\t\u0026:hover\r\n\t{\r\n\t\tborder: 1px solid #08f;\r\n\t}\r\n\r\n\t\u0026:active\r\n\t{\r\n\t\tborder: 1px solid #fff;\r\n\t}\r\n\r\n\t.handle\r\n\t{\r\n\t\twidth: 16px;\r\n\t\theight: 16px;\r\n\t\tborder-radius: 100px;\r\n\t\tborder: 2px solid #444;\r\n\t\tposition: absolute;\r\n\t\tbackground-color: white;\r\n\t\tbox-shadow: 2px 2px 16px #000a;\r\n\t\ttransform: translateX( -50% ) translateY( -50% );\r\n\t\tpointer-events: none;\r\n\t\tz-index: 100;\r\n\t\tz-index: 100;\r\n\t}\r\n\r\n\t.gradient\r\n\t{\r\n\t\tposition: absolute;\r\n\t\twidth: 100%;\r\n\t\theight: 100%;\r\n\t\tborder-radius: 4px;\r\n\t\tbackground: linear-gradient( to right, white, rgba( 255, 255, 255, 0 ) );\r\n\r\n\t\t\u0026:after\r\n\t\t{\r\n\t\t\tcontent: \u0022\u0022;\r\n\t\t\tposition: absolute;\r\n\t\t\twidth: 100%;\r\n\t\t\theight: 100%;\r\n\t\t\tborder-radius: 4px;\r\n\t\t\tbackground: linear-gradient( to top, black, rgba( 0, 0, 0, 0 ) );\r\n\t\t}\r\n\t}\r\n}\r\n"},{"Ident":"rue.house","Path":"Grid/Grid.cs","FileName":"Grid.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing Sandbox;\n\nnamespace GridAStar;\n\npublic partial class Grid : IValid\n{\n\tpublic static Grid Main\n\t{\n\t\tget =\u003E Grids.GetValueOrDefault( \u0022main\u0022 );\n\t\tset\n\t\t{\n\t\t\tif ( Grids.ContainsKey( \u0022main\u0022 ) )\n\t\t\t\tGrids[\u0022main\u0022] = value;\n\t\t\telse\n\t\t\t\tGrids.Add( \u0022main\u0022, value );\n\t\t}\n\t}\n\n\tpublic static Dictionary\u003Cstring, Grid\u003E Grids { get; set; } = new();\n\n\t/// \u003Csummary\u003EThe scene this grid traces against. Set on creation (Scene-System port).\u003C/summary\u003E\n\tpublic Scene Scene { get; set; }\n\n\t// --- Generation diagnostics ---\n\tpublic static int DebugCastsHit;\n\tpublic static int DebugAngleRejected;\n\tpublic static int DebugOutOfBounds;\n\n\tpublic GridBuilder Settings { get; internal set; }\n\tpublic string Identifier =\u003E Settings.Identifier;\n\tpublic Dictionary\u003CIntVector2, List\u003CCell\u003E\u003E CellStacks { get; internal set; } = new();\n\tpublic IEnumerable\u003CCell\u003E AllCells =\u003E CellStacks.Values.SelectMany( list =\u003E list );\n\tpublic Vector3 Position =\u003E Settings.Position;\n\tpublic BBox Bounds =\u003E Settings.Bounds;\n\tpublic BBox RotatedBounds =\u003E Bounds.GetRotatedBounds( Rotation );\n\tpublic BBox WorldBounds =\u003E RotatedBounds.Translate( Position );\n\tpublic Transform Transform =\u003E new Transform( WorldBounds.Center, AxisRotation );\n\tpublic Rotation Rotation =\u003E Settings.Rotation;\n\tpublic bool AxisAligned =\u003E Settings.AxisAligned;\n\tpublic float StandableAngle =\u003E Settings.StandableAngle;\n\tpublic float StepSize =\u003E Settings.StepSize;\n\tpublic float CellSize =\u003E Settings.CellSize;\n\tpublic float HeightClearance =\u003E Settings.HeightClearance;\n\tpublic float WidthClearance =\u003E Settings.WidthClearance;\n\tpublic bool GridPerfect =\u003E Settings.GridPerfect;\n\tpublic bool StaticOnly =\u003E Settings.StaticOnly;\n\tpublic float MaxDropHeight =\u003E Settings.MaxDropHeight;\n\tpublic List\u003CJumpDefinition\u003E JumpDefinitions =\u003E Settings.JumpDefinitions;\n\tpublic int MinNeighbourCount =\u003E Settings.MinNeighbourCount;\n\tpublic bool IgnoreConnectionsForJumps =\u003E Settings.IgnoreConnectionsForJumps;\n\tpublic bool IgnoreLOSForJumps =\u003E Settings.IgnoreLOSForJumps;\n\tpublic bool CylinderShaped =\u003E Settings.CylinderShaped;\n\tpublic float Tolerance =\u003E GridPerfect ? 0.001f : 0f;\n\tpublic Rotation AxisRotation =\u003E AxisAligned ? new Rotation() : Rotation;\n\tpublic int MinimumColumn =\u003E WorldBounds.Mins.ToIntVector2( CellSize ).y;\n\tpublic int MaximumColumn =\u003E WorldBounds.Maxs.ToIntVector2( CellSize ).y;\n\tpublic int Columns =\u003E MaximumColumn - MinimumColumn;\n\tpublic int MinimumRow =\u003E WorldBounds.Mins.ToIntVector2( CellSize ).x;\n\tpublic int MaximumRow =\u003E WorldBounds.Maxs.ToIntVector2( CellSize ).x;\n\tpublic int Rows =\u003E MaximumRow - MinimumRow;\n\tbool IValid.IsValid { get; }\n\n\tpublic Grid()\n\t{\n\t\tSettings = new GridBuilder();\n\t}\n\n\tpublic Grid( GridBuilder settings )\n\t{\n\t\tSettings = settings;\n\t}\n\n\tpublic void Print( string message ) =\u003E Print( Identifier, message );\n\tpublic static void Print( string identifier, string message ) =\u003E Log.Info( $\u0022Grid \u0027{identifier}\u0027: {message}\u0022 );\n\n\tpublic BBox ToWorld( BBox bounds ) =\u003E bounds.GetRotatedBounds( AxisRotation ).Translate( WorldBounds.Center );\n\tpublic BBox ToLocal( BBox bounds ) =\u003E bounds.GetRotatedBounds( AxisRotation.Inverse ).Translate( -WorldBounds.Center );\n\n\tpublic IntVector2 PositionToCoordinates( Vector3 position ) =\u003E (position - WorldBounds.Mins - CellSize / 2).ToIntVector2( CellSize );\n\n\t/// \u003Csummary\u003EFind the nearest cell from a position even if outside the grid (expensive).\u003C/summary\u003E\n\tpublic Cell GetNearestCell( Vector3 position, bool onlyBelow = true, bool unoccupiedOnly = false )\n\t{\n\t\tvar validCells = AllCells;\n\n\t\tif ( unoccupiedOnly )\n\t\t\tvalidCells = validCells.Where( x =\u003E !x.Occupied );\n\t\tif ( onlyBelow )\n\t\t\tvalidCells = validCells.Where( x =\u003E x.Vertices.Min() - Math.Max( HeightClearance, StepSize ) \u003C= position.z );\n\n\t\treturn validCells.OrderBy( x =\u003E x.Position.DistanceSquared( position ) )\n\t\t\t.FirstOrDefault();\n\t}\n\n\tpublic Cell GetCellInArea( Vector3 position, float width, bool onlyBelow = true, bool withinStepRange = true )\n\t{\n\t\tvar cellsToCheck = (int)Math.Ceiling( width / CellSize ) * 2;\n\t\tfor ( int y = 0; y \u003C= cellsToCheck; y\u002B\u002B )\n\t\t{\n\t\t\tvar spiralY = MathAStar.SpiralPattern( y );\n\t\t\tfor ( int x = 0; x \u003C= cellsToCheck; x\u002B\u002B )\n\t\t\t{\n\t\t\t\tvar spiralX = MathAStar.SpiralPattern( x );\n\t\t\t\tvar cellFound = GetCell( position \u002B AxisRotation.Forward * spiralX * CellSize \u002B AxisRotation.Right * spiralY * CellSize \u002B Vector3.Up * StepSize, onlyBelow );\n\n\t\t\t\tif ( cellFound == null ) continue;\n\n\t\t\t\tif ( withinStepRange )\n\t\t\t\t\tif ( position.z - cellFound.Position.z \u003C= Math.Max( HeightClearance, StepSize ) ) return cellFound; else continue;\n\n\t\t\t\treturn cellFound;\n\t\t\t}\n\t\t}\n\n\t\treturn null;\n\t}\n\n\tpublic Cell GetCell( Vector3 position, bool onlyBelow = true ) =\u003E GetCell( PositionToCoordinates( position ), onlyBelow ? position.z : WorldBounds.Maxs.z );\n\n\tpublic Cell GetCell( IntVector2 coordinates, float height )\n\t{\n\t\tvar cellsAtCoordinates = CellStacks.GetValueOrDefault( coordinates );\n\n\t\tif ( cellsAtCoordinates == null ) return null;\n\n\t\t// Return the cell CLOSEST to the query height among the candidates, not the first match. A column on\n\t\t// a spiral staircase / multi-floor area stacks several cells at the same XY; the original first-match\n\t\t// returned an arbitrary one (often the bottom of the spiral), so an NPC partway up got a path from the\n\t\t// wrong height and couldn\u0027t follow it. Candidate window (\u003C= height \u002B clearance) is unchanged.\n\t\tCell best = null;\n\t\tvar bestDist = float.MaxValue;\n\n\t\tforeach ( var cell in cellsAtCoordinates )\n\t\t{\n\t\t\tif ( cell.Vertices.Min() - Math.Max( HeightClearance, StepSize ) \u003E= height )\n\t\t\t\tcontinue;\n\n\t\t\tvar dist = Math.Abs( cell.Position.z - height );\n\t\t\tif ( dist \u003C bestDist )\n\t\t\t{\n\t\t\t\tbestDist = dist;\n\t\t\t\tbest = cell;\n\t\t\t}\n\t\t}\n\n\t\treturn best;\n\t}\n\n\tpublic void AddCell( Cell cell )\n\t{\n\t\tif ( cell == null ) return;\n\t\tvar coordinates = cell.GridPosition;\n\t\tif ( !CellStacks.ContainsKey( coordinates ) )\n\t\t\tCellStacks.Add( coordinates, new List\u003CCell\u003E() { cell } );\n\t\telse\n\t\t\tif ( !CellStacks[coordinates].Any( x =\u003E Math.Abs( x.Position.z - cell.Position.z ) \u003C Math.Max( HeightClearance, StepSize ) ) )\n\t\t\tCellStacks[coordinates].Add( cell );\n\t}\n\n\tpublic Cell GetCellInDirection( Cell startingCell, Vector3 direction, int numOfCellsInDirection = 1 ) =\u003E GetCell( startingCell.Position \u002B direction * CellSize * numOfCellsInDirection );\n\n\tpublic Cell GetNeighbourInDirection( Cell cell, Vector3 direction )\n\t{\n\t\tvar horizontalDirection = direction.WithZ( 0 ).Normal;\n\t\tvar localCoordinates = horizontalDirection.ToIntVector2();\n\t\tvar coordinatesToCheck = cell.GridPosition \u002B localCoordinates;\n\n\t\tvar cellsAtCoordinates = CellStacks.GetValueOrDefault( coordinatesToCheck );\n\n\t\tif ( cellsAtCoordinates == null ) return null;\n\n\t\tforeach ( var cellAtCoordinate in cellsAtCoordinates )\n\t\t\tif ( cell.IsNeighbour( cellAtCoordinate ) \u0026\u0026 cell != cellAtCoordinate )\n\t\t\t\treturn cellAtCoordinate;\n\n\t\treturn null;\n\t}\n\n\t/// \u003Csummary\u003EReturns if there\u0027s a valid, unoccupied, and direct line of sight from a cell to another\u003C/summary\u003E\n\tpublic bool LineOfSight( Cell startingCell, Cell endingCell, Component pathCreator = null, bool debugShow = false )\n\t{\n\t\tvar startingPosition = startingCell.Position;\n\t\tvar endingPosition = endingCell.Position;\n\t\tvar distanceInSteps = (int)Math.Ceiling( startingPosition.Distance( endingPosition ) / CellSize );\n\n\t\tif ( pathCreator == null \u0026\u0026 startingCell.Occupied ) return false;\n\t\tif ( pathCreator != null \u0026\u0026 startingCell.Occupied \u0026\u0026 startingCell.OccupyingEntity != pathCreator ) return false;\n\n\t\tif ( pathCreator == null \u0026\u0026 endingCell.Occupied ) return false;\n\t\tif ( pathCreator != null \u0026\u0026 endingCell.Occupied \u0026\u0026 endingCell.OccupyingEntity != pathCreator ) return false;\n\n\t\tCell lastCell = startingCell;\n\t\tfor ( int i = 0; i \u003C= distanceInSteps; i\u002B\u002B )\n\t\t{\n\t\t\tvar direction = (endingPosition - lastCell.Position).Normal;\n\t\t\tvar cellToCheck = GetNeighbourInDirection( lastCell, direction );\n\n\t\t\tif ( cellToCheck == null ) return false;\n\t\t\tif ( cellToCheck == endingCell ) return true;\n\t\t\tif ( cellToCheck == lastCell ) continue;\n\t\t\tif ( pathCreator == null \u0026\u0026 cellToCheck.Occupied ) return false;\n\t\t\tif ( pathCreator != null \u0026\u0026 cellToCheck.Occupied \u0026\u0026 cellToCheck.OccupyingEntity != pathCreator ) return false;\n\t\t\tif ( !cellToCheck.IsNeighbour( lastCell ) ) return false;\n\n\t\t\tlastCell = cellToCheck;\n\n\t\t\tif ( debugShow )\n\t\t\t\tlastCell.Draw( 2f, false, false, false );\n\t\t}\n\n\t\treturn true;\n\t}\n\n\t/// \u003Csummary\u003ECan you roughly walk towards the cell without it being a direct line of sight\u003C/summary\u003E\n\tpublic bool IsDirectlyWalkable( Cell startingCell, Cell endingCell, float maxDistanceFromDirectPath = 150f, Component pathCreator = null, bool withConnections = true )\n\t{\n\t\tif ( startingCell == null || endingCell == null ) return false;\n\n\t\tvar currentCell = startingCell;\n\t\tvar directPath = new Line( startingCell.Position.WithZ( 0 ), endingCell.Position.WithZ( 0 ) );\n\t\tList\u003CCell\u003E cellsChecked = new();\n\n\t\tif ( pathCreator == null \u0026\u0026 startingCell.Occupied ) return false;\n\t\tif ( pathCreator != null \u0026\u0026 startingCell.Occupied \u0026\u0026 startingCell.OccupyingEntity != pathCreator ) return false;\n\n\t\tif ( pathCreator == null \u0026\u0026 endingCell.Occupied ) return false;\n\t\tif ( pathCreator != null \u0026\u0026 endingCell.Occupied \u0026\u0026 endingCell.OccupyingEntity != pathCreator ) return false;\n\n\t\twhile ( currentCell != endingCell \u0026\u0026 directPath.Distance( currentCell.Position.WithZ( 0 ) ) \u003C= maxDistanceFromDirectPath )\n\t\t{\n\t\t\tvar cellToCheck = withConnections ? currentCell.GetClosestNeighbourAndConnection( endingCell.Position ) : currentCell.GetClosestNeighbour( endingCell.Position );\n\n\t\t\tif ( cellToCheck == null ) return false;\n\t\t\tif ( cellsChecked.Contains( cellToCheck ) ) return false;\n\t\t\tif ( pathCreator == null \u0026\u0026 cellToCheck.Occupied ) return false;\n\t\t\tif ( pathCreator != null \u0026\u0026 cellToCheck.Occupied \u0026\u0026 cellToCheck.OccupyingEntity != pathCreator ) return false;\n\n\t\t\tif ( cellToCheck == endingCell ) return true;\n\n\t\t\tcellsChecked.Add( currentCell );\n\t\t\tcurrentCell = cellToCheck;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\tpublic bool IsInsideBounds( Vector3 point ) =\u003E Bounds.IsRotatedPointWithinBounds( Position, point, Rotation );\n\tpublic bool IsInsideCylinder( Vector3 point ) =\u003E Bounds.IsInsideSquishedRotatedCylinder( Position, point, Rotation );\n\n\tpublic void Initialize()\n\t{\n\t\tif ( Grids.ContainsKey( Identifier ) )\n\t\t{\n\t\t\tif ( Grids[Identifier] != null )\n\t\t\t\tGrids[Identifier].Delete( true );\n\n\t\t\tGrids[Identifier] = this;\n\t\t}\n\t\telse\n\t\t\tGrids.Add( Identifier, this );\n\t}\n\n\tpublic void Delete( bool deleteSave = false )\n\t{\n\t\tif ( Grids.ContainsKey( Identifier ) )\n\t\t{\n\t\t\tGrids[Identifier] = null;\n\t\t\tGrids.Remove( Identifier );\n\t\t}\n\t}\n\n\tpublic List\u003CCell\u003E GetCellsInBBox( BBox bbox )\n\t{\n\t\tvar cells = new List\u003CCell\u003E();\n\n\t\tforeach ( var cell in AllCells )\n\t\t\tif ( bbox.Contains( cell.Position ) )\n\t\t\t\tcells.Add( cell );\n\n\t\treturn cells;\n\t}\n\n\tpublic override int GetHashCode() =\u003E Settings.GetHashCode();\n\n\t/// \u003Csummary\u003EGives the edge tag to all cells with less than 8 neighbours\u003C/summary\u003E\n\tpublic async Task AssignEdgeCells( int maxNeighourCount = 8, int threadsToUse = 1, bool clearTags = false, string tagToExclude = \u0022\u0022, string tagToAssign = \u0022edge\u0022 ) =\u003E await assignEdgeCellsInternal( AllCells.ToList(), maxNeighourCount, threadsToUse, clearTags, tagToExclude, tagToAssign );\n\n\tpublic async Task AssignEdgeCells( BBox bounds, int maxNeighourCount = 8, int threadsToUse = 1, bool clearTags = false, string tagToExclude = \u0022\u0022, string tagToAssign = \u0022edge\u0022 ) =\u003E await assignEdgeCellsInternal( GetCellsInBBox( bounds ), maxNeighourCount, threadsToUse, clearTags, tagToExclude, tagToAssign );\n\n\tinternal async Task assignEdgeCellsInternal( List\u003CCell\u003E cells, int maxNeighourCount = 8, int threadsToUse = 1, bool clearTags = false, string tagToExclude = \u0022\u0022, string tagToAssign = \u0022edge\u0022 )\n\t{\n\t\tvar cellsCount = cells.Count();\n\t\tthreadsToUse = Math.Max( 1, threadsToUse );\n\t\tvar cellsEachThread = (int)(cellsCount / threadsToUse);\n\t\tvar lastThreadCount = cellsCount - (cellsEachThread * (threadsToUse - 1));\n\t\tList\u003CTask\u003E tasks = new();\n\n\t\tfor ( int i = 0; i \u003C threadsToUse; i\u002B\u002B )\n\t\t{\n\t\t\tvar curentThread = i;\n\n\t\t\ttasks.Add( GameTask.RunInThreadAsync( () =\u003E\n\t\t\t{\n\t\t\t\tvar cellsRange = curentThread == threadsToUse - 1 ? cellsEachThread : lastThreadCount;\n\t\t\t\tvar cellsToCheck = cells.Skip( cellsEachThread * curentThread ).Take( cellsRange );\n\n\t\t\t\tforeach ( var cell in cellsToCheck )\n\t\t\t\t{\n\t\t\t\t\tif ( clearTags )\n\t\t\t\t\t\tcell.Tags.Remove( tagToAssign );\n\n\t\t\t\t\tvar neighbours = cell.GetNeighbours();\n\n\t\t\t\t\tif ( tagToExclude != \u0022\u0022 )\n\t\t\t\t\t\tneighbours = neighbours.Where( x =\u003E !x.Tags.Has( tagToExclude ) );\n\n\t\t\t\t\tif ( neighbours.Count() \u003C maxNeighourCount )\n\t\t\t\t\t\tcell.Tags.Add( tagToAssign );\n\t\t\t\t}\n\t\t\t} ) );\n\t\t}\n\n\t\tawait GameTask.WhenAll( tasks );\n\t}\n\n\t/// \u003Csummary\u003EAdds the droppable connection to cells you can drop from\u003C/summary\u003E\n\tpublic async Task AssignDroppableCells( int threadsToUse = 1 ) =\u003E await internalAssignDroppableCells( CellsWithTag( \u0022edge\u0022 ).ToList(), threadsToUse );\n\n\tpublic async Task AssignDroppableCells( BBox bounds, int threadsToUse = 1 ) =\u003E await internalAssignDroppableCells( CellsWithTag( bounds, \u0022edge\u0022 ).ToList(), threadsToUse );\n\n\tinternal async Task internalAssignDroppableCells( List\u003CCell\u003E cells, int threadsToUse = 1 )\n\t{\n\t\tvar allCells = cells;\n\t\tvar cellsCount = allCells.Count();\n\t\tthreadsToUse = Math.Max( 1, threadsToUse );\n\t\tvar cellsEachThread = (int)(cellsCount / threadsToUse);\n\t\tvar lastThreadCount = cellsCount - (cellsEachThread * (threadsToUse - 1));\n\t\tList\u003CTask\u003E tasks = new();\n\n\t\tfor ( int i = 0; i \u003C threadsToUse; i\u002B\u002B )\n\t\t{\n\t\t\tvar curentThread = i;\n\n\t\t\ttasks.Add( GameTask.RunInThreadAsync( () =\u003E\n\t\t\t{\n\t\t\t\tvar cellsRange = curentThread == threadsToUse - 1 ? cellsEachThread : lastThreadCount;\n\t\t\t\tvar cellsToCheck = allCells.Skip( cellsEachThread * curentThread ).Take( cellsRange );\n\n\t\t\t\tforeach ( var cell in cellsToCheck )\n\t\t\t\t{\n\t\t\t\t\tvar droppableCell = cell.GetFirstValidDroppable( maxHeightDistance: MaxDropHeight );\n\t\t\t\t\tif ( droppableCell != null )\n\t\t\t\t\t\tcell.AddConnection( droppableCell, \u0022drop\u0022 );\n\t\t\t\t}\n\t\t\t} ) );\n\t\t}\n\n\t\tawait GameTask.WhenAll( tasks );\n\t}\n\n\tpublic IEnumerable\u003CCell\u003E JumpableCandidates()\n\t{\n\t\tvar droppedCells = CellsWithConnection( \u0022drop\u0022 ).SelectMany( cell =\u003E cell.GetConnections( \u0022drop\u0022 ).Select( connection =\u003E connection.Current ) );\n\t\treturn CellsWithTag( \u0022edge\u0022 ).Concat( droppedCells );\n\t}\n\n\tpublic async Task AssignJumpableCells( JumpDefinition definition, int threadsToUse = 16 ) =\u003E await internalAssignJumpableCells( JumpableCandidates().ToList(), definition, threadsToUse );\n\n\tinternal async Task internalAssignJumpableCells( List\u003CCell\u003E cells, JumpDefinition definition, int threadsToUse = 16 )\n\t{\n\t\tvar allCells = cells;\n\t\tvar cellsCount = allCells.Count();\n\t\tthreadsToUse = Math.Max( 1, threadsToUse );\n\t\tvar cellsEachThread = (int)(cellsCount / threadsToUse);\n\t\tvar lastThreadCount = cellsCount - (cellsEachThread * (threadsToUse - 1));\n\t\tList\u003CTask\u003E tasks = new();\n\n\t\tfor ( int i = 0; i \u003C threadsToUse; i\u002B\u002B )\n\t\t{\n\t\t\tvar curentThread = i;\n\n\t\t\ttasks.Add( GameTask.RunInThreadAsync( () =\u003E\n\t\t\t{\n\t\t\t\tvar totalFraction = 1f;\n\t\t\t\tvar cellsRange = curentThread == threadsToUse - 1 ? cellsEachThread : lastThreadCount;\n\t\t\t\tvar cellsToCheck = allCells.Skip( cellsEachThread * curentThread ).Take( cellsRange );\n\n\t\t\t\tforeach ( var cell in cellsToCheck )\n\t\t\t\t{\n\t\t\t\t\tif ( totalFraction \u003E= 1f )\n\t\t\t\t\t{\n\t\t\t\t\t\tList\u003CCell\u003E connectedCells = new();\n\t\t\t\t\t\tList\u003CAStarNode\u003E jumpConnections = new();\n\n\t\t\t\t\t\tforeach ( var jumpableCell in cell.GetValidJumpables( definition, MaxDropHeight, IgnoreConnectionsForJumps, IgnoreLOSForJumps ) )\n\t\t\t\t\t\t\tif ( jumpableCell != null )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tjumpConnections.Add( cell.AddConnection( jumpableCell, definition.Name ) );\n\t\t\t\t\t\t\t\tconnectedCells.Add( jumpableCell );\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\tforeach ( var jumpableConnection in connectedCells )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar direction = (cell.Position - jumpableConnection.Position).WithZ( 0 ).Normal;\n\t\t\t\t\t\t\tvar jumpbackCell = jumpableConnection.GetValidJumpable( definition, direction, MaxDropHeight, IgnoreConnectionsForJumps, IgnoreLOSForJumps );\n\n\t\t\t\t\t\t\tif ( jumpbackCell != null )\n\t\t\t\t\t\t\t\tif ( !IsDirectlyWalkable( jumpbackCell, cell ) )\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\tvar duplicate = false;\n\t\t\t\t\t\t\t\t\tforeach ( var connection in jumpConnections )\n\t\t\t\t\t\t\t\t\t\tif ( connection.Parent.Current == jumpbackCell \u0026\u0026 connection.MovementTag == definition.Name )\n\t\t\t\t\t\t\t\t\t\t\tduplicate = true;\n\t\t\t\t\t\t\t\t\tif ( !duplicate )\n\t\t\t\t\t\t\t\t\t\tjumpConnections.Add( jumpableConnection.AddConnection( jumpbackCell, definition.Name ) );\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tforeach ( var connection in jumpConnections )\n\t\t\t\t\t\t\tif ( LineOfSight( connection.Parent.Current, connection.Current ) )\n\t\t\t\t\t\t\t\tconnection.Parent.Current.RemoveConnection( connection );\n\n\t\t\t\t\t\ttotalFraction = 0f;\n\t\t\t\t\t}\n\n\t\t\t\t\ttotalFraction \u002B= definition.GenerateFraction;\n\t\t\t\t}\n\t\t\t} ) );\n\t\t}\n\n\t\tawait GameTask.WhenAll( tasks );\n\t}\n\n\tpublic Vector3 TraceParabola( Vector3 startingPosition, Vector3 horizontalVelocity, float verticalSpeed, float gravity, float maxDropHeight, int subSteps = 2 )\n\t{\n\t\tvar horizontalDirection = horizontalVelocity.WithZ( 0 ).Normal;\n\t\tvar horizontalSpeed = horizontalVelocity.WithZ( 0 ).Length;\n\t\tvar maxHeight = startingPosition.z \u002B MathAStar.ParabolaMaxHeight( verticalSpeed, gravity );\n\t\tvar minHeight = maxHeight - maxDropHeight;\n\t\tvar currentDistance = 1;\n\t\tvar lastPositionChecked = startingPosition;\n\n\t\twhile ( lastPositionChecked.z \u003E= minHeight )\n\t\t{\n\t\t\tvar horizontalOffset = CellSize * currentDistance / subSteps;\n\t\t\tvar verticalOffset = MathAStar.ParabolaHeight( horizontalOffset, horizontalSpeed, verticalSpeed, gravity );\n\t\t\tvar nextPositionToCheck = startingPosition \u002B horizontalDirection * horizontalOffset \u002B Vector3.Up * verticalOffset;\n\n\t\t\tvar clearanceBBox = new BBox( new Vector3( -WidthClearance / 2f, -WidthClearance / 2f, StepSize ), new Vector3( WidthClearance / 2f, WidthClearance / 2f, HeightClearance ) );\n\t\t\tvar jumpTrace = Scene.Trace.Box( clearanceBBox, lastPositionChecked, nextPositionToCheck )\n\t\t\t\t.WithGridSettings( Settings )\n\t\t\t\t.Run();\n\n\t\t\tif ( jumpTrace.Hit )\n\t\t\t\treturn jumpTrace.EndPosition;\n\n\t\t\tlastPositionChecked = nextPositionToCheck;\n\t\t\tcurrentDistance\u002B\u002B;\n\t\t}\n\n\t\treturn lastPositionChecked;\n\t}\n\n\tpublic void RemoveCells( BBox bounds, bool printInfo = false )\n\t{\n\t\tvar cellsToRemove = GetCellsInBBox( bounds );\n\t\tvar count = cellsToRemove.Count();\n\n\t\tforeach ( var cell in cellsToRemove )\n\t\t\tcell.Delete();\n\n\t\tif ( printInfo )\n\t\t\tPrint( $\u0022Removed {count} cells\u0022 );\n\t}\n\n\tpublic async Task GenerateCells( BBox bounds, int threadedChunkSides = 1, bool printInfo = true )\n\t{\n\t\tList\u003CTask\u003CList\u003CCell\u003E\u003E\u003E tasks = new();\n\t\tvar totalMins = bounds.Mins;\n\t\tvar totalMaxs = bounds.Maxs;\n\t\tvar totalSize = bounds.Size;\n\n\t\tthreadedChunkSides = Math.Max( 1, threadedChunkSides );\n\n\t\tfor ( int x = 1; x \u003C= threadedChunkSides; x\u002B\u002B )\n\t\t{\n\t\t\tfor ( int y = 1; y \u003C= threadedChunkSides; y\u002B\u002B )\n\t\t\t{\n\t\t\t\tvar xOffset = totalSize.x / threadedChunkSides * x - totalSize.x / threadedChunkSides / 2;\n\t\t\t\tvar yOffset = totalSize.y / threadedChunkSides * y - totalSize.y / threadedChunkSides / 2;\n\t\t\t\tvar offset = new Vector3( xOffset, yOffset );\n\t\t\t\tvar chunkSize = totalSize / threadedChunkSides;\n\t\t\t\tvar chunkMins = totalMins \u002B offset - chunkSize / 2;\n\t\t\t\tvar chunkMaxs = totalMins \u002B offset \u002B chunkSize / 2;\n\t\t\t\tvar dividedBounds = new BBox( chunkMins.WithZ( totalMins.z ), chunkMaxs.WithZ( totalMaxs.z ) );\n\n\t\t\t\ttasks.Add( GameTask.RunInThreadAsync( () =\u003E createCells( dividedBounds, printInfo ) ) );\n\t\t\t}\n\t\t}\n\n\t\tawait GameTask.WhenAll( tasks );\n\n\t\tforeach ( var task in tasks )\n\t\t\tforeach ( var cell in task.Result )\n\t\t\t\tAddCell( cell );\n\t}\n\n\t/// \u003Csummary\u003ECreate cells in that local bbox (Doesn\u0027t add them)\u003C/summary\u003E\n\tprivate List\u003CCell\u003E createCells( BBox bounds, bool printInfo = true )\n\t{\n\t\tvar generatedCells = new List\u003CCell\u003E();\n\n\t\tvar minimumGrid = bounds.Mins.ToIntVector2( CellSize );\n\t\tvar maximumGrid = bounds.Maxs.ToIntVector2( CellSize );\n\t\tvar startingColumn = minimumGrid.y - MinimumColumn;\n\t\tvar totalColumns = maximumGrid.y - minimumGrid.y;\n\t\tvar endingColumn = startingColumn \u002B totalColumns;\n\t\tvar startingRow = minimumGrid.x - MinimumRow;\n\t\tvar totalRows = maximumGrid.x - minimumGrid.x;\n\t\tvar endingRow = startingRow \u002B totalRows;\n\n\t\tfor ( int column = startingColumn; column \u003C endingColumn; column\u002B\u002B )\n\t\t{\n\t\t\tfor ( int row = startingRow; row \u003C endingRow; row\u002B\u002B )\n\t\t\t{\n\t\t\t\tvar startPosition = WorldBounds.Mins.WithZ( WorldBounds.Maxs.z ) \u002B new Vector3( row * CellSize \u002B CellSize / 2f, column * CellSize \u002B CellSize / 2f, Tolerance * 2f ) * AxisRotation;\n\t\t\t\tvar endPosition = WorldBounds.Mins \u002B new Vector3( row * CellSize \u002B CellSize / 2f, column * CellSize \u002B CellSize / 2f, -Tolerance ) * AxisRotation;\n\t\t\t\tvar checkBBox = new BBox( new Vector3( -CellSize / 2f \u002B Tolerance, -CellSize / 2f \u002B Tolerance, 0f ), new Vector3( CellSize / 2f - Tolerance, CellSize / 2f - Tolerance, 0.001f ) );\n\t\t\t\tvar positionTrace = Scene.Trace.Box( checkBBox, startPosition, endPosition )\n\t\t\t\t\t.WithGridSettings( Settings );\n\n\t\t\t\tvar positionResult = positionTrace.Run();\n\n\t\t\t\twhile ( positionResult.Hit \u0026\u0026 startPosition.z \u003E= endPosition.z )\n\t\t\t\t{\n\t\t\t\t\tDebugCastsHit\u002B\u002B;\n\t\t\t\t\tif ( IsInsideBounds( positionResult.HitPosition ) )\n\t\t\t\t\t{\n\t\t\t\t\t\tif ( !CylinderShaped || IsInsideCylinder( positionResult.HitPosition ) )\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tvar angle = Vector3.GetAngle( Vector3.Up, positionResult.Normal );\n\t\t\t\t\t\t\tif ( angle \u003C= StandableAngle )\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tvar newCell = Cell.TryCreate( this, positionResult.HitPosition );\n\n\t\t\t\t\t\t\t\tif ( newCell != null )\n\t\t\t\t\t\t\t\t\tgeneratedCells.Add( newCell );\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tDebugAngleRejected\u002B\u002B;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tDebugOutOfBounds\u002B\u002B;\n\t\t\t\t\t}\n\n\t\t\t\t\tstartPosition = positionResult.HitPosition \u002B Vector3.Down * HeightClearance;\n\n\t\t\t\t\t// Scene-System port of Sandbox.Trace.TestPoint: a zero-length sphere trace reports\n\t\t\t\t\t// StartedSolid when the point is inside geometry. Step down until we\u0027re clear.\n\t\t\t\t\twhile ( Scene.Trace.Sphere( CellSize / 2f - Tolerance, startPosition, startPosition ).Run().StartedSolid )\n\t\t\t\t\t\tstartPosition \u002B= Vector3.Down * HeightClearance;\n\n\t\t\t\t\tpositionTrace = Scene.Trace.Box( checkBBox, startPosition, endPosition )\n\t\t\t\t\t\t.WithGridSettings( Settings );\n\n\t\t\t\t\tpositionResult = positionTrace.Run();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn generatedCells;\n\t}\n\n\tpublic IEnumerable\u003CCell\u003E CellsWithTag( string tag ) =\u003E AllCells.Where( cell =\u003E cell.Tags.Has( tag ) );\n\tpublic IEnumerable\u003CCell\u003E CellsWithTags( params string[] tags ) =\u003E AllCells.Where( cell =\u003E cell.Tags.Has( tags ) );\n\tpublic IEnumerable\u003CCell\u003E CellsWithTags( List\u003Cstring\u003E tags ) =\u003E AllCells.Where( cell =\u003E cell.Tags.Has( tags ) );\n\tpublic IEnumerable\u003CCell\u003E CellsWithTag( BBox bounds, string tag ) =\u003E GetCellsInBBox( bounds ).Where( cell =\u003E cell.Tags.Has( tag ) );\n\tpublic IEnumerable\u003CCell\u003E CellsWithTags( BBox bounds, params string[] tags ) =\u003E GetCellsInBBox( bounds ).Where( cell =\u003E cell.Tags.Has( tags ) );\n\tpublic IEnumerable\u003CCell\u003E CellsWithTags( BBox bounds, List\u003Cstring\u003E tags ) =\u003E GetCellsInBBox( bounds ).Where( cell =\u003E cell.Tags.Has( tags ) );\n\tpublic IEnumerable\u003CCell\u003E CellsWithConnection( string movementTag ) =\u003E AllCells.Where( cell =\u003E cell.GetConnections( movementTag ).Count() \u003E 0 );\n\tpublic IEnumerable\u003CCell\u003E CellsWithConnection( BBox bounds, string movementTag ) =\u003E GetCellsInBBox( bounds ).Where( cell =\u003E cell.GetConnections( movementTag ).Count() \u003E 0 );\n\n\tpublic void CheckOccupancy( string tag )\n\t{\n\t\tforeach ( var cell in AllCells )\n\t\t\tcell.Occupied = cell.TestForOccupancy( tag );\n\t}\n}\n"},{"Ident":"rue.house","Path":"Grid/GridSettings.cs","FileName":"GridSettings.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"namespace GridAStar;\n\n// Set STEP_SIZE or WIDTH_CLEARANCE to 0 to disable them (faster grid generation)\npublic static partial class GridSettings\n{\n\tpublic const float DEFAULT_STANDABLE_ANGLE = 40f;   // How steep the terrain can be on a cell before it gets discarded\n\tpublic const float DEFAULT_STEP_SIZE = 12f;         // How big steps can be on a cell before it gets discarded\n\tpublic const float DEFAULT_CELL_SIZE = 16f;         // How large each cell will be in hammer units\n\tpublic const float DEFAULT_HEIGHT_CLEARANCE = 72f;  // How much vertical space there should be\n\tpublic const float DEFAULT_WIDTH_CLEARANCE = 24f;   // How much horizontal space there should be\n\tpublic const float DEFAULT_DROP_HEIGHT = 400f;      // How high you can drop down from\n\tpublic const bool DEFAULT_GRID_PERFECT = false;     // For grid-perfect terrain, if true it will not be checking for steps, so use ramps instead\n\tpublic const bool DEFAULT_STATIC_ONLY = true;       // Will it only hit world and static or also dynamic\n}\n"},{"Ident":"rue.house","Path":"Grid/TraceExtensions.cs","FileName":"TraceExtensions.cs","PackageType":"game","CodeKind":"Game","AssetVersionId":301999,"Code":"using Sandbox;\n\nnamespace GridAStar;\n\npublic static partial class TraceExtensions\n{\n\t/// \u003Csummary\u003E\n\t/// Apply a grid\u0027s generation filters to a scene trace. Scene-System port: legacy \u003Cc\u003EStaticOnly()\u003C/c\u003E\n\t/// becomes \u003Cc\u003EIgnoreDynamic()\u003C/c\u003E. Tag filters are only applied when non-empty (an empty\n\t/// \u003Cc\u003EWithAllTags\u003C/c\u003E would otherwise filter against nothing).\n\t/// \u003C/summary\u003E\n\tpublic static SceneTrace WithGridSettings( this SceneTrace self, GridBuilder settings )\n\t{\n\t\tif ( settings.StaticOnly )\n\t\t\tself = self.IgnoreDynamic();\n\n\t\t// ANY of the include tags (not all) - the floor may be tagged \u0022world\u0022 while props are \u0022solid\u0022, and\n\t\t// requiring both would match nothing. Legacy used WithAllTags but its maps used a single floor tag.\n\t\tif ( settings.TagsToInclude.Count \u003E 0 )\n\t\t\tself = self.WithAnyTags( settings.TagsToInclude.ToArray() );\n\n\t\tif ( settings.TagsToExclude.Count \u003E 0 )\n\t\t\tself = self.WithoutTags( settings.TagsToExclude.ToArray() );\n\n\t\treturn self;\n\t}\n}\n"}]}