feat: Перенос суперматерии

This commit is contained in:
Remuchi
2024-01-22 18:32:33 +07:00
parent 5900aadb93
commit 6d20951c5d
16 changed files with 1126 additions and 2 deletions

View File

@@ -0,0 +1,21 @@
using Content.Shared.White.Supermatter.Components;
using Content.Shared.White.Supermatter.Systems;
using Robust.Shared.GameStates;
namespace Content.Client.White.Supermatter.Systems;
public sealed class SupermatterSystem : SharedSupermatterSystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SupermatterComponent, ComponentHandleState>(HandleSupermatterState);
}
private void HandleSupermatterState(EntityUid uid, SupermatterComponent comp, ref ComponentHandleState args)
{
if (args.Current is not SupermatterComponentState state)
return;
}
}

View File

@@ -0,0 +1,582 @@
using System.Linq;
using Content.Server.Alert.Click;
using JetBrains.Annotations;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Timing;
using Robust.Server.GameObjects;
using Content.Shared.Atmos;
using Content.Shared.Interaction;
using Content.Shared.Projectiles;
using Content.Shared.Tag;
using Content.Shared.Mobs.Components;
using Content.Shared.Radiation.Components;
using Content.Server.Audio;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Chat.Systems;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Explosion.Components;
using Content.Shared.Singularity.Components;
using Content.Shared.Singularity.EntitySystems;
using Content.Shared.White.Supermatter.Components;
using Content.Shared.White.Supermatter.Systems;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Random;
namespace Content.Server.White.Supermatter.Systems
{
[UsedImplicitly]
public sealed class SupermatterSystem : SharedSupermatterSystem
{
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly ExplosionSystem _explosion = default!;
[Dependency] private readonly TransformSystem _xform = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly AmbientSoundSystem _ambient = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedSingularitySystem _singularity = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SupermatterComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<SupermatterComponent, StartCollideEvent>(OnCollideEvent);
SubscribeLocalEvent<SupermatterComponent, InteractHandEvent>(OnHandInteract);
SubscribeLocalEvent<SupermatterComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<SupermatterComponent, ComponentGetState>(HandleSupermatterState);
SubscribeLocalEvent<SupermatterComponent, ComponentRemove>(OnComponentRemove);
}
private void OnComponentInit(EntityUid uid, SupermatterComponent component, ComponentInit args)
{
if (_random.Prob(0.2f))
{
component.DelamType = DelamType.Singulo;
}
}
private void OnComponentRemove(EntityUid uid, SupermatterComponent component, ComponentRemove args)
{
// turn off any ambient if component is removed (ex. entity deleted)
_ambient.SetAmbience(uid, false);
_audio.Stop(component.AudioEntity);
}
private void OnMapInit(EntityUid uid, SupermatterComponent component, MapInitEvent args)
{
// Set the Sound
_ambient.SetAmbience(uid, true);
//Add Air to the initialized SM in the Map so it doesnt delam on default
var mixture = _atmosphere.GetContainingMixture(uid, true, true);
mixture?.AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesStandard);
mixture?.AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesStandard);
}
private void HandleSupermatterState(EntityUid uid, SupermatterComponent comp, ref ComponentGetState args)
{
args.State = new SupermatterComponentState(comp);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
if (!_gameTiming.IsFirstTimePredicted)
return;
foreach (var (supermatter, xplode, rads) in EntityManager
.EntityQuery<SupermatterComponent, ExplosiveComponent, RadiationSourceComponent>())
{
var mixture = _atmosphere.GetContainingMixture(supermatter.Owner, true, true);
HandleOutput(supermatter.Owner, frameTime, supermatter, rads, mixture);
HandleDamage(supermatter.Owner, frameTime, supermatter, xplode, mixture);
}
}
/// <summary>
/// Handle outputting based off enery, damage, gas mix and radiation
/// </summary>
private void HandleOutput(
EntityUid uid,
float frameTime,
SupermatterComponent? sMcomponent = null,
RadiationSourceComponent? radcomponent = null,
Atmos.GasMixture? mixture = null)
{
if (!Resolve(uid, ref sMcomponent, ref radcomponent))
{
return;
}
sMcomponent.AtmosUpdateAccumulator += frameTime;
if (!(sMcomponent.AtmosUpdateAccumulator > sMcomponent.AtmosUpdateTimer) || mixture is null)
return;
sMcomponent.AtmosUpdateAccumulator -= sMcomponent.AtmosUpdateTimer;
//Absorbed gas from surrounding area
var absorbedGas = mixture.Remove(sMcomponent.GasEfficiency * mixture.TotalMoles);
var absorbedTotalMoles = absorbedGas.TotalMoles;
if (!(absorbedTotalMoles > 0f))
return;
var gasStorage = sMcomponent.GasStorage;
var gasEffect = sMcomponent.GasDataFields;
//Lets get the proportions of the gasses in the mix for scaling stuff later
//They range between 0 and 1
gasStorage = gasStorage.ToDictionary(
gas => gas.Key,
gas => Math.Clamp(absorbedGas.GetMoles(gas.Key) / absorbedTotalMoles, 0, 1)
);
//No less then zero, and no greater then one, we use this to do explosions
//and heat to power transfer
var gasmixPowerRatio = gasStorage.Sum(gas => gasStorage[gas.Key] * gasEffect[gas.Key].PowerMixRatio);
//Minimum value of -10, maximum value of 23. Effects plasma and o2 output
//and the output heat
var dynamicHeatModifier = gasStorage.Sum(gas => gasStorage[gas.Key] * gasEffect[gas.Key].HeatPenalty);
//Minimum value of -10, maximum value of 23. Effects plasma and o2 output
// and the output heat
var powerTransmissionBonus =
gasStorage.Sum(gas => gasStorage[gas.Key] * gasEffect[gas.Key].TransmitModifier);
var h2OBonus = 1 - gasStorage[Gas.WaterVapor] * 0.25f;
gasmixPowerRatio = Math.Clamp(gasmixPowerRatio, 0, 1);
dynamicHeatModifier = Math.Max(dynamicHeatModifier, 0.5f);
powerTransmissionBonus *= h2OBonus;
//Effects the damage heat does to the crystal
sMcomponent.DynamicHeatResistance = 1f;
//more moles of gases are harder to heat than fewer,
//so let's scale heat damage around them
sMcomponent.MoleHeatPenaltyThreshold =
(float) Math.Max(absorbedTotalMoles / sMcomponent.MoleHeatPenalty, 0.25);
//Ramps up or down in increments of 0.02 up to the proportion of co2
//Given infinite time, powerloss_dynamic_scaling = co2comp
//Some value between 0 and 1
if (absorbedTotalMoles > sMcomponent.PowerlossInhibitionMoleThreshold &&
gasStorage[Gas.CarbonDioxide] > sMcomponent.PowerlossInhibitionGasThreshold)
{
sMcomponent.PowerlossDynamicScaling =
Math.Clamp(
sMcomponent.PowerlossDynamicScaling + Math.Clamp(
gasStorage[Gas.CarbonDioxide] - sMcomponent.PowerlossDynamicScaling, -0.02f, 0.02f), 0f,
1f);
}
else
{
sMcomponent.PowerlossDynamicScaling = Math.Clamp(sMcomponent.PowerlossDynamicScaling - 0.05f, 0f, 1f);
}
//Ranges from 0 to 1(1-(value between 0 and 1 * ranges from 1 to 1.5(mol / 500)))
//We take the mol count, and scale it to be our inhibitor
var powerlossInhibitor =
Math.Clamp(
1 - sMcomponent.PowerlossDynamicScaling *
Math.Clamp(absorbedTotalMoles / sMcomponent.PowerlossInhibitionMoleBoostThreshold, 1f, 1.5f),
0f, 1f);
if (sMcomponent.MatterPower != 0) //We base our removed power off one 10th of the matter_power.
{
var removedMatter = Math.Max(sMcomponent.MatterPower / sMcomponent.MatterPowerConversion, 40);
//Adds at least 40 power
sMcomponent.Power = Math.Max(sMcomponent.Power + removedMatter, 0);
//Removes at least 40 matter power
sMcomponent.MatterPower = Math.Max(sMcomponent.MatterPower - removedMatter, 0);
}
//based on gas mix, makes the power more based on heat or less effected by heat
var tempFactor = gasmixPowerRatio > 0.8 ? 50f : 30f;
//if there is more pluox and n2 then anything else, we receive no power increase from heat
sMcomponent.Power =
Math.Max(
absorbedGas.Temperature * tempFactor / Atmospherics.T0C * gasmixPowerRatio + sMcomponent.Power,
0);
//Rad Pulse Calculation
radcomponent.Intensity = sMcomponent.Power * Math.Max(0, 1f + powerTransmissionBonus / 10f) * 0.003f;
//Power * 0.55 * a value between 1 and 0.8
var energy = sMcomponent.Power * sMcomponent.ReactionPowerModefier;
//Keep in mind we are only adding this temperature to (efficiency)% of the one tile the rock
//is on. An increase of 4*C @ 25% efficiency here results in an increase of 1*C / (#tilesincore) overall.
//Power * 0.55 * (some value between 1.5 and 23) / 5
absorbedGas.Temperature += energy * dynamicHeatModifier / sMcomponent.ThermalReleaseModifier;
absorbedGas.Temperature = Math.Max(0,
Math.Min(absorbedGas.Temperature, sMcomponent.HeatThreshold * dynamicHeatModifier));
//Calculate how much gas to release
//Varies based on power and gas content
absorbedGas.AdjustMoles(Gas.Plasma,
Math.Max(energy * dynamicHeatModifier / sMcomponent.PlasmaReleaseModifier, 0f));
absorbedGas.AdjustMoles(Gas.Oxygen,
Math.Max(
(energy + absorbedGas.Temperature * dynamicHeatModifier - Atmospherics.T0C) /
sMcomponent.OxygenReleaseModifier, 0f));
_atmosphere.Merge(mixture, absorbedGas);
var powerReduction = (float) Math.Pow(sMcomponent.Power / 500, 3);
//After this point power is lowered
//This wraps around to the begining of the function
sMcomponent.Power =
Math.Max(
sMcomponent.Power - Math.Min(powerReduction * powerlossInhibitor,
sMcomponent.Power * 0.83f * powerlossInhibitor), 0f);
}
/// <summary>
/// Handles environmental damage and dispatching damage warning
/// </summary>
private void HandleDamage(
EntityUid uid,
float frameTime,
SupermatterComponent? sMcomponent = null,
ExplosiveComponent? xplode = null,
Atmos.GasMixture? mixture = null)
{
if (!Resolve(uid, ref sMcomponent, ref xplode))
{
return;
}
var xform = Transform(uid);
var indices = _xform.GetGridOrMapTilePosition(uid, xform);
sMcomponent.DamageUpdateAccumulator += frameTime;
sMcomponent.YellAccumulator += frameTime;
if (!(sMcomponent.DamageUpdateAccumulator > sMcomponent.DamageUpdateTimer))
return;
sMcomponent.DamageArchived = sMcomponent.Damage;
//we're in space or there is no gas to process
if (!xform.GridUid.HasValue || mixture is null || mixture.TotalMoles == 0f)
{
sMcomponent.Damage += Math.Max(sMcomponent.Power / 1000 * sMcomponent.DamageIncreaseMultiplier, 0.1f);
}
else
{
//Absorbed gas from surrounding area
var absorbedGas = mixture.Remove(sMcomponent.GasEfficiency * mixture.TotalMoles);
var absorbedTotalMoles = absorbedGas.TotalMoles;
//Mols start to have a positive effect on damage after 350
sMcomponent.Damage = (float) Math.Max(
sMcomponent.Damage + Math.Max(
Math.Clamp(absorbedTotalMoles / 200, 0.5, 1) * absorbedGas.Temperature -
(Atmospherics.T0C + sMcomponent.HeatPenaltyThreshold) * sMcomponent.DynamicHeatResistance,
0)
* sMcomponent.MoleHeatPenalty / 150 * sMcomponent.DamageIncreaseMultiplier,
0);
//Power only starts affecting damage when it is above 5000
sMcomponent.Damage =
Math.Max(
sMcomponent.Damage +
Math.Max(sMcomponent.Power - sMcomponent.PowerPenaltyThreshold, 0) / 500 *
sMcomponent.DamageIncreaseMultiplier, 0);
//Molar count only starts affecting damage when it is above 1800
sMcomponent.Damage =
Math.Max(
sMcomponent.Damage + Math.Max(absorbedTotalMoles - sMcomponent.MolePenaltyThreshold, 0) / 80 *
sMcomponent.DamageIncreaseMultiplier, 0);
//There might be a way to integrate healing and hurting via heat
//healing damage
if (absorbedTotalMoles < sMcomponent.MolePenaltyThreshold)
{
//Only has a net positive effect when the temp is below 313.15, heals up to 2 damage. Psycologists increase this temp min by up to 45
sMcomponent.Damage =
Math.Max(
sMcomponent.Damage +
Math.Min(absorbedGas.Temperature - (Atmospherics.T0C + sMcomponent.HeatPenaltyThreshold),
0) / 150,
0);
}
//if there are space tiles next to SM
//TODO: change moles out for checking if adjacent tiles exist
foreach (var ind in _atmosphere.GetAdjacentTileMixtures(xform.GridUid.Value, indices))
{
if (ind.TotalMoles != 0)
continue;
var integrity = GetIntegrity(sMcomponent.Damage, sMcomponent.ExplosionPoint);
var factor = integrity switch
{
< 10 => 0.0005f,
< 25 => 0.0009f,
< 45 => 0.005f,
< 75 => 0.002f,
_ => 0f
};
sMcomponent.Damage += Math.Clamp(sMcomponent.Power * factor * sMcomponent.DamageIncreaseMultiplier,
0, sMcomponent.MaxSpaceExposureDamage);
break;
}
sMcomponent.Damage =
Math.Min(sMcomponent.DamageArchived + sMcomponent.DamageHardcap * sMcomponent.ExplosionPoint,
sMcomponent.Damage);
}
HandleSoundLoop(uid, sMcomponent);
if (sMcomponent.Damage > sMcomponent.ExplosionPoint)
{
Delamination(uid, frameTime, sMcomponent, xplode, mixture);
return;
}
if (sMcomponent.Damage > sMcomponent.WarningPoint)
{
var integrity = GetIntegrity(sMcomponent.Damage, sMcomponent.ExplosionPoint);
if (sMcomponent.YellAccumulator >= sMcomponent.YellTimer)
{
if (sMcomponent.Damage > sMcomponent.EmergencyPoint)
{
_chat.TrySendInGameICMessage(uid,
Loc.GetString("supermatter-danger-message", ("integrity", integrity.ToString("0.00"))),
InGameICChatType.Speak, hideChat: true);
}
else if (sMcomponent.Damage >= sMcomponent.DamageArchived)
{
_chat.TrySendInGameICMessage(uid,
Loc.GetString("supermatter-warning-message", ("integrity", integrity.ToString("0.00"))),
InGameICChatType.Speak, hideChat: true);
}
else
{
_chat.TrySendInGameICMessage(uid,
Loc.GetString("supermatter-safe-alert", ("integrity", integrity.ToString("0.00"))),
InGameICChatType.Speak, hideChat: true);
}
sMcomponent.YellAccumulator = 0;
}
}
sMcomponent.DamageUpdateAccumulator -= sMcomponent.DamageUpdateTimer;
}
private float GetIntegrity(float damage, float explosionPoint)
{
var integrity = damage / explosionPoint;
integrity = (float) Math.Round(100 - integrity * 100, 2);
integrity = integrity < 0 ? 0 : integrity;
return integrity;
}
/// <summary>
/// Runs the logic and timers for Delamination
/// </summary>
private void Delamination(
EntityUid uid,
float frameTime,
SupermatterComponent? sMcomponent = null,
ExplosiveComponent? xplode = null,
Atmos.GasMixture? mixture = null)
{
if (!Resolve(uid, ref sMcomponent, ref xplode))
{
return;
}
var xform = Transform(uid);
//before we actually start counting down, check to see what delam type we're doing.
if (!sMcomponent.FinalCountdown)
{
//if we're in atmos
if (mixture is { })
{
//Absorbed gas from surrounding area
var absorbedGas = mixture.Remove(sMcomponent.GasEfficiency * mixture.TotalMoles);
var absorbedTotalMoles = absorbedGas.TotalMoles;
//if the moles on the sm's tile are above MolePenaltyThreshold
if (absorbedTotalMoles >= sMcomponent.MolePenaltyThreshold)
{
sMcomponent.DelamType = DelamType.Singulo;
_chat.TrySendInGameICMessage(uid, Loc.GetString("supermatter-delamination-overmass"),
InGameICChatType.Speak, hideChat: true);
}
}
else
{
sMcomponent.DelamType = DelamType.Explosion;
_chat.TrySendInGameICMessage(uid, Loc.GetString("supermatter-delamination-default"),
InGameICChatType.Speak, hideChat: true);
}
}
sMcomponent.FinalCountdown = true;
sMcomponent.DelamTimerAccumulator += frameTime;
sMcomponent.SpeakAccumulator += frameTime;
var roundSeconds = sMcomponent.DelamTimerTimer - (int) Math.Floor(sMcomponent.DelamTimerAccumulator);
//we're more than 5 seconds from delam, only yell every 5 seconds.
if (roundSeconds >= sMcomponent.YellDelam && sMcomponent.SpeakAccumulator >= sMcomponent.YellDelam)
{
sMcomponent.SpeakAccumulator -= sMcomponent.YellDelam;
_chat.TrySendInGameICMessage(uid,
Loc.GetString("supermatter-seconds-before-delam", ("Seconds", roundSeconds)),
InGameICChatType.Speak, hideChat: true);
}
//less than 5 seconds to delam, count every second.
else if (roundSeconds < sMcomponent.YellDelam && sMcomponent.SpeakAccumulator >= 1)
{
sMcomponent.SpeakAccumulator -= 1;
_chat.TrySendInGameICMessage(uid,
Loc.GetString("supermatter-seconds-before-delam", ("Seconds", roundSeconds)),
InGameICChatType.Speak, hideChat: true);
}
//TODO: make tesla(?) spawn at SupermatterComponent.PowerPenaltyThreshold and think up other delam types
//times up, explode or make a singulo
if (!(sMcomponent.DelamTimerAccumulator >= sMcomponent.DelamTimerTimer))
return;
if (sMcomponent.DelamType == DelamType.Singulo)
{
//spawn a singulo :)
var singuloEntity = EntityManager.SpawnEntity("Singularity", xform.Coordinates);
_singularity.SetLevel(singuloEntity, 4);
_explosion.TriggerExplosive(
uid,
explosive: xplode,
totalIntensity: sMcomponent.TotalIntensity / 50,
radius: sMcomponent.Radius / 50,
user: uid
);
_audio.Stop(sMcomponent.AudioEntity);
}
else
{
//explosion!!!!!
_explosion.TriggerExplosive(
uid,
explosive: xplode,
totalIntensity: sMcomponent.TotalIntensity,
radius: sMcomponent.Radius,
user: uid
);
_audio.Stop(sMcomponent.AudioEntity);
_ambient.SetAmbience(uid, false);
}
sMcomponent.FinalCountdown = false;
}
private void HandleSoundLoop(EntityUid uid, SupermatterComponent sMcomponent)
{
var isAggressive = sMcomponent.Damage > sMcomponent.WarningPoint;
var isDelamming = sMcomponent.Damage > sMcomponent.ExplosionPoint;
if (!isAggressive && !isDelamming)
{
_audio.Stop(sMcomponent.AudioEntity);
return;
}
var smSound = isDelamming ? SuperMatterSound.Delam : SuperMatterSound.Aggressive;
if (sMcomponent.SmSound == smSound)
return;
_audio.Stop(sMcomponent.AudioEntity);
var sounds = isDelamming ? sMcomponent.DelamAlarm : sMcomponent.DelamSound;
var sound = _audio.GetSound(sounds);
var param = sounds.Params.WithLoop(true).WithVolume(5f).WithMaxDistance(20f);
sMcomponent.AudioEntity = _audio.PlayPvs(sound, uid, param).Value.Entity;
sMcomponent.SmSound = smSound;
}
/// <summary>
/// Determines if an entity can be dusted
/// </summary>
private bool CannotDestroy(EntityUid uid)
{
var @static = false;
var tag = _tag.HasTag(uid, "SMImmune");
if (EntityManager.TryGetComponent<PhysicsComponent>(uid, out var physicsComp))
{
@static = physicsComp.BodyType == BodyType.Static;
}
return tag || @static;
}
private void OnCollideEvent(EntityUid uid, SupermatterComponent supermatter, ref StartCollideEvent args)
{
var target = args.OtherBody.Owner;
if (!supermatter.Whitelist.IsValid(target) || CannotDestroy(target) || _container.IsEntityInContainer(uid))
return;
if (EntityManager.TryGetComponent<SupermatterFoodComponent>(target, out var supermatterFood))
supermatter.Power += supermatterFood.Energy;
else if (EntityManager.TryGetComponent<ProjectileComponent>(target, out var projectile))
supermatter.Power += (float) projectile.Damage.Total;
else
supermatter.Power++;
supermatter.MatterPower += EntityManager.HasComponent<MobStateComponent>(target) ? 200 : 0;
if (!EntityManager.HasComponent<ProjectileComponent>(target))
{
EntityManager.SpawnEntity("Ash", Transform(target).Coordinates);
_audio.PlayPvs(supermatter.DustSound, uid);
}
EntityManager.QueueDeleteEntity(target);
}
private void OnHandInteract(EntityUid uid, SupermatterComponent supermatter, InteractHandEvent args)
{
var target = args.User;
if (_tag.HasTag(target, "SMImmune"))
{
return;
}
supermatter.MatterPower += 200;
EntityManager.SpawnEntity("Ash", Transform(target).Coordinates);
_audio.PlayPvs(supermatter.DustSound, uid);
EntityManager.QueueDeleteEntity(target);
}
}
}

View File

@@ -0,0 +1,372 @@
using Robust.Shared.GameStates;
using Robust.Shared.Audio;
using Content.Shared.Atmos;
using Content.Shared.White.Supermatter.Systems;
using Content.Shared.Whitelist;
namespace Content.Shared.White.Supermatter.Components;
[RegisterComponent, NetworkedComponent]
public sealed partial class SupermatterComponent : Component
{
#region SM Base
[DataField("whitelist")] public EntityWhitelist Whitelist = new();
public string IdTag = "EmitterBolt";
[ViewVariables(VVAccess.ReadWrite)]
[DataField("power")]
public float Power;
/// <summary>
/// The amount of damage we have currently
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("damage")]
public float Damage = 0f;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("matterPower")]
public float MatterPower;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("matterPowerConversion")]
public float MatterPowerConversion = 10f;
[ViewVariables(VVAccess.ReadWrite)]
public SharedSupermatterSystem.DelamType DelamType;
/// <summary>
/// The portion of the gasmix we're on
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("gasEfficiency")]
public float GasEfficiency = 0.15f;
/// <summary>
/// The amount of heat we apply scaled
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("heatThreshold")]
public float HeatThreshold = 2500f;
#endregion SM Base
#region SM Sound
/// <summary>
/// Current stream of SM audio.
/// </summary>
public EntityUid? AudioEntity;
public SharedSupermatterSystem.SuperMatterSound? SmSound;
[DataField("dustSound")]
public SoundSpecifier DustSound = new SoundPathSpecifier("/Audio/White/Supermatter/dust.ogg");
[DataField("delamSound")]
public SoundSpecifier DelamSound = new SoundPathSpecifier("/Audio/White/Supermatter/delamming.ogg");
[DataField("delamAlarm")]
public SoundSpecifier DelamAlarm = new SoundPathSpecifier("/Audio/Machines/alarm.ogg");
#endregion SM Sound
#region SM Calculation
/// <summary>
/// Based on co2 percentage, slowly moves between
/// 0 and 1. We use it to calc the powerloss_inhibitor
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("powerlossdynamicScaling")]
public float PowerlossDynamicScaling;
/// <summary>
/// Affects the amount of damage and minimum point
/// at which the sm takes heat damage
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("dynamicheatResistance")]
public float DynamicHeatResistance = 1;
/// <summary>
/// Used to increase or lessen the amount of damage the sm takes
/// from heat based on molar counts.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("moleheatPenalty")]
public float MoleHeatPenalty = 350f;
/// <summary>
/// Higher == more overall power
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("reactionpowerModefier")]
public float ReactionPowerModefier = 0.55f;
/// <summary>
/// Higher == less heat released during reaction
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("thermalreleaseModifier")]
public float ThermalReleaseModifier = 5f;
/// <summary>
/// Higher == less plasma released by reaction
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("plasmareleaseModifier")]
public float PlasmaReleaseModifier = 750f;
/// <summary>
/// Higher == less oxygen released at high temperature/power
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("oxygenreleaseModifier")]
public float OxygenReleaseModifier = 325f;
#endregion SM Calculation
#region SM Timer
/// <summary>
/// The point at which we should start sending messeges
/// about the damage to the engi channels.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("WarningPoint")]
public float WarningPoint = 50;
/// <summary>
/// The point at which we start sending messages to the common channel
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("emergencyPoint")]
public float EmergencyPoint = 500;
/// <summary>
/// we yell if over 50 damage every YellTimer Seconds
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("yellTimer")]
public float YellTimer = 30f;
/// <summary>
/// set to YellTimer at first so it doesnt yell a minute after being hit
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("yellAccumulator")]
public float YellAccumulator = 30f;
/// <summary>
/// YellTimer before the SM is about the delam
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("yellDelam")]
public float YellDelam = 5f;
/// <summary>
/// Timer for Damage
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("damageupdateAccumulator")]
public float DamageUpdateAccumulator;
/// <summary>
/// update environment damage every 1 second
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("damageupdateTimer")]
public float DamageUpdateTimer = 1f;
/// <summary>
/// Timer for delam
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("delamtimerAccumulator")]
public float DelamTimerAccumulator;
/// <summary>
/// updates delam
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("delamtimerTimer")]
public int DelamTimerTimer = 30;
/// <summary>
/// The message timer
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("speakaccumulator")]
public float SpeakAccumulator = 5f;
/// <summary>
/// Atmos update timer
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("atmosupdateAccumulator")]
public float AtmosUpdateAccumulator;
/// <summary>
/// update atmos every 1 second
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("atmosupdateTimer")]
public float AtmosUpdateTimer = 1f;
#endregion SM Timer
#region SM Threshold
/// <summary>
/// Higher == Higher percentage of inhibitor gas needed
/// before the charge inertia chain reaction effect starts.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("powerlossinhibitiongasThreshold")]
public float PowerlossInhibitionGasThreshold = 0.20f;
/// <summary>
/// Higher == More moles of the gas are needed before the charge
/// inertia chain reaction effect starts.
/// Scales powerloss inhibition down until this amount of moles is reached
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("powerlossinhibitionmoleThreshold")]
public float PowerlossInhibitionMoleThreshold = 20f;
/// <summary>
/// bonus powerloss inhibition boost if this amount of moles is reached
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("powerlossinhibitionmoleboostThreshold")]
public float PowerlossInhibitionMoleBoostThreshold = 500f;
/// <summary>
/// Above this value we can get lord singulo and independent mol damage,
/// below it we can heal damage
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("molepenaltyThreshold")]
public float MolePenaltyThreshold = 1800f;
/// <summary>
/// more moles of gases are harder to heat than fewer,
/// so let's scale heat damage around them
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("moleheatpenaltyThreshold")]
public float MoleHeatPenaltyThreshold;
/// <summary>
/// The cutoff on power properly doing damage, pulling shit around,
/// and delamming into a tesla. Low chance of pyro anomalies, +2 bolts of electricity
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("powerPenaltyThreshold")]
public float PowerPenaltyThreshold = 5000f;
/// <summary>
/// Higher == Crystal safe operational temperature is higher.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("heatpenaltyThreshold")]
public float HeatPenaltyThreshold = 40f;
/// <summary>
/// The damage we had before this cycle. Used to limit the damage we can take each cycle, and for safe alert
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("damagearchived")]
public float DamageArchived = 0f;
/// <summary>
/// is multiplied by ExplosionPoint to cap
/// evironmental damage per cycle
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("damageHardcap")]
public float DamageHardcap = 0.002f;
/// <summary>
/// environmental damage is scaled by this
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("damageincreaseMultiplier")]
public float DamageIncreaseMultiplier = 0.25f;
/// <summary>
/// if spaced sm wont take more than 2 damage per cycle
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("maxspaceexposureDamage")]
public float MaxSpaceExposureDamage = 2;
#endregion SM Threshold
#region SM Delamm
/// <summary>
/// The point at which we delamm
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("explosionPoint")]
public int ExplosionPoint = 900;
//Are we delamming?
[ViewVariables(VVAccess.ReadOnly)] public bool Delamming = false;
//it's the final countdown
[ViewVariables(VVAccess.ReadOnly)] public bool FinalCountdown = false;
//Explosion totalIntensity value
[ViewVariables(VVAccess.ReadOnly)]
[DataField("totalIntensity")]
public float TotalIntensity= 500000f;
//Explosion radius value
[ViewVariables(VVAccess.ReadOnly)]
[DataField("radius")]
public float Radius = 500f;
/// <summary>
/// These would be what you would get at point blank, decreases with distance
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("detonationRads")]
public float DetonationRads = 200f;
#endregion SM Delamm
#region SM Gas
/// <summary>
/// Is used to store gas
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("gasStorage")]
public Dictionary<Gas, float> GasStorage = new()
{
{Gas.Oxygen, 0f},
{Gas.Nitrogen, 0f},
{Gas.CarbonDioxide, 0f},
{Gas.Plasma, 0f},
{Gas.Tritium, 0f},
{Gas.WaterVapor, 0f}
};
/// <summary>
/// Stores each gases calculation
/// </summary>
public readonly Dictionary<Gas, (float TransmitModifier, float HeatPenalty, float PowerMixRatio)> GasDataFields = new()
{
[Gas.Oxygen] = (TransmitModifier: 1.5f, HeatPenalty: 1f, PowerMixRatio: 1f),
[Gas.Nitrogen] = (TransmitModifier: 0f, HeatPenalty: -1.5f, PowerMixRatio: -1f),
[Gas.CarbonDioxide] = (TransmitModifier: 0f, HeatPenalty: 0.1f, PowerMixRatio: 1f),
[Gas.Plasma] = (TransmitModifier: 4f, HeatPenalty: 15f, PowerMixRatio: 1f),
[Gas.Tritium] = (TransmitModifier: 30f, HeatPenalty: 10f, PowerMixRatio: 1f),
[Gas.WaterVapor] = (TransmitModifier: 2f, HeatPenalty: 12f, PowerMixRatio: 1f)
};
#endregion SM Gas
}

