Skip to content
This repository has been archived by the owner on Dec 30, 2024. It is now read-only.

Commit

Permalink
[Species] InterPersonal Conflict (#97)
Browse files Browse the repository at this point in the history
# Description
Adds IPCs!
Everyone's favourite Positronic species finally available to be played
in Space Station 14!

# TODO
- [x] Make the basis of the SiliconCharge system
- [x] Make a working IPC species
- [x] Improve upon Borg
- [x] Add chargers and charging systems
- [x] Localize all strings
- [ ] ~~Make a hud/shader effect for power drain~~
- [ ] ~~Make sure IPCs can be refilled (blood transfusions)~~ Seperate
pr #172
- [x] Make sure IPCs can't become zombies/suffer from many other
biological issues (will likely need testing)
- [x] Make chargers take machine parts for upgrading
- [ ] ~~Make Silicon batteries upgradable(?)~~ Gonna probably just wait
on bodysystem.
- [x] Ensure death, crit, and revival work as intended
- [x] Fix skin color being weird
- [x] Make sure BatteryDrinker is clean

Ready to merge?


# Changelog

:cl:
- add: Added IPC to the game as a new species! Your favourite robot
people are finally around to suffer like the rest of us. Be sure to stop
by Engineering for a charge!

---------

Co-authored-by: DEATHB4DEFEAT <[email protected]>
  • Loading branch information
Pspritechologist and DEATHB4DEFEAT authored Jul 27, 2023
1 parent 3d86d8f commit 81ba344
Show file tree
Hide file tree
Showing 164 changed files with 6,937 additions and 295 deletions.
32 changes: 32 additions & 0 deletions Content.Server/Administration/Commands/SetOutfitCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
using Content.Shared.Radio.Components; // Parkstation-IPC
using Content.Shared.Containers; // Parkstation-IPC
using Robust.Shared.Containers; // Parkstation-IPC

namespace Content.Server.Administration.Commands
{
Expand Down Expand Up @@ -128,6 +131,35 @@ public static bool SetOutfit(EntityUid target, string gear, IEntityManager entit
}
}

// Parkstation-IPC-Start
// Pretty much copied from StationSpawningSystem.SpawnStartingGear
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);
keyHolderComp.KeyContainer.Insert(keyEntity, force: true);
}
}

entityManager.QueueDeleteEntity(earEntity);
}
}
// Parkstation-IPC-End

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 @@ -16,6 +16,7 @@
using Content.Shared.Mobs.Systems;
using Robust.Shared.Prototypes;
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 @@ -74,7 +75,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
6 changes: 3 additions & 3 deletions Content.Server/Electrocution/ElectrocutionSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem
private const string DamageType = "Shock";

// Yes, this is absurdly small for a reason.
private const float ElectrifiedDamagePerWatt = 0.0015f;
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 @@ -292,7 +292,7 @@ public bool TryDoElectrocution(
|| !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); // Parkstation-IPC
return true;
}

Expand Down Expand Up @@ -338,7 +338,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
3 changes: 2 additions & 1 deletion Content.Server/Nyanotrasen/Borgs/CyborgComponent.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Content.Server.Borgs
{
[Obsolete("CyborgComponent is unused on Parkstation, see SiliconComponent")] // Parkstation-IPCs
[RegisterComponent]
public sealed class CyborgComponent : Component
{}
}
}
7 changes: 4 additions & 3 deletions Content.Server/Nyanotrasen/Borgs/CyborgSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

namespace Content.Server.Borgs
{
[Obsolete("'Cyborg' should be unused on Parkstation, see SiliconSystem")] // Parkstation-IPCs
public sealed class Cyborg : EntitySystem
{
[Dependency] private readonly ChatSystem _chatSystem = default!;
Expand All @@ -15,7 +16,7 @@ public override void Initialize()
base.Initialize();
SubscribeLocalEvent<CyborgComponent, MobStateChangedEvent>(OnChangeState);
}

private void OnChangeState(EntityUid uid, CyborgComponent component, MobStateChangedEvent args)
{
if (args.NewMobState == MobState.Dead){
Expand All @@ -27,7 +28,7 @@ private void OnChangeState(EntityUid uid, CyborgComponent component, MobStateCha

// Stop dead borg from being movable AA cards by removing their ability to bump doors.
_tagSystem.RemoveTag(uid, "DoorBumpOpener");

}
else if(args.NewMobState == MobState.Alive && args.OldMobState == MobState.Dead)
{
Expand All @@ -38,4 +39,4 @@ private void OnChangeState(EntityUid uid, CyborgComponent component, MobStateCha
return;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ namespace Content.Server.Radio.Components;
public sealed class IntrinsicRadioTransmitterComponent : Component
{
[DataField("channels", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<RadioChannelPrototype>))]
public readonly HashSet<string> Channels = new() { SharedChatSystem.CommonChannel };
public HashSet<string> Channels = new() { SharedChatSystem.CommonChannel }; // Parkstation-IPC // Did this really need to be readonly? :prerealization:
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace Content.Server.SimpleStation14.Power;

[RegisterComponent]
public sealed 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 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 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;
}
146 changes: 146 additions & 0 deletions Content.Server/SimpleStation14/Power/Systems/BatteryDrinkerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using System.Diagnostics.CodeAnalysis;
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;

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!;

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) ||
!TryGetFillableBattery(args.User, out var drinkerBattery, out _))
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 bool TryGetFillableBattery(EntityUid uid, [NotNullWhen(true)] out BatteryComponent? battery, [NotNullWhen(true)] out EntityUid batteryUid)
{
if (_silicon.TryGetSiliconBattery(uid, out battery, out batteryUid))
return true;

if (TryComp(uid, out battery))
return true;

if (TryComp<PowerCellSlotComponent>(uid, out var powerCellSlot) &&
_slots.TryGetSlot(uid, powerCellSlot.CellSlotId, out var slot) &&
slot.Item != null &&
TryComp(slot.Item.Value, out battery))
{
batteryUid = slot.Item.Value;
return true;
}

return false;
}

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(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);

TryGetFillableBattery(drinker, out var drinkerBattery, out var drinkerBatteryUid);

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, drinkerBattery.MaxCharge - drinkerBattery.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, sourceBattery))
_battery.SetCharge(drinkerBatteryUid, drinkerBattery.Charge + amountToDrink, drinkerBattery);
else
{
_battery.SetCharge(drinker, sourceBattery.Charge + drinkerBattery.Charge, drinkerBattery);
_battery.SetCharge(source, 0, sourceBattery);
}

if (sourceComp != null && sourceComp.DrinkSound != null)
_audio.PlayPvs(sourceComp.DrinkSound, source);
}
}
Loading

0 comments on commit 81ba344

Please sign in to comment.