403Webshell
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/clients/client2/web31/web/slots/includes/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/clients/client2/web31/web/slots/includes/engine.php
<?php
/**
 * Reel Royale – Generische Slot-Engine.
 * Eine Engine bedient alle Spiele. Jedes Spiel liefert nur seine Konfiguration
 * (Symbole, Gewichte, Auszahlungen, Paylines, Feature). Die komplette
 * Gewinn- und Feature-Auswertung läuft hier – serverautoritativ.
 */

/** Master-Set von 25 Paylines für ein 5x3 Raster. Koordinaten: [reel, row]. */
function master_paylines(): array {
    return [
        1  => [[0,1],[1,1],[2,1],[3,1],[4,1]],
        2  => [[0,0],[1,0],[2,0],[3,0],[4,0]],
        3  => [[0,2],[1,2],[2,2],[3,2],[4,2]],
        4  => [[0,0],[1,1],[2,2],[3,1],[4,0]],
        5  => [[0,2],[1,1],[2,0],[3,1],[4,2]],
        6  => [[0,0],[1,0],[2,1],[3,2],[4,2]],
        7  => [[0,2],[1,2],[2,1],[3,0],[4,0]],
        8  => [[0,1],[1,0],[2,0],[3,0],[4,1]],
        9  => [[0,1],[1,2],[2,2],[3,2],[4,1]],
        10 => [[0,0],[1,1],[2,1],[3,1],[4,0]],
        11 => [[0,2],[1,1],[2,1],[3,1],[4,2]],
        12 => [[0,1],[1,1],[2,0],[3,1],[4,1]],
        13 => [[0,1],[1,1],[2,2],[3,1],[4,1]],
        14 => [[0,0],[1,1],[2,0],[3,1],[4,0]],
        15 => [[0,2],[1,1],[2,2],[3,1],[4,2]],
        16 => [[0,1],[1,0],[2,1],[3,0],[4,1]],
        17 => [[0,1],[1,2],[2,1],[3,2],[4,1]],
        18 => [[0,0],[1,2],[2,0],[3,2],[4,0]],
        19 => [[0,2],[1,0],[2,2],[3,0],[4,2]],
        20 => [[0,0],[1,1],[2,2],[3,2],[4,2]],
        21 => [[0,0],[1,0],[2,1],[3,0],[4,0]],
        22 => [[0,2],[1,2],[2,1],[3,2],[4,2]],
        23 => [[0,1],[1,0],[2,2],[3,0],[4,1]],
        24 => [[0,1],[1,2],[2,0],[3,2],[4,1]],
        25 => [[0,0],[1,2],[2,2],[3,2],[4,0]],
    ];
}

function game_paylines(array $game): array {
    $max = (int)($game['max_lines'] ?? 25);
    return array_slice(master_paylines(), 0, $max, true);
}

function game_active_paylines(array $game, int $count): array {
    return array_slice(game_paylines($game), 0, $count, true);
}

function rr_random_float(): float {
    return mt_rand() / mt_getrandmax();
}

function rr_weighted_pick(array $weights): string {
    $total = array_sum($weights);
    if ($total <= 0) {
        return (string)array_key_first($weights);
    }
    $pick = rr_random_float() * $total;
    $running = 0.0;
    foreach ($weights as $code => $weight) {
        $running += $weight;
        if ($pick <= $running) {
            return (string)$code;
        }
    }
    return (string)array_key_last($weights);
}

/** Symbole, deren Typ in der Liste enthalten ist. */
function game_symbols_of_type(array $game, array $types): array {
    $out = [];
    foreach ($game['symbols'] as $code => $sym) {
        if (in_array($sym['type'], $types, true)) {
            $out[] = (string)$code;
        }
    }
    return $out;
}

/** Erzeugt ein 5x3 Raster nach Walzengewichten. $grid[$row][$col]. */
function game_generate_grid(array $game, array $state = []): array {
    $reels = (int)($game['reels'] ?? 5);
    $rows  = (int)($game['rows'] ?? 3);
    $isFree = !empty($state['is_free_spin']);

    $grid = [];
    for ($row = 0; $row < $rows; $row++) {
        $grid[$row] = [];
    }
    for ($col = 0; $col < $reels; $col++) {
        $weights = [];
        foreach ($game['symbols'] as $code => $sym) {
            $w = (float)($sym['weights'][$col] ?? 1);
            // Im Free-Spin abweichende Gewichte erlaubt (z.B. mehr Money-Symbole)
            if ($isFree && isset($sym['weights_free'][$col])) {
                $w = (float)$sym['weights_free'][$col];
            }
            $weights[$code] = max($w, 0.0);
        }
        // Pro Walze ziehen – Scatter/Bonus/Collector höchstens einmal je Walze
        $usedOnce = [];
        for ($row = 0; $row < $rows; $row++) {
            $local = $weights;
            foreach ($usedOnce as $u) {
                unset($local[$u]);
            }
            $code = rr_weighted_pick($local);
            $type = $game['symbols'][$code]['type'] ?? 'normal';
            if (in_array($type, ['scatter', 'bonus', 'collector', 'money'], true)) {
                $usedOnce[] = $code;
            }
            $grid[$row][$col] = $code;
        }
    }
    return $grid;
}

function game_count_type(array $game, array $grid, string $type): int {
    $n = 0;
    foreach ($grid as $rowArr) {
        foreach ($rowArr as $code) {
            if (($game['symbols'][$code]['type'] ?? '') === $type) {
                $n++;
            }
        }
    }
    return $n;
}

function game_positions_of(array $game, array $grid, string $type): array {
    $pos = [];
    foreach ($grid as $row => $rowArr) {
        foreach ($rowArr as $col => $code) {
            if (($game['symbols'][$code]['type'] ?? '') === $type) {
                $pos[] = [$col, $row];
            }
        }
    }
    return $pos;
}

function symbol_is_wild(array $game, string $code): bool {
    return ($game['symbols'][$code]['type'] ?? '') === 'wild';
}

function symbol_can_pay(array $game, string $code): bool {
    $t = $game['symbols'][$code]['type'] ?? 'normal';
    return in_array($t, ['normal', 'wild'], true);
}

/** Skalierte Auszahlung eines Symbols (RTP-Tuning über game['pay_scale']). */
function game_pay(array $game, string $code, int $count): float {
    $base = (float)($game['symbols'][$code]['payouts'][$count] ?? 0);
    return round($base * (float)($game['pay_scale'] ?? 1), 4);
}

/** Bestimmt den Liniengewinn (links nach rechts) für eine Payline. */
function game_evaluate_line(array $game, array $grid, array $line, float $betPerLine, ?string $expand = null): ?array {
    $cells = [];
    foreach ($line as [$col, $row]) {
        $cells[] = ['code' => $grid[$row][$col], 'col' => $col, 'row' => $row];
    }

    // Mögliche Zielsymbole: alle zahlbaren Nicht-Wilds der Linie + reines Wild
    $targets = [];
    foreach ($cells as $c) {
        if (symbol_can_pay($game, $c['code']) && !symbol_is_wild($game, $c['code'])) {
            $targets[$c['code']] = true;
        }
    }
    $targetCodes = array_keys($targets);
    if (!$targetCodes) {
        $targetCodes = game_symbols_of_type($game, ['wild']);
    }

    $best = null;
    foreach ($targetCodes as $target) {
        $positions = [];
        $count = 0;
        foreach ($cells as $c) {
            $code = $c['code'];
            $match = ($code === $target) || symbol_is_wild($game, $code);
            if ($match) {
                $positions[] = [$c['col'], $c['row']];
                $count++;
            } else {
                break;
            }
        }
        $payouts = $game['symbols'][$target]['payouts'] ?? [];
        if ($count < 2 || !isset($payouts[$count])) {
            continue;
        }
        $mult = game_pay($game, $target, $count);
        if ($expand && $target === $expand) {
            $mult = round($mult * 1.5, 2);
        }
        $candidate = [
            'symbol'    => $target,
            'count'     => $count,
            'multiplier'=> $mult,
            'win'       => round($mult * $betPerLine, 2),
            'positions' => $positions,
            'path'      => $line,
        ];
        if ($best === null || $candidate['win'] > $best['win']) {
            $best = $candidate;
        }
    }
    return $best;
}

