From 422a0c5e1056d180fec997798096df4a47169fa2 Mon Sep 17 00:00:00 2001 From: Red <96445749+TheShuEd@users.noreply.github.com> Date: Fri, 13 Jun 2025 14:15:48 +0300 Subject: [PATCH] Gods and religions (#1405) * basic religion vision * block god interactions * skill tree mob specify * silvania setup?? * clustering shader optimization * gods department & job * random gods jobs * Luxian god * Update Nature.png * Update sphere_of_light.yml * god chat * Update ChatSystem.cs * OBSERVATION * public observation and basic follower api * shader tweaks * improve shaders * spawning and praying on altars * altars ppvs override * move pvs overridiation from altars to observers * shader coloration * spectral z mover * god magic radius restricted * guide how to believe in god * sends messages to god when smoeone wanna become follower * follower doAfter * goodbye luxian, welcome lumera * goodbye silvania, welcome merkas * som polish and renamings * gods fast travel * Update altar.ftl * some lumera sfx * renouncing patrons! * renounce followers * followewr percentage calculation * remove from player-facing * fix * Update sphere_of_light.yml * Update base.yml --- .../Religion/CP14ClientReligionGodSystem.cs | 83 ++++++++ .../CP14ReligionEntityBoundUserInterface.cs | 35 ++++ .../Religion/CP14ReligionEntityWindow.xaml | 28 +++ .../Religion/CP14ReligionEntityWindow.xaml.cs | 69 +++++++ .../Religion/CP14ReligionVisionOverlay.cs | 148 +++++++++++++++ .../Systems/Skill/CP14SkillUIController.cs | 28 +-- Content.Server/Chat/Systems/ChatSystem.cs | 14 ++ .../Components/SpawnPointComponent.cs | 3 + .../EntitySystems/SpawnPointSystem.cs | 9 + .../CP14DemiplaneTravelingSystem.cs | 3 + .../CP14StationRandomJobsComponent.cs | 26 +++ .../RandomJobs/CP14StationRandomJobsSystem.cs | 52 +++++ .../_CP14/Religion/CP14ReligionSystem.UI.cs | 75 ++++++++ .../_CP14/Religion/CP14ReligionSystem.cs | 177 ++++++++++++++++++ .../CP14StationZLevelsSystem.Actions.cs | 31 ++- Content.Shared/Verbs/VerbCategory.cs | 6 - .../CP14SharedMagicSystem.Checks.cs | 19 ++ ...4MagicEffectReligionRestrictedComponent.cs | 12 ++ .../MagicSpell/Spells/CP14SpellGodRenounce.cs | 23 +++ .../MagicSpell/Spells/CP14SpellGodTouch.cs | 24 +++ .../Components/CP14ReligionAltarComponent.cs | 16 ++ .../Components/CP14ReligionEntityComponent.cs | 27 +++ .../CP14ReligionFollowerComponent.cs | 25 +++ .../CP14ReligionObserverComponent.cs | 19 ++ .../CP14ReligionPendingFollowerComponent.cs | 17 ++ .../Components/CP14ReligionVisionComponent.cs | 14 ++ .../Prototypes/CP14ReligionPrototype.cs | 18 ++ .../CP14SharedReligionGodSystem.Altars.cs | 106 +++++++++++ .../CP14SharedReligionGodSystem.Followers.cs | 154 +++++++++++++++ ...CP14SharedReligionGodSystem.Observation.cs | 96 ++++++++++ .../Systems/CP14SharedReligionGodSystem.UI.cs | 23 +++ .../Systems/CP14SharedReligionGodSystem.cs | 46 +++++ .../_CP14/Skill/CP14SharedSkillSystem.cs | 4 + .../_CP14/Skill/CP14SkillSystem.Admin.cs | 42 ----- .../Components/CP14SkillStorageComponent.cs | 8 +- .../Prototypes/CP14SkillTreePrototype.cs | 3 - .../Restrictions/GodFollowerPercentage.cs | 27 +++ .../Locale/en-US/_CP14/headset/radio.ftl | 1 + .../Locale/en-US/_CP14/job/department.ftl | 5 +- Resources/Locale/en-US/_CP14/job/job.ftl | 10 +- .../Locale/en-US/_CP14/religion/altar.ftl | 19 ++ .../Locale/en-US/_CP14/skill/requirements.ftl | 3 +- .../en-US/_CP14/skill/skill_meta_gods.ftl | 3 + .../Locale/ru-RU/_CP14/headset/radio.ftl | 1 + .../Locale/ru-RU/_CP14/job/department.ftl | 5 +- Resources/Locale/ru-RU/_CP14/job/job.ftl | 10 +- .../Locale/ru-RU/_CP14/religion/altar.ftl | 19 ++ .../Locale/ru-RU/_CP14/skill/requirements.ftl | 3 +- .../ru-RU/_CP14/skill/skill_meta_gods.ftl | 3 + Resources/Prototypes/_CP14/Alerts/alerts.yml | 8 + .../Spells/Demigods/Lumera/renounce.yml | 45 +++++ .../Actions/Spells/Demigods/Lumera/touch.yml | 49 +++++ .../Actions/Spells/Demigods/Lumera/warp.yml | 12 ++ .../Actions/Spells/Demigods/generic.yml | 13 ++ .../Entities/Mobs/Player/Demigods/gods.yml | 113 +++++++++++ .../_CP14/Entities/Mobs/Species/base.yml | 1 - .../Prototypes/_CP14/Entities/Mobs/base.yml | 10 + .../_CP14/Entities/Stations/base.yml | 14 ++ .../_CP14/Entities/Structures/Flora/trees.yml | 1 - .../Structures/Specific/Religion/base.yml | 35 ++++ .../Specific/Religion/primordial.yml | 35 ++++ .../Prototypes/_CP14/Religion/religions.yml | 5 + .../_CP14/Roles/Jobs/Demigods/lumera.yml | 16 ++ .../_CP14/Roles/Jobs/Demigods/merkas.yml | 16 ++ .../_CP14/Roles/Jobs/departments.yml | 16 +- .../_CP14/Roles/play_time_tracker.yml | 6 + .../Prototypes/_CP14/Shaders/shaders.yml | 5 + .../_CP14/Skill/{ => Basic}/atlethic.yml | 0 .../_CP14/Skill/{ => Basic}/craftsmanship.yml | 0 .../_CP14/Skill/{ => Basic}/dimension.yml | 0 .../_CP14/Skill/{ => Basic}/healing.yml | 0 .../Skill/{ => Basic}/hydrosophistry.yml | 0 .../_CP14/Skill/{ => Basic}/illusion.yml | 0 .../_CP14/Skill/{ => Basic}/martial_arts.yml | 0 .../_CP14/Skill/{ => Basic}/metamagic.yml | 0 .../_CP14/Skill/{ => Basic}/pyrokinetic.yml | 0 .../Skill/{skill_tree.yml => Basic/trees.yml} | 24 --- .../_CP14/Skill/Demigods/lumera.yml | 75 ++++++++ .../_CP14/Skill/Demigods/merkas.yml | 12 ++ .../Prototypes/_CP14/Skill/Demigods/trees.yml | 12 ++ .../Prototypes/_CP14/StatusIcon/god_job.yml | 15 ++ Resources/Prototypes/_CP14/radio_channels.yml | 7 + .../DemigodSpells/lumera.rsi/meta.json | 29 +++ .../DemigodSpells/lumera.rsi/renounce.png | Bin 0 -> 422 bytes .../Actions/DemigodSpells/lumera.rsi/t1.png | Bin 0 -> 229 bytes .../Actions/DemigodSpells/lumera.rsi/t2.png | Bin 0 -> 358 bytes .../Actions/DemigodSpells/lumera.rsi/t3.png | Bin 0 -> 375 bytes .../DemigodSpells/lumera.rsi/touch.png | Bin 0 -> 359 bytes .../Actions/DemigodSpells/lumera.rsi/warp.png | Bin 0 -> 477 bytes .../Effects/Magic/cast_impact.rsi/meta.json | 15 ++ .../Effects/Magic/cast_impact.rsi/stars.png | Bin 0 -> 3230 bytes .../Alerts/divine_offer.rsi/meta.json | 19 ++ .../Alerts/divine_offer.rsi/offer.png | Bin 0 -> 396 bytes .../Alerts/divine_offer.rsi/unoffer.png | Bin 0 -> 776 bytes .../Misc/job_god_icons.rsi/Lumera.png | Bin 0 -> 247 bytes .../Misc/job_god_icons.rsi/Merkas.png | Bin 0 -> 214 bytes .../Misc/job_god_icons.rsi/meta.json | 17 ++ .../Mobs/Demigods/temp_icons.rsi/lumera.png | Bin 0 -> 1990 bytes .../Mobs/Demigods/temp_icons.rsi/merkas.png | Bin 0 -> 1096 bytes .../Mobs/Demigods/temp_icons.rsi/meta.json | 19 ++ .../Textures/_CP14/Shaders/religion.swsl | 84 +++++++++ 101 files changed, 2217 insertions(+), 103 deletions(-) create mode 100644 Content.Client/_CP14/Religion/CP14ClientReligionGodSystem.cs create mode 100644 Content.Client/_CP14/Religion/CP14ReligionEntityBoundUserInterface.cs create mode 100644 Content.Client/_CP14/Religion/CP14ReligionEntityWindow.xaml create mode 100644 Content.Client/_CP14/Religion/CP14ReligionEntityWindow.xaml.cs create mode 100644 Content.Client/_CP14/Religion/CP14ReligionVisionOverlay.cs create mode 100644 Content.Server/_CP14/RandomJobs/CP14StationRandomJobsComponent.cs create mode 100644 Content.Server/_CP14/RandomJobs/CP14StationRandomJobsSystem.cs create mode 100644 Content.Server/_CP14/Religion/CP14ReligionSystem.UI.cs create mode 100644 Content.Server/_CP14/Religion/CP14ReligionSystem.cs create mode 100644 Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectReligionRestrictedComponent.cs create mode 100644 Content.Shared/_CP14/MagicSpell/Spells/CP14SpellGodRenounce.cs create mode 100644 Content.Shared/_CP14/MagicSpell/Spells/CP14SpellGodTouch.cs create mode 100644 Content.Shared/_CP14/Religion/Components/CP14ReligionAltarComponent.cs create mode 100644 Content.Shared/_CP14/Religion/Components/CP14ReligionEntityComponent.cs create mode 100644 Content.Shared/_CP14/Religion/Components/CP14ReligionFollowerComponent.cs create mode 100644 Content.Shared/_CP14/Religion/Components/CP14ReligionObserverComponent.cs create mode 100644 Content.Shared/_CP14/Religion/Components/CP14ReligionPendingFollowerComponent.cs create mode 100644 Content.Shared/_CP14/Religion/Components/CP14ReligionVisionComponent.cs create mode 100644 Content.Shared/_CP14/Religion/Prototypes/CP14ReligionPrototype.cs create mode 100644 Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Altars.cs create mode 100644 Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Followers.cs create mode 100644 Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Observation.cs create mode 100644 Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.UI.cs create mode 100644 Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.cs create mode 100644 Content.Shared/_CP14/Skill/Restrictions/GodFollowerPercentage.cs create mode 100644 Resources/Locale/en-US/_CP14/headset/radio.ftl create mode 100644 Resources/Locale/en-US/_CP14/religion/altar.ftl create mode 100644 Resources/Locale/en-US/_CP14/skill/skill_meta_gods.ftl create mode 100644 Resources/Locale/ru-RU/_CP14/headset/radio.ftl create mode 100644 Resources/Locale/ru-RU/_CP14/religion/altar.ftl create mode 100644 Resources/Locale/ru-RU/_CP14/skill/skill_meta_gods.ftl create mode 100644 Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/renounce.yml create mode 100644 Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/touch.yml create mode 100644 Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/warp.yml create mode 100644 Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/generic.yml create mode 100644 Resources/Prototypes/_CP14/Entities/Mobs/Player/Demigods/gods.yml create mode 100644 Resources/Prototypes/_CP14/Entities/Structures/Specific/Religion/base.yml create mode 100644 Resources/Prototypes/_CP14/Entities/Structures/Specific/Religion/primordial.yml create mode 100644 Resources/Prototypes/_CP14/Religion/religions.yml create mode 100644 Resources/Prototypes/_CP14/Roles/Jobs/Demigods/lumera.yml create mode 100644 Resources/Prototypes/_CP14/Roles/Jobs/Demigods/merkas.yml rename Resources/Prototypes/_CP14/Skill/{ => Basic}/atlethic.yml (100%) rename Resources/Prototypes/_CP14/Skill/{ => Basic}/craftsmanship.yml (100%) rename Resources/Prototypes/_CP14/Skill/{ => Basic}/dimension.yml (100%) rename Resources/Prototypes/_CP14/Skill/{ => Basic}/healing.yml (100%) rename Resources/Prototypes/_CP14/Skill/{ => Basic}/hydrosophistry.yml (100%) rename Resources/Prototypes/_CP14/Skill/{ => Basic}/illusion.yml (100%) rename Resources/Prototypes/_CP14/Skill/{ => Basic}/martial_arts.yml (100%) rename Resources/Prototypes/_CP14/Skill/{ => Basic}/metamagic.yml (100%) rename Resources/Prototypes/_CP14/Skill/{ => Basic}/pyrokinetic.yml (100%) rename Resources/Prototypes/_CP14/Skill/{skill_tree.yml => Basic/trees.yml} (75%) create mode 100644 Resources/Prototypes/_CP14/Skill/Demigods/lumera.yml create mode 100644 Resources/Prototypes/_CP14/Skill/Demigods/merkas.yml create mode 100644 Resources/Prototypes/_CP14/Skill/Demigods/trees.yml create mode 100644 Resources/Prototypes/_CP14/StatusIcon/god_job.yml create mode 100644 Resources/Prototypes/_CP14/radio_channels.yml create mode 100644 Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/meta.json create mode 100644 Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/renounce.png create mode 100644 Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/t1.png create mode 100644 Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/t2.png create mode 100644 Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/t3.png create mode 100644 Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/touch.png create mode 100644 Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/warp.png create mode 100644 Resources/Textures/_CP14/Effects/Magic/cast_impact.rsi/stars.png create mode 100644 Resources/Textures/_CP14/Interface/Alerts/divine_offer.rsi/meta.json create mode 100644 Resources/Textures/_CP14/Interface/Alerts/divine_offer.rsi/offer.png create mode 100644 Resources/Textures/_CP14/Interface/Alerts/divine_offer.rsi/unoffer.png create mode 100644 Resources/Textures/_CP14/Interface/Misc/job_god_icons.rsi/Lumera.png create mode 100644 Resources/Textures/_CP14/Interface/Misc/job_god_icons.rsi/Merkas.png create mode 100644 Resources/Textures/_CP14/Interface/Misc/job_god_icons.rsi/meta.json create mode 100644 Resources/Textures/_CP14/Mobs/Demigods/temp_icons.rsi/lumera.png create mode 100644 Resources/Textures/_CP14/Mobs/Demigods/temp_icons.rsi/merkas.png create mode 100644 Resources/Textures/_CP14/Mobs/Demigods/temp_icons.rsi/meta.json create mode 100644 Resources/Textures/_CP14/Shaders/religion.swsl diff --git a/Content.Client/_CP14/Religion/CP14ClientReligionGodSystem.cs b/Content.Client/_CP14/Religion/CP14ClientReligionGodSystem.cs new file mode 100644 index 0000000000..3be4e03fdb --- /dev/null +++ b/Content.Client/_CP14/Religion/CP14ClientReligionGodSystem.cs @@ -0,0 +1,83 @@ +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared._CP14.Religion.Systems; +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Client._CP14.Religion; + +public sealed partial class CP14ClientReligionGodSystem : CP14SharedReligionGodSystem +{ + [Dependency] private readonly IOverlayManager _overlayMgr = default!; + [Dependency] private readonly IPlayerManager _player = default!; + + private CP14ReligionVisionOverlay? _overlay; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + SubscribeLocalEvent(OnOverlayInit); + SubscribeLocalEvent(OnOverlayRemove); + } + + protected override void SendMessageToGods(ProtoId religion, string msg, EntityUid source) { } + + public override void Shutdown() + { + base.Shutdown(); + _overlayMgr.RemoveOverlay(); + } + + private void OnPlayerAttached(Entity ent, ref LocalPlayerAttachedEvent args) + { + AddOverlay(); + } + + private void OnPlayerDetached(Entity ent, ref LocalPlayerDetachedEvent args) + { + RemoveOverlay(); + } + + private void OnOverlayInit(Entity ent, ref ComponentInit args) + { + var attachedEnt = _player.LocalEntity; + + if (attachedEnt != ent.Owner) + return; + + AddOverlay(); + } + + private void OnOverlayRemove(Entity ent, ref ComponentRemove args) + { + var attachedEnt = _player.LocalEntity; + + if (attachedEnt != ent.Owner) + return; + + RemoveOverlay(); + } + + private void AddOverlay() + { + if (_overlay != null) + return; + + _overlay = new CP14ReligionVisionOverlay(); + _overlayMgr.AddOverlay(_overlay); + } + + private void RemoveOverlay() + { + if (_overlay == null) + return; + + _overlayMgr.RemoveOverlay(_overlay); + _overlay = null; + } +} diff --git a/Content.Client/_CP14/Religion/CP14ReligionEntityBoundUserInterface.cs b/Content.Client/_CP14/Religion/CP14ReligionEntityBoundUserInterface.cs new file mode 100644 index 0000000000..ba517f05d8 --- /dev/null +++ b/Content.Client/_CP14/Religion/CP14ReligionEntityBoundUserInterface.cs @@ -0,0 +1,35 @@ +using Content.Client._CP14.DemiplaneTraveling; +using Content.Shared._CP14.DemiplaneTraveling; +using Content.Shared._CP14.Religion.Systems; +using Robust.Client.UserInterface; + +namespace Content.Client._CP14.Religion; + +public sealed class CP14ReligionEntityBoundUserInterface : BoundUserInterface +{ + private CP14ReligionEntityWindow? _window; + + public CP14ReligionEntityBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + IoCManager.InjectDependencies(this); + } + + protected override void Open() + { + base.Open(); + + _window = this.CreateWindow(); + + _window.OnTeleportAttempt += netId => SendMessage(new CP14ReligionEntityTeleportAttempt(netId)); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (_window == null || state is not CP14ReligionEntityUiState mapState) + return; + + _window?.UpdateState(mapState); + } +} diff --git a/Content.Client/_CP14/Religion/CP14ReligionEntityWindow.xaml b/Content.Client/_CP14/Religion/CP14ReligionEntityWindow.xaml new file mode 100644 index 0000000000..5242a4bbdf --- /dev/null +++ b/Content.Client/_CP14/Religion/CP14ReligionEntityWindow.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + diff --git a/Content.Client/_CP14/Religion/CP14ReligionEntityWindow.xaml.cs b/Content.Client/_CP14/Religion/CP14ReligionEntityWindow.xaml.cs new file mode 100644 index 0000000000..18f98380bb --- /dev/null +++ b/Content.Client/_CP14/Religion/CP14ReligionEntityWindow.xaml.cs @@ -0,0 +1,69 @@ +using Content.Shared._CP14.Religion.Systems; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client._CP14.Religion; + +[GenerateTypedNameReferences] +public sealed partial class CP14ReligionEntityWindow : DefaultWindow +{ + [Dependency] private readonly ILogManager _log = default!; + private ISawmill Sawmill { get; init; } + + public event Action? OnTeleportAttempt; + + public CP14ReligionEntityWindow() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + Sawmill = _log.GetSawmill("cp14_religion_entity_window"); + } + + public void UpdateState(CP14ReligionEntityUiState state) + { + AltarsContainer.RemoveAllChildren(); + FollowersContainer.RemoveAllChildren(); + + Altars.Visible = state.Altars.Count > 0; + Followers.Visible = state.Followers.Count > 0; + + foreach (var (netId, name) in state.Altars) + { + var btn = new Button + { + Text = name, + HorizontalAlignment = HAlignment.Center + }; + + btn.OnPressed += _ => + { + OnTeleportAttempt?.Invoke(netId); + }; + AltarsContainer.AddChild(btn); + } + + foreach (var (netId, name) in state.Followers) + { + var btn = new Button + { + Text = name, + HorizontalAlignment = HAlignment.Center + }; + + btn.OnPressed += _ => + { + OnTeleportAttempt?.Invoke(netId); + }; + FollowersContainer.AddChild(btn); + } + Status.Text = GetStatusText(state); + } + + private string GetStatusText(CP14ReligionEntityUiState state) + { + return Loc.GetString("cp14-god-ui-follower-percentage", ("count", state.FollowerPercentage * 100)); + } +} diff --git a/Content.Client/_CP14/Religion/CP14ReligionVisionOverlay.cs b/Content.Client/_CP14/Religion/CP14ReligionVisionOverlay.cs new file mode 100644 index 0000000000..ce1b5405fc --- /dev/null +++ b/Content.Client/_CP14/Religion/CP14ReligionVisionOverlay.cs @@ -0,0 +1,148 @@ +using System.Numerics; +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Prototypes; +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; + +namespace Content.Client._CP14.Religion; + +public sealed class CP14ReligionVisionOverlay : Overlay +{ + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + + private readonly SharedTransformSystem _transform; + + /// + /// Maximum number of observers zones that can be shown on screen at a time. + /// If this value is changed, the shader itself also needs to be updated. + /// + public const int MaxCount = 64; + + public override bool RequestScreenTexture => true; + public override OverlaySpace Space => OverlaySpace.WorldSpace; + + private readonly ProtoId? _religion = null; + + private readonly ShaderInstance _religionShader; + private readonly Vector2[] _positions = new Vector2[MaxCount]; + private readonly float[] _radii = new float[MaxCount]; + private int _count = 0; + public CP14ReligionVisionOverlay() + { + IoCManager.InjectDependencies(this); + + _religionShader = _proto.Index("CP14ReligionVision").InstanceUnique(); + + _transform = _entManager.System(); + + if (_entManager.TryGetComponent(_player.LocalEntity, out var vision)) + { + _religion = vision.Religion; + } + } + + + protected override bool BeforeDraw(in OverlayDrawArgs args) + { + if (args.Viewport.Eye == null) + return false; + + _count = 0; + + var clusters = new List(); + var religionQuery = _entManager.AllEntityQueryEnumerator(); + while (religionQuery.MoveNext(out var uid, out var rel, out var xform)) + { + if (_religion is null) + continue; + + var observation = rel.Observation; + if (!observation.ContainsKey(_religion.Value)) + continue; + + if (!rel.Active || xform.MapID != args.MapId) + continue; + + var mapPos = _transform.GetWorldPosition(uid); + + // To be clear, this needs to use "inside-viewport" pixels. + // In other words, specifically NOT IViewportControl.WorldToScreen (which uses outer coordinates). + var tempCoords = args.Viewport.WorldToLocal(mapPos); + tempCoords.Y = args.Viewport.Size.Y - tempCoords.Y; // Local space to fragment space. + + // try find cluster to merge with + bool merged = false; + foreach (var cluster in clusters) + { + if ((cluster.Position - tempCoords).Length() < 150f) + { + cluster.Add(tempCoords, rel.Observation[_religion.Value]); + merged = true; + break; + } + } + + if (!merged) + clusters.Add(new Cluster(tempCoords, rel.Observation[_religion.Value])); + + if (clusters.Count >= MaxCount) + break; + } + + _count = 0; + foreach (var cluster in clusters) + { + _positions[_count] = cluster.Position; + _radii[_count] = cluster.Radius; + _count++; + } + + return true; + } + + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenTexture == null || args.Viewport.Eye == null) + return; + + if (!_entManager.TryGetComponent(_player.LocalEntity, out var visionComponent)) + return; + + _religionShader?.SetParameter("shaderColor", visionComponent.ShaderColor); + _religionShader?.SetParameter("renderScale", args.Viewport.RenderScale * args.Viewport.Eye.Scale); + _religionShader?.SetParameter("count", _count); + _religionShader?.SetParameter("position", _positions); + _religionShader?.SetParameter("radius", _radii); + _religionShader?.SetParameter("SCREEN_TEXTURE", ScreenTexture); + + var worldHandle = args.WorldHandle; + worldHandle.UseShader(_religionShader); + worldHandle.DrawRect(args.WorldAABB, Color.White); + worldHandle.UseShader(null); + } + + private sealed class Cluster + { + public Vector2 Position; + public float Radius; + public int Count; + + public Cluster(Vector2 pos, float radius) + { + Position = pos; + Radius = radius; + Count = 1; + } + + public void Add(Vector2 pos, float radius) + { + Position = (Position * Count + pos) / (Count + 1); + Radius = Math.Max(Radius, radius); + Count++; + } + } +} diff --git a/Content.Client/_CP14/UserInterface/Systems/Skill/CP14SkillUIController.cs b/Content.Client/_CP14/UserInterface/Systems/Skill/CP14SkillUIController.cs index 3dfd6f5667..27c0992964 100644 --- a/Content.Client/_CP14/UserInterface/Systems/Skill/CP14SkillUIController.cs +++ b/Content.Client/_CP14/UserInterface/Systems/Skill/CP14SkillUIController.cs @@ -37,7 +37,6 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered _allSkills = []; - private IEnumerable _allTrees = []; private CP14SkillPrototype? _selectedSkill; private CP14SkillTreePrototype? _selectedSkillTree; @@ -72,7 +71,6 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered(); - _allTrees = _proto.EnumeratePrototypes().OrderBy(tree => Loc.GetString(tree.Name)); } public void OnStateExited(GameplayState state) @@ -293,9 +291,9 @@ public sealed class CP14SkillUIController : UIController, IOnStateEntered { - SelectTree(tree, storage); + SelectTree(indexedTree); }; _window.TreeTabsContainer.AddChild(treeButton2); } } - private void SelectTree(CP14SkillTreePrototype tree, CP14SkillStorageComponent storage) + private void SelectTree(ProtoId tree) { if (_window == null) return; - _selectedSkillTree = tree; - _window.ParallaxBackground.ParallaxPrototype = tree.Parallax; - _window.TreeName.Text = Loc.GetString(tree.Name); + if (!_proto.TryIndex(tree, out var indexedTree)) + return; + + _selectedSkillTree = indexedTree; + _window.ParallaxBackground.ParallaxPrototype = indexedTree.Parallax; + _window.TreeName.Text = Loc.GetString(indexedTree.Name); UpdateGraphControl(); } diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index fd8c1b9d5c..05547fc46b 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -6,10 +6,12 @@ using Content.Server.Administration.Managers; using Content.Server.Chat.Managers; using Content.Server.GameTicking; using Content.Server.Players.RateLimiting; +using Content.Server.Radio.EntitySystems; using Content.Server.Speech.Prototypes; using Content.Server.Speech.EntitySystems; using Content.Server.Station.Components; using Content.Server.Station.Systems; +using Content.Shared._CP14.Religion.Components; using Content.Shared.ActionBlocker; using Content.Shared.Administration; using Content.Shared.CCVar; @@ -60,6 +62,9 @@ public sealed partial class ChatSystem : SharedChatSystem [Dependency] private readonly ReplacementAccentSystem _wordreplacement = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; [Dependency] private readonly ExamineSystemShared _examineSystem = default!; + //CP14 + [Dependency] private readonly RadioSystem _radio = default!; + //CP14 end public const int VoiceRange = 10; // how far voice goes in world units public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units @@ -174,6 +179,15 @@ public sealed partial class ChatSystem : SharedChatSystem bool ignoreActionBlocker = false ) { + //CP14 Zone + if (HasComp(source)) + { + TryProccessRadioMessage(source, message, out var modMessage, out var channel); + _radio.SendRadioMessage(source, modMessage, "CP14Gods", source); + return; + } + //CP14 Zone end + if (HasComp(source)) { // Ghosts can only send dead chat messages, so we'll forward it to InGame OOC. diff --git a/Content.Server/Spawners/Components/SpawnPointComponent.cs b/Content.Server/Spawners/Components/SpawnPointComponent.cs index c6d14dfeb3..16b24d6d62 100644 --- a/Content.Server/Spawners/Components/SpawnPointComponent.cs +++ b/Content.Server/Spawners/Components/SpawnPointComponent.cs @@ -27,4 +27,7 @@ public enum SpawnPointType LateJoin, Job, Observer, + //CP14 + Always, //Always use only these spawn point, and latejoin, and roundstart + //CP14 end } diff --git a/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs b/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs index bd905e2982..56c01f64de 100644 --- a/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs +++ b/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs @@ -32,6 +32,15 @@ public sealed class SpawnPointSystem : EntitySystem if (args.Station != null && _stationSystem.GetOwningStation(uid, xform) != args.Station) continue; + //CP14 always spawn gods on gods spawnpoints + if (spawnPoint.SpawnType == SpawnPointType.Always && (args.Job == null || spawnPoint.Job == args.Job)) + { + possiblePositions.Clear(); + possiblePositions.Add(xform.Coordinates); + break; + } + //CP14end + if (_gameTicker.RunLevel == GameRunLevel.InRound && spawnPoint.SpawnType == SpawnPointType.LateJoin) { possiblePositions.Add(xform.Coordinates); diff --git a/Content.Server/_CP14/DemiplaneTraveling/CP14DemiplaneTravelingSystem.cs b/Content.Server/_CP14/DemiplaneTraveling/CP14DemiplaneTravelingSystem.cs index 2361534bfd..6e6516c753 100644 --- a/Content.Server/_CP14/DemiplaneTraveling/CP14DemiplaneTravelingSystem.cs +++ b/Content.Server/_CP14/DemiplaneTraveling/CP14DemiplaneTravelingSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Popups; using Content.Shared._CP14.Demiplane; using Content.Shared._CP14.Demiplane.Components; using Content.Shared._CP14.DemiplaneTraveling; +using Content.Shared._CP14.Religion.Components; using Content.Shared.Ghost; using Content.Shared.Item; using Content.Shared.Movement.Pulling.Components; @@ -58,6 +59,8 @@ public sealed partial class CP14DemiplaneTravelingSystem : EntitySystem { if (HasComp(ent)) continue; + if (HasComp(ent)) //TODO: make some generic way to whitelist entities from teleporting + continue; if (!_mind.TryGetMind(ent, out var mindId, out var mind)) continue; diff --git a/Content.Server/_CP14/RandomJobs/CP14StationRandomJobsComponent.cs b/Content.Server/_CP14/RandomJobs/CP14StationRandomJobsComponent.cs new file mode 100644 index 0000000000..12c5d8582f --- /dev/null +++ b/Content.Server/_CP14/RandomJobs/CP14StationRandomJobsComponent.cs @@ -0,0 +1,26 @@ + +using Content.Shared.Destructible.Thresholds; +using Content.Shared.Roles; +using Robust.Shared.Prototypes; + +namespace Content.Server._CP14.RandomJobs; + +[RegisterComponent, Access(typeof(CP14StationRandomJobsSystem))] +public sealed partial class CP14StationRandomJobsComponent : Component +{ + [DataField] + public List Entries = new(); +} + +[Serializable, DataDefinition] +public sealed partial class CP14RandomJobEntry +{ + [DataField(required: true)] + public List> Jobs = default!; + + [DataField(required: true)] + public MinMax Count = new(1, 1); + + [DataField] + public float Prob = 1f; +} diff --git a/Content.Server/_CP14/RandomJobs/CP14StationRandomJobsSystem.cs b/Content.Server/_CP14/RandomJobs/CP14StationRandomJobsSystem.cs new file mode 100644 index 0000000000..3440696304 --- /dev/null +++ b/Content.Server/_CP14/RandomJobs/CP14StationRandomJobsSystem.cs @@ -0,0 +1,52 @@ + +using Content.Server.Station.Events; +using Content.Server.Station.Systems; +using Content.Shared.Roles; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server._CP14.RandomJobs; + +public sealed partial class CP14StationRandomJobsSystem : EntitySystem +{ + [Dependency] private readonly StationJobsSystem _jobs = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit, after: new[] { typeof(StationJobsSystem) }); + } + + private void OnInit(StationInitializedEvent args) + { + if (!TryComp(args.Station, out var randomJobs)) + return; + + foreach (var entry in randomJobs.Entries) + { + if (!_random.Prob(entry.Prob)) + continue; + + var count = entry.Count.Next(_random); + + var tempList = new List>(entry.Jobs); + + for (var i = 0; i < count; i++) + { + if (tempList.Count == 0) + break; + + var job = _random.Pick(tempList); + tempList.Remove(job); + + if (!_proto.TryIndex(job, out var jobProto)) + continue; + + _jobs.TryAdjustJobSlot(args.Station, jobProto, 1, createSlot: true); + } + } + } +} diff --git a/Content.Server/_CP14/Religion/CP14ReligionSystem.UI.cs b/Content.Server/_CP14/Religion/CP14ReligionSystem.UI.cs new file mode 100644 index 0000000000..2716e4cfcb --- /dev/null +++ b/Content.Server/_CP14/Religion/CP14ReligionSystem.UI.cs @@ -0,0 +1,75 @@ +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Systems; +using Content.Shared.Follower; +using Robust.Server.GameObjects; + +namespace Content.Server._CP14.Religion; + +public sealed partial class CP14ReligionGodSystem +{ + [Dependency] private readonly UserInterfaceSystem _userInterface = default!; + [Dependency] private readonly FollowerSystem _follower = default!; + private void InitializeUI() + { + SubscribeLocalEvent(OnOpenInterface); + SubscribeLocalEvent(OnTeleportAttempt); + } + + private void OnTeleportAttempt(Entity ent, ref CP14ReligionEntityTeleportAttempt args) + { + var target = GetEntity(args.Entity); + + var canTeleport = false; + if (TryComp(target, out var altar)) + { + if (altar.Religion == ent.Comp.Religion) + { + canTeleport = true; + } + } + else if (TryComp(target, out var follower)) + { + if (follower.Religion == ent.Comp.Religion) + { + canTeleport = true; + } + } + + if (!canTeleport) + return; + + _follower.StartFollowingEntity(ent, target); + } + + private void OnOpenInterface(Entity ent, ref OpenBoundInterfaceMessage args) + { + if (ent.Comp.Religion is null) + return; + + var altars = new Dictionary(); + var queryAltars = EntityQueryEnumerator(); + while (queryAltars.MoveNext(out var uid, out var altar, out var meta)) + { + if (altar.Religion != ent.Comp.Religion) + continue; + + altars.TryAdd(GetNetEntity(uid), meta.EntityName); + } + + var followers = new Dictionary(); + var queryFollowers = EntityQueryEnumerator(); + while (queryFollowers.MoveNext(out var uid, out var follower, out var meta)) + { + if (follower.Religion != ent.Comp.Religion) + continue; + + followers.TryAdd(GetNetEntity(uid), meta.EntityName); + } + + var followerPercentage = GetFollowerPercentage(ent); + ent.Comp.FollowerPercentage = followerPercentage; + Dirty(ent); + + _userInterface.SetUiState(ent.Owner, CP14ReligionEntityUiKey.Key, new CP14ReligionEntityUiState(altars, followers, followerPercentage)); + } +} diff --git a/Content.Server/_CP14/Religion/CP14ReligionSystem.cs b/Content.Server/_CP14/Religion/CP14ReligionSystem.cs new file mode 100644 index 0000000000..a64d086016 --- /dev/null +++ b/Content.Server/_CP14/Religion/CP14ReligionSystem.cs @@ -0,0 +1,177 @@ +using Content.Server.Chat.Managers; +using Content.Server.Speech; +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared._CP14.Religion.Systems; +using Content.Shared.Chat; +using Robust.Server.GameStates; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server._CP14.Religion; + +public sealed partial class CP14ReligionGodSystem : CP14SharedReligionGodSystem +{ + [Dependency] private readonly IChatManager _chat = default!; + [Dependency] private readonly PvsOverrideSystem _pvs = default!; + + public override void Initialize() + { + base.Initialize(); + InitializeUI(); + + SubscribeLocalEvent(OnObserverInit); + SubscribeLocalEvent(OnObserverHandleState); + + SubscribeLocalEvent(OnGodInit); + SubscribeLocalEvent(OnGodShutdown); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + + SubscribeLocalEvent(OnListen); + } + + private void OnObserverHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var god)) + { + UpdatePvsOverrides(new Entity(uid, god)); + } + } + + private void OnObserverInit(Entity ent, ref ComponentInit args) + { + foreach (var (religion, _) in ent.Comp.Observation) + { + var gods = GetGods(religion); + + foreach (var god in gods) + { + UpdatePvsOverrides(god); + } + } + } + + private void OnGodInit(Entity ent, ref ComponentInit args) + { + AddPvsOverrides(ent); + } + + private void OnGodShutdown(Entity ent, ref ComponentShutdown args) + { + RemovePvsOverrides(ent); + } + + private void OnPlayerAttached(Entity ent, ref PlayerAttachedEvent args) + { + AddPvsOverrides(ent); + } + + private void OnPlayerDetached(Entity ent, ref PlayerDetachedEvent args) + { + RemovePvsOverrides(ent); + } + + private void OnListen(Entity ent, ref ListenEvent args) + { + if (ent.Comp.Religion is null) + return; + + var wrappedMessage = + Loc.GetString("cp14-altar-wrapped-message", ("name", MetaData(args.Source).EntityName), ("msg", args.Message)); + + SendMessageToGods(ent.Comp.Religion.Value, wrappedMessage, args.Source); + } + + protected override void SendMessageToGods(ProtoId religion, string msg, EntityUid source) + { + var gods = GetGods(religion); + + HashSet channels = new(); + foreach (var god in gods) + { + if (!TryComp(god, out var godActor)) + continue; + + channels.Add(godActor.PlayerSession.Channel); + } + + _chat.ChatMessageToMany(ChatChannel.Notifications, msg, msg, source, false, true, channels, colorOverride: Color.Aqua); + } + + public float GetFollowerPercentage(Entity god) + { + var total = 0; + var followers = 0; + + var allHumans = Mind.GetAliveHumans(); + foreach (var human in allHumans) + { + total++; + + if (!TryComp(human.Comp.CurrentEntity, out var relFollower)) + continue; + if (relFollower.Religion != god.Comp.Religion) + continue; + + followers++; + } + + if (total == 0) + return 0f; + + return (float)followers / total; + } + + private void AddPvsOverrides(Entity ent) + { + if (ent.Comp.Religion is null) + return; + + if (!TryComp(ent, out var actor)) + return; + + ent.Comp.Session = actor.PlayerSession; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var observer)) + { + if (!observer.Observation.ContainsKey(ent.Comp.Religion.Value)) + continue; + + if (observer.Observation[ent.Comp.Religion.Value] <= 6.5f) //Maybe there is a variable for the distance outside the screen in PVS, I don't know. This number works best + continue; + + ent.Comp.PvsOverridedObservers.Add(uid); + _pvs.AddSessionOverride(uid, actor.PlayerSession); + } + } + + private void RemovePvsOverrides(Entity ent) + { + if (ent.Comp.Religion is null) + return; + + if (ent.Comp.Session is null) + return; + + foreach (var altar in ent.Comp.PvsOverridedObservers) + { + _pvs.RemoveSessionOverride(altar, ent.Comp.Session); + } + + ent.Comp.Session = null; + ent.Comp.PvsOverridedObservers.Clear(); + } + + private void UpdatePvsOverrides(Entity ent) + { + if (ent.Comp.Session is null) + return; + + RemovePvsOverrides(ent); + AddPvsOverrides(ent); + } +} diff --git a/Content.Server/_CP14/ZLevels/EntitySystems/CP14StationZLevelsSystem.Actions.cs b/Content.Server/_CP14/ZLevels/EntitySystems/CP14StationZLevelsSystem.Actions.cs index 682d9e2383..b083cb61f8 100644 --- a/Content.Server/_CP14/ZLevels/EntitySystems/CP14StationZLevelsSystem.Actions.cs +++ b/Content.Server/_CP14/ZLevels/EntitySystems/CP14StationZLevelsSystem.Actions.cs @@ -1,4 +1,3 @@ -using System.Numerics; using Content.Shared._CP14.ZLevel; using Content.Shared.Ghost; using Robust.Shared.Map; @@ -9,11 +8,13 @@ public sealed partial class CP14StationZLevelsSystem { private void InitActions() { - SubscribeLocalEvent(OnZLevelUp); - SubscribeLocalEvent(OnZLevelDown); + SubscribeLocalEvent(OnZLevelUpGhost); + SubscribeLocalEvent(OnZLevelDownGhost); + SubscribeLocalEvent(OnZLevelUp); + SubscribeLocalEvent(OnZLevelDown); } - private void OnZLevelDown(Entity ent, ref CP14ZLevelActionDown args) + private void OnZLevelDownGhost(Entity ent, ref CP14ZLevelActionDown args) { if (args.Handled) return; @@ -23,7 +24,27 @@ public sealed partial class CP14StationZLevelsSystem args.Handled = true; } - private void OnZLevelUp(Entity ent, ref CP14ZLevelActionUp args) + private void OnZLevelUpGhost(Entity ent, ref CP14ZLevelActionUp args) + { + if (args.Handled) + return; + + ZLevelMove(ent, 1); + + args.Handled = true; + } + + private void OnZLevelDown(Entity ent, ref CP14ZLevelActionDown args) + { + if (args.Handled) + return; + + ZLevelMove(ent, -1); + + args.Handled = true; + } + + private void OnZLevelUp(Entity ent, ref CP14ZLevelActionUp args) { if (args.Handled) return; diff --git a/Content.Shared/Verbs/VerbCategory.cs b/Content.Shared/Verbs/VerbCategory.cs index 880f21f58d..3a2fdb8f4e 100644 --- a/Content.Shared/Verbs/VerbCategory.cs +++ b/Content.Shared/Verbs/VerbCategory.cs @@ -49,12 +49,6 @@ namespace Content.Shared.Verbs public static readonly VerbCategory CP14RitualBook = new("cp14-verb-categories-ritual-book", null); public static readonly VerbCategory CP14CurrencyConvert = new("cp14-verb-categories-currency-converter", null); //CP14 - - public static readonly VerbCategory CP14AdminSkillAdd = - new ("cp14-verb-categories-admin-skill-add", null, iconsOnly: true) { Columns = 6 }; - - public static readonly VerbCategory CP14AdminSkillRemove = - new ("cp14-verb-categories-admin-skill-remove", null, iconsOnly: true) { Columns = 6 }; //CP14 verbs public static readonly VerbCategory Admin = diff --git a/Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.Checks.cs b/Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.Checks.cs index efa751e987..c80a3e0cce 100644 --- a/Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.Checks.cs +++ b/Content.Shared/_CP14/MagicSpell/CP14SharedMagicSystem.Checks.cs @@ -1,5 +1,8 @@ using Content.Shared._CP14.MagicSpell.Components; using Content.Shared._CP14.MagicSpell.Events; +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared._CP14.Religion.Systems; using Content.Shared.CombatMode.Pacification; using Content.Shared.Damage.Components; using Content.Shared.Hands.Components; @@ -7,12 +10,14 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; using Content.Shared.Speech.Muting; +using Robust.Shared.Prototypes; namespace Content.Shared._CP14.MagicSpell; public abstract partial class CP14SharedMagicSystem { [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly CP14SharedReligionGodSystem _god = default!; private void InitializeChecks() { @@ -22,12 +27,14 @@ public abstract partial class CP14SharedMagicSystem SubscribeLocalEvent(OnStaminaCheck); SubscribeLocalEvent(OnPacifiedCheck); SubscribeLocalEvent(OnMobStateCheck); + SubscribeLocalEvent(OnReligionRestrictedCheck); //Verbal speaking SubscribeLocalEvent(OnVerbalAspectStartCast); SubscribeLocalEvent(OnVerbalAspectAfterCast); SubscribeLocalEvent(OnEmoteStartCast); SubscribeLocalEvent(OnEmoteEndCast); + } /// @@ -148,6 +155,18 @@ public abstract partial class CP14SharedMagicSystem } } + private void OnReligionRestrictedCheck(Entity ent, ref CP14CastMagicEffectAttemptEvent args) + { + + if (!TryComp(args.Performer, out var religionComp)) + return; + + if (args.Position is not null && _god.InVision(args.Position.Value, (args.Performer, religionComp))) + return; + + args.Cancel(); + } + private void OnVerbalAspectStartCast(Entity ent, ref CP14StartCastMagicEffectEvent args) { diff --git a/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectReligionRestrictedComponent.cs b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectReligionRestrictedComponent.cs new file mode 100644 index 0000000000..ec18a93490 --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Components/CP14MagicEffectReligionRestrictedComponent.cs @@ -0,0 +1,12 @@ +using Content.Shared._CP14.MagicSpellStorage; +using Content.Shared.FixedPoint; + +namespace Content.Shared._CP14.MagicSpell.Components; + +/// +/// If the user belongs to a religion, this spell can only be used within the area of influence of that religion +/// +[RegisterComponent, Access(typeof(CP14SharedMagicSystem), typeof(CP14SpellStorageSystem))] +public sealed partial class CP14MagicEffectReligionRestrictedComponent : Component +{ +} diff --git a/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellGodRenounce.cs b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellGodRenounce.cs new file mode 100644 index 0000000000..747d7fff7e --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellGodRenounce.cs @@ -0,0 +1,23 @@ +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Systems; + +namespace Content.Shared._CP14.MagicSpell.Spells; + +public sealed partial class CP14SpellGodRenounce : CP14SpellEffect +{ + public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args) + { + if (args.Target is null) + return; + + if (!entManager.TryGetComponent(args.User, out var god) || god.Religion is null) + return; + + if (!entManager.TryGetComponent(args.Target.Value, out var follower) || follower.Religion != god.Religion) + return; + + var religionSys = entManager.System(); + + religionSys.ToDisbelieve(args.Target.Value); + } +} diff --git a/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellGodTouch.cs b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellGodTouch.cs new file mode 100644 index 0000000000..4121e3b52a --- /dev/null +++ b/Content.Shared/_CP14/MagicSpell/Spells/CP14SpellGodTouch.cs @@ -0,0 +1,24 @@ +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Prototypes; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.MagicSpell.Spells; + +public sealed partial class CP14SpellGodTouch : CP14SpellEffect +{ + public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args) + { + if (args.Target is null) + return; + + if (!entManager.TryGetComponent(args.User, out var god) || god.Religion is null) + return; + + var ev = new CP14GodTouchEvent(god.Religion.Value); + entManager.EventBus.RaiseLocalEvent(args.Target.Value, ev); + } +} +public sealed class CP14GodTouchEvent(ProtoId religion) : EntityEventArgs +{ + public ProtoId Religion = religion; +} diff --git a/Content.Shared/_CP14/Religion/Components/CP14ReligionAltarComponent.cs b/Content.Shared/_CP14/Religion/Components/CP14ReligionAltarComponent.cs new file mode 100644 index 0000000000..b561fa3f7c --- /dev/null +++ b/Content.Shared/_CP14/Religion/Components/CP14ReligionAltarComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared._CP14.Religion.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Religion.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(CP14SharedReligionGodSystem))] +public sealed partial class CP14ReligionAltarComponent : Component +{ + [DataField, AutoNetworkedField] + public ProtoId? Religion; + + [DataField, AutoNetworkedField] + public bool CanBeConverted = true; +} diff --git a/Content.Shared/_CP14/Religion/Components/CP14ReligionEntityComponent.cs b/Content.Shared/_CP14/Religion/Components/CP14ReligionEntityComponent.cs new file mode 100644 index 0000000000..2f04dd8440 --- /dev/null +++ b/Content.Shared/_CP14/Religion/Components/CP14ReligionEntityComponent.cs @@ -0,0 +1,27 @@ +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared._CP14.Religion.Systems; +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Religion.Components; + +/// +/// +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(CP14SharedReligionGodSystem))] +public sealed partial class CP14ReligionEntityComponent : Component +{ + [DataField(required: true)] + public ProtoId? Religion; + + public HashSet PvsOverridedObservers = new(); + public ICommonSession? Session; + + /// + /// Number of followers as a percentage. Automatically calculated on the server and sent to the client for data synchronization. + /// + [DataField, AutoNetworkedField] + public FixedPoint2 FollowerPercentage = 0; +} diff --git a/Content.Shared/_CP14/Religion/Components/CP14ReligionFollowerComponent.cs b/Content.Shared/_CP14/Religion/Components/CP14ReligionFollowerComponent.cs new file mode 100644 index 0000000000..4a8a452848 --- /dev/null +++ b/Content.Shared/_CP14/Religion/Components/CP14ReligionFollowerComponent.cs @@ -0,0 +1,25 @@ +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared._CP14.Religion.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Religion.Components; + +/// +/// Determines whether the entity is a follower of God, or may never be able to become one +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(CP14SharedReligionGodSystem))] +public sealed partial class CP14ReligionFollowerComponent : Component +{ + [DataField, AutoNetworkedField] + public ProtoId? Religion; + + [DataField, AutoNetworkedField] + public HashSet> RejectedReligions = new(); + + [DataField] + public EntProtoId RenounceActionProto = "CP14ActionRenounceFromGod"; + + [DataField] + public EntityUid? RenounceAction; +} diff --git a/Content.Shared/_CP14/Religion/Components/CP14ReligionObserverComponent.cs b/Content.Shared/_CP14/Religion/Components/CP14ReligionObserverComponent.cs new file mode 100644 index 0000000000..5d9b31e6c5 --- /dev/null +++ b/Content.Shared/_CP14/Religion/Components/CP14ReligionObserverComponent.cs @@ -0,0 +1,19 @@ +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared._CP14.Religion.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Religion.Components; + +/// +/// Allows the god of a particular religion to see within a radius around the observer. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(CP14SharedReligionGodSystem))] +public sealed partial class CP14ReligionObserverComponent : Component +{ + [DataField, AutoNetworkedField] + public Dictionary, float> Observation = new(); //DAMNATION + + [DataField, AutoNetworkedField] + public bool Active = true; +} diff --git a/Content.Shared/_CP14/Religion/Components/CP14ReligionPendingFollowerComponent.cs b/Content.Shared/_CP14/Religion/Components/CP14ReligionPendingFollowerComponent.cs new file mode 100644 index 0000000000..f7c384cac4 --- /dev/null +++ b/Content.Shared/_CP14/Religion/Components/CP14ReligionPendingFollowerComponent.cs @@ -0,0 +1,17 @@ +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared._CP14.Religion.Systems; +using Content.Shared.Alert; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Religion.Components; + +/// +/// This entity has not yet become a follower of God, but wants to become one. Confirmation from god is expected +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(CP14SharedReligionGodSystem))] +public sealed partial class CP14ReligionPendingFollowerComponent : Component +{ + [DataField, AutoNetworkedField] + public ProtoId? Religion; +} diff --git a/Content.Shared/_CP14/Religion/Components/CP14ReligionVisionComponent.cs b/Content.Shared/_CP14/Religion/Components/CP14ReligionVisionComponent.cs new file mode 100644 index 0000000000..9c7727b37b --- /dev/null +++ b/Content.Shared/_CP14/Religion/Components/CP14ReligionVisionComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared._CP14.Religion.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared._CP14.Religion.Components; + +/// +/// Limits the vision of entities, allowing them to see only areas within a radius around observers of their religion. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(CP14SharedReligionGodSystem))] +public sealed partial class CP14ReligionVisionComponent : Component +{ + [DataField] + public Vector3 ShaderColor = new (1f, 1f, 1f); +} diff --git a/Content.Shared/_CP14/Religion/Prototypes/CP14ReligionPrototype.cs b/Content.Shared/_CP14/Religion/Prototypes/CP14ReligionPrototype.cs new file mode 100644 index 0000000000..01639cb564 --- /dev/null +++ b/Content.Shared/_CP14/Religion/Prototypes/CP14ReligionPrototype.cs @@ -0,0 +1,18 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Religion.Prototypes; + +/// +/// +/// +[Prototype("cp14Religion")] +public sealed partial class CP14ReligionPrototype : IPrototype +{ + [IdDataField] public string ID { get; } = default!; + + [DataField] + public float FollowerObservationRadius = 8f; + + [DataField] + public float AltarObservationRadius = 25f; +} diff --git a/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Altars.cs b/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Altars.cs new file mode 100644 index 0000000000..135b587c12 --- /dev/null +++ b/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Altars.cs @@ -0,0 +1,106 @@ + +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared.DoAfter; +using Content.Shared.Verbs; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared._CP14.Religion.Systems; + +public abstract partial class CP14SharedReligionGodSystem +{ + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + private void InitializeAltars() + { + SubscribeLocalEvent>(GetBaseVerb); + SubscribeLocalEvent>(GetAltVerb); + } + + private void GetBaseVerb(Entity ent, ref GetVerbsEvent args) + { + + } + + private void GetAltVerb(Entity ent, ref GetVerbsEvent args) + { + if (ent.Comp.Religion is null) + return; + + var disabled = !CanBecomeFollower(args.User, ent.Comp.Religion.Value); + + if (!disabled && TryComp(args.User, out var pendingFollower)) + { + if (pendingFollower.Religion is not null) + disabled = true; + } + + if (disabled) + return; + + var user = args.User; + args.Verbs.Add(new AlternativeVerb() + { + Text = Loc.GetString("cp14-altar-become-follower"), + Message = Loc.GetString("cp14-altar-become-follower-desc"), + Act = () => + { + var doAfterArgs = new DoAfterArgs(EntityManager, user, 5f, new CP14AltarOfferDoAfter(), ent, used: ent) + { + BreakOnDamage = true, + BreakOnMove = true, + }; + _doAfter.TryStartDoAfter(doAfterArgs); + }, + }); + } + + public bool TryConvertAltar(EntityUid target, ProtoId religion) + { + if (!_proto.TryIndex(religion, out var indexedReligion)) + return false; + + EnsureComp(target, out var altar); + + if (!altar.CanBeConverted) + return false; + + var oldReligion = altar.Religion; + altar.Religion = religion; + Dirty(target, altar); + + EditObservation(target, religion, indexedReligion.AltarObservationRadius); + + var ev = new CP14ReligionChangedEvent(oldReligion, religion); + RaiseLocalEvent(target, ev); + + return true; + } + + public void DeconvertAltar(EntityUid target) + { + if (!TryComp(target, out var altar)) + return; + + if (altar.Religion is null) + return; + + if (!_proto.TryIndex(altar.Religion, out var indexedReligion)) + return; + + EditObservation(target, altar.Religion.Value, -indexedReligion.AltarObservationRadius); + + var oldReligion = altar.Religion; + altar.Religion = null; + + var ev = new CP14ReligionChangedEvent(oldReligion, null); + RaiseLocalEvent(target, ev); + + Dirty(target, altar); + } +} + +[Serializable, NetSerializable] +public sealed partial class CP14AltarOfferDoAfter : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Followers.cs b/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Followers.cs new file mode 100644 index 0000000000..da7a10a3e9 --- /dev/null +++ b/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Followers.cs @@ -0,0 +1,154 @@ +using Content.Shared._CP14.MagicSpell.Spells; +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared.Actions; +using Content.Shared.Alert; +using Content.Shared.Mind; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Religion.Systems; + +public abstract partial class CP14SharedReligionGodSystem +{ + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] protected readonly SharedMindSystem Mind = default!; + private void InitializeFollowers() + { + SubscribeLocalEvent(OnPendingFollowerInit); + SubscribeLocalEvent(OnPendingFollowerShutdown); + SubscribeLocalEvent(OnBreakDivineOffer); + SubscribeLocalEvent(OnGodTouch); + + SubscribeLocalEvent(OnOfferDoAfter); + + SubscribeLocalEvent(OnRenounceFromGod); + } + + private void OnRenounceFromGod(Entity ent, ref CP14RenounceFromGodEvent args) + { + ToDisbelieve(ent); + } + + private void OnOfferDoAfter(Entity ent, ref CP14AltarOfferDoAfter args) + { + if (args.Handled || args.Cancelled) + return; + + if (ent.Comp.Religion is null) + return; + + TryAddPendingFollower(args.User, ent.Comp.Religion.Value); + + args.Handled = true; + } + + private void OnGodTouch(Entity ent, ref CP14GodTouchEvent args) + { + if (args.Religion != ent.Comp.Religion) + return; + + TryToBelieve(ent); + } + + private void OnBreakDivineOffer(Entity ent, ref CP14BreakDivineOfferEvent args) + { + RemCompDeferred(ent); + + if (ent.Comp.Religion is null) + return; + + SendMessageToGods(ent.Comp.Religion.Value, Loc.GetString("cp14-unoffer-soul-god-message", ("name", MetaData(ent).EntityName)), ent); + } + + private void OnPendingFollowerInit(Entity ent, ref MapInitEvent args) + { + _alerts.ShowAlert(ent, "CP14DivineOffer"); + } + + private void OnPendingFollowerShutdown(Entity ent, ref ComponentShutdown args) + { + _alerts.ClearAlert(ent, "CP14DivineOffer"); + } + + private bool CanBecomeFollower(EntityUid target, ProtoId religion) + { + if (HasComp(target)) + return false; + + EnsureComp(target, out var follower); + + return !follower.RejectedReligions.Contains(religion); + } + + private void TryAddPendingFollower(EntityUid target, ProtoId religion) + { + if (!CanBecomeFollower(target, religion)) + return; + + EnsureComp(target, out var pendingFollower); + pendingFollower.Religion = religion; + + SendMessageToGods(religion, Loc.GetString("cp14-offer-soul-god-message", ("name", MetaData(target).EntityName)), target); + } + + private bool TryToBelieve(Entity pending) + { + if (pending.Comp.Religion is null) + return false; + + if (!_proto.TryIndex(pending.Comp.Religion, out var indexedReligion)) + return false; + + if (!CanBecomeFollower(pending, pending.Comp.Religion.Value)) + return false; + + EnsureComp(pending, out var follower); + + var oldReligion = follower.Religion; + follower.Religion = pending.Comp.Religion; + Dirty(pending, follower); + + EditObservation(pending, pending.Comp.Religion.Value, indexedReligion.FollowerObservationRadius); + + var ev = new CP14ReligionChangedEvent(oldReligion, pending.Comp.Religion); + RaiseLocalEvent(pending, ev); + + RemCompDeferred(pending); + SendMessageToGods(pending.Comp.Religion.Value, Loc.GetString("cp14-become-follower-message", ("name", MetaData(pending).EntityName)), pending); + + _actions.AddAction(pending, ref follower.RenounceAction, follower.RenounceActionProto); + return true; + } + + public void ToDisbelieve(EntityUid target) + { + if (!TryComp(target, out var follower)) + return; + + if (follower.Religion is null) + return; + + if (!_proto.TryIndex(follower.Religion, out var indexedReligion)) + return; + + SendMessageToGods(follower.Religion.Value, Loc.GetString("cp14-remove-follower-message", ("name", MetaData(target).EntityName)), target); + EditObservation(target, follower.Religion.Value, -indexedReligion.FollowerObservationRadius); + + var oldReligion = follower.Religion; + follower.Religion = null; + if (oldReligion is not null) + follower.RejectedReligions.Add(oldReligion.Value); + + var ev = new CP14ReligionChangedEvent(oldReligion, null); + RaiseLocalEvent(target, ev); + + Dirty(target, follower); + + _actions.RemoveAction(target, follower.RenounceAction); + } +} + +public sealed partial class CP14BreakDivineOfferEvent : BaseAlertEvent; + +public sealed partial class CP14RenounceFromGodEvent : InstantActionEvent; diff --git a/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Observation.cs b/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Observation.cs new file mode 100644 index 0000000000..c2ee697f4a --- /dev/null +++ b/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.Observation.cs @@ -0,0 +1,96 @@ +using System.Numerics; +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Prototypes; +using Content.Shared.Interaction; +using Content.Shared.Verbs; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Religion.Systems; + +public abstract partial class CP14SharedReligionGodSystem +{ + private void InitializeObservation() + { + SubscribeLocalEvent(OnGodInRange); + SubscribeLocalEvent(OnGodMenu); + } + + private void OnGodInRange(Entity ent, ref InRangeOverrideEvent args) + { + args.Handled = true; + + args.InRange = InVision(args.Target, ent); + } + + private void OnGodMenu(Entity ent, ref MenuVisibilityEvent args) + { + args.Visibility &= ~MenuVisibility.NoFov; + } + + public void EditObservation(EntityUid target, ProtoId religion, float range) + { + EnsureComp(target, out var observer); + + if (observer.Observation.ContainsKey(religion)) + { + var newRange = Math.Clamp(observer.Observation[religion] + range, 0, float.MaxValue); + + if (newRange <= 0) + { + observer.Observation.Remove(religion); + } + else + { + observer.Observation[religion] = newRange; + } + } + else + { + // Otherwise, add a new observation for the religion. + observer.Observation.Add(religion, range); + } + Dirty(target, observer); + } + + public bool InVision(EntityUid target, Entity user) + { + var position = Transform(target).Coordinates; + + return InVision(position, user); + } + + public bool InVision(EntityCoordinates coords, Entity user) + { + if (!HasComp(user)) + return true; + + var userXform = Transform(user); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var observer, out var xform)) + { + if (!observer.Active) + continue; + + if (xform.MapID != userXform.MapID) + continue; + + if (user.Comp.Religion is null) + continue; + + if (!observer.Observation.ContainsKey(user.Comp.Religion.Value)) + continue; + + + var obsPos = _transform.GetWorldPosition(uid); + var targetPos = coords.Position; + if (Vector2.Distance(obsPos, targetPos) <= observer.Observation[user.Comp.Religion.Value]) + { + // If the observer is within range of the target, they can see it. + return true; + } + } + + return false; + } +} diff --git a/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.UI.cs b/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.UI.cs new file mode 100644 index 0000000000..3667d1ecf4 --- /dev/null +++ b/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.UI.cs @@ -0,0 +1,23 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared._CP14.Religion.Systems; + +[Serializable, NetSerializable] +public enum CP14ReligionEntityUiKey +{ + Key, +} + +[Serializable, NetSerializable] +public sealed class CP14ReligionEntityUiState(Dictionary altars, Dictionary followers, float followerPercentage) : BoundUserInterfaceState +{ + public Dictionary Altars = altars; + public Dictionary Followers = followers; + public float FollowerPercentage = followerPercentage; +} + +[Serializable, NetSerializable] +public sealed class CP14ReligionEntityTeleportAttempt(NetEntity entity) : BoundUserInterfaceMessage +{ + public readonly NetEntity Entity = entity; +} diff --git a/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.cs b/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.cs new file mode 100644 index 0000000000..9f38e06caa --- /dev/null +++ b/Content.Shared/_CP14/Religion/Systems/CP14SharedReligionGodSystem.cs @@ -0,0 +1,46 @@ +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Religion.Prototypes; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Religion.Systems; + +public abstract partial class CP14SharedReligionGodSystem : EntitySystem +{ + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + + public override void Initialize() + { + base.Initialize(); + InitializeObservation(); + InitializeFollowers(); + InitializeAltars(); + } + + public HashSet> GetGods(ProtoId religion) + { + HashSet> gods = new(); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var god)) + { + if (god.Religion != religion) + continue; + + gods.Add(new Entity(uid, god)); + } + + return gods; + } + + protected abstract void SendMessageToGods(ProtoId religion, string msg, EntityUid source); +} + +/// +/// It is invoked on altars and followers when they change their religion. +/// +public sealed class CP14ReligionChangedEvent(ProtoId? oldRel, ProtoId? newRel) : EntityEventArgs +{ + public ProtoId? OldReligion = oldRel; + public ProtoId? NewReligion = newRel; +} diff --git a/Content.Shared/_CP14/Skill/CP14SharedSkillSystem.cs b/Content.Shared/_CP14/Skill/CP14SharedSkillSystem.cs index 1649c30677..0a581fc08c 100644 --- a/Content.Shared/_CP14/Skill/CP14SharedSkillSystem.cs +++ b/Content.Shared/_CP14/Skill/CP14SharedSkillSystem.cs @@ -174,6 +174,10 @@ public abstract partial class CP14SharedSkillSystem : EntitySystem if (HaveSkill(target, skill, component)) return false; + //Check if the skill is in the available skill trees + if (!component.AvailableSkillTrees.Contains(skill.Tree)) + return false; + //Check max cap if (component.SkillsSumExperience + skill.LearnCost > component.ExperienceMaxCap) return false; diff --git a/Content.Shared/_CP14/Skill/CP14SkillSystem.Admin.cs b/Content.Shared/_CP14/Skill/CP14SkillSystem.Admin.cs index 326377d8a4..4c78e77ba3 100644 --- a/Content.Shared/_CP14/Skill/CP14SkillSystem.Admin.cs +++ b/Content.Shared/_CP14/Skill/CP14SkillSystem.Admin.cs @@ -51,58 +51,16 @@ public abstract partial class CP14SharedSkillSystem var target = args.Target; - //Add Skill - foreach (var skill in _allSkills) - { - if (ent.Comp.LearnedSkills.Contains(skill)) - continue; - - var name = Loc.GetString(GetSkillName(skill)); - args.Verbs.Add(new Verb - { - Text = name, - Message = name + ": " + Loc.GetString(GetSkillDescription(skill)), - Category = VerbCategory.CP14AdminSkillAdd, - Icon = skill.Icon, - Act = () => - { - TryAddSkill(target, skill); - }, - }); - } - - //Remove Skill - foreach (var skill in ent.Comp.LearnedSkills) - { - if (!_proto.TryIndex(skill, out var indexedSkill)) - continue; - - var name = Loc.GetString(GetSkillName(skill)); - args.Verbs.Add(new Verb - { - Text = name, - Message = name + ": " + Loc.GetString(GetSkillDescription(skill)), - Category = VerbCategory.CP14AdminSkillRemove, - Icon = indexedSkill.Icon, - Act = () => - { - TryRemoveSkill(target, skill); - }, - }); - } - //Reset/Remove All Skills args.Verbs.Add(new Verb { Text = "Reset skills", Message = "Remove all learned skills", - Category = VerbCategory.CP14AdminSkillRemove, Icon = new SpriteSpecifier.Rsi(new("/Textures/_CP14/Interface/Misc/reroll.rsi"), "reroll"), Act = () => { TryResetSkills(target); }, }); - } } diff --git a/Content.Shared/_CP14/Skill/Components/CP14SkillStorageComponent.cs b/Content.Shared/_CP14/Skill/Components/CP14SkillStorageComponent.cs index bfb11e4776..12b8c47bd6 100644 --- a/Content.Shared/_CP14/Skill/Components/CP14SkillStorageComponent.cs +++ b/Content.Shared/_CP14/Skill/Components/CP14SkillStorageComponent.cs @@ -14,9 +14,15 @@ namespace Content.Shared._CP14.Skill.Components; [Access(typeof(CP14SharedSkillSystem), typeof(CP14SharedResearchSystem))] public sealed partial class CP14SkillStorageComponent : Component { + /// + /// Skill trees displayed in the skill tree interface. Only skills from these trees can be learned by this player. + /// + [DataField] + public HashSet> AvailableSkillTrees = new(); + /// /// Tracks skills that are learned without spending memory points. - /// the skills that are here are DUBLED in the LearnedSkills, + /// the skills that are here are DOUBLED in the LearnedSkills, /// [DataField, AutoNetworkedField] public List> FreeLearnedSkills = new(); diff --git a/Content.Shared/_CP14/Skill/Prototypes/CP14SkillTreePrototype.cs b/Content.Shared/_CP14/Skill/Prototypes/CP14SkillTreePrototype.cs index 8a00c6e2b6..6057dc0e67 100644 --- a/Content.Shared/_CP14/Skill/Prototypes/CP14SkillTreePrototype.cs +++ b/Content.Shared/_CP14/Skill/Prototypes/CP14SkillTreePrototype.cs @@ -41,7 +41,4 @@ public sealed partial class CP14SkillTreePrototype : IPrototype [DataField] public SoundSpecifier LearnSound = new SoundCollectionSpecifier("CP14LearnSkill"); - - [DataField] - public SpriteSpecifier? Icon = null; } diff --git a/Content.Shared/_CP14/Skill/Restrictions/GodFollowerPercentage.cs b/Content.Shared/_CP14/Skill/Restrictions/GodFollowerPercentage.cs new file mode 100644 index 0000000000..aafe472d7d --- /dev/null +++ b/Content.Shared/_CP14/Skill/Restrictions/GodFollowerPercentage.cs @@ -0,0 +1,27 @@ +using Content.Shared._CP14.Religion.Components; +using Content.Shared._CP14.Skill.Prototypes; +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; + +namespace Content.Shared._CP14.Skill.Restrictions; + +public sealed partial class GodFollowerPercentage : CP14SkillRestriction +{ + [DataField] + public FixedPoint2 Percentage = 0.5f; + public override bool Check(IEntityManager entManager, EntityUid target, CP14SkillPrototype skill) + { + if (!entManager.TryGetComponent(target, out var god)) + return false; + + if (god.Religion is null) + return false; + + return god.FollowerPercentage >= Percentage; + } + + public override string GetDescription(IEntityManager entManager, IPrototypeManager protoManager) + { + return Loc.GetString("cp14-skill-req-god-follower-percentage", ("count", Percentage * 100)); + } +} diff --git a/Resources/Locale/en-US/_CP14/headset/radio.ftl b/Resources/Locale/en-US/_CP14/headset/radio.ftl new file mode 100644 index 0000000000..298b150008 --- /dev/null +++ b/Resources/Locale/en-US/_CP14/headset/radio.ftl @@ -0,0 +1 @@ +cp14-chat-radio-gods = Divine \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/job/department.ftl b/Resources/Locale/en-US/_CP14/job/department.ftl index bc604e6f8c..4e2b78f247 100644 --- a/Resources/Locale/en-US/_CP14/job/department.ftl +++ b/Resources/Locale/en-US/_CP14/job/department.ftl @@ -11,4 +11,7 @@ department-CP14Guard = Guards department-CP14Guard-desc = Protectors and warriors who oversee security and law and order in all corners of the Empire. department-CP14Artisan = Artisans -department-CP14Artisan-desc = People who have learnt peaceful professions, people who help the settlement with their knowledge and skills. \ No newline at end of file +department-CP14Artisan-desc = People who have learnt peaceful professions, people who help the settlement with their knowledge and skills. + +department-CP14Demigods = Patrons +department-CP14Demigods-desc = Higher beings playing their own games, where ordinary creatures are merely pawns in their plans. \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/job/job.ftl b/Resources/Locale/en-US/_CP14/job/job.ftl index 76908b9b79..b34b558a59 100644 --- a/Resources/Locale/en-US/_CP14/job/job.ftl +++ b/Resources/Locale/en-US/_CP14/job/job.ftl @@ -34,4 +34,12 @@ cp14-job-name-blacksmith = Blacksmith cp14-job-desc-blacksmith = Create and improve equipment for everyone in need! You have the power of metal and fire in your hands, and only you know how to use them carefully to create masterpieces. cp14-job-name-apprentice = Apprentice -cp14-job-desc-apprentice = A peaceful citizen of the empire, just beginning to learn the subtleties of various sciences. Choose a specialisation in equipment, and try to help others in their work, in exchange for a salary and invaluable experience. \ No newline at end of file +cp14-job-desc-apprentice = A peaceful citizen of the empire, just beginning to learn the subtleties of various sciences. Try to help others in their work, in exchange for a salary and invaluable experience. + +# Demigods + +cp14-job-name-god-merkas = Merkas +cp14-job-desc-god-merkas = God of purity and healing. TODO + +cp14-job-name-god-lumera = Lumera +cp14-job-desc-god-lumera = Patroness of the night and the starry sky. TODO \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/religion/altar.ftl b/Resources/Locale/en-US/_CP14/religion/altar.ftl new file mode 100644 index 0000000000..26d64f8228 --- /dev/null +++ b/Resources/Locale/en-US/_CP14/religion/altar.ftl @@ -0,0 +1,19 @@ +cp14-altar-wrapped-message = [bold]{$name}[/bold] prays, {$msg} +cp14-offer-soul-god-message = [bold]{$name}[/bold] [color=green]wants to become your follower[/color]. Touch him to establish a connection. +cp14-unoffer-soul-god-message = [bold]{$name}[/bold] [color=red]has changed his mind about becoming your follower.[/color] +cp14-become-follower-message = [bold]{$name}[/bold] [color=green]becomes your follower[/color]! +cp14-remove-follower-message = [bold]{$name}[/bold] [color=red]rejects you and will never be able to return to you![/color] + +cp14-renounce-action-popup = YOU ARE RENOUNCING YOUR PATRON! To confirm, perform the action again. +cp-renounce-action-god-popup = YOU ARE REJECTING YOUR FOLLOWER! To confirm, perform the action again. + +cp14-god-ui-title = Fast Travel +cp14-god-ui-follower = Followers +cp14-god-ui-altars = Altars +cp14-god-ui-follower-percentage = Follower percentage: {$count}% + +cp14-altar-become-follower = Become a follower +cp14-altar-become-follower-desc = You offer yourself into the service of your patron. If he agrees, your bond will be strengthened. + +cp14-alert-offer = Offer of patronage +cp14-alert-offer-desc = You want to become a follower of the patron, but there is no response from him yet. Click to cancel the offer. \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/skill/requirements.ftl b/Resources/Locale/en-US/_CP14/skill/requirements.ftl index e9756629bf..0e15c7f90e 100644 --- a/Resources/Locale/en-US/_CP14/skill/requirements.ftl +++ b/Resources/Locale/en-US/_CP14/skill/requirements.ftl @@ -1,4 +1,5 @@ cp14-skill-req-prerequisite = Skill "{$name}" must be learned cp14-skill-req-species = You must be the race of “{$name}” cp14-skill-req-researched = A study needs to be done on the research table -cp14-skill-req-impossible = Unable to explore during a round at the current moment \ No newline at end of file +cp14-skill-req-impossible = Unable to explore during a round at the current moment +cp14-skill-req-god-follower-percentage = The number of your followers should be more than {$count}% \ No newline at end of file diff --git a/Resources/Locale/en-US/_CP14/skill/skill_meta_gods.ftl b/Resources/Locale/en-US/_CP14/skill/skill_meta_gods.ftl new file mode 100644 index 0000000000..4ed5976601 --- /dev/null +++ b/Resources/Locale/en-US/_CP14/skill/skill_meta_gods.ftl @@ -0,0 +1,3 @@ +cp14-skill-lumera-t1-name = The origins of the secret night +cp14-skill-lumera-t2-name = The waxing moon +cp14-skill-lumera-t3-name = The full moon of Lumera \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/headset/radio.ftl b/Resources/Locale/ru-RU/_CP14/headset/radio.ftl new file mode 100644 index 0000000000..09c8f115ab --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/headset/radio.ftl @@ -0,0 +1 @@ +cp14-chat-radio-gods = Божественный \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/job/department.ftl b/Resources/Locale/ru-RU/_CP14/job/department.ftl index d8f03718fe..1463c2921b 100644 --- a/Resources/Locale/ru-RU/_CP14/job/department.ftl +++ b/Resources/Locale/ru-RU/_CP14/job/department.ftl @@ -11,4 +11,7 @@ department-CP14Guard = Стража department-CP14Guard-desc = Защитники и войны, следящие за безопасностью и правопорядком во всех уголках империи. department-CP14Artisan = Ремесленники -department-CP14Artisan-desc = Освоившие мирные профессии, люди, которые помогают поселению своими знаниями и умениями. \ No newline at end of file +department-CP14Artisan-desc = Освоившие мирные профессии, люди, которые помогают поселению своими знаниями и умениями. + +department-CP14Demigods = Покровители +department-CP14Demigods-desc = Высшие сущности, играющие в свои собственные игры, где обычные существа лишь пешки в их планах. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/job/job.ftl b/Resources/Locale/ru-RU/_CP14/job/job.ftl index 985eab7686..abfc220bb7 100644 --- a/Resources/Locale/ru-RU/_CP14/job/job.ftl +++ b/Resources/Locale/ru-RU/_CP14/job/job.ftl @@ -34,4 +34,12 @@ cp14-job-name-blacksmith = Кузнец cp14-job-desc-blacksmith = Создавайте и улучшайте экипировку для всех нуждающихся! В ваших руках мощь металла и огня, и только вы знаете как аккуратно использовать их, чтобы создавать шедевры. cp14-job-name-apprentice = Подмастерье -cp14-job-desc-apprentice = Мирный житель империи, только начинающий постигать тонкости различных наук. Выберите специализацию в экипировке, и постарайтесь помочь другим в их работе, в обмен на зарплату и бесценный опыт. \ No newline at end of file +cp14-job-desc-apprentice = Мирный житель империи, только начинающий постигать тонкости различных наук. Постарайтесь помочь другим в их работе, в обмен на зарплату и бесценный опыт. + +# Demigods + +cp14-job-name-god-merkas = Меркас +cp14-job-desc-god-merkas = Бог света и исцеления. TODO + +cp14-job-name-god-lumera = Лумера +cp14-job-desc-god-lumera = Покровительница ночи и звёздного неба. TODO \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/religion/altar.ftl b/Resources/Locale/ru-RU/_CP14/religion/altar.ftl new file mode 100644 index 0000000000..54c2e671ed --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/religion/altar.ftl @@ -0,0 +1,19 @@ +cp14-altar-wrapped-message = [bold]{$name}[/bold] молится, {$msg} +cp14-offer-soul-god-message = [bold]{$name}[/bold] [color=green]хочет стать вашим последователем[/color]. Прикоснитесь к нему, чтобы установить связь. +cp14-unoffer-soul-god-message = [bold]{$name}[/bold] [color=red]передумал становиться вашим последователем.[/color] +cp14-become-follower-message = [bold]{$name}[/bold] [color=green]становится вашим последователем[/color]! +cp14-remove-follower-message = [bold]{$name}[/bold] [color=red]отвергает вас, и больше никогда не сможет вернться к вам![/color] + +cp14-renounce-action-popup = ВЫ ОТРЕКАЕТЕСЬ ОТ ПОКРОВИТЕЛЯ! Для подтверждения выполните действие еще раз. +cp-renounce-action-god-popup = ВЫ ОТВЕРГАЕТЕ СВОЕГО ПОСЛЕДОВАТЕЛЯ! Для подтверждения выполните действие еще раз. + +cp14-god-ui-title = Быстрое перемещение +cp14-god-ui-follower = Последователи +cp14-god-ui-altars = Алтари +cp14-god-ui-follower-percentage = Процент последователей: {$count}% + +cp14-altar-become-follower = Стать последователем +cp14-altar-become-follower-desc = Вы предлагаете себя в службу покровителю. Если он согласится, ваша связь укрепится. + +cp14-alert-offer = Предложение о покровительстве +cp14-alert-offer-desc = Вы хотите стать последователем покровителя, но пока от него нет ответа. Нажмите, чтобы отменить предложение. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/skill/requirements.ftl b/Resources/Locale/ru-RU/_CP14/skill/requirements.ftl index f174639cf1..1eede4ac12 100644 --- a/Resources/Locale/ru-RU/_CP14/skill/requirements.ftl +++ b/Resources/Locale/ru-RU/_CP14/skill/requirements.ftl @@ -1,4 +1,5 @@ cp14-skill-req-prerequisite = Навык "{$name}" должен быть изучен cp14-skill-req-species = Вы должны быть расы "{$name}" cp14-skill-req-researched = Необходимо провести исследование на исследовательском столе -cp14-skill-req-impossible = Невозможно изучить во время раунда на текущий момент \ No newline at end of file +cp14-skill-req-impossible = Невозможно изучить во время раунда на текущий момент +cp14-skill-req-god-follower-percentage = Количество ваших последователей должно быть больше {$count}% \ No newline at end of file diff --git a/Resources/Locale/ru-RU/_CP14/skill/skill_meta_gods.ftl b/Resources/Locale/ru-RU/_CP14/skill/skill_meta_gods.ftl new file mode 100644 index 0000000000..7b0f26b259 --- /dev/null +++ b/Resources/Locale/ru-RU/_CP14/skill/skill_meta_gods.ftl @@ -0,0 +1,3 @@ +cp14-skill-lumera-t1-name = Истоки тайной ночи +cp14-skill-lumera-t2-name = Растущая луна +cp14-skill-lumera-t3-name = Полнолуние Лумеры \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Alerts/alerts.yml b/Resources/Prototypes/_CP14/Alerts/alerts.yml index e6336b3570..9d37b892cd 100644 --- a/Resources/Prototypes/_CP14/Alerts/alerts.yml +++ b/Resources/Prototypes/_CP14/Alerts/alerts.yml @@ -48,3 +48,11 @@ name: alerts-hunger-name description: alerts-hunger-desc +- type: alert + id: CP14DivineOffer + icons: + - sprite: /Textures/_CP14/Interface/Alerts/divine_offer.rsi + state: offer + name: cp14-alert-offer + description: cp14-alert-offer-desc + clickEvent: !type:CP14BreakDivineOfferEvent \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/renounce.yml b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/renounce.yml new file mode 100644 index 0000000000..3b59a05282 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/renounce.yml @@ -0,0 +1,45 @@ +- type: entity + id: CP14ActionSpellGodLumeraRenounce + name: Renunciation of a follower + description: You are rejecting the chosen follower. They lose the opportunity to become your follower at any time. + components: + - type: CP14MagicEffect + effects: + - !type:CP14SpellSpawnEntityOnTarget + spawns: + - CP14LumeraRenounceImpact + - !type:CP14SpellGodRenounce + - type: ConfirmableAction + popup: cp-renounce-action-god-popup + - type: EntityWorldTargetAction + repeat: true + checkCanAccess: false + itemIconStyle: BigAction + range: 100 + sound: !type:SoundPathSpecifier + path: /Audio/Magic/rumble.ogg + icon: + sprite: _CP14/Actions/DemigodSpells/lumera.rsi + state: renounce + event: !type:CP14EntityWorldTargetActionEvent + cooldown: 0.5 + +- type: entity + id: CP14LumeraRenounceImpact + categories: [ ForkFiltered ] + parent: CP14BaseMagicImpact + save: false + components: + - type: PointLight + color: "#94154e" + enabled: true + radius: 5 + energy: 4 + netsync: false + - type: Sprite + layers: + - state: stars + color: "#94154e" + shader: unshaded + - type: LightFade + duration: 1 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/touch.yml b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/touch.yml new file mode 100644 index 0000000000..b899f6a462 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/touch.yml @@ -0,0 +1,49 @@ +- type: entity + id: CP14ActionSpellGodLumeraTouch + name: Touch of Lumera + description: "Multitasking effects on the world: depending on what you click on, the effect may vary. Using it on an empty space will create a glowing sign that attracts the attention of mortals." + components: + - type: CP14MagicEffectReligionRestricted + - type: CP14MagicEffectManaCost + manaCost: 5 + - type: CP14MagicEffect + effects: + - !type:CP14SpellSpawnEntityOnTarget + spawns: + - CP14LumeraTouchImpact + - !type:CP14SpellGodTouch + - !type:CP14SpellApplyEntityEffect + effects: + - !type:Jitter + - type: EntityWorldTargetAction + repeat: true + checkCanAccess: false + itemIconStyle: BigAction + range: 100 + sound: !type:SoundPathSpecifier + path: /Audio/Magic/rumble.ogg + icon: + sprite: _CP14/Actions/DemigodSpells/lumera.rsi + state: touch + event: !type:CP14EntityWorldTargetActionEvent + cooldown: 0.5 + +- type: entity + id: CP14LumeraTouchImpact + categories: [ ForkFiltered ] + parent: CP14BaseMagicImpact + save: false + components: + - type: PointLight + color: "#3843a8" + enabled: true + radius: 5 + energy: 4 + netsync: false + - type: Sprite + layers: + - state: stars + color: "#3843a8" + shader: unshaded + - type: LightFade + duration: 1 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/warp.yml b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/warp.yml new file mode 100644 index 0000000000..de354f4db0 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/Lumera/warp.yml @@ -0,0 +1,12 @@ +- type: entity + id: CP14ActionSpellGodLumeraWarp + name: Fast travel + description: Allows you to quickly teleport to your altars and followers. + components: + - type: InstantAction + icon: + sprite: _CP14/Actions/DemigodSpells/lumera.rsi + state: warp + priority: -8 + event: !type:ToggleIntrinsicUIEvent + key: enum.CP14ReligionEntityUiKey.Key \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/generic.yml b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/generic.yml new file mode 100644 index 0000000000..1f3db51599 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Actions/Spells/Demigods/generic.yml @@ -0,0 +1,13 @@ +- type: entity + id: CP14ActionRenounceFromGod + name: Renounce patron + description: You renounce your patron by severing your connection with him. After that, you can never become his follower again, but you can become a follower of another patron. + components: + - type: InstantAction + icon: + sprite: _CP14/Interface/Alerts/divine_offer.rsi + state: unoffer + priority: -8 + event: !type:CP14RenounceFromGodEvent + - type: ConfirmableAction + popup: cp14-renounce-action-popup \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Mobs/Player/Demigods/gods.yml b/Resources/Prototypes/_CP14/Entities/Mobs/Player/Demigods/gods.yml new file mode 100644 index 0000000000..b8663233ad --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Mobs/Player/Demigods/gods.yml @@ -0,0 +1,113 @@ +- type: entity + id: CP14MobGodBase + parent: + - Incorporeal + - BaseMob + - CP14MobMagical + name: god + description: The supreme entity formed from the beliefs, desires, and fears of the human race. + abstract: true + components: + - type: Sprite + sprite: _CP14/Mobs/Demigods/temp_icons.rsi + color: "#ffffff99" + - type: PointLight + radius: 10 + softness: 1 + castShadows: false + - type: Input + context: "ghost" + - type: Spectral + - type: MovementSpeedModifier + baseWalkSpeed: 3 + baseSprintSpeed: 6 + friction: 0.5 + acceleration: 3 + - type: NoSlip + - type: Eye + drawFov: false + visMask: + - Normal + - Ghost + - type: Visibility + layer: 2 #ghost vis layer + - type: ContentEye + maxZoom: 1.2, 1.2 + - type: Speech + speechVerb: Ghost + - type: CP14ReligionVision + - type: CP14MagicEnergyContainer + magicAlert: CP14MagicEnergy + maxEnergy: 1000 + energy: 1000 + unsafeSupport: false + - type: CP14MagicEnergyDraw + energy: 5 + delay: 6 # 20m to full restore + - type: IntrinsicRadioTransmitter + channels: + - CP14Gods + - type: IntrinsicRadioReceiver + - type: ActiveRadio + receiveAllChannels: true + globalReceive: true + channels: + - CP14Gods + - type: CP14SpellStorage + grantAccessToSelf: true + spells: + - CP14ActionZLevelUp + - CP14ActionZLevelDown + - ActionToggleLighting + - ActionToggleFov + - type: CP14DemiplaneStabilizer # teleports gods outside when the demiplane closes + enabled: false + - type: UserInterface + interfaces: + enum.CP14ReligionEntityUiKey.Key: + type: CP14ReligionEntityBoundUserInterface + +- type: entity + parent: CP14MobGodBase + id: CP14MobGodMerkas + name: Merkas + categories: [ HideSpawnMenu ] + description: Merkas is the god of purity and healing. In his presence, decay is destroyed. He does not save — he purifies. Those who remain can start anew. + components: + - type: Sprite + layers: + - state: merkas + shader: unshaded + - type: PointLight + color: "#9bf2b5" + - type: CP14SkillStorage + availableSkillTrees: + - GodMerkas + - type: CP14ReligionEntity + religion: Merkas + - type: CP14ReligionVision + shaderColor: 0.02, 0.36, 0.05 + +- type: entity + parent: CP14MobGodBase + id: CP14MobGodLumera + name: Lumera + description: The goddess of the night and the night sky. Her stars hold so many secrets, which she protects with her wings. + components: + - type: Sprite + layers: + - state: lumera + shader: unshaded + - type: PointLight + color: "#4367ba" + - type: CP14SkillStorage + availableSkillTrees: + - GodLumera + - type: CP14ReligionEntity + religion: Lumera + - type: CP14ReligionVision + shaderColor: 0.29, 0.66, 0.87 + - type: IntrinsicUI + uis: + enum.CP14ReligionEntityUiKey.Key: + toggleAction: CP14ActionSpellGodLumeraWarp \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Mobs/Species/base.yml b/Resources/Prototypes/_CP14/Entities/Mobs/Species/base.yml index b7c92bfd61..822bad3780 100644 --- a/Resources/Prototypes/_CP14/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/_CP14/Entities/Mobs/Species/base.yml @@ -231,7 +231,6 @@ types: Cold: 0.25 Bloodloss: 0.25 - - type: CP14SkillStorage - type: CP14TradingReputation diff --git a/Resources/Prototypes/_CP14/Entities/Mobs/base.yml b/Resources/Prototypes/_CP14/Entities/Mobs/base.yml index 5b13e55d92..08b348a2a9 100644 --- a/Resources/Prototypes/_CP14/Entities/Mobs/base.yml +++ b/Resources/Prototypes/_CP14/Entities/Mobs/base.yml @@ -57,6 +57,16 @@ delay: 3 # 5m to full restore - type: CP14MagicUnsafeDamage - type: CP14MagicUnsafeSleep + - type: CP14SkillStorage + availableSkillTrees: + - Pyrokinetic + - Hydrosophistry + - Illusion + - Metamagic + - Healing + - Atlethic + - MartialArts + - Craftsmanship - type: entity diff --git a/Resources/Prototypes/_CP14/Entities/Stations/base.yml b/Resources/Prototypes/_CP14/Entities/Stations/base.yml index f3ed2fed25..68f5be042e 100644 --- a/Resources/Prototypes/_CP14/Entities/Stations/base.yml +++ b/Resources/Prototypes/_CP14/Entities/Stations/base.yml @@ -10,6 +10,7 @@ - CP14BaseStationCommonObjectives - CP14BaseStationDemiplaneMap - CP14BaseStationEconomy + #- CP14BaseStationGods - type: entity id: CP14BaseStationCommonObjectives @@ -35,3 +36,16 @@ abstract: true components: - type: CP14StationEconomy + +#- type: entity +# id: CP14BaseStationGods +# abstract: true +# components: +# - type: CP14StationRandomJobs +# entries: +# - jobs: +# - CP14GodMerkas +# - CP14GodLumera +# count: +# min: 2 +# max: 2 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Structures/Flora/trees.yml b/Resources/Prototypes/_CP14/Entities/Structures/Flora/trees.yml index 7d4833105f..4011a695e9 100644 --- a/Resources/Prototypes/_CP14/Entities/Structures/Flora/trees.yml +++ b/Resources/Prototypes/_CP14/Entities/Structures/Flora/trees.yml @@ -470,4 +470,3 @@ CP14BirchWoodLog: min: 3 max: 6 - diff --git a/Resources/Prototypes/_CP14/Entities/Structures/Specific/Religion/base.yml b/Resources/Prototypes/_CP14/Entities/Structures/Specific/Religion/base.yml new file mode 100644 index 0000000000..5debef049a --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Structures/Specific/Religion/base.yml @@ -0,0 +1,35 @@ +- type: entity + id: CP14BaseAltar + parent: BaseStructure + abstract: true + name: altar + description: TODO + categories: [ ForkFiltered ] + components: + - type: Sprite + noRot: true + drawdepth: Mobs + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.45,-0.45,0.45,0.45" + density: 60 + mask: + - MachineMask + layer: + - MachineLayer + - type: CP14ReligionAltar + - type: ActiveListener + range: 1 + +- type: entity + parent: CP14BaseAltar + id: CP14BaseAltarPrimordial + abstract: true + components: + - type: CP14ReligionAltar + canBeConverted: false + - type: SpawnPoint + spawn_type: Always \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Entities/Structures/Specific/Religion/primordial.yml b/Resources/Prototypes/_CP14/Entities/Structures/Specific/Religion/primordial.yml new file mode 100644 index 0000000000..c71fad7530 --- /dev/null +++ b/Resources/Prototypes/_CP14/Entities/Structures/Specific/Religion/primordial.yml @@ -0,0 +1,35 @@ +- type: entity + parent: CP14BaseAltarPrimordial + id: CP14AltarPrimordialGodLumera + name: primordial statue of Lumera + description: "The enchanting statue of Lumera, patroness of the starry sky, night, and mysteries. She does not demand worship—she simply observes. And waits for you to ask." + components: + - type: Sprite + sprite: Structures/Furniture/Altars/Gods/convertaltar.rsi + layers: + - state: white + - type: CP14ReligionAltar + religion: Lumera + - type: CP14ReligionObserver + observation: + Lumera: 25 + - type: SpawnPoint + job_id: CP14GodLumera + +- type: entity + parent: CP14BaseAltarPrimordial + id: CP14AltarPrimordialGodMerkas + name: primordial statue of Merkas + description: A beautiful, overgrown statue of Sylvania, revered as a goddess of nature. + components: + - type: Sprite + sprite: Structures/Furniture/Altars/Gods/nanotrasen.rsi + layers: + - state: druid + - type: CP14ReligionAltar + religion: Merkas + - type: CP14ReligionObserver + observation: + Merkas: 25 + - type: SpawnPoint + job_id: CP14GodMerkas \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Religion/religions.yml b/Resources/Prototypes/_CP14/Religion/religions.yml new file mode 100644 index 0000000000..da2846cb79 --- /dev/null +++ b/Resources/Prototypes/_CP14/Religion/religions.yml @@ -0,0 +1,5 @@ +- type: cp14Religion + id: Merkas + +- type: cp14Religion + id: Lumera \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Roles/Jobs/Demigods/lumera.yml b/Resources/Prototypes/_CP14/Roles/Jobs/Demigods/lumera.yml new file mode 100644 index 0000000000..90c4e7e1ca --- /dev/null +++ b/Resources/Prototypes/_CP14/Roles/Jobs/Demigods/lumera.yml @@ -0,0 +1,16 @@ +- type: job + id: CP14GodLumera + name: cp14-job-name-god-lumera + description: cp14-job-desc-god-lumera + setPreference: false + playTimeTracker: CP14GodLumera + requirements: + - !type:OverallPlaytimeRequirement + time: 36000 # 10 hrs + canBeAntag: false + icon: CP14JobIconGodLumera + jobEntity: CP14MobGodLumera + joinNotifyCrew: false + requireAdminNotify: true + jobPreviewEntity: CP14MobGodLumera + applyTraits: false \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Roles/Jobs/Demigods/merkas.yml b/Resources/Prototypes/_CP14/Roles/Jobs/Demigods/merkas.yml new file mode 100644 index 0000000000..de2828a4d5 --- /dev/null +++ b/Resources/Prototypes/_CP14/Roles/Jobs/Demigods/merkas.yml @@ -0,0 +1,16 @@ +- type: job + id: CP14GodMerkas + name: cp14-job-name-god-merkas + description: cp14-job-desc-god-merkas + setPreference: false + playTimeTracker: CP14GodMerkas + requirements: + - !type:OverallPlaytimeRequirement + time: 36000 # 10 hrs + canBeAntag: false + icon: CP14JobIconGodMerkas + jobEntity: CP14MobGodMerkas + joinNotifyCrew: false + requireAdminNotify: true + jobPreviewEntity: CP14MobGodMerkas + applyTraits: false \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Roles/Jobs/departments.yml b/Resources/Prototypes/_CP14/Roles/Jobs/departments.yml index c769d2d731..839585efda 100644 --- a/Resources/Prototypes/_CP14/Roles/Jobs/departments.yml +++ b/Resources/Prototypes/_CP14/Roles/Jobs/departments.yml @@ -1,3 +1,14 @@ +- type: department + id: CP14Demigods + name: department-CP14Demigods + description: department-CP14Demigods-desc + weight: 666 # >:) + color: "#ffffff" + editorHidden: true #temp + roles: + - CP14GodLumera + - CP14GodMerkas + - type: department id: CP14Command name: department-CP14Command @@ -5,6 +16,7 @@ primary: false weight: 10 color: "#3ec8fa" + editorHidden: true #temp roles: - CP14GuardCommander - CP14Guildmaster @@ -15,6 +27,7 @@ description: department-CP14Guard-desc weight: 9 color: "#576384" + editorHidden: true #temp roles: - CP14GuardCommander - CP14Guard @@ -40,5 +53,4 @@ color: "#588151" roles: - CP14Guildmaster - - CP14Adventurer - + - CP14Adventurer \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Roles/play_time_tracker.yml b/Resources/Prototypes/_CP14/Roles/play_time_tracker.yml index 521b6a568c..e0b11fe95f 100644 --- a/Resources/Prototypes/_CP14/Roles/play_time_tracker.yml +++ b/Resources/Prototypes/_CP14/Roles/play_time_tracker.yml @@ -37,3 +37,9 @@ - type: playTimeTracker id: CP14JobMerchant +# Gods +- type: playTimeTracker + id: CP14GodMerkas + +- type: playTimeTracker + id: CP14GodLumera \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Shaders/shaders.yml b/Resources/Prototypes/_CP14/Shaders/shaders.yml index 468675576d..abe9eaf7b2 100644 --- a/Resources/Prototypes/_CP14/Shaders/shaders.yml +++ b/Resources/Prototypes/_CP14/Shaders/shaders.yml @@ -19,3 +19,8 @@ FalloffStrength: 0.75 FalloffPow: 3.0 HDR: 1.5 + +- type: shader + id: CP14ReligionVision + kind: source + path: "/Textures/_CP14/Shaders/religion.swsl" \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Skill/atlethic.yml b/Resources/Prototypes/_CP14/Skill/Basic/atlethic.yml similarity index 100% rename from Resources/Prototypes/_CP14/Skill/atlethic.yml rename to Resources/Prototypes/_CP14/Skill/Basic/atlethic.yml diff --git a/Resources/Prototypes/_CP14/Skill/craftsmanship.yml b/Resources/Prototypes/_CP14/Skill/Basic/craftsmanship.yml similarity index 100% rename from Resources/Prototypes/_CP14/Skill/craftsmanship.yml rename to Resources/Prototypes/_CP14/Skill/Basic/craftsmanship.yml diff --git a/Resources/Prototypes/_CP14/Skill/dimension.yml b/Resources/Prototypes/_CP14/Skill/Basic/dimension.yml similarity index 100% rename from Resources/Prototypes/_CP14/Skill/dimension.yml rename to Resources/Prototypes/_CP14/Skill/Basic/dimension.yml diff --git a/Resources/Prototypes/_CP14/Skill/healing.yml b/Resources/Prototypes/_CP14/Skill/Basic/healing.yml similarity index 100% rename from Resources/Prototypes/_CP14/Skill/healing.yml rename to Resources/Prototypes/_CP14/Skill/Basic/healing.yml diff --git a/Resources/Prototypes/_CP14/Skill/hydrosophistry.yml b/Resources/Prototypes/_CP14/Skill/Basic/hydrosophistry.yml similarity index 100% rename from Resources/Prototypes/_CP14/Skill/hydrosophistry.yml rename to Resources/Prototypes/_CP14/Skill/Basic/hydrosophistry.yml diff --git a/Resources/Prototypes/_CP14/Skill/illusion.yml b/Resources/Prototypes/_CP14/Skill/Basic/illusion.yml similarity index 100% rename from Resources/Prototypes/_CP14/Skill/illusion.yml rename to Resources/Prototypes/_CP14/Skill/Basic/illusion.yml diff --git a/Resources/Prototypes/_CP14/Skill/martial_arts.yml b/Resources/Prototypes/_CP14/Skill/Basic/martial_arts.yml similarity index 100% rename from Resources/Prototypes/_CP14/Skill/martial_arts.yml rename to Resources/Prototypes/_CP14/Skill/Basic/martial_arts.yml diff --git a/Resources/Prototypes/_CP14/Skill/metamagic.yml b/Resources/Prototypes/_CP14/Skill/Basic/metamagic.yml similarity index 100% rename from Resources/Prototypes/_CP14/Skill/metamagic.yml rename to Resources/Prototypes/_CP14/Skill/Basic/metamagic.yml diff --git a/Resources/Prototypes/_CP14/Skill/pyrokinetic.yml b/Resources/Prototypes/_CP14/Skill/Basic/pyrokinetic.yml similarity index 100% rename from Resources/Prototypes/_CP14/Skill/pyrokinetic.yml rename to Resources/Prototypes/_CP14/Skill/Basic/pyrokinetic.yml diff --git a/Resources/Prototypes/_CP14/Skill/skill_tree.yml b/Resources/Prototypes/_CP14/Skill/Basic/trees.yml similarity index 75% rename from Resources/Prototypes/_CP14/Skill/skill_tree.yml rename to Resources/Prototypes/_CP14/Skill/Basic/trees.yml index 3a59a71c23..fa7cf18678 100644 --- a/Resources/Prototypes/_CP14/Skill/skill_tree.yml +++ b/Resources/Prototypes/_CP14/Skill/Basic/trees.yml @@ -3,72 +3,48 @@ name: cp14-skill-tree-pyrokinetic-name desc: cp14-skill-tree-pyrokinetic-desc color: "#d6933c" - icon: - sprite: _CP14/Actions/skill_tree.rsi - state: pyro - type: cp14SkillTree id: Hydrosophistry name: cp14-skill-tree-hydrosophistry-name desc: cp14-skill-tree-hydrosophistry-desc color: "#1554a1" - icon: - sprite: _CP14/Actions/skill_tree.rsi - state: water - type: cp14SkillTree id: Illusion name: cp14-skill-tree-illusion-name desc: cp14-skill-tree-illusion-desc color: "#f55faf" - icon: - sprite: _CP14/Actions/skill_tree.rsi - state: light - type: cp14SkillTree id: Metamagic name: cp14-skill-tree-metamagic-name desc: cp14-skill-tree-metamagic-desc color: "#56e5f5" - icon: - sprite: _CP14/Actions/skill_tree.rsi - state: meta - type: cp14SkillTree id: Healing name: cp14-skill-tree-healing-name desc: cp14-skill-tree-healing-desc color: "#51cf72" - icon: - sprite: _CP14/Actions/skill_tree.rsi - state: heal - type: cp14SkillTree id: Atlethic name: cp14-skill-tree-atlethic-name desc: cp14-skill-tree-atlethic-desc color: "#b32e37" - icon: - sprite: _CP14/Actions/skill_tree.rsi - state: atlethic #- type: cp14SkillTree # id: Dimension # name: cp14-skill-tree-dimension-name # desc: cp14-skill-tree-dimension-desc # color: "#ac66be" -# icon: -# sprite: _CP14/Actions/skill_tree.rsi -# state: dimension - type: cp14SkillTree id: MartialArts name: cp14-skill-tree-martial-name desc: cp14-skill-tree-martial-desc color: "#f54242" - icon: - sprite: _CP14/Actions/skill_tree.rsi - state: martial # diff --git a/Resources/Prototypes/_CP14/Skill/Demigods/lumera.yml b/Resources/Prototypes/_CP14/Skill/Demigods/lumera.yml new file mode 100644 index 0000000000..4aa20b8d1c --- /dev/null +++ b/Resources/Prototypes/_CP14/Skill/Demigods/lumera.yml @@ -0,0 +1,75 @@ +# T1 + +- type: cp14Skill + id: LumeraT1 + skillUiPosition: 1, 0 + tree: GodLumera + name: cp14-skill-lumera-t1-name + learnCost: 0.5 + icon: + sprite: _CP14/Actions/DemigodSpells/lumera.rsi + state: t1 + +- type: cp14Skill + id: LumeraTouch + skillUiPosition: 0, 3 + tree: GodLumera + learnCost: 0.0 + icon: + sprite: _CP14/Actions/DemigodSpells/lumera.rsi + state: touch + effects: + - !type:AddAction + action: CP14ActionSpellGodLumeraTouch + restrictions: + - !type:NeedPrerequisite + prerequisite: LumeraT1 + +- type: cp14Skill + id: LumeraRenounce + skillUiPosition: 2, 3 + tree: GodLumera + learnCost: 0.0 + icon: + sprite: _CP14/Actions/DemigodSpells/lumera.rsi + state: renounce + effects: + - !type:AddAction + action: CP14ActionSpellGodLumeraRenounce + restrictions: + - !type:NeedPrerequisite + prerequisite: LumeraT1 + +# T2 + +- type: cp14Skill + id: LumeraT2 + skillUiPosition: 7, 0 + tree: GodLumera + name: cp14-skill-lumera-t2-name + learnCost: 0.5 + icon: + sprite: _CP14/Actions/DemigodSpells/lumera.rsi + state: t2 + restrictions: + - !type:NeedPrerequisite + prerequisite: LumeraT1 + - !type:GodFollowerPercentage + percentage: 0.3 + +# T3 + +- type: cp14Skill + id: LumeraT3 + skillUiPosition: 13, 0 + tree: GodLumera + name: cp14-skill-lumera-t3-name + learnCost: 0.5 + icon: + sprite: _CP14/Actions/DemigodSpells/lumera.rsi + state: t3 + restrictions: + - !type:NeedPrerequisite + prerequisite: LumeraT2 + - !type:GodFollowerPercentage + percentage: 0.6 \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Skill/Demigods/merkas.yml b/Resources/Prototypes/_CP14/Skill/Demigods/merkas.yml new file mode 100644 index 0000000000..53c5d70e09 --- /dev/null +++ b/Resources/Prototypes/_CP14/Skill/Demigods/merkas.yml @@ -0,0 +1,12 @@ +- type: cp14Skill + id: NatureGodT1 + skillUiPosition: 1, 0 + tree: GodMerkas + name: cp14-skill-life-t1-name + learnCost: 0.5 + icon: + sprite: _CP14/Actions/skill_tree.rsi + state: heal + effects: + - !type:AddAction + action: CP14ActionSpellBloodPurification \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/Skill/Demigods/trees.yml b/Resources/Prototypes/_CP14/Skill/Demigods/trees.yml new file mode 100644 index 0000000000..45f51c7847 --- /dev/null +++ b/Resources/Prototypes/_CP14/Skill/Demigods/trees.yml @@ -0,0 +1,12 @@ +- type: cp14SkillTree + id: GodMerkas + name: cp14-job-name-god-merkas + desc: cp14-job-desc-god-merkas + color: "#51cf72" + +- type: cp14SkillTree + id: GodLumera + name: cp14-job-name-god-lumera + desc: cp14-job-desc-god-lumera + color: "#5632a8" + parallax: Default \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/StatusIcon/god_job.yml b/Resources/Prototypes/_CP14/StatusIcon/god_job.yml new file mode 100644 index 0000000000..071828dcdd --- /dev/null +++ b/Resources/Prototypes/_CP14/StatusIcon/god_job.yml @@ -0,0 +1,15 @@ +- type: jobIcon + parent: CP14JobIcon + id: CP14JobIconGodMerkas + icon: + sprite: /Textures/_CP14/Interface/Misc/job_god_icons.rsi + state: Merkas + jobName: cp14-job-name-god-merkas + +- type: jobIcon + parent: CP14JobIcon + id: CP14JobIconGodLumera + icon: + sprite: /Textures/_CP14/Interface/Misc/job_god_icons.rsi + state: Lumera + jobName: cp14-job-name-god-lumera \ No newline at end of file diff --git a/Resources/Prototypes/_CP14/radio_channels.yml b/Resources/Prototypes/_CP14/radio_channels.yml new file mode 100644 index 0000000000..d0f0b2b09a --- /dev/null +++ b/Resources/Prototypes/_CP14/radio_channels.yml @@ -0,0 +1,7 @@ +- type: radioChannel + id: CP14Gods + name: cp14-chat-radio-gods + keycode: 'g' + frequency: 666 + color: "#fad634" + longRange: true diff --git a/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/meta.json b/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/meta.json new file mode 100644 index 0000000000..9e51bb79c9 --- /dev/null +++ b/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/meta.json @@ -0,0 +1,29 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "All right reserved", + "copyright": "Created by TheShuEd", + "states": [ + { + "name": "touch" + }, + { + "name": "warp" + }, + { + "name": "renounce" + }, + { + "name": "t1" + }, + { + "name": "t2" + }, + { + "name": "t3" + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/renounce.png b/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/renounce.png new file mode 100644 index 0000000000000000000000000000000000000000..04a67e9dfcf158f2e40729151cfa1df558d1de53 GIT binary patch literal 422 zcmV;X0a^ZuP)Px$U`a$lR9J=WRXuCNFcdtOLj4aqb_*p_p<9!dkR_f&$L`rZ7Z3RX-o2+=$ZRhm zr0t%m1aBVw57bE=ia;^?cv75@!3V}*KfgQc-AO=)4g@Un9cPSjYCU6&FY+C!{+d!$ zRyCHJ|Hfe$1~&5LW{739@wO~lE`O(M=lt`c!}a)G7*-#$i1 zn;_f{0I#Ea*wUc`DTX?75jAQ=TY+lkl!%^;+(iUgpl{A?`-HgU=cJhlmzPzI`D|kR zCdmrkySoJdyhRTOwJM)nQ3UA7$MK4fh0Pl@j9J)^&+ppMM5=# z)}f1zOni(8>j;|0k;&da_WW2OP99JZJU#ZUH70mDS4ZNL1m_gU*$I8gJ6r_*0AhGb^vCRM Q#{d8T07*qoM6N<$f~DiT@c;k- literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/t1.png b/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/t1.png new file mode 100644 index 0000000000000000000000000000000000000000..7e39d4d0b9dbdf31d8acb1a9f59aba3699b9c95e GIT binary patch literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}^F3W0Ln2y} z6C`*Z+?}fbKYQA}AMZo9yt8L!W^UfL^ zZ&A9)a!M3m)6J7d4jed8@aB!_A`{M+9}I#{Gak9+Deus2As> z#ewd@22y*juq{@!;__fpRz7%PS7V2+2>T+%9_a(SQkWCfg+Z21-6*#At^3yE1J=u# aIT_Y0NSO64K0_VoR0dC1KbLh*2~7a}wOFPx$AW1|)R9J=0mmv;=FcgM=&{NYpz_KivnA;p`ci;GO~TVM;S@>+yRA-o_J0C0Z(+g4&CWWZ-S~P=UQi#!+ zy+FAFl1vu>UhfZS(*C@2xB{Ru?Z`yVH^t(6RLR8ia^6`{_BC38ptME=oHE!ZE6vl5YiB)L1efK(UL6fjw1`EOs( zHaezRnSLM+xwytdcwF*Jwg%G$fZO$Co8M)w{yAM!UyJL;vLF>FG5`Po07*qoM6N<$ Ef*O&ORR910 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/t3.png b/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/t3.png new file mode 100644 index 0000000000000000000000000000000000000000..49057930600e7f0a45d3d404183457af07b2ab68 GIT binary patch literal 375 zcmV--0f_#IP)Px$F-b&0R9J=0S1S^OFc4kPRn;7T>2wBB*E&@1z%8O9(cB`ILsZmNFvD~>02+l) zRXPJD$>u|oXT$D$>^A|30aHJ7$_8GNmIDCX?&RT{L-;I7%PAZ9)X)5TNZG)pmMfvY z1PRU*0O0w0ql}K{L*h#Smh(T@Bj-_Z8rKfSKvc5mAX{<9MMc@bNs(=yHQfkxl^gj) zf2<;Q5t6!)H{H1TJt;EuY(c5C9YC15p^X6cPnvH0i3rA4D*&5!TU%v_ObSm1T|p`Z z_*y=yT0nKdo=^&qwV4TIK16i2ZQ_~+T`Ou+;1UIlZCry$Mr0EOtj$7zOmSW1iOS*n z%w!n_U&mU=(&Fnv5(oCaAZ|r5#6?&R@g&2**Mbt9DFDFrazY#WdL1n|gFj9~;sbXL V)C2-%{}ccK002ovPDHLkV1j-HnhgK| literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/touch.png b/Resources/Textures/_CP14/Actions/DemigodSpells/lumera.rsi/touch.png new file mode 100644 index 0000000000000000000000000000000000000000..f41f19d474bd660a545c205c6aecddfbb2807100 GIT binary patch literal 359 zcmV-t0hs=YP)Px$AxT6*R9J=Wma%SwFcd{^s!CZpb>;&qt5Sw+Kr(pB+_itW7O0M4SbLFz5Q&dKj3v7qPx$mq|oHR9J=WRIyIPFbvHhr0c-M%mS1ZG3w^K3f1J+D?jc z(A}~B`tOdt(|?K&!MX_{#=dPjAySwPx>SV=@dRCt{2o%wPbxe>;HqOS3^cGiiLR4K`8*blN#=?8J%!>-yR>+E`MOVCB` zA3$@O@XT;d3`Xp)YDyHvqX*~@eW4*R7z_r3!C){L3t2w{i zV3-?5fQ4i36?6!4kp18Zki#VO#ZqN=&TVp${h$eu%Ov!{l4UQ7;%IIe1cM2%9PsEaulhx=d zOHmX@W6EzXP)|IR6y#Fn7YvnuRs!U-5*;#@NP@8nX=VWi;vtauA>6|fNo<%IOIMnl zHq{vny)Z<9$54KAfeZ%2aV9)Aj5J7^2A^lIosk$+BDL(Bs~8xrJ9lznl*RI=M{P}-B&Ik(A?@NHzu zS@{K3_c68H2kinuH|3vNmZ`7=+3V(E>IEk$0hTZkOG6t4$T$9ih6|w_R6vcW5aXO<;erA}1gNy;f1g!A=5U1hiY6$_OQl1rXjFAI5>13i`CkBk176|>|2N=O8X7U}jw6Ifd?hzRVQJ4$86Iujr$z9|12;5`di{V7+SCwS%mH}F@y^4tTz zl7CEej9>L1;VsxXUh$9cF3=5dmr{Y+K&}4b`kdg&E8{J0oQ@5W$lrIu|Akln*WvL} zAS>uIyz;*aKcC@O)#Drk;YfjW{PH+lkMQ>;Zbf$Y5D;4dapO+Zb1T?i!FJ9a$u&fv zF;0cJ04|(!7Yhi4#m5vs{{*i<&+#Pq4Y+a6-8HSCGp11WcSsgXR{wKKWvX=}W=^5T z@(Wov1i8btrp5|X$seBUH3yw7Kv42`z*|bcU)-XcApzng@H^h+`HWw6p5raSb3$gQ zZe^Jxg{mf2{QMid(yxOo=Xfh{&Te+7O7=NZsImZwqRV~Bc!~w68~f2ysJ(7lk>Fi; z>J9LNbMCcHjW)qKcOh3F8M-1Ukr3b`@H3tWYrNvW!Ux5VDOc7ih{8R^Ta71pB24Xe zh~>HiuJN(UO?dv4(k(irVqT*xlDz&bT*vA)qaz})UggE=9JA{Jed7kH$4AlYPt@x> z>vM0`2NKA2fsX~c!~+*3wj9CC-vVFo?#}Pw_cOdgKVdgG+d&xMbG+ieq_i66b?+Hb zb|JtmB>}D}vp%9Yb$+xc&KcfXJPDVOrqXJRh>iNGQ_k9LsdUd2c{PEBygHrQ8M9

Oc;{Fq#Qk|Rt0a|%(!gY-&MZBtN*~RTS z#Vh<1{Czy7!D@^NWd*dHcj}UHukzz{4MAK_xwPxl*@}%31iueI-vaOU1;UZ?&y_;m z;FabI_&@L!_$55PBq}C_K|RL1M=$Zp{|c{scQm_9qoF8@jdM=i2(bj>3W!D6)&i8$ zX#zZ=RQ&t!HzCs0*ifcC?}Mx|ZrU}9#~w-xCyFmMgj@A_odfwX2q11y7v=u}pF)Ls z3TPYr;D5%)0^ozg}0Dr-|6{iW(iL&Ae0ltRoD#*0szExyk;_QoLkED)}YFBI=%Sarvp+2V{#|WAX zaf-AR23dfvtG;B8oOAM&!~!3jb9-H=5h1IFF1R%Lkm52)3iTKKM!N6u1o%6i6xYC) zDcuOQii)y`{1PD7eYdt6*OW?srXD*Y7it3e*I|*6?+|5OtJmxhSXm97M!jb@Z7Q+Q z8d$fr0(0$s(N^O%J|=jRxF#S9vkkoJ227#5U;u&el5S({c) z@wp{dxTY+mNm^0o)Th3OxZt&d#)`C_FTHD5*9GbtZ5oA0j;Mx^92FJO6}}o;J9t5{ z?(ih|j3>Y)r4L@U$9;4}TLI)AgbbD6tw?c487s&YE7R2S>n+fN4fWPn{x0tk0_gOq zes8)o=m-rll0to_9;bj5w7zMGp~3VAb9_%pf?vaD$hL|bChR9@(-6~O`r{hH6{Y(i zWZ1faQ@C(#8e*yv2^GP!kU+n88$WcBY3_y?u>x;X`2Qm{#7Hpw2k>Kf|3~0|_+V70 z_QsaIcSB52BC%pZ0C}DKEc#m z6y#8h)es{DUg4Dp%3cDoU{lFIv>`?aAg_;8s2e(3fjJvuQv3hC8)7Q9;NIY4fG?E6 z@pVxYQ+H!^ZiuOf8N^CB{Qc?x`JM^3b`NQYsgPfw-JqRq?p@^XYXNja4D_hx&Qv(7 zYKXbSEAzG?Mh~E}96GxryGmhy3Ux3V8e$$%27!*!t=IsYDA?B<%JjaQHYwDx5LUt+nx1R3eg0u+GpOELY-3n zriK_HLmV*cIADhiedO;_APq6HBT^b-KH=Sn%MdUg zQ6DhZZJ8JyQ5Ga_h>^Yhk0|r|x~r-xU>3J;o%k3Sy?5uHlE6adJ5)aa%>ncF^M(+& zmjzHHxF8g<078ZLL%hQ0aDs$NVM;akDSiusSb$e}Vtl16%b$_Y2@*PuDg6Ki<@R8) zQdg7;BHN#5Jwc)-jVZ31uHqi6-`A9iUllNG3)~x%HpGOEuWhB5ncF5iG{lJ8pabUr z;P?N3M?=%hgtF`OUz>&)-TWdJ>?ykq^n;tqJ3#yEO${+|tv~{1-2>2T-%k}%Ok!3i zNOWk3$?*hwfi%MY=)DDn4f{4 z*lmcB38@R13;Y)7WF$~lKsUr(;@!4Ob{k@}GKm{;t}aogl>bDdZcBq4>!`=QDqucQ z*OmYbAn~LPF#`hTsUgLbx`r4D#@YtVQx(7J(9{s4i&<>~W=)us^6zViS+N^4)CJ6) z8en2f-VoC@U`~X+4KX1L)FxmS1)GBawGA zN$V3NQXobniml9O!Kitxk~WgVY3QeS*Z)P+ejBurFYaPmoB04i(&{ zfO*pP$2!Q^YqzUm4BG_Ey$PI>0EcvfL{8WnFwb?AdeirgaDqfDq$I$SlwLyz%<=Z; z+?8Jk%&_Z1%#r@=3%&mbqCj~Ffm28Ri$leB=xx9}*UH%i>O^IoGf%bsc`%}5W-0^b zA)jvqXn`63AHXQ6Ijd+9_fYr$3>Zm%BYPx$MoC0LR9J=Wl`(F@KoCWLq7<&(x!Qe?km$@0$p|JE9>fuy9Qq(9a}Y{P1I?6vjxm**%?wO9)C1;qA< z3uGcS8e`NqkY(ARNAnYlsn8b|DuUwPA^;WqRcjxP z{x*5VVmkIhJZa%|(*gjhvcP7&joNKh7MFewVvOlI#G}igZdz1jfvPN=JM}%Iegcss z0#G>Dd^x*%_4z2jx=7ur!+bd#fVdz;fW>r-{o#!L;jBW~lETSHng<|Y2>Be4K0m%h zGagRxcCbB!!JnZ7BFPmjg%1EY1Rx(o+CXW+wzm_23W1-a^VWlj2Xj9!z`YPx%!bwCyR9J=Wmp^D)K^(`w&x$-AbrF>Z8LHqAY+oiD5rV~`iBm!8(2kiBDRk-5 zDFm`)aS2GwkU1Sn3Z2AG6+wbdX)755SBF4Qd5fipv|IC&6#?g_jXv84zyf763mXyqI$V z{DAOkZVTW9aliN8(fmv13A~!Ss?jI36qIRIQfN1kprlZ6%o9~fp`=it-9&;`C54uP zGHvvU<7wiOu|dUjQUHKbP^KH981nH6qRAxAFUEMGb`4`{k%zW3H^+0GHEU0&Q_4QP zm0*3mnd`^$VslrZ(I>Q+P73-#dOg3JfO#bVSZ03q?JIY3q9_W?!vf%BUT^l$`S2N$ zdlB5eerbRJU@(^tkDv}zclLGrY0yX*myI9UR(h4tN~!VvITXZBDHsbayAM8Xbr~3aob$x@Y>l?%K_0yVV!4E^D2jqPwk-hrA;2#zc1ebbm#kO; z-PGt4da{;-m-~d}fzxHet+X;ZRZ%m9;S0N(1NigH#6o%q7FQ2pcrRdWzs!O8&59)} zo|m=<+OrWhLNQ;>^$=KG@0b@?rJzhpL3wGfX3LKMZ|WDxr-b0RDpnEz0000zw|dCy)D_)tmWfQ5r`}0LQv-RylP_qODJ#`!vM@-NxYQ L>gTe~DWM4fVpLK1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CP14/Interface/Misc/job_god_icons.rsi/meta.json b/Resources/Textures/_CP14/Interface/Misc/job_god_icons.rsi/meta.json new file mode 100644 index 0000000000..752f9b0614 --- /dev/null +++ b/Resources/Textures/_CP14/Interface/Misc/job_god_icons.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "version": 1, + "size": { + "x": 13, + "y": 13 + }, + "license": "CC-BY-SA-4.0", + "copyright": "Created by TheShuEd (Github)", + "states": [ + { + "name": "Merkas" + }, + { + "name": "Lumera" + } + ] +} diff --git a/Resources/Textures/_CP14/Mobs/Demigods/temp_icons.rsi/lumera.png b/Resources/Textures/_CP14/Mobs/Demigods/temp_icons.rsi/lumera.png new file mode 100644 index 0000000000000000000000000000000000000000..47725ded6eafb4509253ac3a91f5da4e90936b50 GIT binary patch literal 1990 zcmV;%2RZnOP)Px+fJsC_RCt{2T{~_gIS{1{1J+r51v%0t!bFQXN{4#@hdPR}(ZI=0WM9E&u_H|| zOQTd>e-_!@13h4{veYE2SgayhY`!h!ibZkLtK!4-e8%7XJbmxhUN%O_f0tb$CHS9$ zs#~TZZnG-7A_ma_A0HoQ_+Jg$=+}O?w$lPj8?AbH+G9fy?EKwscT*eve19!(FQ*F^ zYzaI2<8;1%cr2xqvfJ&t&k>p~R#?Ij?nCMs)&pt*Sn%`xH6rT;C}U;R>jl?l<{U=# zA%+-&YQg<+I%f*Z8DW2%x`&`_T{LK3Ev|wN(=#dT{y3dA*QLtUPy_3s>b`$?OkeV8 zx@2`etjD4Q>j4pgKHp!<=lg5<@$~KMyZ`=u@>UaWe?(nU-I8~4APvwe@LzxbLw@!m zqvJ;HOQ1JbBUsuN=K#D)fSV*M?^l zaq#F8H3BZ+ehVL{s~Ac!>hI_-k}Avn4P~+zVYDt7(92BAP$)1bU^WiZ^Vt#( zD3?A=&u7XI@O!T=`{Q)pAE#NpumkJopQrCPRNU9v;Reqg7C4Gp2R8>)p<5=^&-Yh# zj>*tP8@YA8PsxG}0iQce&$EgCR$89bza{I!4z+p?*GXDv(w3;Lx|A~Se9jo2#Rd$A z7>Db)QMyW5QtJ8iiATcy!{gAo#tJAu zmJxnmiafu-)Osnujez?G=eb-RP%EQ4&b!8@gy-tc1lNv5erO!2d^H000vHMEZ{{OO zoOH65qaX>-P12szGVWPP2aDmkIL`HlWx)|Xl*c3K?d3FcH)|alEGJ8mXQN)H<;2t# z5}3nG9w4oBqM=6`fq3LtPL{N1EjsiFf394R1k3tbQ=rAoJ?F*TvpsacOIx^uw?H+3 zWY!bF^w0ghC8A%mz&-OkGZt07e0iwcs%0*(Wr~Zfz9+@%? zYTgNZC*WxK>6s0U)}0+nA<~qk^4=WQdR0mC0z4074V3E*nT65C06krHDfH55RUWY9 z6{Qh(6DV{sKy))S>WUKn&eHk4Zt-;UfF-USMS$Pfu)fWZh0u{CwezEnBrPveEdg%C zTRWdx0a-Ew6ha=rH6*2#E7rOMZRz|{;^DJPXK*8IE$;zo7%Bg-e9o3Tzo+b*=#)`t zNdQK%s(rwkuoaKJq7mJ!yk)+ik+P@r1xe_^x08t_xQ?!Kz`}4FU~POmS!FY1j>zf` zL2KjNX)W6tc3Sz4!Xaoad^D%neW-H&02Y8g?R~fUoj&R)) zn(qf_uyO}Q`F1o=Z%9j_B8%$?wk^SRL&n3m<2i^~;yl*VrWSrYm&q~A7c?^YcJN(V zu1+XJ1h_1&8@6p7FjD8k@2p(%b~t45?PNUf zSpiAENBE9rSCm>IxhVDG7FC{;$I_`7xD8sAQ@vK+Gcj>r(SCB7XCsJX$io5kqCca(ACV$TJc~^Bv7rL0jY7;XrN3vQOSJ_>S2Bfx}k# zb||#GFs31!@2E>Qv|JY1nX7L2Es7R|W^-=KPQAd?n{Ouqt}QJU0riVy zh$*B^!b|b(M9{)_R7&nLw2RVLz92i&D;&+Y(-R|fNv&)NzMT>1saLm+*q_q*rS#S1 Y7nQOyW5CmeRsaA107*qoM6N<$f;qLsF8}}l literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CP14/Mobs/Demigods/temp_icons.rsi/merkas.png b/Resources/Textures/_CP14/Mobs/Demigods/temp_icons.rsi/merkas.png new file mode 100644 index 0000000000000000000000000000000000000000..4d2d27105db3d6c9766c02019a2725234f251030 GIT binary patch literal 1096 zcmV-O1h@N%P)Px(0!c(cRCt{2T|08zFbu_JQs@#ceLR;gT}5P9 z@G~RFqmEbpe=A;10)G7Bcji-qpEG`h7R15(>*oasH>;Q1RL4dkAlq5`UB?7-J@g=abOW!8ED31%E;bDui-!XMUai+}RX~fEfZS4QfnOS`fAhEC|KgrS(t(n}9sGulK=6 zZ$+)(XJ1?EnqG)hKsM+Zjw+>nB@@t-)iXRx)Z+uE!cRL7*fY|yJ$bN6oPgF?FdFuH zza&Hm=$#9XFc|mRD7Bl)B4C7oYJ(dC(=%ZY1X|mmLck2;z#07?K#+jCCmtDWop~jW zY^i;bJWXR0&@z68g;~o7i6n~Wf{YOa7(=8*V9rFnJ|Eg^7w7y}6{EZX!-ce;(SnQ- z1Q-WI5lM)~FW}MWcCSI~1K+YCQY);9k3P~pq`a|uYIOs9VJ=>mG9O{yl2Zr zuRU6oO3FH{Xj2bH(+k?V*69^cmT@j+uXB6}lwwWUUzcJXGm@a?v*;Kwx<14nG(|g9 z*8ww-pzQS;;nqo}=LIlhz+N3Sk1Q26@If4LVh_wf| zLPmnn@G-