Ir para conteúdo
  • Cadastre-se

Posts Recomendados

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!

 

 

Link para o post
Compartilhar em outros sites
  • Administrador

Obrigado pela contribuição!

TibiaKing Team- KingTópicos
www.tibiaking.com

Link para o post
Compartilhar em outros sites
  • 1 month later...

 

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)
Link para o post
Compartilhar em outros sites
  • 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

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emojis são permitidos.

×   Seu link foi automaticamente incorporado.   Mostrar como link

×   Seu conteúdo anterior foi restaurado.   Limpar o editor

×   Não é possível colar imagens diretamente. Carregar ou inserir imagens do URL.

  • Quem Está Navegando   0 membros estão online

    Nenhum usuário registrado visualizando esta página.

  • Conteúdo Similar

    • Por xWhiteWolf
      Fala galera do TK, vejo que tem bastante gente procurando esse sisteminha que é praticamente igual ao dodge system, então eu decidi fazer visto que na realidade era só mudar 3 linhas kkkk em todo caso ta aí pra quem quiser:

      creaturescripts.xml:
       <!-- CRITICAL SYSTEM -->    <event type="statschange" name="critical" event="script" value="critical.lua"/> creaturescripts\scripts\login.lua:
      --- CRITICAL SYSTEM ---- registerCreatureEvent(cid, "critical") if getPlayerStorageValue(cid, 48913) == -1 then         setPlayerStorageValue(cid, 48913, 0)      end creaturescritps\scripts\critical.lua:
      --[[Critical System -------------------------  By Night Wolf]] local lvlcrit = 48913 local multiplier = 1.5 function onStatsChange(cid, attacker, type, combat, value) if isPlayer(attacker) and (not (attacker == cid)) and (type == STATSCHANGE_HEALTHLOSS or type == STATSCHANGE_MANALOSS)  then if (getPlayerStorageValue(attacker, lvlcrit)*3) >= math.random (0,1000) then dano = math.ceil(value*(multiplier)) doTargetCombatHealth(attacker, cid, combat, -dano, -dano, 255) doSendAnimatedText(getCreaturePos(attacker), "CRITICAL!!", 144) return false end end return true end lvlcrit é o storage que fica salvo o seu level de critical e multiplier é o multiplicador do dano para ataques críticos.. nesse caso um ataque critico vai ser 1,5 vezes maior doque um ataque normal (50% maior)

      Agora em actions.xml adicione:
      <action itemid="1294" script="criticalrock.lua"/> e em actions\scripts\criticalrock.lua adicione:
      --- CRITICAL System by Night Wolf       local config = {    effectonuse = 14, -- efeito que sai    levelscrit = 100,  --- leveis que terão    storagecrit = 48913 -- storage que será verificado    }     function onUse(cid, item, frompos, item2, topos)     if getPlayerStorageValue(cid, config.storagecrit) < config.levelscrit then    doRemoveItem(item.uid, 1) doSendMagicEffect(topos,config.effectonuse) doPlayerSendTextMessage(cid,22,"You've Leveled your Critical Skill to ["..(getPlayerStorageValue(cid, config.storagecrit)+1).."/"..config.levelscrit.."].") setPlayerStorageValue(cid, config.storagecrit, getPlayerStorageValue(cid, config.storagecrit)+1) elseif getPlayerStorageValue(cid, config.storagecrit) >= config.levelscrit then doPlayerSendTextMessage(cid,22,"You've already reached the MAX level of Critical Skill.\nCongratulations!!!!")     return 0     end return 1 end Feito isso tá pronto, pra editar o item que dá a skill de critical vc edita no actions.xml mesmo:
      <action itemid="1294"   << ID do item que será usado pra dar a skill.
      A config tá bem simples:
      effectonuse = 14, -- efeito que sai
         levelscrit = 100,  --- leveis que terão 
         storagecrit = 48913 -- storage que será verificado.

      Lembrando que cada pedra utilizada dará 0,3% a mais de chance.. 10 pedras dão 3% de chance de dar critico a cada ataque e 100 pedras (NIVEL MÁXIMO PADRÃO) dará 30% de chance de dar crítico em cada ataque.
      Espero que vcs gostem, qualquer coisa deixem os comentários aqui.

      Obs: aqui tá uma foto


      Note que esse script só funciona em players, se vc quiser que funcione em monstros você vai ter que abrir um por um todos os monstros do server e colocar essa tag aqui: 
      <script> <event name="critical"/> </script> coloque antes de  </monster>
      Minha dica: coloquem apenas no Trainer pra que o player consiga ver que ele tem o critical e quanto ele tira e deixem avisado que o sistema só vai funcionar em players. 
    • Por KekezitoLHP
      Nome: Fist Fighting/Attackspeed
      Tipo: C++
      Autor: Oneshot
       
      Já vi alguns pedidos no fórum sobre a skill Fist Fighting, onde quanto mais você treinasse ela, mais rápido você atacaria no jogo, e parece que isto é um feature do Tibia. Como é uma modificação muito fácil nas sources, resolvi passar aí para a galera.
      Por padrão, o intervalo entre ataques do Tibia é 2000ms, ou seja, um ataque físico a cada dois segundos. Eu fiz uma pequena modificação nas sources onde o Fist Fighting seria inversamente proporcional ao tal intervalo, ou seja, quanto maior o valor da skill, menor seria o intervalo.

      Fiz de um modo que um jogador com Fist Fighting de nível 200, então, teria uma redução de 75% no intervalo de ataque, ou seja, um ataque a cada meio segundo ou dois ataques por segundo

      Leve em consideração que ele pega como base o attackspeed da vocação ou da arma usada, ou seja, se seu servidor já tem o tal chamado "fast attack", de nada adianta adicionar esse código C++.
       
       
      Abra seu player.cpp, procure por isso:
      Player::getAttackSpeed() Substitua toda a função, dependendo da versão de seu servidor:
       
      0.3.6
      uint32_t Player::getAttackSpeed() {     Item* weapon = getWeapon();     if(weapon && weapon->getAttackSpeed() != 0)         return std::ceil(weapon->getAttackSpeed() * (1 - (getSkill(SKILL_FIST, SKILL_LEVEL) * 0.00375)));     return std::ceil(vocation->getAttackSpeed() * (1 - (getSkill(SKILL_FIST, SKILL_LEVEL) * 0.00375))); } 0.4
      uint32_t Player::getAttackSpeed() const {     return std::ceil(((weapon && weapon->getAttackSpeed() != 0) ? weapon->getAttackSpeed() * (1 - (getSkill(SKILL_FIST, SKILL_LEVEL) * 0.00375)) : (vocation->getAttackSpeed() / std::max((size_t)1, getWeapons().size()) * (1 - (getSkill(SKILL_FIST, SKILL_LEVEL) * 0.00375))))); } Isso adiciona uma utilidade para a skill Fist Fighting que em muitos dos servidores é algo deixado de lado e inútil.
    • Por SlowK
      Pokémon Centurion
      (Tópico atualizado apenas para melhorar a aparência, servidor sem atualização desde 19/06/2014).
       
      Distro: Pokémon Dash Online Advanced 1.0 (PDA [Baseada na TFS 0.3.6])
      (Não tenho source).
       - Antigo cliente 8.54 -
       
      • Informações •
      - Ataques m1 ao m12
      - Sistema de bike
      - Sistema de boost
      - Trade Center
      - Mapa LunusOT
      - Sistema de profissões
      - Pokémons com níveis (com evolução)
      - Vários Shinies dentre eles da nova geração
      - Rocket (Giovani) e Police
      - Addons novos
      - Alguns Pokés com remakes
      - Várias quests escondidas
       
      • Algumas imagens •
      (Caso não estejam aparecendo, baixe aqui: IMAGENS)
       
      Senha do ADM: god/god
       
      • Links •
      Servidor
      Cliente
      (Mediafire já fornece scan semelhante ao vírustotal, basta descer a página um pouco).
       
      • Créditos •
      Cacaiu Alexandre
      Bruno Maciel
      Patrick Estrela
      SlowMCZ
       
       
      Obs: Servidor não será mais atualizado, nem darei ajuda, pois faz muito tempo que não mexo com tal server.
    • Por dodoby
      - Pokemon Imperium -
      ✺ INFORMAÇÕES
      ✺EDIÇÕES
      ✺PRINT's
      ✺ERROS - BUGS
      ✺CRÉDITOS
      ✺DOWNLOAD's

      Projeto tinha somente minha (LORDBAXX) dedicação, e tenho certeza que servirá para boas bases de projetos futuros.
      Eu trabalhava nele sempre um pouco, tanto na criação de sistemas quanto em codes para as sources (Ainda não muito modificada), o servidor é instituído somente pela primeira geração e todos os shinys (Primeira Versão), um detalhe sobre os shinys é que são de respawn com cores diferentes, pode nascer tanto de uma cor quanto de outra! (EXCRUSIVO)
      Continuo Trabalhando no servidor, então qualquer atualização estarei postando, e quem puder ajudar em melhorias, toda ajuda é bem vinda! 
      Vou listar alguns sistemas novos!
      ¤ Egg System (Único) - 100%
      ¤ TM System
      (Não único porém diferenciado)
      ¤ Age System
      (Sistema de idade nos pokemons, quando seu pokemons alcançar certas idades, vc pode ganhar items e até egg's, "IDADE MÁXIMA 30", Pode ser modificado)
      ¤ Evolution System
      (Modificado, seu pokemon pode ser evoluído pelo level usando um item de evolução que mostrarei logo mais nos prints, ou utilizando stone sem precisar de level)
      ¤ Pokeball's animadas
      ¤ Rare Candy para level e para idade
      ¤ Boost system
      (Temporariamente retirado para testes, talvez volte na v2)
      Bom estes são alguns dos sistemas, agora irei mostrar os sistemas tradicionais de todo servidor!
      ¤ Catch System - 100%
      ¤ Nurse - 100%
      ¤ 6 Balls diferentes - Normal ball, Super ball, Great ball, Ultra ball, Master ball e fresh ball
      ¤ Order system - 100% (Move, rock smash, dig, cut, fly, ride, surf ambos funcionando perfeitamente)
      ¤ Evoluções - 100%
      ¤ Pokemons Passivos e Agressivos - 100% (Demorou mais consegui rs')
      ¤ Mapa incompleto - somento uns 5 ou 10 % do mapa feito por mim ( Estava focado nas script's )
      ¤ Gender system - 100%
      ¤ Pokemons balanceados de acordo com o level e com seus determinados loot's e moves
      Acho que é isso se esqueci algo posto logo mais!
      Print's




      Erros-Bug's



      Créditos
      Lordbaxx - Sistemas, Scripts, codes, mapa - SERVER
      NextBr - Pelo sistema de idade no qual trabalhei
      Qualquer outro envolvido será creditado!
      DOWNLOAD's



      LEMBRANDO QUE AS SPRITES DOS POKEMONS SÃO DAS MAIS ANTIGAS ENTÃO SEM RECLAMAÇÕES POIS É UMA COISA SIMPLES DE SE FAZER!
      OBRIGADO VLW FLW
      att: LORDBAXX
    • Por Elitondeveloper
      Venho disponibilizar para vocês gratuitamente o servidor Alfa-Baiak, que atualmente está online com 250+ players.
      Algumas imagens em anexo!
      Totalmente Completo e de graça !!
      Façam bom proveito para seus projetos !!
       
      Informações
      Várias Quests
      4 sistemas de Vip
      1 donate
      hunts para level 620+
      Caves exclusivas
      novos itens!
      Muita jogabilidade
      Vocações balancedas
      Magias Balanceadas
      Sem Bugs
       
      Eventos
      Battle Field
      Capiture a Bandeira
      Defesa das torres
      Fire Storm
      Zombie Ataque
      SnowBall War
      War Anti-Entrosa
      Castle 24hrs
      Castle War

       Exp e Rates
      Exp inicial: 700x | (stages)
      Magic Rate: 10.0x |
      Skill Rate: 25.0x |
      Loot Rate: 8.0x
       
      Versão online para verificarem !
      Site: http://alfa-baiak.com/
      Ip: alfa-baiak.com
      Versão: 8.60
       
      Download: Download COMPLETO+SOURCE
      Scan: Virus total



       
      Créditos: Gabriel Correa
  • Estatísticas dos Fóruns

    96844
    Tópicos
    519597
    Posts
×
×
  • Criar Novo...

Informação Importante

Confirmação de Termo