busted simulation
This commit is contained in:
parent
9214d13054
commit
90d6c5c926
6 changed files with 1215 additions and 33 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
[gd_scene format=3 uid="uid://br6hgvb4buyji"]
|
[gd_scene format=3 uid="uid://br6hgvb4buyji"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bup76ad02kuse" path="res://scripts/sim_cached.gd" id="1_sim"]
|
[ext_resource type="Script" uid="uid://citjokiv6skqi" path="res://scripts/sim_direct.gd" id="1_sim"]
|
||||||
|
|
||||||
[node name="Simulator" type="Control" unique_id=1833845714]
|
[node name="Simulator" type="Control" unique_id=1833845714]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
|
|
|
||||||
1036
scripts/sim_direct.gd
Normal file
1036
scripts/sim_direct.gd
Normal file
File diff suppressed because it is too large
Load diff
1
scripts/sim_direct.gd.uid
Normal file
1
scripts/sim_direct.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://citjokiv6skqi
|
||||||
|
|
@ -2,11 +2,16 @@ class_name TickProcess
|
||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
# Dependency injection - can be overridden for simulation
|
# Dependency injection - can be overridden for simulation
|
||||||
var unlocks_provider: Node = null
|
# Using Variant to accept both Node (singletons) and RefCounted (IsolatedGameState)
|
||||||
var inventory_provider: Node = null
|
var unlocks_provider = null
|
||||||
|
var inventory_provider = null
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# Default to singletons if not explicitly set
|
# Default to singletons if not explicitly set
|
||||||
|
_ensure_providers_initialized()
|
||||||
|
|
||||||
|
func _ensure_providers_initialized():
|
||||||
|
"""Ensure providers are set (called before every tick if needed)"""
|
||||||
if unlocks_provider == null:
|
if unlocks_provider == null:
|
||||||
unlocks_provider = Unlocks
|
unlocks_provider = Unlocks
|
||||||
if inventory_provider == null:
|
if inventory_provider == null:
|
||||||
|
|
@ -19,71 +24,72 @@ func set_providers(unlocks, inventory):
|
||||||
|
|
||||||
func tick():
|
func tick():
|
||||||
# Log.pr("Tick Process Ticking...")
|
# Log.pr("Tick Process Ticking...")
|
||||||
|
_ensure_providers_initialized() # Safety check before each tick
|
||||||
do_autowood()
|
do_autowood()
|
||||||
do_whittling()
|
do_whittling()
|
||||||
do_selling()
|
do_selling()
|
||||||
|
|
||||||
func do_autowood():
|
func do_autowood():
|
||||||
# If the autowood unlock is unlocked then automatically gain wood based on the modifier
|
# If the autowood unlock is unlocked then automatically gain wood based on the modifier
|
||||||
var autowood_unlock = Unlocks.get_unlock_by_id(Global.autowood_unlock_id)
|
var autowood_unlock = unlocks_provider.get_unlock_by_id(Global.autowood_unlock_id)
|
||||||
if autowood_unlock and autowood_unlock.is_unlocked:
|
if autowood_unlock and autowood_unlock.is_unlocked:
|
||||||
# Log.pr("Autowood modifier", str(Unlocks.get_modifier_value("autowood_modifier")))
|
# Log.pr("Autowood modifier", str(unlocks_provider.get_modifier_value("autowood_modifier")))
|
||||||
var wood_to_gather = max(Unlocks.get_wood_per_click() * Unlocks.get_modifier_value("autowood_modifier"), 1)
|
var wood_to_gather = max(unlocks_provider.get_wood_per_click() * unlocks_provider.get_modifier_value("autowood_modifier"), 1)
|
||||||
Inventory.add_wood(wood_to_gather)
|
inventory_provider.add_wood(wood_to_gather)
|
||||||
# Log.pr("Auto-gathered", str(wood_to_gather), "wood via autowood unlock.")
|
# Log.pr("Auto-gathered", str(wood_to_gather), "wood via autowood unlock.")
|
||||||
|
|
||||||
func do_whittling():
|
func do_whittling():
|
||||||
# If there's more than 1 whole wood available, then whittle based on the efficiency modifier
|
# If there's more than 1 whole wood available, then whittle based on the efficiency modifier
|
||||||
if Inventory.get_wood() >= 1:
|
if inventory_provider.get_wood() >= 1:
|
||||||
whittle_max_wood_possible()
|
whittle_max_wood_possible()
|
||||||
|
|
||||||
## If multicraft is unlocked, whittle additional wood based on multicraft unlock
|
## If multicraft is unlocked, whittle additional wood based on multicraft unlock
|
||||||
var multicraft_unlock = Unlocks.get_unlock_by_id(Global.multicraft_unlock_id)
|
var multicraft_unlock = unlocks_provider.get_unlock_by_id(Global.multicraft_unlock_id)
|
||||||
if multicraft_unlock and multicraft_unlock.is_unlocked:
|
if multicraft_unlock and multicraft_unlock.is_unlocked:
|
||||||
var additional_whittles = multicraft_unlock.current_rank # Each rank allows one additional whittling action
|
var additional_whittles = multicraft_unlock.current_rank # Each rank allows one additional whittling action
|
||||||
for i in range(additional_whittles):
|
for i in range(additional_whittles):
|
||||||
if Inventory.get_wood() >= 1:
|
if inventory_provider.get_wood() >= 1:
|
||||||
whittle_max_wood_possible()
|
whittle_max_wood_possible()
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
|
|
||||||
func do_selling():
|
func do_selling():
|
||||||
# If the wholesale unlock is purchased, sell blocks of 100 whittled wood if possible
|
# If the wholesale unlock is purchased, sell blocks of 100 whittled wood if possible
|
||||||
var wholesale_unlock = Unlocks.get_unlock_by_id(Global.wholesale_unlock_id)
|
var wholesale_unlock = unlocks_provider.get_unlock_by_id(Global.wholesale_unlock_id)
|
||||||
if wholesale_unlock and wholesale_unlock.is_unlocked:
|
if wholesale_unlock and wholesale_unlock.is_unlocked:
|
||||||
while Inventory.get_stock() >= Global.wholesale_bundle_size:
|
while inventory_provider.get_stock() >= Global.wholesale_bundle_size:
|
||||||
Inventory.spend_stock(Global.wholesale_bundle_size)
|
inventory_provider.spend_stock(Global.wholesale_bundle_size)
|
||||||
var currency_earned = Global.wholesale_bundle_size * Unlocks.get_sale_price_per_item() * Global.wholesale_discount_multiplier
|
var currency_earned = Global.wholesale_bundle_size * unlocks_provider.get_sale_price_per_item() * Global.wholesale_discount_multiplier
|
||||||
Inventory.add_currency(currency_earned)
|
inventory_provider.add_currency(currency_earned)
|
||||||
# Log.pr("Sold 100 whittled wood for", str(currency_earned), "currency via wholesale unlock.")
|
# Log.pr("Sold 100 whittled wood for", str(currency_earned), "currency via wholesale unlock.")
|
||||||
|
|
||||||
|
|
||||||
# If there's whittled wood available to sell, sell it for currency
|
# If there's whittled wood available to sell, sell it for currency
|
||||||
if Inventory.get_stock() > 0:
|
if inventory_provider.get_stock() > 0:
|
||||||
var whittle_wood_to_sell = Inventory.get_stock()
|
var whittle_wood_to_sell = inventory_provider.get_stock()
|
||||||
# Sell whatever people are willing to buy
|
# Sell whatever people are willing to buy
|
||||||
var purchase_rate = Global.base_purchase_rate * Unlocks.get_modifier_value("purchase_rate_modifier")
|
var purchase_rate = Global.base_purchase_rate * unlocks_provider.get_modifier_value("purchase_rate_modifier")
|
||||||
var max_stock_to_sell = floor(purchase_rate)
|
var max_stock_to_sell = floor(purchase_rate)
|
||||||
|
|
||||||
# Sell up to the max stock to sell this tick, but no more than available stock
|
# Sell up to the max stock to sell this tick, but no more than available stock
|
||||||
# We should always sell at least one, up to the max
|
# We should always sell at least one, up to the max
|
||||||
var actual_stock_to_sell = min(whittle_wood_to_sell, max(1, max_stock_to_sell))
|
var actual_stock_to_sell = min(whittle_wood_to_sell, max(1, max_stock_to_sell))
|
||||||
|
|
||||||
Inventory.spend_stock(actual_stock_to_sell)
|
inventory_provider.spend_stock(actual_stock_to_sell)
|
||||||
var currency_earned = actual_stock_to_sell * Unlocks.get_sale_price_per_item()
|
var currency_earned = actual_stock_to_sell * unlocks_provider.get_sale_price_per_item()
|
||||||
Inventory.add_currency(currency_earned)
|
inventory_provider.add_currency(currency_earned)
|
||||||
|
|
||||||
|
|
||||||
func whittle_max_wood_possible():
|
func whittle_max_wood_possible():
|
||||||
# Get the items that can be produced per tick
|
# Get the items that can be produced per tick
|
||||||
var items_produced_per_tick = Unlocks.get_items_produced_per_tick()
|
var items_produced_per_tick = unlocks_provider.get_items_produced_per_tick()
|
||||||
# Log.pr("Items produced per tick:", str(items_produced_per_tick))
|
# Log.pr("Items produced per tick:", str(items_produced_per_tick))
|
||||||
var wood_needed = ceil(items_produced_per_tick)
|
var wood_needed = ceil(items_produced_per_tick)
|
||||||
|
|
||||||
# Whittle as much wood as possible this tick, up to the max allowed by efficiency
|
# Whittle as much wood as possible this tick, up to the max allowed by efficiency
|
||||||
var wood_to_whittle = min(Inventory.get_wood(), wood_needed)
|
var wood_to_whittle = min(inventory_provider.get_wood(), wood_needed)
|
||||||
var actual_items_produced = wood_to_whittle
|
var actual_items_produced = wood_to_whittle
|
||||||
|
|
||||||
Inventory.spend_wood(wood_to_whittle)
|
inventory_provider.spend_wood(wood_to_whittle)
|
||||||
Inventory.add_stock(actual_items_produced)
|
inventory_provider.add_stock(actual_items_produced)
|
||||||
# Log.pr("Whittled", str(wood_to_whittle), "wood into", str(actual_items_produced), "whittle wood.")
|
# Log.pr("Whittled", str(wood_to_whittle), "wood into", str(actual_items_produced), "whittle wood.")
|
||||||
|
|
|
||||||
138
scripts/unlock_data_lightweight.gd
Normal file
138
scripts/unlock_data_lightweight.gd
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
class_name UnlockDataLightweight
|
||||||
|
## Lightweight unlock data structure for simulations
|
||||||
|
## Contains the same calculation logic as UnlockDataResource but without Resource overhead
|
||||||
|
|
||||||
|
var unlock_id: int = 0
|
||||||
|
var unlock_name: String = ""
|
||||||
|
var base_cost: int = 0
|
||||||
|
var is_unlocked: bool = false
|
||||||
|
|
||||||
|
# Scaling settings
|
||||||
|
var is_scaling: bool = false
|
||||||
|
var current_rank: int = 0
|
||||||
|
var max_rank: int = -1
|
||||||
|
|
||||||
|
# Cost scaling
|
||||||
|
var cost_scaling_type: int = 1 # 0=Linear, 1=Exponential
|
||||||
|
var cost_scaling_multiplier: float = 1.5
|
||||||
|
var cost_linear_increase: int = 100
|
||||||
|
var cost_ladder: Array[int] = []
|
||||||
|
|
||||||
|
# Effect scaling
|
||||||
|
var effect_scaling_type: int = 1 # 0=Linear, 1=Exponential
|
||||||
|
var effect_scaling_multiplier: float = 1.2
|
||||||
|
var effect_linear_increase: float = 0.1
|
||||||
|
|
||||||
|
# Base modifiers
|
||||||
|
var base_modifiers: Dictionary = {}
|
||||||
|
|
||||||
|
## Static factory method to create from UnlockDataResource (one-time conversion)
|
||||||
|
static func from_resource(resource: UnlockDataResource) -> UnlockDataLightweight:
|
||||||
|
var data = UnlockDataLightweight.new()
|
||||||
|
data.unlock_id = resource.unlock_id
|
||||||
|
data.unlock_name = resource.unlock_name
|
||||||
|
data.base_cost = resource.base_cost
|
||||||
|
data.is_scaling = resource.is_scaling
|
||||||
|
data.max_rank = resource.max_rank
|
||||||
|
data.cost_scaling_type = resource.cost_scaling_type
|
||||||
|
data.cost_scaling_multiplier = resource.cost_scaling_multiplier
|
||||||
|
data.cost_linear_increase = resource.cost_linear_increase
|
||||||
|
data.cost_ladder = resource.cost_ladder.duplicate()
|
||||||
|
data.effect_scaling_type = resource.effect_scaling_type
|
||||||
|
data.effect_scaling_multiplier = resource.effect_scaling_multiplier
|
||||||
|
data.effect_linear_increase = resource.effect_linear_increase
|
||||||
|
data.base_modifiers = resource.base_modifiers.duplicate(true)
|
||||||
|
# Start fresh
|
||||||
|
data.is_unlocked = false
|
||||||
|
data.current_rank = 0
|
||||||
|
return data
|
||||||
|
|
||||||
|
## Clone for thread safety (fast - no Resource creation)
|
||||||
|
func clone() -> UnlockDataLightweight:
|
||||||
|
var copy = UnlockDataLightweight.new()
|
||||||
|
copy.unlock_id = unlock_id
|
||||||
|
copy.unlock_name = unlock_name
|
||||||
|
copy.base_cost = base_cost
|
||||||
|
copy.is_scaling = is_scaling
|
||||||
|
copy.max_rank = max_rank
|
||||||
|
copy.cost_scaling_type = cost_scaling_type
|
||||||
|
copy.cost_scaling_multiplier = cost_scaling_multiplier
|
||||||
|
copy.cost_linear_increase = cost_linear_increase
|
||||||
|
copy.cost_ladder = cost_ladder # Shared - read-only
|
||||||
|
copy.effect_scaling_type = effect_scaling_type
|
||||||
|
copy.effect_scaling_multiplier = effect_scaling_multiplier
|
||||||
|
copy.effect_linear_increase = effect_linear_increase
|
||||||
|
copy.base_modifiers = base_modifiers # Shared - read-only
|
||||||
|
# Mutable state
|
||||||
|
copy.is_unlocked = false
|
||||||
|
copy.current_rank = 0
|
||||||
|
return copy
|
||||||
|
|
||||||
|
## Same logic as UnlockDataResource.get_next_cost()
|
||||||
|
func get_next_cost() -> int:
|
||||||
|
if not is_scaling:
|
||||||
|
return base_cost
|
||||||
|
|
||||||
|
if cost_ladder.size() > 0 and current_rank < cost_ladder.size():
|
||||||
|
return cost_ladder[current_rank]
|
||||||
|
|
||||||
|
if cost_scaling_type == 0: # Linear
|
||||||
|
return base_cost + (cost_linear_increase * current_rank)
|
||||||
|
else: # Exponential
|
||||||
|
return int(base_cost * pow(cost_scaling_multiplier, current_rank))
|
||||||
|
|
||||||
|
## Same logic as UnlockDataResource.get_current_modifiers()
|
||||||
|
func get_current_modifiers() -> Dictionary:
|
||||||
|
if not is_unlocked or current_rank == 0:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if current_rank == 1:
|
||||||
|
return base_modifiers.duplicate()
|
||||||
|
|
||||||
|
return get_modifiers_at_rank(current_rank)
|
||||||
|
|
||||||
|
## Same logic as UnlockDataResource.get_modifiers_at_rank()
|
||||||
|
func get_modifiers_at_rank(rank: int) -> Dictionary:
|
||||||
|
if not is_scaling or rank == 0:
|
||||||
|
return base_modifiers.duplicate()
|
||||||
|
|
||||||
|
if rank == 1:
|
||||||
|
return base_modifiers.duplicate()
|
||||||
|
|
||||||
|
var scaled_modifiers = {}
|
||||||
|
for key in base_modifiers.keys():
|
||||||
|
var base_value = base_modifiers[key]
|
||||||
|
|
||||||
|
if effect_scaling_type == 0: # Linear
|
||||||
|
var additional_ranks = rank - 1
|
||||||
|
scaled_modifiers[key] = base_value + (effect_linear_increase * additional_ranks)
|
||||||
|
else: # Exponential
|
||||||
|
var base_bonus = base_value - 1.0
|
||||||
|
var scaled_bonus = base_bonus * pow(effect_scaling_multiplier, rank - 1)
|
||||||
|
scaled_modifiers[key] = 1.0 + scaled_bonus
|
||||||
|
|
||||||
|
return scaled_modifiers
|
||||||
|
|
||||||
|
## Same logic as UnlockDataResource.can_rank_up()
|
||||||
|
func can_rank_up() -> bool:
|
||||||
|
if not is_scaling:
|
||||||
|
return not is_unlocked
|
||||||
|
|
||||||
|
if max_rank > 0 and current_rank >= max_rank:
|
||||||
|
return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
## Same logic as UnlockDataResource.unlock()
|
||||||
|
func unlock() -> bool:
|
||||||
|
if not can_rank_up():
|
||||||
|
return false
|
||||||
|
|
||||||
|
if not is_scaling:
|
||||||
|
is_unlocked = true
|
||||||
|
current_rank = 1
|
||||||
|
else:
|
||||||
|
current_rank += 1
|
||||||
|
is_unlocked = true
|
||||||
|
|
||||||
|
return true
|
||||||
1
scripts/unlock_data_lightweight.gd.uid
Normal file
1
scripts/unlock_data_lightweight.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://yx6cnoob2can
|
||||||
Loading…
Add table
Add a link
Reference in a new issue