diff --git a/Content.Server/Supermatter/Systems/SupermatterSystem.cs b/Content.Server/Supermatter/Systems/SupermatterSystem.cs index 420725079b..cb1b1b9fa2 100644 --- a/Content.Server/Supermatter/Systems/SupermatterSystem.cs +++ b/Content.Server/Supermatter/Systems/SupermatterSystem.cs @@ -1,553 +1,617 @@ -using System.Linq; -using JetBrains.Annotations; using Robust.Shared.Audio.Systems; 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.Radio; -using Content.Server.Radio.EntitySystems; using Content.Shared.Supermatter.Components; -using Content.Shared.Supermatter.Systems; -using Robust.Shared.Prototypes; +using Content.Server.Lightning; +using Content.Server.AlertLevel; +using Content.Server.Station.Systems; +using System.Text; +using Content.Server.Kitchen.Components; +using Content.Shared.DoAfter; +using Content.Shared.Examine; +using Content.Server.DoAfter; +using Content.Server.Popups; +using System.Linq; +using Content.Shared.Audio; + +namespace Content.Server.Supermatter.Systems; -namespace Content.Server.Supermatter.Systems +public sealed class SupermatterSystem : EntitySystem { - [UsedImplicitly] - public sealed class SupermatterSystem : SharedSupermatterSystem + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + [Dependency] private readonly ChatSystem _chat = 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 SharedAmbientSoundSystem _ambient = default!; + [Dependency] private readonly LightningSystem _lightning = default!; + [Dependency] private readonly AlertLevelSystem _alert = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly PopupSystem _popup = default!; + + public override void Initialize() { - [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 RadioSystem _radioSystem = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - public override void Initialize() - { - base.Initialize(); + base.Initialize(); - SubscribeLocalEvent(OnCollideEvent); - SubscribeLocalEvent(OnHandInteract); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(HandleSupermatterState); - SubscribeLocalEvent(OnComponentRemove); - } + SubscribeLocalEvent(OnMapInit); - private void OnComponentRemove(EntityUid uid, SupermatterComponent component, ComponentRemove args) - { - // turn off any ambient if component is removed (ex. entity deleted) - _ambient.SetAmbience(uid, false); - component.AudioStream = _audio.Stop(component.AudioStream); - } + SubscribeLocalEvent(OnCollideEvent); + SubscribeLocalEvent(OnHandInteract); + SubscribeLocalEvent(OnItemInteract); + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnGetSliver); + } - private void OnMapInit(EntityUid uid, SupermatterComponent component, MapInitEvent args) + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var sm in EntityManager.EntityQuery()) { - // Set the Sound - _ambient.SetAmbience(uid, true); + if (!sm.Activated) + return; - //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); - } + var uid = sm.Owner; + sm.UpdateAccumulator += frameTime; - private void HandleSupermatterState(EntityUid uid, SupermatterComponent comp, ref ComponentGetState args) - { - args.State = new SupermatterComponentState(comp); + if (sm.UpdateAccumulator >= sm.UpdateTimer) + { + sm.UpdateAccumulator -= sm.UpdateTimer; + Cycle(uid, sm); + } } + } - public override void Update(float frameTime) - { - base.Update(frameTime); + public void Cycle(EntityUid uid, SupermatterComponent sm) + { + sm.ZapAccumulator++; + sm.YellAccumulator++; - if (!_gameTiming.IsFirstTimePredicted) - return; + ProcessAtmos(uid, sm); + HandleDamage(uid, sm); - foreach (var (supermatter, xplode, rads) in EntityManager - .EntityQuery()) - { - var mixture = _atmosphere.GetContainingMixture(supermatter.Owner, true, true); - HandleOutput(supermatter.Owner, frameTime, supermatter, rads, mixture); - HandleDamage(supermatter.Owner, frameTime, supermatter, xplode, mixture); - } + if (sm.Damage >= sm.DelaminationPoint || sm.Delamming) + HandleDelamination(uid, sm); + + HandleSoundLoop(uid, sm); + + if (sm.ZapAccumulator >= sm.ZapTimer) + { + sm.ZapAccumulator -= sm.ZapTimer; + SupermatterZap(uid, sm); } - /// - /// Handle outputting based off enery, damage, gas mix and radiation - /// - private void HandleOutput( - EntityUid uid, - float frameTime, - SupermatterComponent? sMcomponent = null, - RadiationSourceComponent? radcomponent = null, - Atmos.GasMixture? mixture = null) + if (sm.YellAccumulator >= sm.YellTimer) { - if (!Resolve(uid, ref sMcomponent, ref radcomponent)) - { - return; - } + sm.YellAccumulator -= sm.YellTimer; + HandleAnnouncements(uid, sm); + } + } - sMcomponent.AtmosUpdateAccumulator += frameTime; + #region Processing - if (!(sMcomponent.AtmosUpdateAccumulator > sMcomponent.AtmosUpdateTimer) || - mixture is not { }) - return; + /// + /// Handle power and radiation output depending on atmospheric things. + /// + private void ProcessAtmos(EntityUid uid, SupermatterComponent sm) + { + var mix = _atmosphere.GetContainingMixture(uid, true, true); - sMcomponent.AtmosUpdateAccumulator -= sMcomponent.AtmosUpdateTimer; + if (mix is not { }) + return; - //Absorbed gas from surrounding area - var absorbedGas = mixture.Remove(sMcomponent.GasEfficiency * mixture.TotalMoles); - var absorbedTotalMoles = absorbedGas.TotalMoles; + var absorbedGas = mix.Remove(sm.GasEfficiency * mix.TotalMoles); + var moles = absorbedGas.TotalMoles; - if (!(absorbedTotalMoles > 0f)) - return; + if (!(moles > 0f)) + return; - var gasStorage = sMcomponent.GasStorage; - var gasEffect = sMcomponent.GasDataFields; + var gases = sm.GasStorage; + var facts = sm.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) - ); + //Lets get the proportions of the gasses in the mix for scaling stuff later + //They range between 0 and 1 + gases = gases.ToDictionary( + gas => gas.Key, + gas => Math.Clamp(absorbedGas.GetMoles(gas.Key) / moles, 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); + //No less then zero, and no greater then one, we use this to do explosions and heat to power transfer. + var powerRatio = gases.Sum(gas => gases[gas.Key] * facts[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. Affects plasma, o2 and heat output. + var heatModifier = gases.Sum(gas => gases[gas.Key] * facts[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); + // Minimum value of -10, maximum value of 23. Affects plasma, o2 and heat output. + var transmissionBonus = gases.Sum(gas => gases[gas.Key] * facts[gas.Key].TransmitModifier); - var h2OBonus = 1 - gasStorage[Gas.WaterVapor] * 0.25f; + var h2OBonus = 1 - gases[Gas.WaterVapor] * 0.25f; - gasmixPowerRatio = Math.Clamp(gasmixPowerRatio, 0, 1); - dynamicHeatModifier = Math.Max(dynamicHeatModifier, 0.5f); - powerTransmissionBonus *= h2OBonus; + powerRatio = Math.Clamp(powerRatio, 0, 1); + heatModifier = Math.Max(heatModifier, 0.5f); + transmissionBonus *= h2OBonus; - //Effects the damage heat does to the crystal - sMcomponent.DynamicHeatResistance = 1f; + // Effects the damage heat does to the crystal + sm.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); + // more moles of gases are harder to heat than fewer, + // so let's scale heat damage around them + sm.MoleHeatPenaltyThreshold = (float) Math.Max(moles * sm.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); - } + // 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 (moles > sm.PowerlossInhibitionMoleThreshold && gases[Gas.CarbonDioxide] > sm.PowerlossInhibitionGasThreshold) + { + var co2powerloss = Math.Clamp(gases[Gas.CarbonDioxide] - sm.PowerlossDynamicScaling, -0.02f, 0.02f); + sm.PowerlossDynamicScaling = Math.Clamp(sm.PowerlossDynamicScaling + co2powerloss, 0f, 1f); + } + else + { + sm.PowerlossDynamicScaling = Math.Clamp(sm.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); + // 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 - sm.PowerlossDynamicScaling * + Math.Clamp(moles / sm.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); - } + if (sm.MatterPower != 0) //We base our removed power off one 10th of the matter_power. + { + var removedMatter = Math.Max(sm.MatterPower / sm.MatterPowerConversion, 40); + //Adds at least 40 power + sm.Power = Math.Max(sm.Power + removedMatter, 0); + //Removes at least 40 matter power + sm.MatterPower = Math.Max(sm.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; + //based on gas mix, makes the power more based on heat or less effected by heat + var tempFactor = powerRatio > 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); + //if there is more pluox and n2 then anything else, we receive no power increase from heat + sm.Power = Math.Max(absorbedGas.Temperature * tempFactor / Atmospherics.T0C * powerRatio + sm.Power, 0); - //Rad Pulse Calculation - radcomponent.Intensity = sMcomponent.Power * Math.Max(0, 1f + powerTransmissionBonus / 10f) * 0.003f; + //Radiate stuff + if (TryComp(uid, out var rad)) + rad.Intensity = sm.Power * Math.Max(0, 1f + transmissionBonus / 10f) * 0.003f; - //Power * 0.55 * a value between 1 and 0.8 - var energy = sMcomponent.Power * sMcomponent.ReactionPowerModefier; + //Power * 0.55 * a value between 1 and 0.8 + var energy = sm.Power * sm.ReactionPowerModifier; - //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 + // 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 * heatModifier * sm.ThermalReleaseModifier; + absorbedGas.Temperature = Math.Max(0, + Math.Min(absorbedGas.Temperature, sm.HeatThreshold * heatModifier)); - absorbedGas.Temperature += energy * dynamicHeatModifier / sMcomponent.ThermalReleaseModifier; - absorbedGas.Temperature = Math.Max(0, - Math.Min(absorbedGas.Temperature, sMcomponent.HeatThreshold * dynamicHeatModifier)); + // Release the waste + absorbedGas.AdjustMoles(Gas.Plasma, Math.Max(energy * heatModifier * sm.PlasmaReleaseModifier, 0f)); + absorbedGas.AdjustMoles(Gas.Oxygen, Math.Max((energy + absorbedGas.Temperature * heatModifier - Atmospherics.T0C) * sm.OxygenReleaseEfficiencyModifier, 0f)); - //Calculate how much gas to release - //Varies based on power and gas content + _atmosphere.Merge(mix, absorbedGas); - absorbedGas.AdjustMoles(Gas.Plasma, - Math.Max(energy * dynamicHeatModifier / sMcomponent.PlasmaReleaseModifier, 0f)); + var powerReduction = (float) Math.Pow(sm.Power / 500, 3); - absorbedGas.AdjustMoles(Gas.Oxygen, - Math.Max( - (energy + absorbedGas.Temperature * dynamicHeatModifier - Atmospherics.T0C) / - sMcomponent.OxygenReleaseModifier, 0f)); + // After this point power is lowered + // This wraps around to the begining of the function + sm.Power = Math.Max(sm.Power - Math.Min(powerReduction * powerlossInhibitor, sm.Power * 0.83f * powerlossInhibitor), 0f); + } - _atmosphere.Merge(mixture, absorbedGas); + /// + /// Shoot lightning bolts depensing on accumulated power. + /// + private void SupermatterZap(EntityUid uid, SupermatterComponent sm) + { + // Divide power by it's threshold to get a value from 0 to 1, then multiply by the amount of possible lightnings + var zapPower = sm.Power / sm.PowerPenaltyThreshold * sm.LightningPrototypes.Length; + var zapPowerNorm = (int) Math.Clamp(zapPower, 0, sm.LightningPrototypes.Length - 1); + _lightning.ShootRandomLightnings(uid, 3.5f, sm.Power > sm.PowerPenaltyThreshold ? 3 : 1, sm.LightningPrototypes[zapPowerNorm]); + } - var powerReduction = (float) Math.Pow(sMcomponent.Power / 500, 3); + /// + /// Handles environmental damage. + /// + private void HandleDamage(EntityUid uid, SupermatterComponent sm) + { + var xform = Transform(uid); + var indices = _xform.GetGridOrMapTilePosition(uid, xform); - //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); - } + sm.DamageArchived = sm.Damage; + + var mix = _atmosphere.GetContainingMixture(uid, true, true); - /// - /// Handles environmental damage and dispatching damage warning - /// - private void HandleDamage( - EntityUid uid, - float frameTime, - SupermatterComponent? sMcomponent = null, - ExplosiveComponent? xplode = null, - Atmos.GasMixture? mixture = null) + // We're in space or there is no gas to process + if (!xform.GridUid.HasValue || mix is not { } || mix.TotalMoles == 0f) { - if (!Resolve(uid, ref sMcomponent, ref xplode)) - { - return; - } + sm.Damage += Math.Max(sm.Power / 1000 * sm.DamageIncreaseMultiplier, 0.1f); + return; + } - var xform = Transform(uid); - var indices = _xform.GetGridOrMapTilePosition(uid, xform); + // Absorbed gas from surrounding area + var absorbedGas = mix.Remove(sm.GasEfficiency * mix.TotalMoles); + var moles = absorbedGas.TotalMoles; - sMcomponent.DamageUpdateAccumulator += frameTime; - sMcomponent.YellAccumulator += frameTime; + var totalDamage = 0f; - if (!(sMcomponent.DamageUpdateAccumulator > sMcomponent.DamageUpdateTimer)) - return; + var tempThreshold = Atmospherics.T0C + sm.HeatPenaltyThreshold; - sMcomponent.DamageArchived = sMcomponent.Damage; - //we're in space or there is no gas to process - if (!xform.GridUid.HasValue || mixture is not { } || 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); + // Temperature start to have a positive effect on damage after 350 + var tempDamage = Math.Max(Math.Clamp(moles / 200f, .5f, 1f) * absorbedGas.Temperature - tempThreshold * sm.DynamicHeatResistance, 0f) * sm.MoleHeatThreshold / 150f * sm.DamageIncreaseMultiplier; + totalDamage += tempDamage; - break; - } + // Power only starts affecting damage when it is above 5000 + var powerDamage = Math.Max(sm.Power - sm.PowerPenaltyThreshold, 0f) / 500f * sm.DamageIncreaseMultiplier; + totalDamage += powerDamage; - sMcomponent.Damage = - Math.Min(sMcomponent.DamageArchived + sMcomponent.DamageHardcap * sMcomponent.ExplosionPoint, - sMcomponent.Damage); - } + // Molar count only starts affecting damage when it is above 1800 + var moleDamage = Math.Max(moles - sm.MolePenaltyThreshold, 0) / 80 * sm.DamageIncreaseMultiplier; + totalDamage += moleDamage; - HandleSoundLoop(uid, sMcomponent); + // Healing damage + if (moles < sm.MolePenaltyThreshold) + { + // left there a very small float value so that it doesn't eventually divide by 0. + var healHeatDamage = Math.Min(absorbedGas.Temperature - tempThreshold, 0.001f) / 150; + totalDamage += healHeatDamage; + } - if (sMcomponent.Damage > sMcomponent.ExplosionPoint) - { - Delamination(uid, frameTime, sMcomponent, xplode, mixture); - return; - } + // Check for space tiles next to SM + // TODO: change moles out for checking if adjacent tiles exist + var adjacentMixes = _atmosphere.GetAdjacentTileMixtures(xform.GridUid.Value, indices, false, false); + foreach (var ind in adjacentMixes) + { + if (ind.TotalMoles != 0) + continue; + + var integrity = GetIntegrity(sm); - if (sMcomponent.Damage > sMcomponent.WarningPoint) + var factor = integrity switch { - var integrity = GetIntegrity(sMcomponent.Damage, sMcomponent.ExplosionPoint); - if (sMcomponent.YellAccumulator >= sMcomponent.YellTimer) - { - if (sMcomponent.Damage > sMcomponent.EmergencyPoint) - { - var message = Loc.GetString("supermatter-danger-message", ("integrity", integrity.ToString("0.00"))); - var channel = _prototypeManager.Index("Engineering"); - _radioSystem.SendRadioMessage(uid, message, channel, uid); - } - else if (sMcomponent.Damage >= sMcomponent.DamageArchived) - { - var message = Loc.GetString("supermatter-warning-message", ("integrity", integrity.ToString("0.00"))); - var channel = _prototypeManager.Index("Engineering"); - _radioSystem.SendRadioMessage(uid, message, channel, uid); - } - else - { - var message = Loc.GetString("supermatter-safe-alert", ("integrity", integrity.ToString("0.00"))); - var channel = _prototypeManager.Index("Engineering"); - _radioSystem.SendRadioMessage(uid, message, channel, uid); - } - - sMcomponent.YellAccumulator = 0; - } - } + < 10 => 0.0005f, + < 25 => 0.0009f, + < 45 => 0.005f, + < 75 => 0.002f, + _ => 0f + }; + + totalDamage += Math.Clamp(sm.Power * factor * sm.DamageIncreaseMultiplier, 0, sm.MaxSpaceExposureDamage); - sMcomponent.DamageUpdateAccumulator -= sMcomponent.DamageUpdateTimer; + break; } - private float GetIntegrity(float damage, float explosionPoint) + var damage = Math.Min(sm.DamageArchived + sm.DamageHardcap * sm.DelaminationPoint, totalDamage); + + // prevent it from going negative + sm.Damage = Math.Clamp(damage, 0, float.PositiveInfinity); + } + + /// + /// Handles announcements. + /// + private void HandleAnnouncements(EntityUid uid, SupermatterComponent sm) + { + var message = string.Empty; + var global = false; + + var integrity = GetIntegrity(sm).ToString("0.00"); + + // Special cases + if (sm.Damage < sm.DelaminationPoint && sm.Delamming) { - var integrity = damage / explosionPoint; - integrity = (float) Math.Round(100 - integrity * 100, 2); - integrity = integrity < 0 ? 0 : integrity; - return integrity; + message = Loc.GetString("supermatter-delam-cancel", ("integrity", integrity)); + sm.DelamAnnounced = false; + global = true; } - - /// - /// Runs the logic and timers for Delamination - /// - private void Delamination( - EntityUid uid, - float frameTime, - SupermatterComponent? sMcomponent = null, - ExplosiveComponent? xplode = null, - Atmos.GasMixture? mixture = null) + if (sm.Delamming && !sm.DelamAnnounced) { - if (!Resolve(uid, ref sMcomponent, ref xplode)) + var sb = new StringBuilder(); + var loc = string.Empty; + var alertLevel = sm.AlertCodeYellowId; + + switch (sm.PreferredDelamType) { - return; - } + case DelamType.Explosion: + default: + loc = "supermatter-delam-explosion"; + break; - var xform = Transform(uid); + case DelamType.Singulo: + loc = "supermatter-delam-overmass"; + alertLevel = sm.AlertCodeDeltaId; + break; - //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; - var message = Loc.GetString("supermatter-delamination-overmass"); - var channel = _prototypeManager.Index("Engineering"); - _radioSystem.SendRadioMessage(uid, message, channel, uid); - } - } - else - { - sMcomponent.DelamType = DelamType.Explosion; - var message = Loc.GetString("supermatter-delamination-default"); - var channel = _prototypeManager.Index("Engineering"); - _radioSystem.SendRadioMessage(uid, message, channel, uid); - } + case DelamType.Tesla: + loc = "supermatter-delam-tesla"; + alertLevel = sm.AlertCodeDeltaId; + break; + + case DelamType.Cascade: + loc = "supermatter-delam-cascade"; + alertLevel = sm.AlertCodeDeltaId; + break; } - sMcomponent.FinalCountdown = true; + var station = _station.GetOwningStation(uid); + if (station != null) + _alert.SetLevel((EntityUid) station, alertLevel, true, true, true, false); - sMcomponent.DelamTimerAccumulator += frameTime; - sMcomponent.SpeakAccumulator += frameTime; - var roundSeconds = sMcomponent.DelamTimerTimer - (int) Math.Floor(sMcomponent.DelamTimerAccumulator); + sb.AppendLine(Loc.GetString(loc)); + sb.AppendLine(Loc.GetString("supermatter-seconds-before-delam", ("seconds", sm.DelamTimer))); - //we're more than 5 seconds from delam, only yell every 5 seconds. - if (roundSeconds >= sMcomponent.YellDelam && sMcomponent.SpeakAccumulator >= sMcomponent.YellDelam) - { - var message = Loc.GetString("supermatter-seconds-before-delam", ("seconds", roundSeconds)); - var channel = _prototypeManager.Index("Engineering"); - _radioSystem.SendRadioMessage(uid, message, channel, uid); - sMcomponent.SpeakAccumulator -= sMcomponent.YellDelam; - } - //less than 5 seconds to delam, count every second. - else if (roundSeconds < sMcomponent.YellDelam && sMcomponent.SpeakAccumulator >= 1) - { - sMcomponent.SpeakAccumulator -= 1; - var message = Loc.GetString("supermatter-seconds-before-delam", ("seconds", roundSeconds)); - var channel = _prototypeManager.Index("Engineering"); - _radioSystem.SendRadioMessage(uid, message, channel, uid); - } + message = sb.ToString(); + global = true; + sm.DelamAnnounced = 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; + SupermatterAnnouncement(uid, message, global); + return; + } - if (sMcomponent.DelamType == DelamType.Singulo) - { - //spawn a singulo :) - EntityManager.SpawnEntity("Singularity", xform.Coordinates); - sMcomponent.AudioStream = _audio.Stop(sMcomponent.AudioStream); - } - else + // ignore the 0% integrity alarm + if (sm.Delamming) + return; + + // We are not taking consistent damage. Engis not needed. + if (sm.Damage <= sm.DamageArchived) + return; + + if (sm.Damage >= sm.WarningPoint) + { + message = Loc.GetString("supermatter-warning", ("integrity", integrity)); + if (sm.Damage >= sm.EmergencyPoint) { - //explosion!!!!! - _explosion.TriggerExplosive( - uid, - explosive: xplode, - totalIntensity: sMcomponent.TotalIntensity, - radius: sMcomponent.Radius, - user: uid - ); - - sMcomponent.AudioStream = _audio.Stop(sMcomponent.AudioStream); - _ambient.SetAmbience(uid, false); + message = Loc.GetString("supermatter-emergency", ("integrity", integrity)); + global = true; } + } + SupermatterAnnouncement(uid, message, global); + } - sMcomponent.FinalCountdown = false; + /// + /// Help the SM announce something. + /// + /// If true, does the station announcement. + /// If true, sends the announcement from Central Command. + public void SupermatterAnnouncement(EntityUid uid, string message, bool global = false, string? customSender = null) + { + if (global) + { + var sender = customSender != null ? customSender : Loc.GetString("supermatter-announcer"); + _chat.DispatchStationAnnouncement(uid, message, sender, colorOverride: Color.Yellow); + return; } + _chat.TrySendInGameICMessage(uid, message, InGameICChatType.Speak, hideChat: false, checkRadioPrefix: true); + } - private void HandleSoundLoop(EntityUid uid, SupermatterComponent sMcomponent) + /// + /// Returns the integrity rounded to hundreds, e.g. 100.00% + /// + public float GetIntegrity(SupermatterComponent sm) + { + var integrity = sm.Damage / sm.DelaminationPoint; + integrity = (float) Math.Round(100 - integrity * 100, 2); + integrity = integrity < 0 ? 0 : integrity; + return integrity; + } + + /// + /// Decide on how to delaminate. + /// + public DelamType ChooseDelamType(EntityUid uid, SupermatterComponent sm) + { + var mix = _atmosphere.GetContainingMixture(uid, true, true); + + if (mix is { }) { - var isAggressive = sMcomponent.Damage > sMcomponent.WarningPoint; - var isDelamming = sMcomponent.Damage > sMcomponent.ExplosionPoint; + var absorbedGas = mix.Remove(sm.GasEfficiency * mix.TotalMoles); + var moles = absorbedGas.TotalMoles; - if (!isAggressive && !isDelamming) - { - sMcomponent.AudioStream = _audio.Stop(sMcomponent.AudioStream); - return; - } + if (moles >= sm.MolePenaltyThreshold) + return DelamType.Singulo; + } + if (sm.Power >= sm.PowerPenaltyThreshold) + return DelamType.Tesla; - var smSound = isDelamming ? SuperMatterSound.Delam : SuperMatterSound.Aggressive; + // TODO: add resonance cascade when there's crazy conditions or a destabilizing crystal - if (sMcomponent.SmSound == smSound) - return; + return DelamType.Explosion; + } + + /// + /// Handle the end of the station. + /// + private void HandleDelamination(EntityUid uid, SupermatterComponent sm) + { + var xform = Transform(uid); + + sm.PreferredDelamType = ChooseDelamType(uid, sm); - sMcomponent.AudioStream = _audio.Stop(sMcomponent.AudioStream); - sMcomponent.SmSound = smSound; + if (!sm.Delamming) + { + sm.Delamming = true; + HandleAnnouncements(uid, sm); + } + if (sm.Damage < sm.DelaminationPoint && sm.Delamming) + { + sm.Delamming = false; + HandleAnnouncements(uid, sm); } - /// - /// Determines if an entity can be dusted - /// - private bool CannotDestroy(EntityUid uid) + sm.DelamTimerAccumulator++; + + if (sm.DelamTimerAccumulator < sm.DelamTimer) + return; + + switch (sm.PreferredDelamType) { - var @static = false; + case DelamType.Explosion: + default: + _explosion.TriggerExplosive(uid); + break; + + case DelamType.Singulo: + Spawn(sm.SingularityPrototypeId, xform.Coordinates); + break; + + case DelamType.Tesla: + Spawn(sm.TeslaPrototypeId, xform.Coordinates); + break; + + case DelamType.Cascade: + Spawn(sm.SupermatterKudzuPrototypeId, xform.Coordinates); + break; + } + } + + /// + /// Swaps out ambience sounds whether the SM is delamming or not. + /// + private void HandleSoundLoop(EntityUid uid, SupermatterComponent sm) + { + var ambient = Comp(uid); - var tag = _tag.HasTag(uid, "SMImmune"); + if (ambient == null) + return; - if (EntityManager.TryGetComponent(uid, out var physicsComp)) - { - @static = physicsComp.BodyType == BodyType.Static; - } + if (sm.Delamming && sm.CurrentSoundLoop != sm.DelamSound) + sm.CurrentSoundLoop = sm.DelamSound; + + else if (!sm.Delamming && sm.CurrentSoundLoop != sm.CalmSound) + sm.CurrentSoundLoop = sm.CalmSound; + + if (ambient.Sound != sm.CurrentSoundLoop) + _ambient.SetSound(uid, sm.CurrentSoundLoop, ambient); + } - return tag || @static; + #endregion + + #region Event Handlers + + private void OnMapInit(EntityUid uid, SupermatterComponent sm, 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 mix = _atmosphere.GetContainingMixture(uid, true, true); + mix?.AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesStandard); + mix?.AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesStandard); + } + + private void OnCollideEvent(EntityUid uid, SupermatterComponent sm, ref StartCollideEvent args) + { + if (!sm.Activated) + sm.Activated = true; + + var target = args.OtherEntity; + if (args.OtherBody.BodyType == BodyType.Static + || HasComp(target) + || _container.IsEntityInContainer(uid)) + return; + + if (!HasComp(target)) + { + EntityManager.SpawnEntity(sm.CollisionResultPrototypeId, Transform(target).Coordinates); + _audio.PlayPvs(sm.DustSound, uid); } - private void OnCollideEvent(EntityUid uid, SupermatterComponent supermatter, ref StartCollideEvent args) + EntityManager.QueueDeleteEntity(target); + + if (TryComp(target, out var food)) + sm.Power += food.Energy; + else if (TryComp(target, out var projectile)) + sm.Power += (float) projectile.Damage.GetTotal(); + else + sm.Power++; + + sm.MatterPower += HasComp(target) ? 200 : 0; + } + + private void OnHandInteract(EntityUid uid, SupermatterComponent sm, ref InteractHandEvent args) + { + if (!sm.Activated) + sm.Activated = true; + + var target = args.User; + + if (HasComp(target)) + return; + + sm.MatterPower += 200; + + EntityManager.SpawnEntity(sm.CollisionResultPrototypeId, Transform(target).Coordinates); + _audio.PlayPvs(sm.DustSound, uid); + EntityManager.QueueDeleteEntity(target); + } + + private void OnItemInteract(EntityUid uid, SupermatterComponent sm, ref InteractUsingEvent args) + { + if (!sm.Activated) + sm.Activated = true; + + if (sm.SliverRemoved) + return; + + if (!HasComp(args.Used)) + return; + + var dae = new DoAfterArgs(EntityManager, args.User, 30f, new SupermatterDoAfterEvent(), args.Target) { - var target = args.OtherBody.Owner; + BreakOnDamage = true, + BreakOnHandChange = false, + BreakOnTargetMove = true, + BreakOnUserMove = true, + BreakOnWeightlessMove = false, + NeedHand = true, + RequireCanInteract = true, + }; + + _doAfter.TryStartDoAfter(dae); + _popup.PopupClient(Loc.GetString("supermatter-tamper-begin"), uid, args.User); + } - if (!supermatter.Whitelist.IsValid(target) || CannotDestroy(target) || _container.IsEntityInContainer(uid)) - return; + private void OnGetSliver(EntityUid uid, SupermatterComponent sm, ref SupermatterDoAfterEvent args) + { + if (args.Cancelled) + return; - if (EntityManager.TryGetComponent(target, out var supermatterFood)) - supermatter.Power += supermatterFood.Energy; - else if (EntityManager.TryGetComponent(target, out var projectile)) - supermatter.Power += (float) projectile.Damage.GetTotal(); - else - supermatter.Power++; + // your criminal actions will not go unnoticed + sm.Damage += sm.DelaminationPoint / 10; - supermatter.MatterPower += EntityManager.HasComponent(target) ? 200 : 0; - if (!EntityManager.HasComponent(target)) - { - EntityManager.SpawnEntity("Ash", Transform(target).Coordinates); - _audio.PlayPvs(supermatter.DustSound, uid); - } + var integrity = GetIntegrity(sm).ToString("0.00"); + SupermatterAnnouncement(uid, Loc.GetString("supermatter-announcement-cc-tamper", ("integrity", integrity)), true, "Central Command"); - EntityManager.QueueDeleteEntity(target); - } + Spawn(sm.SliverPrototypeId, _transform.GetMapCoordinates(args.User)); + _popup.PopupClient(Loc.GetString("supermatter-tamper-end"), uid, args.User); + + sm.DelamTimer /= 2; + } - private void OnHandInteract(EntityUid uid, SupermatterComponent supermatter, InteractHandEvent args) + private void OnExamine(EntityUid uid, SupermatterComponent sm, ref ExaminedEvent args) + { + // get all close and personal to it + if (args.IsInDetailsRange) { - var target = args.User; - supermatter.MatterPower += 200; - EntityManager.SpawnEntity("Ash", Transform(target).Coordinates); - _audio.PlayPvs(supermatter.DustSound, uid); - EntityManager.QueueDeleteEntity(target); + args.PushMarkup(Loc.GetString("supermatter-examine-integrity", ("integrity", GetIntegrity(sm).ToString("0.00")))); } } + + #endregion } diff --git a/Content.Shared/Supermatter/Components/SupermatterComponent.cs b/Content.Shared/Supermatter/Components/SupermatterComponent.cs index 64ba90ce01..5bbefff8ac 100644 --- a/Content.Shared/Supermatter/Components/SupermatterComponent.cs +++ b/Content.Shared/Supermatter/Components/SupermatterComponent.cs @@ -1,345 +1,336 @@ using Robust.Shared.GameStates; using Robust.Shared.Audio; using Content.Shared.Atmos; -using Content.Shared.Supermatter.Systems; using Content.Shared.Whitelist; +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; namespace Content.Shared.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; + #region Base /// - /// The amount of damage we have currently + /// The SM will only cycle if activated. /// + [DataField("activated")] [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; + public bool Activated = false; - public SharedSupermatterSystem.DelamType DelamType; + [DataField("supermatterSliverPrototype")] + public string SliverPrototypeId = "SupermatterSliver"; /// - /// The portion of the gasmix we're on + /// Affects delamination timer. If removed - delamination timer is divided by 2. /// + [DataField("sliverRemoved")] [ViewVariables(VVAccess.ReadWrite)] - [DataField("gasEfficiency")] - public float GasEfficiency = 0.15f; + public bool SliverRemoved = false; - /// - /// The amount of heat we apply scaled - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("heatThreshold")] - public float HeatThreshold = 2500f; + [DataField("whitelist")] + public EntityWhitelist Whitelist = new(); + public string IdTag = "EmitterBolt"; + + public string[] LightningPrototypes = + { + "Lightning", + "ChargedLightning", + "SuperchargedLightning", + "HyperchargedLightning" + }; + + [DataField("singularitySpawnPrototype")] + public string SingularityPrototypeId = "Singularity"; - #endregion SM Base + [DataField("teslaSpawnPrototype")] + public string TeslaPrototypeId = "TeslaEnergyBall"; + + [DataField("supermatterKudzuSpawnPrototype")] + public string SupermatterKudzuPrototypeId = "SupermatterKudzu"; - #region SM Sound /// - /// Current stream of SM audio. + /// What spawns in the place of an unfortunate entity that got removed by the SM. /// - public EntityUid? AudioStream; - - public SharedSupermatterSystem.SuperMatterSound? SmSound; + [DataField("collisionResultPrototype")] + public string CollisionResultPrototypeId = "Ash"; [DataField("dustSound")] - public SoundSpecifier DustSound = new SoundPathSpecifier("/Audio/Supermatter/dust.ogg"); + public SoundSpecifier DustSound = new SoundPathSpecifier("/Audio/Effects/Grenades/Supermatter/supermatter_start.ogg"); + + [DataField("calmSound")] + public SoundSpecifier CalmSound = new SoundPathSpecifier("/Audio/Supermatter/calm.ogg"); [DataField("delamSound")] public SoundSpecifier DelamSound = new SoundPathSpecifier("/Audio/Supermatter/delamming.ogg"); - [DataField("delamAlarm")] - public SoundSpecifier DelamAlarm = new SoundPathSpecifier("/Audio/Machines/alarm.ogg"); + [DataField("currentSoundLoop")] + public SoundSpecifier CurrentSoundLoop = new SoundPathSpecifier("/Audio/Supermatter/calm.ogg"); + + #endregion - #endregion SM Sound + #region Processing + + [ViewVariables(VVAccess.ReadWrite)] + public float Power; + + [ViewVariables(VVAccess.ReadWrite)] + public float MatterPower; - #region SM Calculation + [ViewVariables(VVAccess.ReadWrite)] + public float MatterPowerConversion = 10f; + /// + /// The portion of the gasmix we're on + /// + [ViewVariables(VVAccess.ReadWrite)] + public float GasEfficiency = 0.15f; /// - /// Based on co2 percentage, slowly moves between - /// 0 and 1. We use it to calc the powerloss_inhibitor + /// Based on co2 percentage, slowly moves between 0 and 1. We use it to calc the powerloss_inhibitor /// [ViewVariables(VVAccess.ReadOnly)] - [DataField("powerlossdynamicScaling")] public float PowerlossDynamicScaling; /// - /// Affects the amount of damage and minimum point - /// at which the sm takes heat damage + /// Affects the amount of damage and minimum point at which the sm takes heat damage /// [ViewVariables(VVAccess.ReadOnly)] - [DataField("dynamicheatResistance")] public float DynamicHeatResistance = 1; /// - /// Used to increase or lessen the amount of damage the sm takes - /// from heat based on molar counts. + /// Multiplier on damage the core takes from absorbing hot gas. + /// Default is ~1/350 + /// + [ViewVariables(VVAccess.ReadOnly)] + public float MoleHeatPenalty = 0.00286f; + + /// + /// Inverse of MoleHeatPenalty /// [ViewVariables(VVAccess.ReadOnly)] - [DataField("moleheatPenalty")] - public float MoleHeatPenalty = 350f; + public float MoleHeatThreshold = 350f; /// - /// Higher == more overall power + /// Multiplier on power generated by nuclear reactions /// [ViewVariables(VVAccess.ReadOnly)] - [DataField("reactionpowerModefier")] - public float ReactionPowerModefier = 0.55f; + [DataField("reactionpowerModifier")] + public float ReactionPowerModifier = 0.55f; /// - /// Higher == less heat released during reaction + /// Acts as a multiplier on the amount that nuclear reactions increase the supermatter core temperature /// [ViewVariables(VVAccess.ReadWrite)] [DataField("thermalreleaseModifier")] - public float ThermalReleaseModifier = 5f; + public float ThermalReleaseModifier = 0.2f; /// - /// Higher == less plasma released by reaction + /// Multiplier on how much plasma is released during supermatter reactions + /// Default is ~1/750 /// [ViewVariables(VVAccess.ReadOnly)] [DataField("plasmareleaseModifier")] - public float PlasmaReleaseModifier = 750f; + public float PlasmaReleaseModifier = 0.001333f; /// - /// Higher == less oxygen released at high temperature/power + /// Multiplier on how much oxygen is released during supermatter reactions. + /// Default is ~1/325 /// [ViewVariables(VVAccess.ReadOnly)] [DataField("oxygenreleaseModifier")] - public float OxygenReleaseModifier = 325f; + public float OxygenReleaseEfficiencyModifier = 0.0031f; - #endregion SM Calculation + #endregion - #region SM Timer + #region Timing /// - /// The point at which we should start sending messeges - /// about the damage to the engi channels. + /// We yell if over 50 damage every YellTimer Seconds /// - [ViewVariables(VVAccess.ReadOnly)] - [DataField("WarningPoint")] - public float WarningPoint = 50; + [ViewVariables(VVAccess.ReadWrite)] + public float YellTimer = 60f; /// - /// The point at which we start sending messages to the common channel + /// Set to YellTimer at first so it doesnt yell a minute after being hit /// [ViewVariables(VVAccess.ReadOnly)] - [DataField("emergencyPoint")] - public float EmergencyPoint = 500; + public float YellAccumulator = 60f; /// - /// we yell if over 50 damage every YellTimer Seconds + /// Timer for delam /// [ViewVariables(VVAccess.ReadOnly)] - [DataField("yellTimer")] - public float YellTimer = 30f; + public float DelamTimerAccumulator; /// - /// set to YellTimer at first so it doesnt yell a minute after being hit + /// Time until delam /// - [ViewVariables(VVAccess.ReadOnly)] - [DataField("yellAccumulator")] - public float YellAccumulator = 30f; + [ViewVariables(VVAccess.ReadWrite)] + [DataField("delamTimer")] + public float DelamTimer = 120f; /// - /// YellTimer before the SM is about the delam + /// The message timer /// - [ViewVariables(VVAccess.ReadOnly)] - [DataField("yellDelam")] - public float YellDelam = 5f; + [ViewVariables(VVAccess.ReadWrite)] + public float SpeakAccumulator = 60f; - /// - /// Timer for Damage - /// [ViewVariables(VVAccess.ReadOnly)] - [DataField("damageupdateAccumulator")] - public float DamageUpdateAccumulator; + public float UpdateAccumulator = 0f; - /// - /// update environment damage every 1 second - /// - [ViewVariables(VVAccess.ReadOnly)] - [DataField("damageupdateTimer")] - public float DamageUpdateTimer = 1f; + [ViewVariables(VVAccess.ReadWrite)] + public float UpdateTimer = 1f; - /// - /// Timer for delam - /// [ViewVariables(VVAccess.ReadOnly)] - [DataField("delamtimerAccumulator")] - public float DelamTimerAccumulator; + public float ZapAccumulator = 0f; - /// - /// updates delam - /// - [ViewVariables(VVAccess.ReadOnly)] - [DataField("delamtimerTimer")] - public int DelamTimerTimer = 30; + [ViewVariables(VVAccess.ReadWrite)] + public float ZapTimer = 10f; - /// - /// The message timer - /// - [ViewVariables(VVAccess.ReadOnly)] - [DataField("speakaccumulator")] - public float SpeakAccumulator = 5f; + #endregion - /// - /// Atmos update timer - /// - [ViewVariables(VVAccess.ReadOnly)] - [DataField("atmosupdateAccumulator")] - public float AtmosUpdateAccumulator; + #region Thresholds /// - /// update atmos every 1 second + /// The amount of heat we apply scaled /// - [ViewVariables(VVAccess.ReadOnly)] - [DataField("atmosupdateTimer")] - public float AtmosUpdateTimer = 1f; - - #endregion SM Timer - - #region SM Threshold + [ViewVariables(VVAccess.ReadWrite)] + public float HeatThreshold = 2500f; /// - /// Higher == Higher percentage of inhibitor gas needed - /// before the charge inertia chain reaction effect starts. + /// Higher == Higher percentage of inhibitor gas needed + /// before the charge inertia chain reaction effect starts. /// [ViewVariables(VVAccess.ReadOnly)] [DataField("powerlossinhibitiongasThreshold")] public float PowerlossInhibitionGasThreshold = 0.20f; /// - /// 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 + /// 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 /// [ViewVariables(VVAccess.ReadOnly)] [DataField("powerlossinhibitionmoleThreshold")] public float PowerlossInhibitionMoleThreshold = 20f; /// - /// bonus powerloss inhibition boost if this amount of moles is reached + /// Bonus powerloss inhibition boost if this amount of moles is reached /// [ViewVariables(VVAccess.ReadOnly)] [DataField("powerlossinhibitionmoleboostThreshold")] public float PowerlossInhibitionMoleBoostThreshold = 500f; /// - /// Above this value we can get lord singulo and independent mol damage, - /// below it we can heal damage + /// Above this value we can get lord singulo and independent mol damage, below it we can heal damage /// [ViewVariables(VVAccess.ReadOnly)] [DataField("molepenaltyThreshold")] - public float MolePenaltyThreshold = 1800f; + public float MolePenaltyThreshold = 900f; /// - /// more moles of gases are harder to heat than fewer, - /// so let's scale heat damage around them + /// More moles of gases are harder to heat than fewer, so let's scale heat damage around them /// [ViewVariables(VVAccess.ReadOnly)] [DataField("moleheatpenaltyThreshold")] public float MoleHeatPenaltyThreshold; /// - /// The cutoff on power properly doing damage, pulling shit around, - /// and delamming into a tesla. Low chance of pyro anomalies, +2 bolts of electricity + /// The cutoff on power properly doing damage, pulling shit around, + /// and delamming into a tesla. Low chance of pyro anomalies, +2 bolts of electricity /// [ViewVariables(VVAccess.ReadOnly)] [DataField("powerPenaltyThreshold")] - public float PowerPenaltyThreshold = 5000f; + public float PowerPenaltyThreshold = 4000f; /// - /// Higher == Crystal safe operational temperature is higher. + /// Maximum safe operational temperature in degrees Celsius. Supermatter begins taking damage above this temperature. /// [ViewVariables(VVAccess.ReadOnly)] [DataField("heatpenaltyThreshold")] public float HeatPenaltyThreshold = 40f; + #endregion + + #region Damage + + /// + /// The amount of damage we have currently + /// + [ViewVariables(VVAccess.ReadWrite)] + public float Damage = 0f; + /// - /// The damage we had before this cycle. Used to limit the damage we can take each cycle, and for safe alert + /// The damage we had before this cycle. Used to limit the damage we can take each cycle, and for safe alert /// [ViewVariables(VVAccess.ReadWrite)] - [DataField("damagearchived")] public float DamageArchived = 0f; /// - /// is multiplied by ExplosionPoint to cap - /// evironmental damage per cycle + /// Is multiplied by ExplosionPoint to cap evironmental damage per cycle /// [ViewVariables(VVAccess.ReadOnly)] - [DataField("damageHardcap")] public float DamageHardcap = 0.002f; /// - /// environmental damage is scaled by this + /// Environmental damage is scaled by this /// [ViewVariables(VVAccess.ReadOnly)] [DataField("damageincreaseMultiplier")] public float DamageIncreaseMultiplier = 0.25f; /// - /// if spaced sm wont take more than 2 damage per cycle + /// If spaced sm wont take more than 2 damage per cycle /// [ViewVariables(VVAccess.ReadOnly)] [DataField("maxspaceexposureDamage")] public float MaxSpaceExposureDamage = 2; - #endregion SM Threshold + /// + /// The point at which we should start sending messeges about the damage. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("WarningPoint")] + public float WarningPoint = 50; - #region SM Delamm + /// + /// The point at which we start sending announcements. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("emergencyPoint")] + public float EmergencyPoint = 500; /// - /// The point at which we delamm + /// The point at which we begin delaminating. /// [ViewVariables(VVAccess.ReadOnly)] [DataField("explosionPoint")] - public int ExplosionPoint = 900; - - //Are we delamming? - [ViewVariables(VVAccess.ReadOnly)] public bool Delamming = false; + public int DelaminationPoint = 900; - //it's the final countdown - [ViewVariables(VVAccess.ReadOnly)] public bool FinalCountdown = false; - - //Explosion totalIntensity value [ViewVariables(VVAccess.ReadOnly)] - [DataField("totalIntensity")] - public float TotalIntensity= 500000f; + public bool Delamming = false; - //Explosion radius value [ViewVariables(VVAccess.ReadOnly)] - [DataField("radius")] - public float Radius = 500f; + public DelamType PreferredDelamType = DelamType.Explosion; - /// - /// These would be what you would get at point blank, decreases with distance - /// - [ViewVariables(VVAccess.ReadOnly)] - [DataField("detonationRads")] - public float DetonationRads = 200f; + #endregion + + #region Announcements + + [DataField("alertCodeYellowId")] + public string AlertCodeYellowId = "yellow"; - #endregion SM Delamm + [DataField("alertCodeDeltaId")] + public string AlertCodeDeltaId = "delta"; + + public bool DelamAnnounced = false; + + #endregion + + #region Gases - #region SM Gas /// - /// Is used to store gas + /// Is used to store gas /// [ViewVariables(VVAccess.ReadOnly)] [DataField("gasStorage")] @@ -347,30 +338,71 @@ public sealed partial class SupermatterComponent : Component { {Gas.Oxygen, 0f}, {Gas.Nitrogen, 0f}, - {Gas.NitrousOxide, 0f}, {Gas.CarbonDioxide, 0f}, {Gas.Plasma, 0f}, {Gas.Tritium, 0f}, {Gas.WaterVapor, 0f}, {Gas.Frezon, 0f}, - {Gas.Ammonia, 0f} + {Gas.Ammonia, 0f}, + {Gas.NitrousOxide, 0f}, }; /// - /// Stores each gases calculation + /// Stores each gas facts /// + // todo: replace this with serializable GasFact array something public readonly Dictionary GasDataFields = new() { [Gas.Oxygen] = (TransmitModifier: 1.5f, HeatPenalty: 1f, PowerMixRatio: 1f), [Gas.Nitrogen] = (TransmitModifier: 0f, HeatPenalty: -1.5f, PowerMixRatio: -1f), - [Gas.NitrousOxide] = (TransmitModifier: 1f, HeatPenalty: -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), - [Gas.Frezon] = (TransmitModifier: 3f, HeatPenalty: -9f, PowerMixRatio: -1f), - [Gas.Ammonia] = (TransmitModifier: 1.5f, HeatPenalty: 1.5f, PowerMixRatio: 1.5f) + [Gas.Frezon] = (TransmitModifier: 3f, HeatPenalty: -10f, PowerMixRatio: -1f), + [Gas.Ammonia] = (TransmitModifier: 0f, HeatPenalty: .5f, PowerMixRatio: 1f), + [Gas.NitrousOxide] = (TransmitModifier: 0f, HeatPenalty: -5f, PowerMixRatio: -1f), }; - #endregion SM Gas + #endregion +} + +public enum SupermatterSound : sbyte +{ + Aggressive = 0, + Delam = 1 +} + +public enum DelamType : sbyte +{ + Explosion = 0, + Singulo = 1, + Tesla = 2, + Cascade = 3 +} + +[Serializable, DataDefinition] +public sealed partial class GasFact +{ + [DataField("transmitModifier")] + public float TransmitModifier; + + [DataField("heatPenalty")] + public float HeatPenalty; + + [DataField("powerMixRatio")] + public float PowerMixRatio; + + public GasFact(float transmitModifier, float heatPenalty, float powerMixRatio) + { + TransmitModifier = transmitModifier; + HeatPenalty = heatPenalty; + PowerMixRatio = powerMixRatio; + } +} + +[Serializable, NetSerializable] +public sealed partial class SupermatterDoAfterEvent : SimpleDoAfterEvent +{ + } diff --git a/Content.Shared/Supermatter/Components/SupermatterImmuneComponent.cs b/Content.Shared/Supermatter/Components/SupermatterImmuneComponent.cs new file mode 100644 index 0000000000..b517115eca --- /dev/null +++ b/Content.Shared/Supermatter/Components/SupermatterImmuneComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Supermatter.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class SupermatterImmuneComponent : Component +{ + +} diff --git a/Resources/Locale/en-US/objectives/conditions/steal.ftl b/Resources/Locale/en-US/objectives/conditions/steal.ftl index 00c8e0fdaf..8547a01db2 100644 --- a/Resources/Locale/en-US/objectives/conditions/steal.ftl +++ b/Resources/Locale/en-US/objectives/conditions/steal.ftl @@ -9,3 +9,6 @@ objective-condition-steal-Ian = head of personnel's corgi objective-condition-thief-description = The {$itemName} would be a great addition to my collection! objective-condition-thief-animal-description = The {$itemName} would be a great addition to my collection! Most importantly, alive. objective-condition-thief-multiply-description = I need to get {$count} {MAKEPLURAL($itemName)} and take them with me. + +objective-condition-steal-smsliver-title = Cut off a sliver from the supermatter crystal. +objective-condition-steal-smsliver-description = Use any cutting tool that comes in handy. A scalpel is more recommended. Also, don't die of radiation poisoning. \ No newline at end of file diff --git a/Resources/Locale/en-US/supermatter/supermatter.ftl b/Resources/Locale/en-US/supermatter/supermatter.ftl index 530fae8a6e..b656fcd128 100644 --- a/Resources/Locale/en-US/supermatter/supermatter.ftl +++ b/Resources/Locale/en-US/supermatter/supermatter.ftl @@ -1,7 +1,26 @@ -supermatter-self = Supermatter -supermatter-danger-message = Danger! Crystal hyperstructure integrity faltering! Integrity: { $integrity }% -supermatter-warning-message = WARNING! Crystal hyperstructure integrity reaching critical levels! Integrity: { $integrity }% -supermatter-safe-alert = Crystalline hyperstructure returning to safe operating parameters. Failsafe has been Disengaged. Integrity: { $integrity }% -supermatter-delamination-overmass = The Supermatter has Reached Critical Mass Falure. Singularity formation Imminent -supermatter-delamination-default = The Supermatter has Reached Critical Integrity Falure. Emergency Causality Destabilization Field has been Activated. -supermatter-seconds-before-delam = { $Seconds } Seconds Remain Before Delamination. +supermatter-announcer = Automatic Supermatter Engine +supermatter-examine-integrity = + It's integrity is [color=yellow]{$integrity}%[/color]. +supermatter-warning = + Warning! Crystal hyperstructure integrity faltering! Integrity: {$integrity}%. +supermatter-emergency = + DANGER! Crystal hyperstructure integrity reaching critical levels! Integrity: {$integrity}%. +supermatter-delam-explosion = + CRYSTAL DELAMINATION IMMINENT! The crystal has reached critical integrity failure! Emergency causality destabilization field has been engaged. +supermatter-delam-overmass = + CRYSTAL DELAMINATION IMMINENT! Crystal hyperstructure integrity has reached critical mass failure! Singularity formation imminent! +supermatter-delam-tesla = + CRYSTAL DELAMINATION IMMINENT! Crystal hyperstructure integrity has reached critical power surge failure! Energy ball formation imminent! +supermatter-delam-cascade = + CRYSTAL DELAMINATION IMMINENT! Harmonic frequency limits exceeded, casualty destabilization field could not be engaged! +supermatter-delam-cancel = + Crystalline hyperstructure returning to safe operating parameters. Failsafe has been Disengaged. Integrity: {$integrity}%. +supermatter-seconds-before-delam = + Estimated time before delamination: {$seconds} seconds. +supermatter-tamper-begin = + You begin carefully cutting a piece off the supermatter crystal... +supermatter-tamper-end = + You feel the power of a thousand suns laying on your palms. Or is it all the radiation? +supermatter-announcement-cc-tamper = + Our automatic casualty system has detected that the supermatter crystal structural integrity was compromised by an external force. + Engineering department, report to the supermatter engine immediately. diff --git a/Resources/Maps/edge.yml b/Resources/Maps/edge.yml index ffe7e11156..b624368643 100644 --- a/Resources/Maps/edge.yml +++ b/Resources/Maps/edge.yml @@ -243,7 +243,7 @@ entities: version: 6 -3,-3: ind: -3,-3 - tiles: AAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAUwAAAAAAUwAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAUwAAAAAASAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAPQAAAAAAUwAAAAAASAAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAUwAAAAAASAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAUwAAAAAAUwAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAA + tiles: AAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAUwAAAAAAUwAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAUwAAAAAASAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAPQAAAAAAUwAAAAAASAAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAUwAAAAAASAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAUwAAAAAAUwAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAAggAAAAAA version: 6 -3,-4: ind: -3,-4 @@ -345,10 +345,6 @@ entities: ind: 1,-4 tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAggAAAAAAgQAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAggAAAAAAgQAAAAAAgQAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 - -4,-3: - ind: -4,-3 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 - type: Broadphase - type: Physics bodyStatus: InAir @@ -40884,6 +40880,25 @@ entities: - type: Transform pos: -40.5,49.5 parent: 2 +- proto: CrateEngineeringTeslaCoil + entities: + - uid: 17875 + components: + - type: Transform + pos: -25.5,-30.5 + parent: 2 + - uid: 17916 + components: + - type: Transform + pos: -26.5,-30.5 + parent: 2 +- proto: CrateEngineeringTeslaGroundingRod + entities: + - uid: 17917 + components: + - type: Transform + pos: -27.5,-30.5 + parent: 2 - proto: CrateEngineeringToolbox entities: - uid: 6253 @@ -47505,15 +47520,6 @@ entities: - type: DeviceLinkSink links: - 17680 - - uid: 17875 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -28.5,-38.5 - parent: 2 - - type: DeviceLinkSink - links: - - 17680 - proto: EncryptionKeyCargo entities: - uid: 7261 @@ -50480,14 +50486,6 @@ entities: color: '#ADD8E6FF' - proto: GasPassiveVent entities: - - uid: 2623 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -47.5,-39.5 - parent: 2 - - type: AtmosDevice - joinedGrid: 2 - uid: 3909 components: - type: Transform @@ -50624,46 +50622,6 @@ entities: parent: 2 - type: AtmosDevice joinedGrid: 2 - - uid: 17850 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -32.5,-37.5 - parent: 2 - - type: AtmosDevice - joinedGrid: 2 - - uid: 17916 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -32.5,-38.5 - parent: 2 - - type: AtmosDevice - joinedGrid: 2 - - uid: 17917 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -32.5,-39.5 - parent: 2 - - type: AtmosDevice - joinedGrid: 2 - - uid: 17918 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -47.5,-40.5 - parent: 2 - - type: AtmosDevice - joinedGrid: 2 - - uid: 17919 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -47.5,-41.5 - parent: 2 - - type: AtmosDevice - joinedGrid: 2 - proto: GasPipeBend entities: - uid: 2635 @@ -52409,41 +52367,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 17920 - components: - - type: Transform - pos: -46.5,-39.5 - parent: 2 - - uid: 17921 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -46.5,-41.5 - parent: 2 - - uid: 17922 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -33.5,-37.5 - parent: 2 - - uid: 17923 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -33.5,-39.5 - parent: 2 - - uid: 17932 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -41.5,-38.5 - parent: 2 - - uid: 17933 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -41.5,-40.5 - parent: 2 - proto: GasPipeFourway entities: - uid: 3169 @@ -52768,16 +52691,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#07FFFFFF' - - uid: 17924 - components: - - type: Transform - pos: -33.5,-38.5 - parent: 2 - - uid: 17939 - components: - - type: Transform - pos: -46.5,-40.5 - parent: 2 - proto: GasPipeStraight entities: - uid: 3141 @@ -67664,72 +67577,6 @@ entities: parent: 2 - type: AtmosPipeColor color: '#07FFFFFF' - - uid: 17925 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -34.5,-38.5 - parent: 2 - - uid: 17926 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -35.5,-38.5 - parent: 2 - - uid: 17927 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -36.5,-38.5 - parent: 2 - - uid: 17928 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -37.5,-38.5 - parent: 2 - - uid: 17929 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -38.5,-38.5 - parent: 2 - - uid: 17931 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -40.5,-38.5 - parent: 2 - - uid: 17934 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -41.5,-39.5 - parent: 2 - - uid: 17935 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -42.5,-40.5 - parent: 2 - - uid: 17936 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -43.5,-40.5 - parent: 2 - - uid: 17937 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -44.5,-40.5 - parent: 2 - - uid: 17938 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -45.5,-40.5 - parent: 2 - proto: GasPipeTJunction entities: - uid: 2633 @@ -71258,18 +71105,6 @@ entities: joinedGrid: 2 - type: AtmosPipeColor color: '#FF0000FF' - - uid: 17930 - components: - - type: MetaData - name: emergency core vent - - type: Transform - rot: 1.5707963267948966 rad - pos: -39.5,-38.5 - parent: 2 - - type: GasValve - open: False - - type: AtmosDevice - joinedGrid: 2 - proto: GasVentPump entities: - uid: 2584 @@ -73123,8 +72958,6 @@ entities: entities: - uid: 2675 components: - - type: MetaData - name: high volume scrubber - type: Transform rot: 3.141592653589793 rad pos: -32.5,-37.5 @@ -73134,15 +72967,10 @@ entities: - 17544 - type: AtmosDevice joinedGrid: 2 - - type: GasVentScrubber - maxPressure: 9000 - maxTransferRate: 400 - type: AtmosPipeColor color: '#FF5349FF' - uid: 2676 components: - - type: MetaData - name: high volume scrubber - type: Transform rot: 3.141592653589793 rad pos: -30.5,-37.5 @@ -73152,15 +72980,10 @@ entities: - 17544 - type: AtmosDevice joinedGrid: 2 - - type: GasVentScrubber - maxPressure: 9000 - maxTransferRate: 400 - type: AtmosPipeColor color: '#FF5349FF' - uid: 3901 components: - - type: MetaData - name: high volume scrubber - type: Transform rot: 3.141592653589793 rad pos: -31.5,-37.5 @@ -73170,9 +72993,6 @@ entities: - 17544 - type: AtmosDevice joinedGrid: 2 - - type: GasVentScrubber - maxPressure: 9000 - maxTransferRate: 400 - type: AtmosPipeColor color: '#FF5349FF' - uid: 10409 @@ -81693,8 +81513,6 @@ entities: - Pressed: Toggle 3902: - Pressed: Toggle - 17875: - - Pressed: Toggle - proto: LockerAtmosphericsFilled entities: - uid: 11778 @@ -89055,6 +88873,11 @@ entities: - 14706 - proto: ReinforcedGirder entities: + - uid: 2623 + components: + - type: Transform + pos: -47.5,-41.5 + parent: 2 - uid: 2624 components: - type: Transform @@ -99921,6 +99744,13 @@ entities: - type: Transform pos: -24.5,-80.5 parent: 2 +- proto: TeslaGroundingRod + entities: + - uid: 17850 + components: + - type: Transform + pos: -30.5,-38.5 + parent: 2 - proto: ThermomachineFreezerMachineCircuitBoard entities: - uid: 14636 diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml index a05d346c2f..2c7c99ede0 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml @@ -342,6 +342,9 @@ - type: HeldSpeedModifier - type: ToggleableClothing clothingPrototype: ClothingHeadHelmetHardsuitEngineeringWhite + - type: ClothingGrantComponent + component: + - type: SupermatterImmune #Chief Medical Officer's Hardsuit - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index 80e87d3670..761b1011bb 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -97,6 +97,7 @@ - type: InventorySlots - type: Loadout prototypes: [ MobAghostGear ] + - type: SupermatterImmune - type: entity id: ActionAGhostShowSolar diff --git a/Resources/Prototypes/Entities/Objects/Misc/supermatter_sliver.yml b/Resources/Prototypes/Entities/Objects/Misc/supermatter_sliver.yml new file mode 100644 index 0000000000..e524a4fbf8 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Misc/supermatter_sliver.yml @@ -0,0 +1,25 @@ +- type: entity + parent: BaseItem + id: SupermatterSliver + name: supermatter sliver + description: A shard from the station's supermatter crystal. Highly radioactive. + components: + - type: PointLight + enabled: true + radius: 3 + energy: 2 + color: "#fff633" + - type: RadiationSource + intensity: .75 + - type: Icon + sprite: Supermatter/supermatter_sliver.rsi + state: icon + - type: Sprite + sprite: Supermatter/supermatter_sliver.rsi + state: icon + - type: StealTarget + stealGroup: SupermatterSliver + - type: Tag + tags: + - HighRiskItem + - type: SupermatterImmune \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml index bffd34e4fc..4ea66adc6a 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/morgue.yml @@ -110,7 +110,6 @@ - type: Tag tags: - Trash - - SMImmune - type: SolutionContainerManager solutions: food: @@ -123,6 +122,7 @@ ignoreEmpty: true - type: Extractable grindableSolutionName: food + - type: SupermatterImmune - type: entity parent: Ash diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml index 25d219ab94..b8d66c61e3 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/singularity.yml @@ -3,6 +3,7 @@ name: gravitational singularity description: A mesmerizing swirl of darkness that sucks in everything. If it's moving towards you, run. components: + - type: SupermatterImmune - type: Clickable - type: AmbientSound volume: -4 diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Supermatter/supermatter.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Supermatter/supermatter.yml new file mode 100644 index 0000000000..1351b88a17 --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Supermatter/supermatter.yml @@ -0,0 +1,72 @@ +- type: entity + id: Supermatter + name: supermatter crystal + 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: 0 + sound: + path: /Audio/Supermatter/calm.ogg + - type: Physics + - 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: Supermatter/supermatter.rsi + state: supermatter + - type: Icon + sprite: Supermatter/supermatter.rsi + state: supermatter + - type: PointLight + enabled: true + radius: 10 + energy: 5 + color: "#ffe000" + - type: Explosive + explosionType: Supermatter + maxIntensity: 10000 + intensitySlope: 5 + totalIntensity: 10000 + - type: GuideHelp + guides: [ Supermatter, Power ] + - type: WarpPoint + follow: true + location: supermatter + - type: SinguloFood + energy: 10000 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml index f236bb8a41..e2efd89eb5 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/coil.yml @@ -5,6 +5,7 @@ placement: mode: SnapgridCenter components: + - type: SupermatterImmune - type: Transform anchored: true - type: Physics @@ -111,6 +112,7 @@ placement: mode: SnapgridCenter components: + - type: SupermatterImmune - type: Transform anchored: true - type: Physics diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml index 4567c6d044..c5c980193f 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/energyball.yml @@ -2,6 +2,7 @@ id: BaseEnergyBall abstract: true components: + - type: SupermatterImmune - type: Clickable - type: Physics bodyType: KinematicController diff --git a/Resources/Prototypes/Guidebook/engineering.yml b/Resources/Prototypes/Guidebook/engineering.yml index 21d17f0227..ef502d8ab4 100644 --- a/Resources/Prototypes/Guidebook/engineering.yml +++ b/Resources/Prototypes/Guidebook/engineering.yml @@ -66,6 +66,7 @@ - Singularity - TEG - RTG + - Supermatter - type: guideEntry id: AME @@ -91,3 +92,8 @@ id: PortableGenerator name: guide-entry-portable-generator text: "/ServerInfo/Guidebook/Engineering/PortableGenerator.xml" + +- type: guideEntry + id: Supermatter + name: guide-entry-sm + text: "/ServerInfo/Guidebook/Engineering/Supermatter.xml" \ No newline at end of file diff --git a/Resources/Prototypes/Objectives/objectiveGroups.yml b/Resources/Prototypes/Objectives/objectiveGroups.yml index d711e9a1b1..954e8db06f 100644 --- a/Resources/Prototypes/Objectives/objectiveGroups.yml +++ b/Resources/Prototypes/Objectives/objectiveGroups.yml @@ -26,6 +26,7 @@ LOLuckyBillStealObjective: 0.5 # DeltaV - LO steal objective, see Resources/Prototypes/DeltaV/Objectives/traitor.yml HoPBookIanDossierStealObjective: 1 # DeltaV - HoP steal objective, see Resources/Prototypes/DeltaV/Objectives/traitor.yml HoSGunStealObjective: 0.5 + StealSupermatterSliverObjective: 0.5 - type: weightedRandom id: TraitorObjectiveGroupKill diff --git a/Resources/Prototypes/Objectives/stealTargetGroups.yml b/Resources/Prototypes/Objectives/stealTargetGroups.yml index 0ce8a9781e..92b5d2aef5 100644 --- a/Resources/Prototypes/Objectives/stealTargetGroups.yml +++ b/Resources/Prototypes/Objectives/stealTargetGroups.yml @@ -394,3 +394,10 @@ sprite: sprite: Mobs/Animals/crab.rsi state: crab + +- type: stealTargetGroup + id: SupermatterSliver + name: Supermatter sliver + sprite: + sprite: Supermatter/supermatter_sliver.rsi + state: icon diff --git a/Resources/Prototypes/Objectives/traitor.yml b/Resources/Prototypes/Objectives/traitor.yml index ffeba32546..d9c071c30c 100644 --- a/Resources/Prototypes/Objectives/traitor.yml +++ b/Resources/Prototypes/Objectives/traitor.yml @@ -309,3 +309,15 @@ - type: StealCondition stealGroup: NukeDisk owner: objective-condition-steal-station + +- type: entity + noSpawn: true + parent: BaseTraitorStealObjective + id: StealSupermatterSliverObjective + components: + - type: Objective + difficulty: 3.5 + - type: StealCondition + stealGroup: SupermatterSliver + objectiveNoOwnerText: objective-condition-steal-smsliver-title + descriptionText: objective-condition-steal-smsliver-description \ No newline at end of file diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 9170a2076d..b7fa0fa2b6 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -1100,9 +1100,6 @@ - type: Tag id: SmallMech -- type: Tag - id: SMImmune - - type: Tag id: Smokable diff --git a/Resources/ServerInfo/Guidebook/Engineering/Supermatter.xml b/Resources/ServerInfo/Guidebook/Engineering/Supermatter.xml new file mode 100644 index 0000000000..ab67ce41c6 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/Engineering/Supermatter.xml @@ -0,0 +1,65 @@ + + + + + # The Supermatter Engine + + So you've decided to take on the challenge and set up the Supermatter Engine? First, let's give you a short overview of the main Supermatter crystal beforehand. + + Its primary features are emitting electrical arcs that are harnessed to power the station through tesla coils. + + Side effects include radiation emission, releasing hot oxygen and plasma, heating the air around, and exploding, transforming into a black hole or an energy ball and eating the entire station if you screw up hard enough. + + It begins inert but being hit by an object or a projectile will activate it and it'll start exhibiting nearly all of the aforementioned properties. + + ## Words of Warning + + 1. The Supermatter crystal is [color=red]VERY DANGEROUS[/color]. Activating the crystal should be the last step in setting up any form of Supermatter based power! + + 2. [color=red]PUT YOUR GOD DAMN RADIATION SUIT ON[/color]!! + + 3. Most of setting up the Supermatter involves a gas loop that is designed to cool down the Supermatter chamber. Please have at least some knowledge of gases and their atmospheric properties. + + 4. Anything that bumps into the Supermatter is [color=red]fundamentally annihilated[/color]. [color=red]Do not touch it[/color]. This means weld and bolt the door to the chamber. + + ## Gas Interactions + + Here's a list of all gases from least dangerous to most dangerous. + + 1. [color=#bffffe]Frezon[/color]. Aside from cooling down the Supermatter, it basically stops power and waste production, which may come handy if the Supermatter is close to delaminating and you need to shut it down fast. + + 2. [color=#c20000]Nitrogen[/color]. N2 is the basic gas most Supermatter setups will run exclusively, being bog simple to set up for. It dampens the power generation from heat, and reduces the amount of plasma the SM belches out, making it good for when you aren't trying to do something silly. + + 3. [color=#b16d6d]Nitrous oxide[/color]. Reinforces the heat resistance of the crystal, allowing for much hotter setups than usual. Hovewer, at high temperatures it will decompose into Nitrogen and Oxygen. While N2 is good, O2 certainly is not. This O2 will also react with the Plasma to create Tritium and then... a Tritium fire. + + 4. [color=#62d5ca]Oxygen[/color]. Provides a boost to power transmission without actively increasing the waste gas amount or temperature. Pretty risky to use, as any disruption of the cooling loop will soon cause a plasma fire in the crystal chamber. Even just a high concentration of O2 will activate and continuously power the crystal. + + 5. [color=#19b348]Ammonia[/color]. Increases the power generation slightly at a minor cost to the heat penalty. + + 6. [color=#979797]Carbon Dioxide[/color]. In low concentrations, it will increase the crystal's power generation. In high concentrations it will raise the crystal's energy to extremely high levels. With poor management and insufficient or downright bad preparation, it will eventually exceed safe energy levels and begin a charge delamination, producing electric arcs and anomalies until it eventually explodes into a Tesla ball. + + [color=red]7[/color]. [color=#ff9d00]Plasma[/color]. Very similar to Oxygen but provides a higher power boost as well as a much higher waste and heat penalty. The extreme pressures and volumes of gas produced by this gas are very likely to clog pipes and overheat the chamber. + + [color=red]8[/color]. [color=#08a800]Tritium[/color]. Increases the power production of the Supermatter by up to 3 times, there is one slight issue with it. It is dangerous. It is very dangerous. Tritium is a horrifyingly irritable and jumpy gas. While it isn't as harmful to the heat level as Plasma is (just barely), it also has the second worst heat capacity of all gasses while Plasma has the second highest. This means that Plasma can be kept happy with enough cooling, whereas Tritium eagerly goes from a safe space loop into a burning hellfire. Add to this the byproduct of large amounts of Oxygen production (not exclusive to Tritium. An issue in a Plasma engine too), and you have a tritium fire and a very hot crystal. Do not use this gas unless you have a very strong understanding of atmospherics and the Supermatter, and are willing to get creative. + + ## Practical guide to the Supermatter + + Now, forget about everything you've just read and get to setting up the most basic loop there is: the Nitrogen loop. + + The atmospheric setup in it's most basic form should look like this: + + (We did not have enough budget for images, here is a text representation) + + 1. Nitrogen gets pumped into the chamber by passive vents from one side + + 2. Every gas gets pumped out of the chamber by using scrubbers set on Siphon on the other side. + + 3. The output gets cooled down, filtered and excess nitrogen gets either outted into space or rerouted into the input. + + That's basically it. I hope you understand at least something in this example. Now get to it! + + ## Experiment + + You're not a real engineer if you haven't figured out the most efficient way to produce electricity using the Supermatter crystal, are you? + + \ No newline at end of file diff --git a/Resources/Textures/Supermatter/supermatter.rsi/meta.json b/Resources/Textures/Supermatter/supermatter.rsi/meta.json index 3c25e1a830..d0a000ae2b 100644 --- a/Resources/Textures/Supermatter/supermatter.rsi/meta.json +++ b/Resources/Textures/Supermatter/supermatter.rsi/meta.json @@ -1,6 +1,6 @@ { "version": 1, - "copyright": "Taken from https://github.com/tgstation/tgstation/blob/master/icons/obj/supermatter.dmi", + "copyright": "Taken and edited from https://tgstation13.org/wiki/images/a/a4/Supermatter-bg.gif", "license": "CC-BY-SA-3.0", "size": { "x": 32, @@ -8,7 +8,14 @@ }, "states": [ { - "name": "supermatter" + "name": "supermatter", + "delays": [ + [ + 0.08, + 0.08, + 0.08 + ] + ] } ] } diff --git a/Resources/Textures/Supermatter/supermatter.rsi/supermatter.png b/Resources/Textures/Supermatter/supermatter.rsi/supermatter.png index b8fa4defeb..0c5747a315 100644 Binary files a/Resources/Textures/Supermatter/supermatter.rsi/supermatter.png and b/Resources/Textures/Supermatter/supermatter.rsi/supermatter.png differ diff --git a/Resources/Textures/Supermatter/supermatter_sliver.rsi/icon.png b/Resources/Textures/Supermatter/supermatter_sliver.rsi/icon.png new file mode 100644 index 0000000000..2187706b10 Binary files /dev/null and b/Resources/Textures/Supermatter/supermatter_sliver.rsi/icon.png differ diff --git a/Resources/Textures/Supermatter/supermatter_sliver.rsi/meta.json b/Resources/Textures/Supermatter/supermatter_sliver.rsi/meta.json new file mode 100644 index 0000000000..f157223291 --- /dev/null +++ b/Resources/Textures/Supermatter/supermatter_sliver.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "copyright": "Taken and edited from https://github.com/tgstation/tgstation/blob/master/icons/obj/antags/syndicate_tools.dmi", + "license": "CC-BY-SA-3.0", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + } + ] +} \ No newline at end of file