XenoArch [Science Overhaul] (#12204)

* multi-node xeno artifacts

* refactor existing artifact effects

* more tweaks to generation

* more shit plus fix tests

* more generation stuff plus threat levels

* doink

* now make it build

* defer the artifact activation to not cause errors

also pricing

* some changes

* all of the yaml + ui stuff for artifact analyzer

* machine linking and starting to make the ui functional

* artifact analyzer display

* a shit ton of artifact analyzer stuff

* more changes; making destroy work properly; progress bar tweaks

* getting shit going!

ALL RIGHT

* small tweaks that didn't help much

* Komm susser todd: the end of analysis

* recipes and hints and ui, oh my!

* add some in-game sources

gotta prepare for day 1 launch

* node data + ditch random seed in place of id

* bunch of triggers

* finish off the last few triggers

* implement machine examine verb

* knock, flicker, blink, throw

* shatter, foam, shuffle, heat

* fix all the shit i broke

* *some* of these have to be good, no?

25 effects

* callin' it there for effects

* comments + reword some trigger hints

* don't mind this little commit here

* byref event

* fix brokey node entry

* fix low pressure trigger

* mirror review plus fixing 0x40's bug

also the throw artifact threw incorrectly

* randomize the event message a teeny bit
This commit is contained in:
Nemanja
2022-11-06 18:05:44 -05:00
committed by GitHub
parent 0d4a605a94
commit 273e0968e4
107 changed files with 3321 additions and 358 deletions

View File

@@ -0,0 +1,44 @@
using Content.Shared.Damage;
using Content.Shared.Whitelist;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
/// When activated, damages nearby entities.
/// </summary>
[RegisterComponent]
public sealed class DamageNearbyArtifactComponent : Component
{
/// <summary>
/// The radius of entities that will be affected
/// </summary>
[DataField("radius")]
public float Radius = 3f;
/// <summary>
/// A whitelist for filtering certain damage.
/// </summary>
/// <remarks>
/// TODO: The component portion, since it uses an array, does not work currently.
/// </remarks>
[DataField("whitelist")]
public EntityWhitelist? Whitelist;
/// <summary>
/// The damage that is applied
/// </summary>
[DataField("damage", required: true)]
public DamageSpecifier Damage = default!;
/// <summary>
/// The chance that damage is applied to each individual entity
/// </summary>
[DataField("damageChance")]
public float DamageChance = 1f;
/// <summary>
/// Whether or not this should ignore resistances for the damage
/// </summary>
[DataField("ignoreResistances")]
public bool IgnoreResistances;
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.Disease;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
@@ -8,10 +9,15 @@ namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
public sealed class DiseaseArtifactComponent : Component
{
/// <summary>
/// Disease the artifact will spawn
/// If empty, picks a random one from its list
/// The diseases that the artifact can use.
/// </summary>
[DataField("diseasePrototype", customTypeSerializer: typeof(PrototypeIdListSerializer<DiseasePrototype>))]
public List<string> DiseasePrototypes = new();
/// <summary>
/// Disease the artifact will spawn
/// Picks a random one from its list
/// </summary>
[DataField("disease")]
[ViewVariables(VVAccess.ReadWrite)]
public DiseasePrototype? SpawnDisease;
@@ -19,7 +25,6 @@ public sealed class DiseaseArtifactComponent : Component
/// How far away it will check for people
/// If empty, picks a random one from its list
/// </summary>
[DataField("range")]
[ViewVariables(VVAccess.ReadWrite)]
[DataField("range"), ViewVariables(VVAccess.ReadWrite)]
public float Range = 5f;
}

View File

@@ -0,0 +1,54 @@
using Content.Shared.Chemistry.Reagent;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
/// Generates foam from the artifact when activated
/// </summary>
[RegisterComponent]
public sealed class FoamArtifactComponent : Component
{
/// <summary>
/// The list of reagents that will randomly be picked from
/// to choose the foam reagent
/// </summary>
[DataField("reagents", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer<ReagentPrototype>))]
public List<string> Reagents = new();
/// <summary>
/// The foam reagent
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public string? SelectedReagent;
/// <summary>
/// How long does the foam last?
/// </summary>
[DataField("duration")]
public float Duration = 10;
/// <summary>
/// How much reagent is in the foam?
/// </summary>
[DataField("reagentAmount")]
public float ReagentAmount = 100;
/// <summary>
/// Minimum radius of foam spawned
/// </summary>
[DataField("minFoamAmount")]
public int MinFoamAmount = 2;
/// <summary>
/// Maximum radius of foam spawned
/// </summary>
[DataField("maxFoamAmount")]
public int MaxFoamAmount = 6;
/// <summary>
/// How long it takes for each tile of foam to spawn
/// </summary>
[DataField("spreadDuration")]
public float SpreadDuration = 1;
}

View File

@@ -20,7 +20,7 @@ public sealed class GasArtifactComponent : Component
/// List of possible activation gases to pick on startup.
/// </summary>
[DataField("possibleGas")]
public Gas[] PossibleGases =
public List<Gas> PossibleGases = new()
{
Gas.Oxygen,
Gas.Plasma,

View File

@@ -0,0 +1,14 @@
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
/// This is used for using the "knock" spell when the artifact is activated
/// </summary>
[RegisterComponent]
public sealed class KnockArtifactComponent : Component
{
/// <summary>
/// The range of the spell
/// </summary>
[DataField("knockRange")]
public float KnockRange = 4f;
}

View File

@@ -0,0 +1,20 @@
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
/// Flickers all the lights within a certain radius.
/// </summary>
[RegisterComponent]
public sealed class LightFlickerArtifactComponent : Component
{
/// <summary>
/// Lights within this radius will be flickered on activation
/// </summary>
[DataField("radius")]
public float Radius = 4;
/// <summary>
/// The chance that the light will flicker
/// </summary>
[DataField("flickerChance")]
public float FlickerChance = 0.75f;
}

View File

@@ -1,17 +0,0 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
/// Spawn RadiationPulse when artifact activated.
/// </summary>
[RegisterComponent]
public sealed class RadiateArtifactComponent : Component
{
/// <summary>
/// Radiation pulse prototype to spawn.
/// </summary>
[DataField("pulsePrototype", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string PulsePrototype = "RadiationPulse";
}

View File

@@ -0,0 +1,15 @@
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
/// When activated, will teleport the artifact
/// to a random position within a certain radius
/// </summary>
[RegisterComponent]
public sealed class RandomTeleportArtifactComponent : Component
{
/// <summary>
/// The max distance that the artifact will teleport.
/// </summary>
[DataField("range")]
public float Range = 7.5f;
}

View File

@@ -0,0 +1,12 @@
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
/// When activated, will shuffle the position of all players
/// within a certain radius.
/// </summary>
[RegisterComponent]
public sealed class ShuffleArtifactComponent : Component
{
[DataField("radius")]
public float Radius = 7.5f;
}

View File

@@ -11,21 +11,35 @@ namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
[RegisterComponent]
public sealed class SpawnArtifactComponent : Component
{
[DataField("random")]
public bool RandomPrototype = true;
/// <summary>
/// The list of possible prototypes to spawn that it picks from.
/// </summary>
[DataField("possiblePrototypes", customTypeSerializer:typeof(PrototypeIdListSerializer<EntityPrototype>))]
public List<string> PossiblePrototypes = new();
/// <summary>
/// The prototype it selected to spawn.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("prototype", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? Prototype;
/// <summary>
/// The range around the artifact that it will spawn the entity
/// </summary>
[DataField("range")]
public float Range = 0.5f;
/// <summary>
/// The maximum number of times the spawn will occur
/// </summary>
[DataField("maxSpawns")]
public int MaxSpawns = 20;
public int SpawnsCount = 0;
/// <summary>
/// Whether or not the artifact spawns the same entity every time
/// or picks through the list each time.
/// </summary>
[DataField("consistentSpawn")]
public bool ConsistentSpawn = true;
}

View File

@@ -13,7 +13,7 @@ public sealed class TelepathicArtifactComponent : Component
/// </summary>
[DataField("messages")]
[ViewVariables(VVAccess.ReadWrite)]
public string[] Messages = default!;
public List<string> Messages = default!;
/// <summary>
/// Loc string ids of telepathic messages (spooky version).
@@ -21,7 +21,7 @@ public sealed class TelepathicArtifactComponent : Component
/// </summary>
[DataField("drastic")]
[ViewVariables(VVAccess.ReadWrite)]
public string[] DrasticMessages = default!;
public List<string>? DrasticMessages;
/// <summary>
/// Probability to pick drastic version of message.

View File

@@ -8,7 +8,7 @@ namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
[RegisterComponent]
public sealed class TemperatureArtifactComponent : Component
{
[DataField("targetTemp")]
[DataField("targetTemp"), ViewVariables(VVAccess.ReadWrite)]
public float TargetTemperature = Atmospherics.T0C;
[DataField("spawnTemp")]
@@ -21,6 +21,6 @@ public sealed class TemperatureArtifactComponent : Component
/// If true, artifact will heat/cool not only its current tile, but surrounding tiles too.
/// This will change room temperature much faster.
/// </summary>
[DataField("effectAdjacent")]
public bool EffectAdjacentTiles = true;
[DataField("affectAdjacent")]
public bool AffectAdjacentTiles = true;
}

View File

@@ -0,0 +1,27 @@
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
/// Throws all nearby entities backwards.
/// Also pries nearby tiles.
/// </summary>
[RegisterComponent]
public sealed class ThrowArtifactComponent : Component
{
/// <summary>
/// How close do you have to be to get yeeted?
/// </summary>
[DataField("range")]
public float Range = 2f;
/// <summary>
/// How likely is it that an individual tile will get pried?
/// </summary>
[DataField("tilePryChance")]
public float TilePryChance = 0.5f;
/// <summary>
/// How strongly does stuff get thrown?
/// </summary>
[DataField("throwStrength"), ViewVariables(VVAccess.ReadWrite)]
public float ThrowStrength = 5f;
}

View File

@@ -0,0 +1,36 @@
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Damage;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public sealed class BreakWindowArtifactSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<DamageNearbyArtifactComponent, ArtifactActivatedEvent>(OnActivated);
}
private void OnActivated(EntityUid uid, DamageNearbyArtifactComponent component, ArtifactActivatedEvent args)
{
var ents = _lookup.GetEntitiesInRange(uid, component.Radius);
if (args.Activator != null)
ents.Add(args.Activator.Value);
foreach (var ent in ents)
{
if (component.Whitelist != null && !component.Whitelist.IsValid(ent))
continue;
if (!_random.Prob(component.DamageChance))
return;
_damageable.TryChangeDamage(ent, component.Damage, component.IgnoreResistances);
}
}
}

View File

@@ -1,9 +1,9 @@
using System.Linq;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Shared.Disease;
using Content.Server.Disease;
using Content.Server.Disease.Components;
using Robust.Shared.Random;
using Robust.Shared.Prototypes;
using Content.Shared.Interaction;
@@ -15,38 +15,25 @@ namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems
public sealed class DiseaseArtifactSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly DiseaseSystem _disease = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
// TODO: YAML Serializer won't catch this.
[ViewVariables(VVAccess.ReadWrite)]
public readonly IReadOnlyList<string> ArtifactDiseases = new[]
{
"VanAusdallsRobovirus",
"OwOnavirus",
"BleedersBite",
"Ultragigacancer",
"MemeticAmirmir",
"TongueTwister",
"AMIV"
};
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DiseaseArtifactComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<DiseaseArtifactComponent, ArtifactNodeEnteredEvent>(OnNodeEntered);
SubscribeLocalEvent<DiseaseArtifactComponent, ArtifactActivatedEvent>(OnActivate);
}
/// <summary>
/// Makes sure this artifact is assigned a disease
/// </summary>
private void OnMapInit(EntityUid uid, DiseaseArtifactComponent component, MapInitEvent args)
private void OnNodeEntered(EntityUid uid, DiseaseArtifactComponent component, ArtifactNodeEnteredEvent args)
{
if (component.SpawnDisease != null || ArtifactDiseases.Count == 0) return;
var diseaseName = _random.Pick(ArtifactDiseases);
if (component.SpawnDisease != null || !component.DiseasePrototypes.Any())
return;
var diseaseName = component.DiseasePrototypes[args.RandomSeed % component.DiseasePrototypes.Count];
if (!_prototypeManager.TryIndex<DiseasePrototype>(diseaseName, out var disease))
{

View File

@@ -0,0 +1,42 @@
using System.Linq;
using Content.Server.Chemistry.ReactionEffects;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Chemistry.Components;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public sealed class FoamArtifactSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<FoamArtifactComponent, ArtifactNodeEnteredEvent>(OnNodeEntered);
SubscribeLocalEvent<FoamArtifactComponent, ArtifactActivatedEvent>(OnActivated);
}
private void OnNodeEntered(EntityUid uid, FoamArtifactComponent component, ArtifactNodeEnteredEvent args)
{
if (!component.Reagents.Any())
return;
component.SelectedReagent = component.Reagents[args.RandomSeed % component.Reagents.Count];
}
private void OnActivated(EntityUid uid, FoamArtifactComponent component, ArtifactActivatedEvent args)
{
if (component.SelectedReagent == null)
return;
var sol = new Solution();
var xform = Transform(uid);
sol.AddReagent(component.SelectedReagent, component.ReagentAmount);
FoamAreaReactionEffect.SpawnFoam("Foam", xform.Coordinates, sol,
_random.Next(component.MinFoamAmount, component.MaxFoamAmount), component.Duration,
component.SpreadDuration, component.SpreadDuration, entityManager: EntityManager);
}
}

View File

@@ -2,33 +2,32 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public sealed class GasArtifactSystem : EntitySystem
{
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GasArtifactComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<GasArtifactComponent, ArtifactNodeEnteredEvent>(OnNodeEntered);
SubscribeLocalEvent<GasArtifactComponent, ArtifactActivatedEvent>(OnActivate);
}
private void OnMapInit(EntityUid uid, GasArtifactComponent component, MapInitEvent args)
private void OnNodeEntered(EntityUid uid, GasArtifactComponent component, ArtifactNodeEnteredEvent args)
{
if (component.SpawnGas == null && component.PossibleGases.Length != 0)
if (component.SpawnGas == null && component.PossibleGases.Count != 0)
{
var gas = _random.Pick(component.PossibleGases);
var gas = component.PossibleGases[args.RandomSeed % component.PossibleGases.Count];
component.SpawnGas = gas;
}
if (component.SpawnTemperature == null)
{
var temp = _random.NextFloat(component.MinRandomTemperature, component.MaxRandomTemperature);
var temp = args.RandomSeed % component.MaxRandomTemperature - component.MinRandomTemperature +
component.MinRandomTemperature;
component.SpawnTemperature = temp;
}
}
@@ -38,8 +37,6 @@ public sealed class GasArtifactSystem : EntitySystem
if (component.SpawnGas == null || component.SpawnTemperature == null)
return;
var transform = Transform(uid);
var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);
if (environment == null)
return;

