farming-simulator/scripts/util/texture_generator.gd
Dmitrii Bykov c3190ad058
Some checks failed
Godot CI / Import + smoke test (push) Failing after 29s
prologue: целиком Акт 1 (комната+кухня) + Акт 2 (установка)
ИНФРАСТРУКТУРА:
- GameState autoload: player_name, прогресс актов, ачивки.
- SceneManager autoload: change_scene с deferred переключением,
  shortcuts go_room/go_kitchen/go_install.
- TextureGen autoload: процедурные pixel-art ImageTexture через
  Image.set_pixel, кешируются в Dictionary. Текстуры: wallpaper
  (бежевая с растительным узором), linoleum (полосы паркета),
  ceiling (побелка с шумом), wall_carpet (бордовый с ромбами),
  blanket (тёмно-синее со звёздами), poster_csgo (жёлтый с
  силуэтом AWP), poster_morgen (тёмно-красный с головой),
  door (дерево с волокном), desk_white, book_color(0-4).

АКТ 1 КОМНАТА (act1_room.tscn / act1.gd):
- Все стены/пол/потолок/ковёр/одеяло/постеры/стол/дверь теперь
  с процедурными текстурами через TextureGen.apply_to_material с
  правильным uv1_scale. Texture filter NEAREST для пиксель-арта.
- Door interactable добавлен в east-wall (рядом с DoorPanel).
  Включается после ухода отца. Клик → переход на кухню.
- GameState persists прогресс — возврат из кухни не повторяет интро.
- _setup_post_intro_state восстанавливает SEEK_DRIVE после кухни.
- После подбора флешки — переход на act2_install.

АКТ 1 КУХНЯ (act1_kitchen.tscn / act1_kitchen.gd):
- 3×3м комнатка с холодильником «Атлант», плитой (4 ручки красные),
  столешницей+раковиной, чайником, столом+2 стула, окном+радиатором.
- Wi-Fi записка приклеена на холодильнике, читается → ачивка
  «Wi-Fi разведчик» + GameState.wifi_password_found = true.
- Дверь обратно в комнату — SceneManager.go_room().
- Текстуры через TextureGen.

АКТ 2 УСТАНОВКА ШИНДОВС (act2_install.tscn / act2_install.gd):
- Полная 2D-сцена с 7 шагами state machine:
  BOOT (4 квадратика логотипа 1.6 сек) → LICENSE_INTRO →
  LICENSE_FULL (по «Читать») → DISK → USERNAME → PASSWORD →
  TIMEZONE → INSTALLING (5 фаз прогресс-бара 3 сек) → COMPLETE.
- ШИЗО-пункты лицензионки: 7.3.2, 11.4, 12.1, 15.7, 18.0, 21.3,
  23.9, 27.1 (полный текст из канона Пролог Акт 2).
- Ввод имени с валидацией: 2-24 символа, regex буквы+цифры,
  ban-list титулов (повелитель/владыка/бог/лорд/царь) и мата.
- Подсказка-плейсхолдеры из канона: bossiara13, fr3sh_n3kit и т.п.
- Пароль с чекбоксом «Без пароля» + ачивки «Главное чтоб удобно»
  (123/qwerty/empty) и «Параноик» (12+ символов со спец).
- Часовой пояс OptionButton с 7 регионами страны Р, UTC+3 default.
- Welcome-экран после установки: «Добро пожаловать, {playerName}».
- Кнопка «Запустить стеме» — заглушка Акта 3.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 17:50:26 +03:00

