Карта Backrooms, порталы, комнаты по картам и зрение камня

- Backrooms на готовой модели (CC-BY, VTX): отдельный пакет backrooms.pck,
  качается при первом входе в портал (World/map_loader.gd), навмеш запечён заранее.
- Порталы между картами (World/portal.gd); выход из Backrooms возвращает в мир.
- Мультиплеер: игроки и общий камень делятся на комнаты по картам (Net/net.gd).
- Камень видит игрока только вблизи или в поле зрения, может потерять и бродит
  по навмешу (sight_limited); в мире и в Backrooms включено.
- Экспорт: пресет Backrooms для пакета карты, основной пресет без неё.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 16:15:00 +05:00
co-authored by Claude Sonnet 5.5
parent b7ca18ae3d
commit 97fafe6a30
71 changed files with 1920 additions and 97 deletions
+123
View File
@@ -0,0 +1,123 @@
extends Node
# Подгружает карту отдельным пакетом при первом входе в портал: в вебе её не качают при старте игры.
# Пакет (например backrooms.pck) лежит рядом с index.html. Если сцена уже есть в проекте
# (редактор, десктоп-запуск) — качать нечего, переходим сразу.
signal enter_failed
const PACK_URL_ARG := "--pack-url=" # Для тестов на десктопе: --pack-url=http://127.0.0.1:8000/
var _loaded := {} # имя пакета -> true, чтобы не качать и не монтировать дважды
var _busy := false
var _layer: CanvasLayer
var _label: Label
var _bar: ProgressBar
func _ready() -> void:
process_mode = Node.PROCESS_MODE_ALWAYS
func enter(map: String, scene_path: String, pack_name: String, title: String) -> void:
if _busy:
return
if pack_name != "" and not _loaded.has(pack_name) and not ResourceLoader.exists(scene_path):
_busy = true
var ok := await _download_and_mount(pack_name, title)
_busy = false
if not ok:
enter_failed.emit()
return
Net.go_to_map(map, scene_path)
func _download_and_mount(pack_name: String, title: String) -> bool:
_show("Загрузка: %s…" % title)
get_tree().paused = true # Пока качаем, мир стоит (камень не должен убить игрока на экране загрузки)
var http := HTTPRequest.new()
http.process_mode = Node.PROCESS_MODE_ALWAYS
http.download_file = "user://" + pack_name
add_child(http)
var state := {"done": false, "result": -1, "code": 0}
http.request_completed.connect(func(result: int, code: int, _h, _b):
state.result = result
state.code = code
state.done = true)
var ok := false
# no-cache: браузер сверяет файл с сервером (после обновления пакета не подсунет старый из кэша)
if http.request(_pack_url(pack_name), ["Cache-Control: no-cache"]) == OK:
while not state.done:
_update_progress(http, title)
await get_tree().process_frame
if state.result == HTTPRequest.RESULT_SUCCESS and state.code == 200:
ok = ProjectSettings.load_resource_pack("user://" + pack_name, false) # false: файлы основной игры в приоритете
http.queue_free()
if ok:
_loaded[pack_name] = true
_hide()
else:
_label.text = "Не удалось загрузить карту"
_bar.visible = false
await get_tree().create_timer(2.5).timeout
_hide()
get_tree().paused = false
return ok
func _pack_url(pack_name: String) -> String:
for arg in OS.get_cmdline_user_args():
if arg.begins_with(PACK_URL_ARG):
return arg.substr(PACK_URL_ARG.length()) + pack_name
if OS.has_feature("web"):
return str(JavaScriptBridge.eval("new URL('%s', window.location.href).href" % pack_name))
return "http://127.0.0.1:8000/" + pack_name
# ---------- Экран загрузки ----------
func _show(text: String) -> void:
_layer = CanvasLayer.new()
_layer.layer = 100
add_child(_layer)
var bg := ColorRect.new()
bg.color = Color(0, 0, 0, 0.88)
bg.mouse_filter = Control.MOUSE_FILTER_IGNORE
_layer.add_child(bg)
bg.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
_label = Label.new()
_label.text = text
_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
_label.add_theme_font_size_override("font_size", 40)
bg.add_child(_label)
_label.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
_bar = ProgressBar.new()
_bar.custom_minimum_size = Vector2(420, 14)
_bar.show_percentage = false
_bar.indeterminate = true
var track := StyleBoxFlat.new()
track.bg_color = Color(1, 1, 1, 0.15)
var fill := StyleBoxFlat.new()
fill.bg_color = Color(1.0, 0.85, 0.25)
_bar.add_theme_stylebox_override("background", track)
_bar.add_theme_stylebox_override("fill", fill)
bg.add_child(_bar)
_bar.set_anchors_and_offsets_preset(Control.PRESET_CENTER_BOTTOM, Control.PRESET_MODE_KEEP_SIZE)
_bar.position.y -= 120
func _update_progress(http: HTTPRequest, title: String) -> void:
var got := http.get_downloaded_bytes()
var total := http.get_body_size() # -1, если сервер не прислал Content-Length (например, gzip)
if total > 0:
_bar.indeterminate = false
_bar.max_value = total
_bar.value = got
_label.text = "Загрузка: %s\n%.1f / %.1f МБ" % [title, got / 1048576.0, total / 1048576.0]
else:
_label.text = "Загрузка: %s\n%.1f МБ" % [title, got / 1048576.0]
func _hide() -> void:
if _layer:
_layer.queue_free()
_layer = null