Skip to content

Commit

Permalink
Merge branch 'master' into fixsmoothslider
Browse files Browse the repository at this point in the history
  • Loading branch information
sleepyyapril authored Jan 1, 2025
2 parents 785ad3d + 71f2005 commit 0069ea0
Show file tree
Hide file tree
Showing 12 changed files with 1,540 additions and 627 deletions.
580 changes: 580 additions & 0 deletions Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.Functions.cs

Large diffs are not rendered by default.

634 changes: 260 additions & 374 deletions Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Content.Server/Chat/TelepathicChatSystem.Psychognomy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ private void DescribeGlimmerSource(EntityUid uid, GlimmerSourceComponent compone
// This one's also a bit of a catch-all for "lacks component"
private void DescribePsion(EntityUid uid, PsionicComponent component, GetPsychognomicDescriptorEvent ev)
{
if (component.PsychognomicDescriptors != null)
if (component.PsychognomicDescriptors.Count > 0)
{
foreach (var descriptor in component.PsychognomicDescriptors)
{
Expand Down
31 changes: 15 additions & 16 deletions Content.Server/Nyanotrasen/Psionics/NPC/PsionicNpcCombatSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,24 @@ public override void Initialize()
SubscribeLocalEvent<NoosphericZapPowerComponent, NPCSteeringEvent>(ZapCombat);

NoosphericZap = _protoMan.Index(NoosphericZapProto);
DebugTools.Assert(NoosphericZap.Actions.Count == 1, "I can't account for this, so it's your problem now");
}

private void ZapCombat(Entity<NoosphericZapPowerComponent> ent, ref NPCSteeringEvent args)
{
PsionicComponent? psionics = null;
if (!Resolve(ent, ref psionics, logMissing: true)
|| !psionics.Actions.TryGetValue(NoosphericZap.Actions[0], out var action)
|| action is null)
return;

var actionTarget = Comp<EntityTargetActionComponent>(action.Value);
if (actionTarget.Cooldown is {} cooldown && cooldown.End > _timing.CurTime
|| !TryComp<NPCRangedCombatComponent>(ent, out var combat)
|| !_actions.ValidateEntityTarget(ent, combat.Target, (action.Value, actionTarget))
|| actionTarget.Event is not {} ev)
return;

ev.Target = combat.Target;
_actions.PerformAction(ent, null, action.Value, actionTarget, ev, _timing.CurTime, predicted: false);
// Nothing uses this anyway, what the hell it's pure shitcode?
// PsionicComponent? psionics = null;
// if (!Resolve(ent, ref psionics, logMissing: true)
// || !psionics.ActivePowers.Contains(NoosphericZap))
// return;

// var actionTarget = Comp<EntityTargetActionComponent>(action.Value);
// if (actionTarget.Cooldown is {} cooldown && cooldown.End > _timing.CurTime
// || !TryComp<NPCRangedCombatComponent>(ent, out var combat)
// || !_actions.ValidateEntityTarget(ent, combat.Target, (action.Value, actionTarget))
// || actionTarget.Event is not {} ev)
// return;

// ev.Target = combat.Target;
// _actions.PerformAction(ent, null, action.Value, actionTarget, ev, _timing.CurTime, predicted: false);
}
}
42 changes: 40 additions & 2 deletions Content.Server/Psionics/PsionicsSystem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Content.Shared.Abilities.Psionics;
using Content.Shared.StatusEffect;
using Content.Shared.Psionics;
using Content.Shared.Psionics.Glimmer;
using Content.Shared.Random;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Damage.Events;
using Content.Shared.CCVar;
Expand All @@ -19,9 +21,9 @@
using Content.Shared.Mobs;
using Content.Shared.Damage;
using Content.Shared.Interaction.Events;
using Timer = Robust.Shared.Timing.Timer;
using Content.Shared.Alert;
using Content.Shared.Rounding;
using Content.Shared.Psionics;

namespace Content.Server.Psionics;

Expand Down Expand Up @@ -62,6 +64,9 @@ public sealed class PsionicsSystem : EntitySystem
public override void Update(float frameTime)
{
base.Update(frameTime);
if (!_cfg.GetCVar(CCVars.PsionicRollsEnabled))
return;

foreach (var roller in _rollers)
RollPsionics(roller.uid, roller.component, true);
_rollers.Clear();
Expand All @@ -87,7 +92,22 @@ private void OnStartup(EntityUid uid, PsionicComponent component, MapInitEvent a
|| !component.CanReroll)
return;

Timer.Spawn(TimeSpan.FromSeconds(30), () => DeferRollers(uid));

}

/// <summary>
/// We wait a short time before starting up the rolled powers, so that other systems have a chance to modify the list first.
/// This is primarily for the sake of TraitSystem and AddJobSpecial.
/// </summary>
private void DeferRollers(EntityUid uid)
{
if (!Exists(uid)
|| !TryComp(uid, out PsionicComponent? component))
return;

CheckPowerCost(uid, component);
GenerateAvailablePowers(component);
_rollers.Enqueue((component, uid));
}

Expand All @@ -108,6 +128,24 @@ private void CheckPowerCost(EntityUid uid, PsionicComponent component)
component.NextPowerCost = 100 * MathF.Pow(2, powerCount);
}

/// <summary>
/// The power pool is itself a DataField, and things like Traits/Antags are allowed to modify or replace the pool.
/// </summary>
private void GenerateAvailablePowers(PsionicComponent component)
{
if (!_protoMan.TryIndex<WeightedRandomPrototype>(component.PowerPool.Id, out var pool))
return;

foreach (var id in pool.Weights)
{
if (!_protoMan.TryIndex<PsionicPowerPrototype>(id.Key, out var power)
|| component.ActivePowers.Contains(power))
continue;

component.AvailablePowers.Add(id.Key, id.Value);
}
}

private void OnMeleeHit(EntityUid uid, AntiPsionicWeaponComponent component, MeleeHitEvent args)
{
foreach (var entity in args.HitEntities)
Expand Down Expand Up @@ -200,7 +238,7 @@ private bool HandlePotentiaCalculations(EntityUid uid, PsionicComponent componen

component.Potentia -= component.NextPowerCost;
_psionicAbilitiesSystem.AddPsionics(uid);
component.NextPowerCost = 100 * MathF.Pow(2, component.PowerSlotsTaken);
component.NextPowerCost = component.BaselinePowerCost * MathF.Pow(2, component.PowerSlotsTaken);
return true;
}

Expand Down
40 changes: 37 additions & 3 deletions Content.Shared/Psionics/PsionicComponent.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Content.Shared.Alert;
using Content.Shared.DoAfter;
using Content.Shared.Psionics;
using Content.Shared.Random;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

Expand Down Expand Up @@ -45,6 +46,12 @@ public sealed partial class PsionicComponent : Component
[DataField]
public float Potentia;

/// <summary>
/// The base cost for new powers.
/// </summary>
[DataField]
public float BaselinePowerCost = 100;

/// <summary>
/// Each time a Psion rolls for a new power, they roll a number between 0 and 100, adding any relevant modifiers. This number is then added to Potentia,
/// meaning that it carries over between rolls. When a character has an amount of potentia equal to at least 100 * 2^(total powers), the potentia is then spent, and a power is generated.
Expand Down Expand Up @@ -81,6 +88,11 @@ public sealed partial class PsionicComponent : Component
[DataField]
public string MindbreakingFeedback = "mindbreaking-feedback";

/// <summary>
/// </summary>
[DataField]
public string HardMindbreakingFeedback = "hard-mindbreaking-feedback";

/// <summary>
/// How much should the odds of obtaining a Psionic Power be multiplied when rolling for one.
/// </summary>
Expand Down Expand Up @@ -139,6 +151,12 @@ private set
}
}

/// <summary>
/// Whether this entity is capable of randomly rolling for powers.
/// </summary>
[DataField]
public bool Roller = true;

/// <summary>
/// Ifrits, revenants, etc are explicitly magical beings that shouldn't get mindbroken
/// </summary>
Expand All @@ -153,10 +171,10 @@ private set
public HashSet<PsionicPowerPrototype> ActivePowers = new();

/// <summary>
/// The list of each Psionic Power by action with entityUid.
/// The list of each Psionic Power by prototype with entityUid.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
public Dictionary<EntProtoId, EntityUid?> Actions = new();
public Dictionary<string, EntityUid?> Actions = new();

/// <summary>
/// What sources of Amplification does this Psion have?
Expand Down Expand Up @@ -202,7 +220,7 @@ private set
/// unneccesary subs for unique psionic entities like e.g. Oracle.
/// </summary>
[DataField]
public List<string>? PsychognomicDescriptors = null;
public List<string> PsychognomicDescriptors = new();

/// Used for tracking what spell a Psion is actively casting
[DataField]
Expand All @@ -228,6 +246,22 @@ private set
[DataField]
public int FamiliarLimit = 1;

/// <summary>
/// The list of all potential Assay messages that can be obtained from this Psion.
/// </summary>
[DataField]
public List<string> AssayFeedback = new();

/// <summary>
/// The list of powers that this Psion is eligible to roll new abilities from.
/// This generates the initial ability pool, but can also be modified by other systems.
/// </summary>
[DataField]
public ProtoId<WeightedRandomPrototype> PowerPool = "RandomPsionicPowerPool";

[DataField]
public Dictionary<string, float> AvailablePowers = new();

[DataField]
public ProtoId<AlertPrototype> ManaAlert = "Mana";
}
Expand Down
90 changes: 29 additions & 61 deletions Content.Shared/Psionics/PsionicPowerPrototype.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using Content.Shared.Chat;
using Content.Shared.Abilities.Psionics;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;

namespace Content.Shared.Psionics;

Expand All @@ -19,77 +21,43 @@ public sealed partial class PsionicPowerPrototype : IPrototype
public string Name = default!;

/// <summary>
/// The description of a power in yml, used for player notifications.
/// </summary>
[DataField(required: true)]
public string Description = default!;

