Merge remote-tracking branch 'upstream/master' into huistream
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Numerics;
|
||||
using System.Threading;
|
||||
using Content.Client.CombatMode;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.UserInterface.Systems.Actions;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
@@ -16,7 +17,7 @@ namespace Content.Client.ContextMenu.UI
|
||||
/// <remarks>
|
||||
/// This largely involves setting up timers to open and close sub-menus when hovering over other menu elements.
|
||||
/// </remarks>
|
||||
public sealed class ContextMenuUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>, IOnSystemChanged<CombatModeSystem>
|
||||
public sealed class ContextMenuUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>, IOnSystemChanged<CombatModeSystem>, IOnSystemChanged<ChargeActionSystem>
|
||||
{
|
||||
public static readonly TimeSpan HoverDelay = TimeSpan.FromSeconds(0.2);
|
||||
|
||||
@@ -216,6 +217,12 @@ namespace Content.Client.ContextMenu.UI
|
||||
Close();
|
||||
}
|
||||
|
||||
private void OnChargingUpdated(bool charging)
|
||||
{
|
||||
if (charging)
|
||||
Close();
|
||||
}
|
||||
|
||||
public void OnSystemLoaded(CombatModeSystem system)
|
||||
{
|
||||
system.LocalPlayerCombatModeUpdated += OnCombatModeUpdated;
|
||||
@@ -225,5 +232,15 @@ namespace Content.Client.ContextMenu.UI
|
||||
{
|
||||
system.LocalPlayerCombatModeUpdated -= OnCombatModeUpdated;
|
||||
}
|
||||
|
||||
public void OnSystemLoaded(ChargeActionSystem system)
|
||||
{
|
||||
system.ChargingUpdated += OnChargingUpdated;
|
||||
}
|
||||
|
||||
public void OnSystemUnloaded(ChargeActionSystem system)
|
||||
{
|
||||
system.ChargingUpdated -= OnChargingUpdated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Graphics.RSI;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using static Content.Client.Actions.ActionsSystem;
|
||||
@@ -29,8 +30,7 @@ using static Content.Client.UserInterface.Systems.Actions.Windows.ActionsWindow;
|
||||
using static Robust.Client.UserInterface.Control;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
using static Robust.Client.UserInterface.Controls.LineEdit;
|
||||
using static Robust.Client.UserInterface.Controls.MultiselectOptionButton<
|
||||
Content.Client.UserInterface.Systems.Actions.Windows.ActionsWindow.Filters>;
|
||||
using static Robust.Client.UserInterface.Controls.MultiselectOptionButton<Content.Client.UserInterface.Systems.Actions.Windows.ActionsWindow.Filters>;
|
||||
using static Robust.Client.UserInterface.Controls.TextureRect;
|
||||
using static Robust.Shared.Input.Binding.PointerInputCmdHandler;
|
||||
|
||||
@@ -128,25 +128,45 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
}
|
||||
|
||||
builder
|
||||
.Bind(ContentKeyFunctions.OpenActionsMenu,
|
||||
InputCmdHandler.FromDelegate(_ => ToggleWindow()))
|
||||
.BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(TargetingOnUse, outsidePrediction: true),
|
||||
typeof(ConstructionSystem), typeof(DragDropSystem))
|
||||
.BindBefore(EngineKeyFunctions.UIRightClick, new PointerInputCmdHandler(TargetingCancel, outsidePrediction: true))
|
||||
.Bind(ContentKeyFunctions.OpenActionsMenu, InputCmdHandler.FromDelegate(_ => ToggleWindow()))
|
||||
.BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(TargetingOnUse, outsidePrediction: true), typeof(ConstructionSystem), typeof(DragDropSystem))
|
||||
.BindBefore(ContentKeyFunctions.AltActivateItemInWorld, new PointerInputCmdHandler(AltUse, outsidePrediction: true))
|
||||
.Register<ActionUIController>();
|
||||
}
|
||||
|
||||
private bool TargetingCancel(in PointerInputCmdArgs args)
|
||||
private bool AltUse(in PointerInputCmdArgs args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
if (!_timing.IsFirstTimePredicted || _actionsSystem == null || SelectingTargetFor is not { } actionId)
|
||||
return false;
|
||||
|
||||
// only do something for actual target-based actions
|
||||
if (SelectingTargetFor == null)
|
||||
if (_playerManager.LocalEntity is not { } user)
|
||||
return false;
|
||||
|
||||
StopTargeting();
|
||||
return true;
|
||||
if (!EntityManager.TryGetComponent(user, out ActionsComponent? comp))
|
||||
return false;
|
||||
|
||||
if (!_actionsSystem.TryGetActionData(actionId, out var baseAction) ||
|
||||
baseAction is not BaseTargetActionComponent action || !action.IsAltEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the action currently valid?
|
||||
if (!action.Enabled
|
||||
|| action is { Charges: 0, RenewCharges: false }
|
||||
|| action.Cooldown.HasValue && action.Cooldown.Value.End > _timing.CurTime)
|
||||
{
|
||||
// The user is targeting with this action, but it is not valid. Maybe mark this click as
|
||||
// handled and prevent further interactions.
|
||||
return !action.InteractOnMiss;
|
||||
}
|
||||
|
||||
return action switch
|
||||
{
|
||||
WorldTargetActionComponent mapTarget => TryTargetWorld(args.Coordinates, actionId, mapTarget, user, comp,
|
||||
ActionUseType.AltUse, target: args.EntityUid) || !mapTarget.InteractOnMiss,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,28 +199,23 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
return !action.InteractOnMiss;
|
||||
}
|
||||
|
||||
switch (action)
|
||||
return action switch
|
||||
{
|
||||
case WorldTargetActionComponent mapTarget:
|
||||
return TryTargetWorld(args, actionId, mapTarget, user, comp) || !mapTarget.InteractOnMiss;
|
||||
|
||||
case EntityTargetActionComponent entTarget:
|
||||
return TryTargetEntity(args, actionId, entTarget, user, comp) || !entTarget.InteractOnMiss;
|
||||
|
||||
default:
|
||||
Logger.Error($"Unknown targeting action: {actionId.GetType()}");
|
||||
return false;
|
||||
}
|
||||
WorldTargetActionComponent mapTarget => TryTargetWorld(args.Coordinates, actionId, mapTarget, user, comp) ||
|
||||
!mapTarget.InteractOnMiss,
|
||||
EntityTargetActionComponent entTarget => TryTargetEntity(args.EntityUid, actionId, entTarget, user, comp) ||
|
||||
!entTarget.InteractOnMiss,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
private bool TryTargetWorld(in PointerInputCmdArgs args, EntityUid actionId, WorldTargetActionComponent action, EntityUid user, ActionsComponent actionComp)
|
||||
public bool TryTargetWorld(EntityCoordinates coordinates, EntityUid actionId, WorldTargetActionComponent action,
|
||||
EntityUid user, ActionsComponent actionComp, ActionUseType type = ActionUseType.Default, int chargeLevel = 0, EntityUid? target = default)
|
||||
{
|
||||
if (_actionsSystem == null)
|
||||
return false;
|
||||
|
||||
var coords = args.Coordinates;
|
||||
|
||||
if (!_actionsSystem.ValidateWorldTarget(user, coords, action))
|
||||
if (!_actionsSystem.ValidateWorldTarget(user, coordinates, action))
|
||||
{
|
||||
// Invalid target.
|
||||
if (action.DeselectOnMiss)
|
||||
@@ -213,14 +228,24 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
{
|
||||
if (action.Event != null)
|
||||
{
|
||||
action.Event.Target = coords;
|
||||
action.Event.Target = coordinates;
|
||||
action.Event.Performer = user;
|
||||
}
|
||||
|
||||
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
|
||||
}
|
||||
else
|
||||
EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(EntityManager.GetNetEntity(actionId), EntityManager.GetNetCoordinates(coords)));
|
||||
{
|
||||
var msg = new RequestPerformActionEvent(EntityManager.GetNetEntity(actionId), EntityManager.GetNetCoordinates(coordinates))
|
||||
{
|
||||
ActionUseType = type,
|
||||
ChargeLevel = chargeLevel,
|
||||
EntityTarget = EntityManager.GetNetEntity(target)
|
||||
};
|
||||
|
||||
EntityManager.RaisePredictiveEvent(msg);
|
||||
}
|
||||
|
||||
|
||||
if (!action.Repeat)
|
||||
StopTargeting();
|
||||
@@ -228,14 +253,12 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryTargetEntity(in PointerInputCmdArgs args, EntityUid actionId, EntityTargetActionComponent action, EntityUid user, ActionsComponent actionComp)
|
||||
public bool TryTargetEntity(EntityUid uid, EntityUid actionId, EntityTargetActionComponent action, EntityUid user, ActionsComponent actionComp, ActionUseType type = ActionUseType.Default)
|
||||
{
|
||||
if (_actionsSystem == null)
|
||||
return false;
|
||||
|
||||
var entity = args.EntityUid;
|
||||
|
||||
if (!_actionsSystem.ValidateEntityTarget(user, entity, action))
|
||||
if (!_actionsSystem.ValidateEntityTarget(user, uid, action))
|
||||
{
|
||||
if (action.DeselectOnMiss)
|
||||
StopTargeting();
|
||||
@@ -247,14 +270,21 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
{
|
||||
if (action.Event != null)
|
||||
{
|
||||
action.Event.Target = entity;
|
||||
action.Event.Target = uid;
|
||||
action.Event.Performer = user;
|
||||
}
|
||||
|
||||
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
|
||||
}
|
||||
else
|
||||
EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(EntityManager.GetNetEntity(actionId), EntityManager.GetNetEntity(args.EntityUid)));
|
||||
{
|
||||
var msg = new RequestPerformActionEvent(EntityManager.GetNetEntity(actionId), EntityManager.GetNetEntity(uid))
|
||||
{
|
||||
ActionUseType = type
|
||||
};
|
||||
|
||||
EntityManager.RaisePredictiveEvent(msg);
|
||||
}
|
||||
|
||||
if (!action.Repeat)
|
||||
StopTargeting();
|
||||
@@ -409,12 +439,6 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
};
|
||||
}
|
||||
|
||||
private void ClearList()
|
||||
{
|
||||
if (_window?.Disposed == false)
|
||||
_window.ResultsGrid.RemoveAllChildren();
|
||||
}
|
||||
|
||||
private void PopulateActions(IEnumerable<(EntityUid Id, BaseActionComponent Comp)> actions)
|
||||
{
|
||||
if (_window is not { Disposed: false, IsOpen: true })
|
||||
@@ -432,7 +456,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
existing.Add(button);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
var i = 0;
|
||||
foreach (var action in actions)
|
||||
{
|
||||
if (i < existing.Count)
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
using Content.Client.Actions;
|
||||
using Content.Shared._White.Wizard;
|
||||
using Content.Shared._White.Wizard.Charging;
|
||||
using Content.Shared.Actions;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Actions;
|
||||
|
||||
public sealed class ChargeActionSystem : SharedChargingSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly ActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
[Dependency] private readonly InputSystem _inputSystem = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
private ActionUIController? _controller;
|
||||
|
||||
public event Action<bool>? ChargingUpdated;
|
||||
|
||||
private bool _charging;
|
||||
private bool _prevCharging;
|
||||
|
||||
private float _chargeTime;
|
||||
private int _chargeLevel;
|
||||
private int _prevChargeLevel;
|
||||
|
||||
private bool _isChargingPlaying;
|
||||
private bool _isChargedPlaying;
|
||||
|
||||
private const float LevelChargeTime = 1.5f;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_controller = _uiManager.GetUIController<ActionUIController>();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
if (_playerManager.LocalEntity is not { } user)
|
||||
return;
|
||||
|
||||
if (!_timing.IsFirstTimePredicted || _controller == null || _controller.SelectingTargetFor is not { } actionId)
|
||||
return;
|
||||
|
||||
if (!_actionsSystem.TryGetActionData(actionId, out var baseAction) ||
|
||||
baseAction is not BaseTargetActionComponent action || !action.IsChargeEnabled)
|
||||
return;
|
||||
|
||||
if (!action.Enabled
|
||||
|| action is { Charges: 0, RenewCharges: false }
|
||||
|| action.Cooldown.HasValue && action.Cooldown.Value.End > _timing.CurTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var altDown = _inputSystem.CmdStates.GetState(EngineKeyFunctions.UseSecondary);
|
||||
switch (altDown)
|
||||
{
|
||||
case BoundKeyState.Down:
|
||||
_prevCharging = _charging;
|
||||
_charging = true;
|
||||
_chargeTime += frameTime;
|
||||
_chargeLevel = (int) (_chargeTime / LevelChargeTime) + 1;
|
||||
_chargeLevel = Math.Clamp(_chargeLevel, 1, action.MaxChargeLevel);
|
||||
break;
|
||||
case BoundKeyState.Up when _charging:
|
||||
_prevCharging = _charging;
|
||||
_charging = false;
|
||||
_chargeTime = 0f;
|
||||
_isChargingPlaying = false;
|
||||
_isChargedPlaying = false;
|
||||
|
||||
HandleAction(actionId, action, user, _chargeLevel);
|
||||
_chargeLevel = 0;
|
||||
|
||||
RaiseNetworkEvent(new RequestAudioSpellStop());
|
||||
RaiseNetworkEvent(new RemoveWizardChargeEvent());
|
||||
break;
|
||||
case BoundKeyState.Up:
|
||||
_prevCharging = _charging;
|
||||
_chargeLevel = 0;
|
||||
_charging = false;
|
||||
_chargeTime = 0f;
|
||||
_isChargingPlaying = false;
|
||||
_isChargedPlaying = false;
|
||||
|
||||
RaiseNetworkEvent(new RequestAudioSpellStop());
|
||||
RaiseNetworkEvent(new RemoveWizardChargeEvent());
|
||||
break;
|
||||
}
|
||||
|
||||
if (_chargeLevel != _prevChargeLevel)
|
||||
{
|
||||
if (_chargeLevel > 0 && _charging)
|
||||
{
|
||||
RaiseNetworkEvent(new AddWizardChargeEvent(action.ChargeProto));
|
||||
}
|
||||
_prevChargeLevel = _chargeLevel;
|
||||
}
|
||||
|
||||
if (_prevCharging != _charging)
|
||||
{
|
||||
ChargingUpdated?.Invoke(_charging);
|
||||
}
|
||||
|
||||
if (_charging && !_isChargingPlaying)
|
||||
{
|
||||
_isChargingPlaying = true;
|
||||
RaiseNetworkEvent(new RequestSpellChargingAudio(action.ChargingSound, action.LoopCharging));
|
||||
}
|
||||
|
||||
if (_chargeLevel >= action.MaxChargeLevel && !_isChargedPlaying && _charging)
|
||||
{
|
||||
_isChargedPlaying = true;
|
||||
RaiseNetworkEvent(new RequestSpellChargedAudio(action.MaxChargedSound, action.LoopMaxCharged));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAction(EntityUid actionId, BaseTargetActionComponent action, EntityUid user, int chargeLevel)
|
||||
{
|
||||
var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition);
|
||||
if (mousePos.MapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
var coordinates = EntityCoordinates.FromMap(_mapManager.TryFindGridAt(mousePos, out var gridUid, out _)
|
||||
? gridUid
|
||||
: _mapManager.GetMapEntityId(mousePos.MapId), mousePos, _transformSystem, EntityManager);
|
||||
|
||||
if (!EntityManager.TryGetComponent(user, out ActionsComponent? comp))
|
||||
return;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case WorldTargetActionComponent mapTarget:
|
||||
_controller?.TryTargetWorld(coordinates, actionId, mapTarget, user, comp, ActionUseType.Charge, chargeLevel);
|
||||
break;
|
||||
}
|
||||
|
||||
RaiseNetworkEvent(new RequestAudioSpellStop());
|
||||
RaiseNetworkEvent(new RemoveWizardChargeEvent());
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_controller = null;
|
||||
|
||||
_charging = false;
|
||||
_prevCharging = false;
|
||||
_chargeTime = 0f;
|
||||
_chargeLevel = 0;
|
||||
_prevChargeLevel = 0;
|
||||
_isChargingPlaying = false;
|
||||
_isChargedPlaying = false;
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,6 @@ using Robust.Client.State;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Weapons.Melee;
|
||||
|
||||
|
||||
7
Content.Client/_White/Wizard/Scrolls/ScrollSystem.cs
Normal file
7
Content.Client/_White/Wizard/Scrolls/ScrollSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Content.Shared._White.Wizard.ScrollSystem;
|
||||
|
||||
namespace Content.Client._White.Wizard.Scrolls;
|
||||
|
||||
public sealed class ScrollSystem : SharedScrollSystem
|
||||
{
|
||||
}
|
||||
@@ -69,7 +69,6 @@ namespace Content.Server.Bed.Sleep
|
||||
emitSound.Sound = sleepSound.Snore;
|
||||
emitSound.PlayChance = sleepSound.Chance;
|
||||
emitSound.RollInterval = sleepSound.Interval;
|
||||
emitSound.PopUp = sleepSound.PopUp;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -30,7 +30,12 @@ public sealed class DamagePopupSystem : EntitySystem
|
||||
DamagePopupType.Hit => "!",
|
||||
_ => "Invalid type",
|
||||
};
|
||||
|
||||
if (args.Origin.HasValue)
|
||||
_popupSystem.PopupEntity(msg, uid, args.Origin.Value);
|
||||
else
|
||||
_popupSystem.PopupEntity(msg, uid);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
15
Content.Server/EnergyDome/EnergyDomeComponent.cs
Normal file
15
Content.Server/EnergyDome/EnergyDomeComponent.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Content.Server.EnergyDome;
|
||||
|
||||
/// <summary>
|
||||
/// marker component that allows linking the dome generator with the dome itself
|
||||
/// </summary>
|
||||
|
||||
[RegisterComponent, Access(typeof(EnergyDomeSystem))]
|
||||
public sealed partial class EnergyDomeComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// A linked generator that uses energy
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Generator;
|
||||
}
|
||||
85
Content.Server/EnergyDome/EnergyDomeGeneratorComponent.cs
Normal file
85
Content.Server/EnergyDome/EnergyDomeGeneratorComponent.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using Content.Shared.DeviceLinking;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.EnergyDome;
|
||||
|
||||
/// <summary>
|
||||
/// component, allows an entity to generate a battery-powered energy dome of a specific type.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(EnergyDomeSystem))] //Access add
|
||||
public sealed partial class EnergyDomeGeneratorComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public bool Enabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// How much energy will be spent from the battery per unit of damage taken by the shield.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float DamageEnergyDraw = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the dome can be toggled via standard interactions
|
||||
/// (alt verbs, using in hand, etc)
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool CanInteractUse = true;
|
||||
|
||||
/// <summary>
|
||||
/// Can the NetworkDevice system activate and deactivate the barrier?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool CanDeviceNetworkUse = false;
|
||||
|
||||
//Dome
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public EntProtoId DomePrototype = "EnergyDomeSmallRed";
|
||||
|
||||
[DataField]
|
||||
public EntityUid? SpawnedDome;
|
||||
|
||||
/// <summary>
|
||||
/// the entity on which the shield will be hung. This is either the container containing
|
||||
/// the item or the item itself. Determined when the shield is activated,
|
||||
/// it is stored in the component for changing the protected entity.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? DomeParentEntity;
|
||||
|
||||
//Action
|
||||
[DataField]
|
||||
public EntProtoId ToggleAction = "ActionToggleDome";
|
||||
|
||||
[DataField]
|
||||
public EntityUid? ToggleActionEntity;
|
||||
|
||||
//Sounds
|
||||
[DataField]
|
||||
public SoundSpecifier AccessDeniedSound = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier TurnOnSound = new SoundPathSpecifier("/Audio/Machines/anomaly_sync_connect.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier EnergyOutSound = new SoundPathSpecifier("/Audio/Machines/energyshield_down.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier TurnOffSound = new SoundPathSpecifier("/Audio/Machines/button.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier ParrySound = new SoundPathSpecifier("/Audio/Machines/energyshield_parry.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVariation(0.05f)
|
||||
};
|
||||
|
||||
//Ports
|
||||
[DataField]
|
||||
public ProtoId<SinkPortPrototype> TogglePort = "Toggle";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<SinkPortPrototype> OnPort = "On";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<SinkPortPrototype> OffPort = "Off";
|
||||
}
|
||||
329
Content.Server/EnergyDome/EnergyDomeSystem.cs
Normal file
329
Content.Server/EnergyDome/EnergyDomeSystem.cs
Normal file
@@ -0,0 +1,329 @@
|
||||
using Content.Server.DeviceLinking.Events;
|
||||
using Content.Server.DeviceLinking.Systems;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.PowerCell;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.Toggleable;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.EnergyDome;
|
||||
|
||||
public sealed partial class EnergyDomeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly BatterySystem _battery = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
//Generator events
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, MapInitEvent>(OnInit);
|
||||
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, ActivateInWorldEvent>(OnActivatedInWorld);
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, SignalReceivedEvent>(OnSignalReceived);
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, GetItemActionsEvent>(OnGetActions);
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, ToggleActionEvent>(OnToggleAction);
|
||||
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, PowerCellChangedEvent>(OnPowerCellChanged);
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, ChargeChangedEvent>(OnChargeChanged);
|
||||
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, EntParentChangedMessage>(OnParentChanged);
|
||||
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, GetVerbsEvent<ActivationVerb>>(AddToggleDomeVerb);
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, ExaminedEvent>(OnExamine);
|
||||
|
||||
|
||||
SubscribeLocalEvent<EnergyDomeGeneratorComponent, ComponentRemove>(OnComponentRemove);
|
||||
|
||||
//Dome events
|
||||
SubscribeLocalEvent<EnergyDomeComponent, DamageChangedEvent>(OnDomeDamaged);
|
||||
}
|
||||
|
||||
|
||||
private void OnInit(Entity<EnergyDomeGeneratorComponent> generator, ref MapInitEvent args)
|
||||
{
|
||||
if (generator.Comp.CanDeviceNetworkUse)
|
||||
_signalSystem.EnsureSinkPorts(generator, generator.Comp.TogglePort, generator.Comp.OnPort, generator.Comp.OffPort);
|
||||
}
|
||||
|
||||
//different ways of use
|
||||
|
||||
private void OnSignalReceived(Entity<EnergyDomeGeneratorComponent> generator, ref SignalReceivedEvent args)
|
||||
{
|
||||
if (!generator.Comp.CanDeviceNetworkUse)
|
||||
return;
|
||||
|
||||
if (args.Port == generator.Comp.OnPort)
|
||||
{
|
||||
AttemptToggle(generator, true);
|
||||
}
|
||||
if (args.Port == generator.Comp.OffPort)
|
||||
{
|
||||
AttemptToggle(generator, false);
|
||||
}
|
||||
if (args.Port == generator.Comp.TogglePort)
|
||||
{
|
||||
AttemptToggle(generator, !generator.Comp.Enabled);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAfterInteract(Entity<EnergyDomeGeneratorComponent> generator, ref AfterInteractEvent args)
|
||||
{
|
||||
if (generator.Comp.CanInteractUse)
|
||||
AttemptToggle(generator, !generator.Comp.Enabled);
|
||||
}
|
||||
|
||||
private void OnActivatedInWorld(Entity<EnergyDomeGeneratorComponent> generator, ref ActivateInWorldEvent args)
|
||||
{
|
||||
if (generator.Comp.CanInteractUse)
|
||||
AttemptToggle(generator, !generator.Comp.Enabled);
|
||||
}
|
||||
|
||||
private void OnExamine(Entity<EnergyDomeGeneratorComponent> generator, ref ExaminedEvent args)
|
||||
{
|
||||
args.PushMarkup(Loc.GetString(
|
||||
(generator.Comp.Enabled)
|
||||
? "energy-dome-on-examine-is-on-message"
|
||||
: "energy-dome-on-examine-is-off-message"
|
||||
));
|
||||
}
|
||||
|
||||
private void AddToggleDomeVerb(Entity<EnergyDomeGeneratorComponent> generator, ref GetVerbsEvent<ActivationVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || !generator.Comp.CanInteractUse)
|
||||
return;
|
||||
|
||||
var @event = args;
|
||||
ActivationVerb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("energy-dome-verb-toggle"),
|
||||
Act = () => AttemptToggle(generator, !generator.Comp.Enabled)
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
private void OnGetActions(Entity<EnergyDomeGeneratorComponent> generator, ref GetItemActionsEvent args)
|
||||
{
|
||||
if (generator.Comp.CanInteractUse)
|
||||
args.AddAction(ref generator.Comp.ToggleActionEntity, generator.Comp.ToggleAction);
|
||||
}
|
||||
|
||||
private void OnToggleAction(Entity<EnergyDomeGeneratorComponent> generator, ref ToggleActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
AttemptToggle(generator, !generator.Comp.Enabled);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
// System interactions
|
||||
|
||||
private void OnPowerCellSlotEmpty(Entity<EnergyDomeGeneratorComponent> generator, ref PowerCellSlotEmptyEvent args)
|
||||
{
|
||||
TurnOff(generator, true);
|
||||
}
|
||||
|
||||
private void OnPowerCellChanged(Entity<EnergyDomeGeneratorComponent> generator, ref PowerCellChangedEvent args)
|
||||
{
|
||||
if (args.Ejected || !_powerCell.HasDrawCharge(generator))
|
||||
TurnOff(generator, true);
|
||||
}
|
||||
|
||||
private void OnChargeChanged(Entity<EnergyDomeGeneratorComponent> generator, ref ChargeChangedEvent args)
|
||||
{
|
||||
if (args.Charge == 0)
|
||||
TurnOff(generator, true);
|
||||
}
|
||||
private void OnDomeDamaged(Entity<EnergyDomeComponent> dome, ref DamageChangedEvent args)
|
||||
{
|
||||
if (dome.Comp.Generator == null)
|
||||
return;
|
||||
|
||||
var generatorUid = dome.Comp.Generator.Value;
|
||||
|
||||
if (!TryComp<EnergyDomeGeneratorComponent>(generatorUid, out var generatorComp))
|
||||
return;
|
||||
|
||||
if (args.DamageDelta == null)
|
||||
return;
|
||||
|
||||
float totalDamage = args.DamageDelta.GetTotal().Float();
|
||||
var energyLeak = totalDamage * generatorComp.DamageEnergyDraw;
|
||||
|
||||
_audio.PlayPvs(generatorComp.ParrySound, dome);
|
||||
|
||||
if (HasComp<PowerCellDrawComponent>(generatorUid))
|
||||
{
|
||||
_powerCell.TryGetBatteryFromSlot(generatorUid, out var cell);
|
||||
if (cell != null)
|
||||
{
|
||||
_battery.UseCharge(cell.Owner, energyLeak);
|
||||
|
||||
if (cell.Charge == 0)
|
||||
TurnOff((generatorUid, generatorComp), true);
|
||||
}
|
||||
}
|
||||
|
||||
//it seems to me it would not work well to hang both a powercell and an internal battery with wire charging on the object....
|
||||
if (TryComp<BatteryComponent>(generatorUid, out var battery)) {
|
||||
_battery.UseCharge(generatorUid, energyLeak);
|
||||
|
||||
if (battery.Charge == 0)
|
||||
TurnOff((generatorUid, generatorComp), true);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnParentChanged(Entity<EnergyDomeGeneratorComponent> generator, ref EntParentChangedMessage args)
|
||||
{
|
||||
//To do: taking the active barrier in hand for some reason does not manage to change the parent in this case,
|
||||
//and the barrier is not turned off.
|
||||
//
|
||||
//Laying down works well (-_-)
|
||||
if (GetProtectedEntity(generator) != generator.Comp.DomeParentEntity)
|
||||
TurnOff(generator, false);
|
||||
}
|
||||
|
||||
private void OnComponentRemove(Entity<EnergyDomeGeneratorComponent> generator, ref ComponentRemove args)
|
||||
{
|
||||
TurnOff(generator, false);
|
||||
}
|
||||
|
||||
// Functional
|
||||
|
||||
public bool AttemptToggle(Entity<EnergyDomeGeneratorComponent> generator, bool status)
|
||||
{
|
||||
if (TryComp<UseDelayComponent>(generator, out var useDelay) && _useDelay.IsDelayed(new Entity<UseDelayComponent>(generator, useDelay)))
|
||||
{
|
||||
_audio.PlayPvs(generator.Comp.TurnOffSound, generator);
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("energy-dome-recharging"),
|
||||
generator);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TryComp<PowerCellSlotComponent>(generator, out var powerCellSlot))
|
||||
{
|
||||
if (!_powerCell.TryGetBatteryFromSlot(generator, out var cell) && !TryComp(generator, out cell))
|
||||
{
|
||||
_audio.PlayPvs(generator.Comp.TurnOffSound, generator);
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("energy-dome-no-cell"),
|
||||
generator);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_powerCell.HasDrawCharge(generator))
|
||||
{
|
||||
_audio.PlayPvs(generator.Comp.TurnOffSound, generator);
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("energy-dome-no-power"),
|
||||
generator);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<BatteryComponent>(generator, out var battery))
|
||||
{
|
||||
if (battery.Charge == 0)
|
||||
{
|
||||
_audio.PlayPvs(generator.Comp.TurnOffSound, generator);
|
||||
_popup.PopupEntity(
|
||||
Loc.GetString("energy-dome-no-power"),
|
||||
generator);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Toggle(generator, status);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Toggle(Entity<EnergyDomeGeneratorComponent> generator, bool status)
|
||||
{
|
||||
if (status)
|
||||
TurnOn(generator);
|
||||
else
|
||||
TurnOff(generator, false);
|
||||
}
|
||||
|
||||
private void TurnOn(Entity<EnergyDomeGeneratorComponent> generator)
|
||||
{
|
||||
if (generator.Comp.Enabled)
|
||||
return;
|
||||
|
||||
var protectedEntity = GetProtectedEntity(generator);
|
||||
|
||||
var newDome = Spawn(generator.Comp.DomePrototype, Transform(protectedEntity).Coordinates);
|
||||
generator.Comp.DomeParentEntity = protectedEntity;
|
||||
_transform.SetParent(newDome, protectedEntity);
|
||||
|
||||
if (TryComp<EnergyDomeComponent>(newDome, out var domeComp))
|
||||
{
|
||||
domeComp.Generator = generator;
|
||||
}
|
||||
|
||||
_powerCell.SetPowerCellDrawEnabled(generator, true);
|
||||
if (TryComp<BatterySelfRechargerComponent>(generator, out var recharger)) {
|
||||
recharger.AutoRecharge = true;
|
||||
}
|
||||
|
||||
generator.Comp.SpawnedDome = newDome;
|
||||
_audio.PlayPvs(generator.Comp.TurnOnSound, generator);
|
||||
generator.Comp.Enabled = true;
|
||||
}
|
||||
|
||||
private void TurnOff(Entity<EnergyDomeGeneratorComponent> generator, bool startReloading)
|
||||
{
|
||||
if (!generator.Comp.Enabled)
|
||||
return;
|
||||
|
||||
generator.Comp.Enabled = false;
|
||||
QueueDel(generator.Comp.SpawnedDome);
|
||||
|
||||
_powerCell.SetPowerCellDrawEnabled(generator, false);
|
||||
if (TryComp<BatterySelfRechargerComponent>(generator, out var recharger))
|
||||
{
|
||||
recharger.AutoRecharge = false;
|
||||
}
|
||||
|
||||
_audio.PlayPvs(generator.Comp.TurnOffSound, generator);
|
||||
if (startReloading)
|
||||
{
|
||||
_audio.PlayPvs(generator.Comp.EnergyOutSound, generator);
|
||||
if (TryComp<UseDelayComponent>(generator, out var useDelay))
|
||||
{
|
||||
_useDelay.TryResetDelay(new Entity<UseDelayComponent>(generator, useDelay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Util
|
||||
|
||||
private EntityUid GetProtectedEntity(EntityUid entity)
|
||||
{
|
||||
return (_container.TryGetOuterContainer(entity, Transform(entity), out var container))
|
||||
? container.Owner
|
||||
: entity;
|
||||
}
|
||||
}
|
||||
@@ -297,7 +297,7 @@ public sealed class ExecutionSystem : EntitySystem
|
||||
{
|
||||
if (attemptEv.Message != null)
|
||||
{
|
||||
_popupSystem.PopupClient(attemptEv.Message, weapon, attacker);
|
||||
_popupSystem.PopupEntity(attemptEv.Message, weapon, attacker);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public sealed partial class NukeopsRuleComponent : Component
|
||||
/// This amount of TC will be given to each nukie
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int WarTCAmountPerNukie = 20;
|
||||
public int WarTCAmountPerNukie = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Time allowed for declaration of war
|
||||
|
||||
@@ -120,7 +120,7 @@ public sealed class InteractionPopupSystem : EntitySystem
|
||||
_popupSystem.PopupEntity(msg, uid, user);
|
||||
_popupSystem.PopupEntity(msgOthers, uid, Filter.PvsExcept(user, entityManager: EntityManager), true);
|
||||
}
|
||||
else
|
||||
else if (msg != "") // WD edit
|
||||
_popupSystem.PopupEntity(msg, uid, user); //play only for the initiating entity.
|
||||
|
||||
if (sfx is not null) //not all cases will have sound.
|
||||
|
||||
@@ -57,7 +57,6 @@ public sealed class LightningSystem : SharedLightningSystem
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks for objects with a LightningTarget component in the radius, prioritizes them, and hits the highest priority targets with lightning.
|
||||
/// </summary>
|
||||
@@ -78,9 +77,9 @@ public sealed class LightningSystem : SharedLightningSystem
|
||||
_random.Shuffle(targets);
|
||||
targets.Sort((x, y) => y.Priority.CompareTo(x.Priority));
|
||||
|
||||
int shootedCount = 0;
|
||||
int count = -1;
|
||||
while(shootedCount < boltCount)
|
||||
var shootCount = 0;
|
||||
var count = -1;
|
||||
while(shootCount < boltCount)
|
||||
{
|
||||
count++;
|
||||
|
||||
@@ -95,7 +94,7 @@ public sealed class LightningSystem : SharedLightningSystem
|
||||
{
|
||||
ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].LightningResistance, triggerLightningEvents);
|
||||
}
|
||||
shootedCount++;
|
||||
shootCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||
|
||||
namespace Content.Server.Magic.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Spellbooks for having an entity learn spells as long as they've read the book and it's in their hand.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class SpellbookComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// List of spells that this book has. This is a combination of the WorldSpells, EntitySpells, and InstantSpells.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public readonly List<EntityUid> Spells = new();
|
||||
|
||||
/// <summary>
|
||||
/// The three fields below is just used for initialization.
|
||||
/// </summary>
|
||||
[DataField("spells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<int, EntityPrototype>))]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Dictionary<string, int> SpellActions = new();
|
||||
|
||||
[DataField("learnTime")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float LearnTime = .75f;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the spell action stays even after the book is removed
|
||||
/// </summary>
|
||||
[DataField("learnPermanently")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool LearnPermanently;
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Doors.Systems;
|
||||
using Content.Server.Magic.Components;
|
||||
using Content.Server.Weapons.Ranged.Systems;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Magic;
|
||||
using Content.Shared.Magic.Events;
|
||||
using Content.Shared.Maps;
|
||||
@@ -21,6 +19,7 @@ using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
@@ -33,31 +32,25 @@ namespace Content.Server.Magic;
|
||||
/// </summary>
|
||||
public sealed class MagicSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ISerializationManager _seriMan = default!;
|
||||
[Dependency] private readonly ISerializationManager _serializationManager = default!;
|
||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly DoorBoltSystem _boltsSystem = default!;
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly GunSystem _gunSystem = default!;
|
||||
[Dependency] private readonly PhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpellbookComponent, MapInitEvent>(OnInit);
|
||||
SubscribeLocalEvent<SpellbookComponent, UseInHandEvent>(OnUse);
|
||||
SubscribeLocalEvent<SpellbookComponent, SpellbookDoAfterEvent>(OnDoAfter);
|
||||
|
||||
SubscribeLocalEvent<InstantSpawnSpellEvent>(OnInstantSpawn);
|
||||
SubscribeLocalEvent<TeleportSpellEvent>(OnTeleportSpell);
|
||||
SubscribeLocalEvent<KnockSpellEvent>(OnKnockSpell);
|
||||
@@ -67,73 +60,8 @@ public sealed class MagicSystem : EntitySystem
|
||||
SubscribeLocalEvent<ChangeComponentsSpellEvent>(OnChangeComponentsSpell);
|
||||
}
|
||||
|
||||
private void OnDoAfter(EntityUid uid, SpellbookComponent component, DoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
if (!component.LearnPermanently)
|
||||
{
|
||||
_actionsSystem.GrantActions(args.Args.User, component.Spells, uid);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (id, charges) in component.SpellActions)
|
||||
{
|
||||
// TOOD store spells entity ids on some sort of innate magic user component or something like that.
|
||||
EntityUid? actionId = null;
|
||||
if (_actionsSystem.AddAction(args.Args.User, ref actionId, id))
|
||||
_actionsSystem.SetCharges(actionId, charges < 0 ? null : charges);
|
||||
}
|
||||
|
||||
component.SpellActions.Clear();
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, SpellbookComponent component, MapInitEvent args)
|
||||
{
|
||||
if (component.LearnPermanently)
|
||||
return;
|
||||
|
||||
foreach (var (id, charges) in component.SpellActions)
|
||||
{
|
||||
var spell = _actionContainer.AddAction(uid, id);
|
||||
if (spell == null)
|
||||
continue;
|
||||
|
||||
_actionsSystem.SetCharges(spell, charges < 0 ? null : charges);
|
||||
component.Spells.Add(spell.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUse(EntityUid uid, SpellbookComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
AttemptLearn(uid, component, args);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void AttemptLearn(EntityUid uid, SpellbookComponent component, UseInHandEvent args)
|
||||
{
|
||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.LearnTime, new SpellbookDoAfterEvent(), uid, target: uid)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
NeedHand = true //What, are you going to read with your eyes only??
|
||||
};
|
||||
|
||||
_doAfter.TryStartDoAfter(doAfterEventArgs);
|
||||
}
|
||||
|
||||
#region Spells
|
||||
|
||||
/// <summary>
|
||||
/// Handles the instant action (i.e. on the caster) attempting to spawn an entity.
|
||||
/// </summary>
|
||||
private void OnInstantSpawn(InstantSpawnSpellEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -145,12 +73,12 @@ public sealed class MagicSystem : EntitySystem
|
||||
{
|
||||
var ent = Spawn(args.Prototype, position.SnapToGrid(EntityManager, _mapManager));
|
||||
|
||||
if (args.PreventCollideWithCaster)
|
||||
{
|
||||
if (!args.PreventCollideWithCaster)
|
||||
continue;
|
||||
|
||||
var comp = EnsureComp<PreventCollideComponent>(ent);
|
||||
comp.Uid = args.Performer;
|
||||
}
|
||||
}
|
||||
|
||||
Speak(args);
|
||||
args.Handled = true;
|
||||
@@ -166,22 +94,17 @@ public sealed class MagicSystem : EntitySystem
|
||||
|
||||
var xform = Transform(ev.Performer);
|
||||
|
||||
// var userVelocity = _physics.GetMapLinearVelocity(ev.Performer); WD EDIT
|
||||
|
||||
foreach (var pos in GetSpawnPositions(xform, ev.Pos))
|
||||
{
|
||||
// If applicable, this ensures the projectile is parented to grid on spawn, instead of the map.
|
||||
var mapPos = pos.ToMap(EntityManager);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out var grid) // WD EDIT
|
||||
var mapPos = _transformSystem.ToMapCoordinates(pos);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out var grid)
|
||||
? pos.WithEntityId(gridUid, EntityManager)
|
||||
: new(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
: new EntityCoordinates(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
|
||||
// WD EDIT
|
||||
var userVelocity = Vector2.Zero;
|
||||
|
||||
if (grid != null && TryComp(gridUid, out PhysicsComponent? physics))
|
||||
userVelocity = physics.LinearVelocity;
|
||||
// WD EDIT
|
||||
|
||||
var ent = Spawn(ev.Prototype, spawnCoords);
|
||||
var direction = ev.Target.ToMapPos(EntityManager, _transformSystem) -
|
||||
@@ -194,7 +117,9 @@ public sealed class MagicSystem : EntitySystem
|
||||
{
|
||||
if (ev.Handled)
|
||||
return;
|
||||
|
||||
ev.Handled = true;
|
||||
|
||||
Speak(ev);
|
||||
|
||||
foreach (var toRemove in ev.ToRemove)
|
||||
@@ -209,75 +134,12 @@ public sealed class MagicSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
var component = (Component) _compFact.GetComponent(name);
|
||||
component.Owner = ev.Target;
|
||||
var temp = (object) component;
|
||||
_seriMan.CopyTo(data.Component, ref temp);
|
||||
_serializationManager.CopyTo(data.Component, ref temp);
|
||||
EntityManager.AddComponent(ev.Target, (Component) temp!);
|
||||
}
|
||||
}
|
||||
|
||||
private List<EntityCoordinates> GetSpawnPositions(TransformComponent casterXform, MagicSpawnData data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
case TargetCasterPos:
|
||||
return new List<EntityCoordinates>(1) {casterXform.Coordinates};
|
||||
case TargetInFront:
|
||||
{
|
||||
// This is shit but you get the idea.
|
||||
var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized());
|
||||
|
||||
if (!_mapManager.TryGetGrid(casterXform.GridUid, out var mapGrid))
|
||||
return new List<EntityCoordinates>();
|
||||
|
||||
if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager))
|
||||
return new List<EntityCoordinates>();
|
||||
|
||||
var tileIndex = tileReference.Value.GridIndices;
|
||||
var coords = mapGrid.GridTileToLocal(tileIndex);
|
||||
EntityCoordinates coordsPlus;
|
||||
EntityCoordinates coordsMinus;
|
||||
|
||||
var dir = casterXform.LocalRotation.GetCardinalDir();
|
||||
switch (dir)
|
||||
{
|
||||
case Direction.North:
|
||||
case Direction.South:
|
||||
{
|
||||
coordsPlus = mapGrid.GridTileToLocal(tileIndex + (1, 0));
|
||||
coordsMinus = mapGrid.GridTileToLocal(tileIndex + (-1, 0));
|
||||
return new List<EntityCoordinates>(3)
|
||||
{
|
||||
coords,
|
||||
coordsPlus,
|
||||
coordsMinus,
|
||||
};
|
||||
}
|
||||
case Direction.East:
|
||||
case Direction.West:
|
||||
{
|
||||
coordsPlus = mapGrid.GridTileToLocal(tileIndex + (0, 1));
|
||||
coordsMinus = mapGrid.GridTileToLocal(tileIndex + (0, -1));
|
||||
return new List<EntityCoordinates>(3)
|
||||
{
|
||||
coords,
|
||||
coordsPlus,
|
||||
coordsMinus,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new List<EntityCoordinates>();
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Teleports the user to the clicked location
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
private void OnTeleportSpell(TeleportSpellEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -285,19 +147,16 @@ public sealed class MagicSystem : EntitySystem
|
||||
|
||||
var transform = Transform(args.Performer);
|
||||
|
||||
if (transform.MapID != args.Target.GetMapId(EntityManager)) return;
|
||||
if (transform.MapID != args.Target.GetMapId(EntityManager))
|
||||
return;
|
||||
|
||||
_transformSystem.SetCoordinates(args.Performer, args.Target);
|
||||
transform.AttachToGridOrMap();
|
||||
_transformSystem.AttachToGridOrMap(args.Performer);
|
||||
_audio.PlayPvs(args.BlinkSound, args.Performer, AudioParams.Default.WithVolume(args.BlinkVolume));
|
||||
Speak(args);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens all doors within range
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
private void OnKnockSpell(KnockSpellEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -306,13 +165,11 @@ public sealed class MagicSystem : EntitySystem
|
||||
args.Handled = true;
|
||||
Speak(args);
|
||||
|
||||
//Get the position of the player
|
||||
var transform = Transform(args.Performer);
|
||||
var coords = transform.Coordinates;
|
||||
|
||||
_audio.PlayPvs(args.KnockSound, args.Performer, AudioParams.Default.WithVolume(args.KnockVolume));
|
||||
|
||||
//Look for doors and don't open them if they're already open.
|
||||
foreach (var entity in _lookup.GetEntitiesInRange(coords, args.Range))
|
||||
{
|
||||
if (TryComp<DoorBoltComponent>(entity, out var bolts))
|
||||
@@ -329,9 +186,10 @@ public sealed class MagicSystem : EntitySystem
|
||||
return;
|
||||
|
||||
ev.Handled = true;
|
||||
|
||||
Speak(ev);
|
||||
|
||||
var direction = Transform(ev.Target).MapPosition.Position - Transform(ev.Performer).MapPosition.Position;
|
||||
var direction = _transformSystem.GetMapCoordinates(ev.Target).Position - _transformSystem.GetMapCoordinates(ev.Performer).Position;
|
||||
var impulseVector = direction * 10000;
|
||||
|
||||
_physics.ApplyLinearImpulse(ev.Target, impulseVector);
|
||||
@@ -339,28 +197,17 @@ public sealed class MagicSystem : EntitySystem
|
||||
if (!TryComp<BodyComponent>(ev.Target, out var body))
|
||||
return;
|
||||
|
||||
var ents = _bodySystem.GibBody(ev.Target, true, body);
|
||||
var entities = _bodySystem.GibBody(ev.Target, true, body);
|
||||
|
||||
if (!ev.DeleteNonBrainParts)
|
||||
return;
|
||||
|
||||
foreach (var part in ents)
|
||||
{
|
||||
// just leaves a brain and clothes
|
||||
if (HasComp<BodyComponent>(part) && !HasComp<BrainComponent>(part))
|
||||
foreach (var part in entities.Where(part => HasComp<BodyComponent>(part) && !HasComp<BrainComponent>(part)))
|
||||
{
|
||||
QueueDel(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns entity prototypes from a list within range of click.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It will offset mobs after the first mob based on the OffsetVector2 property supplied.
|
||||
/// </remarks>
|
||||
/// <param name="args"> The Spawn Spell Event args.</param>
|
||||
private void OnWorldSpawn(WorldSpawnSpellEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -373,24 +220,85 @@ public sealed class MagicSystem : EntitySystem
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loops through a supplied list of entity prototypes and spawns them
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If an offset of 0, 0 is supplied then the entities will all spawn on the same tile.
|
||||
/// Any other offset will spawn entities starting from the source Map Coordinates and will increment the supplied
|
||||
/// offset
|
||||
/// </remarks>
|
||||
/// <param name="entityEntries"> The list of Entities to spawn in</param>
|
||||
/// <param name="entityCoords"> Map Coordinates where the entities will spawn</param>
|
||||
/// <param name="lifetime"> Check to see if the entities should self delete</param>
|
||||
/// <param name="offsetVector2"> A Vector2 offset that the entities will spawn in</param>
|
||||
private void SpawnSpellHelper(List<EntitySpawnEntry> entityEntries, EntityCoordinates entityCoords, float? lifetime, Vector2 offsetVector2)
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
public List<EntityCoordinates> GetSpawnPositions(TransformComponent casterXform, MagicSpawnData data)
|
||||
{
|
||||
var getProtos = EntitySpawnCollection.GetSpawns(entityEntries, _random);
|
||||
return data switch
|
||||
{
|
||||
TargetCasterPos => GetCasterPosition(casterXform),
|
||||
TargetInFront => GetPositionsInFront(casterXform),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
public List<EntityCoordinates> GetCasterPosition(TransformComponent casterXform)
|
||||
{
|
||||
return new List<EntityCoordinates>(1) { casterXform.Coordinates };
|
||||
}
|
||||
|
||||
public List<EntityCoordinates> GetPositionsInFront(TransformComponent casterXform)
|
||||
{
|
||||
var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized());
|
||||
|
||||
if (!TryComp<MapGridComponent>(casterXform.GridUid, out var mapGrid) ||
|
||||
!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager))
|
||||
{
|
||||
return new List<EntityCoordinates>();
|
||||
}
|
||||
|
||||
var tileIndex = tileReference.Value.GridIndices;
|
||||
var coords = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex);
|
||||
|
||||
var directions = GetCardinalDirections(casterXform.LocalRotation.GetCardinalDir());
|
||||
var spawnPositions = new List<EntityCoordinates>(3);
|
||||
|
||||
foreach (var direction in directions)
|
||||
{
|
||||
var offset = GetOffsetForDirection(direction);
|
||||
var coordinates = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex + offset);
|
||||
spawnPositions.Add(coordinates);
|
||||
}
|
||||
|
||||
spawnPositions.Add(coords);
|
||||
return spawnPositions;
|
||||
}
|
||||
|
||||
public IEnumerable<Direction> GetCardinalDirections(Direction dir)
|
||||
{
|
||||
switch (dir)
|
||||
{
|
||||
case Direction.North:
|
||||
case Direction.South:
|
||||
return new[] { Direction.North, Direction.South };
|
||||
case Direction.East:
|
||||
case Direction.West:
|
||||
return new[] { Direction.East, Direction.West };
|
||||
default:
|
||||
return Array.Empty<Direction>();
|
||||
}
|
||||
}
|
||||
|
||||
public (int, int) GetOffsetForDirection(Direction direction)
|
||||
{
|
||||
return direction switch
|
||||
{
|
||||
Direction.North => (1, 0),
|
||||
Direction.South => (-1, 0),
|
||||
Direction.East => (0, 1),
|
||||
Direction.West => (0, -1),
|
||||
_ => (0, 0)
|
||||
};
|
||||
}
|
||||
|
||||
public void SpawnSpellHelper(List<EntitySpawnEntry> entityEntries, EntityCoordinates entityCoords, float? lifetime, Vector2 offsetVector2)
|
||||
{
|
||||
var getPrototypes = EntitySpawnCollection.GetSpawns(entityEntries, _random);
|
||||
|
||||
var offsetCoords = entityCoords;
|
||||
foreach (var proto in getProtos)
|
||||
foreach (var proto in getPrototypes)
|
||||
{
|
||||
// TODO: Share this code with instant because they're both doing similar things for positioning.
|
||||
var entity = Spawn(proto, offsetCoords);
|
||||
@@ -404,8 +312,6 @@ public sealed class MagicSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void Speak(BaseActionEvent args)
|
||||
{
|
||||
if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech))
|
||||
@@ -414,4 +320,6 @@ public sealed class MagicSystem : EntitySystem
|
||||
_chat.TrySendInGameICMessage(args.Performer, Loc.GetString(speak.Speech),
|
||||
InGameICChatType.Speak, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -84,9 +84,18 @@ namespace Content.Server.Power.EntitySystems
|
||||
while (query.MoveNext(out var uid, out var comp, out var batt))
|
||||
{
|
||||
if (!comp.AutoRecharge) continue;
|
||||
|
||||
if (comp.AutoRechargeRate > 0)
|
||||
{
|
||||
if (batt.IsFullyCharged) continue;
|
||||
SetCharge(uid, batt.CurrentCharge + comp.AutoRechargeRate * frameTime, batt);
|
||||
}
|
||||
if (comp.AutoRechargeRate < 0) //self discharging
|
||||
{
|
||||
if (batt.Charge == 0) continue;
|
||||
UseCharge(uid, -comp.AutoRechargeRate * frameTime, batt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -3,7 +3,6 @@ using Content.Server._White.AspectsSystem.Aspects;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Humanoid;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||
|
||||
@@ -32,12 +31,6 @@ public sealed partial class VocalComponent : Component
|
||||
[DataField("wilhelmProbability")]
|
||||
public float WilhelmProbability = 0.0002f;
|
||||
|
||||
[DataField("screamAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string ScreamAction = "ActionScream";
|
||||
|
||||
[DataField("screamActionEntity")]
|
||||
public EntityUid? ScreamActionEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Currently loaded emote sounds prototype, based on entity sex.
|
||||
/// Null if no valid prototype for entity sex was found.
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using Content.Server.Actions;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Speech;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
@@ -17,33 +15,13 @@ public sealed class VocalSystem : EntitySystem
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly ActionsSystem _actions = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<VocalComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<VocalComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<VocalComponent, SexChangedEvent>(OnSexChanged);
|
||||
SubscribeLocalEvent<VocalComponent, EmoteEvent>(OnEmote);
|
||||
SubscribeLocalEvent<VocalComponent, ScreamActionEvent>(OnScreamAction);
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, VocalComponent component, MapInitEvent args)
|
||||
{
|
||||
// try to add scream action when vocal comp added
|
||||
_actions.AddAction(uid, ref component.ScreamActionEntity, component.ScreamAction);
|
||||
LoadSounds(uid, component);
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, VocalComponent component, ComponentShutdown args)
|
||||
{
|
||||
// remove scream action when component removed
|
||||
if (component.ScreamActionEntity != null)
|
||||
{
|
||||
_actions.RemoveAction(uid, component.ScreamActionEntity);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSexChanged(EntityUid uid, VocalComponent component, SexChangedEvent args)
|
||||
@@ -67,15 +45,6 @@ public sealed class VocalSystem : EntitySystem
|
||||
args.Handled = _chat.TryPlayEmoteSound(uid, component.EmoteSounds, args.Emote);
|
||||
}
|
||||
|
||||
private void OnScreamAction(EntityUid uid, VocalComponent component, ScreamActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
_chat.TryEmoteWithChat(uid, component.ScreamId);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private bool TryPlayScreamSound(EntityUid uid, VocalComponent component)
|
||||
{
|
||||
if (_random.Prob(component.WilhelmProbability))
|
||||
|
||||
@@ -12,12 +12,12 @@ namespace Content.Server.Speech.Muting
|
||||
public sealed class MutingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<MutedComponent, SpeakAttemptEvent>(OnSpeakAttempt);
|
||||
SubscribeLocalEvent<MutedComponent, EmoteEvent>(OnEmote, before: new[] { typeof(VocalSystem) });
|
||||
SubscribeLocalEvent<MutedComponent, ScreamActionEvent>(OnScreamAction, before: new[] { typeof(VocalSystem) });
|
||||
}
|
||||
|
||||
private void OnEmote(EntityUid uid, MutedComponent component, ref EmoteEvent args)
|
||||
@@ -30,20 +30,6 @@ namespace Content.Server.Speech.Muting
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnScreamAction(EntityUid uid, MutedComponent component, ScreamActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (HasComp<MimePowersComponent>(uid))
|
||||
_popupSystem.PopupEntity(Loc.GetString("mime-cant-speak"), uid, uid);
|
||||
|
||||
else
|
||||
_popupSystem.PopupEntity(Loc.GetString("speech-muted"), uid, uid);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
||||
private void OnSpeakAttempt(EntityUid uid, MutedComponent component, SpeakAttemptEvent args)
|
||||
{
|
||||
// TODO something better than this.
|
||||
|
||||
@@ -323,7 +323,7 @@ namespace Content.Server.VendingMachines
|
||||
if (_accessReader.IsAllowed(sender, uid, accessReader) || HasComp<EmaggedComponent>(uid))
|
||||
return true;
|
||||
|
||||
Popup.PopupClient(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid, sender);
|
||||
Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid, sender);
|
||||
Deny(uid, vendComponent);
|
||||
return false;
|
||||
}
|
||||
@@ -352,7 +352,7 @@ namespace Content.Server.VendingMachines
|
||||
if (entry == null)
|
||||
{
|
||||
if (sender.HasValue)
|
||||
Popup.PopupClient(Loc.GetString("vending-machine-component-try-eject-invalid-item"), uid, sender.Value);
|
||||
Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-invalid-item"), uid, sender.Value);
|
||||
|
||||
|
||||
Deny(uid, vendComponent);
|
||||
@@ -362,7 +362,7 @@ namespace Content.Server.VendingMachines
|
||||
if (entry.Amount <= 0)
|
||||
{
|
||||
if (sender.HasValue)
|
||||
Popup.PopupClient(Loc.GetString("vending-machine-component-try-eject-out-of-stock"), uid, sender.Value);
|
||||
Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-out-of-stock"), uid, sender.Value);
|
||||
|
||||
Deny(uid, vendComponent);
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Xenoarchaeology.Equipment.Components;
|
||||
using Content.Server.Xenoarchaeology.XenoArtifacts;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Xenoarchaeology.Equipment.Systems;
|
||||
@@ -9,7 +9,7 @@ namespace Content.Server.Xenoarchaeology.Equipment.Systems;
|
||||
public sealed class NodeScannerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
@@ -36,7 +36,7 @@ public sealed class NodeScannerSystem : EntitySystem
|
||||
return;
|
||||
|
||||
// WD edit
|
||||
_popupSystem.PopupClient(Loc.GetString("node-scan-popup",
|
||||
_popupSystem.PopupEntity(Loc.GetString("node-scan-popup",
|
||||
("id", $"{artifact.CurrentNodeId}")), target, args.User);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Content.Server._White.ChangeTemperatureOnCollide;
|
||||
public sealed partial class ClothingTemperatureAdjustComponent : Component
|
||||
{
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Rate = 1f;
|
||||
public float Rate = 2f;
|
||||
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float TargetTemperature = 310.15f;
|
||||
|
||||
@@ -57,7 +57,7 @@ public sealed partial class CultSystem
|
||||
|
||||
if (comp.SelectedEmpowers.Count >= 1)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("verb-spell-create-too-much"), ent);
|
||||
_popupSystem.PopupEntity(Loc.GetString("verb-spell-create-too-much"), ent, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ public sealed partial class CultSystem
|
||||
{
|
||||
if (ent.Comp.SelectedEmpowers.Count == 0)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("verb-spell-remove-no-spells"), ent);
|
||||
_popupSystem.PopupEntity(Loc.GetString("verb-spell-remove-no-spells"), ent, ent);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -147,12 +147,12 @@ public sealed class CultistFactorySystem : EntitySystem
|
||||
if (args.IsAnchored)
|
||||
{
|
||||
_transform.Unanchor(target, xform);
|
||||
_popup.PopupClient(Loc.GetString("anchorable-unanchored"), uid, args.User);
|
||||
_popup.PopupEntity(Loc.GetString("anchorable-unanchored"), uid, args.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
_transform.AnchorEntity(target, xform);
|
||||
_popup.PopupClient(Loc.GetString("anchorable-anchored"), uid, args.User);
|
||||
_popup.PopupEntity(Loc.GetString("anchorable-anchored"), uid, args.User);
|
||||
}
|
||||
|
||||
_audio.PlayPvs("/Audio/Items/ratchet.ogg", uid);
|
||||
|
||||
@@ -69,6 +69,11 @@ public sealed class MindslaveSystem : SharedMindslaveSystem
|
||||
|
||||
private void OnMindslaveRemoved(Entity<SubdermalImplantComponent> ent, ref SubdermalImplantRemoved args)
|
||||
{
|
||||
if (!Tag.HasTag(ent.Owner, MindslaveTag))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp(args.Target, out MindSlaveComponent? mindslave))
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using Content.Shared.Eye;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Stealth;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
@@ -10,11 +12,10 @@ namespace Content.Server._White.IncorporealSystem;
|
||||
|
||||
public sealed class IncorporealSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly VisibilitySystem _visibilitySystem = default!;
|
||||
|
||||
[Dependency] private readonly SharedStealthSystem _stealth = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -41,6 +42,9 @@ public sealed class IncorporealSystem : EntitySystem
|
||||
_visibilitySystem.RefreshVisibility(uid);
|
||||
}
|
||||
|
||||
Spawn("EffectEmpPulse", Transform(uid).Coordinates);
|
||||
EnsureComp<StealthComponent>(uid);
|
||||
_stealth.SetVisibility(uid, -1);
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
}
|
||||
|
||||
@@ -50,8 +54,8 @@ public sealed class IncorporealSystem : EntitySystem
|
||||
{
|
||||
var fixture = fixtures.Fixtures.First();
|
||||
|
||||
_physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) (CollisionGroup.FlyingMobMask | CollisionGroup.GhostImpassable), fixtures);
|
||||
_physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, (int) CollisionGroup.FlyingMobLayer, fixtures);
|
||||
_physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) (CollisionGroup.MobMask | CollisionGroup.GhostImpassable), fixtures);
|
||||
_physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, (int) CollisionGroup.MobLayer, fixtures);
|
||||
}
|
||||
|
||||
if (TryComp<VisibilityComponent>(uid, out var visibility))
|
||||
@@ -62,6 +66,10 @@ public sealed class IncorporealSystem : EntitySystem
|
||||
}
|
||||
|
||||
component.MovementSpeedBuff = 1;
|
||||
|
||||
Spawn("EffectEmpPulse", Transform(uid).Coordinates);
|
||||
_stealth.SetVisibility(uid, 1);
|
||||
RemComp<StealthComponent>(uid);
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ public sealed class RandomDamageSystem : EntitySystem
|
||||
private void HandleHit(Entity<RandomDamageComponent> ent, ref MeleeHitEvent args)
|
||||
{
|
||||
var damage = _random.NextFloat() * ent.Comp.Max;
|
||||
if (args.Direction != null) // Heavy attack
|
||||
damage *= 0.7f;
|
||||
args.BonusDamage = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Slash"), damage);
|
||||
}
|
||||
}
|
||||
|
||||
183
Content.Server/_White/Wizard/Charging/ChargingSystem.cs
Normal file
183
Content.Server/_White/Wizard/Charging/ChargingSystem.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using Content.Shared._White.Wizard;
|
||||
using Content.Shared._White.Wizard.Charging;
|
||||
using Content.Shared.Follower;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server._White.Wizard.Charging;
|
||||
|
||||
public sealed class ChargingSystem : SharedChargingSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly FollowerSystem _followerSystem = default!;
|
||||
|
||||
private readonly Dictionary<EntityUid, List<EntityUid>> _charges = new();
|
||||
|
||||
private readonly Dictionary<EntityUid, EntityUid> _chargingLoops = new();
|
||||
private readonly Dictionary<EntityUid, EntityUid> _chargedLoop = new();
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeNetworkEvent<RequestSpellChargingAudio>(OnCharging);
|
||||
SubscribeNetworkEvent<RequestSpellChargedAudio>(OnCharged);
|
||||
SubscribeNetworkEvent<RequestAudioSpellStop>(OnStop);
|
||||
SubscribeLocalEvent<PlayerDetachedEvent>(OnDetach);
|
||||
|
||||
SubscribeNetworkEvent<AddWizardChargeEvent>(Add);
|
||||
SubscribeNetworkEvent<RemoveWizardChargeEvent>(Remove);
|
||||
}
|
||||
|
||||
#region Audio
|
||||
|
||||
private void OnCharging(RequestSpellChargingAudio msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var user = args.SenderSession?.AttachedEntity;
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
var shouldLoop = msg.Loop;
|
||||
var sound = msg.Sound;
|
||||
|
||||
if (!shouldLoop)
|
||||
{
|
||||
_audio.PlayPvs(sound, user.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_chargingLoops.TryGetValue(user.Value, out var currentStream))
|
||||
{
|
||||
_audio.Stop(currentStream);
|
||||
_chargingLoops.Remove(user.Value);
|
||||
}
|
||||
|
||||
var newStream = _audio.PlayPvs(sound, user.Value, AudioParams.Default.WithLoop(true));
|
||||
|
||||
if (newStream.HasValue)
|
||||
{
|
||||
_chargingLoops[user.Value] = newStream.Value.Entity;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCharged(RequestSpellChargedAudio msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var user = args.SenderSession?.AttachedEntity;
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
if (_chargingLoops.TryGetValue(user.Value, out var currentStream))
|
||||
{
|
||||
_audio.Stop(currentStream);
|
||||
_chargingLoops.Remove(user.Value);
|
||||
}
|
||||
|
||||
var shouldLoop = msg.Loop;
|
||||
var sound = msg.Sound;
|
||||
|
||||
if (!shouldLoop)
|
||||
{
|
||||
_audio.PlayPvs(sound, user.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_chargedLoop.TryGetValue(user.Value, out var chargedLoop))
|
||||
{
|
||||
_audio.Stop(chargedLoop);
|
||||
_chargedLoop.Remove(user.Value);
|
||||
}
|
||||
|
||||
var newStream = _audio.PlayPvs(sound, user.Value, AudioParams.Default.WithLoop(true));
|
||||
|
||||
if (newStream.HasValue)
|
||||
{
|
||||
_chargedLoop[user.Value] = newStream.Value.Entity;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStop(RequestAudioSpellStop msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var user = args.SenderSession?.AttachedEntity;
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
if (_chargingLoops.TryGetValue(user.Value, out var currentStream))
|
||||
{
|
||||
_audio.Stop(currentStream);
|
||||
_chargingLoops.Remove(user.Value);
|
||||
}
|
||||
|
||||
if (_chargedLoop.TryGetValue(user.Value, out var chargedLoop))
|
||||
{
|
||||
_audio.Stop(chargedLoop);
|
||||
_chargedLoop.Remove(user.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDetach(PlayerDetachedEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var user = msg.Entity;
|
||||
|
||||
if (_chargingLoops.TryGetValue(user, out var currentStream))
|
||||
{
|
||||
_audio.Stop(currentStream);
|
||||
_chargingLoops.Remove(user);
|
||||
}
|
||||
|
||||
if (_chargedLoop.TryGetValue(user, out var chargedLoop))
|
||||
{
|
||||
_audio.Stop(chargedLoop);
|
||||
_chargedLoop.Remove(user);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Charges
|
||||
|
||||
private void Add(AddWizardChargeEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity != null)
|
||||
AddCharge(args.SenderSession.AttachedEntity.Value, msg.ChargeProto);
|
||||
}
|
||||
|
||||
private void Remove(RemoveWizardChargeEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity != null)
|
||||
RemoveAllCharges(args.SenderSession.AttachedEntity.Value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
public void AddCharge(EntityUid uid, string msgChargeProto)
|
||||
{
|
||||
var itemEnt = Spawn(msgChargeProto, Transform(uid).Coordinates);
|
||||
_followerSystem.StartFollowingEntity(itemEnt, uid);
|
||||
|
||||
if (!_charges.ContainsKey(uid))
|
||||
{
|
||||
_charges[uid] = new List<EntityUid>();
|
||||
}
|
||||
|
||||
_charges[uid].Add(itemEnt);
|
||||
}
|
||||
|
||||
public void RemoveAllCharges(EntityUid uid)
|
||||
{
|
||||
if (!_charges.ContainsKey(uid))
|
||||
return;
|
||||
|
||||
foreach (var followerEnt in _charges[uid])
|
||||
{
|
||||
Del(followerEnt);
|
||||
}
|
||||
|
||||
_charges.Remove(uid);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server._White.Wizard.Magic.Amaterasu;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class AmaterasuComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Mobs;
|
||||
|
||||
namespace Content.Server._White.Wizard.Magic.Amaterasu;
|
||||
|
||||
public sealed class AmaterasuSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AmaterasuComponent, MobStateChangedEvent>(OnMobState);
|
||||
}
|
||||
|
||||
private void OnMobState(EntityUid uid, AmaterasuComponent component, MobStateChangedEvent args)
|
||||
{
|
||||
if (args.NewMobState is MobState.Critical or MobState.Dead)
|
||||
{
|
||||
if(!TryComp<FlammableComponent>(uid, out var flammable))
|
||||
return;
|
||||
|
||||
if (flammable.OnFire)
|
||||
{
|
||||
_bodySystem.GibBody(uid);
|
||||
return;
|
||||
}
|
||||
|
||||
RemComp<AmaterasuComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Content.Server._White.Wizard.Magic.Other;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class InstantRecallComponent : Component
|
||||
{
|
||||
public EntityUid? Item;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace Content.Server._White.Wizard.Magic.TeslaProjectile;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class TeslaProjectileComponent : Component {}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Content.Server.Lightning;
|
||||
using Content.Shared.Projectiles;
|
||||
|
||||
namespace Content.Server._White.Wizard.Magic.TeslaProjectile;
|
||||
|
||||
public sealed class TeslaProjectileSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly LightningSystem _lightning = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TeslaProjectileComponent, ProjectileHitEvent>(OnStartCollide);
|
||||
}
|
||||
|
||||
private void OnStartCollide(Entity<TeslaProjectileComponent> ent, ref ProjectileHitEvent args)
|
||||
{
|
||||
_lightning.ShootRandomLightnings(ent, 2, 4, arcDepth:2);
|
||||
}
|
||||
}
|
||||
701
Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs
Normal file
701
Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs
Normal file
@@ -0,0 +1,701 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server._White.IncorporealSystem;
|
||||
using Content.Server._White.Wizard.Magic.Amaterasu;
|
||||
using Content.Server._White.Wizard.Magic.Other;
|
||||
using Content.Server.Abilities.Mime;
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Emp;
|
||||
using Content.Server.Lightning;
|
||||
using Content.Server.Magic;
|
||||
using Content.Server.Singularity.EntitySystems;
|
||||
using Content.Server.Weapons.Ranged.Systems;
|
||||
using Content.Shared._White.Wizard;
|
||||
using Content.Shared._White.Wizard.Magic;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Cluwne;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.VirtualItem;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Magic;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server._White.Wizard.Magic;
|
||||
|
||||
public sealed class WizardSpellsSystem : EntitySystem
|
||||
{
|
||||
#region Dependencies
|
||||
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly GunSystem _gunSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly LightningSystem _lightning = default!;
|
||||
[Dependency] private readonly MagicSystem _magicSystem = default!;
|
||||
[Dependency] private readonly GravityWellSystem _gravityWell = default!;
|
||||
[Dependency] private readonly FlammableSystem _flammableSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly EmpSystem _empSystem = default!;
|
||||
|
||||
#endregion
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<InstantRecallSpellEvent>(OnInstantRecallSpell);
|
||||
SubscribeLocalEvent<MimeTouchSpellEvent>(OnMimeTouchSpell);
|
||||
SubscribeLocalEvent<BananaTouchSpellEvent>(OnBananaTouchSpell);
|
||||
SubscribeLocalEvent<CluwneCurseSpellEvent>(OnCluwneCurseSpell);
|
||||
SubscribeLocalEvent<EmpSpellEvent>(OnEmpSpell);
|
||||
SubscribeLocalEvent<EtherealJauntSpellEvent>(OnJauntSpell);
|
||||
SubscribeLocalEvent<BlinkSpellEvent>(OnBlinkSpell);
|
||||
SubscribeLocalEvent<ForceWallSpellEvent>(OnForcewallSpell);
|
||||
SubscribeLocalEvent<CardsSpellEvent>(OnCardsSpell);
|
||||
SubscribeLocalEvent<FireballSpellEvent>(OnFireballSpell);
|
||||
SubscribeLocalEvent<ForceSpellEvent>(OnForceSpell);
|
||||
SubscribeLocalEvent<ArcSpellEvent>(OnArcSpell);
|
||||
|
||||
SubscribeLocalEvent<MagicComponent, BeforeCastSpellEvent>(OnBeforeCastSpell);
|
||||
}
|
||||
|
||||
#region Instant Recall
|
||||
|
||||
private void OnInstantRecallSpell(InstantRecallSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
if (!TryComp<HandsComponent>(msg.Performer, out var handsComponent))
|
||||
return;
|
||||
|
||||
if (!TryComp<InstantRecallComponent>(msg.Action, out var recallComponent))
|
||||
{
|
||||
_popupSystem.PopupEntity("Что-то поломалось!", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handsComponent.ActiveHandEntity != null)
|
||||
{
|
||||
if (HasComp<VirtualItemComponent>(handsComponent.ActiveHandEntity.Value))
|
||||
{
|
||||
_popupSystem.PopupEntity("Не могу работать с этим!", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
recallComponent.Item = handsComponent.ActiveHandEntity.Value;
|
||||
_popupSystem.PopupEntity($"Сопряжено с {MetaData(handsComponent.ActiveHandEntity.Value).EntityName}", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handsComponent.ActiveHandEntity == null && recallComponent.Item != null)
|
||||
{
|
||||
var coordsItem = Transform(recallComponent.Item.Value).Coordinates;
|
||||
var coordsPerformer = Transform(msg.Performer).Coordinates;
|
||||
|
||||
Spawn("EffectEmpPulse", coordsItem);
|
||||
|
||||
_transformSystem.SetCoordinates(recallComponent.Item.Value, coordsPerformer);
|
||||
_transformSystem.AttachToGridOrMap(recallComponent.Item.Value);
|
||||
|
||||
_handsSystem.TryForcePickupAnyHand(msg.Performer, recallComponent.Item.Value);
|
||||
|
||||
msg.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_popupSystem.PopupEntity("Нет привязки.", msg.Performer, msg.Performer);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mime Touch
|
||||
|
||||
private void OnMimeTouchSpell(MimeTouchSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
if (!HasComp<HumanoidAppearanceComponent>(msg.Target))
|
||||
{
|
||||
_popupSystem.PopupEntity("Работает только на людях!", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
SetOutfitCommand.SetOutfit(msg.Target, "MimeGear", EntityManager);
|
||||
EnsureComp<MimePowersComponent>(msg.Target);
|
||||
|
||||
Spawn("AdminInstantEffectSmoke3", Transform(msg.Target).Coordinates);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Banana Touch
|
||||
|
||||
private void OnBananaTouchSpell(BananaTouchSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
if (!HasComp<HumanoidAppearanceComponent>(msg.Target))
|
||||
{
|
||||
_popupSystem.PopupEntity("Работает только на людях!", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
SetOutfitCommand.SetOutfit(msg.Target, "ClownGear", EntityManager);
|
||||
EnsureComp<ClumsyComponent>(msg.Target);
|
||||
|
||||
Spawn("AdminInstantEffectSmoke3", Transform(msg.Target).Coordinates);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cluwne Curse
|
||||
|
||||
private void OnCluwneCurseSpell(CluwneCurseSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
if (!HasComp<HumanoidAppearanceComponent>(msg.Target))
|
||||
{
|
||||
_popupSystem.PopupEntity("Работает только на людях!", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureComp<CluwneComponent>(msg.Target);
|
||||
|
||||
Spawn("AdminInstantEffectSmoke3", Transform(msg.Target).Coordinates);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EMP
|
||||
|
||||
private void OnEmpSpell(EmpSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
var coords = _transformSystem.ToMapCoordinates(Transform(msg.Performer).Coordinates);
|
||||
|
||||
_empSystem.EmpPulse(coords, 15, 1000000, 60f);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ethereal Jaunt
|
||||
|
||||
private void OnJauntSpell(EtherealJauntSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
if (_statusEffectsSystem.HasStatusEffect(msg.Performer, "Incorporeal"))
|
||||
{
|
||||
_popupSystem.PopupEntity("Вы уже в потустороннем мире", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
Spawn("AdminInstantEffectSmoke10", Transform(msg.Performer).Coordinates);
|
||||
|
||||
_statusEffectsSystem.TryAddStatusEffect<IncorporealComponent>(msg.Performer, "Incorporeal", TimeSpan.FromSeconds(10), false);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Blink
|
||||
|
||||
private void OnBlinkSpell(BlinkSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
var transform = Transform(msg.Performer);
|
||||
|
||||
var oldCoords = transform.Coordinates;
|
||||
|
||||
EntityCoordinates coords = default;
|
||||
var foundTeleportPos = false;
|
||||
var attempts = 10;
|
||||
|
||||
while (attempts > 0)
|
||||
{
|
||||
attempts--;
|
||||
|
||||
var random = new Random().Next(10, 20);
|
||||
var offset = transform.LocalRotation.ToWorldVec().Normalized();
|
||||
var direction = transform.LocalRotation.GetDir().ToVec();
|
||||
var newOffset = offset + direction * random;
|
||||
coords = transform.Coordinates.Offset(newOffset).SnapToGrid(EntityManager);
|
||||
|
||||
var tile = coords.GetTileRef();
|
||||
|
||||
if (tile != null && _turf.IsTileBlocked(tile.Value, CollisionGroup.AllMask))
|
||||
continue;
|
||||
|
||||
foundTeleportPos = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundTeleportPos)
|
||||
return;
|
||||
|
||||
_transformSystem.SetCoordinates(msg.Performer, coords);
|
||||
_transformSystem.AttachToGridOrMap(msg.Performer);
|
||||
|
||||
_audio.PlayPvs("/Audio/White/Cult/veilin.ogg", coords);
|
||||
_audio.PlayPvs("/Audio/White/Cult/veilout.ogg", oldCoords);
|
||||
|
||||
Spawn("AdminInstantEffectSmoke10", oldCoords);
|
||||
Spawn("AdminInstantEffectSmoke10", coords);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Forcewall
|
||||
|
||||
private void OnForcewallSpell(ForceWallSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
switch (msg.ActionUseType)
|
||||
{
|
||||
case ActionUseType.Default:
|
||||
ForcewallSpellDefault(msg);
|
||||
break;
|
||||
case ActionUseType.Charge:
|
||||
ForcewallSpellCharge(msg);
|
||||
break;
|
||||
case ActionUseType.AltUse:
|
||||
ForcewallSpellAlt(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
private void ForcewallSpellDefault(ForceWallSpellEvent msg)
|
||||
{
|
||||
var transform = Transform(msg.Performer);
|
||||
|
||||
foreach (var position in _magicSystem.GetPositionsInFront(transform))
|
||||
{
|
||||
var ent = Spawn(msg.Prototype, position.SnapToGrid(EntityManager, _mapManager));
|
||||
|
||||
var comp = EnsureComp<PreventCollideComponent>(ent);
|
||||
comp.Uid = msg.Performer;
|
||||
}
|
||||
}
|
||||
|
||||
private void ForcewallSpellCharge(ForceWallSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.Performer);
|
||||
|
||||
var positions = GetArenaPositions(xform, msg.ChargeLevel);
|
||||
|
||||
foreach (var position in positions)
|
||||
{
|
||||
var ent = Spawn(msg.Prototype, position);
|
||||
|
||||
var comp = EnsureComp<PreventCollideComponent>(ent);
|
||||
comp.Uid = msg.Performer;
|
||||
}
|
||||
}
|
||||
|
||||
private void ForcewallSpellAlt(ForceWallSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.TargetUid);
|
||||
|
||||
var positions = GetArenaPositions(xform, 2);
|
||||
|
||||
foreach (var direction in positions)
|
||||
{
|
||||
var ent = Spawn(msg.Prototype, direction);
|
||||
|
||||
var comp = EnsureComp<PreventCollideComponent>(ent);
|
||||
comp.Uid = msg.Performer;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cards
|
||||
|
||||
private void OnCardsSpell(CardsSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
switch (msg.ActionUseType)
|
||||
{
|
||||
case ActionUseType.Default:
|
||||
CardsSpellDefault(msg);
|
||||
break;
|
||||
case ActionUseType.Charge:
|
||||
CardsSpellCharge(msg);
|
||||
break;
|
||||
case ActionUseType.AltUse:
|
||||
CardsSpellAlt(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
private void CardsSpellDefault(CardsSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.Performer);
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
foreach (var pos in _magicSystem.GetSpawnPositions(xform, msg.Pos))
|
||||
{
|
||||
var mapPos = _transformSystem.ToMapCoordinates(pos);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out _)
|
||||
? pos.WithEntityId(gridUid, EntityManager)
|
||||
: new EntityCoordinates(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
|
||||
var ent = Spawn(msg.Prototype, spawnCoords);
|
||||
|
||||
var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - spawnCoords.ToMapPos(EntityManager, _transformSystem);
|
||||
var randomizedDirection = direction + new Vector2(_random.Next(-2, 2), _random.Next(-2, 2));
|
||||
|
||||
_throwingSystem.TryThrow(ent, randomizedDirection, 60, msg.Performer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CardsSpellCharge(CardsSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.Performer);
|
||||
|
||||
var count = 5 * msg.ChargeLevel;
|
||||
var angleStep = 360f / count;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var angle = i * angleStep;
|
||||
|
||||
var direction = new Vector2(MathF.Cos(MathHelper.DegreesToRadians(angle)), MathF.Sin(MathHelper.DegreesToRadians(angle)));
|
||||
|
||||
foreach (var pos in _magicSystem.GetSpawnPositions(xform, msg.Pos))
|
||||
{
|
||||
var mapPos = _transformSystem.ToMapCoordinates(pos);
|
||||
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out _)
|
||||
? pos.WithEntityId(gridUid, EntityManager)
|
||||
: new EntityCoordinates(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
|
||||
var ent = Spawn(msg.Prototype, spawnCoords);
|
||||
|
||||
_throwingSystem.TryThrow(ent, direction, 60, msg.Performer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CardsSpellAlt(CardsSpellEvent msg)
|
||||
{
|
||||
if (!HasComp<ItemComponent>(msg.TargetUid))
|
||||
return;
|
||||
|
||||
Del(msg.TargetUid);
|
||||
var item = Spawn(msg.Prototype);
|
||||
_handsSystem.TryPickupAnyHand(msg.Performer, item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fireball
|
||||
|
||||
private void OnFireballSpell(FireballSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
switch (msg.ActionUseType)
|
||||
{
|
||||
case ActionUseType.Default:
|
||||
FireballSpellDefault(msg);
|
||||
break;
|
||||
case ActionUseType.Charge:
|
||||
FireballSpellCharge(msg);
|
||||
break;
|
||||
case ActionUseType.AltUse:
|
||||
FireballSpellAlt(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
private void FireballSpellDefault(FireballSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.Performer);
|
||||
|
||||
foreach (var pos in _magicSystem.GetSpawnPositions(xform, msg.Pos))
|
||||
{
|
||||
var mapPos = _transformSystem.ToMapCoordinates(pos);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out var grid)
|
||||
? pos.WithEntityId(gridUid, EntityManager)
|
||||
: new EntityCoordinates(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
|
||||
var userVelocity = Vector2.Zero;
|
||||
|
||||
if (grid != null && TryComp(gridUid, out PhysicsComponent? physics))
|
||||
userVelocity = physics.LinearVelocity;
|
||||
|
||||
var ent = Spawn(msg.Prototype, spawnCoords);
|
||||
var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - spawnCoords.ToMapPos(EntityManager, _transformSystem);
|
||||
_gunSystem.ShootProjectile(ent, direction, userVelocity, msg.Performer, msg.Performer);
|
||||
}
|
||||
}
|
||||
|
||||
private void FireballSpellCharge(FireballSpellEvent msg)
|
||||
{
|
||||
var coords = Transform(msg.Performer).Coordinates;
|
||||
|
||||
var targets = _lookup.GetEntitiesInRange<FlammableComponent>(coords, 2 * msg.ChargeLevel);
|
||||
|
||||
foreach (var target in targets.Where(target => target.Owner != msg.Performer))
|
||||
{
|
||||
target.Comp.FireStacks += 3;
|
||||
_flammableSystem.Ignite(target, msg.Performer);
|
||||
}
|
||||
}
|
||||
|
||||
private void FireballSpellAlt(FireballSpellEvent msg)
|
||||
{
|
||||
if (!TryComp<FlammableComponent>(msg.TargetUid, out var flammableComponent))
|
||||
return;
|
||||
|
||||
flammableComponent.FireStacks += 4;
|
||||
|
||||
_flammableSystem.Ignite(msg.TargetUid, msg.Performer);
|
||||
|
||||
EnsureComp<AmaterasuComponent>(msg.TargetUid);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Force
|
||||
|
||||
private void OnForceSpell(ForceSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
switch (msg.ActionUseType)
|
||||
{
|
||||
case ActionUseType.Default:
|
||||
ForceSpellDefault(msg);
|
||||
break;
|
||||
case ActionUseType.Charge:
|
||||
ForceSpellCharge(msg);
|
||||
break;
|
||||
case ActionUseType.AltUse:
|
||||
ForceSpellAlt(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
private void ForceSpellDefault(ForceSpellEvent msg)
|
||||
{
|
||||
Spawn("AdminInstantEffectMinusGravityWell", msg.Target);
|
||||
}
|
||||
|
||||
private void ForceSpellCharge(ForceSpellEvent msg)
|
||||
{
|
||||
_gravityWell.GravPulse(msg.Performer, 15, 0, -80 * msg.ChargeLevel, -2 * msg.ChargeLevel);
|
||||
}
|
||||
|
||||
private void ForceSpellAlt(ForceSpellEvent msg)
|
||||
{
|
||||
_gravityWell.GravPulse(msg.Target, 10, 0, 200, 10);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Arc
|
||||
|
||||
private void OnArcSpell(ArcSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
switch (msg.ActionUseType)
|
||||
{
|
||||
case ActionUseType.Default:
|
||||
ArcSpellDefault(msg);
|
||||
break;
|
||||
case ActionUseType.Charge:
|
||||
ArcSpellCharge(msg);
|
||||
break;
|
||||
case ActionUseType.AltUse:
|
||||
ArcSpellAlt(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
private void ArcSpellDefault(ArcSpellEvent msg)
|
||||
{
|
||||
const int possibleEntitiesCount = 2;
|
||||
|
||||
var entitiesInRange = _lookup.GetEntitiesInRange(msg.Target, 1);
|
||||
var entitiesToHit = entitiesInRange.Where(HasComp<MobStateComponent>).Take(possibleEntitiesCount);
|
||||
|
||||
foreach (var entity in entitiesToHit)
|
||||
{
|
||||
_lightning.ShootLightning(msg.Performer, entity);
|
||||
}
|
||||
}
|
||||
|
||||
private void ArcSpellCharge(ArcSpellEvent msg)
|
||||
{
|
||||
_lightning.ShootRandomLightnings(msg.Performer, 2 * msg.ChargeLevel, msg.ChargeLevel * 2, arcDepth: 2);
|
||||
}
|
||||
|
||||
private void ArcSpellAlt(ArcSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.Performer);
|
||||
|
||||
foreach (var pos in _magicSystem.GetSpawnPositions(xform, msg.Pos))
|
||||
{
|
||||
var mapPos = _transformSystem.ToMapCoordinates(pos);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out var grid)
|
||||
? pos.WithEntityId(gridUid, EntityManager)
|
||||
: new EntityCoordinates(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
|
||||
var userVelocity = Vector2.Zero;
|
||||
|
||||
if (grid != null && TryComp(gridUid, out PhysicsComponent? physics))
|
||||
userVelocity = physics.LinearVelocity;
|
||||
|
||||
var ent = Spawn(msg.Prototype, spawnCoords);
|
||||
var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - spawnCoords.ToMapPos(EntityManager, _transformSystem);
|
||||
_gunSystem.ShootProjectile(ent, direction, userVelocity, msg.Performer, msg.Performer);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
private void Speak(BaseActionEvent args)
|
||||
{
|
||||
if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech))
|
||||
return;
|
||||
|
||||
_chat.TrySendInGameICMessage(args.Performer, Loc.GetString(speak.Speech),
|
||||
InGameICChatType.Speak, false);
|
||||
}
|
||||
|
||||
private List<EntityCoordinates> GetArenaPositions(TransformComponent casterXform, int arenaSize)
|
||||
{
|
||||
var positions = new List<EntityCoordinates>();
|
||||
|
||||
arenaSize--;
|
||||
|
||||
for (var i = -arenaSize; i <= arenaSize; i++)
|
||||
{
|
||||
for (var j = -arenaSize; j <= arenaSize; j++)
|
||||
{
|
||||
var position = new Vector2(i, j);
|
||||
var coordinates = casterXform.Coordinates.Offset(position);
|
||||
positions.Add(coordinates);
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
private bool CheckRequirements(EntityUid spell, EntityUid performer)
|
||||
{
|
||||
var ev = new BeforeCastSpellEvent(performer);
|
||||
RaiseLocalEvent(spell, ref ev);
|
||||
return !ev.Cancelled;
|
||||
}
|
||||
|
||||
private void OnBeforeCastSpell(Entity<MagicComponent> ent, ref BeforeCastSpellEvent args)
|
||||
{
|
||||
var comp = ent.Comp;
|
||||
var hasReqs = false;
|
||||
|
||||
if (comp.RequiresClothes)
|
||||
{
|
||||
var enumerator = _inventory.GetSlotEnumerator(args.Performer, SlotFlags.OUTERCLOTHING | SlotFlags.HEAD);
|
||||
while (enumerator.MoveNext(out var containerSlot))
|
||||
{
|
||||
if (containerSlot.ContainedEntity is { } item)
|
||||
hasReqs = HasComp<WizardClothesComponent>(item);
|
||||
else
|
||||
hasReqs = false;
|
||||
|
||||
if (!hasReqs)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasReqs)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
_popupSystem.PopupEntity("Missing Requirements! You need to wear your robe and hat!", args.Performer, args.Performer);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
8
Content.Server/_White/Wizard/Scrolls/ScrollSystem.cs
Normal file
8
Content.Server/_White/Wizard/Scrolls/ScrollSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared._White.Wizard.ScrollSystem;
|
||||
|
||||
namespace Content.Server._White.Wizard.Scrolls;
|
||||
|
||||
public sealed class ScrollSystem : SharedScrollSystem
|
||||
{
|
||||
protected override void BurnScroll(EntityUid uid) => Del(uid);
|
||||
}
|
||||
@@ -81,8 +81,10 @@ public sealed class GetItemActionsEvent : EntityEventArgs
|
||||
public sealed class RequestPerformActionEvent : EntityEventArgs
|
||||
{
|
||||
public readonly NetEntity Action;
|
||||
public readonly NetEntity? EntityTarget;
|
||||
public NetEntity? EntityTarget;
|
||||
public readonly NetCoordinates? EntityCoordinatesTarget;
|
||||
public ActionUseType ActionUseType = ActionUseType.Default;
|
||||
public int ChargeLevel;
|
||||
|
||||
public RequestPerformActionEvent(NetEntity action)
|
||||
{
|
||||
@@ -148,6 +150,8 @@ public abstract partial class WorldTargetActionEvent : BaseActionEvent
|
||||
/// The coordinates of the location that the user targeted.
|
||||
/// </summary>
|
||||
public EntityCoordinates Target;
|
||||
|
||||
public EntityUid TargetUid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -161,4 +165,18 @@ public abstract partial class BaseActionEvent : HandledEntityEventArgs
|
||||
/// The user performing the action.
|
||||
/// </summary>
|
||||
public EntityUid Performer;
|
||||
|
||||
public EntityUid Action;
|
||||
|
||||
public ActionUseType ActionUseType = ActionUseType.Default;
|
||||
|
||||
public int ChargeLevel;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ActionUseType
|
||||
{
|
||||
Default, // left mouse click.
|
||||
Charge, // Holding right mouse click(has 4 charges).
|
||||
AltUse // Alt + left mouse click.
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Shared.Actions;
|
||||
|
||||
@@ -40,4 +41,28 @@ public abstract partial class BaseTargetActionComponent : BaseActionComponent
|
||||
/// over lay in place of the currently held item "held item".
|
||||
/// </summary>
|
||||
[DataField("targetingIndicator")] public bool TargetingIndicator = true;
|
||||
|
||||
[DataField]
|
||||
public bool IsAltEnabled;
|
||||
|
||||
[DataField]
|
||||
public bool IsChargeEnabled;
|
||||
|
||||
[DataField]
|
||||
public string ChargeProto = "MagicFollowerEntity";
|
||||
|
||||
[DataField]
|
||||
public int MaxChargeLevel = 4;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier ChargingSound = new SoundPathSpecifier("/Audio/White/Magic/chargingfallback.ogg");
|
||||
|
||||
[DataField]
|
||||
public bool LoopCharging = true;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier MaxChargedSound = new SoundPathSpecifier("/Audio/White/Magic/maxchargefallback.ogg");
|
||||
|
||||
[DataField]
|
||||
public bool LoopMaxCharged;
|
||||
}
|
||||
|
||||
@@ -413,6 +413,8 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
if (worldAction.Event != null)
|
||||
{
|
||||
worldAction.Event.Target = entityCoordinatesTarget;
|
||||
if (ev.EntityTarget != null)
|
||||
worldAction.Event.TargetUid = GetEntity(ev.EntityTarget.Value);
|
||||
Dirty(actionEnt, worldAction);
|
||||
performEvent = worldAction.Event;
|
||||
}
|
||||
@@ -430,7 +432,12 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
}
|
||||
|
||||
if (performEvent != null)
|
||||
{
|
||||
performEvent.Performer = user;
|
||||
performEvent.Action = actionEnt;
|
||||
performEvent.ActionUseType = ev.ActionUseType;
|
||||
performEvent.ChargeLevel = ev.ChargeLevel;
|
||||
}
|
||||
|
||||
// All checks passed. Perform the action!
|
||||
PerformAction(user, component, actionEnt, action, performEvent, curTime);
|
||||
@@ -677,6 +684,8 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
/// <param name="performer">Entity to receive the actions</param>
|
||||
/// <param name="actions">The actions to add</param>
|
||||
/// <param name="container">The entity that enables these actions (e.g., flashlight). May be null (innate actions).</param>
|
||||
/// <param name="comp">ActionsComponent.</param>
|
||||
/// <param name="containerComp">ActionContainerComponent.</param>
|
||||
public void GrantActions(EntityUid performer, IEnumerable<EntityUid> actions, EntityUid container, ActionsComponent? comp = null, ActionsContainerComponent? containerComp = null)
|
||||
{
|
||||
if (!Resolve(container, ref containerComp))
|
||||
|
||||
@@ -22,10 +22,4 @@ public sealed partial class SleepEmitSoundComponent : Component
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Chance = 0.33f;
|
||||
|
||||
/// <summary>
|
||||
/// Popup for snore (e.g. Zzz...)
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public LocId PopUp = "sleep-onomatopoeia";
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ public sealed partial class StaminaDamageOnCollideComponent : Component
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("damage")]
|
||||
public float Damage = 55f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField]
|
||||
public bool IgnoreResistances = true;
|
||||
|
||||
[DataField("sound")]
|
||||
public SoundSpecifier? Sound;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Shared._White.StaminaProtection;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.CombatMode;
|
||||
@@ -204,7 +205,18 @@ public sealed partial class StaminaSystem : EntitySystem
|
||||
if (ev.Cancelled)
|
||||
return;
|
||||
|
||||
TakeStaminaDamage(target, component.Damage, source: uid, sound: component.Sound);
|
||||
// WD EDIT START
|
||||
var damage = component.Damage;
|
||||
|
||||
if (!component.IgnoreResistances)
|
||||
{
|
||||
var modifyEv = new StaminaDamageModifyEvent {Damage = damage};
|
||||
RaiseLocalEvent(target, modifyEv);
|
||||
damage = modifyEv.Damage;
|
||||
}
|
||||
|
||||
TakeStaminaDamage(target, damage, source: uid, sound: component.Sound);
|
||||
// WD EDIT END
|
||||
}
|
||||
|
||||
private void SetStaminaAlert(EntityUid uid, StaminaComponent? component = null)
|
||||
|
||||
@@ -57,6 +57,11 @@ public sealed partial class ImplanterComponent : Component
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool ImplantOnly;
|
||||
|
||||
// WD START
|
||||
[DataField]
|
||||
public bool SingleUse = true;
|
||||
// WD END
|
||||
|
||||
/// <summary>
|
||||
/// The current mode of the implanter
|
||||
/// Mode is changed automatically depending if it implants or draws
|
||||
|
||||
@@ -117,6 +117,9 @@ public abstract class SharedImplanterSystem : EntitySystem
|
||||
_container.Insert(implant.Value, implantContainer);
|
||||
RaiseLocalEvent(implant.Value, new SubdermalImplantInserted(user, target, implantComp)); //WD EDIT
|
||||
|
||||
if (component.CurrentMode == ImplanterToggleMode.Inject && component.SingleUse) // WD EDIT
|
||||
component.ImplantOnly = true;
|
||||
|
||||
if (component.CurrentMode == ImplanterToggleMode.Inject && !component.ImplantOnly)
|
||||
DrawMode(implanter, component);
|
||||
else
|
||||
@@ -205,7 +208,8 @@ public abstract class SharedImplanterSystem : EntitySystem
|
||||
break;
|
||||
}
|
||||
|
||||
if (component.CurrentMode == ImplanterToggleMode.Draw && !component.ImplantOnly && !permanentFound)
|
||||
if (component.CurrentMode == ImplanterToggleMode.Draw && !component.ImplantOnly && !permanentFound &&
|
||||
implanterContainer.Count > 0) // WD EDIT
|
||||
ImplantMode(implanter, component);
|
||||
|
||||
Dirty(implanter, component);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared._White.StaminaProtection;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Electrocution;
|
||||
@@ -27,6 +28,7 @@ public partial class InventorySystem
|
||||
SubscribeLocalEvent<InventoryComponent, SeeIdentityAttemptEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, AdjustTemperatureEvent>(RelayInventoryEvent); // WD
|
||||
SubscribeLocalEvent<InventoryComponent, StaminaDamageModifyEvent>(RelayInventoryEvent); // WD
|
||||
SubscribeLocalEvent<InventoryComponent, GetDefaultRadioChannelEvent>(RelayInventoryEvent);
|
||||
|
||||
// by-ref events
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared.Speech;
|
||||
|
||||
public sealed partial class ScreamActionEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
@@ -123,6 +123,14 @@ public abstract partial class SharedGunSystem
|
||||
|
||||
private void OnGunSelected(EntityUid uid, GunComponent component, HandSelectedEvent args)
|
||||
{
|
||||
// WD EDIT START
|
||||
if (component.FireRateModified <= 0f)
|
||||
component.FireRateModified = component.FireRate;
|
||||
|
||||
if (component.FireRateModified <= 0f)
|
||||
return;
|
||||
// WD EDIT END
|
||||
|
||||
var fireDelay = 1f / component.FireRateModified;
|
||||
if (fireDelay.Equals(0f))
|
||||
return;
|
||||
|
||||
@@ -48,5 +48,10 @@ public sealed class BackstabSystem : EntitySystem
|
||||
|
||||
args.PenetrateArmor = ent.Comp.PenetrateArmor;
|
||||
|
||||
if (!_net.IsServer)
|
||||
return;
|
||||
|
||||
var message = Loc.GetString("backstab-damage-betrayal-dagger", ("damage", damage));
|
||||
_popup.PopupClient(message, args.User, args.User, PopupType.MediumCaution);
|
||||
}
|
||||
}
|
||||
|
||||
23
Content.Shared/_White/Chemistry/HasComponentCondition.cs
Normal file
23
Content.Shared/_White/Chemistry/HasComponentCondition.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._White.Chemistry;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed partial class HasComponent : ReagentEffectCondition
|
||||
{
|
||||
[DataField]
|
||||
public string Component = default!;
|
||||
|
||||
public override bool Condition(ReagentEffectArgs args)
|
||||
{
|
||||
return args.EntityManager.HasComponent(args.SolutionEntity,
|
||||
args.EntityManager.ComponentFactory.GetRegistration(Component).Type);
|
||||
}
|
||||
|
||||
public override string GuidebookExplanation(IPrototypeManager prototype)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Content.Shared.Armor;
|
||||
using Content.Shared.Inventory;
|
||||
|
||||
namespace Content.Shared._White.StaminaProtection;
|
||||
|
||||
public sealed class StaminaProtectionSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ArmorComponent, InventoryRelayedEvent<StaminaDamageModifyEvent>>(OnDamageModify);
|
||||
}
|
||||
|
||||
private void OnDamageModify(Entity<ArmorComponent> ent, ref InventoryRelayedEvent<StaminaDamageModifyEvent> args)
|
||||
{
|
||||
var modifiers = ent.Comp.Modifiers;
|
||||
|
||||
if (modifiers.FlatReduction.TryGetValue("Blunt", out var flat))
|
||||
args.Args.Damage = MathF.Max(0f, args.Args.Damage - flat);
|
||||
|
||||
if (modifiers.Coefficients.TryGetValue("Blunt", out var coefficient))
|
||||
args.Args.Damage *= coefficient;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class StaminaDamageModifyEvent : EntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
public SlotFlags TargetSlots => ~SlotFlags.POCKET;
|
||||
|
||||
public float Damage;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Content.Shared._White.Wizard.Charging;
|
||||
|
||||
public abstract class SharedChargingSystem : EntitySystem
|
||||
{
|
||||
}
|
||||
13
Content.Shared/_White/Wizard/Magic/MagicComponent.cs
Normal file
13
Content.Shared/_White/Wizard/Magic/MagicComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Wizard.Magic;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class MagicComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Does this spell require Wizard Robes & Hat?
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool RequiresClothes;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Wizard.Magic;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class WizardClothesComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
43
Content.Shared/_White/Wizard/ScrollSystem/ScrollComponent.cs
Normal file
43
Content.Shared/_White/Wizard/ScrollSystem/ScrollComponent.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Wizard.ScrollSystem;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedScrollSystem))]
|
||||
public sealed partial class ScrollComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// ActionId to give on use.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables]
|
||||
public string ActionId;
|
||||
|
||||
/// <summary>
|
||||
/// How time it takes to learn.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float LearnTime = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// Popup on learn.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables]
|
||||
public string LearnPopup;
|
||||
|
||||
/// <summary>
|
||||
/// Sound to play on use.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables]
|
||||
public SoundSpecifier UseSound;
|
||||
|
||||
/// <summary>
|
||||
/// Sound to play after use.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables]
|
||||
public SoundSpecifier AfterUseSound;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Shared._White.Wizard.ScrollSystem;
|
||||
|
||||
public abstract class SharedScrollSystem : EntitySystem
|
||||
{
|
||||
#region Dependencies
|
||||
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ScrollComponent, UseInHandEvent>(OnScrollUse);
|
||||
SubscribeLocalEvent<ScrollComponent, ScrollDoAfterEvent>(OnScrollDoAfter);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Handlers
|
||||
|
||||
private void OnScrollUse(EntityUid uid, ScrollComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.LearnTime, new ScrollDoAfterEvent(), uid, target: uid)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
NeedHand = true
|
||||
};
|
||||
|
||||
if (_net.IsServer)
|
||||
{
|
||||
_audioSystem.PlayPvs(component.UseSound, args.User);
|
||||
}
|
||||
|
||||
_popupSystem.PopupClient($"You start learning about {component.LearnPopup}.", args.User, args.User, PopupType.Medium);
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(doAfterEventArgs);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnScrollDoAfter(EntityUid uid, ScrollComponent component, ScrollDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled)
|
||||
return;
|
||||
|
||||
_actionsSystem.AddAction(args.User, component.ActionId);
|
||||
|
||||
if (_net.IsServer)
|
||||
{
|
||||
_audioSystem.PlayEntity(component.AfterUseSound, args.User, args.User);
|
||||
}
|
||||
|
||||
_popupSystem.PopupClient($"You learned much about {component.LearnPopup}. The scroll is slowly burning in your hands.", args.User, args.User, PopupType.Medium);
|
||||
|
||||
BurnScroll(uid);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
protected virtual void BurnScroll(EntityUid uid) {}
|
||||
|
||||
#endregion
|
||||
}
|
||||
175
Content.Shared/_White/Wizard/WizardEvents.cs
Normal file
175
Content.Shared/_White/Wizard/WizardEvents.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Magic;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared._White.Wizard;
|
||||
|
||||
#region HelperEvents
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class ScrollDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public struct BeforeCastSpellEvent
|
||||
{
|
||||
public EntityUid Performer;
|
||||
|
||||
public bool Cancelled;
|
||||
|
||||
public BeforeCastSpellEvent(EntityUid performer)
|
||||
{
|
||||
Performer = performer;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class AddWizardChargeEvent : EntityEventArgs
|
||||
{
|
||||
public string ChargeProto;
|
||||
|
||||
public AddWizardChargeEvent(string chargeProto)
|
||||
{
|
||||
ChargeProto = chargeProto;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class RemoveWizardChargeEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class RequestSpellChargingAudio : EntityEventArgs
|
||||
{
|
||||
public SoundSpecifier Sound;
|
||||
public bool Loop;
|
||||
|
||||
public RequestSpellChargingAudio(SoundSpecifier sound, bool loop)
|
||||
{
|
||||
Sound = sound;
|
||||
Loop = loop;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class RequestSpellChargedAudio : EntityEventArgs
|
||||
{
|
||||
public SoundSpecifier Sound;
|
||||
public bool Loop;
|
||||
|
||||
public RequestSpellChargedAudio(SoundSpecifier sound, bool loop)
|
||||
{
|
||||
Sound = sound;
|
||||
Loop = loop;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class RequestAudioSpellStop : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Spells
|
||||
|
||||
public sealed partial class ArcSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype = default!;
|
||||
|
||||
[DataField("posData")]
|
||||
public MagicSpawnData Pos = new TargetCasterPos();
|
||||
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class ForceSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class FireballSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype = default!;
|
||||
|
||||
[DataField("posData")]
|
||||
public MagicSpawnData Pos = new TargetCasterPos();
|
||||
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class CardsSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype = default!;
|
||||
|
||||
[DataField("posData")]
|
||||
public MagicSpawnData Pos = new TargetCasterPos();
|
||||
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class ForceWallSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype = default!;
|
||||
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class BlinkSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class EtherealJauntSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class EmpSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class CluwneCurseSpellEvent : EntityTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class BananaTouchSpellEvent : EntityTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class MimeTouchSpellEvent : EntityTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class InstantRecallSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -152,3 +152,23 @@
|
||||
license: "CC0-1.0"
|
||||
copyright: "dakamakat on freesound.org"
|
||||
source: "https://freesound.org/people/Dakamakat/sounds/717370/"
|
||||
|
||||
- files: ["energyshield_up.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: "unfa on freesound.org"
|
||||
source: "https://freesound.org/people/unfa/sounds/584173/"
|
||||
|
||||
- files: ["energyshield_down.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: "SilverIllusionist on freesound.org"
|
||||
source: "https://freesound.org/people/SilverIllusionist/sounds/673556/"
|
||||
|
||||
- files: ["energyshield_ambient.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: "julianmateo_ on freesound.org"
|
||||
source: "https://freesound.org/people/julianmateo_/sounds/524165/"
|
||||
|
||||
- files: ["energyshield_parry.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: "Robinhood76 on freesound.org"
|
||||
source: "https://freesound.org/people/Robinhood76/sounds/107613/"
|
||||
BIN
Resources/Audio/Machines/energyshield_ambient.ogg
Normal file
BIN
Resources/Audio/Machines/energyshield_ambient.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Machines/energyshield_down.ogg
Normal file
BIN
Resources/Audio/Machines/energyshield_down.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Machines/energyshield_parry.ogg
Normal file
BIN
Resources/Audio/Machines/energyshield_parry.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Machines/energyshield_up.ogg
Normal file
BIN
Resources/Audio/Machines/energyshield_up.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Items/scroll/after_use.ogg
Normal file
BIN
Resources/Audio/White/Items/scroll/after_use.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Items/scroll/use.ogg
Normal file
BIN
Resources/Audio/White/Items/scroll/use.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/Arc/cast.ogg
Normal file
BIN
Resources/Audio/White/Magic/Arc/cast.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/Arc/charge.ogg
Normal file
BIN
Resources/Audio/White/Magic/Arc/charge.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/Arc/max.ogg
Normal file
BIN
Resources/Audio/White/Magic/Arc/max.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/Cards/cast.ogg
Normal file
BIN
Resources/Audio/White/Magic/Cards/cast.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/Cards/charge.ogg
Normal file
BIN
Resources/Audio/White/Magic/Cards/charge.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/Cards/max.ogg
Normal file
BIN
Resources/Audio/White/Magic/Cards/max.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/Force/cast.ogg
Normal file
BIN
Resources/Audio/White/Magic/Force/cast.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/Force/charge.ogg
Normal file
BIN
Resources/Audio/White/Magic/Force/charge.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/Force/max.ogg
Normal file
BIN
Resources/Audio/White/Magic/Force/max.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/chargingfallback.ogg
Normal file
BIN
Resources/Audio/White/Magic/chargingfallback.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/White/Magic/maxchargefallback.ogg
Normal file
BIN
Resources/Audio/White/Magic/maxchargefallback.ogg
Normal file
Binary file not shown.
@@ -1914,3 +1914,260 @@
|
||||
id: 177
|
||||
time: '2024-03-02T14:17:52.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/152
|
||||
- author: Aviu
|
||||
changes:
|
||||
- message: "\u0411\u0417 \u0442\u0435\u043F\u0435\u0440\u044C \u0443\u0441\u044B\
|
||||
\u043F\u043B\u044F\u0435\u0442 \u0433\u0435\u043D\u043E\u043A\u0440\u0430\u0434\
|
||||
\u043E\u0432."
|
||||
type: Add
|
||||
- message: "\u041D\u0438\u0442\u0440\u0438\u0443\u043C \u0441\u043D\u043E\u0432\u0430\
|
||||
\ \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442."
|
||||
type: Fix
|
||||
id: 178
|
||||
time: '2024-03-03T08:09:04.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/167
|
||||
- author: ThereDrD
|
||||
changes:
|
||||
- message: "\u0424\u0438\u043A\u0441 \u043F\u0443\u0441\u0442\u044B\u0445 \u0441\
|
||||
\u0442\u0440\u043E\u0447\u0435\u043A \u0432\u043C\u0435\u0441\u0442\u043E \u043F\
|
||||
\u043E\u043F\u0430\u043F\u043E\u0432"
|
||||
type: Fix
|
||||
- message: "\u0424\u0438\u043A\u0441 \u043F\u0440\u043E\u043F\u0430\u0432\u0448\u0438\
|
||||
\u0445 \u043F\u043E\u043F\u0430\u043F\u043E\u0432"
|
||||
type: Fix
|
||||
id: 179
|
||||
time: '2024-03-04T06:56:23.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/176
|
||||
- author: Aviu
|
||||
changes:
|
||||
- message: "\u0422\u041A \u0437\u0430 \u0432\u043E\u0439\u043D\u0443 20 -> 30."
|
||||
type: Tweak
|
||||
- message: "\u041A\u043E\u0441\u0430 \u0436\u043D\u0435\u0446\u0430: \u0443\u0440\
|
||||
\u043E\u043D 27 -> 24."
|
||||
type: Tweak
|
||||
- message: "\u041F\u043E\u0441\u043E\u0445 \u043C\u043E\u043D\u0430\u0445\u0430\
|
||||
: \u0443\u0440\u043E\u043D 20 -> 18."
|
||||
type: Tweak
|
||||
- message: "\u041D\u0435\u0447\u0435\u0441\u0442\u0438\u0432\u044B\u0435 \u0432\u0438\
|
||||
\u043B\u044B: \u0443\u0440\u043E\u043D 25 -> 24."
|
||||
type: Tweak
|
||||
- message: "\u0413\u0438\u043F\u0435\u0440\u0438\u043D\u0441\u0442\u0440\u0443\u043C\
|
||||
\u0435\u043D\u0442: \u0441\u043A\u043E\u0440\u043E\u0441\u0442\u044C \u0430\u0442\
|
||||
\u0430\u043A\u0438 0.75 -> 1, \u0443\u0440\u043E\u043D \u043F\u043E \u0441\u0442\
|
||||
\u0430\u043C\u0438\u043D\u0435 60 -> 40."
|
||||
type: Tweak
|
||||
- message: "\u0423 \u0432\u043D\u0435\u043F\u0440\u043E\u0441\u0442\u0440\u0430\u043D\
|
||||
\u0441\u0442\u0432\u0435\u043D\u043D\u043E\u0433\u043E \u043A\u043B\u0438\u043D\
|
||||
\u043A\u0430 \u0443\u043C\u0435\u043D\u044C\u0448\u0430\u0435\u0442\u0441\u044F\
|
||||
\ \u0443\u0440\u043E\u043D \u0441 \u041F\u041A\u041C \u0430\u0442\u0430\u043A\
|
||||
\u0438, \u043A\u0430\u043A \u0438 \u0443 \u043E\u0441\u0442\u0430\u043B\u044C\
|
||||
\u043D\u044B\u0445 \u043E\u0440\u0443\u0436\u0438\u0439."
|
||||
type: Tweak
|
||||
- message: "\u0413\u043B\u0443\u0448\u0438\u043B\u043A\u0430 \u0440\u0430\u0434\u0438\
|
||||
\u043E: \u0446\u0435\u043D\u0430 4 -> 2."
|
||||
type: Tweak
|
||||
- message: "\u0421\u0432\u0438\u043D\u0446\u043E\u0432\u044B\u0435 \u0431\u043E\u043A\
|
||||
\u0441\u0435\u0440\u0441\u043A\u0438\u0435 \u043F\u0435\u0440\u0447\u0430\u0442\
|
||||
\u043A\u0438: \u0443\u0440\u043E\u043D 8 -> 10, \u0443\u0440\u043E\u043D \u043F\
|
||||
\u043E \u0441\u0442\u0430\u043C\u0438\u043D\u0435 25 -> 30, \u0441\u043A\u043E\
|
||||
\u0440\u043E\u0441\u0442\u044C \u0430\u0442\u0430\u043A\u0438 1.4 -> 1.5."
|
||||
type: Tweak
|
||||
- message: "\u0421\u0435\u0442\u0447\u0430\u0442\u044B\u0439 \u0436\u0438\u043B\u0435\
|
||||
\u0442: \u0440\u0435\u0437\u0438\u0441\u0442 \u043A \u043F\u0440\u043E\u043D\
|
||||
\u0438\u043A\u0430\u044E\u0449\u0435\u043C\u0443 \u0443\u0440\u043E\u043D\u0443\
|
||||
\ 70 -> 60, \u043A \u0442\u0435\u043F\u043B\u043E\u0432\u043E\u043C\u0443 10\
|
||||
\ -> 20."
|
||||
type: Tweak
|
||||
- message: "\u0421\u0442\u0430\u043D\u043F\u0440\u043E\u0434 \u0438 \u0441\u043D\
|
||||
\u0430\u0442\u0447\u0435\u0440\u043F\u0440\u043E\u0434: \u0443\u0440\u043E\u043D\
|
||||
\ \u043F\u043E \u0441\u0442\u0430\u043C\u0438\u043D\u0435 30 -> 40."
|
||||
type: Tweak
|
||||
- message: "\u0418\u0437\u043C\u0435\u043D\u0435\u043D \u043A\u0440\u0430\u0444\u0442\
|
||||
\ \u0441\u0442\u0430\u043D\u043F\u0440\u043E\u0434\u0430 \u0438 \u0441\u043D\
|
||||
\u0430\u0442\u0447\u0435\u0440\u043F\u0440\u043E\u0434\u0430, \u0442\u0435\u043F\
|
||||
\u0435\u0440\u044C \u043D\u0443\u0436\u043D\u043E \u043A\u043B\u0438\u043A\u043D\
|
||||
\u0443\u0442\u044C \u0441\u0442\u0435\u0440\u0436\u043D\u044F\u043C\u0438 \u043F\
|
||||
\u043E \u0441\u0442\u044F\u0436\u043A\u0430\u043C, \u0434\u043E\u0431\u0430\u0432\
|
||||
\u0438\u0442\u044C \u0438\u0433\u043D\u0438\u0442\u0435\u0440 \u0438 \u043A\u043E\
|
||||
\u043D\u0434\u0435\u043D\u0441\u0430\u0442\u043E\u0440/\u0442\u0435\u043B\u0435\
|
||||
\u043A\u0440\u0438\u0441\u0442\u0430\u043B\u043B."
|
||||
type: Tweak
|
||||
- message: "\u0412\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D \u043A\u0440\u0430\
|
||||
\u0444\u0442 \u0441\u0442\u0430\u043D\u043F\u0440\u043E\u0434\u0430 \u0432 \u043C\
|
||||
\u0435\u043D\u044E \u043A\u0440\u0430\u0444\u0442\u0430."
|
||||
type: Tweak
|
||||
- message: "\u0412 \u0432\u0435\u043D\u0434\u043E\u043C\u0430\u0442\u0435 \u0442\
|
||||
\u0435\u043F\u0435\u0440\u044C 5 \u0438\u0433\u043D\u0438\u0442\u0435\u0440\u043E\
|
||||
\u0432 \u0438 4 \u043F\u0435\u0440\u0435\u0434\u0430\u0442\u0447\u0438\u043A\
|
||||
\u0430 \u0441\u0438\u0433\u043D\u0430\u043B\u0430."
|
||||
type: Tweak
|
||||
id: 180
|
||||
time: '2024-03-04T07:02:16.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/170
|
||||
- author: Aviu
|
||||
changes:
|
||||
- message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D \u043F\u0440\u043E\u0434\
|
||||
\u0432\u0438\u043D\u0443\u0442\u044B\u0439 \u0438\u043C\u043F\u043B\u0430\u043D\
|
||||
\u0442\u0435\u0440 \u0432 \u0430\u043F\u043B\u0438\u043D\u043A."
|
||||
type: Add
|
||||
- message: "\u0423\u043C\u0435\u043D\u044C\u0448\u0435\u043D\u043E \u0432\u0440\u0435\
|
||||
\u043C\u044F \u0432\u043A\u0430\u043B\u044B\u0432\u0430\u043D\u0438\u044F \u0438\
|
||||
\u043C\u043F\u043B\u0430\u043D\u0442\u043E\u0432 \u0441\u0438\u043D\u0434\u0438\
|
||||
\u043A\u0430\u0442\u0430 5s -> 2s."
|
||||
type: Tweak
|
||||
- message: "\u0412\u0441\u0435 \u0438\u043C\u043F\u043B\u0430\u043D\u0442\u0435\u0440\
|
||||
\u044B \u0442\u0435\u043F\u0435\u0440\u044C \u043E\u0434\u043D\u043E\u0440\u0430\
|
||||
\u0437\u043E\u0432\u044B\u0435, \u0442\u043E \u0435\u0441\u0442\u044C \u043B\
|
||||
\u043E\u043C\u0430\u044E\u0442\u0441\u044F \u043F\u043E\u0441\u043B\u0435 \u0432\
|
||||
\u043A\u0430\u043B\u044B\u0432\u0430\u043D\u0438\u044F."
|
||||
type: Tweak
|
||||
id: 181
|
||||
time: '2024-03-05T18:42:07.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/171
|
||||
- author: ThereDrD
|
||||
changes:
|
||||
- message: "\u041F\u043E\u0444\u0438\u043A\u0448\u0435\u043D\u043E \u0430\u0432\u0442\
|
||||
\u043E\u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u0435 the \u0432\
|
||||
\ \u0440\u0443\u0441\u0441\u043A\u043E\u0439 \u043B\u043E\u043A\u0430\u043B\u0438\
|
||||
\u0437\u0430\u0446\u0438\u0438."
|
||||
type: Fix
|
||||
id: 182
|
||||
time: '2024-03-06T18:06:28.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/178
|
||||
- author: rhailrake
|
||||
changes:
|
||||
- message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B \u0430\u043B\u044C\
|
||||
\u0442\u0435\u0440\u043D\u0430\u0442\u0438\u0432\u043D\u044B\u0435 \u0441\u043F\
|
||||
\u0435\u043B\u043B\u044B."
|
||||
type: Add
|
||||
id: 183
|
||||
time: '2024-03-07T16:01:55.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/156
|
||||
- author: Aviu
|
||||
changes:
|
||||
- message: "\u0422\u0435\u043F\u0435\u0440\u044C \u0431\u0440\u043E\u043D\u044F\
|
||||
\ \u0437\u0430\u0449\u0438\u0449\u0430\u0435\u0442 \u043E\u0442 \u0440\u0435\
|
||||
\u0437\u0438\u043D\u043E\u0432\u044B\u0445 \u043F\u0443\u043B\u044C \u043F\u0440\
|
||||
\u043E\u043F\u043E\u0440\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u043E\
|
||||
\ \u0440\u0435\u0437\u0438\u0441\u0442\u0443 \u043E\u0442 \u0442\u0443\u043F\
|
||||
\u043E\u0433\u043E \u0443\u0440\u043E\u043D\u0430."
|
||||
type: Add
|
||||
- message: "\u0423\u0440\u043E\u043D \u043F\u043E \u0441\u0442\u0430\u043C\u0438\
|
||||
\u043D\u0435 \u0442\u0440\u0430\u0432\u043C\u0430\u0442\u0438\u0447\u0435\u0441\
|
||||
\u043A\u0438\u0445 \u043F\u0430\u0442\u0440\u043E\u043D\u043E\u0432 \u043E\u0442\
|
||||
\ \u0434\u0440\u043E\u0431\u043E\u0432\u0438\u043A\u0430: 40 -> 70."
|
||||
type: Tweak
|
||||
- message: "\u041D\u0435\u043C\u043D\u043E\u0433\u043E \u0441\u043D\u0438\u0436\u0435\
|
||||
\u043D \u0440\u0435\u0437\u0438\u0441\u0442\u044B \u0443 \u0431\u0440\u043E\u043D\
|
||||
\u0438 \u0441\u0432\u044F\u0449\u0435\u043D\u043D\u0438\u043A\u0430."
|
||||
type: Tweak
|
||||
- message: "\u0412 \u0438\u043C\u043F\u043B\u0430\u043D\u0442\u0435 \u0430\u043F\
|
||||
\u043B\u0438\u043D\u043A\u0430 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\
|
||||
\u0442 \u0441\u043A\u0438\u0434\u043E\u043A."
|
||||
type: Tweak
|
||||
- message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u044B \u0431\u043E\u043B\
|
||||
\u044B \u0432 \u0421\u0411\u0422\u0435\u0445."
|
||||
type: Add
|
||||
- message: "\u0412\u044B\u0441\u0442\u0440\u0435\u043B\u044B \u0442\u0435\u043C\u043F\
|
||||
\u0435\u0440\u0430\u0442\u0443\u0440\u043D\u044B\u0445 \u043F\u0443\u0448\u0435\
|
||||
\u043A \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u043F\u0440\u043E\u0445\
|
||||
\u043E\u0434\u044F\u0442 \u0441\u043A\u0432\u043E\u0437\u044C \u0441\u0442\u0435\
|
||||
\u043A\u043B\u043E."
|
||||
type: Tweak
|
||||
- message: "\u0423\u0434\u0432\u043E\u0435\u043D\u0430 \u0441\u043A\u043E\u0440\u043E\
|
||||
\u0441\u0442\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\
|
||||
\u0435\u043D\u0438\u044F \u0442\u0435\u043C\u043F\u0435\u0440\u0430\u0442\u0443\
|
||||
\u0440\u044B \u0432 \u0441\u043A\u0430\u0444\u0430\u043D\u0434\u0440\u0430\u0445\
|
||||
."
|
||||
type: Tweak
|
||||
id: 184
|
||||
time: '2024-03-08T15:35:11.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/177
|
||||
- author: Remuchi
|
||||
changes:
|
||||
- message: "\u0418\u043C\u043F\u043B\u0430\u043D\u0442 \u043F\u043E\u0434\u0447\u0438\
|
||||
\u043D\u0435\u043D\u0438\u044F \u0442\u0435\u043F\u0435\u0440\u044C \u043D\u0435\
|
||||
\ \u0443\u0431\u0438\u0440\u0430\u0435\u0442\u0441\u044F \u043F\u0440\u0438\
|
||||
\ \u0434\u043E\u0441\u0442\u0430\u0432\u0430\u043D\u0438\u0438 \u043B\u044E\u0431\
|
||||
\u043E\u0433\u043E \u0438\u043C\u043F\u043B\u0430\u043D\u0442\u0430 \u0438\u0437\
|
||||
\ \u0442\u0435\u043B\u0430 \u0440\u0430\u0431\u0430"
|
||||
type: Fix
|
||||
- message: "\u0421\u043A\u0430\u043D\u0435\u0440 \u0440\u0430\u0441\u0442\u0435\u043D\
|
||||
\u0438\u0439 \u0442\u0435\u043F\u0435\u0440\u044C \u043C\u043E\u0436\u043D\u043E\
|
||||
\ \u043F\u043E\u043B\u043E\u0436\u0438\u0442\u044C \u0432 \u043F\u043E\u044F\
|
||||
\u0441 \u0431\u043E\u0442\u0430\u043D\u0438\u043A\u0430"
|
||||
type: Fix
|
||||
- message: "\u041F\u0435\u0440\u0435\u0432\u043E\u0434 \u0432\u043A\u0443\u0441\u0430\
|
||||
\ \u043F\u0438\u0432\u0430, \u0440\u0430\u0437\u043C\u0435\u0440\u043E\u0432\
|
||||
\ \u043F\u0440\u0435\u0434\u043C\u0435\u0442\u043E\u0432"
|
||||
type: Add
|
||||
- message: "\u041A\u043E\u0441\u043C\u0438\u0447\u0435\u0441\u043A\u0438\u0439 \u043E\
|
||||
\u0447\u0438\u0441\u0442\u0438\u0442\u0435\u043B\u044C \u0441\u043D\u043E\u0432\
|
||||
\u0430 \u043C\u043E\u0436\u0435\u0442 \u0441\u0442\u0438\u0440\u0430\u0442\u044C\
|
||||
\ \u0440\u0438\u0441\u0443\u043D\u043A\u0438"
|
||||
type: Add
|
||||
- message: "\u0411\u043E\u043B\u044C\u0448\u0435 \u043D\u0435\u043B\u044C\u0437\u044F\
|
||||
\ \u0432\u0441\u0442\u0430\u0432\u043B\u044F\u0442\u044C \u0438\u043C\u043F\u043B\
|
||||
\u0430\u043D\u0442\u044B \u0432 \u0431\u043E\u0440\u0433\u043E\u0432 (\u043D\
|
||||
\u0438\u043A\u0430\u043A\u0438\u0445 \u0431\u043E\u0440\u0433\u043E\u0432-\u0440\
|
||||
\u0430\u0431\u043E\u0432)"
|
||||
type: Add
|
||||
- message: "\u0422\u0435\u043F\u0435\u0440\u044C \u0442\u043E\u043B\u044C\u043A\u043E\
|
||||
\ \u043B\u044E\u0434\u0438 \u0441\u043F\u0430\u0432\u043D\u044F\u0442\u0441\u044F\
|
||||
\ \u043D\u0430 \u0440\u043E\u043B\u044F\u0445 \u0426\u041A (\u041E\u0442\u0440\
|
||||
\u044F\u0434 \u0421\u043C\u0435\u0440\u0442\u0438, \u041E\u0411\u0420, \u041F\
|
||||
\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u0435\u043B\u044C \u0426\
|
||||
\u041A, \u0420\u0425\u0411\u0417"
|
||||
type: Add
|
||||
- message: "\u0418\u0441\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u044B \u0440\u0430\
|
||||
\u0437\u043C\u0435\u0440\u044B \u043E\u0441\u0442\u0430\u0432\u0448\u0438\u0445\
|
||||
\u0441\u044F \u043C\u0430\u0442\u0435\u0440\u0438\u0430\u043B\u043E\u0432 (\u0441\
|
||||
\u0442\u0435\u043A\u043B\u0430, \u0441\u0442\u0435\u0440\u0436\u043D\u0438,\
|
||||
\ \u0441\u043B\u0438\u0442\u043A\u0438, \u043F\u043B\u0430\u0441\u0442\u0438\
|
||||
\u043A, \u0443\u0440\u0430\u043D \u0438 \u043F\u0440\u043E\u0447\u0435\u0435\
|
||||
)"
|
||||
type: Tweak
|
||||
- message: "\u0420\u0430\u0437\u043C\u0435\u0440 \u043F\u043E\u0440\u0442\u0441\u0438\
|
||||
\u0433\u0430\u0440\u0430 \u0443\u043C\u0435\u043D\u044C\u0448\u0435\u043D \u0434\
|
||||
\u043E 2x2"
|
||||
type: Tweak
|
||||
- message: "\u0423\u0434\u0430\u043B\u0435\u043D\u043E \u0434\u0435\u0439\u0441\u0442\
|
||||
\u0432\u0438\u0435 \"\u041A\u0440\u0438\u0447\u0430\u0442\u044C\" \u0441 \u043F\
|
||||
\u0430\u043D\u0435\u043B\u0438 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439"
|
||||
type: Remove
|
||||
id: 185
|
||||
time: '2024-03-08T16:50:01.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/181
|
||||
- author: ThereDrD
|
||||
changes:
|
||||
- message: "\u0420\u0430\u0443\u043D\u0434\u0441\u0442\u0430\u0440\u0442\u043E\u0432\
|
||||
\u044B\u0439 \u0438\u043D\u0432\u0435\u043D\u0442\u0430\u0440\u044C \u0440\u043E\
|
||||
\u043B\u0435\u0439 \u043F\u0435\u0440\u0435\u0440\u0430\u0431\u043E\u0442\u0430\
|
||||
\u043D"
|
||||
type: Add
|
||||
- message: "\u0423\u0432\u0435\u043B\u0438\u0447\u0435\u043D\u043E \u043A\u043E\u043B\
|
||||
\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0432\u0435\u0449\u0435\u0439 \u0432\
|
||||
\ \u0448\u043A\u0430\u0444\u0447\u0438\u043A\u0430\u0445 \u0440\u043E\u043B\u0435\
|
||||
\u0439."
|
||||
type: Add
|
||||
id: 186
|
||||
time: '2024-03-10T23:02:48.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/180
|
||||
- author: ThereDrD
|
||||
changes:
|
||||
- message: "\u041F\u043E\u043F\u043E\u043B\u043D\u0435\u043D\u043E \u0441\u043E\u0434\
|
||||
\u0435\u0440\u0436\u0438\u043C\u043E\u0435 \u0432\u0435\u043D\u0434\u043E\u043C\
|
||||
\u0430\u0442\u043E\u0432 \u043D\u043E\u0432\u044B\u043C\u0438 \u0442\u043E\u0432\
|
||||
\u0430\u0440\u0430\u043C\u0438"
|
||||
type: Add
|
||||
id: 187
|
||||
time: '2024-03-10T23:02:31.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/179
|
||||
- author: ThereDrD
|
||||
changes:
|
||||
- message: "\u0423\u0431\u0440\u0430\u043D \u043F\u043E\u043F\u0430\u043F \u0441\
|
||||
\u043D\u0430(Zzz...)"
|
||||
type: Remove
|
||||
id: 188
|
||||
time: '2024-03-10T23:07:37.0000000+00:00'
|
||||
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/146
|
||||
|
||||
9
Resources/Locale/en-US/EnergyDome/energydome.ftl
Normal file
9
Resources/Locale/en-US/EnergyDome/energydome.ftl
Normal file
@@ -0,0 +1,9 @@
|
||||
energy-dome-access-denied = Access denied
|
||||
energy-dome-recharging = Recharging...
|
||||
energy-dome-no-power = Low battery
|
||||
energy-dome-no-cell = There is no power source
|
||||
|
||||
energy-dome-on-examine-is-on-message = The energy barrier is [color=darkgreen]up[/color].
|
||||
energy-dome-on-examine-is-off-message = The energy barrier is [color=darkred]down[/color].
|
||||
|
||||
energy-dome-verb-toggle = Toggle energy dome
|
||||
@@ -1,6 +1,5 @@
|
||||
action-name-wake = Wake up
|
||||
|
||||
sleep-onomatopoeia = Zzz...
|
||||
sleep-examined = [color=lightblue]{CAPITALIZE(SUBJECT($target))} {CONJUGATE-BE($target)} asleep.[/color]
|
||||
|
||||
wake-other-success = You shake {THE($target)} awake.
|
||||
|
||||
@@ -32,6 +32,7 @@ research-technology-wave-particle-harnessing = Wave Particle Harnessing
|
||||
research-technology-advanced-riot-control = Advanced Riot Control
|
||||
research-technology-portable-microfusion-weaponry = Portable Microfusion Weaponry
|
||||
research-technology-experimental-battery-ammo = Experimental Battery Ammo
|
||||
research-technology-energy_barriers = Energy Barriers
|
||||
research-technology-basic-shuttle-armament = Shuttle basic armament
|
||||
research-technology-advanced-shuttle-weapon = Advanced shuttle weapons
|
||||
|
||||
|
||||
@@ -306,6 +306,9 @@ uplink-hardsuit-syndieelite-desc = An elite version of the blood-red hardsuit, w
|
||||
uplink-clothing-outer-hardsuit-juggernaut-name = Cybersun Juggernaut Suit
|
||||
uplink-clothing-outer-hardsuit-juggernaut-desc = Hyper resilient armor made of materials tested in the Tau chromosphere facility. The only thing that's going to be slowing you down is this suit... and tasers.
|
||||
|
||||
uplink-energy-dome-name = Personal energy dome
|
||||
uplink-energy-dome-desc = A personal shield generator that protects the wearer from lasers and bullets but prevents from using ranged weapons himself. Comes with a small power cell.
|
||||
|
||||
# Misc
|
||||
uplink-cyberpen-name = Cybersun Pen
|
||||
uplink-cyberpen-desc = Cybersun's legal department pen, invaluable for forging documents and escaping prisons. Smells vaguely of hard-light and war profiteering.
|
||||
|
||||
1
Resources/Locale/ru-RU/White/items/betrayal-dagger.ftl
Normal file
1
Resources/Locale/ru-RU/White/items/betrayal-dagger.ftl
Normal file
@@ -0,0 +1 @@
|
||||
backstab-damage-betrayal-dagger = Удар в спину: {$damage}!
|
||||
@@ -2,7 +2,6 @@ action-name-sleep = Спать
|
||||
action-desc-sleep = Лечь спать.
|
||||
action-name-wake = Проснуться
|
||||
action-desc-wake = Перестать спать.
|
||||
sleep-onomatopoeia = Zzz...
|
||||
sleep-examined = [color=lightblue]{ CAPITALIZE($target) } спит.[/color]
|
||||
wake-other-success = Вы разбудили { $target }.
|
||||
wake-other-failure = Вы тормошите { $target }, но { $target } не просыпается.
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
bonkable-success-message-others = { CAPITALIZE(THE($user)) } стукает голову { POSS-ADJ($user) } об { $bonkable }
|
||||
bonkable-success-message-others = { CAPITALIZE($user) } стукает голову { POSS-ADJ($user) } об { $bonkable }
|
||||
bonkable-success-message-user = Вы стукаете свою голову об { $bonkable }
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
cluwne-transform = { CAPITALIZE(THE($target)) } превратился в клувна!
|
||||
cluwne-transform = { CAPITALIZE($target) } превратился в клувна!
|
||||
cluwne-name-prefix = Клувнифицированный { $target }
|
||||
|
||||
@@ -3,7 +3,7 @@ comp-storage-cant-insert = Невозможно поместить.
|
||||
comp-storage-insufficient-capacity = Недостаточная вместимость.
|
||||
comp-storage-invalid-container = Неправильный контейнер для этого предмета.
|
||||
comp-storage-anchored-failure = Невозможно поместить закрепленный предмет.
|
||||
comp-storage-cant-drop = Вы не можете отпустить { THE($entity) }!
|
||||
comp-storage-cant-drop = Вы не можете отпустить { $entity }!
|
||||
comp-storage-window-title = Предмет хранилище
|
||||
comp-storage-window-volume = Занято: { $usedVolume }/{ $maxVolume }, предметов: { $itemCount }
|
||||
comp-storage-window-volume-unlimited = Предметов: { $itemCount }
|
||||
|
||||
@@ -179,7 +179,7 @@ flavor-complex-rocksandstones = как скалы и камни
|
||||
|
||||
## Basic drinks
|
||||
flavor-complex-water = как вода
|
||||
flavor-complex-water = как вода
|
||||
flavor-complex-beer = как моча
|
||||
flavor-complex-cognac = как сухой пряный алкоголь
|
||||
flavor-complex-mead = как забродивший мёд
|
||||
flavor-complex-vermouth = как виноградная мякоть
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
mopping-system-tool-full = { CAPITALIZE($used) } полная!
|
||||
mopping-system-target-container-empty-water = { CAPITALIZE(THE($target)) } не имеет воды!
|
||||
mopping-system-puddle-space = { THE($used) } заполнено водой
|
||||
mopping-system-puddle-evaporate = { THE($target) } испаряется
|
||||
mopping-system-no-water = { THE($used) } не имеет воды!
|
||||
mopping-system-full = { THE($used) } полное!
|
||||
mopping-system-empty = { THE($used) } пустое!
|
||||
mopping-system-target-container-empty-water = { CAPITALIZE($target) } не имеет воды!
|
||||
mopping-system-puddle-space = { $used } заполнено водой
|
||||
mopping-system-puddle-evaporate = { $target } испаряется
|
||||
mopping-system-no-water = { $used } не имеет воды!
|
||||
mopping-system-full = { $used } полное!
|
||||
mopping-system-empty = { $used } пустое!
|
||||
mopping-system-puddle-diluted = Вы разбавляете лужу.
|
||||
mopping-system-puddle-success = Вы убираете лужу.
|
||||
mopping-system-release-to-floor = Вы выжимаете часть жидкости на пол.
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
drain-component-empty-verb-using-is-empty-message = { CAPITALIZE(THE($object)) } пусто!
|
||||
drain-component-empty-verb-target-is-full-message = { CAPITALIZE(THE($object)) } полный!
|
||||
drain-component-empty-verb-using-is-empty-message = { CAPITALIZE($object) } пусто!
|
||||
drain-component-empty-verb-target-is-full-message = { CAPITALIZE($object) } полный!
|
||||
drain-component-empty-verb-inhand = Пустой { $object }
|
||||
drain-component-examine-hint-full = [color="blue"]Он наполнен до краев. Может вантуз поможет?[/color]
|
||||
drain-component-examine-volume = [color="blue"]Оставшееся место - { $volume }ед.[/color]
|
||||
drain-component-unclog-fail = { CAPITALIZE(THE($object)) } все еще полон.
|
||||
drain-component-unclog-success = { CAPITALIZE(THE($object)) } очищается.
|
||||
drain-component-unclog-notapplicable = { CAPITALIZE(THE($object)) } не забит.
|
||||
drain-component-unclog-fail = { CAPITALIZE($object) } все еще полон.
|
||||
drain-component-unclog-success = { CAPITALIZE($object) } очищается.
|
||||
drain-component-unclog-notapplicable = { CAPITALIZE($object) } не забит.
|
||||
|
||||
@@ -2,4 +2,4 @@ puddle-component-examine-is-slipper-text = Выглядит скользким.
|
||||
puddle-component-examine-evaporating = Оно [color=#5E7C16]испаряемое[/color].
|
||||
puddle-component-examine-evaporating-partial = Оно [color=#FED83D]частично испаряемое[/color].
|
||||
puddle-component-examine-evaporating-no = Оно [color=#B02E26]не испаряемое[/color].
|
||||
puddle-component-slipped-touch-reaction = Вещества в { THE($puddle) } попадают на вашу кожу!
|
||||
puddle-component-slipped-touch-reaction = Вещества в { $puddle } попадают на вашу кожу!
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
spill-target-verb-get-data-text = Выплеснуть содержимое
|
||||
spill-target-verb-activate-cannot-drain-message = Вы не можете ничего выплеснуть из { $owner }!
|
||||
spill-target-verb-activate-is-empty-message = В { $owner } пусто!
|
||||
spill-melee-hit-attacker = Вы проливаете { $amount }ед { THE($spillable) } на { THE($target) }!
|
||||
spill-melee-hit-others = { CAPITALIZE(THE($attacker)) } выливаете часть { THE($spillable) } на { THE($target) }!
|
||||
spill-land-spilled-on-other = { CAPITALIZE(THE($spillable)) } проливает часть своего содержимого на { THE($target) }!
|
||||
spill-melee-hit-attacker = Вы проливаете { $amount }ед { $spillable } на { $target }!
|
||||
spill-melee-hit-others = { CAPITALIZE($attacker) } выливаете часть { $spillable } на { $target }!
|
||||
spill-land-spilled-on-other = { CAPITALIZE($spillable) } проливает часть своего содержимого на { $target }!
|
||||
spill-examine-is-spillable = Этот контейнер выглядит так, что его можно пролить.
|
||||
spill-examine-spillable-weapon = Вы могли бы выплеснуть это на кого-нибудь при атаке в ближнем бою.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
glue-success = { THE($target) } покрыли в клее.
|
||||
glue-success = { $target } покрыли в клее.
|
||||
glued-name-prefix = Приклеенный { $target }
|
||||
glue-failure = { THE($target) } уже покрыт клеем.
|
||||
glue-failure = { $target } уже покрыт клеем.
|
||||
|
||||
@@ -10,7 +10,7 @@ petting-success-bird = Вы гладите { $target } по { POSS-ADJ($target)
|
||||
petting-success-cat = Вы гладите { $target } по { POSS-ADJ($target) } маленькой пушистой голове.
|
||||
petting-success-corrupted-corgi = В порыве самонадеянности, вы гладите { $target } по { POSS-ADJ($target) } маленькой проклятой голове.
|
||||
petting-success-crab = Вы гладите { $target } по { POSS-ADJ($target) } маленькой гладкой голове.
|
||||
petting-success-dehydrated-carp = Вы гладите { THE($target) } по { POSS-ADJ($target) } сухой маленькой голове. { CAPITALIZE(OBJECT($target)) } похоже теперь любит вас.
|
||||
petting-success-dehydrated-carp = Вы гладите { $target } по { POSS-ADJ($target) } сухой маленькой голове. { CAPITALIZE(OBJECT($target)) } похоже теперь любит вас.
|
||||
petting-success-dog = Вы гладите { $target } по { POSS-ADJ($target) } мягкой пушистой голове.
|
||||
petting-success-frog = Вы гладите { $target } по { POSS-ADJ($target) } маленькой скользкой голове.
|
||||
petting-success-goat = Вы гладите { $target } по { POSS-ADJ($target) } рогатой пушистой голове.
|
||||
@@ -24,17 +24,17 @@ petting-success-tarantula = Вы гладите { $target } по { POSS-ADJ($tar
|
||||
petting-success-holo = Вы гладите { $target } по { POSS-ADJ($target) } металлической шипастой голове.
|
||||
petting-success-dragon = Уворачиваясь от клыков, когтей, и пламени, вы гладите { $target } по { POSS-ADJ($target) } огромной чешуйчатой голове.
|
||||
petting-success-hamster = Вы гладите { $target } по { POSS-ADJ($target) } маленькой пушистой голове.
|
||||
petting-success-bear = Вы неохотно гладите { THE($target) } по { POSS-ADJ($target) } мистической голове.
|
||||
petting-success-slimes = Вы гладите { THE($target) } по { POSS-ADJ($target) } слизистой поверхности.
|
||||
petting-success-snake = Вы гладите {THE($target)} по {POSS-ADJ($target)} чешуйчатой большой голове.
|
||||
petting-success-monkey = Вы гладите {THE($target)} по {POSS-ADJ($target)} озорной маленькой голове.
|
||||
petting-success-nymph = Вы гладите {THE($target)} по {POSS-ADJ($target)} деревянной маленькой голове.
|
||||
petting-success-bear = Вы неохотно гладите { $target } по { POSS-ADJ($target) } мистической голове.
|
||||
petting-success-slimes = Вы гладите { $target } по { POSS-ADJ($target) } слизистой поверхности.
|
||||
petting-success-snake = Вы гладите { $target } по { POSS-ADJ($target) } чешуйчатой большой голове.
|
||||
petting-success-monkey = Вы гладите { $target } по { POSS-ADJ($target) } озорной маленькой голове.
|
||||
petting-success-nymph = Вы гладите { $target } по { POSS-ADJ($target) } деревянной маленькой голове.
|
||||
|
||||
petting-failure-generic = Вы тянетесь погладить { $target }, но { $target } настороженно уклоняется от вас.
|
||||
petting-failure-bat = Вы тянетесь погладить { $target }, но { $target } очень трудно поймать!
|
||||
petting-failure-corrupted-corgi = Вы тянетесь погладить { $target }, но решаете, что лучше не надо.
|
||||
petting-failure-crab = Вы тянетесь погладить { $target }, но { $target } щёлкает клешнями в вашу сторону!
|
||||
petting-failure-dehydrated-carp = Вы гладите { THE($target) } по { POSS-ADJ($target) } сухой маленькой голове.
|
||||
petting-failure-dehydrated-carp = Вы гладите { $target } по { POSS-ADJ($target) } сухой маленькой голове.
|
||||
petting-failure-goat = Вы тянетесь погладить { $target }, но { $target } упорно отказывается!
|
||||
petting-failure-goose = Вы тянетесь погладить { $target }, но { $target } слишком ужасен!
|
||||
petting-failure-possum = Вы тянетесь погладить { $target }, но на вас шипят и рычат.
|
||||
@@ -42,20 +42,20 @@ petting-failure-sloth = Вы тянетесь погладить { $target }, н
|
||||
petting-failure-holo = Вы тянетесь погладить { $target }, но { $target } едва не пронзает шипами вашу руку!
|
||||
petting-failure-dragon = Вы поднимаете руку, но { $target } издаёт рёв, и вы решаете, что не хотите стать кормом для карпов.
|
||||
petting-failure-hamster = Вы тянетесь погладить { $target }, но { $target } пытается укусить вас за палец, и только ваши молниеносные рефлексы спасают вас от почти смертельной травмы.
|
||||
petting-failure-bear = Вы протягиваете руку, чтобы погладить { THE($target) }, но { SUBJECT($target) } рычит, заставляя вас дважды подумать.
|
||||
petting-failure-monkey = Вы протягиваете руку, чтобы погладить {THE($target)}, но {SUBJECT($target)} чуть-ли не откусывает ваши пальцы!
|
||||
petting-failure-nymph = Вы протягиваете руку, чтобы погладить {THE($target)}, но {POSS-ADJ($target)} отодвигает свои ветки от вас.
|
||||
petting-failure-shadow = Вы пытаетесь погладить {THE($target)}, но ваша рука проходит сквозь холодную темноту его тела.
|
||||
petting-failure-bear = Вы протягиваете руку, чтобы погладить { $target }, но { SUBJECT($target) } рычит, заставляя вас дважды подумать.
|
||||
petting-failure-monkey = Вы протягиваете руку, чтобы погладить { $target }, но { SUBJECT($target) } чуть-ли не откусывает ваши пальцы!
|
||||
petting-failure-nymph = Вы протягиваете руку, чтобы погладить { $target }, но { POSS-ADJ($target) } отодвигает свои ветки от вас.
|
||||
petting-failure-shadow = Вы пытаетесь погладить {$target}, но ваша рука проходит сквозь холодную темноту его тела.
|
||||
|
||||
## Knocking on windows
|
||||
|
||||
petting-success-honkbot = Вы гладите { $target } по его скользкой металлической голове.
|
||||
petting-success-mimebot = Вы гладите { THE($target) } по { POSS-ADJ($target) } холодной металлической голове.
|
||||
petting-success-mimebot = Вы гладите { $target } по { POSS-ADJ($target) } холодной металлической голове.
|
||||
petting-success-cleanbot = Вы гладите { $target } по его влажной металлической голове.
|
||||
petting-success-medibot = Вы гладите { $target } по его стерильной металлической голове.
|
||||
petting-failure-honkbot = Вы тянетесь погладить { $target }, но { $target } хонкает и уворачивается!
|
||||
petting-failure-cleanbot = Вы тянетесь погладить { $target }, но { $target } занят уборкой!
|
||||
petting-failure-mimebot = Вы тянетесь погладить { THE($target) }, но { SUBJECT($target) } { CONJUGATE-BE($target) } занятый мимикацией!
|
||||
petting-failure-mimebot = Вы тянетесь погладить { $target }, но { SUBJECT($target) } { CONJUGATE-BE($target) } занятый мимикацией!
|
||||
petting-failure-medibot = Вы тянетесь погладить { $target }, но { $target } едва не пронзает вашу руку шприцом!
|
||||
# Shown when knocking on a window
|
||||
hugging-success-generic = Вы обнимаете { $target }.
|
||||
|
||||
@@ -5,3 +5,12 @@ pick-up-verb-get-data-text = Подобрать
|
||||
# "pick up" doesn't make sense if the item is already in their inventory
|
||||
|
||||
pick-up-verb-get-data-text-inventory = Взять в руку
|
||||
|
||||
item-component-on-examine-size = Это {INDEFINITE($size)} [bold]{$size}[/bold] предмет.
|
||||
|
||||
item-component-size-Tiny = крошечный
|
||||
item-component-size-Small = маленький
|
||||
item-component-size-Normal = средний
|
||||
item-component-size-Large = большой
|
||||
item-component-size-Huge = огромный
|
||||
item-component-size-Ginormous = гигантский
|
||||
@@ -1,4 +1,4 @@
|
||||
butcherable-different-tool = Вам понадобится другой инструмент для разделки { THE($target) }.
|
||||
butcherable-different-tool = Вам понадобится другой инструмент для разделки { $target }.
|
||||
butcherable-knife-butchered-success = Вы разделываете { $target } с помощью { $knife }.
|
||||
butcherable-need-knife = Используйте острый предмет чтобы разделать { $target }.
|
||||
butcherable-not-in-container = Сперва достаньте { $target } из контейнера.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
comp-kitchen-spike-deny-collect = На { CAPITALIZE($this) } уже что-то есть, закончите сначала собирать мясо!
|
||||
comp-kitchen-spike-deny-butcher = { CAPITALIZE($victim) } не может быть разделан на { $this }.
|
||||
comp-kitchen-spike-deny-butcher-knife = { CAPITALIZE(THE($victim)) } не может быть разделан на { THE($this) }, вы должны разделать это, используя нож.
|
||||
comp-kitchen-spike-deny-butcher-knife = { CAPITALIZE($victim) } не может быть разделан на { THE($this) }, вы должны разделать это, используя нож.
|
||||
comp-kitchen-spike-deny-not-dead = { CAPITALIZE($victim) } не может быть разделан. { CAPITALIZE(SUBJECT($victim)) } { $victim } не умер!
|
||||
comp-kitchen-spike-begin-hook-victim = { $user } начинает насаживать вас на { $this }!
|
||||
comp-kitchen-spike-begin-hook-self = Вы начинаете насаживать себя на { $this }!
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
lube-success = { THE($target) } был покрыт смазкой!
|
||||
lube-success = { $target } был покрыт смазкой!
|
||||
lubed-name-prefix = Смазал { $target }
|
||||
lube-failure = Не могу покрыть { THE($target) } в смазке!
|
||||
lube-slip = { THE($target) } выскальзывает из твоих рук!
|
||||
lube-failure = Не могу покрыть { $target } в смазке!
|
||||
lube-slip = { $target } выскальзывает из твоих рук!
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
infant-name-prefix = младенец { $name }
|
||||
reproductive-birth-popup = { CAPITALIZE(THE($parent)) } родила!
|
||||
reproductive-laid-egg-popup = { CAPITALIZE(THE($parent)) } откладывает яйцо!
|
||||
reproductive-birth-popup = { CAPITALIZE($parent) } родила!
|
||||
reproductive-laid-egg-popup = { CAPITALIZE($parent) } откладывает яйцо!
|
||||
|
||||
@@ -11,8 +11,8 @@ food-system-you-cannot-eat-any-more = В вас больше не лезет!
|
||||
food-system-you-cannot-eat-any-more-other = В него больше не лезет!
|
||||
food-system-try-use-food-is-empty = { $entity } пустая!
|
||||
food-system-wrong-utensil = вы не можете есть { $food } с помощью { $utensil }.
|
||||
food-system-cant-digest = Ты не можешь переварить { THE($entity) }!
|
||||
food-system-cant-digest-other = Они не могут переварить { THE($entity) }!
|
||||
food-system-cant-digest = Ты не можешь переварить { $entity }!
|
||||
food-system-cant-digest-other = Они не могут переварить { $entity }!
|
||||
food-system-verb-eat = Съесть
|
||||
|
||||
## Force feeding
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
vape-component-vape-success = Ты затянулся вейпом.
|
||||
vape-component-vape-success-forced = { CAPITALIZE(THE($user)) } заставил тебя затянуться вейпом.
|
||||
vape-component-vape-success-user-forced = Вас успешно заставили затянуться { THE($target) }.
|
||||
vape-component-try-use-vape-forced = { CAPITALIZE(THE($user)) } пытается заставить тебя затянуться вейпом.
|
||||
vape-component-try-use-vape-forced-user = Вы вынуждаете { THE($target) } затянуться вейпом.
|
||||
vape-component-vape-success-forced = { CAPITALIZE($user) } заставил тебя затянуться вейпом.
|
||||
vape-component-vape-success-user-forced = Вас успешно заставили затянуться { $target }.
|
||||
vape-component-try-use-vape-forced = { CAPITALIZE($user) } пытается заставить тебя затянуться вейпом.
|
||||
vape-component-try-use-vape-forced-user = Вы вынуждаете { $target } затянуться вейпом.
|
||||
vape-component-try-use-vape = Ты пытаешься затянуться вейпом.
|
||||
vape-component-vape-empty = Вейп пуст!
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
payload-case-not-close-enough = Вам нужно подойти ближе, чтобы определить, содержит ли { $ent } заряд.
|
||||
payload-case-has-payload = В { CAPITALIZE($ent) } установлен заряд!
|
||||
payload-case-does-not-have-payload = { CAPITALIZE(THE($ent)) } не содержит заряд.
|
||||
payload-case-does-not-have-payload = { CAPITALIZE($ent) } не содержит заряд.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
generator-clogged = {THE($generator)} резко отключается!
|
||||
generator-clogged = {$generator} резко отключается!
|
||||
|
||||
portable-generator-verb-start = Запустить генератор
|
||||
portable-generator-verb-start-msg-unreliable = Запуск генератора. Это может потребовать нескольких попыток.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user