Ritualizm (#474)
* ritual cucumber setup
* entities requirements
* try graph???
* Revert "try graph???"
This reverts commit c90c6353cb.
* pipyau
* fixes
* yay, it works
* spawn effect
* regex lower message restrictions
* unique speakers support
* apply entity effect ritual
* ritual chalk
* Update SpawnEntity.cs
* ritual stability
* stability event
* add guidebook description to all ritual actions
* Readability added
* Update RequiredResource.cs
* finish describer
* clean up describer
* Update triggers.ftl
* cave ambient loop
* parry sound update
* rituals end start
* magic ambience add
* global sharedization
* Update phases.yml
* daytime requirement
* Update phases.yml
* start ritual
* fixes
* more ambient work
* rritual visualizer
* end ritual
* magic orbs!
* required orbs
* orbs design
* consume orbs
* setup neutral cluster triggers and edges
* listener proxy
* restucture graph
* fix time triggers
* healing cluster
* fixes
* Create CP14RitualTest.cs
* test errors for check test
* YEEEE
* Fuck triggers, its broken now, YAY
* triggers redo
* fix
* fix test
* Update CP14RitualTest.cs
* Update neutral_cluster.yml
* Update CP14RitualSystem.Triggers.cs
* clean up, documentation
* redo triggers again
* and another one
* species sacrifice trigger
* whitelist trigger
* fix
* describer refactor
* fix memory leaking + hyperlinks
* dd
35
Content.Client/_CP14/MagicRituals/CP14ClientRitualSystem.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Server._CP14.MagicRituals;
|
||||
|
||||
public partial class CP14ClientRitualSystem : CP14SharedRitualSystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<CP14MagicRitualComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(Entity<CP14MagicRitualComponent> ent, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
if (!args.Sprite.LayerMapTryGet(ent.Comp.RitualLayerMap, out var ritualLayer))
|
||||
return;
|
||||
|
||||
if (_appearance.TryGetData<Color>(ent, RitualVisuals.Color, out var ritualColor, args.Component))
|
||||
{
|
||||
args.Sprite.LayerSetColor(ritualLayer, ritualColor);
|
||||
}
|
||||
|
||||
if (_appearance.TryGetData<bool>(ent, RitualVisuals.Enabled, out var enabled, args.Component))
|
||||
{
|
||||
args.Sprite.LayerSetVisible(ritualLayer, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Content.IntegrationTests/Tests/_CP14/CP14RitualTest.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests._CP14;
|
||||
|
||||
#nullable enable
|
||||
|
||||
[TestFixture]
|
||||
public sealed class CP14RitualTest
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// States that all edges of the ritual phase have triggers.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task RitualHasAllTriggersTest()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var compFactory = server.ResolveDependency<IComponentFactory>();
|
||||
var protoManager = server.ResolveDependency<IPrototypeManager>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
foreach (var proto in protoManager.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (!proto.TryGetComponent(out CP14MagicRitualPhaseComponent? phase, compFactory))
|
||||
continue;
|
||||
|
||||
if (phase.DeadEnd)
|
||||
{
|
||||
Assert.That(phase.Edges.Count == 0, $"{proto} is a ritual node, but has no paths to other nodes. Either add deadEnd = true, or add paths to other nodes.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.That(phase.Edges.Count > 0, $"{proto} is a deadEnd ritual node, but has {phase.Edges.Count} edges! Remove all edges, or make it a non dead-end node");
|
||||
}
|
||||
|
||||
foreach (var edge in phase.Edges)
|
||||
{
|
||||
Assert.That(edge.Triggers.Count > 0, $"{{proto}} is ritual node, but edge to {edge.Target} has no triggers and cannot be activated.");
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Shared.Speech;
|
||||
|
||||
namespace Content.Server.Speech;
|
||||
|
||||
public sealed class ListenEvent : EntityEventArgs
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Whitelist;
|
||||
|
||||
namespace Content.Server._CP14.MagicRitualTrigger;
|
||||
|
||||
|
||||
public partial class CP14RitualTriggerSystem
|
||||
{
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
|
||||
private void InitializeSacrifice()
|
||||
{
|
||||
SubscribeLocalEvent<MobStateChangedEvent>(OnMobStateChanged);
|
||||
}
|
||||
|
||||
private void OnMobStateChanged(MobStateChangedEvent ev)
|
||||
{
|
||||
if (ev.NewMobState != MobState.Dead)
|
||||
return;
|
||||
|
||||
var deathXform = Transform(ev.Target);
|
||||
|
||||
SacrificeSpecies(ev, deathXform);
|
||||
SacrificeWhitelist(ev, deathXform);
|
||||
}
|
||||
|
||||
private void SacrificeSpecies(MobStateChangedEvent ev, TransformComponent deathXform)
|
||||
{
|
||||
|
||||
if (!TryComp<HumanoidAppearanceComponent>(ev.Target, out var humanoid))
|
||||
return;
|
||||
|
||||
var query = EntityQueryEnumerator<CP14RitualSacrificeSpeciesTriggerComponent, CP14MagicRitualPhaseComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var sacrifice, out var phase, out var xform))
|
||||
{
|
||||
if (!deathXform.Coordinates.TryDistance(EntityManager, xform.Coordinates, out var distance))
|
||||
continue;
|
||||
|
||||
foreach (var trigger in sacrifice.Triggers)
|
||||
{
|
||||
if (distance > trigger.Range)
|
||||
continue;
|
||||
|
||||
if (trigger.Edge is null)
|
||||
continue;
|
||||
|
||||
if (trigger.Species != humanoid.Species)
|
||||
continue;
|
||||
|
||||
TriggerRitualPhase((uid, phase), trigger.Edge.Value.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SacrificeWhitelist(MobStateChangedEvent ev, TransformComponent deathXform)
|
||||
{
|
||||
var query = EntityQueryEnumerator<CP14RitualSacrificeWhitelistTriggerComponent, CP14MagicRitualPhaseComponent, TransformComponent>();
|
||||
while (query.MoveNext(out var uid, out var sacrifice, out var phase, out var xform))
|
||||
{
|
||||
if (!deathXform.Coordinates.TryDistance(EntityManager, xform.Coordinates, out var distance))
|
||||
continue;
|
||||
|
||||
foreach (var trigger in sacrifice.Triggers)
|
||||
{
|
||||
if (distance > trigger.Range)
|
||||
continue;
|
||||
|
||||
if (trigger.Edge is null)
|
||||
continue;
|
||||
|
||||
var entProto = MetaData(ev.Target).EntityPrototype;
|
||||
|
||||
if (entProto is null)
|
||||
continue;
|
||||
|
||||
if (!_whitelist.IsValid(trigger.Whitelist, ev.Target))
|
||||
continue;
|
||||
|
||||
TriggerRitualPhase((uid, phase), trigger.Edge.Value.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server._CP14.MagicRitualTrigger;
|
||||
|
||||
|
||||
public partial class CP14RitualTriggerSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private void InitializeTimer()
|
||||
{
|
||||
SubscribeLocalEvent<CP14RitualTimerTriggerComponent, MapInitEvent>(OnMapInit);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<CP14RitualTimerTriggerComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
foreach (var trigger in ent.Comp.Triggers)
|
||||
{
|
||||
trigger.TriggerTime = _timing.CurTime + TimeSpan.FromSeconds(trigger.Delay);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTimer(float frameTime)
|
||||
{
|
||||
var query = EntityQueryEnumerator<CP14RitualTimerTriggerComponent, CP14MagicRitualPhaseComponent>();
|
||||
while (query.MoveNext(out var uid, out var timer, out var phase))
|
||||
{
|
||||
foreach (var trigger in timer.Triggers)
|
||||
{
|
||||
if (_timing.CurTime < trigger.TriggerTime || trigger.TriggerTime == TimeSpan.Zero)
|
||||
continue;
|
||||
|
||||
if (trigger.Edge is null)
|
||||
continue;
|
||||
|
||||
TriggerRitualPhase((uid, phase), trigger.Edge.Value.Target);
|
||||
trigger.TriggerTime = TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Server.Speech;
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
|
||||
namespace Content.Server._CP14.MagicRitualTrigger;
|
||||
|
||||
public partial class CP14RitualTriggerSystem
|
||||
{
|
||||
private void InitializeVoice()
|
||||
{
|
||||
SubscribeLocalEvent<CP14RitualVoiceTriggerComponent, ListenEvent>(OnListenEvent);
|
||||
}
|
||||
|
||||
private void OnListenEvent(Entity<CP14RitualVoiceTriggerComponent> ent, ref ListenEvent args)
|
||||
{
|
||||
if (!TryComp<CP14MagicRitualPhaseComponent>(ent, out var phase))
|
||||
return;
|
||||
|
||||
// Lowercase the phrase and remove all punctuation marks
|
||||
var message = Regex.Replace(args.Message.Trim().ToLower(), @"[^\w\s]", "");
|
||||
|
||||
foreach (var trigger in ent.Comp.Triggers)
|
||||
{
|
||||
var triggerMessage = Regex.Replace(trigger.Message.ToLower(), @"[^\w\s]", "");
|
||||
|
||||
if (triggerMessage != message)
|
||||
continue;
|
||||
|
||||
if (trigger.Edge is null)
|
||||
continue;
|
||||
|
||||
TriggerRitualPhase((ent.Owner, phase), trigger.Edge.Value.Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Content.Shared._CP14.MagicRitualTrigger;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._CP14.MagicRitualTrigger;
|
||||
|
||||
public partial class CP14RitualTriggerSystem : CP14SharedRitualTriggerSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
InitializeTimer();
|
||||
InitializeVoice();
|
||||
InitializeSacrifice();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
UpdateTimer(frameTime);
|
||||
}
|
||||
|
||||
private void TriggerRitualPhase(Entity<CP14MagicRitualPhaseComponent> ent, EntProtoId nextPhase)
|
||||
{
|
||||
var evConfirmed = new CP14RitualTriggerEvent(nextPhase);
|
||||
RaiseLocalEvent(ent, evConfirmed);
|
||||
}
|
||||
}
|
||||
167
Content.Server/_CP14/MagicRituals/CP14RitualSystem.Describer.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using System.Text;
|
||||
using Content.Server._CP14.MagicRituals.Components;
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Content.Shared.Paper;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._CP14.MagicRituals;
|
||||
|
||||
public sealed partial class CP14RitualSystem
|
||||
{
|
||||
[Dependency] private readonly PaperSystem _paper = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
private void InitializeDescriber()
|
||||
{
|
||||
|
||||
SubscribeLocalEvent<CP14PaperPhaseDescriberComponent, GetVerbsEvent<Verb>>(OnDescriberVerbs);
|
||||
SubscribeLocalEvent<CP14PaperPhaseDescriberComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<CP14PaperPhaseDescriberComponent, ComponentShutdown>(OnShutdown);
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<CP14PaperPhaseDescriberComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
QueueDel(ent.Comp.CurrentPhase);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<CP14PaperPhaseDescriberComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
SetPhase(ent, ent.Comp.StartPhase);
|
||||
}
|
||||
|
||||
private void SetPhase(Entity<CP14PaperPhaseDescriberComponent> ent, EntProtoId newProto, bool saveHistory = true)
|
||||
{
|
||||
var oldPhase = ent.Comp.CurrentPhase;
|
||||
if (oldPhase is not null && saveHistory)
|
||||
{
|
||||
var oldProto = MetaData(oldPhase.Value).EntityPrototype;
|
||||
if (oldProto is not null && oldProto != newProto)
|
||||
{
|
||||
ent.Comp.SearchHistory.Add(oldProto);
|
||||
if (ent.Comp.SearchHistory.Count > 50)
|
||||
ent.Comp.SearchHistory.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
QueueDel(oldPhase);
|
||||
var newPhase = Spawn(newProto, MapCoordinates.Nullspace);
|
||||
|
||||
ent.Comp.CurrentPhase = newPhase;
|
||||
|
||||
if (!TryComp<PaperComponent>(ent, out var paper))
|
||||
return;
|
||||
|
||||
_paper.SetContent((ent, paper), GetPhaseDescription(newPhase));
|
||||
_audio.PlayPvs(ent.Comp.UseSound, ent);
|
||||
}
|
||||
|
||||
private void BackPhase(Entity<CP14PaperPhaseDescriberComponent> ent)
|
||||
{
|
||||
if (ent.Comp.SearchHistory.Count > 0)
|
||||
SetPhase(ent, ent.Comp.SearchHistory[^1], false);
|
||||
}
|
||||
|
||||
private void OnDescriberVerbs(Entity<CP14PaperPhaseDescriberComponent> ent, ref GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
if (!TryComp<CP14MagicRitualPhaseComponent>(ent.Comp.CurrentPhase, out var phase))
|
||||
return;
|
||||
|
||||
if (!TryComp<CP14MagicRitualPhaseComponent>(ent.Comp.CurrentPhase.Value, out var phaseComp))
|
||||
return;
|
||||
|
||||
foreach (var edge in phaseComp.Edges)
|
||||
{
|
||||
if (!_proto.TryIndex(edge.Target, out var indexedTarget))
|
||||
continue;
|
||||
|
||||
Verb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("cp14-ritual-describer-verb-item", ("name", indexedTarget.Name)),
|
||||
Category = VerbCategory.CP14RitualBook,
|
||||
Priority = 1,
|
||||
Act = () => SetPhase(ent, edge.Target),
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
foreach (var hyperlink in ent.Comp.Hyperlinks)
|
||||
{
|
||||
if (!_proto.TryIndex(hyperlink, out var indexedTarget))
|
||||
continue;
|
||||
|
||||
Verb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("cp14-ritual-describer-verb-hyperlink", ("name", indexedTarget.Name)),
|
||||
Category = VerbCategory.CP14RitualBook,
|
||||
Priority = 0,
|
||||
Act = () => SetPhase(ent, hyperlink),
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
if (ent.Comp.SearchHistory.Count > 0)
|
||||
{
|
||||
Verb verb = new()
|
||||
{
|
||||
Text = Loc.GetString("cp14-ritual-describer-verb-back"),
|
||||
Category = VerbCategory.CP14RitualBook,
|
||||
Priority = -1,
|
||||
Act = () => BackPhase(ent),
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetPhaseDescription(EntityUid uid)
|
||||
{
|
||||
if (!TryComp<CP14MagicRitualPhaseComponent>(uid, out var phase))
|
||||
return string.Empty;
|
||||
|
||||
return GetPhaseDescription((uid, phase));
|
||||
}
|
||||
|
||||
private string GetPhaseDescription(Entity<CP14MagicRitualPhaseComponent> ent)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append($"[color=#e6a132][head=1]{MetaData(ent).EntityName}[/head][/color] \n \n");
|
||||
sb.Append($"[italic]{MetaData(ent).EntityDescription}[/italic] \n \n");
|
||||
|
||||
sb.Append(Loc.GetString("cp14-ritual-intro") + "\n \n \n");
|
||||
foreach (var edge in ent.Comp.Edges)
|
||||
{
|
||||
if (!_proto.TryIndex(edge.Target, out var targetIndexed))
|
||||
continue;
|
||||
|
||||
sb.Append($"[color=#b5783c][head=3]{targetIndexed.Name}[/head][/color]" + "\n");
|
||||
|
||||
//TRIGGERS
|
||||
if (edge.Triggers.Count > 0)
|
||||
{
|
||||
sb.Append($"[bold]{Loc.GetString("cp14-ritual-trigger-header")}[/bold] \n");
|
||||
foreach (var trigger in edge.Triggers)
|
||||
sb.Append(trigger.GetGuidebookTriggerDescription(_proto, _entitySystem) + "\n");
|
||||
}
|
||||
|
||||
//REQUIREMENTS
|
||||
if (edge.Requirements.Count > 0)
|
||||
{
|
||||
sb.Append($"[bold]{Loc.GetString("cp14-ritual-req-header")}[/bold] \n");
|
||||
foreach (var req in edge.Requirements)
|
||||
sb.Append(req.GetGuidebookRequirementDescription(_proto, _entitySystem) + "\n");
|
||||
}
|
||||
|
||||
//ACTIONS
|
||||
if (edge.Actions.Count > 0)
|
||||
{
|
||||
sb.Append($"[bold]{Loc.GetString("cp14-ritual-effect-header")}[/bold] \n");
|
||||
foreach (var act in edge.Actions)
|
||||
sb.Append(act.GetGuidebookEffectDescription(_proto, _entitySystem) + "\n");
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server._CP14.MagicRituals;
|
||||
|
||||
public sealed partial class CP14RitualSystem
|
||||
{
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
private void InitializeVisuals()
|
||||
{
|
||||
SubscribeLocalEvent<CP14MagicRitualPhaseComponent, CP14RitualPhaseBoundEvent>(OnPhaseBound);
|
||||
}
|
||||
|
||||
private void OnPhaseBound(Entity<CP14MagicRitualPhaseComponent> ent, ref CP14RitualPhaseBoundEvent args)
|
||||
{
|
||||
if (!TryComp<CP14MagicRitualComponent>(args.Ritual, out var ritual))
|
||||
return;
|
||||
|
||||
_pointLight.SetColor(ent, ent.Comp.PhaseColor);
|
||||
_appearance.SetData(args.Ritual, RitualVisuals.Color, ent.Comp.PhaseColor);
|
||||
}
|
||||
}
|
||||
190
Content.Server/_CP14/MagicRituals/CP14RitualSystem.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using System.Text;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server._CP14.MagicRituals;
|
||||
|
||||
public partial class CP14RitualSystem : CP14SharedRitualSystem
|
||||
{
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
[Dependency] private readonly PointLightSystem _pointLight = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
InitializeDescriber();
|
||||
InitializeVisuals();
|
||||
|
||||
SubscribeLocalEvent<CP14MagicRitualComponent, CP14ActivateRitualDoAfter>(OnActivateRitual);
|
||||
SubscribeLocalEvent<CP14MagicRitualComponent, GetVerbsEvent<AlternativeVerb>>(OnAlternativeVerb);
|
||||
|
||||
SubscribeLocalEvent<CP14MagicRitualPhaseComponent, CP14RitualTriggerEvent>(OnPhaseTrigger);
|
||||
|
||||
SubscribeLocalEvent<CP14MagicRitualOrbComponent, ExaminedEvent>(OnOrbExamine);
|
||||
}
|
||||
|
||||
private void OnActivateRitual(Entity<CP14MagicRitualComponent> ent, ref CP14ActivateRitualDoAfter args)
|
||||
{
|
||||
if (args.Cancelled || args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
StartRitual(ent);
|
||||
}
|
||||
|
||||
private void OnAlternativeVerb(Entity<CP14MagicRitualComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanInteract || ent.Comp.CurrentPhase is not null)
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
AlternativeVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
var doAfterArgs =
|
||||
new DoAfterArgs(EntityManager, user, ent.Comp.ActivationTime, new CP14ActivateRitualDoAfter(), ent, ent)
|
||||
{
|
||||
BreakOnDamage = true,
|
||||
BreakOnMove = true,
|
||||
};
|
||||
|
||||
_doAfter.TryStartDoAfter(doAfterArgs);
|
||||
},
|
||||
Text = Loc.GetString("cp14-ritual-verb-text"),
|
||||
Priority = 1,
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
|
||||
private void OnOrbExamine(Entity<CP14MagicRitualOrbComponent> ent, ref ExaminedEvent args)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(Loc.GetString("cp14-ritual-orb-examine", ("name", MetaData(ent).EntityName)) + "\n");
|
||||
foreach (var orbType in ent.Comp.Powers)
|
||||
{
|
||||
if (!_proto.TryIndex(orbType.Key, out var indexedType))
|
||||
continue;
|
||||
|
||||
sb.Append($"[color={indexedType.Color.ToHex()}]");
|
||||
sb.Append(Loc.GetString("cp14-ritual-entry-item",
|
||||
("name", Loc.GetString(indexedType.Name)),
|
||||
("count", orbType.Value)));
|
||||
sb.Append($"[/color] \n");
|
||||
}
|
||||
|
||||
args.PushMarkup(sb.ToString());
|
||||
}
|
||||
|
||||
public void StartRitual(Entity<CP14MagicRitualComponent> ritual)
|
||||
{
|
||||
EndRitual(ritual);
|
||||
|
||||
var ev = new CP14RitualStartEvent(ritual);
|
||||
RaiseLocalEvent(ritual, ev);
|
||||
|
||||
ChangePhase(ritual, ritual.Comp.StartPhase);
|
||||
_appearance.SetData(ritual, RitualVisuals.Enabled, true);
|
||||
}
|
||||
|
||||
private void ChangePhase(Entity<CP14MagicRitualComponent> ritual, EntProtoId newPhase)
|
||||
{
|
||||
QueueDel(ritual.Comp.CurrentPhase);
|
||||
|
||||
var newPhaseEnt = Spawn(newPhase, Transform(ritual).Coordinates);
|
||||
_transform.SetParent(newPhaseEnt, ritual);
|
||||
var newPhaseComp = EnsureComp<CP14MagicRitualPhaseComponent>(newPhaseEnt);
|
||||
|
||||
ritual.Comp.CurrentPhase = (newPhaseEnt, newPhaseComp);
|
||||
newPhaseComp.Ritual = ritual;
|
||||
|
||||
foreach (var edge in newPhaseComp.Edges)
|
||||
{
|
||||
foreach (var trigger in edge.Triggers)
|
||||
{
|
||||
trigger.Initialize(EntityManager, ritual.Comp.CurrentPhase.Value, edge);
|
||||
}
|
||||
}
|
||||
|
||||
var ev = new CP14RitualPhaseBoundEvent(ritual, newPhaseEnt);
|
||||
RaiseLocalEvent(ritual, ev);
|
||||
RaiseLocalEvent(newPhaseEnt, ev);
|
||||
|
||||
if (newPhaseComp.DeadEnd)
|
||||
EndRitual(ritual);
|
||||
}
|
||||
|
||||
public void EndRitual(Entity<CP14MagicRitualComponent> ritual)
|
||||
{
|
||||
if (ritual.Comp.CurrentPhase is null)
|
||||
return;
|
||||
|
||||
QueueDel(ritual.Comp.CurrentPhase);
|
||||
ritual.Comp.CurrentPhase = null;
|
||||
|
||||
var ev = new CP14RitualEndEvent(ritual);
|
||||
RaiseLocalEvent(ritual, ev);
|
||||
|
||||
_appearance.SetData(ritual, RitualVisuals.Enabled, false);
|
||||
|
||||
foreach (var orb in ritual.Comp.Orbs)
|
||||
{
|
||||
QueueDel(orb);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPhaseTrigger(Entity<CP14MagicRitualPhaseComponent> phase, ref CP14RitualTriggerEvent args)
|
||||
{
|
||||
if (phase.Comp.Ritual is null)
|
||||
return;
|
||||
|
||||
RitualPhaseEdge? selectedEdge = null;
|
||||
foreach (var edge in phase.Comp.Edges)
|
||||
{
|
||||
if (edge.Target == args.NextPhase)
|
||||
{
|
||||
selectedEdge = edge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedEdge is null)
|
||||
return;
|
||||
|
||||
var passed = true;
|
||||
foreach (var req in selectedEdge.Value.Requirements)
|
||||
{
|
||||
if (!req.Check(EntityManager, phase, phase.Comp.Ritual.Value.Comp.Stability)) //lol
|
||||
{
|
||||
ChangeRitualStability(phase.Comp.Ritual.Value, -req.FailStabilityCost);
|
||||
passed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!passed)
|
||||
return;
|
||||
|
||||
foreach (var action in selectedEdge.Value.Actions)
|
||||
{
|
||||
action.Effect(EntityManager, _transform, phase);
|
||||
}
|
||||
|
||||
ChangePhase(phase.Comp.Ritual.Value, args.NextPhase);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._CP14.MagicRituals.Components;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14RitualSystem))]
|
||||
public sealed partial class CP14PaperPhaseDescriberComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public EntProtoId StartPhase = default!;
|
||||
|
||||
[DataField]
|
||||
public EntityUid? CurrentPhase = null;
|
||||
|
||||
public List<EntProtoId> SearchHistory = new();
|
||||
|
||||
[DataField]
|
||||
public List<EntProtoId> Hyperlinks = new();
|
||||
|
||||
public SoundSpecifier UseSound = new SoundCollectionSpecifier("CP14Book")
|
||||
{
|
||||
Params = AudioParams.Default
|
||||
.WithVariation(0.05f)
|
||||
.WithVolume(0.5f),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -89,6 +89,6 @@ namespace Content.Shared.Verbs
|
||||
|
||||
public static readonly VerbCategory PowerLevel = new("verb-categories-power-level", null);
|
||||
|
||||
public static readonly VerbCategory CP14Craft = new("cp14-verb-categories-craft", null); //CP14
|
||||
public static readonly VerbCategory CP14RitualBook = new("cp14-verb-categories-ritual-book", null); //CP14
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,7 @@ public sealed class CP14DayCyclePeriodPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; } = string.Empty;
|
||||
|
||||
[DataField(required: true)]
|
||||
public LocId Name = default!;
|
||||
}
|
||||
|
||||
44
Content.Shared/_CP14/MagicRitual/Actions/AddOrb.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Text;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Actions;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a key-orb to the ritual.
|
||||
/// </summary>
|
||||
public sealed partial class AddOrb : CP14RitualAction
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public Dictionary<EntProtoId, int> Orbs = new();
|
||||
|
||||
public override string? GetGuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(Loc.GetString("cp14-ritual-effect-add-orb")+ "\n");
|
||||
foreach (var orb in Orbs)
|
||||
{
|
||||
if (!prototype.TryIndex(orb.Key, out var indexedOrb))
|
||||
continue;
|
||||
|
||||
sb.Append(Loc.GetString("cp14-ritual-entry-item", ("name", indexedOrb.Name), ("count", orb.Value)) + "\n");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override void Effect(EntityManager entManager, SharedTransformSystem transform, Entity<CP14MagicRitualPhaseComponent> phase)
|
||||
{
|
||||
if (phase.Comp.Ritual is null)
|
||||
return;
|
||||
|
||||
var ritual = entManager.System<CP14SharedRitualSystem>();
|
||||
|
||||
foreach (var orb in Orbs)
|
||||
{
|
||||
for (var i = 0; i < orb.Value; i++)
|
||||
{
|
||||
ritual.AddOrbToRitual(phase.Comp.Ritual.Value, orb.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
using System.Text;
|
||||
using Content.Shared.EntityEffects;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Actions;
|
||||
|
||||
/// <summary>
|
||||
/// Filters the nearest X entities by whitelist and applies the specified EntityEffects on them
|
||||
/// </summary>
|
||||
public sealed partial class ApplyEntityEffect : CP14RitualAction
|
||||
{
|
||||
[DataField]
|
||||
public float CheckRange = 1f;
|
||||
|
||||
[DataField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
[DataField]
|
||||
public LocId? WhitelistDesc;
|
||||
|
||||
[DataField(required: true, serverOnly: true)]
|
||||
public List<EntityEffect> Effects = new();
|
||||
|
||||
[DataField]
|
||||
public int MaxEntities = 1;
|
||||
|
||||
public override string? GetGuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.Append(Loc.GetString("cp14-ritual-range", ("range", CheckRange)) + "\n");
|
||||
sb.Append(Loc.GetString("cp14-ritual-effect-apply-effect", ("count", MaxEntities), ("range", CheckRange)) + "\n");
|
||||
|
||||
if (WhitelistDesc is not null)
|
||||
{
|
||||
sb.Append(Loc.GetString(WhitelistDesc));
|
||||
sb.Append("\n");
|
||||
}
|
||||
|
||||
foreach (var effect in Effects)
|
||||
{
|
||||
sb.Append("- " + effect.GuidebookEffectDescription(prototype, entSys) + "\n");
|
||||
}
|
||||
sb.Append("\n");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override void Effect(EntityManager entManager, SharedTransformSystem transform, Entity<CP14MagicRitualPhaseComponent> phase)
|
||||
{
|
||||
var lookup = entManager.System<EntityLookupSystem>();
|
||||
var whitelist = entManager.System<EntityWhitelistSystem>();
|
||||
|
||||
var entitiesAround = lookup.GetEntitiesInRange(phase, CheckRange, LookupFlags.Uncontained);
|
||||
|
||||
var count = 0;
|
||||
foreach (var entity in entitiesAround)
|
||||
{
|
||||
if (Whitelist is not null && !whitelist.IsValid(Whitelist, entity))
|
||||
continue;
|
||||
|
||||
foreach (var effect in Effects)
|
||||
{
|
||||
effect.Effect(new EntityEffectBaseArgs(entity, entManager));
|
||||
}
|
||||
|
||||
entManager.Spawn(VisualEffect, transform.GetMapCoordinates(entity));
|
||||
count++;
|
||||
|
||||
if (count >= MaxEntities)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Content.Shared/_CP14/MagicRitual/Actions/CP14RitualAction.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Actions;
|
||||
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
[MeansImplicitUse]
|
||||
public abstract partial class CP14RitualAction
|
||||
{
|
||||
/// <summary>
|
||||
/// Effect appearing in place of interacted entities
|
||||
/// </summary>
|
||||
[DataField("vfx")]
|
||||
public EntProtoId? VisualEffect = "CP14DustEffect";
|
||||
|
||||
public abstract void Effect(EntityManager entManager, SharedTransformSystem transform, Entity<CP14MagicRitualPhaseComponent> phase);
|
||||
|
||||
public abstract string? GetGuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys);
|
||||
}
|
||||
41
Content.Shared/_CP14/MagicRitual/Actions/ConsumeOrb.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System.Text;
|
||||
using Content.Shared._CP14.MagicRitual.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Actions;
|
||||
|
||||
/// <summary>
|
||||
/// Removes the orb key from the ritual.
|
||||
/// </summary>
|
||||
public sealed partial class ConsumeOrb : CP14RitualAction
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public ProtoId<CP14MagicTypePrototype> MagicType = new();
|
||||
|
||||
[DataField(required: true)]
|
||||
public int Count = 0;
|
||||
|
||||
public override string? GetGuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
if (!prototype.TryIndex(MagicType, out var indexedType))
|
||||
return null;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(Loc.GetString("cp14-ritual-effect-consume-orb", ("name", Loc.GetString(indexedType.Name)), ("count", Count))+ "\n");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override void Effect(EntityManager entManager, SharedTransformSystem transform, Entity<CP14MagicRitualPhaseComponent> phase)
|
||||
{
|
||||
if (phase.Comp.Ritual is null)
|
||||
return;
|
||||
|
||||
var ritual = entManager.System<CP14SharedRitualSystem>();
|
||||
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
ritual.ConsumeOrbType(phase.Comp.Ritual.Value, MagicType);
|
||||
}
|
||||
}
|
||||
}
|
||||
104
Content.Shared/_CP14/MagicRitual/Actions/ConsumeResource.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using System.Text;
|
||||
using Content.Shared.Stacks;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Actions;
|
||||
|
||||
public sealed partial class ConsumeResource : CP14RitualAction
|
||||
{
|
||||
[DataField]
|
||||
public float CheckRange = 1f;
|
||||
|
||||
[DataField]
|
||||
public Dictionary<EntProtoId, int> RequiredEntities = new ();
|
||||
|
||||
[DataField]
|
||||
public Dictionary<ProtoId<StackPrototype>, int> RequiredStacks = new();
|
||||
|
||||
public override string? GetGuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(Loc.GetString("cp14-ritual-effect-consume-resource", ("range", CheckRange)) + "\n");
|
||||
|
||||
foreach (var entity in RequiredEntities)
|
||||
{
|
||||
if (!prototype.TryIndex(entity.Key, out var indexed))
|
||||
continue;
|
||||
|
||||
sb.Append(Loc.GetString("cp14-ritual-entry-item", ("name", indexed.Name), ("count", entity.Value)) + "\n");
|
||||
}
|
||||
|
||||
foreach (var stack in RequiredStacks)
|
||||
{
|
||||
if (!prototype.TryIndex(stack.Key, out var indexed))
|
||||
continue;
|
||||
|
||||
sb.Append(Loc.GetString("cp14-ritual-entry-item", ("name", Loc.GetString(indexed.Name)), ("count", stack.Value)) + "\n");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override void Effect(EntityManager entManager, SharedTransformSystem transform, Entity<CP14MagicRitualPhaseComponent> phase)
|
||||
{
|
||||
var lookup = entManager.System<EntityLookupSystem>();
|
||||
var stack = entManager.System<SharedStackSystem>();
|
||||
|
||||
var entitiesAround = lookup.GetEntitiesInRange(phase, CheckRange, LookupFlags.Uncontained);
|
||||
|
||||
foreach (var reqEnt in RequiredEntities)
|
||||
{
|
||||
var requiredCount = reqEnt.Value;
|
||||
|
||||
foreach (var entity in entitiesAround)
|
||||
{
|
||||
if (!entManager.TryGetComponent<MetaDataComponent>(entity, out var metaData))
|
||||
continue;
|
||||
if (!entManager.HasComponent<TransformComponent>(entity))
|
||||
continue;
|
||||
|
||||
var entProto = metaData.EntityPrototype;
|
||||
if (entProto is null)
|
||||
continue;
|
||||
|
||||
if (entProto.ID == reqEnt.Key && requiredCount > 0)
|
||||
{
|
||||
if (VisualEffect is not null)
|
||||
entManager.Spawn(VisualEffect.Value, transform.GetMapCoordinates(entity));
|
||||
|
||||
entManager.DeleteEntity(entity);
|
||||
|
||||
requiredCount--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var reqStack in RequiredStacks)
|
||||
{
|
||||
var requiredCount = reqStack.Value;
|
||||
|
||||
foreach (var entity in entitiesAround)
|
||||
{
|
||||
if (!entManager.TryGetComponent<StackComponent>(entity, out var stackComp))
|
||||
continue;
|
||||
|
||||
if (stackComp.StackTypeId != reqStack.Key)
|
||||
continue;
|
||||
|
||||
var count = (int)MathF.Min(requiredCount, stackComp.Count);
|
||||
|
||||
|
||||
if (stackComp.Count - count <= 0)
|
||||
entManager.DeleteEntity(entity);
|
||||
else
|
||||
stack.SetCount(entity, stackComp.Count - count, stackComp);
|
||||
|
||||
|
||||
requiredCount -= count;
|
||||
|
||||
if (VisualEffect is not null)
|
||||
entManager.Spawn(VisualEffect.Value, transform.GetMapCoordinates(entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Content.Shared/_CP14/MagicRitual/Actions/EditStability.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Actions;
|
||||
|
||||
public sealed partial class EditStability : CP14RitualAction
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public float Mod;
|
||||
|
||||
public override void Effect(EntityManager entManager, SharedTransformSystem transform, Entity<CP14MagicRitualPhaseComponent> phase)
|
||||
{
|
||||
var ritual = entManager.System<CP14SharedRitualSystem>();
|
||||
|
||||
if (phase.Comp.Ritual is not null)
|
||||
ritual.ChangeRitualStability(phase.Comp.Ritual.Value, Mod);
|
||||
}
|
||||
|
||||
public override string? GetGuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
return Mod switch
|
||||
{
|
||||
> 0 => Loc.GetString("cp14-ritual-effect-stability-add", ("count", Mod * 100)) + "\n",
|
||||
< 0 => Loc.GetString("cp14-ritual-effect-stability-minus", ("count", -Mod * 100)) + "\n",
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
44
Content.Shared/_CP14/MagicRitual/Actions/SpawnEntity.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Text;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Actions;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an entity in the coordinates of the ritual.
|
||||
/// </summary> TODO: EntityTable support?
|
||||
public sealed partial class SpawnEntity : CP14RitualAction
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public Dictionary<EntProtoId, int> Spawns = new();
|
||||
|
||||
[DataField]
|
||||
public LocId? Name;
|
||||
|
||||
public override string? GetGuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(Loc.GetString("cp14-ritual-effect-spawn-entity")+ "\n");
|
||||
foreach (var spawn in Spawns)
|
||||
{
|
||||
if (!prototype.TryIndex(spawn.Key, out var indexed))
|
||||
return null;
|
||||
|
||||
sb.Append(Loc.GetString("cp14-ritual-entry-item",
|
||||
("name", Name is null ? indexed.Name : Loc.GetString(Name)),
|
||||
("count", spawn.Value)) + "\n");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override void Effect(EntityManager entManager, SharedTransformSystem transform, Entity<CP14MagicRitualPhaseComponent> phase)
|
||||
{
|
||||
foreach (var spawn in Spawns)
|
||||
{
|
||||
for (var i = 0; i < spawn.Value; i++)
|
||||
{
|
||||
entManager.Spawn(spawn.Key, transform.GetMapCoordinates(phase));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Content.Shared/_CP14/MagicRitual/CP14MagicRitualComponent.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual;
|
||||
|
||||
/// <summary>
|
||||
/// Ritual Behavior Controller. Creates and removes entities of magical phases
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedRitualSystem))]
|
||||
public sealed partial class CP14MagicRitualComponent : Component
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public EntProtoId StartPhase;
|
||||
|
||||
[DataField]
|
||||
public Entity<CP14MagicRitualPhaseComponent>? CurrentPhase;
|
||||
|
||||
[DataField]
|
||||
public float Stability = 1f;
|
||||
|
||||
[DataField]
|
||||
public float ActivationTime = 5f;
|
||||
|
||||
[DataField]
|
||||
public string RitualLayerMap = "ritual";
|
||||
|
||||
[DataField]
|
||||
public int MaxOrbCapacity = 3;
|
||||
|
||||
[DataField]
|
||||
public float RitualRadius = 5;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan TriggerTime = TimeSpan.Zero;
|
||||
|
||||
[DataField]
|
||||
public List<Entity<CP14MagicRitualOrbComponent>> Orbs = new();
|
||||
}
|
||||
86
Content.Shared/_CP14/MagicRitual/CP14MagicRitualEvents.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual;
|
||||
|
||||
/// <summary>
|
||||
/// Called out a ritual when any of its phase triggers are activated
|
||||
/// </summary>
|
||||
public sealed class CP14RitualTriggerEvent : EntityEventArgs
|
||||
{
|
||||
public EntProtoId NextPhase;
|
||||
|
||||
public CP14RitualTriggerEvent(EntProtoId phase)
|
||||
{
|
||||
NextPhase = phase;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called out at a ritual when his stability is altered
|
||||
/// </summary>
|
||||
public sealed class CP14RitualStabilityChangedEvent : EntityEventArgs
|
||||
{
|
||||
public float OldStability;
|
||||
public float NewStability;
|
||||
|
||||
public CP14RitualStabilityChangedEvent(float oldS, float newS)
|
||||
{
|
||||
OldStability = oldS;
|
||||
NewStability = newS;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called on both the ritual and the phase when they link together
|
||||
/// </summary>
|
||||
public sealed class CP14RitualPhaseBoundEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Ritual;
|
||||
public EntityUid Phase;
|
||||
|
||||
public CP14RitualPhaseBoundEvent(EntityUid r, EntityUid p)
|
||||
{
|
||||
Ritual = r;
|
||||
Phase = p;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked at the ritual holder entity when the ritual is complete and the phase entities have been removed
|
||||
/// </summary>
|
||||
public sealed class CP14RitualEndEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Ritual;
|
||||
|
||||
public CP14RitualEndEvent(EntityUid r)
|
||||
{
|
||||
Ritual = r;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked at the ritual holder entity when the ritual begins, and invokes the starting phase
|
||||
/// </summary>
|
||||
public sealed class CP14RitualStartEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Ritual;
|
||||
|
||||
public CP14RitualStartEvent(EntityUid r)
|
||||
{
|
||||
Ritual = r;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class CP14ActivateRitualDoAfter : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum RitualVisuals
|
||||
{
|
||||
Color,
|
||||
Enabled,
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Content.Shared._CP14.MagicRitual.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual;
|
||||
|
||||
/// <summary>
|
||||
/// “Key” in the concept of rituals. An entity that can be a key to a ritual, and holds certain characteristics that can be spent, or by which a phase transition requirement check can be made.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedRitualSystem))]
|
||||
public sealed partial class CP14MagicRitualOrbComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public Dictionary<ProtoId<CP14MagicTypePrototype>, int> Powers = new();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using Content.Shared._CP14.MagicRitual.Actions;
|
||||
using Content.Shared._CP14.MagicRitual.Requirements;
|
||||
using Content.Shared._CP14.MagicRitualTrigger;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual;
|
||||
|
||||
/// <summary>
|
||||
/// Magical entity that reacts to world events
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(CP14SharedRitualSystem))]
|
||||
public sealed partial class CP14MagicRitualPhaseComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// A link to the ritual itself in which this phase is found
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Entity<CP14MagicRitualComponent>? Ritual;
|
||||
|
||||
[DataField]
|
||||
public Color PhaseColor = Color.White;
|
||||
|
||||
[DataField]
|
||||
public List<RitualPhaseEdge> Edges = new();
|
||||
|
||||
/// <summary>
|
||||
/// by moving to this node, the ritual will end instantly.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool DeadEnd = false;
|
||||
}
|
||||
|
||||
[DataRecord]
|
||||
public partial record struct RitualPhaseEdge()
|
||||
{
|
||||
public EntProtoId Target { get; set; }
|
||||
|
||||
public List<CP14RitualTrigger> Triggers { get; set; } = new();
|
||||
public List<CP14RitualRequirement> Requirements { get; set; } = new();
|
||||
public List<CP14RitualAction> Actions { get; set; } = new();
|
||||
}
|
||||
61
Content.Shared/_CP14/MagicRitual/CP14SharedRitualSystem.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Content.Shared._CP14.MagicRitual.Prototypes;
|
||||
using Content.Shared.Follower;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual;
|
||||
|
||||
public partial class CP14SharedRitualSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly FollowerSystem _followerSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
|
||||
public void ChangeRitualStability(Entity<CP14MagicRitualComponent> ritual, float dStab)
|
||||
{
|
||||
var newS = MathHelper.Clamp01(ritual.Comp.Stability + dStab);
|
||||
|
||||
var ev = new CP14RitualStabilityChangedEvent(ritual.Comp.Stability, newS);
|
||||
RaiseLocalEvent(ritual, ev);
|
||||
|
||||
ritual.Comp.Stability = newS;
|
||||
}
|
||||
|
||||
public void AddOrbToRitual(Entity<CP14MagicRitualComponent> ritual, EntProtoId orb)
|
||||
{
|
||||
if (_net.IsClient)
|
||||
return;
|
||||
|
||||
if (!_proto.TryIndex(orb, out var indexedOrb))
|
||||
return;
|
||||
|
||||
if (ritual.Comp.Orbs.Count >= ritual.Comp.MaxOrbCapacity)
|
||||
return;
|
||||
|
||||
var spawnedOrb = Spawn(orb, _transform.GetMapCoordinates(ritual));
|
||||
|
||||
if (!TryComp<CP14MagicRitualOrbComponent>(spawnedOrb, out var orbComp))
|
||||
{
|
||||
QueueDel(spawnedOrb);
|
||||
return;
|
||||
}
|
||||
|
||||
_followerSystem.StartFollowingEntity(spawnedOrb, ritual);
|
||||
ritual.Comp.Orbs.Add((spawnedOrb, orbComp));
|
||||
}
|
||||
|
||||
public void ConsumeOrbType(Entity<CP14MagicRitualComponent> ritual, ProtoId<CP14MagicTypePrototype> magicType)
|
||||
{
|
||||
foreach (var orb in ritual.Comp.Orbs)
|
||||
{
|
||||
var powers = orb.Comp.Powers;
|
||||
if (!powers.ContainsKey(magicType))
|
||||
continue;
|
||||
|
||||
ritual.Comp.Orbs.Remove(orb);
|
||||
QueueDel(orb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Prototypes;
|
||||
|
||||
/// <summary>
|
||||
/// A round-start setup preset, such as which antagonists to spawn.
|
||||
/// </summary>
|
||||
[Prototype("magicType")]
|
||||
public sealed partial class CP14MagicTypePrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[DataField(required: true)]
|
||||
public string Name = string.Empty;
|
||||
|
||||
[DataField(required: true)]
|
||||
public Color Color = Color.White;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Requirements;
|
||||
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
[MeansImplicitUse]
|
||||
public abstract partial class CP14RitualRequirement
|
||||
{
|
||||
/// <summary>
|
||||
/// If this checks fails, the ritual will lose some of its stability.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float FailStabilityCost;
|
||||
|
||||
public abstract bool Check(EntityManager entManager, Entity<CP14MagicRitualPhaseComponent> phaseEnt, float stability);
|
||||
|
||||
public abstract string? GetGuidebookRequirementDescription(IPrototypeManager prototype, IEntitySystemManager entSys);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
using System.Text;
|
||||
using Content.Shared._CP14.MagicRitual.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Requirements;
|
||||
|
||||
/// <summary>
|
||||
/// Requires specific daytime period
|
||||
/// </summary>
|
||||
public sealed partial class RequiredOrbs : CP14RitualRequirement
|
||||
{
|
||||
[DataField]
|
||||
public ProtoId<CP14MagicTypePrototype> MagicType = new();
|
||||
|
||||
[DataField]
|
||||
public int? Min;
|
||||
|
||||
[DataField]
|
||||
public int? Max;
|
||||
|
||||
public override string? GetGuidebookRequirementDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (!prototype.TryIndex(MagicType, out var indexedType))
|
||||
return null;
|
||||
|
||||
sb.Append(Loc.GetString("cp14-ritual-required-orbs", ("name", Loc.GetString(indexedType.Name))) + " ");
|
||||
if (Min is not null && Max is not null)
|
||||
sb.Append(Loc.GetString("cp14-ritual-required-orbs-item-minmax", ("min", Min), ("max", Max))+ "\n");
|
||||
else if (Min is not null)
|
||||
sb.Append(Loc.GetString("cp14-ritual-required-orbs-item-min", ("min", Min))+ "\n");
|
||||
else if (Max is not null)
|
||||
sb.Append(Loc.GetString("cp14-ritual-required-orbs-item-min", ("max", Max))+ "\n");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override bool Check(EntityManager entManager, Entity<CP14MagicRitualPhaseComponent> phaseEnt, float stability)
|
||||
{
|
||||
if (phaseEnt.Comp.Ritual is null)
|
||||
return false;
|
||||
|
||||
var count = 0;
|
||||
foreach (var orb in phaseEnt.Comp.Ritual.Value.Comp.Orbs)
|
||||
{
|
||||
foreach (var power in orb.Comp.Powers)
|
||||
{
|
||||
if (power.Key == MagicType)
|
||||
count += power.Value;
|
||||
}
|
||||
}
|
||||
|
||||
if (Min is not null && Max is not null)
|
||||
return count >= Min && count <= Max;
|
||||
if (Min is not null)
|
||||
return count >= Min;
|
||||
if (Max is not null)
|
||||
return count <= Max;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
using System.Text;
|
||||
using Content.Shared.Stacks;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Requirements;
|
||||
|
||||
/// <summary>
|
||||
/// Requires certain specific entities to be near the ritual. TODO: Replace with Whitelist
|
||||
/// </summary>
|
||||
public sealed partial class RequiredResource : CP14RitualRequirement
|
||||
{
|
||||
[DataField]
|
||||
public float CheckRange = 3f;
|
||||
|
||||
[DataField]
|
||||
public Dictionary<EntProtoId, int> RequiredEntities = new ();
|
||||
|
||||
[DataField]
|
||||
public Dictionary<ProtoId<StackPrototype>, int> RequiredStacks = new();
|
||||
|
||||
/// <summary>
|
||||
/// Effect appearing in place of used entities
|
||||
/// </summary>
|
||||
[DataField("vfx")]
|
||||
public EntProtoId? Effect = "CP14DustEffect";
|
||||
|
||||
public override string? GetGuidebookRequirementDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(Loc.GetString("cp14-ritual-required-resource", ("range", CheckRange)) + "\n");
|
||||
|
||||
foreach (var entity in RequiredEntities)
|
||||
{
|
||||
if (!prototype.TryIndex(entity.Key, out var indexed))
|
||||
continue;
|
||||
|
||||
sb.Append(Loc.GetString("cp14-ritual-entry-item", ("name", indexed.Name), ("count", entity.Value)) + "\n");
|
||||
}
|
||||
|
||||
foreach (var stack in RequiredStacks)
|
||||
{
|
||||
if (!prototype.TryIndex(stack.Key, out var indexed))
|
||||
continue;
|
||||
|
||||
sb.Append(Loc.GetString("cp14-ritual-entry-item", ("name", Loc.GetString(indexed.Name)), ("count", stack.Value)) + "\n");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public override bool Check(EntityManager entManager, Entity<CP14MagicRitualPhaseComponent> phaseEnt, float stability)
|
||||
{
|
||||
var _lookup = entManager.System<EntityLookupSystem>();
|
||||
var _transform = entManager.System<SharedTransformSystem>();
|
||||
|
||||
var entitiesAround = _lookup.GetEntitiesInRange(phaseEnt, CheckRange, LookupFlags.Uncontained);
|
||||
|
||||
var passed = true;
|
||||
|
||||
foreach (var reqEnt in RequiredEntities)
|
||||
{
|
||||
var requiredCount = reqEnt.Value;
|
||||
|
||||
foreach (var entity in entitiesAround)
|
||||
{
|
||||
if (!entManager.TryGetComponent<MetaDataComponent>(entity, out var metaData))
|
||||
continue;
|
||||
if (!entManager.TryGetComponent<TransformComponent>(entity, out var xform))
|
||||
continue;
|
||||
|
||||
var entProto = metaData.EntityPrototype;
|
||||
if (entProto is null)
|
||||
continue;
|
||||
|
||||
if (entProto.ID == reqEnt.Key && requiredCount > 0)
|
||||
{
|
||||
if (Effect is not null)
|
||||
entManager.Spawn(Effect.Value, _transform.GetMapCoordinates(entity));
|
||||
|
||||
requiredCount--;
|
||||
}
|
||||
}
|
||||
|
||||
if (requiredCount > 0)
|
||||
passed = false;
|
||||
}
|
||||
|
||||
foreach (var reqStack in RequiredStacks)
|
||||
{
|
||||
var requiredCount = reqStack.Value;
|
||||
|
||||
foreach (var entity in entitiesAround)
|
||||
{
|
||||
if (!entManager.TryGetComponent<StackComponent>(entity, out var stack))
|
||||
continue;
|
||||
|
||||
if (stack.StackTypeId != reqStack.Key)
|
||||
continue;
|
||||
|
||||
var count = (int)MathF.Min(requiredCount, stack.Count);
|
||||
requiredCount -= count;
|
||||
|
||||
if (Effect is not null)
|
||||
entManager.Spawn(Effect.Value, _transform.GetMapCoordinates(entity));
|
||||
}
|
||||
|
||||
if (requiredCount > 0)
|
||||
passed = false;
|
||||
}
|
||||
|
||||
return passed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Requirements;
|
||||
|
||||
/// <summary>
|
||||
/// Requires that the stability of the ritual be within specified limits. If the stability is above or below the specified values, the check will fail
|
||||
/// </summary>
|
||||
public sealed partial class RequiredStability : CP14RitualRequirement
|
||||
{
|
||||
[DataField]
|
||||
public float Min = 0;
|
||||
[DataField]
|
||||
public float Max = 1;
|
||||
|
||||
public override string? GetGuidebookRequirementDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
return Min switch
|
||||
{
|
||||
> 0 when Max < 1 =>
|
||||
Loc.GetString("cp14-ritual-required-stability-minmax", ("min", Min*100), ("max", Max*100)),
|
||||
> 0 => Loc.GetString("cp14-ritual-required-stability-min", ("min", Min*100)),
|
||||
< 0 => Loc.GetString("cp14-ritual-required-stability-max", ("min", Max*100)),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
public override bool Check(EntityManager entManager, Entity<CP14MagicRitualPhaseComponent> phaseEnt, float stability)
|
||||
{
|
||||
if (phaseEnt.Comp.Ritual is null)
|
||||
return false;
|
||||
|
||||
var s = phaseEnt.Comp.Ritual.Value.Comp.Stability;
|
||||
|
||||
return !(s < Min) && !(s > Max);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Content.Shared._CP14.DayCycle;
|
||||
using Content.Shared._CP14.DayCycle.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitual.Requirements;
|
||||
|
||||
/// <summary>
|
||||
/// Requires specific daytime
|
||||
/// </summary>
|
||||
public sealed partial class RequiredTime : CP14RitualRequirement
|
||||
{
|
||||
[DataField]
|
||||
public ProtoId<CP14DayCyclePeriodPrototype> TimePeriod;
|
||||
|
||||
public override string? GetGuidebookRequirementDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
if (!prototype.TryIndex(TimePeriod, out var indexed))
|
||||
return null;
|
||||
|
||||
return Loc.GetString("cp14-ritual-required-time", ("period", Loc.GetString(indexed.Name)));
|
||||
}
|
||||
|
||||
public override bool Check(EntityManager entManager, Entity<CP14MagicRitualPhaseComponent> phaseEnt, float stability)
|
||||
{
|
||||
var transform = entManager.System<SharedTransformSystem>();
|
||||
var map = transform.GetMap(phaseEnt.Owner);
|
||||
|
||||
if (!entManager.TryGetComponent<CP14DayCycleComponent>(map, out var dayCycle))
|
||||
return false;
|
||||
|
||||
return TimePeriod == dayCycle.CurrentPeriod;
|
||||
}
|
||||
}
|
||||
17
Content.Shared/_CP14/MagicRitualTrigger/CP14RitualTrigger.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitualTrigger;
|
||||
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
[MeansImplicitUse]
|
||||
public abstract partial class CP14RitualTrigger
|
||||
{
|
||||
[DataField]
|
||||
public RitualPhaseEdge? Edge = null;
|
||||
|
||||
public abstract void Initialize(EntityManager entManager, Entity<CP14MagicRitualPhaseComponent> ritual, RitualPhaseEdge edge);
|
||||
|
||||
public abstract string? GetGuidebookTriggerDescription(IPrototypeManager prototype, IEntitySystemManager entSys);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
namespace Content.Shared._CP14.MagicRitualTrigger;
|
||||
|
||||
public partial class CP14SharedRitualTriggerSystem : EntitySystem
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// triggers when a creature of a certain race dies within range of the ritual.
|
||||
/// </summary>
|
||||
public sealed partial class CP14SacrificeSpeciesTrigger : CP14RitualTrigger
|
||||
{
|
||||
[DataField]
|
||||
public float Range = 3f;
|
||||
|
||||
[DataField(required: true)]
|
||||
public ProtoId<SpeciesPrototype> Species = default!;
|
||||
|
||||
public override void Initialize(EntityManager entManager, Entity<CP14MagicRitualPhaseComponent> ritual, RitualPhaseEdge edge)
|
||||
{
|
||||
entManager.EnsureComponent<CP14RitualSacrificeSpeciesTriggerComponent>(ritual, out var trigger);
|
||||
trigger.Triggers.Add(this);
|
||||
Edge = edge;
|
||||
}
|
||||
|
||||
public override string? GetGuidebookTriggerDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
if (!prototype.TryIndex(Species, out var indexedSpecies))
|
||||
return null;
|
||||
|
||||
return Loc.GetString("cp14-ritual-trigger-sacrifice",
|
||||
("name", Loc.GetString(indexedSpecies.Name)),
|
||||
("range", Range));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CP14RitualSacrificeSpeciesTriggerComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public HashSet<CP14SacrificeSpeciesTrigger> Triggers = new();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers when a creature passing the whitelist dies within range of the ritual.
|
||||
/// </summary>
|
||||
public sealed partial class CP14SacrificeWhitelistTrigger : CP14RitualTrigger
|
||||
{
|
||||
[DataField]
|
||||
public float Range = 3f;
|
||||
|
||||
[DataField(required: true)]
|
||||
public EntityWhitelist Whitelist = default!;
|
||||
|
||||
[DataField(required: true)]
|
||||
public LocId WhitelistDesc = default!;
|
||||
|
||||
public override void Initialize(EntityManager entManager, Entity<CP14MagicRitualPhaseComponent> ritual, RitualPhaseEdge edge)
|
||||
{
|
||||
entManager.EnsureComponent<CP14RitualSacrificeWhitelistTriggerComponent>(ritual, out var trigger);
|
||||
trigger.Triggers.Add(this);
|
||||
Edge = edge;
|
||||
}
|
||||
|
||||
public override string? GetGuidebookTriggerDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
return Loc.GetString("cp14-ritual-trigger-sacrifice",
|
||||
("name", Loc.GetString(WhitelistDesc)),
|
||||
("range", Range));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CP14RitualSacrificeWhitelistTriggerComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public HashSet<CP14SacrificeWhitelistTrigger> Triggers = new();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the phase transition after a certain period of time
|
||||
/// </summary>
|
||||
public sealed partial class CP14TimerTrigger : CP14RitualTrigger
|
||||
{
|
||||
[DataField]
|
||||
public float Delay = 10f;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan TriggerTime = TimeSpan.Zero;
|
||||
|
||||
public override void Initialize(EntityManager entManager, Entity<CP14MagicRitualPhaseComponent> ritual, RitualPhaseEdge edge)
|
||||
{
|
||||
entManager.EnsureComponent<CP14RitualTimerTriggerComponent>(ritual, out var trigger);
|
||||
trigger.Triggers.Add(this);
|
||||
Edge = edge;
|
||||
}
|
||||
|
||||
public override string? GetGuidebookTriggerDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
return Loc.GetString("cp14-ritual-trigger-timer-stable", ("time", Delay));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CP14RitualTimerTriggerComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public HashSet<CP14TimerTrigger> Triggers = new();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Content.Shared._CP14.MagicRitual;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers a phase transition when the Ritual hears a certain message
|
||||
/// </summary>
|
||||
public sealed partial class CP14VoiceTrigger : CP14RitualTrigger
|
||||
{
|
||||
[DataField]
|
||||
public string Message = string.Empty;
|
||||
|
||||
[DataField]
|
||||
public int Speakers = 1;
|
||||
|
||||
public override void Initialize(EntityManager entManager, Entity<CP14MagicRitualPhaseComponent> ritual, RitualPhaseEdge edge)
|
||||
{
|
||||
entManager.EnsureComponent<CP14RitualVoiceTriggerComponent>(ritual, out var trigger);
|
||||
trigger.Triggers.Add(this);
|
||||
Edge = edge;
|
||||
}
|
||||
|
||||
public override string? GetGuidebookTriggerDescription(IPrototypeManager prototype, IEntitySystemManager entSys)
|
||||
{
|
||||
return Loc.GetString("cp14-ritual-trigger-voice", ("phrase", Message), ("count", Speakers));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Content.Shared._CP14.MagicRitualTrigger.Triggers;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class CP14RitualVoiceTriggerComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public HashSet<CP14VoiceTrigger> Triggers = new();
|
||||
}
|
||||
@@ -21,5 +21,5 @@ public sealed partial class CP14MeleeParriableComponent : Component
|
||||
public float NeedParryPower = 1f;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier ParrySound = new SoundPathSpecifier("/Audio/_CP14/Effects/parry1.ogg", AudioParams.Default.WithVariation(0.2f));
|
||||
public SoundSpecifier ParrySound = new SoundCollectionSpecifier("CP14Parry", AudioParams.Default.WithVariation(0.05f));
|
||||
}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
using Content.Shared._CP14.DayCycle;
|
||||
using Content.Shared._CP14.DayCycle.Components;
|
||||
using Content.Shared.Random.Rules;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.Random.Rules;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the attached entity is in space.
|
||||
/// Checks whether there is a time of day on the current map, and whether the current time of day corresponds to the specified periods.
|
||||
/// </summary>
|
||||
public sealed partial class IsDaylight : RulesRule
|
||||
public sealed partial class CP14TimePeriod : RulesRule
|
||||
{
|
||||
[DataField] private List<ProtoId<CP14DayCyclePeriodPrototype>> Periods = new();
|
||||
|
||||
public override bool Check(EntityManager entManager, EntityUid uid)
|
||||
{
|
||||
var dayCycle = entManager.System<CP14SharedDayCycleSystem>();
|
||||
var transform = entManager.System<SharedTransformSystem>();
|
||||
|
||||
if (Inverted)
|
||||
return !dayCycle.TryDaylightThere(uid, true);
|
||||
else
|
||||
return dayCycle.TryDaylightThere(uid, true);
|
||||
var map = transform.GetMap(uid);
|
||||
return entManager.TryGetComponent<CP14DayCycleComponent>(map, out var dayCycle) && Periods.Contains(dayCycle.CurrentPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,9 @@
|
||||
- files: ["water.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: 'by jackthemurray of Freesound.org.'
|
||||
source: "https://freesound.org/people/jackthemurray/sounds/433589/"
|
||||
source: "https://freesound.org/people/jackthemurray/sounds/433589/"
|
||||
|
||||
- files: ["cave.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: 'by EminYILDIRIM of Freesound.org.'
|
||||
source: "https://freesound.org/people/EminYILDIRIM/sounds/711956/"
|
||||
BIN
Resources/Audio/_CP14/Ambience/Loops/cave.ogg
Normal file
BIN
Resources/Audio/_CP14/Ambience/ambimagic1.ogg
Normal file
BIN
Resources/Audio/_CP14/Ambience/ambimagic2.ogg
Normal file
BIN
Resources/Audio/_CP14/Ambience/ambimagic3.ogg
Normal file
BIN
Resources/Audio/_CP14/Ambience/ambimagic4.ogg
Normal file
BIN
Resources/Audio/_CP14/Ambience/ambimagic5.ogg
Normal file
BIN
Resources/Audio/_CP14/Ambience/ambimagic6.ogg
Normal file
@@ -18,12 +18,17 @@
|
||||
copyright: 'by 7by7 of Freesound.org. Mixed from stereo to mono.'
|
||||
source: "https://freesound.org/people/7by7/sounds/72849/"
|
||||
|
||||
- files: ["weatherWindy.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: 'by Benboncan of Freesound.org. Mixed from stereo to mono.'
|
||||
source: "https://freesound.org/people/Benboncan/sounds/134699/"
|
||||
|
||||
- files: ["ambiAlchemy.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: 'by be-steele of Freesound.org.'
|
||||
source: "https://freesound.org/people/be-steele/sounds/130753/"
|
||||
source: "https://freesound.org/people/be-steele/sounds/130753/"
|
||||
|
||||
- files: ["ambimagic1.ogg", "ambimagic2.ogg", "ambimagic3.ogg", "ambimagic4.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: 'by EminYILDIRIM of Freesound.org.'
|
||||
source: "https://freesound.org/people/EminYILDIRIM/sounds/581627/"
|
||||
|
||||
- files: ["ambimagic5.ogg", "ambimagic6.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: 'by xkeril of Freesound.org.'
|
||||
source: "https://freesound.org/people/xkeril/sounds/706092/"
|
||||
@@ -23,7 +23,12 @@
|
||||
copyright: 'Breviceps of Freesound.org'
|
||||
source: "https://freesound.org/people/Breviceps/sounds/493162/"
|
||||
|
||||
- files: ["parry1.ogg"]
|
||||
license: "CC0-1.0"
|
||||
copyright: 'JohnBuhr of Freesound.org'
|
||||
source: "https://freesound.org/people/JohnBuhr/sounds/326803/"
|
||||
- files: ["parry1.ogg", "parry2.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: 'by EminYILDIRIM of Freesound.org.'
|
||||
source: "https://freesound.org/people/EminYILDIRIM/sounds/536104/"
|
||||
|
||||
- files: ["parry2.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: 'by EminYILDIRIM of Freesound.org.'
|
||||
source: "https://freesound.org/people/EminYILDIRIM/sounds/536105/"
|
||||
BIN
Resources/Audio/_CP14/Effects/parry2.ogg
Normal file
@@ -56,4 +56,14 @@
|
||||
- files: ["broom1.ogg", "broom2.ogg", "broom3.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: 'by F.M.Audio of Freesound.org. Cropped by TheShuEd.'
|
||||
source: "https://freesound.org/people/F.M.Audio/sounds/552056/"
|
||||
source: "https://freesound.org/people/F.M.Audio/sounds/552056/"
|
||||
|
||||
- files: ["book1.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: 'by flag2 of Freesound.org. edit to Mono by TheShuEd.'
|
||||
source: "https://freesound.org/people/flag2/sounds/63318/"
|
||||
|
||||
- files: ["book2.ogg"]
|
||||
license: "CC-BY-4.0"
|
||||
copyright: 'by InspectorJ of Freesound.org. edit to Mono by TheShuEd.'
|
||||
source: "https://freesound.org/people/InspectorJ/sounds/416179/"
|
||||
BIN
Resources/Audio/_CP14/Items/book1.ogg
Normal file
BIN
Resources/Audio/_CP14/Items/book2.ogg
Normal file
4
Resources/Locale/en-US/_CP14/daycycle/periods.ftl
Normal file
@@ -0,0 +1,4 @@
|
||||
cp14-daycycle-sunrise = sunrise
|
||||
cp14-daycycle-day = day
|
||||
cp14-daycycle-night = night
|
||||
cp14-daycycle-evening = evening
|
||||
19
Resources/Locale/en-US/_CP14/magicRitual/base.ftl
Normal file
@@ -0,0 +1,19 @@
|
||||
cp14-verb-categories-ritual-book = Choose a page:
|
||||
cp14-ritual-describer-verb-item = Turn the page: {$name}
|
||||
cp14-ritual-describer-verb-hyperlink = Quick navigate: {$name}
|
||||
cp14-ritual-describer-verb-back = Back
|
||||
|
||||
cp14-ritual-verb-text = Activate the ritual
|
||||
|
||||
cp14-ritual-category-all-living = any living thing
|
||||
|
||||
cp14-ritual-intro = The following neighboring nodes are known to be available for transition:
|
||||
|
||||
cp14-ritual-range = In a radius {$range},
|
||||
|
||||
cp14-ritual-orb-examine = {$name} contains the following energies:
|
||||
cp14-ritual-entry-item =
|
||||
- { $count ->
|
||||
[1] {$name}
|
||||
*[other] {$name} ({$count} pcs.)
|
||||
}
|
||||
20
Resources/Locale/en-US/_CP14/magicRitual/effects.ftl
Normal file
@@ -0,0 +1,20 @@
|
||||
cp14-ritual-effect-header = The moment of transition will cause the following effects:
|
||||
|
||||
##
|
||||
|
||||
cp14-ritual-effect-apply-effect =
|
||||
{ $count ->
|
||||
[1] From the nearest entity
|
||||
*[other] At the {$count} closest entity
|
||||
}
|
||||
|
||||
cp14-ritual-effect-spawn-entity = Incorporates the following objects into reality:
|
||||
|
||||
cp14-ritual-effect-consume-resource = Within a radius of {$range}, absorbs the following entities:
|
||||
|
||||
cp14-ritual-effect-stability-add = Stabilizes the ritual by [color=#34eb89]{$count}%[/color]
|
||||
cp14-ritual-effect-stability-minus = Destabilizes the ritual by [color=#eb4034]{$count}%[/color]
|
||||
|
||||
cp14-ritual-effect-add-orb = Ritual receives:
|
||||
|
||||
cp14-ritual-effect-consume-orb = The ritual consumes the {$name} spheres: {$count}
|
||||
17
Resources/Locale/en-US/_CP14/magicRitual/requirements.ftl
Normal file
@@ -0,0 +1,17 @@
|
||||
cp14-ritual-req-header = The following conditions must be met for the transition to take place:
|
||||
|
||||
#
|
||||
|
||||
cp14-ritual-required-stability-min = The stability of the ritual should be higher than {$min}%
|
||||
cp14-ritual-required-stability-max = The stability of the ritual must be lower than {$max}%
|
||||
cp14-ritual-required-stability-minmax = The stability of the ritual should be within the range of {$min} to {$max}%
|
||||
|
||||
cp14-ritual-required-resource = The following items must be within the {$range} radius:
|
||||
|
||||
cp14-ritual-required-time = It must be {$period} outside
|
||||
|
||||
|
||||
cp14-ritual-required-orbs = Energies like “{$name}” should be
|
||||
cp14-ritual-required-orbs-item-min = at least {$min}
|
||||
cp14-ritual-required-orbs-item-max = no more than {$max}
|
||||
cp14-ritual-required-orbs-item-minmax = from {$min} to {$max}
|
||||
15
Resources/Locale/en-US/_CP14/magicRitual/triggers.ftl
Normal file
@@ -0,0 +1,15 @@
|
||||
cp14-ritual-trigger-header = The transition to this node can take place:
|
||||
|
||||
#
|
||||
|
||||
cp14-ritual-trigger-timer-stable = Automatically in {$time} seconds.
|
||||
|
||||
cp14-ritual-trigger-voice = After casting the spell “{$phrase}”
|
||||
{ $count ->
|
||||
[1].
|
||||
*[other], by at least {$count} different entities at the same time
|
||||
}
|
||||
|
||||
cp14-ritual-trigger-failure = As a result of various errors.
|
||||
|
||||
cp14-ritual-trigger-sacrifice = When {$name} dies painfully within a {$range} radius of the ritual site.
|
||||
2
Resources/Locale/en-US/_CP14/magicTypes/magic.ftl
Normal file
@@ -0,0 +1,2 @@
|
||||
cp14-magic-type-creation = Creation
|
||||
cp14-magic-type-destruction = Destruction
|
||||
4
Resources/Locale/ru-RU/_CP14/daycycle/periods.ftl
Normal file
@@ -0,0 +1,4 @@
|
||||
cp14-daycycle-sunrise = рассвет
|
||||
cp14-daycycle-day = день
|
||||
cp14-daycycle-night = ночь
|
||||
cp14-daycycle-evening = вечер
|
||||
19
Resources/Locale/ru-RU/_CP14/magicRitual/base.ftl
Normal file
@@ -0,0 +1,19 @@
|
||||
cp14-verb-categories-ritual-book = Выбрать страницу
|
||||
cp14-ritual-describer-verb-item = Перелистнуть: {$name}
|
||||
cp14-ritual-describer-verb-hyperlink = Быстрый переход: {$name}
|
||||
cp14-ritual-describer-verb-back = Назад
|
||||
|
||||
cp14-ritual-verb-text = Активировать ритуал
|
||||
|
||||
cp14-ritual-category-all-living = любое живое существо
|
||||
|
||||
cp14-ritual-intro = Известны следующие соседние узлы, доступные для перехода:
|
||||
|
||||
cp14-ritual-range = В радиусе {$range},
|
||||
|
||||
cp14-ritual-orb-examine = {$name} содержит в себе следующие энергии:
|
||||
cp14-ritual-entry-item =
|
||||
- { $count ->
|
||||
[1] {$name}
|
||||
*[other] {$name} ({$count} шт.)
|
||||
}
|
||||
20
Resources/Locale/ru-RU/_CP14/magicRitual/effects.ftl
Normal file
@@ -0,0 +1,20 @@
|
||||
cp14-ritual-effect-header = Момент перехода вызовет следующие эффекты:
|
||||
|
||||
##
|
||||
|
||||
cp14-ritual-effect-apply-effect =
|
||||
у { $count ->
|
||||
[1] ближайшей сущности
|
||||
*[other] {$count} ближайших сущностей
|
||||
}
|
||||
|
||||
cp14-ritual-effect-spawn-entity = Воплощает в реальность следующие объекты:
|
||||
|
||||
cp14-ritual-effect-consume-resource = В радиусе {$range} поглощает следующие сущности:
|
||||
|
||||
cp14-ritual-effect-stability-add = Стабилизирует ритуал на [color=#34eb89]{$count}%[/color]
|
||||
cp14-ritual-effect-stability-minus = Дестабилизирует ритуал на [color=#eb4034]{$count}%[/color]
|
||||
|
||||
cp14-ritual-effect-add-orb = Ритуал получает:
|
||||
|
||||
cp14-ritual-effect-consume-orb = Ритуал поглощает сфер {$name}: {$count}
|
||||
17
Resources/Locale/ru-RU/_CP14/magicRitual/requirements.ftl
Normal file
@@ -0,0 +1,17 @@
|
||||
cp14-ritual-req-header = Для перехода необходимо соблюсти следующие условия:
|
||||
|
||||
#
|
||||
|
||||
cp14-ritual-required-stability-min = Стабильность ритуала должна быть выше {$min}%
|
||||
cp14-ritual-required-stability-max = Стабильность ритуала должна быть ниже {$max}%
|
||||
cp14-ritual-required-stability-minmax = Стабильность ритуала должна быть в рамках от {$min} до {$max}%
|
||||
|
||||
cp14-ritual-required-resource = В радиусе {$range} должны находиться следующие предметы:
|
||||
|
||||
cp14-ritual-required-time = На улице должен быть {$period}
|
||||
|
||||
|
||||
cp14-ritual-required-orbs = Энергии типа "{$name}" должно быть
|
||||
cp14-ritual-required-orbs-item-min = как минимум {$min}
|
||||
cp14-ritual-required-orbs-item-max = не больше {$max}
|
||||
cp14-ritual-required-orbs-item-minmax = от {$min} до {$max}
|
||||
15
Resources/Locale/ru-RU/_CP14/magicRitual/triggers.ftl
Normal file
@@ -0,0 +1,15 @@
|
||||
cp14-ritual-trigger-header = Переход в этот узел может состояться:
|
||||
|
||||
#
|
||||
|
||||
cp14-ritual-trigger-timer-stable = Автоматически через {$time} секунд.
|
||||
|
||||
cp14-ritual-trigger-voice = После произнесения заклинания "{$phrase}"
|
||||
{ $count ->
|
||||
[1].
|
||||
*[other], как минимум {$count} разными сущностями одновременно
|
||||
}
|
||||
|
||||
cp14-ritual-trigger-failure = В результате различных ошибок.
|
||||
|
||||
cp14-ritual-trigger-sacrifice = Когда {$name} мучительно погибает в радиусе {$range} от места проведения ритуала.
|
||||
2
Resources/Locale/ru-RU/_CP14/magicTypes/magic.ftl
Normal file
@@ -0,0 +1,2 @@
|
||||
cp14-magic-type-creation = Созидание
|
||||
cp14-magic-type-destruction = Разрушение
|
||||
@@ -1,11 +1,15 @@
|
||||
- type: CP14DayCyclePeriod
|
||||
id: Sunrise # HOLY SHIT!
|
||||
id: Sunrise
|
||||
name: cp14-daycycle-sunrise
|
||||
|
||||
- type: CP14DayCyclePeriod
|
||||
id: Day
|
||||
name: cp14-daycycle-day
|
||||
|
||||
- type: CP14DayCyclePeriod
|
||||
id: Night
|
||||
name: cp14-daycycle-night
|
||||
|
||||
- type: CP14DayCyclePeriod
|
||||
id: Evening
|
||||
name: cp14-daycycle-evening
|
||||
|
||||
@@ -16,4 +16,16 @@
|
||||
params:
|
||||
variation: 0.03
|
||||
- type: UseDelay
|
||||
delay: 4
|
||||
delay: 4
|
||||
|
||||
- type: entity
|
||||
id: CP14RitualChalk
|
||||
parent: BaseItem
|
||||
name: ritual chalk
|
||||
description: Quartz chalk, handy for drawing temporary ritual circles.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: _CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi
|
||||
state: icon
|
||||
- type: Item
|
||||
sprite: _CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi
|
||||
@@ -0,0 +1,24 @@
|
||||
- type: entity
|
||||
parent: BookBase
|
||||
id: CP14RitualGrimoire
|
||||
name: ritualist's grimoire
|
||||
description: a book that holds the knowledge of hundreds of ritualists before you. Use it on an active ritual to get all the information about its current state.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Misc/books.rsi
|
||||
layers:
|
||||
- state: paper
|
||||
- state: cover_old
|
||||
color: "#a6161d"
|
||||
- state: decor_bottom
|
||||
color: "#6e1022"
|
||||
- state: decor_wingette
|
||||
color: "#4a101b"
|
||||
- state: icon_pentagramm
|
||||
color: "#911129"
|
||||
- state: detail_bookmark
|
||||
color: red
|
||||
- type: CP14PaperPhaseDescriber
|
||||
startPhase: CP14_NeutralCluster_Root
|
||||
hyperlinks:
|
||||
- CP14_NeutralCluster_Root
|
||||
@@ -0,0 +1,55 @@
|
||||
- type: entity
|
||||
parent: CP14BaseRitualPhase
|
||||
abstract: true
|
||||
id: CP14_NeutralCluster_Base
|
||||
components:
|
||||
- type: CP14MagicRitualPhase
|
||||
phaseColor: "#FFFFFF"
|
||||
- type: ActiveListener
|
||||
range: 3
|
||||
|
||||
- type: entity
|
||||
parent: CP14_NeutralCluster_Base
|
||||
id: CP14_NeutralCluster_Root
|
||||
name: Te-Se-Ra
|
||||
description: The perfect energetic position to begin any ritual.
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: CP14MagicRitualPhase
|
||||
edges:
|
||||
- target: CP14_NeutralCluster_00
|
||||
triggers:
|
||||
- !type:CP14SacrificeWhitelistTrigger
|
||||
whitelist:
|
||||
components:
|
||||
- MobState
|
||||
whitelistDesc: cp14-ritual-category-all-living
|
||||
|
||||
- type: entity
|
||||
parent: CP14_NeutralCluster_Base
|
||||
id: CP14_NeutralCluster_00
|
||||
name: Li-Ra
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: CP14MagicRitualPhase
|
||||
edges:
|
||||
- target: CP14RitualEnd
|
||||
triggers:
|
||||
- !type:CP14VoiceTrigger
|
||||
message: "Vespere nebula"
|
||||
actions:
|
||||
- !type:ApplyEntityEffect
|
||||
maxEntities: 3
|
||||
vfx: CP14ImpactEffectCureWounds
|
||||
whitelist:
|
||||
components:
|
||||
- HumanoidAppearance
|
||||
effects:
|
||||
- !type:HealthChange
|
||||
damage:
|
||||
types:
|
||||
Asphyxiation: -50
|
||||
Bloodloss: -10
|
||||
- !type:ModifyBleedAmount
|
||||
- !type:ModifyBloodLevel
|
||||
- !type:Jitter
|
||||
22
Resources/Prototypes/_CP14/Entities/Ritual/basic.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
- type: entity
|
||||
id: CP14BaseRitualPhase
|
||||
abstract: true
|
||||
components:
|
||||
- type: CP14MagicRitualPhase
|
||||
- type: Tag
|
||||
tags:
|
||||
- HideContextMenu
|
||||
- type: PointLight
|
||||
- type: EmitSoundOnSpawn
|
||||
sound:
|
||||
path: /Audio/Effects/tesla_consume.ogg
|
||||
params:
|
||||
variation: 0.3
|
||||
|
||||
- type: entity
|
||||
parent: CP14BaseRitualPhase
|
||||
id: CP14RitualEnd
|
||||
name: end of ritual
|
||||
components:
|
||||
- type: CP14MagicRitualPhase
|
||||
deadEnd: true
|
||||
39
Resources/Prototypes/_CP14/Entities/Ritual/orbs.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
- type: entity
|
||||
id: CP14BaseRitualOrb
|
||||
abstract: true
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Physics
|
||||
canCollide: false
|
||||
- type: Sprite
|
||||
sprite: _CP14/Effects/Magic/ritual_orbs.rsi
|
||||
drawDepth: Mobs
|
||||
noRot: true
|
||||
|
||||
- type: entity
|
||||
parent: CP14BaseRitualOrb
|
||||
id: CP14RitualOrbCreation
|
||||
name: creation orb
|
||||
categories: [ HideSpawnMenu]
|
||||
components:
|
||||
- type: CP14MagicRitualOrb
|
||||
powers:
|
||||
Creation: 1
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: creation
|
||||
shader: unshaded
|
||||
|
||||
- type: entity
|
||||
parent: CP14BaseRitualOrb
|
||||
id: CP14RitualOrbDestruction
|
||||
name: destruction orb
|
||||
categories: [ HideSpawnMenu]
|
||||
components:
|
||||
- type: CP14MagicRitualOrb
|
||||
powers:
|
||||
Destruction: 1
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: destruction
|
||||
shader: unshaded
|
||||
@@ -8,7 +8,12 @@
|
||||
- type: Sprite
|
||||
noRot: true
|
||||
sprite: _CP14/Structures/Decoration/statue_gob.rsi
|
||||
state: gob
|
||||
layers:
|
||||
- state: gob
|
||||
- state: eyes
|
||||
map: ["ritual"]
|
||||
visible: false
|
||||
shader: unshaded
|
||||
drawdepth: Mobs
|
||||
offset: "0.0,0.5"
|
||||
- type: Fixtures
|
||||
@@ -43,6 +48,9 @@
|
||||
CP14StoneBlock1:
|
||||
min: 4
|
||||
max: 5
|
||||
- type: CP14MagicRitual
|
||||
startPhase: CP14_NeutralCluster_Root
|
||||
- type: Appearance
|
||||
|
||||
- type: entity
|
||||
id: CP14StatueGobVines
|
||||
@@ -51,9 +59,12 @@
|
||||
suffix: Normal. Overgrown.
|
||||
components:
|
||||
- type: Sprite
|
||||
noRot: true
|
||||
sprite: _CP14/Structures/Decoration/statue_gob.rsi
|
||||
state: gob_vines
|
||||
layers:
|
||||
- state: gob_vines
|
||||
- state: eyes
|
||||
map: ["ritual"]
|
||||
visible: false
|
||||
shader: unshaded
|
||||
|
||||
- type: entity
|
||||
id: CP14StatueGobRuined
|
||||
@@ -62,9 +73,12 @@
|
||||
suffix: Ruined
|
||||
components:
|
||||
- type: Sprite
|
||||
noRot: true
|
||||
sprite: _CP14/Structures/Decoration/statue_gob.rsi
|
||||
state: gob_ruined
|
||||
layers:
|
||||
- state: gob_ruined
|
||||
- state: eyes
|
||||
map: ["ritual"]
|
||||
visible: false
|
||||
shader: unshaded
|
||||
|
||||
- type: entity
|
||||
id: CP14StatueGobRuinedVines
|
||||
@@ -73,6 +87,9 @@
|
||||
suffix: Ruined. Overgrown.
|
||||
components:
|
||||
- type: Sprite
|
||||
noRot: true
|
||||
sprite: _CP14/Structures/Decoration/statue_gob.rsi
|
||||
state: gob_ruined_vines
|
||||
layers:
|
||||
- state: gob_ruined_vines
|
||||
- state: eyes
|
||||
map: ["ritual"]
|
||||
visible: false
|
||||
shader: unshaded
|
||||
|
||||
@@ -171,6 +171,71 @@
|
||||
loot:
|
||||
All: CP14GatherLumiMushroom
|
||||
|
||||
- type: entity
|
||||
parent: BaseRock
|
||||
id: CP14QuartzCrystal
|
||||
name: quartz
|
||||
description: Quartz is an essential mineral capable of interacting with magical energy. It is highly sought after by alchemists for extracting beneficial properties from liquids
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: Mobs
|
||||
sprite: _CP14/Structures/crystal.rsi
|
||||
offset: 0, 0.25
|
||||
noRot: true
|
||||
layers:
|
||||
- state: big
|
||||
map: ["random"]
|
||||
- type: InteractionPopup
|
||||
interactSuccessString: popup-cp14crystal-ding
|
||||
messagePerceivedByOthers: popup-cp14crystal-ding
|
||||
interactSuccessSound:
|
||||
collection: CP14CrystalDings
|
||||
params:
|
||||
variation: 0.03
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
damageModifierSet: Glass
|
||||
- type: MeleeSound
|
||||
soundGroups:
|
||||
Brute:
|
||||
collection: GlassSmash
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
- trigger:
|
||||
!type:DamageTrigger
|
||||
damage: 20
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound:
|
||||
collection: GlassBreak
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- !type:SpawnEntitiesBehavior
|
||||
spawn:
|
||||
CP14QuartzShard:
|
||||
min: 1
|
||||
max: 3
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
fix1:
|
||||
shape:
|
||||
!type:PhysShapeCircle
|
||||
radius: 0.30
|
||||
density: 60
|
||||
mask:
|
||||
- MachineMask
|
||||
layer:
|
||||
- MidImpassable
|
||||
- LowImpassable
|
||||
- BulletImpassable
|
||||
- Opaque
|
||||
- type: RandomSprite
|
||||
available:
|
||||
- random:
|
||||
big: ""
|
||||
medium: ""
|
||||
small: ""
|
||||
|
||||
# Blue amanita
|
||||
|
||||
- type: entityLootTable
|
||||
@@ -203,4 +268,4 @@
|
||||
world5: ""
|
||||
- type: Gatherable
|
||||
loot:
|
||||
All: CP14GatherBlueAmanita
|
||||
All: CP14GatherBlueAmanita
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
- type: Sprite
|
||||
drawdepth: Mobs
|
||||
- type: CP14Wallmount
|
||||
- type: Physics
|
||||
canCollide: false
|
||||
|
||||
- type: entity
|
||||
id: CP14WallmountTorch
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
- type: entity
|
||||
id: CP14WallmountCrystalBase
|
||||
parent:
|
||||
- BaseStructure
|
||||
- CP14BaseWallmount
|
||||
abstract: true
|
||||
name: sparkling quartz
|
||||
description: bioluminescent quartz crystals that can take on any color - a very handy light source in a deep caves. Unfortunately, the luminous properties are very hard to preserve.
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: Mobs
|
||||
|
||||
9
Resources/Prototypes/_CP14/MagicTypes/magic.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
- type: magicType
|
||||
id: Creation
|
||||
name: cp14-magic-type-creation
|
||||
color: "#4bcc7d"
|
||||
|
||||
- type: magicType
|
||||
id: Destruction
|
||||
name: cp14-magic-type-destruction
|
||||
color: "#c94c1a"
|
||||
@@ -52,24 +52,7 @@
|
||||
- FloraRockSolid01
|
||||
- FloraRockSolid02
|
||||
- FloraRockSolid03
|
||||
- CP14CrystalRubiesSmall
|
||||
- CP14CrystalRubiesMedium
|
||||
- CP14CrystalRubiesBig
|
||||
- CP14CrystalTopazesSmall
|
||||
- CP14CrystalTopazesMedium
|
||||
- CP14CrystalTopazesBig
|
||||
- CP14CrystalEmeraldsSmall
|
||||
- CP14CrystalEmeraldsMedium
|
||||
- CP14CrystalEmeraldsBig
|
||||
- CP14CrystalSapphiresSmall
|
||||
- CP14CrystalSapphiresMedium
|
||||
- CP14CrystalSapphiresBig
|
||||
- CP14CrystalAmethystsSmall
|
||||
- CP14CrystalAmethystsMedium
|
||||
- CP14CrystalAmethystsBig
|
||||
- CP14CrystalDiamondsSmall
|
||||
- CP14CrystalDiamondsMedium
|
||||
- CP14CrystalDiamondsBig
|
||||
- CP14QuartzCrystal
|
||||
- !type:BiomeEntityLayer # lumishroom sage
|
||||
threshold: 0.6
|
||||
noise:
|
||||
|
||||
@@ -35,4 +35,16 @@
|
||||
files:
|
||||
- /Audio/_CP14/Items/broom1.ogg
|
||||
- /Audio/_CP14/Items/broom2.ogg
|
||||
- /Audio/_CP14/Items/broom3.ogg
|
||||
- /Audio/_CP14/Items/broom3.ogg
|
||||
|
||||
- type: soundCollection
|
||||
id: CP14Parry
|
||||
files:
|
||||
- /Audio/_CP14/Effects/parry1.ogg
|
||||
- /Audio/_CP14/Effects/parry2.ogg
|
||||
|
||||
- type: soundCollection
|
||||
id: CP14Book
|
||||
files:
|
||||
- /Audio/_CP14/Items/book1.ogg
|
||||
- /Audio/_CP14/Items/book2.ogg
|
||||
@@ -3,7 +3,7 @@
|
||||
id: OpenWinds
|
||||
sound:
|
||||
params:
|
||||
volume: -3
|
||||
volume: -6
|
||||
collection: CP14LoopWinds
|
||||
rules: OpenWinds
|
||||
|
||||
@@ -15,6 +15,14 @@
|
||||
collection: CP14LoopWater
|
||||
rules: NearWater
|
||||
|
||||
- type: ambientLoop
|
||||
id: Cave
|
||||
sound:
|
||||
params:
|
||||
volume: 2
|
||||
collection: CP14LoopCave
|
||||
rules: CaveFloor
|
||||
|
||||
# Sound collections
|
||||
- type: soundCollection
|
||||
id: CP14LoopWinds
|
||||
@@ -26,6 +34,11 @@
|
||||
files:
|
||||
- /Audio/_CP14/Ambience/Loops/water.ogg
|
||||
|
||||
- type: soundCollection
|
||||
id: CP14LoopCave
|
||||
files:
|
||||
- /Audio/_CP14/Ambience/Loops/cave.ogg
|
||||
|
||||
# Rules
|
||||
- type: rules
|
||||
id: OpenWinds
|
||||
@@ -41,6 +54,16 @@
|
||||
- CP14FloorSand
|
||||
range: 5
|
||||
|
||||
- type: rules
|
||||
id: CaveFloor
|
||||
rules:
|
||||
- !type:NearbyTilesPercentRule
|
||||
ignoreAnchored: true
|
||||
percent: 0.5
|
||||
tiles:
|
||||
- CP14FloorBase
|
||||
range: 5
|
||||
|
||||
- type: rules
|
||||
id: NearWater
|
||||
rules:
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
# 4 Priority - Special
|
||||
|
||||
- type: ambientMusic
|
||||
id: CP14Ritual
|
||||
sound:
|
||||
params:
|
||||
volume: -12
|
||||
collection: CP14AmbienceMagic
|
||||
rules: NearRitual
|
||||
priority: 4
|
||||
|
||||
# 3 Priority - Departments
|
||||
|
||||
# 2 Priority - General areas
|
||||
- type: ambientMusic
|
||||
id: CP14NightForest
|
||||
sound:
|
||||
params:
|
||||
volume: -6
|
||||
collection: CP14NightForest
|
||||
rules: NightForest
|
||||
priority: 2
|
||||
|
||||
## Fallback if nothing else found
|
||||
- type: ambientMusic
|
||||
@@ -28,8 +45,61 @@
|
||||
- /Audio/_CP14/Ambience/ambicreepy2.ogg
|
||||
- /Audio/_CP14/Ambience/ambicreepy3.ogg
|
||||
|
||||
- type: soundCollection
|
||||
id: CP14AmbienceMagic
|
||||
files:
|
||||
- /Audio/_CP14/Ambience/ambimagic1.ogg
|
||||
- /Audio/_CP14/Ambience/ambimagic2.ogg
|
||||
- /Audio/_CP14/Ambience/ambimagic3.ogg
|
||||
- /Audio/_CP14/Ambience/ambimagic4.ogg
|
||||
- /Audio/_CP14/Ambience/ambimagic5.ogg
|
||||
- /Audio/_CP14/Ambience/ambimagic6.ogg
|
||||
- /Audio/_CP14/Ambience/ambiAlchemy.ogg
|
||||
|
||||
- type: soundCollection
|
||||
id: CP14NightForest
|
||||
files:
|
||||
- /Audio/_CP14/Animals/owl1.ogg
|
||||
- /Audio/_CP14/Animals/owl2.ogg
|
||||
- /Audio/_CP14/Animals/owl3.ogg
|
||||
- /Audio/_CP14/Animals/owl4.ogg
|
||||
- /Audio/_CP14/Animals/owl5.ogg
|
||||
- /Audio/_CP14/Animals/owl6.ogg
|
||||
- /Audio/_CP14/Animals/owl7.ogg
|
||||
- /Audio/_CP14/Animals/owl8.ogg
|
||||
- /Audio/_CP14/Animals/owl9.ogg
|
||||
|
||||
# Rules
|
||||
- type: rules
|
||||
id: AlwaysTrue
|
||||
rules:
|
||||
- !type:AlwaysTrueRule
|
||||
- !type:AlwaysTrueRule
|
||||
|
||||
- type: rules
|
||||
id: NearRitual
|
||||
rules:
|
||||
- !type:NearbyEntitiesRule
|
||||
count: 1
|
||||
whitelist:
|
||||
components:
|
||||
- CP14MagicRitualPhase
|
||||
- CP14MagicRitual
|
||||
range: 6
|
||||
|
||||
- type: rules
|
||||
id: NightForest
|
||||
rules:
|
||||
- !type:CP14TimePeriod
|
||||
periods:
|
||||
- Night
|
||||
- Evening
|
||||
- !type:NearbyTilesPercentRule
|
||||
ignoreAnchored: true
|
||||
percent: 0.5
|
||||
tiles:
|
||||
- CP14FloorGrass
|
||||
- CP14FloorGrassLight
|
||||
- CP14FloorGrassTall
|
||||
- CP14FloorDirt
|
||||
- CP14FloorSand
|
||||
range: 5
|
||||
|
After Width: | Height: | Size: 914 B |
|
After Width: | Height: | Size: 943 B |
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 16,
|
||||
"y": 16
|
||||
},
|
||||
"license": "CLA",
|
||||
"copyright": "Created by TheShuEd for CrystallPunk",
|
||||
"states": [
|
||||
{
|
||||
"name": "creation",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "destruction",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2,
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 338 B |
|
After Width: | Height: | Size: 287 B |
|
After Width: | Height: | Size: 299 B |
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CLA",
|
||||
"copyright": "Created by TheShuEd (Github)",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CLA",
|
||||
"copyright": "Created by omsoyk (Github/Discord)",
|
||||
"copyright": "Created by TheShuEd (Github)",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
|
||||
|
Before Width: | Height: | Size: 204 B After Width: | Height: | Size: 150 B |