/// <summary>
/// The list of each Action that this power adds in the form of ActionId and ActionEntity
/// </summary>
[DataField]
public List<EntProtoId> Actions = new();

/// <summary>
/// The list of what Components this power adds.
/// </summary>
[DataField]
public ComponentRegistry Components = new();

/// <summary>
/// What message will be sent to the player as a Popup.
/// If left blank, it will default to the Const "generic-power-initialization-feedback"
/// </summary>
[DataField]
public string? InitializationPopup;

/// <summary>
/// What message will be sent to the chat window when the power is initialized. Leave it blank to send no message.
/// Initialization messages won't play for powers that are Innate, only powers obtained during the round.
/// These should generally also be written in the first person, and can be far lengthier than popups.
/// </summary>
[DataField]
public string? InitializationFeedback;

/// <summary>
/// What color will the initialization feedback display in the chat window with.
/// </summary>
[DataField]
public string InitializationFeedbackColor = "#8A00C2";

/// <summary>
/// What font size will the initialization message use in chat.
/// </summary>
[DataField]
public int InitializationFeedbackFontSize = 12;

/// <summary>
/// Which chat channel will the initialization message use.
/// What category of psionics does this power come from.
/// EG: Mentalics, Anomalists, Blood Cults, Heretics, etc.
/// </summary>
[DataField]
public ChatChannel InitializationFeedbackChannel = ChatChannel.Emotes;
public List<string> PowerCategories = new();

/// <summary>
/// What message will this power generate when scanned by a Metempsionic Focused Pulse.
/// These functions are called when a Psionic Power is added to a Psion.
/// </summary>
[DataField]
public string MetapsionicFeedback = "psionic-metapsionic-feedback-default";
[DataField(serverOnly: true)]
public PsionicPowerFunction[] InitializeFunctions { get; private set; } = Array.Empty<PsionicPowerFunction>();

/// <summary>
/// How much this power will increase or decrease a user's Amplification.
/// These functions are called when a Psionic Power is removed from a Psion,
/// as a rule of thumb these should do the exact opposite of most of a power's init functions.
/// </summary>
[DataField]
public float AmplificationModifier = 0;

/// <summary>
/// How much this power will increase or decrease a user's Dampening.
/// </summary>
[DataField]
public float DampeningModifier = 0;
[DataField(serverOnly: true)]
public PsionicPowerFunction[] RemovalFunctions { get; private set; } = Array.Empty<PsionicPowerFunction>();

/// <summary>
/// How many "Power Slots" this power occupies.
/// </summary>
[DataField]
public int PowerSlotCost = 1;
}
}

/// This serves as a hook for psionic powers to modify the psionic component.
[ImplicitDataDefinitionForInheritors]
public abstract partial class PsionicPowerFunction
{
public abstract void OnAddPsionic(
EntityUid mob,
IComponentFactory factory,
IEntityManager entityManager,
ISerializationManager serializationManager,
ISharedPlayerManager playerManager,
ILocalizationManager loc,
PsionicComponent psionicComponent,
PsionicPowerPrototype proto);
}
24 changes: 24 additions & 0 deletions Resources/Changelog/Changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8901,3 +8901,27 @@ Entries:
id: 6603
time: '2025-01-01T03:33:47.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1390
- author: VMSolidus
changes:
- type: Add
message: >-
Psionic Refactor V3 is here! No new powers are added in this update, but
the options for creating new powers has been SIGNIFICANTLY EXPANDED.
- type: Add
message: >-
Xenoglossy and Psychognomy now can only be rolled if you first have the
Telepathy power.
- type: Add
message: >-
Breath of Life can now only be rolled if you first have the Healing Word
power
- type: Add
message: Pyrokinesis and Summon Imp now require the Pyroknetic Flare power
- type: Add
message: >-
All new Psychognomy descriptors for many pre-existing powers. Have fun
being unintentionally screamed at telepathically by someone with the
POWER OVERWHELMING trait.
id: 6604
time: '2025-01-01T21:59:50.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1383
1 change: 1 addition & 0 deletions Resources/Locale/en-US/psionics/psionic-powers.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ summon-remilia-power-description = { action-description-summon-remilia }
# Psionic System Messages
mindbreaking-feedback = The light of life vanishes from {CAPITALIZE($entity)}'s eyes, leaving behind a husk pretending at sapience
hard-mindbreaking-feedback = Your character's personhood has been obliterated. If you wish to continue playing, consider respawning as a new character.
examine-mindbroken-message =
Eyes unblinking, staring deep into the horizon. {CAPITALIZE($entity)} is a sack of meat pretending it has a soul.
There is nothing behind its gaze, no evidence there can be found of the divine light of creation.
Expand Down
2 changes: 1 addition & 1 deletion Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@
noMana: shadowkin-tired
- type: InnatePsionicPowers
powersToAdd:
- ShadowkinPowers
- DarkSwapPower
- type: LanguageKnowledge
speaks:
- TauCetiBasic
Expand Down
Loading

0 comments on commit 0069ea0

Please sign in to comment.