Revise Art Kit workflow and refresh fox patterns

This commit is contained in:
Alexander Sellite 2026-08-13 15:24:23 -04:00
parent 117fdbfdaa
commit c06d5714c0
21 changed files with 363 additions and 141 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 945 B

After

Width:  |  Height:  |  Size: 945 B

Before After
Before After

View file

@ -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(

View file

@ -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()

View file

@ -259,8 +259,7 @@ 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:
player_menu.close_menu(PlayerMenu.CloseReason.SESSION_END)
await process_frame
game_ui.call("_on_quick_action_selected", &"hud")
await process_frame
@ -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(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(
eraser_button.icon.resource_path.ends_with(
"/art/art_kit_eraser.png"
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

View file

@ -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(

View file

@ -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)

View file

@ -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
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
if _surface_drawing.is_active():
_surface_drawing.deactivate()
elif _surface_drawing.can_activate():
_surface_drawing.activate(_drawing_pointer_window_position())
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():
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 _surface_drawing.is_active():
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()

View file

@ -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"

View file

@ -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"]