Implemented research cost, tweaked some values.

This commit is contained in:
2026-05-09 22:30:18 +02:00
parent fc21c7c8d3
commit 09df4334b9
8 changed files with 119 additions and 39 deletions
+8 -8
View File
@@ -20,7 +20,13 @@
], ],
"research": "basics", "research": "basics",
"crafttime": 4.0, "crafttime": 4.0,
"texture": "res://Assets/Images/Research/StoneageSymbol.png" "texture": "res://Assets/Images/Research/StoneageSymbol.png",
"effects": [
{
"stat": "robot_count_increase",
"value": 10
}
]
}, },
{ {
"id": "stone_tools", "id": "stone_tools",
@@ -36,13 +42,7 @@
], ],
"research": "stoneage", "research": "stoneage",
"crafttime": 5.0, "crafttime": 5.0,
"texture": "res://Assets/Images/Items/StoneGearSymbol.png", "texture": "res://Assets/Images/Items/StoneGearSymbol.png"
"effects": [
{
"stat": "robot_count_increase",
"value": 10
}
]
}, },
{ {
"id": "basic_machines", "id": "basic_machines",
+1 -1
View File
@@ -5,7 +5,7 @@ public class Inventory
{ {
public List<Item> items = new List<Item>(); public List<Item> items = new List<Item>();
public int maxInventorySize = 16; public int maxInventorySize = 10;
public event EventHandler OnInventoryUpdate; public event EventHandler OnInventoryUpdate;
public bool AddItem(Item item, int amount) public bool AddItem(Item item, int amount)
+16 -4
View File
@@ -15,10 +15,9 @@ public class Research
{ {
if (!paidResources) if (!paidResources)
{ {
foreach (Ingredient ingredient in data.Inputs) if (!CanStart()) return ResearchResult.FAILED;
{
GameData.inventory.RemoveItem(ingredient.Item, ingredient.Amount); PayResources();
}
paidResources = true; paidResources = true;
} }
@@ -31,6 +30,19 @@ public class Research
return ResearchResult.RESEARCHING; return ResearchResult.RESEARCHING;
} }
public bool CanStart()
{
return GameData.inventory.CanCraft(data.Inputs, 1);
}
public void PayResources()
{
foreach (Ingredient ingredient in data.Inputs)
{
GameData.inventory.RemoveItem(ingredient.Item, ingredient.Amount);
}
}
public void Complete() public void Complete()
{ {
if (state == ResearchState.RESEARCHED) return; if (state == ResearchState.RESEARCHED) return;
+5 -5
View File
@@ -4,12 +4,12 @@ using Godot;
public partial class Robot : Node3D public partial class Robot : Node3D
{ {
private const float EnergyUsePerSecond = 0.2f; private const float EnergyUsePerSecond = 0.12f;
private const float HeatGainPerSecond = 7.5f; private const float HeatGainPerSecond = 5f;
private const float ActiveHeatLossPerSecond = 18f; private const float ActiveHeatLossPerSecond = 22f;
private const float IdleHeatLossPerSecond = 9f; private const float IdleHeatLossPerSecond = 12f;
private const float CooldownTarget = 35f; private const float CooldownTarget = 35f;
private const float MaintenanceLossPerSecond = 0.04f; private const float MaintenanceLossPerSecond = 0.025f;
private bool isExecuting = false; private bool isExecuting = false;
private ProgramNode currentNode; private ProgramNode currentNode;
+9 -9
View File
@@ -2,10 +2,10 @@ using System;
public class SurvivalState public class SurvivalState
{ {
private const float HungerDrainPerSecond = 0.035f; private const float HungerDrainPerSecond = 0.012f;
private const float ThirstDrainPerSecond = 0.055f; private const float ThirstDrainPerSecond = 0.018f;
private const float PassiveEnergyDrainPerSecond = 0.025f; private const float PassiveEnergyDrainPerSecond = 0.01f;
private const float AutoConsumeThreshold = 35f; private const float AutoConsumeThreshold = 30f;
public float hunger = 100f; public float hunger = 100f;
public float thirst = 100f; public float thirst = 100f;
@@ -48,7 +48,7 @@ public class SurvivalState
if (hunger > AutoConsumeThreshold) return; if (hunger > AutoConsumeThreshold) return;
if (!GameData.inventory.TryRemoveItem("mushroom", 1)) return; if (!GameData.inventory.TryRemoveItem("mushroom", 1)) return;
hunger = Math.Clamp(hunger + 35f, 0f, maxHunger); hunger = Math.Clamp(hunger + 45f, 0f, maxHunger);
} }
private void TryAutoConsumeWater() private void TryAutoConsumeWater()
@@ -56,7 +56,7 @@ public class SurvivalState
if (thirst > AutoConsumeThreshold) return; if (thirst > AutoConsumeThreshold) return;
if (!GameData.inventory.TryRemoveItem("water", 1)) return; if (!GameData.inventory.TryRemoveItem("water", 1)) return;
thirst = Math.Clamp(thirst + 40f, 0f, maxThirst); thirst = Math.Clamp(thirst + 50f, 0f, maxThirst);
} }
private void TryAutoConsumeEnergy() private void TryAutoConsumeEnergy()
@@ -65,19 +65,19 @@ public class SurvivalState
if (GameData.inventory.TryRemoveItem("battery_v2", 1)) if (GameData.inventory.TryRemoveItem("battery_v2", 1))
{ {
energy = Math.Clamp(energy + 70f, 0f, maxEnergy); energy = Math.Clamp(energy + 90f, 0f, maxEnergy);
return; return;
} }
if (GameData.inventory.TryRemoveItem("battery_v1", 1)) if (GameData.inventory.TryRemoveItem("battery_v1", 1))
{ {
energy = Math.Clamp(energy + 45f, 0f, maxEnergy); energy = Math.Clamp(energy + 65f, 0f, maxEnergy);
return; return;
} }
if (GameData.inventory.TryRemoveItem("steam", 1)) if (GameData.inventory.TryRemoveItem("steam", 1))
{ {
energy = Math.Clamp(energy + 25f, 0f, maxEnergy); energy = Math.Clamp(energy + 40f, 0f, maxEnergy);
} }
} }
+24
View File
@@ -16,6 +16,7 @@ public partial class TestRunner : Node
Run("Robot research effects change robot stats", TestRobotResearchEffects); Run("Robot research effects change robot stats", TestRobotResearchEffects);
Run("Research completion applies effects once", TestResearchCompletionAppliesEffectsOnce); Run("Research completion applies effects once", TestResearchCompletionAppliesEffectsOnce);
Run("Research execution pays resources and finishes", TestResearchExecutionPaysResourcesAndFinishes); Run("Research execution pays resources and finishes", TestResearchExecutionPaysResourcesAndFinishes);
Run("Research cannot start without resources", TestResearchCannotStartWithoutResources);
Run("Inventory add failure keeps inventory unchanged", TestInventoryAddFailureKeepsInventoryUnchanged); Run("Inventory add failure keeps inventory unchanged", TestInventoryAddFailureKeepsInventoryUnchanged);
Run("Resource extraction and save data roundtrip", TestResourceSaveRoundtrip); Run("Resource extraction and save data roundtrip", TestResourceSaveRoundtrip);
Run("Robot save data roundtrip keeps robot state", TestRobotSaveRoundtrip); Run("Robot save data roundtrip keeps robot state", TestRobotSaveRoundtrip);
@@ -238,6 +239,29 @@ public partial class TestRunner : Node
AssertEqual(0, GameData.inventory.items.Count, "failed add should not create stacks"); AssertEqual(0, GameData.inventory.items.Count, "failed add should not create stacks");
} }
private void TestResearchCannotStartWithoutResources()
{
ResearchData researchData = new ResearchData
{
Id = "missing_stones",
Inputs = new List<Ingredient>
{
new Ingredient { Item = "stone", Amount = 3 }
},
Research = "basics",
CraftTime = 1.0,
Texture = "",
Effects = new List<ResearchEffect>()
};
Research research = new Research(researchData);
ResearchResult result = research.Execute(1.0);
AssertFalse(research.CanStart(), "research should not be startable");
AssertEqual(ResearchResult.FAILED, result, "research should fail without resources");
AssertEqual(ResearchState.UNDEFINED, research.state, "research state should stay unchanged");
}
private void TestSplitSaveFilesRoundtrip() private void TestSplitSaveFilesRoundtrip()
{ {
GameData.inventory.AddItem(new Item { data = GameData.availableItems["water"] }, 7); GameData.inventory.AddItem(new Item { data = GameData.availableItems["water"] }, 7);
+54 -10
View File
@@ -23,12 +23,20 @@ public partial class ResearchList : PanelContainer
if(currentResearch.Count > 0) toDelete = new List<Research>(); if(currentResearch.Count > 0) toDelete = new List<Research>();
foreach (Research research in currentResearch) foreach (Research research in currentResearch)
{ {
if (research.Execute(delta) == ResearchResult.FINISHED) ResearchResult result = research.Execute(delta);
if (result == ResearchResult.FINISHED)
{ {
toDelete.Add(research); toDelete.Add(research);
RecalculateResearchStates(); RecalculateResearchStates();
SetupGraph(); SetupGraph();
} }
else if (result == ResearchResult.FAILED)
{
research.state = ResearchState.AVAILABLE;
toDelete.Add(research);
RecalculateResearchStates();
SetupGraph();
}
} }
foreach (Research delete in toDelete) foreach (Research delete in toDelete)
{ {
@@ -133,8 +141,8 @@ public partial class ResearchList : PanelContainer
Button button = new Button Button button = new Button
{ {
Text = "Research", Text = GetResearchButtonText(GameData.availableResearch[id], state),
Disabled = state != ResearchState.AVAILABLE, Disabled = state != ResearchState.AVAILABLE || !GameData.availableResearch[id].CanStart(),
TooltipText = tooltipText TooltipText = tooltipText
}; };
@@ -171,21 +179,57 @@ public partial class ResearchList : PanelContainer
private void OnResearchPressed(string id) private void OnResearchPressed(string id)
{ {
GameData.availableResearch[id].state = ResearchState.RESEARCHING; Research research = GameData.availableResearch[id];
currentResearch.Add(GameData.availableResearch[id]); if (!research.CanStart()) return;
if (currentResearch.Contains(research)) return;
research.state = ResearchState.RESEARCHING;
currentResearch.Add(research);
RecalculateResearchStates(); RecalculateResearchStates();
SetupGraph(); SetupGraph();
} }
private string GetResearchButtonText(Research research, ResearchState state)
{
if (state == ResearchState.RESEARCHED) return "Done";
if (state == ResearchState.RESEARCHING) return "Researching";
if (state == ResearchState.LOCKED) return "Locked";
if (!research.CanStart()) return "Missing items";
return "Research";
}
private string GetResearchTooltip(Research research) private string GetResearchTooltip(Research research)
{ {
if (research.data.Effects == null || research.data.Effects.Count <= 0)
{
return Research.GetReadableName(research.data.Id);
}
StringBuilder tooltip = new StringBuilder(Research.GetReadableName(research.data.Id)); StringBuilder tooltip = new StringBuilder(Research.GetReadableName(research.data.Id));
tooltip.AppendLine(); tooltip.AppendLine();
tooltip.AppendLine();
tooltip.AppendLine("Costs:");
if (research.data.Inputs.Count <= 0)
{
tooltip.AppendLine("- None");
}
foreach (Ingredient ingredient in research.data.Inputs)
{
tooltip.Append("- ");
tooltip.Append(ItemData.GetReadableName(ingredient.Item));
tooltip.Append(": ");
tooltip.Append(GameData.inventory.GetItemAmount(ingredient.Item));
tooltip.Append("/");
tooltip.AppendLine(ingredient.Amount.ToString());
}
tooltip.AppendLine();
tooltip.Append("Time: ");
tooltip.AppendLine(research.data.CraftTime.ToString("0"));
if (research.data.Effects == null || research.data.Effects.Count <= 0)
{
return tooltip.ToString();
}
tooltip.AppendLine(); tooltip.AppendLine();
tooltip.AppendLine("Effects:"); tooltip.AppendLine("Effects:");
+2 -2
View File
@@ -304,7 +304,7 @@ public partial class World : Node3D
} while (addedNewItem); } while (addedNewItem);
//Step 3: Choose gate items needed based on crafting time and layer it is for (Lower layers -> More advanced items -> More crafting time) //Step 3: Choose gate items needed based on crafting time and layer it is for (Lower layers -> More advanced items -> More crafting time)
double goalCraftTime = Mathf.Lerp(lowestCraftTime, highestCraftTime, Mathf.Clamp(layer.level/(float)ruinSize, 0, 1)); double goalCraftTime = Mathf.Lerp(lowestCraftTime, highestCraftTime, Mathf.Clamp(layer.level/(float)ruinSize, 0, 1));
int ingredientAmount = rand.Next(1, 1 + ruinSize / 2); int ingredientAmount = Mathf.Clamp(1 + layer.level / 3, 1, 4);
float craftTimeModifier = 0f; float craftTimeModifier = 0f;
double craftTimeLower, craftTimeUpper; double craftTimeLower, craftTimeUpper;
for (int i = 0; i < ingredientAmount; i++) for (int i = 0; i < ingredientAmount; i++)
@@ -331,7 +331,7 @@ public partial class World : Node3D
layer.gateIngredients.Add(new Ingredient layer.gateIngredients.Add(new Ingredient
{ {
Item = item.Id, Item = item.Id,
Amount = rand.Next(5 + layer.level, 20 + layer.level) Amount = rand.Next(3 + layer.level * 2, 9 + layer.level * 4)
}); });
craftTimeModifier = 0f; craftTimeModifier = 0f;
} }