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 _noise(seed_v: int, freq: float, ntype: int = FastNoiseLite.TYPE_PERLIN) -> FastNoiseLite:
	var n: FastNoiseLite = FastNoiseLite.new()
	n.seed = seed_v
	n.frequency = freq
	n.noise_type = ntype
	return n


# Бежевые обои — растительный узор поверх шумной базы
func wallpaper() -> ImageTexture:
	return _get_or_create("wallpaper", func() -> ImageTexture:
		var size: int = 128
		var base: Color = Color(0.83, 0.72, 0.55)
		var darker: Color = Color(0.7, 0.58, 0.42)
		var darker_dark: Color = Color(0.58, 0.46, 0.32)
		var img: Image = _make_image(size, size, base)

		var noise: FastNoiseLite = _noise(424242, 0.06)

		# Шумная база
		for y in range(size):
			for x in range(size):
				var n: float = noise.get_noise_2d(x, y) * 0.5 + 0.5
				var c: Color = base.lerp(darker, n * 0.18)
				# Лёгкая горизонтальная полоса (стык обоев каждые 32px)
				if y % 32 == 0:
					c = c.darkened(0.04)
				img.set_pixel(x, y, c)

		# Цветочки шахматно
		var spacing: int = 22
		for row in range(0, size / spacing + 1):
			for col in range(0, size / spacing + 1):
				var offset_x: int = (spacing / 2) if row % 2 == 0 else 0
				var cx: int = (col * spacing + offset_x) % size
				var cy: int = row * spacing
				if cy >= size:
					continue
				# Лепестки крестом 3х3, центр темнее
				_draw_flower(img, cx, cy, size, darker, darker_dark)

		return _to_texture(img)
	)


func _draw_flower(img: Image, cx: int, cy: int, size: int, petal: Color, center: Color) -> void:
	var w: int = img.get_width()
	var h: int = img.get_height()
	# 5-точечный крест
	var pts: Array = [
		[0, 0, center],
		[-1, 0, petal], [1, 0, petal],
		[0, -1, petal], [0, 1, petal],
		[-2, 0, petal], [2, 0, petal],
		[0, -2, petal], [0, 2, petal],
	]
	for p in pts:
		var x: int = cx + p[0]
		var y: int = cy + p[1]
		if x >= 0 and x < w and y >= 0 and y < h:
			img.set_pixel(x, y, p[2])


# Линолеум: «под светлый паркет» — доски с волокном
func linoleum() -> ImageTexture:
	return _get_or_create("linoleum", func() -> ImageTexture:
		var size: int = 128
		var base: Color = Color(0.72, 0.6, 0.41)
		var dark: Color = Color(0.55, 0.43, 0.28)
		var darker: Color = Color(0.4, 0.3, 0.18)
		var lighter: Color = Color(0.85, 0.74, 0.55)
		var img: Image = _make_image(size, size, base)

		var grain_noise: FastNoiseLite = _noise(1234, 0.25)
		var plank_h: int = 16  # высота доски
		var plank_w: int = 64  # ширина

		for y in range(size):
			var plank_row: int = y / plank_h
			var plank_offset: int = (plank_row * 23) % plank_w
			for x in range(size):
				# Тон доски варьируется
				var plank_idx: int = (x + plank_offset) / plank_w
				var plank_tint: float = float((plank_row + plank_idx) % 4) / 4.0
				var tinted: Color = base.lerp(lighter if plank_tint > 0.5 else dark, plank_tint * 0.25)
				# Волокно — вертикальные тонкие штрихи через шум
				var grain: float = grain_noise.get_noise_2d(x, y * 4) * 0.5 + 0.5
				tinted = tinted.lerp(dark, grain * 0.25)
				img.set_pixel(x, y, tinted)

		# Швы между досками (горизонтальные)
		for y in range(0, size, plank_h):
			for x in range(size):
				img.set_pixel(x, y, darker)
		# Швы между досками (вертикальные, смещённые)
		for row in range(size / plank_h):
			var offset: int = (row * 23) % plank_w
			var y_start: int = row * plank_h
			for col in range(size / plank_w + 1):
				var seam_x: int = (col * plank_w + offset) % size
				for dy in range(1, plank_h):
					if y_start + dy < size:
						img.set_pixel(seam_x, y_start + dy, darker)

		return _to_texture(img)
	)


