Balancing

This commit is contained in:
Dan 2026-01-28 20:18:44 +00:00
parent 90d6c5c926
commit 22d7326565
6 changed files with 116 additions and 105 deletions

View file

@ -1,4 +1,4 @@
[gd_resource type="Resource" script_class="UnlockDataCollection" load_steps=10 format=3 uid="uid://b4c01yrmp1wf2"] [gd_resource type="Resource" script_class="UnlockDataCollection" load_steps=12 format=3 uid="uid://b4c01yrmp1wf2"]
[ext_resource type="Script" uid="uid://bg1ymgbdcwc0j" path="res://resources/UnlockDataCollection.gd" id="1_gdehu"] [ext_resource type="Script" uid="uid://bg1ymgbdcwc0j" path="res://resources/UnlockDataCollection.gd" id="1_gdehu"]
[ext_resource type="Script" uid="uid://biqqffne7dd8r" path="res://resources/UnlockDataResource.gd" id="2_1js7i"] [ext_resource type="Script" uid="uid://biqqffne7dd8r" path="res://resources/UnlockDataResource.gd" id="2_1js7i"]
@ -8,13 +8,13 @@ script = ExtResource("2_1js7i")
unlock_id = 1 unlock_id = 1
unlock_name = "Marketing" unlock_name = "Marketing"
unlock_description = "Affects the amount people are willing to pay for your whittling" unlock_description = "Affects the amount people are willing to pay for your whittling"
base_cost = 200 base_cost = 100
is_scaling = true is_scaling = true
max_rank = 5 max_rank = 8
cost_ladder = [200, 1200, 6000, 25000, 80000] cost_ladder = [100, 350, 1000, 3000, 9000, 28000, 80000, 220000]
effect_scaling_multiplier = 1.6 effect_ladder = [1.08, 1.18, 1.3, 1.45, 1.65, 1.9, 2.3, 3.0]
base_modifiers = { base_modifiers = {
"sale_price_modifier": 1.5 "sale_price_modifier": 1.08
} }
metadata/_custom_type_script = "uid://biqqffne7dd8r" metadata/_custom_type_script = "uid://biqqffne7dd8r"
@ -23,14 +23,13 @@ script = ExtResource("2_1js7i")
unlock_id = 2 unlock_id = 2
unlock_name = "Wood" unlock_name = "Wood"
unlock_description = "Increases the amount of wood produced per click" unlock_description = "Increases the amount of wood produced per click"
base_cost = 100 base_cost = 30
is_scaling = true is_scaling = true
max_rank = 4 max_rank = 5
cost_ladder = [100, 400, 2000, 8000] cost_ladder = [30, 100, 300, 900, 2500]
effect_scaling_type = 0 effect_ladder = [2.0, 3.0, 5.0, 7.0, 10.0]
effect_linear_increase = 5.0
base_modifiers = { base_modifiers = {
"wood_per_click_modifier": 5.0 "wood_per_click_modifier": 2.0
} }
metadata/_custom_type_script = "uid://biqqffne7dd8r" metadata/_custom_type_script = "uid://biqqffne7dd8r"
@ -39,11 +38,11 @@ script = ExtResource("2_1js7i")
unlock_id = 3 unlock_id = 3
unlock_name = "Demand" unlock_name = "Demand"
unlock_description = "How many whittled products can be purchased per tick" unlock_description = "How many whittled products can be purchased per tick"
base_cost = 300 base_cost = 120
is_scaling = true is_scaling = true
max_rank = 4 max_rank = 7
cost_ladder = [300, 1500, 8000, 35000] cost_ladder = [120, 400, 1200, 4000, 12000, 38000, 110000]
effect_scaling_multiplier = 2.0 effect_ladder = [2.0, 3.0, 5.0, 7.0, 10.0, 14.0, 18.0]
base_modifiers = { base_modifiers = {
"purchase_rate_modifier": 2.0 "purchase_rate_modifier": 2.0
} }
@ -54,12 +53,11 @@ script = ExtResource("2_1js7i")
unlock_id = 4 unlock_id = 4
unlock_name = "Efficiency" unlock_name = "Efficiency"
unlock_description = "How many things you can produce per whittle" unlock_description = "How many things you can produce per whittle"
base_cost = 150 base_cost = 60
is_scaling = true is_scaling = true
max_rank = 5 max_rank = 6
cost_ladder = [150, 800, 4000, 18000, 60000] cost_ladder = [60, 400, 1800, 8000, 35000, 140000]
effect_scaling_type = 0 effect_ladder = [2.0, 3.0, 4.0, 5.0, 6.0, 8.0]
effect_linear_increase = 1.0
base_modifiers = { base_modifiers = {
"efficiency_modifier": 2.0 "efficiency_modifier": 2.0
} }
@ -69,8 +67,8 @@ metadata/_custom_type_script = "uid://biqqffne7dd8r"
script = ExtResource("2_1js7i") script = ExtResource("2_1js7i")
unlock_id = 5 unlock_id = 5
unlock_name = "Wholesale" unlock_name = "Wholesale"
unlock_description = "Sell multiples of 100 at 20% extra income" unlock_description = "Sell multiples of 100 at 20% less income"
base_cost = 18000 base_cost = 35000
base_modifiers = { base_modifiers = {
"UNLOCK_ONESHOT_WHOLESALE": 1.0 "UNLOCK_ONESHOT_WHOLESALE": 1.0
} }
@ -81,12 +79,11 @@ script = ExtResource("2_1js7i")
unlock_id = 6 unlock_id = 6
unlock_name = "Multicraft" unlock_name = "Multicraft"
unlock_description = "Just craft more stuff" unlock_description = "Just craft more stuff"
base_cost = 30000 base_cost = 6000
is_scaling = true is_scaling = true
max_rank = 2 max_rank = 5
cost_ladder = [30000, 100000] cost_ladder = [6000, 20000, 55000, 140000, 320000]
effect_scaling_type = 0 effect_ladder = [1.0, 2.0, 3.0, 4.0, 5.0]
effect_linear_increase = 1.0
base_modifiers = { base_modifiers = {
"multicraft_increase_modifier": 1.0 "multicraft_increase_modifier": 1.0
} }
@ -97,18 +94,47 @@ script = ExtResource("2_1js7i")
unlock_id = 7 unlock_id = 7
unlock_name = "Autowood" unlock_name = "Autowood"
unlock_description = "Automatically gather a percent of a clicks wood per tick" unlock_description = "Automatically gather a percent of a clicks wood per tick"
base_cost = 500 base_cost = 150
is_scaling = true is_scaling = true
max_rank = 5 max_rank = 5
cost_ladder = [500, 2000, 8000, 25000, 70000] cost_ladder = [150, 600, 2000, 6500, 20000]
effect_scaling_type = 0 effect_ladder = [0.1, 0.2, 0.35, 0.55, 0.8]
effect_linear_increase = 0.15
base_modifiers = { base_modifiers = {
"autowood_modifier": 0.15 "autowood_modifier": 0.1
}
metadata/_custom_type_script = "uid://biqqffne7dd8r"
[sub_resource type="Resource" id="Resource_premium"]
script = ExtResource("2_1js7i")
unlock_id = 8
unlock_name = "Premium Crafts"
unlock_description = "Your reputation for quality allows higher prices"
base_cost = 8000
is_scaling = true
max_rank = 5
cost_ladder = [8000, 25000, 70000, 160000, 350000]
effect_ladder = [1.08, 1.18, 1.3, 1.45, 1.65]
base_modifiers = {
"premium_price_modifier": 1.08
}
metadata/_custom_type_script = "uid://biqqffne7dd8r"
[sub_resource type="Resource" id="Resource_reputation"]
script = ExtResource("2_1js7i")
unlock_id = 9
unlock_name = "Reputation"
unlock_description = "Loyal customers provide steady passive income"
base_cost = 4000
is_scaling = true
max_rank = 5
cost_ladder = [4000, 12000, 35000, 90000, 200000]
effect_ladder = [4.0, 10.0, 22.0, 42.0, 75.0]
base_modifiers = {
"reputation_income": 4.0
} }
metadata/_custom_type_script = "uid://biqqffne7dd8r" metadata/_custom_type_script = "uid://biqqffne7dd8r"
[resource] [resource]
script = ExtResource("1_gdehu") script = ExtResource("1_gdehu")
unlocks = Array[ExtResource("2_1js7i")]([SubResource("Resource_gdehu"), SubResource("Resource_1js7i"), SubResource("Resource_xbpe0"), SubResource("Resource_nbe0w"), SubResource("Resource_ppuju"), SubResource("Resource_chx6j"), SubResource("Resource_f82ch")]) unlocks = Array[ExtResource("2_1js7i")]([SubResource("Resource_gdehu"), SubResource("Resource_1js7i"), SubResource("Resource_xbpe0"), SubResource("Resource_nbe0w"), SubResource("Resource_ppuju"), SubResource("Resource_chx6j"), SubResource("Resource_f82ch"), SubResource("Resource_premium"), SubResource("Resource_reputation")])
metadata/_custom_type_script = "uid://bg1ymgbdcwc0j" metadata/_custom_type_script = "uid://bg1ymgbdcwc0j"

View file

@ -18,10 +18,7 @@ extends Resource
@export var cost_linear_increase: int = 100 # Only used if cost_scaling_type is Linear @export var cost_linear_increase: int = 100 # Only used if cost_scaling_type is Linear
@export var cost_ladder: Array[int] = [] # Fixed costs per rank (overrides scaling if defined) @export var cost_ladder: Array[int] = [] # Fixed costs per rank (overrides scaling if defined)
@export_subgroup("Effect Scaling") @export var effect_ladder: Array[float] = [] # Effect values per rank
@export_enum("Linear", "Exponential") var effect_scaling_type: int = 1 # Default to Exponential
@export var effect_scaling_multiplier: float = 1.2 # Exponential: multiplier per rank
@export var effect_linear_increase: float = 0.1 # Linear: flat increase per rank (e.g., 0.1 = +10% per rank)
@export_group("Base Modifiers") @export_group("Base Modifiers")
@export var base_modifiers: Dictionary = {} @export var base_modifiers: Dictionary = {}
@ -44,44 +41,27 @@ func get_next_cost() -> int:
## Returns the current modifiers based on rank ## Returns the current modifiers based on rank
func get_current_modifiers() -> Dictionary: func get_current_modifiers() -> Dictionary:
# If not unlocked yet, return empty modifiers
if not is_unlocked or current_rank == 0: if not is_unlocked or current_rank == 0:
return {} return {}
# Rank 1 should give base modifiers without scaling
if current_rank == 1:
return base_modifiers.duplicate()
# Rank 2+ applies scaling
return get_modifiers_at_rank(current_rank) return get_modifiers_at_rank(current_rank)
## Returns modifiers for a specific rank (useful for preview) ## Returns modifiers for a specific rank (useful for preview)
func get_modifiers_at_rank(rank: int) -> Dictionary: func get_modifiers_at_rank(rank: int) -> Dictionary:
if not is_scaling or rank == 0: if rank == 0:
return {}
# For one-shot unlocks or empty ladder, return base_modifiers
if effect_ladder.size() == 0:
return base_modifiers.duplicate() return base_modifiers.duplicate()
# Rank 1 returns base modifiers without scaling var ladder_index = rank - 1
if rank == 1: if ladder_index >= effect_ladder.size():
return base_modifiers.duplicate() ladder_index = effect_ladder.size() - 1 # Use last value if rank exceeds ladder
var scaled_modifiers = {} var result = {}
for key in base_modifiers.keys(): for key in base_modifiers.keys():
var base_value = base_modifiers[key] result[key] = effect_ladder[ladder_index]
return result
if effect_scaling_type == 0: # Linear scaling
# Linear: add flat increase per rank above 1
# e.g., base 1.5 (+50%), linear 0.1 (+10%), rank 2 = 1.6 (+60%), rank 3 = 1.7 (+70%)
var additional_ranks = rank - 1
scaled_modifiers[key] = base_value + (effect_linear_increase * additional_ranks)
else: # Exponential scaling
# Exponential: scale the bonus part from rank 1
# The bonus at rank 1 is (base_value - 1.0)
# At higher ranks, multiply this bonus by multiplier^(rank-1)
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
## Convert a modifier value to a percentage string ## Convert a modifier value to a percentage string
func _modifier_to_percentage(value: float) -> String: func _modifier_to_percentage(value: float) -> String:
@ -133,16 +113,16 @@ func get_modifiers_comparison_string() -> String:
var next_rank = get_next_rank() var next_rank = get_next_rank()
var next = get_modifiers_at_rank(next_rank) var next = get_modifiers_at_rank(next_rank)
var lines = []
# If no current modifiers (not unlocked yet), show next only # If no current modifiers (not unlocked yet), show next only
if current.is_empty(): if current.is_empty():
var lines = []
for key in next.keys(): for key in next.keys():
var display_name = key.replace("_", " ").capitalize() var display_name = key.replace("_", " ").capitalize()
var next_pct = _modifier_to_percentage(next[key]) var next_pct = _modifier_to_percentage(next[key])
lines.append("%s: %s" % [display_name, next_pct]) lines.append("%s: %s" % [display_name, next_pct])
return "\n".join(lines) return "\n".join(lines)
var lines = []
for key in current.keys(): for key in current.keys():
var display_name = key.replace("_", " ").capitalize() var display_name = key.replace("_", " ").capitalize()
var current_pct = _modifier_to_percentage(current[key]) var current_pct = _modifier_to_percentage(current[key])

View file

@ -13,7 +13,7 @@ var wood_color: Color = Color(0.95, 0.6, 0.35) # Light pumpkin orange (autumn le
var stock_color: Color = Color(0.6, 0.75, 0.95) # Light periwinkle blue (clear autumn sky) var stock_color: Color = Color(0.6, 0.75, 0.95) # Light periwinkle blue (clear autumn sky)
# GAMEPLAY VALUES # GAMEPLAY VALUES
var target_currency: float = 1000 var target_currency: float = 1000000
var base_sale_price: float = 30 var base_sale_price: float = 30
var base_wood_respawn: float = 5 # seconds var base_wood_respawn: float = 5 # seconds
var wood_per_click: float = 5 var wood_per_click: float = 5
@ -22,8 +22,11 @@ var base_purchase_rate: float = 1
var wholesale_unlock_id: int = 5 var wholesale_unlock_id: int = 5
var wholesale_bundle_size: int = 100 var wholesale_bundle_size: int = 100
var wholesale_discount_multiplier: float = 1.2 var wholesale_discount_multiplier: float = 0.8
var multicraft_unlock_id: int = 6 var multicraft_unlock_id: int = 6
var autowood_unlock_id: int = 7 var autowood_unlock_id: int = 7
var premium_crafts_unlock_id: int = 8
var reputation_unlock_id: int = 9

