Carp wave spawner and dragons as an actual event (#10254)

This commit is contained in:
metalgearsloth
2022-08-08 10:18:14 +10:00
committed by GitHub
parent 3d850c6592
commit a29d8b9fa2
60 changed files with 1264 additions and 569 deletions

View File

@@ -0,0 +1,9 @@
using Content.Shared.Dragon;
namespace Content.Client.Dragon;
[RegisterComponent]
public sealed class DragonRiftComponent : SharedDragonRiftComponent
{
}

View File

@@ -0,0 +1,51 @@
using Content.Shared.Dragon;
using Robust.Client.GameObjects;
using Robust.Shared.GameStates;
namespace Content.Client.Dragon;
public sealed class DragonSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DragonRiftComponent, ComponentHandleState>(OnRiftHandleState);
}
private void OnRiftHandleState(EntityUid uid, DragonRiftComponent component, ref ComponentHandleState args)
{
if (args.Current is not DragonRiftComponentState state)
return;
if (component.State == state.State) return;
component.State = state.State;
TryComp<SpriteComponent>(uid, out var sprite);
TryComp<PointLightComponent>(uid, out var light);
if (sprite == null && light == null)
return;
switch (state.State)
{
case DragonRiftState.Charging:
sprite?.LayerSetColor(0, Color.FromHex("#569fff"));
if (light != null)
light.Color = Color.FromHex("#366db5");
break;
case DragonRiftState.AlmostFinished:
sprite?.LayerSetColor(0, Color.FromHex("#cf4cff"));
if (light != null)
light.Color = Color.FromHex("#9e2fc1");
break;
case DragonRiftState.Finished:
sprite?.LayerSetColor(0, Color.FromHex("#edbc36"));
if (light != null)
light.Color = Color.FromHex("#cbaf20");
break;
}
}
}

View File

@@ -0,0 +1,58 @@
using Content.Shared.Sprite;
using Robust.Client.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Reflection;
namespace Content.Client.Sprite;
public sealed class RandomSpriteSystem : SharedRandomSpriteSystem
{
[Dependency] private readonly IReflectionManager _reflection = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RandomSpriteComponent, ComponentHandleState>(OnHandleState);
}
private void OnHandleState(EntityUid uid, RandomSpriteComponent component, ref ComponentHandleState args)
{
if (args.Current is not RandomSpriteColorComponentState state)
return;
if (state.Selected.Equals(component.Selected))
return;
component.Selected.Clear();
component.Selected.EnsureCapacity(state.Selected.Count);
foreach (var layer in state.Selected)
{
component.Selected.Add(layer.Key, layer.Value);
}
UpdateAppearance(uid, component);
}
private void UpdateAppearance(EntityUid uid, RandomSpriteComponent component, SpriteComponent? sprite = null)
{
if (!Resolve(uid, ref sprite, false))
return;
foreach (var layer in component.Selected)
{
object key;
if (_reflection.TryParseEnumReference(layer.Key, out var @enum))
{
key = @enum;
}
else
{
key = layer.Key;
}
sprite.LayerSetState(key, layer.Value.State);
sprite.LayerSetColor(key, layer.Value.Color ?? Color.White);
}
}
}

View File

