When the heat left drops on the same tick as a smithing action that reduces the heat (trip hammer, polishing wheel), it is possible to skip the heat change notification. This is because line 225 looks for equivalence:
state.getActionsLeftInStage() == config.StageNotificationsThreshold() &&
...
Instead, for each stage, there should probably be something like a heatAlreadyNotified boolean that is set to false at the beginning of each stage. Then, we could look for state.getActionsLeftInStage() <= config.StageNotificationsThreshold() && heatAlreadyNotified == false to avoid repeating notifiers but still ensure that they fire if there is never a tick where the heat remaining and the threshold are actually equivalent.
When the heat left drops on the same tick as a smithing action that reduces the heat (trip hammer, polishing wheel), it is possible to skip the heat change notification. This is because line 225 looks for equivalence:
Instead, for each stage, there should probably be something like a
heatAlreadyNotifiedboolean that is set tofalseat the beginning of each stage. Then, we could look forstate.getActionsLeftInStage() <= config.StageNotificationsThreshold() && heatAlreadyNotified == falseto avoid repeating notifiers but still ensure that they fire if there is never a tick where the heat remaining and the threshold are actually equivalent.