Blocking and Shields (#8584)
* Blocking * Fixes Bodytype bug * Blocking Damage Modifier * Storing bodytype * Consolidates Stop Blocking code * Consolidates more methods * Some cleanup, hitbox fix * Shield Textures * Passive blocking modifier check * Localization, popups, and more cleanup * Small cleanup * Relay event * Fixes a shutdown bug, adds specific containers and sets * Popups and sounds * Fixes typo * Removes whitespace, adds comment * Some requested changes * Remove Shared * Audio fix * More changes * More requested changes * Properly remove on shutdown * Adds riot shields to seclathes * SecTech Riot shield * Constant variable * Relay transfer to user blocking system * More destruction behavior * Adds a shape field * Riot shield cleanup * More requested changes. * Prevents blocking attempt where a user cannot be anchored * Listen for anchor change * Unused using cleanup * More shields. * Buckler * Construction * Linter fix
194
Content.Shared/Blocking/BlockingSystem.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Toggleable;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Blocking;
|
||||
|
||||
public sealed class BlockingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly FixtureSystem _fixtureSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BlockingComponent, GotEquippedHandEvent>(OnEquip);
|
||||
SubscribeLocalEvent<BlockingComponent, GotUnequippedHandEvent>(OnUnequip);
|
||||
|
||||
SubscribeLocalEvent<BlockingComponent, GetItemActionsEvent>(OnGetActions);
|
||||
SubscribeLocalEvent<BlockingComponent, ToggleActionEvent>(OnToggleAction);
|
||||
|
||||
SubscribeLocalEvent<BlockingComponent, ComponentShutdown>(OnShutdown);
|
||||
}
|
||||
|
||||
private void OnEquip(EntityUid uid, BlockingComponent component, GotEquippedHandEvent args)
|
||||
{
|
||||
component.User = args.User;
|
||||
|
||||
//To make sure that this bodytype doesn't get set as anything but the original
|
||||
if (TryComp<PhysicsComponent>(args.User, out var physicsComponent) && physicsComponent.BodyType != BodyType.Static
|
||||
&& !TryComp<BlockingUserComponent>(args.User, out var blockingUserComponent))
|
||||
{
|
||||
var userComp = EnsureComp<BlockingUserComponent>(args.User);
|
||||
userComp.BlockingItem = uid;
|
||||
userComp.OriginalBodyType = physicsComponent.BodyType;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUnequip(EntityUid uid, BlockingComponent component, GotUnequippedHandEvent args)
|
||||
{
|
||||
BlockingShutdownHelper(uid, component, args.User);
|
||||
}
|
||||
|
||||
private void OnGetActions(EntityUid uid, BlockingComponent component, GetItemActionsEvent args)
|
||||
{
|
||||
if (component.BlockingToggleAction == null
|
||||
&& _proto.TryIndex(component.BlockingToggleActionId, out InstantActionPrototype? act))
|
||||
{
|
||||
component.BlockingToggleAction = new(act);
|
||||
}
|
||||
|
||||
if (component.BlockingToggleAction != null)
|
||||
args.Actions.Add(component.BlockingToggleAction);
|
||||
}
|
||||
|
||||
private void OnToggleAction(EntityUid uid, BlockingComponent component, ToggleActionEvent args)
|
||||
{
|
||||
if(args.Handled)
|
||||
return;
|
||||
|
||||
if (component.IsBlocking)
|
||||
StopBlocking(uid, component, args.Performer);
|
||||
else
|
||||
StartBlocking(uid, component, args.Performer);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnShutdown(EntityUid uid, BlockingComponent component, ComponentShutdown args)
|
||||
{
|
||||
//In theory the user should not be null when this fires off
|
||||
if (component.User != null)
|
||||
{
|
||||
_actionsSystem.RemoveProvidedActions(component.User.Value, uid);
|
||||
BlockingShutdownHelper(uid, component, component.User.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called where you want the user to start blocking
|
||||
/// Creates a new hard fixture to bodyblock
|
||||
/// Also makes the user static to prevent prediction issues
|
||||
/// </summary>
|
||||
/// <param name="uid"> The entity with the blocking component</param>
|
||||
/// <param name="component"> The <see cref="BlockingComponent"/></param>
|
||||
/// <param name="user"> The entity who's using the item to block</param>
|
||||
/// <returns></returns>
|
||||
public bool StartBlocking(EntityUid item, BlockingComponent component, EntityUid user)
|
||||
{
|
||||
if (component.IsBlocking) return false;
|
||||
|
||||
var xform = Transform(user);
|
||||
|
||||
var shieldName = Name(item);
|
||||
|
||||
var msgUser = Loc.GetString("action-popup-blocking-user", ("shield", shieldName));
|
||||
var msgOther = Loc.GetString("action-popup-blocking-other", ("blockerName", Name(user)), ("shield", shieldName));
|
||||
|
||||
if (component.BlockingToggleAction != null)
|
||||
{
|
||||
_transformSystem.AnchorEntity(xform);
|
||||
if (!xform.Anchored)
|
||||
{
|
||||
var msgError = Loc.GetString("action-popup-blocking-user-cant-block");
|
||||
_popupSystem.PopupEntity(msgError, user, Filter.Entities(user));
|
||||
return false;
|
||||
}
|
||||
_actionsSystem.SetToggled(component.BlockingToggleAction, true);
|
||||
_popupSystem.PopupEntity(msgUser, user, Filter.Entities(user));
|
||||
_popupSystem.PopupEntity(msgOther, user, Filter.Pvs(user).RemoveWhereAttachedEntity(e => e == user));
|
||||
}
|
||||
|
||||
if (TryComp<PhysicsComponent>(user, out var physicsComponent))
|
||||
{
|
||||
var fixture = new Fixture(physicsComponent, component.Shape)
|
||||
{
|
||||
ID = BlockingComponent.BlockFixtureID,
|
||||
Hard = true,
|
||||
CollisionLayer = (int) CollisionGroup.WallLayer
|
||||
};
|
||||
|
||||
_fixtureSystem.TryCreateFixture(physicsComponent, fixture);
|
||||
}
|
||||
|
||||
component.IsBlocking = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called where you want the user to stop blocking.
|
||||
/// </summary>
|
||||
/// <param name="item"> The entity with the blocking component</param>
|
||||
/// <param name="component"> The <see cref="BlockingComponent"/></param>
|
||||
/// <param name="user"> The entity who's using the item to block</param>
|
||||
/// <returns></returns>
|
||||
public bool StopBlocking(EntityUid item, BlockingComponent component, EntityUid user)
|
||||
{
|
||||
if (!component.IsBlocking) return false;
|
||||
|
||||
var xform = Transform(user);
|
||||
|
||||
var shieldName = Name(item);
|
||||
|
||||
var msgUser = Loc.GetString("action-popup-blocking-disabling-user", ("shield", shieldName));
|
||||
var msgOther = Loc.GetString("action-popup-blocking-disabling-other", ("blockerName", Name(user)), ("shield", shieldName));
|
||||
|
||||
//If the component blocking toggle isn't null, grab the users SharedBlockingUserComponent and PhysicsComponent
|
||||
//then toggle the action to false, unanchor the user, remove the hard fixture
|
||||
//and set the users bodytype back to their original type
|
||||
if (component.BlockingToggleAction != null && TryComp<BlockingUserComponent>(user, out var blockingUserComponent)
|
||||
&& TryComp<PhysicsComponent>(user, out var physicsComponent))
|
||||
{
|
||||
_transformSystem.Unanchor(xform);
|
||||
_actionsSystem.SetToggled(component.BlockingToggleAction, false);
|
||||
_fixtureSystem.DestroyFixture(physicsComponent, BlockingComponent.BlockFixtureID);
|
||||
physicsComponent.BodyType = blockingUserComponent.OriginalBodyType;
|
||||
_popupSystem.PopupEntity(msgUser, user, Filter.Entities(user));
|
||||
_popupSystem.PopupEntity(msgOther, user, Filter.Pvs(user).RemoveWhereAttachedEntity(e => e == user));
|
||||
}
|
||||
|
||||
component.IsBlocking = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called where you want someone to stop blocking and to remove the <see cref="BlockingUserComponent"/> from them
|
||||
/// </summary>
|
||||
/// <param name="uid"> The item the component is attached to</param>
|
||||
/// <param name="component"> The <see cref="BlockingComponent"/> </param>
|
||||
/// <param name="user"> The person holding the blocking item </param>
|
||||
private void BlockingShutdownHelper(EntityUid uid, BlockingComponent component, EntityUid user)
|
||||
{
|
||||
if (component.IsBlocking)
|
||||
StopBlocking(uid, component, user);
|
||||
|
||||
RemComp<BlockingUserComponent>(user);
|
||||
component.User = null;
|
||||
}
|
||||
|
||||
}
|
||||
60
Content.Shared/Blocking/BlockingUserSystem.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Blocking;
|
||||
|
||||
public sealed class BlockingUserSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly BlockingSystem _blockingSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BlockingUserComponent, DamageChangedEvent>(OnDamageChanged);
|
||||
SubscribeLocalEvent<BlockingUserComponent, DamageModifyEvent>(OnUserDamageModified);
|
||||
|
||||
SubscribeLocalEvent<BlockingUserComponent, AnchorStateChangedEvent>(OnAnchorChanged);
|
||||
}
|
||||
|
||||
private void OnAnchorChanged(EntityUid uid, BlockingUserComponent component, ref AnchorStateChangedEvent args)
|
||||
{
|
||||
if (!args.Anchored)
|
||||
return;
|
||||
|
||||
if (TryComp<BlockingComponent>(component.BlockingItem, out var blockComp) && blockComp.IsBlocking)
|
||||
{
|
||||
_blockingSystem.StopBlocking(component.BlockingItem.Value, blockComp, uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDamageChanged(EntityUid uid, BlockingUserComponent component, DamageChangedEvent args)
|
||||
{
|
||||
if (component.BlockingItem != null)
|
||||
{
|
||||
RaiseLocalEvent(component.BlockingItem.Value, args);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUserDamageModified(EntityUid uid, BlockingUserComponent component, DamageModifyEvent args)
|
||||
{
|
||||
if (TryComp<BlockingComponent>(component.BlockingItem, out var blockingComponent))
|
||||
{
|
||||
if (_proto.TryIndex(blockingComponent.PassiveBlockDamageModifer, out DamageModifierSetPrototype? passiveblockModifier) && !blockingComponent.IsBlocking)
|
||||
args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, passiveblockModifier);
|
||||
|
||||
if (_proto.TryIndex(blockingComponent.ActiveBlockDamageModifier, out DamageModifierSetPrototype? activeBlockModifier) && blockingComponent.IsBlocking)
|
||||
{
|
||||
args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, activeBlockModifier);
|
||||
SoundSystem.Play(blockingComponent.BlockSound.GetSound(), Filter.Pvs(component.Owner, entityManager: EntityManager), component.Owner, AudioHelpers.WithVariation(0.2f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Content.Shared/Blocking/Components/BlockingComponent.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.Physics.Collision.Shapes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Blocking;
|
||||
|
||||
/// <summary>
|
||||
/// This component goes on an item that you want to use to block
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class BlockingComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity that's blocking
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public EntityUid? User;
|
||||
|
||||
/// <summary>
|
||||
/// Is it currently blocking?
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool IsBlocking;
|
||||
|
||||
/// <summary>
|
||||
/// The ID for the fixture that's dynamically created when blocking
|
||||
/// </summary>
|
||||
public const string BlockFixtureID = "blocking-active";
|
||||
|
||||
/// <summary>
|
||||
/// The shape of the blocking fixture that will be dynamically spawned
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("shape")]
|
||||
public IPhysShape Shape = new PhysShapeCircle {Radius = 0.5F};
|
||||
|
||||
/// <summary>
|
||||
/// The damage modifer to use while passively blocking
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("passiveBlockModifier")]
|
||||
public string PassiveBlockDamageModifer = "Metallic";
|
||||
|
||||
/// <summary>
|
||||
/// The damage modifier to use while actively blocking.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("activeBlockModifier")]
|
||||
public string ActiveBlockDamageModifier = "Metallic";
|
||||
|
||||
[DataField("blockingToggleActionId", customTypeSerializer:typeof(PrototypeIdSerializer<InstantActionPrototype>))]
|
||||
public string BlockingToggleActionId = "ToggleBlock";
|
||||
|
||||
[DataField("blockingToggleAction")]
|
||||
public InstantAction? BlockingToggleAction;
|
||||
|
||||
/// <summary>
|
||||
/// The sound to be played when you get hit while actively blocking
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("blockSound")]
|
||||
public SoundSpecifier BlockSound = new SoundPathSpecifier("/Audio/Weapons/block_metal1.ogg");
|
||||
}
|
||||
31
Content.Shared/Blocking/Components/BlockingUserComponent.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Shared.Blocking;
|
||||
|
||||
/// <summary>
|
||||
/// This component gets dynamically added to an Entity via the <see cref="BlockingSystem"/>
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class BlockingUserComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity that's being used to block
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("blockingItem")]
|
||||
public EntityUid? BlockingItem;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("modifiers")]
|
||||
public DamageModifierSet Modifiers = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the entities original bodytype
|
||||
/// Used so that it can be put back to what it was after anchoring
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("originalBodyType")]
|
||||
public BodyType OriginalBodyType;
|
||||
|
||||
}
|
||||
BIN
Resources/Audio/Weapons/block_metal1.ogg
Normal file
@@ -5,3 +5,5 @@ grille_hit.ogg taken from https://github.com/tgstation/tgstation/blob/803ca4537d
|
||||
slash.ogg taken from https://github.com/tgstation/tgstation/blob/5d264fbea0124e5af511af3fed24203e196d108b/sound/weapons/slash.ogg under CC BY-SA 3.0
|
||||
|
||||
tap.ogg taken from https://github.com/tgstation/tgstation/blob/803ca4537df35cf252b056d8460d510be8a4f353/sound/weapons/tap.ogg under CC BY-SA 3.0
|
||||
|
||||
block_metal1.ogg taken from https://github.com/Citadel-Station-13/Citadel-Station-13/commit/31c5996a5db8cce0cb431cb1dc20d99cac83f268 under CC BY-SA 3.0
|
||||
10
Resources/Locale/en-US/actions/actions/blocking.ftl
Normal file
@@ -0,0 +1,10 @@
|
||||
action-name-blocking = Block
|
||||
action-description-blocking = Raise or lower your shield.
|
||||
|
||||
action-popup-blocking-user = You raise your {$shield}!
|
||||
action-popup-blocking-disabling-user = You lower your {$shield}!
|
||||
|
||||
action-popup-blocking-other = {$blockerName} raises their {$shield}!
|
||||
action-popup-blocking-disabling-other = {$blockerName} lowers their {$shield}!
|
||||
|
||||
action-popup-blocking-user-cant-block = The gravity here prevents you from blocking.
|
||||
@@ -54,3 +54,11 @@
|
||||
description: vending-machine-action-description
|
||||
useDelay: 30
|
||||
event: !type:VendingMachineSelfDispenseEvent
|
||||
|
||||
- type: instantAction
|
||||
id: ToggleBlock
|
||||
name: action-name-blocking
|
||||
description: action-description-blocking
|
||||
icon: Objects/Weapons/Melee/shields.rsi/teleriot-icon.png
|
||||
iconOn: Objects/Weapons/Melee/shields.rsi/teleriot-on.png
|
||||
event: !type:ToggleActionEvent
|
||||
|
||||
@@ -158,6 +158,7 @@
|
||||
- Flash
|
||||
- Handcuffs
|
||||
- Stunbaton
|
||||
- RiotShield
|
||||
- FlashPayload
|
||||
|
||||
- type: technology
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
ClothingEyesGlassesSunglasses: 2
|
||||
ClothingBeltSecurityWebbing: 5
|
||||
Zipties: 12
|
||||
RiotShield: 2
|
||||
RiotLaserShield: 2
|
||||
RiotBulletShield: 2
|
||||
# security officers need to follow a diet regimen!
|
||||
contrabandInventory:
|
||||
FoodDonutHomer: 12
|
||||
|
||||
@@ -15,3 +15,12 @@
|
||||
- Heat
|
||||
- Shock
|
||||
- Structural # this probably should be in separate container
|
||||
|
||||
- type: damageContainer
|
||||
id: Shield
|
||||
supportedGroups:
|
||||
- Brute
|
||||
supportedTypes:
|
||||
- Heat
|
||||
|
||||
|
||||
|
||||
@@ -151,3 +151,138 @@
|
||||
Bloodloss: 0.0 # no double dipping
|
||||
Cellular: 0.0
|
||||
|
||||
# Represents what a riot shield should block passively
|
||||
# Each shield will probably have their own passive and active modifier sets
|
||||
# Honestly it should not be too high
|
||||
- type: damageModifierSet
|
||||
id: PassiveRiotShieldBlock
|
||||
coefficients:
|
||||
Blunt: 0.9
|
||||
Slash: 0.9
|
||||
Piercing: 0.9
|
||||
Heat: 0.9
|
||||
|
||||
# Represents what a riot shield should block while active
|
||||
# Should be a higher reduction because you're anchored
|
||||
- type: damageModifierSet
|
||||
id: ActiveRiotShieldBlock
|
||||
coefficients:
|
||||
Blunt: 0.8
|
||||
Slash: 0.8
|
||||
Piercing: 0.8
|
||||
Heat: 0.8
|
||||
flatReductions:
|
||||
Blunt: 1
|
||||
Slash: 1
|
||||
Piercing: 1
|
||||
Heat: 1
|
||||
|
||||
- type: damageModifierSet
|
||||
id: PassiveRiotLaserShieldBlock
|
||||
coefficients:
|
||||
Heat: 0.8
|
||||
|
||||
- type: damageModifierSet
|
||||
id: ActiveRiotLaserShieldBlock
|
||||
coefficients:
|
||||
Heat: 0.7
|
||||
flatReductions:
|
||||
Heat: 2
|
||||
|
||||
- type: damageModifierSet
|
||||
id: PassiveRiotBulletShieldBlock
|
||||
coefficients:
|
||||
Blunt: 0.8
|
||||
Piercing: 0.8
|
||||
|
||||
- type: damageModifierSet
|
||||
id: ActiveRiotBulletShieldBlock
|
||||
coefficients:
|
||||
Blunt: 0.7
|
||||
Piercing: 0.7
|
||||
flatReductions:
|
||||
Blunt: 1.5
|
||||
Piercing: 1.5
|
||||
|
||||
#It's made from wood, so it should probably take more heat/laser damage
|
||||
- type: damageModifierSet
|
||||
id: PassiveBucklerBlock
|
||||
coefficients:
|
||||
Blunt: 0.95
|
||||
Slash: 0.95
|
||||
Piercing: 0.95
|
||||
Heat: 2
|
||||
|
||||
- type: damageModifierSet
|
||||
id: ActiveBucklerBlock
|
||||
coefficients:
|
||||
Blunt: 0.85
|
||||
Slash: 0.85
|
||||
Piercing: 0.85
|
||||
Heat: 2
|
||||
flatReductions:
|
||||
Blunt: 1
|
||||
Slash: 1
|
||||
Piercing: 1
|
||||
|
||||
#It's a really crummy shield
|
||||
- type: damageModifierSet
|
||||
id: PassiveMakeshiftBlock
|
||||
coefficients:
|
||||
Blunt: 0.95
|
||||
Slash: 0.95
|
||||
Piercing: 0.95
|
||||
Heat: 0.9
|
||||
|
||||
- type: damageModifierSet
|
||||
id: ActiveMakeshiftBlock
|
||||
coefficients:
|
||||
Blunt: 0.85
|
||||
Slash: 0.85
|
||||
Piercing: 0.85
|
||||
Heat: 0.8
|
||||
flatReductions:
|
||||
Blunt: 0.5
|
||||
Slash: 0.5
|
||||
Piercing: 0.5
|
||||
Heat: 1
|
||||
|
||||
#Clockwork generally takes more laser/heat damage
|
||||
- type: damageModifierSet
|
||||
id: PassiveClockworkShieldBlock
|
||||
coefficients:
|
||||
Blunt: 0.8
|
||||
Slash: 0.8
|
||||
Piercing: 0.8
|
||||
Heat: 1.5
|
||||
|
||||
- type: damageModifierSet
|
||||
id: ActiveClockworkShieldBlock
|
||||
coefficients:
|
||||
Blunt: 0.7
|
||||
Slash: 0.7
|
||||
Piercing: 0.7
|
||||
Heat: 1.5
|
||||
flatReductions:
|
||||
Blunt: 1
|
||||
Slash: 1
|
||||
Piercing: 1
|
||||
|
||||
#Mirror shield should reflect heat/laser eventually, but be relatively weak to everything else.
|
||||
- type: damageModifierSet
|
||||
id: PassiveMirrorShieldBlock
|
||||
coefficients:
|
||||
Blunt: 1.2
|
||||
Slash: 1.2
|
||||
Piercing: 1.2
|
||||
Heat: .7
|
||||
|
||||
- type: damageModifierSet
|
||||
id: ActiveMirrorShieldBlock
|
||||
coefficients:
|
||||
Blunt: 1.2
|
||||
Slash: 1.2
|
||||
Piercing: 1.2
|
||||
Heat: .6
|
||||
flatReductions:
|
||||
Heat: 1
|
||||
|
||||
194
Resources/Prototypes/Entities/Objects/Shields/shields.yml
Normal file
@@ -0,0 +1,194 @@
|
||||
- type: entity
|
||||
name: base shield
|
||||
parent: BaseItem
|
||||
id: BaseShield
|
||||
description: A shield!
|
||||
abstract: true
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Weapons/Melee/shields.rsi
|
||||
state: riot-icon
|
||||
netsync: false
|
||||
- type: Item
|
||||
sprite: Objects/Weapons/Melee/shields.rsi
|
||||
size: 100
|
||||
HeldPrefix: riot
|
||||
- type: Blocking
|
||||
passiveBlockModifier: PassiveRiotShieldBlock
|
||||
activeBlockModifier: ActiveRiotShieldBlock
|
||||
blockingToggleAction:
|
||||
name: action-name-blocking
|
||||
description: action-description-blocking
|
||||
icon: Objects/Weapons/Melee/shields.rsi/teleriot-icon.png
|
||||
iconOn: Objects/Weapons/Melee/shields.rsi/teleriot-on.png
|
||||
event: !type:ToggleActionEvent
|
||||
- type: Damageable
|
||||
damageContainer: Shield
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 40 #This is probably enough damage before it breaks
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/metalbreak.ogg
|
||||
- !type:SpawnEntitiesBehavior
|
||||
spawn:
|
||||
SheetSteel:
|
||||
min: 5
|
||||
max: 5
|
||||
SheetGlass:
|
||||
min: 5
|
||||
max: 5
|
||||
|
||||
#Security Shields
|
||||
|
||||
- type: entity
|
||||
name: riot shield
|
||||
parent: BaseShield
|
||||
id: RiotShield
|
||||
description: A large tower shield. Good for controlling crowds.
|
||||
|
||||
- type: entity
|
||||
name: riot laser shield
|
||||
parent: BaseShield
|
||||
id: RiotLaserShield
|
||||
description: A riot shield built for withstanding lasers, but not much else.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: riot_laser-icon
|
||||
- type: Item
|
||||
HeldPrefix: riot_laser
|
||||
- type: Blocking
|
||||
passiveBlockModifier: PassiveRiotLaserShieldBlock
|
||||
activeBlockModifier: ActiveRiotLaserShieldBlock
|
||||
|
||||
- type: entity
|
||||
name: riot bullet shield
|
||||
parent: BaseShield
|
||||
id: RiotBulletShield
|
||||
description: A ballistic riot shield built for withstanding bullets, but not much else.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: riot_bullet-icon
|
||||
- type: Item
|
||||
HeldPrefix: riot_bullet
|
||||
- type: Blocking
|
||||
passiveBlockModifier: PassiveRiotBulletShieldBlock
|
||||
activeBlockModifier: ActiveRiotBulletShieldBlock
|
||||
|
||||
#Craftable shields
|
||||
|
||||
- type: entity
|
||||
name: wooden buckler
|
||||
parent: BaseShield
|
||||
id: WoodenBuckler
|
||||
description: A small round wooden makeshift shield.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: buckler-icon
|
||||
- type: Item
|
||||
HeldPrefix: buckler
|
||||
- type: Blocking
|
||||
passiveBlockModifier: PassiveBucklerBlock
|
||||
activeBlockModifier: ActiveBucklerBlock
|
||||
- type: Construction
|
||||
graph: WoodenBuckler
|
||||
node: woodenBuckler
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 30 #Weaker shield
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/metalbreak.ogg
|
||||
- !type:SpawnEntitiesBehavior
|
||||
spawn:
|
||||
MaterialWoodPlank:
|
||||
min: 5
|
||||
max: 5
|
||||
|
||||
- type: entity
|
||||
name: makeshift shield
|
||||
parent: BaseShield
|
||||
id: MakeshiftShield
|
||||
description: A rundown looking shield, not good for much.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: makeshift-icon
|
||||
- type: Item
|
||||
HeldPrefix: metal
|
||||
- type: Blocking
|
||||
passiveBlockModifier: PassiveMakeshiftBlock
|
||||
activeBlockModifier: ActiveMakeshiftBlock
|
||||
- type: Construction
|
||||
graph: MakeshiftShield
|
||||
node: makeshiftShield
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 20 #Very weak shield
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/metalbreak.ogg
|
||||
- !type:SpawnEntitiesBehavior
|
||||
spawn:
|
||||
SheetSteel:
|
||||
min: 1
|
||||
max: 2
|
||||
|
||||
#Magic/Cult Shields (give these to wizard for now)
|
||||
|
||||
- type: entity
|
||||
name: Clockwork Shield
|
||||
parent: BaseShield
|
||||
id: ClockworkShield
|
||||
description: Ratvar oyrffrf lbh jvgu uvf cebgrpgvba.
|
||||
components:
|
||||
- type: Sprite
|
||||
state: ratvarian-icon
|
||||
- type: Item
|
||||
HeldPrefix: ratvarian
|
||||
- type: Blocking
|
||||
passiveBlockModifier: PassiveClockworkShieldBlock
|
||||
activeBlockModifier: ActiveClockworkShieldBlock
|
||||
#Have it break into brass when clock cult is in
|
||||
|
||||
- type: entity
|
||||
name: Mirror Shield
|
||||
parent: BaseShield
|
||||
id: MirrorShield
|
||||
description: Eerily glows red... you hear the geometer whispering
|
||||
components:
|
||||
- type: Sprite
|
||||
state: mirror-icon
|
||||
- type: Item
|
||||
HeldPrefix: mirror
|
||||
- type: Blocking
|
||||
passiveBlockModifier: PassiveMirrorShieldBlock
|
||||
activeBlockModifier: ActiveMirrorShieldBlock
|
||||
blockSound: !type:SoundPathSpecifier
|
||||
path: /Audio/Effects/glass_step.ogg
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 40
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/glass_break1.ogg
|
||||
- !type:SpawnEntitiesBehavior
|
||||
spawn:
|
||||
SheetGlass:
|
||||
min: 5
|
||||
max: 5
|
||||
@@ -305,6 +305,7 @@
|
||||
- Flash
|
||||
- Handcuffs
|
||||
- Stunbaton
|
||||
- RiotShield
|
||||
- CartridgePistol
|
||||
- ShellShotgun
|
||||
- CartridgeLightRifle
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
- type: constructionGraph
|
||||
id: MakeshiftShield
|
||||
start: start
|
||||
graph:
|
||||
- node: start
|
||||
edges:
|
||||
- to: makeshiftShield
|
||||
steps:
|
||||
- material: Cable
|
||||
amount: 15
|
||||
doAfter: 2
|
||||
- material: Steel
|
||||
amount: 30
|
||||
doAfter: 2
|
||||
|
||||
- node: makeshiftShield
|
||||
entity: MakeshiftShield
|
||||
@@ -0,0 +1,24 @@
|
||||
- type: constructionGraph
|
||||
id: WoodenBuckler
|
||||
start: start
|
||||
graph:
|
||||
- node: start
|
||||
edges:
|
||||
- to: woodenBuckler
|
||||
steps:
|
||||
- tag: Handcuffs
|
||||
icon:
|
||||
sprite: Objects/Misc/cablecuffs.rsi
|
||||
state: cuff
|
||||
color: red
|
||||
name: cuffs
|
||||
doAfter: 2
|
||||
- material: WoodPlank
|
||||
amount: 20
|
||||
doAfter: 2
|
||||
- material: Steel
|
||||
amount: 10
|
||||
doAfter: 2
|
||||
|
||||
- node: woodenBuckler
|
||||
entity: WoodenBuckler
|
||||
@@ -31,3 +31,24 @@
|
||||
icon: Objects/Weapons/Guns/Battery/makeshift.rsi/icon.png
|
||||
objectType: Item
|
||||
|
||||
- type: construction
|
||||
name: wooden buckler
|
||||
id: WoodenBuckler
|
||||
graph: WoodenBuckler
|
||||
startNode: start
|
||||
targetNode: woodenBuckler
|
||||
category: Weapons
|
||||
description: A nicely carved wooden shield!
|
||||
icon: Objects/Weapons/Melee/shields.rsi/buckler-icon.png
|
||||
objectType: Item
|
||||
|
||||
- type: construction
|
||||
name: makeshift shield
|
||||
id: MakeshiftShield
|
||||
graph: MakeshiftShield
|
||||
startNode: start
|
||||
targetNode: makeshiftShield
|
||||
category: Weapons
|
||||
description: Crude and falling apart. Why would you make this?
|
||||
icon: Objects/Weapons/Melee/shields.rsi/makeshift-icon.png
|
||||
objectType: Item
|
||||
|
||||
@@ -19,6 +19,17 @@
|
||||
Steel: 300
|
||||
Plastic: 300
|
||||
|
||||
- type: latheRecipe
|
||||
id: RiotShield
|
||||
icon:
|
||||
sprite: Objects/Weapons/Melee/shields.rsi
|
||||
state: riot-icon
|
||||
result: RiotShield
|
||||
completetime: 4
|
||||
materials:
|
||||
Steel: 400
|
||||
Glass: 400
|
||||
|
||||
- type: latheRecipe
|
||||
id: Flash
|
||||
icon:
|
||||
|
||||
|
After Width: | Height: | Size: 786 B |
|
After Width: | Height: | Size: 844 B |
|
After Width: | Height: | Size: 890 B |
|
After Width: | Height: | Size: 171 B |
|
After Width: | Height: | Size: 339 B |
|
After Width: | Height: | Size: 834 B |
|
After Width: | Height: | Size: 351 B |
|
After Width: | Height: | Size: 462 B |
|
After Width: | Height: | Size: 833 B |
|
After Width: | Height: | Size: 488 B |
195
Resources/Textures/Objects/Weapons/Melee/shields.rsi/meta.json
Normal file
@@ -0,0 +1,195 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from https://github.com/Citadel-Station-13/Citadel-Station-13/commit/84223c65f5caf667a84f3c0f49bc2a41cdc6c4e3",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "riot-icon"
|
||||
},
|
||||
{
|
||||
"name": "riot-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "riot-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "riot_laser-icon"
|
||||
},
|
||||
{
|
||||
"name": "riot_laser-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "riot_laser-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "riot_bullet-icon"
|
||||
},
|
||||
{
|
||||
"name": "riot_bullet-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "riot_bullet-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "ratvarian-icon"
|
||||
},
|
||||
{
|
||||
"name": "ratvarian-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "ratvarian-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "mirror-icon",
|
||||
"delays": [
|
||||
[
|
||||
0.5,
|
||||
0.05,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "mirror-inhand-right",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
0.5,
|
||||
0.05,
|
||||
0.1,
|
||||
0.1
|
||||
],
|
||||
[
|
||||
0.5,
|
||||
0.05,
|
||||
0.1,
|
||||
0.1
|
||||
],
|
||||
[
|
||||
0.5,
|
||||
0.05,
|
||||
0.1,
|
||||
0.1
|
||||
],
|
||||
[
|
||||
0.5,
|
||||
0.05,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "mirror-inhand-left",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
0.5,
|
||||
0.05,
|
||||
0.1,
|
||||
0.1
|
||||
],
|
||||
[
|
||||
0.5,
|
||||
0.05,
|
||||
0.1,
|
||||
0.1
|
||||
],
|
||||
[
|
||||
0.5,
|
||||
0.05,
|
||||
0.1,
|
||||
0.1
|
||||
],
|
||||
[
|
||||
0.5,
|
||||
0.05,
|
||||
0.1,
|
||||
0.1
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "buckler-icon"
|
||||
},
|
||||
{
|
||||
"name": "buckler-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "buckler-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "metal-icon"
|
||||
},
|
||||
{
|
||||
"name": "metal-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "metal-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "makeshift-icon"
|
||||
},
|
||||
{
|
||||
"name": "teleriot-icon"
|
||||
},
|
||||
{
|
||||
"name": "teleriot-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "teleriot-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "teleriot-on"
|
||||
},
|
||||
{
|
||||
"name": "teleriot-inhand-right-on",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "teleriot-inhand-left-on",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "eshield-icon"
|
||||
},
|
||||
{
|
||||
"name": "eshield-inhand-right",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "eshield-inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "eshield-on"
|
||||
},
|
||||
{
|
||||
"name": "eshield-inhand-right-on",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "eshield1-inhand-left-on",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 205 B |
|
After Width: | Height: | Size: 606 B |
|
After Width: | Height: | Size: 628 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 502 B |
|
After Width: | Height: | Size: 914 B |
|
After Width: | Height: | Size: 885 B |
|
After Width: | Height: | Size: 383 B |
|
After Width: | Height: | Size: 847 B |
|
After Width: | Height: | Size: 863 B |
|
After Width: | Height: | Size: 496 B |
|
After Width: | Height: | Size: 1003 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 540 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 352 B |
|
After Width: | Height: | Size: 751 B |
|
After Width: | Height: | Size: 658 B |
|
After Width: | Height: | Size: 784 B |
|
After Width: | Height: | Size: 667 B |
|
After Width: | Height: | Size: 396 B |