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_MASK: int = 1
|
||||
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 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
|
||||
|
||||
var _decorLayer: Node2D
|
||||
var _gridOverlay: Node2D
|
||||
var _inventoryRequest: HTTPRequest
|
||||
var _inventoryPanel: PanelContainer
|
||||
var _inventoryGrid: GridContainer
|
||||
@@ -43,6 +45,7 @@ var _layoutStatusLabel: Label
|
||||
var _escHintLabel: Label
|
||||
var _editButton: Button
|
||||
var _backpackButton: Button
|
||||
var _gridButton: Button
|
||||
var _undoButton: Button
|
||||
var _redoButton: Button
|
||||
var _rotateButton: Button
|
||||
@@ -67,6 +70,7 @@ var _visitorMode: bool = false
|
||||
var _visitorOwnerId: String = ""
|
||||
var _visitorOwnerName: String = ""
|
||||
var _editMode: bool = false
|
||||
var _showPlacementGrid: bool = false
|
||||
var _undoHistory: Array[Dictionary] = []
|
||||
var _redoHistory: Array[Dictionary] = []
|
||||
var _dirtyDecorIds: Dictionary = {}
|
||||
@@ -191,6 +195,41 @@ func _build_decor_layer() -> void:
|
||||
_decorLayer.y_sort_enabled = true
|
||||
ySortWorld.add_child(_decorLayer)
|
||||
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:
|
||||
_inventoryRequest = HTTPRequest.new()
|
||||
@@ -355,6 +394,7 @@ func _build_room_toolbar() -> void:
|
||||
|
||||
_editButton = _create_toolbar_button("编辑布局", func() -> void: _set_edit_mode(true))
|
||||
_backpackButton = _create_toolbar_button("背包", _toggle_inventory_panel)
|
||||
_gridButton = _create_toolbar_button("网格:关", _toggle_placement_grid)
|
||||
_undoButton = _create_toolbar_button("撤销", _undo_last_change)
|
||||
_redoButton = _create_toolbar_button("重做", _redo_last_change)
|
||||
_rotateButton = _create_toolbar_button("旋转", _rotate_selected_decor)
|
||||
@@ -364,6 +404,7 @@ func _build_room_toolbar() -> void:
|
||||
_finishEditButton = _create_toolbar_button("完成编辑", _request_finish_edit)
|
||||
row.add_child(_editButton)
|
||||
row.add_child(_backpackButton)
|
||||
row.add_child(_gridButton)
|
||||
row.add_child(_undoButton)
|
||||
row.add_child(_redoButton)
|
||||
row.add_child(_rotateButton)
|
||||
@@ -770,7 +811,7 @@ func _finish_decor_drag() -> void:
|
||||
var decorId := str(_draggedDecor.get_meta("decor_id", ""))
|
||||
if _decorItems.has(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
|
||||
item["position_x"] = snapped_position.x
|
||||
item["position_y"] = snapped_position.y
|
||||
@@ -793,10 +834,10 @@ func _finish_decor_drag() -> void:
|
||||
|
||||
func _update_dragged_decor_position() -> void:
|
||||
if _draggedDecor != null:
|
||||
_draggedDecor.global_position = _constrain_room_position(get_global_mouse_position() + _dragOffset)
|
||||
var decorId := str(_draggedDecor.get_meta("decor_id", ""))
|
||||
if not decorId.is_empty() and _decorItems.has(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_y"] = _draggedDecor.global_position.y
|
||||
_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):
|
||||
return
|
||||
_serverDecorItems[decorId] = item.duplicate(true)
|
||||
_decorItems[decorId] = item.duplicate(true)
|
||||
_saveAwaitingIds.erase(decorId)
|
||||
if _decorItems.has(decorId) and _placement_signature(_decorItems[decorId]) == _placement_signature(item):
|
||||
_dirtyDecorIds.erase(decorId)
|
||||
else:
|
||||
_dirtyDecorIds[decorId] = true
|
||||
_saveFailedIds.erase(decorId)
|
||||
_render_placed_decors()
|
||||
_render_inventory_list()
|
||||
_finalize_save_if_ready()
|
||||
|
||||
func _can_edit_layout() -> bool:
|
||||
@@ -912,6 +954,8 @@ func _set_edit_mode(enabled: bool) -> void:
|
||||
_undoHistory.clear()
|
||||
_redoHistory.clear()
|
||||
_resetArmed = false
|
||||
_showPlacementGrid = false
|
||||
_update_placement_grid_visibility()
|
||||
_refresh_decor_selection()
|
||||
_update_editor_toolbar()
|
||||
_show_toast("布局编辑已开启;改动会在点击保存后提交")
|
||||
@@ -936,6 +980,8 @@ func _end_edit_session() -> void:
|
||||
_redoHistory.clear()
|
||||
_resetArmed = false
|
||||
_exitAfterSave = false
|
||||
_showPlacementGrid = false
|
||||
_update_placement_grid_visibility()
|
||||
if is_instance_valid(_inventoryPanel):
|
||||
_inventoryPanel.visible = false
|
||||
_hide_unsaved_dialog()
|
||||
@@ -975,6 +1021,10 @@ func _update_editor_toolbar() -> void:
|
||||
if is_instance_valid(_backpackButton):
|
||||
_backpackButton.visible = _editMode
|
||||
_backpackButton.disabled = _saveInProgress
|
||||
if is_instance_valid(_gridButton):
|
||||
_gridButton.visible = _editMode
|
||||
_gridButton.disabled = _saveInProgress
|
||||
_gridButton.text = "网格:开" if _showPlacementGrid else "网格:关"
|
||||
if is_instance_valid(_finishEditButton):
|
||||
_finishEditButton.visible = _editMode
|
||||
_finishEditButton.disabled = _saveInProgress
|
||||
@@ -1015,6 +1065,18 @@ func _update_editor_toolbar() -> void:
|
||||
_escHintLabel.visible = _editMode
|
||||
_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:
|
||||
if before == after:
|
||||
return
|
||||
@@ -1171,6 +1233,7 @@ func _save_layout(exit_after_save: bool = false) -> void:
|
||||
return
|
||||
_saveInProgress = true
|
||||
_exitAfterSave = exit_after_save
|
||||
_hide_unsaved_dialog()
|
||||
_saveAwaitingIds.clear()
|
||||
_saveFailedIds.clear()
|
||||
for decor_id in save_ids:
|
||||
@@ -1291,18 +1354,23 @@ func _leave_visited_room() -> void:
|
||||
if not SceneManager.return_from_room_visit():
|
||||
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(
|
||||
clampf(position.x, ROOM_PLACE_BOUNDS.position.x, ROOM_PLACE_BOUNDS.end.x),
|
||||
clampf(position.y, ROOM_PLACE_BOUNDS.position.y, ROOM_PLACE_BOUNDS.end.y)
|
||||
clampf(position.x, bounds.position.x, bounds.end.x),
|
||||
clampf(position.y, bounds.position.y, bounds.end.y)
|
||||
)
|
||||
|
||||
func _snap_to_room_grid(position: Vector2) -> Vector2:
|
||||
var constrained := _constrain_room_position(position)
|
||||
return Vector2(
|
||||
func _snap_to_room_grid(position: Vector2, item: Dictionary) -> Vector2:
|
||||
var constrained := _constrain_room_position(position, item)
|
||||
var snapped := Vector2(
|
||||
roundf(constrained.x / 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:
|
||||
if node.texture == null:
|
||||
|
||||
Reference in New Issue
Block a user