using System; using System.Collections.Generic; using Godot; public partial class Robot : Node3D { private const float EnergyUsePerSecond = 0.2f; private const float HeatGainPerSecond = 7.5f; private const float ActiveHeatLossPerSecond = 18f; private const float IdleHeatLossPerSecond = 9f; private const float CooldownTarget = 35f; private const float MaintenanceLossPerSecond = 0.04f; private bool isExecuting = false; private 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 (isExecuting) { if (CanExecute(delta)) { 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; } } } else if (currentMessage.Length <= 0) { CoolDown( delta, GameData.robotStats.GetCoolingRate(IdleHeatLossPerSecond) * TypeStats.CoolingMultiplier ); currentMessage = "No script executing"; } Visible = Math.Round(Math.Abs(Position.Y / GameData.tileHeight), 0) == GameData.visibleLayer; } public void SetupExecution(List 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 = ""; } 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; } } }