• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Action [1.3] Bomberman 💣💥

Snavy

Bakasta
Senator
Joined
Apr 1, 2012
Messages
1,249
Solutions
71
Reaction score
621
Location
Hell
How to play
- to place bombs use !bomb command



Setup
  • Create 15x11 area
  • Configure from-to positions where the players will stand before entering the game.
Lua:
--| Where players will stand before starting the game.
ENTER_TILES = {
    FROM = Position(52, 424, 7),
    TO   = Position(55, 424, 7)
},
  • Configure corner positions.
  • - 1st spot ( top left )
  • - 2nd spot ( bottom right )
  • - 3rd spot ( top right )
  • - 4th spot ( bottom left )
Lua:
START_POSITIONS = {
    [1] = Position(47, 412, 7), -- top left     | 1 |   |   |   | 3 |
    [2] = Position(61, 422, 7), -- bottom right |   |   |   |   |   |
    [3] = Position(61, 412, 7), -- top right    |   |   |   |   |   |
    [4] = Position(47, 422, 7)  -- bottom left  | 4 |   |   |   | 2 |
},
- Configure and set lever action id
Lua:
LEVER_ENTER_AID     = 13442

Skärmavbild 2021-04-14 kl. 13.14.37.png


Preview
Skärmavbild 2021-04-14 kl. 13.32.18.png
prevbomberman.gif

Code (UPDATED 14 Apr 2021 - 15:56 - GMT+2)
data/scripts/bomberman.lua
Lua:
--[[
    https://otland.net/members/snavy.155163/
    ####################################################################
    #   ____   ____  __  __ ____  ______ _____  __  __          _   _  #
    #  |  _ \ / __ \|  \/  |  _ \|  ____|  __ \|  \/  |   /\   | \ | | #
    #  | |_) | |  | | \  / | |_) | |__  | |__) | \  / |  /  \  |  \| | #
    #  |  _ <| |  | | |\/| |  _ <|  __| |  _  /| |\/| | / /\ \ | . ` | #
    #  | |_) | |__| | |  | | |_) | |____| | \ \| |  | |/ ____ \| |\  | #
    #  |____/ \____/|_|  |_|____/|______|_|  \_\_|  |_/_/    \_\_| \_| #
    ####################################################################
]]

BOMBERMAN_PLAYERS = {}
local config = {
    --| Where players will stand before starting the game.
    enterTiles = {
        from = Position(52, 424, 7),
        to   = Position(55, 424, 7)
    },
    ----------------------------------
    startPositions = {
        [1] = Position(47, 412, 7), -- top left     | 1 |   |   |   | 3 |
        [2] = Position(61, 422, 7), -- bottom right |   |   |   |   |   |
        [3] = Position(61, 412, 7), -- top right    |   |   |   |   |   |
        [4] = Position(47, 422, 7)  -- bottom left  | 4 |   |   |   | 2 |
    },
    ----------------------------------
    minPlayerLimit      = 1,
    maxPlayerLimit      = 4,
    ----------------------------------
    leverEnterAid       = 13442,
    barrelId            = 9468,
    ----------------------------------
    exhaustionDelay     = 1,     -- seconds
    playerMaxBombs      = 3,
    bombsAtStart        = 1,
    bombMaxRadius       = 5,
    bombRadius          = 1,
    bombDelay           = 3,     -- seconds
    bombAid             = 13443,
    ----------------------------------
    bonusBombChance     = 25,    -- %
    bonusRadiusChance   = 25,
    bonusBombAid        = 13444,
    bonusRadiusAid      = 13445,
    bonusPointAid       = 13447,
    ----------------------------------
    storagePoints       = 13448,
    storageInGame       = 13449,
    storageBombRadius   = 13450,
    storagePlayerBombs  = 13451,
    storageTrackBombs   = 13452,
    storageExhaustion   = 13453,
    ----------------------------------
    countDownEffect    = CONST_ME_ENERGYAREA,
    explodeEffect      = CONST_ME_FIREAREA,
}

-----------------------[ LOCAL FUNCTIONS ]----------------------
local function positionToReadable(p)
    return "( ".. p.x .." / ".. p.y .." / ".. p.z .." )"
end

local function validateEnterTiles()
    -- if both x & y are inequal, FROM & TO does not make a straight line.
    if config.enterTiles.from.x ~= config.enterTiles.to.X
    and config.enterTiles.from.y ~= config.enterTiles.to.y then
        return false
    end
    return true
end

local function getPlayersWaiting()
    local players = {}
    local z = config.enterTiles.from.z
    for x = config.enterTiles.from.x, config.enterTiles.to.x do
        for y = config.enterTiles.from.y, config.enterTiles.to.y do
            local tile = Tile(x, y, z)
            if not tile then
                print('[Error - Bomberman::countPlayersToEnter] Tile not found ('.. positionToReadable(Position(x, y, z)) ..')')
                return false
            end
            local creature = tile:getTopCreature()
            if creature and creature:isPlayer() then
                table.insert(players, creature)
            end
        end
    end
    return players
end
-------------------------[ Action ]-----------------------------
local bombEnter = Action('bombermanEnter')
function bombEnter.onUse(player, item, position, fromPosition)
    if not validateEnterTiles() then
        player:say('! ERROR !', TALKTYPE_MONSTER_SAY)
        print('[Error - Bomberman::Action::onUse] Tile positions are incorrect.')
        return true
    end

    local playersWaiting = getPlayersWaiting()
    if not playersWaiting then
        player:say('! ERROR !', TALKTYPE_MONSTER_SAY, false, nil, item:getPosition())
        return true
    end

    if #playersWaiting < config.minPlayerLimit then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, 'Atleast ' .. config.minPlayerLimit .. ' players need to join.')
        return true
    end

    BOMBERMAN_PLAYERS = {}
    -- Telepot players to the game
    for i, position in ipairs(config.startPositions) do
        local p = playersWaiting[i]
        if p then
            p:teleportTo(position)
            p:setStorageValue(config.storageInGame, 1)
            p:setStorageValue(config.storageBombRadius, config.bombRadius)
            p:setStorageValue(config.storagePlayerBombs, config.bombsAtStart)
            p:setStorageValue(config.storageTrackBombs, config.bombsAtStart)
            BOMBERMAN_PLAYERS[p:getId()] = p
        end
    end
    return true
