first commit

This commit is contained in:
Colin-Joel Scupin
2026-08-05 01:29:39 +02:00
commit 86d4bcdc37
85 changed files with 7706 additions and 0 deletions
@@ -0,0 +1,231 @@
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)