diff --git a/Content.Client/_CP14/MagicRituals/CP14ClientRitualSystem.cs b/Content.Client/_CP14/MagicRituals/CP14ClientRitualSystem.cs new file mode 100644 index 0000000000..92db60ba09 --- /dev/null +++ b/Content.Client/_CP14/MagicRituals/CP14ClientRitualSystem.cs @@ -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(OnAppearanceChange); + } + + private void OnAppearanceChange(Entity ent, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + + if (!args.Sprite.LayerMapTryGet(ent.Comp.RitualLayerMap, out var ritualLayer)) + return; + + if (_appearance.TryGetData(ent, RitualVisuals.Color, out var ritualColor, args.Component)) + { + args.Sprite.LayerSetColor(ritualLayer, ritualColor); + } + + if (_appearance.TryGetData(ent, RitualVisuals.Enabled, out var enabled, args.Component)) + { + args.Sprite.LayerSetVisible(ritualLayer, enabled); + } + } +} diff --git a/Content.IntegrationTests/Tests/_CP14/CP14RitualTest.cs b/Content.IntegrationTests/Tests/_CP14/CP14RitualTest.cs new file mode 100644 index 0000000000..e22685fa46 --- /dev/null +++ b/Content.IntegrationTests/Tests/_CP14/CP14RitualTest.cs @@ -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 +{ + + /// + /// States that all edges of the ritual phase have triggers. + /// + [Test] + public async Task RitualHasAllTriggersTest() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var compFactory = server.ResolveDependency(); + var protoManager = server.ResolveDependency(); + + await server.WaitAssertion(() => + { + Assert.Multiple(() => + { + foreach (var proto in protoManager.EnumeratePrototypes()) + { + 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(); + } +} diff --git a/Content.Server/Speech/ListenEvent.cs b/Content.Server/Speech/ListenEvent.cs index b67aa92f65..8d8d12c0d2 100644 --- a/Content.Server/Speech/ListenEvent.cs +++ b/Content.Server/Speech/ListenEvent.cs @@ -1,3 +1,5 @@ +using Content.Shared.Speech; + namespace Content.Server.Speech; public sealed class ListenEvent : EntityEventArgs diff --git a/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.Sacrifice.cs b/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.Sacrifice.cs new file mode 100644 index 0000000000..0976c9a9cb --- /dev/null +++ b/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.Sacrifice.cs @@ -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(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(ev.Target, out var humanoid)) + return; + + var query = EntityQueryEnumerator(); + 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(); + 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); + } + } + } +} diff --git a/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.Timer.cs b/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.Timer.cs new file mode 100644 index 0000000000..63bccc6f43 --- /dev/null +++ b/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.Timer.cs @@ -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(OnMapInit); + } + + private void OnMapInit(Entity 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(); + 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; + } + } + } +} diff --git a/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.Voice.cs b/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.Voice.cs new file mode 100644 index 0000000000..3cdd461067 --- /dev/null +++ b/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.Voice.cs @@ -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(OnListenEvent); + } + + private void OnListenEvent(Entity ent, ref ListenEvent args) + { + if (!TryComp(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); + } + } +} diff --git a/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.cs b/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.cs new file mode 100644 index 0000000000..37852bbc2f --- /dev/null +++ b/Content.Server/_CP14/MagicRitualTrigger/CP14RitualTriggerSystem.cs @@ -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 ent, EntProtoId nextPhase) + { + var evConfirmed = new CP14RitualTriggerEvent(nextPhase); + RaiseLocalEvent(ent, evConfirmed); + } +} diff --git a/Content.Server/_CP14/MagicRituals/CP14RitualSystem.Describer.cs b/Content.Server/_CP14/MagicRituals/CP14RitualSystem.Describer.cs new file mode 100644 index 0000000000..da99773a6a --- /dev/null +++ b/Content.Server/_CP14/MagicRituals/CP14RitualSystem.Describer.cs @@ -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>(OnDescriberVerbs); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + } + + private void OnShutdown(Entity ent, ref ComponentShutdown args) + { + QueueDel(ent.Comp.CurrentPhase); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + SetPhase(ent, ent.Comp.StartPhase); + } + + private void SetPhase(Entity 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(ent, out var paper)) + return; + + _paper.SetContent((ent, paper), GetPhaseDescription(newPhase)); + _audio.PlayPvs(ent.Comp.UseSound, ent); + } + + private void BackPhase(Entity ent) + { + if (ent.Comp.SearchHistory.Count > 0) + SetPhase(ent, ent.Comp.SearchHistory[^1], false); + } + + private void OnDescriberVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + if (!TryComp(ent.Comp.CurrentPhase, out var phase)) + return; + + if (!TryComp(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(uid, out var phase)) + return string.Empty; + + return GetPhaseDescription((uid, phase)); + } + + private string GetPhaseDescription(Entity 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(); + } +} diff --git a/Content.Server/_CP14/MagicRituals/CP14RitualSystem.Visuals.cs b/Content.Server/_CP14/MagicRituals/CP14RitualSystem.Visuals.cs new file mode 100644 index 0000000000..9b88426e68 --- /dev/null +++ b/Content.Server/_CP14/MagicRituals/CP14RitualSystem.Visuals.cs @@ -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(OnPhaseBound); + } + + private void OnPhaseBound(Entity ent, ref CP14RitualPhaseBoundEvent args) + { + if (!TryComp(args.Ritual, out var ritual)) + return; + + _pointLight.SetColor(ent, ent.Comp.PhaseColor); + _appearance.SetData(args.Ritual, RitualVisuals.Color, ent.Comp.PhaseColor); + } +} diff --git a/Content.Server/_CP14/MagicRituals/CP14RitualSystem.cs b/Content.Server/_CP14/MagicRituals/CP14RitualSystem.cs new file mode 100644 index 0000000000..c1eb754bb3 --- /dev/null +++ b/Content.Server/_CP14/MagicRituals/CP14RitualSystem.cs @@ -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(OnActivateRitual); + SubscribeLocalEvent>(OnAlternativeVerb); + + SubscribeLocalEvent(OnPhaseTrigger); + + SubscribeLocalEvent(OnOrbExamine); + } + + private void OnActivateRitual(Entity ent, ref CP14ActivateRitualDoAfter args) + { + if (args.Cancelled || args.Handled) + return; + + args.Handled = true; + + StartRitual(ent); + } + + private void OnAlternativeVerb(Entity ent, ref GetVerbsEvent 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 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 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 ritual, EntProtoId newPhase) + { + QueueDel(ritual.Comp.CurrentPhase); + + var newPhaseEnt = Spawn(newPhase, Transform(ritual).Coordinates); + _transform.SetParent(newPhaseEnt, ritual); + var newPhaseComp = EnsureComp(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 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 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); + } +} diff --git a/Content.Server/_CP14/MagicRituals/Components/CP14PaperPhaseDescriberComponent.cs b/Content.Server/_CP14/MagicRituals/Components/CP14PaperPhaseDescriberComponent.cs new file mode 100644 index 0000000000..d3bd9b00af --- /dev/null +++ b/Content.Server/_CP14/MagicRituals/Components/CP14PaperPhaseDescriberComponent.cs @@ -0,0 +1,31 @@ +using Content.Shared._CP14.MagicRitual; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Server._CP14.MagicRituals.Components; + +/// +/// +/// +[RegisterComponent, Access(typeof(CP14RitualSystem))] +public sealed partial class CP14PaperPhaseDescriberComponent : Component +{ + [DataField(required: true)] + public EntProtoId StartPhase = default!; + + [DataField] + public EntityUid? CurrentPhase = null; + + public List SearchHistory = new(); + + [DataField] + public List Hyperlinks = new(); + + public SoundSpecifier UseSound = new SoundCollectionSpecifier("CP14Book") + { + Params = AudioParams.Default + .WithVariation(0.05f) + .WithVolume(0.5f), + }; +} + diff --git a/Content.Shared/Verbs/VerbCategory.cs b/Content.Shared/Verbs/VerbCategory.cs index 308d6e91b5..d1d0becf38 100644 --- a/Content.Shared/Verbs/VerbCategory.cs +++ b/Content.Shared/Verbs/VerbCategory.cs @@ -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 } } diff --git a/Content.Shared/_CP14/DayCycle/CP14DayCyclePeriodPrototype.cs b/Content.Shared/_CP14/DayCycle/CP14DayCyclePeriodPrototype.cs index 79823aacaf..5fdc58c4d2 100644 --- a/Content.Shared/_CP14/DayCycle/CP14DayCyclePeriodPrototype.cs +++ b/Content.Shared/_CP14/DayCycle/CP14DayCyclePeriodPrototype.cs @@ -7,4 +7,7 @@ public sealed class CP14DayCyclePeriodPrototype : IPrototype { [IdDataField] public string ID { get; } = string.Empty; + + [DataField(required: true)] + public LocId Name = default!; } diff --git a/Content.Shared/_CP14/MagicRitual/Actions/AddOrb.cs b/Content.Shared/_CP14/MagicRitual/Actions/AddOrb.cs new file mode 100644 index 0000000000..599b393526 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Actions/AddOrb.cs @@ -0,0 +1,44 @@ +using System.Text; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual.Actions; + +/// +/// Adds a key-orb to the ritual. +/// +public sealed partial class AddOrb : CP14RitualAction +{ + [DataField(required: true)] + public Dictionary 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 phase) + { + if (phase.Comp.Ritual is null) + return; + + var ritual = entManager.System(); + + foreach (var orb in Orbs) + { + for (var i = 0; i < orb.Value; i++) + { + ritual.AddOrbToRitual(phase.Comp.Ritual.Value, orb.Key); + } + } + } +} diff --git a/Content.Shared/_CP14/MagicRitual/Actions/ApplyEntityEffect.cs b/Content.Shared/_CP14/MagicRitual/Actions/ApplyEntityEffect.cs new file mode 100644 index 0000000000..b5b0159c18 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Actions/ApplyEntityEffect.cs @@ -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; + +/// +/// Filters the nearest X entities by whitelist and applies the specified EntityEffects on them +/// +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 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 phase) + { + var lookup = entManager.System(); + var whitelist = entManager.System(); + + 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; + } + } +} diff --git a/Content.Shared/_CP14/MagicRitual/Actions/CP14RitualAction.cs b/Content.Shared/_CP14/MagicRitual/Actions/CP14RitualAction.cs new file mode 100644 index 0000000000..b8e4d5b55c --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Actions/CP14RitualAction.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual.Actions; + +[ImplicitDataDefinitionForInheritors] +[MeansImplicitUse] +public abstract partial class CP14RitualAction +{ + /// + /// Effect appearing in place of interacted entities + /// + [DataField("vfx")] + public EntProtoId? VisualEffect = "CP14DustEffect"; + + public abstract void Effect(EntityManager entManager, SharedTransformSystem transform, Entity phase); + + public abstract string? GetGuidebookEffectDescription(IPrototypeManager prototype, IEntitySystemManager entSys); +} diff --git a/Content.Shared/_CP14/MagicRitual/Actions/ConsumeOrb.cs b/Content.Shared/_CP14/MagicRitual/Actions/ConsumeOrb.cs new file mode 100644 index 0000000000..4bfa738bde --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Actions/ConsumeOrb.cs @@ -0,0 +1,41 @@ +using System.Text; +using Content.Shared._CP14.MagicRitual.Prototypes; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual.Actions; + +/// +/// Removes the orb key from the ritual. +/// +public sealed partial class ConsumeOrb : CP14RitualAction +{ + [DataField(required: true)] + public ProtoId 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 phase) + { + if (phase.Comp.Ritual is null) + return; + + var ritual = entManager.System(); + + for (var i = 0; i < Count; i++) + { + ritual.ConsumeOrbType(phase.Comp.Ritual.Value, MagicType); + } + } +} diff --git a/Content.Shared/_CP14/MagicRitual/Actions/ConsumeResource.cs b/Content.Shared/_CP14/MagicRitual/Actions/ConsumeResource.cs new file mode 100644 index 0000000000..cad1cb1ee1 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Actions/ConsumeResource.cs @@ -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 RequiredEntities = new (); + + [DataField] + public Dictionary, 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 phase) + { + var lookup = entManager.System(); + var stack = entManager.System(); + + 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(entity, out var metaData)) + continue; + if (!entManager.HasComponent(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(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)); + } + } + } +} diff --git a/Content.Shared/_CP14/MagicRitual/Actions/EditStability.cs b/Content.Shared/_CP14/MagicRitual/Actions/EditStability.cs new file mode 100644 index 0000000000..f70cdfab23 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Actions/EditStability.cs @@ -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 phase) + { + var ritual = entManager.System(); + + 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, + }; + } +} diff --git a/Content.Shared/_CP14/MagicRitual/Actions/SpawnEntity.cs b/Content.Shared/_CP14/MagicRitual/Actions/SpawnEntity.cs new file mode 100644 index 0000000000..faff072030 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Actions/SpawnEntity.cs @@ -0,0 +1,44 @@ +using System.Text; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual.Actions; + +/// +/// Creates an entity in the coordinates of the ritual. +/// TODO: EntityTable support? +public sealed partial class SpawnEntity : CP14RitualAction +{ + [DataField(required: true)] + public Dictionary 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 phase) + { + foreach (var spawn in Spawns) + { + for (var i = 0; i < spawn.Value; i++) + { + entManager.Spawn(spawn.Key, transform.GetMapCoordinates(phase)); + } + } + } +} diff --git a/Content.Shared/_CP14/MagicRitual/CP14MagicRitualComponent.cs b/Content.Shared/_CP14/MagicRitual/CP14MagicRitualComponent.cs new file mode 100644 index 0000000000..2c2935ea81 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/CP14MagicRitualComponent.cs @@ -0,0 +1,37 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual; + +/// +/// Ritual Behavior Controller. Creates and removes entities of magical phases +/// +[RegisterComponent, Access(typeof(CP14SharedRitualSystem))] +public sealed partial class CP14MagicRitualComponent : Component +{ + [DataField(required: true)] + public EntProtoId StartPhase; + + [DataField] + public Entity? 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> Orbs = new(); +} diff --git a/Content.Shared/_CP14/MagicRitual/CP14MagicRitualEvents.cs b/Content.Shared/_CP14/MagicRitual/CP14MagicRitualEvents.cs new file mode 100644 index 0000000000..fe676a44eb --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/CP14MagicRitualEvents.cs @@ -0,0 +1,86 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared._CP14.MagicRitual; + +/// +/// Called out a ritual when any of its phase triggers are activated +/// +public sealed class CP14RitualTriggerEvent : EntityEventArgs +{ + public EntProtoId NextPhase; + + public CP14RitualTriggerEvent(EntProtoId phase) + { + NextPhase = phase; + } +} + +/// +/// Called out at a ritual when his stability is altered +/// +public sealed class CP14RitualStabilityChangedEvent : EntityEventArgs +{ + public float OldStability; + public float NewStability; + + public CP14RitualStabilityChangedEvent(float oldS, float newS) + { + OldStability = oldS; + NewStability = newS; + } +} + +/// +/// Called on both the ritual and the phase when they link together +/// +public sealed class CP14RitualPhaseBoundEvent : EntityEventArgs +{ + public EntityUid Ritual; + public EntityUid Phase; + + public CP14RitualPhaseBoundEvent(EntityUid r, EntityUid p) + { + Ritual = r; + Phase = p; + } +} + +/// +/// Invoked at the ritual holder entity when the ritual is complete and the phase entities have been removed +/// +public sealed class CP14RitualEndEvent : EntityEventArgs +{ + public EntityUid Ritual; + + public CP14RitualEndEvent(EntityUid r) + { + Ritual = r; + } +} + +/// +/// Invoked at the ritual holder entity when the ritual begins, and invokes the starting phase +/// +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, +} diff --git a/Content.Shared/_CP14/MagicRitual/CP14MagicRitualOrbComponent.cs b/Content.Shared/_CP14/MagicRitual/CP14MagicRitualOrbComponent.cs new file mode 100644 index 0000000000..fb82663d62 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/CP14MagicRitualOrbComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared._CP14.MagicRitual.Prototypes; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual; + +/// +/// “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. +/// +[RegisterComponent, Access(typeof(CP14SharedRitualSystem))] +public sealed partial class CP14MagicRitualOrbComponent : Component +{ + [DataField] + public Dictionary, int> Powers = new(); +} diff --git a/Content.Shared/_CP14/MagicRitual/CP14MagicRitualPhaseComponent.cs b/Content.Shared/_CP14/MagicRitual/CP14MagicRitualPhaseComponent.cs new file mode 100644 index 0000000000..1c51eb3173 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/CP14MagicRitualPhaseComponent.cs @@ -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; + +/// +/// Magical entity that reacts to world events +/// +[RegisterComponent, Access(typeof(CP14SharedRitualSystem))] +public sealed partial class CP14MagicRitualPhaseComponent : Component +{ + /// + /// A link to the ritual itself in which this phase is found + /// + [DataField] + public Entity? Ritual; + + [DataField] + public Color PhaseColor = Color.White; + + [DataField] + public List Edges = new(); + + /// + /// by moving to this node, the ritual will end instantly. + /// + [DataField] + public bool DeadEnd = false; +} + +[DataRecord] +public partial record struct RitualPhaseEdge() +{ + public EntProtoId Target { get; set; } + + public List Triggers { get; set; } = new(); + public List Requirements { get; set; } = new(); + public List Actions { get; set; } = new(); +} diff --git a/Content.Shared/_CP14/MagicRitual/CP14SharedRitualSystem.cs b/Content.Shared/_CP14/MagicRitual/CP14SharedRitualSystem.cs new file mode 100644 index 0000000000..18d943aaf3 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/CP14SharedRitualSystem.cs @@ -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 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 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(spawnedOrb, out var orbComp)) + { + QueueDel(spawnedOrb); + return; + } + + _followerSystem.StartFollowingEntity(spawnedOrb, ritual); + ritual.Comp.Orbs.Add((spawnedOrb, orbComp)); + } + + public void ConsumeOrbType(Entity ritual, ProtoId 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; + } + } +} diff --git a/Content.Shared/_CP14/MagicRitual/Prototypes/CP14MagicTypePrototype.cs b/Content.Shared/_CP14/MagicRitual/Prototypes/CP14MagicTypePrototype.cs new file mode 100644 index 0000000000..60da4bbf74 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Prototypes/CP14MagicTypePrototype.cs @@ -0,0 +1,19 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual.Prototypes; + +/// +/// A round-start setup preset, such as which antagonists to spawn. +/// +[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; +} diff --git a/Content.Shared/_CP14/MagicRitual/Requirements/CP14RitualRequirement.cs b/Content.Shared/_CP14/MagicRitual/Requirements/CP14RitualRequirement.cs new file mode 100644 index 0000000000..dafe4d5183 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Requirements/CP14RitualRequirement.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual.Requirements; + +[ImplicitDataDefinitionForInheritors] +[MeansImplicitUse] +public abstract partial class CP14RitualRequirement +{ + /// + /// If this checks fails, the ritual will lose some of its stability. + /// + [DataField] + public float FailStabilityCost; + + public abstract bool Check(EntityManager entManager, Entity phaseEnt, float stability); + + public abstract string? GetGuidebookRequirementDescription(IPrototypeManager prototype, IEntitySystemManager entSys); +} diff --git a/Content.Shared/_CP14/MagicRitual/Requirements/RequiredOrbs.cs b/Content.Shared/_CP14/MagicRitual/Requirements/RequiredOrbs.cs new file mode 100644 index 0000000000..c97b7bcd83 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Requirements/RequiredOrbs.cs @@ -0,0 +1,63 @@ +using System.Text; +using Content.Shared._CP14.MagicRitual.Prototypes; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual.Requirements; + +/// +/// Requires specific daytime period +/// +public sealed partial class RequiredOrbs : CP14RitualRequirement +{ + [DataField] + public ProtoId 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 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; + } +} diff --git a/Content.Shared/_CP14/MagicRitual/Requirements/RequiredResource.cs b/Content.Shared/_CP14/MagicRitual/Requirements/RequiredResource.cs new file mode 100644 index 0000000000..88a2ceb9d4 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Requirements/RequiredResource.cs @@ -0,0 +1,113 @@ +using System.Text; +using Content.Shared.Stacks; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual.Requirements; + +/// +/// Requires certain specific entities to be near the ritual. TODO: Replace with Whitelist +/// +public sealed partial class RequiredResource : CP14RitualRequirement +{ + [DataField] + public float CheckRange = 3f; + + [DataField] + public Dictionary RequiredEntities = new (); + + [DataField] + public Dictionary, int> RequiredStacks = new(); + + /// + /// Effect appearing in place of used entities + /// + [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 phaseEnt, float stability) + { + var _lookup = entManager.System(); + var _transform = entManager.System(); + + 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(entity, out var metaData)) + continue; + if (!entManager.TryGetComponent(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(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; + } +} diff --git a/Content.Shared/_CP14/MagicRitual/Requirements/RequiredStability.cs b/Content.Shared/_CP14/MagicRitual/Requirements/RequiredStability.cs new file mode 100644 index 0000000000..1bfd43d353 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Requirements/RequiredStability.cs @@ -0,0 +1,36 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitual.Requirements; + +/// +/// 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 +/// +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 phaseEnt, float stability) + { + if (phaseEnt.Comp.Ritual is null) + return false; + + var s = phaseEnt.Comp.Ritual.Value.Comp.Stability; + + return !(s < Min) && !(s > Max); + } +} diff --git a/Content.Shared/_CP14/MagicRitual/Requirements/RequiredTime.cs b/Content.Shared/_CP14/MagicRitual/Requirements/RequiredTime.cs new file mode 100644 index 0000000000..4263d66534 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitual/Requirements/RequiredTime.cs @@ -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; + +/// +/// Requires specific daytime +/// +public sealed partial class RequiredTime : CP14RitualRequirement +{ + [DataField] + public ProtoId 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 phaseEnt, float stability) + { + var transform = entManager.System(); + var map = transform.GetMap(phaseEnt.Owner); + + if (!entManager.TryGetComponent(map, out var dayCycle)) + return false; + + return TimePeriod == dayCycle.CurrentPeriod; + } +} diff --git a/Content.Shared/_CP14/MagicRitualTrigger/CP14RitualTrigger.cs b/Content.Shared/_CP14/MagicRitualTrigger/CP14RitualTrigger.cs new file mode 100644 index 0000000000..a86b942ed5 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitualTrigger/CP14RitualTrigger.cs @@ -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 ritual, RitualPhaseEdge edge); + + public abstract string? GetGuidebookTriggerDescription(IPrototypeManager prototype, IEntitySystemManager entSys); +} diff --git a/Content.Shared/_CP14/MagicRitualTrigger/CP14SharedRitualTriggerSystem.cs b/Content.Shared/_CP14/MagicRitualTrigger/CP14SharedRitualTriggerSystem.cs new file mode 100644 index 0000000000..49e3a8f0d3 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitualTrigger/CP14SharedRitualTriggerSystem.cs @@ -0,0 +1,5 @@ +namespace Content.Shared._CP14.MagicRitualTrigger; + +public partial class CP14SharedRitualTriggerSystem : EntitySystem +{ +} diff --git a/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeSpeciesTrigger.cs b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeSpeciesTrigger.cs new file mode 100644 index 0000000000..f2f6629fb3 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeSpeciesTrigger.cs @@ -0,0 +1,34 @@ +using Content.Shared._CP14.MagicRitual; +using Content.Shared.Humanoid.Prototypes; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitualTrigger.Triggers; + +/// +/// triggers when a creature of a certain race dies within range of the ritual. +/// +public sealed partial class CP14SacrificeSpeciesTrigger : CP14RitualTrigger +{ + [DataField] + public float Range = 3f; + + [DataField(required: true)] + public ProtoId Species = default!; + + public override void Initialize(EntityManager entManager, Entity ritual, RitualPhaseEdge edge) + { + entManager.EnsureComponent(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)); + } +} diff --git a/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeSpeciesTriggerComponent.cs b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeSpeciesTriggerComponent.cs new file mode 100644 index 0000000000..cb92dc1cd8 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeSpeciesTriggerComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared._CP14.MagicRitualTrigger.Triggers; + +[RegisterComponent] +public sealed partial class CP14RitualSacrificeSpeciesTriggerComponent : Component +{ + [DataField] + public HashSet Triggers = new(); +} diff --git a/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeWhitelistTrigger.cs b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeWhitelistTrigger.cs new file mode 100644 index 0000000000..7b8b29cc24 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeWhitelistTrigger.cs @@ -0,0 +1,34 @@ +using Content.Shared._CP14.MagicRitual; +using Content.Shared.Whitelist; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitualTrigger.Triggers; + +/// +/// Triggers when a creature passing the whitelist dies within range of the ritual. +/// +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 ritual, RitualPhaseEdge edge) + { + entManager.EnsureComponent(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)); + } +} diff --git a/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeWhitelistTriggerComponent.cs b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeWhitelistTriggerComponent.cs new file mode 100644 index 0000000000..20cad400cb --- /dev/null +++ b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualSacrificeWhitelistTriggerComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared._CP14.MagicRitualTrigger.Triggers; + +[RegisterComponent] +public sealed partial class CP14RitualSacrificeWhitelistTriggerComponent : Component +{ + [DataField] + public HashSet Triggers = new(); +} diff --git a/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualTimerTrigger.cs b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualTimerTrigger.cs new file mode 100644 index 0000000000..2c84afb3ec --- /dev/null +++ b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualTimerTrigger.cs @@ -0,0 +1,28 @@ +using Content.Shared._CP14.MagicRitual; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitualTrigger.Triggers; + +/// +/// Triggers the phase transition after a certain period of time +/// +public sealed partial class CP14TimerTrigger : CP14RitualTrigger +{ + [DataField] + public float Delay = 10f; + + [DataField] + public TimeSpan TriggerTime = TimeSpan.Zero; + + public override void Initialize(EntityManager entManager, Entity ritual, RitualPhaseEdge edge) + { + entManager.EnsureComponent(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)); + } +} diff --git a/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualTimerTriggerComponent.cs b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualTimerTriggerComponent.cs new file mode 100644 index 0000000000..c1293c83ac --- /dev/null +++ b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualTimerTriggerComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared._CP14.MagicRitualTrigger.Triggers; + +[RegisterComponent] +public sealed partial class CP14RitualTimerTriggerComponent : Component +{ + [DataField] + public HashSet Triggers = new(); +} diff --git a/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualVoiceTrigger.cs b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualVoiceTrigger.cs new file mode 100644 index 0000000000..3793c64761 --- /dev/null +++ b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualVoiceTrigger.cs @@ -0,0 +1,28 @@ +using Content.Shared._CP14.MagicRitual; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicRitualTrigger.Triggers; + +/// +/// Triggers a phase transition when the Ritual hears a certain message +/// +public sealed partial class CP14VoiceTrigger : CP14RitualTrigger +{ + [DataField] + public string Message = string.Empty; + + [DataField] + public int Speakers = 1; + + public override void Initialize(EntityManager entManager, Entity ritual, RitualPhaseEdge edge) + { + entManager.EnsureComponent(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)); + } +} diff --git a/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualVoiceTriggerComponent.cs b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualVoiceTriggerComponent.cs new file mode 100644 index 0000000000..9704086dbe --- /dev/null +++ b/Content.Shared/_CP14/MagicRitualTrigger/Triggers/CP14RitualVoiceTriggerComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared._CP14.MagicRitualTrigger.Triggers; + +[RegisterComponent] +public sealed partial class CP14RitualVoiceTriggerComponent : Component +{ + [DataField] + public HashSet Triggers = new(); +} diff --git a/Content.Shared/_CP14/MeleeWeapon/Components/CP14MeleeParriableComponent.cs b/Content.Shared/_CP14/MeleeWeapon/Components/CP14MeleeParriableComponent.cs index 319cf20cad..8028815da2 100644 --- a/Content.Shared/_CP14/MeleeWeapon/Components/CP14MeleeParriableComponent.cs +++ b/Content.Shared/_CP14/MeleeWeapon/Components/CP14MeleeParriableComponent.cs @@ -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)); } diff --git a/Content.Shared/_CP14/Random/Rules/IsDaylight.cs b/Content.Shared/_CP14/Random/Rules/IsDaylight.cs index 869ee7d2f5..79dbde4d3b 100644 --- a/Content.Shared/_CP14/Random/Rules/IsDaylight.cs +++ b/Content.Shared/_CP14/Random/Rules/IsDaylight.cs @@ -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; /// -/// 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. /// -public sealed partial class IsDaylight : RulesRule +public sealed partial class CP14TimePeriod : RulesRule { + [DataField] private List> Periods = new(); + public override bool Check(EntityManager entManager, EntityUid uid) { - var dayCycle = entManager.System(); + var transform = entManager.System(); - if (Inverted) - return !dayCycle.TryDaylightThere(uid, true); - else - return dayCycle.TryDaylightThere(uid, true); + var map = transform.GetMap(uid); + return entManager.TryGetComponent(map, out var dayCycle) && Periods.Contains(dayCycle.CurrentPeriod); } } diff --git a/Resources/Audio/_CP14/Ambience/Loops/attributions.yml b/Resources/Audio/_CP14/Ambience/Loops/attributions.yml index 3aced15a86..96f4000b26 100644 --- a/Resources/Audio/_CP14/Ambience/Loops/attributions.yml +++ b/Resources/Audio/_CP14/Ambience/Loops/attributions.yml @@ -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/" \ No newline at end of file + 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/" \ No newline at end of file diff --git a/Resources/Audio/_CP14/Ambience/Loops/cave.ogg b/Resources/Audio/_CP14/Ambience/Loops/cave.ogg new file mode 100644 index 0000000000..091e6bce92 Binary files /dev/null and b/Resources/Audio/_CP14/Ambience/Loops/cave.ogg differ diff --git a/Resources/Audio/_CP14/Ambience/ambimagic1.ogg b/Resources/Audio/_CP14/Ambience/ambimagic1.ogg new file mode 100644 index 0000000000..e785f692de Binary files /dev/null and b/Resources/Audio/_CP14/Ambience/ambimagic1.ogg differ diff --git a/Resources/Audio/_CP14/Ambience/ambimagic2.ogg b/Resources/Audio/_CP14/Ambience/ambimagic2.ogg new file mode 100644 index 0000000000..305374e52d Binary files /dev/null and b/Resources/Audio/_CP14/Ambience/ambimagic2.ogg differ diff --git a/Resources/Audio/_CP14/Ambience/ambimagic3.ogg b/Resources/Audio/_CP14/Ambience/ambimagic3.ogg new file mode 100644 index 0000000000..63dcbe555b Binary files /dev/null and b/Resources/Audio/_CP14/Ambience/ambimagic3.ogg differ diff --git a/Resources/Audio/_CP14/Ambience/ambimagic4.ogg b/Resources/Audio/_CP14/Ambience/ambimagic4.ogg new file mode 100644 index 0000000000..7e92e046c2 Binary files /dev/null and b/Resources/Audio/_CP14/Ambience/ambimagic4.ogg differ diff --git a/Resources/Audio/_CP14/Ambience/ambimagic5.ogg b/Resources/Audio/_CP14/Ambience/ambimagic5.ogg new file mode 100644 index 0000000000..8c469f77a8 Binary files /dev/null and b/Resources/Audio/_CP14/Ambience/ambimagic5.ogg differ diff --git a/Resources/Audio/_CP14/Ambience/ambimagic6.ogg b/Resources/Audio/_CP14/Ambience/ambimagic6.ogg new file mode 100644 index 0000000000..37f6f7cfc2 Binary files /dev/null and b/Resources/Audio/_CP14/Ambience/ambimagic6.ogg differ diff --git a/Resources/Audio/_CP14/Ambience/attributions.yml b/Resources/Audio/_CP14/Ambience/attributions.yml index 36b45a0b6e..ec34617556 100644 --- a/Resources/Audio/_CP14/Ambience/attributions.yml +++ b/Resources/Audio/_CP14/Ambience/attributions.yml @@ -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/" \ No newline at end of file + 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/" \ No newline at end of file diff --git a/Resources/Audio/_CP14/Ambience/weatherWindy.ogg b/Resources/Audio/_CP14/Ambience/weatherWindy.ogg deleted file mode 100644 index 9b1cb2e514..0000000000 Binary files a/Resources/Audio/_CP14/Ambience/weatherWindy.ogg and /dev/null differ diff --git a/Resources/Audio/_CP14/Effects/attributions.yml b/Resources/Audio/_CP14/Effects/attributions.yml index 20ab5bd5fe..d5d8957d43 100644 --- a/Resources/Audio/_CP14/Effects/attributions.yml +++ b/Resources/Audio/_CP14/Effects/attributions.yml @@ -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/" \ No newline at end of file +- 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/" \ No newline at end of file diff --git a/Resources/Audio/_CP14/Effects/parry1.ogg b/Resources/Audio/_CP14/Effects/parry1.ogg index 0954342e82..0c69a53b0f 100644 Binary files a/Resources/Audio/_CP14/Effects/parry1.ogg and b/Resources/Audio/_CP14/Effects/parry1.ogg differ diff --git a/Resources/Audio/_CP14/Effects/parry2.ogg b/Resources/Audio/_CP14/Effects/parry2.ogg new file mode 100644 index 0000000000..cfdacee4bf Binary files /dev/null and b/Resources/Audio/_CP14/Effects/parry2.ogg differ diff --git a/Resources/Audio/_CP14/Items/attributions.yml b/Resources/Audio/_CP14/Items/attributions.yml index bff67cf737..cfdc40a237 100644 --- a/Resources/Audio/_CP14/Items/attributions.yml +++ b/Resources/Audio/_CP14/Items/attributions.yml @@ -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/" \ No newline at end of file + 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/" \ No newline at end of file diff --git a/Resources/Audio/_CP14/Items/book1.ogg b/Resources/Audio/_CP14/Items/book1.ogg new file mode 100644 index 0000000000..a7ef8ae188 Binary files /dev/null and b/Resources/Audio/_CP14/Items/book1.ogg differ diff --git a/Resources/Audio/_CP14/Items/book2.ogg b/Resources/Audio/_CP14/Items/book2.ogg new file mode 100644 index 0000000000..dfd21f2095 Binary files /dev/null and b/Resources/Audio/_CP14/Items/book2.ogg differ diff --git a/Resources/Locale/en-US/_CP14/daycycle/periods.ftl b/Resources/Locale/en-US/_CP14/daycycle/periods.ftl new file mode 100644 index 0000000000..4b20ebad4e --- /dev/null +++ b/Resources/Locale/en-US/_CP14/daycycle/periods.ftl @@ -0,0 +1,4 @@ +cp14-daycycle-sunrise = sunrise +cp14-daycycle-day = day +cp14-daycycle-night = night +cp14-daycycle-evening = evening \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/magicRitual/base.ftl b/Resources/Locale/en-US/_CP14/magicRitual/base.ftl new file mode 100644 index 0000000000..fb1a4b09df --- /dev/null +++ b/Resources/Locale/en-US/_CP14/magicRitual/base.ftl @@ -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.) + } \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/magicRitual/effects.ftl b/Resources/Locale/en-US/_CP14/magicRitual/effects.ftl new file mode 100644 index 0000000000..1ad3448d0b --- /dev/null +++ b/Resources/Locale/en-US/_CP14/magicRitual/effects.ftl @@ -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} \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/magicRitual/requirements.ftl b/Resources/Locale/en-US/_CP14/magicRitual/requirements.ftl new file mode 100644 index 0000000000..d77752fde1 --- /dev/null +++ b/Resources/Locale/en-US/_CP14/magicRitual/requirements.ftl @@ -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} \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/magicRitual/triggers.ftl b/Resources/Locale/en-US/_CP14/magicRitual/triggers.ftl new file mode 100644 index 0000000000..5f49f584a0 --- /dev/null +++ b/Resources/Locale/en-US/_CP14/magicRitual/triggers.ftl @@ -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. \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/magicTypes/magic.ftl b/Resources/Locale/en-US/_CP14/magicTypes/magic.ftl new file mode 100644 index 0000000000..a7b53e0b82 --- /dev/null +++ b/Resources/Locale/en-US/_CP14/magicTypes/magic.ftl @@ -0,0 +1,2 @@ +cp14-magic-type-creation = Creation +cp14-magic-type-destruction = Destruction \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/daycycle/periods.ftl b/Resources/Locale/ru-RU/_CP14/daycycle/periods.ftl new file mode 100644 index 0000000000..a05012d007 --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/daycycle/periods.ftl @@ -0,0 +1,4 @@ +cp14-daycycle-sunrise = рассвет +cp14-daycycle-day = день +cp14-daycycle-night = ночь +cp14-daycycle-evening = вечер \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/magicRitual/base.ftl b/Resources/Locale/ru-RU/_CP14/magicRitual/base.ftl new file mode 100644 index 0000000000..b0916ef817 --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/magicRitual/base.ftl @@ -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} шт.) + } \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/magicRitual/effects.ftl b/Resources/Locale/ru-RU/_CP14/magicRitual/effects.ftl new file mode 100644 index 0000000000..bbb1046b32 --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/magicRitual/effects.ftl @@ -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} \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/magicRitual/requirements.ftl b/Resources/Locale/ru-RU/_CP14/magicRitual/requirements.ftl new file mode 100644 index 0000000000..1329d6d5bf --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/magicRitual/requirements.ftl @@ -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} \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/magicRitual/triggers.ftl b/Resources/Locale/ru-RU/_CP14/magicRitual/triggers.ftl new file mode 100644 index 0000000000..ca77de24a6 --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/magicRitual/triggers.ftl @@ -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} от места проведения ритуала. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/magicTypes/magic.ftl b/Resources/Locale/ru-RU/_CP14/magicTypes/magic.ftl new file mode 100644 index 0000000000..d6909852d2 --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/magicTypes/magic.ftl @@ -0,0 +1,2 @@ +cp14-magic-type-creation = Созидание +cp14-magic-type-destruction = Разрушение \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/DayCycle/periods.yml b/Resources/Prototypes/_CP14/DayCycle/periods.yml index 7881923e29..6045d5c4ba 100644 --- a/Resources/Prototypes/_CP14/DayCycle/periods.yml +++ b/Resources/Prototypes/_CP14/DayCycle/periods.yml @@ -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 diff --git a/Resources/Prototypes/_CP14/Entities/Objects/Specific/Thaumaturgy/tools.yml b/Resources/Prototypes/_CP14/Entities/Objects/Specific/Thaumaturgy/tools.yml index e505dca6a2..e4808ba7cd 100644 --- a/Resources/Prototypes/_CP14/Entities/Objects/Specific/Thaumaturgy/tools.yml +++ b/Resources/Prototypes/_CP14/Entities/Objects/Specific/Thaumaturgy/tools.yml @@ -16,4 +16,16 @@ params: variation: 0.03 - type: UseDelay - delay: 4 \ No newline at end of file + 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 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Objects/ritual_describer.yml b/Resources/Prototypes/_CP14/Entities/Objects/ritual_describer.yml new file mode 100644 index 0000000000..9df2dfbac1 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Objects/ritual_describer.yml @@ -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 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Ritual/Graph/neutral_cluster.yml b/Resources/Prototypes/_CP14/Entities/Ritual/Graph/neutral_cluster.yml new file mode 100644 index 0000000000..3b24ea224e --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Ritual/Graph/neutral_cluster.yml @@ -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 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Ritual/basic.yml b/Resources/Prototypes/_CP14/Entities/Ritual/basic.yml new file mode 100644 index 0000000000..121a766ae1 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Ritual/basic.yml @@ -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 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Ritual/orbs.yml b/Resources/Prototypes/_CP14/Entities/Ritual/orbs.yml new file mode 100644 index 0000000000..88061ae14d --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Ritual/orbs.yml @@ -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 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Structures/Decorations/statues.yml b/Resources/Prototypes/_CP14/Entities/Structures/Decorations/statues.yml index 576c5735d2..f89de8dcf1 100644 --- a/Resources/Prototypes/_CP14/Entities/Structures/Decorations/statues.yml +++ b/Resources/Prototypes/_CP14/Entities/Structures/Decorations/statues.yml @@ -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 diff --git a/Resources/Prototypes/_CP14/Entities/Structures/Flora/Gatherable/wild.yml b/Resources/Prototypes/_CP14/Entities/Structures/Flora/Gatherable/wild.yml index c76fcdb03f..08d42f1226 100644 --- a/Resources/Prototypes/_CP14/Entities/Structures/Flora/Gatherable/wild.yml +++ b/Resources/Prototypes/_CP14/Entities/Structures/Flora/Gatherable/wild.yml @@ -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 \ No newline at end of file + All: CP14GatherBlueAmanita diff --git a/Resources/Prototypes/_CP14/Entities/Structures/Furniture/wallmount.yml b/Resources/Prototypes/_CP14/Entities/Structures/Furniture/wallmount.yml index 1ad7a5f129..1131384275 100644 --- a/Resources/Prototypes/_CP14/Entities/Structures/Furniture/wallmount.yml +++ b/Resources/Prototypes/_CP14/Entities/Structures/Furniture/wallmount.yml @@ -11,6 +11,8 @@ - type: Sprite drawdepth: Mobs - type: CP14Wallmount + - type: Physics + canCollide: false - type: entity id: CP14WallmountTorch diff --git a/Resources/Prototypes/_CP14/Entities/Structures/crystal.yml b/Resources/Prototypes/_CP14/Entities/Structures/crystal.yml index ce9e4b2def..d6a67488ee 100644 --- a/Resources/Prototypes/_CP14/Entities/Structures/crystal.yml +++ b/Resources/Prototypes/_CP14/Entities/Structures/crystal.yml @@ -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 diff --git a/Resources/Prototypes/_CP14/MagicTypes/magic.yml b/Resources/Prototypes/_CP14/MagicTypes/magic.yml new file mode 100644 index 0000000000..c4f36ec62e --- /dev/null +++ b/Resources/Prototypes/_CP14/MagicTypes/magic.yml @@ -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" \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Procedural/biome_template.yml b/Resources/Prototypes/_CP14/Procedural/biome_template.yml index d45472f547..67d7744caf 100644 --- a/Resources/Prototypes/_CP14/Procedural/biome_template.yml +++ b/Resources/Prototypes/_CP14/Procedural/biome_template.yml @@ -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: diff --git a/Resources/Prototypes/_CP14/SoundCollections/misc.yml b/Resources/Prototypes/_CP14/SoundCollections/items.yml similarity index 77% rename from Resources/Prototypes/_CP14/SoundCollections/misc.yml rename to Resources/Prototypes/_CP14/SoundCollections/items.yml index afda6d4ad5..533e6886ea 100644 --- a/Resources/Prototypes/_CP14/SoundCollections/misc.yml +++ b/Resources/Prototypes/_CP14/SoundCollections/items.yml @@ -35,4 +35,16 @@ files: - /Audio/_CP14/Items/broom1.ogg - /Audio/_CP14/Items/broom2.ogg - - /Audio/_CP14/Items/broom3.ogg \ No newline at end of file + - /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 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/audio_loops.yml b/Resources/Prototypes/_CP14/audio_loops.yml index 8185d240bd..0661c65e3f 100644 --- a/Resources/Prototypes/_CP14/audio_loops.yml +++ b/Resources/Prototypes/_CP14/audio_loops.yml @@ -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: diff --git a/Resources/Prototypes/_CP14/audio_music.yml b/Resources/Prototypes/_CP14/audio_music.yml index f941c2ae69..edbee084bf 100644 --- a/Resources/Prototypes/_CP14/audio_music.yml +++ b/Resources/Prototypes/_CP14/audio_music.yml @@ -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 \ No newline at end of file + - !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 \ No newline at end of file diff --git a/Resources/Textures/_CP14/Effects/Magic/ritual_orbs.rsi/creation.png b/Resources/Textures/_CP14/Effects/Magic/ritual_orbs.rsi/creation.png new file mode 100644 index 0000000000..fc1c7592c8 Binary files /dev/null and b/Resources/Textures/_CP14/Effects/Magic/ritual_orbs.rsi/creation.png differ diff --git a/Resources/Textures/_CP14/Effects/Magic/ritual_orbs.rsi/destruction.png b/Resources/Textures/_CP14/Effects/Magic/ritual_orbs.rsi/destruction.png new file mode 100644 index 0000000000..e17f392c02 Binary files /dev/null and b/Resources/Textures/_CP14/Effects/Magic/ritual_orbs.rsi/destruction.png differ diff --git a/Resources/Textures/_CP14/Effects/Magic/ritual_orbs.rsi/meta.json b/Resources/Textures/_CP14/Effects/Magic/ritual_orbs.rsi/meta.json new file mode 100644 index 0000000000..c221e7fe10 --- /dev/null +++ b/Resources/Textures/_CP14/Effects/Magic/ritual_orbs.rsi/meta.json @@ -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 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/icon.png b/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/icon.png new file mode 100644 index 0000000000..1a26bb0bdb Binary files /dev/null and b/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/icon.png differ diff --git a/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/inhand-left.png b/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/inhand-left.png new file mode 100644 index 0000000000..4705d2417c Binary files /dev/null and b/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/inhand-left.png differ diff --git a/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/inhand-right.png b/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/inhand-right.png new file mode 100644 index 0000000000..8166ee1a5c Binary files /dev/null and b/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/inhand-right.png differ diff --git a/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/meta.json b/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/meta.json new file mode 100644 index 0000000000..82588ec9cb --- /dev/null +++ b/Resources/Textures/_CP14/Objects/Specific/Thaumaturgy/ritual_chalk.rsi/meta.json @@ -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 + } + ] +} diff --git a/Resources/Textures/_CP14/Objects/Tools/rope.rsi/meta.json b/Resources/Textures/_CP14/Objects/Tools/rope.rsi/meta.json index b0a1db5a22..85885ae455 100644 --- a/Resources/Textures/_CP14/Objects/Tools/rope.rsi/meta.json +++ b/Resources/Textures/_CP14/Objects/Tools/rope.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CLA", - "copyright": "Created by omsoyk (Github/Discord)", + "copyright": "Created by TheShuEd (Github)", "size": { "x": 32, "y": 32 diff --git a/Resources/Textures/_CP14/Structures/Decoration/statue_gob.rsi/eyes.png b/Resources/Textures/_CP14/Structures/Decoration/statue_gob.rsi/eyes.png index 73a30731db..1cca785515 100644 Binary files a/Resources/Textures/_CP14/Structures/Decoration/statue_gob.rsi/eyes.png and b/Resources/Textures/_CP14/Structures/Decoration/statue_gob.rsi/eyes.png differ