Proto-kinetic crusher (#16277)
Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com>
@@ -1,48 +1,49 @@
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Markers
|
||||
namespace Content.Client.Markers;
|
||||
|
||||
public sealed class MarkerSystem : EntitySystem
|
||||
{
|
||||
public sealed class MarkerSystem : EntitySystem
|
||||
private bool _markersVisible;
|
||||
|
||||
public bool MarkersVisible
|
||||
{
|
||||
private bool _markersVisible;
|
||||
|
||||
public bool MarkersVisible
|
||||
get => _markersVisible;
|
||||
set
|
||||
{
|
||||
get => _markersVisible;
|
||||
set
|
||||
{
|
||||
_markersVisible = value;
|
||||
UpdateMarkers();
|
||||
}
|
||||
_markersVisible = value;
|
||||
UpdateMarkers();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MarkerComponent, ComponentStartup>(OnStartup);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, MarkerComponent marker, ComponentStartup args)
|
||||
{
|
||||
UpdateVisibility(uid);
|
||||
}
|
||||
|
||||
private void UpdateVisibility(EntityUid uid)
|
||||
{
|
||||
if (EntityManager.TryGetComponent(uid, out SpriteComponent? sprite))
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MarkerComponent, ComponentStartup>(OnStartup);
|
||||
sprite.Visible = MarkersVisible;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, MarkerComponent marker, ComponentStartup args)
|
||||
{
|
||||
UpdateVisibility(marker);
|
||||
}
|
||||
private void UpdateMarkers()
|
||||
{
|
||||
var query = AllEntityQuery<MarkerComponent>();
|
||||
|
||||
private void UpdateVisibility(MarkerComponent marker)
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (EntityManager.TryGetComponent(marker.Owner, out SpriteComponent? sprite))
|
||||
{
|
||||
sprite.Visible = MarkersVisible;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMarkers()
|
||||
{
|
||||
foreach (var markerComponent in EntityManager.EntityQuery<MarkerComponent>(true))
|
||||
{
|
||||
UpdateVisibility(markerComponent);
|
||||
}
|
||||
UpdateVisibility(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ProjectileComponent, ComponentHandleState>(OnHandleState);
|
||||
SubscribeNetworkEvent<ImpactEffectEvent>(OnProjectileImpact);
|
||||
}
|
||||
|
||||
@@ -54,11 +53,4 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
||||
_player.Play(ent, anim, "impact-effect");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, ProjectileComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not ProjectileComponentState state) return;
|
||||
component.Shooter = state.Shooter;
|
||||
component.IgnoreShooter = state.IgnoreShooter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Content.Client.Toggleable;
|
||||
public sealed class ToggleableLightVisualsComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Sprite layer that will have it's visibility toggled when this item is toggled.
|
||||
/// Sprite layer that will have its visibility toggled when this item is toggled.
|
||||
/// </summary>
|
||||
[DataField("spriteLayer")]
|
||||
public string SpriteLayer = "light";
|
||||
|
||||
39
Content.Client/Weapons/Marker/DamageMarkerSystem.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using Content.Shared.Weapons.Marker;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Weapons.Marker;
|
||||
|
||||
public sealed class DamageMarkerSystem : SharedDamageMarkerSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<DamageMarkerComponent, ComponentStartup>(OnMarkerStartup);
|
||||
SubscribeLocalEvent<DamageMarkerComponent, ComponentShutdown>(OnMarkerShutdown);
|
||||
}
|
||||
|
||||
private void OnMarkerStartup(EntityUid uid, DamageMarkerComponent component, ComponentStartup args)
|
||||
{
|
||||
if (!_timing.ApplyingState || component.Effect == null || !TryComp<SpriteComponent>(uid, out var sprite))
|
||||
return;
|
||||
|
||||
var layer = sprite.LayerMapReserveBlank(DamageMarkerKey.Key);
|
||||
sprite.LayerSetState(layer, component.Effect.RsiState, component.Effect.RsiPath);
|
||||
}
|
||||
|
||||
private void OnMarkerShutdown(EntityUid uid, DamageMarkerComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (!_timing.ApplyingState || !TryComp<SpriteComponent>(uid, out var sprite) || !sprite.LayerMapTryGet(DamageMarkerKey.Key, out var weh))
|
||||
return;
|
||||
|
||||
sprite.RemoveLayer(weh);
|
||||
}
|
||||
|
||||
private enum DamageMarkerKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
@@ -85,6 +86,16 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
// Heavy attack.
|
||||
if (altDown == BoundKeyState.Down)
|
||||
{
|
||||
// TODO: Need to make alt-fire melee its own component I guess?
|
||||
// Melee and guns share a lot in the middle but share virtually nothing at the start and end so
|
||||
// it's kinda tricky.
|
||||
// I think as long as we make secondaries their own component it's probably fine
|
||||
// as long as guncomp has an alt-use key then it shouldn't be too much of a PITA to deal with.
|
||||
if (HasComp<GunComponent>(weaponUid))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We did the click to end the attack but haven't pulled the key up.
|
||||
if (weapon.Attacking)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Client.Items;
|
||||
using Content.Client.Weapons.Ranged.Components;
|
||||
using Content.Shared.Camera;
|
||||
using Content.Shared.Input;
|
||||
using Content.Shared.Spawners.Components;
|
||||
using Content.Shared.Weapons.Ranged;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
@@ -137,7 +138,9 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (_inputSystem.CmdStates.GetState(EngineKeyFunctions.Use) != BoundKeyState.Down)
|
||||
var useKey = gun.UseKey ? EngineKeyFunctions.Use : EngineKeyFunctions.UseSecondary;
|
||||
|
||||
if (_inputSystem.CmdStates.GetState(useKey) != BoundKeyState.Down)
|
||||
{
|
||||
if (gun.ShotCounter != 0)
|
||||
EntityManager.RaisePredictiveEvent(new RequestStopShootEvent { Gun = gunUid });
|
||||
@@ -296,6 +299,9 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
_animPlayer.Play(ent, anim, "muzzle-flash");
|
||||
var light = EnsureComp<PointLightComponent>(uid);
|
||||
|
||||
if (light.Enabled)
|
||||
return;
|
||||
|
||||
light.NetSyncEnabled = false;
|
||||
light.Enabled = true;
|
||||
light.Color = Color.FromHex("#cc8e2b");
|
||||
|
||||
@@ -86,6 +86,6 @@ public sealed class ProjectileAnomalySystem : EntitySystem
|
||||
|
||||
comp.Damage *= severity;
|
||||
|
||||
_gunSystem.ShootProjectile(ent, direction, Vector2.Zero, uid, component.MaxProjectileSpeed * severity);
|
||||
_gunSystem.ShootProjectile(ent, direction, Vector2.Zero, uid, uid, component.MaxProjectileSpeed * severity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,5 +6,9 @@ namespace Content.Server.Gatherable.Components;
|
||||
[RegisterComponent]
|
||||
public sealed class GatheringProjectileComponent : Component
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// How many more times we can gather.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("amount")]
|
||||
public int Amount = 1;
|
||||
}
|
||||
|
||||
@@ -17,12 +17,16 @@ public sealed partial class GatherableSystem
|
||||
{
|
||||
if (!args.OtherFixture.Hard ||
|
||||
args.OurFixture.ID != SharedProjectileSystem.ProjectileFixture ||
|
||||
component.Amount <= 0 ||
|
||||
!TryComp<GatherableComponent>(args.OtherEntity, out var gatherable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Gather(args.OtherEntity, uid, gatherable);
|
||||
QueueDel(uid);
|
||||
component.Amount--;
|
||||
|
||||
if (component.Amount <= 0)
|
||||
QueueDel(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using Content.Server.Actions;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Light;
|
||||
@@ -11,10 +8,7 @@ using Content.Shared.Toggleable;
|
||||
using Content.Shared.Verbs;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -37,7 +31,6 @@ namespace Content.Server.Light.EntitySystems
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<HandheldLightComponent, ComponentRemove>(OnRemove);
|
||||
SubscribeLocalEvent<HandheldLightComponent, ComponentGetState>(OnGetState);
|
||||
|
||||
SubscribeLocalEvent<HandheldLightComponent, ExaminedEvent>(OnExamine);
|
||||
@@ -45,41 +38,7 @@ namespace Content.Server.Light.EntitySystems
|
||||
|
||||
SubscribeLocalEvent<HandheldLightComponent, ActivateInWorldEvent>(OnActivate);
|
||||
|
||||
SubscribeLocalEvent<HandheldLightComponent, GetItemActionsEvent>(OnGetActions);
|
||||
SubscribeLocalEvent<HandheldLightComponent, ToggleActionEvent>(OnToggleAction);
|
||||
|
||||
SubscribeLocalEvent<HandheldLightComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
|
||||
SubscribeLocalEvent<HandheldLightComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
|
||||
}
|
||||
|
||||
private void OnEntInserted(
|
||||
EntityUid uid,
|
||||
HandheldLightComponent component,
|
||||
EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
// Not guaranteed to be the correct container for our slot, I don't care.
|
||||
UpdateLevel(uid, component);
|
||||
}
|
||||
|
||||
private void OnEntRemoved(
|
||||
EntityUid uid,
|
||||
HandheldLightComponent component,
|
||||
EntRemovedFromContainerMessage args)
|
||||
{
|
||||
// Ditto above
|
||||
UpdateLevel(uid, component);
|
||||
}
|
||||
|
||||
private void OnGetActions(EntityUid uid, HandheldLightComponent component, GetItemActionsEvent args)
|
||||
{
|
||||
if (component.ToggleAction == null
|
||||
&& _proto.TryIndex(component.ToggleActionId, out InstantActionPrototype? act))
|
||||
{
|
||||
component.ToggleAction = new(act);
|
||||
}
|
||||
|
||||
if (component.ToggleAction != null)
|
||||
args.Actions.Add(component.ToggleAction);
|
||||
}
|
||||
|
||||
private void OnToggleAction(EntityUid uid, HandheldLightComponent component, ToggleActionEvent args)
|
||||
@@ -114,11 +73,6 @@ namespace Content.Server.Light.EntitySystems
|
||||
return (byte?) ContentHelpers.RoundToNearestLevels(battery.CurrentCharge / battery.MaxCharge * 255, 255, HandheldLightComponent.StatusLevels);
|
||||
}
|
||||
|
||||
private void OnRemove(EntityUid uid, HandheldLightComponent component, ComponentRemove args)
|
||||
{
|
||||
_activeLights.Remove(component);
|
||||
}
|
||||
|
||||
private void OnActivate(EntityUid uid, HandheldLightComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -144,36 +98,6 @@ namespace Content.Server.Light.EntitySystems
|
||||
: Loc.GetString("handheld-light-component-on-examine-is-off-message"));
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
_activeLights.Clear();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var toRemove = new RemQueue<HandheldLightComponent>();
|
||||
|
||||
foreach (var handheld in _activeLights)
|
||||
{
|
||||
var uid = handheld.Owner;
|
||||
|
||||
if (handheld.Deleted)
|
||||
{
|
||||
toRemove.Add(handheld);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Paused(uid)) continue;
|
||||
TryUpdate(uid, handheld, frameTime);
|
||||
}
|
||||
|
||||
foreach (var light in toRemove)
|
||||
{
|
||||
_activeLights.Remove(light);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddToggleLightVerb(EntityUid uid, HandheldLightComponent component, GetVerbsEvent<ActivationVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Content.Shared.PowerCell;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
|
||||
91
Content.Server/PowerCell/PowerCellSystem.Draw.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.PowerCell;
|
||||
using Content.Shared.PowerCell.Components;
|
||||
|
||||
namespace Content.Server.PowerCell;
|
||||
|
||||
public sealed partial class PowerCellSystem
|
||||
{
|
||||
/*
|
||||
* Handles PowerCellDraw
|
||||
*/
|
||||
|
||||
private static readonly TimeSpan Delay = TimeSpan.FromSeconds(1);
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
var query = EntityQueryEnumerator<PowerCellDrawComponent, PowerCellSlotComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp, out var slot))
|
||||
{
|
||||
if (!comp.Drawing)
|
||||
continue;
|
||||
|
||||
if (_timing.CurTime < comp.NextUpdateTime)
|
||||
continue;
|
||||
|
||||
comp.NextUpdateTime += Delay;
|
||||
|
||||
if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, slot))
|
||||
continue;
|
||||
|
||||
if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate, battery))
|
||||
continue;
|
||||
|
||||
comp.Drawing = false;
|
||||
var ev = new PowerCellSlotEmptyEvent();
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, PowerCellDrawComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextUpdateTime += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnDrawChargeChanged(EntityUid uid, PowerCellDrawComponent component, ref ChargeChangedEvent args)
|
||||
{
|
||||
// Update the bools for client prediction.
|
||||
bool canDraw;
|
||||
bool canUse;
|
||||
|
||||
if (component.UseRate > 0f)
|
||||
{
|
||||
canUse = args.Charge > component.UseRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
canUse = true;
|
||||
}
|
||||
|
||||
if (component.DrawRate > 0f)
|
||||
{
|
||||
canDraw = args.Charge > 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
canDraw = true;
|
||||
}
|
||||
|
||||
if (canUse != component.CanUse || canDraw != component.CanDraw)
|
||||
{
|
||||
component.CanDraw = canDraw;
|
||||
component.CanUse = canUse;
|
||||
Dirty(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawCellChanged(EntityUid uid, PowerCellDrawComponent component, PowerCellChangedEvent args)
|
||||
{
|
||||
var canDraw = !args.Ejected && HasCharge(uid, float.MinValue);
|
||||
var canUse = !args.Ejected && HasActivatableCharge(uid, component);
|
||||
|
||||
if (canUse != component.CanUse || canDraw != component.CanDraw)
|
||||
{
|
||||
component.CanDraw = canDraw;
|
||||
component.CanUse = canUse;
|
||||
Dirty(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,10 @@ using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.PowerCell;
|
||||
|
||||
public sealed class PowerCellSystem : SharedPowerCellSystem
|
||||
/// <summary>
|
||||
/// Handles Power cells
|
||||
/// </summary>
|
||||
public sealed partial class PowerCellSystem : SharedPowerCellSystem
|
||||
{
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
@@ -39,43 +42,19 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
|
||||
SubscribeLocalEvent<PowerCellComponent, ChargeChangedEvent>(OnChargeChanged);
|
||||
SubscribeLocalEvent<PowerCellComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<PowerCellComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
|
||||
SubscribeLocalEvent<PowerCellComponent, ExaminedEvent>(OnCellExamined);
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, ExaminedEvent>(OnCellSlotExamined);
|
||||
|
||||
SubscribeLocalEvent<PowerCellDrawComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<PowerCellDrawComponent, ChargeChangedEvent>(OnDrawChargeChanged);
|
||||
SubscribeLocalEvent<PowerCellDrawComponent, PowerCellChangedEvent>(OnDrawCellChanged);
|
||||
|
||||
// funny
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, ExaminedEvent>(OnCellSlotExamined);
|
||||
SubscribeLocalEvent<PowerCellSlotComponent, BeingMicrowavedEvent>(OnSlotMicrowaved);
|
||||
|
||||
SubscribeLocalEvent<BatteryComponent, BeingMicrowavedEvent>(OnMicrowaved);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
var query = EntityQueryEnumerator<PowerCellDrawComponent, PowerCellSlotComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp, out var slot))
|
||||
{
|
||||
if (!comp.Enabled)
|
||||
continue;
|
||||
|
||||
if (_timing.CurTime < comp.NextUpdateTime)
|
||||
continue;
|
||||
comp.NextUpdateTime += TimeSpan.FromSeconds(1);
|
||||
|
||||
if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, slot))
|
||||
continue;
|
||||
|
||||
if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate, battery))
|
||||
continue;
|
||||
|
||||
comp.Enabled = false;
|
||||
var ev = new PowerCellSlotEmptyEvent();
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRejuvenate(EntityUid uid, PowerCellComponent component, RejuvenateEvent args)
|
||||
{
|
||||
component.IsRigged = false;
|
||||
@@ -83,13 +62,13 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
|
||||
|
||||
private void OnSlotMicrowaved(EntityUid uid, PowerCellSlotComponent component, BeingMicrowavedEvent args)
|
||||
{
|
||||
if (_itemSlotsSystem.TryGetSlot(uid, component.CellSlotId, out ItemSlot? slot))
|
||||
{
|
||||
if (slot.Item == null)
|
||||
return;
|
||||
if (!_itemSlotsSystem.TryGetSlot(uid, component.CellSlotId, out var slot))
|
||||
return;
|
||||
|
||||
RaiseLocalEvent(slot.Item.Value, args);
|
||||
}
|
||||
if (slot.Item == null)
|
||||
return;
|
||||
|
||||
RaiseLocalEvent(slot.Item.Value, args);
|
||||
}
|
||||
|
||||
private void OnMicrowaved(EntityUid uid, BatteryComponent component, BeingMicrowavedEvent args)
|
||||
@@ -111,17 +90,14 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance))
|
||||
return;
|
||||
|
||||
var frac = args.Charge / args.MaxCharge;
|
||||
var level = (byte) ContentHelpers.RoundToNearestLevels(frac, 1, PowerCellComponent.PowerCellVisualsLevels);
|
||||
_sharedAppearanceSystem.SetData(uid, PowerCellVisuals.ChargeLevel, level, appearance);
|
||||
_sharedAppearanceSystem.SetData(uid, PowerCellVisuals.ChargeLevel, level);
|
||||
|
||||
// If this power cell is inside a cell-slot, inform that entity that the power has changed (for updating visuals n such).
|
||||
if (_containerSystem.TryGetContainingContainer(uid, out var container)
|
||||
&& TryComp(container.Owner, out PowerCellSlotComponent? slot)
|
||||
&& _itemSlotsSystem.TryGetSlot(container.Owner, slot.CellSlotId, out ItemSlot? itemSlot))
|
||||
&& _itemSlotsSystem.TryGetSlot(container.Owner, slot.CellSlotId, out var itemSlot))
|
||||
{
|
||||
if (itemSlot.Item == uid)
|
||||
RaiseLocalEvent(container.Owner, new PowerCellChangedEvent(false));
|
||||
@@ -136,11 +112,6 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
|
||||
private void OnUnpaused(EntityUid uid, PowerCellDrawComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextUpdateTime += args.PausedTime;
|
||||
}
|
||||
|
||||
private void Explode(EntityUid uid, BatteryComponent? battery = null, EntityUid? cause = null)
|
||||
{
|
||||
if (!Resolve(uid, ref battery))
|
||||
@@ -190,10 +161,10 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
|
||||
|
||||
public void SetPowerCellDrawEnabled(EntityUid uid, bool enabled, PowerCellDrawComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
if (!Resolve(uid, ref component, false) || enabled == component.Drawing)
|
||||
return;
|
||||
|
||||
component.Enabled = enabled;
|
||||
component.Drawing = enabled;
|
||||
component.NextUpdateTime = _timing.CurTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,12 +26,6 @@ public sealed class ProjectileSystem : SharedProjectileSystem
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ProjectileComponent, StartCollideEvent>(OnStartCollide);
|
||||
SubscribeLocalEvent<ProjectileComponent, ComponentGetState>(OnGetState);
|
||||
}
|
||||
|
||||
private void OnGetState(EntityUid uid, ProjectileComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new ProjectileComponentState(component.Shooter, component.IgnoreShooter);
|
||||
}
|
||||
|
||||
private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref StartCollideEvent args)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared.PowerCell;
|
||||
using Content.Shared.UserInterface;
|
||||
|
||||
namespace Content.Server.UserInterface;
|
||||
|
||||
8
Content.Server/Weapons/DamageMarkerSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Weapons.Marker;
|
||||
|
||||
namespace Content.Server.Weapons;
|
||||
|
||||
public sealed class DamageMarkerSystem : SharedDamageMarkerSystem
|
||||
{
|
||||
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
// pneumatic cannon doesn't shoot bullets it just throws them, ignore ammo handling
|
||||
if (throwItems && ent != null)
|
||||
{
|
||||
ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, user);
|
||||
ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, gunUid, user);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -136,14 +136,14 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
for (var i = 0; i < cartridge.Count; i++)
|
||||
{
|
||||
var uid = Spawn(cartridge.Prototype, fromEnt);
|
||||
ShootOrThrow(uid, angles[i].ToVec(), gunVelocity, gun, user);
|
||||
ShootOrThrow(uid, angles[i].ToVec(), gunVelocity, gun, gunUid, user);
|
||||
shotProjectiles.Add(uid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var uid = Spawn(cartridge.Prototype, fromEnt);
|
||||
ShootOrThrow(uid, mapDirection, gunVelocity, gun, user);
|
||||
ShootOrThrow(uid, mapDirection, gunVelocity, gun, gunUid, user);
|
||||
shotProjectiles.Add(uid);
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
shotProjectiles.Add(ent!.Value);
|
||||
MuzzleFlash(gunUid, newAmmo, user);
|
||||
Audio.PlayPredicted(gun.SoundGunshot, gunUid, user);
|
||||
ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, user);
|
||||
ShootOrThrow(ent.Value, mapDirection, gunVelocity, gun, gunUid, user);
|
||||
break;
|
||||
case HitscanPrototype hitscan:
|
||||
|
||||
@@ -265,7 +265,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
});
|
||||
}
|
||||
|
||||
private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, GunComponent gun, EntityUid? user)
|
||||
private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVelocity, GunComponent gun, EntityUid gunUid, EntityUid? user)
|
||||
{
|
||||
// Do a throw
|
||||
if (!HasComp<ProjectileComponent>(uid))
|
||||
@@ -276,10 +276,10 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
return;
|
||||
}
|
||||
|
||||
ShootProjectile(uid, mapDirection, gunVelocity, user, gun.ProjectileSpeed);
|
||||
ShootProjectile(uid, mapDirection, gunVelocity, gunUid, user, gun.ProjectileSpeed);
|
||||
}
|
||||
|
||||
public void ShootProjectile(EntityUid uid, Vector2 direction, Vector2 gunVelocity, EntityUid? user = null, float speed = 20f)
|
||||
public void ShootProjectile(EntityUid uid, Vector2 direction, Vector2 gunVelocity, EntityUid gunUid, EntityUid? user = null, float speed = 20f)
|
||||
{
|
||||
var physics = EnsureComp<PhysicsComponent>(uid);
|
||||
Physics.SetBodyStatus(physics, BodyStatus.InAir);
|
||||
@@ -293,6 +293,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
{
|
||||
var projectile = EnsureComp<ProjectileComponent>(uid);
|
||||
Projectiles.SetShooter(projectile, user.Value);
|
||||
projectile.Weapon = gunUid;
|
||||
}
|
||||
|
||||
TransformSystem.SetWorldRotation(uid, direction.ToWorldAngle());
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
namespace Content.Shared.Interaction.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Raised on directed a weapon when being used in a melee attack.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public struct MeleeAttackAttemptEvent
|
||||
{
|
||||
public bool Cancelled = false;
|
||||
public readonly EntityUid User;
|
||||
|
||||
public MeleeAttackAttemptEvent(EntityUid user)
|
||||
{
|
||||
User = user;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,35 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.PowerCell;
|
||||
namespace Content.Shared.PowerCell;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the entity's ActivatableUI requires power or else it closes.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(PowerCellSystem))]
|
||||
public sealed class PowerCellDrawComponent : Component
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class PowerCellDrawComponent : Component
|
||||
{
|
||||
#region Prediction
|
||||
|
||||
/// <summary>
|
||||
/// Whether there is any charge available to draw.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("canDraw"), AutoNetworkedField]
|
||||
public bool CanDraw;
|
||||
|
||||
/// <summary>
|
||||
/// Whether there is sufficient charge to use.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("canUse"), AutoNetworkedField]
|
||||
public bool CanUse;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Is this power cell currently drawing power every tick.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("enabled")]
|
||||
public bool Enabled;
|
||||
public bool Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// How much the entity draws while the UI is open.
|
||||
@@ -21,7 +21,7 @@ public abstract class SharedPowerCellSystem : EntitySystem
|
||||
|
||||
private void OnRejuventate(EntityUid uid, PowerCellSlotComponent component, RejuvenateEvent args)
|
||||
{
|
||||
if (!_itemSlots.TryGetSlot(uid, component.CellSlotId, out ItemSlot? itemSlot) || !itemSlot.Item.HasValue)
|
||||
if (!_itemSlots.TryGetSlot(uid, component.CellSlotId, out var itemSlot) || !itemSlot.Item.HasValue)
|
||||
return;
|
||||
|
||||
// charge entity batteries and remove booby traps.
|
||||
|
||||
@@ -4,34 +4,42 @@ using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Projectiles
|
||||
namespace Content.Shared.Projectiles;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class ProjectileComponent : Component
|
||||
{
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class ProjectileComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("impactEffect", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string? ImpactEffect;
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("impactEffect", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string? ImpactEffect;
|
||||
|
||||
public EntityUid Shooter { get; set; }
|
||||
/// <summary>
|
||||
/// User that shot this projectile.
|
||||
/// </summary>
|
||||
[DataField("shooter"), AutoNetworkedField] public EntityUid Shooter;
|
||||
|
||||
public bool IgnoreShooter = true;
|
||||
/// <summary>
|
||||
/// Weapon used to shoot.
|
||||
/// </summary>
|
||||
[DataField("weapon"), AutoNetworkedField]
|
||||
public EntityUid Weapon;
|
||||
|
||||
[DataField("damage", required: true)]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageSpecifier Damage = default!;
|
||||
[DataField("ignoreShooter"), AutoNetworkedField]
|
||||
public bool IgnoreShooter = true;
|
||||
|
||||
[DataField("deleteOnCollide")]
|
||||
public bool DeleteOnCollide { get; } = true;
|
||||
[DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageSpecifier Damage = new();
|
||||
|
||||
[DataField("ignoreResistances")]
|
||||
public bool IgnoreResistances { get; } = false;
|
||||
[DataField("deleteOnCollide")]
|
||||
public bool DeleteOnCollide = true;
|
||||
|
||||
// Get that juicy FPS hit sound
|
||||
[DataField("soundHit")] public SoundSpecifier? SoundHit;
|
||||
[DataField("ignoreResistances")]
|
||||
public bool IgnoreResistances = false;
|
||||
|
||||
[DataField("soundForce")]
|
||||
public bool ForceSound = false;
|
||||
// Get that juicy FPS hit sound
|
||||
[DataField("soundHit")] public SoundSpecifier? SoundHit;
|
||||
|
||||
public bool DamagedEntity;
|
||||
}
|
||||
[DataField("soundForce")]
|
||||
public bool ForceSound = false;
|
||||
|
||||
public bool DamagedEntity;
|
||||
}
|
||||
|
||||
@@ -30,31 +30,18 @@ namespace Content.Shared.Projectiles
|
||||
component.Shooter = uid;
|
||||
Dirty(component);
|
||||
}
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public sealed class ProjectileComponentState : ComponentState
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ImpactEffectEvent : EntityEventArgs
|
||||
{
|
||||
public string Prototype;
|
||||
public EntityCoordinates Coordinates;
|
||||
|
||||
public ImpactEffectEvent(string prototype, EntityCoordinates coordinates)
|
||||
{
|
||||
public ProjectileComponentState(EntityUid shooter, bool ignoreShooter)
|
||||
{
|
||||
Shooter = shooter;
|
||||
IgnoreShooter = ignoreShooter;
|
||||
}
|
||||
|
||||
public EntityUid Shooter { get; }
|
||||
public bool IgnoreShooter { get; }
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ImpactEffectEvent : EntityEventArgs
|
||||
{
|
||||
public string Prototype;
|
||||
public EntityCoordinates Coordinates;
|
||||
|
||||
public ImpactEffectEvent(string prototype, EntityCoordinates coordinates)
|
||||
{
|
||||
Prototype = prototype;
|
||||
Coordinates = coordinates;
|
||||
}
|
||||
Prototype = prototype;
|
||||
Coordinates = coordinates;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
38
Content.Shared/Weapons/Marker/DamageMarkerComponent.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Weapons.Marker;
|
||||
|
||||
/// <summary>
|
||||
/// Marks an entity to take additional damage
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedDamageMarkerSystem))]
|
||||
public sealed partial class DamageMarkerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Sprite to apply to the entity while damagemarker is applied.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("effect")]
|
||||
public SpriteSpecifier.Rsi? Effect = new(new ResPath("/Textures/Objects/Weapons/Effects"), "shield2");
|
||||
|
||||
/// <summary>
|
||||
/// Sound to play when the damage marker is procced.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("sound")]
|
||||
public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/kinetic_accel.ogg");
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("damage")]
|
||||
public DamageSpecifier Damage = new();
|
||||
|
||||
/// <summary>
|
||||
/// Entity that marked this entity for a damage surplus.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("marker"), AutoNetworkedField]
|
||||
public EntityUid Marker;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("endTime", customTypeSerializer:typeof(TimeOffsetSerializer)), AutoNetworkedField]
|
||||
public TimeSpan EndTime;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Weapons.Marker;
|
||||
|
||||
/// <summary>
|
||||
/// Applies <see cref="DamageMarkerComponent"/> when colliding with an entity.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedDamageMarkerSystem))]
|
||||
public sealed partial class DamageMarkerOnCollideComponent : Component
|
||||
{
|
||||
[DataField("whitelist"), AutoNetworkedField]
|
||||
public EntityWhitelist? Whitelist = new();
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("duration"), AutoNetworkedField]
|
||||
public TimeSpan Duration = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>
|
||||
/// Additional damage to be applied.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("damage")]
|
||||
public DamageSpecifier Damage = new();
|
||||
|
||||
/// <summary>
|
||||
/// How many more times we can apply it.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("amount"), AutoNetworkedField]
|
||||
public int Amount = 1;
|
||||
}
|
||||
81
Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Weapons.Marker;
|
||||
|
||||
public abstract class SharedDamageMarkerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<DamageMarkerOnCollideComponent, StartCollideEvent>(OnMarkerCollide);
|
||||
SubscribeLocalEvent<DamageMarkerComponent, EntityUnpausedEvent>(OnMarkerUnpaused);
|
||||
SubscribeLocalEvent<DamageMarkerComponent, AttackedEvent>(OnMarkerAttacked);
|
||||
}
|
||||
|
||||
private void OnMarkerAttacked(EntityUid uid, DamageMarkerComponent component, AttackedEvent args)
|
||||
{
|
||||
if (component.Marker != args.Used)
|
||||
return;
|
||||
|
||||
args.BonusDamage += component.Damage;
|
||||
RemCompDeferred<DamageMarkerComponent>(uid);
|
||||
_audio.PlayPredicted(component.Sound, uid, args.User);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var query = EntityQueryEnumerator<DamageMarkerComponent>();
|
||||
|
||||
while (query.MoveNext(out var uid, out var comp))
|
||||
{
|
||||
if (comp.EndTime > _timing.CurTime)
|
||||
continue;
|
||||
|
||||
RemCompDeferred<DamageMarkerComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMarkerUnpaused(EntityUid uid, DamageMarkerComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.EndTime += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnMarkerCollide(EntityUid uid, DamageMarkerOnCollideComponent component, ref StartCollideEvent args)
|
||||
{
|
||||
if (!args.OtherFixture.Hard ||
|
||||
args.OurFixture.ID != SharedProjectileSystem.ProjectileFixture ||
|
||||
component.Amount <= 0 ||
|
||||
component.Whitelist?.IsValid(args.OtherEntity, EntityManager) == false ||
|
||||
!TryComp<ProjectileComponent>(uid, out var projectile) ||
|
||||
!projectile.Weapon.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Markers are exclusive, deal with it.
|
||||
var marker = EnsureComp<DamageMarkerComponent>(args.OtherEntity);
|
||||
marker.Damage = new DamageSpecifier(component.Damage);
|
||||
marker.Marker = projectile.Weapon;
|
||||
marker.EndTime = _timing.CurTime + component.Duration;
|
||||
component.Amount--;
|
||||
Dirty(marker);
|
||||
|
||||
if (component.Amount <= 0)
|
||||
{
|
||||
QueueDel(uid);
|
||||
}
|
||||
else
|
||||
{
|
||||
Dirty(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Weapons.Melee.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that this meleeweapon requires wielding to be useable.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class MeleeRequiresWieldComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
@@ -37,6 +38,8 @@ namespace Content.Shared.Weapons.Melee.Events
|
||||
/// </summary>
|
||||
public EntityCoordinates ClickLocation { get; }
|
||||
|
||||
public DamageSpecifier BonusDamage = new();
|
||||
|
||||
public AttackedEvent(EntityUid used, EntityUid user, EntityCoordinates clickLocation)
|
||||
{
|
||||
Used = used;
|
||||
|
||||
7
Content.Shared/Weapons/Melee/Events/AttemptMeleeEvent.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Content.Shared.Weapons.Melee.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed on a weapon when attempt a melee attack.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct AttemptMeleeEvent(bool Cancelled, string? Message);
|
||||
@@ -15,6 +15,8 @@ using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Weapons.Melee.Components;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -70,6 +72,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
SubscribeLocalEvent<MeleeWeaponComponent, ComponentHandleState>(OnHandleState);
|
||||
SubscribeLocalEvent<MeleeWeaponComponent, HandDeselectedEvent>(OnMeleeDropped);
|
||||
SubscribeLocalEvent<MeleeWeaponComponent, HandSelectedEvent>(OnMeleeSelected);
|
||||
SubscribeLocalEvent<MeleeWeaponComponent, GunShotEvent>(OnMeleeShot);
|
||||
|
||||
SubscribeAllEvent<HeavyAttackEvent>(OnHeavyAttack);
|
||||
SubscribeAllEvent<LightAttackEvent>(OnLightAttack);
|
||||
@@ -89,6 +92,18 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnMeleeShot(EntityUid uid, MeleeWeaponComponent component, ref GunShotEvent args)
|
||||
{
|
||||
if (!TryComp<GunComponent>(uid, out var gun))
|
||||
return;
|
||||
|
||||
if (gun.NextFire > component.NextAttack)
|
||||
{
|
||||
component.NextAttack = gun.NextFire;
|
||||
Dirty(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMeleeUnpaused(EntityUid uid, MeleeWeaponComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextAttack += args.PausedTime;
|
||||
@@ -356,38 +371,64 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
}
|
||||
|
||||
// Windup time checked elsewhere.
|
||||
var fireRate = TimeSpan.FromSeconds(1f / weapon.AttackRate);
|
||||
var swings = 0;
|
||||
|
||||
// TODO: If we get autoattacks then probably need a shotcounter like guns so we can do timing properly.
|
||||
if (weapon.NextAttack < curTime)
|
||||
weapon.NextAttack = curTime;
|
||||
|
||||
weapon.NextAttack += TimeSpan.FromSeconds(1f / weapon.AttackRate);
|
||||
|
||||
// Attack confirmed
|
||||
string animation;
|
||||
|
||||
switch (attack)
|
||||
while (weapon.NextAttack <= curTime)
|
||||
{
|
||||
case LightAttackEvent light:
|
||||
DoLightAttack(user, light, weaponUid, weapon, session);
|
||||
animation = weapon.ClickAnimation;
|
||||
break;
|
||||
case DisarmAttackEvent disarm:
|
||||
if (!DoDisarm(user, disarm, weaponUid, weapon, session))
|
||||
return;
|
||||
|
||||
animation = weapon.ClickAnimation;
|
||||
break;
|
||||
case HeavyAttackEvent heavy:
|
||||
DoHeavyAttack(user, heavy, weaponUid, weapon, session);
|
||||
animation = weapon.WideAnimation;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
weapon.NextAttack += fireRate;
|
||||
swings++;
|
||||
}
|
||||
|
||||
DoLungeAnimation(user, weapon.Angle, attack.Coordinates.ToMap(EntityManager, TransformSystem), weapon.Range, animation);
|
||||
weapon.Attacking = true;
|
||||
Dirty(weapon);
|
||||
|
||||
// Do this AFTER attack so it doesn't spam every tick
|
||||
var ev = new AttemptMeleeEvent();
|
||||
RaiseLocalEvent(weaponUid, ref ev);
|
||||
|
||||
if (ev.Cancelled)
|
||||
{
|
||||
if (ev.Message != null)
|
||||
{
|
||||
PopupSystem.PopupClient(ev.Message, weaponUid, user);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Attack confirmed
|
||||
for (var i = 0; i < swings; i++)
|
||||
{
|
||||
string animation;
|
||||
|
||||
switch (attack)
|
||||
{
|
||||
case LightAttackEvent light:
|
||||
DoLightAttack(user, light, weaponUid, weapon, session);
|
||||
animation = weapon.ClickAnimation;
|
||||
break;
|
||||
case DisarmAttackEvent disarm:
|
||||
if (!DoDisarm(user, disarm, weaponUid, weapon, session))
|
||||
return;
|
||||
|
||||
animation = weapon.ClickAnimation;
|
||||
break;
|
||||
case HeavyAttackEvent heavy:
|
||||
DoHeavyAttack(user, heavy, weaponUid, weapon, session);
|
||||
animation = weapon.WideAnimation;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
DoLungeAnimation(user, weapon.Angle, attack.Coordinates.ToMap(EntityManager, TransformSystem), weapon.Range, animation);
|
||||
}
|
||||
|
||||
weapon.Attacking = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -469,9 +510,10 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
Interaction.DoContactInteraction(user, ev.Target);
|
||||
|
||||
// For stuff that cares about it being attacked.
|
||||
RaiseLocalEvent(ev.Target.Value, new AttackedEvent(meleeUid, user, targetXform.Coordinates));
|
||||
var attackedEvent = new AttackedEvent(meleeUid, user, targetXform.Coordinates);
|
||||
RaiseLocalEvent(ev.Target.Value, attackedEvent);
|
||||
|
||||
var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage, hitEvent.ModifiersList);
|
||||
var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage + attackedEvent.BonusDamage, hitEvent.ModifiersList);
|
||||
var damageResult = Damageable.TryChangeDamage(ev.Target, modifiedDamage, origin:user);
|
||||
|
||||
if (damageResult != null && damageResult.Total > FixedPoint2.Zero)
|
||||
@@ -596,16 +638,15 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
// If the user is using a long-range weapon, this probably shouldn't be happening? But I'll interpret melee as a
|
||||
// somewhat messy scuffle. See also, light attacks.
|
||||
Interaction.DoContactInteraction(user, target);
|
||||
|
||||
RaiseLocalEvent(target, new AttackedEvent(meleeUid, user, Transform(target).Coordinates));
|
||||
}
|
||||
|
||||
var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage, hitEvent.ModifiersList);
|
||||
var appliedDamage = new DamageSpecifier();
|
||||
|
||||
foreach (var entity in targets)
|
||||
{
|
||||
RaiseLocalEvent(entity, new AttackedEvent(meleeUid, user, ev.Coordinates));
|
||||
var attackedEvent = new AttackedEvent(meleeUid, user, ev.Coordinates);
|
||||
RaiseLocalEvent(entity, attackedEvent);
|
||||
var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage + attackedEvent.BonusDamage, hitEvent.ModifiersList);
|
||||
|
||||
var damageResult = Damageable.TryChangeDamage(entity, modifiedDamage, origin:user);
|
||||
|
||||
@@ -631,7 +672,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
if (appliedDamage.Total > FixedPoint2.Zero)
|
||||
{
|
||||
var target = entities.First();
|
||||
PlayHitSound(target, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component.HitSound);
|
||||
PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component.HitSound);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -79,6 +79,12 @@ public partial class GunComponent : Component
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Whether this gun is shot via the use key or the alt-use key.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("useKey"), AutoNetworkedField]
|
||||
public bool UseKey = true;
|
||||
|
||||
/// <summary>
|
||||
/// Where the gun is being requested to shoot.
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Weapons.Ranged.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that this gun requires wielding to be useable.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class GunRequiresWieldComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Weapons.Ranged.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Applies UseDelay whenever the entity shoots.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(UseDelayOnShootSystem))]
|
||||
public sealed class UseDelayOnShootComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
@@ -47,8 +47,10 @@ public sealed class RechargeBasicEntityAmmoSystem : EntitySystem
|
||||
|
||||
if (_gun.UpdateBasicEntityAmmoCount(uid, ammo.Count.Value + 1, ammo))
|
||||
{
|
||||
if (_netManager.IsClient && _timing.IsFirstTimePredicted)
|
||||
_audio.Play(recharge.RechargeSound, Filter.Local(), uid, true);
|
||||
// We don't predict this because occasionally on client it may not play.
|
||||
// PlayPredicted will still be predicted on the client.
|
||||
if (_netManager.IsServer)
|
||||
_audio.PlayPvs(recharge.RechargeSound, uid);
|
||||
}
|
||||
|
||||
if (ammo.Count == ammo.Capacity)
|
||||
|
||||
@@ -13,11 +13,12 @@ using Content.Shared.Projectiles;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics.Components;
|
||||
@@ -70,7 +71,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
Sawmill.Level = LogLevel.Info;
|
||||
SubscribeAllEvent<RequestShootEvent>(OnShootRequest);
|
||||
SubscribeAllEvent<RequestStopShootEvent>(OnStopShootRequest);
|
||||
SubscribeLocalEvent<GunComponent, MeleeAttackAttemptEvent>(OnGunMeleeAttempt);
|
||||
SubscribeLocalEvent<GunComponent, MeleeHitEvent>(OnGunMelee);
|
||||
|
||||
// Ammo providers
|
||||
InitializeBallistic();
|
||||
@@ -103,19 +104,23 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
#endif
|
||||
}
|
||||
|
||||
private void OnGunMelee(EntityUid uid, GunComponent component, MeleeHitEvent args)
|
||||
{
|
||||
if (!TryComp<MeleeWeaponComponent>(uid, out var melee))
|
||||
return;
|
||||
|
||||
if (melee.NextAttack > component.NextFire)
|
||||
{
|
||||
component.NextFire = melee.NextAttack;
|
||||
Dirty(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGunUnpaused(EntityUid uid, GunComponent component, ref EntityUnpausedEvent args)
|
||||
{
|
||||
component.NextFire += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnGunMeleeAttempt(EntityUid uid, GunComponent component, ref MeleeAttackAttemptEvent args)
|
||||
{
|
||||
if (TagSystem.HasTag(args.User, "GunsDisabled"))
|
||||
return;
|
||||
|
||||
args.Cancelled = true;
|
||||
}
|
||||
|
||||
private void OnShootRequest(RequestShootEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var user = args.SenderSession.AttachedEntity;
|
||||
@@ -214,15 +219,12 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
if (toCoordinates == null)
|
||||
return;
|
||||
|
||||
if (TagSystem.HasTag(user, "GunsDisabled"))
|
||||
{
|
||||
if (Timing.IsFirstTimePredicted)
|
||||
Popup(Loc.GetString("gun-disabled"), user, user);
|
||||
return;
|
||||
}
|
||||
|
||||
var curTime = Timing.CurTime;
|
||||
|
||||
// Maybe Raise an event for this? CanAttack doesn't seem appropriate.
|
||||
if (TryComp<MeleeWeaponComponent>(gunUid, out var melee) && melee.NextAttack > curTime)
|
||||
return;
|
||||
|
||||
// Need to do this to play the clicking sound for empty automatic weapons
|
||||
// but not play anything for burst fire.
|
||||
if (gun.NextFire > curTime)
|
||||
@@ -232,7 +234,8 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
|
||||
// First shot
|
||||
// Previously we checked shotcounter but in some cases all the bullets got dumped at once
|
||||
if (gun.NextFire < curTime - fireRate)
|
||||
// curTime - fireRate is insufficient because if you time it just right you can get a 3rd shot out slightly quicker.
|
||||
if (gun.NextFire < curTime - fireRate || gun.ShotCounter == 0 && gun.NextFire < curTime)
|
||||
gun.NextFire = curTime;
|
||||
|
||||
var shots = 0;
|
||||
@@ -263,6 +266,20 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
throw new ArgumentOutOfRangeException($"No implemented shooting behavior for {gun.SelectedMode}!");
|
||||
}
|
||||
|
||||
var attemptEv = new AttemptShootEvent(user, null);
|
||||
RaiseLocalEvent(gunUid, ref attemptEv);
|
||||
|
||||
if (attemptEv.Cancelled)
|
||||
{
|
||||
if (attemptEv.Message != null)
|
||||
{
|
||||
PopupSystem.PopupClient(attemptEv.Message, gunUid, user);
|
||||
}
|
||||
|
||||
gun.NextFire = TimeSpan.FromSeconds(Math.Max(lastFire.TotalSeconds + SafetyNextFire, gun.NextFire.TotalSeconds));
|
||||
return;
|
||||
}
|
||||
|
||||
var fromCoordinates = Transform(user).Coordinates;
|
||||
// Remove ammo
|
||||
var ev = new TakeAmmoEvent(shots, new List<(EntityUid? Entity, IShootable Shootable)>(), fromCoordinates, user);
|
||||
@@ -279,10 +296,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
// where the gun may be SemiAuto or Burst.
|
||||
gun.ShotCounter += shots;
|
||||
|
||||
var attemptEv = new AttemptShootEvent(user);
|
||||
RaiseLocalEvent(gunUid, ref attemptEv);
|
||||
|
||||
if (ev.Ammo.Count <= 0 || attemptEv.Cancelled)
|
||||
if (ev.Ammo.Count <= 0)
|
||||
{
|
||||
// Play empty gun sounds if relevant
|
||||
// If they're firing an existing clip then don't play anything.
|
||||
@@ -415,7 +429,7 @@ public abstract partial class SharedGunSystem : EntitySystem
|
||||
/// <param name="Cancelled">Set this to true if the shot should be cancelled.</param>
|
||||
/// <param name="ThrowItems">Set this to true if the ammo shouldn't actually be fired, just thrown.</param>
|
||||
[ByRefEvent]
|
||||
public record struct AttemptShootEvent(EntityUid User, bool Cancelled = false, bool ThrowItems = false);
|
||||
public record struct AttemptShootEvent(EntityUid User, string? Message, bool Cancelled = false, bool ThrowItems = false);
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed on the gun after firing.
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
|
||||
namespace Content.Shared.Weapons.Ranged.Systems;
|
||||
|
||||
public sealed class UseDelayOnShootSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly UseDelaySystem _delay = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<UseDelayOnShootComponent, GunShotEvent>(OnUseShoot);
|
||||
}
|
||||
|
||||
private void OnUseShoot(EntityUid uid, UseDelayOnShootComponent component, ref GunShotEvent args)
|
||||
{
|
||||
_delay.BeginDelay(uid);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,10 @@ using Content.Shared.Item;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.Weapons.Melee.Components;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Content.Shared.Weapons.Ranged.Systems;
|
||||
using Content.Shared.Wieldable.Components;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
@@ -34,9 +38,32 @@ public sealed class WieldableSystem : EntitySystem
|
||||
SubscribeLocalEvent<WieldableComponent, GetVerbsEvent<InteractionVerb>>(AddToggleWieldVerb);
|
||||
SubscribeLocalEvent<WieldableComponent, DisarmAttemptEvent>(OnDisarmAttemptEvent);
|
||||
|
||||
SubscribeLocalEvent<MeleeRequiresWieldComponent, AttemptMeleeEvent>(OnMeleeAttempt);
|
||||
SubscribeLocalEvent<GunRequiresWieldComponent, AttemptShootEvent>(OnShootAttempt);
|
||||
|
||||
SubscribeLocalEvent<IncreaseDamageOnWieldComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
}
|
||||
|
||||
private void OnMeleeAttempt(EntityUid uid, MeleeRequiresWieldComponent component, ref AttemptMeleeEvent args)
|
||||
{
|
||||
if (TryComp<WieldableComponent>(uid, out var wieldable) &&
|
||||
!wieldable.Wielded)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
args.Message = Loc.GetString("wieldable-component-requires", ("item", uid));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShootAttempt(EntityUid uid, GunRequiresWieldComponent component, ref AttemptShootEvent args)
|
||||
{
|
||||
if (TryComp<WieldableComponent>(uid, out var wieldable) &&
|
||||
!wieldable.Wielded)
|
||||
{
|
||||
args.Cancelled = true;
|
||||
args.Message = Loc.GetString("wieldable-component-requires", ("item", uid));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisarmAttemptEvent(EntityUid uid, WieldableComponent component, DisarmAttemptEvent args)
|
||||
{
|
||||
if (component.Wielded)
|
||||
|
||||
4
Resources/Audio/Weapons/attributions.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
- files: ["plasm_cutter.ogg"]
|
||||
license: "CC-BY-SA-3.0"
|
||||
copyright: "Taken from Citadel station."
|
||||
source: "https://github.com/Citadel-Station-13/Citadel-Station-13-RP/blob/5b43cb2545a19957ec6ce3352dceac5e347e77df/sound/weapons/plasma_cutter.ogg"
|
||||
BIN
Resources/Audio/Weapons/plasma_cutter.ogg
Normal file
@@ -9,9 +9,11 @@ wieldable-component-successful-wield-other = { THE($user) } wields { THE($item)
|
||||
wieldable-component-failed-wield-other = { THE($user) } unwields { THE($item) }.
|
||||
|
||||
wieldable-component-no-hands = You don't have enough hands!
|
||||
wieldable-component-not-enough-free-hands = {$number ->
|
||||
wieldable-component-not-enough-free-hands = {$number ->
|
||||
[one] You need a free hand to wield { THE($item) }.
|
||||
*[other] You need { $number } free hands to wield { THE($item) }.
|
||||
}
|
||||
wieldable-component-not-in-hands = { CAPITALIZE(THE($item)) } isn't in your hands!
|
||||
|
||||
wieldable-component-requires = { CAPITALIZE(THE($item))} must be wielded!
|
||||
|
||||
|
||||
@@ -33,12 +33,6 @@
|
||||
isLooped: true
|
||||
property: Radius
|
||||
enabled: false
|
||||
toggleAction:
|
||||
name: action-name-toggle-light
|
||||
description: action-description-toggle-light
|
||||
icon: { sprite: Objects/Tools/flashlight.rsi, state: flashlight }
|
||||
iconOn: Objects/Tools/flashlight.rsi/flashlight-on.png
|
||||
event: !type:ToggleActionEvent
|
||||
- type: ToggleableLightVisuals
|
||||
spriteLayer: light
|
||||
inhandVisuals:
|
||||
|
||||
@@ -130,6 +130,7 @@
|
||||
parent: BaseBulletHighVelocity
|
||||
name: Icicle
|
||||
description: Brrrrr.
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Structures/Specific/Anomalies/ice_anom.rsi
|
||||
|
||||
@@ -309,6 +309,37 @@
|
||||
- type: TimedDespawn
|
||||
lifetime: 0.4
|
||||
|
||||
- type: entity
|
||||
id: BulletCharge
|
||||
name: charge bolt
|
||||
parent: BaseBulletHighVelocity
|
||||
noSpawn: true
|
||||
description: Marks a target for additional damage.
|
||||
components:
|
||||
- type: Sprite
|
||||
noRot: false
|
||||
sprite: Objects/Weapons/Guns/Projectiles/magic.rsi
|
||||
layers:
|
||||
- state: chronobolt
|
||||
shader: unshaded
|
||||
- type: GatheringProjectile
|
||||
- type: DamageMarkerOnCollide
|
||||
whitelist:
|
||||
components:
|
||||
- MobState
|
||||
damage:
|
||||
types:
|
||||
Blunt: 20
|
||||
Slash: 5
|
||||
- type: Projectile
|
||||
impactEffect: BulletImpactEffectKinetic
|
||||
damage:
|
||||
types:
|
||||
Blunt: 0
|
||||
# Short lifespan
|
||||
- type: TimedDespawn
|
||||
lifetime: 0.4
|
||||
|
||||
- type: entity
|
||||
parent: BaseBullet
|
||||
id: AnomalousParticleDelta
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
- type: entity
|
||||
name: crusher
|
||||
parent: BaseItem
|
||||
id: BaseWeaponCrusher # Crusher? But I...
|
||||
abstract: true
|
||||
description: An early design of the proto-kinetic accelerator.
|
||||
components:
|
||||
- type: Sharp
|
||||
- type: UnpoweredFlashlight
|
||||
toggleAction:
|
||||
name: action-name-toggle-light
|
||||
description: action-description-toggle-light
|
||||
icon: { sprite: Objects/Tools/flashlight.rsi, state: flashlight }
|
||||
iconOn: Objects/Tools/flashlight.rsi/flashlight-on.png
|
||||
event: !type:ToggleActionEvent
|
||||
- type: PointLight
|
||||
enabled: false
|
||||
radius: 4
|
||||
|
||||
- type: entity
|
||||
name: crusher
|
||||
parent: BaseWeaponCrusher
|
||||
id: WeaponCrusher
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Melee/crusher.rsi
|
||||
state: icon
|
||||
- type: AmmoCounter
|
||||
- type: UseDelayOnShoot
|
||||
- type: UseDelay
|
||||
delay: 1.9
|
||||
- type: Gun
|
||||
soundGunshot: /Audio/Weapons/plasma_cutter.ogg
|
||||
fireRate: 0.5
|
||||
useKey: false
|
||||
- type: RechargeBasicEntityAmmo
|
||||
rechargeCooldown: 1.5
|
||||
rechargeSound:
|
||||
path: /Audio/Weapons/Guns/MagIn/kinetic_reload.ogg
|
||||
- type: BasicEntityAmmoProvider
|
||||
proto: BulletCharge
|
||||
capacity: 1
|
||||
count: 1
|
||||
- type: MeleeWeapon
|
||||
attackRate: 0.75
|
||||
damage:
|
||||
types:
|
||||
Blunt: 10
|
||||
Slash: 5
|
||||
- type: Wieldable
|
||||
- type: MeleeRequiresWield
|
||||
- type: GunRequiresWield
|
||||
- type: Item
|
||||
size: 150
|
||||
- type: DisarmMalus
|
||||
- type: Tool
|
||||
qualities:
|
||||
- Prying
|
||||
|
||||
- type: entity
|
||||
name: crusher dagger
|
||||
parent: BaseWeaponCrusher
|
||||
id: WeaponCrusherDagger
|
||||
description: A scaled down version of a proto-kinetic crusher, usually used in a last ditch scenario.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Melee/crusher_dagger.rsi
|
||||
state: icon
|
||||
- type: MeleeWeapon
|
||||
attackRate: 1.5
|
||||
damage:
|
||||
types:
|
||||
Slash: 6.5
|
||||
- type: Item
|
||||
size: 30
|
||||
|
||||
# Like a crusher... but better
|
||||
- type: entity
|
||||
name: crusher glaive
|
||||
parent: WeaponCrusher
|
||||
id: WeaponCrusherGlaive
|
||||
description: An early design of the proto-kinetic accelerator, in glaive form.
|
||||
components:
|
||||
- type: UseDelayOnShoot
|
||||
- type: UseDelay
|
||||
delay: 1.9
|
||||
- type: Gun
|
||||
fireRate: 1
|
||||
- type: RechargeBasicEntityAmmo
|
||||
rechargeCooldown: 0.5
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Melee/crusher_glaive.rsi
|
||||
- type: MeleeWeapon
|
||||
attackRate: 1.25
|
||||
- type: Item
|
||||
size: 150
|
||||
@@ -3,6 +3,7 @@
|
||||
description: Accelerated particles.
|
||||
id: ParticlesProjectile
|
||||
parent: BaseBullet
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: Sprite
|
||||
layers:
|
||||
|
||||
@@ -314,9 +314,6 @@
|
||||
- type: Tag
|
||||
id: GuideEmbeded
|
||||
|
||||
- type: Tag
|
||||
id: GunsDisabled # Allow certain entities to not use guns without complicating the system with an event
|
||||
|
||||
- type: Tag
|
||||
id: Handcuffs
|
||||
|
||||
|
||||
25
Resources/Textures/Objects/Weapons/Effects/meta.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "https://github.com/tgstation/tgstation/blob/192e2ce0821c8ed347f3b4164e7d76fe344f4bbf/icons/effects/effects.dmi",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "shield2",
|
||||
"delays": [
|
||||
[
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Objects/Weapons/Effects/shield2.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 230 B |
|
After Width: | Height: | Size: 198 B |
BIN
Resources/Textures/Objects/Weapons/Melee/crusher.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 716 B |
|
After Width: | Height: | Size: 655 B |
|
After Width: | Height: | Size: 637 B |
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "https://github.com/Citadel-Station-13/Citadel-Station-13-RP/blob/817e7c1f225876b45891e3f06908e6d032f0a8bc/icons/obj/mining.dmi",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "icon-lit"
|
||||
},
|
||||
{
|
||||
"name": "icon-uncharged",
|
||||
"delays": [
|
||||
[
|
||||
0.3,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "wielded-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "wielded-inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 823 B |
|
After Width: | Height: | Size: 851 B |
|
After Width: | Height: | Size: 185 B |
|
After Width: | Height: | Size: 353 B |
|
After Width: | Height: | Size: 503 B |
|
After Width: | Height: | Size: 495 B |
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "https://github.com/Citadel-Station-13/Citadel-Station-13-RP/blob/817e7c1f225876b45891e3f06908e6d032f0a8bc/icons/obj/mining.dmi",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "icon-lit"
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 190 B |
|
After Width: | Height: | Size: 198 B |
|
After Width: | Height: | Size: 618 B |
|
After Width: | Height: | Size: 576 B |
|
After Width: | Height: | Size: 568 B |
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "https://github.com/Citadel-Station-13/Citadel-Station-13-RP/blob/817e7c1f225876b45891e3f06908e6d032f0a8bc/icons/obj/mining.dmi",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "icon-lit"
|
||||
},
|
||||
{
|
||||
"name": "icon-uncharged",
|
||||
"delays": [
|
||||
[
|
||||
0.3,
|
||||
0.3
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "wielded-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "wielded-inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 567 B |
|
After Width: | Height: | Size: 520 B |