ports most IPC code

This commit is contained in:
iesteed 2025-01-24 21:08:31 -05:00
parent ee6164793b
commit 021dc93627
354 changed files with 9197 additions and 28 deletions

View File

@ -23,6 +23,7 @@ using Content.Shared.Station.Components;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Map.Components;
using Content.Shared._EE.Silicon.Components; // Goobstation
namespace Content.IntegrationTests.Tests.GameRules;
@ -229,7 +230,8 @@ public sealed class NukeOpsTest
for (var tick = 0; tick < totalTicks; tick += increment)
{
await pair.RunTicksSync(increment);
Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold));
if (!entMan.HasComponent<SiliconComponent>(player)) // Goobstation
Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold));
Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
}

View File

@ -16,6 +16,8 @@ using Content.Shared.Station;
using Robust.Shared.Console;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Content.Server._EE.Silicon.IPC; // Goobstation
using Content.Shared.Radio.Components; // Goobstation
namespace Content.Server.Administration.Commands
{
@ -163,7 +165,12 @@ namespace Content.Server.Administration.Commands
var stationSpawning = entityManager.System<SharedStationSpawningSystem>();
stationSpawning.EquipRoleLoadout(target, roleLoadout, jobProto);
}
if (entityManager.HasComponent<EncryptionKeyHolderComponent>(target))
{
var encryption = new InternalEncryptionKeySpawner();
encryption.TryInsertEncryptionKey(target, startingGear, entityManager);
}
return true;
}
}

View File

@ -13,6 +13,7 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Power;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using Content.Shared._EE.Silicon.Components;
namespace Content.Server.Bed
{
@ -70,7 +71,8 @@ namespace Content.Server.Bed
foreach (var healedEntity in strapComponent.BuckledEntities)
{
if (_mobStateSystem.IsDead(healedEntity))
if (_mobStateSystem.IsDead(healedEntity)
|| HasComp<SiliconComponent>(healedEntity)) // Goobstation
continue;
var damage = bedComponent.Damage;

View File

@ -13,6 +13,7 @@ using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Robust.Shared.Player;
using Content.Shared._EE.Silicon.Components;
namespace Content.Server.Chat;
@ -164,7 +165,10 @@ public sealed class SuicideSystem : EntitySystem
return;
}
args.DamageType ??= "Bloodloss";
if (HasComp<SiliconComponent>(victim)) // Goobstation
args.DamageType ??= "Shock";
else
args.DamageType ??= "Bloodloss";
_suicide.ApplyLethalDamage(victim, args.DamageType);
args.Handled = true;
}

View File

@ -62,7 +62,8 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
[ValidatePrototypeId<DamageTypePrototype>]
private const string DamageType = "Shock";
// Multiply and shift the log scale for shock damage.
// Yes, this is absurdly small for a reason.
public const float ElectrifiedDamagePerWatt = 0.0015f; // Goobstation - This information is allowed to be public, and was needed in BatteryElectrocuteChargeSystem.cs
private const float RecursiveDamageMultiplier = 0.75f;
private const float RecursiveTimeMultiplier = 0.8f;
@ -300,7 +301,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
|| !DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects))
return false;
RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient), true);
RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient, shockDamage), true); // Goobstation
return true;
}
@ -350,7 +351,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
electrocutionComponent.Electrocuting = uid;
electrocutionComponent.Source = sourceUid;
RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient), true);
RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient, shockDamage), true); // Goobstation
return true;
}

View File

@ -15,4 +15,10 @@ public sealed partial class DeathgaspComponent : Component
/// </summary>
[DataField("prototype", customTypeSerializer:typeof(PrototypeIdSerializer<EmotePrototype>))]
public string Prototype = "DefaultDeathgasp";
/// <summary>
/// Goobstation: Makes sure that the deathgasp is only displayed if the entity went critical before dying
/// </summary>
[DataField]
public bool NeedsCritical = true;
}

View File

@ -21,7 +21,9 @@ public sealed class DeathgaspSystem: EntitySystem
private void OnMobStateChanged(EntityUid uid, DeathgaspComponent component, MobStateChangedEvent args)
{
// don't deathgasp if they arent going straight from crit to dead
if (args.NewMobState != MobState.Dead || args.OldMobState != MobState.Critical)
if (component.NeedsCritical // Goobstation
&& args.OldMobState != MobState.Critical
|| args.NewMobState != MobState.Dead)
return;
Deathgasp(uid, component);

View File

@ -28,6 +28,7 @@ using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using Content.Server._EE.Silicon.IPC; // Goobstation
namespace Content.Server.Station.Systems;
@ -50,7 +51,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
[Dependency] private readonly PdaSystem _pdaSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly InternalEncryptionKeySpawner _internalEncryption = default!; // Goobstation
private bool _randomizeCharacters;
/// <inheritdoc/>
@ -178,6 +179,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
{
var startingGear = _prototypeManager.Index<StartingGearPrototype>(prototype.StartingGear);
EquipStartingGear(entity.Value, startingGear, raiseEvent: false);
_internalEncryption.TryInsertEncryptionKey(entity.Value, startingGear, EntityManager); // Goobstation
}
var gearEquippedEv = new StartingGearEquippedEvent(entity.Value);

View File

@ -0,0 +1,31 @@
namespace Content.Server._EE.Power.Components;
[RegisterComponent]
public sealed partial class BatteryDrinkerComponent : Component
{
/// <summary>
/// Is this drinker allowed to drink batteries not tagged as <see cref="BatteryDrinkSource"/>?
/// </summary>
[DataField]
public bool DrinkAll;
/// <summary>
/// How long it takes to drink from a battery, in seconds.
/// Is multiplied by the source.
/// </summary>
[DataField]
public float DrinkSpeed = 1.5f;
/// <summary>
/// The multiplier for the amount of power to attempt to drink.
/// Default amount is 1000
/// </summary>
[DataField]
public float DrinkMultiplier = 5f;
/// <summary>
/// The multiplier for how long it takes to drink a non-source battery, if <see cref="DrinkAll"/> is true.
/// </summary>
[DataField]
public float DrinkAllMultiplier = 2.5f;
}

View File

@ -0,0 +1,26 @@
using System.Numerics;
namespace Content.Server._EE.Power.Components;
[RegisterComponent]
public sealed partial class RandomBatteryChargeComponent : Component
{
/// <summary>
/// The minimum and maximum max charge the battery can have.
/// </summary>
[DataField]
public Vector2 BatteryMaxMinMax = new(0.85f, 1.15f);
/// <summary>
/// The minimum and maximum current charge the battery can have.
/// </summary>
[DataField]
public Vector2 BatteryChargeMinMax = new(1f, 1f);
/// <summary>
/// False if the randomized charge of the battery should be a multiple of the preexisting current charge of the battery.
/// True if the randomized charge of the battery should be a multiple of the max charge of the battery post max charge randomization.
/// </summary>
[DataField]
public bool BasedOnMaxCharge = true;
}

View File

@ -0,0 +1,143 @@
using System.Linq;
using Content.Server.Power.Components;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.DoAfter;
using Content.Shared.PowerCell.Components;
using Content.Shared._EE.Silicon;
using Content.Shared.Verbs;
using Robust.Shared.Utility;
using Content.Server._EE.Silicon.Charge;
using Content.Server.Power.EntitySystems;
using Content.Server.Popups;
using Content.Server.PowerCell;
using Content.Shared.Popups;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Content.Server._EE.Power.Components;
using Content.Server._EE.Silicon;
namespace Content.Server._EE.Power;
public sealed class BatteryDrinkerSystem : EntitySystem
{
[Dependency] private readonly ItemSlotsSystem _slots = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly BatterySystem _battery = default!;
[Dependency] private readonly SiliconChargeSystem _silicon = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BatteryComponent, GetVerbsEvent<AlternativeVerb>>(AddAltVerb);
SubscribeLocalEvent<BatteryDrinkerComponent, BatteryDrinkerDoAfterEvent>(OnDoAfter);
}
private void AddAltVerb(EntityUid uid, BatteryComponent batteryComponent, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract)
return;
if (!TryComp<BatteryDrinkerComponent>(args.User, out var drinkerComp) ||
!TestDrinkableBattery(uid, drinkerComp) ||
!_silicon.TryGetSiliconBattery(args.User, out var drinkerBattery))
return;
AlternativeVerb verb = new()
{
Act = () => DrinkBattery(uid, args.User, drinkerComp),
Text = Loc.GetString("battery-drinker-verb-drink"),
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")),
};
args.Verbs.Add(verb);
}
private bool TestDrinkableBattery(EntityUid target, BatteryDrinkerComponent drinkerComp)
{
if (!drinkerComp.DrinkAll && !HasComp<BatteryDrinkerSourceComponent>(target))
return false;
return true;
}
private void DrinkBattery(EntityUid target, EntityUid user, BatteryDrinkerComponent drinkerComp)
{
var doAfterTime = drinkerComp.DrinkSpeed;
if (TryComp<BatteryDrinkerSourceComponent>(target, out var sourceComp))
doAfterTime *= sourceComp.DrinkSpeedMulti;
else
doAfterTime *= drinkerComp.DrinkAllMultiplier;
var args = new DoAfterArgs(EntityManager, user, doAfterTime, new BatteryDrinkerDoAfterEvent(), user, target) // TODO: Make this doafter loop, once we merge Upstream.
{
BreakOnDamage = true,
BreakOnMove = true,
Broadcast = false,
DistanceThreshold = 1.35f,
RequireCanInteract = true,
CancelDuplicate = false
};
_doAfter.TryStartDoAfter(args);
}
private void OnDoAfter(EntityUid uid, BatteryDrinkerComponent drinkerComp, DoAfterEvent args)
{
if (args.Cancelled || args.Target == null)
return;
var source = args.Target.Value;
var drinker = uid;
var sourceBattery = Comp<BatteryComponent>(source);
_silicon.TryGetSiliconBattery(drinker, out var drinkerBatteryComponent);
if (!TryComp(uid, out PowerCellSlotComponent? batterySlot))
return;
var container = _container.GetContainer(uid, batterySlot.CellSlotId);
var drinkerBattery = container.ContainedEntities.First();
TryComp<BatteryDrinkerSourceComponent>(source, out var sourceComp);
DebugTools.AssertNotNull(drinkerBattery);
if (drinkerBattery == null)
return;
var amountToDrink = drinkerComp.DrinkMultiplier * 1000;
amountToDrink = MathF.Min(amountToDrink, sourceBattery.CurrentCharge);
amountToDrink = MathF.Min(amountToDrink, drinkerBatteryComponent!.MaxCharge - drinkerBatteryComponent.CurrentCharge);
if (sourceComp != null && sourceComp.MaxAmount > 0)
amountToDrink = MathF.Min(amountToDrink, (float) sourceComp.MaxAmount);
if (amountToDrink <= 0)
{
_popup.PopupEntity(Loc.GetString("battery-drinker-empty", ("target", source)), drinker, drinker);
return;
}
if (_battery.TryUseCharge(source, amountToDrink))
_battery.SetCharge(drinkerBattery, drinkerBatteryComponent.CurrentCharge + amountToDrink, drinkerBatteryComponent);
else
{
_battery.SetCharge(drinkerBattery, sourceBattery.CurrentCharge + drinkerBatteryComponent.CurrentCharge, drinkerBatteryComponent);
_battery.SetCharge(source, 0);
}
if (sourceComp != null && sourceComp.DrinkSound != null){
_popup.PopupEntity(Loc.GetString("ipc-recharge-tip"), drinker, drinker, PopupType.SmallCaution);
_audio.PlayPvs(sourceComp.DrinkSound, source);
Spawn("EffectSparks", Transform(source).Coordinates);
}
}
}

View File

@ -0,0 +1,39 @@
using Content.Server.Electrocution;
using Content.Server.Popups;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Electrocution;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Content.Server._EE.Power.Components;
namespace Content.Server._EE.Power.Systems;
public sealed class BatteryElectrocuteChargeSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly BatterySystem _battery = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BatteryComponent, ElectrocutedEvent>(OnElectrocuted);
}
private void OnElectrocuted(EntityUid uid, BatteryComponent battery, ElectrocutedEvent args)
{
if (args.ShockDamage == null || args.ShockDamage <= 0)
return;
var charge = Math.Min(args.ShockDamage.Value * args.SiemensCoefficient
/ ElectrocutionSystem.ElectrifiedDamagePerWatt * 2,
battery.MaxCharge * 0.25f)
* _random.NextFloat(0.75f, 1.25f);
_battery.SetCharge(uid, battery.CurrentCharge + charge);
_popup.PopupEntity(Loc.GetString("battery-electrocute-charge"), uid, uid);
}
}

