More blood (#514)

* - add: Some construct stuff.

* - add: Some rune stuff.

* - add: Unvisit.

* - add: Better blood rites.
This commit is contained in:
Aviu00
2024-07-30 00:12:49 +00:00
committed by GitHub
parent 58b337a893
commit 74e1f3c618
29 changed files with 494 additions and 255 deletions

View File

@@ -0,0 +1,7 @@
namespace Content.Server._White.Cult.Items.Components;
public abstract partial class BaseMagicHandComponent : Component
{
[DataField]
public string Speech;
}

View File

@@ -0,0 +1,25 @@
using Content.Shared._White.Cult;
using Robust.Shared.Audio;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Server._White.Cult.Items.Components;
[RegisterComponent]
public sealed partial class CultRitesHandComponent : BaseMagicHandComponent
{
[DataField]
public SoundSpecifier HealSound = new SoundPathSpecifier("/Audio/White/Cult/rites.ogg");
[DataField]
public SoundSpecifier SuckSound = new SoundPathSpecifier("/Audio/White/Cult/enter_blood.ogg");
[DataField]
public float HealModifier = 0.5f;
[DataField(customTypeSerializer: typeof(PrototypeIdListSerializer<CultistFactoryProductionPrototype>))]
public List<string> BloodRites = new ()
{
"FactoryCultBloodSpear",
"FactoryCultBloodBarrage"
};
}

View File

@@ -1,7 +1,7 @@
namespace Content.Server._White.Cult.Items.Components; namespace Content.Server._White.Cult.Items.Components;
[RegisterComponent] [RegisterComponent]
public sealed partial class CultStunHandComponent : Component public sealed partial class CultStunHandComponent : BaseMagicHandComponent
{ {
[DataField] [DataField]
public TimeSpan Duration = TimeSpan.FromSeconds(16); public TimeSpan Duration = TimeSpan.FromSeconds(16);
@@ -14,7 +14,4 @@ public sealed partial class CultStunHandComponent : Component
[DataField] [DataField]
public TimeSpan HaloMuteDuration = TimeSpan.FromSeconds(1); public TimeSpan HaloMuteDuration = TimeSpan.FromSeconds(1);
[DataField]
public string Speech = "Fuu ma'jin!";
} }

View File

@@ -1,56 +0,0 @@
using Content.Server._White.Cult.Items.Components;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chat.Systems;
using Content.Server.Popups;
using Content.Server.Stunnable;
using Content.Shared._White.Chaplain;
using Content.Shared.Popups;
using Content.Shared.StatusEffect;
using Content.Shared.Weapons.Melee.Events;
namespace Content.Server._White.Cult.Items.Systems;
public sealed class CultStunHandSystem : EntitySystem
{
[Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly HolyWeaponSystem _holyWeapon = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CultStunHandComponent, MeleeHitEvent>(OnHit);
}
private void OnHit(Entity<CultStunHandComponent> ent, ref MeleeHitEvent args)
{
if (args.HitEntities.Count == 0)
return;
var target = args.HitEntities[0];
var (uid, comp) = ent;
QueueDel(uid);
Spawn("CultStunFlashEffect", Transform(target).Coordinates);
_chat.TrySendInGameICMessage(args.User, comp.Speech, InGameICChatType.Whisper, false);
if (TryComp(args.User, out BloodstreamComponent? bloodstream))
_bloodstream.TryModifyBloodLevel(args.User, -10, bloodstream, createPuddle: false);
if (_holyWeapon.IsHoldingHolyWeapon(target))
{
_popupSystem.PopupEntity(Loc.GetString("cult-magic-holy"), args.User, args.User, PopupType.MediumCaution);
return;
}
var halo = HasComp<PentagramComponent>(args.User);
_statusEffects.TryAddStatusEffect(target, "Muted", halo ? comp.HaloMuteDuration : comp.MuteDuration, true,
"Muted");
_stun.TryParalyze(target, halo ? comp.HaloDuration : comp.Duration, true);
}
}

View File

@@ -0,0 +1,257 @@
using System.Linq;
using Content.Server._White.Cult.Items.Components;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chat.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Hands.Systems;
using Content.Server.Popups;
using Content.Server.Stunnable;
using Content.Shared._White.Chaplain;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.UI;
using Content.Shared.Chemistry.Components;
using Content.Shared.Damage;
using Content.Shared.Examine;
using Content.Shared.FixedPoint;
using Content.Shared.Fluids.Components;
using Content.Shared.Interaction;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.StatusEffect;
using Content.Shared.UserInterface;
using Content.Shared.Weapons.Melee.Events;
using Robust.Server.Audio;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Server._White.Cult.Items.Systems;
public sealed class MagicHandSystem : EntitySystem
{
[Dependency] private readonly StunSystem _stun = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly HolyWeaponSystem _holyWeapon = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly SolutionContainerSystem _solutionSystem = default!;
[Dependency] private readonly HandsSystem _handsSystem = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CultStunHandComponent, MeleeHitEvent>(OnStunHit);
SubscribeLocalEvent<CultRitesHandComponent, MeleeHitEvent>(OnRitesHit);
SubscribeLocalEvent<CultRitesHandComponent, AfterInteractEvent>(OnInteract);
SubscribeLocalEvent<CultRitesHandComponent, CultistFactoryItemSelectedMessage>(OnBloodRitesSelected);
SubscribeLocalEvent<CultRitesHandComponent, ActivatableUIOpenAttemptEvent>(OnRitesSelectAttempt);
SubscribeLocalEvent<CultRitesHandComponent, BeforeActivatableUIOpenEvent>(BeforeRitesSelect);
SubscribeLocalEvent<CultRitesHandComponent, ExaminedEvent>(OnExamine);
}
private void OnExamine(Entity<CultRitesHandComponent> ent, ref ExaminedEvent args)
{
var user = args.Examiner;
if (user != Transform(ent).ParentUid || !TryComp(user, out CultistComponent? cultist))
return;
args.PushMarkup(Loc.GetString("cult-rites-examine", ("blood", cultist.RitesBloodAmount)));
}
private void BeforeRitesSelect(Entity<CultRitesHandComponent> ent, ref BeforeActivatableUIOpenEvent args)
{
if (_ui.TryGetUi(ent, BloodRitesUi.Key, out var bui))
_ui.SetUiState(bui, new CultistFactoryBUIState(ent.Comp.BloodRites));
}
private void OnRitesSelectAttempt(Entity<CultRitesHandComponent> ent, ref ActivatableUIOpenAttemptEvent args)
{
if (!HasComp<CultistComponent>(args.User))
args.Cancel();
}
private void OnBloodRitesSelected(Entity<CultRitesHandComponent> ent, ref CultistFactoryItemSelectedMessage args)
{
var attachedEntity = args.Session.AttachedEntity;
if (!TryComp(attachedEntity, out CultistComponent? cultist))
return;
if (!_prototypeManager.TryIndex<CultistFactoryProductionPrototype>(args.Item, out var prototype))
return;
var uid = attachedEntity.Value;
if (cultist.RitesBloodAmount < prototype.BloodCost)
{
var message = Loc.GetString("cult-rites-no-blood", ("required", prototype.BloodCost),
("blood", cultist.RitesBloodAmount));
Popup(message, uid);
return;
}
Speak(uid, ent.Comp);
Del(ent);
var success = false;
foreach (var entity in prototype.Item.Select(item => Spawn(item, Transform(uid).Coordinates)))
{
if (_handsSystem.TryPickupAnyHand(uid, entity))
{
success = true;
continue;
}
Popup(Loc.GetString("cult-rites-no-hand"), uid);
QueueDel(entity);
break;
}
if (!success)
return;
cultist.RitesBloodAmount -= prototype.BloodCost;
}
private void OnInteract(Entity<CultRitesHandComponent> ent, ref AfterInteractEvent args)
{
if (!args.CanReach || args.Target is not { } target)
return;
var puddleQuery = GetEntityQuery<PuddleComponent>();
if (!puddleQuery.HasComp(target))
return;
if (!TryComp(args.User, out BloodstreamComponent? bloodstreamComponent) ||
!TryComp(args.User, out CultistComponent? cultist))
return;
var xform = Transform(target);
var entitiesInRange = _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(xform), 1.5f);
var totalBloodAmount = FixedPoint2.Zero;
foreach (var solutionEntity in entitiesInRange.ToList())
{
if (!puddleQuery.TryComp(solutionEntity, out var puddleComponent))
continue;
if (!_solutionSystem.TryGetSolution(solutionEntity, puddleComponent.SolutionName, out var solution))
continue;
var hasBlood = false;
foreach (var solutionContent in solution.Value.Comp.Solution.Contents.ToList()
.Where(solutionContent => solutionContent.Reagent.Prototype == "Blood"))
{
hasBlood = true;
totalBloodAmount += solutionContent.Quantity;
_bloodstream.TryModifyBloodLevel(args.User, solutionContent.Quantity / 6f, bloodstreamComponent);
_solutionSystem.RemoveReagent((Entity<SolutionComponent>) solution, "Blood", FixedPoint2.MaxValue);
}
if (hasBlood)
Spawn("CultTileEffect", Transform(solutionEntity).Coordinates);
}
if (totalBloodAmount <= FixedPoint2.Zero)
return;
cultist.RitesBloodAmount += totalBloodAmount;
_audio.PlayPvs(ent.Comp.SuckSound, args.User);
_popupSystem.PopupEntity(Loc.GetString("cult-rites-message", ("blood", cultist.RitesBloodAmount)),
args.User, args.User);
args.Handled = true;
}
private void OnRitesHit(Entity<CultRitesHandComponent> ent, ref MeleeHitEvent args)
{
if (args.HitEntities.Count == 0)
return;
var target = args.HitEntities[0];
var (uid, comp) = ent;
QueueDel(uid);
if (!TryComp(args.User, out CultistComponent? cultist) || !TryComp(target, out DamageableComponent? damageable))
return;
if (_mobState.IsDead(target))
{
Popup(Loc.GetString("cult-rites-dead"), args.User);
return;
}
if (cultist.RitesBloodAmount <= FixedPoint2.Zero)
{
Popup(Loc.GetString("cult-rites-heal-no-blood"), args.User);
return;
}
var damage = damageable.Damage;
var totalDamage = damage.GetTotal();
if (totalDamage <= FixedPoint2.Zero)
{
Popup(Loc.GetString("cult-rites-already-healed"), args.User);
return;
}
var coef = FixedPoint2.Min(cultist.RitesBloodAmount * comp.HealModifier, totalDamage) / totalDamage;
cultist.RitesBloodAmount =
FixedPoint2.Max(FixedPoint2.Zero, cultist.RitesBloodAmount - totalDamage / comp.HealModifier);
_damageable.TryChangeDamage(target, -damage * coef, true, false, damageable, args.User);
Popup(Loc.GetString("cult-rites-after-heal", ("blood", cultist.RitesBloodAmount)), args.User);
_audio.PlayPvs(comp.HealSound, target);
Speak(args.User, comp);
}
private void OnStunHit(Entity<CultStunHandComponent> ent, ref MeleeHitEvent args)
{
if (args.HitEntities.Count == 0)
return;
var target = args.HitEntities[0];
var (uid, comp) = ent;
QueueDel(uid);
Spawn("CultStunFlashEffect", Transform(target).Coordinates);
Speak(args.User, comp);
if (TryComp(args.User, out BloodstreamComponent? bloodstream))
_bloodstream.TryModifyBloodLevel(args.User, -10, bloodstream, createPuddle: false);
if (_holyWeapon.IsHoldingHolyWeapon(target))
{
Popup(Loc.GetString("cult-magic-holy"), args.User, PopupType.MediumCaution);
return;
}
var halo = HasComp<PentagramComponent>(args.User);
_statusEffects.TryAddStatusEffect(target, "Muted", halo ? comp.HaloMuteDuration : comp.MuteDuration, true,
"Muted");
_stun.TryParalyze(target, halo ? comp.HaloDuration : comp.Duration, true);
}
private void Popup(string msg, EntityUid user, PopupType type = PopupType.Small)
{
_popupSystem.PopupEntity(msg, user, user, type);
}
private void Speak(EntityUid user, BaseMagicHandComponent comp)
{
_chat.TrySendInGameICMessage(user, comp.Speech, InGameICChatType.Whisper, false);
}
}

