Merge remote-tracking branch 'upstream/master' into ups

This commit is contained in:
Jabak
2024-07-31 01:26:23 +03:00
63 changed files with 1267 additions and 449 deletions

View File

@@ -0,0 +1,16 @@
using Content.Shared._White.Animations;
namespace Content.Server._White.Animations;
public sealed class WaddleAnimationSystem : SharedWaddledAnimationSystem
{
protected override void PlayAnimation(EntityUid user)
{
RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(user)));
}
protected override void StopAnimation(EntityUid user)
{
RaiseNetworkEvent(new StoppedWaddlingEvent(GetNetEntity(user)));
}
}

View File

@@ -5,6 +5,15 @@ namespace Content.Server._White.Carrying;
[RegisterComponent]
public sealed partial class CarriableComponent : Component
{
[DataField, ViewVariables(VVAccess.ReadWrite)]
public TimeSpan DoAfterLength = TimeSpan.FromSeconds(4);
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float WalkModifier = 0.7f;
[DataField, ViewVariables(VVAccess.ReadWrite)]
public float SprintModifier = 0.7f;
/// <summary>
/// Number of free hands required
/// to carry the entity
@@ -13,4 +22,4 @@ public sealed partial class CarriableComponent : Component
public int FreeHandsRequired = 2;
public CancellationTokenSource? CancelToken;
}
}

View File