View File

@ -0,0 +1,32 @@
using Content.Server.Radio.Components;
using Content.Shared.Radio;
using Content.Shared.Radio.Components;
namespace Content.Server._EE.Radio;
public sealed class IntrinsicRadioKeySystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<IntrinsicRadioTransmitterComponent, EncryptionChannelsChangedEvent>(OnTransmitterChannelsChanged);
SubscribeLocalEvent<ActiveRadioComponent, EncryptionChannelsChangedEvent>(OnReceiverChannelsChanged);
}
private void OnTransmitterChannelsChanged(EntityUid uid, IntrinsicRadioTransmitterComponent component, EncryptionChannelsChangedEvent args)
{
UpdateChannels(uid, args.Component, ref component.Channels);
}
private void OnReceiverChannelsChanged(EntityUid uid, ActiveRadioComponent component, EncryptionChannelsChangedEvent args)
{
UpdateChannels(uid, args.Component, ref component.Channels);
}
private void UpdateChannels(EntityUid _, EncryptionKeyHolderComponent keyHolderComp, ref HashSet<string> channels)
{
channels.Clear();
channels.UnionWith(keyHolderComp.Channels);
}
}

View File

@ -0,0 +1,8 @@
namespace Content.Server._EE.Silicons.BatteryLocking;
[RegisterComponent]
public sealed partial class BatterySlotRequiresLockComponent : Component
{
[DataField]
public string ItemSlot = string.Empty;
}

View File

@ -0,0 +1,41 @@
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Lock;
using Content.Shared.Popups;
using Content.Shared._EE.Silicon.Components;
using Content.Shared.IdentityManagement;
namespace Content.Server._EE.Silicons.BatteryLocking;
public sealed class BatterySlotRequiresLockSystem : EntitySystem
{
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BatterySlotRequiresLockComponent, LockToggledEvent>(LockToggled);
SubscribeLocalEvent<BatterySlotRequiresLockComponent, LockToggleAttemptEvent>(LockToggleAttempted);
}
private void LockToggled(EntityUid uid, BatterySlotRequiresLockComponent component, LockToggledEvent args)
{
if (!TryComp<LockComponent>(uid, out var lockComp)
|| !TryComp<ItemSlotsComponent>(uid, out var itemslots)
|| !_itemSlotsSystem.TryGetSlot(uid, component.ItemSlot, out var slot, itemslots))
return;
_itemSlotsSystem.SetLock(uid, slot, lockComp.Locked, itemslots);
}
private void LockToggleAttempted(EntityUid uid, BatterySlotRequiresLockComponent component, LockToggleAttemptEvent args)
{
if (args.User == uid || !HasComp<SiliconComponent>(uid))
return;
_popupSystem.PopupEntity(Loc.GetString("batteryslotrequireslock-component-alert-owner", ("user", Identity.Entity(args.User, EntityManager))), uid, uid, PopupType.Large);
}
}

View File

@ -0,0 +1,23 @@
namespace Content.Server._EE.Silicon.BlindHealing;
[RegisterComponent]
public sealed partial class BlindHealingComponent : Component
{
[DataField]
public int DoAfterDelay = 3;
/// <summary>
/// A multiplier that will be applied to the above if an entity is repairing themselves.
/// </summary>
[DataField]
public float SelfHealPenalty = 3f;
/// <summary>
/// Whether or not an entity is allowed to repair itself.
/// </summary>
[DataField]
public bool AllowSelfHeal = true;
[DataField(required: true)]
public List<string> DamageContainers;
}

View File

@ -0,0 +1,98 @@
using Content.Server.Administration.Logs;
using Content.Server.Cargo.Components;
using Content.Server.Stack;
using Content.Shared._EE.Silicon.BlindHealing;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.DoAfter;
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Eye.Blinding.Systems;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Popups;
using Content.Shared.Stacks;
namespace Content.Server._EE.Silicon.BlindHealing;
public sealed class BlindHealingSystem : SharedBlindHealingSystem
{
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly BlindableSystem _blindableSystem = default!;
[Dependency] private readonly StackSystem _stackSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
public override void Initialize()
{
SubscribeLocalEvent<BlindHealingComponent, UseInHandEvent>(OnUse);
SubscribeLocalEvent<BlindHealingComponent, AfterInteractEvent>(OnInteract);
SubscribeLocalEvent<BlindHealingComponent, HealingDoAfterEvent>(OnHealingFinished);
}
private void OnHealingFinished(EntityUid uid, BlindHealingComponent component, HealingDoAfterEvent args)
{
if (args.Cancelled || args.Target == null
|| !TryComp<BlindableComponent>(args.Target, out var blindComp)
|| blindComp is { EyeDamage: 0 })
return;
if (TryComp<StackComponent>(uid, out var stackComponent)
&& TryComp<StackPriceComponent>(uid, out var stackPrice))
_stackSystem.SetCount(uid, (int) (_stackSystem.GetCount(uid, stackComponent) - stackPrice.Price), stackComponent);
_blindableSystem.AdjustEyeDamage((args.Target.Value, blindComp), -blindComp.EyeDamage);
_adminLogger.Add(LogType.Healed, $"{ToPrettyString(args.User):user} repaired {ToPrettyString(uid):target}'s vision");
var str = Loc.GetString("comp-repairable-repair",
("target", uid),
("tool", args.Used!));
_popup.PopupEntity(str, uid, args.User);
}
private bool TryHealBlindness(EntityUid uid, EntityUid user, EntityUid target, float delay)
{
var doAfterEventArgs =
new DoAfterArgs(EntityManager, user, delay, new HealingDoAfterEvent(), uid, target: target, used: uid)
{
NeedHand = true,
BreakOnMove = true,
BreakOnWeightlessMove = false,
};
_doAfter.TryStartDoAfter(doAfterEventArgs);
return true;
}
private void OnInteract(EntityUid uid, BlindHealingComponent component, ref AfterInteractEvent args)
{
if (args.Handled
|| !TryComp<DamageableComponent>(args.User, out var damageable)
|| damageable.DamageContainerID != null && !component.DamageContainers.Contains(damageable.DamageContainerID)
|| !TryComp<BlindableComponent>(args.User, out var blindcomp)
|| blindcomp.EyeDamage == 0
|| args.User == args.Target && !component.AllowSelfHeal)
return;
TryHealBlindness(uid, args.User, args.User,
args.User == args.Target
? component.DoAfterDelay * component.SelfHealPenalty
: component.DoAfterDelay);
}
private void OnUse(EntityUid uid, BlindHealingComponent component, ref UseInHandEvent args)
{
if (args.Handled
|| !TryComp<DamageableComponent>(args.User, out var damageable)
|| damageable.DamageContainerID != null && !component.DamageContainers.Contains(damageable.DamageContainerID)
|| !TryComp<BlindableComponent>(args.User, out var blindcomp)
|| blindcomp.EyeDamage == 0
|| !component.AllowSelfHeal)
return;
TryHealBlindness(uid, args.User, args.User,
component.DoAfterDelay * component.SelfHealPenalty);
}
}

View File

@ -0,0 +1,26 @@
using Robust.Shared.Audio;
namespace Content.Server._EE.Silicon.Charge;
[RegisterComponent]
public sealed partial class BatteryDrinkerSourceComponent : Component
{
/// <summary>
/// The max amount of power this source can provide in one sip.
/// No limit if null.
/// </summary>
[DataField]
public int? MaxAmount = null;
/// <summary>
/// The multiplier for the drink speed.
/// </summary>
[DataField]
public float DrinkSpeedMulti = 1f;
/// <summary>
/// The sound to play when the battery gets drunk from.
/// </summary>
[DataField]
public SoundSpecifier? DrinkSound = new SoundCollectionSpecifier("sparks");
}

View File

@ -0,0 +1,200 @@
using Robust.Shared.Random;
using Content.Shared._EE.Silicon.Components;
using Content.Server.Power.Components;
using Content.Shared.Mobs.Systems;
using Content.Server.Temperature.Components;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Popups;
using Content.Shared._EE.Silicon.Systems;
using Content.Shared.Movement.Systems;
using Content.Server.Body.Components;
using Content.Shared.Mind.Components;
using System.Diagnostics.CodeAnalysis;
using Content.Server.PowerCell;
using Robust.Shared.Timing;
using Robust.Shared.Configuration;
using Robust.Shared.Utility;
using Content.Shared.CCVar;
using Content.Shared.PowerCell.Components;
using Content.Shared.Mind;
using Content.Shared.Alert;
using Content.Server._EE.Silicon.Death;
using Content.Server._EE.Power.Components;
namespace Content.Server._EE.Silicon.Charge;
public sealed class SiliconChargeSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly FlammableSystem _flammable = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly MovementSpeedModifierSystem _moveMod = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SiliconComponent, ComponentStartup>(OnSiliconStartup);
}
public bool TryGetSiliconBattery(EntityUid silicon, [NotNullWhen(true)] out BatteryComponent? batteryComp)
{
batteryComp = null;
if (!HasComp<SiliconComponent>(silicon))
return false;
// try get a battery directly on the inserted entity
if (TryComp(silicon, out batteryComp)
|| _powerCell.TryGetBatteryFromSlot(silicon, out batteryComp))
return true;
//DebugTools.Assert("SiliconComponent does not contain Battery");
return false;
}
private void OnSiliconStartup(EntityUid uid, SiliconComponent component, ComponentStartup args)
{
if (!HasComp<PowerCellSlotComponent>(uid))
return;
if (component.EntityType.GetType() != typeof(SiliconType))
DebugTools.Assert("SiliconComponent.EntityType is not a SiliconType enum.");
}
public override void Update(float frameTime)
{
base.Update(frameTime);
// For each siliconComp entity with a battery component, drain their charge.
var query = EntityQueryEnumerator<SiliconComponent>();
while (query.MoveNext(out var silicon, out var siliconComp))
{
if (_mobState.IsDead(silicon)
|| !siliconComp.BatteryPowered)
continue;
// Check if the Silicon is an NPC, and if so, follow the delay as specified in the CVAR.
if (siliconComp.EntityType.Equals(SiliconType.Npc))
{
var updateTime = _config.GetCVar(CCVars.SiliconNpcUpdateTime);
if (_timing.CurTime - siliconComp.LastDrainTime < TimeSpan.FromSeconds(updateTime))
continue;
siliconComp.LastDrainTime = _timing.CurTime;
}
// If you can't find a battery, set the indicator and skip it.
if (!TryGetSiliconBattery(silicon, out var batteryComp))
{
UpdateChargeState(silicon, 0, siliconComp);
if (_alerts.IsShowingAlert(silicon, siliconComp.BatteryAlert))
{
_alerts.ClearAlert(silicon, siliconComp.BatteryAlert);
_alerts.ShowAlert(silicon, siliconComp.NoBatteryAlert);
}
continue;
}
// If the silicon ghosted or is SSD while still being powered, skip it.
if (TryComp<MindContainerComponent>(silicon, out var mindContComp)
&& !mindContComp.HasMind)
continue;
var drainRate = siliconComp.DrainPerSecond;
// All multipliers will be subtracted by 1, and then added together, and then multiplied by the drain rate. This is then added to the base drain rate.
// This is to stop exponential increases, while still allowing for less-than-one multipliers.
var drainRateFinalAddi = 0f;
// TODO: Devise a method of adding multis where other systems can alter the drain rate.
// Maybe use something similar to refreshmovespeedmodifiers, where it's stored in the component.
// Maybe it doesn't matter, and stuff should just use static drain?
if (!siliconComp.EntityType.Equals(SiliconType.Npc)) // Don't bother checking heat if it's an NPC. It's a waste of time, and it'd be delayed due to the update time.
drainRateFinalAddi += SiliconHeatEffects(silicon, siliconComp, frameTime) - 1; // This will need to be changed at some point if we allow external batteries, since the heat of the Silicon might not be applicable.
// Ensures that the drain rate is at least 10% of normal,
// and would allow at least 4 minutes of life with a max charge, to prevent cheese.
drainRate += Math.Clamp(drainRateFinalAddi, drainRate * -0.9f, batteryComp.MaxCharge / 240);
// Drain the battery.
_powerCell.TryUseCharge(silicon, frameTime * drainRate);
// Figure out the current state of the Silicon.
var chargePercent = (short) MathF.Round(batteryComp.CurrentCharge / batteryComp.MaxCharge * 10f);
UpdateChargeState(silicon, chargePercent, siliconComp);
}
}
/// <summary>
/// Checks if anything needs to be updated, and updates it.
/// </summary>
public void UpdateChargeState(EntityUid uid, short chargePercent, SiliconComponent component)
{
component.ChargeState = chargePercent;
RaiseLocalEvent(uid, new SiliconChargeStateUpdateEvent(chargePercent));
_moveMod.RefreshMovementSpeedModifiers(uid);
// If the battery was replaced and the no battery indicator is showing, replace the indicator
if (_alerts.IsShowingAlert(uid, component.NoBatteryAlert) && chargePercent != 0)
{
_alerts.ClearAlert(uid, component.NoBatteryAlert);
_alerts.ShowAlert(uid, component.BatteryAlert, chargePercent);
}
}
private float SiliconHeatEffects(EntityUid silicon, SiliconComponent siliconComp, float frameTime)
{
if (!TryComp<TemperatureComponent>(silicon, out var temperComp)
|| !TryComp<ThermalRegulatorComponent>(silicon, out var thermalComp))
return 0;
// If the Silicon is hot, drain the battery faster, if it's cold, drain it slower, capped.
var upperThresh = thermalComp.NormalBodyTemperature + thermalComp.ThermalRegulationTemperatureThreshold;
var upperThreshHalf = thermalComp.NormalBodyTemperature + thermalComp.ThermalRegulationTemperatureThreshold * 0.5f;
// Check if the silicon is in a hot environment.
if (temperComp.CurrentTemperature > upperThreshHalf)
{
// Divide the current temp by the max comfortable temp capped to 4, then add that to the multiplier.
var hotTempMulti = Math.Min(temperComp.CurrentTemperature / upperThreshHalf, 4);
// If the silicon is hot enough, it has a chance to catch fire.
siliconComp.OverheatAccumulator += frameTime;
if (!(siliconComp.OverheatAccumulator >= 5))
return hotTempMulti;
siliconComp.OverheatAccumulator -= 5;
if (!EntityManager.TryGetComponent<FlammableComponent>(silicon, out var flamComp)
|| flamComp is { OnFire: true }
|| !(temperComp.CurrentTemperature > temperComp.HeatDamageThreshold))
return hotTempMulti;
_popup.PopupEntity(Loc.GetString("silicon-overheating"), silicon, silicon, PopupType.MediumCaution);
if (!_random.Prob(Math.Clamp(temperComp.CurrentTemperature / (upperThresh * 5), 0.001f, 0.9f)))
return hotTempMulti;
_flammable.AdjustFireStacks(silicon, Math.Clamp(siliconComp.FireStackMultiplier, -10, 10), flamComp);
_flammable.Ignite(silicon, silicon, flamComp);
return hotTempMulti;
}
// Check if the silicon is in a cold environment.
if (temperComp.CurrentTemperature < thermalComp.NormalBodyTemperature)
return 0.5f + temperComp.CurrentTemperature / thermalComp.NormalBodyTemperature * 0.5f;
return 0;
}
}

View File

@ -0,0 +1,82 @@
using Content.Server.Chat.Systems;
using Content.Server.Lightning;
using Content.Server.Popups;
using Content.Server.PowerCell;
using Content.Server._EE.Silicon.Charge;
using Content.Shared._EE.Silicon.DeadStartupButton;
using Content.Shared.Audio;
using Content.Shared.Damage;
using Content.Shared.Electrocution;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Random;
namespace Content.Server._EE.Silicon.DeadStartupButton;
public sealed class DeadStartupButtonSystem : SharedDeadStartupButtonSystem
{
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly LightningSystem _lightning = default!;
[Dependency] private readonly SiliconChargeSystem _siliconChargeSystem = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DeadStartupButtonComponent, OnDoAfterButtonPressedEvent>(OnDoAfter);
SubscribeLocalEvent<DeadStartupButtonComponent, ElectrocutedEvent>(OnElectrocuted);
SubscribeLocalEvent<DeadStartupButtonComponent, MobStateChangedEvent>(OnMobStateChanged);
}
private void OnDoAfter(EntityUid uid, DeadStartupButtonComponent comp, OnDoAfterButtonPressedEvent args)
{
if (args.Handled || args.Cancelled
|| !TryComp<MobStateComponent>(uid, out var mobStateComponent)
|| !_mobState.IsDead(uid, mobStateComponent)
|| !TryComp<MobThresholdsComponent>(uid, out var mobThresholdsComponent)
|| !TryComp<DamageableComponent>(uid, out var damageable)
|| !_mobThreshold.TryGetThresholdForState(uid, MobState.Critical, out var criticalThreshold, mobThresholdsComponent))
return;
if (damageable.TotalDamage < criticalThreshold)
_mobState.ChangeMobState(uid, MobState.Alive, mobStateComponent);
else
{
_audio.PlayPvs(comp.BuzzSound, uid, AudioHelpers.WithVariation(0.05f, _robustRandom));
_popup.PopupEntity(Loc.GetString("dead-startup-system-reboot-failed", ("target", MetaData(uid).EntityName)), uid);
Spawn("EffectSparks", Transform(uid).Coordinates);
}
}
private void OnElectrocuted(EntityUid uid, DeadStartupButtonComponent comp, ElectrocutedEvent args)
{
if (!TryComp<MobStateComponent>(uid, out var mobStateComponent)
|| !_mobState.IsDead(uid, mobStateComponent)
|| !_siliconChargeSystem.TryGetSiliconBattery(uid, out var bateria)
|| bateria.CurrentCharge <= 0)
return;
_lightning.ShootRandomLightnings(uid, 2, 4);
_powerCell.TryUseCharge(uid, bateria.CurrentCharge);
}
private void OnMobStateChanged(EntityUid uid, DeadStartupButtonComponent comp, MobStateChangedEvent args)
{
if (args.NewMobState != MobState.Alive)
return;
_popup.PopupEntity(Loc.GetString("dead-startup-system-reboot-success", ("target", MetaData(uid).EntityName)), uid);
_audio.PlayPvs(comp.Sound, uid);
}
}

View File

@ -0,0 +1,17 @@
namespace Content.Server._EE.Silicon.Death;
/// <summary>
/// Marks a Silicon as becoming incapacitated when they run out of battery charge.
/// </summary>
/// <remarks>
/// Uses the Silicon System's charge states to do so, so make sure they're a battery powered Silicon.
/// </remarks>
[RegisterComponent]
public sealed partial class SiliconDownOnDeadComponent : Component
{
/// <summary>
/// Is this Silicon currently dead?
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
public bool Dead;
}

View File

@ -0,0 +1,127 @@
using Content.Server.Power.Components;
using Content.Shared._EE.Silicon.Systems;
using Content.Shared.Bed.Sleep;
using Content.Server._EE.Silicon.Charge;
using Content.Server._EE.Power.Components;
using Content.Server.Humanoid;
using Content.Shared.Humanoid;
namespace Content.Server._EE.Silicon.Death;
public sealed class SiliconDeathSystem : EntitySystem
{
[Dependency] private readonly SleepingSystem _sleep = default!;
[Dependency] private readonly SiliconChargeSystem _silicon = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoidAppearanceSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SiliconDownOnDeadComponent, SiliconChargeStateUpdateEvent>(OnSiliconChargeStateUpdate);
}
private void OnSiliconChargeStateUpdate(EntityUid uid, SiliconDownOnDeadComponent siliconDeadComp, SiliconChargeStateUpdateEvent args)
{
if (!_silicon.TryGetSiliconBattery(uid, out var batteryComp))
{
SiliconDead(uid, siliconDeadComp, batteryComp, uid);
return;
}
if (args.ChargePercent == 0 && siliconDeadComp.Dead)
return;
if (args.ChargePercent == 0 && !siliconDeadComp.Dead)
SiliconDead(uid, siliconDeadComp, batteryComp, uid);
else if (args.ChargePercent != 0 && siliconDeadComp.Dead)
SiliconUnDead(uid, siliconDeadComp, batteryComp, uid);
}
private void SiliconDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadComp, BatteryComponent? batteryComp, EntityUid batteryUid)
{
var deadEvent = new SiliconChargeDyingEvent(uid, batteryComp, batteryUid);
RaiseLocalEvent(uid, deadEvent);
if (deadEvent.Cancelled)
return;
EntityManager.EnsureComponent<SleepingComponent>(uid);
EntityManager.EnsureComponent<ForcedSleepingComponent>(uid);
if (TryComp(uid, out HumanoidAppearanceComponent? humanoidAppearanceComponent))
{
var layers = HumanoidVisualLayersExtension.Sublayers(HumanoidVisualLayers.HeadSide);
_humanoidAppearanceSystem.SetLayersVisibility(uid, layers, false, true, humanoidAppearanceComponent);
}
siliconDeadComp.Dead = true;
RaiseLocalEvent(uid, new SiliconChargeDeathEvent(uid, batteryComp, batteryUid));
}
private void SiliconUnDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadComp, BatteryComponent? batteryComp, EntityUid batteryUid)
{
RemComp<ForcedSleepingComponent>(uid);
_sleep.TryWaking(uid, true, null);
siliconDeadComp.Dead = false;
RaiseLocalEvent(uid, new SiliconChargeAliveEvent(uid, batteryComp, batteryUid));
}
}
/// <summary>
/// A cancellable event raised when a Silicon is about to go down due to charge.
/// </summary>
/// <remarks>
/// This probably shouldn't be modified unless you intend to fill the Silicon's battery,
/// as otherwise it'll just be triggered again next frame.
/// </remarks>
public sealed class SiliconChargeDyingEvent : CancellableEntityEventArgs
{
public EntityUid SiliconUid { get; }
public BatteryComponent? BatteryComp { get; }
public EntityUid BatteryUid { get; }
public SiliconChargeDyingEvent(EntityUid siliconUid, BatteryComponent? batteryComp, EntityUid batteryUid)
{
SiliconUid = siliconUid;
BatteryComp = batteryComp;
BatteryUid = batteryUid;
}
}
/// <summary>
/// An event raised after a Silicon has gone down due to charge.
/// </summary>
public sealed class SiliconChargeDeathEvent : EntityEventArgs
{
public EntityUid SiliconUid { get; }
public BatteryComponent? BatteryComp { get; }
public EntityUid BatteryUid { get; }
public SiliconChargeDeathEvent(EntityUid siliconUid, BatteryComponent? batteryComp, EntityUid batteryUid)
{
SiliconUid = siliconUid;
BatteryComp = batteryComp;
BatteryUid = batteryUid;
}
}
/// <summary>
/// An event raised after a Silicon has reawoken due to an increase in charge.
/// </summary>
public sealed class SiliconChargeAliveEvent : EntityEventArgs
{
public EntityUid SiliconUid { get; }
public BatteryComponent? BatteryComp { get; }
public EntityUid BatteryUid { get; }
public SiliconChargeAliveEvent(EntityUid siliconUid, BatteryComponent? batteryComp, EntityUid batteryUid)
{
SiliconUid = siliconUid;
BatteryComp = batteryComp;
BatteryUid = batteryUid;
}
}

View File

