Skip to content

Commit

Permalink
Complete IPC overhaul (Simple-Station#31)
Browse files Browse the repository at this point in the history
  • Loading branch information
Day-OS authored and Timemaster99 committed Aug 16, 2024
1 parent 08822e3 commit 2ce54df
Show file tree
Hide file tree
Showing 322 changed files with 7,804 additions and 41 deletions.
3 changes: 2 additions & 1 deletion Content.Server/Administration/Commands/SetOutfitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Robust.Shared.Console;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Content.Server.Silicons.IPC;

namespace Content.Server.Administration.Commands
{
Expand Down Expand Up @@ -126,7 +127,7 @@ public static bool SetOutfit(EntityUid target, string gear, IEntityManager entit
handsSystem.TryPickup(target, inhandEntity, checkActionBlocker: false, handsComp: handsComponent);
}
}

InternalEncryptionKeySpawner.TryInsertEncryptionKey(target, startingGear, entityManager, profile); // Parkstation - IPC
return true;
}
}
Expand Down
3 changes: 2 additions & 1 deletion Content.Server/Bed/BedSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Content.Shared.Emag.Systems;
using Content.Shared.Mobs.Systems;
using Robust.Shared.Timing;
using Content.Shared.SimpleStation14.Silicon.Components; // Parkstation-IPCs // I shouldn't have to modify this.

namespace Content.Server.Bed
{
Expand Down Expand Up @@ -65,7 +66,7 @@ public override void Update(float frameTime)

foreach (var healedEntity in strapComponent.BuckledEntities)
{
if (_mobStateSystem.IsDead(healedEntity))
if (_mobStateSystem.IsDead(healedEntity) || HasComp<SiliconComponent>(healedEntity)) // Parkstation-IPCs // I shouldn't have to modify this.
continue;

var damage = bedComponent.Damage;
Expand Down
12 changes: 7 additions & 5 deletions Content.Server/Electrocution/ElectrocutionSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ 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; // Parkstation-IPC // 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;

Expand Down Expand Up @@ -297,9 +299,9 @@ public override bool TryDoElectrocution(
|| !DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects))
return false;

RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient), true);
return true;
}
RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient, shockDamage), true); // Parkstation-IPC
return true;
}

private bool TryDoElectrocutionPowered(
EntityUid uid,
Expand Down Expand Up @@ -347,7 +349,7 @@ private bool TryDoElectrocutionPowered(
electrocutionComponent.Electrocuting = uid;
electrocutionComponent.Source = sourceUid;

RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient), true);
RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient, shockDamage), true); // Parkstation-IPC

return true;
}
Expand Down
6 changes: 6 additions & 0 deletions Content.Server/Mobs/DeathgaspComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ public sealed partial class DeathgaspComponent : Component
/// </summary>
[DataField("prototype", customTypeSerializer:typeof(PrototypeIdSerializer<EmotePrototype>))]
public string Prototype = "DefaultDeathgasp";

/// <summary>
/// Makes sure that the deathgasp is only displayed if the entity went critical before dying
/// </summary>
[DataField("needsCritical")]
public bool NeedsCritical = true;
}
4 changes: 3 additions & 1 deletion Content.Server/Mobs/DeathgaspSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ public override void Initialize()
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 && args.OldMobState != MobState.Critical)
return;
if (args.NewMobState != MobState.Dead)
return;

Deathgasp(uid, component);
Expand Down
39 changes: 39 additions & 0 deletions Content.Server/Silicons/IPC/InternalEncryptionKeySpawner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Content.Shared.Radio.Components; // Parkstation-IPC
using Content.Shared.Containers; // Parkstation-IPC
using Robust.Shared.Containers; // Parkstation-IPC

