Improve remaining low-detail weapon icons

This commit is contained in:
2026-07-10 00:38:42 +09:00
parent 72253a44d0
commit 7ff5baa12c
5 changed files with 182 additions and 32 deletions

View File

@@ -30,22 +30,24 @@ Generated by `scripts/audit-equipment-icon-quality.mjs`.
- The previous shared armor and robe silhouettes now use item-specific helper shapes with stronger outer contours and material cues. - The previous shared armor and robe silhouettes now use item-specific helper shapes with stronger outer contours and material cues.
- accessory charms and tokens: `bravery-token`, `mountain-guide-scroll`, `peach-charm`, `war-manual`, `wind-quiver`, `grain-pouch`, `yellow-scarf-charm` - accessory charms and tokens: `bravery-token`, `mountain-guide-scroll`, `peach-charm`, `war-manual`, `wind-quiver`, `grain-pouch`, `yellow-scarf-charm`
- Accessory icons now use distinct badge, scroll, charm, pouch, manual, and quiver silhouettes at small camp UI sizes. - Accessory icons now use distinct badge, scroll, charm, pouch, manual, and quiver silhouettes at small camp UI sizes.
- remaining low-detail weapon icons: `leader-axe`, `training-sword`, `yellow-turban-saber`
- The final low-detail common weapons now use dedicated axe, training sword, and Yellow Turban saber silhouettes.
## Next Improvement Target ## Next Improvement Target
- Target: remaining low-detail equipment icons - Target: no flagged equipment icon batch
- Item ids: `leader-axe`, `training-sword`, `yellow-turban-saber` - Item ids: none currently flagged
- Reason: All named batches are complete; remaining work should be selected from the low-detail inventory rows. - Reason: No generated equipment icon currently has a low-detail or shared-helper audit concern; choose the next visual asset pass from in-game QA.
## Highest-Risk Shared Silhouette Groups ## Highest-Risk Shared Silhouette Groups
| score | signature | slots | ranks | items | | score | signature | slots | ranks | items |
| ---: | --- | --- | --- | --- | | ---: | --- | --- | --- | --- |
| 6 | `drawAxeIcon` | weapon | common | `leader-axe` |
| 6 | `drawSaberIcon` | weapon | common | `yellow-turban-saber` |
| 6 | `drawSwordIcon` | weapon | common | `training-sword` |
| 5 | `drawFanIcon` | weapon | treasure | `white-feather-fan` | | 5 | `drawFanIcon` | weapon | treasure | `white-feather-fan` |
| 5 | `drawGreenDragonGlaiveIcon` | weapon | treasure | `green-dragon-glaive` | | 5 | `drawGreenDragonGlaiveIcon` | weapon | treasure | `green-dragon-glaive` |
| 5 | `drawSerpentSpearIcon` | weapon | treasure | `serpent-spear` |
| 5 | `drawSkyPiercerHalberdIcon` | weapon | treasure | `sky-piercer-halberd` |
| 5 | `drawSwordIcon+drawSwordIcon` | weapon | treasure | `twin-oath-blades` |
## Full Item Inventory ## Full Item Inventory
@@ -57,11 +59,11 @@ Generated by `scripts/audit-equipment-icon-quality.mjs`.
| `twin-oath-blades` | weapon | treasure | `drawSwordIcon`<br>`drawSwordIcon` | 2 | 32 | 4 | none | | `twin-oath-blades` | weapon | treasure | `drawSwordIcon`<br>`drawSwordIcon` | 2 | 32 | 4 | none |
| `white-feather-fan` | weapon | treasure | `drawFanIcon` | 1 | 19 | 3 | none | | `white-feather-fan` | weapon | treasure | `drawFanIcon` | 1 | 19 | 3 | none |
| `iron-spear` | weapon | common | `drawIronSpearIcon` | 1 | 26 | 7 | none | | `iron-spear` | weapon | common | `drawIronSpearIcon` | 1 | 26 | 7 | none |
| `leader-axe` | weapon | common | `drawAxeIcon` | 1 | 7 | 2 | low primitive detail | | `leader-axe` | weapon | common | `drawLeaderAxeIcon` | 1 | 26 | 8 | none |
| `short-bow` | weapon | common | `drawBowIcon` | 1 | 18 | 3 | none | | `short-bow` | weapon | common | `drawBowIcon` | 1 | 18 | 3 | none |
| `training-sword` | weapon | common | `drawSwordIcon` | 1 | 16 | 4 | low primitive detail | | `training-sword` | weapon | common | `drawTrainingSwordIcon` | 1 | 31 | 7 | none |
| `western-cavalry-spear` | weapon | common | `drawWesternCavalrySpearIcon` | 1 | 27 | 7 | none | | `western-cavalry-spear` | weapon | common | `drawWesternCavalrySpearIcon` | 1 | 27 | 7 | none |
| `yellow-turban-saber` | weapon | common | `drawSaberIcon` | 1 | 12 | 2 | low primitive detail | | `yellow-turban-saber` | weapon | common | `drawYellowTurbanSaberIcon` | 1 | 35 | 4 | none |
| `oath-robe` | armor | treasure | `drawOathRobeIcon` | 1 | 25 | 6 | none | | `oath-robe` | armor | treasure | `drawOathRobeIcon` | 1 | 25 | 6 | none |
| `reinforced-lamellar` | armor | treasure | `drawReinforcedLamellarIcon` | 1 | 24 | 5 | none | | `reinforced-lamellar` | armor | treasure | `drawReinforcedLamellarIcon` | 1 | 24 | 5 | none |
| `cloth-armor` | armor | common | `drawClothArmorIcon` | 1 | 24 | 6 | none | | `cloth-armor` | armor | common | `drawClothArmorIcon` | 1 | 24 | 6 | none |
@@ -77,8 +79,7 @@ Generated by `scripts/audit-equipment-icon-quality.mjs`.
## Recommended Next Batch Definition ## Recommended Next Batch Definition
- Scope: improve remaining low-detail equipment icons while keeping `item-${itemId}` texture keys stable. - Scope: no generated equipment icon is currently below the audit threshold.
- Item ids: `leader-axe`, `training-sword`, `yellow-turban-saber` - Item ids: none currently flagged
- Success criteria: Clear remaining low-detail concerns without changing stable texture keys. - Success criteria: Keep the generated equipment icon audit green while selecting the next art target from runtime screenshots.
- Keep the existing 64px texture size unless a UI-wide equipment icon pass is planned. - Suggested next area: map/background or unit sprite assets that still look low-resolution in gameplay.
- Add before/after contact sheets and verify the sortie equipment list and equipment swap panel in a desktop browser.