end
bombEnter:aid(config.leverEnterAid)
bombEnter:register()

-------------------------[ TalkAction ]-----------------------------
local function resetArena()
    BOMBERMAN_PLAYERS = {}
    local ignorePositions = {
        config.startPositions[1],
        Position(config.startPositions[1].x + 1, config.startPositions[1].y,     config.startPositions[1].z),
        Position(config.startPositions[1].x,     config.startPositions[1].y + 1, config.startPositions[1].z),
        config.startPositions[2],
        Position(config.startPositions[2].x - 1, config.startPositions[2].y,     config.startPositions[2].z),
        Position(config.startPositions[2].x,     config.startPositions[2].y - 1, config.startPositions[2].z),
        config.startPositions[3],
        Position(config.startPositions[3].x - 1, config.startPositions[3].y,     config.startPositions[3].z),
        Position(config.startPositions[3].x,     config.startPositions[3].y + 1, config.startPositions[3].z),
        config.startPositions[4],
        Position(config.startPositions[4].x + 1, config.startPositions[4].y,     config.startPositions[4].z),
        Position(config.startPositions[4].x,     config.startPositions[4].y - 1, config.startPositions[4].z),
    }

    local z = config.startPositions[1].z
    for x = config.startPositions[1].x, config.startPositions[2].x do
        for y = config.startPositions[1].y, config.startPositions[2].y do
            local position = Position(x, y, z)
            local tile = Tile(position)
            if tile then
                tile:getGround():setActionId(0)
                if tile:isWalkable() and not table.contains(ignorePositions, position) then
                    if not Game.createItem(config.barrelId, 1, position) then
                        print('could not create at ' .. positionToReadable(position))
                    end
                end
            end
        end
    end
end

local function checkWinner()
    local count = 0
    local lastPlayer = nil
    for cid, player in pairs(BOMBERMAN_PLAYERS) do
        if player then
            count = count + 1
            lastPlayer = player
        end
    end

    if not lastPlayer then
        addEvent(resetArena, 200)
        return
    end

    if lastPlayer and count == 1 then
        lastPlayer:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'You have won!')
        lastPlayer:teleportTo(lastPlayer:getTown():getTemplePosition())
        addEvent(resetArena, 200)
    end
end

local function showBonusBomb(position)
    local tile = Tile(position)
    if not tile then return end
    local ground = tile:getGround()
    if ground:getActionId() ~= config.bonusBombAid then return end
    position:sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    addEvent(showBonusBomb, 500, position)
end

local function showBonusRadius(position)
    local tile = Tile(position)
    if not tile then return end
    local ground = tile:getGround()
    if ground:getActionId() ~= config.bonusRadiusAid then return end
    position:sendMagicEffect(CONST_ME_FIREWORK_BLUE)
    addEvent(showBonusRadius, 500, position)
end

local function checkTiles(bombRadius, killer, position)
    local directions = {'WEST','EAST','NORTH','SOUTH'}
    for _, direction in pairs(directions) do
        for i = 0, bombRadius do
            local f = (function()
            --------------------------------------------------------
            local tile
            if direction == 'WEST'  then tile = Tile(position.x - i, position.y,     position.z) end
            if direction == 'EAST'  then tile = Tile(position.x + i, position.y,     position.z) end
            if direction == 'NORTH' then tile = Tile(position.x,     position.y - i, position.z) end
            if direction == 'SOUTH' then tile = Tile(position.x,     position.y + i, position.z) end
            if not tile then
                return false
            end

            local barrel = tile:getItemById(config.barrelId)
            if tile:getItemById(config.barrelId) then
                local tilePos = tile:getPosition()
                tilePos:sendMagicEffect(CONST_ME_GROUNDSHAKER)
                if math.random(1, 100) <= config.bonusBombChance then
                    tile:getGround():setActionId(config.bonusBombAid)
                    showBonusBomb(tilePos)
                elseif math.random(1, 100) <= config.bonusRadiusChance then
                    tile:getGround():setActionId(config.bonusRadiusAid)
                    showBonusRadius(tilePos)
                end
                barrel:remove()
                return false
            end

            if not tile:isWalkable() then
                return false
            end

            local topCreature = tile:getTopCreature()
            if topCreature and topCreature:isPlayer() then
                killer = Player(killer)
                local cname = topCreature:getName()
                local deadMsg = cname .. ' was killed by '
                -- killer might have died earlier & logged out.
                if not killer then
                    deadMsg = deadMsg .. 'a bomb.'
                else
                    if cname == killer:getName() then
                        deadMsg = deadMsg .. (topCreature:getSex() == PLAYERSEX_FEMALE and 'her' or 'him') .. 'self'
                    else
                        deadMsg = deadMsg .. killer:getName()
                    end
                    deadMsg = deadMsg .. ' in bomberman.'
                end

                topCreature:teleportTo(topCreature:getTown():getTemplePosition())
                topCreature:setStorageValue(config.storageInGame, -1)
                for _, player in pairs(BOMBERMAN_PLAYERS) do
                    if player then
                        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, deadMsg)
                    end
                end

                BOMBERMAN_PLAYERS[topCreature:getId()] = nil
                checkWinner()
            end

            tile:getPosition():sendMagicEffect(config.explodeEffect)
            return true
            --------------------------------------------------------
            end)()
            if not f then
                break
            end
        end
    end
end

local function detonateBomb(position, n)
    if n <= 0 then
        position:sendMagicEffect(config.explodeEffect)
        local bombTile   = Tile(position)
        local ground     = bombTile:getGround()
        local bombRadius = ground:getCustomAttribute('bombRadius')
        local killer     = ground:getCustomAttribute('killerId')
        local p = Player(killer)
        if p then
            p:setStorageValue(config.storagePlayerBombs, p:getStorageValue(config.storagePlayerBombs) + 1)
        end
        checkTiles(bombRadius, killer, position)
        ground:setActionId(0)
        return
    end
    position:sendMagicEffect(config.countDownEffect)
    addEvent(detonateBomb, 1000, position, n-1)
end

local ta = TalkAction('!bomb')
function ta.onSay(player, words, param)
    local taExhaustion = player:getStorageValue(config.storageExhaustion)
    if taExhaustion > os.time() then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'Slow down.')
        return false
    end

    if player:getStorageValue(config.storageInGame) <= 0 then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You may not place bombs outside of bomberman game.')
        return false
    end

    local position = player:getPosition()
    local tile = Tile(position)
    if not tile then -- probably not necessary but JUSTINCASE
        print('[Error - bomberman::TalkAction] Tile not found ' .. positionToReadable(position))
        player:sendTextMessage(MESSAGE_STATUS_SMALL, '!Error!')
        player:teleportTo(player:getTown():getTemplePosition())
        return false
    end

    local ground = tile:getGround()
    if ground:getActionId() == config.bombAid then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You cannot place two bombs on the same tile.')
        return false
    end

    local playerBombsLeft = player:getStorageValue(config.storagePlayerBombs)
    if playerBombsLeft <= 0 then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You have no bombs left!')
        return false
    end

    local bombRadius = config.bombRadius
    local playerBombRadius = player:getStorageValue(config.storageBombRadius)
    if playerBombRadius > 0 then
        bombRadius = playerBombRadius
    end

    player:setStorageValue(config.storageExhaustion, os.time() + config.exhaustionDelay)
    player:setStorageValue(config.storagePlayerBombs, playerBombsLeft - 1)
    ground:setActionId(config.bombAid)
    ground:setCustomAttribute('bombRadius', bombRadius)
    ground:setCustomAttribute('killerId', player:getId())
    detonateBomb(position, config.bombDelay)
    return false
end
ta:separator(' ')
ta:register()
------------------------[ MoveEvent ]------------------------
local me = MoveEvent('bombermanBonusRadius')
function me.onStepIn(player, item, position, fromPosition)
    if item:getActionId() == config.bonusRadiusAid then
        local playerBombRadius = player:getStorageValue(config.storageBombRadius)
        if playerBombRadius < config.bombMaxRadius then
            player:setStorageValue(config.storageBombRadius, playerBombRadius + 1)
        end
        position:sendMagicEffect(CONST_ME_YELLOWENERGY)
    else
        local playerBombs = player:getStorageValue(config.storageTrackBombs)
        if playerBombs < config.playerMaxBombs then
            player:setStorageValue(config.storagePlayerBombs, playerBombs + 1)
            player:setStorageValue(config.storageTrackBombs, playerBombs + 1)
        end
        position:sendMagicEffect(CONST_ME_PURPLEENERGY)
    end
    item:setActionId(0)
    return true
end

me:aid(config.bonusRadiusAid, config.bonusBombAid)
me:register()



Support
If you have problems with the code, create a new thread on the support board.
 
Last edited:
love it!

Would be fun to see this in a regular "super serious" server and have tournaments and u can win mounts/outfits and even maybe low level good items
 
How to play
- to place bombs use !bomb command



Setup
  • Create 15x11 area
  • Configure from-to positions where the players will stand before entering the game.
Lua:
--| Where players will stand before starting the game.
ENTER_TILES = {
    FROM = Position(52, 424, 7),
    TO   = Position(55, 424, 7)
},
  • Configure corner positions.
  • - 1st spot ( top left )
  • - 2nd spot ( bottom right )
  • - 3rd spot ( top right )
  • - 4th spot ( bottom left )
Lua:
START_POSITIONS = {
    [1] = Position(47, 412, 7), -- top left     | 1 |   |   |   | 3 |
    [2] = Position(61, 422, 7), -- bottom right |   |   |   |   |   |
    [3] = Position(61, 412, 7), -- top right    |   |   |   |   |   |
    [4] = Position(47, 422, 7)  -- bottom left  | 4 |   |   |   | 2 |
},
- Configure and set lever action id
Lua:
LEVER_ENTER_AID     = 13442



Preview
View attachment 57689
View attachment 57691

Code (UPDATED 14 Apr 2021 - 15:56 - GMT+2)
data/scripts/bomberman.lua
Lua:
--[[
    https://otland.net/members/snavy.155163/
    ####################################################################
    #   ____   ____  __  __ ____  ______ _____  __  __          _   _  #
    #  |  _ \ / __ \|  \/  |  _ \|  ____|  __ \|  \/  |   /\   | \ | | #
    #  | |_) | |  | | \  / | |_) | |__  | |__) | \  / |  /  \  |  \| | #
    #  |  _ <| |  | | |\/| |  _ <|  __| |  _  /| |\/| | / /\ \ | . ` | #
    #  | |_) | |__| | |  | | |_) | |____| | \ \| |  | |/ ____ \| |\  | #
    #  |____/ \____/|_|  |_|____/|______|_|  \_\_|  |_/_/    \_\_| \_| #
    ####################################################################
]]

BOMBERMAN_PLAYERS = {}
local config = {
    --| Where players will stand before starting the game.
    enterTiles = {
        from = Position(52, 424, 7),
        to   = Position(55, 424, 7)
    },
    ----------------------------------
    startPositions = {
        [1] = Position(47, 412, 7), -- top left     | 1 |   |   |   | 3 |
        [2] = Position(61, 422, 7), -- bottom right |   |   |   |   |   |
        [3] = Position(61, 412, 7), -- top right    |   |   |   |   |   |
        [4] = Position(47, 422, 7)  -- bottom left  | 4 |   |   |   | 2 |
    },
    ----------------------------------
    minPlayerLimit      = 1,
    maxPlayerLimit      = 4,
    ----------------------------------
    leverEnterAid       = 13442,
    barrelId            = 9468,
    ----------------------------------
    exhaustionDelay     = 1,     -- seconds
    playerMaxBombs      = 3,
    bombsAtStart        = 1,
    bombMaxRadius       = 5,
    bombRadius          = 1,
    bombDelay           = 3,     -- seconds
    bombAid             = 13443,
    ----------------------------------
    bonusBombChance     = 25,    -- %
    bonusRadiusChance   = 25,
    bonusBombAid        = 13444,
    bonusRadiusAid      = 13445,
    bonusPointAid       = 13447,
    ----------------------------------
    storagePoints       = 13448,
    storageInGame       = 13449,
    storageBombRadius   = 13450,
    storagePlayerBombs  = 13451,
    storageTrackBombs   = 13452,
    storageExhaustion   = 13453,
    ----------------------------------
    countDownEffect    = CONST_ME_ENERGYAREA,
    explodeEffect      = CONST_ME_FIREAREA,
}

-----------------------[ LOCAL FUNCTIONS ]----------------------
local function positionToReadable(p)
    return "( ".. p.x .." / ".. p.y .." / ".. p.z .." )"
end

local function validateEnterTiles()
    -- if both x & y are inequal, FROM & TO does not make a straight line.
    if config.enterTiles.from.x ~= config.enterTiles.to.X
    and config.enterTiles.from.y ~= config.enterTiles.to.y then
        return false
    end
    return true
end

local function getPlayersWaiting()
    local players = {}
    local z = config.enterTiles.from.z
    for x = config.enterTiles.from.x, config.enterTiles.to.x do
        for y = config.enterTiles.from.y, config.enterTiles.to.y do
            local tile = Tile(x, y, z)
            if not tile then
                print('[Error - Bomberman::countPlayersToEnter] Tile not found ('.. positionToReadable(Position(x, y, z)) ..')')
                return false
            end
            local creature = tile:getTopCreature()
            if creature and creature:isPlayer() then
                table.insert(players, creature)
            end
        end
    end
    return players
end
-------------------------[ Action ]-----------------------------
local bombEnter = Action('bombermanEnter')
function bombEnter.onUse(player, item, position, fromPosition)
    if not validateEnterTiles() then
        player:say('! ERROR !', TALKTYPE_MONSTER_SAY)
        print('[Error - Bomberman::Action::onUse] Tile positions are incorrect.')
        return true
    end

    local playersWaiting = getPlayersWaiting()
    if not playersWaiting then
        player:say('! ERROR !', TALKTYPE_MONSTER_SAY, false, nil, item:getPosition())
        return true
    end

    if #playersWaiting < config.minPlayerLimit then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, 'Atleast ' .. config.minPlayerLimit .. ' players need to join.')
        return true
    end

    BOMBERMAN_PLAYERS = {}
    -- Telepot players to the game
    for i, position in ipairs(config.startPositions) do
        local p = playersWaiting[i]
        if p then
            p:teleportTo(position)
            p:setStorageValue(config.storageInGame, 1)
            p:setStorageValue(config.storageBombRadius, config.bombRadius)
            p:setStorageValue(config.storagePlayerBombs, config.bombsAtStart)
            p:setStorageValue(config.storageTrackBombs, config.bombsAtStart)
            BOMBERMAN_PLAYERS[p:getId()] = p
        end
    end
    return true
end
bombEnter:aid(config.leverEnterAid)
bombEnter:register()

-------------------------[ TalkAction ]-----------------------------
local function resetArena()
    BOMBERMAN_PLAYERS = {}
    local ignorePositions = {
        config.startPositions[1],
        Position(config.startPositions[1].x + 1, config.startPositions[1].y,     config.startPositions[1].z),
        Position(config.startPositions[1].x,     config.startPositions[1].y + 1, config.startPositions[1].z),
        config.startPositions[2],
        Position(config.startPositions[2].x - 1, config.startPositions[2].y,     config.startPositions[2].z),
        Position(config.startPositions[2].x,     config.startPositions[2].y - 1, config.startPositions[2].z),
        config.startPositions[3],
        Position(config.startPositions[3].x - 1, config.startPositions[3].y,     config.startPositions[3].z),
        Position(config.startPositions[3].x,     config.startPositions[3].y + 1, config.startPositions[3].z),
        config.startPositions[4],
        Position(config.startPositions[4].x + 1, config.startPositions[4].y,     config.startPositions[4].z),
        Position(config.startPositions[4].x,     config.startPositions[4].y - 1, config.startPositions[4].z),
    }

    local z = config.startPositions[1].z
    for x = config.startPositions[1].x, config.startPositions[2].x do
        for y = config.startPositions[1].y, config.startPositions[2].y do
            local position = Position(x, y, z)
            local tile = Tile(position)
            if tile then
                tile:getGround():setActionId(0)
                if tile:isWalkable() and not table.contains(ignorePositions, position) then
                    if not Game.createItem(config.barrelId, 1, position) then
                        print('could not create at ' .. positionToReadable(position))
                    end
                end
            end
        end
    end
end

local function checkWinner()
    local count = 0
    local lastPlayer = nil
    for cid, player in pairs(BOMBERMAN_PLAYERS) do
        if player then
            count = count + 1
            lastPlayer = player
        end
    end

    if not lastPlayer then
        addEvent(resetArena, 200)
        return
    end

    if lastPlayer and count == 1 then
        lastPlayer:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'You have won!')
        lastPlayer:teleportTo(lastPlayer:getTown():getTemplePosition())
        addEvent(resetArena, 200)
    end
end

local function showBonusBomb(position)
    local tile = Tile(position)
    if not tile then return end
    local ground = tile:getGround()
    if ground:getActionId() ~= config.bonusBombAid then return end
    position:sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    addEvent(showBonusBomb, 500, position)
end

local function showBonusRadius(position)
    local tile = Tile(position)
    if not tile then return end
    local ground = tile:getGround()
    if ground:getActionId() ~= config.bonusRadiusAid then return end
    position:sendMagicEffect(CONST_ME_FIREWORK_BLUE)
    addEvent(showBonusRadius, 500, position)
end

local function checkTiles(bombRadius, killer, position)
    local directions = {'WEST','EAST','NORTH','SOUTH'}
    for _, direction in pairs(directions) do
        for i = 0, bombRadius do
            local f = (function()
            --------------------------------------------------------
            local tile
            if direction == 'WEST'  then tile = Tile(position.x - i, position.y,     position.z) end
            if direction == 'EAST'  then tile = Tile(position.x + i, position.y,     position.z) end
            if direction == 'NORTH' then tile = Tile(position.x,     position.y - i, position.z) end
            if direction == 'SOUTH' then tile = Tile(position.x,     position.y + i, position.z) end
            if not tile then
                return false
            end

            local barrel = tile:getItemById(config.barrelId)
            if tile:getItemById(config.barrelId) then
                local tilePos = tile:getPosition()
                tilePos:sendMagicEffect(CONST_ME_GROUNDSHAKER)
                if math.random(1, 100) <= config.bonusBombChance then
                    tile:getGround():setActionId(config.bonusBombAid)
                    showBonusBomb(tilePos)
                elseif math.random(1, 100) <= config.bonusRadiusChance then
                    tile:getGround():setActionId(config.bonusRadiusAid)
                    showBonusRadius(tilePos)
                end
                barrel:remove()
                return false
            end

            if not tile:isWalkable() then
                return false
            end

            local topCreature = tile:getTopCreature()
            if topCreature and topCreature:isPlayer() then
                killer = Player(killer)
                local cname = topCreature:getName()
                local deadMsg = cname .. ' was killed by '
                -- killer might have died earlier & logged out.
                if not killer then
                    deadMsg = deadMsg .. 'a bomb.'
                else
                    if cname == killer:getName() then
                        deadMsg = deadMsg .. (topCreature:getSex() == PLAYERSEX_FEMALE and 'her' or 'him') .. 'self'
                    else
                        deadMsg = deadMsg .. killer:getName()
                    end
                    deadMsg = deadMsg .. ' in bomberman.'
                end

                topCreature:teleportTo(topCreature:getTown():getTemplePosition())
                topCreature:setStorageValue(config.storageInGame, -1)
                for _, player in pairs(BOMBERMAN_PLAYERS) do
                    if player then
                        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, deadMsg)
                    end
                end

                BOMBERMAN_PLAYERS[topCreature:getId()] = nil
                checkWinner()
            end

            tile:getPosition():sendMagicEffect(config.explodeEffect)
            return true
            --------------------------------------------------------
            end)()
            if not f then
                break
            end
        end
    end
end

local function detonateBomb(position, n)
    if n <= 0 then
        position:sendMagicEffect(config.explodeEffect)
        local bombTile   = Tile(position)
        local ground     = bombTile:getGround()
        local bombRadius = ground:getCustomAttribute('bombRadius')
        local killer     = ground:getCustomAttribute('killerId')
        local p = Player(killer)
        if p then
            p:setStorageValue(config.storagePlayerBombs, p:getStorageValue(config.storagePlayerBombs) + 1)
        end
        checkTiles(bombRadius, killer, position)
        ground:setActionId(0)
        return
    end
    position:sendMagicEffect(config.countDownEffect)
    addEvent(detonateBomb, 1000, position, n-1)
end

local ta = TalkAction('!bomb')
function ta.onSay(player, words, param)
    local taExhaustion = player:getStorageValue(config.storageExhaustion)
    if taExhaustion > os.time() then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'Slow down.')
        return false
    end

    if player:getStorageValue(config.storageInGame) <= 0 then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You may not place bombs outside of bomberman game.')
        return false
    end

    local position = player:getPosition()
    local tile = Tile(position)
    if not tile then -- probably not necessary but JUSTINCASE
        print('[Error - bomberman::TalkAction] Tile not found ' .. positionToReadable(position))
        player:sendTextMessage(MESSAGE_STATUS_SMALL, '!Error!')
        player:teleportTo(player:getTown():getTemplePosition())
        return false
    end

    local ground = tile:getGround()
    if ground:getActionId() == config.bombAid then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You cannot place two bombs on the same tile.')
        return false
    end

    local playerBombsLeft = player:getStorageValue(config.storagePlayerBombs)
    if playerBombsLeft <= 0 then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You have no bombs left!')
        return false
    end

    local bombRadius = config.bombRadius
    local playerBombRadius = player:getStorageValue(config.storageBombRadius)
    if playerBombRadius > 0 then
        bombRadius = playerBombRadius
    end

    player:setStorageValue(config.storageExhaustion, os.time() + config.exhaustionDelay)
    player:setStorageValue(config.storagePlayerBombs, playerBombsLeft - 1)
    ground:setActionId(config.bombAid)
    ground:setCustomAttribute('bombRadius', bombRadius)
    ground:setCustomAttribute('killerId', player:getId())
    detonateBomb(position, config.bombDelay)
    return false
end
ta:separator(' ')
ta:register()
------------------------[ MoveEvent ]------------------------
local me = MoveEvent('bombermanBonusRadius')
function me.onStepIn(player, item, position, fromPosition)
    if item:getActionId() == config.bonusRadiusAid then
        local playerBombRadius = player:getStorageValue(config.storageBombRadius)
        if playerBombRadius < config.bombMaxRadius then
            player:setStorageValue(config.storageBombRadius, playerBombRadius + 1)
        end
        position:sendMagicEffect(CONST_ME_YELLOWENERGY)
    else
        local playerBombs = player:getStorageValue(config.storageTrackBombs)
        if playerBombs < config.playerMaxBombs then
            player:setStorageValue(config.storagePlayerBombs, playerBombs + 1)
            player:setStorageValue(config.storageTrackBombs, playerBombs + 1)
        end
        position:sendMagicEffect(CONST_ME_PURPLEENERGY)
    end
    item:setActionId(0)
    return true
end

me:aid(config.bonusRadiusAid, config.bonusBombAid)
me:register()



Support
If you have problems with the code, create a new thread on the support board.
bro, is it possible to place reward? What has each player take away 10 crystal coins and the winner takes it all?
 
bro, is it possible to place reward? What has each player take away 10 crystal coins and the winner takes it all?
Try this, reward only for the winner.

1. Add to config:

Code:
rewardBagId = 1992,
    rewards = {
        {2160, 1}, -- Crystal Coin
        {2159, 2}, -- Scarab Coin
        {15515, 5}  -- Golf of Bar
    },

2. Replace function checkWinner():

Lua:
local function checkWinner()

    local count = 0
    local lastPlayer = nil
    for cid, player in pairs(BOMBERMAN_PLAYERS) do
        if player then
            count = count + 1
           lastPlayer = player
        end
    end
    if not lastPlayer then
        addEvent(resetArena, 200)
        return
    end
    if lastPlayer and count == 1 then     
    local depot = lastPlayer:getDepotChest(1, true)
    local bag   = Game.createItem(config.rewardBagId, 1)
    local itemId = nil
    local itemCount = nil
    for _, reward in pairs(config.rewards) do
        itemId = reward[1]
        itemCount = reward[2]
        bag:addItemEx(Game.createItem(itemId, itemCount), INDEX_WHEREEVER, FLAG_NOLIMIT)
    end
    depot:addItemEx(bag)
    lastPlayer:sendTextMessage(MESSAGE_INFO_DESCR, string.format("You reward: %s.\nCheck your inbox!", bag:getContentDescription()))     
        lastPlayer:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'You have won!')
        lastPlayer:teleportTo(lastPlayer:getTown():getTemplePosition())
        addEvent(resetArena, 200)
    end
end
Post automatically merged:

How to play
- to place bombs use !bomb command



Setup
  • Create 15x11 area
  • Configure from-to positions where the players will stand before entering the game.
Lua:
--| Where players will stand before starting the game.
ENTER_TILES = {
    FROM = Position(52, 424, 7),
    TO   = Position(55, 424, 7)
},
  • Configure corner positions.
  • - 1st spot ( top left )
  • - 2nd spot ( bottom right )
  • - 3rd spot ( top right )
  • - 4th spot ( bottom left )
Lua:
START_POSITIONS = {
    [1] = Position(47, 412, 7), -- top left     | 1 |   |   |   | 3 |
    [2] = Position(61, 422, 7), -- bottom right |   |   |   |   |   |
    [3] = Position(61, 412, 7), -- top right    |   |   |   |   |   |
    [4] = Position(47, 422, 7)  -- bottom left  | 4 |   |   |   | 2 |
},
- Configure and set lever action id
Lua:
LEVER_ENTER_AID     = 13442



Preview
View attachment 57689
View attachment 57691

Code (UPDATED 14 Apr 2021 - 15:56 - GMT+2)
data/scripts/bomberman.lua
Lua:
--[[
    https://otland.net/members/snavy.155163/
    ####################################################################
    #   ____   ____  __  __ ____  ______ _____  __  __          _   _  #
    #  |  _ \ / __ \|  \/  |  _ \|  ____|  __ \|  \/  |   /\   | \ | | #
    #  | |_) | |  | | \  / | |_) | |__  | |__) | \  / |  /  \  |  \| | #
    #  |  _ <| |  | | |\/| |  _ <|  __| |  _  /| |\/| | / /\ \ | . ` | #
    #  | |_) | |__| | |  | | |_) | |____| | \ \| |  | |/ ____ \| |\  | #
    #  |____/ \____/|_|  |_|____/|______|_|  \_\_|  |_/_/    \_\_| \_| #
    ####################################################################
]]

BOMBERMAN_PLAYERS = {}
local config = {
    --| Where players will stand before starting the game.
    enterTiles = {
        from = Position(52, 424, 7),
        to   = Position(55, 424, 7)
    },
    ----------------------------------
    startPositions = {
        [1] = Position(47, 412, 7), -- top left     | 1 |   |   |   | 3 |
        [2] = Position(61, 422, 7), -- bottom right |   |   |   |   |   |
        [3] = Position(61, 412, 7), -- top right    |   |   |   |   |   |
        [4] = Position(47, 422, 7)  -- bottom left  | 4 |   |   |   | 2 |
    },
    ----------------------------------
    minPlayerLimit      = 1,
    maxPlayerLimit      = 4,
    ----------------------------------
    leverEnterAid       = 13442,
    barrelId            = 9468,
    ----------------------------------
    exhaustionDelay     = 1,     -- seconds
    playerMaxBombs      = 3,
    bombsAtStart        = 1,
    bombMaxRadius       = 5,
    bombRadius          = 1,
    bombDelay           = 3,     -- seconds
    bombAid             = 13443,
    ----------------------------------
    bonusBombChance     = 25,    -- %
    bonusRadiusChance   = 25,
    bonusBombAid        = 13444,
    bonusRadiusAid      = 13445,
    bonusPointAid       = 13447,
    ----------------------------------
    storagePoints       = 13448,
    storageInGame       = 13449,
    storageBombRadius   = 13450,
    storagePlayerBombs  = 13451,
    storageTrackBombs   = 13452,
    storageExhaustion   = 13453,
    ----------------------------------
    countDownEffect    = CONST_ME_ENERGYAREA,
    explodeEffect      = CONST_ME_FIREAREA,
}

-----------------------[ LOCAL FUNCTIONS ]----------------------
local function positionToReadable(p)
    return "( ".. p.x .." / ".. p.y .." / ".. p.z .." )"
end

local function validateEnterTiles()
    -- if both x & y are inequal, FROM & TO does not make a straight line.
    if config.enterTiles.from.x ~= config.enterTiles.to.X
    and config.enterTiles.from.y ~= config.enterTiles.to.y then
        return false
    end
    return true
end

local function getPlayersWaiting()
    local players = {}
    local z = config.enterTiles.from.z
    for x = config.enterTiles.from.x, config.enterTiles.to.x do
        for y = config.enterTiles.from.y, config.enterTiles.to.y do
            local tile = Tile(x, y, z)
            if not tile then
                print('[Error - Bomberman::countPlayersToEnter] Tile not found ('.. positionToReadable(Position(x, y, z)) ..')')
                return false
            end
            local creature = tile:getTopCreature()
            if creature and creature:isPlayer() then
                table.insert(players, creature)
            end
        end
    end
    return players
end
-------------------------[ Action ]-----------------------------
local bombEnter = Action('bombermanEnter')
function bombEnter.onUse(player, item, position, fromPosition)
    if not validateEnterTiles() then
        player:say('! ERROR !', TALKTYPE_MONSTER_SAY)
        print('[Error - Bomberman::Action::onUse] Tile positions are incorrect.')
        return true
    end

    local playersWaiting = getPlayersWaiting()
    if not playersWaiting then
        player:say('! ERROR !', TALKTYPE_MONSTER_SAY, false, nil, item:getPosition())
        return true
    end

    if #playersWaiting < config.minPlayerLimit then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, 'Atleast ' .. config.minPlayerLimit .. ' players need to join.')
        return true
    end

    BOMBERMAN_PLAYERS = {}
    -- Telepot players to the game
    for i, position in ipairs(config.startPositions) do
        local p = playersWaiting[i]
        if p then
            p:teleportTo(position)
            p:setStorageValue(config.storageInGame, 1)
            p:setStorageValue(config.storageBombRadius, config.bombRadius)
            p:setStorageValue(config.storagePlayerBombs, config.bombsAtStart)
            p:setStorageValue(config.storageTrackBombs, config.bombsAtStart)
            BOMBERMAN_PLAYERS[p:getId()] = p
        end
    end
    return true
end
bombEnter:aid(config.leverEnterAid)
bombEnter:register()

-------------------------[ TalkAction ]-----------------------------
local function resetArena()
    BOMBERMAN_PLAYERS = {}
    local ignorePositions = {
        config.startPositions[1],
        Position(config.startPositions[1].x + 1, config.startPositions[1].y,     config.startPositions[1].z),
        Position(config.startPositions[1].x,     config.startPositions[1].y + 1, config.startPositions[1].z),
        config.startPositions[2],
        Position(config.startPositions[2].x - 1, config.startPositions[2].y,     config.startPositions[2].z),
        Position(config.startPositions[2].x,     config.startPositions[2].y - 1, config.startPositions[2].z),
        config.startPositions[3],
        Position(config.startPositions[3].x - 1, config.startPositions[3].y,     config.startPositions[3].z),
        Position(config.startPositions[3].x,     config.startPositions[3].y + 1, config.startPositions[3].z),
        config.startPositions[4],
        Position(config.startPositions[4].x + 1, config.startPositions[4].y,     config.startPositions[4].z),
        Position(config.startPositions[4].x,     config.startPositions[4].y - 1, config.startPositions[4].z),
    }

    local z = config.startPositions[1].z
    for x = config.startPositions[1].x, config.startPositions[2].x do
        for y = config.startPositions[1].y, config.startPositions[2].y do
            local position = Position(x, y, z)
            local tile = Tile(position)
            if tile then
                tile:getGround():setActionId(0)
                if tile:isWalkable() and not table.contains(ignorePositions, position) then
                    if not Game.createItem(config.barrelId, 1, position) then
                        print('could not create at ' .. positionToReadable(position))
                    end
                end
            end
        end
    end
end

local function checkWinner()
    local count = 0
    local lastPlayer = nil
    for cid, player in pairs(BOMBERMAN_PLAYERS) do
        if player then
            count = count + 1
            lastPlayer = player
        end
    end

    if not lastPlayer then
        addEvent(resetArena, 200)
        return
    end

    if lastPlayer and count == 1 then
        lastPlayer:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, 'You have won!')
        lastPlayer:teleportTo(lastPlayer:getTown():getTemplePosition())
        addEvent(resetArena, 200)
    end
end

local function showBonusBomb(position)
    local tile = Tile(position)
    if not tile then return end
    local ground = tile:getGround()
    if ground:getActionId() ~= config.bonusBombAid then return end
    position:sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
    addEvent(showBonusBomb, 500, position)
end

local function showBonusRadius(position)
    local tile = Tile(position)
    if not tile then return end
    local ground = tile:getGround()
    if ground:getActionId() ~= config.bonusRadiusAid then return end
    position:sendMagicEffect(CONST_ME_FIREWORK_BLUE)
    addEvent(showBonusRadius, 500, position)
end

local function checkTiles(bombRadius, killer, position)
    local directions = {'WEST','EAST','NORTH','SOUTH'}
    for _, direction in pairs(directions) do
        for i = 0, bombRadius do
            local f = (function()
            --------------------------------------------------------
            local tile
            if direction == 'WEST'  then tile = Tile(position.x - i, position.y,     position.z) end
            if direction == 'EAST'  then tile = Tile(position.x + i, position.y,     position.z) end
            if direction == 'NORTH' then tile = Tile(position.x,     position.y - i, position.z) end
            if direction == 'SOUTH' then tile = Tile(position.x,     position.y + i, position.z) end
            if not tile then
                return false
            end

            local barrel = tile:getItemById(config.barrelId)
            if tile:getItemById(config.barrelId) then
                local tilePos = tile:getPosition()
                tilePos:sendMagicEffect(CONST_ME_GROUNDSHAKER)
                if math.random(1, 100) <= config.bonusBombChance then
                    tile:getGround():setActionId(config.bonusBombAid)
                    showBonusBomb(tilePos)
                elseif math.random(1, 100) <= config.bonusRadiusChance then
                    tile:getGround():setActionId(config.bonusRadiusAid)
                    showBonusRadius(tilePos)
                end
                barrel:remove()
                return false
            end

            if not tile:isWalkable() then
                return false
            end

            local topCreature = tile:getTopCreature()
            if topCreature and topCreature:isPlayer() then
                killer = Player(killer)
                local cname = topCreature:getName()
                local deadMsg = cname .. ' was killed by '
                -- killer might have died earlier & logged out.
                if not killer then
                    deadMsg = deadMsg .. 'a bomb.'
                else
                    if cname == killer:getName() then
                        deadMsg = deadMsg .. (topCreature:getSex() == PLAYERSEX_FEMALE and 'her' or 'him') .. 'self'
                    else
                        deadMsg = deadMsg .. killer:getName()
                    end
                    deadMsg = deadMsg .. ' in bomberman.'
                end

                topCreature:teleportTo(topCreature:getTown():getTemplePosition())
                topCreature:setStorageValue(config.storageInGame, -1)
                for _, player in pairs(BOMBERMAN_PLAYERS) do
                    if player then
                        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, deadMsg)
                    end
                end

                BOMBERMAN_PLAYERS[topCreature:getId()] = nil
                checkWinner()
            end

            tile:getPosition():sendMagicEffect(config.explodeEffect)
            return true
            --------------------------------------------------------
            end)()
            if not f then
                break
            end
        end
    end
end

local function detonateBomb(position, n)
    if n <= 0 then
        position:sendMagicEffect(config.explodeEffect)
        local bombTile   = Tile(position)
        local ground     = bombTile:getGround()
        local bombRadius = ground:getCustomAttribute('bombRadius')
        local killer     = ground:getCustomAttribute('killerId')
        local p = Player(killer)
        if p then
            p:setStorageValue(config.storagePlayerBombs, p:getStorageValue(config.storagePlayerBombs) + 1)
        end
        checkTiles(bombRadius, killer, position)
        ground:setActionId(0)
        return
    end
    position:sendMagicEffect(config.countDownEffect)
    addEvent(detonateBomb, 1000, position, n-1)
end

local ta = TalkAction('!bomb')
function ta.onSay(player, words, param)
    local taExhaustion = player:getStorageValue(config.storageExhaustion)
    if taExhaustion > os.time() then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'Slow down.')
        return false
    end

    if player:getStorageValue(config.storageInGame) <= 0 then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You may not place bombs outside of bomberman game.')
        return false
    end

    local position = player:getPosition()
    local tile = Tile(position)
    if not tile then -- probably not necessary but JUSTINCASE
        print('[Error - bomberman::TalkAction] Tile not found ' .. positionToReadable(position))
        player:sendTextMessage(MESSAGE_STATUS_SMALL, '!Error!')
        player:teleportTo(player:getTown():getTemplePosition())
        return false
    end

    local ground = tile:getGround()
    if ground:getActionId() == config.bombAid then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You cannot place two bombs on the same tile.')
        return false
    end

    local playerBombsLeft = player:getStorageValue(config.storagePlayerBombs)
    if playerBombsLeft <= 0 then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You have no bombs left!')
        return false
    end

    local bombRadius = config.bombRadius
    local playerBombRadius = player:getStorageValue(config.storageBombRadius)
    if playerBombRadius > 0 then
        bombRadius = playerBombRadius
    end

    player:setStorageValue(config.storageExhaustion, os.time() + config.exhaustionDelay)
    player:setStorageValue(config.storagePlayerBombs, playerBombsLeft - 1)
    ground:setActionId(config.bombAid)
    ground:setCustomAttribute('bombRadius', bombRadius)
    ground:setCustomAttribute('killerId', player:getId())
    detonateBomb(position, config.bombDelay)
    return false
end
ta:separator(' ')
ta:register()
------------------------[ MoveEvent ]------------------------
local me = MoveEvent('bombermanBonusRadius')
function me.onStepIn(player, item, position, fromPosition)
    if item:getActionId() == config.bonusRadiusAid then
        local playerBombRadius = player:getStorageValue(config.storageBombRadius)
        if playerBombRadius < config.bombMaxRadius then
            player:setStorageValue(config.storageBombRadius, playerBombRadius + 1)
        end
        position:sendMagicEffect(CONST_ME_YELLOWENERGY)
    else
        local playerBombs = player:getStorageValue(config.storageTrackBombs)
        if playerBombs < config.playerMaxBombs then
            player:setStorageValue(config.storagePlayerBombs, playerBombs + 1)
            player:setStorageValue(config.storageTrackBombs, playerBombs + 1)
        end
        position:sendMagicEffect(CONST_ME_PURPLEENERGY)
    end
    item:setActionId(0)
    return true
end

me:aid(config.bonusRadiusAid, config.bonusBombAid)
me:register()



Support
If you have problems with the code, create a new thread on the support board.
Would it be interesting to add a date / time for the event to start automatically? it's a great event!
 
Last edited:
Someone can fix this script, if players are in event, then the next 4 people have to wait until the previous event ends???
 
Hello @Snavy, thanks for the contribution, great script!

I don't know if you like to ask or suggestions...

I would like to ask if the table that keeps the ids of the players that are in the event could not be local?

One suggestion, the creation of a "removePlayer" function for the question of when the player is withdrawn and when he wins, which in addition to sending to the temple, could withdraw all the storages that are added to it (and not just inGame, as in the script).

Thank you again.
 
Back
Top