Files
k4rli-plugins/src/main/java/com/toofifty/easygiantsfoundry/HeatActionStateMachine.java

302 lines
6.6 KiB
Java

package com.toofifty.easygiantsfoundry;
import com.toofifty.easygiantsfoundry.enums.Stage;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
/**
* A state-machine that keeps track of heating/cooling actions.
*/
@Slf4j
@Data
public class HeatActionStateMachine
{
/**
* Tick counter for heating, -1 means not currently heating.
*/
int HeatingTicks = -1;
/**
* Tick counter for cooling, -1 means not currently cooling.
*/
int CoolingTicks = -1;
/**
* The velocity of the heating/cooling action.
*/
int Velocity;
/**
* The acceleration bonus of the heating/cooling action.
*/
int AccelerationBonus;
/**
* The starting heat amount of the heating/cooling action.
*/
int StartingHeat;
/**
* The estimated tick duration of the heating/cooling action.
*/
int EstimatedDuration;
/**
* The goal heat amount of the heating/cooling action.
*/
int GoalHeat = 0;
/**
* The last action the player clicked on. Used for ui overlay to display.
* When null, the state-machine will stop() and reset.
*/
String ActionName = null;
private EasyGiantsFoundryState State;
private EasyGiantsFoundryConfig Config;
/**
* Start the state-machine with the given parameters.
* <p>
* These parameters have to be set-up manually before start().
* Velocity, AccelerationBonus, ActionName
*
* @param state the current state of the foundry
* @param config the current configuration of the plugin
* @param startingHeat the starting heat amount
* @see HeatActionStateMachine#setup(int, int, String)
*/
public void start(EasyGiantsFoundryState state, EasyGiantsFoundryConfig config, int startingHeat)
{
// use Velocity to determine if heating or cooling
if (Velocity > 0)
{
HeatingTicks = 0;
CoolingTicks = -1;
}
else
{
CoolingTicks = 0;
HeatingTicks = -1;
}
StartingHeat = startingHeat - Velocity;
State = state;
Config = config;
calculateEstimates();
}
/**
* Get the estimated remaining duration of the heating/cooling action.
*
* @return the estimated remaining duration in ticks
*/
public int getRemainingDuration()
{
if (isHeating())
{
return Math.max(0, EstimatedDuration - HeatingTicks);
}
else if (isCooling())
{
return Math.max(0, EstimatedDuration - CoolingTicks);
}
else
{
return 0;
}
}
/**
* Core logic. Runs once on {@link HeatActionStateMachine#start} and assumes synchronization with the game.
* Calculate the estimated duration and goal heat amount of the heating/cooling action.
*/
public void calculateEstimates()
{
// 0: left/min 1: right/max
int[] range = State.getCurrentHeatRange();
int stageMin = range[0];
int stageMax = range[1];
Stage stage = State.getCurrentStage();
int actionsLeft = State.getActionsLeftInStage();
int actionsLeft_DeltaHeat = (actionsLeft+1) * stage.getHeatChange();
if (isHeating())
{
if (stage.isHeating())
{
GoalHeat = Math.max(stageMin, stageMax - actionsLeft_DeltaHeat);
if (StartingHeat < GoalHeat)
{
int duration = HeatActionSolver.findDx0Index(
GoalHeat - StartingHeat,
Velocity, AccelerationBonus
);
GoalHeat += duration / 2;
EstimatedDuration = HeatActionSolver.findDx0Index(
GoalHeat - StartingHeat,
Velocity, AccelerationBonus
);
}
else // overheating
{
EstimatedDuration = 0;
}
}
else // is cooling
{
// actionsLeft_DeltaHeat is negative here
GoalHeat = Math.min(stageMax, stageMin - actionsLeft_DeltaHeat);
if (StartingHeat < GoalHeat)
{
int duration = HeatActionSolver.findDx0Index(
GoalHeat - StartingHeat,
Velocity, AccelerationBonus
) - 1;
GoalHeat -= duration / 2;
EstimatedDuration = HeatActionSolver.findDx0Index(
GoalHeat - StartingHeat,
Velocity, AccelerationBonus
) - 1;
}
else // cold enough
{
EstimatedDuration = 0;
}
}
}
else if (isCooling())
{
if (stage.isHeating()) {
GoalHeat = Math.max(stageMin, stageMax - actionsLeft_DeltaHeat);
if (StartingHeat > GoalHeat)
{
int duration = HeatActionSolver.findDx0Index(
StartingHeat - GoalHeat,
Math.abs(Velocity), Math.abs(AccelerationBonus)
) - 1;
GoalHeat += duration / 2;
EstimatedDuration = HeatActionSolver.findDx0Index(
(StartingHeat - GoalHeat),
Math.abs(Velocity), Math.abs(AccelerationBonus)
) - 1;
}
else
{
EstimatedDuration = 0;
}
}
// Heating Stage
else {
GoalHeat = Math.max(stageMax, stageMin + actionsLeft_DeltaHeat);
if (StartingHeat > GoalHeat) // too hot
{
int duration = HeatActionSolver.findDx0Index(
StartingHeat - GoalHeat,
Math.abs(Velocity), Math.abs(AccelerationBonus)
);
GoalHeat -= duration / 2;
EstimatedDuration = HeatActionSolver.findDx0Index(
StartingHeat - GoalHeat,
Math.abs(Velocity), Math.abs(AccelerationBonus)
);
}
else // hot enough
{
EstimatedDuration = 0;
}
}
}
}
/**
* Helper to remind the neccessary parameters to start the state-machine.
*
* @param velocity the velocity of the heating/cooling action, 7 for slow, 27 for fast.
* @param accelerationBonus the acceleration bonus of the heating/cooling action. Usually 0 for slow, 2 for fast.
* @param actionName the name of the action to display in the ui overlay
*/
public void setup(int velocity, int accelerationBonus, String actionName)
{
Velocity = velocity;
AccelerationBonus = accelerationBonus;
ActionName = actionName;
}
/**
* Stop the state-machine.
*/
public void stop()
{
HeatingTicks = -1;
CoolingTicks = -1;
ActionName = null;
}
/**
* Check if the state is currently heating.
*
* @return true if heating, false otherwise
*/
public boolean isHeating()
{
return HeatingTicks >= 0;
}
/**
* Check if the state is currently cooling.
*
* @return true if cooling, false otherwise
*/
public boolean isCooling()
{
return CoolingTicks >= 0;
}
/**
* Check if the heating/cooling state is currently idle. Neither heating nor cooling.
*
* @return
*/
public boolean isIdle()
{
return !(isHeating() || isCooling());
}
/**
* Tick the state-machine. This has to be called onVarbitChanged in order to sync with the game.
*/
public void onTick()
{
if (isHeating())
{
HeatingTicks++;
if (HeatingTicks >= EstimatedDuration)
{
stop();
}
}
if (isCooling())
{
CoolingTicks++;
if (CoolingTicks >= EstimatedDuration)
{
stop();
}
}
// log.info("\nReal Heat: " + State.getHeatAmount()
// + "\nGoal Heat - StartingHeat: " + (GoalHeat - StartingHeat)
// + "\nDuration: " + EstimatedDuration);
}
}