View File

@@ -0,0 +1,24 @@
using Content.Server.Magic.Events;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public sealed class KnockArtifactSystem : EntitySystem
{
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<KnockArtifactComponent, ArtifactActivatedEvent>(OnActivated);
}
private void OnActivated(EntityUid uid, KnockArtifactComponent component, ArtifactActivatedEvent args)
{
var ev = new KnockSpellEvent
{
Performer = uid,
Range = component.KnockRange
};
RaiseLocalEvent(ev);
}
}

View File

@@ -0,0 +1,38 @@
using Content.Server.Ghost;
using Content.Server.Light.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
/// <summary>
/// This handles...
/// </summary>
public sealed class LightFlickerArtifactSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly GhostSystem _ghost = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<LightFlickerArtifactComponent, ArtifactActivatedEvent>(OnActivated);
}
private void OnActivated(EntityUid uid, LightFlickerArtifactComponent component, ArtifactActivatedEvent args)
{
var lights = GetEntityQuery<PoweredLightComponent>();
foreach (var light in _lookup.GetEntitiesInRange(uid, component.Radius, LookupFlags.StaticSundries ))
{
if (!lights.HasComponent(light))
continue;
if (!_random.Prob(component.FlickerChance))
continue;
_ghost.DoGhostBooEvent(light);
}
}
}

View File

@@ -1,20 +0,0 @@
using Content.Server.Radiation;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public sealed class RadiateArtifactSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RadiateArtifactComponent, ArtifactActivatedEvent>(OnActivate);
}
private void OnActivate(EntityUid uid, RadiateArtifactComponent component, ArtifactActivatedEvent args)
{
var transform = Transform(uid);
EntityManager.SpawnEntity(component.PulsePrototype, transform.Coordinates);
}
}

View File

@@ -0,0 +1,30 @@
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Popups;
using Robust.Shared.Player;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
/// <summary>
/// This handles...
/// </summary>
public sealed class RandomTeleportArtifactSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<RandomTeleportArtifactComponent, ArtifactActivatedEvent>(OnActivate);
}
private void OnActivate(EntityUid uid, RandomTeleportArtifactComponent component, ArtifactActivatedEvent args)
{
var xform = Transform(uid);
_popup.PopupCoordinates(Loc.GetString("blink-artifact-popup"), xform.Coordinates, Filter.Pvs(uid), PopupType.Medium);
xform.Coordinates = xform.Coordinates.Offset(_random.NextVector2(component.Range));
}
}

View File

@@ -0,0 +1,43 @@
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.MobState.Components;
using Robust.Shared.Map;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public sealed class ShuffleArtifactSystem : EntitySystem
{
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly IRobustRandom _random = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<ShuffleArtifactComponent, ArtifactActivatedEvent>(OnActivated);
}
private void OnActivated(EntityUid uid, ShuffleArtifactComponent component, ArtifactActivatedEvent args)
{
var mobState = GetEntityQuery<MobStateComponent>();
List<EntityCoordinates> allCoords = new();
List<TransformComponent> toShuffle = new();
foreach (var ent in _lookup.GetEntitiesInRange(uid, component.Radius, LookupFlags.Dynamic | LookupFlags.Sundries))
{
if (!mobState.HasComponent(ent))
continue;
var xform = Transform(ent);
toShuffle.Add(xform);
allCoords.Add(xform.Coordinates);
}
foreach (var xform in toShuffle)
{
xform.Coordinates = _random.PickAndTake(allCoords);
}
}
}

