act1: фиксы багов + crosshair + улучшенные текстуры
Some checks failed
Godot CI / Import + smoke test (push) Failing after 14s
Some checks failed
Godot CI / Import + smoke test (push) Failing after 14s
БАГ-ФИКСЫ: - Трясёт у двери: Interactable теперь extends Area3D (не StaticBody3D). RayCast3D детектит Area3D через collide_with_areas=true. Игрок физически проходит через интерактивы (gift box, флешка, записка, дверь) — не толкается в узких местах. Бампается только в стены/мебель/пол. - Все 6 интерактивов в act1_room.tscn и act1_kitchen.tscn переведены на type="Area3D". UI: - Crosshair в центре экрана: 8×8 точка с тёмной рамкой и белой центральной 2×2 точкой. set_crosshair_visible() для 2D-сцен (Акт 2 прячет его). - SubtitlePanel расширен: 16-624 × 240-354 (было 24-616 × 270-350). Text-лейбл заменён на RichTextLabel с fit_content + bbcode. Длинные реплики отца теперь нормально влезают. - NoteOverlay аналогично RichTextLabel. - ControlsHint перенесён сверху (под HUD-датой), 14px меньше. ТЕКСТУРЫ через FastNoiseLite + 128×128 разрешалка: - wallpaper: шумная бежевая база + растительный крест-цветок каждые 22px, шахматное смещение, тонкая горизонтальная полоса каждые 32px (стык обоев). - linoleum: настоящая паркетная доска 16×64px со смещением между рядами, тонкие швы, волокно через шум. - ceiling: побелка с пятном протёчки в углу (canon-деталь). - wall_carpet: бордовая база + двойная золотая рамка + ромб- медальон с золотыми углами + декоративные уголки. - blanket: 30 звёздочек (белые/жёлтые) разного размера на шумной тёмно-синей базе. - poster_csgo: диагональная штриховка + силуэт AWP по центру + пиксельный «CS:GO» текст внизу. - poster_morgen: круг с лицом, глаза-щели, рот + пиксельный «MS». - door: вертикальное волокно Perlin + 3 горизонтальных шва (панели) + 4 сучка разного размера. - desk_white: чистый белый + жёлтый Steam-стикер в углу + царапина. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
cc23e7fe3c
commit
773b15fd8e
8 changed files with 498 additions and 183 deletions
|
|
@ -34,44 +34,73 @@ modulate = Color(0.75, 0.75, 0.75, 1)
|
|||
|
||||
[node name="WeatherLabel" type="Label" parent="."]
|
||||
offset_left = 340.0
|
||||
offset_top = 332.0
|
||||
offset_top = 215.0
|
||||
offset_right = 632.0
|
||||
offset_bottom = 348.0
|
||||
offset_bottom = 231.0
|
||||
text = ""
|
||||
horizontal_alignment = 2
|
||||
modulate = Color(0.7, 0.75, 0.85, 1)
|
||||
|
||||
[node name="StatsLabel" type="Label" parent="."]
|
||||
offset_left = 8.0
|
||||
offset_top = 332.0
|
||||
offset_top = 215.0
|
||||
offset_right = 300.0
|
||||
offset_bottom = 348.0
|
||||
offset_bottom = 231.0
|
||||
text = ""
|
||||
modulate = Color(0.94, 0.71, 0.1, 1)
|
||||
|
||||
[node name="ControlsHint" type="Label" parent="."]
|
||||
offset_left = 0.0
|
||||
offset_top = 246.0
|
||||
offset_top = 28.0
|
||||
offset_right = 640.0
|
||||
offset_bottom = 260.0
|
||||
text = "WASD — ходить · мышь — смотреть · ЛКМ — взаимодействие · ESC — мышь · Q — выход"
|
||||
offset_bottom = 42.0
|
||||
text = "WASD ходить · мышь смотреть · ЛКМ взаимодействие · ESC мышь · Q выход"
|
||||
horizontal_alignment = 1
|
||||
modulate = Color(0.5, 0.5, 0.5, 0.8)
|
||||
modulate = Color(0.45, 0.45, 0.45, 0.8)
|
||||
|
||||
[node name="Crosshair" type="Control" parent="."]
|
||||
offset_left = 316.0
|
||||
offset_top = 176.0
|
||||
offset_right = 324.0
|
||||
offset_bottom = 184.0
|
||||
|
||||
[node name="CenterDot" type="ColorRect" parent="Crosshair"]
|
||||
offset_left = 3.0
|
||||
offset_top = 3.0
|
||||
offset_right = 5.0
|
||||
offset_bottom = 5.0
|
||||
color = Color(1, 1, 1, 0.85)
|
||||
|
||||
[node name="OuterRing" type="ColorRect" parent="Crosshair"]
|
||||
offset_left = 0.0
|
||||
offset_top = 0.0
|
||||
offset_right = 8.0
|
||||
offset_bottom = 8.0
|
||||
color = Color(0, 0, 0, 0.4)
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="InnerHole" type="ColorRect" parent="Crosshair"]
|
||||
offset_left = 1.0
|
||||
offset_top = 1.0
|
||||
offset_right = 7.0
|
||||
offset_bottom = 7.0
|
||||
color = Color(0, 0, 0, 0)
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="PromptLabel" type="Label" parent="."]
|
||||
offset_left = 0.0
|
||||
offset_top = 200.0
|
||||
offset_top = 192.0
|
||||
offset_right = 640.0
|
||||
offset_bottom = 220.0
|
||||
offset_bottom = 212.0
|
||||
text = ""
|
||||
horizontal_alignment = 1
|
||||
modulate = Color(0.94, 0.71, 0.1, 1)
|
||||
|
||||
[node name="SubtitlePanel" type="PanelContainer" parent="."]
|
||||
offset_left = 24.0
|
||||
offset_top = 270.0
|
||||
offset_right = 616.0
|
||||
offset_bottom = 350.0
|
||||
offset_left = 16.0
|
||||
offset_top = 240.0
|
||||
offset_right = 624.0
|
||||
offset_bottom = 354.0
|
||||
|
||||
[node name="V" type="VBoxContainer" parent="SubtitlePanel"]
|
||||
layout_mode = 2
|
||||
|
|
@ -81,9 +110,13 @@ layout_mode = 2
|
|||
text = "[Я]"
|
||||
modulate = Color(0.94, 0.71, 0.1, 1)
|
||||
|
||||
[node name="Text" type="Label" parent="SubtitlePanel/V"]
|
||||
[node name="Text" type="RichTextLabel" parent="SubtitlePanel/V"]
|
||||
custom_minimum_size = Vector2(590, 60)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = ""
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
autowrap_mode = 3
|
||||
|
||||
[node name="Hint" type="Label" parent="SubtitlePanel/V"]
|
||||
|
|
@ -104,7 +137,7 @@ anchor_right = 1.0
|
|||
anchor_bottom = 1.0
|
||||
offset_right = 640.0
|
||||
offset_bottom = 360.0
|
||||
color = Color(0, 0, 0, 0.75)
|
||||
color = Color(0, 0, 0, 0.78)
|
||||
|
||||
[node name="Center" type="CenterContainer" parent="NoteOverlay"]
|
||||
anchor_right = 1.0
|
||||
|
|
@ -113,7 +146,7 @@ offset_right = 640.0
|
|||
offset_bottom = 360.0
|
||||
|
||||
[node name="P" type="PanelContainer" parent="NoteOverlay/Center"]
|
||||
custom_minimum_size = Vector2(420, 180)
|
||||
custom_minimum_size = Vector2(440, 200)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="V2" type="VBoxContainer" parent="NoteOverlay/Center/P"]
|
||||
|
|
@ -125,11 +158,13 @@ text = "Записка"
|
|||
horizontal_alignment = 1
|
||||
modulate = Color(0.94, 0.71, 0.1, 1)
|
||||
|
||||
[node name="Text" type="Label" parent="NoteOverlay/Center/P/V2"]
|
||||
[node name="Text" type="RichTextLabel" parent="NoteOverlay/Center/P/V2"]
|
||||
custom_minimum_size = Vector2(420, 100)
|
||||
layout_mode = 2
|
||||
bbcode_enabled = true
|
||||
text = ""
|
||||
fit_content = true
|
||||
autowrap_mode = 3
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Hint" type="Label" parent="NoteOverlay/Center/P/V2"]
|
||||
layout_mode = 2
|
||||
|
|
|
|||
|
|
@ -23,3 +23,4 @@ far = 50.0
|
|||
[node name="InteractRay" type="RayCast3D" parent="Camera3D"]
|
||||
target_position = Vector3(0, 0, -2.5)
|
||||
collision_mask = 1
|
||||
collide_with_areas = true
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.7, -0.25, -0.06)
|
|||
mesh = SubResource("mesh_door_panel")
|
||||
material_override = SubResource("mat_door")
|
||||
|
||||
[node name="DoorBack" type="StaticBody3D" parent="WallS"]
|
||||
[node name="DoorBack" type="Area3D" parent="WallS"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.7, -0.25, -0.12)
|
||||
script = ExtResource("2_int")
|
||||
prompt = "вернуться в комнату"
|
||||
|
|
@ -254,7 +254,7 @@ material_override = SubResource("mat_fridge")
|
|||
[node name="Coll" type="CollisionShape3D" parent="Fridge"]
|
||||
shape = SubResource("shape_fridge")
|
||||
|
||||
[node name="WiFiNote" type="StaticBody3D" parent="Fridge"]
|
||||
[node name="WiFiNote" type="Area3D" parent="Fridge"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0.31)
|
||||
script = ExtResource("2_int")
|
||||
prompt = "прочитать записку"
|
||||
|
|
|
|||
|
|
@ -396,7 +396,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.06, -0.25, 0)
|
|||
mesh = SubResource("mesh_door")
|
||||
material_override = SubResource("mat_door")
|
||||
|
||||
[node name="Door" type="StaticBody3D" parent="WallE"]
|
||||
[node name="Door" type="Area3D" parent="WallE"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.1, -0.25, 0)
|
||||
script = ExtResource("2_int")
|
||||
prompt = "выйти на кухню"
|
||||
|
|
@ -493,7 +493,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.1, 0.36, 0.0)
|
|||
mesh = SubResource("mesh_lays")
|
||||
material_override = SubResource("mat_lays")
|
||||
|
||||
[node name="FlashDrive" type="StaticBody3D" parent="Desk"]
|
||||
[node name="FlashDrive" type="Area3D" parent="Desk"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.4, 0.36, 0.15)
|
||||
script = ExtResource("2_int")
|
||||
prompt = "взять флешку"
|
||||
|
|
@ -535,7 +535,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.85, 0.015, 0.2)
|
|||
mesh = SubResource("mesh_sock")
|
||||
material_override = SubResource("mat_sock")
|
||||
|
||||
[node name="GiftBox" type="StaticBody3D" parent="."]
|
||||
[node name="GiftBox" type="Area3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.2, -0.3)
|
||||
script = ExtResource("2_int")
|
||||
prompt = "открыть коробку"
|
||||
|
|
@ -555,7 +555,7 @@ visible = false
|
|||
mesh = SubResource("mesh_sys")
|
||||
material_override = SubResource("mat_sys")
|
||||
|
||||
[node name="Note" type="StaticBody3D" parent="SystemUnit"]
|
||||
[node name="Note" type="Area3D" parent="SystemUnit"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.26, 0)
|
||||
script = ExtResource("2_int")
|
||||
prompt = "прочитать записку"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
class_name Interactable extends StaticBody3D
|
||||
class_name Interactable extends Area3D
|
||||
|
||||
@export var prompt: String = "Взаимодействовать"
|
||||
@export var enabled: bool = false
|
||||
|
|
@ -7,6 +7,7 @@ signal interacted(player: Node)
|
|||
|
||||
|
||||
func _ready() -> void:
|
||||
monitoring = false
|
||||
_apply_collision()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ func _ready() -> void:
|
|||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
||||
DialogManager.clear_prompt()
|
||||
DialogManager.set_controls_hint("")
|
||||
DialogManager.set_crosshair_visible(false)
|
||||
DialogManager.set_date_time(GameState.game_date, GameState.game_time)
|
||||
DialogManager.set_objective("Установи Шиндовс 9")
|
||||
|
||||
|
|
|
|||
|
|
@ -4,14 +4,15 @@ signal advanced
|
|||
|
||||
@onready var subtitle_panel: PanelContainer = $SubtitlePanel
|
||||
@onready var who_label: Label = $SubtitlePanel/V/Who
|
||||
@onready var text_label: Label = $SubtitlePanel/V/Text
|
||||
@onready var text_label: RichTextLabel = $SubtitlePanel/V/Text
|
||||
@onready var hint_label: Label = $SubtitlePanel/V/Hint
|
||||
|
||||
@onready var note_overlay: Control = $NoteOverlay
|
||||
@onready var note_text: Label = $NoteOverlay/Center/P/V2/Text
|
||||
@onready var note_text: RichTextLabel = $NoteOverlay/Center/P/V2/Text
|
||||
|
||||
@onready var prompt_label: Label = $PromptLabel
|
||||
@onready var controls_hint: Label = $ControlsHint
|
||||
@onready var crosshair: Control = $Crosshair
|
||||
|
||||
@onready var date_time_label: Label = $DateTimeLabel
|
||||
@onready var objective_label: Label = $ObjectiveLabel
|
||||
|
|
@ -30,7 +31,11 @@ func _ready() -> void:
|
|||
money_label.visible = false
|
||||
weather_label.visible = false
|
||||
stats_label.visible = false
|
||||
# ControlsHint всегда виден, не блокирует игру
|
||||
crosshair.visible = true
|
||||
|
||||
|
||||
func set_crosshair_visible(v: bool) -> void:
|
||||
crosshair.visible = v
|
||||
|
||||
|
||||
func say(who: String, line: String) -> Signal:
|
||||
|
|
|
|||
|
|
@ -21,98 +21,211 @@ func _get_or_create(key: String, maker: Callable) -> ImageTexture:
|
|||
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 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)
|
||||
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 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)
|
||||
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.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)
|
||||
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 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)
|
||||
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)
|
||||
)
|
||||
|
||||
|
|
@ -120,123 +233,273 @@ func wall_carpet() -> ImageTexture:
|
|||
# Тёмно-синее одеяло «звёзды»
|
||||
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 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)
|
||||
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)
|
||||
# Постер 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 img: Image = _make_image(32, 40, base)
|
||||
# рамка
|
||||
for i in range(32):
|
||||
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, 39, dark)
|
||||
for i in range(40):
|
||||
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(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)
|
||||
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 img: Image = _make_image(32, 40, base)
|
||||
# рамка
|
||||
for i in range(32):
|
||||
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, 39, dark)
|
||||
for i in range(40):
|
||||
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(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)
|
||||
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 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:
|
||||
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: белый с малозаметной шумной текстурой
|
||||
# Стол 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(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)
|
||||
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:
|
||||
|
|
@ -248,22 +511,31 @@ func book_color(color_idx: int) -> ImageTexture:
|
|||
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):
|
||||
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, 31, trim)
|
||||
img.set_pixel(x, 5, trim)
|
||||
img.set_pixel(x, 26, 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)
|
||||
)
|
||||
|
||||
|
||||
# Применить текстуру к материалу с заданным uv-scale
|
||||
# Применить текстуру к материалу с заданным 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 # пиксель-арт
|
||||
mat.texture_filter = BaseMaterial3D.TEXTURE_FILTER_NEAREST
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue