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; 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 (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"; } 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 = ""; } 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; } } }