Files
RuinAdventurer/Scripts/Gameplay/Robots/Robot.cs
T

293 lines
6.2 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))
{
switch (currentNode.Execute(this, delta))
{
case NodeResult.SUCCESS:
if (currentNode.DisplayText == "Until")
{
while (true)
{
currentNode = currentNode.previousNode;
if (currentNode == null)
{
isExecuting = false;
break;
}
if (currentNode.DisplayText == "For" || currentNode.DisplayText == "While") break;
}
}
else
{
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:
string sourceNode = currentNode.DisplayText;
while (true)
{
currentNode = currentNode.nextNode;
if (currentNode == null)
{
isExecuting = false;
break;
}
if (sourceNode == "If")
{
if (currentNode.DisplayText == "If" || currentNode.DisplayText == "Else") break;
}
else
{
if (currentNode.DisplayText == "While" || currentNode.DisplayText == "For" || currentNode.DisplayText == "Until")
{
if (currentNode.nextNode == null)
{
isExecuting = false;
break;
}
currentNode = currentNode.nextNode;
break;
}
}
}
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 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;
}
}
}