Merge remote-tracking branch 'upstream/master' into huistream

This commit is contained in:
2024-03-11 11:21:24 +03:00
287 changed files with 4801 additions and 1247 deletions

View File

@@ -2,6 +2,7 @@ using System.Numerics;
using System.Threading; using System.Threading;
using Content.Client.CombatMode; using Content.Client.CombatMode;
using Content.Client.Gameplay; using Content.Client.Gameplay;
using Content.Client.UserInterface.Systems.Actions;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers; using Robust.Client.UserInterface.Controllers;
using Timer = Robust.Shared.Timing.Timer; using Timer = Robust.Shared.Timing.Timer;
@@ -16,7 +17,7 @@ namespace Content.Client.ContextMenu.UI
/// <remarks> /// <remarks>
/// This largely involves setting up timers to open and close sub-menus when hovering over other menu elements. /// This largely involves setting up timers to open and close sub-menus when hovering over other menu elements.
/// </remarks> /// </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); public static readonly TimeSpan HoverDelay = TimeSpan.FromSeconds(0.2);
@@ -216,6 +217,12 @@ namespace Content.Client.ContextMenu.UI
Close(); Close();
} }
private void OnChargingUpdated(bool charging)
{
if (charging)
Close();
}
public void OnSystemLoaded(CombatModeSystem system) public void OnSystemLoaded(CombatModeSystem system)
{ {
system.LocalPlayerCombatModeUpdated += OnCombatModeUpdated; system.LocalPlayerCombatModeUpdated += OnCombatModeUpdated;
@@ -225,5 +232,15 @@ namespace Content.Client.ContextMenu.UI
{ {
system.LocalPlayerCombatModeUpdated -= OnCombatModeUpdated; system.LocalPlayerCombatModeUpdated -= OnCombatModeUpdated;
} }
public void OnSystemLoaded(ChargeActionSystem system)
{
system.ChargingUpdated += OnChargingUpdated;
}
public void OnSystemUnloaded(ChargeActionSystem system)
{
system.ChargingUpdated -= OnChargingUpdated;
}
} }
} }

View File

@@ -22,6 +22,7 @@ using Robust.Client.UserInterface.Controls;
using Robust.Shared.Graphics.RSI; using Robust.Shared.Graphics.RSI;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Input.Binding; using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using static Content.Client.Actions.ActionsSystem; 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.Control;
using static Robust.Client.UserInterface.Controls.BaseButton; using static Robust.Client.UserInterface.Controls.BaseButton;
using static Robust.Client.UserInterface.Controls.LineEdit; using static Robust.Client.UserInterface.Controls.LineEdit;
using static Robust.Client.UserInterface.Controls.MultiselectOptionButton< using static Robust.Client.UserInterface.Controls.MultiselectOptionButton<Content.Client.UserInterface.Systems.Actions.Windows.ActionsWindow.Filters>;
Content.Client.UserInterface.Systems.Actions.Windows.ActionsWindow.Filters>;
using static Robust.Client.UserInterface.Controls.TextureRect; using static Robust.Client.UserInterface.Controls.TextureRect;
using static Robust.Shared.Input.Binding.PointerInputCmdHandler; using static Robust.Shared.Input.Binding.PointerInputCmdHandler;
@@ -128,25 +128,45 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
} }
builder builder
.Bind(ContentKeyFunctions.OpenActionsMenu, .Bind(ContentKeyFunctions.OpenActionsMenu, InputCmdHandler.FromDelegate(_ => ToggleWindow()))
InputCmdHandler.FromDelegate(_ => ToggleWindow())) .BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(TargetingOnUse, outsidePrediction: true), typeof(ConstructionSystem), typeof(DragDropSystem))
.BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(TargetingOnUse, outsidePrediction: true), .BindBefore(ContentKeyFunctions.AltActivateItemInWorld, new PointerInputCmdHandler(AltUse, outsidePrediction: true))
typeof(ConstructionSystem), typeof(DragDropSystem))
.BindBefore(EngineKeyFunctions.UIRightClick, new PointerInputCmdHandler(TargetingCancel, outsidePrediction: true))
.Register<ActionUIController>(); .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; return false;
// only do something for actual target-based actions if (_playerManager.LocalEntity is not { } user)
if (SelectingTargetFor == null)
return false; return false;
StopTargeting(); if (!EntityManager.TryGetComponent(user, out ActionsComponent? comp))
return true; 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> /// <summary>
@@ -179,28 +199,23 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
return !action.InteractOnMiss; return !action.InteractOnMiss;
} }
switch (action) return action switch
{ {
case WorldTargetActionComponent mapTarget: WorldTargetActionComponent mapTarget => TryTargetWorld(args.Coordinates, actionId, mapTarget, user, comp) ||
return TryTargetWorld(args, actionId, mapTarget, user, comp) || !mapTarget.InteractOnMiss; !mapTarget.InteractOnMiss,
EntityTargetActionComponent entTarget => TryTargetEntity(args.EntityUid, actionId, entTarget, user, comp) ||
case EntityTargetActionComponent entTarget: !entTarget.InteractOnMiss,
return TryTargetEntity(args, actionId, entTarget, user, comp) || !entTarget.InteractOnMiss; _ => false
};
default:
Logger.Error($"Unknown targeting action: {actionId.GetType()}");
return 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) if (_actionsSystem == null)
return false; return false;
var coords = args.Coordinates; if (!_actionsSystem.ValidateWorldTarget(user, coordinates, action))
if (!_actionsSystem.ValidateWorldTarget(user, coords, action))
{ {
// Invalid target. // Invalid target.
if (action.DeselectOnMiss) if (action.DeselectOnMiss)
@@ -213,14 +228,24 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
{ {
if (action.Event != null) if (action.Event != null)
{ {
action.Event.Target = coords; action.Event.Target = coordinates;
action.Event.Performer = user; action.Event.Performer = user;
} }
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime); _actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
} }
else 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) if (!action.Repeat)
StopTargeting(); StopTargeting();
@@ -228,14 +253,12 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
return true; 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) if (_actionsSystem == null)
return false; return false;
var entity = args.EntityUid; if (!_actionsSystem.ValidateEntityTarget(user, uid, action))
if (!_actionsSystem.ValidateEntityTarget(user, entity, action))
{ {
if (action.DeselectOnMiss) if (action.DeselectOnMiss)
StopTargeting(); StopTargeting();
@@ -247,14 +270,21 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
{ {
if (action.Event != null) if (action.Event != null)
{ {
action.Event.Target = entity; action.Event.Target = uid;
action.Event.Performer = user; action.Event.Performer = user;
} }
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime); _actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
} }
else 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) if (!action.Repeat)
StopTargeting(); 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) private void PopulateActions(IEnumerable<(EntityUid Id, BaseActionComponent Comp)> actions)
{ {
if (_window is not { Disposed: false, IsOpen: true }) if (_window is not { Disposed: false, IsOpen: true })
@@ -432,7 +456,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
existing.Add(button); existing.Add(button);
} }
int i = 0; var i = 0;
foreach (var action in actions) foreach (var action in actions)
{ {
if (i < existing.Count) if (i < existing.Count)

View File

@@ -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;
}
}

View File

@@ -17,8 +17,6 @@ using Robust.Client.State;
using Robust.Shared.Input; using Robust.Shared.Input;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Weapons.Melee; namespace Content.Client.Weapons.Melee;

View File

@@ -0,0 +1,7 @@
using Content.Shared._White.Wizard.ScrollSystem;
namespace Content.Client._White.Wizard.Scrolls;
public sealed class ScrollSystem : SharedScrollSystem
{
}

View File

@@ -69,7 +69,6 @@ namespace Content.Server.Bed.Sleep
emitSound.Sound = sleepSound.Snore; emitSound.Sound = sleepSound.Snore;
emitSound.PlayChance = sleepSound.Chance; emitSound.PlayChance = sleepSound.Chance;
emitSound.RollInterval = sleepSound.Interval; emitSound.RollInterval = sleepSound.Interval;
emitSound.PopUp = sleepSound.PopUp;
} }
return; return;

