Merge branch 'master' into tailstuff

This commit is contained in:
AeraAulin 2025-10-18 16:29:45 -07:00 committed by GitHub
commit da8804dd9d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
177 changed files with 79873 additions and 85867 deletions

View File

@ -0,0 +1,90 @@
using System.Numerics;
using Content.Shared.Alert.Components;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
namespace Content.Client.Alerts;
/// <summary>
/// This handles <see cref="GenericCounterAlertComponent"/>
/// </summary>
public sealed class GenericCounterAlertSystem : EntitySystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<GenericCounterAlertComponent, UpdateAlertSpriteEvent>(OnUpdateAlertSprite);
}
private void OnUpdateAlertSprite(Entity<GenericCounterAlertComponent> ent, ref UpdateAlertSpriteEvent args)
{
var sprite = args.SpriteViewEnt.Comp;
var ev = new GetGenericAlertCounterAmountEvent(args.Alert);
RaiseLocalEvent(args.ViewerEnt, ref ev);
if (!ev.Handled)
return;
// It cannot be null if its handled, but good to check to avoid ugly null ignores.
if (ev.Amount == null)
return;
// How many digits can we display
var maxDigitCount = GetMaxDigitCount((ent, ent, sprite));
// Clamp it to a positive number that we can actually display in full (no rollover to 0)
var amount = (int) Math.Clamp(ev.Amount.Value, 0, Math.Pow(10, maxDigitCount) - 1);
// This is super wack but ig it works?
var digitCount = ent.Comp.HideLeadingZeroes
? amount.ToString().Length
: maxDigitCount;
if (ent.Comp.HideLeadingZeroes)
{
for (var i = 0; i < ent.Comp.DigitKeys.Count; i++)
{
if (!_sprite.LayerMapTryGet(ent.Owner, ent.Comp.DigitKeys[i], out var layer, false))
continue;
_sprite.LayerSetVisible(ent.Owner, layer, i <= digitCount - 1);
}
}
// ReSharper disable once PossibleLossOfFraction
var baseOffset = (ent.Comp.AlertSize.X - digitCount * ent.Comp.GlyphWidth) / 2 * (1f / EyeManager.PixelsPerMeter);
for (var i = 0; i < ent.Comp.DigitKeys.Count; i++)
{
if (!_sprite.LayerMapTryGet(ent.Owner, ent.Comp.DigitKeys[i], out var layer, false))
continue;
var result = amount / (int) Math.Pow(10, i) % 10;
_sprite.LayerSetRsiState(ent.Owner, layer, result.ToString());
if (ent.Comp.CenterGlyph)
{
var offset = baseOffset + (digitCount - 1 - i) * ent.Comp.GlyphWidth * (1f / EyeManager.PixelsPerMeter);
_sprite.LayerSetOffset(ent.Owner, layer, new Vector2(offset, 0));
}
}
}
/// <summary>
/// Gets the number of digits that we can display.
/// </summary>
/// <returns>The number of digits.</returns>
private int GetMaxDigitCount(Entity<GenericCounterAlertComponent, SpriteComponent> ent)
{
for (var i = ent.Comp1.DigitKeys.Count - 1; i >= 0; i--)
{
if (_sprite.LayerExists((ent.Owner, ent.Comp2), ent.Comp1.DigitKeys[i]))
return i + 1;
}
return 0;
}
}

View File

@ -11,11 +11,14 @@ public record struct UpdateAlertSpriteEvent
{
public Entity<SpriteComponent> SpriteViewEnt;
public EntityUid ViewerEnt;
public AlertPrototype Alert;
public UpdateAlertSpriteEvent(Entity<SpriteComponent> spriteViewEnt, AlertPrototype alert)
public UpdateAlertSpriteEvent(Entity<SpriteComponent> spriteViewEnt, EntityUid viewerEnt, AlertPrototype alert)
{
SpriteViewEnt = spriteViewEnt;
ViewerEnt = viewerEnt;
Alert = alert;
}
}

View File

@ -1,4 +1,6 @@
using Content.Client.Alerts;
using Content.Shared.Alert;
using Content.Shared.Alert.Components;
using Content.Shared.Revenant;
using Content.Shared.Revenant.Components;
using Robust.Client.GameObjects;
@ -15,7 +17,7 @@ public sealed class RevenantSystem : EntitySystem
base.Initialize();
SubscribeLocalEvent<RevenantComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<RevenantComponent, UpdateAlertSpriteEvent>(OnUpdateAlert);
SubscribeLocalEvent<RevenantComponent, GetGenericAlertCounterAmountEvent>(OnGetCounterAmount);
}
private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref AppearanceChangeEvent args)
@ -40,14 +42,14 @@ public sealed class RevenantSystem : EntitySystem
}
}
private void OnUpdateAlert(Entity<RevenantComponent> ent, ref UpdateAlertSpriteEvent args)
private void OnGetCounterAmount(Entity<RevenantComponent> ent, ref GetGenericAlertCounterAmountEvent args)
{
if (args.Alert.ID != ent.Comp.EssenceAlert)
if (args.Handled)
return;
var essence = Math.Clamp(ent.Comp.Essence.Int(), 0, 999);
_sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit1, $"{(essence / 100) % 10}");
_sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit2, $"{(essence / 10) % 10}");
_sprite.LayerSetRsiState(args.SpriteViewEnt.AsNullable(), RevenantVisualLayers.Digit3, $"{essence % 10}");
if (ent.Comp.EssenceAlert != args.Alert)
return;
args.Amount = ent.Comp.Essence.Int();
}
}

View File

@ -1,4 +1,5 @@
using Content.Shared.CCVar;
using Content.Shared._DV.Mind; // DeltaV
using Content.Shared.CCVar; // DeltaV
using Content.Shared.Mind.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.NPC;
@ -35,6 +36,12 @@ public sealed class SSDIndicatorSystem : EntitySystem
TryComp<MindContainerComponent>(uid, out var mindContainer) &&
mindContainer.ShowExamineInfo)
{
// Begin DeltaV Addition
var ev = new ShowSSDIndicatorEvent();
RaiseLocalEvent(uid, ref ev);
if (ev.Hidden)
return;
// End DeltaV Addition
args.StatusIcons.Add(_prototype.Index(component.Icon));
}
}

View File

@ -98,7 +98,8 @@ public sealed class AlertsUIController : UIController, IOnStateEntered<GameplayS
if (!EntityManager.TryGetComponent<SpriteComponent>(spriteViewEnt, out var sprite))
return;
var ev = new UpdateAlertSpriteEvent((spriteViewEnt, sprite), alert);
var ev = new UpdateAlertSpriteEvent((spriteViewEnt, sprite), player, alert);
EntityManager.EventBus.RaiseLocalEvent(player, ref ev);
EntityManager.EventBus.RaiseLocalEvent(spriteViewEnt, ref ev);
}
}

View File

@ -57,10 +57,15 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls
_sprite = _entityManager.System<SpriteSystem>();
TooltipSupplier = SupplyTooltip;
Alert = alert;
HorizontalAlignment = HAlignment.Left;
_severity = severity;
_icon = new SpriteView
{
Scale = new Vector2(2, 2)
Scale = new Vector2(2, 2),
MaxSize = new Vector2(64, 64),
Stretch = SpriteView.StretchMode.None,
HorizontalAlignment = HAlignment.Left
};
SetupIcon();

View File

@ -0,0 +1,5 @@
using Content.Shared._DV.Abilities.Psionics;
namespace Content.Client._DV.Abilities.Psionics;
public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem;

View File

@ -0,0 +1,8 @@
using Content.Shared._DV.Grappling.EntitySystems;
namespace Content.Client._DV.Grappling.EntitySystems;
/// <summary>
/// Client side handling for grapplers.
/// </summary>
public sealed class GrapplingSystem : SharedGrapplingSystem;

View File

@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Client.UserInterface.Controls;
using Content.Shared._Mono.CorticalBorer;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client._Mono.CorticalBorer
{
[UsedImplicitly]
public sealed class CorticalBorerDispenserBoundUserInterface : BoundUserInterface
{
private CorticalBorerDispenserWindow? _window;
public CorticalBorerDispenserBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
_window = this.CreateWindow<CorticalBorerDispenserWindow>();
_window.SetInfoFromEntity(EntMan, Owner);
// Setup static button actions.
_window.AmountGrid.OnButtonPressed += s => SendMessage(new CorticalBorerDispenserSetInjectAmountMessage(s));
_window.OnDispenseReagentButtonPressed += id => SendMessage(new CorticalBorerDispenserInjectMessage(id));
}
/// <summary>
/// Update the UI each time new state data is sent from the server.
/// </summary>
/// <param name="state">
/// Data of the <see cref="ReagentDispenserComponent"/> that this UI represents.
/// Sent from the server.
/// </param>
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (CorticalBorerDispenserBoundUserInterfaceState) state;
_window?.UpdateState(castState);
}
}
}

View File

@ -0,0 +1,43 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:ui="clr-namespace:Content.Client.Chemistry.UI"
Title="{Loc 'reagent-dispenser-bound-user-interface-title'}"
MinSize="400 175"
SetSize="600 300">
<BoxContainer Orientation="Horizontal">
<BoxContainer Orientation="Vertical" MinWidth="170">
<Label Text="{Loc 'reagent-dispenser-window-amount-to-dispense-label'}" HorizontalAlignment="Center" />
<ui:ButtonGrid
Name="AmountGrid"
Access="Public"
Columns="3"
HorizontalAlignment="Center"
Margin="5"
ButtonList="1,5,10,15,20,25,30,50,100"
RadioGroup="True">
</ui:ButtonGrid>
</BoxContainer>
<SplitContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<ScrollContainer HScrollEnabled="False"
HorizontalExpand="True"
VerticalExpand="True"
MinHeight="50"
SizeFlagsStretchRatio="2.5">
<GridContainer Name="ReagentList"
HorizontalExpand="True"
VerticalExpand="True"
Access="Public"
Columns="3" />
</ScrollContainer>
<ScrollContainer HScrollEnabled="False"
HorizontalExpand="True"
VerticalExpand="True"
MinHeight="50">
</ScrollContainer>
</SplitContainer>
</BoxContainer>
</controls:FancyWindow>

View File

@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Client.UserInterface.Controls;
using Content.Shared._Mono.CorticalBorer;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._Mono.CorticalBorer;
[GenerateTypedNameReferences]
public sealed partial class CorticalBorerDispenserWindow : FancyWindow
{
public event Action<string>? OnDispenseReagentButtonPressed;
public CorticalBorerDispenserWindow()
{
RobustXamlLoader.Load(this);
}
public void UpdateState(BoundUserInterfaceState state)
{
var castState = (CorticalBorerDispenserBoundUserInterfaceState)state;
UpdateReagentsList(castState.DisList);
AmountGrid.Selected = castState.SelectedDispenseAmount.ToString();
}
public void UpdateReagentsList(List<CorticalBorerDispenserItem> chemicals)
{
if (ReagentList == null)
return;
ReagentList.Children.Clear();
chemicals.Sort((x, y) => x.ReagentName.CompareTo(y.ReagentName));
foreach (var chem in chemicals)
{
var card = new CorticalBorerReagentCardControl(chem);
card.OnPressed += OnDispenseReagentButtonPressed;
ReagentList.Children.Add(card);
}
}
}

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Shared._Mono.CorticalBorer;
using Content.Shared.Chemistry;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._Mono.CorticalBorer;
[GenerateTypedNameReferences]
public sealed partial class CorticalBorerReagentCardControl : Control
{
public Action<string>? OnPressed;
public CorticalBorerReagentCardControl(CorticalBorerDispenserItem set)
{
RobustXamlLoader.Load(this);
ColorPanel.PanelOverride = new StyleBoxFlat{ BackgroundColor = set.ReagentColor};
ReagentNameLabel.Text = set.ReagentName;
FillLabel.Text = Loc.GetString("cortical-borer-dispenser-window-cost", ("cost", set.Cost * set.Amount));
// disable the button if you can't afford it, makes it easier
MainButton.Disabled = set.Chems < set.Cost * set.Amount;
MainButton.OnPressed += args => OnPressed?.Invoke(set.ReagentId);
}
}

View File

@ -0,0 +1,27 @@
<Control xmlns="https://spacestation14.io" HorizontalExpand="True">
<BoxContainer Name="MainContainer"
Orientation="Horizontal"
HorizontalExpand="True">
<PanelContainer Name="ColorPanel"
VerticalExpand="True"
SetWidth="7"
Margin="0 1 0 0" />
<Button Name="MainButton"
HorizontalExpand="True"
VerticalExpand="True"
StyleClasses="ButtonSquare"
Margin="-1 0 0 0">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<BoxContainer Orientation="Vertical"
VerticalExpand="True"
HorizontalExpand="True"
Margin="-5 0 0 0">
<Label Name="ReagentNameLabel" />
<Label Name="FillLabel"
StyleClasses="LabelSubText"
Margin="0 -5 0 0" />
</BoxContainer>
</BoxContainer>
</Button>
</BoxContainer>
</Control>

View File

@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Shared._Mono.CorticalBorer;
using Content.Shared.Alert.Components;
namespace Content.Client._Mono.CorticalBorer;
/// <inheritdoc/>
public sealed class CorticalBorerSystem : SharedCorticalBorerSystem
{
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CorticalBorerComponent, GetGenericAlertCounterAmountEvent>(OnGetCounterAmount);
}
private void OnGetCounterAmount(Entity<CorticalBorerComponent> ent, ref GetGenericAlertCounterAmountEvent args)
{
if (args.Handled)
return;
if (ent.Comp.ChemicalAlert != args.Alert)
return;
args.Amount = ent.Comp.ChemicalPoints;
}
}

View File

@ -79,7 +79,8 @@ namespace Content.IntegrationTests.Tests
"Chibi", // DeltaV
"Elegance", // DeltaV
"Division", // DeltaV
"ArenaMedieval" //DeltaV
"ArenaMedieval", //DeltaV
"Submarine" //DeltaV
};
/// <summary>

View File

@ -251,6 +251,17 @@ public sealed partial class ChatSystem : SharedChatSystem
if (string.IsNullOrEmpty(message))
return;
// Begin Mono Changes - Is this being sent direct
var targetEv = new CheckTargetedSpeechEvent();
RaiseLocalEvent(source, targetEv);
if (targetEv.Targets.Count > 0)
{
SendEntityDirect(source, message, range, nameOverride, targetEv.Targets);
return;
}
// End Mono Changes - Is this being sent direct
// This message may have a radio prefix, and should then be whispered to the resolved radio channel
if (checkRadioPrefix)
{
@ -591,6 +602,72 @@ public sealed partial class ChatSystem : SharedChatSystem
}
}
// Begin Mono Changes
private void SendEntityDirect(
EntityUid source,
string originalMessage,
ChatTransmitRange range,
string? nameOverride,
List<EntityUid> recipients,
bool hideLog = false,
bool ignoreActionBlocker = false)
{
var message = TransformSpeech(source, FormattedMessage.RemoveMarkupOrThrow(originalMessage));
if (message.Length == 0)
return;
string name;
if (nameOverride != null)
{
name = nameOverride;
}
else
{
var nameEv = new TransformSpeakerNameEvent(source, Name(source));
RaiseLocalEvent(source, nameEv);
name = nameEv.VoiceName;
}
name = FormattedMessage.EscapeText(name);
var wrappedMessage = Loc.GetString("chat-manager-entity-whisper-wrap-message",
("entityName", name), ("message", FormattedMessage.EscapeText(message)));
foreach (var (session, data) in GetRecipients(source, WhisperMuffledRange))
{
EntityUid listener;
if (session.AttachedEntity is not { Valid: true } playerEntity)
continue;
listener = session.AttachedEntity.Value;
if (MessageRangeCheck(session, data, range) != MessageRangeCheckResult.Full ||
!recipients.Contains(listener) &&
!HasComp<GhostComponent>(listener))
continue;
_chatManager.ChatMessageToOne(ChatChannel.Local, message, wrappedMessage, source, false, session.Channel); // DeltaV - no collective mind chat channel, use local..?
}
if (!hideLog)
if (originalMessage == message)
{
if (name != Name(source))
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Direct messaged from {ToPrettyString(source):user} as {name}: {originalMessage}.");
else
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Direct messaged from {ToPrettyString(source):user}: {originalMessage}.");
}
else
{
if (name != Name(source))
_adminLogger.Add(LogType.Chat, LogImpact.Low,
$"Direct messaged from {ToPrettyString(source):user} as {name}, original: {originalMessage}, transformed: {message}.");
else
_adminLogger.Add(LogType.Chat, LogImpact.Low,
$"Direct messaged from {ToPrettyString(source):user}, original: {originalMessage}, transformed: {message}.");
}
}
// End Mono Changes
private void SendEntityEmote(
EntityUid source,
string action,
@ -959,6 +1036,13 @@ public sealed class CheckIgnoreSpeechBlockerEvent : EntityEventArgs
}
}
// Begin Mono Changes
public sealed class CheckTargetedSpeechEvent : EntityEventArgs
{
public List<EntityUid> Targets = new List<EntityUid>();
}
// End Mono Changes
/// <summary>
/// Raised on an entity when it speaks, either through 'say' or 'whisper'.
/// </summary>

View File

