Xenoarchaeology artifacts (#6069)

Co-authored-by: Alexander Evgrashin <evgrashin.adl@gmail.com>
This commit is contained in:
Alex Evgrashin
2022-01-22 15:55:11 +03:00
committed by GitHub
parent 90a5c6ea54
commit 8d6565ea42
50 changed files with 1016 additions and 1 deletions

View File

@@ -0,0 +1,37 @@
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Xenoarchaeology.XenoArtifacts;
[RegisterComponent]
public class ArtifactComponent : Component
{
public override string Name => "Artifact";
/// <summary>
/// Should artifact pick a random trigger on startup?
/// </summary>
[DataField("randomTrigger")]
public bool RandomTrigger = true;
/// <summary>
/// List of all possible triggers activations.
/// Should be same as components names.
/// </summary>
[DataField("possibleTriggers")]
public string[] PossibleTriggers = {
"ArtifactInteractionTrigger",
"ArtifactGasTrigger"
};
/// <summary>
/// Cooldown time between artifact activations (in seconds).
/// </summary>
[DataField("timer")]
[ViewVariables(VVAccess.ReadWrite)]
public double CooldownTime = 10;
public TimeSpan LastActivationTime;
}

View File

@@ -0,0 +1,70 @@
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server.Xenoarchaeology.XenoArtifacts;
public class ArtifactSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IComponentFactory _componentFactory = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ArtifactComponent, ComponentInit>(OnInit);
}
private void OnInit(EntityUid uid, ArtifactComponent component, ComponentInit args)
{
if (component.RandomTrigger)
{
AddRandomTrigger(uid, component);
}
}
public void AddRandomTrigger(EntityUid uid, ArtifactComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
var triggerName = _random.Pick(component.PossibleTriggers);
var trigger = (Component) _componentFactory.GetComponent(triggerName);
trigger.Owner = uid;
EntityManager.AddComponent(uid, trigger);
}
public bool TryActivateArtifact(EntityUid uid, EntityUid? user = null,
ArtifactComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
// check if artifact isn't under cooldown
var timeDif = _gameTiming.CurTime - component.LastActivationTime;
if (timeDif.TotalSeconds < component.CooldownTime)
return false;
ForceActivateArtifact(uid, user, component);
return true;
}
public void ForceActivateArtifact(EntityUid uid, EntityUid? user = null,
ArtifactComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
component.LastActivationTime = _gameTiming.CurTime;
var ev = new ArtifactActivatedEvent()
{
Activator = user
};
RaiseLocalEvent(uid, ev);
}
}

View File

@@ -0,0 +1,37 @@
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
using Robust.Shared.ViewVariables;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
/// When activated artifact will spawn an entity from prototype.
/// It could be an angry mob or some random item.
/// </summary>
[RegisterComponent]
public class SpawnArtifactComponent : Component
{
public override string Name => "SpawnArtifact";
[DataField("random")]
public bool RandomPrototype = true;
[DataField("possiblePrototypes", customTypeSerializer:typeof(PrototypeIdListSerializer<EntityPrototype>))]
public List<string> PossiblePrototypes = new();
[ViewVariables(VVAccess.ReadWrite)]
[DataField("prototype", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? Prototype;
[DataField("range")]
public float Range = 0.5f;
[DataField("maxSpawns")]
public int MaxSpawns = 20;
public int SpawnsCount = 0;
}

View File

@@ -0,0 +1,45 @@
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
/// <summary>
/// Harmless artifact that broadcast "thoughts" to players nearby.
/// Thoughts are shown as popups and unique for each player.
/// </summary>
[RegisterComponent]
public class TelepathicArtifactComponent : Component
{
public override string Name => "TelepathicArtifact";
/// <summary>
/// Loc string ids of telepathic messages.
/// Will be randomly picked and shown to player.
/// </summary>
[DataField("messages")]
[ViewVariables(VVAccess.ReadWrite)]
public string[] Messages = default!;
/// <summary>
/// Loc string ids of telepathic messages (spooky version).
/// Will be randomly picked and shown to player.
/// </summary>
[DataField("drastic")]
[ViewVariables(VVAccess.ReadWrite)]
public string[] DrasticMessages = default!;
/// <summary>
/// Probability to pick drastic version of message.
/// </summary>
[DataField("drasticProb")]
[ViewVariables(VVAccess.ReadWrite)]
public float DrasticMessageProb = 0.2f;
/// <summary>
/// Radius in which player can receive artifacts messages.
/// </summary>
[DataField("range")]
[ViewVariables(VVAccess.ReadWrite)]
public float Range = 10f;
}

View File

@@ -0,0 +1,67 @@
using Content.Server.Clothing.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Hands.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public class SpawnArtifactSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SpawnArtifactComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<SpawnArtifactComponent, ArtifactActivatedEvent>(OnActivate);
}
private void OnInit(EntityUid uid, SpawnArtifactComponent component, ComponentInit args)
{
ChooseRandomPrototype(uid, component);
}
private void OnActivate(EntityUid uid, SpawnArtifactComponent component, ArtifactActivatedEvent args)
{
if (component.Prototype == null)
return;
if (component.SpawnsCount >= component.MaxSpawns)
return;
// select spawn position near artifact
var artifactCord = Transform(uid).Coordinates;
var dx = _random.NextFloat(-component.Range, component.Range);
var dy = _random.NextFloat(-component.Range, component.Range);
var spawnCord = artifactCord.Offset(new Vector2(dx, dy));
// spawn entity
var spawned = EntityManager.SpawnEntity(component.Prototype, spawnCord);
component.SpawnsCount++;
// if there is an user - try to put spawned item in their hands
// doesn't work for spawners
if (args.Activator != null &&
EntityManager.TryGetComponent(args.Activator.Value, out SharedHandsComponent? hands) &&
EntityManager.HasComponent<ItemComponent>(spawned))
{
hands.TryPutInAnyHand(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

@@ -0,0 +1,46 @@
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Popups;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Player;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public class TelepathicArtifactSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IEntityLookup _lookup = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TelepathicArtifactComponent, ArtifactActivatedEvent>(OnActivate);
}
private void OnActivate(EntityUid uid, TelepathicArtifactComponent component, ArtifactActivatedEvent args)
{
// try to find victims nearby
var victims = _lookup.GetEntitiesInRange(uid, component.Range);
foreach (var victimUid in victims)
{
if (!EntityManager.HasComponent<ActorComponent>(victimUid))
continue;
// roll if msg should be usual or drastic
var isDrastic = _random.NextFloat() <= component.DrasticMessageProb;
var msgArr = isDrastic ? component.DrasticMessages : component.Messages;
// pick a random message
var msgId = _random.Pick(msgArr);
var msg = Loc.GetString(msgId);
// show it as a popup, but only for the victim
_popupSystem.PopupEntity(msg, victimUid, Filter.Entities(victimUid));
}
}
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.GameObjects;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Events;
/// <summary>
/// Invokes when artifact was successfully activated.
/// Used to start attached effects.
/// </summary>
public class ArtifactActivatedEvent : EntityEventArgs
{
/// <summary>
/// Entity that activate this artifact.
/// Usually player, but can also be another object.
/// </summary>
public EntityUid? Activator;
}

View File

@@ -0,0 +1,22 @@
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.Xenoarchaeology.XenoArtifacts;
[RegisterComponent]
public class RandomArtifactSpriteComponent : Component
{
public override string Name => "RandomArtifactSprite";
[DataField("minSprite")]
public int MinSprite = 1;
[DataField("maxSprite")]
public int MaxSprite = 14;
[DataField("activationTime")]
public double ActivationTime = 2.0;
public TimeSpan? ActivationStart;
}

View File

@@ -0,0 +1,58 @@
using System;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Xenoarchaeology.XenoArtifacts;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server.Xenoarchaeology.XenoArtifacts;
public class RandomArtifactSpriteSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IGameTiming _time = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RandomArtifactSpriteComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<RandomArtifactSpriteComponent, ArtifactActivatedEvent>(OnActivated);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityManager.EntityQuery<RandomArtifactSpriteComponent, AppearanceComponent>();
foreach (var (component, appearance) in query)
{
if (component.ActivationStart == null)
return;
var timeDif = _time.CurTime - component.ActivationStart.Value;
if (timeDif.Seconds >= component.ActivationTime)
{
appearance.SetData(SharedArtifactsVisuals.IsActivated, false);
component.ActivationStart = null;
}
}
}
private void OnInit(EntityUid uid, RandomArtifactSpriteComponent component, ComponentInit args)
{
if (!TryComp(uid, out AppearanceComponent? appearance))
return;
var randomSprite = _random.Next(component.MinSprite, component.MaxSprite + 1);
appearance.SetData(SharedArtifactsVisuals.SpriteIndex, randomSprite);
}
private void OnActivated(EntityUid uid, RandomArtifactSpriteComponent component, ArtifactActivatedEvent args)
{
if (!TryComp(uid, out AppearanceComponent? appearance))
return;
appearance.SetData(SharedArtifactsVisuals.IsActivated, true);
component.ActivationStart = _time.CurTime;
}
}

View File

@@ -0,0 +1,41 @@
using Content.Shared.Atmos;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
/// <summary>
/// Activates artifact when it surrounded by certain gas.
/// </summary>
[RegisterComponent]
public class ArtifactGasTriggerComponent : Component
{
public override string Name => "ArtifactGasTrigger";
/// <summary>
/// List of possible activation gases to pick on startup.
/// </summary>
[DataField("possibleGas")]
public Gas[] PossibleGases =
{
Gas.Oxygen,
Gas.Plasma,
Gas.Nitrogen,
Gas.CarbonDioxide
};
/// <summary>
/// Gas id that will activate artifact.
/// </summary>
[DataField("gas")]
[ViewVariables(VVAccess.ReadWrite)]
public Gas? ActivationGas;
/// <summary>
/// How many moles of gas should be present in room to activate artifact.
/// </summary>
[DataField("moles")]
[ViewVariables(VVAccess.ReadWrite)]
public float ActivationMoles = Atmospherics.MolesCellStandard * 0.1f;
}

View File

@@ -0,0 +1,12 @@
using Robust.Shared.GameObjects;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
/// <summary>
/// Activate artifact just by touching it.
/// </summary>
[RegisterComponent]
public class ArtifactInteractionTriggerComponent : Component
{
public override string Name => "ArtifactInteractionTrigger";
}

View File

@@ -0,0 +1,51 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems;
public class ArtifactGasTriggerSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly ArtifactSystem _artifactSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ArtifactGasTriggerComponent, ComponentInit>(OnInit);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityManager.EntityQuery<ArtifactGasTriggerComponent, TransformComponent>();
foreach (var (trigger, transform) in query)
{
if (trigger.ActivationGas == null)
return;
var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates, true);
if (environment == null)
return;
// check if outside there is enough moles to activate artifact
var moles = environment.GetMoles(trigger.ActivationGas.Value);
if (moles < trigger.ActivationMoles)
return;
_artifactSystem.TryActivateArtifact(trigger.Owner);
}
}
private void OnInit(EntityUid uid, ArtifactGasTriggerComponent component, ComponentInit args)
{
if (component.ActivationGas == null)
{
var gas = _random.Pick(component.PossibleGases);
component.ActivationGas = gas;
}
}
}

View File

@@ -0,0 +1,29 @@
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
using Content.Shared.ActionBlocker;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems;
public class ArtifactInteractionTriggerSystem : EntitySystem
{
[Dependency] private readonly ArtifactSystem _artifactSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ArtifactInteractionTriggerComponent, InteractHandEvent>(OnInteract);
}
private void OnInteract(EntityUid uid, ArtifactInteractionTriggerComponent component, InteractHandEvent args)
{
if (args.Handled)
return;
if (!args.InRangeUnobstructed())
return;
args.Handled = _artifactSystem.TryActivateArtifact(uid, args.User);
}
}