mirror of
https://github.com/wega-team/ss14-wega.git
synced 2026-06-09 10:06:49 +02:00
927 lines
33 KiB
C#
927 lines
33 KiB
C#
using System.Linq;
|
|
using System.Numerics;
|
|
using Content.Server.Body.Systems;
|
|
using Content.Server.Cargo.Components;
|
|
using Content.Server.Chat.Systems;
|
|
using Content.Server.Dice;
|
|
using Content.Server.Economy.SlotMachine;
|
|
using Content.Server.Guardian;
|
|
using Content.Server.Hallucinations;
|
|
using Content.Server.Polymorph.Systems;
|
|
using Content.Server.Revolutionary.Components;
|
|
using Content.Server.Shuttles.Components;
|
|
using Content.Server.Stack;
|
|
using Content.Server.Station.Components;
|
|
using Content.Server.Surgery;
|
|
using Content.Shared.Administration.Systems;
|
|
using Content.Shared.Armor;
|
|
using Content.Shared.Blood.Cult.Components;
|
|
using Content.Shared.Body.Components;
|
|
using Content.Shared.Card.Tarot;
|
|
using Content.Shared.Card.Tarot.Components;
|
|
using Content.Shared.Chat;
|
|
using Content.Shared.Chemistry.EntitySystems;
|
|
using Content.Shared.Chemistry.Reagent;
|
|
using Content.Shared.CombatMode.Pacification;
|
|
using Content.Shared.Damage;
|
|
using Content.Shared.Damage.Components;
|
|
using Content.Shared.Damage.Prototypes;
|
|
using Content.Shared.Damage.Systems;
|
|
using Content.Shared.Disease.Components;
|
|
using Content.Shared.EnergyShield;
|
|
using Content.Shared.FixedPoint;
|
|
using Content.Shared.Ghost;
|
|
using Content.Shared.Gravity;
|
|
using Content.Shared.Humanoid;
|
|
using Content.Shared.IdentityManagement;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.Interaction.Events;
|
|
using Content.Shared.Inventory;
|
|
using Content.Shared.Lock;
|
|
using Content.Shared.Mobs.Components;
|
|
using Content.Shared.Nutrition.EntitySystems;
|
|
using Content.Shared.Pinpointer;
|
|
using Content.Shared.Polymorph;
|
|
using Content.Shared.Popups;
|
|
using Content.Shared.Stacks;
|
|
using Content.Shared.StatusEffectNew;
|
|
using Content.Shared.Storage.Components;
|
|
using Content.Shared.Stunnable;
|
|
using Content.Shared.Tag;
|
|
using Content.Shared.Throwing;
|
|
using Content.Shared.Tiles;
|
|
using Content.Shared.Traits.Assorted;
|
|
using Content.Shared.Trigger.Components.Triggers;
|
|
using Content.Shared.Trigger.Systems;
|
|
using Content.Shared.VendingMachines;
|
|
using Content.Shared.Weapons.Melee;
|
|
using Content.Shared.Weapons.Ranged.Components;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Enums;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Physics.Systems;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Random;
|
|
using Robust.Shared.Timing;
|
|
|
|
namespace Content.Server.Card.Tarot;
|
|
|
|
public sealed partial class CardTarotSystem : EntitySystem
|
|
{
|
|
[Dependency] private AppearanceSystem _appearance = default!;
|
|
[Dependency] private BloodstreamSystem _blood = default!;
|
|
[Dependency] private ChatSystem _chat = default!;
|
|
[Dependency] private SharedContainerSystem _container = default!;
|
|
[Dependency] private DamageableSystem _damage = default!;
|
|
[Dependency] private DiceOfFateSystem _dice = default!;
|
|
[Dependency] private EntityLookupSystem _entityLookup = default!;
|
|
[Dependency] private SharedGravitySystem _gravity = default!;
|
|
[Dependency] private HallucinationsSystem _hallucinations = default!;
|
|
[Dependency] private IngestionSystem _ingestion = default!;
|
|
[Dependency] private InventorySystem _inventory = default!;
|
|
[Dependency] private LockSystem _lock = default!;
|
|
[Dependency] private SharedPhysicsSystem _physics = default!;
|
|
[Dependency] private MetaDataSystem _meta = default!;
|
|
[Dependency] private PolymorphSystem _polymorph = default!;
|
|
[Dependency] private SharedPopupSystem _popup = default!;
|
|
[Dependency] private IMapManager _mapManager = default!;
|
|
[Dependency] private IRobustRandom _random = default!;
|
|
[Dependency] private RejuvenateSystem _rejuvenate = default!;
|
|
[Dependency] private SlotMachineSystem _slotMachine = default!;
|
|
[Dependency] private SharedSolutionContainerSystem _solution = default!;
|
|
[Dependency] private StackSystem _stack = default!;
|
|
[Dependency] private StatusEffectsSystem _statusEffects = default!;
|
|
[Dependency] private SharedStunSystem _stun = default!;
|
|
[Dependency] private SurgerySystem _surgery = default!;
|
|
[Dependency] private TagSystem _tag = default!;
|
|
[Dependency] private ThrowingSystem _throwing = default!;
|
|
[Dependency] private TriggerSystem _trigger = default!;
|
|
[Dependency] private SharedTransformSystem _transform = default!;
|
|
[Dependency] private SharedAudioSystem _audio = default!;
|
|
|
|
// 200,000 static variables are ready, and another million is on the way
|
|
private static readonly EntProtoId Ash = "Ash";
|
|
private static readonly EntProtoId ClusterBang = "ClusterBangFull";
|
|
private static readonly EntProtoId ClusterGrenade = "ClusterGrenade";
|
|
private static readonly EntProtoId CursedSlotMachine = "CursedSlotMachine";
|
|
private static readonly EntProtoId Drunk = "StatusEffectDrunk";
|
|
private static readonly EntProtoId EmptyCardTarot = "CardTarotNotEnchanted";
|
|
private static readonly EntProtoId Pill = "StrangePill";
|
|
private static readonly EntProtoId RandomContainer = "RandomContainerBlank";
|
|
private static readonly EntProtoId RandomVending = "RandomVending";
|
|
private static readonly EntProtoId Rock = "WallRock";
|
|
private static readonly EntProtoId Smoke = "AdminInstantEffectSmoke30";
|
|
private static readonly EntProtoId SpaceCash = "SpaceCash";
|
|
private static readonly EntProtoId Stand = "MobHoloparasiteGuardian";
|
|
|
|
private static readonly List<EntProtoId> DeathEnt = new() {
|
|
"BloodCultConstruct", "BloodCultSoulStone"
|
|
};
|
|
|
|
private static readonly List<EntProtoId> JusticeEnt = new() {
|
|
"MedkitFilled", "TearGasGrenade", "WeaponWandPolymorphDoor", "SpaceCash100"
|
|
};
|
|
|
|
private static readonly ProtoId<DamageTypePrototype> BluntDamage = "Blunt";
|
|
private static readonly ProtoId<DamageTypePrototype> CellularDamage = "Cellular";
|
|
private static readonly ProtoId<DamageTypePrototype> HeatDamage = "Heat";
|
|
private static readonly ProtoId<DamageTypePrototype> PoisonDamage = "Poison";
|
|
private static readonly ProtoId<DamageTypePrototype> RadiationDamage = "Blunt";
|
|
|
|
private static readonly ProtoId<PolymorphPrototype> ChariotStatue = "ChariotStatue";
|
|
|
|
private static readonly ProtoId<TagPrototype> Grenade = "HandGrenade";
|
|
private static readonly ProtoId<TagPrototype> SlowImmune = "SlowImmune";
|
|
private static readonly ProtoId<TagPrototype> StunImmune = "StunImmune";
|
|
|
|
private static readonly string Drug = "Desoxyephedrine";
|
|
private static readonly string NotHeal = "Puncturase"; // LoL
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<CardTarotComponent, AfterInteractEvent>(OnTarotInteract);
|
|
SubscribeLocalEvent<CardTarotComponent, UseInHandEvent>(OnUseTarot);
|
|
}
|
|
|
|
#region Card Tarot
|
|
private void OnTarotInteract(Entity<CardTarotComponent> ent, ref AfterInteractEvent args)
|
|
{
|
|
if (args.Handled || !args.CanReach || args.Target is not { Valid: true } target)
|
|
return;
|
|
|
|
if (ent.Comp.Card == CardTarot.NotEnchanted)
|
|
{
|
|
_popup.PopupEntity("tarot-card-not-enchanted", args.User, args.User);
|
|
return;
|
|
}
|
|
|
|
PerformTarotEffect(target, args.User, ent);
|
|
args.Handled = true;
|
|
}
|
|
|
|
private void OnUseTarot(Entity<CardTarotComponent> ent, ref UseInHandEvent args)
|
|
{
|
|
if (args.Handled)
|
|
return;
|
|
|
|
if (ent.Comp.Card == CardTarot.NotEnchanted)
|
|
{
|
|
_popup.PopupEntity("tarot-card-not-enchanted", args.User, args.User);
|
|
return;
|
|
}
|
|
|
|
PerformTarotEffect(args.User, args.User, ent);
|
|
args.Handled = true;
|
|
}
|
|
|
|
private void PerformTarotEffect(EntityUid target, EntityUid user, Entity<CardTarotComponent> card)
|
|
{
|
|
var isReversed = card.Comp.CardType == CardTarotType.Reversed;
|
|
|
|
switch (card.Comp.Card)
|
|
{
|
|
case CardTarot.NotEnchanted:
|
|
_popup.PopupEntity("tarot-card-not-enchanted", user, user);
|
|
return;
|
|
case CardTarot.Fool:
|
|
PerformFool(target, isReversed);
|
|
break;
|
|
case CardTarot.Magician:
|
|
PerformMagician(target, isReversed);
|
|
break;
|
|
case CardTarot.HighPriestess:
|
|
PerformHighPriestess(target, isReversed);
|
|
break;
|
|
case CardTarot.Empress:
|
|
PerformEmpress(target, isReversed);
|
|
break;
|
|
case CardTarot.Emperor:
|
|
PerformEmperor(target, isReversed);
|
|
break;
|
|
case CardTarot.Hierophant:
|
|
PerformHierophant(target, isReversed);
|
|
break;
|
|
case CardTarot.Lovers:
|
|
PerformLovers(target, isReversed);
|
|
break;
|
|
case CardTarot.Chariot:
|
|
PerformChariot(target, isReversed);
|
|
break;
|
|
case CardTarot.Justice:
|
|
PerformJustice(target, isReversed);
|
|
break;
|
|
case CardTarot.Hermit:
|
|
PerformHermit(target, isReversed);
|
|
break;
|
|
case CardTarot.WheelOfFortune:
|
|
PerformWheelOfFortune(target, isReversed);
|
|
break;
|
|
case CardTarot.Strength:
|
|
PerformStrength(target, isReversed);
|
|
break;
|
|
case CardTarot.HangedMan:
|
|
PerformHangedMan(target, isReversed);
|
|
break;
|
|
case CardTarot.Death:
|
|
PerformDeath(user, target, isReversed);
|
|
break;
|
|
case CardTarot.Temperance:
|
|
PerformTemperance(target, isReversed);
|
|
break;
|
|
case CardTarot.Devil:
|
|
PerformDevil(target, isReversed);
|
|
break;
|
|
case CardTarot.Tower:
|
|
PerformTower(target, isReversed);
|
|
break;
|
|
case CardTarot.Stars:
|
|
PerformStars(target, isReversed);
|
|
break;
|
|
case CardTarot.Moon:
|
|
PerformMoon(target, isReversed);
|
|
break;
|
|
case CardTarot.Sun:
|
|
PerformSun(target, isReversed);
|
|
break;
|
|
case CardTarot.Judgement:
|
|
PerformJudgement(target, isReversed);
|
|
break;
|
|
case CardTarot.TheWorld:
|
|
PerformTheWorld(target, isReversed);
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
var coords = Transform(user).Coordinates;
|
|
|
|
var ash = Spawn(Ash, coords);
|
|
_throwing.TryThrow(ash, _random.NextVector2());
|
|
_audio.PlayPredicted(card.Comp.UseSound, coords, user);
|
|
_popup.PopupEntity(Loc.GetString("tarot-used", ("name", Identity.Name(user, EntityManager)),
|
|
("type", Loc.GetString($"tarot-card-{card.Comp.Card.ToString().ToLower()}"))),
|
|
user, PopupType.Medium);
|
|
|
|
QueueDel(card);
|
|
}
|
|
|
|
#region Card Effects
|
|
private void PerformFool(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
if (_inventory.TryGetSlots(target, out var slots))
|
|
{
|
|
foreach (var slot in slots)
|
|
{
|
|
_inventory.TryUnequip(target, slot.Name, force: true);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EntityUid? shuttle = null;
|
|
var shuttles = EntityQueryEnumerator<ArrivalsShuttleComponent>();
|
|
while (shuttles.MoveNext(out var uid, out _))
|
|
{
|
|
shuttle = uid;
|
|
break;
|
|
}
|
|
|
|
if (shuttle == null)
|
|
return;
|
|
|
|
_transform.SetCoordinates(target, Transform(shuttle.Value).Coordinates);
|
|
}
|
|
}
|
|
|
|
private void PerformMagician(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var nearbyEntity = _entityLookup.GetEntitiesInRange<TransformComponent>(Transform(target).Coordinates, 6f, LookupFlags.Dynamic)
|
|
.Where(e => !e.Comp.Anchored).ToList();
|
|
|
|
foreach (var entity in nearbyEntity)
|
|
{
|
|
var entityUid = entity.Owner;
|
|
if (entityUid == target)
|
|
continue;
|
|
|
|
var targetPosition = _transform.GetWorldPosition(target);
|
|
var entityPosition = _transform.GetWorldPosition(entityUid);
|
|
var direction = (entityPosition - targetPosition).Normalized();
|
|
|
|
_physics.ApplyLinearImpulse(entityUid, direction * 2000f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_statusEffects.TrySetStatusEffectDuration(target, "StatusEffectPainNumbness", TimeSpan.FromMinutes(2));
|
|
}
|
|
}
|
|
|
|
private void PerformHighPriestess(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
/// Temporarily empty
|
|
}
|
|
else
|
|
{
|
|
var time = TimeSpan.FromSeconds(20);
|
|
_stun.TryAddParalyzeDuration(target, time);
|
|
Timer.Spawn(time, () =>
|
|
{
|
|
var damage = new DamageSpecifier { DamageDict = { { BluntDamage, 60 } } };
|
|
_damage.TryChangeDamage(target, damage);
|
|
});
|
|
}
|
|
}
|
|
|
|
private void PerformEmpress(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var nearbyEntity = _entityLookup.GetEntitiesInRange<MobStateComponent>(Transform(target).Coordinates, 6f)
|
|
.Where(e => e.Owner != target).ToList();
|
|
|
|
foreach (var entity in nearbyEntity)
|
|
{
|
|
var entityUid = entity.Owner;
|
|
if (HasComp<PacifiedComponent>(entityUid))
|
|
return;
|
|
|
|
EnsureComp<PacifiedComponent>(entityUid);
|
|
Timer.Spawn(TimeSpan.FromSeconds(40), () => { RemComp<PacifiedComponent>(entityUid); });
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!TryComp<BloodstreamComponent>(target, out var bloodstream) || bloodstream.BloodSolution == null)
|
|
return;
|
|
|
|
var drugQuantity = new ReagentQuantity(Drug, FixedPoint2.New(4.5));
|
|
var notHealQuantity = new ReagentQuantity(NotHeal, FixedPoint2.New(12));
|
|
|
|
_solution.TryAddReagent(bloodstream.BloodSolution.Value, drugQuantity, out _);
|
|
_solution.TryAddReagent(bloodstream.BloodSolution.Value, notHealQuantity, out _);
|
|
}
|
|
}
|
|
|
|
private void PerformEmperor(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var selected = new List<EntityUid>();
|
|
var commandQuery = EntityQueryEnumerator<CommandStaffComponent>();
|
|
while (commandQuery.MoveNext(out var uid, out _))
|
|
selected.Add(uid);
|
|
|
|
if (selected.Count == 0)
|
|
return;
|
|
|
|
_transform.SetCoordinates(target, Transform(_random.Pick(selected)).Coordinates);
|
|
}
|
|
else
|
|
{
|
|
EntityUid? bridgeUid = null;
|
|
var beaconQuery = EntityQueryEnumerator<NavMapBeaconComponent>();
|
|
while (beaconQuery.MoveNext(out var uid, out var beacon))
|
|
{
|
|
if (beacon.DefaultText == "station-beacon-bridge")
|
|
{
|
|
bridgeUid = uid;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bridgeUid == null)
|
|
return;
|
|
|
|
_transform.SetCoordinates(target, Transform(bridgeUid.Value).Coordinates);
|
|
}
|
|
}
|
|
|
|
private void PerformHierophant(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
/// Temporarily empty
|
|
}
|
|
else
|
|
{
|
|
var slot = "jumpsuit";
|
|
if (!_inventory.TryGetSlotEntity(target, slot, out var clothing))
|
|
return;
|
|
|
|
if (!HasComp<BloodShieldActivaebleComponent>(clothing))
|
|
{
|
|
EnsureComp<BloodShieldActivaebleComponent>(clothing.Value).CurrentSlot = slot;
|
|
|
|
var shield = EnsureComp<EnergyShieldOwnerComponent>(target);
|
|
shield.ShieldEntity = Spawn("BloodCultShieldEffect", Transform(target).Coordinates);
|
|
_transform.SetParent(shield.ShieldEntity.Value, target);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PerformLovers(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var damage = new DamageSpecifier { DamageDict = { { BluntDamage, 20 }, { HeatDamage, 20 } } };
|
|
_damage.TryChangeDamage(target, damage, true);
|
|
_blood.TryModifyBloodLevel(target, -120);
|
|
}
|
|
else
|
|
{
|
|
var damage = new DamageSpecifier { DamageDict = { { BluntDamage, -40 }, { HeatDamage, -40 }, { PoisonDamage, -40 } } };
|
|
_damage.TryChangeDamage(target, damage, true);
|
|
_blood.TryModifyBloodLevel(target, 100);
|
|
}
|
|
}
|
|
|
|
private void PerformChariot(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
_polymorph.PolymorphEntity(target, ChariotStatue);
|
|
}
|
|
else
|
|
{
|
|
var time = TimeSpan.FromSeconds(10);
|
|
_statusEffects.TrySetStatusEffectDuration(target, "StatusEffectDesoxyStamina", time);
|
|
|
|
bool isStunImmuned = false;
|
|
bool isSlowImmuned = false;
|
|
bool isPacified = false;
|
|
if (!_tag.HasTag(target, StunImmune))
|
|
{
|
|
isStunImmuned = _tag.TryAddTag(target, StunImmune);
|
|
}
|
|
|
|
if (!_tag.HasTag(target, SlowImmune))
|
|
{
|
|
isSlowImmuned = _tag.TryAddTag(target, SlowImmune);
|
|
}
|
|
|
|
if (!HasComp<PacifiedComponent>(target))
|
|
{
|
|
EnsureComp<PacifiedComponent>(target);
|
|
isPacified = true;
|
|
}
|
|
|
|
Timer.Spawn(time, () =>
|
|
{
|
|
if (isStunImmuned) _tag.RemoveTag(target, StunImmune);
|
|
if (isSlowImmuned) _tag.RemoveTag(target, SlowImmune);
|
|
if (isPacified) RemComp<PacifiedComponent>(target);
|
|
});
|
|
}
|
|
}
|
|
|
|
private void PerformJustice(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
Spawn(RandomContainer, Transform(target).Coordinates);
|
|
}
|
|
else
|
|
{
|
|
var coords = Transform(target).Coordinates;
|
|
foreach (var ent in JusticeEnt)
|
|
Spawn(ent, coords);
|
|
}
|
|
}
|
|
|
|
private void PerformHermit(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var allEnt = new HashSet<EntityUid>();
|
|
var nearbyGuns = _entityLookup.GetEntitiesInRange<GunComponent>(Transform(target).Coordinates, 6f);
|
|
var nearbyMelees = _entityLookup.GetEntitiesInRange<MeleeWeaponComponent>(Transform(target).Coordinates, 6f)
|
|
.Where(e => !HasComp<MobStateComponent>(e) && !Transform(e).Anchored);
|
|
var nearbyArmor = _entityLookup.GetEntitiesInRange<ArmorComponent>(Transform(target).Coordinates, 6f);
|
|
var nearbyGrenades = _entityLookup.GetEntitiesInRange<TriggerOnUseComponent>(Transform(target).Coordinates, 6f)
|
|
.Where(e => _tag.HasTag(e, Grenade));
|
|
|
|
allEnt.UnionWith(nearbyGuns.Select(e => e.Owner));
|
|
allEnt.UnionWith(nearbyMelees.Select(e => e.Owner));
|
|
allEnt.UnionWith(nearbyArmor.Select(e => e.Owner));
|
|
allEnt.UnionWith(nearbyGrenades.Select(e => e.Owner));
|
|
|
|
foreach (var ent in allEnt)
|
|
{
|
|
var cash = Spawn(SpaceCash, Transform(ent).Coordinates);
|
|
if (TryComp<StackComponent>(cash, out var stack) && TryComp<StaticPriceComponent>(ent, out var price))
|
|
_stack.SetCount((cash, stack), (int)price.Price);
|
|
|
|
QueueDel(ent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var map = Transform(target).MapID;
|
|
|
|
var selected = new List<EntityUid>();
|
|
var vendigsQuery = EntityQueryEnumerator<VendingMachineComponent>();
|
|
while (vendigsQuery.MoveNext(out var uid, out _))
|
|
{
|
|
if (map == Transform(uid).MapID)
|
|
selected.Add(uid);
|
|
}
|
|
|
|
if (selected.Count == 0)
|
|
return;
|
|
|
|
_transform.SetCoordinates(target, Transform(_random.Pick(selected)).Coordinates);
|
|
}
|
|
}
|
|
|
|
private void PerformWheelOfFortune(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var luck = _random.Next(1, 21);
|
|
_dice.RollFate(target, luck);
|
|
}
|
|
else
|
|
{
|
|
Spawn(RandomVending, Transform(target).Coordinates);
|
|
}
|
|
}
|
|
|
|
private void PerformStrength(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var nearbyEntity = _entityLookup.GetEntitiesInRange<MobStateComponent>(Transform(target).Coordinates, 6f);
|
|
|
|
foreach (var entity in nearbyEntity)
|
|
{
|
|
var entityUid = entity.Owner;
|
|
_hallucinations.StartHallucinations(entityUid, "Hallucinations", TimeSpan.FromMinutes(2), true, "MindBreaker");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!TryComp<DamageableComponent>(target, out var damageable))
|
|
return;
|
|
|
|
var oldMod = damageable.DamageModifierSetId;
|
|
_damage.SetDamageModifierSetId(target, "VampireBloodSwell");
|
|
|
|
Timer.Spawn(TimeSpan.FromSeconds(30), () => { _damage.SetDamageModifierSetId(target, oldMod); });
|
|
}
|
|
}
|
|
|
|
private void PerformHangedMan(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var slotMachine = Spawn(CursedSlotMachine, Transform(target).Coordinates);
|
|
_slotMachine.FreeSpeen(slotMachine, target);
|
|
}
|
|
else
|
|
{
|
|
_gravity.RefreshWeightless(target, false);
|
|
Timer.Spawn(TimeSpan.FromMinutes(1), () => { _gravity.RefreshWeightless(target, true); });
|
|
}
|
|
}
|
|
|
|
private void PerformDeath(EntityUid user, EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var coords = Transform(target).Coordinates;
|
|
foreach (var ent in DeathEnt)
|
|
Spawn(ent, coords);
|
|
}
|
|
else
|
|
{
|
|
var nearbyEntity = _entityLookup.GetEntitiesInRange<MobStateComponent>(Transform(target).Coordinates, 6f)
|
|
.Where(e => e.Owner != user);
|
|
|
|
var damage = new DamageSpecifier { DamageDict = { { BluntDamage, 20 }, { HeatDamage, 20 } } };
|
|
foreach (var entity in nearbyEntity)
|
|
{
|
|
_damage.TryChangeDamage(entity.Owner, damage, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PerformTemperance(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
var pill = Spawn(Pill, Transform(target).Coordinates);
|
|
if (!_ingestion.TryIngest(target, pill))
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_statusEffects.TryRemoveStatusEffect(target, Drunk);
|
|
if (TryComp<DiseaseCarrierComponent>(target, out var disease))
|
|
disease.Diseases.Clear();
|
|
|
|
if (HasComp<DamageableComponent>(target))
|
|
{
|
|
var healSpecifier = new DamageSpecifier();
|
|
|
|
var damage = _damage.GetAllDamage(target);
|
|
if (damage.DamageDict.TryGetValue(RadiationDamage, out var radDamage) && radDamage > 0)
|
|
healSpecifier.DamageDict[RadiationDamage] = -radDamage;
|
|
|
|
if (damage.DamageDict.TryGetValue(PoisonDamage, out var poisonDamage) && poisonDamage > 0)
|
|
healSpecifier.DamageDict[PoisonDamage] = -poisonDamage;
|
|
|
|
if (healSpecifier.DamageDict.Count > 0)
|
|
_damage.TryChangeDamage(target, healSpecifier, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PerformDevil(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var grenade = Spawn(ClusterBang, Transform(target).Coordinates);
|
|
_trigger.Trigger(grenade);
|
|
}
|
|
else
|
|
{
|
|
var nearbyEntity = _entityLookup.GetEntitiesInRange<DamageableComponent>(Transform(target).Coordinates, 6f)
|
|
.Where(e => e.Owner != target && HasComp<MobStateComponent>(e)).Select(e => e.Owner).ToList();
|
|
|
|
var heal = new DamageSpecifier { DamageDict = { { BluntDamage, -45 }, { HeatDamage, -45 } } };
|
|
_damage.TryChangeDamage(target, heal, true);
|
|
_popup.PopupEntity(Loc.GetString("tarot-devil-healed"), target, target);
|
|
|
|
Timer.Spawn(TimeSpan.FromSeconds(3), () =>
|
|
{
|
|
if (!Exists(target))
|
|
return;
|
|
|
|
StartDamagePhase(nearbyEntity);
|
|
});
|
|
}
|
|
}
|
|
|
|
private void StartDamagePhase(List<EntityUid> initialNearby)
|
|
{
|
|
var validTargets = initialNearby.Where(Exists).ToList();
|
|
if (validTargets.Count == 0)
|
|
return;
|
|
|
|
var damageTicks = 30;
|
|
var currentDamageTick = 0;
|
|
|
|
var totalBluntPerTarget = 22;
|
|
var totalHeatPerTarget = 23;
|
|
|
|
var bluntPerSecondPerTarget = totalBluntPerTarget / (float)damageTicks;
|
|
var heatPerSecondPerTarget = totalHeatPerTarget / (float)damageTicks;
|
|
|
|
var damageDealt = new Dictionary<EntityUid, (float Blunt, float Heat)>();
|
|
foreach (var target in validTargets)
|
|
{
|
|
damageDealt[target] = (0, 0);
|
|
}
|
|
|
|
void DamageTick()
|
|
{
|
|
validTargets = validTargets.Where(Exists).ToList();
|
|
if (validTargets.Count == 0)
|
|
return;
|
|
|
|
foreach (var target in validTargets)
|
|
{
|
|
var expectedBlunt = bluntPerSecondPerTarget * (currentDamageTick + 1);
|
|
var expectedHeat = heatPerSecondPerTarget * (currentDamageTick + 1);
|
|
|
|
var (dealtBlunt, dealtHeat) = damageDealt[target];
|
|
var bluntThisTick = (int)(expectedBlunt - dealtBlunt);
|
|
var heatThisTick = (int)(expectedHeat - dealtHeat);
|
|
|
|
if (bluntThisTick > 0 || heatThisTick > 0)
|
|
{
|
|
var damageSpec = new DamageSpecifier();
|
|
|
|
if (bluntThisTick > 0)
|
|
damageSpec.DamageDict[BluntDamage] = bluntThisTick;
|
|
|
|
if (heatThisTick > 0)
|
|
damageSpec.DamageDict[HeatDamage] = heatThisTick;
|
|
|
|
_damage.TryChangeDamage(target, damageSpec, true);
|
|
|
|
if (_random.Prob(0.2f))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("tarot-devil-damaged"), target, target);
|
|
}
|
|
}
|
|
}
|
|
|
|
currentDamageTick++;
|
|
if (currentDamageTick < damageTicks)
|
|
{
|
|
Timer.Spawn(TimeSpan.FromSeconds(1), DamageTick);
|
|
}
|
|
}
|
|
|
|
DamageTick();
|
|
}
|
|
|
|
private void PerformTower(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var radius = 6;
|
|
var center = Transform(target).Coordinates;
|
|
for (int x = -radius; x <= radius; x++)
|
|
{
|
|
for (int y = -radius; y <= radius; y++)
|
|
{
|
|
if (x * x + y * y <= radius * radius)
|
|
{
|
|
if (_random.Prob(0.25f))
|
|
{
|
|
var spawnCoords = center.Offset(new Vector2(x, y));
|
|
Spawn(Rock, spawnCoords);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var grenade = Spawn(ClusterGrenade, Transform(target).Coordinates);
|
|
_trigger.Trigger(grenade);
|
|
}
|
|
}
|
|
|
|
private void PerformStars(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
var damage = new DamageSpecifier { DamageDict = { { CellularDamage, 50 } } };
|
|
_damage.TryChangeDamage(target, damage, true);
|
|
|
|
var damageTypes = new[] { "ClosedFracture", "ArterialBleeding", "MildBurns" };
|
|
_surgery.TryAddInternalDamage(target, _random.Pick(damageTypes));
|
|
|
|
for (var i = 0; i < 2; i++)
|
|
{
|
|
var card = Spawn(EmptyCardTarot, Transform(target).Coordinates);
|
|
var tarot = EnsureComp<CardTarotComponent>(card);
|
|
|
|
var allCards = Enum.GetValues<CardTarot>();
|
|
tarot.Card = (CardTarot)_random.Next(1, allCards.Length);
|
|
|
|
bool reversedCard = _random.Prob(0.5f);
|
|
if (reversed) tarot.CardType = CardTarotType.Reversed;
|
|
|
|
_appearance.SetData(card, CardTarotVisuals.State, tarot.Card);
|
|
_appearance.SetData(card, CardTarotVisuals.Reversed, reversedCard);
|
|
|
|
_meta.SetEntityName(card, Loc.GetString("tarot-card-name"));
|
|
_meta.SetEntityDescription(card, Loc.GetString("tarot-card-desc"));
|
|
|
|
_throwing.TryThrow(card, _random.NextVector2());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var lockers = new List<EntityUid>();
|
|
var lockersQuery = EntityQueryEnumerator<EntityStorageComponent, LockComponent, MetaDataComponent>();
|
|
while (lockersQuery.MoveNext(out var uid, out _, out _, out var meta))
|
|
{
|
|
if (meta.EntityPrototype != null && meta.EntityPrototype.ID == "LockerEvidence")
|
|
lockers.Add(uid);
|
|
}
|
|
|
|
if (lockers.Count == 0)
|
|
return;
|
|
|
|
var locker = _random.Pick(lockers);
|
|
var lockerCoords = Transform(locker).Coordinates;
|
|
var centerTile = Transform(locker).LocalPosition;
|
|
|
|
for (int radiusStep = 1; radiusStep <= 3; radiusStep++)
|
|
{
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
var angle = (float)i / 8 * MathHelper.TwoPi;
|
|
var offset = new Vector2(
|
|
(float)Math.Cos(angle) * radiusStep,
|
|
(float)Math.Sin(angle) * radiusStep);
|
|
|
|
var testPos = centerTile + offset;
|
|
var testCoords = new EntityCoordinates(lockerCoords.EntityId, testPos);
|
|
|
|
var mapCoords = _transform.ToMapCoordinates(testCoords);
|
|
var intersecting = _entityLookup.GetEntitiesIntersecting(mapCoords, LookupFlags.Dynamic)
|
|
.Where(e => e != target).ToList();
|
|
|
|
if (intersecting.Count == 0)
|
|
{
|
|
_transform.SetCoordinates(target, testCoords);
|
|
_lock.Unlock(locker, null);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PerformMoon(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
// Well, like i couldn't think of anything smarter
|
|
var message = Loc.GetString("tarot-moon-m-message");
|
|
if (TryComp<HumanoidProfileComponent>(target, out var humanoid) && humanoid.Gender == Gender.Female)
|
|
message = Loc.GetString("tarot-moon-f-message");
|
|
|
|
_chat.TrySendInGameICMessage(target, message, InGameICChatType.Speak, false);
|
|
|
|
}
|
|
else
|
|
{
|
|
var grids = _mapManager.GetAllGrids(Transform(target).MapID)
|
|
.Where(g => !HasComp<BecomesStationComponent>(g) && !HasComp<ProtectedGridComponent>(g)).ToList();
|
|
|
|
if (grids.Count == 0)
|
|
return;
|
|
|
|
var randomGrid = _random.Pick(grids);
|
|
_transform.SetCoordinates(target, Transform(randomGrid).Coordinates);
|
|
}
|
|
}
|
|
|
|
private void PerformSun(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
if (HasComp<PermanentBlindnessComponent>(target))
|
|
return;
|
|
|
|
EnsureComp<PermanentBlindnessComponent>(target).Blindness = 4;
|
|
Timer.Spawn(TimeSpan.FromMinutes(1), () => { RemComp<PermanentBlindnessComponent>(target); });
|
|
}
|
|
else
|
|
{
|
|
_rejuvenate.PerformRejuvenate(target);
|
|
}
|
|
}
|
|
|
|
private void PerformJudgement(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
/// Temporarily empty
|
|
}
|
|
else
|
|
{
|
|
// ALL GHOSTS BE MINE!!!
|
|
var ghosts = new List<EntityUid>();
|
|
var ghostsQuery = EntityQueryEnumerator<GhostComponent>();
|
|
while (ghostsQuery.MoveNext(out var uid, out _))
|
|
ghosts.Add(uid);
|
|
|
|
foreach (var ghost in ghosts)
|
|
_transform.SetCoordinates(ghost, Transform(target).Coordinates);
|
|
}
|
|
}
|
|
|
|
private void PerformTheWorld(EntityUid target, bool reversed)
|
|
{
|
|
if (reversed)
|
|
{
|
|
// He should be doing something else, but that means "Temporarily empty." So it's a reference to JoJo
|
|
var host = EnsureComp<GuardianHostComponent>(target);
|
|
var guardian = Spawn(Stand, Transform(target).Coordinates);
|
|
|
|
_container.Insert(guardian, host.GuardianContainer);
|
|
host.HostedGuardian = guardian;
|
|
|
|
if (TryComp<GuardianComponent>(guardian, out var guardianComp))
|
|
guardianComp.Host = target;
|
|
}
|
|
else
|
|
{
|
|
Spawn(Smoke, Transform(target).Coordinates);
|
|
}
|
|
}
|
|
#endregion
|
|
#endregion
|
|
}
|