[feat] Egun

This commit is contained in:
rhailrake
2023-04-28 02:10:32 +06:00
committed by Aviu00
parent 663cae3181
commit 72fa97798f
35 changed files with 425 additions and 15 deletions

View File

@@ -30,4 +30,6 @@ public enum GunVisualLayers : byte
BaseUnshaded,
Mag,
MagUnshaded,
TwoModeFirst,
TwoModeSecond
}

View File

@@ -251,14 +251,19 @@ public sealed partial class GunSystem
_ammoCount.Visible = true;
_ammoCount.Text = $"x{count:00}";
max = Math.Min(max, 8);
FillBulletRow(_bulletsList, count, max);
float step = 1;
if (max > 8)
{
step = ((float)max / 8);
}
FillBulletRow(_bulletsList, count, max, step);
}
private static void FillBulletRow(Control container, int count, int capacity)
private static void FillBulletRow(Control container, int count, int capacity, float step = 1)
{
var colorGone = Color.FromHex("#000000");
var color = Color.FromHex("#E00000");
int emptyNumber = 0;
// Draw the empty ones
for (var i = count; i < capacity; i++)

View File

@@ -14,6 +14,10 @@ public sealed partial class GunSystem
// Projectile
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, AmmoCounterControlEvent>(OnControl);
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, UpdateAmmoCounterEvent>(OnAmmoCountUpdate);
// TwoModeEnergy
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, AmmoCounterControlEvent>(OnControl);
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, UpdateAmmoCounterEvent>(OnAmmoCountUpdate);
}
private void OnAmmoCountUpdate(EntityUid uid, BatteryAmmoProviderComponent component, UpdateAmmoCounterEvent args)

View File

@@ -28,6 +28,18 @@ public sealed partial class GunSystem
sprite.LayerSetState(GunVisualLayers.MagUnshaded, $"{component.MagState}-unshaded-{component.MagSteps - 1}");
sprite.LayerSetVisible(GunVisualLayers.MagUnshaded, false);
}
if (sprite.LayerMapTryGet(GunVisualLayers.TwoModeFirst, out _))
{
sprite.LayerSetState(GunVisualLayers.TwoModeFirst, $"{component.MagState}-twomode1-{component.MagSteps - 1}");
sprite.LayerSetVisible(GunVisualLayers.TwoModeFirst, false);
}
if (sprite.LayerMapTryGet(GunVisualLayers.TwoModeSecond, out _))
{
sprite.LayerSetState(GunVisualLayers.TwoModeSecond, $"{component.MagState}-twomode2-{component.MagSteps - 1}");
sprite.LayerSetVisible(GunVisualLayers.TwoModeSecond, false);
}
}
private void OnMagazineVisualsChange(EntityUid uid, MagazineVisualsComponent component, ref AppearanceChangeEvent args)
@@ -67,6 +79,16 @@ public sealed partial class GunSystem
sprite.LayerSetVisible(GunVisualLayers.MagUnshaded, false);
}
if (sprite.LayerMapTryGet(GunVisualLayers.TwoModeFirst, out _))
{
sprite.LayerSetVisible(GunVisualLayers.TwoModeFirst, false);
}
if (sprite.LayerMapTryGet(GunVisualLayers.TwoModeSecond, out _))
{
sprite.LayerSetVisible(GunVisualLayers.TwoModeSecond, false);
}
return;
}
@@ -81,6 +103,27 @@ public sealed partial class GunSystem
sprite.LayerSetVisible(GunVisualLayers.MagUnshaded, true);
sprite.LayerSetState(GunVisualLayers.MagUnshaded, $"{component.MagState}-unshaded-{step}");
}
if (!args.AppearanceData.TryGetValue(AmmoVisuals.InStun, out var inStun) || inStun is true)
{
if (sprite.LayerMapTryGet(GunVisualLayers.TwoModeFirst, out var _))
{
sprite.LayerSetVisible(GunVisualLayers.TwoModeSecond, false);
sprite.LayerSetVisible(GunVisualLayers.TwoModeFirst, true);
sprite.LayerSetState(GunVisualLayers.TwoModeFirst, $"{component.MagState}-twomode1-{step}");
}
}
else if (inStun is false)
{
if (sprite.LayerMapTryGet(GunVisualLayers.TwoModeSecond, out var _))
{
sprite.LayerSetVisible(GunVisualLayers.TwoModeFirst, false);
sprite.LayerSetVisible(GunVisualLayers.TwoModeSecond, true);
sprite.LayerSetState(GunVisualLayers.TwoModeSecond, $"{component.MagState}-twomode2-{step}");
}
}
}
else
{
@@ -93,6 +136,16 @@ public sealed partial class GunSystem
{
sprite.LayerSetVisible(GunVisualLayers.MagUnshaded, false);
}
if (sprite.LayerMapTryGet(GunVisualLayers.TwoModeFirst, out _))
{
sprite.LayerSetVisible(GunVisualLayers.TwoModeFirst, false);
}
if (sprite.LayerMapTryGet(GunVisualLayers.TwoModeSecond, out _))
{
sprite.LayerSetVisible(GunVisualLayers.TwoModeSecond, false);
}
}
}
}

