forked from xiangwang25/whale-town-front-v2
fix: improve personal space layout editing
This commit is contained in:
@@ -23,7 +23,8 @@ const DECOR_TEXTURE_FILTER: int = CanvasItem.TEXTURE_FILTER_LINEAR
|
|||||||
const DECOR_COLLISION_LAYER: int = 1
|
const DECOR_COLLISION_LAYER: int = 1
|
||||||
const DECOR_COLLISION_MASK: int = 1
|
const DECOR_COLLISION_MASK: int = 1
|
||||||
const ROOM_GRID_SIZE: float = 16.0
|
const ROOM_GRID_SIZE: float = 16.0
|
||||||
const ROOM_PLACE_BOUNDS: Rect2 = Rect2(-426.0, -186.0, 852.0, 398.0)
|
const FLOOR_PLACE_BOUNDS: Rect2 = Rect2(-426.0, -186.0, 852.0, 398.0)
|
||||||
|
const WALL_PLACE_BOUNDS: Rect2 = Rect2(-378.0, -258.0, 756.0, 72.0)
|
||||||
|
|
||||||
@onready var player: PlayerController = $YSortWorld/Characters/Players/Player
|
@onready var player: PlayerController = $YSortWorld/Characters/Players/Player
|
||||||
@onready var playerCamera: Camera2D = $YSortWorld/Characters/Players/Player/Camera2D
|
@onready var playerCamera: Camera2D = $YSortWorld/Characters/Players/Player/Camera2D
|
||||||
@@ -32,6 +33,7 @@ const ROOM_PLACE_BOUNDS: Rect2 = Rect2(-426.0, -186.0, 852.0, 398.0)
|
|||||||
@onready var staticCollision: Node2D = $StaticCollision
|
@onready var staticCollision: Node2D = $StaticCollision
|
||||||
|
|
||||||
var _decorLayer: Node2D
|
var _decorLayer: Node2D
|
||||||
|
var _gridOverlay: Node2D
|
||||||
var _inventoryRequest: HTTPRequest
|
var _inventoryRequest: HTTPRequest
|
||||||
var _inventoryPanel: PanelContainer
|
var _inventoryPanel: PanelContainer
|
||||||
var _inventoryGrid: GridContainer
|
var _inventoryGrid: GridContainer
|
||||||
@@ -43,6 +45,7 @@ var _layoutStatusLabel: Label
|
|||||||
var _escHintLabel: Label
|
var _escHintLabel: Label
|
||||||
var _editButton: Button
|
var _editButton: Button
|
||||||
var _backpackButton: Button
|
var _backpackButton: Button
|
||||||
|
var _gridButton: Button
|
||||||
var _undoButton: Button
|
var _undoButton: Button
|
||||||
var _redoButton: Button
|
var _redoButton: Button
|
||||||
var _rotateButton: Button
|
var _rotateButton: Button
|
||||||
@@ -67,6 +70,7 @@ var _visitorMode: bool = false
|
|||||||
var _visitorOwnerId: String = ""
|
var _visitorOwnerId: String = ""
|
||||||
var _visitorOwnerName: String = ""
|
var _visitorOwnerName: String = ""
|
||||||
var _editMode: bool = false
|
var _editMode: bool = false
|
||||||
|
var _showPlacementGrid: bool = false
|
||||||
var _undoHistory: Array[Dictionary] = []
|
var _undoHistory: Array[Dictionary] = []
|
||||||
var _redoHistory: Array[Dictionary] = []
|
var _redoHistory: Array[Dictionary] = []
|
||||||
var _dirtyDecorIds: Dictionary = {}
|
var _dirtyDecorIds: Dictionary = {}
|
||||||
@@ -191,6 +195,41 @@ func _build_decor_layer() -> void:
|
|||||||
_decorLayer.y_sort_enabled = true
|
_decorLayer.y_sort_enabled = true
|
||||||
ySortWorld.add_child(_decorLayer)
|
ySortWorld.add_child(_decorLayer)
|
||||||
ySortWorld.move_child(_decorLayer, 0)
|
ySortWorld.move_child(_decorLayer, 0)
|
||||||
|
_build_placement_grid()
|
||||||
|
|
||||||
|
func _build_placement_grid() -> void:
|
||||||
|
_gridOverlay = Node2D.new()
|
||||||
|
_gridOverlay.name = "RoomPlacementGrid"
|
||||||
|
_gridOverlay.z_index = -30
|
||||||
|
_gridOverlay.visible = false
|
||||||
|
ySortWorld.add_child(_gridOverlay)
|
||||||
|
ySortWorld.move_child(_gridOverlay, 0)
|
||||||
|
_add_grid_zone(FLOOR_PLACE_BOUNDS, Color(0.050, 0.560, 0.900, 0.20), Color(0.050, 0.560, 0.900, 0.62))
|
||||||
|
_add_grid_zone(WALL_PLACE_BOUNDS, Color(0.620, 0.330, 0.860, 0.20), Color(0.620, 0.330, 0.860, 0.62))
|
||||||
|
|
||||||
|
func _add_grid_zone(bounds: Rect2, line_color: Color, edge_color: Color) -> void:
|
||||||
|
if _gridOverlay == null:
|
||||||
|
return
|
||||||
|
var x: float = ceilf(bounds.position.x / ROOM_GRID_SIZE) * ROOM_GRID_SIZE
|
||||||
|
while x < bounds.end.x:
|
||||||
|
_add_grid_line(Vector2(x, bounds.position.y), Vector2(x, bounds.end.y), line_color, 1.0)
|
||||||
|
x += ROOM_GRID_SIZE
|
||||||
|
var y: float = ceilf(bounds.position.y / ROOM_GRID_SIZE) * ROOM_GRID_SIZE
|
||||||
|
while y < bounds.end.y:
|
||||||
|
_add_grid_line(Vector2(bounds.position.x, y), Vector2(bounds.end.x, y), line_color, 1.0)
|
||||||
|
y += ROOM_GRID_SIZE
|
||||||
|
_add_grid_line(bounds.position, Vector2(bounds.end.x, bounds.position.y), edge_color, 2.0)
|
||||||
|
_add_grid_line(Vector2(bounds.end.x, bounds.position.y), bounds.end, edge_color, 2.0)
|
||||||
|
_add_grid_line(bounds.end, Vector2(bounds.position.x, bounds.end.y), edge_color, 2.0)
|
||||||
|
_add_grid_line(Vector2(bounds.position.x, bounds.end.y), bounds.position, edge_color, 2.0)
|
||||||
|
|
||||||
|
func _add_grid_line(from: Vector2, to: Vector2, color: Color, width: float) -> void:
|
||||||
|
var line := Line2D.new()
|
||||||
|
line.points = PackedVector2Array([from, to])
|
||||||
|
line.default_color = color
|
||||||
|
line.width = width
|
||||||
|
line.antialiased = true
|
||||||
|
_gridOverlay.add_child(line)
|
||||||
|
|
||||||
func _build_room_decor_requests() -> void:
|
func _build_room_decor_requests() -> void:
|
||||||
_inventoryRequest = HTTPRequest.new()
|
_inventoryRequest = HTTPRequest.new()
|
||||||
@@ -355,6 +394,7 @@ func _build_room_toolbar() -> void:
|
|||||||
|
|
||||||
_editButton = _create_toolbar_button("编辑布局", func() -> void: _set_edit_mode(true))
|
_editButton = _create_toolbar_button("编辑布局", func() -> void: _set_edit_mode(true))
|
||||||
_backpackButton = _create_toolbar_button("背包", _toggle_inventory_panel)
|
_backpackButton = _create_toolbar_button("背包", _toggle_inventory_panel)
|
||||||
|
_gridButton = _create_toolbar_button("网格:关", _toggle_placement_grid)
|
||||||
_undoButton = _create_toolbar_button("撤销", _undo_last_change)
|
_undoButton = _create_toolbar_button("撤销", _undo_last_change)
|
||||||
_redoButton = _create_toolbar_button("重做", _redo_last_change)
|
_redoButton = _create_toolbar_button("重做", _redo_last_change)
|
||||||
_rotateButton = _create_toolbar_button("旋转", _rotate_selected_decor)
|
_rotateButton = _create_toolbar_button("旋转", _rotate_selected_decor)
|
||||||
@@ -364,6 +404,7 @@ func _build_room_toolbar() -> void:
|
|||||||
_finishEditButton = _create_toolbar_button("完成编辑", _request_finish_edit)
|
_finishEditButton = _create_toolbar_button("完成编辑", _request_finish_edit)
|
||||||
row.add_child(_editButton)
|
row.add_child(_editButton)
|
||||||
row.add_child(_backpackButton)
|
row.add_child(_backpackButton)
|
||||||
|
row.add_child(_gridButton)
|
||||||
row.add_child(_undoButton)
|
row.add_child(_undoButton)
|
||||||
row.add_child(_redoButton)
|
row.add_child(_redoButton)
|
||||||
row.add_child(_rotateButton)
|
row.add_child(_rotateButton)
|
||||||
@@ -770,7 +811,7 @@ func _finish_decor_drag() -> void:
|
|||||||
var decorId := str(_draggedDecor.get_meta("decor_id", ""))
|
var decorId := str(_draggedDecor.get_meta("decor_id", ""))
|
||||||
if _decorItems.has(decorId):
|
if _decorItems.has(decorId):
|
||||||
var item: Dictionary = _decorItems[decorId]
|
var item: Dictionary = _decorItems[decorId]
|
||||||
var snapped_position := _snap_to_room_grid(_draggedDecor.global_position)
|
var snapped_position := _snap_to_room_grid(_draggedDecor.global_position, item)
|
||||||
_draggedDecor.global_position = snapped_position
|
_draggedDecor.global_position = snapped_position
|
||||||
item["position_x"] = snapped_position.x
|
item["position_x"] = snapped_position.x
|
||||||
item["position_y"] = snapped_position.y
|
item["position_y"] = snapped_position.y
|
||||||
@@ -793,10 +834,10 @@ func _finish_decor_drag() -> void:
|
|||||||
|
|
||||||
func _update_dragged_decor_position() -> void:
|
func _update_dragged_decor_position() -> void:
|
||||||
if _draggedDecor != null:
|
if _draggedDecor != null:
|
||||||
_draggedDecor.global_position = _constrain_room_position(get_global_mouse_position() + _dragOffset)
|
|
||||||
var decorId := str(_draggedDecor.get_meta("decor_id", ""))
|
var decorId := str(_draggedDecor.get_meta("decor_id", ""))
|
||||||
if not decorId.is_empty() and _decorItems.has(decorId):
|
if not decorId.is_empty() and _decorItems.has(decorId):
|
||||||
var item: Dictionary = _decorItems[decorId]
|
var item: Dictionary = _decorItems[decorId]
|
||||||
|
_draggedDecor.global_position = _constrain_room_position(get_global_mouse_position() + _dragOffset, item)
|
||||||
item["position_x"] = _draggedDecor.global_position.x
|
item["position_x"] = _draggedDecor.global_position.x
|
||||||
item["position_y"] = _draggedDecor.global_position.y
|
item["position_y"] = _draggedDecor.global_position.y
|
||||||
_sync_decor_collision(decorId, item)
|
_sync_decor_collision(decorId, item)
|
||||||
@@ -889,11 +930,12 @@ func _on_decor_save_succeeded(savedItem: Dictionary) -> void:
|
|||||||
if decorId.is_empty() or not _saveAwaitingIds.has(decorId):
|
if decorId.is_empty() or not _saveAwaitingIds.has(decorId):
|
||||||
return
|
return
|
||||||
_serverDecorItems[decorId] = item.duplicate(true)
|
_serverDecorItems[decorId] = item.duplicate(true)
|
||||||
|
_decorItems[decorId] = item.duplicate(true)
|
||||||
_saveAwaitingIds.erase(decorId)
|
_saveAwaitingIds.erase(decorId)
|
||||||
if _decorItems.has(decorId) and _placement_signature(_decorItems[decorId]) == _placement_signature(item):
|
|
||||||
_dirtyDecorIds.erase(decorId)
|
_dirtyDecorIds.erase(decorId)
|
||||||
else:
|
_saveFailedIds.erase(decorId)
|
||||||
_dirtyDecorIds[decorId] = true
|
_render_placed_decors()
|
||||||
|
_render_inventory_list()
|
||||||
_finalize_save_if_ready()
|
_finalize_save_if_ready()
|
||||||
|
|
||||||
func _can_edit_layout() -> bool:
|
func _can_edit_layout() -> bool:
|
||||||
@@ -912,6 +954,8 @@ func _set_edit_mode(enabled: bool) -> void:
|
|||||||
_undoHistory.clear()
|
_undoHistory.clear()
|
||||||
_redoHistory.clear()
|
_redoHistory.clear()
|
||||||
_resetArmed = false
|
_resetArmed = false
|
||||||
|
_showPlacementGrid = false
|
||||||
|
_update_placement_grid_visibility()
|
||||||
_refresh_decor_selection()
|
_refresh_decor_selection()
|
||||||
_update_editor_toolbar()
|
_update_editor_toolbar()
|
||||||
_show_toast("布局编辑已开启;改动会在点击保存后提交")
|
_show_toast("布局编辑已开启;改动会在点击保存后提交")
|
||||||
@@ -936,6 +980,8 @@ func _end_edit_session() -> void:
|
|||||||
_redoHistory.clear()
|
_redoHistory.clear()
|
||||||
_resetArmed = false
|
_resetArmed = false
|
||||||
_exitAfterSave = false
|
_exitAfterSave = false
|
||||||
|
_showPlacementGrid = false
|
||||||
|
_update_placement_grid_visibility()
|
||||||
if is_instance_valid(_inventoryPanel):
|
if is_instance_valid(_inventoryPanel):
|
||||||
_inventoryPanel.visible = false
|
_inventoryPanel.visible = false
|
||||||
_hide_unsaved_dialog()
|
_hide_unsaved_dialog()
|
||||||
@@ -975,6 +1021,10 @@ func _update_editor_toolbar() -> void:
|
|||||||
if is_instance_valid(_backpackButton):
|
if is_instance_valid(_backpackButton):
|
||||||
_backpackButton.visible = _editMode
|
_backpackButton.visible = _editMode
|
||||||
_backpackButton.disabled = _saveInProgress
|
_backpackButton.disabled = _saveInProgress
|
||||||
|
if is_instance_valid(_gridButton):
|
||||||
|
_gridButton.visible = _editMode
|
||||||
|
_gridButton.disabled = _saveInProgress
|
||||||
|
_gridButton.text = "网格:开" if _showPlacementGrid else "网格:关"
|
||||||
if is_instance_valid(_finishEditButton):
|
if is_instance_valid(_finishEditButton):
|
||||||
_finishEditButton.visible = _editMode
|
_finishEditButton.visible = _editMode
|
||||||
_finishEditButton.disabled = _saveInProgress
|
_finishEditButton.disabled = _saveInProgress
|
||||||
@@ -1015,6 +1065,18 @@ func _update_editor_toolbar() -> void:
|
|||||||
_escHintLabel.visible = _editMode
|
_escHintLabel.visible = _editMode
|
||||||
_escHintLabel.text = "Esc 结束编辑" if _editMode else ""
|
_escHintLabel.text = "Esc 结束编辑" if _editMode else ""
|
||||||
|
|
||||||
|
func _toggle_placement_grid() -> void:
|
||||||
|
if not _can_edit_layout():
|
||||||
|
return
|
||||||
|
_showPlacementGrid = not _showPlacementGrid
|
||||||
|
_update_placement_grid_visibility()
|
||||||
|
_update_editor_toolbar()
|
||||||
|
_show_toast("已显示摆放网格(蓝色:地面,紫色:墙面)" if _showPlacementGrid else "已隐藏摆放网格")
|
||||||
|
|
||||||
|
func _update_placement_grid_visibility() -> void:
|
||||||
|
if is_instance_valid(_gridOverlay):
|
||||||
|
_gridOverlay.visible = _editMode and _showPlacementGrid
|
||||||
|
|
||||||
func _record_item_change(before: Dictionary, after: Dictionary) -> void:
|
func _record_item_change(before: Dictionary, after: Dictionary) -> void:
|
||||||
if before == after:
|
if before == after:
|
||||||
return
|
return
|
||||||
@@ -1171,6 +1233,7 @@ func _save_layout(exit_after_save: bool = false) -> void:
|
|||||||
return
|
return
|
||||||
_saveInProgress = true
|
_saveInProgress = true
|
||||||
_exitAfterSave = exit_after_save
|
_exitAfterSave = exit_after_save
|
||||||
|
_hide_unsaved_dialog()
|
||||||
_saveAwaitingIds.clear()
|
_saveAwaitingIds.clear()
|
||||||
_saveFailedIds.clear()
|
_saveFailedIds.clear()
|
||||||
for decor_id in save_ids:
|
for decor_id in save_ids:
|
||||||
@@ -1291,18 +1354,23 @@ func _leave_visited_room() -> void:
|
|||||||
if not SceneManager.return_from_room_visit():
|
if not SceneManager.return_from_room_visit():
|
||||||
SceneManager.change_scene("square")
|
SceneManager.change_scene("square")
|
||||||
|
|
||||||
func _constrain_room_position(position: Vector2) -> Vector2:
|
func _placement_bounds_for_item(item: Dictionary) -> Rect2:
|
||||||
|
return WALL_PLACE_BOUNDS if str(item.get("placement_surface", "floor")) == "wall" else FLOOR_PLACE_BOUNDS
|
||||||
|
|
||||||
|
func _constrain_room_position(position: Vector2, item: Dictionary) -> Vector2:
|
||||||
|
var bounds := _placement_bounds_for_item(item)
|
||||||
return Vector2(
|
return Vector2(
|
||||||
clampf(position.x, ROOM_PLACE_BOUNDS.position.x, ROOM_PLACE_BOUNDS.end.x),
|
clampf(position.x, bounds.position.x, bounds.end.x),
|
||||||
clampf(position.y, ROOM_PLACE_BOUNDS.position.y, ROOM_PLACE_BOUNDS.end.y)
|
clampf(position.y, bounds.position.y, bounds.end.y)
|
||||||
)
|
)
|
||||||
|
|
||||||
func _snap_to_room_grid(position: Vector2) -> Vector2:
|
func _snap_to_room_grid(position: Vector2, item: Dictionary) -> Vector2:
|
||||||
var constrained := _constrain_room_position(position)
|
var constrained := _constrain_room_position(position, item)
|
||||||
return Vector2(
|
var snapped := Vector2(
|
||||||
roundf(constrained.x / ROOM_GRID_SIZE) * ROOM_GRID_SIZE,
|
roundf(constrained.x / ROOM_GRID_SIZE) * ROOM_GRID_SIZE,
|
||||||
roundf(constrained.y / ROOM_GRID_SIZE) * ROOM_GRID_SIZE
|
roundf(constrained.y / ROOM_GRID_SIZE) * ROOM_GRID_SIZE
|
||||||
)
|
)
|
||||||
|
return _constrain_room_position(snapped, item)
|
||||||
|
|
||||||
func _is_point_inside_sprite(node: Sprite2D, worldPosition: Vector2) -> bool:
|
func _is_point_inside_sprite(node: Sprite2D, worldPosition: Vector2) -> bool:
|
||||||
if node.texture == null:
|
if node.texture == null:
|
||||||
|
|||||||
Reference in New Issue
Block a user