Prepare Void Bastion alpha 7 release
This commit is contained in:
1 parent
56077e88a5
commit
e63e08ddde
18 files changed
+799
-95
No files matched your search
@@ -17,6 +17,9 @@ const ENEMY_TEXTURE := preload("res://assets/enemy_ships.png")
|
||||
const ENEMY_INTERCEPTOR_TEXTURE := preload("res://assets/enemy_interceptor.png")
|
||||
const ENEMY_SHIELD_CARRIER_TEXTURE := preload("res://assets/enemy_shield_carrier.png")
|
||||
const ENEMY_SIEGE_DESTROYER_TEXTURE := preload("res://assets/enemy_siege_destroyer.png")
|
||||
const ENEMY_GROUND_ROVER_TEXTURE := preload("res://assets/enemy_ground_rover.png")
|
||||
const ENEMY_GROUND_TANK_TEXTURE := preload("res://assets/enemy_ground_tank.png")
|
||||
const ENEMY_GROUND_WALKER_TEXTURE := preload("res://assets/enemy_ground_walker.png")
|
||||
const ROAD_STRAIGHT_TEXTURE := preload("res://assets/road_straight.svg")
|
||||
const ROAD_CORNER_TEXTURE := preload("res://assets/road_corner.svg")
|
||||
const ROAD_SURFACE_TEXTURE := preload("res://assets/road_surface_material_v1.png")
|
||||
@@ -81,21 +84,24 @@ const DIFFICULTY_DESCRIPTIONS := [
|
||||
]
|
||||
|
||||
const TOWER_DATA := [
|
||||
{"name": "KINETIK", "cost": 120, "damage": 22.0, "range": 190.0, "rate": 0.55, "shot_speed": 620.0, "damage_type": "EINZELZIEL", "color": Color("7de7ff"), "unlock_cost": 0, "description": "Zuverlässiges Projektilgeschütz."},
|
||||
{"name": "LASER", "cost": 180, "damage": 12.0, "range": 165.0, "rate": 0.18, "shot_speed": 1100.0, "damage_type": "EINZELZIEL", "color": Color("62fff1"), "unlock_cost": 0, "description": "Schneller Strahl, stark gegen Schilde."},
|
||||
{"name": "RAKETE", "cost": 250, "damage": 65.0, "range": 260.0, "rate": 1.40, "shot_speed": 360.0, "damage_type": "FLÄCHE 76", "color": Color("ffb35a"), "unlock_cost": 0, "description": "Explosionsschaden gegen Gruppen."},
|
||||
{"name": "PLASMA", "cost": 230, "damage": 48.0, "range": 240.0, "rate": 1.05, "shot_speed": 480.0, "damage_type": "PLASMA-FLÄCHE 92", "color": Color("e27bff"), "unlock_cost": 18, "description": "Plasmaexplosion trifft Gegner im Umkreis."},
|
||||
{"name": "EMP-PULS", "cost": 210, "damage": 34.0, "range": 215.0, "rate": 0.92, "shot_speed": 540.0, "damage_type": "EMP-FLÄCHE 118", "color": Color("77e8ff"), "unlock_cost": 24, "description": "Breiter Impuls trifft ganze Gegnergruppen."},
|
||||
{"name": "RAILGUN", "cost": 300, "damage": 110.0, "range": 330.0, "rate": 1.85, "shot_speed": 1450.0, "damage_type": "SCHILD-DURCHSCHLAG", "color": Color("87efff"), "unlock_cost": 30, "description": "Weitreichender Schuss, ignoriert Schildschutz."}
|
||||
{"name": "KINETIK", "cost": 120, "damage": 22.0, "range": 190.0, "rate": 0.55, "shot_speed": 620.0, "damage_type": "EINZELZIEL", "target_class": "ground", "color": Color("7de7ff"), "unlock_cost": 0, "description": "Zuverlässig gegen Bodenziele."},
|
||||
{"name": "LASER", "cost": 180, "damage": 12.0, "range": 165.0, "rate": 0.18, "shot_speed": 1100.0, "damage_type": "EINZELZIEL", "target_class": "both", "both_damage_scale": 0.82, "color": Color("62fff1"), "unlock_cost": 0, "description": "Mehrzwecklaser; durchdringt Schilde von Luft- und Bodenzielen."},
|
||||
{"name": "RAKETE", "cost": 250, "damage": 65.0, "range": 260.0, "rate": 1.40, "shot_speed": 360.0, "damage_type": "FLÄCHE 76", "target_class": "ground", "color": Color("ffb35a"), "unlock_cost": 0, "description": "Bodenraketen treffen Gegnergruppen."},
|
||||
{"name": "PLASMA", "cost": 230, "damage": 34.0, "range": 240.0, "rate": 1.05, "shot_speed": 480.0, "damage_type": "PLASMA-FLÄCHE 92", "target_class": "both", "both_damage_scale": 0.72, "color": Color("e27bff"), "unlock_cost": 18, "description": "Mehrzweckwaffe mit reduziertem Schaden."},
|
||||
{"name": "EMP-PULS", "cost": 210, "damage": 20.0, "range": 215.0, "rate": 0.92, "shot_speed": 540.0, "damage_type": "EMP-FLÄCHE 118 · ALLE", "target_class": "both", "both_damage_scale": 1.0, "color": Color("77e8ff"), "unlock_cost": 24, "description": "Impuls trifft alle Luft- und Bodenziele im Radius mit 20 Schaden."},
|
||||
{"name": "RAILGUN", "cost": 300, "damage": 77.0, "range": 330.0, "rate": 1.85, "shot_speed": 1450.0, "damage_type": "PIERCING-STRAHL", "target_class": "both", "both_damage_scale": 0.70, "color": Color("c86bff"), "unlock_cost": 30, "description": "Lila Durchschlagsstrahl trifft alle Gegner auf seiner Schusslinie."}
|
||||
]
|
||||
|
||||
const ENEMY_DATA := [
|
||||
{"name": "JAEGER", "hp": 70.0, "speed": 105.0, "reward": 18, "size": 58.0, "breach_damage": 1, "shield_resistance": 0.28},
|
||||
{"name": "FREGATTE", "hp": 185.0, "speed": 72.0, "reward": 34, "size": 78.0, "breach_damage": 2, "shield_resistance": 0.28},
|
||||
{"name": "KREUZER", "hp": 520.0, "speed": 43.0, "reward": 78, "size": 112.0, "breach_damage": 3, "shield_resistance": 0.28},
|
||||
{"name": "ABFANGJAEGER", "hp": 52.0, "speed": 178.0, "reward": 22, "size": 48.0, "breach_damage": 2, "shield_resistance": 0.28},
|
||||
{"name": "SCHILDKREUZER", "hp": 260.0, "speed": 58.0, "reward": 52, "size": 92.0, "breach_damage": 2, "shield_resistance": 0.18, "default_shield": true},
|
||||
{"name": "BELAGERER", "hp": 740.0, "speed": 48.0, "reward": 108, "size": 128.0, "breach_damage": 4, "shield_resistance": 0.28}
|
||||
{"name": "JAEGER", "category": "air", "hp": 70.0, "speed": 105.0, "reward": 18, "size": 58.0, "breach_damage": 1, "shield_resistance": 0.28},
|
||||
{"name": "FREGATTE", "category": "air", "hp": 185.0, "speed": 72.0, "reward": 34, "size": 78.0, "breach_damage": 2, "shield_resistance": 0.28},
|
||||
{"name": "KREUZER", "category": "air", "hp": 520.0, "speed": 43.0, "reward": 78, "size": 112.0, "breach_damage": 3, "shield_resistance": 0.28},
|
||||
{"name": "ABFANGJAEGER", "category": "air", "hp": 52.0, "speed": 178.0, "reward": 22, "size": 48.0, "breach_damage": 2, "shield_resistance": 0.28},
|
||||
{"name": "SCHILDKREUZER", "category": "air", "hp": 260.0, "speed": 58.0, "reward": 52, "size": 92.0, "breach_damage": 2, "shield_resistance": 0.18, "default_shield": true},
|
||||
{"name": "BELAGERER", "category": "air", "hp": 740.0, "speed": 48.0, "reward": 108, "size": 128.0, "breach_damage": 4, "shield_resistance": 0.28},
|
||||
{"name": "SPAEHER-ROVER", "category": "ground", "hp": 88.0, "speed": 142.0, "reward": 22, "size": 54.0, "breach_damage": 1, "shield_resistance": 0.28},
|
||||
{"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}
|
||||
]
|
||||
|
||||
var path_points := PackedVector2Array()
|
||||
@@ -139,6 +145,11 @@ var next_enemy_id := 1
|
||||
var wave_active := false
|
||||
var enemies_to_spawn := 0
|
||||
var spawn_timer := 0.0
|
||||
var wave_type_schedule: Array[Dictionary] = []
|
||||
var wave_spawn_index := 0
|
||||
var online_wave_types_remaining: Array[int] = []
|
||||
var enemy_strength_multiplier := 1.0
|
||||
var developer_panel_open := false
|
||||
var status_text := ""
|
||||
var status_timer := 0.0
|
||||
var elapsed := 0.0
|
||||
@@ -163,6 +174,13 @@ var wave_button := Rect2(1320, 18, 255, 54)
|
||||
var pause_button := Rect2(1242, 18, 62, 54)
|
||||
var speed_button := Rect2(1170, 18, 62, 54)
|
||||
var auto_wave_button := Rect2(1320, 73, 255, 22)
|
||||
var wave_current_preview_rect := Rect2(520, 67, 258, 44)
|
||||
var wave_next_preview_rect := Rect2(786, 67, 258, 44)
|
||||
var developer_panel_rect := Rect2(1060, 104, 515, 78)
|
||||
var developer_strength_down_button := Rect2(1074, 140, 42, 30)
|
||||
var developer_strength_up_button := Rect2(1260, 140, 42, 30)
|
||||
var developer_crystals_down_button := Rect2(1320, 140, 50, 30)
|
||||
var developer_crystals_up_button := Rect2(1512, 140, 50, 30)
|
||||
var back_button := Rect2(1070, 18, 90, 54)
|
||||
var tower_buttons := [
|
||||
Rect2(15, 768, 255, 122), Rect2(278, 768, 255, 122), Rect2(541, 768, 255, 122),
|
||||
@@ -173,6 +191,7 @@ var sell_button := Rect2(1430, 187, 145, 44)
|
||||
var tutorial_skip_button := Rect2(365, 205, 145, 30)
|
||||
var reward_ad_button := Rect2(1070, 690, 300, 54)
|
||||
var tower_info_rect := Rect2(1275, 103, 300, 76)
|
||||
var target_priority_button := Rect2(1275, 236, 300, 34)
|
||||
var endless_button := Rect2(545, 515, 510, 70)
|
||||
var finish_mission_button := Rect2(625, 610, 350, 62)
|
||||
var profile_button := Rect2(1280, 25, 270, 52)
|
||||
@@ -314,10 +333,14 @@ func _update_wave(delta: float) -> void:
|
||||
|
||||
|
||||
func _spawn_enemy() -> void:
|
||||
var is_boss := wave > 0 and wave % BOSS_WAVE_INTERVAL == 0 and enemies_to_spawn <= (2 if coop_mode else 1)
|
||||
var type := _enemy_type_for_wave(wave, randf(), is_boss)
|
||||
var schedule_entry: Dictionary = wave_type_schedule[wave_spawn_index] if wave_spawn_index < wave_type_schedule.size() else {}
|
||||
var is_boss := bool(schedule_entry.get("boss", wave > 0 and wave % BOSS_WAVE_INTERVAL == 0 and enemies_to_spawn <= (2 if coop_mode else 1)))
|
||||
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)
|
||||
var health_scale := _enemy_health_scale(wave, is_boss) * 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 route_id := 0
|
||||
var route := path_points
|
||||
if coop_mode:
|
||||
@@ -332,13 +355,16 @@ 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),
|
||||
"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,
|
||||
"flash": 0.0,
|
||||
"boss": is_boss,
|
||||
"size_scale": 1.34 if is_boss else 1.0,
|
||||
"shield": bool(data.get("default_shield", false)) or (wave >= 4 and randf() < minf(0.42, 0.10 + wave * 0.018 + selected_difficulty * 0.06)),
|
||||
"shield": has_shield,
|
||||
"shield_hp": max_shield_hp,
|
||||
"max_shield_hp": max_shield_hp,
|
||||
"shield_resistance": float(data.shield_resistance)
|
||||
})
|
||||
if is_boss:
|
||||
@@ -348,8 +374,14 @@ func _spawn_enemy() -> void:
|
||||
|
||||
func _enemy_type_for_wave(for_wave: int, roll: float, is_boss: bool) -> int:
|
||||
if is_boss:
|
||||
return 5
|
||||
return 8 if for_wave >= 10 else 5
|
||||
var advanced_offset := 2 if selected_difficulty == 2 else (1 if selected_difficulty == 1 else 0)
|
||||
if for_wave >= 10 and roll >= 0.96:
|
||||
return 8
|
||||
if for_wave >= 8 and roll >= 0.84 and roll < 0.94:
|
||||
return 7
|
||||
if for_wave >= 3 and roll >= 0.52 and roll < 0.64:
|
||||
return 6
|
||||
if for_wave >= 10 - advanced_offset and roll >= 0.94:
|
||||
return 5
|
||||
if for_wave >= 7 - advanced_offset and roll >= 0.80:
|
||||
@@ -363,6 +395,78 @@ func _enemy_type_for_wave(for_wave: int, roll: float, is_boss: bool) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
func _wave_population(for_wave: int) -> int:
|
||||
if for_wave <= 0:
|
||||
return 0
|
||||
var population_scale := 1.0 + maxf(0.0, for_wave - 3.0) * 0.08
|
||||
if selected_difficulty == 2:
|
||||
population_scale += maxf(0.0, for_wave - 4.0) * 0.025
|
||||
if for_wave > MAX_WAVES:
|
||||
population_scale += (for_wave - MAX_WAVES) * 0.035
|
||||
return ceili((7 + for_wave * 2) * float(_difficulty().count) * population_scale) * (2 if coop_mode else 1)
|
||||
|
||||
|
||||
func _build_wave_schedule(for_wave: int, count: int) -> Array[Dictionary]:
|
||||
var schedule: Array[Dictionary] = []
|
||||
var boss_count := mini(2 if coop_mode else 1, count) if for_wave > 0 and for_wave % BOSS_WAVE_INTERVAL == 0 else 0
|
||||
for index in count:
|
||||
var is_boss := index >= count - boss_count
|
||||
var roll := fposmod(float(index + 1) * 0.61803398875 + float(for_wave) * 0.38196601125, 1.0)
|
||||
var type := _enemy_type_for_wave(for_wave, roll, is_boss)
|
||||
schedule.append({"type": type, "boss": is_boss})
|
||||
return schedule
|
||||
|
||||
|
||||
func _enemy_category(enemy_type: int) -> String:
|
||||
return str(ENEMY_DATA[clampi(enemy_type, 0, ENEMY_DATA.size() - 1)].category)
|
||||
|
||||
|
||||
func _tower_target_class(tower_type: int) -> String:
|
||||
return str(TOWER_DATA[clampi(tower_type, 0, TOWER_DATA.size() - 1)].get("target_class", "both"))
|
||||
|
||||
|
||||
func _tower_display_order() -> Array[int]:
|
||||
var ordered_types: Array[int] = []
|
||||
for target_class in ["air", "ground", "both"]:
|
||||
for tower_type in TOWER_DATA.size():
|
||||
if _tower_target_class(tower_type) == target_class:
|
||||
ordered_types.append(tower_type)
|
||||
return ordered_types
|
||||
|
||||
|
||||
func _tower_can_target(tower_type: int, enemy_type: int) -> bool:
|
||||
var target_class := _tower_target_class(tower_type)
|
||||
return target_class == "both" or target_class == _enemy_category(enemy_type)
|
||||
|
||||
|
||||
func _tower_target_label(tower_type: int) -> String:
|
||||
match _tower_target_class(tower_type):
|
||||
"air": return "LUFT"
|
||||
"ground": return "BODEN"
|
||||
_: return "BEIDE"
|
||||
|
||||
|
||||
func _set_enemy_strength_multiplier(value: float) -> void:
|
||||
if coop_online_mode:
|
||||
_show_status("TESTMULTIPLIKATOR IST IM ONLINE-KOOP DEAKTIVIERT")
|
||||
return
|
||||
enemy_strength_multiplier = clampf(roundf(value * 10.0) / 10.0, 0.5, 3.0)
|
||||
for enemy in enemies:
|
||||
var health_ratio := clampf(float(enemy.hp) / maxf(1.0, float(enemy.max_hp)), 0.0, 1.0)
|
||||
var adjusted_max := float(enemy.get("base_max_hp", enemy.max_hp)) * enemy_strength_multiplier
|
||||
enemy.max_hp = adjusted_max
|
||||
enemy.hp = adjusted_max * health_ratio
|
||||
_show_status("TEST: GEGNERSTÄRKE ×%.1f" % enemy_strength_multiplier)
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _adjust_core_crystals(amount: int) -> void:
|
||||
core_crystals = clampi(core_crystals + amount, 0, 999999)
|
||||
_save_progress()
|
||||
_show_status("TEST: KERNKRISTALLE %s25 • STAND %d" % ["+" if amount >= 0 else "−", core_crystals])
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _enemy_health_scale(for_wave: int, is_boss: bool) -> float:
|
||||
var early_ramp := maxf(0.0, for_wave - 1.0) * 0.20
|
||||
var post_four_ramp := maxf(0.0, for_wave - 3.0) * 0.09
|
||||
@@ -407,24 +511,77 @@ func _update_towers(delta: float) -> void:
|
||||
|
||||
func _find_target(tower: Dictionary) -> Dictionary:
|
||||
var best: Dictionary = {}
|
||||
var best_progress := -1.0
|
||||
var priority := "shield" if int(tower.type) == 1 else str(tower.get("target_priority", "first"))
|
||||
var best_progress := -INF if priority != "last" else INF
|
||||
var best_hp := -1.0
|
||||
var best_is_shielded := false
|
||||
var effective_range := _tower_range(tower.type, tower.level)
|
||||
for enemy in enemies:
|
||||
if tower.pos.distance_to(enemy.pos) <= effective_range:
|
||||
if _tower_can_target(tower.type, int(enemy.type)) and tower.pos.distance_to(enemy.pos) <= effective_range:
|
||||
var route: PackedVector2Array = _enemy_route(enemy)
|
||||
var progress: float = enemy.path_index * 1000.0 - enemy.pos.distance_to(route[enemy.path_index])
|
||||
if progress > best_progress:
|
||||
var enemy_hp := float(enemy.hp)
|
||||
var is_shielded := bool(enemy.get("shield", false))
|
||||
var is_better := best.is_empty()
|
||||
match priority:
|
||||
"last": is_better = is_better or progress < best_progress
|
||||
"strongest": is_better = is_better or enemy_hp > best_hp
|
||||
"shield":
|
||||
is_better = is_better or (is_shielded and not best_is_shielded)
|
||||
if is_shielded == best_is_shielded and not best.is_empty():
|
||||
is_better = progress > best_progress
|
||||
_:
|
||||
is_better = is_better or progress > best_progress
|
||||
if is_better:
|
||||
best_progress = progress
|
||||
best_hp = enemy_hp
|
||||
best_is_shielded = is_shielded
|
||||
best = enemy
|
||||
return best
|
||||
|
||||
|
||||
func _tower_priority_label(priority: String) -> String:
|
||||
match priority:
|
||||
"shield": return "SCHILD ZUERST"
|
||||
"last": return "LETZTE"
|
||||
"strongest": return "MEISTE HP"
|
||||
_: return "ERSTE"
|
||||
|
||||
|
||||
func _cycle_selected_tower_priority() -> void:
|
||||
if selected_tower_index < 0 or selected_tower_index >= towers.size():
|
||||
return
|
||||
var tower: Dictionary = towers[selected_tower_index]
|
||||
if int(tower.type) == 1:
|
||||
_show_status("LASER VISIERT SCHILDGEGNER IMMER ZUERST AN")
|
||||
return
|
||||
var priorities := ["first", "last", "strongest", "shield"]
|
||||
var current := priorities.find(str(tower.get("target_priority", "first")))
|
||||
tower.target_priority = priorities[(current + 1) % priorities.size()]
|
||||
_show_status("TURMZIEL: %s" % _tower_priority_label(str(tower.target_priority)))
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _fire_tower(tower: Dictionary, target: Dictionary) -> void:
|
||||
var data: Dictionary = TOWER_DATA[tower.type]
|
||||
var damage := _tower_damage(tower.type, tower.level)
|
||||
if _tower_target_class(tower.type) == "both":
|
||||
damage *= float(data.get("both_damage_scale", 0.7))
|
||||
if tower.type == 1:
|
||||
_apply_damage(target, damage, int(tower.get("owner", 0)), tower.type)
|
||||
beams.append({"from": tower.pos, "to": target.pos, "ttl": 0.09, "color": data.color})
|
||||
elif tower.type == 5:
|
||||
var direction: Vector2 = tower.pos.direction_to(target.pos)
|
||||
var beam_end: Vector2 = tower.pos + direction * _tower_range(tower.type, tower.level)
|
||||
for enemy in enemies.duplicate():
|
||||
if not _tower_can_target(tower.type, int(enemy.type)):
|
||||
continue
|
||||
var offset: Vector2 = enemy.pos - tower.pos
|
||||
var along_beam: float = offset.dot(direction)
|
||||
var across_beam: float = absf(offset.cross(direction))
|
||||
if along_beam >= 0.0 and along_beam <= _tower_range(tower.type, tower.level) and across_beam <= 18.0:
|
||||
_apply_damage(enemy, damage, int(tower.get("owner", 0)), tower.type)
|
||||
beams.append({"from": tower.pos, "to": beam_end, "ttl": 0.14, "color": data.color, "piercing": true})
|
||||
else:
|
||||
projectiles.append({
|
||||
"pos": tower.pos,
|
||||
@@ -448,9 +605,9 @@ func _update_projectiles(delta: float) -> void:
|
||||
if shot.pos.distance_to(target.pos) < 14.0:
|
||||
if shot.type in [2, 3, 4]:
|
||||
var blast_radius := 76.0 if shot.type == 2 else (92.0 if shot.type == 3 else 118.0)
|
||||
var splash_factor := 0.45 if shot.type == 2 else (0.62 if shot.type == 3 else 0.38)
|
||||
var splash_factor := 0.45 if shot.type == 2 else (0.62 if shot.type == 3 else 1.0)
|
||||
for enemy in enemies.duplicate():
|
||||
if enemy.pos.distance_to(target.pos) < blast_radius:
|
||||
if _tower_can_target(int(shot.type), int(enemy.type)) and enemy.pos.distance_to(target.pos) < blast_radius:
|
||||
_apply_damage(enemy, shot.damage if enemy.id == target.id else shot.damage * splash_factor, int(shot.get("owner", 0)), int(shot.type))
|
||||
var blast_color: Color = Color("ff9b4a") if shot.type == 2 else (Color("db65ff") if shot.type == 3 else Color("71eaff"))
|
||||
_spawn_burst(target.pos, blast_color, 14 if shot.type == 2 else 18)
|
||||
@@ -464,8 +621,19 @@ func _apply_damage(enemy: Dictionary, amount: float, owner := 0, weapon_type :=
|
||||
if not enemies.has(enemy):
|
||||
return
|
||||
var applied_damage := amount
|
||||
if bool(enemy.get("shield", false)) and weapon_type not in [1, 5]:
|
||||
applied_damage *= float(enemy.get("shield_resistance", 0.28))
|
||||
if bool(enemy.get("shield", false)):
|
||||
if weapon_type == 1:
|
||||
var shield_hp := float(enemy.get("shield_hp", enemy.max_hp * 0.55))
|
||||
var shield_damage := minf(shield_hp, amount)
|
||||
shield_hp -= shield_damage
|
||||
enemy.shield_hp = shield_hp
|
||||
if shield_hp <= 0.0:
|
||||
enemy.shield = false
|
||||
applied_damage = amount - shield_damage
|
||||
else:
|
||||
applied_damage = 0.0
|
||||
elif weapon_type != 5:
|
||||
applied_damage *= float(enemy.get("shield_resistance", 0.28))
|
||||
enemy.hp -= applied_damage
|
||||
enemy.flash = 0.06
|
||||
if enemy.hp <= 0.0:
|
||||
@@ -508,6 +676,10 @@ func _update_effects(delta: float) -> void:
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed and not event.echo and event.keycode == KEY_F9 and game_mode in ["playing", "paused"]:
|
||||
developer_panel_open = not developer_panel_open
|
||||
queue_redraw()
|
||||
return
|
||||
if event is InputEventKey and event.pressed and not event.echo and event.keycode in [KEY_ESCAPE, KEY_BACK, KEY_Q]:
|
||||
if audio_settings_open:
|
||||
audio_settings_open = false
|
||||
@@ -573,6 +745,21 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
|
||||
func _handle_pointer(position: Vector2, is_touch := false) -> void:
|
||||
_play_ui_sound()
|
||||
if developer_panel_open and game_mode in ["playing", "paused"]:
|
||||
if developer_strength_down_button.has_point(position):
|
||||
_set_enemy_strength_multiplier(enemy_strength_multiplier - 0.1)
|
||||
return
|
||||
if developer_strength_up_button.has_point(position):
|
||||
_set_enemy_strength_multiplier(enemy_strength_multiplier + 0.1)
|
||||
return
|
||||
if developer_crystals_down_button.has_point(position):
|
||||
_adjust_core_crystals(-25)
|
||||
return
|
||||
if developer_crystals_up_button.has_point(position):
|
||||
_adjust_core_crystals(25)
|
||||
return
|
||||
if developer_panel_rect.has_point(position):
|
||||
return
|
||||
if audio_settings_open:
|
||||
if audio_close_button.has_point(position) or not audio_panel_rect.has_point(position):
|
||||
audio_settings_open = false
|
||||
@@ -812,9 +999,10 @@ func _handle_pointer(position: Vector2, is_touch := false) -> void:
|
||||
if wave_button.has_point(position):
|
||||
_start_wave()
|
||||
return
|
||||
var tower_display_order := _tower_display_order()
|
||||
for index in tower_buttons.size():
|
||||
if tower_buttons[index].has_point(position):
|
||||
var tower_type := index
|
||||
var tower_type: int = tower_display_order[index]
|
||||
if not unlocked_towers.has(tower_type):
|
||||
store_return_mode = game_mode
|
||||
game_mode = "store"
|
||||
@@ -827,6 +1015,12 @@ func _handle_pointer(position: Vector2, is_touch := false) -> void:
|
||||
tutorial_step = 1
|
||||
return
|
||||
if selected_tower_index >= 0:
|
||||
if target_priority_button.has_point(position):
|
||||
if coop_online_mode:
|
||||
_show_status("ZIELPRIORITÄT IST IM ONLINE-KOOP NOCH NICHT EINSTELLBAR")
|
||||
else:
|
||||
_cycle_selected_tower_priority()
|
||||
return
|
||||
if upgrade_button.has_point(position):
|
||||
_upgrade_selected()
|
||||
return
|
||||
@@ -1164,12 +1358,9 @@ func _start_wave() -> void:
|
||||
_show_status("WELLENSTART AN SERVER GESENDET")
|
||||
return
|
||||
wave += 1
|
||||
var wave_population_scale := 1.0 + maxf(0.0, wave - 3.0) * 0.08
|
||||
if selected_difficulty == 2:
|
||||
wave_population_scale += maxf(0.0, wave - 4.0) * 0.025
|
||||
if endless_mode:
|
||||
wave_population_scale += maxf(0.0, wave - MAX_WAVES) * 0.035
|
||||
enemies_to_spawn = ceili((7 + wave * 2) * float(_difficulty().count) * wave_population_scale) * (2 if coop_mode else 1)
|
||||
enemies_to_spawn = _wave_population(wave)
|
||||
wave_spawn_index = 0
|
||||
wave_type_schedule = _build_wave_schedule(wave, enemies_to_spawn)
|
||||
spawn_timer = 0.15
|
||||
wave_active = true
|
||||
if coop_ai_mode:
|
||||
@@ -1339,7 +1530,7 @@ func _place_tower(position: Vector2) -> void:
|
||||
return
|
||||
_spend_credits(0, int(data.cost))
|
||||
towers_built_this_run += 1
|
||||
towers.append({"pos": position, "type": selected_tower_type, "level": 1, "cooldown": 0.15, "angle": 0.0, "owner": 0})
|
||||
towers.append({"pos": position, "type": selected_tower_type, "level": 1, "cooldown": 0.15, "angle": 0.0, "owner": 0, "target_priority": "shield" if selected_tower_type == 1 else "first"})
|
||||
selected_tower_index = towers.size() - 1
|
||||
selected_tower_type = -1
|
||||
pending_touch_build_position = Vector2(-1, -1)
|
||||
@@ -1611,6 +1802,9 @@ func _apply_online_snapshot(snapshot: Dictionary) -> void:
|
||||
wave = int(snapshot.get("wave", wave))
|
||||
wave_active = bool(snapshot.get("wave_active", false))
|
||||
enemies_to_spawn = int(snapshot.get("enemies_to_spawn", 0))
|
||||
online_wave_types_remaining.clear()
|
||||
for enemy_type in snapshot.get("wave_types_remaining", []):
|
||||
online_wave_types_remaining.append(int(enemy_type))
|
||||
credits = _player_credits(local_player_slot)
|
||||
|
||||
var previous_enemy_positions: Dictionary = {}
|
||||
@@ -1896,8 +2090,13 @@ func _draw_world() -> void:
|
||||
draw_circle(shot.pos, 6.0 if shot.type == 2 else 3.5, shot.color)
|
||||
draw_circle(shot.pos, 12.0 if shot.type == 2 else 7.0, Color(shot.color, 0.20))
|
||||
for beam in beams:
|
||||
draw_line(beam.from, beam.to, Color(beam.color, 0.92), 3.0)
|
||||
draw_line(beam.from, beam.to, Color.WHITE, 1.0)
|
||||
if bool(beam.get("piercing", false)):
|
||||
draw_line(beam.from, beam.to, Color("c86bff", 0.18), 18.0)
|
||||
draw_line(beam.from, beam.to, Color("c86bff", 0.9), 8.0)
|
||||
draw_line(beam.from, beam.to, Color("f3ddff", 1.0), 2.0)
|
||||
else:
|
||||
draw_line(beam.from, beam.to, Color(beam.color, 0.92), 3.0)
|
||||
draw_line(beam.from, beam.to, Color.WHITE, 1.0)
|
||||
for particle in particles:
|
||||
draw_circle(particle.pos, maxf(1.0, particle.ttl * 8.0), Color(particle.color, minf(1.0, particle.ttl * 3.0)))
|
||||
|
||||
@@ -2045,22 +2244,17 @@ func _draw_tower(tower: Dictionary) -> void:
|
||||
|
||||
func _draw_enemy(enemy: Dictionary) -> void:
|
||||
var enemy_type := int(enemy.type)
|
||||
var enemy_texture: Texture2D = ENEMY_TEXTURE
|
||||
var source: Rect2
|
||||
var destination: Rect2
|
||||
var size: float = ENEMY_DATA[enemy.type].size * enemy.size_scale
|
||||
if enemy_type < 3:
|
||||
var source_width: float = ENEMY_TEXTURE.get_width() / 3.0
|
||||
source = Rect2(source_width * enemy_type, 0, source_width, ENEMY_TEXTURE.get_height())
|
||||
destination = Rect2(-size * 0.5, -size * 0.5, size, size)
|
||||
else:
|
||||
enemy_texture = [ENEMY_INTERCEPTOR_TEXTURE, ENEMY_SHIELD_CARRIER_TEXTURE, ENEMY_SIEGE_DESTROYER_TEXTURE][enemy_type - 3]
|
||||
source = Rect2(Vector2.ZERO, enemy_texture.get_size())
|
||||
destination = Rect2(-size * 0.75, -size * 0.5, size * 1.5, size)
|
||||
var enemy_texture := _enemy_texture(enemy_type)
|
||||
var source := _enemy_source_rect(enemy_type)
|
||||
destination = Rect2(-size * 0.75, -size * 0.5, size * 1.5, size)
|
||||
var tint := Color.WHITE if enemy.flash <= 0.0 else Color("ffb6a3")
|
||||
if bool(enemy.get("shield", false)):
|
||||
draw_circle(enemy.pos, size * 0.63, Color(0.25, 0.80, 1.0, 0.12))
|
||||
draw_arc(enemy.pos, size * 0.63, 0.0, TAU, 28, Color(0.38, 0.86, 1.0, 0.76), 2.2)
|
||||
var shield_ratio := clampf(float(enemy.get("shield_hp", 1.0)) / maxf(1.0, float(enemy.get("max_shield_hp", 1.0))), 0.0, 1.0)
|
||||
draw_circle(enemy.pos, size * 0.63, Color(0.25, 0.80, 1.0, 0.08))
|
||||
draw_arc(enemy.pos, size * 0.63, 0.0, TAU, 40, Color(0.28, 0.66, 0.82, 0.28), 2.0)
|
||||
draw_arc(enemy.pos, size * 0.63, -PI * 0.5, -PI * 0.5 - TAU * shield_ratio, 48, Color(0.38, 0.86, 1.0, 0.92), 3.2)
|
||||
draw_set_transform(enemy.pos - camera_offset, enemy.angle, Vector2.ONE)
|
||||
draw_texture_rect_region(enemy_texture, destination, source, tint)
|
||||
draw_set_transform(-camera_offset, 0.0, Vector2.ONE)
|
||||
@@ -2074,6 +2268,22 @@ func _draw_enemy(enemy: Dictionary) -> void:
|
||||
draw_string(ThemeDB.fallback_font, enemy.pos + Vector2(-45, -size * 0.58), "KOMMANDOSCHIFF", HORIZONTAL_ALIGNMENT_CENTER, 90, 11, Color("ffab78"))
|
||||
|
||||
|
||||
func _enemy_texture(enemy_type: int) -> Texture2D:
|
||||
if enemy_type < 3:
|
||||
return ENEMY_TEXTURE
|
||||
if enemy_type < 6:
|
||||
return [ENEMY_INTERCEPTOR_TEXTURE, ENEMY_SHIELD_CARRIER_TEXTURE, ENEMY_SIEGE_DESTROYER_TEXTURE][enemy_type - 3]
|
||||
return [ENEMY_GROUND_ROVER_TEXTURE, ENEMY_GROUND_TANK_TEXTURE, ENEMY_GROUND_WALKER_TEXTURE][enemy_type - 6]
|
||||
|
||||
|
||||
func _enemy_source_rect(enemy_type: int) -> Rect2:
|
||||
var texture := _enemy_texture(enemy_type)
|
||||
if enemy_type < 3:
|
||||
var source_width := texture.get_width() / 3.0
|
||||
return Rect2(source_width * enemy_type, 0.0, source_width, texture.get_height())
|
||||
return Rect2(Vector2.ZERO, texture.get_size())
|
||||
|
||||
|
||||
func _draw_hud() -> void:
|
||||
var font := ThemeDB.fallback_font
|
||||
draw_rect(Rect2(0, 0, 1600, 90), Color(0.015, 0.035, 0.07, 0.92))
|
||||
@@ -2101,15 +2311,27 @@ func _draw_hud() -> void:
|
||||
_draw_button(reward_ad_button, "VIDEO: +150 NOTFALLENERGIE", false, Color("285044"))
|
||||
draw_rect(Rect2(0, 760, 1600, 140), Color(0.012, 0.028, 0.055, 0.97))
|
||||
draw_line(Vector2(0, 760), Vector2(1600, 760), Color("2e7593"), 2.0)
|
||||
var tower_display_order := _tower_display_order()
|
||||
for index in tower_buttons.size():
|
||||
_draw_tower_card(index, index, selected_tower_type == index)
|
||||
var tower_type: int = tower_display_order[index]
|
||||
_draw_tower_card(index, tower_type, selected_tower_type == tower_type)
|
||||
if selected_tower_index >= 0 and selected_tower_index < towers.size():
|
||||
var tower: Dictionary = towers[selected_tower_index]
|
||||
var upgrade_label := "MAX. STUFE" if tower.level >= 3 else "UPGRADE %d" % _upgrade_cost(tower)
|
||||
_draw_button(upgrade_button, upgrade_label, false)
|
||||
_draw_button(sell_button, "VERKAUFEN", false, Color("6b3542"))
|
||||
if not coop_online_mode:
|
||||
var priority_label := "ZIEL: %s" % _tower_priority_label(str(tower.get("target_priority", "first")))
|
||||
if int(tower.type) == 1:
|
||||
priority_label = "ZIEL: SCHILD ZUERST (LASER FIX)"
|
||||
_draw_button(target_priority_button, priority_label, int(tower.type) == 1, Color("26394a"))
|
||||
if selected_tower_type >= 0 or selected_tower_index >= 0:
|
||||
_draw_tower_info_panel()
|
||||
_draw_wave_preview_panel(wave_current_preview_rect, "JETZT", wave)
|
||||
var next_wave := wave + 1
|
||||
if wave >= MAX_WAVES and not endless_mode:
|
||||
next_wave = 0
|
||||
_draw_wave_preview_panel(wave_next_preview_rect, "DANN", next_wave)
|
||||
draw_rect(auto_wave_button, Color("102d40"), true)
|
||||
draw_rect(auto_wave_button, Color("37677b"), false, 1.0)
|
||||
draw_rect(Rect2(auto_wave_button.position + Vector2(7, 4), Vector2(14, 14)), Color("07131f"), true)
|
||||
@@ -2122,11 +2344,99 @@ func _draw_hud() -> void:
|
||||
draw_rect(option_rect, Color("102d40"), true)
|
||||
draw_rect(option_rect, Color("37677b"), false, 1.0)
|
||||
draw_string(font, option_rect.position + Vector2(0, 18), "%.0f×" % SPEED_OPTIONS[speed_index], HORIZONTAL_ALIGNMENT_CENTER, option_rect.size.x, 13, Color("8ff4ff") if is_equal_approx(game_speed, SPEED_OPTIONS[speed_index]) else Color.WHITE)
|
||||
if developer_panel_open:
|
||||
draw_rect(developer_panel_rect, Color(0.008, 0.024, 0.045, 0.96), true)
|
||||
draw_rect(developer_panel_rect, Color("ffad62"), false, 1.5)
|
||||
draw_string(font, developer_panel_rect.position + Vector2(12, 22), "TESTMODUS • F9 ZUM SCHLIESSEN", HORIZONTAL_ALIGNMENT_LEFT, 490, 11, Color("ffc277"))
|
||||
_draw_button(developer_strength_down_button, "−", false, Color("44333a"))
|
||||
draw_string(font, developer_panel_rect.position + Vector2(58, 57), "GEGNER ×%.1f" % enemy_strength_multiplier, HORIZONTAL_ALIGNMENT_CENTER, 128, 13, Color.WHITE)
|
||||
_draw_button(developer_strength_up_button, "+", false, Color("44333a"))
|
||||
_draw_button(developer_crystals_down_button, "−25", false, Color("44333a"))
|
||||
draw_string(font, developer_panel_rect.position + Vector2(310, 57), "KRISTALLE %04d" % core_crystals, HORIZONTAL_ALIGNMENT_CENTER, 136, 13, Color("8ff4ff"))
|
||||
_draw_button(developer_crystals_up_button, "+25", false, Color("44333a"))
|
||||
if status_timer > 0.0:
|
||||
draw_rect(Rect2(560, 108, 480, 44), Color(0.01, 0.035, 0.07, 0.88), true)
|
||||
draw_string(font, Vector2(570, 138), status_text, HORIZONTAL_ALIGNMENT_CENTER, 460, 18, Color("a9efff"))
|
||||
|
||||
|
||||
func _draw_wave_preview_panel(rect: Rect2, heading: String, for_wave: int) -> void:
|
||||
var font := ThemeDB.fallback_font
|
||||
draw_rect(rect, Color(0.008, 0.028, 0.052, 0.90), true)
|
||||
draw_rect(rect, Color(0.22, 0.54, 0.66, 0.68), false, 1.0)
|
||||
var summary := _wave_preview_summary(for_wave)
|
||||
var title := "%s --" % heading if for_wave <= 0 else "%s %02d" % [heading, for_wave]
|
||||
draw_string(font, rect.position + Vector2(8, 12), title, HORIZONTAL_ALIGNMENT_LEFT, 82, 9, Color("96d8e8"))
|
||||
draw_string(font, rect.position + Vector2(99, 12), "%d SCHIFFE" % int(summary.total), HORIZONTAL_ALIGNMENT_RIGHT, 150, 9, Color("dceaf0"))
|
||||
var type_counts: Dictionary = summary.types
|
||||
var type_order: Array = summary.get("type_order", [])
|
||||
for category_info in [{"name": "air", "x": 7.0}, {"name": "ground", "x": 133.0}]:
|
||||
var category: String = category_info.name
|
||||
var category_x: float = float(category_info.x)
|
||||
_draw_wave_category_icon(Vector2(rect.position.x + category_x + 7.0, rect.position.y + 32.0), category, Color("78f29a"))
|
||||
var drawn := 0
|
||||
for type in type_order:
|
||||
if _enemy_category(type) != category:
|
||||
continue
|
||||
if drawn >= 3:
|
||||
break
|
||||
var icon_rect := Rect2(rect.position.x + category_x + 17.0 + drawn * 35.0, rect.position.y + 19.0, 15.0, 13.0)
|
||||
_draw_enemy_miniature(type, icon_rect)
|
||||
draw_string(font, icon_rect.position + Vector2(16, 12), "×%d" % int(type_counts[type]), HORIZONTAL_ALIGNMENT_LEFT, 19, 8, Color("dceaf0"))
|
||||
drawn += 1
|
||||
|
||||
|
||||
func _wave_preview_summary(for_wave: int) -> Dictionary:
|
||||
var summary := {"total": 0, "air": 0, "ground": 0, "types": {}, "type_order": []}
|
||||
for enemy_type in _wave_preview_types(for_wave):
|
||||
var type := int(enemy_type)
|
||||
var category := _enemy_category(type)
|
||||
if not summary.types.has(type):
|
||||
summary.types[type] = 0
|
||||
summary.type_order.append(type)
|
||||
summary.types[type] = int(summary.types[type]) + 1
|
||||
summary[category] = int(summary[category]) + 1
|
||||
summary.total = int(summary.total) + 1
|
||||
return summary
|
||||
|
||||
|
||||
func _wave_preview_types(for_wave: int) -> Array[int]:
|
||||
var result: Array[int] = []
|
||||
if for_wave <= 0:
|
||||
return result
|
||||
if coop_online_mode and for_wave == wave:
|
||||
for enemy in enemies:
|
||||
result.append(int(enemy.type))
|
||||
result.append_array(online_wave_types_remaining)
|
||||
return result
|
||||
if for_wave == wave and not wave_type_schedule.is_empty():
|
||||
for entry in wave_type_schedule:
|
||||
result.append(int(entry.type))
|
||||
return result
|
||||
for entry in _build_wave_schedule(for_wave, _wave_population(for_wave)):
|
||||
result.append(int(entry.type))
|
||||
return result
|
||||
|
||||
|
||||
func _draw_wave_category_icon(center: Vector2, category: String, color: Color) -> void:
|
||||
var direction := 1.0 if category == "air" else -1.0
|
||||
var arrow := PackedVector2Array([
|
||||
center + Vector2(0, -7.0 * direction),
|
||||
center + Vector2(-5, -1.0 * direction),
|
||||
center + Vector2(-2, -1.0 * direction),
|
||||
center + Vector2(-2, 5.0 * direction),
|
||||
center + Vector2(2, 5.0 * direction),
|
||||
center + Vector2(2, -1.0 * direction),
|
||||
center + Vector2(5, -1.0 * direction)
|
||||
])
|
||||
draw_colored_polygon(arrow, color)
|
||||
|
||||
|
||||
func _draw_enemy_miniature(enemy_type: int, rect: Rect2) -> void:
|
||||
draw_rect(rect.grow(1.0), Color(0.008, 0.018, 0.03, 0.88), true)
|
||||
draw_texture_rect_region(_enemy_texture(enemy_type), rect, _enemy_source_rect(enemy_type), Color.WHITE)
|
||||
draw_rect(rect.grow(1.0), Color("4e8194", 0.75), false, 1.0)
|
||||
|
||||
|
||||
func _speed_option_rect(index: int) -> Rect2:
|
||||
return Rect2(speed_button.position.x, speed_button.end.y + 2.0 + index * 28.0, speed_button.size.x, 27.0)
|
||||
|
||||
@@ -2136,16 +2446,29 @@ func _draw_tower_card(slot: int, type: int, active: bool) -> void:
|
||||
var data: Dictionary = TOWER_DATA[type]
|
||||
var font := ThemeDB.fallback_font
|
||||
var unlocked := unlocked_towers.has(type)
|
||||
draw_rect(rect, Color("226c88") if active else (Color("173c55") if unlocked else Color("102b3b")), true)
|
||||
draw_rect(rect, Color("65dfff") if active else (Color("37677b") if unlocked else Color("29485a")), false, 1.5)
|
||||
var class_color := _tower_class_color(type)
|
||||
var panel_color := class_color.lightened(0.28) if active else (class_color.darkened(0.42) if unlocked else class_color.darkened(0.62))
|
||||
draw_rect(rect, panel_color, true)
|
||||
draw_rect(rect, class_color.lightened(0.30) if active else class_color.darkened(0.18), false, 1.5)
|
||||
draw_rect(Rect2(rect.position, Vector2(5, rect.size.y)), class_color.lightened(0.25) if unlocked else class_color.darkened(0.15), true)
|
||||
draw_string(font, rect.position + Vector2(9, 22), data.name, HORIZONTAL_ALIGNMENT_LEFT, 150, 15, Color.WHITE if unlocked else Color("9ab0ba"))
|
||||
draw_string(font, rect.position + Vector2(158, 22), "%d EN" % data.cost, HORIZONTAL_ALIGNMENT_RIGHT, 86, 12, Color("7ce7ff"))
|
||||
draw_string(font, rect.position + Vector2(9, 47), "SCH %.0f • %.2f SCHUSS/S" % [data.damage, _tower_shots_per_second(type, 1)], HORIZONTAL_ALIGNMENT_LEFT, 237, 11, Color("dceaf0"))
|
||||
draw_string(font, rect.position + Vector2(9, 72), "RW %.0f • %s" % [data.range, data.damage_type], HORIZONTAL_ALIGNMENT_LEFT, 237, 10, Color("9fdce9"))
|
||||
draw_string(font, rect.position + Vector2(9, 72), "RW %.0f • %s" % [data.range, _tower_target_label(type)], HORIZONTAL_ALIGNMENT_LEFT, 237, 10, Color("9fdce9"))
|
||||
var hint := "ANTIPPEN • BAUPLATZ WÄHLEN" if unlocked else "GESPERRT • IM SHOP FREISCHALTEN"
|
||||
draw_string(font, rect.position + Vector2(9, 105), hint, HORIZONTAL_ALIGNMENT_LEFT, 237, 10, Color("719aaa") if unlocked else Color("ffcf7a"))
|
||||
|
||||
|
||||
func _tower_class_color(tower_type: int) -> Color:
|
||||
match _tower_target_class(tower_type):
|
||||
"air":
|
||||
return Color("bb5260") # Rot: Luft
|
||||
"ground":
|
||||
return Color("668c65") # Grün: Boden
|
||||
_:
|
||||
return Color("347b98") # Blau-Cyan: Luft + Boden
|
||||
|
||||
|
||||
func _draw_store() -> void:
|
||||
var font := ThemeDB.fallback_font
|
||||
draw_rect(Rect2(Vector2.ZERO, SCREEN), Color(0.005, 0.015, 0.035, 0.70), true)
|
||||
@@ -2181,7 +2504,7 @@ func _draw_store() -> void:
|
||||
draw_texture_rect(textures[slot], Rect2(card.position + Vector2(112, 18), Vector2(136, 136)), false)
|
||||
draw_string(font, card.position + Vector2(20, 190), data.name, HORIZONTAL_ALIGNMENT_CENTER, 320, 23, Color(data.color))
|
||||
draw_string(font, card.position + Vector2(22, 224), "SCH %.0f • %.2f SCHUSS/S" % [data.damage, _tower_shots_per_second(tower_type, 1)], HORIZONTAL_ALIGNMENT_CENTER, 316, 15, Color.WHITE)
|
||||
draw_string(font, card.position + Vector2(22, 251), "RW %.0f • %s" % [data.range, data.damage_type], HORIZONTAL_ALIGNMENT_CENTER, 316, 14, Color("9fdce9"))
|
||||
draw_string(font, card.position + Vector2(22, 251), "RW %.0f • %s" % [data.range, _tower_target_label(tower_type)], HORIZONTAL_ALIGNMENT_CENTER, 316, 14, Color("9fdce9"))
|
||||
draw_string(font, card.position + Vector2(22, 295), data.description, HORIZONTAL_ALIGNMENT_CENTER, 316, 14, Color("c4d7df"))
|
||||
var is_unlocked := unlocked_towers.has(tower_type)
|
||||
var button_text := "FREIGESCHALTET" if is_unlocked else "FREISCHALTEN • %d KERNKRISTALLE" % int(data.unlock_cost)
|
||||
@@ -2212,7 +2535,7 @@ func _draw_tower_info_panel() -> void:
|
||||
elif is_built:
|
||||
draw_string(font, tower_info_rect.position + Vector2(10, 62), "MAXIMALE AUSBAUSTUFE", HORIZONTAL_ALIGNMENT_LEFT, 280, 9, Color("84ffca"))
|
||||
else:
|
||||
draw_string(font, tower_info_rect.position + Vector2(10, 62), str(data.damage_type), HORIZONTAL_ALIGNMENT_LEFT, 280, 9, Color("84ffca"))
|
||||
draw_string(font, tower_info_rect.position + Vector2(10, 62), "%s • %s" % [_tower_target_label(type), str(data.damage_type)], HORIZONTAL_ALIGNMENT_LEFT, 280, 9, Color("84ffca"))
|
||||
|
||||
|
||||
func _draw_button(rect: Rect2, text: String, active: bool, base := Color("173c55")) -> void:
|
||||
|
||||
Reference in new issue
Block a user