extends ConfirmationDialog ## „Kartengröße ändern“-Dialog (R3, Karte t_c863b93b — PLAN.md „Map-Größe ## ändern“): aktuelle Größe anzeigen, neue X/Z in Metern eingeben (Preset- ## Buttons + Freifeld), Live-Vorschau aus SvmapDocument.resize_preview (R2). const UIScale := preload("res://src/ui/ui_scale.gd") # Karte t_297221b5 ## ## Stil wie new_map_dialog.gd: alles per Code, Deutsch, Klartext-Warnungen. ## Der Dialog mutiert NICHTS — das Anwenden macht main.gd (_on_resize_confirmed) ## als EINE Undo-Einheit über resize_apply + ActionHistory-Snapshot. var size_x_edit: SpinBox var size_z_edit: SpinBox var drop_check: CheckBox var info_label: Label # aktuelle Größe + Auflösung var preview_label: Label # Live-Vorschau (Verlust-Liste / Warnband) var _old_sx := 0.0 var _old_sz := 0.0 var _old_res := 0 var _doc: Dictionary = {} const _PRESET_SIZES := [256, 512, 1024, 2048] func _init() -> void: title = "Kartengröße ändern" ok_button_text = "Größe ändern" cancel_button_text = "Abbrechen" min_size = UIScale.v2i(520, 0) var vbox := VBoxContainer.new() vbox.set_anchors_preset(Control.PRESET_FULL_RECT) vbox.add_theme_constant_override("separation", UIScale.px(12)) add_child(vbox) info_label = _make_label("") vbox.add_child(info_label) var preset_h := HBoxContainer.new() preset_h.add_theme_constant_override("separation", UIScale.px(8)) for size in _PRESET_SIZES: var btn := Button.new() btn.text = "%d m" % size btn.custom_minimum_size = UIScale.v2(0, 40) btn.pressed.connect(_on_preset.bind(float(size))) preset_h.add_child(btn) vbox.add_child(preset_h) vbox.add_child(_make_label("Neue Breite (X) in Metern:")) size_x_edit = SpinBox.new() _fill_spin(size_x_edit) vbox.add_child(size_x_edit) vbox.add_child(_make_label("Neue Tiefe (Z) in Metern:")) size_z_edit = SpinBox.new() _fill_spin(size_z_edit) vbox.add_child(size_z_edit) var hint := _make_label("Erlaubt: 256–4096 m. Die Karte bleibt mittig — das bestehende Terrain\nbehaftet seine Weltposition, die neue Randzone wird flach (Gras).") hint.modulate = Color(1, 1, 1, 0.7) vbox.add_child(hint) preview_label = _make_label("") preview_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART preview_label.custom_minimum_size = UIScale.v2(0, 60) vbox.add_child(preview_label) drop_check = CheckBox.new() drop_check.text = "Draußen liegende Objekte/Bauteile/Spawn-Punkte beim Verkleinern entfernen" drop_check.button_pressed = true drop_check.toggled.connect(func(_p: bool) -> void: _update_preview()) vbox.add_child(drop_check) size_x_edit.value_changed.connect(func(_v: float) -> void: _update_preview()) size_z_edit.value_changed.connect(func(_v: float) -> void: _update_preview()) func _make_label(text: String) -> Label: var label := Label.new() label.text = text label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART return label func _fill_spin(spin: SpinBox) -> void: spin.min_value = SvmapDocument.MIN_SIZE_M spin.max_value = SvmapDocument.MAX_SIZE_M spin.step = 1.0 spin.suffix = " m" spin.value = 1024.0 spin.custom_minimum_size = UIScale.v2(0, 40) ## Vom Editor vor dem Öffnen füllen: aktueller Stand + Live-Vorschau. func setup(doc: Dictionary) -> void: _doc = doc var terrain: Dictionary = doc.get("terrain", {}) _old_sx = float(terrain.get("size_x_m", 0.0)) _old_sz = float(terrain.get("size_z_m", 0.0)) _old_res = int(terrain.get("resolution", 0)) info_label.text = "Aktuelle Größe: %d × %d m — Auflösung: %d × %d Höhenpunkte" % [ int(_old_sx), int(_old_sz), _old_res, _old_res] size_x_edit.value = _old_sx size_z_edit.value = _old_sz _update_preview() func _on_preset(size: float) -> void: size_x_edit.value = size size_z_edit.value = size func get_new_size_x() -> float: return clampf(size_x_edit.value, SvmapDocument.MIN_SIZE_M, SvmapDocument.MAX_SIZE_M) func get_new_size_z() -> float: return clampf(size_z_edit.value, SvmapDocument.MIN_SIZE_M, SvmapDocument.MAX_SIZE_M) func get_drop_outside() -> bool: return drop_check.button_pressed ## Live-Vorschau: Verlust-Liste (R2 resize_preview), Spawn-Speicher-Warnung ## (V11 verlangt ≥ 4 Punkte/Team), Warnband bei Vergrößern über ~1024 m ## (Dichte-Leiter deckelt bei 513 Punkten → grobes Raster, Quest-Perf). func _update_preview() -> void: if _doc.is_empty(): preview_label.text = "" preview_label.modulate = Color(1, 1, 1, 0.7) return var new_sx := get_new_size_x() var new_sz := get_new_size_z() var preview: Dictionary = SvmapDocument.resize_preview(_doc, new_sx, new_sz) var lines: Array[String] = [] var shrink := new_sx < _old_sx - 0.5 or new_sz < _old_sz - 0.5 var grow := new_sx > _old_sx + 0.5 or new_sz > _old_sz + 0.5 var out_total := (preview["objects_out"] as Array).size() \ + (preview["parts_out"] as Array).size() \ + (preview["spawns_out"] as Array).size() if shrink and out_total > 0: var detail := "Beim Verkleinern liegen draußen: %d Objekt(e), %d Bauteil(e), %d Spawn-Punkt(e)." % [ (preview["objects_out"] as Array).size(), (preview["parts_out"] as Array).size(), (preview["spawns_out"] as Array).size()] if drop_check.button_pressed: detail += "\nDiese werden beim Anwenden ENTFERNT (Strg+Z holt sie zurück)." else: detail += "\nSie bleiben in der Map, ragen aber über den Rand hinaus — die Map ist dann NICHT speicherbar (nichts darf außerhalb der Match-Grenzen liegen). Zieh sie später wieder rein oder entferne sie." preview_label.modulate = Color(1.0, 0.75, 0.3) # Warnorange lines.append(detail) elif shrink: preview_label.modulate = Color(1, 1, 1, 0.7) lines.append("Beim Verkleinern geht nichts verloren — alles liegt innerhalb der neuen Grenzen.") elif grow: preview_label.modulate = Color(1, 1, 1, 0.7) lines.append("Beim Vergrößern bleibt alles erhalten — die neue Randzone wird flaches Gras.") # Spawn-Speicher-Warnung (R2-Befund): V11 verlangt ≥ 4 Punkte je Team beim # SPEICHERN — nach dem Entfernen drohen Teams unter den Deckwert. if drop_check.button_pressed and out_total > 0: var zones = _doc.get("spawn_zones", {}) if zones is Dictionary: var dropped: Dictionary = {} for entry in (preview["spawns_out"] as Array): if entry is Dictionary: dropped[int(entry.get("team", -1))] = int(dropped.get(int(entry.get("team", -1)), 0)) + 1 for team_key in (zones as Dictionary).keys(): var zone = zones[team_key] if not (zone is Dictionary): continue var kept := (zone.get("points", []) as Array).size() - int(dropped.get(int(team_key), 0)) if kept < 4: lines.append("Achtung: Team „%s“ hätte danach nur %d Spawn-Punkte — Maps brauchen mindestens 4 pro Team, um SPEICHERN zu können. Abhilfe: Entfernen ausstellen oder später Punkte ergänzen." % [ str(zone.get("label", "Team %s" % str(team_key))), kept]) preview_label.modulate = Color(1.0, 0.75, 0.3) # Warnband Vergrößern: über ~1024 m Kante grobt die Dichte (Deckel 513 # Punkte) — Detail-Dichte + Quest-Perf (Objektzahl/Draw-Calls) erwähnen. if grow: var m_per_point := SvmapDocument.resize_m_per_point(_projected_resolution(), maxf(new_sx, new_sz)) if m_per_point > SvmapDocument.RESIZE_DENSITY_TARGET_M: lines.append("Achtung: Ab dieser Größe wird das Terrain grober aufgelöst (%.1f m pro Höhenpunkt statt 2 m). Die Hügel-Details werden weicher — für Quest-Perf gilt: mehr Fläche = mehr Objekte/Draw-Calls beachten." % m_per_point) preview_label.modulate = Color(1.0, 0.75, 0.3) preview_label.text = "\n".join(lines) ## Projected new resolution nach dem Resize (gleiche Leiter wie resize_terrain, ## hier nur für die Warnband-Anzeige — kein Terrain-Resample im Dialog). func _projected_resolution() -> int: var res := _old_res for step in SvmapDocument.RESIZE_RESOLUTION_LADDER: if step < res: continue if maxf(get_new_size_x(), get_new_size_z()) / float(step - 1) <= SvmapDocument.RESIZE_DENSITY_TARGET_M: return step return SvmapIO.MAX_TERRAIN_RESOLUTION