Initial commit: ssessesse ki battle

This commit is contained in:
2026-10-13 11:15:26 +09:00
commit 4f24129e29
104 changed files with 10520 additions and 0 deletions
+474
View File
@@ -0,0 +1,474 @@
import type { ActionId } from '../shared/types.js';
import { COSMETIC_COLORS, SKIN_TONES, type EquipmentId } from '../../constants/cosmetics.js';
import { normalizeAppearance, type FighterAppearance } from '../shared/cosmetics.js';
function equipped(appearance: FighterAppearance | undefined, item: EquipmentId) {
return !appearance || !!appearance.equipment?.includes(item);
}
function fighterPalette(index: number, appearance?: FighterAppearance) {
const palette = (appearance?.characterId ?? index) === 1
? '--skin:#b59173;--skin-shade:#786650;--skin-light:#dcc0a0;--suit:#455862;--suit-dark:#293840;--trim:#d7d5bf;--hair:#d7d9cf;--hair-shadow:#7e8d90'
: '--skin:#d7ad8a;--skin-shade:#996b55;--skin-light:#f0cdaa;--suit:#aa5740;--suit-dark:#613b33;--trim:#d3bf99;--hair:#242b30;--hair-shadow:#454e53';
if (!appearance) return palette;
const selected = normalizeAppearance(appearance);
const color = COSMETIC_COLORS.find(color => color.id === selected.colorId) ?? COSMETIC_COLORS[0];
const skin = SKIN_TONES.find(tone => tone.id === selected.skinTone) ?? SKIN_TONES[0];
return palette
.replace(/--skin:[^;]+;--skin-shade:[^;]+;--skin-light:[^;]+;/,
`--skin:${skin.skin};--skin-shade:${skin.shade};--skin-light:${skin.light};`)
.replace(/--suit:[^;]+;--suit-dark:[^;]+;--trim:[^;]+;/,
`--suit:${color.suit};--suit-dark:${color.suitDark};--trim:${color.trim};`)
.replace(/--hair:[^;]+;--hair-shadow:[^;]+/,
selected.hairStyle === 'white' ? '--hair:#d7d9cf;--hair-shadow:#7e8d90' : '--hair:#242b30;--hair-shadow:#454e53');
}
/** Shared head drawing: full-body art and the hit close-up always depict the same fighter. */
function fighterHead(index: number, includeNeck = true, appearance?: FighterAppearance) {
const veteran = index === 1;
const hairStyle = appearance ? normalizeAppearance(appearance).hairStyle : veteran ? 'white' : 'black';
const bald = hairStyle === 'bald', whiteHair = hairStyle === 'white';
const headband = appearance ? equipped(appearance, 'headband') : !veteran;
const eyewear = appearance?.equipment?.includes('sunglasses') ? 'sunglasses'
: appearance?.equipment?.includes('glasses') ? 'glasses' : undefined;
return `<g class="portrait" stroke="#232a2c" stroke-width="1.8" stroke-linejoin="round">
${bald ? '' : whiteHair ? '<path d="m110 36-7-12 14 2 7-14 9 11 20-7-5 11 15 4-7 26-43 4Z" fill="var(--hair)"/><path d="m146 29 10 3-4 22-14-7Z" fill="var(--hair-shadow)"/>' : '<path d="m113 33-15-7 14-3-1-13 16 9 16-14 2 16 19-5-9 12 11 8-15 27-40-6Z" fill="var(--hair)"/><path d="M109 29 103 35 100 44 109 39 113 32Z" fill="var(--hair)"/>'}
${includeNeck ? '<path d="m116 66-3 17 14 12 24-12-9-20Z" fill="var(--skin)"/>\n <path d="m118 69 21-5 9 16-20 3-5 8-6-10Z" fill="var(--skin-shade)" stroke="none"/>' : ''}
<path d="m113 44-6-3-2 9 7 9m39-14 7-5 2 8-7 10" fill="var(--skin)"/>
<path${bald ? ' class="bald-scalp"' : ''} d="${bald
? veteran ? 'M110 45C108 29 116 20 130 20C145 19 154 29 153 44L151 60 142 74 127 78 114 66Z' : 'M111 47C109 30 117 21 131 21C145 21 154 32 152 47L152 57 144 72 130 77 116 67Z'
: veteran ? 'm113 33 19-6 21 8-2 25-9 14-15 4-13-12-4-20Z' : 'm113 34 18-9 20 10 1 22-8 15-14 5-14-10-5-20Z'}" fill="var(--skin)"/>
${bald ? '<path d="M117 33Q124 23 135 26" stroke="var(--skin-light)" stroke-width="3" fill="none"/>' : ''}
<path d="m143 34 7 3 1 18-8 16-13 6-8-7 14-3 6-11-4-9Z" fill="var(--skin-shade)" stroke="none"/>
<path d="m116 52 5 7-5 3m27-10-4 9 7-2m-18-12-3 11 6 1" fill="none" stroke-width="1.3"/>
${bald ? '' : whiteHair ? '<path d="m111 42-2-12 18-10 25 4 7 8-11-1-12 8 2-9-11 10-5-4-6 10Z" fill="var(--hair)"/><path d="m121 24 8 4 14-3m-6 8 13-5" fill="none" stroke="var(--hair-shadow)"/>' : '<path d="m109 43 1-17 18-7 25 7 5 15-14-10-2 14-10-11-8 12-1-14-8 12Z" fill="var(--hair)"/><path d="m116 24 9-3m13 4 11 3" stroke="var(--hair-shadow)" fill="none"/>'}
${veteran ? '<path d="m118 68 5 3m0-1 3 3m7-1 2 2m3-5 2 1m3-4 2 1" fill="none" stroke-width="1"/>' : ''}
${headband ? '<path d="m111 34 42-2-1 6-40 3Z" fill="#864237"/><path d="m111 37-19 8-19-5 13 13 24-11-12 18 17-15Z" fill="#864237"/>' : ''}
<g class="face face-ready">
<path d="m114 47 12 3-9 3Zm23 3 12-5-3 8Z" fill="#f0eee2" stroke-width="1.3"/>
<path d="m121 49 1 3m21-4 1 3" stroke-width="2.6"/><path d="m114 44 12 4m12-1 12-5" stroke-width="2.5"/>
<path d="m123 65 13-1" fill="none"/><path d="m117 56 5 1m19-1 5-2" fill="none" stroke-width=".8"/>
</g>
<g class="face face-focus">
<path d="m113 48 13 3-10 2Zm23 2 13-5-4 8Z" fill="#f4f2e9" stroke-width="1.4"/>
<path d="m122 50v2m21-4v3m-30-8 13 6m12-1 13-6" stroke-width="2.5"/>
<path d="m122 63 16-1-3 6-11-1Z" fill="#ede6d4" stroke-width="1.2"/><path d="m128 64 5 1" fill="none" stroke-width=".7"/>
</g>
<g class="face face-win">
<path d="m115 48 11 2m13-1 10-3" stroke-width="2.1"/><path d="m122 65 8 2 10-5" fill="none"/>
<path d="m122 49v3m22-5v3" stroke-width="2.4"/>
</g>
<g class="face face-loss">
<path d="m113 48 13 4m12-1 12-5m-28 18 14-1" fill="none" stroke-width="2.2"/>
<path d="m116 55 5 4m18-4 5-3m-24 6 2 2m-1-3 2 2m18-4 3-1" fill="none" stroke-width=".8"/>
</g>
<g class="face face-draw">
<path d="m114 47 12 3-10 3Zm24 3 11-3-3 6Z" fill="#ece9df" stroke-width="1.2"/><path d="m122 49v3m21-3v3" stroke-width="2.5"/>
<path d="m124 65 12 1m-22-23 12 4m12 0 12-4" fill="none" stroke-width="1.5"/>
</g>
<g class="face face-shock">
<path d="m112 44 15 3-3 10-9-2Zm25 4 16-8-2 13-11 4Z" fill="#fffdf1" stroke-width="1.6"/>
<ellipse cx="122" cy="51" rx="1.9" ry="3" fill="#232a2c" stroke="none"/><ellipse cx="143" cy="50" rx="1.9" ry="3.2" fill="#232a2c" stroke="none"/>
<path d="m112 40 14 3m11 0 16-8" fill="none" stroke-width="2.2"/>
<path d="m121 63 15-3 5 11-10 5-10-5Z" fill="#33323a"/><path d="m123 63 11-1 1 3-11 2Z" fill="#faf4e7" stroke-width=".7"/>
<path d="m114 57 5 3m-6-1 4 3m31-7 4-2m-4 5 4-2m-23-18 1 6m4-7 0 6" fill="none" stroke-width=".7"/>
</g>
${eyewear ? `<g class="eyewear eyewear-${eyewear}" stroke="#232a2c" stroke-width="2" stroke-linecap="round">
<path d="M111 46 107 43M129 49Q132 47 136 49M153 43 158 40" fill="none"/>
<path d="M111 43 129 46 127 59 113 57Z M135 46 154 39 153 55 138 59Z" fill="${eyewear === 'sunglasses' ? '#26343d' : '#d8f0f2'}" fill-opacity="${eyewear === 'sunglasses' ? '1' : '.14'}"/>
<path d="m115 47 4 1m21 0 8-3" fill="none" stroke="#e4f1ec" stroke-width="1" opacity=".65"/>
</g>` : ''}
</g>`;
}
/** Athletic ink-and-cel figures retain the arena's existing 260 × 300 geometry. */
export function fighterArt(index: number, closeup = false, appearance?: FighterAppearance) {
const characterId = appearance?.characterId ?? index;
const veteran = characterId === 1;
const shoes = equipped(appearance, 'shoes'), wristbands = equipped(appearance, 'wristbands');
return `<svg class="fighter-art" viewBox="${closeup ? '72 10 136 130' : '0 0 260 300'}" fill="none" aria-hidden="true" style="${fighterPalette(index, appearance)}">
<ellipse class="ground-shadow" cx="132" cy="285" rx="88" ry="7" fill="#222c3025"/>
<g class="character" stroke="#232a2c" stroke-width="2.4" stroke-linejoin="round" stroke-linecap="round">
<g class="legs legs-stance">
<path d="m104 160-18 43-22 60 24 8 37-56 13-27 18 29 26 47 24-10-20-63-32-35Z" fill="var(--suit)"/>
<path d="m105 166-8 40-23 51 15 7 27-53 18-24 5-23m3 15 24 37 19 40 10-5-20-58-20-31Z" fill="var(--suit-dark)" stroke="none"/>
<path d="m111 179-12 34-13 11m39-24-16 20m42-29 10 26 18 15m-11 1 9 10" fill="none" stroke-width="1.5"/>
${shoes ? `<path d="M66 263 88 271 83 280 59 279Z M184 263 203 255 207 280 185 281Z" fill="var(--skin)"/>
<path d="M60 275Q69 280 85 276L83 289Q64 293 44 292Q39 292 41 287Q43 283 54 281Z M185 276Q195 281 207 276L211 282Q218 284 217 290Q211 294 182 292L178 287Z" fill="#30383c"/>
<path d="M42 289Q60 291 82 287M182 289Q200 291 215 289" stroke-width="1"/>` : `<g class="bare-feet">
<path d="M66 263 88 271 83 279Q84 285 80 289L48 291Q40 291 40 287Q40 284 46 283L57 279Q62 275 66 263Z M184 263 203 255 206 277Q209 281 216 284Q222 286 218 290Q215 292 208 291L185 290Q180 289 182 283L187 276Z" fill="var(--skin)"/>
<path d="M61 280Q69 284 80 280M190 279Q198 283 206 280" stroke="var(--skin-shade)" stroke-width="1.4"/>
<path d="m47 284 1 5m5-6 1 5m5-6 1 5m147-2-1 5m7-4-1 5m7-3-1 3" stroke-width="1"/>
</g>`}
</g>
<g class="legs legs-airborne">
<path d="m102 158-34 42-27 1-14 22 45 6 42-35 37 20 27-9-18-46Z" fill="var(--suit)"/>
<path d="m116 175-46 41-29-3-6 10 37 6 42-35 34 19 19-6Z" fill="var(--suit-dark)" stroke="none"/>
<path d="m146 191 30 12 14 42 22 7-7 23-32-17-19-34-27-14" fill="var(--suit)"/>
${shoes ? `<path d="M192 249 211 253 207 270 190 265Z M43 203 43 225 32 225 30 204Z" fill="var(--skin)"/>
<path d="M198 257Q203 263 213 261L216 276Q218 283 211 285L196 280Q188 277 188 271L190 264Z M32 204Q28 214 35 225L24 229Q19 230 18 223L16 210Q16 203 22 202Z" fill="#30383c"/>
<path d="M194 276 208 282Q214 284 214 279M21 207 22 221Q22 226 27 226" stroke-width="1"/>` : `<g class="bare-feet">
<path d="M192 249 211 253 207 266Q209 273 215 278Q219 284 214 286Q210 287 205 282L194 276Q188 273 189 267Z M43 203 43 225 32 224Q27 226 23 230Q18 232 16 227L15 213Q14 205 20 202L30 204Z" fill="var(--skin)"/>
<path d="M195 267Q198 269 201 268M26 208Q24 215 28 221" stroke="var(--skin-shade)" stroke-width="1.3"/>
<path d="m205 277-2 4m6-1-2 3m6 0-1 2M19 221l5-1m-5-4 4-1m-4-4 4-1" stroke-width="1"/>
</g>`}
<path d="m83 192-9 14-23 7m103-13 8 11m-1 14 7 14" stroke-width="1.5"/>
</g>
<path d="${veteran ? 'm110 77-28 9-5 20 18 28 8 34 50 4 16-41 10-27-22-24-17-4Z' : 'm113 77-26 10-7 21 20 26 3 34 49 4 13-41 7-30-25-23Z'}" fill="var(--suit)"/>
<path d="m111 78 17 16 15-17 11 12-23 47-32-39Z" fill="var(--skin)"/>
<path d="m112 89 12 12 4 18 10-14 9-24-8 0-11 12-11-13Z" fill="var(--skin-shade)" stroke="none"/>
<path d="m111 102 12 3m10 3 7-6m-14 10 2 6" fill="none" stroke-width="1.2"/>
${veteran ? '<path d="m90 86 21-8 6 8-17 30-7 36-9-48Z" fill="var(--suit-dark)"/><path d="m151 82 20 9-9 26-17-12Z" fill="var(--trim)"/><path d="m154 91 12 5m-15 2 12 5m-7 0 5 3" stroke="var(--suit-dark)" stroke-width="2"/>' : '<path d="m109 79-12 13 30 43 24-51-8-7-16 39Z" fill="var(--trim)"/><path d="m100 87 7 2m-9 3 7 3m37-10-9 22" fill="none" stroke-width="1.1"/>'}
<path d="m155 112-12 32-6 17 19 2 6-40-2-12Zm-56 18 14 17 23 12-32 1Z" fill="var(--suit-dark)" stroke="none"/>
<path d="m105 133 9 11m28-16-7 20m13-4-5 12m-34-7 16 6" fill="none" stroke-width="1.4"/>
<path d="m101 155 55 4-3 12-50-4Z" fill="#303238"/><path d="m138 165 7 30 11 8 1-17-10-23-3 2-1 38-9 7-3-40Z" fill="${veteran ? '#9b5448' : '#484c4b'}"/>
${fighterHead(characterId, true, appearance)}
<g class="pose pose-ready">
<path d="m88 90-14 5-18 28-5 17 15 9 29-27 8-14Z" fill="var(--skin)"/><path d="m75 102-15 29 4 10 21-17 8-18" fill="var(--skin-shade)" stroke="none"/>
<path d="m54 137 20-21 12-19 13 7-10 23-24 23Z" fill="var(--skin)"/>${wristbands ? '<path d="m76 113 15-16 11 8-12 20Z" fill="var(--trim)"/>' : ''}
<path d="m87 100 1-13 10-9 13 4 4 10-9 12-11 1Z" fill="var(--skin)"/><path d="m94 86 1 7m5-9 1 7m5-5-1 7m-7 8 6-4" stroke-width="1.2"/>
<path d="m163 91 15 13 14 27-9 18-18-18-15-27Z" fill="var(--skin)"/><path d="m166 101 12 22 7 17-12-11-16-24" fill="var(--skin-shade)" stroke="none"/>
<path d="m178 130 14-26 14 4-9 32-13 10-12-9Z" fill="var(--skin)"/>${wristbands ? '<path d="m187 110 3-12 18 5-4 16Z" fill="var(--trim)"/>' : ''}
<path d="m190 100-3-12 7-9 16 3 5 9-8 15Z" fill="var(--skin)"/><path d="m194 84 0 8m6-8 0 9m6-6-1 8m-17-3 7 7" stroke-width="1.2"/>
<path d="${wristbands ? 'm66 122 6-5m98 2 6 5m-93-6 10 4m-11 0 9 4m104-20 13 4m-14 1 13 4' : 'm66 122 6-5m98 2 6 5'}" fill="none" stroke-width="1"/>
</g>
<g class="pose pose-charge">
<path d="m88 90-14 10-13 39 10 21 22-8-7-18 15-28Z M163 90l17 12 18 38-5 21-23-8 3-19-21-29Z" fill="var(--skin)"/>
<path d="m74 111-6 25 8 11 6-15 10-24m75 0 16 28-5 11-4-17-15-19" fill="var(--skin-shade)" stroke="none"/>
${wristbands ? '<path d="m71 143 18-2 4 15-20 4Zm107-3 19 1-3 18-20-5Z" fill="var(--trim)"/>' : ''}
<path d="m74 157-1 14 10 8 14-7-2-16-12-3Zm102-3-4 16 12 9 12-6 1-14-12-7Z" fill="var(--skin)"/>
<path d="${wristbands ? 'm78 159 0 8m6-10 0 9m5-9 0 8m89-7-1 8m7-6-1 8m6-5-1 7m-114-28 14-2m96 1 13 3' : 'm78 159 0 8m6-10 0 9m5-9 0 8m89-7-1 8m7-6-1 8m6-5-1 7'}" stroke-width="1.1"/>
</g>
<g class="pose pose-attack">
<path d="m86 91-10 18 19 27 35 8 46-18-7-17-42 13-19-14-7-18Z" fill="var(--skin)"/>
<path d="m84 112 20 18 24 7 40-17 1 7-37 17-37-8Z" fill="var(--skin-shade)" stroke="none"/>
<path d="m156 89 25 12 29-2 6 20-38 6-23-12-9-15Z" fill="var(--skin)"/><path d="m167 111 17 7 25-6 3 7-34 6-17-7Z" fill="var(--skin-shade)" stroke="none"/>
${wristbands ? '<path d="m195 100 18-4 7 22-21 4Zm-36 12 12-4 8 17-13 7Z" fill="var(--trim)"/>' : ''}
<path d="m211 100 6-13 5-5 5 2-4 15 21-10 5 4-16 12 17-5 3 5-17 10 13-1 1 5-25 8-12-6Z" fill="var(--skin)"/>
<path d="m172 115 12-10 9-1 2 5-12 9 14-4 3 5-20 12-9-6Z" fill="var(--skin)"/>
<path d="${wristbands ? 'm197 104 17-4m-16 9 17-5m-16 10 18-5m-107 11 11 5m58-19 12-1m30 8 6-9' : 'm110 120 11 5m58-19 12-1m30 8 6-9'}" stroke-width="1.1"/>
</g>
<g class="pose pose-guard">
<path d="m87 92-10 14 19 35 26-6 29-52-15-11-29 43-9-23Zm74 0 16 18-15 34-24-9-31-48 14-14 30 40 3-13Z" fill="var(--skin)"/>
<path d="m83 107 20 25 14-2 5 6-26 5Zm78-6 9 13-12 21-7-7Z" fill="var(--skin-shade)" stroke="none"/>
${wristbands ? '<path d="m121 91 14-23 20 12-15 23Zm-16-13 15-11 17 24-17 13Z" fill="var(--trim)"/>' : ''}
<path d="m134 72 0-13 8-9 14 4 3 12-8 14Zm-31 5-6-11 4-13 15-3 8 12-5 11Z" fill="var(--skin)"/>
<path d="${wristbands ? 'm141 57 0 9m5-10 0 8m-42-7 4 8m2-11 3 8m-3 17 12-7m-9 11 12-7m11 5 15 8m-17-4 13 7' : 'm141 57 0 9m5-10 0 8m-42-7 4 8m2-11 3 8'}" stroke-width="1"/>
</g>
<g class="pose pose-dodge">
<path d="m88 91-18 14-22 24 11 14 27-15 14-25Z" fill="var(--skin)"/><path d="m76 109-20 23 5 5 23-15 8-18" fill="var(--skin-shade)" stroke="none"/>
${wristbands ? '<path d="m48 125 15 14-9 13-17-14Z" fill="var(--trim)"/>' : ''}<path d="m38 136-13 4-3 12 12 9 12-4 8-10Z" fill="var(--skin)"/>
<path d="m159 90 16 9 7-17-20-24-10 10 11 17-8 14Z" fill="var(--skin)"/><path d="m162 99 12 5 8-22-8-1-4 13Z" fill="var(--skin-shade)" stroke="none"/>
${wristbands ? '<path d="m153 59 10-7 11 14-12 9Z" fill="var(--trim)"/>' : ''}<path d="m154 62-8-10-5-14 5-2 7 13 1-18 5 0 1 23 4 4-3 8Z" fill="var(--skin)"/>
<path d="${wristbands ? 'm156 62 10-6m-7 10 9-6m-137 83 5 6m0-9 6 6' : 'm31 143 5 6m0-9 6 6'}" stroke-width="1.1"/>
</g>
<g class="pose pose-win">
<path d="m88 92-14 15-8 38 16 7 13-41 5-9Zm72-2 18 11 14-20-10-8-16 11Z" fill="var(--skin)"/>
<path d="m76 111-7 31 8 3 9-31m82-24 12 6 6-8" fill="var(--skin-shade)" stroke="none"/>
${wristbands ? '<path d="m67 136 20 6-4 16-20-6Zm108-58 10-15 17 11-12 16Z" fill="var(--trim)"/>' : ''}
<path d="m65 151-2 14 12 8 12-9-5-12Zm118-86 1-15 12-5 11 8-3 13-11 8Z" fill="var(--skin)"/>
<path d="m189 53v8m6-10v9m5-6v8m-130 94v8m6-6v7" stroke-width="1.1"/>
</g>
<g class="pose pose-loss">
<path d="m86 94-13 23-5 41 15 5 14-50Zm74-3 18 15 9 35-15 16-38-14 4-14 30 8-12-29Z" fill="var(--skin)"/>
<path d="m78 115-6 36 7 4 9-40m77 29 11-3 8-3-10 15-34-12 1-6Z" fill="var(--skin-shade)" stroke="none"/>
${wristbands ? '<path d="m68 150 18 4-3 15-17-4Zm63-23 17 5-7 17-14-6Z" fill="var(--trim)"/>' : ''}
<path d="m67 164-2 15 8 8 11-5 0-13Zm65-35-12-6-16 3-3 7 8 9 19 2Z" fill="var(--skin)"/>
<path d="m108 130 13 1m-12 5 10 1m-49 35v8m6-7v8" stroke-width="1.1"/>
</g>
<g class="pose pose-draw">
<path d="m86 92-16 24 13 23 58-6-1-16-47 4 8-17Zm75 0 17 20-7 33-54-6 1-17 37 1-6-20Z" fill="var(--skin)"/>
<path d="m76 117 11 14 43-3 1 5-48 6Zm79 15 13-7-2 15-41-4 1-8Z" fill="var(--skin-shade)" stroke="none"/>
${wristbands ? '<path d="m121 117 20 0 3 17-20 1Zm-16 3 20 3-2 18-20-4Z" fill="var(--trim)"/>' : ''}
<path d="m140 119 12-4 9 7-5 10-12 0Zm-35 1-11-4-10 5 4 12 15 3Z" fill="var(--skin)"/>
${wristbands ? '<path d="m126 122h13m-12 6h13m-32-2 14 3m-15 2 13 3" stroke-width="1"/>' : ''}
</g>
<g class="pose pose-hit">
<path d="m88 92-15-7-25-24-12 12 18 33 24 12 22-13Zm72 0 19-8 22 23 28-9 7 14-30 16-20-5-31-15Z" fill="var(--skin)"/>
<path d="m46 75 15 24 22 7-5 10-24-10Zm132 18 19 22 27-8 5 6-23 15-20-5Z" fill="var(--skin-shade)" stroke="none"/>
${wristbands ? '<path d="m34 66 12-15 16 14-15 18Zm185 32 12-5 9 22-15 6Z" fill="var(--trim)"/>' : ''}
<path d="m37 58-10-3-13-15 4-4 13 11-7-18 5-3 11 21-2-21 5 0 7 27-4 9Zm194 36 8-13 5 2-4 16 12-5 3 5-11 13-6 5Z" fill="var(--skin)"/>
${wristbands ? '<path d="m42 64 9-9m-5 13 9-9m169 45 11-5m-9 11 12-6" stroke-width="1.1"/>' : ''}
</g>
</g>
</svg>`;
}
/** Preserved Yamcha-reference variant; not used by the active finisher. */
export function yamchaFallenFighterArt(index: number) {
return `<svg class="fallen-fighter-art" data-fallen-index="${index}" viewBox="0 0 640 400" fill="none" aria-hidden="true" style="${fighterPalette(index)}">
<g stroke="#343731" stroke-linejoin="round" stroke-linecap="round">
<path d="m42 212 42-85 95-64 136-25 123 23 102 61 57 99-39 91-115 61-134 12-150-38-86-64Z" fill="#b9af93" stroke-width="3"/>
<path d="m78 210 43-64 74-48 117-24 115 27 83 49 47 70-38 66-88 48-121 15-121-33-83-57Z" fill="#8c8974" stroke-width="2.5"/>
<path d="m125 213 42-50 79-33 89-15 94 34 71 54-10 66-78 42-102 7-98-27-69-38Z" fill="#777c68" stroke-width="2"/>
<g stroke-width="2">
<path d="m51 214 59-6 51 18 38-13m-99-73 31 25 40-3 33 23m-28-111 22 48 30 6m77-79-6 47 19 25m75-68-15 46 19 29m87-16-33 21-13 38m91-18-33 39-36 2m67 59-52-16-35 15m13 70-28-40-21-3m-38 69-4-35-31-27m-61 65 8-37-23-19m-92 29 31-37-9-29m-79 26 40-34 30-2"/>
<path d="m87 91 12-8 17 8-6 13-21-1ZM522 85l14-8 20 13-10 14-20-3ZM50 292l21-8 15 19-27 7ZM498 344l20-15 23 8-2 21-29 4ZM130 355l19-23 27-2 19 16-7 16-34 7Z" fill="#c4b89c"/>
<path d="m146 337 12 14 25-7m-26 9-3 14m-92-75 9 9m465-213 9 8m-32 248 11 8" stroke-width="1.3"/>
</g>
<g fill="#dbd1b7" stroke="#747664" stroke-width="1.5">
<path d="M145 87q-13-7-6-18-4-15 10-17 6-13 17-5 15-2 14 14 9 12-5 19-8 14-20 5Z"/>
<path d="M482 123q-10-6-5-16-2-10 9-12 6-10 14-3 12 2 8 13 8 11-4 16-11 12-22 2Z"/>
<path d="M457 62q-7-7 0-13 7-5 12 1 6 9-4 14Z"/>
</g>
<g stroke="#525b4c" stroke-width="1.3">
<path d="m173 206-36-5m45 13-42-3m38 13-30-1m29 12-28 3m325-60 28 4m-26 4 35 4m-33 4 27 4m-25 52 31 9m-36-1 24 9m-232 42-24 12m32-6-23 13m111-226-11-15m22 9-4-18"/>
</g>
<g class="fallen-pose-trace" transform="translate(130 -100) scale(1.2)" fill="none" stroke="#242b2b" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round">
<path d="M49 291 64 272 86 267 112 242 165 235 196 256 206 291 218 328 215 356 199 359 183 338 149 331 128 321 111 334 96 327 94 308 76 304 52 301Z" fill="#333c30" stroke="none" opacity=".55"/>
<!-- The hidden right arm terminates here; its shoulder has no visible contour. -->
<path d="M112 259 102 277 88 290 75 290 72 279 87 272 98 256Z" fill="var(--skin-shade)"/>
<path d="m86 271 13 4-7 15-14 5-6-10Z" fill="var(--trim)"/>
<path d="m82 278 10 3m-12 2 10 3" stroke-width="1"/>
<path d="M77 281 67 277 55 280 46 290 49 297 63 299 74 296 81 289Z" fill="var(--skin)"/>
<path d="m54 283 3 6m3-9 3 6m3-7 3 6m-5 7 8-3" stroke-width="1.1"/>
<path d="m49 293 13 2 10-4 9-2-7 7-11 3-14-2Z" fill="var(--skin-shade)" stroke="none"/>
<!-- The far thigh disappears beneath the abdomen and the central forward sole. -->
<path d="M130 252 114 257 108 269 106 281 98 294 100 307 114 313 130 299 147 284 159 269Z" fill="var(--suit)"/>
<path d="M119 262 115 277 120 286 111 300 102 302 100 307 114 313 136 293 147 284 138 273Z" fill="var(--suit-dark)" stroke="none"/>
<path d="m111 269 6-7m-9 18 7-8m3 3 5-11m-20 29 7-5" stroke-width="1.2"/>
<!-- A low back and pelvis share one contour; the knee interrupts its lower edge. -->
<path d="M118 226 130 219 146 219 162 222 177 225 188 231 195 241 199 255 202 269 199 280 191 289 179 296 163 295 147 288 136 275 128 264 119 259 111 249Z" fill="var(--suit)"/>
<path d="M180 228 190 239 194 254 195 268 188 279 173 284 151 281 159 289 179 296 191 289 199 280 202 269 199 255 195 241 188 231Z" fill="var(--suit-dark)" stroke="none"/>
${index === 1
? '<path d="M124 229 136 223 148 226 139 240 143 261 133 253 124 241Z" fill="var(--suit-dark)" stroke="none"/><path d="M169 226 182 230 191 241 187 253 174 242 163 236Z" fill="var(--trim)"/><path d="m172 234 12 8m-11-3 10 7" stroke="var(--suit-dark)" stroke-width="1.5"/>'
: '<path d="M119 228 131 222 150 223 170 226 185 233 182 237 167 232 149 229 134 229 124 235Z" fill="var(--trim)"/><path d="m139 225 10 1m17 3 12 5" stroke-width="1"/>'}
<path d="m140 238 8 16 11 9m22-15 1 13-12 9-12 1m-17-8 7 11m39-4 9-4" stroke-width="1.3"/>
<!-- Short rear hair retains the tucked head position and never exposes the face. -->
<path d="M111 218 122 215 139 217 147 223 138 231 121 233 111 228Z" fill="var(--skin-shade)"/>
${index === 1
? '<path d="M76 236 77 224 71 214 84 216 93 202 101 211 119 204 116 213 135 213 130 222 139 226 128 236 111 238 97 242 85 240Z" fill="var(--hair)"/><path d="M116 216 130 218 128 231 113 236 101 237 110 227Z" fill="var(--hair-shadow)" stroke="none"/><path d="m82 222 13-7 12 3-6 8m5-9 11-5m-26 16 10-6" stroke="var(--hair-shadow)" stroke-width="1.4"/>'
: '<path d="M75 237 78 225 70 217 84 218 83 206 97 212 107 198 111 211 128 204 123 215 138 213 132 223 140 227 127 237 111 239 96 243 83 241Z" fill="var(--hair)"/><path d="M98 215 107 208 106 222 117 216 112 229 125 223 119 234 106 237 91 239 99 227 86 231Z" fill="var(--hair-shadow)" stroke="none"/><path d="M77 233 92 229 109 225 132 222 134 226 111 229 94 233 79 237Z" fill="#864237"/><path d="M80 234 68 234 63 230 67 239 78 238 72 245 83 239Z" fill="#864237" stroke-width="1.2"/>'}
<!-- This broad bent arm masks the face and cuts off the lower hair contour. -->
<path d="M122 226 112 230 100 232 86 234 75 238 68 244 64 251 62 259 68 265 78 268 89 268 104 262 118 254 126 247 132 234Z" fill="var(--skin)"/>
<path d="M63 256 68 260 80 262 91 261 107 254 119 244 125 233 132 234 126 247 118 254 104 262 89 268 78 268 68 265 62 259Z" fill="var(--skin-shade)" stroke="none"/>
<path d="M119 226 131 222 142 225 148 231 137 237 127 247 119 245 126 235Z" fill="var(--suit)"/>
<path d="M119 226 124 225 130 235 123 245 119 245 126 235Z" fill="${index === 1 ? 'var(--suit-dark)' : 'var(--trim)'}"/>
<path d="m109 248-9 7-10 3m-14-12-3 8m52-24 6 3" stroke-width="1.2"/>
<path d="M72 233 76 228 82 228 88 232 86 235 77 239 70 241Z" fill="var(--trim)"/>
<path d="m75 231 5 5m-8-2 5 4" stroke-width="1"/>
<!-- The right boot is a narrow side view behind the folded right knee. -->
<path d="M188 309 201 312 209 318 213 330 206 348 196 350 188 337 184 321Z" fill="#30383c"/>
<path d="m190 314 9 2 6 6m-16-3 9 2 6 6" stroke="var(--trim)" stroke-width="2.5"/>
<path d="M208 315 213 319 216 326 217 339 212 352 206 356 197 355 194 346 195 334 200 323Z" fill="#30383c"/>
<path d="M207 320 201 330 198 341 200 350 205 352 211 339 211 330Z" fill="#454e53" stroke="none"/>
<path d="M209 319 213 327 214 338 209 350 205 353 199 352" stroke="#b5b4a5" stroke-width="1.5"/>
<path d="m202 330 7 5m-10 7 7 4" stroke-width="1.2"/>
<!-- Folded thighs overlap the abdomen without an elongated exposed shin. -->
<path d="M148 284q11-7 25-4l13 5 7 8-3 8 7 5 2 11-5 10-9 4-9 7-5-6-4 5-6-8-10-3-9-8-6-9-1-12Z" fill="var(--suit)"/>
<path d="M146 295 153 287 163 284 177 286 187 291 174 289 162 290 154 297 147 311 151 319 159 324 171 329 171 334 167 337 161 329 151 326 142 318 136 309 135 297Z" fill="var(--suit-dark)" stroke="none"/>
<path d="m154 299 8-5 12 0 13 3m-34 13 10-5 10 2m-14 15 15 6 15-8" stroke-width="1.3"/>
<!-- The large central sole overlaps the folded leg and the knee's left edge. -->
<path d="M123 277 135 279 143 287 145 297 137 304 125 313 110 316 106 307 113 291Z" fill="#30383c"/>
<path d="m122 280 9 2 10 10m-21-8 9 2 10 10" stroke="var(--trim)" stroke-width="2.5"/>
<path d="M131 283 137 283 141 289 142 296 135 307 126 316 118 328 112 331 104 328 100 322 102 312 110 300 120 288Z" fill="#30383c"/>
<path d="M130 287 136 287 139 292 137 299 128 308 120 319 114 327 108 327 103 322 105 313 113 301 122 291Z" fill="#454e53" stroke="none"/>
<path d="M132 286 137 289 139 295 132 305 123 314 115 326 111 328 105 325 103 321" stroke="#b5b4a5" stroke-width="1.5"/>
<path d="m122 295 10 10m-17 0 9 10m-16 0 8 8" stroke-width="1.2"/>
</g>
<g stroke="#4a5547" stroke-width="1.2">
<path d="m181 192 14 2m-19 4 16 2m-18 5 15 2m120 119 12 3m-16 3 15 4m-18 3 14 3m126-99 22 3m-20 3 25 4m-23 3 17 4m-27 83 20 3m-21 4 16 3"/>
</g>
</g>
</svg>`;
}
/** Slumped against a rock face, with loose arms and legs extending along the ground. */
function fallenFighterArt(index: number, appearance?: FighterAppearance) {
const characterId = appearance?.characterId ?? index;
const shoes = equipped(appearance, 'shoes'), wristbands = equipped(appearance, 'wristbands');
return `<svg class="fallen-fighter-art rock-fallen-art" data-fallen-index="${index}" viewBox="0 0 640 400" fill="none" aria-hidden="true" style="${fighterPalette(index, appearance)}">
<g stroke="#343731" stroke-width="2" stroke-linejoin="round" stroke-linecap="round">
<path d="M-1200 276 100 272 201 278 294 273 419 282 1800 270V400H-1200Z" fill="#c4b89a" stroke="none"/>
<path d="M0 276 100 272 201 278 294 273 419 282M29 321l151-17 90 1m-192 38 145-22 57-1m-151 40 126-27" stroke="#8c8771" stroke-width="1.3"/>
<!-- A tall rock face supports the shoulder; the open ground runs off to the left. -->
<path d="M393 287 422 241 438 171 456 116 473 84 487 23 524-14 579-9 602 61 630 103 658 129 790 20 960 64 1150-60 1800-80V321L740 323 638 310 538 305 468 319Z" fill="#a59f86" stroke-width="3"/>
<path d="m438 171 18-55 17-32 14-61 37-37 12 91-36 77-15 72-28 52-64 9 29-46Z" fill="#c8bea1" stroke="none"/>
<path d="M536 77 579-9 602 61 630 103 594 143 607 222 680 281 790 20 745 211 850 298 960 64 950 278 1150-60 1110 300 1800 321 740 323 638 310 538 305 468 319 495 273 498 209 524 155Z" fill="#858571" stroke="none"/>
<path d="m487 23 25 52-26 64 14 15m36-77-12 78-26 54-3 64-27 46m126-176-32 26 9 64-33 72m-92-134 21 30-13 43m59-107 15 37-12 34m-35 65 14 11 23-5m40-180 8 26-10 34"/>
<path d="m432 190 22-15 10-27m-23 66 26-8 15 13m-46 14 15 18-4 24m-11-65 8-16 13-2" stroke="#595e50" stroke-width="1.5"/>
<path d="m447 125 8-21m-2 37 9-26m25 64 6-18m-10 31 6-19m64 64 9-22m-7 39 9-24m-14 39 8-17" stroke="#686e5c" stroke-width="1.2"/>
<path d="M155 296q99-27 231-24l62 19-41 27-108 33-72-3 38-25-110-12Z" fill="#4a5143" stroke="none" opacity=".3"/>
<g stroke="#242b2b" stroke-width="2.5">
<!-- Far arm hangs alongside the rock instead of supporting the body. -->
<path d="m412 181 15 6 11 35-5 30-8 22-11-3 6-34-13-30Z" fill="var(--skin)"/>
<path d="m423 195 9 28-6 30-6 14 5 7 8-22 5-30-11-35Z" fill="var(--skin-shade)" stroke="none"/>
${wristbands ? '<path d="m416 259 13 4-5 15-13-4Z" fill="var(--trim)"/>' : ''}
<path d="m412 274 12 3-3 13-6 6-4-3 2-9-7 7-4-4 4-8Z" fill="var(--skin)"/>
<!-- The far leg lies almost horizontally; its shoe is seen from the side. -->
<path d="M377 244 391 260 361 281 313 285 264 289 216 287 213 269 264 268 307 265 348 250Z" fill="var(--suit)"/>
<path d="m217 280 46 2 49-5 45-5 28-17 6 5-30 21-48 4-49 4-48-2Z" fill="var(--suit-dark)" stroke="none"/>
<path d="m314 269-15 9m-29-5-12 7m81-15 15-6" stroke-width="1.5"/>
${shoes ? `<path d="M214 270 216 286 199 289 197 273Z" fill="var(--skin)"/>
<path d="M199 273Q204 281 201 289L170 292Q158 293 155 287Q153 282 161 279L184 276Z" fill="#30383c"/>
<path d="M158 288Q164 290 172 289L200 286" stroke-width="1"/>` : `<g class="bare-feet">
<path d="M214 270 216 286 199 289 175 292Q160 294 157 289Q153 285 159 282L182 278Q191 277 198 273Z" fill="var(--skin)"/>
<path d="M195 281Q198 284 200 283M161 283l2 6m5-7 2 7m5-8 2 7" stroke-width="1"/>
</g>`}
<!-- The pelvis rests on the ground while the upper back leans into the rock. -->
<path d="M371 180 389 169 409 174 426 188 419 218 404 252 381 267 353 249 362 216 359 194Z" fill="var(--suit)"/>
<path d="m409 177 11 15-13 39-26 20-21-4-7 2 28 18 23-15 15-34 7-30Z" fill="var(--suit-dark)" stroke="none"/>
<path d="M377 165 393 165 400 179 391 193 375 182Z" fill="var(--skin)"/>
<path d="m377 169 15 3 6 8-7 9-12-10Z" fill="var(--skin-shade)" stroke="none"/>
${characterId === 1
? '<path d="m363 190 8-10 7 13-10 15-8-6Zm44-14 14 8 5 10-13 10-11-8Z" fill="var(--trim)"/>'
: '<path d="m370 181 6-5 13 16 14-16 7 4-20 22Z" fill="var(--trim)"/>'}
<path d="m375 214 12 11 13-5m-35 15 18 4-9 9m29-44 8-9" stroke-width="1.5"/>
<!-- Knee, shin and shoe share one diagonal instead of turning outward at the ankle. -->
<path d="M377 248 397 258 374 291 351 313 325 345 304 334 327 298 346 273 356 253Z" fill="var(--suit)"/>
<path d="m381 258-18 29-27 26-21 26 10 6 26-32 23-22 23-33-10-5Z" fill="var(--suit-dark)" stroke="none"/>
<path d="m349 279 12-6m-30 33 13-13m-14 32 9-13m28-49 9 5" stroke-width="1.5"/>
${shoes ? `<path d="M305 335 324 345 315 359 295 347Z" fill="var(--skin)"/>
<path d="M296 348Q302 356 316 358L305 374Q301 380 286 384Q280 386 274 379Q272 376 277 370Z" fill="#30383c"/>
<path d="M276 378Q280 382 286 381L300 375 312 361" stroke-width="1"/>` : `<g class="bare-feet">
<path d="M305 335 324 345 313 361 303 373Q296 379 285 383Q278 386 274 381Q271 378 276 372L294 351Z" fill="var(--skin)"/>
<path d="M296 350Q300 355 305 355M277 374l5 5m1-10 6 7m0-12 6 8" stroke-width="1"/>
</g>`}
<!-- Right arm follows the user's short, slack outline with the same width as the far arm. -->
<path d="M351 201 364 209 351 227 337 236 323 247 311 241 327 226 338 215Z" fill="var(--skin)"/>
<path d="M356 209 346 223 333 234 317 244 323 247 337 236 351 227 364 209 359 206Z" fill="var(--skin-shade)" stroke="none"/>
<path d="M372 182Q361 184 356 193L350 203 362 211 376 197 380 188Z" fill="var(--suit)"/>
<path d="M350 201 363 207 374 196 376 199 363 212 348 205Z" fill="var(--trim)"/>
${wristbands ? '<path d="m317 230 13 10-7 7-13-10Z" fill="var(--trim)"/>' : ''}
<path d="m311 239 12 7-5 6-12 0-5-3 1-4 5-2Z" fill="var(--skin)"/>
<path d="m312 247-5 2m10 0-5 2m35-35-6 8" stroke-width="1.2"/>
<g class="rock-fallen-head" transform="translate(384 150) rotate(16) scale(.86) translate(-130 -52)">${fighterHead(characterId, false, appearance)}</g>
</g>
<g fill="#b4ac90" stroke="#676d5b" stroke-width="1.5">
<path d="m438 288 16-11 20 13-11 13-22-3Zm-318 28 12-5 9 8-7 6-15-2Zm258 37 12-9 16 5-3 11-20 2Z"/>
<path d="m454 279 2 12 17-1m-341 25 1 8"/>
</g>
<path d="m95 299 45-4m-23 38 37-6m20-21 19-2m173 24 18-4m-7 46 47-4m-54 14 28-3" stroke="#767b65" stroke-width="1.2"/>
<path d="M82 298q17-10 33-2m-2 8q22-9 39-4m29 25q20-9 41-5" stroke="#e8dcc0" stroke-width="7" opacity=".7"/>
</g>
</svg>`;
}
function beamFinisherArt(hitIndex: 0 | 1, appearances?: [FighterAppearance, FighterAppearance]) {
const attacker = 1 - hitIndex;
const victim = appearances?.[hitIndex];
const maskId = `beam-remnant-${hitIndex}`;
const reactionMaskId = `beam-reaction-${hitIndex}`;
return `<div class="cutscene-page cutscene-beam-page" aria-hidden="true">
<div class="cutscene-panel cutscene-attack" data-panel="attack">
<div class="cutscene-ink-strokes"></div>
<div class="fighter cutscene-closeup" data-fighter="${attacker}" data-action="beam"><div class="character-direction">${fighterArt(attacker, true, appearances?.[attacker])}</div></div>
</div>
<div class="cutscene-panel cutscene-impact beam-impact" data-panel="impact">
<div class="beam-rush-lines"></div>
<div class="fighter beam-impact-victim" data-fighter="${hitIndex}" data-hit="true"><div class="character-direction">${fighterArt(hitIndex, false, appearances?.[hitIndex])}</div></div>
<div class="beam-launch" data-caster="${attacker}">
<div class="fighter beam-caster" data-fighter="${attacker}" data-action="beam"><div class="character-direction">${fighterArt(attacker, false, appearances?.[attacker])}</div></div>
<svg class="beam-ray" viewBox="0 0 260 300" fill="none" aria-hidden="true">
<path d="M182 122Q195 103 225 99L5000-3400V3800L213 138Q191 140 182 122Z" fill="#89d7e6" stroke="#477d90" stroke-width="2"/>
<path d="M193 119 225 103 5000-3100V3500L215 134Z" fill="#d4faff"/>
<path d="M204 116 228 109 5000-2470V2760L220 128Z" fill="#fcffff"/>
<path d="m218 108 193-120-164 117 380-197m-411 222 222 151-192-145 376 206" stroke="#f4ffff" stroke-width="4"/>
<path d="m236 103 154-85m-149 114 212 129m-177-150 340-159m-350 176 446 218" stroke="#79c9df" stroke-width="2"/>
<path d="M184 122q12-20 36-19m-28 28q12 8 27 1" stroke="#f7ffff" stroke-width="5"/>
</svg>
</div>
</div>
<div class="cutscene-panel cutscene-fallen beam-aftermath" data-panel="fallen">
<div class="beam-reaction" data-panel="reaction">
<div class="beam-shock-lines"></div>
<div class="fighter beam-reaction-portrait" data-fighter="${hitIndex}" data-hit="true">
<svg class="beam-reaction-art" viewBox="0 0 136 130" fill="none" aria-hidden="true" style="${fighterPalette(hitIndex, appearances?.[hitIndex])}">
<defs><mask id="${reactionMaskId}" maskUnits="userSpaceOnUse" x="0" y="0" width="136" height="130">
<rect width="136" height="130" fill="white"/>
<path d="M0 0H28L20 12 30 19 22 25 28 32 19 42 28 46 23 56 32 61 24 72 32 80 22 92 29 99 16 109 25 119 17 130H0Z" fill="black"/>
<path class="beam-reaction-erosion" d="M85 0 79 12 90 20 77 26 89 34 73 42 83 50 72 57 79 68 68 77 78 85 63 97 75 112 58 130H136V0Z" fill="black"/>
<path d="m26 47 11-5-6 7 7 5-10-3Zm48 18-10 6 6 3-9 8 4-11Zm-43 37 14-7-7 8 3 6-10-3Z" fill="black"/>
</mask></defs>
<g class="beam-reaction-remnant" mask="url(#${reactionMaskId})">${fighterArt(hitIndex, true, appearances?.[hitIndex])}</g>
<g class="beam-reaction-light" stroke="#c1f5fa" stroke-width=".8" stroke-linejoin="round">
<path d="m84 1-3 11 11 8-12 6 11 8-15 8 10 8-11 7 7 11-11 9 10 8-15 12 12 15-17 18h8l17-19-11-14 13-12-9-10 9-8-8-10 10-8-9-8 14-7-11-9 11-6-11-9 4-10Z" fill="#fcffff"/>
<path d="m28 30-6 12 8 4-4 11 9 4-6 11 6 7-7-3 3-13-9-5 3-11-8-4 8-13Zm44 31-8 10 6 3-10 11 6-12-6-2Z" fill="#fcffff"/>
<path d="m88 30 8-5-3 6Zm-3 31 6-3-2 5Zm-8 33 7-4-3 6Z" fill="var(--skin)" stroke="none"/>
<path d="m87 13 7-4-2 5Zm9 4 4-2-1 3Z" fill="var(--${victim?.hairStyle === 'bald' ? 'skin' : 'hair'})" stroke="none"/>
<path d="m81 106 7-3-3 5Zm-12 17 9-4-4 5Z" fill="var(--suit)" stroke="none"/>
<path d="m94 43 18-9m-24 40 22-11m-37 51 27-15m-77-32 12-7" fill="none"/>
</g>
</svg>
</div>
</div>
<div class="beam-dissolve" data-panel="dissolve">
<div class="beam-dissolve-lines"></div>
<svg class="beam-dissolve-art" viewBox="0 0 260 300" fill="none" aria-hidden="true" style="${fighterPalette(hitIndex, appearances?.[hitIndex])}">
<defs><mask id="${maskId}" maskUnits="userSpaceOnUse" x="0" y="0" width="260" height="300">
<rect width="260" height="300" fill="black"/>
<path d="m90 27 21-8-8 10 12 4-16 9-13-3 9-5Zm23 18 19-7-6 10-13 4Zm-10 31 13-9-4 12 8 6-16 5-7-7Z" fill="white"/>
<path d="m117 110 16-8-5 13-13 5Zm-29 23 12-6-4 9-10 3Zm31 32 18-9-7 11-12 4Zm-4 30 10-6-4 8Z" fill="#888"/>
</mask></defs>
<g class="fighter beam-remnant" data-fighter="${hitIndex}" data-hit="true" mask="url(#${maskId})">${fighterArt(hitIndex, false, appearances?.[hitIndex])}</g>
<g class="beam-vanish-wake" stroke="#81b3bb" stroke-linecap="round">
<path d="M59 63q30-19 68-19m-60 30 29-12m10-20 48-13M72 113q27-22 57-23m-49 31 26-13m20-22 49-13M82 166q23-17 47-19m-36 26 27-14m9-20 43-12M94 214l35-16m-19 27 32-19" stroke-width="1.4"/>
<path d="m54 76 18-8m-9 49 17-7m-4 62 16-8m-5 63 24-13m24-165 28-11m-25 66 44-17m-45 77 28-12" stroke-width=".7"/>
</g>
<g class="beam-fragments" stroke="none">
<path d="m118 35 7-4-3 5Zm16-10 5-2-2 3Zm-28 27 5-3-1 3Z" fill="var(--${victim?.hairStyle === 'bald' ? 'skin' : 'hair'})"/>
<path d="m124 73 7-3-4 5Zm-14 16 5-2-2 3Z" fill="var(--skin)"/>
<path d="m134 111 8-4-4 5Zm-17 20 5-3-2 4Zm17 28 7-3-3 4Zm-10 28 5-2-2 3Z" fill="var(--suit)"/>
<path d="m128 225 5-3-2 4Zm13-15 4-2-1 2Z" fill="${equipped(victim, 'shoes') ? '#30383c' : 'var(--skin)'}"/>
</g>
</svg>
</div>
</div>
</div>`;
}
export function finisherArt(hitIndex: 0 | 1, attack: ActionId, appearances?: [FighterAppearance, FighterAppearance]) {
if (attack === 'beam') return beamFinisherArt(hitIndex, appearances);
const attacker = 1 - hitIndex;
const speedLines = Array.from({ length: 44 }, (_, i) => {
const angle = i * Math.PI / 22, inner = 85 + (i % 4) * 21;
const x = (r: number) => (400 + Math.cos(angle) * r).toFixed(1);
const y = (r: number) => (170 + Math.sin(angle) * r * .65).toFixed(1);
return `<path d="M${x(inner)} ${y(inner)} ${x(740)} ${y(740)}" stroke-width="${i % 3 === 0 ? 5 : 1.5}"/>`;
}).join('');
return `<div class="cutscene-page" aria-hidden="true">
<div class="cutscene-panel cutscene-attack" data-panel="attack">
<div class="cutscene-ink-strokes"></div>
<div class="fighter cutscene-closeup" data-fighter="${attacker}" data-action="${attack}"><div class="character-direction">${fighterArt(attacker, true, appearances?.[attacker])}</div></div>
</div>
<div class="cutscene-panel cutscene-impact" data-panel="impact">
<svg class="cutscene-speedlines" viewBox="0 0 800 340" preserveAspectRatio="none" fill="none"><g stroke="#292d2b">${speedLines}</g></svg>
${[0, 1].map(index => `<div class="fighter cutscene-combatant" data-fighter="${index}" data-hit="${index === hitIndex}"${index === attacker ? ` data-action="${attack}"` : ''}><div class="character-direction">${fighterArt(index, false, appearances?.[index])}</div></div>`).join('')}
<div class="cutscene-shot"></div>
<svg class="cutscene-contact" viewBox="0 0 240 240"><path d="m122 6 11 62 46-50-22 64 73-19-51 45 55 19-63 10 40 61-62-29-18 66-17-62-67 49 33-66-71-3 58-38L12 57l69 28-3-68 31 59Z" fill="#f6eed2" stroke="#28292a" stroke-width="3"/><path d="m119 55 10 42 38-24-22 40 42 11-43 14 15 33-35-24-20 42-2-47-39 8 30-26-29-26 43 11Z" fill="#a84a34"/></svg>
<strong class="cutscene-sfx">쾅!</strong>
</div>
<div class="cutscene-panel cutscene-fallen" data-panel="fallen">${fallenFighterArt(hitIndex, appearances?.[hitIndex])}</div>
</div>`;
}
export function ashPileArt() {
return `<svg class="ash-pile-art" viewBox="0 0 260 300" fill="none" aria-hidden="true">
<path d="m39 279 28-7 39-3 52 1 42 5 20 8-33 5-65 2-55-4Z" fill="#46504b" opacity=".18"/>
<g stroke="#414a47" stroke-width="2" stroke-linejoin="round" stroke-linecap="round">
<path d="m49 278 18-7 12-1 9-8 15 1 11-9 14 3 12-6 13 7 15-1 10 8 15 2 8 9 13 5-23 3-27-1-18 4-30-2-23 1-19-5-14 1Z" fill="#898e87"/>
<path d="m64 277 25-4 18 3 22-6 18 4 20-3 17 7 18 1-11 5-27-1-18 4-30-2-23 1-19-5-14 1Z" fill="#606a65" stroke="none"/>
<path d="m88 269 13-3 10 3m10-8 9 4 11-3m12 4 12 2m-58 10 13-3m20 4 13-2m18-1 8 3" stroke-width="1.3"/>
<path d="m41 282 5-3 5 3-5 2Zm175-8 5 1-1 3-6-1Zm-141 16 6-2 5 2-6 1Zm103-1 8-2 4 2-7 1Z" fill="#707973" stroke-width="1"/>
</g>
<g class="ash-drift" fill="#66716b">
<path d="m95 241 4-3 3 2-4 3Zm37-16 3-4 2 2-2 4Zm29 18 4-2 2 2-4 2Z"/>
</g>
<g class="ash-drift ash-drift-late" fill="#858e86">
<path d="m111 234 3-2 2 2-3 2Zm38-20 2-3 2 2-2 2Zm27 22 3-2 2 1-3 3Z"/>
</g>
</svg>`;
}
export function actionIcon(action: ActionId) {
const shapes: Record<ActionId, string> = {
charge: '<path d="M29 3 9 28h13l-3 17 21-26H27Z"/>',
blast: '<path fill-rule="evenodd" d="M27 8h9l10 16-10 16h-9L15 30H3l9-6-9-6h12Zm2 10-5 6 5 6h4l4-6-4-6Z"/><path d="M3 8h14v6H3Zm0 26h14v6H3Z"/>',
guard: '<path fill-rule="evenodd" d="m24 3 19 7v14l-6 12-13 9-13-9-6-12V10Zm0 9-10 4v8l5 8 5 4 5-4 5-8v-8Z"/>',
beam: '<path fill-rule="evenodd" d="m4 18 9-6 10 5L45 4v40L23 31l-10 5-9-6Zm8 3v6h9l24 8V13l-24 8Z"/><path d="M17 22h28v4H17Z"/>',
dodge: '<path d="m32 3 8 3-3 8-8-3ZM23 14l10 3 4 8h8v6H32l-4-6-3 8 7 4-2 9h-7l1-6-8-5 4-13h-6l-4 7H3l7-14Zm-10 23h7l-7 8H4ZM3 5h18v5H3Z"/>',
};
return `<svg viewBox="0 0 48 48" fill="currentColor" aria-hidden="true">${shapes[action]}</svg>`;
}
export const checkIcon = '<svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="m5 12 5 5L20 7" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/></svg>';