// Pretty much copied from StationSpawningSystem.SpawnStartingGear
namespace Content.Server.Silicons.IPC;
public static class InternalEncryptionKeySpawner
{
public static void TryInsertEncryptionKey(EntityUid target, StartingGearPrototype startingGear, IEntityManager entityManager, HumanoidCharacterProfile? profile)
{
if (entityManager.TryGetComponent<EncryptionKeyHolderComponent>(target, out var keyHolderComp))
{
var earEquipString = startingGear.GetGear("ears", profile);
var containerMan = entityManager.System<SharedContainerSystem>();

if (!string.IsNullOrEmpty(earEquipString))
{
var earEntity = entityManager.SpawnEntity(earEquipString, entityManager.GetComponent<TransformComponent>(target).Coordinates);

if (entityManager.TryGetComponent<EncryptionKeyHolderComponent>(earEntity, out _) && // I had initially wanted this to spawn the headset, and simply move all the keys over, but the headset didn't seem to have any keys in it when spawned...
entityManager.TryGetComponent<ContainerFillComponent>(earEntity, out var fillComp) &&
fillComp.Containers.TryGetValue(EncryptionKeyHolderComponent.KeyContainerName, out var defaultKeys))
{
containerMan.CleanContainer(keyHolderComp.KeyContainer);

foreach (var key in defaultKeys)
{
var keyEntity = entityManager.SpawnEntity(key, entityManager.GetComponent<TransformComponent>(target).Coordinates);
containerMan.Insert(keyEntity, keyHolderComp.KeyContainer, force: true);
}
}

entityManager.QueueDeleteEntity(earEntity);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace Content.Server.SimpleStation14.Power;

[RegisterComponent]
public sealed partial class BatteryDrinkerComponent : Component
{
/// <summary>
/// Is this drinker allowed to drink batteries not tagged as <see cref="BatteryDrinkSource"/>?
/// </summary>
[DataField("drinkAll"), ViewVariables(VVAccess.ReadWrite)]
public bool DrinkAll = false;

/// <summary>
/// How long it takes to drink from a battery, in seconds.
/// Is multiplied by the source.
/// </summary>
[DataField("drinkSpeed"), ViewVariables(VVAccess.ReadWrite)]
public float DrinkSpeed = 1.5f;

/// <summary>
/// The multiplier for the amount of power to attempt to drink.
/// Default amount is 1000
/// </summary>
[DataField("drinkMultiplier"), ViewVariables(VVAccess.ReadWrite)]
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("drinkAllMultiplier"), ViewVariables(VVAccess.ReadWrite)]
public float DrinkAllMultiplier = 2.5f;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Numerics;

namespace Content.Server.SimpleStation14.Power.Components;

[RegisterComponent]
public sealed partial class RandomBatteryChargeComponent : Component
{
/// <summary>
/// The minimum and maximum max charge the battery can have.
/// </summary>
[DataField("batteryMaxMinMax")]
public Vector2 BatteryMaxMinMax = new(0.85f, 1.15f);

/// <summary>
/// The minimum and maximum current charge the battery can have.
/// </summary>
[DataField("batteryChargeMinMax")]
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("basedOnMaxCharge")]
public bool BasedOnMaxCharge = true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.ComponentModel.DataAnnotations;
using Robust.Shared.Audio;
using Content.Server.Sound.Components;

namespace Content.Server.SimpleStation14.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("sound"), Required]
public SoundSpecifier Sound = default!;

[DataField("interval")]
public float Interval = 8f;

[DataField("playChance")]
public float PlayChance = 1f;

[DataField("popUp")]
public string? PopUp;
}
144 changes: 144 additions & 0 deletions Content.Server/SimpleStation14/Power/Systems/BatteryDrinkerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
using System.Diagnostics.CodeAnalysis;
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.SimpleStation14.Silicon;
using Content.Shared.Verbs;
using Robust.Shared.Utility;
using Content.Server.SimpleStation14.Silicon.Charge;
using Content.Server.Power.EntitySystems;
using Content.Server.Popups;
using Content.Server.PowerCell;
using Content.Shared.Popups;
using Content.Shared.SimpleStation14.Silicon.Components;
using FastAccessors;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;

namespace Content.Server.SimpleStation14.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,
BreakOnTargetMove = 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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;

namespace Content.Server.SimpleStation14.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 damagePerWatt = ElectrocutionSystem.ElectrifiedDamagePerWatt * 2;

var damage = args.ShockDamage.Value * args.SiemensCoefficient;
var charge = Math.Min(damage / damagePerWatt, 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);
}
}
Loading

0 comments on commit 2ce54df

Please sign in to comment.