Refactor game scripts and update vault scene structure. Enhanced organization of game logic by separating data and rendering responsibilities across multiple scripts. Removed unused background assets and updated vault scene to include new game clock and weather system functionalities.

This commit is contained in:
Leonid Pershin
2026-08-11 10:18:30 +03:00
parent 709776116b
commit a81747781d
48 changed files with 979 additions and 4509 deletions
+255
View File
@@ -0,0 +1,255 @@
class_name SurfaceView
extends Node2D
## Поверхность над убежищем: горы, холмы, деревья, облака и осадки.
##
## Текстуры — набор Kenney из `background/PNG/Flat`. Слои рисуются с разной
## скоростью относительно камеры (параллакс) и подмешивают цвет неба тем
## сильнее, чем дальше стоят, — это даёт воздушную перспективу.
##
## Как и остальной вид, всё живёт в _draw(): узлов-спрайтов нет, поэтому
## перекрасить пейзаж под время суток — это один параметр, а не обход дерева.
const MOUNTAINS := preload("res://background/PNG/Flat/pointy_mountains.png")
const HILLS_FAR := preload("res://background/PNG/Flat/hills2.png")
const HILLS_NEAR := preload("res://background/PNG/Flat/hills1.png")
const CLOUDS_HIGH := preload("res://background/PNG/Flat/clouds1.png")
const CLOUDS_LOW := preload("res://background/PNG/Flat/clouds2.png")
## Ближний план берём из основного набора: там деревья и трава уже цветные.
## Набор Flat — бледные силуэты, он идёт на дальние слои, которые всё равно
## перекрашиваются под воздушную перспективу.
const TREE_PATHS := [
"res://background/PNG/tree01.png", "res://background/PNG/tree05.png",
"res://background/PNG/tree09.png", "res://background/PNG/tree12.png",
"res://background/PNG/tree18.png", "res://background/PNG/tree24.png",
"res://background/PNG/tree30.png",
]
const GRASS_PATHS := [
"res://background/PNG/grass1.png", "res://background/PNG/grass2.png",
"res://background/PNG/grass3.png", "res://background/PNG/grass4.png",
]
## Собственный цвет дальних слоёв: сами текстуры почти белые, тон задаём тут.
const MOUNTAIN_TONE := Color("#7e93ad")
const HILLS_FAR_TONE := Color("#6b8f5e")
const HILLS_NEAR_TONE := Color("#5d8248")
const LEFT := -1600.0
const RIGHT := 2900.0
const SCENERY_SEED := 20120401
## Полоса дёрна на уровне земли.
const TURF := Color("#4a6b3a")
const TURF_HEIGHT := 16.0
## Не ставим деревья вплотную к будке входа, чтобы её не загораживали.
const ENTRANCE_CLEAR := Vector2(-140.0, 300.0)
## Час суток и погода — сцена обновляет их вслед за часами.
var hour := 6.0
var clouds := 0.0
var precipitation := 0.0
var fog := 0.0
var wind := 0.3
var snowing := false
## Множитель хода: 0 на паузе, иначе скорость игры.
var time_scale := 1.0
var _trees: Array[Dictionary] = []
var _grass: Array[Dictionary] = []
var _drops: Array[Vector2] = []
var _drift := 0.0
var _fall := 0.0
func _ready() -> void:
# Плиточная заливка широких полос требует повтора текстуры на узле.
texture_repeat = CanvasItem.TEXTURE_REPEAT_ENABLED
_scatter_scenery()
_scatter_drops()
func _process(delta: float) -> void:
if is_zero_approx(time_scale):
return
var step := delta * time_scale
_drift += step * (18.0 + wind * 120.0)
_fall += step * (900.0 + precipitation * 900.0)
queue_redraw()
## Одним вызовом — чтобы не гонять перерисовку на каждый параметр.
func set_conditions(
hour_of_day: float, weather: WeatherSystem
) -> void:
hour = hour_of_day
clouds = weather.clouds
precipitation = weather.precipitation
fog = weather.fog
wind = weather.wind
snowing = weather.is_snowing()
queue_redraw()
func _draw() -> void:
var sky := SkyCycle.sample(hour)
var light: Color = sky["light"]
var haze: Color = sky["bottom"]
var view := _view_rect()
_draw_band(MOUNTAINS, MOUNTAIN_TONE, 1.7, 0.45, 0.25, light, haze, view)
_draw_band(HILLS_FAR, HILLS_FAR_TONE, 1.5, 0.24, 0.5, light, haze, view)
_draw_clouds(view, light)
_draw_band(HILLS_NEAR, HILLS_NEAR_TONE, 1.15, 0.08, 0.75, light, haze, view)
_draw_turf(light)
_draw_scenery(light)
_draw_fog(haze)
_draw_precipitation(view, light)
# --- Слои пейзажа -----------------------------------------------------------
## Широкая полоса, замощённая по горизонтали и стоящая на уровне земли.
## `distance` — насколько слой выцветает в цвет неба, `parallax` — насколько
## он привязан к миру (1.0 — намертво, меньше — отстаёт от камеры).
func _draw_band(
texture: Texture2D, tone: Color, scale: float, distance: float, parallax: float,
light: Color, haze: Color, view: Rect2
) -> void:
var size := Vector2(texture.get_size()) * scale
var offset := view.get_center().x * (1.0 - parallax)
var rect := Rect2(Vector2(LEFT + offset, -size.y), Vector2(RIGHT - LEFT, size.y))
draw_texture_rect(texture, rect, true, (tone * light).lerp(haze, distance))
func _draw_turf(light: Color) -> void:
draw_rect(Rect2(Vector2(LEFT, -TURF_HEIGHT), Vector2(RIGHT - LEFT, TURF_HEIGHT)), TURF * light)
func _draw_scenery(light: Color) -> void:
for tuft: Dictionary in _grass:
_draw_item(tuft, light)
for tree: Dictionary in _trees:
_draw_item(tree, light)
func _draw_item(item: Dictionary, light: Color) -> void:
var texture: Texture2D = item["texture"]
var size := Vector2(texture.get_size()) * float(item["scale"])
var origin := Vector2(float(item["x"]) - size.x * 0.5, -size.y + TURF_HEIGHT * 0.4)
draw_texture_rect(texture, Rect2(origin, size), false, light * Color(item["tint"]))
# --- Погода -----------------------------------------------------------------
func _draw_clouds(view: Rect2, light: Color) -> void:
if clouds <= 0.02:
return
# Полосы держим выше линии горизонта: их нижний край не должен наезжать
# на землю, поэтому высота и масштаб подобраны в паре.
_draw_cloud_band(CLOUDS_HIGH, 1.4, -900.0, 0.35, 0.75, view, light)
_draw_cloud_band(CLOUDS_LOW, 1.6, -620.0, 0.6, 1.0, view, light)
func _draw_cloud_band(
texture: Texture2D, scale: float, height: float, parallax: float,
weight: float, view: Rect2, light: Color
) -> void:
var size := Vector2(texture.get_size()) * scale
var offset := view.get_center().x * (1.0 - parallax) - _drift * parallax
var rect := Rect2(Vector2(LEFT + offset, height), Vector2(RIGHT - LEFT, size.y))
var alpha := clampf(clouds * weight, 0.0, 1.0)
draw_texture_rect(texture, rect, true, Color(light, alpha))
## Дымка у горизонта: чем гуще, тем сильнее пейзаж растворяется в небе.
func _draw_fog(haze: Color) -> void:
if fog <= 0.01:
return
var top := -520.0
var points := PackedVector2Array([
Vector2(LEFT, top), Vector2(RIGHT, top), Vector2(RIGHT, 0.0), Vector2(LEFT, 0.0),
])
var clear := Color(haze, 0.0)
var dense := Color(haze, clampf(fog, 0.0, 1.0) * 0.85)
# Ниже нуля тумана нет — там уже разрез породы.
draw_polygon(points, PackedColorArray([clear, clear, dense, dense]))
func _draw_precipitation(view: Rect2, light: Color) -> void:
if precipitation <= 0.02:
return
# Рисуем только то, что попадает в кадр и находится над землёй.
var band := view.intersection(Rect2(LEFT, -1700.0, RIGHT - LEFT, 1700.0))
if band.size.x <= 0.0 or band.size.y <= 0.0:
return
var count := int(float(_drops.size()) * clampf(precipitation, 0.0, 1.0))
var slant := wind * 0.45
for index in count:
var drop := _drops[index]
var x := band.position.x + drop.x * band.size.x
var y := band.position.y + fposmod(drop.y * band.size.y + _fall, band.size.y)
if snowing:
draw_circle(Vector2(x, y), 3.0, Color(light, 0.75))
continue
var length := 16.0 + precipitation * 12.0
draw_line(
Vector2(x, y), Vector2(x - slant * length, y + length),
Color(light.lerp(Color("#9fc4e8"), 0.5), 0.5), 1.5
)
# --- Расстановка ------------------------------------------------------------
func _scatter_scenery() -> void:
var rng := RandomNumberGenerator.new()
rng.seed = SCENERY_SEED
var trees: Array[Texture2D] = []
for path: String in TREE_PATHS:
trees.append(load(path) as Texture2D)
var grass: Array[Texture2D] = []
for path: String in GRASS_PATHS:
grass.append(load(path) as Texture2D)
var x := LEFT
while x < RIGHT:
x += rng.randf_range(90.0, 260.0)
if x > ENTRANCE_CLEAR.x and x < ENTRANCE_CLEAR.y:
continue
_trees.append({
"texture": trees[rng.randi_range(0, trees.size() - 1)],
"x": x,
"scale": rng.randf_range(0.55, 1.0),
"tint": Color("#ffffff").lerp(Color("#9db98a"), rng.randf() * 0.45),
})
var tuft_x := LEFT
while tuft_x < RIGHT:
tuft_x += rng.randf_range(34.0, 130.0)
_grass.append({
"texture": grass[rng.randi_range(0, grass.size() - 1)],
"x": tuft_x,
"scale": rng.randf_range(0.9, 1.7),
"tint": Color("#ffffff"),
})
func _scatter_drops() -> void:
var rng := RandomNumberGenerator.new()
rng.seed = SCENERY_SEED + 7
for index in 520:
_drops.append(Vector2(rng.randf(), rng.randf()))
## Прямоугольник, который сейчас видно камерой, в координатах мира.
func _view_rect() -> Rect2:
var canvas := get_canvas_transform()
var zoom := canvas.get_scale()
return Rect2(-canvas.origin / zoom, get_viewport_rect().size / zoom)