diff --git a/Content.Server/AI/Operators/Combat/SwingMeleeWeaponOperator.cs b/Content.Server/AI/Operators/Combat/Melee/SwingMeleeWeaponOperator.cs similarity index 86% rename from Content.Server/AI/Operators/Combat/SwingMeleeWeaponOperator.cs rename to Content.Server/AI/Operators/Combat/Melee/SwingMeleeWeaponOperator.cs index d4c5ade0d6..38065d5973 100644 --- a/Content.Server/AI/Operators/Combat/SwingMeleeWeaponOperator.cs +++ b/Content.Server/AI/Operators/Combat/Melee/SwingMeleeWeaponOperator.cs @@ -5,7 +5,7 @@ using Content.Server.GameObjects.EntitySystems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; -namespace Content.Server.AI.Operators.Combat +namespace Content.Server.AI.Operators.Combat.Melee { public class SwingMeleeWeaponOperator : AiOperator { @@ -41,6 +41,15 @@ namespace Content.Server.AI.Operators.Combat return true; } + + public override void Shutdown(Outcome outcome) + { + base.Shutdown(outcome); + if (_owner.TryGetComponent(out CombatModeComponent combatModeComponent)) + { + combatModeComponent.IsInCombatMode = false; + } + } public override Outcome Execute(float frameTime) { @@ -70,5 +79,4 @@ namespace Content.Server.AI.Operators.Combat return Outcome.Continuing; } } - } diff --git a/Content.Server/AI/Operators/Combat/Melee/UnarmedCombatOperator.cs b/Content.Server/AI/Operators/Combat/Melee/UnarmedCombatOperator.cs new file mode 100644 index 0000000000..5fc0332351 --- /dev/null +++ b/Content.Server/AI/Operators/Combat/Melee/UnarmedCombatOperator.cs @@ -0,0 +1,88 @@ +using Content.Server.GameObjects; +using Content.Server.GameObjects.Components.Mobs; +using Content.Server.GameObjects.Components.Weapon.Melee; +using Content.Server.GameObjects.EntitySystems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Server.AI.Operators.Combat.Melee +{ + public sealed class UnarmedCombatOperator : AiOperator + { + private float _burstTime; + private float _elapsedTime; + + private readonly IEntity _owner; + private readonly IEntity _target; + private UnarmedCombatComponent _unarmedCombat; + + public UnarmedCombatOperator(IEntity owner, IEntity target, float burstTime = 1.0f) + { + _owner = owner; + _target = target; + _burstTime = burstTime; + } + + public override bool TryStartup() + { + if (!base.TryStartup()) + { + return true; + } + + if (!_owner.TryGetComponent(out CombatModeComponent combatModeComponent)) + { + return false; + } + + if (!combatModeComponent.IsInCombatMode) + { + combatModeComponent.IsInCombatMode = true; + } + + if (_owner.TryGetComponent(out UnarmedCombatComponent unarmedCombatComponent)) + { + _unarmedCombat = unarmedCombatComponent; + } + else + { + return false; + } + + return true; + } + + public override void Shutdown(Outcome outcome) + { + base.Shutdown(outcome); + if (_owner.TryGetComponent(out CombatModeComponent combatModeComponent)) + { + combatModeComponent.IsInCombatMode = false; + } + } + + public override Outcome Execute(float frameTime) + { + if (_burstTime <= _elapsedTime) + { + return Outcome.Success; + } + + if (_unarmedCombat.Deleted) + { + return Outcome.Failed; + } + + if ((_target.Transform.GridPosition.Position - _owner.Transform.GridPosition.Position).Length > + _unarmedCombat.Range) + { + return Outcome.Failed; + } + + var interactionSystem = IoCManager.Resolve().GetEntitySystem(); + interactionSystem.UseItemInHand(_owner, _target.Transform.GridPosition, _target.Uid); + _elapsedTime += frameTime; + return Outcome.Continuing; + } + } +} \ No newline at end of file diff --git a/Content.Server/AI/Utility/Actions/Combat/Melee/MeleeAttackEntity.cs b/Content.Server/AI/Utility/Actions/Combat/Melee/MeleeWeaponAttackEntity.cs similarity index 93% rename from Content.Server/AI/Utility/Actions/Combat/Melee/MeleeAttackEntity.cs rename to Content.Server/AI/Utility/Actions/Combat/Melee/MeleeWeaponAttackEntity.cs index 25f329fdc1..2da20091a1 100644 --- a/Content.Server/AI/Utility/Actions/Combat/Melee/MeleeAttackEntity.cs +++ b/Content.Server/AI/Utility/Actions/Combat/Melee/MeleeWeaponAttackEntity.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Content.Server.AI.Operators; using Content.Server.AI.Operators.Combat; +using Content.Server.AI.Operators.Combat.Melee; using Content.Server.AI.Operators.Movement; using Content.Server.AI.Utility.Considerations; using Content.Server.AI.Utility.Considerations.Combat; @@ -17,11 +18,11 @@ using Robust.Shared.Interfaces.GameObjects; namespace Content.Server.AI.Utility.Actions.Combat.Melee { - public sealed class MeleeAttackEntity : UtilityAction + public sealed class MeleeWeaponAttackEntity : UtilityAction { private IEntity _entity; - public MeleeAttackEntity(IEntity owner, IEntity entity, float weight) : base(owner) + public MeleeWeaponAttackEntity(IEntity owner, IEntity entity, float weight) : base(owner) { _entity = entity; Bonus = weight; diff --git a/Content.Server/AI/Utility/Actions/Combat/Melee/UnarmedAttackEntity.cs b/Content.Server/AI/Utility/Actions/Combat/Melee/UnarmedAttackEntity.cs new file mode 100644 index 0000000000..6883b294bf --- /dev/null +++ b/Content.Server/AI/Utility/Actions/Combat/Melee/UnarmedAttackEntity.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using Content.Server.AI.Operators; +using Content.Server.AI.Operators.Combat.Melee; +using Content.Server.AI.Operators.Movement; +using Content.Server.AI.Utility.Considerations; +using Content.Server.AI.Utility.Considerations.Combat; +using Content.Server.AI.Utility.Considerations.Combat.Melee; +using Content.Server.AI.Utility.Considerations.Movement; +using Content.Server.AI.Utility.Curves; +using Content.Server.AI.WorldState; +using Content.Server.AI.WorldState.States; +using Content.Server.AI.WorldState.States.Combat; +using Content.Server.AI.WorldState.States.Movement; +using Content.Server.GameObjects.Components.Weapon.Melee; +using Robust.Shared.Interfaces.GameObjects; + +namespace Content.Server.AI.Utility.Actions.Combat.Melee +{ + public sealed class UnarmedAttackEntity : UtilityAction + { + private IEntity _entity; + + public UnarmedAttackEntity(IEntity owner, IEntity entity, float weight) : base(owner) + { + _entity = entity; + Bonus = weight; + } + + public override void SetupOperators(Blackboard context) + { + MoveToEntityOperator moveOperator; + if (Owner.TryGetComponent(out UnarmedCombatComponent unarmedCombatComponent)) + { + moveOperator = new MoveToEntityOperator(Owner, _entity, unarmedCombatComponent.Range - 0.01f); + } + // I think it's possible for this to happen given planning is time-sliced? + // TODO: At this point we should abort + else + { + moveOperator = new MoveToEntityOperator(Owner, _entity); + } + + ActionOperators = new Queue(new AiOperator[] + { + moveOperator, + new UnarmedCombatOperator(Owner, _entity), + }); + } + + protected override void UpdateBlackboard(Blackboard context) + { + base.UpdateBlackboard(context); + context.GetState().SetValue(_entity); + context.GetState().SetValue(_entity); + // Can just set ourselves as entity given unarmed just inherits from meleeweapon + context.GetState().SetValue(Owner); + } + + protected override Consideration[] Considerations { get; } = { + new CanUnarmedCombatCon( + new BoolCurve()), + // Don't attack a dead target + new TargetIsDeadCon( + new InverseBoolCurve()), + // Deprioritise a target in crit + new TargetIsCritCon( + new QuadraticCurve(-0.8f, 1.0f, 1.0f, 0.0f)), + // Somewhat prioritise distance + new DistanceCon( + new QuadraticCurve(-1.0f, 1.0f, 1.02f, 0.0f)), + // Prefer weaker targets + new TargetHealthCon( + new QuadraticCurve(1.0f, 0.4f, 0.0f, -0.02f)), + // TODO: Consider our Speed and Damage to compare this to using a weapon + // Also need to unequip our weapon if we have one (xenos can't hold one so no issue for now) + }; + } +} \ No newline at end of file diff --git a/Content.Server/AI/Utility/AiLogic/Mimic.cs b/Content.Server/AI/Utility/AiLogic/Mimic.cs new file mode 100644 index 0000000000..bc8ecb9f28 --- /dev/null +++ b/Content.Server/AI/Utility/AiLogic/Mimic.cs @@ -0,0 +1,18 @@ +using Content.Server.AI.Utility.BehaviorSets; +using JetBrains.Annotations; +using Robust.Server.AI; + +namespace Content.Server.AI.Utility.AiLogic +{ + [AiLogicProcessor("Mimic")] + [UsedImplicitly] + public sealed class Mimic : UtilityAi + { + public override void Setup() + { + base.Setup(); + AddBehaviorSet(new UnarmedAttackPlayersBehaviorSet(SelfEntity), false); + SortActions(); + } + } +} \ No newline at end of file diff --git a/Content.Server/AI/Utility/AiLogic/Xeno.cs b/Content.Server/AI/Utility/AiLogic/Xeno.cs new file mode 100644 index 0000000000..b658d0cfb0 --- /dev/null +++ b/Content.Server/AI/Utility/AiLogic/Xeno.cs @@ -0,0 +1,19 @@ +using Content.Server.AI.Utility.BehaviorSets; +using JetBrains.Annotations; +using Robust.Server.AI; + +namespace Content.Server.AI.Utility.AiLogic +{ + [AiLogicProcessor("Xeno")] + [UsedImplicitly] + public sealed class Xeno : UtilityAi + { + public override void Setup() + { + base.Setup(); + AddBehaviorSet(new IdleBehaviorSet(SelfEntity), false); + AddBehaviorSet(new UnarmedAttackPlayersBehaviorSet(SelfEntity), false); + SortActions(); + } + } +} \ No newline at end of file diff --git a/Content.Server/AI/Utility/BehaviorSets/UnarmedAttackPlayersBehaviorSet.cs b/Content.Server/AI/Utility/BehaviorSets/UnarmedAttackPlayersBehaviorSet.cs new file mode 100644 index 0000000000..6b479b87e9 --- /dev/null +++ b/Content.Server/AI/Utility/BehaviorSets/UnarmedAttackPlayersBehaviorSet.cs @@ -0,0 +1,17 @@ +using Content.Server.AI.Utility.Actions; +using Content.Server.AI.Utility.ExpandableActions.Combat.Melee; +using Robust.Shared.Interfaces.GameObjects; + +namespace Content.Server.AI.Utility.BehaviorSets +{ + public sealed class UnarmedAttackPlayersBehaviorSet : BehaviorSet + { + public UnarmedAttackPlayersBehaviorSet(IEntity owner) : base(owner) + { + Actions = new IAiUtility[] + { + new UnarmedAttackNearbyPlayerExp(), + }; + } + } +} \ No newline at end of file diff --git a/Content.Server/AI/Utility/Considerations/Combat/Melee/CanUnarmedCombatCon.cs b/Content.Server/AI/Utility/Considerations/Combat/Melee/CanUnarmedCombatCon.cs new file mode 100644 index 0000000000..f77e6ce5c4 --- /dev/null +++ b/Content.Server/AI/Utility/Considerations/Combat/Melee/CanUnarmedCombatCon.cs @@ -0,0 +1,17 @@ +using Content.Server.AI.Utility.Curves; +using Content.Server.AI.WorldState; +using Content.Server.AI.WorldState.States; +using Content.Server.GameObjects.Components.Weapon.Melee; + +namespace Content.Server.AI.Utility.Considerations.Combat.Melee +{ + public sealed class CanUnarmedCombatCon : Consideration + { + public CanUnarmedCombatCon(IResponseCurve curve) : base(curve) {} + + public override float GetScore(Blackboard context) + { + return context.GetState().GetValue().HasComponent() ? 1.0f : 0.0f; + } + } +} \ No newline at end of file diff --git a/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/MeleeAttackNearbyPlayerExp.cs b/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/MeleeAttackNearbyPlayerExp.cs index e70dba6c86..adab7f24cd 100644 --- a/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/MeleeAttackNearbyPlayerExp.cs +++ b/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/MeleeAttackNearbyPlayerExp.cs @@ -28,7 +28,7 @@ namespace Content.Server.AI.Utility.ExpandableActions.Combat.Melee { if (entity.HasComponent() && entity != owner) { - yield return new MeleeAttackEntity(owner, entity, Bonus); + yield return new MeleeWeaponAttackEntity(owner, entity, Bonus); } } } diff --git a/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/MeleeAttackNearbySpeciesExp.cs b/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/MeleeAttackNearbySpeciesExp.cs index ad00223233..617252184e 100644 --- a/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/MeleeAttackNearbySpeciesExp.cs +++ b/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/MeleeAttackNearbySpeciesExp.cs @@ -16,7 +16,7 @@ namespace Content.Server.AI.Utility.ExpandableActions.Combat.Melee var owner = context.GetState().GetValue(); foreach (var entity in context.GetState().GetValue()) { - yield return new MeleeAttackEntity(owner, entity, Bonus); + yield return new MeleeWeaponAttackEntity(owner, entity, Bonus); } } } diff --git a/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/UnarmedAttackNearbyPlayerExp.cs b/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/UnarmedAttackNearbyPlayerExp.cs new file mode 100644 index 0000000000..e168116431 --- /dev/null +++ b/Content.Server/AI/Utility/ExpandableActions/Combat/Melee/UnarmedAttackNearbyPlayerExp.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using Content.Server.AI.Utility.Actions; +using Content.Server.AI.Utility.Actions.Combat.Melee; +using Content.Server.AI.Utils; +using Content.Server.AI.WorldState; +using Content.Server.AI.WorldState.States; +using Content.Server.GameObjects; +using Content.Server.GameObjects.Components.Movement; +using Robust.Server.GameObjects; + +namespace Content.Server.AI.Utility.ExpandableActions.Combat.Melee +{ + public sealed class UnarmedAttackNearbyPlayerExp : ExpandableUtilityAction + { + public override float Bonus => UtilityAction.CombatBonus; + + public override IEnumerable GetActions(Blackboard context) + { + var owner = context.GetState().GetValue(); + if (!owner.TryGetComponent(out AiControllerComponent controller)) + { + throw new InvalidOperationException(); + } + + foreach (var entity in Visibility.GetEntitiesInRange(owner.Transform.GridPosition, typeof(SpeciesComponent), + controller.VisionRadius)) + { + if (entity.HasComponent() && entity != owner) + { + yield return new UnarmedAttackEntity(owner, entity, Bonus); + } + } + } + } +} \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Effects/weapon_arc.yml b/Resources/Prototypes/Entities/Effects/weapon_arc.yml index 2653321b72..10e70bb4fd 100644 --- a/Resources/Prototypes/Entities/Effects/weapon_arc.yml +++ b/Resources/Prototypes/Entities/Effects/weapon_arc.yml @@ -12,6 +12,8 @@ - type: entity id: WeaponTGArc + save: false + abstract: true parent: WeaponArc components: - type: Sprite diff --git a/Resources/Prototypes/Entities/Mobs/npcs.yml b/Resources/Prototypes/Entities/Mobs/NPCs/human.yml similarity index 100% rename from Resources/Prototypes/Entities/Mobs/npcs.yml rename to Resources/Prototypes/Entities/Mobs/NPCs/human.yml diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml b/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml new file mode 100644 index 0000000000..3ecbbd0322 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml @@ -0,0 +1,59 @@ +# Hacky for the stress test so don't even consider adding to this +- type: entity + save: false + name: Mimic + id: MimicMob_Content + description: Surprise. # When this gets a proper write this should use the object's actual description >:) + drawdepth: Mobs + suffix: AI + components: + - type: AiController + logic: Mimic + - type: Hands + hands: + - left + - right + - type: MovementSpeedModifier + - type: InteractionOutline + - type: Sprite + netsync: false + drawdepth: Mobs + sprite: Buildings/VendingMachines/cola.rsi + state: normal + - type: Icon + sprite: Buildings/VendingMachines/cola.rsi + state: normal + - type: Physics + mass: 85 + - type: Collidable + shapes: + - !type:PhysShapeAabb + bounds: "-0.35,-0.35,0.35,0.35" + mask: + - Impassable + - MobImpassable + - VaultImpassable + - SmallImpassable + layer: + - Opaque + - MobImpassable + - type: Species + Template: Human + HeatResistance: 323 + - type: BodyManager + BaseTemplate: bodyTemplate.Humanoid + BasePreset: bodyPreset.BasicHuman + - type: HeatResistance + - type: Damageable + - type: CombatMode + - type: Teleportable + - type: CharacterInfo + - type: FootstepSound + - type: HumanoidAppearance + - type: Stunnable + - type: AnimationPlayer + - type: UnarmedCombat + range: 1.5 + arcwidth: 0 + arc: fist + damage: 90 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml new file mode 100644 index 0000000000..6203b61bb2 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -0,0 +1,59 @@ +# Hacky for the stress test so don't even consider adding to this +- type: entity + save: false + name: Xeno + id: XenoMob_Content + description: They mostly come at night. Mostly. + drawdepth: Mobs + suffix: AI + components: + - type: AiController + logic: Xeno + - type: Hands + hands: + - left + - right + - type: MovementSpeedModifier + # Organs + - type: InteractionOutline + - type: Sprite + drawdepth: Mobs + sprite: Mob/xeno.rsi + state: running + - type: Icon + sprite: Mob/xeno.rsi + state: running + - type: Physics + mass: 85 + - type: Collidable + shapes: + - !type:PhysShapeAabb + bounds: "-0.35,-0.35,0.35,0.35" + mask: + - Impassable + - MobImpassable + - VaultImpassable + - SmallImpassable + layer: + - Opaque + - MobImpassable + - type: Species + Template: Human + HeatResistance: 323 + - type: BodyManager + BaseTemplate: bodyTemplate.Humanoid + BasePreset: bodyPreset.BasicHuman + - type: HeatResistance + - type: Damageable + - type: CombatMode + - type: Teleportable + - type: CharacterInfo + - type: FootstepSound + - type: HumanoidAppearance + - type: Stunnable + - type: AnimationPlayer + - type: UnarmedCombat + range: 1.5 + arcwidth: 0 + arc: claw + damage: 90 diff --git a/Resources/Prototypes/Entities/Mobs/Player/human.yml b/Resources/Prototypes/Entities/Mobs/Player/human.yml new file mode 100644 index 0000000000..8995218961 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Player/human.yml @@ -0,0 +1,20 @@ +- type: entity + save: false + name: Urist McHands + parent: BaseHumanMob_Content + abstract: true + id: HumanMob_Content + description: A miserable pile of secrets + drawdepth: Mobs + components: + - type: Mind + show_examine_info: true + - type: Input + context: "human" + - type: StatusEffectsUI + - type: OverlayEffectsUI + - type: Eye + zoom: 0.5, 0.5 + - type: CameraRecoil + - type: Examiner + - type: HumanInventoryController \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/human.yml b/Resources/Prototypes/Entities/Mobs/human.yml index 44f5752f16..c12cd79d92 100644 --- a/Resources/Prototypes/Entities/Mobs/human.yml +++ b/Resources/Prototypes/Entities/Mobs/human.yml @@ -1,5 +1,5 @@ # Both humans and NPCs inherit from this. -# Anything human specific (e.g. UI, input) goes under HumanMob_Content +# Anything player specific (e.g. UI, input) goes under HumanMob_Content - type: entity save: false name: Urist McHands @@ -138,26 +138,6 @@ arcwidth: 30 arc: fist -- type: entity - save: false - name: Urist McHands - parent: BaseHumanMob_Content - id: HumanMob_Content - description: A miserable pile of secrets - drawdepth: Mobs - components: - - type: Mind - show_examine_info: true - - type: Input - context: "human" - - type: StatusEffectsUI - - type: OverlayEffectsUI - - type: Eye - zoom: 0.5, 0.5 - - type: CameraRecoil - - type: Examiner - - type: HumanInventoryController - - type: entity save: false name: Urist McHands diff --git a/Resources/Textures/Mob/xeno.rsi/meta.json b/Resources/Textures/Mob/xeno.rsi/meta.json new file mode 100644 index 0000000000..b6ea7da0c2 --- /dev/null +++ b/Resources/Textures/Mob/xeno.rsi/meta.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "size": { + "x": 64, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/discordia-space/CEV-Eris/raw/7344da18b5e3dd0b1994a84e9c9c0774d71b93a5/icons/mob/alien.dmi", + "states": [ + { + "name": "running", + "directions": 4 + }, + { + "name": "standing", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Mob/xeno.rsi/running.png b/Resources/Textures/Mob/xeno.rsi/running.png new file mode 100644 index 0000000000..b2ca1b8d4f Binary files /dev/null and b/Resources/Textures/Mob/xeno.rsi/running.png differ diff --git a/Resources/Textures/Mob/xeno.rsi/standing.png b/Resources/Textures/Mob/xeno.rsi/standing.png new file mode 100644 index 0000000000..c1b2b72505 Binary files /dev/null and b/Resources/Textures/Mob/xeno.rsi/standing.png differ diff --git a/Resources/Textures/Objects/Melee/xeno_claw.rsi/icon.png b/Resources/Textures/Objects/Melee/xeno_claw.rsi/icon.png new file mode 100644 index 0000000000..39e2553f73 Binary files /dev/null and b/Resources/Textures/Objects/Melee/xeno_claw.rsi/icon.png differ diff --git a/Resources/Textures/Objects/Melee/xeno_claw.rsi/inhand-left.png b/Resources/Textures/Objects/Melee/xeno_claw.rsi/inhand-left.png new file mode 100644 index 0000000000..b45459851f Binary files /dev/null and b/Resources/Textures/Objects/Melee/xeno_claw.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Melee/xeno_claw.rsi/inhand-right.png b/Resources/Textures/Objects/Melee/xeno_claw.rsi/inhand-right.png new file mode 100644 index 0000000000..10a8c89181 Binary files /dev/null and b/Resources/Textures/Objects/Melee/xeno_claw.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Melee/xeno_claw.rsi/meta.json b/Resources/Textures/Objects/Melee/xeno_claw.rsi/meta.json new file mode 100644 index 0000000000..bce8d4e9b0 --- /dev/null +++ b/Resources/Textures/Objects/Melee/xeno_claw.rsi/meta.json @@ -0,0 +1,23 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/discordia-space/CEV-Eris/raw/7344da18b5e3dd0b1994a84e9c9c0774d71b93a5/icons/mob/alien.dmi", + "states": [ + { + "name": "icon", + "directions": 1 + }, + { + "name": "inhand-left", + "directions": 1 + }, + { + "name": "inhand-right", + "directions": 1 + } + ] +} \ No newline at end of file