# Потолок: побелка с очень лёгким шумом + пятно протёчки
func ceiling() -> ImageTexture:
	return _get_or_create("ceiling", func() -> ImageTexture:
		var size: int = 64
		var base: Color = Color(0.92, 0.9, 0.85)
		var spot: Color = Color(0.85, 0.82, 0.74)
		var stain: Color = Color(0.78, 0.72, 0.62)
		var img: Image = _make_image(size, size, base)
		var noise: FastNoiseLite = _noise(8888, 0.12)
		for y in range(size):
			for x in range(size):
				var n: float = noise.get_noise_2d(x, y) * 0.5 + 0.5
				var c: Color = base.lerp(spot, n * 0.18)
				img.set_pixel(x, y, c)
		# Пятно от протёчки в углу
		var cx: int = 12
		var cy: int = 12
		for r in range(8):
			for ang in range(8):
				var a: float = float(ang) * PI / 4.0
				var px: int = cx + int(cos(a) * r)
				var py: int = cy + int(sin(a) * r)
				if px >= 0 and px < size and py >= 0 and py < size:
					var t: float = 1.0 - (float(r) / 8.0)
					img.set_pixel(px, py, base.lerp(stain, t * 0.4))
		return _to_texture(img)
	)


# Бордовый ковёр персидский с медальоном
func wall_carpet() -> ImageTexture:
	return _get_or_create("wall_carpet", func() -> ImageTexture:
		var size: int = 128
		var base: Color = Color(0.36, 0.12, 0.13)
		var dark: Color = Color(0.22, 0.07, 0.08)
		var gold: Color = Color(0.78, 0.62, 0.2)
		var cream: Color = Color(0.85, 0.75, 0.5)
		var img: Image = _make_image(size, size, base)

		var noise: FastNoiseLite = _noise(5555, 0.1)
		# Шумная основа
		for y in range(size):
			for x in range(size):
				var n: float = noise.get_noise_2d(x, y) * 0.5 + 0.5
				img.set_pixel(x, y, base.lerp(dark, n * 0.25))

		# Внешняя рамка
		for i in range(size):
			for t in range(3):
				img.set_pixel(i, t, dark)
				img.set_pixel(i, size - 1 - t, dark)
				img.set_pixel(t, i, dark)
				img.set_pixel(size - 1 - t, i, dark)
		# Внутренняя золотая полоса
		for i in range(6, size - 6):
			img.set_pixel(i, 5, gold)
			img.set_pixel(i, size - 6, gold)
			img.set_pixel(5, i, gold)
			img.set_pixel(size - 6, i, gold)

		# Центральный медальон-ромб
		var cx: int = size / 2
		var cy: int = size / 2
		var med_r: int = 32
		for r in range(med_r):
			var w: int = med_r - r
			for dx in range(-w, w + 1):
				var py1: int = cy - r
				var py2: int = cy + r
				var px: int = cx + dx
				if px >= 0 and px < size and py1 >= 0:
					if r == med_r - 1 or abs(dx) == w:
						img.set_pixel(px, py1, gold)
					elif r % 4 == 0 and abs(dx) % 4 == 0:
						img.set_pixel(px, py1, cream)
				if px >= 0 and px < size and py2 < size:
					if r == med_r - 1 or abs(dx) == w:
						img.set_pixel(px, py2, gold)
					elif r % 4 == 0 and abs(dx) % 4 == 0:
						img.set_pixel(px, py2, cream)

		# Уголки
		var corners: Array = [[20, 20], [size - 20, 20], [20, size - 20], [size - 20, size - 20]]
		for c in corners:
			for r in range(8):
				var w: int = 8 - r
				for dx in range(-w, w + 1):
					var py: int = c[1] + r
					var px: int = c[0] + dx
					if px >= 0 and px < size and py >= 0 and py < size:
						if abs(dx) == w or r == 7:
							img.set_pixel(px, py, gold)
					py = c[1] - r
					if px >= 0 and px < size and py >= 0 and py < size:
						if abs(dx) == w or r == 7:
							img.set_pixel(px, py, gold)

		return _to_texture(img)
	)


