Add server-managed balance configuration

This commit is contained in:
Emporex committed 2026-10-11 10:47:13 +02:00
1 parent 67abdf13b0
commit 39ed4c3bf7
12 files changed
+289 -27

No files matched your search

+63 -3
View File
@@ -216,6 +216,7 @@ const ENEMY_DATA := [
{"name": "STURMPANZER", "category": "ground", "hp": 390.0, "speed": 61.0, "reward": 61, "size": 92.0, "breach_damage": 3, "shield_resistance": 0.28},
{"name": "BELAGERUNGSLAEUFER", "category": "ground", "hp": 980.0, "speed": 36.0, "reward": 132, "size": 124.0, "breach_damage": 5, "shield_resistance": 0.28}
]
const BALANCE_CACHE_PATH := "user://balance_cache.json"
var path_points := PackedVector2Array()
var path_cells: Dictionary = {}
@@ -405,6 +406,7 @@ var ui_language := "de"
func _ready() -> void:
_load_balance_cache()
_select_map(selected_map)
_load_progress()
_load_audio_settings()
@@ -539,10 +541,65 @@ func _create_promo_code_input() -> void:
func _on_online_authenticated(_user_id: String, _username: String) -> void:
var balance_response: Dictionary = await online_manager.fetch_game_balance()
var balance_config = balance_response.get("config", {})
if balance_config is Dictionary and not balance_config.is_empty():
_save_balance_cache(balance_config)
if game_mode in ["menu", "promo_code", "store", "leaderboard", "profile", "multiplayer_menu", "friends", "room_code_entry", "matchmaking"]:
_apply_balance_config(balance_config)
var entitlements: Dictionary = await online_manager.get_promo_unlocks()
_apply_promo_unlocks(entitlements)
func _load_balance_cache() -> void:
if not FileAccess.file_exists(BALANCE_CACHE_PATH):
return
var file := FileAccess.open(BALANCE_CACHE_PATH, FileAccess.READ)
if file == null:
return
var parsed = JSON.parse_string(file.get_as_text())
if parsed is Dictionary:
_apply_balance_config(parsed)
func _save_balance_cache(config: Dictionary) -> void:
var file := FileAccess.open(BALANCE_CACHE_PATH, FileAccess.WRITE)
if file != null:
file.store_string(JSON.stringify(config))
func _apply_balance_config(config: Dictionary) -> bool:
var towers_config = config.get("towers", [])
var enemies_config = config.get("enemies", [])
if not towers_config is Array or towers_config.size() != TOWER_DATA.size() or not enemies_config is Array or enemies_config.size() != ENEMY_DATA.size():
return false
for values in towers_config:
if not values is Dictionary or str(values.get("targetClass", "both")) not in ["air", "ground", "both"]:
return false
for values in enemies_config:
if not values is Dictionary:
return false
for index in TOWER_DATA.size():
var values: Dictionary = towers_config[index]
var tower: Dictionary = TOWER_DATA[index]
tower["cost"] = clampi(int(values.get("cost", tower.cost)), 10, 50000)
tower["damage"] = clampf(float(values.get("damage", tower.damage)), 0.1, 10000.0)
tower["range"] = clampf(float(values.get("range", tower.range)), 20.0, 1200.0)
tower["rate"] = clampf(float(values.get("cooldown", tower.rate)), 0.04, 20.0)
var target_class := str(values.get("targetClass", tower.get("target_class", "both")))
tower["target_class"] = target_class
tower["both_damage_scale"] = clampf(float(values.get("bothScale", tower.get("both_damage_scale", 1.0))), 0.05, 2.0)
for index in ENEMY_DATA.size():
var values: Dictionary = enemies_config[index]
var enemy: Dictionary = ENEMY_DATA[index]
enemy["hp"] = clampf(float(values.get("hp", enemy.hp)), 1.0, 1000000.0)
enemy["speed"] = clampf(float(values.get("speed", enemy.speed)), 1.0, 1500.0)
enemy["reward"] = clampi(int(values.get("reward", enemy.reward)), 0, 50000)
enemy["breach_damage"] = clampi(int(values.get("breachDamage", enemy.breach_damage)), 1, 100)
enemy["shield_resistance"] = clampf(float(values.get("shieldResistance", enemy.shield_resistance)), 0.01, 1.0)
return true
func _apply_promo_unlocks(entitlements: Dictionary) -> void:
if bool(entitlements.get("all_towers", false)):
unlocked_towers = []
@@ -654,9 +711,12 @@ func _spawn_enemy() -> void:
var type := int(schedule_entry.get("type", _enemy_type_for_wave(wave, randf(), is_boss)))
wave_spawn_index += 1
var data: Dictionary = ENEMY_DATA[type]
var health_scale := _enemy_health_scale(wave, is_boss) * enemy_strength_multiplier
var base_health_scale := _enemy_health_scale(wave, is_boss)
var first_boss_hull_scale := 0.5 if is_boss and wave == 5 else 1.0
var health_scale := base_health_scale * enemy_strength_multiplier * first_boss_hull_scale
var shield_scale := base_health_scale * enemy_strength_multiplier
var has_shield := bool(data.get("default_shield", false)) or (wave >= 4 and randf() < minf(0.42, 0.10 + wave * 0.018 + selected_difficulty * 0.06))
var max_shield_hp: float = data.hp * health_scale * 0.55 if has_shield else 0.0
var max_shield_hp: float = data.hp * shield_scale * 0.55 if has_shield else 0.0
if is_boss and wave == 5:
max_shield_hp *= 0.5
var route_id := 0
@@ -673,7 +733,7 @@ func _spawn_enemy() -> void:
"route_id": route_id,
"hp": data.hp * health_scale,
"max_hp": data.hp * health_scale,
"base_max_hp": data.hp * _enemy_health_scale(wave, is_boss),
"base_max_hp": data.hp * base_health_scale * first_boss_hull_scale,
"speed": data.speed * (1.0 + wave * 0.012) * (0.82 if is_boss else 1.0) * float(_difficulty().speed),
"reward": int((data.reward + wave) * (4 if is_boss else 1) * float(_difficulty().income)),
"angle": 0.0,