xhfmjf
This commit is contained in:
parent
61043ebdd4
commit
4d114385b3
@ -67,9 +67,8 @@
|
|||||||
|
|
||||||
/* Locked: dunkle Abdunklung über dem Bildkreis */
|
/* Locked: dunkle Abdunklung über dem Bildkreis */
|
||||||
.ds-locked .ds-overlay {
|
.ds-locked .ds-overlay {
|
||||||
fill: rgba(0,0,5,.62);
|
fill: rgba(0,0,5,.35);
|
||||||
stroke: rgba(50,60,100,.45);
|
stroke: none;
|
||||||
stroke-width: .8;
|
|
||||||
}
|
}
|
||||||
.ds-locked .ds-num {
|
.ds-locked .ds-num {
|
||||||
fill: rgba(120,130,160,.38);
|
fill: rgba(120,130,160,.38);
|
||||||
@ -79,9 +78,8 @@
|
|||||||
|
|
||||||
/* Available: transparente Mitte, leuchtender Rand */
|
/* Available: transparente Mitte, leuchtender Rand */
|
||||||
.ds-available .ds-overlay {
|
.ds-available .ds-overlay {
|
||||||
fill: rgba(40,100,255,.08);
|
fill: transparent;
|
||||||
stroke: rgba(100,200,255,.9);
|
stroke: none;
|
||||||
stroke-width: 1.2;
|
|
||||||
}
|
}
|
||||||
.ds-available .ds-num {
|
.ds-available .ds-num {
|
||||||
fill: #fff;
|
fill: #fff;
|
||||||
@ -112,9 +110,9 @@
|
|||||||
|
|
||||||
/* Done: grüne Einfärbung */
|
/* Done: grüne Einfärbung */
|
||||||
.ds-done .ds-overlay {
|
.ds-done .ds-overlay {
|
||||||
fill: rgba(30,160,70,.35);
|
fill: rgba(30,160,70,.55);
|
||||||
stroke: rgba(80,220,110,.85);
|
stroke: rgba(80,220,110,.7);
|
||||||
stroke-width: 1.2;
|
stroke-width: .8;
|
||||||
}
|
}
|
||||||
.ds-done .ds-check {
|
.ds-done .ds-check {
|
||||||
fill: #fff;
|
fill: #fff;
|
||||||
|
|||||||
@ -843,18 +843,6 @@ function updateHpDisplay(slot, currentHp, maxHp) {
|
|||||||
orbEl.style.boxShadow = `0 0 12px ${color}cc`;
|
orbEl.style.boxShadow = `0 0 12px ${color}cc`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Avatar-Schüttelanimation + roter Flash */
|
|
||||||
if (avEl) {
|
|
||||||
avEl.style.transition = 'transform 0.07s ease';
|
|
||||||
avEl.style.transform = 'scale(1.07)';
|
|
||||||
setTimeout(() => { avEl.style.transform = 'scale(0.96)'; }, 70);
|
|
||||||
setTimeout(() => { avEl.style.transform = ''; }, 150);
|
|
||||||
|
|
||||||
const flash = document.createElement('div');
|
|
||||||
flash.style.cssText = 'position:absolute;inset:0;border-radius:inherit;background:rgba(220,50,50,0.4);z-index:20;pointer-events:none;';
|
|
||||||
avEl.appendChild(flash);
|
|
||||||
setTimeout(() => flash.remove(), 320);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyHpFromEvent(data) {
|
function applyHpFromEvent(data) {
|
||||||
@ -891,6 +879,17 @@ socket.on('avatar_damaged', data => {
|
|||||||
animation:dmg-float 2.5s ease forwards;`;
|
animation:dmg-float 2.5s ease forwards;`;
|
||||||
avEl.appendChild(dmg);
|
avEl.appendChild(dmg);
|
||||||
setTimeout(() => dmg.remove(), 2500);
|
setTimeout(() => dmg.remove(), 2500);
|
||||||
|
|
||||||
|
/* Schüttelanimation + roter Flash NUR bei Treffer */
|
||||||
|
avEl.style.transition = 'transform 0.07s ease';
|
||||||
|
avEl.style.transform = 'scale(1.07)';
|
||||||
|
setTimeout(() => { avEl.style.transform = 'scale(0.96)'; }, 70);
|
||||||
|
setTimeout(() => { avEl.style.transform = ''; }, 150);
|
||||||
|
|
||||||
|
const flash = document.createElement('div');
|
||||||
|
flash.style.cssText = 'position:absolute;inset:0;border-radius:inherit;background:rgba(220,50,50,0.4);z-index:20;pointer-events:none;';
|
||||||
|
avEl.appendChild(flash);
|
||||||
|
setTimeout(() => flash.remove(), 320);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
/* ── Positionen (% in viewBox 0 0 100 100) ───────────────── */
|
/* ── Positionen (% in viewBox 0 0 100 100) ───────────────── */
|
||||||
const STATIONS = [
|
const STATIONS = [
|
||||||
{ id:1, cx:49.5, cy:86.5 },
|
{ id:1, cx:48.5, cy:87.0 },
|
||||||
{ id:2, cx:37.0, cy:73.0 },
|
{ id:2, cx:36.5, cy:73.5 },
|
||||||
{ id:3, cx:44.5, cy:62.0 },
|
{ id:3, cx:44.0, cy:63.0 },
|
||||||
{ id:4, cx:54.5, cy:53.5 },
|
{ id:4, cx:53.5, cy:54.5 },
|
||||||
{ id:5, cx:49.0, cy:46.0 },
|
{ id:5, cx:47.5, cy:46.5 },
|
||||||
{ id:6, cx:44.5, cy:37.5 },
|
{ id:6, cx:43.5, cy:38.5 },
|
||||||
{ id:7, cx:50.0, cy:22.0 },
|
{ id:7, cx:49.5, cy:23.0 },
|
||||||
];
|
];
|
||||||
const R = 4.8; // Kreis-Radius in SVG-Einheiten
|
const R = 4.8; // Kreis-Radius in SVG-Einheiten
|
||||||
|
|
||||||
@ -90,40 +90,34 @@ function buildSvg(completed) {
|
|||||||
</g>`);
|
</g>`);
|
||||||
|
|
||||||
} else if (isAvail) {
|
} else if (isAvail) {
|
||||||
|
// Available: nur pulsierende Ringe über dem Bild-Kreis, kein eigener Kreis
|
||||||
parts.push(`
|
parts.push(`
|
||||||
<g class="ds-station ${cls}" data-station="${s.id}">
|
<g class="ds-station ${cls}" data-station="${s.id}">
|
||||||
<!-- Pulsierender Ring 1 -->
|
<circle class="ds-ring1" cx="${s.cx}" cy="${s.cy}" r="${R + 1.2}"
|
||||||
<circle class="ds-ring1" cx="${s.cx}" cy="${s.cy}" r="${R + 1.2}">
|
fill="none" stroke="rgba(80,200,255,.8)" stroke-width="1.2">
|
||||||
<animate attributeName="r"
|
<animate attributeName="r"
|
||||||
values="${R+1.2};${R+2.6};${R+1.2}" dur="2s" repeatCount="indefinite"/>
|
values="${R+1.2};${R+2.8};${R+1.2}" dur="2s" repeatCount="indefinite"/>
|
||||||
<animate attributeName="opacity"
|
<animate attributeName="opacity"
|
||||||
values=".7;0;.7" dur="2s" repeatCount="indefinite"/>
|
values=".8;0;.8" dur="2s" repeatCount="indefinite"/>
|
||||||
</circle>
|
</circle>
|
||||||
<!-- Pulsierender Ring 2 (versetzt) -->
|
<circle class="ds-ring2" cx="${s.cx}" cy="${s.cy}" r="${R + 2}"
|
||||||
<circle class="ds-ring2" cx="${s.cx}" cy="${s.cy}" r="${R + 2}">
|
fill="none" stroke="rgba(80,200,255,.4)" stroke-width="0.8">
|
||||||
<animate attributeName="r"
|
<animate attributeName="r"
|
||||||
values="${R+2};${R+3.6};${R+2}" dur="2s" begin=".7s" repeatCount="indefinite"/>
|
values="${R+2};${R+4};${R+2}" dur="2s" begin=".7s" repeatCount="indefinite"/>
|
||||||
<animate attributeName="opacity"
|
<animate attributeName="opacity"
|
||||||
values=".4;0;.4" dur="2s" begin=".7s" repeatCount="indefinite"/>
|
values=".5;0;.5" dur="2s" begin=".7s" repeatCount="indefinite"/>
|
||||||
</circle>
|
</circle>
|
||||||
<!-- Sichtbarer Kreis -->
|
<!-- Transparente Hitbox -->
|
||||||
<circle class="ds-overlay" cx="${s.cx}" cy="${s.cy}" r="${R}"/>
|
|
||||||
<!-- Nummer -->
|
|
||||||
<text class="ds-num" x="${s.cx}" y="${s.cy}"
|
|
||||||
text-anchor="middle" dominant-baseline="central"
|
|
||||||
font-size="${fSize}">${s.id}</text>
|
|
||||||
<!-- Unsichtbare Hitbox -->
|
|
||||||
<circle class="ds-hit" cx="${s.cx}" cy="${s.cy}" r="${R + 1.5}"
|
<circle class="ds-hit" cx="${s.cx}" cy="${s.cy}" r="${R + 1.5}"
|
||||||
data-station="${s.id}"/>
|
fill="transparent" stroke="none" data-station="${s.id}"/>
|
||||||
</g>`);
|
</g>`);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// Locked: unsichtbare Hitfläche, kein eigener Kreis (Bild-Kreis bleibt sichtbar)
|
||||||
parts.push(`
|
parts.push(`
|
||||||
<g class="ds-station ${cls}" data-station="${s.id}">
|
<g class="ds-station ${cls}" data-station="${s.id}">
|
||||||
<circle class="ds-overlay" cx="${s.cx}" cy="${s.cy}" r="${R}"/>
|
<circle fill="rgba(0,0,0,0.35)" stroke="none" cx="${s.cx}" cy="${s.cy}" r="${R}"/>
|
||||||
<text class="ds-num" x="${s.cx}" y="${s.cy}"
|
<circle fill="transparent" stroke="none" cx="${s.cx}" cy="${s.cy}" r="${R + 1}"/>
|
||||||
text-anchor="middle" dominant-baseline="central"
|
|
||||||
font-size="${fSize}">${s.id}</text>
|
|
||||||
</g>`);
|
</g>`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -76,21 +76,41 @@ async function loadAiDeck(station, playerLevel) {
|
|||||||
[maxRarity, deckSize]
|
[maxRarity, deckSize]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (cards.length > 0) {
|
// Wenn nicht genug Karten → Rarity schrittweise erhöhen
|
||||||
console.log(`[HT] KI-Deck Station ${station}: ${cards.length} Karten, max. Rarity ${maxRarity}`);
|
let result = [...cards];
|
||||||
return cards.map(c => ({ ...c, currentCd: 0 }));
|
|
||||||
|
if (result.length < deckSize) {
|
||||||
|
for (let r = maxRarity + 1; r <= 6 && result.length < deckSize; r++) {
|
||||||
|
const [more] = await db.query(
|
||||||
|
`SELECT id, name, image, attack, defends, cooldown, \`range\`, \`race\`, rarity
|
||||||
|
FROM cards WHERE rarity = ? ORDER BY RAND() LIMIT ?`,
|
||||||
|
[r, deckSize - result.length]
|
||||||
|
);
|
||||||
|
result = [...result, ...more];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: beliebige Karten wenn keine mit passender Rarity gefunden
|
// Letzter Fallback: alle Karten ohne Rarity-Filter
|
||||||
console.warn(`[HT] Keine Karten mit Rarity <= ${maxRarity} – Fallback auf alle Karten`);
|
if (result.length === 0) {
|
||||||
const [all] = await db.query(
|
const [all] = await db.query(
|
||||||
`SELECT id, name, image, attack, defends, cooldown, \`range\`, \`race\`, rarity
|
`SELECT id, name, image, attack, defends, cooldown, \`range\`, \`race\`, rarity
|
||||||
FROM cards
|
FROM cards ORDER BY RAND() LIMIT ?`,
|
||||||
ORDER BY RAND()
|
[deckSize]
|
||||||
LIMIT ?`,
|
);
|
||||||
[deckSize]
|
result = all;
|
||||||
);
|
}
|
||||||
return all.map(c => ({ ...c, currentCd: 0 }));
|
|
||||||
|
// Noch immer zu wenig? → vorhandene Karten so oft wiederholen bis deckSize erreicht
|
||||||
|
if (result.length > 0 && result.length < deckSize) {
|
||||||
|
const base = [...result];
|
||||||
|
while (result.length < deckSize) {
|
||||||
|
result.push({ ...base[result.length % base.length] });
|
||||||
|
}
|
||||||
|
console.log(`[HT] KI-Deck aufgefüllt durch Wiederholung: ${base.length} unique → ${result.length} Karten`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[HT] KI-Deck Station ${station}: ${result.length} Karten, max. Rarity ${maxRarity}`);
|
||||||
|
return result.map(c => ({ ...c, currentCd: 0 }));
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[HT] loadAiDeck Fehler:', err);
|
console.error('[HT] loadAiDeck Fehler:', err);
|
||||||
@ -437,8 +457,18 @@ function registerHimmelstorHandlers(io, socket) {
|
|||||||
const aiRoom = htAiRooms.get(matchId);
|
const aiRoom = htAiRooms.get(matchId);
|
||||||
if (!aiRoom) return;
|
if (!aiRoom) return;
|
||||||
|
|
||||||
// Socket + Namen aktualisieren
|
// Socket + Namen + AccountId aktualisieren
|
||||||
aiRoom.playerSocketId = socket.id;
|
aiRoom.playerSocketId = socket.id;
|
||||||
|
// accountId aus arena_join data setzen falls noch nicht gesetzt (Session-Fallback)
|
||||||
|
if (!aiRoom.playerAccountId && data.accountId) {
|
||||||
|
aiRoom.playerAccountId = data.accountId;
|
||||||
|
}
|
||||||
|
// Auch io._arenaRooms synchronisieren
|
||||||
|
const ioRoom2 = io._arenaRooms?.get(matchId);
|
||||||
|
if (ioRoom2 && !ioRoom2.accountIds?.player1 && data.accountId) {
|
||||||
|
if (!ioRoom2.accountIds) ioRoom2.accountIds = {};
|
||||||
|
ioRoom2.accountIds.player1 = data.accountId;
|
||||||
|
}
|
||||||
const myIngameName = data.playerName || 'Du';
|
const myIngameName = data.playerName || 'Du';
|
||||||
const ioRoom = io._arenaRooms?.get(matchId);
|
const ioRoom = io._arenaRooms?.get(matchId);
|
||||||
if (ioRoom) ioRoom.sockets.player1 = socket.id;
|
if (ioRoom) ioRoom.sockets.player1 = socket.id;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user