1
0

LoadoutSystem.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. using System.Linq;
  2. using Content.Shared.Body.Systems;
  3. using Content.Shared.Clothing.Components;
  4. using Content.Shared.Humanoid;
  5. using Content.Shared.Preferences;
  6. using Content.Shared.Preferences.Loadouts;
  7. using Content.Shared.Roles;
  8. using Content.Shared.Station;
  9. using Robust.Shared.Player;
  10. using Robust.Shared.Prototypes;
  11. using Robust.Shared.Random;
  12. namespace Content.Shared.Clothing;
  13. /// <summary>
  14. /// Assigns a loadout to an entity based on the RoleLoadout prototype
  15. /// </summary>
  16. public sealed class LoadoutSystem : EntitySystem
  17. {
  18. // Shared so we can predict it for placement manager.
  19. [Dependency] private readonly ActorSystem _actors = default!;
  20. [Dependency] private readonly SharedStationSpawningSystem _station = default!;
  21. [Dependency] private readonly IPrototypeManager _protoMan = default!;
  22. [Dependency] private readonly IRobustRandom _random = default!;
  23. public override void Initialize()
  24. {
  25. base.Initialize();
  26. // Wait until the character has all their organs before we give them their loadout
  27. SubscribeLocalEvent<LoadoutComponent, MapInitEvent>(OnMapInit, after: [typeof(SharedBodySystem)]);
  28. }
  29. public static string GetJobPrototype(string? loadout)
  30. {
  31. if (string.IsNullOrEmpty(loadout))
  32. return string.Empty;
  33. return "Job" + loadout;
  34. }
  35. public EntProtoId? GetFirstOrNull(LoadoutPrototype loadout)
  36. {
  37. EntProtoId? proto = null;
  38. if (_protoMan.TryIndex(loadout.StartingGear, out var gear))
  39. {
  40. proto = GetFirstOrNull(gear);
  41. }
  42. proto ??= GetFirstOrNull((IEquipmentLoadout)loadout);
  43. return proto;
  44. }
  45. /// <summary>
  46. /// Tries to get the first entity prototype for operations such as sprite drawing.
  47. /// </summary>
  48. public EntProtoId? GetFirstOrNull(IEquipmentLoadout? gear)
  49. {
  50. if (gear == null)
  51. return null;
  52. var count = gear.Equipment.Count + gear.Inhand.Count + gear.Storage.Values.Sum(x => x.Count);
  53. if (count == 1)
  54. {
  55. if (gear.Equipment.Count == 1 && _protoMan.TryIndex<EntityPrototype>(gear.Equipment.Values.First(), out var proto))
  56. {
  57. return proto.ID;
  58. }
  59. if (gear.Inhand.Count == 1 && _protoMan.TryIndex<EntityPrototype>(gear.Inhand[0], out proto))
  60. {
  61. return proto.ID;
  62. }
  63. // Storage moment
  64. foreach (var ents in gear.Storage.Values)
  65. {
  66. foreach (var ent in ents)
  67. {
  68. return ent;
  69. }
  70. }
  71. }
  72. return null;
  73. }
  74. public string GetName(LoadoutPrototype loadout)
  75. {
  76. if (loadout.DummyEntity is not null && _protoMan.TryIndex<EntityPrototype>(loadout.DummyEntity, out var proto))
  77. return proto.Name;
  78. if (_protoMan.TryIndex(loadout.StartingGear, out var gear))
  79. {
  80. return GetName(gear);
  81. }
  82. return GetName((IEquipmentLoadout) loadout);
  83. }
  84. /// <summary>
  85. /// Tries to get the name of a loadout.
  86. /// </summary>
  87. public string GetName(IEquipmentLoadout? gear)
  88. {
  89. if (gear == null)
  90. return string.Empty;
  91. var count = gear.Equipment.Count + gear.Storage.Values.Sum(o => o.Count) + gear.Inhand.Count;
  92. if (count == 1)
  93. {
  94. if (gear.Equipment.Count == 1 && _protoMan.TryIndex<EntityPrototype>(gear.Equipment.Values.First(), out var proto))
  95. {
  96. return proto.Name;
  97. }
  98. if (gear.Inhand.Count == 1 && _protoMan.TryIndex<EntityPrototype>(gear.Inhand[0], out proto))
  99. {
  100. return proto.Name;
  101. }
  102. foreach (var values in gear.Storage.Values)
  103. {
  104. if (values.Count != 1)
  105. continue;
  106. if (_protoMan.TryIndex<EntityPrototype>(values[0], out proto))
  107. {
  108. return proto.Name;
  109. }
  110. break;
  111. }
  112. }
  113. return Loc.GetString($"unknown");
  114. }
  115. private void OnMapInit(EntityUid uid, LoadoutComponent component, MapInitEvent args)
  116. {
  117. Equip(uid, component.StartingGear, component.RoleLoadout);
  118. }
  119. public void Equip(EntityUid uid, List<ProtoId<StartingGearPrototype>>? startingGear,
  120. List<ProtoId<RoleLoadoutPrototype>>? loadoutGroups)
  121. {
  122. // First, randomly pick a startingGear profile from those specified, and equip it.
  123. if (startingGear != null && startingGear.Count > 0)
  124. _station.EquipStartingGear(uid, _random.Pick(startingGear));
  125. if (loadoutGroups == null)
  126. {
  127. GearEquipped(uid);
  128. return;
  129. }
  130. // Then, randomly pick a RoleLoadout profile from those specified, and process/equip all LoadoutGroups from it.
  131. // For non-roundstart mobs there is no SelectedLoadout data, so minValue must be set in each LoadoutGroup to force selection.
  132. var id = _random.Pick(loadoutGroups);
  133. var proto = _protoMan.Index(id);
  134. var loadout = new RoleLoadout(id);
  135. loadout.SetDefault(GetProfile(uid), _actors.GetSession(uid), _protoMan, true);
  136. _station.EquipRoleLoadout(uid, loadout, proto);
  137. GearEquipped(uid);
  138. }
  139. public void GearEquipped(EntityUid uid)
  140. {
  141. var ev = new StartingGearEquippedEvent(uid);
  142. RaiseLocalEvent(uid, ref ev);
  143. }
  144. public HumanoidCharacterProfile GetProfile(EntityUid? uid)
  145. {
  146. if (TryComp(uid, out HumanoidAppearanceComponent? appearance))
  147. {
  148. return HumanoidCharacterProfile.DefaultWithSpecies(appearance.Species);
  149. }
  150. return HumanoidCharacterProfile.Random();
  151. }
  152. }