diff --git a/README.md b/README.md index 39a1021..e0d5849 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr - Victory and defeat result overlay. - Dialogue portrait slot with officer default image paths, optional per-line overrides, and speaker-initial fallback. - Initial AI-generated photorealistic officer portraits for Cao Cao, Xiahou Dun, Xiahou Yuan, Cao Ren, Dian Wei, Guo Jia, and Zhang He. -- Initial AI-generated battlefield, transparent class unit, transparent generic-enemy unit, and item icon assets live under `art/backgrounds`, `art/units`, and `art/items`. +- Initial AI-generated battlefield, transparent class unit, transparent generic-enemy unit, and transparent item icon assets live under `art/backgrounds`, `art/units`, and `art/items`. - Battle HUD portrait thumbnails show the selected unit, or a hovered unit when nothing is selected. - Selected and hovered unit focus text shows combat role, movement type, current terrain move cost, terrain bonuses, core stats, and status effects. - Dialogue lines can place the portrait on the left or right side of an expanded visual-novel-style dialogue panel with progress and previous-line controls. @@ -135,7 +135,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr Campaign order and chapter ranges live at `res://data/campaign/campaign.json`. Scenarios live under `res://data/scenarios/`, with reusable officers, classes, items, skills, and terrain definitions under `res://data/defs/`. Generated visual assets live under `res://art/portraits/`, `res://art/units/`, `res://art/items/`, and `res://art/backgrounds/`. Placeholder BGM and SFX live under `res://audio/`. -Data files can be checked with `powershell -NoProfile -ExecutionPolicy Bypass -File tools\validate_data.ps1`. +Data files and item icon cutouts can be checked with `powershell -NoProfile -ExecutionPolicy Bypass -File tools\validate_data.ps1`. Godot 4.6 migration readiness can be checked with `powershell -NoProfile -ExecutionPolicy Bypass -File tools\check_godot46_readiness.ps1`. The migration checklist lives in `docs/GODOT_4_6_MIGRATION.md`. diff --git a/art/items/armor.png b/art/items/armor.png index d9c2f76..8abad39 100644 Binary files a/art/items/armor.png and b/art/items/armor.png differ diff --git a/art/items/axe.png b/art/items/axe.png index 20d0229..0526a81 100644 Binary files a/art/items/axe.png and b/art/items/axe.png differ diff --git a/art/items/bow.png b/art/items/bow.png index 4b780f9..8f3284e 100644 Binary files a/art/items/bow.png and b/art/items/bow.png differ diff --git a/art/items/panacea.png b/art/items/panacea.png index 66266df..d9c1449 100644 Binary files a/art/items/panacea.png and b/art/items/panacea.png differ diff --git a/art/items/spear.png b/art/items/spear.png index 635ba40..c301348 100644 Binary files a/art/items/spear.png and b/art/items/spear.png differ diff --git a/art/items/sword.png b/art/items/sword.png index 851e300..9a7adcf 100644 Binary files a/art/items/sword.png and b/art/items/sword.png differ diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index bca20d7..6fc8403 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -16,7 +16,7 @@ - `scripts/core/battle_state.gd`: Battle rules, unit state, turns, AI, and victory checks. - `scripts/scenes/battle_scene.gd`: Rendering, input handling, and HUD wiring. - `audio/bgm/*.wav` and `audio/sfx/*.wav`: Placeholder music loops and interface/action stingers. -- `art/portraits/*.png`, `art/units/*.png`, `art/units/enemies/*.png`, `art/items/*.png`, and `art/backgrounds/*.png`: Generated visual assets used by dialogue, battle HUD, transparent board-token cutouts, generic-enemy transparent cutouts, item buttons, and map backdrops. +- `art/portraits/*.png`, `art/units/*.png`, `art/units/enemies/*.png`, `art/items/*.png`, and `art/backgrounds/*.png`: Generated visual assets used by dialogue, battle HUD, transparent board-token cutouts, generic-enemy transparent cutouts, transparent item icon cutouts, and map backdrops. - `data/campaign/campaign.json`: Campaign chapter ranges, scenario order, and scenario paths. - `data/defs/*.json`: Officers, classes, terrain, items, and skills. - `data/scenarios/*.json`: Scenario definitions. diff --git a/docs/DATA_MODEL.md b/docs/DATA_MODEL.md index 0ccf74b..3502c0d 100644 --- a/docs/DATA_MODEL.md +++ b/docs/DATA_MODEL.md @@ -323,7 +323,7 @@ Items start as static bonuses and consumable effects. Equipment leveling should ``` Weapon `range` also uses `[min, max]`. When class and equipment both provide range, the runtime unit keeps the lowest minimum and highest maximum. Weapons can optionally define `effective_vs_move_types` plus `effective_bonus_damage` to add a small physical damage bonus during combat against targets with a matching runtime `move_type`; this is shown in attack forecasts, combat logs, shop stock, and equipment menus, but it does not mutate saved unit stats. The first examples are spear vs mounted, bow vs water, and axe vs foot. Accessories use `kind: "accessory"` with an `accessory_type` checked against the class `equipment_slots.accessory` list; `war_drum` and `imperial_seal` are the first branch-choice accessory rewards. Current equipment inventory is stackable stock only; unique equipment instances, durability, and enhancement are planned later. `war_axe` is a stronger axe reward for the Warrior/Bandit weapon family. The first named rewards are `yitian_sword`, `dragon_spear`, `wind_chaser_bow`, and `black_tortoise_robe`; their `rarity` tag is presentation-only and does not enforce uniqueness. -`icon` is an optional item UI image shown in shop, item, and equipment buttons when present. +`icon` is an optional item UI image shown in shop, item, and equipment buttons when present. Current item icons are transparent PNG cutouts under `res://art/items/`, and validation checks their alpha corners and sampled transparent background so inventory buttons stay clean over UI surfaces. Consumables use `kind: "consumable"` with effect records. `heal_hp` restores HP, `heal_mp` restores MP, `cure_status` removes active battle-only effects with a matching `status` from a valid target, and `cleanse_debuffs` removes only negative `stat_bonus` effects while leaving positive buffs intact; `Bean`, `Wine`, `Antidote`, and `Panacea` are the first examples. Item use consumes the acting unit's action and only commits to the save file on victory. Weapons, armor, and accessories can be granted through `rewards.items`; they are shown in inventory, can be equipped through the Equip menu before the unit moves or acts, and are not usable from the battle item menu. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 0a966d5..7bfe23f 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -134,7 +134,7 @@ ## Milestone 5: Presentation -- Portrait and art pipeline. Officer definitions can provide default portrait paths, classes can provide board `sprite` paths, items can provide `icon` paths, scenarios can provide map `background` paths, and the first generated officer portraits, unit sprites, item icons, and battlefield background are in `art/`. +- Portrait and art pipeline. Officer definitions can provide default portrait paths, classes can provide board `sprite` paths, items can provide transparent `icon` cutout paths, scenarios can provide map `background` paths, and the first generated officer portraits, unit sprites, item icons, and battlefield background are in `art/`. - Unit sprites and animations. Board movement now has a short slide animation, attacks request class-specific motion profiles for infantry, archers, cavalry, command units, and heavy warrior/bandit units, and board units show class sprite tokens, team rings, class badges, low-HP warning rings, and small status pips for active support, debuff, poison, seal, snare, and disarm effects, with HUD summaries showing remaining phases. - Battlefield presentation. Maps can draw a high-resolution background under translucent terrain overlays, with terrain-specific detail patterns, connected road segments, shoreline/castle/forest/hill edge blends, and generic-enemy token marks. - Battle HUD presentation. Selected and hovered units now summarize battlefield role, movement type, current terrain move cost, defense/avoid bonuses, core stats, status effects, and gear so class identity reads alongside the visual tokens. diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index c682d30..ff49c01 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -80,7 +80,9 @@ func _check_battle_visual_data(failures: Array[String]) -> void: if item.is_empty(): failures.append("missing expected item: %s" % item_id) continue - _check_image_path(failures, str(item.get("icon", "")), "item %s icon" % item_id) + var icon_path := str(item.get("icon", "")) + _check_image_path(failures, icon_path, "item %s icon" % item_id) + _check_alpha_cutout_path(failures, icon_path, "item %s icon cutout" % item_id) func _check_scene_texture_loading(failures: Array[String]) -> void: diff --git a/tools/validate_data.ps1 b/tools/validate_data.ps1 index 7e74d2e..5248d7d 100644 --- a/tools/validate_data.ps1 +++ b/tools/validate_data.ps1 @@ -877,7 +877,9 @@ function Check-Item-Effects() { $item = $itemProp.Value $kind = [string](Get-Prop $item "kind" "") if (Has-Prop $item "icon") { - Check-Portrait-Path (Get-Prop $item "icon" "") "Item $itemId icon" + $iconPath = [string](Get-Prop $item "icon" "") + Check-Portrait-Path $iconPath "Item $itemId icon" + Check-Item-Icon-Cutout $iconPath "Item $itemId icon" } if (-not $validItemKinds.Contains($kind)) { Fail "Item $itemId has unknown kind: $kind" @@ -1874,6 +1876,51 @@ function Check-Portrait-Image([string]$AbsolutePath, [string]$Portrait, [string] } } +function Check-Item-Icon-Cutout([string]$Icon, [string]$Context) { + $relativePath = $Icon.Substring("res://".Length) + $absolutePath = [System.IO.Path]::GetFullPath((Join-Path ([System.IO.Path]::GetFullPath($Root)) $relativePath)) + $bitmap = $null + try { + $bitmap = [System.Drawing.Bitmap]::FromFile($absolutePath) + if (-not [System.Drawing.Image]::IsAlphaPixelFormat($bitmap.PixelFormat)) { + Fail "$Context must be an alpha PNG cutout: $Icon" + } + $cornerPixels = @( + $bitmap.GetPixel(0, 0), + $bitmap.GetPixel($bitmap.Width - 1, 0), + $bitmap.GetPixel(0, $bitmap.Height - 1), + $bitmap.GetPixel($bitmap.Width - 1, $bitmap.Height - 1) + ) + foreach ($pixel in $cornerPixels) { + if ($pixel.A -gt 3) { + Fail "$Context must have transparent corners: $Icon" + } + } + $transparentSamples = 0 + $totalSamples = 0 + for ($y = 0; $y -lt $bitmap.Height; $y += 4) { + for ($x = 0; $x -lt $bitmap.Width; $x += 4) { + $totalSamples += 1 + if ($bitmap.GetPixel($x, $y).A -le 3) { + $transparentSamples += 1 + } + } + } + if ($transparentSamples -lt [int]($totalSamples * 0.20)) { + Fail "$Context must have substantial transparent background: $Icon ($transparentSamples/$totalSamples sampled pixels)" + } + } catch { + if ($_.Exception.Message -like "$Context*") { + throw + } + Fail "$Context could not inspect alpha cutout data: $Icon" + } finally { + if ($null -ne $bitmap) { + $bitmap.Dispose() + } + } +} + function Check-Post-Battle-Dialogue($Dialogue, [string]$ScenarioId) { if ($null -eq $Dialogue) { return