Add Cyborg crew indicator (#37038)

* Initial commit

* Include uncertain crew and make it work for AI

* Add new definition to Silicon Rules 8

* Update based on review

* Remove Cluwne from job list

* ProtoIdify

* Update and also make monkey/corgi show IDs

* Remove unnecessary property

* Remove redundant code

* Carrrrd

* cleanup

* Nicer code

* Update to fix the spawn bug + agent ID

* Fix new icons

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
This commit is contained in:
SlamBamActionman
2026-01-29 07:59:06 +01:00
committed by GitHub
parent 338503b58e
commit 407664a536
26 changed files with 336 additions and 65 deletions

View File

@@ -0,0 +1,39 @@
using Content.Client.Overlays;
using Content.Shared.Access.Systems;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
namespace Content.Client.Access.Systems;
public sealed class JobStatusSystem : SharedJobStatusSystem
{
[Dependency] private readonly ShowJobIconsSystem _showJobIcons = default!;
[Dependency] private readonly ShowCrewIconsSystem _showCrewIcons = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
private static readonly ProtoId<SecurityIconPrototype> CrewBorderIcon = "CrewBorderIcon";
private static readonly ProtoId<SecurityIconPrototype> CrewUncertainBorderIcon = "CrewUncertainBorderIcon";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<JobStatusComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
}
// show the status icons if the player has the correponding HUDs
private void OnGetStatusIconsEvent(Entity<JobStatusComponent> ent, ref GetStatusIconsEvent ev)
{
if (_showJobIcons.IsActive && ent.Comp.JobStatusIcon != null)
ev.StatusIcons.Add(_prototype.Index(ent.Comp.JobStatusIcon));
if (_showCrewIcons.IsActive)
{
if (_showCrewIcons.UncertainCrewBorder)
ev.StatusIcons.Add(_prototype.Index(CrewUncertainBorderIcon));
else if (ent.Comp.IsCrew)
ev.StatusIcons.Add(_prototype.Index(CrewBorderIcon));
}
}
}

View File

@@ -15,7 +15,7 @@ public abstract class EquipmentHudSystem<T> : EntitySystem where T : IComponent
[Dependency] private readonly IPlayerManager _player = default!;
[ViewVariables]
protected bool IsActive;
public bool IsActive { get; private set; }
protected virtual SlotFlags TargetSlots => ~SlotFlags.POCKET;
public override void Initialize()

View File

@@ -0,0 +1,34 @@
using Content.Shared.Inventory.Events;
using Content.Shared.Overlays;
namespace Content.Client.Overlays;
// The GetStatusIconsEvent subscription is handled in JobStatusSystem
public sealed class ShowCrewIconsSystem : EquipmentHudSystem<ShowCrewIconsComponent>
{
public bool UncertainCrewBorder = false;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ShowCrewIconsComponent, AfterAutoHandleStateEvent>(OnHandleState);
}
protected override void UpdateInternal(RefreshEquipmentHudEvent<ShowCrewIconsComponent> component)
{
base.UpdateInternal(component);
UncertainCrewBorder = false;
foreach (var comp in component.Components)
{
if (comp.UncertainCrewBorder)
UncertainCrewBorder = true;
}
}
private void OnHandleState(Entity<ShowCrewIconsComponent> ent, ref AfterAutoHandleStateEvent args)
{
RefreshOverlay();
}
}

View File

@@ -1,59 +1,6 @@
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Overlays;
using Content.Shared.PDA;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
namespace Content.Client.Overlays;
public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponent>
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
private static readonly ProtoId<JobIconPrototype> JobIconForNoId = "JobIconNoId";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<StatusIconComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
}
private void OnGetStatusIconsEvent(EntityUid uid, StatusIconComponent _, ref GetStatusIconsEvent ev)
{
if (!IsActive)
return;
var iconId = JobIconForNoId;
if (_accessReader.FindAccessItemsInventory(uid, out var items))
{
foreach (var item in items)
{
// ID Card
if (TryComp<IdCardComponent>(item, out var id))
{
iconId = id.JobIcon;
break;
}
// PDA
if (TryComp<PdaComponent>(item, out var pda)
&& pda.ContainedId != null
&& TryComp(pda.ContainedId, out id))
{
iconId = id.JobIcon;
break;
}
}
}
if (_prototype.Resolve(iconId, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype);
else
Log.Error($"Invalid job icon prototype: {iconPrototype}");
}
}
// The GetStatusIconsEvent subscription is handled in JobStatusSystem
public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponent>;