View file

@ -28,6 +28,7 @@ func tick():
do_autowood() do_autowood()
do_whittling() do_whittling()
do_selling() do_selling()
do_reputation_income()
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
@ -93,3 +94,9 @@ func whittle_max_wood_possible():
inventory_provider.spend_wood(wood_to_whittle) inventory_provider.spend_wood(wood_to_whittle)
inventory_provider.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.")
func do_reputation_income():
# Add passive income from reputation unlock
var reputation_income = unlocks_provider.get_modifier_value("reputation_income")
if reputation_income > 0:
inventory_provider.add_currency(reputation_income)

View file

@ -18,10 +18,8 @@ var cost_scaling_multiplier: float = 1.5
var cost_linear_increase: int = 100 var cost_linear_increase: int = 100
var cost_ladder: Array[int] = [] var cost_ladder: Array[int] = []
# Effect scaling # Effect ladder
var effect_scaling_type: int = 1 # 0=Linear, 1=Exponential var effect_ladder: Array[float] = []
var effect_scaling_multiplier: float = 1.2
var effect_linear_increase: float = 0.1
# Base modifiers # Base modifiers
var base_modifiers: Dictionary = {} var base_modifiers: Dictionary = {}
@ -38,9 +36,7 @@ static func from_resource(resource: UnlockDataResource) -> UnlockDataLightweight
data.cost_scaling_multiplier = resource.cost_scaling_multiplier data.cost_scaling_multiplier = resource.cost_scaling_multiplier
data.cost_linear_increase = resource.cost_linear_increase data.cost_linear_increase = resource.cost_linear_increase
data.cost_ladder = resource.cost_ladder.duplicate() data.cost_ladder = resource.cost_ladder.duplicate()
data.effect_scaling_type = resource.effect_scaling_type data.effect_ladder = resource.effect_ladder.duplicate()
data.effect_scaling_multiplier = resource.effect_scaling_multiplier
data.effect_linear_increase = resource.effect_linear_increase
data.base_modifiers = resource.base_modifiers.duplicate(true) data.base_modifiers = resource.base_modifiers.duplicate(true)
# Start fresh # Start fresh
data.is_unlocked = false data.is_unlocked = false
@ -59,9 +55,7 @@ func clone() -> UnlockDataLightweight:
copy.cost_scaling_multiplier = cost_scaling_multiplier copy.cost_scaling_multiplier = cost_scaling_multiplier
copy.cost_linear_increase = cost_linear_increase copy.cost_linear_increase = cost_linear_increase
copy.cost_ladder = cost_ladder # Shared - read-only copy.cost_ladder = cost_ladder # Shared - read-only
copy.effect_scaling_type = effect_scaling_type copy.effect_ladder = effect_ladder # Shared - read-only
copy.effect_scaling_multiplier = effect_scaling_multiplier
copy.effect_linear_increase = effect_linear_increase
copy.base_modifiers = base_modifiers # Shared - read-only copy.base_modifiers = base_modifiers # Shared - read-only
# Mutable state # Mutable state
copy.is_unlocked = false copy.is_unlocked = false
@ -85,33 +79,25 @@ func get_next_cost() -> int:
func get_current_modifiers() -> Dictionary: func get_current_modifiers() -> Dictionary:
if not is_unlocked or current_rank == 0: if not is_unlocked or current_rank == 0:
return {} return {}
if current_rank == 1:
return base_modifiers.duplicate()
return get_modifiers_at_rank(current_rank) return get_modifiers_at_rank(current_rank)
## Same logic as UnlockDataResource.get_modifiers_at_rank() ## Same logic as UnlockDataResource.get_modifiers_at_rank()
func get_modifiers_at_rank(rank: int) -> Dictionary: func get_modifiers_at_rank(rank: int) -> Dictionary:
if not is_scaling or rank == 0: if rank == 0:
return {}
# For one-shot unlocks or empty ladder, return base_modifiers
if effect_ladder.size() == 0:
return base_modifiers.duplicate() return base_modifiers.duplicate()
if rank == 1: var ladder_index = rank - 1
return base_modifiers.duplicate() if ladder_index >= effect_ladder.size():
ladder_index = effect_ladder.size() - 1 # Use last value if rank exceeds ladder
var scaled_modifiers = {} var result = {}
for key in base_modifiers.keys(): for key in base_modifiers.keys():
var base_value = base_modifiers[key] result[key] = effect_ladder[ladder_index]
return result
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() ## Same logic as UnlockDataResource.can_rank_up()
func can_rank_up() -> bool: func can_rank_up() -> bool:

