1
0

RevenantSystem.Abilities.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. using Content.Shared.Popups;
  2. using Content.Shared.Damage;
  3. using Content.Shared.Revenant;
  4. using Robust.Shared.Random;
  5. using Content.Shared.Tag;
  6. using Content.Server.Storage.Components;
  7. using Content.Server.Light.Components;
  8. using Content.Server.Ghost;
  9. using Robust.Shared.Physics;
  10. using Content.Shared.Throwing;
  11. using Content.Server.Storage.EntitySystems;
  12. using Content.Shared.Interaction;
  13. using Content.Shared.Item;
  14. using Content.Shared.Bed.Sleep;
  15. using System.Linq;
  16. using System.Numerics;
  17. using Content.Server.Revenant.Components;
  18. using Content.Shared.Physics;
  19. using Content.Shared.DoAfter;
  20. using Content.Shared.Emag.Systems;
  21. using Content.Shared.FixedPoint;
  22. using Content.Shared.Humanoid;
  23. using Content.Shared.Maps;
  24. using Content.Shared.Mobs;
  25. using Content.Shared.Mobs.Components;
  26. using Content.Shared.Mobs.Systems;
  27. using Content.Shared.Revenant.Components;
  28. using Robust.Shared.Physics.Components;
  29. using Robust.Shared.Utility;
  30. using Robust.Shared.Map.Components;
  31. using Content.Shared.Whitelist;
  32. namespace Content.Server.Revenant.EntitySystems;
  33. public sealed partial class RevenantSystem
  34. {
  35. [Dependency] private readonly ThrowingSystem _throwing = default!;
  36. [Dependency] private readonly EntityStorageSystem _entityStorage = default!;
  37. [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
  38. [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
  39. [Dependency] private readonly GhostSystem _ghost = default!;
  40. [Dependency] private readonly TileSystem _tile = default!;
  41. [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
  42. [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
  43. [Dependency] private readonly SharedMapSystem _mapSystem = default!;
  44. private void InitializeAbilities()
  45. {
  46. SubscribeLocalEvent<RevenantComponent, UserActivateInWorldEvent>(OnInteract);
  47. SubscribeLocalEvent<RevenantComponent, SoulEvent>(OnSoulSearch);
  48. SubscribeLocalEvent<RevenantComponent, HarvestEvent>(OnHarvest);
  49. SubscribeLocalEvent<RevenantComponent, RevenantDefileActionEvent>(OnDefileAction);
  50. SubscribeLocalEvent<RevenantComponent, RevenantOverloadLightsActionEvent>(OnOverloadLightsAction);
  51. SubscribeLocalEvent<RevenantComponent, RevenantBlightActionEvent>(OnBlightAction);
  52. SubscribeLocalEvent<RevenantComponent, RevenantMalfunctionActionEvent>(OnMalfunctionAction);
  53. }
  54. private void OnInteract(EntityUid uid, RevenantComponent component, UserActivateInWorldEvent args)
  55. {
  56. if (args.Handled)
  57. return;
  58. if (args.Target == args.User)
  59. return;
  60. var target = args.Target;
  61. if (HasComp<PoweredLightComponent>(target))
  62. {
  63. args.Handled = _ghost.DoGhostBooEvent(target);
  64. return;
  65. }
  66. if (!HasComp<MobStateComponent>(target) || !HasComp<HumanoidAppearanceComponent>(target) || HasComp<RevenantComponent>(target))
  67. return;
  68. args.Handled = true;
  69. if (!TryComp<EssenceComponent>(target, out var essence) || !essence.SearchComplete)
  70. {
  71. EnsureComp<EssenceComponent>(target);
  72. BeginSoulSearchDoAfter(uid, target, component);
  73. }
  74. else
  75. {
  76. BeginHarvestDoAfter(uid, target, component, essence);
  77. }
  78. args.Handled = true;
  79. }
  80. private void BeginSoulSearchDoAfter(EntityUid uid, EntityUid target, RevenantComponent revenant)
  81. {
  82. var searchDoAfter = new DoAfterArgs(EntityManager, uid, revenant.SoulSearchDuration, new SoulEvent(), uid, target: target)
  83. {
  84. BreakOnMove = true,
  85. BreakOnDamage = true,
  86. DistanceThreshold = 2
  87. };
  88. if (!_doAfter.TryStartDoAfter(searchDoAfter))
  89. return;
  90. _popup.PopupEntity(Loc.GetString("revenant-soul-searching", ("target", target)), uid, uid, PopupType.Medium);
  91. }
  92. private void OnSoulSearch(EntityUid uid, RevenantComponent component, SoulEvent args)
  93. {
  94. if (args.Handled || args.Cancelled)
  95. return;
  96. if (!TryComp<EssenceComponent>(args.Args.Target, out var essence))
  97. return;
  98. essence.SearchComplete = true;
  99. string message;
  100. switch (essence.EssenceAmount)
  101. {
  102. case <= 45:
  103. message = "revenant-soul-yield-low";
  104. break;
  105. case >= 90:
  106. message = "revenant-soul-yield-high";
  107. break;
  108. default:
  109. message = "revenant-soul-yield-average";
  110. break;
  111. }
  112. _popup.PopupEntity(Loc.GetString(message, ("target", args.Args.Target)), args.Args.Target.Value, uid, PopupType.Medium);
  113. args.Handled = true;
  114. }
  115. private void BeginHarvestDoAfter(EntityUid uid, EntityUid target, RevenantComponent revenant, EssenceComponent essence)
  116. {
  117. if (essence.Harvested)
  118. {
  119. _popup.PopupEntity(Loc.GetString("revenant-soul-harvested"), target, uid, PopupType.SmallCaution);
  120. return;
  121. }
  122. if (TryComp<MobStateComponent>(target, out var mobstate) && mobstate.CurrentState == MobState.Alive && !HasComp<SleepingComponent>(target))
  123. {
  124. _popup.PopupEntity(Loc.GetString("revenant-soul-too-powerful"), target, uid);
  125. return;
  126. }
  127. if(_physics.GetEntitiesIntersectingBody(uid, (int) CollisionGroup.Impassable).Count > 0)
  128. {
  129. _popup.PopupEntity(Loc.GetString("revenant-in-solid"), uid, uid);
  130. return;
  131. }
  132. var doAfter = new DoAfterArgs(EntityManager, uid, revenant.HarvestDebuffs.X, new HarvestEvent(), uid, target: target)
  133. {
  134. DistanceThreshold = 2,
  135. BreakOnMove = true,
  136. BreakOnDamage = true,
  137. RequireCanInteract = false, // stuns itself
  138. };
  139. if (!_doAfter.TryStartDoAfter(doAfter))
  140. return;
  141. _appearance.SetData(uid, RevenantVisuals.Harvesting, true);
  142. _popup.PopupEntity(Loc.GetString("revenant-soul-begin-harvest", ("target", target)),
  143. target, PopupType.Large);
  144. TryUseAbility(uid, revenant, 0, revenant.HarvestDebuffs);
  145. }
  146. private void OnHarvest(EntityUid uid, RevenantComponent component, HarvestEvent args)
  147. {
  148. if (args.Cancelled)
  149. {
  150. _appearance.SetData(uid, RevenantVisuals.Harvesting, false);
  151. return;
  152. }
  153. if (args.Handled || args.Args.Target == null)
  154. return;
  155. _appearance.SetData(uid, RevenantVisuals.Harvesting, false);
  156. if (!TryComp<EssenceComponent>(args.Args.Target, out var essence))
  157. return;
  158. _popup.PopupEntity(Loc.GetString("revenant-soul-finish-harvest", ("target", args.Args.Target)),
  159. args.Args.Target.Value, PopupType.LargeCaution);
  160. essence.Harvested = true;
  161. ChangeEssenceAmount(uid, essence.EssenceAmount, component);
  162. _store.TryAddCurrency(new Dictionary<string, FixedPoint2>
  163. { {component.StolenEssenceCurrencyPrototype, essence.EssenceAmount} }, uid);
  164. if (!HasComp<MobStateComponent>(args.Args.Target))
  165. return;
  166. if (_mobState.IsAlive(args.Args.Target.Value) || _mobState.IsCritical(args.Args.Target.Value))
  167. {
  168. _popup.PopupEntity(Loc.GetString("revenant-max-essence-increased"), uid, uid);
  169. component.EssenceRegenCap += component.MaxEssenceUpgradeAmount;
  170. }
  171. //KILL THEMMMM
  172. if (!_mobThresholdSystem.TryGetThresholdForState(args.Args.Target.Value, MobState.Dead, out var damage))
  173. return;
  174. DamageSpecifier dspec = new();
  175. dspec.DamageDict.Add("Cold", damage.Value);
  176. _damage.TryChangeDamage(args.Args.Target, dspec, true, origin: uid);
  177. args.Handled = true;
  178. }
  179. private void OnDefileAction(EntityUid uid, RevenantComponent component, RevenantDefileActionEvent args)
  180. {
  181. if (args.Handled)
  182. return;
  183. if (!TryUseAbility(uid, component, component.DefileCost, component.DefileDebuffs))
  184. return;
  185. args.Handled = true;
  186. //var coords = Transform(uid).Coordinates;
  187. //var gridId = coords.GetGridUid(EntityManager);
  188. var xform = Transform(uid);
  189. if (!TryComp<MapGridComponent>(xform.GridUid, out var map))
  190. return;
  191. var tiles = _mapSystem.GetTilesIntersecting(
  192. xform.GridUid.Value,
  193. map,
  194. Box2.CenteredAround(_transformSystem.GetWorldPosition(xform),
  195. new Vector2(component.DefileRadius * 2, component.DefileRadius)))
  196. .ToArray();
  197. _random.Shuffle(tiles);
  198. for (var i = 0; i < component.DefileTilePryAmount; i++)
  199. {
  200. if (!tiles.TryGetValue(i, out var value))
  201. continue;
  202. _tile.PryTile(value);
  203. }
  204. var lookup = _lookup.GetEntitiesInRange(uid, component.DefileRadius, LookupFlags.Approximate | LookupFlags.Static);
  205. var tags = GetEntityQuery<TagComponent>();
  206. var entityStorage = GetEntityQuery<EntityStorageComponent>();
  207. var items = GetEntityQuery<ItemComponent>();
  208. var lights = GetEntityQuery<PoweredLightComponent>();
  209. foreach (var ent in lookup)
  210. {
  211. //break windows
  212. if (tags.HasComponent(ent) && _tag.HasTag(ent, "Window"))
  213. {
  214. //hardcoded damage specifiers til i die.
  215. var dspec = new DamageSpecifier();
  216. dspec.DamageDict.Add("Structural", 60);
  217. _damage.TryChangeDamage(ent, dspec, origin: uid);
  218. }
  219. if (!_random.Prob(component.DefileEffectChance))
  220. continue;
  221. //randomly opens some lockers and such.
  222. if (entityStorage.TryGetComponent(ent, out var entstorecomp))
  223. _entityStorage.OpenStorage(ent, entstorecomp);
  224. //chucks shit
  225. if (items.HasComponent(ent) &&
  226. TryComp<PhysicsComponent>(ent, out var phys) && phys.BodyType != BodyType.Static)
  227. _throwing.TryThrow(ent, _random.NextAngle().ToWorldVec());
  228. //flicker lights
  229. if (lights.HasComponent(ent))
  230. _ghost.DoGhostBooEvent(ent);
  231. }
  232. }
  233. private void OnOverloadLightsAction(EntityUid uid, RevenantComponent component, RevenantOverloadLightsActionEvent args)
  234. {
  235. if (args.Handled)
  236. return;
  237. if (!TryUseAbility(uid, component, component.OverloadCost, component.OverloadDebuffs))
  238. return;
  239. args.Handled = true;
  240. var xform = Transform(uid);
  241. var poweredLights = GetEntityQuery<PoweredLightComponent>();
  242. var mobState = GetEntityQuery<MobStateComponent>();
  243. var lookup = _lookup.GetEntitiesInRange(uid, component.OverloadRadius);
  244. //TODO: feels like this might be a sin and a half
  245. foreach (var ent in lookup)
  246. {
  247. if (!mobState.HasComponent(ent) || !_mobState.IsAlive(ent))
  248. continue;
  249. var nearbyLights = _lookup.GetEntitiesInRange(ent, component.OverloadZapRadius)
  250. .Where(e => poweredLights.HasComponent(e) && !HasComp<RevenantOverloadedLightsComponent>(e) &&
  251. _interact.InRangeUnobstructed(e, uid, -1)).ToArray();
  252. if (!nearbyLights.Any())
  253. continue;
  254. //get the closest light
  255. var allLight = nearbyLights.OrderBy(e =>
  256. Transform(e).Coordinates.TryDistance(EntityManager, xform.Coordinates, out var dist) ? component.OverloadZapRadius : dist);
  257. var comp = EnsureComp<RevenantOverloadedLightsComponent>(allLight.First());
  258. comp.Target = ent; //who they gon fire at?
  259. }
  260. }
  261. private void OnBlightAction(EntityUid uid, RevenantComponent component, RevenantBlightActionEvent args)
  262. {
  263. if (args.Handled)
  264. return;
  265. if (!TryUseAbility(uid, component, component.BlightCost, component.BlightDebuffs))
  266. return;
  267. args.Handled = true;
  268. // TODO: When disease refactor is in.
  269. }
  270. private void OnMalfunctionAction(EntityUid uid, RevenantComponent component, RevenantMalfunctionActionEvent args)
  271. {
  272. if (args.Handled)
  273. return;
  274. if (!TryUseAbility(uid, component, component.MalfunctionCost, component.MalfunctionDebuffs))
  275. return;
  276. args.Handled = true;
  277. foreach (var ent in _lookup.GetEntitiesInRange(uid, component.MalfunctionRadius))
  278. {
  279. if (_whitelistSystem.IsWhitelistFail(component.MalfunctionWhitelist, ent) ||
  280. _whitelistSystem.IsBlacklistPass(component.MalfunctionBlacklist, ent))
  281. continue;
  282. var ev = new GotEmaggedEvent(uid, EmagType.Interaction | EmagType.Access);
  283. RaiseLocalEvent(ent, ref ev);
  284. }
  285. }
  286. }