/** Buch-Feature: Spezialsymbol expandiert über volle Walzen (nur Free Spins).
 *  $onWildToo=true: eine Walze expandiert auch, wenn dort (nur) ein Wild liegt. */
function game_apply_expanding(array $game, array $grid, string $special, bool $onWildToo = true): array {
    $reels = (int)$game['reels'];
    $rows  = (int)$game['rows'];
    $expanded = [];
    for ($col = 0; $col < $reels; $col++) {
        $has = false;
        for ($row = 0; $row < $rows; $row++) {
            if ($grid[$row][$col] === $special || ($onWildToo && symbol_is_wild($game, $grid[$row][$col]))) {
                $has = true;
                break;
            }
        }
        if ($has) {
            for ($row = 0; $row < $rows; $row++) {
                $grid[$row][$col] = $special;
                $expanded[] = [$col, $row];
            }
        }
    }
    return [$grid, $expanded];
}

/** Wählt ein zufälliges zahlendes Normalsymbol als Glückssymbol (Book-of-Ra-Stil).
 *  Gilt für die gesamte Freispiel-Runde. */
function game_pick_lucky(array $game): ?string {
    $normals = game_symbols_of_type($game, ['normal']);
    return $normals ? $normals[array_rand($normals)] : null;
}

/** Wild-Transformation: zufällige Walze wird komplett Wild (Free Spins). */
function game_apply_wild_transform(array $game, array $grid): array {
    $wilds = game_symbols_of_type($game, ['wild']);
    if (!$wilds) {
        return [$grid, []];
    }
    $wild = $wilds[0];
    $reels = (int)$game['reels'];
    $rows  = (int)$game['rows'];
    // Walze transformieren, wenn dort bereits ein hohes Symbol/Wild liegt
    $candidates = [];
    for ($col = 0; $col < $reels; $col++) {
        for ($row = 0; $row < $rows; $row++) {
            if (symbol_is_wild($game, $grid[$row][$col])) {
                $candidates[] = $col;
                break;
            }
        }
    }
    if (!$candidates && rr_random_float() < 0.5) {
        $candidates[] = mt_rand(0, $reels - 1);
    }
    $transformed = [];
    foreach (array_unique($candidates) as $col) {
        for ($row = 0; $row < $rows; $row++) {
            $grid[$row][$col] = $wild;
            $transformed[] = [$col, $row];
        }
    }
    return [$grid, $transformed];
}

/**
 * Hauptauswertung eines Spins.
 * $state: is_free_spin, special, feature_state (mult, collected, walk)
 */