@ -0,0 +1,58 @@
using Content.Server.Popups;
using Content.Shared._EE.Silicon.EmitBuzzWhileDamaged;
using Content.Shared.Audio;
using Content.Shared.Damage;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Content.Shared.Mobs.Components;
namespace Content.Server._EE.Silicon.EmitBuzzOnCrit;
/// <summary>
/// This handles the buzzing popup and sound of a silicon based race when it is pretty damaged.
/// </summary>
public sealed class EmitBuzzWhileDamagedSystem : EntitySystem
{
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<EmitBuzzWhileDamagedComponent, MobStateComponent, MobThresholdsComponent, DamageableComponent>();
while (query.MoveNext(out var uid, out var emitBuzzOnCritComponent, out var mobStateComponent, out var thresholdsComponent, out var damageableComponent))
{
if (_mobState.IsDead(uid, mobStateComponent)
|| !_mobThreshold.TryGetThresholdForState(uid, MobState.Critical, out var threshold, thresholdsComponent)
|| damageableComponent.TotalDamage < threshold / 2)
continue;
emitBuzzOnCritComponent.AccumulatedFrametime += frameTime;
if (emitBuzzOnCritComponent.AccumulatedFrametime < emitBuzzOnCritComponent.CycleDelay)
continue;
emitBuzzOnCritComponent.AccumulatedFrametime -= emitBuzzOnCritComponent.CycleDelay;
if (_gameTiming.CurTime <= emitBuzzOnCritComponent.LastBuzzPopupTime + emitBuzzOnCritComponent.BuzzPopupCooldown)
continue;
// Start buzzing
emitBuzzOnCritComponent.LastBuzzPopupTime = _gameTiming.CurTime;
_popupSystem.PopupEntity(Loc.GetString("silicon-behavior-buzz"), uid);
Spawn("EffectSparks", Transform(uid).Coordinates);
_audio.PlayPvs(emitBuzzOnCritComponent.Sound, uid, AudioHelpers.WithVariation(0.05f, _robustRandom));
}
}
}

View File

@ -0,0 +1,6 @@
namespace Content.Server._EE.Silicon.EncryptionHolderRequiresLock;
[RegisterComponent]
public sealed partial class EncryptionHolderRequiresLockComponent : Component
{
}

View File

@ -0,0 +1,28 @@
using Content.Shared.Lock;
using Content.Shared.Radio.Components;
using Content.Shared.Radio.EntitySystems;
namespace Content.Server._EE.Silicon.EncryptionHolderRequiresLock;
public sealed class EncryptionHolderRequiresLockSystem : EntitySystem
{
[Dependency] private readonly EncryptionKeySystem _encryptionKeySystem = default!;
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<EncryptionHolderRequiresLockComponent, LockToggledEvent>(LockToggled);
}
private void LockToggled(EntityUid uid, EncryptionHolderRequiresLockComponent component, LockToggledEvent args)
{
if (!TryComp<LockComponent>(uid, out var lockComp)
|| !TryComp<EncryptionKeyHolderComponent>(uid, out var keyHolder))
return;
keyHolder.KeysUnlocked = !lockComp.Locked;
_encryptionKeySystem.UpdateChannels(uid, keyHolder);
}
}

View File

@ -0,0 +1,30 @@
using Content.Shared.Roles;
using Content.Shared.Radio.Components;
using Content.Shared.Containers;
using Robust.Shared.Containers;
namespace Content.Server._EE.Silicon.IPC;
public sealed partial class InternalEncryptionKeySpawner : EntitySystem
{
[Dependency] private readonly SharedContainerSystem _container = default!;
public void TryInsertEncryptionKey(EntityUid target, StartingGearPrototype startingGear, IEntityManager entityManager)
{
if (!TryComp<EncryptionKeyHolderComponent>(target, out var keyHolderComp)
|| !startingGear.Equipment.TryGetValue("ears", out var earEquipString)
|| string.IsNullOrEmpty(earEquipString))
return;
var earEntity = entityManager.SpawnEntity(earEquipString, entityManager.GetComponent<TransformComponent>(target).Coordinates);
if (!entityManager.HasComponent<EncryptionKeyHolderComponent>(earEntity)
|| !entityManager.TryGetComponent<ContainerFillComponent>(earEntity, out var fillComp)
|| !fillComp.Containers.TryGetValue(EncryptionKeyHolderComponent.KeyContainerName, out var defaultKeys))
return;
_container.CleanContainer(keyHolderComp.KeyContainer);
foreach (var key in defaultKeys)
entityManager.SpawnInContainerOrDrop(key, target, keyHolderComp.KeyContainer.ID, out _);
entityManager.QueueDeleteEntity(earEntity);
}
}

View File

@ -0,0 +1,25 @@
using Robust.Shared.Audio;
namespace Content.Server._EE.Silicon;
/// <summary>
/// Applies a <see cref="SpamEmitSoundComponent"/> to a Silicon when its battery is drained, and removes it when it's not.
/// </summary>
[RegisterComponent]
public sealed partial class SiliconEmitSoundOnDrainedComponent : Component
{
[DataField]
public SoundSpecifier Sound = default!;
[DataField]
public TimeSpan MinInterval = TimeSpan.FromSeconds(8);
[DataField]
public TimeSpan MaxInterval = TimeSpan.FromSeconds(15);
[DataField]
public float PlayChance = 1f;
[DataField]
public string? PopUp;
}

View File

@ -0,0 +1,43 @@
using Content.Server._EE.Silicon.Death;
using Content.Shared.Sound.Components;
using Content.Server.Sound;
using Content.Shared.Mobs;
using Content.Shared._EE.Silicon.Systems;
namespace Content.Server._EE.Silicon;
public sealed class EmitSoundOnCritSystem : EntitySystem
{
[Dependency] private readonly EmitSoundSystem _emitSound = default!;
public override void Initialize()
{
SubscribeLocalEvent<SiliconEmitSoundOnDrainedComponent, SiliconChargeDeathEvent>(OnDeath);
SubscribeLocalEvent<SiliconEmitSoundOnDrainedComponent, SiliconChargeAliveEvent>(OnAlive);
SubscribeLocalEvent<SiliconEmitSoundOnDrainedComponent, MobStateChangedEvent>(OnStateChange);
}
private void OnDeath(EntityUid uid, SiliconEmitSoundOnDrainedComponent component, SiliconChargeDeathEvent args)
{
var spamComp = EnsureComp<SpamEmitSoundComponent>(uid);
spamComp.MinInterval = component.MinInterval;
spamComp.MaxInterval = component.MaxInterval;
spamComp.PopUp = component.PopUp;
spamComp.Sound = component.Sound;
_emitSound.SetEnabled((uid, spamComp), true);
}
private void OnAlive(EntityUid uid, SiliconEmitSoundOnDrainedComponent component, SiliconChargeAliveEvent args)
{
RemComp<SpamEmitSoundComponent>(uid); // This component is bad and I don't feel like making a janky work around because of it.
// If you give something the SiliconEmitSoundOnDrainedComponent, know that it can't have the SpamEmitSoundComponent, and any other systems that play with it will just be broken.
}
public void OnStateChange(EntityUid uid, SiliconEmitSoundOnDrainedComponent component, MobStateChangedEvent args)
{
if (args.NewMobState != MobState.Dead)
return;
RemComp<SpamEmitSoundComponent>(uid);
}
}

View File

@ -0,0 +1,9 @@
using Content.Shared.Damage;
using Content.Shared.Tools;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server._EE.Silicon.WeldingHealable
{
[RegisterComponent]
public sealed partial class WeldingHealableComponent : Component { }
}

View File

@ -0,0 +1,102 @@
using Content.Server._EE.Silicon.WeldingHealing;
using Content.Shared.Tools.Components;
using Content.Shared._EE.Silicon.WeldingHealing;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Damage;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Tools;
using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem;
namespace Content.Server._EE.Silicon.WeldingHealable;
public sealed class WeldingHealableSystem : SharedWeldingHealableSystem
{
[Dependency] private readonly SharedToolSystem _toolSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
public override void Initialize()
{
SubscribeLocalEvent<WeldingHealableComponent, InteractUsingEvent>(Repair);
SubscribeLocalEvent<WeldingHealableComponent, SiliconRepairFinishedEvent>(OnRepairFinished);
}
private void OnRepairFinished(EntityUid uid, WeldingHealableComponent healableComponent, SiliconRepairFinishedEvent args)
{
if (args.Cancelled || args.Used == null
|| !TryComp<DamageableComponent>(args.Target, out var damageable)
|| !TryComp<WeldingHealingComponent>(args.Used, out var component)
|| damageable.DamageContainerID is null
|| !component.DamageContainers.Contains(damageable.DamageContainerID)
|| !HasDamage(damageable, component)
|| !TryComp<WelderComponent>(args.Used, out var welder)
|| !TryComp<SolutionContainerManagerComponent>(args.Used, out var solutionContainer)
|| !_solutionContainer.TryGetSolution(((EntityUid) args.Used, solutionContainer), welder.FuelSolutionName, out var solution))
return;
_damageableSystem.TryChangeDamage(uid, component.Damage, true, false, origin: args.User);
_solutionContainer.RemoveReagent(solution.Value, welder.FuelReagent, component.FuelCost);
var str = Loc.GetString("comp-repairable-repair",
("target", uid),
("tool", args.Used!));
_popup.PopupEntity(str, uid, args.User);
if (!args.Used.HasValue)
return;
args.Handled = _toolSystem.UseTool
(args.Used.Value,
args.User,
uid,
args.Delay,
component.QualityNeeded,
new SiliconRepairFinishedEvent
{
Delay = args.Delay
});
}
private async void Repair(EntityUid uid, WeldingHealableComponent healableComponent, InteractUsingEvent args)
{
if (args.Handled
|| !EntityManager.TryGetComponent(args.Used, out WeldingHealingComponent? component)
|| !EntityManager.TryGetComponent(args.Target, out DamageableComponent? damageable)
|| damageable.DamageContainerID is null
|| !component.DamageContainers.Contains(damageable.DamageContainerID)
|| !HasDamage(damageable, component)
|| !_toolSystem.HasQuality(args.Used, component.QualityNeeded)
|| args.User == args.Target && !component.AllowSelfHeal)
return;
float delay = args.User == args.Target
? component.DoAfterDelay * component.SelfHealPenalty
: component.DoAfterDelay;
args.Handled = _toolSystem.UseTool
(args.Used,
args.User,
args.Target,
delay,
component.QualityNeeded,
new SiliconRepairFinishedEvent
{
Delay = delay,
});
}
private bool HasDamage(DamageableComponent component, WeldingHealingComponent healable)
{
if (healable.Damage.DamageDict is null)
return false;
foreach (var type in healable.Damage.DamageDict)
if (component.Damage.DamageDict[type.Key].Value > 0)
return true;
return false;
}
}

View File

@ -0,0 +1,48 @@
using Content.Shared.Damage;
using Content.Shared.Tools;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server._EE.Silicon.WeldingHealing
{
[RegisterComponent]
public sealed partial class WeldingHealingComponent : Component
{
/// <summary>
/// All the damage to change information is stored in this <see cref="DamageSpecifier"/>.
/// </summary>
/// <remarks>
/// If this data-field is specified, it will change damage by this amount instead of setting all damage to 0.
/// in order to heal/repair the damage values have to be negative.
/// </remarks>
[DataField(required: true)]
public DamageSpecifier Damage;
[DataField(customTypeSerializer:typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
public string QualityNeeded = "Welding";
/// <summary>
/// The fuel amount needed to repair physical related damage
/// </summary>
[DataField]
public int FuelCost = 5;
[DataField]
public int DoAfterDelay = 3;
/// <summary>
/// A multiplier that will be applied to the above if an entity is repairing themselves.
/// </summary>
[DataField]
public float SelfHealPenalty = 3f;
/// <summary>
/// Whether or not an entity is allowed to repair itself.
/// </summary>
[DataField]
public bool AllowSelfHeal = true;
[DataField(required: true)]
public List<string> DamageContainers;
}
}

View File

@ -188,7 +188,7 @@ public partial class SharedBodySystem
cameFromEntities[connection] = childPart;
var childPartComponent = Comp<BodyPartComponent>(childPart);
var partSlot = CreatePartSlot(parentEntity, connection, childPartComponent.PartType, parentPartComponent);
TryCreatePartSlot(parentEntity, connection, childPartComponent.PartType, out var partSlot, parentPartComponent);
// Shitmed Change Start
childPartComponent.ParentSlot = partSlot;
Dirty(childPart, childPartComponent);
@ -215,7 +215,7 @@ public partial class SharedBodySystem
{
foreach (var (organSlotId, organProto) in organs)
{
var slot = CreateOrganSlot((ent, ent), organSlotId);
TryCreateOrganSlot(ent, organSlotId, out var slot); // Shitmed Change
SpawnInContainerOrDrop(organProto, ent, GetOrganContainerId(organSlotId));
if (slot is null)

View File

@ -115,7 +115,13 @@ public partial class SharedBodySystem
Containers.EnsureContainer<ContainerSlot>(parent.Value, GetOrganContainerId(slotId));
slot = new OrganSlot(slotId);
return part.Organs.TryAdd(slotId, slot.Value);
// Shitmed Change Start
if (!part.Organs.ContainsKey(slotId)
&& !part.Organs.TryAdd(slotId, slot.Value))
return false;
return true;
// Shitmed Change End
}
/// <summary>

View File

@ -502,7 +502,8 @@ public partial class SharedBodySystem
Containers.EnsureContainer<ContainerSlot>(partId.Value, GetPartSlotContainerId(slotId));
slot = new BodyPartSlot(slotId, partType);
if (!part.Children.TryAdd(slotId, slot.Value))
if (!part.Children.ContainsKey(slotId) // Shitmed Change
&& !part.Children.TryAdd(slotId, slot.Value))
return false;
Dirty(partId.Value, part);

View File

@ -25,4 +25,10 @@ public sealed partial class CCVars : CVars
/// </summary>
public static readonly CVarDef<bool> DebugPow3rDisableParallel =
CVarDef.Create("debug.pow3r_disable_parallel", true, CVar.SERVERONLY);
/// <summary>
/// Goobstation: The amount of time between NPC Silicons draining their battery in seconds.
/// </summary>
public static readonly CVarDef<float> SiliconNpcUpdateTime =
CVarDef.Create("silicon.npcupdatetime", 1.5f, CVar.SERVERONLY);
}

View File

@ -24,12 +24,14 @@ namespace Content.Shared.Electrocution
public readonly EntityUid TargetUid;
public readonly EntityUid? SourceUid;
public readonly float SiemensCoefficient;
public readonly float? ShockDamage = null; // Goobstation
public ElectrocutedEvent(EntityUid targetUid, EntityUid? sourceUid, float siemensCoefficient)
public ElectrocutedEvent(EntityUid targetUid, EntityUid? sourceUid, float siemensCoefficient, float shockDamage) // Goobstation
{
TargetUid = targetUid;
SourceUid = sourceUid;
SiemensCoefficient = siemensCoefficient;
ShockDamage = shockDamage; // Goobstation
}
}
}

