262 lines
5.4 KiB
C#
262 lines
5.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Godot;
|
|
|
|
public partial class Robot : Node3D
|
|
{
|
|
private const float EnergyUsePerSecond = 0.12f;
|
|
private const float HeatGainPerSecond = 5f;
|
|
private const float ActiveHeatLossPerSecond = 22f;
|
|
private const float IdleHeatLossPerSecond = 12f;
|
|
private const float CooldownTarget = 35f;
|
|
private const float MaintenanceLossPerSecond = 0.025f;
|
|
|
|
private bool isExecuting = false;
|
|
|
|
public ProgramNode currentNode;
|
|
public string currentProgram;
|
|
public string currentMessage = "";
|
|
public float heat = 0f;
|
|
public float maintenance = 100f;
|
|
public bool isCoolingDown = false;
|
|
public bool isBroken = false;
|
|
public string robotType = "iron_robot";
|
|
|
|
private RobotTypeStats TypeStats =>
|
|
GameData.robotStats.RobotTypes.TryGetValue(robotType, out RobotTypeStats stats)
|
|
? stats
|
|
: GameData.robotStats.RobotTypes["stone_robot"];
|
|
|
|
public override void _Process(double delta)
|
|
{
|
|
if (GameData.isPaused) return;
|
|
|
|
if (isExecuting)
|
|
{
|
|
if (CanExecute(delta))
|
|
{
|
|
GD.Print(currentNode.DisplayText);
|
|
switch (currentNode.Execute(this, delta))
|
|
{
|
|
case NodeResult.SUCCESS:
|
|
currentNode = currentNode.nextNode;
|
|
if (currentNode == null)
|
|
{
|
|
isExecuting = false;
|
|
}
|
|
break;
|
|
|
|
case NodeResult.FAILURE:
|
|
isExecuting = false;
|
|
currentMessage = "(FAILED)" + currentNode.lastExecutionMessage;
|
|
break;
|
|
|
|
case NodeResult.RUNNING:
|
|
currentMessage = "";
|
|
break;
|
|
case NodeResult.CONDITIONFALSE:
|
|
currentNode = currentNode.NegativeNode;
|
|
if (currentNode == null)
|
|
{
|
|
isExecuting = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (currentMessage.Length <= 0)
|
|
{
|
|
CoolDown(
|
|
delta,
|
|
GameData.robotStats.GetCoolingRate(IdleHeatLossPerSecond)
|
|
* TypeStats.CoolingMultiplier
|
|
);
|
|
|
|
currentMessage = "No script executing";
|
|
}
|
|
else
|
|
{
|
|
CoolDown(
|
|
delta,
|
|
GameData.robotStats.GetCoolingRate(IdleHeatLossPerSecond)
|
|
* TypeStats.CoolingMultiplier
|
|
);
|
|
}
|
|
|
|
Visible = Math.Round(Math.Abs(Position.Y / GameData.tileHeight), 0) == GameData.visibleLayer;
|
|
}
|
|
|
|
public void SetupExecution(List<ProgramNode> nodes)
|
|
{
|
|
if (nodes.Count <= 0) return;
|
|
|
|
isExecuting = true;
|
|
currentNode = nodes[0];
|
|
currentMessage = "";
|
|
}
|
|
|
|
public void StopExecution(string message)
|
|
{
|
|
isExecuting = false;
|
|
currentNode = null;
|
|
currentMessage = message;
|
|
}
|
|
|
|
public float GetMovementSpeed()
|
|
{
|
|
return GameData.robotStats.GetMovementSpeed(GameData.robotSpeed)
|
|
* TypeStats.SpeedMultiplier
|
|
* GetWorkEfficiency();
|
|
}
|
|
|
|
public float GetWorkEfficiency()
|
|
{
|
|
if (isBroken) return 0f;
|
|
if (maintenance >= 50f) return 1f;
|
|
|
|
float minimumEfficiency =
|
|
GameData.robotStats.GetMinimumEfficiency()
|
|
+ TypeStats.MinimumEfficiencyBonus;
|
|
|
|
minimumEfficiency = Math.Clamp(minimumEfficiency, 0f, 1f);
|
|
|
|
return Math.Clamp(
|
|
minimumEfficiency + maintenance / 100f,
|
|
minimumEfficiency,
|
|
1f
|
|
);
|
|
}
|
|
|
|
public void Maintain()
|
|
{
|
|
maintenance = 100f;
|
|
isBroken = false;
|
|
currentMessage = "";
|
|
}
|
|
|
|
public void Maintain(float amount)
|
|
{
|
|
maintenance = Math.Clamp(maintenance + amount, 0f, 100f);
|
|
if (maintenance > 0f)
|
|
{
|
|
isBroken = false;
|
|
}
|
|
currentMessage = "";
|
|
}
|
|
|
|
public RobotSaveData CreateSaveData()
|
|
{
|
|
return new RobotSaveData
|
|
{
|
|
Name = Name,
|
|
CurrentProgram = currentProgram,
|
|
CurrentMessage = currentMessage,
|
|
RobotType = robotType,
|
|
X = Position.X,
|
|
Y = Position.Y,
|
|
Z = Position.Z,
|
|
Heat = heat,
|
|
Maintenance = maintenance,
|
|
IsCoolingDown = isCoolingDown,
|
|
IsBroken = isBroken
|
|
};
|
|
}
|
|
|
|
public void LoadSaveData(RobotSaveData saveData)
|
|
{
|
|
Name = saveData.Name;
|
|
currentProgram = saveData.CurrentProgram;
|
|
currentMessage = saveData.CurrentMessage ?? "";
|
|
robotType = saveData.RobotType ?? "stone_robot";
|
|
Position = new Vector3(saveData.X, saveData.Y, saveData.Z);
|
|
heat = saveData.Heat;
|
|
maintenance = saveData.Maintenance;
|
|
isCoolingDown = saveData.IsCoolingDown;
|
|
isBroken = saveData.IsBroken;
|
|
}
|
|
|
|
private bool CanExecute(double delta)
|
|
{
|
|
if (GameData.survival.isDead)
|
|
{
|
|
currentMessage = "Survival failed";
|
|
return false;
|
|
}
|
|
|
|
if (isBroken)
|
|
{
|
|
currentMessage = "Maintenance required";
|
|
return false;
|
|
}
|
|
|
|
if (isCoolingDown)
|
|
{
|
|
CoolDown(
|
|
delta,
|
|
GameData.robotStats.GetCoolingRate(ActiveHeatLossPerSecond)
|
|
* TypeStats.CoolingMultiplier
|
|
);
|
|
|
|
currentMessage = $"Cooling down ({heat:0}%)";
|
|
return false;
|
|
}
|
|
|
|
float energyUse =
|
|
GameData.robotStats.GetEnergyUse(EnergyUsePerSecond)
|
|
* TypeStats.EnergyUseMultiplier
|
|
* (float)delta;
|
|
|
|
if (!GameData.survival.TryConsumeEnergy(energyUse))
|
|
{
|
|
currentMessage = "Not enough energy";
|
|
return false;
|
|
}
|
|
|
|
heat = Math.Clamp(
|
|
heat + GameData.robotStats.GetHeatGain(HeatGainPerSecond)
|
|
* TypeStats.HeatGainMultiplier
|
|
* (float)delta,
|
|
0f,
|
|
100f
|
|
);
|
|
|
|
maintenance = Math.Clamp(
|
|
maintenance - GameData.robotStats.GetMaintenanceLoss(MaintenanceLossPerSecond)
|
|
* TypeStats.MaintenanceLossMultiplier
|
|
* (float)delta,
|
|
0f,
|
|
100f
|
|
);
|
|
|
|
if (heat >= 100f)
|
|
{
|
|
isCoolingDown = true;
|
|
currentMessage = "Overheated";
|
|
return false;
|
|
}
|
|
|
|
if (maintenance <= 0f)
|
|
{
|
|
isBroken = true;
|
|
currentMessage = "Maintenance required";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void CoolDown(double delta, float heatLossPerSecond)
|
|
{
|
|
heat = Math.Clamp(
|
|
heat - heatLossPerSecond * (float)delta,
|
|
0f,
|
|
100f
|
|
);
|
|
|
|
if (heat <= CooldownTarget)
|
|
{
|
|
isCoolingDown = false;
|
|
}
|
|
}
|
|
}
|