@@ -207,9 +207,9 @@ public sealed class CarryingSystem : EntitySystem
args.Handled = true;
}
private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableComponent component)
public void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableComponent component)
{
var length = TimeSpan.FromSeconds(6); // т.к. удалили систему разницы масс увеличу время с 3 до 6
var length = component.DoAfterLength; // т.к. удалили систему разницы масс увеличу время с 3 до 6
if (length >= TimeSpan.FromSeconds(9))
{
_popupSystem.PopupEntity(Loc.GetString("carry-too-heavy"), carried, carrier,
@@ -230,7 +230,11 @@ public sealed class CarryingSystem : EntitySystem
NeedHand = true
};
_doAfterSystem.TryStartDoAfter(args);
if(_doAfterSystem.TryStartDoAfter(args))
{
_popupSystem.PopupEntity(Loc.GetString("carry-start", ("carrier", carrier)), carried, carried,
Shared.Popups.PopupType.SmallCaution);
}
}
private void Carry(EntityUid carrier, EntityUid carried)
@@ -260,17 +264,22 @@ public sealed class CarryingSystem : EntitySystem
_actionBlockerSystem.UpdateCanMove(carried);
}
public void DropCarried(EntityUid carrier, EntityUid carried)
public void DropCarried(EntityUid carrier,
EntityUid carried,
bool attachToGridOrMap = true,
bool removeCanEscape = true)
{
RemComp<CarryingComponent>(carrier); // get rid of this first so we don't recusrively fire that event
RemComp<CarryingSlowdownComponent>(carrier);
RemComp<BeingCarriedComponent>(carried);
RemComp<KnockedDownComponent>(carried);
RemComp<CanEscapeInventoryComponent>(carried);
if (removeCanEscape)
RemComp<CanEscapeInventoryComponent>(carried);
_actionBlockerSystem.UpdateCanMove(carried);
_virtualItemSystem.DeleteInHandsMatching(carrier, carried);
_transform.AttachToGridOrMap(carried);
if (attachToGridOrMap)
_transform.AttachToGridOrMap(carried);
_movementSpeed.RefreshMovementSpeedModifiers(carrier);
}
@@ -302,4 +311,4 @@ public sealed class CarryingSystem : EntitySystem
return true;
}
}
}

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;
[RegisterComponent]
public sealed partial class CultStunHandComponent : Component
public sealed partial class CultStunHandComponent : BaseMagicHandComponent
{
[DataField]
public TimeSpan Duration = TimeSpan.FromSeconds(16);
@@ -14,7 +14,4 @@ public sealed partial class CultStunHandComponent : Component
[DataField]
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

@@ -8,14 +8,9 @@ using Content.Server._White.Cult.UI;
using Content.Server._White.Wizard.Magic;
using Content.Server.Chat.Systems;
using Content.Shared._White.Chaplain;
using Content.Shared.Chemistry.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Content.Shared.Fluids.Components;
using Content.Shared.Inventory;
using Content.Shared.Stacks;
using Content.Shared.StatusEffect;
using Content.Shared.Stunnable;
using Content.Shared._White.Cult.Actions;
using Content.Shared._White.Cult.Components;
@@ -25,8 +20,8 @@ using Content.Shared.Actions;
using Content.Shared.Cuffs;
using Content.Shared.Cuffs.Components;
using Content.Shared.DoAfter;
using Content.Shared.Doors.Components;
using Content.Shared.Maps;
using Content.Shared.Mindshield.Components;
using Content.Shared.Popups;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
@@ -72,6 +67,21 @@ public partial class CultSystem
SubscribeLocalEvent<CultistComponent, CultStunActionEvent>(OnStun);
SubscribeLocalEvent<CultistComponent, ActionGettingRemovedEvent>(OnActionRemoved);
SubscribeLocalEvent<CultistComponent, ShacklesEvent>(OnShackles);
SubscribeLocalEvent<CultistComponent, TwistedConstructionEvent>(OnTwistedConstruction);
}
private void OnTwistedConstruction(Entity<CultistComponent> ent, ref TwistedConstructionEvent args)
{
if (args.Cancelled)
QueueDel(GetEntity(args.Effect));
if (args.Handled || args.Cancelled)
return;
args.Handled = true;
Del(args.Target);
Spawn(args.RunicDoor, GetCoordinates(args.Location));
}
private void OnShackles(Entity<CultistComponent> ent, ref ShacklesEvent args)
@@ -106,15 +116,7 @@ public partial class CultSystem
private void OnStun(EntityUid uid, CultistComponent component, CultStunActionEvent args)
{
var entity = Spawn("StunHand", 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;
GrantItem(uid, "StunHand", args);
}
private void OnTeleport(EntityUid uid, CultistComponent component, CultTeleportTargetActionEvent args)
@@ -123,22 +125,14 @@ public partial class CultSystem
!TryComp<ActorComponent>(uid, out var actor))
return;
if (_holyWeapon.IsHoldingHolyWeapon(args.Target))
if (!HasComp<CultistComponent>(args.Target))
{
_popupSystem.PopupEntity(Loc.GetString("cult-magic-holy"), args.Performer, args.Performer,
_popupSystem.PopupEntity("Цель должна быть культистом.", args.Performer, args.Performer,
PopupType.MediumCaution);
return;
}
if (!HasComp<CultistComponent>(args.Target) && !HasComp<ConstructComponent>(args.Target) &&
_actionBlocker.CanInteract(args.Target, null))
{
_popupSystem.PopupEntity("Цель должна быть культистом, быть скованной или парализованной.", args.Performer,
args.Performer, PopupType.MediumCaution);
return;
}
_bloodstreamSystem.TryModifyBloodLevel(uid, -5, bloodstream, createPuddle: false);
_bloodstreamSystem.TryModifyBloodLevel(uid, -7, bloodstream, createPuddle: false);
var eui = new CultTeleportSpellEui(args.Performer, args.Target);
_euiManager.OpenEui(eui, actor.PlayerSession);
@@ -150,61 +144,7 @@ public partial class CultSystem
private void OnBloodRites(EntityUid uid, CultistComponent component, CultBloodRitesInstantActionEvent args)
{
if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstreamComponent))
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;
GrantItem(uid, "RitesHand", args);
}
private static FixedPoint2 GetMissingBloodValue(BloodstreamComponent bloodstreamComponent)
@@ -429,13 +369,47 @@ public partial class CultSystem
if (!TryComp(args.Target, out CuffableComponent? cuffs) || cuffs.Container.ContainedEntities.Count > 0)
return;
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Performer, TimeSpan.FromSeconds(2),
new ShacklesEvent(), args.Performer, args.Target)
var doAfterArgs = new DoAfterArgs(EntityManager, args.Performer, TimeSpan.FromSeconds(2), new ShacklesEvent(),
args.Performer, args.Target)
{
BreakOnMove = true,
BreakOnDamage = true
});
};
if (!_doAfterSystem.TryStartDoAfter(doAfterArgs))
return;
Speak(args);
args.Handled = true;
}
private void ConvertDoor(EntityUid uid, EntityUid target, BloodstreamComponent bloodstream,
CultTwistedConstructionActionEvent args)
{
var meta = MetaData(target);
if (meta.EntityPrototype?.ID == args.RunicDoor.Id)
return;
var xform = Transform(target);
if (!xform.Anchored)
return;
var effect = Spawn(args.Effect, xform.Coordinates);
var ev = new TwistedConstructionEvent(GetNetEntity(effect), args.RunicDoor, GetNetCoordinates(xform.Coordinates));
var doAfterArgs = new DoAfterArgs(EntityManager, uid, args.Delay, ev, uid, target)
{
BreakOnDamage = true,
BreakOnMove = true,
};
if (!_doAfterSystem.TryStartDoAfter(doAfterArgs))
{
QueueDel(effect);
return;
}
_audio.PlayPvs(args.Sound, xform.Coordinates);
_bloodstreamSystem.TryModifyBloodLevel(uid, -12, bloodstream, createPuddle: false);
Speak(args);
args.Handled = true;
}
@@ -451,6 +425,12 @@ public partial class CultSystem
if (!TryComp<BloodstreamComponent>(args.Performer, out var bloodstreamComponent))
return;
if (HasComp<DoorComponent>(args.Target))
{
ConvertDoor(uid, args.Target, bloodstreamComponent, args);
return;
}
if (!_entityManager.TryGetComponent<StackComponent>(args.Target, out var stack))
return;
@@ -497,4 +477,17 @@ public partial class CultSystem
{
_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.Popups;
using Content.Server._White.Cult.GameRule;
@@ -46,9 +47,10 @@ public partial class CultSystem
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, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<CultEmpowerComponent, ActivateInWorldEvent>(OnActiveInWorld);
SubscribeLocalEvent<CultEmpowerComponent, CultRuneInvokeEvent>(OnActiveInWorld);
// UI
SubscribeLocalEvent<RuneDrawerProviderComponent, ActivatableUIOpenAttemptEvent>(OnRuneDrawAttempt);
@@ -396,19 +396,19 @@ public sealed partial class CultSystem : EntitySystem
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))
return;
foreach (var cultist in cultists)
{
_chat.TrySendInGameICMessage(cultist, component.InvokePhrase, component.InvokeChatType, false, false, null,
null, null, false);
_chat.TrySendInGameICMessage(cultist, invokePhraseOverride ?? component.InvokePhrase,
component.InvokeChatType, false, false, null, null, null, false);
}
}
@@ -455,16 +455,19 @@ public sealed partial class CultSystem : EntitySystem
!HasComp<MindShieldComponent>(victim.Value))
{
result = Convert(uid, victim.Value, args.User, args.Cultists);
args.InvokePhraseOverride = "Mah'weyh pleggh at e'ntrath!";
}
else
{
result = Sacrifice(uid, victim.Value, args.User, args.Cultists);
args.InvokePhraseOverride = "Barhah hra zar'garis!";
}
}
else
{
// Жертва мертва, выполняется альтернативное действие
result = SacrificeNonObjectiveDead(uid, victim.Value, args.User, args.Cultists);
args.InvokePhraseOverride = "Barhah hra zar'garis!";
}
args.Result = result;
@@ -1217,13 +1220,14 @@ public sealed partial class CultSystem : EntitySystem
* 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 _) ||
!TryComp<ActorComponent>(args.User, out var actor))
return;
_ui.TryOpen(uid, CultEmpowerUiKey.Key, actor.PlayerSession);
args.Result = !_ui.SessionHasOpenUi(uid, CultEmpowerUiKey.Key, actor.PlayerSession) &&
_ui.TryOpen(uid, CultEmpowerUiKey.Key, actor.PlayerSession);
}
private void OnUseInHand(EntityUid uid, CultEmpowerComponent component, UseInHandEvent args)
@@ -1377,6 +1381,7 @@ public sealed partial class CultSystem : EntitySystem
var shard = _entityManager.SpawnEntity("SoulShard", transform.Value);
_mindSystem.TransferTo(mindComponent.Mind.Value, shard);
_mindSystem.UnVisit(mindComponent.Mind.Value);
_bodySystem.GibBody(target);

View File

@@ -2,7 +2,6 @@ using System.Linq;
using Content.Server.Body.Components;
using Content.Shared._White.Cult;
using Content.Shared._White.Cult.Components;
using Content.Shared._White.Cult.UI;
using Content.Shared.DoAfter;
using Content.Shared.Verbs;
using Robust.Shared.Player;
@@ -17,7 +16,6 @@ public sealed partial class CultSystem
SubscribeLocalEvent<CultistComponent, CultEmpowerSelectedBuiMessage>(OnCultistEmpowerSelected);
SubscribeLocalEvent<CultistComponent, CultEmpowerRemoveBuiMessage>(OnCultistEmpowerRemove);
SubscribeLocalEvent<CultistComponent, SpellCreatedEvent>(OnSpellCreated);
SubscribeLocalEvent<CultistComponent, CultistFactoryItemSelectedMessage>(OnBloodRitesSelected);
}
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(removeSpellVerb);
args.Verbs.Add(bloodRitesVerb);
}
private void RemoveSpell(Entity<CultistComponent> ent, ICommonSession session)
@@ -130,36 +112,4 @@ public sealed partial class CultSystem
_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

@@ -0,0 +1,168 @@
using Content.Server._White.Carrying;
using Content.Shared.Verbs;
using Content.Shared.Item;
using Content.Shared.Hands;
using Content.Server.Storage.EntitySystems;
using Content.Server.Item;
using Content.Server.Popups;
using Content.Server.Resist;
using Content.Shared._White.Item.PseudoItem;
using Content.Shared.Resist;
using Content.Shared.Storage;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
namespace Content.Server._White.Items.PseudoItem;
public sealed class PseudoItemSystem : SharedPseudoItemSystem
{
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly StorageSystem _storageSystem = default!;
[Dependency] private readonly ItemSystem _itemSystem = default!;
[Dependency] private readonly CarryingSystem _carrying = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PseudoItemComponent, EntGotRemovedFromContainerMessage>(OnEntRemoved);
SubscribeLocalEvent<PseudoItemComponent, DropAttemptEvent>(OnDropAttempt);
SubscribeLocalEvent<PseudoItemComponent, EscapeInventoryEvent>(OnEscape);
SubscribeLocalEvent<StorageComponent, GetVerbsEvent<AlternativeVerb>>(AddAltVerb);
SubscribeLocalEvent<StorageComponent, GetVerbsEvent<Verb>>(AddVerb);
SubscribeLocalEvent<StorageComponent, PseudoItemInteractEvent>(OnInteract);
}
private void OnInteract(Entity<StorageComponent> ent, ref PseudoItemInteractEvent args)
{
if (!TryComp(args.Used, out PseudoItemComponent? pseudoItem))
return;
if (!TryInsert(ent.Owner, args.Used, args.User, pseudoItem, ent.Comp))
return;
_carrying.DropCarried(args.User, args.Used, false, false);
}
private void OnEscape(Entity<PseudoItemComponent> ent, ref EscapeInventoryEvent args)
{
NoLongerInContainer(ent.Owner, ent.Comp);
}
private void AddAltVerb(Entity<StorageComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract || !args.CanAccess)
return;
var user = args.User;
var (uid, comp) = ent;
if (!TryComp(user, out PseudoItemComponent? pseudoItem) || pseudoItem.Active)
return;
if (Transform(uid).ParentUid == user)
return;
AlternativeVerb verb = new()
{
Act = () =>
{
TryInsert(uid, user, user, pseudoItem, comp);
},
Text = Loc.GetString("action-name-insert-self"),
};
args.Verbs.Add(verb);
}
private void AddVerb(Entity<StorageComponent> ent, ref GetVerbsEvent<Verb> args)
{
if (!args.CanInteract || !args.CanAccess)
return;
var user = args.User;
var (uid, comp) = ent;
if (!TryComp(user, out CarryingComponent? carrying))
return;
var carried = carrying.Carried;
if (!TryComp(carried, out PseudoItemComponent? pseudoItem))
return;
Verb verb = new()
{
Act = () =>
{
if (TryInsert(uid, carried, user, pseudoItem, comp))
_carrying.DropCarried(user, carried, false, false);
},
Text = Loc.GetString("action-name-insert-other"),
};
args.Verbs.Add(verb);
}
private void OnEntRemoved(EntityUid uid, PseudoItemComponent component, EntGotRemovedFromContainerMessage args)
{
NoLongerInContainer(uid, component);
}
protected override void OnGettingPickedUp(Entity<PseudoItemComponent> ent, GettingPickedUpAttemptEvent args)
{
base.OnGettingPickedUp(ent, args);
if (args.User == args.Item)
return;
if (!TryComp(ent, out CarriableComponent? carriable))
_transform.AttachToGridOrMap(ent);
else if (_carrying.CanCarry(args.User, ent))
_carrying.StartCarryDoAfter(args.User, ent, carriable);
}
private void OnDropAttempt(EntityUid uid, PseudoItemComponent component, DropAttemptEvent args)
{
if (component.Active)
args.Cancel();
}
public bool TryInsert(EntityUid storageUid,
EntityUid toInsert,
EntityUid user,
PseudoItemComponent component,
StorageComponent? storage = null)
{
if (!Resolve(storageUid, ref storage))
return false;
var item = EnsureComp<ItemComponent>(toInsert);
_itemSystem.SetSize(toInsert, component.Size, item);
_itemSystem.SetShape(toInsert, component.Shape, item);
Dirty(toInsert, component);
if (!_storageSystem.Insert(storageUid, toInsert, out _, user, storage))
{
_popupSystem.PopupEntity(Loc.GetString("comp-storage-cant-insert"), user, user);
component.Active = false;
RemComp<ItemComponent>(toInsert);
return false;
}
component.Active = true;
EnsureComp<CanEscapeInventoryComponent>(toInsert);
return true;
}
private void NoLongerInContainer(EntityUid uid, PseudoItemComponent component)
{
if (!component.Active)
return;
RemCompDeferred<CanEscapeInventoryComponent>(uid);
RemCompDeferred<ItemComponent>(uid);
component.Active = false;
Dirty(uid, component);
}
}

View File

@@ -69,12 +69,18 @@ public sealed class OfferItemSystem : SharedOfferItemSystem
_popup.PopupEntity(Loc.GetString("offer-item-give",
("item", Identity.Entity(offerItem.Item.Value, EntityManager)),
("target", Identity.Entity(uid, EntityManager))), component.Target.Value, component.Target.Value);
("target", Identity.Entity(uid, EntityManager))),
component.Target.Value,
component.Target.Value);
_popup.PopupEntity(Loc.GetString("offer-item-give-other",
("user", Identity.Entity(component.Target.Value, EntityManager)),
("item", Identity.Entity(offerItem.Item.Value, EntityManager)),
("target", Identity.Entity(uid, EntityManager)))
, component.Target.Value, Filter.PvsExcept(component.Target.Value, entityManager: EntityManager), true);
("target", Identity.Entity(uid, EntityManager))),
component.Target.Value,
Filter.PvsExcept(component.Target.Value, entityManager: EntityManager),
true);
RaiseLocalEvent(offerItem.Item.Value, new HandedEvent(component.Target.Value, uid));
}
offerItem.Item = null;

View File

@@ -24,6 +24,7 @@ using Content.Shared._White.Antag;
using Content.Shared._White.BetrayalDagger;
using Content.Shared._White.Cult.Components;
using Content.Shared._White.Events;
using Content.Shared._White.Item.PseudoItem;
using Content.Shared._White.Wizard;
using Content.Shared._White.Wizard.Magic;
using Content.Shared.Actions;
@@ -854,6 +855,9 @@ public sealed class WizardSpellsSystem : EntitySystem
public bool CanCast(BaseActionEvent msg)
{
if (TryComp(msg.Performer, out PseudoItemComponent? pseudoItem) && pseudoItem.Active)
return false;
return !msg.Handled && CheckRequirements(msg.Action, msg.Performer) &&
!_statusEffectsSystem.HasStatusEffect(msg.Performer, "Incorporeal");
}