Залью спеллы потестить, мне похуй ПР НОМЕР 156
* refactor CheZaHuetaMagicSystem * эщкере * alt-spells system. lmb, rmb, alt-click * fix * ChargeSpellsIndicator + Visual(CheZaHueta) * Custom charge effect for spell * Custom MaxChargeLevel * Finally. Alt spells seems to work!! Need to start do spells and gamerule * fuckkk * fix crash, actually burn scroll.. * some fixes blyat * ArcSpell * очередная CheZaHuetaSystem, ForceSpell * ONI'SOMA! * mraow * prepare this LMAO * Yebanyy rot etogo kazino blyat! - CardsSpell * forcewall * nig * blink * Ethereal Jaunt * игра говно * Блядина * ну на еще спеллов * blyadina * да иди ты нахуй БЛЯДЬ * кто прочитал, тот сдохнет. сделай 5 репостов чтобы выжить.... * icons * та ваще поебать * одежда
@@ -2,6 +2,7 @@ using System.Numerics;
|
||||
using System.Threading;
|
||||
using Content.Client.CombatMode;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.UserInterface.Systems.Actions;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
@@ -16,7 +17,7 @@ namespace Content.Client.ContextMenu.UI
|
||||
/// <remarks>
|
||||
/// This largely involves setting up timers to open and close sub-menus when hovering over other menu elements.
|
||||
/// </remarks>
|
||||
public sealed class ContextMenuUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>, IOnSystemChanged<CombatModeSystem>
|
||||
public sealed class ContextMenuUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>, IOnSystemChanged<CombatModeSystem>, IOnSystemChanged<ChargeActionSystem>
|
||||
{
|
||||
public static readonly TimeSpan HoverDelay = TimeSpan.FromSeconds(0.2);
|
||||
|
||||
@@ -216,6 +217,12 @@ namespace Content.Client.ContextMenu.UI
|
||||
Close();
|
||||
}
|
||||
|
||||
private void OnChargingUpdated(bool charging)
|
||||
{
|
||||
if (charging)
|
||||
Close();
|
||||
}
|
||||
|
||||
public void OnSystemLoaded(CombatModeSystem system)
|
||||
{
|
||||
system.LocalPlayerCombatModeUpdated += OnCombatModeUpdated;
|
||||
@@ -225,5 +232,15 @@ namespace Content.Client.ContextMenu.UI
|
||||
{
|
||||
system.LocalPlayerCombatModeUpdated -= OnCombatModeUpdated;
|
||||
}
|
||||
|
||||
public void OnSystemLoaded(ChargeActionSystem system)
|
||||
{
|
||||
system.ChargingUpdated += OnChargingUpdated;
|
||||
}
|
||||
|
||||
public void OnSystemUnloaded(ChargeActionSystem system)
|
||||
{
|
||||
system.ChargingUpdated -= OnChargingUpdated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Graphics.RSI;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using static Content.Client.Actions.ActionsSystem;
|
||||
@@ -29,8 +30,7 @@ using static Content.Client.UserInterface.Systems.Actions.Windows.ActionsWindow;
|
||||
using static Robust.Client.UserInterface.Control;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
using static Robust.Client.UserInterface.Controls.LineEdit;
|
||||
using static Robust.Client.UserInterface.Controls.MultiselectOptionButton<
|
||||
Content.Client.UserInterface.Systems.Actions.Windows.ActionsWindow.Filters>;
|
||||
using static Robust.Client.UserInterface.Controls.MultiselectOptionButton<Content.Client.UserInterface.Systems.Actions.Windows.ActionsWindow.Filters>;
|
||||
using static Robust.Client.UserInterface.Controls.TextureRect;
|
||||
using static Robust.Shared.Input.Binding.PointerInputCmdHandler;
|
||||
|
||||
@@ -128,25 +128,45 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
}
|
||||
|
||||
builder
|
||||
.Bind(ContentKeyFunctions.OpenActionsMenu,
|
||||
InputCmdHandler.FromDelegate(_ => ToggleWindow()))
|
||||
.BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(TargetingOnUse, outsidePrediction: true),
|
||||
typeof(ConstructionSystem), typeof(DragDropSystem))
|
||||
.BindBefore(EngineKeyFunctions.UIRightClick, new PointerInputCmdHandler(TargetingCancel, outsidePrediction: true))
|
||||
.Bind(ContentKeyFunctions.OpenActionsMenu, InputCmdHandler.FromDelegate(_ => ToggleWindow()))
|
||||
.BindBefore(EngineKeyFunctions.Use, new PointerInputCmdHandler(TargetingOnUse, outsidePrediction: true), typeof(ConstructionSystem), typeof(DragDropSystem))
|
||||
.BindBefore(ContentKeyFunctions.AltActivateItemInWorld, new PointerInputCmdHandler(AltUse, outsidePrediction: true))
|
||||
.Register<ActionUIController>();
|
||||
}
|
||||
|
||||
private bool TargetingCancel(in PointerInputCmdArgs args)
|
||||
private bool AltUse(in PointerInputCmdArgs args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
if (!_timing.IsFirstTimePredicted || _actionsSystem == null || SelectingTargetFor is not { } actionId)
|
||||
return false;
|
||||
|
||||
// only do something for actual target-based actions
|
||||
if (SelectingTargetFor == null)
|
||||
if (_playerManager.LocalEntity is not { } user)
|
||||
return false;
|
||||
|
||||
StopTargeting();
|
||||
return true;
|
||||
if (!EntityManager.TryGetComponent(user, out ActionsComponent? comp))
|
||||
return false;
|
||||
|
||||
if (!_actionsSystem.TryGetActionData(actionId, out var baseAction) ||
|
||||
baseAction is not BaseTargetActionComponent action || !action.IsAltEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is the action currently valid?
|
||||
if (!action.Enabled
|
||||
|| action is { Charges: 0, RenewCharges: false }
|
||||
|| action.Cooldown.HasValue && action.Cooldown.Value.End > _timing.CurTime)
|
||||
{
|
||||
// The user is targeting with this action, but it is not valid. Maybe mark this click as
|
||||
// handled and prevent further interactions.
|
||||
return !action.InteractOnMiss;
|
||||
}
|
||||
|
||||
return action switch
|
||||
{
|
||||
WorldTargetActionComponent mapTarget => TryTargetWorld(args.Coordinates, actionId, mapTarget, user, comp,
|
||||
ActionUseType.AltUse, target: args.EntityUid) || !mapTarget.InteractOnMiss,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,28 +199,23 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
return !action.InteractOnMiss;
|
||||
}
|
||||
|
||||
switch (action)
|
||||
return action switch
|
||||
{
|
||||
case WorldTargetActionComponent mapTarget:
|
||||
return TryTargetWorld(args, actionId, mapTarget, user, comp) || !mapTarget.InteractOnMiss;
|
||||
|
||||
case EntityTargetActionComponent entTarget:
|
||||
return TryTargetEntity(args, actionId, entTarget, user, comp) || !entTarget.InteractOnMiss;
|
||||
|
||||
default:
|
||||
Logger.Error($"Unknown targeting action: {actionId.GetType()}");
|
||||
return false;
|
||||
}
|
||||
WorldTargetActionComponent mapTarget => TryTargetWorld(args.Coordinates, actionId, mapTarget, user, comp) ||
|
||||
!mapTarget.InteractOnMiss,
|
||||
EntityTargetActionComponent entTarget => TryTargetEntity(args.EntityUid, actionId, entTarget, user, comp) ||
|
||||
!entTarget.InteractOnMiss,
|
||||
_ => false
|
||||
};
|
||||
}
|
||||
|
||||
private bool TryTargetWorld(in PointerInputCmdArgs args, EntityUid actionId, WorldTargetActionComponent action, EntityUid user, ActionsComponent actionComp)
|
||||
public bool TryTargetWorld(EntityCoordinates coordinates, EntityUid actionId, WorldTargetActionComponent action,
|
||||
EntityUid user, ActionsComponent actionComp, ActionUseType type = ActionUseType.Default, int chargeLevel = 0, EntityUid? target = default)
|
||||
{
|
||||
if (_actionsSystem == null)
|
||||
return false;
|
||||
|
||||
var coords = args.Coordinates;
|
||||
|
||||
if (!_actionsSystem.ValidateWorldTarget(user, coords, action))
|
||||
if (!_actionsSystem.ValidateWorldTarget(user, coordinates, action))
|
||||
{
|
||||
// Invalid target.
|
||||
if (action.DeselectOnMiss)
|
||||
@@ -213,14 +228,24 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
{
|
||||
if (action.Event != null)
|
||||
{
|
||||
action.Event.Target = coords;
|
||||
action.Event.Target = coordinates;
|
||||
action.Event.Performer = user;
|
||||
}
|
||||
|
||||
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
|
||||
}
|
||||
else
|
||||
EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(EntityManager.GetNetEntity(actionId), EntityManager.GetNetCoordinates(coords)));
|
||||
{
|
||||
var msg = new RequestPerformActionEvent(EntityManager.GetNetEntity(actionId), EntityManager.GetNetCoordinates(coordinates))
|
||||
{
|
||||
ActionUseType = type,
|
||||
ChargeLevel = chargeLevel,
|
||||
EntityTarget = EntityManager.GetNetEntity(target)
|
||||
};
|
||||
|
||||
EntityManager.RaisePredictiveEvent(msg);
|
||||
}
|
||||
|
||||
|
||||
if (!action.Repeat)
|
||||
StopTargeting();
|
||||
@@ -228,14 +253,12 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryTargetEntity(in PointerInputCmdArgs args, EntityUid actionId, EntityTargetActionComponent action, EntityUid user, ActionsComponent actionComp)
|
||||
public bool TryTargetEntity(EntityUid uid, EntityUid actionId, EntityTargetActionComponent action, EntityUid user, ActionsComponent actionComp, ActionUseType type = ActionUseType.Default)
|
||||
{
|
||||
if (_actionsSystem == null)
|
||||
return false;
|
||||
|
||||
var entity = args.EntityUid;
|
||||
|
||||
if (!_actionsSystem.ValidateEntityTarget(user, entity, action))
|
||||
if (!_actionsSystem.ValidateEntityTarget(user, uid, action))
|
||||
{
|
||||
if (action.DeselectOnMiss)
|
||||
StopTargeting();
|
||||
@@ -247,14 +270,21 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
{
|
||||
if (action.Event != null)
|
||||
{
|
||||
action.Event.Target = entity;
|
||||
action.Event.Target = uid;
|
||||
action.Event.Performer = user;
|
||||
}
|
||||
|
||||
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
|
||||
}
|
||||
else
|
||||
EntityManager.RaisePredictiveEvent(new RequestPerformActionEvent(EntityManager.GetNetEntity(actionId), EntityManager.GetNetEntity(args.EntityUid)));
|
||||
{
|
||||
var msg = new RequestPerformActionEvent(EntityManager.GetNetEntity(actionId), EntityManager.GetNetEntity(uid))
|
||||
{
|
||||
ActionUseType = type
|
||||
};
|
||||
|
||||
EntityManager.RaisePredictiveEvent(msg);
|
||||
}
|
||||
|
||||
if (!action.Repeat)
|
||||
StopTargeting();
|
||||
@@ -409,12 +439,6 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
};
|
||||
}
|
||||
|
||||
private void ClearList()
|
||||
{
|
||||
if (_window?.Disposed == false)
|
||||
_window.ResultsGrid.RemoveAllChildren();
|
||||
}
|
||||
|
||||
private void PopulateActions(IEnumerable<(EntityUid Id, BaseActionComponent Comp)> actions)
|
||||
{
|
||||
if (_window is not { Disposed: false, IsOpen: true })
|
||||
@@ -432,7 +456,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
existing.Add(button);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
var i = 0;
|
||||
foreach (var action in actions)
|
||||
{
|
||||
if (i < existing.Count)
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
using Content.Client.Actions;
|
||||
using Content.Shared._White.Wizard;
|
||||
using Content.Shared._White.Wizard.Charging;
|
||||
using Content.Shared.Actions;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Actions;
|
||||
|
||||
public sealed class ChargeActionSystem : SharedChargingSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly ActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
[Dependency] private readonly InputSystem _inputSystem = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
private ActionUIController? _controller;
|
||||
|
||||
public event Action<bool>? ChargingUpdated;
|
||||
|
||||
private bool _charging;
|
||||
private bool _prevCharging;
|
||||
|
||||
private float _chargeTime;
|
||||
private int _chargeLevel;
|
||||
private int _prevChargeLevel;
|
||||
|
||||
private bool _isChargingPlaying;
|
||||
private bool _isChargedPlaying;
|
||||
|
||||
private const float LevelChargeTime = 1.5f;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_controller = _uiManager.GetUIController<ActionUIController>();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
if (_playerManager.LocalEntity is not { } user)
|
||||
return;
|
||||
|
||||
if (!_timing.IsFirstTimePredicted || _controller == null || _controller.SelectingTargetFor is not { } actionId)
|
||||
return;
|
||||
|
||||
if (!_actionsSystem.TryGetActionData(actionId, out var baseAction) ||
|
||||
baseAction is not BaseTargetActionComponent action || !action.IsChargeEnabled)
|
||||
return;
|
||||
|
||||
if (!action.Enabled
|
||||
|| action is { Charges: 0, RenewCharges: false }
|
||||
|| action.Cooldown.HasValue && action.Cooldown.Value.End > _timing.CurTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var altDown = _inputSystem.CmdStates.GetState(EngineKeyFunctions.UseSecondary);
|
||||
switch (altDown)
|
||||
{
|
||||
case BoundKeyState.Down:
|
||||
_prevCharging = _charging;
|
||||
_charging = true;
|
||||
_chargeTime += frameTime;
|
||||
_chargeLevel = (int) (_chargeTime / LevelChargeTime) + 1;
|
||||
_chargeLevel = Math.Clamp(_chargeLevel, 1, action.MaxChargeLevel);
|
||||
break;
|
||||
case BoundKeyState.Up when _charging:
|
||||
_prevCharging = _charging;
|
||||
_charging = false;
|
||||
_chargeTime = 0f;
|
||||
_isChargingPlaying = false;
|
||||
_isChargedPlaying = false;
|
||||
|
||||
HandleAction(actionId, action, user, _chargeLevel);
|
||||
_chargeLevel = 0;
|
||||
|
||||
RaiseNetworkEvent(new RequestAudioSpellStop());
|
||||
RaiseNetworkEvent(new RemoveWizardChargeEvent());
|
||||
break;
|
||||
case BoundKeyState.Up:
|
||||
_prevCharging = _charging;
|
||||
_chargeLevel = 0;
|
||||
_charging = false;
|
||||
_chargeTime = 0f;
|
||||
_isChargingPlaying = false;
|
||||
_isChargedPlaying = false;
|
||||
|
||||
RaiseNetworkEvent(new RequestAudioSpellStop());
|
||||
RaiseNetworkEvent(new RemoveWizardChargeEvent());
|
||||
break;
|
||||
}
|
||||
|
||||
if (_chargeLevel != _prevChargeLevel)
|
||||
{
|
||||
if (_chargeLevel > 0 && _charging)
|
||||
{
|
||||
RaiseNetworkEvent(new AddWizardChargeEvent(action.ChargeProto));
|
||||
}
|
||||
_prevChargeLevel = _chargeLevel;
|
||||
}
|
||||
|
||||
if (_prevCharging != _charging)
|
||||
{
|
||||
ChargingUpdated?.Invoke(_charging);
|
||||
}
|
||||
|
||||
if (_charging && !_isChargingPlaying)
|
||||
{
|
||||
_isChargingPlaying = true;
|
||||
RaiseNetworkEvent(new RequestSpellChargingAudio(action.ChargingSound, action.LoopCharging));
|
||||
}
|
||||
|
||||
if (_chargeLevel >= action.MaxChargeLevel && !_isChargedPlaying && _charging)
|
||||
{
|
||||
_isChargedPlaying = true;
|
||||
RaiseNetworkEvent(new RequestSpellChargedAudio(action.MaxChargedSound, action.LoopMaxCharged));
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleAction(EntityUid actionId, BaseTargetActionComponent action, EntityUid user, int chargeLevel)
|
||||
{
|
||||
var mousePos = _eyeManager.PixelToMap(_inputManager.MouseScreenPosition);
|
||||
if (mousePos.MapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
var coordinates = EntityCoordinates.FromMap(_mapManager.TryFindGridAt(mousePos, out var gridUid, out _)
|
||||
? gridUid
|
||||
: _mapManager.GetMapEntityId(mousePos.MapId), mousePos, _transformSystem, EntityManager);
|
||||
|
||||
if (!EntityManager.TryGetComponent(user, out ActionsComponent? comp))
|
||||
return;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case WorldTargetActionComponent mapTarget:
|
||||
_controller?.TryTargetWorld(coordinates, actionId, mapTarget, user, comp, ActionUseType.Charge, chargeLevel);
|
||||
break;
|
||||
}
|
||||
|
||||
RaiseNetworkEvent(new RequestAudioSpellStop());
|
||||
RaiseNetworkEvent(new RemoveWizardChargeEvent());
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_controller = null;
|
||||
|
||||
_charging = false;
|
||||
_prevCharging = false;
|
||||
_chargeTime = 0f;
|
||||
_chargeLevel = 0;
|
||||
_prevChargeLevel = 0;
|
||||
_isChargingPlaying = false;
|
||||
_isChargedPlaying = false;
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,6 @@ using Robust.Client.State;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Weapons.Melee;
|
||||
|
||||
|
||||
7
Content.Client/_White/Wizard/Scrolls/ScrollSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Content.Shared._White.Wizard.ScrollSystem;
|
||||
|
||||
namespace Content.Client._White.Wizard.Scrolls;
|
||||
|
||||
public sealed class ScrollSystem : SharedScrollSystem
|
||||
{
|
||||
}
|
||||
15
Content.Server/EnergyDome/EnergyDomeComponent.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Content.Server.EnergyDome;
|
||||
|
||||
/// <summary>
|
||||
/// marker component that allows linking the dome generator with the dome itself
|
||||
/// </summary>
|
||||
|
||||
[RegisterComponent, Access(typeof(EnergyDomeSystem))]
|
||||
public sealed partial class EnergyDomeComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// A linked generator that uses energy
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? Generator;
|
||||
}
|
||||
85
Content.Server/EnergyDome/EnergyDomeGeneratorComponent.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using Content.Shared.DeviceLinking;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.EnergyDome;
|
||||
|
||||
/// <summary>
|
||||
/// component, allows an entity to generate a battery-powered energy dome of a specific type.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(EnergyDomeSystem))] //Access add
|
||||
public sealed partial class EnergyDomeGeneratorComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public bool Enabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// How much energy will be spent from the battery per unit of damage taken by the shield.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float DamageEnergyDraw = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the dome can be toggled via standard interactions
|
||||
/// (alt verbs, using in hand, etc)
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool CanInteractUse = true;
|
||||
|
||||
/// <summary>
|
||||
/// Can the NetworkDevice system activate and deactivate the barrier?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool CanDeviceNetworkUse = false;
|
||||
|
||||
//Dome
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public EntProtoId DomePrototype = "EnergyDomeSmallRed";
|
||||
|
||||
[DataField]
|
||||
public EntityUid? SpawnedDome;
|
||||
|
||||
/// <summary>
|
||||
/// the entity on which the shield will be hung. This is either the container containing
|
||||
/// the item or the item itself. Determined when the shield is activated,
|
||||
/// it is stored in the component for changing the protected entity.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? DomeParentEntity;
|
||||
|
||||
//Action
|
||||
[DataField]
|
||||
public EntProtoId ToggleAction = "ActionToggleDome";
|
||||
|
||||
[DataField]
|
||||
public EntityUid? ToggleActionEntity;
|
||||
|
||||
//Sounds
|
||||
[DataField]
|
||||
public SoundSpecifier AccessDeniedSound = new SoundPathSpecifier("/Audio/Machines/custom_deny.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier TurnOnSound = new SoundPathSpecifier("/Audio/Machines/anomaly_sync_connect.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier EnergyOutSound = new SoundPathSpecifier("/Audio/Machines/energyshield_down.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier TurnOffSound = new SoundPathSpecifier("/Audio/Machines/button.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier ParrySound = new SoundPathSpecifier("/Audio/Machines/energyshield_parry.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVariation(0.05f)
|
||||
};
|
||||
|
||||
//Ports
|
||||
[DataField]
|
||||
public ProtoId<SinkPortPrototype> TogglePort = "Toggle";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<SinkPortPrototype> OnPort = "On";
|
||||
|
||||
[DataField]
|
||||
public ProtoId<SinkPortPrototype> OffPort = "Off";
|
||||
}
|
||||
329
Content.Server/EnergyDome/EnergyDomeSystem.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,6 @@ public sealed class LightningSystem : SharedLightningSystem
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Looks for objects with a LightningTarget component in the radius, prioritizes them, and hits the highest priority targets with lightning.
|
||||
/// </summary>
|
||||
@@ -78,9 +77,9 @@ public sealed class LightningSystem : SharedLightningSystem
|
||||
_random.Shuffle(targets);
|
||||
targets.Sort((x, y) => y.Priority.CompareTo(x.Priority));
|
||||
|
||||
int shootedCount = 0;
|
||||
int count = -1;
|
||||
while(shootedCount < boltCount)
|
||||
var shootCount = 0;
|
||||
var count = -1;
|
||||
while(shootCount < boltCount)
|
||||
{
|
||||
count++;
|
||||
|
||||
@@ -95,7 +94,7 @@ public sealed class LightningSystem : SharedLightningSystem
|
||||
{
|
||||
ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].LightningResistance, triggerLightningEvents);
|
||||
}
|
||||
shootedCount++;
|
||||
shootCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||
|
||||
namespace Content.Server.Magic.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Spellbooks for having an entity learn spells as long as they've read the book and it's in their hand.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class SpellbookComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// List of spells that this book has. This is a combination of the WorldSpells, EntitySpells, and InstantSpells.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public readonly List<EntityUid> Spells = new();
|
||||
|
||||
/// <summary>
|
||||
/// The three fields below is just used for initialization.
|
||||
/// </summary>
|
||||
[DataField("spells", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<int, EntityPrototype>))]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Dictionary<string, int> SpellActions = new();
|
||||
|
||||
[DataField("learnTime")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float LearnTime = .75f;
|
||||
|
||||
/// <summary>
|
||||
/// If true, the spell action stays even after the book is removed
|
||||
/// </summary>
|
||||
[DataField("learnPermanently")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool LearnPermanently;
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Doors.Systems;
|
||||
using Content.Server.Magic.Components;
|
||||
using Content.Server.Weapons.Ranged.Systems;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Magic;
|
||||
using Content.Shared.Magic.Events;
|
||||
using Content.Shared.Maps;
|
||||
@@ -21,6 +19,7 @@ using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
@@ -33,31 +32,25 @@ namespace Content.Server.Magic;
|
||||
/// </summary>
|
||||
public sealed class MagicSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ISerializationManager _seriMan = default!;
|
||||
[Dependency] private readonly ISerializationManager _serializationManager = default!;
|
||||
[Dependency] private readonly IComponentFactory _compFact = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly DoorBoltSystem _boltsSystem = default!;
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly GunSystem _gunSystem = default!;
|
||||
[Dependency] private readonly PhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpellbookComponent, MapInitEvent>(OnInit);
|
||||
SubscribeLocalEvent<SpellbookComponent, UseInHandEvent>(OnUse);
|
||||
SubscribeLocalEvent<SpellbookComponent, SpellbookDoAfterEvent>(OnDoAfter);
|
||||
|
||||
SubscribeLocalEvent<InstantSpawnSpellEvent>(OnInstantSpawn);
|
||||
SubscribeLocalEvent<TeleportSpellEvent>(OnTeleportSpell);
|
||||
SubscribeLocalEvent<KnockSpellEvent>(OnKnockSpell);
|
||||
@@ -67,73 +60,8 @@ public sealed class MagicSystem : EntitySystem
|
||||
SubscribeLocalEvent<ChangeComponentsSpellEvent>(OnChangeComponentsSpell);
|
||||
}
|
||||
|
||||
private void OnDoAfter(EntityUid uid, SpellbookComponent component, DoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
if (!component.LearnPermanently)
|
||||
{
|
||||
_actionsSystem.GrantActions(args.Args.User, component.Spells, uid);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (id, charges) in component.SpellActions)
|
||||
{
|
||||
// TOOD store spells entity ids on some sort of innate magic user component or something like that.
|
||||
EntityUid? actionId = null;
|
||||
if (_actionsSystem.AddAction(args.Args.User, ref actionId, id))
|
||||
_actionsSystem.SetCharges(actionId, charges < 0 ? null : charges);
|
||||
}
|
||||
|
||||
component.SpellActions.Clear();
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, SpellbookComponent component, MapInitEvent args)
|
||||
{
|
||||
if (component.LearnPermanently)
|
||||
return;
|
||||
|
||||
foreach (var (id, charges) in component.SpellActions)
|
||||
{
|
||||
var spell = _actionContainer.AddAction(uid, id);
|
||||
if (spell == null)
|
||||
continue;
|
||||
|
||||
_actionsSystem.SetCharges(spell, charges < 0 ? null : charges);
|
||||
component.Spells.Add(spell.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUse(EntityUid uid, SpellbookComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
AttemptLearn(uid, component, args);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void AttemptLearn(EntityUid uid, SpellbookComponent component, UseInHandEvent args)
|
||||
{
|
||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.LearnTime, new SpellbookDoAfterEvent(), uid, target: uid)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
NeedHand = true //What, are you going to read with your eyes only??
|
||||
};
|
||||
|
||||
_doAfter.TryStartDoAfter(doAfterEventArgs);
|
||||
}
|
||||
|
||||
#region Spells
|
||||
|
||||
/// <summary>
|
||||
/// Handles the instant action (i.e. on the caster) attempting to spawn an entity.
|
||||
/// </summary>
|
||||
private void OnInstantSpawn(InstantSpawnSpellEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -145,12 +73,12 @@ public sealed class MagicSystem : EntitySystem
|
||||
{
|
||||
var ent = Spawn(args.Prototype, position.SnapToGrid(EntityManager, _mapManager));
|
||||
|
||||
if (args.PreventCollideWithCaster)
|
||||
{
|
||||
if (!args.PreventCollideWithCaster)
|
||||
continue;
|
||||
|
||||
var comp = EnsureComp<PreventCollideComponent>(ent);
|
||||
comp.Uid = args.Performer;
|
||||
}
|
||||
}
|
||||
|
||||
Speak(args);
|
||||
args.Handled = true;
|
||||
@@ -166,22 +94,17 @@ public sealed class MagicSystem : EntitySystem
|
||||
|
||||
var xform = Transform(ev.Performer);
|
||||
|
||||
// var userVelocity = _physics.GetMapLinearVelocity(ev.Performer); WD EDIT
|
||||
|
||||
foreach (var pos in GetSpawnPositions(xform, ev.Pos))
|
||||
{
|
||||
// If applicable, this ensures the projectile is parented to grid on spawn, instead of the map.
|
||||
var mapPos = pos.ToMap(EntityManager);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out var grid) // WD EDIT
|
||||
var mapPos = _transformSystem.ToMapCoordinates(pos);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out var grid)
|
||||
? pos.WithEntityId(gridUid, EntityManager)
|
||||
: new(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
: new EntityCoordinates(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
|
||||
// WD EDIT
|
||||
var userVelocity = Vector2.Zero;
|
||||
|
||||
if (grid != null && TryComp(gridUid, out PhysicsComponent? physics))
|
||||
userVelocity = physics.LinearVelocity;
|
||||
// WD EDIT
|
||||
|
||||
var ent = Spawn(ev.Prototype, spawnCoords);
|
||||
var direction = ev.Target.ToMapPos(EntityManager, _transformSystem) -
|
||||
@@ -194,7 +117,9 @@ public sealed class MagicSystem : EntitySystem
|
||||
{
|
||||
if (ev.Handled)
|
||||
return;
|
||||
|
||||
ev.Handled = true;
|
||||
|
||||
Speak(ev);
|
||||
|
||||
foreach (var toRemove in ev.ToRemove)
|
||||
@@ -209,75 +134,12 @@ public sealed class MagicSystem : EntitySystem
|
||||
continue;
|
||||
|
||||
var component = (Component) _compFact.GetComponent(name);
|
||||
component.Owner = ev.Target;
|
||||
var temp = (object) component;
|
||||
_seriMan.CopyTo(data.Component, ref temp);
|
||||
_serializationManager.CopyTo(data.Component, ref temp);
|
||||
EntityManager.AddComponent(ev.Target, (Component) temp!);
|
||||
}
|
||||
}
|
||||
|
||||
private List<EntityCoordinates> GetSpawnPositions(TransformComponent casterXform, MagicSpawnData data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
case TargetCasterPos:
|
||||
return new List<EntityCoordinates>(1) {casterXform.Coordinates};
|
||||
case TargetInFront:
|
||||
{
|
||||
// This is shit but you get the idea.
|
||||
var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized());
|
||||
|
||||
if (!_mapManager.TryGetGrid(casterXform.GridUid, out var mapGrid))
|
||||
return new List<EntityCoordinates>();
|
||||
|
||||
if (!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager))
|
||||
return new List<EntityCoordinates>();
|
||||
|
||||
var tileIndex = tileReference.Value.GridIndices;
|
||||
var coords = mapGrid.GridTileToLocal(tileIndex);
|
||||
EntityCoordinates coordsPlus;
|
||||
EntityCoordinates coordsMinus;
|
||||
|
||||
var dir = casterXform.LocalRotation.GetCardinalDir();
|
||||
switch (dir)
|
||||
{
|
||||
case Direction.North:
|
||||
case Direction.South:
|
||||
{
|
||||
coordsPlus = mapGrid.GridTileToLocal(tileIndex + (1, 0));
|
||||
coordsMinus = mapGrid.GridTileToLocal(tileIndex + (-1, 0));
|
||||
return new List<EntityCoordinates>(3)
|
||||
{
|
||||
coords,
|
||||
coordsPlus,
|
||||
coordsMinus,
|
||||
};
|
||||
}
|
||||
case Direction.East:
|
||||
case Direction.West:
|
||||
{
|
||||
coordsPlus = mapGrid.GridTileToLocal(tileIndex + (0, 1));
|
||||
coordsMinus = mapGrid.GridTileToLocal(tileIndex + (0, -1));
|
||||
return new List<EntityCoordinates>(3)
|
||||
{
|
||||
coords,
|
||||
coordsPlus,
|
||||
coordsMinus,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new List<EntityCoordinates>();
|
||||
}
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Teleports the user to the clicked location
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
private void OnTeleportSpell(TeleportSpellEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -285,19 +147,16 @@ public sealed class MagicSystem : EntitySystem
|
||||
|
||||
var transform = Transform(args.Performer);
|
||||
|
||||
if (transform.MapID != args.Target.GetMapId(EntityManager)) return;
|
||||
if (transform.MapID != args.Target.GetMapId(EntityManager))
|
||||
return;
|
||||
|
||||
_transformSystem.SetCoordinates(args.Performer, args.Target);
|
||||
transform.AttachToGridOrMap();
|
||||
_transformSystem.AttachToGridOrMap(args.Performer);
|
||||
_audio.PlayPvs(args.BlinkSound, args.Performer, AudioParams.Default.WithVolume(args.BlinkVolume));
|
||||
Speak(args);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens all doors within range
|
||||
/// </summary>
|
||||
/// <param name="args"></param>
|
||||
private void OnKnockSpell(KnockSpellEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -306,13 +165,11 @@ public sealed class MagicSystem : EntitySystem
|
||||
args.Handled = true;
|
||||
Speak(args);
|
||||
|
||||
//Get the position of the player
|
||||
var transform = Transform(args.Performer);
|
||||
var coords = transform.Coordinates;
|
||||
|
||||
_audio.PlayPvs(args.KnockSound, args.Performer, AudioParams.Default.WithVolume(args.KnockVolume));
|
||||
|
||||
//Look for doors and don't open them if they're already open.
|
||||
foreach (var entity in _lookup.GetEntitiesInRange(coords, args.Range))
|
||||
{
|
||||
if (TryComp<DoorBoltComponent>(entity, out var bolts))
|
||||
@@ -329,9 +186,10 @@ public sealed class MagicSystem : EntitySystem
|
||||
return;
|
||||
|
||||
ev.Handled = true;
|
||||
|
||||
Speak(ev);
|
||||
|
||||
var direction = Transform(ev.Target).MapPosition.Position - Transform(ev.Performer).MapPosition.Position;
|
||||
var direction = _transformSystem.GetMapCoordinates(ev.Target).Position - _transformSystem.GetMapCoordinates(ev.Performer).Position;
|
||||
var impulseVector = direction * 10000;
|
||||
|
||||
_physics.ApplyLinearImpulse(ev.Target, impulseVector);
|
||||
@@ -339,28 +197,17 @@ public sealed class MagicSystem : EntitySystem
|
||||
if (!TryComp<BodyComponent>(ev.Target, out var body))
|
||||
return;
|
||||
|
||||
var ents = _bodySystem.GibBody(ev.Target, true, body);
|
||||
var entities = _bodySystem.GibBody(ev.Target, true, body);
|
||||
|
||||
if (!ev.DeleteNonBrainParts)
|
||||
return;
|
||||
|
||||
foreach (var part in ents)
|
||||
{
|
||||
// just leaves a brain and clothes
|
||||
if (HasComp<BodyComponent>(part) && !HasComp<BrainComponent>(part))
|
||||
foreach (var part in entities.Where(part => HasComp<BodyComponent>(part) && !HasComp<BrainComponent>(part)))
|
||||
{
|
||||
QueueDel(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawns entity prototypes from a list within range of click.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It will offset mobs after the first mob based on the OffsetVector2 property supplied.
|
||||
/// </remarks>
|
||||
/// <param name="args"> The Spawn Spell Event args.</param>
|
||||
private void OnWorldSpawn(WorldSpawnSpellEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -373,24 +220,85 @@ public sealed class MagicSystem : EntitySystem
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loops through a supplied list of entity prototypes and spawns them
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If an offset of 0, 0 is supplied then the entities will all spawn on the same tile.
|
||||
/// Any other offset will spawn entities starting from the source Map Coordinates and will increment the supplied
|
||||
/// offset
|
||||
/// </remarks>
|
||||
/// <param name="entityEntries"> The list of Entities to spawn in</param>
|
||||
/// <param name="entityCoords"> Map Coordinates where the entities will spawn</param>
|
||||
/// <param name="lifetime"> Check to see if the entities should self delete</param>
|
||||
/// <param name="offsetVector2"> A Vector2 offset that the entities will spawn in</param>
|
||||
private void SpawnSpellHelper(List<EntitySpawnEntry> entityEntries, EntityCoordinates entityCoords, float? lifetime, Vector2 offsetVector2)
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
public List<EntityCoordinates> GetSpawnPositions(TransformComponent casterXform, MagicSpawnData data)
|
||||
{
|
||||
var getProtos = EntitySpawnCollection.GetSpawns(entityEntries, _random);
|
||||
return data switch
|
||||
{
|
||||
TargetCasterPos => GetCasterPosition(casterXform),
|
||||
TargetInFront => GetPositionsInFront(casterXform),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
public List<EntityCoordinates> GetCasterPosition(TransformComponent casterXform)
|
||||
{
|
||||
return new List<EntityCoordinates>(1) { casterXform.Coordinates };
|
||||
}
|
||||
|
||||
public List<EntityCoordinates> GetPositionsInFront(TransformComponent casterXform)
|
||||
{
|
||||
var directionPos = casterXform.Coordinates.Offset(casterXform.LocalRotation.ToWorldVec().Normalized());
|
||||
|
||||
if (!TryComp<MapGridComponent>(casterXform.GridUid, out var mapGrid) ||
|
||||
!directionPos.TryGetTileRef(out var tileReference, EntityManager, _mapManager))
|
||||
{
|
||||
return new List<EntityCoordinates>();
|
||||
}
|
||||
|
||||
var tileIndex = tileReference.Value.GridIndices;
|
||||
var coords = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex);
|
||||
|
||||
var directions = GetCardinalDirections(casterXform.LocalRotation.GetCardinalDir());
|
||||
var spawnPositions = new List<EntityCoordinates>(3);
|
||||
|
||||
foreach (var direction in directions)
|
||||
{
|
||||
var offset = GetOffsetForDirection(direction);
|
||||
var coordinates = _mapSystem.GridTileToLocal(casterXform.GridUid.Value, mapGrid, tileIndex + offset);
|
||||
spawnPositions.Add(coordinates);
|
||||
}
|
||||
|
||||
spawnPositions.Add(coords);
|
||||
return spawnPositions;
|
||||
}
|
||||
|
||||
public IEnumerable<Direction> GetCardinalDirections(Direction dir)
|
||||
{
|
||||
switch (dir)
|
||||
{
|
||||
case Direction.North:
|
||||
case Direction.South:
|
||||
return new[] { Direction.North, Direction.South };
|
||||
case Direction.East:
|
||||
case Direction.West:
|
||||
return new[] { Direction.East, Direction.West };
|
||||
default:
|
||||
return Array.Empty<Direction>();
|
||||
}
|
||||
}
|
||||
|
||||
public (int, int) GetOffsetForDirection(Direction direction)
|
||||
{
|
||||
return direction switch
|
||||
{
|
||||
Direction.North => (1, 0),
|
||||
Direction.South => (-1, 0),
|
||||
Direction.East => (0, 1),
|
||||
Direction.West => (0, -1),
|
||||
_ => (0, 0)
|
||||
};
|
||||
}
|
||||
|
||||
public void SpawnSpellHelper(List<EntitySpawnEntry> entityEntries, EntityCoordinates entityCoords, float? lifetime, Vector2 offsetVector2)
|
||||
{
|
||||
var getPrototypes = EntitySpawnCollection.GetSpawns(entityEntries, _random);
|
||||
|
||||
var offsetCoords = entityCoords;
|
||||
foreach (var proto in getProtos)
|
||||
foreach (var proto in getPrototypes)
|
||||
{
|
||||
// TODO: Share this code with instant because they're both doing similar things for positioning.
|
||||
var entity = Spawn(proto, offsetCoords);
|
||||
@@ -404,8 +312,6 @@ public sealed class MagicSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void Speak(BaseActionEvent args)
|
||||
{
|
||||
if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech))
|
||||
@@ -414,4 +320,6 @@ public sealed class MagicSystem : EntitySystem
|
||||
_chat.TrySendInGameICMessage(args.Performer, Loc.GetString(speak.Speech),
|
||||
InGameICChatType.Speak, false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -84,9 +84,18 @@ namespace Content.Server.Power.EntitySystems
|
||||
while (query.MoveNext(out var uid, out var comp, out var batt))
|
||||
{
|
||||
if (!comp.AutoRecharge) continue;
|
||||
|
||||
if (comp.AutoRechargeRate > 0)
|
||||
{
|
||||
if (batt.IsFullyCharged) continue;
|
||||
SetCharge(uid, batt.CurrentCharge + comp.AutoRechargeRate * frameTime, batt);
|
||||
}
|
||||
if (comp.AutoRechargeRate < 0) //self discharging
|
||||
{
|
||||
if (batt.Charge == 0) continue;
|
||||
UseCharge(uid, -comp.AutoRechargeRate * frameTime, batt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using Content.Shared.Eye;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Stealth;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
@@ -10,11 +12,10 @@ namespace Content.Server._White.IncorporealSystem;
|
||||
|
||||
public sealed class IncorporealSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly VisibilitySystem _visibilitySystem = default!;
|
||||
|
||||
[Dependency] private readonly SharedStealthSystem _stealth = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -41,6 +42,9 @@ public sealed class IncorporealSystem : EntitySystem
|
||||
_visibilitySystem.RefreshVisibility(uid);
|
||||
}
|
||||
|
||||
Spawn("EffectEmpPulse", Transform(uid).Coordinates);
|
||||
EnsureComp<StealthComponent>(uid);
|
||||
_stealth.SetVisibility(uid, -1);
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
}
|
||||
|
||||
@@ -50,8 +54,8 @@ public sealed class IncorporealSystem : EntitySystem
|
||||
{
|
||||
var fixture = fixtures.Fixtures.First();
|
||||
|
||||
_physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) (CollisionGroup.FlyingMobMask | CollisionGroup.GhostImpassable), fixtures);
|
||||
_physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, (int) CollisionGroup.FlyingMobLayer, fixtures);
|
||||
_physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) (CollisionGroup.MobMask | CollisionGroup.GhostImpassable), fixtures);
|
||||
_physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, (int) CollisionGroup.MobLayer, fixtures);
|
||||
}
|
||||
|
||||
if (TryComp<VisibilityComponent>(uid, out var visibility))
|
||||
@@ -62,6 +66,10 @@ public sealed class IncorporealSystem : EntitySystem
|
||||
}
|
||||
|
||||
component.MovementSpeedBuff = 1;
|
||||
|
||||
Spawn("EffectEmpPulse", Transform(uid).Coordinates);
|
||||
_stealth.SetVisibility(uid, 1);
|
||||
RemComp<StealthComponent>(uid);
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
}
|
||||
|
||||
|
||||
183
Content.Server/_White/Wizard/Charging/ChargingSystem.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using Content.Shared._White.Wizard;
|
||||
using Content.Shared._White.Wizard.Charging;
|
||||
using Content.Shared.Follower;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server._White.Wizard.Charging;
|
||||
|
||||
public sealed class ChargingSystem : SharedChargingSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly FollowerSystem _followerSystem = default!;
|
||||
|
||||
private readonly Dictionary<EntityUid, List<EntityUid>> _charges = new();
|
||||
|
||||
private readonly Dictionary<EntityUid, EntityUid> _chargingLoops = new();
|
||||
private readonly Dictionary<EntityUid, EntityUid> _chargedLoop = new();
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeNetworkEvent<RequestSpellChargingAudio>(OnCharging);
|
||||
SubscribeNetworkEvent<RequestSpellChargedAudio>(OnCharged);
|
||||
SubscribeNetworkEvent<RequestAudioSpellStop>(OnStop);
|
||||
SubscribeLocalEvent<PlayerDetachedEvent>(OnDetach);
|
||||
|
||||
SubscribeNetworkEvent<AddWizardChargeEvent>(Add);
|
||||
SubscribeNetworkEvent<RemoveWizardChargeEvent>(Remove);
|
||||
}
|
||||
|
||||
#region Audio
|
||||
|
||||
private void OnCharging(RequestSpellChargingAudio msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var user = args.SenderSession?.AttachedEntity;
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
var shouldLoop = msg.Loop;
|
||||
var sound = msg.Sound;
|
||||
|
||||
if (!shouldLoop)
|
||||
{
|
||||
_audio.PlayPvs(sound, user.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_chargingLoops.TryGetValue(user.Value, out var currentStream))
|
||||
{
|
||||
_audio.Stop(currentStream);
|
||||
_chargingLoops.Remove(user.Value);
|
||||
}
|
||||
|
||||
var newStream = _audio.PlayPvs(sound, user.Value, AudioParams.Default.WithLoop(true));
|
||||
|
||||
if (newStream.HasValue)
|
||||
{
|
||||
_chargingLoops[user.Value] = newStream.Value.Entity;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCharged(RequestSpellChargedAudio msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var user = args.SenderSession?.AttachedEntity;
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
if (_chargingLoops.TryGetValue(user.Value, out var currentStream))
|
||||
{
|
||||
_audio.Stop(currentStream);
|
||||
_chargingLoops.Remove(user.Value);
|
||||
}
|
||||
|
||||
var shouldLoop = msg.Loop;
|
||||
var sound = msg.Sound;
|
||||
|
||||
if (!shouldLoop)
|
||||
{
|
||||
_audio.PlayPvs(sound, user.Value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_chargedLoop.TryGetValue(user.Value, out var chargedLoop))
|
||||
{
|
||||
_audio.Stop(chargedLoop);
|
||||
_chargedLoop.Remove(user.Value);
|
||||
}
|
||||
|
||||
var newStream = _audio.PlayPvs(sound, user.Value, AudioParams.Default.WithLoop(true));
|
||||
|
||||
if (newStream.HasValue)
|
||||
{
|
||||
_chargedLoop[user.Value] = newStream.Value.Entity;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStop(RequestAudioSpellStop msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var user = args.SenderSession?.AttachedEntity;
|
||||
if (user == null)
|
||||
return;
|
||||
|
||||
if (_chargingLoops.TryGetValue(user.Value, out var currentStream))
|
||||
{
|
||||
_audio.Stop(currentStream);
|
||||
_chargingLoops.Remove(user.Value);
|
||||
}
|
||||
|
||||
if (_chargedLoop.TryGetValue(user.Value, out var chargedLoop))
|
||||
{
|
||||
_audio.Stop(chargedLoop);
|
||||
_chargedLoop.Remove(user.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDetach(PlayerDetachedEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var user = msg.Entity;
|
||||
|
||||
if (_chargingLoops.TryGetValue(user, out var currentStream))
|
||||
{
|
||||
_audio.Stop(currentStream);
|
||||
_chargingLoops.Remove(user);
|
||||
}
|
||||
|
||||
if (_chargedLoop.TryGetValue(user, out var chargedLoop))
|
||||
{
|
||||
_audio.Stop(chargedLoop);
|
||||
_chargedLoop.Remove(user);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Charges
|
||||
|
||||
private void Add(AddWizardChargeEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity != null)
|
||||
AddCharge(args.SenderSession.AttachedEntity.Value, msg.ChargeProto);
|
||||
}
|
||||
|
||||
private void Remove(RemoveWizardChargeEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity != null)
|
||||
RemoveAllCharges(args.SenderSession.AttachedEntity.Value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
public void AddCharge(EntityUid uid, string msgChargeProto)
|
||||
{
|
||||
var itemEnt = Spawn(msgChargeProto, Transform(uid).Coordinates);
|
||||
_followerSystem.StartFollowingEntity(itemEnt, uid);
|
||||
|
||||
if (!_charges.ContainsKey(uid))
|
||||
{
|
||||
_charges[uid] = new List<EntityUid>();
|
||||
}
|
||||
|
||||
_charges[uid].Add(itemEnt);
|
||||
}
|
||||
|
||||
public void RemoveAllCharges(EntityUid uid)
|
||||
{
|
||||
if (!_charges.ContainsKey(uid))
|
||||
return;
|
||||
|
||||
foreach (var followerEnt in _charges[uid])
|
||||
{
|
||||
Del(followerEnt);
|
||||
}
|
||||
|
||||
_charges.Remove(uid);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Content.Server._White.Wizard.Magic.Amaterasu;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class AmaterasuComponent : Component
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Mobs;
|
||||
|
||||
namespace Content.Server._White.Wizard.Magic.Amaterasu;
|
||||
|
||||
public sealed class AmaterasuSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AmaterasuComponent, MobStateChangedEvent>(OnMobState);
|
||||
}
|
||||
|
||||
private void OnMobState(EntityUid uid, AmaterasuComponent component, MobStateChangedEvent args)
|
||||
{
|
||||
if (args.NewMobState is MobState.Critical or MobState.Dead)
|
||||
{
|
||||
if(!TryComp<FlammableComponent>(uid, out var flammable))
|
||||
return;
|
||||
|
||||
if (flammable.OnFire)
|
||||
{
|
||||
_bodySystem.GibBody(uid);
|
||||
return;
|
||||
}
|
||||
|
||||
RemComp<AmaterasuComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Content.Server._White.Wizard.Magic.Other;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class InstantRecallComponent : Component
|
||||
{
|
||||
public EntityUid? Item;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace Content.Server._White.Wizard.Magic.TeslaProjectile;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class TeslaProjectileComponent : Component {}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Content.Server.Lightning;
|
||||
using Content.Shared.Projectiles;
|
||||
|
||||
namespace Content.Server._White.Wizard.Magic.TeslaProjectile;
|
||||
|
||||
public sealed class TeslaProjectileSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly LightningSystem _lightning = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<TeslaProjectileComponent, ProjectileHitEvent>(OnStartCollide);
|
||||
}
|
||||
|
||||
private void OnStartCollide(Entity<TeslaProjectileComponent> ent, ref ProjectileHitEvent args)
|
||||
{
|
||||
_lightning.ShootRandomLightnings(ent, 2, 4, arcDepth:2);
|
||||
}
|
||||
}
|
||||
701
Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs
Normal file
@@ -0,0 +1,701 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server._White.IncorporealSystem;
|
||||
using Content.Server._White.Wizard.Magic.Amaterasu;
|
||||
using Content.Server._White.Wizard.Magic.Other;
|
||||
using Content.Server.Abilities.Mime;
|
||||
using Content.Server.Administration.Commands;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Emp;
|
||||
using Content.Server.Lightning;
|
||||
using Content.Server.Magic;
|
||||
using Content.Server.Singularity.EntitySystems;
|
||||
using Content.Server.Weapons.Ranged.Systems;
|
||||
using Content.Shared._White.Wizard;
|
||||
using Content.Shared._White.Wizard.Magic;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Cluwne;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.VirtualItem;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Magic;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server._White.Wizard.Magic;
|
||||
|
||||
public sealed class WizardSpellsSystem : EntitySystem
|
||||
{
|
||||
#region Dependencies
|
||||
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly GunSystem _gunSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly LightningSystem _lightning = default!;
|
||||
[Dependency] private readonly MagicSystem _magicSystem = default!;
|
||||
[Dependency] private readonly GravityWellSystem _gravityWell = default!;
|
||||
[Dependency] private readonly FlammableSystem _flammableSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
||||
[Dependency] private readonly TurfSystem _turf = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly EmpSystem _empSystem = default!;
|
||||
|
||||
#endregion
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<InstantRecallSpellEvent>(OnInstantRecallSpell);
|
||||
SubscribeLocalEvent<MimeTouchSpellEvent>(OnMimeTouchSpell);
|
||||
SubscribeLocalEvent<BananaTouchSpellEvent>(OnBananaTouchSpell);
|
||||
SubscribeLocalEvent<CluwneCurseSpellEvent>(OnCluwneCurseSpell);
|
||||
SubscribeLocalEvent<EmpSpellEvent>(OnEmpSpell);
|
||||
SubscribeLocalEvent<EtherealJauntSpellEvent>(OnJauntSpell);
|
||||
SubscribeLocalEvent<BlinkSpellEvent>(OnBlinkSpell);
|
||||
SubscribeLocalEvent<ForceWallSpellEvent>(OnForcewallSpell);
|
||||
SubscribeLocalEvent<CardsSpellEvent>(OnCardsSpell);
|
||||
SubscribeLocalEvent<FireballSpellEvent>(OnFireballSpell);
|
||||
SubscribeLocalEvent<ForceSpellEvent>(OnForceSpell);
|
||||
SubscribeLocalEvent<ArcSpellEvent>(OnArcSpell);
|
||||
|
||||
SubscribeLocalEvent<MagicComponent, BeforeCastSpellEvent>(OnBeforeCastSpell);
|
||||
}
|
||||
|
||||
#region Instant Recall
|
||||
|
||||
private void OnInstantRecallSpell(InstantRecallSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
if (!TryComp<HandsComponent>(msg.Performer, out var handsComponent))
|
||||
return;
|
||||
|
||||
if (!TryComp<InstantRecallComponent>(msg.Action, out var recallComponent))
|
||||
{
|
||||
_popupSystem.PopupEntity("Что-то поломалось!", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handsComponent.ActiveHandEntity != null)
|
||||
{
|
||||
if (HasComp<VirtualItemComponent>(handsComponent.ActiveHandEntity.Value))
|
||||
{
|
||||
_popupSystem.PopupEntity("Не могу работать с этим!", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
recallComponent.Item = handsComponent.ActiveHandEntity.Value;
|
||||
_popupSystem.PopupEntity($"Сопряжено с {MetaData(handsComponent.ActiveHandEntity.Value).EntityName}", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (handsComponent.ActiveHandEntity == null && recallComponent.Item != null)
|
||||
{
|
||||
var coordsItem = Transform(recallComponent.Item.Value).Coordinates;
|
||||
var coordsPerformer = Transform(msg.Performer).Coordinates;
|
||||
|
||||
Spawn("EffectEmpPulse", coordsItem);
|
||||
|
||||
_transformSystem.SetCoordinates(recallComponent.Item.Value, coordsPerformer);
|
||||
_transformSystem.AttachToGridOrMap(recallComponent.Item.Value);
|
||||
|
||||
_handsSystem.TryForcePickupAnyHand(msg.Performer, recallComponent.Item.Value);
|
||||
|
||||
msg.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_popupSystem.PopupEntity("Нет привязки.", msg.Performer, msg.Performer);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mime Touch
|
||||
|
||||
private void OnMimeTouchSpell(MimeTouchSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
if (!HasComp<HumanoidAppearanceComponent>(msg.Target))
|
||||
{
|
||||
_popupSystem.PopupEntity("Работает только на людях!", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
SetOutfitCommand.SetOutfit(msg.Target, "MimeGear", EntityManager);
|
||||
EnsureComp<MimePowersComponent>(msg.Target);
|
||||
|
||||
Spawn("AdminInstantEffectSmoke3", Transform(msg.Target).Coordinates);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Banana Touch
|
||||
|
||||
private void OnBananaTouchSpell(BananaTouchSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
if (!HasComp<HumanoidAppearanceComponent>(msg.Target))
|
||||
{
|
||||
_popupSystem.PopupEntity("Работает только на людях!", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
SetOutfitCommand.SetOutfit(msg.Target, "ClownGear", EntityManager);
|
||||
EnsureComp<ClumsyComponent>(msg.Target);
|
||||
|
||||
Spawn("AdminInstantEffectSmoke3", Transform(msg.Target).Coordinates);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cluwne Curse
|
||||
|
||||
private void OnCluwneCurseSpell(CluwneCurseSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
if (!HasComp<HumanoidAppearanceComponent>(msg.Target))
|
||||
{
|
||||
_popupSystem.PopupEntity("Работает только на людях!", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureComp<CluwneComponent>(msg.Target);
|
||||
|
||||
Spawn("AdminInstantEffectSmoke3", Transform(msg.Target).Coordinates);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EMP
|
||||
|
||||
private void OnEmpSpell(EmpSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
var coords = _transformSystem.ToMapCoordinates(Transform(msg.Performer).Coordinates);
|
||||
|
||||
_empSystem.EmpPulse(coords, 15, 1000000, 60f);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ethereal Jaunt
|
||||
|
||||
private void OnJauntSpell(EtherealJauntSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
if (_statusEffectsSystem.HasStatusEffect(msg.Performer, "Incorporeal"))
|
||||
{
|
||||
_popupSystem.PopupEntity("Вы уже в потустороннем мире", msg.Performer, msg.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
Spawn("AdminInstantEffectSmoke10", Transform(msg.Performer).Coordinates);
|
||||
|
||||
_statusEffectsSystem.TryAddStatusEffect<IncorporealComponent>(msg.Performer, "Incorporeal", TimeSpan.FromSeconds(10), false);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Blink
|
||||
|
||||
private void OnBlinkSpell(BlinkSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
var transform = Transform(msg.Performer);
|
||||
|
||||
var oldCoords = transform.Coordinates;
|
||||
|
||||
EntityCoordinates coords = default;
|
||||
var foundTeleportPos = false;
|
||||
var attempts = 10;
|
||||
|
||||
while (attempts > 0)
|
||||
{
|
||||
attempts--;
|
||||
|
||||
var random = new Random().Next(10, 20);
|
||||
var offset = transform.LocalRotation.ToWorldVec().Normalized();
|
||||
var direction = transform.LocalRotation.GetDir().ToVec();
|
||||
var newOffset = offset + direction * random;
|
||||
coords = transform.Coordinates.Offset(newOffset).SnapToGrid(EntityManager);
|
||||
|
||||
var tile = coords.GetTileRef();
|
||||
|
||||
if (tile != null && _turf.IsTileBlocked(tile.Value, CollisionGroup.AllMask))
|
||||
continue;
|
||||
|
||||
foundTeleportPos = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundTeleportPos)
|
||||
return;
|
||||
|
||||
_transformSystem.SetCoordinates(msg.Performer, coords);
|
||||
_transformSystem.AttachToGridOrMap(msg.Performer);
|
||||
|
||||
_audio.PlayPvs("/Audio/White/Cult/veilin.ogg", coords);
|
||||
_audio.PlayPvs("/Audio/White/Cult/veilout.ogg", oldCoords);
|
||||
|
||||
Spawn("AdminInstantEffectSmoke10", oldCoords);
|
||||
Spawn("AdminInstantEffectSmoke10", coords);
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Forcewall
|
||||
|
||||
private void OnForcewallSpell(ForceWallSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
switch (msg.ActionUseType)
|
||||
{
|
||||
case ActionUseType.Default:
|
||||
ForcewallSpellDefault(msg);
|
||||
break;
|
||||
case ActionUseType.Charge:
|
||||
ForcewallSpellCharge(msg);
|
||||
break;
|
||||
case ActionUseType.AltUse:
|
||||
ForcewallSpellAlt(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
private void ForcewallSpellDefault(ForceWallSpellEvent msg)
|
||||
{
|
||||
var transform = Transform(msg.Performer);
|
||||
|
||||
foreach (var position in _magicSystem.GetPositionsInFront(transform))
|
||||
{
|
||||
var ent = Spawn(msg.Prototype, position.SnapToGrid(EntityManager, _mapManager));
|
||||
|
||||
var comp = EnsureComp<PreventCollideComponent>(ent);
|
||||
comp.Uid = msg.Performer;
|
||||
}
|
||||
}
|
||||
|
||||
private void ForcewallSpellCharge(ForceWallSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.Performer);
|
||||
|
||||
var positions = GetArenaPositions(xform, msg.ChargeLevel);
|
||||
|
||||
foreach (var position in positions)
|
||||
{
|
||||
var ent = Spawn(msg.Prototype, position);
|
||||
|
||||
var comp = EnsureComp<PreventCollideComponent>(ent);
|
||||
comp.Uid = msg.Performer;
|
||||
}
|
||||
}
|
||||
|
||||
private void ForcewallSpellAlt(ForceWallSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.TargetUid);
|
||||
|
||||
var positions = GetArenaPositions(xform, 2);
|
||||
|
||||
foreach (var direction in positions)
|
||||
{
|
||||
var ent = Spawn(msg.Prototype, direction);
|
||||
|
||||
var comp = EnsureComp<PreventCollideComponent>(ent);
|
||||
comp.Uid = msg.Performer;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cards
|
||||
|
||||
private void OnCardsSpell(CardsSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
switch (msg.ActionUseType)
|
||||
{
|
||||
case ActionUseType.Default:
|
||||
CardsSpellDefault(msg);
|
||||
break;
|
||||
case ActionUseType.Charge:
|
||||
CardsSpellCharge(msg);
|
||||
break;
|
||||
case ActionUseType.AltUse:
|
||||
CardsSpellAlt(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
private void CardsSpellDefault(CardsSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.Performer);
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
foreach (var pos in _magicSystem.GetSpawnPositions(xform, msg.Pos))
|
||||
{
|
||||
var mapPos = _transformSystem.ToMapCoordinates(pos);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out _)
|
||||
? pos.WithEntityId(gridUid, EntityManager)
|
||||
: new EntityCoordinates(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
|
||||
var ent = Spawn(msg.Prototype, spawnCoords);
|
||||
|
||||
var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - spawnCoords.ToMapPos(EntityManager, _transformSystem);
|
||||
var randomizedDirection = direction + new Vector2(_random.Next(-2, 2), _random.Next(-2, 2));
|
||||
|
||||
_throwingSystem.TryThrow(ent, randomizedDirection, 60, msg.Performer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CardsSpellCharge(CardsSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.Performer);
|
||||
|
||||
var count = 5 * msg.ChargeLevel;
|
||||
var angleStep = 360f / count;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var angle = i * angleStep;
|
||||
|
||||
var direction = new Vector2(MathF.Cos(MathHelper.DegreesToRadians(angle)), MathF.Sin(MathHelper.DegreesToRadians(angle)));
|
||||
|
||||
foreach (var pos in _magicSystem.GetSpawnPositions(xform, msg.Pos))
|
||||
{
|
||||
var mapPos = _transformSystem.ToMapCoordinates(pos);
|
||||
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out _)
|
||||
? pos.WithEntityId(gridUid, EntityManager)
|
||||
: new EntityCoordinates(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
|
||||
var ent = Spawn(msg.Prototype, spawnCoords);
|
||||
|
||||
_throwingSystem.TryThrow(ent, direction, 60, msg.Performer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CardsSpellAlt(CardsSpellEvent msg)
|
||||
{
|
||||
if (!HasComp<ItemComponent>(msg.TargetUid))
|
||||
return;
|
||||
|
||||
Del(msg.TargetUid);
|
||||
var item = Spawn(msg.Prototype);
|
||||
_handsSystem.TryPickupAnyHand(msg.Performer, item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fireball
|
||||
|
||||
private void OnFireballSpell(FireballSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
switch (msg.ActionUseType)
|
||||
{
|
||||
case ActionUseType.Default:
|
||||
FireballSpellDefault(msg);
|
||||
break;
|
||||
case ActionUseType.Charge:
|
||||
FireballSpellCharge(msg);
|
||||
break;
|
||||
case ActionUseType.AltUse:
|
||||
FireballSpellAlt(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
private void FireballSpellDefault(FireballSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.Performer);
|
||||
|
||||
foreach (var pos in _magicSystem.GetSpawnPositions(xform, msg.Pos))
|
||||
{
|
||||
var mapPos = _transformSystem.ToMapCoordinates(pos);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out var grid)
|
||||
? pos.WithEntityId(gridUid, EntityManager)
|
||||
: new EntityCoordinates(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
|
||||
var userVelocity = Vector2.Zero;
|
||||
|
||||
if (grid != null && TryComp(gridUid, out PhysicsComponent? physics))
|
||||
userVelocity = physics.LinearVelocity;
|
||||
|
||||
var ent = Spawn(msg.Prototype, spawnCoords);
|
||||
var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - spawnCoords.ToMapPos(EntityManager, _transformSystem);
|
||||
_gunSystem.ShootProjectile(ent, direction, userVelocity, msg.Performer, msg.Performer);
|
||||
}
|
||||
}
|
||||
|
||||
private void FireballSpellCharge(FireballSpellEvent msg)
|
||||
{
|
||||
var coords = Transform(msg.Performer).Coordinates;
|
||||
|
||||
var targets = _lookup.GetEntitiesInRange<FlammableComponent>(coords, 2 * msg.ChargeLevel);
|
||||
|
||||
foreach (var target in targets.Where(target => target.Owner != msg.Performer))
|
||||
{
|
||||
target.Comp.FireStacks += 3;
|
||||
_flammableSystem.Ignite(target, msg.Performer);
|
||||
}
|
||||
}
|
||||
|
||||
private void FireballSpellAlt(FireballSpellEvent msg)
|
||||
{
|
||||
if (!TryComp<FlammableComponent>(msg.TargetUid, out var flammableComponent))
|
||||
return;
|
||||
|
||||
flammableComponent.FireStacks += 4;
|
||||
|
||||
_flammableSystem.Ignite(msg.TargetUid, msg.Performer);
|
||||
|
||||
EnsureComp<AmaterasuComponent>(msg.TargetUid);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Force
|
||||
|
||||
private void OnForceSpell(ForceSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
switch (msg.ActionUseType)
|
||||
{
|
||||
case ActionUseType.Default:
|
||||
ForceSpellDefault(msg);
|
||||
break;
|
||||
case ActionUseType.Charge:
|
||||
ForceSpellCharge(msg);
|
||||
break;
|
||||
case ActionUseType.AltUse:
|
||||
ForceSpellAlt(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
private void ForceSpellDefault(ForceSpellEvent msg)
|
||||
{
|
||||
Spawn("AdminInstantEffectMinusGravityWell", msg.Target);
|
||||
}
|
||||
|
||||
private void ForceSpellCharge(ForceSpellEvent msg)
|
||||
{
|
||||
_gravityWell.GravPulse(msg.Performer, 15, 0, -80 * msg.ChargeLevel, -2 * msg.ChargeLevel);
|
||||
}
|
||||
|
||||
private void ForceSpellAlt(ForceSpellEvent msg)
|
||||
{
|
||||
_gravityWell.GravPulse(msg.Target, 10, 0, 200, 10);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Arc
|
||||
|
||||
private void OnArcSpell(ArcSpellEvent msg)
|
||||
{
|
||||
if (msg.Handled || !CheckRequirements(msg.Action, msg.Performer))
|
||||
return;
|
||||
|
||||
switch (msg.ActionUseType)
|
||||
{
|
||||
case ActionUseType.Default:
|
||||
ArcSpellDefault(msg);
|
||||
break;
|
||||
case ActionUseType.Charge:
|
||||
ArcSpellCharge(msg);
|
||||
break;
|
||||
case ActionUseType.AltUse:
|
||||
ArcSpellAlt(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
msg.Handled = true;
|
||||
Speak(msg);
|
||||
}
|
||||
|
||||
private void ArcSpellDefault(ArcSpellEvent msg)
|
||||
{
|
||||
const int possibleEntitiesCount = 2;
|
||||
|
||||
var entitiesInRange = _lookup.GetEntitiesInRange(msg.Target, 1);
|
||||
var entitiesToHit = entitiesInRange.Where(HasComp<MobStateComponent>).Take(possibleEntitiesCount);
|
||||
|
||||
foreach (var entity in entitiesToHit)
|
||||
{
|
||||
_lightning.ShootLightning(msg.Performer, entity);
|
||||
}
|
||||
}
|
||||
|
||||
private void ArcSpellCharge(ArcSpellEvent msg)
|
||||
{
|
||||
_lightning.ShootRandomLightnings(msg.Performer, 2 * msg.ChargeLevel, msg.ChargeLevel * 2, arcDepth: 2);
|
||||
}
|
||||
|
||||
private void ArcSpellAlt(ArcSpellEvent msg)
|
||||
{
|
||||
var xform = Transform(msg.Performer);
|
||||
|
||||
foreach (var pos in _magicSystem.GetSpawnPositions(xform, msg.Pos))
|
||||
{
|
||||
var mapPos = _transformSystem.ToMapCoordinates(pos);
|
||||
var spawnCoords = _mapManager.TryFindGridAt(mapPos, out var gridUid, out var grid)
|
||||
? pos.WithEntityId(gridUid, EntityManager)
|
||||
: new EntityCoordinates(_mapManager.GetMapEntityId(mapPos.MapId), mapPos.Position);
|
||||
|
||||
var userVelocity = Vector2.Zero;
|
||||
|
||||
if (grid != null && TryComp(gridUid, out PhysicsComponent? physics))
|
||||
userVelocity = physics.LinearVelocity;
|
||||
|
||||
var ent = Spawn(msg.Prototype, spawnCoords);
|
||||
var direction = msg.Target.ToMapPos(EntityManager, _transformSystem) - spawnCoords.ToMapPos(EntityManager, _transformSystem);
|
||||
_gunSystem.ShootProjectile(ent, direction, userVelocity, msg.Performer, msg.Performer);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
private void Speak(BaseActionEvent args)
|
||||
{
|
||||
if (args is not ISpeakSpell speak || string.IsNullOrWhiteSpace(speak.Speech))
|
||||
return;
|
||||
|
||||
_chat.TrySendInGameICMessage(args.Performer, Loc.GetString(speak.Speech),
|
||||
InGameICChatType.Speak, false);
|
||||
}
|
||||
|
||||
private List<EntityCoordinates> GetArenaPositions(TransformComponent casterXform, int arenaSize)
|
||||
{
|
||||
var positions = new List<EntityCoordinates>();
|
||||
|
||||
arenaSize--;
|
||||
|
||||
for (var i = -arenaSize; i <= arenaSize; i++)
|
||||
{
|
||||
for (var j = -arenaSize; j <= arenaSize; j++)
|
||||
{
|
||||
var position = new Vector2(i, j);
|
||||
var coordinates = casterXform.Coordinates.Offset(position);
|
||||
positions.Add(coordinates);
|
||||
}
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
private bool CheckRequirements(EntityUid spell, EntityUid performer)
|
||||
{
|
||||
var ev = new BeforeCastSpellEvent(performer);
|
||||
RaiseLocalEvent(spell, ref ev);
|
||||
return !ev.Cancelled;
|
||||
}
|
||||
|
||||
private void OnBeforeCastSpell(Entity<MagicComponent> ent, ref BeforeCastSpellEvent args)
|
||||
{
|
||||
var comp = ent.Comp;
|
||||
var hasReqs = false;
|
||||
|
||||
if (comp.RequiresClothes)
|
||||
{
|
||||
var enumerator = _inventory.GetSlotEnumerator(args.Performer, SlotFlags.OUTERCLOTHING | SlotFlags.HEAD);
|
||||
while (enumerator.MoveNext(out var containerSlot))
|
||||
{
|
||||
if (containerSlot.ContainedEntity is { } item)
|
||||
hasReqs = HasComp<WizardClothesComponent>(item);
|
||||
else
|
||||
hasReqs = false;
|
||||
|
||||
if (!hasReqs)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasReqs)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
_popupSystem.PopupEntity("Missing Requirements! You need to wear your robe and hat!", args.Performer, args.Performer);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
8
Content.Server/_White/Wizard/Scrolls/ScrollSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared._White.Wizard.ScrollSystem;
|
||||
|
||||
namespace Content.Server._White.Wizard.Scrolls;
|
||||
|
||||
public sealed class ScrollSystem : SharedScrollSystem
|
||||
{
|
||||
protected override void BurnScroll(EntityUid uid) => Del(uid);
|
||||
}
|
||||
@@ -81,8 +81,10 @@ public sealed class GetItemActionsEvent : EntityEventArgs
|
||||
public sealed class RequestPerformActionEvent : EntityEventArgs
|
||||
{
|
||||
public readonly NetEntity Action;
|
||||
public readonly NetEntity? EntityTarget;
|
||||
public NetEntity? EntityTarget;
|
||||
public readonly NetCoordinates? EntityCoordinatesTarget;
|
||||
public ActionUseType ActionUseType = ActionUseType.Default;
|
||||
public int ChargeLevel;
|
||||
|
||||
public RequestPerformActionEvent(NetEntity action)
|
||||
{
|
||||
@@ -148,6 +150,8 @@ public abstract partial class WorldTargetActionEvent : BaseActionEvent
|
||||
/// The coordinates of the location that the user targeted.
|
||||
/// </summary>
|
||||
public EntityCoordinates Target;
|
||||
|
||||
public EntityUid TargetUid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -161,4 +165,18 @@ public abstract partial class BaseActionEvent : HandledEntityEventArgs
|
||||
/// The user performing the action.
|
||||
/// </summary>
|
||||
public EntityUid Performer;
|
||||
|
||||
public EntityUid Action;
|
||||
|
||||
public ActionUseType ActionUseType = ActionUseType.Default;
|
||||
|
||||
public int ChargeLevel;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ActionUseType
|
||||
{
|
||||
Default, // left mouse click.
|
||||
Charge, // Holding right mouse click(has 4 charges).
|
||||
AltUse // Alt + left mouse click.
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Shared.Actions;
|
||||
|
||||
@@ -40,4 +41,28 @@ public abstract partial class BaseTargetActionComponent : BaseActionComponent
|
||||
/// over lay in place of the currently held item "held item".
|
||||
/// </summary>
|
||||
[DataField("targetingIndicator")] public bool TargetingIndicator = true;
|
||||
|
||||
[DataField]
|
||||
public bool IsAltEnabled;
|
||||
|
||||
[DataField]
|
||||
public bool IsChargeEnabled;
|
||||
|
||||
[DataField]
|
||||
public string ChargeProto = "MagicFollowerEntity";
|
||||
|
||||
[DataField]
|
||||
public int MaxChargeLevel = 4;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier ChargingSound = new SoundPathSpecifier("/Audio/White/Magic/chargingfallback.ogg");
|
||||
|
||||
[DataField]
|
||||
public bool LoopCharging = true;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier MaxChargedSound = new SoundPathSpecifier("/Audio/White/Magic/maxchargefallback.ogg");
|
||||
|
||||
[DataField]
|
||||
public bool LoopMaxCharged;
|
||||
}
|
||||
|
||||
@@ -413,6 +413,8 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
if (worldAction.Event != null)
|
||||
{
|
||||
worldAction.Event.Target = entityCoordinatesTarget;
|
||||
if (ev.EntityTarget != null)
|
||||
worldAction.Event.TargetUid = GetEntity(ev.EntityTarget.Value);
|
||||
Dirty(actionEnt, worldAction);
|
||||
performEvent = worldAction.Event;
|
||||
}
|
||||
@@ -430,7 +432,12 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
}
|
||||
|
||||
if (performEvent != null)
|
||||
{
|
||||
performEvent.Performer = user;
|
||||
performEvent.Action = actionEnt;
|
||||
performEvent.ActionUseType = ev.ActionUseType;
|
||||
performEvent.ChargeLevel = ev.ChargeLevel;
|
||||
}
|
||||
|
||||
// All checks passed. Perform the action!
|
||||
PerformAction(user, component, actionEnt, action, performEvent, curTime);
|
||||
@@ -677,6 +684,8 @@ public abstract class SharedActionsSystem : EntitySystem
|
||||
/// <param name="performer">Entity to receive the actions</param>
|
||||
/// <param name="actions">The actions to add</param>
|
||||
/// <param name="container">The entity that enables these actions (e.g., flashlight). May be null (innate actions).</param>
|
||||
/// <param name="comp">ActionsComponent.</param>
|
||||
/// <param name="containerComp">ActionContainerComponent.</param>
|
||||
public void GrantActions(EntityUid performer, IEnumerable<EntityUid> actions, EntityUid container, ActionsComponent? comp = null, ActionsContainerComponent? containerComp = null)
|
||||
{
|
||||
if (!Resolve(container, ref containerComp))
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Content.Shared._White.Wizard.Charging;
|
||||
|
||||
public abstract class SharedChargingSystem : EntitySystem
|
||||
{
|
||||
}
|
||||
13
Content.Shared/_White/Wizard/Magic/MagicComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Wizard.Magic;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class MagicComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Does this spell require Wizard Robes & Hat?
|
||||
/// </summary>
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool RequiresClothes;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Wizard.Magic;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class WizardClothesComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
43
Content.Shared/_White/Wizard/ScrollSystem/ScrollComponent.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._White.Wizard.ScrollSystem;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedScrollSystem))]
|
||||
public sealed partial class ScrollComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// ActionId to give on use.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables]
|
||||
public string ActionId;
|
||||
|
||||
/// <summary>
|
||||
/// How time it takes to learn.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float LearnTime = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// Popup on learn.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables]
|
||||
public string LearnPopup;
|
||||
|
||||
/// <summary>
|
||||
/// Sound to play on use.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables]
|
||||
public SoundSpecifier UseSound;
|
||||
|
||||
/// <summary>
|
||||
/// Sound to play after use.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables]
|
||||
public SoundSpecifier AfterUseSound;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Shared._White.Wizard.ScrollSystem;
|
||||
|
||||
public abstract class SharedScrollSystem : EntitySystem
|
||||
{
|
||||
#region Dependencies
|
||||
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ScrollComponent, UseInHandEvent>(OnScrollUse);
|
||||
SubscribeLocalEvent<ScrollComponent, ScrollDoAfterEvent>(OnScrollDoAfter);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Handlers
|
||||
|
||||
private void OnScrollUse(EntityUid uid, ScrollComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.LearnTime, new ScrollDoAfterEvent(), uid, target: uid)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
NeedHand = true
|
||||
};
|
||||
|
||||
if (_net.IsServer)
|
||||
{
|
||||
_audioSystem.PlayPvs(component.UseSound, args.User);
|
||||
}
|
||||
|
||||
_popupSystem.PopupClient($"You start learning about {component.LearnPopup}.", args.User, args.User, PopupType.Medium);
|
||||
|
||||
_doAfterSystem.TryStartDoAfter(doAfterEventArgs);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnScrollDoAfter(EntityUid uid, ScrollComponent component, ScrollDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled)
|
||||
return;
|
||||
|
||||
_actionsSystem.AddAction(args.User, component.ActionId);
|
||||
|
||||
if (_net.IsServer)
|
||||
{
|
||||
_audioSystem.PlayEntity(component.AfterUseSound, args.User, args.User);
|
||||
}
|
||||
|
||||
_popupSystem.PopupClient($"You learned much about {component.LearnPopup}. The scroll is slowly burning in your hands.", args.User, args.User, PopupType.Medium);
|
||||
|
||||
BurnScroll(uid);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
protected virtual void BurnScroll(EntityUid uid) {}
|
||||
|
||||
#endregion
|
||||
}
|
||||
175
Content.Shared/_White/Wizard/WizardEvents.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Magic;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared._White.Wizard;
|
||||
|
||||
#region HelperEvents
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class ScrollDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
|
||||
[ByRefEvent]
|
||||
public struct BeforeCastSpellEvent
|
||||
{
|
||||
public EntityUid Performer;
|
||||
|
||||
public bool Cancelled;
|
||||
|
||||
public BeforeCastSpellEvent(EntityUid performer)
|
||||
{
|
||||
Performer = performer;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class AddWizardChargeEvent : EntityEventArgs
|
||||
{
|
||||
public string ChargeProto;
|
||||
|
||||
public AddWizardChargeEvent(string chargeProto)
|
||||
{
|
||||
ChargeProto = chargeProto;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class RemoveWizardChargeEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class RequestSpellChargingAudio : EntityEventArgs
|
||||
{
|
||||
public SoundSpecifier Sound;
|
||||
public bool Loop;
|
||||
|
||||
public RequestSpellChargingAudio(SoundSpecifier sound, bool loop)
|
||||
{
|
||||
Sound = sound;
|
||||
Loop = loop;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class RequestSpellChargedAudio : EntityEventArgs
|
||||
{
|
||||
public SoundSpecifier Sound;
|
||||
public bool Loop;
|
||||
|
||||
public RequestSpellChargedAudio(SoundSpecifier sound, bool loop)
|
||||
{
|
||||
Sound = sound;
|
||||
Loop = loop;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class RequestAudioSpellStop : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Spells
|
||||
|
||||
public sealed partial class ArcSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype = default!;
|
||||
|
||||
[DataField("posData")]
|
||||
public MagicSpawnData Pos = new TargetCasterPos();
|
||||
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class ForceSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class FireballSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype = default!;
|
||||
|
||||
[DataField("posData")]
|
||||
public MagicSpawnData Pos = new TargetCasterPos();
|
||||
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class CardsSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype = default!;
|
||||
|
||||
[DataField("posData")]
|
||||
public MagicSpawnData Pos = new TargetCasterPos();
|
||||
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class ForceWallSpellEvent : WorldTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype = default!;
|
||||
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class BlinkSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class EtherealJauntSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class EmpSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class CluwneCurseSpellEvent : EntityTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class BananaTouchSpellEvent : EntityTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class MimeTouchSpellEvent : EntityTargetActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
public sealed partial class InstantRecallSpellEvent : InstantActionEvent, ISpeakSpell
|
||||
{
|
||||
[DataField("speech")]
|
||||
public string? Speech { get; private set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -152,3 +152,23 @@
|
||||
license: "CC0-1.0"
|
||||
copyright: "dakamakat on freesound.org"
|
||||
source: "https://freesound.org/people/Dakamakat/sounds/717370/"
|
||||
|
||||
- files: ["energyshield_up.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: "unfa on freesound.org"
|
||||
source: "https://freesound.org/people/unfa/sounds/584173/"
|
||||
|
||||
- files: ["energyshield_down.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: "SilverIllusionist on freesound.org"
|
||||
source: "https://freesound.org/people/SilverIllusionist/sounds/673556/"
|
||||
|
||||
- files: ["energyshield_ambient.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: "julianmateo_ on freesound.org"
|
||||
source: "https://freesound.org/people/julianmateo_/sounds/524165/"
|
||||
|
||||
- files: ["energyshield_parry.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: "Robinhood76 on freesound.org"
|
||||
source: "https://freesound.org/people/Robinhood76/sounds/107613/"
|
||||
BIN
Resources/Audio/Machines/energyshield_ambient.ogg
Normal file
BIN
Resources/Audio/Machines/energyshield_down.ogg
Normal file
BIN
Resources/Audio/Machines/energyshield_parry.ogg
Normal file
BIN
Resources/Audio/Machines/energyshield_up.ogg
Normal file
BIN
Resources/Audio/White/Items/scroll/after_use.ogg
Normal file
BIN
Resources/Audio/White/Items/scroll/use.ogg
Normal file
BIN
Resources/Audio/White/Magic/Arc/cast.ogg
Normal file
BIN
Resources/Audio/White/Magic/Arc/charge.ogg
Normal file
BIN
Resources/Audio/White/Magic/Arc/max.ogg
Normal file
BIN
Resources/Audio/White/Magic/Cards/cast.ogg
Normal file
BIN
Resources/Audio/White/Magic/Cards/charge.ogg
Normal file
BIN
Resources/Audio/White/Magic/Cards/max.ogg
Normal file
BIN
Resources/Audio/White/Magic/Force/cast.ogg
Normal file
BIN
Resources/Audio/White/Magic/Force/charge.ogg
Normal file
BIN
Resources/Audio/White/Magic/Force/max.ogg
Normal file
BIN
Resources/Audio/White/Magic/chargingfallback.ogg
Normal file
BIN
Resources/Audio/White/Magic/maxchargefallback.ogg
Normal file
9
Resources/Locale/en-US/EnergyDome/energydome.ftl
Normal file
@@ -0,0 +1,9 @@
|
||||
energy-dome-access-denied = Access denied
|
||||
energy-dome-recharging = Recharging...
|
||||
energy-dome-no-power = Low battery
|
||||
energy-dome-no-cell = There is no power source
|
||||
|
||||
energy-dome-on-examine-is-on-message = The energy barrier is [color=darkgreen]up[/color].
|
||||
energy-dome-on-examine-is-off-message = The energy barrier is [color=darkred]down[/color].
|
||||
|
||||
energy-dome-verb-toggle = Toggle energy dome
|
||||
@@ -32,6 +32,7 @@ research-technology-wave-particle-harnessing = Wave Particle Harnessing
|
||||
research-technology-advanced-riot-control = Advanced Riot Control
|
||||
research-technology-portable-microfusion-weaponry = Portable Microfusion Weaponry
|
||||
research-technology-experimental-battery-ammo = Experimental Battery Ammo
|
||||
research-technology-energy_barriers = Energy Barriers
|
||||
research-technology-basic-shuttle-armament = Shuttle basic armament
|
||||
research-technology-advanced-shuttle-weapon = Advanced shuttle weapons
|
||||
|
||||
|
||||
@@ -306,6 +306,9 @@ uplink-hardsuit-syndieelite-desc = An elite version of the blood-red hardsuit, w
|
||||
uplink-clothing-outer-hardsuit-juggernaut-name = Cybersun Juggernaut Suit
|
||||
uplink-clothing-outer-hardsuit-juggernaut-desc = Hyper resilient armor made of materials tested in the Tau chromosphere facility. The only thing that's going to be slowing you down is this suit... and tasers.
|
||||
|
||||
uplink-energy-dome-name = Personal energy dome
|
||||
uplink-energy-dome-desc = A personal shield generator that protects the wearer from lasers and bullets but prevents from using ranged weapons himself. Comes with a small power cell.
|
||||
|
||||
# Misc
|
||||
uplink-cyberpen-name = Cybersun Pen
|
||||
uplink-cyberpen-desc = Cybersun's legal department pen, invaluable for forging documents and escaping prisons. Smells vaguely of hard-light and war profiteering.
|
||||
|
||||
@@ -33,6 +33,16 @@
|
||||
iconOn: Objects/Tools/flashlight.rsi/flashlight-on.png
|
||||
event: !type:ToggleActionEvent
|
||||
|
||||
- type: entity
|
||||
id: ActionToggleDome
|
||||
name: Toggle energy dome
|
||||
description: Turn the energy barrier on or off.
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: InstantAction
|
||||
icon: { sprite: Objects/Weapons/Melee/e_shield.rsi, state: eshield-on }
|
||||
event: !type:ToggleActionEvent
|
||||
|
||||
- type: entity
|
||||
id: ActionOpenStorageImplant
|
||||
name: Open Storage Implant
|
||||
|
||||
@@ -171,6 +171,16 @@
|
||||
flatReductions:
|
||||
Heat: 3
|
||||
|
||||
- type: damageModifierSet
|
||||
id: HardLightBarrier
|
||||
coefficients:
|
||||
Heat: 0.8
|
||||
Blunt: 0.8
|
||||
Slash: 0.8
|
||||
Piercing: 0.8
|
||||
Cold: 0.8
|
||||
Shock: 1.6
|
||||
|
||||
- type: damageModifierSet
|
||||
id: Scale # Skin tougher, bones weaker, strong stomachs, cold-blooded (kindof)
|
||||
coefficients:
|
||||
|
||||
@@ -664,12 +664,36 @@
|
||||
Heat: 0.25
|
||||
Radiation: 0.25
|
||||
Caustic: 0.75
|
||||
- type: ClothingSpeedModifier
|
||||
walkModifier: 0.8
|
||||
sprintModifier: 0.8
|
||||
- type: HeldSpeedModifier
|
||||
- type: ToggleableClothing
|
||||
clothingPrototype: ClothingHeadHelmetHardsuitWizard
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
cell_slot: !type:ContainerSlot
|
||||
toggleable-clothing: !type:ContainerSlot
|
||||
- type: PowerCellSlot
|
||||
cellSlotId: cell_slot
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
cell_slot:
|
||||
name: power-cell-slot-component-slot-name-default
|
||||
startingItem: PowerCellMicroreactor
|
||||
whitelist:
|
||||
tags:
|
||||
- PowerCell
|
||||
- PowerCellSmall
|
||||
- PowerCellHyper
|
||||
- PowerCellMicroreactor
|
||||
- type: EnergyDomeGenerator
|
||||
damageEnergyDraw: 3
|
||||
domePrototype: EnergyDomeSmallPink
|
||||
- type: ClothingSpeedModifier
|
||||
walkModifier: 1
|
||||
sprintModifier: 1
|
||||
- type: PowerCellDraw
|
||||
drawRate: 8
|
||||
useRate: 0
|
||||
- type: UseDelay
|
||||
delay: 10.0
|
||||
|
||||
#Ling Space Suit
|
||||
- type: entity
|
||||
|
||||
@@ -103,10 +103,34 @@
|
||||
path: /Audio/Effects/Grenades/Supermatter/supermatter_loop.ogg
|
||||
- type: GravityWell
|
||||
maxRange: 8
|
||||
baseRadialAcceleration: 10
|
||||
baseRadialAcceleration: 250
|
||||
baseTangentialAcceleration: 0
|
||||
gravPulsePeriod: 0.01
|
||||
- type: SingularityDistortion
|
||||
intensity: 10
|
||||
falloffPower: 1.5
|
||||
|
||||
- type: entity
|
||||
id: AdminInstantEffectMinusGravityWell
|
||||
suffix: Gravity Well
|
||||
parent: AdminInstantEffectBase
|
||||
components:
|
||||
- type: SoundOnTrigger
|
||||
removeOnTrigger: true
|
||||
sound:
|
||||
path: /Audio/Effects/Grenades/Supermatter/supermatter_start.ogg
|
||||
volume: 5
|
||||
- type: AmbientSound
|
||||
enabled: true
|
||||
volume: -5
|
||||
range: 14
|
||||
sound:
|
||||
path: /Audio/Effects/Grenades/Supermatter/supermatter_loop.ogg
|
||||
- type: GravityWell
|
||||
maxRange: 10
|
||||
baseRadialAcceleration: -200
|
||||
baseTangentialAcceleration: -5
|
||||
gravPulsePeriod: 0.01
|
||||
- type: SingularityDistortion
|
||||
intensity: 10
|
||||
falloffPower: 1.5
|
||||
|
||||
129
Resources/Prototypes/Entities/Effects/dome.yml
Normal file
@@ -0,0 +1,129 @@
|
||||
- type: entity
|
||||
id: EnergyDomeBase
|
||||
abstract: true
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: Effects
|
||||
noRot: true
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeCircle
|
||||
radius: 0.8
|
||||
density: 0
|
||||
mask:
|
||||
- None
|
||||
layer:
|
||||
- BulletImpassable
|
||||
- Opaque
|
||||
- type: Physics
|
||||
bodyType: Static
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
damageModifierSet: HardLightBarrier
|
||||
- type: AmbientSound
|
||||
volume: 35
|
||||
range: 5
|
||||
sound:
|
||||
path: /Audio/Machines/energyshield_ambient.ogg
|
||||
- type: EnergyDome
|
||||
- type: Tag
|
||||
tags:
|
||||
- HideContextMenu
|
||||
|
||||
- type: entity
|
||||
id: EnergyDomeSmallPink
|
||||
noSpawn: true
|
||||
parent: EnergyDomeBase
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Effects/EnergyDome/energydome_small.rsi
|
||||
layers:
|
||||
- state: small
|
||||
color: "#f5166b"
|
||||
- type: PointLight
|
||||
enabled: true
|
||||
radius: 5
|
||||
power: 2
|
||||
color: "#f5166b"
|
||||
|
||||
- type: entity
|
||||
id: EnergyDomeSmallRed
|
||||
noSpawn: true
|
||||
parent: EnergyDomeBase
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: Effects
|
||||
noRot: true
|
||||
sprite: Effects/EnergyDome/energydome_small.rsi
|
||||
layers:
|
||||
- state: small
|
||||
color: "#b00000"
|
||||
- type: PointLight
|
||||
enabled: true
|
||||
radius: 5
|
||||
power: 2
|
||||
color: "#b00000"
|
||||
|
||||
- type: entity
|
||||
id: EnergyDomeMediumBlue
|
||||
noSpawn: true
|
||||
parent: EnergyDomeBase
|
||||
components:
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeCircle
|
||||
radius: 1.8
|
||||
density: 0
|
||||
mask:
|
||||
- None
|
||||
layer:
|
||||
- BulletImpassable
|
||||
- Opaque
|
||||
- type: Sprite
|
||||
sprite: Effects/EnergyDome/energydome_medium.rsi
|
||||
layers:
|
||||
- state: medium
|
||||
color: "#64b9de"
|
||||
- type: PointLight
|
||||
enabled: true
|
||||
radius: 5
|
||||
power: 10
|
||||
color: "#64b9de"
|
||||
|
||||
- type: entity
|
||||
id: EnergyDomeSlowing
|
||||
noSpawn: true
|
||||
parent: EnergyDomeBase
|
||||
components:
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeCircle
|
||||
radius: 2.8
|
||||
density: 0
|
||||
hard: false
|
||||
mask:
|
||||
- None
|
||||
layer:
|
||||
- MidImpassable
|
||||
- type: Sprite
|
||||
drawdepth: LowFloors
|
||||
sprite: Effects/EnergyDome/energydome_slowdown_big.rsi
|
||||
layers:
|
||||
- state: big
|
||||
color: "#a3d177"
|
||||
- type: PointLight
|
||||
enabled: true
|
||||
radius: 5
|
||||
power: 30
|
||||
color: "#a3d177"
|
||||
- type: DamageContacts
|
||||
damage:
|
||||
types:
|
||||
Slash: -1.5
|
||||
Piercing: -1.5
|
||||
@@ -297,6 +297,7 @@
|
||||
- Muted
|
||||
- Pacified
|
||||
- StaminaModifier
|
||||
- Incorporeal
|
||||
- type: Blindable
|
||||
# Other
|
||||
- type: Temperature
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
- type: entity
|
||||
id: BaseSpellbook
|
||||
name: spellbook
|
||||
parent: BaseItem
|
||||
abstract: true
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/books.rsi
|
||||
layers:
|
||||
- state: book_demonomicon
|
||||
- type: Spellbook
|
||||
- type: Tag
|
||||
tags:
|
||||
- Spellbook
|
||||
|
||||
- type: entity
|
||||
id: SpawnSpellbook
|
||||
name: spawn spellbook
|
||||
parent: BaseSpellbook
|
||||
components:
|
||||
- type: Spellbook
|
||||
spells:
|
||||
ActionSpawnMagicarpSpell: -1
|
||||
|
||||
- type: entity
|
||||
id: ForceWallSpellbook
|
||||
name: force wall spellbook
|
||||
parent: BaseSpellbook
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Magic/spellbooks.rsi
|
||||
layers:
|
||||
- state: bookforcewall
|
||||
- type: Spellbook
|
||||
spells:
|
||||
ActionForceWall: -1
|
||||
|
||||
- type: entity
|
||||
id: BlinkBook
|
||||
name: blink spellbook
|
||||
parent: BaseSpellbook
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Magic/spellbooks.rsi
|
||||
layers:
|
||||
- state: spellbook
|
||||
- type: Spellbook
|
||||
spells:
|
||||
ActionBlink: -1
|
||||
|
||||
- type: entity
|
||||
id: SmiteBook
|
||||
name: smite spellbook
|
||||
parent: BaseSpellbook
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Magic/spellbooks.rsi
|
||||
layers:
|
||||
- state: spellbook
|
||||
- type: Spellbook
|
||||
spells:
|
||||
ActionSmite: -1
|
||||
|
||||
- type: entity
|
||||
id: KnockSpellbook
|
||||
name: knock spellbook
|
||||
parent: BaseSpellbook
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Magic/spellbooks.rsi
|
||||
layers:
|
||||
- state: bookknock
|
||||
- type: Spellbook
|
||||
spells:
|
||||
ActionKnock: -1
|
||||
|
||||
- type: entity
|
||||
id: FireballSpellbook
|
||||
name: fireball spellbook
|
||||
parent: BaseSpellbook
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Magic/spellbooks.rsi
|
||||
layers:
|
||||
- state: bookfireball
|
||||
- type: Spellbook
|
||||
spells:
|
||||
ActionFireball: -1
|
||||
|
||||
- type: entity
|
||||
id: ScrollRunes
|
||||
name: scroll of runes
|
||||
parent: BaseSpellbook
|
||||
components:
|
||||
- type: Item
|
||||
size: Normal
|
||||
- type: Sprite
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
layers:
|
||||
- state: spell_default
|
||||
- type: Spellbook
|
||||
spells:
|
||||
ActionFlashRune: -1
|
||||
ActionExplosionRune: -1
|
||||
ActionIgniteRune: -1
|
||||
ActionStunRune: -1
|
||||
179
Resources/Prototypes/Entities/Objects/Tools/energydome.yml
Normal file
@@ -0,0 +1,179 @@
|
||||
- type: entity
|
||||
name: blood red personal shield generator
|
||||
description: A personal shield generator that protects the wearer from lasers and bullets but prevents from using ranged weapons himself. Uses a power cell.
|
||||
id: EnergyDomeGeneratorPersonalSyndie
|
||||
parent: BaseItem
|
||||
components:
|
||||
- type: Item
|
||||
size: Ginormous
|
||||
- type: Clothing
|
||||
quickEquip: false
|
||||
slots:
|
||||
- Belt
|
||||
- type: Sprite
|
||||
sprite: Objects/Tools/EnergyDome/syndie.rsi
|
||||
layers:
|
||||
- state: icon
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
cell_slot: !type:ContainerSlot
|
||||
- type: PowerCellSlot
|
||||
cellSlotId: cell_slot
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
cell_slot:
|
||||
name: power-cell-slot-component-slot-name-default
|
||||
startingItem: PowerCellSmall
|
||||
whitelist:
|
||||
tags:
|
||||
- PowerCell
|
||||
- PowerCellSmall
|
||||
- type: EnergyDomeGenerator
|
||||
damageEnergyDraw: 5
|
||||
domePrototype: EnergyDomeSmallRed
|
||||
- type: PowerCellDraw
|
||||
drawRate: 10
|
||||
useRate: 0
|
||||
- type: UseDelay
|
||||
delay: 10.0
|
||||
|
||||
- type: entity
|
||||
name: BR-40c "Turtle"
|
||||
description: A two-handed and heavy energy barrier with extremely low passive energy consumption. Can be tethered with a multitool.
|
||||
id: EnergyDomeDirectionalTurtle
|
||||
parent: BaseItem
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Tools/EnergyDome/reinhardt.rsi
|
||||
layers:
|
||||
- state: icon
|
||||
- type: InteractionOutline
|
||||
- type: MultiHandedItem
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeAabb
|
||||
bounds: "-0.25,-0.25,0.25,0.25"
|
||||
density: 20
|
||||
mask:
|
||||
- ItemMask
|
||||
restitution: 0.3
|
||||
friction: 0.2
|
||||
- type: Item
|
||||
size: Ginormous
|
||||
- type: HeldSpeedModifier
|
||||
walkModifier: 0.7
|
||||
sprintModifier: 0.7
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
cell_slot: !type:ContainerSlot
|
||||
- type: PowerCellSlot
|
||||
cellSlotId: cell_slot
|
||||
- type: ItemSlots
|
||||
slots:
|
||||
cell_slot:
|
||||
name: power-cell-slot-component-slot-name-default
|
||||
startingItem: PowerCellSmall
|
||||
whitelist:
|
||||
tags:
|
||||
- PowerCell
|
||||
- PowerCellSmall
|
||||
- type: EnergyDomeGenerator
|
||||
damageEnergyDraw: 7
|
||||
domePrototype: EnergyDomeMediumBlue
|
||||
canDeviceNetworkUse: true
|
||||
- type: PowerCellDraw
|
||||
drawRate: 2
|
||||
useRate: 0
|
||||
- type: UseDelay
|
||||
delay: 10.0
|
||||
- type: DeviceNetwork
|
||||
deviceNetId: Wireless
|
||||
receiveFrequencyId: BasicDevice
|
||||
- type: WirelessNetworkConnection
|
||||
range: 200
|
||||
- type: DeviceLinkSink
|
||||
ports:
|
||||
- Toggle
|
||||
- On
|
||||
- Off
|
||||
|
||||
- type: entity
|
||||
id: EnergyDomeWiredTest
|
||||
name: Static Dome
|
||||
description: Test energy barrier powered by station wiring. I don't know how the hell to balance it.....
|
||||
parent: BaseMachine
|
||||
suffix: DO NOT MERGE
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: Transform
|
||||
anchored: true
|
||||
- type: Physics
|
||||
bodyType: Static
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeAabb
|
||||
bounds: "-0.45,-0.45,0.45,0.45"
|
||||
density: 190
|
||||
mask:
|
||||
- MachineMask
|
||||
layer:
|
||||
- MachineLayer
|
||||
- type: Sprite
|
||||
sprite: Structures/Power/Generation/Tesla/coil.rsi
|
||||
snapCardinals: true
|
||||
noRot: true
|
||||
layers:
|
||||
- state: coil
|
||||
- type: ExaminableBattery
|
||||
- type: Battery
|
||||
maxCharge: 30000 #<- max supply
|
||||
startingCharge: 10000
|
||||
- type: PowerNetworkBattery
|
||||
maxSupply: 30000
|
||||
maxChargeRate: 1000 #<- passive charging frow power net
|
||||
supplyRampTolerance: 500
|
||||
supplyRampRate: 50
|
||||
- type: BatteryCharger
|
||||
voltage: Medium
|
||||
- type: NodeContainer
|
||||
examinable: true
|
||||
nodes:
|
||||
input:
|
||||
!type:CableDeviceNode
|
||||
nodeGroupID: MVPower
|
||||
- type: BatterySelfRecharger
|
||||
autoRecharge: false # true only when active
|
||||
autoRechargeRate: -800 #<- discharge per second while active
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
damageModifierSet: Metallic
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 200
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: UseDelay
|
||||
delay: 30.0
|
||||
- type: DeviceNetwork
|
||||
deviceNetId: Wireless
|
||||
receiveFrequencyId: BasicDevice
|
||||
- type: WirelessNetworkConnection
|
||||
range: 200
|
||||
- type: DeviceLinkSink
|
||||
ports:
|
||||
- Toggle
|
||||
- On
|
||||
- Off
|
||||
- type: EnergyDomeGenerator
|
||||
enabled: true
|
||||
damageEnergyDraw: 100
|
||||
domePrototype: EnergyDomeSlowing
|
||||
canDeviceNetworkUse: true
|
||||
@@ -204,3 +204,34 @@
|
||||
radius: 2.0
|
||||
energy: 7.0
|
||||
- type: BloodBoilProjectile
|
||||
|
||||
- type: entity
|
||||
id: ProjectileTeslaBall
|
||||
name: teslaball
|
||||
description: You better GITTAH WEIGH.
|
||||
parent: BulletRocket
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: PointLight
|
||||
color: "#B3CEFF"
|
||||
radius: 2.0
|
||||
energy: 5.0
|
||||
- type: Projectile
|
||||
damage:
|
||||
types:
|
||||
Caustic: 10
|
||||
- type: Sprite
|
||||
sprite: Structures/Power/Generation/Tesla/energy_miniball.rsi
|
||||
layers:
|
||||
- state: tesla_projectile
|
||||
shader: unshaded
|
||||
- type: Explosive
|
||||
explosionType: Default
|
||||
maxIntensity: 100
|
||||
intensitySlope: 0.1
|
||||
totalIntensity: 0.3
|
||||
maxTileBreak: 0
|
||||
- type: StunOnCollide
|
||||
stunAmount: 2
|
||||
knockdownAmount: 2
|
||||
- type: TeslaProjectile
|
||||
|
||||
@@ -50,3 +50,37 @@
|
||||
# also limits the crew's use
|
||||
- type: TimedDespawn
|
||||
lifetime: 30
|
||||
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
id: ThrowingCard
|
||||
name: card
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Magic/card.rsi
|
||||
layers:
|
||||
- state: icon
|
||||
- type: Appearance
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape: !type:PhysShapeCircle
|
||||
radius: 0.2
|
||||
density: 5
|
||||
mask:
|
||||
- ItemMask
|
||||
restitution: 0.3
|
||||
friction: 0.2
|
||||
- type: EmbeddableProjectile
|
||||
sound: /Audio/Weapons/star_hit.ogg
|
||||
- type: DamageOtherOnHit
|
||||
damage:
|
||||
types:
|
||||
Slash: 8
|
||||
Piercing: 10
|
||||
- type: StaminaDamageOnCollide
|
||||
damage: 45
|
||||
- type: StaminaDamageOnEmbed
|
||||
damage: 10
|
||||
- type: TimedDespawn
|
||||
lifetime: 60
|
||||
|
||||
253
Resources/Prototypes/Magic/white.yml
Normal file
@@ -0,0 +1,253 @@
|
||||
- type: entity
|
||||
id: ActionElectricArcSpell
|
||||
name: Electric arc
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: WorldTargetAction
|
||||
useDelay: 60
|
||||
itemIconStyle: BigAction
|
||||
checkCanAccess: false
|
||||
range: 10
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/White/Magic/Arc/cast.ogg
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: thunder
|
||||
isChargeEnabled: true
|
||||
chargingSound:
|
||||
path: /Audio/White/Magic/Arc/charge.ogg
|
||||
chargeProto: MagicFollowerArcEntity
|
||||
maxChargedSound:
|
||||
path: /Audio/White/Magic/Arc/max.ogg
|
||||
isAltEnabled: true
|
||||
event: !type:ArcSpellEvent
|
||||
speech: "KUH, ABAH'RAH"
|
||||
prototype: ProjectileTeslaBall
|
||||
posData: !type:TargetCasterPos
|
||||
|
||||
- type: entity
|
||||
id: ActionForceSpell
|
||||
name: Force
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: WorldTargetAction
|
||||
useDelay: 60
|
||||
itemIconStyle: BigAction
|
||||
checkCanAccess: false
|
||||
range: 10
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/White/Magic/Force/cast.ogg
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: push
|
||||
isChargeEnabled: true
|
||||
chargingSound:
|
||||
path: /Audio/White/Magic/Force/charge.ogg
|
||||
chargeProto: MagicFollowerForceEntity
|
||||
maxChargedSound:
|
||||
path: /Audio/White/Magic/Force/max.ogg
|
||||
isAltEnabled: true
|
||||
event: !type:ForceSpellEvent
|
||||
speech: "EL DRITCH!"
|
||||
|
||||
- type: entity
|
||||
id: ActionFireballSpell
|
||||
name: Fireball
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: WorldTargetAction
|
||||
useDelay: 60
|
||||
itemIconStyle: BigAction
|
||||
checkCanAccess: false
|
||||
range: 45
|
||||
isChargeEnabled: true
|
||||
chargeProto: MagicFollowerFireEntity
|
||||
isAltEnabled: true
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/Magic/fireball.ogg
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: fireball
|
||||
event: !type:FireballSpellEvent
|
||||
prototype: ProjectileFireball
|
||||
posData: !type:TargetCasterPos
|
||||
speech: action-speech-spell-fireball
|
||||
|
||||
- type: entity
|
||||
id: ActionCardSpell
|
||||
name: Cards
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: WorldTargetAction
|
||||
useDelay: 60
|
||||
itemIconStyle: BigAction
|
||||
checkCanAccess: false
|
||||
range: 45
|
||||
isChargeEnabled: true
|
||||
chargingSound:
|
||||
path: /Audio/White/Magic/Cards/charge.ogg
|
||||
chargeProto: MagicFollowerCardEntity
|
||||
maxChargedSound:
|
||||
path: /Audio/White/Magic/Cards/max.ogg
|
||||
isAltEnabled: true
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/White/Magic/Cards/cast.ogg
|
||||
icon:
|
||||
sprite: Objects/Magic/card.rsi
|
||||
state: icon
|
||||
event: !type:CardsSpellEvent
|
||||
prototype: ThrowingCard
|
||||
posData: !type:TargetCasterPos
|
||||
speech: "SHIZO NERO!"
|
||||
|
||||
- type: entity
|
||||
id: ActionForcewallSpell
|
||||
name: Forcewall
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: WorldTargetAction
|
||||
useDelay: 60
|
||||
itemIconStyle: BigAction
|
||||
checkCanAccess: false
|
||||
range: 10
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/White/Magic/Force/cast.ogg
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: shield
|
||||
isChargeEnabled: true
|
||||
chargingSound:
|
||||
path: /Audio/White/Magic/Force/charge.ogg
|
||||
chargeProto: MagicFollowerForceEntity
|
||||
maxChargedSound:
|
||||
path: /Audio/White/Magic/Force/max.ogg
|
||||
isAltEnabled: true
|
||||
event: !type:ForceWallSpellEvent
|
||||
speech: "TARCOL MINTI ZHERI!"
|
||||
prototype: WallForce
|
||||
|
||||
- type: entity
|
||||
id: ActionBlinkSpell
|
||||
name: Blink
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: InstantAction
|
||||
useDelay: 60
|
||||
itemIconStyle: BigAction
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: blink
|
||||
event: !type:BlinkSpellEvent
|
||||
speech: "SYCAR TYN!"
|
||||
|
||||
- type: entity
|
||||
id: ActionEtherealJauntSpell
|
||||
name: Ethereal Jaunt
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: InstantAction
|
||||
useDelay: 60
|
||||
itemIconStyle: BigAction
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: jaunt
|
||||
event: !type:EtherealJauntSpellEvent
|
||||
speech: "SCYAR NILA!"
|
||||
|
||||
- type: entity
|
||||
id: ActionEmpSpell
|
||||
name: Emp
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: InstantAction
|
||||
useDelay: 60
|
||||
itemIconStyle: BigAction
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: emp_new
|
||||
event: !type:EmpSpellEvent
|
||||
speech: "OCYAR TRINA!"
|
||||
|
||||
- type: entity
|
||||
id: ActionCluwneCurseSpell
|
||||
name: Cluwne Curse
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: EntityTargetAction
|
||||
canTargetSelf: false
|
||||
range: 2
|
||||
useDelay: 300
|
||||
itemIconStyle: BigAction
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: cluwne
|
||||
event: !type:CluwneCurseSpellEvent
|
||||
speech: "CLUWNE FOR ME!"
|
||||
|
||||
- type: entity
|
||||
id: ActionBananaTouchSpell
|
||||
name: Banana Touch
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: EntityTargetAction
|
||||
canTargetSelf: false
|
||||
range: 2
|
||||
useDelay: 300
|
||||
itemIconStyle: BigAction
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: clown
|
||||
event: !type:BananaTouchSpellEvent
|
||||
speech: "HONK FOR ME!"
|
||||
|
||||
- type: entity
|
||||
id: ActionMimeTouchSpell
|
||||
name: Mime Touch
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: EntityTargetAction
|
||||
canTargetSelf: false
|
||||
range: 2
|
||||
useDelay: 300
|
||||
itemIconStyle: BigAction
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: mime_curse
|
||||
event: !type:MimeTouchSpellEvent
|
||||
speech: "SILENCE!"
|
||||
|
||||
- type: entity
|
||||
id: ActionInstantRecallSpell
|
||||
name: Instant Recall
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: InstantRecall
|
||||
- type: Magic
|
||||
requiresClothes: true
|
||||
- type: InstantAction
|
||||
useDelay: 10
|
||||
itemIconStyle: BigAction
|
||||
icon:
|
||||
sprite: Objects/Magic/magicactions.rsi
|
||||
state: summons
|
||||
event: !type:InstantRecallSpellEvent
|
||||
35
Resources/Prototypes/White/Entities/Clothing/nigger.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
- type: entity
|
||||
parent: ClothingHeadHatWizard
|
||||
id: ClothingHeadHatRealWizardBlue
|
||||
components:
|
||||
- type: WizardClothes
|
||||
|
||||
- type: entity
|
||||
parent: ClothingOuterWizard
|
||||
id: ClothingOuterRealWizardBlue
|
||||
components:
|
||||
- type: WizardClothes
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadHatRedwizard
|
||||
id: ClothingHeadHatRealWizardRed
|
||||
components:
|
||||
- type: WizardClothes
|
||||
|
||||
- type: entity
|
||||
parent: ClothingOuterWizardRed
|
||||
id: ClothingOuterRealWizardRed
|
||||
components:
|
||||
- type: WizardClothes
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadHatVioletwizard
|
||||
id: ClothingHeadHatRealWizardViolet
|
||||
components:
|
||||
- type: WizardClothes
|
||||
|
||||
- type: entity
|
||||
parent: ClothingOuterWizardViolet
|
||||
id: ClothingOuterRealWizardViolet
|
||||
components:
|
||||
- type: WizardClothes
|
||||
119
Resources/Prototypes/White/Objects/Scrolls/magic.yml
Normal file
@@ -0,0 +1,119 @@
|
||||
- type: entity
|
||||
id: MagicFollowerEntity
|
||||
name: magic
|
||||
components:
|
||||
- type: Physics
|
||||
bodyType: Dynamic
|
||||
fixedRotation: false
|
||||
- type: Sprite
|
||||
sprite: Structures/Specific/Anomalies/Cores/bluespace_core.rsi
|
||||
noRot: true
|
||||
layers:
|
||||
- state: core
|
||||
- state: pulse
|
||||
map: ["decay"]
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.AnomalyCoreVisuals.Decaying:
|
||||
decay:
|
||||
True: { visible: true }
|
||||
False: { visible: false }
|
||||
- type: PointLight
|
||||
radius: 1.5
|
||||
energy: 3.5
|
||||
color: "#00ccff"
|
||||
castShadows: false
|
||||
|
||||
- type: entity
|
||||
id: MagicFollowerArcEntity
|
||||
name: magic
|
||||
components:
|
||||
- type: Physics
|
||||
bodyType: Dynamic
|
||||
fixedRotation: false
|
||||
- type: Sprite
|
||||
sprite: Structures/Specific/Anomalies/Cores/electric_core.rsi
|
||||
noRot: true
|
||||
layers:
|
||||
- state: core
|
||||
- state: pulse
|
||||
map: ["decay"]
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.AnomalyCoreVisuals.Decaying:
|
||||
decay:
|
||||
True: { visible: true }
|
||||
False: { visible: false }
|
||||
- type: PointLight
|
||||
radius: 1.5
|
||||
energy: 3.5
|
||||
color: "#ccf404"
|
||||
castShadows: false
|
||||
|
||||
- type: entity
|
||||
id: MagicFollowerForceEntity
|
||||
name: magic
|
||||
components:
|
||||
- type: Physics
|
||||
bodyType: Dynamic
|
||||
fixedRotation: false
|
||||
- type: Sprite
|
||||
sprite: Structures/Specific/Anomalies/Cores/bluespace_core.rsi
|
||||
noRot: true
|
||||
layers:
|
||||
- state: core
|
||||
- state: pulse
|
||||
map: ["decay"]
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.AnomalyCoreVisuals.Decaying:
|
||||
decay:
|
||||
True: { visible: true }
|
||||
False: { visible: false }
|
||||
- type: PointLight
|
||||
radius: 1.5
|
||||
energy: 3.5
|
||||
color: "#00ccff"
|
||||
castShadows: false
|
||||
|
||||
- type: entity
|
||||
id: MagicFollowerFireEntity
|
||||
name: magic
|
||||
components:
|
||||
- type: Physics
|
||||
bodyType: Dynamic
|
||||
fixedRotation: false
|
||||
- type: Sprite
|
||||
sprite: Structures/Specific/Anomalies/Cores/pyro_core.rsi
|
||||
noRot: true
|
||||
layers:
|
||||
- state: core
|
||||
- state: pulse
|
||||
map: ["decay"]
|
||||
- type: Appearance
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.AnomalyCoreVisuals.Decaying:
|
||||
decay:
|
||||
True: { visible: true }
|
||||
False: { visible: false }
|
||||
- type: PointLight
|
||||
radius: 1.5
|
||||
energy: 3.5
|
||||
color: "#ce5a25"
|
||||
castShadows: false
|
||||
|
||||
- type: entity
|
||||
id: MagicFollowerCardEntity
|
||||
name: magic
|
||||
components:
|
||||
- type: Physics
|
||||
bodyType: Dynamic
|
||||
fixedRotation: false
|
||||
- type: Sprite
|
||||
sprite: Objects/Magic/card.rsi
|
||||
layers:
|
||||
- state: icon
|
||||
133
Resources/Prototypes/White/Objects/Scrolls/scrolls.yml
Normal file
@@ -0,0 +1,133 @@
|
||||
- type: entity
|
||||
id: BaseScroll
|
||||
parent: BaseItem
|
||||
name: "Magic Scroll"
|
||||
description: "A relic of arcane lore, this ancient parchment bears witness to countless mystical incantations and forgotten spells."
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: White/Misc/scrolls.rsi
|
||||
layers:
|
||||
- state: scroll
|
||||
- type: Scroll
|
||||
useSound:
|
||||
path: /Audio/White/Items/scroll/use.ogg
|
||||
afterUseSound:
|
||||
path: /Audio/White/Items/scroll/after_use.ogg
|
||||
|
||||
- type: entity
|
||||
id: ScrollFireball
|
||||
parent: BaseScroll
|
||||
name: "Fireball scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionFireballSpell
|
||||
learnPopup: fireball
|
||||
|
||||
- type: entity
|
||||
id: ScrollForcewall
|
||||
parent: BaseScroll
|
||||
name: "Forcewall scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionForcewallSpell
|
||||
learnPopup: forcewall
|
||||
|
||||
- type: entity
|
||||
id: ScrollKnock
|
||||
parent: BaseScroll
|
||||
name: "Knock scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionKnock
|
||||
learnPopup: knock-knock
|
||||
|
||||
- type: entity
|
||||
id: ScrollArc
|
||||
parent: BaseScroll
|
||||
name: "Electric Arc scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionElectricArcSpell
|
||||
learnPopup: lightning
|
||||
|
||||
- type: entity
|
||||
id: ScrollForce
|
||||
parent: BaseScroll
|
||||
name: "Force scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionForceSpell
|
||||
learnPopup: force
|
||||
|
||||
- type: entity
|
||||
id: ScrollCards
|
||||
parent: BaseScroll
|
||||
name: "Cards scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionCardSpell
|
||||
learnPopup: cards
|
||||
|
||||
- type: entity
|
||||
id: ScrollBlink
|
||||
parent: BaseScroll
|
||||
name: "Blink scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionBlinkSpell
|
||||
learnPopup: blink
|
||||
|
||||
- type: entity
|
||||
id: ScrollEtherealJaunt
|
||||
parent: BaseScroll
|
||||
name: "Ethereal Jaunt scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionEtherealJauntSpell
|
||||
learnPopup: jaunt
|
||||
|
||||
- type: entity
|
||||
id: ScrollEmp
|
||||
parent: BaseScroll
|
||||
name: "Emp scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionEmpSpell
|
||||
learnPopup: emp
|
||||
|
||||
- type: entity
|
||||
id: ScrollCluwneCurse
|
||||
parent: BaseScroll
|
||||
name: "Cluwne curse scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionCluwneCurseSpell
|
||||
learnPopup: curse
|
||||
|
||||
- type: entity
|
||||
id: ScrollBananaTouch
|
||||
parent: BaseScroll
|
||||
name: "Banana touch scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionBananaTouchSpell
|
||||
learnPopup: banana
|
||||
|
||||
- type: entity
|
||||
id: ScrollMimeTouch
|
||||
parent: BaseScroll
|
||||
name: "Mime touch scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionMimeTouchSpell
|
||||
learnPopup: silence
|
||||
|
||||
- type: entity
|
||||
id: ScrollInstantRecall
|
||||
parent: BaseScroll
|
||||
name: "Instant recall scroll"
|
||||
components:
|
||||
- type: Scroll
|
||||
actionId: ActionInstantRecallSpell
|
||||
learnPopup: recall
|
||||
BIN
Resources/Textures/Effects/EnergyDome/energydome_big.rsi/big.png
Normal file
|
After Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Created by TheShuEd (github) for Space Station 14",
|
||||
"size": {
|
||||
"x": 192,
|
||||
"y": 192
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "big",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Created by TheShuEd (github) for Space Station 14",
|
||||
"size": {
|
||||
"x": 96,
|
||||
"y": 64
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "small",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 25 KiB |
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Created by TheShuEd (github) for Space Station 14",
|
||||
"size": {
|
||||
"x": 128,
|
||||
"y": 128
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "medium",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 44 KiB |
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Created by TheShuEd (github) for Space Station 14",
|
||||
"size": {
|
||||
"x": 192,
|
||||
"y": 192
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "big",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Created by TheShuEd (github) for Space Station 14",
|
||||
"size": {
|
||||
"x": 64,
|
||||
"y": 64
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "small",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 9.7 KiB |
BIN
Resources/Textures/Objects/Magic/card.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 262 B |
14
Resources/Textures/Objects/Magic/card.rsi/meta.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": null,
|
||||
"copyright": null,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Objects/Magic/magicactions.rsi/clown.png
Normal file
|
After Width: | Height: | Size: 526 B |
BIN
Resources/Textures/Objects/Magic/magicactions.rsi/cluwne.png
Normal file
|
After Width: | Height: | Size: 547 B |
BIN
Resources/Textures/Objects/Magic/magicactions.rsi/emp_new.png
Normal file
|
After Width: | Height: | Size: 371 B |
BIN
Resources/Textures/Objects/Magic/magicactions.rsi/jaunt.png
Normal file
|
After Width: | Height: | Size: 736 B |
@@ -27,6 +27,30 @@
|
||||
},
|
||||
{
|
||||
"name": "gib"
|
||||
},
|
||||
{
|
||||
"name": "push"
|
||||
},
|
||||
{
|
||||
"name": "thunder"
|
||||
},
|
||||
{
|
||||
"name": "clown"
|
||||
},
|
||||
{
|
||||
"name": "cluwne"
|
||||
},
|
||||
{
|
||||
"name": "emp_new"
|
||||
},
|
||||
{
|
||||
"name": "jaunt"
|
||||
},
|
||||
{
|
||||
"name": "mime_curse"
|
||||
},
|
||||
{
|
||||
"name": "summons"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Objects/Magic/magicactions.rsi/mime_curse.png
Normal file
|
After Width: | Height: | Size: 479 B |
BIN
Resources/Textures/Objects/Magic/magicactions.rsi/push.png
Normal file
|
After Width: | Height: | Size: 719 B |
BIN
Resources/Textures/Objects/Magic/magicactions.rsi/summons.png
Normal file
|
After Width: | Height: | Size: 746 B |
BIN
Resources/Textures/Objects/Magic/magicactions.rsi/thunder.png
Normal file
|
After Width: | Height: | Size: 962 B |
|
After Width: | Height: | Size: 816 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Created by TheShuEd (github) for Space Station 14",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 812 B |
BIN
Resources/Textures/Objects/Tools/EnergyDome/syndie.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 535 B |
|
After Width: | Height: | Size: 702 B |
|
After Width: | Height: | Size: 718 B |
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Created by TheShuEd (github) for Space Station 14",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "equipped-BELT",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/White/Charge/charge.rsi/flux.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
30
Resources/Textures/White/Charge/charge.rsi/meta.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": null,
|
||||
"copyright": null,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "flux",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
1.5,
|
||||
0.1,
|
||||
0.1,
|
||||
1.5,
|
||||
0.1,
|
||||
0.1,
|
||||
1.5,
|
||||
0.1,
|
||||
0.1,
|
||||
1.5
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
14
Resources/Textures/White/Misc/scrolls.rsi/meta.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": null,
|
||||
"copyright": null,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "scroll"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/White/Misc/scrolls.rsi/scroll.png
Normal file
|
After Width: | Height: | Size: 673 B |