Add skill icons to level-up offer cards

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-08-18 13:26:41 +02:00
co-authored by Qwen-Coder
parent 6f207ba1a0
commit 9e3e5c70e0
3 changed files with 228 additions and 2 deletions
+14 -2
View File
@@ -167,7 +167,7 @@
display: flex; gap: 20px; display: flex; gap: 20px;
} }
.offer-card { .offer-card {
width: 230px; min-height: 140px; background: rgba(20,20,30,0.9); width: 230px; min-height: 220px; background: rgba(20,20,30,0.9);
border: 2px solid #888; padding: 14px; cursor: pointer; border: 2px solid #888; padding: 14px; cursor: pointer;
display: flex; flex-direction: column; gap: 10px; display: flex; flex-direction: column; gap: 10px;
transition: transform 0.1s ease, border-color 0.1s ease; transition: transform 0.1s ease, border-color 0.1s ease;
@@ -177,8 +177,17 @@
border-color: #ffcc00; background: rgba(60,50,20,0.95); border-color: #ffcc00; background: rgba(60,50,20,0.95);
transform: scale(1.04); box-shadow: 0 0 18px rgba(255,204,0,0.4); transform: scale(1.04); box-shadow: 0 0 18px rgba(255,204,0,0.4);
} }
.offer-icon {
width: 76px; height: 76px; margin: 2px auto 0;
filter: drop-shadow(0 3px 6px rgba(0,0,0,0.55));
transition: filter 0.1s ease;
}
.offer-icon svg { width: 100%; height: 100%; display: block; }
.offer-card:hover .offer-icon, .offer-card.selected .offer-icon {
filter: drop-shadow(0 0 9px rgba(255,204,0,0.5));
}
.offer-title { .offer-title {
color: #ffcc00; font-size: 18px; font-weight: bold; color: #ffcc00; font-size: 18px; font-weight: bold; text-align: center;
} }
.offer-desc { .offer-desc {
color: rgba(255,255,255,0.85); font-size: 13px; line-height: 1.5; color: rgba(255,255,255,0.85); font-size: 13px; line-height: 1.5;
@@ -244,16 +253,19 @@
<h2>Level Up!</h2> <h2>Level Up!</h2>
<div id="offer-row"> <div id="offer-row">
<div id="offer-0" class="offer-card"> <div id="offer-0" class="offer-card">
<div id="offer-icon-0" class="offer-icon"></div>
<div id="offer-title-0" class="offer-title"></div> <div id="offer-title-0" class="offer-title"></div>
<div id="offer-desc-0" class="offer-desc"></div> <div id="offer-desc-0" class="offer-desc"></div>
<div class="offer-hint">[1] auswählen</div> <div class="offer-hint">[1] auswählen</div>
</div> </div>
<div id="offer-1" class="offer-card"> <div id="offer-1" class="offer-card">
<div id="offer-icon-1" class="offer-icon"></div>
<div id="offer-title-1" class="offer-title"></div> <div id="offer-title-1" class="offer-title"></div>
<div id="offer-desc-1" class="offer-desc"></div> <div id="offer-desc-1" class="offer-desc"></div>
<div class="offer-hint">[2] auswählen</div> <div class="offer-hint">[2] auswählen</div>
</div> </div>
<div id="offer-2" class="offer-card"> <div id="offer-2" class="offer-card">
<div id="offer-icon-2" class="offer-icon"></div>
<div id="offer-title-2" class="offer-title"></div> <div id="offer-title-2" class="offer-title"></div>
<div id="offer-desc-2" class="offer-desc"></div> <div id="offer-desc-2" class="offer-desc"></div>
<div class="offer-hint">[3] auswählen</div> <div class="offer-hint">[3] auswählen</div>
+4
View File
@@ -19,6 +19,7 @@ import { Cross, initCrossModel } from './Cross';
import { Arena } from './Arena'; import { Arena } from './Arena';
import { SlashEffect, isInSlashArc } from './SwordTrail'; import { SlashEffect, isInSlashArc } from './SwordTrail';
import { SkillSystem, getSkill, type SkillOffer } from './Skills'; import { SkillSystem, getSkill, type SkillOffer } from './Skills';
import { skillIcon } from './SkillIcons';
import { initSounds, playSound, resumeContext } from './SoundManager'; import { initSounds, playSound, resumeContext } from './SoundManager';
const SPAWN_TIME = 10; const SPAWN_TIME = 10;
@@ -92,6 +93,7 @@ export class Game {
private uiShieldText!: HTMLElement; private uiShieldText!: HTMLElement;
private uiLevelUp!: HTMLElement; private uiLevelUp!: HTMLElement;
private uiConfirmBtn!: HTMLButtonElement; private uiConfirmBtn!: HTMLButtonElement;
private uiOfferIcons: HTMLElement[] = [];
private uiOfferTitles: HTMLElement[] = []; private uiOfferTitles: HTMLElement[] = [];
private uiOfferDescs: HTMLElement[] = []; private uiOfferDescs: HTMLElement[] = [];
private mouseOnCanvas = false; private mouseOnCanvas = false;
@@ -180,6 +182,7 @@ export class Game {
for (let i = 0; i < 3; i++) { for (let i = 0; i < 3; i++) {
const card = document.getElementById(`offer-${i}`)!; const card = document.getElementById(`offer-${i}`)!;
card.addEventListener('click', () => this.selectOffer(i)); card.addEventListener('click', () => this.selectOffer(i));
this.uiOfferIcons.push(document.getElementById(`offer-icon-${i}`)!);
this.uiOfferTitles.push(document.getElementById(`offer-title-${i}`)!); this.uiOfferTitles.push(document.getElementById(`offer-title-${i}`)!);
this.uiOfferDescs.push(document.getElementById(`offer-desc-${i}`)!); this.uiOfferDescs.push(document.getElementById(`offer-desc-${i}`)!);
} }
@@ -540,6 +543,7 @@ export class Game {
const offer = this.offers[i]; const offer = this.offers[i];
const skill = getSkill(offer.id); const skill = getSkill(offer.id);
const isNew = offer.nextLevel === 1; const isNew = offer.nextLevel === 1;
this.uiOfferIcons[i].innerHTML = skillIcon(offer.id);
title.textContent = `${skill.name}${isNew ? ' (NEU)' : ` · Stufe ${offer.nextLevel}`}`; title.textContent = `${skill.name}${isNew ? ' (NEU)' : ` · Stufe ${offer.nextLevel}`}`;
desc.textContent = skill.describe(offer.nextLevel); desc.textContent = skill.describe(offer.nextLevel);
card.style.display = ''; card.style.display = '';
+210
View File
@@ -0,0 +1,210 @@
const ICONS: Record<string, string> = {
fireball: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<radialGradient id="fbCore" cx="50%" cy="62%" r="60%">
<stop offset="0%" stop-color="#fff6c0"/>
<stop offset="40%" stop-color="#ffc23d"/>
<stop offset="78%" stop-color="#ff7b1f"/>
<stop offset="100%" stop-color="#d43d17"/>
</radialGradient>
<linearGradient id="fbFlame" x1="0" y1="1" x2="0" y2="0">
<stop offset="0%" stop-color="#ff8a2a"/>
<stop offset="100%" stop-color="#ffdf6b"/>
</linearGradient>
</defs>
<circle cx="32" cy="42" r="21" fill="#ff8020" opacity="0.16"/>
<path d="M32 3 C38 12 45 17 45 27 C45 34 41 38 37 40 L27 40 C23 38 19 34 19 27 C19 17 26 12 32 3 Z" fill="url(#fbFlame)" opacity="0.95"/>
<path d="M32 12 C35 17 39 20 39 26 C39 31 36 34 32 35 C28 34 25 31 25 26 C25 20 29 17 32 12 Z" fill="#fff2b0" opacity="0.9"/>
<circle cx="32" cy="43" r="14" fill="url(#fbCore)" stroke="#8a3014" stroke-width="2"/>
<ellipse cx="27" cy="38" rx="5" ry="3.2" fill="#fff8d8" opacity="0.7" transform="rotate(-25 27 38)"/>
<circle cx="13" cy="22" r="1.6" fill="#ffd75e"/>
<circle cx="50" cy="15" r="1.3" fill="#ffd75e"/>
<circle cx="52" cy="27" r="1.8" fill="#ffb347"/>
<circle cx="15" cy="38" r="1.2" fill="#ffb347"/>
</svg>`,
teleport: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="tpRing" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#59e0ff"/>
<stop offset="100%" stop-color="#8f6bff"/>
</linearGradient>
<radialGradient id="tpGlow" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#59e0ff" stop-opacity="0.3"/>
<stop offset="100%" stop-color="#59e0ff" stop-opacity="0"/>
</radialGradient>
</defs>
<circle cx="32" cy="32" r="27" fill="url(#tpGlow)"/>
<circle cx="32" cy="32" r="23" fill="none" stroke="url(#tpRing)" stroke-width="3.5" stroke-dasharray="11 7" stroke-linecap="round"/>
<circle cx="32" cy="32" r="15" fill="none" stroke="url(#tpRing)" stroke-width="2.5" stroke-dasharray="8 6" stroke-linecap="round" opacity="0.75" transform="rotate(28 32 32)"/>
<path d="M32 21 L40 33 L35 33 L35 43 L29 43 L29 33 L24 33 Z" fill="#eafcff" stroke="#2b7f9e" stroke-width="1.6" stroke-linejoin="round"/>
<path d="M49 10 l2.1 4 4 2.1 -4 2.1 -2.1 4 -2.1 -4 -4 -2.1 4 -2.1 Z" fill="#aef2ff"/>
<path d="M13 43 l1.7 3.2 3.2 1.7 -3.2 1.7 -1.7 3.2 -1.7 -3.2 -3.2 -1.7 3.2 -1.7 Z" fill="#cbb2ff"/>
</svg>`,
chainlightning: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="clBolt" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#fff8c8"/>
<stop offset="45%" stop-color="#ffd83d"/>
<stop offset="100%" stop-color="#ff9d1f"/>
</linearGradient>
</defs>
<circle cx="32" cy="32" r="25" fill="#ffd83d" opacity="0.12"/>
<path d="M37 3 L17 33 L28 33 L23 61 L47 27 L34 27 Z" fill="url(#clBolt)" stroke="#7a4a08" stroke-width="2" stroke-linejoin="round"/>
<path d="M13 10 L8 20 L12 20 L9 29 L17 18 L13 18 Z" fill="#ffd83d" opacity="0.85"/>
<path d="M52 38 L47 47 L51 47 L48 56 L56 45 L52 45 Z" fill="#ffd83d" opacity="0.85"/>
<path d="M15 17 Q22 21 22 29" fill="none" stroke="#ffe98a" stroke-width="1.5" stroke-dasharray="3 3" opacity="0.8"/>
<path d="M49 46 Q42 42 41 34" fill="none" stroke="#ffe98a" stroke-width="1.5" stroke-dasharray="3 3" opacity="0.8"/>
</svg>`,
swordRange: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="srBlade" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#f4f9ff"/>
<stop offset="50%" stop-color="#b9c8d8"/>
<stop offset="100%" stop-color="#7e93a8"/>
</linearGradient>
</defs>
<circle cx="32" cy="32" r="27" fill="#ffcc00" opacity="0.06"/>
<circle cx="32" cy="32" r="27" fill="none" stroke="#ffcc00" stroke-width="2" stroke-dasharray="6 7" opacity="0.85"/>
<path d="M32 6 L38 14 L38 36 L26 36 L26 14 Z" fill="url(#srBlade)" stroke="#3c4a58" stroke-width="1.5" stroke-linejoin="round"/>
<path d="M32 7 L32 36" stroke="#ffffff" stroke-width="1.4" opacity="0.7"/>
<rect x="21" y="36" width="22" height="5" rx="2" fill="#ffcc00" stroke="#7a5c00" stroke-width="1.2"/>
<rect x="29.2" y="41" width="5.6" height="11" rx="1.5" fill="#8a5a2b" stroke="#4a2f14" stroke-width="1.2"/>
<circle cx="32" cy="54.5" r="3.2" fill="#ffcc00" stroke="#7a5c00" stroke-width="1.2"/>
</svg>`,
swordDamage: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="sdBlade" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#ffffff"/>
<stop offset="45%" stop-color="#c8d6e4"/>
<stop offset="100%" stop-color="#8298ac"/>
</linearGradient>
</defs>
<circle cx="32" cy="32" r="26" fill="#ff4444" opacity="0.08"/>
<path d="M56 8 L33.9 37.9 L26.1 30.1 Z" fill="url(#sdBlade)" stroke="#3c4a58" stroke-width="1.5" stroke-linejoin="round"/>
<path d="M56 8 L30 34" stroke="#ffffff" stroke-width="1.4" opacity="0.75"/>
<path d="M35.3 42.9 L38.9 39.3 L24.7 25.1 L21.1 28.7 Z" fill="#ffcc00" stroke="#7a5c00" stroke-width="1.2" stroke-linejoin="round"/>
<path d="M31.8 35.8 L23.3 44.3 L19.7 40.7 L28.2 32.2 Z" fill="#8a5a2b" stroke="#4a2f14" stroke-width="1.2" stroke-linejoin="round"/>
<circle cx="20.3" cy="43.7" r="3.4" fill="#ffcc00" stroke="#7a5c00" stroke-width="1.2"/>
<path d="M46 11 l1.8 5.2 5.2 1.8 -5.2 1.8 -1.8 5.2 -1.8 -5.2 -5.2 -1.8 5.2 -1.8 Z" fill="#ffffff" opacity="0.9"/>
<path d="M14 52 l1.2 3.4 3.4 1.2 -3.4 1.2 -1.2 3.4 -1.2 -3.4 -3.4 -1.2 3.4 -1.2 Z" fill="#ffd75e" opacity="0.85"/>
</svg>`,
regen: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="rgCross" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#b8ffb0"/>
<stop offset="100%" stop-color="#2fae3f"/>
</linearGradient>
</defs>
<circle cx="32" cy="32" r="25" fill="#44cc44" opacity="0.12"/>
<circle cx="32" cy="32" r="24" fill="none" stroke="#7be27b" stroke-width="1.5" stroke-dasharray="2 5" stroke-linecap="round" opacity="0.7"/>
<path d="M26 12 h12 v14 h14 v12 h-14 v14 h-12 v-14 h-14 v-12 h14 Z" fill="url(#rgCross)" stroke="#145a1e" stroke-width="2" stroke-linejoin="round"/>
<path d="M50 12 l1.6 3 3 1.6 -3 1.6 -1.6 3 -1.6 -3 -3 -1.6 3 -1.6 Z" fill="#d6ffd0"/>
<circle cx="13" cy="17" r="1.8" fill="#b8ffb0"/>
<circle cx="52" cy="48" r="1.5" fill="#b8ffb0"/>
</svg>`,
lifesteal: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="lsDrop" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#ff6b7d"/>
<stop offset="55%" stop-color="#c2274f"/>
<stop offset="100%" stop-color="#6e1030"/>
</linearGradient>
</defs>
<circle cx="32" cy="37" r="24" fill="#c2274f" opacity="0.12"/>
<path d="M32 6 C38 15 48 23 48 37 A16 16 0 0 1 16 37 C16 23 26 15 32 6 Z" fill="url(#lsDrop)" stroke="#3d0a1c" stroke-width="2"/>
<path d="M26 27 L29.3 37 L32.6 27 Z" fill="#fff" stroke="#3d0a1c" stroke-width="0.8"/>
<path d="M33.4 27 L36.7 37 L40 27 Z" fill="#fff" stroke="#3d0a1c" stroke-width="0.8"/>
<ellipse cx="24" cy="36" rx="3.5" ry="5" fill="#ff9ab0" opacity="0.5" transform="rotate(18 24 36)"/>
<path d="M4 29 H12 M12 29 L8.5 26 M12 29 L8.5 32" fill="none" stroke="#ff9ab0" stroke-width="2" stroke-linecap="round"/>
<path d="M60 41 H52 M52 41 L55.5 38 M52 41 L55.5 44" fill="none" stroke="#ff9ab0" stroke-width="2" stroke-linecap="round"/>
</svg>`,
shield: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="shBody" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#6fc3ff"/>
<stop offset="60%" stop-color="#2e7cd6"/>
<stop offset="100%" stop-color="#1b4d94"/>
</linearGradient>
</defs>
<circle cx="32" cy="32" r="26" fill="#3399ff" opacity="0.12"/>
<path d="M32 5 L53 13 V30 C53 44 45 53 32 59 C19 53 11 44 11 30 V13 Z" fill="url(#shBody)" stroke="#0f2f5c" stroke-width="2.5" stroke-linejoin="round"/>
<path d="M32 10 L48 16 V30 C48 41 42 48 32 53 C22 48 16 41 16 30 V16 Z" fill="none" stroke="#a8dcff" stroke-width="1.5" opacity="0.7"/>
<path d="M32 10 L32 53" stroke="#a8dcff" stroke-width="1.5" opacity="0.5"/>
<path d="M47 8 l1.6 3 3 1.6 -3 1.6 -1.6 3 -1.6 -3 -3 -1.6 3 -1.6 Z" fill="#dff1ff"/>
</svg>`,
maxHp: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<radialGradient id="vhHeart" cx="35%" cy="30%" r="80%">
<stop offset="0%" stop-color="#ff8f8f"/>
<stop offset="55%" stop-color="#e42536"/>
<stop offset="100%" stop-color="#8c0f1e"/>
</radialGradient>
</defs>
<circle cx="32" cy="32" r="26" fill="#e42536" opacity="0.12"/>
<path d="M32 55 C22 46 9 37 9 24 C9 15 16 9 23.5 9 C27.5 9 30.7 11 32 14.5 C33.3 11 36.5 9 40.5 9 C48 9 55 15 55 24 C55 37 42 46 32 55 Z" fill="url(#vhHeart)" stroke="#4d0812" stroke-width="2.5" stroke-linejoin="round"/>
<ellipse cx="23" cy="20" rx="6" ry="4" fill="#ffd0d0" opacity="0.6" transform="rotate(-20 23 20)"/>
<path d="M29 23 h6 v7 h7 v6 h-7 v7 h-6 v-7 h-7 v-6 h7 Z" fill="#fff" opacity="0.92"/>
</svg>`,
thorns: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="thThorn" x1="0" y1="1" x2="0" y2="0">
<stop offset="0%" stop-color="#3e7a2a"/>
<stop offset="100%" stop-color="#9fdc6e"/>
</linearGradient>
<path id="thSpike" d="M32 5.5 L36.5 16 L27.5 16 Z"/>
</defs>
<circle cx="32" cy="32" r="25" fill="#5a9e3a" opacity="0.1"/>
<g fill="url(#thThorn)" stroke="#1e3d12" stroke-width="1.5" stroke-linejoin="round">
<use href="#thSpike"/>
<use href="#thSpike" transform="rotate(45 32 32)"/>
<use href="#thSpike" transform="rotate(90 32 32)"/>
<use href="#thSpike" transform="rotate(135 32 32)"/>
<use href="#thSpike" transform="rotate(180 32 32)"/>
<use href="#thSpike" transform="rotate(225 32 32)"/>
<use href="#thSpike" transform="rotate(270 32 32)"/>
<use href="#thSpike" transform="rotate(315 32 32)"/>
</g>
<circle cx="32" cy="32" r="13" fill="none" stroke="#2f5c1e" stroke-width="5.5"/>
<circle cx="32" cy="32" r="13" fill="none" stroke="#78b954" stroke-width="1.5" opacity="0.6"/>
</svg>`,
speed: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="spBoot" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#ffe08a"/>
<stop offset="100%" stop-color="#d99a2b"/>
</linearGradient>
</defs>
<circle cx="34" cy="32" r="26" fill="#ffe08a" opacity="0.08"/>
<path d="M4 18 H15" stroke="#ffe08a" stroke-width="3" stroke-linecap="round" opacity="0.85"/>
<path d="M2 28 H12" stroke="#ffe08a" stroke-width="3" stroke-linecap="round" opacity="0.7"/>
<path d="M6 38 H14" stroke="#ffe08a" stroke-width="3" stroke-linecap="round" opacity="0.55"/>
<path d="M23 10 h11 v20 c0 2.5 1 4 3.5 5 l9.5 3.5 c5 1.8 8 5.2 8 9.5 v4 h-32 Z" fill="url(#spBoot)" stroke="#6e4a12" stroke-width="2" stroke-linejoin="round"/>
<rect x="20.5" y="5" width="16" height="6.5" rx="2.5" fill="#ffcf5e" stroke="#6e4a12" stroke-width="2"/>
<path d="M27 48 h24" stroke="#6e4a12" stroke-width="2" opacity="0.6"/>
</svg>`,
};
export function skillIcon(id: string): string {
return ICONS[id] ?? '';
}