View File

@@ -2,6 +2,7 @@ using Content.Server.Power.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Events;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction.Events;
using Content.Shared.Projectiles;
using Content.Shared.Weapons.Ranged;
using Content.Shared.Weapons.Ranged.Components;
@@ -11,6 +12,8 @@ namespace Content.Server.Weapons.Ranged.Systems;
public sealed partial class GunSystem
{
[Dependency] private readonly SharedAudioSystem _audio = default!;
protected override void InitializeBattery()
{
base.InitializeBattery();
@@ -24,6 +27,41 @@ public sealed partial class GunSystem
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ComponentStartup>(OnBatteryStartup);
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ChargeChangedEvent>(OnBatteryChargeChange);
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, DamageExamineEvent>(OnBatteryDamageExamine);
//TwoModeEnergy
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, ComponentStartup>(OnBatteryStartup);
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, ChargeChangedEvent>(OnBatteryChargeChange);
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, DamageExamineEvent>(OnBatteryDamageExamine);
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, UseInHandEvent>(OnBatteryModeChange);
}
private void OnBatteryModeChange(EntityUid uid, TwoModeEnergyAmmoProviderComponent component, UseInHandEvent args)
{
if (!TryComp<GunComponent>(uid, out var gun))
return;
if (component.CurrentMode == EnergyModes.Stun)
{
component.InStun = false;
gun.SoundGunshot = component.HitscanSound;
component.CurrentMode = EnergyModes.Laser;
component.FireCost = component.HitscanFireCost;
_audio.PlayPvs(component.ToggleSound, args.User);
}
else if (component.CurrentMode == EnergyModes.Laser)
{
component.InStun = true;
gun.SoundGunshot = component.ProjSound;
component.CurrentMode = EnergyModes.Stun;
component.FireCost = component.ProjFireCost;
_audio.PlayPvs(component.ToggleSound, args.User);
}
UpdateShots(uid, component);
UpdateTwoModeAppearance(uid, component);
UpdateBatteryAppearance(uid, component);
UpdateAmmoCount(uid);
Dirty(gun);
Dirty(component);
}
private void OnBatteryStartup(EntityUid uid, BatteryAmmoProviderComponent component, ComponentStartup args)
@@ -66,12 +104,24 @@ public sealed partial class GunSystem
if (damageSpec == null)
return;
var damageType = component switch
string? damageType;
switch (component)
{
HitscanBatteryAmmoProviderComponent => Loc.GetString("damage-hitscan"),
ProjectileBatteryAmmoProviderComponent => Loc.GetString("damage-projectile"),
_ => throw new ArgumentOutOfRangeException(),
};
case HitscanBatteryAmmoProviderComponent:
damageType = Loc.GetString("damage-hitscan");
break;
case ProjectileBatteryAmmoProviderComponent:
damageType = Loc.GetString("damage-projectile");
break;
case TwoModeEnergyAmmoProviderComponent twoMode:
if (twoMode.CurrentMode == EnergyModes.Stun)
damageType = Loc.GetString("damage-projectile");
else
damageType = Loc.GetString("damage-hitscan");
break;
default:
throw new ArgumentOutOfRangeException();
}
_damageExamine.AddDamageExamine(args.Message, damageSpec, damageType);
}
@@ -99,6 +149,25 @@ public sealed partial class GunSystem
return ProtoManager.Index<HitscanPrototype>(hitscan.Prototype).Damage;
}
if (component is TwoModeEnergyAmmoProviderComponent twoMode)
{
if (twoMode.CurrentMode == EnergyModes.Stun)
{
if (ProtoManager.Index<EntityPrototype>(twoMode.ProjectilePrototype).Components
.TryGetValue(_factory.GetComponentName(typeof(ProjectileComponent)), out var projectile))
{
var p = (ProjectileComponent) projectile.Component;
if (p.Damage.Total > FixedPoint2.Zero)
{
return p.Damage;
}
}
return null;
}
return ProtoManager.Index<HitscanPrototype>(twoMode.HitscanPrototype).Damage;
}
return null;
}