View File

@@ -0,0 +1,9 @@
namespace Content.Shared.White.Supermatter.Components;
[RegisterComponent]
public sealed partial class SupermatterFoodComponent : Component
{
[ViewVariables(VVAccess.ReadWrite)]
[DataField("energy")]
public int Energy { get; set; } = 1;
}

View File

@@ -0,0 +1,40 @@
using Content.Shared.White.Supermatter.Components;
using Robust.Shared.Serialization;
namespace Content.Shared.White.Supermatter.Systems;
public abstract class SharedSupermatterSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SupermatterComponent, ComponentStartup>(OnSupermatterStartup);
}
public enum SuperMatterSound : sbyte
{
Aggressive = 0,
Delam = 1
}
public enum DelamType : sbyte
{
Explosion = 0,
Singulo = 1,
}
public void OnSupermatterStartup(EntityUid uid, SupermatterComponent comp, ComponentStartup args)
{
}
/// <summary>
/// A state wrapper used to sync the supermatter between the server and client.
/// </summary>
[Serializable, NetSerializable]
protected sealed class SupermatterComponentState : ComponentState
{
public SupermatterComponentState(SupermatterComponent supermatter)
{
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -12,6 +12,7 @@
- CanPilot
- BypassInteractionRangeChecks
- BypassDropChecks
- SMImmune
- type: Input
context: "aghost"
- type: Ghost

View File

@@ -245,7 +245,6 @@
- type: Sprite
sprite: Objects/Tanks/plasma.rsi
- type: Item
size: 25
sprite: Objects/Tanks/plasma.rsi
- type: GasTank
outputPressure: 101.3

View File

@@ -306,7 +306,7 @@
# soundHit: Waiting on serv3
damage:
types:
Heat: 14
Heat: 50
# mining laser real
- type: GatheringProjectile
- type: Tag

View File

@@ -0,0 +1,66 @@
- type: entity
id: supermatter
name: Supermatter
description: A strangely translucent and iridescent crystal.
placement:
mode: SnapgridCenter
components:
- type: Supermatter
whitelist:
tags:
- EmitterBolt
components:
- Body
- Item
- type: RadiationSource
- type: AmbientSound
range: 5
volume: 50
sound:
path: /Audio/White/Supermatter/calm.ogg
- type: Physics
bodyType: Dynamic
- type: Speech
speechSounds: Pai
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeAabb
bounds: "-0.25,-0.25,0.25,0.25"
mask:
- Impassable
- MidImpassable
- HighImpassable
- LowImpassable
- InteractImpassable
- Opaque
layer:
- MidImpassable
- HighImpassable
- BulletImpassable
- InteractImpassable
- type: Transform
anchored: true
noRot: true
- type: CollisionWake
enabled: false
- type: Clickable
- type: InteractionOutline
- type: Sprite
drawdepth: WallMountedItems
sprite: White/Structures/supermatter.rsi
state: supermatter
- type: Icon
sprite: White/Structures/supermatter.rsi
state: supermatter
- type: PointLight
enabled: true
radius: 10
energy: 5
color: "#d9ce00"
- type: Explosive
explosionType: Supermatter
maxIntensity: 10000
intensitySlope: 10
totalIntensity: 10000

View File

@@ -6,3 +6,6 @@
- type: Tag
id: CrossbowBolt
- type: Tag
id: SMImmune

View File

@@ -117,3 +117,20 @@
lightColor: Orange
texturePath: /Textures/Effects/fire.rsi
fireStates: 6
# WD ADDED
- type: explosion
id: Supermatter
damagePerIntensity:
types:
Radiation: 5
Heat: 4
Blunt: 3
Piercing: 3
tileBreakChance: [0, 0.5, 1]
tileBreakIntensity: [0, 10, 30]
tileBreakRerollReduction: 20
lightColor: Yellow
fireColor: Green
texturePath: /Textures/Effects/fire_greyscale.rsi
fireStates: 3

View File

@@ -0,0 +1,14 @@
{
"version": 1,
"copyright": "Taken from https://github.com/tgstation/tgstation/blob/master/icons/obj/supermatter.dmi",
"license": "CC-BY-SA-3.0",
"size": {
"x": 32,
"y": 48
},
"states": [
{
"name": "supermatter"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB