BlockingSystem.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. using System.Linq;
  2. using Content.Shared.Actions;
  3. using Content.Shared.Damage;
  4. using Content.Shared.Examine;
  5. using Content.Shared.Hands;
  6. using Content.Shared.Hands.Components;
  7. using Content.Shared.Hands.EntitySystems;
  8. using Content.Shared.IdentityManagement;
  9. using Content.Shared.Interaction.Events;
  10. using Content.Shared.Maps;
  11. using Content.Shared.Mobs.Components;
  12. using Content.Shared.Physics;
  13. using Content.Shared.Popups;
  14. using Content.Shared.Toggleable;
  15. using Content.Shared.Verbs;
  16. using Robust.Shared.Network;
  17. using Robust.Shared.Physics;
  18. using Robust.Shared.Physics.Components;
  19. using Robust.Shared.Physics.Systems;
  20. using Robust.Shared.Player;
  21. using Robust.Shared.Prototypes;
  22. using Robust.Shared.Timing;
  23. using Robust.Shared.Utility;
  24. namespace Content.Shared.Blocking;
  25. public sealed partial class BlockingSystem : EntitySystem
  26. {
  27. [Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
  28. [Dependency] private readonly ActionContainerSystem _actionContainer = default!;
  29. [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
  30. [Dependency] private readonly FixtureSystem _fixtureSystem = default!;
  31. [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
  32. [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
  33. [Dependency] private readonly EntityLookupSystem _lookup = default!;
  34. [Dependency] private readonly SharedPhysicsSystem _physics = default!;
  35. [Dependency] private readonly ExamineSystemShared _examine = default!;
  36. [Dependency] private readonly INetManager _net = default!;
  37. [Dependency] private readonly IGameTiming _gameTiming = default!;
  38. public override void Initialize()
  39. {
  40. base.Initialize();
  41. InitializeUser();
  42. SubscribeLocalEvent<BlockingComponent, GotEquippedHandEvent>(OnEquip);
  43. SubscribeLocalEvent<BlockingComponent, GotUnequippedHandEvent>(OnUnequip);
  44. SubscribeLocalEvent<BlockingComponent, DroppedEvent>(OnDrop);
  45. SubscribeLocalEvent<BlockingComponent, GetItemActionsEvent>(OnGetActions);
  46. SubscribeLocalEvent<BlockingComponent, ToggleActionEvent>(OnToggleAction);
  47. SubscribeLocalEvent<BlockingComponent, ComponentShutdown>(OnShutdown);
  48. SubscribeLocalEvent<BlockingComponent, GetVerbsEvent<ExamineVerb>>(OnVerbExamine);
  49. SubscribeLocalEvent<BlockingComponent, MapInitEvent>(OnMapInit);
  50. }
  51. private void OnMapInit(EntityUid uid, BlockingComponent component, MapInitEvent args)
  52. {
  53. _actionContainer.EnsureAction(uid, ref component.BlockingToggleActionEntity, component.BlockingToggleAction);
  54. Dirty(uid, component);
  55. }
  56. private void OnEquip(EntityUid uid, BlockingComponent component, GotEquippedHandEvent args)
  57. {
  58. component.User = args.User;
  59. Dirty(uid, component);
  60. //To make sure that this bodytype doesn't get set as anything but the original
  61. if (TryComp<PhysicsComponent>(args.User, out var physicsComponent) && physicsComponent.BodyType != BodyType.Static && !HasComp<BlockingUserComponent>(args.User))
  62. {
  63. var userComp = EnsureComp<BlockingUserComponent>(args.User);
  64. userComp.BlockingItem = uid;
  65. userComp.OriginalBodyType = physicsComponent.BodyType;
  66. }
  67. }
  68. private void OnUnequip(EntityUid uid, BlockingComponent component, GotUnequippedHandEvent args)
  69. {
  70. StopBlockingHelper(uid, component, args.User);
  71. }
  72. private void OnDrop(EntityUid uid, BlockingComponent component, DroppedEvent args)
  73. {
  74. StopBlockingHelper(uid, component, args.User);
  75. }
  76. private void OnGetActions(EntityUid uid, BlockingComponent component, GetItemActionsEvent args)
  77. {
  78. args.AddAction(ref component.BlockingToggleActionEntity, component.BlockingToggleAction);
  79. }
  80. private void OnToggleAction(EntityUid uid, BlockingComponent component, ToggleActionEvent args)
  81. {
  82. if (args.Handled)
  83. return;
  84. var blockQuery = GetEntityQuery<BlockingComponent>();
  85. var handQuery = GetEntityQuery<HandsComponent>();
  86. if (!handQuery.TryGetComponent(args.Performer, out var hands))
  87. return;
  88. var shields = _handsSystem.EnumerateHeld(args.Performer, hands).ToArray();
  89. foreach (var shield in shields)
  90. {
  91. if (shield == uid)
  92. continue;
  93. if (blockQuery.TryGetComponent(shield, out var otherBlockComp) && otherBlockComp.IsBlocking)
  94. {
  95. CantBlockError(args.Performer);
  96. return;
  97. }
  98. }
  99. if (component.IsBlocking)
  100. StopBlocking(uid, component, args.Performer);
  101. else
  102. StartBlocking(uid, component, args.Performer);
  103. args.Handled = true;
  104. }
  105. private void OnShutdown(EntityUid uid, BlockingComponent component, ComponentShutdown args)
  106. {
  107. //In theory the user should not be null when this fires off
  108. if (component.User != null)
  109. {
  110. _actionsSystem.RemoveProvidedActions(component.User.Value, uid);
  111. StopBlockingHelper(uid, component, component.User.Value);
  112. }
  113. }
  114. /// <summary>
  115. /// Called where you want the user to start blocking
  116. /// Creates a new hard fixture to bodyblock
  117. /// Also makes the user static to prevent prediction issues
  118. /// </summary>
  119. /// <param name="item"> The entity with the blocking component</param>
  120. /// <param name="component"> The <see cref="BlockingComponent"/></param>
  121. /// <param name="user"> The entity who's using the item to block</param>
  122. /// <returns></returns>
  123. public bool StartBlocking(EntityUid item, BlockingComponent component, EntityUid user)
  124. {
  125. if (component.IsBlocking)
  126. return false;
  127. var xform = Transform(user);
  128. var shieldName = Name(item);
  129. var blockerName = Identity.Entity(user, EntityManager);
  130. var msgUser = Loc.GetString("action-popup-blocking-user", ("shield", shieldName));
  131. var msgOther = Loc.GetString("action-popup-blocking-other", ("blockerName", blockerName), ("shield", shieldName));
  132. if (component.BlockingToggleAction != null)
  133. {
  134. //Don't allow someone to block if they're not parented to a grid
  135. if (xform.GridUid != xform.ParentUid)
  136. {
  137. CantBlockError(user);
  138. return false;
  139. }
  140. // Don't allow someone to block if they're not holding the shield
  141. if(!_handsSystem.IsHolding(user, item, out _))
  142. {
  143. CantBlockError(user);
  144. return false;
  145. }
  146. //Don't allow someone to block if someone else is on the same tile
  147. var playerTileRef = xform.Coordinates.GetTileRef();
  148. if (playerTileRef != null)
  149. {
  150. var intersecting = _lookup.GetLocalEntitiesIntersecting(playerTileRef.Value, 0f);
  151. var mobQuery = GetEntityQuery<MobStateComponent>();
  152. foreach (var uid in intersecting)
  153. {
  154. if (uid != user && mobQuery.HasComponent(uid))
  155. {
  156. TooCloseError(user);
  157. return false;
  158. }
  159. }
  160. }
  161. //Don't allow someone to block if they're somehow not anchored.
  162. _transformSystem.AnchorEntity(user, xform);
  163. if (!xform.Anchored)
  164. {
  165. CantBlockError(user);
  166. return false;
  167. }
  168. _actionsSystem.SetToggled(component.BlockingToggleActionEntity, true);
  169. if (_gameTiming.IsFirstTimePredicted)
  170. {
  171. _popupSystem.PopupEntity(msgOther, user, Filter.PvsExcept(user), true);
  172. if(_gameTiming.InPrediction)
  173. _popupSystem.PopupEntity(msgUser, user, user);
  174. }
  175. }
  176. if (TryComp<PhysicsComponent>(user, out var physicsComponent))
  177. {
  178. _fixtureSystem.TryCreateFixture(user,
  179. component.Shape,
  180. BlockingComponent.BlockFixtureID,
  181. hard: true,
  182. collisionLayer: (int) CollisionGroup.WallLayer,
  183. body: physicsComponent);
  184. }
  185. component.IsBlocking = true;
  186. Dirty(item, component);
  187. return true;
  188. }
  189. private void CantBlockError(EntityUid user)
  190. {
  191. var msgError = Loc.GetString("action-popup-blocking-user-cant-block");
  192. _popupSystem.PopupEntity(msgError, user, user);
  193. }
  194. private void TooCloseError(EntityUid user)
  195. {
  196. var msgError = Loc.GetString("action-popup-blocking-user-too-close");
  197. _popupSystem.PopupEntity(msgError, user, user);
  198. }
  199. /// <summary>
  200. /// Called where you want the user to stop blocking.
  201. /// </summary>
  202. /// <param name="item"> The entity with the blocking component</param>
  203. /// <param name="component"> The <see cref="BlockingComponent"/></param>
  204. /// <param name="user"> The entity who's using the item to block</param>
  205. /// <returns></returns>
  206. public bool StopBlocking(EntityUid item, BlockingComponent component, EntityUid user)
  207. {
  208. if (!component.IsBlocking)
  209. return false;
  210. var xform = Transform(user);
  211. var shieldName = Name(item);
  212. var blockerName = Identity.Entity(user, EntityManager);
  213. var msgUser = Loc.GetString("action-popup-blocking-disabling-user", ("shield", shieldName));
  214. var msgOther = Loc.GetString("action-popup-blocking-disabling-other", ("blockerName", blockerName), ("shield", shieldName));
  215. //If the component blocking toggle isn't null, grab the users SharedBlockingUserComponent and PhysicsComponent
  216. //then toggle the action to false, unanchor the user, remove the hard fixture
  217. //and set the users bodytype back to their original type
  218. if (component.BlockingToggleAction != null && TryComp<BlockingUserComponent>(user, out var blockingUserComponent)
  219. && TryComp<PhysicsComponent>(user, out var physicsComponent))
  220. {
  221. if (xform.Anchored)
  222. _transformSystem.Unanchor(user, xform);
  223. _actionsSystem.SetToggled(component.BlockingToggleActionEntity, false);
  224. _fixtureSystem.DestroyFixture(user, BlockingComponent.BlockFixtureID, body: physicsComponent);
  225. _physics.SetBodyType(user, blockingUserComponent.OriginalBodyType, body: physicsComponent);
  226. if (_gameTiming.IsFirstTimePredicted)
  227. {
  228. _popupSystem.PopupEntity(msgOther, user, Filter.PvsExcept(user), true);
  229. if(_gameTiming.InPrediction)
  230. _popupSystem.PopupEntity(msgUser, user, user);
  231. }
  232. }
  233. component.IsBlocking = false;
  234. Dirty(item, component);
  235. return true;
  236. }
  237. /// <summary>
  238. /// Called where you want someone to stop blocking and to remove the <see cref="BlockingUserComponent"/> from them
  239. /// Won't remove the <see cref="BlockingUserComponent"/> if they're holding another blocking item
  240. /// </summary>
  241. /// <param name="uid"> The item the component is attached to</param>
  242. /// <param name="component"> The <see cref="BlockingComponent"/> </param>
  243. /// <param name="user"> The person holding the blocking item </param>
  244. private void StopBlockingHelper(EntityUid uid, BlockingComponent component, EntityUid user)
  245. {
  246. if (component.IsBlocking)
  247. StopBlocking(uid, component, user);
  248. var userQuery = GetEntityQuery<BlockingUserComponent>();
  249. var handQuery = GetEntityQuery<HandsComponent>();
  250. if (!handQuery.TryGetComponent(user, out var hands))
  251. return;
  252. var shields = _handsSystem.EnumerateHeld(user, hands).ToArray();
  253. foreach (var shield in shields)
  254. {
  255. if (HasComp<BlockingComponent>(shield) && userQuery.TryGetComponent(user, out var blockingUserComponent))
  256. {
  257. blockingUserComponent.BlockingItem = shield;
  258. return;
  259. }
  260. }
  261. RemComp<BlockingUserComponent>(user);
  262. component.User = null;
  263. }
  264. private void OnVerbExamine(EntityUid uid, BlockingComponent component, GetVerbsEvent<ExamineVerb> args)
  265. {
  266. if (!args.CanInteract || !args.CanAccess || !_net.IsServer)
  267. return;
  268. var fraction = component.IsBlocking ? component.ActiveBlockFraction : component.PassiveBlockFraction;
  269. var modifier = component.IsBlocking ? component.ActiveBlockDamageModifier : component.PassiveBlockDamageModifer;
  270. var msg = new FormattedMessage();
  271. msg.AddMarkupOrThrow(Loc.GetString("blocking-fraction", ("value", MathF.Round(fraction * 100, 1))));
  272. AppendCoefficients(modifier, msg);
  273. _examine.AddDetailedExamineVerb(args, component, msg,
  274. Loc.GetString("blocking-examinable-verb-text"),
  275. "/Textures/Interface/VerbIcons/dot.svg.192dpi.png",
  276. Loc.GetString("blocking-examinable-verb-message")
  277. );
  278. }
  279. private void AppendCoefficients(DamageModifierSet modifiers, FormattedMessage msg)
  280. {
  281. foreach (var coefficient in modifiers.Coefficients)
  282. {
  283. msg.PushNewline();
  284. msg.AddMarkupOrThrow(Robust.Shared.Localization.Loc.GetString("blocking-coefficient-value",
  285. ("type", coefficient.Key),
  286. ("value", MathF.Round(coefficient.Value * 100, 1))
  287. ));
  288. }
  289. foreach (var flat in modifiers.FlatReduction)
  290. {
  291. msg.PushNewline();
  292. msg.AddMarkupOrThrow(Robust.Shared.Localization.Loc.GetString("blocking-reduction-value",
  293. ("type", flat.Key),
  294. ("value", flat.Value)
  295. ));
  296. }
  297. }
  298. }