All
This commit is contained in:
parent
7a8ee29dcb
commit
0fe23420ab
800 changed files with 16547 additions and 0 deletions
35
scripts/audio.gd
Normal file
35
scripts/audio.gd
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
class_name AudioManager
|
||||
extends Node
|
||||
|
||||
# When the game starts, play background music
|
||||
func _ready():
|
||||
if Global.play_background_music:
|
||||
play_background_music()
|
||||
|
||||
func play_chop_sound():
|
||||
## Pick one of the chopping sounds randomly
|
||||
var chop_sounds = [
|
||||
"res://assets/audio/ogg/SFX/Chopping and Mining/chop 1.ogg",
|
||||
"res://assets/audio/ogg/SFX/Chopping and Mining/chop 2.ogg",
|
||||
"res://assets/audio/ogg/SFX/Chopping and Mining/chop 3.ogg",
|
||||
"res://assets/audio/ogg/SFX/Chopping and Mining/chop 4.ogg"
|
||||
]
|
||||
var random_index = randi() % chop_sounds.size()
|
||||
play_sound_effect(chop_sounds[random_index])
|
||||
|
||||
|
||||
func play_sound_effect(sound_path: String):
|
||||
var sfx_player = AudioStreamPlayer.new()
|
||||
sfx_player.stream = load(sound_path)
|
||||
sfx_player.volume_db = -5 # Set volume for sound effects
|
||||
add_child(sfx_player)
|
||||
sfx_player.play()
|
||||
sfx_player.connect("finished", sfx_player.queue_free)
|
||||
|
||||
func play_background_music():
|
||||
var music_player = AudioStreamPlayer.new()
|
||||
music_player.stream = load("res://assets/audio/background_music.ogg")
|
||||
music_player.volume_db = -10 # Set volume to a comfortable level
|
||||
music_player.autoplay = true
|
||||
add_child(music_player)
|
||||
music_player.play()
|
||||
1
scripts/audio.gd.uid
Normal file
1
scripts/audio.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://brmgwlrichckd
|
||||
23
scripts/game_manager.gd
Normal file
23
scripts/game_manager.gd
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
extends Node
|
||||
var tick: Timer
|
||||
var tick_count: int = 0
|
||||
@onready var tick_process: TickProcess = TickProcess.new()
|
||||
|
||||
func _ready():
|
||||
#var simulator = UnlockSimulator.new()
|
||||
#add_child(simulator)
|
||||
Unlocks.apply_modifiers()
|
||||
setup_tick_timer()
|
||||
|
||||
func setup_tick_timer():
|
||||
tick = Timer.new()
|
||||
tick.wait_time = 1.0
|
||||
tick.connect("timeout", _on_tick_timeout)
|
||||
add_child(tick)
|
||||
tick.start()
|
||||
|
||||
func _on_tick_timeout():
|
||||
tick_count += 1
|
||||
tick_process.tick()
|
||||
Log.pr("Tick", str(tick_count))
|
||||
Log.pr("Current Currency:", Inventory.get_currency())
|
||||
1
scripts/game_manager.gd.uid
Normal file
1
scripts/game_manager.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://beoq13ju6x7pk
|
||||
27
scripts/globals.gd
Normal file
27
scripts/globals.gd
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
extends Node
|
||||
|
||||
# SETTINGS
|
||||
var play_background_music: bool = false
|
||||
|
||||
# STRINGS
|
||||
var currency_symbol: String = "¥"
|
||||
|
||||
# COLORS
|
||||
var money_color: Color = Color(1.0, 0.85, 0.4) # Bright golden yellow (autumn sun)
|
||||
var wood_color: Color = Color(0.95, 0.6, 0.35) # Light pumpkin orange (autumn leaves)
|
||||
var stock_color: Color = Color(0.6, 0.75, 0.95) # Light periwinkle blue (clear autumn sky)
|
||||
|
||||
# GAMEPLAY VALUES
|
||||
var base_sale_price: float = 100
|
||||
var base_wood_respawn: float = 5 # seconds
|
||||
var wood_per_click: float = 5
|
||||
var cost_per_whittle: float = 1 # This is how many items can be produced per tick
|
||||
var base_purchase_rate: float = 1
|
||||
|
||||
var wholesale_unlock_id = 5
|
||||
var wholesale_bundle_size = 100
|
||||
var wholesale_discount_multiplier = 1.2
|
||||
|
||||
var multicraft_unlock_id = 6
|
||||
|
||||
var autowood_unlock_id = 7
|
||||
1
scripts/globals.gd.uid
Normal file
1
scripts/globals.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cgb5ptg2ktqbk
|
||||
6
scripts/inputs.gd
Normal file
6
scripts/inputs.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class_name InputsClass
|
||||
extends Node
|
||||
|
||||
func _ready():
|
||||
var cursor_texture = load("res://assets/ui/cursorHand_blue.png")
|
||||
Input.set_custom_mouse_cursor(cursor_texture, Input.CURSOR_POINTING_HAND, Vector2(0, 0))
|
||||
1
scripts/inputs.gd.uid
Normal file
1
scripts/inputs.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://bp0n5t22hq361
|
||||
63
scripts/inventory.gd
Normal file
63
scripts/inventory.gd
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
class_name InventoryClass
|
||||
extends Node
|
||||
|
||||
signal currency_changed(new_amount: float)
|
||||
signal currency_added(amount: float, new_total: float)
|
||||
signal currency_spent(amount: float, new_total: float)
|
||||
signal wood_changed(new_amount: float)
|
||||
signal wood_added(amount: float, new_total: float)
|
||||
signal wood_spent(amount: float, new_total: float)
|
||||
signal stock_changed(new_amount: float)
|
||||
signal stock_added(amount: float, new_total: float)
|
||||
signal stock_spent(amount: float, new_total: float)
|
||||
|
||||
var inventory: InventoryResource = load("res://resources/InventoryData.tres")
|
||||
|
||||
func get_currency() -> float:
|
||||
return inventory.currency
|
||||
|
||||
func add_currency(amount: float):
|
||||
inventory.currency += amount
|
||||
currency_added.emit(amount, inventory.currency)
|
||||
currency_changed.emit(inventory.currency)
|
||||
|
||||
func spend_currency(amount: float) -> bool:
|
||||
if inventory.currency >= amount:
|
||||
inventory.currency -= amount
|
||||
currency_spent.emit(amount, inventory.currency)
|
||||
currency_changed.emit(inventory.currency)
|
||||
return true
|
||||
return false
|
||||
|
||||
func get_wood() -> float:
|
||||
return inventory.wood
|
||||
|
||||
func add_wood(amount: float):
|
||||
inventory.wood += amount
|
||||
wood_added.emit(amount, inventory.wood)
|
||||
wood_changed.emit(inventory.wood)
|
||||
|
||||
func spend_wood(amount: float) -> bool:
|
||||
if inventory.wood >= amount:
|
||||
inventory.wood -= amount
|
||||
wood_spent.emit(amount, inventory.wood)
|
||||
wood_changed.emit(inventory.wood)
|
||||
return true
|
||||
return false
|
||||
|
||||
func get_stock() -> float:
|
||||
return inventory.stock
|
||||
|
||||
func add_stock(amount: float):
|
||||
Log.pr("Adding stock: " + str(amount))
|
||||
inventory.stock += amount
|
||||
stock_added.emit(amount, inventory.stock)
|
||||
stock_changed.emit(inventory.stock)
|
||||
|
||||
func spend_stock(amount: float) -> bool:
|
||||
if inventory.stock >= amount:
|
||||
inventory.stock -= amount
|
||||
stock_spent.emit(amount, inventory.stock)
|
||||
stock_changed.emit(inventory.stock)
|
||||
return true
|
||||
return false
|
||||
1
scripts/inventory.gd.uid
Normal file
1
scripts/inventory.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://b8nmlowx54de0
|
||||
417
scripts/sim.gd
Normal file
417
scripts/sim.gd
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
class_name UnlockSimulator
|
||||
extends Node
|
||||
|
||||
# Load the actual game resources
|
||||
var unlock_collection: UnlockDataCollection = load("res://resources/UnlockData.tres")
|
||||
var inventory_resource: InventoryResource = load("res://resources/InventoryData.tres")
|
||||
|
||||
# Results tracking
|
||||
var all_results: Array[Dictionary] = []
|
||||
var results_mutex: Mutex = Mutex.new()
|
||||
|
||||
# Manual thread pool
|
||||
var num_threads: int = 12 # Increase this for more CPU usage
|
||||
var threads: Array[Thread] = []
|
||||
var task_queue: Array[Dictionary] = []
|
||||
var queue_mutex: Mutex = Mutex.new()
|
||||
var completed_count: int = 0
|
||||
var completed_mutex: Mutex = Mutex.new()
|
||||
var active_threads: int = 0
|
||||
var threads_done: bool = false
|
||||
|
||||
var start_time: int = 0
|
||||
var total_combinations: int = 0
|
||||
var last_progress_time: int = 0
|
||||
var monitoring_active: bool = false
|
||||
|
||||
func _ready():
|
||||
print("=== Unlock Simulator Started ===")
|
||||
var cpu_count = OS.get_processor_count()
|
||||
print("CPU cores detected: %d" % cpu_count)
|
||||
print("Creating %d worker threads (adjust num_threads variable for more/less)" % num_threads)
|
||||
run_comprehensive_test()
|
||||
|
||||
func _process(_delta):
|
||||
if monitoring_active:
|
||||
# Only update progress once per second
|
||||
var current_time = Time.get_ticks_msec()
|
||||
if current_time - last_progress_time >= 1000:
|
||||
last_progress_time = current_time
|
||||
update_progress()
|
||||
|
||||
func update_progress():
|
||||
"""Update progress display"""
|
||||
var current_count = 0
|
||||
completed_mutex.lock()
|
||||
current_count = completed_count
|
||||
completed_mutex.unlock()
|
||||
|
||||
# Check if all work is complete
|
||||
if current_count >= total_combinations:
|
||||
monitoring_active = false
|
||||
finish_processing()
|
||||
return
|
||||
|
||||
var percent = float(current_count) / total_combinations * 100.0
|
||||
var elapsed = (Time.get_ticks_msec() - start_time) / 1000.0
|
||||
var rate = current_count / elapsed if elapsed > 0 else 0
|
||||
var eta_seconds = (total_combinations - current_count) / rate if rate > 0 else 0
|
||||
|
||||
# Format ETA
|
||||
var eta_str = ""
|
||||
if eta_seconds > 0:
|
||||
var eta_minutes = int(eta_seconds) / 60
|
||||
var eta_secs = int(eta_seconds) % 60
|
||||
if eta_minutes > 0:
|
||||
eta_str = "%dm %ds" % [eta_minutes, eta_secs]
|
||||
else:
|
||||
eta_str = "%ds" % eta_secs
|
||||
else:
|
||||
eta_str = "calculating..."
|
||||
|
||||
print("Progress: %.1f%% (%d/%d) - %.1f combos/sec - ETA: %s" % [
|
||||
percent, current_count, total_combinations, rate, eta_str
|
||||
])
|
||||
|
||||
func worker_thread(thread_id: int):
|
||||
"""Worker thread function that pulls tasks from the queue"""
|
||||
while true:
|
||||
# Get next task from queue
|
||||
var task_data = null
|
||||
queue_mutex.lock()
|
||||
if task_queue.size() > 0:
|
||||
task_data = task_queue.pop_front()
|
||||
queue_mutex.unlock()
|
||||
|
||||
# If no more tasks, exit
|
||||
if task_data == null:
|
||||
break
|
||||
|
||||
# Process the task
|
||||
var result = simulate_rank_combination_pure(task_data.combo, task_data.unlock_data, 100000)
|
||||
|
||||
# Store result
|
||||
results_mutex.lock()
|
||||
all_results.append(result)
|
||||
results_mutex.unlock()
|
||||
|
||||
# Increment counter
|
||||
completed_mutex.lock()
|
||||
completed_count += 1
|
||||
completed_mutex.unlock()
|
||||
|
||||
func simulate_rank_combination_pure(rank_targets: Dictionary, unlock_data_array: Array, max_ticks: int) -> Dictionary:
|
||||
"""Pure simulation function that can run in parallel"""
|
||||
var currency: float = 0.0
|
||||
var stock: float = 0.0
|
||||
|
||||
# Create unlock instances from serialized data
|
||||
var unlocks: Array = []
|
||||
for unlock_data in unlock_data_array:
|
||||
var unlock = UnlockDataResource.new()
|
||||
unlock.unlock_id = unlock_data.unlock_id
|
||||
unlock.unlock_name = unlock_data.unlock_name
|
||||
unlock.base_cost = unlock_data.base_cost
|
||||
unlock.is_scaling = unlock_data.is_scaling
|
||||
unlock.max_rank = unlock_data.max_rank
|
||||
unlock.cost_scaling_multiplier = unlock_data.cost_scaling_multiplier
|
||||
unlock.effect_scaling_multiplier = unlock_data.effect_scaling_multiplier
|
||||
unlock.base_modifiers = unlock_data.base_modifiers.duplicate()
|
||||
unlock.is_unlocked = false
|
||||
unlock.current_rank = 0
|
||||
unlocks.append(unlock)
|
||||
|
||||
var ticks = 0
|
||||
var purchases: Array[Dictionary] = []
|
||||
var current_ranks = {}
|
||||
|
||||
# Initialize current ranks
|
||||
for unlock_id in rank_targets.keys():
|
||||
current_ranks[unlock_id] = 0
|
||||
|
||||
# Helper to check if all targets reached
|
||||
var all_targets_reached = func() -> bool:
|
||||
for unlock_id in rank_targets.keys():
|
||||
if current_ranks[unlock_id] < rank_targets[unlock_id]:
|
||||
return false
|
||||
return true
|
||||
|
||||
# Calculate modifiers helper
|
||||
var calc_modifiers = func() -> Dictionary:
|
||||
var mods = {
|
||||
"sale_price_modifier": 1.0,
|
||||
"speed_modifier": 1.0,
|
||||
"efficiency_modifier": 1.0,
|
||||
"wood_respawn_modifier": 1.0,
|
||||
"wood_per_click_modifier": 1.0,
|
||||
"purchase_rate_modifier": 1.0,
|
||||
}
|
||||
for unlock in unlocks:
|
||||
if unlock.is_unlocked:
|
||||
var unlock_modifiers = unlock.get_current_modifiers()
|
||||
for key in unlock_modifiers.keys():
|
||||
if mods.has(key):
|
||||
mods[key] *= unlock_modifiers[key]
|
||||
return mods
|
||||
|
||||
var modifiers = calc_modifiers.call()
|
||||
|
||||
while ticks < max_ticks and currency < 100000.0:
|
||||
# Try to buy the cheapest available unlock that hasn't reached its target
|
||||
var cheapest_unlock_id = null
|
||||
var cheapest_cost = INF
|
||||
var cheapest_unlock_obj = null
|
||||
|
||||
for unlock_id in rank_targets.keys():
|
||||
if current_ranks[unlock_id] < rank_targets[unlock_id]:
|
||||
# Find the unlock object
|
||||
var unlock = null
|
||||
for u in unlocks:
|
||||
if u.unlock_id == unlock_id:
|
||||
unlock = u
|
||||
break
|
||||
|
||||
if unlock and unlock.can_rank_up():
|
||||
var cost = unlock.get_next_cost()
|
||||
if cost < cheapest_cost and currency >= cost:
|
||||
cheapest_cost = cost
|
||||
cheapest_unlock_id = unlock_id
|
||||
cheapest_unlock_obj = unlock
|
||||
|
||||
# Purchase the cheapest unlock if found
|
||||
if cheapest_unlock_obj != null:
|
||||
currency -= cheapest_cost
|
||||
cheapest_unlock_obj.unlock()
|
||||
current_ranks[cheapest_unlock_id] += 1
|
||||
|
||||
# Recalculate modifiers
|
||||
modifiers = calc_modifiers.call()
|
||||
|
||||
purchases.append({
|
||||
"tick": ticks,
|
||||
"unlock_id": cheapest_unlock_id,
|
||||
"unlock_name": cheapest_unlock_obj.unlock_name,
|
||||
"rank": cheapest_unlock_obj.current_rank,
|
||||
"currency": currency,
|
||||
"cost_paid": cheapest_cost,
|
||||
"modifiers_after": modifiers.duplicate()
|
||||
})
|
||||
|
||||
# Simulate one tick
|
||||
var items_per_tick = Global.cost_per_whittle * modifiers.get("efficiency_modifier", 1.0)
|
||||
stock += items_per_tick
|
||||
|
||||
var demand = Global.base_purchase_rate * modifiers.get("purchase_rate_modifier", 1.0)
|
||||
var items_sold = min(stock, demand)
|
||||
stock -= items_sold
|
||||
|
||||
var price_per_item = Global.base_sale_price * modifiers.get("sale_price_modifier", 1.0)
|
||||
var revenue = items_sold * price_per_item
|
||||
currency += revenue
|
||||
|
||||
ticks += 1
|
||||
|
||||
# Check if we've reached target and 10K
|
||||
if all_targets_reached.call() and currency >= 100000.0:
|
||||
break
|
||||
|
||||
var success = currency >= 100000.0
|
||||
|
||||
return {
|
||||
"rank_targets": rank_targets,
|
||||
"success": success,
|
||||
"ticks": ticks if success else -1,
|
||||
"final_currency": currency,
|
||||
"purchases": purchases,
|
||||
"time_formatted": format_time(ticks) if success else "Failed"
|
||||
}
|
||||
|
||||
func format_time(ticks: int) -> String:
|
||||
var seconds = ticks
|
||||
var minutes = seconds / 60
|
||||
var hours = minutes / 60
|
||||
|
||||
if hours > 0:
|
||||
return "%dh %dm %ds" % [hours, minutes % 60, seconds % 60]
|
||||
elif minutes > 0:
|
||||
return "%dm %ds" % [minutes, seconds % 60]
|
||||
else:
|
||||
return "%ds" % seconds
|
||||
|
||||
func generate_rank_combinations(max_ranks_per_unlock: int = 10) -> Array[Dictionary]:
|
||||
"""Generate all combinations of ranks for the first 4 unlocks"""
|
||||
var combinations: Array[Dictionary] = []
|
||||
|
||||
# Get first 4 unlock IDs
|
||||
var unlock_ids = []
|
||||
for i in range(min(4, unlock_collection.unlocks.size())):
|
||||
unlock_ids.append(unlock_collection.unlocks[i].unlock_id)
|
||||
|
||||
print("Generating combinations for unlocks: ", unlock_ids)
|
||||
|
||||
# Generate all combinations (0 to max_ranks for each unlock)
|
||||
for rank1 in range(max_ranks_per_unlock + 1):
|
||||
for rank2 in range(max_ranks_per_unlock + 1):
|
||||
for rank3 in range(max_ranks_per_unlock + 1):
|
||||
for rank4 in range(max_ranks_per_unlock + 1):
|
||||
# Skip the all-zeros case
|
||||
if rank1 == 0 and rank2 == 0 and rank3 == 0 and rank4 == 0:
|
||||
continue
|
||||
|
||||
var combination = {}
|
||||
if rank1 > 0:
|
||||
combination[unlock_ids[0]] = rank1
|
||||
if rank2 > 0:
|
||||
combination[unlock_ids[1]] = rank2
|
||||
if rank3 > 0:
|
||||
combination[unlock_ids[2]] = rank3
|
||||
if rank4 > 0:
|
||||
combination[unlock_ids[3]] = rank4
|
||||
|
||||
combinations.append(combination)
|
||||
|
||||
return combinations
|
||||
|
||||
func serialize_unlock_data() -> Array:
|
||||
"""Convert unlock collection to serializable data for threads"""
|
||||
var unlock_data = []
|
||||
for unlock in unlock_collection.unlocks:
|
||||
unlock_data.append({
|
||||
"unlock_id": unlock.unlock_id,
|
||||
"unlock_name": unlock.unlock_name,
|
||||
"base_cost": unlock.base_cost,
|
||||
"is_scaling": unlock.is_scaling,
|
||||
"max_rank": unlock.max_rank,
|
||||
"cost_scaling_multiplier": unlock.cost_scaling_multiplier,
|
||||
"effect_scaling_multiplier": unlock.effect_scaling_multiplier,
|
||||
"base_modifiers": unlock.base_modifiers.duplicate()
|
||||
})
|
||||
return unlock_data
|
||||
|
||||
func run_comprehensive_test(max_ranks: int = 10):
|
||||
"""Test all combinations of ranks up to max_ranks for each unlock"""
|
||||
print("\n=== Available Unlocks ===")
|
||||
for unlock in unlock_collection.unlocks:
|
||||
print("ID: %d | %s | Base Cost: %d | Scaling: %s" % [
|
||||
unlock.unlock_id,
|
||||
unlock.unlock_name,
|
||||
unlock.base_cost,
|
||||
"Yes" if unlock.is_scaling else "No"
|
||||
])
|
||||
print(" Modifiers: ", unlock.base_modifiers)
|
||||
|
||||
print("\n=== Global Constants ===")
|
||||
print("Base Sale Price: %s" % Global.base_sale_price)
|
||||
print("Base Purchase Rate: %s" % Global.base_purchase_rate)
|
||||
print("Cost Per Whittle: %s" % Global.cost_per_whittle)
|
||||
|
||||
# Generate all combinations
|
||||
var combinations = generate_rank_combinations(max_ranks)
|
||||
total_combinations = combinations.size()
|
||||
print("\n=== Testing %d Combinations ===" % total_combinations)
|
||||
|
||||
# Serialize unlock data for threads
|
||||
var unlock_data = serialize_unlock_data()
|
||||
|
||||
# Fill task queue
|
||||
task_queue.clear()
|
||||
for combo in combinations:
|
||||
task_queue.append({
|
||||
"combo": combo,
|
||||
"unlock_data": unlock_data
|
||||
})
|
||||
|
||||
# Reset counters
|
||||
completed_count = 0
|
||||
all_results.clear()
|
||||
threads_done = false
|
||||
start_time = Time.get_ticks_msec()
|
||||
last_progress_time = start_time
|
||||
monitoring_active = true
|
||||
|
||||
# Create and start threads
|
||||
print("Starting %d worker threads..." % num_threads)
|
||||
for i in range(num_threads):
|
||||
var thread = Thread.new()
|
||||
thread.start(worker_thread.bind(i))
|
||||
threads.append(thread)
|
||||
|
||||
print("All threads started, processing...")
|
||||
|
||||
func finish_processing():
|
||||
"""Called when all processing is complete"""
|
||||
print("\nAll combinations complete! Waiting for threads to finish...")
|
||||
|
||||
# Wait for all threads to finish
|
||||
for thread in threads:
|
||||
thread.wait_to_finish()
|
||||
threads.clear()
|
||||
threads_done = true
|
||||
|
||||
print("All threads finished. Processing results...")
|
||||
|
||||
var total_time = (Time.get_ticks_msec() - start_time) / 1000.0
|
||||
|
||||
# Print results
|
||||
print("\n=== RESULTS ===")
|
||||
print("Total time: %.1f seconds" % total_time)
|
||||
print("Total combinations tested: %d" % all_results.size())
|
||||
|
||||
var successful = all_results.filter(func(r): return r.success)
|
||||
print("Successful strategies: %d" % successful.size())
|
||||
|
||||
if successful.size() > 0:
|
||||
# Sort by ticks (fastest first)
|
||||
successful.sort_custom(func(a, b): return a.ticks < b.ticks)
|
||||
|
||||
print("\n=== TOP 10 FASTEST STRATEGIES ===")
|
||||
for i in range(min(10, successful.size())):
|
||||
var result = successful[i]
|
||||
print("\n#%d: %s (%d ticks)" % [i + 1, result.time_formatted, result.ticks])
|
||||
|
||||
# Format ranks with unlock names
|
||||
var rank_display = []
|
||||
for unlock_id in result.rank_targets.keys():
|
||||
var unlock_name = get_unlock_name_by_id(unlock_id)
|
||||
var ranks = result.rank_targets[unlock_id]
|
||||
rank_display.append("%s: %d" % [unlock_name, ranks])
|
||||
print("Target Ranks: %s" % ", ".join(rank_display))
|
||||
|
||||
# Show purchase order
|
||||
print("Purchase Order:")
|
||||
for purchase in result.purchases:
|
||||
var key_mods = ""
|
||||
if purchase.has("modifiers_after"):
|
||||
var mods = purchase.modifiers_after
|
||||
key_mods = " [Sale:%.2fx Eff:%.2fx Demand:%.2fx]" % [
|
||||
mods.get("sale_price_modifier", 1.0),
|
||||
mods.get("efficiency_modifier", 1.0),
|
||||
mods.get("purchase_rate_modifier", 1.0)
|
||||
]
|
||||
var cost_info = ""
|
||||
if purchase.has("cost_paid"):
|
||||
cost_info = " (paid %d)" % purchase.cost_paid
|
||||
print(" @%s: %s -> Rank %d%s - %.0f currency%s" % [
|
||||
format_time(purchase.tick),
|
||||
purchase.unlock_name,
|
||||
purchase.rank,
|
||||
cost_info,
|
||||
purchase.currency,
|
||||
key_mods
|
||||
])
|
||||
else:
|
||||
print("\nNo successful strategies found!")
|
||||
|
||||
func get_unlock_name_by_id(unlock_id: int) -> String:
|
||||
"""Helper function to get unlock name by ID"""
|
||||
for unlock in unlock_collection.unlocks:
|
||||
if unlock.unlock_id == unlock_id:
|
||||
return unlock.unlock_name
|
||||
return "Unknown"
|
||||
|
||||
func _exit_tree():
|
||||
# Clean up threads
|
||||
monitoring_active = false
|
||||
for thread in threads:
|
||||
if thread.is_alive():
|
||||
thread.wait_to_finish()
|
||||
1
scripts/sim.gd.uid
Normal file
1
scripts/sim.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://daala7g4otu04
|
||||
73
scripts/tick_process.gd
Normal file
73
scripts/tick_process.gd
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
class_name TickProcess
|
||||
extends Node
|
||||
|
||||
func tick():
|
||||
Log.pr("Tick Process Ticking...")
|
||||
do_autowood()
|
||||
do_whittling()
|
||||
do_selling()
|
||||
|
||||
func do_autowood():
|
||||
# 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)
|
||||
if autowood_unlock and autowood_unlock.is_unlocked:
|
||||
Log.pr("Autowood modifier", str(Unlocks.get_modifier_value("autowood_modifier")))
|
||||
var wood_to_gather = max(Unlocks.get_wood_per_click() * Unlocks.get_modifier_value("autowood_modifier"), 1)
|
||||
Inventory.add_wood(wood_to_gather)
|
||||
Log.pr("Auto-gathered", str(wood_to_gather), "wood via autowood unlock.")
|
||||
|
||||
func do_whittling():
|
||||
# If there's more than 1 whole wood available, then whittle based on the efficiency modifier
|
||||
if Inventory.get_wood() >= 1:
|
||||
whittle_max_wood_possible()
|
||||
|
||||
## If multicraft is unlocked, whittle additional wood based on multicraft unlock
|
||||
var multicraft_unlock = Unlocks.get_unlock_by_id(Global.multicraft_unlock_id)
|
||||
if multicraft_unlock and multicraft_unlock.is_unlocked:
|
||||
var additional_whittles = multicraft_unlock.current_rank # Each rank allows one additional whittling action
|
||||
for i in range(additional_whittles):
|
||||
if Inventory.get_wood() >= 1:
|
||||
whittle_max_wood_possible()
|
||||
else:
|
||||
break
|
||||
|
||||
func do_selling():
|
||||
# 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)
|
||||
if wholesale_unlock and wholesale_unlock.is_unlocked:
|
||||
while Inventory.get_stock() >= Global.wholesale_bundle_size:
|
||||
Inventory.spend_stock(Global.wholesale_bundle_size)
|
||||
var currency_earned = Global.wholesale_bundle_size * Unlocks.get_sale_price_per_item() * Global.wholesale_discount_multiplier
|
||||
Inventory.add_currency(currency_earned)
|
||||
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 Inventory.get_stock() > 0:
|
||||
var whittle_wood_to_sell = Inventory.get_stock()
|
||||
# Sell whatever people are willing to buy
|
||||
var purchase_rate = Global.base_purchase_rate * Unlocks.get_modifier_value("purchase_rate_modifier")
|
||||
var max_stock_to_sell = floor(purchase_rate)
|
||||
|
||||
# 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
|
||||
var actual_stock_to_sell = min(whittle_wood_to_sell, max(1, max_stock_to_sell))
|
||||
|
||||
Inventory.spend_stock(actual_stock_to_sell)
|
||||
var currency_earned = actual_stock_to_sell * Unlocks.get_sale_price_per_item()
|
||||
Inventory.add_currency(currency_earned)
|
||||
|
||||
|
||||
func whittle_max_wood_possible():
|
||||
# Get the items that can be produced per tick
|
||||
var items_produced_per_tick = Unlocks.get_items_produced_per_tick()
|
||||
Log.pr("Items produced per tick:", str(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
|
||||
var wood_to_whittle = min(Inventory.get_wood(), wood_needed)
|
||||
var actual_items_produced = wood_to_whittle
|
||||
|
||||
Inventory.spend_wood(wood_to_whittle)
|
||||
Inventory.add_stock(actual_items_produced)
|
||||
Log.pr("Whittled", str(wood_to_whittle), "wood into", str(actual_items_produced), "whittle wood.")
|
||||
1
scripts/tick_process.gd.uid
Normal file
1
scripts/tick_process.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://oxqv2ru5tj0t
|
||||
106
scripts/unlocks.gd
Normal file
106
scripts/unlocks.gd
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
class_name UnlocksClass
|
||||
extends Node
|
||||
|
||||
var unlocks: UnlockDataCollection = load("res://resources/UnlockData.tres")
|
||||
|
||||
var base_modifiers: Dictionary = {
|
||||
"sale_price_modifier": 1.0,
|
||||
"speed_modifier": 1.0,
|
||||
"efficiency_modifier": 1.0,
|
||||
"wood_respawn_modifier": 1.0,
|
||||
"wood_per_click_modifier": 1.0,
|
||||
"purchase_rate_modifier": 1.0,
|
||||
"autowood_modifier": 1.0
|
||||
}
|
||||
|
||||
var current_modifiers: Dictionary = base_modifiers.duplicate()
|
||||
|
||||
signal item_unlocked()
|
||||
|
||||
func reset_modifiers():
|
||||
current_modifiers = base_modifiers.duplicate()
|
||||
Log.pr("Modifiers reset to base values.")
|
||||
|
||||
func apply_modifiers():
|
||||
Log.pr("Applying modifiers for unlocked items...")
|
||||
reset_modifiers()
|
||||
for unlock in unlocks.unlocks:
|
||||
if unlock.is_unlocked:
|
||||
Log.pr("Applying modifier for unlocked item:", unlock.unlock_name)
|
||||
var apply_unlock_modifiers = unlock.get_current_modifiers()
|
||||
for key in apply_unlock_modifiers.keys():
|
||||
if current_modifiers.has(key):
|
||||
Log.pr(" - Current", key, "modifier before:", current_modifiers[key])
|
||||
current_modifiers[key] *= apply_unlock_modifiers[key]
|
||||
Log.pr(" - Applied", key, "modifier:", apply_unlock_modifiers[key], "New value:", current_modifiers[key])
|
||||
else:
|
||||
Log.pr(" - Warning: Unknown modifier key:", key)
|
||||
|
||||
func get_modifier_value(modifier_key: String) -> float:
|
||||
if current_modifiers.has(modifier_key):
|
||||
return current_modifiers[modifier_key]
|
||||
return 1.0
|
||||
|
||||
func get_unlock_by_id(unlock_id: int) -> UnlockDataResource:
|
||||
for unlock in unlocks.unlocks:
|
||||
if unlock.unlock_id == unlock_id:
|
||||
return unlock
|
||||
return null
|
||||
|
||||
func unlock_item(unlock_id: int) -> bool:
|
||||
var unlock_data = get_unlock_by_id(unlock_id)
|
||||
if not unlock_data:
|
||||
return false
|
||||
|
||||
# Check if this unlock can be ranked up (handles both scaling and non-scaling)
|
||||
if not unlock_data.can_rank_up():
|
||||
Log.pr("Cannot rank up:", unlock_data.unlock_name, "- Already at max rank/unlocked")
|
||||
return false
|
||||
|
||||
# Get the cost for the next rank/unlock
|
||||
var cost = unlock_data.get_next_cost()
|
||||
|
||||
# Try to spend the currency
|
||||
if Inventory.spend_currency(cost):
|
||||
# Store previous rank for logging
|
||||
var previous_rank = unlock_data.current_rank
|
||||
|
||||
# Unlock or rank up
|
||||
unlock_data.unlock()
|
||||
|
||||
# Log appropriate message based on unlock type
|
||||
if unlock_data.is_scaling:
|
||||
Log.pr("Ranked up %s: Rank %d -> %d" % [unlock_data.unlock_name, previous_rank, unlock_data.current_rank])
|
||||
else:
|
||||
Log.pr("Unlocked:", unlock_data.unlock_name)
|
||||
|
||||
# Apply modifiers again (now using updated rank)
|
||||
apply_modifiers()
|
||||
call_deferred("_refresh_ui")
|
||||
|
||||
return true
|
||||
else:
|
||||
if unlock_data.is_scaling:
|
||||
Log.pr("Not enough currency to rank up %s to rank %d (Cost: %d)" % [unlock_data.unlock_name, unlock_data.get_next_rank(), cost])
|
||||
else:
|
||||
Log.pr("Not enough currency to unlock %s (Cost: %d)" % [unlock_data.unlock_name, cost])
|
||||
|
||||
return false
|
||||
|
||||
func get_sale_price_per_item():
|
||||
return Global.base_sale_price * get_modifier_value("sale_price_modifier")
|
||||
|
||||
func get_wood_per_click():
|
||||
return Global.wood_per_click * get_modifier_value("wood_per_click_modifier")
|
||||
|
||||
func get_wood_respawn_time():
|
||||
return Global.base_wood_respawn * get_modifier_value("wood_respawn_modifier")
|
||||
|
||||
func get_items_produced_per_tick():
|
||||
return Global.cost_per_whittle * get_modifier_value("efficiency_modifier")
|
||||
|
||||
func get_sale_demand():
|
||||
return Global.base_purchase_rate * get_modifier_value("purchase_rate_modifier")
|
||||
|
||||
func _refresh_ui():
|
||||
item_unlocked.emit()
|
||||
1
scripts/unlocks.gd.uid
Normal file
1
scripts/unlocks.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://p52rtx3sv8jc
|
||||
Loading…
Add table
Add a link
Reference in a new issue