diff --git a/art/exported/characters/patterns/fox/body_arms_fox.png b/art/exported/characters/patterns/fox/body_arms_fox.png index 6fcccd8..ad2da4d 100644 Binary files a/art/exported/characters/patterns/fox/body_arms_fox.png and b/art/exported/characters/patterns/fox/body_arms_fox.png differ diff --git a/art/exported/characters/patterns/fox/body_main_fox.png b/art/exported/characters/patterns/fox/body_main_fox.png index efe8299..b95fbc6 100644 Binary files a/art/exported/characters/patterns/fox/body_main_fox.png and b/art/exported/characters/patterns/fox/body_main_fox.png differ diff --git a/art/exported/characters/patterns/fox/head_pointy_fox.png b/art/exported/characters/patterns/fox/head_pointy_fox.png index 63e8bc0..e77f6a0 100644 Binary files a/art/exported/characters/patterns/fox/head_pointy_fox.png and b/art/exported/characters/patterns/fox/head_pointy_fox.png differ diff --git a/items/icons/art/art_kit_eraser.png b/items/icons/art/art_kit_eraser.png index 034df89..6eb3003 100644 Binary files a/items/icons/art/art_kit_eraser.png and b/items/icons/art/art_kit_eraser.png differ diff --git a/items/icons/art/art_kit_grid_finish_light.png b/items/icons/art/art_kit_grid_finish_light.png new file mode 100644 index 0000000..684efed Binary files /dev/null and b/items/icons/art/art_kit_grid_finish_light.png differ diff --git a/items/icons/art/art_kit_grid_finish_light.png.import b/items/icons/art/art_kit_grid_finish_light.png.import new file mode 100644 index 0000000..63d8261 --- /dev/null +++ b/items/icons/art/art_kit_grid_finish_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dm24dy3ebvqmv" +path="res://.godot/imported/art_kit_grid_finish_light.png-fe16d574640fb174cb8b6ff3cfb95e5e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/art/art_kit_grid_finish_light.png" +dest_files=["res://.godot/imported/art_kit_grid_finish_light.png-fe16d574640fb174cb8b6ff3cfb95e5e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/art/art_kit_grid_hidden_light.png b/items/icons/art/art_kit_grid_hidden_light.png new file mode 100644 index 0000000..af01077 Binary files /dev/null and b/items/icons/art/art_kit_grid_hidden_light.png differ diff --git a/items/icons/art/art_kit_grid_hidden_light.png.import b/items/icons/art/art_kit_grid_hidden_light.png.import new file mode 100644 index 0000000..95d7ac3 --- /dev/null +++ b/items/icons/art/art_kit_grid_hidden_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cv6yicrysb0vs" +path="res://.godot/imported/art_kit_grid_hidden_light.png-e92350d8a3e2c3e3bf286658f1ae0c41.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/art/art_kit_grid_hidden_light.png" +dest_files=["res://.godot/imported/art_kit_grid_hidden_light.png-e92350d8a3e2c3e3bf286658f1ae0c41.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/art/art_kit_grid_light.png b/items/icons/art/art_kit_grid_light.png new file mode 100644 index 0000000..58089a3 Binary files /dev/null and b/items/icons/art/art_kit_grid_light.png differ diff --git a/items/icons/art/art_kit_grid_light.png.import b/items/icons/art/art_kit_grid_light.png.import new file mode 100644 index 0000000..6435819 --- /dev/null +++ b/items/icons/art/art_kit_grid_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dh2tu3ptqe6f7" +path="res://.godot/imported/art_kit_grid_light.png-d445eba68746a34d73bbf1744fe56777.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/art/art_kit_grid_light.png" +dest_files=["res://.godot/imported/art_kit_grid_light.png-d445eba68746a34d73bbf1744fe56777.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/art/art_kit_grid_restore_light.png b/items/icons/art/art_kit_grid_restore_light.png new file mode 100644 index 0000000..9517e6d Binary files /dev/null and b/items/icons/art/art_kit_grid_restore_light.png differ diff --git a/items/icons/art/art_kit_grid_restore_light.png.import b/items/icons/art/art_kit_grid_restore_light.png.import new file mode 100644 index 0000000..9672eab --- /dev/null +++ b/items/icons/art/art_kit_grid_restore_light.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ckxdpi5sqerao" +path="res://.godot/imported/art_kit_grid_restore_light.png-d2f2458a90dabc79430d7ff0e0e3385d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://items/icons/art/art_kit_grid_restore_light.png" +dest_files=["res://.godot/imported/art_kit_grid_restore_light.png-d2f2458a90dabc79430d7ff0e0e3385d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/items/icons/art/art_kit_marker.png b/items/icons/art/art_kit_marker.png index 1841352..403f69c 100644 Binary files a/items/icons/art/art_kit_marker.png and b/items/icons/art/art_kit_marker.png differ diff --git a/main/main.gd b/main/main.gd index 73269b5..589099a 100644 --- a/main/main.gd +++ b/main/main.gd @@ -554,6 +554,7 @@ func _initialize_application(dedicated: bool) -> void: _player, _surface_drawings_root, _player.bag, + _player.hotbar, _player.art_unlocks, ) _network_player_list.set_surface_drawing_service( diff --git a/network/network_surface_drawing_service.gd b/network/network_surface_drawing_service.gd index c16729e..e42519c 100644 --- a/network/network_surface_drawing_service.gd +++ b/network/network_surface_drawing_service.gd @@ -41,6 +41,7 @@ var _spawn_service: PlayerSpawnService var _relationships: PlayerRelationshipStore var _local_player: Player var _bag: PlayerBag +var _hotbar: PlayerHotbar var _art_unlocks: PlayerArtUnlocks var _drawing_root: Node3D var _canvas_states: Dictionary[String, Dictionary] = {} @@ -87,6 +88,7 @@ func setup( local_player: Player, drawing_root: Node3D, bag: PlayerBag, + hotbar: PlayerHotbar, art_unlocks: PlayerArtUnlocks, ) -> void: _session = session @@ -95,6 +97,7 @@ func setup( _local_player = local_player _drawing_root = drawing_root _bag = bag + _hotbar = hotbar _art_unlocks = art_unlocks _refresh_local_unlocks() if _session != null: @@ -123,20 +126,6 @@ func handle_input( can_open: bool, pointer_screen_position: Vector2 = INVALID_POINTER_SCREEN_POSITION, ) -> bool: - if event is InputEventKey: - var key_event := event as InputEventKey - if ( - key_event.pressed - and not key_event.echo - and key_event.physical_keycode == KEY_P - ): - if _active: - deactivate() - return true - if can_open and can_activate(): - activate(pointer_screen_position) - return true - return false if not _active: return false if not can_open: @@ -149,8 +138,7 @@ func handle_input( if _placing_grid: _set_placement_mode(false) return true - deactivate() - return true + return false if event is InputEventKey: var key_event := event as InputEventKey if not key_event.pressed or key_event.echo: @@ -172,28 +160,19 @@ func handle_input( return true if event is InputEventMouseButton: var mouse_event := event as InputEventMouseButton + # Ordinary wheel input always belongs to Hotbar selection. Brush size is + # selected explicitly in the Art Kit toolbar. + if mouse_event.button_index in [ + MOUSE_BUTTON_WHEEL_UP, + MOUSE_BUTTON_WHEEL_DOWN, + ]: + return false _set_pointer_from_input( mouse_event.position, pointer_screen_position, ) _update_aim() - if ( - mouse_event.shift_pressed - and mouse_event.button_index in [ - MOUSE_BUTTON_WHEEL_UP, - MOUSE_BUTTON_WHEEL_DOWN, - ] - ): - return false match mouse_event.button_index: - MOUSE_BUTTON_WHEEL_UP: - if mouse_event.pressed and not _placing_grid: - _set_brush_size(_brush_size + 1) - return true - MOUSE_BUTTON_WHEEL_DOWN: - if mouse_event.pressed and not _placing_grid: - _set_brush_size(_brush_size - 1) - return true MOUSE_BUTTON_LEFT: if _placing_grid: if mouse_event.pressed: @@ -241,7 +220,7 @@ func activate( if _active or not can_activate(): return _active = true - _placing_grid = true + _placing_grid = false _eraser_mode = false _clear_armed_guide_action(false) _refresh_local_unlocks() @@ -257,9 +236,7 @@ func activate( Input.mouse_mode = Input.MOUSE_MODE_VISIBLE _update_aim() _refresh_stencil_visibility() - _emit_hud_state( - "click to place a shared grid" - ) + _emit_hud_state("marker selected") func deactivate() -> void: @@ -288,7 +265,11 @@ func is_placement_mode() -> bool: func can_activate() -> bool: - return _drawing_available() and _owns_art_kit() + return ( + _drawing_available() + and _owns_art_kit() + and _art_kit_is_selected() + ) func set_placement_mode(enabled: bool) -> void: @@ -415,6 +396,15 @@ func _owns_art_kit() -> bool: return _bag != null and _bag.owns_item(ArtShopStockType.ART_KIT_ITEM_ID) +func _art_kit_is_selected() -> bool: + return ( + _hotbar != null + and _hotbar.get_selected_assignment_kind() + == PlayerHotbar.AssignmentKind.ITEM + and _hotbar.get_selected_item_id() == ArtShopStockType.ART_KIT_ITEM_ID + ) + + func _local_entitlement() -> Dictionary: return { "has_kit": _owns_art_kit(), @@ -550,8 +540,7 @@ func request_canvas_at_surface( tangent: Vector3, ) -> bool: if ( - not _drawing_available() - or not _owns_art_kit() + not can_activate() or _art_unlocks == null or not _art_unlocks.is_grid_size_unlocked(_grid_size) or normal.is_zero_approx() @@ -581,8 +570,7 @@ func request_cell_edits( stroke_id: String = "", ) -> bool: if ( - not _drawing_available() - or not _owns_art_kit() + not can_activate() or canvas_id.is_empty() or edits.is_empty() ): @@ -613,7 +601,7 @@ func request_guide_visibility( should_be_visible: bool, should_finalize: bool = false, ) -> bool: - if not _drawing_available() or not _owns_art_kit() or canvas_id.is_empty(): + if not can_activate() or canvas_id.is_empty(): return false var data: Dictionary = { "request_id": _new_request_id("guide"), @@ -633,8 +621,7 @@ func request_guide_visibility( func request_undo_last_stroke() -> bool: if ( - not _drawing_available() - or not _owns_art_kit() + not can_activate() or _last_local_stroke_id.is_empty() ): _emit_hud_state("nothing to undo") @@ -698,6 +685,9 @@ func get_canvas_state(canvas_id: String) -> Dictionary: func _process(_delta: float) -> void: if not _active: return + if not can_activate(): + deactivate() + return _update_aim() if _placing_grid: return @@ -2171,7 +2161,7 @@ func _on_peer_removed(peer_id: int) -> void: func _on_local_art_entitlement_changed() -> void: - if _active and not _owns_art_kit(): + if _active and not can_activate(): deactivate() _publish_local_entitlement() diff --git a/tests/art_tools_validation.gd b/tests/art_tools_validation.gd index 22ca940..e34a289 100644 --- a/tests/art_tools_validation.gd +++ b/tests/art_tools_validation.gd @@ -259,9 +259,8 @@ func _run() -> void: assert(game_ui.is_gameplay_hud_hidden()) assert(player_menu.visible) assert(root.gui_get_focus_owner() is not LineEdit) - player_menu.close_menu() - for _frame: int in 12: - await process_frame + player_menu.close_menu(PlayerMenu.CloseReason.SESSION_END) + await process_frame game_ui.call("_on_quick_action_selected", &"hud") await process_frame assert(not game_ui.is_gameplay_hud_hidden()) @@ -269,14 +268,17 @@ func _run() -> void: assert(not service.can_activate()) assert(not service.is_active() and not toolbar.visible) assert(player.bag.add_item(ArtShopStock.ART_KIT_ITEM_ID, 1)) - assert(service.can_activate()) - + player.hotbar.select_slot(1) + assert(player.hotbar.assign_item(0, ArtShopStock.ART_KIT_ITEM_ID)) + assert(not service.can_activate()) var paint_key := InputEventKey.new() paint_key.physical_keycode = KEY_P paint_key.pressed = true - assert(service.handle_input(paint_key, true)) + assert(not service.handle_input(paint_key, true)) + assert(not service.is_active()) + player.hotbar.select_slot(0) await process_frame - assert(service.is_active() and service.is_placement_mode()) + assert(service.is_active() and not service.is_placement_mode()) assert(toolbar.visible) var ui_root := game_ui.get_node("%UIRoot") as Control assert(toolbar.get_parent() == ui_root) @@ -291,6 +293,8 @@ func _run() -> void: assert(toolbar.get_global_rect().end.y <= 720.0) var top_panel := toolbar.get_node("%TopPanel") as PanelContainer var color_panel := toolbar.get_node("%ColorPanel") as PanelContainer + assert(is_equal_approx(toolbar.size.x, SurfaceDrawingToolbar.TOOLBAR_WIDTH)) + assert(is_equal_approx(top_panel.size.x, SurfaceDrawingToolbar.TOOLBAR_WIDTH)) assert(top_panel.position.is_equal_approx(Vector2.ZERO)) assert(color_panel.position.x > 0.0) assert(is_equal_approx(color_panel.get_rect().end.x, top_panel.get_rect().end.x)) @@ -315,7 +319,6 @@ func _run() -> void: var hide_button := toolbar.get_node("%HideGuideButton") as Button var restore_button := toolbar.get_node("%RestoreGuideButton") as Button var finalize_button := toolbar.get_node("%FinalizeGuideButton") as Button - var close_button := toolbar.get_node("%CloseButton") as Button assert( mode_button != null and eraser_button != null @@ -323,16 +326,30 @@ func _run() -> void: and hide_button != null and restore_button != null and finalize_button != null - and close_button != null ) - assert(eraser_button.text.is_empty()) - assert(eraser_button.icon != null) - assert( - eraser_button.icon.resource_path.ends_with( - "/art/art_kit_eraser.png" + assert(toolbar.get_node_or_null("%CloseButton") == null) + var expected_toolbar_icons: Dictionary[Button, String] = { + mode_button: "/art/art_kit_marker.png", + eraser_button: "/art/art_kit_eraser.png", + hide_button: "/art/art_kit_grid_hidden_light.png", + restore_button: "/art/art_kit_grid_restore_light.png", + finalize_button: "/art/art_kit_grid_finish_light.png", + } + for icon_button: Button in expected_toolbar_icons: + assert(icon_button.text.is_empty()) + assert(icon_button.icon != null) + assert( + icon_button.icon.resource_path.ends_with( + expected_toolbar_icons[icon_button] + ) ) - ) - for icon_button: Button in [eraser_button, undo_button]: + for icon_button: Button in [ + eraser_button, + undo_button, + hide_button, + restore_button, + finalize_button, + ]: assert(icon_button.custom_minimum_size == Vector2(48, 44)) assert(icon_button.get_theme_constant("icon_max_width") == 40) var icon_style := ( @@ -340,6 +357,9 @@ func _run() -> void: ) assert(is_equal_approx(icon_style.content_margin_left, 4.0)) assert(is_equal_approx(icon_style.content_margin_top, 2.0)) + assert(mode_button.custom_minimum_size == Vector2(48, 48)) + assert(mode_button.get_theme_constant("icon_max_width") == 40) + assert(mode_button.get_index() > finalize_button.get_index()) assert(hide_button.button_group != null) assert(hide_button.button_group == restore_button.button_group) assert(hide_button.button_group == finalize_button.button_group) @@ -371,7 +391,12 @@ func _run() -> void: assert(service.get_grid_size() == 128) mode_button.pressed.emit() - assert(not service.is_placement_mode()) + assert(service.is_placement_mode()) + assert( + mode_button.icon.resource_path.ends_with( + "/art/art_kit_grid_light.png" + ) + ) eraser_button.pressed.emit() assert(service.is_eraser_mode()) assert(eraser_button.button_pressed) @@ -427,18 +452,8 @@ func _run() -> void: assert(not service.is_eraser_mode()) undo_button.pressed.emit() assert(service.is_active()) - close_button.pressed.emit() - assert(not service.is_active() and not toolbar.visible) - - assert(service.handle_input(paint_key, true)) - await process_frame + assert(not service.handle_input(paint_key, true)) assert(service.is_active() and toolbar.visible) - - assert(service.handle_input(paint_key, true)) - await process_frame - assert(not service.is_active() and not toolbar.visible) - assert(player.hotbar.assign_item(0, ArtShopStock.ART_KIT_ITEM_ID)) - player.hotbar.select_slot(0) var fishing_spot := main.get_node("%FishingSpot") as FishingSpot await process_frame var held_art_kit_display := player.get("_held_art_kit_display") as Node3D @@ -472,12 +487,36 @@ func _run() -> void: assert(service.get_grid_size() == 128) assert(service.handle_input(world_click, true)) assert(service.is_active() and toolbar.visible) - player.hotbar.select_slot(1) + var hotbar_ui := game_ui.get_node("%Hotbar") as HotbarUI + var wheel_down := InputEventMouseButton.new() + wheel_down.button_index = MOUSE_BUTTON_WHEEL_DOWN + wheel_down.pressed = true + assert(not service.handle_input(wheel_down, true)) + hotbar_ui._unhandled_input(wheel_down) await process_frame + assert(player.hotbar.get_selected_slot() == 1) assert(not service.is_active() and not toolbar.visible) - assert(service.handle_input(paint_key, true)) + + var number_one := InputEventKey.new() + number_one.physical_keycode = KEY_1 + number_one.pressed = true + assert(not service.handle_input(number_one, true)) + hotbar_ui._unhandled_input(number_one) await process_frame assert(service.is_active() and toolbar.visible) + assert(player.hotbar.get_selected_slot() == 0) + var number_two := InputEventKey.new() + number_two.physical_keycode = KEY_2 + number_two.pressed = true + assert(not service.handle_input(number_two, true)) + hotbar_ui._unhandled_input(number_two) + await process_frame + assert(player.hotbar.get_selected_slot() == 1) + assert(not service.is_active() and not toolbar.visible) + game_ui.call("_on_quick_action_selected", &"paint") + await process_frame + assert(player.hotbar.get_selected_slot() == 0) + assert(service.is_active() and toolbar.visible) print("Art tools validation: PASS") var session := main.get_node("%NetworkSession") as NetworkSession diff --git a/tests/surface_drawing_multiplayer_validation.gd b/tests/surface_drawing_multiplayer_validation.gd index 7b4d8d6..74c86d2 100644 --- a/tests/surface_drawing_multiplayer_validation.gd +++ b/tests/surface_drawing_multiplayer_validation.gd @@ -32,6 +32,8 @@ func _run_host() -> void: await physics_frame var player := main.get("_player") as Player assert(player.bag.add_item(&"art_kit", 1)) + assert(player.hotbar.assign_item(0, &"art_kit")) + assert(player.hotbar.select_slot(0)) assert(player.art_unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK)) var service := main.get_node( @@ -140,6 +142,8 @@ func _run_client() -> void: )) var player := main.get("_player") as Player assert(player.bag.add_item(&"art_kit", 1)) + assert(player.hotbar.assign_item(0, &"art_kit")) + assert(player.hotbar.select_slot(0)) assert(player.art_unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK)) var service := main.get_node( diff --git a/tests/surface_drawing_runtime_validation.gd b/tests/surface_drawing_runtime_validation.gd index b8c00d9..8835127 100644 --- a/tests/surface_drawing_runtime_validation.gd +++ b/tests/surface_drawing_runtime_validation.gd @@ -29,12 +29,15 @@ func _run() -> void: var player := main.get("_player") as Player assert(player.bag.add_item(&"art_kit", 1)) + assert(player.hotbar.assign_item(0, &"art_kit")) + assert(player.hotbar.select_slot(0)) assert(player.art_unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK)) var service := main.get_node( "%NetworkSurfaceDrawingService" ) as NetworkSurfaceDrawingService assert(service != null) assert(session.supports_server_capability(SurfaceDrawingProtocol.CAPABILITY)) + service.deactivate() _validate_marker_controls(service, player) var query := PhysicsRayQueryParameters3D.create( player.global_position + Vector3.UP * 3.0, @@ -124,7 +127,7 @@ func _validate_marker_controls( var activation_pointer := Vector2(824.0, 318.0) service.activate(activation_pointer) assert(service.is_active()) - assert(service.is_placement_mode()) + assert(not service.is_placement_mode()) assert(service.get_pointer_screen_position() == activation_pointer) if DisplayServer.get_name() != "headless": assert(Input.mouse_mode == Input.MOUSE_MODE_VISIBLE) @@ -133,7 +136,7 @@ func _validate_marker_controls( placement_key.physical_keycode = KEY_R placement_key.pressed = true assert(service.handle_input(placement_key, true)) - assert(not service.is_placement_mode()) + assert(service.is_placement_mode()) var pointer_before: Vector2 = service.get_pointer_screen_position() var pointer_motion := InputEventMouseMotion.new() @@ -171,6 +174,6 @@ func _validate_marker_controls( assert(not service.handle_input(camera_release, true)) assert(service.handle_input(placement_key, true)) - assert(service.is_placement_mode()) + assert(not service.is_placement_mode()) service.deactivate() assert(Input.mouse_mode == prior_mouse_mode) diff --git a/ui/game_ui.gd b/ui/game_ui.gd index b142e33..08717b3 100644 --- a/ui/game_ui.gd +++ b/ui/game_ui.gd @@ -122,6 +122,7 @@ const SHOP_ANIMALESE_VOICE_ID: String = "natural" const SHOP_ANIMALESE_BASE_PITCH: float = 1.08 const SHOP_SPEECH_CHARACTERS_PER_SECOND: float = 28.0 const SHOP_NPC_SPEECH_COOLDOWN_MILLISECONDS: int = 5000 +const ART_KIT_ITEM_ID: StringName = &"art_kit" @onready var _canonical_stage: Control = %CanonicalStage @@ -164,6 +165,7 @@ var _network_chat_service: NetworkChatService var _network_profile: NetworkProfilePreferences var _spawn_service: PlayerSpawnService var _bag: PlayerBagType +var _hotbar: PlayerHotbarType var _item_catalog: ItemCatalogType var _player_menu_open: bool = false var _gameplay_ui_enabled: bool = false @@ -177,6 +179,7 @@ var _item_effects: PlayerItemEffectsType var _main_shop_buyer: FishBuyerProfileType var _shop_interaction: ShopInteractionType var _surface_drawing: NetworkSurfaceDrawingService +var _surface_drawing_hotbar_selected: bool = false var _experience: PlayerExperienceType var _experience_award_queue: Array[Dictionary] = [] var _experience_animation_active: bool = false @@ -293,6 +296,7 @@ func setup( _network_profile = network_profile _spawn_service = spawn_service _bag = bag + _hotbar = hotbar _item_catalog = item_catalog _settings_manager = settings_manager _fishing_spot = fishing_spot @@ -448,16 +452,7 @@ func _input(event: InputEvent) -> void: ) get_viewport().set_input_as_handled() return - var drawing_can_open: bool = ( - _gameplay_ui_enabled - and not _system_menu_open - and not _player_menu_open - and not _shop_open - and not _chat_input_open - and not _showcase_active - and _fishing_spot != null - and _fishing_spot.can_use_surface_drawing() - ) + var drawing_can_open: bool = _can_surface_drawing_be_active() if ( _surface_drawing != null and _surface_drawing.handle_input( @@ -630,7 +625,7 @@ func _on_quick_action_selected(action_id: StringName) -> void: &"online": _player_menu.open_section(PlayerMenuType.Section.PLAYERS) &"paint": - _toggle_surface_drawing() + _select_art_kit_hotbar_slot() &"chat": _chat_ui.open_chat() &"freecam": @@ -1121,25 +1116,46 @@ static func get_virtual_mouse_window_bounds(window_size: Vector2) -> Rect2: return Rect2(cursor_margin, bounds_size) -func _toggle_surface_drawing() -> void: - if _surface_drawing == null: +func _select_art_kit_hotbar_slot() -> void: + if _hotbar == null: return - if _surface_drawing.is_active(): - _surface_drawing.deactivate() - elif _surface_drawing.can_activate(): - _surface_drawing.activate(_drawing_pointer_window_position()) + for slot_index: int in range(PlayerHotbarType.SLOT_COUNT): + if _hotbar.get_item_id(slot_index) == ART_KIT_ITEM_ID: + _hotbar.select_slot(slot_index) + return func set_surface_drawing_hotbar_selected(is_selected: bool) -> void: + _surface_drawing_hotbar_selected = is_selected + _refresh_surface_drawing_activation() + + +func _refresh_surface_drawing_activation() -> void: if _surface_drawing == null: return - if is_selected: - if not _surface_drawing.is_active() and _surface_drawing.can_activate(): - _surface_drawing.activate(_drawing_pointer_window_position()) - elif _surface_drawing.is_active(): + var should_be_active: bool = _can_surface_drawing_be_active() + if should_be_active and not _surface_drawing.is_active(): + _surface_drawing.activate(_drawing_pointer_window_position()) + elif not should_be_active and _surface_drawing.is_active(): _surface_drawing.deactivate() +func _can_surface_drawing_be_active() -> bool: + return ( + _surface_drawing_hotbar_selected + and _gameplay_ui_enabled + and not _system_menu_open + and not _player_menu_open + and not _shop_open + and not _chat_input_open + and not _showcase_active + and _fishing_spot != null + and _fishing_spot.can_use_surface_drawing() + and _surface_drawing != null + and _surface_drawing.can_activate() + ) + + func _drawing_pointer_window_position() -> Vector2: if _virtual_mouse_active: return _virtual_mouse_window_position @@ -1462,6 +1478,7 @@ func set_gameplay_ui_enabled(enabled: bool) -> void: _hotbar_ui.set_gameplay_input_enabled(true) _refresh_fishing_panel_visibility() call_deferred("_start_next_experience_animation") + _refresh_surface_drawing_activation() func set_gameplay_hud_hidden(hidden: bool) -> void: @@ -1505,8 +1522,7 @@ func _refresh_gameplay_hud_visibility() -> void: func set_system_menu_open(is_open: bool) -> void: _system_menu_open = is_open - if is_open and _surface_drawing != null: - _surface_drawing.deactivate() + _refresh_surface_drawing_activation() _refresh_gameplay_hud_visibility() _refresh_chat_availability() _refresh_hotbar_visibility() @@ -1862,6 +1878,7 @@ func _on_showcase_changed( showcase_visible: bool, ) -> void: _showcase_active = showcase_visible + _refresh_surface_drawing_activation() if not showcase_visible: _set_fishing_panel_showcase_position(false) _showcase_details.text = "" @@ -2103,8 +2120,7 @@ func _on_player_menu_visibility_changed(is_open: bool) -> void: _player_menu_open = is_open if is_open: _end_virtual_mouse() - if is_open and _surface_drawing != null: - _surface_drawing.deactivate() + _refresh_surface_drawing_activation() _refresh_gameplay_hud_visibility() if is_open: _hotbar_ui.set_drag_enabled(false) @@ -2156,8 +2172,7 @@ func _on_shop_exit_started() -> void: func _on_shop_visibility_changed(is_open: bool) -> void: _shop_open = is_open - if is_open and _surface_drawing != null: - _surface_drawing.deactivate() + _refresh_surface_drawing_activation() if is_open: shop_backdrop_visibility_changed.emit(true) if not is_open and _player_menu.is_shop_cooler_mounted(): @@ -2309,8 +2324,7 @@ func _emit_interactive_pointer_ui_changed() -> void: func _on_chat_text_entry_ownership_changed(active: bool) -> void: _chat_input_open = active - if active and _surface_drawing != null: - _surface_drawing.deactivate() + _refresh_surface_drawing_activation() _refresh_chat_availability() _emit_interactive_pointer_ui_changed() diff --git a/ui/surface_drawing_toolbar.gd b/ui/surface_drawing_toolbar.gd index 06cf7e7..e8fd4aa 100644 --- a/ui/surface_drawing_toolbar.gd +++ b/ui/surface_drawing_toolbar.gd @@ -2,7 +2,13 @@ class_name SurfaceDrawingToolbar extends Control const UtilityPageStyleType = preload("res://ui/utility_page_style.gd") -const TOOLBAR_WIDTH: float = 580.0 +const MARKER_MODE_ICON: Texture2D = preload( + "res://items/icons/art/art_kit_marker.png" +) +const GRID_MODE_ICON: Texture2D = preload( + "res://items/icons/art/art_kit_grid_light.png" +) +const TOOLBAR_WIDTH: float = 510.0 const TOOLBAR_HEIGHT: float = 373.0 const COLOR_RAIL_WIDTH: float = 46.0 @@ -17,7 +23,6 @@ const COLOR_RAIL_WIDTH: float = 46.0 @onready var _hide_guide_button: Button = %HideGuideButton @onready var _restore_guide_button: Button = %RestoreGuideButton @onready var _finalize_guide_button: Button = %FinalizeGuideButton -@onready var _close_button: Button = %CloseButton var _service: NetworkSurfaceDrawingService var _unlocks: PlayerArtUnlocks @@ -36,10 +41,11 @@ func _ready() -> void: UtilityPageStyleType.apply_ocean_button(button) button.custom_minimum_size = Vector2(48, 44) _make_button_round(button, 22) - _enlarge_action_icon(_eraser_button) - _enlarge_action_icon(_undo_button) _mode_button.custom_minimum_size = Vector2(48, 48) _make_button_round(_mode_button, 24) + _enlarge_action_icon(_mode_button) + for button: Button in _action_buttons(): + _enlarge_action_icon(button) _mode_button.pressed.connect(_toggle_mode) _eraser_button.pressed.connect(_toggle_eraser) _undo_button.pressed.connect(_undo_last_stroke) @@ -52,7 +58,6 @@ func _ready() -> void: _finalize_guide_button.pressed.connect( _arm_guide_action.bind(NetworkSurfaceDrawingService.GuideAction.FINALIZE) ) - _close_button.pressed.connect(_close_toolbar) _brush_option.item_selected.connect(_select_brush) _grid_option.item_selected.connect(_select_grid) _build_options() @@ -77,7 +82,6 @@ func _action_buttons() -> Array[Button]: _hide_guide_button, _restore_guide_button, _finalize_guide_button, - _close_button, ] @@ -146,6 +150,8 @@ func _apply_dock_side() -> void: offset_right = 0.0 if _dock_right else TOOLBAR_WIDTH offset_top = 0.0 offset_bottom = TOOLBAR_HEIGHT + _top_panel.offset_left = 0.0 + _top_panel.offset_right = TOOLBAR_WIDTH _color_panel.offset_left = ( TOOLBAR_WIDTH - COLOR_RAIL_WIDTH if _dock_right else 0.0 ) @@ -276,11 +282,6 @@ func _arm_guide_action(action: int) -> void: _service.arm_guide_action(action) -func _close_toolbar() -> void: - if _service != null: - _service.deactivate() - - func _on_unlocks_changed(_unlock_mask: int) -> void: _refresh_unlocks() @@ -297,7 +298,14 @@ func _on_service_state_changed( visible = is_active if not is_active: return - _mode_button.text = "▦" if mode_name == "place grid" else "●" + _mode_button.icon = ( + GRID_MODE_ICON if mode_name == "place grid" else MARKER_MODE_ICON + ) + _mode_button.accessibility_name = ( + "switch to marker mode" + if mode_name == "place grid" + else "switch to grid mode" + ) _mode_button.tooltip_text = ( "Switch to marker mode" if mode_name == "place grid" diff --git a/ui/surface_drawing_toolbar.tscn b/ui/surface_drawing_toolbar.tscn index 45559fd..448cae2 100644 --- a/ui/surface_drawing_toolbar.tscn +++ b/ui/surface_drawing_toolbar.tscn @@ -1,11 +1,14 @@ -[gd_scene load_steps=8 format=3] +[gd_scene load_steps=11 format=3] [ext_resource type="Script" path="res://ui/surface_drawing_toolbar.gd" id="1_script"] [ext_resource type="Theme" path="res://ui/game_theme.tres" id="2_theme"] [ext_resource type="Texture2D" path="res://ui/icons/pictograms/undo_light.png" id="3_undo_light"] -[ext_resource type="Texture2D" path="res://ui/icons/pictograms/check_mark_light.png" id="4_check_mark_light"] -[ext_resource type="Texture2D" path="res://ui/icons/pictograms/x_light.png" id="5_x_light"] -[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_eraser.png" id="6_art_kit_eraser"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_eraser.png" id="4_art_kit_eraser"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_marker.png" id="5_art_kit_marker"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_grid_light.png" id="6_grid_light"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_grid_hidden_light.png" id="7_grid_hidden"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_grid_restore_light.png" id="8_grid_restore"] +[ext_resource type="Texture2D" path="res://items/icons/art/art_kit_grid_finish_light.png" id="9_grid_finish"] [sub_resource type="ButtonGroup" id="GuideActionGroup"] allow_unpress = true @@ -17,7 +20,7 @@ layout_mode = 0 anchors_preset = 1 anchor_left = 1.0 anchor_right = 1.0 -offset_left = -580.0 +offset_left = -510.0 offset_bottom = 373.0 grow_horizontal = 0 mouse_filter = 2 @@ -27,19 +30,13 @@ script = ExtResource("1_script") [node name="TopPanel" type="PanelContainer" parent="."] unique_name_in_owner = true layout_mode = 0 -offset_right = 580.0 +offset_right = 510.0 offset_bottom = 60.0 [node name="Top" type="HBoxContainer" parent="TopPanel"] layout_mode = 2 theme_override_constants/separation = 5 -[node name="ModeButton" type="Button" parent="TopPanel/Top"] -unique_name_in_owner = true -layout_mode = 2 -tooltip_text = "switch between grid and marker mode" -text = "▦" - [node name="BrushOption" type="OptionButton" parent="TopPanel/Top"] unique_name_in_owner = true custom_minimum_size = Vector2(80, 48) @@ -59,7 +56,7 @@ texture_filter = 1 tooltip_text = "toggle eraser" toggle_mode = true accessibility_name = "toggle eraser" -icon = ExtResource("6_art_kit_eraser") +icon = ExtResource("4_art_kit_eraser") expand_icon = true [node name="UndoButton" type="Button" parent="TopPanel/Top"] @@ -74,18 +71,24 @@ expand_icon = true [node name="HideGuideButton" type="Button" parent="TopPanel/Top"] unique_name_in_owner = true layout_mode = 2 +texture_filter = 1 tooltip_text = "toggle hide grid mode" toggle_mode = true button_group = SubResource("GuideActionGroup") -text = "◌" +accessibility_name = "toggle hide grid mode" +icon = ExtResource("7_grid_hidden") +expand_icon = true [node name="RestoreGuideButton" type="Button" parent="TopPanel/Top"] unique_name_in_owner = true layout_mode = 2 +texture_filter = 1 tooltip_text = "toggle show grid mode" toggle_mode = true button_group = SubResource("GuideActionGroup") -text = "▤" +accessibility_name = "toggle show grid mode" +icon = ExtResource("8_grid_restore") +expand_icon = true [node name="FinalizeGuideButton" type="Button" parent="TopPanel/Top"] unique_name_in_owner = true @@ -95,24 +98,24 @@ tooltip_text = "toggle finalize grid mode" toggle_mode = true button_group = SubResource("GuideActionGroup") accessibility_name = "toggle finalize grid mode" -icon = ExtResource("4_check_mark_light") +icon = ExtResource("9_grid_finish") expand_icon = true -[node name="CloseButton" type="Button" parent="TopPanel/Top"] +[node name="ModeButton" type="Button" parent="TopPanel/Top"] unique_name_in_owner = true layout_mode = 2 texture_filter = 1 -tooltip_text = "close art tools" -accessibility_name = "close art tools" -icon = ExtResource("5_x_light") +tooltip_text = "switch between grid and marker mode" +accessibility_name = "switch to grid mode" +icon = ExtResource("5_art_kit_marker") expand_icon = true [node name="ColorPanel" type="PanelContainer" parent="."] unique_name_in_owner = true layout_mode = 0 -offset_left = 534.0 +offset_left = 464.0 offset_top = 54.0 -offset_right = 580.0 +offset_right = 510.0 offset_bottom = 373.0 [node name="ColorList" type="VBoxContainer" parent="ColorPanel"]