Merge 6878154760 into fd5b16abb3
This commit is contained in:
commit
2925ec87c9
|
|
@ -1,8 +1,8 @@
|
|||
using Content.Shared._DV.Psionics.Events; // DeltaV - Psionics Refactor
|
||||
using Content.Shared.Body.Events;
|
||||
using Content.Shared.Damage.Events;
|
||||
using Content.Shared.Mobs.Events;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Events;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Rejuvenate;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._DV.Psionics.Components.PsionicPowers;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class AltruisticHemomancyPowerComponent : BasePsionicPowerComponent
|
||||
{
|
||||
public override EntProtoId ActionProtoId { get; set; } = "ActionAltruisticHemomancy";
|
||||
|
||||
public override string PowerName { get; set; } = "Altruistic Hemomancy";
|
||||
|
||||
public override int MinGlimmerChanged { get; set; } = 5;
|
||||
|
||||
public override int MaxGlimmerChanged { get; set; } = 20;
|
||||
|
||||
/// <summary>
|
||||
/// The normal DoAfterDuration of healing.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan DoAfterDuration = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>
|
||||
/// The modifier for the DoAfterDuration if the target is in critical state.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float CriticalDoAfterDurationModifier = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The modifier applied to the blood cost when healing a target in critical state.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float CriticalHealingCostModifier = 1.25f;
|
||||
|
||||
/// <summary>
|
||||
/// The standard blood cost of using the power. It's 1/10th of a human entire blood reserves.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float BloodCost = -30f;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum blood percentage someone has to have to use the power.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MinBloodPercentage = 0.4f;
|
||||
|
||||
/// <summary>
|
||||
/// The healing applied evenly to the target with each DoAfter.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2> Heal = new()
|
||||
{
|
||||
{ "Brute", -20 },
|
||||
{ "Burn", -20 },
|
||||
{ "Airloss", -20 },
|
||||
{ "Toxin", -20 },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The target's bleeding reduced with every healing tick.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float ReducedBleeding = -4f;
|
||||
|
||||
/// <summary>
|
||||
/// How many times a target can be healed with the one ability usage.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MaxHealingTicks = 3;
|
||||
|
||||
/// <summary>
|
||||
/// The current counter of completed healing.
|
||||
/// </summary>
|
||||
[ViewVariables, AutoNetworkedField]
|
||||
public int TickCounter = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The target's rot counter reduced with every healing tick.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan ReduceRot = TimeSpan.FromSeconds(30);
|
||||
|
||||
/// <summary>
|
||||
/// The Damage the psionic user receives everytime they heal targets.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public DamageSpecifier DamageOnHealing = new()
|
||||
{
|
||||
DamageDict = new()
|
||||
{
|
||||
{ "Bloodloss", 10 },
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The Damage the psionic user receives everytime they heal dead targets.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public DamageSpecifier DamageOnHealingRot = new()
|
||||
{
|
||||
DamageDict = new()
|
||||
{
|
||||
{ "Cellular", 30 },
|
||||
},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The modifier applied to the blood cost when healing a dead target.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float RotHealingCostModifier = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// The modifier for the DoAfterDuration if the target is dead.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float RotDoAfterDurationModifier = 2f;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared._DV.Psionics.Events.PowerActionEvents;
|
||||
|
||||
public sealed partial class AltruisticHemomancyPowerActionEvent : EntityTargetActionEvent;
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._DV.Psionics.Events.PowerDoAfterEvents;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class AltruisticHemomancyDoAfterEvent : SimpleDoAfterEvent;
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
using Content.Shared._DV.Psionics.Components.PsionicPowers;
|
||||
using Content.Shared._DV.Psionics.Events.PowerActionEvents;
|
||||
using Content.Shared._DV.Psionics.Events.PowerDoAfterEvents;
|
||||
using Content.Shared.Atmos.Rotting;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
|
||||
namespace Content.Shared._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
public sealed class AltruisticHemomancyPowerSystem : BasePsionicPowerSystem<AltruisticHemomancyPowerComponent, AltruisticHemomancyPowerActionEvent>
|
||||
{
|
||||
[Dependency] private readonly SharedBloodstreamSystem _bloodstream = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly SharedRottingSystem _rotting = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AltruisticHemomancyPowerComponent, AltruisticHemomancyDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
protected override void OnPowerInit(Entity<AltruisticHemomancyPowerComponent> power, ref MapInitEvent args)
|
||||
{
|
||||
base.OnPowerInit(power, ref args);
|
||||
|
||||
Popup.PopupEntity(Loc.GetString("psionic-power-altruistic-hemomancy-init"), power.Owner, power.Owner, PopupType.LargeCaution);
|
||||
}
|
||||
|
||||
protected override void OnPowerUsed(Entity<AltruisticHemomancyPowerComponent> psionic, ref AltruisticHemomancyPowerActionEvent args)
|
||||
{
|
||||
if (!TryComp<DamageableComponent>(args.Target, out var damageable)
|
||||
|| !Psionic.CanBeTargeted(args.Target, hasAggressor: args.Performer))
|
||||
return;
|
||||
|
||||
var damage = _damageable.GetPositiveDamage((args.Target, damageable));
|
||||
|
||||
if (!damage.AnyPositive())
|
||||
{
|
||||
Popup.PopupClient(Loc.GetString("psionic-power-altruistic-hemomancy-healthy"), args.Performer, args.Performer);
|
||||
return;
|
||||
}
|
||||
|
||||
var doAfterDuration = psionic.Comp.DoAfterDuration;
|
||||
if (_mobState.IsCritical(args.Target))
|
||||
doAfterDuration *= psionic.Comp.CriticalDoAfterDurationModifier;
|
||||
else if (_mobState.IsDead(args.Target))
|
||||
doAfterDuration *= psionic.Comp.RotDoAfterDurationModifier;
|
||||
|
||||
var doAfterArgs = new DoAfterArgs(EntityManager,
|
||||
args.Performer,
|
||||
doAfterDuration,
|
||||
new AltruisticHemomancyDoAfterEvent(),
|
||||
psionic,
|
||||
args.Target)
|
||||
{
|
||||
BreakOnMove = true,
|
||||
BreakOnWeightlessMove = false,
|
||||
};
|
||||
|
||||
if (!DoAfter.TryStartDoAfter(doAfterArgs, out var id))
|
||||
return;
|
||||
|
||||
psionic.Comp.SaveDoAfterId(id.Value);
|
||||
Dirty(psionic);
|
||||
|
||||
var messageSelf = Loc.GetString("psionic-power-altruistic-hemomancy-start-self", ("target", Identity.Entity(args.Target, EntityManager)));
|
||||
var messageOthers = Loc.GetString("psionic-power-altruistic-hemomancy-start-others",
|
||||
("user", Identity.Entity(args.Performer, EntityManager)),
|
||||
("target", Identity.Entity(args.Target, EntityManager)));
|
||||
|
||||
Popup.PopupPredicted(messageSelf, messageOthers, args.Performer, args.Performer, PopupType.MediumCaution);
|
||||
AfterPowerUsed(psionic, args.Performer);
|
||||
}
|
||||
|
||||
private void OnDoAfter(Entity<AltruisticHemomancyPowerComponent> psionic, ref AltruisticHemomancyDoAfterEvent args)
|
||||
{
|
||||
if (args.Target is not {} target
|
||||
|| args.Cancelled
|
||||
|| args.Handled
|
||||
|| !Psionic.CanBeTargeted(target, hasAggressor: args.User)
|
||||
|| !TryComp<DamageableComponent>(target, out var damageable)
|
||||
|| !_damageable.GetPositiveDamage((target, damageable)).AnyPositive())
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
// TODO: Fix this when Upstream fixes the prediction issue with this.
|
||||
// I cannot build in a check for sufficient bloodlevel as of now. GetBloodLevel() causes missprediction issues.
|
||||
//if (_bloodstream.GetBloodLevel(args.User) < psionic.Comp.MinBloodPercentage)
|
||||
// {
|
||||
// Popup.PopupClient(Loc.GetString("psionic-power-altruistic-hemomancy-insufficient-blood"), args.User, args.User, PopupType.SmallCaution);
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (_mobState.IsDead(target))
|
||||
HealRot(psionic, args.User, target);
|
||||
else
|
||||
HealDamage(psionic, args.User, target, damageable);
|
||||
|
||||
psionic.Comp.TickCounter++;
|
||||
|
||||
if (psionic.Comp.TickCounter < psionic.Comp.MaxHealingTicks && _damageable.GetPositiveDamage((target, damageable)).AnyPositive())
|
||||
{
|
||||
args.Args.Delay = psionic.Comp.DoAfterDuration;
|
||||
|
||||
if (_mobState.IsCritical(target))
|
||||
args.Args.Delay *= psionic.Comp.CriticalDoAfterDurationModifier;
|
||||
else if (_mobState.IsDead(target))
|
||||
args.Args.Delay *= psionic.Comp.RotDoAfterDurationModifier;
|
||||
|
||||
args.Repeat = true;
|
||||
Dirty(psionic);
|
||||
return;
|
||||
}
|
||||
|
||||
psionic.Comp.TickCounter = 0;
|
||||
psionic.Comp.RemoveSavedDoAfterId();
|
||||
Dirty(psionic);
|
||||
|
||||
var messageSelf = Loc.GetString("psionic-power-altruistic-hemomancy-end-self");
|
||||
var messageOthers = Loc.GetString("psionic-power-altruistic-hemomancy-end-others",
|
||||
("user", Identity.Entity(args.User, EntityManager)));
|
||||
|
||||
Popup.PopupPredicted(messageSelf, messageOthers, args.User, args.User, PopupType.SmallCaution);
|
||||
}
|
||||
|
||||
private void HealDamage(Entity<AltruisticHemomancyPowerComponent> psionic, EntityUid user, EntityUid target, DamageableComponent damageable)
|
||||
{
|
||||
var bloodPercentageCost = psionic.Comp.BloodCost;
|
||||
if (_mobState.IsCritical(target))
|
||||
bloodPercentageCost *= psionic.Comp.CriticalHealingCostModifier;
|
||||
|
||||
if (!_bloodstream.TryModifyBloodLevel(user, bloodPercentageCost))
|
||||
return;
|
||||
// Damage the user for every heal.
|
||||
_damageable.TryChangeDamage(user, psionic.Comp.DamageOnHealing, interruptsDoAfters: false, origin: psionic);
|
||||
_bloodstream.TryModifyBleedAmount(target, psionic.Comp.ReducedBleeding);
|
||||
|
||||
foreach (var groupHeal in psionic.Comp.Heal)
|
||||
{
|
||||
_damageable.HealEvenly((target, damageable), groupHeal.Value, groupHeal.Key, user);
|
||||
}
|
||||
}
|
||||
|
||||
private void HealRot(Entity<AltruisticHemomancyPowerComponent> psionic, EntityUid user, EntityUid target)
|
||||
{
|
||||
if (!_bloodstream.TryModifyBloodLevel(user, psionic.Comp.BloodCost * psionic.Comp.RotHealingCostModifier))
|
||||
return;
|
||||
|
||||
Popup.PopupClient(Loc.GetString("psionic-power-altruistic-hemomancy-rot-damage"), user, user, PopupType.SmallCaution);
|
||||
|
||||
_rotting.ReduceAccumulator(target, psionic.Comp.ReduceRot);
|
||||
_damageable.TryChangeDamage(user, psionic.Comp.DamageOnHealingRot, true, false, user);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
using Content.Shared._DV.Psionics.Components;
|
||||
using Content.Shared.EntityTable;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._DV.Roles;
|
||||
|
||||
public sealed partial class ModifyPsionicPowerTableSpecial : JobSpecial
|
||||
{
|
||||
/// <summary>
|
||||
/// The Prototype ID of the table containing the available psionic powers to roll.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<EntityTablePrototype> PsionicPowerTableId;
|
||||
|
||||
public override void AfterEquip(EntityUid mob)
|
||||
{
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
if (!entityManager.TryGetComponent(mob, out PotentialPsionicComponent? psionic))
|
||||
return;
|
||||
|
||||
psionic.PsionicPowerTableId = PsionicPowerTableId;
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,16 @@ psionic-equipped-shielded-in-doafter = The insulative gear broke your concentrat
|
|||
psionic-dispelled = Someone dispelled your psionic concentration!
|
||||
|
||||
## Specific Psionic messages
|
||||
# Altruistic Hemomancy
|
||||
psionic-power-altruistic-hemomancy-init = Your blood.. feels.. ALIVE!
|
||||
psionic-power-altruistic-hemomancy-healthy = Your blood responds.. with boredom.
|
||||
psionic-power-altruistic-hemomancy-insufficient-blood = Your blood grows thin.. you lose your focus..
|
||||
psionic-power-altruistic-hemomancy-rot-damage = Your blood twists and turns in agony as it vanquishes the rot..
|
||||
psionic-power-altruistic-hemomancy-start-self = Your blood beckons to your command, nurturing {THE($target)}'s body!
|
||||
psionic-power-altruistic-hemomancy-start-others = {CAPITALIZE(THE($user))}'s hands tear open, commanding blood into {THE($target)}'s wounds!
|
||||
psionic-power-altruistic-hemomancy-end-self = Your blood turns numb and sluggish, returning to your embrace.
|
||||
psionic-power-altruistic-hemomancy-end-others = {CAPITALIZE(THE($user))}'s blood retracts, closing their torn hands!
|
||||
|
||||
# Metapsionic Pulse
|
||||
psionic-power-metapsionic-success = You detect psychic presence there.
|
||||
psionic-power-metapsionic-failure = You don't detect any psychic presence there.
|
||||
|
|
|
|||
|
|
@ -17,8 +17,11 @@
|
|||
- type: BibleUser #Lets them heal with bibles
|
||||
- type: Condemned
|
||||
soulOwnedNotDevil: true # This just means they won't suffer the effects of being condemned, while still not being able to sell their soul. - Goobstation
|
||||
- !type:ModifyPsionicChanceSpecial # DeltaV - Chaplains are much more likely to be psionic.
|
||||
# Begin DeltaV Additions
|
||||
- !type:ModifyPsionicChanceSpecial # Chaplains are much more likely to be psionic.
|
||||
jobBonusChance: 0.15 # Additional 15% chance.
|
||||
- !type:ModifyPsionicPowerTableSpecial # Chaplains get a different power table.
|
||||
psionicPowerTableId: ChaplainPowerTable
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainGear
|
||||
|
|
|
|||
|
|
@ -213,3 +213,19 @@
|
|||
useDelay: 60
|
||||
- type: InstantAction
|
||||
event: !type:PsychokineticScreamPowerActionEvent
|
||||
|
||||
- type: entity
|
||||
parent: BasePsionicAction
|
||||
id: ActionAltruisticHemomancy
|
||||
name: Coagulate
|
||||
description: Command your blood to heal another alive being, exchanging your own crimson essence. Command it unto the dead to revert their rot, but it'll cost ya..
|
||||
components:
|
||||
- type: Action
|
||||
icon:
|
||||
sprite: _DV/Interface/Actions/actions_psionics_small.rsi
|
||||
state: altruistic_hemomancy
|
||||
useDelay: 45
|
||||
- type: TargetAction
|
||||
range: 1
|
||||
- type: EntityTargetAction
|
||||
event: !type:AltruisticHemomancyPowerActionEvent
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Table
|
||||
# Tables
|
||||
- type: entityTable
|
||||
id: PsionicPowerTable
|
||||
table: !type:GroupSelector
|
||||
|
|
@ -13,10 +13,41 @@
|
|||
weight: 1
|
||||
- id: TelegnosisPowerEntity
|
||||
weight: 1
|
||||
- id: MassSleepPowerEntity
|
||||
weight: 0.3
|
||||
- id: MetapsionicPulsePowerEntity
|
||||
weight: 0.5
|
||||
- id: AltruisticHemomancyPowerEntity
|
||||
weight: 0.3
|
||||
- id: MassSleepPowerEntity
|
||||
weight: 0.3
|
||||
- id: NoosphericZapPowerEntity
|
||||
weight: 0.3
|
||||
- id: MindSwapPowerEntity
|
||||
weight: 0.15
|
||||
#- id: PsionicEruptionPowerEntity
|
||||
# weight: 0.1
|
||||
|
||||
- type: entityTable
|
||||
id: ChaplainPowerTable # Sadly, we need to copy the entire thing just to change one value.
|
||||
table: !type:GroupSelector
|
||||
children:
|
||||
- id: AltruisticHemomancyPowerEntity
|
||||
weight: 3 # Ten times the chance to gain this healing power as a chaplain.
|
||||
- id: DispelPowerEntity
|
||||
weight: 1
|
||||
- id: PrecognitionPowerEntity
|
||||
weight: 1
|
||||
- id: PsionicRegenerationPowerEntity
|
||||
weight: 1
|
||||
- id: PsychokineticScreamPowerEntity
|
||||
weight: 1
|
||||
- id: TelegnosisPowerEntity
|
||||
weight: 1
|
||||
- id: MetapsionicPulsePowerEntity
|
||||
weight: 0.5
|
||||
- id: FracturedFormPowerEntity
|
||||
weight: 0.3
|
||||
- id: MassSleepPowerEntity
|
||||
weight: 0.3
|
||||
- id: NoosphericZapPowerEntity
|
||||
weight: 0.3
|
||||
- id: MindSwapPowerEntity
|
||||
|
|
@ -84,3 +115,9 @@
|
|||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: TelegnosisPower
|
||||
|
||||
- type: entity
|
||||
id: AltruisticHemomancyPowerEntity
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: AltruisticHemomancyPower
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 664 B |
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-4.0",
|
||||
"copyright": "Fractured Form sprited by TehFlaminTaco on Github for DeltaV.",
|
||||
"copyright": "Fractured Form sprited by TehFlaminTaco on Github for DeltaV, Altruistic Hemomancy sprited by @Stixcking on Github.",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
|
|
@ -9,6 +9,9 @@
|
|||
"states": [
|
||||
{
|
||||
"name": "fractured_form"
|
||||
},
|
||||
{
|
||||
"name": "altruistic_hemomancy"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue