local spinLocks = {} local gameLocks = {} local blackjackSessions = {} local rewards = { { type = 'cash', amount = 500, label = '500 $ Bargeld' }, { type = 'xp', amount = 25, label = '25 RP-XP' }, { type = 'bank', amount = 1000, label = '1.000 $ Bankguthaben' }, { type = 'cash', amount = 750, label = '750 $ Bargeld' }, { type = 'xp', amount = 50, label = '50 RP-XP' }, { type = 'bank', amount = 1500, label = '1.500 $ Bankguthaben' }, { type = 'cash', amount = 1250, label = '1.250 $ Bargeld' }, { type = 'xp', amount = 100, label = '100 RP-XP' } } MySQL.ready(function() MySQL.query.await([[ CREATE TABLE IF NOT EXISTS devx_casino_daily ( character_id BIGINT UNSIGNED NOT NULL, last_spin DATETIME NULL, total_spins INT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (character_id), CONSTRAINT fk_devx_casino_character FOREIGN KEY (character_id) REFERENCES devx_characters (id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ]]) MySQL.query.await([[ CREATE TABLE IF NOT EXISTS devx_casino_wallets ( character_id BIGINT UNSIGNED NOT NULL, chips BIGINT UNSIGNED NOT NULL DEFAULT 0, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (character_id), CONSTRAINT fk_devx_casino_wallet_character FOREIGN KEY (character_id) REFERENCES devx_characters (id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ]]) end) local function amount(value, min, max) local n = math.floor(tonumber(value) or 0) if n < (min or 1) or n > (max or 100000) then return nil end return n end local function wallet(characterId) MySQL.query.await('INSERT IGNORE INTO devx_casino_wallets (character_id, chips) VALUES (?, 0)', { characterId }) return tonumber(MySQL.scalar.await('SELECT chips FROM devx_casino_wallets WHERE character_id = ?', { characterId })) or 0 end local function addChips(characterId, chips) MySQL.query.await([[ INSERT INTO devx_casino_wallets (character_id, chips) VALUES (?, ?) ON DUPLICATE KEY UPDATE chips = chips + VALUES(chips) ]], { characterId, chips }) return wallet(characterId) end local function removeChips(characterId, chips) local changed = MySQL.update.await([[ UPDATE devx_casino_wallets SET chips = chips - ? WHERE character_id = ? AND chips >= ? ]], { chips, characterId, chips }) return changed and changed > 0, wallet(characterId) end local function spinStatus(characterId) local row = MySQL.single.await([[ SELECT UNIX_TIMESTAMP(last_spin) AS lastSpin, total_spins AS totalSpins FROM devx_casino_daily WHERE character_id = ? LIMIT 1 ]], { characterId }) local lastSpin = row and tonumber(row.lastSpin) or 0 local nextSpin = lastSpin + DevXCasinoConfig.CooldownSeconds return { available = os.time() >= nextSpin, remainingSeconds = math.max(0, nextSpin - os.time()), totalSpins = row and tonumber(row.totalSpins) or 0 } end local function card() local rank = math.random(1, 13) local labels = { 'A','2','3','4','5','6','7','8','9','10','J','Q','K' } return { rank = rank, label = labels[rank] } end local function handValue(cards) local total, aces = 0, 0 for _, item in ipairs(cards) do if item.rank == 1 then total = total + 11; aces = aces + 1 elseif item.rank >= 10 then total = total + 10 else total = total + item.rank end end while total > 21 and aces > 0 do total = total - 10; aces = aces - 1 end return total end local function blackjackPayload(session, reveal, result) local dealer = {} for index, item in ipairs(session.dealer) do dealer[index] = (reveal or index == 1) and item or { label = '?' } end return { ok = true, active = result == nil, result = result, playerCards = session.player, dealerCards = dealer, playerValue = handValue(session.player), dealerValue = reveal and handValue(session.dealer) or nil, chips = wallet(session.characterId) } end local function settleBlackjack(source, session, outcome) local result = outcome if outcome == 'win' then addChips(session.characterId, session.bet * 2) elseif outcome == 'blackjack' then addChips(session.characterId, math.floor(session.bet * 2.5)) elseif outcome == 'push' then addChips(session.characterId, session.bet) end blackjackSessions[source] = nil return blackjackPayload(session, true, result) end DevXRegisterCallback('casino:status', function(source) local player = exports.devx_core:GetPlayer(source) if not player then return { ok = false, error = 'Kein Charakter aktiv.' } end local status = spinStatus(player.characterId) status.ok = true; status.chips = wallet(player.characterId) return status end) DevXRegisterCallback('casino:exchange', function(source, payload) local player = exports.devx_core:GetPlayer(source) local value = amount(payload.amount, 100, 100000) local direction = payload.direction if not player or not value or (direction ~= 'buy' and direction ~= 'sell') then return { ok = false, error = 'Ungültige Wechselanfrage.' } end if gameLocks[source] then return { ok = false, error = 'Eine Casino-Buchung läuft bereits.' } end gameLocks[source] = true local response if direction == 'buy' then local paid, balance = exports.devx_banking:ChargeCharacter(player.characterId, value, 'Casino-Chips', 'Diamond Casino') if paid then exports.devx_core:SetBankCache(source, balance); response = { ok = true, chips = addChips(player.characterId, value) } else response = { ok = false, error = balance } end else local removed = removeChips(player.characterId, value) if removed then local credited, balance = exports.devx_banking:CreditCharacter(player.characterId, value, 'Casino-Chips eingelöst', 'Diamond Casino') if credited then exports.devx_core:SetBankCache(source, balance); response = { ok = true, chips = wallet(player.characterId) } else addChips(player.characterId, value); response = { ok = false, error = balance } end else response = { ok = false, error = 'Du besitzt nicht genügend Chips.' } end end gameLocks[source] = nil return response end) DevXRegisterCallback('casino:spin', function(source) local player = exports.devx_core:GetPlayer(source) if not player then return { ok = false, error = 'Kein Charakter aktiv.' } end if spinLocks[source] then return { ok = false, error = 'Dein Glücksrad-Dreh läuft bereits.' } end spinLocks[source] = true local status = spinStatus(player.characterId) if not status.available then spinLocks[source] = nil; return { ok = false, error = ('Nächster Dreh in %s Stunden.'):format(math.ceil(status.remainingSeconds / 3600)) } end local index = math.random(1, #rewards); local reward = rewards[index]; local success = false if reward.type == 'cash' then success = exports.devx_core:AddCash(source, reward.amount, 'Casino-Glücksrad') == true elseif reward.type == 'bank' then local credited, newBalance = exports.devx_banking:CreditCharacter(player.characterId, reward.amount, 'Casino-Glücksrad', 'Diamond Casino') success = credited == true; if success then exports.devx_core:SetBankCache(source, newBalance) end elseif reward.type == 'xp' then success = exports.devx_core:AddMetadataNumber(source, 'rpXp', reward.amount) == true end if not success then spinLocks[source] = nil; return { ok = false, error = 'Gewinn konnte nicht gutgeschrieben werden.' } end MySQL.query.await([[ INSERT INTO devx_casino_daily (character_id, last_spin, total_spins) VALUES (?, NOW(), 1) ON DUPLICATE KEY UPDATE last_spin = NOW(), total_spins = total_spins + 1 ]], { player.characterId }) spinLocks[source] = nil return { ok = true, reward = reward, segment = index - 1, totalSegments = #rewards, nextSpinSeconds = DevXCasinoConfig.CooldownSeconds } end) DevXRegisterCallback('casino:slots', function(source, payload) local player = exports.devx_core:GetPlayer(source); local bet = amount(payload.bet, 100, 5000) if not player or not bet then return { ok = false, error = 'Ungültiger Einsatz.' } end local removed = removeChips(player.characterId, bet) if not removed then return { ok = false, error = 'Nicht genügend Chips.' } end local symbols = { '7', 'BAR', 'KIRSCHE', 'DIAMANT', 'GLOCKE', 'ZITRONE' } local roll = { symbols[math.random(#symbols)], symbols[math.random(#symbols)], symbols[math.random(#symbols)] } local payout = 0 if roll[1] == roll[2] and roll[2] == roll[3] then payout = bet * (roll[1] == '7' and 12 or 7) elseif roll[1] == roll[2] or roll[2] == roll[3] or roll[1] == roll[3] then payout = bet * 2 end if payout > 0 then addChips(player.characterId, payout) end return { ok = true, symbols = roll, payout = payout, chips = wallet(player.characterId) } end) local redNumbers = { [1]=true,[3]=true,[5]=true,[7]=true,[9]=true,[12]=true,[14]=true,[16]=true,[18]=true,[19]=true,[21]=true,[23]=true,[25]=true,[27]=true,[30]=true,[32]=true,[34]=true,[36]=true } DevXRegisterCallback('casino:roulette', function(source, payload) local player = exports.devx_core:GetPlayer(source); local bet = amount(payload.bet, 100, 10000); local choice = payload.choice if not player or not bet or (choice ~= 'red' and choice ~= 'black' and choice ~= 'green') then return { ok = false, error = 'Ungültiger Einsatz.' } end local removed = removeChips(player.characterId, bet); if not removed then return { ok = false, error = 'Nicht genügend Chips.' } end local number = math.random(0, 36); local color = number == 0 and 'green' or (redNumbers[number] and 'red' or 'black') local payout = color == choice and bet * (color == 'green' and 14 or 2) or 0 if payout > 0 then addChips(player.characterId, payout) end return { ok = true, number = number, color = color, payout = payout, chips = wallet(player.characterId) } end) DevXRegisterCallback('casino:blackjackStart', function(source, payload) local player = exports.devx_core:GetPlayer(source); local bet = amount(payload.bet, 100, 10000) if not player or not bet then return { ok = false, error = 'Ungültiger Einsatz.' } end if blackjackSessions[source] then return { ok = false, error = 'Du spielst bereits eine Runde.' } end local removed = removeChips(player.characterId, bet); if not removed then return { ok = false, error = 'Nicht genügend Chips.' } end local session = { characterId = player.characterId, bet = bet, player = { card(), card() }, dealer = { card(), card() } } blackjackSessions[source] = session local pv, dv = handValue(session.player), handValue(session.dealer) if pv == 21 and dv == 21 then return settleBlackjack(source, session, 'push') end if pv == 21 then return settleBlackjack(source, session, 'blackjack') end if dv == 21 then return settleBlackjack(source, session, 'lose') end return blackjackPayload(session, false, nil) end) DevXRegisterCallback('casino:blackjackAction', function(source, payload) local session = blackjackSessions[source] if not session then return { ok = false, error = 'Keine aktive Blackjack-Runde.' } end if payload.action == 'hit' then session.player[#session.player + 1] = card() local value = handValue(session.player) if value > 21 then return settleBlackjack(source, session, 'bust') end if value == 21 then payload.action = 'stand' else return blackjackPayload(session, false, nil) end end if payload.action == 'stand' then while handValue(session.dealer) < 17 do session.dealer[#session.dealer + 1] = card() end local pv, dv = handValue(session.player), handValue(session.dealer) if dv > 21 or pv > dv then return settleBlackjack(source, session, 'win') elseif pv == dv then return settleBlackjack(source, session, 'push') else return settleBlackjack(source, session, 'lose') end end return { ok = false, error = 'Ungültige Aktion.' } end) RegisterCommand('resetwheel', function(source, args) if source ~= 0 and not IsPlayerAceAllowed(source, 'devx.admin') then return end local target = tonumber(args[1]) or source local player = exports.devx_core:GetPlayer(target) if not player then return end MySQL.query.await('DELETE FROM devx_casino_daily WHERE character_id = ?', { player.characterId }) TriggerClientEvent('devx_casino:client:notify', target, 'Dein täglicher Glücksrad-Dreh wurde zurückgesetzt.') end, false) AddEventHandler('playerDropped', function() spinLocks[source] = nil; gameLocks[source] = nil; blackjackSessions[source] = nil end)