function game_evaluate(array $game, array $grid, int $lineCount, float $betPerLine, array $state): array {
    $feature   = $game['feature'] ?? ['type' => 'expanding'];
    $type      = $feature['type'] ?? 'expanding';
    $isFree    = !empty($state['is_free_spin']);
    $special   = $state['special'] ?? null;
    $fs        = $state['feature_state'] ?? [];
    $globalMult= (float)($fs['mult'] ?? 1);

    $meta = [
        'expanded'   => [],
        'transformed'=> [],
        'collected'  => [],
        'walk'       => [],
        'global_mult'=> $globalMult,
        'collect_win'=> 0.0,
        'bonus'      => null,
    ];

    // --- Feature Vor-Effekte (verändern das Grid) ---
    if ($isFree && $type === 'expanding' && $special) {
        [$grid, $meta['expanded']] = game_apply_expanding($game, $grid, $special);
    }
    if ($isFree && $type === 'wild_transform') {
        [$grid, $meta['transformed']] = game_apply_wild_transform($game, $grid);
    }
    if ($isFree && $type === 'walking_wild' && !empty($fs['walk'])) {
        // bestehende Walking-Wild-Spalten erneut als Wild setzen
        $wilds = game_symbols_of_type($game, ['wild']);
        $wild = $wilds[0] ?? null;
        if ($wild) {
            foreach ($fs['walk'] as $col) {
                if ($col >= 0 && $col < (int)$game['reels']) {
                    for ($row = 0; $row < (int)$game['rows']; $row++) {
                        $grid[$row][$col] = $wild;
                        $meta['walk'][] = [$col, $row];
                    }
                }
            }
        }
    }

    // --- Glückssymbol (Freispiele): liegt es in einer Spalte, füllt es die ganze Spalte
    // (oben→unten); sind ALLE Walzen gefüllt = Vollbild-Bonus. Bei 'expanding'-Spielen IST
    // das Spiel-Spezialsymbol das Glückssymbol (kein zweites Expanding → geringere Varianz).
    $lucky = ($type === 'expanding' && $special) ? $special : ($state['lucky'] ?? null);
    $fullscreen = false;
    if ($isFree && $lucky) {
        if ($type !== 'expanding') {
            [$grid, $luckyExpanded] = game_apply_expanding($game, $grid, $lucky, false);
            if ($luckyExpanded) $meta['expanded'] = array_merge($meta['expanded'], $luckyExpanded);
        }
        $meta['lucky'] = $lucky;
        $fullscreen = true;   // Vollbild? (alle Zellen = Glückssymbol)
        foreach ($grid as $rowArr) {
            foreach ($rowArr as $c) { if ($c !== $lucky) { $fullscreen = false; break 2; } }
        }
    }

    // --- Linienauswertung ---
    $expandSym = ($type === 'expanding') ? $special : null;
    $wins = [];
    $totalWin = 0.0;
    foreach (game_active_paylines($game, $lineCount) as $lineNo => $line) {
        $res = game_evaluate_line($game, $grid, $line, $betPerLine, $expandSym);
        if ($res) {
            // globaler Multiplikator (Free Spins) auf Liniengewinne
            if ($globalMult > 1) {
                $res['multiplier'] = round($res['multiplier'] * $globalMult, 2);
                $res['win'] = round($res['win'] * $globalMult, 2);
                $res['boosted'] = $globalMult;
            }
            $res['line'] = $lineNo;
            $wins[] = $res;
            $totalWin += (float)$res['win'];
        }
    }

    // --- Scatter & Free Spins ---
    $scatterCodes = game_symbols_of_type($game, ['scatter']);
    $scatterCount = 0;
    $scatterCode  = $scatterCodes[0] ?? null;
    if ($scatterCode) {
        $scatterCount = game_count_type($game, $grid, 'scatter');
    }
    $scatterWin = 0.0;
    $freeSpinsWon = 0;
    if ($scatterCode) {
        $sPayouts = $game['symbols'][$scatterCode]['payouts'] ?? [];
        if (isset($sPayouts[$scatterCount])) {
            $scatterWin = round(game_pay($game, $scatterCode, $scatterCount) * ($betPerLine * $lineCount), 2);
            $totalWin += $scatterWin;
        }
        $trigger = $feature['free_spins'] ?? ($game['symbols'][$scatterCode]['free_spins'] ?? []);
        if (isset($trigger[$scatterCount])) {
            $freeSpinsWon = (int)$trigger[$scatterCount];
        }
        if ($scatterCount >= 3) {
            $wins[] = [
                'line' => 0, 'symbol' => $scatterCode, 'count' => $scatterCount,
                'multiplier' => $scatterWin > 0 ? round($scatterWin / max(1, $betPerLine * $lineCount), 2) : 0,
                'win' => $scatterWin, 'positions' => game_positions_of($game, $grid, 'scatter'),
                'path' => [], 'free_spins' => $freeSpinsWon, 'is_scatter' => true,
            ];
        }
    }

    // --- Money / Collector Feature (Fishing-Style) ---
    if (in_array($type, ['collector'], true)) {
        $collectorCodes = game_symbols_of_type($game, ['collector', 'wild']);
        $hasCollector = false;
        foreach ($collectorCodes as $cc) {
            foreach ($grid as $rowArr) {
                if (in_array($cc, $rowArr, true)) { $hasCollector = true; break 2; }
            }
        }
        $moneyPositions = game_positions_of($game, $grid, 'money');
        // Im Basisspiel: Money zahlt nur mit Collector. In Free Spins: immer mit Collector.
        if ($moneyPositions && ($hasCollector || $isFree && $hasCollector)) {
            $collectSum = 0.0;
            $scale = (float)($game['pay_scale'] ?? 1);
            foreach ($moneyPositions as [$col, $row]) {
                $code = $grid[$row][$col];
                $val = (float)($game['symbols'][$code]['value'] ?? 0);
                // Münzwert skaliert mit dem GESAMTeinsatz (betPerLine * lineCount), damit die
                // RTP unabhängig von der Linienanzahl bleibt (sonst sinkt sie bei mehr Linien).
                $valued = round($val * $betPerLine * max(1, $lineCount) * $scale * mt_rand(8, 30) / 10, 2);
                $collectSum += $valued;
                $meta['collected'][] = ['col' => $col, 'row' => $row, 'win' => $valued];
            }
            $collectSum = round($collectSum * max(1, $globalMult), 2);
            $meta['collect_win'] = $collectSum;
            $totalWin += $collectSum;
        }
    }

    // --- Bonus-Symbol Trigger (z.B. El Matador Rad) ---
    if (!empty($feature['bonus'])) {
        $bonusCodes = game_symbols_of_type($game, ['bonus']);
        if ($bonusCodes) {
            $bonusCount = game_count_type($game, $grid, 'bonus');
            if ($bonusCount >= 3 && !$isFree) {
                $meta['bonus'] = game_resolve_bonus($game, $betPerLine * $lineCount);
                $totalWin += (float)$meta['bonus']['win'];
            }
        }
    }

    // --- Vollbild-Bonus des Glückssymbols („extra viel Punkte") ---
    // Fixer Aufschlag auf den Gesamteinsatz (NICHT zusätzlich mit dem FS-Multiplikator
    // gekoppelt – die vollflächigen Liniengewinne tragen den Multiplikator bereits; das
    // vermeidet extreme Ausreißer und macht die RTP verlässlich tunebar).
    if ($fullscreen) {
        $fsMult = (float)($feature['fullscreen_mult'] ?? $game['fullscreen_mult'] ?? 50);
        $fsWin = round($fsMult * $betPerLine * max(1, $lineCount), 2);
        $meta['fullscreen'] = true;
        $meta['fullscreen_win'] = $fsWin;
        $totalWin += $fsWin;
    }

    return [
        'grid'              => $grid,
        'wins'              => $wins,
        'total_win'         => round($totalWin, 2),
        'scatter_count'     => $scatterCount,
        'free_spins_won'    => $freeSpinsWon,
        'meta'              => $meta,
    ];
}