View File

@@ -106,15 +106,7 @@ public partial class CultSystem
private void OnStun(EntityUid uid, CultistComponent component, CultStunActionEvent args) private void OnStun(EntityUid uid, CultistComponent component, CultStunActionEvent args)
{ {
var entity = Spawn("StunHand", Transform(uid).Coordinates); GrantItem(uid, "StunHand", args);
if (!_handsSystem.TryPickupAnyHand(uid, entity))
{
_popupSystem.PopupEntity(Loc.GetString("cult-magic-no-empty-hand"), uid, uid);
QueueDel(entity);
_actionsSystem.SetCooldown(args.Action, TimeSpan.FromSeconds(1));
return;
}
args.Handled = true;
} }
private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args) private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args)
@@ -150,61 +142,7 @@ public partial class CultSystem
private void OnBloodRites(EntityUid uid, CultistComponent component, CultBloodRitesInstantActionEvent args) private void OnBloodRites(EntityUid uid, CultistComponent component, CultBloodRitesInstantActionEvent args)
{ {
if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstreamComponent)) GrantItem(uid, "RitesHand", args);
return;
var bruteDamageGroup = _prototypeManager.Index<DamageGroupPrototype>("Brute");
var burnDamageGroup = _prototypeManager.Index<DamageGroupPrototype>("Burn");
var xform = Transform(uid);
var entitiesInRange = _lookup.GetEntitiesInRange(_transform.GetMapCoordinates(xform), 1.5f);
FixedPoint2 totalBloodAmount = 0f;
var breakLoop = false;
foreach (var solutionEntity in entitiesInRange.ToList())
{
if (breakLoop)
break;
if (!TryComp<PuddleComponent>(solutionEntity, out var puddleComponent))
continue;
if (!_solutionSystem.TryGetSolution(solutionEntity, puddleComponent.SolutionName, out var solution))
continue;
foreach (var solutionContent in solution.Value.Comp.Solution.Contents.ToList())
{
if (solutionContent.Reagent.Prototype != "Blood")
continue;
totalBloodAmount += solutionContent.Quantity;
_bloodstreamSystem.TryModifyBloodLevel(uid, solutionContent.Quantity / 6f, bloodstreamComponent);
_solutionSystem.RemoveReagent((Entity<SolutionComponent>) solution, "Blood", FixedPoint2.MaxValue);
/*if (GetMissingBloodValue(bloodstreamComponent) == 0)
{
breakLoop = true;
}*/
}
}
if (totalBloodAmount == 0f)
return;
component.RitesBloodAmount += totalBloodAmount;
_audio.PlayPvs("/Audio/White/Cult/enter_blood.ogg", uid, AudioParams.Default);
_damageableSystem.TryChangeDamage(uid, new DamageSpecifier(bruteDamageGroup, -20));
_damageableSystem.TryChangeDamage(uid, new DamageSpecifier(burnDamageGroup, -20));
_popupSystem.PopupEntity(Loc.GetString("verb-blood-rites-message", ("blood", component.RitesBloodAmount)), uid,
uid);
Speak(args);
args.Handled = true;
} }
private static FixedPoint2 GetMissingBloodValue(BloodstreamComponent bloodstreamComponent) private static FixedPoint2 GetMissingBloodValue(BloodstreamComponent bloodstreamComponent)
@@ -497,4 +435,17 @@ public partial class CultSystem
{ {
_spells.Speak(args, InGameICChatType.Whisper); _spells.Speak(args, InGameICChatType.Whisper);
} }
public void GrantItem(EntityUid uid, string proto, InstantActionEvent args)
{
var entity = Spawn(proto, Transform(uid).Coordinates);
if (!_handsSystem.TryPickupAnyHand(uid, entity))
{
_popupSystem.PopupEntity(Loc.GetString("cult-magic-no-empty-hand"), uid, uid);
QueueDel(entity);
_actionsSystem.SetCooldown(args.Action, TimeSpan.FromSeconds(1));
return;
}
args.Handled = true;
}
} }

View File

@@ -1,4 +1,5 @@
using Content.Server.GameTicking; using System.Linq;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules.Components; using Content.Server.GameTicking.Rules.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server._White.Cult.GameRule; using Content.Server._White.Cult.GameRule;
@@ -46,9 +47,10 @@ public partial class CultSystem
private void OnMapInit(EntityUid uid, ConstructComponent component, MapInitEvent args) private void OnMapInit(EntityUid uid, ConstructComponent component, MapInitEvent args)
{ {
foreach (var id in component.Actions) foreach (var action in component.Actions.Select(id => _actionsSystem.AddAction(uid, id)))
{ {
component.ActionEntities.Add(_actionsSystem.AddAction(uid, id)); _actionsSystem.StartUseDelay(action);
component.ActionEntities.Add(action);
} }
} }

View File

@@ -99,7 +99,7 @@ public sealed partial class CultSystem : EntitySystem
SubscribeLocalEvent<CultEmpowerComponent, CultEmpowerSelectedBuiMessage>(OnEmpowerSelected); SubscribeLocalEvent<CultEmpowerComponent, CultEmpowerSelectedBuiMessage>(OnEmpowerSelected);
SubscribeLocalEvent<CultEmpowerComponent, UseInHandEvent>(OnUseInHand); SubscribeLocalEvent<CultEmpowerComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<CultEmpowerComponent, ActivateInWorldEvent>(OnActiveInWorld); SubscribeLocalEvent<CultEmpowerComponent, CultRuneInvokeEvent>(OnActiveInWorld);
// UI // UI
SubscribeLocalEvent<RuneDrawerProviderComponent, ActivatableUIOpenAttemptEvent>(OnRuneDrawAttempt); SubscribeLocalEvent<RuneDrawerProviderComponent, ActivatableUIOpenAttemptEvent>(OnRuneDrawAttempt);
@@ -396,19 +396,19 @@ public sealed partial class CultSystem : EntitySystem
if (ev.Result) if (ev.Result)
{ {
OnAfterInvoke(uid, cultists); OnAfterInvoke(uid, cultists, ev.InvokePhraseOverride);
} }
} }
private void OnAfterInvoke(EntityUid rune, HashSet<EntityUid> cultists) private void OnAfterInvoke(EntityUid rune, HashSet<EntityUid> cultists, string? invokePhraseOverride = null)
{ {
if (!_entityManager.TryGetComponent<CultRuneBaseComponent>(rune, out var component)) if (!_entityManager.TryGetComponent<CultRuneBaseComponent>(rune, out var component))
return; return;
foreach (var cultist in cultists) foreach (var cultist in cultists)
{ {
_chat.TrySendInGameICMessage(cultist, component.InvokePhrase, component.InvokeChatType, false, false, null, _chat.TrySendInGameICMessage(cultist, invokePhraseOverride ?? component.InvokePhrase,
null, null, false); component.InvokeChatType, false, false, null, null, null, false);
} }
} }
@@ -455,16 +455,19 @@ public sealed partial class CultSystem : EntitySystem
!HasComp<MindShieldComponent>(victim.Value)) !HasComp<MindShieldComponent>(victim.Value))
{ {
result = Convert(uid, victim.Value, args.User, args.Cultists); result = Convert(uid, victim.Value, args.User, args.Cultists);
args.InvokePhraseOverride = "Mah'weyh pleggh at e'ntrath!";
} }
else else
{ {
result = Sacrifice(uid, victim.Value, args.User, args.Cultists); result = Sacrifice(uid, victim.Value, args.User, args.Cultists);
args.InvokePhraseOverride = "Barhah hra zar'garis!";
} }
} }
else else
{ {
// Жертва мертва, выполняется альтернативное действие // Жертва мертва, выполняется альтернативное действие
result = SacrificeNonObjectiveDead(uid, victim.Value, args.User, args.Cultists); result = SacrificeNonObjectiveDead(uid, victim.Value, args.User, args.Cultists);
args.InvokePhraseOverride = "Barhah hra zar'garis!";
} }
args.Result = result; args.Result = result;
@@ -1217,12 +1220,13 @@ public sealed partial class CultSystem : EntitySystem
* Empower rune start ---- * Empower rune start ----
*/ */
private void OnActiveInWorld(EntityUid uid, CultEmpowerComponent component, ActivateInWorldEvent args) private void OnActiveInWorld(EntityUid uid, CultEmpowerComponent component, CultRuneInvokeEvent args)
{ {
if (!component.IsRune || !TryComp<CultistComponent>(args.User, out _) || if (!component.IsRune || !TryComp<CultistComponent>(args.User, out _) ||
!TryComp<ActorComponent>(args.User, out var actor)) !TryComp<ActorComponent>(args.User, out var actor))
return; return;
args.Result = !_ui.SessionHasOpenUi(uid, CultEmpowerUiKey.Key, actor.PlayerSession) &&
_ui.TryOpen(uid, CultEmpowerUiKey.Key, actor.PlayerSession); _ui.TryOpen(uid, CultEmpowerUiKey.Key, actor.PlayerSession);
} }
@@ -1377,6 +1381,7 @@ public sealed partial class CultSystem : EntitySystem
var shard = _entityManager.SpawnEntity("SoulShard", transform.Value); var shard = _entityManager.SpawnEntity("SoulShard", transform.Value);
_mindSystem.TransferTo(mindComponent.Mind.Value, shard); _mindSystem.TransferTo(mindComponent.Mind.Value, shard);
_mindSystem.UnVisit(mindComponent.Mind.Value);
_bodySystem.GibBody(target); _bodySystem.GibBody(target);

View File

@@ -2,7 +2,6 @@ using System.Linq;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Shared._White.Cult; using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components; using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.UI;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Player; using Robust.Shared.Player;
@@ -17,7 +16,6 @@ public sealed partial class CultSystem
SubscribeLocalEvent<CultistComponent, CultEmpowerSelectedBuiMessage>(OnCultistEmpowerSelected); SubscribeLocalEvent<CultistComponent, CultEmpowerSelectedBuiMessage>(OnCultistEmpowerSelected);
SubscribeLocalEvent<CultistComponent, CultEmpowerRemoveBuiMessage>(OnCultistEmpowerRemove); SubscribeLocalEvent<CultistComponent, CultEmpowerRemoveBuiMessage>(OnCultistEmpowerRemove);
SubscribeLocalEvent<CultistComponent, SpellCreatedEvent>(OnSpellCreated); SubscribeLocalEvent<CultistComponent, SpellCreatedEvent>(OnSpellCreated);
SubscribeLocalEvent<CultistComponent, CultistFactoryItemSelectedMessage>(OnBloodRitesSelected);
} }
private void OnCultistEmpowerRemove(Entity<CultistComponent> ent, ref CultEmpowerRemoveBuiMessage args) private void OnCultistEmpowerRemove(Entity<CultistComponent> ent, ref CultEmpowerRemoveBuiMessage args)
@@ -100,24 +98,8 @@ public sealed partial class CultSystem
} }
}; };
var bloodRitesVerb = new Verb
{
Text = Loc.GetString("verb-blood-rites-text"),
Message = Loc.GetString("verb-blood-rites-message", ("blood", ent.Comp.RitesBloodAmount)),
Category = VerbCategory.Cult,
Act = () =>
{
if (!_ui.TryGetUi(ent, BloodRitesUi.Key, out var bui))
return;
_ui.SetUiState(bui, new CultistFactoryBUIState(ent.Comp.BloodRites));
_ui.OpenUi(bui, actor.PlayerSession);
}
};
args.Verbs.Add(createSpellVerb); args.Verbs.Add(createSpellVerb);
args.Verbs.Add(removeSpellVerb); args.Verbs.Add(removeSpellVerb);
args.Verbs.Add(bloodRitesVerb);
} }
private void RemoveSpell(Entity<CultistComponent> ent, ICommonSession session) private void RemoveSpell(Entity<CultistComponent> ent, ICommonSession session)
@@ -130,36 +112,4 @@ public sealed partial class CultSystem
_ui.TryOpen(ent, CultEmpowerRemoveUiKey.Key, session); _ui.TryOpen(ent, CultEmpowerRemoveUiKey.Key, session);
} }
private void OnBloodRitesSelected(Entity<CultistComponent> ent, ref CultistFactoryItemSelectedMessage args)
{
if (!_prototypeManager.TryIndex<CultistFactoryProductionPrototype>(args.Item, out var prototype))
return;
if (ent.Comp.RitesBloodAmount < prototype.BloodCost)
{
var message = Loc.GetString("verb-blood-rites-no-blood", ("required", prototype.BloodCost),
("blood", ent.Comp.RitesBloodAmount));
_popupSystem.PopupEntity(message, ent, ent);
return;
}
var success = false;
foreach (var item in prototype.Item)
{
var entity = Spawn(item, Transform(ent).Coordinates);
if (_handsSystem.TryPickupAnyHand(ent, entity))
{
success = true;
continue;
}
_popupSystem.PopupEntity(Loc.GetString("verb-blood-rites-no-hand"), ent, ent);
QueueDel(entity);
break;
}
if (success)
ent.Comp.RitesBloodAmount -= prototype.BloodCost;
}
} }

View File

@@ -3,7 +3,6 @@ using Content.Shared.FixedPoint;
using Content.Shared.Mind; using Content.Shared.Mind;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared._White.Cult.Components; namespace Content.Shared._White.Cult.Components;
@@ -51,13 +50,6 @@ public sealed partial class CultistComponent : Component
CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, ConcealPresenceAction, CultShadowShacklesAction CultSummonCombatEquipmentAction, CultStunAction, EmpPulseAction, ConcealPresenceAction, CultShadowShacklesAction
}; };
[DataField("bloodRites", customTypeSerializer: typeof(PrototypeIdListSerializer<CultistFactoryProductionPrototype>))]
public List<string> BloodRites = new ()
{
"FactoryCultBloodSpear",
"FactoryCultBloodBarrage"
};
[ViewVariables, NonSerialized] [ViewVariables, NonSerialized]
public Entity<BloodSpearComponent>? BloodSpear; public Entity<BloodSpearComponent>? BloodSpear;

View File

@@ -7,6 +7,8 @@ public sealed class CultRuneInvokeEvent : EntityEventArgs
public HashSet<EntityUid> Cultists { get; } public HashSet<EntityUid> Cultists { get; }
public bool Result { get; set; } public bool Result { get; set; }
public string? InvokePhraseOverride { get; set; } = null;
public CultRuneInvokeEvent(EntityUid rune, EntityUid user, HashSet<EntityUid> cultists) public CultRuneInvokeEvent(EntityUid rune, EntityUid user, HashSet<EntityUid> cultists)
{ {
Rune = rune; Rune = rune;

Binary file not shown.

View File

@@ -8,6 +8,9 @@ ent-SoulShardGhost = камень душ
ent-StunHand = оглушающая аура ent-StunHand = оглушающая аура
.desc = Оглушит и обезмолвит жертву при ударе. .desc = Оглушит и обезмолвит жертву при ударе.
ent-RitesHand = аура кровавых обрядов
.desc = Впитывает кровь из всего, к чему прикасается. При ударе по культистам и конструктам может исцелить их. Используйте в руке, чтобы провести продвинутый обряд.
ent-ShadowShackles = теневые оковы ent-ShadowShackles = теневые оковы
.desc = Оковы, сковывающие запястья с помощью зловещей магии. .desc = Оковы, сковывающие запястья с помощью зловещей магии.

View File

@@ -1,3 +1,12 @@
cult-stun-component-extra-message = [color=darkgray]Выбросите ауру, чтобы навсегда избавиться от неё.[/color] cult-hand-component-extra-message = [color=darkgray]Выбросите ауру, чтобы навсегда избавиться от неё.[/color]
cult-magic-holy = Сила священного предмета в руках цели препятствует магии! cult-magic-holy = Сила священного предмета в руках цели препятствует магии!
cult-magic-no-empty-hand = Вам нужна свободная рука для использования заклинания! cult-magic-no-empty-hand = Вам нужна свободная рука для использования заклинания!
cult-rites-examine = [color=darkred]Всего высосано крови: { $blood }.[/color]
cult-rites-message = Всего высосано крови: { $blood }.
cult-rites-dead = Цель мертва.
cult-rites-heal-no-blood = Нет крови для лечения.
cult-rites-already-healed = Он не имеет повреждений.
cult-rites-after-heal = Осталось крови: { $blood }.
cult-rites-no-blood = Для обряда необходимо высосать { $required } крови с помощью заклинания Кровавые Обряды. Всего высосано: { $blood }.
cult-rites-no-hand = Вам нужна свободная рука для обряда.

View File

@@ -7,8 +7,3 @@ verb-spell-create-too-much = Начертите руну могущества,
verb-spell-remove-text = Удалить заклинание крови verb-spell-remove-text = Удалить заклинание крови
verb-spell-remove-message = Убрать любое из созданных заклинаний крови. verb-spell-remove-message = Убрать любое из созданных заклинаний крови.
verb-spell-remove-no-spells = Заклинания крови отсутствуют. verb-spell-remove-no-spells = Заклинания крови отсутствуют.
verb-blood-rites-text = Кровавые обряды
verb-blood-rites-message = Всего высосано крови: { $blood }.
verb-blood-rites-no-blood = Для обряда необходимо высосать { $required } крови с помощью заклинания Кровавые Обряды. Всего высосано: { $blood }.
verb-blood-rites-no-hand = Вам нужна свободная рука для обряда.

View File

@@ -221,8 +221,6 @@
type: SpellSelectorBUI type: SpellSelectorBUI
- key: enum.CultEmpowerRemoveUiKey.Key - key: enum.CultEmpowerRemoveUiKey.Key
type: SpellRemoverBUI type: SpellRemoverBUI
- key: enum.BloodRitesUi.Key
type: CultistFactoryBUI
# WD-EDIT END # WD-EDIT END
- type: Puller - type: Puller
- type: Butcherable - type: Butcherable

View File

@@ -123,7 +123,6 @@
sprite: /Textures/White/Cult/actions_cult.rsi sprite: /Textures/White/Cult/actions_cult.rsi
state: blood_rites state: blood_rites
event: !type:CultBloodRitesInstantActionEvent event: !type:CultBloodRitesInstantActionEvent
speech: "Fel'th Dol Ab'orod!"
charges: 5 charges: 5
temporary: true temporary: true
removeOnNoCharges: true removeOnNoCharges: true

View File

@@ -1,13 +1,11 @@
- type: entity - type: entity
id: CultTileSpawnEffect id: CultTileEffect
name: Sparkle name: cult tile effect
placement: placement:
mode: SnapgridCenter mode: SnapgridCenter
components: components:
# Animation is like 3 something seconds so we just need to despawn it before then.
- type: TimedDespawn - type: TimedDespawn
lifetime: 0.5 lifetime: 0.5
- type: EvaporationSparkle
- type: Transform - type: Transform
noRot: true noRot: true
anchored: true anchored: true
@@ -19,6 +17,14 @@
netsync: false netsync: false
drawdepth: FloorObjects drawdepth: FloorObjects
color: "#FF0000" color: "#FF0000"
- type: entity
parent: CultTileEffect
id: CultTileSpawnEffect
name: cult tile glow
placement:
mode: SnapgridCenter
components:
- type: PointLight - type: PointLight
color: "#FF0000" color: "#FF0000"
@@ -26,10 +32,8 @@
id: CultTeleportInEffect id: CultTeleportInEffect
name: Teleport in name: Teleport in
components: components:
# Animation is like 3 something seconds so we just need to despawn it before then.
- type: TimedDespawn - type: TimedDespawn
lifetime: 0.8 lifetime: 0.8
- type: EvaporationSparkle
- type: Transform - type: Transform
noRot: true noRot: true
anchored: true anchored: true
@@ -47,10 +51,8 @@
id: CultTeleportOutEffect id: CultTeleportOutEffect
name: Teleport out name: Teleport out
components: components:
# Animation is like 3 something seconds so we just need to despawn it before then.
- type: TimedDespawn - type: TimedDespawn
lifetime: 0.8 lifetime: 0.8
- type: EvaporationSparkle
- type: Transform - type: Transform
noRot: true noRot: true
anchored: true anchored: true
@@ -70,7 +72,6 @@
components: components:
- type: TimedDespawn - type: TimedDespawn
lifetime: 1 lifetime: 1
- type: EvaporationSparkle
- type: Transform - type: Transform
noRot: true noRot: true
anchored: true anchored: true
@@ -90,7 +91,6 @@
components: components:
- type: TimedDespawn - type: TimedDespawn
lifetime: 1 lifetime: 1
- type: EvaporationSparkle
- type: Transform - type: Transform
noRot: true noRot: true
anchored: true anchored: true

View File

@@ -0,0 +1,74 @@
- type: entity
parent: BaseItem
id: BaseCultHand
abstract: true
noSpawn: true
name: magical aura
description: A sinister looking aura that distorts the flow of reality around it.
components:
- type: Item
size: Ginormous
- type: MeleeWeapon
canHeavyAttack: false
canMiss: false
attackRate: 2
wideAnimationRotation: 180
damage:
types:
Heat: 0
- type: DeleteOnDropAttempt
message: cult-hand-component-extra-message
- type: Unremoveable
deleteOnDrop: true
- type: CultItem
canPickUp: false
- type: entity
parent: BaseCultHand
id: StunHand
name: stunning aura
description: Will stun and mute a weak-minded victim on hit.
components:
- type: Sprite
sprite: White/Cult/Entities/stun.rsi
state: icon
- type: Item
sprite: White/Cult/Entities/stun.rsi
- type: MeleeWeapon
canAttackSelf: false
attackWhitelist:
components:
- StatusEffects
attackBlacklist:
components:
- Cultist
- Construct
- type: CultStunHand
speech: "Fuu ma'jin!"
- type: entity
parent: BaseCultHand
id: RitesHand
name: blood rite aura
description: Absorbs blood from anything you touch. Touching cultists and constructs can heal them. Use in-hand to cast an advanced rite.
components:
- type: Sprite
sprite: White/Cult/Entities/rites.rsi
state: icon
- type: Item
sprite: White/Cult/Entities/rites.rsi
- type: MeleeWeapon
attackWhitelist:
components:
- Cultist
- Construct
- type: CultRitesHand
speech: "Fel'th Dol Ab'orod!"
- type: ActivatableUI
key: enum.BloodRitesUi.Key
inHandsOnly: true
closeOnHandDeselect: true
- type: UserInterface
interfaces:
- key: enum.BloodRitesUi.Key
type: CultistFactoryBUI

View File

@@ -1,34 +0,0 @@
- type: entity
parent: BaseItem
id: StunHand
name: stunning aura
description: Will stun and mute a weak-minded victim on hit.
components:
- type: Sprite
sprite: White/Cult/Entities/stun.rsi
state: icon
- type: Item
size: Ginormous
sprite: White/Cult/Entities/stun.rsi
- type: MeleeWeapon
canHeavyAttack: false
canAttackSelf: false
canMiss: false
attackWhitelist:
components:
- StatusEffects
attackBlacklist:
components:
- Cultist
- Construct
wideAnimationRotation: 180
damage:
types:
Heat: 0
- type: CultStunHand
- type: DeleteOnDropAttempt
message: cult-stun-component-extra-message
- type: Unremoveable
deleteOnDrop: true
- type: CultItem
canPickUp: false

View File

@@ -30,6 +30,7 @@
- type: Appearance - type: Appearance
- type: CultRuneBase - type: CultRuneBase
invokePhrase: "Qu'laris ver'don, thal'sorin mik'thar!" invokePhrase: "Qu'laris ver'don, thal'sorin mik'thar!"
gatherInvokers: false
- type: CultRuneBuff - type: CultRuneBuff
- type: CultRune - type: CultRune
- type: Concealable - type: Concealable
@@ -47,6 +48,8 @@
color: '#F80000' color: '#F80000'
- type: Appearance - type: Appearance
- type: CultRuneBase - type: CultRuneBase
invokePhrase: "H'drak v'loso, mir'kanas verbot!"
gatherInvokers: false
- type: CultEmpower - type: CultEmpower
isRune: true isRune: true
- type: UserInterface - type: UserInterface
@@ -55,7 +58,6 @@
type: SpellSelectorBUI type: SpellSelectorBUI
- type: CultRune - type: CultRune
- type: Concealable - type: Concealable
examinableWhileConcealed: true
- type: entity - type: entity
parent: CollideRune parent: CollideRune
@@ -91,7 +93,6 @@
- type: Appearance - type: Appearance
- type: CultRuneBase - type: CultRuneBase
invokePhrase: "N'ath reth sh'yro eth d'rekkathnor!" invokePhrase: "N'ath reth sh'yro eth d'rekkathnor!"
gatherInvokers: true
- type: CultRuneSummoning - type: CultRuneSummoning
- type: CultRune - type: CultRune
- type: Concealable - type: Concealable

View File

@@ -7,8 +7,8 @@
context: "human" context: "human"
- type: InputMover - type: InputMover
- type: MovementSpeedModifier - type: MovementSpeedModifier
baseWalkSpeed: 2.5 baseWalkSpeed: 3
baseSprintSpeed: 2.5 baseSprintSpeed: 3
- type: DamageOnHighSpeedImpact - type: DamageOnHighSpeedImpact
damage: damage:
types: types:
@@ -61,6 +61,8 @@
- type: ContentEye - type: ContentEye
- type: Actions - type: Actions
- type: Hands - type: Hands
- type: Puller
needsHands: false
- type: ShowCultHud - type: ShowCultHud
- type: IsDeadIC - type: IsDeadIC
- type: NightVision - type: NightVision
@@ -138,8 +140,6 @@
baseWalkSpeed: 3 baseWalkSpeed: 3
baseSprintSpeed: 3 baseSprintSpeed: 3
- type: MovementIgnoreGravity - type: MovementIgnoreGravity
- type: Puller
needsHands: false
- type: MeleeWeapon - type: MeleeWeapon
canHeavyAttack: false canHeavyAttack: false
hidden: true hidden: true
@@ -202,8 +202,6 @@
150: Dead 150: Dead
- type: Sprite - type: Sprite
state: harvester state: harvester
- type: Puller
needsHands: false
- type: MeleeWeapon - type: MeleeWeapon
canHeavyAttack: false canHeavyAttack: false
hidden: true hidden: true

View File

@@ -45,8 +45,6 @@
type: SpellSelectorBUI type: SpellSelectorBUI
- key: enum.CultEmpowerRemoveUiKey.Key - key: enum.CultEmpowerRemoveUiKey.Key
type: SpellRemoverBUI type: SpellRemoverBUI
- key: enum.BloodRitesUi.Key
type: CultistFactoryBUI
# WD-EDIT END # WD-EDIT END
- type: Sprite - type: Sprite
scale: 0.9, 0.9 scale: 0.9, 0.9

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 543 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

View File

@@ -0,0 +1,66 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/pull/49264/commits/d0dffe7ca643db2624424fdcebf45863f85c0448",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "inhand-left",
"directions": 4,
"delays": [
[
0.1,
0.1,
0.1
],
[
0.1,
0.1,
0.1
],
[
0.1,
0.1,
0.1
],
[
0.1,
0.1,
0.1
]
]
},
{
"name": "inhand-right",
"directions": 4,
"delays": [
[
0.1,
0.1,
0.1
],
[
0.1,
0.1,
0.1
],
[
0.1,
0.1,
0.1
],
[
0.1,
0.1,
0.1
]
]
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 845 B

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 843 B

After

Width:  |  Height:  |  Size: 504 B