Add power cell and weapon chargers (#430)

* Add power cell and weapon chargers

Functionality's similar to SS13. The cell charger won't show the discrete charging levels because effort to not make it spam appearance updates over the network.
There's some stuff I wasn't sure on:
1. Updating power? Particularly around fixing rounding
2. Duplicating the full and empty sprites as I couldn't figure out a way to not have AppearanceVisualizer throw if the state isn't found
3. I made a BaseCharger abstract class under the assumption that when a mech / borg charger is added it would also inherit from it.
4. GetText currently isn't localized

* Address PJB's feedback

Updated the BaseCharger
* Change nullref exception to invalidoperation
* If the user doesn't have hands it will just return instead
This commit is contained in:
metalgearsloth
2019-11-22 09:48:56 +11:00
committed by Pieter-Jan Briers
parent 94c00dda95
commit 58709d2d26
31 changed files with 975 additions and 0 deletions

View File

@@ -97,6 +97,8 @@ namespace Content.Client
"BallisticBullet", "BallisticBullet",
"HitscanWeaponCapacitor", "HitscanWeaponCapacitor",
"PowerCell", "PowerCell",
"WeaponCapacitorCharger",
"PowerCellCharger",
"AiController", "AiController",
"PlayerInputMover", "PlayerInputMover",
"Computer", "Computer",

View File

@@ -0,0 +1,77 @@
using Content.Shared.GameObjects.Components.Power;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Client.GameObjects.Components.Power
{
[UsedImplicitly]
public class PowerChargerVisualizer2D : AppearanceVisualizer
{
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
var sprite = entity.GetComponent<ISpriteComponent>();
// Base item
sprite.LayerMapSet(Layers.Base, sprite.AddLayerState("empty"));
// Light
sprite.LayerMapSet(Layers.Light, sprite.AddLayerState("light-off"));
sprite.LayerSetShader(Layers.Light, "unshaded");
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
var sprite = component.Owner.GetComponent<ISpriteComponent>();
// Update base item
if (component.TryGetData(CellVisual.Occupied, out bool occupied))
{
// TODO: don't throw if it doesn't have a full state
sprite.LayerSetState(Layers.Base, occupied ? "full" : "empty");
}
else
{
sprite.LayerSetState(Layers.Base, "empty");
}
// Update lighting
if (component.TryGetData(CellVisual.Light, out CellChargerStatus status))
{
switch (status)
{
case CellChargerStatus.Off:
sprite.LayerSetState(Layers.Light, "light-off");
break;
case CellChargerStatus.Empty:
sprite.LayerSetState(Layers.Light, "light-empty");
break;
case CellChargerStatus.Charging:
sprite.LayerSetState(Layers.Light, "light-charging");
break;
case CellChargerStatus.Charged:
sprite.LayerSetState(Layers.Light, "light-charged");
break;
default:
sprite.LayerSetState(Layers.Light, "light-off");
break;
}
}
else
{
sprite.LayerSetState(Layers.Light, "light-off");
}
}
enum Layers
{
Base,
Light,
}
}
}

View File