@ -186,7 +186,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
/// <param name="healthAnalyzer">The health analyzer that should receive the updates</param>
/// <param name="target">The entity to start analyzing</param>
/// <param name="part">Shitmed Change: The body part to analyze, if any</param>
private void BeginAnalyzingEntity(Entity<HealthAnalyzerComponent> healthAnalyzer, EntityUid target, EntityUid? part = null)
public void BeginAnalyzingEntity(Entity<HealthAnalyzerComponent> healthAnalyzer, EntityUid target, EntityUid? part = null) // Mono - make public
{
//Link the health analyzer to the scanned entity
healthAnalyzer.Comp.ScannedEntity = target;
@ -202,7 +202,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
/// </summary>
/// <param name="healthAnalyzer">The health analyzer that's receiving the updates</param>
/// <param name="target">The entity to analyze</param>
private void StopAnalyzingEntity(Entity<HealthAnalyzerComponent> healthAnalyzer, EntityUid target)
public void StopAnalyzingEntity(Entity<HealthAnalyzerComponent> healthAnalyzer, EntityUid target) // Mono - make public
{
//Unlink the analyzer
healthAnalyzer.Comp.ScannedEntity = null;

View File

@ -15,6 +15,7 @@ using Content.Shared.Damage;
using Content.Shared.Mobs.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Content.Shared._DV.Abilities.Psionics;
namespace Content.Server.Abilities.Psionics
{

View File

@ -1,18 +0,0 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Abilities.Psionics
{
[RegisterComponent]
public sealed partial class MindSwappedComponent : Component
{
[ViewVariables]
public EntityUid OriginalEntity = default!;
[DataField("mindSwapReturnActionId",
customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? MindSwapReturnActionId = "ActionMindSwapReturn";
[DataField("mindSwapReturnActionEntity")]
public EntityUid? MindSwapReturnActionEntity;
}
}

View File

@ -1,6 +1,7 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Systems;
using Content.Server.Disposal.Unit;
using Content.Shared._DV.Abilities.Psionics;
using Content.Shared.Abilities.Psionics;
using Content.Shared.Actions;
using Content.Shared.Actions.Events;
@ -24,7 +25,6 @@ namespace Content.Server.Abilities.Psionics
[Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!;
[Dependency] private readonly AtmosphereSystem _atmos = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
public override void Initialize()
{
@ -66,8 +66,6 @@ namespace Content.Server.Abilities.Psionics
{
var projection = Spawn(component.Prototype, Transform(uid).Coordinates);
_mind.ShowExamineInfo(uid, false); // Hide SSD indicator
_transform.AttachToGridOrMap(projection);
_mindSwap.Swap(uid, projection);
@ -76,11 +74,6 @@ namespace Content.Server.Abilities.Psionics
}
private void OnMindRemoved(EntityUid uid, TelegnosticProjectionComponent component, MindRemovedMessage args)
{
// This is called during transfer to, so the MindSwappedComponent is still present.
if (TryComp<MindSwappedComponent>(uid, out var mindSwapped))
{
_mind.ShowExamineInfo(mindSwapped.OriginalEntity, true);
}
QueueDel(uid);
}

View File

@ -18,6 +18,7 @@ using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Random;
using Content.Shared._DV.Abilities.Psionics;
namespace Content.Server.Psionics
{

View File

@ -1,9 +1,9 @@
using Content.Server.Abilities.Psionics;
using Content.Server.Chat.Systems;
using Content.Server.Nyanotrasen.StationEvents.Events;
using Content.Server.Radio.Components;
using Content.Server.Radio.EntitySystems;
using Content.Server.StationEvents.Events;
using Content.Shared._DV.Abilities.Psionics;
using Content.Shared.Interaction;
using Content.Shared.Psionics.Glimmer;
using Content.Shared.Radio;

View File

@ -90,7 +90,11 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
EntityUid? station,
EntityUid? entity = null)
{
_prototypeManager.TryIndex(job ?? string.Empty, out var prototype);
// Delta-V, prevent errors if this is an empty job.
JobPrototype? prototype = null;
if (!string.IsNullOrWhiteSpace(job))
_prototypeManager.TryIndex(job ?? string.Empty, out prototype);
// Delta-V end
RoleLoadout? loadout = null;
// Need to get the loadout up-front to handle names if we use an entity spawn override.
@ -163,7 +167,8 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
SetPdaAndIdCardData(entity.Value, metaData.EntityName, prototype, station);
}
DoJobSpecials(job, entity.Value);
if (!string.IsNullOrWhiteSpace(job)) // Delta-V, empty job is okay, actually
DoJobSpecials(job, entity.Value);
_identity.QueueIdentityUpdate(entity.Value);
return entity.Value;
}

View File

@ -0,0 +1,283 @@
using Content.Server.Abilities.Psionics;
using Content.Server.Chat.Systems;
using Content.Server.Cloning;
using Content.Server.DoAfter;
using Content.Server.Mind;
using Content.Server.Station.Systems;
using Content.Shared._DV.Abilities.Psionics;
using Content.Shared.Abilities.Psionics;
using Content.Shared.Actions;
using Content.Shared.Actions.Events;
using Content.Shared.Bed.Sleep;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Preferences;
using Content.Shared.Psionics.Events;
using Content.Shared.SSDIndicator;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server._DV.Abilities.Psionics;
public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem
{
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly CloningSystem _cloning = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popups = default!;
[Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!;
[Dependency] private readonly SleepingSystem _sleeping = default!;
[Dependency] private readonly StationSpawningSystem _stationSpawning = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly TransformSystem _transform = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FracturedFormPowerComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<FracturedFormPowerComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<FracturedFormPowerComponent, FracturedFormPowerActionEvent>(OnPowerUsed);
SubscribeLocalEvent<FracturedFormPowerComponent, DispelledEvent>(OnDispelled);
SubscribeLocalEvent<FracturedFormPowerComponent, FracturedFormDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<FracturedFormBodyComponent, ExaminedEvent>(OnExamine);
}
private void OnInit(Entity<FracturedFormPowerComponent> entity, ref ComponentInit args)
{
var component = entity.Comp;
_actions.AddAction(entity, ref component.FracturedFormActionEntity, component.FracturedFormActionId);
_actions.StartUseDelay(component.FracturedFormActionEntity);
if (TryComp<PsionicComponent>(entity, out var psionic) && psionic.PsionicAbility == null)
{
psionic.PsionicAbility = component.FracturedFormActionEntity;
psionic.ActivePowers.Add(component);
}
// Next random swap is between 5 to 20 minutes.
component.NextSwap = _timing.CurTime + TimeSpan.FromSeconds(_random.Next(300, 1200));
if (HasComp<FracturedFormBodyComponent>(entity)) return; // Don't generate a new body if we're already part of a network.
AddComp<FracturedFormBodyComponent>(entity);
component.Bodies.Add(entity);
GenerateForm(entity);
}
private void OnShutdown(Entity<FracturedFormPowerComponent> entity, ref ComponentShutdown args)
{
_actions.RemoveAction(entity.Owner, entity.Comp.FracturedFormActionEntity);
if (TryComp<PsionicComponent>(entity, out var psionic))
{
psionic.ActivePowers.Remove(entity.Comp);
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);
// Loop through fracturedForm havers, if they pass the NextSwap threshold, or they're asleep, force swap them.
var ents = EntityQueryEnumerator<FracturedFormPowerComponent>();
var t = _timing.CurTime;
List<Entity<FracturedFormPowerComponent>> toSwap = new();
while (ents.MoveNext(out var uid, out var comp))
{
if (!comp.SleepWarned && t > comp.NextSwap - TimeSpan.FromSeconds(5))
{
comp.SleepWarned = true;
_popups.PopupEntity(Loc.GetString("fractured-form-sleepy"), uid, uid, PopupType.LargeCaution);
_chat.TryEmoteWithChat(uid, "Yawn", ChatTransmitRange.Normal);
}
if (HasComp<SleepingComponent>(uid) || _mobState.IsIncapacitated(uid) || t > comp.NextSwap)
{
toSwap.Add(new(uid, comp));
}
}
foreach (var ent in toSwap)
{
TrySwap(ent);
}
var bodies = EntityQueryEnumerator<FracturedFormBodyComponent>();
while (bodies.MoveNext(out var uid, out var _))
{
if (!HasComp<SleepingComponent>(uid) && !HasComp<FracturedFormPowerComponent>(uid))
{
_sleeping.TrySleeping(uid);
}
if (TryComp<SSDIndicatorComponent>(uid, out var ssd) && ssd.IsSSD)
{
// Ensure the body isn't forcesleep'd by the SSD system.
ssd.IsSSD = false;
}
}
}
private EntityUid GenerateForm(Entity<FracturedFormPowerComponent> original)
{
// Form:
// - Same appearance as original
// - Different apperance, still humanoid
// Equipment:
// - Same as original body
// - Nude and helpless
var xform = Transform(original);
bool hasClothes = _random.Prob(0.4f);
EntityUid? newBody;
if (_random.Prob(0.6f) || !_cloning.TryCloning(original, _transform.GetMapCoordinates(original), hasClothes ? original.Comp.CopyClothed : original.Comp.CopyNaked, out newBody)) // Slightly lower chance to copy the original body
{
// Either the dice rolled poorly, or the cloning failed. Either way, make a new body instead. (Or try to)
var validSpecies = new List<ProtoId<SpeciesPrototype>>();
var speciesPrototypes = _prototype.EnumeratePrototypes<SpeciesPrototype>();
foreach (var proto in speciesPrototypes)
{
if (proto.RoundStart)
validSpecies.Add(proto.ID);
}
var species = _random.Pick(validSpecies);
var character = HumanoidCharacterProfile.RandomWithSpecies(species);
newBody = _stationSpawning.SpawnPlayerMob(xform.Coordinates, hasClothes ? original.Comp.VisitorJob : original.Comp.NakedJob, character, _station.GetCurrentStation(original.Owner));
if (newBody is not { } bodyV || Deleted(bodyV))
{
Log.Error($"Failed to create a new body for {ToPrettyString(original)}. This is a bug.");
return EntityUid.Invalid;
}
}
if (newBody is { } body && !Deleted(body))
{
AddComp<FracturedFormBodyComponent>(body);
original.Comp.Bodies.Add(body);
return body;
}
return default!;
}
private bool IsValidBody(Entity<FracturedFormPowerComponent> entity, EntityUid body)
{
if (body == entity.Owner)
return false;
if (!entity.Comp.Bodies.Contains(body))
return false;
if (!TryComp<MindContainerComponent>(body, out var cmind))
return false;
if (cmind.HasMind)
return false;
if (HasComp<ForcedSleepingComponent>(body))
return false;
if (_mobState.IsIncapacitated(body))
return false;
return true;
}
private List<EntityUid> ValidBodies(Entity<FracturedFormPowerComponent> entity)
{
var bodies = new List<EntityUid>();
foreach (var body in entity.Comp.Bodies)
{
if (IsValidBody(entity, body))
bodies.Add(body);
}
return bodies;
}
public bool CanSwap(Entity<FracturedFormPowerComponent> entity)
{
foreach (var body in entity.Comp.Bodies)
{
if (IsValidBody(entity, body))
return true;
}
return false;
}
private bool TrySwap(Entity<FracturedFormPowerComponent> entity)
{
// Pick a random body, or the other one, if we have more than one.
// Only picks bodies which don't have actives minds (In case of mindswap or something stupid)
if (!CanSwap(entity))
return false;
var targetBody = _random.Pick(ValidBodies(entity));
_audio.PlayPredicted(entity.Comp.SwapSound, entity, entity);
if (TryComp<MindContainerComponent>(entity, out var mindContainer))
_mind.TransferTo(mindContainer.Mind!.Value, targetBody);
// If the body is sleeping, try to wake up.
_sleeping.TryWaking(targetBody, false, entity);
var duplicate = AddComp<FracturedFormPowerComponent>(targetBody);
duplicate.Bodies = entity.Comp.Bodies;
RemCompDeferred(entity, entity.Comp);
return true;
}
private void OnPowerUsed(Entity<FracturedFormPowerComponent> entity, ref FracturedFormPowerActionEvent args)
{
if (!CanSwap(entity))
{
_popups.PopupEntity(Loc.GetString("fractured-form-nobodies"), entity, entity, PopupType.Large);
return;
}
entity.Comp.SleepWarned = true;
_chat.TryEmoteWithChat(entity.Owner, "Yawn", ChatTransmitRange.Normal);
_popups.PopupEntity(Loc.GetString("fractured-form-sleepy"), entity, entity, PopupType.LargeCaution);
var ev = new FracturedFormDoAfterEvent();
var doAfterArgs = new DoAfterArgs(EntityManager, entity, entity.Comp.ManualSwapTime, ev, entity);
_doAfter.TryStartDoAfter(doAfterArgs, out var doAfterId);
entity.Comp.DoAfter = doAfterId;
_psionics.LogPowerUsed(entity, "fractured form swap", 1, 3);
args.Handled = true;
}
private void OnDispelled(Entity<FracturedFormPowerComponent> entity, ref DispelledEvent args)
{
if (entity.Comp.DoAfter == null)
return;
_doAfter.Cancel(entity.Comp.DoAfter);
entity.Comp.DoAfter = null;
args.Handled = true;
}
private void OnDoAfter(Entity<FracturedFormPowerComponent> entity, ref FracturedFormDoAfterEvent args)
{
entity.Comp.DoAfter = null;
if (args.Cancelled || args.Handled)
return;
_sleeping.TrySleeping(entity.Owner);
}
private void OnExamine(Entity<FracturedFormBodyComponent> entity, ref ExaminedEvent args)
{
if (HasComp<FracturedFormPowerComponent>(entity))
return;
if (TryComp<FracturedFormPowerComponent>(args.Examiner, out var fracturedHost) && fracturedHost.Bodies.Contains(entity.Owner))
args.PushMarkup($"[color=yellow]{Loc.GetString("fractured-form-examine-self", ("ent", entity))}[/color]");
else
args.PushMarkup($"[color=yellow]{Loc.GetString("fractured-form-ssd", ("ent", entity))}[/color]");
}
}

View File

@ -0,0 +1,499 @@
using Content.Server.DoAfter;
using Content.Server.Hands.Systems;
using Content.Server.Interaction;
using Content.Server.Popups;
using Content.Shared._DV.Grappling.Components;
using Content.Shared._DV.Grappling.EntitySystems;
using Content.Shared._DV.Grappling.Events;
using Content.Shared.ActionBlocker;
using Content.Shared.Alert;
using Content.Shared.CombatMode;
using Content.Shared.Cuffs.Components;
using Content.Shared.DoAfter;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction.Components;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Mobs;
using Content.Shared.Movement.Events;
using Content.Shared.Popups;
using Content.Shared.Pulling.Events;
using Content.Shared.Standing;
using Content.Shared.Tag;
using Robust.Server.Audio;
using Robust.Server.Physics;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server._DV.Grappling.EntitySystems;
/// <summary>
/// Server side handling of grappling
/// </summary>
public sealed partial class GrapplingSystem : SharedGrapplingSystem
{
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly InteractionSystem _interaction = default!;
[Dependency] private readonly JointSystem _joint = default!;
[Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StandingStateSystem _standingState = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly SharedVirtualItemSystem _virtual = default!;
private ProtoId<TagPrototype> _grappleTargetId = "GrappleTarget";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GrapplerComponent, StartPullAttemptEvent>(OnPullAttempt);
SubscribeLocalEvent<GrapplerComponent, EscapeGrappleAlertEvent>(OnEscapeGrapplerAlert);
SubscribeLocalEvent<GrapplerComponent, MobStateChangedEvent>(OnGrapplerStateChanged);
SubscribeLocalEvent<GrappledComponent, MoveInputEvent>(OnGrappledMove);
SubscribeLocalEvent<GrappledComponent, GrappledEscapeDoAfter>(OnEscapeDoAfter);
SubscribeLocalEvent<GrappledComponent, EscapeGrappleAlertEvent>(OnEscapeGrappledAlert);
SubscribeLocalEvent<GrappledComponent, EntInsertedIntoContainerMessage>(OnCuffsInsertedIntoContainer);
}
/// <summary>
/// Validates whether a grappler can actually grapple the victim.
/// </summary>
/// <param name="grappler">Entity performing the grapple.</param>
/// <param name="victim">Intended victim of the grapple.</param>
/// <returns>True if the grapple could start, false otherwise.</returns>
public bool CanGrapple(Entity<GrapplerComponent?> grappler, EntityUid victim)
{
if (!Resolve(grappler, ref grappler.Comp))
return false;
if (grappler.Comp.ActiveVictim.HasValue)
return false; // Can't grapple more than one target
if (_gameTiming.CurTime < grappler.Comp.CooldownEnd)
return false; // Cooldown on the grapple is not over yet
if (!HasComp<GrappleTargetComponent>(victim))
return false; // Not a valid target
return _actionBlocker.CanInteract(grappler, victim);
}
/// <summary>
/// Returns whether a grapple is currently active on a victim.
/// </summary>
/// <param name="grappler">The grappler to check.</param>
/// <returns>True if there is an ongoing grapple, false otherwise.</returns>
public bool IsGrappling(Entity<GrapplerComponent?> grappler)
{
if (!Resolve(grappler, ref grappler.Comp))
return false; // Isn't a grappler, so nothing to do.
return grappler.Comp.ActiveVictim.HasValue;
}
/// <summary>
/// Attempts to start grappling a victim, rendering them unable to move and possibly
/// removing their ability to use their hands.
/// </summary>
/// <param name="grappler">Entity attempting to begin a grapple</param>
/// <param name="victim">Entity to be grappled.</param>
/// <returns>True if the grapple started, false otherwise.</returns>
public bool TryStartGrapple(Entity<GrapplerComponent?> grappler, EntityUid victim)
{
if (!Resolve(grappler, ref grappler.Comp))
return false;
if (!CanGrapple(grappler, victim))
return false;
if (!_interaction.InRangeUnobstructed(grappler.Owner, victim))
return false;
StartGrapple((grappler, grappler.Comp), victim);
return true;
}
/// <summary>
/// Releases a victim from a grapple allowing them to move again and returning any
/// hands that were disabled.
/// </summary>
/// <param name="grappler">Entity, which was performing the grapple.</param>
/// <param name="manualRelease">Whether this release is performed by the grappler, or by the grappled.</param>
/// <returns>True if the grapple was released, false otherwise.</returns>
public bool ReleaseGrapple(Entity<GrapplerComponent?> grappler, bool manualRelease = false)
{
if (!Resolve(grappler, ref grappler.Comp))
return false;
if (grappler.Comp.ActiveVictim is not { } victim)
return false; // Not grappling anything
if (!TryComp<GrappledComponent>(victim, out var victimComp))
return false; // Somehow not a grappled target
ReleaseGrapple((grappler, grappler.Comp),
(victim, victimComp),
manualRelease: manualRelease);
return true;
}
/// <summary>
/// Handles applying the effects of grappling.
/// Optionally starts a pulling action.
/// </summary>
/// <param name="grappler">Entity starting the grapple.</param>
/// <param name="victim">Entity to be grappled.</param>
private void StartGrapple(Entity<GrapplerComponent> grappler, EntityUid victim)
{
// Throw the victim and grappler (if requested) prone
_standingState.Down(victim);
if (grappler.Comp.ProneOnGrapple)
_standingState.Down(grappler);
// Ensure they have the grappled component for handling escapes and blocking movement.
EnsureComp<GrappledComponent>(victim, out var grappled);
grappled.Grappler = grappler;
grappled.EscapeTime = grappler.Comp.EscapeTime;
// Disable hands if requested
DisableHands(grappler!, (victim, grappled));
// Update the grappler's victim
grappler.Comp.ActiveVictim = victim;
grappler.Comp.PullJointId = $"grapple-joint-{GetNetEntity(victim)}";
Dirty(grappler);
// Update any movement blocks that the grappler/grappled now have
_actionBlocker.UpdateCanMove(grappler);
_actionBlocker.UpdateCanMove(victim);
// Joint the two together so both the grappler and the victim can't be tugged away from one another.
_joint.CreateDistanceJoint(grappler, victim, id: grappler.Comp.PullJointId);
_popup.PopupEntity(
Loc.GetString("grapple-start", ("part", grappler.Comp.GrapplingPart), ("victim", victim)),
victim,
grappler,
PopupType.MediumCaution);
_popup.PopupEntity(
Loc.GetString("grapple-start-victim", ("part", grappler.Comp.GrapplingPart), ("grappler", grappler)),
victim,
victim,
PopupType.MediumCaution);
_audio.PlayPvs(grappler.Comp.GrappleSound, victim);
_alerts.ShowAlert(grappler, grappler.Comp.GrappledAlert);
_alerts.ShowAlert(victim, grappler.Comp.GrappledAlert);
}
/// <summary>
/// Handles when a grappler attempts to start pulling an entity.
/// If they have an existing target, they will drop them.
/// If they do NOT have a target then they will attempt to grapple them if they are in combat mode.
/// Will stop the pull attempt if this system handles it via a grapple.
/// </summary>
/// <param name="grappler">Entity attempting to start pulling.</param>
/// <param name="args">Args for the event, notably the entity being pulled.</param>
private void OnPullAttempt(Entity<GrapplerComponent> grappler, ref StartPullAttemptEvent args)
{
if (grappler.Comp.ActiveVictim.HasValue)
{
if (ReleaseGrapple(grappler.AsNullable(), manualRelease: true))
args.Cancel();
}
else
{
if (!TryComp<CombatModeComponent>(grappler, out var combatMode) ||
!combatMode.IsInCombatMode)
return; // Not in harm mode, this is just a regular pull
if (TryStartGrapple(grappler.AsNullable(), args.Pulled))
args.Cancel(); // We've handled it.
}
}
/// <summary>
/// Attempts to disable hands as requested by the Grappler's component.
/// Disabled hands are stored on the GrappledComponent and will be re-enabled.
/// </summary>
/// <param name="grappler">Entity performing the grapple.</param>
/// <param name="victim">Victim which has become grappled.</param>
private void DisableHands(Entity<GrapplerComponent> grappler, Entity<GrappledComponent> victim)
{
if (!TryComp<HandsComponent>(victim, out var hands))
return; // This victim has no hands
if (grappler.Comp.HandDisabling == HandDisabling.None)
return; // Nothing left to do
var toBlock = new List<Hand>(2); // Most entities have a maximum of two hands, so default to a list of two hands
switch (grappler.Comp.HandDisabling)
{
case HandDisabling.None:
return;
case HandDisabling.SingleRandom:
var randomHand = _random.Next(0, hands.Count);
var handName = hands.SortedHands[randomHand];
var handComp = hands.Hands[handName];
toBlock.Add(handComp);
break;
case HandDisabling.SingleActive:
var activeHand = _hands.GetActiveHand((victim, hands));
if (activeHand != null)
toBlock.Add(activeHand!);
break;
case HandDisabling.All:
foreach (var hand in _hands.EnumerateHands(victim, hands))
{
toBlock.Add(hand);
}
break;
}
foreach (var hand in toBlock)
{
if (_virtual.TrySpawnVirtualItemInHand(grappler, victim, out var virtItem, dropOthers: true, hand))
{
EnsureComp<UnremoveableComponent>(virtItem.Value);
victim.Comp.DisabledHands.Add(hand.Name);
}
}
}
/// <summary>
/// Attempts to enable hands that were previously disabled,
/// as requested by the Grappler's component.
/// </summary>
/// <param name="grappler">Entity which was performing grapple.</param>
/// <param name="victim">Victim which had become grappled.</param>
private void EnableHands(Entity<GrapplerComponent> grappler, Entity<GrappledComponent> victim)
{
if (!TryComp<HandsComponent>(victim, out var hands))
return; // This victim has no hands
if (grappler.Comp.HandDisabling == HandDisabling.None)
return; // Nothing left to do
_virtual.DeleteInHandsMatching(victim, grappler);
// Because the virtual items are queued for deletion, but not actually removed from hands yet,
// we remove the component that makes them "unremovable", so that other systems like cuffs
// can add virtual items immediately.
foreach (var handName in victim.Comp.DisabledHands)
{
if (!_hands.TryGetHand(victim, handName, out var hand, hands))
continue;
if (!hand.HeldEntity.HasValue)
continue;
RemComp<UnremoveableComponent>(hand.HeldEntity.Value);
}
}
/// <summary>
/// Handles when a grappled entity attempts to move, and allows them to start
/// to wriggling free of the grapple.
/// Raises a DoAfter for the user to complete their escape.
/// </summary>
/// <param name="grappled">Entity attempting to move.</param>
/// <param name="args">Args for the event.</param>
private void OnGrappledMove(Entity<GrappledComponent> grappled, ref MoveInputEvent args)
{
if (!args.HasDirectionalMovement)
return;
BeginEscapeAttempt(grappled);
}
/// <summary>
/// Handles starting an escape attempt.
/// </summary>
/// <param name="grappled">Entity beginning the escape.</param>
private void BeginEscapeAttempt(Entity<GrappledComponent> grappled)
{
if (!TryComp<GrapplerComponent>(grappled.Comp.Grappler, out var grappler))
return; // Somehow grappled by a non-grappler?
var escapeDoAfter = new DoAfterArgs(
EntityManager,
grappled,
grappled.Comp.EscapeTime,
new GrappledEscapeDoAfter(),
grappled)
{
BreakOnMove = false,
BreakOnDamage = false,
NeedHand = true
};
if (!_doAfter.TryStartDoAfter(escapeDoAfter, out var doAfterId))
return;
grappled.Comp.DoAfterId = doAfterId;
_popup.PopupEntity(Loc.GetString("grapple-start-escaping", ("victim", grappled)),
grappled,
grappled.Comp.Grappler,
PopupType.MediumCaution);
_popup.PopupEntity(Loc.GetString("grapple-start-escaping-victim", ("part", grappler.GrapplingPart)),
grappled,
grappled,
PopupType.MediumCaution);
}
/// <summary>
/// Handles when the escape doafter is successful, which removes the grappling from the entity.
/// </summary>
/// <param name="grappled">Entity which has finished escaping from the grapple.</param>
/// <param name="args">Args for the event.</param>
private void OnEscapeDoAfter(Entity<GrappledComponent> grappled, ref GrappledEscapeDoAfter args)
{
if (args.Cancelled)
return; // Was manually cancelled in some way
if (!TryComp<GrapplerComponent>(grappled.Comp.Grappler, out var grappler))
return; // Somehow not a grappler for this entity?
ReleaseGrapple((grappled.Comp.Grappler, grappler), grappled, manualRelease: false);
}
/// <summary>
/// Handles when a grappled player clicks the grappled alert, beginning an escape attempt.
/// </summary>
/// <param name="grappled">Grappled player entity which triggered the escape attempt.</param>
/// <param name="args">Args for the event.</param>
private void OnEscapeGrappledAlert(Entity<GrappledComponent> grappled, ref EscapeGrappleAlertEvent args)
{
BeginEscapeAttempt(grappled);
}
/// <summary>
/// Handles when an entity is inserted into a container on the grappled entity.
/// Specifically checks for cuffs being added, which will cause a release of the grapple.
/// </summary>
/// <param name="grappled">Entity which has had an item inserted into a container.</param>
/// <param name="args">Args for the event.</param>
private void OnCuffsInsertedIntoContainer(Entity<GrappledComponent> grappled, ref EntInsertedIntoContainerMessage args)
{
if (!TryComp<CuffableComponent>(grappled, out var cuffable))
return; // Isn't cuffable so don't need to worry
if (args.Container.ID != cuffable.Container?.ID)
return; // Wasn't inserted into the cuff container
// This entity is being cuffed, release the grapple and let hands be cuffed properly.
ReleaseGrapple(grappled.Comp.Grappler);
}
/// <summary>
/// Handles when a grappler player clicks the grappled alert, beginning an escape attempt.
/// </summary>
/// <param name="grappled">Grappler player entity which has stopped grappling.</param>
/// <param name="args">Args for the event.</param>
private void OnEscapeGrapplerAlert(Entity<GrapplerComponent> grappler, ref EscapeGrappleAlertEvent args)
{
ReleaseGrapple(grappler.AsNullable(), manualRelease: true);
}
/// <summary>
/// Handles when a grappler enters crit or dies while holding a grappler, which will then release it.
/// </summary>
/// <param name="grappler">Grappler player entity which has entered crit or death.</param>
/// <param name="args">Args for the event.</param>
private void OnGrapplerStateChanged(Entity<GrapplerComponent> grappler, ref MobStateChangedEvent args)
{
if (!grappler.Comp.ActiveVictim.HasValue)
return; // Not actively grappling anything
if (args.NewMobState == MobState.Critical ||
args.NewMobState == MobState.Dead)
{
ReleaseGrapple(grappler.AsNullable(), manualRelease: true);
}
}
/// <summary>
/// Handles releasing the effects of a grapple from both entities.
/// </summary>
/// <param name="grappler">Entity performing the grapple.</param>
/// <param name="victim">Victim who has escaped or been released from the grapple</param>
private void ReleaseGrapple(Entity<GrapplerComponent> grappler,
Entity<GrappledComponent> victim,
bool manualRelease = false)
{
// Ensure any jointing is cleaned up
if (grappler.Comp.PullJointId != null)
{
_joint.RemoveJoint(grappler, grappler.Comp.PullJointId);
grappler.Comp.PullJointId = null;
}
// Inform the grappler that their victim is now free and they can move, updating the cooldown as well.
grappler.Comp.ActiveVictim = null;
grappler.Comp.CooldownEnd = _gameTiming.CurTime + grappler.Comp.Cooldown;
Dirty(grappler);
_actionBlocker.UpdateCanMove(grappler);
// Clean up the hold on their hands we have
EnableHands(grappler, victim);
// If this was a manul release by the grappler, we should cancel the doafter they have in progress, if any.
if (manualRelease)
{
if (victim.Comp.DoAfterId.HasValue)
{
_doAfter.Cancel(victim.Comp.DoAfterId.Value);
}
_popup.PopupEntity(
Loc.GetString("grapple-manual-release", ("victim", victim), ("part", grappler.Comp.GrapplingPart)),
victim,
grappler,
PopupType.Medium);
_popup.PopupEntity(Loc.GetString("grapple-manual-release-victim",
("grappler", grappler),
("part", grappler.Comp.GrapplingPart)),
victim,
victim,
PopupType.Medium);
}
else
{
_popup.PopupEntity(Loc.GetString("grapple-finished-escaping",
("victim", victim),
("part", grappler.Comp.GrapplingPart)),
victim,
grappler,
PopupType.MediumCaution);
_popup.PopupEntity(
Loc.GetString("grapple-finished-escaping-victim", ("part", grappler.Comp.GrapplingPart)),
victim,
victim,
PopupType.MediumCaution);
}
// Cleanup the grappling on the victim
RemComp<GrappledComponent>(victim);
_actionBlocker.UpdateCanMove(victim); // Must be done AFTER the component is removed.
// Automatically get the grappler back up
if (grappler.Comp.ProneOnGrapple && _standingState.IsDown(grappler))
_standingState.Stand(grappler);
_alerts.ClearAlert(grappler, grappler.Comp.GrappledAlert);
_alerts.ClearAlert(victim, grappler.Comp.GrappledAlert);
}
}

View File

@ -6,21 +6,34 @@ namespace Content.Server._DV.Light;
public sealed partial class LightReactiveSystem : SharedLightReactiveSystem
{
[Dependency] private readonly EntityLookupSystem _lookup = default!;
private EntityQuery<PointLightComponent> _lightQuery = default!;
private readonly HashSet<Entity<PointLightComponent>> _lightsInRange = new();
private readonly HashSet<Entity<SharedPointLightComponent>> _validLightsInRange = new();
public override void Initialize()
{
base.Initialize();
_lightQuery = EntityManager.GetEntityQuery<PointLightComponent>();
}
private readonly HashSet<Entity<SharedPointLightComponent>> _validLightsInRange = [];
public override HashSet<Entity<SharedPointLightComponent>> GetLights(EntityUid targetEntity)
{
_lightsInRange.Clear();
_lookup.GetEntitiesInRange(Transform(targetEntity).Coordinates, 10f, _lightsInRange);
var entitiesInRange = _lookup.GetEntitiesInRange(targetEntity, 10f);
_validLightsInRange.Clear();
foreach (var light in _lightsInRange)
foreach (var ent in entitiesInRange)
{
// On the server, we check if it's Enabled OR if netSyncEnabled is false
if (!_lightQuery.TryComp(ent, out var comp))
continue;
// On the server, we check if it's Enabled OR if netSyncEnabled i s false
// Because sometimes the server doesn't actually know if it should be enabled or not.
// The Client however, can be assumed to always be right.
if ((light.Comp.Enabled || !light.Comp.NetSyncEnabled) && !light.Comp.Deleted)
_validLightsInRange.Add(new(light.Owner, light.Comp));
if (!comp.Enabled && comp.NetSyncEnabled)
continue;
if (comp.Deleted)
continue;
_validLightsInRange.Add((ent, comp));
}
return _validLightsInRange;
}

View File

@ -0,0 +1,86 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
// SPDX-FileCopyrightText: 2025 Cojoke
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Shared._Mono.CorticalBorer;
using Content.Shared._Shitmed.Body.Events;
using Content.Shared.Body.Part;
using Content.Shared.Examine;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs;
using Robust.Server.Containers;
using Robust.Shared.Containers;
using Robust.Shared.Timing;
namespace Content.Server._Mono.CorticalBorer;
public sealed class CorticalBorerInfestedSystem : EntitySystem
{
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly CorticalBorerSystem _borer = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<CorticalBorerInfestedComponent, MapInitEvent>(OnInit);
SubscribeLocalEvent<CorticalBorerInfestedComponent, ExaminedEvent>(OnExaminedInfested);
SubscribeLocalEvent<CorticalBorerInfestedComponent, BodyPartRemovedEvent>(OnBodyPartRemoved);
SubscribeLocalEvent<CorticalBorerInfestedComponent, MobStateChangedEvent>(OnStateChange);
SubscribeLocalEvent<CorticalBorerInfestedComponent, MindRemovedMessage>(OnMindRemoved);
}
private void OnInit(Entity<CorticalBorerInfestedComponent> infested, ref MapInitEvent args)
{
infested.Comp.ControlContainer = _container.EnsureContainer<Container>(infested, "ControlContainer");
infested.Comp.InfestationContainer = _container.EnsureContainer<Container>(infested, "InfestationContainer");
}
private void OnExaminedInfested(Entity<CorticalBorerInfestedComponent> infected, ref ExaminedEvent args)
{
if (!args.IsInDetailsRange
|| args.Examined != args.Examiner)
return;
if (!infected.Comp.Borer.Comp.ControlingHost)
return;
if (infected.Comp.ControlTimeEnd is { } cte)
{
var timeRemaining = Math.Floor((cte - _timing.CurTime).TotalSeconds);
args.PushMarkup(Loc.GetString("infested-control-examined", ("timeremaining", timeRemaining)));
}
args.PushMarkup(Loc.GetString("cortical-borer-self-examine", ("chempoints", infected.Comp.Borer.Comp.ChemicalPoints)));
}
private void OnStateChange(Entity<CorticalBorerInfestedComponent> infected, ref MobStateChangedEvent args)
{
if (args.NewMobState != MobState.Dead)
return;
if(infected.Comp.Borer.Comp.ControlingHost)
_borer.EndControl(infected.Comp.Borer);
}
private void OnBodyPartRemoved(Entity<CorticalBorerInfestedComponent> infected, ref BodyPartRemovedEvent args)
{
if (TryComp<BodyPartComponent>(args.Part, out var part) &&
part.PartType == BodyPartType.Head)
{
_borer.EndControl(infected.Comp.Borer);
_borer.TryEjectBorer(infected.Comp.Borer);
}
}
private void OnMindRemoved(Entity<CorticalBorerInfestedComponent> infected, ref MindRemovedMessage args)
{
if (infected.Comp.Borer.Comp.ControlingHost)
{
_borer.EndControl(infected.Comp.Borer);
_borer.TryEjectBorer(infected.Comp.Borer);
}
}
}

View File

@ -0,0 +1,224 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
// SPDX-FileCopyrightText: 2025 Cojoke
// SPDX-FileCopyrightText: 2025 ark1368
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Server.Body.Components;
using Content.Server.Medical;
using Content.Shared._Mono.CorticalBorer;
using Content.Shared._Shitmed.Medical.Surgery;
using Content.Shared.Body.Components;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Popups;
namespace Content.Server._Mono.CorticalBorer;
public sealed partial class CorticalBorerSystem
{
[Dependency] private readonly VomitSystem _vomit = default!;
private void SubscribeAbilities()
{
SubscribeLocalEvent<CorticalBorerComponent, CorticalInfestEvent>(OnInfest);
SubscribeLocalEvent<CorticalBorerComponent, CorticalInfestDoAfterEvent>(OnInfestDoAfter);
SubscribeLocalEvent<CorticalBorerComponent, CorticalEjectEvent>(OnEjectHost);
SubscribeLocalEvent<CorticalBorerComponent, CorticalTakeControlEvent>(OnTakeControl);
SubscribeLocalEvent<CorticalBorerComponent, CorticalChemMenuActionEvent>(OnChemcialMenu);
SubscribeLocalEvent<CorticalBorerComponent, CorticalCheckBloodEvent>(OnCheckBlood);
SubscribeLocalEvent<CorticalBorerInfestedComponent, CorticalEndControlEvent>(OnEndControl);
SubscribeLocalEvent<CorticalBorerInfestedComponent, CorticalLayEggEvent>(OnLayEgg);
}
private void OnChemcialMenu(Entity<CorticalBorerComponent> ent, ref CorticalChemMenuActionEvent args)
{
if(!TryComp<UserInterfaceComponent>(ent, out var uic))
return;
if (ent.Comp.Host is null)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-no-host"), ent, ent, PopupType.Medium);
return;
}
_ui.TryToggleUi((ent, uic), CorticalBorerDispenserUiKey.Key, ent);
}
private void OnInfest(Entity<CorticalBorerComponent> ent, ref CorticalInfestEvent args)
{
var (uid, comp) = ent;
var target = args.Target;
var targetIdentity = Identity.Entity(target, EntityManager);
if (comp.Host is not null)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-has-host"), uid, uid, PopupType.Medium);
return;
}
if (HasComp<CorticalBorerInfestedComponent>(target))
{
_popup.PopupEntity(Loc.GetString("cortical-borer-host-already-infested", ("target", targetIdentity)), uid, uid, PopupType.Medium);
return;
}
// anything with bloodstream
if (!HasComp<BloodstreamComponent>(target))
{
_popup.PopupEntity(Loc.GetString("cortical-borer-invalid-host", ("target", targetIdentity)), uid, uid, PopupType.Medium);
return;
}
// target is on sugar for some reason, can't go in there
if (!CanUseAbility(ent, target))
return;
var infestAttempt = new InfestHostAttempt();
RaiseLocalEvent(target, infestAttempt);
if (infestAttempt.Cancelled)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-face-covered", ("target", targetIdentity)), uid, uid, PopupType.Medium);
return;
}
_popup.PopupEntity(Loc.GetString("cortical-borer-start-infest", ("target", targetIdentity)), uid, uid, PopupType.Medium);
var infestArgs = new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(3), new CorticalInfestDoAfterEvent(), uid, target)
{
DistanceThreshold = 1.5f,
BreakOnDamage = true,
BreakOnMove = true,
BreakOnWeightlessMove = true,
AttemptFrequency = AttemptFrequency.StartAndEnd,
Hidden = true,
};
_doAfter.TryStartDoAfter(infestArgs);
}
private void OnInfestDoAfter(Entity<CorticalBorerComponent> ent, ref CorticalInfestDoAfterEvent args)
{
if (args.Handled)
return;
if (args.Args.Target is not { } target)
return;
if (args.Cancelled || HasComp<CorticalBorerInfestedComponent>(target))
return;
InfestTarget(ent, target);
args.Handled = true;
}
private void OnEjectHost(Entity<CorticalBorerComponent> ent, ref CorticalEjectEvent args)
{
if (args.Handled)
return;
var (uid, comp) = ent;
if (comp.Host is null)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-no-host"), uid, uid, PopupType.Medium);
return;
}
if (!CanUseAbility(ent, comp.Host.Value))
return;
TryEjectBorer(ent);
args.Handled = true;
}
private void OnCheckBlood(Entity<CorticalBorerComponent> ent, ref CorticalCheckBloodEvent args)
{
if (args.Handled)
return;
if (ent.Comp.Host is null)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-no-host"), ent, ent, PopupType.Medium);
return;
}
TryToggleCheckBlood(ent);
args.Handled = true;
}
private void OnTakeControl(Entity<CorticalBorerComponent> ent, ref CorticalTakeControlEvent args)
{
if (args.Handled)
return;
if (ent.Comp.Host is null)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-no-host"), ent, ent, PopupType.Medium);
return;
}
// Host is dead, you can't take control
if (TryComp<MobStateComponent>(ent.Comp.Host, out var mobState) &&
mobState.CurrentState == MobState.Dead)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-dead-host"), ent, ent, PopupType.Medium);
return;
}
if (!TryComp<CorticalBorerInfestedComponent>(ent.Comp.Host, out var infestedComp))
return;
if (!CanUseAbility(ent, ent.Comp.Host.Value))
return;
// idk how you would cause this...
if (ent.Comp.ControlingHost)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-already-control"), ent, ent, PopupType.Medium);
return;
}
TakeControlHost(ent, infestedComp);
args.Handled = true;
}
private void OnEndControl(Entity<CorticalBorerInfestedComponent> host, ref CorticalEndControlEvent args)
{
if (args.Handled)
return;
EndControl(host.Comp.Borer);
args.Handled = true;
}
private void OnLayEgg(Entity<CorticalBorerInfestedComponent> host, ref CorticalLayEggEvent args)
{
if (args.Handled)
return;
var borer = host.Comp.Borer;
if (borer.Comp.EggCost > borer.Comp.ChemicalPoints)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-not-enough-chem"), host, host, PopupType.Medium);
return;
}
_vomit.Vomit(host, -20, -20); // half as much chem vomit, a lot that is coming up is the egg
LayEgg(borer);
UpdateChems(borer, -borer.Comp.EggCost);
args.Handled = true;
}
}