View File

@@ -9,25 +9,40 @@ public sealed class SpawnArtifactSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly ArtifactSystem _artifact = default!;
public const string NodeDataSpawnAmount = "nodeDataSpawnAmount";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SpawnArtifactComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<SpawnArtifactComponent, ArtifactNodeEnteredEvent>(OnNodeEntered);
SubscribeLocalEvent<SpawnArtifactComponent, ArtifactActivatedEvent>(OnActivate);
}
private void OnMapInit(EntityUid uid, SpawnArtifactComponent component, MapInitEvent args)
private void OnNodeEntered(EntityUid uid, SpawnArtifactComponent component, ArtifactNodeEnteredEvent args)
{
ChooseRandomPrototype(uid, component);
if (component.PossiblePrototypes.Count == 0)
return;
var proto = component.PossiblePrototypes[args.RandomSeed % component.PossiblePrototypes.Count];
component.Prototype = proto;
}
private void OnActivate(EntityUid uid, SpawnArtifactComponent component, ArtifactActivatedEvent args)
{
if (component.Prototype == null)
return;
if (component.SpawnsCount >= component.MaxSpawns)
if (!_artifact.TryGetNodeData(uid, NodeDataSpawnAmount, out int amount))
amount = 0;
if (amount >= component.MaxSpawns)
return;
var toSpawn = component.Prototype;
if (!component.ConsistentSpawn)
toSpawn = _random.Pick(component.PossiblePrototypes);
// select spawn position near artifact
var artifactCord = Transform(uid).Coordinates;
var dx = _random.NextFloat(-component.Range, component.Range);
@@ -35,25 +50,11 @@ public sealed class SpawnArtifactSystem : EntitySystem
var spawnCord = artifactCord.Offset(new Vector2(dx, dy));
// spawn entity
var spawned = EntityManager.SpawnEntity(component.Prototype, spawnCord);
component.SpawnsCount++;
var spawned = EntityManager.SpawnEntity(toSpawn, spawnCord);
_artifact.SetNodeData(uid, NodeDataSpawnAmount, amount+1);
// if there is an user - try to put spawned item in their hands
// doesn't work for spawners
_handsSystem.PickupOrDrop(args.Activator, spawned);
}
private void ChooseRandomPrototype(EntityUid uid, SpawnArtifactComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
if (!component.RandomPrototype)
return;
if (component.PossiblePrototypes.Count == 0)
return;
var proto = _random.Pick(component.PossiblePrototypes);
component.Prototype = proto;
}
}

