| 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/api/ |
Upload File : |
<?php
require_once __DIR__ . '/../includes/bootstrap.php';
header('Content-Type: application/json; charset=utf-8');
$user = current_user();
if (!$user) {
http_response_code(401);
echo json_encode(['ok' => false, 'message' => 'Nicht eingeloggt.']);
exit;
}
$payload = json_decode(file_get_contents('php://input'), true) ?: [];
if (!csrf_verify($payload['csrf'] ?? ($_SERVER['HTTP_X_CSRF'] ?? null))) {
http_response_code(403);
echo json_encode(['ok' => false, 'message' => 'Sicherheitstoken ungültig. Bitte neu laden.']);
exit;
}
update_playtime_ping((int)$user['id']);
$config = app_config();
$gameId = preg_replace('/[^a-z_]/', '', (string)($payload['game'] ?? ''));
$game = get_game($gameId);
if (!$game) {
http_response_code(400);
echo json_encode(['ok' => false, 'message' => 'Unbekanntes Spiel.']);
exit;
}
$maxLines = (int)($game['max_lines'] ?? 10);
$lineCount = max(1, min($maxLines, (int)($payload['lines'] ?? $maxLines)));
$level = (int)($payload['level'] ?? 1);
if (!in_array($level, $config['bet']['level_options'], true)) {
$level = 1;
}
$betPerLine = $level * (float)$config['bet']['coin_value'];
$pdo = db();
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = ? FOR UPDATE');
$stmt->execute([(int)$user['id']]);
$user = $stmt->fetch();
if (!$user) {
throw new RuntimeException('Spieler nicht gefunden.');
}
if ((int)($user['banned'] ?? 0) === 1) {
throw new RuntimeException('Dein Konto ist gesperrt.');
}
// Spielerindividueller Gewinnfaktor (Admin) auf die Auszahlungsstaffel
$game['pay_scale'] = (float)($game['pay_scale'] ?? 1) * player_rtp_factor($user);
$hasFreeSpins = (int)$user['free_spins'] > 0;
$fsGame = $_SESSION['fs_game'] ?? null;
$feature = $game['feature'] ?? ['type' => 'expanding'];
$type = $feature['type'] ?? 'expanding';
// Verwaiste Freispiele (z.B. aus der Alt-Installation /slot) ins aktuelle Spiel übernehmen
$adopted = false;
if ($hasFreeSpins && !$fsGame) {
$fsGame = $gameId;
$_SESSION['fs_game'] = $gameId;
$_SESSION['fs_total'] = (int)$user['free_spins'];
$_SESSION['fs_win'] = 0.0;
$adopt = ['tier' => 0, 'collected' => 0, 'walk' => [], 'special' => null];
$adopt['lucky'] = game_pick_lucky($game); // Glückssymbol für die FS-Runde (alle Spiele)
if ($type === 'expanding') {
$normals = game_symbols_of_type($game, ['normal']);
$adopt['special'] = $normals ? $normals[array_rand($normals)] : null;
}
$_SESSION['fs_state'] = $adopt;
$adopted = true;
}
// Freispiele sind an ihr Spiel gebunden
if ($hasFreeSpins && $fsGame && $fsGame !== $gameId) {
throw new RuntimeException('Beende zuerst deine Freispiele im anderen Spiel.');
}
$isFree = $hasFreeSpins && $fsGame === $gameId;
$totalBet = $isFree ? 0.0 : round($betPerLine * $lineCount, 2);
if (!$isFree && (float)$user['balance'] < $totalBet) {
throw new RuntimeException('Nicht genug Coins für diesen Spin.');
}
// --- Feature-State (Session) ---
$fs = $_SESSION['fs_state'] ?? [];
$special = $isFree ? ($fs['special'] ?? null) : null;
$lucky = $isFree ? ($fs['lucky'] ?? null) : null;
// Multiplikator für DIESEN Freispin festlegen (geteilte Engine-Logik)
$spinMult = $isFree ? game_fs_spin_mult($game, $fs) : 1.0;
$grid = game_generate_grid($game, ['is_free_spin' => $isFree]);
$result = game_evaluate($game, $grid, $lineCount, $betPerLine, [
'is_free_spin' => $isFree,
'special' => $special,
'lucky' => $lucky,
'feature_state' => [
'mult' => $spinMult,
'walk' => $isFree ? ($fs['walk'] ?? []) : [],
],
]);
// Hold & Respin: Löst der Basis-Dreh Freispiele mit >=3 Freispielsymbolen aus (und noch
// nicht alle Walzen), werden die Scatter-Walzen GEHALTEN und die übrigen EINMAL neu gedreht
// → Chance auf weitere Freispielsymbole (4./5. = mehr Freispiele). Neu-Auswertung auf dem
// Ergebnis-Grid; gehaltene Scatter bleiben erhalten, Freispielzahl kann nur steigen.
$respinInfo = null;
if (!$isFree && (int)$result['free_spins_won'] > 0) {
$reels = (int)$game['reels']; $rows = (int)$game['rows'];
$scCount = (int)$result['scatter_count'];
if ($scCount >= 3 && $scCount < $reels) {
$firstGrid = $result['grid'];
$held = [];
foreach (game_positions_of($game, $firstGrid, 'scatter') as $p) { $held[(int)$p[0]] = true; }
$newGrid = game_generate_grid($game, ['is_free_spin' => false]);
$respinGrid = $firstGrid;
for ($c = 0; $c < $reels; $c++) {
if (isset($held[$c])) continue; // Scatter-Walze halten
for ($r = 0; $r < $rows; $r++) { $respinGrid[$r][$c] = $newGrid[$r][$c]; }
}
$result = game_evaluate($game, $respinGrid, $lineCount, $betPerLine, [
'is_free_spin' => false, 'special' => null, 'lucky' => null,
'feature_state' => ['mult' => 1.0, 'walk' => []],
]);
$respinInfo = ['first_grid' => $firstGrid, 'held' => array_map('intval', array_keys($held))];
}
}
$grid = $result['grid'];
$freeSpinsWon = (int)$result['free_spins_won'];
$spentFree = $isFree ? 1 : 0;
$newFreeSpins = max(0, (int)$user['free_spins'] - $spentFree) + $freeSpinsWon;
$totalWin = (float)$result['total_win'];
// --- Feature-State & Freispiel-Session fortschreiben ---
$newFs = $fs;
$fsEntered = $adopted;
if (!$isFree && $freeSpinsWon > 0) {
// Freispiele aus dem Basisspiel neu ausgelöst
$newFs = ['tier' => 0, 'collected' => 0, 'walk' => [], 'special' => null];
$newFs['lucky'] = game_pick_lucky($game); // neues Glückssymbol für die FS-Runde
if ($type === 'expanding') {
$normals = game_symbols_of_type($game, ['normal']);
$newFs['special'] = $normals ? $normals[array_rand($normals)] : null;
}
$_SESSION['fs_game'] = $gameId;
$_SESSION['fs_total'] = $freeSpinsWon;
$_SESSION['fs_win'] = 0.0;
$fsEntered = true;
} elseif ($isFree) {
// laufender Freispin
$newFs = game_advance_fs($game, $fs, $result, $grid);
$_SESSION['fs_win'] = round((float)($_SESSION['fs_win'] ?? 0) + $totalWin, 2);
if ($freeSpinsWon > 0) {
$_SESSION['fs_total'] = (int)($_SESSION['fs_total'] ?? 0) + $freeSpinsWon;
}
}
$_SESSION['fs_state'] = $newFs;
$fsTotal = (int)($_SESSION['fs_total'] ?? 0);
$fsPlayed = max(0, $fsTotal - $newFreeSpins);
$fsEnded = false;
$fsSessionWin = 0.0;
$fsActive = $newFreeSpins > 0 || $isFree;
if (($isFree || $adopted) && $newFreeSpins === 0) {
// letzter Freispin gespielt → Session beenden
$fsEnded = true;
$fsSessionWin = round((float)($_SESSION['fs_win'] ?? 0), 2);
$fsActive = false;
unset($_SESSION['fs_game'], $_SESSION['fs_state'], $_SESSION['fs_total'], $_SESSION['fs_win']);
}
$newBalance = round((float)$user['balance'] - $totalBet + $totalWin, 2);
$totalWagered = round((float)$user['total_wagered'] + $totalBet, 2);
$totalWon = round((float)$user['total_won'] + $totalWin, 2);
$ratio = $totalBet > 0 ? round($totalWin / $totalBet, 2) : (float)$user['best_win_ratio'];
$bestRatio = max((float)$user['best_win_ratio'], $ratio);
$biggest = max((float)$user['biggest_single_win'], $totalWin);
$pdo->prepare('UPDATE users SET balance=?, free_spins=?, free_spin_special_symbol=?, total_wagered=?, total_won=?, biggest_single_win=?, best_win_ratio=?, last_seen_at=NOW() WHERE id=?')
->execute([$newBalance, $newFreeSpins, $newFreeSpins > 0 ? ($newFs['special'] ?? null) : null, $totalWagered, $totalWon, $biggest, $bestRatio, (int)$user['id']]);
$pdo->prepare('INSERT INTO spins (user_id, mode_name, line_count, bet_per_line, total_bet, total_win, scatter_count, free_spins_won, free_spins_after, is_free_spin, free_spin_special_symbol, jackpot_bet, wins_json, grid_json, created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,0,?,?,NOW())')
->execute([
(int)$user['id'], $gameId, $lineCount, $betPerLine, $totalBet, $totalWin,
(int)$result['scatter_count'], $freeSpinsWon, $newFreeSpins, $isFree ? 1 : 0,
$newFreeSpins > 0 ? ($newFs['special'] ?? null) : null,
json_encode($result['wins'], JSON_UNESCAPED_UNICODE),
json_encode($grid, JSON_UNESCAPED_UNICODE),
]);
// Verdopplungsspiel vorbereiten: nur Basisspiel-Gewinne, wenn aktiviert
if (!$isFree && $totalWin > 0 && gamble_enabled()) {
$_SESSION['gamble'] = ['amount' => round($totalWin, 2), 'steps' => 0, 'game' => $gameId];
} else {
unset($_SESSION['gamble']);
}
$pdo->commit();
echo json_encode([
'ok' => true,
'game' => $gameId,
'gamble_available' => isset($_SESSION['gamble']),
'grid' => $grid,
'wins' => $result['wins'],
'total_win' => round($totalWin, 2),
'total_bet' => $totalBet,
'bet_per_line' => $betPerLine,
'line_count' => $lineCount,
'balance' => $newBalance,
'free_spins' => $newFreeSpins,
'free_spins_won'=> $freeSpinsWon,
'is_free_spin' => $isFree,
'special' => $newFreeSpins > 0 ? ($newFs['special'] ?? null) : null,
'fs_total' => $fsTotal,
'fs_played' => $fsPlayed,
'fs_active' => $fsActive,
'fs_entered' => $fsEntered,
'fs_ended' => $fsEnded,
'fs_session_win' => $fsSessionWin,
'fs_collected' => (int)($newFs['collected'] ?? 0),
'scatter_count' => (int)$result['scatter_count'],
'meta' => $result['meta'],
'respin' => $respinInfo,
'spin_mult' => $spinMult,
], JSON_UNESCAPED_UNICODE);
} catch (Throwable $ex) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
http_response_code(400);
echo json_encode(['ok' => false, 'message' => $ex->getMessage()]);
}