View File

@ -0,0 +1,390 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
// SPDX-FileCopyrightText: 2025 Cojoke
// SPDX-FileCopyrightText: 2025 ScyronX
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Server.DoAfter;
using Content.Server.Ghost.Roles;
using Content.Server.Ghost.Roles.Components;
using Content.Server.Medical;
using Content.Server.Medical.Components;
using Content.Server.Nutrition.Components;
using Content.Shared._Mono.CorticalBorer;
using Content.Shared.Administration.Logs;
using Content.Shared.Alert;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Chat; // Einstein Engines - Languages
using Content.Shared.Database;
using Content.Shared.Inventory;
using Content.Shared.MedicalScanner;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Nutrition.Components; // DeltaV - IngestionBlocker in shared
using Content.Shared.Popups;
using Content.Shared.SSDIndicator;
using Robust.Server.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server._Mono.CorticalBorer;
public sealed partial class CorticalBorerSystem : SharedCorticalBorerSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly BloodstreamSystem _blood = default!;
[Dependency] private readonly HealthAnalyzerSystem _analyzer = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly ISharedAdminLogManager _admin = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly GhostRoleSystem _ghost = default!;
public override void Initialize()
{
SubscribeAbilities();
SubscribeLocalEvent<CorticalBorerComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<CorticalBorerComponent, CorticalBorerDispenserInjectMessage>(OnInjectReagentMessage);
SubscribeLocalEvent<CorticalBorerComponent, CorticalBorerDispenserSetInjectAmountMessage>(OnSetInjectAmountMessage);
SubscribeLocalEvent<InventoryComponent, InfestHostAttempt>(OnInfestHostAttempt);
SubscribeLocalEvent<CorticalBorerComponent, CheckTargetedSpeechEvent>(OnSpeakEvent);
SubscribeLocalEvent<CorticalBorerComponent, MindRemovedMessage>(OnMindRemoved);
}
private void OnStartup(Entity<CorticalBorerComponent> ent, ref ComponentStartup args)
{
//add actions
foreach (var actionId in ent.Comp.InitialCorticalBorerActions)
_actions.AddAction(ent, actionId);
_alerts.ShowAlert(ent, ent.Comp.ChemicalAlert);
UpdateUiState(ent);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var comp in EntityManager.EntityQuery<CorticalBorerComponent>())
{
if (_timing.CurTime < comp.UpdateTimer)
continue;
comp.UpdateTimer = _timing.CurTime + TimeSpan.FromSeconds(comp.UpdateCooldown);
if (comp.Host != null)
UpdateChems((comp.Owner, comp), comp.ChemicalGenerationRate);
}
foreach (var comp in EntityManager.EntityQuery<CorticalBorerInfestedComponent>())
{
if (_timing.CurTime >= comp.ControlTimeEnd)
EndControl(comp.Borer);
}
}
private void OnSpeakEvent(Entity<CorticalBorerComponent> ent, ref CheckTargetedSpeechEvent args)
{
//args.ChatTypeIgnore.Add(InGameICChatType.CollectiveMind); // DeltaV - eradicate language code
if (ent.Comp.Host.HasValue)
{
args.Targets.Add(ent);
args.Targets.Add(ent.Comp.Host.Value);
}
}
public void UpdateChems(Entity<CorticalBorerComponent> ent, int change)
{
var (_, comp) = ent;
if (comp.ChemicalPoints + change >= comp.ChemicalPointCap)
comp.ChemicalPoints = comp.ChemicalPointCap;
else if (comp.ChemicalPoints + change <= 0)
comp.ChemicalPoints = 0;
else
comp.ChemicalPoints += change;
if (comp.ChemicalPoints % comp.UiUpdateInterval == 0)
UpdateUiState(ent);
_alerts.ShowAlert(ent, ent.Comp.ChemicalAlert);
Dirty(ent);
}
public void OnInfestHostAttempt(Entity<InventoryComponent> entity, ref InfestHostAttempt args)
{
IngestionBlockerComponent? blocker;
if (_inventory.TryGetSlotEntity(entity.Owner, "head", out var headUid) &&
TryComp(headUid, out blocker) &&
blocker.Enabled)
{
args.Blocker = headUid;
args.Cancel();
}
}
/// <summary>
/// Attempts to inject the Borer's host with chems
/// </summary>
public bool TryInjectHost(Entity<CorticalBorerComponent> ent,
CorticalBorerChemicalPrototype chemicalPrototype,
float chemAmount)
{
var (uid, comp) = ent;
// Need a host to inject something
if (!comp.Host.HasValue)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-no-host"), uid, uid, PopupType.Medium);
return false;
}
// Sugar block from injecting stuff
if (!CanUseAbility(ent, comp.Host.Value))
return false;
// Make sure you can even hold the amount of chems you need
if (chemicalPrototype.Cost > comp.ChemicalPointCap)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-not-enough-chem-storage"), uid, uid, PopupType.Medium);
return false;
}
// Make sure you have enough chems
if (chemicalPrototype.Cost > comp.ChemicalPoints)
{
_popup.PopupEntity(Loc.GetString("cortical-borer-not-enough-chem"), uid, uid, PopupType.Medium);
return false;
}
// no injecting things that don't have blood silly
if (!TryComp<BloodstreamComponent>(comp.Host, out var bloodstream))
return false;
var solution = new Solution();
solution.AddReagent(chemicalPrototype.Reagent, chemAmount);
// add the chemicals to the bloodstream of the host
if (!_blood.TryAddToChemicals(comp.Host.Value, solution, bloodstream))
return false;
UpdateChems(ent, -((int)chemAmount * chemicalPrototype.Cost));
return true;
}
private void OnInjectReagentMessage(Entity<CorticalBorerComponent> ent, ref CorticalBorerDispenserInjectMessage message)
{
CorticalBorerChemicalPrototype? chemProto = null;
foreach (var chem in _proto.EnumeratePrototypes<CorticalBorerChemicalPrototype>())
{
if (chem.Reagent.Equals(message.ChemProtoId))
{
chemProto = chem;
break;
}
}
if (chemProto != null)
TryInjectHost(ent, chemProto, ent.Comp.InjectAmount);
UpdateUiState(ent);
}
private void OnSetInjectAmountMessage(Entity<CorticalBorerComponent> ent, ref CorticalBorerDispenserSetInjectAmountMessage message)
{
ent.Comp.InjectAmount = message.CorticalBorerDispenserDispenseAmount;
UpdateUiState(ent);
}
private List<CorticalBorerDispenserItem> GetAllBorerChemicals(Entity<CorticalBorerComponent> ent)
{
var clones = new List<CorticalBorerDispenserItem>();
foreach (var prototype in _proto.EnumeratePrototypes<CorticalBorerChemicalPrototype>())
{
if (!_proto.TryIndex(prototype.Reagent, out ReagentPrototype? proto))
continue;
var reagentName = proto.LocalizedName;
var reagentId = proto.ID;
var cost = prototype.Cost;
var amount = ent.Comp.InjectAmount;
var chems = ent.Comp.ChemicalPoints;
var color = proto.SubstanceColor;
clones.Add(new CorticalBorerDispenserItem(reagentName,reagentId, cost, amount, chems, color)); // need color and name
}
return clones;
}
private void UpdateUiState(Entity<CorticalBorerComponent> ent)
{
var chems = GetAllBorerChemicals(ent);
var state = new CorticalBorerDispenserBoundUserInterfaceState(chems, (int)ent.Comp.InjectAmount);
_userInterfaceSystem.SetUiState(ent.Owner, CorticalBorerDispenserUiKey.Key, state);
}
public bool TryToggleCheckBlood(Entity<CorticalBorerComponent> ent)
{
if(!TryComp<UserInterfaceComponent>(ent, out var uic))
return false;
if (!TryComp<HealthAnalyzerComponent>(ent, out var health))
return false;
_ui.TryToggleUi((ent, uic), HealthAnalyzerUiKey.Key, ent);
if (health.ScannedEntity is null && ent.Comp.Host.HasValue)
OpenCheckBlood(ent, uic);
return true;
}
public void OpenCheckBlood(Entity<CorticalBorerComponent> ent, UserInterfaceComponent uic)
{
if (!ent.Comp.Host.HasValue)
return;
if (!TryComp<HealthAnalyzerComponent>(ent, out var health))
return;
if (!_ui.IsUiOpen((ent,uic), HealthAnalyzerUiKey.Key))
_ui.OpenUi((ent, uic), HealthAnalyzerUiKey.Key, ent);
_analyzer.BeginAnalyzingEntity((ent, health), ent.Comp.Host.Value);
}
public void CloseCheckBlood(Entity<CorticalBorerComponent> ent, UserInterfaceComponent uic)
{
if (!ent.Comp.Host.HasValue)
return;
if (!TryComp<HealthAnalyzerComponent>(ent, out var health))
return;
if(!health.ScannedEntity.HasValue)
return;
_ui.CloseUi((ent, uic), HealthAnalyzerUiKey.Key, ent);
_analyzer.StopAnalyzingEntity((ent, health), health.ScannedEntity.Value);
}
public void TakeControlHost(Entity<CorticalBorerComponent> ent, CorticalBorerInfestedComponent infestedComp)
{
var (worm, comp) = ent;
if (comp.Host is not { } host)
return;
// make sure they aren't dead, would throw the worm into a ghost mode and just kill em
if (TryComp<MobStateComponent>(ent.Comp.Host, out var mobState) &&
mobState.CurrentState == MobState.Dead)
return;
if (TryComp<MindContainerComponent>(host, out var mindContainer) &&
mindContainer.HasMind ||
HasComp<GhostRoleComponent>(host))
infestedComp.ControlTimeEnd = _timing.CurTime + comp.ControlDuration;
if (_mind.TryGetMind(worm, out var wormMind, out _))
infestedComp.BorerMindId = wormMind;
if (_mind.TryGetMind(host, out var controledMind, out _))
{
infestedComp.OrigininalMindId = controledMind; // set this var here just in case somehow the mind changes from when the infestation started
// fish head...
var dummy = Spawn("FoodMeatFish", MapCoordinates.Nullspace);
_container.Insert(dummy, infestedComp.ControlContainer);
_mind.TransferTo(controledMind, dummy);
}
else
{
infestedComp.OrigininalMindId = null;
}
comp.ControlingHost = true;
_mind.TransferTo(wormMind, host);
if (TryComp<GhostRoleComponent>(worm, out var ghostRole))
_ghost.UnregisterGhostRole((worm, ghostRole)); // prevent players from taking the worm role once mind isn't in the worm
// add the end control and vomit egg action
if (_actions.AddAction(host, "ActionEndControlHost") is {} actionEnd)
infestedComp.RemoveAbilities.Add(actionEnd);
if (comp.CanReproduce &&
infestedComp.ControlTimeEnd != null) // you can't lay eggs with something you can control forever
{
if (_actions.AddAction(host, "ActionLayEggHost") is {} actionLay)
infestedComp.RemoveAbilities.Add(actionLay);
}
var str = $"{ToPrettyString(worm)} has taken control over {ToPrettyString(host)}";
Log.Info(str);
_admin.Add(LogType.Mind, LogImpact.High, $"{ToPrettyString(worm)} has taken control over {ToPrettyString(host)}");
_chat.SendAdminAlert(str);
}
public void EndControl(Entity<CorticalBorerComponent> worm)
{
var (uid, comp) = worm;
if (comp.Host is not { } host)
return;
if (!TryComp<CorticalBorerInfestedComponent>(host, out var infestedComp))
return;
// not controlling anyone
if (!comp.ControlingHost)
return;
comp.ControlingHost = false;
// remove all the actions set to remove
foreach (var ability in infestedComp.RemoveAbilities)
{
_actions.RemoveAction(host, ability);
}
infestedComp.RemoveAbilities = new(); // clear out the list
if (TryComp<GhostRoleComponent>(worm, out var ghostRole))
_ghost.RegisterGhostRole((worm, ghostRole)); // re-enable the ghost role after you return to the body
// Return everyone to their own bodies
if (!TerminatingOrDeleted(infestedComp.BorerMindId))
_mind.TransferTo(infestedComp.BorerMindId, infestedComp.Borer);
if (!TerminatingOrDeleted(infestedComp.OrigininalMindId) && infestedComp.OrigininalMindId.HasValue)
_mind.TransferTo(infestedComp.OrigininalMindId.Value, host);
infestedComp.ControlTimeEnd = null;
_container.CleanContainer(infestedComp.ControlContainer);
}
private void OnMindRemoved(Entity<CorticalBorerComponent> ent, ref MindRemovedMessage args)
{
if (!ent.Comp.ControlingHost)
TryEjectBorer(ent); // No storing them in hosts if you don't have a soul
}
}

View File

@ -0,0 +1,63 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Alert.Components;
/// <summary>
/// This is used for an alert which simply displays a generic number over a texture.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class GenericCounterAlertComponent : Component
{
/// <summary>
/// The width, in pixels, of an individual glyph, accounting for the space between glyphs.
/// A 3 pixel wide glyph with one pixel of space between it and the next would be a width of 4.
/// </summary>
[DataField]
public int GlyphWidth = 6;
/// <summary>
/// Whether the numbers should be centered on the glyph or just follow a static position.
/// </summary>
[DataField]
public bool CenterGlyph = true;
/// <summary>
/// Whether leading zeros should be hidden.
/// If true, "005" would display as "5".
/// </summary>
[DataField]
public bool HideLeadingZeroes = true;
/// <summary>
/// The size of the alert sprite.
/// Used to calculate offsets.
/// </summary>
[DataField]
public Vector2i AlertSize = new(32, 32);
/// <summary>
/// Digits that can be displayed by the alert, represented by their sprite layer.
/// Order defined corresponds to the digit it affects. 1st defined will affect 1st digit, 2nd affect 2nd digit and so on.
/// In this case ones would be on layer "1", tens on layer "10" etc.
/// </summary>
[DataField]
public List<string> DigitKeys = new()
{
"1",
"10",
"100",
"1000",
"10000"
};
}
/// <summary>
/// Event raised to gather the amount the alert will display.
/// </summary>
/// <param name="Alert">The alert which is currently requesting an update.</param>
/// <param name="Amount">The number to display on the alert.</param>
[ByRefEvent]
public record struct GetGenericAlertCounterAmountEvent(AlertPrototype Alert, int? Amount = null)
{
public bool Handled => Amount.HasValue;
}

View File

@ -48,6 +48,12 @@ public sealed partial class GenericStatusEffect : EntityEffect
{
statusSys.TryAddStatusEffect(args.TargetEntity, Key, TimeSpan.FromSeconds(time), Refresh, Component);
}
// Begin Mono Changes - just add status without component
else if (Type == StatusEffectMetabolismType.Add)
{
statusSys.TryAddStatusEffect(args.TargetEntity, Key, TimeSpan.FromSeconds(time), Refresh);
}
// End Mono Changes - just add status without component
else if (Type == StatusEffectMetabolismType.Remove)
{
statusSys.TryRemoveTime(args.TargetEntity, Key, TimeSpan.FromSeconds(time));

View File

@ -1,5 +1,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared._DV.Mind; // DeltaV
using Content.Shared._EE.Silicon.Components; // Goobstation
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
@ -159,6 +160,13 @@ public abstract partial class SharedMindSystem : EntitySystem
if (_net.IsClient)
return;
// Begin DeltaV Addition
var ev = new ShowSSDIndicatorEvent();
RaiseLocalEvent(uid, ref ev);
if (ev.Hidden)
return;
// End DeltaV Addition
var dead = _mobState.IsDead(uid);
var mind = CompOrNull<MindComponent>(mindContainer.Mind);
var hasUserId = mind?.UserId;

View File

@ -77,11 +77,11 @@ public partial class MobStateSystem
//unused
break;
case MobState.Critical:
_standing.Stand(target);
//_standing.Stand(target); // DeltaV - Remove Redundancy
break;
case MobState.Dead:
RemComp<CollisionWakeComponent>(target);
_standing.Stand(target);
//_standing.Stand(target); // DeltaV - Remove Redundancy
break;
case MobState.Invalid:
//unused
@ -102,7 +102,9 @@ public partial class MobStateSystem
switch (state)
{
case MobState.Alive:
_standing.Stand(target);
if (!TryComp(target, out BuckleComponent? buckle)
|| !buckle.Buckled) // DeltaV - Stop buckled mobs from standing up.
_standing.Stand(target);
_appearance.SetData(target, MobStateVisuals.State, MobState.Alive);
break;
case MobState.Critical:

View File

@ -1,3 +1,5 @@
using Content.Shared._DV.Abilities.Psionics;
using Content.Shared._DV.Mind;
using Content.Shared.Interaction.Events;
namespace Content.Shared.Abilities.Psionics;
@ -9,6 +11,7 @@ public abstract class SharedTelegnosisPowerSystem : EntitySystem
base.Initialize();
SubscribeLocalEvent<TelegnosticProjectionComponent, InteractionAttemptEvent>(OnInteractionAttempt);
SubscribeLocalEvent<TelegnosisPowerComponent, ShowSSDIndicatorEvent>(OnShowSSDIndicator);
}
private void OnInteractionAttempt(Entity<TelegnosticProjectionComponent> ent, ref InteractionAttemptEvent args)
@ -16,4 +19,11 @@ public abstract class SharedTelegnosisPowerSystem : EntitySystem
// no astrally stealing someones shoes
args.Cancelled = true;
}
private void OnShowSSDIndicator(Entity<TelegnosisPowerComponent> entity, ref ShowSSDIndicatorEvent args)
{
if (!TryComp<MindSwappedComponent>(entity, out var mindSwapped) || !HasComp<TelegnosticProjectionComponent>(mindSwapped.OriginalEntity))
return; // Only hide if currently projecting
args.Hidden = true;
}
}

View File

@ -1,12 +1,13 @@
using Content.Shared.Actions;
using Content.Shared.Actions.Components;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Abilities.Psionics
{
[RegisterComponent, Access(typeof(SharedTelegnosisPowerSystem))]
[RegisterComponent, NetworkedComponent, Access(typeof(SharedTelegnosisPowerSystem))]
public sealed partial class TelegnosisPowerComponent : Component
{
[DataField("prototype")]

View File

@ -104,7 +104,7 @@ public enum RevenantVisuals : byte
}
[NetSerializable, Serializable]
public enum RevenantVisualLayers : byte
public enum RevenantVisualLayers : byte // DeltaV - kept for impstation rev compatibility
{
Digit1,
Digit2,

View File

@ -0,0 +1,6 @@
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Abilities.Psionics;
[RegisterComponent, NetworkedComponent]
public sealed partial class FracturedFormBodyComponent : Component;

View File

@ -0,0 +1,42 @@
using Content.Shared.Cloning;
using Robust.Shared.Prototypes;
using Content.Shared.Roles;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Content.Shared.DoAfter;
namespace Content.Shared._DV.Abilities.Psionics;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class FracturedFormPowerComponent : Component
{
[DataField]
public EntProtoId FracturedFormActionId = "ActionFracturedForm";
[DataField, AutoNetworkedField]
public EntityUid? FracturedFormActionEntity;
[DataField]
public DoAfterId? DoAfter;
[DataField]
public ProtoId<CloningSettingsPrototype> CopyNaked = "CloningPod";
[DataField]
public ProtoId<CloningSettingsPrototype> CopyClothed = "Antag";
[DataField]
public ProtoId<JobPrototype> VisitorJob = "Passenger";
[DataField]
public ProtoId<JobPrototype>? NakedJob = null; // Scary null, but we explicitely want no job for naked spawns.
[DataField]
public TimeSpan NextSwap = TimeSpan.MaxValue;
[DataField]
public float ManualSwapTime = 5f;
[DataField]
public bool SleepWarned = false;
[DataField]
public List<EntityUid> Bodies { get; set; } = new();
[DataField]
public SoundSpecifier SwapSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg")
{
Params = AudioParams.Default.WithVariation(0.05f)
};
}

View File

@ -0,0 +1,16 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._DV.Abilities.Psionics;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class MindSwappedComponent : Component
{
[DataField, AutoNetworkedField]
public EntityUid OriginalEntity = default!;
[DataField]
public EntProtoId? MindSwapReturnActionId = "ActionMindSwapReturn";
[DataField]
public EntityUid? MindSwapReturnActionEntity;
}

View File

@ -0,0 +1,19 @@
using Content.Shared._DV.Mind;
namespace Content.Shared._DV.Abilities.Psionics;
public abstract class SharedFracturedFormPowerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FracturedFormBodyComponent, ShowSSDIndicatorEvent>(OnShowSSDIndicator);
}
private void OnShowSSDIndicator(Entity<FracturedFormBodyComponent> entity, ref ShowSSDIndicatorEvent args)
{
if (HasComp<FracturedFormPowerComponent>(entity))
return;
args.Hidden = true;
}
}

View File

@ -0,0 +1,3 @@
namespace Content.Shared.Actions.Events;
public sealed partial class FracturedFormPowerActionEvent : InstantActionEvent;

View File

@ -0,0 +1,10 @@
using Content.Shared._DV.Grappling.EntitySystems;
namespace Content.Shared._DV.Grappling.Components;
/// <summary>
/// Marks this entity as a valid target for grapples.
/// </summary>
[RegisterComponent]
[Access(typeof(SharedGrapplingSystem))]
public sealed partial class GrappleTargetComponent : Component;

View File

@ -0,0 +1,45 @@
using Content.Shared._DV.Grappling.EntitySystems;
using Content.Shared.Alert;
using Content.Shared.DoAfter;
using Robust.Shared.GameStates;
namespace Content.Shared._DV.Grappling.Components;
/// <summary>
/// Marks this entity as having been grappled.
/// </summary>
[RegisterComponent, NetworkedComponent]
[Access(typeof(SharedGrapplingSystem))]
public sealed partial class GrappledComponent : Component
{
/// <summary>
/// The entity which is performing the grapple.
/// </summary>
[DataField]
public EntityUid Grappler = EntityUid.Invalid;
/// <summary>
/// How much time is required to escape.
/// </summary>
[DataField]
public TimeSpan EscapeTime = TimeSpan.FromSeconds(15);
/// <summary>
/// The in-progress DoAfter, if any.
/// Used to cancel the doAfter if the grappler manually releases their victim.
/// </summary>
[DataField]
public DoAfterId? DoAfterId = null;
/// <summary>
/// A list of all hands, if any, that have been disabled as part of the grapple
/// via a virtual item.
/// </summary>
[DataField]
public List<string> DisabledHands = new();
}
/// <summary>
/// Raised when a player manually clicks the grappled icon to begin attempting to escape.
/// </summary>
public sealed partial class EscapeGrappleAlertEvent : BaseAlertEvent;

View File

@ -0,0 +1,95 @@
using Content.Shared._DV.Grappling.EntitySystems;
using Content.Shared.Alert;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared._DV.Grappling.Components;
/// <summary>
/// Marks this entity as a grappler.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(SharedGrapplingSystem))]
public sealed partial class GrapplerComponent : Component
{
/// <summary>
/// How much time is required to escape a grapple from this entity.
/// </summary>
[DataField]
public TimeSpan EscapeTime = TimeSpan.FromSeconds(15);
/// <summary>
/// The sound to play when the grapple action is successful on a target.
/// </summary>
[DataField]
public SoundSpecifier GrappleSound = new SoundPathSpecifier("/Audio/_DV/Grappling/grapple_action.ogg");
/// <summary>
/// Whether this entity can move by itself while grappling.
/// </summary>
[DataField]
public bool CanMoveWhileGrappling = false;
/// <summary>
/// Whether this entity should also lay prone when grappling.
/// </summary>
[DataField]
public bool ProneOnGrapple = false;
/// <summary>
/// Whether grapples from this entity should disable no, a random, or all hands of the victim.
/// </summary>
[DataField]
public HandDisabling HandDisabling = HandDisabling.None;
/// <summary>
/// What localized string is to be used for the body part in the grapple.
/// I.e. Jaws, Hands, Claws, etc.
/// </summary>
[DataField(required: true)]
public LocId GrapplingPart;
/// <summary>
/// The entity, if any, which this unit is grappling.
/// </summary>
[DataField]
[AutoNetworkedField]
public EntityUid? ActiveVictim = null;
/// <summary>
/// Cooldown for grappling to apply at the moment the grapple is broken.
/// </summary>
[DataField]
public TimeSpan Cooldown = TimeSpan.FromSeconds(5);
/// <summary>
/// Time when the cooldown for the grapple will be over.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
public TimeSpan CooldownEnd;
/// <summary>
/// Which alert to show when a victim is grappled.
/// </summary>
[DataField]
public ProtoId<AlertPrototype> GrappledAlert = "Grappled";
/// <summary>
/// The joint ID used between the grappler and victim.
/// </summary>
[DataField, AutoNetworkedField]
public string? PullJointId = null;
}
/// <summary>
/// Whether hands should be disabled by the grappling.
/// </summary>
public enum HandDisabling
{
None, // No hands to be disabled.
SingleRandom, // A single hand is disabled at random from available hands.
SingleActive, // The active hand is disabled.
All, // All hands are disabled.
}

View File

@ -0,0 +1,73 @@
using Content.Shared._DV.Grappling.Components;
using Content.Shared.Interaction.Events;
using Content.Shared.Movement.Events;
using Content.Shared.Standing;
namespace Content.Shared._DV.Grappling.EntitySystems;
/// <summary>
/// Shared logic for grapplers.
/// Enables some prediction of events and updates.
/// </summary>
public abstract partial class SharedGrapplingSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GrapplerComponent, UpdateCanMoveEvent>(OnCanMoveQuery);
SubscribeLocalEvent<GrapplerComponent, AttackAttemptEvent>(OnAttemptAttack);
SubscribeLocalEvent<GrappledComponent, StandAttemptEvent>(OnGrappledStand);
SubscribeLocalEvent<GrappledComponent, UpdateCanMoveEvent>(OnGrappleCanMoveQuery);
}
/// <summary>
/// Handles when a grappler attempts to move.
/// Potentially disallows movement of the grappler when they are grappling a target.
/// </summary>
/// <param name="grappler">The grappling entity.</param>
/// <param name="args">Args for the event.</param>
private void OnCanMoveQuery(Entity<GrapplerComponent> grappler, ref UpdateCanMoveEvent args)
{
if (grappler.Comp.CanMoveWhileGrappling)
return; // This entity can always move.
if (grappler.Comp.ActiveVictim.HasValue)
args.Cancel(); // Can't move while grappling
}
/// <summary>
/// Handles when a grappled target attempts to stand and blocks it.
/// </summary>
/// <param name="grappled">Grappled entity attempting to stand.</param>
/// <param name="args">Args for the event.</param>
private void OnGrappledStand(Entity<GrappledComponent> grappled, ref StandAttemptEvent args)
{
args.Cancel(); // Can't stand while being grappled
}
/// <summary>
/// Handles when a grappled target attempts to move and blocks it.
/// </summary>
/// <param name="grappled">Grappled entity attempting to move.</param>
/// <param name="args">Args for the event.</param>
private void OnGrappleCanMoveQuery(Entity<GrappledComponent> grappled, ref UpdateCanMoveEvent args)
{
args.Cancel(); // Can't move while grappled
}
/// <summary>
/// Handles when a grappler attempts to attack an entity.
/// If they have an active victim, they will not be able to attack because their body
/// is currently being used in the grapple.
/// </summary>
/// <param name="grappler">Grappler attempting to attack an entity.</param>
/// <param name="args">Args for the event.</param>
private void OnAttemptAttack(Entity<GrapplerComponent> grappler, ref AttackAttemptEvent args)
{
if (grappler.Comp.ActiveVictim.HasValue)
args.Cancel(); // You cannot attack while grappling
}
}

View File

@ -0,0 +1,10 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared._DV.Grappling.Events;
/// <summary>
/// Raised when a player attempts to wriggle from a grapple.
/// </summary>
[Serializable, NetSerializable]
public sealed partial class GrappledEscapeDoAfter : SimpleDoAfterEvent;

View File

@ -1,20 +0,0 @@
using Content.Shared.Mind.Components;
namespace Content.Shared.Mind;
public partial class SharedMindSystem
{
/// <summary>
/// Set whether or not to show examine information about the mind. Used to obscure if a mind is SSD or not.
/// </summary>
/// <param name="uid">Entity to set the mind examine info for.</param>
/// <param name="showExamineInfo">True to show examine information, false to hide it.</param>
public void ShowExamineInfo(EntityUid uid, bool showExamineInfo)
{
if (!TryComp<MindContainerComponent>(uid, out var mindContainer))
return;
mindContainer.ShowExamineInfo = showExamineInfo;
Dirty(uid, mindContainer);
}
}

View File

@ -0,0 +1,4 @@
namespace Content.Shared._DV.Mind;
[ByRefEvent]
public record struct ShowSSDIndicatorEvent(bool Hidden = false);

View File

@ -14,3 +14,6 @@ public sealed partial class MassSleepDoAfterEvent : SimpleDoAfterEvent;
[Serializable, NetSerializable]
public sealed partial class PsionicEruptionDoAfterEvent : SimpleDoAfterEvent;
[Serializable, NetSerializable]
public sealed partial class FracturedFormDoAfterEvent : SimpleDoAfterEvent;

View File

@ -0,0 +1,109 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
// SPDX-FileCopyrightText: 2025 Cojoke
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Shared.Alert;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._Mono.CorticalBorer;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class CorticalBorerComponent : Component
{
/// <summary>
/// Host of this Borer
/// </summary>
[ViewVariables]
public EntityUid? Host = null;
/// <summary>
/// Current number of chemical points this Borer has, used to level up and buy chems
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[AutoNetworkedField]
[DataField]
public int ChemicalPoints = 50;
/// <summary>
/// Chemicals added every second WHILE IN A HOST
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public int ChemicalGenerationRate = 1;
/// <summary>
/// Max Chemicals that can be held
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public int ChemicalPointCap = 250;
/// <summary>
/// Reagent injection amount
/// </summary>
public int InjectAmount = 10;
/// <summary>
/// At what interval does the chem ui update
/// </summary>
public int UiUpdateInterval = 5; // every 6 to prevent constant update on cap
/// <summary>
/// The max duration you can take control of your host
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public TimeSpan ControlDuration = TimeSpan.FromSeconds(40);
/// <summary>
/// Cooldown between chem regen events.
/// </summary>
public TimeSpan UpdateTimer = TimeSpan.Zero;
public float UpdateCooldown = 1f;
/// <summary>
/// Can this borer make more
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public bool CanReproduce = true;
/// <summary>
/// What does it vomit out of its mouth when it lays an egg
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public string EggProto = "CorticalBorerEgg";
/// <summary>
/// cost to lay an egg... will not update ability desc if changed
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public int EggCost = 200;
[DataField]
public bool ControlingHost;
[DataField]
public ComponentRegistry? AddOnInfest;
[DataField]
public ComponentRegistry? RemoveOnInfest;
[DataField]
public ProtoId<AlertPrototype> ChemicalAlert = "Chemicals";
public readonly List<EntProtoId> InitialCorticalBorerActions = new()
{
"ActionCorticalBorerInfest",
"ActionCorticalBorerEject",
"ActionCorticalBorerChemMenu",
"ActionCheckBlood",
"ActionControlHost",
};
}

View File

@ -0,0 +1,50 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
// SPDX-FileCopyrightText: 2025 Cojoke
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Robust.Shared.GameStates;
using Robust.Shared.Containers;
namespace Content.Shared._Mono.CorticalBorer;
[RegisterComponent, NetworkedComponent]
public sealed partial class CorticalBorerInfestedComponent : Robust.Shared.GameObjects.Component
{
/// <summary>
/// Borer in the person
/// </summary>
[ViewVariables]
public Entity<CorticalBorerComponent> Borer = new();
/// <summary>
/// Container for borer
/// </summary>
public Container InfestationContainer = new();
/// <summary>
/// is the person under the borer's control
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public TimeSpan? ControlTimeEnd;
[ViewVariables]
public EntityUid? OrigininalMindId;
[ViewVariables]
public EntityUid BorerMindId;
/// <summary>
/// Where the mind gets hidden when the worm takes control
/// </summary>
public Container ControlContainer;
/// <summary>
/// Abilities to be removed once host gets control back
/// </summary>
public List<EntityUid> RemoveAbilities = new();
}
[RegisterComponent, NetworkedComponent]
public sealed partial class SurgeryCorticalBorerConditionComponent : Robust.Shared.GameObjects.Component;

View File

@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Robust.Shared.GameStates;
namespace Content.Shared._Mono.CorticalBorer;
[RegisterComponent, NetworkedComponent]
public sealed partial class SurgeryStepRemoveCorticalBorerComponent : Component { }

View File

@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Shared.Actions;
namespace Content.Shared._Mono.CorticalBorer;
public sealed partial class CorticalInfestEvent : EntityTargetActionEvent { }
public sealed partial class CorticalEjectEvent : InstantActionEvent { }
public sealed partial class CorticalChemMenuActionEvent : InstantActionEvent {}
public sealed partial class CorticalCheckBloodEvent : InstantActionEvent {}
public sealed partial class CorticalTakeControlEvent : InstantActionEvent {}
public sealed partial class CorticalEndControlEvent : InstantActionEvent {}
public sealed partial class CorticalLayEggEvent : InstantActionEvent {}

View File

@ -0,0 +1,11 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared._Mono.CorticalBorer;
[Serializable, NetSerializable]
public sealed partial class CorticalInfestDoAfterEvent : SimpleDoAfterEvent { }

View File

@ -0,0 +1,27 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Prototypes;
namespace Content.Shared._Mono.CorticalBorer;
[Prototype("borerChemical")]
public sealed partial class CorticalBorerChemicalPrototype : IPrototype
{
[IdDataField]
public string ID { get; } = default!;
/// <summary>
/// Chemical cost per u of reagent
/// </summary>
[DataField]
public int Cost { get; set; } = 5;
/// <summary>
/// Reagent to inject into host
/// </summary>
[DataField]
public string Reagent { get; set; } = "";
}

View File