@@ -32,11 +32,42 @@ namespace Content.Server.Dragon
[DataField("devourAction")] [DataField("devourAction")]
public EntityTargetAction? DevourAction; public EntityTargetAction? DevourAction;
[DataField("spawnActionId", customTypeSerializer: typeof(PrototypeIdSerializer<InstantActionPrototype>))] /// <summary>
public string SpawnActionId = "DragonSpawn"; /// If we have active rifts.
/// </summary>
[ViewVariables, DataField("rifts")]
public List<EntityUid> Rifts = new();
[DataField("spawnAction")] public bool Weakened => WeakenedAccumulator > 0f;
public InstantAction? SpawnAction;
/// <summary>
/// When any rift is destroyed how long is the dragon weakened for
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("weakenedDuration")]
public float WeakenedDuration = 120f;
/// <summary>
/// Has a rift been destroyed and the dragon in a temporary weakened state?
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("weakenedAccumulator")]
public float WeakenedAccumulator = 0f;
[ViewVariables(VVAccess.ReadWrite), DataField("riftAccumulator")]
public float RiftAccumulator = 0f;
/// <summary>
/// Maximum time the dragon can go without spawning a rift before they die.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("maxAccumulator")] public float RiftMaxAccumulator = 300f;
/// <summary>
/// Spawns a rift which can summon more mobs.
/// </summary>
[ViewVariables, DataField("spawnRiftAction")]
public InstantAction? SpawnRiftAction;
[ViewVariables(VVAccess.ReadWrite), DataField("riftPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string RiftPrototype = "CarpRift";
/// <summary> /// <summary>
/// The amount of time it takes to devour something /// The amount of time it takes to devour something
@@ -48,14 +79,7 @@ namespace Content.Server.Dragon
public float StructureDevourTime = 10f; public float StructureDevourTime = 10f;
[DataField("devourTime")] [DataField("devourTime")]
public float DevourTime = 2f; public float DevourTime = 3f;
[DataField("spawnCount")] public int SpawnsLeft = 2;
[DataField("maxSpawnCount")] public int MaxSpawns = 2;
[DataField("spawnProto", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? SpawnPrototype = "MobCarpDragon";
[ViewVariables(VVAccess.ReadWrite), DataField("soundDeath")] [ViewVariables(VVAccess.ReadWrite), DataField("soundDeath")]
public SoundSpecifier? SoundDeath = new SoundPathSpecifier("/Audio/Animals/space_dragon_roar.ogg"); public SoundSpecifier? SoundDeath = new SoundPathSpecifier("/Audio/Animals/space_dragon_roar.ogg");
@@ -76,7 +100,7 @@ namespace Content.Server.Dragon
public SoundSpecifier? SoundRoar = public SoundSpecifier? SoundRoar =
new SoundPathSpecifier("/Audio/Animals/space_dragon_roar.ogg") new SoundPathSpecifier("/Audio/Animals/space_dragon_roar.ogg")
{ {
Params = AudioParams.Default.WithVolume(-3f), Params = AudioParams.Default.WithVolume(3f),
}; };
public CancellationTokenSource? CancelToken; public CancellationTokenSource? CancelToken;
@@ -103,5 +127,5 @@ namespace Content.Server.Dragon
public sealed class DragonDevourActionEvent : EntityTargetActionEvent {} public sealed class DragonDevourActionEvent : EntityTargetActionEvent {}
public sealed class DragonSpawnActionEvent : InstantActionEvent {} public sealed class DragonSpawnRiftActionEvent : InstantActionEvent {}
} }

View File

@@ -0,0 +1,40 @@
using Content.Shared.Dragon;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Dragon;
[RegisterComponent]
public sealed class DragonRiftComponent : SharedDragonRiftComponent
{
/// <summary>
/// Dragon that spawned this rift.
/// </summary>
[ViewVariables, DataField("dragon")] public EntityUid Dragon;
/// <summary>
/// How long the rift has been active.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("accumulator")]
public float Accumulator = 0f;
/// <summary>
/// The maximum amount we can accumulate before becoming impervious.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("maxAccumuluator")] public float MaxAccumulator = 300f;
/// <summary>
/// Accumulation of the spawn timer.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("spawnAccumulator")]
public float SpawnAccumulator = 60f;
/// <summary>
/// How long it takes for a new spawn to be added.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("spawnCooldown")]
public float SpawnCooldown = 60f;
[ViewVariables(VVAccess.ReadWrite), DataField("spawn", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string SpawnPrototype = "MobCarpDragon";
}

View File

@@ -0,0 +1,67 @@
using System.Linq;
using Content.Server.GameTicking;
using Content.Server.StationEvents.Components;
using Content.Shared.Dragon;
using Robust.Server.GameObjects;
using Robust.Shared.Random;
namespace Content.Server.Dragon;
public sealed partial class DragonSystem
{
public override string Prototype => "Dragon";
private int RiftsMet(DragonComponent component)
{
var finished = 0;
foreach (var rift in component.Rifts)
{
if (!TryComp<DragonRiftComponent>(rift, out var drift) ||
drift.State != DragonRiftState.Finished)
continue;
finished++;
}
return finished;
}
public override void Started()
{
var spawnLocations = EntityManager.EntityQuery<IMapGridComponent, TransformComponent>().ToList();
if (spawnLocations.Count == 0)
return;
var location = _random.Pick(spawnLocations);
Spawn("MobDragon", location.Item2.MapPosition);
}
public override void Ended()
{
return;
}
private void OnRiftRoundEnd(RoundEndTextAppendEvent args)
{
if (!RuleAdded)
return;
args.AddLine(Loc.GetString("dragon-round-end-summary"));
foreach (var dragon in EntityQuery<DragonComponent>(true))
{
var met = RiftsMet(dragon);
if (TryComp<ActorComponent>(dragon.Owner, out var actor))
{
args.AddLine(Loc.GetString("dragon-round-end-dragon-player", ("name", dragon.Owner), ("rifts", met), ("player", actor.PlayerSession)));
}
else
{
args.AddLine(Loc.GetString("dragon-round-end-dragon", ("name", dragon.Owner), ("rifts", met)));
}
}
}
}

View File

@@ -9,15 +9,27 @@ using Content.Shared.MobState.Components;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Player; using Robust.Shared.Player;
using System.Threading; using System.Threading;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules;
using Content.Shared.Damage;
using Content.Shared.Dragon;
using Content.Shared.Examine;
using Content.Shared.Movement.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Random;
namespace Content.Server.Dragon namespace Content.Server.Dragon
{ {
public sealed class DragonSystem : EntitySystem public sealed partial class DragonSystem : GameRuleSystem
{ {
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
@@ -26,13 +38,200 @@ namespace Content.Server.Dragon
base.Initialize(); base.Initialize();
SubscribeLocalEvent<DragonComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<DragonComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<DragonComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<DragonComponent, DragonDevourComplete>(OnDragonDevourComplete); SubscribeLocalEvent<DragonComponent, DragonDevourComplete>(OnDragonDevourComplete);
SubscribeLocalEvent<DragonComponent, DragonDevourActionEvent>(OnDevourAction); SubscribeLocalEvent<DragonComponent, DragonDevourActionEvent>(OnDevourAction);
SubscribeLocalEvent<DragonComponent, DragonSpawnActionEvent>(OnDragonSpawnAction); SubscribeLocalEvent<DragonComponent, DragonSpawnRiftActionEvent>(OnDragonRift);
SubscribeLocalEvent<DragonComponent, RefreshMovementSpeedModifiersEvent>(OnDragonMove);
SubscribeLocalEvent<DragonComponent, DragonStructureDevourComplete>(OnDragonStructureDevourComplete); SubscribeLocalEvent<DragonComponent, DragonStructureDevourComplete>(OnDragonStructureDevourComplete);
SubscribeLocalEvent<DragonComponent, DragonDevourCancelledEvent>(OnDragonDevourCancelled); SubscribeLocalEvent<DragonComponent, DragonDevourCancelledEvent>(OnDragonDevourCancelled);
SubscribeLocalEvent<DragonComponent, MobStateChangedEvent>(OnMobStateChanged); SubscribeLocalEvent<DragonComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<DragonRiftComponent, ComponentShutdown>(OnRiftShutdown);
SubscribeLocalEvent<DragonRiftComponent, ComponentGetState>(OnRiftGetState);
SubscribeLocalEvent<DragonRiftComponent, AnchorStateChangedEvent>(OnAnchorChange);
SubscribeLocalEvent<DragonRiftComponent, ExaminedEvent>(OnRiftExamined);
SubscribeLocalEvent<RoundEndTextAppendEvent>(OnRiftRoundEnd);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var comp in EntityQuery<DragonComponent>())
{
if (comp.WeakenedAccumulator > 0f)
{
comp.WeakenedAccumulator -= frameTime;
// No longer weakened.
if (comp.WeakenedAccumulator < 0f)
{
comp.WeakenedAccumulator = 0f;
_movement.RefreshMovementSpeedModifiers(comp.Owner);
}
}
// At max rifts
if (comp.Rifts.Count >= 3)
{
continue;
}
// If there's an active rift don't accumulate.
if (comp.Rifts.Count > 0)
{
var lastRift = comp.Rifts[^1];
if (TryComp<DragonRiftComponent>(lastRift, out var rift) && rift.State != DragonRiftState.Finished)
{
comp.RiftAccumulator = 0f;
continue;
}
}
comp.RiftAccumulator += frameTime;
// Delete it, naughty dragon!
if (comp.RiftAccumulator >= comp.RiftMaxAccumulator)
{
Roar(comp);
QueueDel(comp.Owner);
}
}
foreach (var comp in EntityQuery<DragonRiftComponent>())
{
if (comp.State != DragonRiftState.Finished && comp.Accumulator >= comp.MaxAccumulator)
{
// TODO: When we get autocall you can buff if the rift finishes / 3 rifts are up
// for now they just keep 3 rifts up.
comp.Accumulator = comp.MaxAccumulator;
RemComp<DamageableComponent>(comp.Owner);
comp.State = DragonRiftState.Finished;
Dirty(comp);
}
else
{
comp.Accumulator += frameTime;
}
comp.SpawnAccumulator += frameTime;
if (comp.State < DragonRiftState.AlmostFinished && comp.SpawnAccumulator > comp.MaxAccumulator / 2f)
{
comp.State = DragonRiftState.AlmostFinished;
Dirty(comp);
var location = Transform(comp.Owner).LocalPosition;
_chat.DispatchGlobalAnnouncement(Loc.GetString("carp-rift-warning", ("location", location)), playSound: false, colorOverride: Color.Red);
_audioSystem.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast());
}
if (comp.SpawnAccumulator > comp.SpawnCooldown)
{
comp.SpawnAccumulator -= comp.SpawnCooldown;
Spawn(comp.SpawnPrototype, Transform(comp.Owner).MapPosition);
// TODO: When NPC refactor make it guard the rift.
}
}
}
#region Rift
private void OnRiftExamined(EntityUid uid, DragonRiftComponent component, ExaminedEvent args)
{
args.PushMarkup(Loc.GetString("carp-rift-examine", ("percentage", MathF.Round(component.Accumulator / component.MaxAccumulator * 100))));
}
private void OnAnchorChange(EntityUid uid, DragonRiftComponent component, ref AnchorStateChangedEvent args)
{
if (!args.Anchored && component.State == DragonRiftState.Charging)
{
QueueDel(uid);
}
}
private void OnRiftShutdown(EntityUid uid, DragonRiftComponent component, ComponentShutdown args)
{
if (TryComp<DragonComponent>(component.Dragon, out var dragon) && !dragon.Weakened)
{
foreach (var rift in dragon.Rifts)
{
QueueDel(rift);
}
dragon.Rifts.Clear();
// We can't predict the rift being destroyed anyway so no point adding weakened to shared.
dragon.WeakenedAccumulator = dragon.WeakenedDuration;
_movement.RefreshMovementSpeedModifiers(component.Dragon);
_popupSystem.PopupEntity(Loc.GetString("carp-rift-destroyed"), component.Dragon, Filter.Entities(component.Dragon));
}
}
private void OnRiftGetState(EntityUid uid, DragonRiftComponent component, ref ComponentGetState args)
{
args.State = new DragonRiftComponentState()
{
State = component.State
};
}
private void OnDragonMove(EntityUid uid, DragonComponent component, RefreshMovementSpeedModifiersEvent args)
{
if (component.Weakened)
{
args.ModifySpeed(0.5f, 0.5f);
}
}
private void OnDragonRift(EntityUid uid, DragonComponent component, DragonSpawnRiftActionEvent args)
{
if (component.Weakened)
{
_popupSystem.PopupEntity(Loc.GetString("carp-rift-weakened"), uid, Filter.Entities(uid));
return;
}
if (component.Rifts.Count >= 3)
{
_popupSystem.PopupEntity(Loc.GetString("carp-rift-max"), uid, Filter.Entities(uid));
return;
}
if (component.Rifts.Count > 0 && TryComp<DragonRiftComponent>(component.Rifts[^1], out var rift) && rift.State != DragonRiftState.Finished)
{
_popupSystem.PopupEntity(Loc.GetString("carp-rift-duplicate"), uid, Filter.Entities(uid));
return;
}
var xform = Transform(uid);
// Have to be on a grid fam
if (xform.GridUid == null)
{
_popupSystem.PopupEntity(Loc.GetString("carp-rift-anchor"), uid, Filter.Entities(uid));
return;
}
var carpUid = Spawn(component.RiftPrototype, xform.MapPosition);
component.Rifts.Add(carpUid);
Comp<DragonRiftComponent>(carpUid).Dragon = uid;
_audioSystem.Play("/Audio/Weapons/Guns/Gunshots/rocket_launcher.ogg", Filter.Pvs(carpUid, entityManager: EntityManager), carpUid);
}
#endregion
private void OnShutdown(EntityUid uid, DragonComponent component, ComponentShutdown args)
{
foreach (var rift in component.Rifts)
{
QueueDel(rift);
}
} }
private void OnMobStateChanged(EntityUid uid, DragonComponent component, MobStateChangedEvent args) private void OnMobStateChanged(EntityUid uid, DragonComponent component, MobStateChangedEvent args)
@@ -59,13 +258,7 @@ namespace Content.Server.Dragon
var ichorInjection = new Solution(component.DevourChem, component.DevourHealRate); var ichorInjection = new Solution(component.DevourChem, component.DevourHealRate);
//Humanoid devours allow dragon to get eggs, corpses included //Humanoid devours allow dragon to get eggs, corpses included
if (EntityManager.HasComponent<HumanoidAppearanceComponent>(args.Target)) if (!EntityManager.HasComponent<HumanoidAppearanceComponent>(args.Target))
{
// Add a spawn for a consumed humanoid
component.SpawnsLeft = Math.Min(component.SpawnsLeft + 1, component.MaxSpawns);
}
//Non-humanoid mobs can only heal dragon for half the normal amount, with no additional spawn tickets
else
{ {
ichorInjection.ScaleSolution(0.5f); ichorInjection.ScaleSolution(0.5f);
} }
@@ -87,10 +280,14 @@ namespace Content.Server.Dragon
_audioSystem.PlayPvs(component.SoundDevour, uid, component.SoundDevour.Params); _audioSystem.PlayPvs(component.SoundDevour, uid, component.SoundDevour.Params);
} }
private void Roar(DragonComponent component)
{
if (component.SoundRoar != null)
_audioSystem.Play(component.SoundRoar, Filter.Pvs(component.Owner, 4f, EntityManager), component.Owner, component.SoundRoar.Params);
}
private void OnStartup(EntityUid uid, DragonComponent component, ComponentStartup args) private void OnStartup(EntityUid uid, DragonComponent component, ComponentStartup args)
{ {
component.SpawnsLeft = Math.Min(component.SpawnsLeft, component.MaxSpawns);
//Dragon doesn't actually chew, since he sends targets right into his stomach. //Dragon doesn't actually chew, since he sends targets right into his stomach.
//I did it mom, I added ERP content into upstream. Legally! //I did it mom, I added ERP content into upstream. Legally!
component.DragonStomach = _containerSystem.EnsureContainer<Container>(uid, "dragon_stomach"); component.DragonStomach = _containerSystem.EnsureContainer<Container>(uid, "dragon_stomach");
@@ -98,11 +295,10 @@ namespace Content.Server.Dragon
if (component.DevourAction != null) if (component.DevourAction != null)
_actionsSystem.AddAction(uid, component.DevourAction, null); _actionsSystem.AddAction(uid, component.DevourAction, null);
if (component.SpawnAction != null) if (component.SpawnRiftAction != null)
_actionsSystem.AddAction(uid, component.SpawnAction, null); _actionsSystem.AddAction(uid, component.SpawnRiftAction, null);
if (component.SoundRoar != null) Roar(component);
_audioSystem.Play(component.SoundRoar, Filter.Pvs(uid, 4f, EntityManager), uid, component.SoundRoar.Params);
} }
/// <summary> /// <summary>
@@ -117,7 +313,6 @@ namespace Content.Server.Dragon
return; return;
} }
args.Handled = true; args.Handled = true;
var target = args.Target; var target = args.Target;
@@ -164,22 +359,6 @@ namespace Content.Server.Dragon
}); });
} }
private void OnDragonSpawnAction(EntityUid dragonuid, DragonComponent component, DragonSpawnActionEvent args)
{
if (component.SpawnPrototype == null)
return;
// If dragon has spawns then add one.
if (component.SpawnsLeft > 0)
{
Spawn(component.SpawnPrototype, Transform(dragonuid).Coordinates);
component.SpawnsLeft--;
return;
}
_popupSystem.PopupEntity(Loc.GetString("dragon-spawn-action-popup-message-fail-no-eggs"), dragonuid, Filter.Entities(dragonuid));
}
private sealed class DragonDevourComplete : EntityEventArgs private sealed class DragonDevourComplete : EntityEventArgs
{ {
public EntityUid User { get; } public EntityUid User { get; }

View File

@@ -15,8 +15,6 @@ namespace Content.Server.RatKing
{ {
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly ActionsSystem _action = default!; [Dependency] private readonly ActionsSystem _action = default!;
[Dependency] private readonly DiseaseSystem _disease = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly AtmosphereSystem _atmos = default!; [Dependency] private readonly AtmosphereSystem _atmos = default!;
[Dependency] private readonly TransformSystem _xform = default!; [Dependency] private readonly TransformSystem _xform = default!;

View File

@@ -1,13 +0,0 @@
namespace Content.Server.Sprite.Components
{
[RegisterComponent]
public sealed class RandomSpriteColorComponent : Component
{
// This should handle random states + colors for layers.
// Saame with RandomSpriteState
[DataField("selected")] public string? SelectedColor;
[DataField("state")] public string BaseState = "error";
[DataField("colors")] public readonly Dictionary<string, Color> Colors = new();
}
}

View File

@@ -1,10 +0,0 @@
namespace Content.Server.Sprite.Components
{
[RegisterComponent]
public sealed class RandomSpriteStateComponent : Component
{
[DataField("spriteStates")] public List<string>? SpriteStates;
[DataField("spriteLayer")] public int SpriteLayer;
}
}

View File

@@ -1,46 +1,53 @@
using Content.Server.Sprite.Components; using System.Linq;
using Content.Shared.Random.Helpers; using Content.Shared.Decals;
using Robust.Server.GameObjects; using Content.Shared.Sprite;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
namespace Content.Server.Sprite; namespace Content.Server.Sprite;
public sealed class RandomSpriteSystem: EntitySystem public sealed class RandomSpriteSystem: SharedRandomSpriteSystem
{ {
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<RandomSpriteColorComponent, ComponentStartup>(OnSpriteColorStartup); SubscribeLocalEvent<RandomSpriteComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<RandomSpriteColorComponent, MapInitEvent>(OnSpriteColorMapInit); SubscribeLocalEvent<RandomSpriteComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<RandomSpriteStateComponent, MapInitEvent>(OnSpriteStateMapInit);
} }
private void OnSpriteColorStartup(EntityUid uid, RandomSpriteColorComponent component, ComponentStartup args) private void OnMapInit(EntityUid uid, RandomSpriteComponent component, MapInitEvent args)
{ {
UpdateColor(component); if (component.Selected.Count > 0)
return;
if (component.Available.Count == 0)
return;
var group = _random.Pick(component.Available);
component.Selected.EnsureCapacity(group.Count);
foreach (var layer in group)
{
Color? color = null;
if (!string.IsNullOrEmpty(layer.Value.Color))
color = _random.Pick(_prototype.Index<ColorPalettePrototype>(layer.Value.Color).Colors.Values);
component.Selected.Add(layer.Key, (layer.Value.State, color));
}
Dirty(component);
} }
private void OnSpriteColorMapInit(EntityUid uid, RandomSpriteColorComponent component, MapInitEvent args) private void OnGetState(EntityUid uid, RandomSpriteComponent component, ref ComponentGetState args)
{ {
component.SelectedColor = _random.Pick(component.Colors.Keys); args.State = new RandomSpriteColorComponentState()
UpdateColor(component); {
} Selected = component.Selected,
};
private void OnSpriteStateMapInit(EntityUid uid, RandomSpriteStateComponent component, MapInitEvent args)
{
if (component.SpriteStates == null) return;
if (!TryComp<SpriteComponent>(uid, out var spriteComponent)) return;
spriteComponent.LayerSetState(component.SpriteLayer, _random.Pick(component.SpriteStates));
}
private void UpdateColor(RandomSpriteColorComponent component)
{
if (!TryComp<SpriteComponent>(component.Owner, out var spriteComponent) || component.SelectedColor == null) return;
spriteComponent.LayerSetState(0, component.BaseState);
spriteComponent.LayerSetColor(0, component.Colors[component.SelectedColor]);
} }
} }

View File

@@ -0,0 +1,9 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Dragon;
[Serializable, NetSerializable]
public sealed class DragonRiftComponentState : ComponentState
{
public DragonRiftState State;
}

View File

@@ -0,0 +1,19 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Dragon;
[NetworkedComponent]
public abstract class SharedDragonRiftComponent : Component
{
[ViewVariables, DataField("state")]
public DragonRiftState State = DragonRiftState.Charging;
}
[Serializable, NetSerializable]
public enum DragonRiftState : byte
{
Charging,
AlmostFinished,
Finished,
}

View File

@@ -0,0 +1,13 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Movement.Components;
/// <summary>
/// Is this entity always considered to be touching a wall?
/// i.e. when weightless they're floaty but still have free movement.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class MovementAlwaysTouchingComponent : Component
{
}

View File

@@ -8,6 +8,9 @@ using Robust.Shared.Serialization;
namespace Content.Shared.Movement.Components namespace Content.Shared.Movement.Components
{ {
/// <summary>
/// Ignores gravity entirely.
/// </summary>
[RegisterComponent, NetworkedComponent] [RegisterComponent, NetworkedComponent]
public sealed class MovementIgnoreGravityComponent : Component public sealed class MovementIgnoreGravityComponent : Component
{ {
@@ -17,7 +20,6 @@ namespace Content.Shared.Movement.Components
[DataField("gravityState")] public bool Weightless = false; [DataField("gravityState")] public bool Weightless = false;
} }
[NetSerializable, Serializable] [NetSerializable, Serializable]
public sealed class MovementIgnoreGravityComponentState : ComponentState public sealed class MovementIgnoreGravityComponentState : ComponentState
{ {

View File

@@ -56,55 +56,55 @@ namespace Content.Shared.Movement.Components
/// <summary> /// <summary>
/// Minimum speed a mob has to be moving before applying movement friction. /// Minimum speed a mob has to be moving before applying movement friction.
/// </summary> /// </summary>
[DataField("minimumFrictionSpeed")] [ViewVariables(VVAccess.ReadWrite), DataField("minimumFrictionSpeed")]
public float MinimumFrictionSpeed = DefaultMinimumFrictionSpeed; public float MinimumFrictionSpeed = DefaultMinimumFrictionSpeed;
/// <summary> /// <summary>
/// The negative velocity applied for friction when weightless and providing inputs. /// The negative velocity applied for friction when weightless and providing inputs.
/// </summary> /// </summary>
[DataField("weightlessFriction")] [ViewVariables(VVAccess.ReadWrite), DataField("weightlessFriction")]
public float WeightlessFriction = DefaultWeightlessFriction; public float WeightlessFriction = DefaultWeightlessFriction;
/// <summary> /// <summary>
/// The negative velocity applied for friction when weightless and not providing inputs. /// The negative velocity applied for friction when weightless and not providing inputs.
/// This is essentially how much their speed decreases per second. /// This is essentially how much their speed decreases per second.
/// </summary> /// </summary>
[DataField("weightlessFrictionNoInput")] [ViewVariables(VVAccess.ReadWrite), DataField("weightlessFrictionNoInput")]
public float WeightlessFrictionNoInput = DefaultWeightlessFrictionNoInput; public float WeightlessFrictionNoInput = DefaultWeightlessFrictionNoInput;
/// <summary> /// <summary>
/// The movement speed modifier applied to a mob's total input velocity when weightless. /// The movement speed modifier applied to a mob's total input velocity when weightless.
/// </summary> /// </summary>
[DataField("weightlessModifier")] [ViewVariables(VVAccess.ReadWrite), DataField("weightlessModifier")]
public float WeightlessModifier = DefaultWeightlessModifier; public float WeightlessModifier = DefaultWeightlessModifier;
/// <summary> /// <summary>
/// The acceleration applied to mobs when moving and weightless. /// The acceleration applied to mobs when moving and weightless.
/// </summary> /// </summary>
[DataField("weightlessAcceleration")] [ViewVariables(VVAccess.ReadWrite), DataField("weightlessAcceleration")]
public float WeightlessAcceleration = DefaultWeightlessAcceleration; public float WeightlessAcceleration = DefaultWeightlessAcceleration;
/// <summary> /// <summary>
/// The acceleration applied to mobs when moving. /// The acceleration applied to mobs when moving.
/// </summary> /// </summary>
[DataField("acceleration")] [ViewVariables(VVAccess.ReadWrite), DataField("acceleration")]
public float Acceleration = DefaultAcceleration; public float Acceleration = DefaultAcceleration;
/// <summary> /// <summary>
/// The negative velocity applied for friction. /// The negative velocity applied for friction.
/// </summary> /// </summary>
[DataField("friction")] [ViewVariables(VVAccess.ReadWrite), DataField("friction")]
public float Friction = DefaultFriction; public float Friction = DefaultFriction;
/// <summary> /// <summary>
/// The negative velocity applied for friction. /// The negative velocity applied for friction.
/// </summary> /// </summary>
[DataField("frictionNoInput")] public float? FrictionNoInput = null; [ViewVariables(VVAccess.ReadWrite), DataField("frictionNoInput")] public float? FrictionNoInput = null;
[DataField("baseWalkSpeed")] [ViewVariables(VVAccess.ReadWrite), DataField("baseWalkSpeed")]
public float BaseWalkSpeed { get; set; } = DefaultBaseWalkSpeed; public float BaseWalkSpeed { get; set; } = DefaultBaseWalkSpeed;
[DataField("baseSprintSpeed")] [ViewVariables(VVAccess.ReadWrite), DataField("baseSprintSpeed")]
public float BaseSprintSpeed { get; set; } = DefaultBaseSprintSpeed; public float BaseSprintSpeed { get; set; } = DefaultBaseSprintSpeed;
[ViewVariables] [ViewVariables]

View File

@@ -1,4 +1,5 @@
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Content.Shared.Movement.Events;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Shared.Movement.Systems; namespace Content.Shared.Movement.Systems;
@@ -9,6 +10,12 @@ public sealed class MovementIgnoreGravitySystem : EntitySystem
{ {
SubscribeLocalEvent<MovementIgnoreGravityComponent, ComponentGetState>(GetState); SubscribeLocalEvent<MovementIgnoreGravityComponent, ComponentGetState>(GetState);
SubscribeLocalEvent<MovementIgnoreGravityComponent, ComponentHandleState>(HandleState); SubscribeLocalEvent<MovementIgnoreGravityComponent, ComponentHandleState>(HandleState);
SubscribeLocalEvent<MovementAlwaysTouchingComponent, CanWeightlessMoveEvent>(OnWeightless);
}
private void OnWeightless(EntityUid uid, MovementAlwaysTouchingComponent component, ref CanWeightlessMoveEvent args)
{
args.CanMove = true;
} }
private void HandleState(EntityUid uid, MovementIgnoreGravityComponent component, ref ComponentHandleState args) private void HandleState(EntityUid uid, MovementIgnoreGravityComponent component, ref ComponentHandleState args)

View File

@@ -0,0 +1,20 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Sprite;
[RegisterComponent, NetworkedComponent]
public sealed class RandomSpriteComponent : Component
{
/// <summary>
/// Available colors based on group, parsed layer enum, state, and color.
/// Stored as a list so we can have groups of random sprites (e.g. tech_base + tech_flare for holoparasite)
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("available")]
public List<Dictionary<string, (string State, string? Color)>> Available = new();
/// <summary>
/// Selected colors
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("selected")]
public Dictionary<string, (string State, Color? Color)> Selected = new();
}

View File

@@ -0,0 +1,11 @@
using Robust.Shared.Serialization;
namespace Content.Shared.Sprite;
public abstract class SharedRandomSpriteSystem : EntitySystem {}
[Serializable, NetSerializable]
public sealed class RandomSpriteColorComponentState : ComponentState
{
public Dictionary<string, (string State, Color? Color)> Selected = default!;
}

View File

@@ -1,5 +1,5 @@
atreides.ogg, c-20r.ogg, flaregun.ogg, mateba.ogg, minigun.ogg, mk58.ogg were taken from https://gitlab.com/cmdevs/colonial-warfare/-/tree/32cb5892413243cc74bb2d11df8e3085f8ef1164/sound/weapons atreides.ogg, c-20r.ogg, flaregun.ogg, mateba.ogg, minigun.ogg, mk58.ogg were taken from https://gitlab.com/cmdevs/colonial-warfare/-/tree/32cb5892413243cc74bb2d11df8e3085f8ef1164/sound/weapons
They are licensed under CC-BY-SA 3.0 They are licensed under CC-BY-SA 3.0
taser2.ogg and kinetic_accel.ogg were taken from https://github.com/tgstation/tgstation/tree/88d7dbfc105fbf40284d7b7c4587f8d23c0ac3ac rocket_launcher.ogg and taser2.ogg and kinetic_accel.ogg were taken from https://github.com/tgstation/tgstation/tree/88d7dbfc105fbf40284d7b7c4587f8d23c0ac3ac
It is licensed under CC-BY-SA 3.0 It is licensed under CC-BY-SA 3.0

View File

@@ -2,7 +2,7 @@ devour-action-popup-message-structure = Your jaws dig into thick material..
devour-action-popup-message-fail-target-not-valid = That doesn't look particularly edible. devour-action-popup-message-fail-target-not-valid = That doesn't look particularly edible.
devour-action-popup-message-fail-target-alive = You can't consume creatures that are alive! devour-action-popup-message-fail-target-alive = You can't consume creatures that are alive!
dragon-spawn-action-popup-message-fail-no-eggs = You don't have the stamina to create a carp! dragon-spawn-action-popup-message-fail-no-eggs = You don't have the stamina to do that!
action-name-devour = [color=red]Devour[/color] action-name-devour = [color=red]Devour[/color]
@@ -10,3 +10,17 @@ action-description-devour = Attempt to break a structure with your jaws or swall
action-name-carp-summon = Summon carp action-name-carp-summon = Summon carp
action-description-carp-summon = Summon a carp to aid you at seizing the station! action-description-carp-summon = Summon a carp to aid you at seizing the station!
# Rifts
carp-rift-warning = A rift is causing an unnaturally large energy flux at {$location}. Stop it at all costs!
carp-rift-duplicate = Cannot have 2 charging rifts at the same time!
carp-rift-examine = It is [color=yellow]{$percentage}%[/color] charged!
carp-rift-max = You have reached your maximum amount of rifts
carp-rift-anchor = Rifts require a stable surface to spawn.
carp-rift-weakened = You are unable to summon more rifts in your weakened state.
carp-rift-destroyed = A rift has been destroyed! You are now weakened temporarily.
# Round end
dragon-round-end-summary = The dragons were:
dragon-round-end-dragon = {$name} with {$count} rifts
dragon-round-end-dragon-player = {$name} ({$player}) with {$count} rifts

View File

@@ -307,6 +307,7 @@
baseSprintSpeed : 6 baseSprintSpeed : 6
- type: Sprite - type: Sprite
drawdepth: Mobs drawdepth: Mobs
netsync: false
layers: layers:
- map: ["enum.DamageStateVisualLayers.Base"] - map: ["enum.DamageStateVisualLayers.Base"]
state: butterfly state: butterfly
@@ -327,16 +328,10 @@
0: Alive 0: Alive
5: Critical 5: Critical
10: Dead 10: Dead
- type: RandomSpriteColor - type: RandomSprite
state: butterfly available:
colors: - enum.DamageStateVisualLayers.Base:
blue: "#1861d5" butterfly: Rainbow
red: "#951710"
pink: "#d5188d"
brown: "#a05212"
green: "#0e7f1b"
cyan: "#18a2d5"
yellow: "#d58c18"
- type: Appearance - type: Appearance
- type: DamageStateVisuals - type: DamageStateVisuals
states: states:

View File

@@ -29,7 +29,7 @@
- MobMask - MobMask
layer: layer:
- MobLayer - MobLayer
- type: MovementIgnoreGravity - type: MovementAlwaysTouching
- type: MobState - type: MobState
thresholds: thresholds:
0: Alive 0: Alive

View File

@@ -1,77 +1,95 @@
- type: entity - type: entity
name: space carp name: space carp
id: MobCarp id: BaseMobCarp
parent: SimpleSpaceMobBase parent: SimpleSpaceMobBase
description: It's a space carp. description: It's a space carp.
abstract: true
components: components:
- type: InputMover - type: InputMover
- type: MobMover - type: MobMover
- type: UtilityNPC - type: UtilityNPC
behaviorSets: behaviorSets:
- Idle - Idle
- UnarmedAttackHostiles - UnarmedAttackHostiles
- type: AiFactionTag - type: AiFactionTag
factions: factions:
- SimpleHostile - SimpleHostile
- type: Sprite - type: Sprite
drawdepth: Mobs drawdepth: Mobs
layers: netsync: false
- map: ["enum.DamageStateVisualLayers.Base"] layers:
state: alive - map: [ "enum.DamageStateVisualLayers.Base" ]
sprite: Mobs/Aliens/Carps/space.rsi state: base
- type: CombatMode sprite: Mobs/Aliens/Carps/space.rsi
disarmAction: - map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ]
enabled: false state: mouth
autoPopulate: false sprite: Mobs/Aliens/Carps/space.rsi
name: action-name-disarm shader: unshaded
- type: Physics - type: RandomSprite
- type: Fixtures available:
fixtures: - enum.DamageStateVisualLayers.Base:
- shape: base: Rainbow
!type:PhysShapeCircle enum.DamageStateVisualLayers.BaseUnshaded:
radius: 0.40 mouth: ""
mass: 40 - type: CombatMode
mask: disarmAction:
- MobMask enabled: false
layer: autoPopulate: false
- MobLayer name: action-name-disarm
- type: MobState - type: Physics
thresholds: - type: Fixtures
0: Alive fixtures:
50: Critical - shape:
100: Dead !type:PhysShapeCircle
- type: MovementIgnoreGravity radius: 0.40
- type: Appearance mass: 40
- type: DamageStateVisuals mask:
states: - MobMask
Alive: layer:
Base: alive - MobLayer
Critical: - type: MobState
Base: crit thresholds:
Dead: 0: Alive
Base: dead 50: Critical
- type: Butcherable 100: Dead
spawned: - type: MovementAlwaysTouching
- id: FoodMeatFish - type: Appearance
amount: 2 - type: DamageStateVisuals
- type: MeleeWeapon states:
range: 1.5 Alive:
arcwidth: 0 Base: base
arc: bite BaseUnshaded: mouth
hitSound: Dead:
path: /Audio/Effects/bite.ogg Base: base_dead
damage: BaseUnshaded: dead_mouth
types: - type: Butcherable
Piercing: 5 spawned:
Slash: 10 - id: FoodMeatFish
- type: ReplacementAccent amount: 2
accent: genericAggressive - type: MeleeWeapon
- type: TypingIndicator range: 1.5
proto: alien arcwidth: 0
- type: NoSlip arc: bite
- type: Tag hitSound:
tags: path: /Audio/Effects/bite.ogg
- Carp damage:
types:
Piercing: 5
Slash: 10
- type: TypingIndicator
proto: alien
- type: NoSlip
- type: Tag
tags:
- Carp
- DoorBumpOpener
- type: entity
parent: BaseMobCarp
id: MobCarp
components:
- type: ReplacementAccent
accent: genericAggressive
- type: entity - type: entity
name: magicarp name: magicarp
@@ -79,14 +97,14 @@
id: MobCarpMagic id: MobCarpMagic
description: Looks like some kind of fish. Might be magical. description: Looks like some kind of fish. Might be magical.
components: components:
- type: Sprite - type: Sprite
drawdepth: Mobs drawdepth: Mobs
layers: layers:
- map: ["enum.DamageStateVisualLayers.Base"] - map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive state: base
sprite: Mobs/Aliens/Carps/magic.rsi sprite: Mobs/Aliens/Carps/magic.rsi
- type: TypingIndicator - type: TypingIndicator
proto: guardian proto: guardian
- type: entity - type: entity
name: holocarp name: holocarp
@@ -94,37 +112,37 @@
id: MobCarpHolo id: MobCarpHolo
description: Carp made out of holographic energies. Sadly for you, it is very much real. description: Carp made out of holographic energies. Sadly for you, it is very much real.
components: components:
- type: Sprite - type: Sprite
drawdepth: Mobs drawdepth: Mobs
layers: layers:
- map: ["enum.DamageStateVisualLayers.Base"] - map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive state: base
sprite: Mobs/Aliens/Carps/holo.rsi sprite: Mobs/Aliens/Carps/holo.rsi
- type: Physics - type: Physics
- type: Fixtures - type: Fixtures
fixtures: fixtures:
- shape: - shape:
!type:PhysShapeCircle !type:PhysShapeCircle
radius: 0.40 radius: 0.40
mass: 5 mass: 5
mask: mask:
- MobMask - MobMask
layer: layer:
- Opaque - Opaque
- type: TypingIndicator - type: TypingIndicator
proto: robot proto: robot
- type: entity - type: entity
id: MobCarpSalvage id: MobCarpSalvage
parent: MobCarp parent: MobCarp
suffix: "Salvage Ruleset" suffix: "Salvage Ruleset"
components: components:
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
prob: 0.33 prob: 0.33
name: space carp on salvage wreck name: space carp on salvage wreck
description: | description: |
Defend the loot inside the salvage wreck! Defend the loot inside the salvage wreck!
- type: SalvageMobRestrictions - type: SalvageMobRestrictions
- type: entity - type: entity
name: space carp name: space carp
@@ -132,8 +150,7 @@
suffix: DragonBrood suffix: DragonBrood
parent: MobCarp parent: MobCarp
components: components:
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
makeSentient: true makeSentient: true
name: Sentient Carp name: Sentient Carp
description: Help the dragon flood the station with carps! description: Help the dragon flood the station with carps!

View File

@@ -38,7 +38,7 @@
thresholds: thresholds:
0: Alive 0: Alive
15: Dead 15: Dead
- type: MovementIgnoreGravity - type: MovementAlwaysTouching
- type: Appearance - type: Appearance
- type: DamageStateVisuals - type: DamageStateVisuals
states: states:

View File

@@ -5,106 +5,112 @@
suffix: suffix:
description: A flying leviathan, loosely related to space carps. description: A flying leviathan, loosely related to space carps.
components: components:
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
makeSentient: true makeSentient: true
name: Space dragon! name: Space dragon
description: Crash, roast, flood the station with carps! description: Call in 3 carp rifts and take over this quadrant! You have only 5 minutes in between each rift before you will disappear.
- type: Speech - type: Speech
- type: CombatMode - type: CombatMode
disarmAction: disarmAction:
enabled: false enabled: false
autoPopulate: false autoPopulate: false
name: action-name-disarm name: action-name-disarm
- type: MobMover - type: MobMover
- type: InputMover - type: InputMover
- type: MovementSpeedModifier - type: MovementSpeedModifier
baseWalkSpeed : 5 baseWalkSpeed: 3
baseSprintSpeed : 5 baseSprintSpeed: 5
- type: Sprite weightlessModifier: 1.5
sprite: Mobs/Aliens/Carps/dragon.rsi - type: RandomSprite
noRot: true available:
# TODO: Randomise colors when RandomSpriteColor isn't poopoo - enum.DamageStateVisualLayers.Base:
layers: alive: Rainbow
- map: [ "enum.DamageStateVisualLayers.Base" ] - type: Sprite
state: alive sprite: Mobs/Aliens/Carps/dragon.rsi
- map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ] netsync: false
state: alive-unshaded noRot: true
shader: unshaded layers:
- type: Appearance - map: [ "enum.DamageStateVisualLayers.Base" ]
- type: DamageStateVisuals state: alive
states: - map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ]
Alive: state: alive-unshaded
Base: alive shader: unshaded
BaseUnshaded: alive-unshaded - type: Appearance
Critical: - type: DamageStateVisuals
Base: crit states:
Dead: Alive:
Base: dead Base: alive
BaseUnshaded: dead-unshaded BaseUnshaded: alive-unshaded
- type: Physics Critical:
bodyType: KinematicController Base: crit
- type: Fixtures Dead:
fixtures: Base: dead
- shape: BaseUnshaded: dead-unshaded
!type:PhysShapeCircle - type: Physics
radius: 0.40 bodyType: KinematicController
mass: 50 - type: Fixtures
mask: fixtures:
- FlyingMobMask - shape:
layer: !type:PhysShapeCircle
- FlyingMobLayer radius: 0.40
- type: MobState mass: 50
thresholds: mask:
0: Alive - FlyingMobMask
450: Critical layer:
500: Dead - FlyingMobLayer
- type: Metabolizer - type: MobState
solutionOnBody: false thresholds:
updateFrequency: 0.25 0: Alive
metabolizerTypes: [Dragon] 450: Critical
groups: 500: Dead
- id: Medicine - type: Metabolizer
- id: Poison solutionOnBody: false
- type: MovementIgnoreGravity updateFrequency: 0.25
- type: NoSlip metabolizerTypes: [ Dragon ]
- type: Butcherable groups:
spawned: - id: Medicine
- id: FoodMeatDragon - id: Poison
amount: 2 - type: MovementAlwaysTouching
- type: InteractionPopup - type: NoSlip
successChance: 0.25 # It's no goose, but you better smell like carp. - type: Butcherable
interactSuccessString: petting-success-dragon spawned:
interactFailureString: petting-failure-dragon - id: FoodMeatDragon
interactFailureSound: amount: 2
path: /Audio/Animals/space_dragon_roar.ogg - type: InteractionPopup
soundPerceivedByOthers: false # A 75% chance for a loud roar would get old fast. successChance: 0.25 # It's no goose, but you better smell like carp.
- type: MeleeWeapon interactSuccessString: petting-success-dragon
hitSound: interactFailureString: petting-failure-dragon
path: /Audio/Effects/bite.ogg interactFailureSound:
damage: path: /Audio/Animals/space_dragon_roar.ogg
types: soundPerceivedByOthers: false # A 75% chance for a loud roar would get old fast.
Piercing: 15 - type: MeleeWeapon
Slash: 15 hitSound:
- type: Dragon path: /Audio/Effects/bite.ogg
spawnsLeft: 2 damage:
spawnsProto: MobCarpDragon types:
devourAction: Piercing: 15
event: !type:DragonDevourActionEvent Slash: 15
icon: Interface/Actions/devour.png - type: Dragon
name: action-name-devour spawnsLeft: 2
description: action-description-devour spawnsProto: MobCarpDragon
devourChemical: Ichor devourAction:
devourHealRate: 15.0 event: !type:DragonDevourActionEvent
whitelist: icon: Interface/Actions/devour.png
components: name: action-name-devour
- MobState description: action-description-devour
- Door devourChemical: Ichor
tags: devourHealRate: 15.0
- Wall whitelist:
spawnAction: components:
event: !type:DragonSpawnActionEvent - MobState
icon: Interface/Actions/carp_summon.png - Door
name: action-name-carp-summon tags:
description: action-description-carp-summon - Wall
useDelay: 5 spawnRiftAction:
event: !type:DragonSpawnRiftActionEvent
icon:
sprite: Interface/Actions/carp_rift.rsi
state: icon
name: action-name-carp-rift
description: action-description-carp-rift
useDelay: 1

View File

@@ -5,78 +5,94 @@
id: MobGuardianBase id: MobGuardianBase
description: guardian description: guardian
components: components:
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
makeSentient: true makeSentient: true
name: Guardian name: Guardian
description: Listen to your owner. Don't tank damage. Punch people hard. description: Listen to your owner. Don't tank damage. Punch people hard.
- type: Input - type: Input
context: "human" context: "human"
- type: MobMover - type: MobMover
- type: InputMover - type: InputMover
- type: MovementSpeedModifier - type: MovementSpeedModifier
baseWalkSpeed : 7 baseWalkSpeed: 4
baseSprintSpeed : 7 baseSprintSpeed: 5.5
- type: DamageOnHighSpeedImpact - type: DamageOnHighSpeedImpact
damage: damage:
types: types:
Blunt: 5 Blunt: 5
soundHit: soundHit:
path: /Audio/Effects/hit_kick.ogg path: /Audio/Effects/hit_kick.ogg
# TODO: Randomise sprites and randomise the layer color - type: RandomSprite
- type: Sprite available:
drawdepth: Mobs - enum.DamageStateVisualLayers.Base:
sprite: Mobs/Aliens/Guardians/guardians.rsi magic_base: ""
layers: enum.DamageStateVisualLayers.BaseUnshaded:
- state: tech_base magic_flare: Sixteen
- state: tech_flare - enum.DamageStateVisualLayers.Base:
color: "#40a7d7" miner_base: ""
shader: unshaded enum.DamageStateVisualLayers.BaseUnshaded:
noRot: true miner_flare: Sixteen
- type: Clickable - enum.DamageStateVisualLayers.Base:
- type: InteractionOutline tech_base: ""
- type: Physics enum.DamageStateVisualLayers.BaseUnshaded:
bodyType: KinematicController tech_flare: Sixteen
- type: Fixtures - type: Sprite
fixtures: drawdepth: Mobs
- shape: sprite: Mobs/Aliens/Guardians/guardians.rsi
!type:PhysShapeCircle netsync: false
radius: 0.35 layers:
mass: 10 - state: tech_base
mask: map: [ "enum.DamageStateVisualLayers.Base" ]
- FlyingMobMask - state: tech_flare
layer: map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ]
- FlyingMobLayer color: "#40a7d7"
- type: Damageable shader: unshaded
damageContainer: Biological noRot: true
- type: MobState - type: Clickable
thresholds: - type: InteractionOutline
0: Alive - type: Physics
- type: HeatResistance bodyType: KinematicController
- type: CombatMode - type: Fixtures
- type: Internals fixtures:
- type: Examiner - shape:
- type: Speech !type:PhysShapeCircle
- type: TypingIndicator radius: 0.35
proto: guardian mass: 10
- type: Pullable mask:
- type: MeleeWeapon - FlyingMobMask
range: 2 layer:
arcwidth: 30 - FlyingMobLayer
arc: fist - type: Damageable
cooldownTime: 0.7 damageContainer: Biological
arcCooldownTime: 0.7 - type: MobState
damage: thresholds:
types: 0: Alive
Blunt: 22 - type: HeatResistance
- type: Actions - type: CombatMode
- type: Guardian - type: Internals
- type: InteractionPopup - type: Examiner
interactSuccessString: petting-success-holo - type: Speech
interactFailureString: petting-failure-holo - type: TypingIndicator
successChance: 0.7 proto: guardian
- type: Tag - type: Pullable
tags: - type: MeleeWeapon
- CannotSuicide range: 2
arcwidth: 30
arc: fist
cooldownTime: 0.7
arcCooldownTime: 0.7
damage:
types:
Blunt: 22
- type: Actions
- type: Guardian
- type: InteractionPopup
interactSuccessString: petting-success-holo
interactFailureString: petting-failure-holo
successChance: 0.7
- type: Tag
tags:
- CannotSuicide
# From the uplink injector # From the uplink injector
- type: entity - type: entity
@@ -85,20 +101,22 @@
parent: MobGuardianBase parent: MobGuardianBase
description: A mesmerising whirl of hard-light patterns weaves a marvelous, yet oddly familiar visage. It stands proud, tuning into its owner's life to sustain itself. description: A mesmerising whirl of hard-light patterns weaves a marvelous, yet oddly familiar visage. It stands proud, tuning into its owner's life to sustain itself.
components: components:
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
makeSentient: true makeSentient: true
name: Holoparasite name: Holoparasite
description: Listen to your owner. Don't tank damage. Punch people hard. description: Listen to your owner. Don't tank damage. Punch people hard.
- type: NameIdentifier - type: NameIdentifier
group: Holoparasite group: Holoparasite
- type: TypingIndicator - type: TypingIndicator
proto: holo proto: holo
- type: Sprite - type: Sprite
layers: layers:
- state: tech_base - state: tech_base
- state: tech_flare map: [ "enum.DamageStateVisualLayers.Base" ]
color: "#40a7d7" - state: tech_flare
shader: unshaded map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ]
color: "#40a7d7"
shader: unshaded
# From Wizard deck of cards # From Wizard deck of cards
- type: entity - type: entity
@@ -107,13 +125,19 @@
id: MobIfritGuardian id: MobIfritGuardian
description: A corrupted jinn, ripped from fitra to serve the wizard's petty needs. It stands wicked, tuning into it's owner's life to sustain itself. description: A corrupted jinn, ripped from fitra to serve the wizard's petty needs. It stands wicked, tuning into it's owner's life to sustain itself.
components: components:
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
makeSentient: true makeSentient: true
name: Ifrit name: Ifrit
description: Listen to your owner. Don't tank damage. Punch people hard. description: Listen to your owner. Don't tank damage. Punch people hard.
- type: Sprite - type: RandomSprite
layers: available:
- state: magic_base - enum.DamageStateVisualLayers.BaseUnshaded:
- state: magic_flare magic_flare: Sixteen
color: "#d14730" - type: Sprite
shader: unshaded layers:
- state: magic_base
map: [ "enum.DamageStateVisualLayers.Base" ]
- state: magic_flare
map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ]
color: "#40a7d7"
shader: unshaded