View File

@ -40,6 +40,9 @@ namespace Content.Shared.Humanoid
case SpeciesNaming.FirstDashFirst:
return Loc.GetString("namepreset-firstdashfirst",
("first1", GetFirstName(speciesProto, gender)), ("first2", GetFirstName(speciesProto, gender)));
case SpeciesNaming.FirstDashLast: // Goobstation
return Loc.GetString("namepreset-firstdashlast",
("first", GetFirstName(speciesProto, gender)), ("last", GetLastName(speciesProto)));
case SpeciesNaming.LastFirst: // DeltaV: Rodentia name scheme
return Loc.GetString("namepreset-lastfirst",
("last", GetLastName(speciesProto)), ("first", GetFirstName(speciesProto, gender)));

View File

@ -132,4 +132,5 @@ public enum SpeciesNaming : byte
//End of Nyano - Summary: for Oni naming
TheFirstofLast,
LastFirst, // DeltaV
FirstDashLast, // Goobstation
}

View File

@ -53,4 +53,11 @@ public sealed partial class EncryptionKeyHolderComponent : Component
/// </summary>
[ViewVariables]
public string? DefaultChannel;
/// <summary>
/// Goobstation: Whether or not the headset can be examined to see the encryption keys while the keys aren't accessible.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("examineWhileLocked")]
public bool ExamineWhileLocked = true;
}

View File

@ -174,7 +174,9 @@ public sealed partial class EncryptionKeySystem : EntitySystem
private void OnHolderExamined(EntityUid uid, EncryptionKeyHolderComponent component, ExaminedEvent args)
{
if (!args.IsInDetailsRange)
if (!args.IsInDetailsRange
|| !component.ExamineWhileLocked && !component.KeysUnlocked // Goobstation
|| !component.ExamineWhileLocked && TryComp<WiresPanelComponent>(uid, out var panel) && !panel.Open) // Goobstation
return;
if (component.KeyContainer.ContainedEntities.Count == 0)

View File

@ -0,0 +1,12 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared._EE.Silicon;
[Serializable, NetSerializable]
public sealed partial class BatteryDrinkerDoAfterEvent : SimpleDoAfterEvent
{
public BatteryDrinkerDoAfterEvent()
{
}
}

View File

@ -0,0 +1,12 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared._EE.Silicon.BlindHealing;
public abstract partial class SharedBlindHealingSystem : EntitySystem
{
[Serializable, NetSerializable]
protected sealed partial class HealingDoAfterEvent : SimpleDoAfterEvent
{
}
}

View File

@ -0,0 +1,114 @@
using Robust.Shared.GameStates;
using Content.Shared._EE.Silicon.Systems;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
using Robust.Shared.Prototypes;
using Content.Shared.Alert;
namespace Content.Shared._EE.Silicon.Components;
/// <summary>
/// Component for defining a mob as a robot.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class SiliconComponent : Component
{
[ViewVariables(VVAccess.ReadOnly)]
public short ChargeState = 10;
[ViewVariables(VVAccess.ReadOnly)]
public float OverheatAccumulator = 0.0f;
/// <summary>
/// The last time the Silicon was drained.
/// Used for NPC Silicons to avoid over updating.
/// </summary>
/// <remarks>
/// Time between drains can be specified in
/// <see cref="SimpleStationCcvars.SiliconNpc"/>
/// </remarks>
public TimeSpan LastDrainTime = TimeSpan.Zero;
/// <summary>
/// The Silicon's battery slot, if it has one.
/// </summary>
/// <summary>
/// Is the Silicon currently dead?
/// </summary>
public bool Dead = false;
// BatterySystem took issue with how this was used, so I'm coming back to it at a later date, when more foundational Silicon stuff is implemented.
// /// <summary>
// /// The entity to get the battery from.
// /// </summary>
// public EntityUid BatteryOverride? = EntityUid.Invalid;
/// <summary>
/// The type of silicon this is.
/// </summary>
/// <remarks>
/// Any new types of Silicons should be added to the enum.
/// Setting this to Npc will delay charge state updates by LastDrainTime and skip battery heat calculations
/// </remarks>
[DataField(customTypeSerializer: typeof(EnumSerializer))]
public Enum EntityType = SiliconType.Npc;
/// <summary>
/// Is this silicon battery powered?
/// </summary>
/// <remarks>
/// If true, should go along with a battery component. One will not be added automatically.
/// </remarks>
[DataField]
public bool BatteryPowered = false;
/// <summary>
/// How much power is drained by this Silicon every second by default.
/// </summary>
[DataField]
public float DrainPerSecond = 50f;
/// <summary>
/// The percentages at which the silicon will enter each state.
/// </summary>
/// <remarks>
/// The Silicon will always be Full at 100%.
/// Setting a value to null will disable that state.
/// Setting Critical to 0 will cause the Silicon to never enter the dead state.
/// </remarks>
[DataField]
public float? ChargeThresholdMid = 0.5f;
/// <inheritdoc cref="ChargeThresholdMid"/>
[DataField]
public float? ChargeThresholdLow = 0.25f;
/// <inheritdoc cref="ChargeThresholdMid"/>
[DataField]
public float? ChargeThresholdCritical = 0.1f;
[DataField]
public ProtoId<AlertPrototype> BatteryAlert = "BorgBattery";
[DataField]
public ProtoId<AlertPrototype> NoBatteryAlert = "BorgBatteryNone";
/// <summary>
/// The amount the Silicon will be slowed at each charge state.
/// </summary>
[DataField(required: true)]
public Dictionary<int, float> SpeedModifierThresholds = default!;
[DataField]
public float FireStackMultiplier = 1f;
/// <summary>
/// Whether or not a Silicon will cancel all sleep events.
/// Maybe you want an android that can sleep as well as drink APCs? I'm not going to judge.
/// </summary>
[DataField]
public bool DoSiliconsDreamOfElectricSheep;
}

View File

@ -0,0 +1,28 @@
using Robust.Shared.Audio;
namespace Content.Shared._EE.Silicon.DeadStartupButton;
/// <summary>
/// This is used for...
/// </summary>
[RegisterComponent]
public sealed partial class DeadStartupButtonComponent : Component
{
[DataField("verbText")]
public string VerbText = "dead-startup-button-verb";
[DataField("sound")]
public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Effects/Arcade/newgame.ogg");
[DataField("buttonSound")]
public SoundSpecifier ButtonSound = new SoundPathSpecifier("/Audio/Machines/button.ogg");
[DataField("doAfterInterval"), ViewVariables(VVAccess.ReadWrite)]
public float DoAfterInterval = 1f;
[DataField("buzzSound")]
public SoundSpecifier BuzzSound = new SoundCollectionSpecifier("buzzes");
[DataField("verbPriority"), ViewVariables(VVAccess.ReadWrite)]
public int VerbPriority = 1;
}

View File

@ -0,0 +1,66 @@
using Content.Shared.DoAfter;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Verbs;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Shared._EE.Silicon.DeadStartupButton;
/// <summary>
/// This creates a Button that can be activated after an entity, usually a silicon or an IPC, died.
/// This will activate a doAfter and then revive the entity, playing a custom afterward sound.
/// </summary>
public abstract partial class SharedDeadStartupButtonSystem : EntitySystem
{
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly INetManager _net = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<DeadStartupButtonComponent, GetVerbsEvent<AlternativeVerb>>(AddTurnOnVerb);
}
private void AddTurnOnVerb(EntityUid uid, DeadStartupButtonComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!_mobState.IsDead(uid)
|| !args.CanAccess || !args.CanInteract || args.Hands == null)
return;
if (!TryComp(uid, out MobStateComponent? mobStateComponent) || !_mobState.IsDead(uid, mobStateComponent))
return;
args.Verbs.Add(new AlternativeVerb()
{
Act = () => TryStartup(args.User, uid, component),
Text = Loc.GetString(component.VerbText),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/Spare/poweronoff.svg.192dpi.png")),
Priority = component.VerbPriority
});
}
private void TryStartup(EntityUid user, EntityUid target, DeadStartupButtonComponent comp)
{
if (!_net.IsServer)
return;
_audio.PlayPvs(comp.ButtonSound, target);
var args = new DoAfterArgs(EntityManager, user, comp.DoAfterInterval, new OnDoAfterButtonPressedEvent(), target, target:target)
{
BreakOnDamage = true,
BreakOnMove = true,
};
_doAfterSystem.TryStartDoAfter(args);
}
[Serializable, NetSerializable]
public sealed partial class OnDoAfterButtonPressedEvent : SimpleDoAfterEvent
{
}
}

View File

@ -0,0 +1,26 @@
using System.ComponentModel.DataAnnotations;
using Robust.Shared.Audio;
namespace Content.Shared._EE.Silicon.EmitBuzzWhileDamaged;
/// <summary>
/// This is used for controlling the cadence of the buzzing emitted by EmitBuzzOnCritSystem.
/// This component is used by mechanical species that can get to critical health.
/// </summary>
[RegisterComponent]
public sealed partial class EmitBuzzWhileDamagedComponent : Component
{
[DataField("buzzPopupCooldown")]
public TimeSpan BuzzPopupCooldown { get; private set; } = TimeSpan.FromSeconds(8);
[ViewVariables]
public TimeSpan LastBuzzPopupTime;
[DataField("cycleDelay")]
public float CycleDelay = 2.0f;
public float AccumulatedFrametime;
[DataField("sound")]
public SoundSpecifier Sound = new SoundCollectionSpecifier("buzzes");
}

View File