View File

@@ -30,7 +30,12 @@ public sealed class DamagePopupSystem : EntitySystem
DamagePopupType.Hit => "!", DamagePopupType.Hit => "!",
_ => "Invalid type", _ => "Invalid type",
}; };
if (args.Origin.HasValue)
_popupSystem.PopupEntity(msg, uid, args.Origin.Value);
else
_popupSystem.PopupEntity(msg, uid); _popupSystem.PopupEntity(msg, uid);
} }
} }
} }

View 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;
}

View 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";
}

View 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;
}
}

View File

@@ -297,7 +297,7 @@ public sealed class ExecutionSystem : EntitySystem
{ {
if (attemptEv.Message != null) if (attemptEv.Message != null)
{ {
_popupSystem.PopupClient(attemptEv.Message, weapon, attacker); _popupSystem.PopupEntity(attemptEv.Message, weapon, attacker);
return; return;
} }
} }

View File

@@ -90,7 +90,7 @@ public sealed partial class NukeopsRuleComponent : Component
/// This amount of TC will be given to each nukie /// This amount of TC will be given to each nukie
/// </summary> /// </summary>
[DataField] [DataField]
public int WarTCAmountPerNukie = 20; public int WarTCAmountPerNukie = 30;
/// <summary> /// <summary>
/// Time allowed for declaration of war /// Time allowed for declaration of war

View File

@@ -120,7 +120,7 @@ public sealed class InteractionPopupSystem : EntitySystem
_popupSystem.PopupEntity(msg, uid, user); _popupSystem.PopupEntity(msg, uid, user);
_popupSystem.PopupEntity(msgOthers, uid, Filter.PvsExcept(user, entityManager: EntityManager), true); _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. _popupSystem.PopupEntity(msg, uid, user); //play only for the initiating entity.
if (sfx is not null) //not all cases will have sound. if (sfx is not null) //not all cases will have sound.

View File

@@ -57,7 +57,6 @@ public sealed class LightningSystem : SharedLightningSystem
} }
} }
/// <summary> /// <summary>
/// Looks for objects with a LightningTarget component in the radius, prioritizes them, and hits the highest priority targets with lightning. /// Looks for objects with a LightningTarget component in the radius, prioritizes them, and hits the highest priority targets with lightning.
/// </summary> /// </summary>
@@ -78,9 +77,9 @@ public sealed class LightningSystem : SharedLightningSystem
_random.Shuffle(targets); _random.Shuffle(targets);
targets.Sort((x, y) => y.Priority.CompareTo(x.Priority)); targets.Sort((x, y) => y.Priority.CompareTo(x.Priority));
int shootedCount = 0; var shootCount = 0;
int count = -1; var count = -1;
while(shootedCount < boltCount) while(shootCount < boltCount)
{ {
count++; count++;
@@ -95,7 +94,7 @@ public sealed class LightningSystem : SharedLightningSystem
{ {
ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].LightningResistance, triggerLightningEvents); ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].LightningResistance, triggerLightningEvents);
} }
shootedCount++; shootCount++;
} }
} }
} }

View File

@@ -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;
}

View File

@@ -1,17 +1,15 @@
using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Body.Systems; using Content.Server.Body.Systems;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.Doors.Systems; using Content.Server.Doors.Systems;
using Content.Server.Magic.Components;
using Content.Server.Weapons.Ranged.Systems; using Content.Server.Weapons.Ranged.Systems;
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Coordinates.Helpers; using Content.Shared.Coordinates.Helpers;
using Content.Shared.DoAfter;
using Content.Shared.Doors.Components; using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems; using Content.Shared.Doors.Systems;
using Content.Shared.Interaction.Events;
using Content.Shared.Magic; using Content.Shared.Magic;
using Content.Shared.Magic.Events; using Content.Shared.Magic.Events;
using Content.Shared.Maps; using Content.Shared.Maps;
@@ -21,6 +19,7 @@ using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Manager;
@@ -33,31 +32,25 @@ namespace Content.Server.Magic;
/// </summary> /// </summary>
public sealed class MagicSystem : EntitySystem 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 IComponentFactory _compFact = default!;
[Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly DoorBoltSystem _boltsSystem = default!; [Dependency] private readonly DoorBoltSystem _boltsSystem = default!;
[Dependency] private readonly BodySystem _bodySystem = default!; [Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedDoorSystem _doorSystem = 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 GunSystem _gunSystem = default!;
[Dependency] private readonly PhysicsSystem _physics = default!; [Dependency] private readonly PhysicsSystem _physics = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<SpellbookComponent, MapInitEvent>(OnInit);
SubscribeLocalEvent<SpellbookComponent, UseInHandEvent>(OnUse);
SubscribeLocalEvent<SpellbookComponent, SpellbookDoAfterEvent>(OnDoAfter);
SubscribeLocalEvent<InstantSpawnSpellEvent>(OnInstantSpawn); SubscribeLocalEvent<InstantSpawnSpellEvent>(OnInstantSpawn);
SubscribeLocalEvent<TeleportSpellEvent>(OnTeleportSpell); SubscribeLocalEvent<TeleportSpellEvent>(OnTeleportSpell);
SubscribeLocalEvent<KnockSpellEvent>(OnKnockSpell); SubscribeLocalEvent<KnockSpellEvent>(OnKnockSpell);
@@ -67,73 +60,8 @@ public sealed class MagicSystem : EntitySystem
SubscribeLocalEvent<ChangeComponentsSpellEvent>(OnChangeComponentsSpell); 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 #region Spells
/// <summary>
/// Handles the instant action (i.e. on the caster) attempting to spawn an entity.
/// </summary>
private void OnInstantSpawn(InstantSpawnSpellEvent args) private void OnInstantSpawn(InstantSpawnSpellEvent args)
{ {
if (args.Handled) if (args.Handled)
@@ -145,12 +73,12 @@ public sealed class MagicSystem : EntitySystem
{ {
var ent = Spawn(args.Prototype, position.SnapToGrid(EntityManager, _mapManager)); var ent = Spawn(args.Prototype, position.SnapToGrid(EntityManager, _mapManager));
if (args.PreventCollideWithCaster) if (!args.PreventCollideWithCaster)
{ continue;
var comp = EnsureComp<PreventCollideComponent>(ent); var comp = EnsureComp<PreventCollideComponent>(ent);
comp.Uid = args.Performer; comp.Uid = args.Performer;
} }
}
Speak(args); Speak(args);
args.Handled = true; args.Handled = true;
@@ -166,22 +94,17 @@ public sealed class MagicSystem : EntitySystem
var xform = Transform(ev.Performer); var xform = Transform(ev.Performer);
// var userVelocity = _physics.GetMapLinearVelocity(ev.Performer); WD EDIT
foreach (var pos in GetSpawnPositions(xform, ev.Pos)) 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 = _transformSystem.ToMapCoordinates(pos);
var mapPos = pos.ToMap(EntityManager); var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out var grid)
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out var grid) // WD EDIT
? pos.WithEntityId(gridUid, EntityManager) ? 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; var userVelocity = Vector2.Zero;
if (grid != null && TryComp(gridUid, out PhysicsComponent? physics)) if (grid != null && TryComp(gridUid, out PhysicsComponent? physics))
userVelocity = physics.LinearVelocity; userVelocity = physics.LinearVelocity;
// WD EDIT
var ent = Spawn(ev.Prototype, spawnCoords); var ent = Spawn(ev.Prototype, spawnCoords);
var direction = ev.Target.ToMapPos(EntityManager, _transformSystem) - var direction = ev.Target.ToMapPos(EntityManager, _transformSystem) -
@@ -194,7 +117,9 @@ public sealed class MagicSystem : EntitySystem
{ {
if (ev.Handled) if (ev.Handled)
return; return;
ev.Handled = true; ev.Handled = true;
Speak(ev); Speak(ev);
foreach (var toRemove in ev.ToRemove) foreach (var toRemove in ev.ToRemove)
@@ -209,75 +134,12 @@ public sealed class MagicSystem : EntitySystem
continue; continue;
var component = (Component) _compFact.GetComponent(name); var component = (Component) _compFact.GetComponent(name);
component.Owner = ev.Target;
var temp = (object) component; var temp = (object) component;
_seriMan.CopyTo(data.Component, ref temp); _serializationManager.CopyTo(data.Component, ref temp);
EntityManager.AddComponent(ev.Target, (Component) 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) private void OnTeleportSpell(TeleportSpellEvent args)
{ {
if (args.Handled) if (args.Handled)
@@ -285,19 +147,16 @@ public sealed class MagicSystem : EntitySystem
var transform = Transform(args.Performer); 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); _transformSystem.SetCoordinates(args.Performer, args.Target);
transform.AttachToGridOrMap(); _transformSystem.AttachToGridOrMap(args.Performer);
_audio.PlayPvs(args.BlinkSound, args.Performer, AudioParams.Default.WithVolume(args.BlinkVolume)); _audio.PlayPvs(args.BlinkSound, args.Performer, AudioParams.Default.WithVolume(args.BlinkVolume));
Speak(args); Speak(args);
args.Handled = true; args.Handled = true;
} }
/// <summary>
/// Opens all doors within range
/// </summary>
/// <param name="args"></param>
private void OnKnockSpell(KnockSpellEvent args) private void OnKnockSpell(KnockSpellEvent args)
{ {
if (args.Handled) if (args.Handled)
@@ -306,13 +165,11 @@ public sealed class MagicSystem : EntitySystem
args.Handled = true; args.Handled = true;
Speak(args); Speak(args);
//Get the position of the player
var transform = Transform(args.Performer); var transform = Transform(args.Performer);
var coords = transform.Coordinates; var coords = transform.Coordinates;
_audio.PlayPvs(args.KnockSound, args.Performer, AudioParams.Default.WithVolume(args.KnockVolume)); _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)) foreach (var entity in _lookup.GetEntitiesInRange(coords, args.Range))
{ {
if (TryComp<DoorBoltComponent>(entity, out var bolts)) if (TryComp<DoorBoltComponent>(entity, out var bolts))
@@ -329,9 +186,10 @@ public sealed class MagicSystem : EntitySystem
return; return;
ev.Handled = true; ev.Handled = true;
Speak(ev); 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; var impulseVector = direction * 10000;
_physics.ApplyLinearImpulse(ev.Target, impulseVector); _physics.ApplyLinearImpulse(ev.Target, impulseVector);
@@ -339,28 +197,17 @@ public sealed class MagicSystem : EntitySystem
if (!TryComp<BodyComponent>(ev.Target, out var body)) if (!TryComp<BodyComponent>(ev.Target, out var body))
return; return;
var ents = _bodySystem.GibBody(ev.Target, true, body); var entities = _bodySystem.GibBody(ev.Target, true, body);
if (!ev.DeleteNonBrainParts) if (!ev.DeleteNonBrainParts)
return; return;
foreach (var part in ents) foreach (var part in entities.Where(part => HasComp<BodyComponent>(part) && !HasComp<BrainComponent>(part)))
{
// just leaves a brain and clothes
if (HasComp<BodyComponent>(part) && !HasComp<BrainComponent>(part))
{ {
QueueDel(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) private void OnWorldSpawn(WorldSpawnSpellEvent args)
{ {
if (args.Handled) if (args.Handled)
@@ -373,24 +220,85 @@ public sealed class MagicSystem : EntitySystem
args.Handled = true; args.Handled = true;
} }
/// <summary> #endregion
/// Loops through a supplied list of entity prototypes and spawns them
/// </summary> #region Helpers
/// <remarks>
/// If an offset of 0, 0 is supplied then the entities will all spawn on the same tile. public List<EntityCoordinates> GetSpawnPositions(TransformComponent casterXform, MagicSpawnData data)
/// 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)
{ {
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; 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. // TODO: Share this code with instant because they're both doing similar things for positioning.
var entity = Spawn(proto, offsetCoords); var entity = Spawn(proto, offsetCoords);
@@ -404,8 +312,6 @@ public sealed class MagicSystem : EntitySystem
} }
} }
#endregion
private void Speak(BaseActionEvent args) private void Speak(BaseActionEvent args)
{ {
if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech)) 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), _chat.TrySendInGameICMessage(args.Performer, Loc.GetString(speak.Speech),
InGameICChatType.Speak, false); InGameICChatType.Speak, false);
} }
#endregion
} }

View File