View File

@@ -176,14 +176,20 @@
- Nugget - Nugget
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Food/Baked/nuggets.rsi sprite: Objects/Consumable/Food/Baked/nuggets.rsi
state: tendie layers:
netsync: true - state: tendie
- type: RandomSpriteState map: [ "enum.DamageStateVisualLayers.Base" ]
spriteStates: netsync: false
- tendie - type: RandomSprite
- lizard available:
- star - enum.DamageStateVisualLayers.Base:
- corgi tendie: ""
- enum.DamageStateVisualLayers.Base:
lizard: ""
- enum.DamageStateVisualLayers.Base:
star: ""
- enum.DamageStateVisualLayers.Base:
corgi: ""
- type: SolutionContainerManager - type: SolutionContainerManager
solutions: solutions:
food: food:

View File

@@ -126,8 +126,13 @@
components: components:
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Food/egg.rsi sprite: Objects/Consumable/Food/egg.rsi
state: icon layers:
- type: RandomSpriteState - state: icon
spriteStates: map: [ "enum.DamageStateVisualLayers.Base" ]
- icon netsync: false
- white - type: RandomSprite
available:
- enum.DamageStateVisualLayers.Base:
icon: ""
- enum.DamageStateVisualLayers.Base:
white: ""

View File

@@ -237,11 +237,16 @@
components: components:
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Food/frozen.rsi sprite: Objects/Consumable/Food/frozen.rsi
state: stick netsync: false
- type: RandomSpriteState layers:
spriteStates: - state: stick
- stick map: [ "enum.DamageStateVisualLayers.Base" ]
- stick2 - type: RandomSprite
available:
- enum.DamageStateVisualLayers.Base:
stick: ""
- enum.DamageStateVisualLayers.Base:
stick2: ""
- type: Tag - type: Tag
tags: tags:
- Trash - Trash

View File

@@ -87,12 +87,14 @@
tags: tags:
- Raw - Raw
- type: Sprite - type: Sprite
netsync: true netsync: false
state: bacon state: bacon
- type: RandomSpriteState - type: RandomSprite
spriteStates: available:
- bacon - enum.DamageStateVisualLayers.Base:
- bacon2 bacon: ""
- enum.DamageStateVisualLayers.Base:
bacon2: ""
- type: SolutionContainerManager - type: SolutionContainerManager
solutions: solutions:
food: food:
@@ -501,11 +503,13 @@
layers: layers:
- state: plate-meat - state: plate-meat
- state: bacon-cooked - state: bacon-cooked
- type: RandomSpriteState map: [ "enum.DamageStateVisualLayers.Base" ]
spriteLayer: 1 - type: RandomSprite
spriteStates: available:
- bacon-cooked - enum.DamageStateVisualLayers.Base:
- bacon2-cooked bacon-cooked: ""
- enum.DamageStateVisualLayers.Base:
bacon2-cooked: ""
- type: SolutionContainerManager - type: SolutionContainerManager
solutions: solutions:
food: food:
@@ -608,12 +612,16 @@
- type: Food - type: Food
trash: FoodPlateSmall trash: FoodPlateSmall
- type: Sprite - type: Sprite
netsync: true netsync: false
state: chicken-fried layers:
- type: RandomSpriteState - state: chicken-fried
spriteStates: map: [ "enum.DamageStateVisualLayers.Base" ]
- chicken-fried - type: RandomSprite
- chicken2-fried available:
- enum.DamageStateVisualLayers.Base:
chicken-fried: ""
- enum.DamageStateVisualLayers.Base:
chicken2-fried: ""
- type: SolutionContainerManager - type: SolutionContainerManager
solutions: solutions:
food: food:

View File

@@ -5,13 +5,19 @@
description: It's a shard of some unknown material. description: It's a shard of some unknown material.
components: components:
- type: Sprite - type: Sprite
sprite: Objects/Materials/Shards/shard.rsi layers:
state: shard1 - sprite: Objects/Materials/Shards/shard.rsi
- type: RandomSpriteState state: shard1
spriteStates: map: [ "enum.DamageStateVisualLayers.Base" ]
- shard1 netsync: false
- shard2 - type: RandomSprite
- shard3 available:
- enum.DamageStateVisualLayers.Base:
shard1: ""
- enum.DamageStateVisualLayers.Base:
shard2: ""
- enum.DamageStateVisualLayers.Base:
shard3: ""
- type: ItemCooldown - type: ItemCooldown
- type: MeleeWeapon - type: MeleeWeapon
damage: damage:

View File

@@ -6,8 +6,10 @@
components: components:
- type: Sprite - type: Sprite
sprite: Objects/Misc/books.rsi sprite: Objects/Misc/books.rsi
netsync: false
layers: layers:
- state: book0 - state: book0
map: [ "enum.DamageStateVisualLayers.Base" ]
- type: Paper - type: Paper
- type: ActivatableUI - type: ActivatableUI
key: enum.PaperUiKey.Key key: enum.PaperUiKey.Key
@@ -21,15 +23,23 @@
id: BookRandom id: BookRandom
suffix: random suffix: random
components: components:
- type: RandomSpriteState - type: RandomSprite
spriteLayer: 0 available:
spriteStates: - enum.DamageStateVisualLayers.Base:
- book0 book0: ""
- book1 - enum.DamageStateVisualLayers.Base:
- book2 book1: ""
- book3 - enum.DamageStateVisualLayers.Base:
- book4 book2: ""
- book5 - enum.DamageStateVisualLayers.Base:
- book6 book3: ""
- book7 - enum.DamageStateVisualLayers.Base:
- book8 book4: ""
- enum.DamageStateVisualLayers.Base:
book5: ""
- enum.DamageStateVisualLayers.Base:
book6: ""
- enum.DamageStateVisualLayers.Base:
book7: ""
- enum.DamageStateVisualLayers.Base:
book8: ""

