Ir para conteúdo

Featured Replies

Postado

Advanced Roulette System (TFS 0.3.6)

 

Hi everyone! This is my first contribution to the Tibia community, and I hope you find it very useful. It's a gacha-style roulette system with dynamic speed mechanics and multi-key functionality.

 

What's it about?

This script adds a roulette (Tested on: Tibia 8.6 -- TFS 0.3.6) where players can use keys to spin the roulette and win rewards. The system includes:

  • Multi-key support: using 1 to 4 keys per spin, increasing the chances of winning.
  • Dynamic speedThe roulette gradually accelerates during the spin to make it more exciting.
  • Configurable rewardsYou can define which items can be won and with what probability. You can even add "reroll" value to increase the difficulty!
  • Visual effectsIncludes simple effects and text animations to make the experience more engaging.
  • Activity logKeeps a record of which players won which items.

 

Demo Video (x1.5 Speed)

 

 

Installation

  1. Copy the `Roulette.lua` file to the `data/actions/scripts` folder of your TFS server.
    --[[
    ================================================================================
    =                   ROULETTE SYSTEM FOR TFS                                     =
    =                                                                               =
    = Author: Neutras                                                               =
    = Version: 2.1                                                                  =
    = Description: Gacha-style roulette system with dynamic speed mechanics         =
    =              and multi-key feature.                                           =
    =                                                                               =
    = Features:                                                                     =
    = - Multi-key support (1-4 keys per spin).                                      =
    = - Dynamic speed animation with configurable initial and final speeds.         =
    = - Persistent "Winner Slot" effects and animated texts.                        =
    = - Configurable rewards with reroll chances.                                   =
    = - Logging system to track player rewards.                                     =
    =                                                                               =
    = Compatible with TFS 0.3.7 (Tibia 8.6).                                        =
    ================================================================================
    --]]
    
    -- ================= LOGGING SYSTEM ================= --
    local logPath = "data/logs/"
    local logFileName = "roulette.log"
    
    -- Logs player rewards to a file.
    -- @param cid: Player ID.
    -- @param keyName: Name of the key used.
    -- @param items: Table of items won.
    -- @param keyCount: Number of keys used.
    local function logEntry(cid, keyName, items, keyCount)
        local file = io.open(logPath .. logFileName, "a")
        if file then
            local itemStrings = {}
            for _, item in ipairs(items) do
                table.insert(itemStrings, string.format("x%d %s", item.count, getItemNameById(item.id)))
            end
            file:write(string.format("[%s] %s used %d '%s' and won: %s\n",
                os.date("%Y-%m-%d %H:%M:%S"),
                getPlayerName(cid),
                keyCount,
                keyName,
                table.concat(itemStrings, ", ")))
            file:close()
        end
    end
    
    -- ================= BASE CONFIGURATION ================= --
    -- Levers Action IDs to key item IDs.
    local keyByAid = {
        [1354] = 9971, -- Key for reward level 1 (Copper)
        [1355] = 9972, -- Key for reward level 2 (Silver)
        [1356] = 9973  -- Key for reward level 3 (Golden)
    }
    
    -- Levers Action IDs to reward levels.
    local rewardByAid = {
        [1354] = 1, -- Reward level 1 (Copper)
        [1355] = 2, -- Reward level 2 (Silver)
        [1356] = 3  -- Reward level 3 (Golden)
    }
    
    -- Relative positions of the slots in the roulette.
    local rouletteSpinOffset = {
        {1, -4}, {2, -4}, {3, -4}, {3, -3}, {4, -3},
        {4, -2}, {4, -1}, {5, -1}, {5, 0},  {5, 1},
        {4, 1},  {4, 2},  {4, 3},  {3, 3},  {3, 4},
        {2, 4},  {1, 4},  {0, 4},  {-1, 4}, {-2, 4},
        {-3, 4}, {-3, 3}, {-4, 3}, {-4, 2}, {-4, 1},
        {-5, 1}, {-5, 0}, {-5, -1},{-4, -1},{-4, -2},
        {-4, -3},{-3, -3},{-3, -4},{-2, -4},{-1, -4},
        {0, -4}
    }
    
    -- ================= MAIN CONFIGURATION ================= --
    local config = {
        rouletteCD = 30,           -- Global cooldown in seconds.
        globalStoCd = 22600,       -- Storage ID for cooldown.
        globalStoKeyCount = 22601, -- Storage ID for key count.
        maxLoops = 100,            -- Maximum iterations per spin.
        initialSpeed = 50,         -- Initial speed in milliseconds.
        finalSpeed = 400,          -- Final speed in milliseconds.
        effectLever = 35,          -- Effect when activating the lever.
        effectRewardPlayer = 28,   -- Effect on the player when winning.
        effectReward = 28,         -- Effect on the winning slot.
    
        -- Reward table by level.
        -- Formula: Real Probability = (Item Chance / Total Chances) * (1 - (Reroll % / 100))
        items = {
            [1] = { 
                {id = 1, chance = 80, count = 5},
            },
            [2] = {
                {id = 2, chance = 70, count = 1, porc_cambio = 30},
            },
            [3] = {
                {id = 3, chance = 25, count = 1, porc_cambio = 70}
            }
        }    
    }
    
    -- ================= PROBABILITY CACHING ================= --
    -- Precalculates cumulative probabilities for each reward level.
    local cumulativeChanceCache = {}
    for rewardId, items in pairs(config.items) do
        local total = 0
        local cumulative = {}
        for _, item in ipairs(items) do
            total = total + item.chance
            table.insert(cumulative, {item = item, threshold = total})
        end
        cumulativeChanceCache[rewardId] = {total = total, items = cumulative}
    end
    
    -- ================= UTILITY FUNCTIONS ================= --
    -- Calculates the speed of the roulette animation based on progress.
    -- @param progress: Current progress (0 to 1).
    -- @return: Speed in milliseconds.
    local function calculateSpeed(progress)
        return config.initialSpeed + (config.finalSpeed - config.initialSpeed) * progress^3
    end
    
    -- Selects a random item from the reward table, considering reroll chances.
    -- @param rewardId: Reward level ID.
    -- @return: Selected item.
    local function chooseRouletteItem(rewardId)
        local cache = cumulativeChanceCache[rewardId]
        local roll = math.random(cache.total)
        for _, entry in ipairs(cache.items) do
            if roll <= entry.threshold then
                if entry.item.porc_cambio and math.random(100) <= entry.item.porc_cambio then
                    return chooseRouletteItem(rewardId)
                end
                return entry.item
            end
        end
        return cache.items[#cache.items].item
    end
    
    -- Rotates the slots in the roulette.
    -- @param slots: Table of slots.
    local function rotateSlots(slots)
        local last = slots[36]
        for i = 36, 2, -1 do slots[i] = slots[i-1] end
        slots[1] = last
    end
    
    -- Updates the visual display of the roulette.
    -- @param cpos: Center position of the roulette.
    -- @param slots: Table of slots.
    -- @param isFillingPhase: Whether the slots are being filled for the first time.
    local function updateRouletteDisplay(cpos, slots, isFillingPhase)
        for i = 1, 36 do
            local pos = {
                x = cpos.x + rouletteSpinOffset[i][1],
                y = cpos.y + rouletteSpinOffset[i][2],
                z = cpos.z
            }
            doCleanTile(pos)
            if slots[i] then
                doCreateItem(slots[i].id, slots[i].count, pos)
                -- Show puff effect only during the initial filling phase.
                if isFillingPhase then
                    doSendMagicEffect(pos, 14)
                end
            end
        end
    end
    
    -- ================= WINNER SLOTS AND EFFECTS ================= --
    -- Shows "Winner Slot" animated text on winning slots.
    -- @param cpos: Center position of the roulette.
    -- @param keyCount: Number of keys used.
    local function showWinnerSlots(cpos, keyCount)
        local winningSlots = {}
        if keyCount == 1 then
            winningSlots = {36}
        elseif keyCount == 2 then
            winningSlots = {36, 18}
        elseif keyCount == 3 then
            winningSlots = {36, 18, 9}
        elseif keyCount == 4 then
            winningSlots = {36, 18, 9, 27}
        else
            winningSlots = {36} -- Default to one winning slot if keyCount is invalid.
        end
    
        for _, slot in ipairs(winningSlots) do
            local pos = {
                x = cpos.x + rouletteSpinOffset[slot][1],
                y = cpos.y + rouletteSpinOffset[slot][2],
                z = cpos.z
            }
            doSendAnimatedText(pos, "Winner Slot", TEXTCOLOR_YELLOW)
        end
    end
    
    -- Shows the number of keys in use.
    -- @param cpos: Center position of the roulette.
    local function showKeyCount(cpos)
        local keyCount = getGlobalStorageValue(config.globalStoKeyCount)
        keyCount = (keyCount < 1 or keyCount > 4) and 1 or keyCount
        local pos = {x = 1013, y = 995, z = 7}
        doSendAnimatedText(pos, string.format("Keys: %d", keyCount), TEXTCOLOR_LIGHTBLUE)
    end
    
    -- ================= MAIN ROULETTE LOGIC ================= --
    -- Main animation function, recursively called to simulate the roulette spin.
    -- @param cid: Player ID.
    -- @param cpos: Center position of the roulette.
    -- @param rewardId: ID of the reward level.
    -- @param nloop: Current iteration number.
    -- @param slots: Table of slots (items).
    -- @param keyName: Name of the key used.
    -- @param keyCount: Number of keys used.
    local function shuffle(cid, cpos, rewardId, nloop, slots, keyName, keyCount)
        if nloop > config.maxLoops then
            if isPlayer(cid) then
                -- Determine winning slots based on the number of keys used.
                local winningSlots = {}
                if keyCount == 1 then
                    winningSlots = {36}
                elseif keyCount == 2 then
                    winningSlots = {36, 18}
                elseif keyCount == 3 then
                    winningSlots = {36, 18, 9}
                elseif keyCount == 4 then
                    winningSlots = {36, 18, 9, 27}
                else
                    winningSlots = {36} -- Default to one winning slot if keyCount is invalid.
                end
    
                -- Get the winning items and their positions.
                local wonItems = {}
                local winPositions = {}
                for _, slot in ipairs(winningSlots) do
                    if slots[slot] then
                        table.insert(wonItems, slots[slot])
                        local pos = {
                            x = cpos.x + rouletteSpinOffset[slot][1],
                            y = cpos.y + rouletteSpinOffset[slot][2],
                            z = cpos.z
                        }
                        table.insert(winPositions, pos)
                    end
                end
    
                -- Award the items and display visual effects.
                if #wonItems > 0 then
                    for _, pos in ipairs(winPositions) do
                        doSendAnimatedText(pos, "Winner Slot", TEXTCOLOR_YELLOW)
                        doSendMagicEffect(pos, config.effectReward)
                    end
                    for _, item in ipairs(wonItems) do
                        doPlayerAddItem(cid, item.id, item.count)
                    end
                    doSendMagicEffect(getCreaturePosition(cid), config.effectRewardPlayer)
                    
                     -- Display a message to the player with all the rewards.
                    local itemList = {}
                    for _, item in ipairs(wonItems) do
                        table.insert(itemList, string.format("x%d %s", item.count, getItemNameById(item.id)))
                    end
                    doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[ROULETTE] You won: " .. table.concat(itemList, ", "))
                    
                    -- Log the player's rewards.
                    logEntry(cid, keyName, wonItems, keyCount)
                else
                    doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[ROULETTE] No items won.")
                end
                setGlobalStorageValue(config.globalStoCd, 0)
            end
            return
        end
    
        -- Initial filling phase of the roulette slots.
        if nloop <= 36 then
            slots[nloop] = chooseRouletteItem(rewardId)
            updateRouletteDisplay(cpos, slots, true)
        else
            -- Rotate the slots and update the display.
            rotateSlots(slots)
            updateRouletteDisplay(cpos, slots, false)
            
            -- Show effects on the winning slots every 5 iterations.
            if nloop % 5 == 0 then
                local winningSlots = {}
                if keyCount == 1 then
                    winningSlots = {36}
                elseif keyCount == 2 then
                    winningSlots = {36, 18}
                elseif keyCount == 3 then
                    winningSlots = {36, 18, 9}
                elseif keyCount == 4 then
                    winningSlots = {36, 18, 9, 27}
                else
                    winningSlots = {36} -- Default to one winning slot if keyCount is invalid.
                end
    
                for _, slot in ipairs(winningSlots) do
                    local pos = {
                        x = cpos.x + rouletteSpinOffset[slot][1],
                        y = cpos.y + rouletteSpinOffset[slot][2],
                        z = cpos.z
                    }
                    doSendMagicEffect(pos, config.effectReward)
                end
            end
        end
    
        -- Schedule the next iteration with dynamic speed.
        local progress = nloop / config.maxLoops
        addEvent(shuffle, calculateSpeed(progress), cid, cpos, rewardId, nloop + 1, slots, keyName, keyCount)
    end
    
    -- ================= PERIODIC EFFECTS AND TEXTS ================= --
    -- Shows effects and texts periodically.
    -- @param cpos: Center position of the roulette.
    local function showEffectsAndTexts(cpos)
        local keyCount = getGlobalStorageValue(config.globalStoKeyCount)
        keyCount = (keyCount < 1 or keyCount > 4) and 1 or keyCount -- Ensure keyCount is within range.
    
        -- Show "Winner Slot" on the winning slots.
        showWinnerSlots(cpos, keyCount)
    
        -- Show the number of keys in use.
        showKeyCount(cpos)
    
        -- Schedule the next execution.
        addEvent(showEffectsAndTexts, 1500, cpos)
    end
    
    -- ================= EFFECT SCRIPT INITIALIZATION ================= --
    -- Start the periodic effects and texts when the script is loaded.
    local cpos = {x = 1012, y = 994, z = 7} -- Center position of the roulette.
    addEvent(function()
        showEffectsAndTexts(cpos)
    end, 5000) -- 5 seconds delay since server start.
    
    -- ================= MAIN OBJECT USE FUNCTION ================= --
    -- Called when the roulette object is used.
    function onUse(cid, item, frompos, item2, topos)
        -- Handle the key change lever.
        if item.aid == 1360 then
            local current = getGlobalStorageValue(config.globalStoKeyCount)
            current = (current < 1 or current > 4) and 1 or (current % 4) + 1
            setGlobalStorageValue(config.globalStoKeyCount, current)
            doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, string.format("Now using %d keys per spin.", current))
            doSendMagicEffect(getThingPos(item.uid), CONST_ME_MAGIC_GREEN)
            return true
        end
    
        -- Handle the roulette levers.
        if not keyByAid[item.aid] then return false end
    
        local key = keyByAid[item.aid]
        local keyName = getItemNameById(key)
        local requiredKeys = getGlobalStorageValue(config.globalStoKeyCount)
        requiredKeys = (requiredKeys < 1 or requiredKeys > 4) and 1 or requiredKeys
    
        if getPlayerAccess(cid) < 5 and getPlayerItemCount(cid, key) < requiredKeys then
            doPlayerSendCancel(cid, string.format("You need %d %s to play!", requiredKeys, keyName))
            doSendMagicEffect(topos, 14)
            return true
        end
    
        local rewardId = rewardByAid[item.aid] or 1 -- Get the reward level based on the lever. Default to 1 if not found.
        local pos = {x = 1012, y = 994, z = 7} -- Center position of the roulette.
    
        if getGlobalStorageValue(config.globalStoCd) > os.time() and getPlayerAccess(cid) < 5 then
            local remaining = getGlobalStorageValue(config.globalStoCd) - os.time()
            doPlayerSendCancel(cid, "Wait " .. remaining .. " seconds to play again.")
            return true
        end
    
        setGlobalStorageValue(config.globalStoCd, os.time() + config.rouletteCD) -- Set the cooldown.
        doTransformItem(item.uid, item.itemid == 9825 and 9826 or 9825) -- Change the lever's appearance.
    
        -- Clear the tiles around the roulette and add magic effects.
        for i = 1, 36 do
            local rpos = {
                x = pos.x + rouletteSpinOffset[i][1],
                y = pos.y + rouletteSpinOffset[i][2],
                z = pos.z
            }
            doCleanTile(rpos)
            doSendMagicEffect(rpos, config.effectReward)
        end
    
        if key > 0 then doPlayerRemoveItem(cid, key, requiredKeys) end -- Remove the keys from the player's inventory.
        doSendMagicEffect(pos, config.effectLever) -- Play the lever activation effect.
    
        math.randomseed(os.time() + getPlayerGUID(cid)) -- Seed the random number generator.
        addEvent(shuffle, config.initialSpeed, cid, pos, rewardId, 1, {}, keyName, requiredKeys) -- Start the roulette animation.
        return true
    end

     

  2. Add the following lines within the `<actions>` tag in your `data/actions/actions.xml` file:
    <action actionid="XXX;YYYY;ZZZZ;AAAA" event="script" value="Roulette.lua"/>

    Replace `XXXX`, `YYYY`, and `ZZZZ` with the unique IDs of the levers that will activate the roulette (make sure they are not in use!).
    The ID `AAAA` is for the lever that changes the number of keys to use.

     

  3. Open the `Roulette.lua` file and adjust the `config.items` table with the items you want players to be able to win. Remember to balance the probabilities.

 

 

Code Explanation
The script is divided into several sections to facilitate understanding:

  • LOGGING SYSTEM: Handles logging the rewards obtained by players.
  • BASE CONFIGURATIONDefines object IDs and reward levels.
  • MAIN CONFIGURATION: Contains the main options of the system, such as the wait time between spins, the speed of the roulette, and the rewards.
  • PROBABILITY CACHING: Optimizes the calculation of probabilities so that the system runs smoothly.
  • UTILITY FUNCTIONS: Helper functions to calculate speed, choose items randomly, and update the roulette display.
  • WINNER SLOTS AND EFFECTS: Displays the "Winner Slot" text and other visual effects in the winning spaces.
  • MAIN ROULETTE LOGIC: The main function that controls the roulette animation and the delivery of rewards.
  • PERIODIC EFFECTS AND TEXTS: Displays effects and texts periodically.
  • SCRIPT INITIALIZATION: Initializes the effect system when the script is loaded.
  • MAIN OBJECT USE FUNCTION: The function that is executed when a player interacts with a lever.

    I hope this roulette system is a great addition to your server! If you have any questions or suggestions, please feel free to leave a comment. Thank you for your support!

 

 

  • 1 month later...
Postado

 

Olha Como o Meu Tá e Não ta Funcionando Olha Mapa: ONDE EU TO É A POS: x266 y233 z15 

 

<action actionid="4725" script="roulette.lua"/>
<action actionid="4571" script="roulette.lua"/>
<action actionid="4545" script="roulette.lua"/>
<action actionid="4478" script="roulette.lua"/>

 

 

 

 

 

 

Screenshot-2

 

 

 

 

 

 

--[[
================================================================================
=                   ROULETTE SYSTEM FOR TFS                                     =
=                                                                               =
= Author: Neutras                                                               =
= Version: 2.1                                                                  =
= Description: Gacha-style roulette system with dynamic speed mechanics         =
=              and multi-key feature.                                           =
=                                                                               =
= Features:                                                                     =
= - Multi-key support (1-4 keys per spin).                                      =
= - Dynamic speed animation with configurable initial and final speeds.         =
= - Persistent "Winner Slot" effects and animated texts.                        =
= - Configurable rewards with reroll chances.                                   =
= - Logging system to track player rewards.                                     =
=                                                                               =
= Compatible with TFS 0.3.7 (Tibia 8.6).                                        =
================================================================================
--]]

--[[
================================================================================
=                   ROULETTE SYSTEM FOR TFS                                     =
=                                                                               =
= Author: Neutras                                                               =
= Version: 2.1                                                                  =
= Description: Gacha-style roulette system with dynamic speed mechanics         =
=              and multi-key feature.                                           =
=                                                                               =
= Features:                                                                     =
= - Multi-key support (1-4 keys per spin).                                      =
= - Dynamic speed animation with configurable initial and final speeds.         =
= - Persistent "Winner Slot" effects and animated texts.                        =
= - Configurable rewards with reroll chances.                                   =
= - Logging system to track player rewards.                                     =
=                                                                               =
= Compatible with TFS 0.3.7 (Tibia 8.6).                                        =
================================================================================
--]]

-- ================= LOGGING SYSTEM ================= --
local logPath = "data/logs/"
local logFileName = "roulette.log"

-- Logs player rewards to a file.
-- @param cid: Player ID.
-- @param keyName: Name of the key used.
-- @param items: Table of items won.
-- @param keyCount: Number of keys used.
local function logEntry(cid, keyName, items, keyCount)
    local file = io.open(logPath .. logFileName, "a")
    if file then
        local itemStrings = {}
        for _, item in ipairs(items) do
            table.insert(itemStrings, string.format("x%d %s", item.count, getItemNameById(item.id)))
        end
        file:write(string.format("[%s] %s used %d '%s' and won: %s\n",
            os.date("%Y-%m-%d %H:%M:%S"),
            getPlayerName(cid),
            keyCount,
            keyName,
            table.concat(itemStrings, ", ")))
        file:close()
    end
end

-- ================= BASE CONFIGURATION ================= --
-- Levers Action IDs to key item IDs.
local keyByAid = {
    [1354] = 9971, -- Key for reward level 1 (Copper)
    [1355] = 9972, -- Key for reward level 2 (Silver)
    [1356] = 9973  -- Key for reward level 3 (Golden)
}

-- Levers Action IDs to reward levels.
local rewardByAid = {
    [1354] = 1, -- Reward level 1 (Copper)
    [1355] = 2, -- Reward level 2 (Silver)
    [1356] = 3  -- Reward level 3 (Golden)
}

-- Relative positions of the slots in the roulette.
local rouletteSpinOffset = {
    {1, -4}, {2, -4}, {3, -4}, {3, -3}, {4, -3},
    {4, -2}, {4, -1}, {5, -1}, {5, 0},  {5, 1},
    {4, 1},  {4, 2},  {4, 3},  {3, 3},  {3, 4},
    {2, 4},  {1, 4},  {0, 4},  {-1, 4}, {-2, 4},
    {-3, 4}, {-3, 3}, {-4, 3}, {-4, 2}, {-4, 1},
    {-5, 1}, {-5, 0}, {-5, -1},{-4, -1},{-4, -2},
    {-4, -3},{-3, -3},{-3, -4},{-2, -4},{-1, -4},
    {0, -4}
}

-- ================= MAIN CONFIGURATION ================= --
local config = {
    rouletteCD = 30,           -- Global cooldown in seconds.
    globalStoCd = 22600,       -- Storage ID for cooldown.
    globalStoKeyCount = 22601, -- Storage ID for key count.
    maxLoops = 100,            -- Maximum iterations per spin.
    initialSpeed = 50,         -- Initial speed in milliseconds.
    finalSpeed = 400,          -- Final speed in milliseconds.
    effectLever = 35,          -- Effect when activating the lever.
    effectRewardPlayer = 28,   -- Effect on the player when winning.
    effectReward = 28,         -- Effect on the winning slot.

    -- Reward table by level.
    -- Formula: Real Probability = (Item Chance / Total Chances) * (1 - (Reroll % / 100))
    items = {
        [1] = { 
        {id = 4871, chance = 100, count = 1},
    },
    [2] = {
        {id = 4872, chance = 100, count = 1},
    },
    [3] = {
        {id = 4870, chance = 100, count = 1},
    },
    [4] = {
        {id = 5899, chance = 100, count = 1},
    },
    [5] = {
        {id = 2349, chance = 100, count = 1},
    },
    [6] = {
        {id = 9003, chance = 100, count = 1},
    },
    [7] = {
        {id = 2300, chance = 100, count = 1},
    },
    [8] = {
        {id = 2309, chance = 100, count = 1},
    },
    [9] = {
        {id = 2272, chance = 100, count = 1},
    },
    [10] = {
        {id = 2306, chance = 100, count = 1},
    },
    [11] = {
        {id = 2447, chance = 100, count = 1},
    },
    [12] = {  
        {id = 7739, chance = 100, count = 1},
    },
    [13] = {
        {id = 7737, chance = 100, count = 1},
    },
    [14] = {          
        {id = 5805, chance = 100, count = 1},
    },
    [15] = {      
        {id = 2446, chance = 100, count = 1}
       }
    }    
}

-- ================= PROBABILITY CACHING ================= --
-- Precalculates cumulative probabilities for each reward level.
local cumulativeChanceCache = {}
for rewardId, items in pairs(config.items) do
    local total = 0
    local cumulative = {}
    for _, item in ipairs(items) do
        total = total + item.chance
        table.insert(cumulative, {item = item, threshold = total})
    end
    cumulativeChanceCache[rewardId] = {total = total, items = cumulative}
end

-- ================= UTILITY FUNCTIONS ================= --
-- Calculates the speed of the roulette animation based on progress.
-- @param progress: Current progress (0 to 1).
-- @return: Speed in milliseconds.
local function calculateSpeed(progress)
    return config.initialSpeed + (config.finalSpeed - config.initialSpeed) * progress^3
end

-- Selects a random item from the reward table, considering reroll chances.
-- @param rewardId: Reward level ID.
-- @return: Selected item.
local function chooseRouletteItem(rewardId)
    local cache = cumulativeChanceCache[rewardId]
    local roll = math.random(cache.total)
    for _, entry in ipairs(cache.items) do
        if roll <= entry.threshold then
            if entry.item.porc_cambio and math.random(100) <= entry.item.porc_cambio then
                return chooseRouletteItem(rewardId)
            end
            return entry.item
        end
    end
    return cache.items[#cache.items].item
end

-- Rotates the slots in the roulette.
-- @param slots: Table of slots.
local function rotateSlots(slots)
    local last = slots[36]
    for i = 36, 2, -1 do slots[i] = slots[i-1] end
    slots[1] = last
end

-- Updates the visual display of the roulette.
-- @param cpos: Center position of the roulette.
-- @param slots: Table of slots.
-- @param isFillingPhase: Whether the slots are being filled for the first time.
local function updateRouletteDisplay(cpos, slots, isFillingPhase)
    for i = 1, 36 do
        local pos = {
            x = cpos.x + rouletteSpinOffset[i][1],
            y = cpos.y + rouletteSpinOffset[i][2],
            z = cpos.z
        }
        doCleanTile(pos)
        if slots[i] then
            doCreateItem(slots[i].id, slots[i].count, pos)
            -- Show puff effect only during the initial filling phase.
            if isFillingPhase then
                doSendMagicEffect(pos, 14)
            end
        end
    end
end

-- ================= WINNER SLOTS AND EFFECTS ================= --
-- Shows "Winner Slot" animated text on winning slots.
-- @param cpos: Center position of the roulette.
-- @param keyCount: Number of keys used.
local function showWinnerSlots(cpos, keyCount)
    local winningSlots = {}
    if keyCount == 1 then
        winningSlots = {36}
    elseif keyCount == 2 then
        winningSlots = {36, 18}
    elseif keyCount == 3 then
        winningSlots = {36, 18, 9}
    elseif keyCount == 4 then
        winningSlots = {36, 18, 9, 27}
    else
        winningSlots = {36} -- Default to one winning slot if keyCount is invalid.
    end

    for _, slot in ipairs(winningSlots) do
        local pos = {
            x = cpos.x + rouletteSpinOffset[slot][1],
            y = cpos.y + rouletteSpinOffset[slot][2],
            z = cpos.z
        }
        doSendAnimatedText(pos, "Winner", TEXTCOLOR_YELLOW)
    end
end

-- Shows the number of keys in use.
-- @param cpos: Center position of the roulette.
local function showKeyCount(cpos)
    local keyCount = getGlobalStorageValue(config.globalStoKeyCount)
    keyCount = (keyCount < 1 or keyCount > 4) and 1 or keyCount
    local pos = {x = 266, y = 233, z = 15}
    doSendAnimatedText(pos, string.format("Keys: %d", keyCount), TEXTCOLOR_LIGHTBLUE)
end

-- ================= MAIN ROULETTE LOGIC ================= --
-- Main animation function, recursively called to simulate the roulette spin.
-- @param cid: Player ID.
-- @param cpos: Center position of the roulette.
-- @param rewardId: ID of the reward level.
-- @param nloop: Current iteration number.
-- @param slots: Table of slots (items).
-- @param keyName: Name of the key used.
-- @param keyCount: Number of keys used.
local function shuffle(cid, cpos, rewardId, nloop, slots, keyName, keyCount)
    if nloop > config.maxLoops then
        if isPlayer(cid) then
            -- Determine winning slots based on the number of keys used.
            local winningSlots = {}
            if keyCount == 1 then
                winningSlots = {36}
            elseif keyCount == 2 then
                winningSlots = {36, 18}
            elseif keyCount == 3 then
                winningSlots = {36, 18, 9}
            elseif keyCount == 4 then
                winningSlots = {36, 18, 9, 27}
            else
                winningSlots = {36} -- Default to one winning slot if keyCount is invalid.
            end

            -- Get the winning items and their positions.
            local wonItems = {}
            local winPositions = {}
            for _, slot in ipairs(winningSlots) do
                if slots[slot] then
                    table.insert(wonItems, slots[slot])
                    local pos = {
                        x = cpos.x + rouletteSpinOffset[slot][1],
                        y = cpos.y + rouletteSpinOffset[slot][2],
                        z = cpos.z
                    }
                    table.insert(winPositions, pos)
                end
            end

            -- Award the items and display visual effects.
            if #wonItems > 0 then
                for _, pos in ipairs(winPositions) do
                    doSendAnimatedText(pos, "Winner Slot", TEXTCOLOR_YELLOW)
                    doSendMagicEffect(pos, config.effectReward)
                end
                for _, item in ipairs(wonItems) do
                    doPlayerAddItem(cid, item.id, item.count)
                end
                doSendMagicEffect(getCreaturePosition(cid), config.effectRewardPlayer)
                
                 -- Display a message to the player with all the rewards.
                local itemList = {}
                for _, item in ipairs(wonItems) do
                    table.insert(itemList, string.format("x%d %s", item.count, getItemNameById(item.id)))
                end
                doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[ROULETTE] You won: " .. table.concat(itemList, ", "))
                
                -- Log the player's rewards.
                logEntry(cid, keyName, wonItems, keyCount)
            else
                doPlayerSendTextMessage(cid, MESSAGE_STATUS_CONSOLE_BLUE, "[ROULETTE] No items won.")
            end
            setGlobalStorageValue(config.globalStoCd, 0)
        end
        return
    end

    -- Initial filling phase of the roulette slots.
    if nloop <= 36 then
        slots[nloop] = chooseRouletteItem(rewardId)
        updateRouletteDisplay(cpos, slots, true)
    else
        -- Rotate the slots and update the display.
        rotateSlots(slots)
        updateRouletteDisplay(cpos, slots, false)
        
        -- Show effects on the winning slots every 5 iterations.
        if nloop % 5 == 0 then
            local winningSlots = {}
            if keyCount == 1 then
                winningSlots = {36}
            elseif keyCount == 2 then
                winningSlots = {36, 18}
            elseif keyCount == 3 then
                winningSlots = {36, 18, 9}
            elseif keyCount == 4 then
                winningSlots = {36, 18, 9, 27}
            else
                winningSlots = {36} -- Default to one winning slot if keyCount is invalid.
            end

            for _, slot in ipairs(winningSlots) do
                local pos = {
                    x = cpos.x + rouletteSpinOffset[slot][1],
                    y = cpos.y + rouletteSpinOffset[slot][2],
                    z = cpos.z
                }
                doSendMagicEffect(pos, config.effectReward)
            end
        end
    end

    -- Schedule the next iteration with dynamic speed.
    local progress = nloop / config.maxLoops
    addEvent(shuffle, calculateSpeed(progress), cid, cpos, rewardId, nloop + 1, slots, keyName, keyCount)
end

-- ================= PERIODIC EFFECTS AND TEXTS ================= --
-- Shows effects and texts periodically.
-- @param cpos: Center position of the roulette.
local function showEffectsAndTexts(cpos)
    local keyCount = getGlobalStorageValue(config.globalStoKeyCount)
    keyCount = (keyCount < 1 or keyCount > 4) and 1 or keyCount -- Ensure keyCount is within range.

    -- Show "Winner Slot" on the winning slots.
    showWinnerSlots(cpos, keyCount)

    -- Show the number of keys in use.
    showKeyCount(cpos)

    -- Schedule the next execution.
    addEvent(showEffectsAndTexts, 1500, cpos)
end

-- ================= EFFECT SCRIPT INITIALIZATION ================= --
-- Start the periodic effects and texts when the script is loaded.
local cpos = {x = 266, y = 233, z = 15} -- Center position of the roulette.
addEvent(function()
    showEffectsAndTexts(cpos)
end, 5000) -- 5 seconds delay since server start.

-- ================= MAIN OBJECT USE FUNCTION ================= --
-- Called when the roulette object is used.
function onUse(cid, item, frompos, item2, topos)
    -- Handle the key change lever.
    if item.aid == 1360 then
        local current = getGlobalStorageValue(config.globalStoKeyCount)
        current = (current < 1 or current > 4) and 1 or (current % 4) + 1
        setGlobalStorageValue(config.globalStoKeyCount, current)
        doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, string.format("Now using %d keys per spin.", current))
        doSendMagicEffect(getThingPos(item.uid), CONST_ME_MAGIC_GREEN)
        return true
    end

    -- Handle the roulette levers.
    if not keyByAid[item.aid] then return false end

    local key = keyByAid[item.aid]
    local keyName = getItemNameById(key)
    local requiredKeys = getGlobalStorageValue(config.globalStoKeyCount)
    requiredKeys = (requiredKeys < 1 or requiredKeys > 4) and 1 or requiredKeys

    if getPlayerAccess(cid) < 5 and getPlayerItemCount(cid, key) < requiredKeys then
        doPlayerSendCancel(cid, string.format("You need %d %s to play!", requiredKeys, keyName))
        doSendMagicEffect(topos, 14)
        return true
    end

    local rewardId = rewardByAid[item.aid] or 1 -- Get the reward level based on the lever. Default to 1 if not found.
    local pos = {x = 266, y = 233, z = 15} -- Center position of the roulette.

    if getGlobalStorageValue(config.globalStoCd) > os.time() and getPlayerAccess(cid) < 5 then
        local remaining = getGlobalStorageValue(config.globalStoCd) - os.time()
        doPlayerSendCancel(cid, "Wait " .. remaining .. " seconds to play again.")
        return true
    end

    setGlobalStorageValue(config.globalStoCd, os.time() + config.rouletteCD) -- Set the cooldown.
    doTransformItem(item.uid, item.itemid == 9825 and 9826 or 9825) -- Change the lever's appearance.

    -- Clear the tiles around the roulette and add magic effects.
    for i = 1, 36 do
        local rpos = {
            x = pos.x + rouletteSpinOffset[i][1],
            y = pos.y + rouletteSpinOffset[i][2],
            z = pos.z
        }
        doCleanTile(rpos)
        doSendMagicEffect(rpos, config.effectReward)
    end

    if key > 0 then doPlayerRemoveItem(cid, key, requiredKeys) end -- Remove the keys from the player's inventory.
    doSendMagicEffect(pos, config.effectLever) -- Play the lever activation effect.

    math.randomseed(os.time() + getPlayerGUID(cid)) -- Seed the random number generator.
    addEvent(shuffle, config.initialSpeed, cid, pos, rewardId, 1, {}, keyName, requiredKeys) -- Start the roulette animation.
    return true
end

 

Editado por Muvukaa (veja o histórico de edições)

  • Under mudou o título para [Actions] Advanced Roulette System

Participe da conversa

Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.

Visitante
Responder

Quem Está Navegando 0

  • Nenhum usuário registrado visualizando esta página.

Estatísticas dos Fóruns

  • Tópicos 96.9k
  • Posts 519.6k

Informação Importante

Confirmação de Termo