@@ -84,9 +84,18 @@ namespace Content.Server.Power.EntitySystems
while (query.MoveNext(out var uid, out var comp, out var batt)) while (query.MoveNext(out var uid, out var comp, out var batt))
{ {
if (!comp.AutoRecharge) continue; if (!comp.AutoRecharge) continue;
if (comp.AutoRechargeRate > 0)
{
if (batt.IsFullyCharged) continue; if (batt.IsFullyCharged) continue;
SetCharge(uid, batt.CurrentCharge + comp.AutoRechargeRate * frameTime, batt); 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> /// <summary>

View File

@@ -3,7 +3,6 @@ using Content.Server._White.AspectsSystem.Aspects;
using Content.Shared.Chat.Prototypes; using Content.Shared.Chat.Prototypes;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
@@ -32,12 +31,6 @@ public sealed partial class VocalComponent : Component
[DataField("wilhelmProbability")] [DataField("wilhelmProbability")]
public float WilhelmProbability = 0.0002f; public float WilhelmProbability = 0.0002f;
[DataField("screamAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string ScreamAction = "ActionScream";
[DataField("screamActionEntity")]
public EntityUid? ScreamActionEntity;
/// <summary> /// <summary>
/// Currently loaded emote sounds prototype, based on entity sex. /// Currently loaded emote sounds prototype, based on entity sex.
/// Null if no valid prototype for entity sex was found. /// Null if no valid prototype for entity sex was found.

View File

@@ -1,10 +1,8 @@
using Content.Server.Actions;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.Speech.Components; using Content.Server.Speech.Components;
using Content.Shared.Chat.Prototypes; using Content.Shared.Chat.Prototypes;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Content.Shared.Speech; using Content.Shared.Speech;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -17,33 +15,13 @@ public sealed class VocalSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly ActionsSystem _actions = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<VocalComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<VocalComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<VocalComponent, SexChangedEvent>(OnSexChanged); SubscribeLocalEvent<VocalComponent, SexChangedEvent>(OnSexChanged);
SubscribeLocalEvent<VocalComponent, EmoteEvent>(OnEmote); 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) 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); 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) private bool TryPlayScreamSound(EntityUid uid, VocalComponent component)
{ {
if (_random.Prob(component.WilhelmProbability)) if (_random.Prob(component.WilhelmProbability))

View File

@@ -12,12 +12,12 @@ namespace Content.Server.Speech.Muting
public sealed class MutingSystem : EntitySystem public sealed class MutingSystem : EntitySystem
{ {
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<MutedComponent, SpeakAttemptEvent>(OnSpeakAttempt); SubscribeLocalEvent<MutedComponent, SpeakAttemptEvent>(OnSpeakAttempt);
SubscribeLocalEvent<MutedComponent, EmoteEvent>(OnEmote, before: new[] { typeof(VocalSystem) }); 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) private void OnEmote(EntityUid uid, MutedComponent component, ref EmoteEvent args)
@@ -30,20 +30,6 @@ namespace Content.Server.Speech.Muting
args.Handled = true; 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) private void OnSpeakAttempt(EntityUid uid, MutedComponent component, SpeakAttemptEvent args)
{ {
// TODO something better than this. // TODO something better than this.

View File

@@ -323,7 +323,7 @@ namespace Content.Server.VendingMachines
if (_accessReader.IsAllowed(sender, uid, accessReader) || HasComp<EmaggedComponent>(uid)) if (_accessReader.IsAllowed(sender, uid, accessReader) || HasComp<EmaggedComponent>(uid))
return true; 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); Deny(uid, vendComponent);
return false; return false;
} }
@@ -352,7 +352,7 @@ namespace Content.Server.VendingMachines
if (entry == null) if (entry == null)
{ {
if (sender.HasValue) 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); Deny(uid, vendComponent);
@@ -362,7 +362,7 @@ namespace Content.Server.VendingMachines
if (entry.Amount <= 0) if (entry.Amount <= 0)
{ {
if (sender.HasValue) 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); Deny(uid, vendComponent);
return; return;

View File

@@ -1,7 +1,7 @@
using Content.Server.Popups;
using Content.Server.Xenoarchaeology.Equipment.Components; using Content.Server.Xenoarchaeology.Equipment.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts; using Content.Server.Xenoarchaeology.XenoArtifacts;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Timing; using Content.Shared.Timing;
namespace Content.Server.Xenoarchaeology.Equipment.Systems; namespace Content.Server.Xenoarchaeology.Equipment.Systems;
@@ -9,7 +9,7 @@ namespace Content.Server.Xenoarchaeology.Equipment.Systems;
public sealed class NodeScannerSystem : EntitySystem public sealed class NodeScannerSystem : EntitySystem
{ {
[Dependency] private readonly UseDelaySystem _useDelay = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
@@ -36,7 +36,7 @@ public sealed class NodeScannerSystem : EntitySystem
return; return;
// WD edit // WD edit
_popupSystem.PopupClient(Loc.GetString("node-scan-popup", _popupSystem.PopupEntity(Loc.GetString("node-scan-popup",
("id", $"{artifact.CurrentNodeId}")), target, args.User); ("id", $"{artifact.CurrentNodeId}")), target, args.User);
} }
} }

View File

@@ -4,7 +4,7 @@ namespace Content.Server._White.ChangeTemperatureOnCollide;
public sealed partial class ClothingTemperatureAdjustComponent : Component public sealed partial class ClothingTemperatureAdjustComponent : Component
{ {
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField, ViewVariables(VVAccess.ReadWrite)]
public float Rate = 1f; public float Rate = 2f;
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField, ViewVariables(VVAccess.ReadWrite)]
public float TargetTemperature = 310.15f; public float TargetTemperature = 310.15f;

View File

@@ -57,7 +57,7 @@ public sealed partial class CultSystem
if (comp.SelectedEmpowers.Count >= 1) 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; return;
} }
@@ -106,7 +106,7 @@ public sealed partial class CultSystem
{ {
if (ent.Comp.SelectedEmpowers.Count == 0) 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; return;
} }

View File

@@ -147,12 +147,12 @@ public sealed class CultistFactorySystem : EntitySystem
if (args.IsAnchored) if (args.IsAnchored)
{ {
_transform.Unanchor(target, xform); _transform.Unanchor(target, xform);
_popup.PopupClient(Loc.GetString("anchorable-unanchored"), uid, args.User); _popup.PopupEntity(Loc.GetString("anchorable-unanchored"), uid, args.User);
} }
else else
{ {
_transform.AnchorEntity(target, xform); _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); _audio.PlayPvs("/Audio/Items/ratchet.ogg", uid);

View File

@@ -69,6 +69,11 @@ public sealed class MindslaveSystem : SharedMindslaveSystem
private void OnMindslaveRemoved(Entity<SubdermalImplantComponent> ent, ref SubdermalImplantRemoved args) private void OnMindslaveRemoved(Entity<SubdermalImplantComponent> ent, ref SubdermalImplantRemoved args)
{ {
if (!Tag.HasTag(ent.Owner, MindslaveTag))
{
return;
}
if (!TryComp(args.Target, out MindSlaveComponent? mindslave)) if (!TryComp(args.Target, out MindSlaveComponent? mindslave))
{ {
return; return;

View File

@@ -2,6 +2,8 @@
using Content.Shared.Eye; using Content.Shared.Eye;
using Content.Shared.Movement.Systems; using Content.Shared.Movement.Systems;
using Content.Shared.Physics; using Content.Shared.Physics;
using Content.Shared.Stealth;
using Content.Shared.Stealth.Components;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Physics; using Robust.Shared.Physics;
using Robust.Shared.Physics.Systems; using Robust.Shared.Physics.Systems;
@@ -10,11 +12,10 @@ namespace Content.Server._White.IncorporealSystem;
public sealed class IncorporealSystem : EntitySystem public sealed class IncorporealSystem : EntitySystem
{ {
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!; [Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly VisibilitySystem _visibilitySystem = default!; [Dependency] private readonly VisibilitySystem _visibilitySystem = default!;
[Dependency] private readonly SharedStealthSystem _stealth = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -41,6 +42,9 @@ public sealed class IncorporealSystem : EntitySystem
_visibilitySystem.RefreshVisibility(uid); _visibilitySystem.RefreshVisibility(uid);
} }
Spawn("EffectEmpPulse", Transform(uid).Coordinates);
EnsureComp<StealthComponent>(uid);
_stealth.SetVisibility(uid, -1);
_movement.RefreshMovementSpeedModifiers(uid); _movement.RefreshMovementSpeedModifiers(uid);
} }
@@ -50,8 +54,8 @@ public sealed class IncorporealSystem : EntitySystem
{ {
var fixture = fixtures.Fixtures.First(); var fixture = fixtures.Fixtures.First();
_physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) (CollisionGroup.FlyingMobMask | CollisionGroup.GhostImpassable), fixtures); _physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) (CollisionGroup.MobMask | CollisionGroup.GhostImpassable), fixtures);
_physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, (int) CollisionGroup.FlyingMobLayer, fixtures); _physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, (int) CollisionGroup.MobLayer, fixtures);
} }
if (TryComp<VisibilityComponent>(uid, out var visibility)) if (TryComp<VisibilityComponent>(uid, out var visibility))
@@ -62,6 +66,10 @@ public sealed class IncorporealSystem : EntitySystem
} }
component.MovementSpeedBuff = 1; component.MovementSpeedBuff = 1;
Spawn("EffectEmpPulse", Transform(uid).Coordinates);
_stealth.SetVisibility(uid, 1);
RemComp<StealthComponent>(uid);
_movement.RefreshMovementSpeedModifiers(uid); _movement.RefreshMovementSpeedModifiers(uid);
} }

View File

@@ -21,6 +21,8 @@ public sealed class RandomDamageSystem : EntitySystem
private void HandleHit(Entity<RandomDamageComponent> ent, ref MeleeHitEvent args) private void HandleHit(Entity<RandomDamageComponent> ent, ref MeleeHitEvent args)
{ {
var damage = _random.NextFloat() * ent.Comp.Max; 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); args.BonusDamage = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Slash"), damage);
} }
} }

View 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
}

View File

@@ -0,0 +1,6 @@
namespace Content.Server._White.Wizard.Magic.Amaterasu;
[RegisterComponent]
public sealed partial class AmaterasuComponent : Component
{
}

View File

@@ -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);
}
}
}

View File

@@ -0,0 +1,7 @@
namespace Content.Server._White.Wizard.Magic.Other;
[RegisterComponent]
public sealed partial class InstantRecallComponent : Component
{
public EntityUid? Item;
}

View File

@@ -0,0 +1,4 @@
namespace Content.Server._White.Wizard.Magic.TeslaProjectile;
[RegisterComponent]
public sealed partial class TeslaProjectileComponent : Component {}

View File

@@ -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);
}
}

View 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
}

View 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);
}

View File