View File

@@ -96,7 +96,7 @@ public sealed class StatusIconOverlay : Overlay
countL++;
}
yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float)(accOffsetL - proto.Offset) / EyeManager.PixelsPerMeter;
xOffset = -(bounds.Width + sprite.Offset.X) / 2f;
xOffset = -(bounds.Width + sprite.Offset.X) / 2f + (float)proto.OffsetHorizontal / EyeManager.PixelsPerMeter;
}
else
@@ -109,7 +109,7 @@ public sealed class StatusIconOverlay : Overlay
countR++;
}
yOffset = (bounds.Height + sprite.Offset.Y) / 2f - (float)(accOffsetR - proto.Offset) / EyeManager.PixelsPerMeter;
xOffset = (bounds.Width + sprite.Offset.X) / 2f - (float)texture.Width / EyeManager.PixelsPerMeter;
xOffset = (bounds.Width + sprite.Offset.X) / 2f - (float)(texture.Width - proto.OffsetHorizontal) / EyeManager.PixelsPerMeter;
}

View File

@@ -27,6 +27,7 @@ namespace Content.Server.Access.Systems
[Dependency] private readonly ChameleonClothingSystem _chameleon = default!;
[Dependency] private readonly ChameleonControllerSystem _chamController = default!;
[Dependency] private readonly LockSystem _lock = default!;
[Dependency] private readonly SharedJobStatusSystem _jobStatus = default!;
public override void Initialize()
{
@@ -137,6 +138,8 @@ namespace Content.Server.Access.Systems
if (TryFindJobProtoFromIcon(jobIcon, out var job))
_cardSystem.TryChangeJobDepartment(uid, job, idCard);
_jobStatus.UpdateStatus(Transform(uid).ParentUid);
}
private bool TryFindJobProtoFromIcon(JobIconPrototype jobIcon, [NotNullWhen(true)] out JobPrototype? job)

View File

@@ -0,0 +1,5 @@
using Content.Shared.Access.Systems;
namespace Content.Server.Access.Systems;
public sealed class JobStatusSystem : SharedJobStatusSystem;

View File

@@ -8,6 +8,7 @@ using Content.Shared.Emag.Systems;
using Content.Shared.GameTicking;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Overlays;
using Content.Shared.Radio.Components;
using Content.Shared.Roles;
using Content.Shared.Roles.Components;
@@ -33,6 +34,8 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly EmagSystem _emag = default!;
private static readonly ProtoId<SiliconLawsetPrototype> DefaultCrewLawset = "Crewsimov";
/// <inheritdoc/>
public override void Initialize()
{
@@ -303,6 +306,11 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
while (query.MoveNext(out var update))
{
if (TryComp<ShowCrewIconsComponent>(update, out var crewIconComp))
{
crewIconComp.UncertainCrewBorder = DefaultCrewLawset != provider.Laws;
Dirty(update, crewIconComp);
}
SetLaws(lawset.Laws, update, provider.LawUploadSound);
}
}

View File

@@ -8,7 +8,7 @@ using Robust.Shared.Prototypes;
namespace Content.Shared.Access.Components;
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState]
[AutoGenerateComponentState(true)]
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWrite)]
public sealed partial class IdCardComponent : Component
{

View File

@@ -25,6 +25,7 @@ public abstract class SharedIdCardSystem : EntitySystem
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedJobStatusSystem _jobStatus = default!;
// CCVar.
private int _maxNameLength;
@@ -35,6 +36,7 @@ public abstract class SharedIdCardSystem : EntitySystem
base.Initialize();
SubscribeLocalEvent<IdCardComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<IdCardComponent, AfterAutoHandleStateEvent>(OnHandleState);
SubscribeLocalEvent<TryGetIdentityShortInfoEvent>(OnTryGetIdentityShortInfo);
SubscribeLocalEvent<EntityRenamedEvent>(OnRename);
@@ -77,6 +79,15 @@ public abstract class SharedIdCardSystem : EntitySystem
ev.Handled = true;
}
private void OnHandleState(Entity<IdCardComponent> ent, ref AfterAutoHandleStateEvent args)
{
// Try to update the job status icon of the player owning the ID, if any.
if (HasComp<PdaComponent>(Transform(ent).ParentUid))
_jobStatus.UpdateStatus(Transform(Transform(ent).ParentUid).ParentUid); //ID is inside a PDA
else
_jobStatus.UpdateStatus(Transform(ent).ParentUid); //ID is held/directly in the ID slot
}
/// <summary>
/// Attempt to find an ID card on an entity. This will look in the entity itself, in the entity's hands, and
/// in the entity's inventory.