# Тёмно-синее одеяло «звёзды»
func blanket() -> ImageTexture:
	return _get_or_create("blanket", func() -> ImageTexture:
		var size: int = 64
		var base: Color = Color(0.13, 0.16, 0.32)
		var deeper: Color = Color(0.09, 0.11, 0.24)
		var star_w: Color = Color(0.9, 0.88, 0.78)
		var star_y: Color = Color(0.94, 0.85, 0.45)
		var img: Image = _make_image(size, size, base)

		var noise: FastNoiseLite = _noise(7777, 0.08)
		for y in range(size):
			for x in range(size):
				var n: float = noise.get_noise_2d(x, y) * 0.5 + 0.5
				img.set_pixel(x, y, base.lerp(deeper, n * 0.4))

		var rng: RandomNumberGenerator = RandomNumberGenerator.new()
		rng.seed = 9999
		for i in range(30):
			var sx: int = rng.randi_range(2, size - 3)
			var sy: int = rng.randi_range(2, size - 3)
			var c: Color = star_y if rng.randf() > 0.7 else star_w
			var big: bool = rng.randf() > 0.7
			img.set_pixel(sx, sy, c)
			if big:
				img.set_pixel(sx + 1, sy, c)
				img.set_pixel(sx - 1, sy, c)
				img.set_pixel(sx, sy + 1, c)
				img.set_pixel(sx, sy - 1, c)
		return _to_texture(img)
	)


# Постер CS:GO — жёлтый с силуэтом AWP + надпись
func poster_csgo() -> ImageTexture:
	return _get_or_create("poster_csgo", func() -> ImageTexture:
		var w: int = 64
		var h: int = 80
		var base: Color = Color(0.94, 0.71, 0.1)
		var dark: Color = Color(0.18, 0.13, 0.08)
		var stripe: Color = Color(0.88, 0.65, 0.08)
		var img: Image = _make_image(w, h, base)

		# Диагональная полоса
		for y in range(h):
			for x in range(w):
				if (x + y) % 8 < 2:
					img.set_pixel(x, y, stripe)

		# Рамка
		for i in range(w):
			img.set_pixel(i, 0, dark)
			img.set_pixel(i, 1, dark)
			img.set_pixel(i, h - 1, dark)
			img.set_pixel(i, h - 2, dark)
		for i in range(h):
			img.set_pixel(0, i, dark)
			img.set_pixel(1, i, dark)
			img.set_pixel(w - 1, i, dark)
			img.set_pixel(w - 2, i, dark)

		# Силуэт AWP горизонтально по центру
		var by: int = 30
		# приклад
		for x in range(8, 14):
			img.set_pixel(x, by + 2, dark)
			img.set_pixel(x, by + 3, dark)
			img.set_pixel(x, by + 4, dark)
		# ствол
		for x in range(14, 52):
			img.set_pixel(x, by + 3, dark)
		# прицел
		for x in range(28, 36):
			img.set_pixel(x, by + 1, dark)
		for x in range(30, 34):
			img.set_pixel(x, by, dark)
		# рукоятка
		img.set_pixel(18, by + 4, dark)
		img.set_pixel(18, by + 5, dark)
		img.set_pixel(17, by + 5, dark)

		# CS:GO текст внизу
		_draw_text_csgo(img, 18, 60, dark)

		return _to_texture(img)
	)


