feat: update

This commit is contained in:
2026-04-28 01:22:38 +08:00
parent b76dd2e737
commit 1585b91509
9 changed files with 529 additions and 229 deletions
+8 -2
View File
@@ -1,13 +1,19 @@
## [NEXT] ## [NEXT]
## [2.1.8] — 2026-04-24 ## [2.1.8] — 2026-04-28
- Optimized network synchronization with deterministic tile slots and granular board sync - Optimized network synchronization with deterministic tile slots and granular board sync
- Removed lag-sensitive server adjacency checks to fix "disappearing tiles" on high-latency connections - Removed lag-sensitive server adjacency checks to fix "disappearing tiles" on high-latency connections
- Fixed "No multiplayer peer assigned" crash during host disconnection and cleanup - Fixed "No multiplayer peer assigned" crash during host disconnection and cleanup
- Finalized AP system removal, transitioning to a fully real-time authoritative model - Finalized AP system removal, transitioning to a fully real-time authoritative model
- Restored bot mission completion logic - Restored bot mission completion logic
- Stabilized Nakama socket cleanup during match-to-lobby transitions - Stabilized Nakama socket cleanup during match-to-lobby transitions
- Fixed Tekton not rotating toward spawning direction when throwing tiles
- Fixed purple powerup tiles spawning on top of existing layer 1 tiles
- Added animation delay to tile spawning to match Tekton throw animation timing
- Implemented 1-tile perimeter buffer on all spawn areas to prevent edge spawning and stuck gaps
- Tekton NPCs now spawn exclusively in corner zones; players spawn in middle zones
- Fixed outline shader being lost when applying skin material overrides
- Added Unstuck button to pause menu to teleport stuck players to a safe area
## [2.1.7] — 2026-04-24 ## [2.1.7] — 2026-04-24
- Upgraded Gacha interface with dynamic CSGO-style sequential reveal animations - Upgraded Gacha interface with dynamic CSGO-style sequential reveal animations
+14 -1
View File
@@ -1,7 +1,20 @@
{ {
"latest_version": "2.1.7", "latest_version": "2.1.8",
"minimum_app_version": "2.1.0", "minimum_app_version": "2.1.0",
"releases": [ "releases": [
{
"version": "2.1.8",
"date": "2026-04-28",
"pck_url": "https://raw.githubusercontent.com/adtpdn/tekton-updates/main/latest/patch.pck",
"pck_size": 0,
"changelog": [
"Fixed Tekton rotation and tile spawning animation timing",
"Implemented 1-tile perimeter buffer for all spawn areas to prevent stuck gaps",
"Tekton NPCs now spawn in corner zones; players spawn in middle zones",
"Fixed outline shader being lost when applying skin material overrides",
"Added Unstuck button to pause menu to teleport stuck players to a safe area"
]
},
{ {
"version": "2.1.7", "version": "2.1.7",
"date": "2026-04-24", "date": "2026-04-24",
+251 -69
View File
@@ -21,6 +21,8 @@ var vfx_manager
# Minimal local state # Minimal local state
var _connection_check_timer: float = 0.0 var _connection_check_timer: float = 0.0
var reserved_static_positions: Array[Vector2i] = [] var reserved_static_positions: Array[Vector2i] = []
var _unstuck_cooldown_remaining: float = 0.0
const UNSTUCK_COOLDOWN = 120.0 # 2 minutes
func _can_rpc() -> bool: func _can_rpc() -> bool:
if not is_inside_tree(): return false if not is_inside_tree(): return false
@@ -523,6 +525,19 @@ func _process(delta):
if not is_inside_tree(): return if not is_inside_tree(): return
if not check_multiplayer(): return if not check_multiplayer(): return
# Tick down unstuck cooldown and update button label
if _unstuck_cooldown_remaining > 0.0:
_unstuck_cooldown_remaining -= delta
var unstuck_btn = get_node_or_null("PauseMenu/Panel/VBox/UnstuckBtn")
if unstuck_btn:
if _unstuck_cooldown_remaining > 0.0:
unstuck_btn.text = "Unstuck (%ds)" % ceil(_unstuck_cooldown_remaining)
unstuck_btn.disabled = true
else:
_unstuck_cooldown_remaining = 0.0
unstuck_btn.text = "Unstuck"
unstuck_btn.disabled = false
if ui_manager and get_tree(): if ui_manager and get_tree():
var all_players = get_tree().get_nodes_in_group("Players") var all_players = get_tree().get_nodes_in_group("Players")
if all_players.size() > 0: if all_players.size() > 0:
@@ -849,17 +864,91 @@ func _start_game():
spawn_tekton_npc() spawn_tekton_npc()
# =============================================================================
# Spawn Zone System - Prevents edge gaps with 1-tile perimeter buffer
# =============================================================================
const PERIMETER_BUFFER = 1 # 1-tile safe zone on all sides
enum SpawnZone {
TOP_LEFT_CORNER,
TOP_CENTER,
TOP_RIGHT_CORNER,
MIDDLE_LEFT,
MIDDLE_CENTER,
MIDDLE_RIGHT,
BOTTOM_LEFT_CORNER,
BOTTOM_CENTER,
BOTTOM_RIGHT_CORNER
}
func _get_spawn_zones(gridmap: Node) -> Dictionary:
"""Returns a dictionary of spawn zones based on 3x3 grid layout.
Corner zones are for Tektons, middle zones are for players."""
var width = gridmap.columns
var height = gridmap.rows
# Apply perimeter buffer
var safe_width = width - (PERIMETER_BUFFER * 2)
var safe_height = height - (PERIMETER_BUFFER * 2)
# Divide safe area into 3x3 grid
var zone_w = safe_width / 3
var zone_h = safe_height / 3
var zones = {}
# Define 9 zones with buffer offsets
for row in range(3):
for col in range(3):
var zone_idx = row * 3 + col
var zone_rect = Rect2i(
PERIMETER_BUFFER + (col * zone_w),
PERIMETER_BUFFER + (row * zone_h),
zone_w,
zone_h
)
zones[zone_idx] = zone_rect
return zones
func _is_position_in_zone(pos: Vector2i, zone: Rect2i) -> bool:
"""Check if position is within a spawn zone."""
return zone.has_point(pos)
func _get_tekton_spawn_zones(zones: Dictionary) -> Array:
"""Returns corner zones for Tekton spawning."""
return [
zones[SpawnZone.TOP_LEFT_CORNER],
zones[SpawnZone.TOP_RIGHT_CORNER],
zones[SpawnZone.BOTTOM_LEFT_CORNER],
zones[SpawnZone.BOTTOM_RIGHT_CORNER]
]
func _get_player_spawn_zones(zones: Dictionary) -> Array:
"""Returns middle zones for player spawning."""
return [
zones[SpawnZone.TOP_CENTER],
zones[SpawnZone.MIDDLE_LEFT],
zones[SpawnZone.MIDDLE_CENTER],
zones[SpawnZone.MIDDLE_RIGHT],
zones[SpawnZone.BOTTOM_CENTER]
]
func _assign_random_spawn_positions(): func _assign_random_spawn_positions():
"""Assign spawn positions distributed to 4 corners (2 per corner for 8 players).""" """Assign spawn positions distributed across middle zones (avoiding corners reserved for Tektons)."""
var enhanced_gridmap = $EnhancedGridMap var enhanced_gridmap = $EnhancedGridMap
if not enhanced_gridmap: if not enhanced_gridmap:
return return
# Lists for each quadrant # Get spawn zones with perimeter buffer
var spawns_TL = [] # Top-Left var spawn_zones = _get_spawn_zones(enhanced_gridmap)
var spawns_TR = [] # Top-Right var player_zones = _get_player_spawn_zones(spawn_zones)
var spawns_BL = [] # Bottom-Left
var spawns_BR = [] # Bottom-Right # Lists for player spawns in each zone
var spawns_by_zone = {} # zone_rect -> [positions]
for zone in player_zones:
spawns_by_zone[zone] = []
var all_spawns = [] # Fallback var all_spawns = [] # Fallback
# Stop n Go Custom Spawn Logic # Stop n Go Custom Spawn Logic
@@ -874,23 +963,20 @@ func _assign_random_spawn_positions():
_assign_portal_mode_spawn_positions(all_players) _assign_portal_mode_spawn_positions(all_players)
return return
var mid_x = enhanced_gridmap.columns / 2
var mid_z = enhanced_gridmap.rows / 2
# If static positions were not calculated yet, do it now to avoid players spawning in them # If static positions were not calculated yet, do it now to avoid players spawning in them
if reserved_static_positions.is_empty() and LobbyManager.game_mode != "Stop n Go": if reserved_static_positions.is_empty() and LobbyManager.game_mode != "Stop n Go":
if not static_tekton_manager: if not static_tekton_manager:
static_tekton_manager = preload("res://scripts/managers/static_tekton_manager.gd").new() static_tekton_manager = preload("res://scripts/managers/static_tekton_manager.gd").new()
reserved_static_positions = static_tekton_manager.calculate_spawn_points(3, enhanced_gridmap) reserved_static_positions = static_tekton_manager.calculate_spawn_points(3, enhanced_gridmap)
for x in range(enhanced_gridmap.columns): # Scan grid for walkable positions within player zones (respecting buffer)
for z in range(enhanced_gridmap.rows): for x in range(PERIMETER_BUFFER, enhanced_gridmap.columns - PERIMETER_BUFFER):
for z in range(PERIMETER_BUFFER, enhanced_gridmap.rows - PERIMETER_BUFFER):
var ground = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) var ground = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
if ground == 0: # Walkable if ground == 0: # Walkable
var pos = Vector2i(x, z) var pos = Vector2i(x, z)
# SAFETY CHECK: Is this reserved for a Static Tekton Stand? # SAFETY CHECK: Is this reserved for a Static Tekton Stand?
# Stand clears exactly 3x3 area
var is_safe = true var is_safe = true
for reserved in reserved_static_positions: for reserved in reserved_static_positions:
if abs(x - reserved.x) <= 1 and abs(z - reserved.y) <= 1: if abs(x - reserved.x) <= 1 and abs(z - reserved.y) <= 1:
@@ -900,32 +986,21 @@ func _assign_random_spawn_positions():
if not is_safe: if not is_safe:
continue continue
# Check if position is in any player zone
var in_player_zone = false
for zone in player_zones:
if _is_position_in_zone(pos, zone):
spawns_by_zone[zone].append(pos)
in_player_zone = true
break
# Add to fallback list regardless of zone
if in_player_zone:
all_spawns.append(pos) all_spawns.append(pos)
if x < mid_x and z < mid_z: # Shuffle each zone's spawn list for randomization
spawns_TL.append(pos) for zone in spawns_by_zone:
elif x >= mid_x and z < mid_z: spawns_by_zone[zone].shuffle()
spawns_TR.append(pos)
elif x < mid_x and z >= mid_z:
spawns_BL.append(pos)
else:
spawns_BR.append(pos)
# Sort lists by distance to corners (closest to corner should be last, to be popped first)
# TL: Close to (0,0) -> Sort descending distance (so closest is at end)
spawns_TL.sort_custom(func(a, b): return a.length_squared() > b.length_squared())
# TR: Close to (13, 0)
var tr_corner = Vector2i(enhanced_gridmap.columns - 1, 0)
spawns_TR.sort_custom(func(a, b): return a.distance_squared_to(tr_corner) > b.distance_squared_to(tr_corner))
# BL: Close to (0, 13)
var bl_corner = Vector2i(0, enhanced_gridmap.rows - 1)
spawns_BL.sort_custom(func(a, b): return a.distance_squared_to(bl_corner) > b.distance_squared_to(bl_corner))
# BR: Close to (13, 13)
var br_corner = Vector2i(enhanced_gridmap.columns - 1, enhanced_gridmap.rows - 1)
spawns_BR.sort_custom(func(a, b): return a.distance_squared_to(br_corner) > b.distance_squared_to(br_corner))
# Fallback shuffle # Fallback shuffle
all_spawns.shuffle() all_spawns.shuffle()
@@ -936,24 +1011,24 @@ func _assign_random_spawn_positions():
var spawn_index = 0 var spawn_index = 0
# Round-robin assignment to corners: TL, TR, BR, BL, TL, TR, BR, BL... # Round-robin assignment across player zones
# Order: TL -> TR -> BR -> BL (Clockwise-ish) var zone_list = spawns_by_zone.keys()
var quadrants = [spawns_TL, spawns_TR, spawns_BR, spawns_BL] var zone_arrays = spawns_by_zone.values()
for player in all_players: for player in all_players:
var assigned_pos = Vector2i(-1, -1) var assigned_pos = Vector2i(-1, -1)
# Try to get from the current quadrant # Try to get from the current zone (round-robin)
var quadrant_idx = spawn_index % 4 var zone_idx = spawn_index % zone_arrays.size()
var quadrant = quadrants[quadrant_idx] var zone_spawns = zone_arrays[zone_idx]
if quadrant.size() > 0: if zone_spawns.size() > 0:
assigned_pos = quadrant.pop_back() assigned_pos = zone_spawns.pop_back()
else: else:
# Fallback: Try other quadrants if preferred one is empty # Fallback: Try other zones if preferred one is empty
for q in quadrants: for zone_arr in zone_arrays:
if q.size() > 0: if zone_arr.size() > 0:
assigned_pos = q.pop_back() assigned_pos = zone_arr.pop_back()
break break
# Ultimate fallback: Random from anywhere # Ultimate fallback: Random from anywhere
@@ -1091,33 +1166,28 @@ const StaticTektonManager = preload("res://scripts/managers/static_tekton_manage
var static_tekton_manager var static_tekton_manager
func spawn_tekton_npc(): func spawn_tekton_npc():
"""Spawn a Tekton NPC at a random location.""" """Spawn Tektons in corner zones only (avoiding player spawn areas)."""
if not multiplayer.is_server(): return if not multiplayer.is_server(): return
# Find random valid position
var enhanced_gridmap = $EnhancedGridMap var enhanced_gridmap = $EnhancedGridMap
if not enhanced_gridmap: return if not enhanced_gridmap: return
# Spawn 3 Roaming Tektons # Get corner zones for Tekton spawning
var spawned_count = 0 var spawn_zones = _get_spawn_zones(enhanced_gridmap)
var attempts = 0 var tekton_zones = _get_tekton_spawn_zones(spawn_zones)
while spawned_count < 3 and attempts < 50: # Collect valid spawn positions in corner zones
attempts += 1 var valid_positions = []
for zone in tekton_zones:
# Find random valid position for x in range(zone.position.x, zone.position.x + zone.size.x):
var valid_pos = Vector2i(-1, -1) for y in range(zone.position.y, zone.position.y + zone.size.y):
var x = randi() % enhanced_gridmap.columns
var y = randi() % enhanced_gridmap.rows
var cell = Vector3i(x, 0, y) var cell = Vector3i(x, 0, y)
# Check if walkable and no existing Tekton nearby?
if enhanced_gridmap.get_cell_item(cell) == 0: # Walkable floor if enhanced_gridmap.get_cell_item(cell) == 0: # Walkable floor
# Ensure not occupied by static tekton stand (Item 4) # Ensure not occupied by static tekton stand
var item_id = enhanced_gridmap.get_cell_item(Vector3i(x, 1, y)) var item_id = enhanced_gridmap.get_cell_item(Vector3i(x, 1, y))
if item_id == 4: continue # Wall/Stand if item_id == 4: continue # Wall/Stand
# Also check RESERVED positions (if they haven't spawned yet or for safety) # Check RESERVED positions (static tekton stands)
var is_safe = true var is_safe = true
for reserved in reserved_static_positions: for reserved in reserved_static_positions:
if abs(x - reserved.x) <= 1 and abs(y - reserved.y) <= 1: if abs(x - reserved.x) <= 1 and abs(y - reserved.y) <= 1:
@@ -1125,17 +1195,25 @@ func spawn_tekton_npc():
break break
if not is_safe: continue if not is_safe: continue
valid_pos = Vector2i(x, y) valid_positions.append(Vector2i(x, y))
# Generate a consistent ID/Name for sync (add index to ensure uniqueness) # Shuffle and spawn 3 Roaming Tektons
valid_positions.shuffle()
var spawned_count = 0
for pos in valid_positions:
if spawned_count >= 3: break
# Generate a consistent ID/Name for sync
var tekton_id = Time.get_ticks_msec() + spawned_count var tekton_id = Time.get_ticks_msec() + spawned_count
_create_tekton(valid_pos, tekton_id) _create_tekton(pos, tekton_id)
# Only broadcast to clients if there are remote peers connected # Only broadcast to clients if there are remote peers connected
if can_rpc() and multiplayer.get_peers().size() > 0: if can_rpc() and multiplayer.get_peers().size() > 0:
rpc("sync_spawn_tekton", valid_pos, tekton_id) rpc("sync_spawn_tekton", pos, tekton_id)
spawned_count += 1 spawned_count += 1
print("[Main] Spawned Tekton %d at %s" % [spawned_count, valid_pos]) print("[Main] Spawned Tekton %d at %s (Corner Zone)" % [spawned_count, pos])
@rpc("call_remote", "reliable") @rpc("call_remote", "reliable")
func sync_spawn_tekton(pos: Vector2i, tekton_id: int): func sync_spawn_tekton(pos: Vector2i, tekton_id: int):
@@ -2527,6 +2605,110 @@ func _on_settings_back_pressed():
if pause_menu: if pause_menu:
pause_menu.visible = true pause_menu.visible = true
func _on_unstuck_pressed():
"""Teleport the local player to a safe spawn position when stuck."""
if _unstuck_cooldown_remaining > 0.0:
print("[Unstuck] On cooldown: %.0fs remaining" % _unstuck_cooldown_remaining)
return
var enhanced_gridmap = $EnhancedGridMap
if not enhanced_gridmap:
print("[Unstuck] No gridmap found")
return
# Find the local player
var local_player = null
var all_players = get_tree().get_nodes_in_group("Players")
for player in all_players:
if player.is_multiplayer_authority():
local_player = player
break
if not local_player:
print("[Unstuck] No local player found")
return
# Find a safe spawn position using the spawn zone system
var safe_pos = _find_safe_spawn_position(enhanced_gridmap, local_player)
if safe_pos == Vector2i(-1, -1):
print("[Unstuck] Failed to find safe position")
return
# Teleport the player to the safe position
local_player.current_position = safe_pos
local_player.position = local_player.grid_to_world(safe_pos)
local_player.is_player_moving = false
# Sync the new position to all clients
if local_player.has_method("rpc"):
local_player.rpc("sync_position", safe_pos)
print("[Unstuck] Teleported player to safe position: %s" % safe_pos)
# Start cooldown
_unstuck_cooldown_remaining = UNSTUCK_COOLDOWN
var unstuck_btn = get_node_or_null("PauseMenu/Panel/VBox/UnstuckBtn")
if unstuck_btn:
unstuck_btn.disabled = true
# Close the pause menu
var pause_menu = get_node_or_null("PauseMenu")
if pause_menu:
pause_menu.visible = false
func _find_safe_spawn_position(gridmap: Node, player: Node) -> Vector2i:
"""Find a safe spawn position using the existing spawn zone system.
Prioritizes player zones but will fall back to any walkable position."""
# Get spawn zones with perimeter buffer
var spawn_zones = _get_spawn_zones(gridmap)
var player_zones = _get_player_spawn_zones(spawn_zones)
# Collect valid positions from player zones
var valid_positions = []
for zone in player_zones:
for x in range(zone.position.x, zone.position.x + zone.size.x):
for z in range(zone.position.y, zone.position.y + zone.size.y):
var cell = Vector3i(x, 0, z)
if gridmap.get_cell_item(cell) == 0: # Walkable floor
# Check for obstacles on layer 1
var layer1_item = gridmap.get_cell_item(Vector3i(x, 1, z))
if layer1_item == -1 or layer1_item in [7, 8, 9, 10, 11, 12, 13, 14]: # Empty or pickable tiles
# Check if position is not occupied by another player
var occupied = false
var all_players = get_tree().get_nodes_in_group("Players")
for p in all_players:
if p != player and p.current_position == Vector2i(x, z):
occupied = true
break
if not occupied:
valid_positions.append(Vector2i(x, z))
# If we found valid positions, pick a random one
if valid_positions.size() > 0:
valid_positions.shuffle()
return valid_positions[0]
# Fallback: Search the entire grid with buffer for ANY walkable position
for x in range(PERIMETER_BUFFER, gridmap.columns - PERIMETER_BUFFER):
for z in range(PERIMETER_BUFFER, gridmap.rows - PERIMETER_BUFFER):
var cell = Vector3i(x, 0, z)
if gridmap.get_cell_item(cell) == 0: # Walkable
var layer1_item = gridmap.get_cell_item(Vector3i(x, 1, z))
if layer1_item == -1 or layer1_item in [7, 8, 9, 10, 11, 12, 13, 14]:
var occupied = false
var all_players = get_tree().get_nodes_in_group("Players")
for p in all_players:
if p != player and p.current_position == Vector2i(x, z):
occupied = true
break
if not occupied:
return Vector2i(x, z)
# Ultimate fallback: center of the map
return Vector2i(gridmap.columns / 2, gridmap.rows / 2)
func _on_button_size_changed(value: float): func _on_button_size_changed(value: float):
if touch_controls: if touch_controls:
touch_controls.button_size = value touch_controls.button_size = value
+7
View File
@@ -1819,6 +1819,12 @@ layout_mode = 2
theme_override_fonts/font = ExtResource("13_j8jky") theme_override_fonts/font = ExtResource("13_j8jky")
text = "Settings" text = "Settings"
[node name="UnstuckBtn" type="Button" parent="PauseMenu/Panel/VBox" unique_id=345678901]
custom_minimum_size = Vector2(200, 45)
layout_mode = 2
theme_override_fonts/font = ExtResource("13_j8jky")
text = "Unstuck"
[node name="QuitBtn" type="Button" parent="PauseMenu/Panel/VBox" unique_id=1771850243] [node name="QuitBtn" type="Button" parent="PauseMenu/Panel/VBox" unique_id=1771850243]
custom_minimum_size = Vector2(200, 45) custom_minimum_size = Vector2(200, 45)
layout_mode = 2 layout_mode = 2
@@ -2056,5 +2062,6 @@ flat = true
[connection signal="pressed" from="PauseMenu/Panel/VBox/ResumeBtn" to="." method="_on_resume_pressed"] [connection signal="pressed" from="PauseMenu/Panel/VBox/ResumeBtn" to="." method="_on_resume_pressed"]
[connection signal="pressed" from="PauseMenu/Panel/VBox/HowToPlayBtn" to="." method="_on_how_to_play_pressed"] [connection signal="pressed" from="PauseMenu/Panel/VBox/HowToPlayBtn" to="." method="_on_how_to_play_pressed"]
[connection signal="pressed" from="PauseMenu/Panel/VBox/SettingsBtn" to="." method="_on_settings_pressed"] [connection signal="pressed" from="PauseMenu/Panel/VBox/SettingsBtn" to="." method="_on_settings_pressed"]
[connection signal="pressed" from="PauseMenu/Panel/VBox/UnstuckBtn" to="." method="_on_unstuck_pressed"]
[connection signal="pressed" from="PauseMenu/Panel/VBox/QuitBtn" to="." method="_on_quit_match_pressed"] [connection signal="pressed" from="PauseMenu/Panel/VBox/QuitBtn" to="." method="_on_quit_match_pressed"]
[connection signal="pressed" from="HowToPlayPanel/Panel/VBox/BackBtn" to="." method="_on_how_to_play_back_pressed"] [connection signal="pressed" from="HowToPlayPanel/Panel/VBox/BackBtn" to="." method="_on_how_to_play_back_pressed"]
+10
View File
@@ -596,6 +596,16 @@ func sync_loadout(loadout_data: Dictionary) -> void:
# Pass 'self' as character_root — SkinManager will find $Oldpop/$Bob/etc. by name # Pass 'self' as character_root — SkinManager will find $Oldpop/$Bob/etc. by name
SkinManager.apply_loadout(self, _player_loadout) SkinManager.apply_loadout(self, _player_loadout)
# Re-apply outline shader to ensure it's present after skin changes
var active_character: Node3D = null
match _selected_character:
"Bob": active_character = character_bob
"Masbro": active_character = character_masbro
"Gatot": active_character = character_gatot
"Oldpop": active_character = character_oldpop
if active_character:
_apply_outline_recursive(active_character)
func _setup_character() -> void: func _setup_character() -> void:
"""Initialize character based on LobbyManager selection or defaults.""" """Initialize character based on LobbyManager selection or defaults."""
# Bots self-assign characters based on ID in _ready() # Bots self-assign characters based on ID in _ready()
+148 -103
View File
@@ -3,7 +3,7 @@
[ext_resource type="Script" uid="uid://dbcyurv4m7mka" path="res://scripts/tools/skin_shader_generator.gd" id="1_script"] [ext_resource type="Script" uid="uid://dbcyurv4m7mka" path="res://scripts/tools/skin_shader_generator.gd" id="1_script"]
[ext_resource type="Texture2D" uid="uid://c1i00chwkmxtw" path="res://assets/characters/Oldpop_oldpop-tex.png" id="2_ac8h2"] [ext_resource type="Texture2D" uid="uid://c1i00chwkmxtw" path="res://assets/characters/Oldpop_oldpop-tex.png" id="2_ac8h2"]
[node name="SkinShaderGenerator" type="Control"] [node name="SkinShaderGenerator" type="Control" unique_id=1463212218]
layout_mode = 3 layout_mode = 3
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
@@ -12,7 +12,7 @@ grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
script = ExtResource("1_script") script = ExtResource("1_script")
[node name="MainHSplit" type="HSplitContainer" parent="."] [node name="MainHSplit" type="HSplitContainer" parent="." unique_id=1205691437]
layout_mode = 1 layout_mode = 1
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
@@ -26,61 +26,61 @@ grow_vertical = 2
split_offsets = PackedInt32Array(480) split_offsets = PackedInt32Array(480)
split_offset = 480 split_offset = 480
[node name="UVEditorVBox" type="VBoxContainer" parent="MainHSplit"] [node name="UVEditorVBox" type="VBoxContainer" parent="MainHSplit" unique_id=1641474519]
custom_minimum_size = Vector2(480, 0) custom_minimum_size = Vector2(480, 0)
layout_mode = 2 layout_mode = 2
[node name="UVPanel" type="PanelContainer" parent="MainHSplit/UVEditorVBox"] [node name="UVPanel" type="PanelContainer" parent="MainHSplit/UVEditorVBox" unique_id=759141139]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
[node name="UVPreview" type="TextureRect" parent="MainHSplit/UVEditorVBox/UVPanel"] [node name="UVPreview" type="TextureRect" parent="MainHSplit/UVEditorVBox/UVPanel" unique_id=1983575193]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
texture = ExtResource("2_ac8h2") texture = ExtResource("2_ac8h2")
expand_mode = 1 expand_mode = 1
stretch_mode = 5 stretch_mode = 5
[node name="UVOverlay" type="Control" parent="MainHSplit/UVEditorVBox/UVPanel"] [node name="UVOverlay" type="Control" parent="MainHSplit/UVEditorVBox/UVPanel" unique_id=1992150626]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
[node name="UVToolsHBox" type="HBoxContainer" parent="MainHSplit/UVEditorVBox"] [node name="UVToolsHBox" type="HBoxContainer" parent="MainHSplit/UVEditorVBox" unique_id=875713341]
custom_minimum_size = Vector2(0, 28) custom_minimum_size = Vector2(0, 28)
layout_mode = 2 layout_mode = 2
[node name="ShowAllUVs" type="CheckBox" parent="MainHSplit/UVEditorVBox/UVToolsHBox"] [node name="ShowAllUVs" type="CheckBox" parent="MainHSplit/UVEditorVBox/UVToolsHBox" unique_id=924232453]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "Show All UVs" text = "Show All UVs"
[node name="FlipUV" type="CheckBox" parent="MainHSplit/UVEditorVBox/UVToolsHBox"] [node name="FlipUV" type="CheckBox" parent="MainHSplit/UVEditorVBox/UVToolsHBox" unique_id=486835238]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "Flip UV" text = "Flip UV"
[node name="HideRef" type="CheckBox" parent="MainHSplit/UVEditorVBox/UVToolsHBox"] [node name="HideRef" type="CheckBox" parent="MainHSplit/UVEditorVBox/UVToolsHBox" unique_id=486814611]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "Hide Ref" text = "Hide Ref"
[node name="RightHSplit" type="HSplitContainer" parent="MainHSplit"] [node name="RightHSplit" type="HSplitContainer" parent="MainHSplit" unique_id=2005506586]
layout_mode = 2 layout_mode = 2
split_offsets = PackedInt32Array(400) split_offsets = PackedInt32Array(400)
split_offset = 400 split_offset = 400
[node name="PreviewVBox" type="VBoxContainer" parent="MainHSplit/RightHSplit"] [node name="PreviewVBox" type="VBoxContainer" parent="MainHSplit/RightHSplit" unique_id=1034615855]
layout_mode = 2 layout_mode = 2
[node name="ViewportPanel" type="PanelContainer" parent="MainHSplit/RightHSplit/PreviewVBox"] [node name="ViewportPanel" type="PanelContainer" parent="MainHSplit/RightHSplit/PreviewVBox" unique_id=778081482]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
[node name="ViewportContainer" type="SubViewportContainer" parent="MainHSplit/RightHSplit/PreviewVBox/ViewportPanel"] [node name="ViewportContainer" type="SubViewportContainer" parent="MainHSplit/RightHSplit/PreviewVBox/ViewportPanel" unique_id=2067285271]
layout_mode = 2 layout_mode = 2
stretch = true stretch = true
[node name="Viewport3D" type="SubViewport" parent="MainHSplit/RightHSplit/PreviewVBox/ViewportPanel/ViewportContainer"] [node name="Viewport3D" type="SubViewport" parent="MainHSplit/RightHSplit/PreviewVBox/ViewportPanel/ViewportContainer" unique_id=319895518]
unique_name_in_owner = true unique_name_in_owner = true
own_world_3d = true own_world_3d = true
handle_input_locally = false handle_input_locally = false
@@ -88,15 +88,15 @@ msaa_3d = 2
size = Vector2i(400, 646) size = Vector2i(400, 646)
render_target_update_mode = 4 render_target_update_mode = 4
[node name="ZoomHBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/PreviewVBox"] [node name="ZoomHBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/PreviewVBox" unique_id=1494239181]
layout_mode = 2 layout_mode = 2
[node name="ZoomMinus" type="Button" parent="MainHSplit/RightHSplit/PreviewVBox/ZoomHBox"] [node name="ZoomMinus" type="Button" parent="MainHSplit/RightHSplit/PreviewVBox/ZoomHBox" unique_id=1909808732]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = " " text = " "
[node name="ZoomSlider" type="HSlider" parent="MainHSplit/RightHSplit/PreviewVBox/ZoomHBox"] [node name="ZoomSlider" type="HSlider" parent="MainHSplit/RightHSplit/PreviewVBox/ZoomHBox" unique_id=117912774]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
@@ -106,140 +106,150 @@ max_value = 10.0
step = 0.1 step = 0.1
value = 1.8 value = 1.8
[node name="ZoomPlus" type="Button" parent="MainHSplit/RightHSplit/PreviewVBox/ZoomHBox"] [node name="ZoomPlus" type="Button" parent="MainHSplit/RightHSplit/PreviewVBox/ZoomHBox" unique_id=1171788517]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = " + " text = " + "
[node name="InspectedLabel" type="Label" parent="MainHSplit/RightHSplit/PreviewVBox"] [node name="InspectedLabel" type="Label" parent="MainHSplit/RightHSplit/PreviewVBox" unique_id=1759338532]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(1, 1, 0, 1) theme_override_colors/font_color = Color(1, 1, 0, 1)
horizontal_alignment = 1 horizontal_alignment = 1
[node name="TabContainer" type="TabContainer" parent="MainHSplit/RightHSplit"] [node name="TabContainer" type="TabContainer" parent="MainHSplit/RightHSplit" unique_id=542984640]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(360, 0) custom_minimum_size = Vector2(360, 0)
layout_mode = 2 layout_mode = 2
current_tab = 1 current_tab = 1
[node name="Scene Tree" type="ScrollContainer" parent="MainHSplit/RightHSplit/TabContainer"] [node name="Scene Tree" type="ScrollContainer" parent="MainHSplit/RightHSplit/TabContainer" unique_id=899733231]
visible = false visible = false
layout_mode = 2 layout_mode = 2
metadata/_tab_index = 0 metadata/_tab_index = 0
[node name="MeshList" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Scene Tree"] [node name="MeshList" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Scene Tree" unique_id=150906488]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
[node name="Inspector" type="ScrollContainer" parent="MainHSplit/RightHSplit/TabContainer"] [node name="Inspector" type="ScrollContainer" parent="MainHSplit/RightHSplit/TabContainer" unique_id=748873039]
layout_mode = 2 layout_mode = 2
horizontal_scroll_mode = 0 horizontal_scroll_mode = 0
metadata/_tab_index = 1 metadata/_tab_index = 1
[node name="PIPanel" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector"] [node name="PIPanel" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector" unique_id=1353242101]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
theme_override_constants/separation = 12 theme_override_constants/separation = 12
[node name="Margin" type="MarginContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel"] [node name="Margin" type="MarginContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel" unique_id=718046840]
layout_mode = 2 layout_mode = 2
theme_override_constants/margin_left = 6 theme_override_constants/margin_left = 6
theme_override_constants/margin_top = 10 theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 6 theme_override_constants/margin_right = 6
theme_override_constants/margin_bottom = 6 theme_override_constants/margin_bottom = 6
[node name="VBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin"] [node name="VBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin" unique_id=368414230]
layout_mode = 2 layout_mode = 2
theme_override_constants/separation = 12 theme_override_constants/separation = 12
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox" unique_id=1406164062]
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0, 1, 1, 1) theme_override_colors/font_color = Color(0, 1, 1, 1)
text = "▼ Part Properties" text = "▼ Part Properties"
[node name="PIName" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox"] [node name="PIName" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox" unique_id=174857139]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(1, 1, 0, 1) theme_override_colors/font_color = Color(1, 1, 0, 1)
text = "(no part selected)" text = "(no part selected)"
[node name="Grid" type="GridContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox"] [node name="Grid" type="GridContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox" unique_id=68655076]
layout_mode = 2 layout_mode = 2
columns = 2 columns = 2
[node name="Label4" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid"] [node name="Label4" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid" unique_id=455538616]
layout_mode = 2 layout_mode = 2
text = "Ref UV Img:" text = "Ref UV Img:"
[node name="PIRefHBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid"] [node name="PIRefHBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid" unique_id=1269364062]
layout_mode = 2 layout_mode = 2
[node name="PIRefBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid/PIRefHBox"] [node name="PIRefBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid/PIRefHBox" unique_id=1975704724]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
text = "Default" text = "Default"
text_overrun_behavior = 3 text_overrun_behavior = 3
[node name="PIRefClear" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid/PIRefHBox"] [node name="PIRefClear" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid/PIRefHBox" unique_id=1144899648]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "✕" text = "✕"
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid" unique_id=1210824002]
layout_mode = 2 layout_mode = 2
text = "Category:" text = "Category:"
[node name="PICategory" type="OptionButton" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid"] [node name="PICategory" type="OptionButton" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid" unique_id=1287709635]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
selected = 0
item_count = 4
popup/item_0/text = "None / Skin"
popup/item_0/id = 0
popup/item_1/text = "Hat"
popup/item_1/id = 0
popup/item_2/text = "Gloves"
popup/item_2/id = 1
popup/item_3/text = "Cloth"
popup/item_3/id = 2
[node name="Label2" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid"] [node name="Label2" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid" unique_id=667580391]
layout_mode = 2 layout_mode = 2
text = "Fill Color:" text = "Fill Color:"
[node name="PIColor" type="ColorPickerButton" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid"] [node name="PIColor" type="ColorPickerButton" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid" unique_id=1375411324]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(60, 0) custom_minimum_size = Vector2(60, 0)
layout_mode = 2 layout_mode = 2
color = Color(1, 1, 1, 1) color = Color(1, 1, 1, 1)
[node name="Label3" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid"] [node name="Label3" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid" unique_id=269090273]
layout_mode = 2 layout_mode = 2
text = "Sticker Img:" text = "Sticker Img:"
[node name="PIImageHBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid"] [node name="PIImageHBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid" unique_id=982037215]
layout_mode = 2 layout_mode = 2
[node name="PIImageBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid/PIImageHBox"] [node name="PIImageBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid/PIImageHBox" unique_id=710574420]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
text = "None" text = "None"
text_overrun_behavior = 3 text_overrun_behavior = 3
[node name="PIImageClear" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid/PIImageHBox"] [node name="PIImageClear" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/Grid/PIImageHBox" unique_id=579810771]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "✕" text = "✕"
[node name="TransformVBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox"] [node name="TransformVBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox" unique_id=1554876686]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
theme_override_constants/separation = 8 theme_override_constants/separation = 8
[node name="HBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox"] [node name="HBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox" unique_id=759048628]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox" unique_id=1162724878]
layout_mode = 2 layout_mode = 2
text = "Pos X:" text = "Pos X:"
[node name="PIPosX" type="HSlider" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox"] [node name="PIPosX" type="HSlider" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox" unique_id=1858913695]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
@@ -247,14 +257,14 @@ min_value = -1.0
max_value = 2.0 max_value = 2.0
step = 0.01 step = 0.01
[node name="HBox2" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox"] [node name="HBox2" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox" unique_id=1136376502]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox2"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox2" unique_id=171517440]
layout_mode = 2 layout_mode = 2
text = "Pos Y:" text = "Pos Y:"
[node name="PIPosY" type="HSlider" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox2"] [node name="PIPosY" type="HSlider" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox2" unique_id=419957765]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
@@ -262,14 +272,14 @@ min_value = -1.0
max_value = 2.0 max_value = 2.0
step = 0.01 step = 0.01
[node name="HBox3" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox"] [node name="HBox3" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox" unique_id=191899165]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox3"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox3" unique_id=1617628574]
layout_mode = 2 layout_mode = 2
text = "Scale:" text = "Scale:"
[node name="PIScale" type="HSlider" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox3"] [node name="PIScale" type="HSlider" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox3" unique_id=1132682974]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
@@ -278,155 +288,174 @@ max_value = 5.0
step = 0.01 step = 0.01
value = 1.0 value = 1.0
[node name="HBox4" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox"] [node name="HBox4" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox" unique_id=1943002195]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox4"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox4" unique_id=1211717333]
layout_mode = 2 layout_mode = 2
text = "Rotate:" text = "Rotate:"
[node name="PIRot" type="HSlider" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox4"] [node name="PIRot" type="HSlider" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/TransformVBox/HBox4" unique_id=2006377715]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
min_value = -180.0 min_value = -180.0
max_value = 180.0 max_value = 180.0
[node name="HSeparator" type="HSeparator" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox"] [node name="HSeparator" type="HSeparator" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox" unique_id=208929617]
layout_mode = 2 layout_mode = 2
[node name="MaskSection" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox"] [node name="MaskSection" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox" unique_id=1009501329]
layout_mode = 2 layout_mode = 2
theme_override_constants/separation = 8 theme_override_constants/separation = 8
[node name="HBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection"] [node name="HBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection" unique_id=2142764592]
layout_mode = 2 layout_mode = 2
[node name="Label2" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection/HBox"] [node name="Label2" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection/HBox" unique_id=1750314177]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
theme_override_colors/font_color = Color(1, 1, 0, 1) theme_override_colors/font_color = Color(1, 1, 0, 1)
text = "UV Masks (isolate regions):" text = "UV Masks (isolate regions):"
[node name="AddMaskBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection/HBox"] [node name="AddMaskBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection/HBox" unique_id=841819147]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "+ Add" text = "+ Add"
[node name="MaskList" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection"] [node name="MaskList" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection" unique_id=396917805]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
[node name="DrawingToolbar" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection"] [node name="DrawingToolbar" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection" unique_id=1024349553]
unique_name_in_owner = true unique_name_in_owner = true
visible = false visible = false
layout_mode = 2 layout_mode = 2
[node name="DrawStatus" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection/DrawingToolbar"] [node name="DrawStatus" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection/DrawingToolbar" unique_id=1815443043]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
text = "Editing Mask A..." text = "Editing Mask A..."
[node name="FinishBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection/DrawingToolbar"] [node name="FinishBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection/DrawingToolbar" unique_id=1820527087]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "✓ Done" text = "✓ Done"
[node name="ClearBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection/DrawingToolbar"] [node name="ClearBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Inspector/PIPanel/Margin/VBox/MaskSection/DrawingToolbar" unique_id=1393501427]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
text = "✕ Clear" text = "✕ Clear"
[node name="Settings" type="ScrollContainer" parent="MainHSplit/RightHSplit/TabContainer"] [node name="Settings" type="ScrollContainer" parent="MainHSplit/RightHSplit/TabContainer" unique_id=602338965]
visible = false visible = false
layout_mode = 2 layout_mode = 2
horizontal_scroll_mode = 0 horizontal_scroll_mode = 0
metadata/_tab_index = 2 metadata/_tab_index = 2
[node name="VBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings"] [node name="VBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings" unique_id=45543798]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
theme_override_constants/separation = 10 theme_override_constants/separation = 10
[node name="Margin" type="MarginContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox"] [node name="Margin" type="MarginContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox" unique_id=556399693]
layout_mode = 2 layout_mode = 2
theme_override_constants/margin_left = 6 theme_override_constants/margin_left = 6
theme_override_constants/margin_top = 10 theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 6 theme_override_constants/margin_right = 6
theme_override_constants/margin_bottom = 6 theme_override_constants/margin_bottom = 6
[node name="VBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin"] [node name="VBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin" unique_id=1455447429]
layout_mode = 2 layout_mode = 2
theme_override_constants/separation = 12 theme_override_constants/separation = 12
[node name="CharBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox"] [node name="CharBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox" unique_id=668836559]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/CharBox"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/CharBox" unique_id=1834665707]
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0.5, 1, 1, 1) theme_override_colors/font_color = Color(0.5, 1, 1, 1)
text = "Active Character:" text = "Active Character:"
[node name="CharacterOpt" type="OptionButton" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/CharBox"] [node name="CharacterOpt" type="OptionButton" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/CharBox" unique_id=1040984097]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
selected = 0
item_count = 4
popup/item_0/text = "Oldpop"
popup/item_0/id = 0
popup/item_1/text = "Masbro"
popup/item_1/id = 1
popup/item_2/text = "Bob"
popup/item_2/id = 2
popup/item_3/text = "Gatot"
popup/item_3/id = 3
[node name="WireBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox"] [node name="WireBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox" unique_id=1106179936]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/WireBox"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/WireBox" unique_id=1522414156]
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0.5, 1, 1, 1) theme_override_colors/font_color = Color(0.5, 1, 1, 1)
text = "Wireframe Opacity:" text = "Wireframe Opacity:"
[node name="WireOpacity" type="HSlider" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/WireBox"] [node name="WireOpacity" type="HSlider" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/WireBox" unique_id=2084209816]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
max_value = 1.0 max_value = 1.0
step = 0.05 step = 0.05
value = 0.07 value = 0.05
[node name="HBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/WireBox"] [node name="HBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/WireBox" unique_id=552477298]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/WireBox/HBox"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/WireBox/HBox" unique_id=1111725652]
layout_mode = 2 layout_mode = 2
text = "Color:" text = "Color:"
[node name="WireColor" type="OptionButton" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/WireBox/HBox"] [node name="WireColor" type="OptionButton" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/WireBox/HBox" unique_id=1491986963]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
selected = 0 selected = 0
item_count = 4
popup/item_0/text = "Black"
popup/item_0/id = 0
popup/item_1/text = "White"
popup/item_1/id = 1
popup/item_2/text = "Yellow"
popup/item_2/id = 2
popup/item_3/text = "Green"
popup/item_3/id = 3
[node name="HSeparator" type="HSeparator" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox"] [node name="HSeparator" type="HSeparator" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox" unique_id=426048616]
layout_mode = 2 layout_mode = 2
[node name="PresetBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox"] [node name="PresetBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox" unique_id=12323314]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/PresetBox"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/PresetBox" unique_id=744742837]
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0.5, 1, 1, 1) theme_override_colors/font_color = Color(0.5, 1, 1, 1)
text = "Session Presets:" text = "Session Presets:"
[node name="HBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/PresetBox"] [node name="HBox" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/PresetBox" unique_id=327547742]
layout_mode = 2 layout_mode = 2
[node name="SavePresetBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/PresetBox/HBox"] [node name="SavePresetBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/PresetBox/HBox" unique_id=1945639309]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
text = "💾 Save JSON" text = "💾 Save JSON"
[node name="LoadPresetBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/PresetBox/HBox"] [node name="LoadPresetBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox/PresetBox/HBox" unique_id=1206320620]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
text = "📂 Load JSON" text = "📂 Load JSON"
[node name="Label2" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox"] [node name="Label2" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Settings/VBox/Margin/VBox" unique_id=1382080014]
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0.5, 0.5, 0.5, 1) theme_override_colors/font_color = Color(0.5, 0.5, 0.5, 1)
theme_override_font_sizes/font_size = 12 theme_override_font_sizes/font_size = 12
@@ -437,33 +466,33 @@ text = "Workflow:
4. Hit GENERATE to bake." 4. Hit GENERATE to bake."
autowrap_mode = 3 autowrap_mode = 3
[node name="Export" type="ScrollContainer" parent="MainHSplit/RightHSplit/TabContainer"] [node name="Export" type="ScrollContainer" parent="MainHSplit/RightHSplit/TabContainer" unique_id=19499909]
visible = false visible = false
layout_mode = 2 layout_mode = 2
horizontal_scroll_mode = 0 horizontal_scroll_mode = 0
metadata/_tab_index = 3 metadata/_tab_index = 3
[node name="ExportVBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Export"] [node name="ExportVBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Export" unique_id=793639475]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
[node name="Margin" type="MarginContainer" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox"] [node name="Margin" type="MarginContainer" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox" unique_id=1909064484]
layout_mode = 2 layout_mode = 2
theme_override_constants/margin_left = 10 theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10 theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10 theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10 theme_override_constants/margin_bottom = 10
[node name="VBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin"] [node name="VBox" type="VBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin" unique_id=1822993310]
layout_mode = 2 layout_mode = 2
theme_override_constants/separation = 12 theme_override_constants/separation = 12
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox" unique_id=1218142446]
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0, 1, 0.5, 1) theme_override_colors/font_color = Color(0, 1, 0.5, 1)
text = "▼ Export Assets" text = "▼ Export Assets"
[node name="PrefixLabel" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox"] [node name="PrefixLabel" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox" unique_id=1416817408]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
theme_override_colors/font_color = Color(0, 1, 0.5, 1) theme_override_colors/font_color = Color(0, 1, 0.5, 1)
@@ -471,50 +500,66 @@ theme_override_font_sizes/font_size = 13
text = "oldpop_skin_" text = "oldpop_skin_"
horizontal_alignment = 2 horizontal_alignment = 2
[node name="HBoxContainer" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox"] [node name="HBoxContainer" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox" unique_id=2142158910]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer" unique_id=1163691924]
layout_mode = 2 layout_mode = 2
text = "Name:" text = "Name:"
[node name="ExportName" type="LineEdit" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer"] [node name="ExportName" type="LineEdit" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer" unique_id=712721669]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
placeholder_text = "custom_name" placeholder_text = "custom_name"
[node name="HBoxContainer2" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox"] [node name="HBoxContainer2" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox" unique_id=814721742]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer2"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer2" unique_id=1413953937]
layout_mode = 2 layout_mode = 2
text = "Target Folder:" text = "Target Folder:"
[node name="ExportFolder" type="OptionButton" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer2"] [node name="ExportFolder" type="OptionButton" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer2" unique_id=1025584347]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
selected = 3
item_count = 4
popup/item_0/text = "Clothing"
popup/item_0/id = 0
popup/item_1/text = "Gloves"
popup/item_1/id = 1
popup/item_2/text = "Hat"
popup/item_2/id = 2
popup/item_3/text = "General / Tex"
popup/item_3/id = 3
[node name="HBoxContainer3" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox"] [node name="HBoxContainer3" type="HBoxContainer" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox" unique_id=839646285]
layout_mode = 2 layout_mode = 2
[node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer3"] [node name="Label" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer3" unique_id=178279817]
layout_mode = 2 layout_mode = 2
text = "Mode:" text = "Mode:"
[node name="ExportMode" type="OptionButton" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer3"] [node name="ExportMode" type="OptionButton" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox/HBoxContainer3" unique_id=1343098213]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
selected = 0
item_count = 2
popup/item_0/text = "Texture Only (.png)"
popup/item_0/id = 0
popup/item_1/text = "Texture + Material"
popup/item_1/id = 1
[node name="GenerateBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox"] [node name="GenerateBtn" type="Button" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox" unique_id=430835639]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(0, 44) custom_minimum_size = Vector2(0, 44)
layout_mode = 2 layout_mode = 2
text = "🚀 GENERATE & EXPORT" text = "🚀 GENERATE & EXPORT"
[node name="StatusLabel" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox"] [node name="StatusLabel" type="Label" parent="MainHSplit/RightHSplit/TabContainer/Export/ExportVBox/Margin/VBox" unique_id=1899608283]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
horizontal_alignment = 1 horizontal_alignment = 1
+25 -1
View File
@@ -242,7 +242,18 @@ func _apply_skin_data(character_root: Node3D, skin: Dictionary) -> void:
if mode == "overlay": if mode == "overlay":
mn.material_overlay = mat mn.material_overlay = mat
else: else:
mn.set_surface_override_material(0, mat) # PRESERVE OUTLINE SHADER: Check if existing material has a next_pass (outline shader)
var existing_mat = mn.get_surface_override_material(0)
var preserved_next_pass = null
if existing_mat and existing_mat.next_pass:
preserved_next_pass = existing_mat.next_pass
# Apply the skin material
var skin_mat = mat.duplicate() if mat else null
if skin_mat and preserved_next_pass:
skin_mat.next_pass = preserved_next_pass
mn.set_surface_override_material(0, skin_mat)
func _clear_slot(char_node: Node3D, slot: Dictionary) -> void: func _clear_slot(char_node: Node3D, slot: Dictionary) -> void:
@@ -253,6 +264,19 @@ func _clear_slot(char_node: Node3D, slot: Dictionary) -> void:
if not mn: return if not mn: return
if mode == "overlay": if mode == "overlay":
mn.material_overlay = null mn.material_overlay = null
else:
# PRESERVE OUTLINE SHADER: When clearing, check if we need to restore outline
var existing_mat = mn.get_surface_override_material(0)
if existing_mat and existing_mat.next_pass:
# Has outline - restore base material with outline preserved
var base_mat = mn.get_active_material(0)
if base_mat:
var restored_mat = base_mat.duplicate()
restored_mat.next_pass = existing_mat.next_pass
mn.set_surface_override_material(0, restored_mat)
else:
# No base material, just clear but this shouldn't happen normally
mn.set_surface_override_material(0, null)
else: else:
mn.set_surface_override_material(0, null) mn.set_surface_override_material(0, null)
+30 -30
View File
@@ -6,6 +6,7 @@ extends Node
const STAND_SCENE_PATH = "res://scenes/static_tekton_stand.tscn" const STAND_SCENE_PATH = "res://scenes/static_tekton_stand.tscn"
const TEKTON_SCENE_PATH = "res://scenes/tekton.tscn" const TEKTON_SCENE_PATH = "res://scenes/tekton.tscn"
const STATIC_CONTROLLER_SCRIPT = "res://scripts/static_tekton_controller.gd" const STATIC_CONTROLLER_SCRIPT = "res://scripts/static_tekton_controller.gd"
const PERIMETER_BUFFER = 1 # 1-tile safe zone on all sides (matches main.gd)
# Zone Definitions based on CameraContextManager logic # Zone Definitions based on CameraContextManager logic
# 9 Zones in a 3x3 grid (approximate for 14x14 map) # 9 Zones in a 3x3 grid (approximate for 14x14 map)
@@ -16,7 +17,8 @@ const STATIC_CONTROLLER_SCRIPT = "res://scripts/static_tekton_controller.gd"
func calculate_spawn_points(_count: int, gridmap: Node) -> Array[Vector2i]: func calculate_spawn_points(_count: int, gridmap: Node) -> Array[Vector2i]:
""" """
Calculates the 5 fixed potential spawn positions for static tektons. Calculates the 5 fixed potential spawn positions for static tektons.
Corners: (1,1), (W-2,1), (1,H-2), (W-2,H-2) Now respects 1-tile perimeter buffer to prevent edge spawning.
Corners: (2,2), (W-3,2), (2,H-3), (W-3,H-3)
Center: (W/2, H/2) Center: (W/2, H/2)
Returns exactly 5 spots if possible. Returns exactly 5 spots if possible.
""" """
@@ -25,14 +27,14 @@ func calculate_spawn_points(_count: int, gridmap: Node) -> Array[Vector2i]:
var width = gridmap.columns var width = gridmap.columns
var height = gridmap.rows var height = gridmap.rows
print("[StaticTektonManager] Calculating static tekton positions (Fixed 5-Spots)...") print("[StaticTektonManager] Calculating static tekton positions (Fixed 5-Spots with Buffer)...")
# Fixed Spots # Fixed Spots with buffer (at least 2 tiles from edge for 3x3 stand + 1 tile buffer)
var spots: Array[Vector2i] = [ var spots: Array[Vector2i] = [
Vector2i(1, 1), # Top-Left Vector2i(PERIMETER_BUFFER + 1, PERIMETER_BUFFER + 1), # Top-Left
Vector2i(width - 2, 1), # Top-Right Vector2i(width - PERIMETER_BUFFER - 2, PERIMETER_BUFFER + 1), # Top-Right
Vector2i(1, height - 2), # Bottom-Left Vector2i(PERIMETER_BUFFER + 1, height - PERIMETER_BUFFER - 2), # Bottom-Left
Vector2i(width - 2, height - 2), # Bottom-Right Vector2i(width - PERIMETER_BUFFER - 2, height - PERIMETER_BUFFER - 2), # Bottom-Right
Vector2i(width / 2, height / 2) # Center Vector2i(width / 2, height / 2) # Center
] ]
@@ -52,18 +54,17 @@ func _pick_spot_in_zone_biased(zone: Rect2i, gridmap: Node, type: int) -> Vector
# ideal target relative to map # ideal target relative to map
var target = Vector2i.ZERO var target = Vector2i.ZERO
match type: match type:
0: target = Vector2i(0, 0) 0: target = Vector2i(PERIMETER_BUFFER + 1, PERIMETER_BUFFER + 1)
1: target = Vector2i(gridmap.columns, 0) 1: target = Vector2i(gridmap.columns - PERIMETER_BUFFER - 2, PERIMETER_BUFFER + 1)
2: target = Vector2i(0, gridmap.rows) 2: target = Vector2i(PERIMETER_BUFFER + 1, gridmap.rows - PERIMETER_BUFFER - 2)
3: target = Vector2i(gridmap.columns, gridmap.rows) 3: target = Vector2i(gridmap.columns - PERIMETER_BUFFER - 2, gridmap.rows - PERIMETER_BUFFER - 2)
4: target = Vector2i(gridmap.columns / 2, gridmap.rows / 2) 4: target = Vector2i(gridmap.columns / 2, gridmap.rows / 2)
# Clamp target to be inside valid area (taking 3x3 margin into account) # Clamp target to be inside valid area (respecting perimeter buffer + 3x3 margin)
# Center of 3x3 must be at least 1 tile from edge var min_x = max(PERIMETER_BUFFER + 1, zone.position.x + 1)
var min_x = max(1, zone.position.x + 1) var max_x = min(gridmap.columns - PERIMETER_BUFFER - 2, zone.position.x + zone.size.x - 2)
var max_x = min(gridmap.columns - 2, zone.position.x + zone.size.x - 2) var min_y = max(PERIMETER_BUFFER + 1, zone.position.y + 1)
var min_y = max(1, zone.position.y + 1) var max_y = min(gridmap.rows - PERIMETER_BUFFER - 2, zone.position.y + zone.size.y - 2)
var max_y = min(gridmap.rows - 2, zone.position.y + zone.size.y - 2)
if min_x > max_x or min_y > max_y: if min_x > max_x or min_y > max_y:
return Vector2i(-1, -1) return Vector2i(-1, -1)
@@ -111,33 +112,32 @@ func _pick_spot_in_zone(zone: Rect2i, gridmap: Node, zone_idx: int = -1) -> Vect
""" """
Find a valid 3x3 spot in the zone. Find a valid 3x3 spot in the zone.
The returned position is the CENTER of the 3x3 area. The returned position is the CENTER of the 3x3 area.
If zone_idx is a corner (0, 2, 6, 8), we snap to the absolute map corner. If zone_idx is a corner (0, 2, 6, 8), we snap to corner position with buffer.
""" """
# CORNER SNAPPING: If this is a corner zone, force it to the extreme corner # CORNER SNAPPING: If this is a corner zone, force it to the corner with buffer
# to ensure the 3x3 Stand fills the corner completely (no 1-tile gaps). # to ensure the 3x3 Stand stays away from edges (no 1-tile gaps).
if zone_idx == 0: # Top-Left if zone_idx == 0: # Top-Left
var center = Vector2i(1, 1) var center = Vector2i(PERIMETER_BUFFER + 1, PERIMETER_BUFFER + 1)
if _is_valid_3x3(center, gridmap): return center if _is_valid_3x3(center, gridmap): return center
elif zone_idx == 2: # Top-Right elif zone_idx == 2: # Top-Right
var center = Vector2i(gridmap.columns - 2, 1) var center = Vector2i(gridmap.columns - PERIMETER_BUFFER - 2, PERIMETER_BUFFER + 1)
if _is_valid_3x3(center, gridmap): return center if _is_valid_3x3(center, gridmap): return center
elif zone_idx == 6: # Bottom-Left elif zone_idx == 6: # Bottom-Left
var center = Vector2i(1, gridmap.rows - 2) var center = Vector2i(PERIMETER_BUFFER + 1, gridmap.rows - PERIMETER_BUFFER - 2)
if _is_valid_3x3(center, gridmap): return center if _is_valid_3x3(center, gridmap): return center
elif zone_idx == 8: # Bottom-Right elif zone_idx == 8: # Bottom-Right
var center = Vector2i(gridmap.columns - 2, gridmap.rows - 2) var center = Vector2i(gridmap.columns - PERIMETER_BUFFER - 2, gridmap.rows - PERIMETER_BUFFER - 2)
if _is_valid_3x3(center, gridmap): return center if _is_valid_3x3(center, gridmap): return center
# Fallback/Random logic for non-corner zones or if preferred corner was invalid # Fallback/Random logic for non-corner zones or if preferred corner was invalid
var attempts = 0 var attempts = 0
while attempts < 30: while attempts < 30:
attempts += 1 attempts += 1
# Ensure center is at least 1 tile away from edges of the map to fit 3x3 # Ensure center respects perimeter buffer (1 tile from edge + 1 tile for 3x3 center = 2 tiles)
# zone.position might be 0,0. var min_x = max(PERIMETER_BUFFER + 1, zone.position.x + 1)
var min_x = max(1, zone.position.x + 1) var max_x = min(gridmap.columns - PERIMETER_BUFFER - 2, zone.position.x + zone.size.x - 2)
var max_x = min(gridmap.columns - 2, zone.position.x + zone.size.x - 2) var min_y = max(PERIMETER_BUFFER + 1, zone.position.y + 1)
var min_y = max(1, zone.position.y + 1) var max_y = min(gridmap.rows - PERIMETER_BUFFER - 2, zone.position.y + zone.size.y - 2)
var max_y = min(gridmap.rows - 2, zone.position.y + zone.size.y - 2)
if min_x > max_x or min_y > max_y: if min_x > max_x or min_y > max_y:
break # Zone too small break # Zone too small
+17 -4
View File
@@ -407,6 +407,15 @@ func spawn_tiles_around(count: int = 4):
"""Spawns a mix of normal and special tiles in a radius.""" """Spawns a mix of normal and special tiles in a radius."""
if not enhanced_gridmap: return if not enhanced_gridmap: return
var radius = 2
var rng = RandomNumberGenerator.new()
rng.randomize()
# FIX 1: Make tekton look/rotate toward a random spawning direction
if not is_carried and not is_thrown:
var random_angle = rng.randf_range(0, TAU)
rotation.y = random_angle
# Play throw animation # Play throw animation
if not is_carried and not is_thrown: if not is_carried and not is_thrown:
play_animation("tekton_throw_tile") play_animation("tekton_throw_tile")
@@ -416,12 +425,11 @@ func spawn_tiles_around(count: int = 4):
play_animation("tekton_idle") play_animation("tekton_idle")
) )
var radius = 2
var rng = RandomNumberGenerator.new()
rng.randomize()
print("[Tekton] Spawning %d tiles around %s" % [count, current_position]) print("[Tekton] Spawning %d tiles around %s" % [count, current_position])
# FIX 3: Delay tile spawning to match throw animation timing (feels better)
await get_tree().create_timer(0.4).timeout
var spawned = 0 var spawned = 0
var attempts = 0 var attempts = 0
while spawned < count and attempts < 25: while spawned < count and attempts < 25:
@@ -449,6 +457,11 @@ func spawn_tiles_around(count: int = 4):
if floor_0_item in [4, -1]: if floor_0_item in [4, -1]:
continue continue
# FIX 2: Check if layer 1 already has tiles (purple powerups or any tile) to prevent blocking
var floor_1_item = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 1, pos.y))
if floor_1_item != -1:
continue # Skip if there's already a tile on layer 1
# 50% chance to spawn something # 50% chance to spawn something
if rng.randf() > 0.5: continue if rng.randf() > 0.5: continue