1
0

ConstructionSystem.Interactions.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. using System.Linq;
  2. using Content.Server.Administration.Logs;
  3. using Content.Server.Construction.Components;
  4. using Content.Server.Temperature.Components;
  5. using Content.Shared.Construction;
  6. using Content.Shared.Construction.Components;
  7. using Content.Shared.Construction.EntitySystems;
  8. using Content.Shared.Construction.Steps;
  9. using Content.Shared.DoAfter;
  10. using Content.Shared.Interaction;
  11. using Content.Shared.Prying.Systems;
  12. using Content.Shared.Radio.EntitySystems;
  13. using Content.Shared.Temperature;
  14. using Content.Shared.Tools.Systems;
  15. using Robust.Shared.Containers;
  16. using Robust.Shared.Utility;
  17. #if EXCEPTION_TOLERANCE
  18. // ReSharper disable once RedundantUsingDirective
  19. using Robust.Shared.Exceptions;
  20. #endif
  21. namespace Content.Server.Construction
  22. {
  23. public sealed partial class ConstructionSystem
  24. {
  25. [Dependency] private readonly IAdminLogManager _adminLogger = default!;
  26. #if EXCEPTION_TOLERANCE
  27. [Dependency] private readonly IRuntimeLog _runtimeLog = default!;
  28. #endif
  29. private readonly Queue<EntityUid> _constructionUpdateQueue = new();
  30. private readonly HashSet<EntityUid> _queuedUpdates = new();
  31. private void InitializeInteractions()
  32. {
  33. SubscribeLocalEvent<ConstructionComponent, ConstructionInteractDoAfterEvent>(EnqueueEvent);
  34. // Event handling. Add your subscriptions here! Just make sure they're all handled by EnqueueEvent.
  35. SubscribeLocalEvent<ConstructionComponent, InteractUsingEvent>(EnqueueEvent,
  36. new[] { typeof(AnchorableSystem), typeof(PryingSystem), typeof(WeldableSystem) },
  37. new[] { typeof(EncryptionKeySystem) });
  38. SubscribeLocalEvent<ConstructionComponent, OnTemperatureChangeEvent>(EnqueueEvent);
  39. SubscribeLocalEvent<ConstructionComponent, PartAssemblyPartInsertedEvent>(EnqueueEvent);
  40. }
  41. /// <summary>
  42. /// Takes in an entity with <see cref="ConstructionComponent"/> and an object event, and handles any
  43. /// possible construction interactions, depending on the construction's state.
  44. /// </summary>
  45. /// <remarks>When <see cref="validation"/> is true, this method will simply return whether the interaction
  46. /// would be handled by the entity or not. It essentially becomes a pure method that modifies nothing.</remarks>
  47. /// <returns>The result of this interaction with the entity.</returns>
  48. private HandleResult HandleEvent(EntityUid uid, object ev, bool validation, ConstructionComponent? construction = null)
  49. {
  50. if (!Resolve(uid, ref construction))
  51. return HandleResult.False;
  52. // If the state machine is in an invalid state (not on a valid node) we can't do anything, ever.
  53. if (GetCurrentNode(uid, construction) is not { } node)
  54. {
  55. return HandleResult.False;
  56. }
  57. // If we're currently in an edge, we'll let the edge handle or validate the interaction.
  58. if (GetCurrentEdge(uid, construction) is { } edge)
  59. {
  60. var result = HandleEdge(uid, ev, edge, validation, construction);
  61. // Reset edge index to none if this failed...
  62. if (!validation && result is HandleResult.False && construction.StepIndex == 0)
  63. construction.EdgeIndex = null;
  64. return result;
  65. }
  66. // If we're not on an edge, let the node handle or validate the interaction.
  67. return HandleNode(uid, ev, node, validation, construction);
  68. }
  69. /// <summary>
  70. /// Takes in an entity, a <see cref="ConstructionGraphNode"/> and an object event, and handles any
  71. /// possible construction interactions. This will check the interaction against all possible edges,
  72. /// and if any of the edges accepts the interaction, we will enter it.
  73. /// </summary>
  74. /// <remarks>When <see cref="validation"/> is true, this method will simply return whether the interaction
  75. /// would be handled by the entity or not. It essentially becomes a pure method that modifies nothing.</remarks>
  76. /// <returns>The result of this interaction with the entity.</returns>
  77. private HandleResult HandleNode(EntityUid uid, object ev, ConstructionGraphNode node, bool validation, ConstructionComponent? construction = null)
  78. {
  79. if (!Resolve(uid, ref construction))
  80. return HandleResult.False;
  81. // Let's make extra sure this is zero...
  82. construction.StepIndex = 0;
  83. // When we handle a node, we're essentially testing the current event interaction against all of this node's
  84. // edges' first steps. If any of them accepts the interaction, we stop iterating and enter that edge.
  85. for (var i = 0; i < node.Edges.Count; i++)
  86. {
  87. var edge = node.Edges[i];
  88. if (HandleEdge(uid, ev, edge, validation, construction) is var result and not HandleResult.False)
  89. {
  90. // Only a True result may modify the state.
  91. // In the case of DoAfter, it's only allowed to modify the waiting flag and the current edge index.
  92. // In the case of validated, it should NEVER modify the state at all.
  93. if (result is not HandleResult.True)
  94. {
  95. if (result is HandleResult.DoAfter)
  96. {
  97. construction.EdgeIndex = i;
  98. }
  99. return result;
  100. }
  101. // If we're not on the same edge as we were before, that means handling that edge changed the node.
  102. if (construction.Node != node.Name)
  103. return result;
  104. // If we're still in the same node, that means we entered the edge and it's still not done.
  105. construction.EdgeIndex = i;
  106. UpdatePathfinding(uid, construction);
  107. return result;
  108. }
  109. }
  110. return HandleResult.False;
  111. }
  112. /// <summary>
  113. /// Takes in an entity, a <see cref="ConstructionGraphEdge"/> and an object event, and handles any
  114. /// possible construction interactions. This will check the interaction against one of the steps in the edge
  115. /// depending on the construction's <see cref="ConstructionComponent.StepIndex"/>.
  116. /// </summary>
  117. /// <remarks>When <see cref="validation"/> is true, this method will simply return whether the interaction
  118. /// would be handled by the entity or not. It essentially becomes a pure method that modifies nothing.</remarks>
  119. /// <returns>The result of this interaction with the entity.</returns>
  120. private HandleResult HandleEdge(EntityUid uid, object ev, ConstructionGraphEdge edge, bool validation, ConstructionComponent? construction = null)
  121. {
  122. if (!Resolve(uid, ref construction))
  123. return HandleResult.False;
  124. var step = GetStepFromEdge(edge, construction.StepIndex);
  125. if (step == null)
  126. {
  127. Log.Warning($"Called {nameof(HandleEdge)} on entity {ToPrettyString(uid)} but the current state is not valid for that!");
  128. return HandleResult.False;
  129. }
  130. // We need to ensure we currently satisfy any and all edge conditions.
  131. if (!CheckConditions(uid, edge.Conditions))
  132. return HandleResult.False;
  133. var handle = HandleStep(uid, ev, step, validation, out var user, construction);
  134. if (handle is not HandleResult.True)
  135. return handle;
  136. // Handle step should never handle the interaction during validation.
  137. DebugTools.Assert(!validation);
  138. // We increase the step index, meaning we move to the next step!
  139. construction.StepIndex++;
  140. // Check if the new step index is greater than the amount of steps in the edge...
  141. if (construction.StepIndex >= edge.Steps.Count)
  142. {
  143. // Edge finished!
  144. PerformActions(uid, user, edge.Completed);
  145. if (construction.Deleted)
  146. return HandleResult.True;
  147. construction.TargetEdgeIndex = null;
  148. construction.EdgeIndex = null;
  149. construction.StepIndex = 0;
  150. // We change the node now.
  151. ChangeNode(uid, user, edge.Target, true, construction);
  152. }
  153. return HandleResult.True;
  154. }
  155. /// <summary>
  156. /// Takes in an entity, a <see cref="ConstructionGraphStep"/> and an object event, and handles any possible
  157. /// construction interaction. Unlike <see cref="HandleInteraction"/>, if this succeeds it will perform the
  158. /// step's completion actions. Also sets the out parameter to the user's EntityUid.
  159. /// </summary>
  160. /// <remarks>When <see cref="validation"/> is true, this method will simply return whether the interaction
  161. /// would be handled by the entity or not. It essentially becomes a pure method that modifies nothing.</remarks>
  162. /// <returns>The result of this interaction with the entity.</returns>
  163. private HandleResult HandleStep(EntityUid uid, object ev, ConstructionGraphStep step, bool validation, out EntityUid? user, ConstructionComponent? construction = null)
  164. {
  165. user = null;
  166. if (!Resolve(uid, ref construction))
  167. return HandleResult.False;
  168. // Let HandleInteraction actually handle the event for this step.
  169. // We can only perform the rest of our logic if it returns true.
  170. var handle = HandleInteraction(uid, ev, step, validation, out user, construction);
  171. if (handle is not HandleResult.True)
  172. return handle;
  173. DebugTools.Assert(!validation);
  174. // Actually perform the step completion actions, since the step was handled correctly.
  175. PerformActions(uid, user, step.Completed);
  176. UpdatePathfinding(uid, construction);
  177. return HandleResult.True;
  178. }
  179. /// <summary>
  180. /// Takes in an entity, a <see cref="ConstructionGraphStep"/> and an object event, and handles any possible
  181. /// construction interaction. Unlike <see cref="HandleStep"/>, this only handles the interaction itself
  182. /// and doesn't perform any step completion actions. Also sets the out parameter to the user's EntityUid.
  183. /// </summary>
  184. /// <remarks>When <see cref="validation"/> is true, this method will simply return whether the interaction
  185. /// would be handled by the entity or not. It essentially becomes a pure method that modifies nothing.</remarks>
  186. /// <returns>The result of this interaction with the entity.</returns>
  187. private HandleResult HandleInteraction(EntityUid uid, object ev, ConstructionGraphStep step, bool validation, out EntityUid? user, ConstructionComponent? construction = null)
  188. {
  189. user = null;
  190. if (!Resolve(uid, ref construction))
  191. return HandleResult.False;
  192. // Whether this event is being re-handled after a DoAfter or not. Check DoAfterState for more info.
  193. var doAfterState = DoAfterState.None;
  194. // The DoAfter events can only perform special logic when we're not validating events.
  195. if (ev is ConstructionInteractDoAfterEvent interactDoAfter)
  196. {
  197. if (interactDoAfter.Cancelled)
  198. return HandleResult.False;
  199. ev = new InteractUsingEvent(
  200. interactDoAfter.User,
  201. interactDoAfter.Used!.Value,
  202. uid,
  203. GetCoordinates(interactDoAfter.ClickLocation));
  204. doAfterState = DoAfterState.Completed;
  205. }
  206. // The cases in this switch will handle the interaction and return
  207. switch (step)
  208. {
  209. // --- CONSTRUCTION STEP EVENT HANDLING START ---
  210. #region Construction Step Event Handling
  211. // So you want to create your own custom step for construction?
  212. // You're looking at the right place, then! You should create
  213. // a new case for your step here, and handle it as you see fit.
  214. // Make extra sure you handle DoAfter (if applicable) properly!
  215. // Also make sure your event handler properly handles validation.
  216. // Note: Please use braces for your new case, it's convenient.
  217. case EntityInsertConstructionGraphStep insertStep:
  218. {
  219. // EntityInsert steps only work with InteractUsing!
  220. if (ev is not InteractUsingEvent interactUsing)
  221. break;
  222. // TODO: Sanity checks.
  223. user = interactUsing.User;
  224. var insert = interactUsing.Used;
  225. // Since many things inherit this step, we delegate the "is this entity valid?" logic to them.
  226. // While this is very OOP and I find it icky, I must admit that it simplifies the code here a lot.
  227. if (!insertStep.EntityValid(insert, EntityManager, _factory))
  228. return HandleResult.False;
  229. // If we're only testing whether this step would be handled by the given event, then we're done.
  230. if (validation)
  231. return HandleResult.Validated;
  232. // If we still haven't completed this step's DoAfter...
  233. if (doAfterState == DoAfterState.None && insertStep.DoAfter > 0)
  234. {
  235. var doAfterEv = new ConstructionInteractDoAfterEvent(EntityManager, interactUsing);
  236. var doAfterEventArgs = new DoAfterArgs(EntityManager, interactUsing.User, step.DoAfter, doAfterEv, uid, uid, interactUsing.Used)
  237. {
  238. BreakOnDamage = false,
  239. BreakOnMove = true,
  240. NeedHand = true,
  241. };
  242. var started = _doAfterSystem.TryStartDoAfter(doAfterEventArgs);
  243. if (!started)
  244. return HandleResult.False;
  245. #if DEBUG
  246. // Verify that the resulting DoAfter event will be handled by the current construction state.
  247. // if it can't what is even the point of raising this DoAfter?
  248. doAfterEv.DoAfter = new(default, doAfterEventArgs, default);
  249. var result = HandleInteraction(uid, doAfterEv, step, validation: true, out _, construction);
  250. DebugTools.Assert(result == HandleResult.Validated);
  251. #endif
  252. return HandleResult.DoAfter;
  253. }
  254. // Material steps, which use stacks, are handled specially. Instead of inserting the whole item,
  255. // we split the stack in two and insert the split stack.
  256. if (insertStep is MaterialConstructionGraphStep materialInsertStep)
  257. {
  258. if (_stackSystem.Split(insert, materialInsertStep.Amount, Transform(interactUsing.User).Coordinates) is not { } stack)
  259. return HandleResult.False;
  260. insert = stack;
  261. }
  262. // Container-storage handling.
  263. if (!string.IsNullOrEmpty(insertStep.Store))
  264. {
  265. // In the case we want to store this item in a container on the entity...
  266. var store = insertStep.Store;
  267. // Add this container to the collection of "construction-owned" containers.
  268. // Containers in that set will be transferred to new entities in the case of a prototype change.
  269. construction.Containers.Add(store);
  270. // The container doesn't necessarily need to exist, so we ensure it.
  271. _container.Insert(insert, _container.EnsureContainer<Container>(uid, store));
  272. }
  273. else
  274. {
  275. // If we don't store the item in a container on the entity, we just delete it right away.
  276. Del(insert);
  277. }
  278. // Step has been handled correctly, so we signal this.
  279. return HandleResult.True;
  280. }
  281. case ToolConstructionGraphStep toolInsertStep:
  282. {
  283. if (ev is not InteractUsingEvent interactUsing)
  284. break;
  285. // TODO: Sanity checks.
  286. user = interactUsing.User;
  287. // If we're validating whether this event handles the step...
  288. if (validation)
  289. {
  290. // Then we only really need to check whether the tool entity has that quality or not.
  291. return _toolSystem.HasQuality(interactUsing.Used, toolInsertStep.Tool)
  292. ? HandleResult.Validated
  293. : HandleResult.False;
  294. }
  295. // If we're handling an event after its DoAfter finished...
  296. if (doAfterState == DoAfterState.Completed)
  297. return HandleResult.True;
  298. var result = _toolSystem.UseTool(
  299. interactUsing.Used,
  300. interactUsing.User,
  301. uid,
  302. TimeSpan.FromSeconds(toolInsertStep.DoAfter),
  303. new[] { toolInsertStep.Tool },
  304. new ConstructionInteractDoAfterEvent(EntityManager, interactUsing),
  305. out var doAfter,
  306. toolInsertStep.Fuel);
  307. return result && doAfter != null ? HandleResult.DoAfter : HandleResult.False;
  308. }
  309. case TemperatureConstructionGraphStep temperatureChangeStep:
  310. {
  311. if (ev is not OnTemperatureChangeEvent)
  312. break;
  313. // Some things, like microwaves, might need to block the temperature construction step from kicking in, or override it entirely.
  314. var tempEvent = new OnConstructionTemperatureEvent();
  315. RaiseLocalEvent(uid, tempEvent, true);
  316. if (tempEvent.Result is not null)
  317. return tempEvent.Result.Value;
  318. // prefer using InternalTemperature since that's more accurate for cooking.
  319. float temp;
  320. if (TryComp<InternalTemperatureComponent>(uid, out var internalTemp))
  321. {
  322. temp = internalTemp.Temperature;
  323. }
  324. else if (TryComp<TemperatureComponent>(uid, out var tempComp))
  325. {
  326. temp = tempComp.CurrentTemperature;
  327. }
  328. else
  329. {
  330. return HandleResult.False;
  331. }
  332. if ((!temperatureChangeStep.MinTemperature.HasValue || temp >= temperatureChangeStep.MinTemperature.Value) &&
  333. (!temperatureChangeStep.MaxTemperature.HasValue || temp <= temperatureChangeStep.MaxTemperature.Value))
  334. {
  335. return HandleResult.True;
  336. }
  337. return HandleResult.False;
  338. }
  339. case PartAssemblyConstructionGraphStep partAssemblyStep:
  340. {
  341. if (ev is not PartAssemblyPartInsertedEvent)
  342. break;
  343. if (partAssemblyStep.Condition(uid, EntityManager))
  344. return HandleResult.True;
  345. return HandleResult.False;
  346. }
  347. #endregion
  348. // --- CONSTRUCTION STEP EVENT HANDLING FINISH ---
  349. default:
  350. throw new ArgumentOutOfRangeException(nameof(step),
  351. "You need to code your ConstructionGraphStep behavior by adding a case to the switch.");
  352. }
  353. // If the handlers were not able to handle this event, return...
  354. return HandleResult.False;
  355. }
  356. /// <summary>
  357. /// Checks whether a number of <see cref="IGraphCondition"/>s are true for a given entity.
  358. /// </summary>
  359. /// <param name="uid">The entity to pass to the conditions.</param>
  360. /// <param name="conditions">The conditions to evaluate.</param>
  361. /// <remarks>This method is short-circuiting; if a condition evaluates to false, we stop checking the rest of conditions.</remarks>
  362. /// <returns>Whether all conditions evaluate to true for the given entity.</returns>
  363. public bool CheckConditions(EntityUid uid, IEnumerable<IGraphCondition> conditions)
  364. {
  365. foreach (var condition in conditions)
  366. {
  367. if (!condition.Condition(uid, EntityManager))
  368. return false;
  369. }
  370. return true;
  371. }
  372. /// <summary>
  373. /// Performs a number of <see cref="IGraphAction"/>s for a given entity, with an optional user entity.
  374. /// </summary>
  375. /// <param name="uid">The entity to perform the actions on.</param>
  376. /// <param name="userUid">An optional user entity to pass into the actions.</param>
  377. /// <param name="actions">The actions to perform.</param>
  378. /// <remarks>This method checks whether the given target entity exists before performing any actions.
  379. /// If the entity is deleted by an action, it will short-circuit and stop performing the rest of actions.</remarks>
  380. public void PerformActions(EntityUid uid, EntityUid? userUid, IEnumerable<IGraphAction> actions)
  381. {
  382. foreach (var action in actions)
  383. {
  384. // If an action deletes the entity, we stop performing the rest of actions.
  385. if (!Exists(uid))
  386. break;
  387. action.PerformAction(uid, userUid, EntityManager);
  388. }
  389. }
  390. /// <summary>
  391. /// Resets the current construction edge status on an entity.
  392. /// </summary>
  393. /// <param name="uid">The target entity.</param>
  394. /// <param name="construction">The construction component. If null, it will be resolved on the entity.</param>
  395. /// <remarks>This method updates the construction pathfinding on the entity automatically.</remarks>
  396. public void ResetEdge(EntityUid uid, ConstructionComponent? construction = null)
  397. {
  398. if (!Resolve(uid, ref construction))
  399. return;
  400. construction.TargetEdgeIndex = null;
  401. construction.EdgeIndex = null;
  402. construction.StepIndex = 0;
  403. // Update pathfinding to keep it in sync with the current construction status.
  404. UpdatePathfinding(uid, construction);
  405. }
  406. private void UpdateInteractions()
  407. {
  408. // We iterate all entities waiting for their interactions to be handled.
  409. // This is much more performant than making an EntityQuery for ConstructionComponent,
  410. // since, for example, every single wall has a ConstructionComponent....
  411. while (_constructionUpdateQueue.TryDequeue(out var uid))
  412. {
  413. _queuedUpdates.Remove(uid);
  414. // Ensure the entity exists and has a Construction component.
  415. if (!TryComp(uid, out ConstructionComponent? construction))
  416. continue;
  417. #if EXCEPTION_TOLERANCE
  418. try
  419. {
  420. #endif
  421. // Handle all queued interactions!
  422. while (construction.InteractionQueue.TryDequeue(out var interaction))
  423. {
  424. if (construction.Deleted)
  425. {
  426. Log.Error($"Construction component was deleted while still processing interactions." +
  427. $"Entity {ToPrettyString(uid)}, graph: {construction.Graph}, " +
  428. $"Next: {interaction.GetType().Name}, " +
  429. $"Remaining Queue: {string.Join(", ", construction.InteractionQueue.Select(x => x.GetType().Name))}");
  430. break;
  431. }
  432. // We set validation to false because we actually want to perform the interaction here.
  433. HandleEvent(uid, interaction, false, construction);
  434. }
  435. #if EXCEPTION_TOLERANCE
  436. }
  437. catch (Exception e)
  438. {
  439. Log.Error($"Caught exception while processing construction queue. Entity {ToPrettyString(uid)}, graph: {construction.Graph}");
  440. _runtimeLog.LogException(e, $"{nameof(ConstructionSystem)}.{nameof(UpdateInteractions)}");
  441. Del(uid);
  442. }
  443. #endif
  444. }
  445. DebugTools.Assert(_queuedUpdates.Count == 0);
  446. }
  447. #region Event Handlers
  448. /// <summary>
  449. /// Queues a directed event to be handled by construction on the next update tick.
  450. /// Used as a handler for any events that construction can listen to. <seealso cref="InitializeInteractions"/>
  451. /// </summary>
  452. /// <param name="uid">The entity the event is directed to.</param>
  453. /// <param name="construction">The construction component to queue the event on.</param>
  454. /// <param name="args">The directed event to be queued.</param>
  455. /// <remarks>Events inheriting <see cref="HandledEntityEventArgs"/> are treated specially by this method.
  456. /// They will only be queued if they can be validated against the current construction state,
  457. /// in which case they will also be set as handled.</remarks>
  458. private void EnqueueEvent(EntityUid uid, ConstructionComponent construction, object args)
  459. {
  460. // For handled events, we will check if the event leads to a valid construction interaction.
  461. // If it does, we mark the event as handled and then enqueue it as normal.
  462. if (args is HandledEntityEventArgs handled)
  463. {
  464. // If they're already handled, we do nothing.
  465. if (handled.Handled)
  466. return;
  467. // Otherwise, let's check if this event could be handled by the construction's current state.
  468. if (HandleEvent(uid, args, true, construction) != HandleResult.Validated)
  469. return; // Not validated, so we don't even enqueue this event.
  470. handled.Handled = true;
  471. }
  472. // Enqueue this event so it'll be handled in the next tick.
  473. // This prevents some issues that could occur from entity deletion, component deletion, etc in a handler.
  474. construction.InteractionQueue.Enqueue(args);
  475. // Add this entity to the queue so it'll be updated next tick.
  476. if (_queuedUpdates.Add(uid))
  477. _constructionUpdateQueue.Enqueue(uid);
  478. }
  479. #endregion
  480. #region Internal Enum Definitions
  481. /// <summary>
  482. /// Specifies the DoAfter status for a construction step event handler.
  483. /// </summary>
  484. private enum DoAfterState : byte
  485. {
  486. /// <summary>
  487. /// If None, this is the first time we're seeing this event and we might want to call a DoAfter
  488. /// if the step needs it.
  489. /// </summary>
  490. None,
  491. /// <summary>
  492. /// If Completed, this is the second (and last) time we're seeing this event, and
  493. /// the doAfter that was called the first time successfully completed. Handle completion logic now.
  494. /// </summary>
  495. Completed
  496. }
  497. }
  498. /// <summary>
  499. /// Specifies the result after attempting to handle a specific step with an event.
  500. /// </summary>
  501. public enum HandleResult : byte
  502. {
  503. /// <summary>
  504. /// The interaction wasn't handled or validated.
  505. /// </summary>
  506. False,
  507. /// <summary>
  508. /// The interaction would be handled successfully. Nothing was modified.
  509. /// </summary>
  510. Validated,
  511. /// <summary>
  512. /// The interaction was handled successfully.
  513. /// </summary>
  514. True,
  515. /// <summary>
  516. /// The interaction is waiting on a DoAfter now.
  517. /// This means the interaction started the DoAfter.
  518. /// </summary>
  519. DoAfter,
  520. }
  521. #endregion
  522. public sealed class OnConstructionTemperatureEvent : HandledEntityEventArgs
  523. {
  524. public HandleResult? Result;
  525. }
  526. }