269 lines
8.5 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends Node
var _cache: Dictionary = {}
func _make_image(w: int, h: int, fill: Color) -> Image:
var img: Image = Image.create(w, h, false, Image.FORMAT_RGB8)
img.fill(fill)
return img
func _to_texture(img: Image) -> ImageTexture:
return ImageTexture.create_from_image(img)
func _get_or_create(key: String, maker: Callable) -> ImageTexture:
if _cache.has(key):
return _cache[key]
var tex: ImageTexture = maker.call()
_cache[key] = tex
return tex
# Бежевые обои с растительным узором (мелкие точки)
func wallpaper() -> ImageTexture:
return _get_or_create("wallpaper", func() -> ImageTexture:
var base: Color = Color(0.83, 0.72, 0.55)
var dot: Color = Color(0.72, 0.6, 0.42)
var darker: Color = Color(0.78, 0.68, 0.5)
var img: Image = _make_image(64, 64, base)
# мелкие крестики-цветы
for y in range(0, 64, 8):
for x in range(0, 64, 8):
var ox: int = 4 if (y / 8) % 2 == 0 else 0
var px: int = (x + ox) % 64
img.set_pixel(px, y, dot)
if px + 1 < 64:
img.set_pixel(px + 1, y, darker)
if y + 1 < 64:
img.set_pixel(px, y + 1, darker)
return _to_texture(img)
)
# Линолеум жёлто-коричневый с горизонтальными полосами под паркет
func linoleum() -> ImageTexture:
return _get_or_create("linoleum", func() -> ImageTexture:
var a: Color = Color(0.72, 0.6, 0.41)
var b: Color = Color(0.65, 0.53, 0.36)
var c: Color = Color(0.58, 0.46, 0.3)
var img: Image = _make_image(64, 64, a)
# полосы паркета
for y in range(64):
var stripe: int = y / 8
var col: Color = a if stripe % 2 == 0 else b
for x in range(64):
img.set_pixel(x, y, col)
# швы между досками
for y in range(0, 64, 8):
for x in range(64):
img.set_pixel(x, y, c)
# вертикальные швы пэчворк
for y in range(64):
var stripe: int = y / 8
var seam_x: int = ((stripe * 23) % 32) + 16
img.set_pixel(seam_x, y, c)
return _to_texture(img)
)
# Потолок: светлая побелка с лёгким шумом
func ceiling() -> ImageTexture:
return _get_or_create("ceiling", func() -> ImageTexture:
var base: Color = Color(0.92, 0.9, 0.85)
var spot: Color = Color(0.88, 0.86, 0.82)
var img: Image = _make_image(32, 32, base)
var rng: RandomNumberGenerator = RandomNumberGenerator.new()
rng.seed = 4242
for i in range(20):
var x: int = rng.randi_range(0, 31)
var y: int = rng.randi_range(0, 31)
img.set_pixel(x, y, spot)
return _to_texture(img)
)
# Бордовый ковёр с персидским ромбовидным узором
func wall_carpet() -> ImageTexture:
return _get_or_create("wall_carpet", func() -> ImageTexture:
var base: Color = Color(0.36, 0.12, 0.13)
var pattern: Color = Color(0.78, 0.62, 0.2)
var dark: Color = Color(0.22, 0.07, 0.08)
var img: Image = _make_image(64, 64, base)
# ромбы
for cy in [16, 48]:
for cx in [16, 48]:
for r in range(6):
var px1: int = cx - r
var px2: int = cx + r
var py1: int = cy - (6 - r)
var py2: int = cy + (6 - r)
if px1 >= 0 and py1 >= 0 and px1 < 64 and py1 < 64:
img.set_pixel(px1, py1, pattern)
if px2 < 64 and py1 >= 0:
img.set_pixel(px2, py1, pattern)
if px1 >= 0 and py2 < 64:
img.set_pixel(px1, py2, pattern)
if px2 < 64 and py2 < 64:
img.set_pixel(px2, py2, pattern)
# рамка
for i in range(64):
img.set_pixel(i, 0, dark)
img.set_pixel(i, 63, dark)
img.set_pixel(0, i, dark)
img.set_pixel(63, i, dark)
return _to_texture(img)
)
# Тёмно-синее одеяло «звёзды»
func blanket() -> ImageTexture:
return _get_or_create("blanket", func() -> ImageTexture:
var base: Color = Color(0.13, 0.16, 0.32)
var star: Color = Color(0.85, 0.85, 0.75)
var img: Image = _make_image(32, 32, base)
var stars: Array = [[5,5],[14,3],[24,7],[8,12],[20,16],[3,21],[27,22],[15,26],[10,28]]
for s in stars:
img.set_pixel(s[0], s[1], star)
if s[0] + 1 < 32:
img.set_pixel(s[0] + 1, s[1], star)
if s[0] - 1 >= 0:
img.set_pixel(s[0] - 1, s[1], star)
if s[1] + 1 < 32:
img.set_pixel(s[0], s[1] + 1, star)
if s[1] - 1 >= 0:
img.set_pixel(s[0], s[1] - 1, star)
return _to_texture(img)
)
# CS:GO постер (жёлтый с силуэтом AWP)
func poster_csgo() -> ImageTexture:
return _get_or_create("poster_csgo", func() -> ImageTexture:
var base: Color = Color(0.94, 0.71, 0.1)
var dark: Color = Color(0.18, 0.13, 0.08)
var img: Image = _make_image(32, 40, base)
# рамка
for i in range(32):
img.set_pixel(i, 0, dark)
img.set_pixel(i, 39, dark)
for i in range(40):
img.set_pixel(0, i, dark)
img.set_pixel(31, i, dark)
# буквы CS внизу (примитивно)
for x in range(8, 12):
img.set_pixel(x, 32, dark)
img.set_pixel(x, 35, dark)
img.set_pixel(8, 33, dark)
img.set_pixel(8, 34, dark)
for x in range(14, 18):
img.set_pixel(x, 32, dark)
img.set_pixel(x, 35, dark)
img.set_pixel(14, 33, dark)
img.set_pixel(17, 34, dark)
# силуэт AWP (горизонталь сверху)
for x in range(6, 26):
img.set_pixel(x, 18, dark)
img.set_pixel(x, 19, dark)
for x in range(20, 26):
img.set_pixel(x, 17, dark)
img.set_pixel(7, 20, dark)
img.set_pixel(8, 20, dark)
return _to_texture(img)
)
# Моргенштерн постер (тёмно-красный с белым контуром головы)
func poster_morgen() -> ImageTexture:
return _get_or_create("poster_morgen", func() -> ImageTexture:
var base: Color = Color(0.7, 0.13, 0.13)
var light: Color = Color(0.9, 0.85, 0.8)
var dark: Color = Color(0.2, 0.05, 0.05)
var img: Image = _make_image(32, 40, base)
# рамка
for i in range(32):
img.set_pixel(i, 0, dark)
img.set_pixel(i, 39, dark)
for i in range(40):
img.set_pixel(0, i, dark)
img.set_pixel(31, i, dark)
# силуэт головы по центру
var cx: int = 16
var cy: int = 18
for r in range(7):
var w: int = int(sqrt(49 - r * r))
for dx in range(-w, w + 1):
if cy - r >= 0 and cy - r < 40 and cx + dx >= 0 and cx + dx < 32:
img.set_pixel(cx + dx, cy - r, light)
if cy + r < 40 and cx + dx >= 0 and cx + dx < 32:
img.set_pixel(cx + dx, cy + r, light)
return _to_texture(img)
)
# Двери: деревянная текстура с волокном
func door() -> ImageTexture:
return _get_or_create("door", func() -> ImageTexture:
var a: Color = Color(0.36, 0.23, 0.12)
var b: Color = Color(0.45, 0.31, 0.18)
var c: Color = Color(0.28, 0.18, 0.09)
var img: Image = _make_image(32, 64, a)
for y in range(64):
for x in range(32):
var v: float = sin(float(x) * 0.5 + float(y) * 0.1) * 0.5 + 0.5
if v > 0.6:
img.set_pixel(x, y, b)
elif v < 0.3:
img.set_pixel(x, y, c)
return _to_texture(img)
)
# Стол MICKE: белый с малозаметной шумной текстурой
func desk_white() -> ImageTexture:
return _get_or_create("desk_white", func() -> ImageTexture:
var base: Color = Color(0.96, 0.95, 0.93)
var nick: Color = Color(0.88, 0.86, 0.82)
var img: Image = _make_image(32, 32, base)
var rng: RandomNumberGenerator = RandomNumberGenerator.new()
rng.seed = 1337
for i in range(8):
var x: int = rng.randi_range(0, 31)
var y: int = rng.randi_range(0, 31)
img.set_pixel(x, y, nick)
return _to_texture(img)
)
# Книжный корешок (несколько цветов)
func book_color(color_idx: int) -> ImageTexture:
var key: String = "book_%d" % color_idx
return _get_or_create(key, func() -> ImageTexture:
var palette: Array[Color] = [
Color(0.36, 0.18, 0.15),
Color(0.2, 0.35, 0.5),
Color(0.13, 0.13, 0.15),
Color(0.55, 0.45, 0.25),
Color(0.3, 0.5, 0.3),
]
var base: Color = palette[color_idx % palette.size()]
var trim: Color = Color(base.r * 0.6, base.g * 0.6, base.b * 0.6)
var img: Image = _make_image(16, 32, base)
# узкие линии-трим сверху и снизу
for x in range(16):
img.set_pixel(x, 0, trim)
img.set_pixel(x, 31, trim)
img.set_pixel(x, 5, trim)
img.set_pixel(x, 26, trim)
return _to_texture(img)
)
# Применить текстуру к материалу с заданным uv-scale
func apply_to_material(mat: BaseMaterial3D, texture: ImageTexture, scale: Vector3 = Vector3.ONE) -> void:
if mat == null:
return
mat.albedo_texture = texture
mat.uv1_scale = scale
mat.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST # пиксель-арт