• 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!

[TFS 1.3] Capture The Flag

Fablow

Intermediate OT User
Content Editor
Joined
May 7, 2008
Messages
1,565
Solutions
6
Reaction score
145
Location
Canada
GitHub
Fablow77
It's been awhile since I released something, so here it is. I upgraded someones else Capture The Flag event, from one of my old servers. If you reconize it, please be sure to add credits. This was tested on latest OTX based off TFS 1.3.

Let's install:

data/actions/actions.xml
Code:
<action fromaid="15150" toaid="15151" script="ctf.lua" />

data/actions/actions/scripts/ctf.lua
Code:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local flag = captureTheFlag.getFlag(item)
    if flag then
        local info = captureTheFlag.players[player:getName()]
        if info.team ~= flag.team then
            flag:remove()
            item:remove()
            flag:setHolder(player)
            info.flag = flag
 
            for name, _ in pairs(captureTheFlag.players) do
                local player = Player(name)
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, info.name .. ' has captured the flag for team ' .. (info.team == 1 and 'A' or 'B') .. '!')
            end
        end
    end
    return true
end

data/creaturescripts/creaturescripts.xml
Code:
    <event type="preparedeath" name="CTFDeath" script="ctf.lua" />
    <event type="healthchange" name="CTFHealthChange" script="ctf.lua" />

data/creaturescripts/scripts/ctf.lua
Code:
function onPrepareDeath(creature, killer)
    if captureTheFlag.getPlayerState(creature) == CTF_STATE_FIGHT then
        captureTheFlag.onDeath(creature)
        return false
    end
    return true
end

function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    local infoA = captureTheFlag.players[creature:getName()]
    local infoB = attacker and captureTheFlag.players[attacker:getName()]
    if infoA and infoB and infoA.team == infoB.team and primaryType ~= COMBAT_HEALING then
        return 0, 0, 0, 0
    end
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

data/globalevents/globalevents.xml
Code:
<globalevent name="Capture The Flag" time="10:00:00" script="ctf.lua" />

data/globalevents/scripts/ctf.lua
Code:
function onTime()
     captureTheFlag.close()
     captureTheFlag.start()
     addEvent(captureTheFlag.round, 10*60*1000)
     broadcastMessage("Capture the Flag event will start in 10 minutes, entrance in Event Room.", MESSAGE_STATUS_WARNING)
     addEvent(broadcastMessage, 5 * 60 * 1000, "Capture the Flag event will start in 5 minutes, entrance in Event Room.", MESSAGE_STATUS_WARNING)
     addEvent(broadcastMessage, 8 * 60 * 1000, "Capture the Flag event will start in 2 minutes, entrance in Event Room .", MESSAGE_STATUS_WARNING)
end

data/lib/libs.lua
Code:
dofile('data/lib/ctf.lua')

data/lib/ctf.lua - CONFIGURATION IS ALL HERE.

Code:
local function newOutfitCondition(outfit)
    local condition = Condition(CONDITION_OUTFIT)
    condition:setTicks(120 * 60 * 1000) -- estava: condition:setTicks(-1)
    condition:setOutfit(outfit)
    return condition
end
 
CTF_STATE_NONE  = 0
CTF_STATE_WAIT  = 1
CTF_STATE_FIGHT = 2
 
CTF_TEAM_A = 1
CTF_TEAM_B = 2
 
if not captureTheFlag then
    Flag = {
        -- (Modify) effect period (in ms)
        effectPeriod = 3000,
 
        new = function(self, obj)
            return setmetatable(obj, {__index = self})
        end
    }
 
    captureTheFlag = {
        -- Whether the event is happening right now
        open = false,
 
        -- (Modify) How many points to win
        goal = 10,
 
        -- (Modify) Rewards: {itemid, count}
        rewards = {
            {26337, 1},
        },
 
        -- [name] = {name = 'playerName', state = CTF_STATE_, team = CTF_TEAM_, flag = Frag|nil}
        players = { },
 
        -- (Modify) Waiting Room position
        waitingRoom = Position(1536, 896, 14),
        
        -- Death Room position
        deathRoom = Position(1459, 907, 14),
 
        -- (Modify) Exit position,
        exit = Position(1000, 1000, 7),
 
        teams = {
            [CTF_TEAM_A] = {
                -- (Modify) Team A Outfit
                outfit = newOutfitCondition {
                    lookType = 619,
                     lookHead = 114,
                     lookBody = 94,
                     lookLegs = 94,
                     lookFeet = 114
                },
                
                
                -- (Modify) Team A Outfit
                outfitFlag = newOutfitCondition {
                    lookType = 619,
                     lookHead = 132,
                     lookBody = 132,
                     lookLegs = 132,
                     lookFeet = 132
                },
 
                -- (Modify) Base position (entrance)
                base = Position(1412, 854, 14),
                -- (Modify) Flag, change position and actionid
                flag = Flag:new {position = Position(1424, 875, 14), id = 8617, actionid = 15150, team = CTF_TEAM_A},
                -- How many players are in this team
                players = 0,
                -- How many points the team has scored so far
                score = 0
            },
 
            [CTF_TEAM_B] = {
                -- (Modify) Team B Outfit
                outfit = newOutfitCondition {
                    lookType = 619,
                     lookHead = 114,
                     lookBody = 86,
                     lookLegs = 86,
                     lookFeet = 114
                },
 
                -- (Modify) Team B Outfit
                outfitFlag = newOutfitCondition {
                    lookType = 619,
                     lookHead = 126,
                     lookBody = 126,
                     lookLegs = 126,
                     lookFeet = 126
                },
                
                -- (Modify) Base position (entrance)
                base = Position(1484, 918, 14),
                -- (Modify) Flag, change position and actionid
                flag = Flag:new {position = Position(1470, 913, 14), id = 8622, actionid = 15151, team = CTF_TEAM_B},
                -- How many players are in this team
                players = 0,
                -- How many points the team has scored so far
                score = 0
            },
        }
    }
 
    function Flag:setHolder(player)
        self.holder = player and player:getName()
        if not self.event then
            self:doEffect()
        end
    end
 
    function Flag:remove()
        if not self.holder then
            local flag = Tile(self:getPosition()):getItemById(self.id)
            flag:remove()         
        end
 
        if self.event then
            stopEvent(self.event)
            self.event = nil
        end
 
        self.currentPosition = nil
        self.holder = nil
    end
 
    function Flag:getPosition()
        return self.currentPosition or self.position
    end
 
    function Flag:doEffect()
        local position = self:getPosition()
        local holder = Player(self.holder)
        if holder then
            holder:addCondition(captureTheFlag.teams[captureTheFlag.players[holder:getName()].team].outfitFlag)
            position = holder:getPosition()
        end
        position:sendMagicEffect(CONST_ME_TUTORIALARROW)
 
        self.event = addEvent(self.doEffect, self.effectPeriod, self)
    end
 
    function Flag:drop(position)
        local flag = Game.createItem(self.id, 1, position)
        flag:setActionId(self.actionid)
        self.currentPosition = position
        self.holder = nil
 
        if not self.event then
            self:doEffect()
        end
    end
 
    captureTheFlag.start = function()
        if captureTheFlag.open then
            return false
        end
 
        captureTheFlag.open = true
 
        for _, team in ipairs(captureTheFlag.teams) do
            team.score = 0
            team.flag:drop(team.flag.position)
        end
    end
 
    captureTheFlag.close = function()
        if not captureTheFlag.open then
            return true
        end   
 
        for _, info in pairs(captureTheFlag.players) do
            local player = Player(info.name)
            captureTheFlag.onLeave(player)
        end
 
        for _, team in ipairs(captureTheFlag.teams) do
            team.flag:remove()
            team.players = 0
            team.score = 0
        end
 
        captureTheFlag.open = false
    end
 
    captureTheFlag.round = function(team)
        if not captureTheFlag.open then
            return true
        end
 
        local winner
        for _, team in ipairs(captureTheFlag.teams) do
            team.flag:remove()
            team.flag:drop(team.flag.position)
            if team.score == captureTheFlag.goal then
                winner = _
            end
        end
 
        if not winner then
            local status = 'Team A: ' .. captureTheFlag.teams[CTF_TEAM_A].score .. ' x Team B: ' .. captureTheFlag.teams[CTF_TEAM_B].score
            if team then
                status = string.format('Team %s has returned the flag and won the round. %s', team == CTF_TEAM_A and 'A' or 'B', status)
            end
            for _, info in pairs(captureTheFlag.players) do
                local player = Player(info.name)
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, status)
                player:teleportTo(captureTheFlag.teams[info.team].base)
                player:addCondition(captureTheFlag.teams[info.team].outfit)
                info.state = CTF_STATE_FIGHT
            end
        else
            local status = 'Team ' .. (winner == CTF_TEAM_A and 'A' or 'B') .. ' has won!'
            for _, info in pairs(captureTheFlag.players) do
                local player = Player(info.name)
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, status)
                if info.team == winner then
                    for i, reward in ipairs(captureTheFlag.rewards) do
                        player:addItem(reward[1], reward[2])
                    end
                end
            end
 
            captureTheFlag.close()
        end
    end
 
    captureTheFlag.getPlayerState = function(player)
        local info = captureTheFlag.players[player:getName()]
        return info and info.state or CTF_STATE_NONE
    end
 
    captureTheFlag.setPlayerState = function(player, state)
        local info = captureTheFlag.players[player:getName()]
        if info then
            info.state = state
        end
    end
 
    captureTheFlag.onJoin = function(player)
        local info = {name = player:getName(), state = CTF_STATE_WAIT, flag = false}
        local playersA, playersB = captureTheFlag.teams[CTF_TEAM_A].players, captureTheFlag.teams[CTF_TEAM_B].players
        if playersA == playersB then
            info.team = math.random(CTF_TEAM_A, CTF_TEAM_B)
        elseif playersA > playersB then
            info.team = CTF_TEAM_B
        else
            info.team = CTF_TEAM_A
        end
 
        captureTheFlag.players[info.name] = info
        captureTheFlag.teams[info.team].players = captureTheFlag.teams[info.team].players + 1
 
        player:teleportTo(captureTheFlag.waitingRoom)
        player:addCondition(captureTheFlag.teams[info.team].outfit)
        player:registerEvent('CTFHealthChange')
        player:registerEvent('CTFDeath')
    end
 
    captureTheFlag.onDeath = function(player)
        local info = captureTheFlag.players[player:getName()]
        if info then
        
            -- info.state = CTF_STATE_WAIT   MUDEI AQUI, PROBLEM?
            info.state = CTF_STATE_FIGHT
 
            if info.flag then
                info.flag:drop(info.flag.position)
                info.flag = nil
            end
        end
        
        --player:teleportTo(captureTheFlag.waitingRoom)  MUDEI AQUI, PROBLEM?
        player:teleportTo(captureTheFlag.deathRoom)
        player:addCondition(captureTheFlag.teams[info.team].outfit)
        -- testar
        player:sendTextMessage(MESSAGE_INFO_DESCR, "You was killed.")
        --teste
        player:addHealth(player:getMaxHealth())
        player:addMana(player:getMaxMana())
 
        if player:getIp() == 0 then
            captureTheFlag.onLeave(player)
        end
    end
 
    captureTheFlag.onLeave = function(player)
        local info = captureTheFlag.players[player:getName()]
        if info then
            captureTheFlag.teams[info.team].players = captureTheFlag.teams[info.team].players - 1
            captureTheFlag.players[info.name] = nil
 
            if info.flag then
                info.flag:drop(info.flag.position)
                info.flag = nil
            end
        end
 
        player:teleportTo(captureTheFlag.exit)
        player:removeCondition(CONDITION_OUTFIT)
        player:unregisterEvent('CTFHealthChange')
        player:unregisterEvent('CTFDeath')
    end
 
    captureTheFlag.getFlag = function(item)
        for _, team in ipairs(captureTheFlag.teams) do
            if team.flag.id == item:getId() and team.flag.actionid == item:getActionId() then
                return team.flag
            end
        end
        return nil
    end