@ -0,0 +1,241 @@
// SPDX-FileCopyrightText: 2025 Coenx-flex
// SPDX-FileCopyrightText: 2025 Cojoke
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using System.Linq;
using Content.Shared.Actions;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Body.Systems;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Examine;
using Content.Shared.MedicalScanner;
using Content.Shared.Popups;
using Content.Shared.StatusEffect;
using Content.Shared.Coordinates;
using Content.Shared.Damage;
using Content.Shared.IdentityManagement;
using Robust.Shared.Containers;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
namespace Content.Shared._Mono.CorticalBorer;
public partial class SharedCorticalBorerSystem : EntitySystem
{
[Dependency] private readonly SharedBodySystem _bodySystem = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly ISerializationManager _serManager = default!;
[Dependency] private readonly DamageableSystem _damage = default!;
[Dependency] protected readonly SharedPopupSystem _popup = default!;
[Dependency] protected readonly SharedUserInterfaceSystem _ui = default!;
[Dependency] protected readonly SharedActionsSystem _actions = default!;
[Dependency] protected readonly SharedContainerSystem _container = default!;
public bool CanUseAbility(Entity<CorticalBorerComponent> ent, EntityUid target)
{
if (_statusEffects.HasStatusEffect(target,
"CorticalBorerProtection")) // hardcoded the status effect because...
{
_popup.PopupEntity(Loc.GetString("cortical-borer-sugar-block"), ent.Owner, ent.Owner, PopupType.Medium);
return false;
}
return true;
}
public void InfestTarget(Entity<CorticalBorerComponent> ent, EntityUid target)
{
var (uid, comp) = ent;
// Make sure the infected person is infected right
var infestedComp = EnsureComp<CorticalBorerInfestedComponent>(target);
// Make sure they get into the target
if (!_container.Insert(uid, infestedComp.InfestationContainer))
{
RemCompDeferred<CorticalBorerInfestedComponent>(target); // oh no it didn't work somehow so remove the comp you just added...
return;
}
// Set up the Borer
infestedComp.Borer = ent;
comp.Host = target;
if (comp.AddOnInfest is not null)
{
foreach (var (key, compReg) in comp.AddOnInfest)
{
var compType = compReg.Component.GetType();
if (HasComp(ent, compType))
continue;
var newComp = (Component) _serManager.CreateCopy(compReg.Component, notNullableOverride: true);
EntityManager.AddComponent(ent, newComp, true);
}
}
if (comp.RemoveOnInfest is not null)
{
foreach (var (key, compReg) in comp.RemoveOnInfest)
RemCompDeferred(ent, compReg.Component.GetType());
}
if (TryComp<DamageableComponent>(ent, out var damComp))
_damage.SetAllDamage(ent, damComp, 0);
}
public bool TryEjectBorer(Entity<CorticalBorerComponent> ent)
{
var (uid, comp) = ent;
if (ent.Comp.Host is not { } host)
return false;
// Make sure they get out of the host
if (!_container.TryRemoveFromContainer(uid))
return false;
// close all the UIs that relate to host
if (TryComp<UserInterfaceComponent>(ent, out var uic))
{
_ui.CloseUi((ent.Owner,uic), HealthAnalyzerUiKey.Key);
_ui.CloseUi((ent.Owner,uic), CorticalBorerDispenserUiKey.Key);
}
RemCompDeferred<CorticalBorerInfestedComponent>(ent.Comp.Host.Value);
ent.Comp.Host = null;
if (comp.RemoveOnInfest is not null)
{
foreach (var (key, compReg) in comp.RemoveOnInfest)
{
var compType = compReg.Component.GetType();
if (HasComp(ent, compType))
continue;
var newComp = (Component) _serManager.CreateCopy(compReg.Component, notNullableOverride: true);
EntityManager.AddComponent(ent, newComp, true);
}
}
if (comp.AddOnInfest is not null)
{
foreach (var (key, compReg) in comp.AddOnInfest)
RemCompDeferred(ent, compReg.Component.GetType());
}
return true;
}
public void LayEgg(Entity<CorticalBorerComponent> ent)
{
if (ent.Comp.Host is not { } host)
return;
if (ent.Comp.EggProto is not {} egg)
return;
var coordinates = _transform.ToMapCoordinates(host.ToCoordinates());
var spawnedEgg = Spawn(egg, coordinates);
}
}
public sealed class InfestHostAttempt : CancellableEntityEventArgs
{
/// <summary>
/// The equipment that is blocking the entrance
/// </summary>
public EntityUid? Blocker = null;
}
[Serializable, NetSerializable]
public enum CorticalBorerDispenserUiKey
{
Key
}
[Serializable, NetSerializable]
public sealed class CorticalBorerDispenserSetInjectAmountMessage : BoundUserInterfaceMessage
{
public readonly int CorticalBorerDispenserDispenseAmount;
public CorticalBorerDispenserSetInjectAmountMessage(int amount)
{
CorticalBorerDispenserDispenseAmount = amount;
}
public CorticalBorerDispenserSetInjectAmountMessage(String s)
{
switch (s)
{
case "1":
CorticalBorerDispenserDispenseAmount = 1;
break;
case "5":
CorticalBorerDispenserDispenseAmount = 5;
break;
case "10":
CorticalBorerDispenserDispenseAmount = 10;
break;
case "15":
CorticalBorerDispenserDispenseAmount = 15;
break;
case "20":
CorticalBorerDispenserDispenseAmount = 20;
break;
case "25":
CorticalBorerDispenserDispenseAmount = 25;
break;
case "30":
CorticalBorerDispenserDispenseAmount = 30;
break;
case "50":
CorticalBorerDispenserDispenseAmount = 50;
break;
case "100":
CorticalBorerDispenserDispenseAmount = 100;
break;
default:
throw new Exception($"Cannot convert the string `{s}` into a valid DispenseAmount");
}
}
}
[Serializable, NetSerializable]
public sealed class CorticalBorerDispenserInjectMessage : BoundUserInterfaceMessage
{
public readonly string ChemProtoId;
public CorticalBorerDispenserInjectMessage(string proto)
{
ChemProtoId = proto;
}
}
[Serializable, NetSerializable]
public sealed class CorticalBorerDispenserBoundUserInterfaceState : BoundUserInterfaceState
{
public readonly List<CorticalBorerDispenserItem> DisList;
public readonly int SelectedDispenseAmount;
public CorticalBorerDispenserBoundUserInterfaceState(List<CorticalBorerDispenserItem> disList, int dispenseAmount)
{
DisList = disList;
SelectedDispenseAmount = dispenseAmount;
}
}
[Serializable, NetSerializable]
public sealed class CorticalBorerDispenserItem(string reagentName, string reagentId, int cost, int amount, int chems, Color reagentColor)
{
public string ReagentName = reagentName;
public string ReagentId = reagentId;
public int Cost = cost;
public int Amount = amount;
public int Chems = chems;
public Color ReagentColor = reagentColor;
}

View File

@ -26,6 +26,7 @@ using Content.Shared.Popups;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed.TypeParsers;
using System.Linq;
using Content.Shared._Mono.CorticalBorer; // Mono
namespace Content.Shared._Shitmed.Medical.Surgery;
@ -47,6 +48,7 @@ public abstract partial class SharedSurgerySystem
SubSurgery<SurgeryTendWoundsEffectComponent>(OnTendWoundsStep, OnTendWoundsCheck);
SubSurgery<SurgeryStepCavityEffectComponent>(OnCavityStep, OnCavityCheck);
SubSurgery<SurgeryStepRemoveCorticalBorerComponent>(OnCorticalBorerRemovalStep, OnCorticalBorerRemovalCheck); // Mono
SubSurgery<SurgeryAddPartStepComponent>(OnAddPartStep, OnAddPartCheck);
SubSurgery<SurgeryAffixPartStepComponent>(OnAffixPartStep, OnAffixPartCheck);
SubSurgery<SurgeryRemovePartStepComponent>(OnRemovePartStep, OnRemovePartCheck);
@ -427,6 +429,21 @@ public abstract partial class SharedSurgerySystem
args.Cancelled = true;
}
// Begin Mono Changes - borer
private void OnCorticalBorerRemovalStep(Entity<SurgeryStepRemoveCorticalBorerComponent> ent, ref SurgeryStepEvent args)
{
if (TryComp<CorticalBorerInfestedComponent>(args.Body, out var infested) &&
infested.InfestationContainer.ContainedEntities.Count != 0)
_corticalBorer.TryEjectBorer(infested.Borer);
}
private void OnCorticalBorerRemovalCheck(Entity<SurgeryStepRemoveCorticalBorerComponent> ent, ref SurgeryStepCompleteCheckEvent args)
{
if (HasComp<CorticalBorerInfestedComponent>(args.Body))
args.Cancelled = true;
}
// End Mono Changes - borer
private void OnAddPartStep(Entity<SurgeryAddPartStepComponent> ent, ref SurgeryStepEvent args)
{
if (!TryComp(args.Surgery, out SurgeryPartRemovedConditionComponent? removedComp))

View File

@ -1,4 +1,5 @@
using System.Linq;
using Content.Shared._Mono.CorticalBorer; // Mono
using Content.Shared._Shitmed.Medical.Surgery.Conditions;
using Content.Shared._Shitmed.Medical.Surgery.Effects.Complete;
using Content.Shared.Body.Systems;
@ -50,6 +51,7 @@ public abstract partial class SharedSurgerySystem : EntitySystem
[Dependency] private readonly StandingStateSystem _standing = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly TagSystem _tagSystem = default!; // DeltaV: surgery can operate through some clothing
[Dependency] private readonly SharedCorticalBorerSystem _corticalBorer = default!; // Mono
/// <summary>
/// Cache of all surgery prototypes' singleton entities.
@ -87,6 +89,7 @@ public abstract partial class SharedSurgerySystem : EntitySystem
SubscribeLocalEvent<SurgeryPartComponentConditionComponent, SurgeryValidEvent>(OnPartComponentConditionValid);
SubscribeLocalEvent<SurgeryOrganOnAddConditionComponent, SurgeryValidEvent>(OnOrganOnAddConditionValid);
//SubscribeLocalEvent<SurgeryRemoveLarvaComponent, SurgeryCompletedEvent>(OnRemoveLarva);
SubscribeLocalEvent<SurgeryCorticalBorerConditionComponent, SurgeryValidEvent>(OnCorticalBorerValid); // Mono
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
InitializeSteps();
@ -161,6 +164,15 @@ public abstract partial class SharedSurgerySystem : EntitySystem
args.Cancelled = true;
}*/
// Begin Mono Changes - borer
private void OnCorticalBorerValid(Entity<SurgeryCorticalBorerConditionComponent> ent, ref SurgeryValidEvent args)
{
if (!HasComp<CorticalBorerInfestedComponent>(args.Body) ||
!HasComp<IncisionOpenComponent>(args.Part))
args.Cancelled = true;
}
// End Mono Changes - borer
private void OnBodyComponentConditionValid(Entity<SurgeryBodyComponentConditionComponent> ent, ref SurgeryValidEvent args)
{
var present = true;

View File

@ -0,0 +1,4 @@
- files: ["grapple_action.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Made for DeltaV by BarryNorfolk"
source: "https://github.com/BarryNorfolk"

Binary file not shown.

View File

@ -1,102 +1,4 @@
Entries:
- author: ewokswagger
changes:
- message: Paramedics will now start with 15tc (down from 16)
type: Tweak
id: 1246
time: '2025-04-12T02:24:53.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3416
- author: boogiebogus
changes:
- message: Revised juggernaut suit sprites, as well as its reverse engineered counterpart.
type: Tweak
id: 1247
time: '2025-04-12T02:42:26.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3364
- author: erhardsteinhauer
changes:
- message: The Vulcan, Energy Gun, Ceremonial rifle and the Jackdaw now have proper
sprites when in the back.
type: Fix
- message: The Vulcan and Jackdaw now have wielded sprites.
type: Fix
id: 1248
time: '2025-04-12T02:45:31.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3405
- author: deltanedas
changes:
- message: Merged last 2 weeks changes from upstream.
type: Add
id: 1249
time: '2025-04-12T14:46:26.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3394
- author: deltanedas
changes:
- message: Fixed operating on animals not making surgical tools dirty.
type: Fix
- message: Fixed held non-surgical items becoming dirty during surgery.
type: Fix
id: 1250
time: '2025-04-12T14:52:21.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3424
- author: HTMLSystem
changes:
- message: Added jumpskirt versions of the casual atmos jumpsuit and botanist overalls.
Find them in the uniform printer and loadouts!
type: Add
- message: Added recipes for the senior bartender outfits in the uniform printer!
type: Add
id: 1251
time: '2025-04-13T02:09:18.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3399
- author: sowelipililimute
changes:
- message: Robotics can now print IPC chasses that can receive a brain and limbs
type: Add
- message: IPCs do not feel pain in surgery anymore
type: Add
- message: MMIs can now be inserted into IPC chasses
type: Add
id: 1252
time: '2025-04-13T10:27:42.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3418
- author: Toby222
changes:
- message: Disabled mob pushing for the time being
type: Remove
id: 1253
time: '2025-04-13T15:00:57.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3443
- author: Quanteey
changes:
- message: Medical masks can now be found in surgeon lockers and made in the medical
techfab!
type: Tweak
id: 1254
time: '2025-04-13T22:47:05.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3391
- author: Solaris7518
changes:
- message: Resprited a variety of objects that are commonly used by medical! Medical
mains rejoice!
type: Add
id: 1255
time: '2025-04-13T22:55:53.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3420
- author: Field Command
changes:
- message: Reworked security on Pebble, alongside some other changes.
type: Tweak
id: 1256
time: '2025-04-13T23:51:41.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3440
- author: Radezolid
changes:
- message: Now the tape recorder is able to be printed at every departamental TechFab.
type: Tweak
id: 1257
time: '2025-04-14T12:08:32.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3454
- author: Blackdutchie
changes:
- message: Slimes can now digest and metabolize food, drinks, medicine, and other
@ -4183,3 +4085,107 @@
id: 1745
time: '2025-10-13T18:57:32.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4509
- author: EmberAstra
changes:
- message: Fixed the criminal records computer board being called "security records"
type: Fix
- message: Fixed robotic arms (and other automation machines) icons in the circuit
lathe.
type: Fix
- message: Fixed unholy water's guidebook description.
type: Fix
id: 1746
time: '2025-10-14T13:39:59.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4511
- author: TehFlaminTaco
changes:
- message: Added a new psionic ability, "Fractured Form"
type: Add
id: 1747
time: '2025-10-14T19:42:43.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4506
- author: TehFlaminTaco
changes:
- message: Held items such as PDAs and Flashlights now count as light sources as
they were supposed to.
type: Fix
- message: Diona no-longer take 4 times the intended damage in darkness
type: Fix
id: 1748
time: '2025-10-15T09:37:17.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4525
- author: kotobdev
changes:
- message: Reworded the asakim >CONFISCATE objective for clarity.
type: Tweak
id: 1749
time: '2025-10-15T15:58:02.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4528
- author: leonardo-dabepis
changes:
- message: "Added four paintings by Belgian surrealist painter Ren\xE9 Magritte"
type: Add
id: 1750
time: '2025-10-16T04:02:06.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3732
- author: BarryNorfolk
changes:
- message: Added ability for Laika to grapple ne'er-do-wells with her jaws, grabbing
in combat mode will throw them to the floor!
type: Add
id: 1751
time: '2025-10-16T04:52:35.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4157
- author: kotobdev
changes:
- message: Cortical Borers have been ported from Monolith, check Minor Antagonists
in the guidebook for more information. Remember to not listen to the voices
in your head.
type: Add
id: 1752
time: '2025-10-16T05:11:24.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4466
- author: SirWarock
changes:
- message: Most Cryochemicals now evenly heal 7 damage of their respective type!
type: Tweak
- message: Necrosol has been buffed to even heal your emotional damage, as well
as every other damage type!
type: Tweak
- message: Prometheum and Ichor now evenly heal!
type: Tweak
- message: The infamous Pyrogenics also warmed up to evenly heal!
type: Tweak
- message: The Cryogenics guidebookpage has been updated!
type: Tweak
id: 1753
time: '2025-10-16T05:21:56.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4455
- author: TehFlaminTaco
changes:
- message: Fractured Form bodies no-longer get stuck when left too long
type: Fix
id: 1754
time: '2025-10-16T05:54:33.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4530
- author: SirWarock
changes:
- message: Buckled Patients do not stand up after recovering from critical state!
type: Tweak
id: 1755
time: '2025-10-16T09:04:22.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4316
- author: Stop-Signs
changes:
- message: Added the Rhino Industrial hardsuit, a unique suit for logistics officers.
type: Add
id: 1756
time: '2025-10-16T13:47:45.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/3911
- author: Vapetastic-Gaming
changes:
- message: SUBMARINE RETURNS TO DELTA-V
type: Add
id: 1757
time: '2025-10-18T16:26:03.0000000+00:00'
url: https://github.com/DeltaV-Station/Delta-v/pull/4497

View File

@ -1,5 +1,6 @@
ent-CriminalRecordsComputerCircuitboard = security records computer board
.desc = A computer printed circuit board for a security records computer.
# DeltaV - Was security records computer board
ent-CriminalRecordsComputerCircuitboard = criminal records computer board
.desc = A computer printed circuit board for a criminal records computer.
ent-StationRecordsComputerCircuitboard = employment records computer board
.desc = A computer printed circuit board for a employment records computer.

View File

@ -83,3 +83,8 @@ eruption-warning-window-prompt-text-part = You feel a strong pressure building u
eruption-warning-window-acknowledge-button = I Understand
telegnosis-power-ssd = { CAPITALIZE(POSS-ADJ($ent)) } eyes are unfocused and darting around, as if trying to see something that isn't there.
fractured-form-nobodies = You have no alternate forms to switch to!
fractured-form-sleepy = You feel very sleepy... You should find somewhere to rest.
fractured-form-ssd = { CAPITALIZE(SUBJECT($ent)) } { CONJUGATE-BE($ent) } in a deep sleep. { CAPITALIZE(POSS-ADJ($ent)) } eyes seem to be darting around as if dreaming.
fractured-form-examine-self = You feel a strange connection to { OBJECT($ent) }.

View File

@ -2,6 +2,9 @@ alerts-vow-silence-desc-delta = You have taken a vow forbidding verbal communica
alerts-vow-broken-desc-delta = You've broken your vows to Mimes everywhere. You can speak, but you've lost your powers for at least 5 entire minutes!!! Click to try and retake your vow.
alerts-grappled-name = [color=yellow]Grappled[/color]
alerts-grappled-desc = You're [color=yellow]grappled[/color] and cannot move, your hands may be bound.
alerts-light-level-dark-name = Darkness
alerts-light-level-dark-desc = You are in an area with very low light.
alerts-light-level-neutral-name = Dim Light

View File

@ -0,0 +1,15 @@
grapple-start = Your {LOC($part)} clamp down on {$victim}!
grapple-start-victim = The {$grappler}'s {LOC($part)} clamps down on you, throwing you to the floor!
grapple-start-escaping = {THE($victim)} is attempting to get free!
grapple-start-escaping-victim = You start trying to escape from their {LOC($part)}!
grapple-manual-release = You gently release {$victim} from your {LOC($part)}.
grapple-manual-release-victim = {THE($grappler)} releases you from their {LOC($part)}.
grapple-finished-escaping = {$victim} has escaped from your {LOC($part)}!
grapple-finished-escaping-victim = You escape from their {LOC($part)}!
grapple-part-hands = hands
grapple-part-jaws = jaws
grapple-part-claws = claws

View File

@ -13,3 +13,4 @@ steal-target-groups-engineering-techfab-circuitboard = engineering techfab's cir
steal-target-groups-logistics-techfab-circuitboard = logistics techfab's circuitboard
steal-target-groups-service-techfab-circuitboard = service techfab's circuitboard
steal-target-groups-epistemics-techfab-circuitboard = epistemics techfab's circuitboard
steal-target-groups-hardsuit-rhino = logistics officer's rhino hardsuit

View File

@ -40,3 +40,6 @@ construction-graph-tag-hoodie = hoodie
# Ancient books
construction-graph-tag-ancientbook = Damaged Ancient Book
# Weapons
construction-graph-tag-silversword = silver sword

View File

@ -9,3 +9,6 @@ tool-quality-censer-tool-name = Censer
tool-quality-restoration-name = Restoring
tool-quality-restoration-tool-name = Restoration Kit
tool-quality-bible-name = Blessing
tool-quality-bible-tool-name = Bible

View File

@ -0,0 +1,2 @@
alerts-borer-chemical-name = Chemicals
alerts-borer-chemical-desc = Chemicals made in your body, used for your abilities.

View File

@ -0,0 +1,28 @@
## Infest Messages
cortical-borer-has-host = You already have a host.
cortical-borer-host-already-infested = {THE($target)} is already infested.
cortical-borer-invalid-host = {THE($target)} is not a valid host.
cortical-borer-face-covered = {THE($target)}'s face is covered.
cortical-borer-headless = {THE($target)} does not have a head!
cortical-borer-start-infest = You begin to crawl into {THE($target)}.
## Generic messages
cortical-borer-no-host = You do not have a host.
cortical-borer-dead-host = Your host is dead.
cortical-borer-not-enough-chem = You do not have enough chemicals.
cortical-borer-not-enough-chem-storage = You cannot hold enough chemicals.
cortical-borer-sugar-block = You taste something sweet.
## Control messages
cortical-borer-already-control = You are already controlling your host.
cortical-borer-vomit = {$name} vomits out a {$egg}!
## UI
cortical-borer-dispenser-window-cost = {$cost} chemicals
cortical-borer-ghostrole-name = Cortical Borer
cortical-borer-ghostrole-desc = A space worm with the sole purpose in life to enter peoples heads and lay its eggs.
## Examine Text
infested-control-examined = You have [color=#d842fc]{$timeremaining}[/color] seconds left controlling this body.
cortical-borer-self-examine = You have [color=#d842fc]{$chempoints}[/color] chemicals.

View File

@ -0,0 +1 @@
surgery-popup-step-SurgeryStepRemoveCorticalBorer = {$user} is removing the Cortical Borer from {$target}'s {$part}!

View File

@ -1,6 +1,17 @@
meta:
format: 6
postmapinit: false
format: 7
category: Grid
engineVersion: 262.0.0
forkId: ""
forkVersion: ""
time: 10/05/2025 22:51:52
entityCount: 2351
maps: []
grids:
- 1
orphans:
- 1
nullspace: []
tilemap:
0: Space
14: FloorBar
@ -31,52 +42,52 @@ entities:
chunks:
-1,0:
ind: -1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAABNQAAAAADNQAAAAABTwAAAAADTwAAAAAATwAAAAADTwAAAAABXgAAAAACPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAANQAAAAAANQAAAAAANQAAAAABTwAAAAAATwAAAAACXgAAAAADTwAAAAAAXgAAAAADPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACTwAAAAABTwAAAAABTwAAAAABTwAAAAACTwAAAAABXgAAAAACTwAAAAADXgAAAAABTwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACTwAAAAACTwAAAAABTwAAAAAATwAAAAAATwAAAAADXgAAAAACTwAAAAABTwAAAAACTwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACXgAAAAAAXgAAAAADXgAAAAADTwAAAAAAXgAAAAACXgAAAAAAXgAAAAAAXgAAAAADXgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACTwAAAAAATwAAAAACTwAAAAACTwAAAAADTwAAAAAAXgAAAAABTwAAAAACTwAAAAADTwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABTwAAAAADTwAAAAAATwAAAAABTwAAAAAATwAAAAABXgAAAAAATwAAAAACXgAAAAACXgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAANQAAAAACNQAAAAACNQAAAAAATwAAAAACTwAAAAAAXgAAAAADTwAAAAACXgAAAAAAIAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAANQAAAAACTwAAAAACTwAAAAADTwAAAAAATwAAAAADXgAAAAADIAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAANQAAAAADTwAAAAACIAAAAAACIAAAAAADIAAAAAAAXgAAAAACIAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAAATwAAAAABIAAAAAADIAAAAAABIAAAAAABTwAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAABTwAAAAAAIAAAAAACIAAAAAADIAAAAAADXgAAAAAAIAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAAATwAAAAADIAAAAAADIAAAAAABIAAAAAAAXgAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAAATwAAAAABIAAAAAAAIAAAAAACIAAAAAABTwAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAIAAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAA
version: 6
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUAAAAAAQA1AAAAAAMANQAAAAABAE8AAAAAAwBPAAAAAAAATwAAAAADAE8AAAAAAQBeAAAAAAIAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAA1AAAAAAAANQAAAAAAADUAAAAAAQBPAAAAAAAATwAAAAACAF4AAAAAAwBPAAAAAAAAXgAAAAADAD8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAIATwAAAAABAE8AAAAAAQBPAAAAAAEATwAAAAACAE8AAAAAAQBeAAAAAAIATwAAAAADAF4AAAAAAQBPAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACAE8AAAAAAgBPAAAAAAEATwAAAAAAAE8AAAAAAABPAAAAAAMAXgAAAAACAE8AAAAAAQBPAAAAAAIATwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAgBeAAAAAAAAXgAAAAADAF4AAAAAAwBPAAAAAAAAXgAAAAACAF4AAAAAAABeAAAAAAAAXgAAAAADAF4AAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAIATwAAAAAAAE8AAAAAAgBPAAAAAAIATwAAAAADAE8AAAAAAABeAAAAAAEATwAAAAACAE8AAAAAAwBPAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABAE8AAAAAAwBPAAAAAAAATwAAAAABAE8AAAAAAABPAAAAAAEAXgAAAAAAAE8AAAAAAgBeAAAAAAIAXgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAA1AAAAAAIANQAAAAACADUAAAAAAABPAAAAAAIATwAAAAAAAF4AAAAAAwBPAAAAAAIAXgAAAAAAACAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAH4AAAAAAAA1AAAAAAIATwAAAAACAE8AAAAAAwBPAAAAAAAATwAAAAADAF4AAAAAAwAgAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAANQAAAAADAE8AAAAAAgAgAAAAAAIAIAAAAAADACAAAAAAAABeAAAAAAIAIAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUAAAAAAABPAAAAAAEAIAAAAAADACAAAAAAAQAgAAAAAAEATwAAAAACACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1AAAAAAEATwAAAAAAACAAAAAAAgAgAAAAAAMAIAAAAAADAF4AAAAAAAAgAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAAAAE8AAAAAAwAgAAAAAAMAIAAAAAABACAAAAAAAABeAAAAAAIAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUAAAAAAABPAAAAAAEAIAAAAAAAACAAAAAAAgAgAAAAAAEATwAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAACAAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAA==
version: 7
0,-1:
ind: 0,-1
tiles: TwAAAAAATwAAAAAATwAAAAADTwAAAAABTwAAAAADTwAAAAAATwAAAAADTwAAAAABXgAAAAADTwAAAAABXgAAAAADTwAAAAACTwAAAAADNQAAAAABfgAAAAAAfgAAAAAAXgAAAAADXgAAAAAAXgAAAAAAXgAAAAABXgAAAAABXgAAAAADXgAAAAACXgAAAAACXgAAAAADXgAAAAABXgAAAAABTwAAAAACTwAAAAACNQAAAAAANQAAAAADNQAAAAABTwAAAAACTwAAAAADTwAAAAAATwAAAAACTwAAAAAATwAAAAABTwAAAAACTwAAAAABTwAAAAADTwAAAAADXgAAAAABTwAAAAACTwAAAAABTwAAAAAATwAAAAABTwAAAAAATwAAAAABTwAAAAABTwAAAAABTwAAAAADTwAAAAAATwAAAAACTwAAAAAATwAAAAABTwAAAAADTwAAAAABXgAAAAADTwAAAAABTwAAAAAATwAAAAABTwAAAAACTwAAAAABXgAAAAADXgAAAAABXgAAAAADXgAAAAAAXgAAAAACXgAAAAABXgAAAAABXgAAAAADXgAAAAABXgAAAAABXgAAAAABXgAAAAAATwAAAAAAXgAAAAAAXgAAAAAAXgAAAAADTwAAAAACTwAAAAABTwAAAAACTwAAAAABTwAAAAACTwAAAAACTwAAAAAATwAAAAAATwAAAAABTwAAAAABXgAAAAACTwAAAAABTwAAAAACTwAAAAACTwAAAAACTwAAAAAATwAAAAACXgAAAAABXgAAAAABXgAAAAADXgAAAAAAXgAAAAAATwAAAAACTwAAAAABXgAAAAACTwAAAAAAXgAAAAADTwAAAAADTwAAAAACTwAAAAAATwAAAAADTwAAAAADcQAAAAACcQAAAAAAcQAAAAAAcQAAAAABcQAAAAADcQAAAAAAcQAAAAACcQAAAAABXgAAAAACTwAAAAABXgAAAAACTwAAAAABTwAAAAACNQAAAAAANQAAAAACNQAAAAABcQAAAAABcQAAAAADcQAAAAABcQAAAAAAcQAAAAABcQAAAAADcQAAAAADcQAAAAAAXgAAAAACTwAAAAAATwAAAAABTwAAAAADTwAAAAABNQAAAAAANQAAAAABNQAAAAABcQAAAAACcQAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAcQAAAAADcQAAAAAAcQAAAAADTwAAAAAATwAAAAACXgAAAAADTwAAAAABTwAAAAACNQAAAAAANQAAAAABNQAAAAADcQAAAAADcQAAAAABTwAAAAAATwAAAAAATwAAAAADcQAAAAADcQAAAAABcQAAAAACTwAAAAAATwAAAAADXgAAAAABTwAAAAADTwAAAAACTwAAAAADTwAAAAADTwAAAAADcQAAAAAAcQAAAAADTwAAAAAATwAAAAAATwAAAAABcQAAAAADcQAAAAADcQAAAAABTwAAAAACTwAAAAADXgAAAAADTwAAAAADTwAAAAAATwAAAAADTwAAAAAATwAAAAACXgAAAAACXgAAAAACXgAAAAACXgAAAAACXgAAAAADXgAAAAAAXgAAAAABTwAAAAADXgAAAAABTwAAAAABXgAAAAACXgAAAAABTwAAAAAAXgAAAAADXgAAAAADXgAAAAACDgAAAAACDgAAAAACDgAAAAADDgAAAAADDgAAAAABDgAAAAAADgAAAAADDgAAAAADTwAAAAACTwAAAAACXgAAAAADTwAAAAACTwAAAAACTwAAAAABTwAAAAABTwAAAAABDgAAAAADDgAAAAADDgAAAAAADgAAAAADDgAAAAACDgAAAAACDgAAAAADDgAAAAABTwAAAAAATwAAAAADXgAAAAACTwAAAAACTwAAAAACTwAAAAADTwAAAAACTwAAAAADPwAAAAAAPwAAAAAADgAAAAABDgAAAAABDgAAAAACDgAAAAAADgAAAAACTwAAAAABTwAAAAADTwAAAAACXgAAAAAATwAAAAAATwAAAAADNQAAAAABNQAAAAACNQAAAAAA
version: 6
tiles: TwAAAAAAAE8AAAAAAABPAAAAAAMATwAAAAABAE8AAAAAAwBPAAAAAAAATwAAAAADAE8AAAAAAQBeAAAAAAMATwAAAAABAF4AAAAAAwBPAAAAAAIATwAAAAADADUAAAAAAQB+AAAAAAAAfgAAAAAAAF4AAAAAAwBeAAAAAAAAXgAAAAAAAF4AAAAAAQBeAAAAAAEAXgAAAAADAF4AAAAAAgBeAAAAAAIAXgAAAAADAF4AAAAAAQBeAAAAAAEATwAAAAACAE8AAAAAAgA1AAAAAAAANQAAAAADADUAAAAAAQBPAAAAAAIATwAAAAADAE8AAAAAAABPAAAAAAIATwAAAAAAAE8AAAAAAQBPAAAAAAIATwAAAAABAE8AAAAAAwBPAAAAAAMAXgAAAAABAE8AAAAAAgBPAAAAAAEATwAAAAAAAE8AAAAAAQBPAAAAAAAATwAAAAABAE8AAAAAAQBPAAAAAAEATwAAAAADAE8AAAAAAABPAAAAAAIATwAAAAAAAE8AAAAAAQBPAAAAAAMATwAAAAABAF4AAAAAAwBPAAAAAAEATwAAAAAAAE8AAAAAAQBPAAAAAAIATwAAAAABAF4AAAAAAwBeAAAAAAEAXgAAAAADAF4AAAAAAABeAAAAAAIAXgAAAAABAF4AAAAAAQBeAAAAAAMAXgAAAAABAF4AAAAAAQBeAAAAAAEAXgAAAAAAAE8AAAAAAABeAAAAAAAAXgAAAAAAAF4AAAAAAwBPAAAAAAIATwAAAAABAE8AAAAAAgBPAAAAAAEATwAAAAACAE8AAAAAAgBPAAAAAAAATwAAAAAAAE8AAAAAAQBPAAAAAAEAXgAAAAACAE8AAAAAAQBPAAAAAAIATwAAAAACAE8AAAAAAgBPAAAAAAAATwAAAAACAF4AAAAAAQBeAAAAAAEAXgAAAAADAF4AAAAAAABeAAAAAAAATwAAAAACAE8AAAAAAQBeAAAAAAIATwAAAAAAAF4AAAAAAwBPAAAAAAMATwAAAAACAE8AAAAAAABPAAAAAAMATwAAAAADAHEAAAAAAgBxAAAAAAAAcQAAAAAAAHEAAAAAAQBxAAAAAAMAcQAAAAAAAHEAAAAAAgBxAAAAAAEAXgAAAAACAE8AAAAAAQBeAAAAAAIATwAAAAABAE8AAAAAAgA1AAAAAAAANQAAAAACADUAAAAAAQBxAAAAAAEAcQAAAAADAHEAAAAAAQBxAAAAAAAAcQAAAAABAHEAAAAAAwBxAAAAAAMAcQAAAAAAAF4AAAAAAgBPAAAAAAAATwAAAAABAE8AAAAAAwBPAAAAAAEANQAAAAAAADUAAAAAAQA1AAAAAAEAcQAAAAACAHEAAAAAAABQAAAAAAAAUAAAAAAAAFAAAAAAAABxAAAAAAMAcQAAAAAAAHEAAAAAAwBPAAAAAAAATwAAAAACAF4AAAAAAwBPAAAAAAEATwAAAAACADUAAAAAAAA1AAAAAAEANQAAAAADAHEAAAAAAwBxAAAAAAEATwAAAAAAAE8AAAAAAABPAAAAAAMAcQAAAAADAHEAAAAAAQBxAAAAAAIATwAAAAAAAE8AAAAAAwBeAAAAAAEATwAAAAADAE8AAAAAAgBPAAAAAAMATwAAAAADAE8AAAAAAwBxAAAAAAAAcQAAAAADAE8AAAAAAABPAAAAAAAATwAAAAABAHEAAAAAAwBxAAAAAAMAcQAAAAABAE8AAAAAAgBPAAAAAAMAXgAAAAADAE8AAAAAAwBPAAAAAAAATwAAAAADAE8AAAAAAABPAAAAAAIAXgAAAAACAF4AAAAAAgBeAAAAAAIAXgAAAAACAF4AAAAAAwBeAAAAAAAAXgAAAAABAE8AAAAAAwBeAAAAAAEATwAAAAABAF4AAAAAAgBeAAAAAAEATwAAAAAAAF4AAAAAAwBeAAAAAAMAXgAAAAACAA4AAAAAAgAOAAAAAAIADgAAAAADAA4AAAAAAwAOAAAAAAEADgAAAAAAAA4AAAAAAwAOAAAAAAMATwAAAAACAE8AAAAAAgBeAAAAAAMATwAAAAACAE8AAAAAAgBPAAAAAAEATwAAAAABAE8AAAAAAQAOAAAAAAMADgAAAAADAA4AAAAAAAAOAAAAAAMADgAAAAACAA4AAAAAAgAOAAAAAAMADgAAAAABAE8AAAAAAABPAAAAAAMAXgAAAAACAE8AAAAAAgBPAAAAAAIATwAAAAADAE8AAAAAAgBPAAAAAAMAPwAAAAAAAD8AAAAAAAAOAAAAAAEADgAAAAABAA4AAAAAAgAOAAAAAAAADgAAAAACAE8AAAAAAQBPAAAAAAMATwAAAAACAF4AAAAAAABPAAAAAAAATwAAAAADADUAAAAAAQA1AAAAAAIANQAAAAAAAA==
version: 7
-1,-1:
ind: -1,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAAATwAAAAAATwAAAAABXgAAAAACTwAAAAAAXgAAAAABTwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAANQAAAAACNQAAAAAANQAAAAACTwAAAAAATwAAAAACXgAAAAACXgAAAAAAXgAAAAAAXgAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABTwAAAAADTwAAAAAATwAAAAACTwAAAAACTwAAAAAAXgAAAAADTwAAAAADTwAAAAADTwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACTwAAAAABTwAAAAABTwAAAAACTwAAAAACTwAAAAABXgAAAAADTwAAAAAATwAAAAAATwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABXgAAAAAAXgAAAAAAXgAAAAABTwAAAAADXgAAAAACXgAAAAADXgAAAAACXgAAAAACXgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAADTwAAAAAATwAAAAABTwAAAAACTwAAAAADTwAAAAACXgAAAAADTwAAAAADTwAAAAAATwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAATwAAAAACTwAAAAACTwAAAAAATwAAAAADTwAAAAAAXgAAAAADTwAAAAACXgAAAAADTwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAANQAAAAAANQAAAAAANQAAAAADTwAAAAACTwAAAAADXgAAAAADTwAAAAAAXgAAAAABcQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAACNQAAAAADNQAAAAADTwAAAAACTwAAAAACTwAAAAABTwAAAAABXgAAAAACcQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAANQAAAAADNQAAAAADNQAAAAABTwAAAAAATwAAAAACXgAAAAABTwAAAAABTwAAAAAAcQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAXgAAAAABXgAAAAABXgAAAAADTwAAAAADTwAAAAAAXgAAAAADTwAAAAABTwAAAAADcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABTwAAAAABTwAAAAAATwAAAAADTwAAAAACTwAAAAACXgAAAAABTwAAAAACTwAAAAABcQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACXgAAAAABXgAAAAAAXgAAAAAATwAAAAACXgAAAAABXgAAAAADTwAAAAACTwAAAAABTwAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAADTwAAAAACTwAAAAABTwAAAAADTwAAAAACTwAAAAAAXgAAAAABTwAAAAAATwAAAAADDgAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACTwAAAAACTwAAAAACTwAAAAADTwAAAAADTwAAAAACXgAAAAAATwAAAAADTwAAAAABDgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAANQAAAAACNQAAAAABNQAAAAAATwAAAAAATwAAAAAAXgAAAAADTwAAAAAATwAAAAABTwAAAAAD
version: 6
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAAAAE8AAAAAAABPAAAAAAEAXgAAAAACAE8AAAAAAABeAAAAAAEATwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAA1AAAAAAIANQAAAAAAADUAAAAAAgBPAAAAAAAATwAAAAACAF4AAAAAAgBeAAAAAAAAXgAAAAAAAF4AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAEATwAAAAADAE8AAAAAAABPAAAAAAIATwAAAAACAE8AAAAAAABeAAAAAAMATwAAAAADAE8AAAAAAwBPAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACAE8AAAAAAQBPAAAAAAEATwAAAAACAE8AAAAAAgBPAAAAAAEAXgAAAAADAE8AAAAAAABPAAAAAAAATwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAQBeAAAAAAAAXgAAAAAAAF4AAAAAAQBPAAAAAAMAXgAAAAACAF4AAAAAAwBeAAAAAAIAXgAAAAACAF4AAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAMATwAAAAAAAE8AAAAAAQBPAAAAAAIATwAAAAADAE8AAAAAAgBeAAAAAAMATwAAAAADAE8AAAAAAABPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAE8AAAAAAgBPAAAAAAIATwAAAAAAAE8AAAAAAwBPAAAAAAAAXgAAAAADAE8AAAAAAgBeAAAAAAMATwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAA1AAAAAAAANQAAAAAAADUAAAAAAwBPAAAAAAIATwAAAAADAF4AAAAAAwBPAAAAAAAAXgAAAAABAHEAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAACADUAAAAAAwA1AAAAAAMATwAAAAACAE8AAAAAAgBPAAAAAAEATwAAAAABAF4AAAAAAgBxAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAADUAAAAAAwA1AAAAAAMANQAAAAABAE8AAAAAAABPAAAAAAIAXgAAAAABAE8AAAAAAQBPAAAAAAAAcQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAABeAAAAAAEAXgAAAAABAF4AAAAAAwBPAAAAAAMATwAAAAAAAF4AAAAAAwBPAAAAAAEATwAAAAADAHEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAEATwAAAAABAE8AAAAAAABPAAAAAAMATwAAAAACAE8AAAAAAgBeAAAAAAEATwAAAAACAE8AAAAAAQBxAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACAF4AAAAAAQBeAAAAAAAAXgAAAAAAAE8AAAAAAgBeAAAAAAEAXgAAAAADAE8AAAAAAgBPAAAAAAEATwAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAwBPAAAAAAIATwAAAAABAE8AAAAAAwBPAAAAAAIATwAAAAAAAF4AAAAAAQBPAAAAAAAATwAAAAADAA4AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAIATwAAAAACAE8AAAAAAgBPAAAAAAMATwAAAAADAE8AAAAAAgBeAAAAAAAATwAAAAADAE8AAAAAAQAOAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAADUAAAAAAgA1AAAAAAEANQAAAAAAAE8AAAAAAABPAAAAAAAAXgAAAAADAE8AAAAAAABPAAAAAAEATwAAAAADAA==
version: 7
0,0:
ind: 0,0
tiles: PwAAAAAAPwAAAAAADgAAAAACDgAAAAAADgAAAAADDgAAAAABDgAAAAABDgAAAAADXgAAAAABTwAAAAADTwAAAAADTwAAAAABTwAAAAADNQAAAAACNQAAAAACNQAAAAAAPwAAAAAAPwAAAAAADgAAAAADDgAAAAAADgAAAAACDgAAAAABDgAAAAAADgAAAAADXgAAAAACTwAAAAADXgAAAAABTwAAAAABTwAAAAAANQAAAAAANQAAAAACNQAAAAABTwAAAAAATwAAAAACTwAAAAAAXgAAAAABTwAAAAAATwAAAAABTwAAAAACTwAAAAABXgAAAAAATwAAAAABXgAAAAACTwAAAAACTwAAAAAATwAAAAABTwAAAAADTwAAAAAATwAAAAADTwAAAAAATwAAAAABTwAAAAADTwAAAAACTwAAAAACTwAAAAAATwAAAAADTwAAAAADTwAAAAADXgAAAAAATwAAAAACTwAAAAACTwAAAAACTwAAAAABTwAAAAADXgAAAAAAXgAAAAACXgAAAAAAXgAAAAABXgAAAAAAXgAAAAAAXgAAAAABXgAAAAABXgAAAAACXgAAAAADXgAAAAABXgAAAAADTwAAAAABXgAAAAADXgAAAAAAXgAAAAACTwAAAAAATwAAAAAATwAAAAABTwAAAAABTwAAAAAATwAAAAACTwAAAAAATwAAAAABTwAAAAACTwAAAAAAXgAAAAAATwAAAAACTwAAAAACTwAAAAADTwAAAAACTwAAAAACTwAAAAACXgAAAAAATwAAAAAATwAAAAAATwAAAAAAXgAAAAABTwAAAAADXgAAAAACXgAAAAABTwAAAAACXgAAAAAATwAAAAABTwAAAAABTwAAAAADTwAAAAABTwAAAAABIAAAAAACAgAAAAAAAgAAAAAATwAAAAAAAgAAAAAAAgAAAAAAIAAAAAABIAAAAAAAXgAAAAADTwAAAAADXgAAAAAATwAAAAABTwAAAAADNQAAAAADNQAAAAACNQAAAAACIAAAAAACAgAAAAAAAgAAAAAATwAAAAAAAgAAAAAAAgAAAAAAIAAAAAACIAAAAAAAXgAAAAAATwAAAAABTwAAAAACTwAAAAACTwAAAAACNQAAAAAAAAAAAAAAAAAAAAAAIAAAAAABAgAAAAAAAgAAAAAATwAAAAAAAgAAAAAAAgAAAAAAIAAAAAABIAAAAAADTwAAAAABIAAAAAACIAAAAAABIAAAAAADTwAAAAAANQAAAAABfgAAAAAAAAAAAAAAIAAAAAAAIAAAAAAAAgAAAAAAIAAAAAADAgAAAAAAIAAAAAAAIAAAAAADIAAAAAADTwAAAAADIAAAAAAAIAAAAAAAIAAAAAACTwAAAAAANQAAAAABfgAAAAAAAAAAAAAAIAAAAAADIAAAAAAAIAAAAAAAIAAAAAACIAAAAAAAIAAAAAAAIAAAAAACIAAAAAAATwAAAAABIAAAAAADIAAAAAABIAAAAAAAXgAAAAADNQAAAAACAAAAAAAAAAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAIAAAAAAAXgAAAAAAIAAAAAABIAAAAAAAIAAAAAAATwAAAAAANQAAAAABfgAAAAAAAAAAAAAAAgAAAAAAIAAAAAACAgAAAAAAAgAAAAAAAgAAAAAAIAAAAAACAgAAAAAAAgAAAAAATwAAAAADIAAAAAACIAAAAAABIAAAAAAATwAAAAADNQAAAAABAAAAAAAAAAAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAAIAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAATwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
tiles: PwAAAAAAAD8AAAAAAAAOAAAAAAIADgAAAAAAAA4AAAAAAwAOAAAAAAEADgAAAAABAA4AAAAAAwBeAAAAAAEATwAAAAADAE8AAAAAAwBPAAAAAAEATwAAAAADADUAAAAAAgA1AAAAAAIANQAAAAAAAD8AAAAAAAA/AAAAAAAADgAAAAADAA4AAAAAAAAOAAAAAAIADgAAAAABAA4AAAAAAAAOAAAAAAMAXgAAAAACAE8AAAAAAwBeAAAAAAEATwAAAAABAE8AAAAAAAA1AAAAAAAANQAAAAACADUAAAAAAQBPAAAAAAAATwAAAAACAE8AAAAAAABeAAAAAAEATwAAAAAAAE8AAAAAAQBPAAAAAAIATwAAAAABAF4AAAAAAABPAAAAAAEAXgAAAAACAE8AAAAAAgBPAAAAAAAATwAAAAABAE8AAAAAAwBPAAAAAAAATwAAAAADAE8AAAAAAABPAAAAAAEATwAAAAADAE8AAAAAAgBPAAAAAAIATwAAAAAAAE8AAAAAAwBPAAAAAAMATwAAAAADAF4AAAAAAABPAAAAAAIATwAAAAACAE8AAAAAAgBPAAAAAAEATwAAAAADAF4AAAAAAABeAAAAAAIAXgAAAAAAAF4AAAAAAQBeAAAAAAAAXgAAAAAAAF4AAAAAAQBeAAAAAAEAXgAAAAACAF4AAAAAAwBeAAAAAAEAXgAAAAADAE8AAAAAAQBeAAAAAAMAXgAAAAAAAF4AAAAAAgBPAAAAAAAATwAAAAAAAE8AAAAAAQBPAAAAAAEATwAAAAAAAE8AAAAAAgBPAAAAAAAATwAAAAABAE8AAAAAAgBPAAAAAAAAXgAAAAAAAE8AAAAAAgBPAAAAAAIATwAAAAADAE8AAAAAAgBPAAAAAAIATwAAAAACAF4AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABeAAAAAAEATwAAAAADAF4AAAAAAgBeAAAAAAEATwAAAAACAF4AAAAAAABPAAAAAAEATwAAAAABAE8AAAAAAwBPAAAAAAEATwAAAAABACAAAAAAAgACAAAAAAAAAgAAAAAAAE8AAAAAAAACAAAAAAAAAgAAAAAAACAAAAAAAQAgAAAAAAAAXgAAAAADAE8AAAAAAwBeAAAAAAAATwAAAAABAE8AAAAAAwA1AAAAAAMANQAAAAACADUAAAAAAgAgAAAAAAIAAgAAAAAAAAIAAAAAAABPAAAAAAAAAgAAAAAAAAIAAAAAAAAgAAAAAAIAIAAAAAAAAF4AAAAAAABPAAAAAAEATwAAAAACAE8AAAAAAgBPAAAAAAIANQAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAABAAIAAAAAAAACAAAAAAAATwAAAAAAAAIAAAAAAAACAAAAAAAAIAAAAAABACAAAAAAAwBPAAAAAAEAIAAAAAACACAAAAAAAQAgAAAAAAMATwAAAAAAADUAAAAAAQB+AAAAAAAAAAAAAAAAACAAAAAAAAAgAAAAAAAAAgAAAAAAACAAAAAAAwACAAAAAAAAIAAAAAAAACAAAAAAAwAgAAAAAAMATwAAAAADACAAAAAAAAAgAAAAAAAAIAAAAAACAE8AAAAAAAA1AAAAAAEAfgAAAAAAAAAAAAAAAAAgAAAAAAMAIAAAAAAAACAAAAAAAAAgAAAAAAIAIAAAAAAAACAAAAAAAAAgAAAAAAIAIAAAAAAAAE8AAAAAAQAgAAAAAAMAIAAAAAABACAAAAAAAABeAAAAAAMANQAAAAACAAAAAAAAAAAAAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAAAgAAAAAAAAIAAAAAAAACAAAAAAAABeAAAAAAAAIAAAAAABACAAAAAAAAAgAAAAAAAATwAAAAAAADUAAAAAAQB+AAAAAAAAAAAAAAAAAAIAAAAAAAAgAAAAAAIAAgAAAAAAAAIAAAAAAAACAAAAAAAAIAAAAAACAAIAAAAAAAACAAAAAAAATwAAAAADACAAAAAAAgAgAAAAAAEAIAAAAAAAAE8AAAAAAwA1AAAAAAEAAAAAAAAAAAAAAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAACAAAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAwAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAABPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
version: 7
1,-1:
ind: 1,-1
tiles: fgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
tiles: fgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
version: 7
1,0:
ind: 1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
version: 7
-1,-2:
ind: -1,-2
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAIAAAAAAAIAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAIAAAAAACIAAAAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAIAAAAAADIAAAAAADIAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAIAAAAAABIAAAAAADIAAAAAAAXgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAIAAAAAACIAAAAAADTwAAAAABXgAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAAATwAAAAABTwAAAAADIAAAAAAAIAAAAAABTwAAAAAAXgAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAANQAAAAADfwAAAAAATwAAAAABIAAAAAAAIAAAAAAATwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANQAAAAADfwAAAAAATwAAAAABIAAAAAACIAAAAAADTwAAAAABfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAANQAAAAABfwAAAAAATwAAAAABTwAAAAADTwAAAAAAIAAAAAACfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAANQAAAAACXgAAAAAATwAAAAACXgAAAAABTwAAAAAAXgAAAAABTwAAAAAC
version: 6
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAACAAAAAAAAAgAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAH8AAAAAAAAgAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAAgAAAAAAIAIAAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAB/AAAAAAAAIAAAAAADACAAAAAAAwAgAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAIAAAAAABACAAAAAAAwAgAAAAAAAAXgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAfwAAAAAAACAAAAAAAgAgAAAAAAMATwAAAAABAF4AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1AAAAAAAATwAAAAABAE8AAAAAAwAgAAAAAAAAIAAAAAABAE8AAAAAAABeAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAANQAAAAADAH8AAAAAAABPAAAAAAEAIAAAAAAAACAAAAAAAABPAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADUAAAAAAwB/AAAAAAAATwAAAAABACAAAAAAAgAgAAAAAAMATwAAAAABAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAA1AAAAAAEAfwAAAAAAAH8AAAAAAABPAAAAAAMATwAAAAAAACAAAAAAAgB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAANQAAAAACAF4AAAAAAABPAAAAAAIAXgAAAAABAE8AAAAAAABeAAAAAAEATwAAAAACAA==
version: 7
0,-2:
ind: 0,-2
tiles: fwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAACIAAAAAABIAAAAAACIAAAAAADIAAAAAADIAAAAAADIAAAAAABIAAAAAAAIAAAAAABfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAADIAAAAAACfwAAAAAAfwAAAAAAfwAAAAAAIAAAAAAAIAAAAAADIAAAAAADIAAAAAABfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAABIAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAIAAAAAADIAAAAAABIAAAAAACIAAAAAACIAAAAAACfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAIAAAAAACIAAAAAACIAAAAAAAIAAAAAADfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAXgAAAAABfwAAAAAAfwAAAAAAfwAAAAAAXgAAAAABIAAAAAACIAAAAAACIAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAXgAAAAACfwAAAAAAfwAAAAAAfwAAAAAAXgAAAAADTwAAAAADIAAAAAAAIAAAAAADfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAXgAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAXgAAAAAATwAAAAACIAAAAAABIAAAAAAATwAAAAAAfwAAAAAANQAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAATwAAAAADTwAAAAABIAAAAAADIAAAAAADTwAAAAACfwAAAAAANQAAAAABfgAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAATwAAAAADTwAAAAACIAAAAAACIAAAAAAATwAAAAAAfwAAAAAANQAAAAABAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAIAAAAAADTwAAAAABIAAAAAADTwAAAAACfwAAAAAANQAAAAADAAAAAAAAAAAAAAAATwAAAAAATwAAAAAATwAAAAACTwAAAAADTwAAAAABTwAAAAABTwAAAAACTwAAAAABXgAAAAADTwAAAAACXgAAAAABTwAAAAADXgAAAAACNQAAAAAAfgAAAAAAAAAAAAAA
version: 6
tiles: fwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAIAIAAAAAABACAAAAAAAgAgAAAAAAMAIAAAAAADACAAAAAAAwAgAAAAAAEAIAAAAAAAACAAAAAAAQB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAADACAAAAAAAgB/AAAAAAAAfwAAAAAAAH8AAAAAAAAgAAAAAAAAIAAAAAADACAAAAAAAwAgAAAAAAEAfwAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAQAgAAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAIAAAAAADACAAAAAAAQAgAAAAAAIAIAAAAAACACAAAAAAAgB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAAgAAAAAAIAIAAAAAACACAAAAAAAAAgAAAAAAMAfwAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAXgAAAAABAH8AAAAAAAB/AAAAAAAAfwAAAAAAAF4AAAAAAQAgAAAAAAIAIAAAAAACACAAAAAAAAB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAF4AAAAAAgB/AAAAAAAAfwAAAAAAAH8AAAAAAABeAAAAAAMATwAAAAADACAAAAAAAAAgAAAAAAMAfwAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAABeAAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAXgAAAAAAAE8AAAAAAgAgAAAAAAEAIAAAAAAAAE8AAAAAAAB/AAAAAAAANQAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAE8AAAAAAwBPAAAAAAEAIAAAAAADACAAAAAAAwBPAAAAAAIAfwAAAAAAADUAAAAAAQB+AAAAAAAAAAAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAABPAAAAAAMATwAAAAACACAAAAAAAgAgAAAAAAAATwAAAAAAAH8AAAAAAAA1AAAAAAEAAAAAAAAAAAAAAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAACAAAAAAAwBPAAAAAAEAIAAAAAADAH8AAAAAAAB/AAAAAAAANQAAAAADAAAAAAAAAAAAAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAIATwAAAAADAE8AAAAAAQBPAAAAAAEATwAAAAACAE8AAAAAAQBeAAAAAAMATwAAAAACAF4AAAAAAQBPAAAAAAMAXgAAAAACADUAAAAAAAB+AAAAAAAAAAAAAAAAAA==
version: 7
0,1:
ind: 0,1
tiles: AwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAATwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAIAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAQAAAAAAIAAAAAAAIAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAQAAAAAAAQAAAAAATwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAQAAAAAAAQAAAAAATwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAwAAAAAAAQAAAAAAAQAAAAAATwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAATwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
tiles: AwAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAwAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAABPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAgAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAEAAAAAAAADAAAAAAAAAQAAAAAAACAAAAAAAAAgAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAMAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAADAAAAAAAAAQAAAAAAAAEAAAAAAABPAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAADAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAwAAAAAAAAEAAAAAAAABAAAAAAAATwAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAwAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAMAAAAAAAABAAAAAAAAAQAAAAAAAE8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAABPAAAAAAAATwAAAAAAAE8AAAAAAAB+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
version: 7
-1,1:
ind: -1,1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAIAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAIAAAAAAAIAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAATwAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAATwAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATwAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAATwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPAAAAAAAAAQAAAAAAAAEAAAAAAAABAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAACAAAAAAAAADAAAAAAAAAwAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAAIAAAAAAAACAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAABPAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAATwAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE8AAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAAAAAATwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
version: 7
0,-3:
ind: 0,-3
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
version: 7
-1,-3:
ind: -1,-3
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/AAAAAAAAfwAAAAAAAH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH8AAAAAAAB/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
version: 7
- type: Broadphase
- type: Physics
bodyStatus: InAir
@ -89,15 +100,16 @@ entities:
- type: OccluderTree
- type: SpreaderGrid
- type: Shuttle
dampingModifier: 0.25
- type: GridPathfinding
- type: Gravity
gravityShakeSound: !type:SoundPathSpecifier
path: /Audio/Effects/alert.ogg
- type: DeviceNetwork
deviceNetId: Wireless
configurators: []
deviceLists: []
transmitFrequencyId: ShuttleTimer
deviceNetId: Wireless
- type: DecalGrid
chunkCollection:
version: 2
@ -1251,6 +1263,7 @@ entities:
chunkSize: 4
- type: GasTileOverlay
- type: RadiationGridResistance
- type: ImplicitRoof
- proto: AirAlarm
entities:
- uid: 1885
@ -1379,7 +1392,6 @@ entities:
- 1562
- 85
- 1737
- 21
- 722
- 762
- 1589
@ -1398,10 +1410,8 @@ entities:
- 1342
- 1340
- 1738
- 1739
- 971
- 1737
- 21
- 973
- 1592
- 1591
@ -1422,7 +1432,6 @@ entities:
- 681
- 689
- 1738
- 1739
- 971
- 1601
- 762
@ -1523,11 +1532,6 @@ entities:
rot: 1.5707963267948966 rad
pos: 9.5,-17.5
parent: 1
- uid: 1748
components:
- type: Transform
pos: 11.5,-17.5
parent: 1
- proto: AirlockCommandGlassLocked
entities:
- uid: 47
@ -1560,12 +1564,6 @@ entities:
- type: Transform
pos: -2.5,-22.5
parent: 1
- uid: 1747
components:
- type: Transform
rot: 3.141592653589793 rad
pos: -4.5,-17.5
parent: 1
- proto: AirlockExternalEngineeringLocked
entities:
- uid: 1636
@ -1577,9 +1575,11 @@ entities:
- type: DeviceLinkSource
linkedPorts:
1637:
- DoorStatus: DoorBolt
- - DoorStatus
- DoorBolt
1635:
- DoorStatus: DoorBolt
- - DoorStatus
- DoorBolt
- uid: 1640
components:
- type: Transform
@ -1589,9 +1589,11 @@ entities:
- type: DeviceLinkSource
linkedPorts:
1635:
- DoorStatus: DoorBolt
- - DoorStatus
- DoorBolt
1637:
- DoorStatus: DoorBolt
- - DoorStatus
- DoorBolt
- proto: AirlockExternalGlass
entities:
- uid: 25
@ -1671,9 +1673,11 @@ entities:
- type: DeviceLinkSource
linkedPorts:
1636:
- DoorStatus: DoorBolt
- - DoorStatus
- DoorBolt
1640:
- DoorStatus: DoorBolt
- - DoorStatus
- DoorBolt
- uid: 1637
components:
- type: Transform
@ -1683,9 +1687,11 @@ entities:
- type: DeviceLinkSource
linkedPorts:
1636:
- DoorStatus: DoorBolt
- - DoorStatus
- DoorBolt
1640:
- DoorStatus: DoorBolt
- - DoorStatus
- DoorBolt
- proto: AirlockGlass
entities:
- uid: 26
@ -2260,12 +2266,6 @@ entities:
parent: 1
- proto: BenchSofaCorpLeft
entities:
- uid: 45
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -4.5,-19.5
parent: 1
- uid: 268
components:
- type: Transform
@ -2446,12 +2446,6 @@ entities:
- type: Transform
pos: 6.5,-13.5
parent: 1
- uid: 690
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -4.5,-20.5
parent: 1
- uid: 733
components:
- type: Transform
@ -5247,21 +5241,28 @@ entities:
- type: Transform
pos: 7.5,-4.5
parent: 1
- uid: 215
- uid: 45
components:
- type: Transform
pos: -0.5,-4.5
parent: 1
- uid: 216
components:
- type: Transform
pos: 1.5,-4.5
parent: 1
- uid: 326
- uid: 214
components:
- type: Transform
pos: 0.5,-4.5
parent: 1
- uid: 215
components:
- type: Transform
rot: 3.141592653589793 rad
pos: -0.5,-6.5
parent: 1
- uid: 330
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 0.5,-6.5
parent: 1
- uid: 378
components:
- type: Transform
@ -5284,18 +5285,6 @@ entities:
rot: 3.141592653589793 rad
pos: 7.5,-6.5
parent: 1
- uid: 426
components:
- type: Transform
rot: 3.141592653589793 rad
pos: -0.5,-6.5
parent: 1
- uid: 427
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 0.5,-6.5
parent: 1
- uid: 485
components:
- type: Transform
@ -5338,6 +5327,11 @@ entities:
rot: -1.5707963267948966 rad
pos: 7.5,8.5
parent: 1
- uid: 690
components:
- type: Transform
pos: 1.5,-4.5
parent: 1
- uid: 1584
components:
- type: Transform
@ -5441,6 +5435,14 @@ entities:
- type: Transform
pos: 4.5,18.5
parent: 1
- proto: ChemistryBottleEpinephrine
entities:
- uid: 352
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: 2.3577118,-8.211421
parent: 1
- proto: ClosetWallEmergencyFilledRandom
entities:
- uid: 175
@ -5517,21 +5519,6 @@ entities:
rot: -1.5707963267948966 rad
pos: 12.5,-9.5
parent: 1
- proto: ComputerCloningConsole
entities:
- uid: 214
components:
- type: Transform
pos: 3.5,-6.5
parent: 1
- type: DeviceLinkSource
linkedPorts:
330:
- MedicalScannerSender: MedicalScannerReceiver
- CloningPodSender: MedicalScannerReceiver
370:
- MedicalScannerSender: CloningPodReceiver
- CloningPodSender: CloningPodReceiver
- proto: ComputerComms
entities:
- uid: 2238
@ -6291,26 +6278,8 @@ entities:
- type: Transform
pos: 3.136067,-5.337432
parent: 1
- proto: EpinephrineChemistryBottle
entities:
- uid: 352
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: 2.3577118,-8.211421
parent: 1
- proto: FirelockGlass
entities:
- uid: 21
components:
- type: Transform
rot: 3.141592653589793 rad
pos: -4.5,-17.5
parent: 1
- type: DeviceNetwork
deviceLists:
- 1893
- 1894
- uid: 23
components:
- type: Transform
@ -6550,16 +6519,6 @@ entities:
deviceLists:
- 1895
- 1894
- uid: 1739
components:
- type: Transform
rot: 3.141592653589793 rad
pos: 11.5,-17.5
parent: 1
- type: DeviceNetwork
deviceLists:
- 1895
- 1894
- proto: FloorDrain
entities:
- uid: 2085
@ -6593,7 +6552,7 @@ entities:
parent: 1
- type: Fixtures
fixtures: {}
- proto: FloraTree02
- proto: FloraTree
entities:
- uid: 1923
components:
@ -6607,15 +6566,13 @@ entities:
rot: 1.5707963267948966 rad
pos: -7.6932545,7.499033
parent: 1
- proto: FloraTree04
entities:
- uid: 1926
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: 14.759519,-6.5801034
parent: 1
- proto: FloraTreeLarge04
- proto: FloraTreeLarge
entities:
- uid: 1927
components:
@ -11424,6 +11381,13 @@ entities:
rot: 1.5707963267948966 rad
pos: -0.70859075,18.949242
parent: 1
- proto: HolopadCentCommEvacShuttle
entities:
- uid: 1748
components:
- type: Transform
pos: 3.5,19.5
parent: 1
- proto: HospitalCurtainsOpen
entities:
- uid: 382
@ -11527,6 +11491,23 @@ entities:
- type: Transform
pos: -1.5,-25.5
parent: 1
- proto: LockerEvacRepairFilled
entities:
- uid: 21
components:
- type: Transform
pos: -4.5,-18.5
parent: 1
- uid: 1739
components:
- type: Transform
pos: -4.5,-20.5
parent: 1
- uid: 1747
components:
- type: Transform
pos: -4.5,-19.5
parent: 1
- proto: LockerEvidence
entities:
- uid: 685
@ -11637,13 +11618,6 @@ entities:
- type: Transform
pos: 7.5,-8.5
parent: 1
- proto: MedicalScanner
entities:
- uid: 330
components:
- type: Transform
pos: 4.5,-6.5
parent: 1
- proto: MedkitAdvancedFilled
entities:
- uid: 2064
@ -11660,13 +11634,6 @@ entities:
rot: 1.5707963267948966 rad
pos: 2.1233368,-8.3442335
parent: 1
- proto: MetempsychoticMachine
entities:
- uid: 370
components:
- type: Transform
pos: 2.5,-6.5
parent: 1
- proto: MopBucketFull
entities:
- uid: 2083
@ -11770,13 +11737,6 @@ entities:
rot: 1.5707963267948966 rad
pos: -0.21379477,-16.4499
parent: 1
- proto: PlushieLizard
entities:
- uid: 2044
components:
- type: Transform
pos: 1.5054002,-4.5560694
parent: 1
- proto: PlushieMoth
entities:
- uid: 2039
@ -11799,14 +11759,6 @@ entities:
- type: Transform
pos: 6.8242917,-37.207977
parent: 1
- proto: PlushiePenguin
entities:
- uid: 2034
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -4.4982705,-20.393938
parent: 1
- proto: PlushieRGBee
entities:
- uid: 2028
@ -11867,14 +11819,6 @@ entities:
rot: 1.5707963267948966 rad
pos: 9.399769,-3.016137
parent: 1
- proto: PlushieSpaceLizard
entities:
- uid: 2037
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -4.529521,-19.893938
parent: 1
- proto: PlushieXeno
entities:
- uid: 2038
@ -13242,42 +13186,7 @@ entities:
- type: Transform
pos: 5.5,14.5
parent: 1
- proto: ShadowTree01
entities:
- uid: 1918
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: 13.273117,-15.984801
parent: 1
- uid: 1929
components:
- type: Transform
pos: -6.6538024,7.966305
parent: 1
- proto: ShadowTree03
entities:
- uid: 1914
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -6.367117,13.323404
parent: 1
- uid: 1920
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -8.080735,-6.4842486
parent: 1
- proto: ShadowTree04
entities:
- uid: 1915
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: 14.303209,7.7148037
parent: 1
- proto: ShadowTree05
- proto: ShadowTree
entities:
- uid: 422
components:
@ -13290,26 +13199,53 @@ entities:
rot: 1.5707963267948966 rad
pos: 13.382492,13.271562
parent: 1
- uid: 1914
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -6.367117,13.323404
parent: 1
- uid: 1915
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: 14.303209,7.7148037
parent: 1
- uid: 1916
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: 13.640305,-19.63101
parent: 1
- uid: 1918
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: 13.273117,-15.984801
parent: 1
- uid: 1919
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -7.7875977,-14.561567
parent: 1
- proto: ShadowTree06
entities:
- uid: 1920
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: -8.080735,-6.4842486
parent: 1
- uid: 1922
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: 13.233812,1.3556528
parent: 1
- uid: 1929
components:
- type: Transform
pos: -6.6538024,7.966305
parent: 1
- proto: SheetSteel
entities:
- uid: 556
@ -14239,6 +14175,18 @@ entities:
- type: Transform
pos: 3.5,-7.5
parent: 1
- proto: StasisBed
entities:
- uid: 216
components:
- type: Transform
pos: 2.5,-6.5
parent: 1
- uid: 326
components:
- type: Transform
pos: 4.5,-6.5
parent: 1
- proto: StoolBar
entities:
- uid: 399
@ -14346,7 +14294,7 @@ entities:
- type: Transform
pos: 9.5,12.5
parent: 1
- proto: SuitStorageSec
- proto: SuitStorageSecDeltaV
entities:
- uid: 1373
components:
@ -15338,6 +15286,11 @@ entities:
rot: -1.5707963267948966 rad
pos: -5.5,-5.5
parent: 1
- uid: 370
components:
- type: Transform
pos: -4.5,-17.5
parent: 1
- uid: 373
components:
- type: Transform
@ -15349,6 +15302,11 @@ entities:
rot: -1.5707963267948966 rad
pos: -5.5,4.5
parent: 1
- uid: 426
components:
- type: Transform
pos: 11.5,-17.5
parent: 1
- uid: 442
components:
- type: Transform
@ -15874,6 +15832,14 @@ entities:
rot: -1.5707963267948966 rad
pos: -0.5,19.5
parent: 1
- proto: WehMedipen
entities:
- uid: 427
components:
- type: Transform
rot: 1.5707963267948966 rad
pos: 2.6532867,-4.4037185
parent: 1
- proto: WindoorBarKitchenLocked
entities:
- uid: 392

View File

@ -4,7 +4,7 @@ meta:
engineVersion: 262.0.0
forkId: ""
forkVersion: ""
time: 10/09/2025 12:04:17
time: 10/13/2025 17:47:39
entityCount: 10383
maps: []
grids:
@ -52930,7 +52930,7 @@ entities:
- uid: 5608
components:
- type: Transform
pos: -19.5,17.5
pos: -20.5,17.5
parent: 1
- uid: 5609
components:

View File

@ -4,8 +4,8 @@ meta:
engineVersion: 262.0.0
forkId: ""
forkVersion: ""
time: 10/13/2025 02:04:30
entityCount: 46427
time: 10/15/2025 20:26:10
entityCount: 46437
maps:
- 1
grids:
@ -75107,11 +75107,6 @@ entities:
- type: Transform
pos: -2.5,40.5
parent: 2
- uid: 46228
components:
- type: Transform
pos: -2.5,39.5
parent: 2
- uid: 46396
components:
- type: Transform
@ -75122,6 +75117,16 @@ entities:
- type: Transform
pos: -48.5,-16.5
parent: 2
- uid: 46435
components:
- type: Transform
pos: -3.5,41.5
parent: 2
- uid: 46436
components:
- type: Transform
pos: -2.5,35.5
parent: 2
- proto: CableApcStack
entities:
- uid: 5935
@ -98775,6 +98780,11 @@ entities:
- type: Transform
pos: -70.5,-9.5
parent: 2
- uid: 46437
components:
- type: Transform
pos: -67.5,-8.5
parent: 2
- proto: CableMVStack
entities:
- uid: 5937
@ -263305,6 +263315,48 @@ entities:
- type: Transform
pos: 10.5,47.5
parent: 2
- proto: SpawnPointLatejoin
entities:
- uid: 46228
components:
- type: Transform
pos: 33.5,22.5
parent: 2
- uid: 46428
components:
- type: Transform
pos: 33.5,21.5
parent: 2
- uid: 46429
components:
- type: Transform
pos: 33.5,17.5
parent: 2
- uid: 46430
components:
- type: Transform
pos: 33.5,16.5
parent: 2
- uid: 46431
components:
- type: Transform
pos: 35.5,22.5
parent: 2
- uid: 46432
components:
- type: Transform
pos: 35.5,21.5
parent: 2
- uid: 46433
components:
- type: Transform
pos: 35.5,17.5
parent: 2
- uid: 46434
components:
- type: Transform
pos: 35.5,16.5
parent: 2
- proto: SpawnPointLawyer
entities:
- uid: 46047

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
- type: alert
- type: alert
id: Essence
category: Health #it's like ghostie health
icons:
@ -18,12 +18,15 @@
id: AlertEssenceSpriteView
categories: [ HideSpawnMenu ]
components:
- type: GenericCounterAlert
centerGlyph: false
hideLeadingZeroes: false
- type: Sprite
sprite: /Textures/Interface/Alerts/essence_counter.rsi
layers:
- map: [ "enum.AlertVisualLayers.Base" ]
- map: [ "enum.RevenantVisualLayers.Digit1" ]
- map: [ "enum.RevenantVisualLayers.Digit2" ]
offset: 0.125, 0
- map: [ "enum.RevenantVisualLayers.Digit3" ]
- map: [ "1" ]
offset: 0.25, 0
- map: [ "10" ]
offset: 0.125, 0
- map: [ "100" ]

View File

@ -26,6 +26,7 @@
SilverRing: 2 # DeltaV
RingBox: 2 # DeltaV
CosmicCenser: 1 # DeltaV - Cosmic Cult
SilverSword: 1 # DeltaV - Most stations start them with a kitchen knife. They can have this.
contrabandInventory:
FoodBakedBunHotX: 2
DrinkWineBottleFull: 1

View File

@ -27,6 +27,10 @@
- PaintingRedBlueYellow
- PaintingHelloWorld
- PaintingSpoon # DeltaV Painting, see Resources/Prototypes/_DV/Entities/Structures/Wallmount/painting.yml
- PaintingUnePipe # _DEN edit
- PaintingTheWrathOfGods # _DEN edit
- PaintingHumanCondition # _DEN edit
- PaintingKnowledge # _DEN edit
chance: 1
rarePrototypes:
- PaintingSkeletonBoof

View File

@ -13,7 +13,7 @@
id: SpawnPointGhostRatKing
name: ghost role spawn point
suffix: rat king
parent: MarkerBase
parent: BaseAntagSpawner
components:
- type: GhostRole
name: ghost-role-information-rat-king-name

View File

@ -237,6 +237,7 @@
- type: CosmicCenserTarget # DeltaV - Cosmic Cult
- type: Carriable # DeltaV
- type: EmoteCooldown # DeltaV - implement RMC emote cooldown
- type: GrappleTarget # DeltaV - Allow targets to be grappled
- type: entity
save: false

View File

@ -126,8 +126,8 @@
darkThreshold: 0.4
lightThreshold: 1.2
darkDamage:
groups:
Burn: 1.0
types:
Heat: 1.0
lightDamage:
groups:
Brute: -0.1

View File

@ -37,6 +37,9 @@
- type: Sprite
sprite: Objects/Specific/Chapel/bible.rsi
state: icon
- type: Tool # DeltaV - Lets it be used to bless things.
qualities:
- Blessing
- type: Item
size: Small
sprite: Objects/Specific/Chapel/bible.rsi

View File

@ -42,6 +42,8 @@
- id: MobWhimperlet # DeltaV - added Whimperlet
weight: 0.03
#- id: MobMouseCancer # DeltaV - no
- id: MobCorticalBorer # DeltaV - ported Borer
weight: 0.015 # DeltaV - original probability was 0.001 but that seems WAY too low
# Events always spawn a critter regardless of Probability https://github.com/space-wizards/space-station-14/issues/28480 I added the Rat King to their own event with a player cap.
- type: entity
@ -96,6 +98,8 @@
weight: 0.21
- id: MobWhimperlet # DeltaV - Added Whimperlet and its weight
weight: 0.02
- id: MobCorticalBorer # DeltaV - ported Borer
weight: 0.015 # DeltaV - original probability was 0.001 but that seems WAY too low
- type: entity
id: SnailMigrationLowPop
@ -140,7 +144,9 @@
- id: MobSnail
weight: 0.84
- id: MobSnailSpeed
weight: 0.08
weight: 0.07 # DeltaV - was 0.08
- id: MobSnailMoth
weight: 0.08
weight: 0.07 # DeltaV - was 0.08
#- id: MobSnailInstantDeath # DeltaV - no
- id: MobCorticalBorer # DeltaV - ported Borer
weight: 0.02 # DeltaV - 2% chance on snails

View File

@ -15,6 +15,7 @@
# - Micro - to be revisited later ~Colin
- Pebble
- Shoukou
- Submarine
- Terra
- Tortuga
- TheHive

View File

@ -0,0 +1,83 @@
- type: gameMap
id: Submarine
mapName: 'Submarine'
mapPath: /Maps/submarine.yml
minPlayers: 90
stations:
Submarine:
stationProto: StandardNanotrasenStation
components:
- type: StationNameSetup
mapNameTemplate: '{0} Submarine {1}'
nameGenerator:
!type:NanotrasenNameGenerator
prefixCreator: 'DV'
- type: StationEmergencyShuttle
emergencyShuttlePath: /Maps/_DV/Shuttles/NTES_Propeller.yml
- type: StationCargoShuttle
path: /Maps/_DV/Shuttles/cargo_model_B.yml
- type: StationJobs
availableJobs:
#civilian
Passenger: [ -1, -1 ]
Librarian: [ 1, 1 ]
#command
Captain: [ 1, 1 ]
#engineering
AtmosphericTechnician: [ 3, 4 ]
ChiefEngineer: [ 1, 1 ]
StationEngineer: [ 4, 6 ]
TechnicalAssistant: [ 1, 2 ]
#medical
Chemist: [ 2, 2 ]
ChiefMedicalOfficer: [ 1, 1 ]
MedicalDoctor: [ 4, 5 ]
MedicalIntern: [ 2, 3 ]
Paramedic: [ 2, 3 ]
Psychologist: [ 1, 2 ]
Surgeon: [ 1, 2 ]
#security
Brigmedic: [ 2, 2 ]
Detective: [ 2, 2 ]
HeadOfSecurity: [ 1, 1 ]
Prisoner: [ 2, 4 ]
PrisonGuard: [ 1, 2 ]
SecurityOfficer: [ 8, 10 ]
SecurityCadet: [ 2, 4 ]
Warden: [ 1, 1 ]
#service
Bartender: [ 2, 3 ]
Botanist: [ 2, 4 ]
Boxer: [ 2, 2 ]
Chef: [ 4, 5 ]
Clown: [ 1, 2 ]
HeadOfPersonnel: [ 1, 1 ]
Janitor: [ 2, 3 ]
MartialArtist: [ 1, 2 ]
Mime: [ 1, 1 ]
Musician: [ 2, 2 ]
Reporter: [ 1, 2 ]
ServiceWorker: [ 1, 2 ]
Zookeeper: [ 2, 2 ]
#science
Roboticist: [ 1, 1 ]
Chaplain: [ 1, 1 ]
ForensicMantis: [ 1, 1 ]
ResearchAssistant: [ 1, 2 ]
ResearchDirector: [ 2, 2 ]
Scientist: [ 4, 5 ]
#supply
CargoTechnician: [ 2, 4 ]
CargoAssistant: [ 1, 2 ]
Courier: [ 3, 3 ]
SalvageSpecialist: [ 3, 5 ]
Quartermaster: [ 1, 1 ]
#Justice
ChiefJustice: [ 1, 1 ]
Prosecutor: [ 2, 2 ]
Clerk: [ 1, 1 ]
Lawyer: [ 2, 2 ]
#Synthetics
StationAi: [ 1, 1]
Borg: [ 2, 4 ]
MedicalBorg: [ 2, 2 ]

View File

@ -27,13 +27,13 @@
amount: -1
- !type:ModifyBloodLevel
amount: 2
- !type:EvenHealthChange
damage:
Burn: -4
Brute: -4
- !type:HealthChange
damage:
groups:
Burn: -5
Brute: -4
types:
Caustic: -3
Poison: -3
Bloodloss: -3
@ -108,4 +108,4 @@
probability: 0.4
damage:
groups:
Toxin: 2
Toxin: 2

View File

@ -9,6 +9,7 @@
PsychokineticScreamPower: 1
MassSleepPower: 0.3
NoosphericZapPower: 0.3
FracturedFormPower: 0.3
# PsionicInvisibilityPower: 0.15
MindSwapPower: 0.15
PsionicEruptionPower: 0.1

View File

@ -16,21 +16,23 @@
#CMOCrewMonitorStealObjective: 1 # DeltaV - You can buy this + paramedics all have them
RDHardsuitStealObjective: 1
NukeDiskStealObjective: 0.25 # DeltaV - was 1
PlutoniumCoreStealObjective: 0.5 # DeltaV
MagbootsStealObjective: 1
# CorgiMeatStealObjective: 1 # DeltaV - Disable the horrible murder of Ian as an objective
MantisKnifeStealObjective: 1 # Nyanotrasen - ForensicMantis steal objective, see Resources/Prototypes/Nyanotrasen/Objectives/traitor.yml
ClipboardStealObjective: 1
RdClipboardStealObjective: 1 # DeltaV - Mysta steal Objective see Resources/Prototypes/_DV/Objectives/traitor.yml
CaptainGunStealObjective: 0.5
CaptainJetpackStealObjective: 0.5
HandTeleporterStealObjective: 0.5
#EnergyShotgunStealObjective: 0.5 # DeltaV - replaced by X-01 objective
ClerkNotaryStealObjective: 0.5 # DeltaV
LOLuckyBillStealObjective: 0.5 # DeltaV - LO steal objective, see Resources/Prototypes/_DV/Objectives/traitor.yml
HoPBookIanDossierStealObjective: 1 # DeltaV - HoP steal objective, see Resources/Prototypes/_DV/Objectives/traitor.yml
HoSGunStealObjective: 0.5 # DeltaV
BibleMystagogueStealObjective: 0.5 # DeltaV - Mysta steal objective see Resources/Prototypes/_DV/Objectives/traitor.yml
# Begin DeltaV Additions
ClerkNotaryStealObjective: 0.5
HoPBookIanDossierStealObjective: 1
HoSGunStealObjective: 0.5
BibleMystagogueStealObjective: 0.5
HardsuitRhinoStealObjective: 0.5
PlutoniumCoreStealObjective: 0.5
MantisKnifeStealObjective: 1
RdClipboardStealObjective: 1
# End DeltaV Additions
- type: weightedRandom
id: TraitorObjectiveGroupKill

View File

@ -90,6 +90,11 @@
reagent: Nutriment
min: 0.1
factor: 1
- !type:GenericStatusEffect # Mono change
key: CorticalBorerProtection
type: Add
time: 3 # May be too much, needs testing
refresh: false
plantMetabolism:
- !type:PlantAdjustNutrition
amount: 0.1

View File

@ -166,15 +166,15 @@
# Dragon doesn't require airloss healing, so omnizine is still best for humans.
- !type:ModifyBloodLevel
amount: 3
- !type:HealthChange
# Begin DeltaV additions
- !type:EvenHealthChange # Changed from HealthChange to EvenHealthChange
damage:
groups:
Burn: -5
Brute: -5
Toxin: -4 # Funkychem - was -2
Airloss: -6 # Funkychem - ichor does airloss now too
types:
Bloodloss: -5
Brute: -2 # Was -5 with HealthChange
Burn: -2 # Was -5 with HealthChange
Toxin: -2 # Funkychem - Was -2 with HealthChange
Airloss: -3 # Funkychem - Ichor heals Airloss now.
# -6 Bloodloss with HealthChange has been removed due to -3 Airloss with EvenHealthChange above.
# End DeltaV additions
- !type:ModifyBleedAmount
amount: -1.5
# Just in case you REALLY want to water your plants

View File

@ -194,16 +194,15 @@
Medicine:
effects:
- !type:ModifyBloodLevel
amount: 4
- !type:HealthChange
amount: 6 # DeltaV - Same as Saline, was 4.
- !type:EvenHealthChange # DeltaV - Cryochems heal evenly.
conditions:
- !type:Temperature
# this is a little arbitrary but they gotta be pretty cold
max: 213.0
damage:
# todo scale with temp like SS13
groups:
Airloss: -10
Airloss: -7 # DeltaV - Was -10 with HealthChange.
- type: reagent
id: Doxarubixadone
@ -1278,16 +1277,20 @@
metabolisms:
Medicine:
effects:
- !type:HealthChange
# Begin DeltaV additions
- !type:EvenHealthChange # DeltaV - Cryochems heal evenly.
conditions:
- !type:MobStateCondition
mobstate: Dead # It's called NECROsol, not ALIVEsol
- !type:Temperature
max: 213.0
max: 150.0 # Same as Opporozidone for flavor with chemicals for dead bodies.
damage:
groups:
Brute: -4
Burn: -5
types:
Poison: -2
Brute: -5 # Was -4 with HealthChange.
Burn: -5
Airloss: -5 # Added as a buff.
Toxin: -5 # Was -2 Poison with HealthChange.
Genetic: -5 # Added as a buff.
# End DeltaV additions
- type: reagent
id: Aloxadone
@ -1301,16 +1304,13 @@
metabolisms:
Medicine:
effects:
- !type:HealthChange
- !type:EvenHealthChange # DeltaV - Cryochems heal evenly.
conditions:
- !type:Temperature
max: 213.0
damage:
types:
Cold: -5 # DeltaV - was -4
Heat: -5 # DeltaV - was -4
Shock: -5 # DeltaV - was -4
Caustic: -2 # DeltaV - was -1.5
Burn: -7 # DeltaV - Cryochems heal evenly.
# Previous values with HealthChange were: -4 Heat/Cold/Shock, -1.5 Caustic
- type: reagent
id: Traumoxadone
@ -1329,7 +1329,7 @@
- !type:Temperature
max: 213.0
damage:
Brute: -4
Brute: -7 # DeltaV - Was -4
- type: reagent
id : Mannitol # currently this is just a way to create psicodine
@ -1486,14 +1486,14 @@
metabolisms:
Medicine:
effects:
- !type:HealthChange
- !type:EvenHealthChange # DeltaV - Cryochems heal evenly.
conditions:
- !type:Temperature
max: 213.0
damage:
groups:
Brute: 3
Genetic: 1
types:
Poison: -6
Radiation: -3
# Begin DeltaV additions
Brute: 3
Genetic: 1
Toxin: -7 # Was -6 Poison and -3 Radiation.
# Previous values with HealthChange were: +3 Brute, +1 Genetic, -6 Poison, -3 Radiation
# End DeltaV additions

View File

@ -0,0 +1,39 @@
- type: entity #den port
parent: PaintingBase
id: PaintingUnePipe
name: The Treachery of Images
description: Ceci n'est pas une pipe...
components:
- type: Sprite
sprite: _DEN/Structures/Wallmounts/paintings.rsi
state: painting0
- type: entity #den port
parent: PaintingBase
id: PaintingTheWrathOfGods
name: The Wrath of Gods
description: A symbolic painting of a racing horse juxtaposed above a car.
components:
- type: Sprite
sprite: _DEN/Structures/Wallmounts/paintings.rsi
state: painting1
- type: entity #den port
parent: PaintingBase
id: PaintingHumanCondition
name: The Human Condition
description: A painting of a painting of a beach in front of a door in front of a beach... Really makes you think.
components:
- type: Sprite
sprite: _DEN/Structures/Wallmounts/paintings.rsi
state: painting2
- type: entity #den port
parent: PaintingBase
id: PaintingKnowledge
name: Knowledge
description: A painting of a daytime landscape with a door. Inside the door is a nighttime landscape.
components:
- type: Sprite
sprite: _DEN/Structures/Wallmounts/paintings.rsi
state: painting3

View File

@ -222,3 +222,17 @@
useDelay: 60
- type: InstantAction
event: !type:ShatterLightsActionEvent
- type: entity
id: ActionFracturedForm
name: Swap Form
description: Switch to one of your alternate forms, if you have one. This can also be done by sleeping.
components:
- type: Action
icon:
sprite: _DV/Interface/Actions/actions_psionics_small.rsi
state: fractured_form
useDelay: 60
checkCanInteract: false
- type: InstantAction
event: !type:FracturedFormPowerActionEvent

View File

@ -0,0 +1,6 @@
- type: alert
id: Grappled
clickEvent: !type:EscapeGrappleAlertEvent
icons: [ /Textures/_DV/Interface/Alerts/Grappled/grappled.png ]
name: alerts-grappled-name
description: alerts-grappled-desc

View File

@ -5,13 +5,13 @@
id: LockerFillQuarterMasterDeltaV
table: !type:AllSelector
children:
- id: SpaceCashLuckyBill # LO steal objective, see Resources/Prototypes/_DV/Entities/Objects/Misc/first_bill.yml
- id: BoxPDACargo
- id: QuartermasterIDCard
- id: ClothingShoesBootsWinterLogisticsOfficer
- id: MiningShuttleConsoleCircuitboard
- id: StockTradingCartridge
- id: LogisticsTechFabCircuitboard
- id: ClothingOuterHardsuitRhino # LO suit and steal objective
- id: SalvageExpeditionsComputerCircuitboard
- id: FundingAllocationComputerCircuitboard # To be removed when it's in techvault on all maps
- id: LunchboxCommandFilledRandom

View File

@ -414,3 +414,39 @@
sprite: _DV/Clothing/OuterClothing/Hardsuits/ERTSuits/eliteertjanitor.rsi
- type: ToggleableClothing
clothingPrototype: ClothingHeadHelmetHardsuitERTJanitorElite
#LO Hardsuit
- type: entity
parent: [ClothingOuterHardsuitSalvage, BaseJetpack, BaseCargoContraband]
id: ClothingOuterHardsuitRhino
name: rhino industrial hardsuit
description: A hardsuit adorned with purple fabric and gold metal. Made specifically by Nanotrasen for their Logistics Officers, this hardsuit boasts better protection than most salvage suits and has an internal jetpack.
components:
- type: Sprite
sprite: _DV/Clothing/OuterClothing/Hardsuits/rhinosuit.rsi
- type: Clothing
sprite: _DV/Clothing/OuterClothing/Hardsuits/rhinosuit.rsi
- type: Item
sprite: _DV/Clothing/OuterClothing/Hardsuits/rhinosuit.rsi
- type: Armor
modifiers:
coefficients:
Blunt: 0.7
Slash: 0.5
Piercing: 0.7
Heat: 0.6
Radiation: 0.3
Caustic: 0.7
- type: ClothingSpeedModifier
walkModifier: 0.7
sprintModifier: 0.7
- type: GasTank
outputPressure: 21.3
air:
volume: 5
temperature: 293.15
moles:
- 1.025689525 # oxygen
- 1.025689525 # nitrogen
- type: StealTarget
stealGroup: HardsuitRhino

View File

@ -59,6 +59,13 @@
- type: Temperature
heatDamageThreshold: 315 # 10 lower than base
coldDamageThreshold: 240 # 20 lower than base
- type: LayingDown
- type: Grappler
handDisabling: SingleActive
grapplingPart: grapple-part-jaws
proneOnGrapple: true # Make sure Laika isn't shot by sprays
grappleSound:
collection: VulpkaninGrowls
- type: entity
parent: MobCarp

View File

@ -0,0 +1,64 @@
- type: entity
parent: BaseSword
id: SilverSword
name: silver sword
description: A beautiful sword made almost entirely of silver. It's blade is dull and can't hold an edge, but perhaps it could be something more...
components:
- type: Sprite
sprite: _DV/Objects/Weapons/Melee/silversword.rsi
- type: MeleeWeapon
damage:
types:
Slash: 15
- type: Item
sprite: _DV/Objects/Weapons/Melee/silversword.rsi
- type: Construction
graph: RadiantSword
node: start
- type: Clothing
quickEquip: false
slots:
- back
- suitStorage
- belt
- type: Tag
tags:
- SilverSword
- type: entity
parent: SilverSword
id: EmbeddedSword
name: bluespace embedded sword
description: you didn't think just adding bluespace would make this a workable weapon, did you?
components:
- type: Sprite
sprite: _DV/Objects/Weapons/Melee/silversword.rsi
state: bluespace_embedded
- type: Construction
graph: RadiantSword
node: embeddedsword
- type: entity
parent: SilverSword
id: RadiantSword
name: radiant sword
description: A silver sword infused with the light of a Chaplain's faith. While it remains ineffective against the physical, it is a powerful tool against those from beyond the veil.
components:
- type: Sprite
sprite: _DV/Objects/Weapons/Melee/radiantsword.rsi
- type: MeleeWeapon
damage:
types:
Slash: 15
Holy: 15
soundHit:
path: /Audio/Weapons/bladeslice.ogg
- type: Item
sprite: _DV/Objects/Weapons/Melee/radiantsword.rsi
- type: Construction
graph: RadiantSword
node: radiantsword
- type: RevealRevenantOnCollide
- type: CollisionWake
enabled: false

View File

@ -15,6 +15,7 @@
- id: RoboNeuroticist
- id: MenaceSkeleton
- id: SkiaSpawn
- id: RatKingSpawn
# Replaces upstream meteor events until they're good
- type: entity
@ -159,15 +160,23 @@
minimumPlayers: 15
earliestStart: 25
- type: MidRoundAntagRule
# Rat king
#- type: entity
# parent: BaseMidRoundAntag
# id: RatKingSpawn
# components:
# if you uncomment this update this for antag refactor, below wont work
# - type: MidRoundAntagRule
# spawner: SpawnPointGhostRatKing
# Fugitive
- type: entity
parent: BaseMidRoundAntag
id: RatKingSpawn
components:
- type: StationEvent
weight: 7
minimumPlayers: 20
- type: AntagSpawner
prototype: MobRatKing
- type: AntagSelection
definitions:
- spawnerPrototype: SpawnPointGhostRatKing
min: 1
max: 1
pickPlayer: false
- type: entity
parent: BaseMidRoundAntag
id: Fugitive

View File

@ -39,7 +39,7 @@
parent: [BaseAsakimObjective, BaseSurviveObjective]
id: AsakimTheftObjective
name: ">CONFISCATE"
description: Records indicate that this sector may have valuable artifacts. Confiscate such items for further study.
description: Records indicate that this sector may have valuable items. Confiscate such items for further study.
components:
- type: Objective

Some files were not shown because too many files have changed in this diff Show More