@@ -81,8 +81,10 @@ public sealed class GetItemActionsEvent : EntityEventArgs
public sealed class RequestPerformActionEvent : EntityEventArgs public sealed class RequestPerformActionEvent : EntityEventArgs
{ {
public readonly NetEntity Action; public readonly NetEntity Action;
public readonly NetEntity? EntityTarget; public NetEntity? EntityTarget;
public readonly NetCoordinates? EntityCoordinatesTarget; public readonly NetCoordinates? EntityCoordinatesTarget;
public ActionUseType ActionUseType = ActionUseType.Default;
public int ChargeLevel;
public RequestPerformActionEvent(NetEntity action) public RequestPerformActionEvent(NetEntity action)
{ {
@@ -148,6 +150,8 @@ public abstract partial class WorldTargetActionEvent : BaseActionEvent
/// The coordinates of the location that the user targeted. /// The coordinates of the location that the user targeted.
/// </summary> /// </summary>
public EntityCoordinates Target; public EntityCoordinates Target;
public EntityUid TargetUid;
} }
/// <summary> /// <summary>
@@ -161,4 +165,18 @@ public abstract partial class BaseActionEvent : HandledEntityEventArgs
/// The user performing the action. /// The user performing the action.
/// </summary> /// </summary>
public EntityUid Performer; 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.
} }

View File

@@ -1,4 +1,5 @@
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Robust.Shared.Audio;
namespace Content.Shared.Actions; 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". /// over lay in place of the currently held item "held item".
/// </summary> /// </summary>
[DataField("targetingIndicator")] public bool TargetingIndicator = true; [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;
} }

View File

@@ -413,6 +413,8 @@ public abstract class SharedActionsSystem : EntitySystem
if (worldAction.Event != null) if (worldAction.Event != null)
{ {
worldAction.Event.Target = entityCoordinatesTarget; worldAction.Event.Target = entityCoordinatesTarget;
if (ev.EntityTarget != null)
worldAction.Event.TargetUid = GetEntity(ev.EntityTarget.Value);
Dirty(actionEnt, worldAction); Dirty(actionEnt, worldAction);
performEvent = worldAction.Event; performEvent = worldAction.Event;
} }
@@ -430,7 +432,12 @@ public abstract class SharedActionsSystem : EntitySystem
} }
if (performEvent != null) if (performEvent != null)
{
performEvent.Performer = user; performEvent.Performer = user;
performEvent.Action = actionEnt;
performEvent.ActionUseType = ev.ActionUseType;
performEvent.ChargeLevel = ev.ChargeLevel;
}
// All checks passed. Perform the action! // All checks passed. Perform the action!
PerformAction(user, component, actionEnt, action, performEvent, curTime); 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="performer">Entity to receive the actions</param>
/// <param name="actions">The actions to add</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="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) public void GrantActions(EntityUid performer, IEnumerable<EntityUid> actions, EntityUid container, ActionsComponent? comp = null, ActionsContainerComponent? containerComp = null)
{ {
if (!Resolve(container, ref containerComp)) if (!Resolve(container, ref containerComp))

View File

@@ -22,10 +22,4 @@ public sealed partial class SleepEmitSoundComponent : Component
/// </summary> /// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)] [DataField, ViewVariables(VVAccess.ReadWrite)]
public float Chance = 0.33f; public float Chance = 0.33f;
/// <summary>
/// Popup for snore (e.g. Zzz...)
/// </summary>
[DataField, ViewVariables(VVAccess.ReadWrite)]
public LocId PopUp = "sleep-onomatopoeia";
} }

View File

@@ -11,6 +11,9 @@ public sealed partial class StaminaDamageOnCollideComponent : Component
[ViewVariables(VVAccess.ReadWrite), DataField("damage")] [ViewVariables(VVAccess.ReadWrite), DataField("damage")]
public float Damage = 55f; public float Damage = 55f;
[ViewVariables(VVAccess.ReadWrite), DataField]
public bool IgnoreResistances = true;
[DataField("sound")] [DataField("sound")]
public SoundSpecifier? Sound; public SoundSpecifier? Sound;
} }

View File

@@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using Content.Shared._White.StaminaProtection;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.CombatMode; using Content.Shared.CombatMode;
@@ -204,7 +205,18 @@ public sealed partial class StaminaSystem : EntitySystem
if (ev.Cancelled) if (ev.Cancelled)
return; 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) private void SetStaminaAlert(EntityUid uid, StaminaComponent? component = null)

View File

@@ -57,6 +57,11 @@ public sealed partial class ImplanterComponent : Component
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public bool ImplantOnly; public bool ImplantOnly;
// WD START
[DataField]
public bool SingleUse = true;
// WD END
/// <summary> /// <summary>
/// The current mode of the implanter /// The current mode of the implanter
/// Mode is changed automatically depending if it implants or draws /// Mode is changed automatically depending if it implants or draws

View File

@@ -117,6 +117,9 @@ public abstract class SharedImplanterSystem : EntitySystem
_container.Insert(implant.Value, implantContainer); _container.Insert(implant.Value, implantContainer);
RaiseLocalEvent(implant.Value, new SubdermalImplantInserted(user, target, implantComp)); //WD EDIT 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) if (component.CurrentMode == ImplanterToggleMode.Inject && !component.ImplantOnly)
DrawMode(implanter, component); DrawMode(implanter, component);
else else
@@ -205,7 +208,8 @@ public abstract class SharedImplanterSystem : EntitySystem
break; 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); ImplantMode(implanter, component);
Dirty(implanter, component); Dirty(implanter, component);

View File

@@ -1,3 +1,4 @@
using Content.Shared._White.StaminaProtection;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Electrocution; using Content.Shared.Electrocution;
@@ -27,6 +28,7 @@ public partial class InventorySystem
SubscribeLocalEvent<InventoryComponent, SeeIdentityAttemptEvent>(RelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, SeeIdentityAttemptEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent);
SubscribeLocalEvent<InventoryComponent, AdjustTemperatureEvent>(RelayInventoryEvent); // WD SubscribeLocalEvent<InventoryComponent, AdjustTemperatureEvent>(RelayInventoryEvent); // WD
SubscribeLocalEvent<InventoryComponent, StaminaDamageModifyEvent>(RelayInventoryEvent); // WD
SubscribeLocalEvent<InventoryComponent, GetDefaultRadioChannelEvent>(RelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, GetDefaultRadioChannelEvent>(RelayInventoryEvent);
// by-ref events // by-ref events

View File

@@ -1,7 +0,0 @@
using Content.Shared.Actions;
namespace Content.Shared.Speech;
public sealed partial class ScreamActionEvent : InstantActionEvent
{
}

View File

@@ -123,6 +123,14 @@ public abstract partial class SharedGunSystem
private void OnGunSelected(EntityUid uid, GunComponent component, HandSelectedEvent args) 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; var fireDelay = 1f / component.FireRateModified;
if (fireDelay.Equals(0f)) if (fireDelay.Equals(0f))
return; return;

View File

@@ -48,5 +48,10 @@ public sealed class BackstabSystem : EntitySystem
args.PenetrateArmor = ent.Comp.PenetrateArmor; 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);
} }
} }

View 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;
}
}

View File

@@ -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;
}

View File

@@ -0,0 +1,5 @@
namespace Content.Shared._White.Wizard.Charging;
public abstract class SharedChargingSystem : EntitySystem
{
}

View 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;
}

View File

@@ -0,0 +1,9 @@
using Robust.Shared.GameStates;
namespace Content.Shared._White.Wizard.Magic;
[RegisterComponent, NetworkedComponent]
public sealed partial class WizardClothesComponent : Component
{
}

View 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;
}

View File

@@ -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
}

View 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

View File

@@ -152,3 +152,23 @@
license: "CC0-1.0" license: "CC0-1.0"
copyright: "dakamakat on freesound.org" copyright: "dakamakat on freesound.org"
source: "https://freesound.org/people/Dakamakat/sounds/717370/" 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/"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1914,3 +1914,260 @@
id: 177 id: 177
time: '2024-03-02T14:17:52.0000000+00:00' time: '2024-03-02T14:17:52.0000000+00:00'
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/152 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