end

data/movements/movements.xml
Code:
<movevent event="StepIn" fromaid="15153" toaid="15155" script="ctf.lua" />

data/movements/ctf.lua
Code:
local CTF_JOIN = 15153
local CTF_LEAVE = 15154
local CTF_DROPFLAG = 15155

function onStepIn(creature, item, position, fromPosition)
    local actionid = item:getActionId()
    if not captureTheFlag.open then
        if position ~= fromPosition then
            creature:teleportTo(fromPosition)
        end
        creature:sendCancelMessage('This event is currently closed.')
        return true
    end
 
 if creature:getLevel() < 200 then
    local vaeas = {x = 1000, y = 1000, z = 7}  -- 32470, 32473, 6
    creature:teleportTo(vaeas)
    creature:sendTextMessage(MESSAGE_INFO_DESCR, "YOU NEED LEVEL 200+ TO ENTER.")
    return false
    end   
    
 -- CAPTURE START
if creature:isPlayer() then
if creature:getItemCount(2165) > 0 then
local vaeap = {x = 1000, y = 1000, z = 7}  -- 32470, 32473, 6
    creature:teleportTo(vaeap)
    creature:sendTextMessage(MESSAGE_INFO_DESCR, "You can't enter with stealth ring.")
        return false   
    end
    end