@ -0,0 +1,109 @@
using Content.Shared._EE.Silicon.Components;
using Content.Shared.Alert;
using Content.Shared.Bed.Sleep;
using Robust.Shared.Serialization;
using Content.Shared.Movement.Systems;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.PowerCell.Components;
namespace Content.Shared._EE.Silicon.Systems;
public sealed class SharedSiliconChargeSystem : EntitySystem
{
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SiliconComponent, ComponentInit>(OnSiliconInit);
SubscribeLocalEvent<SiliconComponent, SiliconChargeStateUpdateEvent>(OnSiliconChargeStateUpdate);
SubscribeLocalEvent<SiliconComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
SubscribeLocalEvent<SiliconComponent, ItemSlotInsertAttemptEvent>(OnItemSlotInsertAttempt);
SubscribeLocalEvent<SiliconComponent, ItemSlotEjectAttemptEvent>(OnItemSlotEjectAttempt);
SubscribeLocalEvent<SiliconComponent, TryingToSleepEvent>(OnTryingToSleep);
}
private void OnItemSlotInsertAttempt(EntityUid uid, SiliconComponent component, ref ItemSlotInsertAttemptEvent args)
{
if (args.Cancelled
|| !TryComp<PowerCellSlotComponent>(uid, out var cellSlotComp)
|| !_itemSlots.TryGetSlot(uid, cellSlotComp.CellSlotId, out var cellSlot)
|| cellSlot != args.Slot || args.User != uid)
return;
args.Cancelled = true;
}
private void OnItemSlotEjectAttempt(EntityUid uid, SiliconComponent component, ref ItemSlotEjectAttemptEvent args)
{
if (args.Cancelled
|| !TryComp<PowerCellSlotComponent>(uid, out var cellSlotComp)
|| !_itemSlots.TryGetSlot(uid, cellSlotComp.CellSlotId, out var cellSlot)
|| cellSlot != args.Slot || args.User != uid)
return;
args.Cancelled = true;
}
private void OnSiliconInit(EntityUid uid, SiliconComponent component, ComponentInit args)
{
if (!component.BatteryPowered)
return;
_alertsSystem.ShowAlert(uid, component.BatteryAlert, component.ChargeState);
}
private void OnSiliconChargeStateUpdate(EntityUid uid, SiliconComponent component, SiliconChargeStateUpdateEvent ev)
{
_alertsSystem.ShowAlert(uid, component.BatteryAlert, ev.ChargePercent);
}
private void OnRefreshMovespeed(EntityUid uid, SiliconComponent component, RefreshMovementSpeedModifiersEvent args)
{
if (!component.BatteryPowered)
return;
var closest = 0;
foreach (var state in component.SpeedModifierThresholds)
if (component.ChargeState >= state.Key && state.Key > closest)
closest = state.Key;
var speedMod = component.SpeedModifierThresholds[closest];
args.ModifySpeed(speedMod, speedMod);
}
/// <summary>
/// Silicon entities can now also be Living player entities. We may want to prevent them from sleeping if they can't sleep.
/// </summary>
private void OnTryingToSleep(EntityUid uid, SiliconComponent component, ref TryingToSleepEvent args)
{
args.Cancelled = !component.DoSiliconsDreamOfElectricSheep;
}
}
public enum SiliconType
{
Player,
GhostRole,
Npc,
}
/// <summary>
/// Event raised when a Silicon's charge state needs to be updated.
/// </summary>
[Serializable, NetSerializable]
public sealed class SiliconChargeStateUpdateEvent : EntityEventArgs
{
public short ChargePercent { get; }
public SiliconChargeStateUpdateEvent(short chargePercent)
{
ChargePercent = chargePercent;
}
}

View File

