xhfmjf
This commit is contained in:
parent
61043ebdd4
commit
4d114385b3
@ -67,9 +67,8 @@
|
||||
|
||||
/* Locked: dunkle Abdunklung über dem Bildkreis */
|
||||
.ds-locked .ds-overlay {
|
||||
fill: rgba(0,0,5,.62);
|
||||
stroke: rgba(50,60,100,.45);
|
||||
stroke-width: .8;
|
||||
fill: rgba(0,0,5,.35);
|
||||
stroke: none;
|
||||
}
|
||||
.ds-locked .ds-num {
|
||||
fill: rgba(120,130,160,.38);
|
||||
@ -79,9 +78,8 @@
|
||||
|
||||
/* Available: transparente Mitte, leuchtender Rand */
|
||||
.ds-available .ds-overlay {
|
||||
fill: rgba(40,100,255,.08);
|
||||
stroke: rgba(100,200,255,.9);
|
||||
stroke-width: 1.2;
|
||||
fill: transparent;
|
||||
stroke: none;
|
||||
}
|
||||
.ds-available .ds-num {
|
||||
fill: #fff;
|
||||
@ -112,9 +110,9 @@
|
||||
|
||||
/* Done: grüne Einfärbung */
|
||||
.ds-done .ds-overlay {
|
||||
fill: rgba(30,160,70,.35);
|
||||
stroke: rgba(80,220,110,.85);
|
||||
stroke-width: 1.2;
|
||||
fill: rgba(30,160,70,.55);
|
||||
stroke: rgba(80,220,110,.7);
|
||||
stroke-width: .8;
|
||||
}
|
||||
.ds-done .ds-check {
|
||||
fill: #fff;
|
||||
|
||||
@ -843,18 +843,6 @@ function updateHpDisplay(slot, currentHp, maxHp) {
|
||||
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) {
|
||||
@ -891,6 +879,17 @@ socket.on('avatar_damaged', data => {
|
||||
animation:dmg-float 2.5s ease forwards;`;
|
||||
avEl.appendChild(dmg);
|
||||
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) ───────────────── */
|
||||
const STATIONS = [
|
||||
{ id:1, cx:49.5, cy:86.5 },
|
||||
{ id:2, cx:37.0, cy:73.0 },
|
||||
{ id:3, cx:44.5, cy:62.0 },
|
||||
{ id:4, cx:54.5, cy:53.5 },
|
||||
{ id:5, cx:49.0, cy:46.0 },
|
||||
{ id:6, cx:44.5, cy:37.5 },
|
||||
{ id:7, cx:50.0, cy:22.0 },
|
||||
{ id:1, cx:48.5, cy:87.0 },
|
||||
{ id:2, cx:36.5, cy:73.5 },
|
||||
{ id:3, cx:44.0, cy:63.0 },
|
||||
{ id:4, cx:53.5, cy:54.5 },
|
||||
{ id:5, cx:47.5, cy:46.5 },
|
||||
{ id:6, cx:43.5, cy:38.5 },
|
||||
{ id:7, cx:49.5, cy:23.0 },
|
||||
];
|
||||
const R = 4.8; // Kreis-Radius in SVG-Einheiten
|
||||
|
||||
@ -90,40 +90,34 @@ function buildSvg(completed) {
|
||||
</g>`);
|
||||
|
||||
} else if (isAvail) {
|
||||
// Available: nur pulsierende Ringe über dem Bild-Kreis, kein eigener Kreis
|
||||
parts.push(`
|
||||
<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"
|
||||
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"
|
||||
values=".7;0;.7" dur="2s" repeatCount="indefinite"/>
|
||||
values=".8;0;.8" dur="2s" repeatCount="indefinite"/>
|
||||
</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"
|
||||
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"
|
||||
values=".4;0;.4" dur="2s" begin=".7s" repeatCount="indefinite"/>
|
||||
values=".5;0;.5" dur="2s" begin=".7s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<!-- Sichtbarer Kreis -->
|
||||
<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 -->
|
||||
<!-- Transparente Hitbox -->
|
||||
<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>`);
|
||||
|
||||
} else {
|
||||
// Locked: unsichtbare Hitfläche, kein eigener Kreis (Bild-Kreis bleibt sichtbar)
|
||||
parts.push(`
|
||||
<g class="ds-station ${cls}" data-station="${s.id}">
|
||||
<circle class="ds-overlay" cx="${s.cx}" cy="${s.cy}" r="${R}"/>
|
||||
<text class="ds-num" x="${s.cx}" y="${s.cy}"
|
||||
text-anchor="middle" dominant-baseline="central"
|
||||
font-size="${fSize}">${s.id}</text>
|
||||
<circle fill="rgba(0,0,0,0.35)" stroke="none" cx="${s.cx}" cy="${s.cy}" r="${R}"/>
|
||||
<circle fill="transparent" stroke="none" cx="${s.cx}" cy="${s.cy}" r="${R + 1}"/>
|
||||
</g>`);
|
||||
}
|
||||
});
|
||||
|
||||
@ -76,21 +76,41 @@ async function loadAiDeck(station, playerLevel) {
|
||||
[maxRarity, deckSize]
|
||||
);
|
||||
|
||||
if (cards.length > 0) {
|
||||
console.log(`[HT] KI-Deck Station ${station}: ${cards.length} Karten, max. Rarity ${maxRarity}`);
|
||||
return cards.map(c => ({ ...c, currentCd: 0 }));
|
||||
// Wenn nicht genug Karten → Rarity schrittweise erhöhen
|
||||
let result = [...cards];
|
||||
|
||||
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
|
||||
console.warn(`[HT] Keine Karten mit Rarity <= ${maxRarity} – Fallback auf alle Karten`);
|
||||
const [all] = await db.query(
|
||||
`SELECT id, name, image, attack, defends, cooldown, \`range\`, \`race\`, rarity
|
||||
FROM cards
|
||||
ORDER BY RAND()
|
||||
LIMIT ?`,
|
||||
[deckSize]
|
||||
);
|
||||
return all.map(c => ({ ...c, currentCd: 0 }));
|
||||
// Letzter Fallback: alle Karten ohne Rarity-Filter
|
||||
if (result.length === 0) {
|
||||
const [all] = await db.query(
|
||||
`SELECT id, name, image, attack, defends, cooldown, \`range\`, \`race\`, rarity
|
||||
FROM cards ORDER BY RAND() LIMIT ?`,
|
||||
[deckSize]
|
||||
);
|
||||
result = all;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
console.error('[HT] loadAiDeck Fehler:', err);
|
||||
@ -437,8 +457,18 @@ function registerHimmelstorHandlers(io, socket) {
|
||||
const aiRoom = htAiRooms.get(matchId);
|
||||
if (!aiRoom) return;
|
||||
|
||||
// Socket + Namen aktualisieren
|
||||
// Socket + Namen + AccountId aktualisieren
|
||||
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 ioRoom = io._arenaRooms?.get(matchId);
|
||||
if (ioRoom) ioRoom.sockets.player1 = socket.id;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user