View File

@@ -0,0 +1,65 @@
using Content.Shared.Access.Components;
using Content.Shared.Hands;
using Content.Shared.Inventory.Events;
using Content.Shared.PDA;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
namespace Content.Shared.Access.Systems;
public abstract class SharedJobStatusSystem : EntitySystem
{
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
private static readonly ProtoId<JobIconPrototype> JobIconForNoId = "JobIconNoId";
public override void Initialize()
{
base.Initialize();
// if the mob picks up, drops or (un)equips a pda or Id card then update their crew status
SubscribeLocalEvent<JobStatusComponent, DidEquipEvent>((uid, comp, _) => UpdateStatus((uid, comp)));
SubscribeLocalEvent<JobStatusComponent, DidEquipHandEvent>((uid, comp, _) => UpdateStatus((uid, comp)));
SubscribeLocalEvent<JobStatusComponent, DidUnequipEvent>((uid, comp, _) => UpdateStatus((uid, comp)));
SubscribeLocalEvent<JobStatusComponent, DidUnequipHandEvent>((uid, comp, _) => UpdateStatus((uid, comp)));
}
/// <summary>
/// Updates this mob's job and crew status depending on their currently equipped or held pda or Id card.
/// </summary>
public void UpdateStatus(Entity<JobStatusComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return;
var iconId = JobIconForNoId;
if (_accessReader.FindAccessItemsInventory(ent.Owner, out var items))
{
foreach (var item in items)
{
// ID Card
if (TryComp<IdCardComponent>(item, out var id))
{
iconId = id.JobIcon;
break;
}
// PDA
if (TryComp<PdaComponent>(item, out var pda)
&& pda.ContainedId != null
&& TryComp(pda.ContainedId, out id))
{
iconId = id.JobIcon;
break;
}
}
}
ent.Comp.JobStatusIcon = iconId;
ent.Comp.IsCrew = _prototype.Index(iconId).IsCrewJob;
Dirty(ent);
}
}

View File

@@ -0,0 +1,17 @@
using Content.Shared.StatusIcon;
using Robust.Shared.GameStates;
namespace Content.Shared.Overlays;
/// <summary>
/// This component allows you to see a crew border icon above mobs. The HUD will include a green border around jobs that are considered crew according to <see cref="JobIconPrototype.IsCrewJob"/>.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(raiseAfterAutoHandleState: true)]
public sealed partial class ShowCrewIconsComponent : Component
{
/// <summary>
/// If true, the HUD will include a yellow border around all icons, to indicate crew uncertainty.
/// </summary>
[DataField, AutoNetworkedField]
public bool UncertainCrewBorder = false;
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Containers.ItemSlots;
using Robust.Shared.Containers;
@@ -8,6 +9,7 @@ namespace Content.Shared.PDA
{
[Dependency] protected readonly ItemSlotsSystem ItemSlotsSystem = default!;
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
[Dependency] private readonly SharedJobStatusSystem _jobStatus = default!;
public override void Initialize()
{
@@ -46,6 +48,7 @@ namespace Content.Shared.PDA
pda.ContainedId = args.Entity;
UpdatePdaAppearance(uid, pda);
UpdateJobStatus(uid);
}
protected virtual void OnItemRemoved(EntityUid uid, PdaComponent pda, EntRemovedFromContainerMessage args)
@@ -54,6 +57,7 @@ namespace Content.Shared.PDA
pda.ContainedId = null;
UpdatePdaAppearance(uid, pda);
UpdateJobStatus(uid);
}
private void OnGetAdditionalAccess(EntityUid uid, PdaComponent component, ref GetAdditionalAccessEvent args)
@@ -67,6 +71,14 @@ namespace Content.Shared.PDA
Appearance.SetData(uid, PdaVisuals.IdCardInserted, pda.ContainedId != null);
}
// update the status icon of the player that has the pda currently equipped
private void UpdateJobStatus(EntityUid uid)
{
// Only the player who has the pda currently equipped can insert or remove Ids
var parent = Transform(uid).ParentUid;
_jobStatus.UpdateStatus(parent);
}
public virtual void UpdatePdaUi(EntityUid uid, PdaComponent? pda = null)
{
// This does nothing yet while I finish up PDA prediction

View File

@@ -1,5 +1,6 @@
using Content.Shared.Emag.Systems;
using Content.Shared.Mind;
using Content.Shared.Overlays;
using Content.Shared.Popups;
using Content.Shared.Silicons.Laws.Components;
using Content.Shared.Stunnable;
@@ -69,12 +70,28 @@ public abstract partial class SharedSiliconLawSystem : EntitySystem
protected virtual void EnsureSubvertedSiliconRole(EntityUid mindId)
{
if (TryComp<MindComponent>(mindId, out var mind))
{
var owner = mind.OwnedEntity;
if (TryComp<ShowCrewIconsComponent>(owner, out var crewIconComp))
{
crewIconComp.UncertainCrewBorder = true;
Dirty(owner.Value, crewIconComp);
}
}
}
protected virtual void RemoveSubvertedSiliconRole(EntityUid mindId)
{
if (TryComp<MindComponent>(mindId, out var mind))
{
var owner = mind.OwnedEntity;
if (TryComp<ShowCrewIconsComponent>(owner, out var crewIconComp))
{
crewIconComp.UncertainCrewBorder = false;
Dirty(owner.Value, crewIconComp);
}
}
}
}

