Структура проекта: папки по назначению, карты самодостаточны, инструменты, README
- Перенос: Net→net, Player→player, World/*→maps/{hub,sandbox,backrooms}, core, objects,
models/*→chasers/{rock,verity} и maps/*/models. Имена внутри папок с .glb не менялись
(рядом лежат извлечённые текстуры). Все пути, пресеты экспорта и автолоады обновлены.
- Преследователи: rock-follow.gd → chasers/chaser.gd; камень в песочнице теперь инстанс
chasers/rock/rock.tscn (как Верити).
- Удалён мусор: неиспользуемое небо sunflowers, Tree_01.glb, rock-path.gd, старая
модель Верити, unwrap_cache (теперь в .gitignore).
- Основной пакет игры 70 КБ (камень и Верити уехали в пакеты карт).
- tools/: export_all.sh, serve_web.py (nginx-подобный dev-сервер), deploy_server.sh,
test/ (боты, временный проект и сервер). Net: флаг --client для headless-клиента.
- deploy/: боевые nginx.conf и my-world.service, project.godot выделенного сервера.
- README переписан: что есть и чего нет, структура, карты, преследователи, сеть, пакеты,
деплой, грабли, дорожная карта (телефон).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
extends Area3D
|
||||
# Портал на другую карту: шаг в рамку — переход. Для сети игрок попадает в «комнату» новой карты.
|
||||
|
||||
@export var target_map := "backrooms"
|
||||
@export var target_scene := "res://maps/backrooms/backrooms.tscn"
|
||||
@export var pack_name := "" # Пакет с картой (качается при первом входе); пусто — карта уже в игре
|
||||
@export var title := "BACKROOMS"
|
||||
@export var color := Color(1.0, 0.85, 0.25)
|
||||
@export var frame_size := Vector2(2.6, 3.6)
|
||||
@export var trigger_size := Vector3(3.2, 3.6, 3.0) # Зона входа шире рамки, чтобы не обойти сбоку
|
||||
|
||||
var _used := false
|
||||
var _glow: StandardMaterial3D
|
||||
|
||||
func _ready() -> void:
|
||||
collision_layer = 0
|
||||
collision_mask = 1
|
||||
var cs := CollisionShape3D.new()
|
||||
var shape := BoxShape3D.new()
|
||||
shape.size = trigger_size
|
||||
cs.shape = shape
|
||||
cs.position.y = trigger_size.y * 0.5
|
||||
add_child(cs)
|
||||
_build_visuals()
|
||||
body_entered.connect(_on_body_entered)
|
||||
MapLoader.enter_failed.connect(func(): _used = false) # Не скачалось — можно зайти ещё раз
|
||||
|
||||
func _build_visuals() -> void:
|
||||
var w := frame_size.x
|
||||
var h := frame_size.y
|
||||
|
||||
var frame_mat := StandardMaterial3D.new()
|
||||
frame_mat.albedo_color = color.darkened(0.5)
|
||||
frame_mat.emission_enabled = true
|
||||
frame_mat.emission = color
|
||||
frame_mat.emission_energy_multiplier = 1.5
|
||||
_add_box(Vector3(0.2, h, 0.2), Vector3(-w * 0.5 - 0.1, h * 0.5, 0), frame_mat)
|
||||
_add_box(Vector3(0.2, h, 0.2), Vector3(w * 0.5 + 0.1, h * 0.5, 0), frame_mat)
|
||||
_add_box(Vector3(w + 0.4, 0.2, 0.2), Vector3(0, h + 0.1, 0), frame_mat)
|
||||
|
||||
_glow = StandardMaterial3D.new()
|
||||
_glow.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
||||
_glow.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
||||
_glow.cull_mode = BaseMaterial3D.CULL_DISABLED
|
||||
_glow.albedo_color = Color(color, 0.35)
|
||||
var quad := QuadMesh.new()
|
||||
quad.size = frame_size
|
||||
quad.material = _glow
|
||||
var plane := MeshInstance3D.new()
|
||||
plane.mesh = quad
|
||||
plane.position.y = h * 0.5
|
||||
plane.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
add_child(plane)
|
||||
|
||||
var label := Label3D.new()
|
||||
label.text = title
|
||||
label.billboard = BaseMaterial3D.BILLBOARD_ENABLED
|
||||
label.pixel_size = 0.01
|
||||
label.font_size = 64
|
||||
label.outline_size = 12
|
||||
label.modulate = color
|
||||
label.position.y = h + 0.8
|
||||
add_child(label)
|
||||
|
||||
var light := OmniLight3D.new()
|
||||
light.light_color = color
|
||||
light.light_energy = 1.5
|
||||
light.omni_range = 9.0
|
||||
light.position.y = h * 0.5
|
||||
light.shadow_enabled = false
|
||||
add_child(light)
|
||||
|
||||
func _add_box(size: Vector3, pos: Vector3, mat: Material) -> void:
|
||||
var mesh := BoxMesh.new()
|
||||
mesh.size = size
|
||||
mesh.material = mat
|
||||
var mi := MeshInstance3D.new()
|
||||
mi.mesh = mesh
|
||||
mi.position = pos
|
||||
mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
|
||||
add_child(mi)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
_glow.albedo_color.a = 0.3 + 0.12 * sin(Time.get_ticks_msec() * 0.004)
|
||||
|
||||
func _on_body_entered(body: Node3D) -> void:
|
||||
if _used or not body.is_in_group("local_player") or body.get("is_dead"):
|
||||
return
|
||||
_used = true
|
||||
MapLoader.enter(target_map, target_scene, pack_name, title)
|
||||
Reference in New Issue
Block a user