Add Art Kit painting upgrades

This commit is contained in:
Alexander Sellite 2026-08-02 00:45:07 -04:00
parent 0edbfabdb1
commit 1406a0abc7
34 changed files with 1530 additions and 165 deletions

View file

@ -0,0 +1,97 @@
extends SceneTree
const MainScene: PackedScene = preload("res://main/main.tscn")
const TEST_PORT: int = 18136
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
root.size = Vector2i(1280, 720)
var main: Node = MainScene.instantiate()
root.add_child(main)
for _frame: int in 4:
await process_frame
if not bool(main.get("_application_initialized")):
main.call("_activate_selected_data_path", "", true)
for _frame: int in 8:
await process_frame
assert(bool(main.get("_application_initialized")))
assert(bool(main.call("_prepare_private_host")))
var save_manager := main.get("_save_manager") as PlayerSaveManager
assert(save_manager.initialize_new_game())
main.call("_enter_gameplay")
await physics_frame
await physics_frame
var player := main.get("_player") as Player
var service := main.get_node(
"%NetworkSurfaceDrawingService"
) as NetworkSurfaceDrawingService
var game_ui := main.get_node("%GameUI") as GameUI
var toolbar := game_ui.get_node(
"%SurfaceDrawingToolbar"
) as SurfaceDrawingToolbar
assert(player != null and service != null and toolbar != null)
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())
var paint_key := InputEventKey.new()
paint_key.physical_keycode = KEY_P
paint_key.pressed = true
assert(service.handle_input(paint_key, true))
await process_frame
assert(service.is_active() and service.is_placement_mode())
assert(toolbar.visible)
assert(toolbar.position.is_equal_approx(Vector2(18.0, 18.0)))
assert(toolbar.get_global_rect().end.x <= 1280.0)
assert(toolbar.get_global_rect().end.y <= 720.0)
var brush_option := toolbar.get_node("%BrushOption") as OptionButton
var grid_option := toolbar.get_node("%GridOption") as OptionButton
assert(brush_option.item_count == 4)
assert(grid_option.item_count == 4)
assert(not brush_option.get_popup().is_item_disabled(0))
assert(brush_option.get_popup().is_item_disabled(1))
assert(not grid_option.get_popup().is_item_disabled(0))
assert(grid_option.get_popup().is_item_disabled(1))
var color_buttons: Dictionary = toolbar.get("_color_buttons")
assert(color_buttons.size() == SurfaceDrawingPalette.COLORS.size())
assert(not (color_buttons[&"chalk_white"] as Button).disabled)
assert((color_buttons[&"ocean_teal"] as Button).disabled)
for product_id: StringName in [
&"marker_ocean_teal", &"brush_4x", &"grid_128x",
]:
assert(player.art_unlocks.unlock_product(product_id))
await process_frame
assert(not (color_buttons[&"ocean_teal"] as Button).disabled)
assert(not brush_option.get_popup().is_item_disabled(3))
assert(not grid_option.get_popup().is_item_disabled(3))
assert(service.set_color_id(&"ocean_teal"))
assert(service.set_brush_size(4))
assert(service.set_grid_size(128))
assert(service.get_color_id() == &"ocean_teal")
assert(service.get_brush_size() == 4)
assert(service.get_grid_size() == 128)
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))
var fishing_spot := main.get_node("%FishingSpot") as FishingSpot
fishing_spot.art_ui_toggle_requested.emit()
await process_frame
assert(service.is_active() and toolbar.visible)
assert(service.get_grid_size() == 128)
print("Art tools validation: PASS")
var session := main.get_node("%NetworkSession") as NetworkSession
session.disconnect_session("")
main.queue_free()
await process_frame
quit()

View file

@ -0,0 +1 @@
uid://dr671f64t58wc

View file

@ -72,6 +72,7 @@ func _run() -> void:
assert(session.set_host_open(false))
await _test_host_shop_purchase(main, player, shop_service)
await _test_host_art_shop_purchase(main, player, shop_service)
await _test_fishing_shop_sale_ui(
main, player, catalog, sale_service, reservations
)
@ -205,6 +206,25 @@ func _run_multiplayer_client() -> void:
print("Client shop result: ", _shop_result)
assert(not _shop_result.is_empty() and bool(_shop_result[1]))
assert(not shop_service.is_local_purchase_pending())
var required_art_balance: int = (
ArtShopStock.ART_KIT_PRICE + ArtShopStock.UPGRADE_PRICE
)
if player.wallet.get_balance() < required_art_balance:
assert(player.wallet.credit(
required_art_balance - player.wallet.get_balance()
))
_shop_result.clear()
assert(not shop_service.request_art_kit().is_empty())
while _shop_result.is_empty():
await process_frame
assert(bool(_shop_result[1]))
_shop_result.clear()
assert(not shop_service.request_art_upgrade(&"grid_32x").is_empty())
while _shop_result.is_empty():
await process_frame
assert(bool(_shop_result[1]))
assert(player.bag.owns_item(ArtShopStock.ART_KIT_ITEM_ID))
assert(player.art_unlocks.is_grid_size_unlocked(32))
print("Economy multiplayer client validation: PASS")
session.disconnect_session("Economy client validation complete.")
main.queue_free()
@ -450,6 +470,42 @@ func _test_host_shop_purchase(
assert(not shop_service.is_local_purchase_pending())
func _test_host_art_shop_purchase(
main: Node,
player: Player,
shop_service: NetworkShopService,
) -> void:
var interaction := main.get("_shop_interaction") as FishingShopInteraction
assert(interaction != null)
player.global_position = interaction.global_position
for _frame: int in 4:
await physics_frame
var required_balance: int = (
ArtShopStock.ART_KIT_PRICE + ArtShopStock.UPGRADE_PRICE * 3
)
if player.wallet.get_balance() < required_balance:
assert(player.wallet.credit(required_balance - player.wallet.get_balance()))
_shop_result.clear()
assert(not shop_service.request_art_kit().is_empty())
assert(not _shop_result.is_empty() and bool(_shop_result[1]))
assert(player.bag.owns_item(ArtShopStock.ART_KIT_ITEM_ID))
var item_catalog := main.get("item_catalog") as ItemCatalog
assert(item_catalog != null)
var art_item: ItemData = item_catalog.get_item_by_id(
ArtShopStock.ART_KIT_ITEM_ID
)
assert(art_item != null and art_item.hotbar_allowed and art_item.equippable)
assert(player.hotbar.assign_item(0, ArtShopStock.ART_KIT_ITEM_ID))
for product_id: StringName in [
&"marker_ocean_teal", &"brush_2x", &"grid_32x",
]:
_shop_result.clear()
assert(not shop_service.request_art_upgrade(product_id).is_empty())
assert(not _shop_result.is_empty() and bool(_shop_result[1]))
assert(player.art_unlocks.owns_product(product_id))
assert(not shop_service.is_local_purchase_pending())
func _test_fishing_shop_sale_ui(
main: Node,
player: Player,
@ -499,6 +555,11 @@ func _test_fishing_shop_sale_ui(
var buy_mode := shop.get_node("%BuyModeButton") as Button
var sell_mode := shop.get_node("%SellModeButton") as Button
assert(buy_mode.visible and sell_mode.visible)
var stock_sections: Array[String] = []
for child: Node in shop.get_node("%SuppliesList").get_children():
if child is Label:
stock_sections.append((child as Label).text)
assert(stock_sections == ["supplies", "art kit", "markers", "brushes", "grids"])
await _activate_focused_button(sell_mode, ui_viewport)
await process_frame
assert(shop.visible and not player_menu.visible)

View file

@ -30,6 +30,9 @@ func _run_host() -> void:
main.call("_enter_gameplay")
await physics_frame
await physics_frame
var player := main.get("_player") as Player
assert(player.bag.add_item(&"art_kit", 1))
assert(player.art_unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK))
var service := main.get_node(
"%NetworkSurfaceDrawingService"
@ -133,6 +136,9 @@ func _run_client() -> void:
assert(session.supports_server_capability(
SurfaceDrawingProtocol.CAPABILITY
))
var player := main.get("_player") as Player
assert(player.bag.add_item(&"art_kit", 1))
assert(player.art_unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK))
var service := main.get_node(
"%NetworkSurfaceDrawingService"

View file

@ -28,6 +28,8 @@ func _run() -> void:
await physics_frame
var player := main.get("_player") as Player
assert(player.bag.add_item(&"art_kit", 1))
assert(player.art_unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK))
var service := main.get_node(
"%NetworkSurfaceDrawingService"
) as NetworkSurfaceDrawingService
@ -119,19 +121,19 @@ func _validate_marker_controls(
var prior_mouse_mode: Input.MouseMode = Input.mouse_mode
service.activate()
assert(service.is_active())
assert(not service.is_placement_mode())
assert(service.is_placement_mode())
if DisplayServer.get_name() != "headless":
assert(Input.mouse_mode == Input.MOUSE_MODE_CAPTURED)
assert(Input.mouse_mode == Input.MOUSE_MODE_VISIBLE)
var placement_key := InputEventKey.new()
placement_key.physical_keycode = KEY_R
placement_key.pressed = true
assert(service.handle_input(placement_key, true))
assert(service.is_placement_mode())
assert(not service.is_placement_mode())
var pointer_before: Vector2 = service.get_pointer_screen_position()
var pointer_motion := InputEventMouseMotion.new()
pointer_motion.screen_relative = Vector2(30.0, -12.0)
pointer_motion.position = pointer_before + Vector2(30.0, -12.0)
assert(service.handle_input(pointer_motion, true))
assert(service.get_pointer_screen_position() != pointer_before)
var zoom_event := InputEventMouseButton.new()
@ -156,6 +158,6 @@ func _validate_marker_controls(
assert(not service.handle_input(camera_release, true))
assert(service.handle_input(placement_key, true))
assert(not service.is_placement_mode())
assert(service.is_placement_mode())
service.deactivate()
assert(Input.mouse_mode == prior_mouse_mode)

View file

@ -14,6 +14,7 @@ func _initialize() -> void:
func _run() -> void:
_validate_palette()
_validate_art_unlocks()
_validate_protocol_bounds()
_validate_grid_snapping()
await _validate_canvas_geometry_and_collaboration()
@ -37,13 +38,14 @@ func _validate_palette() -> void:
func _validate_protocol_bounds() -> void:
assert(SurfaceDrawingProtocol.GRID_WIDTH == 32)
assert(SurfaceDrawingProtocol.GRID_HEIGHT == 32)
assert(SurfaceDrawingProtocol.GRID_WIDTH == 16)
assert(SurfaceDrawingProtocol.GRID_HEIGHT == 16)
assert(SurfaceDrawingProtocol.GRID_SIZES == [16, 32, 64, 128])
assert(is_equal_approx(SurfaceDrawingProtocol.CELL_SIZE, 0.075))
assert(is_equal_approx(
float(SurfaceDrawingProtocol.GRID_WIDTH)
* SurfaceDrawingProtocol.CELL_SIZE,
2.4,
1.2,
))
var canvas_request: Dictionary = {
"request_id": "canvas-1",
@ -64,15 +66,28 @@ func _validate_protocol_bounds() -> void:
"session_id": "session",
"canvas_id": "canvas-1",
"stroke_id": "stroke-1",
"brush_size": 1,
"edits": [{"x": 0, "y": 0, "color_id": "ocean_teal"}],
}
assert(SurfaceDrawingProtocol.validate_edit_request(edit_request))
edit_request["edits"] = [{
"x": SurfaceDrawingProtocol.GRID_WIDTH,
"x": SurfaceDrawingProtocol.MAX_GRID_SIZE,
"y": 0,
"color_id": "ocean_teal",
}]
assert(not SurfaceDrawingProtocol.validate_edit_request(edit_request))
edit_request["edits"] = [{"x": 0, "y": 0, "color_id": "ocean_teal"}]
edit_request["brush_size"] = 5
assert(not SurfaceDrawingProtocol.validate_edit_request(edit_request))
for grid_size: int in SurfaceDrawingProtocol.GRID_SIZES:
var sized_request: Dictionary = canvas_request.duplicate(true)
sized_request["width"] = grid_size
sized_request["height"] = grid_size
assert(SurfaceDrawingProtocol.validate_canvas_request(sized_request))
var unsupported_size: Dictionary = canvas_request.duplicate(true)
unsupported_size["width"] = 48
unsupported_size["height"] = 48
assert(not SurfaceDrawingProtocol.validate_canvas_request(unsupported_size))
var guide_request: Dictionary = {
"request_id": "guide-1",
"session_id": "session",
@ -91,6 +106,20 @@ func _validate_protocol_bounds() -> void:
assert(SurfaceDrawingProtocol.validate_undo_request(undo_request))
func _validate_art_unlocks() -> void:
var unlocks := PlayerArtUnlocks.new()
assert(unlocks.get_unlocked_color_ids() == [&"chalk_white"])
assert(unlocks.get_unlocked_brush_sizes() == [1])
assert(unlocks.get_unlocked_grid_sizes() == [16])
assert(unlocks.unlock_product(&"marker_ocean_teal"))
assert(unlocks.unlock_product(&"brush_4x"))
assert(unlocks.unlock_product(&"grid_128x"))
assert(unlocks.is_color_unlocked(&"ocean_teal"))
assert(unlocks.is_brush_size_unlocked(4))
assert(unlocks.is_grid_size_unlocked(128))
assert(not unlocks.restore_mask(PlayerArtUnlocks.ALL_UNLOCK_MASK + 1))
func _validate_grid_snapping() -> void:
var anchor: Dictionary = {
"session_id": "session",
@ -109,16 +138,16 @@ func _validate_grid_snapping() -> void:
"cells": [],
}
var snapped: Dictionary = SurfaceDrawingPlacement.resolve(
Vector3(2.28, 0.02, 0.08),
Vector3(1.08, 0.02, 0.08),
Vector3.UP,
Vector3.RIGHT,
[anchor],
)
assert(bool(snapped["snapped"]))
var snapped_origin: Vector3 = snapped["origin"]
assert(snapped_origin.is_equal_approx(Vector3(2.4, 0.0, 0.0)))
assert(snapped_origin.is_equal_approx(Vector3(1.2, 0.0, 0.0)))
var unsnapped: Dictionary = SurfaceDrawingPlacement.resolve(
Vector3(1.2, 0.0, 0.0),
Vector3(0.6, 0.0, 0.0),
Vector3.UP,
Vector3.RIGHT,
[anchor],