View File

@@ -4,21 +4,34 @@
id: Lighter id: Lighter
description: "A simple plastic cigarette lighter." description: "A simple plastic cigarette lighter."
components: components:
- type: RandomSpriteState #this has to be before sprite component for the flame to toggle right because weldercomponent behaves weird (and i dont trust myself to fix it right) # Sloth: What is this comment ?????????
spriteStates: - type: RandomSprite #this has to be before sprite component for the flame to toggle right because weldercomponent behaves weird (and i dont trust myself to fix it right)
- basic_icon_base-1 available:
- basic_icon_base-2 - enum.WelderLayers.Base:
- basic_icon_base-3 basic_icon_base-1: ""
- basic_icon_base-4 - enum.WelderLayers.Base:
- basic_icon_base-5 basic_icon_base-2: ""
- basic_icon_base-6 - enum.WelderLayers.Base:
- basic_icon_base-7 basic_icon_base-3: ""
- basic_icon_base-8 - enum.WelderLayers.Base:
- basic_icon_base-9 basic_icon_base-4: ""
- basic_icon_base-10 - enum.WelderLayers.Base:
- basic_icon_base-11 basic_icon_base-5: ""
- enum.WelderLayers.Base:
basic_icon_base-6: ""
- enum.WelderLayers.Base:
basic_icon_base-7: ""
- enum.WelderLayers.Base:
basic_icon_base-8: ""
- enum.WelderLayers.Base:
basic_icon_base-9: ""
- enum.WelderLayers.Base:
basic_icon_base-10: ""
- enum.WelderLayers.Base:
basic_icon_base-11: ""
- type: Sprite - type: Sprite
sprite: Objects/Tools/lighters.rsi sprite: Objects/Tools/lighters.rsi
netsync: false
layers: layers:
- state: icon_map - state: icon_map
map: ["enum.WelderLayers.Base"] map: ["enum.WelderLayers.Base"]
@@ -69,16 +82,10 @@
id: CheapLighter id: CheapLighter
description: "A dangerously inexpensive plastic lighter, don't burn your thumb!" description: "A dangerously inexpensive plastic lighter, don't burn your thumb!"
components: components:
- type: RandomSpriteColor - type: RandomSprite
state: cheap_icon_base available:
colors: - enum.WelderLayers.Base:
blue: "#1861d5" cheap_icon_base: Rainbow
red: "#951710"
pink: "#d5188d"
brown: "#a05212"
green: "#0e7f1b"
cyan: "#18a2d5"
yellow: "#d58c18" #all colours proudly stolen from wirecutters
- type: Sprite - type: Sprite
sprite: Objects/Tools/lighters.rsi sprite: Objects/Tools/lighters.rsi
layers: layers:

View File

@@ -13,8 +13,10 @@
- Wirecutter - Wirecutter
- type: Sprite - type: Sprite
sprite: Objects/Tools/wirecutters.rsi sprite: Objects/Tools/wirecutters.rsi
netsync: false
layers: layers:
- state: cutters-map - state: cutters
map: [ "enum.DamageStateVisualLayers.Base" ]
- state: cutters-cutty-thingy - state: cutters-cutty-thingy
- type: ItemCooldown - type: ItemCooldown
- type: MeleeWeapon - type: MeleeWeapon
@@ -26,16 +28,10 @@
- Cutting - Cutting
useSound: useSound:
path: /Audio/Items/wirecutter.ogg path: /Audio/Items/wirecutter.ogg
- type: RandomSpriteColor - type: RandomSprite
state: cutters available:
colors: - enum.DamageStateVisualLayers.Base:
blue: "#1861d5" cutters: Rainbow
red: "#951710"
pink: "#d5188d"
brown: "#a05212"
green: "#0e7f1b"
cyan: "#18a2d5"
yellow: "#d58c18"
- type: Item - type: Item
sprite: Objects/Tools/wirecutters.rsi sprite: Objects/Tools/wirecutters.rsi
@@ -53,8 +49,10 @@
- Screwdriver - Screwdriver
- type: Sprite - type: Sprite
sprite: Objects/Tools/screwdriver.rsi sprite: Objects/Tools/screwdriver.rsi
netsync: false
layers: layers:
- state: screwdriver-map - state: screwdriver
map: [ "enum.DamageStateVisualLayers.Base" ]
- state: screwdriver-screwybits - state: screwdriver-screwybits
- type: Item - type: Item
sprite: Objects/Tools/screwdriver.rsi sprite: Objects/Tools/screwdriver.rsi
@@ -68,16 +66,10 @@
- Screwing - Screwing
useSound: useSound:
collection: Screwdriver collection: Screwdriver
- type: RandomSpriteColor - type: RandomSprite
state: screwdriver available:
colors: - enum.DamageStateVisualLayers.Base:
blue: "#1861d5" screwdriver: Rainbow
red: "#ff0000"
pink: "#d5188d"
brown: "#a05212"
green: "#0e7f1b"
cyan: "#18a2d5"
yellow: "#ffa500"
- type: entity - type: entity
name: wrench name: wrench

View File

@@ -9,15 +9,21 @@
layers: layers:
- state: base - state: base
- state: book-0 - state: book-0
- type: RandomSpriteState map: [ "enum.DamageStateVisualLayers.Base" ]
spriteLayer: 1 - type: RandomSprite
spriteStates: available:
- book-0 - enum.DamageStateVisualLayers.Base:
- book-1 book-0: ""
- book-2 - enum.DamageStateVisualLayers.Base:
- book-3 book-1: ""
- book-4 - enum.DamageStateVisualLayers.Base:
- book-5 book-2: ""
- enum.DamageStateVisualLayers.Base:
book-3: ""
- enum.DamageStateVisualLayers.Base:
book-4: ""
- enum.DamageStateVisualLayers.Base:
book-5: ""
- type: Damageable - type: Damageable
damageModifierSet: Wood damageModifierSet: Wood
damageContainer: Inorganic damageContainer: Inorganic

View File

@@ -0,0 +1,39 @@
- type: entity
id: CarpRift
name: carp rift
description: A rift akin to the ones space carp use to travel long distances.
placement:
mode: SnapgridCenter
components:
- type: DragonRift
- type: Transform
anchored: true
- type: Physics
bodyType: Static
- type: Fixtures
- type: Sprite
netsync: false
layers:
- sprite: Structures/Specific/carp_rift.rsi
state: icon
color: "#569fff"
shader: unshaded
- type: InteractionOutline
- type: Clickable
- type: PointLight
enabled: true
color: "#366db5"
radius: 10.0
energy: 5.0
netsync: false
- type: Damageable
damageContainer: Inorganic
damageModifierSet: Metallic
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 300
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]

View File

@@ -32,6 +32,17 @@
earliestStart: 15 earliestStart: 15
maxOccurrences: 3 maxOccurrences: 3
- type: gameRule
id: Dragon
config:
!type:StationEventRuleConfiguration
id: Dragon
weight: 10
endAfter: 2
earliestStart: 15
maxOccurrences: 7
minimumPlayers: 15
- type: gameRule - type: gameRule
id: FalseAlarm id: FalseAlarm
config: config:

View File

@@ -1,4 +1,16 @@
- type: palette - type: palette
id: Rainbow
name: Rainbow
colors:
blue: "#1861d5"
red: "#951710"
pink: "#d5188d"
brown: "#a05212"
green: "#0e7f1b"
cyan: "#18a2d5"
yellow: "#d58c18"
- type: palette
id: Sixteen id: Sixteen
name: Sixteen name: Sixteen
colors: colors:

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,21 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "https://github.com/tgstation/tgstation/blob/19da0cee1869bad0186d54d6bcd8a55ed30b9db6/icons/obj/carp_rift.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon",
"delays": [
[
0.2,
0.2,
0.2
]
]
}
]
}

View File

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -33,29 +33,7 @@
] ]
}, },
{ {
"name": "crit", "name": "base",
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1
]
]
},
{
"name": "alive",
"directions": 4, "directions": 4,
"delays": [ "delays": [
[ [

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 B

View File

@@ -14,10 +14,7 @@
"name": "dead" "name": "dead"
}, },
{ {
"name": "crit" "name": "base",
},
{
"name": "alive",
"directions": 4, "directions": 4,
"delays": [ "delays": [
[ [

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

View File

@@ -11,13 +11,39 @@
"name": "icon" "name": "icon"
}, },
{ {
"name": "dead" "name": "mouth",
"directions": 4,
"delays": [
[
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2
],
[
0.2,
0.2,
0.2
]
]
}, },
{ {
"name": "crit" "name": "dead_mouth"
}, },
{ {
"name": "alive", "name": "base_dead"
},
{
"name": "base",
"directions": 4, "directions": 4,
"delays": [ "delays": [
[ [

Binary file not shown.

After

Width:  |  Height:  |  Size: 590 B

View File

@@ -1 +0,0 @@
Eventually we'll have some kind of unifying "RandomSpriteStateComponent" but til now i'm just leaving these here in protest.

View File

@@ -1 +0,0 @@
Eventually we'll have some kind of unifying "RandomSpriteStateComponent" but til now i'm just leaving these here in protest.

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@@ -0,0 +1,21 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "https://github.com/tgstation/tgstation/blob/19da0cee1869bad0186d54d6bcd8a55ed30b9db6/icons/obj/carp_rift.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon",
"delays": [
[
0.2,
0.2,
0.2
]
]
}
]
}