/**
 * Multiplikator für den aktuellen Freispin (geteilt von Live-Spin & Simulator).
 * $fs = Feature-State der laufenden Freispiele.
 */
function game_fs_spin_mult(array $game, array $fs): float {
    $feature = $game['feature'] ?? [];
    if (!empty($feature['multipliers'])) {
        $m = $feature['multipliers'];
        return (float)$m[array_rand($m)];
    }
    if (!empty($feature['rising_mult'])) {
        $rm = $feature['rising_mult'];
        $tier = (int)($fs['tier'] ?? 0);
        return (float)$rm[min($tier, count($rm) - 1)];
    }
    return 1.0;
}

/**
 * Schreibt den Feature-State eines Freispins fort (Sammler-Anzahl, Tier, Walking Wilds).
 * Liefert den neuen State.
 */
function game_advance_fs(array $game, array $fs, array $result, array $grid): array {
    $feature = $game['feature'] ?? [];
    $type = $feature['type'] ?? 'expanding';
    $newFs = $fs;
    $newFs['collected'] = (int)($fs['collected'] ?? 0) + count($result['meta']['collected'] ?? []);

    if (!empty($feature['rising_mult'])) {
        $c = $newFs['collected'];
        $tier = $c >= 10 ? 3 : ($c >= 6 ? 2 : ($c >= 4 ? 1 : 0));
        $newFs['tier'] = min($tier, count($feature['rising_mult']) - 1);
    }
    if ($type === 'walking_wild') {
        $walk = $fs['walk'] ?? [];
        $wilds = game_symbols_of_type($game, ['wild']);
        $wild = $wilds[0] ?? null;
        if ($wild) {
            for ($col = 0; $col < (int)$game['reels']; $col++) {
                for ($row = 0; $row < (int)$game['rows']; $row++) {
                    if ($grid[$row][$col] === $wild && !in_array($col, $walk, true)) {
                        $walk[] = $col;
                    }
                }
            }
        }
        $newFs['active_wilds'] = count($walk);
        $moved = [];
        foreach ($walk as $col) { if ($col - 1 >= 0) { $moved[] = $col - 1; } }
        $newFs['walk'] = array_values(array_unique($moved));
        $rm = $feature['rising_mult'] ?? [1, 2, 3, 5];
        $newFs['tier'] = min(count($walk), count($rm) - 1);
    }
    $newFs['special'] = $fs['special'] ?? null;
    $newFs['lucky']   = $fs['lucky'] ?? null;   // Glückssymbol bleibt die ganze FS-Runde gleich
    return $newFs;
}

