Мозговой Червь (#17)
* - add: Added Cortic Borer. * - fix: Removed unnecessary imports, unused fields, variables, methods. * - fix: Изменён принцип вселения: теперь не создаётся новый энтити с переходом разума, вместо этого хост хранит в себе контейнер для червя, в который последний и погружается * - fix: Убрано использование устаревших методов и полей, исправлена ошибка, из-за которой при вселении в носителя уровень сахара не проверялся * - fix: Изменено тестировочное значение добавления очков химикатов * - fix: Borer can't speak now * - fix: Some bug and shitcode fixes * - fix: Some bug and shitcode fixes * - fix: Added cooldown after releasing the humanoid's body * - fix: fix * - add: Added russian localization * - add: Убрал использование метода _chatManager.ChatMessageToOne в некоторых местах, т.к. popup включает в себя вывод сообщения в чат. * - fix: fix * - fix: fix
55
Content.Client/Borer/BorerOverlay.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.Borer;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.Player;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
|
using Robust.Shared.Enums;
|
||||||
|
|
||||||
|
namespace Content.Client.Borer;
|
||||||
|
|
||||||
|
public sealed class BorerOverlay : Overlay
|
||||||
|
{
|
||||||
|
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
|
||||||
|
[Dependency] private readonly IResourceCache _client = default!;
|
||||||
|
private IPlayerManager _playerManager;
|
||||||
|
private IEntityManager _entManager;
|
||||||
|
private Font _font;
|
||||||
|
private Texture _chemBackground;
|
||||||
|
|
||||||
|
int points;
|
||||||
|
float X, Y;
|
||||||
|
|
||||||
|
public BorerOverlay(IEntityManager entManager, IPlayerManager playerManager, IResourceCache client)
|
||||||
|
{
|
||||||
|
_entManager = entManager;
|
||||||
|
_playerManager = playerManager;
|
||||||
|
_client = client;
|
||||||
|
_font = new VectorFont(_client.GetResource<FontResource>("/Fonts/Boxfont-round/Boxfont Round.ttf"), 30);
|
||||||
|
_chemBackground = _client.GetResource<TextureResource>("/Textures/Interface/Borer/chem_bg.png");
|
||||||
|
}
|
||||||
|
protected override void Draw(in OverlayDrawArgs args)
|
||||||
|
{
|
||||||
|
var localPlayer = _playerManager.LocalEntity;
|
||||||
|
if (_entManager.TryGetComponent(localPlayer, out BorerComponent? borComp))
|
||||||
|
points = borComp.Points;
|
||||||
|
else if (_entManager.TryGetComponent(localPlayer, out InfestedBorerComponent? infestedComp))
|
||||||
|
{
|
||||||
|
if (infestedComp.ControllingBrain)
|
||||||
|
return;
|
||||||
|
points = infestedComp.Points;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.ViewportControl != null)
|
||||||
|
{
|
||||||
|
X = (args.ViewportControl.Window!.Size.X / 2f) - 32;
|
||||||
|
Y = args.ViewportControl.Window!.Size.Y - 130f;
|
||||||
|
}
|
||||||
|
|
||||||
|
var screenHandle = args.ScreenHandle;
|
||||||
|
|
||||||
|
screenHandle.DrawTextureRect(_chemBackground, new UIBox2(new Vector2(X,Y), new Vector2(X+128f,Y+128f)));
|
||||||
|
screenHandle.DrawString(_font, new Vector2(X+18, Y+42), points.ToString(), new Color(30, 200, 30));
|
||||||
|
}
|
||||||
|
}
|
||||||
60
Content.Client/Borer/BorerScannerUIController.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using Content.Client.UserInterface.Systems.Gameplay;
|
||||||
|
using Content.Shared.Borer;
|
||||||
|
using Robust.Client.Player;
|
||||||
|
using Robust.Client.UserInterface.Controllers;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Borer;
|
||||||
|
|
||||||
|
|
||||||
|
public sealed class BorerScannerUIController : UIController
|
||||||
|
{
|
||||||
|
[Dependency] private readonly GameplayStateLoadController _gameplayStateLoad = default!;
|
||||||
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
|
|
||||||
|
private ScannerWindow? _window;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_gameplayStateLoad.OnScreenLoad += LoadGui;
|
||||||
|
_gameplayStateLoad.OnScreenUnload += UnloadGui;
|
||||||
|
|
||||||
|
SubscribeNetworkEvent<BorerScanDoAfterEvent>(OpenWindow);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadGui()
|
||||||
|
{
|
||||||
|
DebugTools.Assert(_window == null);
|
||||||
|
_window = UIManager.CreateWindow<ScannerWindow>();
|
||||||
|
LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnloadGui()
|
||||||
|
{
|
||||||
|
if (_window != null)
|
||||||
|
{
|
||||||
|
_window.Dispose();
|
||||||
|
_window = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void OpenWindow(BorerScanDoAfterEvent msg, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
var ent = _playerManager.LocalEntity;
|
||||||
|
if (_window == null || _window.IsOpen || ent != args.SenderSession.AttachedEntity)
|
||||||
|
return;
|
||||||
|
_window.SolutionContainer.DisposeAllChildren();
|
||||||
|
foreach (var reagent in msg.Solution)
|
||||||
|
{
|
||||||
|
var reagLabel = new Label();
|
||||||
|
reagLabel.Text = reagent.Key + " - " + reagent.Value + "u";
|
||||||
|
_window.SolutionContainer.Children.Add(reagLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
_window.Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
31
Content.Client/Borer/ClientBorerSystem.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Content.Shared.Borer;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.Player;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
|
|
||||||
|
namespace Content.Client.Borer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This handles...
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ClientBorerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IResourceCache _client = default!;
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||||
|
[Dependency] private readonly IPlayerManager _playerMgr = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeNetworkEvent<BorerOverlayResponceEvent>(OnOverlayResponce);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOverlayResponce(BorerOverlayResponceEvent ev)
|
||||||
|
{
|
||||||
|
if(!_overlayManager.HasOverlay<BorerOverlay>())
|
||||||
|
_overlayManager.AddOverlay(new BorerOverlay(
|
||||||
|
_entManager,
|
||||||
|
_playerMgr,
|
||||||
|
_client));
|
||||||
|
}
|
||||||
|
}
|
||||||
110
Content.Client/Borer/ReagentUIController.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using Content.Client.Actions;
|
||||||
|
using Content.Client.Gameplay;
|
||||||
|
using Content.Client.UserInterface.Systems.Actions;
|
||||||
|
using Content.Client.UserInterface.Systems.Gameplay;
|
||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Borer;
|
||||||
|
using Content.Shared.Input;
|
||||||
|
using Robust.Client.Player;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controllers;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.Input.Binding;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Borer;
|
||||||
|
|
||||||
|
|
||||||
|
public sealed class ReagentUIController : UIController, IOnSystemChanged<ActionsSystem>, IOnStateEntered<GameplayState>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly GameplayStateLoadController _gameplayStateLoad = default!;
|
||||||
|
[UISystemDependency] private readonly SharedBorerSystem _borerSystem = default!;
|
||||||
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
|
|
||||||
|
private ReagentWindow? _window;
|
||||||
|
|
||||||
|
private bool _reagentsLoaded = false;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
|
||||||
|
_gameplayStateLoad.OnScreenLoad += LoadGui;
|
||||||
|
_gameplayStateLoad.OnScreenUnload += UnloadGui;
|
||||||
|
|
||||||
|
SubscribeLocalEvent<BorerInjectWindowOpenEvent>(ev =>
|
||||||
|
{
|
||||||
|
OpenWindow();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadGui()
|
||||||
|
{
|
||||||
|
DebugTools.Assert(_window == null);
|
||||||
|
_window = UIManager.CreateWindow<ReagentWindow>();
|
||||||
|
LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnloadGui()
|
||||||
|
{
|
||||||
|
if (_window != null)
|
||||||
|
{
|
||||||
|
_window.Dispose();
|
||||||
|
_window = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInjectReagent(string protoId, int cost)
|
||||||
|
{
|
||||||
|
_borerSystem.RaiseInjectEvent(protoId, cost);
|
||||||
|
}
|
||||||
|
private void OpenWindow()
|
||||||
|
{
|
||||||
|
var ent = _playerManager.LocalEntity;
|
||||||
|
if (_window == null || _window.IsOpen || !ent.HasValue)
|
||||||
|
return;
|
||||||
|
if (!_reagentsLoaded)
|
||||||
|
{
|
||||||
|
foreach (var reagent in _borerSystem.GetReagents(ent.Value))
|
||||||
|
{
|
||||||
|
var button = new Button();
|
||||||
|
button.Text = Loc.GetString("borer-ui-secrete-inject-label",
|
||||||
|
("reagent",Loc.GetString("reagent-name-"+
|
||||||
|
reagent.Key.ToLower().Replace("spacedrugs", "space-drugs"))),
|
||||||
|
("cost", reagent.Value));
|
||||||
|
button.OnPressed += _ => OnInjectReagent(reagent.Key, reagent.Value);
|
||||||
|
_window.MainContainer.AddChild(button);
|
||||||
|
}
|
||||||
|
_reagentsLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_window.Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSystemLoaded(ActionsSystem system)
|
||||||
|
{
|
||||||
|
system.LinkActions += OnComponentLinked;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSystemUnloaded(ActionsSystem system)
|
||||||
|
{
|
||||||
|
system.LinkActions -= OnComponentLinked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnComponentLinked(ActionsComponent component)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnStateEntered(GameplayState state)
|
||||||
|
{
|
||||||
|
CommandBinds.Builder
|
||||||
|
.Bind(ContentKeyFunctions.OpenActionsMenu, InputCmdHandler.FromDelegate(_ => OpenWindow()))
|
||||||
|
.Register<ReagentWindow>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnStateExited(GameplayState state)
|
||||||
|
{
|
||||||
|
CommandBinds.Unregister<ReagentWindow>();
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Content.Client/Borer/ReagentWindow.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.Borer;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class ReagentWindow : DefaultWindow
|
||||||
|
{
|
||||||
|
public ReagentWindow()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
13
Content.Client/Borer/ReagentWindow.xaml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<windows:ReagentWindow
|
||||||
|
xmlns="https://spacestation14.io"
|
||||||
|
xmlns:windows="clr-namespace:Content.Client.Borer"
|
||||||
|
Name="AvailableReagents"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Title="Reagents"
|
||||||
|
VerticalExpand="True"
|
||||||
|
Resizable="True"
|
||||||
|
MinHeight="300"
|
||||||
|
MinWidth="300">
|
||||||
|
<BoxContainer Name="MainContainer" Orientation="Vertical" SeparationOverride="4" MinWidth="150" Access="Public">
|
||||||
|
</BoxContainer>
|
||||||
|
</windows:ReagentWindow>
|
||||||
16
Content.Client/Borer/ScannerWindow.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.Borer;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class ScannerWindow : DefaultWindow
|
||||||
|
{
|
||||||
|
public ScannerWindow()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
17
Content.Client/Borer/ScannerWindow.xaml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<windows:ScannerWindow
|
||||||
|
xmlns="https://spacestation14.io"
|
||||||
|
xmlns:windows="clr-namespace:Content.Client.Borer"
|
||||||
|
Name="AvailableReagents"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Title="{Loc 'borer-ui-scan-title'}"
|
||||||
|
VerticalExpand="True"
|
||||||
|
Resizable="True"
|
||||||
|
MinHeight="300"
|
||||||
|
MinWidth="300">
|
||||||
|
<BoxContainer Name="MainContainer" Orientation="Vertical" SeparationOverride="4" MinWidth="150">
|
||||||
|
<Label FontColorOverride="#55FF55" HorizontalAlignment="Center" Text="{Loc 'borer-ui-scan-label'}"/>
|
||||||
|
<BoxContainer Name="SolutionContainer" Orientation="Vertical" SeparationOverride="4" MinWidth="150" Access="Public">
|
||||||
|
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</windows:ScannerWindow>
|
||||||
28
Content.Server/Borer/ServerBorerHostSystem.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Content.Shared.Borer;
|
||||||
|
using Content.Shared.Mobs;
|
||||||
|
using Robust.Server.Containers;
|
||||||
|
|
||||||
|
namespace Content.Server.Borer;
|
||||||
|
|
||||||
|
|
||||||
|
public sealed class ServerBorerHostSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private ServerBorerSystem _borerSystem = default!;
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<BorerHostComponent, MobStateChangedEvent>(OnDamageChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDamageChanged(EntityUid uid, BorerHostComponent component, MobStateChangedEvent args)
|
||||||
|
{
|
||||||
|
if (args.NewMobState == MobState.Critical)
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(uid, new BorerBrainReleaseEvent(), true);
|
||||||
|
}
|
||||||
|
else if (args.NewMobState == MobState.Dead)
|
||||||
|
{
|
||||||
|
_borerSystem.GetOut(component.BorerContainer.ContainedEntities[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
541
Content.Server/Borer/ServerBorerSystem.cs
Normal file
@@ -0,0 +1,541 @@
|
|||||||
|
using Content.Server.Administration;
|
||||||
|
using Content.Server.Body.Components;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
|
using Content.Server.Chat.Managers;
|
||||||
|
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||||
|
using Content.Server.Medical;
|
||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Server.Stunnable;
|
||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Borer;
|
||||||
|
using Content.Shared.Changeling;
|
||||||
|
using Content.Shared.Chat;
|
||||||
|
using Content.Shared.Chemistry;
|
||||||
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Chemistry.Reagent;
|
||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.FixedPoint;
|
||||||
|
using Content.Shared.Humanoid;
|
||||||
|
using Content.Shared.Mind;
|
||||||
|
using Content.Shared.Mobs;
|
||||||
|
using Content.Shared.Mobs.Components;
|
||||||
|
using Content.Shared.Movement.Systems;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Silicons.Borgs.Components;
|
||||||
|
using Robust.Server.Containers;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using Robust.Shared.Physics.Systems;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Server.Borer;
|
||||||
|
|
||||||
|
public sealed class ServerBorerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedActionsSystem _action = default!;
|
||||||
|
|
||||||
|
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
|
||||||
|
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||||
|
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
|
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
|
||||||
|
[Dependency] private readonly QuickDialogSystem _quickDialog = default!;
|
||||||
|
[Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
|
||||||
|
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||||
|
|
||||||
|
[Dependency] private readonly StunSystem _stuns = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly VomitSystem _vomitSystem = default!;
|
||||||
|
[Dependency] private readonly ContainerSystem _container = default!;
|
||||||
|
|
||||||
|
[Dependency] private readonly SharedBorerSystem _sharedBorerSystem = default!;
|
||||||
|
|
||||||
|
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||||
|
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<BorerComponent, BorerInfestActionEvent>(OnInfest);
|
||||||
|
SubscribeLocalEvent<BorerComponent, BorerInfestDoAfterEvent>(OnInfestAfter);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<InfestedBorerComponent, BorerOutActionEvent>(OnGetOut);
|
||||||
|
SubscribeLocalEvent<InfestedBorerComponent, BorerBrainSpeechActionEvent>(OnTelepathicSpeech);
|
||||||
|
|
||||||
|
SubscribeNetworkEvent<BorerInjectActionEvent>(OnInjectChemicals);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<InfestedBorerComponent, BorerScanInstantActionEvent>(OnBorerScan);
|
||||||
|
SubscribeLocalEvent<BorerComponent, BorerStunActionEvent>(OnStunEvent);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<InfestedBorerComponent, BorerBrainTakeEvent>(OnTakeControl);
|
||||||
|
SubscribeLocalEvent<InfestedBorerComponent, BorerBrainTakeAfterEvent>(OnTakeControlAfter);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<BorerHostComponent, BorerBrainReleaseEvent>(OnReleaseControl);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<BorerHostComponent, BorerReproduceEvent>(OnReproduce);
|
||||||
|
SubscribeLocalEvent<BorerHostComponent, BorerReproduceAfterEvent>(OnReproduceAfter);
|
||||||
|
SubscribeLocalEvent<InfestedBorerComponent, BorerBrainResistAfterEvent>(OnResistAfterControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnReproduceAfter(EntityUid uid, BorerHostComponent component, BorerReproduceAfterEvent args)
|
||||||
|
{
|
||||||
|
if (args.Cancelled || !TryComp(component.BorerContainer.ContainedEntities[0], out InfestedBorerComponent? borerComp)
|
||||||
|
|| !WithrawPoints(component.BorerContainer.ContainedEntities[0], borerComp.ReproduceCost)
|
||||||
|
|| !TryComp(uid, out TransformComponent? targetTransform))
|
||||||
|
return;
|
||||||
|
args.Handled = true;
|
||||||
|
_vomitSystem.Vomit(uid, -30, -30);
|
||||||
|
_entityManager.SpawnEntity("MobSimpleBorer", targetTransform.Coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnReproduce(EntityUid uid, BorerHostComponent component, BorerReproduceEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp(component.BorerContainer.ContainedEntities[0], out InfestedBorerComponent? borerComp))
|
||||||
|
return;
|
||||||
|
if (GetPoints(component.BorerContainer.ContainedEntities[0]) < borerComp.ReproduceCost)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-lowchem"),
|
||||||
|
uid,
|
||||||
|
uid, PopupType.LargeCaution);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
|
||||||
|
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager,
|
||||||
|
uid,
|
||||||
|
TimeSpan.FromSeconds(3),
|
||||||
|
new BorerReproduceAfterEvent(), uid)
|
||||||
|
{
|
||||||
|
Hidden = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReleaseControl(EntityUid borerUid)
|
||||||
|
{
|
||||||
|
if (!TryComp(borerUid, out ActionsComponent? wormActComp) ||
|
||||||
|
!TryComp(borerUid, out InfestedBorerComponent? borComp) ||
|
||||||
|
!borComp.Host.HasValue ||
|
||||||
|
!borComp.ControllingBrain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
TryComp(borComp.Host.Value, out ActionsComponent? bodyActComp);
|
||||||
|
var wormHasMind = _mindSystem.TryGetMind(borerUid, out var hostMindId, out var hostMind);
|
||||||
|
var bodyHasMind = _mindSystem.TryGetMind(borComp.Host.Value, out var mindId, out var mind);
|
||||||
|
if (!bodyHasMind && !wormHasMind)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (wormHasMind)
|
||||||
|
_mindSystem.TransferTo(hostMindId, borComp.Host.Value, mind: hostMind);
|
||||||
|
if (bodyHasMind)
|
||||||
|
_mindSystem.TransferTo(mindId, borerUid, mind: mind);
|
||||||
|
|
||||||
|
|
||||||
|
_sharedBorerSystem.AddInfestedAbilities(borerUid, borComp);
|
||||||
|
|
||||||
|
_action.RemoveAction(borerUid, borComp.ActionBorerBrainResistEntity, wormActComp);
|
||||||
|
|
||||||
|
_action.RemoveAction(borComp.Host.Value, borComp.ActionBorerBrainReleaseEntity, bodyActComp);
|
||||||
|
|
||||||
|
_action.RemoveAction(borComp.Host.Value, borComp.ActionBorerReproduceEntity, bodyActComp);
|
||||||
|
|
||||||
|
borComp.ControllingBrain = false;
|
||||||
|
Dirty(borerUid, borComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnReleaseControl(EntityUid uid, BorerHostComponent component, BorerBrainReleaseEvent args)
|
||||||
|
{
|
||||||
|
args.Handled = true;
|
||||||
|
ReleaseControl(component.BorerContainer.ContainedEntities[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnResistAfterControl(EntityUid uid, InfestedBorerComponent component, BorerBrainResistAfterEvent args)
|
||||||
|
{
|
||||||
|
if (args.Cancelled)
|
||||||
|
return;
|
||||||
|
ReleaseControl(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTakeControl(EntityUid uid, InfestedBorerComponent component, BorerBrainTakeEvent args)
|
||||||
|
{
|
||||||
|
if (GetPoints(uid) < component.AssumeControlCost)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-lowchem"),
|
||||||
|
uid,
|
||||||
|
uid, PopupType.LargeCaution);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetSugarQuantityInHost(uid) > 0)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-toomuchsugar"), uid,
|
||||||
|
uid, PopupType.LargeCaution);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryComp(component.Host, out MobStateComponent? state) &&
|
||||||
|
state.CurrentState == MobState.Critical)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-braintake-critical"), uid,
|
||||||
|
uid, PopupType.LargeCaution);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager,
|
||||||
|
uid,
|
||||||
|
TimeSpan.FromSeconds(30),
|
||||||
|
new BorerBrainTakeAfterEvent(), uid)
|
||||||
|
{
|
||||||
|
Hidden = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTakeControlAfter(EntityUid uid, InfestedBorerComponent component, BorerBrainTakeAfterEvent args)
|
||||||
|
{
|
||||||
|
if (!TryComp(uid, out ActionsComponent? comp) ||
|
||||||
|
!TryComp(component.Host, out ActionsComponent? hostComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
else if (TryComp(component.Host, out MobStateComponent? state) &&
|
||||||
|
state.CurrentState == MobState.Critical)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-braintake-critical"), uid,
|
||||||
|
uid, PopupType.LargeCaution);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (GetSugarQuantityInHost(uid) > 0)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-toomuchsugar"), uid,
|
||||||
|
uid, PopupType.LargeCaution);
|
||||||
|
}
|
||||||
|
else if (args.Cancelled || !WithrawPoints(uid, component.AssumeControlCost))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var borHasMind = _mindSystem.TryGetMind(uid, out var mindId, out var mind);
|
||||||
|
var hostHasMind = _mindSystem.TryGetMind(component.Host.Value, out var hostMindId, out var hostMind);
|
||||||
|
|
||||||
|
if (!borHasMind && !hostHasMind)
|
||||||
|
return;
|
||||||
|
if (borHasMind)
|
||||||
|
{
|
||||||
|
_mindSystem.TransferTo(mindId, component.Host, mind: mind);
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-braintake-success"), component.Host.Value,
|
||||||
|
component.Host.Value, PopupType.Large);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostHasMind)
|
||||||
|
{
|
||||||
|
_mindSystem.TransferTo(hostMindId, uid, mind: hostMind);
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-braintake-alert"), uid, uid, PopupType.LargeCaution);
|
||||||
|
if (EntityManager.TryGetComponent(uid, out ActorComponent? actor))
|
||||||
|
{
|
||||||
|
_chatManager.ChatMessageToOne(ChatChannel.Local,
|
||||||
|
Loc.GetString("borer-message-braintake-alert"),
|
||||||
|
Loc.GetString("borer-message-braintake-alert"),
|
||||||
|
EntityUid.Invalid, false, actor.PlayerSession.Channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_action.RemoveAction(uid, component.ActionBorerOutEntity, comp);
|
||||||
|
_action.RemoveAction(uid, component.ActionBorerScanEntity, comp);
|
||||||
|
_action.RemoveAction(uid, component.ActionBorerBrainTakeEntity, comp);
|
||||||
|
_action.RemoveAction(uid, component.ActionBorerInjectWindowOpenEntity, comp);
|
||||||
|
_action.RemoveAction(uid, component.ActionBorerReproduceEntity, comp);
|
||||||
|
|
||||||
|
_action.AddAction(uid, ref component.ActionBorerBrainResistEntity,
|
||||||
|
component.ActionBorerBrainResist, component: comp);
|
||||||
|
|
||||||
|
_action.AddAction(component.Host.Value, ref component.ActionBorerBrainReleaseEntity,
|
||||||
|
component.ActionBorerBrainRelease, component: hostComp);
|
||||||
|
_action.AddAction(component.Host.Value, ref component.ActionBorerReproduceEntity,
|
||||||
|
component.ActionBorerReproduce, component: hostComp);
|
||||||
|
|
||||||
|
if(component.ActionBorerReproduceEntity.HasValue)
|
||||||
|
_metaData.SetEntityName(component.ActionBorerReproduceEntity.Value,
|
||||||
|
$"{Loc.GetString("borer-abilities-reproduce-name")} ([color=red]{component.ReproduceCost}c[/color])");
|
||||||
|
|
||||||
|
component.ControllingBrain = true;
|
||||||
|
Dirty(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStunEvent(EntityUid uid, BorerComponent component, BorerStunActionEvent args)
|
||||||
|
{
|
||||||
|
_stuns.TryParalyze(args.Target, TimeSpan.FromSeconds(5.7f), true);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInfest(EntityUid uid, BorerComponent component, BorerInfestActionEvent args)
|
||||||
|
{
|
||||||
|
if (!HasComp<HumanoidAppearanceComponent>(args.Target)
|
||||||
|
|| HasComp<ChangelingComponent>(args.Target)
|
||||||
|
|| HasComp<BorgChassisComponent>(args.Target))
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-infest-failed"), uid, uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryComp(args.Target, out MobStateComponent? state) &&
|
||||||
|
state.CurrentState == MobState.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (HasComp<BorerHostComponent>(args.Target))
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-infest-occupied"), uid, uid);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetSugarQuantityInEntity(args.Target) > 10)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-infest-sugar"), uid, uid, PopupType.LargeCaution);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StartInfest(uid, args.Target, component);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInfestAfter(EntityUid uid, BorerComponent component, BorerInfestDoAfterEvent args)
|
||||||
|
{
|
||||||
|
if (args.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!HasComp<HumanoidAppearanceComponent>(args.Target))
|
||||||
|
return;
|
||||||
|
if (TryComp(args.Target, out MobStateComponent? state) &&
|
||||||
|
state.CurrentState == MobState.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (HasComp<BorerHostComponent>(args.Target))
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-infest-occupied"), uid, uid);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetSugarQuantityInHost(uid) > 10)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-infest-sugar"), uid, uid, PopupType.LargeCaution);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hostComp = AddComp<BorerHostComponent>(args.Target.Value);
|
||||||
|
hostComp.BorerContainer = _container.EnsureContainer<Container>(args.Target.Value, "borerContainer");
|
||||||
|
_container.Insert(uid, hostComp.BorerContainer);
|
||||||
|
|
||||||
|
var infestedComponent = AddComp<InfestedBorerComponent>(uid);
|
||||||
|
infestedComponent.Host = args.Target;
|
||||||
|
infestedComponent.Points = component.Points;
|
||||||
|
Dirty(uid, infestedComponent);
|
||||||
|
|
||||||
|
RemComp<BorerComponent>(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartInfest(EntityUid user, EntityUid target, BorerComponent comp)
|
||||||
|
{
|
||||||
|
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager,
|
||||||
|
user,
|
||||||
|
TimeSpan.FromSeconds(5),
|
||||||
|
new BorerInfestDoAfterEvent(), user, target)
|
||||||
|
{
|
||||||
|
BreakOnTargetMove = true,
|
||||||
|
BreakOnUserMove = true,
|
||||||
|
Hidden = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBorerScan(EntityUid uid, InfestedBorerComponent component, BorerScanInstantActionEvent args)
|
||||||
|
{
|
||||||
|
if (!component.Host.HasValue)
|
||||||
|
return;
|
||||||
|
Dictionary<string, FixedPoint2> solution = new();
|
||||||
|
if (EntityManager.TryGetComponent(component.Host.Value,
|
||||||
|
out BloodstreamComponent? bloodContainer))
|
||||||
|
{
|
||||||
|
if(_solutionContainerSystem.TryGetSolution(component.Host.Value, bloodContainer.ChemicalSolutionName,
|
||||||
|
out var sol))
|
||||||
|
{
|
||||||
|
foreach (var reagent in sol.Value.Comp.Solution)
|
||||||
|
{
|
||||||
|
solution.Add(reagent.Reagent.ToString(), reagent.Quantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RaiseNetworkEvent(new BorerScanDoAfterEvent(solution), uid);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var infestedQuery = EntityQueryEnumerator<InfestedBorerComponent>();
|
||||||
|
while (infestedQuery.MoveNext(out var uid, out var comp) && comp.Host is not null)
|
||||||
|
{
|
||||||
|
if (comp.PointUpdateNext == TimeSpan.Zero)
|
||||||
|
{
|
||||||
|
comp.PointUpdateNext = _timing.CurTime + comp.PointUpdateRate;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_timing.CurTime < comp.PointUpdateNext)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (GetSugarQuantityInHost(uid) > 30)
|
||||||
|
{
|
||||||
|
GetOut(uid);
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-sugarleave"), uid, uid, PopupType.LargeCaution);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
comp.PointUpdateNext += comp.PointUpdateRate;
|
||||||
|
AddPoints(uid, comp.PointUpdateValue);
|
||||||
|
Dirty(uid, comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTelepathicSpeech(EntityUid uid, InfestedBorerComponent component, BorerBrainSpeechActionEvent args)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(uid, out ActorComponent? actor))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_quickDialog.OpenDialog(actor.PlayerSession, Loc.GetString("borer-ui-converse-title"),
|
||||||
|
Loc.GetString("borer-ui-converse-message"), (string message) =>
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(message, uid, uid, PopupType.Medium);
|
||||||
|
|
||||||
|
if (EntityManager.TryGetComponent(component.Host, out ActorComponent? hostActor))
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(message, component.Host.Value, component.Host.Value,
|
||||||
|
PopupType.Medium);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInjectChemicals(BorerInjectActionEvent injectEvent, EntitySessionEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
var borerEn = eventArgs.SenderSession.AttachedEntity;
|
||||||
|
if (EntityManager.TryGetComponent(borerEn,
|
||||||
|
out InfestedBorerComponent? infestedComponent) &&
|
||||||
|
infestedComponent.Host.HasValue)
|
||||||
|
{
|
||||||
|
if (!WithrawPoints(borerEn.Value, injectEvent.Cost))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var solution = new Solution();
|
||||||
|
solution.AddReagent(injectEvent.ProtoId, 10);
|
||||||
|
_bloodstreamSystem.TryAddToChemicals(infestedComponent.Host.Value, solution);
|
||||||
|
_reactiveSystem.DoEntityReaction(infestedComponent.Host.Value, solution, ReactionMethod.Injection);
|
||||||
|
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-injected", ("reagent", Loc.GetString("reagent-name-"+
|
||||||
|
injectEvent.ProtoId.ToLower().Replace("spacedrugs", "space-drugs")))),
|
||||||
|
borerEn.Value, borerEn.Value, PopupType.Medium);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddPoints(EntityUid borerUid, int value)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(borerUid,
|
||||||
|
out InfestedBorerComponent? infestedComponent))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
infestedComponent.Points += value;
|
||||||
|
Dirty(borerUid, infestedComponent);
|
||||||
|
RaiseNetworkEvent(new BorerPointsUpdateEvent());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetPoints(EntityUid borerUid)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(borerUid,
|
||||||
|
out InfestedBorerComponent? infestedComponent))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return infestedComponent.Points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool WithrawPoints(EntityUid borerUid, int value)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(borerUid,
|
||||||
|
out InfestedBorerComponent? infestedComponent) || infestedComponent.Points < value)
|
||||||
|
{
|
||||||
|
_popup.PopupEntity(Loc.GetString("borer-popup-lowchem"),
|
||||||
|
borerUid,
|
||||||
|
borerUid, PopupType.LargeCaution);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
infestedComponent.Points -= value;
|
||||||
|
Dirty(borerUid, infestedComponent);
|
||||||
|
RaiseNetworkEvent(new BorerPointsUpdateEvent());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetOut(EntityUid uid, InfestedBorerComponent component, BorerOutActionEvent args)
|
||||||
|
{
|
||||||
|
GetOut(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetOut(EntityUid uid)
|
||||||
|
{
|
||||||
|
if (!TryComp(uid, out InfestedBorerComponent? component) ||
|
||||||
|
!TryComp(component.Host, out BorerHostComponent? hostComponent))
|
||||||
|
return;
|
||||||
|
ReleaseControl(uid);
|
||||||
|
|
||||||
|
_vomitSystem.Vomit(component.Host.Value, -20, -20);
|
||||||
|
_container.Remove(uid, hostComponent.BorerContainer);
|
||||||
|
RemComp<BorerHostComponent>(component.Host.Value);
|
||||||
|
|
||||||
|
var borerComponent = AddComp<BorerComponent>(uid);
|
||||||
|
borerComponent.Points = component.Points;
|
||||||
|
Dirty(uid, borerComponent);
|
||||||
|
|
||||||
|
RemComp<InfestedBorerComponent>(uid);
|
||||||
|
_action.SetCooldown(borerComponent.ActionStunEntity, _timing.CurTime, _timing.CurTime+TimeSpan.FromSeconds(20));
|
||||||
|
_action.SetCooldown(borerComponent.ActionInfestEntity, _timing.CurTime, _timing.CurTime+TimeSpan.FromSeconds(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetSugarQuantityInHost(EntityUid borerUid)
|
||||||
|
{
|
||||||
|
var sugarQuantity = 0;
|
||||||
|
if (EntityManager.TryGetComponent(borerUid,
|
||||||
|
out InfestedBorerComponent? component) &&
|
||||||
|
component.Host.HasValue)
|
||||||
|
{
|
||||||
|
sugarQuantity = GetSugarQuantityInEntity(component.Host.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sugarQuantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetSugarQuantityInEntity(EntityUid uid)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent(uid,
|
||||||
|
out BloodstreamComponent? bloodContainer))
|
||||||
|
{
|
||||||
|
if(_solutionContainerSystem.TryGetSolution(uid, bloodContainer.ChemicalSolutionName,
|
||||||
|
out var sol))
|
||||||
|
{
|
||||||
|
foreach (var reagent in sol.Value.Comp.Solution)
|
||||||
|
{
|
||||||
|
if (reagent.Reagent.ToString() == "Sugar")
|
||||||
|
{
|
||||||
|
return reagent.Quantity.Int();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
Content.Shared/Borer/Components/BorerComponent.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Borer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is used for...
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[AutoGenerateComponentState]
|
||||||
|
public sealed partial class BorerComponent : Component
|
||||||
|
{
|
||||||
|
public string ActionInfest = "ActionInfest";
|
||||||
|
|
||||||
|
public EntityUid? ActionInfestEntity;
|
||||||
|
|
||||||
|
public string ActionStun = "ActionBorerStunVictim";
|
||||||
|
|
||||||
|
public EntityUid? ActionStunEntity;
|
||||||
|
|
||||||
|
[AutoNetworkedField]
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public int Points = 0;
|
||||||
|
}
|
||||||
14
Content.Shared/Borer/Components/BorerHostComponent.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Borer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is used for...
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class BorerHostComponent : Component
|
||||||
|
{
|
||||||
|
//public EntityUid Borer;
|
||||||
|
public Container BorerContainer;
|
||||||
|
}
|
||||||
79
Content.Shared/Borer/Components/InfestedBorerComponent.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Borer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is used for...
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[AutoGenerateComponentState]
|
||||||
|
public sealed partial class InfestedBorerComponent : Component
|
||||||
|
{
|
||||||
|
[DataField("reproduceCost")]
|
||||||
|
public int ReproduceCost = 100;
|
||||||
|
|
||||||
|
[DataField("assumeControlCost")]
|
||||||
|
public int AssumeControlCost = 250;
|
||||||
|
|
||||||
|
[AutoNetworkedField]
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public bool ControllingBrain = false;
|
||||||
|
|
||||||
|
public TimeSpan PointUpdateNext = TimeSpan.Zero;
|
||||||
|
|
||||||
|
public TimeSpan PointUpdateRate = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
|
public readonly int PointUpdateValue = 1;
|
||||||
|
|
||||||
|
public string ActionBorerOut = "ActionBorerOut";
|
||||||
|
|
||||||
|
public EntityUid? ActionBorerOutEntity;
|
||||||
|
|
||||||
|
public string ActionBorerBrainSpeech = "ActionBorerBrainSpeech";
|
||||||
|
|
||||||
|
public EntityUid? ActionBorerBrainSpeechEntity;
|
||||||
|
|
||||||
|
public string ActionBorerInjectWindowOpen = "ActionBorerInjectWindowOpen";
|
||||||
|
|
||||||
|
public EntityUid? ActionBorerInjectWindowOpenEntity;
|
||||||
|
|
||||||
|
public string ActionBorerScan = "ActionBorerScan";
|
||||||
|
|
||||||
|
public EntityUid? ActionBorerScanEntity;
|
||||||
|
|
||||||
|
public string ActionBorerBrainTake = "ActionBorerBrainTake";
|
||||||
|
|
||||||
|
public EntityUid? ActionBorerBrainTakeEntity;
|
||||||
|
|
||||||
|
public string ActionBorerBrainRelease = "ActionBorerBrainRelease";
|
||||||
|
|
||||||
|
public EntityUid? ActionBorerBrainReleaseEntity;
|
||||||
|
|
||||||
|
public string ActionBorerBrainResist = "ActionBorerBrainResist";
|
||||||
|
|
||||||
|
public EntityUid? ActionBorerBrainResistEntity;
|
||||||
|
|
||||||
|
public string ActionBorerReproduce = "ActionBorerReproduce";
|
||||||
|
|
||||||
|
public EntityUid? ActionBorerReproduceEntity;
|
||||||
|
|
||||||
|
[AutoNetworkedField]
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public EntityUid? Host;
|
||||||
|
|
||||||
|
[AutoNetworkedField]
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public int Points = 0;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
|
public readonly Dictionary<string, int> AvailableReagents = new()
|
||||||
|
{
|
||||||
|
{ "Epinephrine", 30 },
|
||||||
|
{ "Bicaridine", 30 },
|
||||||
|
{ "Kelotane", 30 },
|
||||||
|
{ "Dylovene", 30 },
|
||||||
|
{ "Dexalin", 30 },
|
||||||
|
{ "SpaceDrugs", 75 },
|
||||||
|
{ "Leporazine", 75 }
|
||||||
|
};
|
||||||
|
}
|
||||||
10
Content.Shared/Borer/Events/BorerBrainResistAfterEvent.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Borer;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class BorerBrainResistAfterEvent : SimpleDoAfterEvent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
Content.Shared/Borer/Events/BorerBrainTakeAfterEvent.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Borer;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class BorerBrainTakeAfterEvent : SimpleDoAfterEvent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
Content.Shared/Borer/Events/BorerInfestDoAfterEvent.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Borer;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class BorerInfestDoAfterEvent : SimpleDoAfterEvent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
9
Content.Shared/Borer/Events/BorerOverlayResponceEvent.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Borer;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class BorerOverlayResponceEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
10
Content.Shared/Borer/Events/BorerReproduceAfterEvent.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Borer;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class BorerReproduceAfterEvent : SimpleDoAfterEvent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
16
Content.Shared/Borer/Events/BorerScanDoAfterEvent.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.FixedPoint;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Borer;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class BorerScanDoAfterEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public Dictionary<string, FixedPoint2> Solution;
|
||||||
|
|
||||||
|
public BorerScanDoAfterEvent(Dictionary<string, FixedPoint2> solution)
|
||||||
|
{
|
||||||
|
Solution = solution;
|
||||||
|
}
|
||||||
|
}
|
||||||
176
Content.Shared/Borer/SharedBorerSystem.cs
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.Alert;
|
||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Mind;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Borer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This handles...
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SharedBorerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
|
||||||
|
[Dependency] private readonly SharedActionsSystem _action = default!;
|
||||||
|
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||||
|
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<BorerComponent, ComponentStartup>(OnStartup);
|
||||||
|
SubscribeLocalEvent<BorerComponent, ComponentRemove>(OnRemove);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<InfestedBorerComponent, ComponentStartup>(OnStartup);
|
||||||
|
SubscribeLocalEvent<InfestedBorerComponent, ComponentRemove>(OnRemove);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<InfestedBorerComponent, ExamineAttemptEvent>(OnExamineAttempt);
|
||||||
|
SubscribeLocalEvent<BorerComponent, ExamineAttemptEvent>(OnExamineAttempt);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<InfestedBorerComponent, BorerBrainResistEvent>(OnResistControl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRemove(EntityUid uid, InfestedBorerComponent component, ComponentRemove args)
|
||||||
|
{
|
||||||
|
if (!TryComp(uid, out ActionsComponent? borerActComponent))
|
||||||
|
return;
|
||||||
|
_action.RemoveAction(uid, component.ActionBorerOutEntity, borerActComponent);
|
||||||
|
_action.RemoveAction(uid, component.ActionBorerScanEntity, borerActComponent);
|
||||||
|
_action.RemoveAction(uid, component.ActionBorerBrainTakeEntity, borerActComponent);
|
||||||
|
_action.RemoveAction(uid, component.ActionBorerBrainSpeechEntity, borerActComponent);
|
||||||
|
_action.RemoveAction(uid, component.ActionBorerInjectWindowOpenEntity, borerActComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRemove(EntityUid uid, BorerComponent component, ComponentRemove args)
|
||||||
|
{
|
||||||
|
if (!TryComp(uid, out ActionsComponent? borerActComponent))
|
||||||
|
return;
|
||||||
|
_action.RemoveAction(uid, component.ActionInfestEntity, borerActComponent);
|
||||||
|
_action.RemoveAction(uid, component.ActionStunEntity, borerActComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnResistControl(EntityUid uid, InfestedBorerComponent component, BorerBrainResistEvent args)
|
||||||
|
{
|
||||||
|
args.Handled = true;
|
||||||
|
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager,
|
||||||
|
uid,
|
||||||
|
TimeSpan.FromSeconds(30),
|
||||||
|
new BorerBrainResistAfterEvent(), uid)
|
||||||
|
{
|
||||||
|
Hidden = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStartup(EntityUid uid, BorerComponent component, ComponentStartup args)
|
||||||
|
{
|
||||||
|
if (!TryComp(uid, out ActionsComponent? comp))
|
||||||
|
return;
|
||||||
|
_action.AddAction(uid, ref component.ActionInfestEntity, component.ActionInfest, component: comp);
|
||||||
|
_action.AddAction(uid, ref component.ActionStunEntity, component.ActionStun, component: comp);
|
||||||
|
_metaData.SetEntityName(uid, Loc.GetString("borer-entity-name"));
|
||||||
|
_metaData.SetEntityDescription(uid, Loc.GetString("borer-entity-description"));
|
||||||
|
RaiseNetworkEvent(new BorerOverlayResponceEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStartup(EntityUid uid, InfestedBorerComponent component, ComponentStartup args)
|
||||||
|
{
|
||||||
|
AddInfestedAbilities(uid, component);
|
||||||
|
}
|
||||||
|
private void OnExamineAttempt(EntityUid uid, InfestedBorerComponent component, ExamineAttemptEvent args)
|
||||||
|
{
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExamineAttempt(EntityUid uid, BorerComponent component, ExamineAttemptEvent args)
|
||||||
|
{
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RaiseInjectEvent(string protoId, int cost)
|
||||||
|
{
|
||||||
|
RaiseNetworkEvent(new BorerInjectActionEvent(protoId, cost));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dictionary<string, int> GetReagents(EntityUid borerUid)
|
||||||
|
{
|
||||||
|
if (TryComp(borerUid, out InfestedBorerComponent? infestedComp))
|
||||||
|
return infestedComp.AvailableReagents;
|
||||||
|
else
|
||||||
|
return new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddInfestedAbilities(EntityUid uid, InfestedBorerComponent component)
|
||||||
|
{
|
||||||
|
if (!TryComp(uid, out ActionsComponent? comp))
|
||||||
|
return false;
|
||||||
|
_action.AddAction(uid, ref component.ActionBorerOutEntity, component.ActionBorerOut, component: comp);
|
||||||
|
_action.AddAction(uid, ref component.ActionBorerBrainSpeechEntity, component.ActionBorerBrainSpeech, component: comp);
|
||||||
|
_action.AddAction(uid, ref component.ActionBorerInjectWindowOpenEntity, component.ActionBorerInjectWindowOpen, component: comp);
|
||||||
|
_action.AddAction(uid, ref component.ActionBorerScanEntity, component.ActionBorerScan, component: comp);
|
||||||
|
_action.AddAction(uid, ref component.ActionBorerBrainTakeEntity, component.ActionBorerBrainTake, component: comp);
|
||||||
|
if (component.ActionBorerBrainTakeEntity.HasValue)
|
||||||
|
{
|
||||||
|
_metaData.SetEntityName(component.ActionBorerBrainTakeEntity.Value,
|
||||||
|
$"{Loc.GetString("borer-abilities-control-name")} ([color=red]{component.AssumeControlCost}c[/color])");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetPoints(EntityUid borerUid)
|
||||||
|
{
|
||||||
|
if (TryComp(borerUid, out InfestedBorerComponent? infestedComp))
|
||||||
|
return infestedComp.Points;
|
||||||
|
else return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityUid? GetHost(EntityUid borerUid)
|
||||||
|
{
|
||||||
|
if (TryComp(borerUid, out InfestedBorerComponent? infestedComp))
|
||||||
|
return infestedComp.Host;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed partial class BorerInfestActionEvent : EntityTargetActionEvent {}
|
||||||
|
|
||||||
|
public sealed partial class BorerOutActionEvent : InstantActionEvent {}
|
||||||
|
|
||||||
|
public sealed partial class BorerBrainSpeechActionEvent : InstantActionEvent {}
|
||||||
|
|
||||||
|
public sealed partial class BorerInjectWindowOpenEvent : InstantActionEvent{}
|
||||||
|
|
||||||
|
public sealed partial class BorerBrainTakeEvent : InstantActionEvent{}
|
||||||
|
|
||||||
|
public sealed partial class BorerBrainReleaseEvent : InstantActionEvent{}
|
||||||
|
|
||||||
|
public sealed partial class BorerBrainResistEvent : InstantActionEvent{}
|
||||||
|
|
||||||
|
public sealed partial class BorerStunActionEvent : EntityTargetActionEvent{}
|
||||||
|
|
||||||
|
public sealed partial class BorerReproduceEvent : InstantActionEvent { }
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class BorerPointsUpdateEvent : EntityEventArgs{}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class BorerInjectActionEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public string ProtoId;
|
||||||
|
|
||||||
|
public int Cost;
|
||||||
|
|
||||||
|
public BorerInjectActionEvent(string protoId, int cost)
|
||||||
|
{
|
||||||
|
ProtoId = protoId;
|
||||||
|
Cost = cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public sealed partial class BorerScanInstantActionEvent : InstantActionEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
52
Resources/Locale/en-US/borer/borer.ftl
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
ghost-role-information-borer-name = Cortical Borer
|
||||||
|
ghost-role-information-borer-description = We are Borer!
|
||||||
|
|
||||||
|
borer-entity-name = Cortical Borer
|
||||||
|
borer-entity-description = It looks like it's making you lose your mind.
|
||||||
|
|
||||||
|
borer-abilities-infest-name = Infest
|
||||||
|
borer-abilities-infest-description = Allows you to [color=red]bury[/color] yourself into a host
|
||||||
|
borer-abilities-paralyze-name = Paralyze Victim
|
||||||
|
borer-abilities-paralyze-description = Sending a [color=red]psychic lance[/color] straight to an unsuspecting victim.
|
||||||
|
borer-abilities-release-host-name = Release Host
|
||||||
|
borer-abilities-release-host-description = Starts to [color=red]leave[/color] the host
|
||||||
|
borer-abilities-converse-name = Converse with Host
|
||||||
|
borer-abilities-converse-description = [color=red]Talks[/color] to the host. Nobody can intercept this. Only the host.
|
||||||
|
borer-abilities-secrete-name = Secrete Chemicals
|
||||||
|
borer-abilities-secrete-description = [color=red]Injects[/color] different kinda of chemicals into the host, from meth to bicardine.
|
||||||
|
borer-abilities-scan-name = Chemical Scanning
|
||||||
|
borer-abilities-scan-description = [color=red]Scan the host's blood[/color] for the presence of reagents in it.
|
||||||
|
borer-abilities-control-name = Assume Control
|
||||||
|
borer-abilities-control-description = Allows you to [color=red]assume direct control[/color] of your host.
|
||||||
|
borer-abilities-reproduce-name = Reproduce
|
||||||
|
borer-abilities-reproduce-description = Create one of your own kind.
|
||||||
|
borer-abilities-restore-name = Restore Control
|
||||||
|
borer-abilities-restore-description = [color=red]Restores[/color] control over the host's body.
|
||||||
|
borer-abilities-resist-name = Resist
|
||||||
|
borer-abilities-resist-description = [color=red]Resisting the control[/color] of your body.
|
||||||
|
|
||||||
|
borer-ui-scan-title = Scanning results
|
||||||
|
borer-ui-scan-label = Reagents in the host's blood:
|
||||||
|
borer-ui-secrete-title = Secrete chemicals
|
||||||
|
borer-ui-secrete-inject-label = Inject {$reagent}(10u) - {$cost}c
|
||||||
|
|
||||||
|
borer-ui-converse-title = Converse
|
||||||
|
borer-ui-converse-message = Message
|
||||||
|
|
||||||
|
borer-popup-infest-occupied = This creature's brain is already occupied
|
||||||
|
borer-popup-infest-sugar = The creature has too much sugar in it's blood
|
||||||
|
borer-popup-infest-failed = You can't infest into this creature
|
||||||
|
|
||||||
|
borer-popup-braintake-alert = Your brain has been taken over!
|
||||||
|
borer-popup-braintake-success = You've taken control of the host's brain!
|
||||||
|
borer-popup-braintake-critical = You cannot control the brain of a creature in critical condition
|
||||||
|
|
||||||
|
borer-popup-toomuchsugar = Your host's blood sugar prevents you from doing that
|
||||||
|
borer-popup-lowchem = Not enough chemicals!
|
||||||
|
borer-popup-injected = {$reagent}(10u) successfully injected!
|
||||||
|
borer-popup-sugarleave = The host has too much sugar in his blood, you can't be in his body anymore
|
||||||
|
|
||||||
|
borer-message-braintake-success = You've taken control of the host's brain! The host may resist this and try to regain control. Have fun while you still have time!
|
||||||
|
borer-message-braintake-alert = Your brain has been taken over! You can resist or just let it happen
|
||||||
|
|
||||||
|
borer-event-announcement = Detected unidentified life forms on the board. Secure all exterior entrances and exits, including ventilation and hoods.
|
||||||
52
Resources/Locale/ru-RU/borer/borer.ftl
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
ghost-role-information-borer-name = Аскарида космическая
|
||||||
|
ghost-role-information-borer-description = Мы - червь!
|
||||||
|
|
||||||
|
borer-entity-name = Аскарида космическая
|
||||||
|
borer-entity-description = Выглядит так, будто от неё можно сойти с ума
|
||||||
|
|
||||||
|
borer-abilities-infest-name = Вселение
|
||||||
|
borer-abilities-infest-description = [color=red]Поселиться в мозгу[/color] носителя.
|
||||||
|
borer-abilities-paralyze-name = Паралич
|
||||||
|
borer-abilities-paralyze-description = [color=red]Парализовать[/color] ничего не подозревающую жертву .
|
||||||
|
borer-abilities-release-host-name = Покинуть носителя
|
||||||
|
borer-abilities-release-host-description = [color=red]Покинуть[/color] тело носителя.
|
||||||
|
borer-abilities-converse-name = Связь с носителем
|
||||||
|
borer-abilities-converse-description = [color=red]Пообщаться[/color] с носителем, посылая сообщения прямо в его мозг.
|
||||||
|
borer-abilities-secrete-name = Впрыск реагентов
|
||||||
|
borer-abilities-secrete-description = [color=red]Вводит[/color] разлиные химикаты в кровь носителя.
|
||||||
|
borer-abilities-scan-name = Анализ крови
|
||||||
|
borer-abilities-scan-description = [color=red]Сканирует кровь[/color] носителя на наличие реагентов.
|
||||||
|
borer-abilities-control-name = Захватить контроль
|
||||||
|
borer-abilities-control-description = Отобрать [color=red]контроль над телом[/color] у носителя.
|
||||||
|
borer-abilities-reproduce-name = Репродукция
|
||||||
|
borer-abilities-reproduce-description = Создать себе подобного.
|
||||||
|
borer-abilities-restore-name = Вернуть контроль
|
||||||
|
borer-abilities-restore-description = [color=red]Возвращает[/color] контроль над телом разуму носителя.
|
||||||
|
borer-abilities-resist-name = Сопротивляться
|
||||||
|
borer-abilities-resist-description = Попытаться [color=red]вернуть[/color] себе контроль над телом.
|
||||||
|
|
||||||
|
borer-ui-scan-title = Scanning result
|
||||||
|
borer-ui-scan-label = Реагенты в крови носителя:
|
||||||
|
borer-ui-secrete-title = Ввод реагентов
|
||||||
|
borer-ui-secrete-inject-label = Ввести {$reagent}(10u) - {$cost}c
|
||||||
|
|
||||||
|
borer-ui-converse-title = Связь
|
||||||
|
borer-ui-converse-message = Сообщение
|
||||||
|
|
||||||
|
borer-popup-infest-occupied = Мозг этого существа уже кем-то занят
|
||||||
|
borer-popup-infest-sugar = В крови существа слишком много сахара
|
||||||
|
borer-popup-infest-failed = Вы не можете вселиться в это существо
|
||||||
|
|
||||||
|
borer-popup-braintake-alert = Вы не управляете своим телом!
|
||||||
|
borer-popup-braintake-success = Вы забрали контроль над телом!
|
||||||
|
borer-popup-braintake-critical = Вы не можете забрать контроль над телом у существа в критическом состоянии!
|
||||||
|
|
||||||
|
borer-popup-toomuchsugar = Сахар в крови носителя не позволяет Вам сделать это
|
||||||
|
borer-popup-lowchem = Недостаточно химикатов!
|
||||||
|
borer-popup-injected = {$reagent}(10u) успешно введено!
|
||||||
|
borer-popup-sugarleave = В крови носителя слишком много сахара, Вы не можете больше находиться в его теле
|
||||||
|
|
||||||
|
borer-message-braintake-success = Вы захватили контроль над мозгом носителя! Носитель может воспротивиться этому и попытаться вернуть контроль. Веселитесь, пока есть время!
|
||||||
|
borer-message-braintake-alert = Ваш мозг захвачен! Вы можете сопротивляться или просто позволить этому случиться
|
||||||
|
|
||||||
|
borer-event-announcement = Обнаружены неопознанные формы жизни на борту. Перекройте все внешние входы и выходы, включая вентиляцию и вытяжки.
|
||||||
133
Resources/Prototypes/Actions/borer.yml
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
- type: entity
|
||||||
|
id: ActionInfest
|
||||||
|
name: borer-abilities-infest-name
|
||||||
|
description: borer-abilities-infest-description
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: EntityTargetAction
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
state: action_infest
|
||||||
|
event: !type:BorerInfestActionEvent
|
||||||
|
itemIconStyle: BigAction
|
||||||
|
canTargetSelf: false
|
||||||
|
useDelay: 15
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionBorerStunVictim
|
||||||
|
name: borer-abilities-paralyze-name
|
||||||
|
description: borer-abilities-paralyze-description
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: EntityTargetAction
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
state: action_stun
|
||||||
|
event: !type:BorerStunActionEvent
|
||||||
|
itemIconStyle: BigAction
|
||||||
|
canTargetSelf: false
|
||||||
|
useDelay: 40
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionBorerOut
|
||||||
|
name: borer-abilities-release-host-name
|
||||||
|
description: borer-abilities-release-host-description
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: InstantAction
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
state: action_out
|
||||||
|
event: !type:BorerOutActionEvent
|
||||||
|
useDelay: 4
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionBorerBrainSpeech
|
||||||
|
name: borer-abilities-converse-name
|
||||||
|
description: borer-abilities-converse-description
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: InstantAction
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
state: action_brainspeech
|
||||||
|
event: !type:BorerBrainSpeechActionEvent
|
||||||
|
useDelay: 4
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionBorerInjectWindowOpen
|
||||||
|
name: borer-abilities-secrete-name
|
||||||
|
description: borer-abilities-secrete-description
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: InstantAction
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
state: action_inject
|
||||||
|
event: !type:BorerInjectWindowOpenEvent
|
||||||
|
useDelay: 4
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionBorerScan
|
||||||
|
name: borer-abilities-scan-name
|
||||||
|
description: borer-abilities-scan-description
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: InstantAction
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
state: action_scanreagents
|
||||||
|
event: !type:BorerScanInstantActionEvent
|
||||||
|
useDelay: 5
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionBorerBrainTake
|
||||||
|
name: borer-abilities-control-name
|
||||||
|
description: borer-abilities-control-description
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: InstantAction
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
state: action_braintake
|
||||||
|
event: !type:BorerBrainTakeEvent
|
||||||
|
useDelay: 4
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionBorerReproduce
|
||||||
|
name: borer-abilities-reproduce-name
|
||||||
|
description: borer-abilities-reproduce-description
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: InstantAction
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
state: action_reproduce
|
||||||
|
event: !type:BorerReproduceEvent
|
||||||
|
useDelay: 30
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionBorerBrainRelease
|
||||||
|
name: borer-abilities-restore-name
|
||||||
|
description: borer-abilities-restore-description
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: InstantAction
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
state: action_brainrelease
|
||||||
|
event: !type:BorerBrainReleaseEvent
|
||||||
|
useDelay: 4
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionBorerBrainResist
|
||||||
|
name: borer-abilities-resist-name
|
||||||
|
description: borer-abilities-resist-description
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: InstantAction
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
state: action_brainrelease
|
||||||
|
event: !type:BorerBrainResistEvent
|
||||||
|
useDelay: 4
|
||||||
72
Resources/Prototypes/Entities/Mobs/NPCs/borer.yml
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
- type: entity
|
||||||
|
name: basic borer
|
||||||
|
id: MobBorerBase
|
||||||
|
parent: SimpleSpaceMobBase
|
||||||
|
abstract: true
|
||||||
|
description: It looks like it's making you lose your mind.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
drawdepth: SmallMobs
|
||||||
|
sprite: Mobs/Animals/borer.rsi
|
||||||
|
layers:
|
||||||
|
- map: [ "enum.DamageStateVisualLayers.Base" ]
|
||||||
|
state: borer
|
||||||
|
- type: DamageStateVisuals
|
||||||
|
states:
|
||||||
|
Alive:
|
||||||
|
Base: borer
|
||||||
|
Dead:
|
||||||
|
Base: dead
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
fix1:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeCircle
|
||||||
|
radius: 0.2
|
||||||
|
density: 100
|
||||||
|
mask:
|
||||||
|
- SmallMobMask
|
||||||
|
layer:
|
||||||
|
- SmallMobLayer
|
||||||
|
- type: MobThresholds
|
||||||
|
thresholds:
|
||||||
|
0: Alive
|
||||||
|
20: Dead
|
||||||
|
- type: MovementSpeedModifier
|
||||||
|
baseWalkSpeed: 2
|
||||||
|
baseSprintSpeed: 4
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- CannotSuicide
|
||||||
|
maxSaturation: 15
|
||||||
|
- type: Bloodstream
|
||||||
|
bloodReagent: Slime
|
||||||
|
bloodlossDamage:
|
||||||
|
types:
|
||||||
|
Bloodloss:
|
||||||
|
1
|
||||||
|
bloodlossHealDamage:
|
||||||
|
types:
|
||||||
|
Bloodloss:
|
||||||
|
-0.25
|
||||||
|
- type: NoSlip
|
||||||
|
- type: GhostTakeoverAvailable
|
||||||
|
- type: GhostRole
|
||||||
|
makeSentient: true
|
||||||
|
name: ghost-role-information-borer-name
|
||||||
|
description: ghost-role-information-borer-description
|
||||||
|
- type: CombatMode
|
||||||
|
combatToggleAction: ActionCombatModeToggleOff
|
||||||
|
- type: Actions
|
||||||
|
- type: ActionsContainer
|
||||||
|
- type: Speech
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
name: Cortical Borer
|
||||||
|
parent: MobBorerBase
|
||||||
|
id: MobSimpleBorer
|
||||||
|
components:
|
||||||
|
- type: Borer
|
||||||
|
reproduceCost: 100
|
||||||
|
assumeControlCost: 250
|
||||||
@@ -373,6 +373,25 @@
|
|||||||
entries:
|
entries:
|
||||||
- id: MobGiantSpiderAngry
|
- id: MobGiantSpiderAngry
|
||||||
prob: 0.05
|
prob: 0.05
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: BorerSpawn
|
||||||
|
parent: BaseGameRule
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: StationEvent
|
||||||
|
earliestStart: 20
|
||||||
|
minimumPlayers: 15
|
||||||
|
weight: 5
|
||||||
|
duration: 60
|
||||||
|
startDelay: 30
|
||||||
|
startAnnouncement: borer-event-announcement
|
||||||
|
startAudio:
|
||||||
|
path: /Audio/Announcements/attention.ogg
|
||||||
|
- type: VentCrittersRule
|
||||||
|
entries:
|
||||||
|
- id: MobSimpleBorer
|
||||||
|
prob: 0.04
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: SpiderClownSpawn
|
id: SpiderClownSpawn
|
||||||
|
|||||||
BIN
Resources/Textures/Interface/Borer/chem_bg.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Resources/Textures/Mobs/Animals/borer.rsi/action_brainspeech.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Resources/Textures/Mobs/Animals/borer.rsi/action_braintake.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Resources/Textures/Mobs/Animals/borer.rsi/action_infest.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Resources/Textures/Mobs/Animals/borer.rsi/action_inject.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Resources/Textures/Mobs/Animals/borer.rsi/action_out.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
Resources/Textures/Mobs/Animals/borer.rsi/action_reproduce.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Resources/Textures/Mobs/Animals/borer.rsi/action_stun.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Resources/Textures/Mobs/Animals/borer.rsi/borer.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Resources/Textures/Mobs/Animals/borer.rsi/dead.png
Normal file
|
After Width: | Height: | Size: 524 B |
55
Resources/Textures/Mobs/Animals/borer.rsi/meta.json
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Created by Ogunefu",
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "borer",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dead",
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "action_out",
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "action_infest",
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "action_stun",
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "action_brainspeech",
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "action_inject",
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "action_scanreagents",
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "action_braintake",
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "action_reproduce",
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "action_brainrelease",
|
||||||
|
"directions": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "8.0.100",
|
"version": "8.0.0",
|
||||||
"rollForward": "latestFeature"
|
"rollForward": "latestFeature",
|
||||||
|
"allowPrerelease": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||