diff --git a/docs/plans/M52_CHIBI_HAND_DRAWN_MOBILE_DIRECTION.md b/docs/plans/M52_CHIBI_HAND_DRAWN_MOBILE_DIRECTION.md index 1b4733b2..41aa04a9 100644 --- a/docs/plans/M52_CHIBI_HAND_DRAWN_MOBILE_DIRECTION.md +++ b/docs/plans/M52_CHIBI_HAND_DRAWN_MOBILE_DIRECTION.md @@ -169,6 +169,15 @@ interim dashboard composition. The reusable page grammar is now explicit: 760x360, 844x390 and 1280x720 in Chinese and English. The detailed reusable rules live in `docs/runtime/M52_SHARED_UI_RULES.md`. +Market component asset pass (`working tree`, 2026-09-09): a transparent 2x2 +component sheet was generated through API Mart task +`task_01M22JS3QVF9TE1J2V9R9FSJ74` and sliced into title, recruit-card, ledger +and wood-button assets under `runtime/assets/ui_m52_market/v2/`. The first +NinePatch experiment was rejected because the source silhouettes contained +non-uniform transparent margins; the current preview uses whole-silhouette +TextureRect scaling while dedicated edge/center slices remain a follow-up. +The raw sheet, prompts and provider report are retained for provenance. + ### M52-C — Complete one playable loop and owner tryout - Extend the same visual language through Recruit Review, Party, Battle and diff --git a/docs/runtime/m52_market_ui_components_prompt.txt b/docs/runtime/m52_market_ui_components_prompt.txt new file mode 100644 index 00000000..c2fc174d --- /dev/null +++ b/docs/runtime/m52_market_ui_components_prompt.txt @@ -0,0 +1,9 @@ +Use case: ui-mockup +Asset type: reusable game UI component sheet for a Godot Japanese 2D hand-drawn fantasy RPG +Primary request: Create ONE REGULAR 2x2 GRID with FOUR EQUAL CELLS and clear fixed boundaries. Produce four blank, text-free, transparent-ready UI components that match one another as a single authored family. Warm Q-style Japanese chibi fantasy, hand-inked contours, cel-painted paper and wood, brass corner fittings, soft painted shadows, crisp at play size. No characters, no logos, no letters, no numbers, no fake glyphs, no watermark. Use a solid bright green #00FF00 background across the entire sheet. Subjects must stay inside their cells with generous padding and must not cross cell boundaries. +TOP-LEFT: ornate horizontal market title plaque, cream parchment center, carved warm wood frame, teal cloth ribbon, brass corner studs, soft shadow, wide horizontal shape. +TOP-RIGHT: vertical recruit parchment card, hanging rope loops at the top, cream paper with warm wood trim, one empty colored profession ribbon slot and one empty circular seal slot, blank interior for runtime portrait and text. +BOTTOM-LEFT: tall adventurer ledger panel, warm parchment book-board with wood trim, empty portrait window, empty text bands, large empty action-button area at the bottom, no text. +BOTTOM-RIGHT: horizontal wood command button, chunky carved wood frame, brass studs, teal inset accent, soft shadow, empty center for runtime label and icon. +Composition: each cell is an isolated production asset with clean edges, front-facing orthographic UI view, consistent light from upper-left, readable material separation, no perspective distortion. +Avoid: UI dashboard look, dark navy rectangles, generic web cards, pixel art, hard pixel edges, thin hairline borders, gradients without material, baked text, symbols, people, scenery, overlapping cells. diff --git a/docs/runtime/m52_market_ui_components_transparent_prompt.txt b/docs/runtime/m52_market_ui_components_transparent_prompt.txt new file mode 100644 index 00000000..95f14ab7 --- /dev/null +++ b/docs/runtime/m52_market_ui_components_transparent_prompt.txt @@ -0,0 +1,9 @@ +Use case: ui-mockup +Asset type: reusable game UI component sheet for a Godot Japanese 2D hand-drawn fantasy RPG +Primary request: Create ONE REGULAR 2x2 GRID with FOUR EQUAL CELLS and clear fixed boundaries. Produce four blank, text-free, production-ready UI components in one coherent Q-style Japanese chibi fantasy game UI family. Output a true transparent PNG with real alpha in every empty area; absolutely no green screen, no black background, no colored matte. No characters, no logos, no letters, no numbers, no fake glyphs, no watermark. Subjects must stay inside their cells with generous padding and must not cross cell boundaries. +TOP-LEFT: ornate horizontal market title plaque, cream parchment center, carved warm wood frame, teal cloth ribbon, brass corner studs, soft shadow, wide horizontal shape. +TOP-RIGHT: vertical recruit parchment card, hanging rope loops at the top, cream paper with warm wood trim, one empty colored profession ribbon slot and one empty circular seal slot, blank interior for runtime portrait and text. +BOTTOM-LEFT: tall adventurer ledger panel, warm parchment book-board with wood trim, empty portrait window, empty text bands, large empty teal action-button area at the bottom, no text. +BOTTOM-RIGHT: horizontal wood command button, chunky carved wood frame, brass studs, teal inset accent, soft shadow, empty center for runtime label and icon. +Composition: each cell is an isolated production asset with clean anti-aliased alpha edges, front-facing orthographic UI view, consistent light from upper-left, readable material separation, no perspective distortion. +Avoid: UI dashboard look, dark navy rectangles, generic web cards, pixel art, hard pixel edges, thin hairline borders, gradients without material, baked text, symbols, people, scenery, overlapping cells, any background color. diff --git a/docs/runtime/m52_market_ui_components_transparent_report.json b/docs/runtime/m52_market_ui_components_transparent_report.json new file mode 100644 index 00000000..dbc69934 --- /dev/null +++ b/docs/runtime/m52_market_ui_components_transparent_report.json @@ -0,0 +1,15 @@ +{ + "mode": "execute", + "model": "gpt-image-2", + "size": "1:1", + "resolution": "2k", + "output": "/tmp/m52_market_ui_components_transparent_raw.png", + "local_references": [], + "remote_reference_count": 0, + "prompt_chars": 1829, + "task_id": "task_01M22JS3QVF9TE1J2V9R9FSJ74", + "status": "completed", + "bytes": 3876599, + "cost": 0.014, + "credits_cost": 0.14 +} diff --git a/runtime/assets/ui_m52_market/v2/ledger_panel.png b/runtime/assets/ui_m52_market/v2/ledger_panel.png new file mode 100644 index 00000000..c3474836 Binary files /dev/null and b/runtime/assets/ui_m52_market/v2/ledger_panel.png differ diff --git a/runtime/assets/ui_m52_market/v2/recruit_card.png b/runtime/assets/ui_m52_market/v2/recruit_card.png new file mode 100644 index 00000000..f3b0bd4f Binary files /dev/null and b/runtime/assets/ui_m52_market/v2/recruit_card.png differ diff --git a/runtime/assets/ui_m52_market/v2/title_plaque.png b/runtime/assets/ui_m52_market/v2/title_plaque.png new file mode 100644 index 00000000..5a4bd46a Binary files /dev/null and b/runtime/assets/ui_m52_market/v2/title_plaque.png differ diff --git a/runtime/assets/ui_m52_market/v2/wood_button.png b/runtime/assets/ui_m52_market/v2/wood_button.png new file mode 100644 index 00000000..f63b903f Binary files /dev/null and b/runtime/assets/ui_m52_market/v2/wood_button.png differ diff --git a/runtime/assets/ui_m52_market_raw/m52_market_ui_components_transparent_raw.png b/runtime/assets/ui_m52_market_raw/m52_market_ui_components_transparent_raw.png new file mode 100644 index 00000000..bb391acf Binary files /dev/null and b/runtime/assets/ui_m52_market_raw/m52_market_ui_components_transparent_raw.png differ diff --git a/runtime/entry/views/market_page_view.gd b/runtime/entry/views/market_page_view.gd index 71e7d576..2926828b 100644 --- a/runtime/entry/views/market_page_view.gd +++ b/runtime/entry/views/market_page_view.gd @@ -829,7 +829,7 @@ func _render_m52_market_restore(data: Dictionary) -> void: var title_plate := Control.new() title_plate.name = "MarketTitlePlaque" _market_place(title_plate, Rect2(0.045, 0.035, 0.34, 0.18)) - var title_chrome := MarketChrome.new().configure("title") + var title_chrome := _m52_texture_surface(preload("res://assets/ui_m52_market/v2/title_plaque.png"), "TitleChrome", Vector4(56, 46, 56, 46)) title_chrome.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) title_plate.add_child(title_chrome) var title := UIFactory.label(_tr("市场", "MARKET", data), "title", 1.0, MarketChrome.INK) @@ -931,7 +931,7 @@ func _render_m52_market_restore(data: Dictionary) -> void: ledger.mouse_filter = Control.MOUSE_FILTER_IGNORE ledger.z_index = 12 ledger.add_theme_stylebox_override("panel", StyleBoxEmpty.new()) - var ledger_chrome := MarketChrome.new().configure("ledger") + var ledger_chrome := _m52_texture_surface(preload("res://assets/ui_m52_market/v2/ledger_panel.png"), "LedgerChrome", Vector4(44, 44, 44, 44)) ledger_chrome.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) ledger.add_child(ledger_chrome) page.add_child(ledger) @@ -959,19 +959,19 @@ func _render_m52_market_restore(data: Dictionary) -> void: portrait.expand_mode = TextureRect.EXPAND_IGNORE_SIZE portrait.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED portrait.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS - _market_place(portrait, Rect2(0.19, 0.18, 0.62, 0.28)) + _market_place(portrait, Rect2(0.10, 0.24, 0.38, 0.32)) portrait.mouse_filter = Control.MOUSE_FILTER_IGNORE ledger.add_child(portrait) var detail_title := UIFactory.label(selected_title if not selected_title.is_empty() else _tr("候选人名册", "ADVENTURER LEDGER", data), "label", 1.0, MarketChrome.INK) detail_title.name = "MarketCandidateName" - _market_place(detail_title, Rect2(0.10, 0.47, 0.80, 0.08)) + _market_place(detail_title, Rect2(0.50, 0.26, 0.40, 0.10)) detail_title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER detail_title.vertical_alignment = VERTICAL_ALIGNMENT_CENTER detail_title.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS ledger.add_child(detail_title) var detail := UIFactory.label(selected_detail, "micro", 1.0, MarketChrome.INK_SOFT) detail.name = "MarketNoSelection" if selected_offer_id.is_empty() else "M50CommissionDetail" - _market_place(detail, Rect2(0.12, 0.56, 0.76, 0.12)) + _market_place(detail, Rect2(0.50, 0.38, 0.40, 0.22)) detail.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER detail.vertical_alignment = VERTICAL_ALIGNMENT_CENTER detail.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART @@ -979,11 +979,11 @@ func _render_m52_market_restore(data: Dictionary) -> void: if not selected_price.is_empty(): var price := UIFactory.label(selected_price, "label", 1.0, MarketChrome.INK) price.name = "MarketCandidatePrice" - _market_place(price, Rect2(0.12, 0.69, 0.76, 0.06)) + _market_place(price, Rect2(0.50, 0.63, 0.40, 0.07)) price.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER price.vertical_alignment = VERTICAL_ALIGNMENT_CENTER ledger.add_child(price) - var review := _m52_action_button("MarketReviewRecruit", _tr("查看详情", "VIEW DETAILS", data), Rect2(0.12, 0.80, 0.76, 0.14), "recruit_review", selected_offer_id.is_empty() or selected_kind != "recruit", data) + var review := _m52_action_button("MarketReviewRecruit", _tr("查看详情", "VIEW DETAILS", data), Rect2(0.10, 0.80, 0.80, 0.14), "recruit_review", selected_offer_id.is_empty() or selected_kind != "recruit", data) review.z_index = 20 ledger.add_child(review) @@ -1013,6 +1013,20 @@ func _render_m52_market_restore(data: Dictionary) -> void: page.add_child(category_button) +func _m52_texture_surface(texture: Texture2D, node_name: String, margins: Vector4) -> TextureRect: + var surface := TextureRect.new() + surface.name = node_name + surface.texture = texture + # The generated source is a transparent component atlas crop. Keep the + # whole authored silhouette together while the next pass produces dedicated + # nine-patch center/edge slices. + surface.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + surface.stretch_mode = TextureRect.STRETCH_SCALE + surface.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS + surface.mouse_filter = Control.MOUSE_FILTER_IGNORE + return surface + + func _m52_resource_badge(node_name: String, icon_kind: String, caption: String, value: String, area: Rect2) -> Control: var badge := Control.new() badge.name = node_name @@ -1045,7 +1059,7 @@ func _m52_recruit_card(index: int, offer: Dictionary, area: Rect2, selected_kind for state in ["normal", "hover", "pressed", "disabled", "focus", "hover_pressed"]: card.add_theme_stylebox_override(state, StyleBoxEmpty.new()) var accent := "blue" if index == 0 else "red" if index == 1 else "gold" - var chrome := MarketChrome.new().configure("card", accent) + var chrome := _m52_texture_surface(preload("res://assets/ui_m52_market/v2/recruit_card.png"), "CardChrome", Vector4(38, 38, 38, 38)) chrome.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) card.add_child(chrome) var ribbon := UIFactory.label(String(p.get("profession_name", "")), "micro", 1.0, MarketChrome.PAPER_LIGHT) @@ -1111,7 +1125,7 @@ func _m52_wood_button(node_name: String, text_value: String, area: Rect2, icon_k _market_place(button, area) for state in ["normal", "hover", "pressed", "disabled", "focus", "hover_pressed"]: button.add_theme_stylebox_override(state, StyleBoxEmpty.new()) - var chrome := MarketChrome.new().configure("wood_button") + var chrome := _m52_texture_surface(preload("res://assets/ui_m52_market/v2/wood_button.png"), "WoodChrome", Vector4(46, 34, 46, 34)) chrome.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) button.add_child(chrome) var icon := UIFactory.attach_icon(button, icon_kind, "CommandIcon", "light", Rect2(0.05, 0.20, 0.18, 0.60))