View file

@ -10,7 +10,9 @@ var base_modifiers: Dictionary = {
"wood_respawn_modifier": 1.0, "wood_respawn_modifier": 1.0,
"wood_per_click_modifier": 1.0, "wood_per_click_modifier": 1.0,
"purchase_rate_modifier": 1.0, "purchase_rate_modifier": 1.0,
"autowood_modifier": 1.0 "autowood_modifier": 1.0,
"premium_price_modifier": 1.0,
"reputation_income": 0.0
} }
var current_modifiers: Dictionary = base_modifiers.duplicate() var current_modifiers: Dictionary = base_modifiers.duplicate()
@ -31,6 +33,10 @@ func apply_modifiers():
for key in apply_unlock_modifiers.keys(): for key in apply_unlock_modifiers.keys():
if current_modifiers.has(key): if current_modifiers.has(key):
Log.pr(" - Current", key, "modifier before:", current_modifiers[key]) Log.pr(" - Current", key, "modifier before:", current_modifiers[key])
# Reputation income is additive, not multiplicative
if key == "reputation_income":
current_modifiers[key] = apply_unlock_modifiers[key]
else:
current_modifiers[key] *= apply_unlock_modifiers[key] current_modifiers[key] *= apply_unlock_modifiers[key]
Log.pr(" - Applied", key, "modifier:", apply_unlock_modifiers[key], "New value:", current_modifiers[key]) Log.pr(" - Applied", key, "modifier:", apply_unlock_modifiers[key], "New value:", current_modifiers[key])
else: else:
@ -88,7 +94,7 @@ func unlock_item(unlock_id: int) -> bool:
return false return false
func get_sale_price_per_item(): func get_sale_price_per_item():
return Global.base_sale_price * get_modifier_value("sale_price_modifier") return Global.base_sale_price * get_modifier_value("sale_price_modifier") * get_modifier_value("premium_price_modifier")
func get_wood_per_click(): func get_wood_per_click():
return Global.wood_per_click * get_modifier_value("wood_per_click_modifier") return Global.wood_per_click * get_modifier_value("wood_per_click_modifier")
@ -102,5 +108,8 @@ func get_items_produced_per_tick():
func get_sale_demand(): func get_sale_demand():
return Global.base_purchase_rate * get_modifier_value("purchase_rate_modifier") return Global.base_purchase_rate * get_modifier_value("purchase_rate_modifier")
func get_reputation_income():
return get_modifier_value("reputation_income")
func _refresh_ui(): func _refresh_ui():
item_unlocked.emit() item_unlocked.emit()