SharedBodySystem.Parts.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. using System.Diagnostics.CodeAnalysis;
  2. using System.Linq;
  3. using Content.Shared.Body.Components;
  4. using Content.Shared.Body.Events;
  5. using Content.Shared.Body.Organ;
  6. using Content.Shared.Body.Part;
  7. using Content.Shared.Damage;
  8. using Content.Shared.Damage.Prototypes;
  9. using Content.Shared.Movement.Components;
  10. using Robust.Shared.Containers;
  11. using Robust.Shared.Utility;
  12. namespace Content.Shared.Body.Systems;
  13. public partial class SharedBodySystem
  14. {
  15. private void InitializeParts()
  16. {
  17. // TODO: This doesn't handle comp removal on child ents.
  18. // If you modify this also see the Body partial for root parts.
  19. SubscribeLocalEvent<BodyPartComponent, EntInsertedIntoContainerMessage>(OnBodyPartInserted);
  20. SubscribeLocalEvent<BodyPartComponent, EntRemovedFromContainerMessage>(OnBodyPartRemoved);
  21. }
  22. private void OnBodyPartInserted(Entity<BodyPartComponent> ent, ref EntInsertedIntoContainerMessage args)
  23. {
  24. // Body part inserted into another body part.
  25. var insertedUid = args.Entity;
  26. var slotId = args.Container.ID;
  27. if (ent.Comp.Body is null)
  28. return;
  29. if (TryComp(insertedUid, out BodyPartComponent? part))
  30. {
  31. AddPart(ent.Comp.Body.Value, (insertedUid, part), slotId);
  32. RecursiveBodyUpdate((insertedUid, part), ent.Comp.Body.Value);
  33. }
  34. if (TryComp(insertedUid, out OrganComponent? organ))
  35. AddOrgan((insertedUid, organ), ent.Comp.Body.Value, ent);
  36. }
  37. private void OnBodyPartRemoved(Entity<BodyPartComponent> ent, ref EntRemovedFromContainerMessage args)
  38. {
  39. // Body part removed from another body part.
  40. var removedUid = args.Entity;
  41. var slotId = args.Container.ID;
  42. DebugTools.Assert(!TryComp(removedUid, out BodyPartComponent? b) || b.Body == ent.Comp.Body);
  43. DebugTools.Assert(!TryComp(removedUid, out OrganComponent? o) || o.Body == ent.Comp.Body);
  44. if (TryComp(removedUid, out BodyPartComponent? part) && part.Body is not null)
  45. {
  46. RemovePart(part.Body.Value, (removedUid, part), slotId);
  47. RecursiveBodyUpdate((removedUid, part), null);
  48. }
  49. if (TryComp(removedUid, out OrganComponent? organ))
  50. RemoveOrgan((removedUid, organ), ent);
  51. }
  52. private void RecursiveBodyUpdate(Entity<BodyPartComponent> ent, EntityUid? bodyUid)
  53. {
  54. ent.Comp.Body = bodyUid;
  55. Dirty(ent, ent.Comp);
  56. foreach (var slotId in ent.Comp.Organs.Keys)
  57. {
  58. if (!Containers.TryGetContainer(ent, GetOrganContainerId(slotId), out var container))
  59. continue;
  60. foreach (var organ in container.ContainedEntities)
  61. {
  62. if (!TryComp(organ, out OrganComponent? organComp))
  63. continue;
  64. Dirty(organ, organComp);
  65. if (organComp.Body is { Valid: true } oldBodyUid)
  66. {
  67. var removedEv = new OrganRemovedFromBodyEvent(oldBodyUid, ent);
  68. RaiseLocalEvent(organ, ref removedEv);
  69. }
  70. organComp.Body = bodyUid;
  71. if (bodyUid is not null)
  72. {
  73. var addedEv = new OrganAddedToBodyEvent(bodyUid.Value, ent);
  74. RaiseLocalEvent(organ, ref addedEv);
  75. }
  76. }
  77. }
  78. foreach (var slotId in ent.Comp.Children.Keys)
  79. {
  80. if (!Containers.TryGetContainer(ent, GetPartSlotContainerId(slotId), out var container))
  81. continue;
  82. foreach (var containedUid in container.ContainedEntities)
  83. {
  84. if (TryComp(containedUid, out BodyPartComponent? childPart))
  85. RecursiveBodyUpdate((containedUid, childPart), bodyUid);
  86. }
  87. }
  88. }
  89. protected virtual void AddPart(
  90. Entity<BodyComponent?> bodyEnt,
  91. Entity<BodyPartComponent> partEnt,
  92. string slotId)
  93. {
  94. Dirty(partEnt, partEnt.Comp);
  95. partEnt.Comp.Body = bodyEnt;
  96. var ev = new BodyPartAddedEvent(slotId, partEnt);
  97. RaiseLocalEvent(bodyEnt, ref ev);
  98. AddLeg(partEnt, bodyEnt);
  99. }
  100. protected virtual void RemovePart(
  101. Entity<BodyComponent?> bodyEnt,
  102. Entity<BodyPartComponent> partEnt,
  103. string slotId)
  104. {
  105. Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false);
  106. Dirty(partEnt, partEnt.Comp);
  107. partEnt.Comp.Body = null;
  108. var ev = new BodyPartRemovedEvent(slotId, partEnt);
  109. RaiseLocalEvent(bodyEnt, ref ev);
  110. RemoveLeg(partEnt, bodyEnt);
  111. PartRemoveDamage(bodyEnt, partEnt);
  112. }
  113. private void AddLeg(Entity<BodyPartComponent> legEnt, Entity<BodyComponent?> bodyEnt)
  114. {
  115. if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false))
  116. return;
  117. if (legEnt.Comp.PartType == BodyPartType.Leg)
  118. {
  119. bodyEnt.Comp.LegEntities.Add(legEnt);
  120. UpdateMovementSpeed(bodyEnt);
  121. Dirty(bodyEnt, bodyEnt.Comp);
  122. }
  123. }
  124. private void RemoveLeg(Entity<BodyPartComponent> legEnt, Entity<BodyComponent?> bodyEnt)
  125. {
  126. if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false))
  127. return;
  128. if (legEnt.Comp.PartType == BodyPartType.Leg)
  129. {
  130. bodyEnt.Comp.LegEntities.Remove(legEnt);
  131. UpdateMovementSpeed(bodyEnt);
  132. Dirty(bodyEnt, bodyEnt.Comp);
  133. if (!bodyEnt.Comp.LegEntities.Any())
  134. {
  135. Standing.Down(bodyEnt);
  136. }
  137. }
  138. }
  139. private void PartRemoveDamage(Entity<BodyComponent?> bodyEnt, Entity<BodyPartComponent> partEnt)
  140. {
  141. if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false))
  142. return;
  143. if (!_timing.ApplyingState
  144. && partEnt.Comp.IsVital
  145. && !GetBodyChildrenOfType(bodyEnt, partEnt.Comp.PartType, bodyEnt.Comp).Any()
  146. )
  147. {
  148. // TODO BODY SYSTEM KILL : remove this when wounding and required parts are implemented properly
  149. var damage = new DamageSpecifier(Prototypes.Index<DamageTypePrototype>("Bloodloss"), 300);
  150. Damageable.TryChangeDamage(bodyEnt, damage);
  151. }
  152. }
  153. /// <summary>
  154. /// Tries to get the parent body part to this if applicable.
  155. /// Doesn't validate if it's a part of body system.
  156. /// </summary>
  157. public EntityUid? GetParentPartOrNull(EntityUid uid)
  158. {
  159. if (!Containers.TryGetContainingContainer((uid, null, null), out var container))
  160. return null;
  161. var parent = container.Owner;
  162. if (!HasComp<BodyPartComponent>(parent))
  163. return null;
  164. return parent;
  165. }
  166. /// <summary>
  167. /// Tries to get the parent body part and slot to this if applicable.
  168. /// </summary>
  169. public (EntityUid Parent, string Slot)? GetParentPartAndSlotOrNull(EntityUid uid)
  170. {
  171. if (!Containers.TryGetContainingContainer((uid, null, null), out var container))
  172. return null;
  173. var slotId = GetPartSlotContainerIdFromContainer(container.ID);
  174. if (string.IsNullOrEmpty(slotId))
  175. return null;
  176. var parent = container.Owner;
  177. if (!TryComp<BodyPartComponent>(parent, out var parentBody)
  178. || !parentBody.Children.ContainsKey(slotId))
  179. return null;
  180. return (parent, slotId);
  181. }
  182. /// <summary>
  183. /// Tries to get the relevant parent body part to this if it exists.
  184. /// It won't exist if this is the root body part or if it's not in a body.
  185. /// </summary>
  186. public bool TryGetParentBodyPart(
  187. EntityUid partUid,
  188. [NotNullWhen(true)] out EntityUid? parentUid,
  189. [NotNullWhen(true)] out BodyPartComponent? parentComponent)
  190. {
  191. DebugTools.Assert(HasComp<BodyPartComponent>(partUid));
  192. parentUid = null;
  193. parentComponent = null;
  194. if (Containers.TryGetContainingContainer((partUid, null, null), out var container) &&
  195. TryComp(container.Owner, out parentComponent))
  196. {
  197. parentUid = container.Owner;
  198. return true;
  199. }
  200. return false;
  201. }
  202. #region Slots
  203. /// <summary>
  204. /// Creates a BodyPartSlot on the specified partUid.
  205. /// </summary>
  206. private BodyPartSlot? CreatePartSlot(
  207. EntityUid partUid,
  208. string slotId,
  209. BodyPartType partType,
  210. BodyPartComponent? part = null)
  211. {
  212. if (!Resolve(partUid, ref part, logMissing: false))
  213. return null;
  214. Containers.EnsureContainer<ContainerSlot>(partUid, GetPartSlotContainerId(slotId));
  215. var partSlot = new BodyPartSlot(slotId, partType);
  216. part.Children.Add(slotId, partSlot);
  217. Dirty(partUid, part);
  218. return partSlot;
  219. }
  220. /// <summary>
  221. /// Tries to create a BodyPartSlot on the specified partUid.
  222. /// </summary>
  223. /// <returns>false if not relevant or can't add it.</returns>
  224. public bool TryCreatePartSlot(
  225. EntityUid? partId,
  226. string slotId,
  227. BodyPartType partType,
  228. [NotNullWhen(true)] out BodyPartSlot? slot,
  229. BodyPartComponent? part = null)
  230. {
  231. slot = null;
  232. if (partId is null
  233. || !Resolve(partId.Value, ref part, logMissing: false))
  234. {
  235. return false;
  236. }
  237. Containers.EnsureContainer<ContainerSlot>(partId.Value, GetPartSlotContainerId(slotId));
  238. slot = new BodyPartSlot(slotId, partType);
  239. if (!part.Children.TryAdd(slotId, slot.Value))
  240. return false;
  241. Dirty(partId.Value, part);
  242. return true;
  243. }
  244. public bool TryCreatePartSlotAndAttach(
  245. EntityUid parentId,
  246. string slotId,
  247. EntityUid childId,
  248. BodyPartType partType,
  249. BodyPartComponent? parent = null,
  250. BodyPartComponent? child = null)
  251. {
  252. return TryCreatePartSlot(parentId, slotId, partType, out _, parent)
  253. && AttachPart(parentId, slotId, childId, parent, child);
  254. }
  255. #endregion
  256. #region RootPartManagement
  257. /// <summary>
  258. /// Returns true if the partId is the root body container for the specified bodyId.
  259. /// </summary>
  260. public bool IsPartRoot(
  261. EntityUid bodyId,
  262. EntityUid partId,
  263. BodyComponent? body = null,
  264. BodyPartComponent? part = null)
  265. {
  266. return Resolve(partId, ref part)
  267. && Resolve(bodyId, ref body)
  268. && Containers.TryGetContainingContainer(bodyId, partId, out var container)
  269. && container.ID == BodyRootContainerId;
  270. }
  271. /// <summary>
  272. /// Returns true if we can attach the partId to the bodyId as the root entity.
  273. /// </summary>
  274. public bool CanAttachToRoot(
  275. EntityUid bodyId,
  276. EntityUid partId,
  277. BodyComponent? body = null,
  278. BodyPartComponent? part = null)
  279. {
  280. return Resolve(bodyId, ref body)
  281. && Resolve(partId, ref part)
  282. && body.RootContainer.ContainedEntity is null
  283. && bodyId != part.Body;
  284. }
  285. /// <summary>
  286. /// Returns the root part of this body if it exists.
  287. /// </summary>
  288. public (EntityUid Entity, BodyPartComponent BodyPart)? GetRootPartOrNull(EntityUid bodyId, BodyComponent? body = null)
  289. {
  290. if (!Resolve(bodyId, ref body)
  291. || body.RootContainer.ContainedEntity is null)
  292. {
  293. return null;
  294. }
  295. return (body.RootContainer.ContainedEntity.Value,
  296. Comp<BodyPartComponent>(body.RootContainer.ContainedEntity.Value));
  297. }
  298. /// <summary>
  299. /// Returns true if the partId can be attached to the parentId in the specified slot.
  300. /// </summary>
  301. public bool CanAttachPart(
  302. EntityUid parentId,
  303. BodyPartSlot slot,
  304. EntityUid partId,
  305. BodyPartComponent? parentPart = null,
  306. BodyPartComponent? part = null)
  307. {
  308. return Resolve(partId, ref part, logMissing: false)
  309. && Resolve(parentId, ref parentPart, logMissing: false)
  310. && CanAttachPart(parentId, slot.Id, partId, parentPart, part);
  311. }
  312. /// <summary>
  313. /// Returns true if we can attach the specified partId to the parentId in the specified slot.
  314. /// </summary>
  315. public bool CanAttachPart(
  316. EntityUid parentId,
  317. string slotId,
  318. EntityUid partId,
  319. BodyPartComponent? parentPart = null,
  320. BodyPartComponent? part = null)
  321. {
  322. return Resolve(partId, ref part, logMissing: false)
  323. && Resolve(parentId, ref parentPart, logMissing: false)
  324. && parentPart.Children.TryGetValue(slotId, out var parentSlotData)
  325. && part.PartType == parentSlotData.Type
  326. && Containers.TryGetContainer(parentId, GetPartSlotContainerId(slotId), out var container)
  327. && Containers.CanInsert(partId, container);
  328. }
  329. public bool AttachPartToRoot(
  330. EntityUid bodyId,
  331. EntityUid partId,
  332. BodyComponent? body = null,
  333. BodyPartComponent? part = null)
  334. {
  335. return Resolve(bodyId, ref body)
  336. && Resolve(partId, ref part)
  337. && CanAttachToRoot(bodyId, partId, body, part)
  338. && Containers.Insert(partId, body.RootContainer);
  339. }
  340. #endregion
  341. #region Attach/Detach
  342. /// <summary>
  343. /// Attaches a body part to the specified body part parent.
  344. /// </summary>
  345. public bool AttachPart(
  346. EntityUid parentPartId,
  347. string slotId,
  348. EntityUid partId,
  349. BodyPartComponent? parentPart = null,
  350. BodyPartComponent? part = null)
  351. {
  352. return Resolve(parentPartId, ref parentPart, logMissing: false)
  353. && parentPart.Children.TryGetValue(slotId, out var slot)
  354. && AttachPart(parentPartId, slot, partId, parentPart, part);
  355. }
  356. /// <summary>
  357. /// Attaches a body part to the specified body part parent.
  358. /// </summary>
  359. public bool AttachPart(
  360. EntityUid parentPartId,
  361. BodyPartSlot slot,
  362. EntityUid partId,
  363. BodyPartComponent? parentPart = null,
  364. BodyPartComponent? part = null)
  365. {
  366. if (!Resolve(parentPartId, ref parentPart, logMissing: false)
  367. || !Resolve(partId, ref part, logMissing: false)
  368. || !CanAttachPart(parentPartId, slot.Id, partId, parentPart, part)
  369. || !parentPart.Children.ContainsKey(slot.Id))
  370. {
  371. return false;
  372. }
  373. if (!Containers.TryGetContainer(parentPartId, GetPartSlotContainerId(slot.Id), out var container))
  374. {
  375. DebugTools.Assert($"Unable to find body slot {slot.Id} for {ToPrettyString(parentPartId)}");
  376. return false;
  377. }
  378. return Containers.Insert(partId, container);
  379. }
  380. #endregion
  381. #region Misc
  382. public void UpdateMovementSpeed(
  383. EntityUid bodyId,
  384. BodyComponent? body = null,
  385. MovementSpeedModifierComponent? movement = null)
  386. {
  387. if (!Resolve(bodyId, ref body, ref movement, logMissing: false)
  388. || body.RequiredLegs <= 0)
  389. {
  390. return;
  391. }
  392. var walkSpeed = 0f;
  393. var sprintSpeed = 0f;
  394. var acceleration = 0f;
  395. foreach (var legEntity in body.LegEntities)
  396. {
  397. if (!TryComp<MovementBodyPartComponent>(legEntity, out var legModifier))
  398. continue;
  399. walkSpeed += legModifier.WalkSpeed;
  400. sprintSpeed += legModifier.SprintSpeed;
  401. acceleration += legModifier.Acceleration;
  402. }
  403. walkSpeed /= body.RequiredLegs;
  404. sprintSpeed /= body.RequiredLegs;
  405. acceleration /= body.RequiredLegs;
  406. Movement.ChangeBaseSpeed(bodyId, walkSpeed, sprintSpeed, acceleration, movement);
  407. }
  408. #endregion
  409. #region Queries
  410. /// <summary>
  411. /// Get all organs for the specified body part.
  412. /// </summary>
  413. public IEnumerable<(EntityUid Id, OrganComponent Component)> GetPartOrgans(EntityUid partId, BodyPartComponent? part = null)
  414. {
  415. if (!Resolve(partId, ref part, logMissing: false))
  416. yield break;
  417. foreach (var slotId in part.Organs.Keys)
  418. {
  419. var containerSlotId = GetOrganContainerId(slotId);
  420. if (!Containers.TryGetContainer(partId, containerSlotId, out var container))
  421. continue;
  422. foreach (var containedEnt in container.ContainedEntities)
  423. {
  424. if (!TryComp(containedEnt, out OrganComponent? organ))
  425. continue;
  426. yield return (containedEnt, organ);
  427. }
  428. }
  429. }
  430. /// <summary>
  431. /// Gets all BaseContainers for body parts on this entity and its child entities.
  432. /// </summary>
  433. public IEnumerable<BaseContainer> GetPartContainers(EntityUid id, BodyPartComponent? part = null)
  434. {
  435. if (!Resolve(id, ref part, logMissing: false) ||
  436. part.Children.Count == 0)
  437. {
  438. yield break;
  439. }
  440. foreach (var slotId in part.Children.Keys)
  441. {
  442. var containerSlotId = GetPartSlotContainerId(slotId);
  443. if (!Containers.TryGetContainer(id, containerSlotId, out var container))
  444. continue;
  445. yield return container;
  446. foreach (var ent in container.ContainedEntities)
  447. {
  448. foreach (var childContainer in GetPartContainers(ent))
  449. {
  450. yield return childContainer;
  451. }
  452. }
  453. }
  454. }
  455. /// <summary>
  456. /// Returns all body part components for this entity including itself.
  457. /// </summary>
  458. public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyPartChildren(
  459. EntityUid partId,
  460. BodyPartComponent? part = null)
  461. {
  462. if (!Resolve(partId, ref part, logMissing: false))
  463. yield break;
  464. yield return (partId, part);
  465. foreach (var slotId in part.Children.Keys)
  466. {
  467. var containerSlotId = GetPartSlotContainerId(slotId);
  468. if (Containers.TryGetContainer(partId, containerSlotId, out var container))
  469. {
  470. foreach (var containedEnt in container.ContainedEntities)
  471. {
  472. if (!TryComp(containedEnt, out BodyPartComponent? childPart))
  473. continue;
  474. foreach (var value in GetBodyPartChildren(containedEnt, childPart))
  475. {
  476. yield return value;
  477. }
  478. }
  479. }
  480. }
  481. }
  482. /// <summary>
  483. /// Returns all body part slots for this entity.
  484. /// </summary>
  485. public IEnumerable<BodyPartSlot> GetAllBodyPartSlots(
  486. EntityUid partId,
  487. BodyPartComponent? part = null)
  488. {
  489. if (!Resolve(partId, ref part, logMissing: false))
  490. yield break;
  491. foreach (var (slotId, slot) in part.Children)
  492. {
  493. yield return slot;
  494. var containerSlotId = GetOrganContainerId(slotId);
  495. if (Containers.TryGetContainer(partId, containerSlotId, out var container))
  496. {
  497. foreach (var containedEnt in container.ContainedEntities)
  498. {
  499. if (!TryComp(containedEnt, out BodyPartComponent? childPart))
  500. continue;
  501. foreach (var subSlot in GetAllBodyPartSlots(containedEnt, childPart))
  502. {
  503. yield return subSlot;
  504. }
  505. }
  506. }
  507. }
  508. }
  509. /// <summary>
  510. /// Returns true if the bodyId has any parts of this type.
  511. /// </summary>
  512. public bool BodyHasPartType(
  513. EntityUid bodyId,
  514. BodyPartType type,
  515. BodyComponent? body = null)
  516. {
  517. return GetBodyChildrenOfType(bodyId, type, body).Any();
  518. }
  519. /// <summary>
  520. /// Returns true if the parentId has the specified childId.
  521. /// </summary>
  522. public bool PartHasChild(
  523. EntityUid parentId,
  524. EntityUid childId,
  525. BodyPartComponent? parent,
  526. BodyPartComponent? child)
  527. {
  528. if (!Resolve(parentId, ref parent, logMissing: false)
  529. || !Resolve(childId, ref child, logMissing: false))
  530. {
  531. return false;
  532. }
  533. foreach (var (foundId, _) in GetBodyPartChildren(parentId, parent))
  534. {
  535. if (foundId == childId)
  536. return true;
  537. }
  538. return false;
  539. }
  540. /// <summary>
  541. /// Returns true if the bodyId has the specified partId.
  542. /// </summary>
  543. public bool BodyHasChild(
  544. EntityUid bodyId,
  545. EntityUid partId,
  546. BodyComponent? body = null,
  547. BodyPartComponent? part = null)
  548. {
  549. return Resolve(bodyId, ref body, logMissing: false)
  550. && body.RootContainer.ContainedEntity is not null
  551. && Resolve(partId, ref part, logMissing: false)
  552. && TryComp(body.RootContainer.ContainedEntity, out BodyPartComponent? rootPart)
  553. && PartHasChild(body.RootContainer.ContainedEntity.Value, partId, rootPart, part);
  554. }
  555. public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildrenOfType(
  556. EntityUid bodyId,
  557. BodyPartType type,
  558. BodyComponent? body = null)
  559. {
  560. foreach (var part in GetBodyChildren(bodyId, body))
  561. {
  562. if (part.Component.PartType == type)
  563. yield return part;
  564. }
  565. }
  566. /// <summary>
  567. /// Returns a list of ValueTuples of <see cref="T"/> and OrganComponent on each organ
  568. /// in the given part.
  569. /// </summary>
  570. /// <param name="uid">The part entity id to check on.</param>
  571. /// <param name="part">The part to check for organs on.</param>
  572. /// <typeparam name="T">The component to check for.</typeparam>
  573. public List<(T Comp, OrganComponent Organ)> GetBodyPartOrganComponents<T>(
  574. EntityUid uid,
  575. BodyPartComponent? part = null)
  576. where T : IComponent
  577. {
  578. if (!Resolve(uid, ref part))
  579. return new List<(T Comp, OrganComponent Organ)>();
  580. var query = GetEntityQuery<T>();
  581. var list = new List<(T Comp, OrganComponent Organ)>();
  582. foreach (var organ in GetPartOrgans(uid, part))
  583. {
  584. if (query.TryGetComponent(organ.Id, out var comp))
  585. list.Add((comp, organ.Component));
  586. }
  587. return list;
  588. }
  589. /// <summary>
  590. /// Tries to get a list of ValueTuples of <see cref="T"/> and OrganComponent on each organs
  591. /// in the given part.
  592. /// </summary>
  593. /// <param name="uid">The part entity id to check on.</param>
  594. /// <param name="comps">The list of components.</param>
  595. /// <param name="part">The part to check for organs on.</param>
  596. /// <typeparam name="T">The component to check for.</typeparam>
  597. /// <returns>Whether any were found.</returns>
  598. public bool TryGetBodyPartOrganComponents<T>(
  599. EntityUid uid,
  600. [NotNullWhen(true)] out List<(T Comp, OrganComponent Organ)>? comps,
  601. BodyPartComponent? part = null)
  602. where T : IComponent
  603. {
  604. if (!Resolve(uid, ref part))
  605. {
  606. comps = null;
  607. return false;
  608. }
  609. comps = GetBodyPartOrganComponents<T>(uid, part);
  610. if (comps.Count != 0)
  611. return true;
  612. comps = null;
  613. return false;
  614. }
  615. /// <summary>
  616. /// Gets the parent body part and all immediate child body parts for the partId.
  617. /// </summary>
  618. public IEnumerable<EntityUid> GetBodyPartAdjacentParts(
  619. EntityUid partId,
  620. BodyPartComponent? part = null)
  621. {
  622. if (!Resolve(partId, ref part, logMissing: false))
  623. yield break;
  624. if (TryGetParentBodyPart(partId, out var parentUid, out _))
  625. yield return parentUid.Value;
  626. foreach (var slotId in part.Children.Keys)
  627. {
  628. var container = Containers.GetContainer(partId, GetPartSlotContainerId(slotId));
  629. foreach (var containedEnt in container.ContainedEntities)
  630. {
  631. yield return containedEnt;
  632. }
  633. }
  634. }
  635. public IEnumerable<(EntityUid AdjacentId, T Component)> GetBodyPartAdjacentPartsComponents<T>(
  636. EntityUid partId,
  637. BodyPartComponent? part = null)
  638. where T : IComponent
  639. {
  640. if (!Resolve(partId, ref part, logMissing: false))
  641. yield break;
  642. var query = GetEntityQuery<T>();
  643. foreach (var adjacentId in GetBodyPartAdjacentParts(partId, part))
  644. {
  645. if (query.TryGetComponent(adjacentId, out var component))
  646. yield return (adjacentId, component);
  647. }
  648. }
  649. public bool TryGetBodyPartAdjacentPartsComponents<T>(
  650. EntityUid partId,
  651. [NotNullWhen(true)] out List<(EntityUid AdjacentId, T Component)>? comps,
  652. BodyPartComponent? part = null)
  653. where T : IComponent
  654. {
  655. if (!Resolve(partId, ref part, logMissing: false))
  656. {
  657. comps = null;
  658. return false;
  659. }
  660. var query = GetEntityQuery<T>();
  661. comps = new List<(EntityUid AdjacentId, T Component)>();
  662. foreach (var adjacentId in GetBodyPartAdjacentParts(partId, part))
  663. {
  664. if (query.TryGetComponent(adjacentId, out var component))
  665. comps.Add((adjacentId, component));
  666. }
  667. if (comps.Count != 0)
  668. return true;
  669. comps = null;
  670. return false;
  671. }
  672. #endregion
  673. }