-- CAPTURE END
  -- AQUI
    if actionid == CTF_JOIN then
        captureTheFlag.onJoin(creature)
        broadcastMessage("Player "..creature:getName().." joined Capture The Flag event!", MESSAGE_STATUS_WARNING)
    elseif actionid == CTF_LEAVE then
        captureTheFlag.onLeave(creature)
        broadcastMessage("Player "..creature:getName().." left the event Capture The Flag!", MESSAGE_STATUS_WARNING)
    elseif actionid == CTF_DROPFLAG then
        local info = captureTheFlag.players[creature:getName()]
        local team = captureTheFlag.teams[info.team]
        if info.flag and position:getDistance(team.base) < position:getDistance(captureTheFlag.teams[info.team == 1 and 2 or 1].base) then         
            team.score = team.score + 1
            captureTheFlag.round(info.team)
            info.flag = nil
        end
    end
    return true
end

Now you have installed your files, you must set up your map first as such:
-Arena
-Waiting Room
-Death Room

*NOTE you must have a teleport out of the event in the Arena (on both sides) and waiting room.

On your map, you need the follow IDs:
ACTIONID - 15155 - is what you put on the tiles on each respawn of the teams. This is how you score points.
ACTIONID - 15154 - use this actionID on your teleports that teleport the player out of the arena.
ACTIONID - 15153 - use this actionID on your teleport TO the event.

Known Complications:
When the event is running, players can still enter the event tp. This only happens when the game is not maxed out players. They can only go to the waiting room and will be teleport out, or they can use the teleport in the waiting room to go back to the temple. This does NOT effect the event whatsoever. It works perfectly fine. When the event is not running, players can't enter.

You must provide your own map, sorry!
 
very good the script! Where do you establish the duration of the event?
Post automatically merged:

one more question, where do you set the point limit before closing the event?
 
Last edited:
in your lib.lua (data/lib/ctf.lua)

-- (Modify) How many points to win goal = 10,

You can edit how many goals a team must have win. This also counts as how many "rounds" per game. There is no time limit, a team must complete all rounds in order to receive the reward/complete the event.
 
Everything works except for dropping the flag :eek:

Lua:
                flag = Flag:new {position = Position(688, 1884, 7), id = 1437, actionid = 15151, team = CTF_TEAM_B},

KK0pVSV.png

dydATAC.png


Got some help to fix the issue.

The issue was in the Movement script
Change
Code:
if info.flag and position:getDistance(team.base) < position:getDistance(captureTheFlag.teams[info.team == 1 and 2 or 1].base) then
To
Code:
if info.flag and position:getDistance(team.base) <= position:getDistance(captureTheFlag.teams[info.team == 1 and 2 or 1].base) then
 
Last edited:
How can i fix this conflict ? Player cant die
1.png

firestorm.lua:

Lua:
dofile('data/firestorm_event.lua')
function onPrepareDeath(player, killer)
    local winner = 0
    if player:getStorageValue(fsJoinStorage) >= 1 then
       Game.setStorageValue(fsJoinedCountGlobalStorage, Game.getStorageValue(fsJoinedCountGlobalStorage)-1)
        player:teleportTo(player:getTown():getTemplePosition())
        player:setStorageValue(fsJoinStorage, 0)
        player:addHealth(player:getMaxHealth())

        if Game.getStorageValue(fsJoinedCountGlobalStorage) <= 1 then --Event ended, someone won!
         local pla, play = nil, Game.getPlayers()
         for i = 1, #play do
        pla = play[i]
              if pla:getStorageValue(fsJoinStorage) == 1 then
            winner = pla:getId()
            break
            end
            end
            winner = Player(winner)
             if winner then
                winner:teleportTo(winner:getTown():getTemplePosition())
                winner:addHealth(winner:getMaxHealth())
                local trophy = winner:addItem(fsTrophy, 1)
                if trophy then
                    trophy:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, '['.. winner:getName() ..'] has won the firestorm Event.')
                end
                 Game.broadcastMessage(string.format('%s is the winner of firestorm Event', winner:getName()), MESSAGE_STATUS_WARNING)
            end
            resetfsVariables()
        end
        return false
    end
    return true
end

ctf.lua:
Code:
function onPrepareDeath(creature, killer)
    if captureTheFlag.getPlayerState(creature) == CTF_STATE_FIGHT then
        captureTheFlag.onDeath(creature)
        return false
    end
    return true
end

function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    local infoA = captureTheFlag.players[creature:getName()]
    local infoB = attacker and captureTheFlag.players[attacker:getName()]
    if infoA and infoB and infoA.team == infoB.team and primaryType ~= COMBAT_HEALING then
        return 0, 0, 0, 0
    end
    return primaryDamage, primaryType, secondaryDamage, secondaryType
end

creaturescript.xml:
Code:
    <!-- firestorm -->
    <event type="prepareDeath" name="fire_event" script="firestorm.lua"/>
    <!-- Missions -->
    <event type="kill" name="Missions" script="killmissions.lua"/>
    <!-- CTF -->
      <event type="preparedeath" name="CTFDeath" script="ctf.lua" />
    <event type="healthchange" name="CTFHealthChange" script="ctf.lua" />

login.lua:
Code:
    player:registerEvent("fire_event")
    player:registerEvent('BountyHunterKill')
    player:registerEvent("AutoLoot")
    player:registerEvent("CTFDeath")
    player:registerEvent("CTFHealthChange")
 
It would be very interesting if there was a minimum number of players and if the time was exceeded and the players did not enter, the event would close. Forgive me if I skipped that part in the setup!
 
It would be very interesting if there was a minimum number of players and if the time was exceeded and the players did not enter, the event would close. Forgive me if I skipped that part in the setup!
There isn't at this time. You should have tps in your map and waiting room so players can exit.
 
I mean, the Event starts, example: 19:30, at 00:00 you can enter that is still running!
You can enter the tp room until the event ends. If the event hasn't been triggered, it will allow players to join until it starts.

When the event is running, players can still enter the event tp. This only happens when the game is not maxed out players. They can only go to the waiting room and will be teleport out, or they can use the teleport in the waiting room to go back to the temple. This does NOT effect the event whatsoever. It works perfectly fine. When the event is not running, players can't enter.
 
You can enter the tp room until the event ends. If the event hasn't been triggered, it will allow players to join until it starts.
I don't think I found that part in the settings, I couldn't make the teleport unavailable when the event is taking place
Post automatically merged:

The event has 10 minutes to start, After 10 minutes it remains open
 
The event has 10 minutes to start, After 10 minutes it remains open

It will remain open until the event is over. Meaning, that the event Capture the Flag has to be over, in order to stop accessing it. You will have access to the waiting room until one team wins capture the flag.
 
Okay, unfortunately I'm having free access to the event and the waiting room after the 10 minutes are open! Would it be possible to change that? 10 minutes to start, if you don't have enough players close Thank you!
 
Okay, unfortunately I'm having free access to the event and the waiting room after the 10 minutes are open! Would it be possible to change that? 10 minutes to start, if you don't have enough players close Thank you!
You can do that should be fairly simple.
 
Back
Top