func _draw_text_csgo(img: Image, x0: int, y0: int, c: Color) -> void:
	# C
	for dy in range(8):
		img.set_pixel(x0, y0 + dy, c)
	for dx in range(1, 5):
		img.set_pixel(x0 + dx, y0, c)
		img.set_pixel(x0 + dx, y0 + 7, c)
	# S
	var sx: int = x0 + 7
	for dx in range(5):
		img.set_pixel(sx + dx, y0, c)
		img.set_pixel(sx + dx, y0 + 3, c)
		img.set_pixel(sx + dx, y0 + 7, c)
	for dy in range(1, 3):
		img.set_pixel(sx, y0 + dy, c)
	for dy in range(4, 7):
		img.set_pixel(sx + 4, y0 + dy, c)
	# :
	img.set_pixel(x0 + 14, y0 + 2, c)
	img.set_pixel(x0 + 14, y0 + 5, c)
	# G
	var gx: int = x0 + 17
	for dy in range(8):
		img.set_pixel(gx, y0 + dy, c)
	for dx in range(1, 5):
		img.set_pixel(gx + dx, y0, c)
		img.set_pixel(gx + dx, y0 + 7, c)
	img.set_pixel(gx + 4, y0 + 4, c)
	img.set_pixel(gx + 4, y0 + 5, c)
	img.set_pixel(gx + 4, y0 + 6, c)
	img.set_pixel(gx + 3, y0 + 4, c)
	# O
	var ox: int = x0 + 24
	for dy in range(8):
		img.set_pixel(ox, y0 + dy, c)
		img.set_pixel(ox + 4, y0 + dy, c)
	for dx in range(1, 4):
		img.set_pixel(ox + dx, y0, c)
		img.set_pixel(ox + dx, y0 + 7, c)


# Постер Моргенштерн — тёмно-красный с белой головой
func poster_morgen() -> ImageTexture:
	return _get_or_create("poster_morgen", func() -> ImageTexture:
		var w: int = 64
		var h: int = 80
		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 pink: Color = Color(0.95, 0.7, 0.65)
		var img: Image = _make_image(w, h, base)

		# Рамка
		for i in range(w):
			img.set_pixel(i, 0, dark)
			img.set_pixel(i, 1, dark)
			img.set_pixel(i, h - 1, dark)
			img.set_pixel(i, h - 2, dark)
		for i in range(h):
			img.set_pixel(0, i, dark)
			img.set_pixel(1, i, dark)
			img.set_pixel(w - 1, i, dark)
			img.set_pixel(w - 2, i, dark)

		# Голова в круге
		var cx: int = w / 2
		var cy: int = 32
		var radius: int = 18
		for y in range(h):
			for x in range(w):
				var d: float = sqrt(pow(x - cx, 2) + pow(y - cy, 2))
				if d <= float(radius):
					var t: float = d / float(radius)
					img.set_pixel(x, y, light.lerp(pink, t * 0.6))
				if d > float(radius) - 1 and d <= float(radius):
					img.set_pixel(x, y, dark)

		# Глаза-щели
		img.set_pixel(cx - 5, cy - 2, dark)
		img.set_pixel(cx - 4, cy - 2, dark)
		img.set_pixel(cx + 4, cy - 2, dark)
		img.set_pixel(cx + 5, cy - 2, dark)
		# Рот
		for dx in range(-4, 5):
			img.set_pixel(cx + dx, cy + 6, dark)

		# Лого MS внизу
		_draw_text_ms(img, 22, 60, light)
		return _to_texture(img)
	)


func _draw_text_ms(img: Image, x0: int, y0: int, c: Color) -> void:
	# M
	for dy in range(8):
		img.set_pixel(x0, y0 + dy, c)
		img.set_pixel(x0 + 6, y0 + dy, c)
	img.set_pixel(x0 + 1, y0 + 1, c)
	img.set_pixel(x0 + 5, y0 + 1, c)
	img.set_pixel(x0 + 2, y0 + 2, c)
	img.set_pixel(x0 + 4, y0 + 2, c)
	img.set_pixel(x0 + 3, y0 + 3, c)
	# S
	var sx: int = x0 + 12
	for dx in range(5):
		img.set_pixel(sx + dx, y0, c)
		img.set_pixel(sx + dx, y0 + 3, c)
		img.set_pixel(sx + dx, y0 + 7, c)
	for dy in range(1, 3):
		img.set_pixel(sx, y0 + dy, c)
	for dy in range(4, 7):
		img.set_pixel(sx + 4, y0 + dy, c)


