1
0

SharedObjectivesSystem.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. using System.Diagnostics.CodeAnalysis;
  2. using Content.Shared.Mind;
  3. using Content.Shared.Objectives.Components;
  4. using Robust.Shared.Prototypes;
  5. using Robust.Shared.Utility;
  6. namespace Content.Shared.Objectives.Systems;
  7. /// <summary>
  8. /// Provides API for creating and interacting with objectives.
  9. /// </summary>
  10. public abstract class SharedObjectivesSystem : EntitySystem
  11. {
  12. [Dependency] private readonly SharedMindSystem _mind = default!;
  13. [Dependency] private readonly IPrototypeManager _protoMan = default!;
  14. private EntityQuery<MetaDataComponent> _metaQuery;
  15. public override void Initialize()
  16. {
  17. base.Initialize();
  18. _metaQuery = GetEntityQuery<MetaDataComponent>();
  19. }
  20. /// <summary>
  21. /// Checks requirements and duplicate objectives to see if an objective can be assigned.
  22. /// </summary>
  23. public bool CanBeAssigned(EntityUid uid, EntityUid mindId, MindComponent mind, ObjectiveComponent? comp = null)
  24. {
  25. if (!Resolve(uid, ref comp))
  26. return false;
  27. var ev = new RequirementCheckEvent(mindId, mind);
  28. RaiseLocalEvent(uid, ref ev);
  29. if (ev.Cancelled)
  30. return false;
  31. // only check for duplicate prototypes if it's unique
  32. if (comp.Unique)
  33. {
  34. var proto = _metaQuery.GetComponent(uid).EntityPrototype?.ID;
  35. foreach (var objective in mind.Objectives)
  36. {
  37. if (_metaQuery.GetComponent(objective).EntityPrototype?.ID == proto)
  38. return false;
  39. }
  40. }
  41. return true;
  42. }
  43. /// <summary>
  44. /// Spawns and assigns an objective for a mind.
  45. /// The objective is not added to the mind's objectives, mind system does that in TryAddObjective.
  46. /// If the objective could not be assigned the objective is deleted and null is returned.
  47. /// </summary>
  48. public EntityUid? TryCreateObjective(EntityUid mindId, MindComponent mind, string proto)
  49. {
  50. if (!_protoMan.HasIndex<EntityPrototype>(proto))
  51. return null;
  52. var uid = Spawn(proto);
  53. if (!TryComp<ObjectiveComponent>(uid, out var comp))
  54. {
  55. Del(uid);
  56. Log.Error($"Invalid objective prototype {proto}, missing ObjectiveComponent");
  57. return null;
  58. }
  59. if (!CanBeAssigned(uid, mindId, mind, comp))
  60. {
  61. Log.Warning($"Objective {proto} did not match the requirements for {_mind.MindOwnerLoggingString(mind)}, deleted it");
  62. return null;
  63. }
  64. var ev = new ObjectiveAssignedEvent(mindId, mind);
  65. RaiseLocalEvent(uid, ref ev);
  66. if (ev.Cancelled)
  67. {
  68. Del(uid);
  69. Log.Warning($"Could not assign objective {proto}, deleted it");
  70. return null;
  71. }
  72. // let the title description and icon be set by systems
  73. var afterEv = new ObjectiveAfterAssignEvent(mindId, mind, comp, MetaData(uid));
  74. RaiseLocalEvent(uid, ref afterEv);
  75. Log.Debug($"Created objective {ToPrettyString(uid):objective}");
  76. return uid;
  77. }
  78. /// <summary>
  79. /// Spawns and assigns an objective for a mind.
  80. /// The objective is not added to the mind's objectives, mind system does that in TryAddObjective.
  81. /// If the objective could not be assigned the objective is deleted and false is returned.
  82. /// </summary>
  83. public bool TryCreateObjective(Entity<MindComponent> mind, EntProtoId proto, [NotNullWhen(true)] out EntityUid? objective)
  84. {
  85. objective = TryCreateObjective(mind.Owner, mind.Comp, proto);
  86. return objective != null;
  87. }
  88. /// <summary>
  89. /// Get the title, description, icon and progress of an objective using <see cref="ObjectiveGetInfoEvent"/>.
  90. /// If any of them are null it is logged and null is returned.
  91. /// </summary>
  92. /// <param name="uid"/>ID of the condition entity</param>
  93. /// <param name="mindId"/>ID of the player's mind entity</param>
  94. /// <param name="mind"/>Mind component of the player's mind</param>
  95. public ObjectiveInfo? GetInfo(EntityUid uid, EntityUid mindId, MindComponent? mind = null)
  96. {
  97. if (!Resolve(mindId, ref mind))
  98. return null;
  99. if (GetProgress(uid, (mindId, mind)) is not {} progress)
  100. return null;
  101. var comp = Comp<ObjectiveComponent>(uid);
  102. var meta = MetaData(uid);
  103. var title = meta.EntityName;
  104. var description = meta.EntityDescription;
  105. if (comp.Icon == null)
  106. {
  107. Log.Error($"An objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind)} is missing an icon!");
  108. return null;
  109. }
  110. return new ObjectiveInfo(title, description, comp.Icon, progress);
  111. }
  112. /// <summary>
  113. /// Gets the progress of an objective using <see cref="ObjectiveGetProgressEvent"/>.
  114. /// Returning null is a programmer error.
  115. /// </summary>
  116. public float? GetProgress(EntityUid uid, Entity<MindComponent> mind)
  117. {
  118. var ev = new ObjectiveGetProgressEvent(mind, mind.Comp);
  119. RaiseLocalEvent(uid, ref ev);
  120. if (ev.Progress != null)
  121. return ev.Progress;
  122. Log.Error($"Objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind.Comp)} didn't set a progress value!");
  123. return null;
  124. }
  125. /// <summary>
  126. /// Returns true if an objective is completed.
  127. /// </summary>
  128. public bool IsCompleted(EntityUid uid, Entity<MindComponent> mind)
  129. {
  130. return (GetProgress(uid, mind) ?? 0f) >= 0.999f;
  131. }
  132. /// <summary>
  133. /// Sets the objective's icon to the one specified.
  134. /// Intended for <see cref="ObjectiveAfterAssignEvent"/> handlers to set an icon.
  135. /// </summary>
  136. public void SetIcon(EntityUid uid, SpriteSpecifier icon, ObjectiveComponent? comp = null)
  137. {
  138. if (!Resolve(uid, ref comp))
  139. return;
  140. comp.Icon = icon;
  141. }
  142. }