Add update alerts and leaderboard filters

This commit is contained in:
Emporex committed 2026-10-04 22:12:04 +02:00
1 parent 178a6bbc85
commit 6124331067
6 files changed
+239 -44

No files matched your search

+105 -38
View File
@@ -23,9 +23,9 @@ const MAP_TEXTURES := [
const MAP_NAMES := ["BASTION PRIME", "HELIOS-WERFT", "PYRAX-RAFFINERIE"]
const MAP_DESCRIPTIONS := ["Dreifache Feuerlinie", "Langer Werft-Korridor", "Gefährlicher Asteroidenring"]
const MAP_PATHS := [
[Vector2(-40, 120), Vector2(1160, 120), Vector2(1160, 360), Vector2(360, 360), Vector2(360, 680), Vector2(1560, 680), Vector2(1560, 520), Vector2(1640, 520)],
[Vector2(-40, 280), Vector2(1320, 280), Vector2(1320, 440), Vector2(280, 440), Vector2(280, 680), Vector2(1480, 680), Vector2(1480, 440), Vector2(1640, 440)],
[Vector2(-40, 120), Vector2(360, 120), Vector2(360, 600), Vector2(1320, 600), Vector2(1320, 280), Vector2(1640, 280)]
[Vector2(-40, 120), Vector2(1160, 120), Vector2(1160, 360), Vector2(360, 360), Vector2(360, 680), Vector2(1560, 680), Vector2(1560, 520), Vector2(1480, 520), Vector2(1480, 440)],
[Vector2(-40, 280), Vector2(1320, 280), Vector2(1320, 440), Vector2(280, 440), Vector2(280, 680), Vector2(1560, 680), Vector2(1560, 440), Vector2(1480, 440)],
[Vector2(-40, 120), Vector2(360, 120), Vector2(360, 600), Vector2(1320, 600), Vector2(1320, 280), Vector2(1560, 280), Vector2(1560, 360), Vector2(1480, 360)]
]
const MAP_BUILD_ZONES := [
[Rect2(155, 95, 1380, 660)],
@@ -33,16 +33,16 @@ const MAP_BUILD_ZONES := [
[Rect2(180, 110, 1270, 645)]
]
const MAP_REACTOR_ZONES := [
[{"center": Vector2(1470, 430), "radius": 190.0}],
[{"center": Vector2(1480, 430), "radius": 185.0}],
[{"center": Vector2(1480, 440), "radius": 190.0}],
[{"center": Vector2(1480, 440), "radius": 185.0}],
[
{"center": Vector2(1450, 355), "radius": 165.0},
{"center": Vector2(1480, 360), "radius": 165.0},
{"center": Vector2(690, 385), "radius": 155.0}
]
]
const COOP_MIRROR_X := 1480.0
const COOP_WORLD_WIDTH := COOP_MIRROR_X * 2.0
const COOP_LEFT_PATH := [Vector2(-40, 120), Vector2(1160, 120), Vector2(1160, 360), Vector2(360, 360), Vector2(360, 680), Vector2(1400, 680), Vector2(1400, 520), Vector2(1480, 520)]
const COOP_LEFT_PATH := [Vector2(-40, 120), Vector2(1160, 120), Vector2(1160, 360), Vector2(360, 360), Vector2(360, 680), Vector2(1400, 680), Vector2(1400, 520), Vector2(1480, 520), Vector2(1480, 440)]
const COOP_REACTOR_CENTER := Vector2(COOP_MIRROR_X, 440)
const COOP_REACTOR_RADIUS := 112.0
@@ -125,17 +125,25 @@ var upgrade_button := Rect2(1000, 814, 270, 64)
var sell_button := Rect2(1290, 814, 285, 64)
var tutorial_skip_button := Rect2(365, 205, 145, 30)
var reward_ad_button := Rect2(1070, 690, 300, 54)
var tower_info_rect := Rect2(1135, 105, 440, 104)
var tower_info_rect := Rect2(1275, 103, 300, 76)
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)
var leaderboard_button := Rect2(250, 705, 350, 60)
var leaderboard_back_button := Rect2(55, 35, 185, 54)
var leaderboard_mission_button := Rect2(510, 155, 270, 54)
var leaderboard_endless_button := Rect2(820, 155, 270, 54)
var leaderboard_mission_button := Rect2(510, 212, 270, 54)
var leaderboard_endless_button := Rect2(820, 212, 270, 54)
var leaderboard_refresh_button := Rect2(1265, 35, 280, 54)
var leaderboard_map_previous_button := Rect2(235, 150, 58, 50)
var leaderboard_map_button := Rect2(303, 150, 410, 50)
var leaderboard_map_next_button := Rect2(723, 150, 58, 50)
var leaderboard_difficulty_previous_button := Rect2(819, 150, 58, 50)
var leaderboard_difficulty_button := Rect2(887, 150, 300, 50)
var leaderboard_difficulty_next_button := Rect2(1197, 150, 58, 50)
var leaderboard_rows: Array = []
var leaderboard_endless := false
var leaderboard_map := 0
var leaderboard_difficulty := 1
var leaderboard_loading := false
var leaderboard_message := ""
var profile_back_button := Rect2(55, 35, 185, 54)
@@ -160,11 +168,16 @@ var profile_privacy_button := Rect2(840, 685, 250, 48)
var profile_delete_button := Rect2(1110, 685, 250, 48)
var profile_name_input_rect := Rect2(460, 330, 680, 82)
var profile_name_save_button := Rect2(625, 470, 350, 62)
var update_button := Rect2(30, 25, 390, 54)
var update_dismiss_button := Rect2(430, 25, 54, 54)
var available_update: Dictionary = {}
var update_notice_visible := false
@onready var ads_manager = $AdsManager
@onready var account_manager = $AccountManager
@onready var multiplayer_manager = $MultiplayerManager
@onready var online_manager = $OnlineManager
@onready var update_manager = $UpdateManager
func _ready() -> void:
@@ -175,6 +188,7 @@ func _ready() -> void:
multiplayer_manager.online_game_ready.connect(_start_coop_online_game)
multiplayer_manager.online_snapshot_updated.connect(_apply_online_snapshot)
multiplayer_manager.online_event_received.connect(_handle_online_event)
update_manager.update_available.connect(_on_update_available)
queue_redraw()
@@ -435,6 +449,13 @@ func _unhandled_input(event: InputEvent) -> void:
func _handle_pointer(position: Vector2, is_touch := false) -> void:
if game_mode == "menu":
if update_notice_visible and update_button.has_point(position):
OS.shell_open(str(available_update.get("url", "https://void-bastion.gergames.de/")))
return
if update_notice_visible and update_dismiss_button.has_point(position):
update_notice_visible = false
queue_redraw()
return
if profile_button.has_point(position):
game_mode = "profile"
queue_redraw()
@@ -467,6 +488,18 @@ func _handle_pointer(position: Vector2, is_touch := false) -> void:
_refresh_leaderboard()
elif leaderboard_refresh_button.has_point(position):
_refresh_leaderboard()
elif leaderboard_map_previous_button.has_point(position):
leaderboard_map = wrapi(leaderboard_map - 1, 0, MAP_NAMES.size())
_refresh_leaderboard()
elif leaderboard_map_button.has_point(position) or leaderboard_map_next_button.has_point(position):
leaderboard_map = wrapi(leaderboard_map + 1, 0, MAP_NAMES.size())
_refresh_leaderboard()
elif leaderboard_difficulty_previous_button.has_point(position):
leaderboard_difficulty = wrapi(leaderboard_difficulty - 1, 0, DIFFICULTY_DATA.size())
_refresh_leaderboard()
elif leaderboard_difficulty_button.has_point(position) or leaderboard_difficulty_next_button.has_point(position):
leaderboard_difficulty = wrapi(leaderboard_difficulty + 1, 0, DIFFICULTY_DATA.size())
_refresh_leaderboard()
queue_redraw()
return
if game_mode == "multiplayer_menu":
@@ -612,6 +645,7 @@ func _select_map(index: int) -> void:
path_points = PackedVector2Array()
for point in MAP_PATHS[selected_map]:
path_points.append(point * WORLD_SCALE)
_validate_route_ends_at_reactor(path_points, selected_map)
_rebuild_path_cells()
build_zones.clear()
for zone in MAP_BUILD_ZONES[selected_map]:
@@ -632,6 +666,19 @@ func _configure_coop_arena() -> void:
_add_path_cells(route)
build_zones = [Rect2(120, PLAYFIELD_TOP, 1280, PLAYFIELD_BOTTOM - PLAYFIELD_TOP), Rect2(1560, PLAYFIELD_TOP, 1280, PLAYFIELD_BOTTOM - PLAYFIELD_TOP)]
camera_offset = Vector2.ZERO
if left[-1].distance_to(COOP_REACTOR_CENTER) > 1.0 or right[-1].distance_to(COOP_REACTOR_CENTER) > 1.0:
push_error("Koop-Routen müssen sichtbar im gemeinsamen Reaktor enden.")
func _validate_route_ends_at_reactor(route: PackedVector2Array, map_index: int) -> bool:
if route.is_empty() or map_index < 0 or map_index >= MAP_REACTOR_ZONES.size():
push_error("Kartenroute oder Reaktordaten fehlen.")
return false
var reactor_center: Vector2 = MAP_REACTOR_ZONES[map_index][0].center
var is_connected := route[-1].distance_to(reactor_center) <= 1.0
if not is_connected:
push_error("Karte %s: Der Laufweg muss exakt im Reaktormittelpunkt enden." % MAP_NAMES[map_index])
return is_connected
func _toggle_coop_side() -> void:
@@ -812,6 +859,8 @@ func _delete_profile_account() -> void:
func _open_leaderboard() -> void:
game_mode = "leaderboard"
leaderboard_endless = false
leaderboard_map = selected_map
leaderboard_difficulty = selected_difficulty
_refresh_leaderboard()
@@ -827,7 +876,7 @@ func _refresh_leaderboard() -> void:
leaderboard_message = "GERGAMES-SERVER WIRD VERBUNDEN – BITTE GLEICH ERNEUT LADEN"
queue_redraw()
return
leaderboard_rows = await online_manager.load_leaderboard(selected_map, selected_difficulty, leaderboard_endless, 12)
leaderboard_rows = await online_manager.load_leaderboard(leaderboard_map, leaderboard_difficulty, leaderboard_endless, 12)
leaderboard_loading = false
leaderboard_message = "NOCH KEINE EINTRÄGE" if leaderboard_rows.is_empty() else ""
queue_redraw()
@@ -977,6 +1026,12 @@ func _difficulty() -> Dictionary:
return DIFFICULTY_DATA[clampi(selected_difficulty, 0, DIFFICULTY_DATA.size() - 1)]
func _on_update_available(info: Dictionary) -> void:
available_update = info.duplicate(true)
update_notice_visible = true
queue_redraw()
func _on_energy_reward_granted(amount: int) -> void:
_award_credits(0, amount)
_show_status("NOTFALLENERGIE +%d" % amount)
@@ -1407,10 +1462,10 @@ func _draw_world() -> void:
var valid_position := _is_valid_build_position(snapped_position, _local_credit_slot())
var placement_color := Color(0.25, 1.0, 0.7, 0.22) if valid_position else Color(1.0, 0.2, 0.2, 0.22)
var preview_range := _tower_range(selected_tower_type, 1)
draw_circle(snapped_position, preview_range, Color(placement_color, 0.09))
draw_arc(snapped_position, preview_range, 0, TAU, 96, Color(placement_color, 0.82), 3.0)
draw_rect(Rect2(snapped_position - Vector2(35, 35), Vector2(70, 70)), placement_color, true)
draw_rect(Rect2(snapped_position - Vector2(35, 35), Vector2(70, 70)), Color(placement_color, 0.95), false, 3.0)
draw_circle(snapped_position, preview_range, Color(placement_color, 0.025))
draw_arc(snapped_position, preview_range, 0, TAU, 96, Color(placement_color, 0.46), 1.5)
draw_rect(Rect2(snapped_position - Vector2(35, 35), Vector2(70, 70)), Color(placement_color, 0.10), true)
draw_rect(Rect2(snapped_position - Vector2(35, 35), Vector2(70, 70)), Color(placement_color, 0.62), false, 1.5)
_draw_tower({"pos": snapped_position, "type": selected_tower_type, "level": 1, "angle": 0.0})
for tower_index in towers.size():
var tower: Dictionary = towers[tower_index]
@@ -1472,8 +1527,8 @@ func _draw_route() -> void:
func _draw_build_grid() -> void:
var line_color := Color(0.24, 0.82, 1.0, 0.22 if selected_tower_type >= 0 else 0.10)
var fill_color := Color(0.08, 0.55, 0.72, 0.08 if selected_tower_type >= 0 else 0.025)
var line_color := Color(0.24, 0.82, 1.0, 0.14 if selected_tower_type >= 0 else 0.10)
var fill_color := Color(0.08, 0.55, 0.72, 0.035 if selected_tower_type >= 0 else 0.025)
for x_index in int(_active_world_width() / GRID_SIZE):
for y_index in int(WORLD_SIZE.y / GRID_SIZE):
var center := Vector2(x_index * GRID_SIZE + GRID_SIZE * 0.5, y_index * GRID_SIZE + GRID_SIZE * 0.5)
@@ -1587,18 +1642,18 @@ func _draw_tower_info_panel() -> void:
return
var data: Dictionary = TOWER_DATA[type]
var font := ThemeDB.fallback_font
draw_rect(tower_info_rect, Color(0.008, 0.028, 0.055, 0.94), true)
draw_rect(tower_info_rect, Color("4a9ab2"), false, 2.0)
draw_rect(tower_info_rect, Color(0.008, 0.028, 0.055, 0.58), true)
draw_rect(tower_info_rect, Color(0.29, 0.60, 0.70, 0.72), false, 1.5)
var heading := "%s • STUFE %d" % [data.name, level] if is_built else "%s • BAUVORSCHAU" % data.name
draw_string(font, tower_info_rect.position + Vector2(14, 26), heading, HORIZONTAL_ALIGNMENT_LEFT, 412, 17, Color("a9efff"))
draw_string(font, tower_info_rect.position + Vector2(14, 52), "SCH %.0f | %.2f/S | RW %.0f | %s" % [_tower_damage(type, level), _tower_shots_per_second(type, level), _tower_range(type, level), data.damage_type], HORIZONTAL_ALIGNMENT_LEFT, 412, 13, Color.WHITE)
draw_string(font, tower_info_rect.position + Vector2(10, 20), heading, HORIZONTAL_ALIGNMENT_LEFT, 280, 13, Color("a9efff"))
draw_string(font, tower_info_rect.position + Vector2(10, 41), "SCH %.0f • %.2f/S • RW %.0f" % [_tower_damage(type, level), _tower_shots_per_second(type, level), _tower_range(type, level)], HORIZONTAL_ALIGNMENT_LEFT, 280, 10, Color.WHITE)
if is_built and level < 3:
var next_level := level + 1
draw_string(font, tower_info_rect.position + Vector2(14, 80), "UPGRADE: +%.0f SCH +%.2f/S +%.0f RW" % [_tower_damage(type, next_level) - _tower_damage(type, level), _tower_shots_per_second(type, next_level) - _tower_shots_per_second(type, level), _tower_range(type, next_level) - _tower_range(type, level)], HORIZONTAL_ALIGNMENT_LEFT, 412, 13, Color("84ffca"))
draw_string(font, tower_info_rect.position + Vector2(10, 62), "UPGRADE +%.0f SCH +%.2f/S +%.0f RW" % [_tower_damage(type, next_level) - _tower_damage(type, level), _tower_shots_per_second(type, next_level) - _tower_shots_per_second(type, level), _tower_range(type, next_level) - _tower_range(type, level)], HORIZONTAL_ALIGNMENT_LEFT, 280, 9, Color("84ffca"))
elif is_built:
draw_string(font, tower_info_rect.position + Vector2(14, 80), "MAXIMALE AUSBAUSTUFE ERREICHT", HORIZONTAL_ALIGNMENT_LEFT, 412, 13, Color("84ffca"))
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(14, 80), "Reichweite wird vor dem Bau angezeigt.", HORIZONTAL_ALIGNMENT_LEFT, 412, 13, Color("84ffca"))
draw_string(font, tower_info_rect.position + Vector2(10, 62), str(data.damage_type), HORIZONTAL_ALIGNMENT_LEFT, 280, 9, Color("84ffca"))
func _draw_button(rect: Rect2, text: String, active: bool, base := Color("173c55")) -> void:
@@ -1638,7 +1693,10 @@ func _draw_menu() -> void:
_draw_button(leaderboard_button, "BESTENLISTE", false, Color("24475b"))
_draw_button(multiplayer_button, "MULTIPLAYER", false, Color("254f75"))
draw_string(font, Vector2(0, 792), "BESTE WELLE %02d • KERNKRISTALLE %04d • KARTENBONUS %d" % [best_wave, core_crystals, int(_difficulty().crystals)], HORIZONTAL_ALIGNMENT_CENTER, 1600, 17, Color("86c8d8"))
draw_string(font, Vector2(0, 860), "VERSION 0.23 • GERGAMES-ONLINEKONTO • GLOBALE BESTENLISTEN", HORIZONTAL_ALIGNMENT_CENTER, 1600, 14, Color("688293"))
draw_string(font, Vector2(0, 860), "VERSION %s • GERGAMES-ONLINEKONTO • GLOBALE BESTENLISTEN" % _app_version(), HORIZONTAL_ALIGNMENT_CENTER, 1600, 14, Color("688293"))
if update_notice_visible:
_draw_button(update_button, "UPDATE %s VERFÜGBAR" % str(available_update.get("version", "")), true, Color("1f5d4d"))
_draw_button(update_dismiss_button, "×", false, Color("3d4650"))
func _draw_leaderboard() -> void:
@@ -1647,28 +1705,37 @@ func _draw_leaderboard() -> void:
_draw_button(leaderboard_back_button, "< ZURÜCK", false, Color("26394a"))
_draw_button(leaderboard_refresh_button, "AKTUALISIEREN", leaderboard_loading, Color("24516a"))
draw_string(font, Vector2(0, 108), "GLOBALE BESTENLISTE", HORIZONTAL_ALIGNMENT_CENTER, 1600, 42, Color("c7f5ff"))
draw_string(font, Vector2(0, 142), "%s • %s" % [MAP_NAMES[selected_map], DIFFICULTY_DATA[selected_difficulty].name], HORIZONTAL_ALIGNMENT_CENTER, 1600, 16, Color("66dff3"))
_draw_button(leaderboard_map_previous_button, "<", false, Color("26394a"))
_draw_button(leaderboard_map_button, "KARTE • %s" % MAP_NAMES[leaderboard_map], true, Color("24475b"))
_draw_button(leaderboard_map_next_button, ">", false, Color("26394a"))
_draw_button(leaderboard_difficulty_previous_button, "<", false, Color("26394a"))
_draw_button(leaderboard_difficulty_button, "STUFE • %s" % DIFFICULTY_DATA[leaderboard_difficulty].name, true, Color("5b452b"))
_draw_button(leaderboard_difficulty_next_button, ">", false, Color("26394a"))
_draw_button(leaderboard_mission_button, "MISSION", not leaderboard_endless, Color("24475b"))
_draw_button(leaderboard_endless_button, "ENDLOSMODUS", leaderboard_endless, Color("5b452b"))
var panel := Rect2(235, 235, 1130, 525)
var panel := Rect2(205, 280, 1190, 505)
draw_rect(panel, Color(0.012, 0.035, 0.065, 0.96), true)
draw_rect(panel, Color("39788e"), false, 2.0)
draw_string(font, Vector2(275, 277), "RANG", HORIZONTAL_ALIGNMENT_LEFT, 100, 14, Color("91a8bc"))
draw_string(font, Vector2(410, 277), "PILOT", HORIZONTAL_ALIGNMENT_LEFT, 480, 14, Color("91a8bc"))
draw_string(font, Vector2(950, 277), "WELLE", HORIZONTAL_ALIGNMENT_CENTER, 130, 14, Color("91a8bc"))
draw_string(font, Vector2(1125, 277), "PUNKTE", HORIZONTAL_ALIGNMENT_RIGHT, 190, 14, Color("91a8bc"))
draw_line(Vector2(270, 293), Vector2(1330, 293), Color("315c70"), 1.0)
draw_string(font, Vector2(235, 319), "RANG", HORIZONTAL_ALIGNMENT_LEFT, 75, 13, Color("91a8bc"))
draw_string(font, Vector2(325, 319), "PILOT", HORIZONTAL_ALIGNMENT_LEFT, 270, 13, Color("91a8bc"))
draw_string(font, Vector2(610, 319), "KARTE", HORIZONTAL_ALIGNMENT_LEFT, 240, 13, Color("91a8bc"))
draw_string(font, Vector2(850, 319), "STUFE", HORIZONTAL_ALIGNMENT_LEFT, 130, 13, Color("91a8bc"))
draw_string(font, Vector2(995, 319), "WELLE", HORIZONTAL_ALIGNMENT_CENTER, 110, 13, Color("91a8bc"))
draw_string(font, Vector2(1160, 319), "PUNKTE", HORIZONTAL_ALIGNMENT_RIGHT, 190, 13, Color("91a8bc"))
draw_line(Vector2(230, 334), Vector2(1370, 334), Color("315c70"), 1.0)
var own_user_id: String = str(online_manager.session.user_id) if online_manager.session != null else ""
for index in leaderboard_rows.size():
var row: Dictionary = leaderboard_rows[index]
var y := 331.0 + index * 34.0
var y := 370.0 + index * 32.0
if index % 2 == 0:
draw_rect(Rect2(260, y - 24, 1080, 31), Color(0.03, 0.09, 0.14, 0.64), true)
draw_rect(Rect2(225, y - 23, 1150, 29), Color(0.03, 0.09, 0.14, 0.64), true)
var owner_color := Color("83ffc8") if not own_user_id.is_empty() and str(row.get("owner_id", "")) == own_user_id else Color.WHITE
draw_string(font, Vector2(280, y), "%02d" % int(row.get("rank", index + 1)), HORIZONTAL_ALIGNMENT_LEFT, 90, 16, Color("8ff4ff"))
draw_string(font, Vector2(410, y), str(row.get("username", "PILOT")).to_upper(), HORIZONTAL_ALIGNMENT_LEFT, 480, 16, owner_color)
draw_string(font, Vector2(950, y), "%02d" % int(row.get("wave", 0)), HORIZONTAL_ALIGNMENT_CENTER, 130, 16, Color("ffcf7a"))
draw_string(font, Vector2(1125, y), "%09d" % int(row.get("score", 0)), HORIZONTAL_ALIGNMENT_RIGHT, 190, 16, Color("8fffea"))
draw_string(font, Vector2(240, y), "%02d" % int(row.get("rank", index + 1)), HORIZONTAL_ALIGNMENT_LEFT, 65, 14, Color("8ff4ff"))
draw_string(font, Vector2(325, y), str(row.get("username", "PILOT")).to_upper(), HORIZONTAL_ALIGNMENT_LEFT, 270, 14, owner_color)
draw_string(font, Vector2(610, y), MAP_NAMES[int(row.get("map", leaderboard_map))], HORIZONTAL_ALIGNMENT_LEFT, 225, 13, Color("9fdce9"))
draw_string(font, Vector2(850, y), DIFFICULTY_DATA[int(row.get("difficulty", leaderboard_difficulty))].name, HORIZONTAL_ALIGNMENT_LEFT, 130, 13, Color("ffcf7a"))
draw_string(font, Vector2(995, y), "%02d" % int(row.get("wave", 0)), HORIZONTAL_ALIGNMENT_CENTER, 110, 14, Color("ffcf7a"))
draw_string(font, Vector2(1160, y), "%09d" % int(row.get("score", 0)), HORIZONTAL_ALIGNMENT_RIGHT, 190, 14, Color("8fffea"))
if not leaderboard_message.is_empty():
draw_string(font, Vector2(300, 520), leaderboard_message, HORIZONTAL_ALIGNMENT_CENTER, 1000, 17, Color("ffcf7a"))
draw_string(font, Vector2(0, 810), "Spielernamen werden aus dem aktuellen öffentlichen Profil geladen.", HORIZONTAL_ALIGNMENT_CENTER, 1600, 14, Color("91a8bc"))