# Дверь: вертикальные деревянные планки + сучки
func door() -> ImageTexture:
	return _get_or_create("door", func() -> ImageTexture:
		var w: int = 64
		var h: int = 128
		var base: Color = Color(0.45, 0.31, 0.18)
		var dark: Color = Color(0.28, 0.18, 0.09)
		var light: Color = Color(0.55, 0.4, 0.25)
		var knot: Color = Color(0.18, 0.1, 0.05)
		var img: Image = _make_image(w, h, base)

		# Вертикальное волокно через шум
		var grain: FastNoiseLite = _noise(3333, 0.2)
		grain.noise_type = FastNoiseLite.TYPE_PERLIN
		for y in range(h):
			for x in range(w):
				var n: float = grain.get_noise_2d(x * 0.5, y * 3) * 0.5 + 0.5
				var c: Color = base.lerp(dark if n > 0.5 else light, abs(n - 0.5) * 1.2)
				img.set_pixel(x, y, c)

		# Панели — горизонтальные швы
		for y in [32, 64, 96]:
			for x in range(w):
				img.set_pixel(x, y, dark)
				if y + 1 < h:
					img.set_pixel(x, y + 1, dark)

		# Сучки
		var knots: Array = [[18, 20], [42, 50], [25, 85], [48, 110]]
		for k in knots:
			for r in range(4):
				for ang in range(12):
					var a: float = float(ang) * PI / 6.0
					var px: int = k[0] + int(cos(a) * r)
					var py: int = k[1] + int(sin(a) * r)
					if px >= 0 and px < w and py >= 0 and py < h:
						var t: float = 1.0 - (float(r) / 4.0)
						img.set_pixel(px, py, base.lerp(knot, t))

		return _to_texture(img)
	)


# Стол MICKE: белый с минимальной шумной фактурой + наклейка-стикер
func desk_white() -> ImageTexture:
	return _get_or_create("desk_white", func() -> ImageTexture:
		var size: int = 64
		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(size, size, base)
		var noise: FastNoiseLite = _noise(1337, 0.18)
		for y in range(size):
			for x in range(size):
				var n: float = noise.get_noise_2d(x, y) * 0.5 + 0.5
				img.set_pixel(x, y, base.lerp(nick, n * 0.08))
		# Стикер в углу — жёлтый Steam-inventory
		var sticker: Color = Color(0.94, 0.71, 0.1)
		for x in range(2, 8):
			for y in range(2, 8):
				img.set_pixel(x, y, sticker)
		# Царапины
		for x in range(20, 40):
			img.set_pixel(x, 32, nick.darkened(0.1))
		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 = base.darkened(0.4)
		var hi: Color = base.lightened(0.2)
		var w: int = 16
		var h: int = 64
		var img: Image = _make_image(w, h, base)
		# Верхние/нижние полосы
		for x in range(w):
			img.set_pixel(x, 0, trim)
			img.set_pixel(x, 1, trim)
			img.set_pixel(x, h - 1, trim)
			img.set_pixel(x, h - 2, trim)
			img.set_pixel(x, 6, hi)
			img.set_pixel(x, h - 7, hi)
		# Тонкая текстура корешка
		for y in range(8, h - 8):
			img.set_pixel(0, y, trim)
			img.set_pixel(w - 1, y, trim)
		return _to_texture(img)
	)


# Этикетка Adrenaline Rush — синий со штрихами + молния
func adrenaline_label() -> ImageTexture:
	return _get_or_create("adrenaline_label", func() -> ImageTexture:
		var w: int = 64
		var h: int = 64
		var blue_dark: Color = Color(0.08, 0.18, 0.45)
		var blue: Color = Color(0.13, 0.32, 0.68)
		var blue_light: Color = Color(0.3, 0.5, 0.85)
		var yellow: Color = Color(0.95, 0.85, 0.2)
		var img: Image = _make_image(w, h, blue)
		# Диагональные полосы
		for y in range(h):
			for x in range(w):
				var d: int = (x + y) % 12
				if d < 2:
					img.set_pixel(x, y, blue_dark)
				elif d < 4:
					img.set_pixel(x, y, blue_light)
		# Молния по центру
		var bolt: Array = [
			[32, 10], [32, 12], [31, 14], [31, 16], [30, 18], [33, 20],
			[33, 22], [32, 24], [32, 26], [31, 28], [34, 30], [34, 32],
			[33, 34], [33, 36], [32, 38], [35, 40], [34, 42], [33, 44],
			[32, 46], [31, 48], [30, 50], [29, 52]
		]
		for p in bolt:
			img.set_pixel(p[0], p[1], yellow)
			if p[0] + 1 < w:
				img.set_pixel(p[0] + 1, p[1], yellow)
			if p[0] - 1 >= 0:
				img.set_pixel(p[0] - 1, p[1], yellow)
		# Верх/низ — тёмные полосы
		for x in range(w):
			img.set_pixel(x, 0, blue_dark)
			img.set_pixel(x, 1, blue_dark)
			img.set_pixel(x, h - 1, blue_dark)
			img.set_pixel(x, h - 2, blue_dark)
		return _to_texture(img)
	)