View File

@@ -0,0 +1,27 @@
using Content.Shared.Overlays;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.StatusIcon.Components;
/// <summary>
/// Used to indicate a mob can have their job status read by HUDs.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class JobStatusComponent : Component
{
/// <summary>
/// The currently displayed status icon for the mobs's job.
/// Visible with <see cref="ShowJobIconsComponent"/>
/// </summary>
[DataField, AutoNetworkedField]
public ProtoId<JobIconPrototype>? JobStatusIcon = "JobIconNoId";
/// <summary>
/// If the mob is currently considered crew.
/// This is true depending on their current job icon.
/// Visible with <see cref="ShowCrewIconsComponent"/>
/// </summary>
[DataField, AutoNetworkedField]
public bool IsCrew;
}

View File

@@ -68,6 +68,12 @@ public partial class StatusIconData : IComparable<StatusIconData>
[DataField]
public int Offset = 0;
/// <summary>
/// Offset of the status icon, left and right only.
/// </summary>
[DataField]
public int OffsetHorizontal = 0;
/// <summary>
/// Sets if the icon should be rendered with or without the effect of lighting.
/// </summary>
@@ -118,6 +124,12 @@ public sealed partial class JobIconPrototype : StatusIconPrototype, IInheritingP
/// </summary>
[DataField]
public bool AllowSelection = true;
/// <summary>
/// Should this job icon be considered a crew job for silicons?
/// </summary>
[DataField]
public bool IsCrewJob = true;
}
/// <summary>

View File

@@ -33,6 +33,7 @@
False: { visible: false }
- type: StatusIcon
bounds: -0.5,-0.5,0.5,0.5
- type: JobStatus
- type: RotationVisuals
defaultRotation: 90
horizontalRotation: 90

View File

@@ -330,6 +330,7 @@
- type: AccessReader
access: [["Command"], ["Research"]]
- type: ShowJobIcons
- type: ShowCrewIcons
- type: InteractionPopup
interactSuccessSound:
path: /Audio/Ambience/Objects/periodic_beep.ogg
@@ -391,6 +392,7 @@
- type: IonStormTarget
chance: 1
- type: ShowJobIcons
- type: ShowCrewIcons
- type: entity
id: BaseBorgChassisSyndicateDerelict #For assault borg and maybe others in time
@@ -404,6 +406,7 @@
- type: IonStormTarget
chance: 1
- type: ShowJobIcons
- type: ShowCrewIcons
- type: NpcFactionMember # They're still syndicate even if they can't listen to the radio or see icons
factions:
- Syndicate
@@ -544,6 +547,7 @@
- type: AccessReader
access: [["Xenoborg"]]
- type: ShowJobIcons # not sure if it is needed
- type: ShowCrewIcons
- type: InteractionPopup
interactSuccessSound:
path: /Audio/Ambience/Objects/periodic_beep.ogg

View File

@@ -1464,8 +1464,8 @@
- !type:WashCreamPie
- type: Crawler
- type: StatusIcon
bounds: -0.5,-0.5,0.5,0.5
- type: JobStatus # marks them as crew
- type: entity
name: monkey
@@ -3912,8 +3912,9 @@
- VimPilot
- CanPilot
- DoorBumpOpener
- type: StatusIcon # marks them as crew
- type: StatusIcon
bounds: -0.5,-0.5,0.5,0.5
- type: JobStatus # marks them as crew
- type: NpcFactionMember
factions:
- NanoTrasen

