| Server IP : 202.61.199.114 / Your IP : 216.73.217.139 Web Server : nginx/1.22.1 System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64 User : web20 ( 1018) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/arni-solutions.de/web/slots/js/engine/ |
Upload File : |
/* Reel Royale – BonusEngine: animiertes Bonusrad (serverbestimmtes Ergebnis). */
import { $, money } from '../util.js';
export class BonusEngine {
constructor(audio, particles) {
this.audio = audio;
this.particles = particles;
}
/** bonus = { segments:[...], index, multiplier, win } */
async play(bonus, totalBet) {
const overlay = $('bonusOverlay');
const canvas = $('bonusWheel');
const ctx = canvas.getContext('2d');
const result = $('bonusResult');
const closeBtn = $('bonusClose');
result.textContent = '';
closeBtn.hidden = true;
overlay.hidden = false;
this.audio.play('bonus');
const segs = bonus.segments;
const n = segs.length;
const arc = (Math.PI * 2) / n;
const colors = ['#ffce53', '#ff7a2c', '#56d6ff', '#4ad991', '#c77dff', '#ff5d6c', '#ffae2b', '#8be9fd'];
const cx = canvas.width / 2, cy = canvas.height / 2, R = cx - 12;
const draw = (rot) => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < n; i++) {
const a0 = rot + i * arc, a1 = a0 + arc;
ctx.beginPath(); ctx.moveTo(cx, cy); ctx.arc(cx, cy, R, a0, a1); ctx.closePath();
ctx.fillStyle = colors[i % colors.length]; ctx.fill();
ctx.strokeStyle = 'rgba(0,0,0,.3)'; ctx.lineWidth = 2; ctx.stroke();
ctx.save(); ctx.translate(cx, cy); ctx.rotate(a0 + arc / 2);
ctx.fillStyle = '#1a1206'; ctx.font = 'bold 26px Cinzel, serif'; ctx.textAlign = 'right';
ctx.fillText('×' + segs[i], R - 16, 9); ctx.restore();
}
// Nabe
ctx.beginPath(); ctx.arc(cx, cy, 26, 0, 7); ctx.fillStyle = '#1c1530'; ctx.fill();
ctx.strokeStyle = '#ffce53'; ctx.lineWidth = 3; ctx.stroke();
// Zeiger (oben)
ctx.beginPath(); ctx.moveTo(cx - 14, 6); ctx.lineTo(cx + 14, 6); ctx.lineTo(cx, 34); ctx.closePath();
ctx.fillStyle = '#ffce53'; ctx.fill();
};
// Ziel: Index landet oben (−90°)
const targetAngle = (-Math.PI / 2) - (bonus.index * arc) - arc / 2;
const spins = 5;
const finalRot = targetAngle - spins * Math.PI * 2;
const startRot = 0;
const dur = 3600;
const t0 = performance.now();
await new Promise((resolve) => {
let lastTick = 0;
const anim = (now) => {
const t = Math.min(1, (now - t0) / dur);
const eased = 1 - Math.pow(1 - t, 4);
const rot = startRot + (finalRot - startRot) * eased;
draw(rot);
if (Math.floor(t * 40) !== lastTick) { lastTick = Math.floor(t * 40); if (t < 0.92) this.audio.play('click'); }
if (t < 1) requestAnimationFrame(anim); else resolve();
};
requestAnimationFrame(anim);
});
this.audio.play('bigwin');
this.particles.coinShower(50);
result.innerHTML = `Bonus ×${bonus.multiplier} · <strong style="color:var(--rr-gold)">+${money(bonus.win)}</strong>`;
closeBtn.hidden = false;
// Automatisch einsammeln nach kurzer Zeit; Klick sammelt sofort ein.
await new Promise((resolve) => {
let done = false;
let timer = null;
const finish = () => {
if (done) return;
done = true;
if (timer) clearTimeout(timer);
overlay.hidden = true;
closeBtn.removeEventListener('click', finish);
resolve();
};
closeBtn.addEventListener('click', finish);
timer = setTimeout(finish, 1800);
});
}
}