# Обёртка Lay's — жёлтая с красной волной
func lays_wrapper() -> ImageTexture:
	return _get_or_create("lays_wrapper", func() -> ImageTexture:
		var w: int = 64
		var h: int = 48
		var yellow: Color = Color(0.96, 0.84, 0.18)
		var red: Color = Color(0.78, 0.12, 0.12)
		var red_dark: Color = Color(0.55, 0.08, 0.08)
		var brown: Color = Color(0.35, 0.22, 0.1)
		var img: Image = _make_image(w, h, yellow)
		# Красная волнистая лента посередине
		for x in range(w):
			var wave: int = int(sin(x * 0.3) * 4.0)
			var band_y: int = 20 + wave
			for dy in range(-4, 5):
				var y: int = band_y + dy
				if y >= 0 and y < h:
					if dy == -4 or dy == 4:
						img.set_pixel(x, y, red_dark)
					else:
						img.set_pixel(x, y, red)
		# Силуэт чипса (овальная коричневая клякса)
		for ang in range(20):
			var a: float = float(ang) * PI / 10.0
			var px: int = 16 + int(cos(a) * 6.0)
			var py: int = 38 + int(sin(a) * 3.0)
			if px >= 0 and px < w and py >= 0 and py < h:
				img.set_pixel(px, py, brown)
		# Уголки обёртки темнее
		for i in range(3):
			img.set_pixel(i, h - 1 - i, red_dark)
			img.set_pixel(w - 1 - i, h - 1 - i, red_dark)
		return _to_texture(img)
	)


# Носок — тёмно-серый со светлыми полосками
func sock_fabric() -> ImageTexture:
	return _get_or_create("sock_fabric", func() -> ImageTexture:
		var w: int = 32
		var h: int = 32
		var base: Color = Color(0.18, 0.22, 0.32)
		var stripe: Color = Color(0.28, 0.32, 0.42)
		var dark: Color = Color(0.1, 0.13, 0.2)
		var img: Image = _make_image(w, h, base)
		for y in range(h):
			if y % 6 == 0 or y % 6 == 1:
				for x in range(w):
					img.set_pixel(x, y, stripe)
			elif y % 6 == 3:
				for x in range(w):
					img.set_pixel(x, y, dark)
		return _to_texture(img)
	)


# Экран монитора — тёмное стекло с легким bluish свечением
func monitor_screen() -> ImageTexture:
	return _get_or_create("monitor_screen", func() -> ImageTexture:
		var w: int = 64
		var h: int = 48
		var dark: Color = Color(0.04, 0.05, 0.08)
		var glow: Color = Color(0.15, 0.2, 0.3)
		var scan: Color = Color(0.08, 0.1, 0.14)
		var img: Image = _make_image(w, h, dark)
		# Боковые градиенты к свечению
		for y in range(h):
			for x in range(w):
				var dx: float = abs(float(x) - float(w) / 2.0) / (float(w) / 2.0)
				var dy: float = abs(float(y) - float(h) / 2.0) / (float(h) / 2.0)
				var edge: float = max(dx, dy)
				var c: Color = dark.lerp(glow, (1.0 - edge) * 0.4)
				img.set_pixel(x, y, c)
		# Горизонтальные scanlines каждые 2 строки
		for y in range(0, h, 2):
			for x in range(w):
				img.set_pixel(x, y, img.get_pixel(x, y).lerp(scan, 0.4))
		return _to_texture(img)
	)


# Применить текстуру к материалу с заданным uv-scale + nearest filter
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