View File

@@ -16,7 +16,7 @@ public partial class GunComponent : Component
#region Sound
[ViewVariables(VVAccess.ReadWrite), DataField("soundGunshot")]
public SoundSpecifier? SoundGunshot = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/smg.ogg");
public SoundSpecifier? SoundGunshot { get; set; } = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/smg.ogg");
[ViewVariables(VVAccess.ReadWrite), DataField("soundEmpty")]
public SoundSpecifier? SoundEmpty = new SoundPathSpecifier("/Audio/Weapons/Guns/Empty/empty.ogg");

View File

@@ -0,0 +1,43 @@
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Weapons.Ranged.Components;
[RegisterComponent, NetworkedComponent]
public sealed class TwoModeEnergyAmmoProviderComponent : BatteryAmmoProviderComponent
{
[ViewVariables(VVAccess.ReadOnly),
DataField("projProto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string ProjectilePrototype = default!;
[ViewVariables(VVAccess.ReadOnly),
DataField("hitscanProto", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<HitscanPrototype>))]
public string HitscanPrototype = default!;
[ViewVariables(VVAccess.ReadOnly), DataField("projFireCost")]
public float ProjFireCost = 50;
[ViewVariables(VVAccess.ReadOnly), DataField("hitscanFireCost")]
public float HitscanFireCost = 100;
[ViewVariables(VVAccess.ReadOnly), DataField("currentMode")]
public EnergyModes CurrentMode { get; set; } = EnergyModes.Stun;
[ViewVariables(VVAccess.ReadOnly), DataField("projSound")]
public SoundSpecifier? ProjSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/taser2.ogg");
[ViewVariables(VVAccess.ReadOnly), DataField("hitscanSound")]
public SoundSpecifier? HitscanSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/laser_cannon.ogg");
public SoundSpecifier? ToggleSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Misc/egun_toggle.ogg");
[ViewVariables(VVAccess.ReadOnly)] public bool InStun = true;
}
public enum EnergyModes
{
Stun,
Laser
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.Examine;
using Content.Shared.Item;
using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Events;
using Robust.Shared.GameStates;
@@ -9,6 +10,8 @@ namespace Content.Shared.Weapons.Ranged.Systems;
public abstract partial class SharedGunSystem
{
[Dependency] private readonly SharedItemSystem _item = default!;
protected virtual void InitializeBattery()
{
// Trying to dump comp references hence the below
@@ -25,6 +28,62 @@ public abstract partial class SharedGunSystem
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, TakeAmmoEvent>(OnBatteryTakeAmmo);
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, GetAmmoCountEvent>(OnBatteryAmmoCount);
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ExaminedEvent>(OnBatteryExamine);
// TwoModeEnergy
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, ComponentInit>(OnTwoModeInit);
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, ComponentGetState>(OnBatteryTwoModeGetState);
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, ComponentHandleState>(OnBatteryTwoModeHandleState);
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, TakeAmmoEvent>(OnBatteryTakeAmmo);
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, GetAmmoCountEvent>(OnBatteryAmmoCount);
SubscribeLocalEvent<TwoModeEnergyAmmoProviderComponent, ExaminedEvent>(OnBatteryExamine);
}
private void OnTwoModeInit(EntityUid uid, TwoModeEnergyAmmoProviderComponent component, ComponentInit args)
{
if (!Timing.IsFirstTimePredicted || !TryComp<AppearanceComponent>(component.Owner, out var appearance)) return;
Appearance.SetData(appearance.Owner, AmmoVisuals.InStun, component.InStun, appearance);
}
private void OnBatteryTwoModeHandleState(EntityUid uid, TwoModeEnergyAmmoProviderComponent component, ref ComponentHandleState args)
{
if (args.Current is not TwoModeComponentState state)
return;
component.Shots = state.Shots;
component.Capacity = state.MaxShots;
component.FireCost = state.FireCost;
component.CurrentMode = state.CurrentMode;
component.InStun = state.InStun;
}
private void OnBatteryTwoModeGetState(EntityUid uid, TwoModeEnergyAmmoProviderComponent component, ref ComponentGetState args)
{
args.State = new TwoModeComponentState()
{
Shots = component.Shots,
MaxShots = component.Capacity,
FireCost = component.FireCost,
CurrentMode = component.CurrentMode,
InStun = component.InStun
};
}
protected void UpdateTwoModeAppearance(EntityUid uid, TwoModeEnergyAmmoProviderComponent component)
{
if (!TryComp<AppearanceComponent>(uid, out var appearance))
return;
if (!TryComp<ItemComponent?>(uid, out var item))
return;
if (component.InStun)
_item.SetHeldPrefix(uid, null, item);
else
_item.SetHeldPrefix(uid, "laser", item);
Appearance.SetData(uid, AmmoVisuals.InStun, component.InStun, appearance);
}
private void OnBatteryHandleState(EntityUid uid, BatteryAmmoProviderComponent component, ref ComponentHandleState args)
@@ -101,6 +160,13 @@ public abstract partial class SharedGunSystem
return (ent, EnsureShootable(ent));
case HitscanBatteryAmmoProviderComponent hitscan:
return (null, ProtoManager.Index<HitscanPrototype>(hitscan.Prototype));
case TwoModeEnergyAmmoProviderComponent twoMode:
if (twoMode.CurrentMode == EnergyModes.Stun)
{
var projEnt = Spawn(twoMode.ProjectilePrototype, coordinates);
return (projEnt, EnsureComp<AmmoComponent>(projEnt));
}
return (null, ProtoManager.Index<HitscanPrototype>(twoMode.HitscanPrototype));
default:
throw new ArgumentOutOfRangeException();
}
@@ -113,4 +179,14 @@ public abstract partial class SharedGunSystem
public int MaxShots;
public float FireCost;
}
[Serializable, NetSerializable]
public sealed class TwoModeComponentState : ComponentState
{
public EnergyModes CurrentMode { get; init; }
public int Shots;
public int MaxShots;
public float FireCost;
public bool InStun;
}
}

View File

@@ -20,9 +20,20 @@ public abstract partial class SharedGunSystem
("mode", GetLocSelector(component.SelectedMode))));
args.PushMarkup(Loc.GetString("gun-fire-rate-examine", ("color", FireRateExamineColor),
("fireRate", $"{component.FireRate:0.0}")));
if (!TryComp<TwoModeEnergyAmmoProviderComponent>(uid, out var comp))
return;
args.PushMarkup(Loc.GetString("gun-twomode-mode-examine", ("color", TwoModeExamineColor),
("mode", GetLocMode(comp.CurrentMode))));
}
}
private object GetLocMode(EnergyModes mode)
{
return Loc.GetString($"gun-twomode-{mode.ToString()}");
}
private string GetLocSelector(SelectiveFire mode)
{
return Loc.GetString($"gun-{mode.ToString()}");

View File

@@ -67,6 +67,7 @@ public abstract partial class SharedGunSystem : EntitySystem
protected const string AmmoExamineColor = "yellow";
protected const string FireRateExamineColor = "yellow";
protected const string ModeExamineColor = "cyan";
protected const string TwoModeExamineColor = "red";
public override void Initialize()
{
@@ -501,4 +502,5 @@ public enum AmmoVisuals : byte
HasAmmo, // used for generic visualizers. c# stuff can just check ammocount != 0
MagLoaded,
BoltClosed,
InStun
}

Binary file not shown.

View File

@@ -57,3 +57,13 @@
cost: 5200
category: Armory
group: market
- type: cargoProduct
id: ArmoryEgun
icon:
sprite: Objects/Weapons/Guns/Battery/egun.rsi
state: base
product: CrateArmoryEgun
cost: 2500
category: Armory
group: market

View File

@@ -57,3 +57,12 @@
amount: 2
- id: MagazinePistol
amount: 4
- type: entity
id: CrateArmoryEgun
parent: CrateWeaponSecure
components:
- type: StorageFill
contents:
- id: WeaponEgun
amount: 3

View File

@@ -31,7 +31,7 @@
- id: CaptainIDCard
- id: ClothingOuterHardsuitCap
- id: ClothingMaskGasCaptain
- id: WeaponDisabler
- id: WeaponEgun
- id: CommsComputerCircuitboard
- id: ClothingHeadsetAltCommand
- id: SpaceCash1000
@@ -85,7 +85,7 @@
- id: BoxID
- id: BoxHeadset
- id: IDComputerCircuitboard
- id: WeaponDisabler
- id: WeaponEgun
- id: CigarGoldCase
prob: 0.25
# Fuck the HoP they don't deserve fucking cigars.
@@ -258,7 +258,10 @@
- type: StorageFill
contents:
- id: ClothingEyesHudSecurity
- id: WeaponDisabler
- id: WeaponEgun
- id: ClothingHeadHatBeretHoS
- id: ClothingHeadHatHoshat
- id: ClothingNeckCloakHos
- id: ClothingOuterCoatHoSTrench
- id: ClothingBeltSecurityFilled
- id: ClothingHeadsetAltSecurity

View File

@@ -30,8 +30,9 @@
- type: StorageFill
contents:
- id: FlashlightSeclite
- id: WeaponDisabler
prob: 0.3
- id: WeaponEgun
- id: ClothingHeadHatWarden
- id: ClothingHeadHatBeretWarden
- id: ClothingBeltSecurityFilled
- id: Flash
- id: ClothingEyesGlassesSunglasses
@@ -68,6 +69,7 @@
- id: WeaponMeleeNeedle
prob: 0.1
- id: ClothingEyesHudSecurity
- id: WeaponEgun
- type: entity
id: LockerBrigmedicFilled
@@ -103,6 +105,7 @@
prob: 0.7
- id: ClothingNeckCloakMoth #bzzz Moth-pocalypse
prob: 0.15
- id: WeaponEgun
- type: entity
id: LockerDetectiveFilled

View File

@@ -355,7 +355,7 @@
walkModifier: 0.6
sprintModifier: 0.6
- type: HeldSpeedModifier
- type: Sprite
- type: Sprite
sprite: Objects/Weapons/Guns/Battery/particle_decelerator.rsi
layers:
- state: base
@@ -695,3 +695,41 @@
- type: Appearance
- type: StaticPrice
price: 750
- type: entity
name: egun
parent: BaseWeaponBattery
id: WeaponEgun
description: Egun
components:
- type: Sprite
sprite: Objects/Weapons/Guns/Battery/egun.rsi
layers:
- state: base
map: ["enum.GunVisualLayers.Base"]
- state: mag-twomode1-4
map: ["enum.GunVisualLayers.TwoModeFirst"]
shader: unshaded
- state: mag-twomode2-4
map: ["enum.GunVisualLayers.TwoModeSecond"]
shader: unshaded
- type: Clothing
sprite: Objects/Weapons/Guns/Battery/egun.rsi
- type: Gun
soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/taser2.ogg
- type: TwoModeEnergyAmmoProvider
projProto: BulletDisabler
fireCost: 50
projFireCost: 50
hitscanProto: RedLaser
hitscanFireCost: 100
projSound: "/Audio/Weapons/Guns/Gunshots/taser2.ogg"
hitscanSound: "/Audio/Weapons/Guns/Gunshots/laser_cannon.ogg"
- type: MagazineVisuals
magState: mag
steps: 5
zeroVisible: true
- type: Appearance
- type: StaticPrice
price: 800

View File

@@ -108,6 +108,7 @@
- HitscanBatteryAmmoProvider
- ProjectileBatteryAmmoProvider
- Stunbaton
- TwoModeEnergyAmmoProvider
- type: entity
parent: BaseItemRecharger
@@ -125,6 +126,7 @@
- type: WallMount
- type: Charger
chargeRate: 25
slotId: charger_slot
- type: ItemSlots
slots:
charger_slot:
@@ -134,6 +136,7 @@
- HitscanBatteryAmmoProvider
- ProjectileBatteryAmmoProvider
- Stunbaton
- TwoModeEnergyAmmoProvider
- type: entity
parent: BaseRecharger

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

View File

@@ -0,0 +1,79 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from White at https://github.com/frosty-dev/white",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "base"
},
{
"name": "mag-twomode1-0",
"delays": [
[
0.3,
0.3
]
]
},
{
"name": "mag-twomode1-1"
},
{
"name": "mag-twomode1-2"
},
{
"name": "mag-twomode1-3"
},
{
"name": "mag-twomode1-4"
},
{
"name": "mag-twomode2-0",
"delays": [
[
0.3,
0.3
]
]
},
{
"name": "mag-twomode2-1"
},
{
"name": "mag-twomode2-2"
},
{
"name": "mag-twomode2-3"
},
{
"name": "mag-twomode2-4"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
},
{
"name": "laser-inhand-left",
"directions": 4
},
{
"name": "laser-inhand-right",
"directions": 4
},
{
"name": "equipped-BACKPACK",
"directions": 4
}
]
}