-
Notifications
You must be signed in to change notification settings - Fork 414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Race: IPCs #1480
New Race: IPCs #1480
Changes from all commits
da48783
4a7c1c4
fe305f6
d7bfcef
5f0fff2
0244121
dbcbfe8
1981561
28bc046
dc9bcd3
d8ea098
8d16f5c
dcaaf59
ee7685f
4a70062
d2f99c0
20183b5
46f8045
1865d65
4f363f3
a0eecea
843232e
712979d
58aadc4
68eeb10
af0b295
b6f0543
d91246c
98bcb3e
3d922d0
0b74862
51d15e3
ce661cb
34cf141
2ada76f
ed5f1c1
932d389
5cb93ee
80902f5
70c3c36
1140035
4671d56
666ff29
50284ab
c268b95
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
using Content.Shared.Chat.Prototypes; | ||
using Content.Shared.Chat.Prototypes; | ||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; | ||
|
||
namespace Content.Server.Mobs; | ||
|
@@ -13,6 +13,12 @@ public sealed partial class DeathgaspComponent : Component | |
/// <summary> | ||
/// The emote prototype to use. | ||
/// </summary> | ||
[DataField("prototype", customTypeSerializer:typeof(PrototypeIdSerializer<EmotePrototype>))] | ||
[DataField(customTypeSerializer:typeof(PrototypeIdSerializer<EmotePrototype>))] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no reason to change this, it can be done upstream and use ProtoId too |
||
public string Prototype = "DefaultDeathgasp"; | ||
|
||
/// <summary> | ||
/// Makes sure that the deathgasp is only displayed if the entity went critical before dying - Estacao Pirata | ||
/// </summary> | ||
[DataField] | ||
public bool NeedsCritical = true; | ||
} |
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] | ||
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; | ||
} |
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] | ||
public Vector2 BatteryMaxMinMax = new(0.85f, 1.15f); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use destruction MinMax instead of vector2 |
||
|
||
/// <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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using System.ComponentModel.DataAnnotations; | ||
using Robust.Shared.Audio; | ||
using Content.Server.Sound.Components; | ||
using System; | ||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should use a ComponentRegistry instead of being extremely hardcoded to this one thing |
||
{ | ||
[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; | ||
} |
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, | ||
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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. power cell system has a method for this dont reinvent the wheel |
||
|
||
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); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
undo these indents