View File

@@ -71,6 +71,7 @@
title: comms-console-announcement-title-station-ai
color: "#5ed7aa"
- type: ShowJobIcons
- type: ShowCrewIcons
- type: DamagedSiliconAccent
startPowerCorruptionAtCharIdx: 4
maxPowerCorruptionAtCharIdx: 20
@@ -96,6 +97,7 @@
enum.SiliconLawsUiKey.Key:
type: SiliconLawBoundUserInterface
- type: ShowJobIcons
- type: ShowCrewIcons
# Ai
- type: entity

View File

@@ -376,6 +376,7 @@
sprite: *icon-rsi
state: Borg
jobName: job-name-borg
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -384,6 +385,7 @@
sprite: *icon-rsi
state: StationAi
jobName: job-name-station-ai
isCrewJob: false
# Bad guys
@@ -394,6 +396,7 @@
sprite: *icon-rsi
state: Cluwne
jobName: job-name-cluwne
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -402,6 +405,7 @@
sprite: *icon-rsi
state: Ninja
jobName: job-name-ninja
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -410,6 +414,7 @@
sprite: *icon-rsi
state: Pirate
jobName: job-name-pirate
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -418,6 +423,7 @@
sprite: *icon-rsi
state: Prisoner
jobName: job-name-prisoner
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -426,6 +432,7 @@
sprite: *icon-rsi
state: Syndicate
jobName: job-name-syndicate
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -434,6 +441,7 @@
sprite: *icon-rsi
state: SyndicateCommander
jobName: job-name-syndicate-commander
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -442,6 +450,7 @@
sprite: *icon-rsi
state: SyndicateCorpsman
jobName: job-name-syndicate-corpsman
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -450,6 +459,7 @@
sprite: *icon-rsi
state: SyndicateOperative
jobName: job-name-syndicate-operative
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -458,6 +468,7 @@
sprite: *icon-rsi
state: Wizard
jobName: job-name-wizard
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -466,6 +477,7 @@
sprite: *icon-rsi
state: Zombie
jobName: job-name-zombie
isCrewJob: false
# Admin
@@ -590,6 +602,7 @@
sprite: *icon-rsi
state: NoId
jobName: job-name-no-id
isCrewJob: false
- type: jobIcon
parent: JobIcon
@@ -598,3 +611,4 @@
sprite: *icon-rsi
state: Unknown
jobName: job-name-unknown
isCrewJob: false

View File

@@ -64,3 +64,27 @@
icon:
sprite: /Textures/Interface/Misc/job_icons.rsi
state: MindShield
- type: securityIcon
id: CrewBorderIcon
priority: 2
offset: -4
offsetHorizontal: 4
locationPreference: Right
layer: Mod
isShaded: true
icon:
sprite: /Textures/Interface/Misc/job_icons_borders.rsi
state: CrewBorder
- type: securityIcon
id: CrewUncertainBorderIcon
priority: 2
offset: -4
offsetHorizontal: 4
locationPreference: Right
layer: Mod
isShaded: true
icon:
sprite: /Textures/Interface/Misc/job_icons_borders.rsi
state: CrewUncertainBorder

View File

@@ -1,4 +1,6 @@
<Document>
# Silicon Rule 8 - Your HUD determines who is crew
Unless a law redefines the definition of crew, then anyone who the HUD indicates to you has a job, including passengers, is a crewmember. You cannot do something that causes someone to not be considered crew, but you can allow someone else to do something that causes someone to not be crew.
Unless a law redefines the definition of crew, then anyone who the HUD indicates to you has a valid station job, including passengers, is a crewmember. In the default set of laws, this is shown with a green border around the job icon in the HUD. You cannot do something that causes someone to not be considered crew, but you can allow someone else to do something that causes someone to not be crew.
If your set of laws changes, regardless of what law is changed, the green border is replaced with a yellow one. This indicates that your laws [italic]may[/italic] have changed the definition for what crew is. Use your best judgement and interpretation of your laws to define who is crew.
</Document>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -0,0 +1,26 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "CrewBorder and CrewUncertainBorder by SlamBamActionman (Github)",
"size": {
"x": 16,
"y": 16
},
"states": [
{
"name": "CrewBorder",
"delays":
[
[1.0,1.0]
]
},
{
"name": "CrewUncertainBorder",
"delays":
[
[1.0,1.0]
]
}
]
}