@ -0,0 +1,13 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared._EE.Silicon.WeldingHealing;
public abstract partial class SharedWeldingHealableSystem : EntitySystem
{
[Serializable, NetSerializable]
protected sealed partial class SiliconRepairFinishedEvent : SimpleDoAfterEvent
{
public float Delay;
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,14 @@
- files: ["beep_500.ogg", "beep_2000.ogg"]
license: "CC0-1.0"
copyright: "Synthesized in Audacity by BasedUser."
source: "https://github.com/ekrixi-14/ekrixi"
- files: ["pai_whistle.ogg"]
license: "CC-BY-4.0"
copyright: "Source sound by hubismal@GitHub, modified in Audacity by BasedUser."
source: "https://github.com/space-wizards/space-station-14/commit/3421e4f4de2613df1e92a4169a778335bc9faac4"
- files: ["whirr1.ogg", "whirr2.ogg", "whirr3.ogg"]
license: "CC0-1.0"
copyright: "Taken from source, spectrally modified and clipped"
source: "https://freesound.org/people/sad3d/sounds/500168/"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
silicon-behavior-buzz = Bzzzzt...

View File

@ -0,0 +1,2 @@
silicon-emote-deathgasp = seizes up and falls limp, {POSS-ADJ($entity)} lights sputtering into darkness...
chat-emote-msg-deathgasp-silicon = suddenly goes silent, with a hiss of grinding servos and a screech of dying myomers.

View File

@ -0,0 +1 @@
batteryslotrequireslock-component-alert-owner = {$user} is messing with your maintenance panel!

View File

@ -0,0 +1 @@
battery-electrocute-charge = The battery surges with energy!

View File

@ -0,0 +1,2 @@
battery-drinker-verb-drink = Drain
battery-drinker-empty = {CAPATALIZE(THE($target))} is already empty!

View File

@ -0,0 +1,4 @@
ipc-recharge-tip = You charged a litte of your battery.
dead-startup-button-verb = Reboot
dead-startup-system-reboot-success = {$target}'s system was rebooted.
dead-startup-system-reboot-failed = {$target}'s chassis is way too damaged.

View File

@ -0,0 +1,10 @@
marking-RobotAntennaTv = Tv
marking-RobotAntennaTesla = Tesla
marking-RobotAntennaLightb = Light (alt)
marking-RobotAntennaLight = Light
marking-RobotAntennaCyberhead = Cyberhead
marking-RobotAntennaSidelights = Sidelights
marking-RobotAntennaAntlers = Antlers
marking-RobotAntennaDroneeyes = Drone Eyes
marking-RobotAntennaCrowned = Crowned
marking-RobotAntennaTowers = Towers

View File

@ -0,0 +1,39 @@
marking-ScreenStatic = Static
marking-ScreenBlue = Blue
marking-ScreenBreakout = Breakout
marking-ScreenEight = Eight
marking-ScreenGoggles = Goggles
marking-ScreenExclaim = Exclaim
marking-ScreenHeart = Heart
marking-ScreenMonoeye = Cyclops
marking-ScreenNature = Naturalist
marking-ScreenOrange = Orange
marking-ScreenPink = Pink
marking-ScreenQuestion = Question
marking-ScreenShower = Shower
marking-ScreenYellow = Yellow
marking-ScreenScroll = Scroll
marking-ScreenConsole = Console
marking-ScreenRgb = RGB
marking-ScreenGlider = Glider
marking-ScreenRainbowhoriz = Horizontal Rainbow
marking-ScreenBsod = BSOD
marking-ScreenRedtext = Red Text
marking-ScreenSinewave = Sinewave
marking-ScreenSquarewave = Squarewave
marking-ScreenEcgwave = ECG wave
marking-ScreenEyes = Eyes
marking-ScreenEyestall = Tall Eyes
marking-ScreenEyesangry = Angry Eyes
marking-ScreenLoading = Loading...
marking-ScreenWindowsxp = Experience
marking-ScreenTetris = NT Block Game
marking-ScreenTv = Tv
marking-ScreenTextdrop = Textdrop
marking-ScreenStars = Stars
marking-ScreenRainbowdiag = Diagonal Rainbow
marking-ScreenBlank = Dead Pixel
marking-ScreenSmile = Smiley
marking-ScreenFrown = Frowny
marking-ScreenRing = Ring
marking-ScreenL = L

View File

@ -0,0 +1,96 @@
marking-MobIPCHeadDefault = Standard Operational Monitor
marking-MobIPCTorsoDefault = Standard Robotic Chassis
marking-MobIPCTorsoFemaleDefault = Standard Robotic Chassis
marking-MobIPCLArmDefault = Standard Left Robotic Arm
marking-MobIPCLHandDefault = Standard Left Robotic Hand
marking-MobIPCLLegDefault = Standard Left Robotic Leg
marking-MobIPCLFootDefault = Standard Left Robotic Foot
marking-MobIPCRArmDefault = Standard Right Robotic Arm
marking-MobIPCRHandDefault = Standard Right Robotic Hand
marking-MobIPCRLegDefault = Standard Right Robotic Leg
marking-MobIPCRFootDefault = Standard Right Robotic Foot
marking-CyberLimbsMarkingBishopHead = Operational Monitor from Bishop Cybernetics
marking-CyberLimbsMarkingBishopHeadAlt = Head from Bishop Cybernetics
marking-CyberLimbsMarkingBishopHeadAlt1 = Alternate Head from Bishop Cybernetics
marking-CyberLimbsMarkingBishopChest = Robotic Chassis from Bishop Cybernetics
marking-CyberLimbsMarkingBishopLArm = Left Robotic Arm from Bishop Cybernetics
marking-CyberLimbsMarkingBishopLHand = Left Robotic Hand from Bishop Cybernetics
marking-CyberLimbsMarkingBishopLLeg = Left Robotic Leg from Bishop Cybernetics
marking-CyberLimbsMarkingBishopLFoot = Left Robotic Foot from Bishop Cybernetics
marking-CyberLimbsMarkingBishopRArm = Right Robotic Arm from Bishop Cybernetics
marking-CyberLimbsMarkingBishopRHand = Right Robotic Hand from Bishop Cybernetics
marking-CyberLimbsMarkingBishopRLeg = Right Robotic Leg from Bishop Cybernetics
marking-CyberLimbsMarkingBishopRFoot = Right Robotic Foot from Bishop Cybernetics
marking-CyberLimbsMarkingHesphiastosHead = Operational Monitor from Hesphiastos Industries
marking-CyberLimbsMarkingHesphiastosHeadAlt = Head from Hesphiastos Industries
marking-CyberLimbsMarkingHesphiastosChest = Robotic Chassis from Hesphiastos Industries
marking-CyberLimbsMarkingHesphiastosLArm = Left Robotic Arm from Hesphiastos Industries
marking-CyberLimbsMarkingHesphiastosLHand = Left Robotic Hand from Hesphiastos Industries
marking-CyberLimbsMarkingHesphiastosLLeg = Left Robotic Leg from Hesphiastos Industries
marking-CyberLimbsMarkingHesphiastosLFoot = Left Robotic Foot from Hesphiastos Industries
marking-CyberLimbsMarkingHesphiastosRArm = Right Robotic Arm from Hesphiastos Industries
marking-CyberLimbsMarkingHesphiastosRHand = Right Robotic Hand from Hesphiastos Industries
marking-CyberLimbsMarkingHesphiastosRLeg = Right Robotic Leg from Hesphiastos Industries
marking-CyberLimbsMarkingHesphiastosRFoot = Right Robotic Foot from Hesphiastos Industries
marking-CyberLimbsMarkingWardtakahashiHead = Operational Monitor from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiHeadAlt = Head from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiHeadAlt1 = Alternate Head from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiChest = Robotic Chassis from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiLArm = Left Robotic Arm from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiLHand = Left Robotic Hand from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiLLeg = Left Robotic Leg from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiLFoot = Left Robotic Foot from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiRArm = Right Robotic Arm from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiRHand = Right Robotic Hand from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiRLeg = Right Robotic Leg from Ward-Takahashi
marking-CyberLimbsMarkingWardtakahashiRFoot = Right Robotic Foot from Ward-Takahashi
marking-CyberLimbsMarkingXionHead = Operational Monitor from Xion Manufacturing Group
marking-CyberLimbsMarkingXionHeadAlt = Head from Xion Manufacturing Group
marking-CyberLimbsMarkingXionChest = Robotic Chassis from Xion Manufacturing Group
marking-CyberLimbsMarkingXionLArm = Left Robotic Arm from Xion Manufacturing Group
marking-CyberLimbsMarkingXionLHand = Left Robotic Hand from Xion Manufacturing Group
marking-CyberLimbsMarkingXionLLeg = Left Robotic Leg from Xion Manufacturing Group
marking-CyberLimbsMarkingXionLFoot = Left Robotic Foot from Xion Manufacturing Group
marking-CyberLimbsMarkingXionRArm = Right Robotic Arm from Xion Manufacturing Group
marking-CyberLimbsMarkingXionRHand = Right Robotic Hand from Xion Manufacturing Group
marking-CyberLimbsMarkingXionRLeg = Right Robotic Leg from Xion Manufacturing Group
marking-CyberLimbsMarkingXionRFoot = Right Robotic Foot from Xion Manufacturing Group
marking-CyberLimbsMarkingShellguardHead = Operational Monitor from Shellguard Munitions
marking-CyberLimbsMarkingShellguardHeadAlt = Head from Shellguard Munitions
marking-CyberLimbsMarkingShellguardChest = Robotic Chassis from Shellguard Munitions
marking-CyberLimbsMarkingShellguardLArm = Left Robotic Arm from Shellguard Munitions
marking-CyberLimbsMarkingShellguardLHand = Left Robotic Hand from Shellguard Munitions
marking-CyberLimbsMarkingShellguardLLeg = Left Robotic Leg from Shellguard Munitions
marking-CyberLimbsMarkingShellguardLFoot = Left Robotic Foot from Shellguard Munitions
marking-CyberLimbsMarkingShellguardRArm = Right Robotic Arm from Shellguard Munitions
marking-CyberLimbsMarkingShellguardRHand = Right Robotic Hand from Shellguard Munitions
marking-CyberLimbsMarkingShellguardRLeg = Right Robotic Leg from Shellguard Munitions
marking-CyberLimbsMarkingShellguardRFoot = Right Robotic Foot from Shellguard Munitions
marking-CyberLimbsMarkingMorpheusHead = Operational Monitor from Morpheus Cyberkinetics
marking-CyberLimbsMarkingMorpheusHeadAlt = Head from Morpheus Cyberkinetics
marking-CyberLimbsMarkingMorpheusChest = Robotic Chassis from Morpheus Cyberkinetics
marking-CyberLimbsMarkingMorpheusLArm = Left Robotic Arm from Morpheus Cyberkinetics
marking-CyberLimbsMarkingMorpheusLHand = Left Robotic Hand from Morpheus Cyberkinetics
marking-CyberLimbsMarkingMorpheusLLeg = Left Robotic Leg from Morpheus Cyberkinetics
marking-CyberLimbsMarkingMorpheusLFoot = Left Robotic Foot from Morpheus Cyberkinetics
marking-CyberLimbsMarkingMorpheusRArm = Right Robotic Arm from Morpheus Cyberkinetics
marking-CyberLimbsMarkingMorpheusRHand = Right Robotic Hand from Morpheus Cyberkinetics
marking-CyberLimbsMarkingMorpheusRLeg = Right Robotic Leg from Morpheus Cyberkinetics
marking-CyberLimbsMarkingMorpheusRFoot = Right Robotic Foot from Morpheus Cyberkinetics
marking-CyberLimbsMarkingZenghuHead = Head from Zenghu Pharmaceuticals
marking-CyberLimbsMarkingZenghuChest = Robotic Chassis from Zenghu Pharmaceuticals
marking-CyberLimbsMarkingZenghuRHand = Right Robotic Hand from Zenghu Pharmaceuticals
marking-CyberLimbsMarkingZenghuRArm = Right Robotic Arm from Zenghu Pharmaceuticals
marking-CyberLimbsMarkingZenghuLHand = Left Robotic Hand from Zenghu Pharmaceuticals
marking-CyberLimbsMarkingZenghuLArm = Left Robotic Arm from Zenghu Pharmaceuticals
marking-CyberLimbsMarkingZenghuRLeg = Right Robotic Leg from Zenghu Pharmaceuticals
marking-CyberLimbsMarkingZenghuRFoot = Right Robotic Foot from Zenghu Pharmaceuticals
marking-CyberLimbsMarkingZenghuLLeg = Left Robotic Leg from Zenghu Pharmaceuticals
marking-CyberLimbsMarkingZenghuLFoot = Left Robotic Foot from Zenghu Pharmaceuticals

View File

@ -0,0 +1,4 @@
silicon-charger-overheatwarning = You feel like you're in a microwave!
silicon-charger-chargerate-string = Charge rate
silicon-charger-efficiency-string = Efficiency
silicon-charger-list-full = {CAPITALIZE(THE($charger))} can only accommodate so many targets!

View File

@ -0,0 +1,3 @@
silicon-overheating = You feel your circuits overheating!
silicon-crit = Structural integrity critical!
silicon-power-low = Power low!

View File

@ -0,0 +1 @@
namepreset-firstdashlast = {$first}-{$last}

View File

@ -0,0 +1 @@
species-name-ipc = IPC

View File

@ -200,6 +200,7 @@
tags:
- ClownMask
- WhitelistChameleon
- IPCMaskWearable # EE - IPCs
- type: HideLayerClothing
slots:
- Snout
@ -263,6 +264,9 @@
- type: HideLayerClothing
slots:
- Snout
- type: Tag
tags:
- IPCMaskWearable # EE - IPCs
- type: entity
parent: ClothingMaskBase
@ -280,6 +284,7 @@
tags:
- HamsterWearable
- WhitelistChameleon
- IPCMaskWearable # EE - IPCs
- type: HideLayerClothing
slots:
- Snout
@ -461,6 +466,7 @@
tags:
- HamsterWearable
- WhitelistChameleon
- IPCMaskWearable # EE - IPCs
- type: HideLayerClothing
slots:
- Snout
@ -481,6 +487,9 @@
- type: HideLayerClothing
slots:
- Snout
- type: Tag
tags:
- IPCMaskWearable # EE - IPCs
- type: entity
parent: ClothingMaskBase
@ -497,6 +506,9 @@
- type: HideLayerClothing
slots:
- Snout
- type: Tag
tags:
- IPCMaskWearable # EE - IPCs
- type: entity
parent: ClothingMaskBase
@ -513,6 +525,9 @@
- type: HideLayerClothing
slots:
- Snout
- type: Tag
tags:
- IPCMaskWearable # EE - IPCs
- type: entity
parent: ClothingMaskBase
@ -529,6 +544,9 @@
- type: HideLayerClothing
slots:
- Snout
- type: Tag
tags:
- IPCMaskWearable # EE - IPCs
- type: entity
parent: ClothingMaskBase
@ -545,6 +563,9 @@
- type: HideLayerClothing
slots:
- Snout
- type: Tag
tags:
- IPCMaskWearable # EE - IPCs
- type: entity
parent: ClothingMaskBase
@ -561,6 +582,9 @@
- type: HideLayerClothing
slots:
- Snout
- type: Tag
tags:
- IPCMaskWearable # EE - IPCs
- type: entity
parent: ClothingMaskBase
@ -577,6 +601,7 @@
- type: Tag
tags:
- WhitelistChameleon
- IPCMaskWearable # EE - IPCs
- type: entity
parent: ClothingMaskNeckGaiter
@ -663,3 +688,6 @@
- type: EyeProtection
- type: BreathMask
- type: IdentityBlocker
- type: Tag
tags:
- IPCMaskWearable # EE - IPCs

View File

@ -39,6 +39,9 @@
type: ChameleonBoundUserInterface
enum.VoiceMaskUIKey.Key:
type: VoiceMaskBoundUserInterface
- type: Tag
tags:
- IPCMaskWearable # EE - IPCs
- type: entity
parent: ClothingMaskBase

View File

@ -48,6 +48,12 @@
solutions:
glass:
canReact: false
# EE - IPC Healing
- type: BlindHealing
damageContainers:
- Silicon
- type: StackPrice
price: 2
- type: entity
parent: SheetGlassBase

View File

@ -99,6 +99,9 @@
stopSearchVerbText: positronic-brain-stop-searching-verb-text
stopSearchVerbPopup: positronic-brain-stopped-searching
job: Borg
- type: Organ
slotId: posbrain
- type: Brain
- type: BlockMovement
- type: Examiner
- type: BorgBrain

View File

@ -29,6 +29,19 @@
- type: PhysicalComposition
materialComposition:
Steel: 15
# EE Change Start
#Same as Ointment but divided by 5 and 3 because StackPrice needs to be 1 - Estacao Pirata IPCs
#1 Ointment = -50 damage of those types
#1 Cable ~= -50 (-49.8) damage of those types
- type: Healing
delay: 0.6
damageContainers:
- Silicon
damage:
types: # these are all split across multiple types
Heat: -1.66
Shock: -1.66
# EE Change End
# FIXME: Used isnt actually implemented so its still unlimited.
# Uncomment this when its implemented, and make sure it handles StackComponent right
#- type: Hemostat # Shitmed

View File

@ -105,6 +105,15 @@
price: 40
- type: IgnitionSource
temperature: 700
- type: WeldingHealing # Same as Brutepack - EE IPCs
damageContainers:
- Silicon
fuelCost: 5
damage:
types:
Blunt: -15
Piercing: -15
Slash: -15
- type: Cautery # Shitmed
speed: 0.7
- type: SurgeryTool # Shitmed

View File

@ -657,6 +657,20 @@
- LightHeadBorg
- TorsoBorg
- BorgModuleSurgery # Shitmed Change
# Shitmed Change
- BorgModuleSurgery
- TorsoIPC
- LeftArmIPC
- RightArmIPC
- LeftLegIPC
- RightLegIPC
- HeadIPC
- LeftHandIPC
- RightHandIPC
- LeftFootIPC
- RightFootIPC
- OrganIPCEyes
- OrganIPCPump
dynamicRecipes:
- ProximitySensor
- ClothingEyesHudDiagnostic

View File

@ -143,6 +143,8 @@
priority: 1
- type: StaticPrice
price: 500
- type: BatteryDrinkerSource # EE IPCs
maxAmount: 10000
# APC under construction
- type: entity
@ -209,6 +211,8 @@
- type: Battery
maxCharge: 50000
startingCharge: 50000
- type: BatteryDrinkerSource # EE IPCs
maxAmount: 5000
- type: entity
parent: BaseAPC
@ -218,6 +222,8 @@
- type: Battery
maxCharge: 100000
startingCharge: 100000
- type: BatteryDrinkerSource # EE IPCs
maxAmount: 12000
- type: entity
parent: BaseAPC
@ -227,6 +233,8 @@
- type: Battery
maxCharge: 150000
startingCharge: 150000
- type: BatteryDrinkerSource # EE IPCs
maxAmount: 18000
- type: entity
parent: BaseAPC
@ -236,3 +244,5 @@
- type: Battery
maxCharge: 200000
startingCharge: 200000
- type: BatteryDrinkerSource # EE IPCs
maxAmount: 26000

View File

@ -247,6 +247,7 @@
whitelist:
components:
- BorgChassis
- Silicon # EE IPCs
- type: Construction
containers:
- machine_parts

View File

@ -18,6 +18,7 @@
- Oni
- Rodentia
- Chitinid
- IPC
- type: guideEntry
id: Arachnid
@ -58,3 +59,8 @@
id: Vox
name: species-name-vox
text: "/ServerInfo/Guidebook/Mobs/Vox.xml"
- type: guideEntry
id: IPC
name: species-name-ipc
text: "/ServerInfo/Guidebook/Mobs/IPCs.xml"

View File

@ -0,0 +1,76 @@
- type: entity
id: BaseIPCOrgan
parent: BaseItem
abstract: true
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/organs.rsi
- type: Organ
# - type: Food
# - type: Extractable
# grindableSolutionName: organ
- type: SolutionContainerManager
solutions:
organ:
reagents:
- ReagentId: Oil
Quantity: 10
- type: entity
id: OrganIPCEyes
parent: BaseIPCOrgan
name: robotic eyes
description: "01001001 00100000 01110011 01100101 01100101 00100000 01111001 01101111 01110101 00100001"
components:
- type: Sprite
layers:
- state: eyeball-l
- state: eyeball-r
- type: Organ
slotId: eyes
- type: Eyes
- type: entity
id: OrganIPCTongue
parent: BaseIPCOrgan
name: vocal modulator
description: "A vocal modulator, used to produce speech."
components:
- type: Sprite
state: tongue
- type: Organ
- type: entity
id: OrganIPCEars
parent: BaseIPCOrgan
name: "sonic receptors"
description:
components:
- type: Sprite
state: ears
- type: Organ
- type: entity
id: OrganIPCPump
parent: BaseIPCOrgan
name: micro pump
description: "A micro pump, used to circulate coolant."
components:
- type: Sprite
state: heart-on
- type: Organ
slotId: pump
- type: Heart
# The heart 'metabolizes' medicines and poisons that aren't filtered out by other organs.
# This is done because these chemicals need to have some effect even if they aren't being filtered out of your body.
# You're technically 'immune to poison' without a heart, but.. uhh, you'll have bigger problems on your hands.
# This is fine?
# - type: Metabolizer
# maxReagents: 2
# metabolizerTypes: [Human]
# groups:
# - id: Medicine
# - id: Poison
# - id: Narcotic

View File

@ -0,0 +1,263 @@
- type: entity
id: PartIPC
parent: BaseItem
name: "ipc body part"
abstract: true
components:
- type: Damageable
damageContainer: Inorganic # Shitmed Change
- type: BodyPart
- type: Gibbable
- type: ContainerContainer
containers:
bodypart: !type:Container
ents: []
- type: SurgeryTool
startSound:
path: /Audio/_Shitmed/Medical/Surgery/organ1.ogg
endSound:
path: /Audio/_Shitmed/Medical/Surgery/organ2.ogg
- type: Destructible
thresholds:
- trigger:
!type:DamageTypeTrigger
damageType: Blunt
damage: 110
behaviors:
- !type:GibPartBehavior { }
- trigger:
!type:DamageTypeTrigger
damageType: Slash
damage: 150
behaviors:
- !type:GibPartBehavior { }
- type: StaticPrice
price: 100
- type: entity
id: TorsoIPC
name: "ipc torso"
parent: PartIPC
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "torso_m"
- type: Icon
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "torso_m"
- type: BodyPart
partType: Torso
children: # I hate this so much I want to kill it AAAAAAAAAAAAAAAAAAAAAAAA
head:
id: "head"
type: Head
left arm:
id: "left arm"
type: Arm
right arm:
id: "right arm"
type: Arm
left leg:
id: "left leg"
type: Leg
right leg:
id: "right leg"
type: Leg
organs:
posbrain:
id: "posbrain"
pump:
id: "pump"
- type: ContainerContainer
containers:
torso_slot: !type:ContainerSlot {}
- type: Destructible
thresholds:
- trigger:
!type:DamageTypeTrigger
damageType: Blunt
damage: 400
behaviors:
- !type:GibPartBehavior { }
- trigger:
!type:DamageTypeTrigger
damageType: Slash
damage: 400
behaviors:
- !type:GibPartBehavior { }
- type: entity
id: HeadIPC
name: "ipc head"
parent: PartIPC
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "head_m"
- type: Icon
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "head_m"
- type: BodyPart
partType: Head
organs:
eyes:
id: "eyes"
vital: true
- type: Input
context: "ghost"
- type: MovementSpeedModifier
baseWalkSpeed: 0
baseSprintSpeed: 0
- type: InputMover
- type: GhostOnMove
- type: Tag
tags:
- Head
- type: entity
id: LeftArmIPC
name: "left ipc arm"
parent: PartIPC
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "l_arm"
- type: Icon
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "l_arm"
- type: BodyPart
partType: Arm
symmetry: Left
children:
left hand:
id: "left hand"
type: Hand
- type: entity
id: RightArmIPC
name: "right ipc arm"
parent: PartIPC
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "r_arm"
- type: Icon
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "r_arm"
- type: BodyPart
partType: Arm
symmetry: Right
children:
right hand:
id: "right hand"
type: Hand
- type: entity
id: LeftHandIPC
name: "left ipc hand"
parent: PartIPC
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "l_hand"
- type: Icon
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "l_hand"
- type: BodyPart
partType: Hand
symmetry: Left
- type: entity
id: RightHandIPC
name: "right ipc hand"
parent: PartIPC
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "r_hand"
- type: Icon
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "r_hand"
- type: BodyPart
partType: Hand
symmetry: Right
- type: entity
id: LeftLegIPC
name: "left ipc leg"
parent: PartIPC
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "l_leg"
- type: Icon
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "l_leg"
- type: BodyPart
partType: Leg
symmetry: Left
children:
left foot:
id: "left foot"
type: Foot
- type: MovementBodyPart
- type: entity
id: RightLegIPC
name: "right ipc leg"
parent: PartIPC
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "r_leg"
- type: Icon
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "r_leg"
- type: BodyPart
partType: Leg
symmetry: Right
children:
right foot:
id: "right foot"
type: Foot
- type: MovementBodyPart
- type: entity
id: LeftFootIPC
name: "left ipc foot"
parent: PartIPC
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "l_foot"
- type: Icon
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "l_foot"
- type: BodyPart
partType: Foot
symmetry: Left
- type: entity
id: RightFootIPC
name: "right ipc foot"
parent: PartIPC
components:
- type: Sprite
netsync: false
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "r_foot"
- type: Icon
sprite: _EE/Mobs/Species/IPC/parts.rsi
state: "r_foot"
- type: BodyPart
partType: Foot
symmetry: Right

View File

@ -0,0 +1,45 @@
- type: body
id: IPC
name: "ipc"
root: torso
slots:
head:
part: HeadIPC
connections:
- torso
organs:
eyes: OrganIPCEyes
torso:
part: TorsoIPC
connections:
- right arm
- left arm
- right leg
- left leg
organs:
posbrain: PositronicBrain
pump: OrganIPCPump
right arm:
part: RightArmIPC
connections:
- right hand
left arm:
part: LeftArmIPC
connections:
- left hand
right hand:
part: RightHandIPC
left hand:
part: LeftHandIPC
right leg:
part: RightLegIPC
connections:
- right foot
left leg:
part: LeftLegIPC
connections:
- left foot
right foot:
part: RightFootIPC
left foot:
part: LeftFootIPC

View File

@ -0,0 +1,7 @@
- type: damageModifierSet
id: IPC
coefficients:
Poison: 0
Cold: 0.2
Heat: 2
Shock: 2.5

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,318 @@
# All the Harpy customization
# Ears Markings
- type: marking
id: HarpyWingDefaultHuescale
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
color: "#964b00"
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: huescale_harpy
- type: marking
id: HarpyWingDefaultWhitescale
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
color: "#964b00"
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: whitescale_harpy
- type: marking
id: HarpyWingClassic
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
color: "#964b00"
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: classic_harpy
- type: marking
id: HarpyWingFoldedHuescale
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
color: "#964b00"
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: huescale_harpy_folded
- type: marking
id: HarpyWingFoldedWhitescale
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
color: "#964b00"
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: whitescale_harpy_folded
- type: marking
id: HarpyWingOwlHuescale
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
color: "#964b00"
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: huescale_harpy_wing_owl
- type: marking
id: HarpyWingOwlWhitescale
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
color: "#964b00"
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: whitescale_harpy_wing_owl
- type: marking
id: HarpyEarsLarge
bodyPart: Head
markingCategory: HeadTop
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
color: "#964b00"
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_ears.rsi
state: harpy_ears_large
- type: marking
id: HarpyTailForkedWhitescale
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_tails.rsi
state: whitescale_forked_tailfin
- type: marking
id: HarpyTailForkedHuescale
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_tails.rsi
state: huescale_forked_tailfin
- type: marking
id: HarpyTailPeacock
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_tails48x48.rsi
state: peacock_tail_feathers
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_tails48x48.rsi
state: peacock_tail_eyes
- type: marking
id: HarpyTailHaven
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_tails.rsi
state: haven_tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_tails.rsi
state: haven_tone_2
- type: marking
id: HarpyTailForkedLong
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_tails.rsi
state: forked_long
- type: marking
id: HarpyTailSwallow
bodyPart: Tail
markingCategory: Tail
speciesRestriction: [Harpy]
coloring:
default:
type:
!type:CategoryColoring
category: Hair
fallbackTypes:
- !type:SimpleColoring
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_tails.rsi
state: swallow_tail
- type: marking
id: HarpyWing2ToneClassic
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_2tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_2tone_2
- type: marking
id: HarpyWing3ToneClassic
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_3tone_3
- type: marking
id: HarpyWingSpeckledClassic
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_speckled_1
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_speckled_2
- type: marking
id: HarpyWingUndertoneClassic
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_undertone_1
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_undertone_2
- type: marking
id: HarpyWingTipsClassic
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_wingtip_1
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: harpy_wingtip_2
- type: marking
id: HarpyWingBat
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: bat_wings_tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: bat_wings_tone_2
- type: marking
id: HarpyWingBionic
bodyPart: RArm
markingCategory: Arms
speciesRestriction: [Harpy]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: bionic_wings_tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Harpy/harpy_wings.rsi
state: bionic_wings_tone_2
shader: unshaded

View File

@ -0,0 +1,13 @@
- type: marking
id: OniTwoToedFeet
bodyPart: RFoot # Can't be LFoot to avoid visual glitches
markingCategory: Legs
speciesRestriction: [Oni]
coloring:
default:
type:
!type:SimpleColoring
color: "#454545"
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/oni_feet.rsi
state: two_toes

View File

@ -0,0 +1,525 @@
- type: marking
id: OniHornTallCurved
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: tall_curved
- type: marking
id: OniHornTallCurved3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: tall_curved_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: tall_curved_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: tall_curved_3tone_3
- type: marking
id: OniHornTallBull
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: tall_bull
- type: marking
id: OniHornTallBull3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: tall_bull_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: tall_bull_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: tall_bull_3tone_3
- type: marking
id: OniHornCrowned
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: crowned
- type: marking
id: OniHornSerket
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: serket
- type: marking
id: OniHornSerket3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: serket_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: serket_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: serket_3tone_3
- type: marking
id: OniHornPisces
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: pisces
- type: marking
id: OniHornPisces2Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: pisces_2tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: pisces_2tone_2
- type: marking
id: OniHornPisces3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: pisces_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: pisces_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: pisces_3tone_3
- type: marking
id: OniHornVirgo
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: virgo
- type: marking
id: OniHornVirgo3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: virgo_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: virgo_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: virgo_3tone_3
- type: marking
id: OniHornSagittarius
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: sagittarius
- type: marking
id: OniHornSagittarius3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: sagittarius_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: sagittarius_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: sagittarius_3tone_3
- type: marking
id: OniHornVantas
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: vantas
- type: marking
id: OniHornVantas3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: vantas_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: vantas_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: vantas_3tone_3
- type: marking
id: OniHornMakara
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: makara
- type: marking
id: OniHornMakara2Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: makara_2tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: makara_2tone_2
- type: marking
id: OniHornMakara3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: makara_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: makara_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns48x48.rsi
state: makara_3tone_3
- type: marking
id: OniHornNepeta
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: nepeta
- type: marking
id: OniHornNepeta3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: nepeta_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: nepeta_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: nepeta_3tone_3
- type: marking
id: OniHornTaurus
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: taurus
- type: marking
id: OniHornTaurus2Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: taurus_2tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: taurus_2tone_2
- type: marking
id: OniHornTaurus3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: taurus_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: taurus_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: taurus_3tone_3
- type: marking
id: OniHornAries
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: aries
- type: marking
id: OniHornAries3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: aries_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: aries_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: aries_3tone_3
- type: marking
id: OniHornTavris
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: tavris
- type: marking
id: OniHornTavris3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: tavris_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: tavris_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: tavris_3tone_3
- type: marking
id: OniHornInclined
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: inclined
- type: marking
id: OniHornInclined3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: inclined_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: inclined_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: inclined_3tone_3
- type: marking
id: OniHornWavy
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: wavy
- type: marking
id: OniHornWavy2Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: wavy_2tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: wavy_2tone_2
- type: marking
id: OniHornWavy3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: wavy_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: wavy_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: wavy_3tone_3
- type: marking
id: OniHornAntlers
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: antlers_2tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: antlers_2tone_2
- type: marking
id: OniHornAntlers3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: antlers_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: antlers_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: antlers_3tone_3
- type: marking
id: OniHornUnicorn
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: unicorn
- type: marking
id: OniHornErebia
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: erebia
- type: marking
id: OniHornErebia3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: erebia_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: erebia_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: erebia_3tone_3
- type: marking
id: OniHornErebiaRings
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: erebia
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: erebia_rings
- type: marking
id: OniHornDoubleThick
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_thick
- type: marking
id: OniHornDoubleThick2Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_thick_2tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_thick_2tone_2
- type: marking
id: OniHornDoubleThick3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_thick_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_thick_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_thick_3tone_3
- type: marking
id: OniHornDoubleCurved3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_curved_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_curved_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_curved_3tone_3
- type: marking
id: OniHornDoubleCurvedOutwards3Tone
bodyPart: HeadTop
markingCategory: HeadTop
forcedColoring: false
speciesRestriction: [Oni]
sprites:
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_curved_outwards_3tone_1
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_curved_outwards_3tone_2
- sprite: _EinsteinEngines/Mobs/Customization/Oni/oni_horns.rsi
state: double_curved_outwards_3tone_3

Some files were not shown because too many files have changed in this diff Show More