View 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

View File

@@ -1,6 +1,5 @@
action-name-wake = Wake up action-name-wake = Wake up
sleep-onomatopoeia = Zzz...
sleep-examined = [color=lightblue]{CAPITALIZE(SUBJECT($target))} {CONJUGATE-BE($target)} asleep.[/color] sleep-examined = [color=lightblue]{CAPITALIZE(SUBJECT($target))} {CONJUGATE-BE($target)} asleep.[/color]
wake-other-success = You shake {THE($target)} awake. wake-other-success = You shake {THE($target)} awake.

View File

@@ -32,6 +32,7 @@ research-technology-wave-particle-harnessing = Wave Particle Harnessing
research-technology-advanced-riot-control = Advanced Riot Control research-technology-advanced-riot-control = Advanced Riot Control
research-technology-portable-microfusion-weaponry = Portable Microfusion Weaponry research-technology-portable-microfusion-weaponry = Portable Microfusion Weaponry
research-technology-experimental-battery-ammo = Experimental Battery Ammo research-technology-experimental-battery-ammo = Experimental Battery Ammo
research-technology-energy_barriers = Energy Barriers
research-technology-basic-shuttle-armament = Shuttle basic armament research-technology-basic-shuttle-armament = Shuttle basic armament
research-technology-advanced-shuttle-weapon = Advanced shuttle weapons research-technology-advanced-shuttle-weapon = Advanced shuttle weapons

View File

@@ -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-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-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 # Misc
uplink-cyberpen-name = Cybersun Pen 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. uplink-cyberpen-desc = Cybersun's legal department pen, invaluable for forging documents and escaping prisons. Smells vaguely of hard-light and war profiteering.

View File

@@ -0,0 +1 @@
backstab-damage-betrayal-dagger = Удар в спину: {$damage}!

View File

@@ -2,7 +2,6 @@ action-name-sleep = Спать
action-desc-sleep = Лечь спать. action-desc-sleep = Лечь спать.
action-name-wake = Проснуться action-name-wake = Проснуться
action-desc-wake = Перестать спать. action-desc-wake = Перестать спать.
sleep-onomatopoeia = Zzz...
sleep-examined = [color=lightblue]{ CAPITALIZE($target) } спит.[/color] sleep-examined = [color=lightblue]{ CAPITALIZE($target) } спит.[/color]
wake-other-success = Вы разбудили { $target }. wake-other-success = Вы разбудили { $target }.
wake-other-failure = Вы тормошите { $target }, но { $target } не просыпается. wake-other-failure = Вы тормошите { $target }, но { $target } не просыпается.

View File

@@ -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 } bonkable-success-message-user = Вы стукаете свою голову об { $bonkable }

View File

@@ -1,2 +1,2 @@
cluwne-transform = { CAPITALIZE(THE($target)) } превратился в клувна! cluwne-transform = { CAPITALIZE($target) } превратился в клувна!
cluwne-name-prefix = Клувнифицированный { $target } cluwne-name-prefix = Клувнифицированный { $target }

View File

@@ -3,7 +3,7 @@ comp-storage-cant-insert = Невозможно поместить.
comp-storage-insufficient-capacity = Недостаточная вместимость. comp-storage-insufficient-capacity = Недостаточная вместимость.
comp-storage-invalid-container = Неправильный контейнер для этого предмета. comp-storage-invalid-container = Неправильный контейнер для этого предмета.
comp-storage-anchored-failure = Невозможно поместить закрепленный предмет. comp-storage-anchored-failure = Невозможно поместить закрепленный предмет.
comp-storage-cant-drop = Вы не можете отпустить { THE($entity) }! comp-storage-cant-drop = Вы не можете отпустить { $entity }!
comp-storage-window-title = Предмет хранилище comp-storage-window-title = Предмет хранилище
comp-storage-window-volume = Занято: { $usedVolume }/{ $maxVolume }, предметов: { $itemCount } comp-storage-window-volume = Занято: { $usedVolume }/{ $maxVolume }, предметов: { $itemCount }
comp-storage-window-volume-unlimited = Предметов: { $itemCount } comp-storage-window-volume-unlimited = Предметов: { $itemCount }

View File

@@ -179,7 +179,7 @@ flavor-complex-rocksandstones = как скалы и камни
## Basic drinks ## Basic drinks
flavor-complex-water = как вода flavor-complex-water = как вода
flavor-complex-water = как вода flavor-complex-beer = как моча
flavor-complex-cognac = как сухой пряный алкоголь flavor-complex-cognac = как сухой пряный алкоголь
flavor-complex-mead = как забродивший мёд flavor-complex-mead = как забродивший мёд
flavor-complex-vermouth = как виноградная мякоть flavor-complex-vermouth = как виноградная мякоть

View File

@@ -1,10 +1,10 @@
mopping-system-tool-full = { CAPITALIZE($used) } полная! mopping-system-tool-full = { CAPITALIZE($used) } полная!
mopping-system-target-container-empty-water = { CAPITALIZE(THE($target)) } не имеет воды! mopping-system-target-container-empty-water = { CAPITALIZE($target) } не имеет воды!
mopping-system-puddle-space = { THE($used) } заполнено водой mopping-system-puddle-space = { $used } заполнено водой
mopping-system-puddle-evaporate = { THE($target) } испаряется mopping-system-puddle-evaporate = { $target } испаряется
mopping-system-no-water = { THE($used) } не имеет воды! mopping-system-no-water = { $used } не имеет воды!
mopping-system-full = { THE($used) } полное! mopping-system-full = { $used } полное!
mopping-system-empty = { THE($used) } пустое! mopping-system-empty = { $used } пустое!
mopping-system-puddle-diluted = Вы разбавляете лужу. mopping-system-puddle-diluted = Вы разбавляете лужу.
mopping-system-puddle-success = Вы убираете лужу. mopping-system-puddle-success = Вы убираете лужу.
mopping-system-release-to-floor = Вы выжимаете часть жидкости на пол. mopping-system-release-to-floor = Вы выжимаете часть жидкости на пол.

View File

@@ -1,8 +1,8 @@
drain-component-empty-verb-using-is-empty-message = { CAPITALIZE(THE($object)) } пусто! drain-component-empty-verb-using-is-empty-message = { CAPITALIZE($object) } пусто!
drain-component-empty-verb-target-is-full-message = { CAPITALIZE(THE($object)) } полный! drain-component-empty-verb-target-is-full-message = { CAPITALIZE($object) } полный!
drain-component-empty-verb-inhand = Пустой { $object } drain-component-empty-verb-inhand = Пустой { $object }
drain-component-examine-hint-full = [color="blue"]Он наполнен до краев. Может вантуз поможет?[/color] drain-component-examine-hint-full = [color="blue"]Он наполнен до краев. Может вантуз поможет?[/color]
drain-component-examine-volume = [color="blue"]Оставшееся место - { $volume }ед.[/color] drain-component-examine-volume = [color="blue"]Оставшееся место - { $volume }ед.[/color]
drain-component-unclog-fail = { CAPITALIZE(THE($object)) } все еще полон. drain-component-unclog-fail = { CAPITALIZE($object) } все еще полон.
drain-component-unclog-success = { CAPITALIZE(THE($object)) } очищается. drain-component-unclog-success = { CAPITALIZE($object) } очищается.
drain-component-unclog-notapplicable = { CAPITALIZE(THE($object)) } не забит. drain-component-unclog-notapplicable = { CAPITALIZE($object) } не забит.

View File