@@ -0,0 +1,153 @@
using System;
using Content.Shared.GameObjects.Components.Power;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Power.Chargers
{
public abstract class BaseCharger : Component
{
protected IEntity _heldItem;
protected ContainerSlot _container;
protected PowerDeviceComponent _powerDevice;
public CellChargerStatus Status => _status;
protected CellChargerStatus _status;
protected AppearanceComponent _appearanceComponent;
public abstract double CellChargePercent { get; }
// Powered items have their own charge rates, this is just a way to have chargers with different rates as well
public float TransferRatio => _transferRatio;
[ViewVariables]
protected float _transferRatio;
public float TransferEfficiency => _transferEfficiency;
[ViewVariables]
protected float _transferEfficiency;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _transferRatio, "transfer_ratio", 0.1f);
serializer.DataField(ref _transferEfficiency, "transfer_efficiency", 0.85f);
}
public override void Initialize()
{
base.Initialize();
_powerDevice = Owner.GetComponent<PowerDeviceComponent>();
if (_powerDevice == null)
{
var exc = new InvalidOperationException("Chargers requires a PowerDevice to function");
Logger.FatalS("charger", exc.Message);
throw exc;
}
_container =
ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-powerCellContainer", Owner);
_appearanceComponent = Owner.GetComponent<AppearanceComponent>();
// Default state in the visualizer is OFF, so when this gets powered on during initialization it will generally show empty
_powerDevice.OnPowerStateChanged += PowerUpdate;
}
/// <summary>
/// This will remove the item directly into the user's hand rather than the floor
/// </summary>
/// <param name="user"></param>
public void RemoveItemToHand(IEntity user)
{
var heldItem = _container.ContainedEntity;
if (heldItem == null)
{
return;
}
RemoveItem();
if (user.TryGetComponent(out HandsComponent handsComponent) &&
heldItem.TryGetComponent(out ItemComponent itemComponent))
{
handsComponent.PutInHand(itemComponent);
}
}
/// <summary>
/// Will put the charger's item on the floor if available
/// </summary>
public void RemoveItem()
{
if (_container.ContainedEntity == null)
{
return;
}
_container.Remove(_heldItem);
UpdateStatus();
}
protected void PowerUpdate(object sender, PowerStateEventArgs eventArgs)
{
UpdateStatus();
}
protected abstract CellChargerStatus GetStatus();
protected abstract void TransferPower(float frameTime);
protected void UpdateStatus()
{
// Not called UpdateAppearance just because it messes with the load
var status = GetStatus();
if (_status == status)
{
return;
}
_status = status;
switch (_status)
{
// Update load just in case
case CellChargerStatus.Off:
_powerDevice.Load = 0;
_appearanceComponent?.SetData(CellVisual.Light, CellChargerStatus.Off);
break;
case CellChargerStatus.Empty:
_powerDevice.Load = 0;
_appearanceComponent?.SetData(CellVisual.Light, CellChargerStatus.Empty); ;
break;
case CellChargerStatus.Charging:
_appearanceComponent?.SetData(CellVisual.Light, CellChargerStatus.Charging);
break;
case CellChargerStatus.Charged:
_powerDevice.Load = 0;
_appearanceComponent?.SetData(CellVisual.Light, CellChargerStatus.Charged);
break;
default:
throw new ArgumentOutOfRangeException();
}
_appearanceComponent?.SetData(CellVisual.Occupied, _container.ContainedEntity != null);
_status = status;
}
public void OnUpdate(float frameTime)
{
if (_status == CellChargerStatus.Empty || _status == CellChargerStatus.Charged ||
_container.ContainedEntity == null)
{
return;
}
TransferPower(frameTime);
}
}
}

View File

@@ -0,0 +1,192 @@
using System;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Power;
using Content.Shared.Interfaces;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
namespace Content.Server.GameObjects.Components.Power.Chargers
{
/// <summary>
/// This is used for the standalone cell rechargers (e.g. from a flashlight)
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(IAttackBy))]
public sealed class PowerCellChargerComponent : BaseCharger, IActivate, IAttackBy
{
public override string Name => "PowerCellCharger";
public override double CellChargePercent => _container.ContainedEntity != null ?
_container.ContainedEntity.GetComponent<PowerCellComponent>().Charge /
_container.ContainedEntity.GetComponent<PowerCellComponent>().Capacity * 100 : 0.0f;
public override void Initialize()
{
base.Initialize();
_powerDevice = Owner.GetComponent<PowerDeviceComponent>();
_container =
ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-powerCellContainer", Owner);
_appearanceComponent = Owner.GetComponent<AppearanceComponent>();
// Default state in the visualizer is OFF, so when this gets powered on during initialization it will generally show empty
_powerDevice.OnPowerStateChanged += PowerUpdate;
}
bool IAttackBy.AttackBy(AttackByEventArgs eventArgs)
{
var result = TryInsertItem(eventArgs.AttackWith);
if (result)
{
return true;
}
var localizationManager = IoCManager.Resolve<ILocalizationManager>();
eventArgs.User.PopupMessage(Owner, localizationManager.GetString("Unable to insert capacitor"));
return false;
}
void IActivate.Activate(ActivateEventArgs eventArgs)
{
RemoveItemToHand(eventArgs.User);
}
[Verb]
private sealed class InsertVerb : Verb<PowerCellChargerComponent>
{
protected override string GetText(IEntity user, PowerCellChargerComponent component)
{
if (!user.TryGetComponent(out HandsComponent handsComponent) || handsComponent.GetActiveHand == null)
{
return "Insert";
}
return $"Insert {handsComponent.GetActiveHand.Owner.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, PowerCellChargerComponent component)
{
if (!user.TryGetComponent(out HandsComponent handsComponent))
{
return VerbVisibility.Invisible;
}
if (component._container.ContainedEntity != null || handsComponent.GetActiveHand == null)
{
return VerbVisibility.Disabled;
}
return VerbVisibility.Visible;
}
protected override void Activate(IEntity user, PowerCellChargerComponent component)
{
if (!user.TryGetComponent(out HandsComponent handsComponent))
{
return;
}
if (handsComponent.GetActiveHand == null)
{
return;
}
var userItem = handsComponent.GetActiveHand.Owner;
handsComponent.Drop(userItem);
component.TryInsertItem(userItem);
}
}
[Verb]
private sealed class EjectVerb : Verb<PowerCellChargerComponent>
{
protected override string GetText(IEntity user, PowerCellChargerComponent component)
{
if (component._container.ContainedEntity == null)
{
return "Eject";
}
return $"Eject {component._container.ContainedEntity.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, PowerCellChargerComponent component)
{
if (component._container.ContainedEntity == null)
{
return VerbVisibility.Disabled;
}
return VerbVisibility.Visible;
}
protected override void Activate(IEntity user, PowerCellChargerComponent component)
{
component.RemoveItem();
}
}
public bool TryInsertItem(IEntity entity)
{
if (!entity.HasComponent<PowerCellComponent>() ||
_container.ContainedEntity != null)
{
return false;
}
_heldItem = entity;
if (!_container.Insert(_heldItem))
{
return false;
}
UpdateStatus();
return true;
}
protected override CellChargerStatus GetStatus()
{
if (!_powerDevice.Powered)
{
return CellChargerStatus.Off;
}
if (_container.ContainedEntity == null)
{
return CellChargerStatus.Empty;
}
if (_container.ContainedEntity.TryGetComponent(out PowerCellComponent component) &&
Math.Abs(component.Capacity - component.Charge) < 0.01)
{
return CellChargerStatus.Charged;
}
return CellChargerStatus.Charging;
}
protected override void TransferPower(float frameTime)
{
// Two numbers: One for how much power actually goes into the device (chargeAmount) and
// chargeLoss which is how much is drawn from the powernet
_container.ContainedEntity.TryGetComponent(out PowerCellComponent cellComponent);
var chargeLoss = cellComponent.RequestCharge(frameTime) * _transferRatio;
_powerDevice.Load = chargeLoss;
if (!_powerDevice.Powered)
{
// No power: Event should update to Off status
return;
}
var chargeAmount = chargeLoss * _transferEfficiency;
cellComponent.AddCharge(chargeAmount);
// Just so the sprite won't be set to 99.99999% visibility
if (cellComponent.Capacity - cellComponent.Charge < 0.01)
{
cellComponent.Charge = cellComponent.Capacity;
}
UpdateStatus();
}
}
}

View File

@@ -0,0 +1,183 @@
using System;
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Power;
using Content.Shared.Interfaces;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Power.Chargers
{
/// <summary>
/// This is used for the lasergun / flash rechargers
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(IActivate))]
[ComponentReference(typeof(IAttackBy))]
public sealed class WeaponCapacitorChargerComponent : BaseCharger, IActivate, IAttackBy
{
public override string Name => "WeaponCapacitorCharger";
public override double CellChargePercent => _container.ContainedEntity != null ?
_container.ContainedEntity.GetComponent<HitscanWeaponCapacitorComponent>().Charge /
_container.ContainedEntity.GetComponent<HitscanWeaponCapacitorComponent>().Capacity * 100 : 0.0f;
bool IAttackBy.AttackBy(AttackByEventArgs eventArgs)
{
var result = TryInsertItem(eventArgs.AttackWith);
if (!result)
{
var localizationManager = IoCManager.Resolve<ILocalizationManager>();
eventArgs.User.PopupMessage(Owner, localizationManager.GetString("Unable to insert capacitor"));
}
return result;
}
void IActivate.Activate(ActivateEventArgs eventArgs)
{
RemoveItemToHand(eventArgs.User);
}
[Verb]
private sealed class InsertVerb : Verb<WeaponCapacitorChargerComponent>
{
protected override string GetText(IEntity user, WeaponCapacitorChargerComponent component)
{
if (!user.TryGetComponent(out HandsComponent handsComponent) || handsComponent.GetActiveHand == null)
{
return "Insert";
}
return $"Insert {handsComponent.GetActiveHand.Owner.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, WeaponCapacitorChargerComponent component)
{
if (!user.TryGetComponent(out HandsComponent handsComponent))
{
return VerbVisibility.Invisible;
}
if (component._container.ContainedEntity != null || handsComponent.GetActiveHand == null)
{
return VerbVisibility.Disabled;
}
return VerbVisibility.Visible;
}
protected override void Activate(IEntity user, WeaponCapacitorChargerComponent component)
{
if (!user.TryGetComponent(out HandsComponent handsComponent))
{
return;
}
if (handsComponent.GetActiveHand == null)
{
return;
}
var userItem = handsComponent.GetActiveHand.Owner;
handsComponent.Drop(userItem);
component.TryInsertItem(userItem);
}
}
[Verb]
private sealed class EjectVerb : Verb<WeaponCapacitorChargerComponent>
{
protected override string GetText(IEntity user, WeaponCapacitorChargerComponent component)
{
if (component._container.ContainedEntity == null)
{
return "Eject";
}
return $"Eject {component._container.ContainedEntity.Name}";
}
protected override VerbVisibility GetVisibility(IEntity user, WeaponCapacitorChargerComponent component)
{
if (component._container.ContainedEntity == null)
{
return VerbVisibility.Disabled;
}
return VerbVisibility.Visible;
}
protected override void Activate(IEntity user, WeaponCapacitorChargerComponent component)
{
component.RemoveItem();
}
}
public bool TryInsertItem(IEntity entity)
{
if (!entity.HasComponent<HitscanWeaponCapacitorComponent>() ||
_container.ContainedEntity != null)
{
return false;
}
_heldItem = entity;
if (!_container.Insert(_heldItem))
{
return false;
}
UpdateStatus();
return true;
}
protected override CellChargerStatus GetStatus()
{
if (!_powerDevice.Powered)
{
return CellChargerStatus.Off;
}
if (_container.ContainedEntity == null)
{
return CellChargerStatus.Empty;
}
if (_container.ContainedEntity.TryGetComponent(out HitscanWeaponCapacitorComponent component) &&
Math.Abs(component.Capacity - component.Charge) < 0.01)
{
return CellChargerStatus.Charged;
}
return CellChargerStatus.Charging;
}
protected override void TransferPower(float frameTime)
{
// Two numbers: One for how much power actually goes into the device (chargeAmount) and
// chargeLoss which is how much is drawn from the powernet
_container.ContainedEntity.TryGetComponent(out HitscanWeaponCapacitorComponent weaponCapacitorComponent);
var chargeLoss = weaponCapacitorComponent.RequestCharge(frameTime) * _transferRatio;
_powerDevice.Load = chargeLoss;
if (!_powerDevice.Powered)
{
// No power: Event should update to Off status
return;
}
var chargeAmount = chargeLoss * _transferEfficiency;
weaponCapacitorComponent.AddCharge(chargeAmount);
// Just so the sprite won't be set to 99.99999% visibility
if (weaponCapacitorComponent.Capacity - weaponCapacitorComponent.Charge < 0.01)
{
weaponCapacitorComponent.Charge = weaponCapacitorComponent.Capacity;
}
UpdateStatus();
}
}
}

View File

@@ -0,0 +1,25 @@
using Content.Server.GameObjects.Components.Power.Chargers;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
internal class CellChargerSystem : EntitySystem
{
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(PowerCellChargerComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
{
var comp = entity.GetComponent<PowerCellChargerComponent>();
comp.OnUpdate(frameTime);
}
}
}
}

View File

@@ -0,0 +1,26 @@
using Content.Server.GameObjects.Components.Power;
using Content.Server.GameObjects.Components.Power.Chargers;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
internal class WeaponCapacitorChargerSystem : EntitySystem
{
public override void Initialize()
{
EntityQuery = new TypeEntityQuery(typeof(WeaponCapacitorChargerComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
{
var comp = entity.GetComponent<WeaponCapacitorChargerComponent>();
comp.OnUpdate(frameTime);
}
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Power
{
[Serializable, NetSerializable]
public enum CellChargerStatus
{
Off,
Empty,
Charging,
Charged,
}
[Serializable, NetSerializable]
public enum CellVisual
{
Occupied, // If there's an item in it
Light,
}
}

View File

@@ -94,3 +94,95 @@
visuals: visuals:
- type: PowerCellVisualizer2D - type: PowerCellVisualizer2D
prefix: s_hy prefix: s_hy
- type: entity
name: Cell Recharger
id: PowerCellRecharger
components:
- type: Sprite
netsync: false
sprite: Objects/Power/PowerCells/cell_recharger.rsi
drawdepth: Items
- type: PowerCellCharger
transfer_ratio: 0.10
transfer_efficiency: 0.85
- type: PowerDevice
priority: Low
- type: Icon
sprite: Objects/Power/PowerCells/cell_recharger.rsi
state: empty
- type: Appearance
visuals:
- type: PowerChargerVisualizer2D
- type: Wrenchable
- type: Physics
mass: 5
- type: Clickable
- type: Collidable
shapes:
- !type:PhysShapeAabb
bounds: "-0.25,-0.25,0.25,0.25"
mask: 19
layer: 16
IsScrapingFloor: true
- type: entity
name: Recharger
id: WeaponCapacitorRecharger
components:
- type: Sprite
netsync: false
sprite: Objects/Power/PowerCells/recharger.rsi
drawdepth: Items
- type: WeaponCapacitorCharger
transfer_ratio: 0.10
transfer_efficiency: 0.85
- type: PowerDevice
priority: Low
- type: Icon
sprite: Objects/Power/PowerCells/recharger.rsi
state: empty
- type: Appearance
visuals:
- type: PowerChargerVisualizer2D
- type: Wrenchable
- type: Physics
mass: 5
- type: Clickable
- type: Collidable
shapes:
- !type:PhysShapeAabb
bounds: "-0.25,-0.25,0.25,0.25"
mask: 19
layer: 16
IsScrapingFloor: true
- type: entity
name: Wall recharger
id: WallWeaponCapacitorRecharger
components:
- type: Sprite
netsync: false
sprite: Objects/Power/PowerCells/wall_recharger.rsi
drawdepth: Items
- type: WeaponCapacitorCharger
transfer_ratio: 0.15
transfer_efficiency: 0.95
- type: PowerDevice
priority: Low
- type: Icon
sprite: Objects/Power/PowerCells/wall_recharger.rsi
state: empty
- type: Appearance
visuals:
- type: PowerChargerVisualizer2D
- type: Physics
mass: 5
- type: Clickable
- type: Collidable
shapes:
- !type:PhysShapeAabb
bounds: "-0.25,-0.25,0.25,0.25"
mask: 19
layer: 16
IsScrapingFloor: true

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,59 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-3.0",
"copyright": "https://github.com/vgstation-coders/vgstation13/raw/78a32d846158a67dcd0ca2375e408fec298b46a0/icons/obj/power.dmi",
"states": [
{
"name": "light-off",
"directions": 1
},
{
"name": "empty",
"directions": 1
},
{
"name": "full",
"directions": 1
},
{
"name": "open",
"directions": 1
},
{
"name": "light-charging",
"directions": 1,
"delays": [
[
0.2,
0.2,
0.2,
0.2
]
]
},
{
"name": "light-charged",
"directions": 1,
"delays": [
[
0.5,
0.5
]
]
},
{
"name": "light-empty",
"directions": 1,
"delays": [
[
0.5,
0.5
]
]
},
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,74 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-3.0",
"copyright": "https://github.com/discordia-space/CEV-Eris/raw/9ea3eccbe22e18d24653949067f3d7dd12194ea9/icons/obj/stationobjs.dmi",
"states": [
{
"name": "empty",
"directions": 1,
"delays": [
[
1.0
]
]
},
{
"name": "full",
"directions": 1,
"delays": [
[
1.0
]
]
},
{
"name": "light-off",
"directions": 1,
"delays": [
[
1
]
]
},
{
"name": "light-empty",
"directions": 1,
"delays": [
[
1,
0.2
]
]
},
{
"name": "light-charging",
"directions": 1,
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1
]
]
},
{
"name": "light-charged",
"directions": 1,
"delays": [
[
0.2,
0.1
]
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,71 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-3.0",
"copyright": "https://github.com/discordia-space/CEV-Eris/raw/9ea3eccbe22e18d24653949067f3d7dd12194ea9/icons/obj/stationobjs.dmi",
"states": [
{
"name": "empty",
"directions": 1,
"delays": [
[
1.0
]
]
},
{
"name": "full",
"directions": 1,
"delays": [
[
1.0
]
]
},
{
"name": "light-off",
"directions": 1,
"delays": [
[
1
]
]
},
{
"name": "light-empty",
"directions": 1,
"delays": [
[
1,
0.2
]
]
},
{
"name": "light-charging",
"directions": 1,
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1
]
]
},
{
"name": "light-charged",
"directions": 1,
"delays": [
[
0.2,
0.1
]
]
}
]
}