/** Serverautoritatives Bonus-Rad-Ergebnis. */
function game_resolve_bonus(array $game, float $totalBet): array {
    $cfg = $game['feature']['bonus'];
    $segments = $cfg['segments'] ?? [2, 5, 8, 12, 20, 50];
    $idx = mt_rand(0, count($segments) - 1);
    $mult = (float)$segments[$idx];
    return [
        'type'     => $cfg['kind'] ?? 'wheel',
        'segments' => $segments,
        'index'    => $idx,
        'multiplier' => $mult,
        'win'      => round($mult * $totalBet, 2),
    ];
}

/**
 * Erzeugt die clientseitige (gewichtsfreie) Konfiguration eines Spiels.
 * Walzengewichte werden bewusst NICHT übertragen (Anti-Cheat).
 */
function game_client_config(array $game): array {
    $symbols = [];
    $scale = (float)($game['pay_scale'] ?? 1);
    $overrides = function_exists('game_symbol_overrides') ? game_symbol_overrides($game['id'] ?? '') : [];
    foreach ($game['symbols'] as $code => $sym) {
        $scaled = [];
        foreach (($sym['payouts'] ?? []) as $count => $mult) {
            $scaled[$count] = round($mult * $scale, 2);
        }
        $symbols[$code] = [
            'name'    => $sym['name'],
            'icon'    => $sym['icon'],
            'type'    => $sym['type'],
            'payouts' => $scaled,
            'free_spins' => $sym['free_spins'] ?? [],
            'value'   => $sym['value'] ?? null,
            'color'   => $sym['color'] ?? null,
            'img'     => $sym['img'] ?? null,
        ];
        // Admin-Symbol-Override (eigenes Emoji/Text oder hochgeladenes Bild)
        if (isset($overrides[$code])) {
            $ov = $overrides[$code];
            if (array_key_exists('img', $ov)) {
                $symbols[$code]['img'] = $ov['img'] !== '' ? $ov['img'] : null;
            }
            if (!empty($ov['icon'])) {
                $symbols[$code]['icon'] = $ov['icon'];
            }
        }
    }
    return [
        'id'        => $game['id'],
        'title'     => $game['title'],
        'subtitle'  => $game['subtitle'] ?? '',
        'reels'     => (int)($game['reels'] ?? 5),
        'rows'      => (int)($game['rows'] ?? 3),
        'rtp'       => $game['rtp'] ?? 0.96,
        'volatility'=> $game['volatility'] ?? 'Mittel',
        'hit_frequency' => $game['hit_frequency'] ?? null,
        'max_lines' => (int)($game['max_lines'] ?? 25),
        'paylines'  => array_values(game_paylines($game)),
        'symbols'   => $symbols,
        'feature'   => [
            'type'       => $game['feature']['type'] ?? 'expanding',
            'name'       => $game['feature']['name'] ?? 'Freispiele',
            'free_spins' => $game['feature']['free_spins'] ?? [],
            'bonus'      => isset($game['feature']['bonus']) ? ['kind' => $game['feature']['bonus']['kind'] ?? 'wheel'] : null,
        ],
        'theme'     => $game['theme'] ?? [],
    ];
}

Youez - 2016 - github.com/yon3zu
LinuXploit