@@ -2,4 +2,4 @@ puddle-component-examine-is-slipper-text = Выглядит скользким.
puddle-component-examine-evaporating = Оно [color=#5E7C16]испаряемое[/color]. puddle-component-examine-evaporating = Оно [color=#5E7C16]испаряемое[/color].
puddle-component-examine-evaporating-partial = Оно [color=#FED83D]частично испаряемое[/color]. puddle-component-examine-evaporating-partial = Оно [color=#FED83D]частично испаряемое[/color].
puddle-component-examine-evaporating-no = Оно [color=#B02E26]не испаряемое[/color]. puddle-component-examine-evaporating-no = Оно [color=#B02E26]не испаряемое[/color].
puddle-component-slipped-touch-reaction = Вещества в { THE($puddle) } попадают на вашу кожу! puddle-component-slipped-touch-reaction = Вещества в { $puddle } попадают на вашу кожу!

View File

@@ -3,8 +3,8 @@
spill-target-verb-get-data-text = Выплеснуть содержимое spill-target-verb-get-data-text = Выплеснуть содержимое
spill-target-verb-activate-cannot-drain-message = Вы не можете ничего выплеснуть из { $owner }! spill-target-verb-activate-cannot-drain-message = Вы не можете ничего выплеснуть из { $owner }!
spill-target-verb-activate-is-empty-message = В { $owner } пусто! spill-target-verb-activate-is-empty-message = В { $owner } пусто!
spill-melee-hit-attacker = Вы проливаете { $amount }ед { THE($spillable) } на { THE($target) }! spill-melee-hit-attacker = Вы проливаете { $amount }ед { $spillable } на { $target }!
spill-melee-hit-others = { CAPITALIZE(THE($attacker)) } выливаете часть { THE($spillable) } на { THE($target) }! spill-melee-hit-others = { CAPITALIZE($attacker) } выливаете часть { $spillable } на { $target }!
spill-land-spilled-on-other = { CAPITALIZE(THE($spillable)) } проливает часть своего содержимого на { THE($target) }! spill-land-spilled-on-other = { CAPITALIZE($spillable) } проливает часть своего содержимого на { $target }!
spill-examine-is-spillable = Этот контейнер выглядит так, что его можно пролить. spill-examine-is-spillable = Этот контейнер выглядит так, что его можно пролить.
spill-examine-spillable-weapon = Вы могли бы выплеснуть это на кого-нибудь при атаке в ближнем бою. spill-examine-spillable-weapon = Вы могли бы выплеснуть это на кого-нибудь при атаке в ближнем бою.

View File

@@ -1,3 +1,3 @@
glue-success = { THE($target) } покрыли в клее. glue-success = { $target } покрыли в клее.
glued-name-prefix = Приклеенный { $target } glued-name-prefix = Приклеенный { $target }
glue-failure = { THE($target) } уже покрыт клеем. glue-failure = { $target } уже покрыт клеем.

View File

@@ -10,7 +10,7 @@ petting-success-bird = Вы гладите { $target } по { POSS-ADJ($target)
petting-success-cat = Вы гладите { $target } по { POSS-ADJ($target) } маленькой пушистой голове. petting-success-cat = Вы гладите { $target } по { POSS-ADJ($target) } маленькой пушистой голове.
petting-success-corrupted-corgi = В порыве самонадеянности, вы гладите { $target } по { POSS-ADJ($target) } маленькой проклятой голове. petting-success-corrupted-corgi = В порыве самонадеянности, вы гладите { $target } по { POSS-ADJ($target) } маленькой проклятой голове.
petting-success-crab = Вы гладите { $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-dog = Вы гладите { $target } по { POSS-ADJ($target) } мягкой пушистой голове.
petting-success-frog = Вы гладите { $target } по { POSS-ADJ($target) } маленькой скользкой голове. petting-success-frog = Вы гладите { $target } по { POSS-ADJ($target) } маленькой скользкой голове.
petting-success-goat = Вы гладите { $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-holo = Вы гладите { $target } по { POSS-ADJ($target) } металлической шипастой голове.
petting-success-dragon = Уворачиваясь от клыков, когтей, и пламени, вы гладите { $target } по { POSS-ADJ($target) } огромной чешуйчатой голове. petting-success-dragon = Уворачиваясь от клыков, когтей, и пламени, вы гладите { $target } по { POSS-ADJ($target) } огромной чешуйчатой голове.
petting-success-hamster = Вы гладите { $target } по { POSS-ADJ($target) } маленькой пушистой голове. petting-success-hamster = Вы гладите { $target } по { POSS-ADJ($target) } маленькой пушистой голове.
petting-success-bear = Вы неохотно гладите { THE($target) } по { POSS-ADJ($target) } мистической голове. petting-success-bear = Вы неохотно гладите { $target } по { POSS-ADJ($target) } мистической голове.
petting-success-slimes = Вы гладите { THE($target) } по { POSS-ADJ($target) } слизистой поверхности. petting-success-slimes = Вы гладите { $target } по { POSS-ADJ($target) } слизистой поверхности.
petting-success-snake = Вы гладите {THE($target)} по {POSS-ADJ($target)} чешуйчатой большой голове. petting-success-snake = Вы гладите { $target } по { POSS-ADJ($target) } чешуйчатой большой голове.
petting-success-monkey = Вы гладите {THE($target)} по {POSS-ADJ($target)} озорной маленькой голове. petting-success-monkey = Вы гладите { $target } по { POSS-ADJ($target) } озорной маленькой голове.
petting-success-nymph = Вы гладите {THE($target)} по {POSS-ADJ($target)} деревянной маленькой голове. petting-success-nymph = Вы гладите { $target } по { POSS-ADJ($target) } деревянной маленькой голове.
petting-failure-generic = Вы тянетесь погладить { $target }, но { $target } настороженно уклоняется от вас. petting-failure-generic = Вы тянетесь погладить { $target }, но { $target } настороженно уклоняется от вас.
petting-failure-bat = Вы тянетесь погладить { $target }, но { $target } очень трудно поймать! petting-failure-bat = Вы тянетесь погладить { $target }, но { $target } очень трудно поймать!
petting-failure-corrupted-corgi = Вы тянетесь погладить { $target }, но решаете, что лучше не надо. petting-failure-corrupted-corgi = Вы тянетесь погладить { $target }, но решаете, что лучше не надо.
petting-failure-crab = Вы тянетесь погладить { $target }, но { $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-goat = Вы тянетесь погладить { $target }, но { $target } упорно отказывается!
petting-failure-goose = Вы тянетесь погладить { $target }, но { $target } слишком ужасен! petting-failure-goose = Вы тянетесь погладить { $target }, но { $target } слишком ужасен!
petting-failure-possum = Вы тянетесь погладить { $target }, но на вас шипят и рычат. petting-failure-possum = Вы тянетесь погладить { $target }, но на вас шипят и рычат.
@@ -42,20 +42,20 @@ petting-failure-sloth = Вы тянетесь погладить { $target }, н
petting-failure-holo = Вы тянетесь погладить { $target }, но { $target } едва не пронзает шипами вашу руку! petting-failure-holo = Вы тянетесь погладить { $target }, но { $target } едва не пронзает шипами вашу руку!
petting-failure-dragon = Вы поднимаете руку, но { $target } издаёт рёв, и вы решаете, что не хотите стать кормом для карпов. petting-failure-dragon = Вы поднимаете руку, но { $target } издаёт рёв, и вы решаете, что не хотите стать кормом для карпов.
petting-failure-hamster = Вы тянетесь погладить { $target }, но { $target } пытается укусить вас за палец, и только ваши молниеносные рефлексы спасают вас от почти смертельной травмы. petting-failure-hamster = Вы тянетесь погладить { $target }, но { $target } пытается укусить вас за палец, и только ваши молниеносные рефлексы спасают вас от почти смертельной травмы.
petting-failure-bear = Вы протягиваете руку, чтобы погладить { THE($target) }, но { SUBJECT($target) } рычит, заставляя вас дважды подумать. petting-failure-bear = Вы протягиваете руку, чтобы погладить { $target }, но { SUBJECT($target) } рычит, заставляя вас дважды подумать.
petting-failure-monkey = Вы протягиваете руку, чтобы погладить {THE($target)}, но {SUBJECT($target)} чуть-ли не откусывает ваши пальцы! petting-failure-monkey = Вы протягиваете руку, чтобы погладить { $target }, но { SUBJECT($target) } чуть-ли не откусывает ваши пальцы!
petting-failure-nymph = Вы протягиваете руку, чтобы погладить {THE($target)}, но {POSS-ADJ($target)} отодвигает свои ветки от вас. petting-failure-nymph = Вы протягиваете руку, чтобы погладить { $target }, но { POSS-ADJ($target) } отодвигает свои ветки от вас.
petting-failure-shadow = Вы пытаетесь погладить {THE($target)}, но ваша рука проходит сквозь холодную темноту его тела. petting-failure-shadow = Вы пытаетесь погладить {$target}, но ваша рука проходит сквозь холодную темноту его тела.
## Knocking on windows ## Knocking on windows
petting-success-honkbot = Вы гладите { $target } по его скользкой металлической голове. 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-cleanbot = Вы гладите { $target } по его влажной металлической голове.
petting-success-medibot = Вы гладите { $target } по его стерильной металлической голове. petting-success-medibot = Вы гладите { $target } по его стерильной металлической голове.
petting-failure-honkbot = Вы тянетесь погладить { $target }, но { $target } хонкает и уворачивается! petting-failure-honkbot = Вы тянетесь погладить { $target }, но { $target } хонкает и уворачивается!
petting-failure-cleanbot = Вы тянетесь погладить { $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 } едва не пронзает вашу руку шприцом! petting-failure-medibot = Вы тянетесь погладить { $target }, но { $target } едва не пронзает вашу руку шприцом!
# Shown when knocking on a window # Shown when knocking on a window
hugging-success-generic = Вы обнимаете { $target }. hugging-success-generic = Вы обнимаете { $target }.

View File

@@ -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" doesn't make sense if the item is already in their inventory
pick-up-verb-get-data-text-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 = гигантский

View File

@@ -1,4 +1,4 @@
butcherable-different-tool = Вам понадобится другой инструмент для разделки { THE($target) }. butcherable-different-tool = Вам понадобится другой инструмент для разделки { $target }.
butcherable-knife-butchered-success = Вы разделываете { $target } с помощью { $knife }. butcherable-knife-butchered-success = Вы разделываете { $target } с помощью { $knife }.
butcherable-need-knife = Используйте острый предмет чтобы разделать { $target }. butcherable-need-knife = Используйте острый предмет чтобы разделать { $target }.
butcherable-not-in-container = Сперва достаньте { $target } из контейнера. butcherable-not-in-container = Сперва достаньте { $target } из контейнера.

View File

@@ -1,6 +1,6 @@
comp-kitchen-spike-deny-collect = На { CAPITALIZE($this) } уже что-то есть, закончите сначала собирать мясо! comp-kitchen-spike-deny-collect = На { CAPITALIZE($this) } уже что-то есть, закончите сначала собирать мясо!
comp-kitchen-spike-deny-butcher = { CAPITALIZE($victim) } не может быть разделан на { $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-deny-not-dead = { CAPITALIZE($victim) } не может быть разделан. { CAPITALIZE(SUBJECT($victim)) } { $victim } не умер!
comp-kitchen-spike-begin-hook-victim = { $user } начинает насаживать вас на { $this }! comp-kitchen-spike-begin-hook-victim = { $user } начинает насаживать вас на { $this }!
comp-kitchen-spike-begin-hook-self = Вы начинаете насаживать себя на { $this }! comp-kitchen-spike-begin-hook-self = Вы начинаете насаживать себя на { $this }!

View File

@@ -1,4 +1,4 @@
lube-success = { THE($target) } был покрыт смазкой! lube-success = { $target } был покрыт смазкой!
lubed-name-prefix = Смазал { $target } lubed-name-prefix = Смазал { $target }
lube-failure = Не могу покрыть { THE($target) } в смазке! lube-failure = Не могу покрыть { $target } в смазке!
lube-slip = { THE($target) } выскальзывает из твоих рук! lube-slip = { $target } выскальзывает из твоих рук!

View File

@@ -1,3 +1,3 @@
infant-name-prefix = младенец { $name } infant-name-prefix = младенец { $name }
reproductive-birth-popup = { CAPITALIZE(THE($parent)) } родила! reproductive-birth-popup = { CAPITALIZE($parent) } родила!
reproductive-laid-egg-popup = { CAPITALIZE(THE($parent)) } откладывает яйцо! reproductive-laid-egg-popup = { CAPITALIZE($parent) } откладывает яйцо!

View File

@@ -11,8 +11,8 @@ food-system-you-cannot-eat-any-more = В вас больше не лезет!
food-system-you-cannot-eat-any-more-other = В него больше не лезет! food-system-you-cannot-eat-any-more-other = В него больше не лезет!
food-system-try-use-food-is-empty = { $entity } пустая! food-system-try-use-food-is-empty = { $entity } пустая!
food-system-wrong-utensil = вы не можете есть { $food } с помощью { $utensil }. food-system-wrong-utensil = вы не можете есть { $food } с помощью { $utensil }.
food-system-cant-digest = Ты не можешь переварить { THE($entity) }! food-system-cant-digest = Ты не можешь переварить { $entity }!
food-system-cant-digest-other = Они не могут переварить { THE($entity) }! food-system-cant-digest-other = Они не могут переварить { $entity }!
food-system-verb-eat = Съесть food-system-verb-eat = Съесть
## Force feeding ## Force feeding

View File

@@ -1,7 +1,7 @@
vape-component-vape-success = Ты затянулся вейпом. vape-component-vape-success = Ты затянулся вейпом.
vape-component-vape-success-forced = { CAPITALIZE(THE($user)) } заставил тебя затянуться вейпом. vape-component-vape-success-forced = { CAPITALIZE($user) } заставил тебя затянуться вейпом.
vape-component-vape-success-user-forced = Вас успешно заставили затянуться { THE($target) }. vape-component-vape-success-user-forced = Вас успешно заставили затянуться { $target }.
vape-component-try-use-vape-forced = { CAPITALIZE(THE($user)) } пытается заставить тебя затянуться вейпом. vape-component-try-use-vape-forced = { CAPITALIZE($user) } пытается заставить тебя затянуться вейпом.
vape-component-try-use-vape-forced-user = Вы вынуждаете { THE($target) } затянуться вейпом. vape-component-try-use-vape-forced-user = Вы вынуждаете { $target } затянуться вейпом.
vape-component-try-use-vape = Ты пытаешься затянуться вейпом. vape-component-try-use-vape = Ты пытаешься затянуться вейпом.
vape-component-vape-empty = Вейп пуст! vape-component-vape-empty = Вейп пуст!

View File

@@ -1,3 +1,3 @@
payload-case-not-close-enough = Вам нужно подойти ближе, чтобы определить, содержит ли { $ent } заряд. payload-case-not-close-enough = Вам нужно подойти ближе, чтобы определить, содержит ли { $ent } заряд.
payload-case-has-payload = В { CAPITALIZE($ent) } установлен заряд! payload-case-has-payload = В { CAPITALIZE($ent) } установлен заряд!
payload-case-does-not-have-payload = { CAPITALIZE(THE($ent)) } не содержит заряд. payload-case-does-not-have-payload = { CAPITALIZE($ent) } не содержит заряд.

View File

@@ -1,4 +1,4 @@
generator-clogged = {THE($generator)} резко отключается! generator-clogged = {$generator} резко отключается!
portable-generator-verb-start = Запустить генератор portable-generator-verb-start = Запустить генератор
portable-generator-verb-start-msg-unreliable = Запуск генератора. Это может потребовать нескольких попыток. portable-generator-verb-start-msg-unreliable = Запуск генератора. Это может потребовать нескольких попыток.

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