View File

@@ -29,8 +29,15 @@ public sealed class TelepathicArtifactSystem : EntitySystem
continue;
// roll if msg should be usual or drastic
var isDrastic = _random.NextFloat() <= component.DrasticMessageProb;
var msgArr = isDrastic ? component.DrasticMessages : component.Messages;
List<string> msgArr;
if (_random.NextFloat() <= component.DrasticMessageProb && component.DrasticMessages != null)
{
msgArr = component.DrasticMessages;
}
else
{
msgArr = component.Messages;
}
// pick a random message
var msgId = _random.Pick(msgArr);

View File

@@ -26,7 +26,7 @@ public sealed class TemperatureArtifactSystem : EntitySystem
return;
UpdateTileTemperature(component, center);
if (component.EffectAdjacentTiles && transform.GridUid != null)
if (component.AffectAdjacentTiles && transform.GridUid != null)
{
var adjacent = _atmosphereSystem.GetAdjacentTileMixtures(transform.GridUid.Value,
_transformSystem.GetGridOrMapTilePosition(uid, transform), excite: true);

View File

@@ -0,0 +1,49 @@
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Maps;
using Content.Shared.Throwing;
using Robust.Shared.Map;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public sealed class ThrowArtifactSystem : EntitySystem
{
[Dependency] private readonly IMapManager _map = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly ThrowingSystem _throwing = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<ThrowArtifactComponent, ArtifactActivatedEvent>(OnActivated);
}
private void OnActivated(EntityUid uid, ThrowArtifactComponent component, ArtifactActivatedEvent args)
{
var xform = Transform(uid);
if (_map.TryGetGrid(xform.GridUid, out var grid))
{
var tiles = grid.GetTilesIntersecting(
Box2.CenteredAround(xform.WorldPosition, (component.Range*2, component.Range)));
foreach (var tile in tiles)
{
if (!_random.Prob(component.TilePryChance))
continue;
tile.PryTile();
}
}
var lookup = _lookup.GetEntitiesInRange(uid, component.Range, LookupFlags.Dynamic | LookupFlags.Sundries);
foreach (var ent in lookup)
{
var tempXform = Transform(ent);
var foo = tempXform.MapPosition.Position - xform.MapPosition.Position;
_throwing.TryThrow(ent, foo*2, component.ThrowStrength, uid, 0);
}
}
}