View File

@@ -0,0 +1,26 @@
# Remaining Weapon Equipment Icon Pass
Generated after the final low-detail weapon icon rework in `src/game/scenes/BootScene.ts`.
## Scope
- `leader-axe`
- `training-sword`
- `yellow-turban-saber`
## Before and After
| item id | previous helper | current helper | previous primitives | current primitives | audit concerns |
| --- | --- | --- | ---: | ---: | --- |
| `leader-axe` | `drawAxeIcon` | `drawLeaderAxeIcon` | 7 | 26 | none |
| `training-sword` | `drawSwordIcon` | `drawTrainingSwordIcon` | 16 | 31 | none |
| `yellow-turban-saber` | `drawSaberIcon` | `drawYellowTurbanSaberIcon` | 12 | 35 | none |
## Browser QA
![Sortie prep remaining weapon icon QA](./equipment-icon-remaining-weapons-sortie-prep.png)
- URL checked: `http://127.0.0.1:4201/heros_web/?debug&debugSortiePrep&renderer=canvas`
- Result: sortie prep opened on a desktop 1280x720 canvas and rendered the generated weapon textures in roster, slot, and detail surfaces.
- Browser console warnings/errors: none observed.
- Follow-up target from the updated audit: no generated equipment icon batch is currently flagged; select the next art pass from runtime screenshots.

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@@ -315,6 +315,7 @@ function buildRecommendations(itemRows, groupedRows) {
]; ];
const armorTargetIds = ['oath-robe', 'reinforced-lamellar', 'cloth-armor', 'lamellar-armor', 'rebel-vest']; const armorTargetIds = ['oath-robe', 'reinforced-lamellar', 'cloth-armor', 'lamellar-armor', 'rebel-vest'];
const accessoryIds = itemRows.filter((row) => row.slot === 'accessory').map((row) => row.itemId); const accessoryIds = itemRows.filter((row) => row.slot === 'accessory').map((row) => row.itemId);
const lowDetailWeaponTargetIds = ['leader-axe', 'training-sword', 'yellow-turban-saber'];
const targets = [ const targets = [
{ {
title: 'weapon spear/polearm family', title: 'weapon spear/polearm family',
@@ -344,6 +345,15 @@ function buildRecommendations(itemRows, groupedRows) {
openReason: 'Accessories are the smallest UI read and several use compact badge-like shapes.', openReason: 'Accessories are the smallest UI read and several use compact badge-like shapes.',
successCriteria: successCriteria:
'Each accessory should keep a compact footprint while gaining a distinct readable prop shape at 20px and 30px.' 'Each accessory should keep a compact footprint while gaining a distinct readable prop shape at 20px and 30px.'
},
{
title: 'remaining low-detail weapon icons',
itemIds: lowDetailWeaponTargetIds,
completeReason:
'The final low-detail common weapons now use dedicated axe, training sword, and Yellow Turban saber silhouettes.',
openReason:
'These are the last generated equipment icons still below the primitive-detail threshold after the larger weapon, armor, and accessory passes.',
successCriteria: 'Clear the low primitive detail concerns without changing stable item texture keys.'
} }
].map((target) => ({ ].map((target) => ({
...target, ...target,
@@ -356,12 +366,17 @@ function buildRecommendations(itemRows, groupedRows) {
itemIds: target.rows.map((row) => row.itemId), itemIds: target.rows.map((row) => row.itemId),
reason: target.completeReason reason: target.completeReason
})); }));
const remainingConcernRows = itemRows.filter((row) => row.concerns.length > 0);
const nextTarget = const nextTarget =
targets.find((target) => !improvementTargetComplete(target)) ?? { targets.find((target) => !improvementTargetComplete(target)) ?? {
title: 'remaining low-detail equipment icons', title: remainingConcernRows.length ? 'remaining low-detail equipment icons' : 'no flagged equipment icon batch',
rows: itemRows.filter((row) => row.concerns.length > 0), rows: remainingConcernRows,
openReason: 'All named batches are complete; remaining work should be selected from the low-detail inventory rows.', openReason: remainingConcernRows.length
successCriteria: 'Clear remaining low-detail concerns without changing stable texture keys.' ? 'All named batches are complete; remaining work should be selected from the low-detail inventory rows.'
: 'No generated equipment icon currently has a low-detail or shared-helper audit concern; choose the next visual asset pass from in-game QA.',
successCriteria: remainingConcernRows.length
? 'Clear remaining low-detail concerns without changing stable texture keys.'
: 'Keep the generated equipment icon audit green while selecting the next art target from runtime screenshots.'
}; };
const nextTargetIndex = targets.findIndex((target) => target.title === nextTarget.title); const nextTargetIndex = targets.findIndex((target) => target.title === nextTarget.title);
const followUps = targets const followUps = targets
@@ -426,7 +441,7 @@ function renderReport({ iconSourceSize, iconTextureSize, itemRows, groupedRows,
'## Next Improvement Target', '## Next Improvement Target',
'', '',
`- Target: ${recommendations.firstTargetTitle}`, `- Target: ${recommendations.firstTargetTitle}`,
`- Item ids: ${recommendations.firstTargetIds.map((itemId) => `\`${itemId}\``).join(', ')}`, `- Item ids: ${formatItemIds(recommendations.firstTargetIds)}`,
`- Reason: ${recommendations.firstTargetReason}`, `- Reason: ${recommendations.firstTargetReason}`,
'', '',
...renderFollowUpTargets(recommendations.followUps), ...renderFollowUpTargets(recommendations.followUps),
@@ -456,15 +471,37 @@ function renderReport({ iconSourceSize, iconTextureSize, itemRows, groupedRows,
].join(' | ') ].join(' | ')
).map((line) => `| ${line} |`), ).map((line) => `| ${line} |`),
'', '',
...renderRecommendedNextBatch(recommendations)
].join('\n')}`;
}
function formatItemIds(itemIds) {
return itemIds.length ? itemIds.map((itemId) => `\`${itemId}\``).join(', ') : 'none currently flagged';
}
function renderRecommendedNextBatch(recommendations) {
if (!recommendations.firstTargetIds.length) {
return [
'## Recommended Next Batch Definition',
'',
'- Scope: no generated equipment icon is currently below the audit threshold.',
'- Item ids: none currently flagged',
`- Success criteria: ${recommendations.firstTargetSuccessCriteria}`,
'- Suggested next area: map/background or unit sprite assets that still look low-resolution in gameplay.',
''
];
}
return [
'## Recommended Next Batch Definition', '## Recommended Next Batch Definition',
'', '',
`- Scope: improve ${recommendations.firstTargetTitle} while keeping \`item-\${itemId}\` texture keys stable.`, `- Scope: improve ${recommendations.firstTargetTitle} while keeping \`item-\${itemId}\` texture keys stable.`,
`- Item ids: ${recommendations.firstTargetIds.map((itemId) => `\`${itemId}\``).join(', ')}`, `- Item ids: ${formatItemIds(recommendations.firstTargetIds)}`,
`- Success criteria: ${recommendations.firstTargetSuccessCriteria}`, `- Success criteria: ${recommendations.firstTargetSuccessCriteria}`,
'- Keep the existing 64px texture size unless a UI-wide equipment icon pass is planned.', '- Keep the existing 64px texture size unless a UI-wide equipment icon pass is planned.',
'- Add before/after contact sheets and verify the sortie equipment list and equipment swap panel in a desktop browser.', '- Add before/after contact sheets and verify the sortie equipment list and equipment swap panel in a desktop browser.',
'' ''
].join('\n')}`; ];
} }
function renderCompletedTargets(completedTargets) { function renderCompletedTargets(completedTargets) {

View File

@@ -30,7 +30,7 @@ export class BootScene extends Phaser.Scene {
} }
private createEquipmentIconTextures() { private createEquipmentIconTextures() {
this.createItemIcon('item-training-sword', (graphics) => this.drawSwordIcon(graphics, 0xbfd6e6, 0x6b4a2c)); this.createItemIcon('item-training-sword', (graphics) => this.drawTrainingSwordIcon(graphics));
this.createItemIcon('item-twin-oath-blades', (graphics) => { this.createItemIcon('item-twin-oath-blades', (graphics) => {
this.drawSwordIcon(graphics, 0xe8edf7, 0x8a5a32, -4); this.drawSwordIcon(graphics, 0xe8edf7, 0x8a5a32, -4);
this.drawSwordIcon(graphics, 0xd8b15f, 0x8a5a32, 4); this.drawSwordIcon(graphics, 0xd8b15f, 0x8a5a32, 4);
@@ -41,9 +41,9 @@ export class BootScene extends Phaser.Scene {
this.createItemIcon('item-sky-piercer-halberd', (graphics) => this.drawSkyPiercerHalberdIcon(graphics)); this.createItemIcon('item-sky-piercer-halberd', (graphics) => this.drawSkyPiercerHalberdIcon(graphics));
this.createItemIcon('item-western-cavalry-spear', (graphics) => this.drawWesternCavalrySpearIcon(graphics)); this.createItemIcon('item-western-cavalry-spear', (graphics) => this.drawWesternCavalrySpearIcon(graphics));
this.createItemIcon('item-white-feather-fan', (graphics) => this.drawFanIcon(graphics, 0xf3f0df, 0x6e8eb8)); this.createItemIcon('item-white-feather-fan', (graphics) => this.drawFanIcon(graphics, 0xf3f0df, 0x6e8eb8));
this.createItemIcon('item-yellow-turban-saber', (graphics) => this.drawSaberIcon(graphics, 0xd8b15f, 0x9c6b2f)); this.createItemIcon('item-yellow-turban-saber', (graphics) => this.drawYellowTurbanSaberIcon(graphics));
this.createItemIcon('item-short-bow', (graphics) => this.drawBowIcon(graphics, 0xc58a4c, 0xe8dfca)); this.createItemIcon('item-short-bow', (graphics) => this.drawBowIcon(graphics, 0xc58a4c, 0xe8dfca));
this.createItemIcon('item-leader-axe', (graphics) => this.drawAxeIcon(graphics, 0xd8dfe6, 0x8d5a3a)); this.createItemIcon('item-leader-axe', (graphics) => this.drawLeaderAxeIcon(graphics));
this.createItemIcon('item-cloth-armor', (graphics) => this.drawClothArmorIcon(graphics)); this.createItemIcon('item-cloth-armor', (graphics) => this.drawClothArmorIcon(graphics));
this.createItemIcon('item-lamellar-armor', (graphics) => this.drawLamellarArmorIcon(graphics)); this.createItemIcon('item-lamellar-armor', (graphics) => this.drawLamellarArmorIcon(graphics));
@@ -95,6 +95,43 @@ export class BootScene extends Phaser.Scene {
graphics.fillCircle(8 + offsetX, 23, 2); graphics.fillCircle(8 + offsetX, 23, 2);
} }
private drawTrainingSwordIcon(graphics: Phaser.GameObjects.Graphics) {
graphics.lineStyle(5, 0x50606a, 1);
graphics.beginPath();
graphics.moveTo(7, 23);
graphics.lineTo(20, 5);
graphics.strokePath();
graphics.lineStyle(3, 0xbfd6e6, 1);
graphics.beginPath();
graphics.moveTo(8, 22);
graphics.lineTo(20, 6);
graphics.strokePath();
graphics.lineStyle(1, 0xeff7ff, 0.9);
graphics.beginPath();
graphics.moveTo(11, 18);
graphics.lineTo(18, 8);
graphics.strokePath();
graphics.fillStyle(0xd8dfe6, 1);
graphics.fillTriangle(18, 4, 23, 5, 19, 10);
graphics.lineStyle(3, 0xd8b15f, 1);
graphics.beginPath();
graphics.moveTo(5, 18);
graphics.lineTo(13, 23);
graphics.moveTo(8, 16);
graphics.lineTo(15, 20);
graphics.strokePath();
graphics.lineStyle(2, 0x6b4a2c, 1);
graphics.beginPath();
graphics.moveTo(6, 22);
graphics.lineTo(10, 25);
graphics.strokePath();
graphics.fillStyle(0x6b4a2c, 1);
graphics.fillCircle(7, 24, 2);
graphics.fillStyle(0x9b4c3a, 1);
graphics.fillTriangle(10, 22, 15, 24, 12, 27);
graphics.fillCircle(13, 23, 1);
}
private drawGreenDragonGlaiveIcon(graphics: Phaser.GameObjects.Graphics) { private drawGreenDragonGlaiveIcon(graphics: Phaser.GameObjects.Graphics) {
graphics.lineStyle(4, 0x704728, 1); graphics.lineStyle(4, 0x704728, 1);
graphics.beginPath(); graphics.beginPath();
@@ -254,19 +291,45 @@ export class BootScene extends Phaser.Scene {
graphics.strokePath(); graphics.strokePath();
} }
private drawSaberIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, gripColor: number) { private drawYellowTurbanSaberIcon(graphics: Phaser.GameObjects.Graphics) {
graphics.lineStyle(4, bladeColor, 1); graphics.lineStyle(5, 0x6b4a2c, 1);
graphics.beginPath();
graphics.moveTo(7, 22);
graphics.lineTo(12, 15);
graphics.lineTo(19, 8);
graphics.lineTo(24, 5);
graphics.strokePath();
graphics.lineStyle(4, 0xd8b15f, 1);
graphics.beginPath(); graphics.beginPath();
graphics.moveTo(8, 22); graphics.moveTo(8, 22);
graphics.lineTo(13, 15); graphics.lineTo(13, 15);
graphics.lineTo(19, 8); graphics.lineTo(19, 8);
graphics.lineTo(23, 6); graphics.lineTo(23, 6);
graphics.strokePath(); graphics.strokePath();
graphics.lineStyle(3, gripColor, 1); graphics.lineStyle(2, 0xf2d68b, 0.9);
graphics.beginPath();
graphics.moveTo(12, 17);
graphics.lineTo(19, 9);
graphics.lineTo(22, 7);
graphics.strokePath();
graphics.fillStyle(0xf2d68b, 1);
graphics.fillTriangle(21, 4, 26, 5, 22, 9);
graphics.fillStyle(0x9c6b2f, 1);
graphics.fillTriangle(10, 17, 17, 18, 12, 22);
graphics.lineStyle(3, 0x9c6b2f, 1);
graphics.beginPath(); graphics.beginPath();
graphics.moveTo(6, 21); graphics.moveTo(6, 21);
graphics.lineTo(12, 23); graphics.lineTo(12, 23);
graphics.strokePath(); graphics.strokePath();
graphics.lineStyle(2, 0xd8b15f, 1);
graphics.beginPath();
graphics.moveTo(8, 20);
graphics.lineTo(5, 25);
graphics.moveTo(10, 21);
graphics.lineTo(11, 26);
graphics.strokePath();
graphics.fillStyle(0xd8b15f, 1);
graphics.fillCircle(7, 22, 2);
} }
private drawBowIcon(graphics: Phaser.GameObjects.Graphics, bowColor: number, stringColor: number) { private drawBowIcon(graphics: Phaser.GameObjects.Graphics, bowColor: number, stringColor: number) {
@@ -290,15 +353,38 @@ export class BootScene extends Phaser.Scene {
graphics.strokePath(); graphics.strokePath();
} }
private drawAxeIcon(graphics: Phaser.GameObjects.Graphics, bladeColor: number, gripColor: number) { private drawLeaderAxeIcon(graphics: Phaser.GameObjects.Graphics) {
graphics.lineStyle(3, gripColor, 1); graphics.lineStyle(4, 0x6b4a2c, 1);
graphics.beginPath(); graphics.beginPath();
graphics.moveTo(9, 24); graphics.moveTo(9, 24);
graphics.lineTo(18, 5); graphics.lineTo(18, 5);
graphics.strokePath(); graphics.strokePath();
graphics.fillStyle(bladeColor, 1); graphics.lineStyle(1, 0xc99755, 0.9);
graphics.fillTriangle(16, 5, 25, 8, 16, 15); graphics.beginPath();
graphics.fillTriangle(16, 5, 9, 9, 15, 14); graphics.moveTo(11, 22);
graphics.lineTo(18, 7);
graphics.strokePath();
graphics.fillStyle(0xd8dfe6, 1);
graphics.fillEllipse(20, 10, 12, 12);
graphics.fillTriangle(16, 4, 26, 8, 17, 15);
graphics.fillTriangle(16, 5, 8, 9, 15, 15);
graphics.fillStyle(0xeff7ff, 0.85);
graphics.fillTriangle(18, 6, 24, 8, 18, 11);
graphics.fillTriangle(15, 7, 10, 9, 15, 12);
graphics.fillStyle(0x50606a, 1);
graphics.fillTriangle(17, 11, 22, 14, 15, 16);
graphics.lineStyle(2, 0x8d5a3a, 1);
graphics.beginPath();
graphics.moveTo(14, 7);
graphics.lineTo(20, 13);
graphics.moveTo(12, 14);
graphics.lineTo(18, 16);
graphics.strokePath();
graphics.fillStyle(0xd8b15f, 1);
graphics.fillCircle(17, 10, 2);
graphics.fillCircle(9, 24, 2);
graphics.fillStyle(0x9b4c3a, 1);
graphics.fillTriangle(13, 15, 18, 17, 14, 22);
} }
private drawFanIcon(graphics: Phaser.GameObjects.Graphics, featherColor: number, ribColor: number) { private drawFanIcon(graphics: Phaser.GameObjects.Graphics, featherColor: number, ribColor: number) {